From: pk910 Date: Mon, 17 Dec 2012 17:34:01 +0000 (+0100) Subject: Merge remote-tracking branch 'IOMultiplexer.git/master' into development X-Git-Url: http://git.pk910.de/?p=NeonServV5.git;a=commitdiff_plain;h=259cd63d66c9870dc57aac7c34ef9b2033d9daf0;hp=f83bd22fe2dcb3ca0ddb9321944ee0b9c9b61ea6 Merge remote-tracking branch 'IOMultiplexer.git/master' into development --- diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..34025fe --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +mysqlConfig.h +neonserv.ini +neonserv.conf +src/version.c +src/version.php +src/year.php +src/.deps +src/.dirstamp +src/lib/.deps +src/lib/.dirstamp +src/lib/*.o +src/*.o +bin/ +scripts/*.php +autom4te.cache +aclocal.m4 +config.h.in~ +config.h.in +config.h +config.status +config.log +configure +depcomp +install-sh +Makefile.in +Makefile +missing +stamp-h1 +neonserv +neonserv*.exe +libmysql.dll +pthreadGC2.dll +motd.txt \ No newline at end of file diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..c19ce60 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,25 @@ + NeonServ dev + +Project Admin: +pk910 + +Coders: +pk910 + +Code Testers: +TeaTow +Zer0n +Phil + +Documentation: +- none - + +Translation: +Buschman +Zer0n +and some others? + +You can find us on irc://irc.onlinegamesnet.net/#NeonServ +If you found a bug you're welcome to report it on +http://bugtrack.pk910.de/git_view.php?p=NeonServV5.git +or contact us on IRC. diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. 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 +them 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 prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. 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. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey 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; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If 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 convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU 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 that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + 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. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +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. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 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, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program 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, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..4775b94 --- /dev/null +++ b/INSTALL @@ -0,0 +1,68 @@ + NeonServ Installation Instructions + +Introduction: +---------- +If you encounter any problems compiling/running NeonServ, please make +sure you have followed the directions in this file correctly and that +all of the requirements listed below are met. + +If the problem persists, report it to one (or all) of the coders +listed in the AUTHORS file. Please try to include ALL relevant +information about the error/bug, including anything out of the +ordinary reported from make and the appropriate entries from the log +files. + +Quick Install: +---------- +$ ./autogen.sh +$ ./configure +$ make +$ ${EDITOR} neonserv.conf + NOTE: You may want to copy neonserv.example.conf to neonserv.conf and + edit that. +$ php language.php (optional) +$ ./neonserv + +Compiling: +---------- + 1) Enter the root directory of the NeonServ tree. If installation is + done from outside of it, it may cause problems during compile, or + during runtime. + + 2) If you've cloned a development snapshot of NeonServ from git.pk910.de + you need to generate the configure and Makefile files. For that you + simply need to run the autogen.sh script. + (chmood it to be executeable if needed) + + 3) Run the configure script (sh configure), it will verify that your + system will have the resources needed for NeonServ to compile. If + you would like to change the path where NeonServ will be installed + to, execute configure with the --prefix=/path option. The default + path is ~/neonserv/. + + + 4) You may optionally edit config.h in case the configure script made a + mistake. + + 5) Execute the "make" command to begin compiling. If you encounter any + uncorrectable errors/warnings, please scroll up to the introduction + section and follow the instructions. + + 6) You may now either type "make install" to install it to your + installation path, or work from your build directory, either is fine. + + 7) Copy neonserv.example.conf to neonserv.conf and edit the mysql information. + + 8) Start NeonServ by executing the "neonserv" binary. + When you start NeonServ for the first time, it will create the neccesary + tables in your database and ask you for the initial bot settings + (Bot Administrator, first IRC Connection) + + 9) To get the actual language pack (including help strings) run the included + language.php (and rehash the bot's language cache with `reloadlang `) + + 10) You can now register a channel via the bots query ;) + +End of file, INSTALL. + +-pk910 (neonserv@pk910.de) diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..679e1f6 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,243 @@ +AUTOMAKE_OPTIONS = foreign +AM_CFLAGS = $(MYSQL_CFLAGS) + +BUILT_SOURCES = version.c +version.c: checkversion +checkversion: + cd src && ./version.sh && cd .. + +noinst_PROGRAMS = neonserv +noinst_LTLIBRARIES = libDummyServ.la libfuncmds.la libglobalcmd.la libNeonHelp.la libNeonBackup.la libNeonServ.la libNeonSpam.la libstats.la libNeonFun.la libNeonKick.la + +libDummyServ_la_SOURCES = src/modules/DummyServ.mod/bot_DummyServ.c \ + src/modules/DummyServ.mod/module.c +libDummyServ_la_LDFLAGS = -module -rpath /nowhere -avoid-version -no-undefined +libDummyServ_la_LIBADD = $(MYSQL_LIBS) + +libfuncmds_la_SOURCES = src/modules/funcmd.mod/cmd_funcmds.c \ + src/modules/funcmd.mod/module.c +libfuncmds_la_LDFLAGS = -module -rpath /nowhere -avoid-version -no-undefined +libfuncmds_la_LIBADD = $(MYSQL_LIBS) + +libglobalcmd_la_SOURCES = src/modules/global.mod/cmd_global.c \ + src/modules/global.mod/cmd_global_bind.c \ + src/modules/global.mod/cmd_global_command.c \ + src/modules/global.mod/cmd_global_commands.c \ + src/modules/global.mod/cmd_global_emote.c \ + src/modules/global.mod/cmd_global_god.c \ + src/modules/global.mod/cmd_global_netinfo.c \ + src/modules/global.mod/cmd_global_notice.c \ + src/modules/global.mod/cmd_global_raw.c \ + src/modules/global.mod/cmd_global_register.c \ + src/modules/global.mod/cmd_global_reloadlang.c \ + src/modules/global.mod/cmd_global_say.c \ + src/modules/global.mod/cmd_global_setaccess.c \ + src/modules/global.mod/cmd_global_unbind.c \ + src/modules/global.mod/cmd_global_unregister.c \ + src/modules/global.mod/cmd_global_version.c \ + src/modules/global.mod/cmd_global_staff.c \ + src/modules/global.mod/cmd_global_motd.c \ + src/modules/global.mod/cmd_global_bots.c \ + src/modules/global.mod/cmd_global_reload.c \ + src/modules/global.mod/cmd_global_restart.c \ + src/modules/global.mod/cmd_global_die.c \ + src/modules/global.mod/cmd_global_setbot.c \ + src/modules/global.mod/cmd_global_addbot.c \ + src/modules/global.mod/cmd_global_delbot.c \ + src/modules/global.mod/cmd_global_reconnect.c \ + src/modules/global.mod/cmd_global_modcmd.c \ + src/modules/global.mod/cmd_global_meminfo.c \ + src/modules/global.mod/cmd_global_extscript.c \ + src/modules/global.mod/cmd_global_move.c \ + src/modules/global.mod/cmd_global_global.c \ + src/modules/global.mod/module.c +libglobalcmd_la_LDFLAGS = -module -rpath /nowhere -avoid-version -no-undefined +libglobalcmd_la_LIBADD = $(MYSQL_LIBS) + +libNeonHelp_la_SOURCES = src/modules/NeonHelp.mod/bot_NeonHelp.c \ + src/modules/NeonHelp.mod/cmd_neonhelp.c \ + src/modules/NeonHelp.mod/cmd_neonhelp_next.c \ + src/modules/NeonHelp.mod/cmd_neonhelp_delete.c \ + src/modules/NeonHelp.mod/cmd_neonhelp_requests.c \ + src/modules/NeonHelp.mod/cmd_neonhelp_stats.c \ + src/modules/NeonHelp.mod/cmd_neonhelp_history.c \ + src/modules/NeonHelp.mod/module.c +libNeonHelp_la_LDFLAGS = -module -rpath /nowhere -avoid-version -no-undefined +libNeonHelp_la_LIBADD = $(MYSQL_LIBS) + +libNeonBackup_la_SOURCES = src/modules/NeonBackup.mod/bot_NeonBackup.c \ + src/modules/NeonBackup.mod/cmd_neonbackup.c \ + src/modules/NeonBackup.mod/cmd_neonbackup_recover.c \ + src/modules/NeonBackup.mod/module.c +libNeonBackup_la_LDFLAGS = -module -rpath /nowhere -avoid-version -no-undefined +libNeonBackup_la_LIBADD = $(MYSQL_LIBS) + +libNeonServ_la_SOURCES = src/modules/NeonServ.mod/bot_NeonServ.c \ + src/modules/NeonServ.mod/cmd_neonserv.c \ + src/modules/NeonServ.mod/cmd_neonserv_access.c \ + src/modules/NeonServ.mod/cmd_neonserv_addban.c \ + src/modules/NeonServ.mod/cmd_neonserv_addtimeban.c \ + src/modules/NeonServ.mod/cmd_neonserv_adduser.c \ + src/modules/NeonServ.mod/cmd_neonserv_ban.c \ + src/modules/NeonServ.mod/cmd_neonserv_bans.c \ + src/modules/NeonServ.mod/cmd_neonserv_chanservsync.c \ + src/modules/NeonServ.mod/cmd_neonserv_clvl.c \ + src/modules/NeonServ.mod/cmd_neonserv_csuspend.c \ + src/modules/NeonServ.mod/cmd_neonserv_cunsuspend.c \ + src/modules/NeonServ.mod/cmd_neonserv_delban.c \ + src/modules/NeonServ.mod/cmd_neonserv_delme.c \ + src/modules/NeonServ.mod/cmd_neonserv_deluser.c \ + src/modules/NeonServ.mod/cmd_neonserv_deop.c \ + src/modules/NeonServ.mod/cmd_neonserv_deopall.c \ + src/modules/NeonServ.mod/cmd_neonserv_devoice.c \ + src/modules/NeonServ.mod/cmd_neonserv_devoiceall.c \ + src/modules/NeonServ.mod/cmd_neonserv_down.c \ + src/modules/NeonServ.mod/cmd_neonserv_downall.c \ + src/modules/NeonServ.mod/cmd_neonserv_events.c \ + src/modules/NeonServ.mod/cmd_neonserv_giveowner.c \ + src/modules/NeonServ.mod/cmd_neonserv_help.c \ + src/modules/NeonServ.mod/cmd_neonserv_invite.c \ + src/modules/NeonServ.mod/cmd_neonserv_inviteme.c \ + src/modules/NeonServ.mod/cmd_neonserv_invitemeall.c \ + src/modules/NeonServ.mod/cmd_neonserv_kick.c \ + src/modules/NeonServ.mod/cmd_neonserv_kickban.c \ + src/modules/NeonServ.mod/cmd_neonserv_mdeluser.c \ + src/modules/NeonServ.mod/cmd_neonserv_mode.c \ + src/modules/NeonServ.mod/cmd_neonserv_myaccess.c \ + src/modules/NeonServ.mod/cmd_neonserv_op.c \ + src/modules/NeonServ.mod/cmd_neonserv_opall.c \ + src/modules/NeonServ.mod/cmd_neonserv_oplog.c \ + src/modules/NeonServ.mod/cmd_neonserv_peek.c \ + src/modules/NeonServ.mod/cmd_neonserv_recover.c \ + src/modules/NeonServ.mod/cmd_neonserv_resync.c \ + src/modules/NeonServ.mod/cmd_neonserv_search.c \ + src/modules/NeonServ.mod/cmd_neonserv_set.c \ + src/modules/NeonServ.mod/cmd_neonserv_suspend.c \ + src/modules/NeonServ.mod/cmd_neonserv_topic.c \ + src/modules/NeonServ.mod/cmd_neonserv_trace.c \ + src/modules/NeonServ.mod/cmd_neonserv_trim.c \ + src/modules/NeonServ.mod/cmd_neonserv_unban.c \ + src/modules/NeonServ.mod/cmd_neonserv_unbanall.c \ + src/modules/NeonServ.mod/cmd_neonserv_unbanme.c \ + src/modules/NeonServ.mod/cmd_neonserv_unsuspend.c \ + src/modules/NeonServ.mod/cmd_neonserv_up.c \ + src/modules/NeonServ.mod/cmd_neonserv_upall.c \ + src/modules/NeonServ.mod/cmd_neonserv_users.c \ + src/modules/NeonServ.mod/cmd_neonserv_uset.c \ + src/modules/NeonServ.mod/cmd_neonserv_voice.c \ + src/modules/NeonServ.mod/cmd_neonserv_voiceall.c \ + src/modules/NeonServ.mod/cmd_neonserv_wipeinfo.c \ + src/modules/NeonServ.mod/cmd_neonserv_addrank.c \ + src/modules/NeonServ.mod/cmd_neonserv_assignrank.c \ + src/modules/NeonServ.mod/cmd_neonserv_delrank.c \ + src/modules/NeonServ.mod/cmd_neonserv_listrank.c \ + src/modules/NeonServ.mod/cmd_neonserv_setrank.c \ + src/modules/NeonServ.mod/cmd_neonserv_info.c \ + src/modules/NeonServ.mod/cmd_neonserv_rename.c \ + src/modules/NeonServ.mod/cmd_neonserv_unvisited.c \ + src/modules/NeonServ.mod/cmd_neonserv_noregister.c \ + src/modules/NeonServ.mod/cmd_neonserv_nicklist.c \ + src/modules/NeonServ.mod/cmd_neonserv_halfop.c \ + src/modules/NeonServ.mod/cmd_neonserv_dehalfop.c \ + src/modules/NeonServ.mod/cmd_neonserv_halfopall.c \ + src/modules/NeonServ.mod/cmd_neonserv_dehalfopall.c \ + src/modules/NeonServ.mod/module.c +libNeonServ_la_LDFLAGS = -module -rpath /nowhere -avoid-version -no-undefined +libNeonServ_la_LIBADD = $(MYSQL_LIBS) + +libNeonSpam_la_SOURCES = src/modules/NeonSpam.mod/bot_NeonSpam.c \ + src/modules/NeonSpam.mod/cmd_neonspam.c \ + src/modules/NeonSpam.mod/cmd_neonspam_set.c \ + src/modules/NeonSpam.mod/cmd_neonspam_addbad.c \ + src/modules/NeonSpam.mod/cmd_neonspam_delbad.c \ + src/modules/NeonSpam.mod/cmd_neonspam_badwords.c \ + src/modules/NeonSpam.mod/module.c +libNeonSpam_la_LDFLAGS = -module -rpath /nowhere -avoid-version -no-undefined +libNeonSpam_la_LIBADD = $(MYSQL_LIBS) + +libstats_la_SOURCES = src/modules/stats.mod/module.c +libstats_la_LDFLAGS = -module -rpath /nowhere -avoid-version -no-undefined +libstats_la_LIBADD = $(MYSQL_LIBS) + +libNeonFun_la_SOURCES = src/modules/NeonFun.mod/bot_NeonFun.c \ + src/modules/NeonFun.mod/cmd_neonfun.c \ + src/modules/NeonFun.mod/cmd_neonfun_uno.c \ + src/modules/NeonFun.mod/cmd_neonfun_unoplay.c \ + src/modules/NeonFun.mod/cmd_neonfun_unotake.c \ + src/modules/NeonFun.mod/game_uno.c \ + src/modules/NeonFun.mod/cmd_neonfun_4wins.c \ + src/modules/NeonFun.mod/cmd_neonfun_4stone.c \ + src/modules/NeonFun.mod/cmd_neonfun_4view.c \ + src/modules/NeonFun.mod/game_4wins.c \ + src/modules/NeonFun.mod/cmd_neonfun_blackjack.c \ + src/modules/NeonFun.mod/cmd_neonfun_bjtake.c \ + src/modules/NeonFun.mod/cmd_neonfun_bjenough.c \ + src/modules/NeonFun.mod/game_blackjack.c \ + src/modules/NeonFun.mod/module.c +libNeonFun_la_LDFLAGS = -module -rpath /nowhere -avoid-version -no-undefined +libNeonFun_la_LIBADD = $(MYSQL_LIBS) + +libNeonKick_la_SOURCES = src/modules/NeonKick.mod/bot_NeonKick.c \ + src/modules/NeonKick.mod/module.c +libNeonKick_la_LDFLAGS = -module -rpath /nowhere -avoid-version -no-undefined +libNeonKick_la_LIBADD = $(MYSQL_LIBS) + +neonserv_SOURCES = src/version.c \ + src/IOEngine_epoll.c \ + src/IOEngine_kevent.c \ + src/IOEngine_select.c \ + src/IOEngine_win32.c \ + src/IOHandler.c \ + src/IOHandler_SSL.c \ + src/EventLogger.c \ + src/IRCEvents.c \ + src/main.c \ + src/signal.c \ + src/ChanNode.c \ + src/IRCParser.c \ + src/ClientSocket.c \ + src/UserNode.c \ + src/ChanUser.c \ + src/ModeNode.c \ + src/BanNode.c \ + src/IPNode.c \ + src/WHOHandler.c \ + src/modcmd.c \ + src/mysqlConn.c \ + src/lang.c \ + src/HandleInfoHandler.c \ + src/tools.c \ + src/timeq.c \ + src/DBHelper.c \ + src/IRCQueue.c \ + src/bots.c \ + src/ConfigParser.c \ + src/QServer.c \ + src/modules.c \ + src/module_commands.c \ + src/ModuleFunctions.c \ + src/statistics.c \ + src/log.c \ + src/memoryDebug.c \ + src/mutexDebug.c + +neonserv_LDADD = $(MYSQL_LIBS) $(SYSTEM_LIBS) + +install-exec-local: + $(INSTALL) -d -m 755 $(prefix) + $(INSTALL) -m 644 $(srcdir)/AUTHORS $(prefix) + $(INSTALL) -m 644 $(srcdir)/COPYING $(prefix) + $(INSTALL) -m 644 $(srcdir)/INSTALL $(prefix) + $(INSTALL) -m 644 $(srcdir)/VERSION $(prefix) + $(INSTALL) -m 744 ./neonserv $(prefix) + $(INSTALL) -m 644 $(srcdir)/database.sql $(prefix) + $(INSTALL) -m 644 $(srcdir)/database.upgrade.sql $(prefix) + $(INSTALL) -m 644 $(srcdir)/database.defaults.sql $(prefix) + $(INSTALL) -m 600 $(srcdir)/neonserv.example.conf $(prefix) + $(INSTALL) -m 744 $(srcdir)/language.php $(prefix) + $(INSTALL) -m 644 $(srcdir)/.libs/*.so $(prefix) + @echo + @echo NeonServ-$(VERSION) has been installed to $(prefix) + @echo Edit the neonserv.example.conf and save it as neonserv.conf before you start! + @echo You should use an own database for this Bot - table names are NOT prefixed! + @echo diff --git a/QServer/NeonServ_QServer.class.php b/QServer/NeonServ_QServer.class.php new file mode 100644 index 0000000..e3fb4b0 --- /dev/null +++ b/QServer/NeonServ_QServer.class.php @@ -0,0 +1,150 @@ +. + */ + +class NeonServ_QServer { + private $admin, $socket, $numeric; + + function NeonServ_QServer() { + $this->admin['pass']='*'; // QServer Passwort + $this->admin['host']='127.0.0.1'; + $this->admin['port']=7499; + } + + public function disconnect() { + fclose($this->socket); + $this->socket=false; + } + + public function connect() { + if($this->socket) { + $this->disconnect(); + } + $this->socket=@fsockopen($this->admin['host'], $this->admin['port'], $errno, $errstr, 3); + if ($this->socket) { + stream_set_timeout($this->socket, 2); + fputs($this->socket, "A ".$this->admin['pass']."\n"); + $data = $this->parseLine(@fgets($this->socket)); + if($data[0] == "A") + return true; + $this->disconnect(); //not logged in... + return false; + } + } + + public function connected() { + if($this->socket) return true; + return false; + } + + private function parseLine($line) { + $line = str_replace(array("\r", "\n"), array("", ""), $line); + $highExplode = explode(" :", $line, 2); + $tokens = explode(" ", $highExplode[0]); + if(count($highExplode) > 1) + $tokens[] = $highExplode[1]; + return $tokens; + } + + /* Get IRC User Information + * returns array: nick, ident, host, auth, realname + */ + public function getUser($nick) { + fputs($this->socket, "U ".$nick."\n"); + $data = $this->parseLine(@fgets($this->socket)); + if($data[0] != "U") return NULL; + if($data[1] == "0") return NULL; //User not found + $user = array(); + $user['nick'] = $data[2]; + $user['ident'] = $data[3]; + $user['host'] = $data[4]; + $user['auth'] = ($data[5] == "0" ? NULL : $data[5]); + $user['realname'] = $data[6]; + return $user; + } + + /* Get IRC Channel Information + * returns array: name, usercount, modes, topic + */ + public function getChannel($name) { + fputs($this->socket, "C ".$name."\n"); + $data = $this->parseLine(@fgets($this->socket)); + if($data[0] != "C") return NULL; + if($data[1] == "0") return NULL; //Channel not found + $chan = array(); + $chan['name'] = $data[2]; + $chan['usercount'] = $data[3]; + $chan['modes'] = implode(" ", array_slice($data, 4, (count($data) - 5))); + $chan['topic'] = $data[count($data)-1]; + return $chan; + } + + /* Get All IRC Channels + * returns array with subarrays: name, usercount, modes, topic + */ + public function getChannels() { + fputs($this->socket, "AC\n"); + $channels = array(); + while(true) { + $data = $this->parseLine(@fgets($this->socket)); + if($data[0] == "E") return NULL; + if($data[0] == "ACE") break; + if($data[0] == "AC") { + $chan = array(); + $chan['name'] = $data[1]; + $chan['usercount'] = $data[2]; + $chan['modes'] = implode(" ", array_slice($data, 3, (count($data) - 4))); + $chan['topic'] = $data[count($data)-1]; + $channels[] = $chan; + } + } + return $channels; + } + + /* Get IRC Channel Users + * returns array with subarrays: nick, auth, flags + */ + public function getChannelUsers($name, $invisibles = false) { + fputs($this->socket, "ACU ".$name." ".($invisibles ? "1" : "0")."\n"); + $chanusers = array(); + while(true) { + $data = $this->parseLine(@fgets($this->socket)); + if($data[0] == "E") return NULL; + if($data[0] == "ACUE") break; + if($data[0] == "ACU") { + $chanuser = array(); + $chanuser['nick'] = $data[1]; + $chanuser['auth'] = ($data[2] == "0" ? NULL : $data[2]); + $chanuser['flags'] = $data[3]; + $chanusers[] = $chanuser; + } + } + return $chanusers; + } + + /* send IRC RAW + * returns true / false + */ + public function sendRAW($class, $raw, $classIsNick = false) { + fputs($this->socket, "R ".($classIsNick ? "1" : "0")." ".$class." :".$raw."\n"); + $data = $this->parseLine(@fgets($this->socket)); + if($data[0] == "R" && $data[1] == "1") return true; + return false; + } +} + +?> diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..ada9b65 --- /dev/null +++ b/VERSION @@ -0,0 +1,22 @@ +VERSION LOG +2012-09-06 5.6 + merged IOMultiplexer project into NeonServ (faster than the old select calls) + new version numbering + +2012-08-12 5.5 + new core to increase stability (NeonServ is now 1 year old :)) + +2012-02-16 5.4 + modular buildup + +2011-12-22 5.3 + dynamic bot management + +2011-10-26 5.2 + added NeonSpam (anti spam service) + +2011-10-05 5.1 + first production version... + +2011-08-09 5.0 (dev) + developing period... diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..01c075c --- /dev/null +++ b/autogen.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +echo "Generating configure files... may take a while." + +autoreconf --install --force && \ + echo "Preparing was successful if there was no error messages above." && \ + echo "Now type:" && \ + echo " ./configure && make" && \ + echo "Run './configure --help' for more information" diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..0a6c9d9 --- /dev/null +++ b/configure.ac @@ -0,0 +1,95 @@ +# Process this file with autoconf to produce a configure script. + +AC_PREREQ([2.63]) +AC_INIT([NeonServ], [5.6], [bugs@pk910.de], [neonserv], [http://neonserv.krypton-bouncer.de]) +AC_PREFIX_DEFAULT([~/neonserv]) +AC_CANONICAL_TARGET +AM_INIT_AUTOMAKE([foreign subdir-objects]) +AM_SILENT_RULES([yes]) +AC_CONFIG_HEADERS([config.h]) + +LT_INIT([disable-static]) + +# Checks for programs. +AC_PROG_CC +AC_PROG_AWK + +# Checks for libraries. +# Get MySQL library and include locations +AC_ARG_WITH([mysql], + [AS_HELP_STRING([--with-mysql=DIR], + [location of the MySQL headers, defaults to /usr/include/mysql])], + [MYSQL_CFLAGS="-I$withval"], + [MYSQL_CFLAGS='-I/usr/include/mysql']) +AC_SUBST([MYSQL_CFLAGS]) + +AC_ARG_WITH([mysql-lib], + [AS_HELP_STRING([--with-mysql-lib=DIR], [location of the MySQL libraries])], + [MYSQL_LIBS="-L$withval -lmysqlclient"], + [MYSQL_LIBS='-lmysqlclient']) +AC_SUBST([MYSQL_LIBS]) + +AC_CHECK_LIB([ws2_32], [_head_libws2_32_a], [LIBS="$LIBS -lws2_32"]) +AC_CHECK_LIB([dl], [dlopen], [LIBS="$LIBS -ldl"]) +AC_CHECK_LIB([pthread], [pthread_create], [LIBS="$LIBS -lpthread"]) +AC_CHECK_LIB([ssl], [SSL_read], [LIBS="$LIBS -lssl"]) +AC_CHECK_LIB([crypto], [X509_new], [LIBS="$LIBS -lcrypto"]) + +do_have_ssl="no"; +AC_CHECK_LIB(ssl, SSL_read, [ + AC_CHECK_LIB(crypto, X509_new, [ + AC_CHECK_HEADERS(openssl/ssl.h openssl/err.h openssl/rand.h, [ + AC_DEFINE([HAVE_SSL], 1, [Define if you are using SSL]) + ]) + ]) +]) + +do_have_threads="no"; +AC_CHECK_LIB(pthread, pthread_create, [ + AC_CHECK_HEADERS(pthread.h, [ + do_have_threads="yes" + ]) +]) + +AC_ARG_ENABLE([threads], + [AS_HELP_STRING([--enable-threads], [use threads if possible])], + [ + if test x"$do_have_threads" = xyes; then + LIBS="$LIBS -lpthread" + AC_DEFINE([HAVE_THREADS], 1, [Define if you have Threads]) + fi + ], + []) + +AC_ARG_ENABLE([memory-debug], + [AS_HELP_STRING([--enable-memory-debug], [run memory debugger])], + [ + AC_DEFINE([ENABLE_MEMORY_DEBUG], 1, [Define if you enable memoryDebug.c]) + ], + []) + +AC_ARG_ENABLE([mutex-debug], + [AS_HELP_STRING([--enable-mutex-debug], [run mutex debugger])], + [ + AC_DEFINE([ENABLE_MUTEX_DEBUG], 1, [Define if you enable mutexDebug.c]) + ], + []) + +AC_ARG_ENABLE([debug], + [AS_HELP_STRING([--enable-debug], [debug mode (compile using -O0 -Wall -Wshadow -Werror)])], + [CFLAGS='-g -O0 -Wall -Wshadow -Werror'], + [CFLAGS='-g -O2']) + +CFLAGS="$CFLAGS -D_GNU_SOURCE" + +# Checks for header files. +AC_CHECK_HEADERS([stdio.h stdlib.h string.h ctype.h windows.h winsock2.h malloc.h features.h sys/types.h sys/socket.h netinet/in.h netinet/tcp.h arpa/inet.h netdb.h sys/wait.h errno.h unistd.h getopt.h stdarg.h sys/time.h time.h signal.h sys/epoll.h sys/event.h ws2tcpip.h mysql.h mysql/errmsg.h errmsg.h]) + +# Checks for typedefs, structures, and compiler characteristics. + +# Checks for library functions. +AC_FUNC_MALLOC +AC_CHECK_FUNCS([gethostbyname memset select socket strchr strdup strstr]) + +AC_CONFIG_FILES(Makefile) +AC_OUTPUT \ No newline at end of file diff --git a/database.defaults.sql b/database.defaults.sql new file mode 100644 index 0000000..25e3336 --- /dev/null +++ b/database.defaults.sql @@ -0,0 +1,275 @@ +-- +-- Daten für Tabelle `bot_binds` +-- +-- +-- Daten für Tabelle `bot_binds` +-- + +INSERT INTO `bot_binds` (NULL, `botclass`, `botid`, `command`, `function`, `parameters`, `chan_access`, `global_access`, `flags`) VALUES +(NULL, 1, 0, 'bind', 'bind', '', NULL, NULL, 0), +(NULL, 1, 0, 'unbind', 'unbind', '', NULL, NULL, 0), +(NULL, 1, 0, 'adduser', 'adduser', '', NULL, NULL, 0), +(NULL, 1, 0, 'addowner', 'adduser', '%1 500', NULL, NULL, 0), +(NULL, 1, 0, 'addcoowner', 'adduser', '%1 400', NULL, NULL, 0), +(NULL, 1, 0, 'addco', 'adduser', '%1 400', NULL, NULL, 0), +(NULL, 1, 0, 'addmaster', 'adduser', '%1 300', NULL, NULL, 0), +(NULL, 1, 0, 'addop', 'adduser', '%1 200', NULL, NULL, 0), +(NULL, 1, 0, 'addpeon', 'adduser', '%1 100', NULL, NULL, 0), +(NULL, 1, 0, 'addvoice', 'adduser', '%1 100', NULL, NULL, 0), +(NULL, 1, 0, 'deluser', 'deluser', '', NULL, NULL, 0), +(NULL, 1, 0, 'clvl', 'clvl', '', NULL, NULL, 0), +(NULL, 1, 0, 'access', 'access', '', NULL, NULL, 0), +(NULL, 1, 0, 'a', 'access', '', NULL, NULL, 0), +(NULL, 1, 0, 'users', 'users', '', NULL, NULL, 0), +(NULL, 1, 0, 'vlist', 'users', '%1|* 100 199', NULL, NULL, 0), +(NULL, 1, 0, 'olist', 'users', '%1|* 200 299', NULL, NULL, 0), +(NULL, 1, 0, 'plist', 'users', '%1|* 100 199', NULL, NULL, 0), +(NULL, 1, 0, 'mlist', 'users', '%1|* 300 399', NULL, NULL, 0), +(NULL, 1, 0, 'clist', 'users', '%1|* 400 499', NULL, NULL, 0), +(NULL, 1, 0, 'suspend', 'suspend', '', NULL, NULL, 0), +(NULL, 1, 0, 'unsuspend', 'unsuspend', '', NULL, NULL, 0), +(NULL, 1, 0, 'delme', 'delme', '', NULL, NULL, 0), +(NULL, 1, 0, 'deleteme', 'delme', '', NULL, NULL, 0), +(NULL, 1, 0, 'myaccess', 'myaccess', '', NULL, NULL, 0), +(NULL, 1, 0, 'mya', 'myaccess', '', NULL, NULL, 0), +(NULL, 1, 0, 'ma', 'myaccess', '', NULL, NULL, 0), +(NULL, 1, 0, 'up', 'up', '', NULL, NULL, 0), +(NULL, 1, 0, 'down', 'down', '', NULL, NULL, 0), +(NULL, 1, 0, 'upall', 'upall', '', NULL, NULL, 0), +(NULL, 1, 0, 'uall', 'upall', '', NULL, NULL, 0), +(NULL, 1, 0, 'downall', 'downall', '', NULL, NULL, 0), +(NULL, 1, 0, 'dall', 'downall', '', NULL, NULL, 0), +(NULL, 1, 0, 'mdeluser', 'mdeluser', '', NULL, NULL, 0), +(NULL, 1, 0, 'mdelop', 'mdeluser', '200 %1', NULL, NULL, 0), +(NULL, 1, 0, 'mdelvoice', 'mdeluser', '100 %1', NULL, NULL, 0), +(NULL, 1, 0, 'mdelpeon', 'mdeluser', '100 %1', NULL, NULL, 0), +(NULL, 1, 0, 'mdelmaster', 'mdeluser', '300 %1', NULL, NULL, 0), +(NULL, 1, 0, 'mdelcoowner', 'mdeluser', '400 %1', NULL, NULL, 0), +(NULL, 1, 0, 'mdelco', 'mdeluser', '400 %1', NULL, NULL, 0), +(NULL, 1, 0, 'trim', 'trim', '', NULL, NULL, 0), +(NULL, 1, 0, 'giveowner', 'giveowner', '', NULL, NULL, 0), +(NULL, 1, 0, 'giveownership', 'giveowner', '', NULL, NULL, 0), +(NULL, 1, 0, 'op', 'op', '', NULL, NULL, 0), +(NULL, 1, 0, 'deop', 'deop', '', NULL, NULL, 0), +(NULL, 1, 0, 'voice', 'voice', '', NULL, NULL, 0), +(NULL, 1, 0, 'devoice', 'devoice', '', NULL, NULL, 0), +(NULL, 1, 0, 'v', 'voice', '', NULL, NULL, 0), +(NULL, 1, 0, 'opall', 'opall', '', NULL, NULL, 0), +(NULL, 1, 0, 'deopall', 'deopall', '', NULL, NULL, 0), +(NULL, 1, 0, 'voiceall', 'voiceall', '', NULL, NULL, 0), +(NULL, 1, 0, 'vall', 'voiceall', '', NULL, NULL, 0), +(NULL, 1, 0, 'devoiceall', 'devoiceall', '', NULL, NULL, 0), +(NULL, 1, 0, 'set', 'set', '', NULL, NULL, 0), +(NULL, 1, 0, 'kick', 'kick', '', NULL, NULL, 0), +(NULL, 1, 0, 'k', 'kick', '', NULL, NULL, 0), +(NULL, 1, 0, 'kickban', 'kickban', '', NULL, NULL, 0), +(NULL, 1, 0, 'kb', 'kickban', '', NULL, NULL, 0), +(NULL, 1, 0, 'ban', 'ban', '', NULL, NULL, 0), +(NULL, 1, 0, 'b', 'ban', '', NULL, NULL, 0), +(NULL, 1, 0, 'wipeinfo', 'wipeinfo', '', NULL, NULL, 0), +(NULL, 1, 0, 'deluinfo', 'wipeinfo', '', NULL, NULL, 0), +(NULL, 1, 0, 'addban', 'addban', '', NULL, NULL, 0), +(NULL, 1, 0, 'ab', 'addban', '', NULL, NULL, 0), +(NULL, 1, 0, 'bans', 'bans', '', NULL, NULL, 0), +(NULL, 1, 0, 'shitlist', 'bans', '', NULL, NULL, 0), +(NULL, 1, 0, 'showbans', 'bans', '', NULL, NULL, 0), +(NULL, 1, 0, 'showban', 'bans', '', NULL, NULL, 0), +(NULL, 1, 0, 'delban', 'delban', '', NULL, NULL, 0), +(NULL, 1, 0, 'netinfo', 'netinfo', '', NULL, NULL, 0), +(NULL, 1, 0, 'topic', 'topic', '', NULL, NULL, 0), +(NULL, 1, 0, 'chanservsync', 'chanservsync', '', NULL, NULL, 0), +(NULL, 1, 0, 'resync', 'resync', '', NULL, NULL, 0), +(NULL, 1, 0, 'addtimeban', 'addtimeban', '', NULL, NULL, 0), +(NULL, 1, 0, 'addtimedban', 'addtimeban', '', NULL, NULL, 0), +(NULL, 1, 0, 'timeban', 'addtimeban', '', NULL, NULL, 0), +(NULL, 1, 0, 'timedban', 'addtimeban', '', NULL, NULL, 0), +(NULL, 1, 0, 'tb', 'addtimeban', '', NULL, NULL, 0), +(NULL, 1, 0, 'atb', 'addtimeban', '', NULL, NULL, 0), +(NULL, 1, 0, 'mode', 'mode', '', NULL, NULL, 0), +(NULL, 1, 0, 'version', 'version', '', NULL, NULL, 0), +(NULL, 1, 0, 'peek', 'peek', '', NULL, NULL, 0), +(NULL, 1, 0, 'uset', 'uset', '', NULL, NULL, 0), +(NULL, 1, 0, 'aset', 'uset', '', NULL, NULL, 0), +(NULL, 1, 0, 'unban', 'unban', '', NULL, NULL, 0), +(NULL, 1, 0, 'ub', 'unban', '', NULL, NULL, 0), +(NULL, 1, 0, 'unbanall', 'unbanall', '', NULL, NULL, 0), +(NULL, 1, 0, 'uba', 'unbanall', '', NULL, NULL, 0), +(NULL, 1, 0, 'unbanme', 'unbanme', '', NULL, NULL, 0), +(NULL, 1, 0, 'ubme', 'unbanme', '', NULL, NULL, 0), +(NULL, 1, 0, 'invite', 'invite', '', NULL, NULL, 0), +(NULL, 1, 0, 'inviteme', 'inviteme', '', NULL, NULL, 65536), +(NULL, 1, 0, 'help', 'help', '', NULL, NULL, 0), +(NULL, 1, 0, 'cmds', 'commands', '', NULL, NULL, 0), +(NULL, 1, 0, 'trace', 'trace', '', NULL, NULL, 0), +(NULL, 1, 0, 'register', 'register', '', NULL, NULL, 0), +(NULL, 1, 0, 'reg', 'register', '', NULL, NULL, 0), +(NULL, 1, 0, 'unregister', 'unregister', '', NULL, NULL, 0), +(NULL, 1, 0, 'unreg', 'unregister', '', NULL, NULL, 0), +(NULL, 1, 0, 'recover', 'recover', '', NULL, NULL, 0), +(NULL, 1, 0, 'say', 'say', '', NULL, NULL, 0), +(NULL, 1, 0, 'emote', 'emote', '', NULL, NULL, 0), +(NULL, 1, 0, 'notice', 'notice', '', NULL, NULL, 0), +(NULL, 1, 0, 'raw', 'raw', '', NULL, NULL, 0), +(NULL, 1, 0, 'god', 'god', '', NULL, NULL, 0), +(NULL, 1, 0, 'godmode', 'god', '', NULL, NULL, 0), +(NULL, 1, 0, 'reloadlang', 'reloadlang', '', NULL, NULL, 0), +(NULL, 1, 0, 'csuspend', 'csuspend', '', NULL, NULL, 0), +(NULL, 1, 0, 'cunsuspend', 'cunsuspend', '', NULL, NULL, 0), +(NULL, 1, 0, 'move', 'move', '', NULL, NULL, 0), +(NULL, 1, 0, 'events', 'events', '', NULL, NULL, 0), +(NULL, 1, 0, 'oplog', 'oplog', '', NULL, NULL, 0), +(NULL, 1, 0, 'search', 'search', '', NULL, NULL, 0), +(NULL, 1, 0, 'command', 'command', '', NULL, NULL, 0), +(NULL, 1, 0, 'va', 'voiceall', '', NULL, NULL, 0), +(NULL, 1, 0, 'setaccess', 'setaccess', '', NULL, NULL, 0), +(NULL, 1, 0, 'blist', 'bans', '', NULL, NULL, 0), +(NULL, 1, 0, 'addrank', 'addrank', '', NULL, NULL, 0), +(NULL, 1, 0, 'delrank', 'delrank', '', NULL, NULL, 0), +(NULL, 1, 0, 'listrank', 'listrank', '', NULL, NULL, 0), +(NULL, 1, 0, 'rank', 'assignrank', '', NULL, NULL, 0), +(NULL, 1, 0, 'assignrank', 'assignrank', '', NULL, NULL, 0), +(NULL, 1, 0, 'setrank', 'setrank', '', NULL, NULL, 0), +(NULL, 1, 0, 'ranks', 'listrank', '', NULL, NULL, 0), +(NULL, 1, 0, 'setinfo', 'uset', 'info %1-', NULL, NULL, 0), +(NULL, 1, 0, 'info', 'info', '', NULL, NULL, 0), +(NULL, 1, 0, 'rename', 'rename', '', NULL, NULL, 0), +(NULL, 1, 0, 'unvisited', 'unvisited', '', NULL, NULL, 0), +(NULL, 1, 0, 'add', 'adduser', '', NULL, NULL, 0), +(NULL, 1, 0, 'sync', 'resync', '', NULL, NULL, 0), +(NULL, 1, 0, 'dv', 'devoice', '', NULL, NULL, 0), +(NULL, 1, 0, 'ping', 'funcmd.ping', '', NULL, NULL, 0), +(NULL, 1, 0, 'pong', 'funcmd.pong', '', NULL, NULL, 0), +(NULL, 1, 0, 'dice', 'funcmd.dice', '', NULL, NULL, 0), +(NULL, 1, 0, 'd', 'funcmd.dice', '', NULL, NULL, 0), +(NULL, 1, 0, '8ball', 'funcmd.8ball', '', NULL, NULL, 0), +(NULL, 1, 0, '8', 'funcmd.8ball', '', NULL, NULL, 0), +(NULL, 1, 0, 'staff', 'staff', '', NULL, NULL, 0), +(NULL, 1, 0, 'noregister', 'noregister', '', NULL, NULL, 0), +(NULL, 1, 0, 'commands', 'commands', '', NULL, NULL, 0), +(NULL, 1, 0, 'oplogs', 'oplog', '', NULL, NULL, 0), +(NULL, 1, 0, 'cmd', 'command', '', NULL, NULL, 0), +(NULL, 1, 0, 'team', 'staff', '', NULL, NULL, 0), +(NULL, 1, 0, 'syncusers', 'chanservsync', '', NULL, NULL, 0), +(NULL, 1, 0, 'cookie', 'FunCMD.cookie', '', NULL, NULL, 0), +(NULL, 1, 0, 'banlist', 'bans', '', NULL, NULL, 0), +(NULL, 1, 0, 'del', 'deluser', '', NULL, NULL, 0), +(NULL, 1, 0, 'nicklist', 'nicklist', '', NULL, NULL, 0), +(NULL, 1, 0, 'bots', 'bots', '', NULL, NULL, 0), +(NULL, 1, 0, 'bot list', 'bots', '', NULL, NULL, 0), +(NULL, 1, 0, 'bot add', 'addbot', '', NULL, NULL, 0), +(NULL, 1, 0, 'bot del', 'delbot', '', NULL, NULL, 0), +(NULL, 1, 0, 'bot reconnect', 'reconnect', '', NULL, NULL, 0), +(NULL, 1, 0, 'bot', 'linker', '', NULL, NULL, 0), +(NULL, 1, 0, 'bot set', 'setbot', '', NULL, NULL, 0), +(NULL, 1, 0, 'dva', 'devoiceall', '', NULL, NULL, 0), +(NULL, 1, 0, 'restart', 'restart', '', NULL, NULL, 0), +(NULL, 1, 0, 'showcommands', 'commands', '', NULL, NULL, 0), +(NULL, 1, 0, 'moo', 'extscript', 'toys apt-get moo %1+', NULL, NULL, 0), +(NULL, 1, 0, 'botinfo', 'netinfo', '', NULL, NULL, 0), +(NULL, 1, 0, 'reload', 'reload', '', NULL, NULL, 0), +(NULL, 1, 0, 'moo', 'extscript', 'toys apt-get moo %1+', NULL, NULL, 0), +(NULL, 1, 0, 'botinfo', 'netinfo', '', NULL, NULL, 0), +(NULL, 1, 0, 'meminfo', 'meminfo', '', NULL, NULL, 0), +(NULL, 1, 0, 'die', 'die', '', NULL, NULL, 0), +(NULL, 1, 0, 'loadmod', 'loadmod', '', NULL, NULL, 0), +(NULL, 1, 0, 'unloadmod', 'unloadmod', '', NULL, NULL, 0), +(NULL, 1, 0, 'reloadmod', 'reloadmod', '', NULL, NULL, 0), +(NULL, 1, 0, 'modules', 'modules', '', NULL, NULL, 0), +(NULL, 1, 0, 'changelevel', 'clvl', '', NULL, NULL, 0), +(NULL, 1, 0, 'changelvl', 'clvl', '', NULL, NULL, 0), +(NULL, 1, 0, '4wins', 'NeonFun.4wins', '', NULL, NULL, 0), +(NULL, 1, 0, '4stone', 'NeonFun.4stone', '', NULL, NULL, 0), +(NULL, 1, 0, '4view', 'NeonFun.4view', '', NULL, NULL, 0), +(NULL, 1, 0, 'uno', 'NeonFun.uno', '', NULL, NULL, 0), +(NULL, 1, 0, 'unoplay', 'NeonFun.unoplay', '', NULL, NULL, 0), +(NULL, 1, 0, 'unotake', 'NeonFun.unotake', '', NULL, NULL, 0), +(NULL, 1, 0, 'netstats', 'netinfo', '', NULL, NULL, 0), +(NULL, 1, 0, 'bomb', 'funcmd.bomb', '', NULL, NULL, 0), +(NULL, 1, 0, 'inviteme all', 'invitemeall', '', NULL, NULL, 0), +(NULL, 1, 0, 'ship', 'funcmd.ship', '', NULL, NULL, 0), +(NULL, 1, 0, 'bj', 'NeonFun.blackjack', '', NULL, NULL, 0), +(NULL, 1, 0, 'bjtake', 'NeonFun.bjtake', '', NULL, NULL, 0), +(NULL, 1, 0, 'bjenough', 'NeonFun.bjenough', '', NULL, NULL, 0), + +(NULL, 2, 0, 'bind', 'bind', '', NULL, NULL, 0), +(NULL, 2, 0, 'unbind', 'unbind', '', NULL, NULL, 0), +(NULL, 2, 0, 'register', 'register', '', NULL, NULL, 0), +(NULL, 2, 0, 'unregister', 'unregister', '', NULL, NULL, 0), +(NULL, 2, 0, 'version', 'version', '', NULL, NULL, 0), +(NULL, 2, 0, 'netinfo', 'netinfo', '', NULL, NULL, 0), +(NULL, 2, 0, 'set', 'set', '', NULL, NULL, 0), +(NULL, 2, 0, 'csuspend', 'csuspend', '', NULL, NULL, 0), +(NULL, 2, 0, 'cunsuspend', 'cunsuspend', '', NULL, NULL, 0), +(NULL, 2, 0, 'say', 'say', '', NULL, NULL, 0), +(NULL, 2, 0, 'emote', 'emote', '', NULL, NULL, 0), +(NULL, 2, 0, 'notice', 'notice', '', NULL, NULL, 0), +(NULL, 2, 0, 'raw', 'raw', '', NULL, NULL, 0), +(NULL, 2, 0, 'god', 'god', '', NULL, NULL, 0), +(NULL, 2, 0, 'commands', 'commands', '', NULL, NULL, 0), +(NULL, 2, 0, 'bans', 'NeonServ.bans', '', NULL, NULL, 0), +(NULL, 2, 0, 'banlist', 'NeonServ.bans', '', NULL, NULL, 0), +(NULL, 2, 0, 'delban', 'NeonServ.delban', '', NULL, NULL, 0), +(NULL, 2, 0, 'addban', 'NeonServ.addban', '', NULL, NULL, 0), +(NULL, 2, 0, 'search', 'NeonServ.search', '', NULL, NULL, 0), +(NULL, 2, 0, 'unreg', 'unregister', '', NULL, NULL, 0), +(NULL, 2, 0, 'reg', 'register', '', NULL, NULL, 0), +(NULL, 2, 0, 'move', 'move', '', NULL, NULL, 0), +(NULL, 2, 0, 'badwords', 'badwords', '', NULL, NULL, 0), +(NULL, 2, 0, 'listbad', 'badwords', '', NULL, NULL, 0), +(NULL, 2, 0, 'addbad', 'addbad', '', NULL, NULL, 0), +(NULL, 2, 0, 'delbad', 'delbad', '', NULL, NULL, 0), + +(NULL, 3, 0, 'bind', 'bind', '', NULL, NULL, 0), +(NULL, 3, 0, 'unbind', 'unbind', '', NULL, NULL, 0), +(NULL, 3, 0, 'say', 'say', '', NULL, NULL, 0), +(NULL, 3, 0, 'notice', 'notice', '', NULL, NULL, 0), +(NULL, 3, 0, 'emote', 'emote', '', NULL, NULL, 0), + +(NULL, 4, 0, 'raw', 'raw', '', NULL, NULL, 0), +(NULL, 4, 0, 'next', 'next', '', NULL, NULL, 0), +(NULL, 4, 0, 'requests', 'requests', '', NULL, NULL, 0), +(NULL, 4, 0, 'delete', 'delete', '', NULL, NULL, 0), +(NULL, 4, 0, 'del', 'delete', '', NULL, NULL, 0), +(NULL, 4, 0, 'reqs', 'requests', '', NULL, NULL, 0), +(NULL, 4, 0, 'commands', 'commands', '', NULL, NULL, 0), +(NULL, 4, 0, 'version', 'version', '', NULL, NULL, 0), +(NULL, 4, 0, 'netinfo', 'netinfo', '', NULL, NULL, 0), +(NULL, 4, 0, 'stats', 'stats', '', NULL, NULL, 0), +(NULL, 4, 0, 'history', 'history', '', NULL, NULL, 0), +(NULL, 4, 0, 'register', 'register', '', NULL, NULL, 0), +(NULL, 4, 0, 'unregister', 'unregister', '', NULL, NULL, 0), + +(NULL, 6, 0, 'backup', 'linker', '', NULL, NULL, 0), +(NULL, 6, 0, 'backup bind', 'bind', '', NULL, NULL, 0), +(NULL, 6, 0, 'backup unbind', 'unbind', '', NULL, NULL, 0), +(NULL, 6, 0, 'backup modcmd', 'modcmd', '', NULL, NULL, 0), +(NULL, 6, 0, 'backup register', 'register', '', NULL, NULL, 0), +(NULL, 6, 0, 'backup unregister', 'unregister', '', NULL, NULL, 0), +(NULL, 6, 0, 'backup commands', 'commands', '', NULL, NULL, 0), +(NULL, 6, 0, 'backup command', 'command', '', NULL, NULL, 0), +(NULL, 6, 0, 'ping', 'funcmd.ping', '', NULL, NULL, 0), +(NULL, 6, 0, 'backup search', 'neonserv.search', '', NULL, NULL, 0), +(NULL, 6, 0, 'backup raw', 'raw', '', NULL, NULL, 0), + +(NULL, 7, 0, 'kickserv register', 'register', '', NULL, NULL, 0), +(NULL, 7, 0, 'kickserv unregister', 'unregister', '', NULL, NULL, 0), +(NULL, 7, 0, 'kickserv raw', 'raw', '', NULL, NULL, 0), +(NULL, 7, 0, 'kickserv bind', 'bind', '', NULL, NULL, 0), +(NULL, 7, 0, 'kickserv unbind', 'unbind', '', NULL, NULL, 0), +(NULL, 7, 0, 'kickserv modcmd', 'modcmd', '', NULL, NULL, 0), +(NULL, 7, 0, 'kickserv', 'linker', '', NULL, NULL, 0), + +(NULL, 1, 0, 'dns', 'extscript', 'toys php scripts/examples/dnslookup.php $1 $2 %1+', NULL, NULL, 0), +(NULL, 1, 0, 'calc', 'funcmd.extscript', 'toys php scripts/examples/calc.php $1- %1+', NULL, NULL, 0); + + +-- +-- Daten für Tabelle `channels` +-- + +INSERT INTO `channels` (`channel_id`, `channel_name`, `channel_key`, `channel_maxusers`, `channel_lastvisit`, `channel_lastgiveowner`, `channel_pubcmd`, `channel_nodelete`, `channel_nogaccess`, `channel_canadd`, `channel_candel`, `channel_canclvl`, `channel_cankick`, `channel_canban`, `channel_staticban`, `channel_protect`, `channel_canop`, `channel_canhalfop`, `channel_canvoice`, `channel_getop`, `channel_gethalfop`, `channel_getvoice`, `channel_greeting`, `channel_usergreeting`, `channel_userinfo`, `channel_dynlimit`, `channel_getinvite`, `channel_topicmask`, `channel_exttopic`, `channel_exttopic_topic`, `channel_defaulttopic`, `channel_wipeinfo`, `channel_modes`, `channel_enfmodes`, `channel_enftopic`, `channel_topicsnarf`, `channel_changetopic`, `channel_setters`, `channel_canresync`, `channel_cansuspend`, `channel_notice`, `channel_noticereaction`, `channel_ctcp`, `channel_ctcpreaction`, `channel_registered`, `channel_registrator`, `channel_toys`, `channel_scanner`, `channel_spam_limit`, `channel_spam_reaction`, `channel_spam_reaction_duration`, `channel_spam_except`, `channel_flood_limit`, `channel_flood_time`, `channel_flood_reaction`, `channel_flood_reaction_duration`, `channel_flood_except`, `channel_join_limit`, `channel_join_time`, `channel_join_reaction`, `channel_join_reaction_duration`, `channel_join_except`, `channel_botnet_bantime`, `channel_botnet_except`, `channel_caps_percent`, `channel_caps_reaction`, `channel_caps_reaction_duration`, `channel_caps_except`, `channel_digit_percent`, `channel_digit_reaction`, `channel_digit_reaction_duration`, `channel_digit_except`, `channel_badword_reaction`, `channel_badword_reaction_duration`, `channel_badword_except`) VALUES +(NULL, 'defaults', '', 0, 0, 0, 0, 0, 0, 300, 300, 300, 200, 200, 250, 0, 200, 200, 200, 200, 150, 100, '', '', 1, 0, 1, '', 0, '', '', 300, '+', 400, 400, 501, 200, 400, 200, 300, 0, 0, 0, 0, 0, 0, 0, '', 4, 0, 120, 400, 5, 3, 0, 120, 400, 4, 20, 2, 300, 400, 1800, 1, 60, 0, 60, 200, 60, 0, 60, 200, 0, 60, 400); + +-- +-- please run language.php for help and language entries. +-- diff --git a/database.sql b/database.sql new file mode 100644 index 0000000..d378118 --- /dev/null +++ b/database.sql @@ -0,0 +1,443 @@ +-- NeonServ Database v5.1 + +-- +-- Tabellenstruktur für Tabelle `bans` +-- + +CREATE TABLE IF NOT EXISTS `bans` ( + `ban_id` int(11) NOT NULL AUTO_INCREMENT, + `ban_channel` int(11) NOT NULL, + `ban_mask` varchar(250) NOT NULL, + `ban_triggered` int(15) NOT NULL, + `ban_timeout` int(15) NOT NULL, + `ban_owner` int(11) NOT NULL, + `ban_reason` varchar(512) NOT NULL, + PRIMARY KEY (`ban_id`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `bots` +-- + +CREATE TABLE IF NOT EXISTS `bots` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `active` tinyint(1) NOT NULL, + `nick` varchar(255) NOT NULL, + `server` varchar(255) NOT NULL, + `port` int(5) NOT NULL, + `pass` varchar(255) NOT NULL, + `ssl` tinyint(1) NOT NULL, + `bind` varchar(255) DEFAULT NULL, + `ident` varchar(12) NOT NULL, + `realname` varchar(255) NOT NULL, + `automodes` varchar(20) NOT NULL, + `oper_user` varchar(50) DEFAULT NULL, + `oper_pass` varchar(50) DEFAULT NULL, + `botclass` int(10) NOT NULL, + `textbot` tinyint(1) NOT NULL, + `queue` tinyint(1) NOT NULL, + `secret` tinyint(1) NOT NULL, + `defaulttrigger` varchar(10) NOT NULL, + `max_channels` int(5) NOT NULL, + `register_priority` int(2) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `nick` (`nick`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `bot_binds` +-- + +CREATE TABLE IF NOT EXISTS `bot_binds` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `botclass` int(11) NOT NULL, + `botid` int(11) NOT NULL, + `command` varchar(60) NOT NULL, + `function` varchar(60) NOT NULL, + `parameters` varchar(100) NOT NULL, + `chan_access` varchar(256) DEFAULT NULL, + `global_access` int(3) DEFAULT NULL, + `flags` int(10) NOT NULL, + PRIMARY KEY (`id`), + KEY `botid` (`botid`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `bot_channels` +-- + +CREATE TABLE IF NOT EXISTS `bot_channels` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `botid` int(11) NOT NULL, + `chanid` int(11) NOT NULL, + `trigger` varchar(50) NULL DEFAULT '+', + `suspended` tinyint(1) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `channels` +-- + +CREATE TABLE IF NOT EXISTS `channels` ( + `channel_id` int(11) NOT NULL AUTO_INCREMENT, + `channel_name` varchar(250) NOT NULL, + `channel_key` varchar(50) NOT NULL, + `channel_maxusers` int(5) NOT NULL, + `channel_lastvisit` int(11) NOT NULL, + `channel_lastgiveowner` int(15) NOT NULL, + `channel_pubcmd` smallint(3) DEFAULT NULL, + `channel_nodelete` tinyint(1) NOT NULL, + `channel_nogaccess` tinyint(1) DEFAULT NULL, + `channel_canadd` smallint(3) DEFAULT NULL, + `channel_candel` smallint(3) DEFAULT NULL, + `channel_canclvl` smallint(3) DEFAULT NULL, + `channel_cankick` smallint(3) DEFAULT NULL, + `channel_canban` smallint(3) DEFAULT NULL, + `channel_staticban` smallint(3) DEFAULT NULL, + `channel_protect` tinyint(1) DEFAULT NULL, + `channel_canop` smallint(3) DEFAULT NULL, + `channel_canhalfop` smallint(3) DEFAULT NULL, + `channel_canvoice` smallint(3) DEFAULT NULL, + `channel_getop` smallint(3) DEFAULT NULL, + `channel_gethalfop` smallint(3) DEFAULT NULL, + `channel_getvoice` smallint(3) DEFAULT NULL, + `channel_greeting` varchar(512) NOT NULL, + `channel_usergreeting` varchar(512) NOT NULL, + `channel_userinfo` smallint(3) DEFAULT NULL, + `channel_dynlimit` smallint(5) DEFAULT NULL, + `channel_getinvite` smallint(3) DEFAULT NULL, + `channel_topicmask` varchar(512) NOT NULL, + `channel_exttopic` tinyint(1) NOT NULL, + `channel_exttopic_topic` varchar(512) NOT NULL, + `channel_defaulttopic` varchar(512) NOT NULL, + `channel_wipeinfo` smallint(3) DEFAULT NULL, + `channel_modes` varchar(500) DEFAULT NULL, + `channel_enfmodes` smallint(3) DEFAULT NULL, + `channel_enftopic` smallint(3) DEFAULT NULL, + `channel_topicsnarf` smallint(3) DEFAULT NULL, + `channel_changetopic` smallint(3) DEFAULT NULL, + `channel_setters` smallint(3) DEFAULT NULL, + `channel_canresync` smallint(3) DEFAULT NULL, + `channel_cansuspend` smallint(3) DEFAULT NULL, + `channel_notice` smallint(3) DEFAULT NULL, + `channel_noticereaction` tinyint(1) DEFAULT NULL, + `channel_ctcp` smallint(3) DEFAULT NULL, + `channel_ctcpreaction` tinyint(1) DEFAULT NULL, + `channel_registered` int(11) NOT NULL, + `channel_registrator` int(11) NOT NULL, + `channel_toys` tinyint(1) NOT NULL, + `channel_scanner` varchar(50) DEFAULT NULL, + `channel_spam_limit` smallint(3) DEFAULT NULL, + `channel_spam_reaction` tinyint(1) DEFAULT NULL, + `channel_spam_reaction_duration` mediumint(7) DEFAULT NULL, + `channel_spam_except` smallint(3) DEFAULT NULL, + `channel_flood_limit` smallint(3) DEFAULT NULL, + `channel_flood_time` smallint(3) DEFAULT NULL, + `channel_flood_reaction` tinyint(1) DEFAULT NULL, + `channel_flood_reaction_duration` mediumint(7) DEFAULT NULL, + `channel_flood_except` smallint(3) DEFAULT NULL, + `channel_join_limit` smallint(3) DEFAULT NULL, + `channel_join_time` smallint(3) DEFAULT NULL, + `channel_join_reaction` tinyint(1) DEFAULT NULL, + `channel_join_reaction_duration` mediumint(7) DEFAULT NULL, + `channel_join_except` smallint(3) DEFAULT NULL, + `channel_botnet_bantime` mediumint(7) DEFAULT NULL, + `channel_botnet_except` smallint(3) DEFAULT NULL, + `channel_caps_percent` tinyint(3) DEFAULT NULL, + `channel_caps_reaction` tinyint(1) DEFAULT NULL, + `channel_caps_reaction_duration` mediumint(7) DEFAULT NULL, + `channel_caps_except` smallint(3) DEFAULT NULL, + `channel_digit_percent` tinyint(3) DEFAULT NULL, + `channel_digit_reaction` tinyint(1) DEFAULT NULL, + `channel_digit_reaction_duration` mediumint(7) DEFAULT NULL, + `channel_digit_except` smallint(3) DEFAULT NULL, + `channel_badword_reaction` tinyint(1) DEFAULT NULL, + `channel_badword_reaction_duration` mediumint(7) DEFAULT NULL, + `channel_badword_except` smallint(3) DEFAULT NULL, + PRIMARY KEY (`channel_id`), + UNIQUE KEY `channel_name` (`channel_name`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `chanusers` +-- + +CREATE TABLE IF NOT EXISTS `chanusers` ( + `chanuser_id` int(11) NOT NULL AUTO_INCREMENT, + `chanuser_cid` int(11) NOT NULL, + `chanuser_uid` int(11) NOT NULL, + `chanuser_access` int(3) NOT NULL, + `chanuser_flags` int(11) NOT NULL, + `chanuser_seen` int(11) NOT NULL, + `chanuser_infoline` varchar(512) NOT NULL, + PRIMARY KEY (`chanuser_id`), + KEY `chanuser_cid` (`chanuser_cid`), + KEY `chanuser_uid` (`chanuser_uid`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `events` +-- + +CREATE TABLE IF NOT EXISTS `events` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `cid` int(11) NOT NULL, + `nick` varchar(50) NOT NULL, + `auth` varchar(50) NOT NULL, + `time` int(11) NOT NULL, + `command` varchar(512) NOT NULL, + PRIMARY KEY (`id`), + KEY `cid` (`cid`), + KEY `time` (`time`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `funcmd` +-- + +CREATE TABLE IF NOT EXISTS `funcmd` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `lang` int(11) NOT NULL, + `cmd` varchar(50) NOT NULL, + `text` text NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COMMENT='neonserv v3'; + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `fundata` +-- + +CREATE TABLE IF NOT EXISTS `fundata` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `cid` int(11) NOT NULL, + `user` varchar(50) NOT NULL, + `name` varchar(50) NOT NULL, + `value` text NOT NULL, + PRIMARY KEY (`id`), + KEY `cid` (`cid`), + KEY `user` (`user`), + KEY `name` (`name`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 COMMENT='neonserv v3' AUTO_INCREMENT=1 ; + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `godlog` +-- + +CREATE TABLE IF NOT EXISTS `godlog` ( + `godlog_id` int(11) NOT NULL AUTO_INCREMENT, + `godlog_uid` int(11) NOT NULL, + `godlog_cid` int(15) NOT NULL, + `godlog_time` int(15) NOT NULL, + `godlog_cmd` varchar(512) NOT NULL, + PRIMARY KEY (`godlog_id`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `help` +-- + +CREATE TABLE IF NOT EXISTS `help` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `lang` varchar(6) NOT NULL, + `ident` varchar(64) NOT NULL, + `text` text NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `language` +-- + +CREATE TABLE IF NOT EXISTS `language` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `lang` varchar(5) NOT NULL, + `ident` varchar(64) NOT NULL, + `text` varchar(256) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `noinvite` +-- + +CREATE TABLE IF NOT EXISTS `noinvite` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `cid` int(11) NOT NULL, + `uid` int(11) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `owner_history` +-- + + +CREATE TABLE IF NOT EXISTS `owner_history` ( + `owner_history_id` int(11) NOT NULL AUTO_INCREMENT, + `owner_history_cid` int(11) NOT NULL, + `owner_history_to_uid` int(11) NOT NULL, + `owner_history_from_uid` int(11) NOT NULL, + `owner_history_time` int(11) NOT NULL, + PRIMARY KEY (`owner_history_id`), + KEY `owner_history_cid` (`owner_history_cid`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `users` +-- + +CREATE TABLE IF NOT EXISTS `users` ( + `user_id` int(11) NOT NULL AUTO_INCREMENT, + `user_user` varchar(250) NOT NULL, + `user_access` int(4) NOT NULL, + `user_rank` int(11) NOT NULL, + `user_god` tinyint(1) NOT NULL, + `user_lang` varchar(6) NOT NULL, + `user_reply_privmsg` tinyint(1) NOT NULL, + `user_block_invites` tinyint(1) NOT NULL, + `user_registered` INT(20) NOT NULL, + `user_lastcheck` INT(20) NOT NULL, + PRIMARY KEY (`user_id`), + UNIQUE KEY `user_user` (`user_user`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `support_ranks` +-- + +CREATE TABLE `support_ranks` ( +`rank_id` INT( 11 ) NOT NULL AUTO_INCREMENT PRIMARY KEY , +`rank_name` VARCHAR( 256 ) NOT NULL , +`rank_access` INT( 4 ) NOT NULL , +`rank_info` VARCHAR( 512 ) NOT NULL , +`rank_order` SMALLINT( 4 ) NOT NULL +) ENGINE = MYISAM ; + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `version` +-- + +CREATE TABLE IF NOT EXISTS `version` ( + `database_version` int(5) NOT NULL, + PRIMARY KEY (`database_version`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `donotregister` +-- + +CREATE TABLE `donotregister` ( +`dnr_id` INT( 11 ) NOT NULL AUTO_INCREMENT PRIMARY KEY , +`dnr_target` VARCHAR( 256 ) NOT NULL , +`dnr_user` INT( 11 ) NOT NULL , +`dnr_timeout` INT( 20 ) NOT NULL , +`dnr_reason` TEXT NOT NULL , +UNIQUE ( +`dnr_target` +) +) ENGINE = MYISAM ; + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `helpserv_requests` +-- + +CREATE TABLE IF NOT EXISTS `helpserv_requests` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `botid` INT(11) NOT NULL, + `host` varchar(200) NOT NULL, + `hand` varchar(50) NOT NULL, + `nick` varchar(50) NOT NULL, + `status` int(1) NOT NULL, + `supporter` int(11) NOT NULL, + `time` int(20) NOT NULL, + `delay` int(20) NOT NULL, + `text` text NOT NULL, + `log` text NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `helpserv_settings` +-- + +CREATE TABLE IF NOT EXISTS `helpserv_settings` ( + `helpserv_botid` int(11) NOT NULL, + `helpserv_support` varchar(256) NOT NULL, + `helpserv_public` varchar(256) DEFAULT NULL, + `helpserv_intern` varchar(256) DEFAULT NULL, + `helpserv_intern_announce` TINYINT(1) NOT NULL, + PRIMARY KEY (`helpserv_botid`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `settings` +-- + +CREATE TABLE IF NOT EXISTS `settings` ( + `name` varchar(100) NOT NULL, + `value` text NOT NULL, + PRIMARY KEY (`name`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +-- -------------------------------------------------------- + +-- +-- Tabellenstruktur für Tabelle `spamserv_badwords` +-- + +CREATE TABLE IF NOT EXISTS `spamserv_badwords` ( + `badword_id` int(11) NOT NULL AUTO_INCREMENT, + `badword_cid` int(11) NOT NULL, + `badword_match` varchar(128) NOT NULL, + `badword_uid` int(11) NOT NULL, + `badword_use_default` tinyint(1) NOT NULL, + `badword_exceptlevel` smallint(3) NOT NULL, + `badword_scan_ops` tinyint(1) NOT NULL, + `badword_scan_voice` tinyint(1) NOT NULL, + `badword_use_default_reaction` tinyint(1) NOT NULL, + `badword_reaction` tinyint(1) NOT NULL, + `badword_reaction_time` int(20) NOT NULL, + PRIMARY KEY (`badword_id`), + KEY `badword_cid` (`badword_cid`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 ; diff --git a/database.upgrade.sql b/database.upgrade.sql new file mode 100644 index 0000000..bb26d92 --- /dev/null +++ b/database.upgrade.sql @@ -0,0 +1,188 @@ +-- Database upgrades (running all SQL commands below the matching "-- version: x" line) + +-- version: 1 + +CREATE TABLE `support_ranks` ( +`rank_id` INT( 11 ) NOT NULL AUTO_INCREMENT PRIMARY KEY , +`rank_name` VARCHAR( 256 ) NOT NULL , +`rank_access` INT( 4 ) NOT NULL , +`rank_info` VARCHAR( 512 ) NOT NULL , +`rank_order` SMALLINT( 4 ) NOT NULL +) ENGINE = MYISAM ; + +ALTER TABLE `users` ADD `user_rank` INT( 11 ) NOT NULL AFTER `user_access`; + +-- version: 2 + +ALTER TABLE `owner_history` CHANGE `owner_history_uid` `owner_history_to_uid` INT( 11 ) NOT NULL; +ALTER TABLE `owner_history` ADD `owner_history_from_uid` INT( 11 ) NOT NULL AFTER `owner_history_to_uid`; + +-- version: 3 + +ALTER TABLE `channels` + DROP `channel_scanstate`, + DROP `channel_scanexcept`, + DROP `channel_maxrepeat`, + DROP `channel_repeatreaction`, + DROP `channel_maxflood`, + DROP `channel_floodtime`, + DROP `channel_floodreaction`, + DROP `channel_maxjoin`, + DROP `channel_jointime`, + DROP `channel_joinreaction`; + +ALTER TABLE `channels` + ADD `channel_scanner` VARCHAR(50) NULL, + ADD `channel_spam_limit` SMALLINT(3) NULL, + ADD `channel_spam_reaction` TINYINT(1) NULL, + ADD `channel_spam_reaction_duration` MEDIUMINT(7) NULL, + ADD `channel_spam_except` SMALLINT(3) NULL, + ADD `channel_flood_limit` SMALLINT(3) NULL, + ADD `channel_flood_time` SMALLINT(3) NULL, + ADD `channel_flood_reaction` TINYINT(1) NULL, + ADD `channel_flood_reaction_duration` MEDIUMINT(7) NULL, + ADD `channel_flood_except` SMALLINT(3) NULL, + ADD `channel_join_limit` SMALLINT(3) NULL, + ADD `channel_join_time` SMALLINT(3) NULL, + ADD `channel_join_reaction` TINYINT(1) NULL, + ADD `channel_join_reaction_duration` MEDIUMINT(7) NULL, + ADD `channel_join_except` SMALLINT(3) NULL, + ADD `channel_botnet_bantime` MEDIUMINT(7) NULL, + ADD `channel_botnet_except` SMALLINT(3) NULL, + ADD `channel_caps_percent` TINYINT(3) NULL, + ADD `channel_caps_reaction` TINYINT(1) NULL, + ADD `channel_caps_reaction_duration` MEDIUMINT(7) NULL, + ADD `channel_caps_except` SMALLINT(3) NULL, + ADD `channel_digit_percent` TINYINT(3) NULL, + ADD `channel_digit_reaction` TINYINT(1) NULL, + ADD `channel_digit_reaction_duration` MEDIUMINT(7) NULL, + ADD `channel_digit_except` SMALLINT(3) NULL; + +-- version: 4 + +ALTER TABLE `bots` ADD `queue` TINYINT( 1 ) NOT NULL AFTER `textbot` + +-- version: 5 + +CREATE TABLE `donotregister` ( +`dnr_id` INT( 11 ) NOT NULL AUTO_INCREMENT PRIMARY KEY , +`dnr_target` VARCHAR( 256 ) NOT NULL , +`dnr_user` INT( 11 ) NOT NULL , +`dnr_timeout` INT( 20 ) NOT NULL , +`dnr_reason` TEXT NOT NULL , +UNIQUE ( +`dnr_target` +) +) ENGINE = MYISAM ; + +-- version: 6 + +ALTER TABLE `fundata` CHANGE `uid` `user` VARCHAR( 50 ) NOT NULL; +ALTER TABLE `fundata` ADD INDEX ( `cid` ); +ALTER TABLE `fundata` ADD INDEX ( `user` ); +ALTER TABLE `fundata` ADD INDEX ( `name` ); + +-- version: 7 + +ALTER TABLE `bot_binds` ADD `botid` INT( 11 ) NOT NULL AFTER `botclass`; +ALTER TABLE `bot_binds` ADD INDEX ( `botclass` ); + +-- version: 8 + +ALTER TABLE `bot_channels` CHANGE `trigger` `trigger` VARCHAR( 50 ) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT '+'; + +-- version: 9 + +ALTER TABLE `bot_binds` CHANGE `flags` `flags` INT( 10 ) NOT NULL ; + +-- version: 10 + +CREATE TABLE IF NOT EXISTS `helpserv_requests` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `host` varchar(200) NOT NULL, + `hand` varchar(50) NOT NULL, + `nick` varchar(50) NOT NULL, + `status` int(1) NOT NULL, + `supporter` int(11) NOT NULL, + `time` int(20) NOT NULL, + `text` text NOT NULL, + `log` text NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +CREATE TABLE IF NOT EXISTS `helpserv_settings` ( + `helpserv_botid` int(11) NOT NULL, + `helpserv_support` varchar(256) NOT NULL, + `helpserv_public` varchar(256) DEFAULT NULL, + `helpserv_intern` varchar(256) DEFAULT NULL, + PRIMARY KEY (`helpserv_botid`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +-- version: 11 + +ALTER TABLE `helpserv_requests` ADD `botid` INT( 11 ) NOT NULL AFTER `id`; + +-- version: 12 + +ALTER TABLE `helpserv_settings` ADD `helpserv_intern_announce` TINYINT( 1 ) NOT NULL; + +-- version: 13 + +ALTER TABLE `channels` ADD `channel_canhalfop` SMALLINT( 3 ) NULL AFTER `channel_canop`; +ALTER TABLE `channels` ADD `channel_gethalfop` SMALLINT( 3 ) NULL AFTER `channel_getop`; +UPDATE `channels` SET `channel_canhalfop` = '150', +`channel_gethalfop` = '150' WHERE `channel_name` = 'defaults'; + +-- version: 14 + +ALTER TABLE `users` ADD `user_registered` INT( 20 ) NOT NULL , +ADD `user_lastcheck` INT( 20 ) NOT NULL; + +-- version: 15 + +CREATE TABLE IF NOT EXISTS `settings` ( + `name` varchar(100) NOT NULL, + `value` text NOT NULL, + PRIMARY KEY (`name`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +-- version: 16 + +CREATE TABLE IF NOT EXISTS `spamserv_badwords` ( + `badword_id` int(11) NOT NULL AUTO_INCREMENT, + `badword_cid` int(11) NOT NULL, + `badword_match` varchar(128) NOT NULL, + `badword_uid` int(11) NOT NULL, + `badword_use_default` tinyint(1) NOT NULL, + `badword_exceptlevel` smallint(3) NOT NULL, + `badword_scan_ops` tinyint(1) NOT NULL, + `badword_scan_voice` tinyint(1) NOT NULL, + `badword_use_default_reaction` tinyint(1) NOT NULL, + `badword_reaction` tinyint(1) NOT NULL, + `badword_reaction_time` int(20) NOT NULL, + PRIMARY KEY (`badword_id`), + KEY `badword_cid` (`badword_cid`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +ALTER TABLE `channels` ADD `channel_badword_reaction` TINYINT( 1 ) NULL , +ADD `channel_badword_reaction_duration` MEDIUMINT( 7 ) NULL AFTER `channel_badword_reaction` , +ADD `channel_badword_except` SMALLINT( 3 ) NULL AFTER `channel_badword_reaction_duration`; + +UPDATE `channels` SET `channel_badword_reaction` = '0', +`channel_badword_reaction_duration` = '60', +`channel_badword_except` = '400' WHERE `channel_name` = 'defaults'; + +-- version: 17 + +ALTER TABLE `bots` ADD `oper_user` VARCHAR( 50 ) NULL AFTER `automodes` , +ADD `oper_pass` VARCHAR( 50 ) NULL AFTER `oper_user`; + +-- version: 18 + +ALTER TABLE `users` ADD `user_block_invites` TINYINT NOT NULL AFTER `user_reply_privmsg`; + +-- version: 19 + +ALTER TABLE `bots` ADD `secret` TINYINT( 1 ) NOT NULL AFTER `queue`; + +-- version: 20 diff --git a/database.upgrade_v4_v5.sql b/database.upgrade_v4_v5.sql new file mode 100644 index 0000000..3aee182 --- /dev/null +++ b/database.upgrade_v4_v5.sql @@ -0,0 +1,76 @@ +-- Database of NeonServ V4 modifications for NeonServ V5 + +ALTER TABLE `bots` CHANGE `botclass` `botclass` INT( 10 ) NOT NULL; + +ALTER TABLE `users` CHANGE `user_lang` `user_lang` VARCHAR( 6 ) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL; +ALTER TABLE `users` ADD `user_reply_privmsg` TINYINT( 1 ) NOT NULL ; + +CREATE TABLE IF NOT EXISTS `godlog` ( + `godlog_id` int(11) NOT NULL AUTO_INCREMENT, + `godlog_uid` int(11) NOT NULL, + `godlog_cid` int(15) NOT NULL, + `godlog_time` int(15) NOT NULL, + `godlog_cmd` varchar(512) NOT NULL, + PRIMARY KEY (`godlog_id`) +) ENGINE=MyISAM; + +ALTER TABLE `channels` ADD `channel_lastgiveowner` INT( 11 ) NOT NULL AFTER `channel_lastvisit`; + +CREATE TABLE IF NOT EXISTS `owner_history` ( +`owner_history_id` INT( 11 ) NOT NULL AUTO_INCREMENT PRIMARY KEY , +`owner_history_cid` INT( 11 ) NOT NULL , +`owner_history_uid` INT( 11 ) NOT NULL , +`owner_history_time` INT( 11 ) NOT NULL , +INDEX ( `owner_history_cid` ) +) ENGINE = MYISAM ; + +ALTER TABLE `chanusers` ADD INDEX ( `chanuser_cid` ) ; +ALTER TABLE `chanusers` ADD INDEX ( `chanuser_uid` ) ; + +ALTER TABLE `bot_binds` ADD `chan_access` VARCHAR( 256 ) NULL DEFAULT NULL AFTER `parameters`; + +ALTER TABLE `bot_binds` CHANGE `global_access` `global_access` INT( 3 ) NULL; + +ALTER TABLE `bans` CHANGE `ban_owner` `ban_owner` INT( 11 ) NOT NULL; + +ALTER TABLE `channels` ADD `channel_exttopic` TINYINT( 1 ) NOT NULL AFTER `channel_topicmask` , +ADD `channel_exttopic_topic` VARCHAR( 512 ) NOT NULL AFTER `channel_exttopic`; + +ALTER TABLE `bots` ADD `max_channels` INT( 5 ) NOT NULL ; + +ALTER TABLE `bot_binds` CHANGE `botid` `botclass` INT( 11 ) NOT NULL; +ALTER TABLE `bots` DROP `whoisbot` ; +ALTER TABLE `bots` DROP `bindFrom` ; +ALTER TABLE `bots` ADD `register_priority` INT( 2 ) NOT NULL; + +CREATE TABLE IF NOT EXISTS `help` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `lang` varchar(6) NOT NULL, + `ident` varchar(64) NOT NULL, + `text` text NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +CREATE TABLE IF NOT EXISTS `language` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `lang` varchar(5) NOT NULL, + `ident` varchar(64) NOT NULL, + `text` varchar(256) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +ALTER TABLE `bot_channels` ADD `suspended` TINYINT( 1 ) NOT NULL; + +ALTER TABLE `users` ADD UNIQUE (`user_user`); + +ALTER TABLE `noinvite` ADD INDEX ( `cid`, `uid` ); + +CREATE TABLE IF NOT EXISTS `version` ( + `database_version` int(5) NOT NULL, + PRIMARY KEY (`database_version`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +INSERT INTO `version` (`database_version`) VALUES (1); + +INSERT INTO `channels` (`channel_name`, `channel_key`, `channel_maxusers`, `channel_lastvisit`, `channel_lastgiveowner`, `channel_pubcmd`, `channel_nodelete`, `channel_nogaccess`, `channel_canadd`, `channel_candel`, `channel_canclvl`, `channel_cankick`, `channel_canban`, `channel_staticban`, `channel_protect`, `channel_canop`, `channel_canvoice`, `channel_getop`, `channel_getvoice`, `channel_greeting`, `channel_usergreeting`, `channel_userinfo`, `channel_scanstate`, `channel_scanexcept`, `channel_maxrepeat`, `channel_repeatreaction`, `channel_maxflood`, `channel_floodtime`, `channel_floodreaction`, `channel_maxjoin`, `channel_jointime`, `channel_joinreaction`, `channel_dynlimit`, `channel_getinvite`, `channel_topicmask`, `channel_exttopic`, `channel_exttopic_topic`, `channel_defaulttopic`, `channel_wipeinfo`, `channel_modes`, `channel_enfmodes`, `channel_enftopic`, `channel_topicsnarf`, `channel_changetopic`, `channel_setters`, `channel_canresync`, `channel_cansuspend`, `channel_notice`, `channel_noticereaction`, `channel_ctcp`, `channel_ctcpreaction`, `channel_registered`, `channel_registrator`, `channel_toys`) VALUES +('defaults', '', 0, 0, 0, 0, 0, 0, 300, 300, 300, 200, 200, 250, 0, 200, 200, 200, 100, '', '', 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, '', 0, '', '', 300, '+', 400, 400, 501, 200, 400, 200, 300, 0, 0, 0, 0, 0, 0, 0); diff --git a/language.php b/language.php new file mode 100644 index 0000000..570b0d6 --- /dev/null +++ b/language.php @@ -0,0 +1,152 @@ +#!/usr/bin/php + \ No newline at end of file diff --git a/neonserv.example.conf b/neonserv.example.conf new file mode 100644 index 0000000..b10eca7 --- /dev/null +++ b/neonserv.example.conf @@ -0,0 +1,133 @@ +/* + NeonServ example configuration +*/ + +"MySQL" { + "host" = "127.0.0.1"; + "port" = 3306; + "user" = "neonserv"; + "pass" = "password"; + "base" = "neonserv"; +}; +"statistics" { + "enable" = 0; + "frequency" = 5; //minutes + "include_lusers" = 1; //general network statistics + "execute" = "php scripts/statistic.php"; + /* parameters: + - visible users + - visible chanusers + - visible channels + - privmsg per minute + - commands per minute + - network users + - network channels + */ +}; +"General" { + "worker_threads" = 5; //threads + "alertchan" = ""; //internal channel for important notifications + "have_halfop" = 0; + //unregister NeonSpam channels together with NeonServ? + "sync_neonspam_unreg" = 1; + "CheckAuths" { //check all AuthNames in the database for existence (AuthServ querys) + "enabled" = 1; + "start_time" = 3; //24h format - 3 means 3am + "duration" = 180; //minutes - query till start_time + duration o'clock ;) + "interval" = 2; //seconds - query every x seconds + "min_unckecked" = 172800; //check auth only if unchecked for x seconds + //180 miutes, every 2 seconds: 5400 auth checks + "alertchan" = "#krypton.intern"; // notifications about deleted users + }; + "UserBots" { + "OtherServ" { + "enabled" = 0; //enable, if you want to use ;) + "nick" = "OtherServ,OtherServ2"; //optional check (mask) + "sourcebot" = "NeonServ"; //send messages through a bot of this botclass + "opless_part" = "PRIVMSG %s :part %s"; //args: botnick, channel + "opless_join" = "PRIVMSG %s :join %s"; //args: botnick, channel + }; + }; +}; +"QServer" { //QServer is an unused + "enabled" = 0; //leave this disabled if you don't know what you're doing! + "port" = 7499; + "pass" = "blaa"; +}; +"logs" { + /* + module:section = target_type:target + possible sections: + "info","debug","raw","mysql","override","warning","error","fatal" + possible targets: + file:log.log + irc:#channel + std:out + std:err + */ + "*:info,warning,error,fatal" = "file:neonserv.log"; + // "*:override,error,fatal" = "irc:#neonserv"; + "*:info" = "std:out"; +}; +"modules" { + "libglobalcmd" { + "enabled" = 1; + "protected" = 1; //may not be unloaded + }; + "libDummyServ" { + "enabled" = 1; + "protected" = 0; + }; + "libfuncmds" { + "enabled" = 1; + "protected" = 0; + }; + "libNeonHelp" { + "enabled" = 1; + "protected" = 0; + //require users to be authenticated when opening a request + "need_auth" = 0; + //custom error message - if commented out "You need to be authenticated with AuthServ to use this command." will be used + //"need_auth_message" = "Sorry, we require you to be authenticated against AuthServ to use our Services. Please log in or register an account on http://xyz.de/register.php"; + }; + "libNeonServ" { + "enabled" = 1; + "protected" = 0; + // How often to look for channels that have expired? + "chan_expire_freq" = "3d"; + // How long is a channel unvisited (by masters or above) before it can be expired? + "chan_expire_delay" = "30d"; + // Automatically register a Backup bot with NeonServ + "auto_backup_register" = 0; + // Automatically unregister all Backup when NeonServ gets removed + "auto_backup_unregister" = 1; + // BackupServ channel setting + "channel_backup_setting" = 1; + }; + "libNeonBackup" { + "enabled" = 1; + "protected" = 0; + }; + "libNeonSpam" { + "enabled" = 1; + "protected" = 0; + }; + "libstats" { + /* statistics module + this module doesn't extend the bot functions at all! + It will simply send a small UDP packet to neonserv.krypton-bouncer.de + this packet is used to keep the statistics on the website up to date. + + If you don't want your bot to be listed on this site just disable this plugin + (entries will be removed after 6h on the website) + */ + "enabled" = 1; + "protected" = 0; + "hide_networkname" = 0; //hide network name + "hide_botnick" = 0; //hide bot nick, ident, host + // "use_bot" = "BotNick"; //use this bot for the statistics + "hide_chancount" = 0; //hide joined channel count + "hide_usercount" = 0; //hide number of users the bot can see + }; +}; + diff --git a/scripts/example.php.txt b/scripts/example.php.txt new file mode 100644 index 0000000..090b3f4 --- /dev/null +++ b/scripts/example.php.txt @@ -0,0 +1,11 @@ + \ No newline at end of file diff --git a/scripts/examples/calc.php b/scripts/examples/calc.php new file mode 100644 index 0000000..b907bc5 --- /dev/null +++ b/scripts/examples/calc.php @@ -0,0 +1,102 @@ +. + */ + +$text = implode(" ", array_slice($argv, 1)); + +$descriptor = array(0 => array("pipe", "r"),1 => array("pipe", "w"),2 => array("pipe", "w")); +$proc = proc_open('bc -l -q', $descriptor, $pipes); + +if(!is_resource($proc)) { + echo"internal calc error - please contact an administrator.\n"; + die(); +} + +fwrite($pipes[0], $text."\nquit\n"); + +$start = time(); + +$read=array($pipes[1]); +$write=NULL; +$except=NULL; + +while(1) { + $data = proc_get_status($proc); + if(!$data['running']) { + $out=""; + while(!feof($pipes[1])) { + $out .= fgets($pipes[1], 128); + } + if($out == "") { + $out="\0034"; + while(!feof($pipes[2])) { + $out .= fgets($pipes[2], 128); + } + $out.=""; + } + $out=str_replace("\n","",$out); + $out=str_replace("\r","",$out); + $out=str_replace("\\","",$out); + + if(strlen($text) > 20) { + if(strlen($out) > 450) { + echo "output too long (".strlen($out).")\n"; + } else { + echo "".$text."\n"; + echo $out."\n"; + } + } else { + $fout="".$text." = ".$out; + if(strlen($fout) > 450) { + echo "output too long (".strlen($out).")"; + } else { + echo $fout."\n"; + } + } + + fclose($pipes[0]); + fclose($pipes[1]); + fclose($pipes[2]); + proc_close($proc); + + die(); + } else { + + if($start+3 > time()) { + usleep(200000); //200ms + } else { + //TIMEOUT + fclose($pipes[0]); + fclose($pipes[1]); + fclose($pipes[2]); + //can't simply use proc_terminate: https://bugs.php.net/bug.php?id=39992 + $ppid = $data['pid']; + //use ps to get all the children of this process, and kill them + $pids = preg_split('/\s+/', `ps -o pid --no-heading --ppid $ppid`); + foreach($pids as $pid) { + if(is_numeric($pid)) { + posix_kill($pid, 9); //SIGKILL signal + } + } + proc_close($proc); + echo "calculator timeout. (maximum of 3 seconds exceeded)\n"; + die(); + } + } +} + +?> diff --git a/scripts/examples/dnslookup.php b/scripts/examples/dnslookup.php new file mode 100644 index 0000000..84ad4bc --- /dev/null +++ b/scripts/examples/dnslookup.php @@ -0,0 +1,180 @@ +. + */ + +function time2str($time, $anz = 9) { + $str=""; + if(!$anz) $anz=9; + if($time>=(60*60*24*365) && $anz > 0) { + $anz--; + $years=floor($time/(60*60*24*365)); + $str.=$years."Y"; + $time=$time-((60*60*24*365)*$years); + } + if($time>=(60*60*24*30) && $anz > 0) { + $anz--; + $months=floor($time/(60*60*24*30)); + if($str != "") $str.=" "; + $str.=$months."M"; + $time=$time-((60*60*24*30)*$months); + } + if($time>=(60*60*24) && $anz > 0) { + $anz--; + $days=floor($time/(60*60*24)); + if($str != "") $str.=" "; + $str.=$days."d"; + $time=$time-((60*60*24)*$days); + } + if($time>=(60*60) && $anz > 0) { + $anz--; + $stunden=floor($time/(60*60)); + if($str != "") $str.=" "; + $str.=$stunden."h"; + $time=$time-((60*60)*$stunden); + } + if($time>=(60) && $anz > 0) { + $anz--; + $min=floor($time/(60)); + if($str != "") $str.=" "; + $str.=$min."m"; + $time=$time-((60)*$min); + } + if(($time>1 || $str == "") && $anz > 0){ + $anz--; + if($str != "") $str.=" "; + $str.=$time."s"; + } + return $str; +} + + +$host = $argv[1]; +$show_record = strtoupper($argv[2]); + +$pattern_ipv6 = '/^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))(|\/[0-9]{1,3})$/'; +$pattern_ipv4 = '/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}(|\/[0-9]{1,2})$/'; + +$record_order = array("SOA", "NS", "A", "AAAA", "MX"); + +function show_subrecords($host) { + $dns = dns_get_record($host, DNS_ALL); + if(count($dns)) { + usort($dns, "sort_records"); + foreach($dns as $record) { + switch($record['type']) { + case "A": + echo" \002A \002 ".$record['ip'].($record['ttl'] < 86400 ? " (ttl: ".time2str($record['ttl'],2).")" : "")."\n"; + break; + case "AAAA": + echo" \002AAAA \002 ".$record['ipv6'].($record['ttl'] < 86400 ? " (ttl: ".time2str($record['ttl'],2).")" : "")."\n"; + break; + case "CNAME": + echo" \002CNAME \002 ".$record['target']."\n"; + break; + } + } + } +} + +function sort_records($a, $b) { + global $record_order; + $index_a = array_search($a['type'], $record_order); + if($index_a === FALSE) $index_a = count($record_order); + $index_b = array_search($b['type'], $record_order); + if($index_b === FALSE) $index_b = count($record_order); + $order = $index_a - $index_b; + if($order == 0) { + switch($record['type']) { + case "A": + $suborder = "ip"; + break; + case "AAAA": + $suborder = "ipv6"; + break; + case "TXT": + $suborder = "txt"; + break; + default: + $suborder = "target"; + break; + } + $order = strcmp($a[$suborder], $b[$suborder]); + } + return $order; +} + +if(strlen($host) && preg_match("#^((([a-z0-9-.]*)\.|)([a-z]{2,5})|).?$#i", $host)) { + $dns = dns_get_record($host, DNS_ALL); + echo"DNS Records for \002".$host."\002:"; + if($show_record != "" && $show_record != "*" && $show_record != "ANY") + echo" (".$show_record.")"; + echo"\n"; + if(count($dns)) { + usort($dns, "sort_records"); + foreach($dns as $record) { + if($show_record != "" && $show_record != "*" && $show_record != "ANY" && $show_record != $record['type']) + continue; + switch($record['type']) { + case "A": + echo" \002A \002 ".$record['ip'].($record['ttl'] < 86400 ? " (ttl: ".time2str($record['ttl'],2).")" : "")."\n"; + break; + case "AAAA": + echo" \002AAAA \002 ".$record['ipv6'].($record['ttl'] < 86400 ? " (ttl: ".time2str($record['ttl'],2).")" : "")."\n"; + break; + case "MX": + echo" \002MX \002 ".$record['target']." (priority: ".$record['pri'].")\n"; + if($show_record == "MX") { + show_subrecords($record['target']); + } + break; + case "NS": + echo" \002NS \002 ".$record['target']."\n"; + if($show_record == "NS") { + show_subrecords($record['target']); + } + break; + case "CNAME": + echo" \002CNAME \002 ".$record['target']."\n"; + break; + case "TXT": + echo" \002TXT \002 ".$record['txt']."\n"; + break; + case "SOA": + if($show_record != "SOA") { + echo" \002SOA \002 (see: dns ".$host." SOA)\n"; + break; + } + echo" \002SOA \002 (Start of Authority):\n"; + echo" name: ".$record['mname']."\n"; + echo" serial: ".$record['serial']."\n"; + echo" refresh: ".$record['refresh']." (".time2str($record['refresh'], 2).")\n"; + echo" retry: ".$record['retry']." (".time2str($record['retry'], 2).")\n"; + echo" expire: ".$record['expire']." (".time2str($record['expire'], 2).")\n"; + echo" TTL: ".$record['minimum-ttl']." (".time2str($record['minimum-ttl'], 2).")\n"; + break; + } + } + } else + echo"No records found.\n"; +} else if(preg_match($pattern_ipv4, $host) || preg_match($pattern_ipv6, $host)) { + $hostname = gethostbyaddr($host); + echo"Reverse Lookup for \002".$host."\002:\n"; + echo " \002PTR \002 ".$hostname."\n"; +} else { + echo"Invalid Hostname or IP-Address.\n"; +} +?> diff --git a/src/BanNode.c b/src/BanNode.c new file mode 100644 index 0000000..df3e38e --- /dev/null +++ b/src/BanNode.c @@ -0,0 +1,93 @@ +/* BanNode.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "BanNode.h" +#include "ChanNode.h" +#include "tools.h" + +struct BanNode* addChannelBan(struct ChanNode *chan, char *mask) { + struct BanNode *ban = malloc(sizeof(*ban)); + ban->chan = chan; + ban->mask = strdup(mask); + SYNCHRONIZE(cache_sync); + ban->next = chan->bans; + chan->bans = ban; + DESYNCHRONIZE(cache_sync); + return ban; +} + +struct BanNode* getMatchingChannelBan(struct ChanNode *chan, char *mask) { + SYNCHRONIZE(cache_sync); + struct BanNode *cban; + for(cban = chan->bans; cban; cban = cban->next) { + if(!match(cban->mask, mask)) { + DESYNCHRONIZE(cache_sync); + return cban; + } + } + DESYNCHRONIZE(cache_sync); + return NULL; +} + +void removeChannelBanMask(struct ChanNode *chan, char *mask) { + SYNCHRONIZE(cache_sync); + struct BanNode *cban, *last = NULL; + for(cban = chan->bans; cban; cban = cban->next) { + if(!strcmp(cban->mask, mask)) { + if(last) + last->next = cban->next; + else + chan->bans = cban->next; + free(cban->mask); + free(cban); + break; + } else + last = cban; + } + DESYNCHRONIZE(cache_sync); +} + +void removeChannelBan(struct BanNode *ban) { + SYNCHRONIZE(cache_sync); + struct BanNode *cban, *last = NULL; + struct ChanNode *chan = ban->chan; + for(cban = chan->bans; cban; cban = cban->next) { + if(cban == ban) { + if(last) + last->next = ban->next; + else + chan->bans = ban->next; + free(ban->mask); + free(ban); + break; + } else + last = cban; + } + DESYNCHRONIZE(cache_sync); +} + +void removeChannelBans(struct ChanNode *chan) { + SYNCHRONIZE(cache_sync); + struct BanNode *ban, *next; + for(ban = chan->bans; ban; ban = next) { + next = ban->next; + free(ban->mask); + free(ban); + } + chan->bans = NULL; + DESYNCHRONIZE(cache_sync); +} diff --git a/src/BanNode.h b/src/BanNode.h new file mode 100644 index 0000000..8fa96fd --- /dev/null +++ b/src/BanNode.h @@ -0,0 +1,38 @@ +/* BanNode.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _BanNode_h +#define _BanNode_h +#include "main.h" + +struct ChanNode; + +struct BanNode { + char *mask; + struct ChanNode *chan; + + struct BanNode *next; +}; + +#ifndef DND_FUNCTIONS +struct BanNode* addChannelBan(struct ChanNode *chan, char *mask); +/* MODULAR ACCESSIBLE */ struct BanNode* getMatchingChannelBan(struct ChanNode *chan, char *mask); +void removeChannelBanMask(struct ChanNode *chan, char *mask); +void removeChannelBan(struct BanNode *ban); +void removeChannelBans(struct ChanNode *chan); +#endif + +#endif \ No newline at end of file diff --git a/src/ChanNode.c b/src/ChanNode.c new file mode 100644 index 0000000..44a335f --- /dev/null +++ b/src/ChanNode.c @@ -0,0 +1,260 @@ +/* ChanNode.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#include "ChanNode.h" +#include "ChanUser.h" +#include "UserNode.h" +#include "BanNode.h" +#include "modcmd.h" +#include "ModeNode.h" +#include "IRCEvents.h" +#include "tools.h" +#include "log.h" + +static struct ChanNode **chanList; + +void init_ChanNode() { + /* + len pos chars + 26 0 a-z + 10 26 0-9 + 10 36 {|}~[\]^_` + 1 46 *everything else* + --------------------------- + = 47 + */ + #define CHANNEL_LIST_SIZE 47 + chanList = calloc(CHANNEL_LIST_SIZE, sizeof(*chanList)); +} + +void free_ChanNode() { + SYNCHRONIZE(cache_sync); + //kamikaze free all channels and chanusers + int i; + struct ChanNode *chan, *next; + struct ChanUser *chanuser, *next_chanuser; + for(i = 0; i < CHANNEL_LIST_SIZE; i++) { + for(chan = chanList[i]; chan; chan = next) { + next = chan->next; + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = next_chanuser) { + next_chanuser = getChannelUsers(chan, chanuser); + freeChanUser(chanuser); + } + freeChanNode(chan); + } + } + free(chanList); + DESYNCHRONIZE(cache_sync); +} + +int is_valid_chan(const char *name) { + unsigned int ii; + if (*name !='#') + return 0; + for (ii=1; name[ii]; ++ii) { + if ((name[ii] > 0) && (name[ii] <= 32)) + return 0; + if (name[ii] == ',') + return 0; + if (name[ii] == '\xa0') + return 0; + } + return 1; +} + +static int get_chanlist_entry(int name) { + if((name > 0 && name <= 32) || name == ',' || name == '\xa0') return -1; //invalid name + if(tolower(name) >= 97 && tolower(name) <= 122) { + return (tolower(name) - 97); + } + if(tolower(name) >= 48 && tolower(name) <= 57) { + return (tolower(name) - 48 + 26); + } + /* {|}~[\]^_` */ + if(name == '{') return 36; + if(name == '|') return 37; + if(name == '}') return 38; + if(name == '~') return 39; + if(name == '[') return 40; + if(name == '\\') return 41; + if(name == ']') return 42; + if(name == '^') return 43; + if(name == '_') return 44; + if(name == '`') return 45; + return 46; +} + +struct ChanNode* getAllChans(struct ChanNode *last) { + if(last == NULL || last->next == NULL) { + int cindex; + if(last == NULL) + cindex = 0; + else + cindex = get_chanlist_entry(last->name[1]) + 1; + while(chanList[cindex] == NULL && cindex < CHANNEL_LIST_SIZE) + cindex++; + if(cindex >= CHANNEL_LIST_SIZE) return NULL; + return chanList[cindex]; + } else { + return last->next; + } +} + +struct ChanNode* getChanByName(const char *name) { //case insensitive + int chanListIndex = get_chanlist_entry(name[1]); + if(chanListIndex == -1 || chanList[chanListIndex] == NULL) + return NULL; + struct ChanNode *chan; + for(chan = chanList[chanListIndex]; chan; chan = chan->next) { + if(!stricmp(name, chan->name)) + return chan; + } + return NULL; +} + +struct ChanNode* addChannel(const char *name) { + int chanListIndex = get_chanlist_entry(name[1]); + if(chanListIndex == -1 || !is_valid_chan(name)) + return NULL; + struct ChanNode *chan = malloc(sizeof(*chan)); + if (!chan) + { + printf_log("main", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return NULL; + } + strcpy(chan->name, name); + chan->user = NULL; + chan->bans = NULL; + chan->spam_settings = NULL; + chan->usercount = 0; + chan->botcount = 0; + chan->topic[0] = 0; + chan->flags = 0; + /* mode lists */ + chan->modes = createModeNode(chan); + chan->trigger = NULL; + + SYNCHRONIZE(cache_sync); + chan->next = chanList[chanListIndex]; + chanList[chanListIndex] = chan; + DESYNCHRONIZE(cache_sync); + return chan; +} + +int getChannelCount() { + int i, count = 0; + struct ChanNode *chan; + for(i = 0; i < CHANNEL_LIST_SIZE; i++) { + for(chan = chanList[i]; chan; chan = chan->next) { + count++; + } + } + return count; +} + +int getChanUserCount() { + int i, count = 0; + struct ChanNode *chan; + for(i = 0; i < CHANNEL_LIST_SIZE; i++) { + for(chan = chanList[i]; chan; chan = chan->next) { + count += chan->usercount; + } + } + return count; +} + +int getChanBanCount() { + int i, count = 0; + struct ChanNode *chan; + struct BanNode *ban; + for(i = 0; i < CHANNEL_LIST_SIZE; i++) { + for(chan = chanList[i]; chan; chan = chan->next) { + for(ban = chan->bans; ban; ban = ban->next) + count ++; + } + } + return count; +} + +void delChannel(struct ChanNode* chan, int freeChan) { + int chanListIndex = get_chanlist_entry(chan->name[1]); + if(chanListIndex == -1) return; + SYNCHRONIZE(cache_sync); + struct ChanNode *cchan, *last_chan = NULL; + for(cchan = chanList[chanListIndex]; cchan; cchan = cchan->next) { + if(cchan == chan) { + if(last_chan) + last_chan->next = chan->next; + else + chanList[chanListIndex] = chan->next; + break; + } else + last_chan = cchan; + } + if(chan->user) { + //free all chanusers + struct ChanUser *chanuser, *next; + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = next) { + next = getChannelUsers(chan, chanuser); + removeChanUserFromLists(chanuser, 0, 1, 1); + } + } + if(freeChan) + freeChanNode(chan); + else + chan->next = NULL; + DESYNCHRONIZE(cache_sync); +} + +void freeChanNode(struct ChanNode* chan) { + event_freechan(chan); + if(chan->trigger) { + struct trigger_cache *trigger, *next_trigger; + for(trigger = chan->trigger; trigger; trigger = next_trigger) { + next_trigger = trigger->next; + free(trigger->trigger); + free(trigger); + } + } + freeModeNode(chan->modes); + if(chan->bans) + removeChannelBans(chan); + free(chan); +} + +int checkChannelVisibility(struct ChanNode* chan) { + struct ChanUser *chanuser, *next; + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + if(chanuser->user->flags & USERFLAG_ISBOT) + return 1; + } + //free the channel... + SYNCHRONIZE(cache_sync); + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = next) { + next = getChannelUsers(chan, chanuser); + //remove the channel from the user's channel-list + removeChanUserFromLists(chanuser, 0, 1, 0); + if(!chanuser->user->channel) { + //free the user (no more channels) + delUser(chanuser->user, 1); + } + freeChanUser(chanuser); + } + chan->user = NULL; + delChannel(chan, 1); + DESYNCHRONIZE(cache_sync); + return 0; +} diff --git a/src/ChanNode.h b/src/ChanNode.h new file mode 100644 index 0000000..fe3a4de --- /dev/null +++ b/src/ChanNode.h @@ -0,0 +1,70 @@ +/* ChanNode.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _ChanNode_h +#define _ChanNode_h +#include "main.h" + +struct ChanUser; +struct trigger_cache; +struct ModeNode; +struct NeonSpamSettings; +struct timeq_entry; + +#define CHANFLAG_RECEIVED_USERLIST 0x01 +#define CHANFLAG_REQUESTED_CHANINFO 0x02 +#define CHANFLAG_CHAN_REGISTERED 0x04 +#define CHANFLAG_HAVE_INVISIBLES 0x08 +#define CHANFLAG_REJOINING 0x10 + +#define CHANFLAG_SCRIPTFLAG1 0x80 /* used by neonserv_cmd_search */ + +struct ChanNode { + char name[CHANNELLEN+1]; + char topic[TOPICLEN+1]; + struct ChanUser *user; + unsigned int usercount; + unsigned int botcount; + unsigned char flags; + struct ModeNode *modes; + struct BanNode *bans; + + struct trigger_cache *trigger; + int channel_id; + + struct NeonSpamSettings *spam_settings; + + void *rejoin_bots; + struct timeq_entry *rejoin_timeout; + + struct ChanNode *next; +}; + +#ifndef DND_FUNCTIONS +void init_ChanNode(); +void free_ChanNode(); +/* MODULAR ACCESSIBLE */ int is_valid_chan(const char *name); +/* MODULAR ACCESSIBLE */ struct ChanNode* getAllChans(struct ChanNode *last); +/* MODULAR ACCESSIBLE */ struct ChanNode* getChanByName(const char *name); +struct ChanNode* addChannel(const char *chan); +/* MODULAR ACCESSIBLE */ int getChannelCount(); +/* MODULAR ACCESSIBLE */ int getChanUserCount(); +/* MODULAR ACCESSIBLE */ int getChanBanCount(); +void delChannel(struct ChanNode* chan, int freeChan); +void freeChanNode(struct ChanNode* chan); +int checkChannelVisibility(struct ChanNode* chan); +#endif +#endif \ No newline at end of file diff --git a/src/ChanUser.c b/src/ChanUser.c new file mode 100644 index 0000000..e18ce4d --- /dev/null +++ b/src/ChanUser.c @@ -0,0 +1,208 @@ +/* ChanUser.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "ChanUser.h" +#include "ChanNode.h" +#include "ModeNode.h" +#include "UserNode.h" +#include "log.h" + +struct ChanUser* addChanUser(struct ChanNode *chan, struct UserNode *user) { + struct ChanUser *chanuser = malloc(sizeof(*chanuser)); + if (!chanuser) + { + printf_log("main", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return NULL; + } + chanuser->flags = 0; + chanuser->user = user; + chanuser->chan = chan; + chanuser->visCount = 0; + + chanuser->changeTime = 0; + chanuser->spamnode = NULL; + + SYNCHRONIZE(cache_sync); + + chanuser->next_user = chan->user; + chan->user = chanuser; + chan->usercount++; + + chanuser->next_chan = user->channel; + user->channel = chanuser; + + DESYNCHRONIZE(cache_sync); + + return chanuser; +} + +struct ChanUser* addInvisibleChanUser(struct ChanNode *chan, struct UserNode *user) { + struct ChanUser *chanuser = malloc(sizeof(*chanuser)); + if (!chanuser) + { + printf_log("main", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return NULL; + } + chanuser->flags = CHANUSERFLAG_INVISIBLE; + chanuser->user = user; + chanuser->chan = chan; + chanuser->visCount = 0; + + chanuser->changeTime = 0; + chanuser->spamnode = NULL; + + SYNCHRONIZE(cache_sync); + chanuser->next_user = chan->user; + chan->user = chanuser; + DESYNCHRONIZE(cache_sync); + chan->usercount++; + + return chanuser; +} + +int isUserOnChan(struct UserNode *user, struct ChanNode *chan) { + struct ChanUser *chanuser; + if(isModeSet(chan->modes, 'd') || isModeSet(chan->modes, 'D')) { + for(chanuser = chan->user; chanuser; chanuser = chanuser->next_user) { + if(chanuser->user == user) + return 1; + } + } else { + for(chanuser = user->channel; chanuser; chanuser = chanuser->next_chan) { + if(chanuser->chan == chan) + return 1; + } + } + return 0; +} + +struct ChanUser* getChanUser(struct UserNode *user, struct ChanNode *chan) { + struct ChanUser *chanuser; + if(isModeSet(chan->modes, 'd') || isModeSet(chan->modes, 'D')) { + for(chanuser = chan->user; chanuser; chanuser = chanuser->next_user) { + if(chanuser->user == user) + return chanuser; + } + } else { + for(chanuser = user->channel; chanuser; chanuser = chanuser->next_chan) { + if(chanuser->chan == chan) + return chanuser; + } + } + return NULL; +} + +struct ChanUser* getChannelUsers(struct ChanNode *chan, struct ChanUser *last) { + if(last == NULL) + return chan->user; + else + return last->next_user; +} + +struct ChanUser* getUserChannels(struct UserNode *user, struct ChanUser *last) { + if(last == NULL) + return user->channel; + else + return last->next_chan; +} + +void delChanUser(struct ChanUser *chanuser, int do_freeChanUser) { + SYNCHRONIZE(cache_sync); + struct ChanUser *cchanuser, *last; + //remove it from the user's channel-list + last = NULL; + for(cchanuser = chanuser->user->channel; cchanuser; cchanuser = cchanuser->next_chan) { + if(cchanuser == chanuser) { + if(last) + last->next_chan = chanuser->next_chan; + else + chanuser->user->channel = chanuser->next_chan; + break; + } else + last = cchanuser; + } + + //remove it from the channel's user-list + last = NULL; + for(cchanuser = chanuser->chan->user; cchanuser; cchanuser = cchanuser->next_user) { + if(cchanuser == chanuser) { + chanuser->chan->usercount--; + if(last) + last->next_user = chanuser->next_user; + else + chanuser->chan->user = chanuser->next_user; + break; + } else + last = cchanuser; + } + + if(do_freeChanUser) { + freeChanUser(chanuser); + } else { + chanuser->next_chan = NULL; + chanuser->next_user = NULL; + } + DESYNCHRONIZE(cache_sync); +} + +void removeChanUserFromLists(struct ChanUser *chanuser, int remove_from_userlist, int remove_from_channellist, int do_freeChanUser) { + SYNCHRONIZE(cache_sync); + struct ChanUser *cchanuser, *last; + if(remove_from_userlist) { + //remove it from the channel's user-list + last = NULL; + for(cchanuser = chanuser->chan->user; cchanuser; cchanuser = cchanuser->next_user) { + if(cchanuser == chanuser) { + chanuser->chan->usercount--; + if(last) + last->next_user = chanuser->next_user; + else + chanuser->chan->user = chanuser->next_user; + break; + } else + last = cchanuser; + } + chanuser->next_user = NULL; + } + if(remove_from_channellist) { + //remove it from the user's channel-list + last = NULL; + for(cchanuser = chanuser->user->channel; cchanuser; cchanuser = cchanuser->next_chan) { + if(cchanuser == chanuser) { + if(last) + last->next_chan = chanuser->next_chan; + else + chanuser->user->channel = chanuser->next_chan; + break; + } else + last = cchanuser; + } + chanuser->next_chan = NULL; + } + + if(do_freeChanUser) { + freeChanUser(chanuser); + } + DESYNCHRONIZE(cache_sync); +} + +void freeChanUser(struct ChanUser *chanuser) { + if(chanuser->spamnode) + free(chanuser->spamnode); + free(chanuser); +} + diff --git a/src/ChanUser.h b/src/ChanUser.h new file mode 100644 index 0000000..62d8361 --- /dev/null +++ b/src/ChanUser.h @@ -0,0 +1,61 @@ +/* ChanUser.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _ChanUser_h +#define _ChanUser_h +#include "main.h" + +#define CHANUSERFLAG_OPPED 0x01 +#define CHANUSERFLAG_VOICED 0x02 +#define CHANUSERFLAG_INVISIBLE 0x04 +#define CHANUSERFLAG_HALFOPPED 0x08 +#define CHANUSERFLAG_PARTING 0x10 + +#define CHANUSERFLAG_OPPED_OR_VOICED (CHANUSERFLAG_OPPED | CHANUSERFLAG_HALFOPPED | CHANUSERFLAG_VOICED) + +struct ChanNode; +struct UserNode; +struct NeonSpamNode; + +struct ChanUser { + unsigned char flags; + struct ChanNode *chan; + struct UserNode *user; + + int visCount : 8; //visible counter - if this is 0, the ChanUser gets removed + int old_visCount : 8; + + int chageEvents; + time_t changeTime; + + struct NeonSpamNode *spamnode; + + struct ChanUser *next_user; + struct ChanUser *next_chan; +}; + +#ifndef DND_FUNCTIONS +struct ChanUser* addChanUser(struct ChanNode *chan, struct UserNode *user); +struct ChanUser* addInvisibleChanUser(struct ChanNode *chan, struct UserNode *user); +/* MODULAR ACCESSIBLE */ int isUserOnChan(struct UserNode *user, struct ChanNode *chan); +/* MODULAR ACCESSIBLE */ struct ChanUser* getChanUser(struct UserNode *user, struct ChanNode *chan); +/* MODULAR ACCESSIBLE */ struct ChanUser* getChannelUsers(struct ChanNode *chan, struct ChanUser *last); +/* MODULAR ACCESSIBLE */ struct ChanUser* getUserChannels(struct UserNode *user, struct ChanUser *last); +void delChanUser(struct ChanUser *chanuser, int freeChanUser); +void removeChanUserFromLists(struct ChanUser *chanuser, int remove_from_userlist, int remove_from_channellist, int freeChanUser); +void freeChanUser(struct ChanUser *chanuser); +#endif +#endif diff --git a/src/ClientSocket.c b/src/ClientSocket.c new file mode 100644 index 0000000..a624a30 --- /dev/null +++ b/src/ClientSocket.c @@ -0,0 +1,313 @@ +/* ClientSocket.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#include "main.h" +#include "ClientSocket.h" +#include "IRCParser.h" +#include "UserNode.h" +#include "IRCQueue.h" +#include "WHOHandler.h" +#include "HandleInfoHandler.h" +#include "ConfigParser.h" +#include "version.h" +#include "IOHandler.h" +#include "IRCEvents.h" +#include "log.h" + +struct socket_list { + struct ClientSocket *data; + unsigned count; +}; + +#ifdef HAVE_THREADS +static pthread_mutex_t synchronized; +static pthread_mutex_t synchronized_recv; + +struct ParseOrder { + unsigned int tid; + struct ParseOrder *next; +}; +struct ParseOrder *parse_order = NULL; +#endif + +//the magic list :P +static struct socket_list *sockets = NULL; + +static IOHANDLER_CALLBACK(socket_callback); + +void init_sockets() { + THREAD_MUTEX_INIT(synchronized); + THREAD_MUTEX_INIT(synchronized_recv); + + sockets = malloc(sizeof(*sockets)); + if (!sockets) + { + printf_log("main", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + sockets->data = NULL; + sockets->count = 0; +} + +struct ClientSocket* create_socket(char *host, int port, char *bindto, char *pass, char *nick, char *ident, char *realname) { + if(!sockets) + init_sockets(); + struct ClientSocket *client = malloc(sizeof(*client)); + if (!client) { + return NULL; + } + client->iofd = NULL; + client->host = strdup(host); + client->port = port; + client->bind = (bindto ? strdup(bindto) : NULL); + client->pass = (pass == NULL ? NULL : strdup(pass)); + client->nick = strdup(nick); + client->ident = strdup(ident); + client->realname = strdup(realname); + client->user = NULL; + client->network_name = NULL; + client->flags = 0; + client->traffic_in = 0; + client->traffic_out = 0; + client->connection_time = 0; + client->botid = 0; + client->clientid = 0; + client->queue = NULL; + client->whoqueue_first = NULL; + client->whoqueue_last = NULL; + client->handleinfo_first = NULL; + client->handleinfo_last = NULL; + SYNCHRONIZE(synchronized); + client->next = sockets->data; + sockets->data = client; + DESYNCHRONIZE(synchronized); + return client; +} + +void connect_socket(struct ClientSocket *client) { + client->iofd = iohandler_connect(client->host, client->port, ((client->flags & SOCKET_FLAG_SSL) ? 1 : 0), client->bind, socket_callback); + client->iofd->data = client; + client->flags |= SOCKET_FLAG_RECONNECT; +} + +static int _close_socket(struct ClientSocket *client) { + if(client == NULL) return 0; + if((client->flags & SOCKET_FLAG_CONNECTED)) { + iohandler_printf(client->iofd, "QUIT :[NeonServ %s.%d] disconnect requested.\n", NEONSERV_VERSION, patchlevel); + + bot_disconnect(client); + + iohandler_close(client->iofd); + client->iofd = NULL; + } + client->flags &= ~(SOCKET_FLAG_READY | SOCKET_FLAG_CONNECTED); + if(client->queue) + queue_destroy(client); + if(client->whoqueue_first) + clear_whoqueue(client); + if(client->handleinfo_first) + clear_handleinfoqueue(client); + return 1; +} + +int close_socket(struct ClientSocket *client) { //external call (don't reconnect) + if(client == NULL) return 0; + client->flags &= ~SOCKET_FLAG_RECONNECT; + return _close_socket(client); +} + +int destroy_socket(struct ClientSocket *client) { + if(client == NULL) return 0; + close_socket(client); + SYNCHRONIZE(synchronized); + struct ClientSocket *sock, *last_sock = NULL; + for (sock = sockets->data; sock; sock = sock->next) { + if(sock == client) { + if(last_sock) + last_sock->next = sock->next; + else + sockets->data = sock->next; + sockets->count--; + break; + } else + last_sock = sock; + } + event_freeclient(client); + free(client->host); + if(client->bind) + free(client->bind); + if(client->pass) + free(client->pass); + if(client->network_name) + free(client->network_name); + if(client->iofd) //reconnect timer? + iohandler_close(client->iofd); + free(client); + DESYNCHRONIZE(synchronized); + return 1; +} + +int write_socket_force(struct ClientSocket *client, char* msg, int len) { + if(!(client && (client->flags & SOCKET_FLAG_CONNECTED))) return 0; + SYNCHRONIZE(synchronized); + #ifdef HAVE_THREADS + printf_log("main", LOG_IRCRAW, "[%d send %d] %s", getCurrentThreadID(), len, msg); + #else + printf_log("main", LOG_IRCRAW, "[send %d] %s", len, msg); + #endif + iohandler_send(client->iofd, msg, len); + client->traffic_out += len; + DESYNCHRONIZE(synchronized); + return 1; +} + +int write_socket(struct ClientSocket *client, char* msg, int len) { + if(!(client && (client->flags & SOCKET_FLAG_CONNECTED))) return 0; + if(client->flags & SOCKET_FLAG_USE_QUEUE) + return queue_add(client, msg, len); + else + return write_socket_force(client, msg, len); +} + +#if HAVE_THREADS +static void clientsocket_start_of_recv(unsigned int tid) { + SYNCHRONIZE(whohandler_sync); + struct ParseOrder *entry, *last; + for(last = parse_order; last; last = last->next) { + if(last->next == NULL) + break; + } + entry = malloc(sizeof(*entry)); + entry->tid = tid; + entry->next = NULL; + if(last) + last->next = entry; + else + parse_order = entry; + DESYNCHRONIZE(whohandler_sync); +} + +static void clientsocket_end_of_recv(unsigned int tid) { + SYNCHRONIZE(whohandler_sync); + struct ParseOrder *entry, *last = NULL; + for(entry = parse_order; entry; entry = entry->next) { + if(entry->tid == tid) { + if(last) + last->next = entry->next; + else + parse_order = entry->next; + free(entry); + break; + } else + last = entry; + } + DESYNCHRONIZE(whohandler_sync); +} + +int clientsocket_parseorder_top(unsigned int tid) { + if(parse_order && parse_order->tid == tid) + return 1; + else + return 0; +} +#endif + +static IOHANDLER_CALLBACK(socket_callback) { + struct ClientSocket *client = event->iofd->data; + #ifdef HAVE_THREADS + unsigned int tid; + #endif + if(process_state.running == 0) + return; //just ignore the event (shutdown sequence) + switch(event->type) { + case IOEVENT_CONNECTED: + client->flags |= SOCKET_FLAG_CONNECTED; + if(client->pass && strcmp(client->pass, "")) + putsock(client, "PASS :%s", client->pass); + putsock(client, "USER %s 0 0 :%s", client->ident, client->realname); + putsock(client, "NICK %s", client->nick); + break; + case IOEVENT_NOTCONNECTED: + case IOEVENT_CLOSED: + _close_socket(client); + if(client->flags & SOCKET_FLAG_RECONNECT) { + struct timeval timeout; + gettimeofday(&timeout, NULL); + timeout.tv_sec += SOCKET_RECONNECT_TIME; + client->iofd = iohandler_timer(timeout, socket_callback); + client->iofd->data = client; + } + break; + case IOEVENT_TIMEOUT: //reconnect timer + connect_socket(client); + break; + case IOEVENT_RECV: + #ifdef HAVE_THREADS + tid = (unsigned int) pthread_self_tid(); + clientsocket_start_of_recv(tid); + #endif + client->traffic_in += strlen(event->data.recv_str); + parse_line(client, event->data.recv_str); + #ifdef HAVE_THREADS + clientsocket_end_of_recv(tid); + #endif + break; + default: + break; + } +} + +void putsock(struct ClientSocket *client, const char *text, ...) { + va_list arg_list; + char sendBuf[MAXLEN]; + int pos; + if (!(client && (client->flags & SOCKET_FLAG_CONNECTED))) return; + sendBuf[0] = '\0'; + va_start(arg_list, text); + pos = vsnprintf(sendBuf, MAXLEN - 2, text, arg_list); + va_end(arg_list); + if (pos < 0 || pos > (MAXLEN - 2)) pos = MAXLEN - 2; + sendBuf[pos] = '\n'; + sendBuf[pos+1] = '\0'; + write_socket(client, sendBuf, pos+1); +} + +struct ClientSocket* getBots(int flags, struct ClientSocket* last_bot) { + struct ClientSocket *sock = (last_bot ? last_bot->next : sockets->data); + if(sock == NULL) return NULL; + for (; sock; sock = sock->next) { + if(!flags || (sock->flags & flags) == flags) + return sock; + } + return NULL; +} + +void free_sockets(int close_only) { + if(!sockets) return; + struct ClientSocket *client, *next; + for (client = sockets->data; client; client = next) { + next = client->next; + if(close_only) { + if((client->flags & SOCKET_FLAG_CONNECTED)) + iohandler_printf(client->iofd, "QUIT :[NeonServ %s.%d] shutdown requested.\n", NEONSERV_VERSION, patchlevel); + } else + destroy_socket(client); + } + if(!close_only) { + free(sockets); + sockets = NULL; + } +} diff --git a/src/ClientSocket.h b/src/ClientSocket.h new file mode 100644 index 0000000..9e73fd1 --- /dev/null +++ b/src/ClientSocket.h @@ -0,0 +1,97 @@ +/* ClientSocket.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _ClientSocket_h +#define _ClientSocket_h + +#include "main.h" + +#define SOCKET_FLAG_CONNECTED 0x02 +#define SOCKET_FLAG_READY 0x04 +#define SOCKET_FLAG_PREFERRED 0x08 /* prefered bot to send datas to the IRC World (NOTICE's WHO's etc pp) */ +#define SOCKET_FLAG_USE_QUEUE 0x10 +#define SOCKET_FLAG_RECONNECT 0x20 +#define SOCKET_FLAG_SSL 0x40 + +#define SOCKET_FLAG_FAST_JUMP 0x200 +#define SOCKET_FLAG_SILENT 0x400 +#define SOCKET_FLAG_CHANGENICK 0x800 +#define SOCKET_FLAG_REQUEST_INVITE 0x1000 +#define SOCKET_FLAG_REQUEST_OP 0x2000 +#define SOCKET_FLAG_SECRET_BOT 0x4000 + +#define SOCKET_HAVE_BOTCLASSVALUE1 0x10000000 +#define SOCKET_HAVE_BOTCLASSVALUE2 0x20000000 +#define SOCKET_HAVE_BOTCLASSVALUE3 0x40000000 + +#define BUF_SIZ 512 + +struct UserNode; +struct trigger_cache; +struct IODescriptor; + +struct ClientSocket { + struct IODescriptor *iofd; + unsigned int flags; + char *host; + int port; + char *bind; + char *pass; + char *nick; + char *ident; + char *realname; + struct UserNode *user; + char *network_name; + unsigned long traffic_in; + unsigned long traffic_out; + time_t connection_time; + + struct BotQueue *queue; + + struct WHOQueueEntry *whoqueue_first; + struct WHOQueueEntry *whoqueue_last; + + struct HandleInfoQueueEntry *handleinfo_first; + struct HandleInfoQueueEntry *handleinfo_last; + + int botid : 16; + int clientid : 16; + + void *botclassvalue1; + void *botclassvalue2; + void *botclassvalue3; + + void *changenick_channels; //parted channels due raw 437 + + struct ClientSocket *next; +}; + +#ifndef DND_FUNCTIONS +/* MODULAR ACCESSIBLE */ struct ClientSocket* create_socket(char *host, int port, char *bindto, char *pass, char *nick, char *ident, char *realname); +/* MODULAR ACCESSIBLE */ void connect_socket(struct ClientSocket *client); +/* MODULAR ACCESSIBLE */ int close_socket(struct ClientSocket *client); +/* MODULAR ACCESSIBLE */ int destroy_socket(struct ClientSocket *client); +int write_socket_force(struct ClientSocket *client, char* msg, int len); +/* MODULAR ACCESSIBLE */ int write_socket(struct ClientSocket *client, char* msg, int len); +#ifdef HAVE_THREADS +int clientsocket_parseorder_top(unsigned int tid); +#endif +/* MODULAR ACCESSIBLE */ void putsock(struct ClientSocket *client, const char *text, ...) PRINTF_LIKE(2, 3); +/* MODULAR ACCESSIBLE */ struct ClientSocket* getBots(int flags, struct ClientSocket* last_bot); +void init_sockets(); +void free_sockets(int close_only); +#endif +#endif diff --git a/src/ConfigParser.c b/src/ConfigParser.c new file mode 100644 index 0000000..050d6b4 --- /dev/null +++ b/src/ConfigParser.c @@ -0,0 +1,377 @@ +/* ConfigParser.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "ConfigParser.h" +#include "tools.h" + +#define ENTRYTYPE_BLOCK 1 +#define ENTRYTYPE_STRING 2 +#define ENTRYTYPE_INTEGER 3 + +struct ConfigEntry { + char *name; + int type; + void *value; + struct ConfigEntry *next; +}; + +static struct ConfigEntry *root_entry = NULL; + +static char *parse_config_recursive(struct ConfigEntry *centry, char *buffer, int root_element); +static void free_entry_rekursiv(struct ConfigEntry *centry, int is_root_entry); + +int loadConfig(const char *filename) { + FILE *f; + f = fopen(filename, "rb"); + if(!f) return 0; + + // obtain file size: + long lSize; + fseek (f, 0, SEEK_END); + lSize = ftell(f); + rewind(f); + + // allocate memory to contain the whole file: + char *buffer = (char*) malloc (sizeof(char)*lSize + 1); + if (buffer == NULL) { + fclose(f); + return 0; + } + + // copy the file into the buffer: + size_t result = fread (buffer, 1, lSize, f); + if (result != lSize) { + fclose(f); + return 0; + } + buffer[lSize] = '\0'; + + fclose(f); + + // now parse the config file... + if(root_entry) { + free_loaded_config(); + } + root_entry = malloc(sizeof(*root_entry)); + if (!root_entry) return 0; + parse_config_recursive(root_entry, buffer, 1); + + // free the buffer... + free(buffer); + return 1; +} + +#define PARSER_FLAG_ESCAPED 0x01 +#define PARSER_FLAG_BLOCK 0x02 +#define PARSER_FLAG_STRING 0x04 +#define PARSER_FLAG_EXPECT_END 0x08 +#define PARSER_FLAG_NEXTCHAR 0x10 +#define PARSER_FLAG_INTEGER 0x20 +#define PARSER_FLAG_EOBN 0x40 /* End of Block name */ +#define PARSER_FLAG_COMMAND 0x80 +static char *parse_config_recursive(struct ConfigEntry *centry, char *buffer, int root_element) { + int flags = 0; + int type = (root_element ? ENTRYTYPE_BLOCK : 0); + char cbuf[1024]; + int cbufpos = 0; + struct ConfigEntry *sub_entrys = NULL; + while(*buffer) { + if(flags & PARSER_FLAG_NEXTCHAR) { + buffer++; + if(*buffer == '\0') + break; + } + flags |= PARSER_FLAG_NEXTCHAR; + if(flags & PARSER_FLAG_EOBN) { + flags &= ~(PARSER_FLAG_EOBN | PARSER_FLAG_NEXTCHAR); + struct ConfigEntry *new_entry = malloc(sizeof(*new_entry)); + if (!new_entry) return buffer; + new_entry->name = strdup(cbuf); + buffer = parse_config_recursive(new_entry, buffer, 0); + if(sub_entrys) + new_entry->next = sub_entrys; + else + new_entry->next = NULL; + sub_entrys = new_entry; + centry->value = sub_entrys; + } + if(flags & PARSER_FLAG_ESCAPED) { + cbuf[cbufpos++] = *buffer; + flags &= ~PARSER_FLAG_ESCAPED; + continue; + } + if(flags & PARSER_FLAG_EXPECT_END) { + if(*buffer == ';') { + centry->type = type; + return (buffer+1); + } + continue; + } + if(flags & PARSER_FLAG_STRING) { + if(*buffer == '"') { + cbuf[cbufpos] = '\0'; + flags &= ~PARSER_FLAG_STRING; + if(type == ENTRYTYPE_STRING) { + flags |= PARSER_FLAG_EXPECT_END; + centry->value = strdup(cbuf); + } else if(type == ENTRYTYPE_BLOCK) { + flags |= PARSER_FLAG_EOBN; + } + } else if(*buffer == '\\') + flags |= PARSER_FLAG_ESCAPED; + else + cbuf[cbufpos++] = *buffer; + continue; + } + if(flags & PARSER_FLAG_INTEGER) { + if(!isdigit(*buffer)) { + cbuf[cbufpos] = '\0'; + flags &= ~PARSER_FLAG_INTEGER; + if(type == ENTRYTYPE_INTEGER) { + flags |= PARSER_FLAG_EXPECT_END; + int *intbuf = malloc(sizeof(int)); + *intbuf = atoi(cbuf); + centry->value = intbuf; + } + if(*buffer == ';') { + centry->type = type; + return (buffer+1); + } + } else + cbuf[cbufpos++] = *buffer; + continue; + } + if(flags & PARSER_FLAG_COMMAND) { + int found_command = 0; + char *tmp_buffer; + switch(*buffer) { + case '/': + tmp_buffer = buffer; + buffer = strchr(buffer, '\r'); + if(!buffer) + buffer = strchr(tmp_buffer, '\n'); + if(!buffer) + buffer = tmp_buffer + strlen(tmp_buffer)-1; + found_command = 1; + break; + case '*': + //simple search for the next */ + buffer = strstr(buffer, "*/")+1; + found_command = 1; + } + flags &= ~PARSER_FLAG_COMMAND; + if(found_command) + continue; + } + switch(*buffer) { + case '\\': + flags |= PARSER_FLAG_ESCAPED; + break; + case '/': + if(!(flags & PARSER_FLAG_STRING)) { + flags |= PARSER_FLAG_COMMAND; + } + break; + case '{': + flags |= PARSER_FLAG_BLOCK; + type = ENTRYTYPE_BLOCK; + break; + case '}': + if(flags & PARSER_FLAG_BLOCK) + flags &= ~PARSER_FLAG_BLOCK; + flags |= PARSER_FLAG_EXPECT_END; + break; + case '"': + flags |= PARSER_FLAG_STRING; + if(!type) + type = ENTRYTYPE_STRING; + cbufpos = 0; + break; + case ';': + centry->type = type; + return (buffer+1); + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if(!type) + type = ENTRYTYPE_INTEGER; + flags |= PARSER_FLAG_INTEGER; + cbufpos = 0; + cbuf[cbufpos++] = *buffer; + break; + default: + break; + } + } + centry->type = type; + return buffer; //end of the buffer +} + +int get_int_field(char *field_path) { + struct ConfigEntry *centry = root_entry; + char *a, *b = field_path; + struct ConfigEntry *subentry; + while((a = strstr(b, ".")) && centry) { + if(centry->type == ENTRYTYPE_BLOCK) { + int found = 0; + for(subentry = centry->value; subentry; subentry = subentry->next) { + if(!stricmplen(subentry->name, b, a-b)) { + centry = subentry; + found = 1; + break; + } + } + if(!found) + return 0; + } else + return 0; + b = a+1; + } + if(centry->type == ENTRYTYPE_BLOCK) { + int found = 0; + for(subentry = centry->value; subentry; subentry = subentry->next) { + if(!stricmp(subentry->name, b)) { + centry = subentry; + found = 1; + break; + } + } + if(!found) + return 0; + } else + return 0; + if(centry->type == ENTRYTYPE_INTEGER) + return ((int *)centry->value)[0]; + else + return 0; +} + +char *get_string_field(char *field_path) { + struct ConfigEntry *centry = root_entry; + char *a, *b = field_path; + struct ConfigEntry *subentry; + while((a = strstr(b, ".")) && centry) { + if(centry->type == ENTRYTYPE_BLOCK) { + int found = 0; + for(subentry = centry->value; subentry; subentry = subentry->next) { + if(!stricmplen(subentry->name, b, a-b)) { + centry = subentry; + found = 1; + break; + } + } + if(!found) + return NULL; + } else + return NULL; + b = a+1; + } + if(centry->type == ENTRYTYPE_BLOCK) { + int found = 0; + for(subentry = centry->value; subentry; subentry = subentry->next) { + if(!stricmp(subentry->name, b)) { + centry = subentry; + found = 1; + break; + } + } + if(!found) + return NULL; + } else + return NULL; + if(centry->type == ENTRYTYPE_STRING) + return centry->value; + else + return NULL; +} + +char **get_all_fieldnames(char *block_path) { + struct ConfigEntry *centry = root_entry; + char *a, *b = block_path; + struct ConfigEntry *subentry; + while((a = strstr(b, ".")) && centry) { + if(centry->type == ENTRYTYPE_BLOCK) { + int found = 0; + for(subentry = centry->value; subentry; subentry = subentry->next) { + if(!stricmplen(subentry->name, b, a-b)) { + centry = subentry; + found = 1; + break; + } + } + if(!found) + return NULL; + } else + return NULL; + b = a+1; + } + if(centry->type == ENTRYTYPE_BLOCK) { + int found = 0; + for(subentry = centry->value; subentry; subentry = subentry->next) { + if(!stricmp(subentry->name, b)) { + centry = subentry; + found = 1; + break; + } + } + if(!found) + return NULL; + } else + return NULL; + if(centry->type != ENTRYTYPE_BLOCK) return NULL; + int count = 0; + for(subentry = centry->value; subentry; subentry = subentry->next) { + count++; + } + char **fieldnames = calloc(count+1, sizeof(char *)); + count = 0; + for(subentry = centry->value; subentry; subentry = subentry->next) { + fieldnames[count++] = subentry->name; + } + return fieldnames; +} + +void free_loaded_config() { + if(root_entry) { + free_entry_rekursiv(root_entry, 1); + root_entry = NULL; + } +} + +static void free_entry_rekursiv(struct ConfigEntry *centry, int is_root_entry) { + if(centry->type == ENTRYTYPE_BLOCK) { + struct ConfigEntry *subentry, *nextentry; + for(subentry = centry->value; subentry; subentry = nextentry) { + nextentry = subentry->next; + free_entry_rekursiv(subentry, 0); + } + } else if(centry->type == ENTRYTYPE_STRING) { + free(centry->value); + } else if(centry->type == ENTRYTYPE_INTEGER) { + free(centry->value); + } + if(!is_root_entry) + free(centry->name); + free(centry); +} diff --git a/src/ConfigParser.h b/src/ConfigParser.h new file mode 100644 index 0000000..efdd237 --- /dev/null +++ b/src/ConfigParser.h @@ -0,0 +1,29 @@ +/* ConfigParser.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#ifndef _ConfigParser_h +#define _ConfigParser_h +#include "main.h" + +#ifndef DND_FUNCTIONS +int loadConfig(const char *filename); +/* MODULAR ACCESSIBLE */ int get_int_field(char *field_path); +/* MODULAR ACCESSIBLE */ char *get_string_field(char *field_path); +char **get_all_fieldnames(char *block_path); +void free_loaded_config(); +#endif +#endif diff --git a/src/DBHelper.c b/src/DBHelper.c new file mode 100644 index 0000000..6e2f843 --- /dev/null +++ b/src/DBHelper.c @@ -0,0 +1,365 @@ +/* DBHelper.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "DBHelper.h" +#include "UserNode.h" +#include "ChanNode.h" +#include "ChanUser.h" +#include "mysqlConn.h" +#include "lang.h" +#include "tools.h" +#include "IRCEvents.h" +#include "HandleInfoHandler.h" +#include "ClientSocket.h" +#include "bots.h" +#include "ConfigParser.h" +#include "log.h" + +void _loadUserSettings(struct UserNode *user) { + SYNCHRONIZE(cache_sync); + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `user_lang`, `user_reply_privmsg`, `user_god`, `user_id` FROM `users` WHERE `user_user` = '%s'", escape_string(user->auth)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + user->language = get_language_by_tag(row[0]); + if(user->language == NULL) user->language = get_default_language(); + if(strcmp(row[1], "0")) + user->flags |= USERFLAG_REPLY_PRIVMSG; + if(strcmp(row[2], "0")) + user->flags |= USERFLAG_GOD_MODE; + user->user_id = atoi(row[3]); + user->flags |= USERFLAG_HAS_USERID; + } else + user->language = get_default_language(); + user->flags |= USERFLAG_LOADED_SETTINGS; + DESYNCHRONIZE(cache_sync); +} + +int isGodMode(struct UserNode *user) { + loadUserSettings(user); + return (user->flags & USERFLAG_GOD_MODE); +} + +int getChannelAccess(struct UserNode *user, struct ChanNode *chan) { + if(!(user->flags & USERFLAG_ISAUTHED)) return 0; + loadChannelSettings(chan); + if(!(chan->flags & CHANFLAG_CHAN_REGISTERED)) return 0; + MYSQL_RES *res; + MYSQL_ROW row; + int caccess = 0; + int userid; + if(user->flags & USERFLAG_HAS_USERID) + userid = user->user_id; + else { + printf_mysql_query("SELECT `user_id` FROM `users` WHERE `user_user` = '%s'", escape_string(user->auth)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + userid = atoi(row[0]); + user->user_id = userid; + user->flags |= USERFLAG_HAS_USERID; + } else + return 0; + } + printf_mysql_query("SELECT `chanuser_access`, `chanuser_flags` FROM `chanusers` WHERE `chanuser_uid` = '%d' AND `chanuser_cid` = '%d'", userid, chan->channel_id); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + int cflags = atoi(row[1]); + if(!(cflags & DB_CHANUSER_SUSPENDED) && atoi(row[0]) > caccess) + caccess = atoi(row[0]); + } + return caccess; +} + +char *getChanDefault(char *channel_setting) { + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `%s` FROM `channels` WHERE `channel_name` = 'defaults'", channel_setting); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) == NULL) return ""; + return row[0]; +} + +int checkChannelAccess(struct UserNode *user, struct ChanNode *chan, char *channel_setting, int allow_501) { + loadChannelSettings(chan); + if(!(chan->flags & CHANFLAG_CHAN_REGISTERED)) return 0; + if((user->flags & USERFLAG_ISIRCOP)) return 1; + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `%s` FROM `channels` WHERE `channel_id` = '%d'", channel_setting, chan->channel_id); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) == NULL) return 0; + int require_access = atoi((row[0] ? row[0] : getChanDefault(channel_setting))); + if(require_access == 0) return 1; + if(!(user->flags & USERFLAG_ISAUTHED)) return 0; + int caccess = 0; + int userid; + if(user->flags & USERFLAG_HAS_USERID) + userid = user->user_id; + else { + printf_mysql_query("SELECT `user_id` FROM `users` WHERE `user_user` = '%s'", escape_string(user->auth)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + userid = atoi(row[0]); + user->user_id = userid; + user->flags |= USERFLAG_HAS_USERID; + } else + userid = -1; + } + if(userid > -1) { + printf_mysql_query("SELECT `chanuser_access`, `chanuser_flags` FROM `chanusers` WHERE `chanuser_uid` = '%d' AND `chanuser_cid` = '%d'", userid, chan->channel_id); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + int cflags = atoi(row[1]); + if(!(cflags & DB_CHANUSER_SUSPENDED)) + caccess = atoi(row[0]); + } + } + if(caccess >= require_access) return 1; + if(caccess == 500 && require_access == 501 && allow_501) return 1; + return 0; +} + +void _loadChannelSettings(struct ChanNode *chan) { + SYNCHRONIZE(cache_sync); + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `channel_id` FROM `channels` WHERE `channel_name` = '%s'", escape_string(chan->name)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + chan->flags |= CHANFLAG_CHAN_REGISTERED; + chan->channel_id = atoi(row[0]); + } + chan->flags |= CHANFLAG_REQUESTED_CHANINFO; + DESYNCHRONIZE(cache_sync); +} + +//TODO: fix performance: we should cache the user access +int isUserProtected(struct ChanNode *chan, struct UserNode *victim, struct UserNode *issuer) { + /* Don't protect if someone is attacking himself, or if the aggressor is an IRC Operator. */ + if(victim == issuer || (issuer->flags & USERFLAG_ISIRCOP)) return 0; + + /* Don't protect if no one is to be protected. */ + MYSQL_RES *res; + MYSQL_ROW row; + char protection; + loadChannelSettings(chan); + if(!(chan->flags & CHANFLAG_CHAN_REGISTERED)) return 0; + printf_mysql_query("SELECT `channel_protect` FROM `channels` WHERE `channel_id` = '%d'", chan->channel_id); + res = mysql_use(); + if(!(row = mysql_fetch_row(res))) return 0; + if(row[0]) { + protection = (char) atoi(row[0]); + } else { + printf_mysql_query("SELECT `channel_protect` FROM `channels` WHERE `channel_name` = 'defaults'"); + res = mysql_use(); + row = mysql_fetch_row(res); + protection = (char) atoi(row[0]); + } + if(protection == 3) return 0; + + /* Don't protect if the victim isn't added to the channel, unless we are to protect non-users also. */ + int victim_access = getChannelAccess(victim, chan); + if (!victim_access && protection != 0) return 0; + + /* Protect if the aggressor isn't a user because at this point, the aggressor can only be less than or equal to the victim. */ + int issuer_access = getChannelAccess(issuer, chan); + if (!issuer_access) return 1; + + /* If the aggressor was a user, then the victim can't be helped. */ + if(!victim_access) return 0; + + switch(protection) { + case 0: + case 1: + if(victim_access >= issuer_access) return 1; + break; + case 2: + if(victim_access > issuer_access) return 1; + break; + } + return 0; +} + +char *getBanAffectingMask(struct ChanNode *chan, char *mask) { + loadChannelSettings(chan); + if(!(chan->flags & CHANFLAG_CHAN_REGISTERED)) return 0; + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `ban_mask` FROM `bans` WHERE `ban_channel` = '%d'", chan->channel_id); + res = mysql_use(); + while ((row = mysql_fetch_row(res)) != NULL) { + if(!match(row[0], mask)) + return row[0]; + } + return NULL; +} + +int renameAccount(char *oldauth, char *newauth) { + MYSQL_RES *res, *res2; + MYSQL_ROW row, row2; + printf_mysql_query("SELECT `user_id` FROM `users` WHERE `user_user` = '%s'", escape_string(oldauth)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + int userid = atoi(row[0]); + printf_mysql_query("SELECT `user_id` FROM `users` WHERE `user_user` = '%s'", escape_string(newauth)); + res = mysql_use(); + if((row = mysql_fetch_row(res)) != NULL) { + //merge + int newuid = atoi(row[0]); + printf_mysql_query("SELECT `chanuser_id`, `chanuser_access`, `chanuser_flags` FROM `chanusers` WHERE `chanuser_uid` = '%d'", newuid); + res = mysql_use(); + while((row = mysql_fetch_row(res)) != NULL) { + printf_mysql_query("SELECT `chanuser_id`, `chanuser_access`, `chanuser_flags` FROM `chanusers` WHERE `chanuser_uid` = '%d'", userid); + res2 = mysql_use(); + if((row2 = mysql_fetch_row(res2)) != NULL) { + if(atoi(row[1]) > atoi(row2[1])) { + printf_mysql_query("UPDATE `chanusers` SET `chanuser_access` = '%s' WHERE `chanuser_id` = '%s'", row[1], row2[0]); + } + printf_mysql_query("DELETE FROM `chanusers` WHERE `chanuser_id` = '%s'", row[0]); + } else + printf_mysql_query("UPDATE `chanusers` SET `chanuser_uid` = '%d' WHERE `chanuser_id` = '%s'", userid, row[0]); + } + printf_mysql_query("UPDATE `channels` SET `channel_registrator` = '%d' WHERE `channel_registrator` = '%d'", userid, newuid); + printf_mysql_query("UPDATE `bans` SET `ban_owner` = '%d' WHERE `ban_owner` = '%d'", userid, newuid); + printf_mysql_query("UPDATE `events` SET `auth` = '%s' WHERE `auth` = '%s'", escape_string(newauth), escape_string(oldauth)); + printf_mysql_query("UPDATE `godlog` SET `godlog_uid` = '%d' WHERE `godlog_uid` = '%d'", userid, newuid); + printf_mysql_query("UPDATE `owner_history` SET `owner_history_to_uid` = '%d' WHERE `owner_history_to_uid` = '%d'", userid, newuid); + printf_mysql_query("UPDATE `owner_history` SET `owner_history_from_uid` = '%d' WHERE `owner_history_from_uid` = '%d'", userid, newuid); + printf_mysql_query("UPDATE `owner_history` SET `owner_history_from_uid` = '%d' WHERE `owner_history_from_uid` = '%d'", userid, newuid); + printf_mysql_query("UPDATE `noinvite` SET `uid` = '%d' WHERE `uid` = '%d'", userid, newuid); + printf_mysql_query("DELETE FROM `users` WHERE `user_id` = '%d'", newuid); + } + //simply rename the account + printf_mysql_query("UPDATE `users` SET `user_user` = '%s' WHERE `user_id` = '%d'", escape_string(newauth), userid); + char *alertchan = get_string_field("General.CheckAuths.alertchan"); + if(alertchan) { + struct ChanNode *alertchan_chan = getChanByName(alertchan); + struct ClientSocket *alertclient; + if(alertchan_chan && (alertclient = getChannelBot(alertchan_chan, 0)) != NULL) { + putsock(alertclient, "PRIVMSG %s :Renamed User %s to %s", alertchan_chan->name, oldauth, newauth); + } + } + return 1; + } + return 0; +} + +static AUTHLOOKUP_CALLBACK(event_user_registered_auth_lookup); + +struct event_user_registered_cache { + struct UserNode *user; + char *oldauth; +}; + +static void event_user_registered(struct UserNode *user, char *new_mask) { + //check if there is a fakehost on both sides... + //extract host from new_mask + char *new_host = strchr(new_mask, '@'); + if(new_host) + new_host++; + else + return; + if(!isFakeHost(user->host) || !isFakeHost(new_host)) + return; + //extract user names + char oldauth[AUTHLEN], newauth[AUTHLEN]; + char *p; + if((p = strstr(user->host, "."))) { + *p = '\0'; + strcpy(oldauth, user->host); + *p = '.'; + } + if((p = strstr(new_host, "."))) { + *p = '\0'; + strcpy(newauth, new_host); + *p = '.'; + } + if(!stricmp(oldauth, newauth)) + return; + //check if we know this user; then check the new auth + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `user_id` FROM `users` WHERE `user_user` = '%s'", escape_string(oldauth)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + struct event_user_registered_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("main", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->user = user; + cache->oldauth = strdup(oldauth); + lookup_authname(newauth, 0, event_user_registered_auth_lookup, cache); + } + return; +} + +static AUTHLOOKUP_CALLBACK(event_user_registered_auth_lookup) { + struct event_user_registered_cache *cache = data; + if(exists) { + renameAccount(cache->oldauth, auth); + strcpy(cache->user->auth, auth); + cache->user->flags |= USERFLAG_ISAUTHED; + } + free(cache->oldauth); + free(cache); +} + +void deleteUser(int userid) { + //simply delete the user + MYSQL_RES *res, *res2; + MYSQL_ROW row, row2; + printf_mysql_query("SELECT a.`chanuser_access`, a.`chanuser_cid`, (SELECT COUNT(*) FROM `chanusers` AS b WHERE b.`chanuser_cid` = a.`chanuser_cid` AND b.`chanuser_access` = 500) FROM `chanusers` AS a WHERE a.`chanuser_uid` = '%d'", userid); + res = mysql_use(); + while((row = mysql_fetch_row(res))) { + if(!strcmp(row[0], "500") && !strcmp(row[2], "1")) { + //unregister channel + printf_mysql_query("SELECT `botid`, `channel_name` FROM `bot_channels` LEFT JOIN `channels` ON `chanid` = `channel_id` WHERE `chanid` = '%s' AND `suspended` = '0'", row[1]); + res2 = mysql_use(); + while((row2 = mysql_fetch_row(res2))) { + struct ClientSocket *bot; + int clientid = atoi(row2[0]); + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(bot->clientid == clientid) + putsock(bot, "PART %s :Channel unregistered.", row2[1]); + } + } + printf_mysql_query("DELETE FROM `bot_channels` WHERE `chanid` = '%s'", row[1]); + } + } + printf_mysql_query("DELETE FROM `chanusers` WHERE `chanuser_uid` = '%d'", userid); + printf_mysql_query("UPDATE `bans` SET `ban_owner` = 0 WHERE `ban_owner` = '%d'", userid); + printf_mysql_query("UPDATE `donotregister` SET `dnr_user` = 0 WHERE `dnr_user` = '%d'", userid); + printf_mysql_query("UPDATE `bans` SET `ban_owner` = 0 WHERE `ban_owner` = '%d'", userid); + printf_mysql_query("UPDATE `godlog` SET `godlog_uid` = 0 WHERE `godlog_uid` = '%d'", userid); + printf_mysql_query("DELETE FROM `noinvite` WHERE `uid` = '%d'", userid); + printf_mysql_query("UPDATE `owner_history` SET `owner_history_to_uid` = 0 WHERE `owner_history_to_uid` = '%d'", userid); + printf_mysql_query("UPDATE `owner_history` SET `owner_history_from_uid` = 0 WHERE `owner_history_from_uid` = '%d'", userid); + printf_mysql_query("UPDATE `channels` SET `channel_registrator` = 0 WHERE `channel_registrator` = '%d'", userid); + printf_mysql_query("DELETE FROM `users` WHERE `user_id` = '%d'", userid); + struct UserNode *user; + for(user = getAllUsers(NULL); user; user = getAllUsers(user)) { + if(user->flags & USERFLAG_HAS_USERID) + user->flags &= ~USERFLAG_HAS_USERID; + } +} + +void init_DBHelper() { + bind_registered(event_user_registered, 0); +} + diff --git a/src/DBHelper.h b/src/DBHelper.h new file mode 100644 index 0000000..b90ed11 --- /dev/null +++ b/src/DBHelper.h @@ -0,0 +1,49 @@ +/* DBHelper.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _DBHelper_h +#define _DBHelper_h + +#include "main.h" +struct UserNode; +struct ChanNode; + +#define DB_CHANUSER_SUSPENDED 0x01 +#define DB_CHANUSER_AUTOINVITE 0x02 +#define DB_CHANUSER_NOAUTOOP 0x04 + +#define loadUserSettings(USER) if((USER->flags & USERFLAG_ISAUTHED) && !(USER->flags & USERFLAG_LOADED_SETTINGS)) _loadUserSettings(USER) +#define loadChannelSettings(CHAN) if(!(CHAN->flags & CHANFLAG_REQUESTED_CHANINFO)) _loadChannelSettings(CHAN) + +#ifndef DND_FUNCTIONS +/* MODULAR ACCESSIBLE */ void _loadUserSettings(struct UserNode* user); +/* MODULAR ACCESSIBLE */ int isGodMode(struct UserNode *user); +/* MODULAR ACCESSIBLE */ char *getChanDefault(char *channel_setting); +/* MODULAR ACCESSIBLE */ int getChannelAccess(struct UserNode *user, struct ChanNode *chan); +/* MODULAR ACCESSIBLE */ int checkChannelAccess(struct UserNode *user, struct ChanNode *chan, char *channel_setting, int allow_501); +/* MODULAR ACCESSIBLE */ void _loadChannelSettings(struct ChanNode *chan); + +/* MODULAR ACCESSIBLE */ int isUserProtected(struct ChanNode *chan, struct UserNode *victim, struct UserNode *issuer); + +/* MODULAR ACCESSIBLE */ char *getBanAffectingMask(struct ChanNode *chan, char *mask); //returns bans that match a given mask eg. *!*@ab* if you pass *!*@abcdefg.* + +/* MODULAR ACCESSIBLE */ int renameAccount(char *oldauth, char *newauth); + +/* MODULAR ACCESSIBLE */ void deleteUser(int userid); + +void init_DBHelper(); +#endif +#endif diff --git a/src/EventLogger.c b/src/EventLogger.c new file mode 100644 index 0000000..e131478 --- /dev/null +++ b/src/EventLogger.c @@ -0,0 +1,106 @@ +/* EventLogger.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "EventLogger.h" +#include "modcmd.h" +#include "mysqlConn.h" +#include "UserNode.h" +#include "ChanNode.h" +#include "DBHelper.h" +#include "log.h" + +static struct Event *first_event = NULL, *last_event = NULL; + +struct Event *createEvent(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, struct cmd_binding *command, char **args, int argc, int flags) { + struct Event *event = malloc(sizeof(*event)); + if (!event) + { + printf_log("main", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return NULL; + } + event->client = client; + event->user = user; + event->chan = chan; + event->event_time = time(0); + event->command = command; + char arguments[MAXLEN]; + int argpos = 0; + int i; + for(i = 0; i < argc; i++) + argpos += sprintf(arguments + argpos, "%s ", args[i]); + arguments[(argpos ? argpos-1 : 0)] = '\0'; + event->arguments = strdup(arguments); + event->flags = flags; + event->next = NULL; + if(last_event) { + last_event->next = event; + last_event = event; + } else { + last_event = event; + first_event = event; + } + return event; +} + +void logEvent(struct Event *event) { + char fullcmd[MAXLEN]; + sprintf(fullcmd, "%s %s", event->command->func->name, event->arguments); + if((event->flags & CMDFLAG_LOG) && event->chan) { + char *auth = ((event->user->flags & USERFLAG_ISAUTHED) ? event->user->auth : "*"); + loadChannelSettings(event->chan); + if((event->chan->flags & CHANFLAG_CHAN_REGISTERED)) + printf_mysql_query("INSERT INTO `events` (`cid`, `nick`, `auth`, `time`, `command`) VALUES ('%d', '%s', '%s', UNIX_TIMESTAMP(), '%s')", event->chan->channel_id, escape_string(event->user->nick), auth, escape_string(fullcmd)); + } + if((event->flags & CMDFLAG_OPLOG)) { + MYSQL_RES *res; + MYSQL_ROW row; + int userid; + char *auth = ((event->user->flags & USERFLAG_ISAUTHED) ? event->user->auth : "*"); + printf_mysql_query("SELECT `user_id` FROM `users` WHERE `user_user` = '%s'", escape_string(auth)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) == NULL) + userid = 0; + else + userid = atoi(row[0]); + if(event->chan) { + loadChannelSettings(event->chan); + if((event->chan->flags & CHANFLAG_CHAN_REGISTERED)) + printf_mysql_query("INSERT INTO `godlog` (`godlog_cid`, `godlog_uid`, `godlog_time`, `godlog_cmd`) VALUES ('%d', '%d', UNIX_TIMESTAMP(), '%s')", event->chan->channel_id, userid, escape_string(fullcmd)); + } + printf_log("main", LOG_OVERRIDE, "[%s:%s (%s)] %s", (event->chan ? event->chan->name : "*"), event->user->nick, auth, fullcmd); + } +} + +static void destroyEvent(struct Event *event) { + if(event == first_event) + first_event = event->next; + if(event == last_event) { + struct Event *last; + for(last = first_event; last; last = last->next) + if(last->next == NULL) break; + last_event = last; + } + free(event->arguments); + free(event); +} + +void destroyEvents() { + time_t now = time(0); + while(first_event && now - first_event->event_time >= 60) { + destroyEvent(first_event); + } +} diff --git a/src/EventLogger.h b/src/EventLogger.h new file mode 100644 index 0000000..63fb4e1 --- /dev/null +++ b/src/EventLogger.h @@ -0,0 +1,43 @@ +/* EventLogger.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _EventLogger_h +#define _EventLogger_h + +#include "main.h" +struct ClientSocket; +struct UserNode; +struct ChanNode; +struct cmd_binding; + +struct Event { + struct ClientSocket *client; + struct UserNode *user; + struct ChanNode *chan; + time_t event_time; + struct cmd_binding *command; + char *arguments; + unsigned int flags; /* defined in modcmd.h */ + + struct Event *next; +}; + +#ifndef DND_FUNCTIONS +struct Event *createEvent(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, struct cmd_binding *command, char **args, int argc, int flags); +/* MODULAR ACCESSIBLE */ void logEvent(struct Event *event); +void destroyEvents(); +#endif +#endif diff --git a/src/HandleInfoHandler.c b/src/HandleInfoHandler.c new file mode 100644 index 0000000..8a8a46c --- /dev/null +++ b/src/HandleInfoHandler.c @@ -0,0 +1,247 @@ +/* HandleInfoHandler.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "HandleInfoHandler.h" +#include "ClientSocket.h" +#include "UserNode.h" +#include "ChanNode.h" +#include "IRCEvents.h" +#include "tools.h" +#include "modules.h" +#include "log.h" + +#define AUTHSERV_NICK "AuthServ" + +#define MAXCALLBACKS 3 + +struct HandleInfoQueueEntry { + char *auth; + void *callback[MAXCALLBACKS]; + int module_id[MAXCALLBACKS]; + void *data[MAXCALLBACKS]; + + struct HandleInfoQueueEntry *next; +}; + +static struct HandleInfoQueueEntry* addHandleInfoQueueEntry(struct ClientSocket *client) { + struct HandleInfoQueueEntry *entry = malloc(sizeof(*entry)); + if (!entry) + { + printf_log("main", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return NULL; + } + SYNCHRONIZE(cache_sync); + entry->next = NULL; + if(client->handleinfo_last) + client->handleinfo_last->next = entry; + else + client->handleinfo_first = entry; + client->handleinfo_last = entry; + DESYNCHRONIZE(cache_sync); + return entry; +} + +static struct HandleInfoQueueEntry* getNextHandleInfoQueueEntry(struct ClientSocket *client, int freeEntry) { + if(!client->handleinfo_first) return NULL; + SYNCHRONIZE(cache_sync); + struct HandleInfoQueueEntry *entry = client->handleinfo_first; + if(freeEntry) { + client->handleinfo_first = entry->next; + if(entry == client->handleinfo_last) { + client->handleinfo_last = NULL; + } + } + DESYNCHRONIZE(cache_sync); + return entry; +} + +void clear_handleinfoqueue(struct ClientSocket *client) { + if(!client->handleinfo_first) return; + SYNCHRONIZE(cache_sync); + struct HandleInfoQueueEntry *entry, *next; + for(entry = client->handleinfo_first; entry; entry = next) { + next = entry->next; + free(entry); + } + client->handleinfo_last = NULL; + client->handleinfo_first = NULL; + DESYNCHRONIZE(cache_sync); +} + +void lookup_authname(char *auth, int module_id, authlookup_callback_t callback, void *data) { + struct ClientSocket *bot; + struct HandleInfoQueueEntry* entry; + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + for(entry = bot->handleinfo_first; entry; entry = entry->next) { + if(!stricmp(entry->auth, auth)) { + int i = 0; + for(i = 1; i < MAXCALLBACKS; i++) { + if(!entry->callback[i]) { + entry->callback[i] = callback; + entry->module_id[i] = module_id; + entry->data[i] = data; + return; + } + } + } + } + if(bot->flags & SOCKET_FLAG_PREFERRED) + break; + } + if(bot == NULL) return; + entry = addHandleInfoQueueEntry(bot); + int i; + entry->auth = strdup(auth); + entry->callback[0] = callback; + entry->module_id[0] = module_id; + for(i = 1; i < MAXCALLBACKS; i++) + entry->callback[i] = NULL; + entry->data[0] = data; + for(i = 1; i < MAXCALLBACKS; i++) + entry->data[i] = NULL; + putsock(bot, "PRIVMSG " AUTHSERV_NICK " :ACCOUNTINFO *%s", auth); +} + +static void recv_notice(struct UserNode *user, struct UserNode *target, char *message) { + if(stricmp(user->nick, AUTHSERV_NICK)) return; + struct ClientSocket *bot; + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(bot->user == target) break; + } + if(!bot) return; + char *auth = NULL; + int do_match = 0, exists = 0; + char *tmp; + time_t registered = time(0); + struct tm *timeinfo; + //messages to parse: + // Account * has not been registered. + // Account information for Skynet: + if(!match("Account * has not been registered.", message)) { + do_match = 1; + tmp = strstr(message, "\002"); + auth = tmp+1; + tmp = strstr(auth, "\002"); + *tmp = '\0'; + } else if(!match("Account information for *", message)) { + do_match = 2; + exists = 1; + tmp = strstr(message, "\002"); + auth = tmp+1; + tmp = strstr(auth, "\002"); + *tmp = '\0'; + } else if(!match(" Registered on: *", message)) { + do_match = 1; + exists = 1; + tmp = strstr(message, ": "); + tmp += 2; + timeinfo = localtime(®istered); + timeinfo->tm_year = 0; + //parse time + //Sat Nov 19 14:52:57 2011 + tmp = strchr(tmp, ' '); + if(!tmp) goto errparse; + tmp++; + char *tmp2 = strchr(tmp, ' '); + if(!tmp2) goto errparse; + *tmp2 = '\0'; + if(!stricmp(tmp, "Jan")) + timeinfo->tm_mon = 0; + else if(!stricmp(tmp, "Feb")) + timeinfo->tm_mon = 1; + else if(!stricmp(tmp, "Mar")) + timeinfo->tm_mon = 2; + else if(!stricmp(tmp, "Apr")) + timeinfo->tm_mon = 3; + else if(!stricmp(tmp, "May")) + timeinfo->tm_mon = 4; + else if(!stricmp(tmp, "Jun")) + timeinfo->tm_mon = 5; + else if(!stricmp(tmp, "Jul")) + timeinfo->tm_mon = 6; + else if(!stricmp(tmp, "Aug")) + timeinfo->tm_mon = 7; + else if(!stricmp(tmp, "Sep")) + timeinfo->tm_mon = 8; + else if(!stricmp(tmp, "Oct")) + timeinfo->tm_mon = 9; + else if(!stricmp(tmp, "Nov")) + timeinfo->tm_mon = 10; + else if(!stricmp(tmp, "Dec")) + timeinfo->tm_mon = 11; + tmp = tmp2 + 1; + tmp2 = strchr(tmp, ' '); + if(!tmp2) goto errparse; + *tmp2 = '\0'; + timeinfo->tm_mday = atoi(tmp); + tmp = tmp2 + 1; + if(*tmp == ' ') tmp++; + tmp2 = strchr(tmp, ':'); + if(!tmp2) goto errparse; + *tmp2 = '\0'; + timeinfo->tm_hour = atoi(tmp); + tmp = tmp2 + 1; + tmp2 = strchr(tmp, ':'); + if(!tmp2) goto errparse; + *tmp2 = '\0'; + timeinfo->tm_min = atoi(tmp); + tmp = tmp2 + 1; + tmp2 = strchr(tmp, ' '); + if(!tmp2) goto errparse; + *tmp2 = '\0'; + timeinfo->tm_sec = atoi(tmp); + tmp = tmp2 + 1; + timeinfo->tm_year = atoi(tmp) - 1900; + registered = mktime(timeinfo); + } + errparse: + + if(do_match) { + #ifdef HAVE_THREADS + unsigned int tid = (unsigned int) pthread_self_tid(); + while(!clientsocket_parseorder_top(tid)) { + usleep(1000); //1ms + } + #endif + struct HandleInfoQueueEntry* entry = getNextHandleInfoQueueEntry(bot, ((do_match != 2) ? 1 : 0)); + if(entry) { + if(do_match == 2) { + free(entry->auth); + entry->auth = strdup(auth); + return; + } + authlookup_callback_t *callback; + int i; + for(i = 0; i < MAXCALLBACKS; i++) { + callback = entry->callback[i]; + if(!callback) break; + if(!entry->module_id[i] || module_loaded(entry->module_id[i])) + callback(entry->auth, exists, registered, entry->data[i]); + } + free(entry->auth); + free(entry); + } + } +} + +void init_handleinfohandler() { + bind_privnotice(recv_notice, 0); +} + +void free_handleinfohandler() { + +} diff --git a/src/HandleInfoHandler.h b/src/HandleInfoHandler.h new file mode 100644 index 0000000..a796a6d --- /dev/null +++ b/src/HandleInfoHandler.h @@ -0,0 +1,34 @@ +/* HandleInfoHandler.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _HandleInfoHandler_h +#define _HandleInfoHandler_h + +#include "main.h" + +struct ClientSocket; +struct UserNode; + +#define AUTHLOOKUP_CALLBACK(NAME) void NAME(UNUSED_ARG(char *auth), UNUSED_ARG(int exists), UNUSED_ARG(time_t registered), UNUSED_ARG(void *data)) +typedef AUTHLOOKUP_CALLBACK(authlookup_callback_t); + +#ifndef DND_FUNCTIONS +void clear_handleinfoqueue(struct ClientSocket *client); +/* MODULAR ACCESSIBLE */ void lookup_authname(char *auth, int module_id, authlookup_callback_t callback, void *data); +void init_handleinfohandler(); +void free_handleinfohandler(); +#endif +#endif diff --git a/src/IOEngine.h b/src/IOEngine.h index 5f68240..411173a 100644 --- a/src/IOEngine.h +++ b/src/IOEngine.h @@ -31,8 +31,14 @@ pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE_VAL);\ pthread_mutex_init(&var, &mutex_attr); \ } +#ifdef ENABLE_MUTEX_DEBUG +#include "mutexDebug.h" +#define IOSYNCHRONIZE(var) xmutex(1, &var, __FILE__, __LINE__); pthread_mutex_lock(&var) +#define IODESYNCHRONIZE(var) xmutex(0, &var, __FILE__, __LINE__); pthread_mutex_unlock(&var) +#else #define IOSYNCHRONIZE(var) pthread_mutex_lock(&var) #define IODESYNCHRONIZE(var) pthread_mutex_unlock(&var) +#endif #else #define IOTHREAD_MUTEX_INIT(var) #define IOSYNCHRONIZE(var) diff --git a/src/IOHandler.h b/src/IOHandler.h index eb32cdc..82781d6 100644 --- a/src/IOHandler.h +++ b/src/IOHandler.h @@ -16,6 +16,7 @@ */ #ifndef _IOHandler_h #define _IOHandler_h +#include "../config.h" /* configure script autogenerated */ #include #include /* struct timeval */ diff --git a/src/IPNode.c b/src/IPNode.c new file mode 100644 index 0000000..bed8ac7 --- /dev/null +++ b/src/IPNode.c @@ -0,0 +1,150 @@ +/* IPNode.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#include "IPNode.h" + +#define hex2dec(x) (isxdigit(x) ? (isdigit(x) ? x - '0' : (isupper(x) ? x - 'A' : x - 'a') + 10) : 0) + +static struct IPNode *parseIP(struct IPNode *ip, const char *org_ipstr); + +struct IPNode *createIPNode(const char *ipstr) { + struct IPNode *ip = malloc(sizeof(*ip)); + if(!ip) return NULL; + ip->flags = 0; + if(!parseIP(ip, ipstr)) { + free(ip); + ip = NULL; + } + return ip; +} + +static struct IPNode *parseIP(struct IPNode *ip, const char *org_ipstr) { + char ipstr[strlen(org_ipstr)+1]; + strcpy(ipstr, org_ipstr); + if(strchr(ipstr, ':')) { + ip->flags |= IPNODE_IS_IPV6; + char *p1 = ipstr; + char *p2; + int blocks = 1; + while(*p1) { + if(p1[0] == ':' && p1[1] == ':') { + //double :: + p1++; + } + if(*p1 == ':') + blocks++; + if(*p1 == '/') { + *p1 = '\0'; + break; + } + p1++; + } + if(blocks > 8) return 0; + char hexa[32]; + int i; + for(i = 0; i < 32; i++) + hexa[i] = '0'; + i = 0; + int p = 0; + int len; + while(*p1) { + if(p1[0] == ':' && p1[1] == ':') { + i = 8 - blocks; + p1 += 2; + p = 0; + p2 = p1; + len = 0; + while(*p2 && *p2 != ':') { + len++; + p2++; + } + continue; + } + else if(*p1 == ':') { + p1++; + i++; + p = 0; + len = 0; + while(*p2 && *p2 != ':') { + len++; + p2++; + } + continue; + } + if(p >= 4 || i >= 8) return NULL; + hexa[i*4 + ((4-len) + (p++))] = *p1; + p1++; + } + for(i = 0, p = 0; i < 32; i+=2, p++) { + ip->ipdata[p] = hex2dec(hexa[i]) << 4; + ip->ipdata[p] |= hex2dec(hexa[i+1]); + } + } else if(strchr(ipstr, '.')) { + char *ippart = ipstr; + char *next = strchr(ipstr, '/'); + int i = 0; + if(next) { + *next = '\0'; + next = NULL; + } + do { + next = strchr(ippart, '.'); + if(next) *next = '\0'; + if(i >= 4) return NULL; + ip->ipdata[12+(i++)] = (unsigned char) atoi(ippart); + if(next) { + ippart = next+1; + continue; + } + break; + } while(1); + if(i != 3) return NULL; + } else + return NULL; + return ip; +} + +int ipmatch(struct IPNode *ip1, struct IPNode *ip2, int bits) { + if(!bits) return 0; + bits = (ip2->flags & IPNODE_IS_IPV6 ? bits : bits + (12*8)); + if(bits > 128) return 1; + int sbit = (bits % 8); + int check = (bits / 8) + (sbit ? 1 : 0); + int i; + for(i = 0; i < check; i++) { + if(i == check-1 && sbit) { + int bitmask = 0; + int j = 0; + for(;j < sbit; j++) { + bitmask <<= 1; + bitmask |= 1; + } + for(;j < 8; j++) { + bitmask <<= 1; + bitmask |= 0; + } + if((ip1->ipdata[i] & bitmask) != (ip2->ipdata[i] & bitmask)) + return 1; + } else if(ip1->ipdata[i] != ip2->ipdata[i]) + return 1; + } + return 0; +} + +void freeIPNode(struct IPNode *ip) { + free(ip); +} + diff --git a/src/IPNode.h b/src/IPNode.h new file mode 100644 index 0000000..737bf7f --- /dev/null +++ b/src/IPNode.h @@ -0,0 +1,33 @@ +/* IPNode.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _IPNode_h +#define _IPNode_h +#include "main.h" + +#define IPNODE_IS_IPV6 0x01 +#define IPNODE_IS_LOCAL 0x02 + +struct IPNode { + unsigned char flags; + unsigned char ipdata[16]; // 16 * 8bit = 128bit +}; + +struct IPNode *createIPNode(const char *ipstr); +int ipmatch(struct IPNode *ip1, struct IPNode *ip2, int bits); +void freeIPNode(struct IPNode *ip); + +#endif \ No newline at end of file diff --git a/src/IRCEvents.c b/src/IRCEvents.c new file mode 100644 index 0000000..2eea34d --- /dev/null +++ b/src/IRCEvents.c @@ -0,0 +1,237 @@ +/* IRCEvents.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "IRCEvents.h" +#include "UserNode.h" +#include "ChanNode.h" +#include "ChanUser.h" +#include "ClientSocket.h" +#include "mysqlConn.h" +#include "log.h" + +struct binding { + void *func; + int module_id; + struct binding *next; +}; + +static void **binds = NULL; +#define BIND_TYPE_JOIN 0 +#define BIND_TYPE_NICK 1 +#define BIND_TYPE_PART 2 +#define BIND_TYPE_QUIT 3 +#define BIND_TYPE_KICK 4 +#define BIND_TYPE_TOPIC 5 +#define BIND_TYPE_MODE 6 +#define BIND_TYPE_CHANMSG 7 +#define BIND_TYPE_PRIVMSG 8 +#define BIND_TYPE_CHANNOTICE 9 +#define BIND_TYPE_PRIVNOTICE 10 +#define BIND_TYPE_CHANCTCP 11 +#define BIND_TYPE_PRIVCTCP 12 +#define BIND_TYPE_INVITE 13 +#define BIND_TYPE_RAW 14 +#define BIND_TYPE_BOT_READY 15 +#define BIND_TYPE_REGISTERED 16 +#define BIND_TYPE_FREEUSER 17 +#define BIND_TYPE_FREECHAN 18 +#define BIND_TYPE_RELOAD 19 +#define BIND_TYPE_FREECLIENT 20 + +#define TOTAL_BIND_TYPES 21 + +void init_bind() { + if(binds) + return; + binds = calloc(TOTAL_BIND_TYPES, sizeof(*binds)); +} + +void free_bind() { + struct binding *cbind, *next; + int i; + for(i = 0; i < TOTAL_BIND_TYPES; i++) { + for(cbind = binds[i]; cbind; cbind = next) { + next = cbind->next; + free(cbind); + } + } + free(binds); + binds = NULL; +} + +void unregister_module_events(int module_id) { + struct binding *cbind, *next, *prev; + int i; + for(i = 0; i < TOTAL_BIND_TYPES; i++) { + prev = NULL; + for(cbind = binds[i]; cbind; cbind = next) { + next = cbind->next; + if(cbind->module_id == module_id) { + if(prev) + prev->next = next; + else + binds[i] = next; + free(cbind); + } else + prev = cbind; + } + } +} + +static int is_bound(unsigned char type, void *func) { + struct binding *cbind; + for(cbind = binds[type]; cbind; cbind = cbind->next) { + if(cbind->func == func) + return 1; + } + return 0; +} + +#define FUNC_BIND(NAME,FUNCTYPE,TYPE) \ +int bind_##NAME(FUNCTYPE *func, int module_id) { \ + if(!is_bound(TYPE, func)) { \ + struct binding *cbind = malloc(sizeof(*cbind)); \ + if (!cbind) { \ + printf_log("main", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); \ + return 0; \ + } \ + cbind->func = func; \ + cbind->module_id = module_id; \ + cbind->next = binds[TYPE]; \ + binds[TYPE] = cbind; \ + return 1; \ + } \ + return 0; \ +} + +#define FUNC_UNBIND(NAME,FUNCTYPE,TYPE) \ +void unbind_##NAME(FUNCTYPE *func) { \ + struct binding *cbind, *last = NULL, *next; \ + for(cbind = binds[TYPE]; cbind; cbind = next) { \ + next = cbind->next; \ + if(cbind->func == func) { \ + if(last) \ + last->next = cbind->next; \ + else \ + binds[TYPE] = cbind->next; \ + free(cbind); \ + } else \ + last = cbind; \ + } \ +} + +#define FUNC_EVENT(NAME,FUNCTYPE,TYPE,PDECLARATION,PLIST) \ +int event_##NAME PDECLARATION { \ + struct binding *cbind; \ + pre_event(TYPE); \ + for(cbind = binds[TYPE]; cbind; cbind = cbind->next) { \ + FUNCTYPE *func = cbind->func; \ + func PLIST; \ + } \ + post_event(TYPE); \ + return 1; \ +} + +void pre_event(UNUSED_ARG(int type)) { + +} + +void post_event(UNUSED_ARG(int type)) { + +} + +//EVENTS + +FUNC_BIND(join, join_func_t, BIND_TYPE_JOIN) +FUNC_UNBIND(join, join_func_t, BIND_TYPE_JOIN) +FUNC_EVENT(join, join_func_t, BIND_TYPE_JOIN, (struct ChanUser *chanuser), (chanuser)) + +FUNC_BIND(nick, nick_func_t, BIND_TYPE_NICK) +FUNC_UNBIND(nick, nick_func_t, BIND_TYPE_NICK) +FUNC_EVENT(nick, nick_func_t, BIND_TYPE_NICK, (struct UserNode *user, char *new_nick), (user, new_nick)) + +FUNC_BIND(part, part_func_t, BIND_TYPE_PART) +FUNC_UNBIND(part, part_func_t, BIND_TYPE_PART) +FUNC_EVENT(part, part_func_t, BIND_TYPE_PART, (struct ChanUser *chanuser, int quit, char *reason), (chanuser, quit, reason)) + +FUNC_BIND(kick, kick_func_t, BIND_TYPE_KICK) +FUNC_UNBIND(kick, kick_func_t, BIND_TYPE_KICK) +FUNC_EVENT(kick, kick_func_t, BIND_TYPE_KICK, (struct UserNode *user, struct ChanUser *target, char *reason), (user, target, reason)) + +FUNC_BIND(topic, topic_func_t, BIND_TYPE_TOPIC) +FUNC_UNBIND(topic, topic_func_t, BIND_TYPE_TOPIC) +FUNC_EVENT(topic, topic_func_t, BIND_TYPE_TOPIC, (struct UserNode *user, struct ChanNode *chan, const char *new_topic), (user, chan, new_topic)) + +FUNC_BIND(mode, mode_func_t, BIND_TYPE_MODE) +FUNC_UNBIND(mode, mode_func_t, BIND_TYPE_MODE) +FUNC_EVENT(mode, mode_func_t, BIND_TYPE_MODE, (struct UserNode *user, struct ChanNode *chan, char *modes, char **args, int argc), (user, chan, modes, args, argc)) + +FUNC_BIND(chanmsg, chanmsg_func_t, BIND_TYPE_CHANMSG) +FUNC_UNBIND(chanmsg, chanmsg_func_t, BIND_TYPE_CHANMSG) +FUNC_EVENT(chanmsg, chanmsg_func_t, BIND_TYPE_CHANMSG, (struct UserNode *user, struct ChanNode *chan, char *message), (user, chan, message)) + +FUNC_BIND(privmsg, privmsg_func_t, BIND_TYPE_PRIVMSG) +FUNC_UNBIND(privmsg, privmsg_func_t, BIND_TYPE_PRIVMSG) +FUNC_EVENT(privmsg, privmsg_func_t, BIND_TYPE_PRIVMSG, (struct UserNode *user, struct UserNode *target, char *message), (user, target, message)) + +FUNC_BIND(channotice, channotice_func_t, BIND_TYPE_CHANNOTICE) +FUNC_UNBIND(channotice, channotice_func_t, BIND_TYPE_CHANNOTICE) +FUNC_EVENT(channotice, channotice_func_t, BIND_TYPE_CHANNOTICE, (struct UserNode *user, struct ChanNode *chan, char *message), (user, chan, message)) + +FUNC_BIND(privnotice, privnotice_func_t, BIND_TYPE_PRIVNOTICE) +FUNC_UNBIND(privnotice, privnotice_func_t, BIND_TYPE_PRIVNOTICE) +FUNC_EVENT(privnotice, privnotice_func_t, BIND_TYPE_PRIVNOTICE, (struct UserNode *user, struct UserNode *target, char *message), (user, target, message)) + +FUNC_BIND(chanctcp, chanctcp_func_t, BIND_TYPE_CHANCTCP) +FUNC_UNBIND(chanctcp, chanctcp_func_t, BIND_TYPE_CHANCTCP) +FUNC_EVENT(chanctcp, chanctcp_func_t, BIND_TYPE_CHANCTCP, (struct UserNode *user, struct ChanNode *chan, char *command, char *text), (user, chan, command, text)) + +FUNC_BIND(privctcp, privctcp_func_t, BIND_TYPE_PRIVCTCP) +FUNC_UNBIND(privctcp, privctcp_func_t, BIND_TYPE_PRIVCTCP) +FUNC_EVENT(privctcp, privctcp_func_t, BIND_TYPE_PRIVCTCP, (struct UserNode *user, struct UserNode *target, char *command, char *text), (user, target, command, text)) + +FUNC_BIND(invite, invite_func_t, BIND_TYPE_INVITE) +FUNC_UNBIND(invite, invite_func_t, BIND_TYPE_INVITE) +FUNC_EVENT(invite, invite_func_t, BIND_TYPE_INVITE, (struct ClientSocket *client, struct UserNode *user, char *channel), (client, user, channel)) + +FUNC_BIND(raw, raw_func_t, BIND_TYPE_RAW) +FUNC_UNBIND(raw, raw_func_t, BIND_TYPE_RAW) +FUNC_EVENT(raw, raw_func_t, BIND_TYPE_RAW, (struct ClientSocket *client, char *from, char *cmd, char **argv, int argc), (client, from, cmd, argv, argc)) + +FUNC_BIND(bot_ready, bot_ready_func_t, BIND_TYPE_BOT_READY) +FUNC_UNBIND(bot_ready, bot_ready_func_t, BIND_TYPE_BOT_READY) +FUNC_EVENT(bot_ready, bot_ready_func_t, BIND_TYPE_BOT_READY, (struct ClientSocket *client), (client)) + +FUNC_BIND(registered, registered_func_t, BIND_TYPE_REGISTERED) +FUNC_UNBIND(registered, registered_func_t, BIND_TYPE_REGISTERED) +FUNC_EVENT(registered, registered_func_t, BIND_TYPE_REGISTERED, (struct UserNode *user, char *new_mask), (user, new_mask)) + +FUNC_BIND(freeuser, freeuser_func_t, BIND_TYPE_FREEUSER) +FUNC_UNBIND(freeuser, freeuser_func_t, BIND_TYPE_FREEUSER) +FUNC_EVENT(freeuser, freeuser_func_t, BIND_TYPE_FREEUSER, (struct UserNode *user), (user)) + +FUNC_BIND(freechan, freechan_func_t, BIND_TYPE_FREECHAN) +FUNC_UNBIND(freechan, freechan_func_t, BIND_TYPE_FREECHAN) +FUNC_EVENT(freechan, freechan_func_t, BIND_TYPE_FREECHAN, (struct ChanNode *chan), (chan)) + +FUNC_BIND(reload, reload_func_t, BIND_TYPE_RELOAD) +FUNC_UNBIND(reload, reload_func_t, BIND_TYPE_RELOAD) +FUNC_EVENT(reload, reload_func_t, BIND_TYPE_RELOAD, (int initialization), (initialization)) + +FUNC_BIND(freeclient, freeclient_func_t, BIND_TYPE_FREECLIENT) +FUNC_UNBIND(freeclient, freeclient_func_t, BIND_TYPE_FREECLIENT) +FUNC_EVENT(freeclient, freeclient_func_t, BIND_TYPE_FREECLIENT, (struct ClientSocket *client), (client)) diff --git a/src/IRCEvents.h b/src/IRCEvents.h new file mode 100644 index 0000000..7a7ac3e --- /dev/null +++ b/src/IRCEvents.h @@ -0,0 +1,173 @@ +/* IRCEvents.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _IRCEvents_h +#define _IRCEvents_h + +#include "main.h" + +struct UserNode; +struct ChanNode; +struct ChanUser; +struct ClientSocket; + +#ifndef DND_FUNCTIONS +void init_bind(); +void free_bind(); +void unregister_module_events(int module_id); +#endif + +typedef void join_func_t(struct ChanUser *chanuser); +#ifndef DND_FUNCTIONS +/* MODULAR ACCESSIBLE */ int bind_join(join_func_t *func, int module_id); +/* MODULAR ACCESSIBLE */ void unbind_join(join_func_t *func); +int event_join(struct ChanUser *chanuser); +#endif + +typedef void nick_func_t(struct UserNode *user, char *new_nick); +#ifndef DND_FUNCTIONS +/* MODULAR ACCESSIBLE */ int bind_nick(nick_func_t *func, int module_id); +/* MODULAR ACCESSIBLE */ void unbind_nick(nick_func_t *func); +int event_nick(struct UserNode *user, char *new_nick); +#endif + +typedef void part_func_t(struct ChanUser *chanuser, int quit, char *reason); +#ifndef DND_FUNCTIONS +/* MODULAR ACCESSIBLE */ int bind_part(part_func_t *func, int module_id); +/* MODULAR ACCESSIBLE */ void unbind_part(part_func_t *func); +int event_part(struct ChanUser *chanuser, int quit, char *reason); +#endif + +typedef void kick_func_t(struct UserNode *user, struct ChanUser *target, char *reason); +#ifndef DND_FUNCTIONS +/* MODULAR ACCESSIBLE */ int bind_kick(kick_func_t *func, int module_id); +/* MODULAR ACCESSIBLE */ void unbind_kick(kick_func_t *func); +int event_kick(struct UserNode *user, struct ChanUser *target, char *reason); +#endif + +typedef void topic_func_t(struct UserNode *user, struct ChanNode *chan, const char *new_topic); +#ifndef DND_FUNCTIONS +/* MODULAR ACCESSIBLE */ int bind_topic(topic_func_t *func, int module_id); +/* MODULAR ACCESSIBLE */ void unbind_topic(topic_func_t *func); +int event_topic(struct UserNode *user, struct ChanNode *chan, const char *new_topic); +#endif + +typedef void mode_func_t(struct UserNode *user, struct ChanNode *chan, char *modes, char **argv, int argc); +#ifndef DND_FUNCTIONS +/* MODULAR ACCESSIBLE */ int bind_mode(mode_func_t *func, int module_id); +/* MODULAR ACCESSIBLE */ void unbind_mode(mode_func_t *func); +int event_mode(struct UserNode *user, struct ChanNode *chan, char *modes, char **argv, int argc); +#endif + +typedef void chanmsg_func_t(struct UserNode *user, struct ChanNode *chan, char *message); +#ifndef DND_FUNCTIONS +/* MODULAR ACCESSIBLE */ int bind_chanmsg(chanmsg_func_t *func, int module_id); +/* MODULAR ACCESSIBLE */ void unbind_chanmsg(chanmsg_func_t *func); +int event_chanmsg(struct UserNode *user, struct ChanNode *chan, char *message); +#endif + +typedef void privmsg_func_t(struct UserNode *user, struct UserNode *target, char *message); +#ifndef DND_FUNCTIONS +/* MODULAR ACCESSIBLE */ int bind_privmsg(privmsg_func_t *func, int module_id); +/* MODULAR ACCESSIBLE */ void unbind_privmsg(privmsg_func_t *func); +int event_privmsg(struct UserNode *user, struct UserNode *target, char *message); +#endif + +typedef void channotice_func_t(struct UserNode *user, struct ChanNode *chan, char *message); +#ifndef DND_FUNCTIONS +/* MODULAR ACCESSIBLE */ int bind_channotice(channotice_func_t *func, int module_id); +/* MODULAR ACCESSIBLE */ void unbind_channotice(channotice_func_t *func); +int event_channotice(struct UserNode *user, struct ChanNode *chan, char *message); +#endif + +typedef void privnotice_func_t(struct UserNode *user, struct UserNode *target, char *message); +#ifndef DND_FUNCTIONS +/* MODULAR ACCESSIBLE */ int bind_privnotice(privnotice_func_t *func, int module_id); +/* MODULAR ACCESSIBLE */ void unbind_privnotice(privnotice_func_t *func); +int event_privnotice(struct UserNode *user, struct UserNode *target, char *message); +#endif + +typedef void chanctcp_func_t(struct UserNode *user, struct ChanNode *chan, char *command, char *text); +#ifndef DND_FUNCTIONS +/* MODULAR ACCESSIBLE */ int bind_chanctcp(chanctcp_func_t *func, int module_id); +/* MODULAR ACCESSIBLE */ void unbind_chanctcp(chanctcp_func_t *func); +int event_chanctcp(struct UserNode *user, struct ChanNode *chan, char *command, char *text); +#endif + +typedef void privctcp_func_t(struct UserNode *user, struct UserNode *target, char *command, char *text); +#ifndef DND_FUNCTIONS +/* MODULAR ACCESSIBLE */ int bind_privctcp(privctcp_func_t *func, int module_id); +/* MODULAR ACCESSIBLE */ void unbind_privctcp(privctcp_func_t *func); +int event_privctcp(struct UserNode *user, struct UserNode *target, char *command, char *text); +#endif + +typedef void invite_func_t(struct ClientSocket *client, struct UserNode *user, char *channel); +#ifndef DND_FUNCTIONS +/* MODULAR ACCESSIBLE */ int bind_invite(invite_func_t *func, int module_id); +/* MODULAR ACCESSIBLE */ void unbind_invite(invite_func_t *func); +int event_invite(struct ClientSocket *client, struct UserNode *user, char *channel); +#endif + +typedef void raw_func_t(struct ClientSocket *client, char *from, char *cmd, char **argv, int argc); +#ifndef DND_FUNCTIONS +/* MODULAR ACCESSIBLE */ int bind_raw(raw_func_t *func, int module_id); +/* MODULAR ACCESSIBLE */ void unbind_raw(raw_func_t *func); +int event_raw(struct ClientSocket *client, char *from, char *cmd, char **argv, int argc); +#endif + +typedef void bot_ready_func_t(struct ClientSocket *client); +#ifndef DND_FUNCTIONS +/* MODULAR ACCESSIBLE */ int bind_bot_ready(bot_ready_func_t *func, int module_id); +/* MODULAR ACCESSIBLE */ void unbind_bot_ready(bot_ready_func_t *func); +int event_bot_ready(struct ClientSocket *client); +#endif + +typedef void registered_func_t(struct UserNode *user, char *new_mask); +#ifndef DND_FUNCTIONS +/* MODULAR ACCESSIBLE */ int bind_registered(registered_func_t *func, int module_id); +/* MODULAR ACCESSIBLE */ void unbind_registered(registered_func_t *func); +int event_registered(struct UserNode *user, char *new_mask); +#endif + +typedef int freeuser_func_t(struct UserNode *user); +#ifndef DND_FUNCTIONS +/* MODULAR ACCESSIBLE */ int bind_freeuser(freeuser_func_t *func, int module_id); +/* MODULAR ACCESSIBLE */ void unbind_freeuser(freeuser_func_t *func); +int event_freeuser(struct UserNode *user); +#endif + +typedef int freechan_func_t(struct ChanNode *chan); +#ifndef DND_FUNCTIONS +/* MODULAR ACCESSIBLE */ int bind_freechan(freechan_func_t *func, int module_id); +/* MODULAR ACCESSIBLE */ void unbind_freechan(freechan_func_t *func); +int event_freechan(struct ChanNode *chan); +#endif + +typedef int reload_func_t(int initialization); +#ifndef DND_FUNCTIONS +/* MODULAR ACCESSIBLE */ int bind_reload(reload_func_t *func, int module_id); +/* MODULAR ACCESSIBLE */ void unbind_reload(reload_func_t *func); +int event_reload(int initialization); +#endif + +typedef void freeclient_func_t(struct ClientSocket *client); +#ifndef DND_FUNCTIONS +/* MODULAR ACCESSIBLE */ int bind_freeclient(freeclient_func_t *func, int module_id); +/* MODULAR ACCESSIBLE */ void unbind_freeclient(freeclient_func_t *func); +int event_freeclient(struct ClientSocket *chan); +#endif + +#endif diff --git a/src/IRCParser.c b/src/IRCParser.c new file mode 100644 index 0000000..89047f8 --- /dev/null +++ b/src/IRCParser.c @@ -0,0 +1,1016 @@ +/* IRCParser.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "IRCParser.h" +#include "UserNode.h" +#include "ChanNode.h" +#include "ChanUser.h" +#include "IRCEvents.h" +#include "ClientSocket.h" +#include "WHOHandler.h" +#include "lang.h" +#include "DBHelper.h" +#include "BanNode.h" +#include "ModeNode.h" +#include "tools.h" +#include "bots.h" +#include "timeq.h" +#include "ConfigParser.h" +#include "statistics.h" +#include "log.h" + +struct irc_cmd *irc_commands = NULL; +//static struct UserNode *registering_users = NULL; +int statistics_privmsg = 0; +int statistics_network_users = 0; +int statistics_network_channels = 0; + +static void register_irc_function(char *command, irc_cmd_t *func); +static void parse_raw(struct ClientSocket *client, char *from, char *cmd, char **argv, int argc); + +struct OplessRejoinUserbot { + char *nick; + char *auth; +}; + +struct OplessRejoinBot { + unsigned char is_client; + union { + struct ClientSocket *client; + struct OplessRejoinUserbot *userbot; + } bot; + struct OplessRejoinBot *next; +}; + +void parse_line(struct ClientSocket *client, char *line) { + int argc = 0; + char *argv[MAXNUMPARAMS]; + #ifdef HAVE_THREADS + printf_log("main", LOG_IRCRAW, "[%d recv %lu] %s\n", getCurrentThreadID(), (unsigned long) strlen(line), line); + #else + printf_log("main", LOG_IRCRAW, "[recv %lu] %s\n", (unsigned long) strlen(line), line); + #endif + if(line[0] == ':') + line++; + else + argv[argc++] = NULL; + while(*line) { + //skip leading spaces + while (*line == ' ') + *line++ = 0; + if (*line == ':') { + //the rest is a single parameter + argv[argc++] = line + 1; + break; + } + argv[argc++] = line; + if (argc >= MAXNUMPARAMS) + break; + while (*line != ' ' && *line) + line++; + } + if(argc >= 2) { + parse_raw(client, argv[0], argv[1], argv+2, argc-2); + } +} + +static void register_irc_function(char *command, irc_cmd_t *func) { + struct irc_cmd *irc_cmd = malloc(sizeof(*irc_cmd)); + if (!irc_cmd) + { + printf_log("main", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + irc_cmd->cmd = command; + irc_cmd->func = func; + irc_cmd->next = irc_commands; + irc_commands = irc_cmd; +} + +static void parse_raw(struct ClientSocket *client, char *from, char *cmd, char **argv, int argc) { + struct irc_cmd *irc_cmd; + int ret = 0; + for(irc_cmd = irc_commands; irc_cmd; irc_cmd = irc_cmd->next) { + if(!stricmp(irc_cmd->cmd, cmd)) { + ret = irc_cmd->func(client, from, argv, argc); + break; + } + } + if(!irc_cmd) { + event_raw(client, from, cmd, argv, argc); + } else if(!ret) { + printf_log("main", LOG_WARNING | LOG_IRCRAW, "PARSE ERROR: %s %s %s\n", (from ? from : "*"), cmd, merge_argv(argv, 0, argc)); + } +} + +static void increase_viscount_butone(struct ChanNode *chan, struct ChanUser *ignore) { + struct ChanUser *chanuser; + + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + if(chanuser == ignore) + continue; + chanuser->visCount++; + } +} + +static void decrease_viscount_butone(struct ChanNode *chan, struct ChanUser *ignore) { + struct ChanUser *chanuser, *next_chanuser; + + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = next_chanuser) { + next_chanuser = getChannelUsers(chan, chanuser); + if(chanuser == ignore) + continue; + chanuser->visCount--; + if(chanuser->visCount <= 0) + delChanUser(chanuser, 1); + } +} + +static USERLIST_CALLBACK(got_channel_userlist) { + struct ChanUser *chanuser = data; + + increase_viscount_butone(chanuser->chan, chanuser); + + event_join(chanuser); +} + +static IRC_CMD(raw_002) { //fixed: ZNC fakes a 001 raw even if we're not connected! + struct UserNode *user = getUserByNick(argv[0]); + if(!user) + user = addUser(argv[0]); + client->user = user; + client->user->flags |= USERFLAG_ISBOT; + client->flags |= SOCKET_FLAG_READY; + event_bot_ready(client); + return 1; +} + +static int check_userbot_rejoin(struct UserNode *user) { + if(!(user->flags & USERFLAG_ISAUTHED)) return 0; + char tmp[MAXLEN]; + sprintf(tmp, "General.UserBots.%s.enabled", user->auth); + if(!get_int_field(tmp)) + return 0; + sprintf(tmp, "General.UserBots.%s.nicks", user->auth); + char *nicks = get_string_field(tmp); + if(nicks) { + char *cnick = nicks; + int matching_nick = 0; + do { + nicks = strchr(cnick, ','); + if(nicks) + *nicks = '\0'; + if(!match(cnick, user->nick)) + matching_nick = 1; + if(nicks) { + *nicks = ','; + nicks++; + } + } while((cnick = nicks) && !matching_nick); + if(!matching_nick) + return 0; + } + sprintf(tmp, "General.UserBots.%s.opless_part", user->auth); + if(!get_string_field(tmp)) + return 0; + return 1; +} + +static void free_rejoin_clients(struct ChanNode *chan, int rejoin) { + struct OplessRejoinBot *rejoin_bot, *next_rejoin_bot; + char tmp[MAXLEN]; + int sourceid; + struct ClientSocket *bot; + for(rejoin_bot = chan->rejoin_bots; rejoin_bot; rejoin_bot = next_rejoin_bot) { + next_rejoin_bot = rejoin_bot->next; + if(rejoin) { + if(rejoin_bot->is_client) { + putsock(rejoin_bot->bot.client, "JOIN %s", chan->name); + } else { + sprintf(tmp, "General.UserBots.%s.sourcebot", rejoin_bot->bot.userbot->auth); + if(get_string_field(tmp)) { + sourceid = resolve_botalias(get_string_field(tmp)); + if(sourceid == -1) + sourceid = 0; + } else + sourceid = 0; + bot = getChannelBot(NULL, sourceid); + if(!bot) + bot = getChannelBot(NULL, 0); + sprintf(tmp, "General.UserBots.%s.opless_join", rejoin_bot->bot.userbot->auth); + if(get_string_field(tmp)) + putsock(bot, get_string_field(tmp), rejoin_bot->bot.userbot->nick, chan->name); + } + } + if(!rejoin_bot->is_client) { + free(rejoin_bot->bot.userbot->nick); + free(rejoin_bot->bot.userbot->auth); + free(rejoin_bot->bot.userbot); + } + free(rejoin_bot); + } + if(chan->rejoin_timeout) + timeq_del(chan->rejoin_timeout); +} + +static TIMEQ_CALLBACK(full_rejoin_timeout) { + struct ChanNode *chan = data; + chan->rejoin_timeout = NULL; + free_rejoin_clients(chan, 1); + chan->flags &= ~CHANFLAG_REJOINING; +} + +static void check_full_rejoin(struct ChanNode *chan) { + struct ChanUser *chanuser; + char do_rejoin = 1; + int botcount = 0; + int userbots = 0; + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + if((chanuser->flags & CHANUSERFLAG_OPPED) || !(isBot(chanuser->user) || check_userbot_rejoin(chanuser->user))) { + do_rejoin = 0; + break; + } + botcount++; + if(!isBot(chanuser->user)) + userbots++; + } + if(do_rejoin) { + struct OplessRejoinBot *rejoin_bot; + struct ClientSocket *bot, *chanbot = NULL; + chan->rejoin_bots = NULL; + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(!isUserOnChan(bot->user, chan)) + continue; + if(!chanbot && ((bot->flags & SOCKET_FLAG_PREFERRED) || !getBots(SOCKET_FLAG_READY, bot))) + chanbot = bot; + else { + rejoin_bot = malloc(sizeof(*rejoin_bot)); + rejoin_bot->is_client = 1; + rejoin_bot->bot.client = bot; + rejoin_bot->next = chan->rejoin_bots; + chan->rejoin_bots = rejoin_bot; + putsock(bot, "PART %s :rejoining", chan->name); + } + } + if(userbots) { + char tmp[MAXLEN]; + int sourceid; + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + if(check_userbot_rejoin(chanuser->user)) { + rejoin_bot = malloc(sizeof(*rejoin_bot)); + rejoin_bot->is_client = 0; + rejoin_bot->bot.userbot = malloc(sizeof(struct OplessRejoinUserbot)); + rejoin_bot->bot.userbot->nick = strdup(chanuser->user->nick); + rejoin_bot->bot.userbot->auth = strdup(chanuser->user->auth); + rejoin_bot->next = chan->rejoin_bots; + chan->rejoin_bots = rejoin_bot; + sprintf(tmp, "General.UserBots.%s.sourcebot", chanuser->user->auth); + if(get_string_field(tmp)) { + sourceid = resolve_botalias(get_string_field(tmp)); + if(sourceid == -1) + sourceid = 0; + } else + sourceid = 0; + bot = getChannelBot(NULL, sourceid); + if(!bot) + bot = getChannelBot(NULL, 0); + sprintf(tmp, "General.UserBots.%s.opless_part", chanuser->user->auth); + putsock(bot, get_string_field(tmp), chanuser->user->nick, chan->name); + } + } + } + + if(botcount == 1) { + //we're alone + free(chan->rejoin_bots); + putsock(chanbot, "PART %s :magic hop", chan->name); + putsock(chanbot, "JOIN %s", chan->name); + } else { + chan->flags |= CHANFLAG_REJOINING; + chan->rejoin_timeout = timeq_add(10, 0, full_rejoin_timeout, chan); + } + } +} + +static IRC_CMD(raw_join) { + if(from == NULL || argc < 1) return 0; + SYNCHRONIZE(cache_sync); + struct UserNode *user = getUserByMask(from); + struct ChanNode *chan = getChanByName(argv[0]); + struct ChanUser *chanuser; + if(!chan && (!user || !(user->flags & USERFLAG_ISBOT))) { + //parse error if the channel is not known, yet and it's not a bot joining + DESYNCHRONIZE(cache_sync); + return 0; + } + if(user == NULL) + user = addUserMask(from); + if(chan == NULL) { + //new channel + chan = addChannel(argv[0]); + chanuser = addChanUser(chan, user); + chanuser->visCount = 1; + chan->botcount = 1; + get_userlist_with_invisible(chan, 0, got_channel_userlist, chanuser); + putsock(client, "MODE %s", chan->name); + putsock(client, "MODE %s +b", chan->name); + } else if((user->flags & USERFLAG_WAS_REGISTERING)) { + //user rejoined after registering (should still be present in the channel) + if(!(chanuser = getChanUser(user, chan))) + chanuser = addChanUser(chan, user); + chanuser->visCount++; + + event_registered(user, from); + user->flags &= ~USERFLAG_WAS_REGISTERING; + if(user->last_who > REWHO_TIMEOUT) + user->last_who -= REWHO_TIMEOUT; + + event_join(chanuser); + } else if(!(chan->flags & CHANFLAG_RECEIVED_USERLIST)) { + if(client->user != user) { //bots are allowed to add themselves + DESYNCHRONIZE(cache_sync); + return 1; //ignore join + } + + if(!(chanuser = getChanUser(user, chan))) { + chanuser = addChanUser(chan, user); + } + chanuser->visCount++; + chan->botcount++; + + if(isModeSet(chan->modes, 'D')) //if the bot joins a channel it could also be invisible + chanuser->flags |= CHANUSERFLAG_INVISIBLE; + + get_userlist_with_invisible(chan, 0, got_channel_userlist, chanuser); + } else if(!isUserOnChan(user, chan)) { + //join user to an existing channel + chanuser = addChanUser(chan, user); + chanuser->visCount = 1; + if(isBot(user) && client->user == user) { + if(isModeSet(chan->modes, 'D')) //if the bot joins a channel it could also be invisible + chanuser->flags |= CHANUSERFLAG_INVISIBLE; + increase_viscount_butone(chan, chanuser); + chan->botcount++; + } + + event_join(chanuser); + + if(!(user->flags & USERFLAG_ISBOT) && (chan->flags & CHANFLAG_REJOINING)) { + //ABORT AUTOMATIC REJOIN (security break) + free_rejoin_clients(chan, 1); + chan->flags &= ~CHANFLAG_REJOINING; + } + } else { + //user is already in the channel + chanuser = getChanUser(user, chan); + chanuser->visCount++; + + if(isBot(user) && client->user == user) { + increase_viscount_butone(chan, chanuser); + chan->botcount++; + } + + if(chanuser->visCount > chan->botcount) { + printf_log("main", LOG_WARNING, "visCount (%d) bigger than botcount (%d) on channel %s (user %s).", chanuser->visCount, chan->botcount, chan->name, user->nick); + chanuser->visCount = chan->botcount; + } + + //if multiple bots see the user, it can't be invisible + chanuser->flags &= ~CHANUSERFLAG_INVISIBLE; + } + DESYNCHRONIZE(cache_sync); + return 1; +} + +static IRC_CMD(raw_part) { + if(from == NULL || argc < 1) return 0; + SYNCHRONIZE(cache_sync); + struct UserNode *user = getUserByMask(from); + struct ChanNode *chan = getChanByName(argv[0]); + struct ChanUser *chanuser; + if(user == NULL || chan == NULL || !(chanuser = getChanUser(user, chan))) { + DESYNCHRONIZE(cache_sync); + return 0; + } + if(isBot(user) && user == client->user) { + decrease_viscount_butone(chan, chanuser); + chan->botcount--; + } + if(chanuser->flags & CHANUSERFLAG_PARTING) + chanuser->old_visCount--; + chanuser->visCount--; + if(chanuser->visCount == 0) { + delChanUser(chanuser, 0); //not free, yet! + event_part(chanuser, 0, (argc > 1 ? argv[1] : NULL)); + freeChanUser(chanuser); + } else if(!(chanuser->flags & CHANUSERFLAG_PARTING)) { + chanuser->flags |= CHANUSERFLAG_PARTING; + chanuser->old_visCount = chanuser->visCount; + } else if(chanuser->old_visCount == 0) { + int visCount = chanuser->visCount; + delChanUser(chanuser, 0); //not free, yet! + event_part(chanuser, 0, (argc > 1 ? argv[1] : NULL)); + freeChanUser(chanuser); + chanuser = addChanUser(chan, user); + chanuser->visCount = visCount; + event_join(chanuser); + } + + //check if channel is still present + int keep_channel = 1; + if(chan->usercount == 0) { + if((chan->flags & CHANFLAG_REJOINING)) { + free_rejoin_clients(chan, 1); + chan->flags &= ~CHANFLAG_REJOINING; + } + delChannel(chan, 1); + keep_channel = 0; + } else if(isBot(user)) //bot parted - check if theres another bot in the channel + keep_channel = checkChannelVisibility(chan); + + // free user if he/she is in no other channel + if(user->channel == NULL && !(user->flags & USERFLAG_ISBOT)) + delUser(user, 1); + + if(keep_channel && (chan->flags & CHANFLAG_RECEIVED_USERLIST) && !(chan->flags & CHANFLAG_REJOINING)) + check_full_rejoin(chan); + else if(keep_channel && (chan->flags & CHANFLAG_REJOINING) && chan->usercount == 1) { + //bot is alone... rejoin! + struct ClientSocket *bot; + //find the last bot in the channel + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(isUserOnChan(bot->user, chan)) { + putsock(bot, "PART %s :magic hop", chan->name); + putsock(bot, "JOIN %s", chan->name); + } + } + } + DESYNCHRONIZE(cache_sync); + return 1; +} + +static IRC_CMD(raw_kick) { + if(from == NULL || argc < 3) return 0; + SYNCHRONIZE(cache_sync); + struct UserNode *user = getUserByMask(from); + struct UserNode *target = getUserByNick(argv[1]); + struct ChanNode *chan = getChanByName(argv[0]); + struct ChanUser *chanuser; + if(chan == NULL || target == NULL || !(chanuser = getChanUser(target, chan))) { + DESYNCHRONIZE(cache_sync); + return 0; + } + if(isBot(target) && target == client->user) { + decrease_viscount_butone(chan, chanuser); + chan->botcount--; + } + chanuser->visCount--; + if(chanuser->visCount == 0) { + delChanUser(chanuser, 0); //not free, yet! + if(user) + event_kick(user, chanuser, argv[2]); + freeChanUser(chanuser); + } + + //check if channel is still present + int keep_channel = 1; + if(chan->usercount == 0) { + if((chan->flags & CHANFLAG_REJOINING)) { + free_rejoin_clients(chan, 1); + chan->flags &= ~CHANFLAG_REJOINING; + } + delChannel(chan, 1); + keep_channel = 0; + } else if(isBot(target)) //bot parted - check if theres another bot in the channel + keep_channel = checkChannelVisibility(chan); + + // free user if he/she is in no other channel + if(target->channel == NULL && !(target->flags & USERFLAG_ISBOT)) + delUser(target, 1); + + if(keep_channel && (chan->flags & CHANFLAG_RECEIVED_USERLIST) && !(chan->flags & CHANFLAG_REJOINING)) + check_full_rejoin(chan); + else if(keep_channel && (chan->flags & CHANFLAG_REJOINING) && chan->usercount == 1) { + //bot is alone... rejoin! + struct ClientSocket *bot; + //find the last bot in the channel + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(isUserOnChan(bot->user, chan)) { + putsock(bot, "PART %s :magic hop", chan->name); + putsock(bot, "JOIN %s", chan->name); + } + } + } + DESYNCHRONIZE(cache_sync); + return 1; +} + +static IRC_CMD(raw_quit) { + if(from == NULL || argc < 1) return 0; + SYNCHRONIZE(cache_sync); + struct UserNode *user = getUserByMask(from); + if(user == NULL) { + DESYNCHRONIZE(cache_sync); + return 0; + } + + if(client->user == user) { + DESYNCHRONIZE(cache_sync); + return 0; + } + + //check if user is just registering + if(!stricmp(argv[0], "Registered")) + user->flags |= USERFLAG_WAS_REGISTERING; + + //decrease visCount counter + struct ChanUser *chanuser, *next_chanuser; + for(chanuser = getUserChannels(user, NULL); chanuser; chanuser = next_chanuser) { + next_chanuser = getUserChannels(user, chanuser); + //decrease visCount counter only if client is in the channel + if(isUserOnChan(client->user, chanuser->chan)) { + chanuser->visCount--; + if(chanuser->visCount <= 0 && !(user->flags & USERFLAG_WAS_REGISTERING)) { + delChanUser(chanuser, 0); //not free, yet! + event_part(chanuser, 1, argv[0]); + if((chanuser->chan->flags & CHANFLAG_RECEIVED_USERLIST) && !(chanuser->chan->flags & CHANFLAG_REJOINING)) + check_full_rejoin(chanuser->chan); + freeChanUser(chanuser); + } + } + } + + if(user->channel == NULL) { + if(isBot(user)) { + //ASSERT + return 0; + } + if((user->flags & USERFLAG_WAS_REGISTERING)) { + //TODO: set a timeout or sth like that? + } else + delUser(user, 1); + } + + DESYNCHRONIZE(cache_sync); + return 1; +} + +void bot_disconnect(struct ClientSocket *client) { + struct UserNode *user = client->user; + if(user) { + //just remove the bot mark from the user and handle a normal quit :) + user->flags &= ~USERFLAG_ISBOT; + client->user = NULL; + client->flags &= ~SOCKET_FLAG_READY; + + //decrease visCount counter + struct ChanUser *chanuser, *next_chanuser; + for(chanuser = getUserChannels(user, NULL); chanuser; chanuser = next_chanuser) { + next_chanuser = getUserChannels(user, chanuser); + decrease_viscount_butone(chanuser->chan, chanuser); + chanuser->chan->botcount--; + chanuser->visCount--; + if(chanuser->visCount <= 0) { + delChanUser(chanuser, 0); //not free, yet! + event_part(chanuser, 1, "QUIT"); + if(chanuser->chan->flags & CHANFLAG_RECEIVED_USERLIST) + checkChannelVisibility(chanuser->chan); + freeChanUser(chanuser); + } + } + + if(user->channel == NULL) + delUser(user, 1); + } +} + +static struct ClientSocket *get_first_prefered_bot_in_channel(struct ChanNode *chan) { + struct ClientSocket *bot, *chanbot = NULL; + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(!isUserOnChan(bot->user, chan)) + continue; + if(bot->flags & SOCKET_FLAG_PREFERRED) + return bot; + chanbot = bot; + } + return chanbot; +} + +static IRC_CMD(raw_topic) { + if(from == NULL || argc < 2) return 0; + SYNCHRONIZE(cache_sync); + struct UserNode *user = getUserByMask(from); + struct ChanNode *chan = getChanByName(argv[0]); + if(chan == NULL) { + DESYNCHRONIZE(cache_sync); + return 0; + } + if(client != get_first_prefered_bot_in_channel(chan)) { + DESYNCHRONIZE(cache_sync); + return 1; //just ignore it to prevent event duplicates + } + if(user == NULL) { + user = createTempUserMask(from); + if(!user) { + DESYNCHRONIZE(cache_sync); + return 0; + } + user->flags |= USERFLAG_ISTMPUSER; + } + event_topic(user, chan, argv[1]); + strcpy(chan->topic, argv[1]); + DESYNCHRONIZE(cache_sync); + return 1; +} + +static IRC_CMD(raw_privmsg) { + if(from == NULL || argc < 2) return 0; + struct UserNode *user = getUserByMask(from); + if(user == NULL) { + if(stricmplen(from, "***!", 4) == 0) return 1; /* ZNC Playback */ + user = createTempUserMask(from); + if(!user) return 0; + user->flags |= USERFLAG_ISTMPUSER; + } + if(argv[0][0] == '#') { //Channel message + struct ChanNode *chan = getChanByName(argv[0]); + if(chan && client == get_first_prefered_bot_in_channel(chan)) { + statistics_privmsg++; + if(argv[1][0] == '\001') { + char *cmd = &argv[1][1]; + char *text = strstr(cmd, " "); + if(text) { + *text = '\0'; + text++; + if(strlen(text) && text[strlen(text)-1] == '\001') + text[strlen(text)-1] = '\0'; + } else if(strlen(cmd) && cmd[strlen(cmd)-1] == '\001') + cmd[strlen(cmd)-1] = '\0'; + event_chanctcp(user, chan, cmd, text); + } else + event_chanmsg(user, chan, argv[1]); + } + } else { + struct UserNode *target = getUserByNick(argv[0]); + if(target) { + if(argv[1][0] == '\001') { + char *cmd = &argv[1][1]; + char *text = strstr(cmd, " "); + if(text) { + *text = '\0'; + text++; + if(strlen(text) && text[strlen(text)-1] == '\001') + text[strlen(text)-1] = '\0'; + } else if(strlen(cmd) && cmd[strlen(cmd)-1] == '\001') + cmd[strlen(cmd)-1] = '\0'; + event_privctcp(user, target, cmd, text); + } else + event_privmsg(user, target, argv[1]); + } + } + return 1; +} + +static IRC_CMD(raw_notice) { + if(from == NULL && argc && !stricmp(argv[0], "AUTH")) return 1; //NOTICE AUTH is NOT a parse error ;) + if(from == NULL || argc < 2) return 0; + struct UserNode *user = getUserByMask(from); + if(user == NULL) { + user = createTempUserMask(from); + if(!user) return 0; + user->flags |= USERFLAG_ISTMPUSER; + } + if(argv[0][0] == '#') { //Channel notice + struct ChanNode *chan = getChanByName(argv[0]); + if(chan && client == get_first_prefered_bot_in_channel(chan)) + event_channotice(user, chan, argv[1]); + } else { + struct UserNode *target = getUserByNick(argv[0]); + if(target) + event_privnotice(user, target, argv[1]); + } + return 1; +} + +static void client_renamed(struct ClientSocket *client); + +static IRC_CMD(raw_nick) { + if(from == NULL || argc == 0) return 0; + SYNCHRONIZE(cache_sync); + struct UserNode *user = getUserByMask(from); + if(user == NULL) { + DESYNCHRONIZE(cache_sync); + return 1; //maybe already renamed - no parse error + } + if(isBot(user)) { + if(client->user != user) { + DESYNCHRONIZE(cache_sync); + return 1; + } + client_renamed(client); + } + else if(!strcmp(user->nick, argv[0])) { + DESYNCHRONIZE(cache_sync); + return 1; //user has already this nick (case sensitive) + } + event_nick(user, argv[0]); + renameUser(user, argv[0]); + DESYNCHRONIZE(cache_sync); + return 1; +} + +static IRC_CMD(raw_ping) { + if(argc == 0) return 0; + putsock(client, "PONG :%s", argv[0]); + return 1; +} + +static IRC_CMD(raw_354) { + recv_whohandler_354(client, argv, argc); + return 1; +} + +static IRC_CMD(raw_315) { + recv_whohandler_315(client, argv, argc); + return 1; +} + +static IRC_CMD(raw_324) { //MODE LIST + //Watchcat #pktest +stnzN + if(from == NULL || argc < 3) return 0; + struct ChanNode *chan = getChanByName(argv[1]); + if(chan == NULL) return 0; + parseModes(chan->modes, argv[2], argv+3, argc-3); + return 1; +} + +static IRC_CMD(raw_invite) { + if(from == NULL || argc < 2) return 0; + struct UserNode *user = getUserByMask(from); + if(user == NULL) { + user = createTempUserMask(from); + if(!user) return 0; + user->flags |= USERFLAG_ISTMPUSER; + } + event_invite(client, user, argv[1]); + return 1; +} + +static IRC_CMD(raw_mode) { + if(from == NULL || argc < 2) return 0; + SYNCHRONIZE(cache_sync); + struct UserNode *user = getUserByMask(from); + if(user == NULL) { + user = createTempUserMask(from); + if(!user) { + DESYNCHRONIZE(cache_sync); + return 0; + } + user->flags |= USERFLAG_ISTMPUSER; + } + if(argv[0][0] == '#') { + //ChannelMode + struct ChanNode *chan = getChanByName(argv[0]); + if(!chan) { + DESYNCHRONIZE(cache_sync); + return 0; + } + if(client != get_first_prefered_bot_in_channel(chan)) { + DESYNCHRONIZE(cache_sync); + return 1; + } + parseModes(chan->modes, argv[1], argv+2, argc-2); + event_mode(user, chan, argv[1], argv+2, argc-2); + } else { + //UserMode + if(stricmp(client->user->nick, argv[0])) { + DESYNCHRONIZE(cache_sync); + return 0; + } + parseUserModes(client->user, argv[1]); + } + DESYNCHRONIZE(cache_sync); + return 1; +} + +static IRC_CMD(raw_367) { + //Watchcat #pktest pk911!*@* TestBot!~bot@pktest.user.WebGamesNet 1315863279 + struct ChanNode *chan = getChanByName(argv[1]); + if(!chan) return 0; + struct BanNode *ban; + while((ban = getMatchingChannelBan(chan, argv[2]))) { + removeChannelBan(ban); + } + addChannelBan(chan, argv[2]); + return 1; +} + +static IRC_CMD(raw_251) { + if(argc < 2) return 0; + char *total_user_str = argv[1]; + char total_visible[20], total_invisible[20]; + int i = 0, total_visible_pos = 0, total_invisible_pos = 0; + while(*total_user_str) { + if(*total_user_str == ' ') { + i++; + } else if(i == 2) { + if(total_visible_pos < 20) + total_visible[total_visible_pos++] = *total_user_str; + } else if(i == 5) { + if(total_invisible_pos < 20) + total_invisible[total_invisible_pos++] = *total_user_str; + } + total_user_str++; + } + total_visible[total_visible_pos] = '\0'; + total_invisible[total_invisible_pos] = '\0'; + statistics_network_users = atoi(total_visible) + atoi(total_invisible); + return 1; +} + +static IRC_CMD(raw_254) { + if(argc < 3) return 0; + statistics_network_channels = atoi(argv[1]); + statistics_update(); + return 1; +} + +static IRC_CMD(raw_332) { + //Skynet #neonserv :topic + struct ChanNode *chan = getChanByName(argv[1]); + if(!chan) return 0; + strcpy(chan->topic, argv[2]); + return 1; +} + +struct ClientRenamePartedChannel { + char channel[CHANNELLEN+1]; + struct ClientRenamePartedChannel *next; +}; + +static IRC_CMD(raw_437) { //can NOT change nick + struct ClientRenamePartedChannel *partedchan = malloc(sizeof(*partedchan)); + strcpy(partedchan->channel, argv[1]); + if((client->flags & SOCKET_FLAG_CHANGENICK)) + partedchan->next = client->changenick_channels; + else + partedchan->next = NULL; + client->changenick_channels = partedchan; + client->flags |= SOCKET_FLAG_CHANGENICK; + putsock(client, "PART %s", argv[1]); + putsock(client, "NICK %s", client->nick); + return 1; +} + +static void client_renamed(struct ClientSocket *client) { + if((client->flags & SOCKET_FLAG_CHANGENICK)) { + struct ClientRenamePartedChannel *partedchan, *nextchan; + for(partedchan = client->changenick_channels; partedchan; partedchan = nextchan) { + nextchan = partedchan->next; + putsock(client, "JOIN %s", partedchan->channel); + free(partedchan); + } + client->flags &= ~SOCKET_FLAG_CHANGENICK; + } +} + +static void raw_005_network(struct ClientSocket *client, char *value) { + if(!value) return; + //check all other networknames + //if they are NOT simular to value throw a warning + SYNCHRONIZE(cache_sync); //all bots connect to the same time so there is a higher chance that this code is running on multiple threads at the same time + if(client->network_name) + free(client->network_name); + client->network_name = strdup(value); + struct ClientSocket *bot; + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(bot == client) continue; + if(!bot->network_name) continue; + if(stricmp(bot->network_name, value)) { + printf_log("main", LOG_ERROR, "WARNING: Network name '%s' (%s) differs from '%s' (%s)! Connecting to multiple IRC-Networks with one instance is NOT supported!\n", client->network_name, client->nick, bot->network_name, bot->nick); + break; + } + } + DESYNCHRONIZE(cache_sync); +} + +static IRC_CMD(raw_005) { + char *ptr1 = merge_argv(argv, 1, argc); + char *ptr2, *name, *value; + do { + ptr2 = strchr(ptr1, ' '); + if(ptr2) + *ptr2 = '\0'; + name = ptr1; + if((value = strchr(ptr1, '='))) { + *value = '\0'; + value++; + } + if(!stricmp(name, "NETWORK")) raw_005_network(client, value); + if(ptr2) + ptr1 = ptr2 + 1; + } while(ptr2); + return 1; +} + +static IRC_CMD(raw_nojoin) { + if(from == NULL || argc < 3) return 0; + struct ChanNode *chan = getChanByName(argv[1]); + if(chan == NULL) return 0; + if(client->flags & SOCKET_FLAG_REQUEST_INVITE) + requestInvite(client->user, chan); + return 1; +} + +void init_parser() { + //all the raws we receive... + register_irc_function("437", raw_437); + register_irc_function("002", raw_002); + register_irc_function("005", raw_005); + register_irc_function("251", raw_251); + register_irc_function("254", raw_254); + register_irc_function("324", raw_324); + register_irc_function("332", raw_332); + register_irc_function("367", raw_367); + register_irc_function("471", raw_nojoin); + register_irc_function("473", raw_nojoin); + register_irc_function("474", raw_nojoin); + register_irc_function("475", raw_nojoin); + register_irc_function("INVITE", raw_invite); + register_irc_function("NOTICE", raw_notice); + register_irc_function("TOPIC", raw_topic); + register_irc_function("KICK", raw_kick); + register_irc_function("PART", raw_part); + register_irc_function("QUIT", raw_quit); + register_irc_function("JOIN", raw_join); + register_irc_function("MODE", raw_mode); + register_irc_function("NICK", raw_nick); + register_irc_function("354", raw_354); + register_irc_function("315", raw_315); + register_irc_function("PING", raw_ping); + register_irc_function("PRIVMSG", raw_privmsg); +} + +void free_parser() { + struct irc_cmd *cmd, *next; + for(cmd = irc_commands; cmd; cmd = next) { + next = cmd->next; + free(cmd); + } +} + +void reply(struct ClientSocket *client, struct UserNode *user, const char *text, ...) { + const char *reply_format = get_language_string(user, text); + if(reply_format == NULL) + reply_format = text; + loadUserSettings(user); + char formatBuf[MAXLEN]; + sprintf(formatBuf, "%s %s :%s", ((user->flags & USERFLAG_REPLY_PRIVMSG) ? "PRIVMSG" : "NOTICE"), user->nick, reply_format); + va_list arg_list; + char sendBuf[MAXLEN]; + int pos; + if (!(client->flags & SOCKET_FLAG_CONNECTED)) return; + sendBuf[0] = '\0'; + va_start(arg_list, text); + pos = vsnprintf(sendBuf, MAXLEN - 2, formatBuf, arg_list); + va_end(arg_list); + if (pos < 0 || pos > (MAXLEN - 2)) pos = MAXLEN - 2; + sendBuf[pos] = '\n'; + sendBuf[pos+1] = '\0'; + write_socket(client, sendBuf, pos+1); +} + +char* merge_argv(char **argv, int start, int end) { + return merge_argv_char(argv, start, end, ' '); +} + +char* merge_argv_char(char **argv, int start, int end, char seperator) { + int i; + char *p = NULL; + for(i = start; i < end; i++) { + p = argv[i]; + while(*p) p++; + if(i < end-1) { + while(p != argv[i+1]) { + *p++ = seperator; + } + } else + *p = seperator; + } + if(p) *p = '\0'; + return argv[start]; +} diff --git a/src/IRCParser.h b/src/IRCParser.h new file mode 100644 index 0000000..7e06f8d --- /dev/null +++ b/src/IRCParser.h @@ -0,0 +1,47 @@ +/* IRCParser.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _IRCParser_h +#define _IRCParser_h + +#include "main.h" + +struct ClientSocket; +struct UserNode; + +#define IRC_CMD(NAME) int NAME(struct ClientSocket *client, UNUSED_ARG(char *from), UNUSED_ARG(char **argv), UNUSED_ARG(unsigned int argc)) +typedef IRC_CMD(irc_cmd_t); + +struct irc_cmd { + char *cmd; + irc_cmd_t *func; + struct irc_cmd *next; +}; + +#ifndef DND_FUNCTIONS +extern int statistics_privmsg; +extern int statistics_network_users; +extern int statistics_network_channels; + +void parse_line(struct ClientSocket *client, char *line); +void bot_disconnect(struct ClientSocket *client); +void init_parser(); +void free_parser(); +/* MODULAR ACCESSIBLE */ void reply(struct ClientSocket *client, struct UserNode *user, const char *text, ...); +/* MODULAR ACCESSIBLE */ char* merge_argv(char **argv, int start, int end); +/* MODULAR ACCESSIBLE */ char* merge_argv_char(char **argv, int start, int end, char seperator); +#endif +#endif diff --git a/src/IRCQueue.c b/src/IRCQueue.c new file mode 100644 index 0000000..a7cc1a4 --- /dev/null +++ b/src/IRCQueue.c @@ -0,0 +1,266 @@ +/* IRCQueue.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#include "IRCQueue.h" +#include "ClientSocket.h" +#include "IOHandler.h" +#include "tools.h" +#include "log.h" + +#define MAXPENALTY 8 /* 4 messages */ + +struct QueueEntry { + char *msg; + struct QueueEntry *next; +}; + +struct BotQueue { + struct ClientSocket *client; + struct IODescriptor *iofd; + int penalty : 8; + int rem_penalty : 8; + struct QueueEntry *fastqueue_first, *fastqueue_last; + struct QueueEntry *normalqueue_first, *normalqueue_last; + struct QueueEntry *textqueue_first, *textqueue_last; +}; + +static IOHANDLER_CALLBACK(queue_callback); + +static struct BotQueue *initialize_queue(struct ClientSocket *client) { + struct BotQueue *queue = malloc(sizeof(*queue)); + if (!queue) { + printf_log("main", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return NULL; + } + queue->client = client; + client->queue = queue; + queue->iofd = NULL; + queue->penalty = 0; + queue->fastqueue_first = NULL; + queue->fastqueue_last = NULL; + queue->normalqueue_first = NULL; + queue->normalqueue_last = NULL; + queue->textqueue_first = NULL; + queue->textqueue_last = NULL; + return queue; +} + +static int calculate_penalty(char *message) { + int msglen = strlen(message); + int penalty = (2 + msglen / 100); + return penalty; +} + +int queue_add(struct ClientSocket *client, char* msg, int len) { + if(!client->queue) + client->queue = initialize_queue(client); + struct BotQueue *queue = client->queue; + char *args = strstr(msg, " "); + int type; + int add_queue = 0; + if(args) { + *args = '\0'; + if(!stricmp(msg, "MODE")) + type = 3; + else if(!stricmp(msg, "KICK")) + type = 3; + else if(!stricmp(msg, "PONG")) + type = 3; + else if(!stricmp(msg, "PRIVMSG")) + type = 1; + else if(!stricmp(msg, "NOTICE")) + type = 1; + else if(!stricmp(msg, "WHO")) + type = 1; + else + type = 2; + *args = ' '; + } else + type = 2; + + //check if we need to queue + switch(type) { + case 1: + if(queue->textqueue_first) { + add_queue = 1; + break; + } + case 2: + if(queue->normalqueue_first) { + add_queue = 1; + break; + } + case 3: + if(queue->fastqueue_first) { + add_queue = 1; + break; + } + default: + if(queue->penalty >= MAXPENALTY) + add_queue = 1; + break; + } + + if(!add_queue) { + int penalty = calculate_penalty(msg); + write_socket_force(client, msg, len); + queue->penalty += penalty; + if(!queue->iofd) { + struct timeval timeout; + gettimeofday(&timeout, NULL); + if(queue->penalty >= MAXPENALTY) + queue->rem_penalty = (queue->penalty - MAXPENALTY)+1; + else + queue->rem_penalty = queue->penalty; + timeout.tv_sec += queue->rem_penalty; + queue->iofd = iohandler_timer(timeout, queue_callback); + queue->iofd->data = queue; + } + } else { + struct QueueEntry *entry = malloc(sizeof(*entry)); + if (!entry) { + printf_log("main", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return 0; + } + entry->msg = strdup(msg); + entry->next = NULL; + if(type == 1) { //low priority + if(queue->textqueue_last) { + queue->textqueue_last->next = entry; + queue->textqueue_last = entry; + } else { + queue->textqueue_last = entry; + queue->textqueue_first = entry; + } + } else if(type == 2) { //normal priority + if(queue->normalqueue_last) { + queue->normalqueue_last->next = entry; + queue->normalqueue_last = entry; + } else { + queue->normalqueue_last = entry; + queue->normalqueue_first = entry; + } + } else if(type == 3) { //high priority + if(queue->fastqueue_last) { + queue->fastqueue_last->next = entry; + queue->fastqueue_last = entry; + } else { + queue->fastqueue_last = entry; + queue->fastqueue_first = entry; + } + } + } + return 1; +} + +static void dequeue_bot(struct ClientSocket *client) { + if(client->queue->penalty >= MAXPENALTY) return; + int penalty; + //try to send high priority messages + if(client->queue->fastqueue_first) { + do { + struct QueueEntry *entry = client->queue->fastqueue_first; + if(!entry->next) + client->queue->fastqueue_last = NULL; + client->queue->fastqueue_first = client->queue->fastqueue_first->next; + penalty = calculate_penalty(entry->msg); + write_socket_force(client, entry->msg, strlen(entry->msg)); + client->queue->penalty += penalty; + free(entry->msg); + free(entry); + } while(client->queue->penalty < MAXPENALTY && client->queue->fastqueue_first); + } + if(client->queue->penalty >= MAXPENALTY) return; + //try to send normal priority messages + if(client->queue->normalqueue_first) { + do { + struct QueueEntry *entry = client->queue->normalqueue_first; + if(!entry->next) + client->queue->normalqueue_last = NULL; + client->queue->normalqueue_first = client->queue->normalqueue_first->next; + penalty = calculate_penalty(entry->msg); + write_socket_force(client, entry->msg, strlen(entry->msg)); + client->queue->penalty += penalty; + free(entry->msg); + free(entry); + } while(client->queue->penalty < MAXPENALTY && client->queue->normalqueue_first); + } + if(client->queue->penalty >= MAXPENALTY) return; + //try to send low priority messages + if(client->queue->textqueue_first) { + do { + struct QueueEntry *entry = client->queue->textqueue_first; + if(!entry->next) + client->queue->textqueue_last = NULL; + client->queue->textqueue_first = client->queue->textqueue_first->next; + penalty = calculate_penalty(entry->msg); + write_socket_force(client, entry->msg, strlen(entry->msg)); + client->queue->penalty += penalty; + free(entry->msg); + free(entry); + } while(client->queue->penalty < MAXPENALTY && client->queue->textqueue_first); + } +} + +void queue_destroy(struct ClientSocket *client) { + if(!client->queue) return; + struct QueueEntry *entry, *next; + for(entry = client->queue->fastqueue_first; entry; entry = next) { + next = entry->next; + free(entry->msg); + free(entry); + } + for(entry = client->queue->normalqueue_first; entry; entry = next) { + next = entry->next; + free(entry->msg); + free(entry); + } + for(entry = client->queue->textqueue_first; entry; entry = next) { + next = entry->next; + free(entry->msg); + free(entry); + } + if(client->queue->iofd) + iohandler_close(client->queue->iofd); + free(client->queue); + client->queue = NULL; +} + +static IOHANDLER_CALLBACK(queue_callback) { + struct BotQueue *queue = event->iofd->data; + switch(event->type) { + case IOEVENT_TIMEOUT: + queue->penalty -= queue->rem_penalty; + dequeue_bot(queue->client); + if(queue->penalty > 0) { + struct timeval timeout; + gettimeofday(&timeout, NULL); + if(queue->penalty >= MAXPENALTY) + queue->rem_penalty = (queue->penalty - MAXPENALTY)+1; + else + queue->rem_penalty = queue->penalty; + timeout.tv_sec += queue->rem_penalty; + queue->iofd = iohandler_timer(timeout, queue_callback); + queue->iofd->data = queue; + } else { + queue->iofd = NULL; + queue->penalty = 0; + } + break; + default: + break; + } +} diff --git a/src/IRCQueue.h b/src/IRCQueue.h new file mode 100644 index 0000000..cd27b1c --- /dev/null +++ b/src/IRCQueue.h @@ -0,0 +1,27 @@ +/* IRCQueue.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _IRCQueue_h +#define _IRCQueue_h + +#include "main.h" + +struct ClientSocket; + +int queue_add(struct ClientSocket *client, char* msg, int len); +void queue_destroy(struct ClientSocket *client); + +#endif \ No newline at end of file diff --git a/src/ModeNode.c b/src/ModeNode.c new file mode 100644 index 0000000..f3579cd --- /dev/null +++ b/src/ModeNode.c @@ -0,0 +1,366 @@ +/* ModeNode.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#include "ModeNode.h" +#include "ChanNode.h" +#include "ChanUser.h" +#include "UserNode.h" +#include "BanNode.h" +#include "log.h" + +static int modes_with_strarg, modes_with_intarg, modes_count; + +unsigned int valid_modes[] = { /* Thats our mode list :P */ + 1, 'b', CHANNEL_MODE_TYPE_A, + 2, 'o', CHANNEL_MODE_TYPE_A, + 3, 'h', CHANNEL_MODE_TYPE_A, + 4, 'v', CHANNEL_MODE_TYPE_A, + 5, 'k', CHANNEL_MODE_TYPE_B | CHANNEL_MODE_VALUE_STRING | CHANNEL_MODE_KEY, + 6, 'a', CHANNEL_MODE_TYPE_C | CHANNEL_MODE_VALUE_INTEGER, + 7, 'l', CHANNEL_MODE_TYPE_C | CHANNEL_MODE_VALUE_INTEGER, + 8, 'f', CHANNEL_MODE_TYPE_C | CHANNEL_MODE_VALUE_STRING, + 9, 'F', CHANNEL_MODE_TYPE_C | CHANNEL_MODE_VALUE_STRING, + 10, 'c', CHANNEL_MODE_TYPE_D, + 11, 'C', CHANNEL_MODE_TYPE_D, + 12, 'i', CHANNEL_MODE_TYPE_D, + 13, 'm', CHANNEL_MODE_TYPE_D, + 14, 'M', CHANNEL_MODE_TYPE_D, + 15, 'n', CHANNEL_MODE_TYPE_D, + 16, 'N', CHANNEL_MODE_TYPE_D, + 17, 'p', CHANNEL_MODE_TYPE_D, + 18, 'r', CHANNEL_MODE_TYPE_D, + 19, 's', CHANNEL_MODE_TYPE_D, + 20, 'S', CHANNEL_MODE_TYPE_D, + 21, 't', CHANNEL_MODE_TYPE_D, + 22, 'u', CHANNEL_MODE_TYPE_D, + 23, 'D', CHANNEL_MODE_TYPE_D, + 24, 'd', CHANNEL_MODE_TYPE_D, + 25, 'R', CHANNEL_MODE_TYPE_D, + 26, 'z', CHANNEL_MODE_TYPE_D, +// ^ maximum is 32!!! + 0x00, 0x00, 0x00 +}; + +void init_ModeNode() { + unsigned int *mode, flag = 1; + modes_with_strarg = 0; + modes_with_intarg = 0; + modes_count = 0; + for (mode = valid_modes; mode[1]; mode += 3) { + if((mode[2] & CHANNEL_MODE_VALUE) == CHANNEL_MODE_VALUE_STRING) { + mode[2] |= modes_with_strarg << CHANNEL_MODE_VALUE_INDEX_SHIFT; + modes_with_strarg++; + } + if((mode[2] & CHANNEL_MODE_VALUE) == CHANNEL_MODE_VALUE_INTEGER) { + mode[2] |= modes_with_intarg << CHANNEL_MODE_VALUE_INDEX_SHIFT; + modes_with_intarg++; + } + modes_count++; + mode[0] = flag; + flag = flag << 1; + } +} + +struct ModeNode *createModeNode(struct ChanNode *chan) { + struct ModeNode *modes = malloc(sizeof(*modes)); + if (!modes) + { + printf_log("main", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return NULL; + } + modes->chan = chan; + modes->modes = 0; + modes->allmodes = 0; + modes->mode_str_args = calloc(modes_with_strarg, sizeof(char*)); + modes->mode_int_args = calloc(modes_with_intarg, sizeof(int)); + return modes; +} + +void freeModeNode(struct ModeNode *modes) { + int i; + for(i = 0; i < modes_with_strarg; i++) { + if(modes->mode_str_args[i]) + free(modes->mode_str_args[i]); + } + free(modes->mode_str_args); + free(modes->mode_int_args); + free(modes); +} + +static unsigned int* getModeOptions(char mode) { + unsigned int *cmode; + for (cmode = valid_modes; cmode[1]; cmode += 3) { + if(cmode[1] == mode) + return cmode; + } + return NULL; +} + +int isModeSet(struct ModeNode* modes, char modeChar) { + unsigned int *modeOpt = getModeOptions(modeChar); + return (modes->modes & modeOpt[0]); +} + +int isModeAffected(struct ModeNode* modes, char modeChar) { + unsigned int *modeOpt = getModeOptions(modeChar); + return (modes->allmodes & modeOpt[0]); +} + +void* getModeValue(struct ModeNode* modes, char modeChar) { + #define MODE_VALUE_INDEX (modeOpt[2] & CHANNEL_MODE_VALUE_INDEX_MASK) >> CHANNEL_MODE_VALUE_INDEX_SHIFT + unsigned int *modeOpt = getModeOptions(modeChar); + if((modeOpt[2] & CHANNEL_MODE_VALUE) == CHANNEL_MODE_VALUE_STRING) + return modes->mode_str_args[MODE_VALUE_INDEX]; + if((modeOpt[2] & CHANNEL_MODE_VALUE) == CHANNEL_MODE_VALUE_INTEGER) + return &modes->mode_int_args[MODE_VALUE_INDEX]; + return NULL; + #undef MODE_VALUE_INDEX +} + +unsigned int getModeType(struct ModeNode* modes, char modeChar) { + #define MODE_VALUE_INDEX (modeOpt[2] & CHANNEL_MODE_VALUE_INDEX_MASK) >> CHANNEL_MODE_VALUE_INDEX_SHIFT + unsigned int *modeOpt = getModeOptions(modeChar); + if(!modeOpt) return 0; + return modeOpt[2]; + #undef MODE_VALUE_INDEX +} + +static void parseModesUserPriv(struct ModeNode* modes, unsigned char flag, int add, char *nick) { + if(modes->chan == NULL) return; + struct UserNode *user = getUserByNick(nick); + if(user == NULL) return; + struct ChanUser *chanuser = getChanUser(user, modes->chan); + if(chanuser == NULL) return; + if(add) + chanuser->flags |= flag; + else + chanuser->flags &= ~flag; + if((chanuser->flags & CHANUSERFLAG_OPPED_OR_VOICED) && (chanuser->flags & CHANUSERFLAG_INVISIBLE)) + chanuser->flags &= ~CHANUSERFLAG_INVISIBLE; +} + +static void parseModesBan(struct ModeNode* modes, int add, char *mask) { + if(modes->chan == NULL) return; + if(add) + addChannelBan(modes->chan, mask); + else + removeChannelBanMask(modes->chan, mask); +} + +void parseModes(struct ModeNode* modes, char *modeStr, char **argv, int argc) { + int i, argpos = 0, add = 1; + #define MODE_TYPE (modeOpt[2] & CHANNEL_MODE_TYPE) + #define MODE_VALUE (modeOpt[2] & CHANNEL_MODE_VALUE) + #define MODE_VALUE_INDEX (modeOpt[2] & CHANNEL_MODE_VALUE_INDEX_MASK) >> CHANNEL_MODE_VALUE_INDEX_SHIFT + unsigned int *modeOpt; + for(i = 0; i < strlen(modeStr); i++) { + if(modeStr[i] == '+') { + add = 1; + continue; + } + if(modeStr[i] == '-') { + add = 0; + continue; + } + modeOpt = getModeOptions(modeStr[i]); + if(!modeOpt) continue; // unknown mode? + if(MODE_TYPE == CHANNEL_MODE_TYPE_A) { + if(argpos == argc) continue; + //special mode ;) + switch(modeStr[i]) { + case 'o': + parseModesUserPriv(modes, CHANUSERFLAG_OPPED, add, argv[argpos]); + break; + case 'h': + parseModesUserPriv(modes, CHANUSERFLAG_HALFOPPED, add, argv[argpos]); + break; + case 'v': + parseModesUserPriv(modes, CHANUSERFLAG_VOICED, add, argv[argpos]); + break; + case 'b': + parseModesBan(modes, add, argv[argpos]); + break; + default: + //we have an unknown TYPE_A mode??? + break; + } + argpos++; + continue; + } + if(add) { + if(MODE_TYPE != CHANNEL_MODE_TYPE_D) { //all other types take parameters when set + if(argpos == argc) continue; + if(MODE_VALUE == CHANNEL_MODE_VALUE_STRING) { + if(modes->mode_str_args[MODE_VALUE_INDEX]) + free(modes->mode_str_args[MODE_VALUE_INDEX]); + modes->mode_str_args[MODE_VALUE_INDEX] = strdup(argv[argpos++]); + } else if(MODE_VALUE == CHANNEL_MODE_VALUE_INTEGER) + modes->mode_int_args[MODE_VALUE_INDEX] = atoi(argv[argpos++]); + else + argpos++; //we simply don't know what to do with the argument... + } + modes->modes |= modeOpt[0]; + modes->allmodes |= modeOpt[0]; + } else { + modes->modes &= ~modeOpt[0]; + modes->allmodes |= modeOpt[0]; + if(MODE_TYPE == CHANNEL_MODE_TYPE_B) { + if(argpos == argc) continue; + if(MODE_VALUE == CHANNEL_MODE_VALUE_STRING) { + free(modes->mode_str_args[MODE_VALUE_INDEX]); + modes->mode_str_args[MODE_VALUE_INDEX] = NULL; + } else if(MODE_VALUE == CHANNEL_MODE_VALUE_INTEGER) + modes->mode_int_args[MODE_VALUE_INDEX] = 0; + argpos++; //we don't need the argument when unsetting a mode... + } + } + } + #undef MODE_TYPE + #undef MODE_VALUE + #undef MODE_VALUE_INDEX +} + +void parseModeString(struct ModeNode* modes, char *modeStr) { + int argc = 0; + char *args[modes_count+1]; + char *a, *b = modeStr; + do { + a = strstr(b, " "); + if(a) *a = '\0'; + args[argc++] = b; + if(a) b = a+1; + } while(a); + parseModes(modes, args[0], args+1, argc-1); +} + +int parseMode(struct ModeNode* modes, int add, char mode, char *param) { + #define MODE_TYPE (modeOpt[2] & CHANNEL_MODE_TYPE) + #define MODE_VALUE (modeOpt[2] & CHANNEL_MODE_VALUE) + #define MODE_VALUE_INDEX (modeOpt[2] & CHANNEL_MODE_VALUE_INDEX_MASK) >> CHANNEL_MODE_VALUE_INDEX_SHIFT + unsigned int *modeOpt = getModeOptions(mode); + if(!modeOpt) return 0; + if(MODE_TYPE == CHANNEL_MODE_TYPE_A) { + if(!param) return 0; + //special mode ;) + switch(mode) { + case 'o': + parseModesUserPriv(modes, CHANUSERFLAG_OPPED, add, param); + break; + case 'v': + parseModesUserPriv(modes, CHANUSERFLAG_VOICED, add, param); + break; + case 'b': + parseModesBan(modes, add, param); + break; + default: + return 0; //we have an unknown TYPE_A mode??? + } + } + if(add) { + if(MODE_TYPE != CHANNEL_MODE_TYPE_D) { //all other types take parameters when set + if(!param) return 0; + if(MODE_VALUE == CHANNEL_MODE_VALUE_STRING) { + if(modes->mode_str_args[MODE_VALUE_INDEX]) + free(modes->mode_str_args[MODE_VALUE_INDEX]); + modes->mode_str_args[MODE_VALUE_INDEX] = strdup(param); + } else if(MODE_VALUE == CHANNEL_MODE_VALUE_INTEGER) + modes->mode_int_args[MODE_VALUE_INDEX] = atoi(param); + } + modes->modes |= modeOpt[0]; + modes->allmodes |= modeOpt[0]; + } else { + modes->modes &= ~modeOpt[0]; + modes->allmodes |= modeOpt[0]; + if(MODE_TYPE == CHANNEL_MODE_TYPE_B) { + if(!param && !(modeOpt[2] & CHANNEL_MODE_KEY)) return 0; + if(MODE_VALUE == CHANNEL_MODE_VALUE_STRING) { + free(modes->mode_str_args[MODE_VALUE_INDEX]); + modes->mode_str_args[MODE_VALUE_INDEX] = NULL; + } else if(MODE_VALUE == CHANNEL_MODE_VALUE_INTEGER) + modes->mode_int_args[MODE_VALUE_INDEX] = 0; + } + } + #undef MODE_TYPE + #undef MODE_VALUE + #undef MODE_VALUE_INDEX + return 1; +} + +void getModeString(struct ModeNode* modes, char *modesStr) { + #define MODE_TYPE (mode[2] & CHANNEL_MODE_TYPE) + #define MODE_VALUE (mode[2] & CHANNEL_MODE_VALUE) + #define MODE_VALUE_INDEX (mode[2] & CHANNEL_MODE_VALUE_INDEX_MASK) >> CHANNEL_MODE_VALUE_INDEX_SHIFT + char paramStr[MAXLEN]; + modesStr[0] = '+'; + unsigned int *mode; + int modePos = 1; + int paramPos = 0; + for (mode = valid_modes; mode[1]; mode += 3) { + if(modes->modes & mode[0]) { + modesStr[modePos++] = (char) mode[1]; + if(MODE_TYPE != CHANNEL_MODE_TYPE_D) { + if(MODE_VALUE == CHANNEL_MODE_VALUE_STRING) + paramPos += sprintf(paramStr + paramPos, " %s", modes->mode_str_args[MODE_VALUE_INDEX]); + else if(MODE_VALUE == CHANNEL_MODE_VALUE_INTEGER) + paramPos += sprintf(paramStr + paramPos, " %d", modes->mode_int_args[MODE_VALUE_INDEX]); + } + } + } + paramStr[paramPos] = '\0'; + strcpy(modesStr + modePos, paramStr); + #undef MODE_TYPE + #undef MODE_VALUE + #undef MODE_VALUE_INDEX +} + +void getFullModeString(struct ModeNode* modes, char *modesStr) { + #define MODE_TYPE (mode[2] & CHANNEL_MODE_TYPE) + #define MODE_VALUE (mode[2] & CHANNEL_MODE_VALUE) + #define MODE_VALUE_INDEX (mode[2] & CHANNEL_MODE_VALUE_INDEX_MASK) >> CHANNEL_MODE_VALUE_INDEX_SHIFT + char addMode[modes_count+1]; + int addModePos = 0; + char addParams[MAXLEN]; + addParams[0] = '\0'; + int addParamsPos = 0; + char delMode[modes_count+1]; + int delModePos = 0; + unsigned int *mode; + for (mode = valid_modes; mode[1]; mode += 3) { + if(modes->allmodes & mode[0]) { + if(modes->modes & mode[0]) { + addMode[addModePos++] = (char) mode[1]; + if(MODE_TYPE != CHANNEL_MODE_TYPE_D) { + if(MODE_VALUE == CHANNEL_MODE_VALUE_STRING) + addParamsPos += sprintf(addParams + addParamsPos, " %s", modes->mode_str_args[MODE_VALUE_INDEX]); + else if(MODE_VALUE == CHANNEL_MODE_VALUE_INTEGER) + addParamsPos += sprintf(addParams + addParamsPos, " %d", modes->mode_int_args[MODE_VALUE_INDEX]); + } + } else { + delMode[delModePos++] = (char) mode[1]; + } + } + } + addMode[addModePos] = '\0'; + delMode[delModePos] = '\0'; + addParams[addParamsPos] = '\0'; + sprintf(modesStr, "%s%s%s%s%s", (addModePos ? "+" : ""), addMode, (delModePos ? "-" : ""), delMode, addParams); + if(*modesStr == '\0') { + sprintf(modesStr, "+"); + } + #undef MODE_TYPE + #undef MODE_VALUE + #undef MODE_VALUE_INDEX +} diff --git a/src/ModeNode.h b/src/ModeNode.h new file mode 100644 index 0000000..0b8ab56 --- /dev/null +++ b/src/ModeNode.h @@ -0,0 +1,61 @@ +/* ModeNode.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _ModeNode_h +#define _ModeNode_h +#include "main.h" + +struct ChanNode; + +//Types: http://www.irc.org/tech_docs/draft-brocklesby-irc-isupport-03.txt +#define CHANNEL_MODE_TYPE_A 0x01 /* ... special (addresses or users) ... */ +#define CHANNEL_MODE_TYPE_B 0x02 /* These modes always take a parameter. */ +#define CHANNEL_MODE_TYPE_C 0x03 /* These modes take a parameter only when set. */ +#define CHANNEL_MODE_TYPE_D 0x04 /* These modes never take a parameter. */ +#define CHANNEL_MODE_TYPE 0x07 /* bit mask to get the type */ + +#define CHANNEL_MODE_VALUE_STRING 0x10 +#define CHANNEL_MODE_VALUE_INTEGER 0x20 +#define CHANNEL_MODE_VALUE 0x30 /* bit mask to get the value */ + +#define CHANNEL_MODE_KEY 0x40 /* mode is a key - automatically add the current key as parameter on unset */ + +#define CHANNEL_MODE_VALUE_INDEX_SHIFT 8 +#define CHANNEL_MODE_VALUE_INDEX_MASK (0xff << CHANNEL_MODE_VALUE_INDEX_SHIFT) /* this "bitrange" is reserved for storing the array indexes of the mode values */ + +struct ModeNode { + struct ChanNode *chan; + unsigned int modes; + unsigned int allmodes; + char **mode_str_args; + int *mode_int_args; +}; + +#ifndef DND_FUNCTIONS +void init_ModeNode(); +/* MODULAR ACCESSIBLE */ struct ModeNode *createModeNode(struct ChanNode *chan); +/* MODULAR ACCESSIBLE */ void freeModeNode(struct ModeNode *modes); +/* MODULAR ACCESSIBLE */ int isModeSet(struct ModeNode* modes, char modeChar); +/* MODULAR ACCESSIBLE */ int isModeAffected(struct ModeNode* modes, char modeChar); +/* MODULAR ACCESSIBLE */ void* getModeValue(struct ModeNode* modes, char modeChar); +/* MODULAR ACCESSIBLE */ unsigned int getModeType(struct ModeNode* modes, char modeChar); +/* MODULAR ACCESSIBLE */ void parseModes(struct ModeNode* modes, char *modeStr, char **argv, int argc); +/* MODULAR ACCESSIBLE */ void parseModeString(struct ModeNode* modes, char *modeStr); +/* MODULAR ACCESSIBLE */ int parseMode(struct ModeNode* modes, int add, char mode, char *param); +/* MODULAR ACCESSIBLE */ void getModeString(struct ModeNode* modes, char *modesStr); +/* MODULAR ACCESSIBLE */ void getFullModeString(struct ModeNode* modes, char *modesStr); +#endif +#endif diff --git a/src/ModuleFunctions.c b/src/ModuleFunctions.c new file mode 100644 index 0000000..4e9a290 --- /dev/null +++ b/src/ModuleFunctions.c @@ -0,0 +1,90 @@ +/* ModuleFunctions.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#include "ModuleFunctions.h" +#include "modules.h" +#include "ChanNode.h" +#ifndef WIN32 +#include +#endif + +#define FUNCTION_GLOBAL_CMD_REGISTER_NEONBACKUP 0 +#define FUNCTION_GLOBAL_CMD_UNREGISTER_NEONBACKUP 1 +#define FUNCTION_NEONBACKUP_RECOVER_CHAN 2 + +#define TOTAL_FUNCTIONS 3 + +struct module_function { + struct ModuleInfo *module; + void *function; +}; + +static struct module_function module_functions[TOTAL_FUNCTIONS]; + +void init_modulefunctions() { + int i; + for(i = 0; i < TOTAL_FUNCTIONS; i++) { + module_functions[i].function = NULL; + } +} + +void scan_module(struct ModuleInfo *module) { + char *function_names[] = { + "global_cmd_register_neonbackup", /* FUNCTION_GLOBAL_CMD_REGISTER_NEONBACKUP */ + "global_cmd_unregister_neonbackup", /* FUNCTION_GLOBAL_CMD_UNREGISTER_NEONBACKUP */ + "neonbackup_recover_chan" /* FUNCTION_NEONBACKUP_RECOVER_CHAN */ + }; + int i; + for(i = 0; i < TOTAL_FUNCTIONS; i++) { + #ifndef WIN32 + void* func = dlsym(module->module, function_names[i]); + #else + FARPROC func = GetProcAddress(module->module, function_names[i]); + #endif + if(func) { + module_functions[i].module = module; + module_functions[i].function = func; + } + } +} + +void free_module_functions(struct ModuleInfo *module) { + int i; + for(i = 0; i < TOTAL_FUNCTIONS; i++) { + if(module_functions[i].module == module) { + module_functions[i].function = NULL; + } + } +} + +/* function handlers */ +void module_global_cmd_register_neonbackup(char *chan) { + if(!module_functions[FUNCTION_GLOBAL_CMD_REGISTER_NEONBACKUP].function) + return; + ((void (*)(char *)) module_functions[FUNCTION_GLOBAL_CMD_REGISTER_NEONBACKUP].function)(chan); +} + +void module_global_cmd_unregister_neonbackup(char *chan) { + if(!module_functions[FUNCTION_GLOBAL_CMD_UNREGISTER_NEONBACKUP].function) + return; + ((void (*)(char *)) module_functions[FUNCTION_GLOBAL_CMD_UNREGISTER_NEONBACKUP].function)(chan); +} + +void module_neonbackup_recover_chan(struct ChanNode *chan) { + if(!module_functions[FUNCTION_NEONBACKUP_RECOVER_CHAN].function) + return; + ((void (*)(struct ChanNode *)) module_functions[FUNCTION_NEONBACKUP_RECOVER_CHAN].function)(chan); +} diff --git a/src/ModuleFunctions.h b/src/ModuleFunctions.h new file mode 100644 index 0000000..9f26d0d --- /dev/null +++ b/src/ModuleFunctions.h @@ -0,0 +1,34 @@ +/* ModuleFunctions.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _ModuleFunctions_h +#define _ModuleFunctions_h +#include "overall.h" + +#ifndef DND_FUNCTIONS +struct ModuleInfo; +struct ChanNode; + +void init_modulefunctions(); +void scan_module(struct ModuleInfo *module); +void free_module_functions(struct ModuleInfo *module); + +/* MODULAR ACCESSIBLE */ void module_global_cmd_register_neonbackup(char *chan); +/* MODULAR ACCESSIBLE */ void module_global_cmd_unregister_neonbackup(char *chan); +/* MODULAR ACCESSIBLE */ void module_neonbackup_recover_chan(struct ChanNode *chan); + +#endif +#endif diff --git a/src/QServer.c b/src/QServer.c new file mode 100644 index 0000000..ef382c9 --- /dev/null +++ b/src/QServer.c @@ -0,0 +1,388 @@ +/* QServer.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "QServer.h" +#include "UserNode.h" +#include "ChanNode.h" +#include "ModeNode.h" +#include "ChanUser.h" +#include "ClientSocket.h" +#include "WHOHandler.h" +#include "ConfigParser.h" +#include "bots.h" +#include "IOHandler.h" +#include "tools.h" + +#ifdef WIN32 +typedef uint32_t socklen_t; +#endif + +#define QSERVER_TIMEOUT 30 +#define QSERVER_MAXCLIENTS 100 + +#define QSERVER_FLAG_AUTHED 0x01 +#define QSERVER_FLAG_IN_USE 0x02 + +struct QServerClient { + struct IODescriptor *iofd; + unsigned int flags; + int references; + struct QServerClient *next; +}; + +static struct IODescriptor *server_iofd = NULL; +struct QServerClient *qserver_clients = NULL; +static int qserver_clientcount = 0; + +static IOHANDLER_CALLBACK(qserver_callback); + +void qserver_init() { + if(get_int_field("QServer.enabled")) { + char *host = get_string_field("QServer.host"); + if(!host) + host = "0.0.0.0"; + int port = get_int_field("QServer.port"); + if(!port) + port = 7499; + server_iofd = iohandler_listen(host, port, qserver_callback); + } +} + +void qserver_free() { + if(!server_iofd) + return; + struct QServerClient *client, *next; + for (client = qserver_clients; client; client = next) { + next = client->next; + if(client->iofd) + iohandler_close(client->iofd); + free(client); + } + qserver_clients = NULL; + qserver_clientcount = 0; + iohandler_close(server_iofd); + server_iofd = NULL; +} + +static int qserver_write(struct QServerClient *client, char* msg, int len) { + if (!client || !client->iofd) return 0; + if(!len) + len = strlen(msg); + iohandler_send(client->iofd, msg, len); + return 1; +} + +static void qserver_put(struct QServerClient *client, const char *text, ...) { + va_list arg_list; + char sendBuf[MAXLEN]; + int pos; + if (!client || !client->iofd) return; + sendBuf[0] = '\0'; + va_start(arg_list, text); + pos = vsnprintf(sendBuf, MAXLEN - 2, text, arg_list); + va_end(arg_list); + if (pos < 0 || pos > (MAXLEN - 2)) pos = MAXLEN - 2; + sendBuf[pos] = '\n'; + sendBuf[pos+1] = '\0'; + qserver_write(client, sendBuf, pos+1); +} + +static void qserver_update_lastmsg(struct QServerClient *client) { + struct timeval timeout; + gettimeofday(&timeout, NULL); + timeout.tv_sec += QSERVER_TIMEOUT; + iohandler_set_timeout(client->iofd, &timeout); +} + +static void qserver_parse_A(struct QServerClient *client, char **argv, int argc) { + if(client->flags & QSERVER_FLAG_AUTHED) { + qserver_put(client, "E :Already Authed"); + return; + } + if(!argv) { + qserver_put(client, "E :Missing Parameter"); + return; + } + if(strcmp(argv[0], get_string_field("QServer.pass"))) { + qserver_put(client, "E :Wrong Password"); + return; + } + client->flags |= QSERVER_FLAG_AUTHED; + qserver_update_lastmsg(client); + qserver_put(client, "A :Logged in"); +} + +#define QSERVER_COMMAND_HEADER {\ + if(!(client->flags & QSERVER_FLAG_AUTHED)) {\ + qserver_put(client, "E :Not Authed");\ + return;\ + }\ + qserver_update_lastmsg(client);\ +} + +static void qserver_parse_U(struct QServerClient *client, char **argv, int argc); +static void qserver_parse_C(struct QServerClient *client, char **argv, int argc); +static void qserver_parse_AC(struct QServerClient *client, char **argv, int argc); +static void qserver_parse_ACU(struct QServerClient *client, char **argv, int argc); +static void qserver_parse_R(struct QServerClient *client, char **argv, int argc); + +static void qserver_parse(struct QServerClient *client, char *line) { + int argc = 0; + char *argv[MAXNUMPARAMS]; + while(*line) { + //skip leading spaces + while (*line == ' ') + *line++ = 0; + if (*line == ':') { + //the rest is a single parameter + argv[argc++] = line + 1; + break; + } + argv[argc++] = line; + if (argc >= MAXNUMPARAMS) + break; + while (*line != ' ' && *line) + line++; + } + if(!stricmp(argv[0], "A")) //AUTH + qserver_parse_A(client, argv+1, argc-1); + else if(!stricmp(argv[0], "U")) //get User + qserver_parse_U(client, argv+1, argc-1); + else if(!stricmp(argv[0], "C")) //get Channel + qserver_parse_C(client, argv+1, argc-1); + else if(!stricmp(argv[0], "AC")) //get All Channels + qserver_parse_AC(client, argv+1, argc-1); + else if(!stricmp(argv[0], "ACU")) //get All ChannelUsers + qserver_parse_ACU(client, argv+1, argc-1); + else if(!stricmp(argv[0], "R")) //RAW + qserver_parse_R(client, argv+1, argc-1); + else + qserver_put(client, "E :Unknown Command"); +} + +static void qserver_accept(int sockfd) { + struct IODescriptor *client_iofd = iohandler_add(sockfd, IOTYPE_CLIENT, NULL, qserver_callback); + client_iofd->state = IO_CONNECTED; + iohandler_update(client_iofd); + if(qserver_clientcount >= QSERVER_MAXCLIENTS) { + iohandler_printf(client_iofd, "E :Maximum QServer Connections reached"); + iohandler_close(client_iofd); + return; + } + struct QServerClient *client = malloc(sizeof(*client)); + client->iofd = client_iofd; + client->references = 0; + client->next = qserver_clients; + qserver_clients = client; + qserver_clientcount++; +} + +static void qserver_cleanup() { + struct QServerClient *client, *next, *prev = NULL; + for (client = qserver_clients; client; client = next) { + next = client->next; + if(client->iofd == NULL && !(client->flags & QSERVER_FLAG_IN_USE)) { + if(prev) + prev->next = client->next; + else + qserver_clients = client->next; + qserver_clientcount--; + free(client); + } + } +} + +static IOHANDLER_CALLBACK(qserver_callback) { + struct QServerClient *client = event->iofd->data; + switch(event->type) { + case IOEVENT_TIMEOUT: + qserver_put(client, "E :Timeout"); + client->iofd = NULL; + break; + case IOEVENT_RECV: + qserver_parse(client, event->data.recv_str); + break; + case IOEVENT_CLOSED: + iohandler_close(client->iofd); + client->iofd = NULL; + break; + case IOEVENT_ACCEPT: + qserver_accept(event->data.accept_fd); + break; + default: + break; + } + qserver_cleanup(); +} + +/* +* Command functions +*/ + +static USERAUTH_CALLBACK(qserver_parse_U_async); +static void qserver_parse_U(struct QServerClient *client, char **argv, int argc) { + QSERVER_COMMAND_HEADER; + if(!argv) { + qserver_put(client, "E :Missing Parameter"); + return; + } + struct UserNode *cuser = getUserByNick(argv[0]); + if(!cuser) { + cuser = createTempUser(argv[0]); + if(!cuser) { + qserver_put(client, "U 0 :Unknown User"); + return; + } + cuser->flags |= USERFLAG_ISTMPUSER; + } + client->references++; + client->flags |= QSERVER_FLAG_IN_USE; + get_userauth(cuser, 0, qserver_parse_U_async, client); +} + +static USERAUTH_CALLBACK(qserver_parse_U_async) { + struct QServerClient *qclient = data; + qclient->references--; + if(!qclient->references) + qclient->flags &= ~QSERVER_FLAG_IN_USE; + if(!user) { + qserver_put(qclient, "U 0 :Unknown User"); + return; + } + qserver_put(qclient, "U 1 %s %s %s %s :%s", user->nick, user->ident, user->host, ((user->flags & USERFLAG_ISAUTHED) ? user->auth : "0"), user->realname); +} + +static void qserver_parse_C(struct QServerClient *client, char **argv, int argc) { + QSERVER_COMMAND_HEADER; + if(!argv) { + qserver_put(client, "E :Missing Parameter"); + return; + } + struct ChanNode *chan = getChanByName(argv[0]); + if(!chan) { + qserver_put(client, "C 0 :Unknown Channel"); + return; + } + char tmpStr[MAXLEN]; + getModeString(chan->modes, tmpStr); + qserver_put(client, "C 1 %s %d %s :%s", chan->name, chan->usercount, tmpStr, chan->topic); +} + +static void qserver_parse_AC(struct QServerClient *client, char **argv, int argc) { + QSERVER_COMMAND_HEADER; + struct ChanNode *chan; + char tmpStr[MAXLEN]; + for(chan = getAllChans(NULL); chan; chan = getAllChans(chan)) { + getModeString(chan->modes, tmpStr); + qserver_put(client, "AC %s %d %s :%s", chan->name, chan->usercount, tmpStr, chan->topic); + } + qserver_put(client, "ACE"); //AllChannelsEnd +} + +static USERLIST_CALLBACK(qserver_parse_ACU_async); +static void qserver_parse_ACU(struct QServerClient *client, char **argv, int argc) { + QSERVER_COMMAND_HEADER; + if(!argv) { + qserver_put(client, "E :Missing Parameter"); + return; + } + struct ChanNode *chan = getChanByName(argv[0]); + if(!chan) { + qserver_put(client, "ACUE 0 :Unknown Channel"); + return; + } + if(argc > 1 && !stricmp(argv[1], "1")) { + client->references++; + client->flags |= QSERVER_FLAG_IN_USE; + get_userlist_if_invisible(chan, 0, qserver_parse_ACU_async, client); + return; + } + char tmpStr[6]; + int tmpStrPos; + struct ChanUser *chanuser; + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + tmpStrPos = 0; + if(chanuser->flags & CHANUSERFLAG_OPPED) + tmpStr[tmpStrPos++] = '@'; + if(chanuser->flags & CHANUSERFLAG_HALFOPPED) + tmpStr[tmpStrPos++] = '%'; + if(chanuser->flags & CHANUSERFLAG_VOICED) + tmpStr[tmpStrPos++] = '+'; + if(chanuser->flags & CHANUSERFLAG_INVISIBLE) + tmpStr[tmpStrPos++] = '<'; + tmpStr[tmpStrPos] = '\0'; + qserver_put(client, "ACU %s %s %s", chanuser->user->nick, ((chanuser->user->flags & USERFLAG_ISAUTHED) ? chanuser->user->auth : "0"), tmpStr); + } + qserver_put(client, "ACUE 1"); +} + +static USERLIST_CALLBACK(qserver_parse_ACU_async) { + struct QServerClient *qclient = data; + qclient->references--; + if(!qclient->references) + qclient->flags &= ~QSERVER_FLAG_IN_USE; + char tmpStr[6]; + int tmpStrPos; + struct ChanUser *chanuser; + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + tmpStrPos = 0; + if(chanuser->flags & CHANUSERFLAG_OPPED) + tmpStr[tmpStrPos++] = '@'; + if(chanuser->flags & CHANUSERFLAG_HALFOPPED) + tmpStr[tmpStrPos++] = '%'; + if(chanuser->flags & CHANUSERFLAG_VOICED) + tmpStr[tmpStrPos++] = '+'; + if(chanuser->flags & CHANUSERFLAG_INVISIBLE) + tmpStr[tmpStrPos++] = '<'; + tmpStr[tmpStrPos] = '\0'; + qserver_put(qclient, "ACU %s %s %s", chanuser->user->nick, ((chanuser->user->flags & USERFLAG_ISAUTHED) ? chanuser->user->auth : "0"), tmpStr); + } + qserver_put(qclient, "ACUE 1"); +} + +static void qserver_parse_R(struct QServerClient *client, char **argv, int argc) { + QSERVER_COMMAND_HEADER; + if(argc < 3) { + qserver_put(client, "E :Missing Parameter"); + return; + } + struct ClientSocket *bot; + if(!strcmp(argv[0], "1")) { + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(!stricmp(bot->user->nick, argv[1])) break; + } + } else { + struct ClientSocket *low_bot; + int botid = resolve_botalias(argv[1]); + if(botid == -1) + botid = atoi(argv[1]); + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(bot->botid == botid) { + if(bot->flags & SOCKET_FLAG_PREFERRED) break; + low_bot = bot; + } + } + if(!bot) + bot = low_bot; + } + if(!bot) { + qserver_put(client, "R 0 :Bot not found"); + return; + } + putsock(bot, "%s", argv[2]); + qserver_put(client, "R 1"); +} diff --git a/src/QServer.h b/src/QServer.h new file mode 100644 index 0000000..01289ba --- /dev/null +++ b/src/QServer.h @@ -0,0 +1,25 @@ +/* QServer.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _QServer_h +#define _QServer_h + +#include "main.h" + +void qserver_init(); +void qserver_free(); + +#endif \ No newline at end of file diff --git a/src/UserNode.c b/src/UserNode.c new file mode 100644 index 0000000..cd8735e --- /dev/null +++ b/src/UserNode.c @@ -0,0 +1,559 @@ +/* UserNode.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#include "UserNode.h" +#include "ChanNode.h" +#include "ChanUser.h" +#include "tools.h" +#include "IRCEvents.h" +#include "IPNode.h" +#include "log.h" + +static struct UserNode **userList; + +unsigned int valid_user_modes[] = { + 1, 'o', + 2, 'O', + 3, 'i', + 4, 'w', + 5, 's', + 6, 'd', + 7, 'k', + 8, 'g', + 9, 'n', + 10, 'I', + 11, 'X', + 12, 'S', + 13, 'H', + 14, 'c', + 15, 'W', + 16, 't', + 17, 'D', + 18, 'x', +// ^ maximum is 32!!! + 0x00, 0x00 +}; + +void init_UserNode() { + unsigned int *mode, flag = 1; + userList = calloc(VALID_NICK_CHARS_FIRST_LEN+1, sizeof(*userList)); + for (mode = valid_user_modes; mode[1]; mode += 2) { + mode[0] = flag; + flag = flag << 1; + } +} + +void free_UserNode() { + //kamikaze free all users + //chanusers will be destroyed in free_ChanNode() + int i; + struct UserNode *user, *next; + for(i = 0; i < VALID_NICK_CHARS_FIRST_LEN+1; i++) { + for(user = userList[i]; user; user = next) { + next = user->next; + free(user); + } + } + free(userList); +} + +int is_valid_nick(const char *nick) { + unsigned int i; + //first char must be one of: a-zA-Z{|}~[\]^_` + if (!strchr(VALID_NICK_CHARS_FIRST, *nick)) + return 0; + //all other chars must be one of: a-zA-Z0-9{|}~[\]^_` + for (i = 0; nick[i]; ++i) + if (!strchr(VALID_NICK_CHARS, nick[i])) + return 0; + if (strlen(nick) > NICKLEN) + return 0; + return 1; +} + +static int get_nicklist_entry(int nick) { + int i; + char *valid_chars = VALID_NICK_CHARS_FIRST; + for(i = 0; i < VALID_NICK_CHARS_FIRST_LEN; i++) { + if(valid_chars[i] == nick) + return i; + } + return -1; //ERROR! +} + +static unsigned int* getUserModeOptions(char mode) { + unsigned int *cmode; + for (cmode = valid_user_modes; cmode[1]; cmode += 2) { + if(cmode[1] == mode) + return cmode; + } + return NULL; +} + +int isUserModeSet(struct UserNode *user, char modeChar) { + unsigned int *modeOpt = getUserModeOptions(modeChar); + return (user->usermode & modeOpt[0]); +} + +void parseUserModes(struct UserNode* user, char *modeStr) { + int i, add = 1; + unsigned int *modeOpt; + for(i = 0; i < strlen(modeStr); i++) { + if(modeStr[i] == '+') { + add = 1; + continue; + } + if(modeStr[i] == '-') { + add = 0; + continue; + } + modeOpt = getUserModeOptions(modeStr[i]); + if(!modeOpt) continue; // unknown mode? + if(add) { + user->usermode |= modeOpt[0]; + } else { + user->usermode &= ~modeOpt[0]; + } + } +} + + +struct UserNode* getUserByNick(const char *nick) { //case sensitive + int userListIndex = get_nicklist_entry(*nick); + if(userListIndex == -1 || userList[userListIndex] == NULL) + return NULL; + SYNCHRONIZE(cache_sync); + struct UserNode *user; + for(user = userList[userListIndex]; user; user = user->next) { + if(!stricmp(nick, user->nick)) { + DESYNCHRONIZE(cache_sync); + return user; + } + } + DESYNCHRONIZE(cache_sync); + return NULL; +} + +struct UserNode* getUserByMask(const char *mask) { //case sensitive + SYNCHRONIZE(cache_sync); + char cmask[strlen(mask)+1]; + strcpy(cmask, mask); + int i; + struct UserNode *user = NULL; + for(i = 0; i < strlen(mask); i++) { + if(cmask[i] == '!') { + cmask[i] = 0; + user = getUserByNick(&cmask[0]); + DESYNCHRONIZE(cache_sync); + return user; + } else if(cmask[i] == '.') { + //it's a server + DESYNCHRONIZE(cache_sync); + return NULL; + } + } + DESYNCHRONIZE(cache_sync); + return NULL; +} + +struct UserNode* searchUserByNick(const char *nick) { //case insensitive + if(!isalpha(*nick)) + return getUserByNick(nick); + + SYNCHRONIZE(cache_sync); + int userListIndex; + struct UserNode *user; + + //search in the lower case "section" + userListIndex = get_nicklist_entry(tolower(*nick)); + if(userListIndex != -1 && userList[userListIndex] != NULL) { + for(user = userList[userListIndex]; user; user = user->next) { + if(!stricmp(nick, user->nick)) { + DESYNCHRONIZE(cache_sync); + return user; + } + } + } + //search in the upper case "section" + userListIndex = get_nicklist_entry(toupper(*nick)); + if(userListIndex != -1 && userList[userListIndex] != NULL) { + for(user = userList[userListIndex]; user; user = user->next) { + if(!stricmp(nick, user->nick)) { + DESYNCHRONIZE(cache_sync); + return user; + } + } + } + DESYNCHRONIZE(cache_sync); + return NULL; +} + +int countUsersWithHost(char *host) { + SYNCHRONIZE(cache_sync); + int i, count = 0; + struct UserNode *user; + for(i = 0; i < VALID_NICK_CHARS_FIRST_LEN; i++) { + for(user = userList[i]; user; user = user->next) { + if(!strcmp(user->host, host)) { + count++; + } + } + } + DESYNCHRONIZE(cache_sync); + return count; +} + +char *getAuthFakehost(char *auth) { + SYNCHRONIZE(cache_sync); + int i; + struct UserNode *user; + for(i = 0; i < VALID_NICK_CHARS_FIRST_LEN; i++) { + for(user = userList[i]; user; user = user->next) { + if((user->flags & USERFLAG_ISAUTHED) && !strcmp(user->auth, auth) && isFakeHost(user->host)) { + DESYNCHRONIZE(cache_sync); + return user->host; + } + } + } + DESYNCHRONIZE(cache_sync); + return NULL; +} + +struct UserNode* getAllUsers(struct UserNode *last) { + SYNCHRONIZE(cache_sync); + if(last == NULL || last->next == NULL) { + int cindex; + if(last == NULL) + cindex = 0; + else + cindex = get_nicklist_entry(last->nick[0]) + 1; + while(userList[cindex] == NULL && cindex < VALID_NICK_CHARS_FIRST_LEN) + cindex++; + DESYNCHRONIZE(cache_sync); + if(cindex >= VALID_NICK_CHARS_FIRST_LEN) return NULL; + return userList[cindex]; + } else { + DESYNCHRONIZE(cache_sync); + return last->next; + } +} + +struct UserNode* getUsersWithAuth(const char *auth, struct UserNode *last) { + SYNCHRONIZE(cache_sync); + int cindex = (last ? get_nicklist_entry(last->nick[0]) : 0); + struct UserNode *cuser = last; + while(cindex <= VALID_NICK_CHARS_FIRST_LEN) { + for(cuser = (cuser ? cuser->next : userList[cindex]); cuser; cuser = cuser->next) { + if((cuser->flags & USERFLAG_ISAUTHED) && !strcmp(cuser->auth, auth)) { + DESYNCHRONIZE(cache_sync); + return cuser; + } + } + cindex++; + cuser = NULL; + } + DESYNCHRONIZE(cache_sync); + return NULL; +} + +int getUserCount() { + SYNCHRONIZE(cache_sync); + int i, count = 0; + struct UserNode *user; + for(i = 0; i < VALID_NICK_CHARS_FIRST_LEN; i++) { + for(user = userList[i]; user; user = user->next) { + count++; + } + } + DESYNCHRONIZE(cache_sync); + return count; +} + +struct UserNode* addUser(const char *nick) { + int userListIndex = get_nicklist_entry(*nick); + if(userListIndex == -1 || !is_valid_nick(nick)) + return NULL; + struct UserNode *user = malloc(sizeof(*user)); + if (!user) + { + printf_log("main", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return NULL; + } + strcpy(user->nick, nick); + user->created = time(0); + user->ident[0] = 0; + user->host[0] = 0; + user->ip = NULL; + user->realname[0] = 0; + user->flags = 0; + user->channel = NULL; + user->last_who = 0; + user->usermode = 0; + SYNCHRONIZE(cache_sync); + user->next = userList[userListIndex]; + userList[userListIndex] = user; + DESYNCHRONIZE(cache_sync); + return user; +} + +struct UserNode* addUserMask(const char *mask) { + char cmask[strlen(mask)+1]; + strcpy(cmask, mask); + int i, ii = 0; + struct UserNode *user = NULL; + for(i = 0; i < strlen(mask)+1; i++) { + if(cmask[i] == '!') { + cmask[i] = 0; + user = addUser(cmask); + if(user == NULL) return NULL; + ii = i+1; + } else if(cmask[i] == '.' && !user) { + //it's a server + return NULL; + } else if(cmask[i] == '@') { + if(user == NULL) return NULL; + cmask[i] = 0; + strcpy(user->ident, &cmask[ii]); + ii = i+1; + } else if(cmask[i] == '\0') { + if(user == NULL) return NULL; + strcpy(user->host, &cmask[ii]); + } + } + return user; +} + +struct UserNode* createTempUser(const char *nick) { + int already_on_list = 0; + struct UserNode *user = NULL; + if(!is_valid_nick(nick)) { + return NULL; + } + for(user = userList[TEMPUSER_LIST_INDEX]; user; user = user->next) { + if(!stricmp(user->nick, nick)) { + already_on_list = 1; + break; + } + } + if(!user) { + user = malloc(sizeof(*user)); + if (!user) { + printf_log("main", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return NULL; + } + user->ident[0] = 0; + user->host[0] = 0; + user->ip = NULL; + user->realname[0] = 0; + user->flags = 0; + user->channel = NULL; + user->usermode = 0; + user->last_who = 0; + } else + user->flags &= ~USERFLAG_FREETMPUSER; + user->created = time(0); + if(user->created - user->last_who > REWHO_TIMEOUT) + user->flags &= ~USERFLAG_ISAUTHED; //remove authed flag (security reasons) + strcpy(user->nick, nick); + if(!already_on_list) { + SYNCHRONIZE(cache_sync); + user->next = userList[TEMPUSER_LIST_INDEX]; + userList[TEMPUSER_LIST_INDEX] = user; + DESYNCHRONIZE(cache_sync); + } + return user; +} + +struct UserNode* createTempUserMask(const char *mask) { + //note: it could also be a server we have to create a temponary user for... + char cmask[strlen(mask)+1]; + strcpy(cmask, mask); + int i, ii = 0; + int already_on_list = 0; + struct UserNode *user = NULL; + for(i = 0; i < strlen(mask)+1; i++) { + if(cmask[i] == '!') { + cmask[i] = 0; + if(!is_valid_nick(cmask)) { + return NULL; + } + for(user = userList[TEMPUSER_LIST_INDEX]; user; user = user->next) { + if(!stricmp(user->nick, cmask)) { + already_on_list = 1; + break; + } + } + if(!user) { + user = malloc(sizeof(*user)); + if (!user) { + printf_log("main", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return NULL; + } + user->ident[0] = 0; + user->host[0] = 0; + user->ip = NULL; + user->realname[0] = 0; + user->flags = 0; + user->channel = NULL; + user->usermode = 0; + user->last_who = 0; + } else + user->flags &= ~USERFLAG_FREETMPUSER; + user->created = time(0); + if(user->created - user->last_who > REWHO_TIMEOUT) + user->flags &= ~USERFLAG_ISAUTHED; //remove authed flag (security reasons) + strcpy(user->nick, cmask); + ii = i+1; + } else if(cmask[i] == '.' && !user) { + //it's a server + user = malloc(sizeof(*user)); + if (!user) + { + printf_log("main", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return NULL; + } + strcpy(user->host, cmask); + strncpy(user->nick, cmask, NICKLEN); + user->nick[NICKLEN] = 0; + user->created = time(0); + user->ident[0] = 0; + user->host[0] = 0; + user->ip = NULL; + user->realname[0] = 0; + user->flags = USERFLAG_ISSERVER; + user->channel = NULL; + user->usermode = 0; + user->last_who = 0; + break; + } else if(cmask[i] == '@') { + if(user == NULL) return NULL; + cmask[i] = 0; + strcpy(user->ident, &cmask[ii]); + ii = i+1; + } else if(cmask[i] == '\0') { + if(user == NULL) { + //nick only + user = malloc(sizeof(*user)); + if (!user) + { + printf_log("main", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return NULL; + } + strcpy(user->nick, cmask); + user->created = time(0); + user->ident[0] = 0; + user->host[0] = 0; + user->ip = NULL; + user->realname[0] = 0; + user->flags = 0; + user->channel = NULL; + user->usermode = 0; + user->last_who = 0; + break; + } + strcpy(user->host, &cmask[ii]); + } + } + if(!already_on_list) { + SYNCHRONIZE(cache_sync); + user->next = userList[TEMPUSER_LIST_INDEX]; + userList[TEMPUSER_LIST_INDEX] = user; + DESYNCHRONIZE(cache_sync); + } + return user; +} + +int renameUser(struct UserNode* user, const char *new_nick) { + if(!is_valid_nick(new_nick)) + return 0; + if(user->nick[0] == *new_nick) { + strcpy(user->nick, new_nick); + return 1; + } + //delUser(user, 0); //EPIC FAIL! This deletes the user from the channel Userlist -.- + //manually remove the user from the old userList + SYNCHRONIZE(cache_sync); + int userListIndex = get_nicklist_entry(user->nick[0]); + if(userListIndex != -1) { + struct UserNode *cuser, *last_user = NULL; + for(cuser = userList[userListIndex]; cuser; cuser = cuser->next) { + if(cuser == user) { + if(last_user) + last_user->next = user->next; + else + userList[userListIndex] = user->next; + break; + } else + last_user = cuser; + } + } + userListIndex = get_nicklist_entry(*new_nick); + strcpy(user->nick, new_nick); + user->next = userList[userListIndex]; + userList[userListIndex] = user; + DESYNCHRONIZE(cache_sync); + return 1; +} + +void delUser(struct UserNode* user, int freeUser) { + int userListIndex = ((user->flags & USERFLAG_ISTMPUSER) ? TEMPUSER_LIST_INDEX : get_nicklist_entry(user->nick[0])); + if(userListIndex == -1) return; + SYNCHRONIZE(cache_sync); + event_freeuser(user); + struct UserNode *cuser, *last_user = NULL; + for(cuser = userList[userListIndex]; cuser; cuser = cuser->next) { + if(cuser == user) { + if(last_user) + last_user->next = user->next; + else + userList[userListIndex] = user->next; + break; + } else + last_user = cuser; + } + if(freeUser && (user->flags & USERFLAG_IS_ON_WHO_QUEUE)) { + user->flags |= USERFLAG_FREE_AFTER_WHO; + freeUser = 0; + } + if(user->channel) { + struct ChanUser *chanUser, *next; + for(chanUser = user->channel; chanUser; chanUser = next) { + next = chanUser->next_chan; + removeChanUserFromLists(chanUser, 1, 0, freeUser); + } + } + if(freeUser) { + if(user->ip) + freeIPNode(user->ip); + free(user); + } else + user->next = NULL; + DESYNCHRONIZE(cache_sync); +} + +void clearTempUsers() { + SYNCHRONIZE(cache_sync); + int userListIndex = TEMPUSER_LIST_INDEX; + struct UserNode *cuser, *next; + time_t now = time(0); + for(cuser = userList[userListIndex]; cuser; cuser = next) { + next = cuser->next; + if(cuser->flags & USERFLAG_FREETMPUSER || now - cuser->created >= 300) { + delUser(cuser, 1); + } + } + DESYNCHRONIZE(cache_sync); +} diff --git a/src/UserNode.h b/src/UserNode.h new file mode 100644 index 0000000..ca6b70a --- /dev/null +++ b/src/UserNode.h @@ -0,0 +1,85 @@ +/* UserNode.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _UserNode_h +#define _UserNode_h +#include "main.h" + +#define USERFLAG_ISBOT 0x0001 +#define USERFLAG_ISAUTHED 0x0002 +#define USERFLAG_ISIRCOP 0x0004 +#define USERFLAG_ISTMPUSER 0x0008 +#define USERFLAG_ISSERVER 0x0010 +#define USERFLAG_FREETMPUSER 0x0020 +#define USERFLAG_LOADED_SETTINGS 0x0040 +#define USERFLAG_REPLY_PRIVMSG 0x0080 +#define USERFLAG_GOD_MODE 0x0100 +#define USERFLAG_HAS_USERID 0x0200 +#define USERFLAG_IS_ON_WHO_QUEUE 0x0400 /* prevents the user struct from beeing freed too early */ +#define USERFLAG_FREE_AFTER_WHO 0x0800 /* user struct is no more referenced - free it after WHO */ +#define USERFLAG_QUITTING 0x1000 /* user is currently quitting... */ + +#define USERFLAG_WAS_REGISTERING 0x20000000 /* only set for event_join if the quit reason was Registered */ +#define USERFLAG_SCRIPTFLAG1 0x40000000 +#define USERFLAG_SCRIPTFLAG2 0x80000000 + +struct ChanUser; +struct language; +struct IPNode; + +struct UserNode { + char nick[NICKLEN+1]; + char ident[USERLEN+1]; + char host[HOSTLEN+1]; + char realname[REALLEN+1]; + char auth[AUTHLEN+1]; + struct IPNode *ip; + unsigned int flags, usermode; + time_t created, last_who; + struct ChanUser *channel; + struct language *language; + + int user_id; + + struct UserNode *next; +}; + +#define isNetworkService(USER) (USER->flags & (USERFLAG_ISBOT | USERFLAG_ISIRCOP | USERFLAG_ISSERVER)) +#define isBot(USER) (USER->flags & (USERFLAG_ISBOT)) + +#ifndef DND_FUNCTIONS +void init_UserNode(); +void free_UserNode(); +/* MODULAR ACCESSIBLE */ int isUserModeSet(struct UserNode *user, char modeChar); +void parseUserModes(struct UserNode* user, char *modeStr); +/* MODULAR ACCESSIBLE */ int is_valid_nick(const char *nick); +/* MODULAR ACCESSIBLE */ struct UserNode* getUserByNick(const char *nick); +/* MODULAR ACCESSIBLE */ struct UserNode* getUserByMask(const char *mask); +/* MODULAR ACCESSIBLE */ int countUsersWithHost(char *host); +/* MODULAR ACCESSIBLE */ char *getAuthFakehost(char *auth); +/* MODULAR ACCESSIBLE */ struct UserNode* searchUserByNick(const char *nick); +/* MODULAR ACCESSIBLE */ struct UserNode* getAllUsers(struct UserNode *last); +/* MODULAR ACCESSIBLE */ struct UserNode* getUsersWithAuth(const char *auth, struct UserNode *last); +/* MODULAR ACCESSIBLE */ int getUserCount(); +struct UserNode* addUser(const char *nick); +struct UserNode* addUserMask(const char *mask); +/* MODULAR ACCESSIBLE */ struct UserNode* createTempUser(const char *nick); +struct UserNode* createTempUserMask(const char *mask); +int renameUser(struct UserNode* user, const char *new_nick); +void delUser(struct UserNode* user, int freeUser); +void clearTempUsers(); +#endif +#endif diff --git a/src/WHOHandler.c b/src/WHOHandler.c new file mode 100644 index 0000000..5db3db6 --- /dev/null +++ b/src/WHOHandler.c @@ -0,0 +1,395 @@ +/* WHOHandler.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "WHOHandler.h" +#include "ChanNode.h" +#include "UserNode.h" +#include "ChanUser.h" +#include "ModeNode.h" +#include "ClientSocket.h" +#include "IPNode.h" +#include "modules.h" +#include "log.h" + +#define WHOQUEUETYPE_ISONQUEUE 0x01 +#define WHOQUEUETYPE_USERLIST 0x02 +#define WHOQUEUETYPE_USERAUTH 0x04 +#define WHOQUEUETYPE_CHECKTYPE 0x07 +#define WHOQUEUETYPE_FOUND 0x08 + +#define MAXCALLBACKS 10 + +struct WHOQueueEntry { + char type; + int whoid; + struct ChanNode *chan; + struct UserNode *user; + struct WHOQueueEntry *next; + void *callback[MAXCALLBACKS]; + int module_id[MAXCALLBACKS]; + void *data[MAXCALLBACKS]; +}; + +static int checkWHOID(struct ClientSocket *client, int whoid) { + struct WHOQueueEntry *entry; + for(entry = client->whoqueue_first; entry; entry = entry->next) { + if(entry->whoid == whoid) + return 1; + } + return 0; +} + +static struct WHOQueueEntry* addWHOQueueEntry(struct ClientSocket *client) { + SYNCHRONIZE(whohandler_sync); + int whoid = 0; + do { + whoid++; + } while(checkWHOID(client, whoid) && whoid < 1000); + if(whoid == 1000) { + DESYNCHRONIZE(whohandler_sync); + return NULL; + } + struct WHOQueueEntry *entry = malloc(sizeof(*entry)); + if (!entry) + { + printf_log("main", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + DESYNCHRONIZE(whohandler_sync); + return NULL; + } + entry->next = NULL; + entry->whoid = whoid; + if(client->whoqueue_last) { + client->whoqueue_last->next = entry; + } else { + client->whoqueue_first = entry; + } + client->whoqueue_last = entry; + DESYNCHRONIZE(whohandler_sync); + return entry; +} + +static struct WHOQueueEntry* getNextWHOQueueEntry(struct ClientSocket *client, int whoid, int freeEntry) { + if(!client->whoqueue_first) return NULL; + SYNCHRONIZE(whohandler_sync); + struct WHOQueueEntry *entry; + for(entry = client->whoqueue_first; entry; entry = entry->next) { + if(entry->whoid == whoid) + break; + } + if(!entry) { + DESYNCHRONIZE(whohandler_sync); + return NULL; + } + if(freeEntry) { + client->whoqueue_first = entry->next; + if(entry == client->whoqueue_last) { + client->whoqueue_last = NULL; + } + } + DESYNCHRONIZE(whohandler_sync); + return entry; +} + +void clear_whoqueue(struct ClientSocket *client) { + if(!client->whoqueue_first) return; + SYNCHRONIZE(whohandler_sync); + struct WHOQueueEntry *entry, *next; + for(entry = client->whoqueue_first; entry; entry = next) { + next = entry->next; + free(entry); + } + client->whoqueue_last = NULL; + client->whoqueue_first = NULL; + DESYNCHRONIZE(whohandler_sync); +} + +void get_userlist(struct ChanNode *chan, int module_id, userlist_callback_t callback, void *data) { + struct ClientSocket *bot; + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(isUserOnChan(bot->user, chan)) + break; + } + if(bot == NULL) return; + //check if we really need to who the channel + int do_who = (!(chan->flags & CHANFLAG_RECEIVED_USERLIST)); + if(!do_who) { + struct ChanUser *chanuser; + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + if(!(chanuser->user->flags & (USERFLAG_ISAUTHED | USERFLAG_ISIRCOP | USERFLAG_ISBOT)) && (time(0) - chanuser->user->last_who) > REWHO_TIMEOUT) { + do_who = 1; + break; + } + } + } + if(do_who) { + struct WHOQueueEntry* entry = addWHOQueueEntry(bot); + entry->type = WHOQUEUETYPE_ISONQUEUE | WHOQUEUETYPE_USERLIST; + entry->chan = chan; + entry->callback[0] = callback; + entry->module_id[0] = module_id; + int i; + for(i = 1; i < MAXCALLBACKS; i++) + entry->callback[i] = NULL; + entry->data[0] = data; + for(i = 1; i < MAXCALLBACKS; i++) + entry->data[i] = NULL; + putsock(bot, "WHO %s,%d %%tuihnaf,%d", chan->name, entry->whoid, entry->whoid); + } else + callback(bot, chan, data); +} + +void _get_userlist_with_invisible(struct ChanNode *chan, int module_id, userlist_callback_t callback, void *data, int force) { + struct ClientSocket *bot; + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(isUserOnChan(bot->user, chan)) + break; + } + if(bot == NULL) return; + //check if we really need to who the channel + //invisible users can only be present if chanmode +D or +d is set! + int do_who = (!(chan->flags & CHANFLAG_RECEIVED_USERLIST)) || (isModeSet(chan->modes, 'd') || isModeSet(chan->modes, 'D')); + if(!do_who && force) { + struct ChanUser *chanuser; + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + if(!(chanuser->user->flags & (USERFLAG_ISAUTHED | USERFLAG_ISIRCOP | USERFLAG_ISBOT)) && (time(0) - chanuser->user->last_who) > REWHO_TIMEOUT) { + do_who = 1; + break; + } + } + } + if(do_who) { + struct WHOQueueEntry* entry = addWHOQueueEntry(bot); + entry->type = WHOQUEUETYPE_ISONQUEUE | WHOQUEUETYPE_USERLIST; + entry->chan = chan; + entry->callback[0] = callback; + entry->module_id[0] = module_id; + int i; + for(i = 1; i < MAXCALLBACKS; i++) + entry->callback[i] = NULL; + entry->data[0] = data; + for(i = 1; i < MAXCALLBACKS; i++) + entry->data[i] = NULL; + putsock(bot, "WHO %s,%d d%%tuihnaf,%d", chan->name, entry->whoid, entry->whoid); + } else + callback(bot, chan, data); +} + +void get_userauth(struct UserNode *user, int module_id, userauth_callback_t callback, void *data) { + //check if we have already an active WHO for this user + struct ClientSocket *bot, *whobot = NULL; + struct WHOQueueEntry *entry; + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + for(entry = bot->whoqueue_first; entry; entry = entry->next) { + if((entry->type & WHOQUEUETYPE_USERAUTH) && entry->user == user) { + int i = 0; + for(i = 1; i < MAXCALLBACKS; i++) { + if(!entry->callback[i]) { + entry->callback[i] = callback; + entry->module_id[i] = module_id; + entry->data[i] = data; + return; + } + } + } + } + if(bot->flags & SOCKET_FLAG_PREFERRED) + whobot = bot; + } + bot = whobot; + if(bot == NULL) bot = getBots(SOCKET_FLAG_READY, NULL); + //check if we really need to who the user + if(!is_valid_nick(user->nick)) { + callback(bot, user->nick, NULL, data); + return; + } + if((user->flags & (USERFLAG_ISAUTHED | USERFLAG_ISSERVER)) || (time(0) - user->last_who) <= REWHO_TIMEOUT) { + callback(bot, user->nick, user, data); + return; + } + entry = addWHOQueueEntry(bot); + entry->type = WHOQUEUETYPE_ISONQUEUE | WHOQUEUETYPE_USERAUTH; + entry->user = user; + user->flags |= USERFLAG_IS_ON_WHO_QUEUE; + entry->callback[0] = callback; + entry->module_id[0] = module_id; + int i; + for(i = 1; i < MAXCALLBACKS; i++) + entry->callback[i] = NULL; + entry->data[0] = data; + for(i = 1; i < MAXCALLBACKS; i++) + entry->data[i] = NULL; + //WHO ".$user->getNick().",".$id." %tuhna,".$id + putsock(bot, "WHO %s,%d %%tuhna,%d", user->nick, entry->whoid, entry->whoid); +} + +static void _recv_whohandler_354(struct ClientSocket *client, char **argv, unsigned int argc); +void recv_whohandler_354(struct ClientSocket *client, char **argv, unsigned int argc) { + _recv_whohandler_354(client, argv, argc); +} + +static void _recv_whohandler_354(struct ClientSocket *client, char **argv, unsigned int argc) { + int i; + if(argc < 2) return; + int type = atoi(argv[1]); + if(!type) return; + struct WHOQueueEntry* entry = getNextWHOQueueEntry(client, type, 0); + if(entry == NULL) return; + #ifdef HAVE_THREADS + unsigned int tid = (unsigned int) pthread_self_tid(); + while(!clientsocket_parseorder_top(tid)) { + usleep(1000); //1ms + } + #endif + if(entry->type & WHOQUEUETYPE_USERLIST) { + if(argc < 7) return; + //:OGN2.OnlineGamesNet.net 354 skynet 1 pk910 2001:41d0:2:1d3b::babe skynet H@ pk910 + struct ChanNode *chan = entry->chan; + //add the user toe the channel if he isn't added, yet and update its user data + //parse flags + int userflags = 0; + int chanuserflags = 0; + for(i = 0; i < strlen(argv[6]); i++) { + switch (argv[6][i]) { + case '@': + chanuserflags |= CHANUSERFLAG_OPPED; + break; + case '%': + chanuserflags |= CHANUSERFLAG_HALFOPPED; + break; + case '+': + chanuserflags |= CHANUSERFLAG_VOICED; + break; + case '*': + userflags |= USERFLAG_ISIRCOP; + break; + case '<': + chanuserflags |= CHANUSERFLAG_INVISIBLE; + break; + default: + break; + } + } + + struct UserNode *user = getUserByNick(argv[5]); + struct ChanUser *chanuser; + if((chanuserflags & CHANUSERFLAG_INVISIBLE) && (!user || (user && !isBot(user)))) { + user = createTempUser(argv[5]); //always add a temponary user to prevent cache problems when the user joins right now (while it's stored in our cache as being invisible) + user->flags |= USERFLAG_ISTMPUSER; + chan->flags |= CHANFLAG_HAVE_INVISIBLES; + chanuser = addInvisibleChanUser(chan, user); + chanuser->flags = (chanuser->flags & ~CHANUSERFLAG_OPPED_OR_VOICED) | chanuserflags; + } else { + if(user == NULL) { + user = addUser(argv[5]); + } + if(!(chanuser = getChanUser(user, chan))) { + chanuser = addChanUser(chan, user); + } + chanuser->flags = (chanuser->flags & ~(CHANUSERFLAG_OPPED_OR_VOICED | CHANUSERFLAG_INVISIBLE)) | chanuserflags; + } + user->flags = (user->flags & ~USERFLAG_ISIRCOP) | userflags; + user->last_who = time(0); + if(!*user->ident) + strcpy(user->ident, argv[2]); + if(!user->ip) + user->ip = createIPNode(argv[3]); + if(!*user->host) + strcpy(user->host, argv[4]); + if(!(user->flags & USERFLAG_ISAUTHED) && strcmp(argv[7], "0")) { + strcpy(user->auth, argv[7]); + user->flags |= USERFLAG_ISAUTHED; + } + } else if((entry->type & WHOQUEUETYPE_USERAUTH) && !(entry->type & WHOQUEUETYPE_FOUND)) { + //:OGN2.OnlineGamesNet.net 354 Skynet 1 pk910 2001:41d0:2:1d3b::babe Skynet pk910 + entry->type |= WHOQUEUETYPE_FOUND; + entry->user->last_who = time(0); + if(strcmp(argv[5], "0") && !(entry->user->flags & USERFLAG_ISAUTHED)) { + strcpy(entry->user->auth, argv[5]); + entry->user->flags |= USERFLAG_ISAUTHED; + } + for(i = 0; i < MAXCALLBACKS; i++) { + userauth_callback_t *callback = entry->callback[i]; + if(!callback) break; + if(!entry->module_id[i] || module_loaded(entry->module_id[i])) + callback(client, entry->user->nick, entry->user, entry->data[i]); + } + } +} + +static void _recv_whohandler_315(struct ClientSocket *client, char **argv, unsigned int argc); +void recv_whohandler_315(struct ClientSocket *client, char **argv, unsigned int argc) { + _recv_whohandler_315(client, argv, argc); +} + +static void _recv_whohandler_315(struct ClientSocket *client, char **argv, unsigned int argc) { + if(argc < 2) return; + char *typestr = strstr(argv[1], ","); + if(!typestr) return; + typestr++; + int type = atoi(typestr); + struct WHOQueueEntry* entry = getNextWHOQueueEntry(client, type, 0); + if(entry == NULL) return; + #ifdef HAVE_THREADS + unsigned int tid = (unsigned int) pthread_self_tid(); + while(!clientsocket_parseorder_top(tid)) { + usleep(1000); //1ms + } + #endif + getNextWHOQueueEntry(client, type, 1); + if(entry->type & WHOQUEUETYPE_USERLIST) { + //:OGN2.OnlineGamesNet.net 315 skynet #pk910,1 :End of /WHO list. + entry->chan->flags |= CHANFLAG_RECEIVED_USERLIST; + userlist_callback_t *callback; + int i; + for(i = 0; i < MAXCALLBACKS; i++) { + callback = entry->callback[i]; + if(!callback) break; + if(!entry->module_id[i] || module_loaded(entry->module_id[i])) + callback(client, entry->chan, entry->data[i]); + } + if(entry->chan->flags & CHANFLAG_HAVE_INVISIBLES) { + //remove all invisible users again + struct ChanUser *chanuser, *next; + for(chanuser = getChannelUsers(entry->chan, NULL); chanuser; chanuser = next) { + next = getChannelUsers(entry->chan, chanuser); + if((chanuser->flags & CHANUSERFLAG_INVISIBLE) && !isBot(chanuser->user)) { + delChanUser(chanuser, 1); + } + } + } + } else if(entry->type & WHOQUEUETYPE_USERAUTH) { + if(!(entry->type & WHOQUEUETYPE_FOUND)) { + userauth_callback_t *callback; + int i; + for(i = 0; i < MAXCALLBACKS; i++) { + callback = entry->callback[i]; + if(!callback) break; + if(!entry->module_id[i] || module_loaded(entry->module_id[i])) + callback(client, entry->user->nick, NULL, entry->data[i]); + } + } + entry->user->flags &= ~USERFLAG_IS_ON_WHO_QUEUE; + if(entry->user->flags & USERFLAG_FREE_AFTER_WHO) { + delUser(entry->user, 1); + } + } + free(entry); +} + +void free_whoqueue() { + +} diff --git a/src/WHOHandler.h b/src/WHOHandler.h new file mode 100644 index 0000000..03e30d1 --- /dev/null +++ b/src/WHOHandler.h @@ -0,0 +1,44 @@ +/* WHOHandler.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _WHOHandler_h +#define _WHOHandler_h + +#include "main.h" + +struct ClientSocket; +struct ChanNode; +struct UserNode; + +#define USERLIST_CALLBACK(NAME) void NAME(UNUSED_ARG(struct ClientSocket *client), UNUSED_ARG(struct ChanNode *chan), UNUSED_ARG(void *data)) +typedef USERLIST_CALLBACK(userlist_callback_t); + +#define USERAUTH_CALLBACK(NAME) void NAME(UNUSED_ARG(struct ClientSocket *client), UNUSED_ARG(char *user_nick), UNUSED_ARG(struct UserNode *user), UNUSED_ARG(void *data)) +typedef USERAUTH_CALLBACK(userauth_callback_t); + +#define get_userlist_if_invisible(CHAN, MODID, CALLBACK, DATA) _get_userlist_with_invisible(CHAN, MODID, CALLBACK, DATA, 0) +#define get_userlist_with_invisible(CHAN, MODID, CALLBACK, DATA) _get_userlist_with_invisible(CHAN, MODID, CALLBACK, DATA, 1) + +#ifndef DND_FUNCTIONS +void clear_whoqueue(struct ClientSocket *client); +void recv_whohandler_354(struct ClientSocket *client, char **argv, unsigned int argc); +void recv_whohandler_315(struct ClientSocket *client, char **argv, unsigned int argc); +/* MODULAR ACCESSIBLE */ void get_userlist(struct ChanNode *chan, int module_id, userlist_callback_t callback, void *data); +/* MODULAR ACCESSIBLE */ void _get_userlist_with_invisible(struct ChanNode *chan, int module_id, userlist_callback_t callback, void *data, int force); +/* MODULAR ACCESSIBLE */ void get_userauth(struct UserNode *user, int module_id, userauth_callback_t callback, void *data); +void free_whoqueue(); +#endif +#endif diff --git a/src/bots.c b/src/bots.c new file mode 100644 index 0000000..7da678c --- /dev/null +++ b/src/bots.c @@ -0,0 +1,304 @@ +/* bots.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "bots.h" +#include "timeq.h" +#include "mysqlConn.h" +#include "ClientSocket.h" +#include "UserNode.h" +#include "ChanNode.h" +#include "ChanUser.h" +#include "version.h" +#include "modcmd.h" +#include "DBHelper.h" +#include "IRCEvents.h" +#include "tools.h" +#include "log.h" + +struct cmd_bot_alias { + int botid; + char *alias; + struct cmd_bot_alias *next; +}; + +static struct cmd_bot_alias *bot_aliases = NULL; + +static void start_zero_bots() { + struct ClientSocket *client; + MYSQL_RES *res, *res2; + MYSQL_ROW row; + + printf_mysql_query("SELECT `nick`, `ident`, `realname`, `server`, `port`, `pass`, `textbot`, `id`, `queue`, `ssl`, `bind`, `secret` FROM `bots` WHERE `botclass` = '0' AND `active` = '1'"); + res = mysql_use(); + + while ((row = mysql_fetch_row(res)) != NULL) { + client = create_socket(row[3], atoi(row[4]), row[10], row[5], row[0], row[1], row[2]); + client->flags |= (strcmp(row[6], "0") ? SOCKET_FLAG_PREFERRED : 0); + client->flags |= (strcmp(row[8], "0") ? SOCKET_FLAG_USE_QUEUE : 0); + client->flags |= (strcmp(row[9], "0") ? SOCKET_FLAG_SSL : 0); + client->flags |= (strcmp(row[11], "0") ? SOCKET_FLAG_SECRET_BOT : 0); + client->botid = 0; + client->clientid = atoi(row[7]); + connect_socket(client); + + printf_mysql_query("SELECT `command`, `function`, `parameters`, `global_access`, `chan_access`, `flags` FROM `bot_binds` WHERE `botclass` = '0' AND `botid` = '%d'", client->clientid); + res2 = mysql_use(); + while ((row = mysql_fetch_row(res2)) != NULL) { + if(bind_botwise_cmd_to_command(0, client->clientid, row[0], row[1])) { + if(row[2] && strcmp(row[2], "")) { + bind_botwise_set_parameters(0, client->clientid, row[0], row[2]); + } + if(row[3]) { + bind_botwise_set_global_access(0, client->clientid, row[0], atoi(row[3])); + } + if(row[4]) { + bind_botwise_set_channel_access(0, client->clientid, row[0], row[4]); + } + if(strcmp(row[5], "0")) + bind_botwise_set_bind_flags(0, client->clientid, row[0], atoi(row[5])); + } + } + bind_botwise_unbound_required_functions(0, client->clientid); + } +} + +static void zero_bots_trigger_callback(int clientid, struct ChanNode *chan, char *trigger) { + MYSQL_RES *res; + MYSQL_ROW row; + loadChannelSettings(chan); + if(!(chan->flags & CHANFLAG_CHAN_REGISTERED)) { + strcpy(trigger, "+"); + return; + } + printf_mysql_query("SELECT `trigger`, `defaulttrigger` FROM `bot_channels` LEFT JOIN `bots` ON `botid` = `bots`.`id` WHERE `chanid` = '%d' AND `botclass` = '0' AND `botid` = '%d'", chan->channel_id, clientid); + res = mysql_use(); + if(!(row = mysql_fetch_row(res))) { + strcpy(trigger, ""); + return; + } + if(row[0] && *row[0]) + strcpy(trigger, row[0]); + else + strcpy(trigger, ((row[1] && *row[1]) ? row[1] : "+")); +} + +static void zero_bots_bot_ready(struct ClientSocket *client) { + if(client->botid != 0) + return; + MYSQL_RES *res; + MYSQL_ROW row; + + printf_mysql_query("SELECT `automodes`, `oper_user`, `oper_pass` FROM `bots` WHERE `id` = '%d'", client->clientid); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + if(row[1] && row[2]) { + putsock(client, "OPER %s %s", row[1], row[2]); + } + putsock(client, "MODE %s +%s", client->user->nick, row[0]); + } + + printf_mysql_query("SELECT `channel_name`, `channel_key` FROM `bot_channels` LEFT JOIN `channels` ON `chanid` = `channel_id` WHERE `botid` = '%d' AND `suspended` = '0'", client->clientid); + res = mysql_use(); + + while ((row = mysql_fetch_row(res)) != NULL) { + putsock(client, "JOIN %s %s", row[0], row[1]); + } +} + +static TIMEQ_CALLBACK(load_timed_bans) { + MYSQL_RES *res; + MYSQL_ROW row; + //load all timed bans for the next 7 days + printf_mysql_query("SELECT `ban_id`, `ban_timeout` FROM `bans` WHERE `ban_timeout` > 0 AND `ban_timeout` < (UNIX_TIMESTAMP() + (86400 * 7))"); + res = mysql_use(); + char nameBuf[20]; + while ((row = mysql_fetch_row(res)) != NULL) { + if(atol(row[1]) - time(0) > 0) { + sprintf(nameBuf, "ban_%s", row[0]); + timeq_add_name(nameBuf, atol(row[1]) - time(0), 0, channel_ban_timeout, strdup(row[0])); + } else { + //timed out + printf_mysql_query("DELETE FROM `bans` WHERE `ban_id` = '%s'", row[0]); + } + } + timeq_add(86400*7, 0, load_timed_bans, NULL); +} + +void init_bots() { + set_bot_alias(0, "0"); + start_zero_bots(); + set_trigger_callback(0, 0, zero_bots_trigger_callback); + bind_bot_ready(zero_bots_bot_ready, 0); + + timeq_add(10, 0, load_timed_bans, NULL); + +} + +struct ClientSocket *getChannelBot(struct ChanNode *chan, int botid) { + struct ClientSocket *bot, *use_bot = NULL, *second_bot = NULL, *third_bot = NULL; + struct ChanUser *chanuser; + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(botid && bot->botid != botid) continue; + if(!chan || (chanuser = getChanUser(bot->user, chan)) != NULL) { + if(chan && (chanuser->flags & CHANUSERFLAG_OPPED)) { + use_bot = bot; + if(bot->flags & SOCKET_FLAG_PREFERRED) break; + } else if(bot->flags & SOCKET_FLAG_PREFERRED) + second_bot = bot; + else + third_bot = bot; + } + } + if(!use_bot) use_bot = second_bot; + if(!use_bot) use_bot = third_bot; + return use_bot; +} + +void requestOp(struct UserNode *user, struct ChanNode *chan) { + struct ClientSocket *bot, *userbot = NULL; + struct ChanUser *chanuser = getChanUser(user, chan); + char opped = 0; + if(!chanuser) return; + if((chanuser->flags & CHANUSERFLAG_OPPED)) return; + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(!opped && (chanuser = getChanUser(bot->user, chan)) != NULL && (chanuser->flags & CHANUSERFLAG_OPPED)) { + opped = 1; + putsock(bot, "MODE %s +o %s", chan->name, user->nick); + } + if(bot->user == user) { + userbot = bot; + } + } + if(!opped) { + if(userbot && (isUserModeSet(user, 'o') || isUserModeSet(user, 'O') || isUserModeSet(user, 'k') || isUserModeSet(user, 'X'))) { + putsock(userbot, "MODE %s +o %s", chan->name, user->nick); + putsock(userbot, "OPMODE %s +o %s", chan->name, user->nick); + } + } +} + +void requestInvite(struct UserNode *user, struct ChanNode *chan) { + struct ClientSocket *bot; + struct ChanUser *chanuser = getChanUser(user, chan); + char invited = 0; + if(chanuser) return; + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(!invited && (chanuser = getChanUser(bot->user, chan)) != NULL && (chanuser->flags & CHANUSERFLAG_OPPED)) { + invited = 1; + putsock(bot, "INVITE %s %s", user->nick, chan->name); + } + } +} + +TIMEQ_CALLBACK(channel_ban_timeout) { + char *str_banid = data; + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `ban_mask`, `channel_name` FROM `bans` LEFT JOIN `channels` ON `ban_channel` = `channel_id` WHERE `ban_id` = '%s'", str_banid); + res = mysql_use(); + struct ChanNode *chan; + if((row = mysql_fetch_row(res)) != NULL && (chan = getChanByName(row[1])) != NULL) { + struct ClientSocket *use_bot = getChannelBot(chan, 0); + if(use_bot) { + putsock(use_bot, "MODE %s -b %s", chan->name, row[0]); + } + printf_mysql_query("DELETE FROM `bans` WHERE `ban_id` = '%s'", str_banid); + } + free(str_banid); +} + +static int general_ctcp(char *buffer, char *command, char *text); + +void general_event_privctcp(struct UserNode *user, struct UserNode *target, char *command, char *text) { + char ctcpBuf[MAXLEN]; + if(general_ctcp(ctcpBuf, command, text)) { + struct ClientSocket *bot; + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(bot->user == target) break; + } + if(bot) + putsock(bot, "NOTICE %s :\001%s\001", user->nick, ctcpBuf); + } +} + +static int general_ctcp(char *buffer, char *command, char *text) { + if(!stricmp(command, "VERSION")) { + sprintf(buffer, "VERSION NeonServ v%s.%d by pk910 (%s)", NEONSERV_VERSION, patchlevel, (strcmp(revision, "") ? revision : "-")); + return 1; + } + if(!stricmp(command, "FINGER")) { + sprintf(buffer, "FINGER NeonServ v%s.%d (%s) build %s lines C code using " COMPILER " (see +netinfo)", NEONSERV_VERSION, patchlevel, (strcmp(revision, "") ? revision : "-"), codelines); + return 1; + } + if(!stricmp(command, "PING")) { + sprintf(buffer, "PING %s", (text ? text : "0")); + return 1; + } + if(!stricmp(command, "TIME")) { + time_t rawtime; + struct tm *timeinfo; + char timeBuf[80]; + time(&rawtime); + timeinfo = localtime(&rawtime); + strftime(timeBuf, 80, "%c", timeinfo); + sprintf(buffer, "TIME %s", timeBuf); + return 1; + } + return 0; +} + +void set_bot_alias(int botid, char *alias) { + struct cmd_bot_alias *botalias, *existing_alias = NULL; + for(botalias = bot_aliases; botalias; botalias = botalias->next) { + if(!stricmp(botalias->alias, alias)) + return; + if(botalias->botid == botid) + existing_alias = botalias; + } + if(existing_alias) { + free(existing_alias->alias); + existing_alias->alias = strdup(alias); + return; + } + botalias = malloc(sizeof(*botalias)); + if (!botalias) { + printf_log("main", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + botalias->botid = botid; + botalias->alias = strdup(alias); + botalias->next = bot_aliases; + bot_aliases = botalias; +} + +const char *resolve_botid(int botid) { + struct cmd_bot_alias *botalias; + for(botalias = bot_aliases; botalias; botalias = botalias->next) { + if(botalias->botid == botid) + return botalias->alias; + } + return NULL; +} + +int resolve_botalias(const char *alias) { + struct cmd_bot_alias *botalias; + for(botalias = bot_aliases; botalias; botalias = botalias->next) { + if(!stricmp(botalias->alias, alias)) + return botalias->botid; + } + return -1; +} diff --git a/src/bots.h b/src/bots.h new file mode 100644 index 0000000..fd1eac2 --- /dev/null +++ b/src/bots.h @@ -0,0 +1,39 @@ +/* bots.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _bots_h +#define _bots_h + +#include "main.h" +#include "timeq.h" + +struct UserNode; +struct ChanNode; +struct ClientSocket; + +#ifndef DND_FUNCTIONS +void init_bots(); + +/* MODULAR ACCESSIBLE */ struct ClientSocket *getChannelBot(struct ChanNode *chan, int botid); +/* MODULAR ACCESSIBLE */ void requestOp(struct UserNode *user, struct ChanNode *chan); +/* MODULAR ACCESSIBLE */ void requestInvite(struct UserNode *user, struct ChanNode *chan); +/* MODULAR ACCESSIBLE */ TIMEQ_CALLBACK(channel_ban_timeout); +/* MODULAR ACCESSIBLE */ void general_event_privctcp(struct UserNode *user, struct UserNode *target, char *command, char *text); +/* MODULAR ACCESSIBLE */ void set_bot_alias(int botid, char *alias); +/* MODULAR ACCESSIBLE */ const char *resolve_botid(int botid); +/* MODULAR ACCESSIBLE */ int resolve_botalias(const char *alias); +#endif +#endif \ No newline at end of file diff --git a/src/lang.c b/src/lang.c new file mode 100644 index 0000000..7d1532b --- /dev/null +++ b/src/lang.c @@ -0,0 +1,228 @@ +/* lang.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#include "lang.h" +#include "UserNode.h" +#include "DBHelper.h" +#include "mysqlConn.h" +#include "tools.h" +#include "log.h" + +#define DEFAULT_LANG_TAG "EN" +#define DEFAULT_LANG_NAME "English" + +static struct language **langdict; +static struct language *lang_c; + +void init_lang() { + langdict = calloc(MAXLANGUAGES, sizeof(*langdict)); +} + +void free_lang() { + +} + +void load_languages() { + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `lang`, `text` FROM `language` WHERE `ident` = 'name'"); + res = mysql_use(); + while((row = mysql_fetch_row(res)) != NULL) { + load_language(row[0], row[1]); + } +} + +static struct language* add_language(char *langtag, char *langname) { + int cindex; + for(cindex = 0; cindex < MAXLANGUAGES; cindex++) { + if(langdict[cindex] == NULL) break; + if(!strcmp(langdict[cindex]->langname, langname) || !strcmp(langdict[cindex]->langtag, langtag)) + return langdict[cindex]; + } + if(cindex == MAXLANGUAGES) return NULL; + struct language *lang = malloc(sizeof(*lang)); + if (!lang) { + printf_log("main", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return NULL; + } + lang->langtag = strdup(langtag); + lang->langname = strdup(langname); + struct language_table **entrys = calloc(27, sizeof(*entrys)); + lang->entrys = entrys; + langdict[cindex] = lang; + return lang; +} + +static int get_entry_index(const char *ident) { + const char *underscore = strstr(ident, "_"); + if(!underscore || !(underscore[1] >= 65 && underscore[1] <= 90)) return 26; + return (underscore[1] - 'A'); +} + +void load_language(char *tag, char *name) { + struct language *lang = get_language_by_tag(tag); + if(lang == get_default_language()) return; + if(lang) { + //remove all entrys + int cindex; + struct language_table *entry, *next; + for(cindex = 0; cindex < 27; cindex++) { + for(entry = lang->entrys[cindex]; entry; entry = next) { + next = entry->next; + free(entry->ident); + free(entry->text); + free(entry); + } + lang->entrys[cindex] = NULL; + } + } else + lang = add_language(tag, name); + if(!lang) return; + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `ident`, `text` FROM `language` WHERE `lang` = '%s' AND `ident` != 'name'", escape_string(tag)); + res = mysql_use(); + while((row = mysql_fetch_row(res)) != NULL) { + register_language_string(lang, row[0], row[1]); + } +} + +struct language* get_language_by_tag(char *tag) { + int cindex; + for(cindex = 0; cindex < MAXLANGUAGES; cindex++) { + if(langdict[cindex] == NULL) break; + if(!stricmp(langdict[cindex]->langtag, tag)) + return langdict[cindex]; + } + return NULL; +} + +struct language* get_language_by_name(char *name) { + int cindex; + for(cindex = 0; cindex < MAXLANGUAGES; cindex++) { + if(langdict[cindex] == NULL) break; + if(!stricmp(langdict[cindex]->langname, name)) + return langdict[cindex]; + } + return NULL; +} + +struct language* get_default_language() { + if(lang_c == NULL) + lang_c = add_language(DEFAULT_LANG_TAG, DEFAULT_LANG_NAME); + return lang_c; +} + +void register_default_language_table(const struct default_language_entry *msgtab) { + if(lang_c == NULL) + lang_c = add_language(DEFAULT_LANG_TAG, DEFAULT_LANG_NAME); + while(msgtab->ident) { + register_language_string(lang_c, msgtab->ident, msgtab->text); + msgtab++; + } +} + +void register_language_string(struct language *lang, char *ident, char *text) { + int cindex = get_entry_index(ident); + struct language_table *lang_entry; + for(lang_entry = lang->entrys[cindex]; lang_entry; lang_entry = lang_entry->next) { + if(!strcmp(lang_entry->ident, ident)) break; + } + if(!lang_entry) { + lang_entry = malloc(sizeof(*lang_entry)); + if (!lang_entry) { + printf_log("main", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + lang_entry->ident = strdup(ident); + lang_entry->next = lang->entrys[cindex]; + lang->entrys[cindex] = lang_entry; + } else + free(lang_entry->text); //free old text (new one will be set below) + //replace all: + //$b to \002 + //$k to \003 + char txt[MAXLEN]; + strcpy(txt, text); + char tmp[MAXLEN]; + int tmppos = 0; + char *a, *b = txt; + do { + a = strstr(b, "$"); + if(a) *a = '\0'; + tmppos += sprintf(tmp + tmppos, "%s", b); + if(a) { + switch(a[1]) { + case 'b': + tmp[tmppos++] = 2; + break; + case 'k': + tmp[tmppos++] = 3; + break; + case 'u': + tmp[tmppos++] = 31; + break; + default: + //unknown - just write it + tmppos += sprintf(tmp + tmppos, "$%c", a[1]); + } + b = a+2; + } + } while(a); + lang_entry->text = strdup(tmp); +} + +char *get_language_string(struct UserNode *user, const char* msg_ident) { + struct language* lang; + if(user && (user->flags & USERFLAG_ISAUTHED)) { + loadUserSettings(user); + lang = user->language; + } else + lang = lang_c; + int cindex = get_entry_index(msg_ident); + struct language_table* entry; + for(entry = lang->entrys[cindex]; entry; entry = entry->next) { + if(!strcmp(entry->ident, msg_ident)) + return entry->text; + } + if(lang == lang_c) return NULL; + for(entry = lang_c->entrys[cindex]; entry; entry = entry->next) { + if(!strcmp(entry->ident, msg_ident)) + return entry->text; + } + return NULL; +} + +char *build_language_string(struct UserNode *user, char *buffer, const char *msg_ident, ...) { + char *formatStr = get_language_string(user, msg_ident); + if(!formatStr) return NULL; + if(buffer == NULL) { + buffer = (char *)malloc((MAXLEN+1) * sizeof(char)); + if (!buffer) { + printf_log("main", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return NULL; + } + } + int pos; + va_list arg_list; + buffer[0] = '\0'; + va_start(arg_list, msg_ident); + pos = vsnprintf(buffer, MAXLEN - 2, formatStr, arg_list); + va_end(arg_list); + if (pos < 0 || pos > (MAXLEN - 2)) pos = MAXLEN - 2; + buffer[pos] = '\0'; + return buffer; +} diff --git a/src/lang.h b/src/lang.h new file mode 100644 index 0000000..64652ed --- /dev/null +++ b/src/lang.h @@ -0,0 +1,55 @@ +/* lang.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _lang_h +#define _lang_h + +#include "main.h" + +struct UserNode; + +struct default_language_entry { + char *ident; + char *text; +}; + +struct language_table { + char *ident; + char *text; + + struct language_table *next; +}; + +struct language { + char *langtag; + char *langname; + struct language_table **entrys; +}; + +#ifndef DND_FUNCTIONS +void init_lang(); +void free_lang(); +/* MODULAR ACCESSIBLE */ struct language* get_language_by_tag(char *tag); +/* MODULAR ACCESSIBLE */ struct language* get_language_by_name(char *name); +/* MODULAR ACCESSIBLE */ struct language* get_default_language(); +void load_languages(); +/* MODULAR ACCESSIBLE */ void load_language(char *tag, char *name); +void register_language_string(struct language *lang, char *ident, char *text); +/* MODULAR ACCESSIBLE */ void register_default_language_table(const struct default_language_entry *msgtab); +/* MODULAR ACCESSIBLE */ char *get_language_string(struct UserNode *user, const char* msg_ident); +/* MODULAR ACCESSIBLE */ char *build_language_string(struct UserNode *user, char *buffer, const char *msg_ident, ...); +#endif +#endif diff --git a/src/log.c b/src/log.c new file mode 100644 index 0000000..44d63df --- /dev/null +++ b/src/log.c @@ -0,0 +1,297 @@ +/* log.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "log.h" +#include "ChanNode.h" +#include "IRCEvents.h" +#include "ClientSocket.h" +#include "bots.h" +#include "IOHandler.h" +#include "tools.h" +#include "ConfigParser.h" + +#define MAXLOGLEN 1024 + +static void write_log(const char *module, const int section, const char *line, int len); +static void load_log_targets(); +static void unload_log_targets(); +static int reload_log_targets(int init); + +#ifdef HAVE_THREADS +static pthread_mutex_t log_sync; +#endif + +#define LOG_TARGET_TYPE_FILE 1 +#define LOG_TARGET_TYPE_STDOUT 2 +#define LOG_TARGET_TYPE_STDERR 3 +#define LOG_TARGET_TYPE_IRC 4 + +static const struct { + int id; + char *name; +} log_sections[] = { + {LOG_INFO, "info"}, + {LOG_DEBUG, "debug"}, + {LOG_IRCRAW, "raw"}, + {LOG_MYSQL, "mysql"}, + {LOG_OVERRIDE, "override"}, + {LOG_WARNING, "warning"}, + {LOG_ERROR, "error"}, + {LOG_FATAL, "fatal"}, + {0, NULL} +}; + +struct log_target { + char *module; + int section; + int type : 8; + union { + char *channel; + FILE *fptr; + } target; + struct log_target *next; +}; + +static struct log_target *log_targets = NULL; + +static char *get_section_name(int section_id) { + int i = -1; + while(log_sections[++i].id) { + if(log_sections[i].id == section_id) + return log_sections[i].name; + } + return NULL; +} + +static int get_section_id(char *section_name) { + int i = -1; + while(log_sections[++i].id) { + if(!stricmp(log_sections[i].name, section_name)) + return log_sections[i].id; + } + return 0; +} + + +void printf_log(const char *module, const int section, const char *text, ...) { + va_list arg_list; + char logBuf[MAXLOGLEN]; + int pos; + logBuf[0] = '\0'; + va_start(arg_list, text); + pos = vsnprintf(logBuf, MAXLOGLEN - 1, text, arg_list); + va_end(arg_list); + if (pos < 0 || pos > (MAXLOGLEN - 1)) pos = MAXLOGLEN - 1; + logBuf[pos] = '\0'; + write_log(module, section, logBuf, pos); +} + +static void write_log(const char *module, const int section, const char *line, int len) { + if(!log_targets) + return; + SYNCHRONIZE(log_sync); + struct log_target *target; + char lineBuf[MAXLOGLEN]; + char timeBuf[80]; + int lineBufPos, timeBufPos; + time_t rawtime; + struct tm *timeinfo; + int i, j; + + time(&rawtime); + timeinfo = localtime(&rawtime); + timeBufPos = strftime(timeBuf, 80, "[%X %x] ", timeinfo); + + lineBufPos = sprintf(lineBuf, "(%s:", module); + for(i = 0, j = 0; i < LOG_SECTIONS; i++) { + if((section & (1 << i))) { + char *section_name = get_section_name((1 << i)); + if(!section_name) + continue; + if(j++) + lineBuf[lineBufPos++] = ','; + lineBufPos += sprintf(lineBuf + lineBufPos, "%s", section_name); + } + } + lineBufPos += sprintf(lineBuf + lineBufPos, ") %s", line); + //cut off \n \r + while(lineBuf[lineBufPos-1] == '\r' || lineBuf[lineBufPos-1] == '\n') { + lineBuf[lineBufPos-1] = '\0'; + lineBufPos--; + } + + j = 0; + for(target = log_targets; target; target = target->next) { + if(strcmp(target->module, "*") && stricmp(target->module, module)) + continue; + if(!(target->section & section)) + continue; + if(target->type == LOG_TARGET_TYPE_IRC) { + if(section == LOG_IRCRAW || !stricmp(module, "iohandler")) + continue; //endless loop ;) + struct ChanNode *channel = getChanByName(target->target.channel); + struct ClientSocket *client; + if(channel && (client = getChannelBot(channel, 0))) { + putsock(client, "PRIVMSG %s :%s", channel->name, lineBuf); + } + } else if(target->type == LOG_TARGET_TYPE_FILE) { + fwrite(timeBuf, 1, timeBufPos, target->target.fptr); + fwrite(lineBuf, 1, lineBufPos, target->target.fptr); + fwrite("\n", 1, 1, target->target.fptr); + } else if(target->type == LOG_TARGET_TYPE_STDOUT || target->type == LOG_TARGET_TYPE_STDERR) { + if(process_state.daemonized) + continue; //block stdout / stderr as daemon + fprintf((target->type == LOG_TARGET_TYPE_STDOUT ? stdout : stderr), "%s%s\n", timeBuf, lineBuf); + j = 1; + } + } + if((process_state.loglevel & section) && !process_state.daemonized && !j) { + fprintf(stdout, "%s%s\n", timeBuf, lineBuf); + } + + DESYNCHRONIZE(log_sync); +} + +static void load_log_targets() { + if(log_targets) + return; + char **targetlist = get_all_fieldnames("logs"); + if(!targetlist) return; + int i = 0; + char tmp[MAXLEN]; + struct log_target *ctarget; + while(targetlist[i]) { + sprintf(tmp, "logs.%s", targetlist[i]); + char *type = get_string_field(tmp); + char *target = strchr(type, ':'); + char *module = targetlist[i]; + char *section = strchr(module, ':'); + if(!target || !section) { + i++; + continue; + } + *target = '\0'; + target++; + *section = '\0'; + section++; + ctarget = malloc(sizeof(*ctarget)); + if(!stricmp(type, "file")) { + ctarget->type = LOG_TARGET_TYPE_FILE; + ctarget->target.fptr = fopen(target, "a"); + if(!ctarget->target.fptr) { + free(ctarget); + ctarget = NULL; + } + } else if(!stricmp(type, "std")) { + if(!stricmp(target, "out")) + ctarget->type = LOG_TARGET_TYPE_STDOUT; + else if(!stricmp(target, "err")) + ctarget->type = LOG_TARGET_TYPE_STDERR; + else { + free(ctarget); + ctarget = NULL; + } + } else if(!stricmp(type, "irc")) { + if(is_valid_chan(target)) { + ctarget->type = LOG_TARGET_TYPE_IRC; + ctarget->target.channel = strdup(target); + } else { + free(ctarget); + ctarget = NULL; + } + } else { + free(ctarget); + ctarget = NULL; + } + if(ctarget) { + ctarget->module = strdup(module); + ctarget->section = 0; + char *csection = section; + while(1) { + char *next_section = strchr(csection, ','); + if(next_section) + *next_section = '\0'; + if(!strcmp(csection, "*")) + ctarget->section |= LOG_ALL; + else + ctarget->section |= get_section_id(csection); + if(next_section) { + *next_section = ','; + csection = next_section + 1; + } else + break; + } + ctarget->next = log_targets; + log_targets = ctarget; + } + target--; + *target = ':'; + section--; + *section = ':'; + i++; + } +} + +static void unload_log_targets() { + struct log_target *target, *next_target; + for(target = log_targets; target; target = next_target) { + next_target = target->next; + if(target->type == LOG_TARGET_TYPE_IRC) + free(target->target.channel); + else if(target->type == LOG_TARGET_TYPE_FILE) + fclose(target->target.fptr); + free(target); + } + log_targets = NULL; +} + +static int reload_log_targets(int init) { + unload_log_targets(); + load_log_targets(); + return 1; +} + +static IOHANDLER_LOG_BACKEND(log_iohandler_backend) { + int log_level; + switch(type) { + case IOLOG_DEBUG: + log_level = LOG_DEBUG; + break; + case IOLOG_WARNING: + log_level = LOG_WARNING; + break; + case IOLOG_ERROR: + log_level = LOG_ERROR; + break; + case IOLOG_FATAL: + log_level = LOG_FATAL; + break; + default: + log_level = 0; + } + write_log("iohandler", log_level, line, strlen(line)); +} + +void init_log() { + #if HAVE_THREADS + THREAD_MUTEX_INIT(log_sync); + #endif + load_log_targets(); + bind_reload(reload_log_targets, 0); + iolog_backend = log_iohandler_backend; + printf_log("log", LOG_INFO, "initialized log system."); +} diff --git a/src/log.h b/src/log.h new file mode 100644 index 0000000..657e527 --- /dev/null +++ b/src/log.h @@ -0,0 +1,39 @@ +/* log.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _log_h +#define _log_h +#include "main.h" + +#define LOG_INFO 0x01 +#define LOG_DEBUG 0x02 +#define LOG_IRCRAW 0x04 +#define LOG_MYSQL 0x08 +#define LOG_OVERRIDE 0x10 +#define LOG_WARNING 0x20 +#define LOG_ERROR 0x40 +#define LOG_FATAL 0x80 + +#define LOG_ALL 0xff +#define LOG_SECTIONS 8 + +#ifndef DND_FUNCTIONS + +void printf_log(const char *module, const int section, const char *text, ...); +void init_log(); + +#endif +#endif diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..fb16300 --- /dev/null +++ b/src/main.c @@ -0,0 +1,600 @@ +/* main.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#define DEFAULT_PID_FILE "neonserv.pid" +#define DEFAULT_CONF_FILE "neonserv.conf" + +#include "main.h" +#include "signal.h" +#include "ClientSocket.h" +#include "UserNode.h" +#include "ChanNode.h" +#include "IRCEvents.h" +#include "IRCParser.h" +#include "modcmd.h" +#include "WHOHandler.h" +#include "bots.h" +#include "mysqlConn.h" +#include "HandleInfoHandler.h" +#include "lang.h" +#include "tools.h" +#include "timeq.h" +#include "EventLogger.h" +#include "ModeNode.h" +#include "IRCQueue.h" +#include "DBHelper.h" +#include "ConfigParser.h" +#include "QServer.h" +#include "version.h" +#include "modules.h" +#include "module_commands.h" +#include "ModuleFunctions.h" +#include "IOHandler.h" +#include "statistics.h" +#include "log.h" + +struct ProcessState process_state; + +#ifdef HAVE_THREADS +pthread_mutex_t cache_sync; +pthread_mutex_t whohandler_sync, whohandler_mass_sync; +static pthread_t *current_threads = NULL; +#endif + +static void *main_tread(void *empty); +static TIMEQ_CALLBACK(clear_cache); +static TIMEQ_CALLBACK(main_checkauths); +static void check_firstrun(); + + +static void main_parse_arguments() { + int c; + struct option options[] = { + {"show", 1, 0, 's'}, + {"foreground", 0, 0, 'f'}, + {"config", 1, 0, 'c'}, + {"pid", 1, 0, 'p'}, + {"help", 0, 0, 'h'}, + {"version", 0, 0, 'v'}, + {0, 0, 0, 0} + }; + while ((c = getopt_long(process_state.argc, process_state.argv, "s:fvh", options, NULL)) != -1) { + switch (c) { + case 'c': + strncpy(process_state.config, optarg, MAXLEN-1); + break; + case 'p': + strncpy(process_state.pidfile, optarg, MAXLEN-1); + break; + case 's': + process_state.loglevel = atoi(optarg); + break; + case 'f': + process_state.run_as_daemon = 0; + break; + case 'v': + printf("Version: %s.%d (%s)\n", NEONSERV_VERSION, patchlevel, (strcmp(revision, "") ? revision : "-")); + printf("Build: #%s %s (%s lines, " COMPILER ")\n", compilation, creation, codelines); + exit(0); + break; + case 'h': + printf("Usage: ./neonserv [-c neonserv.conf] [-p neonserv.pid] [-s loglevel] [-f] [-h|-v]\n"); + printf(" -c, --config use this configuration file.\n"); + printf(" -f, --foreground run NeonServ in the foreground.\n"); + printf(" -h, --help prints this usage message.\n"); + printf(" -p, --pid use this pid file.\n"); + printf(" -s, --show show log lines matching loglevel in stdout.\n"); + printf(" -v, --version prints this program's version.\n"); + exit(0); + break; + } + } +} + +static void main_daemon_exit() { + remove(process_state.pidfile); +} + +static void main_daemonize() { + #ifndef WIN32 + /* Attempt to fork into the background if daemon mode is on. */ + pid_t pid = fork(); + if (pid < 0) { + fprintf(stderr, "Unable to fork: %s\n", strerror(errno)); + } else if (pid > 0) { + printf("Forking into the background (pid: %d)...\n", pid); + printf_log("main", LOG_INFO, "Forking into the background (pid: %d)...\n", pid); + exit(0); + } + setsid(); + process_state.daemonized = 1; + atexit(main_daemon_exit); + FILE *pidfile = fopen(process_state.pidfile, "w"); + if (pidfile == NULL) { + fprintf(stderr, "Unable to create PID file: %s\n", strerror(errno)); + printf_log("main", LOG_ERROR, "Unable to create PID file: %s\n", strerror(errno)); + } else { + fprintf(pidfile, "%i\n", (int)getpid()); + fclose(pidfile); + } + FILE *retn; + fclose(stdin); retn = fopen("/dev/null", "r"); + fclose(stdout); retn = fopen("/dev/null", "w"); + fclose(stderr); retn = fopen("/dev/null", "w"); + #endif +} + +static int reload_configuration() { + printf_log("main", LOG_DEBUG, "reloading configuration file: %s", process_state.config); + if(!loadConfig(process_state.config)) { + printf_log("main", LOG_ERROR, "could not reload configuration file: %s", process_state.config); + return 1; + } + if(process_state.loaded_config) { + if(!reload_mysql()) + return 2; + char **modulelist = get_all_fieldnames("modules"); + if(!modulelist || !modulelist[0]) { + free(modulelist); + return 3; + } + free(modulelist); + event_reload(0); + } + process_state.loaded_config = 1; + return 0; +} + + +/* INITIALISATION OF SUBSYSTEMS */ +void initialize_subsystems() { + init_bind(); + init_log(); + printf_log("main", LOG_INFO, "starting up NeonServ %s subsystems...", NEONSERV_VERSION); + init_lang(); + init_parser(); + init_UserNode(); + init_ChanNode(); + init_ModeNode(); + init_modcmd(); + register_module_commands(); + init_handleinfohandler(); + init_tools(); + init_modulefunctions(); + loadModules(); + init_bots(); + init_DBHelper(); + qserver_init(); + load_languages(); + init_statistics(); +} + +void shutdown_subsystems() { + printf_log("main", LOG_INFO, "stopping NeonServ subsystems..."); + free_sockets(1); + //wait 50ms (run iohandler) + { + struct timeval timeout, ctime1, ctime2; + gettimeofday(&ctime1, NULL); + ctime1.tv_usec += 50000; + if(ctime1.tv_usec > 1000000) { + ctime1.tv_usec -= 1000000; + ctime1.tv_sec++; + } + do { + timeout.tv_sec = 0; + timeout.tv_usec = 10000; + iohandler_poll_timeout(timeout); + gettimeofday(&ctime2, NULL); + } while(timeval_is_bigger(ctime1, ctime2)); + } + stop_modules(); + free_sockets(0); + qserver_free(); + free_parser(); + free_UserNode(); + free_ChanNode(); + free_bind(); + free_modcmd(); + free_whoqueue(); + free_mysql(); + free_handleinfohandler(); + free_lang(); +} + +/* THREAD CONTROL */ +#ifdef HAVE_THREADS +int getCurrentThreadID() { + if(!current_threads) return 0; + int i; + unsigned int my_tid = (unsigned int) pthread_self_tid(); + for(i = 0; i < process_state.running_threads; i++) { + #ifdef WIN32 + if((unsigned int) current_threads[i].p == my_tid) + #else + if((unsigned int) current_threads[i] == my_tid) + #endif + return i+1; + } + return 0; +} +#endif + +int getRunningThreads() { + #ifdef HAVE_THREADS + return process_state.running_threads; + #else + return 1; + #endif +} + +static void main_start_threads() { + int worker_threads = get_int_field("General.worker_threads"); + if(!worker_threads) worker_threads = 1; + #ifdef HAVE_THREADS + int tid_id = 0; + { + current_threads = calloc(worker_threads, sizeof(*current_threads)); + for(tid_id = 0; tid_id < worker_threads; tid_id++) { + process_state.running_threads++; + pthread_create(¤t_threads[tid_id], NULL, main_tread, NULL); + } + } + #endif + main_tread(NULL); + #ifdef HAVE_THREADS + { + for(tid_id = 0; tid_id < worker_threads; tid_id++) { + pthread_join(current_threads[tid_id], NULL); + } + process_state.running_threads = 0; + } + #endif +} + +/* MAIN FUNCTION(S) */ + +static void *main_tread(void *empty) { + while(process_state.running) { + iohandler_poll(); + } + return NULL; +} + +static void main_restart_process() { + /* Append a NULL to the end of argv[]. */ + char **restart_argv = (char **)alloca((process_state.argc + 1) * sizeof(char *)); + memcpy(restart_argv, process_state.argv, process_state.argc * sizeof(char *)); + restart_argv[process_state.argc] = NULL; + #ifdef WIN32 + execv(process_state.argv[0], (const char * const*)restart_argv); + #else + execv(process_state.argv[0], restart_argv); + #endif +} + +int main(int argc, char *argv[]) { + memset(&process_state, 0, sizeof(process_state)); + printf("NeonServ v%s\n\n", NEONSERV_VERSION); + + process_state.argv = argv; + process_state.argc = argc; + process_state.run_as_daemon = 1; + strcpy(process_state.config, DEFAULT_CONF_FILE); + strcpy(process_state.pidfile, DEFAULT_PID_FILE); + + //parse argv + main_parse_arguments(); + + //initialize memory debugger BEFORE allocating memory + #ifdef ENABLE_MEMORY_DEBUG + initMemoryDebug(); + #endif + + //initialize mutex debugger BEFORE using any mutexes + #ifdef ENABLE_MUTEX_DEBUG + initMutexDebug(); + #endif + + //deny root startup + #ifndef WIN32 + if(geteuid() == 0 || getuid() == 0) { + fprintf(stderr, "NeonServ may not be run with super user privileges.\n"); + exit(0); + } + #endif + + //load configuration + int errid; + if((errid = reload_configuration())) { + fprintf(stderr, "Unable to load configuration file `%s`. (errid: %d)\n", process_state.config, errid); + exit(0); + } + + //check mysql configuration + if(!reload_mysql()) { + fprintf(stderr, "Unable to load MySQL configuration.\n"); + exit(0); + } + + //check module configuration + char **modulelist = get_all_fieldnames("modules"); + if(!modulelist || !modulelist[0]) { + fprintf(stderr, "Unable to load Module configuration.\n"); + exit(0); + } + free(modulelist); + + #if HAVE_THREADS + THREAD_MUTEX_INIT(cache_sync); + THREAD_MUTEX_INIT(whohandler_sync); + THREAD_MUTEX_INIT(whohandler_mass_sync); + #endif + + //connect to mysql and check if it's the frst bot startup + init_mysql(); + check_firstrun(); + + //deamonize if wanted + if(process_state.run_as_daemon) + main_daemonize(); + + //set signal handlers + signal(SIGABRT, sighandler); + signal(SIGFPE, sighandler); + signal(SIGILL, sighandler); + signal(SIGINT, sighandler); + signal(SIGSEGV, sighandler); + signal(SIGTERM, sighandler); + + //set start time and initialize other code parts + process_state.running = 1; + process_state.start_time = time(0); + initialize_subsystems(); + + //start timers + timeq_add(CLEAR_CACHE_INTERVAL, 0, clear_cache, NULL); + timeq_add(90, 0, main_checkauths, NULL); + + //start worker threads + main_start_threads(); //BLOCKING + + //shutdown sequence... + shutdown_subsystems(); + if(process_state.restart) + main_restart_process(); //terminates the current process on success + + //eop (end of program :P) + //trust me, thats the end! + exit(0); +} + +/* BOT INFORMATION */ +time_t getStartTime() { + return process_state.start_time; +} + +/* BOT CONTROL */ +void restart_bot(int crash) { + if(crash) { + main_daemon_exit(); + main_restart_process(); + } else { + process_state.restart = 1; + process_state.running = 0; + } +} + +void stop_bot() { + process_state.running = 0; +} + +void reload_config() { + reload_configuration(); +} + +/* TIMER FUNCTIONS */ + +static TIMEQ_CALLBACK(clear_cache) { + timeq_add(CLEAR_CACHE_INTERVAL, 0, clear_cache, NULL); + clearTempUsers(); + destroyEvents(); + mysql_free(); +} + +static AUTHLOOKUP_CALLBACK(main_checkauths_callback) { + //check if registered is still valid + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `user_id`, `user_registered` FROM `users` WHERE `user_user` = '%s'", escape_string(auth)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + int diff = registered - atoi(row[1]); + if(diff < 0) + diff *= -1; + if(!exists || (strcmp(row[1], "0") && diff > 86400)) { + //User is no longer valid! Delete it... + deleteUser(atoi(row[0])); + char *alertchan = get_string_field("General.CheckAuths.alertchan"); + if(alertchan) { + char reason[MAXLEN]; + if(!exists) { + strcpy(reason, "USER_NOT_EXISTS"); + } else { + sprintf(reason, "USER_REGISTERED_MISSMATCH: %lu, expected %d (diff: %d)", (unsigned long) registered, atoi(row[1]), diff); + } + struct ChanNode *alertchan_chan = getChanByName(alertchan); + struct ClientSocket *alertclient; + if(alertchan_chan && (alertclient = getChannelBot(alertchan_chan, 0)) != NULL) { + putsock(alertclient, "PRIVMSG %s :Deleted User %s (%s)", alertchan_chan->name, auth, reason); + } + } + } else if(exists && !strcmp(row[1], "0")) { + printf_mysql_query("UPDATE `users` SET `user_registered` = '%lu', `user_lastcheck` = UNIX_TIMESTAMP() WHERE `user_id` = '%s'", (unsigned long) registered, row[0]); + } else { + printf_mysql_query("UPDATE `users` SET `user_lastcheck` = UNIX_TIMESTAMP() WHERE `user_id` = '%s'", row[0]); + } + } +} + +static TIMEQ_CALLBACK(main_checkauths) { + int next_call = 600; + if(get_int_field("General.CheckAuths.enabled")) { + int check_start_time = get_int_field("General.CheckAuths.start_time") * 3600; + int duration = get_int_field("General.CheckAuths.duration") * 60; + int now = getCurrentSecondsOfDay(); + if(now < check_start_time && check_start_time+duration >= 86400) { + check_start_time -= 86400; + } + if(now >= check_start_time && now < (check_start_time + duration)) { + next_call = get_int_field("General.CheckAuths.interval"); + //get the "longest-unchecked-user" + MYSQL_RES *res; + MYSQL_ROW row; + int lastcheck; + time_t unixtime = time(0); + int min_unckecked = get_int_field("General.CheckAuths.min_unckecked"); + printf_mysql_query("SELECT `user_user`, `user_lastcheck` FROM `users` ORDER BY `user_lastcheck` ASC LIMIT 1"); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + lastcheck = atoi(row[1]); + if(!lastcheck || unixtime - lastcheck >= min_unckecked) { + lookup_authname(row[0], 0, main_checkauths_callback, NULL); + } else + next_call = 300; + } + } else { + int pending; + if(now > check_start_time) + pending = 86400 - now + check_start_time; + else + pending = check_start_time - now; + if(pending < 600) + next_call = pending; + } + + } + timeq_add(next_call, 0, main_checkauths, NULL); +} + +/* INSTALLATION SCRIPT */ + +static void check_firstrun() { + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `user_id` FROM `users` WHERE `user_access` = 1000 LIMIT 1"); + res = mysql_use(); + if (mysql_fetch_row(res) == NULL) { + //first run! + printf("No superuser found...\n"); + check_firstrun_admin: + printf("AuthServ account name of admin user: "); + char *ptr; + char adminuser[31]; + ptr = fgets(adminuser, 30, stdin); + for(ptr = adminuser; *ptr; ptr++) { if(*ptr == '\n' || *ptr == '\r') *ptr = '\0'; } + if(strlen(adminuser) < 2) goto check_firstrun_admin; + printf_mysql_query("SELECT `user_id` FROM `users` WHERE `user_user` = '%s'", escape_string(adminuser)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) + printf_mysql_query("UPDATE `users` SET `user_access` = 1000 WHERE `user_id` = '%s'", row[0]); + else + printf_mysql_query("INSERT INTO `users` (`user_user`, `user_access`) VALUES ('%s', 1000)", escape_string(adminuser)); + } + printf_mysql_query("SELECT `id` FROM `bots` WHERE `active` = 1 LIMIT 1"); + res = mysql_use(); + if (mysql_fetch_row(res) == NULL) { + //no bot active + printf("No active bot found...\n\n"); + printf("ADD NEW BOT\n"); + char *ptr; + char bot_nick[31]; + check_firstrun_bot_nick: + printf("Nick: "); + ptr = fgets(bot_nick, 30, stdin); + for(ptr = bot_nick; *ptr; ptr++) { if(*ptr == '\n' || *ptr == '\r') *ptr = '\0'; } + if(strlen(bot_nick) < 2) goto check_firstrun_bot_nick; + char bot_ident[16]; + check_firstrun_bot_ident: + printf("Ident: "); + ptr = fgets(bot_ident, 15, stdin); + for(ptr = bot_ident; *ptr; ptr++) { if(*ptr == '\n' || *ptr == '\r') *ptr = '\0'; } + if(strlen(bot_ident) < 2) goto check_firstrun_bot_ident; + char bot_realname[101]; + check_firstrun_bot_realname: + printf("Realname: "); + ptr = fgets(bot_realname, 100, stdin); + for(ptr = bot_realname; *ptr; ptr++) { if(*ptr == '\n' || *ptr == '\r') *ptr = '\0'; } + if(strlen(bot_realname) < 2) goto check_firstrun_bot_realname; + char bot_server[101]; + check_firstrun_bot_server: + printf("Server: [irc.onlinegamesnet.net] "); + ptr = fgets(bot_server, 100, stdin); + for(ptr = bot_server; *ptr; ptr++) { if(*ptr == '\n' || *ptr == '\r') *ptr = '\0'; } + if(*bot_server && strlen(bot_nick) < 5) goto check_firstrun_bot_server; + if(!*bot_server) + strcpy(bot_server, "irc.onlinegamesnet.net"); + int bot_port; + char bot_port_buf[7]; + printf("Port: [6667] "); + ptr = fgets(bot_port_buf, 6, stdin); + for(ptr = bot_port_buf; *ptr; ptr++) { if(*ptr == '\n' || *ptr == '\r') *ptr = '\0'; } + if(!*bot_port_buf) + bot_port = 6667; + else + bot_port = atoi(bot_port_buf); + int bot_ssl; + char bot_ssl_buf[5]; + check_firstrun_bot_ssl: + printf("SSL: [y/N] "); + ptr = fgets(bot_ssl_buf, 4, stdin); + for(ptr = bot_ssl_buf; *ptr; ptr++) { if(*ptr == '\n' || *ptr == '\r') *ptr = '\0'; } + if(!*bot_ssl_buf || tolower(*bot_ssl_buf) == 'n') + bot_ssl = 0; + else if(tolower(*bot_ssl_buf) == 'y') + bot_ssl = 1; + else + goto check_firstrun_bot_ssl; + char bot_pass[101]; + printf("Server Password: [] "); + ptr = fgets(bot_pass, 100, stdin); + for(ptr = bot_pass; *ptr; ptr++) { if(*ptr == '\n' || *ptr == '\r') *ptr = '\0'; } + int bot_maxchan; + char bot_maxchan_buf[5]; + printf("MaxChannel: [20] "); + ptr = fgets(bot_maxchan_buf, 5, stdin); + for(ptr = bot_maxchan_buf; *ptr; ptr++) { if(*ptr == '\n' || *ptr == '\r') *ptr = '\0'; } + if(*bot_maxchan_buf) + bot_maxchan = atoi(bot_maxchan_buf); + else + bot_maxchan = 20; + int bot_queue; + char bot_queue_buf[5]; + check_firstrun_bot_queue: + printf("Queue (prevents excess floods): [Y/n] "); + ptr = fgets(bot_queue_buf, 4, stdin); + for(ptr = bot_queue_buf; *ptr; ptr++) { if(*ptr == '\n' || *ptr == '\r') *ptr = '\0'; } + if(!*bot_queue_buf || tolower(*bot_queue_buf) == 'y') + bot_queue = 1; + else if(tolower(*bot_queue_buf) == 'n') + bot_queue = 0; + else + goto check_firstrun_bot_queue; + printf_mysql_query("INSERT INTO `bots` (`active`, `nick`, `server`, `port`, `pass`, `ssl`, `ident`, `realname`, `botclass`, `textbot`, `queue`, `defaulttrigger`, `max_channels`) VALUES ('1', '%s', '%s', '%d', '%s', '%d', '%s', '%s', '1', '1', '%d', '+', '%d')", escape_string(bot_nick), escape_string(bot_server), bot_port, escape_string(bot_pass), bot_ssl, escape_string(bot_ident), escape_string(bot_realname), bot_queue, bot_maxchan); + } +} + diff --git a/src/main.h b/src/main.h new file mode 100644 index 0000000..1f1d0f3 --- /dev/null +++ b/src/main.h @@ -0,0 +1,56 @@ +/* main.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _main_h +#define _main_h +#include "overall.h" + +struct ProcessState { + time_t start_time; + int running : 1; + int restart : 1; + int run_as_daemon : 1; + int daemonized : 1; + int loglevel : 8; + int loaded_config : 1; + int running_threads : 8; + + int argc; + char **argv; + + char config[MAXLEN]; + char pidfile[MAXLEN]; +}; + +#ifndef DND_FUNCTIONS + +extern struct ProcessState process_state; + +#ifdef HAVE_THREADS +extern pthread_mutex_t cache_sync; +extern pthread_mutex_t whohandler_sync, whohandler_mass_sync; + +/* MODULAR ACCESSIBLE */ int getCurrentThreadID(); +#endif + +/* MODULAR ACCESSIBLE */ time_t getStartTime(); +/* MODULAR ACCESSIBLE */ int getRunningThreads(); + +/* MODULAR ACCESSIBLE */ void restart_bot(int crash); +/* MODULAR ACCESSIBLE */ void stop_bot(); +/* MODULAR ACCESSIBLE */ void reload_config(); +#endif +#endif \ No newline at end of file diff --git a/src/memoryDebug.c b/src/memoryDebug.c new file mode 100644 index 0000000..d4e828f --- /dev/null +++ b/src/memoryDebug.c @@ -0,0 +1,215 @@ +/* memoryDebug.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#include "main.h" +#include "memoryDebug.h" +#include "memoryInfo.h" +#include "tools.h" + +#define FILE_NAME_LENGTH 256 +#define OUTPUT_FILE "leak_info.txt" + +#undef malloc +#undef calloc +#undef strdup +#undef free + +struct MemoryNode { +void *address; +unsigned int size; +char file_name[FILE_NAME_LENGTH]; +unsigned int line; +}; + +struct MemoryLeak { +struct MemoryNode mem_info; +struct MemoryLeak *next; +}; + +#ifdef HAVE_THREADS +static pthread_mutex_t synchronized; +#endif + +static struct MemoryLeak *ptr_start = NULL; + +static unsigned int own_allocated_memleaks = 0; + +static void add_mem_info(void * mem_ref, unsigned int size, const char *file, unsigned int line); +static void remove_mem_info(void * mem_ref); + +void *xmalloc(unsigned int size, const char *file, unsigned int line) { + void *ptr = malloc(size); + if (ptr != NULL) { + add_mem_info(ptr, size, file, line); + } + return ptr; +} + +void *xcalloc(unsigned int elements, unsigned int size, const char *file, unsigned int line) { + void *ptr = calloc(elements, size); + if(ptr != NULL) { + add_mem_info(ptr, (elements * size), file, line); + } + return ptr; +} + +char *xstrdup(const char *data, const char *file, unsigned int line) { + int len = strlen(data)+1; + char *ptr = malloc(len); + if(ptr != NULL) { + strcpy(ptr, data); + add_mem_info(ptr, len, file, line); + } + return ptr; +} + +void xfree(void *mem_ref) { + remove_mem_info(mem_ref); + free(mem_ref); +} + +static void add_mem_info(void *mem_ref, unsigned int size, const char *file, unsigned int line) { + #ifdef HAVE_THREADS + pthread_mutex_lock(&synchronized); + #endif + struct MemoryLeak *mem_leak_info = malloc(sizeof(*mem_leak_info)); + own_allocated_memleaks++; + mem_leak_info->mem_info.address = mem_ref; + mem_leak_info->mem_info.size = size; + strcpy(mem_leak_info->mem_info.file_name, file); + mem_leak_info->mem_info.line = line; + mem_leak_info->next = ptr_start; + ptr_start = mem_leak_info; + #ifdef HAVE_THREADS + pthread_mutex_unlock(&synchronized); + #endif +} + +static void remove_mem_info(void *mem_ref) { + #ifdef HAVE_THREADS + pthread_mutex_lock(&synchronized); + #endif + struct MemoryLeak *leak_info, *next, *prev = NULL; + for(leak_info = ptr_start; leak_info; leak_info = next) { + next = leak_info->next; + if (leak_info->mem_info.address == mem_ref) { + if(prev) + prev->next = next; + else + ptr_start = next; + own_allocated_memleaks--; + free(leak_info); + break; + } else + prev = leak_info; + } + #ifdef HAVE_THREADS + pthread_mutex_unlock(&synchronized); + #endif +} + +void initMemoryDebug() { + THREAD_MUTEX_INIT(synchronized); +} + +struct memoryInfoFiles *getMemoryInfoFiles() { + #ifdef HAVE_THREADS + pthread_mutex_lock(&synchronized); + #endif + struct MemoryLeak *leak_info; + struct memoryInfoFiles *list = NULL, *element; + for(leak_info = ptr_start; leak_info != NULL; leak_info = leak_info->next) { + for(element = list; element; element = element->next) { + if(!strcmp(leak_info->mem_info.file_name, element->filename)) { + break; + } + } + if(!element) { + element = malloc(sizeof(*element)); + element->filename = strdup(leak_info->mem_info.file_name); + element->allocations = 0; + element->allocated = 0; + element->next = list; + list = element; + } + element->allocations += 1; + element->allocated += leak_info->mem_info.size; + } + element = malloc(sizeof(*element)); + element->filename = strdup(__FILE__); + element->allocations = own_allocated_memleaks; + element->allocated = own_allocated_memleaks * sizeof(struct MemoryLeak); + element->next = list; + list = element; + #ifdef HAVE_THREADS + pthread_mutex_unlock(&synchronized); + #endif + return list; +} + +void freeMemoryInfoFiles(struct memoryInfoFiles *files) { + struct memoryInfoFiles *next; + for(;files;files = next) { + next = files->next; + free(files->filename); + free(files); + } +} + +struct memoryInfoLines *getMemoryInfoLines(const char *filename) { + #ifdef HAVE_THREADS + pthread_mutex_lock(&synchronized); + #endif + struct MemoryLeak *leak_info; + struct memoryInfoLines *list = NULL, *element; + for(leak_info = ptr_start; leak_info != NULL; leak_info = leak_info->next) { + if(stricmp(leak_info->mem_info.file_name, filename)) continue; + for(element = list; element; element = element->next) { + if(element->line == leak_info->mem_info.line && element->allocate == leak_info->mem_info.size) { + break; + } + } + if(!element) { + element = malloc(sizeof(*element)); + element->line = leak_info->mem_info.line; + element->allocations = 0; + element->allocate = leak_info->mem_info.size; + element->next = list; + list = element; + } + element->allocations++; + } + if(!stricmp(filename, __FILE__)) { + element = malloc(sizeof(*element)); + element->line = 0; + element->allocations = own_allocated_memleaks; + element->allocate = sizeof(struct MemoryLeak); + element->next = list; + list = element; + } + #ifdef HAVE_THREADS + pthread_mutex_unlock(&synchronized); + #endif + return list; +} + +void freeMemoryInfoLines(struct memoryInfoLines *lines) { + struct memoryInfoLines *next; + for(;lines;lines = next) { + next = lines->next; + free(lines); + } +} diff --git a/src/memoryDebug.h b/src/memoryDebug.h new file mode 100644 index 0000000..456ed2a --- /dev/null +++ b/src/memoryDebug.h @@ -0,0 +1,33 @@ +/* memoryDebug.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _memorydebug_h +#define _memorydebug_h + +#define malloc(size) xmalloc (size, __FILE__, __LINE__) +#define calloc(elements, size) xcalloc (elements, size, __FILE__, __LINE__) +#define strdup(data) xstrdup (data, __FILE__, __LINE__) +#define free(mem_ref) xfree(mem_ref) + +#ifndef DND_FUNCTIONS +/* MODULAR ACCESSIBLE */ void *xmalloc(unsigned int size, const char *file, unsigned int line); +/* MODULAR ACCESSIBLE */ void *xcalloc(unsigned int elements, unsigned int size, const char *file, unsigned int line); +/* MODULAR ACCESSIBLE */ char *xstrdup(const char *data, const char *file, unsigned int line); +/* MODULAR ACCESSIBLE */ void xfree(void *mem_ref); + +void initMemoryDebug(); +#endif +#endif diff --git a/src/memoryInfo.h b/src/memoryInfo.h new file mode 100644 index 0000000..4ed52fa --- /dev/null +++ b/src/memoryInfo.h @@ -0,0 +1,44 @@ +/* memoryInfo.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _memoryinfo_h +#define _memoryinfo_h + +struct memoryInfoFiles { + char *filename; + unsigned int allocations; + unsigned int allocated; + struct memoryInfoFiles *next; +}; + +#ifndef DND_FUNCTIONS +/* MODULAR ACCESSIBLE */ struct memoryInfoFiles *getMemoryInfoFiles(); +/* MODULAR ACCESSIBLE */ void freeMemoryInfoFiles(struct memoryInfoFiles *files); +#endif + +struct memoryInfoLines { + unsigned int line; + unsigned int allocations; + unsigned int allocate; + struct memoryInfoLines *next; +}; + +#ifndef DND_FUNCTIONS +/* MODULAR ACCESSIBLE */ struct memoryInfoLines *getMemoryInfoLines(const char *filename); +/* MODULAR ACCESSIBLE */ void freeMemoryInfoLines(struct memoryInfoLines *lines); +#endif + +#endif diff --git a/src/modcmd.c b/src/modcmd.c new file mode 100644 index 0000000..8ec2517 --- /dev/null +++ b/src/modcmd.c @@ -0,0 +1,1093 @@ +/* modcmd.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "modcmd.h" +#include "IRCEvents.h" +#include "IRCParser.h" +#include "ClientSocket.h" +#include "UserNode.h" +#include "ChanNode.h" +#include "ChanUser.h" +#include "WHOHandler.h" +#include "lang.h" +#include "mysqlConn.h" +#include "DBHelper.h" +#include "EventLogger.h" +#include "tools.h" +#include "log.h" + +struct trigger_callback { + int botid; + int module_id; + trigger_callback_t *func; + + struct trigger_callback *next; +}; + +struct cmd_bot_alias { + int botid; + char *alias; + + struct cmd_bot_alias *next; +}; + +struct command_check_user_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + struct ChanNode *chan, *sent_chan; + char **argv; + int argc; + char *message, *args_buffer; + struct cmd_binding *cbind; +}; + +static struct cmd_binding **cmd_binds; +static struct cmd_function *cmd_functions = NULL; +static struct trigger_callback *trigger_callbacks = NULL; +static struct cmd_bot_alias *bot_aliases = NULL; +static int total_triggered = 0; +int statistics_commands = 0; + +static const struct default_language_entry msgtab[] = { + {"MODCMD_LESS_PARAM_COUNT", "This command requires more parameters."}, + {"MODCMD_CHAN_REQUIRED", "You must provide the name of a channel that exists and the bot is on."}, + {"MODCMD_AUTH_REQUIRED", "You need to be authenticated with AuthServ to use this command."}, + {"MODCMD_CHAN_SUSPENDED", "This channel is currently suspended."}, + {"MODCMD_PRIVILEGED", "$b%s$b is a privileged command."}, /* {ARGS: "god"} */ + {"MODCMD_PUBCMD", "Public commands in $b%s$b are restricted."}, /* {ARGS: "#TestChan"} */ + {"MODCMD_ACCESS_DENIED", "Access denied."}, + {"MODCMD_SUBCOMMANDS", "Subcommands of %s: %s"}, /* {ARGS: "bot", "ADD, DEL, EDIT"} */ + {"MODCMD_CROSSCHAN", "You must be in %s (or on its userlist) to use this command."}, /* {ARGS: "#TestChan"} */ + {"MODCMD_UNKNOWN", "$b%s$b is an unknown command."}, /* {ARGS: "bla"} */ + {NULL, NULL} +}; + +static int get_binds_index(char first_char) { + if(tolower(first_char) >= 'a' && tolower(first_char) <= 'z') { + return tolower(first_char) - 'a'; + } + return 26; +} + +struct ClientSocket* get_botwise_prefered_bot(int botid, int clientid) { + struct ClientSocket *client, *lowbot = NULL; + for(client = getBots(SOCKET_FLAG_READY, NULL); client; client = getBots(SOCKET_FLAG_READY, client)) { + if(client->botid == botid && (botid || clientid == client->clientid)) { + if((client->flags & SOCKET_FLAG_PREFERRED)) + return client; + else + lowbot = client; + } + } + return lowbot; +} + +static char* get_channel_trigger(int botid, int clientid, struct ChanNode *chan) { + struct trigger_cache *trigger; + for(trigger = chan->trigger; trigger; trigger = trigger->next) { + if(trigger->botid == botid && (botid || trigger->clientid == clientid)) + return trigger->trigger; + } + struct trigger_callback *cb; + for(cb = trigger_callbacks; cb; cb = cb->next) { + if(cb->botid == botid) + break; + } + char triggerStr[TRIGGERLEN]; + if(cb) + cb->func(clientid, chan, triggerStr); + else + triggerStr[0] = '\0'; + trigger = malloc(sizeof(*trigger)); + if (!trigger) { + printf_log("main", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return 0; + } + trigger->botid = botid; + trigger->clientid = clientid; + trigger->trigger = (triggerStr[0] ? strdup(triggerStr) : NULL); + trigger->next = chan->trigger; + chan->trigger = trigger; + return trigger->trigger; +} + +static void handle_command_async(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct ChanNode *sent_chan, struct cmd_binding *cbind, char **argv, int argc); + +static USERAUTH_CALLBACK(command_checked_auth) { + struct command_check_user_cache *cache = data; + if(user) + handle_command_async(cache->client, cache->textclient, user, cache->chan, cache->sent_chan, cache->cbind, cache->argv, cache->argc); + free(cache->message); + if(cache->args_buffer) + free(cache->args_buffer); + free(cache->argv); + free(cache); +} + +static CMD_BIND(modcmd_linker) { + //fake command for subcommands + //just empty +} + +static struct cmd_binding *modcmd_linker_command(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct cmd_binding *cbind, int bind_index, char **args_ptr) { + //links subcommands + char command[MAXLEN]; + struct cmd_binding *parent_bind = cbind; + char *args = *args_ptr; + if(args) { + char *subcmd = args; + args = strstr(args, " "); + if(args) { + *args = '\0'; + args++; + } + sprintf(command, "%s %s", parent_bind->cmd, subcmd); + for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) { + if(cbind->botid == client->botid && (cbind->botid || cbind->clientid == client->clientid) && stricmp(cbind->cmd, command) == 0) + break; + } + *args_ptr = args; + if(cbind && cbind->func->func == modcmd_linker) { + return modcmd_linker_command(client, textclient, user, cbind, bind_index, args_ptr); + } + return cbind; + } else { + //list all sub commands + int commandlen = sprintf(command, "%s ", parent_bind->cmd); + char subcommands[MAXLEN]; + int subcompos = 0; + for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) { + if(cbind->botid == client->botid && (cbind->botid || cbind->clientid == client->clientid) && stricmplen(cbind->cmd, command, commandlen) == 0) { + if(strstr(cbind->cmd + commandlen, " ")) continue; //ignore if there is another space in the command (sub-sub-command :D) + subcompos += sprintf(subcommands + subcompos, (subcompos ? ", %s" : "%s"), cbind->cmd + commandlen); + } + } + reply(textclient, user, "MODCMD_SUBCOMMANDS", parent_bind->cmd, (subcompos ? subcommands : "none")); + return NULL; + } +} + +static struct cmd_binding *modcmd_sub_linker_command(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct cmd_binding *cbind, int bind_index, char **args_ptr) { + //links subcommands + char command[MAXLEN]; + struct cmd_binding *parent_bind = cbind; + char *args = *args_ptr; + if(args) { + char *subcmd = args; + args = strchr(args, ' '); + if(args) { + *args = '\0'; + args++; + } + sprintf(command, "%s %s", parent_bind->cmd, subcmd); + if(args) + args[-1] = ' '; + for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) { + if(cbind->botid == client->botid && (cbind->botid || cbind->clientid == client->clientid) && stricmp(cbind->cmd, command) == 0) + break; + } + if(cbind) { + *args_ptr = args; + if(cbind->func->func == modcmd_linker) + parent_bind = modcmd_linker_command(client, textclient, user, cbind, bind_index, args_ptr); + else + parent_bind = cbind; + } + } + return parent_bind; +} + +static void handle_command(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, char *message) { + struct ChanNode *sent_chan = chan; + if(message[0] == '#') { + char *chanName = message; + message = strstr(message, " "); + if(!message) return; + *message = '\0'; + message++; + struct ChanNode *chan2 = getChanByName(chanName); + if(chan2) + chan = chan2; + } + message = strdup(message); + int bind_index = get_binds_index(message[0]); + char *args = strchr(message, ' '); + char *args_buffer = NULL; //we need this to save a possible pointer to a allocation we need to free + if(args) { + *args = '\0'; + args++; + } + struct cmd_binding *cbind; + int found_cmd = 0; + for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) { + if(cbind->botid == client->botid && (cbind->botid || cbind->clientid == client->clientid) && stricmp(cbind->cmd, message) == 0) { + found_cmd = 1; + //get a text bot + struct ClientSocket *textclient = get_botwise_prefered_bot(client->botid, (client->botid == 0 ? client->clientid : 0)); + if(cbind->func->func == modcmd_linker) { + cbind = modcmd_linker_command(client, textclient, user, cbind, bind_index, &args); + if(cbind == NULL) break; + } else if(cbind->flags & CMDFLAG_SUB_LINKER) + cbind = modcmd_sub_linker_command(client, textclient, user, cbind, bind_index, &args); + statistics_commands++; + total_triggered++; + cbind->triggered++; + if((BIND_FLAGS(cbind) & CMDFLAG_FUNCMD)) { + if(!sent_chan) + break; + chan = sent_chan; + } + //parse the arguments... + char *arga[MAXNUMPARAMS]; + char **argv; + int argc = 0; + int escape = 0; + int offset = 0; + if(args) { + while(*args) { + //skip leading spaces + while (*args == ' ') + *args++ = 0; + arga[argc++] = args; + if (argc >= MAXNUMPARAMS) + break; + while ((escape || *args != ' ') && *args) { + if((BIND_FLAGS(cbind) & CMDFLAG_ESCAPE_ARGS) && *args == '\\') { + escape = 1; + offset++; + } else if(escape) + escape = 0; + if(!escape && offset) { + args[0 - offset] = args[0]; + } + args++; + + } + if(offset) { + args[0-offset] = '\0'; + offset = 0; + } + } + } + argv = arga; + if(cbind->paramcount) { + //userdefined parameters... + args_buffer = malloc(MAXLEN * 2 * sizeof(*args_buffer)); + int args_pos = 0; + char *uargs[MAXNUMPARAMS]; + int uargc = 0; + char *b, *c; + int i; + int allargs, argi; + for(i = 0; i < cbind->paramcount; i++) { + b = cbind->parameters[i]; + if(b[0] == '%') { + b++; + c = b; + while(*c && *c != ' ') { + if(*c == '|') break; + c++; + } + if(!*c || *c == ' ') c = NULL; + else { + *c = '\0'; + c++; + } + if(b[strlen(b)-1] == '-') { + allargs = strlen(b)-1; + b[allargs] = '\0'; + argi = atoi(b); + b[allargs] = '-'; + allargs = 1; + } else if(b[strlen(b)-1] == '+') { + allargs = strlen(b)-1; + b[allargs] = '\0'; + argi = atoi(b); + b[allargs] = '+'; + allargs = 2; + } else { + allargs = 0; + argi = atoi(b); + } + if(argi > 0) { + if(argi <= argc) { + uargs[uargc++] = args_buffer + args_pos; + if(allargs == 0) { + args_pos += sprintf(args_buffer + args_pos, "%s", argv[argi-1]) + 1; + } else if(allargs == 1) { + args_pos += sprintf(args_buffer + args_pos, "%s", argv[argi-1]) + 1; + for(argi++; argi <= argc; argi++) { + uargs[uargc++] = args_buffer + args_pos; + args_pos += sprintf(args_buffer + args_pos, "%s", argv[argi-1]) + 1; + } + } else if(allargs == 2) { + for(;argi <= argc; argi++) { + args_pos += sprintf(args_buffer + args_pos, (allargs ? "%s" : " %s"), argv[argi-1]); + allargs = 0; + } + args_pos++; + } + } else if((BIND_FLAGS(cbind) & CMDFLAG_EMPTY_ARGS)) { + uargs[uargc++] = args_buffer + args_pos; + args_buffer[args_pos++] = '\0'; + } else if(c) { + uargs[uargc++] = args_buffer + args_pos; + args_pos += sprintf(args_buffer + args_pos, "%s", c) + 1; + } + } else if(!strcmp(b, "c")) { + uargs[uargc++] = args_buffer + args_pos; + args_pos += sprintf(args_buffer + args_pos, "%s", (chan ? chan->name : "")) + 1; + } else if(!strcmp(b, "n")) { + uargs[uargc++] = args_buffer + args_pos; + args_pos += sprintf(args_buffer + args_pos, "%s", user->nick) + 1; + } + if(c) c[-1] = '|'; //reset \0 to | + } else { + uargs[uargc++] = args_buffer + args_pos; + args_pos += sprintf(args_buffer + args_pos, "%s", b) + 1; + } + } + argv = uargs; + argc = uargc; + } + if(argc != 0 && argv[0][0] == '#' && !(BIND_FLAGS(cbind) & CMDFLAG_CHAN_PARAM)) { + struct ChanNode *chan2 = getChanByName(argv[0]); + if(chan2) { + argv += 1; + argc -= 1; + chan = chan2; + } + } + if(argc < cbind->func->paramcount) { + reply(textclient, user, "MODCMD_LESS_PARAM_COUNT"); + break; + } + if((BIND_FLAGS(cbind) & CMDFLAG_REQUIRE_CHAN) && !chan) { + reply(textclient, user, "MODCMD_CHAN_REQUIRED"); + break; + } + if(((BIND_FLAGS(cbind) & CMDFLAG_CHECK_AUTH) || (chan && chan != sent_chan && !isUserOnChan(user, chan))) && !(user->flags & USERFLAG_ISAUTHED)) { + //check auth... + struct command_check_user_cache *data = malloc(sizeof(*data)); + char **temp_argv = malloc(argc*sizeof(*temp_argv)); + if (!data || !temp_argv) { + printf_log("main", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + break; + } + memcpy(temp_argv, argv, argc*sizeof(*temp_argv)); + data->argv = temp_argv; + data->argc = argc; + data->client = client; + data->user = user; + data->chan = chan; + data->sent_chan = sent_chan; + data->message = message; + data->args_buffer = args_buffer; + data->cbind = cbind; + data->textclient = textclient; + get_userauth(user, 0, command_checked_auth, data); + return; + } else + handle_command_async(client, textclient, user, chan, sent_chan, cbind, argv, argc); + break; + } + } + if(!found_cmd && !sent_chan && !(client->flags & SOCKET_FLAG_SILENT)) + reply(get_botwise_prefered_bot(client->botid, (client->botid == 0 ? client->clientid : 0)), user, "MODCMD_UNKNOWN", message); + free(message); + if(args_buffer) + free(args_buffer); +} + +static void handle_command_async(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct ChanNode *sent_chan, struct cmd_binding *cbind, char **argv, int argc) { + MYSQL_RES *res; + MYSQL_ROW row; + int uaccess; + char requested_uaccess = 0; + int eventflags = (BIND_FLAGS(cbind) & (CMDFLAG_LOG | CMDFLAG_OPLOG)); + if((BIND_FLAGS(cbind) & CMDFLAG_REQUIRE_AUTH) && !(user->flags & USERFLAG_ISAUTHED)) { + reply(textclient, user, "MODCMD_AUTH_REQUIRED"); + return; + } + if(chan && sent_chan != chan && (BIND_FLAGS(cbind) & CMDFLAG_NO_CROSSCHAN) && !isUserOnChan(user, chan)) { + char user_in_chan = 0; + if((user->flags & USERFLAG_ISAUTHED)) { + //maybe there's another user authed to user->auth on the channel... + struct ChanUser *cchanuser; + for(cchanuser = getChannelUsers(chan, NULL); cchanuser; cchanuser = getChannelUsers(chan, cchanuser)) { + if((cchanuser->user->flags & USERFLAG_ISAUTHED) && !stricmp(user->auth, cchanuser->user->auth)) { + user_in_chan = 1; + break; + } + } + } + if(!user_in_chan) { + //check if we are allowed to execute commands in this channel + requested_uaccess = 1; + uaccess = getChannelAccess(user, chan); + if(!uaccess) { + if(isGodMode(user)) { + eventflags |= CMDFLAG_OPLOG; + } else { + reply(textclient, user, "MODCMD_CROSSCHAN", chan->name); + return; + } + } + } + } + if(sent_chan && sent_chan != chan) { + //check pubcmd of this channel + printf_mysql_query("SELECT `channel_pubcmd` FROM `channels` WHERE `channel_name` = '%s'", escape_string(sent_chan->name)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + int saccess = getChannelAccess(user, sent_chan); + if(row[0] && saccess < atoi(row[0]) && !isGodMode(user)) { //NOTE: HARDCODED DEFAULT: pubcmd = 0 + reply(textclient, user, "MODCMD_PUBCMD", sent_chan->name); + return; + } + } + } + int global_access = ((cbind->flags & CMDFLAG_OVERRIDE_GLOBAL_ACCESS) ? cbind->global_access : cbind->func->global_access); + if(global_access > 0) { + int user_global_access = 0; + printf_mysql_query("SELECT `user_access` FROM `users` WHERE `user_user` = '%s'", escape_string(user->auth)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + user_global_access = atoi(row[0]); + } + if(user_global_access < global_access) { + if(!user_global_access) + reply(textclient, user, "MODCMD_PRIVILEGED", cbind->cmd); + else + reply(textclient, user, "MODCMD_ACCESS_DENIED"); + return; + } + } + if((BIND_FLAGS(cbind) & CMDFLAG_REGISTERED_CHAN)) { + MYSQL_ROW defaults = NULL; + char access_list[256]; + int access_pos = 0; + int access_count = 0; + int minaccess = 0; + char *str_a, *str_b = cbind->func->channel_access, *str_c; + if(cbind->flags & CMDFLAG_OVERRIDE_CHANNEL_ACCESS) + str_b = cbind->channel_access; + access_list[0] = '\0'; + if(str_b) { + struct ChanUser *chanuser = getChanUser(user, chan); + str_c = strdup(str_b); + str_b = str_c; + while((str_a = str_b)) { + str_b = strstr(str_a, ","); + if(str_b) { + *str_b = '\0'; + str_b++; + } + if(*str_a == '@' || *str_a == '+') { + //privs can override this access requirement + int priv = 0; + if(*str_a == '@') priv = CHANUSERFLAG_OPPED; + else if(*str_a == '%') priv = CHANUSERFLAG_HALFOPPED; + else if(*str_a == '+') priv = CHANUSERFLAG_VOICED; + if(chanuser && (chanuser->flags & priv)) continue; + str_a++; + } + if(*str_a == '#') { + str_a++; + access_pos += sprintf(access_list+access_pos, ", `%s`", str_a); + access_count++; + } else { + if(atoi(str_a) > minaccess) + minaccess = atoi(str_a); + } + } + free(str_c); + } + if(!(chan->flags & CHANFLAG_REQUESTED_CHANINFO) || (sent_chan && sent_chan == chan) || access_count || minaccess) { + printf_mysql_query("SELECT `channel_id`, `channel_pubcmd` %s FROM `channels` WHERE `channel_name` = '%s'", access_list, escape_string(chan->name)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + chan->flags |= CHANFLAG_CHAN_REGISTERED; + chan->channel_id = atoi(row[0]); + if((sent_chan && sent_chan == chan) || access_count || minaccess) { + if(!requested_uaccess) uaccess = getChannelAccess(user, chan); + if(uaccess < minaccess && isGodMode(user)) { + eventflags |= CMDFLAG_OPLOG; + } else if(uaccess < minaccess) { + //ACCESS DENIED + reply(textclient, user, "MODCMD_ACCESS_DENIED"); + return; + } + if(!row[1] && !defaults) { + printf_mysql_query("SELECT `channel_id`, `channel_pubcmd` %s FROM `channels` WHERE `channel_name` = 'defaults'", access_list); + defaults = mysql_fetch_row(mysql_use()); + } + if(sent_chan && (sent_chan == chan) && uaccess < (row[1] ? atoi(row[1]) : atoi(defaults[1]))) { + if(isGodMode(user)) { + eventflags |= CMDFLAG_OPLOG; + } else { + //PUBCMD + reply(textclient, user, "MODCMD_PUBCMD", chan->name); + return; + } + } + int i; + for(i = 0; i < access_count; i++) { + if(!row[2+i] && !defaults) { + printf_mysql_query("SELECT `channel_id`, `channel_pubcmd` %s FROM `channels` WHERE `channel_name` = 'defaults'", access_list); + defaults = mysql_fetch_row(mysql_use()); + } + if(uaccess < (row[2+i] ? atoi(row[2+i]) : atoi(defaults[2+i]))) { + if(isGodMode(user)) { + eventflags |= CMDFLAG_OPLOG; + } else { + reply(textclient, user, "MODCMD_ACCESS_DENIED"); + return; + } + } + } + } + } + chan->flags |= CHANFLAG_REQUESTED_CHANINFO; + } + if(!(chan->flags & CHANFLAG_CHAN_REGISTERED)) { + reply(textclient, user, "MODCMD_CHAN_REQUIRED"); + return; + } + printf_mysql_query("SELECT `botid`, `suspended` FROM `bot_channels` LEFT JOIN `bots` ON `bot_channels`.`botid` = `bots`.`id` WHERE `chanid` = '%d' AND `botclass` = '%d'", chan->channel_id, client->botid); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) == NULL) { + reply(textclient, user, "MODCMD_CHAN_REQUIRED"); + return; + } else if(!strcmp(row[1], "1")) { + reply(textclient, user, "MODCMD_CHAN_SUSPENDED"); + return; + } + } + if((BIND_FLAGS(cbind) & CMDFLAG_REQUIRE_GOD) && !isGodMode(user)) { + reply(textclient, user, "MODCMD_PRIVILEGED", cbind->cmd); + return; + } + struct Event *event = createEvent(client, user, chan, cbind, argv, argc, eventflags); + cbind->func->func(client, textclient, user, chan, argv, argc, event); +} + +static void got_chanmsg(struct UserNode *user, struct ChanNode *chan, char *message) { + fd_set fds, fds2; + char *trigger; + struct ClientSocket *client; + FD_ZERO(&fds); + FD_ZERO(&fds2); + got_chanmsg_loop1: + for(client = getBots(SOCKET_FLAG_READY, NULL); client; client = getBots(SOCKET_FLAG_READY, client)) { + if(isUserOnChan(client->user, chan) && (client->flags & SOCKET_FLAG_PREFERRED) && ((client->botid == 0 && !FD_ISSET(client->clientid, &fds)) || (client->botid && !FD_ISSET(client->botid, &fds2)))) { + FD_SET(client->clientid, &fds); + FD_SET(client->botid, &fds2); + trigger = get_channel_trigger(client->botid, client->clientid, chan); + if(trigger && stricmplen(message, trigger, strlen(trigger)) == 0) { + handle_command(client, user, chan, message + strlen(trigger)); + goto got_chanmsg_loop1; //Thats really really bad, i know... But we can't count on the "getBots" list anymore after executing a command + } + } + } + got_chanmsg_loop2: + for(client = getBots(SOCKET_FLAG_READY, NULL); client; client = getBots(SOCKET_FLAG_READY, client)) { + if(isUserOnChan(client->user, chan) && ((client->botid == 0 && !FD_ISSET(client->clientid, &fds)) || (client->botid && !FD_ISSET(client->botid, &fds2)))) { + FD_SET(client->clientid, &fds); + FD_SET(client->botid, &fds2); + trigger = get_channel_trigger(client->botid, client->clientid, chan); + if(trigger && stricmplen(message, trigger, strlen(trigger)) == 0) { + handle_command(client, user, chan, message + strlen(trigger)); + goto got_chanmsg_loop2; //Thats really really bad, i know... But we can't count on the "getBots" list anymore after executing a command + } + } + } +} + +static void got_privmsg(struct UserNode *user, struct UserNode *target, char *message) { + struct ClientSocket *client; + for(client = getBots(SOCKET_FLAG_READY, NULL); client; client = getBots(SOCKET_FLAG_READY, client)) { + if(client->user == target) { + handle_command(client, user, NULL, message); + } + } +} + +int register_command(int botid, char *name, int module_id, cmd_bind_t *func, int paramcount, char *channel_access, int global_access, unsigned int flags) { + struct cmd_function *cmdfunc; + for(cmdfunc = cmd_functions; cmdfunc; cmdfunc = cmdfunc->next) { + if((cmdfunc->botid == botid || cmdfunc->botid == 0) && !stricmp(cmdfunc->name, name)) + return 0; + } + cmdfunc = malloc(sizeof(*cmdfunc)); + if (!cmdfunc) { + printf_log("main", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return 0; + } + cmdfunc->botid = botid; + cmdfunc->name = strdup(name); + cmdfunc->module_id = module_id; + cmdfunc->func = func; + cmdfunc->flags = flags; + cmdfunc->paramcount = paramcount; + cmdfunc->channel_access = channel_access; + cmdfunc->global_access = global_access; + cmdfunc->next = cmd_functions; + cmd_functions = cmdfunc; + return 1; +} + +int set_trigger_callback(int botid, int module_id, trigger_callback_t *func) { + static struct trigger_callback *cb = NULL; + for(cb = trigger_callbacks; cb; cb = cb->next) { + if(cb->botid == botid) + break; + } + if(!cb) { + cb = malloc(sizeof(*cb)); + if (!cb) { + printf_log("main", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return 0; + } + cb->botid = botid; + cb->next = trigger_callbacks; + trigger_callbacks = cb; + } + cb->module_id = module_id; + cb->func = func; + return 1; +} + +int flush_trigger_cache(int botid, int clientid) { + struct ChanNode *chan; + struct trigger_cache *trigger, *last; + for(chan = getAllChans(NULL); chan; chan = getAllChans(chan)) { + last = NULL; + for(trigger = chan->trigger; trigger; trigger = trigger->next) { + if(trigger->botid == botid && (botid || trigger->clientid == clientid)) { + if(last) + last->next = trigger->next; + else + chan->trigger = trigger->next; + free(trigger->trigger); + free(trigger); + break; + } else + last = trigger; + } + } + return 1; +} + +int changeBotwiseChannelTrigger(int botid, int clientid, struct ChanNode *chan, char *new_trigger) { + struct trigger_cache *trigger; + for(trigger = chan->trigger; trigger; trigger = trigger->next) { + if(trigger->botid == botid && (botid || trigger->clientid == clientid)) { + free(trigger->trigger); + trigger->trigger = strdup(new_trigger); + return 1; + } + } + return 0; +} + +int bind_botwise_cmd_to_function(int botid, int clientid, char *cmd, struct cmd_function *func) { + int bind_index = get_binds_index(cmd[0]); + struct cmd_binding *cbind; + for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) { + if(((botid && cbind->botid == botid) || (botid == 0 && clientid == cbind->clientid)) && !stricmp(cbind->cmd, cmd)) + return 0; + } + cbind = malloc(sizeof(*cbind)); + if (!cbind) { + printf_log("main", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return 0; + } + cbind->botid = botid; + cbind->clientid = clientid; + cbind->cmd = strdup(cmd); + cbind->func = func; + cbind->paramcount = 0; + cbind->global_access = 0; + cbind->channel_access = NULL; + cbind->flags = 0; + cbind->triggered = 0; + cbind->next = cmd_binds[bind_index]; + cmd_binds[bind_index] = cbind; + return 1; +} + +int bind_botwise_cmd_to_command(int botid, int clientid, char *cmd, char *func) { + struct cmd_function *cmdfunc; + int fbotid = botid; + char *c; + if((c = strstr(func, "."))) { + *c = '\0'; + struct cmd_bot_alias *botalias; + for(botalias = bot_aliases; botalias; botalias = botalias->next) { + if(!stricmp(botalias->alias, func)) { + fbotid = botalias->botid; + break; + } + } + *c = '.'; + func = c+1; + } + for(cmdfunc = cmd_functions; cmdfunc; cmdfunc = cmdfunc->next) { + if((cmdfunc->botid == fbotid || cmdfunc->botid == 0) && !stricmp(cmdfunc->name, func)) + break; + } + if(!cmdfunc) return 0; + int bind_index = get_binds_index(cmd[0]); + struct cmd_binding *cbind; + for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) { + if(((botid && cbind->botid == botid) || (botid == 0 && clientid == cbind->clientid)) && !stricmp(cbind->cmd, cmd)) + return 0; + } + cbind = malloc(sizeof(*cbind)); + if (!cbind) { + printf_log("main", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return 0; + } + cbind->botid = botid; + cbind->clientid = clientid; + cbind->cmd = strdup(cmd); + cbind->func = cmdfunc; + cbind->next = cmd_binds[bind_index]; + cbind->paramcount = 0; + cbind->global_access = 0; + cbind->channel_access = NULL; + cbind->flags = 0; + cbind->triggered = 0; + cmd_binds[bind_index] = cbind; + return 1; +} + +int unbind_botwise_cmd(int botid, int clientid, char *cmd) { + int bind_index = get_binds_index(cmd[0]); + struct cmd_binding *cbind, *last = NULL; + for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) { + if(cbind->botid == botid && (botid || clientid == cbind->clientid) && !stricmp(cbind->cmd, cmd)) { + if(last) + last->next = cbind->next; + else + cmd_binds[bind_index] = cbind->next; + free(cbind->cmd); + if(cbind->paramcount) { + int i; + for(i = 0; i < cbind->paramcount; i++) + free(cbind->parameters[i]); + } + if(cbind->channel_access) + free(cbind->channel_access); + free(cbind); + return 1; + } else + last = cbind; + } + return 0; +} + +int unbind_botwise_allcmd(int botid, int clientid) { + int i; + for(i = 0; i < 27; i++) { + struct cmd_binding *cbind, *next, *last = NULL; + for(cbind = cmd_binds[i]; cbind; cbind = next) { + next = cbind->next; + if((botid == 0 && clientid == cbind->clientid) || (botid && botid == cbind->botid)) { + if(last) + last->next = cbind->next; + else + cmd_binds[i] = cbind->next; + free(cbind->cmd); + if(cbind->paramcount) { + int j; + for(j = 0; j < cbind->paramcount; j++) + free(cbind->parameters[j]); + } + if(cbind->channel_access) + free(cbind->channel_access); + free(cbind); + } else + last = cbind; + } + } + return 1; +} + +struct cmd_function *find_cmd_function(int botid, char *name) { + struct cmd_function *cmdfunc; + char *c; + if((c = strstr(name, "."))) { + *c = '\0'; + struct cmd_bot_alias *botalias; + for(botalias = bot_aliases; botalias; botalias = botalias->next) { + if(!stricmp(botalias->alias, name)) { + botid = botalias->botid; + break; + } + } + *c = '.'; + name = c+1; + } + for(cmdfunc = cmd_functions; cmdfunc; cmdfunc = cmdfunc->next) { + if((cmdfunc->botid == botid || cmdfunc->botid == 0) && stricmp(cmdfunc->name, name) == 0) + break; + } + return cmdfunc; +} + +void init_modcmd() { + cmd_binds = calloc(27, sizeof(*cmd_binds)); + bind_chanmsg(got_chanmsg, 0); + bind_privmsg(got_privmsg, 0); + register_default_language_table(msgtab); + register_command(0, "linker", 0, modcmd_linker, 0, 0, 0, 0); //fake command for subcommands +} + +void free_modcmd() { + int i; + for(i = 0; i < 27; i++) { + struct cmd_binding *cbind, *next; + for(cbind = cmd_binds[i]; cbind; cbind = next) { + next = cbind->next; + free(cbind->cmd); + if(cbind->paramcount) { + int j; + for(j = 0; j < cbind->paramcount; j++) + free(cbind->parameters[j]); + } + if(cbind->channel_access) + free(cbind->channel_access); + free(cbind); + } + } + free(cmd_binds); + struct cmd_function *cmdfunct, *next; + for(cmdfunct = cmd_functions; cmdfunct; cmdfunct = next) { + next = cmdfunct->next; + free(cmdfunct->name); + free(cmdfunct); + } + struct trigger_callback *cb, *next_cb; + for(cb = trigger_callbacks; cb; cb = next_cb) { + next_cb = cb->next; + free(next_cb); + } + struct cmd_bot_alias *botalias, *next_botalias; + for(botalias = bot_aliases; botalias; botalias = next_botalias) { + next_botalias = botalias->next; + free(botalias->alias); + free(botalias); + } + cmd_functions = NULL; + trigger_callbacks = NULL; + bot_aliases = NULL; +} + +void bind_botwise_set_parameters(int botid, int clientid, char *cmd, char *parameters) { + int bind_index = get_binds_index(cmd[0]); + struct cmd_binding *cbind; + for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) { + if(cbind->botid == botid && (botid || clientid == cbind->clientid) && !stricmp(cbind->cmd, cmd)) { + if(cbind->paramcount) { + int i; + for(i = 0; i < cbind->paramcount; i++) + free(cbind->parameters[i]); + cbind->paramcount = 0; + } + if(parameters) { + char *a, *b = parameters; + do { + a = strstr(b, " "); + if(a) *a = '\0'; + cbind->parameters[cbind->paramcount++] = strdup(b); + if(a) b = a+1; + } while(a); + } + return; + } + } +} + +void bind_botwise_set_global_access(int botid, int clientid, char *cmd, int gaccess) { + int bind_index = get_binds_index(cmd[0]); + struct cmd_binding *cbind; + for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) { + if(cbind->botid == botid && (botid || clientid == cbind->clientid) && !stricmp(cbind->cmd, cmd)) { + if(gaccess > -1) { + cbind->global_access = gaccess; + cbind->flags |= CMDFLAG_OVERRIDE_GLOBAL_ACCESS; + } else { + cbind->flags &= ~CMDFLAG_OVERRIDE_GLOBAL_ACCESS; + } + return; + } + } +} + +void bind_botwise_set_channel_access(int botid, int clientid, char *cmd, char *chanaccess) { + int bind_index = get_binds_index(cmd[0]); + struct cmd_binding *cbind; + for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) { + if(cbind->botid == botid && (botid || clientid == cbind->clientid) && !stricmp(cbind->cmd, cmd)) { + if(cbind->channel_access) + free(cbind->channel_access); + if(chanaccess) { + cbind->channel_access = strdup(chanaccess); + cbind->flags |= CMDFLAG_OVERRIDE_CHANNEL_ACCESS; + } else { + cbind->channel_access = NULL; + cbind->flags &= ~CMDFLAG_OVERRIDE_CHANNEL_ACCESS; + } + return; + } + } +} + +void bind_botwise_set_bind_flags(int botid, int clientid, char *cmd, unsigned int flags) { + int bind_index = get_binds_index(cmd[0]); + struct cmd_binding *cbind; + for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) { + if(cbind->botid == botid && (botid || clientid == cbind->clientid) && !stricmp(cbind->cmd, cmd)) { + cbind->flags |= flags; + return; + } + } +} + +struct cmd_binding *find_botwise_cmd_binding(int botid, int clientid, char *cmd) { + int bind_index = get_binds_index(cmd[0]); + struct cmd_binding *cbind; + for(cbind = cmd_binds[bind_index]; cbind; cbind = cbind->next) { + if(cbind->botid == botid && (botid || clientid == cbind->clientid) && !stricmp(cbind->cmd, cmd)) { + return cbind; + } + } + return NULL; +} + +void bind_botwise_unbound_required_functions(int botid, int clientid) { + struct cmd_function *cmdfunc; + int i, found; + struct cmd_binding *cbind; + for(cmdfunc = cmd_functions; cmdfunc; cmdfunc = cmdfunc->next) { + if((cmdfunc->flags & CMDFLAG_REQUIRED)) { + found = 0; + for(i = 0; i < 27; i++) { + for(cbind = cmd_binds[i]; cbind; cbind = cbind->next) { + if(cbind->botid == botid && (botid || clientid == cbind->clientid) && cbind->func == cmdfunc) { + found = 1; + break; + } + } + if(found) + break; + } + if(!found && bind_botwise_cmd_to_function(botid, clientid, cmdfunc->name, cmdfunc)) { + cbind = find_botwise_cmd_binding(botid, clientid, cmdfunc->name); + cbind->flags |= CMDFLAG_TEMPONARY_BIND; + } + } + } +} + +void register_command_alias(int botid, char *alias) { + struct cmd_bot_alias *botalias; + for(botalias = bot_aliases; botalias; botalias = botalias->next) { + if(!stricmp(botalias->alias, alias)) + return; + } + botalias = malloc(sizeof(*botalias)); + if (!botalias) { + printf_log("main", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + botalias->botid = botid; + botalias->alias = strdup(alias); + botalias->next = bot_aliases; + bot_aliases = botalias; +} + +struct cmd_binding *getAllBinds(struct cmd_binding *last) { + int bind_index; + if(last) { + if(last->next) + return last->next; + bind_index = get_binds_index(last->cmd[0]) + 1; + if(bind_index > 26) + return NULL; + } else + bind_index = 0; + do { + if(cmd_binds[bind_index]) + return cmd_binds[bind_index]; + bind_index++; + } while(bind_index < 27); + return NULL; +} + +void unregister_module_commands(int module_id) { + struct cmd_function *cmdfunct, *nextfunct, *prevfunct = NULL; + int i; + for(cmdfunct = cmd_functions; cmdfunct; cmdfunct = nextfunct) { + nextfunct = cmdfunct->next; + if(cmdfunct->module_id == module_id) { + for(i = 0; i < 27; i++) { + struct cmd_binding *cbind, *next, *last = NULL; + for(cbind = cmd_binds[i]; cbind; cbind = next) { + next = cbind->next; + if(cmdfunct == cbind->func) { + if(last) + last->next = cbind->next; + else + cmd_binds[i] = cbind->next; + free(cbind->cmd); + if(cbind->paramcount) { + int j; + for(j = 0; j < cbind->paramcount; j++) + free(cbind->parameters[j]); + } + if(cbind->channel_access) + free(cbind->channel_access); + free(cbind); + } else + last = cbind; + } + } + if(prevfunct) + prevfunct->next = nextfunct; + else + cmd_functions = nextfunct; + free(cmdfunct->name); + free(cmdfunct); + } else + prevfunct = cmdfunct; + } + struct trigger_callback *cb, *prevcb = NULL, *nextcb; + for(cb = trigger_callbacks; cb; cb = nextcb) { + nextcb = cb->next; + if(cb->module_id == module_id) { + if(prevcb) + prevcb->next = nextcb; + else + trigger_callbacks = nextcb; + free(cb); + } else + prevcb = cb; + } +} diff --git a/src/modcmd.h b/src/modcmd.h new file mode 100644 index 0000000..214a096 --- /dev/null +++ b/src/modcmd.h @@ -0,0 +1,132 @@ +/* modcmd.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _modcmd_h +#define _modcmd_h +#include "main.h" + +#define MAXPARAMETERS 50 + +#define CMDFLAG_REQUIRE_CHAN 0x00001 +#define CMDFLAG_REQUIRE_AUTH 0x00002 +#define CMDFLAG_REQUIRE_GOD 0x00004 +#define CMDFLAG_CHECK_AUTH 0x00008 +#define CMDFLAG_REGISTERED_CHAN 0x00010 +#define CMDFLAG_OVERRIDE_GLOBAL_ACCESS 0x00020 +#define CMDFLAG_OVERRIDE_CHANNEL_ACCESS 0x00040 +#define CMDFLAG_CHAN_PARAM 0x00080 +#define CMDFLAG_LOG 0x00100 +#define CMDFLAG_OPLOG 0x00200 +#define CMDFLAG_EMPTY_ARGS 0x00400 +#define CMDFLAG_REQUIRED 0x00800 +#define CMDFLAG_TEMPONARY_BIND 0x01000 +#define CMDFLAG_FUNCMD 0x02000 +#define CMDFLAG_ESCAPE_ARGS 0x04000 +#define CMDFLAG_NO_CROSSCHAN 0x08000 +#define CMDFLAG_SUB_LINKER 0x10000 + +struct ClientSocket; +struct UserNode; +struct ChanNode; +struct Event; + +#define CMD_BIND(NAME) void NAME(UNUSED_ARG(struct ClientSocket *client), UNUSED_ARG(struct ClientSocket *textclient), UNUSED_ARG(struct UserNode *user), UNUSED_ARG(struct ChanNode *chan), UNUSED_ARG(char **argv), UNUSED_ARG(char argc), UNUSED_ARG(struct Event *event)) +typedef void cmd_bind_t(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, char **argv, char argc, struct Event *event); +typedef void trigger_callback_t(int clientid, struct ChanNode *chan, char *trigger); + +struct cmd_function { + char *name; + int botid; + int module_id; + cmd_bind_t *func; + unsigned int flags; + int paramcount; + int global_access; + char *channel_access; + + struct cmd_function *next; +}; + +struct cmd_binding { + char *cmd; + int botid; + int clientid; + struct cmd_function *func; + unsigned int flags; + char *parameters[MAXPARAMETERS]; + int paramcount; + int global_access; + char *channel_access; + int triggered; + + struct cmd_binding *next; +}; + +#define BIND_FLAGS(BIND) (BIND->flags | BIND->func->flags) + +struct trigger_cache { + int botid; + int clientid; + char *trigger; + + struct trigger_cache *next; +}; + +#define get_prefered_bot(BOTID) get_botwise_prefered_bot(BOTID, 0) +#define changeChannelTrigger(BOTID,CHAN,NEW) changeBotwiseChannelTrigger(BOTID, 0, CHAN, NEW) +#define bind_cmd_to_function(BOTID,CMD,FUNC) bind_botwise_cmd_to_function(BOTID, 0, CMD, FUNC) +#define bind_cmd_to_command(BOTID,CMD,FUNC) bind_botwise_cmd_to_command(BOTID, 0, CMD, FUNC) +#define unbind_cmd(BOTID,CMD) unbind_botwise_cmd(BOTID, 0, CMD) +#define unbind_allcmd(BOTID) unbind_botwise_allcmd(BOTID, 0) +#define bind_set_parameters(BOTID,CMD,PARAMETERS) bind_botwise_set_parameters(BOTID, 0, CMD, PARAMETERS) +#define bind_set_global_access(BOTID,CMD,GACCESS) bind_botwise_set_global_access(BOTID, 0, CMD, GACCESS) +#define bind_set_channel_access(BOTID,CMD,CACCESS) bind_botwise_set_channel_access(BOTID, 0, CMD, CACCESS) +#define bind_set_bind_flags(BOTID,CMD,FLAGS) bind_botwise_set_bind_flags(BOTID, 0, CMD, FLAGS) +#define find_cmd_binding(BOTID,CMD) find_botwise_cmd_binding(BOTID, 0, CMD) +#define bind_unbound_required_functions(BOTID) bind_botwise_unbound_required_functions(BOTID, 0) + + +#ifndef DND_FUNCTIONS +extern int statistics_commands; + +void init_modcmd(); +void free_modcmd(); + +/* MODULAR ACCESSIBLE */ struct ClientSocket* get_botwise_prefered_bot(int botid, int clientid); + +/* MODULAR ACCESSIBLE */ int register_command(int botid, char *name, int module_id, cmd_bind_t *func, int paramcount, char *channel_access, int global_access, unsigned int flags); +/* MODULAR ACCESSIBLE */ int set_trigger_callback(int botid, int module_id, trigger_callback_t *func); + +/* MODULAR ACCESSIBLE */ int flush_trigger_cache(int botid, int clientid); +/* MODULAR ACCESSIBLE */ int changeBotwiseChannelTrigger(int botid, int clientid, struct ChanNode *chan, char *new_trigger); +/* MODULAR ACCESSIBLE */ int bind_botwise_cmd_to_function(int botid, int clientid, char *cmd, struct cmd_function *func); +/* MODULAR ACCESSIBLE */ int bind_botwise_cmd_to_command(int botid, int clientid, char *cmd, char *func); +/* MODULAR ACCESSIBLE */ int unbind_botwise_cmd(int botid, int clientid, char *cmd); +/* MODULAR ACCESSIBLE */ int unbind_botwise_allcmd(int botid, int clientid); +/* MODULAR ACCESSIBLE */ void bind_botwise_set_parameters(int botid, int clientid, char *cmd, char *parameters); +/* MODULAR ACCESSIBLE */ void bind_botwise_set_global_access(int botid, int clientid, char *cmd, int gaccess); +/* MODULAR ACCESSIBLE */ void bind_botwise_set_channel_access(int botid, int clientid, char *cmd, char *chanaccess); +/* MODULAR ACCESSIBLE */ void bind_botwise_set_bind_flags(int botid, int clientid, char *cmd, unsigned int flags); +/* MODULAR ACCESSIBLE */ struct cmd_binding *find_botwise_cmd_binding(int botid, int clientid, char *cmd); +/* MODULAR ACCESSIBLE */ void bind_botwise_unbound_required_functions(int botid, int clientid); +/* MODULAR ACCESSIBLE */ struct cmd_function *find_cmd_function(int botid, char *name); +/* MODULAR ACCESSIBLE */ void register_command_alias(int botid, char *alias); +/* MODULAR ACCESSIBLE */ struct cmd_binding *getAllBinds(struct cmd_binding *last); + +void unregister_module_commands(int module_id); + +#endif +#endif diff --git a/src/module_commands.c b/src/module_commands.c new file mode 100644 index 0000000..7d3b4e1 --- /dev/null +++ b/src/module_commands.c @@ -0,0 +1,86 @@ +/* module_commands.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#include "modcmd.h" +#include "IRCParser.h" +#include "modules.h" +#include "tools.h" +#include "lang.h" + +static const struct default_language_entry msgtab[] = { + {"MODULE_LOADED", "Module %s loaded."}, /* {ARGS: "NeonServ"} */ + {"MODULE_UNLOADED", "Module %s unloaded."}, /* {ARGS: "NeonServ"} */ + {"MODULE_RELOADED", "Module %s reloaded."}, /* {ARGS: "NeonServ"} */ + {"MODULE_ERROR", "An error occured when performing this module operation. View error log for more information."}, + {"MODULE_LIST", "Loaded Modules: %s"}, + {NULL, NULL} +}; + +static CMD_BIND(module_cmd_load); +static CMD_BIND(module_cmd_unload); +static CMD_BIND(module_cmd_reload); +static CMD_BIND(module_cmd_modules); + +void register_module_commands() { + //Module Commands + #define OPER_COMMAND(NAME,FUNCTION,PARAMCOUNT,GACCESS,FLAGS) register_command(0, NAME, 0, FUNCTION, PARAMCOUNT, NULL, GACCESS, FLAGS) + // NAME FUNCTION PARAMS ACCS FLAGS + OPER_COMMAND("loadmod", module_cmd_load, 1, 1000, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_OPLOG); + OPER_COMMAND("unloadmod", module_cmd_unload, 1, 1000, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_OPLOG); + OPER_COMMAND("reloadmod", module_cmd_reload, 1, 1000, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_OPLOG); + OPER_COMMAND("modules", module_cmd_modules, 0, 1000, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH); + #undef OPER_COMMAND + + register_default_language_table(msgtab); +} + +static CMD_BIND(module_cmd_load) { + if(ext_load_module(argv[0])) + reply(textclient, user, "MODULE_LOADED", argv[0]); + else + reply(textclient, user, "MODULE_ERROR"); +} + +static CMD_BIND(module_cmd_unload) { + if(ext_unload_module(argv[0])) + reply(textclient, user, "MODULE_UNLOADED", argv[0]); + else + reply(textclient, user, "MODULE_ERROR"); +} + +static CMD_BIND(module_cmd_reload) { + if(ext_reload_module(argv[0])) + reply(textclient, user, "MODULE_RELOADED", argv[0]); + else + reply(textclient, user, "MODULE_ERROR"); +} + +static CMD_BIND(module_cmd_modules) { + char modules[MAXLEN]; + int modpos = 0; + int modcount = 0; + struct ModuleInfo *modinfo; + for(modinfo = ext_get_modules(NULL); modinfo; modinfo = ext_get_modules(modinfo)) { + if(modpos + strlen(modinfo->name) > 450) { + reply(textclient, user, "MODULE_LIST", modules); + modpos = 0; + } + modcount++; + modpos += sprintf(modules + modpos, (modpos ? ", %s" : "%s"), modinfo->name); + } + if(!modcount || modpos) + reply(textclient, user, "MODULE_LIST", (modpos ? modules : "none")); +} diff --git a/src/module_commands.h b/src/module_commands.h new file mode 100644 index 0000000..c7f285a --- /dev/null +++ b/src/module_commands.h @@ -0,0 +1,24 @@ +/* module_commands.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _module_commands_h +#define _module_commands_h +#ifndef DND_FUNCTIONS + +void register_module_commands(); + +#endif +#endif diff --git a/src/modules.c b/src/modules.c new file mode 100644 index 0000000..f48c41a --- /dev/null +++ b/src/modules.c @@ -0,0 +1,512 @@ +/* modules.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#include "modules.h" +#ifndef WIN32 +#include +#endif + +/* 000-001 */ #include "main.h" +/* 002-004 */ #include "tools.h" +#ifdef ENABLE_MUTEX_DEBUG +/* 005-006 */ #include "mutexDebug.h" +#endif +/* 007-009 */ /* main.h */ +/* 010 */ #include "log.h" +/* 011 */ /* main.h */ +/* 012 */ #include "BanNode.h" +/* 013-019 */ #include "bots.h" +/* 020-025 */ #include "ChanNode.h" +/* 026-029 */ #include "ChanUser.h" +/* 030-036 */ #include "ClientSocket.h" +/* 037-038 */ #include "ConfigParser.h" +/* 039-048 */ #include "DBHelper.h" +/* 049 */ #include "EventLogger.h" +/* 050 */ #include "HandleInfoHandler.h" +/* 051-088 */ #include "IRCEvents.h" +/* 089-091 */ #include "IRCParser.h" +/* 092-098 */ #include "lang.h" +#ifdef ENABLE_MEMORY_DEBUG +/* 099-102 */ #include "memoryDebug.h" +#endif +/* 103-106 */ #include "memoryInfo.h" +/* 107-122 */ #include "modcmd.h" +/* 123 */ /* deprecated */ +/* 124-125 */ /* modcmd.h */ +/* 126-136 */ #include "ModeNode.h" +/* 137-142 */ #include "mysqlConn.h" +/* 143-149 */ #include "timeq.h" +/* 150-169 */ /* tools.h */ +/* 170-180 */ #include "UserNode.h" +/* 181-183 */ #include "WHOHandler.h" +/* 184-188 */ #include "version.h" +/* 189 */ /* modules.h */ +/* 190 */ /* UserNode.h */ +/* 191-193 */ #include "ModuleFunctions.h" +/* 194 */ /* bots.h */ +/* 195-196 */ /* version.h */ +/* 197-198 */ /* IRCEvents.h */ + +#define Function void * + +void *global_functions[] = { +/* 000 */ (Function) getStartTime, +/* 001 */ (Function) getRunningThreads, +/* 002 */ (Function) getCurrentSecondsOfDay, +/* 003 */ (Function) stricmp, +/* 004 */ (Function) stricmplen, +#ifdef ENABLE_MUTEX_DEBUG +/* 005 */ (Function) xmutex, +/* 006 */ (Function) mutex_debug, +#else +/* 005 */ (Function) NULL, +/* 006 */ (Function) NULL, +#endif +/* 007 */ (Function) restart_bot, +/* 008 */ (Function) stop_bot, +/* 009 */ (Function) reload_config, +/* 010 */ (Function) printf_log, +#ifdef HAVE_THREADS +/* 011 */ (Function) getCurrentThreadID, +#else +/* 011 */ (Function) NULL, +#endif +/* 012 */ (Function) getMatchingChannelBan, +/* 013 */ (Function) getChannelBot, +/* 014 */ (Function) requestOp, +/* 015 */ (Function) channel_ban_timeout, +/* 016 */ (Function) general_event_privctcp, +/* 017 */ (Function) set_bot_alias, +/* 018 */ (Function) resolve_botid, +/* 019 */ (Function) resolve_botalias, +/* 020 */ (Function) is_valid_chan, +/* 021 */ (Function) getAllChans, +/* 022 */ (Function) getChanByName, +/* 023 */ (Function) getChannelCount, +/* 024 */ (Function) getChanUserCount, +/* 025 */ (Function) getChanBanCount, +/* 026 */ (Function) isUserOnChan, +/* 027 */ (Function) getChanUser, +/* 028 */ (Function) getChannelUsers, +/* 029 */ (Function) getUserChannels, +/* 030 */ (Function) create_socket, +/* 031 */ (Function) connect_socket, +/* 032 */ (Function) close_socket, +/* 033 */ (Function) destroy_socket, +/* 034 */ (Function) write_socket, +/* 035 */ (Function) putsock, +/* 036 */ (Function) getBots, +/* 037 */ (Function) get_int_field, +/* 038 */ (Function) get_string_field, +/* 039 */ (Function) _loadUserSettings, +/* 040 */ (Function) isGodMode, +/* 041 */ (Function) getChanDefault, +/* 042 */ (Function) getChannelAccess, +/* 043 */ (Function) checkChannelAccess, +/* 044 */ (Function) _loadChannelSettings, +/* 045 */ (Function) isUserProtected, +/* 046 */ (Function) getBanAffectingMask, +/* 047 */ (Function) renameAccount, +/* 048 */ (Function) deleteUser, +/* 049 */ (Function) logEvent, +/* 050 */ (Function) lookup_authname, +/* 051 */ (Function) bind_join, +/* 052 */ (Function) unbind_join, +/* 053 */ (Function) bind_nick, +/* 054 */ (Function) unbind_nick, +/* 055 */ (Function) bind_part, +/* 056 */ (Function) unbind_part, +/* 057 */ (Function) bind_reload, +/* 058 */ (Function) unbind_reload, +/* 059 */ (Function) bind_kick, +/* 060 */ (Function) unbind_kick, +/* 061 */ (Function) bind_topic, +/* 062 */ (Function) unbind_topic, +/* 063 */ (Function) bind_mode, +/* 064 */ (Function) unbind_mode, +/* 065 */ (Function) bind_chanmsg, +/* 066 */ (Function) unbind_chanmsg, +/* 067 */ (Function) bind_privmsg, +/* 068 */ (Function) unbind_privmsg, +/* 069 */ (Function) bind_channotice, +/* 070 */ (Function) unbind_channotice, +/* 071 */ (Function) bind_privnotice, +/* 072 */ (Function) unbind_privnotice, +/* 073 */ (Function) bind_chanctcp, +/* 074 */ (Function) unbind_chanctcp, +/* 075 */ (Function) bind_privctcp, +/* 076 */ (Function) unbind_privctcp, +/* 077 */ (Function) bind_invite, +/* 078 */ (Function) unbind_invite, +/* 079 */ (Function) bind_raw, +/* 080 */ (Function) unbind_raw, +/* 081 */ (Function) bind_bot_ready, +/* 082 */ (Function) unbind_bot_ready, +/* 083 */ (Function) bind_registered, +/* 084 */ (Function) unbind_registered, +/* 085 */ (Function) bind_freeuser, +/* 086 */ (Function) unbind_freeuser, +/* 087 */ (Function) bind_freechan, +/* 088 */ (Function) unbind_freechan, +/* 089 */ (Function) reply, +/* 090 */ (Function) merge_argv, +/* 091 */ (Function) merge_argv_char, +/* 092 */ (Function) get_language_by_tag, +/* 093 */ (Function) get_language_by_name, +/* 094 */ (Function) get_default_language, +/* 095 */ (Function) load_language, +/* 096 */ (Function) register_default_language_table, +/* 097 */ (Function) get_language_string, +/* 098 */ (Function) build_language_string, +#ifdef ENABLE_MEMORY_DEBUG +/* 099 */ (Function) xmalloc, +/* 100 */ (Function) xcalloc, +/* 101 */ (Function) xstrdup, +/* 102 */ (Function) xfree, +#else +/* 099 */ (Function) NULL, +/* 100 */ (Function) NULL, +/* 101 */ (Function) NULL, +/* 102 */ (Function) NULL, +#endif +/* 103 */ (Function) getMemoryInfoFiles, +/* 104 */ (Function) freeMemoryInfoFiles, +/* 105 */ (Function) getMemoryInfoLines, +/* 106 */ (Function) freeMemoryInfoLines, +/* 107 */ (Function) get_botwise_prefered_bot, +/* 108 */ (Function) register_command, +/* 109 */ (Function) set_trigger_callback, +/* 110 */ (Function) flush_trigger_cache, +/* 111 */ (Function) changeBotwiseChannelTrigger, +/* 112 */ (Function) bind_botwise_cmd_to_function, +/* 113 */ (Function) bind_botwise_cmd_to_command, +/* 114 */ (Function) unbind_botwise_cmd, +/* 115 */ (Function) unbind_botwise_allcmd, +/* 116 */ (Function) bind_botwise_set_parameters, +/* 117 */ (Function) bind_botwise_set_global_access, +/* 118 */ (Function) bind_botwise_set_channel_access, +/* 119 */ (Function) bind_botwise_set_bind_flags, +/* 120 */ (Function) find_botwise_cmd_binding, +/* 121 */ (Function) bind_botwise_unbound_required_functions, +/* 122 */ (Function) find_cmd_function, +/* 123 */ (Function) NULL, /* deprecated */ +/* 124 */ (Function) register_command_alias, +/* 125 */ (Function) getAllBinds, +/* 126 */ (Function) createModeNode, +/* 127 */ (Function) freeModeNode, +/* 128 */ (Function) isModeSet, +/* 129 */ (Function) isModeAffected, +/* 130 */ (Function) getModeValue, +/* 131 */ (Function) getModeType, +/* 132 */ (Function) parseModes, +/* 133 */ (Function) parseModeString, +/* 134 */ (Function) parseMode, +/* 135 */ (Function) getModeString, +/* 136 */ (Function) getFullModeString, +/* 137 */ (Function) mysql_use, +/* 138 */ (Function) mysql_free, +/* 139 */ (Function) printf_mysql_query, +/* 140 */ (Function) printf_long_mysql_query, +/* 141 */ (Function) escape_string, +/* 142 */ (Function) get_mysql_conn, +/* 143 */ (Function) timeq_add, +/* 144 */ (Function) timeq_uadd, +/* 145 */ (Function) timeq_add_name, +/* 146 */ (Function) timeq_uadd_name, +/* 147 */ (Function) timeq_del, +/* 148 */ (Function) timeq_del_name, +/* 149 */ (Function) timeq_name_exists, +/* 150 */ (Function) match, +/* 151 */ (Function) table_init, +/* 152 */ (Function) table_add, +/* 153 */ (Function) table_change, +/* 154 */ (Function) table_change_field, +/* 155 */ (Function) table_set_bold, +/* 156 */ (Function) table_end, +/* 157 */ (Function) table_free, +/* 158 */ (Function) timeToStr, +/* 159 */ (Function) strToTime, +/* 160 */ (Function) initModeBuffer, +/* 161 */ (Function) modeBufferSet, +/* 162 */ (Function) flushModeBuffer, +/* 163 */ (Function) freeModeBuffer, +/* 164 */ (Function) is_ircmask, +/* 165 */ (Function) generate_banmask, +/* 166 */ (Function) make_banmask, +/* 167 */ (Function) isFakeHost, +/* 168 */ (Function) mask_match, +/* 169 */ (Function) crc32, +/* 170 */ (Function) is_valid_nick, +/* 171 */ (Function) getUserByNick, +/* 172 */ (Function) getUserByMask, +/* 173 */ (Function) countUsersWithHost, +/* 174 */ (Function) getAuthFakehost, +/* 175 */ (Function) searchUserByNick, +/* 176 */ (Function) getAllUsers, +/* 177 */ (Function) getUsersWithAuth, +/* 178 */ (Function) getUserCount, +/* 179 */ (Function) createTempUser, +/* 180 */ (Function) createTempUserMask, +/* 181 */ (Function) get_userlist, +/* 182 */ (Function) _get_userlist_with_invisible, +/* 183 */ (Function) get_userauth, +/* 184 */ (Function) get_compilation, +/* 185 */ (Function) get_creation, +/* 186 */ (Function) get_revision, +/* 187 */ (Function) get_codelines, +/* 188 */ (Function) get_patchlevel, +/* 189 */ (Function) get_module_name, +/* 190 */ (Function) isUserModeSet, +/* 191 */ (Function) module_global_cmd_register_neonbackup, +/* 192 */ (Function) module_global_cmd_unregister_neonbackup, +/* 193 */ (Function) module_neonbackup_recover_chan, +/* 194 */ (Function) requestInvite, +/* 195 */ (Function) is_stable_revision, +/* 196 */ (Function) get_dev_revision, +/* 197 */ (Function) bind_freeclient, +/* 198 */ (Function) unbind_freeclient +}; + +static int module_id_counter = 1; +static struct ModuleInfo *modules = NULL; + +static void unregister_module_hooks(int module_id); + +void loadModules() { + char **modulelist = get_all_fieldnames("modules"); + if(!modulelist) return; + int i = 0; + char tmp[MAXLEN]; + struct ModuleInfo *modinfo; + while(modulelist[i]) { + sprintf(tmp, "modules.%s.enabled", modulelist[i]); + if(get_int_field(tmp)) { + modinfo = loadModule(modulelist[i]); + sprintf(tmp, "modules.%s.protected", modulelist[i]); + if(get_int_field(tmp)) + modinfo->state |= MODINFO_STATE_PROTECTED; + } + i++; + } + free(modulelist); + start_modules(); +} + +struct ModuleInfo *loadModule(char *name) { + struct ModuleInfo *modinfo; + for(modinfo = modules; modinfo; modinfo = modinfo->next) { + if(!stricmp(modinfo->name, name)) return NULL; + } + char fname[256]; + #ifndef WIN32 + sprintf(fname, "%s.so", name); + void* module = dlopen(fname, RTLD_LAZY); + if(!module) { + sprintf(fname, "./%s.so", name); + module = dlopen(fname, RTLD_LAZY); + } + if(!module) { + sprintf(fname, ".libs/%s.so", name); + module = dlopen(fname, RTLD_LAZY); + } + if(!module) { + printf_log("main", LOG_ERROR, "Error loading module '%s': %s not found.\n", name, fname); + return NULL; + } + void* initfunc = dlsym(module, "init_module"); + void* startfunc = dlsym(module, "start_module"); + void* stopfunc = dlsym(module, "stop_module"); + void* modversion = dlsym(module, "modversion"); + #else + sprintf(fname, "%s.dll", name); + HMODULE module = LoadLibrary(fname); + if(!module) { + printf_log("main", LOG_ERROR, "Error loading module '%s': %s not found.\n", name, fname); + return NULL; + } + FARPROC initfunc = GetProcAddress(module, "init_module"); + FARPROC startfunc = GetProcAddress(module, "start_module"); + FARPROC stopfunc = GetProcAddress(module, "stop_module"); + FARPROC modversion = GetProcAddress(module, "modversion"); + #endif + if(!startfunc || !stopfunc || !modversion) { + printf_log("main", LOG_ERROR, "Error loading module '%s': required symbols not found.\n", name); + return NULL; + } + int version = ((int (*)(void)) modversion)(); + if(version != MODULE_VERSION) { + printf_log("main", LOG_ERROR, "Error loading module '%s': version mismatch ('%d' main code, '%d' module)\n", name, MODULE_VERSION, version); + return NULL; + } + //start module + int errid; + int module_id = module_id_counter++; + if((errid = ((int (*)(void **, int)) initfunc)(global_functions, module_id))) { + printf_log("main", LOG_ERROR, "Error loading module '%s': module reported error (errid: %d)\n", name, errid); + return NULL; + } + modinfo = malloc(sizeof(*modinfo)); + if(!modinfo) { + unregister_module_hooks(module_id); + return NULL; + } + modinfo->name = strdup(name); + modinfo->module_id = module_id; + modinfo->module = module; + modinfo->startfunc = startfunc; + modinfo->stopfunc = stopfunc; + modinfo->state = 0; + modinfo->next = modules; + modules = modinfo; + scan_module(modinfo); + return modinfo; +} + +#ifndef WIN32 +static void closemodule(void *module) { + dlclose(module); +} +#else +static void closemodule(HMODULE module) { + FreeLibrary(module); +} +#endif + +int ext_load_module(char *name) { + if(!loadModule(name)) return 0; + struct ModuleInfo *modinfo; + for(modinfo = modules; modinfo; modinfo = modinfo->next) { + if(!(modinfo->state & MODINFO_STATE_STARTED)) { + modinfo->state |= MODINFO_STATE_STARTED; + ((void (*)(int)) modinfo->startfunc)(MODSTATE_STARTSTOP); + } else + ((void (*)(int)) modinfo->startfunc)(MODSTATE_REBIND); + } + return 1; +} + +int ext_unload_module(char *name) { + struct ModuleInfo *old_modinfo, *old_prev = NULL; + for(old_modinfo = modules; old_modinfo; old_modinfo = old_modinfo->next) { + if(!stricmp(old_modinfo->name, name)) { + if(old_modinfo->state & MODINFO_STATE_PROTECTED) { + return 0; + } + if(old_prev) + old_prev->next = old_modinfo->next; + else + modules = old_modinfo->next; + unregister_module_hooks(old_modinfo->module_id); + ((void (*)(int)) old_modinfo->stopfunc)(MODSTATE_STARTSTOP); + closemodule(old_modinfo->module); + free_module_functions(old_modinfo); + free(old_modinfo->name); + free(old_modinfo); + return 1; + } else + old_prev = old_modinfo; + } + return 0; +} + +int ext_reload_module(char *name) { + char libname[256]; + struct ModuleInfo *old_modinfo, *old_prev = NULL; + for(old_modinfo = modules; old_modinfo; old_modinfo = old_modinfo->next) { + if(!stricmp(old_modinfo->name, name)) { + strcpy(libname, old_modinfo->name); + if(old_prev) + old_prev->next = old_modinfo->next; + else + modules = old_modinfo->next; + unregister_module_hooks(old_modinfo->module_id); + ((void (*)(int)) old_modinfo->stopfunc)(MODSTATE_RELOAD); + closemodule(old_modinfo->module); + free_module_functions(old_modinfo); + free(old_modinfo->name); + free(old_modinfo); + break; + } else + old_prev = old_modinfo; + } + if(!loadModule(libname)) return 0; + struct ModuleInfo *modinfo; + for(modinfo = modules; modinfo; modinfo = modinfo->next) { + if(!(modinfo->state & MODINFO_STATE_STARTED)) { + modinfo->state |= MODINFO_STATE_STARTED; + ((void (*)(int)) modinfo->startfunc)(MODSTATE_RELOAD); + } else + ((void (*)(int)) modinfo->startfunc)(MODSTATE_REBIND); + } + return 1; +} + +struct ModuleInfo *ext_get_modules(struct ModuleInfo *last) { + return (last ? last->next : modules); +} + +void start_modules() { + struct ModuleInfo *modinfo; + for(modinfo = modules; modinfo; modinfo = modinfo->next) { + if(!(modinfo->state & MODINFO_STATE_STARTED)) { + modinfo->state |= MODINFO_STATE_STARTED; + ((void (*)(int)) modinfo->startfunc)(MODSTATE_STARTSTOP); + } + } +} + +void stop_modules() { + struct ModuleInfo *modinfo, *next; + for(modinfo = modules; modinfo; modinfo = next) { + next = modinfo->next; + unregister_module_hooks(modinfo->module_id); + ((void (*)(int)) modinfo->stopfunc)(MODSTATE_STARTSTOP); + closemodule(modinfo->module); + free_module_functions(modinfo); + free(modinfo->name); + free(modinfo); + } + modules = NULL; +} + +static void unregister_module_hooks(int module_id) { + unregister_module_commands(module_id); + unregister_module_events(module_id); + unregister_module_timers(module_id); + +} + +int module_loaded(int module_id) { + if(!module_id) return 1; + struct ModuleInfo *modinfo; + for(modinfo = modules; modinfo; modinfo = modinfo->next) { + if(modinfo->module_id == module_id) + return 1; + } + return 0; +} + +char *get_module_name(int module_id) { + if(!module_id) return NULL; + struct ModuleInfo *modinfo; + for(modinfo = modules; modinfo; modinfo = modinfo->next) { + if(modinfo->module_id == module_id) + return modinfo->name; + } + return NULL; +} + + diff --git a/src/modules.h b/src/modules.h new file mode 100644 index 0000000..7600f6e --- /dev/null +++ b/src/modules.h @@ -0,0 +1,52 @@ +/* modules.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _modules_h +#define _modules_h +#include "main.h" + +#define MODINFO_STATE_STARTED 0x01 +#define MODINFO_STATE_PROTECTED 0x02 + +struct ModuleInfo { + char *name; + int module_id; + #ifndef WIN32 + void *module; + #else + HMODULE module; + #endif + int state; + void *startfunc; + void *stopfunc; + struct ModuleInfo *next; +}; + +#ifndef DND_FUNCTIONS +void loadModules(); +struct ModuleInfo *loadModule(char *name); +void start_modules(); +void stop_modules(); +int module_loaded(int module_id); + +/* MODULAR ACCESSIBLE */ char *get_module_name(int module_id); + +int ext_load_module(char *name); +int ext_unload_module(char *name); +int ext_reload_module(char *name); +struct ModuleInfo *ext_get_modules(struct ModuleInfo *last); +#endif +#endif diff --git a/src/modules/DummyServ.mod/bot_DummyServ.c b/src/modules/DummyServ.mod/bot_DummyServ.c new file mode 100644 index 0000000..4121bc8 --- /dev/null +++ b/src/modules/DummyServ.mod/bot_DummyServ.c @@ -0,0 +1,170 @@ +/* bot_DummyServ.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#include "../module.h" +#include "../botid.h" + +#include "bot_DummyServ.h" +#include "../../modcmd.h" +#include "../../IRCParser.h" +#include "../../IRCEvents.h" +#include "../../UserNode.h" +#include "../../ChanNode.h" +#include "../../ChanUser.h" +#include "../../ModeNode.h" +#include "../../BanNode.h" +#include "../../ClientSocket.h" +#include "../../mysqlConn.h" +#include "../../lang.h" +#include "../../HandleInfoHandler.h" +#include "../../WHOHandler.h" +#include "../../DBHelper.h" +#include "../../tools.h" +#include "../../timeq.h" +#include "../../version.h" +#include "../../EventLogger.h" +#include "../../bots.h" + +#define BOTID DUMMYSERV_BOTID +#define BOTALIAS "DummyServ" + +static void dummyserv_bot_ready(struct ClientSocket *client) { + if(client->botid != BOTID) + return; + MYSQL_RES *res; + MYSQL_ROW row; + + printf_mysql_query("SELECT `automodes`, `oper_user`, `oper_pass` FROM `bots` WHERE `id` = '%d'", client->clientid); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + if(row[1] && row[2]) { + putsock(client, "OPER %s %s", row[1], row[2]); + } + putsock(client, "MODE %s +%s", client->user->nick, row[0]); + } + + printf_mysql_query("SELECT `channel_name`, `channel_key` FROM `bot_channels` LEFT JOIN `channels` ON `chanid` = `channel_id` WHERE `botid` = '%d' AND `suspended` = '0'", client->clientid); + res = mysql_use(); + + while ((row = mysql_fetch_row(res)) != NULL) { + putsock(client, "JOIN %s %s", row[0], row[1]); + } +} + +static void dummyserv_trigger_callback(int clientid, struct ChanNode *chan, char *trigger) { + //this bot doesn't have a trigger + strcpy(trigger, ""); +} + +static void start_bots(int type) { + struct ClientSocket *client; + MYSQL_RES *res, *res2; + MYSQL_ROW row; + + if(type == MODSTATE_STARTSTOP) { + printf_mysql_query("SELECT `nick`, `ident`, `realname`, `server`, `port`, `pass`, `textbot`, `id`, `queue`, `ssl`, `bind`, `secret` FROM `bots` WHERE `botclass` = '%d' AND `active` = '1'", BOTID); + res = mysql_use(); + + while ((row = mysql_fetch_row(res)) != NULL) { + client = create_socket(row[3], atoi(row[4]), row[10], row[5], row[0], row[1], row[2]); + client->flags |= (strcmp(row[6], "0") ? SOCKET_FLAG_PREFERRED : 0); + client->flags |= (strcmp(row[8], "0") ? SOCKET_FLAG_USE_QUEUE : 0); + client->flags |= (strcmp(row[9], "0") ? SOCKET_FLAG_SSL : 0); + client->flags |= (strcmp(row[11], "0") ? SOCKET_FLAG_SECRET_BOT : 0); + client->flags |= SOCKET_FLAG_SILENT; + client->botid = BOTID; + client->clientid = atoi(row[7]); + connect_socket(client); + } + } + + printf_mysql_query("SELECT `command`, `function`, `parameters`, `global_access`, `chan_access`, `flags` FROM `bot_binds` WHERE `botclass` = '%d'", BOTID); + res2 = mysql_use(); + while ((row = mysql_fetch_row(res2)) != NULL) { + if(bind_cmd_to_command(BOTID, row[0], row[1])) { + if(row[2] && strcmp(row[2], "")) { + bind_set_parameters(BOTID, row[0], row[2]); + } + if(row[3]) { + bind_set_global_access(BOTID, row[0], atoi(row[3])); + } + if(row[4]) { + bind_set_channel_access(BOTID, row[0], row[4]); + } + if(strcmp(row[5], "0")) + bind_set_bind_flags(BOTID, row[0], atoi(row[5])); + } + } + bind_unbound_required_functions(BOTID); +} + +static void dummyserv_event_invite(struct ClientSocket *client, struct UserNode *user, char *channel) { + if(client->botid != BOTID) + return; + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `botid`, `bot_channels`.`id`, `suspended` FROM `bot_channels` LEFT JOIN `bots` ON `bot_channels`.`botid` = `bots`.`id` LEFT JOIN `channels` ON `chanid` = `channel_id` WHERE `channel_name` = '%s' AND `botclass` = '%d'", escape_string(channel), client->botid); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) == NULL) { + reply(client, user, "NS_INVITE_FAIL", channel, client->user->nick); + return; + } + if(!strcmp(row[2], "1")) { + return; + } + int botid = atoi(row[0]); + struct ClientSocket *bot; + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(bot->clientid == botid) + break; + } + if(bot) { + struct ChanNode *chan = getChanByName(channel); + if(!(chan && isUserOnChan(bot->user, chan))) + putsock(bot, "JOIN %s", channel); + } +} + +void init_DummyServ(int type) { + set_bot_alias(BOTID, BOTALIAS); + start_bots(type); + + if(type == MODSTATE_REBIND) return; + + //register events + bind_bot_ready(dummyserv_bot_ready, module_id); + bind_invite(dummyserv_event_invite, module_id); + + set_trigger_callback(BOTID, module_id, dummyserv_trigger_callback); +} + +void free_DummyServ(int type) { + unbind_allcmd(BOTID); + if(type == MODSTATE_STARTSTOP) { + //disconnect all our bots + struct ClientSocket *client; + for(client = getBots(0, NULL); client; client = getBots(0, client)) { + if(client->botid == BOTID) { + unbind_botwise_allcmd(0, client->clientid); + close_socket(client); + break; + } + } + } +} + +#undef BOTID +#undef BOTALIAS diff --git a/src/modules/DummyServ.mod/bot_DummyServ.h b/src/modules/DummyServ.mod/bot_DummyServ.h new file mode 100644 index 0000000..335b1c8 --- /dev/null +++ b/src/modules/DummyServ.mod/bot_DummyServ.h @@ -0,0 +1,25 @@ +/* bot_DummyServ.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _bot_DummyServ_h +#define _bot_DummyServ_h + +#include "../../main.h" + +void init_DummyServ(int type); +void free_DummyServ(int type); + +#endif \ No newline at end of file diff --git a/src/modules/DummyServ.mod/module.c b/src/modules/DummyServ.mod/module.c new file mode 100644 index 0000000..cb7937f --- /dev/null +++ b/src/modules/DummyServ.mod/module.c @@ -0,0 +1,32 @@ +/* module.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#include "../module.h" +#include "bot_DummyServ.h" + +static int module_initialize() { + return 0; +} + +static void module_start(int type) { + init_DummyServ(type); +} + +static void module_stop(int type) { + free_DummyServ(type); +} + +MODULE_HEADER(module_initialize, module_start, module_stop); diff --git a/src/modules/NeonBackup.mod/bot_NeonBackup.c b/src/modules/NeonBackup.mod/bot_NeonBackup.c new file mode 100644 index 0000000..a886da3 --- /dev/null +++ b/src/modules/NeonBackup.mod/bot_NeonBackup.c @@ -0,0 +1,221 @@ +/* bot_NeonBackup.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#include "../module.h" +#include "../botid.h" + +#include "bot_NeonBackup.h" +#include "../../modcmd.h" +#include "cmd_neonbackup.h" +#include "../../lang.h" +#include "../../mysqlConn.h" +#include "../../ClientSocket.h" +#include "../../UserNode.h" +#include "../../ChanNode.h" +#include "../../ChanUser.h" +#include "../../IRCEvents.h" +#include "../../IRCParser.h" +#include "../../bots.h" +#include "../../DBHelper.h" +#include "../../WHOHandler.h" + +#define BOTID NEONBACKUP_BOTID +#define BOTALIAS "NeonBackup" + +static const struct default_language_entry msgtab[] = { + {NULL, NULL} +}; + +static void neonbackup_bot_ready(struct ClientSocket *client) { + if(client->botid != BOTID) + return; + MYSQL_RES *res; + MYSQL_ROW row; + + printf_mysql_query("SELECT `automodes`, `oper_user`, `oper_pass` FROM `bots` WHERE `id` = '%d'", client->clientid); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + if(row[1] && row[2]) { + putsock(client, "OPER %s %s", row[1], row[2]); + } + putsock(client, "MODE %s +%s", client->user->nick, row[0]); + } + + printf_mysql_query("SELECT `channel_name`, `channel_key` FROM `bot_channels` LEFT JOIN `channels` ON `chanid` = `channel_id` WHERE `botid` = '%d' AND `suspended` = '0'", client->clientid); + res = mysql_use(); + + while ((row = mysql_fetch_row(res)) != NULL) { + putsock(client, "JOIN %s %s", row[0], row[1]); + } +} + +static void neonbackup_trigger_callback(int clientid, struct ChanNode *chan, char *trigger) { + MYSQL_RES *res; + MYSQL_ROW row; + loadChannelSettings(chan); + if(!(chan->flags & CHANFLAG_CHAN_REGISTERED)) { + strcpy(trigger, "!"); + return; + } + printf_mysql_query("SELECT `trigger`, `defaulttrigger` FROM `bot_channels` LEFT JOIN `bots` ON `botid` = `bots`.`id` WHERE `chanid` = '%d' AND `botclass` = '%d'", chan->channel_id, BOTID); + res = mysql_use(); + if(!(row = mysql_fetch_row(res))) { + strcpy(trigger, "+"); + return; + } + if(row[0] && *row[0]) + strcpy(trigger, row[0]); + else + strcpy(trigger, ((row[1] && *row[1]) ? row[1] : "+")); +} + +static void start_bots(int type) { + struct ClientSocket *client; + MYSQL_RES *res, *res2; + MYSQL_ROW row; + + if(type == MODSTATE_STARTSTOP) { + printf_mysql_query("SELECT `nick`, `ident`, `realname`, `server`, `port`, `pass`, `textbot`, `id`, `queue`, `ssl`, `bind`, `secret` FROM `bots` WHERE `botclass` = '%d' AND `active` = '1'", BOTID); + res = mysql_use(); + + while ((row = mysql_fetch_row(res)) != NULL) { + client = create_socket(row[3], atoi(row[4]), row[10], row[5], row[0], row[1], row[2]); + client->flags |= (strcmp(row[6], "0") ? SOCKET_FLAG_PREFERRED : 0); + client->flags |= (strcmp(row[8], "0") ? SOCKET_FLAG_USE_QUEUE : 0); + client->flags |= (strcmp(row[9], "0") ? SOCKET_FLAG_SSL : 0); + client->flags |= (strcmp(row[11], "0") ? SOCKET_FLAG_SECRET_BOT : 0); + client->flags |= SOCKET_FLAG_REQUEST_INVITE | SOCKET_FLAG_REQUEST_OP; + client->botid = BOTID; + client->clientid = atoi(row[7]); + connect_socket(client); + } + } + + printf_mysql_query("SELECT `command`, `function`, `parameters`, `global_access`, `chan_access`, `flags` FROM `bot_binds` WHERE `botclass` = '%d'", BOTID); + res2 = mysql_use(); + while ((row = mysql_fetch_row(res2)) != NULL) { + if(bind_cmd_to_command(BOTID, row[0], row[1])) { + if(row[2] && strcmp(row[2], "")) { + bind_set_parameters(BOTID, row[0], row[2]); + } + if(row[3]) { + bind_set_global_access(BOTID, row[0], atoi(row[3])); + } + if(row[4]) { + bind_set_channel_access(BOTID, row[0], row[4]); + } + if(strcmp(row[5], "0")) + bind_set_bind_flags(BOTID, row[0], atoi(row[5])); + } + } + bind_unbound_required_functions(BOTID); +} + +void neonbackup_recover_chan(struct ChanNode *chan) { + struct ClientSocket *bot = getChannelBot(chan, BOTID); // prefer backup bot ;) + struct ChanUser *chanuser = (bot ? getChanUser(bot->user, chan) : NULL); + if(!chanuser || !(chanuser->flags & CHANUSERFLAG_OPPED)) { + //search an opped bot + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if((chanuser = getChanUser(bot->user, chan)) && (chanuser->flags & CHANUSERFLAG_OPPED)) + break; + } + } + if(!bot) //no opped bots present... channel can't be recovered + return; + struct ClientSocket *target; + struct ModeBuffer *modeBuf; + modeBuf = initModeBuffer(bot, chan); + for(target = getBots(SOCKET_FLAG_READY, NULL); target; target = getBots(SOCKET_FLAG_READY, target)) { + if((target->flags & SOCKET_FLAG_REQUEST_OP) && (chanuser = getChanUser(target->user, chan)) && !(chanuser->flags & CHANUSERFLAG_OPPED)) { + modeBufferOp(modeBuf, target->user->nick); + } + } + freeModeBuffer(modeBuf); +} + +static void neonbackup_event_join(struct ChanUser *chanuser) { + struct ClientSocket *client = getChannelBot(chanuser->chan, BOTID); + if(!client) return; //we can't "see" this event + if(chanuser->user == client->user) { + requestOp(client->user, chanuser->chan); + neonbackup_recover_chan(chanuser->chan); + } +} + +static void neonbackup_event_invite(struct ClientSocket *client, struct UserNode *user, char *channel) { + if(client->botid != BOTID) + return; + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `botid`, `bot_channels`.`id`, `suspended` FROM `bot_channels` LEFT JOIN `bots` ON `bot_channels`.`botid` = `bots`.`id` LEFT JOIN `channels` ON `chanid` = `channel_id` WHERE `channel_name` = '%s' AND `botclass` = '%d'", escape_string(channel), client->botid); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) == NULL) { + reply(client, user, "NS_INVITE_FAIL", channel, client->user->nick); + return; + } + if(!strcmp(row[2], "1")) { + reply(client, user, "MODCMD_CHAN_SUSPENDED"); + return; + } + int botid = atoi(row[0]); + struct ClientSocket *bot; + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(bot->clientid == botid) + break; + } + if(bot) { + struct ChanNode *chan = getChanByName(channel); + if(chan && isUserOnChan(bot->user, chan)) { + reply(client, user, "NS_INVITE_ON_CHAN", bot->user->nick, chan->name); + } else + putsock(bot, "JOIN %s", channel); + } +} + +void init_NeonBackup(int type) { + set_bot_alias(BOTID, BOTALIAS); + start_bots(type); + + if(type == MODSTATE_REBIND) return; + + //register events + bind_bot_ready(neonbackup_bot_ready, module_id); + bind_join(neonbackup_event_join, module_id); + bind_invite(neonbackup_event_invite, module_id); + + set_trigger_callback(BOTID, module_id, neonbackup_trigger_callback); + + register_default_language_table(msgtab); +} + +void free_NeonBackup(int type) { + unbind_allcmd(BOTID); + if(type == MODSTATE_STARTSTOP) { + //disconnect all our bots + struct ClientSocket *client; + for(client = getBots(0, NULL); client; client = getBots(0, client)) { + if(client->botid == BOTID) { + unbind_botwise_allcmd(0, client->clientid); + close_socket(client); + break; + } + } + } +} + +#undef BOTID +#undef BOTALIAS diff --git a/src/modules/NeonBackup.mod/bot_NeonBackup.h b/src/modules/NeonBackup.mod/bot_NeonBackup.h new file mode 100644 index 0000000..336ba82 --- /dev/null +++ b/src/modules/NeonBackup.mod/bot_NeonBackup.h @@ -0,0 +1,25 @@ +/* bot_NeonBackup.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _bot_NeonBackup_h +#define _bot_NeonBackup_h + +#include "../../main.h" + +void init_NeonBackup(int type); +void free_NeonBackup(int type); + +#endif \ No newline at end of file diff --git a/src/modules/NeonBackup.mod/cmd_neonbackup.c b/src/modules/NeonBackup.mod/cmd_neonbackup.c new file mode 100644 index 0000000..f78bf37 --- /dev/null +++ b/src/modules/NeonBackup.mod/cmd_neonbackup.c @@ -0,0 +1,31 @@ +/* cmd_neonbackup.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#include "../module.h" +#include "cmd_neonbackup.h" +#include "../../modcmd.h" +#include "../../ConfigParser.h" + +void register_commands() { + //NeonBackup Commands + register_command_alias(6, "NeonBackup"); + + #define OPER_COMMAND(NAME,FUNCTION,PARAMCOUNT,GACCESS,FLAGS) register_command(6, NAME, module_id, FUNCTION, PARAMCOUNT, NULL, GACCESS, FLAGS) + // NAME FUNCTION PARAMS ACCS FLAGS + OPER_COMMAND("recover", neonbackup_cmd_recover, 0, 900, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH); + #undef OPER_COMMAND + +} \ No newline at end of file diff --git a/src/modules/NeonBackup.mod/cmd_neonbackup.h b/src/modules/NeonBackup.mod/cmd_neonbackup.h new file mode 100644 index 0000000..d6f1763 --- /dev/null +++ b/src/modules/NeonBackup.mod/cmd_neonbackup.h @@ -0,0 +1,37 @@ +/* cmd_neonbackup.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _cmd_neonbackup_h +#define _cmd_neonbackup_h +#include "../module.h" +#include "../../main.h" +#include "../../modcmd.h" +#include "../../mysqlConn.h" +#include "../../ClientSocket.h" +#include "../../UserNode.h" +#include "../../ChanNode.h" +#include "../../ChanUser.h" +#include "../../DBHelper.h" +#include "../../IRCParser.h" +#include "bot_NeonBackup.h" +#include "../../lang.h" +#include "../../tools.h" + +void register_commands(); + +CMD_BIND(neonbackup_cmd_recover); + +#endif diff --git a/src/modules/NeonBackup.mod/cmd_neonbackup_recover.c b/src/modules/NeonBackup.mod/cmd_neonbackup_recover.c new file mode 100644 index 0000000..dc71276 --- /dev/null +++ b/src/modules/NeonBackup.mod/cmd_neonbackup_recover.c @@ -0,0 +1,56 @@ +/* cmd_neonbackup_recover.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonbackup.h" + +/* +* no argv +*/ +static struct ClientSocket *neonbackup_cmd_recover_get_bot(int clientid) { + struct ClientSocket *bot; + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(bot->clientid == clientid) + return bot; + } + return NULL; +} + +CMD_BIND(neonbackup_cmd_recover) { + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `channels`.`channel_name`, `bots`.`id` FROM `bot_channels`, `bots`, `channels` WHERE `bots`.`id` = `botid` AND `channel_id` = `chanid` AND `active` = 1"); + res = mysql_use(); + + struct ClientSocket *bot; + struct ChanUser *chanuser; + while ((row = mysql_fetch_row(res)) != NULL) { + chan = getChanByName(row[0]); + if(!chan) + continue; + bot = neonbackup_cmd_recover_get_bot(atoi(row[1])); + if(!bot) + continue; + if(!(chanuser = getChanUser(bot->user, chan))) { + requestInvite(bot->user, chan); + reply(textclient, user, "NS_INVITE_DONE", bot->user->nick, chan->name); + } else if(!chanuser->flags & CHANUSERFLAG_OPPED) { + requestOp(bot->user, chan); + reply(textclient, user, "NS_OP_DONE", chan->name); + } + } + logEvent(event); +} diff --git a/src/modules/NeonBackup.mod/module.c b/src/modules/NeonBackup.mod/module.c new file mode 100644 index 0000000..3fe1d39 --- /dev/null +++ b/src/modules/NeonBackup.mod/module.c @@ -0,0 +1,34 @@ +/* module.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#include "../module.h" +#include "bot_NeonBackup.h" +#include "cmd_neonbackup.h" + +static int module_initialize() { + register_commands(); + return 0; +} + +static void module_start(int type) { + init_NeonBackup(type); +} + +static void module_stop(int type) { + free_NeonBackup(type); +} + +MODULE_HEADER(module_initialize, module_start, module_stop); diff --git a/src/modules/NeonFun.mod/bot_NeonFun.c b/src/modules/NeonFun.mod/bot_NeonFun.c new file mode 100644 index 0000000..44df0c3 --- /dev/null +++ b/src/modules/NeonFun.mod/bot_NeonFun.c @@ -0,0 +1,329 @@ +/* bot_NeonFun.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#include "../module.h" +#include "../botid.h" + +#include "bot_NeonFun.h" +#include "../../modcmd.h" +#include "../../IRCParser.h" +#include "../../IRCEvents.h" +#include "../../UserNode.h" +#include "../../ChanNode.h" +#include "../../ChanUser.h" +#include "../../ModeNode.h" +#include "../../BanNode.h" +#include "../../ClientSocket.h" +#include "../../mysqlConn.h" +#include "../../lang.h" +#include "../../HandleInfoHandler.h" +#include "../../WHOHandler.h" +#include "../../DBHelper.h" +#include "../../tools.h" +#include "../../timeq.h" +#include "../../version.h" +#include "../../EventLogger.h" +#include "../../bots.h" +#include "game_uno.h" +#include "game_4wins.h" +#include "game_blackjack.h" + +#define BOTID NEONFUN_BOTID +#define BOTALIAS "NeonFun" + +static void neonfun_bot_ready(struct ClientSocket *client) { + if(client->botid != BOTID) + return; + MYSQL_RES *res; + MYSQL_ROW row; + + printf_mysql_query("SELECT `automodes`, `oper_user`, `oper_pass` FROM `bots` WHERE `id` = '%d'", client->clientid); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + if(row[1] && row[2]) { + putsock(client, "OPER %s %s", row[1], row[2]); + } + putsock(client, "MODE %s +%s", client->user->nick, row[0]); + } + + printf_mysql_query("SELECT `channel_name`, `channel_key` FROM `bot_channels` LEFT JOIN `channels` ON `chanid` = `channel_id` WHERE `botid` = '%d' AND `suspended` = '0'", client->clientid); + res = mysql_use(); + + while ((row = mysql_fetch_row(res)) != NULL) { + putsock(client, "JOIN %s %s", row[0], row[1]); + } +} + +static void neonfun_trigger_callback(int clientid, struct ChanNode *chan, char *trigger) { + MYSQL_RES *res; + MYSQL_ROW row; + loadChannelSettings(chan); + if(!(chan->flags & CHANFLAG_CHAN_REGISTERED)) { + strcpy(trigger, "+"); + return; + } + printf_mysql_query("SELECT `trigger`, `defaulttrigger` FROM `bot_channels` LEFT JOIN `bots` ON `botid` = `bots`.`id` WHERE `chanid` = '%d' AND `botclass` = '%d'", chan->channel_id, BOTID); + res = mysql_use(); + if(!(row = mysql_fetch_row(res))) { + strcpy(trigger, "+"); + return; + } + if(row[0] && *row[0]) + strcpy(trigger, row[0]); + else + strcpy(trigger, ((row[1] && *row[1]) ? row[1] : "~")); +} + +static void start_bots(int type) { + struct ClientSocket *client; + MYSQL_RES *res, *res2; + MYSQL_ROW row; + + if(type == MODSTATE_STARTSTOP) { + printf_mysql_query("SELECT `nick`, `ident`, `realname`, `server`, `port`, `pass`, `textbot`, `id`, `queue`, `ssl`, `bind`, `secret` FROM `bots` WHERE `botclass` = '%d' AND `active` = '1'", BOTID); + res = mysql_use(); + + while ((row = mysql_fetch_row(res)) != NULL) { + client = create_socket(row[3], atoi(row[4]), row[10], row[5], row[0], row[1], row[2]); + client->flags |= (strcmp(row[6], "0") ? SOCKET_FLAG_PREFERRED : 0); + client->flags |= (strcmp(row[8], "0") ? SOCKET_FLAG_USE_QUEUE : 0); + client->flags |= (strcmp(row[9], "0") ? SOCKET_FLAG_SSL : 0); + client->flags |= (strcmp(row[11], "0") ? SOCKET_FLAG_SECRET_BOT : 0); + client->flags |= SOCKET_FLAG_SILENT; + client->flags |= SOCKET_FLAG_REQUEST_INVITE | SOCKET_FLAG_REQUEST_OP; + client->botid = BOTID; + client->clientid = atoi(row[7]); + connect_socket(client); + } + } + + printf_mysql_query("SELECT `command`, `function`, `parameters`, `global_access`, `chan_access`, `flags` FROM `bot_binds` WHERE `botclass` = '%d'", BOTID); + res2 = mysql_use(); + while ((row = mysql_fetch_row(res2)) != NULL) { + if(bind_cmd_to_command(BOTID, row[0], row[1])) { + if(row[2] && strcmp(row[2], "")) { + bind_set_parameters(BOTID, row[0], row[2]); + } + if(row[3]) { + bind_set_global_access(BOTID, row[0], atoi(row[3])); + } + if(row[4]) { + bind_set_channel_access(BOTID, row[0], row[4]); + } + if(strcmp(row[5], "0")) + bind_set_bind_flags(BOTID, row[0], atoi(row[5])); + } + } + bind_unbound_required_functions(BOTID); +} + +static void neonfun_parted(struct ChanUser *chanuser, int quit, char *reason) { + uno_event_part(chanuser); + fourwins_event_part(chanuser); + bj_event_part(chanuser); +} + +static int neonfun_freechan(struct ChanNode *chan) { + uno_event_freechan(chan); + fourwins_event_freechan(chan); + bj_event_freechan(chan); + return 0; +} + +static void neonfun_event_invite(struct ClientSocket *client, struct UserNode *user, char *channel) { + if(client->botid != BOTID) + return; + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `botid`, `bot_channels`.`id`, `suspended` FROM `bot_channels` LEFT JOIN `bots` ON `bot_channels`.`botid` = `bots`.`id` LEFT JOIN `channels` ON `chanid` = `channel_id` WHERE `channel_name` = '%s' AND `botclass` = '%d'", escape_string(channel), client->botid); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) == NULL) { + reply(client, user, "NS_INVITE_FAIL", channel, client->user->nick); + return; + } + if(!strcmp(row[2], "1")) { + reply(client, user, "MODCMD_CHAN_SUSPENDED"); + return; + } + int botid = atoi(row[0]); + struct ClientSocket *bot; + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(bot->clientid == botid) + break; + } + if(bot) { + struct ChanNode *chan = getChanByName(channel); + if(chan && isUserOnChan(bot->user, chan)) { + reply(client, user, "NS_INVITE_ON_CHAN", bot->user->nick, chan->name); + } else + putsock(bot, "JOIN %s", channel); + } +} + +void init_NeonFun(int type) { + set_bot_alias(BOTID, BOTALIAS); + start_bots(type); + + if(type == MODSTATE_REBIND) return; + + //register events + bind_bot_ready(neonfun_bot_ready, module_id); + bind_part(neonfun_parted, module_id); + bind_freechan(neonfun_freechan, module_id); + bind_invite(neonfun_event_invite, module_id); + + set_trigger_callback(BOTID, module_id, neonfun_trigger_callback); +} + +void free_NeonFun(int type) { + unbind_allcmd(BOTID); + if(type == MODSTATE_STARTSTOP) { + //disconnect all our bots + struct ClientSocket *client; + for(client = getBots(0, NULL); client; client = getBots(0, client)) { + if(client->botid == BOTID) { + unbind_botwise_allcmd(0, client->clientid); + close_socket(client); + break; + } + } + } +} + +char* getSetting(struct UserNode *user, struct ChanNode *chan, const char *setting) { + char *uname = ""; + int cid = 0; + MYSQL_RES *res; + MYSQL_ROW row; + if(user) { + uname = ((user->flags & USERFLAG_ISAUTHED) ? user->auth : "*"); + } + if(chan) { + loadChannelSettings(chan); + if(chan->flags & CHANFLAG_CHAN_REGISTERED) + cid = chan->channel_id; + } + printf_mysql_query("SELECT `value` FROM `fundata` WHERE `user` = '%s' AND `cid` = '%d' AND `name` = '%s'", escape_string(uname), cid, escape_string(setting)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + return row[0]; + } else + return NULL; +} + +void setSetting(struct UserNode *user, struct ChanNode *chan, const char *setting, const char *value) { + char *uname = ""; + int cid = 0; + MYSQL_RES *res; + MYSQL_ROW row; + if(user) { + uname = ((user->flags & USERFLAG_ISAUTHED) ? user->auth : "*"); + } + if(chan) { + loadChannelSettings(chan); + if(chan->flags & CHANFLAG_CHAN_REGISTERED) + cid = chan->channel_id; + } + printf_mysql_query("SELECT `id`, `value` FROM `fundata` WHERE `user` = '%s' AND `cid` = '%d' AND `name` = '%s'", escape_string(uname), cid, escape_string(setting)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + if(strcmp(row[1], value)) + printf_mysql_query("UPDATE `fundata` SET `value` = '%s' WHERE `id` = '%s'", escape_string(value), row[0]); + } else + printf_mysql_query("INSERT INTO `fundata` (`user`, `cid`, `name`, `value`) VALUES ('%s', '%d', '%s', '%s')", escape_string(uname), cid, escape_string(setting), escape_string(value)); +} + +void uno_reply(struct uno_game *game, struct UserNode *user, const char *text, ...) { + struct ClientSocket *client = game->textbot; + const char *reply_format = get_language_string(user, text); + if(reply_format == NULL) + reply_format = text; + char formatBuf[MAXLEN]; + sprintf(formatBuf, "PRIVMSG %s :[UNO] %s", game->channel->name, reply_format); + va_list arg_list; + char sendBuf[MAXLEN]; + int pos; + if (!(client->flags & SOCKET_FLAG_CONNECTED)) return; + sendBuf[0] = '\0'; + va_start(arg_list, text); + pos = vsnprintf(sendBuf, MAXLEN - 2, formatBuf, arg_list); + va_end(arg_list); + if (pos < 0 || pos > (MAXLEN - 2)) pos = MAXLEN - 2; + sendBuf[pos] = '\n'; + sendBuf[pos+1] = '\0'; + write_socket(client, sendBuf, pos+1); +} + +void fourwins_reply(struct fourwins_game *game, const char *text, ...) { + struct ClientSocket *client = game->textbot; + struct fourwins_guest *guest; + int guest_count = 0; + for(guest = game->guests; guest; guest = guest->next) { + guest_count++; + } + struct ChanUser *chanusers[guest_count + 2]; + chanusers[0] = game->player[0]; + chanusers[1] = game->player[1]; + guest_count = 0; + for(guest = game->guests; guest; guest = guest->next) { + chanusers[2 + (guest_count++)] = guest->chanuser; + } + int i; + guest_count += 2; + for(i = 0; i < guest_count; i++) { + const char *reply_format = get_language_string(chanusers[i]->user, text); + if(reply_format == NULL) + reply_format = text; + char formatBuf[MAXLEN]; + sprintf(formatBuf, "NOTICE %s :[4WINS] %s", chanusers[i]->user->nick, reply_format); + va_list arg_list; + char sendBuf[MAXLEN]; + int pos; + if (!(client->flags & SOCKET_FLAG_CONNECTED)) return; + sendBuf[0] = '\0'; + va_start(arg_list, text); + pos = vsnprintf(sendBuf, MAXLEN - 2, formatBuf, arg_list); + va_end(arg_list); + if (pos < 0 || pos > (MAXLEN - 2)) pos = MAXLEN - 2; + sendBuf[pos] = '\n'; + sendBuf[pos+1] = '\0'; + write_socket(client, sendBuf, pos+1); + } +} + +void bj_reply(struct bj_game *game, struct UserNode *user, const char *text, ...) { + struct ClientSocket *client = game->textbot; + const char *reply_format = get_language_string(user, text); + if(reply_format == NULL) + reply_format = text; + char formatBuf[MAXLEN]; + sprintf(formatBuf, "PRIVMSG %s :[BJ] %s", game->channel->name, reply_format); + va_list arg_list; + char sendBuf[MAXLEN]; + int pos; + if (!(client->flags & SOCKET_FLAG_CONNECTED)) return; + sendBuf[0] = '\0'; + va_start(arg_list, text); + pos = vsnprintf(sendBuf, MAXLEN - 2, formatBuf, arg_list); + va_end(arg_list); + if (pos < 0 || pos > (MAXLEN - 2)) pos = MAXLEN - 2; + sendBuf[pos] = '\n'; + sendBuf[pos+1] = '\0'; + write_socket(client, sendBuf, pos+1); +} + +#undef BOTID +#undef BOTALIAS diff --git a/src/modules/NeonFun.mod/bot_NeonFun.h b/src/modules/NeonFun.mod/bot_NeonFun.h new file mode 100644 index 0000000..a2a9ea4 --- /dev/null +++ b/src/modules/NeonFun.mod/bot_NeonFun.h @@ -0,0 +1,37 @@ +/* bot_NeonFun.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _bot_NeonFun_h +#define _bot_NeonFun_h + +#include "../../main.h" + +void init_NeonFun(int type); +void free_NeonFun(int type); + +struct uno_game; +struct fourwins_game; +struct bj_game; +struct ChanNode; +struct UserNode; + +char* getSetting(struct UserNode *user, struct ChanNode *chan, const char *setting); +void setSetting(struct UserNode *user, struct ChanNode *chan, const char *setting, const char *value); +void uno_reply(struct uno_game *game, struct UserNode *user, const char *text, ...); +void fourwins_reply(struct fourwins_game *game, const char *text, ...); +void bj_reply(struct bj_game *game, struct UserNode *user, const char *text, ...); + +#endif \ No newline at end of file diff --git a/src/modules/NeonFun.mod/cmd_neonfun.c b/src/modules/NeonFun.mod/cmd_neonfun.c new file mode 100644 index 0000000..9bf3b4b --- /dev/null +++ b/src/modules/NeonFun.mod/cmd_neonfun.c @@ -0,0 +1,121 @@ +/* cmd_neonfun.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#include "../module.h" + +#include "cmd_neonfun.h" +#include "../../modcmd.h" +#include "../../lang.h" +#include "../../ConfigParser.h" + +static const struct default_language_entry msgtab[] = { + {"NF_UNO_ALREADY_RUNNING", "There is already a UNO game running in %s."}, /* {ARGS: "#channel"} */ + {"NF_UNO_ALREADY_JOINED", "You have already joined this UNO game."}, + {"NF_UNO_USER_JOINED", "$b%s$b has joined the game."}, /* {ARGS: "TestUser"} */ + {"NF_UNO_CREATED", "$b%s$b started an UNO game. Type $b+uno$b to join the game."}, /* {ARGS: "TestUser"} */ + {"NF_UNO_ERROR", "UNO program error. Please contact a bot administrator. (ErrCode: %d)"}, /* {ARGS: 0} */ + {"NF_UNO_START", "GAME START"}, + {"NF_UNO_USER_HURRY_UP", "$b%s$b, it's your turn!"}, /* {ARGS: "TestUser"} */ + {"NF_UNO_USER_TOOK_CARD", "$b%s$b took another card."}, /* {ARGS: "TestUser"} */ + {"NF_UNO_USER_TOOK_CARDS", "$b%s$b took $b%d$b cards."}, /* {ARGS: "TestUser", 4} */ + {"NF_UNO_YOUR_CARDS", "Your cards: %s"}, /* {ARGS: "[+2] [+4] [R5] ..."} */ + {"NF_UNO_TOP_CARD", "upper card: %s"}, /* {ARGS: "[+4]"} */ + {"NF_UNO_NOT_YOUR_TURN", "Wait your turn!"}, + {"NF_UNO_UNKNOWN_CARD", "Unknown card [%s]."}, /* {ARGS: "xy"} */ + {"NF_UNO_CARD_NOT_IN_DECK", "You don't have this card."}, + {"NF_UNO_CARD_YELLOW", "yellow"}, + {"NF_UNO_CARD_BLUE", "blue"}, + {"NF_UNO_CARD_RED", "red"}, + {"NF_UNO_CARD_GREEN", "green"}, + {"NF_UNO_DEFINE_COLOR", "Please define the color you want to set as a parameter to the command (eg. $b+unoplay +4 RED$b)"}, + {"NF_UNO_CARD_NOT_POSSIBLE", "You can't play this card."}, + {"NF_UNO_ONE_CARD", "$b%s$b has only one card! $k4U$k$k9N$k$k12O$k"}, /* {ARGS: "TestUser"} */ + {"NF_UNO_USER_WIN", "$b%s$b has 0 cards! $k4U$k$k9N$k$k12O$k $k4U$k$k9N$k$k12O$k!!!"}, /* {ARGS: "TestUser"} */ + {"NF_UNO_TIMEOUT", "game stopped (no more active players)."}, + {"NF_UNO_USER_SKIP", "skipped $b%s$b."}, /* {ARGS: "TestUser"} */ + {"NF_UNO_GAME_FINISHED", "The UNO Game has been finished!"}, + {"NF_UNO_ADD_CARD", "$b%s$b has to take up %d cards. There is still the possibility that he/she has another card of this type...?"}, + {"NF_UNO_RANK", "Rank"}, + {"NF_UNO_NAME", "Name"}, + {"NF_UNO_WON_GAMES", "Won Games"}, + {"NF_UNO_TOTAL_WON_GAMES", "Total won Games"}, + {"NF_UNO_LESS_PLAYERS", "There are not enough players to start the game."}, + + {"NF_4WINS_NOT_YOUR_TURN", "Wait your turn!"}, + {"NF_4WINS_INVALID_COLUMN", "invalid column!"}, + {"NF_4WINS_COLUMN_FULL", "This column is full!"}, + {"NF_4WINS_USER_HURRY_UP", "$b%s$b, it's your turn! To put a stone into a column use: 4stone "}, /* {ARGS: "TestUser"} */ + {"NF_4WINS_USER_WON", "$b%s$b won the game!"}, /* {ARGS: "TestUser"} */ + {"NF_4WINS_ENTER_OPPONENT", "Please enter the nick of the opponent you want to play against as a parameter of 4wins (eg. $b4wins TestUser$b)"}, + {"NF_4WINS_OPPONENT_NOT_IN_CHAN", "$b%s$b needs to be in this channel to start a game against him/her."}, /* {ARGS: "TestUser"} */ + {"NF_4WINS_DRAW", "Nobody has won this game."}, + {"NF_4WINS_REQUEST", "$b%s$b wants to play a game against you. use $b4wins$b to start the game."}, /* {ARGS: "TestUser"} */ + {"NF_4WINS_REQUESTED", "waiting for $b%s$b accepting the game..."}, /* {ARGS: "TestUser"} */ + {"NF_4WINS_START", "%s accepted the challenge! game starts:"}, /* {ARGS: "TestUser"} */ + {"NF_4WINS_APPEND_PLAYER", "Please append the nick of a player playing the game you want to view. (eg. $b4view TestUser$b)"}, + {"NF_4WINS_NO_GAME_FOUND", "Couldn't find a game with this user in this channel."}, + {"NF_4WINS_VIEWING_ANOTHER_GAME", "You are already viewing another game."}, + {"NF_4WINS_VIEWING_GAME", "You are now viewing the game."}, + {"NF_4WINS_GAME_CLOSED", "Game aborted."}, + {"NF_4WINS_TIMEOUT", "Game aborted (timeout)."}, + {"NF_4WINS_SELF", "You may not play against yourself."}, + + {"NF_BJ_ALREADY_RUNNING", "There is already a BlackJack game running in %s."}, /* {ARGS: "#channel"} */ + {"NF_BJ_ALREADY_JOINED", "You have already joined this BlackJack game."}, + {"NF_BJ_USER_JOINED", "$b%s$b has joined the game."}, /* {ARGS: "TestUser"} */ + {"NF_BJ_CREATED", "$b%s$b started a BlackJack game. Type $b+bj$b to join the game."}, /* {ARGS: "TestUser"} */ + {"NF_BJ_ERROR", "BlackJack program error. Please contact a bot administrator. (ErrCode: %d)"}, /* {ARGS: 0} */ + {"NF_BJ_NOT_YOUR_TURN", "Wait your turn!"}, + {"NF_BJ_START", "GAME START"}, + {"NF_BJ_USER_HURRY_UP", "$b%s$b, it's your turn! You can take a card with $bbjtake$b or stop playing with $bbjenough$b"}, /* {ARGS: "TestUser"} */ + {"NF_BJ_TAKE", "You took another card: %s (%d points)."}, /* {ARGS: "[♠A]", 11} */ + {"NF_BJ_YOUR_CARDS", "You have %d points! Your cards: %s"}, /* {ARGS: 18, "[♠A] [♣7] ..."} */ + {"NF_BJ_LESS_PLAYERS", "There are not enough players to start the game."}, + {"NF_BJ_USER_TIMEOUT", "It seems that %s doesn't want another card... (Timeout)"}, /* {ARGS: "TestUser"} */ + {"NF_BJ_POINTS_EXCEEDED", "You have more than 21 points... please use $bbjenough$b to finish."}, + {"NF_BJ_GAME_FINISHED", "Game Finished! Showdown:"}, + {"NF_BJ_RANK", "Rank"}, + {"NF_BJ_NAME", "Name"}, + {"NF_BJ_POINTS", "Points"}, + {"NF_BJ_CARDS", "Cards"}, + + {NULL, NULL} +}; + +void register_commands() { + //NeonFun Commands + register_default_language_table(msgtab); + register_command_alias(5, "NeonFun"); + + #define USER_COMMAND(NAME,FUNCTION,PARAMCOUNT,PRIVS,FLAGS) register_command(5, NAME, module_id, FUNCTION, PARAMCOUNT, PRIVS, 0, FLAGS) + // NAME FUNCTION PARAMS PRIVS FLAGS + USER_COMMAND("uno", neonfun_cmd_uno, 0, NULL, CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN); + USER_COMMAND("unotake", neonfun_cmd_unotake, 0, NULL, CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN); + USER_COMMAND("unoplay", neonfun_cmd_unoplay, 1, NULL, CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN); + + USER_COMMAND("4wins", neonfun_cmd_4wins, 0, NULL, CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN); + USER_COMMAND("4stone", neonfun_cmd_4stone, 1, NULL, CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN); + USER_COMMAND("4view", neonfun_cmd_4view, 0, NULL, CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN); + + USER_COMMAND("blackjack", neonfun_cmd_blackjack,0, NULL, CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN); + USER_COMMAND("bjtake", neonfun_cmd_bjtake, 0, NULL, CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN); + USER_COMMAND("bjenough", neonfun_cmd_bjenough, 0, NULL, CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN); + + #undef USER_COMMAND + +} + + diff --git a/src/modules/NeonFun.mod/cmd_neonfun.h b/src/modules/NeonFun.mod/cmd_neonfun.h new file mode 100644 index 0000000..fdccf0f --- /dev/null +++ b/src/modules/NeonFun.mod/cmd_neonfun.h @@ -0,0 +1,57 @@ +/* cmd_neonfun.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _cmd_neonfun_h +#define _cmd_neonfun_h +#include "../module.h" +#include "../../main.h" +#include "../../modcmd.h" +#include "../../IRCParser.h" +#include "../../IRCEvents.h" +#include "../../UserNode.h" +#include "../../ChanNode.h" +#include "../../ChanUser.h" +#include "../../ModeNode.h" +#include "../../BanNode.h" +#include "../../ClientSocket.h" +#include "../../mysqlConn.h" +#include "../../lang.h" +#include "../../HandleInfoHandler.h" +#include "../../WHOHandler.h" +#include "../../DBHelper.h" +#include "../../tools.h" +#include "../../timeq.h" +#include "../../version.h" +#include "../../EventLogger.h" +#include "../../bots.h" +#include "../../ConfigParser.h" +#include "bot_NeonFun.h" + +void register_commands(); + +CMD_BIND(neonfun_cmd_uno); +CMD_BIND(neonfun_cmd_unotake); +CMD_BIND(neonfun_cmd_unoplay); + +CMD_BIND(neonfun_cmd_4wins); +CMD_BIND(neonfun_cmd_4stone); +CMD_BIND(neonfun_cmd_4view); + +CMD_BIND(neonfun_cmd_blackjack); +CMD_BIND(neonfun_cmd_bjtake); +CMD_BIND(neonfun_cmd_bjenough); + +#endif diff --git a/src/modules/NeonFun.mod/cmd_neonfun_4stone.c b/src/modules/NeonFun.mod/cmd_neonfun_4stone.c new file mode 100644 index 0000000..334d98c --- /dev/null +++ b/src/modules/NeonFun.mod/cmd_neonfun_4stone.c @@ -0,0 +1,107 @@ +/* cmd_neonfun_4stone.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonfun.h" +#include "game_4wins.h" + +CMD_BIND(neonfun_cmd_4stone) { + struct ChanUser *chanuser = getChanUser(user, chan); + if(!chanuser) return; + struct fourwins_game *game; + for(game = fourwins_active_games; game; game = game->next) { + if(chanuser == game->player[0] || chanuser == game->player[1]) { + if(game->state == FOURWINS_STATE_WAITING) + return; + else + break; + } + } + if(game) { + //check if it's the player's turn + if(game->player[game->active_player] != chanuser) { + reply(textclient, user, "NF_4WINS_NOT_YOUR_TURN"); + return; + } + int x = atoi(argv[0])-1; + if(x < 0 || x >= FOURWINS_MATRIX_WIDTH) { + reply(textclient, user, "NF_4WINS_INVALID_COLUMN"); + return; + } + int y = fourwins_next_free_y(game, x); + if(y < 0) { + reply(textclient, user, "NF_4WINS_COLUMN_FULL"); + return; + } + timeq_del(game->timer); + game->timer = NULL; + game->matrix[x][y].field = game->active_player+1; + fourwins_show_matrix(game); + if(fourwins_check_win(game, x, y)) { + fourwins_reply(game, "NF_4WINS_USER_WON", game->player[game->active_player]->user->nick); + if((game->player[game->active_player]->user->flags & USERFLAG_ISAUTHED)) { + char *tmp; + int win_count = ((tmp = getSetting(game->player[game->active_player]->user, chan, "4wins_win")) ? atoi(tmp) : 0); + int total_win_count = ((tmp = getSetting(game->player[game->active_player]->user, NULL, "4wins_win")) ? atoi(tmp) : 0); + win_count++; + total_win_count++; + char buf[10]; + sprintf(buf, "%d", win_count); + setSetting(game->player[game->active_player]->user, chan, "4wins_win", buf); + sprintf(buf, "%d", total_win_count); + setSetting(game->player[game->active_player]->user, NULL, "4wins_win", buf); + } + fourwins_free_game(game); + } else { + game->total_stones++; + if(game->total_stones == FOURWINS_MAX_STONES) { + fourwins_reply(game, "NF_4WINS_DRAW"); + if((game->player[0]->user->flags & USERFLAG_ISAUTHED)) { + char *tmp; + int win_count = ((tmp = getSetting(game->player[0]->user, chan, "4wins_draw")) ? atoi(tmp) : 0); + int total_win_count = ((tmp = getSetting(game->player[0]->user, NULL, "4wins_draw")) ? atoi(tmp) : 0); + win_count++; + total_win_count++; + char buf[10]; + sprintf(buf, "%d", win_count); + setSetting(game->player[0]->user, chan, "4wins_draw", buf); + sprintf(buf, "%d", total_win_count); + setSetting(game->player[0]->user, NULL, "4wins_draw", buf); + } + if((game->player[1]->user->flags & USERFLAG_ISAUTHED)) { + char *tmp; + int win_count = ((tmp = getSetting(game->player[1]->user, chan, "4wins_draw")) ? atoi(tmp) : 0); + int total_win_count = ((tmp = getSetting(game->player[1]->user, NULL, "4wins_draw")) ? atoi(tmp) : 0); + win_count++; + total_win_count++; + char buf[10]; + sprintf(buf, "%d", win_count); + setSetting(game->player[1]->user, chan, "4wins_draw", buf); + sprintf(buf, "%d", total_win_count); + setSetting(game->player[1]->user, NULL, "4wins_draw", buf); + } + fourwins_free_game(game); + return; + } + if(game->active_player) + game->active_player = 0; + else + game->active_player = 1; + fourwins_reply(game, "NF_4WINS_USER_HURRY_UP", game->player[game->active_player]->user->nick); + game->timer = timeq_add(120, module_id, fourwins_timeout, game); + } + } +} diff --git a/src/modules/NeonFun.mod/cmd_neonfun_4view.c b/src/modules/NeonFun.mod/cmd_neonfun_4view.c new file mode 100644 index 0000000..b912e78 --- /dev/null +++ b/src/modules/NeonFun.mod/cmd_neonfun_4view.c @@ -0,0 +1,57 @@ +/* cmd_neonfun_4view.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonfun.h" +#include "game_4wins.h" + +CMD_BIND(neonfun_cmd_4view) { + struct UserNode *gameuser; + struct ChanUser *chanuser; + if(!argc || !(gameuser = getUserByNick(argv[0])) || !(chanuser = getChanUser(gameuser, chan))) { + reply(textclient, user, "NF_4WINS_APPEND_PLAYER"); + return; + } + struct fourwins_game *game, *fgame = NULL; + struct fourwins_guest *guest; + for(game = fourwins_active_games; game; game = game->next) { + for(guest = game->guests; guest; guest = guest->next) { + if(guest->chanuser->user == user) { + reply(textclient, user, "NF_4WINS_VIEWING_ANOTHER_GAME"); + return; + } + } + if(game->player[0]->user == user || game->player[1]->user == user) { + reply(textclient, user, "NF_4WINS_VIEWING_ANOTHER_GAME"); + return; + } + if(chanuser == game->player[0] || chanuser == game->player[1]) { + fgame = game; + } + } + game = fgame; + if(game) { + guest = malloc(sizeof(*guest)); + chanuser = getChanUser(user, chan); + if(!chanuser) return; + guest->chanuser = chanuser; + guest->next = game->guests; + game->guests = guest; + reply(textclient, user, "NF_4WINS_VIEWING_GAME"); + } else { + reply(textclient, user, "NF_4WINS_NO_GAME_FOUND"); + } +} diff --git a/src/modules/NeonFun.mod/cmd_neonfun_4wins.c b/src/modules/NeonFun.mod/cmd_neonfun_4wins.c new file mode 100644 index 0000000..47acf18 --- /dev/null +++ b/src/modules/NeonFun.mod/cmd_neonfun_4wins.c @@ -0,0 +1,102 @@ +/* cmd_neonfun_4wins.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonfun.h" +#include "game_4wins.h" + +CMD_BIND(neonfun_cmd_4wins) { + struct ChanUser *chanuser = getChanUser(user, chan); + if(!chanuser) return; + struct fourwins_game *game; + for(game = fourwins_active_games; game; game = game->next) { + if(chanuser == game->player[0]) { + return; + } else if(chanuser == game->player[1]) { + if(game->state == FOURWINS_STATE_WAITING) + break; + else + return; + } + } + if(game) { + timeq_del(game->timer); + game->timer = NULL; + game->state = FOURWINS_STATE_RUNNING; + if((game->player[0]->user->flags & USERFLAG_ISAUTHED) && (game->player[1]->user->flags & USERFLAG_ISAUTHED) && !stricmp(game->player[0]->user->auth, game->player[1]->user->auth)) { + fourwins_reply(game, "NF_4WINS_SELF"); + fourwins_free_game(game); + return; + } + fourwins_reply(game, "NF_4WINS_START", user->nick); + if((game->player[0]->user->flags & USERFLAG_ISAUTHED)) { + char *tmp; + int win_count = ((tmp = getSetting(game->player[0]->user, chan, "4wins_games")) ? atoi(tmp) : 0); + int total_win_count = ((tmp = getSetting(game->player[0]->user, NULL, "4wins_games")) ? atoi(tmp) : 0); + win_count++; + total_win_count++; + char buf[10]; + sprintf(buf, "%d", win_count); + setSetting(game->player[0]->user, chan, "4wins_games", buf); + sprintf(buf, "%d", total_win_count); + setSetting(game->player[0]->user, NULL, "4wins_games", buf); + } + if((game->player[1]->user->flags & USERFLAG_ISAUTHED)) { + char *tmp; + int win_count = ((tmp = getSetting(game->player[1]->user, chan, "4wins_games")) ? atoi(tmp) : 0); + int total_win_count = ((tmp = getSetting(game->player[1]->user, NULL, "4wins_games")) ? atoi(tmp) : 0); + win_count++; + total_win_count++; + char buf[10]; + sprintf(buf, "%d", win_count); + setSetting(game->player[1]->user, chan, "4wins_games", buf); + sprintf(buf, "%d", total_win_count); + setSetting(game->player[1]->user, NULL, "4wins_games", buf); + } + fourwins_show_matrix(game); + fourwins_reply(game, "NF_4WINS_USER_HURRY_UP", game->player[game->active_player]->user->nick); + game->timer = timeq_add(120, module_id, fourwins_timeout, game); + } else { + if(!argc) { + reply(textclient, user, "NF_4WINS_ENTER_OPPONENT"); + return; + } + struct UserNode *opp_user = getUserByNick(argv[0]); + if(!opp_user) { + reply(textclient, user, "NS_USER_UNKNOWN", argv[0]); + return; + } + if(opp_user == user) { + reply(textclient, user, "NF_4WINS_SELF"); + return; + } + struct ChanUser *opponent = getChanUser(opp_user, chan); + if(!opponent) { + reply(textclient, user, "NF_4WINS_OPPONENT_NOT_IN_CHAN", opp_user->nick); + return; + } + game = calloc(1,sizeof(*game)); + game->player[0] = chanuser; + game->player[1] = opponent; + game->textbot = textclient; + game->state = FOURWINS_STATE_WAITING; + game->timer = timeq_add(120, module_id, fourwins_timeout, game); + game->next = fourwins_active_games; + fourwins_active_games = game; + reply(textclient, user, "NF_4WINS_REQUESTED", opp_user->nick); + reply(textclient, opp_user, "NF_4WINS_REQUEST", user->nick); + } +} diff --git a/src/modules/NeonFun.mod/cmd_neonfun_bjenough.c b/src/modules/NeonFun.mod/cmd_neonfun_bjenough.c new file mode 100644 index 0000000..da1ff9b --- /dev/null +++ b/src/modules/NeonFun.mod/cmd_neonfun_bjenough.c @@ -0,0 +1,41 @@ +/* cmd_neonfun_bjenough.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonfun.h" +#include "game_blackjack.h" + +CMD_BIND(neonfun_cmd_bjenough) { + struct ChanUser *chanuser = getChanUser(user, chan); + if(!chanuser) return; + struct bj_game *game; + for(game = bj_active_games; game; game = game->next) { + if(chan == game->channel) { + if(game->state == BJ_STATE_WAITING) + return; + else + break; + } + } + if(game) { + //check if it's the player's turn + if(game->active_player->chanuser != chanuser) { + reply(textclient, user, "NF_BJ_NOT_YOUR_TURN"); + return; + } + bj_action_next_player(game, game->active_player); + } +} diff --git a/src/modules/NeonFun.mod/cmd_neonfun_bjtake.c b/src/modules/NeonFun.mod/cmd_neonfun_bjtake.c new file mode 100644 index 0000000..91b3f09 --- /dev/null +++ b/src/modules/NeonFun.mod/cmd_neonfun_bjtake.c @@ -0,0 +1,41 @@ +/* cmd_neonfun_bjtake.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonfun.h" +#include "game_blackjack.h" + +CMD_BIND(neonfun_cmd_bjtake) { + struct ChanUser *chanuser = getChanUser(user, chan); + if(!chanuser) return; + struct bj_game *game; + for(game = bj_active_games; game; game = game->next) { + if(chan == game->channel) { + if(game->state == BJ_STATE_WAITING) + return; + else + break; + } + } + if(game) { + //check if it's the player's turn + if(game->active_player->chanuser != chanuser) { + reply(textclient, user, "NF_BJ_NOT_YOUR_TURN"); + return; + } + bj_action_take_card(game, game->active_player); + } +} diff --git a/src/modules/NeonFun.mod/cmd_neonfun_blackjack.c b/src/modules/NeonFun.mod/cmd_neonfun_blackjack.c new file mode 100644 index 0000000..31fe45c --- /dev/null +++ b/src/modules/NeonFun.mod/cmd_neonfun_blackjack.c @@ -0,0 +1,69 @@ +/* cmd_neonfun_blackjack.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonfun.h" +#include "game_blackjack.h" + +CMD_BIND(neonfun_cmd_blackjack) { + struct ChanUser *chanuser = getChanUser(user, chan); + if(!chanuser) return; + struct bj_game *game; + for(game = bj_active_games; game; game = game->next) { + if(chan == game->channel) + break; + } + if(game) { + //check if player has already joined + struct bj_player *player, *last_player = NULL; + for(player = game->player; player; player = player->next) { + if(player->chanuser == chanuser) { + reply(textclient, user, "NF_BJ_ALREADY_JOINED"); + return; + } else + last_player = player; + } + if(!last_player) return; //error (game without players?) + player = malloc(sizeof(*player)); + player->chanuser = chanuser; + player->count = 0; + player->cards = NULL; + player->prev = last_player; + player->next = NULL; + last_player->next = player; + game->players++; + bj_reply(game, user, "NF_BJ_USER_JOINED", user->nick); + } else { + game = malloc(sizeof(*game)); + game->channel = chan; + game->textbot = textclient; + game->state = BJ_STATE_WAITING; + game->deck = NULL; + struct bj_player *player = malloc(sizeof(*player)); + player->chanuser = chanuser; + player->count = 0; + player->cards = NULL; + player->prev = NULL; + player->next = NULL; + game->player = player; + game->active_player = NULL; + game->players = 1; + game->timer = timeq_add(30, module_id, bj_game_wait_timeout, game); + game->next = bj_active_games; + bj_active_games = game; + bj_reply(game, user, "NF_BJ_CREATED", user->nick); + } +} diff --git a/src/modules/NeonFun.mod/cmd_neonfun_uno.c b/src/modules/NeonFun.mod/cmd_neonfun_uno.c new file mode 100644 index 0000000..5742fa0 --- /dev/null +++ b/src/modules/NeonFun.mod/cmd_neonfun_uno.c @@ -0,0 +1,81 @@ +/* cmd_neonfun_uno.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonfun.h" +#include "game_uno.h" + +CMD_BIND(neonfun_cmd_uno) { + struct ChanUser *chanuser = getChanUser(user, chan); + if(!chanuser) return; + struct uno_game *game; + for(game = uno_active_games; game; game = game->next) { + if(chan == game->channel) { + if(game->state == UNO_STATE_WAITING) + break; + else { + reply(textclient, user, "NF_UNO_ALREADY_RUNNING", chan->name); + return; + } + } + } + if(game) { + //check if player has already joined + struct uno_player *player, *last_player = NULL; + for(player = game->player; player; player = player->next) { + if(player->chanuser == chanuser) { + reply(textclient, user, "NF_UNO_ALREADY_JOINED"); + return; + } else + last_player = player; + } + if(!last_player) return; //error (game without players?) + player = malloc(sizeof(*player)); + player->chanuser = chanuser; + player->count = 0; + player->cards = NULL; + player->timeout = 0; + player->prev = last_player; + player->next = NULL; + last_player->next = player; + game->players++; + uno_reply(game, user, "NF_UNO_USER_JOINED", user->nick); + } else { + game = malloc(sizeof(*game)); + game->channel = chan; + game->textbot = textclient; + game->state = UNO_STATE_WAITING; + game->reverse_direction = 0; + game->take_cards_pending = 0; + game->deck = NULL; + game->top_card = NULL; + struct uno_player *player = malloc(sizeof(*player)); + player->chanuser = chanuser; + player->count = 0; + player->cards = NULL; + player->timeout = 0; + player->prev = NULL; + player->next = NULL; + game->player = player; + game->winner = NULL; + game->active_player = NULL; + game->players = 1; + game->timer = timeq_add(30, module_id, uno_game_wait_timeout, game); + game->next = uno_active_games; + uno_active_games = game; + uno_reply(game, user, "NF_UNO_CREATED", user->nick); + } +} diff --git a/src/modules/NeonFun.mod/cmd_neonfun_unoplay.c b/src/modules/NeonFun.mod/cmd_neonfun_unoplay.c new file mode 100644 index 0000000..fcb92ef --- /dev/null +++ b/src/modules/NeonFun.mod/cmd_neonfun_unoplay.c @@ -0,0 +1,68 @@ +/* cmd_neonfun_unoplay.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonfun.h" +#include "game_uno.h" + +CMD_BIND(neonfun_cmd_unoplay) { + struct ChanUser *chanuser = getChanUser(user, chan); + if(!chanuser) return; + struct uno_game *game; + for(game = uno_active_games; game; game = game->next) { + if(chan == game->channel) { + if(game->state == UNO_STATE_WAITING) + return; + else + break; + } + } + if(game) { + //check if it's the player's turn + if(game->active_player->chanuser != chanuser) { + reply(textclient, user, "NF_UNO_NOT_YOUR_TURN"); + return; + } + game->active_player->timeout = 0; + struct uno_card *card = uno_parse_card(game, game->active_player, argv[0]); + if(!card) return; + unsigned char new_color = card->color; + if(card->card == UNO_CARD_ADD_4 || card->card == UNO_CARD_COLOR) { + if(argc < 2) { + reply(game->textbot, user, "NF_UNO_DEFINE_COLOR"); + return; + } + if(!stricmp(argv[1], "RED") || !stricmp(argv[1], "R") || !stricmp(argv[1], get_language_string(user, "NF_UNO_CARD_RED"))) { + new_color = UNO_COLOR_RED; + } else if(!stricmp(argv[1], "BLUE") || !stricmp(argv[1], "B") || !stricmp(argv[1], get_language_string(user, "NF_UNO_CARD_BLUE"))) { + new_color = UNO_COLOR_BLUE; + } else if(!stricmp(argv[1], "GREEN") || !stricmp(argv[1], "G") || !stricmp(argv[1], get_language_string(user, "NF_UNO_CARD_GREEN"))) { + new_color = UNO_COLOR_GREEN; + } else if(!stricmp(argv[1], "YELLOW") || !stricmp(argv[1], "Y") || !stricmp(argv[1], get_language_string(user, "NF_UNO_CARD_YELLOW"))) { + new_color = UNO_COLOR_YELLOW; + } else { + reply(game->textbot, user, "NF_UNO_DEFINE_COLOR"); + return; + } + } + if(uno_check_card_valid(game, card)) { + reply(game->textbot, user, "NF_UNO_CARD_NOT_POSSIBLE"); + return; + } + card->color = new_color; + uno_play_card(game, game->active_player, card); + } +} diff --git a/src/modules/NeonFun.mod/cmd_neonfun_unotake.c b/src/modules/NeonFun.mod/cmd_neonfun_unotake.c new file mode 100644 index 0000000..e66a34c --- /dev/null +++ b/src/modules/NeonFun.mod/cmd_neonfun_unotake.c @@ -0,0 +1,42 @@ +/* cmd_neonfun_unotake.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonfun.h" +#include "game_uno.h" + +CMD_BIND(neonfun_cmd_unotake) { + struct ChanUser *chanuser = getChanUser(user, chan); + if(!chanuser) return; + struct uno_game *game; + for(game = uno_active_games; game; game = game->next) { + if(chan == game->channel) { + if(game->state == UNO_STATE_WAITING) + return; + else + break; + } + } + if(game) { + //check if it's the player's turn + if(game->active_player->chanuser != chanuser) { + reply(textclient, user, "NF_UNO_NOT_YOUR_TURN"); + return; + } + game->active_player->timeout = 0; + uno_action_take_card(game, game->active_player); + } +} diff --git a/src/modules/NeonFun.mod/game_4wins.c b/src/modules/NeonFun.mod/game_4wins.c new file mode 100644 index 0000000..7bcd873 --- /dev/null +++ b/src/modules/NeonFun.mod/game_4wins.c @@ -0,0 +1,190 @@ +/* game_4wins.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#include "../module.h" +#include "game_4wins.h" +#include "bot_NeonFun.h" +#include "../../IRCParser.h" +#include "../../bots.h" +#include "../../UserNode.h" +#include "../../ChanUser.h" +#include "../../tools.h" +#include "../botid.h" + +struct fourwins_game *fourwins_active_games = NULL; + +int fourwins_next_free_y(struct fourwins_game *game, int x) { + int y; + for(y = 0; y < FOURWINS_MATRIX_HEIGHT; y++) { + if(!game->matrix[x][y].field) + return y; + } + return -1; +} + +int fourwins_check_win(struct fourwins_game *game, int x, int y) { + int field = game->matrix[x][y].field; + if(!field) return 0; + //horizontal + { + int fc = 0; + int i; + for(i = x; i < FOURWINS_MATRIX_WIDTH; i++) { + if(game->matrix[i][y].field == field) + fc++; + else + break; + } + for(i = x-1; i >= 0; i--) { + if(game->matrix[i][y].field == field) + fc++; + else + break; + } + if(fc >= 4) return 1; + } + //senkrecht + if(y >= 3) { + int fc = 0; + int i; + for(i = y; i >= 0; i--) { + if(game->matrix[i][y].field == field) + fc++; + else + break; + } + if(fc >= 4) return 1; + } + //diagonal 1 + { + int fc = 0; + int ix, iy; + for(ix = x, iy = y; ix < FOURWINS_MATRIX_WIDTH && iy < FOURWINS_MATRIX_HEIGHT; ix++, iy++) { + if(game->matrix[ix][iy].field == field) + fc++; + else + break; + } + for(ix = x-1, iy = y-1; ix >= 0 && iy >= 0; ix--, iy--) { + if(game->matrix[ix][iy].field == field) + fc++; + else + break; + } + if(fc >= 4) return 1; + } + //diagonal 2 + { + int fc = 0; + int ix, iy; + for(ix = x, iy = y; ix < FOURWINS_MATRIX_WIDTH && iy >= 0; ix++, iy--) { + if(game->matrix[ix][iy].field == field) + fc++; + else + break; + } + for(ix = x-1, iy = y+1; ix >= 0 && iy < FOURWINS_MATRIX_HEIGHT; ix--, iy++) { + if(game->matrix[ix][iy].field == field) + fc++; + else + break; + } + if(fc >= 4) return 1; + } + return 0; +} + +void fourwins_show_matrix(struct fourwins_game *game) { + int x,y; + char lineBuf[MAXLEN]; + int linePos = 0; + for(x = 0; x < FOURWINS_MATRIX_WIDTH; x++) { + linePos += sprintf(lineBuf+linePos, (x ? "| %d " : " %d "), x+1); + } + fourwins_reply(game, lineBuf); + for(y = FOURWINS_MATRIX_HEIGHT-1; y >= 0; y--) { + linePos = 0; + for(x = 0; x < FOURWINS_MATRIX_WIDTH; x++) { + char *field = " "; + if(game->matrix[x][y].field == 1) + field = "\0034o\003"; + if(game->matrix[x][y].field == 2) + field = "\00312o\003"; + linePos += sprintf(lineBuf+linePos, (x ? " (%s)" : "(%s)"), field); + } + fourwins_reply(game, lineBuf); + } +} + +TIMEQ_CALLBACK(fourwins_timeout) { + struct fourwins_game *game = data; + game->timer = NULL; + fourwins_reply(game, "NF_4WINS_TIMEOUT"); + fourwins_free_game(game); +} + +void fourwins_free_game(struct fourwins_game *game) { + struct fourwins_guest *guest, *next_guest; + for(guest = game->guests; guest; guest = next_guest) { + next_guest = guest->next; + free(guest); + } + struct fourwins_game *cgame, *prev = NULL; + for(cgame = fourwins_active_games; cgame; cgame = cgame->next) { + if(cgame == game) { + if(prev) + prev->next = game->next; + else + fourwins_active_games = game->next; + break; + } else + prev = cgame; + } + free(game); +} + +void fourwins_event_part(struct ChanUser *chanuser) { + struct fourwins_game *game; + for(game = fourwins_active_games; game; game = game->next) { + if(game->player[0] == chanuser || game->player[1] == chanuser) { + fourwins_reply(game, "NF_4WINS_GAME_CLOSED"); + fourwins_free_game(game); + return; + } + struct fourwins_guest *guest, *prev_guest = NULL; + for(guest = game->guests; guest; guest = guest->next) { + if(guest->chanuser == chanuser) { + if(prev_guest) + prev_guest->next = guest->next; + else + game->guests = guest->next; + free(guest); + break; + } else + prev_guest = guest; + } + } +} + +void fourwins_event_freechan(struct ChanNode *chan) { + struct fourwins_game *game; + for(game = fourwins_active_games; game; game = game->next) { + if(game->player[0]->chan == chan) { + fourwins_free_game(game); + return; + } + } +} diff --git a/src/modules/NeonFun.mod/game_4wins.h b/src/modules/NeonFun.mod/game_4wins.h new file mode 100644 index 0000000..986f859 --- /dev/null +++ b/src/modules/NeonFun.mod/game_4wins.h @@ -0,0 +1,62 @@ +/* game_4wins.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _game_4wins_h +#define _game_4wins_h +#include "../../timeq.h" + +#define FOURWINS_STATE_WAITING 1 +#define FOURWINS_STATE_RUNNING 2 + +#define FOURWINS_MATRIX_WIDTH 7 +#define FOURWINS_MATRIX_HEIGHT 6 + +#define FOURWINS_MAX_STONES (FOURWINS_MATRIX_WIDTH * FOURWINS_MATRIX_HEIGHT) + +struct UserNode; +struct ChanNode; +struct ChanUser; +struct ClientSocket; +extern struct fourwins_game *fourwins_active_games; + +struct fourwins_guest { + struct ChanUser *chanuser; + struct fourwins_guest *next; +}; + +struct fourwins_game { + struct ChanUser *player[2]; + int active_player; + struct fourwins_guest *guests; + struct ClientSocket *textbot; + int state : 3; + struct { + unsigned int field:2; /* 0,1,2 */ + } matrix[FOURWINS_MATRIX_WIDTH][FOURWINS_MATRIX_HEIGHT]; + int total_stones; + struct timeq_entry *timer; + struct fourwins_game *next; +}; + +int fourwins_next_free_y(struct fourwins_game *game, int x); +int fourwins_check_win(struct fourwins_game *game, int x, int y); +void fourwins_show_matrix(struct fourwins_game *game); +TIMEQ_CALLBACK(fourwins_timeout); +void fourwins_free_game(struct fourwins_game *game); +void fourwins_event_part(struct ChanUser *chanuser); +void fourwins_event_freechan(struct ChanNode *chan); + +#endif diff --git a/src/modules/NeonFun.mod/game_blackjack.c b/src/modules/NeonFun.mod/game_blackjack.c new file mode 100644 index 0000000..f2576dc --- /dev/null +++ b/src/modules/NeonFun.mod/game_blackjack.c @@ -0,0 +1,421 @@ +/* game_blackjack.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#include "../module.h" +#include "game_blackjack.h" +#include "bot_NeonFun.h" +#include "../../IRCParser.h" +#include "../../bots.h" +#include "../../UserNode.h" +#include "../../ChanUser.h" +#include "../../tools.h" +#include "../botid.h" + +struct bj_game *bj_active_games = NULL; + +static int bj_ctype[] = {BJ_CTYPE_HEARTS, BJ_CTYPE_SPADES, BJ_CTYPE_DIAMONDS, BJ_CTYPE_CLUBS}; +static int bj_irc_colors[] = {4, 1, 4, 1}; +static char *bj_color_chars[] = {"\xE2\x9D\xA4", "\xE2\x99\xA0", "\xE2\x99\xA6", "\xE2\x99\xA3"}; + +static const struct { + const char *name; + unsigned char type; + unsigned int points : 8; +} bj_card_types[] = { + {"2", BJ_CARD_NUMBER_2, 2}, + {"3", BJ_CARD_NUMBER_3, 3}, + {"4", BJ_CARD_NUMBER_4, 4}, + {"5", BJ_CARD_NUMBER_5, 5}, + {"6", BJ_CARD_NUMBER_6, 6}, + {"7", BJ_CARD_NUMBER_7, 7}, + {"8", BJ_CARD_NUMBER_8, 8}, + {"9", BJ_CARD_NUMBER_9, 9}, + {"10", BJ_CARD_NUMBER_10, 10}, + {"J", BJ_CARD_JACK, 10}, + {"Q", BJ_CARD_QUEEN, 10}, + {"K", BJ_CARD_KING, 10}, + {"A", BJ_CARD_ACE, 11}, + {NULL, 0} +}; + +struct bj_card_deck *bj_shuffle_deck() { + struct bj_card *card, *last_card = NULL; + int card_count = 0; + #define ADD_CARD(ctype,ccard) \ + card = malloc(sizeof(*card)); \ + card->type = ctype; \ + card->card = ccard; \ + card->prev = NULL; \ + card->next = last_card; \ + if(last_card) \ + last_card->prev = card; \ + last_card = card; \ + card_count++; + int typecount = BJ_CTYPE_COUNT; + for(typecount--; typecount >= 0; typecount--) { + ADD_CARD(bj_ctype[typecount], BJ_CARD_NUMBER_2); + ADD_CARD(bj_ctype[typecount], BJ_CARD_NUMBER_3); + ADD_CARD(bj_ctype[typecount], BJ_CARD_NUMBER_4); + ADD_CARD(bj_ctype[typecount], BJ_CARD_NUMBER_5); + ADD_CARD(bj_ctype[typecount], BJ_CARD_NUMBER_6); + ADD_CARD(bj_ctype[typecount], BJ_CARD_NUMBER_7); + ADD_CARD(bj_ctype[typecount], BJ_CARD_NUMBER_8); + ADD_CARD(bj_ctype[typecount], BJ_CARD_NUMBER_9); + ADD_CARD(bj_ctype[typecount], BJ_CARD_NUMBER_10); + ADD_CARD(bj_ctype[typecount], BJ_CARD_JACK); + ADD_CARD(bj_ctype[typecount], BJ_CARD_QUEEN); + ADD_CARD(bj_ctype[typecount], BJ_CARD_KING); + ADD_CARD(bj_ctype[typecount], BJ_CARD_ACE); + } + #undef ADD_CARD + struct bj_card_deck *deck = malloc(sizeof(*deck)); + deck->cards = last_card; + deck->count = card_count; + return deck; +} + +struct bj_card *bj_get_card(struct bj_card_deck *deck) { + if(!deck->count) return NULL; + int card_id = (rand() % deck->count); + int i = 0; + struct bj_card *card; + for(card = deck->cards; card; card = card->next) { + if(i == card_id) { + if(card->prev) + card->prev->next = card->next; + else + deck->cards = card->next; + if(card->next) + card->next->prev = card->prev; + deck->count--; + card->next = NULL; + card->prev = NULL; + return card; + } + i++; + } + return NULL; +} + +void bj_free_deck(struct bj_card_deck *deck) { + struct bj_card *card, *next_card; + if(deck->count) { + for(card = deck->cards; card; card = next_card) { + next_card = card->next; + free(card); + } + } + free(deck); +} + +void bj_free_player(struct bj_player *player, struct bj_card_deck *deck) { + if(player->count) { + struct bj_card *card, *next_card; + for(card = player->cards; card; card = next_card) { + next_card = card->next; + if(deck) { + card->next = deck->cards; + deck->cards->prev = card; + deck->cards = card; + deck->count++; + } else + free(card); + } + } + if(player->prev) + player->prev->next = player->next; + if(player->next) + player->next->prev = player->prev; + free(player); +} + +void bj_free_game(struct bj_game *game) { + struct bj_player *player, *next_player; + for(player = game->player; player; player = next_player) { + next_player = player->next; + bj_free_player(player, NULL); + } + if(game->deck) + bj_free_deck(game->deck); + if(game->timer) + timeq_del(game->timer); + struct bj_game *cgame, *pgame = NULL; + for(cgame = bj_active_games; cgame; cgame = cgame->next) { + if(cgame == game) { + if(pgame) + pgame->next = game->next; + else + bj_active_games = game->next; + break; + } else + pgame = cgame; + } + free(game); +} + +struct bj_player *bj_get_next_player(struct bj_game *game) { + struct bj_player *player = game->active_player; + player = player->next; + return player; +} + +int bj_get_player_card_points(struct bj_player *player) { + int i, points = 0, ace_count = 0; + struct bj_card *card; + for(card = player->cards; card; card = card->next) { + if(card->card == BJ_CARD_ACE) { + ace_count++; //add these points later ;) + continue; + } + i = 0; + while(bj_card_types[i].name) { + if(bj_card_types[i].type == card->card) { + points += bj_card_types[i].points; + break; + } + i++; + } + } + while((ace_count--) > 0) { + if(points <= 10) + points += 11; + else + points += 1; + } + return points; +} + +static void bj_print_player_cards(struct bj_player *player, char *cards_buf) { + struct bj_card *card; + int cards_bufpos = 0; + cards_buf[0] = '\0'; + for(card = player->cards; card; card = card->next) { + int cardcolor = 1; + char *cardchar = ""; + int i; + for(i = 0; i < BJ_CTYPE_COUNT; i++) { + if(bj_ctype[i] == card->type) { + cardcolor = bj_irc_colors[i]; + cardchar = bj_color_chars[i]; + break; + } + } + i = 0; + while(bj_card_types[i].name) { + if(bj_card_types[i].type == card->card) { + cards_bufpos += sprintf(cards_buf + cards_bufpos, "%s[\003%d%s%s\003]", (cards_bufpos ? " " : ""), cardcolor, cardchar, bj_card_types[i].name); + break; + } + i++; + } + } +} + +void bj_show_player_cards(struct bj_game *game, struct bj_player *player) { + char cards_buf[MAXLEN]; + bj_print_player_cards(player, cards_buf); + int card_points = bj_get_player_card_points(player); + reply(game->textbot, player->chanuser->user, "NF_BJ_YOUR_CARDS", card_points, cards_buf); +} + +TIMEQ_CALLBACK(bj_game_wait_timeout) { + struct bj_game *game = data; + game->timer = NULL; + if(game->players == 1) { + bj_reply(game, NULL, "NF_BJ_LESS_PLAYERS"); + bj_free_game(game); + return; + } + game->deck = bj_shuffle_deck(); + bj_reply(game, NULL, "NF_BJ_START"); + game->state = BJ_STATE_RUNNING; + game->active_player = game->player; //active player + bj_reply(game, NULL, "NF_BJ_USER_HURRY_UP", game->active_player->chanuser->user->nick); + game->timer = timeq_add(30, module_id, bj_player_timeout, game); +} + +TIMEQ_CALLBACK(bj_player_timeout) { + struct bj_game *game = data; + game->timer = NULL; + //player timeout (next player) + struct bj_player *player = game->active_player; + bj_reply(game, NULL, "NF_BJ_USER_TIMEOUT", player->chanuser->user->nick); + bj_action_next_player(game, player); +} + +void bj_action_take_card(struct bj_game *game, struct bj_player *player) { + int points = bj_get_player_card_points(player); + if(points > 21) { + reply(game->textbot, player->chanuser->user, "NF_BJ_POINTS_EXCEEDED"); + return; + } + if(game->timer) { + timeq_del(game->timer); + game->timer = NULL; + } + struct bj_card *card = bj_get_card(game->deck); + if(!card) { + bj_free_deck(game->deck); + game->deck = bj_shuffle_deck(); + card = bj_get_card(game->deck); + } + struct bj_card *last_card; + for(last_card = player->cards; last_card; last_card = last_card->next) { + if(last_card->next == NULL) + break; + } + card->prev = last_card; + if(last_card) + last_card->next = card; + else + player->cards = card; + player->count++; + char cardbuf[16]; + int cardcolor = 1; + char *cardchar = ""; + int i, cardpoints = 0; + cardbuf[0] = '\0'; + for(i = 0; i < BJ_CTYPE_COUNT; i++) { + if(bj_ctype[i] == card->type) { + cardcolor = bj_irc_colors[i]; + cardchar = bj_color_chars[i]; + break; + } + } + i = 0; + while(bj_card_types[i].name) { + if(bj_card_types[i].type == card->card) { + sprintf(cardbuf, "[\003%d%s%s\003]", cardcolor, cardchar, bj_card_types[i].name); + cardpoints = bj_card_types[i].points; + if(bj_card_types[i].type == BJ_CARD_ACE && points > 10) + cardpoints = 1; + break; + } + i++; + } + reply(game->textbot, player->chanuser->user, "NF_BJ_TAKE", cardbuf, cardpoints); + bj_show_player_cards(game, player); + points = bj_get_player_card_points(player); + if(points > 21) + reply(game->textbot, player->chanuser->user, "NF_BJ_POINTS_EXCEEDED"); + game->timer = timeq_add(30, module_id, bj_player_timeout, game); +} + +void bj_action_next_player(struct bj_game *game, struct bj_player *player) { + if(game->timer) { + timeq_del(game->timer); + game->timer = NULL; + } + if(player) + bj_show_player_cards(game, player); + game->active_player = bj_get_next_player(game); + if(game->active_player) { + bj_reply(game, NULL, "NF_BJ_USER_HURRY_UP", game->active_player->chanuser->user->nick); + game->timer = timeq_add(30, module_id, bj_player_timeout, game); + } else { + bj_game_end(game); + bj_free_game(game); + } +} + +void bj_event_part(struct ChanUser *chanuser) { + struct bj_game *game; + for(game = bj_active_games; game; game = game->next) { + if(chanuser->chan == game->channel) { + struct bj_player *player; + for(player = game->player; player; player = player->next) { + if(player->chanuser == chanuser) { + if(game->active_player == player) { + if(bj_get_next_player(game) == NULL) { + bj_game_end(game); + bj_free_game(game); + return; + } else + bj_action_next_player(game, NULL); + } + game->players--; + bj_free_player(player, game->deck); + return; + } + } + } + } +} + +void bj_event_freechan(struct ChanNode *chan) { + struct bj_game *game; + for(game = bj_active_games; game; game = game->next) { + if(game->channel == chan) { + bj_free_game(game); + return; + } + } +} + +static int bj_highscore_sort(const void *a, const void *b) { + const struct bj_player *player_a = *((struct bj_player * const *) a); + const struct bj_player *player_b = *((struct bj_player * const *) b); + int points_a, points_b; + if(player_a->count > 21) + points_a = player_a->count * -1; + else + points_a = player_a->count; + if(player_b->count > 21) + points_b = player_b->count * -1; + else + points_b = player_b->count; + return points_b - points_a; +} + +void bj_game_end(struct bj_game *game) { + bj_reply(game, NULL, "NF_BJ_GAME_FINISHED"); + struct Table *table; + table = table_init(4, game->players+1, 0); + char *content[4]; + content[0] = get_language_string(NULL, "NF_BJ_RANK"); + content[1] = get_language_string(NULL, "NF_BJ_NAME"); + content[2] = get_language_string(NULL, "NF_BJ_POINTS"); + content[3] = get_language_string(NULL, "NF_BJ_CARDS"); + table_add(table, content); + //sort users + struct bj_player *players[game->players]; + struct bj_player *player; + int i = 0; + for(player = game->player; player; player = player->next) { + player->count = bj_get_player_card_points(player); + players[i++] = player; + } + qsort(players, game->players, sizeof(struct bj_player *), bj_highscore_sort); + char rankbuf[12]; + char pointbuf[12]; + char cardsbuf[MAXLEN]; + for(i = 0; i < game->players; i++) { + player = players[i]; + sprintf(rankbuf, "#%d", i+1); + content[0] = rankbuf; + content[1] = player->chanuser->user->nick; + sprintf(pointbuf, "%d", player->count); + content[2] = pointbuf; + bj_print_player_cards(player, cardsbuf); + content[3] = cardsbuf; + table_add(table, content); + } + char **table_lines = table_end(table); + for(i = 0; i < table->entrys; i++) { + bj_reply(game, NULL, table_lines[i]); + } + table_free(table); +} diff --git a/src/modules/NeonFun.mod/game_blackjack.h b/src/modules/NeonFun.mod/game_blackjack.h new file mode 100644 index 0000000..0e643ae --- /dev/null +++ b/src/modules/NeonFun.mod/game_blackjack.h @@ -0,0 +1,102 @@ +/* game_blackjack.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _game_blackjack_h +#define _game_blackjack_h +#include "../../timeq.h" + +#define BJ_STATE_WAITING 1 +#define BJ_STATE_RUNNING 2 + +#define BJ_CTYPE_HEARTS 0 +#define BJ_CTYPE_SPADES 1 +#define BJ_CTYPE_DIAMONDS 2 +#define BJ_CTYPE_CLUBS 3 + +#define BJ_CTYPE_COUNT 4 + +#define BJ_CARD_NUMBER_2 0 +#define BJ_CARD_NUMBER_3 1 +#define BJ_CARD_NUMBER_4 2 +#define BJ_CARD_NUMBER_5 3 +#define BJ_CARD_NUMBER_6 4 +#define BJ_CARD_NUMBER_7 5 +#define BJ_CARD_NUMBER_8 6 +#define BJ_CARD_NUMBER_9 7 +#define BJ_CARD_NUMBER_10 8 +#define BJ_CARD_JACK 9 +#define BJ_CARD_QUEEN 10 +#define BJ_CARD_KING 11 +#define BJ_CARD_ACE 12 + +struct UserNode; +struct ChanNode; +struct ChanUser; +struct ClientSocket; +extern struct bj_game *bj_active_games; + +struct bj_card { + unsigned char type; + unsigned char card; + struct bj_card *prev, *next; +}; + +struct bj_card_deck { + struct bj_card *cards; + int count; +}; + +struct bj_player { + struct ChanUser *chanuser; + struct bj_card *cards; + int count; + struct bj_player *prev, *next; +}; + +struct bj_game { + struct ChanNode *channel; + struct ClientSocket *textbot; + int state : 3; + struct bj_card_deck *deck; + struct bj_player *player; + struct bj_player *active_player; + int players; + + struct timeq_entry *timer; + struct bj_game *next; +}; + +struct bj_card_deck *bj_shuffle_deck(); +struct bj_card *bj_get_card(struct bj_card_deck *deck); +void bj_free_deck(struct bj_card_deck *deck); +void bj_free_player(struct bj_player *player, struct bj_card_deck *deck); +void bj_free_game(struct bj_game *game); +struct bj_player *bj_get_next_player(struct bj_game *game); +int bj_get_player_card_points(struct bj_player *player); +void bj_show_player_cards(struct bj_game *game, struct bj_player *player); + +TIMEQ_CALLBACK(bj_game_wait_timeout); +TIMEQ_CALLBACK(bj_player_timeout); + +void bj_action_take_card(struct bj_game *game, struct bj_player *player); +void bj_action_next_player(struct bj_game *game, struct bj_player *player); + +void bj_event_part(struct ChanUser *chanuser); +void bj_event_freechan(struct ChanNode *chan); + +void bj_game_end(struct bj_game *game); + +#endif diff --git a/src/modules/NeonFun.mod/game_uno.c b/src/modules/NeonFun.mod/game_uno.c new file mode 100644 index 0000000..2e67eda --- /dev/null +++ b/src/modules/NeonFun.mod/game_uno.c @@ -0,0 +1,668 @@ +/* game_uno.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#include "../module.h" +#include "game_uno.h" +#include "bot_NeonFun.h" +#include "../../IRCParser.h" +#include "../../bots.h" +#include "../../UserNode.h" +#include "../../ChanUser.h" +#include "../../tools.h" +#include "../botid.h" + +struct uno_game *uno_active_games = NULL; + +#define UNO_COLOR_COUNT 4 +static int uno_colors[] = {UNO_COLOR_RED, UNO_COLOR_BLUE, UNO_COLOR_GREEN, UNO_COLOR_YELLOW}; +static int uno_irc_colors[] = {4, 12, 3, 7}; +static char *uno_color_chars[] = {"R", "B", "G", "Y"}; + +static const struct { + const char *name; + unsigned char type; +} uno_card_types[] = { + {"0", UNO_CARD_NUMBER_0}, + {"1", UNO_CARD_NUMBER_1}, + {"2", UNO_CARD_NUMBER_2}, + {"3", UNO_CARD_NUMBER_3}, + {"4", UNO_CARD_NUMBER_4}, + {"5", UNO_CARD_NUMBER_5}, + {"6", UNO_CARD_NUMBER_6}, + {"7", UNO_CARD_NUMBER_7}, + {"8", UNO_CARD_NUMBER_8}, + {"9", UNO_CARD_NUMBER_9}, + {"X", UNO_CARD_SKIP}, + {"><", UNO_CARD_DIRECTION}, + {"+2", UNO_CARD_ADD_2}, + {"+4", UNO_CARD_ADD_4}, + {"COLOR", UNO_CARD_COLOR}, + {NULL, 0} +}; + +struct uno_card_deck *uno_shuffle_deck() { + struct uno_card *card, *last_card = NULL; + int card_count = 0; + #define ADD_CARD(ccolor,ctype) \ + card = malloc(sizeof(*card)); \ + card->color = ccolor; \ + card->card = ctype; \ + card->prev = NULL; \ + card->next = last_card; \ + if(last_card) \ + last_card->prev = card; \ + last_card = card; \ + card_count++; + int colorcount = UNO_COLOR_COUNT; + for(colorcount--; colorcount >= 0; colorcount--) { + ADD_CARD(uno_colors[colorcount], UNO_CARD_NUMBER_0); + ADD_CARD(uno_colors[colorcount], UNO_CARD_NUMBER_0); + ADD_CARD(uno_colors[colorcount], UNO_CARD_NUMBER_1); + ADD_CARD(uno_colors[colorcount], UNO_CARD_NUMBER_1); + ADD_CARD(uno_colors[colorcount], UNO_CARD_NUMBER_2); + ADD_CARD(uno_colors[colorcount], UNO_CARD_NUMBER_2); + ADD_CARD(uno_colors[colorcount], UNO_CARD_NUMBER_3); + ADD_CARD(uno_colors[colorcount], UNO_CARD_NUMBER_3); + ADD_CARD(uno_colors[colorcount], UNO_CARD_NUMBER_4); + ADD_CARD(uno_colors[colorcount], UNO_CARD_NUMBER_4); + ADD_CARD(uno_colors[colorcount], UNO_CARD_NUMBER_5); + ADD_CARD(uno_colors[colorcount], UNO_CARD_NUMBER_5); + ADD_CARD(uno_colors[colorcount], UNO_CARD_NUMBER_6); + ADD_CARD(uno_colors[colorcount], UNO_CARD_NUMBER_6); + ADD_CARD(uno_colors[colorcount], UNO_CARD_NUMBER_7); + ADD_CARD(uno_colors[colorcount], UNO_CARD_NUMBER_7); + ADD_CARD(uno_colors[colorcount], UNO_CARD_NUMBER_8); + ADD_CARD(uno_colors[colorcount], UNO_CARD_NUMBER_8); + ADD_CARD(uno_colors[colorcount], UNO_CARD_NUMBER_9); + ADD_CARD(uno_colors[colorcount], UNO_CARD_NUMBER_9); + ADD_CARD(uno_colors[colorcount], UNO_CARD_SKIP); + ADD_CARD(uno_colors[colorcount], UNO_CARD_SKIP); + ADD_CARD(uno_colors[colorcount], UNO_CARD_DIRECTION); + ADD_CARD(uno_colors[colorcount], UNO_CARD_DIRECTION); + ADD_CARD(uno_colors[colorcount], UNO_CARD_ADD_2); + ADD_CARD(uno_colors[colorcount], UNO_CARD_ADD_2); + } + ADD_CARD(UNO_COLOR_BLACK, UNO_CARD_ADD_4); + ADD_CARD(UNO_COLOR_BLACK, UNO_CARD_ADD_4); + ADD_CARD(UNO_COLOR_BLACK, UNO_CARD_ADD_4); + ADD_CARD(UNO_COLOR_BLACK, UNO_CARD_ADD_4); + ADD_CARD(UNO_COLOR_BLACK, UNO_CARD_COLOR); + ADD_CARD(UNO_COLOR_BLACK, UNO_CARD_COLOR); + ADD_CARD(UNO_COLOR_BLACK, UNO_CARD_COLOR); + ADD_CARD(UNO_COLOR_BLACK, UNO_CARD_COLOR); + #undef ADD_CARD + struct uno_card_deck *deck = malloc(sizeof(*deck)); + deck->cards = last_card; + deck->count = card_count; + return deck; +} + +struct uno_card *uno_get_card(struct uno_card_deck *deck) { + if(!deck->count) return NULL; + int card_id = (rand() % deck->count); + int i = 0; + struct uno_card *card; + for(card = deck->cards; card; card = card->next) { + if(i == card_id) { + if(card->prev) + card->prev->next = card->next; + else + deck->cards = card->next; + if(card->next) + card->next->prev = card->prev; + deck->count--; + card->next = NULL; + card->prev = NULL; + return card; + } + i++; + } + return NULL; +} + +void uno_free_deck(struct uno_card_deck *deck) { + struct uno_card *card, *next_card; + if(deck->count) { + for(card = deck->cards; card; card = next_card) { + next_card = card->next; + free(card); + } + } + free(deck); +} + +void uno_free_player(struct uno_player *player, struct uno_card_deck *deck) { + if(player->count) { + struct uno_card *card, *next_card; + for(card = player->cards; card; card = next_card) { + next_card = card->next; + if(deck) { + card->next = deck->cards; + deck->cards->prev = card; + deck->cards = card; + deck->count++; + } else + free(card); + } + } + if(player->prev) + player->prev->next = player->next; + if(player->next) + player->next->prev = player->prev; + free(player); +} + +void uno_free_topcard(struct uno_card *card) { + struct uno_card *next_card; + for(; card; card = next_card) { + next_card = card->prev; + free(card); + } +} + +void uno_free_game(struct uno_game *game) { + struct uno_player *player, *next_player; + for(player = game->player; player; player = next_player) { + next_player = player->next; + uno_free_player(player, NULL); + } + for(player = game->winner; player; player = next_player) { + next_player = player->next; + uno_free_player(player, NULL); + } + if(game->deck) + uno_free_deck(game->deck); + if(game->top_card) + uno_free_topcard(game->top_card); + if(game->timer) + timeq_del(game->timer); + struct uno_game *cgame, *pgame = NULL; + for(cgame = uno_active_games; cgame; cgame = cgame->next) { + if(cgame == game) { + if(pgame) + pgame->next = game->next; + else + uno_active_games = game->next; + break; + } else + pgame = cgame; + } + free(game); +} + +struct uno_player *uno_get_next_player(struct uno_game *game) { + struct uno_player *player = game->active_player; + if(!game->reverse_direction) { + player = player->next; + if(!player) + player = game->player; + } else { + player = player->prev; + if(!player) { + for(player = game->player; player->next; player = player->next) { + //loop to the last player + } + } + } + return player; +} + +void uno_show_player_cards(struct uno_game *game, struct uno_player *player) { + struct uno_card *card; + char cards_buf[MAXLEN]; + int cards_bufpos = 0; + for(card = player->cards; card; card = card->next) { + int cardcolor = 1; + char *cardchar = ""; + int i; + for(i = 0; i < UNO_COLOR_COUNT; i++) { + if(uno_colors[i] == card->color) { + cardcolor = uno_irc_colors[i]; + cardchar = uno_color_chars[i]; + break; + } + } + i = 0; + while(uno_card_types[i].name) { + if(uno_card_types[i].type == card->card) { + cards_bufpos += sprintf(cards_buf + cards_bufpos, "%s[\003%d%s%s\003]", (cards_bufpos ? " " : ""), cardcolor, cardchar, uno_card_types[i].name); + break; + } + i++; + } + } + reply(game->textbot, player->chanuser->user, "NF_UNO_YOUR_CARDS", cards_buf); +} + +void uno_show_top_card(struct uno_game *game) { + struct uno_card *card = game->top_card; + char card_buf[50]; + int cardcolor = 1; + char *cardchar = ""; + int i; + for(i = 0; i < UNO_COLOR_COUNT; i++) { + if(uno_colors[i] == card->color) { + cardcolor = uno_irc_colors[i]; + cardchar = uno_color_chars[i]; + break; + } + } + i = 0; + while(uno_card_types[i].name) { + if(uno_card_types[i].type == card->card) { + if(card->card == UNO_CARD_ADD_4 || card->card == UNO_CARD_COLOR) + cardchar = ""; + sprintf(card_buf, "[\003%d%s%s\003]", cardcolor, cardchar, uno_card_types[i].name); + break; + } + i++; + } + uno_reply(game, NULL, "NF_UNO_TOP_CARD", card_buf); +} + +TIMEQ_CALLBACK(uno_game_wait_timeout) { + struct uno_game *game = data; + game->timer = NULL; + if(game->players == 1) { + uno_reply(game, NULL, "NF_UNO_LESS_PLAYERS"); + uno_free_game(game); + return; + } + game->deck = uno_shuffle_deck(); + uno_reply(game, NULL, "NF_UNO_START"); + struct uno_player *player; + for(player = game->player; player; player = player->next) { + //give 7 cards + int i; + for(i = 0; i < 7; i++) { + if(!game->deck->count) + game->deck = uno_shuffle_deck(); + struct uno_card *card = uno_get_card(game->deck); + if(!card) { + uno_reply(game, NULL, "NF_UNO_ERROR", 1); + uno_free_game(game); + return; + } + card->next = player->cards; + if(player->cards) + player->cards->prev = card; + player->cards = card; + player->count++; + } + uno_show_player_cards(game, player); + if((player->chanuser->user->flags & USERFLAG_ISAUTHED)) { + char *tmp; + int game_count = ((tmp = getSetting(player->chanuser->user, game->channel, "uno_games")) ? atoi(tmp) : 0); + int total_game_count = ((tmp = getSetting(player->chanuser->user, NULL, "uno_games")) ? atoi(tmp) : 0); + game_count++; + total_game_count++; + char buf[10]; + sprintf(buf, "%d", game_count); + setSetting(player->chanuser->user, game->channel, "uno_games", buf); + sprintf(buf, "%d", total_game_count); + setSetting(player->chanuser->user, NULL, "uno_games", buf); + } + } + if(!game->deck->count) + game->deck = uno_shuffle_deck(); + struct uno_card *card = uno_get_card(game->deck); + if(!card) { + uno_reply(game, NULL, "NF_UNO_ERROR", 1); + uno_free_game(game); + return; + } + game->top_card = card; + game->state = UNO_STATE_RUNNING; + uno_show_top_card(game); + game->active_player = game->player; //active player + uno_reply(game, NULL, "NF_UNO_USER_HURRY_UP", game->active_player->chanuser->user->nick); + game->timer = timeq_add(40, module_id, uno_player_timeout, game); +} + +TIMEQ_CALLBACK(uno_player_timeout) { + struct uno_game *game = data; + game->timer = NULL; + //player timeout (take another card) + struct uno_player *player = game->active_player, *next_player = uno_get_next_player(game); + //add a card to the players deck + int ccount; + if(game->take_cards_pending) { + //count cards to take + struct uno_card *card; + ccount = 0; + for(card = game->top_card; card; card = card->prev) { + if(card->card == UNO_CARD_ADD_2) + ccount += 2; + else if(card->card == UNO_CARD_ADD_4) + ccount += 4; + } + } else + ccount = 1; + int i; + for(i = 0; i < ccount; i++) { + if(!game->deck->count) + game->deck = uno_shuffle_deck(); + struct uno_card *card = uno_get_card(game->deck); + if(!card) { + uno_reply(game, NULL, "NF_UNO_ERROR", 2); + uno_free_game(game); + return; + } + card->next = player->cards; + if(player->cards) + player->cards->prev = card; + player->cards = card; + player->count++; + } + player->timeout = 1; + game->active_player = next_player; + if(game->take_cards_pending) { + game->take_cards_pending = 0; + uno_reply(game, NULL, "NF_UNO_USER_TOOK_CARDS", player->chanuser->user->nick, ccount); + } else + uno_reply(game, NULL, "NF_UNO_USER_TOOK_CARD", player->chanuser->user->nick); + struct uno_player *cplayer; + for(cplayer = game->player; cplayer; cplayer = cplayer->next) { + if(!cplayer->timeout) + break; + } + if(!cplayer) { + uno_reply(game, NULL, "NF_UNO_TIMEOUT"); + uno_free_game(game); + return; + } + uno_show_player_cards(game, player); + uno_show_top_card(game); + uno_reply(game, NULL, "NF_UNO_USER_HURRY_UP", game->active_player->chanuser->user->nick); + uno_show_player_cards(game, game->active_player); + game->timer = timeq_add(40, module_id, uno_player_timeout, game); +} + +void uno_action_take_card(struct uno_game *game, struct uno_player *player) { + timeq_del(game->timer); + game->timer = NULL; + struct uno_player *next_player = uno_get_next_player(game); + //add a card to the players deck + int ccount; + if(game->take_cards_pending) { + //count cards to take + struct uno_card *card; + ccount = 0; + for(card = game->top_card; card; card = card->prev) { + if(card->card == UNO_CARD_ADD_2) + ccount += 2; + else if(card->card == UNO_CARD_ADD_4) + ccount += 4; + } + } else + ccount = 1; + int i; + for(i = 0; i < ccount; i++) { + if(!game->deck->count) + game->deck = uno_shuffle_deck(game->deck); + struct uno_card *card = uno_get_card(game->deck); + if(!card) { + uno_reply(game, NULL, "NF_UNO_ERROR", 2); + uno_free_game(game); + return; + } + card->next = player->cards; + if(player->cards) + player->cards->prev = card; + player->cards = card; + player->count++; + } + game->active_player = next_player; + if(game->take_cards_pending) { + game->take_cards_pending = 0; + uno_reply(game, NULL, "NF_UNO_USER_TOOK_CARDS", player->chanuser->user->nick, ccount); + } else + uno_reply(game, NULL, "NF_UNO_USER_TOOK_CARD", player->chanuser->user->nick); + uno_show_player_cards(game, player); + uno_show_top_card(game); + uno_reply(game, NULL, "NF_UNO_USER_HURRY_UP", game->active_player->chanuser->user->nick); + uno_show_player_cards(game, game->active_player); + game->timer = timeq_add(40, module_id, uno_player_timeout, game); +} + + + +struct uno_card *uno_parse_card(struct uno_game *game, struct uno_player *player, char *arg1) { + char *b = arg1; + int a = 0; + while(*b) { + switch (*b) { + case '\003': + if(isdigit(b[1])) + b[1] = '\002'; + if(isdigit(b[2])) + b[2] = '\002'; + case '[': + case '\002': + case ']': + *b = '\0'; + break; + default: + if(!a) { + a = 1; + arg1 = b; + } + } + b++; + } + unsigned char ctype = 255, ccolor = 0; + int i = 0, j; + char tmpbuf[50]; + while(uno_card_types[i].name) { + if(!stricmp(uno_card_types[i].name, arg1)) { + ctype = uno_card_types[i].type; + break; + } + for(j = 0; j < UNO_COLOR_COUNT; j++) { + sprintf(tmpbuf, "%s%s", uno_color_chars[j], uno_card_types[i].name); + if(!stricmp(tmpbuf, arg1)) { + ccolor = uno_colors[j]; + ctype = uno_card_types[i].type; + break; + } + } + i++; + } + if(ctype == 255) { + reply(game->textbot, player->chanuser->user, "NF_UNO_UNKNOWN_CARD", arg1); + return NULL; + } + struct uno_card *card; + for(card = player->cards; card; card = card->next) { + if(card->card == ctype && card->color == ccolor) + break; + } + if(!card) { + reply(game->textbot, player->chanuser->user, "NF_UNO_CARD_NOT_IN_DECK"); + return NULL; + } + return card; +} + +int uno_check_card_valid(struct uno_game *game, struct uno_card *card) { + if(game->take_cards_pending && card->card != game->top_card->card) + return 1; + if(card->color == UNO_COLOR_BLACK || game->top_card->color == UNO_COLOR_BLACK) + return 0; + if(card->color != game->top_card->color && card->card != game->top_card->card) + return 1; + return 0; +} + +void uno_play_card(struct uno_game *game, struct uno_player *player, struct uno_card *card) { + timeq_del(game->timer); + game->timer = NULL; + if(card->prev) + card->prev->next = card->next; + else + player->cards = card->next; + if(card->next) + card->next->prev = card->prev; + player->count--; + if(!game->take_cards_pending) { + uno_free_topcard(game->top_card); + card->prev = NULL; + } else + card->prev = game->top_card; + card->next = NULL; + game->top_card = card; + uno_show_top_card(game); + if(player->count == 1) { + uno_reply(game, NULL, "NF_UNO_ONE_CARD", game->active_player->chanuser->user->nick); + } else if(player->count == 0) { + uno_reply(game, NULL, "NF_UNO_USER_WIN", game->active_player->chanuser->user->nick); + if(player->prev) + player->prev->next = player->next; + else + game->player = player->next; + if(player->next) + player->next->prev = player->prev; + player->next = NULL; + player->prev = NULL; + struct uno_player *cplayer; + int winner_count = 0; + if(game->winner) { + for(cplayer = game->winner; cplayer->next; cplayer = cplayer->next) { + winner_count++; + } + } else + cplayer = NULL; + if(cplayer) { + cplayer->next = player; + } else { + game->winner = player; + if((player->chanuser->user->flags & USERFLAG_ISAUTHED)) { + char *tmp; + int win_count = ((tmp = getSetting(player->chanuser->user, game->channel, "uno_win")) ? atoi(tmp) : 0); + int total_win_count = ((tmp = getSetting(player->chanuser->user, NULL, "uno_win")) ? atoi(tmp) : 0); + win_count++; + total_win_count++; + char buf[10]; + sprintf(buf, "%d", win_count); + setSetting(player->chanuser->user, game->channel, "uno_win", buf); + sprintf(buf, "%d", total_win_count); + setSetting(player->chanuser->user, NULL, "uno_win", buf); + } + } + player->prev = cplayer; + game->players--; + if(game->players <= 1) { + //game finished + uno_reply(game, NULL, "NF_UNO_GAME_FINISHED"); + struct Table *table; + table = table_init(4, winner_count + 3, 0); + char *content[4]; + content[0] = get_language_string(NULL, "NF_UNO_RANK"); + content[1] = get_language_string(NULL, "NF_UNO_NAME"); + content[2] = get_language_string(NULL, "NF_UNO_WON_GAMES"); + content[3] = get_language_string(NULL, "NF_UNO_TOTAL_WON_GAMES"); + table_add(table, content); + winner_count = 1; + char rank_buf[20], won_buf[50], total_won_buf[50]; + char *tmp, *tmp2; + for(cplayer = game->winner; cplayer; cplayer = cplayer->next) { + sprintf(rank_buf, "%d", winner_count++); + content[0] = rank_buf; + content[1] = cplayer->chanuser->user->nick; + if((cplayer->chanuser->user->flags & USERFLAG_ISAUTHED)) { + sprintf(won_buf, "%d/%d", ((tmp = getSetting(cplayer->chanuser->user, game->channel, "uno_win")) ? atoi(tmp) : 0), ((tmp2 = getSetting(cplayer->chanuser->user, game->channel, "uno_games")) ? atoi(tmp2) : 0)); + content[2] = won_buf; + sprintf(total_won_buf, "%d/%d", ((tmp = getSetting(cplayer->chanuser->user, NULL, "uno_win")) ? atoi(tmp) : 0), ((tmp2 = getSetting(cplayer->chanuser->user, NULL, "uno_games")) ? atoi(tmp2) : 0)); + content[3] = total_won_buf; + } else { + content[2] = "?"; + content[3] = "?"; + } + table_add(table, content); + } + cplayer = game->player; + sprintf(rank_buf, "%d", winner_count++); + content[0] = rank_buf; + content[1] = cplayer->chanuser->user->nick; + if((cplayer->chanuser->user->flags & USERFLAG_ISAUTHED)) { + sprintf(won_buf, "%d/%d", ((tmp = getSetting(cplayer->chanuser->user, game->channel, "uno_win")) ? atoi(tmp) : 0), ((tmp2 = getSetting(cplayer->chanuser->user, game->channel, "uno_games")) ? atoi(tmp2) : 0)); + content[2] = won_buf; + sprintf(total_won_buf, "%d/%d", ((tmp = getSetting(cplayer->chanuser->user, NULL, "uno_win")) ? atoi(tmp) : 0), ((tmp2 = getSetting(cplayer->chanuser->user, NULL, "uno_games")) ? atoi(tmp2) : 0)); + content[3] = total_won_buf; + } else { + content[2] = "?"; + content[3] = "?"; + } + table_add(table, content); + char **table_lines = table_end(table); + int i; + for(i = 0; i < table->entrys; i++) { + uno_reply(game, NULL, table_lines[i]); + } + table_free(table); + uno_free_game(game); + return; + } + } + if(card->card == UNO_CARD_DIRECTION) + game->reverse_direction = (game->reverse_direction ? 0 : 1); + struct uno_player *next_player = uno_get_next_player(game); + game->active_player = next_player; + if(card->card == UNO_CARD_SKIP) { + uno_reply(game, NULL, "NF_UNO_USER_SKIP", game->active_player->chanuser->user->nick); + next_player = uno_get_next_player(game); + game->active_player = next_player; + } + if(card->card == UNO_CARD_ADD_2 || card->card == UNO_CARD_ADD_4) { + int ccount = 0; + for(card = game->top_card; card; card = card->prev) { + if(card->card == UNO_CARD_ADD_2) + ccount += 2; + else if(card->card == UNO_CARD_ADD_4) + ccount += 4; + } + uno_reply(game, NULL, "NF_UNO_ADD_CARD", game->active_player->chanuser->user->nick, ccount); + game->take_cards_pending = 1; + } + uno_reply(game, NULL, "NF_UNO_USER_HURRY_UP", game->active_player->chanuser->user->nick); + uno_show_player_cards(game, game->active_player); + game->timer = timeq_add(40, module_id, uno_player_timeout, game); +} + +void uno_event_part(struct ChanUser *chanuser) { + struct uno_game *game; + for(game = uno_active_games; game; game = game->next) { + if(chanuser->chan == game->channel) { + struct uno_player *player; + for(player = game->player; player; player = player->next) { + if(player->chanuser == chanuser) { + uno_free_player(player, game->deck); + return; + } + } + } + } +} + +void uno_event_freechan(struct ChanNode *chan) { + struct uno_game *game; + for(game = uno_active_games; game; game = game->next) { + if(game->channel == chan) { + uno_free_game(game); + return; + } + } +} diff --git a/src/modules/NeonFun.mod/game_uno.h b/src/modules/NeonFun.mod/game_uno.h new file mode 100644 index 0000000..04a02a7 --- /dev/null +++ b/src/modules/NeonFun.mod/game_uno.h @@ -0,0 +1,111 @@ +/* game_uno.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _game_uno_h +#define _game_uno_h +#include "../../timeq.h" + +#define UNO_STATE_WAITING 1 +#define UNO_STATE_RUNNING 2 + +#define UNO_COLOR_BLACK 0 +#define UNO_COLOR_RED 1 +#define UNO_COLOR_BLUE 2 +#define UNO_COLOR_GREEN 3 +#define UNO_COLOR_YELLOW 4 + +#define UNO_CARD_NUMBER_0 0 +#define UNO_CARD_NUMBER_1 1 +#define UNO_CARD_NUMBER_2 2 +#define UNO_CARD_NUMBER_3 3 +#define UNO_CARD_NUMBER_4 4 +#define UNO_CARD_NUMBER_5 5 +#define UNO_CARD_NUMBER_6 6 +#define UNO_CARD_NUMBER_7 7 +#define UNO_CARD_NUMBER_8 8 +#define UNO_CARD_NUMBER_9 9 +#define UNO_CARD_SKIP 10 +#define UNO_CARD_DIRECTION 11 +#define UNO_CARD_ADD_2 12 +#define UNO_CARD_ADD_4 13 +#define UNO_CARD_COLOR 14 + +#define UNO_IS_NUMBER_CARD(ccard) (ccard->card <= 9) + +struct UserNode; +struct ChanNode; +struct ChanUser; +struct ClientSocket; +extern struct uno_game *uno_active_games; + +struct uno_card { + unsigned char color; + unsigned char card; + struct uno_card *prev, *next; +}; + +struct uno_card_deck { + struct uno_card *cards; + int count; +}; + +struct uno_player { + struct ChanUser *chanuser; + struct uno_card *cards; + int count; + int timeout; + struct uno_player *prev, *next; +}; + +struct uno_game { + struct ChanNode *channel; + struct ClientSocket *textbot; + int state : 3; + int reverse_direction : 1; + int take_cards_pending : 1; + struct uno_card_deck *deck; + struct uno_card *top_card; + struct uno_player *player; + struct uno_player *winner; + struct uno_player *active_player; + int players; + + struct timeq_entry *timer; + struct uno_game *next; +}; + +struct uno_card_deck *uno_shuffle_deck(); +struct uno_card *uno_get_card(struct uno_card_deck *deck); +void uno_free_deck(struct uno_card_deck *deck); +void uno_free_player(struct uno_player *player, struct uno_card_deck *deck); +void uno_free_topcard(struct uno_card *card); +void uno_free_game(struct uno_game *game); +struct uno_player *uno_get_next_player(struct uno_game *game); +void uno_show_player_cards(struct uno_game *game, struct uno_player *player); +void uno_show_top_card(struct uno_game *game); + +TIMEQ_CALLBACK(uno_game_wait_timeout); +TIMEQ_CALLBACK(uno_player_timeout); + +void uno_action_take_card(struct uno_game *game, struct uno_player *player); +struct uno_card *uno_parse_card(struct uno_game *game, struct uno_player *player, char *arg1); +int uno_check_card_valid(struct uno_game *game, struct uno_card *card); +void uno_play_card(struct uno_game *game, struct uno_player *player, struct uno_card *card); + +void uno_event_part(struct ChanUser *chanuser); +void uno_event_freechan(struct ChanNode *chan); + +#endif diff --git a/src/modules/NeonFun.mod/module.c b/src/modules/NeonFun.mod/module.c new file mode 100644 index 0000000..b6d2e62 --- /dev/null +++ b/src/modules/NeonFun.mod/module.c @@ -0,0 +1,35 @@ +/* module.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#include "../module.h" +#include "bot_NeonFun.h" +#include "cmd_neonfun.h" + +static int module_initialize() { + register_commands(); + return 0; +} + +static void module_start(int type) { + init_NeonFun(type); + srand(time(NULL)); +} + +static void module_stop(int type) { + free_NeonFun(type); +} + +MODULE_HEADER(module_initialize, module_start, module_stop); diff --git a/src/modules/NeonHelp.mod/NeonHelpHistory.php b/src/modules/NeonHelp.mod/NeonHelpHistory.php new file mode 100644 index 0000000..1c05d08 --- /dev/null +++ b/src/modules/NeonHelp.mod/NeonHelpHistory.php @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + +
Nick:
Hostmask: (auth: )
Time
State + +
", $row[5]); ?>
", $row[6]); ?>
\ No newline at end of file diff --git a/src/modules/NeonHelp.mod/bot_NeonHelp.c b/src/modules/NeonHelp.mod/bot_NeonHelp.c new file mode 100644 index 0000000..5d4aecb --- /dev/null +++ b/src/modules/NeonHelp.mod/bot_NeonHelp.c @@ -0,0 +1,597 @@ +/* bot_NeonHelp.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#include "../module.h" +#include "../botid.h" + +#include "bot_NeonHelp.h" +#include "../../modcmd.h" +#include "cmd_neonhelp.h" +#include "../../lang.h" +#include "../../mysqlConn.h" +#include "../../ClientSocket.h" +#include "../../UserNode.h" +#include "../../ChanNode.h" +#include "../../ChanUser.h" +#include "../../IRCEvents.h" +#include "../../IRCParser.h" +#include "../../bots.h" +#include "../../DBHelper.h" +#include "../../WHOHandler.h" + +#define BOTID NEONHELP_BOTID +#define BOTALIAS "NeonHelp" + +static const struct default_language_entry msgtab[] = { + {"NH_NOT_ON_CHAN_1", "You cannot open this request as you are not in %s."}, /* {ARGS: "#test"} */ + {"NH_NOT_ON_CHAN_2", "You cannot open this request as you are not in %s or %s."}, /* {ARGS: "test", "#test-support"} */ + {"NH_REQUEST_RECORDED", "Your message has been recorded and assigned request ID#%d A helper should contact you shortly."}, /* {ARGS: 5} */ + {"NH_REQUEST_OTHERS_0", "There are no other unhandled requests."}, + {"NH_REQUEST_OTHERS_1", "There is 1 other unhandled request."}, + {"NH_REQUEST_OTHERS_2", "There are %d other unhandled requests."}, /* {ARGS: 1337} */ + {"NH_REQUEST_FOOTER_1", "Everything you tell me until you are helped (or you leave %1$s) will be recorded. If you part %1$s, your request will be lost."}, /* {ARGS: "#test"} */ + {"NH_REQUEST_FOOTER_2", "Everything you tell me until you are helped (or you leave %1$s or %2$s) will be recorded. If you part %1$s or %2$s, your request will be lost."}, /* {ARGS: "#test", "#test-support"} */ + {"NH_NEW_REQUEST", "New request #%d by %s: %s"}, /* {ARGS: 5, "pk910", "Help, I've fallen and I can't get up!"} */ + {"NH_NEXT_NONE", "No more requests."}, + {"NH_NEXT_NOT_FOUND", "No request found."}, + {"NH_NEXT_HEADER", "$bNext request: #%d %s$b"}, /* {ARGS: 5, "pk910"} */ + {"NH_NEXT_HELPER", "Your helper for request ID#%d is %s (Current nick: %s)."}, /* {ARGS: 5, "pk910", "Skynet"} */ + {"NH_NEXT_JOIN", "Please /join %s now."}, /* {ARGS: "#test-support"} */ + {"NH_DELETED", "Your request ID#%d has been deleted."}, /* {ARGS: 5} */ + {"NH_DELETED_STAFF", "Request deleted: #%d (%s)"}, /* {ARGS: 5, "pk910"} */ + {"NH_REMIND_OPEN_REQUESTS_1", "There is %d unhandled request!"}, /* {ARGS: 1} */ + {"NH_REMIND_OPEN_REQUESTS_2", "There are %d unhandled requests!"}, /* {ARGS: 4} */ + {"NH_REQUESTS_HEADER_ID", "ID"}, + {"NH_REQUESTS_HEADER_STATUS", "State"}, + {"NH_REQUESTS_HEADER_NICK", "Nick"}, + {"NH_REQUESTS_HEADER_TIME", "Time"}, + {"NH_REQUESTS_HEADER_STATUS", "State"}, + {"NH_REQUESTS_HEADER_AUTH", "Auth"}, + {"NH_REQUESTS_HEADER_MASK", "Mask"}, + {"NH_REQUESTS_HEADER_REQUEST", "Question"}, + {"NH_REQUESTS_STATE_ACTIVE", "active"}, + {"NH_REQUESTS_STATE_PENDING", "pending"}, + {"NH_REQUESTS_STATE_CLOSED", "closed"}, + {"NH_REQUESTS_STATE_ERROR", "ERROR"}, + {"NH_STATS_HEADER_USER", "User"}, + {"NH_STATS_HEADER_LAST_24H", "last 24h"}, + {"NH_STATS_HEADER_LAST_7DAY", "last 7d"}, + {"NH_STATS_HEADER_LAST_30DAY", "last 30d"}, + {NULL, NULL} +}; + +static void neonhelp_bot_ready(struct ClientSocket *client) { + if(client->botid != BOTID) + return; + MYSQL_RES *res; + MYSQL_ROW row; + + printf_mysql_query("SELECT `automodes`, `oper_user`, `oper_pass` FROM `bots` WHERE `id` = '%d'", client->clientid); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + if(row[1] && row[2]) { + putsock(client, "OPER %s %s", row[1], row[2]); + } + putsock(client, "MODE %s +%s", client->user->nick, row[0]); + } + + printf_mysql_query("SELECT `channel_name`, `channel_key` FROM `bot_channels` LEFT JOIN `channels` ON `chanid` = `channel_id` WHERE `botid` = '%d' AND `suspended` = '0'", client->clientid); + res = mysql_use(); + + while ((row = mysql_fetch_row(res)) != NULL) { + putsock(client, "JOIN %s %s", row[0], row[1]); + } +} + +static void neonhelp_trigger_callback(int clientid, struct ChanNode *chan, char *trigger) { + MYSQL_RES *res; + MYSQL_ROW row; + loadChannelSettings(chan); + if(!(chan->flags & CHANFLAG_CHAN_REGISTERED)) { + strcpy(trigger, "!"); + return; + } + printf_mysql_query("SELECT `trigger`, `defaulttrigger` FROM `bot_channels` LEFT JOIN `bots` ON `botid` = `bots`.`id` WHERE `chanid` = '%d' AND `botclass` = '%d'", chan->channel_id, BOTID); + res = mysql_use(); + if(!(row = mysql_fetch_row(res))) { + strcpy(trigger, "!"); + return; + } + if(row[0] && *row[0]) + strcpy(trigger, row[0]); + else + strcpy(trigger, ((row[1] && *row[1]) ? row[1] : "!")); +} + +static void start_bots(int type) { + struct ClientSocket *client; + MYSQL_RES *res, *res2; + MYSQL_ROW row; + + if(type == MODSTATE_STARTSTOP) { + printf_mysql_query("SELECT `nick`, `ident`, `realname`, `server`, `port`, `pass`, `textbot`, `id`, `queue`, `ssl`, `bind`, `secret` FROM `bots` WHERE `botclass` = '%d' AND `active` = '1'", BOTID); + res = mysql_use(); + + while ((row = mysql_fetch_row(res)) != NULL) { + client = create_socket(row[3], atoi(row[4]), row[10], row[5], row[0], row[1], row[2]); + client->flags |= (strcmp(row[6], "0") ? SOCKET_FLAG_PREFERRED : 0); + client->flags |= (strcmp(row[8], "0") ? SOCKET_FLAG_USE_QUEUE : 0); + client->flags |= (strcmp(row[9], "0") ? SOCKET_FLAG_SSL : 0); + client->flags |= (strcmp(row[11], "0") ? SOCKET_FLAG_SECRET_BOT : 0); + client->flags |= SOCKET_FLAG_SILENT; + client->flags |= SOCKET_FLAG_REQUEST_INVITE | SOCKET_FLAG_REQUEST_OP; + client->botid = BOTID; + client->clientid = atoi(row[7]); + connect_socket(client); + //close old, still opened requests + printf_mysql_query("UPDATE `helpserv_requests` SET `status` = '2' WHERE `botid` = '%d'", client->clientid); + } + } + + printf_mysql_query("SELECT `command`, `function`, `parameters`, `global_access`, `chan_access`, `flags` FROM `bot_binds` WHERE `botclass` = '%d'", BOTID); + res2 = mysql_use(); + while ((row = mysql_fetch_row(res2)) != NULL) { + if(bind_cmd_to_command(BOTID, row[0], row[1])) { + if(row[2] && strcmp(row[2], "")) { + bind_set_parameters(BOTID, row[0], row[2]); + } + if(row[3]) { + bind_set_global_access(BOTID, row[0], atoi(row[3])); + } + if(row[4]) { + bind_set_channel_access(BOTID, row[0], row[4]); + } + if(strcmp(row[5], "0")) + bind_set_bind_flags(BOTID, row[0], atoi(row[5])); + } + } + bind_unbound_required_functions(BOTID); +} + +static void neonhelp_event_privmsg_async(struct ClientSocket *client, struct UserNode *user, struct UserNode *target, char *message); +static USERAUTH_CALLBACK(neonhelp_event_privmsg_nick_lookup); +struct neonhelp_event_privmsg_cache { + struct ClientSocket *client; + struct UserNode *user, *target; + char *message; +}; + +static void neonhelp_event_privmsg(struct UserNode *user, struct UserNode *target, char *message) { + struct ClientSocket *client; + for(client = getBots(SOCKET_FLAG_READY, NULL); client; client = getBots(SOCKET_FLAG_READY, client)) { + if(client->user == target) { + if(client->botid != BOTID) return; + break; + } + } + if(!client) return; //we got the message but we have no client that could receive it??? + if(user->flags & USERFLAG_ISAUTHED) { + neonhelp_event_privmsg_async(client, user, target, message); + } else { + struct neonhelp_event_privmsg_cache *cache = malloc(sizeof(*cache)); + if(!cache) return; + cache->client = client; + cache->user = user; + cache->target = target; + cache->message = strdup(message); + get_userauth(user, module_id, neonhelp_event_privmsg_nick_lookup, cache); + } +} + +static USERAUTH_CALLBACK(neonhelp_event_privmsg_nick_lookup) { + struct neonhelp_event_privmsg_cache *cache = data; + neonhelp_event_privmsg_async(cache->client, cache->user, cache->target, cache->message); + free(cache->message); + free(cache); +} + +static TIMEQ_CALLBACK(neonhelp_remind_open_requests); + +static void neonhelp_event_privmsg_async(struct ClientSocket *client, struct UserNode *user, struct UserNode *target, char *message) { + MYSQL_RES *res; + MYSQL_ROW row, row2; + char setting[128]; + sprintf(setting, "modules.%s.need_auth", get_module_name(module_id)); + if(get_int_field(setting) && !(user->flags & USERFLAG_ISAUTHED)) { + sprintf(setting, "modules.%s.need_auth_message", get_module_name(module_id)); + char *reply_message; + if(!(reply_message = get_string_field(setting))) + reply_message = get_language_string(user, "MODCMD_AUTH_REQUIRED"); + reply(client, user, "%s", reply_message); + return; + } + printf_mysql_query("SELECT `helpserv_support`, `helpserv_public`, `helpserv_intern`, `helpserv_intern_announce` FROM `helpserv_settings` WHERE `helpserv_botid` = '%d'", client->clientid); + res = mysql_use(); + if (!(row = mysql_fetch_row(res))) return; + //check if the user is a supporter (access in the support channel) + if((user->flags & USERFLAG_ISAUTHED)) { + int caccess = 0; + int userid; + if(user->flags & USERFLAG_HAS_USERID) + userid = user->user_id; + else { + printf_mysql_query("SELECT `user_id` FROM `users` WHERE `user_user` = '%s'", escape_string(user->auth)); + res = mysql_use(); + if ((row2 = mysql_fetch_row(res)) != NULL) { + userid = atoi(row2[0]); + user->user_id = userid; + user->flags |= USERFLAG_HAS_USERID; + } else + userid = 0; + } + printf_mysql_query("SELECT `chanuser_access`, `chanuser_flags` FROM `chanusers` LEFT JOIN `channels` ON `chanuser_cid` = `channel_id` WHERE `chanuser_uid` = '%d' AND `channel_name` = '%s'", userid, escape_string(row[0])); + res = mysql_use(); + if ((row2 = mysql_fetch_row(res)) != NULL) { + int cflags = atoi(row2[1]); + if(!(cflags & DB_CHANUSER_SUSPENDED)) + caccess = atoi(row2[0]); + } + if(caccess) return; //ignore messages from supporters + } + //check if the user is in one of the bot's channels + struct ChanUser *chanuser; + struct ChanNode *chan; + for(chanuser = getUserChannels(target, NULL); chanuser; chanuser = getUserChannels(target, chanuser)) { + chan = chanuser->chan; + if((!stricmp(chan->name, row[0]) || (row[1] && !stricmp(chan->name, row[1]))) && isUserOnChan(user, chan)) + break; + } + if(!chanuser) { + if(row[1]) + reply(client, user, "NH_NOT_ON_CHAN_2", row[0], row[1]); + else + reply(client, user, "NH_NOT_ON_CHAN_1", row[0]); + return; + } + //check if there is already a support request + int others = 0; + if(client->flags & SOCKET_HAVE_HELPNODE) { + struct NeonHelpNode *helpnode; + for(helpnode = client->botclass_helpnode; helpnode; helpnode = helpnode->next) { + if(helpnode->user == user) { + //simply append the message to the database + printf_mysql_query("SELECT `text` FROM `helpserv_requests` WHERE `id` = %d", helpnode->suppid); + res = mysql_use(); + if ((row2 = mysql_fetch_row(res)) != NULL) { + char *old_msg = escape_string(row2[0]); + char *new_msg = escape_string(message); + printf_long_mysql_query(1024 + strlen(old_msg) + strlen(new_msg), "UPDATE `helpserv_requests` SET `text` = '%s\n%s' WHERE `id` = %d", old_msg, new_msg, helpnode->suppid); + } + return; + } + others++; + } + } + //add new request + struct NeonHelpNode *helpnode = malloc(sizeof(*helpnode)); + if(!helpnode) return; + helpnode->user = user; + helpnode->logchan = getChanByName(row[0]); + helpnode->status = 0; + helpnode->announce = (row[2] && strcmp(row[3], "0") ? 1 : 0); + if(helpnode->announce) { + char nameBuf[30]; + sprintf(nameBuf, "neonhelp_%d", client->clientid); + if(!timeq_name_exists(nameBuf)) { + int *cidptr = malloc(sizeof(int)); + *cidptr = client->clientid; + timeq_add_name(nameBuf, 300, module_id, neonhelp_remind_open_requests, cidptr); + } + } + printf_mysql_query("INSERT INTO `helpserv_requests` (`botid`, `host`, `hand`, `nick`, `status`, `supporter`, `time`, `text`) VALUES ('%d', '%s@%s', '%s', '%s', '0', '-1', UNIX_TIMESTAMP(), '%s')", client->clientid, escape_string(user->ident), escape_string(user->host), ((user->flags & USERFLAG_ISAUTHED) ? escape_string(user->auth) : "*"), escape_string(user->nick), escape_string(message)); + helpnode->suppid = (int) mysql_insert_id(get_mysql_conn()); + helpnode->log = NULL; + helpnode->next = ((client->flags & SOCKET_HAVE_HELPNODE) ? client->botclass_helpnode : NULL); + client->botclass_helpnode = helpnode; + client->flags |= SOCKET_HAVE_HELPNODE; + //build the user reply... + char user_reply[MAXLEN]; + int user_reply_pos = 0; + char reply_buff[MAXLEN]; + //1st part: NH_REQUEST_RECORDED + strcpy(user_reply + user_reply_pos, build_language_string(user, reply_buff, "NH_REQUEST_RECORDED", helpnode->suppid)); + user_reply_pos += strlen(reply_buff); + //2nd part: NH_REQUEST_OTHERS_0 / NH_REQUEST_OTHERS_1 / NH_REQUEST_OTHERS_2 + user_reply[user_reply_pos++] = ' '; + if(others <= 1) + strcpy(user_reply + user_reply_pos, build_language_string(user, reply_buff, (others ? "NH_REQUEST_OTHERS_1" : "NH_REQUEST_OTHERS_0"))); + else + strcpy(user_reply + user_reply_pos, build_language_string(user, reply_buff, "NH_REQUEST_OTHERS_2", others)); + user_reply_pos += strlen(reply_buff); + //3th part: NH_REQUEST_FOOTER_1 / NH_REQUEST_FOOTER_2 + user_reply[user_reply_pos++] = ' '; + if(row[1]) + strcpy(user_reply + user_reply_pos, build_language_string(user, reply_buff, "NH_REQUEST_FOOTER_2", row[0], row[1])); + else + strcpy(user_reply + user_reply_pos, build_language_string(user, reply_buff, "NH_REQUEST_FOOTER_1", row[0])); + user_reply_pos += strlen(reply_buff); + reply(client, user, "%s", user_reply); + //sent a message to the internal channel / onotice to supp channel + build_language_string(user, reply_buff, "NH_NEW_REQUEST", helpnode->suppid, user->nick, message); + if(row[2]) { + putsock(client, "PRIVMSG %s :%s", row[2], reply_buff); + } else { + putsock(client, "NOTICE @%s :%s", row[0], reply_buff); + } +} + +static TIMEQ_CALLBACK(neonhelp_remind_open_requests) { + int clientid = *((int*)data); + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `helpserv_support`, `helpserv_public`, `helpserv_intern`, `helpserv_intern_announce` FROM `helpserv_settings` WHERE `helpserv_botid` = '%d'", clientid); + res = mysql_use(); + if (!(row = mysql_fetch_row(res)) || !row[2]) { + free(data); + return; + } + struct ClientSocket *client; + for(client = getBots(SOCKET_FLAG_READY, NULL); client; client = getBots(SOCKET_FLAG_READY, client)) { + if(client->clientid == clientid) + break; + } + if(!client) { + free(data); + return; + } + //count open requests + int requests = 0; + struct NeonHelpNode *helpnode; + if(client->flags & SOCKET_HAVE_HELPNODE) { + for(helpnode = client->botclass_helpnode; helpnode; helpnode = helpnode->next) { + if(helpnode->status == 0) { + requests++; + } + } + } + if(requests) { + char nameBuf[30]; + sprintf(nameBuf, "neonhelp_%d", client->clientid); + if(!timeq_name_exists(nameBuf)) { + timeq_add_name(nameBuf, 300, module_id, neonhelp_remind_open_requests, data); + } + char replybuf[MAXLEN]; + build_language_string(NULL, replybuf, (requests == 1 ? "NH_REMIND_OPEN_REQUESTS_1" : "NH_REMIND_OPEN_REQUESTS_2"), requests); + putsock(client, "PRIVMSG %s :%s", row[2], replybuf); + } else + free(data); +} + +static void neonhelp_event_chanmsg(struct UserNode *user, struct ChanNode *chan, char *message) { + char logline[MAXLEN]; + sprintf(logline, "<%s> %s", user->nick, message); + struct ClientSocket *client; + for(client = getBots(SOCKET_FLAG_READY, NULL); client; client = getBots(SOCKET_FLAG_READY, client)) { + if(client->botid == BOTID) { + struct NeonHelpNode *helpnode; + if(client->flags & SOCKET_HAVE_HELPNODE) { + for(helpnode = client->botclass_helpnode; helpnode; helpnode = helpnode->next) { + if(helpnode->logchan == chan && helpnode->status == 1) { + if(!helpnode->log) { + helpnode->log = calloc(LOGBUFFERLINES, sizeof(char*)); + if(!helpnode->log) return; + } + int i; + for(i = 0; i < LOGBUFFERLINES; i++) { + if(!helpnode->log[i]) { + helpnode->log[i] = strdup(logline); + break; + } + } + if(i == LOGBUFFERLINES) { + //write buffer to database + char logbuff[MAXLEN * LOGBUFFERLINES]; + int len = 0; + for(i = 0; i < LOGBUFFERLINES; i++) { + len += sprintf(logbuff + len, "%s\n", helpnode->log[i]); + free(helpnode->log[i]); + helpnode->log[i] = NULL; + } + printf_long_mysql_query(1024 + len, "UPDATE `helpserv_requests` SET `log` = CONCAT(`log`, '%s') WHERE `id` = %d", escape_string(logbuff), helpnode->suppid); + } + break; + } + } + } + } + } +} + +void neonhelp_destroy_support_request(struct ClientSocket *client, struct NeonHelpNode *helpnode, int do_reply) { + //write buffer to database + char logbuff[MAXLEN * LOGBUFFERLINES]; + int len = 0; + int i; + if(helpnode->log) { + for(i = 0; i < LOGBUFFERLINES; i++) { + if(!helpnode->log[i]) break; + len += sprintf(logbuff + len, "%s\n", helpnode->log[i]); + free(helpnode->log[i]); + helpnode->log[i] = NULL; + } + free(helpnode->log); + } else + logbuff[0] = '\0'; + printf_long_mysql_query(1024 + len, "UPDATE `helpserv_requests` SET `status`='2', `log` = CONCAT(`log`, '%s') WHERE `id` = %d", escape_string(logbuff), helpnode->suppid); + if(do_reply) { + reply(client, helpnode->user, "NH_DELETED", helpnode->suppid); + } + free(helpnode); +} + +void neonhelp_invite_active_requests(struct ClientSocket *client, struct ChanNode *support, struct ChanNode *public, struct UserNode *ignore_user) { + struct NeonHelpNode *helpnode; + if(public && client->flags & SOCKET_HAVE_HELPNODE) { + for(helpnode = client->botclass_helpnode; helpnode; helpnode = helpnode->next) { + if(helpnode->status == 1 && helpnode->user != ignore_user && !isUserOnChan(helpnode->user, support)) { + putsock(client, "INVITE %s %s", helpnode->user->nick, support->name); + } + } + } +} + +static void neonhelp_event_kick(struct UserNode *user, struct ChanUser *target, char *reason) { + struct ClientSocket *client; + MYSQL_RES *res; + MYSQL_ROW row; + struct ChanNode *support, *public; + int userHasRequest; + for(client = getBots(SOCKET_FLAG_READY, NULL); client; client = getBots(SOCKET_FLAG_READY, client)) { + if(client->botid == BOTID && isUserOnChan(client->user, target->chan)) { + userHasRequest = 0; + struct NeonHelpNode *helpnode, *prev_helpnode = NULL; + if(client->flags & SOCKET_HAVE_HELPNODE) { + for(helpnode = client->botclass_helpnode; helpnode; helpnode = helpnode->next) { + if(helpnode->user == target->user) { + userHasRequest = 1; + break; + } else + prev_helpnode = helpnode; + } + } + if(!userHasRequest) continue; + printf_mysql_query("SELECT `helpserv_support`, `helpserv_public`, `helpserv_intern` FROM `helpserv_settings` WHERE `helpserv_botid` = '%d'", client->clientid); + res = mysql_use(); + if (!(row = mysql_fetch_row(res))) continue; + support = getChanByName(row[0]); + public = (row[1] ? getChanByName(row[1]) : NULL); + if(target->chan == support || !((support && isUserOnChan(target->user, support)) || (public && isUserOnChan(target->user, public)))) { + if(helpnode->status == 1 && target->chan != support) { + putsock(client, "MODE %s -i", support->name); //clear invite list + if(isModeSet(support->modes, 'i')) + putsock(client, "MODE %s +i", support->name); + neonhelp_invite_active_requests(client, support, public, target->user); + } + //free the user's support request + if(prev_helpnode) + prev_helpnode->next = helpnode->next; + else + client->botclass_helpnode = helpnode->next; + neonhelp_destroy_support_request(client, helpnode, 1); + } + } + } +} + +static void neonhelp_event_part(struct ChanUser *target, int quit, char *reason) { + struct ClientSocket *client; + MYSQL_RES *res; + MYSQL_ROW row; + struct ChanNode *support, *public; + int userHasRequest; + for(client = getBots(SOCKET_FLAG_READY, NULL); client; client = getBots(SOCKET_FLAG_READY, client)) { + if(client->botid == BOTID && isUserOnChan(client->user, target->chan)) { + userHasRequest = 0; + struct NeonHelpNode *helpnode, *prev_helpnode = NULL; + if(client->flags & SOCKET_HAVE_HELPNODE) { + for(helpnode = client->botclass_helpnode; helpnode; helpnode = helpnode->next) { + if(helpnode->user == target->user) { + userHasRequest = 1; + break; + } else + prev_helpnode = helpnode; + } + } + if(!userHasRequest) continue; + printf_mysql_query("SELECT `helpserv_support`, `helpserv_public`, `helpserv_intern` FROM `helpserv_settings` WHERE `helpserv_botid` = '%d'", client->clientid); + res = mysql_use(); + if (!(row = mysql_fetch_row(res))) continue; + support = getChanByName(row[0]); + public = (row[1] ? getChanByName(row[1]) : NULL); + if(target->chan == support || !((support && isUserOnChan(target->user, support)) || (public && isUserOnChan(target->user, public)))) { + if(helpnode->status == 1 && target->chan != support) { + putsock(client, "MODE %s -i", support->name); //clear invite list + if(isModeSet(support->modes, 'i')) + putsock(client, "MODE %s +i", support->name); + neonhelp_invite_active_requests(client, support, public, target->user); + } + //free the user's support request + if(prev_helpnode) + prev_helpnode->next = helpnode->next; + else + client->botclass_helpnode = helpnode->next; + neonhelp_destroy_support_request(client, helpnode, 1); + } + } + } +} + +static void neonhelp_event_invite(struct ClientSocket *client, struct UserNode *user, char *channel) { + if(client->botid != BOTID) + return; + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `botid`, `bot_channels`.`id`, `suspended` FROM `bot_channels` LEFT JOIN `bots` ON `bot_channels`.`botid` = `bots`.`id` LEFT JOIN `channels` ON `chanid` = `channel_id` WHERE `channel_name` = '%s' AND `botclass` = '%d'", escape_string(channel), client->botid); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) == NULL) { + reply(client, user, "NS_INVITE_FAIL", channel, client->user->nick); + return; + } + if(!strcmp(row[2], "1")) { + reply(client, user, "MODCMD_CHAN_SUSPENDED"); + return; + } + int botid = atoi(row[0]); + struct ClientSocket *bot; + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(bot->clientid == botid) + break; + } + if(bot) { + struct ChanNode *chan = getChanByName(channel); + if(chan && isUserOnChan(bot->user, chan)) { + reply(client, user, "NS_INVITE_ON_CHAN", bot->user->nick, chan->name); + } else + putsock(bot, "JOIN %s", channel); + } +} + +void init_NeonHelp(int type) { + set_bot_alias(BOTID, BOTALIAS); + start_bots(type); + + if(type == MODSTATE_REBIND) return; + + //register events + bind_bot_ready(neonhelp_bot_ready, module_id); + bind_privmsg(neonhelp_event_privmsg, module_id); + bind_chanmsg(neonhelp_event_chanmsg, module_id); + bind_part(neonhelp_event_part, module_id); + bind_kick(neonhelp_event_kick, module_id); + bind_invite(neonhelp_event_invite, module_id); + + set_trigger_callback(BOTID, module_id, neonhelp_trigger_callback); + + register_default_language_table(msgtab); +} + +void free_NeonHelp(int type) { + unbind_allcmd(BOTID); + if(type == MODSTATE_STARTSTOP) { + //disconnect all our bots + struct ClientSocket *client; + for(client = getBots(0, NULL); client; client = getBots(0, client)) { + if(client->botid == BOTID) { + unbind_botwise_allcmd(0, client->clientid); + close_socket(client); + break; + } + } + } +} + +#undef BOTID +#undef BOTALIAS diff --git a/src/modules/NeonHelp.mod/bot_NeonHelp.h b/src/modules/NeonHelp.mod/bot_NeonHelp.h new file mode 100644 index 0000000..d20238e --- /dev/null +++ b/src/modules/NeonHelp.mod/bot_NeonHelp.h @@ -0,0 +1,48 @@ +/* bot_NeonHelp.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _bot_NeonHelp_h +#define _bot_NeonHelp_h + +#include "../../main.h" + +/* definition for ClientSocket struct */ +#define botclass_helpnode botclassvalue1 +#define SOCKET_HAVE_HELPNODE SOCKET_HAVE_BOTCLASSVALUE1 + +#define LOGBUFFERLINES 20 + +struct UserNode; +struct ChanNode; +struct ClientSocket; + +struct NeonHelpNode { + struct UserNode *user; + int suppid; + char status:6; + char announce:2; + struct NeonHelpNode *next; + struct ChanNode *logchan; + char **log; +}; + +void init_NeonHelp(int type); +void free_NeonHelp(int type); + +void neonhelp_destroy_support_request(struct ClientSocket *client, struct NeonHelpNode *helpnode, int do_reply); +void neonhelp_invite_active_requests(struct ClientSocket *client, struct ChanNode *support, struct ChanNode *public, struct UserNode *ignore_user); + +#endif \ No newline at end of file diff --git a/src/modules/NeonHelp.mod/cmd_neonhelp.c b/src/modules/NeonHelp.mod/cmd_neonhelp.c new file mode 100644 index 0000000..3c8be7d --- /dev/null +++ b/src/modules/NeonHelp.mod/cmd_neonhelp.c @@ -0,0 +1,35 @@ +/* cmd_neonhelp.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#include "../module.h" +#include "cmd_neonhelp.h" +#include "../../modcmd.h" +#include "../../ConfigParser.h" + +void register_commands() { + //NeonHelp Commands + register_command_alias(4, "NeonHelp"); + + #define USER_COMMAND(NAME,FUNCTION,PARAMCOUNT,PRIVS,FLAGS) register_command(4, NAME, module_id, FUNCTION, PARAMCOUNT, PRIVS, 0, FLAGS) + // NAME FUNCTION PARAMS PRIVS FLAGS + USER_COMMAND("next", neonhelp_cmd_next, 0, NULL, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH); + USER_COMMAND("delete", neonhelp_cmd_delete, 1, NULL, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH); + USER_COMMAND("requests", neonhelp_cmd_requests, 0, NULL, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH); + USER_COMMAND("stats", neonhelp_cmd_stats, 0, NULL, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH); + USER_COMMAND("history", neonhelp_cmd_history, 1, NULL, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH); + #undef USER_COMMAND + +} \ No newline at end of file diff --git a/src/modules/NeonHelp.mod/cmd_neonhelp.h b/src/modules/NeonHelp.mod/cmd_neonhelp.h new file mode 100644 index 0000000..42fe32d --- /dev/null +++ b/src/modules/NeonHelp.mod/cmd_neonhelp.h @@ -0,0 +1,41 @@ +/* cmd_neonhelp.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _cmd_neonhelp_h +#define _cmd_neonhelp_h +#include "../module.h" +#include "../../main.h" +#include "../../modcmd.h" +#include "../../mysqlConn.h" +#include "../../ClientSocket.h" +#include "../../UserNode.h" +#include "../../ChanNode.h" +#include "../../ChanUser.h" +#include "../../DBHelper.h" +#include "../../IRCParser.h" +#include "bot_NeonHelp.h" +#include "../../lang.h" +#include "../../tools.h" + +void register_commands(); + +CMD_BIND(neonhelp_cmd_delete); +CMD_BIND(neonhelp_cmd_history); +CMD_BIND(neonhelp_cmd_next); +CMD_BIND(neonhelp_cmd_requests); +CMD_BIND(neonhelp_cmd_stats); + +#endif \ No newline at end of file diff --git a/src/modules/NeonHelp.mod/cmd_neonhelp_delete.c b/src/modules/NeonHelp.mod/cmd_neonhelp_delete.c new file mode 100644 index 0000000..d9c9262 --- /dev/null +++ b/src/modules/NeonHelp.mod/cmd_neonhelp_delete.c @@ -0,0 +1,96 @@ +/* cmd_neonhelp_delete.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonhelp.h" + +/* +* argv[0] suppid +*/ + +CMD_BIND(neonhelp_cmd_delete) { + //check permissions + MYSQL_RES *res; + MYSQL_ROW row, row2; + int caccess = 0; + printf_mysql_query("SELECT `helpserv_support`, `helpserv_public`, `helpserv_intern` FROM `helpserv_settings` WHERE `helpserv_botid` = '%d'", client->clientid); + res = mysql_use(); + if (!(row = mysql_fetch_row(res))) return; + //check if the user is a supporter (access in the support channel) + if((user->flags & USERFLAG_ISAUTHED)) { + int userid; + if(user->flags & USERFLAG_HAS_USERID) + userid = user->user_id; + else { + printf_mysql_query("SELECT `user_id` FROM `users` WHERE `user_user` = '%s'", escape_string(user->auth)); + res = mysql_use(); + if ((row2 = mysql_fetch_row(res)) != NULL) { + userid = atoi(row2[0]); + user->user_id = userid; + user->flags |= USERFLAG_HAS_USERID; + } else + userid = 0; + } + printf_mysql_query("SELECT `chanuser_access`, `chanuser_flags` FROM `chanusers` LEFT JOIN `channels` ON `chanuser_cid` = `channel_id` WHERE `chanuser_uid` = '%d' AND `channel_name` = '%s'", userid, escape_string(row[0])); + res = mysql_use(); + if ((row2 = mysql_fetch_row(res)) != NULL) { + int cflags = atoi(row2[1]); + if(!(cflags & DB_CHANUSER_SUSPENDED)) + caccess = atoi(row2[0]); + } + } + if(!caccess) { + reply(textclient, user, "MODCMD_ACCESS_DENIED"); + return; + } + if(!(client->flags & SOCKET_HAVE_HELPNODE) || client->botclass_helpnode == NULL) { + reply(textclient, user, "NH_NEXT_NONE"); + return; + } + struct NeonHelpNode *helpnode, *prev_helpnode = NULL; + for(helpnode = client->botclass_helpnode; helpnode; helpnode = helpnode->next) { + if(atoi(argv[0]) == helpnode->suppid) + break; + else + prev_helpnode = helpnode; + } + if(!helpnode) { + reply(textclient, user, "NH_NEXT_NOT_FOUND"); + return; + } + if(helpnode->status == 1) { + struct ChanNode *support, *public; + support = getChanByName(row[0]); + public = (row[1] ? getChanByName(row[1]) : NULL); + if(isUserOnChan(helpnode->user, support)) { + if(public) + putsock(client, "KICK %s %s :your request has been closed", support->name, helpnode->user->nick); + else + putsock(client, "MODE %s -v %s", support->name, helpnode->user->nick); + } else { + putsock(client, "MODE %s -i", support->name); //clear invite list + if(isModeSet(support->modes, 'i')) + putsock(client, "MODE %s +i", support->name); + neonhelp_invite_active_requests(client, support, public, helpnode->user); + } + } + if(prev_helpnode) + prev_helpnode->next = helpnode->next; + else + client->botclass_helpnode = helpnode->next; + reply(textclient, user, "NH_DELETED_STAFF", helpnode->suppid, helpnode->user->nick); + neonhelp_destroy_support_request(client, helpnode, 1); +} diff --git a/src/modules/NeonHelp.mod/cmd_neonhelp_history.c b/src/modules/NeonHelp.mod/cmd_neonhelp_history.c new file mode 100644 index 0000000..72d81f6 --- /dev/null +++ b/src/modules/NeonHelp.mod/cmd_neonhelp_history.c @@ -0,0 +1,136 @@ +/* cmd_neonhelp_history.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonhelp.h" + +/* +* argv[0] matching string +*/ + +CMD_BIND(neonhelp_cmd_history) { + //check permissions + MYSQL_RES *res; + MYSQL_ROW row, row2; + int caccess = 0; + int userid; + printf_mysql_query("SELECT `helpserv_support`, `helpserv_public`, `helpserv_intern` FROM `helpserv_settings` WHERE `helpserv_botid` = '%d'", client->clientid); + res = mysql_use(); + if (!(row = mysql_fetch_row(res))) return; + //check if the user is a supporter (access in the support channel) + if((user->flags & USERFLAG_ISAUTHED)) { + if(user->flags & USERFLAG_HAS_USERID) + userid = user->user_id; + else { + printf_mysql_query("SELECT `user_id` FROM `users` WHERE `user_user` = '%s'", escape_string(user->auth)); + res = mysql_use(); + if ((row2 = mysql_fetch_row(res)) != NULL) { + userid = atoi(row2[0]); + user->user_id = userid; + user->flags |= USERFLAG_HAS_USERID; + } else + userid = 0; + } + printf_mysql_query("SELECT `chanuser_access`, `chanuser_flags` FROM `chanusers` LEFT JOIN `channels` ON `chanuser_cid` = `channel_id` WHERE `chanuser_uid` = '%d' AND `channel_name` = '%s'", userid, escape_string(row[0])); + res = mysql_use(); + if ((row2 = mysql_fetch_row(res)) != NULL) { + int cflags = atoi(row2[1]); + if(!(cflags & DB_CHANUSER_SUSPENDED)) + caccess = atoi(row2[0]); + } + } + if(!caccess) { + reply(textclient, user, "MODCMD_ACCESS_DENIED"); + return; + } + struct Table *table; + char *match_str = escape_string(argv[0]); + printf_mysql_query("SELECT `id`, `nick`, `hand`, `host`, `time`, `text`, `status`, `supporter`, `users`.`user_user`, MD5(CONCAT(`id`, `host`, `time`, `supporter`)) FROM `helpserv_requests` LEFT JOIN `users` ON `supporter` = `user_id` WHERE `botid` = '%d' AND (`host` LIKE '%%%s%%' OR `hand` LIKE '%%%s%%' OR `nick` LIKE '%%%s%%') ORDER BY `time` ASC LIMIT 100", client->clientid, match_str, match_str, match_str); + res = mysql_use(); + table = table_init(6, mysql_num_rows(res)*3 + 1, 0); + table->col_flags[1] |= TABLE_FLAG_COL_SKIP_NULL; + table->col_flags[2] |= TABLE_FLAG_COL_SKIP_NULL; + table->col_flags[3] |= TABLE_FLAG_COL_SKIP_NULL; + table->col_flags[4] |= TABLE_FLAG_COL_SKIP_NULL; + char *content[6]; + content[0] = get_language_string(user, "NH_REQUESTS_HEADER_ID"); + content[1] = get_language_string(user, "NH_REQUESTS_HEADER_NICK"); + content[2] = get_language_string(user, "NH_REQUESTS_HEADER_AUTH"); + content[3] = get_language_string(user, "NH_REQUESTS_HEADER_MASK"); + content[4] = get_language_string(user, "NH_REQUESTS_HEADER_TIME"); + content[5] = get_language_string(user, "NH_REQUESTS_HEADER_STATUS"); + table_add(table, content); + char timestr[MAXLEN]; + time_t rawtime; + struct tm *timeinfo; + char statestr[MAXLEN]; + while ((row = mysql_fetch_row(res)) != NULL) { + content[0] = row[0]; + content[1] = row[1]; + content[2] = row[2]; + content[3] = row[3]; + rawtime = atoi(row[4]); + timeinfo = localtime(&rawtime); + strftime(timestr, MAXLEN, "%d.%m.%Y %H:%M:%S", timeinfo); + content[4] = timestr; + switch(atoi(row[6])) { + case 0: + content[5] = get_language_string(user, "NH_REQUESTS_STATE_PENDING"); + break; + case 1: + sprintf(statestr, "%s (%s)", get_language_string(user, "NH_REQUESTS_STATE_ACTIVE"), row[8]); + content[5] = statestr; + break; + case 2: + sprintf(statestr, "%s (%s)", get_language_string(user, "NH_REQUESTS_STATE_CLOSED"), (strcmp(row[7], "0") ? row[8] : "*")); + content[5] = statestr; + break; + } + table_add(table, content); + char *p; + if((p = strstr(row[5], "\n"))) + *p = '\0'; + content[0] = " "; + content[1] = NULL; + content[2] = NULL; + content[3] = NULL; + content[4] = NULL; + sprintf(statestr, "\00314\002\002%s\003", row[5]); + content[5] = statestr; + table_add(table, content); + char setting[128]; + sprintf(setting, "modules.%s.log_link", get_module_name(module_id)); + if((p = get_string_field(setting))) { + statestr[0] = '\003'; + statestr[1] = '1'; + statestr[2] = '4'; + int pos = sprintf(statestr+3, p, row[0], row[9]); + statestr[pos+3] = '\003'; + statestr[pos+4] = '\0'; + content[5] = statestr; + table_add(table, content); + } + } + //send the table + char **table_lines = table_end(table); + int i; + for(i = 0; i < table->entrys; i++) { + reply(textclient, user, table_lines[i]); + } + if(table->entrys == 1) + reply(textclient, user, "NS_TABLE_NONE"); + table_free(table); +} diff --git a/src/modules/NeonHelp.mod/cmd_neonhelp_next.c b/src/modules/NeonHelp.mod/cmd_neonhelp_next.c new file mode 100644 index 0000000..033772f --- /dev/null +++ b/src/modules/NeonHelp.mod/cmd_neonhelp_next.c @@ -0,0 +1,108 @@ +/* cmd_neonhelp_next.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonhelp.h" + +/* +* argv[0] (optional) suppid +*/ + +CMD_BIND(neonhelp_cmd_next) { + //check permissions + MYSQL_RES *res; + MYSQL_ROW row, row2; + int caccess = 0; + int userid; + printf_mysql_query("SELECT `helpserv_support`, `helpserv_public`, `helpserv_intern` FROM `helpserv_settings` WHERE `helpserv_botid` = '%d'", client->clientid); + res = mysql_use(); + if (!(row = mysql_fetch_row(res))) return; + //check if the user is a supporter (access in the support channel) + if((user->flags & USERFLAG_ISAUTHED)) { + if(user->flags & USERFLAG_HAS_USERID) + userid = user->user_id; + else { + printf_mysql_query("SELECT `user_id` FROM `users` WHERE `user_user` = '%s'", escape_string(user->auth)); + res = mysql_use(); + if ((row2 = mysql_fetch_row(res)) != NULL) { + userid = atoi(row2[0]); + user->user_id = userid; + user->flags |= USERFLAG_HAS_USERID; + } else + userid = 0; + } + printf_mysql_query("SELECT `chanuser_access`, `chanuser_flags` FROM `chanusers` LEFT JOIN `channels` ON `chanuser_cid` = `channel_id` WHERE `chanuser_uid` = '%d' AND `channel_name` = '%s'", userid, escape_string(row[0])); + res = mysql_use(); + if ((row2 = mysql_fetch_row(res)) != NULL) { + int cflags = atoi(row2[1]); + if(!(cflags & DB_CHANUSER_SUSPENDED)) + caccess = atoi(row2[0]); + } + } + if(!caccess) { + reply(textclient, user, "MODCMD_ACCESS_DENIED"); + return; + } + if(!(client->flags & SOCKET_HAVE_HELPNODE) || client->botclass_helpnode == NULL) { + reply(textclient, user, "NH_NEXT_NONE"); + return; + } + struct NeonHelpNode *helpnode, *next_helpnode = NULL; + int lowest_id = -1; + for(helpnode = client->botclass_helpnode; helpnode; helpnode = helpnode->next) { + if(helpnode->status == 0) { + if(argc) { + if(atoi(argv[0]) == helpnode->suppid) { + next_helpnode = helpnode; + break; + } + } else { + if(helpnode->suppid < lowest_id || lowest_id == -1) { + lowest_id = helpnode->suppid; + next_helpnode = helpnode; + } + } + } + } + if(!next_helpnode) { + reply(textclient, user, "NH_NEXT_NOT_FOUND"); + return; + } + printf_mysql_query("SELECT `text` FROM `helpserv_requests` WHERE `id` = '%d'", next_helpnode->suppid); + res = mysql_use(); + if (!(row2 = mysql_fetch_row(res))) return; + reply(textclient, user, "NH_NEXT_HEADER", next_helpnode->suppid, next_helpnode->user->nick); + char *a, *b = row2[0]; + do { + a = strstr(b, "\n"); + if(a) *a = '\0'; + reply(textclient, user, " %s", b); + if(a) { + *a = '\n'; + b = a+1; + } + } while(a); + if(row[1]) + putsock(client, "INVITE %s %s", next_helpnode->user->nick, row[0]); + else + putsock(client, "MODE %s +v %s", row[0], next_helpnode->user->nick); + char sendbuf1[MAXLEN]; + char sendbuf2[MAXLEN]; + char *join_now = (row[1] ? build_language_string(user, sendbuf2, "NH_NEXT_JOIN", row[0]) : ""); + putsock(client, "PRIVMSG %s :%s %s", next_helpnode->user->nick, build_language_string(user, sendbuf1, "NH_NEXT_HELPER", next_helpnode->suppid, user->auth, user->nick), join_now); + next_helpnode->status = 1; + printf_mysql_query("UPDATE `helpserv_requests` SET `status` = '1', `supporter` = '%d' WHERE `id` = '%d'", userid, next_helpnode->suppid); +} diff --git a/src/modules/NeonHelp.mod/cmd_neonhelp_requests.c b/src/modules/NeonHelp.mod/cmd_neonhelp_requests.c new file mode 100644 index 0000000..8906a91 --- /dev/null +++ b/src/modules/NeonHelp.mod/cmd_neonhelp_requests.c @@ -0,0 +1,108 @@ +/* cmd_neonhelp_requests.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonhelp.h" + +/* +* no args +*/ + +CMD_BIND(neonhelp_cmd_requests) { + //check permissions + MYSQL_RES *res; + MYSQL_ROW row, row2; + int caccess = 0; + int userid; + printf_mysql_query("SELECT `helpserv_support`, `helpserv_public`, `helpserv_intern` FROM `helpserv_settings` WHERE `helpserv_botid` = '%d'", client->clientid); + res = mysql_use(); + if (!(row = mysql_fetch_row(res))) return; + //check if the user is a supporter (access in the support channel) + if((user->flags & USERFLAG_ISAUTHED)) { + if(user->flags & USERFLAG_HAS_USERID) + userid = user->user_id; + else { + printf_mysql_query("SELECT `user_id` FROM `users` WHERE `user_user` = '%s'", escape_string(user->auth)); + res = mysql_use(); + if ((row2 = mysql_fetch_row(res)) != NULL) { + userid = atoi(row2[0]); + user->user_id = userid; + user->flags |= USERFLAG_HAS_USERID; + } else + userid = 0; + } + printf_mysql_query("SELECT `chanuser_access`, `chanuser_flags` FROM `chanusers` LEFT JOIN `channels` ON `chanuser_cid` = `channel_id` WHERE `chanuser_uid` = '%d' AND `channel_name` = '%s'", userid, escape_string(row[0])); + res = mysql_use(); + if ((row2 = mysql_fetch_row(res)) != NULL) { + int cflags = atoi(row2[1]); + if(!(cflags & DB_CHANUSER_SUSPENDED)) + caccess = atoi(row2[0]); + } + } + if(!caccess) { + reply(textclient, user, "MODCMD_ACCESS_DENIED"); + return; + } + struct Table *table; + printf_mysql_query("SELECT `id`, `time`, `text` FROM `helpserv_requests` WHERE `botid` = '%d' AND `status` != 2 ORDER BY `time` DESC", client->clientid); + res = mysql_use(); + table = table_init(5, mysql_num_rows(res) + 1, 0); + char *content[5]; + content[0] = get_language_string(user, "NH_REQUESTS_HEADER_ID"); + content[1] = get_language_string(user, "NH_REQUESTS_HEADER_STATUS"); + content[2] = get_language_string(user, "NH_REQUESTS_HEADER_NICK"); + content[3] = get_language_string(user, "NH_REQUESTS_HEADER_TIME"); + content[4] = get_language_string(user, "NH_REQUESTS_HEADER_REQUEST"); + table_add(table, content); + char timestr[MAXLEN]; + struct NeonHelpNode *helpnode; + int suppid; + while ((row = mysql_fetch_row(res)) != NULL) { + content[0] = row[0]; + suppid = atoi(row[0]); + if(client->flags & SOCKET_HAVE_HELPNODE) { + for(helpnode = client->botclass_helpnode; helpnode; helpnode = helpnode->next) { + if(helpnode->suppid == suppid) { + if(helpnode->status) + content[1] = get_language_string(user, "NH_REQUESTS_STATE_ACTIVE"); + else + content[1] = get_language_string(user, "NH_REQUESTS_STATE_PENDING"); + content[2] = helpnode->user->nick; + break; + } + } + } else { + content[1] = get_language_string(user, "NH_REQUESTS_STATE_ERROR"); + content[2] = NULL; + } + timeToStr(user, (time(0) - atoi(row[1])), 2, timestr); + content[3] = timestr; + char *p; + if((p = strstr(row[2], "\n"))) + *p = '\0'; + content[4] = row[2]; + table_add(table, content); + } + //send the table + char **table_lines = table_end(table); + int i; + for(i = 0; i < table->entrys; i++) { + reply(textclient, user, table_lines[i]); + } + if(table->entrys == 1) + reply(textclient, user, "NS_TABLE_NONE"); + table_free(table); +} diff --git a/src/modules/NeonHelp.mod/cmd_neonhelp_stats.c b/src/modules/NeonHelp.mod/cmd_neonhelp_stats.c new file mode 100644 index 0000000..1628b2b --- /dev/null +++ b/src/modules/NeonHelp.mod/cmd_neonhelp_stats.c @@ -0,0 +1,88 @@ +/* cmd_neonhelp_stats.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonhelp.h" + +/* +* argv[0] user +*/ + +CMD_BIND(neonhelp_cmd_stats) { + //check permissions + MYSQL_RES *res; + MYSQL_ROW row, row2; + int caccess = 0; + printf_mysql_query("SELECT `helpserv_support`, `helpserv_public`, `helpserv_intern` FROM `helpserv_settings` WHERE `helpserv_botid` = '%d'", client->clientid); + res = mysql_use(); + if (!(row = mysql_fetch_row(res))) return; + //check if the user is a supporter (access in the support channel) + if((user->flags & USERFLAG_ISAUTHED)) { + int userid; + if(user->flags & USERFLAG_HAS_USERID) + userid = user->user_id; + else { + printf_mysql_query("SELECT `user_id` FROM `users` WHERE `user_user` = '%s'", escape_string(user->auth)); + res = mysql_use(); + if ((row2 = mysql_fetch_row(res)) != NULL) { + userid = atoi(row2[0]); + user->user_id = userid; + user->flags |= USERFLAG_HAS_USERID; + } else + userid = 0; + } + printf_mysql_query("SELECT `chanuser_access`, `chanuser_flags` FROM `chanusers` LEFT JOIN `channels` ON `chanuser_cid` = `channel_id` WHERE `chanuser_uid` = '%d' AND `channel_name` = '%s'", userid, escape_string(row[0])); + res = mysql_use(); + if ((row2 = mysql_fetch_row(res)) != NULL) { + int cflags = atoi(row2[1]); + if(!(cflags & DB_CHANUSER_SUSPENDED)) + caccess = atoi(row2[0]); + } + } + if(!caccess) { + reply(textclient, user, "MODCMD_ACCESS_DENIED"); + return; + } + if(argc > 0) { + //following + } else { + struct Table *table; + printf_mysql_query("SELECT DISTINCT a.`supporter`, `user_user`, (SELECT COUNT(*) FROM `helpserv_requests` AS b WHERE b.`supporter` = a.`supporter` AND b.`time` > (UNIX_TIMESTAMP()-(3600*24))), (SELECT COUNT(*) FROM `helpserv_requests` AS b WHERE b.`supporter` = a.`supporter` AND b.`time` > (UNIX_TIMESTAMP()-(3600*24*7))), (SELECT COUNT(*) FROM `helpserv_requests` AS b WHERE b.`supporter` = a.`supporter` AND b.`time` > (UNIX_TIMESTAMP()-(3600*24*7*4))) FROM `helpserv_requests` AS a LEFT JOIN `users` ON a.`supporter` = `user_id` WHERE a.`time` > (UNIX_TIMESTAMP()-(3600*24*7*4)) AND `botid` = '%d' ORDER BY `user_user` ASC", client->clientid); + res = mysql_use(); + table = table_init(4, mysql_num_rows(res) + 1, 0); + char *content[4]; + content[0] = get_language_string(user, "NH_STATS_HEADER_USER"); + content[1] = get_language_string(user, "NH_STATS_HEADER_LAST_24H"); + content[2] = get_language_string(user, "NH_STATS_HEADER_LAST_7DAY"); + content[3] = get_language_string(user, "NH_STATS_HEADER_LAST_30DAY"); + table_add(table, content); + while ((row = mysql_fetch_row(res)) != NULL) { + content[0] = (strcmp(row[0], "-1") ? row[1] : "-"); + content[1] = row[2]; + content[2] = row[3]; + content[3] = row[4]; + table_add(table, content); + } + char **table_lines = table_end(table); + int i; + for(i = 0; i < table->entrys; i++) { + reply(textclient, user, table_lines[i]); + } + if(table->entrys == 1) + reply(textclient, user, "NS_TABLE_NONE"); + table_free(table); + } +} diff --git a/src/modules/NeonHelp.mod/module.c b/src/modules/NeonHelp.mod/module.c new file mode 100644 index 0000000..53cea0b --- /dev/null +++ b/src/modules/NeonHelp.mod/module.c @@ -0,0 +1,34 @@ +/* module.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#include "../module.h" +#include "bot_NeonHelp.h" +#include "cmd_neonhelp.h" + +static int module_initialize() { + register_commands(); + return 0; +} + +static void module_start(int type) { + init_NeonHelp(type); +} + +static void module_stop(int type) { + free_NeonHelp(type); +} + +MODULE_HEADER(module_initialize, module_start, module_stop); diff --git a/src/modules/NeonKick.mod/bot_NeonKick.c b/src/modules/NeonKick.mod/bot_NeonKick.c new file mode 100644 index 0000000..a3b7e93 --- /dev/null +++ b/src/modules/NeonKick.mod/bot_NeonKick.c @@ -0,0 +1,216 @@ +/* bot_NeonKick.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#include "../module.h" +#include "../botid.h" + +#include "bot_NeonKick.h" +#include "../../modcmd.h" +#include "../../IRCParser.h" +#include "../../IRCEvents.h" +#include "../../UserNode.h" +#include "../../ChanNode.h" +#include "../../ChanUser.h" +#include "../../ModeNode.h" +#include "../../BanNode.h" +#include "../../ClientSocket.h" +#include "../../mysqlConn.h" +#include "../../lang.h" +#include "../../HandleInfoHandler.h" +#include "../../WHOHandler.h" +#include "../../DBHelper.h" +#include "../../tools.h" +#include "../../timeq.h" +#include "../../version.h" +#include "../../EventLogger.h" +#include "../../bots.h" + +#define BOTID NEONKICK_BOTID +#define BOTALIAS "NeonKick" + +static void neonkick_bot_ready(struct ClientSocket *client) { + if(client->botid != BOTID) + return; + MYSQL_RES *res; + MYSQL_ROW row; + + printf_mysql_query("SELECT `automodes`, `oper_user`, `oper_pass` FROM `bots` WHERE `id` = '%d'", client->clientid); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + if(row[1] && row[2]) { + putsock(client, "OPER %s %s", row[1], row[2]); + } + putsock(client, "MODE %s +%s", client->user->nick, row[0]); + } + + printf_mysql_query("SELECT `channel_name`, `channel_key` FROM `bot_channels` LEFT JOIN `channels` ON `chanid` = `channel_id` WHERE `botid` = '%d' AND `suspended` = '0'", client->clientid); + res = mysql_use(); + + while ((row = mysql_fetch_row(res)) != NULL) { + putsock(client, "JOIN %s %s", row[0], row[1]); + } +} + +static void neonkick_trigger_callback(int clientid, struct ChanNode *chan, char *trigger) { + //this bot doesn't have a trigger + strcpy(trigger, ""); +} + +static void start_bots(int type) { + struct ClientSocket *client; + MYSQL_RES *res, *res2; + MYSQL_ROW row; + + if(type == MODSTATE_STARTSTOP) { + printf_mysql_query("SELECT `nick`, `ident`, `realname`, `server`, `port`, `pass`, `textbot`, `id`, `queue`, `ssl`, `bind`, `secret` FROM `bots` WHERE `botclass` = '%d' AND `active` = '1'", BOTID); + res = mysql_use(); + + while ((row = mysql_fetch_row(res)) != NULL) { + client = create_socket(row[3], atoi(row[4]), row[10], row[5], row[0], row[1], row[2]); + client->flags |= (strcmp(row[6], "0") ? SOCKET_FLAG_PREFERRED : 0); + client->flags |= (strcmp(row[8], "0") ? SOCKET_FLAG_USE_QUEUE : 0); + client->flags |= (strcmp(row[9], "0") ? SOCKET_FLAG_SSL : 0); + client->flags |= (strcmp(row[11], "0") ? SOCKET_FLAG_SECRET_BOT : 0); + client->flags |= SOCKET_FLAG_SILENT; + client->botid = BOTID; + client->clientid = atoi(row[7]); + connect_socket(client); + } + } + + printf_mysql_query("SELECT `command`, `function`, `parameters`, `global_access`, `chan_access`, `flags` FROM `bot_binds` WHERE `botclass` = '%d'", BOTID); + res2 = mysql_use(); + while ((row = mysql_fetch_row(res2)) != NULL) { + if(bind_cmd_to_command(BOTID, row[0], row[1])) { + if(row[2] && strcmp(row[2], "")) { + bind_set_parameters(BOTID, row[0], row[2]); + } + if(row[3]) { + bind_set_global_access(BOTID, row[0], atoi(row[3])); + } + if(row[4]) { + bind_set_channel_access(BOTID, row[0], row[4]); + } + if(strcmp(row[5], "0")) + bind_set_bind_flags(BOTID, row[0], atoi(row[5])); + } + } + bind_unbound_required_functions(BOTID); +} + +static void neonkick_event_invite(struct ClientSocket *client, struct UserNode *user, char *channel) { + if(client->botid != BOTID) + return; + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `botid`, `bot_channels`.`id`, `suspended` FROM `bot_channels` LEFT JOIN `bots` ON `bot_channels`.`botid` = `bots`.`id` LEFT JOIN `channels` ON `chanid` = `channel_id` WHERE `channel_name` = '%s' AND `botclass` = '%d'", escape_string(channel), client->botid); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) == NULL) { + reply(client, user, "NS_INVITE_FAIL", channel, client->user->nick); + return; + } + if(!strcmp(row[2], "1")) { + return; + } + int botid = atoi(row[0]); + struct ClientSocket *bot; + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(bot->clientid == botid) + break; + } + if(bot) { + struct ChanNode *chan = getChanByName(channel); + if(!(chan && isUserOnChan(bot->user, chan))) + putsock(bot, "JOIN %s", channel); + } +} + +static char* getSetting(struct UserNode *user, struct ChanNode *chan, const char *setting) { + char *uname = ""; + int cid = 0; + MYSQL_RES *res; + MYSQL_ROW row; + if(user) { + uname = ((user->flags & USERFLAG_ISAUTHED) ? user->auth : "*"); + } + if(chan) { + loadChannelSettings(chan); + if(chan->flags & CHANFLAG_CHAN_REGISTERED) + cid = chan->channel_id; + } + printf_mysql_query("SELECT `value` FROM `fundata` WHERE `user` = '%s' AND `cid` = '%d' AND `name` = '%s'", escape_string(uname), cid, escape_string(setting)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + return row[0]; + } else + return NULL; +} + +static void setSetting(struct UserNode *user, struct ChanNode *chan, const char *setting, const char *value) { + char *uname = ""; + int cid = 0; + MYSQL_RES *res; + MYSQL_ROW row; + if(user) { + uname = ((user->flags & USERFLAG_ISAUTHED) ? user->auth : "*"); + } + if(chan) { + loadChannelSettings(chan); + if(chan->flags & CHANFLAG_CHAN_REGISTERED) + cid = chan->channel_id; + } + printf_mysql_query("SELECT `id`, `value` FROM `fundata` WHERE `user` = '%s' AND `cid` = '%d' AND `name` = '%s'", escape_string(uname), cid, escape_string(setting)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + if(strcmp(row[1], value)) + printf_mysql_query("UPDATE `fundata` SET `value` = '%s' WHERE `id` = '%s'", escape_string(value), row[0]); + } else + printf_mysql_query("INSERT INTO `fundata` (`user`, `cid`, `name`, `value`) VALUES ('%s', '%d', '%s', '%s')", escape_string(uname), cid, escape_string(setting), escape_string(value)); +} + +#include "event_neonkick_join.c" + +void init_NeonKick(int type) { + set_bot_alias(BOTID, BOTALIAS); + start_bots(type); + + if(type == MODSTATE_REBIND) return; + + //register events + bind_bot_ready(neonkick_bot_ready, module_id); + bind_invite(neonkick_event_invite, module_id); + bind_join(neonkick_event_join, module_id); + + set_trigger_callback(BOTID, module_id, neonkick_trigger_callback); +} + +void free_NeonKick(int type) { + unbind_allcmd(BOTID); + if(type == MODSTATE_STARTSTOP) { + //disconnect all our bots + struct ClientSocket *client; + for(client = getBots(0, NULL); client; client = getBots(0, client)) { + if(client->botid == BOTID) { + unbind_botwise_allcmd(0, client->clientid); + close_socket(client); + break; + } + } + } +} + +#undef BOTID +#undef BOTALIAS diff --git a/src/modules/NeonKick.mod/bot_NeonKick.h b/src/modules/NeonKick.mod/bot_NeonKick.h new file mode 100644 index 0000000..bc3d8e7 --- /dev/null +++ b/src/modules/NeonKick.mod/bot_NeonKick.h @@ -0,0 +1,25 @@ +/* bot_NeonKick.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _bot_NeonKick_h +#define _bot_NeonKick_h + +#include "../../main.h" + +void init_NeonKick(int type); +void free_NeonKick(int type); + +#endif \ No newline at end of file diff --git a/src/modules/NeonKick.mod/event_neonkick_join.c b/src/modules/NeonKick.mod/event_neonkick_join.c new file mode 100644 index 0000000..6dc1224 --- /dev/null +++ b/src/modules/NeonKick.mod/event_neonkick_join.c @@ -0,0 +1,98 @@ +/* event_neonkick_join.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +struct neonkick_event_join_cache { + struct ClientSocket *client; + struct ChanUser *chanuser; +}; + +static USERAUTH_CALLBACK(neonkick_event_join_nick_lookup); +static void neonkick_event_join_async1(struct ClientSocket *client, struct ChanUser *chanuser); + +static void neonkick_event_join(struct ChanUser *chanuser) { + struct UserNode *user = chanuser->user; + struct ClientSocket *client = getChannelBot(chanuser->chan, BOTID); + if(!client) return; //we can't "see" this event + if(chanuser->user == client->user) { + requestOp(client->user, chanuser->chan); + module_neonbackup_recover_chan(chanuser->chan); + return; + } + if(chanuser->user->flags & USERFLAG_ISBOT) return; + loadChannelSettings(chanuser->chan); + if(!(chanuser->chan->flags & CHANFLAG_CHAN_REGISTERED)) return; + if(!(user->flags & USERFLAG_ISAUTHED)) { + struct neonkick_event_join_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonkick", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->chanuser = chanuser; + get_userauth(user, module_id, neonkick_event_join_nick_lookup, cache); + } else + neonkick_event_join_async1(client, chanuser); +} + +static USERAUTH_CALLBACK(neonkick_event_join_nick_lookup) { + struct neonkick_event_join_cache *cache = data; + if(user) + neonkick_event_join_async1(cache->client, cache->chanuser); + free(cache); +} + +static void neonkick_event_join_async1(struct ClientSocket *client, struct ChanUser *chanuser) { + struct ChanNode *chan = chanuser->chan; + struct UserNode *user = chanuser->user; + MYSQL_RES *res; + MYSQL_ROW chanuserrow; + + if((user->flags & USERFLAG_ISAUTHED)) { + printf_mysql_query("SELECT `chanuser_access`, `chanuser_flags`, `chanuser_infoline`, `chanuser_seen`, `chanuser_id` FROM `chanusers` LEFT JOIN `users` ON `chanuser_uid` = `user_id` WHERE `chanuser_cid` = '%d' AND `user_user` = '%s'", chan->channel_id, escape_string(user->auth)); + res = mysql_use(); + chanuserrow = mysql_fetch_row(res); + } else + chanuserrow = NULL; + int userflags = (chanuserrow ? atoi(chanuserrow[1]) : 0); + int uaccess = ((chanuserrow && !(userflags & DB_CHANUSER_SUSPENDED)) ? atoi(chanuserrow[0]) : 0); + if(uaccess > 0) + return; //no kick :D + char *tmp; + int global_kick_count = ((tmp = getSetting(NULL, chan, "kicks")) ? atoi(tmp) : 0); + int kick_count = ((tmp = getSetting(user, chan, "kicks")) ? atoi(tmp) : 0); + global_kick_count++; + kick_count++; + char buf[MAXLEN]; + char reason[MAXLEN]; + sprintf(buf, "%d", global_kick_count); + setSetting(NULL, chan, "kicks", buf); + sprintf(buf, "%d", kick_count); + setSetting(user, chan, "kicks", buf); + char *banmask = generate_banmask(chanuser->user, buf); + sprintf(reason, "Kick #%d (#%d for you)", global_kick_count, kick_count); + putsock(client, "KICK %s %s :%s", chanuser->chan->name, chanuser->user->nick, reason); + putsock(client, "MODE %s +b %s", chanuser->chan->name, banmask); + { // add timed ban + printf_mysql_query("INSERT INTO `bans` (`ban_channel`, `ban_mask`, `ban_triggered`, `ban_timeout`, `ban_owner`, `ban_reason`) VALUES ('%d', '%s', UNIX_TIMESTAMP(), '%lu', '%d', '%s')", chanuser->chan->channel_id, escape_string(banmask), (unsigned long) (time(0) + 10), 0, escape_string(reason)); + int banid = (int) mysql_insert_id(get_mysql_conn()); + char nameBuf[MAXLEN]; + char banidBuf[20]; + sprintf(nameBuf, "ban_%d", banid); + sprintf(banidBuf, "%d", banid); + timeq_add_name(nameBuf, 10, module_id, channel_ban_timeout, strdup(banidBuf)); + } +} diff --git a/src/modules/NeonKick.mod/module.c b/src/modules/NeonKick.mod/module.c new file mode 100644 index 0000000..e372bf9 --- /dev/null +++ b/src/modules/NeonKick.mod/module.c @@ -0,0 +1,32 @@ +/* module.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#include "../module.h" +#include "bot_NeonKick.h" + +static int module_initialize() { + return 0; +} + +static void module_start(int type) { + init_NeonKick(type); +} + +static void module_stop(int type) { + free_NeonKick(type); +} + +MODULE_HEADER(module_initialize, module_start, module_stop); diff --git a/src/modules/NeonServ.mod/bot_NeonServ.c b/src/modules/NeonServ.mod/bot_NeonServ.c new file mode 100644 index 0000000..0ea4cbb --- /dev/null +++ b/src/modules/NeonServ.mod/bot_NeonServ.c @@ -0,0 +1,544 @@ +/* bot_NeonServ.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#include "../module.h" +#include "../botid.h" + +#include "bot_NeonServ.h" +#include "../../modcmd.h" +#include "../../IRCEvents.h" +#include "../../IRCParser.h" +#include "../../UserNode.h" +#include "../../ChanNode.h" +#include "../../ChanUser.h" +#include "../../BanNode.h" +#include "../../ModeNode.h" +#include "../../ClientSocket.h" +#include "../../mysqlConn.h" +#include "../../lang.h" +#include "../../HandleInfoHandler.h" +#include "../../WHOHandler.h" +#include "../../DBHelper.h" +#include "../../tools.h" +#include "../../timeq.h" +#include "../../EventLogger.h" +#include "cmd_neonserv.h" +#include "../../ConfigParser.h" + +#define BOTID NEONSERV_BOTID +#define BOTALIAS "NeonServ" + +static const struct default_language_entry msgtab[] = { + {"NS_USER_UNKNOWN", "User with nick $b%s$b does not exist."}, /* {ARGS: "TestUser"} */ + {"NS_AUTH_UNKNOWN", "Account $b%s$b has not been registered."}, /* {ARGS: "TestAuth"} */ + {"NS_USER_NEED_AUTH", "%s must first authenticate with $bAuthServ$b."}, /* {ARGS: "TestUser"} */ + {"NS_YOU_NEED_AUTH", "You must first authenticate with $bAuthServ$b."}, + {"NS_INVALID_ACCESS", "$b%d$b is an invalid access level."}, /* {ARGS: 1337} */ + {"NS_ADDUSER_ALREADY_ADDED", "%s is already on the $b%s$b user list (with access %d)."}, /* {ARGS: "TestUser", "#TestChan", 123} */ + {"NS_ADDUSER_DONE", "Added %s to the %s user list with access %d."}, /* {ARGS: "TestUser", "#TestChan", 123} */ + {"NS_NOT_ON_USERLIST", "%s lacks access to $b%s$b."}, /* {ARGS: "TestUser", "#TestChan"} */ + {"NS_NOT_ON_USERLIST_YOU", "You lack access to $b%s$b."}, /* {ARGS: "#TestChan"} */ + {"NS_NOT_ON_CHANNEL", "%s isn't currently in $b%s$b."}, /* {ARGS: "TestUser", "#TestChan"} */ + {"NS_NOT_ON_CHANNEL_YOU", "You aren't currently in $b%s$b."}, /* {ARGS: "#TestChan"} */ + {"NS_DELUSER_DONE", "Deleted %s (with access %d) from the %s user list."}, /* {ARGS: "TestUser", 123, "#TestChan"} */ + {"NS_ACCESS_OUTRANKED", "You cannot give users access greater than or equal to your own."}, + {"NS_USER_OUTRANKED", "$b%s$b outranks you (command has no effect)."}, /* {ARGS: "TestUser"} */ + {"NS_ACCESS_DENIED", "Access denied."}, + {"NS_NO_ACCESS", "You lack sufficient access to use this command."}, + {"NS_USER_PROTECTED", "Sorry, $b%s$b is protected."}, /* {ARGS: "TestUser"} */ + {"NS_SERVICE_IMMUNE", "$b%s$b may not be kicked, killed, banned, or deopped."}, /* {ARGS: "TestUser"} */ + {"NS_YOU_PROTECTED", "You may not kick or ban yourself."}, + {"NS_TABLE_NONE", " None"}, + {"NS_TABLE_COUNT", "Found $b%d$b matches."}, /* {ARGS: 5} */ + {"NS_BAN_ALREADY_ADDED", "$b%s$b is already banned in %s."}, /* {ARGS: "*!*@moeeep.*", "#TestChan"} */ + {"NS_INVALID_ACCESS_RANGE", "Invalid access range; minimum (%d) must be lower than maximum (%d)."}, /* {ARGS: 450, 400} */ + {"NS_CLVL_DONE", "%s now has access $b%d$b in %s."}, /* {ARGS: "TestUser", 123, "#TestChan"} */ + {"NS_A_LACKS_ACCESS_BUT_GOD_NICK", "%s lacks access to %s but has $bsecurity override$b enabled."}, /* {ARGS: "TestAuth", "#TestChan"} */ + {"NS_A_LACKS_ACCESS_BUT_GOD_AUTH", "%s (%s) lacks access to %s but has $bsecurity override$b enabled."}, /* {ARGS: "TestAuth", "TestUser", "#TestChan"} */ + {"NS_A_ACCESS_NICK", "%s has access $b%d$b in %s."}, /* {ARGS: "TestAuth", 123, "#TestChan"} */ + {"NS_A_ACCESS_AUTH", "%s (%s) has access $b%d$b in %s."}, /* {ARGS: "TestAuth", "TestUser", 123, "#TestChan"} */ + {"NS_A_ACCESS_NICK_GOD", "%s has access $b%d$b in %s and has $bsecurity override$b enabled."}, /* {ARGS: "TestAuth", 123, "#TestChan"} */ + {"NS_A_ACCESS_AUTH_GOD", "%s (%s) has access $b%d$b in %s and has $bsecurity override$b enabled."}, /* {ARGS: "TestAuth", "TestUser", 123, "#TestChan"} */ + {"NS_A_SUSPENDED", "$b%s$b's access to %s has been suspended."}, /* {ARGS: "TestAuth", "#TestChan"} */ + {"NS_A_IS_IRCOP", "%s is an $bIRC operator$b."}, /* {ARGS: "TestUser", "#TestChan"} */ + {"NS_USERS_HEADER", "%s users from level %d to %d:"}, /* {ARGS: "#TestChan", 1, 500} */ + {"NS_USERS_HEADER_MATCH", "%s users from level %d to %d matching %s:"}, /* {ARGS: "#TestChan", 1, 500, "Test*"} */ + {"NS_USERS_HEADER_ACCESS", "Access"}, + {"NS_USERS_HEADER_ACCOUNT", "Account"}, + {"NS_USERS_HEADER_SEEN", "Last Seen"}, + {"NS_USERS_HEADER_STATE", "Status"}, + {"NS_USERS_COUNT", "There are $b%d$b users in %s."}, /* {ARGS: 20, "#TestChan"} */ + {"NS_USERS_COUNT_1", "There is $b%d$b user in %s."}, /* {ARGS: 1, "#TestChan"} */ + {"NS_USERS_COUNT_MATCH", "There are $b%d$b users in %s. ($b%d$b matching your request)"}, /* {ARGS: 20, "#TestChan", 5} */ + {"NS_USERS_COUNT_MATCH_1", "There is $b%d$b user in %s. ($b%d$b matching your request)"}, /* {ARGS: 1, "#TestChan", 1} */ + {"NS_USERS_SEEN_HERE", "Here"}, + {"NS_USERS_SEEN_INVISIBLE", "Here (invisible)"}, + {"NS_USERS_SEEN_NEVER", "Never"}, + {"NS_USERS_STATE_SUSPENDED", "Suspended"}, + {"NS_USERS_STATE_NORMAL", "Normal"}, + {"NS_SUSPEND_ALREADY", "$b%s$b is already suspended." }, /* {ARGS: "TestUser"} */ + {"NS_SUSPEND_NOT", "$b%s$b is not suspended." }, /* {ARGS: "TestUser"} */ + {"NS_SUSPEND_DONE", "$b%s$b's access to $b%s$b has been suspended." }, /* {ARGS: "TestUser", "#TestChan"} */ + {"NS_SUSPEND_RESTORED", "$b%s$b's access to $b%s$b has been restored." }, /* {ARGS: "TestUser", "#TestChan"} */ + {"NS_DELME_OWNER", "You cannot delete your owner access in $b%s$b."}, /* {ARGS: "#TestChan"} */ + {"NS_DELME_KEY", "To really remove yourself, you must use 'deleteme %s'."}, /* {ARGS: "abc123"} */ + {"NS_DELME_DONE", "Your $b%d$b access has been deleted from $b%s$b."}, /* {ARGS: 123, "#TestChan"} */ + {"NS_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $bmyaccess$b with no arguments)."}, + {"NS_MYACCESS_HEADER", "Showing all channel entries for account $b%s$b:"}, /* {ARGS: "TestAuth"} */ + {"NS_MYACCESS_HEADER_MATCH", "Showing all channel entries for account $b%s$b matching %s:"}, /* {ARGS: "TestAuth", "#Test*"} */ + {"NS_MYACCESS_HEADER_NAME", "Name"}, + {"NS_MYACCESS_HEADER_ACCESS", "Access"}, + {"NS_MYACCESS_HEADER_FLAGS", "Rights"}, + {"NS_MYACCESS_HEADER_INFO", "Info"}, + {"NS_MYACCESS_COUNT", "%s has access in $b%d$b channel(s) and is owner of $b%d$b channel(s)."}, /* {ARGS: "TestUser", 15, 5} */ + {"NS_MYACCESS_COUNT_MATCH", "%s has access in $b%d$b channel(s) and is owner of $b%d$b channel(s) ($b%d$b channels matching your request)."}, /* {ARGS: "TestUser", 15, 5, 7} */ + {"NS_UP_ALREADY_OP", "You are already opped in $b%s$b."}, /* {ARGS: "#TestChan"} */ + {"NS_UP_ALREADY_VOICE", "You are already voiced in $b%s$b."}, /* {ARGS: "#TestChan"} */ + {"NS_DOWN_ALREADY", "You are not opped or voiced in $b%s$b."}, /* {ARGS: "#TestChan"} */ + {"NS_MDELUSER_DONE", "Deleted $b%d$b account(s) matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list."}, /* {ARGS: 10, "Test*", 1, 200, "#TestChan"} */ + {"NS_TRIM_DURATION_TOO_SHORT", "You must include a minimum inactivity duration of at least %d seconds to trim."}, + {"NS_TRIM_DONE", "Trimmed $b%d users$b with access from %d to %d from the %s user list who were inactive for at least %s."}, /* {ARGS: 10, 1, 100, "#TestChan", "10 days"} */ + {"NS_TRIM_BAN_DONE", "Trimmed $b%d bans$b from the %s ban list who were banned for at least %s."}, /* {ARGS: 5, "#TestChan", "1 day"} */ + {"NS_GIVEOWNER_SELF", "You cannot give ownership to your own account."}, + {"NS_GIVEOWNER_TIMEOUT", "You must wait %s before you can give ownership of $b%s$b to someone else."}, /* {ARGS: "5 hours", "#TestChan"} */ + {"NS_GIVEOWNER_CONFIRM", "To really give ownership to $b%1$s$b, you must use 'giveownership *%1$s %2$s'."}, /* {ARGS: "TestUser", "abc123"} */ + {"NS_GIVEOWNER_DONE", "Ownership of $b%s$b has been transferred to account $b%s$b."}, /* {ARGS: "#TestChan", "TestUser"} */ + {"NS_OP_FAIL", "$b%s$b could not op some of the nicks you provided."}, /* {ARGS: "NeonServ"} */ + {"NS_OP_DONE", "Opped users in $b%s$b."}, /* {ARGS: "#TestChan"} */ + {"NS_HALFOP_FAIL", "$b%s$b could not halfop some of the nicks you provided."}, /* {ARGS: "NeonServ"} */ + {"NS_HALFOP_DONE", "Half-Opped users in $b%s$b."}, /* {ARGS: "#TestChan"} */ + {"NS_VOICE_FAIL", "$b%s$b could not voice some of the nicks you provided."}, /* {ARGS: "NeonServ"} */ + {"NS_VOICE_DONE", "Voiced users in $b%s$b."}, /* {ARGS: "#TestChan"} */ + {"NS_DEOP_FAIL", "$b%s$b could not deop some of the nicks you provided."}, /* {ARGS: "NeonServ"} */ + {"NS_DEOP_DONE", "Deopped users in $b%s$b."}, /* {ARGS: "#TestChan"} */ + {"NS_DEHALFOP_FAIL", "$b%s$b could not dehalfop some of the nicks you provided."}, /* {ARGS: "NeonServ"} */ + {"NS_DEHALFOP_DONE", "Dehalfopped users in $b%s$b."}, /* {ARGS: "#TestChan"} */ + {"NS_DEVOICE_FAIL", "$b%s$b could not devoice some of the nicks you provided."}, /* {ARGS: "NeonServ"} */ + {"NS_DEVOICE_DONE", "Devoiced users in $b%s$b."}, /* {ARGS: "#TestChan"} */ + {"NS_OPALL_SECURITY", "$bWARNING$b: Opping all users on a channel is very insecure! If you still want do op all users on %s use: '$bopall FORCE$b [nick mask]'"}, + {"NS_OPALL_DONE", "Opped $b%d$b users in %s."}, /* {ARGS: 20, "#TestChan"} */ + {"NS_HALFOPALL_DONE", "Half-Opped $b%d$b users in %s."}, /* {ARGS: 20, "#TestChan"} */ + {"NS_VOICEALL_DONE", "Voiced $b%d$b users in %s."}, /* {ARGS: 20, "#TestChan"} */ + {"NS_DEOPALL_DONE", "Deopped $b%d$b users in %s."}, /* {ARGS: 20, "#TestChan"} */ + {"NS_DEHALFOPALL_DONE", "Dehalfopped $b%d$b users in %s."}, /* {ARGS: 20, "#TestChan"} */ + {"NS_DEVOICEALL_DONE", "Devoiced $b%d$b users in %s."}, /* {ARGS: 20, "#TestChan"} */ + {"NS_KICK_DONE", "Kicked $b%d$b users from %s"}, /* {ARGS: 20, "#TestChan"} */ + {"NS_KICK_FAIL", "$b%s$b could not kick some of the nicks you provided."}, /* {ARGS: "NeonServ"} */ + {"NS_KICKBAN_DONE", "KickBanned $b%d$b users from %s"}, /* {ARGS: 10, "#TestChan"} */ + {"NS_KICKBAN_FAIL", "$b%s$b could not kickban some of the nicks you provided."}, /* {ARGS: "NeonServ"} */ + {"NS_BAN_DONE", "$b%d$b masks added to the %s ban list. (matching %d users)"}, /* {ARGS: 5, "#TestChan", 15} */ + {"NS_BAN_FAIL", "$b%s$b could not ban some of the nicks you provided."}, /* {ARGS: "NeonServ"} */ + {"NS_LAME_MASK", "$b%s$b is a little too general. Try making it more specific."}, /* {ARGS: "*!*@*"} */ + {"NS_LAME_MASK_WARNING", "$k4WARNING$k: $b%s$b is very general. (matches %d users)"}, + {"NS_SET_HEADER", "Channel Settings for %s:"}, /* {ARGS: "#TestChan"} */ + {"NS_SET_ON", "on"}, + {"NS_SET_OFF", "off"}, + {"NS_SET_UNKNOWN_SETTING", "$b%s$b is an unknown channel setting."}, /* {ARGS: "TestSetting"} */ + {"NS_SET_CANNOT_SET", "That setting is above your current level, so you cannot change it."}, + {"NS_SET_BADLEVEL", "You cannot change any setting to above your level."}, + {"NS_SET_INVALID_OPTION", "$b%d$b is not a valid choice. Choose one:"}, /* {ARGS: 5} */ + {"NS_SET_INVALID_BOOLEAN", "$b%s$b is an invalid binary value."}, /* {ARGS: "3"} */ + {"NS_SET_DEFAULTS_OWNER", "You must have access 500 in %s to reset it to the default options."}, /* {ARGS: "#TestChan"} */ + {"NS_SET_DEFAULTS_CODE", "To reset %s's settings to the defaults, you must use 'set defaults %s'."}, /* {ARGS: "#TestChan", "abc123"} */ + {"NS_SET_DEFAULTS_DONE", "All settings for %s have been reset to default values."}, /* {ARGS: "#TestChan"} */ + {"NS_SET_TRIGGER_OWNER", "You must have access 500 in %s to change the channel trigger."}, /* {ARGS: "#TestChan"} */ + {"NS_SET_HELP_USERINFO","(access to set the userinfo)"}, + {"NS_SET_HELP_WIPEINFO","(access to clear the userinfo of other users)"}, + {"NS_SET_HELP_INVITEME","(access to get invited by the bot)"}, + {"NS_SET_HELP_ENFVOICE","(access to give voice to other users)"}, + {"NS_SET_HELP_ENFOPS","(access to give op to their users)"}, + {"NS_SET_HELP_GIVEOPS","(access to get op by the bot)"}, + {"NS_SET_HELP_GIVEVOICE","(access to get voice by the bot)"}, + {"NS_SET_HELP_KICK","(access to kick other users from the channel)"}, + {"NS_SET_HELP_BAN","(access to ban other users from the channel)"}, + {"NS_SET_HELP_STATICBAN","(access to add static bans to the channel banlist e.g. +addban)"}, + {"NS_SET_HELP_PUBCMD","(access to do public commands in the channel e.g. +users)"}, + {"NS_SET_HELP_ENFMODES","(access to override the modelock)"}, + {"NS_SET_HELP_ENFTOPIC","(access to override the topicmask)"}, + {"NS_SET_HELP_TOPICSNARF","(access to set the default topic by changing the topic with /TOPIC)"}, + {"NS_SET_HELP_CHANGETOPIC","(access to change the topic)"}, + {"NS_SET_HELP_SETTERS","(access to change this settings)"}, + {"NS_SET_HELP_ADDUSER","(access to add an user to the userlist)"}, + {"NS_SET_HELP_DELUSER","(access to delete an user from the userlist)"}, + {"NS_SET_HELP_CLVL","(access to change the access of an user in the userlist)"}, + {"NS_SET_HELP_RESYNC","(access to synchronize the channelrights (@,+) with the userlist)"}, + {"NS_SET_HELP_SUSPEND","(access to suspend an user on the userlist)"}, + {"NS_SET_OPTION_CTCPREACTION_0","Kick on disallowed CTCPs"}, + {"NS_SET_OPTION_CTCPREACTION_1","Kickban on disallowed CTCPs"}, + {"NS_SET_OPTION_CTCPREACTION_2","Short timed ban on disallowed CTCPs"}, + {"NS_SET_OPTION_CTCPREACTION_3","Long timed ban on disallowed CTCPs"}, + {"NS_SET_OPTION_NOTICEREACTION_0","Kick on disallowed NOTICEs"}, + {"NS_SET_OPTION_NOTICEREACTION_1","Kickban on disallowed NOTICEs"}, + {"NS_SET_OPTION_NOTICEREACTION_2","Short timed ban on disallowed NOTICEs"}, + {"NS_SET_OPTION_NOTICEREACTION_3","Long timed ban on disallowed NOTICEs"}, + {"NS_SET_OPTION_PROTECT_0","All users will be protected from users with equal or lower access."}, + {"NS_SET_OPTION_PROTECT_1","All users with access will be protected from users with equal or lower access."}, + {"NS_SET_OPTION_PROTECT_2","All users with access will be protected from user with lower access."}, + {"NS_SET_OPTION_PROTECT_3","Nobody will be protected."}, + {"NS_SET_OPTION_TOYS_0","Funcommands can't be used."}, + {"NS_SET_OPTION_TOYS_1","Funcommands are possible but the reply will be sent as a notice."}, + {"NS_SET_OPTION_TOYS_2","Funcommands are possible and the reply will be sent to the channel."}, + {"NS_SET_OPTION_DYNLIMIT_0","off"}, + {"NS_SET_OPTION_NODELETE_0","off (only bot masters)"}, + {"NS_SET_OPTION_NODELETE_1","on (only bot masters)"}, + {"NS_WIPEINFO_DONE", "Removed $b%s$b's infoline in $b%s$b."}, /* {ARGS: "TestUser", "#TestChan"} */ + {"NS_TRACE_HEADER", "The following users were found:"}, + {"NS_ADDBAN_DONE", "$b%s$b permantly added to the %s ban list. (matching %d users)"}, /* {ARGS: "*!*@Test.*", "#TestChan", 4} */ + {"NS_BANS_HEADER_MASK", "Mask"}, + {"NS_BANS_HEADER_SETBY", "Set By"}, + {"NS_BANS_HEADER_TRIGGERED", "Triggered"}, + {"NS_BANS_HEADER_EXPIRES", "Expires"}, + {"NS_BANS_HEADER_REASON", "Reason"}, + {"NS_DELBAN_BANNED_BY", "%s is banned by %s."}, /* {ARGS: "*!*@bla*", "*!*@b*"} */ + {"NS_DELBAN_FAIL", "Sorry, no ban found for $b%s$b."}, /* {ARGS: "*!*@bla*"} */ + {"NS_DELBAN_DONE", "Removed $b%s$b from the %s ban list."}, /* {ARGS: "*!*@bla.*", "#TestChan"} */ + {"NS_NETINFO_HEADER", "$bNetwork information$b"}, + {"NS_NETINFO_BOTS", "Bots:"}, + {"NS_NETINFO_UPTIME", "Uptime:"}, + {"NS_NETINFO_TRAFFIC", "Traffic:"}, + {"NS_NETINFO_CACHE", "Cache:"}, + {"NS_NETINFO_DATABASE", "Database:"}, + {"NS_NETINFO_CHANNEL", " Channel:"}, + {"NS_NETINFO_CHANNEL_BAN", " Bans:"}, + {"NS_NETINFO_USER", " User:"}, + {"NS_NETINFO_CHANUSER", " Channel-User:"}, + {"NS_NETINFO_OTHER", " Other:"}, + {"NS_NETINFO_THREADS", "Threads:"}, + {"NS_NETINFO_VERSION", "Version:"}, + {"NS_NETINFO_CODE", "Code:"}, + {"NS_NETINFO_CODE_VALUE", "%s lines c code (view it at http://dev.pk910.de/NeonServ)"}, /* {ARGS: 20} */ + {"NS_NETINFO_COMPILER", "Compiler:"}, + {"NS_NETINFO_COMPILER_VALUE", "%s (%s)"}, /* {ARGS: "GCC 4.4.5", "Sun Sep 18 2011 at 05:21:33 CEST"} */ + {"NS_EXTTOPIC_INVALID_ID", "ADVANCEDTOPIC is enabled and $b%s$b is an invalid TOPIC ID. Valid topic id's are: 1-9"}, /* {ARGS: 10} */ + {"NS_EXTTOPIC_TOPICID", "Topic %d: %s"}, /* {ARGS: 5, "topic"} */ + {"NS_TOPIC_DONE", "Topic is now '%s'."}, /* {ARGS: "i like you :D"} */ + {"NS_CHANSERVSYNC_UNSUPPORTED", "\0034WARNING\003: the user list style of %s is not known. %s can try to synchronize the userlist, but there is no guarantee that it is successful!"}, /* {ARGS: "CowBot"} */ + {"NS_CHANSERVSYNC_KEY", "If you really want to synchronize the %s userlist with %s use: chanservsync %s %s"}, /* {ARGS: "#TestChan", "CowBot", "CowBot", "abc123"} */ + {"NS_CHANSERVSYNC_INUSE", "$bchanservsync$b is already in use by someone else. Please try again in a few seconds..."}, + {"NS_CHANSERVSYNC_SYNCHRONIZING", "Synchronizing userlist in %s with $b%s$b..."}, /* {ARGS: "#TestChan", "CowBot"} */ + {"NS_CHANSERVSYNC_SYNCHRONIZED", "Synchronized user $b%s$b: access $b%d$b"}, /* {ARGS: "TestUser", 123} */ + {"NS_REGISTER_ALREADY", "%s is already registered with %s."}, /* {ARGS: "#TestChan", "NeonServ"} */ + {"NS_INVALID_CHANNEL_NAME", "%s is not a valid channel name."}, /* {ARGS: "#invalid"} */ + {"NS_REGISTER_FULL", "the bot can not join more channels."}, + {"NS_REGISTER_DISCONNECTED", "%s has been registered with a Bot, that is currently NOT connected. The Bot should join the channel, when it reconnects to the IRC-Network."}, /* {ARGS: "#TestChan"} */ + {"NS_REGISTER_DONE", "$b%s$b is now registered to $b%s$b."}, /* {ARGS: "#TestChan", "TestUser"} */ + {"NS_REGISTER_DONE_NOAUTH", "$b%s$b is now registered."}, /* {ARGS: "#TestChan"} */ + {"NS_UNREGISTER_NOT_REGISTERED", "$b%s$b is not registered with %s."}, /* {ARGS: "#TestChan", "NeonServ"} */ + {"NS_UNREGISTER_NODELETE", "$b%s$b is protected from being unregistered (nodelete)."}, /* {ARGS: "#TestChan"} */ + {"NS_UNREGISTER_DONE", "$b%s$b unregistered."}, /* {ARGS: "#TestChan"} */ + {"NS_RECOVER_DONE", "$b%s$b has been recovered."}, /* {ARGS: "#TestChan"} */ + {"NS_RESYNC_DONE", "Synchronized users in $b%s$b with the userlist."}, /* {ARGS: "#TestChan"} */ + {"NS_TIMEBAN_DURATION_TOO_SHORT", "You must specify a ban duration of at least %d seconds."}, /* {ARGS: 30} */ + {"NS_TIMEBAN_DONE", "Banned $b%s$b from %s for %s. (matching %d users)"}, /* {ARGS: "*!*@bla*", "#TestChan", "2 hours", 5} */ + {"NS_MODE_INVALID", "$b%s$b is an invalid set of channel modes."}, /* {ARGS: "+xyz"} */ + {"NS_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s."}, /* {ARGS: "+xyz", "#TestChan"} */ + {"NS_MODE_DONE", "Channel modes are now $b%s$b."}, /* {ARGS: "+xyz"} */ + {"NS_MODE_ENFOPS", "You may not op or deop users on $b%s$b."}, /* {ARGS: "#TestChan"} */ + {"NS_MODE_ENFVOICE", "You may not voice or devoice users on $b%s$b."}, /* {ARGS: "#TestChan"} */ + {"NS_MODE_CANBAN", "You may not ban or unban users on $b%s$b."}, /* {ARGS: "#TestChan"} */ + {"NS_GOD_ON", "Security override has been enabled."}, + {"NS_GOD_OFF", "Security override has been disabled."}, + {"NS_PEEK_HEADER", "$b%s$b Status:"}, /* {ARGS: "#TestChan"} */ + {"NS_PEEK_TOPIC", "Topic: %s"}, /* {ARGS: "TOPIC"} */ + {"NS_PEEK_MODES", "Modes: %s"}, /* {ARGS: "+xyz"} */ + {"NS_PEEK_USERS", "Total Users: %d (%d ops, %d voices, %d regulars, %d invisible)"}, /* {ARGS: 20, 4, 6, 8, 2} */ + {"NS_PEEK_USERS_HALFOP", "Total Users: %d (%d ops, %d halfops, %d voices, %d regulars, %d invisible)"}, /* {ARGS: 25, 5, 4, 6, 8, 2} */ + {"NS_PEEK_OPS", "Ops:"}, + {"NS_USET_GLOBAL", "$b--- Global ---$b"}, + {"NS_USET_CHANNEL", "$b--- User options (channel) ---$b"}, + {"NS_USET_NO_ACCESS", "no access"}, + {"NS_USET_UNKNOWN_SETTING", "$b%s$b is an unknown uset setting."}, /* {ARGS: "TestSetting"} */ + {"NS_RELOADLANG_UNKNOWN", "$b%s$b is an unknown language tag."}, /* {ARGS: "de"} */ + {"NS_RELOADLANG_DONE", "$b%s$b (%s) reloaded."}, /* {ARGS: "Deutsch", "de"} */ + {"NS_UNBAN_DONE", "$b%d$b masks removed from the %s ban list."}, /* {ARGS: 5, "#TestChan"} */ + {"NS_UNBAN_FAIL", "$b%s$b could not unban some of the masks you provided."}, /* {ARGS: "NeonServ"} */ + {"NS_UNBANALL_DONE", "all $b%d$b masks removed from the %s ban list."}, /* {ARGS: 5, "#TestChan"} */ + {"NS_UNBANALL_FAIL", "$b%s$b could not find any bans in %s."}, /* {ARGS: "NeonServ", "#TestChan"} */ + {"NS_UNBANME_DONE", "removed $b%d$b masks from the %s ban list."}, /* {ARGS: 5, "#TestChan"} */ + {"NS_UNBANME_FAIL", "$b%s$b could not find any bans matching %s."}, /* {ARGS: "NeonServ", "TestUser!TestIdent@TestUser.user.WebGamesNet"} */ + {"NS_INVITE_GLOBALLY_BLOCKED", "$b%s$b doesn't want to be invited at all."}, /* {ARGS: "TestUser"} */ + {"NS_INVITE_RESTRICTION", "%s doesn't want to be invited to %s."}, /* {ARGS: "TestUser", "#TestChan"} */ + {"NS_INVITE_TIMEOUT", "%s has already been invited to $b%s$b."}, /* {ARGS: "TestUser", "#TestChan"} */ + {"NS_INVITE_ON_CHAN", "%s is already in $b%s$b."}, /* {ARGS: "TestUser", "#TestChan"} */ + {"NS_INVITE_DONE_USER", "You have been invited to join $b%s$b by %s. (Do $b/msg %s %1$s uset noinvite 1$b if you don't want to be invited to %1$s anymore.)"}, /* {ARGS: "#TestChan", "TestUser", "NeonServ"} */ + {"NS_INVITE_DONE", "Invited $b%s$b to join %s."}, /* {ARGS: "TestUser", "#TestChan"} */ + {"NS_INVITEME_ON_CHAN", "You are already in $b%s$b."}, /* {ARGS: "#TestChan"} */ + {"NS_INVITEME_DONE", "You have been invited to join %s."}, /* {ARGS: "#TestChan"} */ + {"NS_HELP_TOPIC", "No help on that topic."}, + {"NS_CSUSPEND_ALREADY", "$b%s$b is already suspended."}, /* {ARGS: "#TestChan"} */ + {"NS_CSUSPEND_DONE", "Channel $b%s$b has been temporarily suspended."}, /* {ARGS: "#TestChan"} */ + {"NS_CUNSUSPEND_NOT", "$b%s$b is not suspended."}, /* {ARGS: "#TestChan"} */ + {"NS_CUNSUSPEND_DONE", "Channel $b%s$b has been restored."}, /* {ARGS: "#TestChan"} */ + {"NS_MOVE_SUSPENDED", "Moving cannot be performed if the source channel is suspended."}, + {"NS_MOVE_SELF", "Moving cannot be performed if the source and target channels are the same."}, + {"NS_MOVE_DONE", "Channel $b%s$b has been moved to $b%s$b."}, /* {ARGS: "#TestChan", "#NewTestChan"} */ + {"NS_BIND_ALREADY", "$b%s$b is already bound to %s."}, /* {ARGS: "TestCommand", "TestFunction"} */ + {"NS_BIND_UNKNOWN", "$b%s$b is an undefined function."}, /* {ARGS: "TestFunction"} */ + {"NS_BIND_DONE", "New command $b%s$b bound to %s."}, /* {ARGS: "TestCommand", "TestFunction"} */ + {"NS_UNBIND_NOT_FOUND", "There is no command called $b%s$b bound."}, /* {ARGS: "TestCommand"} */ + {"NS_UNBIND_DONE", "Unbound command $b%s$b."}, /* {ARGS: "TestCommand"} */ + {"NS_EVENTS_HEADER", "The following channel events were found:"}, + {"NS_OPLOG_HEADER", "The following oper events were found:"}, + {"NS_SEARCH_ZOMBIE_SCAN_IN_PROGRESS", "Another Zombie Scan is already in progress."}, + {"NS_SEARCH_HEADER", "The following channels were found:"}, + {"NS_COMMAND_BINDING", "$b%s$b is a binding of %s %s"}, /* {ARGS: "TestCommand", "TestFunction", "TestParameters"} */ + {"NS_COMMAND_ACCESS", "You need at least %d channel access and %d oper access to execute this command."}, /* {ARGS: 500, 100} */ + {"NS_TOPIC_ACCESS", "You lack sufficient access in %s to change the topic."}, /* {ARGS: "#TestChan"} */ + {"NS_BOTWAR_DETECTED", "$b$k4BOTWAR DETECTED!$k Please check the channel configuration!$b"}, + {"NS_BOTWAR_REPORTED", "A supporter has been informed to help you preventing botwars in the future."}, + {"NS_BOTWAR_ALERT", "$b$k4BOTWAR ALERT:$k$b Botwar in $b%s$b detected. (opponent: $b%s$b) Please join and help them preventing Botwars."}, /* {ARGS: "#TestChan", "OtherBot"} */ + {"NS_INVITE_FAIL", "$b%s$b is not registered with %s or suspended."}, /* {ARGS: "#TestChan", "NeonServ"} */ + {"NS_SETACCESS_DONE", "$b%s$b has now %d global access."}, /* {ARGS: "TestUser", 1000} */ + {"NS_ADDRANK_EXISTS", "Another support ranking called '$b%s$b' already exists."}, /* {ARGS: "Supporter"} */ + {"NS_ADDRANK_DONE", "Support ranking '$b%s$b' created."}, /* {ARGS: "Supporter"} */ + {"NS_DELRANK_NOT_FOUND", "There is no support ranking called '$b%s$b'."}, /* {ARGS: "Supporter"} */ + {"NS_DELRANK_DELETED", "Support ranking called '$b%s$b' removed."}, /* {ARGS: "Supporter"} */ + {"NS_LISTRANK_ID", "Id"}, + {"NS_LISTRANK_NAME", "Name"}, + {"NS_LISTRANK_ASSIGNED", "Assigned to"}, + {"NS_LISTRANK_UNRANKED", "There are also %d unranked users with global access."}, /* {ARGS: 10} */ + {"NS_SETRANK_NOT_FOUND", "There is no support ranking with ID '$b%s$b'."}, /* {ARGS: 42} */ + {"NS_SETRANK_HEAD", "Support ranking settings for Id %s:"}, /* {ARGS: 42} */ + {"NS_SETRANK_UNKNOWN_SETTING", "$b%s$b is an unknown support rank setting."}, /* {ARGS: "moep"} */ + {"NS_SETRANK_ORDER_INVALID", "%d is an invalid numeric value. (valid: 1-99)"}, /* {ARGS: 100} */ + {"NS_ASSIGNRANK_DONE", "$b%s$b is now ranked as '$b%s$b'."}, /* {ARGS: "TestUser", "Supporter"} */ + {"NS_INFO_HEADER", "$b%s$b Information:"}, /* {ARGS: "#TestChan"} */ + {"NS_INFO_DEFAULTTOPIC", "Default Topic:"}, + {"NS_INFO_MODELOCK", "Mode Lock:"}, + {"NS_INFO_RECORD", "Record Visitors:"}, + {"NS_INFO_OWNER", "Owner:"}, + {"NS_INFO_USERS", "Total User Count:"}, + {"NS_INFO_BANS", "Ban Count:"}, + {"NS_INFO_VISITED", "Visited:"}, + {"NS_INFO_REGISTERED", "Registered:"}, + {"NS_INFO_REGISTRAR", "Registered by:"}, + {"NS_INFO_OWNERLOG", "Ownership transfer history for $b%s$b:"}, /* {ARGS: "#TestChan"} */ + {"NS_INFO_OWNERCHANGE", " from %s to %s on %s"}, + {"NS_RENAME_DONE", "Renamed $b%s$b to $b%s$b."}, /* {ARGS: "TestUser", "TestUser2"} */ + {"NS_RENAME_FAIL", "Failed renaming $b%s$b."}, /* {ARGS: "TestUser"} */ + {"NS_FUN_DISABLED", "Fun commands are disabled in %s."}, /* {ARGS: "#TestChan"} */ + {"NS_UNBIND_REQUIRED", "%1$s is a required function and there is no other command bound to %1$s. Bind another command to %1$s first."}, /* {ARGS: "bind"} */ + {"NS_COMMANDS_NAME", "Name"}, + {"NS_COMMANDS_ACCESS", "Access"}, + {"NS_COMMANDS_GACCESS", "GodAccess"}, + {"NS_COMMANDS_TRIGGERED", "Triggered"}, + {"NS_COMMANDS_FUNCTION", "Function"}, + {"NS_DNR_SET", "$b%s$b is do-not-register (by $b%s$b): %s" }, /* {ARGS: "#TestChan", "TestUser", "because of it is like it is"} */ + {"NS_DNR_SET_EXPIRES", "$b%s$b is do-not-register (by $b%s$b; expires %s): %s" }, /* {ARGS: "#TestChan", "TestUser", "1 day", "because of it is like it is"} */ + {"NS_DNR_SET_ANONYM", "$b%s$b is do-not-register. Please contact the support to get more information."}, /* {ARGS: "TestUser"} */ + {"NS_NOREGISTER_INVALID_DURATION", "$b%s$b is not a valid duration."}, /* {ARGS: "möp"} */ + {"NS_NOREGISTER_REGISTERED", "$b%s$b is currently registered and can't be added to the do-not-register list."}, /* {ARGS: "#TestChan"} */ + {"NS_NOREGISTER_DONE", "added $b%s$b to the do-not-register list."}, /* {ARGS: "#TestChan"} */ + {"NS_NOREGISTER_HEAD", "The following do-not-registers were found:"}, + {"NS_DNR_TARGET", "Target"}, + {"NS_DNR_USER", "Issuer"}, + {"NS_DNR_EXPIRES", "Expires"}, + {"NS_DNR_REASON", "Reason"}, + {"NS_STAFF_LOGGEDIN", "Logged in as"}, + {"NS_BOTS_ID", "Id"}, + {"NS_BOTS_NICK", "Nick"}, + {"NS_BOTS_SERVER", "Server:Port(:Pass)"}, + {"NS_BOTS_CLASS", "Bot Class"}, + {"NS_BOTS_FLAGS", "Flags"}, + {"NS_BOTS_CHANNELS", "Channels"}, + {"NS_BOTS_TRIGGER", "Trigger"}, + {"NS_NICKLIST_NICK", "Nick"}, + {"NS_NICKLIST_STATE", "State"}, + {"NS_NICKLIST_ACCESS", "Access"}, + {"NS_NICKLIST_SYNC", "use `nicklist sync` to fix all red and orange entrys in the list above (add opped users with %d and voiced with %d access)"}, + {"NS_NICKLIST_ACCESS_BOT", "Bot"}, + {"NS_NICKLIST_ACCESS_OPER", "Operator"}, + {"NS_SETBOT_UNKNOWN", "`%d` is an unknown botid."}, /* {ARGS: 50} */ + {"NS_SETBOT_HEADER", "$bSettings for botid `%d`:$b"}, /* {ARGS: 50} */ + {"NS_SETBOT_SETTING", "$b%s$b is an unknown bot setting."}, /* {ARGS: "strangeSetting"} */ + {"NS_SETBOT_NICK_INVALID", "`%s` is an invalid botnick."}, /* {ARGS: "-SuperMagicBananaBotWithManyFunctions"} */ + {"NS_SETBOT_NEED_RESTART", "You need to reconnect the bot to apply this setting."}, + {"NS_SETBOT_PORT_INVALID", "`%s` is an invalid port number."}, /* {ARGS: "-1"} */ + {"NS_SETBOT_INVALID_CLASS", "`%s` is an invalid botclass."}, /* {ARGS: "MistColaLeer"} */ + {"NS_SETBOT_MAXCHAN_INVALID", "`%s` is an invalid maxchan value."}, /* {ARGS: "-1"} */ + {"NS_SETBOT_PRIORITY_INVALID", "`%s` is an invalid priority value."}, /* {ARGS: "-1"} */ + {"NS_SETBOT_TRIGGER_INVALID", "`%s` is an invalid bot trigger."}, /* {ARGS: "tooLongTrigger"} */ + {"NS_SETBOT_TRIGGER_NOTE", "Please note: This Setting will only affect new channels."}, + {"NS_ADDBOT_EXISTING", "A bot with nick %s does already exist."}, /* {ARGS: "NeonServ"} */ + {"NS_ADDBOT_DONE", "Added %s with BotID $b%d$b."}, /* {ARGS: "NeonServ", 2} */ + {"NS_DELBOT_NOT_FOUND", "Bot with BotID / nick $b%s$b not found."}, /* {ARGS: "NeonServ"} */ + {"NS_DELBOT_DONE", "Bot deleted."}, + {"NS_RECONNECT_DONE", "Reconnected bot."}, + {"NS_MODCMD_SETTING", "$b%s$b is an unknown modcmd setting."}, /* {ARGS: "strangeSetting"} */ + {"NS_MODCMD_HEADER", "$bSettings for command %s:$b"}, /* {ARGS: "access"} */ + {"NS_MODCMD_OUTRANKED", "$b%s$b outranks you. (required access: %d)"}, /* {ARGS: "die", 1000} */ + {"NS_MODCMD_STATIC_FLAG", "This Flag is added statically. It can't be modified manually."}, + {"NS_MEMINFO_DISABLED", "Memory Debugger is disabled!"}, + {"NS_MEMINFO_NAME", "Name"}, + {"NS_MEMINFO_COUNT", "Count"}, + {"NS_MEMINFO_SIZE", "Size"}, + {"NS_MEMINFO_LINE", "Line"}, + {"NS_MEMINFO_TOTAL", "Total"}, + {NULL, NULL} +}; + +/* TODO: +cmd_neonserv_open.c +set modelock +cmd_neonserv_modcmd.c +cmd_neonserv_allowregister.c +cmd_neonserv_noregister.c +cmd_neonserv_expire.c +cmd_neonserv_unvisited.c +cmd_neonserv_merge.c +cmd_neonserv_dnrsearch.c +cmd_neonserv_iplocate.c +cmd_neonserv_calc.c +*/ +//EVENTS +#include "event_neonserv_join.c" +#include "event_neonserv_part.c" +#include "event_neonserv_kick.c" +#include "event_neonserv_mode.c" +#include "event_neonserv_ctcp.c" +#include "event_neonserv_notice.c" +#include "event_neonserv_invite.c" +#include "event_neonserv_topic.c" + + +struct ClientSocket *getBotForChannel(struct ChanNode *chan) { + return getChannelBot(chan, BOTID); +} + +static void neonserv_bot_ready(struct ClientSocket *client) { + if(client->botid != BOTID) + return; + MYSQL_RES *res; + MYSQL_ROW row; + + printf_mysql_query("SELECT `automodes`, `oper_user`, `oper_pass` FROM `bots` WHERE `id` = '%d'", client->clientid); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + if(row[1] && row[2]) { + putsock(client, "OPER %s %s", row[1], row[2]); + } + putsock(client, "MODE %s +%s", client->user->nick, row[0]); + } + + printf_mysql_query("SELECT `channel_name`, `channel_key` FROM `bot_channels` LEFT JOIN `channels` ON `chanid` = `channel_id` WHERE `botid` = '%d' AND `suspended` = '0'", client->clientid); + res = mysql_use(); + + while ((row = mysql_fetch_row(res)) != NULL) { + putsock(client, "JOIN %s %s", row[0], row[1]); + } +} + +static void neonserv_trigger_callback(int clientid, struct ChanNode *chan, char *trigger) { + MYSQL_RES *res; + MYSQL_ROW row; + loadChannelSettings(chan); + if(!(chan->flags & CHANFLAG_CHAN_REGISTERED)) { + strcpy(trigger, "+"); + return; + } + printf_mysql_query("SELECT `trigger`, `defaulttrigger` FROM `bot_channels` LEFT JOIN `bots` ON `botid` = `bots`.`id` WHERE `chanid` = '%d' AND `botclass` = '%d'", chan->channel_id, BOTID); + res = mysql_use(); + if(!(row = mysql_fetch_row(res))) { + strcpy(trigger, "+"); + return; + } + if(row[0] && *row[0]) + strcpy(trigger, row[0]); + else + strcpy(trigger, ((row[1] && *row[1]) ? row[1] : "~")); +} + +static void start_bots(int type) { + struct ClientSocket *client; + MYSQL_RES *res, *res2; + MYSQL_ROW row; + + if(type == MODSTATE_STARTSTOP) { + printf_mysql_query("SELECT `nick`, `ident`, `realname`, `server`, `port`, `pass`, `textbot`, `id`, `queue`, `ssl`, `bind`, `secret` FROM `bots` WHERE `botclass` = '%d' AND `active` = '1'", BOTID); + res = mysql_use(); + while ((row = mysql_fetch_row(res)) != NULL) { + client = create_socket(row[3], atoi(row[4]), row[10], row[5], row[0], row[1], row[2]); + client->flags |= (strcmp(row[6], "0") ? SOCKET_FLAG_PREFERRED : 0); + client->flags |= (strcmp(row[8], "0") ? SOCKET_FLAG_USE_QUEUE : 0); + client->flags |= (strcmp(row[9], "0") ? SOCKET_FLAG_SSL : 0); + client->flags |= (strcmp(row[11], "0") ? SOCKET_FLAG_SECRET_BOT : 0); + client->flags |= SOCKET_FLAG_REQUEST_INVITE | SOCKET_FLAG_REQUEST_OP; + client->botid = BOTID; + client->clientid = atoi(row[7]); + connect_socket(client); + } + } + + printf_mysql_query("SELECT `command`, `function`, `parameters`, `global_access`, `chan_access`, `flags` FROM `bot_binds` WHERE `botclass` = '%d'", BOTID); + res2 = mysql_use(); + while ((row = mysql_fetch_row(res2)) != NULL) { + if(bind_cmd_to_command(BOTID, row[0], row[1])) { + if(row[2] && strcmp(row[2], "")) { + bind_set_parameters(BOTID, row[0], row[2]); + } + if(row[3]) { + bind_set_global_access(BOTID, row[0], atoi(row[3])); + } + if(row[4]) { + bind_set_channel_access(BOTID, row[0], row[4]); + } + if(strcmp(row[5], "0")) + bind_set_bind_flags(BOTID, row[0], atoi(row[5])); + } + } + bind_unbound_required_functions(BOTID); +} + +void init_NeonServ(int type) { + set_bot_alias(BOTID, BOTALIAS); + start_bots(type); + + if(type == MODSTATE_REBIND) return; + + //register events + bind_bot_ready(neonserv_bot_ready, module_id); + bind_join(neonserv_event_join, module_id); + bind_part(neonserv_event_part, module_id); + bind_chanctcp(neonserv_event_chanctcp, module_id); + bind_privctcp(general_event_privctcp, module_id); + bind_channotice(neonserv_event_channotice, module_id); + bind_topic(neonserv_event_topic, module_id); + bind_invite(neonserv_event_invite, module_id); + bind_mode(neonserv_event_mode, module_id); + bind_kick(neonserv_event_kick, module_id); + + set_trigger_callback(BOTID, module_id, neonserv_trigger_callback); + + register_default_language_table(msgtab); +} + +void free_NeonServ(int type) { + unbind_allcmd(BOTID); + if(type == MODSTATE_STARTSTOP) { + //disconnect all our bots + struct ClientSocket *client; + for(client = getBots(0, NULL); client; client = getBots(0, client)) { + if(client->botid == BOTID) { + unbind_botwise_allcmd(0, client->clientid); + close_socket(client); + break; + } + } + } +} + +#undef BOTID +#undef BOTALIAS diff --git a/src/modules/NeonServ.mod/bot_NeonServ.h b/src/modules/NeonServ.mod/bot_NeonServ.h new file mode 100644 index 0000000..1f3f2cc --- /dev/null +++ b/src/modules/NeonServ.mod/bot_NeonServ.h @@ -0,0 +1,29 @@ +/* bot_NeonServ.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _bot_NeonServ_h +#define _bot_NeonServ_h + +#include "../../main.h" + +struct ChanNode; + +void init_NeonServ(int type); +void free_NeonServ(int type); + +struct ClientSocket *getBotForChannel(struct ChanNode *chan); + +#endif \ No newline at end of file diff --git a/src/modules/NeonServ.mod/cmd_neonserv.c b/src/modules/NeonServ.mod/cmd_neonserv.c new file mode 100644 index 0000000..62750a4 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv.c @@ -0,0 +1,104 @@ +/* cmd_neonserv.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#include "../module.h" +#include "cmd_neonserv.h" +#include "../../modcmd.h" +#include "../../ConfigParser.h" + +void register_commands() { + //NeonServ Commands + register_command_alias(1, "NeonServ"); + + #define USER_COMMAND(NAME,FUNCTION,PARAMCOUNT,PRIVS,FLAGS) register_command(1, NAME, module_id, FUNCTION, PARAMCOUNT, PRIVS, 0, FLAGS) + // NAME FUNCTION PARAMS PRIVS FLAGS + USER_COMMAND("adduser", neonserv_cmd_adduser, 2, "#channel_canadd", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("deluser", neonserv_cmd_deluser, 1, "#channel_candel", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("clvl", neonserv_cmd_clvl, 2, "#channel_canclvl", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("access", neonserv_cmd_access, 0, NULL, CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_LOG); + USER_COMMAND("users", neonserv_cmd_users, 0, NULL, CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_NO_CROSSCHAN); + USER_COMMAND("suspend", neonserv_cmd_suspend, 1, "#channel_cansuspend", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("unsuspend", neonserv_cmd_unsuspend, 1, "#channel_cansuspend", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("delme", neonserv_cmd_delme, 0, "1", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("myaccess", neonserv_cmd_myaccess, 0, NULL, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH); + USER_COMMAND("up", neonserv_cmd_up, 0, NULL, CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("down", neonserv_cmd_down, 0, NULL, CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_LOG); + USER_COMMAND("upall", neonserv_cmd_upall, 0, NULL, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("downall", neonserv_cmd_downall, 0, NULL, CMDFLAG_LOG); + USER_COMMAND("mdeluser", neonserv_cmd_mdeluser, 2, "#channel_candel", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("trim", neonserv_cmd_trim, 2, NULL, CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("giveowner", neonserv_cmd_giveowner, 1, "500", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("op", neonserv_cmd_op, 1, "@#channel_getop,#channel_canop", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("deop", neonserv_cmd_deop, 1, "@#channel_getop,#channel_canop", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("voice", neonserv_cmd_voice, 1, "+#channel_getvoice,#channel_canvoice", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("devoice", neonserv_cmd_devoice, 1, "+#channel_getvoice,#channel_canvoice", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("opall", neonserv_cmd_opall, 0, "@#channel_getop,#channel_canop", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("deopall", neonserv_cmd_deopall, 0, "@#channel_getop,#channel_canop", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("voiceall", neonserv_cmd_voiceall, 0, "+#channel_getvoice,#channel_canvoice", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("devoiceall", neonserv_cmd_devoiceall,0, "+#channel_getvoice,#channel_canvoice", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + if(get_int_field("General.have_halfop")) { + USER_COMMAND("halfop", neonserv_cmd_halfop, 1, "%#channel_gethalfop,#channel_canhalfop", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("dehalfop", neonserv_cmd_dehalfop, 1, "%#channel_gethalfop,#channel_canhalfop", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("halfopall", neonserv_cmd_halfopall, 0, "%#channel_gethalfop,#channel_canhalfop", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("dehalfopall", neonserv_cmd_dehalfopall,0,"%#channel_gethalfop,#channel_canhalfop", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + } + USER_COMMAND("set", neonserv_cmd_set, 0, "#channel_setters", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("kick", neonserv_cmd_kick, 1, "#channel_cankick", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("kickban", neonserv_cmd_kickban, 1, "#channel_cankick,#channel_canban", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("ban", neonserv_cmd_ban, 1, "#channel_canban", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("wipeinfo", neonserv_cmd_wipeinfo, 1, "#channel_wipeinfo", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("addban", neonserv_cmd_addban, 1, "#channel_staticban", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("bans", neonserv_cmd_bans, 0, "1", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH); + USER_COMMAND("delban", neonserv_cmd_delban, 1, "#channel_staticban", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("topic", neonserv_cmd_topic, 0, "#channel_changetopic", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("chanservsync", neonserv_cmd_chanservsync, 0,"500", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("resync", neonserv_cmd_resync, 0, "#channel_canresync", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("addtimeban", neonserv_cmd_addtimeban,2, "#channel_staticban", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("mode", neonserv_cmd_mode, 1, "#channel_getop", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("peek", neonserv_cmd_peek, 0, NULL, CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_NO_CROSSCHAN); + USER_COMMAND("uset", neonserv_cmd_uset, 0, NULL, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_NO_CROSSCHAN); + USER_COMMAND("unban", neonserv_cmd_unban, 1, "#channel_canban", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("unbanall", neonserv_cmd_unbanall, 0, "#channel_canban", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("unbanme", neonserv_cmd_unbanme, 0, "#channel_canban", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("invite", neonserv_cmd_invite, 1, "#channel_canop", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("inviteme", neonserv_cmd_inviteme, 0, "#channel_getinvite", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("invitemeall", neonserv_cmd_invitemeall,0,NULL, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("help", neonserv_cmd_help, 0, NULL, 0); + USER_COMMAND("events", neonserv_cmd_events, 0, "1", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH); + USER_COMMAND("info", neonserv_cmd_info, 0, NULL, CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_NO_CROSSCHAN); + USER_COMMAND("nicklist", neonserv_cmd_nicklist, 0, "1", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH); + #undef USER_COMMAND + + #define OPER_COMMAND(NAME,FUNCTION,PARAMCOUNT,GACCESS,FLAGS) register_command(1, NAME, module_id, FUNCTION, PARAMCOUNT, NULL, GACCESS, FLAGS) + // NAME FUNCTION PARAMS ACCS FLAGS + OPER_COMMAND("trace", neonserv_cmd_trace, 1, 400, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH); + OPER_COMMAND("recover", neonserv_cmd_recover, 1, 200, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_CHAN_PARAM | CMDFLAG_OPLOG); + OPER_COMMAND("csuspend", neonserv_cmd_csuspend, 1, 100, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_CHAN_PARAM | CMDFLAG_OPLOG); + OPER_COMMAND("cunsuspend", neonserv_cmd_cunsuspend,1, 100, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_CHAN_PARAM | CMDFLAG_OPLOG); + OPER_COMMAND("oplog", neonserv_cmd_oplog, 0, 1, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_OPLOG); + OPER_COMMAND("search", neonserv_cmd_search, 1, 400, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH); + OPER_COMMAND("addrank", neonserv_cmd_addrank, 1, 1000, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_OPLOG); + OPER_COMMAND("delrank", neonserv_cmd_delrank, 1, 1000, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_OPLOG); + OPER_COMMAND("setrank", neonserv_cmd_setrank, 1, 1000, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_OPLOG); + OPER_COMMAND("assignrank", neonserv_cmd_assignrank,2, 1000, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_OPLOG); + OPER_COMMAND("listrank", neonserv_cmd_listrank, 0, 1, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_OPLOG); + OPER_COMMAND("rename", neonserv_cmd_rename, 2, 300, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_OPLOG); + OPER_COMMAND("unvisited", neonserv_cmd_unvisited, 0, 400, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_OPLOG); + OPER_COMMAND("noregister", neonserv_cmd_noregister,0, 300, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_CHAN_PARAM | CMDFLAG_OPLOG); + #undef OPER_COMMAND + + neonserv_cmd_unvisited_init(); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv.h b/src/modules/NeonServ.mod/cmd_neonserv.h new file mode 100644 index 0000000..da09147 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv.h @@ -0,0 +1,117 @@ +/* cmd_neonserv.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _cmd_neonserv_h +#define _cmd_neonserv_h +#include "../module.h" +#include "../botid.h" +#include "../../main.h" +#include "../../modcmd.h" +#include "../../IRCParser.h" +#include "../../IRCEvents.h" +#include "../../UserNode.h" +#include "../../ChanNode.h" +#include "../../ChanUser.h" +#include "../../ModeNode.h" +#include "../../BanNode.h" +#include "../../ClientSocket.h" +#include "../../mysqlConn.h" +#include "../../lang.h" +#include "../../HandleInfoHandler.h" +#include "../../WHOHandler.h" +#include "../../DBHelper.h" +#include "../../tools.h" +#include "../../timeq.h" +#include "../../version.h" +#include "../../EventLogger.h" +#include "../../bots.h" +#include "bot_NeonServ.h" +#include "../../ConfigParser.h" + +void register_commands(); + +/* cmd_neonserv_unvisited.c */ +void neonserv_cmd_unvisited_init(); + +CMD_BIND(neonserv_cmd_access); +CMD_BIND(neonserv_cmd_addban); +CMD_BIND(neonserv_cmd_addrank); +CMD_BIND(neonserv_cmd_addtimeban); +CMD_BIND(neonserv_cmd_adduser); +CMD_BIND(neonserv_cmd_assignrank); +CMD_BIND(neonserv_cmd_ban); +CMD_BIND(neonserv_cmd_bans); +CMD_BIND(neonserv_cmd_chanservsync); +CMD_BIND(neonserv_cmd_clvl); +CMD_BIND(neonserv_cmd_csuspend); +CMD_BIND(neonserv_cmd_cunsuspend); +CMD_BIND(neonserv_cmd_dehalfop); +CMD_BIND(neonserv_cmd_dehalfopall); +CMD_BIND(neonserv_cmd_delban); +CMD_BIND(neonserv_cmd_delme); +CMD_BIND(neonserv_cmd_delrank); +CMD_BIND(neonserv_cmd_deluser); +CMD_BIND(neonserv_cmd_deop); +CMD_BIND(neonserv_cmd_deopall); +CMD_BIND(neonserv_cmd_devoice); +CMD_BIND(neonserv_cmd_devoiceall); +CMD_BIND(neonserv_cmd_down); +CMD_BIND(neonserv_cmd_downall); +CMD_BIND(neonserv_cmd_events); +CMD_BIND(neonserv_cmd_giveowner); +CMD_BIND(neonserv_cmd_halfop); +CMD_BIND(neonserv_cmd_halfopall); +CMD_BIND(neonserv_cmd_help); +CMD_BIND(neonserv_cmd_info); +CMD_BIND(neonserv_cmd_invite); +CMD_BIND(neonserv_cmd_inviteme); +CMD_BIND(neonserv_cmd_invitemeall); +CMD_BIND(neonserv_cmd_kick); +CMD_BIND(neonserv_cmd_kickban); +CMD_BIND(neonserv_cmd_listrank); +CMD_BIND(neonserv_cmd_mdeluser); +CMD_BIND(neonserv_cmd_mode); +CMD_BIND(neonserv_cmd_myaccess); +CMD_BIND(neonserv_cmd_nicklist); +CMD_BIND(neonserv_cmd_noregister); +CMD_BIND(neonserv_cmd_op); +CMD_BIND(neonserv_cmd_opall); +CMD_BIND(neonserv_cmd_oplog); +CMD_BIND(neonserv_cmd_peek); +CMD_BIND(neonserv_cmd_recover); +CMD_BIND(neonserv_cmd_rename); +CMD_BIND(neonserv_cmd_resync); +CMD_BIND(neonserv_cmd_search); +CMD_BIND(neonserv_cmd_set); +CMD_BIND(neonserv_cmd_setrank); +CMD_BIND(neonserv_cmd_suspend); +CMD_BIND(neonserv_cmd_topic); +CMD_BIND(neonserv_cmd_trace); +CMD_BIND(neonserv_cmd_trim); +CMD_BIND(neonserv_cmd_unban); +CMD_BIND(neonserv_cmd_unbanall); +CMD_BIND(neonserv_cmd_unbanme); +CMD_BIND(neonserv_cmd_unsuspend); +CMD_BIND(neonserv_cmd_unvisited); +CMD_BIND(neonserv_cmd_up); +CMD_BIND(neonserv_cmd_upall); +CMD_BIND(neonserv_cmd_users); +CMD_BIND(neonserv_cmd_uset); +CMD_BIND(neonserv_cmd_voice); +CMD_BIND(neonserv_cmd_voiceall); +CMD_BIND(neonserv_cmd_wipeinfo); + +#endif \ No newline at end of file diff --git a/src/modules/NeonServ.mod/cmd_neonserv_access.c b/src/modules/NeonServ.mod/cmd_neonserv_access.c new file mode 100644 index 0000000..ce84cd4 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_access.c @@ -0,0 +1,173 @@ +/* cmd_neonserv_access.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0] - nick / *auth +*/ +static USERAUTH_CALLBACK(neonserv_cmd_access_nick_lookup); +static void neonserv_cmd_access_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, char *nick, char *auth, struct UserNode *target); +static void neonserv_cmd_access_filter_ranking_info(char *info, char *buffer, char *nick, char *auth, char *rank_name); + +struct neonserv_cmd_access_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + struct ChanNode *chan; + char *nick; +}; + +CMD_BIND(neonserv_cmd_access) { + if(argc == 0) { + if(!(user->flags & USERFLAG_ISAUTHED)) { + struct neonserv_cmd_access_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->chan = chan; + cache->nick = strdup(user->nick); + get_userauth(user, module_id, neonserv_cmd_access_nick_lookup, cache); + } else + neonserv_cmd_access_async1(client, textclient, user, chan, user->nick, user->auth, user); + } + else if(argv[0][0] == '*') { + //we've got an auth + argv[0]++; + neonserv_cmd_access_async1(client, textclient, user, chan, NULL, argv[0], NULL); + } else { + struct UserNode *cuser = getUserByNick(argv[0]); + if(!cuser) { + cuser = createTempUser(argv[0]); + if(!cuser) { + reply(textclient, user, "NS_USER_UNKNOWN", argv[0]); + return; + } + cuser->flags |= USERFLAG_ISTMPUSER; + } + if(cuser->flags & USERFLAG_ISAUTHED) { + neonserv_cmd_access_async1(client, textclient, user, chan, argv[0], cuser->auth, cuser); + } else { + struct neonserv_cmd_access_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->chan = chan; + cache->nick = strdup(argv[0]); + get_userauth(cuser, module_id, neonserv_cmd_access_nick_lookup, cache); + } + } +} + +static USERAUTH_CALLBACK(neonserv_cmd_access_nick_lookup) { + struct neonserv_cmd_access_cache *cache = data; + if(!user) { + //USER_DOES_NOT_EXIST + reply(cache->textclient, cache->user, "NS_USER_UNKNOWN", cache->nick); + } + else if(!(user->flags & USERFLAG_ISAUTHED)) { + //USER_NOT_AUTHED + if(!strcmp(cache->nick, cache->user->nick)) + reply(cache->textclient, cache->user, "NS_YOU_NEED_AUTH"); + else + reply(cache->textclient, cache->user, "NS_USER_NEED_AUTH", cache->nick); + } + else + neonserv_cmd_access_async1(cache->client, cache->textclient, cache->user, cache->chan, user->nick, user->auth, user); + free(cache->nick); + free(cache); +} + +static void neonserv_cmd_access_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, char *nick, char *auth, struct UserNode *target) { + //we've got a valid auth now... + MYSQL_RES *res; + MYSQL_ROW user_row, chanuser_row; + int userid; + printf_mysql_query("SELECT `user_id`, `user_access`, `user_god`, `user_rank`, `rank_info`, `rank_name` FROM `users` LEFT JOIN `support_ranks` ON `rank_id` = `user_rank` WHERE `user_user` = '%s'", escape_string(auth)); + res = mysql_use(); + if ((user_row = mysql_fetch_row(res)) != NULL) { + userid = atoi(user_row[0]); + //check if the user is already added + printf_mysql_query("SELECT `chanuser_access`, `chanuser_flags`, `chanuser_infoline` FROM `chanusers` WHERE `chanuser_cid` = '%d' AND `chanuser_uid` = '%d'", chan->channel_id, userid); + res = mysql_use(); + if(strcmp(user_row[3], "0") && strcmp(user_row[4], "")) { + char rank_info[MAXLEN]; + neonserv_cmd_access_filter_ranking_info(user_row[4], rank_info, nick, auth, user_row[5]); + reply(textclient, user, "%s", rank_info); + } + if ((chanuser_row = mysql_fetch_row(res)) != NULL) { + //access output + if(nick) + reply(textclient, user, (strcmp(user_row[2], "1") ? "NS_A_ACCESS_AUTH" : "NS_A_ACCESS_AUTH_GOD"), nick, auth, atoi(chanuser_row[0]), chan->name); + else + reply(textclient, user, (strcmp(user_row[2], "1") ? "NS_A_ACCESS_NICK" : "NS_A_ACCESS_NICK_GOD"), auth, atoi(chanuser_row[0]), chan->name); + int cflags = atoi(chanuser_row[1]); + if(cflags & DB_CHANUSER_SUSPENDED) + reply(textclient, user, "NS_A_SUSPENDED", (nick ? nick : auth), chan->name); + if(chanuser_row[2] && strcmp(chanuser_row[2], "")) + reply(textclient, user, "[%s] %s", (nick ? nick : auth), chanuser_row[2]); + } else if(!strcmp(user_row[2], "1")) { + if(nick) + reply(textclient, user, "NS_A_LACKS_ACCESS_BUT_GOD_AUTH", nick, auth, chan->name); + else + reply(textclient, user, "NS_A_LACKS_ACCESS_BUT_GOD_NICK", auth, chan->name); + } else + reply(textclient, user, "NS_NOT_ON_USERLIST", (nick ? nick : auth), chan->name); + } else + reply(textclient, user, "NS_NOT_ON_USERLIST", (nick ? nick : auth), chan->name); + if(target && (target->flags & USERFLAG_ISIRCOP)) + reply(textclient, user, "NS_A_IS_IRCOP", nick); +} + +static void neonserv_cmd_access_filter_ranking_info(char *info, char *buffer, char *nick, char *auth, char *rank_name) { + int bufferPos = 0; + char *a, *b = info; + do { + if(!b) break; + a = strstr(b, "$"); + if(a) *a = '\0'; + bufferPos += sprintf(buffer + bufferPos, "%s", b); + if(!a) break; + switch(a[1]) { + case '\0': + a = NULL; + break; + case 'U': + bufferPos += sprintf(buffer + bufferPos, "%s", auth); + break; + case 'N': + bufferPos += sprintf(buffer + bufferPos, "%s", (nick ? nick : auth)); + break; + case 'R': + bufferPos += sprintf(buffer + bufferPos, "%s", rank_name); + break; + default: + buffer[bufferPos++] = '$'; + buffer[bufferPos++] = a[1]; + break; + } + if(a) + b = a+2; + } while(a); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_addban.c b/src/modules/NeonServ.mod/cmd_neonserv_addban.c new file mode 100644 index 0000000..63e8224 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_addban.c @@ -0,0 +1,128 @@ +/* cmd_neonserv_addban.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0] nick|*auth|*!*@mask +* argv[1-*] reason +*/ +static USERLIST_CALLBACK(neonserv_cmd_addban_userlist_lookup); +static void neonserv_cmd_addban_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *mas, char *reason); + +struct neonserv_cmd_addban_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + struct Event *event; + char *mask; + char *reason; +}; + +CMD_BIND(neonserv_cmd_addban) { + struct neonserv_cmd_addban_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->event = event; + cache->mask = strdup(argv[0]); + if(argc > 1) { + cache->reason = strdup(merge_argv(argv, 1, argc)); + } else + cache->reason = NULL; + get_userlist(chan, module_id, neonserv_cmd_addban_userlist_lookup, cache); +} + +static USERLIST_CALLBACK(neonserv_cmd_addban_userlist_lookup) { + struct neonserv_cmd_addban_cache *cache = data; + neonserv_cmd_addban_async1(cache->client, cache->textclient, cache->user, chan, cache->event, cache->mask, (cache->reason ? cache->reason : "Bye.")); + free(cache->mask); + if(cache->reason) + free(cache->reason); + free(cache); +} + +static void neonserv_cmd_addban_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *mask, char *reason) { + int match_count = 0; + char hostmask_buffer[NICKLEN+USERLEN+HOSTLEN+3]; + char usermask[NICKLEN+USERLEN+HOSTLEN+3]; + struct UserNode *cuser; + struct ChanUser *chanuser; + mask = make_banmask(mask, hostmask_buffer); + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + cuser = chanuser->user; + sprintf(usermask, "%s!%s@%s", cuser->nick, cuser->ident, cuser->host); + if(!match(mask, usermask)) { + if(isNetworkService(chanuser->user)) { + reply(textclient, user, "NS_SERVICE_IMMUNE", chanuser->user->nick); + return; + } + if(cuser == user || ((cuser->flags & USERFLAG_ISAUTHED) && !stricmp(user->auth, cuser->auth))) { + reply(textclient, user, "NS_YOU_PROTECTED"); + return; + } + if(isUserProtected(chan, cuser, user)) { + reply(textclient, user, "NS_USER_PROTECTED", cuser->nick); + return; + } + match_count++; + if(match_count > 4 && (match_count * 3) > chan->usercount && !isGodMode(user)) { + reply(textclient, user, "NS_LAME_MASK", mask); + return; + } + } + } + MYSQL_RES *res; + MYSQL_ROW row; + //check if the provided mask is already banned by another ban + char *ban = getBanAffectingMask(chan, mask); + if(ban != NULL) { + reply(textclient, user, "NS_BAN_ALREADY_ADDED", mask, chan->name); + return; + } + //check if the provided mask affects any existing bans + printf_mysql_query("SELECT `ban_mask`, `ban_id` FROM `bans` WHERE `ban_channel` = '%d'", chan->channel_id); + res = mysql_use(); + while ((row = mysql_fetch_row(res)) != NULL) { + if(!match(mask, row[0])) { + //remove the ban + printf_mysql_query("DELETE FROM `bans` WHERE `ban_id` = '%s'", row[1]); + } + } + printf_mysql_query("SELECT `user_id` FROM `users` WHERE `user_user` = '%s'", escape_string(user->auth)); + int userid; + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) + userid = atoi(row[0]); + else + return; + //add the ban + printf_mysql_query("INSERT INTO `bans` (`ban_channel`, `ban_mask`, `ban_triggered`, `ban_owner`, `ban_reason`) VALUES ('%d', '%s', UNIX_TIMESTAMP(), '%d', '%s')", chan->channel_id, escape_string(mask), userid, escape_string(reason)); + putsock(client, "MODE %s +b %s", chan->name, mask); + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + cuser = chanuser->user; + sprintf(usermask, "%s!%s@%s", cuser->nick, cuser->ident, cuser->host); + if(!match(mask, usermask)) { + putsock(client, "KICK %s %s :(%s) %s", chan->name, cuser->nick, user->nick, reason); + } + } + reply(textclient, user, "NS_ADDBAN_DONE", mask, chan->name, match_count); + logEvent(event); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_addrank.c b/src/modules/NeonServ.mod/cmd_neonserv_addrank.c new file mode 100644 index 0000000..a049bbd --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_addrank.c @@ -0,0 +1,36 @@ +/* cmd_neonserv_addrank.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0-*] rank name +*/ + +CMD_BIND(neonserv_cmd_addrank) { + char *name = merge_argv(argv, 0, argc); + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `rank_name` FROM `support_ranks` WHERE `rank_name` = '%s'", escape_string(name)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + reply(textclient, user, "NS_ADDRANK_EXISTS", row[0]); + return; + } + printf_mysql_query("INSERT INTO `support_ranks` (`rank_name`) VALUES ('%s')", escape_string(name)); + reply(textclient, user, "NS_ADDRANK_DONE", name); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_addtimeban.c b/src/modules/NeonServ.mod/cmd_neonserv_addtimeban.c new file mode 100644 index 0000000..2e096f9 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_addtimeban.c @@ -0,0 +1,140 @@ +/* cmd_neonserv_addtimeban.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0] nick|*auth|*!*@mask +* argv[1] time +* argv[2-*] reason +*/ +static USERLIST_CALLBACK(neonserv_cmd_addtimeban_userlist_lookup); +static void neonserv_cmd_addtimeban_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *mask, int duration, char *reason); + +struct neonserv_cmd_addtimeban_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + struct Event *event; + char *mask; + int duration; + char *reason; +}; + +CMD_BIND(neonserv_cmd_addtimeban) { + int duration = strToTime(user, argv[1]); + if(duration < 5) { + reply(textclient, user, "NS_TIMEBAN_DURATION_TOO_SHORT", 5); + return; + } + struct neonserv_cmd_addtimeban_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->event = event; + cache->mask = strdup(argv[0]); + cache->duration = duration; + if(argc > 2) { + cache->reason = strdup(merge_argv(argv, 2, argc)); + } else + cache->reason = NULL; + get_userlist(chan, module_id, neonserv_cmd_addtimeban_userlist_lookup, cache); +} + +static USERLIST_CALLBACK(neonserv_cmd_addtimeban_userlist_lookup) { + struct neonserv_cmd_addtimeban_cache *cache = data; + neonserv_cmd_addtimeban_async1(cache->client, cache->textclient, cache->user, chan, cache->event, cache->mask, cache->duration, (cache->reason ? cache->reason : "Bye.")); + free(cache->mask); + if(cache->reason) + free(cache->reason); + free(cache); +} + +static void neonserv_cmd_addtimeban_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *mask, int duration, char *reason) { + int match_count = 0; + char hostmask_buffer[NICKLEN+USERLEN+HOSTLEN+3]; + char usermask[NICKLEN+USERLEN+HOSTLEN+3]; + struct UserNode *cuser; + struct ChanUser *chanuser; + mask = make_banmask(mask, hostmask_buffer); + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + cuser = chanuser->user; + sprintf(usermask, "%s!%s@%s", cuser->nick, cuser->ident, cuser->host); + if(!match(mask, usermask)) { + if(isNetworkService(chanuser->user)) { + reply(textclient, user, "NS_SERVICE_IMMUNE", chanuser->user->nick); + return; + } + if(isUserProtected(chan, cuser, user)) { + reply(textclient, user, "NS_USER_PROTECTED", cuser->nick); + return; + } + match_count++; + if(match_count > 4 && (match_count * 3) > chan->usercount && !isGodMode(user)) { + reply(textclient, user, "NS_LAME_MASK", mask); + return; + } + } + } + MYSQL_RES *res; + MYSQL_ROW row; + //check if the provided mask is already banned by another ban + char *ban = getBanAffectingMask(chan, mask); + if(ban != NULL) { + reply(textclient, user, "NS_BAN_ALREADY_ADDED", mask, chan->name); + return; + } + //check if the provided mask affects any existing bans + printf_mysql_query("SELECT `ban_mask`, `ban_id` FROM `bans` WHERE `ban_channel` = '%d'", chan->channel_id); + res = mysql_use(); + while ((row = mysql_fetch_row(res)) != NULL) { + if(!match(mask, row[0])) { + //remove the ban + printf_mysql_query("DELETE FROM `bans` WHERE `ban_id` = '%s'", row[1]); + } + } + printf_mysql_query("SELECT `user_id` FROM `users` WHERE `user_user` = '%s'", escape_string(user->auth)); + int userid; + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) + userid = atoi(row[0]); + else + return; + //add the ban + printf_mysql_query("INSERT INTO `bans` (`ban_channel`, `ban_mask`, `ban_triggered`, `ban_timeout`, `ban_owner`, `ban_reason`) VALUES ('%d', '%s', UNIX_TIMESTAMP(), '%lu', '%d', '%s')", chan->channel_id, escape_string(mask), (unsigned long) (time(0) + duration), userid, escape_string(reason)); + int banid = (int) mysql_insert_id(get_mysql_conn()); + putsock(client, "MODE %s +b %s", chan->name, mask); + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + cuser = chanuser->user; + sprintf(usermask, "%s!%s@%s", cuser->nick, cuser->ident, cuser->host); + if(!match(mask, usermask)) { + putsock(client, "KICK %s %s :(%s) %s", chan->name, cuser->nick, user->nick, reason); + } + } + char nameBuf[MAXLEN]; + if(duration < 86400*7) { + char banidBuf[20]; + sprintf(nameBuf, "ban_%d", banid); + sprintf(banidBuf, "%d", banid); + timeq_add_name(nameBuf, duration, module_id, channel_ban_timeout, strdup(banidBuf)); + } + reply(textclient, user, "NS_TIMEBAN_DONE", mask, chan->name, timeToStr(user, duration, 2, nameBuf), match_count); + logEvent(event); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_adduser.c b/src/modules/NeonServ.mod/cmd_neonserv_adduser.c new file mode 100644 index 0000000..e8b14bd --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_adduser.c @@ -0,0 +1,159 @@ +/* cmd_neonserv_adduser.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0] - nick / *auth +* argv[1] - chan access +*/ +static AUTHLOOKUP_CALLBACK(neonserv_cmd_adduser_auth_lookup); +static USERAUTH_CALLBACK(neonserv_cmd_adduser_nick_lookup); +static void neonserv_cmd_adduser_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *nick, char *auth, int access); + +struct neonserv_cmd_adduser_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + struct ChanNode *chan; + struct Event *event; + int access; + char *nick; +}; + +CMD_BIND(neonserv_cmd_adduser) { + int caccess; + MYSQL_RES *res; + MYSQL_ROW row; + caccess = atoi(argv[1]); + if(caccess <= 0 || caccess > 500) { + reply(textclient, user, "NS_INVALID_ACCESS", caccess); + return; + } + if(caccess >= getChannelAccess(user, chan)) { + if(isGodMode(user)) { + event->flags |= CMDFLAG_OPLOG; + } else { + reply(textclient, user, "NS_ACCESS_OUTRANKED"); + return; + } + } + //check own access + if(argv[0][0] == '*') { + //we've got an auth + argv[0]++; + printf_mysql_query("SELECT `user_user` FROM `users` WHERE `user_user` = '%s'", escape_string(argv[0])); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + neonserv_cmd_adduser_async1(client, textclient, user, chan, event, argv[0], row[0], caccess); + } else { + //we need to create a new user... + //but first lookup the auth to check if it really exists + struct neonserv_cmd_adduser_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->chan = chan; + cache->event = event; + cache->access = caccess; + cache->nick = strdup(argv[0]); + lookup_authname(argv[0], module_id, neonserv_cmd_adduser_auth_lookup, cache); + } + } else { + struct UserNode *cuser = getUserByNick(argv[0]); + if(!cuser) { + cuser = createTempUser(argv[0]); + if(!cuser) { + reply(textclient, user, "NS_USER_UNKNOWN", argv[0]); + return; + } + cuser->flags |= USERFLAG_ISTMPUSER; + } + if(cuser->flags & USERFLAG_ISAUTHED) { + neonserv_cmd_adduser_async1(client, textclient, user, chan, event, argv[0], cuser->auth, caccess); + } else { + struct neonserv_cmd_adduser_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->chan = chan; + cache->event = event; + cache->access = caccess; + cache->nick = strdup(argv[0]); + get_userauth(cuser, module_id, neonserv_cmd_adduser_nick_lookup, cache); + } + } +} + +static AUTHLOOKUP_CALLBACK(neonserv_cmd_adduser_auth_lookup) { + struct neonserv_cmd_adduser_cache *cache = data; + if(!exists) { + //AUTH_DOES_NOT_EXIST + reply(cache->textclient, cache->user, "NS_AUTH_UNKNOWN", cache->nick); + } else + neonserv_cmd_adduser_async1(cache->client, cache->textclient, cache->user, cache->chan, cache->event, cache->nick, auth, cache->access); + free(cache->nick); + free(cache); +} + +static USERAUTH_CALLBACK(neonserv_cmd_adduser_nick_lookup) { + struct neonserv_cmd_adduser_cache *cache = data; + if(!user) { + //USER_DOES_NOT_EXIST + reply(cache->textclient, cache->user, "NS_USER_UNKNOWN", cache->nick); + } + else if(!(user->flags & USERFLAG_ISAUTHED)) { + //USER_NOT_AUTHED + reply(cache->textclient, cache->user, "NS_USER_NEED_AUTH", cache->nick); + } + else + neonserv_cmd_adduser_async1(cache->client, cache->textclient, cache->user, cache->chan, cache->event, user->nick, user->auth, cache->access); + free(cache->nick); + free(cache); +} + +static void neonserv_cmd_adduser_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *nick, char *auth, int caccess) { + //we've got a valid auth now... + MYSQL_RES *res; + MYSQL_ROW row; + int userid; + printf_mysql_query("SELECT `user_id` FROM `users` WHERE `user_user` = '%s'", escape_string(auth)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + userid = atoi(row[0]); + //check if the user is already added + printf_mysql_query("SELECT `chanuser_access` FROM `chanusers` WHERE `chanuser_cid` = '%d' AND `chanuser_uid` = '%d'", chan->channel_id, userid); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + reply(textclient, user, "NS_ADDUSER_ALREADY_ADDED", nick, chan->name, atoi(row[0])); + return; + } + } else { + printf_mysql_query("INSERT INTO `users` (`user_user`) VALUES ('%s')", escape_string(auth)); + userid = (int) mysql_insert_id(get_mysql_conn()); + } + printf_mysql_query("INSERT INTO `chanusers` (`chanuser_cid`, `chanuser_uid`, `chanuser_access`) VALUES ('%d', '%d', '%d')", chan->channel_id, userid, caccess); + reply(textclient, user, "NS_ADDUSER_DONE", nick, chan->name, caccess); + logEvent(event); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_assignrank.c b/src/modules/NeonServ.mod/cmd_neonserv_assignrank.c new file mode 100644 index 0000000..19a906a --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_assignrank.c @@ -0,0 +1,151 @@ +/* cmd_neonserv_assignrank.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0] - nick / *auth +* argv[1-*] - rank name +*/ +static AUTHLOOKUP_CALLBACK(neonserv_cmd_assignrank_auth_lookup); +static USERAUTH_CALLBACK(neonserv_cmd_assignrank_nick_lookup); +static void neonserv_cmd_assignrank_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct Event *event, char *nick, char *auth, int rank_id); + +struct neonserv_cmd_assignrank_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + struct Event *event; + int rank_id; + char *nick; +}; + +CMD_BIND(neonserv_cmd_assignrank) { + MYSQL_RES *res; + MYSQL_ROW row; + char *name = merge_argv(argv, 1, argc); + int rank_id = 0; + if(stricmp(name, "*") && stricmp(name, "user") && stricmp(name, "none")) { + printf_mysql_query("SELECT `rank_id`, `rank_name` FROM `support_ranks` WHERE `rank_name` = '%s'", escape_string(name)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) == NULL) { + reply(textclient, user, "NS_DELRANK_NOT_FOUND", name); + return; + } + rank_id = atoi(row[0]); + } + if(argv[0][0] == '*') { + //we've got an auth + argv[0]++; + printf_mysql_query("SELECT `user_user` FROM `users` WHERE `user_user` = '%s'", escape_string(argv[0])); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + neonserv_cmd_assignrank_async1(client, textclient, user, event, argv[0], row[0], rank_id); + } else { + //we need to create a new user... + //but first lookup the auth to check if it really exists + struct neonserv_cmd_assignrank_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->event = event; + cache->rank_id = rank_id; + cache->nick = strdup(argv[0]); + lookup_authname(argv[0], module_id, neonserv_cmd_assignrank_auth_lookup, cache); + } + } else { + struct UserNode *cuser = getUserByNick(argv[0]); + if(!cuser) { + cuser = createTempUser(argv[0]); + if(!cuser) { + reply(textclient, user, "NS_USER_UNKNOWN", argv[0]); + return; + } + cuser->flags |= USERFLAG_ISTMPUSER; + } + if(cuser->flags & USERFLAG_ISAUTHED) { + neonserv_cmd_assignrank_async1(client, textclient, user, event, argv[0], cuser->auth, rank_id); + } else { + struct neonserv_cmd_assignrank_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->event = event; + cache->rank_id = rank_id; + cache->nick = strdup(argv[0]); + get_userauth(cuser, module_id, neonserv_cmd_assignrank_nick_lookup, cache); + } + } +} + +static AUTHLOOKUP_CALLBACK(neonserv_cmd_assignrank_auth_lookup) { + struct neonserv_cmd_assignrank_cache *cache = data; + if(!exists) { + //AUTH_DOES_NOT_EXIST + reply(cache->textclient, cache->user, "NS_AUTH_UNKNOWN", cache->nick); + } else + neonserv_cmd_assignrank_async1(cache->client, cache->textclient, cache->user, cache->event, cache->nick, auth, cache->rank_id); + free(cache->nick); + free(cache); +} + +static USERAUTH_CALLBACK(neonserv_cmd_assignrank_nick_lookup) { + struct neonserv_cmd_assignrank_cache *cache = data; + if(!user) { + //USER_DOES_NOT_EXIST + reply(cache->textclient, cache->user, "NS_USER_UNKNOWN", cache->nick); + } + else if(!(user->flags & USERFLAG_ISAUTHED)) { + //USER_NOT_AUTHED + reply(cache->textclient, cache->user, "NS_USER_NEED_AUTH", cache->nick); + } + else + neonserv_cmd_assignrank_async1(cache->client, cache->textclient, cache->user, cache->event, user->nick, user->auth, cache->rank_id); + free(cache->nick); + free(cache); +} + +static void neonserv_cmd_assignrank_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct Event *event, char *nick, char *auth, int rank_id) { + //we've got a valid auth now... + MYSQL_RES *res; + MYSQL_ROW row, rank = NULL; + int caccess = 0; + if(rank_id) { + printf_mysql_query("SELECT `rank_access`, `rank_name` FROM `support_ranks` WHERE `rank_id` = '%d'", rank_id); + res = mysql_use(); + rank = mysql_fetch_row(res); + caccess = atoi(rank[0]); + } + printf_mysql_query("SELECT `user_id`, `user_rank` FROM `users` WHERE `user_user` = '%s'", escape_string(auth)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + if(atoi(row[1]) != rank_id) { + printf_mysql_query("UPDATE `users` SET `user_access` = '%d', `user_rank` = '%d' WHERE `user_id` = '%s'", caccess, rank_id, row[0]); + } + } else if(rank_id) { + printf_mysql_query("INSERT INTO `users` (`user_user`, `user_access`, `user_rank`) VALUES ('%s', '%d', '%d')", escape_string(auth), caccess, rank_id); + } + reply(textclient, user, "NS_ASSIGNRANK_DONE", auth, (rank_id ? rank[1] : "*")); + logEvent(event); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_ban.c b/src/modules/NeonServ.mod/cmd_neonserv_ban.c new file mode 100644 index 0000000..2a419eb --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_ban.c @@ -0,0 +1,122 @@ +/* cmd_neonserv_ban.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0-*] nick[,*auth[,*!*@mask[...]]] +*/ +static USERLIST_CALLBACK(neonserv_cmd_ban_userlist_lookup); +static void neonserv_cmd_ban_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *masks); + +struct neonserv_cmd_ban_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + struct Event *event; + char *masks; +}; + +CMD_BIND(neonserv_cmd_ban) { + struct neonserv_cmd_ban_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->event = event; + cache->masks = strdup(merge_argv_char(argv, 0, argc, ',')); + get_userlist_with_invisible(chan, module_id, neonserv_cmd_ban_userlist_lookup, cache); +} + +static USERLIST_CALLBACK(neonserv_cmd_ban_userlist_lookup) { + struct neonserv_cmd_ban_cache *cache = data; + neonserv_cmd_ban_async1(cache->client, cache->textclient, cache->user, chan, cache->event, cache->masks); + free(cache->masks); + free(cache); +} + +static void neonserv_cmd_ban_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *masks) { + int done_masks = 0, provided_masks = 0, skip, match_count, total_match; + char *mask, *nextmask; + char hostmask_buffer[NICKLEN+USERLEN+HOSTLEN+3]; + char usermask[NICKLEN+USERLEN+HOSTLEN+3]; + struct UserNode *cuser; + struct ChanUser *chanuser; + struct ModeBuffer *modeBuf; + modeBuf = initModeBuffer(client, chan); + nextmask = masks; + while((mask = nextmask)) { + nextmask = strstr(mask, ","); + if(nextmask) { + *nextmask = '\0'; + nextmask++; + } + provided_masks++; + skip = 0; + match_count = 0; + mask = make_banmask(mask, hostmask_buffer); + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + cuser = chanuser->user; + sprintf(usermask, "%s!%s@%s", cuser->nick, cuser->ident, cuser->host); + if(!match(mask, usermask)) { + cuser->flags |= USERFLAG_SCRIPTFLAG1; //we mark the user as 'matching' + if(isNetworkService(chanuser->user)) { + reply(textclient, user, "NS_SERVICE_IMMUNE", chanuser->user->nick); + skip = 1; + break; + } + if(cuser == user || ((cuser->flags & USERFLAG_ISAUTHED) && !stricmp(user->auth, cuser->auth))) { + reply(textclient, user, "NS_YOU_PROTECTED"); + skip = 1; + break; + } + if(isUserProtected(chan, cuser, user)) { + reply(textclient, user, "NS_USER_PROTECTED", cuser->nick); + skip = 1; + break; + } + match_count++; + if(match_count > 4 && (match_count * 3) > chan->usercount && !isGodMode(user)) { + skip = 1; + reply(textclient, user, "NS_LAME_MASK", mask); + break; + } + } + } + if(!skip) { + done_masks++; + modeBufferBan(modeBuf, mask); + } + } + total_match = 0; // count all users marked as 'matching' + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + cuser = chanuser->user; + if(cuser->flags & USERFLAG_SCRIPTFLAG1) { + cuser->flags &= ~USERFLAG_SCRIPTFLAG1; + total_match++; + } + } + freeModeBuffer(modeBuf); + if(done_masks == provided_masks) + reply(textclient, user, "NS_BAN_DONE", done_masks, chan->name, total_match); + else + reply(textclient, user, "NS_BAN_FAIL", client->user->nick); + if(done_masks) + logEvent(event); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_bans.c b/src/modules/NeonServ.mod/cmd_neonserv_bans.c new file mode 100644 index 0000000..12f8464 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_bans.c @@ -0,0 +1,75 @@ +/* cmd_neonserv_bans.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0] (optional) mask +*/ +CMD_BIND(neonserv_cmd_bans) { + MYSQL_RES *res; + MYSQL_ROW row; + //ban list + int i, with_expire = 0, cindex = 0; + char triggered_str[MAXLEN], expires_str[MAXLEN]; + struct Table *table; + printf_mysql_query("SELECT `ban_mask`, `user_user`, `ban_triggered`, `ban_timeout`, `ban_reason` FROM `bans` LEFT JOIN `users` ON `ban_owner` = `user_id` WHERE `ban_channel` = '%d'", chan->channel_id); + res = mysql_use(); + table = table_init(5, mysql_num_rows(res) + 1, 0); + char *content[5]; + //add a NULL row (we add values later) + content[0] = NULL; + content[1] = NULL; + content[2] = NULL; + content[3] = NULL; + content[4] = NULL; + table_add(table, content); + while ((row = mysql_fetch_row(res)) != NULL) { + if(argc > 0 && match(argv[0], row[0])) continue; + content[0] = row[0]; + content[1] = row[1]; + content[2] = (strcmp(row[2], "0") ? timeToStr(user, (time(0) - atoi(row[2])), 2, triggered_str) : get_language_string(user, "NS_USERS_SEEN_NEVER")); + if(strcmp(row[3], "0")) { + if(!with_expire) { + //we're using expire times now... + for(i = 0; i < cindex; i++) + table_change_field(table, i+1, 3, get_language_string(user, "NS_USERS_SEEN_NEVER")); + with_expire = 1; + } + content[3] = timeToStr(user, (atoi(row[3]) - time(0)), 2, expires_str); + } else + content[3] = (with_expire ? get_language_string(user, "NS_USERS_SEEN_NEVER") : NULL); + content[4] = row[4]; + cindex++; + table_add(table, content); + } + //now we add the table header + content[0] = get_language_string(user, "NS_BANS_HEADER_MASK"); + content[1] = get_language_string(user, "NS_BANS_HEADER_SETBY"); + content[2] = get_language_string(user, "NS_BANS_HEADER_TRIGGERED"); + content[3] = (with_expire ? get_language_string(user, "NS_BANS_HEADER_EXPIRES") : NULL); + content[4] = get_language_string(user, "NS_BANS_HEADER_REASON"); + table_change(table, 0, content); + char **table_lines = table_end(table); + for(i = 0; i < table->entrys; i++) { + reply(textclient, user, table_lines[i]); + } + if(!cindex) + reply(textclient, user, "NS_TABLE_NONE"); + reply(textclient, user, "NS_TABLE_COUNT", cindex); + table_free(table); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_chanservsync.c b/src/modules/NeonServ.mod/cmd_neonserv_chanservsync.c new file mode 100644 index 0000000..e4e0096 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_chanservsync.c @@ -0,0 +1,270 @@ +/* cmd_neonserv_chanservsync.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0] - botnick +* argv[1] - key +*/ +#define CHANSERVSYNC_END_TIMEOUT 5 + +static void neonserv_cmd_chanservsync_notice_listener(struct UserNode *user, struct UserNode *target, char *message); +static void neonserv_cmd_chanservsync_free_cache(); +static AUTHLOOKUP_CALLBACK(neonserv_cmd_chanservsync_auth_lookup); +static void neonserv_cmd_chanservsync_synchronize_user(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, char *username, int userid, int caccess, time_t seen, int flags, int new); + +struct neonserv_cmd_chanservsync_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + struct ChanNode *chan; + char *botnick; + time_t last_response; +}; + +struct neonserv_cmd_chanservsync_auth_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + struct ChanNode *chan; + int caccess; + time_t seen; + int flags; +}; + +struct neonserv_cmd_chanservsync_cache *neonserv_cmd_chanservsync_used = NULL; +const char* neonserv_cmd_chanservsync_supported[] = {"ChanServ", NULL}; + +CMD_BIND(neonserv_cmd_chanservsync) { + if(neonserv_cmd_chanservsync_used && time(0) - neonserv_cmd_chanservsync_used->last_response < CHANSERVSYNC_END_TIMEOUT) { + reply(textclient, user, "NS_CHANSERVSYNC_INUSE"); + return; + } + if(neonserv_cmd_chanservsync_used) { + neonserv_cmd_chanservsync_free_cache(); + } + char *botnick = "ChanServ"; + char *key = ""; + if(argc) { + if(argv[0][0] == '!') { + key = argv[0]; + } else { + botnick = argv[0]; + if(argc > 1) + key = argv[1]; + } + } + int seed = 0; + char *tmp; + char synckey[18]; + for(tmp = user->auth; *tmp; tmp++) + seed = (seed * 0xEECE66DL ^ ((*tmp << 24) | (*tmp << 16) | (*tmp << 8) | *tmp)); + for(tmp = chan->name; *tmp; tmp++) + seed = (seed * 0xEECE66DL ^ ((*tmp << 24) | (*tmp << 16) | (*tmp << 8) | *tmp)); + for(tmp = botnick; *tmp; tmp++) + seed = (seed * 0xEECE66DL ^ ((*tmp << 24) | (*tmp << 16) | (*tmp << 8) | *tmp)); + sprintf(synckey, "!%08x!", seed); + if(strcmp(synckey, key)) { + int f = 0; + const char **supp = neonserv_cmd_chanservsync_supported; + while(*supp) { + if(!stricmp(*supp, botnick)) { + f = 1; + break; + } + supp++; + } + if(!f) { + reply(textclient, user, "NS_CHANSERVSYNC_UNSUPPORTED", botnick, client->user->nick); + } + reply(textclient, user, "NS_CHANSERVSYNC_KEY", client->user->nick, botnick, botnick, synckey); + return; + } + struct neonserv_cmd_chanservsync_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->chan = chan; + cache->botnick = strdup(botnick); + cache->last_response = time(0); + neonserv_cmd_chanservsync_used = cache; + putsock(client, "PRIVMSG %s :users %s", botnick, chan->name); + bind_privnotice(neonserv_cmd_chanservsync_notice_listener, module_id); + reply(textclient, user, "NS_CHANSERVSYNC_SYNCHRONIZING", chan->name, botnick); + logEvent(event); +} + +static void neonserv_cmd_chanservsync_notice_listener(struct UserNode *user, struct UserNode *target, char *message) { + if(neonserv_cmd_chanservsync_used && neonserv_cmd_chanservsync_used->client->user == target && !stricmp(user->nick, neonserv_cmd_chanservsync_used->botnick)) { + //we've got a notice from our bot... + //let's try parsing it.... + char *p = message; + char *tokens[MAXLEN]; + int tokensPos = 0; + while(*p == ' ') //skip leading spaces (airb0t) + p++; + message = p; + char *q = p; + while(*q) { + if(*q < 32) *q = ' '; + q++; + } + while((q = strstr(p, " "))) { + *q = '\0'; + do { + q++; + } while(*q == ' ' || *q == '-'); + if(*p) { + tokens[tokensPos++] = p; + } + p = q; + } + if(*p) { + tokens[tokensPos++] = p; + } + int caccess; + char *username; + if(tokensPos == 1) { + //maybe a chip-like userlist + if(tokens[0][0] == '@') { + caccess = 200; + username = &tokens[0][1]; + } else if(tokens[0][0] == '+') { + caccess = 100; + username = &tokens[0][1]; + } else + return; + } else if(tokensPos >= 2) { + if(atoi(tokens[0]) > 0) { + caccess = atoi(tokens[0]); + username = tokens[1]; + } else { + caccess = atoi(tokens[1]); + username = tokens[0]; + } + } else + return; + if(caccess < 1 || caccess > 500) return; + int flags = 0; + time_t now = time(0); + time_t seen_time = now; //now - now = 0 (never) + neonserv_cmd_chanservsync_used->last_response = now; + if(strlen(username) < 3) return; + //ok we have access and username... maybe there is something else we can parse??? + char *seen = NULL; + char *status = NULL; + if(tokensPos > 2) { + if(!stricmp("normal", tokens[2]) || !stricmp("suspended", tokens[2]) || !stricmp("bot", tokens[2])) { + status = tokens[2]; + if (tokensPos > 3) { + seen = merge_argv(tokens, 3, tokensPos); + } + } else if (tokensPos > 3) { + if(!stricmp("normal", tokens[tokensPos-1]) || !stricmp("suspended", tokens[tokensPos-1]) || !stricmp("bot", tokens[tokensPos-1])) { + status = tokens[tokensPos-1]; + seen = merge_argv(tokens, 2, tokensPos-1); + } else { + seen = merge_argv(tokens, 2, tokensPos); + } + } else { + seen = merge_argv(tokens, 2, tokensPos); + } + } + if(status && !stricmp(status, "suspended")) { + flags |= DB_CHANUSER_SUSPENDED; + } + if(seen) { + if(!stricmp(seen, "here")) + seen_time = 0; + else if(stricmp(seen, "never")) + seen_time = strToTime(user, seen); + } + seen_time = now - seen_time; + //we've collected all information now. synchronize the user (use the higher access if the user is already added) + MYSQL_RES *res; + MYSQL_ROW row; + int userid; + printf_mysql_query("SELECT `user_id` FROM `users` WHERE `user_user` = '%s'", escape_string(username)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + userid = atoi(row[0]); + neonserv_cmd_chanservsync_synchronize_user(neonserv_cmd_chanservsync_used->client, neonserv_cmd_chanservsync_used->textclient, neonserv_cmd_chanservsync_used->user, neonserv_cmd_chanservsync_used->chan, username, userid, caccess, seen_time, flags, 0); + } else if(!stricmp(user->nick, "chanserv")) { + printf_mysql_query("INSERT INTO `users` (`user_user`) VALUES ('%s')", escape_string(username)); + userid = (int) mysql_insert_id(get_mysql_conn()); + neonserv_cmd_chanservsync_synchronize_user(neonserv_cmd_chanservsync_used->client, neonserv_cmd_chanservsync_used->textclient, neonserv_cmd_chanservsync_used->user, neonserv_cmd_chanservsync_used->chan, username, userid, caccess, seen_time, flags, 1); + } else { + //lookup auth + struct neonserv_cmd_chanservsync_auth_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = neonserv_cmd_chanservsync_used->client; + cache->textclient = neonserv_cmd_chanservsync_used->textclient; + cache->user = neonserv_cmd_chanservsync_used->user; + cache->chan = neonserv_cmd_chanservsync_used->chan; + cache->caccess = caccess; + cache->seen = seen_time; + cache->flags = flags; + lookup_authname(username, module_id, neonserv_cmd_chanservsync_auth_lookup, cache); + } + } +} + +static void neonserv_cmd_chanservsync_free_cache() { + free(neonserv_cmd_chanservsync_used->botnick); + free(neonserv_cmd_chanservsync_used); + unbind_privnotice(neonserv_cmd_chanservsync_notice_listener); + neonserv_cmd_chanservsync_used = NULL; +} + +static AUTHLOOKUP_CALLBACK(neonserv_cmd_chanservsync_auth_lookup) { + struct neonserv_cmd_chanservsync_auth_cache *cache = data; + if(exists) { + printf_mysql_query("INSERT INTO `users` (`user_user`) VALUES ('%s')", escape_string(auth)); + int userid = (int) mysql_insert_id(get_mysql_conn()); + neonserv_cmd_chanservsync_synchronize_user(cache->client, cache->textclient, cache->user, cache->chan, auth, userid, cache->caccess, cache->seen, cache->flags, 1); + } + free(cache); +} + +static void neonserv_cmd_chanservsync_synchronize_user(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, char *username, int userid, int caccess, time_t seen, int flags, int new) { + //just sync the user with the given userid with the providet information + if(caccess == 500) caccess = 499; + if(new) { + //just add + printf_mysql_query("INSERT INTO `chanusers` (`chanuser_cid`, `chanuser_uid`, `chanuser_access`, `chanuser_seen`, `chanuser_flags`) VALUES ('%d', '%d', '%d', '%lu', '%d')", chan->channel_id, userid, caccess, (unsigned long) seen, flags); + } else { + MYSQL_RES *res; + MYSQL_ROW row; + //check if already added + printf_mysql_query("SELECT `chanuser_access`, `chanuser_id`, `chanuser_seen` FROM `chanusers` WHERE `chanuser_cid` = '%d' AND `chanuser_uid` = '%d'", chan->channel_id, userid); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + //clvl + if(atoi(row[0]) >= caccess) return; + if(atol(row[2]) > seen) seen = atol(row[2]); + printf_mysql_query("UPDATE `chanusers` SET `chanuser_access` = '%d', `chanuser_seen` = '%lu' WHERE `chanuser_id` = '%s'", caccess, (unsigned long) seen, row[1]); + } else + printf_mysql_query("INSERT INTO `chanusers` (`chanuser_cid`, `chanuser_uid`, `chanuser_access`, `chanuser_seen`, `chanuser_flags`) VALUES ('%d', '%d', '%d', '%lu', '%d')", chan->channel_id, userid, caccess, (unsigned long) seen, flags); + } + reply(textclient, user, "NS_CHANSERVSYNC_SYNCHRONIZED", username, caccess); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_clvl.c b/src/modules/NeonServ.mod/cmd_neonserv_clvl.c new file mode 100644 index 0000000..159d540 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_clvl.c @@ -0,0 +1,130 @@ +/* cmd_neonserv_clvl.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0] - nick / *auth +* argv[1] - access +*/ +static USERAUTH_CALLBACK(neonserv_cmd_clvl_nick_lookup); +static void neonserv_cmd_clvl_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *nick, char *auth, int caccess); + +struct neonserv_cmd_clvl_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + struct ChanNode *chan; + struct Event *event; + char *nick; + int access; +}; + +CMD_BIND(neonserv_cmd_clvl) { + int caccess; + caccess = atoi(argv[1]); + if(caccess <= 0 || caccess > 500) { + reply(textclient, user, "NS_INVALID_ACCESS", caccess); + return; + } + if(caccess >= getChannelAccess(user, chan)) { + if(isGodMode(user)) { + event->flags |= CMDFLAG_OPLOG; + } else { + reply(textclient, user, "NS_ACCESS_OUTRANKED"); + return; + } + } + if(argv[0][0] == '*') { + //we've got an auth + argv[0]++; + neonserv_cmd_clvl_async1(client, textclient, user, chan, event, argv[0], argv[0], caccess); + } else { + struct UserNode *cuser = getUserByNick(argv[0]); + if(!cuser) { + cuser = createTempUser(argv[0]); + if(!cuser) { + reply(textclient, user, "NS_USER_UNKNOWN", argv[0]); + return; + } + cuser->flags |= USERFLAG_ISTMPUSER; + } + if(cuser->flags & USERFLAG_ISAUTHED) { + neonserv_cmd_clvl_async1(client, textclient, user, chan, event, argv[0], cuser->auth, caccess); + } else { + struct neonserv_cmd_clvl_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->chan = chan; + cache->event = event; + cache->nick = strdup(argv[0]); + cache->access = caccess; + get_userauth(cuser, module_id, neonserv_cmd_clvl_nick_lookup, cache); + } + } +} + +static USERAUTH_CALLBACK(neonserv_cmd_clvl_nick_lookup) { + struct neonserv_cmd_clvl_cache *cache = data; + if(!user) { + //USER_DOES_NOT_EXIST + reply(cache->textclient, cache->user, "NS_USER_UNKNOWN", cache->nick); + } + else if(!(user->flags & USERFLAG_ISAUTHED)) { + //USER_NOT_AUTHED + reply(cache->textclient, cache->user, "NS_USER_NEED_AUTH", cache->nick); + } + else + neonserv_cmd_clvl_async1(cache->client, cache->textclient, cache->user, cache->chan, cache->event, user->nick, user->auth, cache->access); + free(cache->nick); + free(cache); +} + +static void neonserv_cmd_clvl_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *nick, char *auth, int caccess) { + //we've got a valid auth now... + MYSQL_RES *res; + MYSQL_ROW row; + int userid; + printf_mysql_query("SELECT `user_id` FROM `users` WHERE `user_user` = '%s'", escape_string(auth)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + userid = atoi(row[0]); + //check if the user is already added + printf_mysql_query("SELECT `chanuser_access`, `chanuser_id` FROM `chanusers` WHERE `chanuser_cid` = '%d' AND `chanuser_uid` = '%d'", chan->channel_id, userid); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + //clvl + if(atoi(row[0]) >= getChannelAccess(user, chan)) { + if(isGodMode(user)) { + event->flags |= CMDFLAG_OPLOG; + } else { + reply(textclient, user, "NS_USER_OUTRANKED", nick); + return; + } + } + printf_mysql_query("UPDATE `chanusers` SET `chanuser_access` = '%d' WHERE `chanuser_id` = '%s'", caccess, row[1]); + reply(textclient, user, "NS_CLVL_DONE", nick, caccess, chan->name); + logEvent(event); + return; + } + } + reply(textclient, user, "NS_NOT_ON_USERLIST", nick, chan->name); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_csuspend.c b/src/modules/NeonServ.mod/cmd_neonserv_csuspend.c new file mode 100644 index 0000000..929fa4e --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_csuspend.c @@ -0,0 +1,68 @@ +/* cmd_neonserv_csuspend.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0] - channel +*/ +CMD_BIND(neonserv_cmd_csuspend) { + MYSQL_RES *res; + MYSQL_ROW row; + char *channel = argv[0]; + if(!is_valid_chan(channel)) { + reply(textclient, user, "NS_INVALID_CHANNEL_NAME", argv[0]); + return; + } + int chanid; + printf_mysql_query("SELECT `channel_id` FROM `channels` WHERE `channel_name` = '%s'", escape_string(channel)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + chanid = atoi(row[0]); + } else { + reply(textclient, user, "NS_UNREGISTER_NOT_REGISTERED", argv[0], client->user->nick); + return; + } + printf_mysql_query("SELECT `botid`, `bot_channels`.`id`, `suspended` FROM `bot_channels` LEFT JOIN `bots` ON `bot_channels`.`botid` = `bots`.`id` WHERE `chanid` = '%d' AND `botclass` = '%d'", chanid, client->botid); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) == NULL) { + reply(textclient, user, "NS_UNREGISTER_NOT_REGISTERED", argv[0], client->user->nick); + return; + } + if(!strcmp(row[2], "1")) { + reply(textclient, user, "NS_CSUSPEND_ALREADY", channel); + return; + } + int botid = atoi(row[0]); + struct ClientSocket *bot; + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(bot->clientid == botid) + break; + } + if(bot) { + putsock(bot, "PART %s :Channel suspended.", channel); + } + if(client->botid == NEONSERV_BOTID) { + char setting[128]; + sprintf(setting, "modules.%s.auto_backup_unregister", get_module_name(module_id)); + if(get_int_field(setting)) + module_global_cmd_unregister_neonbackup(channel); + } + printf_mysql_query("UPDATE `bot_channels` SET `suspended` = '1' WHERE `id` = '%s'", row[1]); + reply(textclient, user, "NS_CSUSPEND_DONE", channel); + logEvent(event); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_cunsuspend.c b/src/modules/NeonServ.mod/cmd_neonserv_cunsuspend.c new file mode 100644 index 0000000..e2fa17f --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_cunsuspend.c @@ -0,0 +1,68 @@ +/* cmd_neonserv_cunsuspend.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0] - channel +*/ +CMD_BIND(neonserv_cmd_cunsuspend) { + MYSQL_RES *res; + MYSQL_ROW row; + char *channel = argv[0]; + if(!is_valid_chan(channel)) { + reply(textclient, user, "NS_INVALID_CHANNEL_NAME", argv[0]); + return; + } + int chanid; + printf_mysql_query("SELECT `channel_id` FROM `channels` WHERE `channel_name` = '%s'", escape_string(channel)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + chanid = atoi(row[0]); + } else { + reply(textclient, user, "NS_UNREGISTER_NOT_REGISTERED", argv[0], client->user->nick); + return; + } + printf_mysql_query("SELECT `botid`, `bot_channels`.`id`, `suspended` FROM `bot_channels` LEFT JOIN `bots` ON `bot_channels`.`botid` = `bots`.`id` WHERE `chanid` = '%d' AND `botclass` = '%d'", chanid, client->botid); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) == NULL) { + reply(textclient, user, "NS_UNREGISTER_NOT_REGISTERED", argv[0], client->user->nick); + return; + } + if(!strcmp(row[2], "0")) { + reply(textclient, user, "NS_CUNSUSPEND_NOT", channel); + return; + } + int botid = atoi(row[0]); + struct ClientSocket *bot; + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(bot->clientid == botid) + break; + } + if(bot) { + putsock(bot, "JOIN %s", channel); + } + if(client->botid == NEONSERV_BOTID) { + char setting[128]; + sprintf(setting, "modules.%s.auto_backup_register", get_module_name(module_id)); + if(get_int_field(setting)) + module_global_cmd_register_neonbackup(channel); + } + printf_mysql_query("UPDATE `bot_channels` SET `suspended` = '0' WHERE `id` = '%s'", row[1]); + reply(textclient, user, "NS_CUNSUSPEND_DONE", channel); + logEvent(event); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_dehalfop.c b/src/modules/NeonServ.mod/cmd_neonserv_dehalfop.c new file mode 100644 index 0000000..2882180 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_dehalfop.c @@ -0,0 +1,94 @@ +/* cmd_neonserv_dehalfop.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0-*] nicks +*/ +static USERLIST_CALLBACK(neonserv_cmd_dehalfop_userlist_lookup); +static void neonserv_cmd_dehalfop_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char **argv, int argc); + +struct neonserv_cmd_dehalfop_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + struct Event *event; + char **argv; + int argc; +}; + +CMD_BIND(neonserv_cmd_dehalfop) { + struct neonserv_cmd_dehalfop_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->event = event; + cache->argv = calloc(argc, sizeof(char*)); + int i; + for(i = 0; i < argc; i++) { + cache->argv[i] = strdup(argv[i]); + } + cache->argc = argc; + get_userlist(chan, module_id, neonserv_cmd_dehalfop_userlist_lookup, cache); +} + +static USERLIST_CALLBACK(neonserv_cmd_dehalfop_userlist_lookup) { + struct neonserv_cmd_dehalfop_cache *cache = data; + neonserv_cmd_dehalfop_async1(cache->client, cache->textclient, cache->user, chan, cache->event, cache->argv, cache->argc); + int i; + for(i = 0; i < cache->argc; i++) { + free(cache->argv[i]); + } + free(cache->argv); + free(cache); +} + +static void neonserv_cmd_dehalfop_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char **argv, int argc) { + int i, done_users = 0; + struct UserNode *cuser; + struct ChanUser *chanuser; + struct ModeBuffer *modeBuf; + modeBuf = initModeBuffer(client, chan); + for(i = 0; i < argc; i++) { + cuser = searchUserByNick(argv[i]); + if(!cuser) continue; + chanuser = getChanUser(cuser, chan); + if(!chanuser) continue; + if(isNetworkService(cuser)) { + reply(textclient, user, "NS_SERVICE_IMMUNE", cuser->nick); + continue; + } + if(isUserProtected(chan, cuser, user)) { + reply(textclient, user, "NS_USER_PROTECTED", cuser->nick); + continue; + } + done_users++; + if(!(chanuser->flags & CHANUSERFLAG_OPPED)) continue; + modeBufferDehalfop(modeBuf, argv[i]); + } + freeModeBuffer(modeBuf); + if(done_users == argc) + reply(textclient, user, "NS_DEHALFOP_DONE", chan->name); + else + reply(textclient, user, "NS_DEHALFOP_FAIL", client->user->nick); + if(done_users) + logEvent(event); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_dehalfopall.c b/src/modules/NeonServ.mod/cmd_neonserv_dehalfopall.c new file mode 100644 index 0000000..ce948ac --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_dehalfopall.c @@ -0,0 +1,85 @@ +/* cmd_neonserv_dehalfopall.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0] (optional) nick mask +*/ +static USERLIST_CALLBACK(neonserv_cmd_dehalfopall_userlist_lookup); +static void neonserv_cmd_dehalfopall_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char **argv, int argc); + +struct neonserv_cmd_dehalfopall_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + struct Event *event; + char **argv; + int argc; +}; + +CMD_BIND(neonserv_cmd_dehalfopall) { + struct neonserv_cmd_dehalfopall_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->event = event; + cache->argv = calloc(argc, sizeof(char*)); + int i; + for(i = 0; i < argc; i++) { + cache->argv[i] = strdup(argv[i]); + } + cache->argc = argc; + get_userlist(chan, module_id, neonserv_cmd_dehalfopall_userlist_lookup, cache); +} + +static USERLIST_CALLBACK(neonserv_cmd_dehalfopall_userlist_lookup) { + struct neonserv_cmd_dehalfopall_cache *cache = data; + neonserv_cmd_dehalfopall_async1(cache->client, cache->textclient, cache->user, chan, cache->event, cache->argv, cache->argc); + int i; + for(i = 0; i < cache->argc; i++) { + free(cache->argv[i]); + } + free(cache->argv); + free(cache); +} + +static void neonserv_cmd_dehalfopall_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char **argv, int argc) { + int issuer_access, victim_access, done_users = 0; + char *nickmask = NULL; + struct ChanUser *chanuser; + struct ModeBuffer *modeBuf; + if(argc > 0) + nickmask = argv[0]; + modeBuf = initModeBuffer(client, chan); + issuer_access = getChannelAccess(user, chan); + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + if(nickmask && match(nickmask, chanuser->user->nick)) continue; + victim_access = getChannelAccess(chanuser->user, chan); + if(victim_access >= issuer_access || isNetworkService(chanuser->user)) continue; + if(!(chanuser->flags & CHANUSERFLAG_OPPED)) continue; + modeBufferDehalfop(modeBuf, chanuser->user->nick); + done_users++; + } + freeModeBuffer(modeBuf); + reply(textclient, user, "NS_DEHALFOPALL_DONE", done_users, chan->name); + if(done_users) + logEvent(event); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_delban.c b/src/modules/NeonServ.mod/cmd_neonserv_delban.c new file mode 100644 index 0000000..0086b9c --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_delban.c @@ -0,0 +1,51 @@ +/* cmd_neonserv_delban.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0] nick|*auth|*!*@mask +*/ + +CMD_BIND(neonserv_cmd_delban) { + char hostmask_buffer[NICKLEN+USERLEN+HOSTLEN+3]; + char *mask = make_banmask(argv[0], hostmask_buffer); + int matching_bans = 0; + MYSQL_RES *res; + MYSQL_ROW row; + //check if the provided mask affects any existing bans + char nameBuf[20]; + printf_mysql_query("SELECT `ban_mask`, `ban_id`, `ban_timeout` FROM `bans` WHERE `ban_channel` = '%d'", chan->channel_id); + res = mysql_use(); + while ((row = mysql_fetch_row(res)) != NULL) { + if(!match(mask, row[0])) { + //remove the ban + if(strcmp(row[2], "0")) { + sprintf(nameBuf, "ban_%s", row[1]); + timeq_del_name(nameBuf); + } + printf_mysql_query("DELETE FROM `bans` WHERE `ban_id` = '%s'", row[1]); + matching_bans++; + } + } + if(matching_bans) { + putsock(client, "MODE %s -b %s", chan->name, mask); + reply(textclient, user, "NS_DELBAN_DONE", mask, chan->name); + logEvent(event); + } else + reply(textclient, user, "NS_DELBAN_FAIL", mask); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_delme.c b/src/modules/NeonServ.mod/cmd_neonserv_delme.c new file mode 100644 index 0000000..29f4582 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_delme.c @@ -0,0 +1,62 @@ +/* cmd_neonserv_delme.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0] - key +*/ + +CMD_BIND(neonserv_cmd_delme) { + MYSQL_RES *res; + MYSQL_ROW row; + int userid; + printf_mysql_query("SELECT `user_id` FROM `users` WHERE `user_user` = '%s'", escape_string(user->auth)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + userid = atoi(row[0]); + //check if the user is added + printf_mysql_query("SELECT `chanuser_access`, `chanuser_id` FROM `chanusers` WHERE `chanuser_cid` = '%d' AND `chanuser_uid` = '%d'", chan->channel_id, userid); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + if(atoi(row[0]) == 500) { + reply(textclient, user, "NS_DELME_OWNER", chan->name); + return; + } + //check key + int seed = 0; + char *tmp; + static char unregkey[16]; + for(tmp = user->auth; *tmp; tmp++) + seed = (seed * 0xEECE66DL ^ ((*tmp << 24) | (*tmp << 16) | (*tmp << 8) | *tmp)); + for(tmp = chan->name; *tmp; tmp++) + seed = (seed * 0xEECE66DL ^ ((*tmp << 24) | (*tmp << 16) | (*tmp << 8) | *tmp)); + sprintf(unregkey, "%08x", seed); + if(argc < 1 || strcmp(argv[0], unregkey)) { + reply(textclient, user, "NS_DELME_KEY", unregkey); + return; + } else { + //delete + printf_mysql_query("DELETE FROM `chanusers` WHERE `chanuser_id` = '%s'", row[1]); + reply(textclient, user, "NS_DELME_DONE", atoi(row[0]), chan->name); + logEvent(event); + return; + } + } + } + reply(textclient, user, "NS_NOT_ON_USERLIST_YOU", chan->name); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_delrank.c b/src/modules/NeonServ.mod/cmd_neonserv_delrank.c new file mode 100644 index 0000000..cf1d3ec --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_delrank.c @@ -0,0 +1,37 @@ +/* cmd_neonserv_delrank.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0-*] rank name +*/ + +CMD_BIND(neonserv_cmd_delrank) { + char *name = merge_argv(argv, 0, argc); + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `rank_id`, `rank_name` FROM `support_ranks` WHERE `rank_name` = '%s'", escape_string(name)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) == NULL) { + reply(textclient, user, "NS_DELRANK_NOT_FOUND", name); + return; + } + printf_mysql_query("UPDATE `users` SET `user_rank` = '0', `user_access` = '0' WHERE `user_rank` = '%s'", row[0]); + printf_mysql_query("DELETE FROM `support_ranks` WHERE `rank_id` = '%s'", row[0]); + reply(textclient, user, "NS_DELRANK_DELETED", row[1]); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_deluser.c b/src/modules/NeonServ.mod/cmd_neonserv_deluser.c new file mode 100644 index 0000000..e283f18 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_deluser.c @@ -0,0 +1,113 @@ +/* cmd_neonserv_deluser.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0] - nick / *auth +*/ +static USERAUTH_CALLBACK(neonserv_cmd_deluser_nick_lookup); +static void neonserv_cmd_deluser_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *nick, char *auth); + +struct neonserv_cmd_deluser_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + struct ChanNode *chan; + struct Event *event; + char *nick; +}; + +CMD_BIND(neonserv_cmd_deluser) { + if(argv[0][0] == '*') { + //we've got an auth + argv[0]++; + neonserv_cmd_deluser_async1(client, textclient, user, chan, event, argv[0], argv[0]); + } else { + struct UserNode *cuser = getUserByNick(argv[0]); + if(!cuser) { + cuser = createTempUser(argv[0]); + if(!cuser) { + reply(textclient, user, "NS_USER_UNKNOWN", argv[0]); + return; + } + cuser->flags |= USERFLAG_ISTMPUSER; + } + if(cuser->flags & USERFLAG_ISAUTHED) { + neonserv_cmd_deluser_async1(client, textclient, user, chan, event, argv[0], cuser->auth); + } else { + struct neonserv_cmd_deluser_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->chan = chan; + cache->event = event; + cache->nick = strdup(argv[0]); + get_userauth(cuser, module_id, neonserv_cmd_deluser_nick_lookup, cache); + } + } +} + +static USERAUTH_CALLBACK(neonserv_cmd_deluser_nick_lookup) { + struct neonserv_cmd_deluser_cache *cache = data; + if(!user) { + //USER_DOES_NOT_EXIST + reply(cache->textclient, cache->user, "NS_USER_UNKNOWN", cache->nick); + } + else if(!(user->flags & USERFLAG_ISAUTHED)) { + //USER_NOT_AUTHED + reply(cache->textclient, cache->user, "NS_USER_NEED_AUTH", cache->nick); + } + else + neonserv_cmd_deluser_async1(cache->client, cache->textclient, cache->user, cache->chan, cache->event, user->nick, user->auth); + free(cache->nick); + free(cache); +} + +static void neonserv_cmd_deluser_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *nick, char *auth) { + //we've got a valid auth now... + MYSQL_RES *res; + MYSQL_ROW row; + int userid; + printf_mysql_query("SELECT `user_id` FROM `users` WHERE `user_user` = '%s'", escape_string(auth)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + userid = atoi(row[0]); + //check if the user is already added + printf_mysql_query("SELECT `chanuser_access`, `chanuser_id` FROM `chanusers` WHERE `chanuser_cid` = '%d' AND `chanuser_uid` = '%d'", chan->channel_id, userid); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + if(atoi(row[0]) >= getChannelAccess(user, chan)) { + if(isGodMode(user)) { + event->flags |= CMDFLAG_OPLOG; + } else { + reply(textclient, user, "NS_USER_OUTRANKED", nick); + return; + } + } + //delete + printf_mysql_query("DELETE FROM `chanusers` WHERE `chanuser_id` = '%s'", row[1]); + reply(textclient, user, "NS_DELUSER_DONE", nick, atoi(row[0]), chan->name); + logEvent(event); + return; + } + } + reply(textclient, user, "NS_NOT_ON_USERLIST", nick, chan->name); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_deop.c b/src/modules/NeonServ.mod/cmd_neonserv_deop.c new file mode 100644 index 0000000..d030b8e --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_deop.c @@ -0,0 +1,94 @@ +/* cmd_neonserv_deop.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0-*] nicks +*/ +static USERLIST_CALLBACK(neonserv_cmd_deop_userlist_lookup); +static void neonserv_cmd_deop_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char **argv, int argc); + +struct neonserv_cmd_deop_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + struct Event *event; + char **argv; + int argc; +}; + +CMD_BIND(neonserv_cmd_deop) { + struct neonserv_cmd_deop_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->event = event; + cache->argv = calloc(argc, sizeof(char*)); + int i; + for(i = 0; i < argc; i++) { + cache->argv[i] = strdup(argv[i]); + } + cache->argc = argc; + get_userlist(chan, module_id, neonserv_cmd_deop_userlist_lookup, cache); +} + +static USERLIST_CALLBACK(neonserv_cmd_deop_userlist_lookup) { + struct neonserv_cmd_deop_cache *cache = data; + neonserv_cmd_deop_async1(cache->client, cache->textclient, cache->user, chan, cache->event, cache->argv, cache->argc); + int i; + for(i = 0; i < cache->argc; i++) { + free(cache->argv[i]); + } + free(cache->argv); + free(cache); +} + +static void neonserv_cmd_deop_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char **argv, int argc) { + int i, done_users = 0; + struct UserNode *cuser; + struct ChanUser *chanuser; + struct ModeBuffer *modeBuf; + modeBuf = initModeBuffer(client, chan); + for(i = 0; i < argc; i++) { + cuser = searchUserByNick(argv[i]); + if(!cuser) continue; + chanuser = getChanUser(cuser, chan); + if(!chanuser) continue; + if(isNetworkService(cuser)) { + reply(textclient, user, "NS_SERVICE_IMMUNE", cuser->nick); + continue; + } + if(isUserProtected(chan, cuser, user)) { + reply(textclient, user, "NS_USER_PROTECTED", cuser->nick); + continue; + } + done_users++; + if(!(chanuser->flags & CHANUSERFLAG_OPPED)) continue; + modeBufferDeop(modeBuf, argv[i]); + } + freeModeBuffer(modeBuf); + if(done_users == argc) + reply(textclient, user, "NS_DEOP_DONE", chan->name); + else + reply(textclient, user, "NS_DEOP_FAIL", client->user->nick); + if(done_users) + logEvent(event); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_deopall.c b/src/modules/NeonServ.mod/cmd_neonserv_deopall.c new file mode 100644 index 0000000..0299a8b --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_deopall.c @@ -0,0 +1,85 @@ +/* cmd_neonserv_deopall.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0] (optional) nick mask +*/ +static USERLIST_CALLBACK(neonserv_cmd_deopall_userlist_lookup); +static void neonserv_cmd_deopall_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char **argv, int argc); + +struct neonserv_cmd_deopall_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + struct Event *event; + char **argv; + int argc; +}; + +CMD_BIND(neonserv_cmd_deopall) { + struct neonserv_cmd_deopall_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->event = event; + cache->argv = calloc(argc, sizeof(char*)); + int i; + for(i = 0; i < argc; i++) { + cache->argv[i] = strdup(argv[i]); + } + cache->argc = argc; + get_userlist(chan, module_id, neonserv_cmd_deopall_userlist_lookup, cache); +} + +static USERLIST_CALLBACK(neonserv_cmd_deopall_userlist_lookup) { + struct neonserv_cmd_deopall_cache *cache = data; + neonserv_cmd_deopall_async1(cache->client, cache->textclient, cache->user, chan, cache->event, cache->argv, cache->argc); + int i; + for(i = 0; i < cache->argc; i++) { + free(cache->argv[i]); + } + free(cache->argv); + free(cache); +} + +static void neonserv_cmd_deopall_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char **argv, int argc) { + int issuer_access, victim_access, done_users = 0; + char *nickmask = NULL; + struct ChanUser *chanuser; + struct ModeBuffer *modeBuf; + if(argc > 0) + nickmask = argv[0]; + modeBuf = initModeBuffer(client, chan); + issuer_access = getChannelAccess(user, chan); + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + if(nickmask && match(nickmask, chanuser->user->nick)) continue; + victim_access = getChannelAccess(chanuser->user, chan); + if(victim_access >= issuer_access || isNetworkService(chanuser->user)) continue; + if(!(chanuser->flags & CHANUSERFLAG_OPPED)) continue; + modeBufferDeop(modeBuf, chanuser->user->nick); + done_users++; + } + freeModeBuffer(modeBuf); + reply(textclient, user, "NS_DEOPALL_DONE", done_users, chan->name); + if(done_users) + logEvent(event); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_devoice.c b/src/modules/NeonServ.mod/cmd_neonserv_devoice.c new file mode 100644 index 0000000..1c25fa5 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_devoice.c @@ -0,0 +1,50 @@ +/* cmd_neonserv_devoice.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0-*] nicks +*/ + +CMD_BIND(neonserv_cmd_devoice) { + int i, done_users = 0; + struct UserNode *cuser; + struct ChanUser *chanuser; + struct ModeBuffer *modeBuf; + modeBuf = initModeBuffer(client, chan); + for(i = 0; i < argc; i++) { + cuser = searchUserByNick(argv[i]); + if(!cuser) continue; + chanuser = getChanUser(cuser, chan); + if(!chanuser) continue; + if(isUserProtected(chan, cuser, user)) { + reply(textclient, user, "NS_USER_PROTECTED", cuser->nick); + continue; + } + done_users++; + if(!(chanuser->flags & CHANUSERFLAG_VOICED)) continue; + modeBufferDevoice(modeBuf, argv[i]); + } + freeModeBuffer(modeBuf); + if(done_users == argc) + reply(textclient, user, "NS_DEVOICE_DONE", chan->name); + else + reply(textclient, user, "NS_DEVOICE_FAIL", client->user->nick); + if(done_users) + logEvent(event); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_devoiceall.c b/src/modules/NeonServ.mod/cmd_neonserv_devoiceall.c new file mode 100644 index 0000000..cc96cc3 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_devoiceall.c @@ -0,0 +1,45 @@ +/* cmd_neonserv_devoiceall.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0] (optional) nick mask +*/ + +CMD_BIND(neonserv_cmd_devoiceall) { + int issuer_access, victim_access, done_users = 0; + char *nickmask = NULL; + struct ChanUser *chanuser; + struct ModeBuffer *modeBuf; + if(argc > 0) + nickmask = argv[0]; + modeBuf = initModeBuffer(client, chan); + issuer_access = getChannelAccess(user, chan); + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + if(nickmask && match(nickmask, chanuser->user->nick)) continue; + victim_access = getChannelAccess(chanuser->user, chan); + if(victim_access >= issuer_access) continue; + if(!(chanuser->flags & CHANUSERFLAG_VOICED)) continue; + modeBufferDevoice(modeBuf, chanuser->user->nick); + done_users++; + } + freeModeBuffer(modeBuf); + reply(textclient, user, "NS_DEVOICEALL_DONE", done_users, chan->name); + if(done_users) + logEvent(event); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_down.c b/src/modules/NeonServ.mod/cmd_neonserv_down.c new file mode 100644 index 0000000..76897be --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_down.c @@ -0,0 +1,38 @@ +/* cmd_neonserv_down.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* no arguments +*/ + +CMD_BIND(neonserv_cmd_down) { + struct ChanUser *chanuser = getChanUser(user, chan); + if(!chanuser) { + reply(textclient, user, "NS_NOT_ON_CHANNEL_YOU", chan->name); + return; + } + if((chanuser->flags & CHANUSERFLAG_OPPED)) { + putsock(client, "MODE %s -ov %s %s", chan->name, user->nick, user->nick); + logEvent(event); + } else if((chanuser->flags & CHANUSERFLAG_VOICED)) { + putsock(client, "MODE %s -v %s", chan->name, user->nick); + logEvent(event); + } else + reply(textclient, user, "NS_DOWN_ALREADY", chan->name); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_downall.c b/src/modules/NeonServ.mod/cmd_neonserv_downall.c new file mode 100644 index 0000000..200eb29 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_downall.c @@ -0,0 +1,50 @@ +/* cmd_neonserv_downall.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* no arguments +*/ + +CMD_BIND(neonserv_cmd_downall) { + struct ChanUser *chanuser; + int botid = client->botid; + for(chanuser = getUserChannels(user, NULL); chanuser; chanuser = getUserChannels(user, chanuser)) { + chan = chanuser->chan; + loadChannelSettings(chan); + if(!(chan->flags & CHANFLAG_CHAN_REGISTERED)) continue; + printf_mysql_query("SELECT `botid` FROM `bot_channels` LEFT JOIN `bots` ON `bot_channels`.`botid` = `bots`.`id` WHERE `chanid` = '%d' AND `botclass` = '%d'", chan->channel_id, client->botid); + if (mysql_fetch_row(mysql_use()) == NULL) continue; + int done = 0; + client = getChannelBot(chan, botid); + if(!client) continue; + if((chanuser->flags & CHANUSERFLAG_OPPED)) { + putsock(client, "MODE %s -o %s", chan->name, user->nick); + done = 1; + } + if((chanuser->flags & CHANUSERFLAG_VOICED)) { + putsock(client, "MODE %s -v %s", chan->name, user->nick); + done = 1; + } + if(done) { + //event hack + event->chan = chan; + logEvent(event); + } + } +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_events.c b/src/modules/NeonServ.mod/cmd_neonserv_events.c new file mode 100644 index 0000000..84312a9 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_events.c @@ -0,0 +1,57 @@ +/* cmd_neonserv_events.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0] time +* argv[1-*] match +*/ + +CMD_BIND(neonserv_cmd_events) { + char *str_match; + int duration = (argc ? strToTime(user, argv[0]) : 0); + if(argc > (duration ? 1 : 0)) + str_match = merge_argv(argv, (duration ? 1 : 0), argc); + else + str_match = ""; + if(!duration) duration = (60*60*24); + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `time`, `auth`, `nick`, `command` FROM `events` WHERE `cid` = '%d' AND `time` > '%lu' ORDER BY `time` ASC", chan->channel_id, ((unsigned long) time(0) - duration)); + res = mysql_use(); + int skip = mysql_num_rows(res) - 100; + int count = 0; + char timeBuf[50]; + struct tm *timeinfo; + time_t event_time; + if(skip < 0) skip = 0; + reply(textclient, user, "NS_EVENTS_HEADER"); + while ((row = mysql_fetch_row(res)) != NULL) { + if(skip) { + skip--; + continue; + } + if(*str_match && match(str_match, row[3])) continue; + count++; + event_time = (time_t) atol(row[0]); + timeinfo = localtime(&event_time); + strftime(timeBuf, 80, "%X %x", timeinfo); + reply(textclient, user, "[%s] [%s:%s]: %s", timeBuf, row[2], row[1], row[3]); + } + reply(textclient, user, "NS_TABLE_COUNT", count); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_giveowner.c b/src/modules/NeonServ.mod/cmd_neonserv_giveowner.c new file mode 100644 index 0000000..67b1463 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_giveowner.c @@ -0,0 +1,157 @@ +/* cmd_neonserv_giveowner.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0] - nick / *auth +* argv[1] - key +*/ + +#define GIVEOWNER_TIMEOUT 86400 /* 60*60*24 = 86400 */ + +static USERAUTH_CALLBACK(neonserv_cmd_giveowner_nick_lookup); +static void neonserv_cmd_giveowner_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *nick, char *auth, char *key); + +struct neonserv_cmd_giveowner_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + struct ChanNode *chan; + struct Event *event; + char *nick; + char *key; +}; + +CMD_BIND(neonserv_cmd_giveowner) { + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `channel_lastgiveowner` FROM `channels` WHERE `channel_id` = '%d'", chan->channel_id); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) == NULL) return; + if(strcmp(row[0], "0") && (atoi(row[0]) + GIVEOWNER_TIMEOUT) > time(0)) { + char timeBuf[MAXLEN]; + reply(textclient, user, "NS_GIVEOWNER_TIMEOUT", timeToStr(user, (GIVEOWNER_TIMEOUT - (time(0) - atoi(row[0]))), 2, timeBuf), chan->name); + return; + } + if(argv[0][0] == '*') { + //we've got an auth + argv[0]++; + neonserv_cmd_giveowner_async1(client, textclient, user, chan, event, argv[0], argv[0], (argc != 1 ? argv[1] : NULL)); + } else { + struct UserNode *cuser = getUserByNick(argv[0]); + if(!cuser) { + cuser = createTempUser(argv[0]); + if(!cuser) { + reply(textclient, user, "NS_USER_UNKNOWN", argv[0]); + return; + } + cuser->flags |= USERFLAG_ISTMPUSER; + } + if(cuser->flags & USERFLAG_ISAUTHED) { + neonserv_cmd_giveowner_async1(client, textclient, user, chan, event, argv[0], cuser->auth, (argc != 1 ? argv[1] : NULL)); + } else { + struct neonserv_cmd_giveowner_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->chan = chan; + cache->event = event; + cache->nick = strdup(argv[0]); + cache->key = (argc != 1 ? strdup(argv[1]) : NULL); + get_userauth(cuser, module_id, neonserv_cmd_giveowner_nick_lookup, cache); + } + } +} + +static USERAUTH_CALLBACK(neonserv_cmd_giveowner_nick_lookup) { + struct neonserv_cmd_giveowner_cache *cache = data; + if(!user) { + //USER_DOES_NOT_EXIST + reply(cache->textclient, cache->user, "NS_USER_UNKNOWN", cache->nick); + } + else if(!(user->flags & USERFLAG_ISAUTHED)) { + //USER_NOT_AUTHED + reply(cache->textclient, cache->user, "NS_USER_NEED_AUTH", cache->nick); + } + else + neonserv_cmd_giveowner_async1(cache->client, cache->textclient, cache->user, cache->chan, cache->event, user->nick, user->auth, cache->key); + free(cache->nick); + if(cache->key) + free(cache->key); + free(cache); +} + +static void neonserv_cmd_giveowner_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *nick, char *auth, char *key) { + //we've got a valid auth now... + if(!stricmp(user->auth, auth)) { + reply(textclient, user, "NS_GIVEOWNER_SELF"); + return; + } + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `user_user`, `dnr_timeout`, `dnr_reason`, `dnr_id` FROM `donotregister` LEFT JOIN `users` ON `dnr_user` = `user_id` WHERE `dnr_target` = '%s'", escape_string(auth)); + res = mysql_use(); + if((row = mysql_fetch_row(res)) != NULL) { + int expire_time = atoi(row[1]); + if(expire_time) { + if(expire_time - time(0) <= 0) { + printf_mysql_query("DELETE FROM `donotregister` WHERE `dnr_id` = '%s'", row[3]); + } else { + reply(textclient, user, "NS_DNR_SET_ANONYM", auth); + return; + } + } else { + reply(textclient, user, "NS_DNR_SET_ANONYM", auth); + return; + } + } + int userid; + printf_mysql_query("SELECT `user_id` FROM `users` WHERE `user_user` = '%s'", escape_string(auth)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + userid = atoi(row[0]); + //check if the user is already added + printf_mysql_query("SELECT `chanuser_access`, `chanuser_id` FROM `chanusers` WHERE `chanuser_cid` = '%d' AND `chanuser_uid` = '%d'", chan->channel_id, userid); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + int seed = 0; + char *tmp; + char giveownerkey[16]; + for(tmp = user->auth; *tmp; tmp++) + seed = (seed * 0xEECE66DL ^ ((*tmp << 24) | (*tmp << 16) | (*tmp << 8) | *tmp)); + for(tmp = chan->name; *tmp; tmp++) + seed = (seed * 0xEECE66DL ^ ((*tmp << 24) | (*tmp << 16) | (*tmp << 8) | *tmp)); + sprintf(giveownerkey, "%08x", seed); + if(key && !stricmp(giveownerkey, key)) { + //give ownership + printf_mysql_query("UPDATE `chanusers` SET `chanuser_access` = '500' WHERE `chanuser_id` = '%s'", row[1]); + printf_mysql_query("UPDATE `chanusers` SET `chanuser_access` = '499' WHERE `chanuser_cid` = '%d' AND `chanuser_uid` = (SELECT `user_id` FROM `users` WHERE `user_user` = '%s')", chan->channel_id, escape_string(user->auth)); + printf_mysql_query("INSERT INTO `owner_history` (`owner_history_cid`, `owner_history_from_uid`, `owner_history_to_uid`, `owner_history_time`) VALUE ('%d', (SELECT `user_id` FROM `users` WHERE `user_user` = '%s'), '%d', UNIX_TIMESTAMP())", chan->channel_id, escape_string(user->auth), userid); + reply(textclient, user, "NS_GIVEOWNER_DONE", chan->name, auth); + logEvent(event); + } else { + reply(textclient, user, "NS_GIVEOWNER_CONFIRM", auth, giveownerkey); + } + return; + } + } + reply(textclient, user, "NS_NOT_ON_USERLIST", nick, chan->name); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_halfop.c b/src/modules/NeonServ.mod/cmd_neonserv_halfop.c new file mode 100644 index 0000000..8c83389 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_halfop.c @@ -0,0 +1,93 @@ +/* cmd_neonserv_halfop.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0-*] nicks +*/ +static USERLIST_CALLBACK(neonserv_cmd_halfop_userlist_lookup); +static void neonserv_cmd_halfop_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *nicks); + +struct neonserv_cmd_halfop_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + struct Event *event; + char *nicks; +}; + +CMD_BIND(neonserv_cmd_halfop) { + struct neonserv_cmd_halfop_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->event = event; + cache->nicks = strdup(merge_argv(argv, 0, argc)); + get_userlist_if_invisible(chan, module_id, neonserv_cmd_halfop_userlist_lookup, cache); +} + +static USERLIST_CALLBACK(neonserv_cmd_halfop_userlist_lookup) { + struct neonserv_cmd_halfop_cache *cache = data; + neonserv_cmd_halfop_async1(cache->client, cache->textclient, cache->user, chan, cache->event, cache->nicks); + free(cache->nicks); + free(cache); +} + +static void neonserv_cmd_halfop_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *nicks) { + int total_users = 0, done_users = 0; + struct UserNode *cuser; + struct ChanUser *chanuser; + struct ModeBuffer *modeBuf; + modeBuf = initModeBuffer(client, chan); + char *a, *b = nicks; + do { + a = strstr(b, " "); + if(a) *a = '\0'; + total_users++; + cuser = searchUserByNick(b); + if(!cuser) { + //check for an invisible user + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + if(!stricmp(chanuser->user->nick, b)) { + cuser = chanuser->user; + break; + } + } + if(!cuser) continue; + } else { + chanuser = getChanUser(cuser, chan); + if(!chanuser) continue; + } + done_users++; + if(chanuser->flags & CHANUSERFLAG_OPPED) continue; + modeBufferHalfop(modeBuf, b); + if(a) { + b = a+1; + } + } while(a); + freeModeBuffer(modeBuf); + if(done_users == total_users) + reply(textclient, user, "NS_HALFOP_DONE", chan->name); + else + reply(textclient, user, "NS_HALFOP_FAIL", client->user->nick); + if(done_users) + logEvent(event); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_halfopall.c b/src/modules/NeonServ.mod/cmd_neonserv_halfopall.c new file mode 100644 index 0000000..5ce22e8 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_halfopall.c @@ -0,0 +1,74 @@ +/* cmd_neonserv_halfopall.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0] "force" +* argv[1] (optional) nick mask +*/ +static USERLIST_CALLBACK(neonserv_cmd_halfopall_userlist_lookup); +static void neonserv_cmd_halfopall_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *nickmask); + +struct neonserv_cmd_halfopall_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + struct Event *event; + char *nickmask; +}; + +CMD_BIND(neonserv_cmd_halfopall) { + struct neonserv_cmd_halfopall_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->event = event; + if(argc) { + cache->nickmask = strdup(argv[0]); + } else + cache->nickmask = NULL; + get_userlist_if_invisible(chan, module_id, neonserv_cmd_halfopall_userlist_lookup, cache); +} + +static USERLIST_CALLBACK(neonserv_cmd_halfopall_userlist_lookup) { + struct neonserv_cmd_halfopall_cache *cache = data; + neonserv_cmd_halfopall_async1(cache->client, cache->textclient, cache->user, chan, cache->event, cache->nickmask); + if(cache->nickmask) + free(cache->nickmask); + free(cache); +} + +static void neonserv_cmd_halfopall_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *nickmask) { + int done_users = 0; + struct ChanUser *chanuser; + struct ModeBuffer *modeBuf; + modeBuf = initModeBuffer(client, chan); + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + if(nickmask && match(nickmask, chanuser->user->nick)) continue; + if(chanuser->flags & CHANUSERFLAG_OPPED) continue; + modeBufferHalfop(modeBuf, chanuser->user->nick); + done_users++; + } + freeModeBuffer(modeBuf); + reply(textclient, user, "NS_HALFOPALL_DONE", done_users, chan->name); + if(done_users) + logEvent(event); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_help.c b/src/modules/NeonServ.mod/cmd_neonserv_help.c new file mode 100644 index 0000000..1d6ce16 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_help.c @@ -0,0 +1,97 @@ +/* cmd_neonserv_help.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0-*] index +*/ + +CMD_BIND(neonserv_cmd_help) { + char *ident; + if(argc) + ident = merge_argv(argv, 0, argc); + else + ident = "0"; + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `user_lang` FROM `users` WHERE `user_user` = '%s'", escape_string(user->auth)); + res = mysql_use(); + char *lang; + if ((row = mysql_fetch_row(res)) != NULL) + lang = row[0]; + else + lang = "en"; + printf_mysql_query("SELECT `text` FROM `help` WHERE `lang` = '%s' AND `ident` = '%s'", escape_string(lang), escape_string(ident)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) == NULL) { + if(stricmp(lang, "en")) { + printf_mysql_query("SELECT `text` FROM `help` WHERE `lang` = 'en' AND `ident` = '%s'", escape_string(ident)); + res = mysql_use(); + } + if ((row = mysql_fetch_row(res)) == NULL) { + reply(textclient, user, "NS_HELP_TOPIC"); + return; + } + } + char sendBuf[MAXLEN]; + int sendBufPos = 0; + int i; + for(i = 0; i < strlen(row[0]); i++) { + switch(row[0][i]) { + case '\n': + if(sendBufPos) { + sendBuf[sendBufPos] = '\0'; + reply(textclient, user, "%s", sendBuf); + sendBufPos = 0; + } + break; + case '$': + switch(row[0][i+1]) { + case 'b': + sendBuf[sendBufPos++] = 2; + i++; + break; + case 'k': + sendBuf[sendBufPos++] = 3; + i++; + break; + case 'u': + sendBuf[sendBufPos++] = 31; + i++; + break; + case 'C': + case 'S': + sendBufPos += sprintf(sendBuf + sendBufPos, "%s", client->user->nick); + i++; + break; + default: + sendBuf[sendBufPos++] = '$'; + break; + } + break; + default: + sendBuf[sendBufPos++] = row[0][i]; + break; + } + } + if(sendBufPos) { + sendBuf[sendBufPos] = '\0'; + reply(textclient, user, "%s", sendBuf); + sendBufPos = 0; + } +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_info.c b/src/modules/NeonServ.mod/cmd_neonserv_info.c new file mode 100644 index 0000000..c562a39 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_info.c @@ -0,0 +1,105 @@ +/* cmd_neonserv_info.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* no parameters +*/ + +CMD_BIND(neonserv_cmd_info) { + MYSQL_RES *res; + MYSQL_ROW row, row2; + struct Table *table; + printf_mysql_query("SELECT `channel_defaulttopic`, `channel_modes`, `channel_maxusers`, `channel_lastvisit`, `channel_registered`, `user_user` FROM `channels` LEFT JOIN `users` ON `channel_registrator` = `user_id` WHERE `channel_id` = '%d'", chan->channel_id); + res = mysql_use(); + row = mysql_fetch_row(res); + table = table_init(2, 9, 0); + table_set_bold(table, 0, 1); + char *content[2]; + reply(textclient, user, "NS_INFO_HEADER", chan->name); + content[0] = get_language_string(user, "NS_INFO_DEFAULTTOPIC"); + content[1] = row[0]; + table_add(table, content); + content[0] = get_language_string(user, "NS_INFO_MODELOCK"); + content[1] = row[1]; + table_add(table, content); + content[0] = get_language_string(user, "NS_INFO_RECORD"); + content[1] = row[2]; + table_add(table, content); + printf_mysql_query("SELECT `user_user`, `chanuser_access` FROM `chanusers` LEFT JOIN `users` ON `user_id` = `chanuser_uid` WHERE `chanuser_cid` = '%d'", chan->channel_id); + res = mysql_use(); + char ownerstr[MAXLEN]; + int ownerpos = 0, usercount = 0; + int userisowner = 0; + while((row2 = mysql_fetch_row(res))) { + if(!strcmp(row2[1], "500")) { + ownerpos += sprintf(ownerstr + ownerpos, (ownerpos ? ", %s" : "%s"), row2[0]); + if((user->flags & USERFLAG_ISAUTHED) && !stricmp(row2[0], user->auth)) + userisowner = 1; + } + usercount++; + } + content[0] = get_language_string(user, "NS_INFO_OWNER"); + content[1] = ownerstr; + table_add(table, content); + sprintf(ownerstr, "%d", usercount); + content[0] = get_language_string(user, "NS_INFO_USERS"); + content[1] = ownerstr; + table_add(table, content); + printf_mysql_query("SELECT COUNT(*) FROM `bans` WHERE `ban_channel` = '%d'", chan->channel_id); + res = mysql_use(); + row2 = mysql_fetch_row(res); + content[0] = get_language_string(user, "NS_INFO_BANS"); + content[1] = row2[0]; + table_add(table, content); + content[0] = get_language_string(user, "NS_INFO_VISITED"); + content[1] = timeToStr(user, time(0) - atoi(row[3]), 2, ownerstr); + table_add(table, content); + if(strcmp(row[4], "0")) { + content[0] = get_language_string(user, "NS_INFO_REGISTERED"); + content[1] = timeToStr(user, time(0) - atoi(row[4]), 2, ownerstr); + table_add(table, content); + } + if(row[5] && isGodMode(user)) { + content[0] = get_language_string(user, "NS_INFO_REGISTRAR"); + content[1] = row[5]; + table_add(table, content); + } + char **table_lines = table_end(table); + int i; + for(i = 0; i < table->entrys; i++) { + reply(textclient, user, table_lines[i]); + } + table_free(table); + if(userisowner || isGodMode(user)) { + printf_mysql_query("SELECT `owner_history_time`, a.`user_user`, b.`user_user` FROM `owner_history` LEFT JOIN `users` a ON `owner_history_from_uid` = a.`user_id` LEFT JOIN `users` b ON `owner_history_to_uid` = b.`user_id` WHERE `owner_history_cid` = '%d'", chan->channel_id); + res = mysql_use(); + if(mysql_num_rows(res)) { + reply(textclient, user, "NS_INFO_OWNERLOG", chan->name); + time_t rawtime; + struct tm *timeinfo; + char timeBuf[80]; + while((row = mysql_fetch_row(res))) { + rawtime = (time_t) atol(row[0]); + timeinfo = localtime(&rawtime); + strftime(timeBuf, 80, "%c", timeinfo); + reply(textclient, user, "NS_INFO_OWNERCHANGE", row[1], row[2], timeBuf); + } + } + } +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_invite.c b/src/modules/NeonServ.mod/cmd_neonserv_invite.c new file mode 100644 index 0000000..b9bce28 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_invite.c @@ -0,0 +1,185 @@ +/* cmd_neonserv_invite.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0] - nick / *auth +*/ +static USERAUTH_CALLBACK(neonserv_cmd_invite_nick_lookup); +static void neonserv_cmd_invite_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *nick, char *auth); +static TIMEQ_CALLBACK(neonserv_cmd_invite_timeout_timeout); +static struct neonserv_cmd_invite_timeout* neonserv_cmd_invite_add_timeout(char *nick, char *chan); +static int neonserv_cmd_invite_is_timeout(char *nick, char *chan); +static void neonserv_cmd_invite_del_timeout(struct neonserv_cmd_invite_timeout *timeout); + + +struct neonserv_cmd_invite_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + struct ChanNode *chan; + struct Event *event; + char *nick; +}; + +struct neonserv_cmd_invite_timeout { + char *nick; + char *chan; + + struct neonserv_cmd_invite_timeout *next; +}; + +static struct neonserv_cmd_invite_timeout *first_timeout = NULL, *last_timeout = NULL; + +CMD_BIND(neonserv_cmd_invite) { + if(neonserv_cmd_invite_is_timeout(argv[0], chan->name)) { + reply(textclient, user, "NS_INVITE_TIMEOUT", argv[0], chan->name); + return; + } + struct UserNode *cuser = getUserByNick(argv[0]); + if(!cuser) { + cuser = createTempUser(argv[0]); + if(!cuser) { + reply(textclient, user, "NS_USER_UNKNOWN", argv[0]); + return; + } + cuser->flags |= USERFLAG_ISTMPUSER; + } else if(getChanUser(cuser, chan)) { + reply(textclient, user, "NS_INVITE_ON_CHAN", cuser->nick, chan->name); + /* BUG + This check does not work if the user is invisible (CHMODE +D/+d) + to fix this we'd need to request the full userlist... + this is really senseless to invite a simple user so we simply mark this bug as unsolvable. + */ + return; + } + if(cuser->flags & USERFLAG_ISAUTHED) { + neonserv_cmd_invite_async1(client, textclient, user, chan, event, argv[0], cuser->auth); + } else { + struct neonserv_cmd_invite_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->chan = chan; + cache->event = event; + cache->nick = strdup(argv[0]); + get_userauth(cuser, module_id, neonserv_cmd_invite_nick_lookup, cache); + } +} + +static USERAUTH_CALLBACK(neonserv_cmd_invite_nick_lookup) { + struct neonserv_cmd_invite_cache *cache = data; + if(!user) { + //USER_DOES_NOT_EXIST + reply(cache->textclient, cache->user, "NS_USER_UNKNOWN", cache->nick); + } else + neonserv_cmd_invite_async1(cache->client, cache->textclient, cache->user, cache->chan, cache->event, user->nick, ((user->flags & USERFLAG_ISAUTHED) ? user->auth : NULL)); + free(cache->nick); + free(cache); +} + +static void neonserv_cmd_invite_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *nick, char *auth) { + if(auth && (!(user->flags & USERFLAG_ISAUTHED) || stricmp(auth, user->auth))) { + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `user_id`, `user_block_invites` FROM `users` WHERE `user_user` = '%s'", escape_string(auth)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + //check if the user has blocked invites globally + if(!strcmp(row[1], "1")) { + reply(textclient, user, "NS_INVITE_GLOBALLY_BLOCKED", nick); + return; + } + //check if the user has set noinvite + printf_mysql_query("SELECT `id` FROM `noinvite` WHERE `uid` = '%s' AND `cid` = '%d'", row[0], chan->channel_id); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + reply(textclient, user, "NS_INVITE_RESTRICTION", nick, chan->name); + return; + } + } + } + struct neonserv_cmd_invite_timeout *timeout = neonserv_cmd_invite_add_timeout(nick, chan->name); + timeq_add(INVITE_TIMEOUT, module_id, neonserv_cmd_invite_timeout_timeout, timeout); + putsock(client, "INVITE %s %s", nick, chan->name); + struct UserNode *tmpu = getUserByNick(nick); + if(!tmpu) { + tmpu = createTempUser(nick); + tmpu->flags |= USERFLAG_ISTMPUSER | (auth ? USERFLAG_ISAUTHED : 0); + if(auth) + strcpy(tmpu->auth, auth); + } + reply(textclient, tmpu, "NS_INVITE_DONE_USER", chan->name, user->nick, client->user->nick); + reply(textclient, user, "NS_INVITE_DONE", nick, chan->name); +} + +static TIMEQ_CALLBACK(neonserv_cmd_invite_timeout_timeout) { + struct neonserv_cmd_invite_timeout *entry = data; + neonserv_cmd_invite_del_timeout(entry); +} + +static struct neonserv_cmd_invite_timeout* neonserv_cmd_invite_add_timeout(char *nick, char *chan) { + struct neonserv_cmd_invite_timeout *entry = malloc(sizeof(*entry)); + if (!entry) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return NULL; + } + entry->next = NULL; + entry->nick = strdup(nick); + entry->chan = strdup(chan); + if(last_timeout) { + last_timeout->next = entry; + last_timeout = entry; + } else { + last_timeout = entry; + first_timeout = entry; + } + return entry; +} + +static int neonserv_cmd_invite_is_timeout(char *nick, char *chan) { + if(!first_timeout) return 0; + struct neonserv_cmd_invite_timeout *entry; + for(entry = first_timeout; entry; entry = entry->next) { + if(!stricmp(entry->nick, nick) && !stricmp(entry->chan, chan)) + return 1; + } + return 0; +} + +static void neonserv_cmd_invite_del_timeout(struct neonserv_cmd_invite_timeout *timeout) { + struct neonserv_cmd_invite_timeout *entry, *prev = NULL; + for(entry = first_timeout; entry; entry = entry->next) { + if(entry == timeout) { + if(prev) + prev->next = entry->next; + else + first_timeout = entry->next; + break; + } else + prev = entry; + } + if(last_timeout == timeout) + last_timeout = prev; + free(timeout->nick); + free(timeout->chan); + free(timeout); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_inviteme.c b/src/modules/NeonServ.mod/cmd_neonserv_inviteme.c new file mode 100644 index 0000000..bbe7a30 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_inviteme.c @@ -0,0 +1,36 @@ +/* cmd_neonserv_inviteme.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* no arguments +*/ + +CMD_BIND(neonserv_cmd_inviteme) { + if(getChanUser(user, chan)) { + reply(textclient, user, "NS_INVITEME_ON_CHAN", chan->name); + /* BUG + This check does not work if the user is invisible (CHMODE +D/+d) + to fix this we'd need to request the full userlist... + this is really senseless to invite a simple user so we simply mark this bug as unsolvable. + */ + return; + } + putsock(client, "INVITE %s %s", user->nick, chan->name); + reply(textclient, user, "NS_INVITEME_DONE", chan->name); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_invitemeall.c b/src/modules/NeonServ.mod/cmd_neonserv_invitemeall.c new file mode 100644 index 0000000..c7d3340 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_invitemeall.c @@ -0,0 +1,52 @@ +/* cmd_neonserv_invitemeall.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* no arguments +*/ + +CMD_BIND(neonserv_cmd_invitemeall) { + //invite to all channels where autoinvite is enabled + MYSQL_RES *res, *res2; + MYSQL_ROW chanuserrow, defaultrow = NULL; + printf_mysql_query("SELECT `chanuser_access`, `chanuser_flags`, `channel_name`, `channel_getinvite` FROM `chanusers` LEFT JOIN `channels` ON `chanuser_cid` = `channel_id` LEFT JOIN `users` ON `chanuser_uid` = `user_id` WHERE `user_user` = '%s' AND `chanuser_flags` >= '%d'", escape_string(user->auth), DB_CHANUSER_AUTOINVITE); + res = mysql_use(); + struct ChanUser *bchanuser; + struct ClientSocket *bot; + while((chanuserrow = mysql_fetch_row(res)) != NULL) { + int userflags = atoi(chanuserrow[1]); + if(!(argc && !stricmp(argv[0], "all")) && !(userflags & DB_CHANUSER_AUTOINVITE)) continue; + if(!(chan = getChanByName(chanuserrow[2]))) continue; //no bot is in the channel + if((bchanuser = getChanUser(client->user, chan)) && (bchanuser->flags & CHANUSERFLAG_OPPED)) + bot = client; + else { + bot = getBotForChannel(chan); + } + if(getChanUser(user, chan)) continue; //user is already in the channel + if(chanuserrow[3] == NULL && defaultrow == NULL) { + printf_mysql_query("SELECT `channel_getinvite` FROM `channels` WHERE `channel_name` = 'defaults'"); + res2 = mysql_use(); + defaultrow = mysql_fetch_row(res2); + } + if(atoi(chanuserrow[0]) >= atoi((chanuserrow[3] ? chanuserrow[3] : defaultrow[0]))) { + putsock(bot, "INVITE %s %s", user->nick, chan->name); + reply(textclient, user, "NS_INVITEME_DONE", chan->name); + } + } +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_kick.c b/src/modules/NeonServ.mod/cmd_neonserv_kick.c new file mode 100644 index 0000000..a4d3481 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_kick.c @@ -0,0 +1,167 @@ +/* cmd_neonserv_kick.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0] nick[,*auth[,*!*@mask[...]]] +* argv[1-*] reason +*/ +static USERLIST_CALLBACK(neonserv_cmd_kick_userlist_lookup); +static void neonserv_cmd_kick_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *nicks, char *reason); + +struct neonserv_cmd_kick_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + struct Event *event; + char *nicks; + char *reason; +}; + +CMD_BIND(neonserv_cmd_kick) { + struct neonserv_cmd_kick_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->event = event; + cache->nicks = strdup(argv[0]); + if(argc > 1) { + cache->reason = strdup(merge_argv(argv, 1, argc)); + } else + cache->reason = NULL; + get_userlist_with_invisible(chan, module_id, neonserv_cmd_kick_userlist_lookup, cache); +} + +static USERLIST_CALLBACK(neonserv_cmd_kick_userlist_lookup) { + struct neonserv_cmd_kick_cache *cache = data; + neonserv_cmd_kick_async1(cache->client, cache->textclient, cache->user, chan, cache->event, cache->nicks, (cache->reason ? cache->reason : "Bye.")); + free(cache->nicks); + if(cache->reason) + free(cache->reason); + free(cache); +} + +static void neonserv_cmd_kick_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *nicks, char *reason) { + int i, kicked_users = 0, provided_nicks = 0; + char *nick, *nextnick; + struct UserNode *cuser; + struct ChanUser *chanuser; + nextnick = nicks; + while((nick = nextnick)) { + nextnick = strstr(nick, ","); + if(nextnick) { + *nextnick = '\0'; + nextnick++; + } + if(!*nick) continue; + if(is_ircmask(nick)) { + //KICK HOSTMASK + char usermask[NICKLEN+USERLEN+HOSTLEN+3]; + struct ChanUser *kick_chanuser[chan->usercount]; + int kick_chanuser_pos = 0; + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + cuser = chanuser->user; + sprintf(usermask, "%s!%s@%s", cuser->nick, cuser->ident, cuser->host); + if(!match(nick, usermask)) { + provided_nicks++; + if(isNetworkService(chanuser->user)) { + reply(textclient, user, "NS_SERVICE_IMMUNE", chanuser->user->nick); + continue; + } + if(cuser == user || ((cuser->flags & USERFLAG_ISAUTHED) && !stricmp(user->auth, cuser->auth))) { + reply(textclient, user, "NS_YOU_PROTECTED"); + continue; + } + if(isUserProtected(chan, cuser, user)) { + reply(textclient, user, "NS_USER_PROTECTED", cuser->nick); + continue; + } + kick_chanuser[kick_chanuser_pos++] = chanuser; + if(kick_chanuser_pos > 4 && (kick_chanuser_pos * 3) > chan->usercount && !isGodMode(user)) { + kick_chanuser_pos = 0; + reply(textclient, user, "NS_LAME_MASK", nick); + break; + } + } + } + for(i = 0; i < kick_chanuser_pos; i++) { + kicked_users++; + putsock(client, "KICK %s %s :(%s) %s", chan->name, kick_chanuser[i]->user->nick, user->nick, reason); + } + } else if(*nick == '*') { + //KICK AUTH + nick++; + cuser = NULL; + if(!stricmp(user->auth, nick)) { + reply(textclient, user, "NS_YOU_PROTECTED"); + continue; + } + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + if((chanuser->user->flags & USERFLAG_ISAUTHED) && !stricmp(chanuser->user->auth, nick)) { + provided_nicks++; + if(isNetworkService(chanuser->user)) { + reply(textclient, user, "NS_SERVICE_IMMUNE", chanuser->user->nick); + continue; + } + if(!cuser) { + //check if the user is protected + if(isUserProtected(chan, chanuser->user, user)) { + reply(textclient, user, "NS_USER_PROTECTED", chanuser->user->nick); + break; //all other users are also protected... + } + cuser = chanuser->user; + } + kicked_users++; + putsock(client, "KICK %s %s :(%s) %s", chan->name, cuser->nick, user->nick, reason); + } + } + } else { + provided_nicks++; + cuser = NULL; + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + if(!stricmp(chanuser->user->nick, nick)) { + cuser = chanuser->user; + } + } + if(!cuser) continue; + if(isNetworkService(cuser)) { + reply(textclient, user, "NS_SERVICE_IMMUNE", cuser->nick); + continue; + } + if(cuser == user || ((cuser->flags & USERFLAG_ISAUTHED) && !stricmp(user->auth, cuser->auth))) { + reply(textclient, user, "NS_YOU_PROTECTED"); + continue; + } + if(isUserProtected(chan, cuser, user)) { + reply(textclient, user, "NS_USER_PROTECTED", cuser->nick); + continue; + } + kicked_users++; + putsock(client, "KICK %s %s :(%s) %s", chan->name, cuser->nick, user->nick, reason); + } + } + if(kicked_users == provided_nicks) + reply(textclient, user, "NS_KICK_DONE", kicked_users, chan->name); + else + reply(textclient, user, "NS_KICK_FAIL", client->user->nick); + if(kicked_users) + logEvent(event); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_kickban.c b/src/modules/NeonServ.mod/cmd_neonserv_kickban.c new file mode 100644 index 0000000..a580764 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_kickban.c @@ -0,0 +1,169 @@ +/* cmd_neonserv_kickban.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0] nick[,*auth[,*!*@mask[...]]] +* argv[1-*] reason +*/ +static USERLIST_CALLBACK(neonserv_cmd_kickban_userlist_lookup); +static void neonserv_cmd_kickban_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *nicks, char *reason); + +struct neonserv_cmd_kickban_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + struct Event *event; + char *nicks; + char *reason; +}; + +CMD_BIND(neonserv_cmd_kickban) { + struct neonserv_cmd_kickban_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->event = event; + cache->nicks = strdup(argv[0]); + if(argc > 1) { + cache->reason = strdup(merge_argv(argv, 1, argc)); + } else + cache->reason = NULL; + get_userlist_with_invisible(chan, module_id, neonserv_cmd_kickban_userlist_lookup, cache); +} + +static USERLIST_CALLBACK(neonserv_cmd_kickban_userlist_lookup) { + struct neonserv_cmd_kickban_cache *cache = data; + neonserv_cmd_kickban_async1(cache->client, cache->textclient, cache->user, chan, cache->event, cache->nicks, (cache->reason ? cache->reason : "Bye.")); + free(cache->nicks); + if(cache->reason) + free(cache->reason); + free(cache); +} + +static void neonserv_cmd_kickban_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *nicks, char *reason) { + int i, kicked_users = 0, provided_nicks = 0; + char *nick, *nextnick; + struct UserNode *cuser; + struct ChanUser *chanuser; + char usermask[NICKLEN+USERLEN+HOSTLEN+3]; + nextnick = nicks; + while((nick = nextnick)) { + nextnick = strstr(nick, ","); + if(nextnick) { + *nextnick = '\0'; + nextnick++; + } + if(!*nick) continue; + if(is_ircmask(nick)) { + //KICK HOSTMASK + struct ChanUser *kickban_chanuser[chan->usercount]; + int kick_chanuser_pos = 0; + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + cuser = chanuser->user; + sprintf(usermask, "%s!%s@%s", cuser->nick, cuser->ident, cuser->host); + if(!match(nick, usermask)) { + provided_nicks++; + if(isNetworkService(chanuser->user)) { + reply(textclient, user, "NS_SERVICE_IMMUNE", chanuser->user->nick); + continue; + } + if(cuser == user || ((cuser->flags & USERFLAG_ISAUTHED) && !stricmp(user->auth, cuser->auth))) { + reply(textclient, user, "NS_YOU_PROTECTED"); + continue; + } + if(isUserProtected(chan, cuser, user)) { + reply(textclient, user, "NS_USER_PROTECTED", cuser->nick); + continue; + } + kickban_chanuser[kick_chanuser_pos++] = chanuser; + if(kick_chanuser_pos > 4 && (kick_chanuser_pos * 3) > chan->usercount && !isGodMode(user)) { + kick_chanuser_pos = 0; + reply(textclient, user, "NS_LAME_MASK", nick); + break; + } + } + } + for(i = 0; i < kick_chanuser_pos; i++) { + if(i == 0) { + putsock(client, "MODE %s +b %s", chan->name, nick); + } + kicked_users++; + putsock(client, "KICK %s %s :(%s) %s", chan->name, kickban_chanuser[i]->user->nick, user->nick, reason); + } + } else if(*nick == '*') { + //KICK AUTH + nick++; + cuser = NULL; + if(!stricmp(user->auth, nick)) { + reply(textclient, user, "NS_YOU_PROTECTED"); + continue; + } + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + if((chanuser->user->flags & USERFLAG_ISAUTHED) && !stricmp(chanuser->user->auth, nick)) { + provided_nicks++; + if(isNetworkService(chanuser->user)) { + reply(textclient, user, "NS_SERVICE_IMMUNE", chanuser->user->nick); + continue; + } + if(!cuser) { + //check if the user is protected + if(isUserProtected(chan, chanuser->user, user)) { + reply(textclient, user, "NS_USER_PROTECTED", chanuser->user->nick); + break; //all other users are also protected... + } + cuser = chanuser->user; + } + kicked_users++; + putsock(client, "MODE %s +b %s", chan->name, generate_banmask(cuser, usermask)); + putsock(client, "KICK %s %s :(%s) %s", chan->name, cuser->nick, user->nick, reason); + } + } + } else { + provided_nicks++; + cuser = searchUserByNick(nick); + if(!cuser) continue; + chanuser = getChanUser(cuser, chan); + if(!chanuser) continue; + if(isNetworkService(cuser)) { + reply(textclient, user, "NS_SERVICE_IMMUNE", cuser->nick); + continue; + } + if(cuser == user || ((cuser->flags & USERFLAG_ISAUTHED) && !stricmp(user->auth, cuser->auth))) { + reply(textclient, user, "NS_YOU_PROTECTED"); + continue; + } + if(isUserProtected(chan, cuser, user)) { + reply(textclient, user, "NS_USER_PROTECTED", cuser->nick); + continue; + } + kicked_users++; + putsock(client, "MODE %s +b %s", chan->name, generate_banmask(cuser, usermask)); + putsock(client, "KICK %s %s :(%s) %s", chan->name, cuser->nick, user->nick, reason); + } + } + if(kicked_users == provided_nicks) + reply(textclient, user, "NS_KICKBAN_DONE", kicked_users, chan->name); + else + reply(textclient, user, "NS_KICKBAN_FAIL", client->user->nick); + if(kicked_users) + logEvent(event); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_listrank.c b/src/modules/NeonServ.mod/cmd_neonserv_listrank.c new file mode 100644 index 0000000..162a6ca --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_listrank.c @@ -0,0 +1,77 @@ +/* cmd_neonserv_listrank.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* no arguments +*/ + +CMD_BIND(neonserv_cmd_listrank) { + MYSQL_RES *res, *res2; + MYSQL_ROW row, row2; + struct Table *table; + int ranks = 0; + printf_mysql_query("SELECT `rank_id`, `rank_name` FROM `support_ranks` ORDER BY `rank_order` ASC"); + res = mysql_use(); + table = table_init(3, mysql_num_rows(res) + 1, 0); + char *content[3]; + content[0] = get_language_string(user, "NS_LISTRANK_ID"); + content[1] = get_language_string(user, "NS_LISTRANK_NAME"); + content[2] = get_language_string(user, "NS_LISTRANK_ASSIGNED"); + table_add(table, content); + char usersBuf[MAXLEN]; + int usersPos, userCount; + char assignedBuf[MAXLEN]; + while ((row = mysql_fetch_row(res)) != NULL) { + ranks++; + content[0] = row[0]; + content[1] = row[1]; + printf_mysql_query("SELECT `user_user`, `user_god` FROM `users` WHERE `user_rank` = '%s'", row[0]); + res2 = mysql_use(); + usersPos = 0; + userCount = 0; + while((row2 = mysql_fetch_row(res2)) != NULL) { + usersPos += sprintf(usersBuf+usersPos, (usersPos ? ", %s%s" : "%s%s"), (strcmp(row2[1], "0") ? "@" : ""), row2[0]); + userCount++; + } + sprintf(assignedBuf, (userCount ? "%d (%s)" : "%d"), userCount, usersBuf); + content[2] = assignedBuf; + table_add(table, content); + } + //send the table + char **table_lines = table_end(table); + int i; + for(i = 0; i < table->entrys; i++) { + reply(textclient, user, table_lines[i]); + } + if(!ranks) + reply(textclient, user, "NS_TABLE_NONE"); + table_free(table); + printf_mysql_query("SELECT `user_user` FROM `users` WHERE `user_rank` = '0' AND `user_access` > 0"); + res2 = mysql_use(); + usersPos = 0; + userCount = 0; + while((row2 = mysql_fetch_row(res2)) != NULL) { + usersPos += sprintf(usersBuf+usersPos, (usersPos ? ", %s%s" : "%s%s"), (strcmp(row2[1], "0") ? "@" : ""), row2[0]); + userCount++; + } + reply(textclient, user, "NS_LISTRANK_UNRANKED", userCount); + if(userCount) { + reply(textclient, user, " %s", usersBuf); + } +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_mdeluser.c b/src/modules/NeonServ.mod/cmd_neonserv_mdeluser.c new file mode 100644 index 0000000..b511366 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_mdeluser.c @@ -0,0 +1,64 @@ +/* cmd_neonserv_mdeluser.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0] access (format: minaccess-maxaccess) +* argv[1] pattern +*/ + +CMD_BIND(neonserv_cmd_mdeluser) { + int min_access, max_access; + char *seperator = strstr(argv[0], "-"); + if(seperator) { + *seperator = '\0'; + seperator++; + min_access = atoi(argv[0]); + max_access = atoi(seperator); + if(max_access < min_access) { + reply(textclient, user, "NS_INVALID_ACCESS_RANGE", min_access, max_access); + return; + } + } else { + min_access = atoi(argv[0]); + max_access = min_access; + } + if(max_access >= getChannelAccess(user, chan)) { + if(isGodMode(user)) { + event->flags |= CMDFLAG_OPLOG; + } else { + reply(textclient, user, "NS_NO_ACCESS"); + return; + } + } + MYSQL_RES *res; + MYSQL_ROW row; + int del_count = 0; + printf_mysql_query("SELECT `user_user`, `chanuser_id` FROM `chanusers` LEFT JOIN `users` ON `chanuser_uid` = `user_id` WHERE `chanuser_cid` = '%d' AND `chanuser_access` >= '%d' AND `chanuser_access` <= '%d'", chan->channel_id, min_access, max_access); + res = mysql_use(); + while((row = mysql_fetch_row(res)) != NULL) { + if(!match(argv[1], row[0])) { + del_count++; + printf_mysql_query("DELETE FROM `chanusers` WHERE `chanuser_id` = '%s'", row[1]); + } + } + reply(textclient, user, "NS_MDELUSER_DONE", del_count, argv[1], min_access, max_access, chan->name); + if(del_count) + logEvent(event); +} + diff --git a/src/modules/NeonServ.mod/cmd_neonserv_mode.c b/src/modules/NeonServ.mod/cmd_neonserv_mode.c new file mode 100644 index 0000000..41e0455 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_mode.c @@ -0,0 +1,297 @@ +/* cmd_neonserv_mode.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0] - modes +* argv[1-*] - parameters +*/ +static USERLIST_CALLBACK(neonserv_cmd_mode_userlist_lookup); +static void neonserv_cmd_mode_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *mode); + +struct neonserv_cmd_mode_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + struct Event *event; + char *mode; +}; + +CMD_BIND(neonserv_cmd_mode) { + struct neonserv_cmd_mode_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->event = event; + cache->mode = strdup(merge_argv(argv, 0, argc)); + get_userlist_with_invisible(chan, module_id, neonserv_cmd_mode_userlist_lookup, cache); +} + +static USERLIST_CALLBACK(neonserv_cmd_mode_userlist_lookup) { + struct neonserv_cmd_mode_cache *cache = data; + neonserv_cmd_mode_async1(cache->client, cache->textclient, cache->user, chan, cache->event, cache->mode); + free(cache->mode); + free(cache); +} + +static void neonserv_cmd_mode_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *mode) { + MYSQL_ROW row, defaults = NULL; + int i, arg, add = 1, skip = 0; + unsigned int modetype; + int db_canop, db_canvoice, db_canban, db_enfmodes; + struct ModeNode *modelock = createModeNode(NULL), *changemodes = createModeNode(NULL); + struct ModeBuffer *modeBuf; + struct UserNode *cuser; + struct ChanUser *chanuser; + modeBuf = initModeBuffer(client, chan); + printf_mysql_query("SELECT `channel_canop`, `channel_canvoice`, `channel_canban`, `channel_enfmodes`, `channel_modes` FROM `channels` WHERE `channel_id` = '%d'", chan->channel_id); + row = mysql_fetch_row(mysql_use()); + if(row[0] == NULL || row[1] == NULL || row[2] == NULL || row[3] == NULL || row[4] == NULL) { + printf_mysql_query("SELECT `channel_canop`, `channel_canvoice`, `channel_canban`, `channel_enfmodes`, `channel_modes` FROM `channels` WHERE `channel_name` = 'defaults'"); + defaults = mysql_fetch_row(mysql_use()); + } + db_canop = atoi((row[0] ? row[0] : defaults[0])); + db_canvoice = atoi((row[1] ? row[1] : defaults[1])); + db_canban = atoi((row[2] ? row[2] : defaults[2])); + db_enfmodes = atoi((row[3] ? row[3] : defaults[3])); + if(row[4]) + parseModeString(modelock, row[4]); + else if(defaults[4]) + parseModeString(modelock, defaults[4]); + int uaccess = getChannelAccess(user, chan); + char *a, *b = mode; + char *argv[MAXNUMPARAMS]; + char *carg; + char tmp[MAXLEN]; + int argc = 0; + do { + a = strstr(b, " "); + if(a) *a = '\0'; + argv[argc++] = b; + if(argc == MAXNUMPARAMS) break; + if(a) b = a+1; + } while(a); + arg = 0; + while(arg < argc) { + char *modeStr = argv[arg++]; + for(i = 0; i < strlen(modeStr); i++) { + switch(modeStr[i]) { + case '+': + add = 1; + break; + case '-': + add = 0; + break; + case 'o': + case 'v': + if(arg == argc) { + reply(textclient, user, "NS_MODE_INVALID", modeStr[i]); + return; + } + carg = argv[arg++]; + if(modeStr[i] == 'o') { + if(uaccess < db_canop) { + reply(textclient, user, "NS_MODE_ENFOPS", chan->name); + db_canop = -1; + break; + } + if(db_canop == -1) break; + } else { + if(uaccess < db_canvoice) { + reply(textclient, user, "NS_MODE_ENFVOICE", chan->name); + db_canvoice = -1; + break; + } + if(db_canvoice == -1) break; + } + cuser = searchUserByNick(carg); + if(!cuser) { + //check for an invisible user + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + if(!stricmp(chanuser->user->nick, carg)) { + cuser = chanuser->user; + break; + } + } + if(!cuser) break; + } else { + chanuser = getChanUser(cuser, chan); + if(!chanuser) break; + } + if(!(add ^ (chanuser->flags & (modeStr[i] == 'o' ? CHANUSERFLAG_OPPED : CHANUSERFLAG_VOICED)))) break; + if(!add) { + //check protection + if(modeStr[i] == 'o' && isNetworkService(cuser)) { + reply(textclient, user, "NS_SERVICE_IMMUNE", cuser->nick); + break; + } + if(isUserProtected(chan, cuser, user)) { + reply(textclient, user, "NS_USER_PROTECTED", cuser->nick); + break; + } + } + modeBufferSet(modeBuf, add, modeStr[i], carg); + break; + case 'b': + if(arg == argc) { + reply(textclient, user, "NS_MODE_INVALID", modeStr[i]); + return; + } + carg = argv[arg++]; + if(uaccess < db_canban) { + reply(textclient, user, "NS_MODE_CANBAN", chan->name); + db_canban = -1; + break; + } + if(db_canban == -1) break; + char hostmask_buffer[NICKLEN+USERLEN+HOSTLEN+3]; + char usermask[NICKLEN+USERLEN+HOSTLEN+3]; + struct BanNode *ban; + int match_count = 0; + carg = make_banmask(carg, hostmask_buffer); + if(add) { + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + cuser = chanuser->user; + sprintf(usermask, "%s!%s@%s", cuser->nick, cuser->ident, cuser->host); + if(!match(carg, usermask)) { + if(isNetworkService(chanuser->user)) { + reply(textclient, user, "NS_SERVICE_IMMUNE", chanuser->user->nick); + skip = 1; + break; + } + if(isUserProtected(chan, cuser, user)) { + reply(textclient, user, "NS_USER_PROTECTED", cuser->nick); + skip = 1; + break; + } + match_count++; + if(match_count > 4 && (match_count * 3) > chan->usercount && !isGodMode(user)) { + skip = 1; + reply(textclient, user, "NS_LAME_MASK", carg); + break; + } + } + } + } else { + skip = 1; + for(ban = chan->bans; ban; ban = ban->next) { + if(!match(carg, ban->mask)) { + skip = 0; + break; + } + } + } + if(!skip) { + modeBufferSet(modeBuf, add, 'b', carg); + } + break; + default: + modetype = getModeType(modelock, modeStr[i]); + if(modetype == 0) { + reply(textclient, user, "NS_MODE_INVALID", modeStr[i]); + return; + } + if(isModeAffected(modelock, modeStr[i]) && add == !isModeSet(modelock, modeStr[i]) && uaccess < db_enfmodes) { + if(isGodMode(user)) + event->flags |= CMDFLAG_OPLOG; + else { + getFullModeString(modelock, tmp); + reply(textclient, user, "NS_MODE_LOCKED", tmp, chan->name); + return; + } + } + if(add && (modetype & CHANNEL_MODE_TYPE) != CHANNEL_MODE_TYPE_D) { + if(arg == argc) { + reply(textclient, user, "NS_MODE_INVALID", modeStr[i]); + return; + } + carg = argv[arg++]; + if((modetype & CHANNEL_MODE_VALUE) == CHANNEL_MODE_VALUE_STRING && isModeSet(modelock, modeStr[i])) { + char *modelock_val = getModeValue(modelock, modeStr[i]); + if(stricmp(carg, modelock_val)) { + if(isGodMode(user)) + event->flags |= CMDFLAG_OPLOG; + else { + getFullModeString(modelock, tmp); + reply(textclient, user, "NS_MODE_LOCKED", tmp, chan->name); + return; + } + } + } + if((modetype & CHANNEL_MODE_VALUE) == CHANNEL_MODE_VALUE_STRING && isModeSet(modelock, modeStr[i])) { + int *modelock_val = getModeValue(modelock, modeStr[i]); + if(atoi(carg) != *modelock_val) { + if(isGodMode(user)) + event->flags |= CMDFLAG_OPLOG; + else { + getFullModeString(modelock, tmp); + reply(textclient, user, "NS_MODE_LOCKED", tmp, chan->name); + return; + } + } + } + } else if(!add && (modetype & CHANNEL_MODE_TYPE) == CHANNEL_MODE_TYPE_B) { + if(arg == argc && !(modetype & CHANNEL_MODE_KEY)) { + reply(textclient, user, "NS_MODE_INVALID", modeStr[i]); + return; + } + carg = (arg == argc ? NULL : argv[arg++]); + } else + carg = NULL; + if((modetype & CHANNEL_MODE_TYPE) == CHANNEL_MODE_TYPE_D && isModeSet(chan->modes, modeStr[i]) == add) + break; + if(!isModeAffected(changemodes, modeStr[i])) { + if(!add && (modetype & CHANNEL_MODE_KEY)) { + if(isModeSet(chan->modes, modeStr[i])) { + char *current_val = getModeValue(chan->modes, modeStr[i]); + carg = current_val; + } + } + if(parseMode(changemodes, add, modeStr[i], carg)) { + if(carg) { + if(add && (modetype & CHANNEL_MODE_KEY) && isModeSet(chan->modes, modeStr[i])) { + char *current_val = getModeValue(chan->modes, modeStr[i]); + modeBufferSet(modeBuf, 0, modeStr[i], current_val); + flushModeBuffer(modeBuf); + } + if(!add && !isModeSet(chan->modes, modeStr[i])) break; + modeBufferSet(modeBuf, add, modeStr[i], carg); + } else { + modeBufferSimpleMode(modeBuf, add, modeStr[i]); + } + } else { + reply(textclient, user, "NS_MODE_INVALID", modeStr[i]); + return; + } + } + break; + } + } + } + getFullModeString(changemodes, tmp); + freeModeBuffer(modeBuf); + if(strcmp(tmp, "+")) + reply(textclient, user, "NS_MODE_DONE", tmp); + + logEvent(event); + freeModeNode(modelock); + freeModeNode(changemodes); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_myaccess.c b/src/modules/NeonServ.mod/cmd_neonserv_myaccess.c new file mode 100644 index 0000000..29f8cfd --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_myaccess.c @@ -0,0 +1,197 @@ +/* cmd_neonserv_myaccess.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0] - nick / *auth +*/ +static USERAUTH_CALLBACK(neonserv_cmd_myaccess_nick_lookup); +static void neonserv_cmd_myaccess_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *nick, char *auth, char *chanmatch); + +struct neonserv_cmd_myaccess_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + struct ChanNode *chan; + struct Event *event; + char *nick; + char *chanmatch; +}; + +CMD_BIND(neonserv_cmd_myaccess) { + char *chanmatch = NULL; + if(argc == 0 || argv[0][0] == '#') { + if(argc != 0) { + chanmatch = argv[0]; + } + if(!(user->flags & USERFLAG_ISAUTHED)) { + struct neonserv_cmd_myaccess_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->chan = chan; + cache->nick = strdup(argv[0]); + cache->chanmatch = (chanmatch ? strdup(chanmatch) : NULL); + get_userauth(user, module_id, neonserv_cmd_myaccess_nick_lookup, cache); + } else + neonserv_cmd_myaccess_async1(client, textclient, user, chan, event, user->nick, user->auth, chanmatch); + } + else if(argv[0][0] == '*') { + //we've got an auth + if(argc > 1 && argv[1][0] == '#') { + chanmatch = argv[1]; + } + argv[0]++; + neonserv_cmd_myaccess_async1(client, textclient, user, chan, event, NULL, argv[0], chanmatch); + } else { + if(argc > 1 && argv[1][0] == '#') { + chanmatch = argv[1]; + } + struct UserNode *cuser = getUserByNick(argv[0]); + if(!cuser) { + cuser = createTempUser(argv[0]); + if(!cuser) { + reply(textclient, user, "NS_USER_UNKNOWN", argv[0]); + return; + } + cuser->flags |= USERFLAG_ISTMPUSER; + } + if(cuser->flags & USERFLAG_ISAUTHED) { + neonserv_cmd_myaccess_async1(client, textclient, user, chan, event, argv[0], cuser->auth, chanmatch); + } else { + struct neonserv_cmd_myaccess_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->chan = chan; + cache->event = event; + cache->nick = strdup(argv[0]); + cache->chanmatch = (chanmatch ? strdup(chanmatch) : NULL); + get_userauth(cuser, module_id, neonserv_cmd_myaccess_nick_lookup, cache); + } + } +} + +static USERAUTH_CALLBACK(neonserv_cmd_myaccess_nick_lookup) { + struct neonserv_cmd_myaccess_cache *cache = data; + if(!user) { + //USER_DOES_NOT_EXIST + reply(cache->textclient, cache->user, "NS_USER_UNKNOWN", cache->nick); + } + else if(!(user->flags & USERFLAG_ISAUTHED)) { + //USER_NOT_AUTHED + if(!strcmp(cache->nick, cache->user->nick)) + reply(cache->textclient, cache->user, "NS_YOU_NEED_AUTH"); + else + reply(cache->textclient, cache->user, "NS_USER_NEED_AUTH", cache->nick); + } + else + neonserv_cmd_myaccess_async1(cache->client, cache->textclient, cache->user, cache->chan, cache->event, user->nick, user->auth, cache->chanmatch); + if(cache->chanmatch) + free(cache->chanmatch); + free(cache->nick); + free(cache); +} + +static void neonserv_cmd_myaccess_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *nick, char *auth, char *chanmatch) { + //we've got a valid auth now... + if(stricmp(user->auth, auth)) { + if(!isGodMode(user)) { + reply(textclient, user, "NS_MYACCESS_SELF_ONLY"); + return; + } else + event->flags |= CMDFLAG_OPLOG; + } + MYSQL_RES *res, *default_res; + MYSQL_ROW user_row, chanuser_row, default_chan = NULL; + char flagBuf[5]; + int userid, cflags, caccess, flagPos; + int i, total_count = 0, match_count = 0, owner_count = 0; + struct Table *table = NULL; + printf_mysql_query("SELECT `user_id`, `user_access`, `user_god` FROM `users` WHERE `user_user` = '%s'", escape_string(auth)); + res = mysql_use(); + char *content[4]; + content[0] = get_language_string(user, "NS_MYACCESS_HEADER_NAME"); + content[1] = get_language_string(user, "NS_MYACCESS_HEADER_ACCESS"); + content[2] = get_language_string(user, "NS_MYACCESS_HEADER_FLAGS"); + content[3] = get_language_string(user, "NS_MYACCESS_HEADER_INFO"); + if(chanmatch) + reply(textclient, user, "NS_MYACCESS_HEADER_MATCH", auth, chanmatch); + else + reply(textclient, user, "NS_MYACCESS_HEADER", auth); + if ((user_row = mysql_fetch_row(res)) != NULL) { + userid = atoi(user_row[0]); + //check if the user is already added + printf_mysql_query("SELECT `chanuser_access`, `chanuser_flags`, `chanuser_infoline`, `channel_name`, `channel_getop`, `channel_getvoice`, `botid` FROM `chanusers` LEFT JOIN `channels` ON `chanuser_cid` = `channel_id` LEFT JOIN `bot_channels` ON `chanuser_cid` = `chanid` LEFT JOIN `bots` ON `bots`.`id` = `botid` WHERE `chanuser_uid` = '%d' AND `botclass` = '%d' AND `active` = '1' ORDER BY `chanuser_access` DESC, `channel_name` ASC", userid, client->botid); + res = mysql_use(); + total_count = mysql_num_rows(res); + table = table_init(4, total_count + 1, 0); + table_add(table, content); + while ((chanuser_row = mysql_fetch_row(res)) != NULL) { + if(!strcmp(chanuser_row[0], "500")) owner_count++; + if(chanmatch && match(chanmatch, chanuser_row[0])) continue; + match_count++; + if((!chanuser_row[4] || !chanuser_row[5]) && !default_chan) { + printf_mysql_query("SELECT `channel_getop`, `channel_getvoice` FROM `channels` WHERE `channel_name` = 'defaults'"); + default_res = mysql_use(); + default_chan = mysql_fetch_row(default_res); + } + flagPos = 0; + content[0] = chanuser_row[3]; + content[1] = chanuser_row[0]; + cflags = atoi(chanuser_row[1]); + caccess = atoi(chanuser_row[0]); + if((cflags & DB_CHANUSER_SUSPENDED)) + flagPos += sprintf(flagBuf + flagPos, "s"); + if(caccess >= (chanuser_row[4] ? atoi(chanuser_row[4]) : atoi(default_chan[0]))) + flagPos += sprintf(flagBuf + flagPos, "o"); + if(caccess >= (chanuser_row[5] ? atoi(chanuser_row[5]) : atoi(default_chan[1]))) + flagPos += sprintf(flagBuf + flagPos, "v"); + if((cflags & DB_CHANUSER_AUTOINVITE)) + flagPos += sprintf(flagBuf + flagPos, "i"); + content[2] = (flagPos ? flagBuf : ""); + content[3] = chanuser_row[2]; + table_add(table, content); + } + } else { + table = table_init(4, 1, 0); + table_add(table, content); + } + //send the table + char **table_lines = table_end(table); + for(i = 0; i < table->entrys; i++) { + reply(textclient, user, table_lines[i]); + } + if(!match_count) + reply(textclient, user, "NS_TABLE_NONE"); + if(chanmatch) { + reply(textclient, user, "NS_MYACCESS_COUNT_MATCH", auth, total_count, owner_count, match_count, chanmatch); + } else { + reply(textclient, user, "NS_MYACCESS_COUNT", auth, total_count, owner_count); + } + table_free(table); + if(event->flags & CMDFLAG_OPLOG) + logEvent(event); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_nicklist.c b/src/modules/NeonServ.mod/cmd_neonserv_nicklist.c new file mode 100644 index 0000000..0fad5a8 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_nicklist.c @@ -0,0 +1,329 @@ +/* cmd_neonserv_nicklist.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0] "force" +* argv[1] (optional) nick mask +*/ +static USERLIST_CALLBACK(neonserv_cmd_nicklist_userlist_lookup); +static void neonserv_cmd_nicklist_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *nickmask, int flags); +static void neonserv_cmd_nicklist_synchronize_user(struct ChanNode *chan, struct UserNode *user, int access); + +#define NEONSERV_CMD_NICKLIST_FLAG_SYNCUSERS 0x01 +#define NEONSERV_CMD_NICKLIST_FLAG_NOWHO 0x02 +#define NEONSERV_CMD_NICKLIST_FLAG_VISCOUNT 0x04 + +struct neonserv_cmd_nicklist_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + struct Event *event; + char *nickmask; + int flags; +}; + +CMD_BIND(neonserv_cmd_nicklist) { + int flags = 0; + while(argc) { + if(!stricmp(argv[0], "sync")) { + if(!checkChannelAccess(user, chan, "channel_canadd", 0)) { + if(isGodMode(user)) { + event->flags |= CMDFLAG_OPLOG; + } else { + reply(textclient, user, "NS_ACCESS_DENIED"); + return; + } + } + flags |= NEONSERV_CMD_NICKLIST_FLAG_SYNCUSERS; + event->flags |= CMDFLAG_LOG; + } else if(argc && !stricmp(argv[0], "nowho") && isGodMode(user)) { + flags |= NEONSERV_CMD_NICKLIST_FLAG_NOWHO; + } else if(argc && !stricmp(argv[0], "viscount") && isGodMode(user)) { + flags |= NEONSERV_CMD_NICKLIST_FLAG_VISCOUNT; + } else + break; + argv++; + argc--; + } + if(flags & NEONSERV_CMD_NICKLIST_FLAG_NOWHO) { + neonserv_cmd_nicklist_async1(client, textclient, user, chan, event, (argc ? argv[0] : NULL), flags); + return; + } + struct neonserv_cmd_nicklist_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->event = event; + if(argc) { + cache->nickmask = strdup(argv[0]); + } else + cache->nickmask = NULL; + cache->flags = flags; + get_userlist_with_invisible(chan, module_id, neonserv_cmd_nicklist_userlist_lookup, cache); +} + +static USERLIST_CALLBACK(neonserv_cmd_nicklist_userlist_lookup) { + struct neonserv_cmd_nicklist_cache *cache = data; + neonserv_cmd_nicklist_async1(cache->client, cache->textclient, cache->user, chan, cache->event, cache->nickmask, cache->flags); + if(cache->nickmask) + free(cache->nickmask); + free(cache); +} + +static int neonserv_cmd_nicklist_sort_flags[] = { + CHANUSERFLAG_OPPED | CHANUSERFLAG_HALFOPPED | CHANUSERFLAG_VOICED, + CHANUSERFLAG_OPPED | CHANUSERFLAG_HALFOPPED, + CHANUSERFLAG_OPPED | CHANUSERFLAG_VOICED, + CHANUSERFLAG_OPPED, + CHANUSERFLAG_HALFOPPED | CHANUSERFLAG_VOICED, + CHANUSERFLAG_HALFOPPED, + CHANUSERFLAG_VOICED, + CHANUSERFLAG_INVISIBLE, + 0 +}; + +static int neonserv_cmd_nicklist_sort(const void *a, const void *b) { + const struct ChanUser *chanuser_a = *((struct ChanUser * const *) a); + const struct ChanUser *chanuser_b = *((struct ChanUser * const *) b); + int i_a = 0, i_b = 0; + while((chanuser_a->flags & (CHANUSERFLAG_OPPED | CHANUSERFLAG_VOICED | CHANUSERFLAG_INVISIBLE)) != neonserv_cmd_nicklist_sort_flags[i_a] && neonserv_cmd_nicklist_sort_flags[i_a]) + i_a++; + while((chanuser_b->flags & (CHANUSERFLAG_OPPED | CHANUSERFLAG_VOICED | CHANUSERFLAG_INVISIBLE)) != neonserv_cmd_nicklist_sort_flags[i_b] && neonserv_cmd_nicklist_sort_flags[i_b]) + i_b++; + if(i_a == i_b) { + return stricmp(chanuser_a->user->nick, chanuser_b->user->nick); + } else + return i_a - i_b; +} + +static void neonserv_cmd_nicklist_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *nickmask, int flags) { + MYSQL_RES *res; + MYSQL_ROW row, defaults = NULL; + struct Table *table; + char *content[4]; + int userlistlen, i, j; + int db_enfops, db_enfvoice; + int caccess, synced_user, accessbufpos; + struct ChanUser *chanusers[chan->usercount]; + struct ChanUser *chanuser; + struct ClientSocket *bot; + int chanuser_count; + char statebuf[5]; + char accessbuf[50]; + char viscountbuf[50]; + int uaccess; + + i = 3; + if(flags & NEONSERV_CMD_NICKLIST_FLAG_VISCOUNT) { + i++; + content[3] = "VisCount"; + } + table = table_init(i, chan->usercount + 1, 0); + content[0] = get_language_string(user, "NS_NICKLIST_NICK"); + content[1] = get_language_string(user, "NS_NICKLIST_STATE"); + content[2] = get_language_string(user, "NS_NICKLIST_ACCESS"); + table_add(table, content); + + printf_mysql_query("SELECT `chanuser_access`, `user_user`, `chanuser_flags` FROM `chanusers` LEFT JOIN `users` ON `chanuser_uid` = `user_id` WHERE `chanuser_cid` = '%d'", chan->channel_id); + res = mysql_use(); + userlistlen = mysql_num_rows(res); + i = 0; + MYSQL_ROW userlist[userlistlen]; + while ((row = mysql_fetch_row(res)) != NULL) { + userlist[i++] = row; + } + + printf_mysql_query("SELECT `channel_getop`, `channel_getvoice` FROM `channels` WHERE `channel_id` = '%d'", chan->channel_id); + row = mysql_fetch_row(mysql_use()); + if(row[0] == NULL || row[1] == NULL) { + printf_mysql_query("SELECT `channel_getop`, `channel_getvoice` FROM `channels` WHERE `channel_name` = 'defaults'"); + defaults = mysql_fetch_row(mysql_use()); + } + db_enfops = atoi((row[0] ? row[0] : defaults[0])); + db_enfvoice = atoi((row[1] ? row[1] : defaults[1])); + + chanuser_count = 0; + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + if(nickmask && match(nickmask, chanuser->user->nick)) continue; + chanusers[chanuser_count++] = chanuser; + } + qsort(chanusers, chanuser_count, sizeof(struct ChanUser *), neonserv_cmd_nicklist_sort); + + caccess = getChannelAccess(user, chan); + synced_user = 0; + for(i = 0; i < chanuser_count; i++) { + chanuser = chanusers[i]; + + content[0] = chanuser->user->nick; + + j = 0; + if((chanuser->flags & CHANUSERFLAG_INVISIBLE)) statebuf[j++] = '<'; + if((chanuser->flags & CHANUSERFLAG_OPPED)) statebuf[j++] = '@'; + if((chanuser->flags & CHANUSERFLAG_HALFOPPED)) statebuf[j++] = '%'; + if((chanuser->flags & CHANUSERFLAG_VOICED)) statebuf[j++] = '+'; + statebuf[j++] = '\0'; + content[1] = statebuf; + + uaccess = 0; + if(chanuser->user->flags & USERFLAG_ISAUTHED) { + for(j = 0; j < userlistlen; j++) { + if(!stricmp(chanuser->user->auth, userlist[j][1])) { + uaccess = atoi(userlist[j][0]); + if((((chanuser->flags & CHANUSERFLAG_OPPED) && uaccess < db_enfops) || ((chanuser->flags & CHANUSERFLAG_VOICED) && uaccess < db_enfvoice)) && !isNetworkService(chanuser->user)) { + if(flags & NEONSERV_CMD_NICKLIST_FLAG_SYNCUSERS) { + if((chanuser->flags & CHANUSERFLAG_OPPED) && (db_enfops < caccess || isGodMode(user))) { + if(db_enfops >= caccess) + event->flags |= CMDFLAG_OPLOG; + uaccess = db_enfops; + } else if((chanuser->flags & CHANUSERFLAG_VOICED) && (caccess < db_enfvoice || isGodMode(user))) { + if(db_enfvoice >= caccess) + event->flags |= CMDFLAG_OPLOG; + uaccess = db_enfvoice; + } else { + //fail... + accessbufpos = sprintf(accessbuf, "\00307%d\003", uaccess); + break; + } + neonserv_cmd_nicklist_synchronize_user(chan, chanuser->user, uaccess); + accessbufpos = sprintf(accessbuf, "\00309%d\003", uaccess); + synced_user = 1; + } else { + synced_user = 1; + accessbufpos = sprintf(accessbuf, "\00307%d\003", uaccess); + } + } else if((uaccess >= db_enfops && !(chanuser->flags & CHANUSERFLAG_OPPED)) || (uaccess >= db_enfvoice && !(chanuser->flags & CHANUSERFLAG_OPPED_OR_VOICED))) + accessbufpos = sprintf(accessbuf, "\00303%d\003", uaccess); + else + accessbufpos = sprintf(accessbuf, "%d", uaccess); + break; + } + } + } + if(!uaccess && (chanuser->flags & CHANUSERFLAG_OPPED_OR_VOICED) && !isNetworkService(chanuser->user)) { + if(flags & NEONSERV_CMD_NICKLIST_FLAG_SYNCUSERS) { + if((chanuser->flags & CHANUSERFLAG_OPPED) && (db_enfops < caccess || isGodMode(user))) { + if(db_enfops >= caccess) + event->flags |= CMDFLAG_OPLOG; + uaccess = db_enfops; + } else if((chanuser->flags & CHANUSERFLAG_VOICED) && (db_enfvoice < caccess || isGodMode(user))) { + if(db_enfvoice >= caccess) + event->flags |= CMDFLAG_OPLOG; + uaccess = db_enfvoice; + } else { + uaccess = 0; + accessbufpos = sprintf(accessbuf, "\003040\003"); + } + if(uaccess && (chanuser->user->flags & USERFLAG_ISAUTHED)) { + neonserv_cmd_nicklist_synchronize_user(chan, chanuser->user, uaccess); + accessbufpos = sprintf(accessbuf, "\00309%d\003", uaccess); + synced_user = 1; + } else if(uaccess) { + accessbufpos = sprintf(accessbuf, "\003040\003"); + } + } else { + synced_user = 1; + if(((chanuser->flags & CHANUSERFLAG_OPPED) && db_enfops > uaccess) || ((chanuser->flags & CHANUSERFLAG_VOICED) && db_enfvoice > uaccess)) + accessbufpos = sprintf(accessbuf, "\003040\003"); + else + accessbufpos = sprintf(accessbuf, "0"); + } + } else if(!uaccess) + accessbufpos = sprintf(accessbuf, "0"); + j = 0; + if(isBot(chanuser->user)) { + //check if bot is secret + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(bot->user == chanuser->user) + break; + } + if(bot && !(bot->flags & SOCKET_FLAG_SECRET_BOT)) { + if(j) + accessbufpos += sprintf(accessbuf+accessbufpos, ", "); + else { + accessbufpos += sprintf(accessbuf+accessbufpos, " ("); + j = 1; + } + accessbufpos += sprintf(accessbuf+accessbufpos, "%s", get_language_string(user, "NS_NICKLIST_ACCESS_BOT")); + } + } + if(chanuser->user->flags & USERFLAG_ISIRCOP) { + if(j) + accessbufpos += sprintf(accessbuf+accessbufpos, ", "); + else { + accessbufpos += sprintf(accessbuf+accessbufpos, " ("); + j = 1; + } + accessbufpos += sprintf(accessbuf+accessbufpos, "%s", get_language_string(user, "NS_NICKLIST_ACCESS_OPER")); + } + if(j) + accessbufpos += sprintf(accessbuf+accessbufpos, ")"); + content[2] = accessbuf; + if(flags & NEONSERV_CMD_NICKLIST_FLAG_VISCOUNT) { + if(chanuser->flags & CHANUSERFLAG_PARTING) + sprintf(viscountbuf, "%d (\003P\003 %d)", chanuser->visCount, chanuser->old_visCount); + else + sprintf(viscountbuf, "%d", chanuser->visCount); + content[3] = viscountbuf; + } + table_add(table, content); + } + + //send the table + char **table_lines = table_end(table); + for(i = 0; i < table->entrys; i++) { + reply(textclient, user, table_lines[i]); + } + if(table->length == 1) + reply(textclient, user, "NS_TABLE_NONE"); + reply(textclient, user, "NS_TABLE_COUNT", table->length - 1); + table_free(table); + if(synced_user) { + if(!(flags & NEONSERV_CMD_NICKLIST_FLAG_SYNCUSERS)) + reply(textclient, user, "NS_NICKLIST_SYNC", db_enfops, db_enfvoice); + else + logEvent(event); + } +} + +static void neonserv_cmd_nicklist_synchronize_user(struct ChanNode *chan, struct UserNode *user, int caccess) { + if(!(user->flags & USERFLAG_ISAUTHED)) return; + MYSQL_RES *res; + MYSQL_ROW row; + int userid; + printf_mysql_query("SELECT `user_id` FROM `users` WHERE `user_user` = '%s'", escape_string(user->auth)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + userid = atoi(row[0]); + } else { + printf_mysql_query("INSERT INTO `users` (`user_user`) VALUES ('%s')", escape_string(user->auth)); + userid = (int) mysql_insert_id(get_mysql_conn()); + } + //check if already added + printf_mysql_query("SELECT `chanuser_access`, `chanuser_id`, `chanuser_seen` FROM `chanusers` WHERE `chanuser_cid` = '%d' AND `chanuser_uid` = '%d'", chan->channel_id, userid); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + //clvl + if(atoi(row[0]) >= caccess) return; + printf_mysql_query("UPDATE `chanusers` SET `chanuser_access` = '%d' WHERE `chanuser_id` = '%s'", caccess, row[1]); + } else + printf_mysql_query("INSERT INTO `chanusers` (`chanuser_cid`, `chanuser_uid`, `chanuser_access`) VALUES ('%d', '%d', '%d')", chan->channel_id, userid, caccess); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_noregister.c b/src/modules/NeonServ.mod/cmd_neonserv_noregister.c new file mode 100644 index 0000000..2c15f49 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_noregister.c @@ -0,0 +1,199 @@ +/* cmd_neonserv_noregister.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0] *auth/#channel +* argv[1] duration +* argv[2-*] reason +*/ + +static AUTHLOOKUP_CALLBACK(neonserv_cmd_noregister_auth_lookup); +static USERAUTH_CALLBACK(neonserv_cmd_noregister_nick_lookup); +static void neonserv_cmd_noregister_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event,char *auth, int duration, char *reason); +static void neonserv_cmd_noregister_list(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char **argv, int argc); + +struct neonserv_cmd_noregister_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + struct ChanNode *chan; + struct Event *event; + int duration; + char *reason, *nick; +}; + +CMD_BIND(neonserv_cmd_noregister) { + if(argc < 3) { + neonserv_cmd_noregister_list(client, textclient, user, chan, event, argv, argc); + return; + } + int duration = strToTime(user, argv[1]); + if(duration == 0 && strcmp(argv[1], "0")) { + reply(textclient, user, "NS_NOREGISTER_INVALID_DURATION", argv[1]); + return; + } + char *reason = merge_argv(argv, 2, argc); + MYSQL_RES *res; + MYSQL_ROW row; + if(argv[0][0] == '#') { + printf_mysql_query("SELECT `botid` FROM `bot_channels` LEFT JOIN `channels` ON `bot_channels`.`chanid` = `channels`.`channel_id` WHERE `channel_name` = '%s'", escape_string(argv[0])); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + reply(textclient, user, "NS_NOREGISTER_REGISTERED", argv[0]); + return; + } + neonserv_cmd_noregister_async1(client, textclient, user, chan, event, argv[0], duration, reason); + } else if(argv[0][0] == '*') { + //we've got an auth + argv[0]++; + printf_mysql_query("SELECT `user_user` FROM `users` WHERE `user_user` = '%s'", escape_string(argv[0])); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + neonserv_cmd_noregister_async1(client, textclient, user, chan, event, row[0], duration, reason); + } else { + //we need to create a new user... + //but first lookup the auth to check if it really exists + struct neonserv_cmd_noregister_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->chan = chan; + cache->event = event; + cache->nick = strdup(argv[0]); + cache->duration = duration; + cache->reason = strdup(reason); + lookup_authname(argv[0], module_id, neonserv_cmd_noregister_auth_lookup, cache); + } + } else { + struct UserNode *cuser = getUserByNick(argv[0]); + if(!cuser) { + cuser = createTempUser(argv[0]); + if(!cuser) { + reply(textclient, user, "NS_USER_UNKNOWN", argv[0]); + return; + } + cuser->flags |= USERFLAG_ISTMPUSER; + } + if(cuser->flags & USERFLAG_ISAUTHED) { + neonserv_cmd_noregister_async1(client, textclient, user, chan, event, cuser->auth, duration, reason); + } else { + struct neonserv_cmd_noregister_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->chan = chan; + cache->event = event; + cache->nick = strdup(user->nick); + cache->duration = duration; + cache->reason = strdup(reason); + get_userauth(cuser, module_id, neonserv_cmd_noregister_nick_lookup, cache); + } + } +} + +static AUTHLOOKUP_CALLBACK(neonserv_cmd_noregister_auth_lookup) { + struct neonserv_cmd_noregister_cache *cache = data; + if(!exists) { + //AUTH_DOES_NOT_EXIST + reply(cache->textclient, cache->user, "NS_AUTH_UNKNOWN", cache->nick); + } else + neonserv_cmd_noregister_async1(cache->client, cache->textclient, cache->user, cache->chan, cache->event, auth, cache->duration, cache->reason); + free(cache->reason); + free(cache->nick); + free(cache); +} + +static USERAUTH_CALLBACK(neonserv_cmd_noregister_nick_lookup) { + struct neonserv_cmd_noregister_cache *cache = data; + if(!user) { + //USER_DOES_NOT_EXIST + reply(cache->textclient, cache->user, "NS_USER_UNKNOWN", cache->nick); + } + else if(!(user->flags & USERFLAG_ISAUTHED)) { + //USER_NOT_AUTHED + reply(cache->textclient, cache->user, "NS_USER_NEED_AUTH", cache->nick); + } + else + neonserv_cmd_noregister_async1(cache->client, cache->textclient, cache->user, cache->chan, cache->event, user->auth, cache->duration, cache->reason); + free(cache->reason); + free(cache->nick); + free(cache); +} + +static void neonserv_cmd_noregister_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event,char *auth, int duration, char *reason) { + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `dnr_id` FROM `donotregister` WHERE `dnr_target` = '%s'", escape_string(auth)); + res = mysql_use(); + if((row = mysql_fetch_row(res)) != NULL) { + printf_mysql_query("DELETE FROM `donotregister` WHERE `dnr_id` = '%s'", row[0]); + } + int userid; + printf_mysql_query("SELECT `user_id` FROM `users` WHERE `user_user` = '%s'", escape_string(user->auth)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) + userid = atoi(row[0]); + else + userid = 0; + printf_mysql_query("INSERT INTO `donotregister` (`dnr_target`, `dnr_timeout`, `dnr_user`, `dnr_reason`) VALUES ('%s', '%lu', '%d', '%s')", escape_string(auth), (duration ? (time(0)+duration) : 0), userid, escape_string(reason)); + reply(textclient, user, "NS_NOREGISTER_DONE", auth); +} + +static void neonserv_cmd_noregister_list(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char **argv, int argc) { + struct Table *table; + int entrys = 0; + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `dnr_target`, `dnr_timeout`, `user_user`, `dnr_reason` FROM `donotregister` LEFT JOIN `users` ON `dnr_user` = `user_id` ORDER BY `dnr_target` ASC"); + res = mysql_use(); + table = table_init(4, mysql_num_rows(res) + 1, 0); + char *content[4]; + content[0] = get_language_string(user, "NS_DNR_TARGET"); + content[1] = get_language_string(user, "NS_DNR_USER"); + content[2] = get_language_string(user, "NS_DNR_EXPIRES"); + content[3] = get_language_string(user, "NS_DNR_REASON"); + table_add(table, content); + int duration; + char expires_str[MAXLEN]; + while ((row = mysql_fetch_row(res)) != NULL) { + entrys++; + content[0] = row[0]; + content[1] = row[2]; + duration = atoi(row[1]); + content[2] = (duration ? timeToStr(user, (duration - time(0)), 2, expires_str) : get_language_string(user, "NS_USERS_SEEN_NEVER")); + content[3] = row[3]; + table_add(table, content); + } + //send the table + char **table_lines = table_end(table); + int i; + for(i = 0; i < table->entrys; i++) { + reply(textclient, user, table_lines[i]); + } + if(!entrys) + reply(textclient, user, "NS_TABLE_NONE"); + table_free(table); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_op.c b/src/modules/NeonServ.mod/cmd_neonserv_op.c new file mode 100644 index 0000000..d6679bc --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_op.c @@ -0,0 +1,93 @@ +/* cmd_neonserv_op.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0-*] nicks +*/ +static USERLIST_CALLBACK(neonserv_cmd_op_userlist_lookup); +static void neonserv_cmd_op_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *nicks); + +struct neonserv_cmd_op_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + struct Event *event; + char *nicks; +}; + +CMD_BIND(neonserv_cmd_op) { + struct neonserv_cmd_op_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->event = event; + cache->nicks = strdup(merge_argv(argv, 0, argc)); + get_userlist_if_invisible(chan, module_id, neonserv_cmd_op_userlist_lookup, cache); +} + +static USERLIST_CALLBACK(neonserv_cmd_op_userlist_lookup) { + struct neonserv_cmd_op_cache *cache = data; + neonserv_cmd_op_async1(cache->client, cache->textclient, cache->user, chan, cache->event, cache->nicks); + free(cache->nicks); + free(cache); +} + +static void neonserv_cmd_op_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *nicks) { + int total_users = 0, done_users = 0; + struct UserNode *cuser; + struct ChanUser *chanuser; + struct ModeBuffer *modeBuf; + modeBuf = initModeBuffer(client, chan); + char *a, *b = nicks; + do { + a = strstr(b, " "); + if(a) *a = '\0'; + total_users++; + cuser = searchUserByNick(b); + if(!cuser) { + //check for an invisible user + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + if(!stricmp(chanuser->user->nick, b)) { + cuser = chanuser->user; + break; + } + } + if(!cuser) continue; + } else { + chanuser = getChanUser(cuser, chan); + if(!chanuser) continue; + } + done_users++; + if(chanuser->flags & CHANUSERFLAG_OPPED) continue; + modeBufferOp(modeBuf, b); + if(a) { + b = a+1; + } + } while(a); + freeModeBuffer(modeBuf); + if(done_users == total_users) + reply(textclient, user, "NS_OP_DONE", chan->name); + else + reply(textclient, user, "NS_OP_FAIL", client->user->nick); + if(done_users) + logEvent(event); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_opall.c b/src/modules/NeonServ.mod/cmd_neonserv_opall.c new file mode 100644 index 0000000..3f2646a --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_opall.c @@ -0,0 +1,78 @@ +/* cmd_neonserv_opall.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0] "force" +* argv[1] (optional) nick mask +*/ +static USERLIST_CALLBACK(neonserv_cmd_opall_userlist_lookup); +static void neonserv_cmd_opall_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *nickmask); + +struct neonserv_cmd_opall_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + struct Event *event; + char *nickmask; +}; + +CMD_BIND(neonserv_cmd_opall) { + if(!argc || strcmp(argv[0], "FORCE")) { + reply(textclient, user, "NS_OPALL_SECURITY", chan->name); + return; + } + struct neonserv_cmd_opall_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->event = event; + if(argc > 1) { + cache->nickmask = strdup(argv[1]); + } else + cache->nickmask = NULL; + get_userlist_if_invisible(chan, module_id, neonserv_cmd_opall_userlist_lookup, cache); +} + +static USERLIST_CALLBACK(neonserv_cmd_opall_userlist_lookup) { + struct neonserv_cmd_opall_cache *cache = data; + neonserv_cmd_opall_async1(cache->client, cache->textclient, cache->user, chan, cache->event, cache->nickmask); + if(cache->nickmask) + free(cache->nickmask); + free(cache); +} + +static void neonserv_cmd_opall_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *nickmask) { + int done_users = 0; + struct ChanUser *chanuser; + struct ModeBuffer *modeBuf; + modeBuf = initModeBuffer(client, chan); + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + if(nickmask && match(nickmask, chanuser->user->nick)) continue; + if(chanuser->flags & CHANUSERFLAG_OPPED) continue; + modeBufferOp(modeBuf, chanuser->user->nick); + done_users++; + } + freeModeBuffer(modeBuf); + reply(textclient, user, "NS_OPALL_DONE", done_users, chan->name); + if(done_users) + logEvent(event); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_oplog.c b/src/modules/NeonServ.mod/cmd_neonserv_oplog.c new file mode 100644 index 0000000..06e33e8 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_oplog.c @@ -0,0 +1,57 @@ +/* cmd_neonserv_oplog.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0] time +* argv[1-*] match +*/ + +CMD_BIND(neonserv_cmd_oplog) { + char *str_match; + int duration = (argc ? strToTime(user, argv[0]) : 0); + if(argc > (duration ? 1 : 0)) + str_match = merge_argv(argv, (duration ? 1 : 0), argc); + else + str_match = ""; + if(!duration) duration = (60*60*24); + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `godlog_time`, `user_user`, `channel_name`, `godlog_cmd` FROM `godlog` LEFT JOIN `channels` ON `godlog_cid` = `channel_id` LEFT JOIN `users` ON `godlog_uid` = `user_id` WHERE `godlog_time` > '%lu' ORDER BY `godlog_time` ASC", ((unsigned long) time(0) - duration)); + res = mysql_use(); + int skip = mysql_num_rows(res) - 100; + int count = 0; + char timeBuf[50]; + struct tm *timeinfo; + time_t event_time; + if(skip < 0) skip = 0; + reply(textclient, user, "NS_EVENTS_HEADER"); + while ((row = mysql_fetch_row(res)) != NULL) { + if(skip) { + skip--; + continue; + } + if(*str_match && match(str_match, row[3])) continue; + count++; + event_time = (time_t) atol(row[0]); + timeinfo = localtime(&event_time); + strftime(timeBuf, 80, "%X %x", timeinfo); + reply(textclient, user, "[%s] [%s%s%s]: %s", timeBuf, row[1], (row[2] ? ":" : ""), (row[2] ? row[2] : ""), row[3]); + } + reply(textclient, user, "NS_TABLE_COUNT", count); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_peek.c b/src/modules/NeonServ.mod/cmd_neonserv_peek.c new file mode 100644 index 0000000..fe600cd --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_peek.c @@ -0,0 +1,89 @@ +/* cmd_neonserv_peek.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* no parameters +*/ +static USERLIST_CALLBACK(neonserv_cmd_peek_userlist_lookup); +static void neonserv_cmd_peek_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan); + +struct neonserv_cmd_peek_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; +}; + +CMD_BIND(neonserv_cmd_peek) { + struct neonserv_cmd_peek_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + get_userlist_if_invisible(chan, module_id, neonserv_cmd_peek_userlist_lookup, cache); +} + +static USERLIST_CALLBACK(neonserv_cmd_peek_userlist_lookup) { + struct neonserv_cmd_peek_cache *cache = data; + neonserv_cmd_peek_async1(cache->client, cache->textclient, cache->user, chan); + free(cache); +} + +static void neonserv_cmd_peek_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan) { + reply(textclient, user, "NS_PEEK_HEADER", chan->name); + reply(textclient, user, "NS_PEEK_TOPIC", chan->topic); + char tmpStr[MAXLEN]; + getModeString(chan->modes, tmpStr); + reply(textclient, user, "NS_PEEK_MODES", tmpStr); + struct ChanUser *chanuser; + int with_halfops = get_int_field("General.have_halfop"); + int op_count = 0, halfop_count = 0, voice_count = 0, normal_count = 0, invi_count = 0; + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + if(chanuser->flags & CHANUSERFLAG_OPPED) + op_count++; + else if(with_halfops && (chanuser->flags & CHANUSERFLAG_HALFOPPED)) + halfop_count++; + else if(chanuser->flags & CHANUSERFLAG_VOICED) + voice_count++; + else if(chanuser->flags & CHANUSERFLAG_INVISIBLE) + invi_count++; + else + normal_count++; + } + if(with_halfops) + reply(textclient, user, "NS_PEEK_USERS_HALFOP", op_count+halfop_count+voice_count+invi_count+normal_count, op_count, halfop_count, voice_count, normal_count, invi_count); + else + reply(textclient, user, "NS_PEEK_USERS", op_count+voice_count+invi_count+normal_count, op_count, voice_count, normal_count, invi_count); + int tmpStrPos = 0; + int headerlen = 10 + strlen(user->nick); + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + if(chanuser->flags & CHANUSERFLAG_OPPED) { + if(tmpStrPos + headerlen + strlen(chanuser->user->nick) + 2 >= 512) { + //clear buffer + reply(textclient, user, "%s", tmpStr); + tmpStrPos = 0; + } + tmpStrPos += sprintf(tmpStr + tmpStrPos, (tmpStrPos ? ", %s" : "%s"), chanuser->user->nick); + } + } + if(tmpStrPos) { + reply(textclient, user, "%s", tmpStr); + } +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_recover.c b/src/modules/NeonServ.mod/cmd_neonserv_recover.c new file mode 100644 index 0000000..814d9ab --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_recover.c @@ -0,0 +1,76 @@ +/* cmd_neonserv_recover.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0] - channel +*/ +CMD_BIND(neonserv_cmd_recover) { + MYSQL_RES *res; + MYSQL_ROW row, row2; + char *channel = argv[0]; + if(!is_valid_chan(channel)) { + reply(textclient, user, "NS_INVALID_CHANNEL_NAME", argv[0]); + return; + } + int chanid; + printf_mysql_query("SELECT `channel_id` FROM `channels` WHERE `channel_name` = '%s'", escape_string(channel)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + chanid = atoi(row[0]); + } else { + reply(textclient, user, "NS_UNREGISTER_NOT_REGISTERED", argv[0], client->user->nick); + return; + } + printf_mysql_query("SELECT `botid`, `bot_channels`.`id` FROM `bot_channels` LEFT JOIN `bots` ON `bot_channels`.`botid` = `bots`.`id` WHERE `chanid` = '%d' AND `botclass` = '%d'", chanid, client->botid); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + reply(textclient, user, "NS_REGISTER_ALREADY", argv[0], client->user->nick); + return; + } + printf_mysql_query("SELECT `id`, `max_channels`, `defaulttrigger` FROM `bots` WHERE `botclass` = '%d' ORDER BY `register_priority` DESC", client->botid); + res = mysql_use(); + int botid = 0; + char *bottrigger; + while ((row = mysql_fetch_row(res)) != NULL) { + //check channel count + printf_mysql_query("SELECT COUNT(*) FROM `bot_channels` WHERE `botid` = '%s'", row[0]); + row2 = mysql_fetch_row(mysql_use()); + if(atoi(row2[0]) < atoi(row[1])) { + botid = atoi(row[0]); + bottrigger = row[2]; + break; + } + } + if(!botid) { + reply(textclient, user, "NS_REGISTER_FULL"); + return; + } + struct ClientSocket *bot; + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(bot->clientid == botid) + break; + } + if(bot) { + putsock(bot, "JOIN %s", channel); + } else + reply(textclient, user, "NS_REGISTER_DISCONNECTED"); + printf_mysql_query("INSERT INTO `bot_channels` (`botid`, `chanid`, `trigger`) VALUES ('%d', '%d', '%s')", botid, chanid, bottrigger); + reply(textclient, user, "NS_RECOVER_DONE", channel); + logEvent(event); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_rename.c b/src/modules/NeonServ.mod/cmd_neonserv_rename.c new file mode 100644 index 0000000..34b72b0 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_rename.c @@ -0,0 +1,68 @@ +/* cmd_neonserv_rename.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0] old auth +* argv[1] new auth +*/ + +struct neonserv_cmd_rename_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + struct Event *event; + char *oldauth, *newauth; +}; + +static AUTHLOOKUP_CALLBACK(neonserv_cmd_rename_auth_lookup); +static void neonserv_cmd_rename_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct Event *event, char *oldauth, char *newauth); + +CMD_BIND(neonserv_cmd_rename) { + struct neonserv_cmd_rename_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->event = event; + cache->oldauth = strdup(argv[0]); + cache->newauth = strdup(argv[1]); + lookup_authname(argv[1], module_id, neonserv_cmd_rename_auth_lookup, cache); +} + +static AUTHLOOKUP_CALLBACK(neonserv_cmd_rename_auth_lookup) { + struct neonserv_cmd_rename_cache *cache = data; + if(!exists) { + //AUTH_DOES_NOT_EXIST + reply(cache->textclient, cache->user, "NS_AUTH_UNKNOWN", cache->newauth); + } else + neonserv_cmd_rename_async1(cache->client, cache->textclient, cache->user, cache->event, cache->oldauth, auth); + free(cache->oldauth); + free(cache->newauth); + free(cache); +} + +static void neonserv_cmd_rename_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct Event *event, char *oldauth, char *newauth) { + if(renameAccount(oldauth, newauth)) { + reply(textclient, user, "NS_RENAME_DONE", oldauth, newauth); + } else { + reply(textclient, user, "NS_RENAME_FAIL", oldauth); + } +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_resync.c b/src/modules/NeonServ.mod/cmd_neonserv_resync.c new file mode 100644 index 0000000..46a095e --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_resync.c @@ -0,0 +1,167 @@ +/* cmd_neonserv_resync.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0] - (optional) usermask +* argv[1] - (optional) min access +* argv[2] - (optional) max access +* argv[1/3] - (optional) FORCE (override NoAutoOp) +*/ +static USERLIST_CALLBACK(neonserv_cmd_resync_userlist_lookup); +static void neonserv_cmd_resync_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, char *usermask, int min_access, int max_access, char override_noautoop); + +struct neonserv_cmd_resync_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + char *usermask; + int min_access; + int max_access; + char override_noautoop; +}; + +CMD_BIND(neonserv_cmd_resync) { + int min_access = 0, max_access = 500; + char *usermask = NULL; + char override_noautoop = 0; + if(argc > 0) { + usermask = argv[0]; + if(argc > 2) { + min_access = atoi(argv[1]); + max_access = atoi(argv[2]); + if(argc > 3) + override_noautoop = (!stricmp(argv[3], "FORCE") ? 1 : 0); + } else if(argc > 1) + override_noautoop = (!stricmp(argv[1], "FORCE") ? 1 : 0); + } + struct neonserv_cmd_resync_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->usermask = (usermask ? strdup(usermask) : NULL); + cache->min_access = min_access; + cache->max_access = max_access; + cache->override_noautoop = override_noautoop; + get_userlist_with_invisible(chan, module_id, neonserv_cmd_resync_userlist_lookup, cache); +} + +static USERLIST_CALLBACK(neonserv_cmd_resync_userlist_lookup) { + struct neonserv_cmd_resync_cache *cache = data; + neonserv_cmd_resync_async1(cache->client, cache->textclient, cache->user, chan, cache->usermask, cache->min_access, cache->max_access, cache->override_noautoop); + if(cache->usermask) + free(cache->usermask); + free(cache); +} + +static void neonserv_cmd_resync_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, char *usermask, int min_access, int max_access, char override_noautoop) { + MYSQL_RES *res; + MYSQL_ROW row, defaults = NULL; + int i; + int resync_op = 1; + int with_halfop = get_int_field("General.have_halfop"); + int resync_halfop = with_halfop; + int resync_voice = 1; + if(usermask && usermask[0] == '@') { + resync_voice = 0; + resync_halfop = 0; + usermask++; + if(!*usermask) usermask = NULL; + } else if(usermask && with_halfop && usermask[0] == 'h') { + resync_op = 0; + resync_voice = 0; + usermask++; + if(!*usermask) usermask = NULL; + } else if(usermask && usermask[0] == '+') { + resync_op = 0; + resync_halfop = 0; + usermask++; + if(!*usermask) usermask = NULL; + } + struct ChanUser *chanuser; + int db_enfops, db_enfhalfop, db_enfvoice; + printf_mysql_query("SELECT `channel_getop`, `channel_getvoice`, `channel_gethalfop` FROM `channels` WHERE `channel_id` = '%d'", chan->channel_id); + row = mysql_fetch_row(mysql_use()); + if(row[0] == NULL || row[1] == NULL) { + printf_mysql_query("SELECT `channel_getop`, `channel_getvoice`, `channel_gethalfop` FROM `channels` WHERE `channel_name` = 'defaults'"); + defaults = mysql_fetch_row(mysql_use()); + } + db_enfops = atoi((row[0] ? row[0] : defaults[0])); + db_enfvoice = atoi((row[1] ? row[1] : defaults[1])); + db_enfhalfop = (with_halfop ? atoi((row[2] ? row[2] : defaults[2])) : 0); + printf_mysql_query("SELECT `chanuser_access`, `user_user`, `chanuser_flags` FROM `chanusers` LEFT JOIN `users` ON `chanuser_uid` = `user_id` WHERE `chanuser_cid` = '%d' ORDER BY `chanuser_access` DESC, `user_user` ASC", chan->channel_id); + res = mysql_use(); + char *db_users[mysql_num_rows(res)]; + int db_access[mysql_num_rows(res)]; + int db_flags[mysql_num_rows(res)]; + int db_count = 0; + while ((row = mysql_fetch_row(res)) != NULL) { + db_users[db_count] = row[1]; + db_access[db_count] = atoi(row[0]); + db_flags[db_count] = atoi(row[2]); + db_count++; + } + int caccess, cflags; + struct ModeBuffer *modeBuf; + modeBuf = initModeBuffer(client, chan); + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + caccess = 0; + cflags = 0; + if((chanuser->user->flags & USERFLAG_ISAUTHED)) { + for(i = 0; i < db_count; i++) { + if(!stricmp(db_users[i], chanuser->user->auth)) { + caccess = db_access[i]; + cflags = db_flags[i]; + if(cflags & DB_CHANUSER_SUSPENDED) + caccess = 0; + break; + } + } + } + if((usermask && *usermask && match(usermask, chanuser->user->nick)) || caccess < min_access || caccess > max_access) continue; + if(caccess >= db_enfops) { + if(!(chanuser->flags & CHANUSERFLAG_OPPED) && resync_op && (override_noautoop || !(cflags & DB_CHANUSER_NOAUTOOP))) + modeBufferOp(modeBuf, chanuser->user->nick); + } else if(with_halfop && caccess >= db_enfhalfop) { + if((chanuser->flags & CHANUSERFLAG_OPPED) && resync_op && !(chanuser->user->flags & (USERFLAG_ISBOT | USERFLAG_ISIRCOP))) + modeBufferDeop(modeBuf, chanuser->user->nick); + if(!(chanuser->flags & CHANUSERFLAG_HALFOPPED) && resync_halfop && (override_noautoop || !(cflags & DB_CHANUSER_NOAUTOOP))) + modeBufferHalfop(modeBuf, chanuser->user->nick); + } else if(caccess >= db_enfvoice) { + if((chanuser->flags & CHANUSERFLAG_OPPED) && resync_op && !(chanuser->user->flags & (USERFLAG_ISBOT | USERFLAG_ISIRCOP))) + modeBufferDeop(modeBuf, chanuser->user->nick); + if((chanuser->flags & CHANUSERFLAG_HALFOPPED) && resync_halfop && !(chanuser->user->flags & (USERFLAG_ISBOT | USERFLAG_ISIRCOP))) + modeBufferDehalfop(modeBuf, chanuser->user->nick); + if(!(chanuser->flags & CHANUSERFLAG_VOICED) && resync_voice && (override_noautoop || !(cflags & DB_CHANUSER_NOAUTOOP))) + modeBufferVoice(modeBuf, chanuser->user->nick); + } else { + if((chanuser->flags & CHANUSERFLAG_OPPED) && resync_op && !(chanuser->user->flags & (USERFLAG_ISBOT | USERFLAG_ISIRCOP))) + modeBufferDeop(modeBuf, chanuser->user->nick); + if((chanuser->flags & CHANUSERFLAG_HALFOPPED) && resync_halfop && !(chanuser->user->flags & (USERFLAG_ISBOT | USERFLAG_ISIRCOP))) + modeBufferDehalfop(modeBuf, chanuser->user->nick); + if((chanuser->flags & CHANUSERFLAG_VOICED) && resync_voice && !(chanuser->user->flags & (USERFLAG_ISBOT | USERFLAG_ISIRCOP))) + modeBufferDevoice(modeBuf, chanuser->user->nick); + } + + } + freeModeBuffer(modeBuf); + reply(textclient, user, "NS_RESYNC_DONE", chan->name); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_search.c b/src/modules/NeonServ.mod/cmd_neonserv_search.c new file mode 100644 index 0000000..f3ea659 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_search.c @@ -0,0 +1,221 @@ +/* cmd_neonserv_search.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +#define CMD_SEARCH_FLAG_HAS_NODELETE 0x001 +#define CMD_SEARCH_FLAG_NOT_NODELETE 0x002 +#define CMD_SEARCH_FLAG_HAS_SUSPENDED 0x004 +#define CMD_SEARCH_FLAG_NOT_SUSPENDED 0x008 +#define CMD_SEARCH_FLAG_IS_JOINED 0x010 /* state */ +#define CMD_SEARCH_FLAG_NOT_JOINED 0x020 /* state */ +#define CMD_SEARCH_FLAG_IS_OPPED 0x040 /* state */ +#define CMD_SEARCH_FLAG_NOT_OPPED 0x080 /* state */ +#define CMD_SEARCH_FLAG_IS_VOICED 0x100 /* state */ +#define CMD_SEARCH_FLAG_NOT_VOICED 0x200 /* state */ +#define CMD_SEARCH_FLAG_IS_ZOMBIE 0x400 +#define CMD_SEARCH_FLAG_NOT_ZOMBIE 0x800 + +#define CMD_SEARCH_FLAG_STATES 0x3f0 + +struct neonserv_cmd_search_criteria { + char *name; + char *registrar; + char *onchan; + unsigned int flags : 16; + unsigned int unvisited; + unsigned int registered; + unsigned int limit : 16; +}; + +static char neonserv_cmd_search_zombie = 0; + +CMD_BIND(neonserv_cmd_search) { + //ok parse the criterias + struct neonserv_cmd_search_criteria criteria; + memset(&criteria, 0, sizeof(criteria)); + criteria.limit = 50; + int i, show_chans = 0, positive; + if(!stricmp(argv[0], "print")) { + show_chans = 1; + } + for(i = 1; i < argc; i += 2) { + if(argc <= i+1) { + reply(textclient, user, "MODCMD_LESS_PARAM_COUNT"); + return; + } + if(!stricmp(argv[i], "name")) criteria.name = argv[i+1]; + else if(!stricmp(argv[i], "registrar")) criteria.registrar = argv[i+1]; + else if(!stricmp(argv[i], "onchan")) criteria.onchan = argv[i+1]; + else if(!stricmp(argv[i], "unvisited")) criteria.unvisited = strToTime(user, argv[i+1]); + else if(!stricmp(argv[i], "registered")) criteria.registered = strToTime(user, argv[i+1]); + else if(!stricmp(argv[i], "flags")) { + if(argv[i+1][0] == '+') { + positive = 1; + argv[i+1]++; + } else if(argv[i+1][0] == '-') { + positive = 0; + argv[i+1]++; + } else + positive = 1; + if(!stricmp(argv[i+1], "nodelete")) { + if(positive) + criteria.flags |= CMD_SEARCH_FLAG_HAS_NODELETE; + else + criteria.flags |= CMD_SEARCH_FLAG_NOT_NODELETE; + } else if(!stricmp(argv[i+1], "suspended")) { + if(positive) + criteria.flags |= CMD_SEARCH_FLAG_HAS_SUSPENDED; + else + criteria.flags |= CMD_SEARCH_FLAG_NOT_SUSPENDED; + } else if(!stricmp(argv[i+1], "zombie")) { + if(positive) + criteria.flags |= CMD_SEARCH_FLAG_IS_ZOMBIE; + else + criteria.flags |= CMD_SEARCH_FLAG_NOT_ZOMBIE; + } + } + else if(!stricmp(argv[i], "state")) { + if(argv[i+1][0] == '+') { + positive = 1; + argv[i+1]++; + } else if(argv[i+1][0] == '-') { + positive = 0; + argv[i+1]++; + } else + positive = 1; + if(!stricmp(argv[i+1], "joined")) { + if(positive) + criteria.flags |= CMD_SEARCH_FLAG_IS_JOINED; + else + criteria.flags |= CMD_SEARCH_FLAG_NOT_JOINED; + } else if(!stricmp(argv[i+1], "opped")) { + if(positive) + criteria.flags |= CMD_SEARCH_FLAG_IS_OPPED; + else + criteria.flags |= CMD_SEARCH_FLAG_NOT_OPPED; + } else if(!stricmp(argv[i+1], "voiced")) { + if(positive) + criteria.flags |= CMD_SEARCH_FLAG_IS_VOICED; + else + criteria.flags |= CMD_SEARCH_FLAG_NOT_VOICED; + } + } + else if(!stricmp(argv[i], "limit")) { + criteria.limit = atoi(argv[i+1]); + } + } + int matches = 0; + if((criteria.flags & (CMD_SEARCH_FLAG_IS_ZOMBIE | CMD_SEARCH_FLAG_NOT_ZOMBIE))) { + if(neonserv_cmd_search_zombie) { + reply(textclient, user, "NS_SEARCH_ZOMBIE_SCAN_IN_PROGRESS"); + return; + } + neonserv_cmd_search_zombie = 1; + } + + reply(textclient, user, "NS_SEARCH_HEADER"); + MYSQL_RES *res, *res2; + MYSQL_ROW row, row2; + printf_mysql_query("SELECT `channel_name`, `user_user`, `channel_registered`, `channel_nodelete`, `suspended`, `channel_id` FROM `bot_channels` LEFT JOIN `bots` ON `bots`.`id` = `botid` LEFT JOIN `channels` ON `chanid` = `channel_id` LEFT JOIN `users` ON `channel_registrator` = `user_id` WHERE `botclass` = '%d' AND `active` = '1'", client->botid); + res = mysql_use(); + struct ChanNode *channel; + while ((row = mysql_fetch_row(res)) != NULL) { + if((criteria.flags & (CMD_SEARCH_FLAG_IS_ZOMBIE | CMD_SEARCH_FLAG_NOT_ZOMBIE))) { + channel = getChanByName(row[0]); + if(channel) + channel->flags |= CHANFLAG_SCRIPTFLAG1; + if(show_chans && matches == criteria.limit) { + //too many + continue; + } + } else { + if(show_chans && matches == criteria.limit) { + //too many + break; + } + } + if((criteria.flags & CMD_SEARCH_FLAG_IS_ZOMBIE)) continue; + if(criteria.name && match(criteria.name, row[0])) continue; + if(criteria.registrar && row[1] && match(criteria.registrar, row[1])) continue; + if(criteria.unvisited) { + printf_mysql_query("SELECT `chanuser_seen` FROM `chanusers` WHERE `chanuser_cid` = '%s' ORDER BY `chanuser_seen` DESC LIMIT 1", row[5]); + res2 = mysql_use(); + row2 = mysql_fetch_row(res2); + if(!row2) continue; + if((time(0) - atoi(row2[0])) < criteria.unvisited) continue; + } + if(criteria.registered && (time(0) - atoi(row[2])) < criteria.registered) continue; + + if((criteria.flags & CMD_SEARCH_FLAG_HAS_NODELETE) && strcmp(row[3], "1")) continue; + if((criteria.flags & CMD_SEARCH_FLAG_NOT_NODELETE) && strcmp(row[3], "0")) continue; + if((criteria.flags & CMD_SEARCH_FLAG_HAS_SUSPENDED) && strcmp(row[4], "1")) continue; + if((criteria.flags & CMD_SEARCH_FLAG_NOT_SUSPENDED) && strcmp(row[4], "0")) continue; + if((criteria.flags & CMD_SEARCH_FLAG_STATES) || criteria.onchan) { + if(!(criteria.flags & (CMD_SEARCH_FLAG_IS_ZOMBIE | CMD_SEARCH_FLAG_NOT_ZOMBIE))) + channel = getChanByName(row[0]); + if(criteria.onchan) { + if(!channel) continue; + struct ChanUser *chanuser = NULL; + for(chanuser = getChannelUsers(channel, NULL); chanuser; chanuser = getChannelUsers(channel, chanuser)) { + if(!match(criteria.onchan, chanuser->user->nick)) break; + } + if(!chanuser) continue; + } + if((criteria.flags & CMD_SEARCH_FLAG_IS_JOINED) && !channel) continue; + if((criteria.flags & CMD_SEARCH_FLAG_NOT_JOINED) && channel) continue; + if(channel && (criteria.flags & (CMD_SEARCH_FLAG_IS_OPPED | CMD_SEARCH_FLAG_NOT_OPPED | CMD_SEARCH_FLAG_IS_VOICED | CMD_SEARCH_FLAG_NOT_VOICED))) { + int flags = 0; + struct ClientSocket *bot; + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(bot->botid == client->botid) { + struct ChanUser *chanuser = getChanUser(bot->user, channel); + if(chanuser) { + flags = chanuser->flags; + break; + } + } + } + if((criteria.flags & CMD_SEARCH_FLAG_IS_OPPED) && !(flags & CHANUSERFLAG_OPPED)) continue; + if((criteria.flags & CMD_SEARCH_FLAG_NOT_OPPED) && (flags & CHANUSERFLAG_OPPED)) continue; + if((criteria.flags & CMD_SEARCH_FLAG_IS_VOICED) && !(flags & CHANUSERFLAG_VOICED)) continue; + if((criteria.flags & CMD_SEARCH_FLAG_NOT_VOICED) && (flags & CHANUSERFLAG_VOICED)) continue; + } + } + matches++; + //output + if(show_chans) { + reply(textclient, user, "%s", row[0]); + } + } + if((criteria.flags & (CMD_SEARCH_FLAG_IS_ZOMBIE | CMD_SEARCH_FLAG_NOT_ZOMBIE))) { + struct ChanUser *chanuser; + for(chanuser = getUserChannels(client->user, NULL); chanuser; chanuser = getUserChannels(client->user, chanuser)) { + channel = chanuser->chan; + if(channel->flags & CHANFLAG_SCRIPTFLAG1) { + channel->flags &= ~CHANFLAG_SCRIPTFLAG1; + } else if((criteria.flags & CMD_SEARCH_FLAG_IS_ZOMBIE)) { + matches++; + if(show_chans) { + reply(textclient, user, "%s", channel->name); + } + } + } + } + neonserv_cmd_search_zombie = 0; + reply(textclient, user, "NS_TABLE_COUNT", matches); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_set.c b/src/modules/NeonServ.mod/cmd_neonserv_set.c new file mode 100644 index 0000000..692823e --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_set.c @@ -0,0 +1,536 @@ +/* cmd_neonserv_set.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +typedef char* neonserv_cmd_set_function(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument); +static void neonserv_cmd_set_setting(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, int setting, char *argument); +static char* neonserv_cmd_set_trigger(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument); +static char* neonserv_cmd_set_modes(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument); +static char* neonserv_cmd_set_dynlimit(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument); +static char* neonserv_cmd_set_nodelete(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument); +static char* neonserv_cmd_set_backupbot(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument); + +#define NS_VALID_FUNCTION 0x01 +#define NS_VALID_STRING 0x02 +#define NS_VALID_ACCESS 0x04 +#define NS_VALID_NO501 0x08 +#define NS_VALID_OPTIONS 0x10 +#define NS_VALID_NUMERIC 0x20 +#define NS_VALID_BOOLEAN 0x40 +#define NS_VALID_IF_HALFOP 0x80 + +#define NS_HAS_OPT 0x100 /* options (SET_OPTION_{NAME}_{VALUE}) */ +#define NS_HAS_HELP 0x200 /* help (SET_HELP_{NAME}) - only shown if help is requested */ + +static const struct { + const char *setting; + const char *chanfield; + unsigned int valid; + void *parameter; +} channel_settings[] = { + {"TRIGGER", NULL, NS_VALID_FUNCTION, neonserv_cmd_set_trigger}, + {"DEFAULTTOPIC", "channel_defaulttopic", NS_VALID_STRING, NULL}, + {"TOPICMASK", "channel_topicmask", NS_VALID_STRING, NULL}, + {"ADVANCEDTOPIC", "channel_exttopic", NS_VALID_BOOLEAN | NS_HAS_OPT, NULL}, + {"GREETING", "channel_greeting", NS_VALID_STRING, NULL}, + {"USERGREETING", "channel_usergreeting", NS_VALID_STRING, NULL}, + {"USERINFO", "channel_userinfo", NS_VALID_ACCESS | NS_HAS_HELP, NULL}, + {"WIPEINFO", "channel_wipeinfo", NS_VALID_ACCESS | NS_HAS_HELP, NULL}, + {"MODES", "channel_modes", NS_VALID_FUNCTION, neonserv_cmd_set_modes}, + {"INVITEME", "channel_getinvite", NS_VALID_ACCESS, NULL}, + {"GIVEOPS", "channel_getop", NS_VALID_ACCESS | NS_HAS_HELP, NULL}, + {"GIVEHALFOPS", "channel_gethalfop", NS_VALID_ACCESS | NS_HAS_HELP | NS_VALID_IF_HALFOP, NULL}, + {"GIVEVOICE", "channel_getvoice", NS_VALID_ACCESS | NS_HAS_HELP, NULL}, + {"ENFOPS", "channel_canop", NS_VALID_ACCESS | NS_HAS_HELP, NULL}, + {"ENFHALFOPS", "channel_canhalfop", NS_VALID_ACCESS | NS_HAS_HELP | NS_VALID_IF_HALFOP, NULL}, + {"ENFVOICE", "channel_canvoice", NS_VALID_ACCESS | NS_HAS_HELP, NULL}, + {"KICK", "channel_cankick", NS_VALID_ACCESS | NS_HAS_HELP, NULL}, + {"BAN", "channel_canban", NS_VALID_ACCESS | NS_HAS_HELP, NULL}, + {"STATICBAN", "channel_staticban", NS_VALID_ACCESS | NS_HAS_HELP, NULL}, + {"PUBCMD", "channel_pubcmd", NS_VALID_ACCESS, NULL}, + {"ENFMODES", "channel_enfmodes", NS_VALID_ACCESS, NULL}, + {"ENFTOPIC", "channel_enftopic", NS_VALID_ACCESS, NULL}, + {"TOPICSNARF", "channel_topicsnarf", NS_VALID_ACCESS | NS_HAS_HELP, NULL}, + {"CHANGETOPIC", "channel_changetopic", NS_VALID_ACCESS | NS_HAS_HELP, NULL}, + {"SETTERS", "channel_setters", NS_VALID_ACCESS | NS_VALID_NO501 | NS_HAS_HELP, NULL}, + {"ADDUSER", "channel_canadd", NS_VALID_ACCESS | NS_HAS_HELP, NULL}, + {"DELUSER", "channel_candel", NS_VALID_ACCESS | NS_HAS_HELP, NULL}, + {"CLVL", "channel_canclvl", NS_VALID_ACCESS | NS_HAS_HELP, NULL}, + {"RESYNC", "channel_canresync", NS_VALID_ACCESS | NS_HAS_HELP, NULL}, + {"SUSPEND", "channel_cansuspend", NS_VALID_ACCESS | NS_HAS_HELP, NULL}, + {"NOTICEUSERS", "channel_notice", NS_VALID_ACCESS, NULL}, + {"NOTICEREACTION", "channel_noticereaction", NS_VALID_OPTIONS | NS_HAS_OPT, "4"}, + {"CTCPUSERS", "channel_ctcp", NS_VALID_ACCESS, NULL}, + {"CTCPREACTION", "channel_ctcpreaction", NS_VALID_OPTIONS | NS_HAS_OPT, "4"}, + {"PROTECT", "channel_protect", NS_VALID_OPTIONS | NS_HAS_OPT, "4"}, + {"TOYS", "channel_toys", NS_VALID_OPTIONS | NS_HAS_OPT, "3"}, + {"DYNLIMIT", "channel_dynlimit", NS_VALID_NUMERIC | NS_VALID_FUNCTION | NS_HAS_OPT, neonserv_cmd_set_dynlimit}, + {"NODELETE", "channel_nodelete", NS_VALID_BOOLEAN | NS_VALID_FUNCTION, neonserv_cmd_set_nodelete}, + {"BACKUPBOT", NULL, NS_VALID_BOOLEAN | NS_VALID_FUNCTION, neonserv_cmd_set_backupbot}, + {NULL, NULL, 0, NULL} +}; + +#define MAX_QUERY_LEN 1024 +CMD_BIND(neonserv_cmd_set) { + int i, j; + if(argc && !strcmp(argv[0], "defaults")) { + //reset channel settings + int uaccess = getChannelAccess(user, chan); + if(uaccess < 500) { + if(isGodMode(user)) { + event->flags |= CMDFLAG_OPLOG; + } else { + reply(textclient, user, "NS_SET_DEFAULTS_OWNER", chan->name); + return; + } + } + int seed = 0; + char *tmp; + static char defaultskey[16]; + for(tmp = user->auth; *tmp; tmp++) + seed = (seed * 0xEECE66DL ^ ((*tmp << 24) | (*tmp << 16) | (*tmp << 8) | *tmp)); + for(tmp = chan->name; *tmp; tmp++) + seed = (seed * 0xEECE66DL ^ ((*tmp << 24) | (*tmp << 16) | (*tmp << 8) | *tmp)); + sprintf(defaultskey, "%08x", seed); + if(argc > 1 && !strcmp(argv[1], defaultskey)) { + char query[MAX_QUERY_LEN]; + int querypos = 0; + i = 0; + while(channel_settings[i].setting) { + if(channel_settings[i].chanfield) + querypos += sprintf(query + querypos, "`%s` = NULL, ", channel_settings[i].chanfield); + i++; + } + if(querypos) { + query[querypos-2] = '\0'; + } + printf_mysql_query("UPDATE `channels` SET %s WHERE `channel_id` = '%d'", query, chan->channel_id); + reply(textclient, user, "NS_SET_DEFAULTS_DONE", chan->name); + logEvent(event); + } else { + reply(textclient, user, "NS_SET_DEFAULTS_CODE", chan->name, defaultskey); + } + } else if(argc && strcmp(argv[0], "help")) { + //find the correct command + i = 0; + j = 0; + char *args = (argc > 1 ? merge_argv(argv, 1, argc) : NULL); + int with_halfops = get_int_field("General.have_halfop"); + while(channel_settings[i].setting) { + if(!stricmp(channel_settings[i].setting, argv[0]) && (!(channel_settings[i].valid & NS_VALID_IF_HALFOP) || with_halfops)) { + //setting found + if(!stricmp(channel_settings[i].setting, "BACKUPBOT")) { + char setting[128]; + sprintf(setting, "modules.%s.channel_backup_setting", get_module_name(module_id)); + if(!get_int_field(setting)) { + i++; + continue; + } + } + if(channel_settings[i].valid & NS_VALID_FUNCTION) { + neonserv_cmd_set_function *func = channel_settings[i].parameter; + func(client, textclient, user, chan, event, channel_settings[i].setting, args); + } else { + neonserv_cmd_set_setting(client, textclient, user, chan, event, i, args); + } + j = 1; + break; + } + i++; + } + if(j == 0) { + //unknown setting + reply(textclient, user, "NS_SET_UNKNOWN_SETTING", argv[0]); + } + } else { + char query[MAX_QUERY_LEN], *value, *org_value, *tmp, nameBuf[64]; + int querypos = 0; + MYSQL_RES *res, *defaults_res; + MYSQL_ROW row, defaults; + struct Table *table; + char *content[2]; + int with_halfops = get_int_field("General.have_halfop"); + int channel_backup_setting; + i = 0; + j = 0; + while(channel_settings[i].setting) { + if((channel_settings[i].valid & NS_VALID_IF_HALFOP) && !with_halfops) { + j++; + i++; + continue; + } + if(!stricmp(channel_settings[i].setting, "BACKUPBOT")) { + char setting[128]; + sprintf(setting, "modules.%s.channel_backup_setting", get_module_name(module_id)); + channel_backup_setting = get_int_field(setting); + if(!channel_backup_setting) { + i++; + j++; + continue; + } + } + if(channel_settings[i].chanfield) + querypos += sprintf(query + querypos, ", `%s`", channel_settings[i].chanfield); + i++; + } + table = table_init(2, i-j, 0); + table_set_bold(table, 0, 1); + printf_mysql_query("SELECT `channel_id` %s FROM `channels` WHERE `channel_name` = 'defaults'", query); + defaults_res = mysql_use(); + defaults = mysql_fetch_row(defaults_res); + printf_mysql_query("SELECT `channel_name` %s FROM `channels` WHERE `channel_id` = '%d'", query, chan->channel_id); + res = mysql_use(); + row = mysql_fetch_row(res); + i = 0; + j = 0; + reply(textclient, user, "NS_SET_HEADER", chan->name); + while(channel_settings[i].setting) { + if((channel_settings[i].valid & NS_VALID_IF_HALFOP) && !with_halfops) { + i++; + continue; + } + if(!stricmp(channel_settings[i].setting, "BACKUPBOT") && !channel_backup_setting) { + i++; + continue; + } + if(channel_settings[i].chanfield) { + j++; + org_value = (row[j] ? row[j] : defaults[j]); + } else if(channel_settings[i].valid & NS_VALID_FUNCTION) { + neonserv_cmd_set_function *func = channel_settings[i].parameter; + org_value = func(client, textclient, user, chan, event, NULL, NULL); + } else + org_value = "0"; + value = org_value; + if(channel_settings[i].valid & NS_VALID_BOOLEAN) { + if(!strcmp(value, "0")) + value = get_language_string(user, "NS_SET_OFF"); + else + value = get_language_string(user, "NS_SET_ON"); + } + strcpy(query, value); + querypos = strlen(query); + if(channel_settings[i].valid & NS_HAS_OPT) { + sprintf(nameBuf, "NS_SET_OPTION_%s_%s", channel_settings[i].setting, org_value); + tmp = get_language_string(user, nameBuf); + if(tmp) { + querypos += sprintf(query+querypos, " - %s", tmp); + } + } + if(argc && channel_settings[i].valid & NS_HAS_HELP) { + sprintf(nameBuf, "NS_SET_HELP_%s", channel_settings[i].setting); + tmp = get_language_string(user, nameBuf); + if(tmp) { + querypos += sprintf(query+querypos, " - %s", tmp); + } + } + content[0] = (char*)channel_settings[i].setting; + content[1] = query; + table_add(table, content); + i++; + } + char **table_lines = table_end(table); + for(i = 0; i < table->entrys; i++) { + reply(textclient, user, table_lines[i]); + } + table_free(table); + } +} + +static void neonserv_cmd_set_setting(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, int setting, char *args) { + char *value; + char nameBuf[64]; + //get current value + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `%s` FROM `channels` WHERE `channel_id` = '%d'", channel_settings[setting].chanfield, chan->channel_id); + res = mysql_use(); + row = mysql_fetch_row(res); + if(row[0] == NULL) { + printf_mysql_query("SELECT `%s` FROM `channels` WHERE `channel_name` = 'defaults'", channel_settings[setting].chanfield); + res = mysql_use(); + row = mysql_fetch_row(res); + } + value = row[0]; + if(args) { + //change the channel setting + //check the new argument + int valid = channel_settings[setting].valid; + if(valid & NS_VALID_STRING) { + if(!strcmp(args, "*")) { + args = ""; + } + } + if(valid & NS_VALID_ACCESS) { + int caccess = atoi(args); + int max = ((valid & NS_VALID_NO501) ? 500 : 501); + if(caccess < 0 || caccess > max) { + reply(textclient, user, "NS_INVALID_ACCESS", caccess); + return; + } + int uaccess = getChannelAccess(user, chan); + if(uaccess == 500) uaccess++; + if(atoi(value) > uaccess) { + if(isGodMode(user)) { + event->flags |= CMDFLAG_OPLOG; + } else { + reply(textclient, user, "NS_SET_CANNOT_SET"); + return; + } + } + if(caccess > uaccess) { + if(isGodMode(user)) { + event->flags |= CMDFLAG_OPLOG; + } else { + reply(textclient, user, "NS_SET_BADLEVEL"); + return; + } + } + sprintf(nameBuf, "%d", caccess); + args = nameBuf; + } + if(valid & NS_VALID_OPTIONS) { + int options = atoi((char *) channel_settings[setting].parameter); + int coption = atoi(args); + if(coption < 0 || coption >= options) { + reply(textclient, user, "NS_SET_INVALID_OPTION", coption); + int i; + int nameBufPos = 0; + if(valid & NS_HAS_OPT) { + for(i = 0; i < options; i++) { + sprintf(nameBuf, "NS_SET_OPTION_%s_%d", channel_settings[setting].setting, i); + reply(textclient, user, "\002%d\002 - %s", i, get_language_string(user, nameBuf)); + } + } else { + for(i = 0; i < options; i++) { + nameBufPos += sprintf(nameBuf + nameBufPos, "\002%d\002, ", i); + } + if(nameBufPos) { + nameBuf[nameBufPos-2] = '\0'; + reply(textclient, user, nameBuf); + } + } + return; + } + } + if(valid & NS_VALID_NUMERIC) { + sprintf(nameBuf, "%d", atoi(args)); + args = nameBuf; + } + if(valid & NS_VALID_BOOLEAN) { + if(!strcmp(args, "0") || !stricmp(args, "off") || !stricmp(args, get_language_string(user, "NS_SET_OFF"))) { + args = "0"; + } else if(!strcmp(args, "1") || !stricmp(args, "on") || !stricmp(args, get_language_string(user, "NS_SET_ON"))) { + args = "1"; + } else { + reply(textclient, user, "NS_SET_INVALID_BOOLEAN", args); + return; + } + } + //valid - set it + value = args; + printf_mysql_query("UPDATE `channels` SET `%s` = '%s' WHERE `channel_id` = '%d'", channel_settings[setting].chanfield, escape_string(value), chan->channel_id); + logEvent(event); + } + if(channel_settings[setting].valid & NS_HAS_OPT) { + sprintf(nameBuf, "NS_SET_OPTION_%s_%s", channel_settings[setting].setting, value); + char *tmp = get_language_string(user, nameBuf); + if(tmp) + reply(textclient, user, "\002%s\002 %s - %s", channel_settings[setting].setting, value, tmp); + else + reply(textclient, user, "\002%s\002 %s", channel_settings[setting].setting, value); + } else + reply(textclient, user, "\002%s\002 %s", channel_settings[setting].setting, value); + if(channel_settings[setting].valid & NS_HAS_HELP) { + sprintf(nameBuf, "NS_SET_HELP_%s", channel_settings[setting].setting); + reply(textclient, user, " %s", get_language_string(user, nameBuf)); + } +} + +static char* neonserv_cmd_set_trigger(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument) { + char *trigger; + //get current trigger + MYSQL_RES *res; + MYSQL_ROW row; + if(client->botid) + printf_mysql_query("SELECT `trigger`, `defaulttrigger`, `bot_channels`.`id` FROM `bot_channels` LEFT JOIN `bots` ON `botid` = `bots`.`id` WHERE `chanid` = '%d' AND `botclass` = '%d'", chan->channel_id, client->botid); + else + printf_mysql_query("SELECT `trigger`, `defaulttrigger`, `bot_channels`.`id` FROM `bot_channels` LEFT JOIN `bots` ON `botid` = `bots`.`id` WHERE `chanid` = '%d' AND `botid` = '%d'", chan->channel_id, client->clientid); + res = mysql_use(); + row = mysql_fetch_row(res); + trigger = (row[0] ? row[0] : row[1]); + if(argument) { + int uaccess = getChannelAccess(user, chan); + if(uaccess < 500) { + if(isGodMode(user)) { + event->flags |= CMDFLAG_OPLOG; + } else { + reply(textclient, user, "NS_SET_TRIGGER_OWNER", chan->name); + return NULL; + } + } + if(strlen(argument) > 15) + argument[15] = '\0'; + printf_mysql_query("UPDATE `bot_channels` SET `trigger` = '%s' WHERE `id` = '%d'", escape_string(argument), row[2]); + trigger = argument; + if(client->botid) + changeChannelTrigger(client->botid, chan, trigger); + else + changeBotwiseChannelTrigger(client->botid, client->clientid, chan, trigger); + logEvent(event); + } + if(setting) { + reply(textclient, user, "\002%s\002 %s", setting, trigger); + } + return trigger; +} + +static char* neonserv_cmd_set_modes(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument) { + char *value; + char valueBuf[MAXLEN]; + //get current value + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `channel_modes` FROM `channels` WHERE `channel_id` = '%d'", chan->channel_id); + res = mysql_use(); + row = mysql_fetch_row(res); + if(row[0] == NULL) { + printf_mysql_query("SELECT `channel_modes` FROM `channels` WHERE `channel_name` = 'defaults'"); + res = mysql_use(); + row = mysql_fetch_row(res); + } + value = row[0]; + if(argument) { + //change the channel setting + struct ModeNode *modenode = createModeNode(NULL); + parseModeString(modenode, argument); + getFullModeString(modenode, valueBuf); + value = valueBuf; + printf_mysql_query("UPDATE `channels` SET `channel_modes` = '%s' WHERE `channel_id` = '%d'", escape_string(value), chan->channel_id); + //TODO: set modelock + freeModeNode(modenode); + } + if(setting) { + reply(textclient, user, "\002%s\002 %s", setting, value); + } + return value; +} + +static char* neonserv_cmd_set_dynlimit(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument) { + char *value; + char tmp[64]; + //get current value + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `channel_dynlimit` FROM `channels` WHERE `channel_id` = '%d'", chan->channel_id); + res = mysql_use(); + row = mysql_fetch_row(res); + if(row[0] == NULL) { + printf_mysql_query("SELECT `channel_dynlimit` FROM `channels` WHERE `channel_name` = 'defaults'"); + res = mysql_use(); + row = mysql_fetch_row(res); + } + value = row[0]; + if(argument) { + //change the channel setting + sprintf(tmp, "%d", atoi(argument)); + argument = tmp; + printf_mysql_query("UPDATE `channels` SET `channel_dynlimit` = '%s' WHERE `channel_id` = '%d'", escape_string(argument), chan->channel_id); + if(strcmp(argument, "0")) + putsock(client, "MODE %s +l %d", chan->name, (chan->usercount + atoi(argument))); + else if(isModeSet(chan->modes, 'l')) + putsock(client, "MODE %s -l", chan->name); + value = argument; + logEvent(event); + } + if(setting) { + reply(textclient, user, "\002%s\002 %s", setting, value); + } + return value; +} + +static char* neonserv_cmd_set_nodelete(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument) { + char *value; + //get current value + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `channel_nodelete` FROM `channels` WHERE `channel_id` = '%d'", chan->channel_id); + res = mysql_use(); + row = mysql_fetch_row(res); + if(row[0] == NULL) { + printf_mysql_query("SELECT `channel_nodelete` FROM `channels` WHERE `channel_name` = 'defaults'"); + res = mysql_use(); + row = mysql_fetch_row(res); + } + value = row[0]; + if(argument && isGodMode(user)) { + //change the channel setting + if(!strcmp(argument, "0") || !strcmp(argument, "off") || !strcmp(argument, get_language_string(user, "NS_SET_OFF"))) { + argument = "0"; + } else if(!strcmp(argument, "1") || !strcmp(argument, "on") || !strcmp(argument, get_language_string(user, "NS_SET_ON"))) { + argument = "1"; + } else { + reply(textclient, user, "NS_SET_INVALID_BOOLEAN", argument); + return NULL; + } + printf_mysql_query("UPDATE `channels` SET `channel_nodelete` = '%s' WHERE `channel_id` = '%d'", escape_string(argument), chan->channel_id); + event->flags |= CMDFLAG_OPLOG; + value = argument; + logEvent(event); + } + if(setting) { + reply(textclient, user, "\002%s\002 %s", setting, value); + } + return value; +} + +static char* neonserv_cmd_set_backupbot(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument) { + int backupbot; + //get current trigger + MYSQL_RES *res; + printf_mysql_query("SELECT `botid` FROM `bot_channels` LEFT JOIN `bots` ON `botid` = `bots`.`id` WHERE `chanid` = '%d' AND `botclass` = '%d'", chan->channel_id, NEONBACKUP_BOTID); + res = mysql_use(); + if(mysql_fetch_row(res)) + backupbot = 1; + else + backupbot = 0; + if(argument) { + //change the channel setting + if(!strcmp(argument, "0") || !strcmp(argument, "off") || !strcmp(argument, get_language_string(user, "NS_SET_OFF"))) { + if(backupbot) { + module_global_cmd_unregister_neonbackup(chan->name); + backupbot = 0; + } + } else if(!strcmp(argument, "1") || !strcmp(argument, "on") || !strcmp(argument, get_language_string(user, "NS_SET_ON"))) { + if(!backupbot) { + module_global_cmd_register_neonbackup(chan->name); + backupbot = 1; + } + } else { + reply(textclient, user, "NS_SET_INVALID_BOOLEAN", argument); + return NULL; + } + logEvent(event); + } + if(setting) { + reply(textclient, user, "\002%s\002 %s", setting, (backupbot ? "1" : "0")); + } + return (backupbot ? "1" : "0"); +} + +#undef MAX_QUERY_LEN diff --git a/src/modules/NeonServ.mod/cmd_neonserv_setrank.c b/src/modules/NeonServ.mod/cmd_neonserv_setrank.c new file mode 100644 index 0000000..91a3588 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_setrank.c @@ -0,0 +1,113 @@ +/* cmd_neonserv_setrank.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0] rank id +* argv[1] setting +* argv[2-*] value +*/ + +static void neonserv_cmd_setrank_name(struct ClientSocket *textclient, struct UserNode *user, int rank_id, char *current, char *value); +static void neonserv_cmd_setrank_info(struct ClientSocket *textclient, struct UserNode *user, int rank_id, char *current, char *value); +static void neonserv_cmd_setrank_access(struct ClientSocket *textclient, struct UserNode *user, int rank_id, char *current, char *value); +static void neonserv_cmd_setrank_order(struct ClientSocket *textclient, struct UserNode *user, int rank_id, char *current, char *value); + +CMD_BIND(neonserv_cmd_setrank) { + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `rank_id`, `rank_name`, `rank_info`, `rank_access`, `rank_order` FROM `support_ranks` WHERE `rank_id` = '%s'", escape_string(argv[0])); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) == NULL) { + reply(textclient, user, "NS_SETRANK_NOT_FOUND", argv[0]); + return; + } + if(argc < 2) { + reply(textclient, user, "NS_SETRANK_HEAD", row[0]); + reply(textclient, user, "\002NAME \002 %s", row[1]); + reply(textclient, user, "\002INFO \002 %s", row[2]); + reply(textclient, user, "\002ACCESS \002 %s", row[3]); + reply(textclient, user, "\002ORDER \002 %s", row[4]); + } else { + char *value = NULL; + if(argc > 2) { + value = merge_argv(argv, 2, argc); + } + if(!stricmp(argv[1], "name")) + neonserv_cmd_setrank_name(textclient, user, atoi(row[0]), row[1], value); + else if(!stricmp(argv[1], "info")) + neonserv_cmd_setrank_info(textclient, user, atoi(row[0]), row[2], value); + else if(!stricmp(argv[1], "access")) + neonserv_cmd_setrank_access(textclient, user, atoi(row[0]), row[3], value); + else if(!stricmp(argv[1], "order")) + neonserv_cmd_setrank_order(textclient, user, atoi(row[0]), row[4], value); + else + reply(textclient, user, "NS_SETRANK_UNKNOWN_SETTING", row[1]); + } +} + +static void neonserv_cmd_setrank_name(struct ClientSocket *textclient, struct UserNode *user, int rank_id, char *current, char *value) { + if(value && stricmp(value, current)) { + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `rank_id` FROM `support_ranks` WHERE `rank_name` = '%s'", escape_string(value)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + reply(textclient, user, "NS_ADDRANK_EXISTS", value); + return; + } + printf_mysql_query("UPDATE `support_ranks` SET `rank_name` = '%s' WHERE `rank_id` = '%d'", escape_string(value), rank_id); + current = value; + } + reply(textclient, user, "\002NAME\002 %s", current); +} + +static void neonserv_cmd_setrank_info(struct ClientSocket *textclient, struct UserNode *user, int rank_id, char *current, char *value) { + if(value && stricmp(value, current)) { + printf_mysql_query("UPDATE `support_ranks` SET `rank_info` = '%s' WHERE `rank_id` = '%d'", escape_string(value), rank_id); + current = value; + } + reply(textclient, user, "\002INFO\002 %s", current); +} + +static void neonserv_cmd_setrank_access(struct ClientSocket *textclient, struct UserNode *user, int rank_id, char *current, char *value) { + if(value && stricmp(value, current)) { + int new_access = atoi(value); + if(new_access <= 0 || new_access > 1000) { + reply(textclient, user, "NS_INVALID_ACCESS", new_access); + return; + } + printf_mysql_query("UPDATE `users` SET `user_access` = '%d' WHERE `user_rank` = '%d'", new_access, rank_id); + printf_mysql_query("UPDATE `support_ranks` SET `rank_access` = '%d' WHERE `rank_id` = '%d'", new_access, rank_id); + current = value; + } + reply(textclient, user, "\002ACCESS\002 %s", current); +} + +static void neonserv_cmd_setrank_order(struct ClientSocket *textclient, struct UserNode *user, int rank_id, char *current, char *value) { + if(value && stricmp(value, current)) { + int new_order = atoi(value); + if(new_order <= 0 || new_order > 99) { + reply(textclient, user, "NS_SETRANK_ORDER_INVALID", new_order); + return; + } + printf_mysql_query("UPDATE `support_ranks` SET `rank_order` = '%d' WHERE `rank_id` = '%d'", new_order, rank_id); + current = value; + } + reply(textclient, user, "\002ORDER\002 %s", current); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_suspend.c b/src/modules/NeonServ.mod/cmd_neonserv_suspend.c new file mode 100644 index 0000000..e4fc158 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_suspend.c @@ -0,0 +1,119 @@ +/* cmd_neonserv_suspend.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0] - nick / *auth +*/ +static USERAUTH_CALLBACK(neonserv_cmd_suspend_nick_lookup); +static void neonserv_cmd_suspend_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *nick, char *auth); + +struct neonserv_cmd_suspend_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + struct ChanNode *chan; + struct Event *event; + char *nick; +}; + +CMD_BIND(neonserv_cmd_suspend) { + if(argv[0][0] == '*') { + //we've got an auth + argv[0]++; + neonserv_cmd_suspend_async1(client, textclient, user, chan, event, argv[0], argv[0]); + } else { + struct UserNode *cuser = getUserByNick(argv[0]); + if(!cuser) { + cuser = createTempUser(argv[0]); + if(!cuser) { + reply(textclient, user, "NS_USER_UNKNOWN", argv[0]); + return; + } + cuser->flags |= USERFLAG_ISTMPUSER; + } + if(cuser->flags & USERFLAG_ISAUTHED) { + neonserv_cmd_suspend_async1(client, textclient, user, chan, event, argv[0], cuser->auth); + } else { + struct neonserv_cmd_suspend_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->chan = chan; + cache->event = event; + cache->nick = strdup(argv[0]); + get_userauth(cuser, module_id, neonserv_cmd_suspend_nick_lookup, cache); + } + } +} + +static USERAUTH_CALLBACK(neonserv_cmd_suspend_nick_lookup) { + struct neonserv_cmd_suspend_cache *cache = data; + if(!user) { + //USER_DOES_NOT_EXIST + reply(cache->textclient, cache->user, "NS_USER_UNKNOWN", cache->nick); + } + else if(!(user->flags & USERFLAG_ISAUTHED)) { + //USER_NOT_AUTHED + reply(cache->textclient, cache->user, "NS_USER_NEED_AUTH", cache->nick); + } + else + neonserv_cmd_suspend_async1(cache->client, cache->textclient, cache->user, cache->chan, cache->event, user->nick, user->auth); + free(cache->nick); + free(cache); +} + +static void neonserv_cmd_suspend_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *nick, char *auth) { + //we've got a valid auth now... + MYSQL_RES *res; + MYSQL_ROW row; + int userid, cflags; + printf_mysql_query("SELECT `user_id` FROM `users` WHERE `user_user` = '%s'", escape_string(auth)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + userid = atoi(row[0]); + //check if the user is added + printf_mysql_query("SELECT `chanuser_access`, `chanuser_id`, `chanuser_flags` FROM `chanusers` WHERE `chanuser_cid` = '%d' AND `chanuser_uid` = '%d'", chan->channel_id, userid); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + if(atoi(row[0]) >= getChannelAccess(user, chan)) { + if(isGodMode(user)) { + event->flags |= CMDFLAG_OPLOG; + } else { + reply(textclient, user, "NS_USER_OUTRANKED", nick); + return; + } + } + //suspend + cflags = atoi(row[2]); + if(cflags & DB_CHANUSER_SUSPENDED) { + reply(textclient, user, "NS_SUSPEND_ALREADY", nick); + return; + } + cflags |= DB_CHANUSER_SUSPENDED; + printf_mysql_query("UPDATE `chanusers` SET `chanuser_flags` = '%d' WHERE `chanuser_id` = '%s'", cflags, row[1]); + reply(textclient, user, "NS_SUSPEND_DONE", nick, chan->name); + logEvent(event); + return; + } + } + reply(textclient, user, "NS_NOT_ON_USERLIST", nick, chan->name); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_topic.c b/src/modules/NeonServ.mod/cmd_neonserv_topic.c new file mode 100644 index 0000000..068c547 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_topic.c @@ -0,0 +1,157 @@ +/* cmd_neonserv_topic.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* ADVANCEDTOPIC enabled +* argv[0] topic id +* argv[1-*] topic +* +* ADVANCEDTOPIC disabled +* argv[0-*] topic +*/ + +#define ADVANCEDTOPIC_MAXID 9 + +CMD_BIND(neonserv_cmd_topic) { + MYSQL_RES *res; + MYSQL_ROW row, default_row = NULL; + int advanced_topic, i; + char *newtopic; + char *a,*b; + + printf_mysql_query("SELECT `channel_exttopic`, `channel_exttopic_topic`, `channel_topicmask`, `channel_enftopic`, `channel_topicsnarf`, `channel_defaulttopic` FROM `channels` WHERE `channel_id` = '%d'", chan->channel_id); + res = mysql_use(); + row = mysql_fetch_row(res); + if(!row[0] || !row[3] || !row[4]) { + printf_mysql_query("SELECT `channel_exttopic`, `channel_enftopic`, `channel_topicsnarf` FROM `channels` WHERE `channel_name` = 'defaults'"); + default_row = mysql_fetch_row(mysql_use()); + } + + if(row[0] == NULL) { + advanced_topic = atoi(default_row[0]); + } else + advanced_topic = atoi(row[0]); + if(argc == 0) { + //default topic! + putsock(client, "TOPIC %s :%s", chan->name, row[5]); + reply(textclient, user, "NS_TOPIC_DONE", row[5]); + logEvent(event); + return; + } + int uaccess = getChannelAccess(user, chan); + if(!strcmp(row[2], "") || uaccess >= atoi((row[3] ? row[3] : default_row[1]))) { + //just set the topic + newtopic = merge_argv(argv, 0, argc); + if(uaccess >= atoi((row[4] ? row[4] : default_row[2]))) { + //set the default topic + printf_mysql_query("UPDATE `channels` SET `channel_defaulttopic` = '%s' WHERE `channel_id` = '%d'", escape_string(newtopic), chan->channel_id); + } + putsock(client, "TOPIC %s :%s", chan->name, newtopic); + reply(textclient, user, "NS_TOPIC_DONE", newtopic); + logEvent(event); + return; + } + if(advanced_topic) { + char *advtopics[ADVANCEDTOPIC_MAXID]; + int topic_id = 0; + topic_id = atoi(argv[0]); + if(!topic_id || topic_id > ADVANCEDTOPIC_MAXID) { + reply(textclient, user, "NS_EXTTOPIC_INVALID_ID", argv[0]); + return; + } + //parse topics + i = 0; + b = row[1]; + while((a = strstr(b, "\n")) != NULL) { + *a = '\0'; + if(i == ADVANCEDTOPIC_MAXID-1) break; + advtopics[i++] = b; + b = a+1; + } + advtopics[i++] = b; + for(;i < ADVANCEDTOPIC_MAXID;i++) + advtopics[i] = ""; + if(argc < 2) { + //just show the topic with this id + reply(textclient, user, "NS_EXTTOPIC_TOPICID", topic_id, advtopics[topic_id-1]); + return; + } + newtopic = merge_argv(argv, 1, argc); + if(!strcmp(newtopic, "*")) + newtopic = ""; + advtopics[topic_id-1] = newtopic; + char topiclist[MAXLEN*2]; + topiclist[0] = '\0'; + int topiclistpos = 0; + for(i = 0; i < ADVANCEDTOPIC_MAXID; i++) { + if(topiclistpos + strlen(advtopics[i]) + 2 >= MAXLEN) break; + topiclistpos += sprintf(topiclist+topiclistpos, (i ? "\n%s" : "%s"), advtopics[i]); + } + printf_mysql_query("UPDATE `channels` SET `channel_exttopic_topic` = '%s' WHERE `channel_id` = '%d'", escape_string(topiclist), chan->channel_id); + //now build the new topic and set it... + topiclistpos = 0; + b = row[2]; + char *topicpart, *debugbb; + while((a = strstr(b, "%")) != NULL) { + *a = '\0'; + if(isdigit(a[1]) && a[1] - 48 > 0) { + topicpart = advtopics[a[1] - 49]; + if(isdigit(topicpart[0]) && isdigit(b[strlen(b)-1])) + debugbb = "\002\002"; //double bold to prevent following digits used as color code + else + debugbb = ""; + topiclistpos += sprintf(topiclist + topiclistpos, "%s%s%s", b, debugbb, topicpart); + b = a+2; + } else { + topiclistpos += sprintf(topiclist + topiclistpos, "%s%%", b); + b = a+1; + } + } + topiclistpos += sprintf(topiclist + topiclistpos, "%s", b); + if(topiclistpos > MAXLEN) + topiclist[MAXLEN] = '\0'; + putsock(client, "TOPIC %s :%s", chan->name, topiclist); + reply(textclient, user, "NS_TOPIC_DONE", topiclist); + logEvent(event); + } else { + newtopic = merge_argv(argv, 0, argc); + char topiclist[MAXLEN*2]; + topiclist[0] = '\0'; + int topiclistpos = 0; + b = row[2]; + char *debugbb; + while((a = strstr(b, "*")) != NULL) { + *a = '\0'; + if(isdigit(newtopic[0]) && isdigit(b[strlen(b)-1])) + debugbb = "\002\002"; //double bold to prevent following digits used as color code + else + debugbb = ""; + topiclistpos += sprintf(topiclist + topiclistpos, "%s%s%s", b, debugbb, newtopic); + b = a+1; + } + topiclistpos += sprintf(topiclist + topiclistpos, "%s", b); + if(topiclistpos > MAXLEN) + topiclist[MAXLEN] = '\0'; + putsock(client, "TOPIC %s :%s", chan->name, topiclist); + reply(textclient, user, "NS_TOPIC_DONE", topiclist); + logEvent(event); + } +} + +#undef ADVANCEDTOPIC_MAXID \ No newline at end of file diff --git a/src/modules/NeonServ.mod/cmd_neonserv_trace.c b/src/modules/NeonServ.mod/cmd_neonserv_trace.c new file mode 100644 index 0000000..49dc741 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_trace.c @@ -0,0 +1,113 @@ +/* cmd_neonserv_trace.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +#define NS_TRACE_CRITERIA_AUTHED 0x01 +#define NS_TRACE_CRITERIA_NUMCHAN 0x02 + +struct neonserv_cmd_trace_criteria { + char *mask; + char *nick; + char *ident; + char *host; + char *account; + unsigned int flags : 4; + unsigned int authed : 1; + unsigned int used_channel : 5; //32 max + char *channel[10]; + unsigned int numchannels; + unsigned int limit : 16; +}; + +CMD_BIND(neonserv_cmd_trace) { + //ok parse the criterias + struct neonserv_cmd_trace_criteria *criteria = malloc(sizeof(*criteria)); + if (!criteria) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + memset(criteria, 0, sizeof(*criteria)); + criteria->limit = 50; + int i, show_user = 0; + if(!stricmp(argv[0], "print")) { + show_user = 1; + } + for(i = 1; i < argc; i += 2) { + if(argc <= i+1) { + reply(textclient, user, "MODCMD_LESS_PARAM_COUNT"); + return; + } + if(!stricmp(argv[i], "mask")) criteria->mask = argv[i+1]; + else if(!stricmp(argv[i], "nick")) criteria->nick = argv[i+1]; + else if(!stricmp(argv[i], "ident")) criteria->ident = argv[i+1]; + else if(!stricmp(argv[i], "host")) criteria->host = argv[i+1]; + else if(!stricmp(argv[i], "account")) criteria->account = argv[i+1]; + else if(!stricmp(argv[i], "authed")) { + if(!strcmp(argv[i+1], "0") || !strcmp(argv[i+1], "off") || !strcmp(argv[i+1], get_language_string(user, "NS_SET_OFF"))) { + criteria->authed = 1; + } else if(!strcmp(argv[i+1], "0") || !strcmp(argv[i+1], "off") || !strcmp(argv[i+1], get_language_string(user, "NS_SET_OFF"))) { + criteria->authed = 0; + } else { + reply(textclient, user, "NS_SET_INVALID_BOOLEAN", argv[i+1]); + return; + } + criteria->flags |= NS_TRACE_CRITERIA_AUTHED; + } + else if(!stricmp(argv[i], "channel")) criteria->channel[criteria->used_channel++] = argv[i+1]; + else if(!stricmp(argv[i], "numchannels")) { + criteria->numchannels = atoi(argv[i+1]); + criteria->flags |= NS_TRACE_CRITERIA_NUMCHAN; + } + else if(!stricmp(argv[i], "limit")) { + criteria->limit = atoi(argv[i+1]); + } + } + char tmp[MAXLEN]; + int matches = 0; + struct UserNode *cuser; + reply(textclient, user, "NS_TRACE_HEADER"); + for(cuser = getAllUsers(NULL); cuser; cuser = getAllUsers(cuser)) { + if(show_user && matches == criteria->limit) { + //too many + break; + } + if(criteria->mask) { + sprintf(tmp, "%s!%s@%s", cuser->nick, cuser->ident, cuser->host); + if(match(criteria->mask, tmp)) continue; + } + if(criteria->nick && match(criteria->nick, cuser->nick)) continue; + if(criteria->ident && match(criteria->ident, cuser->ident)) continue; + if(criteria->host && match(criteria->host, cuser->host)) continue; + if(criteria->account && (!(cuser->flags & USERFLAG_ISAUTHED) || match(criteria->account, cuser->auth))) continue; + if((criteria->flags & NS_TRACE_CRITERIA_AUTHED) && (criteria->authed ^ (cuser->flags & USERFLAG_ISAUTHED))) continue; + if((criteria->flags & NS_TRACE_CRITERIA_NUMCHAN)) { + int ccount = 0; + struct ChanUser *chanuser; + for(chanuser = getUserChannels(cuser, NULL); chanuser; chanuser = getUserChannels(cuser, chanuser)) + ccount++; + if(ccount < criteria->numchannels) + continue; + } + matches++; + //output + if(show_user) { + reply(textclient, user, "%s!%s@%s %s", cuser->nick, cuser->ident, cuser->host, ((cuser->flags & USERFLAG_ISAUTHED) ? cuser->auth : "*")); + } + } + reply(textclient, user, "NS_TABLE_COUNT", matches); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_trim.c b/src/modules/NeonServ.mod/cmd_neonserv_trim.c new file mode 100644 index 0000000..52478a9 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_trim.c @@ -0,0 +1,158 @@ +/* cmd_neonserv_trim.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0] target (format: minaccess-maxaccess/users/bans) +* argv[1] duration +*/ +static USERLIST_CALLBACK(neonserv_cmd_trim_userlist_lookup); +static void neonserv_cmd_trim_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, int min_access, int max_access, int duration); + +struct neonserv_cmd_trim_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + struct Event *event; + int min_access; + int max_access; + int duration; +}; + +CMD_BIND(neonserv_cmd_trim) { + if(stricmp(argv[0], "bans") && !checkChannelAccess(user, chan, "channel_candel", 0)) { + if(isGodMode(user)) { + event->flags |= CMDFLAG_OPLOG; + } else { + reply(textclient, user, "NS_ACCESS_DENIED"); + return; + } + } + int min_access, max_access; + int duration = strToTime(user, argv[1]); + if(duration < 30) { + reply(textclient, user, "NS_TRIM_DURATION_TOO_SHORT", 30); + return; + } + if(!stricmp(argv[0], "users")) { + min_access = 1; + max_access = getChannelAccess(user, chan) - 1; + } else if(!stricmp(argv[0], "bans")) { + if(!checkChannelAccess(user, chan, "channel_staticban", 0)) { + if(isGodMode(user)) { + event->flags |= CMDFLAG_OPLOG; + } else { + reply(textclient, user, "NS_ACCESS_DENIED"); + return; + } + } + MYSQL_RES *res; + MYSQL_ROW row; + char nameBuf[20]; + printf_mysql_query("SELECT `ban_mask`, `ban_id`, `ban_timeout` FROM `bans` WHERE `ban_channel` = '%d' AND `ban_triggered` < %d", chan->channel_id, (int) (time(0) - duration)); + res = mysql_use(); + int bancount = mysql_num_rows(res); + struct ModeBuffer *modenode = initModeBuffer(client, chan); + while ((row = mysql_fetch_row(res)) != NULL) { + if(strcmp(row[2], "0")) { + sprintf(nameBuf, "ban_%s", row[1]); + timeq_del_name(nameBuf); + } + printf_mysql_query("DELETE FROM `bans` WHERE `ban_id` = '%s'", row[1]); + modeBufferUnban(modenode, row[0]); + } + freeModeBuffer(modenode); + char timeBuf[MAXLEN]; + reply(textclient, user, "NS_TRIM_BAN_DONE", bancount, chan->name, timeToStr(user, duration, 3, timeBuf)); + if(bancount) + logEvent(event); + return; + } else { + char *seperator = strstr(argv[0], "-"); + if(seperator) { + *seperator = '\0'; + seperator++; + min_access = atoi(argv[0]); + max_access = atoi(seperator); + if(max_access < min_access) { + reply(textclient, user, "NS_INVALID_ACCESS_RANGE", min_access, max_access); + return; + } + } else { + min_access = atoi(argv[0]); + max_access = min_access; + } + if(max_access >= getChannelAccess(user, chan)) { + if(isGodMode(user)) { + event->flags |= CMDFLAG_OPLOG; + } else { + reply(textclient, user, "NS_NO_ACCESS"); + return; + } + } + } + struct neonserv_cmd_trim_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->event = event; + cache->min_access = min_access; + cache->max_access = max_access; + cache->duration = duration; + get_userlist_with_invisible(chan, module_id, neonserv_cmd_trim_userlist_lookup, cache); +} + +static USERLIST_CALLBACK(neonserv_cmd_trim_userlist_lookup) { + struct neonserv_cmd_trim_cache *cache = data; + //got userlist + neonserv_cmd_trim_async1(cache->client, cache->textclient, cache->user, chan, cache->event, cache->min_access, cache->max_access, cache->duration); + free(cache); +} + +static void neonserv_cmd_trim_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, int min_access, int max_access, int duration) { + MYSQL_RES *res; + MYSQL_ROW row; + int trim_count = 0, is_here; + struct ChanUser *chanuser; + printf_mysql_query("SELECT `chanuser_seen`, `user_user`, `chanuser_id` FROM `chanusers` LEFT JOIN `users` ON `chanuser_uid` = `user_id` WHERE `chanuser_cid` = '%d' AND `chanuser_access` >= '%d' AND `chanuser_access` <= '%d'", chan->channel_id, min_access, max_access); + res = mysql_use(); + while ((row = mysql_fetch_row(res)) != NULL) { + if(!strcmp(row[0], "0") || time(0) - atoi(row[0]) >= duration) { + //check if the user is currently in the channel + is_here = 0; + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + if((chanuser->user->flags & USERFLAG_ISAUTHED) && !strcmp(chanuser->user->auth, row[1])) { + is_here = 1; + break; + } + } + if(!is_here) { + //delete the user + trim_count++; + printf_mysql_query("DELETE FROM `chanusers` WHERE `chanuser_id` = '%s'", row[2]); + } + } + } + char timeBuf[MAXLEN]; + reply(textclient, user, "NS_TRIM_DONE", trim_count, min_access, max_access, chan->name, timeToStr(user, duration, 3, timeBuf)); + if(trim_count) + logEvent(event); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_unban.c b/src/modules/NeonServ.mod/cmd_neonserv_unban.c new file mode 100644 index 0000000..6361bb8 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_unban.c @@ -0,0 +1,145 @@ +/* cmd_neonserv_unban.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0-*] nick[,*auth[,*!*@mask[...]]] +*/ +struct neonserv_cmd_unban_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + struct ChanNode *chan; + struct Event *event; + struct ModeBuffer *modeBuf; + int provided_masks, done_masks, pending_whos, unbanned_masks; +}; + +static USERAUTH_CALLBACK(neonserv_cmd_unban_userauth_lookup); +static void neonserv_cmd_unban_nick(struct neonserv_cmd_unban_cache *cache, struct UserNode *user); +static void neonserv_cmd_unban_mask(struct neonserv_cmd_unban_cache *cache, char *mask); +static void neonserv_cmd_unban_finish(struct neonserv_cmd_unban_cache *cache); + +CMD_BIND(neonserv_cmd_unban) { + char *mask, *nextmask; + struct ModeBuffer *modeBuf; + modeBuf = initModeBuffer(client, chan); + nextmask = merge_argv_char(argv, 0, argc, ','); + struct neonserv_cmd_unban_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->chan = chan; + cache->event = event; + cache->modeBuf = modeBuf; + cache->done_masks = 0; + cache->provided_masks = 0; + cache->unbanned_masks = 0; + while((mask = nextmask)) { + nextmask = strstr(mask, ","); + if(nextmask) { + *nextmask = '\0'; + nextmask++; + } + cache->provided_masks++; + if(is_valid_nick(mask)) { + struct UserNode *cuser = getUserByNick(mask); + if(!cuser) { + cuser = createTempUserMask(mask); + if(!cuser) { + break; //internal bot error + } + cuser->flags |= USERFLAG_ISTMPUSER; + get_userauth(cuser, module_id, neonserv_cmd_unban_userauth_lookup, cache); + cache->pending_whos++; + } else { + neonserv_cmd_unban_nick(cache, cuser); + } + } else { + neonserv_cmd_unban_mask(cache, mask); + } + } + if(!cache->pending_whos) + neonserv_cmd_unban_finish(cache); +} + +static USERAUTH_CALLBACK(neonserv_cmd_unban_userauth_lookup) { + struct neonserv_cmd_unban_cache *cache = data; + cache->pending_whos--; + if(user) + neonserv_cmd_unban_nick(cache, user); + else + neonserv_cmd_unban_mask(cache, user_nick); + if(!cache->pending_whos) + neonserv_cmd_unban_finish(cache); +} + +static void neonserv_cmd_unban_nick(struct neonserv_cmd_unban_cache *cache, struct UserNode *user) { + int matches = 0; + struct BanNode *ban; + char usermask[NICKLEN+USERLEN+HOSTLEN+3]; + sprintf(usermask, "%s!%s@%s", user->nick, user->ident, user->host); + for(ban = cache->chan->bans; ban; ban = ban->next) { + if(!match(ban->mask, usermask)) { + modeBufferUnban(cache->modeBuf, ban->mask); + cache->unbanned_masks++; + matches++; + } + } + if(matches) + cache->done_masks++; +} + +static void neonserv_cmd_unban_mask(struct neonserv_cmd_unban_cache *cache, char *mask) { + char banmask[NICKLEN+USERLEN+HOSTLEN+3]; + int matches = 0; + struct BanNode *ban; + mask = make_banmask(mask, banmask); + for(ban = cache->chan->bans; ban; ban = ban->next) { + if(!match(mask, ban->mask)) { + modeBufferUnban(cache->modeBuf, ban->mask); + cache->unbanned_masks++; + matches++; + } + } + if(matches) + cache->done_masks++; + else { + for(ban = cache->chan->bans; ban; ban = ban->next) { + if(!match(ban->mask, mask)) { + reply(cache->textclient, cache->user, "NS_DELBAN_BANNED_BY", mask, ban->mask); + break; + } + } + } +} + +static void neonserv_cmd_unban_finish(struct neonserv_cmd_unban_cache *cache) { + freeModeBuffer(cache->modeBuf); + if(cache->done_masks == cache->provided_masks) + reply(cache->textclient, cache->user, "NS_UNBAN_DONE", cache->unbanned_masks, cache->chan->name); + else + reply(cache->textclient, cache->user, "NS_UNBAN_FAIL", cache->client->user->nick); + if(cache->done_masks) + logEvent(cache->event); + free(cache); +} + diff --git a/src/modules/NeonServ.mod/cmd_neonserv_unbanall.c b/src/modules/NeonServ.mod/cmd_neonserv_unbanall.c new file mode 100644 index 0000000..edb1150 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_unbanall.c @@ -0,0 +1,39 @@ +/* cmd_neonserv_unbanall.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0-*] nothing +*/ + +CMD_BIND(neonserv_cmd_unbanall) { + struct ModeBuffer *modeBuf; + int bans = 0; + struct BanNode *ban; + modeBuf = initModeBuffer(client, chan); + for(ban = chan->bans; ban; ban = ban->next) { + modeBufferUnban(modeBuf, ban->mask); + bans++; + } + freeModeBuffer(modeBuf); + if(bans) { + reply(textclient, user, "NS_UNBANALL_DONE", bans, chan->name); + logEvent(event); + } else + reply(textclient, user, "NS_UNBANALL_FAIL", client->user->nick, chan->name); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_unbanme.c b/src/modules/NeonServ.mod/cmd_neonserv_unbanme.c new file mode 100644 index 0000000..3cec26d --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_unbanme.c @@ -0,0 +1,43 @@ +/* cmd_neonserv_unbanme.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0-*] nothing +*/ + +CMD_BIND(neonserv_cmd_unbanme) { + struct ModeBuffer *modeBuf; + int bans = 0; + struct BanNode *ban; + modeBuf = initModeBuffer(client, chan); + char usermask[NICKLEN+USERLEN+HOSTLEN+3]; + sprintf(usermask, "%s!%s@%s", user->nick, user->ident, user->host); + for(ban = chan->bans; ban; ban = ban->next) { + if(!match(ban->mask, usermask)) { + modeBufferUnban(modeBuf, ban->mask); + bans++; + } + } + freeModeBuffer(modeBuf); + if(bans) { + reply(textclient, user, "NS_UNBANME_DONE", bans, chan->name); + logEvent(event); + } else + reply(textclient, user, "NS_UNBANME_FAIL", client->user->nick, usermask); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_unsuspend.c b/src/modules/NeonServ.mod/cmd_neonserv_unsuspend.c new file mode 100644 index 0000000..7c88303 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_unsuspend.c @@ -0,0 +1,119 @@ +/* cmd_neonserv_unsuspend.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0] - nick / *auth +*/ +static USERAUTH_CALLBACK(neonserv_cmd_unsuspend_nick_lookup); +static void neonserv_cmd_unsuspend_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *nick, char *auth); + +struct neonserv_cmd_unsuspend_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + struct ChanNode *chan; + struct Event *event; + char *nick; +}; + +CMD_BIND(neonserv_cmd_unsuspend) { + if(argv[0][0] == '*') { + //we've got an auth + argv[0]++; + neonserv_cmd_unsuspend_async1(client, textclient, user, chan, event, argv[0], argv[0]); + } else { + struct UserNode *cuser = getUserByNick(argv[0]); + if(!cuser) { + cuser = createTempUser(argv[0]); + if(!cuser) { + reply(textclient, user, "NS_USER_UNKNOWN", argv[0]); + return; + } + cuser->flags |= USERFLAG_ISTMPUSER; + } + if(cuser->flags & USERFLAG_ISAUTHED) { + neonserv_cmd_unsuspend_async1(client, textclient, user, chan, event, argv[0], cuser->auth); + } else { + struct neonserv_cmd_unsuspend_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->chan = chan; + cache->event = event; + cache->nick = strdup(argv[0]); + get_userauth(cuser, module_id, neonserv_cmd_unsuspend_nick_lookup, cache); + } + } +} + +static USERAUTH_CALLBACK(neonserv_cmd_unsuspend_nick_lookup) { + struct neonserv_cmd_unsuspend_cache *cache = data; + if(!user) { + //USER_DOES_NOT_EXIST + reply(cache->textclient, cache->user, "NS_USER_UNKNOWN", cache->nick); + } + else if(!(user->flags & USERFLAG_ISAUTHED)) { + //USER_NOT_AUTHED + reply(cache->textclient, cache->user, "NS_USER_NEED_AUTH", cache->nick); + } + else + neonserv_cmd_unsuspend_async1(cache->client, cache->textclient, cache->user, cache->chan, cache->event, user->nick, user->auth); + free(cache->nick); + free(cache); +} + +static void neonserv_cmd_unsuspend_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *nick, char *auth) { + //we've got a valid auth now... + MYSQL_RES *res; + MYSQL_ROW row; + int userid, cflags; + printf_mysql_query("SELECT `user_id` FROM `users` WHERE `user_user` = '%s'", escape_string(auth)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + userid = atoi(row[0]); + //check if the user is added + printf_mysql_query("SELECT `chanuser_access`, `chanuser_id`, `chanuser_flags` FROM `chanusers` WHERE `chanuser_cid` = '%d' AND `chanuser_uid` = '%d'", chan->channel_id, userid); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + if(atoi(row[0]) >= getChannelAccess(user, chan)) { + if(isGodMode(user)) { + event->flags |= CMDFLAG_OPLOG; + } else { + reply(textclient, user, "NS_USER_OUTRANKED", nick); + return; + } + } + //unsuspend + cflags = atoi(row[2]); + if(!(cflags & DB_CHANUSER_SUSPENDED)) { + reply(textclient, user, "NS_SUSPEND_NOT", nick); + return; + } + cflags &= ~DB_CHANUSER_SUSPENDED; + printf_mysql_query("UPDATE `chanusers` SET `chanuser_flags` = '%d' WHERE `chanuser_id` = '%s'", cflags, row[1]); + reply(textclient, user, "NS_SUSPEND_RESTORED", nick, chan->name); + logEvent(event); + return; + } + } + reply(textclient, user, "NS_NOT_ON_USERLIST", nick, chan->name); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_unvisited.c b/src/modules/NeonServ.mod/cmd_neonserv_unvisited.c new file mode 100644 index 0000000..5f1572e --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_unvisited.c @@ -0,0 +1,212 @@ +/* cmd_neonserv_unvisited.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" +#include "../botid.h" + +struct neonserv_cmd_unvisited_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + int duration, unregister_matches; + int who_count, matches; +}; + +static USERLIST_CALLBACK(neonserv_cmd_unvisited_userlist_lookup); +static void neonserv_check_unvisited(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, int duration, int unregister_matches); +static int neonserv_cmd_unvisited_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, int duration, int unregister_matches); +static void neonserv_cmd_unvisited_async2(struct neonserv_cmd_unvisited_cache *cache); +static void neonserv_cmd_unvisited_unreg(struct ClientSocket *client, char *channel); +static TIMEQ_CALLBACK(neonserv_check_unvisited_timer); + +CMD_BIND(neonserv_cmd_unvisited) { + int duration = (argc ? strToTime(user, argv[0]) : 60*60*24*7*3); + reply(textclient, user, "NS_SEARCH_HEADER"); + int unreg = 0; + if(argc > 1 && !stricmp(argv[1], "unregister")) + unreg = 1; + neonserv_check_unvisited(client, textclient, user, duration, unreg); +} + +void neonserv_cmd_unvisited_init() { + if(!timeq_name_exists("neonserv_unvisited")) + timeq_add_name("neonserv_unvisited", 1200, module_id, neonserv_check_unvisited_timer, NULL); +} + +static TIMEQ_CALLBACK(neonserv_check_unvisited_timer) { + char tmp[200]; + char *modname = get_module_name(module_id); + sprintf(tmp, "modules.%s.chan_expire_freq", modname); + char *check_freq_str = get_string_field(tmp); + int check_freq; + if(!check_freq_str || (check_freq = strToTime(NULL, check_freq_str)) < (60*60)) { + timeq_add_name("neonserv_unvisited", 1800, module_id, neonserv_check_unvisited_timer, NULL); + return; + } + sprintf(tmp, "modules.%s.chan_expire_delay", modname); + char *check_expire_str = get_string_field(tmp); + int duration; + if(!check_expire_str || (duration = strToTime(NULL, check_expire_str)) < 60*60*24*7) return; + neonserv_check_unvisited(NULL, NULL, NULL, duration, 1); +} + +static void neonserv_check_unvisited(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, int duration, int unregister_matches) { + MYSQL_RES *res, *res2; + MYSQL_ROW row, row2; + struct ChanNode *channel; + struct neonserv_cmd_unvisited_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->duration = duration; + cache->unregister_matches = unregister_matches; + cache->who_count = 1; /* small fake to prevent the cache to be freed too early */ + cache->matches = 0; + int botid; + if(client) + botid = client->botid; + else + botid = NEONSERV_BOTID; + printf_mysql_query("SELECT `channel_id`, `channel_name`, `channel_nodelete` FROM `bot_channels` LEFT JOIN `channels` ON `chanid` = `channel_id` LEFT JOIN `users` ON `channel_registrator` = `user_id` WHERE `botid` = '%d'", botid); + res = mysql_use(); + while ((row = mysql_fetch_row(res)) != NULL) { + if(!strcmp(row[2], "1")) continue; + printf_mysql_query("SELECT `chanuser_seen` FROM `chanusers` WHERE `chanuser_cid` = '%s' AND `chanuser_access` >= 300 ORDER BY `chanuser_seen` DESC LIMIT 1", row[0]); + res2 = mysql_use(); + row2 = mysql_fetch_row(res2); + if(row2 && atol(row2[0]) > (time(0) - duration)) continue; + channel = getChanByName(row[1]); + if(channel) { + cache->who_count++; + channel->channel_id = atoi(row[0]); + get_userlist_with_invisible(channel, module_id, neonserv_cmd_unvisited_userlist_lookup, cache); + } else { + if(textclient) + reply(textclient, user, "%s", row[1]); + if(unregister_matches) + neonserv_cmd_unvisited_unreg(client, row[1]); + cache->matches++; + } + } + cache->who_count--; //see fix on line 78 + if(cache->who_count == 0) { + neonserv_cmd_unvisited_async2(cache); + } +} + +static USERLIST_CALLBACK(neonserv_cmd_unvisited_userlist_lookup) { + struct neonserv_cmd_unvisited_cache *cache = data; + if(neonserv_cmd_unvisited_async1(cache->client, cache->textclient, cache->user, chan, cache->duration, cache->unregister_matches)) + cache->matches++; + cache->who_count--; + if(cache->who_count == 0) { + neonserv_cmd_unvisited_async2(cache); + } +} + +static int neonserv_cmd_unvisited_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, int duration, int unregister_matches) { + struct ChanUser *chanuser; + MYSQL_RES *res2; + MYSQL_ROW row2; + int active = 0; + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + if(isBot(chanuser->user)) continue; + if((chanuser->user->flags & USERFLAG_ISAUTHED)) { + printf_mysql_query("SELECT `chanuser_id`, `chanuser_access` FROM `chanusers` LEFT JOIN `users` ON `user_id` = `chanuser_uid` WHERE `chanuser_cid` = '%d' AND `user_user` = '%s'", chan->channel_id, escape_string(chanuser->user->auth)); + res2 = mysql_use(); + row2 = mysql_fetch_row(res2); + if(row2 && atoi(row2[1]) >= 300) { + printf_mysql_query("UPDATE `chanusers` SET `chanuser_seen` = UNIX_TIMESTAMP() WHERE `chanuser_id` = '%s'", row2[0]); + active = 1; + } + } + } + if(!active) { + if(textclient) + reply(textclient, user, "%s", chan->name); + if(unregister_matches) + neonserv_cmd_unvisited_unreg(client, chan->name); + } + return !active; +} + +static void neonserv_cmd_unvisited_async2(struct neonserv_cmd_unvisited_cache *cache) { + if(cache->textclient) + reply(cache->textclient, cache->user, "NS_TABLE_COUNT", cache->matches); + free(cache); +} + +static void neonserv_cmd_unvisited_unreg(struct ClientSocket *client, char *channel) { + MYSQL_RES *res; + MYSQL_ROW row; + int sync_neonspam_unreg = get_int_field("General.sync_neonspam_unreg"); + int botid; + if(client) + botid = client->botid; + else + botid = NEONSERV_BOTID; + printf_mysql_query("SELECT `botid`, `bot_channels`.`id`, `suspended`, `channels`.`channel_nodelete` FROM `bot_channels` LEFT JOIN `bots` ON `bot_channels`.`botid` = `bots`.`id` LEFT JOIN `channels` ON `chanid` = `channel_id` WHERE `channel_name` = '%s' AND `botclass` = '%d'", escape_string(channel), botid); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) == NULL) { + return; + } + int clientid = atoi(row[0]); + struct ClientSocket *bot; + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(bot->clientid == clientid) + break; + } + if(!strcmp(row[3], "1")) return; + printf_mysql_query("DELETE FROM `bot_channels` WHERE `id` = '%s'", row[1]); + if(bot && strcmp(row[2], "1")) { + putsock(bot, "PART %s :Channel unregistered.", channel); + } + if(botid == NEONSERV_BOTID && sync_neonspam_unreg) { + botid = NEONSPAM_BOTID; + printf_mysql_query("SELECT `botid`, `bot_channels`.`id`, `suspended` FROM `bot_channels` LEFT JOIN `bots` ON `bot_channels`.`botid` = `bots`.`id` LEFT JOIN `channels` ON `chanid` = `channel_id` WHERE `channel_name` = '%s' AND `botclass` = '%d'", escape_string(channel), botid); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) == NULL) { + return; + } + clientid = atoi(row[0]); + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(bot->clientid == clientid) + break; + } + printf_mysql_query("DELETE FROM `bot_channels` WHERE `id` = '%s'", row[1]); + if(bot && strcmp(row[2], "1")) { + putsock(bot, "PART %s :Channel unregistered.", channel); + } + } + if(botid == NEONSERV_BOTID) { + char setting[128]; + sprintf(setting, "modules.%s.auto_backup_unregister", get_module_name(module_id)); + if(get_int_field(setting)) + module_global_cmd_unregister_neonbackup(channel); + } + char *alertchan = get_string_field("General.alertchan"); + if(alertchan) { + struct ChanNode *alertchan_chan = getChanByName(alertchan); + struct ClientSocket *alertclient; + if(alertchan_chan && (alertclient = getChannelBot(alertchan_chan, 0)) != NULL) { + putsock(alertclient, "PRIVMSG %s :Unregistered %s (unvisited)", alertchan_chan->name, channel); + } + } +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_up.c b/src/modules/NeonServ.mod/cmd_neonserv_up.c new file mode 100644 index 0000000..a5d4d85 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_up.c @@ -0,0 +1,95 @@ +/* cmd_neonserv_up.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* no arguments +*/ + +struct neonserv_cmd_up_cache { + struct ClientSocket *client; + struct ClientSocket *textclient; + struct UserNode *user; + struct Event *event; +}; + +static void neonserv_cmd_up_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event); +static USERLIST_CALLBACK(neonserv_cmd_up_userlist_lookup); + +CMD_BIND(neonserv_cmd_up) { + if(isModeSet(chan->modes, 'd') || isModeSet(chan->modes, 'D')) { + struct neonserv_cmd_up_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->event = event; + get_userlist_if_invisible(chan, module_id, neonserv_cmd_up_userlist_lookup, cache); + } else { + neonserv_cmd_up_async1(client, textclient, user, chan, event); + } +} + +static USERLIST_CALLBACK(neonserv_cmd_up_userlist_lookup) { + struct neonserv_cmd_up_cache *cache = data; + neonserv_cmd_up_async1(cache->client, cache->textclient, cache->user, chan, cache->event); + free(cache); +} + +static void neonserv_cmd_up_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event) { + struct ChanUser *chanuser = getChanUser(user, chan); + if(!chanuser) { + reply(textclient, user, "NS_NOT_ON_CHANNEL_YOU", chan->name); + return; + } + loadChannelSettings(chan); + MYSQL_RES *res, *default_res; + MYSQL_ROW row, default_row; + int chan_getop, chan_getvoice, caccess; + printf_mysql_query("SELECT `channel_getop`, `channel_getvoice` FROM `channels` WHERE `channel_id` = '%d'", chan->channel_id); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) == NULL) return; + if(!row[0] || !row[1]) { + printf_mysql_query("SELECT `channel_getop`, `channel_getvoice` FROM `channels` WHERE `channel_name` = 'defaults'"); + default_res = mysql_use(); + if ((default_row = mysql_fetch_row(default_res)) == NULL) return; + chan_getop = (row[0] ? atoi(row[0]) : atoi(default_row[0])); + chan_getvoice = (row[1] ? atoi(row[1]) : atoi(default_row[1])); + } else { + chan_getop = atoi(row[0]); + chan_getvoice = atoi(row[1]); + } + caccess = getChannelAccess(user, chan); + if(caccess >= chan_getop) { + if(!(chanuser->flags & CHANUSERFLAG_OPPED)) { + putsock(client, "MODE %s +o %s", chan->name, user->nick); + logEvent(event); + } else + reply(textclient, user, "NS_UP_ALREADY_OP", chan->name); + } else if(caccess >= chan_getvoice) { + if(!(chanuser->flags & CHANUSERFLAG_VOICED)) { + putsock(client, "MODE %s +v %s", chan->name, user->nick); + logEvent(event); + } else + reply(textclient, user, "NS_UP_ALREADY_VOICE", chan->name); + } else + reply(textclient, user, "NS_NOT_ON_USERLIST_YOU", chan->name); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_upall.c b/src/modules/NeonServ.mod/cmd_neonserv_upall.c new file mode 100644 index 0000000..738ccf6 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_upall.c @@ -0,0 +1,73 @@ +/* cmd_neonserv_upall.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* no arguments +*/ + +CMD_BIND(neonserv_cmd_upall) { + MYSQL_RES *res, *default_res; + MYSQL_ROW row, default_row; + struct ChanUser *chanuser; + int userid, chan_getop, chan_getvoice, caccess; + int botid = client->botid; + printf_mysql_query("SELECT `user_id` FROM `users` WHERE `user_user` = '%s'", user->auth); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) == NULL) + return; + userid = atoi(row[0]); + printf_mysql_query("SELECT `chanuser_access`, `channel_getop`, `channel_getvoice`, `channel_name`, `channel_id` FROM `chanusers` LEFT JOIN `channels` ON `chanuser_cid` = `channel_id` WHERE `chanuser_uid` = '%d'", userid); + res = mysql_use(); + while ((row = mysql_fetch_row(res)) != NULL) { + chan = getChanByName(row[3]); + if(!chan) continue; + printf_mysql_query("SELECT `botid` FROM `bot_channels` LEFT JOIN `bots` ON `bot_channels`.`botid` = `bots`.`id` WHERE `chanid` = '%s' AND `botclass` = '%d'", row[4], client->botid); + if (mysql_fetch_row(mysql_use()) == NULL) continue; + if(!(chanuser = getChanUser(user, chan))) continue; + if(!row[1] || !row[2]) { + printf_mysql_query("SELECT `channel_getop`, `channel_getvoice` FROM `channels` WHERE `channel_name` = 'defaults'"); + default_res = mysql_use(); + if ((default_row = mysql_fetch_row(default_res)) == NULL) return; + chan_getop = (row[1] ? atoi(row[1]) : atoi(default_row[0])); + chan_getvoice = (row[2] ? atoi(row[2]) : atoi(default_row[1])); + } else { + chan_getop = atoi(row[1]); + chan_getvoice = atoi(row[2]); + } + caccess = atoi(row[0]); + int done = 0; + client = getChannelBot(chan, botid); + if(!client) continue; + if(caccess >= chan_getop) { + if(!(chanuser->flags & CHANUSERFLAG_OPPED)) { + putsock(client, "MODE %s +o %s", chan->name, user->nick); + done = 1; + } + } else if(caccess >= chan_getvoice) { + if(!(chanuser->flags & CHANUSERFLAG_VOICED)) { + putsock(client, "MODE %s +v %s", chan->name, user->nick); + done = 1; + } + } + if(done) { + event->chan = chan; + logEvent(event); + } + } +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_users.c b/src/modules/NeonServ.mod/cmd_neonserv_users.c new file mode 100644 index 0000000..4da41b5 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_users.c @@ -0,0 +1,132 @@ +/* cmd_neonserv_users.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0] - usermask +* argv[1] - min access +* argv[2] - max access +*/ +static USERLIST_CALLBACK(neonserv_cmd_users_userlist_lookup); +static void neonserv_cmd_users_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, char *usermask, int min_access, int max_access); + +struct neonserv_cmd_users_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + char *usermask; + int min_access; + int max_access; +}; + +CMD_BIND(neonserv_cmd_users) { + int min_access = 1, max_access = 500; + char *usermask = NULL; + if(argc > 0) + usermask = argv[0]; + if(argc > 2) { + min_access = atoi(argv[1]); + max_access = atoi(argv[2]); + } + struct neonserv_cmd_users_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->usermask = (usermask ? strdup(usermask) : NULL); + cache->min_access = min_access; + cache->max_access = max_access; + get_userlist_with_invisible(chan, module_id, neonserv_cmd_users_userlist_lookup, cache); +} + +static USERLIST_CALLBACK(neonserv_cmd_users_userlist_lookup) { + struct neonserv_cmd_users_cache *cache = data; + neonserv_cmd_users_async1(cache->client, cache->textclient, cache->user, chan, cache->usermask, cache->min_access, cache->max_access); + if(cache->usermask) + free(cache->usermask); + free(cache); +} + +static void neonserv_cmd_users_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, char *usermask, int min_access, int max_access) { + MYSQL_RES *res; + MYSQL_ROW row; + int content_count = 0, cflags, is_here, caccess, i; + char seenstr[MAXLEN]; + struct Table *table; + struct ChanUser *chanuser; + printf_mysql_query("SELECT `chanuser_access`, `user_user`, `chanuser_seen`, `chanuser_flags` FROM `chanusers` LEFT JOIN `users` ON `chanuser_uid` = `user_id` WHERE `chanuser_cid` = '%d' ORDER BY `chanuser_access` DESC, `user_user` ASC", chan->channel_id); + res = mysql_use(); + table = table_init(4, mysql_num_rows(res) + 1, 0); + if(usermask) + reply(textclient, user, "NS_USERS_HEADER_MATCH", chan->name, min_access, max_access, usermask); + else + reply(textclient, user, "NS_USERS_HEADER", chan->name, min_access, max_access); + char *content[4]; + content[0] = get_language_string(user, "NS_USERS_HEADER_ACCESS"); + content[1] = get_language_string(user, "NS_USERS_HEADER_ACCOUNT"); + content[2] = get_language_string(user, "NS_USERS_HEADER_SEEN"); + content[3] = get_language_string(user, "NS_USERS_HEADER_STATE"); + table_add(table, content); + while ((row = mysql_fetch_row(res)) != NULL) { + caccess = atoi(row[0]); + if((!usermask || !match(usermask, row[1])) && (min_access == 1 || caccess >= min_access) && (max_access == 500 || caccess <= max_access)) { + content[0] = row[0]; + content[1] = row[1]; + is_here = 0; + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + if((chanuser->user->flags & USERFLAG_ISAUTHED) && !stricmp(chanuser->user->auth, row[1])) { + if((chanuser->flags & CHANUSERFLAG_INVISIBLE)) + is_here = 2; + else { + is_here = 1; + break; + } + } + } + if(is_here) { + content[2] = get_language_string(user, (is_here == 2 ? "NS_USERS_SEEN_INVISIBLE" : "NS_USERS_SEEN_HERE")); + } else if(!strcmp(row[2], "0")) { + content[2] = get_language_string(user, "NS_USERS_SEEN_NEVER"); + } else { + timeToStr(user, (time(0) - atoi(row[2])), 2, seenstr); + content[2] = seenstr; //generate time + } + cflags = atoi(row[3]); + if(cflags & DB_CHANUSER_SUSPENDED) + content[3] = get_language_string(user, "NS_USERS_STATE_SUSPENDED"); + else + content[3] = get_language_string(user, "NS_USERS_STATE_NORMAL"); + content_count++; + table_add(table, content); + } + } + //send the table + char **table_lines = table_end(table); + for(i = 0; i < table->entrys; i++) { + reply(textclient, user, table_lines[i]); + } + if(!content_count) + reply(textclient, user, "NS_TABLE_NONE"); + if(usermask || min_access != 1 || max_access != 500) + reply(textclient, user, (table->length == 2 ? "NS_USERS_COUNT_MATCH_1" : "NS_USERS_COUNT_MATCH"), table->length - 1, chan->name, content_count); + else + reply(textclient, user, (table->length == 2 ? "NS_USERS_COUNT_1" : "NS_USERS_COUNT"), table->length - 1, chan->name); + table_free(table); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_uset.c b/src/modules/NeonServ.mod/cmd_neonserv_uset.c new file mode 100644 index 0000000..e1b588f --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_uset.c @@ -0,0 +1,272 @@ +/* cmd_neonserv_uset.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +typedef void neonserv_cmd_uset_function(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument, MYSQL_ROW defaults); +static void neonserv_cmd_uset_language(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument, MYSQL_ROW defaults); +static void neonserv_cmd_uset_blockinvite(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument, MYSQL_ROW defaults); +static void neonserv_cmd_uset_noinvite(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument, MYSQL_ROW defaults); +static void neonserv_cmd_uset_autoinvite(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument, MYSQL_ROW defaults); +static void neonserv_cmd_uset_noautoop(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument, MYSQL_ROW defaults); +static void neonserv_cmd_uset_info(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument, MYSQL_ROW defaults); + +#define USET_FLAG_GLOBAL 0x01 +#define USET_FLAG_CHANNEL 0x02 +#define USET_FLAG_STAFF 0x04 +#define USET_FLAG_INVISIBLE 0x08 +#define USET_FLAG_DEFAULTS 0x10 + +#define USET_DEFAULTS_QUERY \ + printf_mysql_query("SELECT `chanuser_flags`, `chanuser_infoline`, `chanuser_access`, `channel_getinvite`, `chanuser_id` FROM `chanusers` LEFT JOIN `users` ON `chanuser_uid` = `user_id` LEFT JOIN `channels` ON `channel_id` = `chanuser_cid` WHERE `chanuser_cid` = '%d' AND `user_user` = '%s'", chan->channel_id, escape_string(user->auth)) + +static const struct { + const char *setting; + neonserv_cmd_uset_function *function; + int flags; +} uset_settings[] = { + {"Language", neonserv_cmd_uset_language, USET_FLAG_GLOBAL}, + {"BlockInvites", neonserv_cmd_uset_blockinvite, USET_FLAG_GLOBAL}, + {"BlockInvite", neonserv_cmd_uset_blockinvite, USET_FLAG_GLOBAL | USET_FLAG_INVISIBLE}, + {"BlockAllInvites", neonserv_cmd_uset_blockinvite, USET_FLAG_GLOBAL | USET_FLAG_INVISIBLE}, + {"NoInvite", neonserv_cmd_uset_noinvite, USET_FLAG_CHANNEL}, + {"AutoInvite", neonserv_cmd_uset_autoinvite, USET_FLAG_CHANNEL | USET_FLAG_DEFAULTS}, + {"NoAutoOp", neonserv_cmd_uset_noautoop, USET_FLAG_CHANNEL | USET_FLAG_DEFAULTS}, + {"NoAutoVoice", neonserv_cmd_uset_noautoop, USET_FLAG_CHANNEL | USET_FLAG_DEFAULTS | USET_FLAG_INVISIBLE}, //alias of NoAutoOp + {"Info", neonserv_cmd_uset_info, USET_FLAG_CHANNEL | USET_FLAG_DEFAULTS}, + {NULL, NULL, 0} +}; + +CMD_BIND(neonserv_cmd_uset) { + MYSQL_RES *res; + MYSQL_ROW row = NULL; + int i = 0, j = 0; + loadUserSettings(user); + if(argc > 0) { + char *args = (argc > 1 ? merge_argv(argv, 1, argc) : NULL); + while(uset_settings[i].setting) { + if(!stricmp(uset_settings[i].setting, argv[0])) { + //setting found + if(uset_settings[i].flags & USET_FLAG_CHANNEL) { + if(!chan) + goto neonserv_cmd_uset_err_chan_required; + loadChannelSettings(chan); + if(!(chan->flags & CHANFLAG_CHAN_REGISTERED)) { + neonserv_cmd_uset_err_chan_required: + reply(textclient, user, "MODCMD_CHAN_REQUIRED"); + return; + } + if((uset_settings[i].flags & USET_FLAG_DEFAULTS) && !row) { + USET_DEFAULTS_QUERY; + res = mysql_use(); + row = mysql_fetch_row(res); + if(!row) { + reply(textclient, user, "NS_NOT_ON_USERLIST_YOU", chan->name); + return; + } + } + } + neonserv_cmd_uset_function *func = uset_settings[i].function; + func(client, textclient, user, chan, event, uset_settings[i].setting, args, row); + j = 1; + break; + } + i++; + } + if(j == 0) { + //unknown setting + reply(textclient, user, "NS_USET_UNKNOWN_SETTING", argv[0]); + } + } else { + //view all options + reply(textclient, user, "NS_USET_GLOBAL"); + while(uset_settings[i].setting) { + if((uset_settings[i].flags & (USET_FLAG_INVISIBLE | USET_FLAG_GLOBAL)) == USET_FLAG_GLOBAL) { + neonserv_cmd_uset_function *func = uset_settings[i].function; + func(client, textclient, user, chan, event, uset_settings[i].setting, NULL, row); + } + i++; + } + if(!chan) return; + loadChannelSettings(chan); + if(!(chan->flags & CHANFLAG_CHAN_REGISTERED)) return; + reply(textclient, user, "NS_USET_CHANNEL"); + i = 0; + while(uset_settings[i].setting) { + if((uset_settings[i].flags & (USET_FLAG_INVISIBLE | USET_FLAG_CHANNEL)) == USET_FLAG_CHANNEL) { + if((uset_settings[i].flags & USET_FLAG_DEFAULTS) && !row) { + USET_DEFAULTS_QUERY; + res = mysql_use(); + row = mysql_fetch_row(res); + if(!row) { + i++; + continue; + } + } + neonserv_cmd_uset_function *func = uset_settings[i].function; + func(client, textclient, user, chan, event, uset_settings[i].setting, NULL, row); + } + i++; + } + } +} + +static void neonserv_cmd_uset_language(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument, MYSQL_ROW defaults) { + MYSQL_RES *res; + MYSQL_ROW row; + struct language* lang; + if(argument) { + if((lang = get_language_by_tag(argument)) == NULL && (lang = get_language_by_name(argument)) == NULL) { + lang = user->language; + } else { + printf_mysql_query("SELECT `user_id` FROM `users` WHERE `user_user` = '%s'", escape_string(user->auth)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + printf_mysql_query("UPDATE `users` SET `user_lang` = '%s' WHERE `user_id` = '%s'", escape_string(lang->langtag), row[0]); + } else { + printf_mysql_query("INSERT INTO `users` (`user_user`, `user_lang`) VALUES ('%s', '%s')", escape_string(user->auth), escape_string(lang->langtag)); + } + struct UserNode *cuser; + for(cuser = getAllUsers(NULL); cuser; cuser = getAllUsers(cuser)) { + if((cuser->flags & USERFLAG_ISAUTHED) && !stricmp(user->auth, cuser->auth)) + cuser->language = lang; + } + } + } else + lang = user->language; + reply(textclient, user, "\002Language \002%s", lang->langname); + char tmp[MAXLEN]; + int tmppos = 0; + lang = get_default_language(); + tmppos = sprintf(tmp, "%s (%s)", lang->langname, lang->langtag); + printf_mysql_query("SELECT `lang`,`text` FROM `language` WHERE `ident` = 'name'"); + res = mysql_use(); + while((row = mysql_fetch_row(res)) != NULL) { + tmppos += sprintf(tmp + tmppos, ", %s (%s)", row[1], row[0]); + } + reply(textclient, user, " %s", tmp); +} + +static void neonserv_cmd_uset_blockinvite(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument, MYSQL_ROW defaults) { + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `user_id`, `user_block_invites` FROM `users` WHERE `user_user` = '%s'", escape_string(user->auth)); + res = mysql_use(); + row = mysql_fetch_row(res); + int blockinvite = (row ? atoi(row[1]) : 0); + if(argument) { + if(!strcmp(argument, "0") || !stricmp(argument, "off") || !stricmp(argument, get_language_string(user, "NS_SET_OFF"))) { + blockinvite = 0; + } else if(!strcmp(argument, "1") || !stricmp(argument, "on") || !stricmp(argument, get_language_string(user, "NS_SET_ON"))) { + blockinvite = 1; + } + if(blockinvite && !row) { + printf_mysql_query("INSERT INTO `users` (`user_user`, `user_block_invites`) VALUES ('%s', '1')", escape_string(user->auth)); + } else if(blockinvite != atoi(row[1])) { + printf_mysql_query("UPDATE `users` SET `user_block_invites` = '%d' WHERE `user_id` = '%s'", blockinvite, row[0]); + } + } + reply(textclient, user, "\002BlockInvite \002%s", (blockinvite ? get_language_string(user, "NS_SET_ON") : get_language_string(user, "NS_SET_OFF"))); +} + +static void neonserv_cmd_uset_noinvite(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument, MYSQL_ROW defaults) { + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `id` FROM `noinvite` LEFT JOIN `users` ON `uid` = `user_id` WHERE `cid` = '%d' AND `user_user` = '%s'", chan->channel_id, escape_string(user->auth)); + res = mysql_use(); + row = mysql_fetch_row(res); + int noinvite = (row ? 1 : 0); + if(argument) { + if(!strcmp(argument, "0") || !stricmp(argument, "off") || !stricmp(argument, get_language_string(user, "NS_SET_OFF"))) { + if(noinvite) { + printf_mysql_query("DELETE FROM `noinvite` WHERE `id` = '%s'", row[0]); + noinvite = 0; + } + } else if(!strcmp(argument, "1") || !stricmp(argument, "on") || !stricmp(argument, get_language_string(user, "NS_SET_ON"))) { + if(!noinvite) { + int userid; + printf_mysql_query("SELECT `user_id` FROM `users` WHERE `user_user` = '%s'", escape_string(user->auth)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + userid = atoi(row[0]); + } else { + printf_mysql_query("INSERT INTO `users` (`user_user`) VALUES ('%s')", escape_string(user->auth)); + userid = (int) mysql_insert_id(get_mysql_conn()); + } + printf_mysql_query("INSERT INTO `noinvite` (`uid`, `cid`) VALUES ('%d', '%d')", userid, chan->channel_id); + noinvite = 1; + } + } + } + reply(textclient, user, "\002NoInvite \002%s", (noinvite ? get_language_string(user, "NS_SET_ON") : get_language_string(user, "NS_SET_OFF"))); +} + +static void neonserv_cmd_uset_autoinvite(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument, MYSQL_ROW defaults) { + int flags = atoi(defaults[0]); + int getInvite = 0; + if(!defaults[3] && atoi(defaults[2]) >= atoi(getChanDefault("channel_getinvite"))) + getInvite = 1; + else if(defaults[3] && atoi(defaults[2]) >= atoi(defaults[3])) + getInvite = 1; + if(getInvite && argument) { + if(!strcmp(argument, "0") || !stricmp(argument, "off") || !stricmp(argument, get_language_string(user, "NS_SET_OFF"))) { + if(flags & DB_CHANUSER_AUTOINVITE) { + flags &= ~DB_CHANUSER_AUTOINVITE; + printf_mysql_query("UPDATE `chanusers` SET `chanuser_flags` = '%d' WHERE `chanuser_id` = '%s'", flags, defaults[4]); + } + } else if(!strcmp(argument, "1") || !stricmp(argument, "on") || !stricmp(argument, get_language_string(user, "NS_SET_ON"))) { + if(!(flags & DB_CHANUSER_AUTOINVITE)) { + flags |= DB_CHANUSER_AUTOINVITE; + printf_mysql_query("UPDATE `chanusers` SET `chanuser_flags` = '%d' WHERE `chanuser_id` = '%s'", flags, defaults[4]); + } + } + } + if(getInvite) + reply(textclient, user, "\002AutoInvite \002%s", ((flags & DB_CHANUSER_AUTOINVITE) ? get_language_string(user, "NS_SET_ON") : get_language_string(user, "NS_SET_OFF"))); + else + reply(textclient, user, "\002AutoInvite \002%s", get_language_string(user, "NS_USET_NO_ACCESS")); +} + +static void neonserv_cmd_uset_noautoop(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument, MYSQL_ROW defaults) { + int flags = atoi(defaults[0]); + if(argument) { + if(!strcmp(argument, "0") || !stricmp(argument, "off") || !stricmp(argument, get_language_string(user, "NS_SET_OFF"))) { + if(flags & DB_CHANUSER_NOAUTOOP) { + flags &= ~DB_CHANUSER_NOAUTOOP; + printf_mysql_query("UPDATE `chanusers` SET `chanuser_flags` = '%d' WHERE `chanuser_id` = '%s'", flags, defaults[4]); + } + } else if(!strcmp(argument, "1") || !stricmp(argument, "on") || !stricmp(argument, get_language_string(user, "NS_SET_ON"))) { + if(!(flags & DB_CHANUSER_NOAUTOOP)) { + flags |= DB_CHANUSER_NOAUTOOP; + printf_mysql_query("UPDATE `chanusers` SET `chanuser_flags` = '%d' WHERE `chanuser_id` = '%s'", flags, defaults[4]); + } + } + } + reply(textclient, user, "\002NoAutoOp \002%s", ((flags & DB_CHANUSER_NOAUTOOP) ? get_language_string(user, "NS_SET_ON") : get_language_string(user, "NS_SET_OFF"))); +} + +static void neonserv_cmd_uset_info(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *argument, MYSQL_ROW defaults) { + char *infoline; + if(argument) { + infoline = argument; + if(!strcmp(infoline, "*")) + infoline = ""; + printf_mysql_query("UPDATE `chanusers` SET `chanuser_infoline` = '%s' WHERE `chanuser_id` = '%s'", escape_string(infoline), defaults[4]); + } else + infoline = defaults[1]; + reply(textclient, user, "\002Info \002%s", infoline); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_voice.c b/src/modules/NeonServ.mod/cmd_neonserv_voice.c new file mode 100644 index 0000000..ea3de40 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_voice.c @@ -0,0 +1,93 @@ +/* cmd_neonserv_voice.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0-*] nicks +*/ +static USERLIST_CALLBACK(neonserv_cmd_voice_userlist_lookup); +static void neonserv_cmd_voice_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *nicks); + +struct neonserv_cmd_voice_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + struct Event *event; + char *nicks; +}; + +CMD_BIND(neonserv_cmd_voice) { + struct neonserv_cmd_voice_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->event = event; + cache->nicks = strdup(merge_argv(argv, 0, argc)); + get_userlist_if_invisible(chan, module_id, neonserv_cmd_voice_userlist_lookup, cache); +} + +static USERLIST_CALLBACK(neonserv_cmd_voice_userlist_lookup) { + struct neonserv_cmd_voice_cache *cache = data; + neonserv_cmd_voice_async1(cache->client, cache->textclient, cache->user, chan, cache->event, cache->nicks); + free(cache->nicks); + free(cache); +} + +static void neonserv_cmd_voice_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *nicks) { + int total_users = 0, done_users = 0; + struct UserNode *cuser; + struct ChanUser *chanuser; + struct ModeBuffer *modeBuf; + modeBuf = initModeBuffer(client, chan); + char *a, *b = nicks; + do { + a = strstr(b, " "); + if(a) *a = '\0'; + total_users++; + cuser = searchUserByNick(b); + if(!cuser) { + //check for an invisible user + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + if(!stricmp(chanuser->user->nick, b)) { + cuser = chanuser->user; + break; + } + } + if(!cuser) continue; + } else { + chanuser = getChanUser(cuser, chan); + if(!chanuser) continue; + } + done_users++; + if(chanuser->flags & CHANUSERFLAG_VOICED) continue; + modeBufferVoice(modeBuf, b); + if(a) { + b = a+1; + } + } while(a); + freeModeBuffer(modeBuf); + if(done_users == total_users) + reply(textclient, user, "NS_VOICE_DONE", chan->name); + else + reply(textclient, user, "NS_VOICE_FAIL", client->user->nick); + if(done_users) + logEvent(event); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_voiceall.c b/src/modules/NeonServ.mod/cmd_neonserv_voiceall.c new file mode 100644 index 0000000..a1c2325 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_voiceall.c @@ -0,0 +1,73 @@ +/* cmd_neonserv_voiceall.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0] (optional) nick mask +*/ +static USERLIST_CALLBACK(neonserv_cmd_voiceall_userlist_lookup); +static void neonserv_cmd_voiceall_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *nickmask); + +struct neonserv_cmd_voiceall_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + struct Event *event; + char *nickmask; +}; + +CMD_BIND(neonserv_cmd_voiceall) { + struct neonserv_cmd_voiceall_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->event = event; + if(argc > 0) { + cache->nickmask = strdup(argv[0]); + } else + cache->nickmask = NULL; + get_userlist_if_invisible(chan, module_id, neonserv_cmd_voiceall_userlist_lookup, cache); +} + +static USERLIST_CALLBACK(neonserv_cmd_voiceall_userlist_lookup) { + struct neonserv_cmd_voiceall_cache *cache = data; + neonserv_cmd_voiceall_async1(cache->client, cache->textclient, cache->user, chan, cache->event, cache->nickmask); + if(cache->nickmask) + free(cache->nickmask); + free(cache); +} + +static void neonserv_cmd_voiceall_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *nickmask) { + int done_users = 0; + struct ChanUser *chanuser; + struct ModeBuffer *modeBuf; + modeBuf = initModeBuffer(client, chan); + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + if(nickmask && match(nickmask, chanuser->user->nick)) continue; + if(chanuser->flags & CHANUSERFLAG_VOICED) continue; + modeBufferVoice(modeBuf, chanuser->user->nick); + done_users++; + } + freeModeBuffer(modeBuf); + reply(textclient, user, "NS_VOICEALL_DONE", done_users, chan->name); + if(done_users) + logEvent(event); +} diff --git a/src/modules/NeonServ.mod/cmd_neonserv_wipeinfo.c b/src/modules/NeonServ.mod/cmd_neonserv_wipeinfo.c new file mode 100644 index 0000000..bde1a24 --- /dev/null +++ b/src/modules/NeonServ.mod/cmd_neonserv_wipeinfo.c @@ -0,0 +1,113 @@ +/* cmd_neonserv_wipeinfo.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonserv.h" + +/* +* argv[0] - nick / *auth +*/ +static USERAUTH_CALLBACK(neonserv_cmd_wipeinfo_nick_lookup); +static void neonserv_cmd_wipeinfo_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *nick, char *auth); + +struct neonserv_cmd_wipeinfo_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + struct ChanNode *chan; + struct Event *event; + char *nick; +}; + +CMD_BIND(neonserv_cmd_wipeinfo) { + if(argv[0][0] == '*') { + //we've got an auth + argv[0]++; + neonserv_cmd_wipeinfo_async1(client, textclient, user, chan, event, argv[0], argv[0]); + } else { + struct UserNode *cuser = getUserByNick(argv[0]); + if(!cuser) { + cuser = createTempUser(argv[0]); + if(!cuser) { + reply(textclient, user, "NS_USER_UNKNOWN", argv[0]); + return; + } + cuser->flags |= USERFLAG_ISTMPUSER; + } + if(cuser->flags & USERFLAG_ISAUTHED) { + neonserv_cmd_wipeinfo_async1(client, textclient, user, chan, event, argv[0], cuser->auth); + } else { + struct neonserv_cmd_wipeinfo_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->chan = chan; + cache->event = event; + cache->nick = strdup(argv[0]); + get_userauth(cuser, module_id, neonserv_cmd_wipeinfo_nick_lookup, cache); + } + } +} + +static USERAUTH_CALLBACK(neonserv_cmd_wipeinfo_nick_lookup) { + struct neonserv_cmd_wipeinfo_cache *cache = data; + if(!user) { + //USER_DOES_NOT_EXIST + reply(cache->textclient, cache->user, "NS_USER_UNKNOWN", cache->nick); + } + else if(!(user->flags & USERFLAG_ISAUTHED)) { + //USER_NOT_AUTHED + reply(cache->textclient, cache->user, "NS_USER_NEED_AUTH", cache->nick); + } + else + neonserv_cmd_wipeinfo_async1(cache->client, cache->textclient, cache->user, cache->chan, cache->event, user->nick, user->auth); + free(cache->nick); + free(cache); +} + +static void neonserv_cmd_wipeinfo_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *nick, char *auth) { + //we've got a valid auth now... + MYSQL_RES *res; + MYSQL_ROW row; + int userid; + printf_mysql_query("SELECT `user_id` FROM `users` WHERE `user_user` = '%s'", escape_string(auth)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + userid = atoi(row[0]); + //check if the user is already added + printf_mysql_query("SELECT `chanuser_access`, `chanuser_id` FROM `chanusers` WHERE `chanuser_cid` = '%d' AND `chanuser_uid` = '%d'", chan->channel_id, userid); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + if(atoi(row[0]) >= getChannelAccess(user, chan)) { + if(isGodMode(user)) { + event->flags |= CMDFLAG_OPLOG; + } else { + reply(textclient, user, "NS_USER_OUTRANKED", nick); + return; + } + } + //delete + printf_mysql_query("UPDATE `chanusers` SET `chanuser_infoline` = '' WHERE `chanuser_id` = '%s'", row[1]); + reply(textclient, user, "NS_WIPEINFO_DONE", nick, chan->name); + logEvent(event); + return; + } + } + reply(textclient, user, "NS_NOT_ON_USERLIST", nick, chan->name); +} diff --git a/src/modules/NeonServ.mod/event_neonserv_ctcp.c b/src/modules/NeonServ.mod/event_neonserv_ctcp.c new file mode 100644 index 0000000..200b80f --- /dev/null +++ b/src/modules/NeonServ.mod/event_neonserv_ctcp.c @@ -0,0 +1,112 @@ +/* event_neonserv_ctcp.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +struct neonserv_event_ctcp_cache { + struct ClientSocket *client; + struct UserNode *user; + struct ChanNode *chan; + char *command; + char *text; +}; + +static USERAUTH_CALLBACK(neonserv_event_ctcp_nick_lookup); +static void neonserv_event_ctcp_async1(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, char *command, char *text); + +static void neonserv_event_chanctcp(struct UserNode *user, struct ChanNode *chan, char *command, char *text) { + if(!stricmp(command, "ACTION")) return; //always allow CTCP ACTION (/me) + struct ClientSocket *client = getBotForChannel(chan); + if(!client) return; //we can't "see" this event + if(isNetworkService(user)) return; + loadChannelSettings(chan); + if(!(chan->flags & CHANFLAG_CHAN_REGISTERED)) return; + if(!(user->flags & USERFLAG_ISAUTHED)) { + struct neonserv_event_ctcp_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->user = user; + cache->chan = chan; + cache->command = strdup(command); + cache->text = (text ? strdup(text) : NULL); + get_userauth(user, module_id, neonserv_event_ctcp_nick_lookup, cache); + } else + neonserv_event_ctcp_async1(client, user, chan, command, text); +} + +static USERAUTH_CALLBACK(neonserv_event_ctcp_nick_lookup) { + struct neonserv_event_ctcp_cache *cache = data; + if(user) { + neonserv_event_ctcp_async1(cache->client, cache->user, cache->chan, cache->command, cache->text); + } + free(cache->command); + if(cache->text) + free(cache->text); + free(cache); +} + +static void neonserv_event_ctcp_async1(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, char *command, char *text) { + MYSQL_RES *res; + MYSQL_ROW row, defaultrow = NULL, chanuser; + int uaccess = 0; + printf_mysql_query("SELECT `channel_ctcp`, `channel_ctcpreaction` FROM `channels` WHERE `channel_id` = '%d'", chan->channel_id); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) == NULL) return; + if(!row[0] || !row[1]) { + printf_mysql_query("SELECT `channel_ctcp`, `channel_ctcpreaction` FROM `channels` WHERE `channel_name` = 'defaults'"); + res = mysql_use(); + defaultrow = mysql_fetch_row(res); + } + int ctcpaccess = atoi((row[0] ? row[0] : defaultrow[0])); + if((user->flags & USERFLAG_ISAUTHED)) { + printf_mysql_query("SELECT `chanuser_access`, `chanuser_flags` FROM `chanusers` LEFT JOIN `users` ON `chanuser_uid` = `user_id` WHERE `chanuser_cid` = '%d' AND `user_user` = '%s'", chan->channel_id, escape_string(user->auth)); + res = mysql_use(); + chanuser = mysql_fetch_row(res); + if(chanuser) + uaccess = ((atoi(chanuser[1]) & DB_CHANUSER_SUSPENDED) ? 0 : atoi(chanuser[0])); + } + int duration = 0; + char banmaskBuf[NICKLEN+USERLEN+HOSTLEN+3]; + char *banmask = NULL; + char *reason = "disallowed channel CTCP"; + if(uaccess < ctcpaccess) { + switch(atoi((row[1] ? row[1] : defaultrow[1]))) { + case 2: //TIMEBAN: 3min + duration = 180; + case 3: //TIMEBAN: 1h + if(!duration) + duration = 3600; + banmask = generate_banmask(user, banmaskBuf); + printf_mysql_query("INSERT INTO `bans` (`ban_channel`, `ban_mask`, `ban_triggered`, `ban_timeout`, `ban_owner`, `ban_reason`) VALUES ('%d', '%s', UNIX_TIMESTAMP(), '%lu', '%d', '%s')", chan->channel_id, escape_string(banmask), (unsigned long) (time(0) + duration), 0, escape_string(reason)); + int banid = (int) mysql_insert_id(get_mysql_conn()); + char nameBuf[MAXLEN]; + char banidBuf[20]; + sprintf(nameBuf, "ban_%d", banid); + sprintf(banidBuf, "%d", banid); + timeq_add_name(nameBuf, duration, module_id, channel_ban_timeout, strdup(banidBuf)); + case 1: //KICKBAN + if(!banmask) + banmask = generate_banmask(user, banmaskBuf); + putsock(client, "MODE %s +b %s", chan->name, banmask); + case 0: //KICK + putsock(client, "KICK %s %s :%s", chan->name, user->nick, reason); + break; + } + } +} + diff --git a/src/modules/NeonServ.mod/event_neonserv_invite.c b/src/modules/NeonServ.mod/event_neonserv_invite.c new file mode 100644 index 0000000..b777ebc --- /dev/null +++ b/src/modules/NeonServ.mod/event_neonserv_invite.c @@ -0,0 +1,47 @@ +/* event_neonserv_invite.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +static void neonserv_event_invite(struct ClientSocket *client, struct UserNode *user, char *channel) { + if(client->botid != BOTID) + return; + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `botid`, `bot_channels`.`id`, `suspended` FROM `bot_channels` LEFT JOIN `bots` ON `bot_channels`.`botid` = `bots`.`id` LEFT JOIN `channels` ON `chanid` = `channel_id` WHERE `channel_name` = '%s' AND `botclass` = '%d'", escape_string(channel), client->botid); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) == NULL) { + reply(client, user, "NS_INVITE_FAIL", channel, client->user->nick); + return; + } + if(!strcmp(row[2], "1")) { + reply(client, user, "MODCMD_CHAN_SUSPENDED"); + return; + } + int botid = atoi(row[0]); + struct ClientSocket *bot; + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(bot->clientid == botid) + break; + } + if(bot) { + struct ChanNode *chan = getChanByName(channel); + if(chan && isUserOnChan(bot->user, chan)) { + reply(client, user, "NS_INVITE_ON_CHAN", bot->user->nick, chan->name); + } else + putsock(bot, "JOIN %s", channel); + } +} + diff --git a/src/modules/NeonServ.mod/event_neonserv_join.c b/src/modules/NeonServ.mod/event_neonserv_join.c new file mode 100644 index 0000000..5f77f42 --- /dev/null +++ b/src/modules/NeonServ.mod/event_neonserv_join.c @@ -0,0 +1,251 @@ +/* event_neonserv_join.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +struct neonserv_event_join_cache { + struct ClientSocket *client; + struct ChanUser *chanuser; +}; + +static USERAUTH_CALLBACK(neonserv_event_join_nick_lookup); +static void neonserv_event_join_async1(struct ClientSocket *client, struct ChanUser *chanuser); +static TIMEQ_CALLBACK(neonserv_event_join_dynlimit); + +static void neonserv_event_join(struct ChanUser *chanuser) { + struct UserNode *user = chanuser->user; + struct ClientSocket *client = getBotForChannel(chanuser->chan); + if(!client) return; //we can't "see" this event + if(chanuser->user == client->user) { + requestOp(client->user, chanuser->chan); + module_neonbackup_recover_chan(chanuser->chan); + return; + } + if(chanuser->user->flags & USERFLAG_ISBOT) return; + loadChannelSettings(chanuser->chan); + if(!(chanuser->chan->flags & CHANFLAG_CHAN_REGISTERED)) return; + char *ban; + char usermask[NICKLEN+USERLEN+HOSTLEN+3]; + sprintf(usermask, "%s!%s@%s", user->nick, user->ident, user->host); + if((ban = getBanAffectingMask(chanuser->chan, usermask)) != NULL && !(user->flags & (USERFLAG_ISBOT | USERFLAG_ISIRCOP))) { + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `ban_reason`, `user_user` FROM `bans` LEFT JOIN `users` ON `ban_owner` = `user_id` WHERE `ban_channel` = '%d' AND `ban_mask` = '%s'", chanuser->chan->channel_id, escape_string(ban)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + putsock(client, "MODE %s +b %s", chanuser->chan->name, ban); + putsock(client, "KICK %s %s :(%s) %s", chanuser->chan->name, chanuser->user->nick, (row[1] ? row[1] : client->user->nick), row[0]); + return; + } + } + if(!(user->flags & USERFLAG_ISAUTHED)) { + struct neonserv_event_join_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->chanuser = chanuser; + get_userauth(user, module_id, neonserv_event_join_nick_lookup, cache); + } else + neonserv_event_join_async1(client, chanuser); +} + +static USERAUTH_CALLBACK(neonserv_event_join_nick_lookup) { + struct neonserv_event_join_cache *cache = data; + if(user) + neonserv_event_join_async1(cache->client, cache->chanuser); + free(cache); +} + +static void neonserv_event_join_async1(struct ClientSocket *client, struct ChanUser *chanuser) { + struct ClientSocket *textclient = ((client->flags & SOCKET_FLAG_PREFERRED) ? client : get_prefered_bot(client->botid)); + struct ChanNode *chan = chanuser->chan; + struct UserNode *user = chanuser->user; + struct ModeBuffer *modeBuf; + int with_halfops = get_int_field("General.have_halfop"); + MYSQL_RES *res, *res2; + MYSQL_ROW row, chanuserrow, defaultrow = NULL; + printf_mysql_query("SELECT `channel_maxusers`, `channel_greeting`, `channel_usergreeting`, `channel_getop`, `channel_getvoice`, `channel_userinfo`, `channel_dynlimit`, `channel_gethalfop` FROM `channels` WHERE `channel_id` = '%d'", chan->channel_id); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) == NULL) return; + if(!row[3] || !row[4] || !row[5] || (!row[7] && with_halfops)) { + printf_mysql_query("SELECT `channel_getop`, `channel_getvoice`, `channel_userinfo`, `channel_gethalfop` FROM `channels` WHERE `channel_name` = 'defaults'"); + res = mysql_use(); + defaultrow = mysql_fetch_row(res); + } + if(chan->usercount > atoi(row[0])) { + //update maxusers + printf_mysql_query("UPDATE `channels` SET `channel_maxusers` = '%d' WHERE `channel_id` = '%d'", chan->usercount, chan->channel_id); + } + if((user->flags & USERFLAG_ISAUTHED)) { + printf_mysql_query("SELECT `chanuser_access`, `chanuser_flags`, `chanuser_infoline`, `chanuser_seen`, `chanuser_id` FROM `chanusers` LEFT JOIN `users` ON `chanuser_uid` = `user_id` WHERE `chanuser_cid` = '%d' AND `user_user` = '%s'", chan->channel_id, escape_string(user->auth)); + res = mysql_use(); + chanuserrow = mysql_fetch_row(res); + } else + chanuserrow = NULL; + if(!chanuserrow) + printf_mysql_query("UPDATE `channels` SET `channel_lastvisit` = UNIX_TIMESTAMP() WHERE `channel_id` = '%d'", chan->channel_id); + int userflags = (chanuserrow ? atoi(chanuserrow[1]) : 0); + int uaccess = ((chanuserrow && !(userflags & DB_CHANUSER_SUSPENDED)) ? atoi(chanuserrow[0]) : 0); + //GREETING + char greeting[MAXLEN]; + int greetingPos = 0; + char *a, *b = (chanuserrow && *row[2] ? row[2] : row[1]); + do { + if(!b) break; + a = strstr(b, "$"); + if(a) *a = '\0'; + greetingPos += sprintf(greeting + greetingPos, "%s", b); + if(!a) break; + switch(a[1]) { + case '\0': + a = NULL; + break; + case 'A': + greetingPos += sprintf(greeting + greetingPos, "%d", uaccess); + break; + case 'B': + greetingPos += sprintf(greeting + greetingPos, "%s", client->user->nick); + break; + case 'N': + greetingPos += sprintf(greeting + greetingPos, "%s", user->nick); + break; + case 'H': + greetingPos += sprintf(greeting + greetingPos, "%s@%s", user->ident, user->host); + break; + case 'U': + greetingPos += sprintf(greeting + greetingPos, "%s", ((user->flags & USERFLAG_ISAUTHED) ? user->auth : "*")); + break; + default: + greeting[greetingPos++] = '$'; + greeting[greetingPos++] = a[1]; + break; + } + if(a) + b = a+2; + } while(a); + if(greetingPos && *row[2]) + reply(textclient, user, "[%s] %s", chan->name, greeting); + //USER RIGHTS + if(!(userflags & DB_CHANUSER_NOAUTOOP)) { + int getop = atoi((row[3] ? row[3] : defaultrow[0])); + int gethalfop = (with_halfops ? atoi((row[7] ? row[7] : defaultrow[3])) : 0); + int getvoice = atoi((row[4] ? row[4] : defaultrow[1])); + modeBuf = initModeBuffer(client, chan); + if(uaccess >= getop && uaccess != 0) { //we disallow auto op for all users + modeBufferOp(modeBuf, user->nick); + } else if(with_halfops && uaccess >= gethalfop) { + modeBufferHalfop(modeBuf, user->nick); + } else if(uaccess >= getvoice) { + modeBufferVoice(modeBuf, user->nick); + } + freeModeBuffer(modeBuf); + } + //INFOLINE + int userinfoaccess = atoi((row[5] ? row[5] : defaultrow[2])); + if(chanuserrow && strcmp(chanuserrow[2], "") && uaccess > userinfoaccess) { + if(!strcmp(chanuserrow[3], "0") || time(0) - atol(chanuserrow[3]) >= 30) { + putsock(client, "PRIVMSG %s :[%s] %s", chan->name, user->nick, chanuserrow[2]); + } + } + //SEEN + if(chanuserrow) { + printf_mysql_query("UPDATE `chanusers` SET `chanuser_seen` = UNIX_TIMESTAMP() WHERE `chanuser_id` = '%s'", chanuserrow[4]); + } + //DYNLIMIT + if(row[6] && strcmp(row[6], "0")) { + char nameBuf[CHANNELLEN + 10]; + sprintf(nameBuf, "dynlimit_%s", chan->name); + if(!timeq_name_exists(nameBuf)) { + //neonserv_event_join_dynlimit + timeq_add_name(nameBuf, 30, module_id, neonserv_event_join_dynlimit, strdup(chan->name)); + } + } + //AUTOINVITE + if((user->flags & USERFLAG_ISAUTHED) && (!chanuserrow || !strcmp(chanuserrow[3], "0") || time(0) - atol(chanuserrow[3]) >= 30)) { + //check if it's the first channel, the user is seen by the bot (IMPORTANT: ignore other bot's channel!) + char first_chan = 1; + char bot_in_chan; + struct ChanUser *cchanuser, *bchanuser; + struct ClientSocket *bot; + for(cchanuser = getUserChannels(user, NULL); cchanuser; cchanuser = getUserChannels(user, cchanuser)) { + if(chanuser == cchanuser) continue; //ignore this one ;) + //check if this bot is in the specific channel + bot_in_chan = 0; + for(bchanuser = getChannelUsers(cchanuser->chan, NULL); bchanuser; bchanuser = getChannelUsers(cchanuser->chan, bchanuser)) { + if(bchanuser->user->flags & USERFLAG_ISBOT) { + //get the botid + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(bot->user == bchanuser->user) { + if(bot->botid == client->botid) + bot_in_chan = 1; + break; + } + } + if(bot_in_chan) break; + } + } + if(!bot_in_chan) continue; + first_chan = 0; + break; + } + if(first_chan) { + //autoinvite :) + defaultrow = NULL; + printf_mysql_query("SELECT `chanuser_access`, `chanuser_flags`, `channel_name`, `channel_getinvite` FROM `chanusers` LEFT JOIN `channels` ON `chanuser_cid` = `channel_id` LEFT JOIN `users` ON `chanuser_uid` = `user_id` WHERE `user_user` = '%s' AND `chanuser_flags` >= '%d'", escape_string(user->auth), DB_CHANUSER_AUTOINVITE); + res = mysql_use(); + int getinvite; + while((chanuserrow = mysql_fetch_row(res)) != NULL) { + userflags = atoi(chanuserrow[1]); + if(!(userflags & DB_CHANUSER_AUTOINVITE)) continue; + if(!(chan = getChanByName(chanuserrow[2]))) continue; //no bot is in the channel + if((bchanuser = getChanUser(client->user, chan)) && (bchanuser->flags & CHANUSERFLAG_OPPED)) + bot = client; + else { + bot = getBotForChannel(chan); + } + if(chanuserrow[3] == NULL && defaultrow == NULL) { + printf_mysql_query("SELECT `channel_getinvite` FROM `channels` WHERE `channel_name` = 'defaults'"); + res2 = mysql_use(); + defaultrow = mysql_fetch_row(res2); + } + getinvite = atoi((chanuserrow[3] ? chanuserrow[3] : defaultrow[0])); + if(atoi(chanuserrow[0]) >= getinvite) { + putsock(bot, "INVITE %s %s", user->nick, chan->name); + } + } + } + } +} + +static TIMEQ_CALLBACK(neonserv_event_join_dynlimit) { + char *chanName = data; + struct ChanNode *chan = getChanByName(chanName); + free(chanName); + if(!chan) return; + struct ClientSocket *client = getBotForChannel(chan); + if(!client) return; + loadChannelSettings(chan); + if(!(chan->flags & CHANFLAG_CHAN_REGISTERED)) return; + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `channel_dynlimit` FROM `channels` WHERE `channel_id` = '%d'", chan->channel_id); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) == NULL) return; + if(row[0] && strcmp(row[0], "0")) { + putsock(client, "MODE %s +l %d", chan->name, chan->usercount + atoi(row[0])); + } +} diff --git a/src/modules/NeonServ.mod/event_neonserv_kick.c b/src/modules/NeonServ.mod/event_neonserv_kick.c new file mode 100644 index 0000000..2daf63a --- /dev/null +++ b/src/modules/NeonServ.mod/event_neonserv_kick.c @@ -0,0 +1,99 @@ +/* event_neonserv_kick.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +struct neonserv_event_kick_cache { + struct ClientSocket *client; + struct UserNode *user; + struct UserNode *target; + struct ChanNode *chan; + int userauth_pending; +}; + +static USERAUTH_CALLBACK(neonserv_event_kick_nick_lookup); +static void neonserv_event_kick_async1(struct neonserv_event_kick_cache *cache); +static void neonserv_event_kick_async2(struct ClientSocket *client, struct UserNode *user, struct UserNode *target, struct ChanNode *chan); + +static void neonserv_event_kick(struct UserNode *user, struct ChanUser *target, char *reason) { + struct ChanNode *chan = target->chan; + struct ClientSocket *client; + if(isBot(target->user)) { + client = getChannelBot(chan, 0); + struct ClientSocket *bot = client; + for(client = getBots(SOCKET_FLAG_READY, NULL); client; client = getBots(SOCKET_FLAG_READY, client)) { + if(client->user == target->user) { + break; + } + } + if(!client) return; + if(bot && bot != client && (isModeSet(chan->modes, 'i') || isModeSet(chan->modes, 'a') || isModeSet(chan->modes, 'l'))) { + struct ChanUser *chanuser = getChanUser(bot->user, chan); + if(chanuser && chanuser->flags & CHANUSERFLAG_OPPED) + putsock(bot, "INVITE %s %s", target->user->nick, chan->name); + } + char *key = ""; + if(isModeSet(chan->modes, 'k')) { + key = getModeValue(chan->modes, 'k'); + } + putsock(client, "JOIN %s %s", chan->name, key); + return; + } + client = getBotForChannel(chan); + if(!client) return; //we can't "see" this event + if(isNetworkService(user)) return; + loadChannelSettings(chan); + if(!(chan->flags & CHANFLAG_CHAN_REGISTERED)) return; + struct neonserv_event_kick_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->user = user; + cache->target = target->user; + cache->chan = target->chan; + cache->userauth_pending = 0; + if(!(user->flags & USERFLAG_ISAUTHED)) { + get_userauth(user, module_id, neonserv_event_kick_nick_lookup, cache); + cache->userauth_pending++; + } + if(!(target->user->flags & USERFLAG_ISAUTHED)) { + get_userauth(target->user, module_id, neonserv_event_kick_nick_lookup, cache); + cache->userauth_pending++; + } + neonserv_event_kick_async1(cache); +} + +static USERAUTH_CALLBACK(neonserv_event_kick_nick_lookup) { + struct neonserv_event_kick_cache *cache = data; + cache->userauth_pending--; + neonserv_event_kick_async1(cache); +} + +static void neonserv_event_kick_async1(struct neonserv_event_kick_cache *cache) { + if(cache->userauth_pending == 0) { + neonserv_event_kick_async2(cache->client, cache->user, cache->target, cache->chan); + free(cache); + } +} + +static void neonserv_event_kick_async2(struct ClientSocket *client, struct UserNode *user, struct UserNode *target, struct ChanNode *chan) { + if(isUserProtected(chan, target, user)) { + char buf[MAXLEN]; + putsock(client, "KICK %s %s :%s", chan->name, user->nick, build_language_string(user, buf, "NS_USER_PROTECTED", target->nick)); + putsock(client, "INVITE %s %s", target->nick, chan->name); + } +} diff --git a/src/modules/NeonServ.mod/event_neonserv_mode.c b/src/modules/NeonServ.mod/event_neonserv_mode.c new file mode 100644 index 0000000..b6d0652 --- /dev/null +++ b/src/modules/NeonServ.mod/event_neonserv_mode.c @@ -0,0 +1,355 @@ +/* event_neonserv_mode.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +static USERLIST_CALLBACK(neonserv_event_mode_userlist_lookup); +static void neonserv_event_mode_async1(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, char *modes, char **argv, int argc); +static int neonserv_cmd_mode_botwar_detect(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, int *botwar_detect_executed); + +struct neonserv_event_mode_cache { + struct ClientSocket *client; + struct UserNode *user; + char *modes; + char **argv; + int argc; +}; + +static void neonserv_event_mode(struct UserNode *user, struct ChanNode *chan, char *modes, char **argv, int argc) { + struct ClientSocket *client = getBotForChannel(chan); + if(!client) return; //we can't "see" this event + if(isNetworkService(user)) return; + loadChannelSettings(chan); + if(!(chan->flags & CHANFLAG_CHAN_REGISTERED)) return; + struct neonserv_event_mode_cache *cache = malloc(sizeof(*cache)); + char **temp_argv = NULL; + if(argc) + temp_argv = malloc(argc*sizeof(*temp_argv)); + if (!cache || (argc && !temp_argv)) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + if(argc) { + int i; + for(i = 0; i < argc; i++) { + temp_argv[i] = strdup(argv[i]); + } + } + cache->client = client; + cache->user = user; + cache->modes = strdup(modes); + cache->argv = temp_argv; + cache->argc = argc; + get_userlist_with_invisible(chan, module_id, neonserv_event_mode_userlist_lookup, cache); +} + +static USERLIST_CALLBACK(neonserv_event_mode_userlist_lookup) { + struct neonserv_event_mode_cache *cache = data; + neonserv_event_mode_async1(cache->client, cache->user, chan, cache->modes, cache->argv, cache->argc); + if(cache->argc) { + int i; + for(i = 0; i < cache->argc; i++) { + free(cache->argv[i]); + } + free(cache->argv); + } + free(cache->modes); + free(cache); +} + +static void neonserv_event_mode_async1(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, char *modes, char **argv, int argc) { + struct ClientSocket *textclient = ((client->flags & SOCKET_FLAG_PREFERRED) ? client : get_prefered_bot(client->botid)); + int botwar_detect_executed = 0; + MYSQL_ROW row, defaults = NULL; + int i, arg, add = 1, skip = 0; + unsigned int modetype; + int db_canop, db_canhalfop, db_canvoice, db_canban, db_enfmodes, db_getop, db_gethalfop, db_getvoice; + struct ModeNode *modelock = createModeNode(NULL); + struct ModeBuffer *modeBuf; + struct UserNode *cuser; + struct ChanUser *chanuser; + int with_halfops = get_int_field("General.have_halfop"); + modeBuf = initModeBuffer(client, chan); + printf_mysql_query("SELECT `channel_canop`, `channel_canvoice`, `channel_canban`, `channel_enfmodes`, `channel_modes`, `channel_getop`, `channel_getvoice`, `channel_gethalfop`, `channel_canhalfop` FROM `channels` WHERE `channel_id` = '%d'", chan->channel_id); + row = mysql_fetch_row(mysql_use()); + if(row[0] == NULL || row[1] == NULL || row[2] == NULL || row[3] == NULL || row[4] == NULL || row[5] == NULL || row[6] == NULL || (row[7] == NULL && with_halfops) || (row[8] == NULL && with_halfops)) { + printf_mysql_query("SELECT `channel_canop`, `channel_canvoice`, `channel_canban`, `channel_enfmodes`, `channel_modes`, `channel_getop`, `channel_getvoice`, `channel_gethalfop`, `channel_canhalfop` FROM `channels` WHERE `channel_name` = 'defaults'"); + defaults = mysql_fetch_row(mysql_use()); + } + db_canop = atoi((row[0] ? row[0] : defaults[0])); + db_canvoice = atoi((row[1] ? row[1] : defaults[1])); + db_canban = atoi((row[2] ? row[2] : defaults[2])); + db_enfmodes = atoi((row[3] ? row[3] : defaults[3])); + db_getop = atoi((row[5] ? row[5] : defaults[5])); + db_getvoice = atoi((row[6] ? row[6] : defaults[6])); + db_gethalfop = (with_halfops ? atoi((row[7] ? row[7] : defaults[7])) : 0); + db_canhalfop = (with_halfops ? atoi((row[8] ? row[8] : defaults[8])) : 0); + if(row[4]) + parseModeString(modelock, row[4]); + else if(defaults[4]) + parseModeString(modelock, defaults[4]); + int uaccess = getChannelAccess(user, chan); + int caccess; + char *carg; + int sent_modes_locked = 0; + char deop_user = 0; + char tmp[MAXLEN]; + arg = 0; + for(i = 0; i < strlen(modes); i++) { + switch(modes[i]) { + case '+': + add = 1; + break; + case '-': + add = 0; + break; + case 'o': + case 'h': + case 'v': + if(arg == argc) { + break; + } + carg = argv[arg++]; + cuser = searchUserByNick(carg); + if(!cuser) { + break; //internal Bot error - this should never happen + } + caccess = getChannelAccess(cuser, chan); + if(modes[i] == 'o' && !add && cuser == client->user) { + //someone deopped the bot??? + if(!neonserv_cmd_mode_botwar_detect(client, user, chan, &botwar_detect_executed)) + requestOp(client->user, chan); + } else if(modes[i] == 'o' && add && isBot(cuser)) { + //someone opped a bot + if(!neonserv_cmd_mode_botwar_detect(client, user, chan, &botwar_detect_executed)) + module_neonbackup_recover_chan(chan); + } + if((modes[i] == 'o' || (modes[i] == 'h' && !with_halfops)) && !(add && isBot(cuser))) { + if(uaccess < db_canop) { + reply(textclient, user, "NS_MODE_ENFOPS", chan->name); + db_canop = -1; + if(uaccess < db_getop) + deop_user = 1; + } + if(db_canop == -1 && caccess < db_getop) { + if(!neonserv_cmd_mode_botwar_detect(client, user, chan, &botwar_detect_executed)) + modeBufferSet(modeBuf, !add, modes[i], carg); + break; + } + } else if(modes[i] == 'h' && with_halfops && !(add && isBot(cuser))) { + if(uaccess < db_canhalfop) { + reply(textclient, user, "NS_MODE_ENFOPS", chan->name); + db_canhalfop = -1; + if(uaccess < db_gethalfop) + deop_user = 1; + } + if(db_canhalfop == -1 && caccess < db_gethalfop) { + if(!neonserv_cmd_mode_botwar_detect(client, user, chan, &botwar_detect_executed)) + modeBufferSet(modeBuf, !add, modes[i], carg); + break; + } + } else if(modes[i] == 'v' && !(add && isBot(cuser))) { + if(uaccess < db_canvoice) { + reply(textclient, user, "NS_MODE_ENFVOICE", chan->name); + db_canvoice = -1; + if(uaccess < db_getop) + deop_user = 1; + } + if(db_canvoice == -1 && caccess < db_getvoice) { + if(!neonserv_cmd_mode_botwar_detect(client, user, chan, &botwar_detect_executed)) + modeBufferSet(modeBuf, !add, modes[i], carg); + break; + } + } + if(!add) { + //check protection + if(isBot(cuser)) { + //don't send this - just try to reop + //reply(textclient, user, "NS_SERVICE_IMMUNE", cuser->nick); + if(!neonserv_cmd_mode_botwar_detect(client, user, chan, &botwar_detect_executed)) + modeBufferSet(modeBuf, !add, modes[i], carg); + break; + } + if(isUserProtected(chan, cuser, user)) { + reply(textclient, user, "NS_USER_PROTECTED", cuser->nick); + if(!neonserv_cmd_mode_botwar_detect(client, user, chan, &botwar_detect_executed)) + modeBufferSet(modeBuf, !add, modes[i], carg); + if(uaccess < db_getop) + deop_user = 1; + break; + } + } + break; + case 'b': + if(arg == argc) { + break; + } + carg = argv[arg++]; + if(uaccess < db_canban) { + reply(textclient, user, "NS_MODE_CANBAN", chan->name); + db_canban = -1; + } + char hostmask_buffer[NICKLEN+USERLEN+HOSTLEN+3]; + char usermask[NICKLEN+USERLEN+HOSTLEN+3]; + int match_count = 0; + if(db_canban == -1) { + if(!neonserv_cmd_mode_botwar_detect(client, user, chan, &botwar_detect_executed)) { + if(!add) { + //check if a user just removed a bot ban + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + cuser = chanuser->user; + sprintf(usermask, "%s!%s@%s", cuser->nick, cuser->ident, cuser->host); + if(!match(carg, usermask) && isBot(cuser)) { + skip = 1; + break; + } + } + if(skip) + break; + } + modeBufferSet(modeBuf, !add, modes[i], carg); + } + if(uaccess < db_getop) + deop_user = 1; + break; + } + carg = make_banmask(carg, hostmask_buffer); + if(add) { + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + cuser = chanuser->user; + sprintf(usermask, "%s!%s@%s", cuser->nick, cuser->ident, cuser->host); + if(!match(carg, usermask)) { + if(isUserProtected(chan, cuser, user)) { + reply(textclient, user, "NS_USER_PROTECTED", cuser->nick); + skip = 1; + break; + } + match_count++; + } + } + if(match_count > 4 && (match_count * 3) > chan->usercount) { + reply(textclient, user, "NS_LAME_MASK_WARNING", carg, match_count); + } + } + if(skip) { + if(!neonserv_cmd_mode_botwar_detect(client, user, chan, &botwar_detect_executed)) + modeBufferSet(modeBuf, !add, modes[i], carg); + if(uaccess < db_getop) + deop_user = 1; + } + break; + default: + modetype = getModeType(modelock, modes[i]); + if(modetype == 0) { + break; //unknown mode + } + + if(add && (modetype & CHANNEL_MODE_TYPE) != CHANNEL_MODE_TYPE_D) { + if(arg == argc) { + break; + } + carg = argv[arg++]; + if((modetype & CHANNEL_MODE_VALUE) == CHANNEL_MODE_VALUE_STRING && isModeSet(modelock, modes[i])) { + char *modelock_val = getModeValue(modelock, modes[i]); + if(stricmp(carg, modelock_val) && uaccess < db_enfmodes && !isGodMode(user)) { + if(!sent_modes_locked) { + getFullModeString(modelock, tmp); + reply(textclient, user, "NS_MODE_LOCKED", tmp, chan->name); + sent_modes_locked = 1; + if(uaccess < db_getop) + deop_user = 1; + } + if(!neonserv_cmd_mode_botwar_detect(client, user, chan, &botwar_detect_executed)) + modeBufferSet(modeBuf, add, modes[i], modelock_val); + break; + } + } + if((modetype & CHANNEL_MODE_VALUE) == CHANNEL_MODE_VALUE_STRING && isModeSet(modelock, modes[i])) { + int *modelock_val = getModeValue(modelock, modes[i]); + if(atoi(carg) != *modelock_val && uaccess < db_enfmodes && !isGodMode(user)) { + if(!sent_modes_locked) { + getFullModeString(modelock, tmp); + reply(textclient, user, "NS_MODE_LOCKED", tmp, chan->name); + sent_modes_locked = 1; + if(uaccess < db_getop) + deop_user = 1; + } + sprintf(tmp, "%d", *modelock_val); + if(!neonserv_cmd_mode_botwar_detect(client, user, chan, &botwar_detect_executed)) + modeBufferSet(modeBuf, add, modes[i], tmp); + break; + } + } + } else if(!add && (modetype & CHANNEL_MODE_TYPE) == CHANNEL_MODE_TYPE_B) { + if(arg == argc) { + break; + } + carg = argv[arg++]; + } else + carg = NULL; + if(isModeAffected(modelock, modes[i]) && add == !isModeSet(modelock, modes[i]) && uaccess < db_enfmodes && !isGodMode(user)) { + if(!sent_modes_locked) { + getFullModeString(modelock, tmp); + reply(textclient, user, "NS_MODE_LOCKED", tmp, chan->name); + sent_modes_locked = 1; + if(uaccess < db_getop) + deop_user = 1; + } + if(!neonserv_cmd_mode_botwar_detect(client, user, chan, &botwar_detect_executed)) + modeBufferSet(modeBuf, !add, modes[i], carg); + break; + } + break; + } + } + if(deop_user && !neonserv_cmd_mode_botwar_detect(client, user, chan, &botwar_detect_executed)) { + modeBufferDeop(modeBuf, user->nick); + } + freeModeBuffer(modeBuf); + freeModeNode(modelock); +} + +static int neonserv_cmd_mode_botwar_detect(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, int *botwar_detect_executed) { + struct ChanUser *chanuser = getChanUser(user, chan); + if(!chanuser) return 0; //flying super cow? + if(time(0) - chanuser->changeTime > BOTWAR_DETECTION_TIME) { + chanuser->changeTime = time(0); + chanuser->chageEvents = 1; + } else { + if(!*botwar_detect_executed) + chanuser->chageEvents++; + *botwar_detect_executed = 1; + if(chanuser->chageEvents >= BOTWAR_DETECTION_EVENTS || chanuser->chageEvents < 0) { + //BOTWAR! + chanuser->changeTime = time(0); + if(chanuser->chageEvents > 0) { + char *alertchan = get_string_field("General.alertchan"); + putsock(client, "NOTICE @%s :%s %s", chan->name, get_language_string(user, "NS_BOTWAR_DETECTED"), (alertchan ? get_language_string(user, "NS_BOTWAR_REPORTED") : "")); + if(alertchan) { + struct ChanNode *alertchan_chan = getChanByName(alertchan); + struct ClientSocket *alertclient; + if(alertchan_chan && (alertclient = getBotForChannel(chan)) != NULL) { + char msgBuf[MAXLEN]; + putsock(alertclient, "PRIVMSG %s :%s", alertchan_chan->name, build_language_string(NULL, msgBuf, "NS_BOTWAR_ALERT", chan->name, user->nick)); + } + } + } + chanuser->chageEvents = -2; + return 1; + } + } + return 0; +} + diff --git a/src/modules/NeonServ.mod/event_neonserv_notice.c b/src/modules/NeonServ.mod/event_neonserv_notice.c new file mode 100644 index 0000000..c15089a --- /dev/null +++ b/src/modules/NeonServ.mod/event_neonserv_notice.c @@ -0,0 +1,107 @@ +/* event_neonserv_notice.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +struct neonserv_event_notice_cache { + struct ClientSocket *client; + struct UserNode *user; + struct ChanNode *chan; + char *message; +}; + +static USERAUTH_CALLBACK(neonserv_event_notice_nick_lookup); +static void neonserv_event_notice_async1(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, char *message); + +static void neonserv_event_channotice(struct UserNode *user, struct ChanNode *chan, char *message) { + struct ClientSocket *client = getBotForChannel(chan); + if(!client) return; //we can't "see" this event + if(isNetworkService(user)) return; + loadChannelSettings(chan); + if(!(chan->flags & CHANFLAG_CHAN_REGISTERED)) return; + if(!(user->flags & USERFLAG_ISAUTHED)) { + struct neonserv_event_notice_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->user = user; + cache->chan = chan; + cache->message = strdup(message); + get_userauth(user, module_id, neonserv_event_notice_nick_lookup, cache); + } else + neonserv_event_notice_async1(client, user, chan, message); +} + +static USERAUTH_CALLBACK(neonserv_event_notice_nick_lookup) { + struct neonserv_event_notice_cache *cache = data; + if(user) { + neonserv_event_notice_async1(cache->client, cache->user, cache->chan, cache->message); + } + free(cache->message); + free(cache); +} + +static void neonserv_event_notice_async1(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, char *message) { + MYSQL_RES *res; + MYSQL_ROW row, defaultrow = NULL, chanuser; + int uaccess = 0; + printf_mysql_query("SELECT `channel_ctcp`, `channel_ctcpreaction` FROM `channels` WHERE `channel_id` = '%d'", chan->channel_id); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) == NULL) return; + if(!row[0] || !row[1]) { + printf_mysql_query("SELECT `channel_ctcp`, `channel_ctcpreaction` FROM `channels` WHERE `channel_name` = 'defaults'"); + res = mysql_use(); + defaultrow = mysql_fetch_row(res); + } + int noticeaccess = atoi((row[0] ? row[0] : defaultrow[0])); + if((user->flags & USERFLAG_ISAUTHED)) { + printf_mysql_query("SELECT `chanuser_access`, `chanuser_flags` FROM `chanusers` LEFT JOIN `users` ON `chanuser_uid` = `user_id` WHERE `chanuser_cid` = '%d' AND `user_user` = '%s'", chan->channel_id, escape_string(user->auth)); + res = mysql_use(); + chanuser = mysql_fetch_row(res); + if(chanuser) + uaccess = ((atoi(chanuser[1]) & DB_CHANUSER_SUSPENDED) ? 0 : atoi(chanuser[0])); + } + int duration = 0; + char banmaskBuf[NICKLEN+USERLEN+HOSTLEN+3]; + char *banmask = NULL; + char *reason = "disallowed channel NOTICE"; + if(uaccess < noticeaccess) { + switch(atoi((row[1] ? row[1] : defaultrow[1]))) { + case 2: //TIMEBAN: 3min + duration = 180; + case 3: //TIMEBAN: 1h + if(!duration) + duration = 3600; + banmask = generate_banmask(user, banmaskBuf); + printf_mysql_query("INSERT INTO `bans` (`ban_channel`, `ban_mask`, `ban_triggered`, `ban_timeout`, `ban_owner`, `ban_reason`) VALUES ('%d', '%s', UNIX_TIMESTAMP(), '%lu', '%d', '%s')", chan->channel_id, escape_string(banmask), (unsigned long) (time(0) + duration), 0, escape_string(reason)); + int banid = (int) mysql_insert_id(get_mysql_conn()); + char nameBuf[MAXLEN]; + char banidBuf[20]; + sprintf(nameBuf, "ban_%d", banid); + sprintf(banidBuf, "%d", banid); + timeq_add_name(nameBuf, duration, module_id, channel_ban_timeout, strdup(banidBuf)); + case 1: //KICKBAN + if(!banmask) + banmask = generate_banmask(user, banmaskBuf); + putsock(client, "MODE %s +b %s", chan->name, banmask); + case 0: //KICK + putsock(client, "KICK %s %s :%s", chan->name, user->nick, reason); + break; + } + } +} + diff --git a/src/modules/NeonServ.mod/event_neonserv_part.c b/src/modules/NeonServ.mod/event_neonserv_part.c new file mode 100644 index 0000000..70a6683 --- /dev/null +++ b/src/modules/NeonServ.mod/event_neonserv_part.c @@ -0,0 +1,31 @@ +/* event_neonserv_part.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +static void neonserv_event_part(struct ChanUser *chanuser, int quit, char *reason) { + struct ChanNode *chan = chanuser->chan; + struct UserNode *user = chanuser->user; + MYSQL_RES *res; + MYSQL_ROW chanuserrow; + loadChannelSettings(chan); + if(!(chan->flags & CHANFLAG_CHAN_REGISTERED)) return; + if((user->flags & USERFLAG_ISAUTHED)) { + printf_mysql_query("SELECT `chanuser_id` FROM `chanusers` LEFT JOIN `users` ON `chanuser_uid` = `user_id` WHERE `chanuser_cid` = '%d' AND `user_user` = '%s'", chan->channel_id, escape_string(user->auth)); + res = mysql_use(); + if((chanuserrow = mysql_fetch_row(res)) != NULL) + printf_mysql_query("UPDATE `chanusers` SET `chanuser_seen` = UNIX_TIMESTAMP() WHERE `chanuser_id` = '%s'", chanuserrow[0]); + } +} diff --git a/src/modules/NeonServ.mod/event_neonserv_topic.c b/src/modules/NeonServ.mod/event_neonserv_topic.c new file mode 100644 index 0000000..3958f83 --- /dev/null +++ b/src/modules/NeonServ.mod/event_neonserv_topic.c @@ -0,0 +1,114 @@ +/* event_neonserv_topic.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +struct neonserv_event_topic_cache { + struct ClientSocket *client; + struct UserNode *user; + struct ChanNode *chan; + char *new_topic; +}; + +static USERAUTH_CALLBACK(neonserv_event_topic_nick_lookup); +static void neonserv_event_topic_async1(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, const char *new_topic); + +static void neonserv_event_topic(struct UserNode *user, struct ChanNode *chan, const char *new_topic) { + struct ClientSocket *client = getBotForChannel(chan); + if(!client) return; //we can't "see" this event + if(isNetworkService(user)) return; + loadChannelSettings(chan); + if(!(chan->flags & CHANFLAG_CHAN_REGISTERED)) return; + if(!(user->flags & USERFLAG_ISAUTHED)) { + struct neonserv_event_topic_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->user = user; + cache->chan = chan; + cache->new_topic = strdup(new_topic); + get_userauth(user, module_id, neonserv_event_topic_nick_lookup, cache); + } else + neonserv_event_topic_async1(client, user, chan, new_topic); +} + +static USERAUTH_CALLBACK(neonserv_event_topic_nick_lookup) { + struct neonserv_event_topic_cache *cache = data; + if(user) { + neonserv_event_topic_async1(cache->client, cache->user, cache->chan, cache->new_topic); + } + free(cache->new_topic); + free(cache); +} + +static void neonserv_event_topic_async1(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, const char *new_topic) { + MYSQL_RES *res; + MYSQL_ROW row, defaultrow = NULL, chanuserrow; + int uaccess = 0; + printf_mysql_query("SELECT `channel_changetopic`, `channel_topicsnarf` FROM `channels` WHERE `channel_id` = '%d'", chan->channel_id); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) == NULL) return; + if(!row[0] || !row[1]) { + printf_mysql_query("SELECT `channel_changetopic`, `channel_topicsnarf` FROM `channels` WHERE `channel_name` = 'defaults'"); + res = mysql_use(); + defaultrow = mysql_fetch_row(res); + } + int changetopic = atoi((row[0] ? row[0] : defaultrow[0])); + int topicsnarf = atoi((row[1] ? row[1] : defaultrow[1])); + if((user->flags & USERFLAG_ISAUTHED)) { + printf_mysql_query("SELECT `chanuser_access`, `chanuser_flags` FROM `chanusers` LEFT JOIN `users` ON `chanuser_uid` = `user_id` WHERE `chanuser_cid` = '%d' AND `user_user` = '%s'", chan->channel_id, escape_string(user->auth)); + res = mysql_use(); + chanuserrow = mysql_fetch_row(res); + if(chanuserrow) + uaccess = ((atoi(chanuserrow[1]) & DB_CHANUSER_SUSPENDED) ? 0 : atoi(chanuserrow[0])); + } + if(uaccess < changetopic) { + //undo topic change + struct ClientSocket *textclient = ((client->flags & SOCKET_FLAG_PREFERRED) ? client : get_prefered_bot(client->botid)); + struct ChanUser *chanuser = getChanUser(user, chan); + if(!chanuser) return; //flying super cow? + if(time(0) - chanuser->changeTime > BOTWAR_DETECTION_TIME) { + chanuser->changeTime = time(0); + chanuser->chageEvents = 1; + } else { + chanuser->chageEvents++; + if(chanuser->chageEvents >= BOTWAR_DETECTION_EVENTS || chanuser->chageEvents < 0) { + //BOTWAR! + chanuser->changeTime = time(0); + if(chanuser->chageEvents > 0) { + char *alertchan = get_string_field("General.alertchan"); + putsock(client, "NOTICE @%s :%s %s", chan->name, get_language_string(user, "NS_BOTWAR_DETECTED"), (alertchan ? get_language_string(user, "NS_BOTWAR_REPORTED") : "")); + if(alertchan) { + struct ChanNode *alertchan_chan = getChanByName(alertchan); + struct ClientSocket *alertclient; + if(alertchan_chan && (alertclient = getBotForChannel(chan)) != NULL) { + char msgBuf[MAXLEN]; + putsock(alertclient, "PRIVMSG %s :%s", alertchan_chan->name, build_language_string(NULL, msgBuf, "NS_BOTWAR_ALERT", chan->name, user->nick)); + } + } + } + chanuser->chageEvents = -2; + return; + } + } + reply(textclient, user, "NS_TOPIC_ACCESS", chan->name); + putsock(client, "TOPIC %s :%s", chan->name, chan->topic); + } else if(uaccess >= topicsnarf) { + printf_mysql_query("UPDATE `channels` SET `channel_defaulttopic` = '%s' WHERE `channel_id` = '%d'", escape_string(new_topic), chan->channel_id); + } +} + diff --git a/src/modules/NeonServ.mod/module.c b/src/modules/NeonServ.mod/module.c new file mode 100644 index 0000000..f24d537 --- /dev/null +++ b/src/modules/NeonServ.mod/module.c @@ -0,0 +1,34 @@ +/* module.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#include "../module.h" +#include "bot_NeonServ.h" +#include "cmd_neonserv.h" + +static int module_initialize() { + register_commands(); + return 0; +} + +static void module_start(int type) { + init_NeonServ(type); +} + +static void module_stop(int type) { + free_NeonServ(type); +} + +MODULE_HEADER(module_initialize, module_start, module_stop); diff --git a/src/modules/NeonSpam.mod/bot_NeonSpam.c b/src/modules/NeonSpam.mod/bot_NeonSpam.c new file mode 100644 index 0000000..dd2e4d1 --- /dev/null +++ b/src/modules/NeonSpam.mod/bot_NeonSpam.c @@ -0,0 +1,421 @@ +/* bot_NeonSpam.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#include "../module.h" +#include "../botid.h" + +#include "bot_NeonSpam.h" +#include "../../modcmd.h" +#include "../../IRCParser.h" +#include "../../IRCEvents.h" +#include "../../UserNode.h" +#include "../../ChanNode.h" +#include "../../ChanUser.h" +#include "../../ModeNode.h" +#include "../../BanNode.h" +#include "../../ClientSocket.h" +#include "../../mysqlConn.h" +#include "../../lang.h" +#include "../../HandleInfoHandler.h" +#include "../../WHOHandler.h" +#include "../../DBHelper.h" +#include "../../tools.h" +#include "../../timeq.h" +#include "../../version.h" +#include "../../EventLogger.h" +#include "../../bots.h" +#include "cmd_neonspam.h" + +#define BOTID NEONSPAM_BOTID +#define BOTALIAS "NeonSpam" + +static const struct default_language_entry msgtab[] = { + {"SS_SET_PERCENT", "%u is an invalid percent value (valid: 0-100)"}, /* {ARGS: 120} */ + {"SS_SET_SENSIBILITY", "%s is an invalid sensibility format. (valid: amount:time e.g. 5:10)"}, /* {ARGS: "möp"} */ + {"SS_SET_SENSIBILITY_AMOUNT", "%d is an invalid amount value. (valid: %d-%d)"}, /* {ARGS: 120, 1, 5} */ + {"SS_SET_SENSIBILITY_TIME", "%d is an invalid time value. (valid: %d-%d)"}, /* {ARGS: 300, 15, 180} */ + {"SS_SET_SPAMLIMIT", "%d is an invalid spamlimit value. (valid: %d-%d)"}, /* {ARGS: 120, 2, 8} */ + {"SS_SET_OPTION_SpamReaction_0", "Kick"}, + {"SS_SET_OPTION_SpamReaction_1", "KickBan"}, + {"SS_SET_OPTION_SpamReaction_2", "Timed Ban"}, + {"SS_SET_OPTION_FloodReaction_0", "Kick"}, + {"SS_SET_OPTION_FloodReaction_1", "KickBan"}, + {"SS_SET_OPTION_FloodReaction_2", "Timed Ban"}, + {"SS_SET_OPTION_JoinReaction_0", "Kick"}, + {"SS_SET_OPTION_JoinReaction_1", "KickBan"}, + {"SS_SET_OPTION_JoinReaction_2", "Timed Ban"}, + {"SS_SET_OPTION_CapsReaction_0", "Kick"}, + {"SS_SET_OPTION_CapsReaction_1", "KickBan"}, + {"SS_SET_OPTION_CapsReaction_2", "Timed Ban"}, + {"SS_SET_OPTION_DigitReaction_0", "Kick"}, + {"SS_SET_OPTION_DigitReaction_1", "KickBan"}, + {"SS_SET_OPTION_DigitReaction_2", "Timed Ban"}, + {"SS_SET_OPTION_BadwordReaction_0", "Kick"}, + {"SS_SET_OPTION_BadwordReaction_1", "KickBan"}, + {"SS_SET_OPTION_BadwordReaction_2", "Timed Ban"}, + {"SS_BADWORD_ADDED", "Badword '%s' added. (ID: %d)"}, /* {ARGS: "NeonServ is bad", 1} */ + {"SS_BADWORD_ID_UNKNOWN", "Badword ID %d was not found in %s."}, /* {ARGS: 1, "#channel"} */ + {"SS_BADWORD_DELETED", "Badword '%s' deleted. (ID: %d)"}, /* {ARGS: "NeonServ is bad", 1} */ + {"SS_BADWORDS_REACTION_0", "Kick"}, + {"SS_BADWORDS_REACTION_1", "KickBan"}, + {"SS_BADWORDS_REACTION_2", "Timed Ban"}, + {"SS_BADWORDS_ID", "Id"}, + {"SS_BADWORDS_PATTERN", "Pattern"}, + {"SS_BADWORDS_SCAN", "Scan"}, + {"SS_BADWORDS_REACTION", "Reaction"}, + {NULL, NULL} +}; + +static unsigned int convertNeonSpamSettingsToFlags(char *str); +static void createSpamNode(struct ChanUser *chanuser); +static void freeJoinNode(struct NeonSpamJoinNode *joinnode); +static struct NeonSpamJoinNode *getNeonSpamJoinNode(struct ChanUser *chanuser); + +#define SPAMSERV_CHECK_IGNORE 0 +#define SPAMSERV_CHECK_WARN 1 +#define SPAMSERV_CHECK_PUNISH 2 +#define SPAMSERV_CHECK_DEAD 3 /* scanner has already killed the user */ + +#define SPAMSERV_MSG_SPAM "Spamming" +#define SPAMSERV_MSG_FLOOD "Flooding the channel/network" +#define SPAMSERV_MSG_ADV "Advertising" +#define SPAMSERV_MSG_JOINFLOOD "Join flooding the channel" +#define SPAMSERV_MSG_WARNING "%s is against the channel rules" +#define SPAMSERV_MSG_BOTNET "BotNet detected." +#define SPAMSERV_MSG_CAPS "Using too many chars in UPPER CASE" +#define SPAMSERV_MSG_DIGIT "Using too many numeric chars" +#define SPAMSERV_MSG_BADWORD "Your message contained a forbidden word." + +//EVENTS +#include "event_neonspam_join.c" +#include "event_neonspam_chanmsg.c" + +static void neonspam_bot_ready(struct ClientSocket *client) { + if(client->botid != BOTID) + return; + MYSQL_RES *res; + MYSQL_ROW row; + + printf_mysql_query("SELECT `automodes`, `oper_user`, `oper_pass` FROM `bots` WHERE `id` = '%d'", client->clientid); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + if(row[1] && row[2]) { + putsock(client, "OPER %s %s", row[1], row[2]); + } + putsock(client, "MODE %s +%s", client->user->nick, row[0]); + } + + printf_mysql_query("SELECT `channel_name`, `channel_key` FROM `bot_channels` LEFT JOIN `channels` ON `chanid` = `channel_id` WHERE `botid` = '%d' AND `suspended` = '0'", client->clientid); + res = mysql_use(); + + while ((row = mysql_fetch_row(res)) != NULL) { + putsock(client, "JOIN %s %s", row[0], row[1]); + } +} + +static void neonspam_trigger_callback(int clientid, struct ChanNode *chan, char *trigger) { + MYSQL_RES *res; + MYSQL_ROW row; + loadChannelSettings(chan); + if(!(chan->flags & CHANFLAG_CHAN_REGISTERED)) { + strcpy(trigger, "~"); + return; + } + printf_mysql_query("SELECT `trigger`, `defaulttrigger` FROM `bot_channels` LEFT JOIN `bots` ON `botid` = `bots`.`id` WHERE `chanid` = '%d' AND `botclass` = '%d'", chan->channel_id, BOTID); + res = mysql_use(); + if(!(row = mysql_fetch_row(res))) { + strcpy(trigger, "~"); + return; + } + if(row[0] && *row[0]) + strcpy(trigger, row[0]); + else + strcpy(trigger, ((row[1] && *row[1]) ? row[1] : "~")); +} + +static void start_bots(int type) { + struct ClientSocket *client; + MYSQL_RES *res, *res2; + MYSQL_ROW row; + + if(type == MODSTATE_STARTSTOP) { + printf_mysql_query("SELECT `nick`, `ident`, `realname`, `server`, `port`, `pass`, `textbot`, `id`, `queue`, `ssl`, `bind`, `secret` FROM `bots` WHERE `botclass` = '%d' AND `active` = '1'", BOTID); + res = mysql_use(); + + while ((row = mysql_fetch_row(res)) != NULL) { + client = create_socket(row[3], atoi(row[4]), row[10], row[5], row[0], row[1], row[2]); + client->flags |= (strcmp(row[6], "0") ? SOCKET_FLAG_PREFERRED : 0); + client->flags |= (strcmp(row[8], "0") ? SOCKET_FLAG_USE_QUEUE : 0); + client->flags |= (strcmp(row[9], "0") ? SOCKET_FLAG_SSL : 0); + client->flags |= (strcmp(row[11], "0") ? SOCKET_FLAG_SECRET_BOT : 0); + client->flags |= SOCKET_FLAG_REQUEST_INVITE | SOCKET_FLAG_REQUEST_OP; + client->botid = BOTID; + client->clientid = atoi(row[7]); + connect_socket(client); + } + } + + printf_mysql_query("SELECT `command`, `function`, `parameters`, `global_access`, `chan_access`, `flags` FROM `bot_binds` WHERE `botclass` = '%d'", BOTID); + res2 = mysql_use(); + while ((row = mysql_fetch_row(res2)) != NULL) { + if(bind_cmd_to_command(BOTID, row[0], row[1])) { + if(row[2] && strcmp(row[2], "")) { + bind_set_parameters(BOTID, row[0], row[2]); + } + if(row[3]) { + bind_set_global_access(BOTID, row[0], atoi(row[3])); + } + if(row[4]) { + bind_set_channel_access(BOTID, row[0], row[4]); + } + if(strcmp(row[5], "0")) + bind_set_bind_flags(BOTID, row[0], atoi(row[5])); + } + } + bind_unbound_required_functions(BOTID); +} + +char* convertNeonSpamSettingsToString(unsigned int flags, char *buffer) { + int pos = 0; + unsigned int i; + int j = 0; + char *chars = SPAMSETTINGS_CHARS; + for(i = 1; i <= SPAMSETTINGS_FLAGS; i = i << 1) { + if(flags & i) + buffer[pos++] = chars[j]; + j++; + } + buffer[pos] = '\0'; + return buffer; +} + +static unsigned int convertNeonSpamSettingsToFlags(char *str) { + unsigned int i = 1, flags = 0; + int j = 0; + char *chars = SPAMSETTINGS_CHARS; + while(*str) { + for(; i <= SPAMSETTINGS_FLAGS; i = i << 1) { + if(*str == chars[j]) { + flags |= i; + j++; + i = i << 1; + break; + } + j++; + } + str++; + } + return flags; +} + +int loadNeonSpamSettings(struct ChanNode *chan) { + if(chan->spam_settings) return 0; + struct NeonSpamSettings *settings = malloc(sizeof(*settings)); + if(!settings) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return 0; + } + MYSQL_RES *res; + MYSQL_ROW row, defaults = NULL; + loadChannelSettings(chan); + printf_mysql_query("SELECT `channel_scanner`, `channel_spam_limit`, `channel_spam_except`, `channel_flood_limit`, `channel_flood_time`, `channel_flood_except`, `channel_join_limit`, `channel_join_time`, `channel_join_except`, `channel_caps_percent`, `channel_caps_except`, `channel_digit_percent`, `channel_digit_except`, `channel_badword_except` FROM `channels` WHERE `channel_id` = '%d'", chan->channel_id); + res = mysql_use(); + if(!(row = mysql_fetch_row(res))) + return 0; + if(!row[0] || !row[1] || !row[2] || !row[3] || !row[4] || !row[5] || !row[6] || !row[7] || !row[8] || !row[9] || !row[10] || !row[11] || !row[12]) { + printf_mysql_query("SELECT `channel_scanner`, `channel_spam_limit`, `channel_spam_except`, `channel_flood_limit`, `channel_flood_time`, `channel_flood_except`, `channel_join_limit`, `channel_join_time`, `channel_join_except`, `channel_caps_percent`, `channel_caps_except`, `channel_digit_percent`, `channel_digit_except`, `channel_badword_except` FROM `channels` WHERE `channel_name` = 'defaults'"); + res = mysql_use(); + defaults = mysql_fetch_row(res); + } + settings->flags = convertNeonSpamSettingsToFlags(row[0] ? row[0] : defaults[0]); + settings->spam_amount = atoi(row[1] ? row[1] : defaults[1]); + settings->exceptlevel[SPAMSETTINGS_SPAMEXCINDEX] = atoi(row[2] ? row[2] : defaults[2]); + settings->sensibility_amount[SPAMSETTINGS_FLOODSENINDEX] = atoi(row[3] ? row[3] : defaults[3]); + settings->sensibility_time[SPAMSETTINGS_FLOODSENINDEX] = atoi(row[4] ? row[4] : defaults[4]); + settings->exceptlevel[SPAMSETTINGS_FLOODEXCINDEX] = atoi(row[5] ? row[5] : defaults[5]); + settings->sensibility_amount[SPAMSETTINGS_JOINSENINDEX] = atoi(row[6] ? row[6] : defaults[6]); + settings->sensibility_time[SPAMSETTINGS_JOINSENINDEX] = atoi(row[7] ? row[7] : defaults[7]); + settings->exceptlevel[SPAMSETTINGS_JOINEXCINDEX] = atoi(row[8] ? row[8] : defaults[8]); + settings->percent[SPAMSETTINGS_CAPSPERCENTINDEX] = atoi(row[9] ? row[9] : defaults[9]); + settings->exceptlevel[SPAMSETTINGS_CAPSEXCINDEX] = atoi(row[10] ? row[10] : defaults[10]); + settings->percent[SPAMSETTINGS_DIGITPERCENTINDEX] = atoi(row[11] ? row[11] : defaults[11]); + settings->exceptlevel[SPAMSETTINGS_DIGITEXCINDEX] = atoi(row[12] ? row[12] : defaults[12]); + settings->exceptlevel[SPAMSETTINGS_BADWORDEXCINDEX] = atoi(row[13] ? row[13] : defaults[13]); + settings->join_nodes = NULL; + settings->lastmsg_time = 0; + int i; + for(i = 0; i < BOTNETSCAN_USERS; i++) + settings->botnicks[i] = NULL; + settings->badwords = NULL; + printf_mysql_query("SELECT `badword_match`, `badword_reaction`, `badword_reaction_time`, `badword_id`, `badword_scan_ops`, `badword_scan_voice`, `badword_exceptlevel`, `badword_use_default`, `badword_use_default_reaction` FROM `spamserv_badwords` WHERE `badword_cid` = '%d'", chan->channel_id); + res = mysql_use(); + while((row = mysql_fetch_row(res))) { + struct NeonSpamBadword *badword = malloc(sizeof(*badword)); + badword->id = atoi(row[3]); + badword->badword = strdup(row[0]); + badword->use_defaults = atoi(row[7]); + badword->exceptlevel = atoi(row[6]); + badword->scan_ops = atoi(row[4]); + badword->scan_voice = atoi(row[5]); + badword->use_default_reaction = atoi(row[8]); + badword->reaction = atoi(row[1]); + badword->reaction_time = atoi(row[2]); + badword->next = settings->badwords; + settings->badwords = badword; + } + chan->spam_settings = settings; + return 1; +} + +void freeNeonSpamSettings(struct NeonSpamSettings *settings) { + struct NeonSpamJoinNode *joinnode, *nextjoinnode; + for(joinnode = settings->join_nodes; joinnode; joinnode = nextjoinnode) { + nextjoinnode = joinnode->next; + freeJoinNode(joinnode); + } + struct NeonSpamBadword *badword, *nextbadword; + for(badword = settings->badwords; badword; badword = nextbadword) { + nextbadword = badword->next; + free(badword->badword); + free(badword); + } + free(settings); +} + +static void freeJoinNode(struct NeonSpamJoinNode *joinnode) { + free(joinnode->ident); + free(joinnode->host); + free(joinnode); +} + +static struct NeonSpamJoinNode *getNeonSpamJoinNode(struct ChanUser *chanuser) { + struct NeonSpamJoinNode *joinnode, *prevjoinnode = NULL, *nextjoinnode, *result = NULL; + for(joinnode = chanuser->chan->spam_settings->join_nodes; joinnode; joinnode = nextjoinnode) { + nextjoinnode = joinnode->next; + if(!stricmp(joinnode->ident, chanuser->user->ident) && !stricmp(joinnode->host, chanuser->user->host)) { + prevjoinnode = joinnode; + result = joinnode; + } else if(time(0) - joinnode->last_penalty_update > MAX_JOIN_TIME) { + freeJoinNode(joinnode); + if(prevjoinnode) + prevjoinnode->next = nextjoinnode; + else + chanuser->chan->spam_settings->join_nodes = nextjoinnode; + } else + prevjoinnode = joinnode; + } + if(result) + return result; + joinnode = malloc(sizeof(*joinnode)); + if(!joinnode) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return NULL; + } + joinnode->ident = strdup(chanuser->user->ident); + joinnode->host = strdup(chanuser->user->host); + joinnode->last_penalty_update = time(0); + joinnode->joinpenalty = 0; + joinnode->next = chanuser->chan->spam_settings->join_nodes; + chanuser->chan->spam_settings->join_nodes = joinnode; + return joinnode; +} + +static void createSpamNode(struct ChanUser *chanuser) { + struct NeonSpamNode *spamnode = malloc(sizeof(*spamnode)); + if(!spamnode) { + printf_log("neonserv", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + spamnode->lastmsg = 0; + spamnode->spamcount = 0; + spamnode->floodpenalty = 0; + spamnode->last_penalty_update = time(0); + chanuser->spamnode = spamnode; +} + +static int neonspam_event_freechan(struct ChanNode *chan) { + if(chan->spam_settings) + freeNeonSpamSettings(chan->spam_settings); + return 1; +} + +static void neonspam_event_invite(struct ClientSocket *client, struct UserNode *user, char *channel) { + if(client->botid != BOTID) + return; + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `botid`, `bot_channels`.`id`, `suspended` FROM `bot_channels` LEFT JOIN `bots` ON `bot_channels`.`botid` = `bots`.`id` LEFT JOIN `channels` ON `chanid` = `channel_id` WHERE `channel_name` = '%s' AND `botclass` = '%d'", escape_string(channel), client->botid); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) == NULL) { + reply(client, user, "NS_INVITE_FAIL", channel, client->user->nick); + return; + } + if(!strcmp(row[2], "1")) { + reply(client, user, "MODCMD_CHAN_SUSPENDED"); + return; + } + int botid = atoi(row[0]); + struct ClientSocket *bot; + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(bot->clientid == botid) + break; + } + if(bot) { + struct ChanNode *chan = getChanByName(channel); + if(chan && isUserOnChan(bot->user, chan)) { + reply(client, user, "NS_INVITE_ON_CHAN", bot->user->nick, chan->name); + } else + putsock(bot, "JOIN %s", channel); + } +} + +void init_NeonSpam(int type) { + + set_bot_alias(BOTID, BOTALIAS); + start_bots(type); + + if(type == MODSTATE_REBIND) return; + + //register events + bind_bot_ready(neonspam_bot_ready, module_id); + bind_join(neonspam_event_join, module_id); + bind_chanmsg(neonspam_event_chanmsg, module_id); + bind_privctcp(general_event_privctcp, module_id); + bind_freechan(neonspam_event_freechan, module_id); + bind_invite(neonspam_event_invite, module_id); + + set_trigger_callback(BOTID, module_id, neonspam_trigger_callback); + + register_default_language_table(msgtab); +} + +void free_NeonSpam(int type) { + unbind_allcmd(BOTID); + if(type == MODSTATE_STARTSTOP) { + //disconnect all our bots + struct ClientSocket *client; + for(client = getBots(0, NULL); client; client = getBots(0, client)) { + if(client->botid == BOTID) { + unbind_botwise_allcmd(0, client->clientid); + close_socket(client); + break; + } + } + } +} + +#undef BOTID +#undef BOTALIAS diff --git a/src/modules/NeonSpam.mod/bot_NeonSpam.h b/src/modules/NeonSpam.mod/bot_NeonSpam.h new file mode 100644 index 0000000..7024986 --- /dev/null +++ b/src/modules/NeonSpam.mod/bot_NeonSpam.h @@ -0,0 +1,170 @@ +/* bot_NeonSpam.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#ifndef _bot_NeonSpam_h +#define _bot_NeonSpam_h + +#include "../../main.h" + +struct ChanNode; + +//SPAMSCAN +#define SPAMSETTINGS_SPAMSCAN 0x000001 +#define SPAMSETTINGS_SPAMSCAN_OPS 0x000002 +#define SPAMSETTINGS_SPAMSCAN_VOICE 0x000004 +#define SPAMSETTINGS_SPAMCHARS "abc" +#define SPAMSETTINGS_SPAMEXCINDEX 0 + +//FLOODSCAN +#define SPAMSETTINGS_FLOODSCAN 0x000008 +#define SPAMSETTINGS_FLOODSCAN_OPS 0x000010 +#define SPAMSETTINGS_FLOODSCAN_VOICE 0x000020 +#define SPAMSETTINGS_FLOODCHARS "def" +#define SPAMSETTINGS_FLOODEXCINDEX 1 +#define SPAMSETTINGS_FLOODSENINDEX 0 + +//JOINSCAN +#define SPAMSETTINGS_JOINSCAN 0x000040 +#define SPAMSETTINGS_JOINSCAN_OPS 0x000080 +#define SPAMSETTINGS_JOINSCAN_VOICE 0x000100 +#define SPAMSETTINGS_JOINCHARS "ghi" +#define SPAMSETTINGS_JOINEXCINDEX 2 +#define SPAMSETTINGS_JOINSENINDEX 1 + +//BOTNET SCAN +#define SPAMSETTINGS_BOTNETSCAN 0x000200 +#define SPAMSETTINGS_BOTNETSCAN_OPS 0x000400 +#define SPAMSETTINGS_BOTNETSCAN_VOICE 0x000800 +#define SPAMSETTINGS_BOTNETSCAN_STRIPCC 0x001000 +#define SPAMSETTINGS_BOTNETCHARS "jklm" +#define SPAMSETTINGS_BOTNETEXCINDEX 3 + +//CAPSSCAN +#define SPAMSETTINGS_CAPSSCAN 0x002000 +#define SPAMSETTINGS_CAPSSCAN_OPS 0x004000 +#define SPAMSETTINGS_CAPSSCAN_VOICE 0x008000 +#define SPAMSETTINGS_CAPSCHARS "nop" +#define SPAMSETTINGS_CAPSEXCINDEX 4 +#define SPAMSETTINGS_CAPSPERCENTINDEX 0 + +//DIGITSCAN +#define SPAMSETTINGS_DIGITSCAN 0x010000 +#define SPAMSETTINGS_DIGITSCAN_OPS 0x020000 +#define SPAMSETTINGS_DIGITSCAN_VOICE 0x040000 +#define SPAMSETTINGS_DIGITCHARS "qrs" +#define SPAMSETTINGS_DIGITEXCINDEX 5 +#define SPAMSETTINGS_DIGITPERCENTINDEX 1 + +//BADWORDSCAN +#define SPAMSETTINGS_BADWORDSCAN 0x080000 +#define SPAMSETTINGS_BADWORDSCAN_OPS 0x100000 +#define SPAMSETTINGS_BADWORDSCAN_VOICE 0x200000 +#define SPAMSETTINGS_BADWORDCHARS "tuv" +#define SPAMSETTINGS_BADWORDEXCINDEX 6 + +#define SPAMSETTINGS_CHARS SPAMSETTINGS_SPAMCHARS SPAMSETTINGS_FLOODCHARS SPAMSETTINGS_JOINCHARS SPAMSETTINGS_BOTNETCHARS SPAMSETTINGS_CAPSCHARS SPAMSETTINGS_DIGITCHARS SPAMSETTINGS_BADWORDCHARS +#define SPAMSETTINGS_FLAGS 0x7fffff /* all flags that can be stored in the database */ +#define SPAMSETTINGS_EXCEPTINDEXES 7 +#define SPAMSETTINGS_SENSIBILITYINDEXES 2 +#define SPAMSETTINGS_PERCENTINDEXES 2 + +//SCRIPT FLAGS +#define SPAMSETTINGS_KICKEDBOTQUEUE 0x0400000 +#define SPAMSETTINGS_ISTIMEBAN 0x0800000 +#define SPAMSETTINGS_SETTIMEBAN 0x1000000 + +#define MAX_FLOOD_AMOUNT 300 +#define MIN_FLOOD_AMOUNT 2 +#define MAX_FLOOD_TIME 200 + +#define MAX_JOIN_AMOUNT 300 +#define MIN_JOIN_AMOUNT 2 +#define MAX_JOIN_TIME 200 + +#define BOTNETSCAN_USERS 4 +#define BOTNETSCAN_TIME 2 + +struct NeonSpamSettings { + unsigned int flags; + unsigned char spam_amount; + unsigned char sensibility_amount[SPAMSETTINGS_SENSIBILITYINDEXES]; + unsigned char sensibility_time[SPAMSETTINGS_SENSIBILITYINDEXES]; + unsigned int exceptlevel[SPAMSETTINGS_EXCEPTINDEXES]; + unsigned char percent[SPAMSETTINGS_PERCENTINDEXES]; + + //joinflood + struct NeonSpamJoinNode *join_nodes; + + //botnet + unsigned long lastmsg; //crc32 hash + time_t lastmsg_time; + char *botnicks[BOTNETSCAN_USERS]; + + //badword scan + struct NeonSpamBadword *badwords; +}; + +struct NeonSpamBadword { + int id; + char *badword; + unsigned char use_defaults : 1; + unsigned int exceptlevel : 9; + unsigned char scan_ops : 1; + unsigned char scan_voice : 1; + unsigned char use_default_reaction : 1; + unsigned int reaction; + unsigned int reaction_time; + struct NeonSpamBadword *next; +}; + +/* PENALTY SYSTEM +* user gets MAX_FLOOD_TIME points per message +* points get removed each loop +* pounts to be removed each second: +* MAX_FLOOD_TIME/flood_time +* +* the floodlimit is reached, if the penalty points +* are bigger than MAX_FLOOD_TIME * flood_amount +*/ + +#define NEONSPAMNODE_FLAG_CAPSSCAN_WARNED 0x01 +#define NEONSPAMNODE_FLAG_DIGITSCAN_WARNED 0x02 + +struct NeonSpamNode { + unsigned long lastmsg; //crc32 hash + unsigned char spamcount; + int floodpenalty; + time_t last_penalty_update; + unsigned char flags; +}; + +struct NeonSpamJoinNode { + char *ident; + char *host; + int joinpenalty; + time_t last_penalty_update; + struct NeonSpamJoinNode *next; +}; + +void init_NeonSpam(int type); +void free_NeonSpam(int type); + +void freeNeonSpamSettings(struct NeonSpamSettings *settings); +char* convertNeonSpamSettingsToString(unsigned int flags, char *buffer); +int loadNeonSpamSettings(struct ChanNode *chan); + +#endif \ No newline at end of file diff --git a/src/modules/NeonSpam.mod/cmd_neonspam.c b/src/modules/NeonSpam.mod/cmd_neonspam.c new file mode 100644 index 0000000..3ca339d --- /dev/null +++ b/src/modules/NeonSpam.mod/cmd_neonspam.c @@ -0,0 +1,36 @@ +/* cmd_neonspam.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#include "../module.h" + +#include "cmd_neonspam.h" +#include "../../modcmd.h" +#include "../../ConfigParser.h" + +void register_commands() { + //NeonSpam Commands + register_command_alias(2, "NeonSpam"); + + #define USER_COMMAND(NAME,FUNCTION,PARAMCOUNT,PRIVS,FLAGS) register_command(2, NAME, module_id, FUNCTION, PARAMCOUNT, PRIVS, 0, FLAGS) + // NAME FUNCTION PARAMS PRIVS FLAGS + USER_COMMAND("set", neonspam_cmd_set, 0, "#channel_setters", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("addbad", neonspam_cmd_addbad, 1, "#channel_setters", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("delbad", neonspam_cmd_delbad, 1, "#channel_setters", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + USER_COMMAND("badwords", neonspam_cmd_badwords, 0, "#channel_setters", CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_LOG); + + #undef USER_COMMAND + +} \ No newline at end of file diff --git a/src/modules/NeonSpam.mod/cmd_neonspam.h b/src/modules/NeonSpam.mod/cmd_neonspam.h new file mode 100644 index 0000000..8023e30 --- /dev/null +++ b/src/modules/NeonSpam.mod/cmd_neonspam.h @@ -0,0 +1,49 @@ +/* cmd_neonspam.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _cmd_neonspam_h +#define _cmd_neonspam_h +#include "../module.h" +#include "../../main.h" +#include "../../modcmd.h" +#include "../../IRCParser.h" +#include "../../IRCEvents.h" +#include "../../UserNode.h" +#include "../../ChanNode.h" +#include "../../ChanUser.h" +#include "../../ModeNode.h" +#include "../../BanNode.h" +#include "../../ClientSocket.h" +#include "../../mysqlConn.h" +#include "../../lang.h" +#include "../../HandleInfoHandler.h" +#include "../../WHOHandler.h" +#include "../../DBHelper.h" +#include "../../tools.h" +#include "../../timeq.h" +#include "../../version.h" +#include "../../EventLogger.h" +#include "../../bots.h" +#include "bot_NeonSpam.h" + +void register_commands(); + +CMD_BIND(neonspam_cmd_addbad); +CMD_BIND(neonspam_cmd_badwords); +CMD_BIND(neonspam_cmd_delbad); +CMD_BIND(neonspam_cmd_set); + +#endif \ No newline at end of file diff --git a/src/modules/NeonSpam.mod/cmd_neonspam_addbad.c b/src/modules/NeonSpam.mod/cmd_neonspam_addbad.c new file mode 100644 index 0000000..49efe50 --- /dev/null +++ b/src/modules/NeonSpam.mod/cmd_neonspam_addbad.c @@ -0,0 +1,57 @@ +/* cmd_neonspam_addbad.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonspam.h" + +CMD_BIND(neonspam_cmd_addbad) { + char *cbadword = merge_argv(argv, 0, argc); + MYSQL_RES *res; + MYSQL_ROW row, defaults; + loadChannelSettings(chan); + loadNeonSpamSettings(chan); + struct NeonSpamSettings *settings = chan->spam_settings; + if(!settings) return; + printf_mysql_query("SELECT `channel_badword_reaction`, `channel_badword_reaction_duration` FROM `channels` WHERE `channel_id` = '%d'", chan->channel_id); + res = mysql_use(); + row = mysql_fetch_row(res); + if(!row[0] || !row[1]) { + printf_mysql_query("SELECT `channel_badword_reaction`, `channel_badword_reaction_duration` FROM `channels` WHERE `channel_name` = 'defaults'"); + res = mysql_use(); + defaults = mysql_fetch_row(res); + } + int reaction = atoi((row[0] ? row[0] : defaults[0])) + 1; + int reaction_time = atoi((row[1] ? row[1] : defaults[1])); + if(strlen(cbadword) > 128) + cbadword[128] = 0; + int scan_ops = ((settings->flags & SPAMSETTINGS_BADWORDSCAN_OPS) ? 1 : 0); + int scan_voice = ((settings->flags & SPAMSETTINGS_BADWORDSCAN_VOICE) ? 1 : 0); + printf_mysql_query("INSERT INTO `spamserv_badwords` (`badword_cid`, `badword_match`, `badword_use_default`, `badword_exceptlevel`, `badword_scan_ops`, `badword_scan_voice`, `badword_use_default_reaction`, `badword_reaction`, `badword_reaction_time`) VALUES ('%d', '%s', '1', '%d', '%d', '%d', '1', '%d', '%d')", chan->channel_id, escape_string(cbadword), settings->exceptlevel[SPAMSETTINGS_BADWORDEXCINDEX], scan_ops, scan_voice, reaction, reaction_time); + int badword_id = (int) mysql_insert_id(get_mysql_conn()); + struct NeonSpamBadword *badword = malloc(sizeof(*badword)); + badword->id = badword_id; + badword->badword = strdup(cbadword); + badword->use_defaults = 1; + badword->exceptlevel = settings->exceptlevel[SPAMSETTINGS_BADWORDEXCINDEX]; + badword->scan_ops = scan_ops; + badword->scan_voice = scan_voice; + badword->use_default_reaction = 1; + badword->reaction = reaction; + badword->reaction_time = reaction_time; + badword->next = settings->badwords; + settings->badwords = badword; + reply(textclient, user, "SS_BADWORD_ADDED", cbadword, badword_id); +} diff --git a/src/modules/NeonSpam.mod/cmd_neonspam_badwords.c b/src/modules/NeonSpam.mod/cmd_neonspam_badwords.c new file mode 100644 index 0000000..02d4d46 --- /dev/null +++ b/src/modules/NeonSpam.mod/cmd_neonspam_badwords.c @@ -0,0 +1,106 @@ +/* cmd_neonspam_badwords.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonspam.h" + +CMD_BIND(neonspam_cmd_badwords) { + MYSQL_RES *res; + MYSQL_ROW row, defaults; + loadChannelSettings(chan); + loadNeonSpamSettings(chan); + struct NeonSpamSettings *settings = chan->spam_settings; + if(!settings) return; + if(argc > 0) { + int badword_id = atoi(argv[0]); + printf_mysql_query("SELECT `badword_match` FROM `spamserv_badwords` WHERE `badword_id` = '%d' AND `badword_cid` = '%d'", badword_id, chan->channel_id); + res = mysql_use(); + if(!(row = mysql_fetch_row(res))) { + reply(textclient, user, "SS_BADWORD_ID_UNKNOWN", badword_id, chan->name); + return; + } + //to be continued... + + } else { + struct Table *table; + printf_mysql_query("SELECT `channel_badword_reaction`, `channel_badword_reaction_duration` FROM `channels` WHERE `channel_id` = '%d'", chan->channel_id); + res = mysql_use(); + row = mysql_fetch_row(res); + if(!row[0] || !row[1]) { + printf_mysql_query("SELECT `channel_badword_reaction`, `channel_badword_reaction_duration` FROM `channels` WHERE `channel_name` = 'defaults'"); + res = mysql_use(); + defaults = mysql_fetch_row(res); + } + int default_reaction = atoi((row[0] ? row[0] : defaults[0])); + int default_reaction_time = atoi((row[1] ? row[1] : defaults[1])); + printf_mysql_query("SELECT `badword_id`, `badword_match`, `badword_use_default`, `badword_exceptlevel`, `badword_scan_ops`, `badword_scan_voice`, `badword_use_default_reaction`, `badword_reaction`, `badword_reaction_time` FROM `spamserv_badwords` WHERE `badword_cid` = '%d' ORDER BY `badword_id` ASC", chan->channel_id); + res = mysql_use(); + table = table_init(4, mysql_num_rows(res) + 1, 0); + char *content[4]; + content[0] = get_language_string(user, "SS_BADWORDS_ID"); + content[1] = get_language_string(user, "SS_BADWORDS_PATTERN"); + content[2] = get_language_string(user, "SS_BADWORDS_SCAN"); + content[3] = get_language_string(user, "SS_BADWORDS_REACTION"); + table_add(table, content); + char default_exception[100]; + char exception_buf[100], reaction_buf[100], reaction_time_buf[100]; + int exception_pos = 0, reaction_pos; + int reaction, reaction_time; + if(settings->flags & SPAMSETTINGS_BADWORDSCAN_OPS) + exception_pos += sprintf(default_exception+exception_pos, (exception_pos ? " & @" : "@")); + if(settings->flags & SPAMSETTINGS_BADWORDSCAN_VOICE) + exception_pos += sprintf(default_exception+exception_pos, (exception_pos ? " & +" : "+")); + if(settings->exceptlevel[SPAMSETTINGS_BADWORDEXCINDEX]) + exception_pos += sprintf(default_exception+exception_pos, (exception_pos ? " & <%d" : "<%d"), settings->exceptlevel[SPAMSETTINGS_BADWORDEXCINDEX]); + while ((row = mysql_fetch_row(res)) != NULL) { + content[0] = row[0]; + content[1] = row[1]; + exception_pos = 0; + if(strcmp(row[2], "0")) { + exception_pos += sprintf(exception_buf, "\00314%s\003", default_exception); + } else { + if(!strcmp(row[4], "1")) + exception_pos += sprintf(exception_buf+exception_pos, (exception_pos ? " & @" : "@")); + if(!strcmp(row[5], "1")) + exception_pos += sprintf(exception_buf+exception_pos, (exception_pos ? " & +" : "+")); + if(atoi(row[3])) + exception_pos += sprintf(exception_buf+exception_pos, (exception_pos ? " & <%d" : "<%d"), atoi(row[3])); + } + content[2] = exception_buf; + reaction_pos = 0; + if(strcmp(row[6], "0")) { + reaction = default_reaction; + reaction_time = default_reaction_time; + } else { + reaction = atoi(row[7]); + reaction_time = atoi(row[8]); + } + sprintf(reaction_buf, "SS_BADWORDS_REACTION_%d", reaction); + reaction_pos += sprintf(reaction_buf, "%s", get_language_string(user, reaction_buf)); + if(reaction == 2) { + reaction_pos += sprintf(reaction_buf+reaction_pos, " (%s)", timeToStr(user, reaction_time, 2, reaction_time_buf)); + } + content[3] = reaction_buf; + table_add(table, content); + } + char **table_lines = table_end(table); + int i; + for(i = 0; i < table->entrys; i++) { + reply(textclient, user, table_lines[i]); + } + table_free(table); + } +} diff --git a/src/modules/NeonSpam.mod/cmd_neonspam_delbad.c b/src/modules/NeonSpam.mod/cmd_neonspam_delbad.c new file mode 100644 index 0000000..c0d1788 --- /dev/null +++ b/src/modules/NeonSpam.mod/cmd_neonspam_delbad.c @@ -0,0 +1,49 @@ +/* cmd_neonspam_delbad.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonspam.h" + +CMD_BIND(neonspam_cmd_delbad) { + MYSQL_RES *res; + MYSQL_ROW row; + loadChannelSettings(chan); + loadNeonSpamSettings(chan); + struct NeonSpamSettings *settings = chan->spam_settings; + if(!settings) return; + int badword_id = atoi(argv[0]); + printf_mysql_query("SELECT `badword_match` FROM `spamserv_badwords` WHERE `badword_id` = '%d' AND `badword_cid` = '%d'", badword_id, chan->channel_id); + res = mysql_use(); + if(!(row = mysql_fetch_row(res))) { + reply(textclient, user, "SS_BADWORD_ID_UNKNOWN", badword_id, chan->name); + return; + } + struct NeonSpamBadword *badword, *prev = NULL; + for(badword = settings->badwords; badword; badword = badword->next) { + if(badword->id == badword_id) { + if(prev) + prev->next = badword->next; + else + settings->badwords = badword->next; + free(badword->badword); + free(badword); + break; + } else + prev = badword; + } + printf_mysql_query("DELETE FROM `spamserv_badwords` WHERE `badword_id` = '%d'", badword_id); + reply(textclient, user, "SS_BADWORD_DELETED", row[0], badword_id); +} diff --git a/src/modules/NeonSpam.mod/cmd_neonspam_set.c b/src/modules/NeonSpam.mod/cmd_neonspam_set.c new file mode 100644 index 0000000..f976376 --- /dev/null +++ b/src/modules/NeonSpam.mod/cmd_neonspam_set.c @@ -0,0 +1,665 @@ +/* cmd_neonspam_set.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_neonspam.h" + +typedef char* neonspam_cmd_set_function(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, int intparam, char *charparam, char *argument, char *retBuf); +static char* neonspam_cmd_set_trigger(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, int intparam, char *charparam, char *argument, char *retBuf); +static char* neonspam_cmd_setflags(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, int intparam, char *charparam, char *argument, char *retBuf); +static char* neonspam_cmd_setexcept(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, int intparam, char *charparam, char *argument, char *retBuf); +static char* neonspam_cmd_set_reaction(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, int intparam, char *charparam, char *argument, char *retBuf); +static char* neonspam_cmd_set_reaction_time(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, int intparam, char *charparam, char *argument, char *retBuf); +static char* neonspam_cmd_setpercent(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, int intparam, char *charparam, char *argument, char *retBuf); +static char* neonspam_cmd_setsensibility(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, int intparam, char *charparam, char *argument, char *retBuf); +static char* neonspam_cmd_set_spamlimit(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, int intparam, char *charparam, char *argument, char *retBuf); +static char* neonspam_cmd_setscanops(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, int intparam, char *charparam, char *argument, char *retBuf); +static char* neonspam_cmd_setscanvoice(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, int intparam, char *charparam, char *argument, char *retBuf); +static char* neonspam_cmd_setscanexcept(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, int intparam, char *charparam, char *argument, char *retBuf); + +static MYSQL_ROW neonspam_settings_row, neonspam_settings_defaults; +#define SPAMSERV_SETTINGS_QUERY "`channel_spam_reaction`, `channel_spam_reaction_duration`, `channel_flood_reaction`, `channel_flood_reaction_duration`, `channel_join_reaction`, `channel_join_reaction_duration`, `channel_botnet_bantime`, `channel_caps_reaction`, `channel_caps_reaction_duration`, `channel_digit_reaction`, `channel_digit_reaction_duration`, `channel_badword_reaction`, `channel_badword_reaction_duration`" +#define SPAMSERV_SETTINGS_RESET "\ +`channel_scanner` = NULL, \ +`channel_spam_limit` = NULL, \ +`channel_spam_reaction` = NULL, \ +`channel_spam_reaction_duration` = NULL, \ +`channel_spam_except` = NULL, \ +`channel_flood_limit` = NULL, \ +`channel_flood_time` = NULL, \ +`channel_flood_reaction` = NULL, \ +`channel_flood_reaction_duration` = NULL, \ +`channel_flood_except` = NULL, \ +`channel_join_limit` = NULL, \ +`channel_join_time` = NULL, \ +`channel_join_reaction` = NULL, \ +`channel_join_reaction_duration` = NULL, \ +`channel_join_except` = NULL, \ +`channel_botnet_bantime` = NULL, \ +`channel_botnet_except` = NULL, \ +`channel_caps_percent` = NULL, \ +`channel_caps_reaction` = NULL, \ +`channel_caps_reaction_duration` = NULL, \ +`channel_caps_except` = NULL,\ +`channel_digit_percent` = NULL, \ +`channel_digit_reaction` = NULL, \ +`channel_digit_reaction_duration` = NULL, \ +`channel_digit_except` = NULL, \ +`channel_badword_reaction` = NULL, \ +`channel_badword_reaction_duration` = NULL, \ +`channel_badword_except` = NULL " + +#define SET_HELP 0x0001 +#define SET_BOOL 0x0002 +#define SET_OPT 0x0004 + +#define SET_SCANOPS 0x0010 +#define SET_SCANVOICE 0x0020 +#define SET_SCANEXCEPT 0x0040 + +#define SET_OPT_MAX 0xff00 +#define SET_OPT_SHIFT 8 + +static const struct { + unsigned int if_flag; + unsigned int indent; + const char *setting; + void *function; + int intparam; + char *charparam; + int flags; +} neonspam_settings[] = { + {0, 0, "Trigger", neonspam_cmd_set_trigger, 0, NULL, SET_HELP}, + + {0, 0, "SpamScan", neonspam_cmd_setflags, SPAMSETTINGS_SPAMSCAN, NULL, SET_BOOL}, + {SPAMSETTINGS_SPAMSCAN, 2, "SpamLimit", neonspam_cmd_set_spamlimit, 0, NULL, SET_HELP}, + {SPAMSETTINGS_SPAMSCAN | SPAMSETTINGS_SETTIMEBAN, 2, "SpamReaction", neonspam_cmd_set_reaction, 0, "channel_spam_reaction", SET_OPT | (3 << SET_OPT_SHIFT)}, + {SPAMSETTINGS_SPAMSCAN | SPAMSETTINGS_ISTIMEBAN, 4, "SpamBanDuration", neonspam_cmd_set_reaction_time, 1, "channel_spam_reaction_duration", 0}, + {SPAMSETTINGS_SPAMSCAN, 2, "SpamScanChanOps", neonspam_cmd_setflags, SPAMSETTINGS_SPAMSCAN_OPS, NULL, SET_BOOL | SET_SCANOPS}, + {SPAMSETTINGS_SPAMSCAN, 2, "SpamScanVoiced", neonspam_cmd_setflags, SPAMSETTINGS_SPAMSCAN_VOICE, NULL, SET_BOOL | SET_SCANVOICE}, + {SPAMSETTINGS_SPAMSCAN, 2, "SpamScanExcept", neonspam_cmd_setexcept, SPAMSETTINGS_SPAMEXCINDEX, "channel_spam_except", SET_HELP | SET_SCANEXCEPT}, + + {0, 0, "FloodScan", neonspam_cmd_setflags, SPAMSETTINGS_FLOODSCAN, NULL, SET_BOOL}, + {SPAMSETTINGS_FLOODSCAN, 2, "FloodSensibility", neonspam_cmd_setsensibility, SPAMSETTINGS_FLOODSENINDEX, "channel_flood_", SET_HELP}, + {SPAMSETTINGS_FLOODSCAN | SPAMSETTINGS_SETTIMEBAN, 2, "FloodReaction", neonspam_cmd_set_reaction, 2, "channel_flood_reaction", SET_OPT | (3 << SET_OPT_SHIFT)}, + {SPAMSETTINGS_FLOODSCAN | SPAMSETTINGS_ISTIMEBAN, 4, "FloodBanDuration", neonspam_cmd_set_reaction_time, 3, "channel_flood_reaction_duration",0}, + {SPAMSETTINGS_FLOODSCAN, 2, "FloodScanChanOps", neonspam_cmd_setflags, SPAMSETTINGS_FLOODSCAN_OPS, NULL, SET_BOOL | SET_SCANOPS}, + {SPAMSETTINGS_FLOODSCAN, 2, "FloodScanVoiced", neonspam_cmd_setflags, SPAMSETTINGS_FLOODSCAN_VOICE, NULL, SET_BOOL | SET_SCANVOICE}, + {SPAMSETTINGS_FLOODSCAN, 2, "FloodScanExcept", neonspam_cmd_setexcept, SPAMSETTINGS_FLOODEXCINDEX, "channel_flood_except", SET_HELP | SET_SCANEXCEPT}, + + {0, 0, "JoinScan", neonspam_cmd_setflags, SPAMSETTINGS_JOINSCAN, NULL, SET_BOOL}, + {SPAMSETTINGS_JOINSCAN, 2, "JoinSensibility", neonspam_cmd_setsensibility, SPAMSETTINGS_JOINSENINDEX, "channel_join_", SET_HELP}, + {SPAMSETTINGS_JOINSCAN | SPAMSETTINGS_SETTIMEBAN, 2, "JoinReaction", neonspam_cmd_set_reaction, 4, "channel_join_reaction", SET_OPT | (3 << SET_OPT_SHIFT)}, + {SPAMSETTINGS_JOINSCAN | SPAMSETTINGS_ISTIMEBAN, 4, "JoinBanDuration", neonspam_cmd_set_reaction_time, 5, "channel_join_reaction_duration", 0}, + {SPAMSETTINGS_JOINSCAN, 2, "JoinScanChanOps", neonspam_cmd_setflags, SPAMSETTINGS_JOINSCAN_OPS, NULL, SET_BOOL | SET_SCANOPS}, + {SPAMSETTINGS_JOINSCAN, 2, "JoinScanVoiced", neonspam_cmd_setflags, SPAMSETTINGS_JOINSCAN_VOICE, NULL, SET_BOOL | SET_SCANVOICE}, + {SPAMSETTINGS_JOINSCAN, 2, "JoinScanExcept", neonspam_cmd_setexcept, SPAMSETTINGS_JOINEXCINDEX, "channel_join_except", SET_HELP | SET_SCANEXCEPT}, + + {0, 0, "BotNetScan", neonspam_cmd_setflags, SPAMSETTINGS_BOTNETSCAN, NULL, SET_BOOL}, + {SPAMSETTINGS_BOTNETSCAN, 4, "BotNetBanDuration", neonspam_cmd_set_reaction_time, 6, "channel_botnet_bantime", 0}, + {SPAMSETTINGS_BOTNETSCAN, 2, "BotNetScanChanOps", neonspam_cmd_setflags, SPAMSETTINGS_BOTNETSCAN_OPS, NULL, SET_BOOL | SET_SCANOPS}, + {SPAMSETTINGS_BOTNETSCAN, 2, "BotNetScanVoiced", neonspam_cmd_setflags, SPAMSETTINGS_BOTNETSCAN_VOICE, NULL, SET_BOOL | SET_SCANVOICE}, + //{SPAMSETTINGS_BOTNETSCAN, 2, "BotNetScanExcept", neonspam_cmd_setexcept, SPAMSETTINGS_BOTNETEXCINDEX, "channel_botnet_except", SET_HELP}, + + {0, 0, "CapsScan", neonspam_cmd_setflags, SPAMSETTINGS_CAPSSCAN, NULL, SET_BOOL}, + {SPAMSETTINGS_CAPSSCAN, 2, "CapsPercent", neonspam_cmd_setpercent, SPAMSETTINGS_CAPSPERCENTINDEX, "channel_caps_percent", SET_HELP}, + {SPAMSETTINGS_CAPSSCAN | SPAMSETTINGS_SETTIMEBAN, 2, "CapsReaction", neonspam_cmd_set_reaction, 7, "channel_caps_reaction", SET_OPT | (3 << SET_OPT_SHIFT)}, + {SPAMSETTINGS_CAPSSCAN | SPAMSETTINGS_ISTIMEBAN, 4, "CapsBanDuration", neonspam_cmd_set_reaction_time, 8, "channel_caps_reaction_duration", 0}, + {SPAMSETTINGS_CAPSSCAN, 2, "CapsScanChanOps", neonspam_cmd_setflags, SPAMSETTINGS_CAPSSCAN_OPS, NULL, SET_BOOL | SET_SCANOPS}, + {SPAMSETTINGS_CAPSSCAN, 2, "CapsScanVoiced", neonspam_cmd_setflags, SPAMSETTINGS_CAPSSCAN_VOICE, NULL, SET_BOOL | SET_SCANVOICE}, + {SPAMSETTINGS_CAPSSCAN, 2, "CapsScanExcept", neonspam_cmd_setexcept, SPAMSETTINGS_CAPSEXCINDEX, "channel_caps_except", SET_HELP | SET_SCANEXCEPT}, + + {0, 0, "DigitScan", neonspam_cmd_setflags, SPAMSETTINGS_DIGITSCAN, NULL, SET_BOOL}, + {SPAMSETTINGS_DIGITSCAN, 2, "DigitPercent", neonspam_cmd_setpercent, SPAMSETTINGS_DIGITPERCENTINDEX, "channel_digit_percent", SET_HELP}, + {SPAMSETTINGS_DIGITSCAN | SPAMSETTINGS_SETTIMEBAN, 2, "DigitReaction", neonspam_cmd_set_reaction, 9, "channel_digit_reaction", SET_OPT | (3 << SET_OPT_SHIFT)}, + {SPAMSETTINGS_DIGITSCAN | SPAMSETTINGS_ISTIMEBAN, 4, "DigitBanDuration", neonspam_cmd_set_reaction_time, 10, "channel_digit_reaction_duration", 0}, + {SPAMSETTINGS_DIGITSCAN, 2, "DigitScanChanOps", neonspam_cmd_setflags, SPAMSETTINGS_DIGITSCAN_OPS, NULL, SET_BOOL | SET_SCANOPS}, + {SPAMSETTINGS_DIGITSCAN, 2, "DigitScanVoiced", neonspam_cmd_setflags, SPAMSETTINGS_DIGITSCAN_VOICE, NULL, SET_BOOL | SET_SCANVOICE}, + {SPAMSETTINGS_DIGITSCAN, 2, "DigitScanExcept", neonspam_cmd_setexcept, SPAMSETTINGS_DIGITEXCINDEX, "channel_digit_except", SET_HELP | SET_SCANEXCEPT}, + + {0, 0, "BadwordScan", neonspam_cmd_setflags, SPAMSETTINGS_BADWORDSCAN, NULL, SET_BOOL}, + {SPAMSETTINGS_BADWORDSCAN | SPAMSETTINGS_SETTIMEBAN,2, "BadwordReaction", neonspam_cmd_set_reaction, 11, "channel_badword_reaction", SET_OPT | (3 << SET_OPT_SHIFT)}, + {SPAMSETTINGS_BADWORDSCAN | SPAMSETTINGS_ISTIMEBAN,4, "BadwordBanDuration",neonspam_cmd_set_reaction_time, 12, "channel_badword_reaction_duration", 0}, + {SPAMSETTINGS_BADWORDSCAN, 2, "BadwordScanChanOps",neonspam_cmd_setflags, SPAMSETTINGS_BADWORDSCAN_OPS, NULL, SET_BOOL | SET_SCANOPS}, + {SPAMSETTINGS_BADWORDSCAN, 2, "BadwordScanVoiced", neonspam_cmd_setflags, SPAMSETTINGS_BADWORDSCAN_VOICE, NULL, SET_BOOL | SET_SCANVOICE}, + {SPAMSETTINGS_BADWORDSCAN, 2, "BadwordScanExcept", neonspam_cmd_setexcept, SPAMSETTINGS_BADWORDEXCINDEX, "channel_badword_except", SET_HELP | SET_SCANEXCEPT}, + + {0, 0, "GlobalScanChanOps", neonspam_cmd_setscanops, 0, NULL, SET_BOOL}, + {0, 0, "GlobalScanVoice", neonspam_cmd_setscanvoice, 0, NULL, SET_BOOL}, + {0, 0, "GlobalScanExcept", neonspam_cmd_setscanexcept, 0, NULL, SET_HELP}, + + {0, 0, NULL, NULL, 0, NULL, 0} +}; + +#define MAX_QUERY_LEN 1024 +CMD_BIND(neonspam_cmd_set) { + int i, j; + loadNeonSpamSettings(chan); + if(argc && !strcmp(argv[0], "defaults")) { + //reset channel settings + int uaccess = getChannelAccess(user, chan); + if(uaccess < 500) { + if(isGodMode(user)) { + event->flags |= CMDFLAG_OPLOG; + } else { + reply(textclient, user, "NS_SET_DEFAULTS_OWNER", chan->name); + return; + } + } + int seed = 0; + char *tmp; + static char defaultskey[16]; + for(tmp = user->auth; *tmp; tmp++) + seed = (seed * 0xEECE66DL ^ ((*tmp << 24) | (*tmp << 16) | (*tmp << 8) | *tmp)); + for(tmp = chan->name; *tmp; tmp++) + seed = (seed * 0xEECE66DL ^ ((*tmp << 24) | (*tmp << 16) | (*tmp << 8) | *tmp)); + sprintf(defaultskey, "%08x", seed); + if(argc > 1 && !strcmp(argv[1], defaultskey)) { + printf_mysql_query("UPDATE `channels` SET " SPAMSERV_SETTINGS_RESET " WHERE `channel_id` = '%d'", chan->channel_id); + reply(textclient, user, "NS_SET_DEFAULTS_DONE", chan->name); + logEvent(event); + } else { + reply(textclient, user, "NS_SET_DEFAULTS_CODE", chan->name, defaultskey); + } + } else if(argc && strcmp(argv[0], "help")) { + //find the correct command + i = 0; + j = 0; + char *args = (argc > 1 ? merge_argv(argv, 1, argc) : NULL); + neonspam_settings_row = NULL; + neonspam_settings_defaults = NULL; + while(neonspam_settings[i].setting) { + if(!stricmp(neonspam_settings[i].setting, argv[0])) { + //setting found + char valueBuf[MAXLEN], nameBuf[MAXLEN]; + char *value, *optimized_value, *option_help; + neonspam_cmd_set_function *func = neonspam_settings[i].function; + value = func(client, textclient, user, chan, event, neonspam_settings[i].intparam, neonspam_settings[i].charparam, args, valueBuf); + if(value) { + optimized_value = value; + if(neonspam_settings[i].flags & SET_BOOL) { + if(!strcmp(value, "0")) + optimized_value = get_language_string(user, "NS_SET_OFF"); + else if(!strcmp(value, "1")) + optimized_value = get_language_string(user, "NS_SET_ON"); + } + if(neonspam_settings[i].flags & SET_OPT) { + sprintf(nameBuf, "SS_SET_OPTION_%s_%s", neonspam_settings[i].setting, value); + option_help = get_language_string(user, nameBuf); + } else + option_help = NULL; + reply(textclient, user, "\002%s\002 %s%s%s", neonspam_settings[i].setting, optimized_value, (option_help ? " - " : ""), (option_help ? option_help : "")); + if(neonspam_settings[i].flags & SET_OPT) { + int maxoption = (neonspam_settings[i].flags & SET_OPT_MAX) >> SET_OPT_SHIFT; + for(j = 0; j < maxoption; j++) { + sprintf(nameBuf, "SS_SET_OPTION_%s_%d", neonspam_settings[i].setting, j); + option_help = get_language_string(user, nameBuf); + reply(textclient, user, " \002%d\002 - %s", j, option_help); + } + } + if((neonspam_settings[i].flags & SET_HELP) && argc && !strcmp(argv[0], "help")) { + char *tmp; + sprintf(nameBuf, "SS_SET_HELP_%s", neonspam_settings[i].setting); + tmp = get_language_string(user, nameBuf); + if(tmp) { + reply(textclient, user, " %s", tmp); + } + } + } + j = 1; + break; + } + i++; + } + if(j == 0) { + //unknown setting + reply(textclient, user, "NS_SET_UNKNOWN_SETTING", argv[0]); + } + } else { + char valueBuf[MAXLEN], nameBuf[MAXLEN]; + char *value; + int namePos, boolflag = 0; + MYSQL_RES *res, *defaults_res; + struct Table *table; + char *content[2]; + i = 0; + while(neonspam_settings[i].setting) + i++; + table = table_init(2, i, 0); + table_set_bold(table, 0, 1); + printf_mysql_query("SELECT " SPAMSERV_SETTINGS_QUERY " FROM `channels` WHERE `channel_name` = 'defaults'"); + defaults_res = mysql_use(); + neonspam_settings_defaults = mysql_fetch_row(defaults_res); + printf_mysql_query("SELECT " SPAMSERV_SETTINGS_QUERY " FROM `channels` WHERE `channel_id` = '%d'", chan->channel_id); + res = mysql_use(); + neonspam_settings_row = mysql_fetch_row(res); + i = -1; + reply(textclient, user, "NS_SET_HEADER", chan->name); + while(neonspam_settings[++i].setting) { + if((chan->spam_settings->flags & (neonspam_settings[i].if_flag & SPAMSETTINGS_FLAGS)) != (neonspam_settings[i].if_flag & SPAMSETTINGS_FLAGS)) + continue; + if((neonspam_settings[i].if_flag & SPAMSETTINGS_ISTIMEBAN) && !boolflag) + continue; + neonspam_cmd_set_function *func = neonspam_settings[i].function; + value = func(client, textclient, user, chan, event, neonspam_settings[i].intparam, neonspam_settings[i].charparam, NULL, valueBuf); + if(neonspam_settings[i].if_flag & SPAMSETTINGS_SETTIMEBAN) + boolflag = !strcmp(value, "2"); + //TODO: append option or help info + strcpy(valueBuf, value); + if(neonspam_settings[i].flags & SET_BOOL) { + if(!strcmp(value, "0")) + strcpy(valueBuf, get_language_string(user, "NS_SET_OFF")); + else if(!strcmp(value, "1")) + strcpy(valueBuf, get_language_string(user, "NS_SET_ON")); + } + if(neonspam_settings[i].flags & SET_OPT) { + char *tmp; + sprintf(nameBuf, "SS_SET_OPTION_%s_%s", neonspam_settings[i].setting, value); + tmp = get_language_string(user, nameBuf); + if(tmp) { + sprintf(valueBuf + strlen(valueBuf), " - %s", tmp); + } + } + if((neonspam_settings[i].flags & SET_HELP) && argc && !strcmp(argv[0], "help")) { + char *tmp; + sprintf(nameBuf, "SS_SET_HELP_%s", neonspam_settings[i].setting); + tmp = get_language_string(user, nameBuf); + if(tmp) { + sprintf(valueBuf + strlen(valueBuf), " - %s", tmp); + } + } + namePos = 0; + for(j = 0; j < neonspam_settings[i].indent; j++) { + nameBuf[namePos++] = ' '; + } + sprintf(nameBuf + namePos, "%s", neonspam_settings[i].setting); + content[0] = nameBuf; + content[1] = valueBuf; + table_add(table, content); + } + char **table_lines = table_end(table); + for(i = 0; i < table->entrys; i++) { + reply(textclient, user, table_lines[i]); + } + table_free(table); + } +} + +static char* neonspam_cmd_set_trigger(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, int intparam, char *charparam, char *argument, char *retBuf) { + char *trigger; + //get current trigger + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `trigger`, `defaulttrigger` FROM `bot_channels` LEFT JOIN `bots` ON `botid` = `bots`.`id` WHERE `chanid` = '%d' AND `botid` = '%d'", chan->channel_id, client->clientid); + res = mysql_use(); + row = mysql_fetch_row(res); + trigger = (row[0] ? row[0] : row[1]); + if(argument) { + int uaccess = getChannelAccess(user, chan); + if(uaccess < 500) { + if(isGodMode(user)) { + event->flags |= CMDFLAG_OPLOG; + } else { + reply(textclient, user, "NS_SET_TRIGGER_OWNER", chan->name); + return NULL; + } + } + if(strlen(argument) > 15) + argument[15] = '\0'; + printf_mysql_query("UPDATE `bot_channels` SET `trigger` = '%s' WHERE `chanid` = '%d' AND `botid` = '%d'", escape_string(argument), chan->channel_id, client->clientid); + trigger = argument; + changeChannelTrigger(client->botid, chan, trigger); + logEvent(event); + } + return trigger; +} + +static char* neonspam_cmd_setflags(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, int flag, char *charparam, char *argument, char *retBuf) { + char *value = ((chan->spam_settings->flags & flag) ? "1" : "0"); + if(argument) { + //binary argument... + if(!strcmp(argument, "0") || !stricmp(argument, "off") || !stricmp(argument, get_language_string(user, "NS_SET_OFF"))) { + chan->spam_settings->flags &= ~flag; + } else if(!strcmp(argument, "1") || !stricmp(argument, "on") || !stricmp(argument, get_language_string(user, "NS_SET_ON"))) { + chan->spam_settings->flags |= flag; + } else { + reply(textclient, user, "NS_SET_INVALID_BOOLEAN", argument); + return NULL; + } + char str_flags[50]; + convertNeonSpamSettingsToString(chan->spam_settings->flags, str_flags); + printf_mysql_query("UPDATE `channels` SET `channel_scanner` = '%s' WHERE `channel_id` = '%d' ", str_flags, chan->channel_id); + value = argument; + } + return value; +} + +static char* neonspam_cmd_setexcept(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, int exceptlvl_index, char *field, char *argument, char *retBuf) { + unsigned int value = chan->spam_settings->exceptlevel[exceptlvl_index]; + if(argument) { + //numeric argument... (access) + int caccess = atoi(argument); + if(caccess < 0 || caccess > 501) { + reply(textclient, user, "NS_INVALID_ACCESS", caccess); + return NULL; + } + int uaccess = getChannelAccess(user, chan); + if(uaccess == 500) uaccess++; + if(value > uaccess) { + if(isGodMode(user)) { + event->flags |= CMDFLAG_OPLOG; + } else { + reply(textclient, user, "NS_SET_CANNOT_SET"); + return NULL; + } + } + if(caccess > uaccess) { + if(isGodMode(user)) { + event->flags |= CMDFLAG_OPLOG; + } else { + reply(textclient, user, "NS_SET_BADLEVEL"); + return NULL; + } + } + value = caccess; + chan->spam_settings->exceptlevel[exceptlvl_index] = value; + printf_mysql_query("UPDATE `channels` SET `%s` = '%u' WHERE `channel_id` = '%d' ", field, value, chan->channel_id); + } + sprintf(retBuf, "%u", value); + return retBuf; +} + +static char* neonspam_cmd_set_reaction(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, int mysqlindex, char *mysqlfield, char *argument, char *retBuf) { + if(argument) { + /* valid options: + * 0/kick - kick + * 1/kickban - kickban + * 2/ban - timed ban + */ + if(!strcmp(argument, "0") || !stricmp(argument, "kick")) { + argument = "0"; + } else if(!strcmp(argument, "1") || !stricmp(argument, "kickban")) { + argument = "1"; + } else if(!strcmp(argument, "2") || !stricmp(argument, "ban")) { + argument = "2"; + } else { + reply(textclient, user, "NS_SET_INVALID_OPTION_STR", argument); + return NULL; + } + printf_mysql_query("UPDATE `channels` SET `%s` = '%s' WHERE `channel_id` = '%d' ", mysqlfield, argument, chan->channel_id); + return argument; + } else { + if(neonspam_settings_row) { + return (neonspam_settings_row[mysqlindex] ? neonspam_settings_row[mysqlindex] : neonspam_settings_defaults[mysqlindex]); + } else { + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `%s` FROM `channels` WHERE `channel_id` = '%d'", mysqlfield, chan->channel_id); + res = mysql_use(); + row = mysql_fetch_row(res); + return row[0]; + } + } +} + +static char* neonspam_cmd_set_reaction_time(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, int mysqlindex, char *mysqlfield, char *argument, char *retBuf) { + int duration; + if(argument) { + duration = strToTime(user, argument); + if(duration < 30) { + reply(textclient, user, "NS_TIMEBAN_DURATION_TOO_SHORT", 30); + return NULL; + } + printf_mysql_query("UPDATE `channels` SET `%s` = '%d' WHERE `channel_id` = '%d' ", mysqlfield, duration, chan->channel_id); + } else { + if(neonspam_settings_row) { + duration = atoi(neonspam_settings_row[mysqlindex] ? neonspam_settings_row[mysqlindex] : neonspam_settings_defaults[mysqlindex]); + } else { + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `%s` FROM `channels` WHERE `channel_id` = '%d'", mysqlfield, chan->channel_id); + res = mysql_use(); + row = mysql_fetch_row(res); + duration = atoi(row[0]); + } + } + timeToStr(user, duration, 3, retBuf); + return retBuf; +} + +static char* neonspam_cmd_setpercent(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, int percent_index, char *mysqlfield, char *argument, char *retBuf) { + unsigned int value = chan->spam_settings->percent[percent_index]; + if(argument) { + //numeric argument... (access) + value = atoi(argument); + if(value < 0 || value > 100) { + //invalid percent value + reply(textclient, user, "SS_SET_PERCENT", value); + return NULL; + } + chan->spam_settings->percent[percent_index] = value; + printf_mysql_query("UPDATE `channels` SET `%s` = '%u' WHERE `channel_id` = '%d' ", mysqlfield, value, chan->channel_id); + } + sprintf(retBuf, "%u", value); + return retBuf; +} + +static char* neonspam_cmd_setsensibility(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, int sensibility_index, char *mysqlfield, char *argument, char *retBuf) { + if(argument) { + //change value + char *delimiter = strstr(argument, ":"); + if(!delimiter) { + //invalid format + reply(textclient, user, "SS_SET_SENSIBILITY", argument); + return NULL; + } + *delimiter = '\0'; + delimiter++; + int amount = atoi(argument); + int timep = atoi(delimiter); + if(amount > MAX_FLOOD_AMOUNT || amount < MIN_FLOOD_AMOUNT) { + //invalid amount + reply(textclient, user, "SS_SET_SENSIBILITY_AMOUNT", amount, MIN_FLOOD_AMOUNT, MAX_FLOOD_AMOUNT); + return NULL; + } + if(timep > MAX_FLOOD_TIME || timep < 0) { + //invalid time period + reply(textclient, user, "SS_SET_SENSIBILITY_TIME", timep, 0, MAX_FLOOD_TIME); + return NULL; + } + char amountfield[50], timefield[50]; + sprintf(amountfield, "%s%s", mysqlfield, "limit"); + sprintf(timefield, "%s%s", mysqlfield, "time"); + printf_mysql_query("UPDATE `channels` SET `%s` = '%d', `%s` = '%d' WHERE `channel_id` = '%d' ", amountfield, amount, timefield, timep, chan->channel_id); + sprintf(retBuf, "%d:%d", amount, timep); + chan->spam_settings->sensibility_amount[sensibility_index] = amount; + chan->spam_settings->sensibility_time[sensibility_index] = timep; + } else { + sprintf(retBuf, "%d:%d", chan->spam_settings->sensibility_amount[sensibility_index], chan->spam_settings->sensibility_time[sensibility_index]); + } + return retBuf; +} + +static char* neonspam_cmd_set_spamlimit(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, int intparam, char *charparam, char *argument, char *retBuf) { + if(argument) { + //change value + int amount = atoi(argument); + if(amount > 10 || amount < 2) { + //invalid amount + reply(textclient, user, "SS_SET_SPAMLIMIT", amount, 2, 10); + return NULL; + } + printf_mysql_query("UPDATE `channels` SET `channel_spam_limit` = '%d' WHERE `channel_id` = '%d' ", amount, chan->channel_id); + sprintf(retBuf, "%d", amount); + chan->spam_settings->spam_amount = amount; + } else + sprintf(retBuf, "%d", chan->spam_settings->spam_amount); + return retBuf; +} + +static char* neonspam_cmd_setscanops(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, int flag, char *charparam, char *argument, char *retBuf) { + int i = 0; + int value = -1; + int identical = 1; + while(neonspam_settings[i].setting) { + if(neonspam_settings[i].flags & SET_SCANOPS) { + if(value == -1) + value = ((chan->spam_settings->flags & neonspam_settings[i].intparam) ? 1 : 0); + else { + if(((chan->spam_settings->flags & neonspam_settings[i].intparam) ? 1 : 0) != value) { + identical = 0; + break; + } + } + } + i++; + } + if(argument) { + //binary argument... + if(!strcmp(argument, "0") || !stricmp(argument, "off") || !stricmp(argument, get_language_string(user, "NS_SET_OFF"))) { + value = 0; + } else if(!strcmp(argument, "1") || !stricmp(argument, "on") || !stricmp(argument, get_language_string(user, "NS_SET_ON"))) { + value = 1; + } else { + reply(textclient, user, "NS_SET_INVALID_BOOLEAN", argument); + return NULL; + } + i = 0; + char valueBuf[MAXLEN]; + while(neonspam_settings[i].setting) { + if(neonspam_settings[i].flags & SET_SCANOPS) { + neonspam_cmd_set_function *func = neonspam_settings[i].function; + func(client, textclient, user, chan, event, neonspam_settings[i].intparam, neonspam_settings[i].charparam, (value ? "1" : "0"), valueBuf); + } + i++; + } + identical = 1; + } + if(identical && value) + return "1"; + if(identical && !value) + return "0"; + return "?"; +} + +static char* neonspam_cmd_setscanvoice(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, int flag, char *charparam, char *argument, char *retBuf) { + int i = 0; + int value = -1; + int identical = 1; + while(neonspam_settings[i].setting) { + if(neonspam_settings[i].flags & SET_SCANVOICE) { + if(value == -1) + value = ((chan->spam_settings->flags & neonspam_settings[i].intparam) ? 1 : 0); + else { + if(((chan->spam_settings->flags & neonspam_settings[i].intparam) ? 1 : 0) != value) { + identical = 0; + break; + } + } + } + i++; + } + if(argument) { + //binary argument... + if(!strcmp(argument, "0") || !stricmp(argument, "off") || !stricmp(argument, get_language_string(user, "NS_SET_OFF"))) { + value = 0; + } else if(!strcmp(argument, "1") || !stricmp(argument, "on") || !stricmp(argument, get_language_string(user, "NS_SET_ON"))) { + value = 1; + } else { + reply(textclient, user, "NS_SET_INVALID_BOOLEAN", argument); + return NULL; + } + i = 0; + char valueBuf[MAXLEN]; + while(neonspam_settings[i].setting) { + if(neonspam_settings[i].flags & SET_SCANVOICE) { + neonspam_cmd_set_function *func = neonspam_settings[i].function; + func(client, textclient, user, chan, event, neonspam_settings[i].intparam, neonspam_settings[i].charparam, (value ? "1" : "0"), valueBuf); + } + i++; + } + identical = 1; + } + if(identical && value) + return "1"; + if(identical && !value) + return "0"; + return "?"; +} + +static char* neonspam_cmd_setscanexcept(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, int flag, char *charparam, char *argument, char *retBuf) { + int i = 0; + int value = -1; + int identical = 1; + while(neonspam_settings[i].setting) { + if(neonspam_settings[i].flags & SET_SCANEXCEPT) { + if(value == -1) + value = chan->spam_settings->exceptlevel[neonspam_settings[i].intparam]; + else { + if(chan->spam_settings->exceptlevel[neonspam_settings[i].intparam] != value) { + identical = 0; + break; + } + } + } + i++; + } + if(argument) { + //numeric argument... (access) + int caccess = atoi(argument); + if(caccess < 0 || caccess > 501) { + reply(textclient, user, "NS_INVALID_ACCESS", caccess); + return NULL; + } + int uaccess = getChannelAccess(user, chan); + if(uaccess == 500) uaccess++; + if(identical && value > uaccess) { + if(isGodMode(user)) { + event->flags |= CMDFLAG_OPLOG; + } else { + reply(textclient, user, "NS_SET_CANNOT_SET"); + return NULL; + } + } + if(caccess > uaccess) { + if(isGodMode(user)) { + event->flags |= CMDFLAG_OPLOG; + } else { + reply(textclient, user, "NS_SET_BADLEVEL"); + return NULL; + } + } + i = 0; + char valueBuf[MAXLEN]; + sprintf(retBuf, "%d", caccess); + while(neonspam_settings[i].setting) { + if(neonspam_settings[i].flags & SET_SCANEXCEPT) { + neonspam_cmd_set_function *func = neonspam_settings[i].function; + func(client, textclient, user, chan, event, neonspam_settings[i].intparam, neonspam_settings[i].charparam, retBuf, valueBuf); + } + i++; + } + identical = 1; + value = caccess; + } + if(identical) { + sprintf(retBuf, "%d", value); + return retBuf; + } + return "?"; +} + +#undef MAX_QUERY_LEN diff --git a/src/modules/NeonSpam.mod/event_neonspam_chanmsg.c b/src/modules/NeonSpam.mod/event_neonspam_chanmsg.c new file mode 100644 index 0000000..5e7f1ae --- /dev/null +++ b/src/modules/NeonSpam.mod/event_neonspam_chanmsg.c @@ -0,0 +1,529 @@ +/* event_neonspam_chanmsg.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +static int neonspam_spamscan(struct NeonSpamSettings *settings, struct ChanUser *chanuser, char *message); +static int neonspam_floodscan(struct NeonSpamSettings *settings, struct ChanUser *chanuser); +static int neonspam_botnetscan(struct ClientSocket *client, struct NeonSpamSettings *settings, struct ChanUser *chanuser, char *message); +static int neonspam_capsscan(struct NeonSpamSettings *settings, struct ChanUser *chanuser, char *message); +static int neonspam_digitscan(struct NeonSpamSettings *settings, struct ChanUser *chanuser, char *message); +static int neonspam_badwordscan(struct ClientSocket *client, struct NeonSpamSettings *settings, struct ChanUser *chanuser, char *message, int punish_now, int uaccess); + +static USERAUTH_CALLBACK(neonspam_event_chanmsg_nick_lookup); +static void neonspam_event_chanmsg_punish(struct ClientSocket *client, struct ChanUser *chanuser, struct NeonSpamSettings *settings, char *message, unsigned int warn, unsigned int punish); + +struct neonspam_event_chanmsg_cache { + struct ClientSocket *client; + struct ChanUser *chanuser; + struct NeonSpamSettings *settings; + char *message; + unsigned int warn; + unsigned int punish; +}; + + + +static void neonspam_event_chanmsg(struct UserNode *user, struct ChanNode *chan, char *message) { + struct ClientSocket *client = getChannelBot(chan, BOTID); + if(!client || (user->flags & USERFLAG_ISBOT)) return; //we can't "see" this event + loadNeonSpamSettings(chan); + struct NeonSpamSettings *settings = chan->spam_settings; + struct ChanUser *chanuser = getChanUser(user, chan); + if(!settings || !chanuser) return; + #define NEONSPAM_CHANMSG_DO_SCANOPS(FLAG) ((settings->flags & FLAG) || !(chanuser->flags & CHANUSERFLAG_OPPED)) + #define NEONSPAM_CHANMSG_DO_SCANVOICE(FLAG) ((settings->flags & FLAG) || !(chanuser->flags & CHANUSERFLAG_VOICED)) + #define NEONSPAM_CHANMSG_DO_EXCEPT(INDEX) (settings->exceptlevel[INDEX] != 0) + #define NEONSPAM_CHANMSG_NEED_WHO(INDEX) (settings->exceptlevel[INDEX] != 501) + //scan the message + int result = 0; + unsigned int warn = 0; + unsigned int punish = 0; + int needwho = 0; + if((settings->flags & SPAMSETTINGS_SPAMSCAN) && NEONSPAM_CHANMSG_DO_SCANOPS(SPAMSETTINGS_SPAMSCAN_OPS) && NEONSPAM_CHANMSG_DO_SCANVOICE(SPAMSETTINGS_SPAMSCAN_VOICE) && NEONSPAM_CHANMSG_DO_EXCEPT(SPAMSETTINGS_SPAMEXCINDEX)) { + result = neonspam_spamscan(settings, chanuser, message); + switch(result) { + case SPAMSERV_CHECK_IGNORE: + break; + case SPAMSERV_CHECK_WARN: + warn |= SPAMSETTINGS_SPAMSCAN; + if(NEONSPAM_CHANMSG_NEED_WHO(SPAMSETTINGS_SPAMEXCINDEX)) + needwho = 1; + break; + case SPAMSERV_CHECK_PUNISH: + punish |= SPAMSETTINGS_SPAMSCAN; + if(NEONSPAM_CHANMSG_NEED_WHO(SPAMSETTINGS_SPAMEXCINDEX)) + needwho = 1; + break; + } + } + if((settings->flags & SPAMSETTINGS_FLOODSCAN) && NEONSPAM_CHANMSG_DO_SCANOPS(SPAMSETTINGS_FLOODSCAN_OPS) && NEONSPAM_CHANMSG_DO_SCANVOICE(SPAMSETTINGS_FLOODSCAN_VOICE) && NEONSPAM_CHANMSG_DO_EXCEPT(SPAMSETTINGS_FLOODEXCINDEX)) { + result = neonspam_floodscan(settings, chanuser); + switch(result) { + case SPAMSERV_CHECK_IGNORE: + break; + case SPAMSERV_CHECK_WARN: + warn |= SPAMSETTINGS_FLOODSCAN; + if(NEONSPAM_CHANMSG_NEED_WHO(SPAMSETTINGS_FLOODEXCINDEX)) + needwho = 1; + break; + case SPAMSERV_CHECK_PUNISH: + punish |= SPAMSETTINGS_FLOODSCAN; + if(NEONSPAM_CHANMSG_NEED_WHO(SPAMSETTINGS_FLOODEXCINDEX)) + needwho = 1; + break; + } + } + if((settings->flags & SPAMSETTINGS_BOTNETSCAN) && NEONSPAM_CHANMSG_DO_SCANOPS(SPAMSETTINGS_BOTNETSCAN_OPS) && NEONSPAM_CHANMSG_DO_SCANVOICE(SPAMSETTINGS_BOTNETSCAN_VOICE)) { + result = neonspam_botnetscan(client, settings, chanuser, message); + switch(result) { + case SPAMSERV_CHECK_DEAD: + return; + case SPAMSERV_CHECK_IGNORE: + break; + } + } + if((settings->flags & SPAMSETTINGS_CAPSSCAN) && NEONSPAM_CHANMSG_DO_SCANOPS(SPAMSETTINGS_CAPSSCAN_OPS) && NEONSPAM_CHANMSG_DO_SCANVOICE(SPAMSETTINGS_CAPSSCAN_VOICE) && NEONSPAM_CHANMSG_DO_EXCEPT(SPAMSETTINGS_CAPSEXCINDEX)) { + result = neonspam_capsscan(settings, chanuser, message); + switch(result) { + case SPAMSERV_CHECK_IGNORE: + break; + case SPAMSERV_CHECK_WARN: + warn |= SPAMSETTINGS_CAPSSCAN; + if(NEONSPAM_CHANMSG_NEED_WHO(SPAMSETTINGS_CAPSEXCINDEX)) + needwho = 1; + break; + case SPAMSERV_CHECK_PUNISH: + punish |= SPAMSETTINGS_CAPSSCAN; + if(NEONSPAM_CHANMSG_NEED_WHO(SPAMSETTINGS_CAPSEXCINDEX)) + needwho = 1; + break; + } + } + if((settings->flags & SPAMSETTINGS_DIGITSCAN) && NEONSPAM_CHANMSG_DO_SCANOPS(SPAMSETTINGS_DIGITSCAN_OPS) && NEONSPAM_CHANMSG_DO_SCANVOICE(SPAMSETTINGS_DIGITSCAN_VOICE) && NEONSPAM_CHANMSG_DO_EXCEPT(SPAMSETTINGS_DIGITEXCINDEX)) { + result = neonspam_digitscan(settings, chanuser, message); + switch(result) { + case SPAMSERV_CHECK_IGNORE: + break; + case SPAMSERV_CHECK_WARN: + warn |= SPAMSETTINGS_DIGITSCAN; + if(NEONSPAM_CHANMSG_NEED_WHO(SPAMSETTINGS_DIGITEXCINDEX)) + needwho = 1; + break; + case SPAMSERV_CHECK_PUNISH: + punish |= SPAMSETTINGS_DIGITSCAN; + if(NEONSPAM_CHANMSG_NEED_WHO(SPAMSETTINGS_DIGITEXCINDEX)) + needwho = 1; + break; + } + } + if((settings->flags & SPAMSETTINGS_BADWORDSCAN)) { + if(user->flags & USERFLAG_ISAUTHED) { + result = neonspam_badwordscan(client, settings, chanuser, message, 1, getChannelAccess(chanuser->user, chanuser->chan)); + } else + result = neonspam_badwordscan(client, settings, chanuser, message, 0, 0); + switch(result) { + case SPAMSERV_CHECK_DEAD: + return; + case SPAMSERV_CHECK_IGNORE: + break; + case SPAMSERV_CHECK_PUNISH: + punish |= SPAMSETTINGS_BADWORDSCAN; + needwho = 1; + break; + } + } + //some other checks? + + if(warn || punish) { + //whois the user to check against exceptlevel + if(!needwho || (user->flags & USERFLAG_ISAUTHED)) { + neonspam_event_chanmsg_punish(client, chanuser, settings, message, warn, punish); + } else { + struct neonspam_event_chanmsg_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonspam", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->chanuser = chanuser; + cache->settings = settings; + cache->message = strdup(message); + cache->warn = warn; + cache->punish = punish; + get_userauth(user, module_id, neonspam_event_chanmsg_nick_lookup, cache); + } + + } + #undef NEONSPAM_CHANMSG_DO_SCANOPS + #undef NEONSPAM_CHANMSG_DO_SCANVOICE + #undef NEONSPAM_CHANMSG_DO_EXCEPT + #undef NEONSPAM_CHANMSG_NEED_WHO +} + +static USERAUTH_CALLBACK(neonspam_event_chanmsg_nick_lookup) { + struct neonspam_event_chanmsg_cache *cache = data; + neonspam_event_chanmsg_punish(cache->client, cache->chanuser, cache->settings, cache->message, cache->warn, cache->punish); + free(cache->message); + free(cache); +} + +static void neonspam_event_chanmsg_punish(struct ClientSocket *client, struct ChanUser *chanuser, struct NeonSpamSettings *settings, char *message, unsigned int warn, unsigned int punish) { + MYSQL_RES *res; + MYSQL_ROW row, defaults; + loadChannelSettings(chanuser->chan); + int uaccess = 0; + if(chanuser->user->flags & USERFLAG_ISAUTHED) + uaccess = getChannelAccess(chanuser->user, chanuser->chan); + char reason[MAXLEN]; + reason[0] = '\0'; + int punishment = 0; + int punish_time = 0; + if(!punishment && (punish & SPAMSETTINGS_SPAMSCAN) && settings->exceptlevel[SPAMSETTINGS_SPAMEXCINDEX] > uaccess) { + printf_mysql_query("SELECT `channel_spam_reaction`, `channel_spam_reaction_duration` FROM `channels` WHERE `channel_id` = '%d'", chanuser->chan->channel_id); + res = mysql_use(); + row = mysql_fetch_row(res); + if(!row[0] || !row[1]) { + printf_mysql_query("SELECT `channel_spam_reaction`, `channel_spam_reaction_duration` FROM `channels` WHERE `channel_name` = 'defaults'"); + res = mysql_use(); + defaults = mysql_fetch_row(res); + } + sprintf(reason, SPAMSERV_MSG_WARNING, SPAMSERV_MSG_SPAM); + punishment = atoi((row[0] ? row[0] : defaults[0])) + 1; + punish_time = atoi((row[1] ? row[1] : defaults[1])); + } + if(!punishment && (punish & SPAMSETTINGS_FLOODSCAN) && settings->exceptlevel[SPAMSETTINGS_FLOODEXCINDEX] > uaccess) { + printf_mysql_query("SELECT `channel_flood_reaction`, `channel_flood_reaction_duration` FROM `channels` WHERE `channel_id` = '%d'", chanuser->chan->channel_id); + res = mysql_use(); + row = mysql_fetch_row(res); + if(!row[0] || !row[1]) { + printf_mysql_query("SELECT `channel_flood_reaction`, `channel_flood_reaction_duration` FROM `channels` WHERE `channel_name` = 'defaults'"); + res = mysql_use(); + defaults = mysql_fetch_row(res); + } + sprintf(reason, SPAMSERV_MSG_WARNING, SPAMSERV_MSG_FLOOD); + punishment = atoi((row[0] ? row[0] : defaults[0])) + 1; + punish_time = atoi((row[1] ? row[1] : defaults[1])); + } + if(!punishment && (punish & SPAMSETTINGS_CAPSSCAN) && settings->exceptlevel[SPAMSETTINGS_CAPSEXCINDEX] > uaccess) { + printf_mysql_query("SELECT `channel_caps_reaction`, `channel_caps_reaction_duration` FROM `channels` WHERE `channel_id` = '%d'", chanuser->chan->channel_id); + res = mysql_use(); + row = mysql_fetch_row(res); + if(!row[0] || !row[1]) { + printf_mysql_query("SELECT `channel_caps_reaction`, `channel_caps_reaction_duration` FROM `channels` WHERE `channel_name` = 'defaults'"); + res = mysql_use(); + defaults = mysql_fetch_row(res); + } + sprintf(reason, SPAMSERV_MSG_WARNING, SPAMSERV_MSG_CAPS); + punishment = atoi((row[0] ? row[0] : defaults[0])) + 1; + punish_time = atoi((row[1] ? row[1] : defaults[1])); + } + if(!punishment && (punish & SPAMSETTINGS_DIGITSCAN) && settings->exceptlevel[SPAMSETTINGS_DIGITEXCINDEX] > uaccess) { + printf_mysql_query("SELECT `channel_digit_reaction`, `channel_digit_reaction_duration` FROM `channels` WHERE `channel_id` = '%d'", chanuser->chan->channel_id); + res = mysql_use(); + row = mysql_fetch_row(res); + if(!row[0] || !row[1]) { + printf_mysql_query("SELECT `channel_digit_reaction`, `channel_digit_reaction_duration` FROM `channels` WHERE `channel_name` = 'defaults'"); + res = mysql_use(); + defaults = mysql_fetch_row(res); + } + sprintf(reason, SPAMSERV_MSG_WARNING, SPAMSERV_MSG_DIGIT); + punishment = atoi((row[0] ? row[0] : defaults[0])) + 1; + punish_time = atoi((row[1] ? row[1] : defaults[1])); + } + if(!punishment && (punish & SPAMSETTINGS_BADWORDSCAN)) { + int result = neonspam_badwordscan(client, settings, chanuser, message, 1, uaccess); + switch(result) { + case SPAMSERV_CHECK_DEAD: + return; + case SPAMSERV_CHECK_IGNORE: + break; + } + + } + if(!punishment && (warn & SPAMSETTINGS_SPAMSCAN) && settings->exceptlevel[SPAMSETTINGS_SPAMEXCINDEX] > uaccess) { + sprintf(reason, SPAMSERV_MSG_WARNING, SPAMSERV_MSG_SPAM); + } + if(!punishment && (warn & SPAMSETTINGS_FLOODSCAN) && settings->exceptlevel[SPAMSETTINGS_FLOODEXCINDEX] > uaccess) { + sprintf(reason, SPAMSERV_MSG_WARNING, SPAMSERV_MSG_FLOOD); + } + if(!punishment && (warn & SPAMSETTINGS_CAPSSCAN) && settings->exceptlevel[SPAMSETTINGS_CAPSEXCINDEX] > uaccess) { + sprintf(reason, SPAMSERV_MSG_WARNING, SPAMSERV_MSG_CAPS); + } + if(!punishment && (warn & SPAMSETTINGS_DIGITSCAN) && settings->exceptlevel[SPAMSETTINGS_DIGITEXCINDEX] > uaccess) { + sprintf(reason, SPAMSERV_MSG_WARNING, SPAMSERV_MSG_DIGIT); + } + if(punishment) { + char banmaskBuf[NICKLEN+USERLEN+HOSTLEN+3]; + char *banmask = NULL; + switch (punishment) { + case 3: //TIMEBAN + banmask = generate_banmask(chanuser->user, banmaskBuf); + printf_mysql_query("INSERT INTO `bans` (`ban_channel`, `ban_mask`, `ban_triggered`, `ban_timeout`, `ban_owner`, `ban_reason`) VALUES ('%d', '%s', UNIX_TIMESTAMP(), '%lu', '%d', '%s')", chanuser->chan->channel_id, escape_string(banmask), (unsigned long) (punish_time ? (time(0) + punish_time) : 0), 0, escape_string(reason)); + if(punish_time) { + int banid = (int) mysql_insert_id(get_mysql_conn()); + char nameBuf[MAXLEN]; + char banidBuf[20]; + sprintf(nameBuf, "ban_%d", banid); + sprintf(banidBuf, "%d", banid); + timeq_add_name(nameBuf, punish_time, module_id, channel_ban_timeout, strdup(banidBuf)); + } + case 2: //KICKBAN + if(!banmask) + banmask = generate_banmask(chanuser->user, banmaskBuf); + putsock(client, "MODE %s +b %s", chanuser->chan->name, banmask); + case 1: //KICK + putsock(client, "KICK %s %s :%s", chanuser->chan->name, chanuser->user->nick, reason); + break; + } + } else if(*reason) + reply(client, chanuser->user, "%s", reason); +} + +static int neonspam_spamscan(struct NeonSpamSettings *settings, struct ChanUser *chanuser, char *message) { + //crc32 hash of the message + unsigned long msghash = crc32(message); + if(chanuser->spamnode) { + if(chanuser->spamnode->lastmsg == msghash) { + chanuser->spamnode->spamcount++; + if(chanuser->spamnode->spamcount == settings->spam_amount) + return SPAMSERV_CHECK_WARN; + else if(chanuser->spamnode->spamcount > settings->spam_amount) + return SPAMSERV_CHECK_PUNISH; + else + return SPAMSERV_CHECK_IGNORE; + } + } else + createSpamNode(chanuser); + chanuser->spamnode->lastmsg = msghash; + chanuser->spamnode->spamcount = 1; + return SPAMSERV_CHECK_IGNORE; +} + +static int neonspam_update_penalty(struct NeonSpamSettings *settings, struct ChanUser *chanuser, int addmsg) { + int last_update = time(0) - chanuser->spamnode->last_penalty_update; + if(last_update) { + if(last_update < MAX_FLOOD_TIME && chanuser->spamnode->floodpenalty) { + chanuser->spamnode->floodpenalty -= last_update * (MAX_FLOOD_TIME / settings->sensibility_time[SPAMSETTINGS_FLOODSENINDEX]); + if(chanuser->spamnode->floodpenalty < 0) + chanuser->spamnode->floodpenalty = 0; + } else + chanuser->spamnode->floodpenalty = 0; + chanuser->spamnode->last_penalty_update = time(0); + } + chanuser->spamnode->floodpenalty += MAX_FLOOD_TIME * addmsg; + return chanuser->spamnode->floodpenalty / MAX_FLOOD_TIME + ((chanuser->spamnode->floodpenalty % MAX_FLOOD_TIME) ? 1 : 0); +} + +static int neonspam_floodscan(struct NeonSpamSettings *settings, struct ChanUser *chanuser) { + if(!chanuser->spamnode) + createSpamNode(chanuser); + int messages_pending = neonspam_update_penalty(settings, chanuser, 1); + if(messages_pending == settings->sensibility_amount[SPAMSETTINGS_FLOODSENINDEX]) + return SPAMSERV_CHECK_WARN; + else if(messages_pending > settings->sensibility_amount[SPAMSETTINGS_FLOODSENINDEX]) + return SPAMSERV_CHECK_PUNISH; + else + return SPAMSERV_CHECK_IGNORE; +} + +static int neonspam_botnetscan(struct ClientSocket *client, struct NeonSpamSettings *settings, struct ChanUser *chanuser, char *message) { + //crc32 hash of the message + unsigned long msghash = crc32(message); + if((time(0) - settings->lastmsg_time) > BOTNETSCAN_TIME || settings->lastmsg != msghash) { + int i; + for(i = 0; i < BOTNETSCAN_USERS; i++) { + if(settings->botnicks[i]) { + free(settings->botnicks[i]); + settings->botnicks[i] = NULL; + } + } + settings->flags &= ~SPAMSETTINGS_KICKEDBOTQUEUE; + settings->lastmsg = msghash; + } else if(settings->lastmsg == msghash) { + int i; + for(i = 0; i < BOTNETSCAN_USERS; i++) { + if(!settings->botnicks[i]) { + settings->botnicks[i] = strdup(chanuser->user->nick); + break; + } else if(!stricmp(chanuser->user->nick, settings->botnicks[i])) { + return SPAMSERV_CHECK_IGNORE; + } + } + if(i == BOTNETSCAN_USERS) { + //BOTNETSCAN_USERS exceeded + if(!(settings->flags & SPAMSETTINGS_KICKEDBOTQUEUE)) { + for(i = 0; i < BOTNETSCAN_USERS; i++) { + putsock(client, "KICK %s %s :%s", chanuser->chan->name, settings->botnicks[i], SPAMSERV_MSG_BOTNET); + } + settings->flags |= SPAMSETTINGS_KICKEDBOTQUEUE; + } + putsock(client, "KICK %s %s :%s", chanuser->chan->name, chanuser->user->nick, SPAMSERV_MSG_BOTNET); + return SPAMSERV_CHECK_DEAD; + } + } + settings->lastmsg_time = time(0); + return SPAMSERV_CHECK_IGNORE; +} + +static int neonspam_capsscan(struct NeonSpamSettings *settings, struct ChanUser *chanuser, char *message) { + int caps = 0, msglen = strlen(message); + int i; + if(msglen <= 4) return SPAMSERV_CHECK_IGNORE; + for(i = 0; i < msglen; i++) { + if(isupper(message[i])) caps++; + } + caps = 100*caps/msglen; + if(caps >= settings->percent[SPAMSETTINGS_CAPSPERCENTINDEX]) { + if(!chanuser->spamnode) + createSpamNode(chanuser); + if(chanuser->spamnode->flags & NEONSPAMNODE_FLAG_CAPSSCAN_WARNED) + return SPAMSERV_CHECK_PUNISH; + else { + chanuser->spamnode->flags |= NEONSPAMNODE_FLAG_CAPSSCAN_WARNED; + return SPAMSERV_CHECK_WARN; + } + } + return SPAMSERV_CHECK_IGNORE; +} + +static int neonspam_digitscan(struct NeonSpamSettings *settings, struct ChanUser *chanuser, char *message) { + int digit = 0, msglen = strlen(message); + int i; + if(msglen <= 4) return SPAMSERV_CHECK_IGNORE; + for(i = 0; i < msglen; i++) { + if(isdigit(message[i])) digit++; + } + digit = 100*digit/msglen; + if(digit >= settings->percent[SPAMSETTINGS_DIGITPERCENTINDEX]) { + if(!chanuser->spamnode) + createSpamNode(chanuser); + if(chanuser->spamnode->flags & NEONSPAMNODE_FLAG_DIGITSCAN_WARNED) + return SPAMSERV_CHECK_PUNISH; + else { + chanuser->spamnode->flags |= NEONSPAMNODE_FLAG_DIGITSCAN_WARNED; + return SPAMSERV_CHECK_WARN; + } + } + return SPAMSERV_CHECK_IGNORE; +} + +static int neonspam_badwordscan(struct ClientSocket *client, struct NeonSpamSettings *settings, struct ChanUser *chanuser, char *message, int punish_now, int uaccess) { + struct NeonSpamBadword *badword; + int kick_user = 0; + int ban_user = 0; + int staticban_user = 0; + int ban_duration = 0; + int checked_defaults = 0; + int apply_default_reaction = 0; + for(badword = settings->badwords; badword; badword = badword->next) { + if(!match(badword->badword, message)) { + if(badword->use_defaults) { + if(checked_defaults == 2) continue; + else if(!checked_defaults) { + if(!(settings->flags & SPAMSETTINGS_BADWORDSCAN_OPS) && (chanuser->flags & CHANUSERFLAG_OPPED)) { + checked_defaults = 2; + continue; + } + if(!(settings->flags & SPAMSETTINGS_BADWORDSCAN_VOICE) && (chanuser->flags & CHANUSERFLAG_VOICED)) { + checked_defaults = 2; + continue; + } + if(settings->exceptlevel[SPAMSETTINGS_BADWORDEXCINDEX] && settings->exceptlevel[SPAMSETTINGS_BADWORDEXCINDEX] < 501) { + if(!punish_now) + return SPAMSERV_CHECK_PUNISH; + if(settings->exceptlevel[SPAMSETTINGS_BADWORDEXCINDEX] <= uaccess) { + checked_defaults = 2; + continue; + } + } + checked_defaults = 1; + } + } else { + if(!badword->scan_ops && (chanuser->flags & CHANUSERFLAG_OPPED)) continue; + if(!badword->scan_voice && (chanuser->flags & CHANUSERFLAG_VOICED)) continue; + if(badword->exceptlevel && badword->exceptlevel < 501) { + if(!punish_now) + return SPAMSERV_CHECK_PUNISH; + if(badword->exceptlevel <= uaccess) { + checked_defaults = 2; + continue; + } + } + } + if(badword->use_default_reaction) { + apply_default_reaction = 1; + } else { + switch(badword->reaction) { + case 2: + staticban_user = 1; + if(badword->reaction_time > ban_duration) + ban_duration = badword->reaction_time; + case 1: + ban_user = 1; + case 0: + kick_user = 1; + } + } + } + } + if(apply_default_reaction) { + MYSQL_RES *res; + MYSQL_ROW row, defaults; + loadChannelSettings(chanuser->chan); + printf_mysql_query("SELECT `channel_badword_reaction`, `channel_badword_reaction_duration` FROM `channels` WHERE `channel_id` = '%d'", chanuser->chan->channel_id); + res = mysql_use(); + row = mysql_fetch_row(res); + if(!row[0] || !row[1]) { + printf_mysql_query("SELECT `channel_badword_reaction`, `channel_badword_reaction_duration` FROM `channels` WHERE `channel_name` = 'defaults'"); + res = mysql_use(); + defaults = mysql_fetch_row(res); + } + int reaction = atoi((row[0] ? row[0] : defaults[0])); + int reaction_time = atoi((row[1] ? row[1] : defaults[1])); + switch(reaction) { + case 2: + staticban_user = 1; + if(reaction_time > ban_duration) + ban_duration = reaction_time; + case 1: + ban_user = 1; + case 0: + kick_user = 1; + } + } + if(!kick_user) return SPAMSERV_CHECK_IGNORE; + char banmaskBuf[NICKLEN+USERLEN+HOSTLEN+3]; + char *banmask = NULL; + if(staticban_user) { + banmask = generate_banmask(chanuser->user, banmaskBuf); + printf_mysql_query("INSERT INTO `bans` (`ban_channel`, `ban_mask`, `ban_triggered`, `ban_timeout`, `ban_owner`, `ban_reason`) VALUES ('%d', '%s', UNIX_TIMESTAMP(), '%lu', '%d', '%s')", chanuser->chan->channel_id, escape_string(banmask), (unsigned long) (ban_duration ? (time(0) + ban_duration) : 0), 0, escape_string(SPAMSERV_MSG_BADWORD)); + if(ban_duration) { + int banid = (int) mysql_insert_id(get_mysql_conn()); + char nameBuf[MAXLEN]; + char banidBuf[20]; + sprintf(nameBuf, "ban_%d", banid); + sprintf(banidBuf, "%d", banid); + timeq_add_name(nameBuf, ban_duration, module_id, channel_ban_timeout, strdup(banidBuf)); + } + } + if(ban_user) { + if(!banmask) + banmask = generate_banmask(chanuser->user, banmaskBuf); + putsock(client, "MODE %s +b %s", chanuser->chan->name, banmask); + } + putsock(client, "KICK %s %s :%s", chanuser->chan->name, chanuser->user->nick, SPAMSERV_MSG_BADWORD); + return SPAMSERV_CHECK_DEAD; +} diff --git a/src/modules/NeonSpam.mod/event_neonspam_join.c b/src/modules/NeonSpam.mod/event_neonspam_join.c new file mode 100644 index 0000000..f34b1a3 --- /dev/null +++ b/src/modules/NeonSpam.mod/event_neonspam_join.c @@ -0,0 +1,144 @@ +/* event_neonspam_join.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +static USERAUTH_CALLBACK(neonspam_event_join_nick_lookup); +static void neonspam_event_join_punish(struct ClientSocket *client, struct ChanUser *chanuser, struct NeonSpamSettings *settings, int action); +static int neonspam_joinfloodscan(struct NeonSpamSettings *settings, struct ChanUser *chanuser); + +struct neonspam_event_join_cache { + struct ClientSocket *client; + struct ChanUser *chanuser; + struct NeonSpamSettings *settings; + int action; +}; + +static void neonspam_event_join(struct ChanUser *chanuser) { + struct ClientSocket *client = getChannelBot(chanuser->chan, BOTID); + if(!client) return; //we can't "see" this event + if(chanuser->user == client->user) { + requestOp(client->user, chanuser->chan); + return; + } + if(chanuser->user->flags & USERFLAG_ISBOT) return; + loadNeonSpamSettings(chanuser->chan); + struct NeonSpamSettings *settings = chanuser->chan->spam_settings; + if(!settings || !(settings->flags & SPAMSETTINGS_JOINSCAN)) return; + if(settings->exceptlevel[SPAMSETTINGS_JOINEXCINDEX] == 0) return; + int result = neonspam_joinfloodscan(settings, chanuser); + if(result != SPAMSERV_CHECK_IGNORE) { + //whois the user to check against exceptlevel + if((chanuser->user->flags & USERFLAG_ISAUTHED) || settings->exceptlevel[SPAMSETTINGS_JOINEXCINDEX] == 501) { + neonspam_event_join_punish(client, chanuser, settings, result); + } else { + struct neonspam_event_join_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("neonspam", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->chanuser = chanuser; + cache->settings = settings; + cache->action = result; + get_userauth(chanuser->user, module_id, neonspam_event_join_nick_lookup, cache); + } + } +} + +static USERAUTH_CALLBACK(neonspam_event_join_nick_lookup) { + struct neonspam_event_join_cache *cache = data; + neonspam_event_join_punish(cache->client, cache->chanuser, cache->settings, cache->action); + free(cache); +} + +static void neonspam_event_join_punish(struct ClientSocket *client, struct ChanUser *chanuser, struct NeonSpamSettings *settings, int action) { + int uaccess = 0; + if(chanuser->user->flags & USERFLAG_ISAUTHED) + uaccess = getChannelAccess(chanuser->user, chanuser->chan); + if(uaccess >= settings->exceptlevel[SPAMSETTINGS_JOINEXCINDEX]) return; + //scanops / scanvoiced + MYSQL_RES *res; + MYSQL_ROW row, defaults = NULL; + loadChannelSettings(chanuser->chan); + printf_mysql_query("SELECT `channel_flood_reaction`, `channel_flood_reaction_duration`, `channel_getop`, `channel_getvoice` FROM `channels` WHERE `channel_id` = '%d'", chanuser->chan->channel_id); + res = mysql_use(); + row = mysql_fetch_row(res); + if(!row[0] || !row[1] || !row[2] || !row[3]) { + printf_mysql_query("SELECT `channel_flood_reaction`, `channel_flood_reaction_duration`, `channel_getop`, `channel_getvoice` FROM `channels` WHERE `channel_name` = 'defaults'"); + res = mysql_use(); + defaults = mysql_fetch_row(res); + } + if(!(settings->flags & SPAMSETTINGS_JOINSCAN_OPS) && uaccess >= atoi((row[2] ? row[2] : defaults[2]))) return; + if(!(settings->flags & SPAMSETTINGS_JOINSCAN_VOICE) && uaccess >= atoi((row[3] ? row[3] : defaults[3]))) return; + char reason[MAXLEN]; + sprintf(reason, SPAMSERV_MSG_WARNING, SPAMSERV_MSG_JOINFLOOD); + if(action == SPAMSERV_CHECK_WARN) { + reply(client, chanuser->user, "%s", reason); + } else if(action == SPAMSERV_CHECK_PUNISH) { + int duration = atoi((row[1] ? row[1] : defaults[1])); + char banmaskBuf[NICKLEN+USERLEN+HOSTLEN+3]; + char *banmask = NULL; + switch (atoi((row[0] ? row[0] : defaults[0]))) { + case 2: //TIMEBAN + banmask = generate_banmask(chanuser->user, banmaskBuf); + printf_mysql_query("INSERT INTO `bans` (`ban_channel`, `ban_mask`, `ban_triggered`, `ban_timeout`, `ban_owner`, `ban_reason`) VALUES ('%d', '%s', UNIX_TIMESTAMP(), '%lu', '%d', '%s')", chanuser->chan->channel_id, escape_string(banmask), (unsigned long) (duration ? (time(0) + duration) : 0), 0, escape_string(reason)); + if(duration) { + int banid = (int) mysql_insert_id(get_mysql_conn()); + char nameBuf[MAXLEN]; + char banidBuf[20]; + sprintf(nameBuf, "ban_%d", banid); + sprintf(banidBuf, "%d", banid); + timeq_add_name(nameBuf, duration, module_id, channel_ban_timeout, strdup(banidBuf)); + } + case 1: //KICKBAN + if(!banmask) + banmask = generate_banmask(chanuser->user, banmaskBuf); + putsock(client, "MODE %s +b %s", chanuser->chan->name, banmask); + case 0: //KICK + putsock(client, "KICK %s %s :%s", chanuser->chan->name, chanuser->user->nick, reason); + break; + } + } +} + +static int neonspam_update_join_penalty(struct NeonSpamSettings *settings, struct NeonSpamJoinNode *joinnode, int addjoin) { + int last_update = time(0) - joinnode->last_penalty_update; + if(last_update) { + if(last_update < MAX_JOIN_TIME && joinnode->joinpenalty) { + joinnode->joinpenalty -= last_update * (MAX_JOIN_TIME / settings->sensibility_time[SPAMSETTINGS_JOINSENINDEX]); + if(joinnode->joinpenalty < 0) + joinnode->joinpenalty = 0; + } else + joinnode->joinpenalty = 0; + joinnode->last_penalty_update = time(0); + } + joinnode->joinpenalty += MAX_JOIN_TIME * addjoin; + return joinnode->joinpenalty / MAX_JOIN_TIME + ((joinnode->joinpenalty % MAX_JOIN_TIME) ? 1 : 0); +} + +static int neonspam_joinfloodscan(struct NeonSpamSettings *settings, struct ChanUser *chanuser) { + if(!chanuser->spamnode) + createSpamNode(chanuser); + int joins_pending = neonspam_update_join_penalty(settings, getNeonSpamJoinNode(chanuser), 1); + if(joins_pending == settings->sensibility_amount[SPAMSETTINGS_JOINSENINDEX]) + return SPAMSERV_CHECK_WARN; + else if(joins_pending > settings->sensibility_amount[SPAMSETTINGS_JOINSENINDEX]) + return SPAMSERV_CHECK_PUNISH; + else + return SPAMSERV_CHECK_IGNORE; +} + + diff --git a/src/modules/NeonSpam.mod/module.c b/src/modules/NeonSpam.mod/module.c new file mode 100644 index 0000000..c731d74 --- /dev/null +++ b/src/modules/NeonSpam.mod/module.c @@ -0,0 +1,34 @@ +/* module.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#include "../module.h" +#include "bot_NeonSpam.h" +#include "cmd_neonspam.h" + +static int module_initialize() { + register_commands(); + return 0; +} + +static void module_start(int type) { + init_NeonSpam(type); +} + +static void module_stop(int type) { + free_NeonSpam(type); +} + +MODULE_HEADER(module_initialize, module_start, module_stop); diff --git a/src/modules/botid.h b/src/modules/botid.h new file mode 100644 index 0000000..319f7cf --- /dev/null +++ b/src/modules/botid.h @@ -0,0 +1,28 @@ +/* botid.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _botid_h +#define _botid_h + +#define NEONSERV_BOTID 1 +#define NEONSPAM_BOTID 2 +#define DUMMYSERV_BOTID 3 +#define NEONHELP_BOTID 4 +#define NEONFUN_BOTID 5 +#define NEONBACKUP_BOTID 6 +#define NEONKICK_BOTID 7 + +#endif \ No newline at end of file diff --git a/src/modules/funcmd.mod/cmd_funcmds.c b/src/modules/funcmd.mod/cmd_funcmds.c new file mode 100644 index 0000000..eda55cc --- /dev/null +++ b/src/modules/funcmd.mod/cmd_funcmds.c @@ -0,0 +1,306 @@ +/* cmd_funcmds.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#include "../module.h" +#include "cmd_funcmds.h" +#include "../../modcmd.h" +#include "../../mysqlConn.h" +#include "../../IRCParser.h" +#include "../../ClientSocket.h" +#include "../../UserNode.h" +#include "../../ChanNode.h" +#include "../../lang.h" +#include "../../tools.h" +#include "../../DBHelper.h" +#include "../../IRCEvents.h" +#include "../../timeq.h" +#include "../../WHOHandler.h" +#include "../../EventLogger.h" + +static const struct default_language_entry msgtab[] = { + {"FUN_DICE", "$b%s$b: A $b%d$b shows on the %d-sided die."}, /* {ARGS: "TestUser", 5, 6} */ + {"FUN_DICE_NUM", "I do not understand $b%s$b. Please use a single number above 1."}, /* {ARGS: "bla"} */ + {"FUN_8BALL", "$b%s$b: %s"}, /* {ARGS: "TestUser", "Not a chance."} */ + {"FUN_8BALL_REPLIES", "Not a chance.|In your dreams.|Absolutely!|Could be, could be.|No!"}, + {"FUN_COOKIE", "gives %1$s a very big chocolate cookie. %1$s has got %2$d cookies until now (%3$d in this channel)."}, /* {ARGS: "TestUser", 20, 50} */ + {"FUN_BOMB_MENU", "There are the following commands:"}, + {"FUN_BOMB_MENU_PLANT", "$b%s plant [wires] $b Plant a bomb in front of another users feet ([wires] is the number of wires - default: %d)"}, /* {ARGS: "bomb", 3} */ + {"FUN_BOMB_MENU_KPLANT", "$b%s kplant [wires]$b Plant a kick-bomb in front of another users feet ([wires] is the number of wires - default: %d) (OP only)"}, /* {ARGS: "bomb", 3} */ + {"FUN_BOMB_MENU_DEFUSE", "$b%s defuse $b Defuses a bomb (maybe :D)"}, /* {ARGS: "bomb"} */ + {"FUN_BOMB_MENU_RETURN", "$b%s return $b Pushes a bomb planted infront of you back to the owner ($k4WARNING$k: There is a highly chance that the bomb detonates)"}, /* {ARGS: "bomb"} */ + {"FUN_BOMB_SELF", "You can not plant a bomb in front of yourself..."}, + {"FUN_BOMB_TARGET_ACTIVE", "%s has already an active bomb..."}, + {"FUN_BOMB_OWNER_ACTIVE", "You have already planted another bomb..."}, + {"FUN_BOMB_PLANTED", "%1$s planted a bomb in front of %2$s's feet. %2$s, you have $b%3$d seconds$b to defuse the bomb by cutting one of the following wires: %4$s ($uuse:$u %5$s defuse )"}, /* {ARGS: "TestUser", "TestTarget", 30, "blue, red, green", "bomb"} */ + {"FUN_BOMB_KPLANTED", "%1$s planted a kick-bomb in front of %2$s's feet. %2$s, you have $b%3$d seconds$b to defuse the bomb by cutting one of the following wires: %4$s ($uuse:$u %5$s defuse )"}, /* {ARGS: "TestUser", "TestTarget", 30, "blue, red, green", "bomb"} */ + {"FUN_BOMB_PLANT_SERVICE", "You can't plant a bomb in front of this user (bot)..."}, + {"FUN_BOMB_PLANT_PROTECTED", "You can't plant a bomb in front of this user (protect)..."}, + {"FUN_BOMB_PLANT_MAXWIRES", "A bomb can only have %d-%d wires!"}, /* {ARGS: 2, 8} */ + {"FUN_BOMB_WIRES", "$k4red$k|$k3green$k|$k8yellow$k|$k12blue$k|$k1black$k|$k6purple$k|$k7orange$k|$k11aqua$k"}, + {"FUN_BOMB_NOBOMB", "There is no bomb you could defuse..."}, + {"FUN_BOMB_UNKNOWN_WIRE", "%s is an unknown wire..."}, /* {ARGS: "white"} */ + {"FUN_BOMB_DEFUSED", "%1$s has successfully defused the bomb! %1$s got %2$d bombs (%3$d in this channel) and has defused %4$d of them."}, /* {ARGS: "TestUser", 20, 10, 5} */ + {"FUN_BOMB_DETONATED", "*BOOOOOOM* The bomb explodes in front of %1$s!!! %2$s was the right wire. %1$s got %3$d bombs (%4$d in this channel)"}, /* {ARGS: "TestUser", "white", 20, 10} */ + {NULL, NULL} +}; + +#define FUNCMD_BOMB_WIRES_DEFAULT 3 +#define FUNCMD_BOMB_WIRES_MIN 2 +#define FUNCMD_BOMB_WIRES_MAX 8 +#define FUNCMD_BOMB_TIME 30 + +static int funcmd_bomb_freeuser(struct UserNode *user); +static int funcmd_bomb_freechan(struct ChanNode *chan); +static void funcmd_bomb_freeclient(struct ClientSocket *client); + +void init_funcmds() { + register_default_language_table(msgtab); + srand(time(NULL)); + bind_freeuser(funcmd_bomb_freeuser, module_id); + bind_freechan(funcmd_bomb_freechan, module_id); + bind_freeclient(funcmd_bomb_freeclient, module_id); +} + +void register_commands() { + //Fun Commands + register_command_alias(3, "FunCMD"); + #define USER_COMMAND(NAME,FUNCTION,PARAMCOUNT,FLAGS) register_command(3, NAME, module_id, FUNCTION, PARAMCOUNT, NULL, 0, FLAGS) + // NAME FUNCTION PARAMS FLAGS + //USER_COMMAND("extscript", neonserv_cmd_extscript, 0, CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_EMPTY_ARGS | CMDFLAG_CHAN_PARAM | CMDFLAG_FUNCMD); + USER_COMMAND("ping", funcmd_ping, 0, CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_FUNCMD); + USER_COMMAND("pong", funcmd_pong, 0, CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_FUNCMD); + USER_COMMAND("dice", funcmd_dice, 1, CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_FUNCMD); + USER_COMMAND("8ball", funcmd_8ball, 1, CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_FUNCMD); + USER_COMMAND("cookie", funcmd_cookie, 0, CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_FUNCMD); + USER_COMMAND("bomb", funcmd_bomb, 0, CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_FUNCMD); + USER_COMMAND("ship", funcmd_ship, 0, CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN | CMDFLAG_FUNCMD); + #undef USER_COMMAND +} + +struct funcmd_header_info { + struct ClientSocket *client; + struct UserNode *user; + struct ChanNode *chan; + char send_notice; + char null_language; +}; + +#define FUNCMD_HEADER \ +struct funcmd_header_info *header = malloc(sizeof(*header)); \ +header->client = textclient; \ +header->user = user; \ +header->chan = chan; \ +header->null_language = 0; \ +{\ + MYSQL_RES *res; \ + MYSQL_ROW row; \ + printf_mysql_query("SELECT `channel_toys` FROM `channels` WHERE `channel_name` = '%s'", escape_string(chan->name)); \ + res = mysql_use(); \ + row = mysql_fetch_row(res); \ + if(!row || !strcmp(row[0], "0")) { \ + reply(textclient, user, "NS_FUN_DISABLED", chan->name); \ + return; \ + } else if(!strcmp(row[0], "1")) \ + header->send_notice = 1; \ + else \ + header->send_notice = 0; \ +} + +#define FUNCMD_FOOTER \ +free(header); + +#define REPLYTYPE_NORMAL 0x01 +#define REPLYTYPE_ACTION 0x02 +#define REPLYTYPE_NOTICE 0x04 +static void funcmd_reply(struct funcmd_header_info *header, const char *text, int type, ...) { + if (!(header->client->flags & SOCKET_FLAG_CONNECTED)) return; + struct ClientSocket *client = header->client; + const char *reply_format = get_language_string((header->null_language ? NULL : header->user), text); + if(reply_format) + text = reply_format; + char formatBuf[MAXLEN]; + if(header->send_notice || (type & REPLYTYPE_NOTICE)) { + if(type & REPLYTYPE_ACTION) + sprintf(formatBuf, "NOTICE %s :%s %s", header->user->nick, header->client->user->nick, text); + else + sprintf(formatBuf, "NOTICE %s :%s", header->user->nick, text); + } else { + if(type & REPLYTYPE_ACTION) + sprintf(formatBuf, "PRIVMSG %s :\001ACTION %s\001", header->chan->name, text); + else + sprintf(formatBuf, "PRIVMSG %s :%s", header->chan->name, text); + if(!isUserOnChan(client->user, header->chan) && isModeSet(header->chan->modes, 'n')) + client = getChannelBot(header->chan, 0); + } + va_list arg_list; + char sendBuf[MAXLEN]; + int pos; + sendBuf[0] = '\0'; + va_start(arg_list, type); + pos = vsnprintf(sendBuf, MAXLEN - 2, formatBuf, arg_list); + va_end(arg_list); + if (pos < 0 || pos > (MAXLEN - 2)) pos = MAXLEN - 2; + sendBuf[pos] = '\n'; + sendBuf[pos+1] = '\0'; + write_socket(client, sendBuf, pos+1); +} + +static char* getSetting(struct UserNode *user, struct ChanNode *chan, const char *setting) { + char *uname = ""; + int cid = 0; + MYSQL_RES *res; + MYSQL_ROW row; + if(user) { + uname = ((user->flags & USERFLAG_ISAUTHED) ? user->auth : "*"); + } + if(chan) { + loadChannelSettings(chan); + if(chan->flags & CHANFLAG_CHAN_REGISTERED) + cid = chan->channel_id; + } + printf_mysql_query("SELECT `value` FROM `fundata` WHERE `user` = '%s' AND `cid` = '%d' AND `name` = '%s'", escape_string(uname), cid, escape_string(setting)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + return row[0]; + } else + return NULL; +} + +static void setSetting(struct UserNode *user, struct ChanNode *chan, const char *setting, const char *value) { + char *uname = ""; + int cid = 0; + MYSQL_RES *res; + MYSQL_ROW row; + if(user) { + uname = ((user->flags & USERFLAG_ISAUTHED) ? user->auth : "*"); + } + if(chan) { + loadChannelSettings(chan); + if(chan->flags & CHANFLAG_CHAN_REGISTERED) + cid = chan->channel_id; + } + printf_mysql_query("SELECT `id`, `value` FROM `fundata` WHERE `user` = '%s' AND `cid` = '%d' AND `name` = '%s'", escape_string(uname), cid, escape_string(setting)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + if(strcmp(row[1], value)) + printf_mysql_query("UPDATE `fundata` SET `value` = '%s' WHERE `id` = '%s'", escape_string(value), row[0]); + } else + printf_mysql_query("INSERT INTO `fundata` (`user`, `cid`, `name`, `value`) VALUES ('%s', '%d', '%s', '%s')", escape_string(uname), cid, escape_string(setting), escape_string(value)); +} + +#include "cmd_funcmds_bomb.c" + + +CMD_BIND(funcmd_ping) { + FUNCMD_HEADER; + funcmd_reply(header, "\002%s\002: Pong!", REPLYTYPE_NORMAL, user->nick); + FUNCMD_FOOTER; +} + +CMD_BIND(funcmd_pong) { + FUNCMD_HEADER; + funcmd_reply(header, "\002%s\002: Ping!", REPLYTYPE_NORMAL, user->nick); + FUNCMD_FOOTER; +} + +CMD_BIND(funcmd_dice) { + FUNCMD_HEADER; + int max = atoi(argv[0]); + if(max > 1) { + int val = (rand() % max) + 1; + funcmd_reply(header, "%s", REPLYTYPE_NORMAL, " ____"); + funcmd_reply(header, "%s", REPLYTYPE_NORMAL, " /\\' .\\ _____"); + funcmd_reply(header, "%s", REPLYTYPE_NORMAL, " /: \\___\\ / . /\\"); + funcmd_reply(header, "%s", REPLYTYPE_NORMAL, " \\' / . / /____/..\\"); + funcmd_reply(header, "%s", REPLYTYPE_NORMAL, " \\/___/ \\' '\\ /"); + funcmd_reply(header, "%s", REPLYTYPE_NORMAL, " \\'__'\\/ "); + funcmd_reply(header, "FUN_DICE", REPLYTYPE_NORMAL, user->nick, val, max); + } else + funcmd_reply(header, "FUN_DICE_NUM", REPLYTYPE_NORMAL, argv[0]); + FUNCMD_FOOTER; +} + +CMD_BIND(funcmd_8ball) { + FUNCMD_HEADER; + char *message = merge_argv(argv, 0, argc); + const char *const_replies = get_language_string(header->user, "FUN_8BALL_REPLIES"); + char replies[MAXLEN]; + int i, reply_count = 1; + for(i = 0; const_replies[i]; i++) { + if(const_replies[i] == '|') + reply_count++; + replies[i] = const_replies[i]; + } + replies[i] = '\0'; + unsigned int crc32_val = (crc32(message)) % reply_count; + char *creply = (crc32_val == 0 ? replies : NULL); + reply_count = 0; + for(i = 0; replies[i]; i++) { + if(replies[i] == '|') { + if(creply) { + replies[i] = '\0'; + break; + } else { + reply_count++; + if(reply_count == crc32_val) { + creply = &replies[i+1]; + } + } + } + } + if(creply) { + funcmd_reply(header, "FUN_8BALL", REPLYTYPE_NORMAL, user->nick, creply); + } + FUNCMD_FOOTER; +} + +CMD_BIND(funcmd_cookie) { + FUNCMD_HEADER; + if(argc) { + if(!(user = getUserByNick(argv[0]))) { + reply(header->client, header->user, "NS_USER_UNKNOWN", argv[0]); + FUNCMD_FOOTER; + return; + } + } + char *tmp; + int user_count = ((tmp = getSetting(user, chan, "cookies")) ? atoi(tmp) : 0); + int total_count = ((tmp = getSetting(user, NULL, "cookies")) ? atoi(tmp) : 0); + user_count++; + total_count++; + char buf[10]; + sprintf(buf, "%d", user_count); + setSetting(user, chan, "cookies", buf); + sprintf(buf, "%d", total_count); + setSetting(user, NULL, "cookies", buf); + funcmd_reply(header, "FUN_COOKIE", REPLYTYPE_ACTION, user->nick, total_count, user_count); + FUNCMD_FOOTER; +} + +CMD_BIND(funcmd_ship) { + FUNCMD_HEADER; + funcmd_reply(header, "%s", REPLYTYPE_NORMAL, "\0030,0------------------------\0031|"); + funcmd_reply(header, "%s", REPLYTYPE_NORMAL, "\0030,0----------------------\00314)_)_)"); + funcmd_reply(header, "%s%c%s", REPLYTYPE_NORMAL, "\0030,0--------------------\0031,4\002=", header->client->user->nick[0], "=\002\00314,0|)___)"); + funcmd_reply(header, "%s", REPLYTYPE_NORMAL, "\0030,0-------------------\00314)__) )___) )"); + funcmd_reply(header, "%s", REPLYTYPE_NORMAL, "\0030,0------------------\00314)___) )____))_)"); + funcmd_reply(header, "%s", REPLYTYPE_NORMAL, "\0030,0----------------\00314)____)_____))__)\\"); + funcmd_reply(header, "%s", REPLYTYPE_NORMAL, "\0030,0---------------\00314)\0035__\00314|\0035____\00314/|\0035___\00314|\0035___\00314-\\\\---"); + funcmd_reply(header, "%s", REPLYTYPE_NORMAL, "\0030,0-------\00312^^^^^^\0035~~~\\ \002oo oo oo oo\002 /~~\00312^^^^^^"); + funcmd_reply(header, "%s", REPLYTYPE_NORMAL, "\0030,0------------\00312~^^^^ ~~~~^^~~~~^^~~^^~~~~~"); + funcmd_reply(header, "%s", REPLYTYPE_NORMAL, "\0030,0-----------------\00312~~^^ ~^^~ ~^~ ~^"); + FUNCMD_FOOTER; +} diff --git a/src/modules/funcmd.mod/cmd_funcmds.h b/src/modules/funcmd.mod/cmd_funcmds.h new file mode 100644 index 0000000..7d9e6f4 --- /dev/null +++ b/src/modules/funcmd.mod/cmd_funcmds.h @@ -0,0 +1,34 @@ +/* cmd_funcmds.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _cmd_funcmds_h +#define _cmd_funcmds_h +#include "../module.h" +#include "../../main.h" +#include "../../modcmd.h" + +void init_funcmds(); +void register_commands(); + +CMD_BIND(funcmd_ping); +CMD_BIND(funcmd_pong); +CMD_BIND(funcmd_dice); +CMD_BIND(funcmd_8ball); +CMD_BIND(funcmd_cookie); +CMD_BIND(funcmd_bomb); +CMD_BIND(funcmd_ship); + +#endif \ No newline at end of file diff --git a/src/modules/funcmd.mod/cmd_funcmds_bomb.c b/src/modules/funcmd.mod/cmd_funcmds_bomb.c new file mode 100644 index 0000000..db48bad --- /dev/null +++ b/src/modules/funcmd.mod/cmd_funcmds_bomb.c @@ -0,0 +1,449 @@ +/* cmd_funcmds_bomb.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +static void funcmd_bomb_plant(struct funcmd_header_info *header, struct Event *event, char **argv, int argc, int kick_bomb); +static USERAUTH_CALLBACK(funcmd_bomb_plant_nick_lookup); +static void funcmd_bomb_plant_async1(struct funcmd_header_info *header, struct Event *event, struct UserNode *user, int wires, int kick_bomb); +static void funcmd_bomb_plant_async2(struct funcmd_header_info *header, struct Event *event, struct UserNode *user, int wires, int kick_bomb); +static void funcmd_bomb_defuse(struct funcmd_header_info *header, struct Event *event, char **argv, int argc); +static void funcmd_bomb_return(struct funcmd_header_info *header, struct Event *event, char **argv, int argc); +static TIMEQ_CALLBACK(funcmd_bomb_timeout); + +struct funcmd_bomb_plant_cache { + struct funcmd_header_info header; + struct Event *event; + int wires : 4; + int kick_bomb : 4; +}; + +struct funcmd_bomb_bomb { + struct funcmd_header_info header; + struct UserNode *owner, *target; + struct timeq_entry *timer; + char wires[FUNCMD_BOMB_WIRES_MAX]; + int right_wire : 4; + int kick_bomb : 4; + struct funcmd_bomb_bomb *next; +}; + +static struct funcmd_bomb_bomb *funcmd_bomb_bombs = NULL; + +static int funcmd_bomb_freeuser(struct UserNode *user) { + struct funcmd_bomb_bomb *bomb, *next_bomb, *prev_bomb = NULL; + for(bomb = funcmd_bomb_bombs; bomb; bomb = next_bomb) { + next_bomb = bomb->next; + if(bomb->owner == user) + bomb->owner = NULL; + if(bomb->target == user) { + timeq_del(bomb->timer); + free(bomb); + if(prev_bomb) + prev_bomb->next = next_bomb; + else + funcmd_bomb_bombs = next_bomb; + } else + prev_bomb = bomb; + } + return 1; +} + +static int funcmd_bomb_freechan(struct ChanNode *chan) { + struct funcmd_bomb_bomb *bomb, *next_bomb, *prev_bomb = NULL; + for(bomb = funcmd_bomb_bombs; bomb; bomb = next_bomb) { + next_bomb = bomb->next; + if(bomb->header.chan == chan) { + timeq_del(bomb->timer); + free(bomb); + if(prev_bomb) + prev_bomb->next = next_bomb; + else + funcmd_bomb_bombs = next_bomb; + } else + prev_bomb = bomb; + } + return 1; +} + +static void funcmd_bomb_freeclient(struct ClientSocket *client) { + struct funcmd_bomb_bomb *bomb, *next_bomb, *prev_bomb = NULL; + for(bomb = funcmd_bomb_bombs; bomb; bomb = next_bomb) { + next_bomb = bomb->next; + if(bomb->header.client == client) { + timeq_del(bomb->timer); + free(bomb); + if(prev_bomb) + prev_bomb->next = next_bomb; + else + funcmd_bomb_bombs = next_bomb; + } else + prev_bomb = bomb; + } +} + +CMD_BIND(funcmd_bomb) { + FUNCMD_HEADER; + if(argc) { + char *subcmd = argv[0]; + argv++; + argc--; + if(!stricmp(subcmd, "plant")) + funcmd_bomb_plant(header, event, argv, argc, 0); + else if(!stricmp(subcmd, "kplant")) + funcmd_bomb_plant(header, event, argv, argc, 1); + else if(!stricmp(subcmd, "defuse")) + funcmd_bomb_defuse(header, event, argv, argc); + else if(!stricmp(subcmd, "return")) + funcmd_bomb_return(header, event, argv, argc); + else { + char tmp[MAXLEN]; + sprintf(tmp, "%s %s", event->command->cmd, subcmd); + reply(header->client, header->user, "MODCMD_UNKNOWN", tmp); + } + } else { + reply(header->client, header->user, "FUN_BOMB_MENU"); + reply(header->client, header->user, "FUN_BOMB_MENU_PLANT", event->command->cmd, FUNCMD_BOMB_WIRES_DEFAULT); + reply(header->client, header->user, "FUN_BOMB_MENU_KPLANT", event->command->cmd, FUNCMD_BOMB_WIRES_DEFAULT); + reply(header->client, header->user, "FUN_BOMB_MENU_DEFUSE", event->command->cmd); + //reply(header->client, header->user, "FUN_BOMB_MENU_RETURN", event->command->cmd); + } + FUNCMD_FOOTER; +} + +static void funcmd_bomb_plant(struct funcmd_header_info *header, struct Event *event, char **argv, int argc, int kick_bomb) { + if(!argc) { + reply(header->client, header->user, "MODCMD_LESS_PARAM_COUNT"); + return; + } + struct UserNode *user; + int wires = FUNCMD_BOMB_WIRES_DEFAULT; + if(!(user = getUserByNick(argv[0]))) { + reply(header->client, header->user, "NS_USER_UNKNOWN", argv[0]); + return; + } + if(isNetworkService(user)) { + reply(header->client, header->user, "FUN_BOMB_PLANT_SERVICE"); + return; + } + if(user == header->user) { + reply(header->client, header->user, "FUN_BOMB_SELF"); + return; + } + if(argc > 1) { + wires = atoi(argv[1]); + if(wires < FUNCMD_BOMB_WIRES_MIN || wires > FUNCMD_BOMB_WIRES_MAX) { + reply(header->client, header->user, "FUN_BOMB_PLANT_MAXWIRES", FUNCMD_BOMB_WIRES_MIN, FUNCMD_BOMB_WIRES_MAX); + return; + } + } + if(kick_bomb == 1) { /* protect shit... */ + if(user->flags & USERFLAG_ISAUTHED) { + funcmd_bomb_plant_async1(header, event, user, wires, kick_bomb); + } else { + struct funcmd_bomb_plant_cache *cache = malloc(sizeof(*cache)); + memcpy(&cache->header, header, sizeof(*header)); + cache->event = event; + cache->wires = wires; + cache->kick_bomb = kick_bomb; + get_userauth(user, module_id, funcmd_bomb_plant_nick_lookup, cache); + } + } else + funcmd_bomb_plant_async2(header, event, user, wires, kick_bomb); +} + +static USERAUTH_CALLBACK(funcmd_bomb_plant_nick_lookup) { + struct funcmd_bomb_plant_cache *cache = data; + if(!user) { + reply(cache->header.client, cache->header.user, "NS_USER_UNKNOWN", "*"); + } else { + funcmd_bomb_plant_async1(&cache->header, cache->event, user, cache->wires, cache->kick_bomb); + } + free(cache); +} + +static void funcmd_bomb_plant_async1(struct funcmd_header_info *header, struct Event *event, struct UserNode *user, int wires, int kick_bomb) { + if(kick_bomb == 1) { /* protect shit... */ + if(isUserProtected(header->chan, user, header->user)) { + reply(header->client, header->user, "FUN_BOMB_PLANT_PROTECTED"); + return; + } + } + funcmd_bomb_plant_async2(header, event, user, wires, kick_bomb); +} + +static void funcmd_bomb_defuse_strip_color(char *buffer) { + int i, j; + j = 0; + for(i = 0; buffer[i]; i++) { + if(buffer[i] == '\003') { + i++; + if(!buffer[i]) break; + if(isdigit(buffer[i])) { + i++; + if(!buffer[i]) break; + } + if(isdigit(buffer[i])) { + i++; + if(!buffer[i]) break; + } + } + buffer[j++] = buffer[i]; + } + buffer[j] = 0; +} + +static int funcmd_bomb_defuse_get_wire_id(char *wire, struct UserNode *user) { + char *wires = get_language_string(user, "FUN_BOMB_WIRES"); + char *cwire = wires; + char tmp[MAXLEN]; + int found = 0, cindex = 0; + do { + wires = strchr(cwire, '|'); + if(wires) { + *wires = '\0'; + } + cindex++; + strcpy(tmp, cwire); + funcmd_bomb_defuse_strip_color(tmp); + if(!stricmp(wire, tmp)) + found = cindex; + if(wires) { + *wires = '|'; + cwire = wires + 1; + } else + cwire = NULL; + } while(!found && cwire); + return found; +} + +static int funcmd_bomb_defuse_get_wire_name(int wire_id, char *buffer, char *wires, struct UserNode *user) { + if(!wires) + wires = get_language_string(user, "FUN_BOMB_WIRES"); + char *cwire = wires; + int cindex = 0, chars = 0; + buffer[0] = 0; + do { + wires = strchr(cwire, '|'); + if(wires) { + *wires = '\0'; + } + cindex++; + if(cindex == wire_id) + chars = sprintf(buffer, "%s", cwire); + if(wires) { + *wires = '|'; + cwire = wires + 1; + } + } while(!chars && cwire); + return chars; +} + +static void funcmd_bomb_plant_async2(struct funcmd_header_info *header, struct Event *event, struct UserNode *user, int wires, int kick_bomb) { + //everything should be checked now... plant the bomb :) + struct funcmd_bomb_bomb *bomb; + for(bomb = funcmd_bomb_bombs; bomb; bomb = bomb->next) { + if(bomb->target == user) { + reply(header->client, header->user, "FUN_BOMB_TARGET_ACTIVE", user->nick); + return; + } + if(bomb->owner == header->user) { + reply(header->client, header->user, "FUN_BOMB_OWNER_ACTIVE"); + return; + } + } + bomb = malloc(sizeof(*bomb)); + memcpy(&bomb->header, header, sizeof(*header)); + bomb->target = user; + bomb->owner = header->user; + bomb->kick_bomb = kick_bomb; + bomb->timer = timeq_add(FUNCMD_BOMB_TIME, module_id, funcmd_bomb_timeout, bomb); + bomb->next = funcmd_bomb_bombs; + funcmd_bomb_bombs = bomb; + int i, j, k, l; + int pool[FUNCMD_BOMB_WIRES_MAX+1]; + for(i = 0; i < FUNCMD_BOMB_WIRES_MAX; i++) { + pool[i] = i+1; + } + pool[i] = 0; + for(i = 0; i < FUNCMD_BOMB_WIRES_MAX; i++) { + if(i < wires) { + j = (rand() % (FUNCMD_BOMB_WIRES_MAX - i)); + bomb->wires[i] = pool[j]; + l = 0; + for(k = 0; k < (FUNCMD_BOMB_WIRES_MAX - i); k++) { + if(k == j) + l = 1; + pool[k] = pool[k+l]; + } + } else + bomb->wires[i] = 0; + } + bomb->right_wire = bomb->wires[(rand() % wires)]; + char *wires_lang_str_a = get_language_string(header->user, "FUN_BOMB_WIRES"); + char *wires_lang_str_b = get_language_string(user, "FUN_BOMB_WIRES"); + if(!header->send_notice && wires_lang_str_a != wires_lang_str_b) { + wires_lang_str_a = get_language_string(NULL, "FUN_BOMB_WIRES"); + header->null_language = 1; + bomb->header.null_language = 1; + } + char wires_str[MAXLEN]; + j = 0; + for(i = 0; i < wires; i++) { + if(i) { + wires_str[j++] = ','; + wires_str[j++] = ' '; + } + j += funcmd_bomb_defuse_get_wire_name(bomb->wires[i], wires_str+j, wires_lang_str_a, NULL); + } + wires_str[j] = '\0'; + if(header->send_notice) { + reply(header->client, header->user, (kick_bomb ? "FUN_BOMB_KPLANTED" : "FUN_BOMB_PLANTED"), header->user->nick, user->nick, FUNCMD_BOMB_TIME, wires_str, event->command->cmd); + j = 0; + for(i = 0; i < wires; i++) { + if(i) { + wires_str[j++] = ','; + wires_str[j++] = ' '; + } + j += funcmd_bomb_defuse_get_wire_name(bomb->wires[i], wires_str+j, wires_lang_str_b, NULL); + } + wires_str[j] = '\0'; + reply(header->client, user, (kick_bomb ? "FUN_BOMB_KPLANTED" : "FUN_BOMB_PLANTED"), header->user->nick, user->nick, FUNCMD_BOMB_TIME, wires_str, event->command->cmd); + } else + funcmd_reply(header, (kick_bomb ? "FUN_BOMB_KPLANTED" : "FUN_BOMB_PLANTED"), REPLYTYPE_NORMAL, header->user->nick, user->nick, FUNCMD_BOMB_TIME, wires_str, event->command->cmd); +} + +static void funcmd_bomb_detonate(struct funcmd_bomb_bomb *bomb) { + char *ptr; + int user_count = ((ptr = getSetting(bomb->target, bomb->header.chan, "bombs")) ? atoi(ptr) : 0); + int total_count = ((ptr = getSetting(bomb->target, NULL, "bombs")) ? atoi(ptr) : 0); + int detonated_count = ((ptr = getSetting(bomb->target, NULL, "bombs_detonated")) ? atoi(ptr) : 0); + user_count++; + total_count++; + detonated_count++; + char buf[10]; + sprintf(buf, "%d", user_count); + setSetting(bomb->target, bomb->header.chan, "bombs", buf); + sprintf(buf, "%d", total_count); + setSetting(bomb->target, NULL, "bombs", buf); + sprintf(buf, "%d", detonated_count); + setSetting(bomb->target, NULL, "bombs_detonated", buf); + char *wires = get_language_string(NULL, "FUN_BOMB_WIRES"); + char *cwire = wires; + char tmp[MAXLEN]; + int cindex = 0; + tmp[0] = 0; + do { + wires = strchr(cwire, '|'); + if(wires) { + *wires = '\0'; + } + cindex++; + if(cindex == bomb->right_wire) + strcpy(tmp, cwire); + if(wires) { + *wires = '|'; + cwire = wires + 1; + } else + cwire = NULL; + } while(!tmp[0] && cwire); + if(bomb->header.send_notice) + reply(bomb->header.client, bomb->owner, "FUN_BOMB_DETONATED", REPLYTYPE_NORMAL, bomb->target->nick, tmp, total_count, user_count); + funcmd_reply(&bomb->header, "FUN_BOMB_DETONATED", REPLYTYPE_NORMAL, bomb->target->nick, tmp, total_count, user_count); + if(bomb->kick_bomb) { + putsock(bomb->header.client, "KICK %s %s :[BOMB] *BOOOOOOM*", bomb->header.chan->name, bomb->target->nick); + } +} + +static void funcmd_bomb_defuse(struct funcmd_header_info *header, struct Event *event, char **argv, int argc) { + if(!argc) { + reply(header->client, header->user, "MODCMD_LESS_PARAM_COUNT"); + return; + } + struct funcmd_bomb_bomb *bomb, *prev_bomb = NULL; + for(bomb = funcmd_bomb_bombs; bomb; bomb = bomb->next) { + if(bomb->target == header->user) + break; + else + prev_bomb = bomb; + } + if(!bomb) { + reply(header->client, header->user, "FUN_BOMB_NOBOMB"); + return; + } + funcmd_bomb_defuse_strip_color(argv[0]); + int cut_wire = funcmd_bomb_defuse_get_wire_id(argv[0], header->user); + if(!cut_wire) + cut_wire = funcmd_bomb_defuse_get_wire_id(argv[0], NULL); + if(!cut_wire) { + reply(header->client, header->user, "FUN_BOMB_UNKNOWN_WIRE", argv[0]); + return; + } + if(bomb->right_wire == cut_wire) { + char *tmp; + int user_count = ((tmp = getSetting(header->user, header->chan, "bombs")) ? atoi(tmp) : 0); + int total_count = ((tmp = getSetting(header->user, NULL, "bombs")) ? atoi(tmp) : 0); + int defused_count = ((tmp = getSetting(header->user, NULL, "bombs_defused")) ? atoi(tmp) : 0); + user_count++; + total_count++; + defused_count++; + char buf[10]; + sprintf(buf, "%d", user_count); + setSetting(header->user, header->chan, "bombs", buf); + sprintf(buf, "%d", total_count); + setSetting(header->user, NULL, "bombs", buf); + sprintf(buf, "%d", defused_count); + setSetting(header->user, NULL, "bombs_defused", buf); + + if(header->send_notice) + reply(header->client, bomb->owner, "FUN_BOMB_DEFUSED", REPLYTYPE_NORMAL, header->user->nick, total_count, user_count, defused_count); + funcmd_reply(header, "FUN_BOMB_DEFUSED", REPLYTYPE_NORMAL, header->user->nick, total_count, user_count, defused_count); + } else { + funcmd_bomb_detonate(bomb); + } + timeq_del(bomb->timer); + if(prev_bomb) + prev_bomb->next = bomb->next; + else + funcmd_bomb_bombs = bomb->next; + free(bomb); +} + +static void funcmd_bomb_return(struct funcmd_header_info *header, struct Event *event, char **argv, int argc) { + if(!argc) { + reply(header->client, header->user, "MODCMD_LESS_PARAM_COUNT"); + return; + } + //following ;) +} + +static TIMEQ_CALLBACK(funcmd_bomb_timeout) { + struct funcmd_bomb_bomb *cbomb = data; + struct funcmd_bomb_bomb *bomb, *prev_bomb = NULL; + for(bomb = funcmd_bomb_bombs; bomb; bomb = bomb->next) { + if(cbomb == bomb) { + cbomb = NULL; + break; + } else + prev_bomb = bomb; + } + if(cbomb) return; + funcmd_bomb_detonate(bomb); + if(prev_bomb) + prev_bomb->next = bomb->next; + else + funcmd_bomb_bombs = bomb->next; + free(bomb); +} diff --git a/src/modules/funcmd.mod/module.c b/src/modules/funcmd.mod/module.c new file mode 100644 index 0000000..ae7d574 --- /dev/null +++ b/src/modules/funcmd.mod/module.c @@ -0,0 +1,34 @@ +/* module.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#include "../module.h" +#include "cmd_funcmds.h" + +static int module_initialize() { + init_funcmds(); + register_commands(); + return 0; +} + +static void module_start(int type) { + +} + +static void module_stop(int type) { + +} + +MODULE_HEADER(module_initialize, module_start, module_stop); diff --git a/src/modules/global.mod/cmd_global.c b/src/modules/global.mod/cmd_global.c new file mode 100644 index 0000000..05d79fc --- /dev/null +++ b/src/modules/global.mod/cmd_global.c @@ -0,0 +1,62 @@ +/* cmd_global.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#include "../module.h" +#include "cmd_global.h" +#include "../../modcmd.h" +#include "../../ConfigParser.h" + +void register_commands() { + + //Global Commands + #define USER_COMMAND(NAME,FUNCTION,PARAMCOUNT,PRIVS,FLAGS) register_command(0, NAME, module_id, FUNCTION, PARAMCOUNT, PRIVS, 0, FLAGS) + // NAME FUNCTION PARAMS PRIVS FLAGS + USER_COMMAND("version", global_cmd_version, 0, NULL, 0); + USER_COMMAND("netinfo", global_cmd_netinfo, 0, NULL, 0); + USER_COMMAND("commands", global_cmd_commands, 0, NULL, 0); + USER_COMMAND("command", global_cmd_command, 1, NULL, CMDFLAG_ESCAPE_ARGS); + USER_COMMAND("staff", global_cmd_staff, 0, NULL, 0); + USER_COMMAND("motd", global_cmd_motd, 0, NULL, 0); + USER_COMMAND("extscript", global_cmd_extscript, 0, NULL, CMDFLAG_EMPTY_ARGS | CMDFLAG_CHAN_PARAM); + #undef USER_COMMAND + + #define OPER_COMMAND(NAME,FUNCTION,PARAMCOUNT,GACCESS,FLAGS) register_command(0, NAME, module_id, FUNCTION, PARAMCOUNT, NULL, GACCESS, FLAGS) + // NAME FUNCTION PARAMS ACCS FLAGS + OPER_COMMAND("register", global_cmd_register, 1, 200, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_CHAN_PARAM | CMDFLAG_OPLOG); + OPER_COMMAND("unregister", global_cmd_unregister,0, 200, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_CHAN_PARAM | CMDFLAG_OPLOG); + OPER_COMMAND("move", global_cmd_move, 2, 300, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_CHAN_PARAM | CMDFLAG_OPLOG); + OPER_COMMAND("say", global_cmd_say, 2, 600, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_CHAN_PARAM | CMDFLAG_OPLOG); + OPER_COMMAND("emote", global_cmd_emote, 2, 600, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_CHAN_PARAM | CMDFLAG_OPLOG); + OPER_COMMAND("notice", global_cmd_notice, 2, 600, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_CHAN_PARAM | CMDFLAG_OPLOG); + OPER_COMMAND("raw", global_cmd_raw, 1, 800, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_OPLOG); + OPER_COMMAND("god", global_cmd_god, 0, 1, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_OPLOG); + OPER_COMMAND("reloadlang", global_cmd_reloadlang,1, 500, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_OPLOG); + OPER_COMMAND("bind", global_cmd_bind, 2, 900, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_OPLOG | CMDFLAG_REQUIRED | CMDFLAG_ESCAPE_ARGS); + OPER_COMMAND("unbind", global_cmd_unbind, 1, 900, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_OPLOG | CMDFLAG_REQUIRED | CMDFLAG_ESCAPE_ARGS); + OPER_COMMAND("setaccess", global_cmd_setaccess, 2, 1000, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_OPLOG); + OPER_COMMAND("bots", global_cmd_bots, 0, 1000, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH); + OPER_COMMAND("reload", global_cmd_reload, 0, 1000, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_OPLOG); + OPER_COMMAND("restart", global_cmd_restart, 0, 1000, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_OPLOG); + OPER_COMMAND("die", global_cmd_die, 0, 1000, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_OPLOG); + OPER_COMMAND("setbot", global_cmd_setbot, 1, 1000, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_OPLOG); + OPER_COMMAND("addbot", global_cmd_addbot, 2, 1000, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_OPLOG); + OPER_COMMAND("delbot", global_cmd_delbot, 1, 1000, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_OPLOG); + OPER_COMMAND("reconnect", global_cmd_reconnect, 0, 1000, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_OPLOG); + OPER_COMMAND("modcmd", global_cmd_modcmd, 1, 900, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_OPLOG | CMDFLAG_REQUIRED | CMDFLAG_ESCAPE_ARGS); + OPER_COMMAND("meminfo", global_cmd_meminfo, 0, 1000, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH); + OPER_COMMAND("global", global_cmd_global, 1, 1000, CMDFLAG_REQUIRE_AUTH | CMDFLAG_CHECK_AUTH | CMDFLAG_OPLOG); + #undef OPER_COMMAND +} \ No newline at end of file diff --git a/src/modules/global.mod/cmd_global.h b/src/modules/global.mod/cmd_global.h new file mode 100644 index 0000000..38421a9 --- /dev/null +++ b/src/modules/global.mod/cmd_global.h @@ -0,0 +1,76 @@ +/* cmd_global.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _cmd_global_h +#define _cmd_global_h +#include "../module.h" +#include "../botid.h" +#include "../../main.h" +#include "../../modcmd.h" +#include "../../IRCParser.h" +#include "../../IRCEvents.h" +#include "../../UserNode.h" +#include "../../ChanNode.h" +#include "../../ChanUser.h" +#include "../../ModeNode.h" +#include "../../BanNode.h" +#include "../../ClientSocket.h" +#include "../../mysqlConn.h" +#include "../../lang.h" +#include "../../HandleInfoHandler.h" +#include "../../WHOHandler.h" +#include "../../DBHelper.h" +#include "../../tools.h" +#include "../../timeq.h" +#include "../../version.h" +#include "../../EventLogger.h" +#include "../../bots.h" + +void register_commands(); + +CMD_BIND(global_cmd_addbot); +CMD_BIND(global_cmd_bind); +CMD_BIND(global_cmd_bots); +CMD_BIND(global_cmd_command); +CMD_BIND(global_cmd_commands); +CMD_BIND(global_cmd_delbot); +CMD_BIND(global_cmd_die); +CMD_BIND(global_cmd_emote); +CMD_BIND(global_cmd_extscript); +CMD_BIND(global_cmd_global); +CMD_BIND(global_cmd_god); +CMD_BIND(global_cmd_meminfo); +CMD_BIND(global_cmd_modcmd); +CMD_BIND(global_cmd_motd); +CMD_BIND(global_cmd_move); +CMD_BIND(global_cmd_netinfo); +CMD_BIND(global_cmd_notice); +CMD_BIND(global_cmd_raw); +CMD_BIND(global_cmd_reconnect); +CMD_BIND(global_cmd_register); +CMD_BIND(global_cmd_reload); +CMD_BIND(global_cmd_restart); +CMD_BIND(global_cmd_reloadlang); +CMD_BIND(global_cmd_say); +CMD_BIND(global_cmd_setaccess); +CMD_BIND(global_cmd_setbot); +CMD_BIND(global_cmd_staff); +CMD_BIND(global_cmd_unbind); +CMD_BIND(global_cmd_unregister); +CMD_BIND(global_cmd_version); + + +#endif \ No newline at end of file diff --git a/src/modules/global.mod/cmd_global_addbot.c b/src/modules/global.mod/cmd_global_addbot.c new file mode 100644 index 0000000..626d895 --- /dev/null +++ b/src/modules/global.mod/cmd_global_addbot.c @@ -0,0 +1,43 @@ +/* cmd_global_addbot.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_global.h" + +/* +* argv[0] nick +* argv[1] class +*/ + +CMD_BIND(global_cmd_addbot) { + MYSQL_RES *res; + int botid; + if((botid = resolve_botalias(argv[1])) == -1) { + reply(textclient, user, "NS_SETBOT_INVALID_CLASS", argv[1]); + return; + } + printf_mysql_query("SELECT `id` FROM `bots` WHERE `nick` = '%s'", escape_string(argv[0])); + res = mysql_use(); + if(mysql_fetch_row(res)) { + reply(textclient, user, "NS_ADDBOT_EXISTING", argv[0]); + return; + } + printf_mysql_query("INSERT INTO `bots` (`nick`, `botclass`) VALUES ('%s', '%d')", escape_string(argv[0]), botid); + botid = (int) mysql_insert_id(get_mysql_conn()); + reply(textclient, user, "NS_ADDBOT_DONE", argv[0], botid); + logEvent(event); +} + diff --git a/src/modules/global.mod/cmd_global_bind.c b/src/modules/global.mod/cmd_global_bind.c new file mode 100644 index 0000000..4ee47a2 --- /dev/null +++ b/src/modules/global.mod/cmd_global_bind.c @@ -0,0 +1,54 @@ +/* cmd_global_bind.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_global.h" + +/* +* argv[0] command name +* argv[1] command function +* argv[2-*] parameters (optional) +*/ + +CMD_BIND(global_cmd_bind) { + MYSQL_RES *res; + MYSQL_ROW row; + if(client->botid == 0) + printf_mysql_query("SELECT `function` FROM `bot_binds` WHERE `botclass` = '%d' AND `botid` = '%d' AND `command` = '%s'", client->botid, client->clientid, escape_string(argv[0])); + else + printf_mysql_query("SELECT `function` FROM `bot_binds` WHERE `botclass` = '%d' AND `command` = '%s'", client->botid, escape_string(argv[0])); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + reply(textclient, user, "NS_BIND_ALREADY", argv[0], row[0]); + return; + } + char *params; + if(argc > 2) + params = merge_argv(argv, 2, argc); + else + params = ""; + struct cmd_function *function = find_cmd_function(client->botid, argv[1]); + if(!function) { + reply(textclient, user, "NS_BIND_UNKNOWN", argv[1]); + return; + } + bind_botwise_cmd_to_function(client->botid, client->clientid, argv[0], function); + printf_mysql_query("INSERT INTO `bot_binds` (`botclass`, `botid`, `command`, `function`, `parameters`) VALUES ('%d', '%d', '%s', '%s', '%s')", client->botid, (client->botid == 0 ? client->clientid : 0), escape_string(argv[0]), escape_string(argv[1]), params); + if(*params) + bind_botwise_set_parameters(client->botid, client->clientid, argv[0], params); + reply(textclient, user, "NS_BIND_DONE", argv[0], function->name); + logEvent(event); +} diff --git a/src/modules/global.mod/cmd_global_bots.c b/src/modules/global.mod/cmd_global_bots.c new file mode 100644 index 0000000..9887b7c --- /dev/null +++ b/src/modules/global.mod/cmd_global_bots.c @@ -0,0 +1,75 @@ +/* cmd_global_bots.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_global.h" + +/* +* argv[0] (optional) class name +*/ + +CMD_BIND(global_cmd_bots) { + struct Table *table; + MYSQL_RES *res, *res2; + MYSQL_ROW row, row2; + printf_mysql_query("SELECT `active`, `nick`, `server`, `port`, `pass`, `botclass`, `textbot`, `queue`, `defaulttrigger`, `max_channels`, `register_priority`, `id`, `secret` FROM `bots`"); + res = mysql_use(); + table = table_init(7, mysql_num_rows(res) + 1, 0); + char *content[7]; + content[0] = get_language_string(user, "NS_BOTS_ID"); + content[1] = get_language_string(user, "NS_BOTS_NICK"); + content[2] = get_language_string(user, "NS_BOTS_SERVER"); + content[3] = get_language_string(user, "NS_BOTS_CLASS"); + content[4] = get_language_string(user, "NS_BOTS_FLAGS"); + content[5] = get_language_string(user, "NS_BOTS_CHANNELS"); + content[6] = get_language_string(user, "NS_BOTS_TRIGGER"); + table_add(table, content); + char botnick[NICKLEN + 3]; + char botserver[MAXLEN]; + char botflags[10]; + int flagspos; + char botchans[20]; + while ((row = mysql_fetch_row(res)) != NULL) { + content[0] = row[11]; + sprintf(botnick, (strcmp(row[0], "0") ? "%s" : "!%s"), row[1]); + content[1] = botnick; + sprintf(botserver, (strcmp(row[4], "") ? "%s:%s:*" : "%s:%s"), row[2], row[3]); + content[2] = botserver; + content[3] = (char *) resolve_botid(atoi(row[5])); + flagspos = 0; + if(!strcmp(row[6], "1")) + botflags[flagspos++] = 't'; + if(!strcmp(row[7], "1")) + botflags[flagspos++] = 'q'; + if(!strcmp(row[12], "1")) + botflags[flagspos++] = 's'; + botflags[flagspos] = '\0'; + content[4] = botflags; + printf_mysql_query("SELECT COUNT(*) FROM `bot_channels` WHERE `botid` = '%s'", row[11]); + res2 = mysql_use(); + row2 = mysql_fetch_row(res2); + sprintf(botchans, "%s/%s", row2[0], row[9]); + content[5] = botchans; + content[6] = row[8]; + table_add(table, content); + } + char **table_lines = table_end(table); + int i; + for(i = 0; i < table->entrys; i++) { + reply(textclient, user, table_lines[i]); + } + table_free(table); +} \ No newline at end of file diff --git a/src/modules/global.mod/cmd_global_command.c b/src/modules/global.mod/cmd_global_command.c new file mode 100644 index 0000000..81beee9 --- /dev/null +++ b/src/modules/global.mod/cmd_global_command.c @@ -0,0 +1,179 @@ +/* cmd_global_command.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_global.h" + +/* +* argv[0-1] command +*/ +static int global_cmd_command_chanaccess(struct cmd_binding *cbind, struct ChanNode *chan); +static int global_cmd_command_operaccess(struct cmd_binding *cbind); + +CMD_BIND(global_cmd_command) { + char *ident; + MYSQL_RES *res; + MYSQL_ROW row; + struct cmd_binding *cbind = find_botwise_cmd_binding(client->botid, client->clientid, argv[0]); + if (!cbind) { + reply(textclient, user, "NS_UNBIND_NOT_FOUND", argv[0]); + return; + } + ident = argv[0]; + char parameters[MAXLEN]; + if(cbind->paramcount) { + int i, parampos = 0; + for(i = 0; i < cbind->paramcount; i++) { + parampos += sprintf(parameters + parampos, (i ? " %s" : "%s"), cbind->parameters[i]); + } + } else + parameters[0] = '\0'; + reply(textclient, user, "NS_COMMAND_BINDING", cbind->cmd, cbind->func->name, parameters); + if(chan) + reply(textclient, user, "NS_COMMAND_ACCESS", global_cmd_command_chanaccess(cbind, chan), global_cmd_command_operaccess(cbind)); + printf_mysql_query("SELECT `user_lang` FROM `users` WHERE `user_user` = '%s'", escape_string(user->auth)); + res = mysql_use(); + char *lang; + if ((row = mysql_fetch_row(res)) != NULL) + lang = row[0]; + else + lang = "en"; + printf_mysql_query("SELECT `text` FROM `help` WHERE `lang` = '%s' AND `ident` = '%s'", escape_string(lang), escape_string(ident)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) == NULL) { + if(stricmp(lang, "en")) { + printf_mysql_query("SELECT `text` FROM `help` WHERE `lang` = 'en' AND `ident` = '%s'", escape_string(ident)); + res = mysql_use(); + } + if ((row = mysql_fetch_row(res)) == NULL) { + printf_mysql_query("SELECT `text` FROM `help` WHERE `lang` = '%s' AND `ident` = '%s'", escape_string(lang), escape_string(cbind->func->name)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) == NULL) { + if(stricmp(lang, "en")) { + printf_mysql_query("SELECT `text` FROM `help` WHERE `lang` = 'en' AND `ident` = '%s'", escape_string(cbind->func->name)); + res = mysql_use(); + } + if ((row = mysql_fetch_row(res)) == NULL) { + return; + } + } + } + } + char sendBuf[MAXLEN]; + int sendBufPos = 0; + int i; + for(i = 0; i < strlen(row[0]); i++) { + switch(row[0][i]) { + case '\n': + if(sendBufPos) { + sendBuf[sendBufPos] = '\0'; + reply(textclient, user, "%s", sendBuf); + sendBufPos = 0; + } + break; + case '$': + switch(row[0][i+1]) { + case 'b': + sendBuf[sendBufPos++] = '\002'; + i++; + break; + case 'k': + sendBuf[sendBufPos++] = '\003'; + i++; + break; + case 'u': + sendBuf[sendBufPos++] = '\031'; + i++; + break; + case 'C': + case 'S': + sendBufPos += sprintf(sendBuf + sendBufPos, "%s", client->user->nick); + i++; + break; + default: + sendBuf[sendBufPos++] = '$'; + break; + } + break; + default: + sendBuf[sendBufPos++] = row[0][i]; + break; + } + } + if(sendBufPos) { + sendBuf[sendBufPos] = '\0'; + reply(textclient, user, "%s", sendBuf); + sendBufPos = 0; + } +} + +static int global_cmd_command_chanaccess(struct cmd_binding *cbind, struct ChanNode *chan) { + char access_list[256]; + int access_pos = 0; + int access_count = 0; + int minaccess = 0; + char *str_a, *str_b = cbind->func->channel_access, *str_c; + if(cbind->flags & CMDFLAG_OVERRIDE_CHANNEL_ACCESS) + str_b = cbind->channel_access; + access_list[0] = '\0'; + if(str_b) { + str_c = strdup(str_b); + str_b = str_c; + while((str_a = str_b)) { + str_b = strstr(str_a, ","); + if(str_b) { + *str_b = '\0'; + str_b++; + } + if(*str_a == '@' || *str_a == '+') { + //privs can override this access requirement + str_a++; + } + if(*str_a == '#') { + str_a++; + access_pos += sprintf(access_list+access_pos, (access_pos ? ", `%s`" : "`%s`"), str_a); + access_count++; + } else { + if(atoi(str_a) > minaccess) + minaccess = atoi(str_a); + } + } + free(str_c); + } + if(access_count) { + MYSQL_RES *res; + MYSQL_ROW row, defaults = NULL; + printf_mysql_query("SELECT %s FROM `channels` WHERE `channel_name` = '%s'", access_list, escape_string(chan->name)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + int i, caccess; + for(i = 0; i < access_count; i++) { + if(!row[i] && !defaults) { + printf_mysql_query("SELECT %s FROM `channels` WHERE `channel_name` = 'defaults'", access_list); + defaults = mysql_fetch_row(mysql_use()); + } + caccess = (row[i] ? atoi(row[i]) : atoi(defaults[i])); + if(caccess > minaccess) + minaccess = caccess; + } + } + } + return minaccess; +} + +static int global_cmd_command_operaccess(struct cmd_binding *cbind) { + return ((cbind->flags & CMDFLAG_OVERRIDE_GLOBAL_ACCESS) ? cbind->global_access : cbind->func->global_access); +} diff --git a/src/modules/global.mod/cmd_global_commands.c b/src/modules/global.mod/cmd_global_commands.c new file mode 100644 index 0000000..fa5f26d --- /dev/null +++ b/src/modules/global.mod/cmd_global_commands.c @@ -0,0 +1,153 @@ +/* cmd_global_commands.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_global.h" + +/* +* argv[0] mask +*/ + +static int global_cmd_commands_sort(const void *a, const void *b); +static int global_cmd_commands_chanaccess(struct cmd_binding *cbind, struct ChanNode *chan); +static int global_cmd_commands_operaccess(struct cmd_binding *cbind); + +CMD_BIND(global_cmd_commands) { + struct cmd_binding *cbind; + int bindcount = 0; + for(cbind = getAllBinds(NULL); cbind; cbind = getAllBinds(cbind)) { + if(cbind->botid == client->botid && (cbind->botid || cbind->clientid == client->clientid) && !(cbind->func->flags & CMDFLAG_FUNCMD)) + bindcount++; + } + struct cmd_binding *binds[bindcount]; + bindcount = 0; + for(cbind = getAllBinds(NULL); cbind; cbind = getAllBinds(cbind)) { + if(cbind->botid == client->botid && (cbind->botid || cbind->clientid == client->clientid) && !(cbind->func->flags & CMDFLAG_FUNCMD)) + binds[bindcount++] = cbind; + } + qsort(binds, bindcount, sizeof(struct cmd_binding *), global_cmd_commands_sort); + int i; + struct Table *table; + table = table_init(5, bindcount + 1, 0); + char *content[5]; + content[0] = get_language_string(user, "NS_COMMANDS_NAME"); + content[1] = get_language_string(user, "NS_COMMANDS_ACCESS"); + content[2] = get_language_string(user, "NS_COMMANDS_GACCESS"); + content[3] = get_language_string(user, "NS_COMMANDS_TRIGGERED"); + content[4] = get_language_string(user, "NS_COMMANDS_FUNCTION"); + table_add(table, content); + char caccess[5]; + char gaccess[5]; + char triggered[10]; + char funcname[MAXLEN]; + int funcpos; + for(i = 0; i < bindcount; i++) { + cbind = binds[i]; + content[0] = cbind->cmd; + sprintf(caccess, "%d", global_cmd_commands_chanaccess(cbind, chan)); + content[1] = caccess; + sprintf(gaccess, "%d", global_cmd_commands_operaccess(cbind)); + content[2] = gaccess; + sprintf(triggered, "%d", cbind->triggered); + content[3] = triggered; + funcpos = sprintf(funcname, "%s", cbind->func->name); + if(cbind->paramcount) { + int j; + for(j = 0; j < cbind->paramcount; j++) { + funcpos += sprintf(funcname + funcpos, " %s", cbind->parameters[j]); + } + } + content[4] = funcname; + table_add(table, content); + } + //send the table + char **table_lines = table_end(table); + for(i = 0; i < table->entrys; i++) { + reply(textclient, user, table_lines[i]); + } + table_free(table); +} + +static int global_cmd_commands_sort(const void *a, const void *b) { + const struct cmd_binding *bind_a = *((struct cmd_binding * const *) a); + const struct cmd_binding *bind_b = *((struct cmd_binding * const *) b); + int i = stricmp(bind_a->func->name, bind_b->func->name); + if(i == 0) { + return stricmp(bind_a->cmd, bind_b->cmd); + } else + return i; +} + +static int global_cmd_commands_chanaccess(struct cmd_binding *cbind, struct ChanNode *chan) { + char access_list[256]; + int access_pos = 0; + int access_count = 0; + int minaccess = 0; + char *str_a, *str_b = cbind->func->channel_access, *str_c; + if(cbind->flags & CMDFLAG_OVERRIDE_CHANNEL_ACCESS) + str_b = cbind->channel_access; + access_list[0] = '\0'; + if(str_b) { + str_c = strdup(str_b); + str_b = str_c; + while((str_a = str_b)) { + str_b = strstr(str_a, ","); + if(str_b) { + *str_b = '\0'; + str_b++; + } + if(*str_a == '@' || *str_a == '+') { + //privs can override this access requirement + str_a++; + } + if(*str_a == '#') { + str_a++; + access_pos += sprintf(access_list+access_pos, (access_pos ? ", `%s`" : "`%s`"), str_a); + access_count++; + } else { + if(atoi(str_a) > minaccess) + minaccess = atoi(str_a); + } + } + free(str_c); + } + if(access_count) { + if(!chan) { + return -1; + } + MYSQL_RES *res; + MYSQL_ROW row, defaults = NULL; + printf_mysql_query("SELECT %s FROM `channels` WHERE `channel_name` = '%s'", access_list, escape_string(chan->name)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + int i, caccess; + for(i = 0; i < access_count; i++) { + if(!row[i] && !defaults) { + printf_mysql_query("SELECT %s FROM `channels` WHERE `channel_name` = 'defaults'", access_list); + defaults = mysql_fetch_row(mysql_use()); + } + caccess = (row[i] ? atoi(row[i]) : atoi(defaults[i])); + if(caccess > minaccess) + minaccess = caccess; + } + } + } + return minaccess; +} + +static int global_cmd_commands_operaccess(struct cmd_binding *cbind) { + return ((cbind->flags & CMDFLAG_OVERRIDE_GLOBAL_ACCESS) ? cbind->global_access : cbind->func->global_access); +} diff --git a/src/modules/global.mod/cmd_global_delbot.c b/src/modules/global.mod/cmd_global_delbot.c new file mode 100644 index 0000000..101c599 --- /dev/null +++ b/src/modules/global.mod/cmd_global_delbot.c @@ -0,0 +1,45 @@ +/* cmd_global_delbot.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_global.h" + +/* +* argv[0] nick/botid +*/ + +CMD_BIND(global_cmd_delbot) { + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `id` FROM `bots` WHERE `nick` = '%s' OR `id` = '%s'", escape_string(argv[0]), escape_string(argv[0])); + res = mysql_use(); + if((row = mysql_fetch_row(res)) == NULL) { + reply(textclient, user, "NS_DELBOT_NOT_FOUND", argv[0]); + return; + } + int botid = atoi(row[0]); + printf_mysql_query("DELETE FROM `bots` WHERE `id` = '%s'", row[0]); + printf_mysql_query("DELETE FROM `bot_binds` WHERE `botid` = '%s'", row[0]); + printf_mysql_query("DELETE FROM `bot_channels` WHERE `botid` = '%s'", row[0]); + for(client = getBots(0, NULL); client; client = getBots(0, client)) { + if(client->clientid == botid) { + close_socket(client); + break; + } + } + reply(textclient, user, "NS_DELBOT_DONE"); + logEvent(event); +} diff --git a/src/modules/global.mod/cmd_global_die.c b/src/modules/global.mod/cmd_global_die.c new file mode 100644 index 0000000..c8da2d1 --- /dev/null +++ b/src/modules/global.mod/cmd_global_die.c @@ -0,0 +1,27 @@ +/* cmd_global_die.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_global.h" + +/* +* no args +*/ + +CMD_BIND(global_cmd_die) { + //hard work! :D + stop_bot(); +} diff --git a/src/modules/global.mod/cmd_global_emote.c b/src/modules/global.mod/cmd_global_emote.c new file mode 100644 index 0000000..a0c5c90 --- /dev/null +++ b/src/modules/global.mod/cmd_global_emote.c @@ -0,0 +1,28 @@ +/* cmd_global_emote.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_global.h" + +/* +* argv[0] target +* argv[1-*] message +*/ + +CMD_BIND(global_cmd_emote) { + char *message = merge_argv(argv, 1, argc); + putsock(client, "PRIVMSG %s :\001ACTION %s\001", argv[0], message); +} \ No newline at end of file diff --git a/src/modules/global.mod/cmd_global_extscript.c b/src/modules/global.mod/cmd_global_extscript.c new file mode 100644 index 0000000..74cde9d --- /dev/null +++ b/src/modules/global.mod/cmd_global_extscript.c @@ -0,0 +1,170 @@ +/* cmd_global_extscript.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_global.h" +#include + +/* +* argv[0] 'toys' if it's a toy command (check if toys are enabled) +* argv[0-*] script name & parameter patterns +* argv[argc-1] all arguments passed to the command +*/ + +static TIMEQ_CALLBACK(global_cmd_extscript_callback); + +struct global_cmd_extscript_cache { + struct ClientSocket *client, *textclient; + struct Event *event; + struct UserNode *user; + struct ChanNode *chan; + int answere_channel; + FILE *pipe; +}; + +CMD_BIND(global_cmd_extscript) { + int i, j; + char *args[MAXNUMPARAMS]; + int argpos = 0; + char *next, *curr; + char command[1024]; + int commandpos = 0; + char part[MAXLEN]; + int partpos; + int escape_param; + int answere_channel = 0; + //check first arg + if(argc && !stricmp(argv[0], "toys")) { + if(!chan) return; //toys are not allowed via query + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `channel_toys` FROM `channels` WHERE `channel_name` = '%s'", escape_string(chan->name)); + res = mysql_use(); + row = mysql_fetch_row(res); + if(!row || !strcmp(row[0], "0")) { + //disabled + reply(textclient, user, "NS_FUN_DISABLED", chan->name); + return; + } else if(!strcmp(row[0], "2")) + answere_channel = 1; + argc--; + argv++; + } + //parse arguments + if(argc < 2) return; + curr = argv[argc-1]; + while(curr) { + next = strstr(curr, " "); + args[argpos++] = curr; + if(next) { + *next = '\0'; + curr = next+1; + } else + curr = NULL; + } + //parse command pattern and build command + commandpos = sprintf(command, "%s", argv[0]); + for(i = 1; i < argc-1; i++) { + partpos = 0; + escape_param = 1; + if(argv[i][0] == '$') { + argv[i]++; + if(argv[i][strlen(argv[i])-1] == '-') { + argv[i][strlen(argv[i])-1] = '\0'; + j = atoi(argv[i]); + if(j <= argpos) + partpos = sprintf(part, "%s", merge_argv(args, j-1, argpos)); + } else if((j = atoi(argv[i])) > 0) { + if(j <= argpos) + partpos = sprintf(part, "%s", args[j-1]); + } else if(!strcmp(argv[i], "c")) { + partpos = sprintf(part, "%s", (chan ? chan->name : "")); + } else if(!strcmp(argv[i], "n")) { + partpos = sprintf(part, "%s", user->nick); + } else if(!strcmp(argv[i], "a")) { + partpos = sprintf(part, "%s", ((user->flags & USERFLAG_ISAUTHED) ? user->auth : "")); + } else if(!strcmp(argv[i], "access")) { + if(chan) + partpos = sprintf(part, "%d", getChannelAccess(user, chan)); + } + } else { + partpos = sprintf(part, "%s", argv[i]); + escape_param = 0; + } + //escape shell argument + command[commandpos++] = ' '; + if(escape_param) { + command[commandpos++] = '\''; + for(j = 0; j < partpos; j++) { + if(part[j] == '\'') { + command[commandpos++] = '\''; + command[commandpos++] = '\\'; + command[commandpos++] = '\''; + command[commandpos++] = '\''; + } else + command[commandpos++] = part[j]; + } + command[commandpos++] = '\''; + } else + commandpos += sprintf(command + commandpos, " %s", part); + } + command[commandpos] = '\0'; + //we should now have a valid command + + struct global_cmd_extscript_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("global", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->event = event; + cache->user = user; + cache->chan = chan; + cache->answere_channel = answere_channel; + cache->pipe = popen(command, "r"); + #ifndef WIN32 + fcntl(fileno(cache->pipe), F_SETFL, O_NONBLOCK); + #endif + timeq_uadd(200, module_id, global_cmd_extscript_callback, cache); +} + +static TIMEQ_CALLBACK(global_cmd_extscript_callback) { + struct global_cmd_extscript_cache *cache = data; + char command[512]; + char *a; + if(feof(cache->pipe)) { + pclose(cache->pipe); + free(cache); + return; + } + while (fgets(command, 512, cache->pipe) != NULL) { + if((a = strchr(command, '\n'))) + *a = '\0'; + if(!stricmp(command, "/log")) { + logEvent(cache->event); + continue; + } + if(cache->answere_channel) + putsock(cache->client, "PRIVMSG %s :%s", cache->chan->name, command); + else + reply(cache->textclient, cache->user, "%s", command); + } + timeq_uadd(200, module_id, global_cmd_extscript_callback, cache); +} + + + diff --git a/src/modules/global.mod/cmd_global_global.c b/src/modules/global.mod/cmd_global_global.c new file mode 100644 index 0000000..1401d29 --- /dev/null +++ b/src/modules/global.mod/cmd_global_global.c @@ -0,0 +1,36 @@ +/* cmd_global_global.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_global.h" + +/* +* argv[0-*] message +*/ + +CMD_BIND(global_cmd_global) { + char *message = merge_argv(argv, 0, argc); + //send message to all channels + struct ClientSocket *bot; + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(bot->botid == client->botid && (bot->botid || bot->clientid == client->clientid)) { + struct ChanUser *chanuser; + for(chanuser = getUserChannels(bot->user, NULL); chanuser; chanuser = getUserChannels(bot->user, chanuser)) { + putsock(client, "PRIVMSG %s :%s", chanuser->chan->name, message); + } + } + } +} \ No newline at end of file diff --git a/src/modules/global.mod/cmd_global_god.c b/src/modules/global.mod/cmd_global_god.c new file mode 100644 index 0000000..bab895c --- /dev/null +++ b/src/modules/global.mod/cmd_global_god.c @@ -0,0 +1,53 @@ +/* cmd_global_god.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_global.h" + +/* +* argv[0] (optional) on/off +*/ + +CMD_BIND(global_cmd_god) { + if(argc > 0) { + if(!strcmp(argv[0], "0") || !stricmp(argv[0], "off") || !stricmp(argv[0], get_language_string(user, "NS_SET_OFF"))) { + if(isGodMode(user)) { + printf_mysql_query("UPDATE `users` SET `user_god` = '0' WHERE `user_user` = '%s'", escape_string(user->auth)); + user->flags &= ~USERFLAG_GOD_MODE; + } + reply(textclient, user, "NS_GOD_OFF"); + } else if(!strcmp(argv[0], "1") || !stricmp(argv[0], "on") || !stricmp(argv[0], get_language_string(user, "NS_SET_ON"))) { + if(!isGodMode(user)) { + printf_mysql_query("UPDATE `users` SET `user_god` = '1' WHERE `user_user` = '%s'", escape_string(user->auth)); + user->flags |= USERFLAG_GOD_MODE; + } + reply(textclient, user, "NS_GOD_ON"); + } else { + reply(textclient, user, "NS_SET_INVALID_BOOLEAN", argv[0]); + return; + } + } else { + if(isGodMode(user)) { + printf_mysql_query("UPDATE `users` SET `user_god` = '0' WHERE `user_user` = '%s'", escape_string(user->auth)); + user->flags &= ~USERFLAG_GOD_MODE; + reply(textclient, user, "NS_GOD_OFF"); + } else { + printf_mysql_query("UPDATE `users` SET `user_god` = '1' WHERE `user_user` = '%s'", escape_string(user->auth)); + user->flags |= USERFLAG_GOD_MODE; + reply(textclient, user, "NS_GOD_ON"); + } + } +} \ No newline at end of file diff --git a/src/modules/global.mod/cmd_global_meminfo.c b/src/modules/global.mod/cmd_global_meminfo.c new file mode 100644 index 0000000..2685798 --- /dev/null +++ b/src/modules/global.mod/cmd_global_meminfo.c @@ -0,0 +1,118 @@ +/* cmd_global_meminfo.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_global.h" +#include "../../memoryInfo.h" + +/* +* no arguments +*/ + +CMD_BIND(global_cmd_meminfo) { + #ifndef ENABLE_MEMORY_DEBUG + reply(textclient, user, "NS_MEMINFO_DISABLED"); + #else + if(argc > 0) { + struct Table *table; + int elementcount = 0; + struct memoryInfoLines *element, *elements; + elements = getMemoryInfoLines(argv[0]); + for(element = elements; element; element = element->next) { + elementcount++; + } + table = table_init(4, elementcount+2, 0); + char *content[4]; + content[0] = get_language_string(user, "NS_MEMINFO_LINE"); + content[1] = get_language_string(user, "NS_MEMINFO_COUNT"); + content[2] = get_language_string(user, "NS_MEMINFO_SIZE"); + content[3] = get_language_string(user, "NS_MEMINFO_TOTAL"); + table_add(table, content); + char lineBuf[20]; + char countBuf[20]; + char sizeBuf[20]; + char totalBuf[50]; + unsigned int total_allocations = 0; + unsigned int total_allocated = 0; + for(element = elements; element; element = element->next) { + sprintf(lineBuf, "%u", element->line); + content[0] = lineBuf; + sprintf(countBuf, "%u", element->allocations); + total_allocations += element->allocations; + content[1] = countBuf; + sprintf(sizeBuf, "%uB", element->allocate); + content[2] = sizeBuf; + sprintf(totalBuf, "%u (%.2f kB)", (element->allocate * element->allocations), ((float)(element->allocate * element->allocations) / 1024)); + total_allocated += (element->allocate * element->allocations); + content[3] = totalBuf; + table_add(table, content); + } + content[0] = "Total"; + sprintf(countBuf, "%u", total_allocations); + content[1] = countBuf; + content[2] = "*"; + sprintf(sizeBuf, "%u (%.2f kB)", total_allocated, ((float)total_allocated / 1024)); + content[3] = sizeBuf; + table_add(table, content); + char **table_lines = table_end(table); + int i; + for(i = 0; i < table->entrys; i++) { + reply(textclient, user, table_lines[i]); + } + table_free(table); + } else { + struct Table *table; + int elementcount = 0; + struct memoryInfoFiles *element, *elements; + elements = getMemoryInfoFiles(); + for(element = elements; element; element = element->next) { + elementcount++; + } + table = table_init(3, elementcount+2, 0); + char *content[3]; + content[0] = get_language_string(user, "NS_MEMINFO_NAME"); + content[1] = get_language_string(user, "NS_MEMINFO_COUNT"); + content[2] = get_language_string(user, "NS_MEMINFO_SIZE"); + table_add(table, content); + char countBuf[20]; + char sizeBuf[50]; + unsigned int total_allocations = 0; + unsigned int total_allocated = 0; + for(element = elements; element; element = element->next) { + content[0] = element->filename; + sprintf(countBuf, "%u", element->allocations); + total_allocations += element->allocations; + content[1] = countBuf; + sprintf(sizeBuf, "%u (%.2f kB)", element->allocated, ((float)element->allocated / 1024)); + total_allocated += element->allocated; + content[2] = sizeBuf; + table_add(table, content); + } + content[0] = "Total"; + sprintf(countBuf, "%u", total_allocations); + content[1] = countBuf; + sprintf(sizeBuf, "%u (%.2f kB)", total_allocated, ((float)total_allocated / 1024)); + content[2] = sizeBuf; + table_add(table, content); + char **table_lines = table_end(table); + int i; + for(i = 0; i < table->entrys; i++) { + reply(textclient, user, table_lines[i]); + } + table_free(table); + } + #endif +} \ No newline at end of file diff --git a/src/modules/global.mod/cmd_global_modcmd.c b/src/modules/global.mod/cmd_global_modcmd.c new file mode 100644 index 0000000..c1066ec --- /dev/null +++ b/src/modules/global.mod/cmd_global_modcmd.c @@ -0,0 +1,223 @@ +/* cmd_global_modcmd.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_global.h" + +/* +* argv[0] command +* argv[1] setting +* argv[2] value +*/ + +static int global_cmd_modcmd_params(struct ClientSocket *textclient, struct UserNode *user, struct cmd_binding *cbind, char *value); +static int global_cmd_modcmd_flags(struct ClientSocket *textclient, struct UserNode *user, struct cmd_binding *cbind, char *value); +static int global_cmd_modcmd_caccess(struct ClientSocket *textclient, struct UserNode *user, struct cmd_binding *cbind, char *value); +static int global_cmd_modcmd_oaccess(struct ClientSocket *textclient, struct UserNode *user, struct cmd_binding *cbind, char *value); + +CMD_BIND(global_cmd_modcmd) { + MYSQL_RES *res; + MYSQL_ROW row; + struct cmd_binding *cbind = find_botwise_cmd_binding(client->botid, client->clientid, argv[0]); + if (!cbind) { + reply(textclient, user, "NS_UNBIND_NOT_FOUND", argv[0]); + return; + } + int uaccess = 0; + printf_mysql_query("SELECT `user_access` FROM `users` WHERE `user_user` = '%s'", escape_string(user->auth)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + uaccess = atoi(row[0]); + } + int gaccess = ((cbind->flags & CMDFLAG_OVERRIDE_GLOBAL_ACCESS) ? cbind->global_access : cbind->func->global_access); + if(gaccess > uaccess) { + reply(textclient, user, "NS_MODCMD_OUTRANKED", cbind->cmd, gaccess); + return; + } + if(argc > 1) { + char *value; + if(argc > 2) { + value = merge_argv(argv, 2, argc); + } else + value = NULL; + int log_event = 0; + if(!stricmp(argv[1], "caccess")) log_event = global_cmd_modcmd_caccess(textclient, user, cbind, value); + else if(!stricmp(argv[1], "oaccess")) log_event = global_cmd_modcmd_oaccess(textclient, user, cbind, value); + else if(!stricmp(argv[1], "parameters")) log_event = global_cmd_modcmd_params(textclient, user, cbind, value); + else if(!stricmp(argv[1], "flags")) log_event = global_cmd_modcmd_flags(textclient, user, cbind, value); + else { + reply(textclient, user, "NS_MODCMD_SETTING", argv[1]); + } + if(log_event) { + logEvent(event); + } + } else { + reply(textclient, user, "NS_MODCMD_HEADER", cbind->cmd); + global_cmd_modcmd_params(textclient, user, cbind, NULL); + global_cmd_modcmd_caccess(textclient, user, cbind, NULL); + global_cmd_modcmd_oaccess(textclient, user, cbind, NULL); + global_cmd_modcmd_flags(textclient, user, cbind, NULL); + } +} + +static int global_cmd_modcmd_params(struct ClientSocket *textclient, struct UserNode *user, struct cmd_binding *cbind, char *value) { + char parameters[MAXLEN]; + int ret = 0; + if(cbind->paramcount) { + int i, parampos = 0; + for(i = 0; i < cbind->paramcount; i++) { + parampos += sprintf(parameters + parampos, (i ? " %s" : "%s"), cbind->parameters[i]); + } + } else + parameters[0] = '\0'; + if(value) { + if(!strcmp(value, "*")) + value = NULL; + bind_botwise_set_parameters(cbind->botid, cbind->clientid, cbind->cmd, value); + if(cbind->botid == 0) + printf_mysql_query("UPDATE `bot_binds` SET `parameters` = '%s' WHERE `botclass` = '0' AND `botid` = '%d' AND `command` = '%s'", (value ? escape_string(value) : ""), cbind->clientid, escape_string(cbind->cmd)); + else + printf_mysql_query("UPDATE `bot_binds` SET `parameters` = '%s' WHERE `botclass` = '%d' AND `command` = '%s'", (value ? escape_string(value) : ""), cbind->botid, escape_string(cbind->cmd)); + strcpy(parameters, (value ? value : "")); + ret = 1; + } + reply(textclient, user, "\002PARAMETERS \002 %s", parameters); + return ret; +} + +static const struct { + const char *name; + unsigned int flag; +} global_cmd_modcmd_show_flags[] = { + {"REQUIRE_CHAN", CMDFLAG_REQUIRE_CHAN}, //The command requires a valid channel (at least one bot in it) + {"REQUIRE_AUTH", CMDFLAG_REQUIRE_AUTH}, //The command requires the user to be authenticated + {"REQUIRE_GOD", CMDFLAG_REQUIRE_GOD}, //The command requires the user to have security override enabled + {"REQUIRE_REGISTERED", CMDFLAG_REGISTERED_CHAN}, //The command requires the channel to be registered (database entry) + {"CHECK_AUTH", CMDFLAG_CHECK_AUTH}, //WHO the user if no auth is known, yet + {"CHANNEL_ARGS", CMDFLAG_CHAN_PARAM}, //don't interpret channel arguments as channel - just pass them as a string + {"LOG", CMDFLAG_LOG}, + {"OPLOG", CMDFLAG_OPLOG}, + {"FUNCMD", CMDFLAG_FUNCMD}, + {"ESCAPED_ARGS", CMDFLAG_ESCAPE_ARGS}, //allows arguments to be escaped ("a\ b" = "a b" as one argument) + {"NO_CROSSCHAN", CMDFLAG_NO_CROSSCHAN}, + {"SUB_LINKER", CMDFLAG_SUB_LINKER}, //adds a "quiet" subcommand linker with the binding function as default + {NULL, 0} +}; + +static int global_cmd_modcmd_flags(struct ClientSocket *textclient, struct UserNode *user, struct cmd_binding *cbind, char *value) { + char flags[MAXLEN]; + int flagpos = 0; + int ret = 0; + unsigned int visible_flags = 0; + int i = 0; + while(global_cmd_modcmd_show_flags[i].name) { + if(cbind->func->flags & global_cmd_modcmd_show_flags[i].flag) { + flagpos += sprintf(flags + flagpos, (flagpos ? " %s" : "\00314%s"), global_cmd_modcmd_show_flags[i].name); + } + visible_flags |= global_cmd_modcmd_show_flags[i].flag; + i++; + } + if(flagpos) + flags[flagpos++] = '\003'; + if(value) { + int add = 1; + unsigned int current_flag = 0; + if(value[0] == '+') { + value++; + } else if(value[0] == '-') { + add = 0; + value++; + } + if(*value) { + i = 0; + while(global_cmd_modcmd_show_flags[i].name) { + if(!stricmp(global_cmd_modcmd_show_flags[i].name, value)) { + current_flag = global_cmd_modcmd_show_flags[i].flag; + break; + } + i++; + } + } + if(cbind->func->flags & current_flag) { + reply(textclient, user, "NS_MODCMD_STATIC_FLAG"); + return 0; + } + if(add) + cbind->flags |= current_flag; + else + cbind->flags &= ~current_flag; + if(cbind->botid == 0) + printf_mysql_query("UPDATE `bot_binds` SET `flags` = '%u' WHERE `botclass` = '0' AND `botid` = '%d' AND `command` = '%s'", (cbind->flags & visible_flags), cbind->clientid, escape_string(cbind->cmd)); + else + printf_mysql_query("UPDATE `bot_binds` SET `flags` = '%u' WHERE `botclass` = '%d' AND `command` = '%s'", (cbind->flags & visible_flags), cbind->botid, escape_string(cbind->cmd)); + ret = 1; + } + i = 0; + while(global_cmd_modcmd_show_flags[i].name) { + if(cbind->flags & global_cmd_modcmd_show_flags[i].flag) { + flagpos += sprintf(flags + flagpos, (flagpos ? " %s" : "%s"), global_cmd_modcmd_show_flags[i].name); + } + i++; + } + flags[flagpos] = '\0'; + reply(textclient, user, "\002FLAGS \002 %s", flags); + return ret; +} + +static int global_cmd_modcmd_caccess(struct ClientSocket *textclient, struct UserNode *user, struct cmd_binding *cbind, char *value) { + char caccess[MAXLEN]; + int ret = 0; + if(value) { + if(!strcmp(value, "*")) + value = NULL; + bind_botwise_set_channel_access(cbind->botid, cbind->clientid, cbind->cmd, value); + if(cbind->botid == 0) + printf_mysql_query("UPDATE `bot_binds` SET `chan_access` = %s%s%s WHERE `botclass` = '0' AND `botid` = '%d' AND `command` = '%s'", (value ? "'" : ""), (value ? escape_string(value) : "NULL"), (value ? "'" : ""), cbind->clientid, escape_string(cbind->cmd)); + else + printf_mysql_query("UPDATE `bot_binds` SET `chan_access` = %s%s%s WHERE `botclass` = '%d' AND `command` = '%s'", (value ? "'" : ""), (value ? escape_string(value) : "NULL"), (value ? "'" : ""), cbind->botid, escape_string(cbind->cmd)); + + ret = 1; + } + if(cbind->flags & CMDFLAG_OVERRIDE_CHANNEL_ACCESS) + sprintf(caccess, "%s", cbind->channel_access); + else + sprintf(caccess, "\00314%s\003", (cbind->func->channel_access ? cbind->func->channel_access : "0")); + reply(textclient, user, "\002CACCESS \002 %s", caccess); + return ret; +} + +static int global_cmd_modcmd_oaccess(struct ClientSocket *textclient, struct UserNode *user, struct cmd_binding *cbind, char *value) { + char oaccess[MAXLEN]; + int ret = 0; + if(value) { + if(!strcmp(value, "*")) + value = NULL; + bind_botwise_set_global_access(cbind->botid, cbind->clientid, cbind->cmd, atoi(value)); + if(cbind->botid == 0) + printf_mysql_query("UPDATE `bot_binds` SET `global_access` = %s%s%s WHERE `botclass` = '0' AND `botid` = '%d' AND `command` = '%s'", (value ? "'" : ""), (value ? escape_string(value) : "NULL"), (value ? "'" : ""), cbind->clientid, escape_string(cbind->cmd)); + else + printf_mysql_query("UPDATE `bot_binds` SET `global_access` = %s%s%s WHERE `botclass` = '%d' AND `command` = '%s'", (value ? "'" : ""), (value ? escape_string(value) : "NULL"), (value ? "'" : ""), cbind->botid, escape_string(cbind->cmd)); + + ret = 1; + } + if(cbind->flags & CMDFLAG_OVERRIDE_GLOBAL_ACCESS) + sprintf(oaccess, "%d", cbind->global_access); + else + sprintf(oaccess, "\00314%d\003", (cbind->func->global_access ? cbind->func->global_access : 0)); + reply(textclient, user, "\002OACCESS \002 %s", oaccess); + return ret; +} + diff --git a/src/modules/global.mod/cmd_global_motd.c b/src/modules/global.mod/cmd_global_motd.c new file mode 100644 index 0000000..6e39f9a --- /dev/null +++ b/src/modules/global.mod/cmd_global_motd.c @@ -0,0 +1,36 @@ +/* cmd_global_motd.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_global.h" + +/* +* no args +*/ + +CMD_BIND(global_cmd_motd) { + FILE *f; + f = fopen("motd.txt", "rb"); + if(!f) return; + char line[MAXLEN]; + char *a; + while (fgets(line, MAXLEN, f) != NULL) { + if((a = strchr(line, '\n'))) + *a = '\0'; + reply(textclient, user, "%s", line); + } + fclose(f); +} \ No newline at end of file diff --git a/src/modules/global.mod/cmd_global_move.c b/src/modules/global.mod/cmd_global_move.c new file mode 100644 index 0000000..9cf53a4 --- /dev/null +++ b/src/modules/global.mod/cmd_global_move.c @@ -0,0 +1,97 @@ +/* cmd_global_move.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_global.h" + +/* +* argv[0] - channel +* argv[1] - new channel +*/ +CMD_BIND(global_cmd_move) { + MYSQL_RES *res; + MYSQL_ROW row; + char *channel = argv[0]; + char *new_channel = argv[1]; + if(!is_valid_chan(new_channel)) { + reply(textclient, user, "NS_INVALID_CHANNEL_NAME", new_channel); + return; + } + if(!stricmp(channel, new_channel)) { + reply(textclient, user, "NS_MOVE_SELF"); + return; + } + printf_mysql_query("SELECT `channel_id` FROM `bot_channels` LEFT JOIN `channels` ON `channel_id` = `chanid` WHERE `channel_name` = '%s'", escape_string(new_channel)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + reply(textclient, user, "NS_REGISTER_ALREADY", new_channel, client->user->nick); + return; + } + int chanid; + printf_mysql_query("SELECT `channel_id` FROM `channels` WHERE `channel_name` = '%s'", escape_string(channel)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + chanid = atoi(row[0]); + } else { + reply(textclient, user, "NS_UNREGISTER_NOT_REGISTERED", argv[0], client->user->nick); + return; + } + printf_mysql_query("SELECT `botid`, `bot_channels`.`id`, `suspended` FROM `bot_channels` LEFT JOIN `bots` ON `bot_channels`.`botid` = `bots`.`id` WHERE `chanid` = '%d' AND `botclass` = '%d'", chanid, client->botid); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) == NULL) { + reply(textclient, user, "NS_UNREGISTER_NOT_REGISTERED", argv[0], client->user->nick); + return; + } + if(!strcmp(row[2], "1")) { + reply(textclient, user, "NS_MOVE_SUSPENDED"); + return; + } + int botid = atoi(row[0]); + struct ClientSocket *bot; + struct ChanNode *channode = getChanByName(channel); + if(channode) { + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(isUserOnChan(bot->user, channode)) { + putsock(bot, "PART %s", channel); + putsock(bot, "JOIN %s", new_channel); + } + } + } else { + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(bot->clientid == botid) + putsock(bot, "JOIN %s", new_channel); + } + } + printf_mysql_query("SELECT `channel_id` FROM `channels` WHERE `channel_name` = '%s'", escape_string(new_channel)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + chanid = atoi(row[0]); + printf_mysql_query("DELETE FROM `chanusers` WHERE `chanuser_cid` = '%d'", chanid); + printf_mysql_query("DELETE FROM `bans` WHERE `ban_channel` = '%d'", chanid); + printf_mysql_query("DELETE FROM `events` WHERE `cid` = '%d'", chanid); + printf_mysql_query("DELETE FROM `noinvite` WHERE `cid` = '%d'", chanid); + printf_mysql_query("DELETE FROM `owner_history` WHERE `owner_history_cid` = '%d'", chanid); + printf_mysql_query("DELETE FROM `channels` WHERE `channel_id` = '%d'", chanid); + } + printf_mysql_query("UPDATE `channels` SET `channel_name` = '%s' WHERE `channel_name` = '%s'", escape_string(new_channel), escape_string(channel)); + + if(channode && channode->flags & CHANFLAG_REQUESTED_CHANINFO) { + channode->flags &= ~CHANFLAG_CHAN_REGISTERED; + channode->channel_id = 0; + } + reply(textclient, user, "NS_MOVE_DONE", channel, new_channel); + logEvent(event); +} diff --git a/src/modules/global.mod/cmd_global_netinfo.c b/src/modules/global.mod/cmd_global_netinfo.c new file mode 100644 index 0000000..fa149de --- /dev/null +++ b/src/modules/global.mod/cmd_global_netinfo.c @@ -0,0 +1,177 @@ +/* cmd_global_netinfo.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_global.h" + +/* +* no args +*/ + +CMD_BIND(global_cmd_netinfo) { + reply(textclient, user, "NS_NETINFO_HEADER"); + char tmp[MAXLEN]; + struct Table *table; + table = table_init(2, 19, 0); + char *content[2]; + + content[0] = get_language_string(user, "NS_NETINFO_UPTIME"); + content[1] = timeToStr(user, (time(0) - getStartTime()), 3, tmp); + table_add(table, content); + + content[0] = get_language_string(user, "NS_NETINFO_BOTS"); + struct ClientSocket *cclient; + int bot_count = 0, connected_bot_count = 0; + float traffic_in = 0, traffic_out = 0; + for(cclient = getBots(0, NULL); cclient; cclient = getBots(0, cclient)) { + bot_count++; + if(cclient->flags & SOCKET_FLAG_READY) + connected_bot_count++; + traffic_in += cclient->traffic_in; + traffic_out += cclient->traffic_out; + } + sprintf(tmp, "%d (%d connected)", bot_count, connected_bot_count); + content[1] = tmp; + table_add(table, content); + + content[0] = get_language_string(user, "NS_NETINFO_TRAFFIC"); + sprintf(tmp, "in: %.2f kb out: %.2f kb", traffic_in / 1024, traffic_out / 1024); + content[1] = tmp; + table_add(table, content); + + int channel_count = getChannelCount(); + float channel_memory = channel_count * sizeof(struct ChanNode); + int channel_ban_count = getChanBanCount(); + float channel_ban_memory = channel_ban_count * sizeof(struct BanNode); + int user_count = getUserCount(); + float user_memory = user_count * sizeof(struct UserNode); + int chanuser_count = getChanUserCount(); + float chanuser_memory = chanuser_count * sizeof(struct ChanUser); + float total_memory = channel_memory + channel_ban_memory + user_memory + chanuser_memory; + content[0] = get_language_string(user, "NS_NETINFO_CACHE"); + sprintf(tmp, "%.2f kB (%.2f MB)", total_memory / 1024, total_memory / 1024 / 1024); + content[1] = tmp; + table_add(table, content); + content[0] = get_language_string(user, "NS_NETINFO_CHANNEL"); + sprintf(tmp, "%d %.2f kB (%d * %u B = %.2f kB)", channel_count, channel_memory / 1024, channel_count, (unsigned int) sizeof(struct ChanNode), channel_memory / 1024); + content[1] = tmp; + table_add(table, content); + content[0] = get_language_string(user, "NS_NETINFO_CHANNEL_BAN"); + sprintf(tmp, "%d %.2f kB (%d * %u B = %.2f kB)", channel_ban_count, channel_ban_memory / 1024, channel_ban_count, (unsigned int) sizeof(struct BanNode), channel_ban_memory / 1024); + content[1] = tmp; + table_add(table, content); + content[0] = get_language_string(user, "NS_NETINFO_USER"); + sprintf(tmp, "%d %.2f kB (%d * %u B = %.2f kB)", user_count, user_memory / 1024, user_count, (unsigned int) sizeof(struct UserNode), user_memory / 1024); + content[1] = tmp; + table_add(table, content); + content[0] = get_language_string(user, "NS_NETINFO_CHANUSER"); + sprintf(tmp, "%d %.2f kB (%d * %u B = %.2f kB)", chanuser_count, chanuser_memory / 1024, chanuser_count, (unsigned int) sizeof(struct ChanUser), chanuser_memory / 1024); + content[1] = tmp; + table_add(table, content); + + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SHOW TABLE STATUS"); + res = mysql_use(); + int mysql_entrys[4]; + float mysql_length[5]; + total_memory = 0; + mysql_entrys[0] = 0; mysql_entrys[1] = 0; mysql_entrys[2] = 0; mysql_entrys[3] = 0; + mysql_length[0] = 0; mysql_length[1] = 0; mysql_length[2] = 0; mysql_length[3] = 0; mysql_length[4] = 0; + while ((row = mysql_fetch_row(res)) != NULL) { + if(!stricmp(row[0], "channels")) { + mysql_entrys[0] = atoi(row[4]); + mysql_length[0] = atof(row[6]); + total_memory += atof(row[6]); + } else if(!stricmp(row[0], "bans")) { + mysql_entrys[1] = atoi(row[4]); + mysql_length[1] = atof(row[6]); + total_memory += atof(row[6]); + } else if(!stricmp(row[0], "users")) { + mysql_entrys[2] = atoi(row[4]); + mysql_length[2] = atof(row[6]); + total_memory += atof(row[6]); + } else if(!stricmp(row[0], "chanusers")) { + mysql_entrys[3] = atoi(row[4]); + mysql_length[3] = atof(row[6]); + total_memory += atof(row[6]); + } else { + mysql_length[4] += atof(row[6]); + total_memory += atof(row[6]); + } + } + + content[0] = get_language_string(user, "NS_NETINFO_DATABASE"); + sprintf(tmp, "%.2f kB (%.2f MB)", total_memory / 1024, total_memory / 1024 / 1024); + content[1] = tmp; + table_add(table, content); + + content[0] = get_language_string(user, "NS_NETINFO_CHANNEL"); + sprintf(tmp, "%d %.2f kB", mysql_entrys[0], mysql_length[0] / 1024); + content[1] = tmp; + table_add(table, content); + + content[0] = get_language_string(user, "NS_NETINFO_CHANNEL_BAN"); + sprintf(tmp, "%d %.2f kB", mysql_entrys[1], mysql_length[1] / 1024); + content[1] = tmp; + table_add(table, content); + + content[0] = get_language_string(user, "NS_NETINFO_USER"); + sprintf(tmp, "%d %.2f kB", mysql_entrys[2], mysql_length[2] / 1024); + content[1] = tmp; + table_add(table, content); + + content[0] = get_language_string(user, "NS_NETINFO_CHANUSER"); + sprintf(tmp, "%d %.2f kB", mysql_entrys[3], mysql_length[3] / 1024); + content[1] = tmp; + table_add(table, content); + + content[0] = get_language_string(user, "NS_NETINFO_OTHER"); + sprintf(tmp, "* %.2f kB", mysql_length[4] / 1024); + content[1] = tmp; + table_add(table, content); + + #ifdef HAVE_THREADS + content[0] = get_language_string(user, "NS_NETINFO_THREADS"); + sprintf(tmp, "%d (current thread: %i)", getRunningThreads(), getCurrentThreadID()); + content[1] = tmp; + table_add(table, content); + #endif + + if(strcmp(get_revision(), "")) + sprintf(tmp, "%s.%d (%s: %s)", NEONSERV_VERSION, get_patchlevel(), (is_stable_revision() ? "stable" : "dev"), (is_stable_revision() ? get_revision() : get_dev_revision())); + else + sprintf(tmp, "%s.%d", NEONSERV_VERSION, get_patchlevel()); + content[0] = get_language_string(user, "NS_NETINFO_VERSION"); + content[1] = tmp; + table_add(table, content); + + content[0] = get_language_string(user, "NS_NETINFO_COMPILER"); + content[1] = build_language_string(user, tmp, "NS_NETINFO_COMPILER_VALUE", COMPILER, get_creation()); + table_add(table, content); + + content[0] = get_language_string(user, "NS_NETINFO_CODE"); + content[1] = build_language_string(user, tmp, "NS_NETINFO_CODE_VALUE", get_codelines()); + table_add(table, content); + + char **table_lines = table_end(table); + int i; + for(i = 0; i < table->entrys; i++) { + reply(textclient, user, table_lines[i]); + } + table_free(table); +} + diff --git a/src/modules/global.mod/cmd_global_notice.c b/src/modules/global.mod/cmd_global_notice.c new file mode 100644 index 0000000..2768a6d --- /dev/null +++ b/src/modules/global.mod/cmd_global_notice.c @@ -0,0 +1,28 @@ +/* cmd_global_notice.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_global.h" + +/* +* argv[0] target +* argv[1-*] message +*/ + +CMD_BIND(global_cmd_notice) { + char *message = merge_argv(argv, 1, argc); + putsock(client, "NOTICE %s :%s", argv[0], message); +} \ No newline at end of file diff --git a/src/modules/global.mod/cmd_global_raw.c b/src/modules/global.mod/cmd_global_raw.c new file mode 100644 index 0000000..98b9d10 --- /dev/null +++ b/src/modules/global.mod/cmd_global_raw.c @@ -0,0 +1,27 @@ +/* cmd_global_raw.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_global.h" + +/* +* argv[0-*] raw +*/ + +CMD_BIND(global_cmd_raw) { + char *raw = merge_argv(argv, 0, argc); + putsock(client, "%s", raw); +} \ No newline at end of file diff --git a/src/modules/global.mod/cmd_global_reconnect.c b/src/modules/global.mod/cmd_global_reconnect.c new file mode 100644 index 0000000..1ac409a --- /dev/null +++ b/src/modules/global.mod/cmd_global_reconnect.c @@ -0,0 +1,49 @@ +/* cmd_global_reconnect.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_global.h" + +/* +* argv[0] nick/botid +*/ + +CMD_BIND(global_cmd_reconnect) { + int botid; + if(argc) { + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `id` FROM `bots` WHERE `nick` = '%s' OR `id` = '%s'", escape_string(argv[0]), escape_string(argv[0])); + res = mysql_use(); + if((row = mysql_fetch_row(res)) == NULL) { + reply(textclient, user, "NS_DELBOT_NOT_FOUND", argv[0]); + return; + } + botid = atoi(row[0]); + for(client = getBots(0, NULL); client; client = getBots(0, client)) { + if(client->clientid == botid) { + close_socket(client); + connect_socket(client); + break; + } + } + } else { + close_socket(client); + connect_socket(client); + } + reply(textclient, user, "NS_RECONNECT_DONE"); + logEvent(event); +} diff --git a/src/modules/global.mod/cmd_global_register.c b/src/modules/global.mod/cmd_global_register.c new file mode 100644 index 0000000..03fa932 --- /dev/null +++ b/src/modules/global.mod/cmd_global_register.c @@ -0,0 +1,313 @@ +/* cmd_global_register.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_global.h" + +/* +* argv[0] - channel +* argv[1] - nick / *auth +* argv[2] - (optional) bot nick +*/ +static AUTHLOOKUP_CALLBACK(global_cmd_register_auth_lookup); +static USERAUTH_CALLBACK(global_cmd_register_nick_lookup); +static void global_cmd_register_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *channel, char *auth, int multibot, char *botname); + +struct global_cmd_register_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + struct ChanNode *chan; + struct Event *event; + char *nick; + char *channel; + char *botname; + int multibot; +}; + +CMD_BIND(global_cmd_register) { + MYSQL_RES *res; + MYSQL_ROW row; + char *channel = argv[0]; + char *botname = (argc > 2 ? argv[2] : NULL); + int multibot = 0; + if(!is_valid_chan(channel)) { + reply(textclient, user, "NS_INVALID_CHANNEL_NAME", argv[0]); + return; + } + printf_mysql_query("SELECT `botid`, `botclass` FROM `bot_channels` LEFT JOIN `bots` ON `bot_channels`.`botid` = `bots`.`id` LEFT JOIN `channels` ON `bot_channels`.`chanid` = `channels`.`channel_id` WHERE `channel_name` = '%s'", escape_string(channel)); + res = mysql_use(); + while ((row = mysql_fetch_row(res)) != NULL) { + if(atoi(row[1]) == client->botid && (client->botid || client->clientid == atoi(row[0]))) { + reply(textclient, user, "NS_REGISTER_ALREADY", argv[0], client->user->nick); + return; + } else + multibot = 1; + } + printf_mysql_query("SELECT `user_user`, `dnr_timeout`, `dnr_reason`, `dnr_id` FROM `donotregister` LEFT JOIN `users` ON `dnr_user` = `user_id` WHERE `dnr_target` = '%s'", escape_string(channel)); + res = mysql_use(); + if((row = mysql_fetch_row(res)) != NULL) { + int expire_time = atoi(row[1]); + if(expire_time) { + if(expire_time - time(0) <= 0) { + printf_mysql_query("DELETE FROM `donotregister` WHERE `dnr_id` = '%s'", row[3]); + } else { + char expireBuf[MAXLEN]; + reply(textclient, user, "NS_DNR_SET_EXPIRES", channel, row[0], timeToStr(user, (expire_time - time(0)), 2, expireBuf), row[2]); + return; + } + } else { + reply(textclient, user, "NS_DNR_SET", channel, row[0], row[2]); + return; + } + } + //if theres already another bot in the channel we don't need a owner parameter... + if(multibot && argc < 2) { + //skip all these owner check lines + multibot = 2; + global_cmd_register_async1(client, textclient, user, chan, event, channel, NULL, multibot, botname); + return; + } else if(argc < 2) { + global_cmd_register_async1(client, textclient, user, chan, event, channel, user->auth, multibot, botname); + return; + } + //check own access + if(argv[1][0] == '*') { + //we've got an auth + argv[1]++; + printf_mysql_query("SELECT `user_user` FROM `users` WHERE `user_user` = '%s'", escape_string(argv[1])); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + global_cmd_register_async1(client, textclient, user, chan, event, channel, row[0], multibot, botname); + } else { + //we need to create a new user... + //but first lookup the auth to check if it really exists + struct global_cmd_register_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("global", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->chan = chan; + cache->event = event; + cache->nick = strdup(argv[1]); + cache->channel = strdup(channel); + cache->multibot = multibot; + cache->botname = (botname ? strdup(botname) : NULL); + lookup_authname(argv[1], module_id, global_cmd_register_auth_lookup, cache); + } + } else { + struct UserNode *cuser = getUserByNick(argv[1]); + if(!cuser) { + cuser = createTempUser(argv[1]); + if(!cuser) { + reply(textclient, user, "NS_USER_UNKNOWN", argv[1]); + return; + } + cuser->flags |= USERFLAG_ISTMPUSER; + } + if(cuser->flags & USERFLAG_ISAUTHED) { + global_cmd_register_async1(client, textclient, user, chan, event, channel, cuser->auth, multibot, botname); + } else { + struct global_cmd_register_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("global", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->chan = chan; + cache->event = event; + cache->nick = strdup(argv[1]); + cache->channel = strdup(channel); + cache->multibot = multibot; + cache->botname = (botname ? strdup(botname) : NULL); + get_userauth(cuser, module_id, global_cmd_register_nick_lookup, cache); + } + } +} + +static AUTHLOOKUP_CALLBACK(global_cmd_register_auth_lookup) { + struct global_cmd_register_cache *cache = data; + if(!exists) { + //AUTH_DOES_NOT_EXIST + reply(cache->textclient, cache->user, "NS_AUTH_UNKNOWN", cache->nick); + } else + global_cmd_register_async1(cache->client, cache->textclient, cache->user, cache->chan, cache->event, cache->channel, auth, cache->multibot, cache->botname); + if(cache->botname) + free(cache->botname); + free(cache->channel); + free(cache->nick); + free(cache); +} + +static USERAUTH_CALLBACK(global_cmd_register_nick_lookup) { + struct global_cmd_register_cache *cache = data; + if(!user) { + //USER_DOES_NOT_EXIST + reply(cache->textclient, cache->user, "NS_USER_UNKNOWN", cache->nick); + } + else if(!(user->flags & USERFLAG_ISAUTHED)) { + //USER_NOT_AUTHED + reply(cache->textclient, cache->user, "NS_USER_NEED_AUTH", cache->nick); + } + else + global_cmd_register_async1(cache->client, cache->textclient, cache->user, cache->chan, cache->event, cache->channel, user->auth, cache->multibot, cache->botname); + if(cache->botname) + free(cache->botname); + free(cache->channel); + free(cache->nick); + free(cache); +} + +static void global_cmd_register_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct ChanNode *chan, struct Event *event, char *channel, char *auth, int multibot, char *botname) { + //we've got a valid auth now... + MYSQL_RES *res; + MYSQL_ROW row, row2; + int userid = 0, adminid; + printf_mysql_query("SELECT `user_id` FROM `users` WHERE `user_user` = '%s'", escape_string(user->auth)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) + adminid = atoi(row[0]); + else + adminid = 0; + if(multibot != 2) { + printf_mysql_query("SELECT `user_user`, `dnr_timeout`, `dnr_reason`, `dnr_id` FROM `donotregister` LEFT JOIN `users` ON `dnr_user` = `user_id` WHERE `dnr_target` = '%s'", escape_string(auth)); + res = mysql_use(); + if((row = mysql_fetch_row(res)) != NULL) { + int expire_time = atoi(row[1]); + if(expire_time) { + if(expire_time - time(0) <= 0) { + printf_mysql_query("DELETE FROM `donotregister` WHERE `dnr_id` = '%s'", row[3]); + } else { + char expireBuf[MAXLEN]; + reply(textclient, user, "NS_DNR_SET_EXPIRES", auth, row[0], timeToStr(user, (expire_time - time(0)), 2, expireBuf), row[2]); + return; + } + } else { + reply(textclient, user, "NS_DNR_SET", auth, row[0], row[2]); + return; + } + } + printf_mysql_query("SELECT `user_id` FROM `users` WHERE `user_user` = '%s'", escape_string(auth)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + userid = atoi(row[0]); + } else { + printf_mysql_query("INSERT INTO `users` (`user_user`) VALUES ('%s')", escape_string(auth)); + userid = (int) mysql_insert_id(get_mysql_conn()); + } + } + if(client->botid) + printf_mysql_query("SELECT `id`, `max_channels`, `defaulttrigger`, `nick` FROM `bots` WHERE `botclass` = '%d' AND `active` = '1' ORDER BY `register_priority` DESC", client->botid); + else + printf_mysql_query("SELECT `id`, `max_channels`, `defaulttrigger`, `nick` FROM `bots` WHERE `id` = '%d' AND `active` = '1'", client->clientid); + res = mysql_use(); + int botid = 0; + while ((row = mysql_fetch_row(res)) != NULL) { + //check channel count + printf_mysql_query("SELECT COUNT(*) FROM `bot_channels` WHERE `botid` = '%s'", row[0]); + row2 = mysql_fetch_row(mysql_use()); + if(atoi(row2[0]) < atoi(row[1]) && (!botname || !stricmp(botname, row[3]))) { + botid = atoi(row[0]); + break; + } + } + if(!botid) { + reply(textclient, user, "NS_REGISTER_FULL"); + return; + } + int chanid; + printf_mysql_query("SELECT `channel_id` FROM `channels` WHERE `channel_name` = '%s'", escape_string(channel)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + chanid = atoi(row[0]); + printf_mysql_query("UPDATE `channels` SET `channel_registered` = UNIX_TIMESTAMP(), `channel_registrator` = '%d' WHERE `channel_id` = '%d'", adminid, chanid); + } else { + printf_mysql_query("INSERT INTO `channels` (`channel_name`, `channel_registered`, `channel_registrator`) VALUES ('%s', UNIX_TIMESTAMP(), '%d')", escape_string(channel), adminid); + chanid = (int) mysql_insert_id(get_mysql_conn()); + } + struct ClientSocket *bot; + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(bot->clientid == botid) + break; + } + if(bot) { + putsock(bot, "JOIN %s", channel); + } else + reply(textclient, user, "NS_REGISTER_DISCONNECTED", channel); + printf_mysql_query("INSERT INTO `bot_channels` (`botid`, `chanid`, `trigger`) VALUES ('%d', '%d', NULL)", botid, chanid); + if(multibot != 2) { + if(multibot) { + printf_mysql_query("UPDATE `chanusers` SET `chanuser_access` = 499 WHERE `chanuser_cid` = '%d' AND `chanuser_access` = '500'", chanid); + printf_mysql_query("DELETE FROM `chanusers` WHERE `chanuser_cid` = '%d' AND `chanuser_uid` = '%d'", chanid, userid); + } else + printf_mysql_query("DELETE FROM `chanusers` WHERE `chanuser_cid` = '%d'", chanid); + printf_mysql_query("INSERT INTO `chanusers` (`chanuser_cid`, `chanuser_uid`, `chanuser_access`) VALUES ('%d', '%d', '%d')", chanid, userid, 500); + reply(textclient, user, "NS_REGISTER_DONE", channel, auth); + } else + reply(textclient, user, "NS_REGISTER_DONE_NOAUTH", channel); + // NeonBackup SubBlock + if(client->botid == NEONSERV_BOTID) { + char setting[128]; + sprintf(setting, "modules.%s.auto_backup_register", get_module_name(module_id)); + if(get_int_field(setting)) + module_global_cmd_register_neonbackup(channel); + } + logEvent(event); +} + +void global_cmd_register_neonbackup(char *channel) { + MYSQL_RES *res; + MYSQL_ROW row, row2; + printf_mysql_query("SELECT `botid` FROM `bot_channels` LEFT JOIN `bots` ON `bot_channels`.`botid` = `bots`.`id` LEFT JOIN `channels` ON `bot_channels`.`chanid` = `channels`.`channel_id` WHERE `channel_name` = '%s' AND botclass = '%d'", escape_string(channel), NEONBACKUP_BOTID); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) + return; + int chanid; + printf_mysql_query("SELECT `channel_id` FROM `channels` WHERE `channel_name` = '%s'", escape_string(channel)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + chanid = atoi(row[0]); + } else + return; + printf_mysql_query("SELECT `id`, `max_channels`, `defaulttrigger`, `nick` FROM `bots` WHERE `botclass` = '%d' AND `active` = '1' ORDER BY `register_priority` DESC", NEONBACKUP_BOTID); + res = mysql_use(); + int botid = 0; + while ((row = mysql_fetch_row(res)) != NULL) { + //check channel count + printf_mysql_query("SELECT COUNT(*) FROM `bot_channels` WHERE `botid` = '%s'", row[0]); + row2 = mysql_fetch_row(mysql_use()); + if(atoi(row2[0]) < atoi(row[1])) { + botid = atoi(row[0]); + break; + } + } + if(!botid) + return; + struct ClientSocket *bot; + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(bot->clientid == botid) + break; + } + if(bot) { + putsock(bot, "JOIN %s", channel); + } + printf_mysql_query("INSERT INTO `bot_channels` (`botid`, `chanid`, `trigger`) VALUES ('%d', '%d', NULL)", botid, chanid); +} + diff --git a/src/modules/global.mod/cmd_global_reload.c b/src/modules/global.mod/cmd_global_reload.c new file mode 100644 index 0000000..ca77ac9 --- /dev/null +++ b/src/modules/global.mod/cmd_global_reload.c @@ -0,0 +1,27 @@ +/* cmd_global_reload.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_global.h" + +/* +* argv[0] soft (optional) +*/ + +CMD_BIND(global_cmd_reload) { + reload_config(); + reply(client, user, "reloaded."); +} diff --git a/src/modules/global.mod/cmd_global_reloadlang.c b/src/modules/global.mod/cmd_global_reloadlang.c new file mode 100644 index 0000000..8e25890 --- /dev/null +++ b/src/modules/global.mod/cmd_global_reloadlang.c @@ -0,0 +1,35 @@ +/* cmd_global_reloadlang.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_global.h" + +/* +* argv[0] language tag +*/ + +CMD_BIND(global_cmd_reloadlang) { + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `text`, `lang` FROM `language` WHERE `ident` = 'name' AND `lang` = '%s'", escape_string(argv[0])); + res = mysql_use(); + if((row = mysql_fetch_row(res)) != NULL) { + load_language(row[1], row[0]); + reply(textclient, user, "NS_RELOADLANG_DONE", row[0], row[1]); + } else { + reply(textclient, user, "NS_RELOADLANG_UNKNOWN", argv[0]); + } +} \ No newline at end of file diff --git a/src/modules/global.mod/cmd_global_restart.c b/src/modules/global.mod/cmd_global_restart.c new file mode 100644 index 0000000..80f4403 --- /dev/null +++ b/src/modules/global.mod/cmd_global_restart.c @@ -0,0 +1,26 @@ +/* cmd_global_restart.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_global.h" + +/* +* argv[0] soft (optional) +*/ + +CMD_BIND(global_cmd_restart) { + restart_bot(0); +} diff --git a/src/modules/global.mod/cmd_global_say.c b/src/modules/global.mod/cmd_global_say.c new file mode 100644 index 0000000..1af7672 --- /dev/null +++ b/src/modules/global.mod/cmd_global_say.c @@ -0,0 +1,28 @@ +/* cmd_global_say.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_global.h" + +/* +* argv[0] target +* argv[1-*] message +*/ + +CMD_BIND(global_cmd_say) { + char *message = merge_argv(argv, 1, argc); + putsock(client, "PRIVMSG %s :%s", argv[0], message); +} \ No newline at end of file diff --git a/src/modules/global.mod/cmd_global_setaccess.c b/src/modules/global.mod/cmd_global_setaccess.c new file mode 100644 index 0000000..8753cc3 --- /dev/null +++ b/src/modules/global.mod/cmd_global_setaccess.c @@ -0,0 +1,144 @@ +/* cmd_global_setaccess.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_global.h" + +/* +* argv[0] - nick / *auth +* argv[1] - global access +*/ +static AUTHLOOKUP_CALLBACK(global_cmd_setaccess_auth_lookup); +static USERAUTH_CALLBACK(global_cmd_setaccess_nick_lookup); +static void global_cmd_setaccess_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct Event *event, char *nick, char *auth, int access); + +struct global_cmd_setaccess_cache { + struct ClientSocket *client, *textclient; + struct UserNode *user; + struct Event *event; + int access; + char *nick; +}; + +CMD_BIND(global_cmd_setaccess) { + int caccess; + MYSQL_RES *res; + MYSQL_ROW row; + caccess = atoi(argv[1]); + if(caccess < 0 || caccess > 1000) { + reply(textclient, user, "NS_INVALID_ACCESS", caccess); + return; + } + printf_mysql_query("SELECT `user_access` FROM `users` WHERE `user_user` = '%s'", escape_string(user->auth)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) == NULL || atoi(row[0]) < caccess) { + reply(textclient, user, "NS_ACCESS_OUTRANKED"); + return; + } + if(argv[0][0] == '*') { + //we've got an auth + argv[0]++; + printf_mysql_query("SELECT `user_user` FROM `users` WHERE `user_user` = '%s'", escape_string(argv[0])); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + global_cmd_setaccess_async1(client, textclient, user, event, argv[0], row[0], caccess); + } else { + //we need to create a new user... + //but first lookup the auth to check if it really exists + struct global_cmd_setaccess_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("global", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->event = event; + cache->access = caccess; + cache->nick = strdup(argv[0]); + lookup_authname(argv[0], module_id, global_cmd_setaccess_auth_lookup, cache); + } + } else { + struct UserNode *cuser = getUserByNick(argv[0]); + if(!cuser) { + cuser = createTempUser(argv[0]); + if(!cuser) { + reply(textclient, user, "NS_USER_UNKNOWN", argv[0]); + return; + } + cuser->flags |= USERFLAG_ISTMPUSER; + } + if(cuser->flags & USERFLAG_ISAUTHED) { + global_cmd_setaccess_async1(client, textclient, user, event, argv[0], cuser->auth, caccess); + } else { + struct global_cmd_setaccess_cache *cache = malloc(sizeof(*cache)); + if (!cache) { + printf_log("global", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return; + } + cache->client = client; + cache->textclient = textclient; + cache->user = user; + cache->event = event; + cache->access = caccess; + cache->nick = strdup(argv[0]); + get_userauth(cuser, module_id, global_cmd_setaccess_nick_lookup, cache); + } + } +} + +static AUTHLOOKUP_CALLBACK(global_cmd_setaccess_auth_lookup) { + struct global_cmd_setaccess_cache *cache = data; + if(!exists) { + //AUTH_DOES_NOT_EXIST + reply(cache->textclient, cache->user, "NS_AUTH_UNKNOWN", cache->nick); + } else + global_cmd_setaccess_async1(cache->client, cache->textclient, cache->user, cache->event, cache->nick, auth, cache->access); + free(cache->nick); + free(cache); +} + +static USERAUTH_CALLBACK(global_cmd_setaccess_nick_lookup) { + struct global_cmd_setaccess_cache *cache = data; + if(!user) { + //USER_DOES_NOT_EXIST + reply(cache->textclient, cache->user, "NS_USER_UNKNOWN", cache->nick); + } + else if(!(user->flags & USERFLAG_ISAUTHED)) { + //USER_NOT_AUTHED + reply(cache->textclient, cache->user, "NS_USER_NEED_AUTH", cache->nick); + } + else + global_cmd_setaccess_async1(cache->client, cache->textclient, cache->user, cache->event, user->nick, user->auth, cache->access); + free(cache->nick); + free(cache); +} + +static void global_cmd_setaccess_async1(struct ClientSocket *client, struct ClientSocket *textclient, struct UserNode *user, struct Event *event, char *nick, char *auth, int caccess) { + //we've got a valid auth now... + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `user_id`, `user_access` FROM `users` WHERE `user_user` = '%s'", escape_string(auth)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + if(atoi(row[1]) != caccess) + printf_mysql_query("UPDATE `users` SET `user_access` = '%d' WHERE `user_id` = '%s'", caccess, row[0]); + } else { + printf_mysql_query("INSERT INTO `users` (`user_user`, `user_access`) VALUES ('%s', '%d')", escape_string(auth), caccess); + } + reply(textclient, user, "NS_SETACCESS_DONE", auth, caccess); + logEvent(event); +} diff --git a/src/modules/global.mod/cmd_global_setbot.c b/src/modules/global.mod/cmd_global_setbot.c new file mode 100644 index 0000000..c81fd7f --- /dev/null +++ b/src/modules/global.mod/cmd_global_setbot.c @@ -0,0 +1,559 @@ +/* cmd_global_setbot.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_global.h" + +/* +* argv[0] botid +* argv[1] setting +* argv[2] value +*/ + +static int global_cmd_setbot_active(struct ClientSocket *textclient, struct UserNode *user, MYSQL_ROW bot, char *value); +static int global_cmd_setbot_nick(struct ClientSocket *textclient, struct UserNode *user, MYSQL_ROW bot, char *value); +static int global_cmd_setbot_ident(struct ClientSocket *textclient, struct UserNode *user, MYSQL_ROW bot, char *value); +static int global_cmd_setbot_realname(struct ClientSocket *textclient, struct UserNode *user, MYSQL_ROW bot, char *value); +static int global_cmd_setbot_server(struct ClientSocket *textclient, struct UserNode *user, MYSQL_ROW bot, char *value); +static int global_cmd_setbot_port(struct ClientSocket *textclient, struct UserNode *user, MYSQL_ROW bot, char *value); +static int global_cmd_setbot_bind(struct ClientSocket *textclient, struct UserNode *user, MYSQL_ROW bot, char *value); +static int global_cmd_setbot_ssl(struct ClientSocket *textclient, struct UserNode *user, MYSQL_ROW bot, char *value); +static int global_cmd_setbot_serverpass(struct ClientSocket *textclient, struct UserNode *user, MYSQL_ROW bot, char *value); +static int global_cmd_setbot_class(struct ClientSocket *textclient, struct UserNode *user, MYSQL_ROW bot, char *value); +static int global_cmd_setbot_queue(struct ClientSocket *textclient, struct UserNode *user, MYSQL_ROW bot, char *value); +static int global_cmd_setbot_prefered(struct ClientSocket *textclient, struct UserNode *user, MYSQL_ROW bot, char *value); +static int global_cmd_setbot_secret(struct ClientSocket *textclient, struct UserNode *user, MYSQL_ROW bot, char *value); +static int global_cmd_setbot_maxchan(struct ClientSocket *textclient, struct UserNode *user, MYSQL_ROW bot, char *value); +static int global_cmd_setbot_priority(struct ClientSocket *textclient, struct UserNode *user, MYSQL_ROW bot, char *value); +static int global_cmd_setbot_trigger(struct ClientSocket *textclient, struct UserNode *user, MYSQL_ROW bot, char *value); + +CMD_BIND(global_cmd_setbot) { + MYSQL_RES *res; + MYSQL_ROW row; + int botid = atoi(argv[0]); + printf_mysql_query("SELECT `active`, `nick`, `server`, `port`, `pass`, `botclass`, `textbot`, `queue`, `defaulttrigger`, `max_channels`, `register_priority`, `bind`, `ident`, `realname`, `ssl`, `id`, `secret` FROM `bots` WHERE `id` = '%d'", botid); + res = mysql_use(); + if(!(row = mysql_fetch_row(res))) { + reply(textclient, user, "NS_SETBOT_UNKNOWN", botid); + return; + } + if(argc > 1) { + char *value; + if(argc > 2) { + value = merge_argv(argv, 2, argc); + } else + value = NULL; + int log_event = 0; + if(!stricmp(argv[1], "active")) log_event = global_cmd_setbot_active(textclient, user, row, value); + else if(!stricmp(argv[1], "nick")) log_event = global_cmd_setbot_nick(textclient, user, row, value); + else if(!stricmp(argv[1], "ident")) log_event = global_cmd_setbot_ident(textclient, user, row, value); + else if(!stricmp(argv[1], "realname")) log_event = global_cmd_setbot_realname(textclient, user, row, value); + else if(!stricmp(argv[1], "server")) log_event = global_cmd_setbot_server(textclient, user, row, value); + else if(!stricmp(argv[1], "port")) log_event = global_cmd_setbot_port(textclient, user, row, value); + else if(!stricmp(argv[1], "bind")) log_event = global_cmd_setbot_bind(textclient, user, row, value); + else if(!stricmp(argv[1], "ssl")) log_event = global_cmd_setbot_ssl(textclient, user, row, value); + else if(!stricmp(argv[1], "serverpass")) log_event = global_cmd_setbot_serverpass(textclient, user, row, value); + else if(!stricmp(argv[1], "botclass")) log_event = global_cmd_setbot_class(textclient, user, row, value); + else if(!stricmp(argv[1], "queue")) log_event = global_cmd_setbot_queue(textclient, user, row, value); + else if(!stricmp(argv[1], "prefered")) log_event = global_cmd_setbot_prefered(textclient, user, row, value); + else if(!stricmp(argv[1], "secret")) log_event = global_cmd_setbot_secret(textclient, user, row, value); + else if(!stricmp(argv[1], "maxchan")) log_event = global_cmd_setbot_maxchan(textclient, user, row, value); + else if(!stricmp(argv[1], "priority")) log_event = global_cmd_setbot_priority(textclient, user, row, value); + else if(!stricmp(argv[1], "trigger")) log_event = global_cmd_setbot_trigger(textclient, user, row, value); + else { + reply(textclient, user, "NS_SETBOT_SETTING", argv[1]); + } + if(log_event) { + if(!stricmp(argv[1], "serverpass") && value) { //censor server password + char cmd_args[MAXLEN]; + sprintf(cmd_args, "%d SERVERPASS ***", botid); + free(event->arguments); + event->arguments = strdup(cmd_args); + } + logEvent(event); + } + } else { + reply(textclient, user, "NS_SETBOT_HEADER", botid); + global_cmd_setbot_active(textclient, user, row, NULL); + global_cmd_setbot_nick(textclient, user, row, NULL); + global_cmd_setbot_ident(textclient, user, row, NULL); + global_cmd_setbot_realname(textclient, user, row, NULL); + global_cmd_setbot_server(textclient, user, row, NULL); + global_cmd_setbot_port(textclient, user, row, NULL); + global_cmd_setbot_bind(textclient, user, row, NULL); + global_cmd_setbot_ssl(textclient, user, row, NULL); + global_cmd_setbot_serverpass(textclient, user, row, NULL); + global_cmd_setbot_class(textclient, user, row, NULL); + global_cmd_setbot_queue(textclient, user, row, NULL); + global_cmd_setbot_prefered(textclient, user, row, NULL); + global_cmd_setbot_secret(textclient, user, row, NULL); + global_cmd_setbot_maxchan(textclient, user, row, NULL); + global_cmd_setbot_priority(textclient, user, row, NULL); + global_cmd_setbot_trigger(textclient, user, row, NULL); + } +} + +static int global_cmd_setbot_active(struct ClientSocket *textclient, struct UserNode *user, MYSQL_ROW bot, char *value) { + int val = ((bot[0] && !strcmp(bot[0], "1")) ? 1 : 0); + int ret = 0; + if(value) { + if(!strcmp(value, "0") || !stricmp(value, "off") || !stricmp(value, get_language_string(user, "NS_SET_OFF"))) { + val = 0; + } else if(!strcmp(value, "1") || !stricmp(value, "on") || !stricmp(value, get_language_string(user, "NS_SET_ON"))) { + val = 1; + } else { + reply(textclient, user, "NS_SET_INVALID_BOOLEAN", value); + return 0; + } + if(val != ((bot[0] && !strcmp(bot[0], "1")) ? 1 : 0)) { + if(val) { + //add the bot + struct ClientSocket *client; + client = create_socket(bot[2], atoi(bot[3]), bot[11], bot[4], bot[1], bot[12], bot[13]); + client->flags |= (strcmp(bot[6], "0") ? SOCKET_FLAG_PREFERRED : 0); + client->flags |= (strcmp(bot[7], "0") ? SOCKET_FLAG_USE_QUEUE : 0); + client->flags |= (strcmp(bot[14], "0") ? SOCKET_FLAG_SSL : 0); + client->botid = atoi(bot[5]); + client->clientid = atoi(bot[15]); + connect_socket(client); + if(client->botid == 0) { + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `command`, `function`, `parameters`, `global_access`, `chan_access` FROM `bot_binds` WHERE `botclass` = '0' AND `botid` = '%d'", client->clientid); + res = mysql_use(); + while ((row = mysql_fetch_row(res)) != NULL) { + if(bind_botwise_cmd_to_command(0, client->clientid, row[0], row[1])) { + if(row[2] && strcmp(row[2], "")) { + bind_botwise_set_parameters(0, client->clientid, row[0], row[2]); + } + if(row[3]) { + bind_botwise_set_global_access(0, client->clientid, row[0], atoi(row[3])); + } + if(row[4]) { + bind_botwise_set_channel_access(0, client->clientid, row[0], row[4]); + } + } + } + bind_botwise_unbound_required_functions(0, client->clientid); + } + } else { + //remove the bot + struct ClientSocket *client; + for(client = getBots(0, NULL); client; client = getBots(0, client)) { + if(client->clientid == atoi(bot[15])) { + unbind_botwise_allcmd(0, client->clientid); + close_socket(client); + break; + } + } + } + printf_mysql_query("UPDATE `bots` SET `active` = '%d' WHERE `id` = '%s'", val, bot[15]); + ret = 1; + } + } + reply(textclient, user, "\002ACTIVE \002 %s", get_language_string(user, (val ? "NS_SET_ON" : "NS_SET_OFF"))); + return ret; +} + +static int global_cmd_setbot_nick(struct ClientSocket *textclient, struct UserNode *user, MYSQL_ROW bot, char *value) { + char *val = bot[1]; + int ret = 0; + if(value) { + if(!is_valid_nick(value)) { + reply(textclient, user, "NS_SETBOT_NICK_INVALID", value); + return 0; + } + //rename the bot + struct ClientSocket *client; + for(client = getBots(0, NULL); client; client = getBots(0, client)) { + if(client->clientid == atoi(bot[15])) { + if(client->nick) + free(client->nick); + client->nick = strdup(value); + if(client->flags & SOCKET_FLAG_READY) + putsock(client, "NICK %s", value); + break; + } + } + printf_mysql_query("UPDATE `bots` SET `nick` = '%s' WHERE `id` = '%s'", escape_string(value), bot[15]); + val = value; + ret = 1; + } + reply(textclient, user, "\002NICK \002 %s", val); + return ret; +} + +static int global_cmd_setbot_ident(struct ClientSocket *textclient, struct UserNode *user, MYSQL_ROW bot, char *value) { + char *val = bot[12]; + int ret = 0; + if(value) { + if(strlen(value) > 12) + value[12] = '\0'; + //rename the bot + struct ClientSocket *client; + for(client = getBots(0, NULL); client; client = getBots(0, client)) { + if(client->clientid == atoi(bot[15])) { + if(client->ident) + free(client->ident); + client->ident = strdup(value); + break; + } + } + printf_mysql_query("UPDATE `bots` SET `ident` = '%s' WHERE `id` = '%s'", escape_string(value), bot[15]); + val = value; + ret = 1; + } + reply(textclient, user, "\002IDENT \002 %s", val); + return ret; +} + +static int global_cmd_setbot_realname(struct ClientSocket *textclient, struct UserNode *user, MYSQL_ROW bot, char *value) { + char *val = bot[13]; + int ret = 0; + if(value) { + if(strlen(value) > 255) + value[255] = '\0'; + //rename the bot + struct ClientSocket *client; + for(client = getBots(0, NULL); client; client = getBots(0, client)) { + if(client->clientid == atoi(bot[15])) { + if(client->ident) + free(client->ident); + client->ident = strdup(value); + break; + } + } + printf_mysql_query("UPDATE `bots` SET `realname` = '%s' WHERE `id` = '%s'", escape_string(value), bot[15]); + val = value; + ret = 1; + } + reply(textclient, user, "\002REALNAME \002 %s", val); + return ret; +} + +static int global_cmd_setbot_server(struct ClientSocket *textclient, struct UserNode *user, MYSQL_ROW bot, char *value) { + char *val = bot[2]; + int ret = 0; + if(value) { + struct ClientSocket *client; + for(client = getBots(0, NULL); client; client = getBots(0, client)) { + if(client->clientid == atoi(bot[15])) { + if(client->host) + free(client->host); + client->host = strdup(value); + if(client->flags & SOCKET_FLAG_READY) + reply(textclient, user, "NS_SETBOT_NEED_RESTART"); + break; + } + } + printf_mysql_query("UPDATE `bots` SET `server` = '%s' WHERE `id` = '%s'", escape_string(value), bot[15]); + val = value; + ret = 1; + } + reply(textclient, user, "\002SERVER \002 %s", val); + return ret; +} + +static int global_cmd_setbot_port(struct ClientSocket *textclient, struct UserNode *user, MYSQL_ROW bot, char *value) { + int val = atoi(bot[3]); + int ret = 0; + if(value) { + val = atoi(value); + if(val <= 0 || val > 65534) { + reply(textclient, user, "NS_SETBOT_PORT_INVALID", value); + return 0; + } + struct ClientSocket *client; + for(client = getBots(0, NULL); client; client = getBots(0, client)) { + if(client->clientid == atoi(bot[15])) { + client->port = val; + if(client->flags & SOCKET_FLAG_READY) + reply(textclient, user, "NS_SETBOT_NEED_RESTART"); + break; + } + } + printf_mysql_query("UPDATE `bots` SET `port` = '%d' WHERE `id` = '%s'", val, bot[15]); + ret = 1; + } + reply(textclient, user, "\002PORT \002 %d", val); + return ret; +} + +static int global_cmd_setbot_bind(struct ClientSocket *textclient, struct UserNode *user, MYSQL_ROW bot, char *value) { + char *val = bot[11]; + int ret = 0; + if(value) { + if(!strcmp(value, "*")) + value = NULL; + struct ClientSocket *client; + for(client = getBots(0, NULL); client; client = getBots(0, client)) { + if(client->clientid == atoi(bot[15])) { + if(client->bind) + free(client->bind); + client->bind = (value ? strdup(value) : NULL); + if(client->flags & SOCKET_FLAG_READY) + reply(textclient, user, "NS_SETBOT_NEED_RESTART"); + break; + } + } + if(value) + printf_mysql_query("UPDATE `bots` SET `bind` = '%s' WHERE `id` = '%s'", escape_string(value), bot[15]); + else + printf_mysql_query("UPDATE `bots` SET `bind` = NULL WHERE `id` = '%s'", bot[15]); + val = value; + ret = 1; + } + reply(textclient, user, "\002BIND \002 %s", val); + return ret; +} + +static int global_cmd_setbot_ssl(struct ClientSocket *textclient, struct UserNode *user, MYSQL_ROW bot, char *value) { + int val = (strcmp(bot[14], "0") ? 1 : 0); + int ret = 0; + if(value) { + if(!strcmp(value, "0") || !stricmp(value, "off") || !stricmp(value, get_language_string(user, "NS_SET_OFF"))) { + val = 0; + } else if(!strcmp(value, "1") || !stricmp(value, "on") || !stricmp(value, get_language_string(user, "NS_SET_ON"))) { + val = 1; + } else { + reply(textclient, user, "NS_SET_INVALID_BOOLEAN", value); + return 0; + } + struct ClientSocket *client; + for(client = getBots(0, NULL); client; client = getBots(0, client)) { + if(client->clientid == atoi(bot[15])) { + if(val) + client->flags |= SOCKET_FLAG_SSL; + else + client->flags &= ~SOCKET_FLAG_SSL; + if(client->flags & SOCKET_FLAG_READY) + reply(textclient, user, "NS_SETBOT_NEED_RESTART"); + break; + } + } + printf_mysql_query("UPDATE `bots` SET `ssl` = '%d' WHERE `id` = '%s'", val, bot[15]); + ret = 1; + } + reply(textclient, user, "\002SSL \002 %s", get_language_string(user, (val ? "NS_SET_ON" : "NS_SET_OFF"))); + return ret; +} + +static int global_cmd_setbot_serverpass(struct ClientSocket *textclient, struct UserNode *user, MYSQL_ROW bot, char *value) { + char *val = bot[4]; + int ret = 0; + if(value) { + if(!strcmp(value, "*")) + value = ""; + struct ClientSocket *client; + for(client = getBots(0, NULL); client; client = getBots(0, client)) { + if(client->clientid == atoi(bot[15])) { + if(client->pass) + free(client->pass); + client->pass = (value ? strdup(value) : NULL); + if(client->flags & SOCKET_FLAG_READY) + reply(textclient, user, "NS_SETBOT_NEED_RESTART"); + break; + } + } + printf_mysql_query("UPDATE `bots` SET `pass` = '%s' WHERE `id` = '%s'", escape_string(value), bot[15]); + val = value; + ret = 1; + } + reply(textclient, user, "\002SERVERPASS \002 %s", val); + return ret; +} + +static int global_cmd_setbot_class(struct ClientSocket *textclient, struct UserNode *user, MYSQL_ROW bot, char *value) { + int val = atoi(bot[5]); + int ret = 0; + if(value) { + if((val = resolve_botalias(value)) == -1) { + reply(textclient, user, "NS_SETBOT_INVALID_CLASS", value); + return 0; + } + if(val != atoi(bot[5])) { + struct ClientSocket *client; + for(client = getBots(0, NULL); client; client = getBots(0, client)) { + if(client->clientid == atoi(bot[15])) { + unbind_botwise_allcmd(0, client->clientid); + client->botid = val; + if(client->botid == 0) { + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `command`, `function`, `parameters`, `global_access`, `chan_access` FROM `bot_binds` WHERE `botclass` = '0' AND `botid` = '%d'", client->clientid); + res = mysql_use(); + while ((row = mysql_fetch_row(res)) != NULL) { + if(bind_botwise_cmd_to_command(client->botid, client->clientid, row[0], row[1])) { + if(row[2] && strcmp(row[2], "")) { + bind_botwise_set_parameters(client->botid, client->clientid, row[0], row[2]); + } + if(row[3]) { + bind_botwise_set_global_access(client->botid, client->clientid, row[0], atoi(row[3])); + } + if(row[4]) { + bind_botwise_set_channel_access(client->botid, client->clientid, row[0], row[4]); + } + } + } + bind_botwise_unbound_required_functions(client->botid, client->clientid); + } + break; + } + } + printf_mysql_query("UPDATE `bots` SET `botclass` = '%d' WHERE `id` = '%s'", val, bot[15]); + ret = 1; + } + } + reply(textclient, user, "\002BOTCLASS \002 %s", resolve_botid(val)); + return ret; +} + +static int global_cmd_setbot_queue(struct ClientSocket *textclient, struct UserNode *user, MYSQL_ROW bot, char *value) { + int val = (strcmp(bot[7], "0") ? 1 : 0); + int ret = 0; + if(value) { + if(!strcmp(value, "0") || !stricmp(value, "off") || !stricmp(value, get_language_string(user, "NS_SET_OFF"))) { + val = 0; + } else if(!strcmp(value, "1") || !stricmp(value, "on") || !stricmp(value, get_language_string(user, "NS_SET_ON"))) { + val = 1; + } else { + reply(textclient, user, "NS_SET_INVALID_BOOLEAN", value); + return 0; + } + struct ClientSocket *client; + for(client = getBots(0, NULL); client; client = getBots(0, client)) { + if(client->clientid == atoi(bot[15])) { + if(val) + client->flags |= SOCKET_FLAG_USE_QUEUE; + else + client->flags &= ~SOCKET_FLAG_USE_QUEUE; + break; + } + } + printf_mysql_query("UPDATE `bots` SET `queue` = '%d' WHERE `id` = '%s'", val, bot[15]); + ret = 1; + } + reply(textclient, user, "\002QUEUE \002 %s", get_language_string(user, (val ? "NS_SET_ON" : "NS_SET_OFF"))); + return ret; +} + +static int global_cmd_setbot_prefered(struct ClientSocket *textclient, struct UserNode *user, MYSQL_ROW bot, char *value) { + int val = (strcmp(bot[6], "0") ? 1 : 0); + int ret = 0; + if(value) { + if(!strcmp(value, "0") || !stricmp(value, "off") || !stricmp(value, get_language_string(user, "NS_SET_OFF"))) { + val = 0; + } else if(!strcmp(value, "1") || !stricmp(value, "on") || !stricmp(value, get_language_string(user, "NS_SET_ON"))) { + val = 1; + } else { + reply(textclient, user, "NS_SET_INVALID_BOOLEAN", value); + return 0; + } + struct ClientSocket *client; + for(client = getBots(0, NULL); client; client = getBots(0, client)) { + if(client->clientid == atoi(bot[15])) { + if(val) + client->flags |= SOCKET_FLAG_PREFERRED; + else + client->flags &= ~SOCKET_FLAG_PREFERRED; + break; + } + } + printf_mysql_query("UPDATE `bots` SET `textbot` = '%d' WHERE `id` = '%s'", val, bot[15]); + ret = 1; + } + reply(textclient, user, "\002PREFERED \002 %s", get_language_string(user, (val ? "NS_SET_ON" : "NS_SET_OFF"))); + return ret; +} + +static int global_cmd_setbot_secret(struct ClientSocket *textclient, struct UserNode *user, MYSQL_ROW bot, char *value) { + int val = (strcmp(bot[16], "0") ? 1 : 0); + int ret = 0; + if(value) { + if(!strcmp(value, "0") || !stricmp(value, "off") || !stricmp(value, get_language_string(user, "NS_SET_OFF"))) { + val = 0; + } else if(!strcmp(value, "1") || !stricmp(value, "on") || !stricmp(value, get_language_string(user, "NS_SET_ON"))) { + val = 1; + } else { + reply(textclient, user, "NS_SET_INVALID_BOOLEAN", value); + return 0; + } + struct ClientSocket *client; + for(client = getBots(0, NULL); client; client = getBots(0, client)) { + if(client->clientid == atoi(bot[15])) { + if(val) + client->flags |= SOCKET_FLAG_SECRET_BOT; + else + client->flags &= ~SOCKET_FLAG_SECRET_BOT; + break; + } + } + printf_mysql_query("UPDATE `bots` SET `secret` = '%d' WHERE `id` = '%s'", val, bot[15]); + ret = 1; + } + reply(textclient, user, "\002SECRET \002 %s", get_language_string(user, (val ? "NS_SET_ON" : "NS_SET_OFF"))); + return ret; +} + +static int global_cmd_setbot_maxchan(struct ClientSocket *textclient, struct UserNode *user, MYSQL_ROW bot, char *value) { + int val = atoi(bot[9]); + int ret = 0; + if(value) { + val = atoi(value); + if(val < 0 || val > 99999) { + reply(textclient, user, "NS_SETBOT_MAXCHAN_INVALID", value); + return 0; + } + printf_mysql_query("UPDATE `bots` SET `max_channels` = '%d' WHERE `id` = '%s'", val, bot[15]); + ret = 1; + } + reply(textclient, user, "\002MAXCHAN \002 %d", val); + return ret; +} + +static int global_cmd_setbot_priority(struct ClientSocket *textclient, struct UserNode *user, MYSQL_ROW bot, char *value) { + int val = atoi(bot[10]); + int ret = 0; + if(value) { + val = atoi(value); + if(val < 0 || val > 99) { + reply(textclient, user, "NS_SETBOT_PRIORITY_INVALID", value); + return 0; + } + printf_mysql_query("UPDATE `bots` SET `register_priority` = '%d' WHERE `id` = '%s'", val, bot[15]); + ret = 1; + } + reply(textclient, user, "\002PRIORITY \002 %d", val); + return ret; +} + +static int global_cmd_setbot_trigger(struct ClientSocket *textclient, struct UserNode *user, MYSQL_ROW bot, char *value) { + char *val = bot[8]; + int ret = 0; + if(value) { + if(!*value || strlen(value) > 10) { + reply(textclient, user, "NS_SETBOT_TRIGGER_INVALID", value); + return 0; + } + printf_mysql_query("UPDATE `bots` SET `defaulttrigger` = '%s' WHERE `id` = '%s'", escape_string(value), bot[15]); + flush_trigger_cache(atoi(bot[5]), atoi(bot[15])); + reply(textclient, user, "NS_SETBOT_TRIGGER_NOTE"); + val = value; + ret = 1; + } + reply(textclient, user, "\002TRIGGER \002 %s", val); + return ret; +} diff --git a/src/modules/global.mod/cmd_global_staff.c b/src/modules/global.mod/cmd_global_staff.c new file mode 100644 index 0000000..73c7b3a --- /dev/null +++ b/src/modules/global.mod/cmd_global_staff.c @@ -0,0 +1,52 @@ +/* cmd_global_staff.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_global.h" + +/* +* no arguments +*/ + +CMD_BIND(global_cmd_staff) { + MYSQL_RES *res, *res2; + MYSQL_ROW row, row2; + printf_mysql_query("SELECT `rank_id`, `rank_name` FROM `support_ranks` ORDER BY `rank_order` ASC"); + res = mysql_use(); + while ((row = mysql_fetch_row(res)) != NULL) { + printf_mysql_query("SELECT `user_user`, `user_god` FROM `users` WHERE `user_rank` = '%s'", row[0]); + res2 = mysql_use(); + if(mysql_num_rows(res2)) { + reply(textclient, user, "\002%s\002", row[1]); + while ((row2 = mysql_fetch_row(res2)) != NULL) { + if(strcmp(row2[1], "0")) { + //god enabled - show nicks + char loggedinBuf[MAXLEN]; + int loggedinPos = 0; + struct UserNode *cuser; + for(cuser = getUsersWithAuth(row2[0], NULL); cuser; cuser = getUsersWithAuth(row2[0], cuser)) { + loggedinPos += sprintf(loggedinBuf+loggedinPos, (loggedinPos ? ", %s" : "%s"), cuser->nick); + } + if(loggedinPos) + reply(textclient, user, " %s (%s: %s)", row2[0], get_language_string(user, "NS_STAFF_LOGGEDIN"), loggedinBuf); + else + reply(textclient, user, " %s", row2[0]); + } else + reply(textclient, user, " %s", row2[0]); + } + } + } +} diff --git a/src/modules/global.mod/cmd_global_unbind.c b/src/modules/global.mod/cmd_global_unbind.c new file mode 100644 index 0000000..14e724a --- /dev/null +++ b/src/modules/global.mod/cmd_global_unbind.c @@ -0,0 +1,54 @@ +/* cmd_global_unbind.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_global.h" + +/* +* argv[0] command name +*/ + +CMD_BIND(global_cmd_unbind) { + MYSQL_RES *res; + MYSQL_ROW row; + struct cmd_binding *cbind = find_botwise_cmd_binding(client->botid, client->clientid, argv[0]); + if(client->botid == 0) + printf_mysql_query("SELECT `id`, `function` FROM `bot_binds` WHERE `botclass` = '0' AND `botid` = '%d' AND `command` = '%s'", client->clientid, escape_string(argv[0])); + else + printf_mysql_query("SELECT `id`, `function` FROM `bot_binds` WHERE `botclass` = '%d' AND `command` = '%s'", client->botid, escape_string(argv[0])); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) == NULL && (!cbind || !(cbind->flags & CMDFLAG_TEMPONARY_BIND))) { + reply(textclient, user, "NS_UNBIND_NOT_FOUND", argv[0]); + return; + } + struct cmd_function *function = find_cmd_function(client->botid, cbind->func->name); + if(function && (function->flags & CMDFLAG_REQUIRED)) { + if(client->botid == 0) + printf_mysql_query("SELECT `id` FROM `bot_binds` WHERE `botclass` = '0' AND `botid` = '%d' AND `function` = '%s'", client->clientid, escape_string(function->name)); + else + printf_mysql_query("SELECT `id` FROM `bot_binds` WHERE `botclass` = '%d' AND `function` = '%s'", client->botid, escape_string(function->name)); + res = mysql_use(); + if (mysql_num_rows(res) <= 1) { + reply(textclient, user, "NS_UNBIND_REQUIRED", function->name); + return; + } + } + unbind_botwise_cmd(client->botid, client->clientid, argv[0]); + if(!cbind || !(cbind->flags & CMDFLAG_TEMPONARY_BIND)) + printf_mysql_query("DELETE FROM `bot_binds` WHERE `id` = '%s'", row[0]); + reply(textclient, user, "NS_UNBIND_DONE", argv[0]); + logEvent(event); +} diff --git a/src/modules/global.mod/cmd_global_unregister.c b/src/modules/global.mod/cmd_global_unregister.c new file mode 100644 index 0000000..df7b1b5 --- /dev/null +++ b/src/modules/global.mod/cmd_global_unregister.c @@ -0,0 +1,118 @@ +/* cmd_global_unregister.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_global.h" +#include "../botid.h" + +/* +* argv[0] - channel +*/ +CMD_BIND(global_cmd_unregister) { + MYSQL_RES *res; + MYSQL_ROW row; + char *channel; + if(argc) + channel = argv[0]; + else + channel = (chan ? chan->name : ""); + if(!is_valid_chan(channel)) { + reply(textclient, user, "NS_INVALID_CHANNEL_NAME", channel); + return; + } + int chanid; + printf_mysql_query("SELECT `channel_id`, `channel_nodelete` FROM `channels` WHERE `channel_name` = '%s'", escape_string(channel)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + chanid = atoi(row[0]); + } else { + reply(textclient, user, "NS_UNREGISTER_NOT_REGISTERED", channel, client->user->nick); + return; + } + if(client->botid == NEONSERV_BOTID && !strcmp(row[1], "1")) { + reply(textclient, user, "NS_UNREGISTER_NODELETE", channel); + return; + } + int sync_neonspam_unreg = get_int_field("General.sync_neonspam_unreg"); + if(client->botid == 0) + printf_mysql_query("SELECT `botid`, `bot_channels`.`id`, `suspended` FROM `bot_channels` LEFT JOIN `bots` ON `bot_channels`.`botid` = `bots`.`id` WHERE `chanid` = '%d' AND `botclass` = '0' AND `botid` = '%d'", chanid, client->clientid); + else + printf_mysql_query("SELECT `botid`, `bot_channels`.`id`, `suspended` FROM `bot_channels` LEFT JOIN `bots` ON `bot_channels`.`botid` = `bots`.`id` WHERE `chanid` = '%d' AND `botclass` = '%d'", chanid, client->botid); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) == NULL) { + reply(textclient, user, "NS_UNREGISTER_NOT_REGISTERED", channel, client->user->nick); + return; + } + int botid = atoi(row[0]); + struct ClientSocket *bot; + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(bot->clientid == botid) + break; + } + printf_mysql_query("DELETE FROM `bot_channels` WHERE `id` = '%s'", row[1]); + reply(textclient, user, "NS_UNREGISTER_DONE", channel); + if(bot && strcmp(row[2], "1")) { + putsock(bot, "PART %s :Channel unregistered.", channel); + } + if(client->botid == NEONSERV_BOTID && sync_neonspam_unreg) { + printf_mysql_query("SELECT `botid`, `bot_channels`.`id`, `suspended` FROM `bot_channels` LEFT JOIN `bots` ON `bot_channels`.`botid` = `bots`.`id` LEFT JOIN `channels` ON `chanid` = `channel_id` WHERE `channel_name` = '%s' AND `botclass` = '%d'", escape_string(channel), NEONSPAM_BOTID); + res = mysql_use(); + if ((row = mysql_fetch_row(res))) { + botid = atoi(row[0]); + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(bot->clientid == botid) + break; + } + printf_mysql_query("DELETE FROM `bot_channels` WHERE `id` = '%s'", row[1]); + if(bot && strcmp(row[2], "1")) + putsock(bot, "PART %s :Channel unregistered.", channel); + } + } + // NeonBackup SubBlock + if(client->botid == NEONSERV_BOTID) { + char setting[128]; + sprintf(setting, "modules.%s.auto_backup_unregister", get_module_name(module_id)); + if(get_int_field(setting)) + module_global_cmd_unregister_neonbackup(channel); + } + logEvent(event); +} + +void global_cmd_unregister_neonbackup(char *channel) { + MYSQL_RES *res; + MYSQL_ROW row; + int chanid; + printf_mysql_query("SELECT `channel_id` FROM `channels` WHERE `channel_name` = '%s'", escape_string(channel)); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) != NULL) { + chanid = atoi(row[0]); + } else + return; + printf_mysql_query("SELECT `botid`, `bot_channels`.`id`, `suspended` FROM `bot_channels` LEFT JOIN `bots` ON `bot_channels`.`botid` = `bots`.`id` WHERE `chanid` = '%d' AND `botclass` = '%d'", chanid, NEONBACKUP_BOTID); + res = mysql_use(); + if ((row = mysql_fetch_row(res)) == NULL) + return; + int botid = atoi(row[0]); + struct ClientSocket *bot; + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(bot->clientid == botid) + break; + } + printf_mysql_query("DELETE FROM `bot_channels` WHERE `id` = '%s'", row[1]); + if(bot && strcmp(row[2], "1")) { + putsock(bot, "PART %s :Channel unregistered.", channel); + } +} diff --git a/src/modules/global.mod/cmd_global_version.c b/src/modules/global.mod/cmd_global_version.c new file mode 100644 index 0000000..9cc3392 --- /dev/null +++ b/src/modules/global.mod/cmd_global_version.c @@ -0,0 +1,40 @@ +/* cmd_global_version.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "cmd_global.h" + +/* +* no args +*/ + +CMD_BIND(global_cmd_version) { + reply(textclient, user, "\002NeonServ %s.%d %s\002 (%s), written by pk910", NEONSERV_VERSION, get_patchlevel(), (is_stable_revision() ? "(stable)" : "(dev)"), (is_stable_revision() ? (strcmp(get_revision(), "") ? get_revision() : "-") : get_dev_revision())); + reply(textclient, user, "Build (#%s) %s (%s lines, " COMPILER ")", get_compilation(), get_creation(), get_codelines()); + reply(textclient, user, "NeonServ can be found on: http://dev.pk910.de/NeonServ"); + //helpers :D + reply(textclient, user, "special thanks to:"); + reply(textclient, user, " Testing and Ideas:"); + reply(textclient, user, " Zer0n, TeaTow, Phil, Stricted"); + reply(textclient, user, " Translations:"); + reply(textclient, user, " Buschman (de), Zer0n (de), Neon (de), TheFlie (nl), GodsWarriors (pt)"); + reply(textclient, user, " Others:"); + reply(textclient, user, " Patschi95, DerGrinch, Darkfly, Zer0n, Buschman (testing and ideas older versions)"); + reply(textclient, user, " Buschman, Georg, richard (translating older versions)"); + reply(textclient, user, "and all the other users that reported all these nasty bugs :D"); + reply(textclient, user, "\002If you found a bug or if you have a good idea report it on http://dev.pk910.de/BugTrack\002"); + +} \ No newline at end of file diff --git a/src/modules/global.mod/module.c b/src/modules/global.mod/module.c new file mode 100644 index 0000000..d000c63 --- /dev/null +++ b/src/modules/global.mod/module.c @@ -0,0 +1,33 @@ +/* module.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#include "../module.h" +#include "cmd_global.h" + +static int module_initialize() { + register_commands(); + return 0; +} + +static void module_start(int type) { + +} + +static void module_stop(int type) { + +} + +MODULE_HEADER(module_initialize, module_start, module_stop); diff --git a/src/modules/module.h b/src/modules/module.h new file mode 100644 index 0000000..4c55c40 --- /dev/null +++ b/src/modules/module.h @@ -0,0 +1,253 @@ +/* module.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _module_h +#define _module_h +#define DND_FUNCTIONS 1 /* DoNotDefine_Functions */ +#include "../overall.h" +#include "../version.h" +#include "../mysqlConn.h" +#include "../log.h" + +extern void **global; +extern int module_id; + +/**** global function list ****/ +/* 000 */ #define getStartTime ((time_t (*)(void))global[0]) +/* 001 */ #define getRunningThreads ((int (*)(void))global[1]) +/* 002 */ #define getCurrentSecondsOfDay ((int (*)(void))global[2]) +/* 003 */ #define stricmp ((int (*)(const char *, const char *))global[3]) +/* 004 */ #define stricmplen ((int (*)(const char *, const char *, int))global[4]) +#ifdef ENABLE_MUTEX_DEBUG +/* 005 */ #define xmutex ((void (*)(int, pthread_mutex_t *, const char *, unsigned int))global[005]) +/* 006 */ #define mutex_debug ((void (*)(pthread_mutex_t *))global[006]) +#endif +/* 007 */ #define restart_bot ((void (*)(int))global[7]) +/* 008 */ #define stop_bot ((void (*)(void))global[8]) +/* 009 */ #define reload_config ((void (*)(void))global[9]) +/* 010 */ #define printf_log ((void (*)(char *, int, const char *, ...))global[10]) +#ifdef HAVE_THREADS +/* 011 */ #define getCurrentThreadID ((int (*)(void))global[11]) +#endif +/* 012 */ #define getMatchingChannelBan ((struct BanNode* (*)(struct ChanNode *, char *))global[12]) +/* 013 */ #define getChannelBot ((struct ClientSocket* (*)(struct ChanNode *, int))global[13]) +/* 014 */ #define requestOp ((void (*)(struct UserNode *, struct ChanNode *))global[14]) +/* 015 */ #define channel_ban_timeout ((void (*)(void *))global[15]) +/* 016 */ #define general_event_privctcp ((void (*)(struct UserNode *, struct UserNode *, char *, char *))global[16]) +/* 017 */ #define set_bot_alias ((void (*)(int, char *))global[17]) +/* 018 */ #define resolve_botid ((const char * (*)(int))global[18]) +/* 019 */ #define resolve_botalias ((int (*)(const char *))global[19]) +/* 020 */ #define is_valid_chan ((int (*)(const char *))global[20]) +/* 021 */ #define getAllChans ((struct ChanNode* (*)(struct ChanNode *))global[21]) +/* 022 */ #define getChanByName ((struct ChanNode* (*)(const char *))global[22]) +/* 023 */ #define getChannelCount ((int (*)(void))global[23]) +/* 024 */ #define getChanUserCount ((int (*)(void))global[24]) +/* 025 */ #define getChanBanCount ((int (*)(void))global[25]) +/* 026 */ #define isUserOnChan ((int (*)(struct UserNode *, struct ChanNode *))global[26]) +/* 027 */ #define getChanUser ((struct ChanUser* (*)(struct UserNode *, struct ChanNode *))global[27]) +/* 028 */ #define getChannelUsers ((struct ChanUser* (*)(struct ChanNode *, struct ChanUser *))global[28]) +/* 029 */ #define getUserChannels ((struct ChanUser* (*)(struct UserNode *, struct ChanUser *))global[29]) +/* 030 */ #define create_socket ((struct ClientSocket* (*)(char *, int, char *, char *, char *, char *, char *))global[30]) +/* 031 */ #define connect_socket ((void (*)(struct ClientSocket *))global[31]) +/* 032 */ #define close_socket ((int (*)(struct ClientSocket *))global[32]) +/* 033 */ #define destroy_socket ((int (*)(struct ClientSocket *))global[33]) +/* 034 */ #define write_socket ((int (*)(struct ClientSocket *, char*, int))global[34]) +/* 035 */ #define putsock ((void (*)(struct ClientSocket *, const char *, ...))global[35]) +/* 036 */ #define getBots ((struct ClientSocket* (*)(int, struct ClientSocket *))global[36]) +/* 037 */ #define get_int_field ((int (*)(char *))global[37]) +/* 038 */ #define get_string_field ((char * (*)(char *))global[38]) +/* 039 */ #define _loadUserSettings ((void (*)(struct UserNode *))global[39]) +/* 040 */ #define isGodMode ((int (*)(struct UserNode *))global[40]) +/* 041 */ #define getChanDefault ((char * (*)(char *))global[41]) +/* 042 */ #define getChannelAccess ((int (*)(struct UserNode *, struct ChanNode *))global[42]) +/* 043 */ #define checkChannelAccess ((int (*)(struct UserNode *, struct ChanNode *, char *, int))global[43]) +/* 044 */ #define _loadChannelSettings ((void (*)(struct ChanNode *))global[44]) +/* 045 */ #define isUserProtected ((int (*)(struct ChanNode *, struct UserNode *, struct UserNode *))global[45]) +/* 046 */ #define getBanAffectingMask ((char * (*)(struct ChanNode *, char *))global[46]) +/* 047 */ #define renameAccount ((int (*)(char *, char *))global[47]) +/* 048 */ #define deleteUser ((void (*)(int))global[48]) +/* 049 */ #define logEvent ((void (*)(struct Event *))global[49]) +/* 050 */ #define lookup_authname ((void (*)(char *, int, authlookup_callback_t, void *))global[50]) +/* 051 */ #define bind_join ((int (*)(join_func_t *, int))global[51]) +/* 052 */ #define unbind_join ((void (*)(join_func_t *))global[52]) +/* 053 */ #define bind_nick ((int (*)(nick_func_t *, int))global[53]) +/* 054 */ #define unbind_nick ((void (*)(nick_func_t *))global[54]) +/* 055 */ #define bind_part ((int (*)(part_func_t *, int))global[55]) +/* 056 */ #define unbind_part ((void (*)(part_func_t *))global[56]) +/* 057 */ #define bind_reload ((int (*)(reload_func_t *, int))global[57]) +/* 058 */ #define unbind_reload ((void (*)(reload_func_t *))global[58]) +/* 059 */ #define bind_kick ((int (*)(kick_func_t *, int))global[59]) +/* 060 */ #define unbind_kick ((void (*)(kick_func_t *))global[60]) +/* 061 */ #define bind_topic ((int (*)(topic_func_t *, int))global[61]) +/* 062 */ #define unbind_topic ((void (*)(topic_func_t *))global[62]) +/* 063 */ #define bind_mode ((int (*)(mode_func_t *, int))global[63]) +/* 064 */ #define unbind_mode ((void (*)(mode_func_t *))global[64]) +/* 065 */ #define bind_chanmsg ((int (*)(chanmsg_func_t *, int))global[65]) +/* 066 */ #define unbind_chanmsg ((void (*)(chanmsg_func_t *))global[66]) +/* 067 */ #define bind_privmsg ((int (*)(privmsg_func_t *, int))global[67]) +/* 068 */ #define unbind_privmsg ((void (*)(privmsg_func_t *))global[68]) +/* 069 */ #define bind_channotice ((int (*)(channotice_func_t *, int))global[69]) +/* 070 */ #define unbind_channotice ((void (*)(channotice_func_t *))global[70]) +/* 071 */ #define bind_privnotice ((int (*)(privnotice_func_t *, int))global[71]) +/* 072 */ #define unbind_privnotice ((void (*)(privnotice_func_t *))global[72]) +/* 073 */ #define bind_chanctcp ((int (*)(chanctcp_func_t *, int))global[73]) +/* 074 */ #define unbind_chanctcp ((void (*)(chanctcp_func_t *))global[74]) +/* 075 */ #define bind_privctcp ((int (*)(privctcp_func_t *, int))global[75]) +/* 076 */ #define unbind_privctcp ((void (*)(privctcp_func_t *))global[76]) +/* 077 */ #define bind_invite ((int (*)(invite_func_t *, int))global[77]) +/* 078 */ #define unbind_invite ((void (*)(invite_func_t *))global[78]) +/* 079 */ #define bind_raw ((int (*)(raw_func_t *, int))global[79]) +/* 080 */ #define unbind_raw ((void (*)(raw_func_t *))global[80]) +/* 081 */ #define bind_bot_ready ((int (*)(bot_ready_func_t *, int))global[81]) +/* 082 */ #define unbind_bot_ready ((void (*)(bot_ready_func_t *))global[82]) +/* 083 */ #define bind_registered ((int (*)(registered_func_t *, int))global[83]) +/* 084 */ #define unbind_registered ((void (*)(registered_func_t *))global[84]) +/* 085 */ #define bind_freeuser ((int (*)(freeuser_func_t *, int))global[85]) +/* 086 */ #define unbind_freeuser ((void (*)(freeuser_func_t *))global[86]) +/* 087 */ #define bind_freechan ((int (*)(freechan_func_t *, int))global[87]) +/* 088 */ #define unbind_freechan ((void (*)(freechan_func_t *))global[88]) +/* 089 */ #define reply ((void (*)(struct ClientSocket *, struct UserNode *, const char *, ...))global[89]) +/* 090 */ #define merge_argv ((char * (*)(char **, int, int))global[90]) +/* 091 */ #define merge_argv_char ((char * (*)(char **, int, int, char))global[91]) +/* 092 */ #define get_language_by_tag ((struct language * (*)(char *))global[92]) +/* 093 */ #define get_language_by_name ((struct language * (*)(char *))global[93]) +/* 094 */ #define get_default_language ((struct language * (*)(void))global[94]) +/* 095 */ #define load_language ((void (*)(char *, char *))global[95]) +/* 096 */ #define register_default_language_table ((void (*)(const struct default_language_entry *))global[96]) +/* 097 */ #define get_language_string ((char * (*)(struct UserNode *, const char *))global[97]) +/* 098 */ #define build_language_string ((char * (*)(struct UserNode *, char *, const char *, ...))global[98]) +#ifdef ENABLE_MEMORY_DEBUG +/* 099 */ #define xmalloc ((void * (*)(unsigned int, const char *, unsigned int))global[99]) +/* 100 */ #define xcalloc ((void * (*)(unsigned int, unsigned int, const char *, unsigned int))global[100]) +/* 101 */ #define xstrdup ((char * (*)(const char *, const char *, unsigned int))global[101]) +/* 102 */ #define xfree ((void (*)(void *))global[102]) +#endif +/* 103 */ #define getMemoryInfoFiles ((struct memoryInfoFiles * (*)(void))global[103]) +/* 104 */ #define freeMemoryInfoFiles ((void (*)(struct memoryInfoFiles *))global[104]) +/* 105 */ #define getMemoryInfoLines ((struct memoryInfoLines * (*)(const char *))global[105]) +/* 106 */ #define freeMemoryInfoLines ((void (*)(struct memoryInfoLines *))global[106]) +/* 107 */ #define get_botwise_prefered_bot ((struct ClientSocket * (*)(int, int))global[107]) +/* 108 */ #define register_command ((int (*)(int, char *, int, cmd_bind_t *, int, char *, int, unsigned int))global[108]) +/* 109 */ #define set_trigger_callback ((int (*)(int, int, trigger_callback_t *))global[109]) +/* 110 */ #define flush_trigger_cache ((int (*)(int, int))global[110]) +/* 111 */ #define changeBotwiseChannelTrigger ((int (*)(int, int, struct ChanNode *, char *))global[111]) +/* 112 */ #define bind_botwise_cmd_to_function ((int (*)(int, int, char *, struct cmd_function *))global[112]) +/* 113 */ #define bind_botwise_cmd_to_command ((int (*)(int, int, char *, char *))global[113]) +/* 114 */ #define unbind_botwise_cmd ((int (*)(int, int, char *))global[114]) +/* 115 */ #define unbind_botwise_allcmd ((int (*)(int, int))global[115]) +/* 116 */ #define bind_botwise_set_parameters ((void (*)(int, int, char *, char *))global[116]) +/* 117 */ #define bind_botwise_set_global_access ((void (*)(int, int, char *, int))global[117]) +/* 118 */ #define bind_botwise_set_channel_access ((void (*)(int, int, char *, char *))global[118]) +/* 119 */ #define bind_botwise_set_bind_flags ((void (*)(int, int, char *, unsigned int))global[119]) +/* 120 */ #define find_botwise_cmd_binding ((struct cmd_binding * (*)(int, int, char *))global[120]) +/* 121 */ #define bind_botwise_unbound_required_functions ((void (*)(int, int))global[121]) +/* 122 */ #define find_cmd_function ((struct cmd_function * (*)(int , char *))global[122]) +/* 123 */ /* deprecated */ +/* 124 */ #define register_command_alias ((void (*)(int, char *))global[124]) +/* 125 */ #define getAllBinds ((struct cmd_binding * (*)(struct cmd_binding *))global[125]) +/* 126 */ #define createModeNode ((struct ModeNode * (*)(struct ChanNode *))global[126]) +/* 127 */ #define freeModeNode ((void (*)(struct ModeNode *))global[127]) +/* 128 */ #define isModeSet ((int (*)(struct ModeNode *, char))global[128]) +/* 129 */ #define isModeAffected ((int (*)(struct ModeNode *, char))global[129]) +/* 130 */ #define getModeValue ((void * (*)(struct ModeNode *, char))global[130]) +/* 131 */ #define getModeType ((unsigned int (*)(struct ModeNode *, char))global[131]) +/* 132 */ #define parseModes ((void (*)(struct ModeNode *, char *, char **, int))global[132]) +/* 133 */ #define parseModeString ((void (*)(struct ModeNode *, char *))global[133]) +/* 134 */ #define parseMode ((int (*)(struct ModeNode *, int, char, char *))global[134]) +/* 135 */ #define getModeString ((void (*)(struct ModeNode *, char *))global[135]) +/* 136 */ #define getFullModeString ((void (*)(struct ModeNode *, char *))global[136]) +/* 137 */ #define mysql_use ((MYSQL_RES * (*)(void))global[137]) +/* 138 */ #define mysql_free ((void (*)(void))global[138]) +/* 139 */ #define printf_mysql_query ((void (*)(const char *, ...))global[139]) +/* 140 */ #define printf_long_mysql_query ((void (*)(int, const char *, ...))global[140]) +/* 141 */ #define escape_string ((char * (*)(const char *))global[141]) +/* 142 */ #define get_mysql_conn ((MYSQL * (*)(void))global[142]) +/* 143 */ #define timeq_add ((struct timeq_entry * (*)(int, int, timeq_callback_t *, void *))global[143]) +/* 144 */ #define timeq_uadd ((struct timeq_entry * (*)(int, int, timeq_callback_t *, void *))global[144]) +/* 145 */ #define timeq_add_name ((struct timeq_entry * (*)(char *, int, int, timeq_callback_t *, void *))global[145]) +/* 146 */ #define timeq_uadd_name ((struct timeq_entry * (*)(char *, int, int, timeq_callback_t *, void *))global[146]) +/* 147 */ #define timeq_del ((int (*)(struct timeq_entry *))global[147]) +/* 148 */ #define timeq_del_name ((int (*)(char *))global[148]) +/* 149 */ #define timeq_name_exists ((int (*)(char *))global[149]) +/* 150 */ #define match ((int (*)(const char *, const char *))global[150]) +/* 151 */ #define table_init ((struct Table * (*)(int, int, int))global[151]) +/* 152 */ #define table_add ((int (*)(struct Table *, char **))global[152]) +/* 153 */ #define table_change ((int (*)(struct Table *, int, char **))global[153]) +/* 154 */ #define table_change_field ((int (*)(struct Table *, int, int, char *))global[154]) +/* 155 */ #define table_set_bold ((int (*)(struct Table *, int, int))global[155]) +/* 156 */ #define table_end ((char ** (*)(struct Table *))global[156]) +/* 157 */ #define table_free ((void (*)(struct Table *))global[157]) +/* 158 */ #define timeToStr ((char * (*)(struct UserNode *, int, int, char *))global[158]) +/* 159 */ #define strToTime ((int (*)(struct UserNode *, char *))global[159]) +/* 160 */ #define initModeBuffer ((struct ModeBuffer * (*)(struct ClientSocket *, struct ChanNode *))global[160]) +/* 161 */ #define modeBufferSet ((void (*)(struct ModeBuffer *, int, char, char *))global[161]) +/* 162 */ #define flushModeBuffer ((void (*)(struct ModeBuffer *))global[162]) +/* 163 */ #define freeModeBuffer ((void (*)(struct ModeBuffer *))global[163]) +/* 164 */ #define is_ircmask ((int (*)(const char *))global[164]) +/* 165 */ #define generate_banmask ((char * (*)(struct UserNode *, char *))global[165]) +/* 166 */ #define make_banmask ((char * (*)(char *, char *))global[166]) +/* 167 */ #define isFakeHost ((int (*)(char *))global[167]) +/* 168 */ #define mask_match ((int (*)(char *, struct UserNode *))global[168]) +/* 169 */ #define crc32 ((unsigned long (*)(const char *))global[169]) +/* 170 */ #define is_valid_nick ((int (*)(const char *))global[170]) +/* 171 */ #define getUserByNick ((struct UserNode * (*)(const char *))global[171]) +/* 172 */ #define getUserByMask ((struct UserNode * (*)(const char *))global[172]) +/* 173 */ #define countUsersWithHost ((int (*)(char *))global[173]) +/* 174 */ #define getAuthFakehost ((char * (*)(char *))global[174]) +/* 175 */ #define searchUserByNick ((struct UserNode * (*)(const char *))global[175]) +/* 176 */ #define getAllUsers ((struct UserNode * (*)(struct UserNode *))global[176]) +/* 177 */ #define getUsersWithAuth ((struct UserNode * (*)(const char *, struct UserNode *))global[177]) +/* 178 */ #define getUserCount ((int (*)(void))global[178]) +/* 179 */ #define createTempUser ((struct UserNode * (*)(const char *))global[179]) +/* 180 */ #define createTempUserMask ((struct UserNode * (*)(const char *))global[180]) +/* 181 */ #define get_userlist ((void (*)(struct ChanNode *, int, userlist_callback_t, void *))global[181]) +/* 182 */ #define _get_userlist_with_invisible ((void (*)(struct ChanNode *, int, userlist_callback_t, void *, int))global[182]) +/* 183 */ #define get_userauth ((void (*)(struct UserNode *, int, userauth_callback_t, void *))global[183]) +/* 184 */ #define get_compilation ((const char * (*)(void))global[184]) +/* 185 */ #define get_creation ((const char * (*)(void))global[185]) +/* 186 */ #define get_revision ((const char * (*)(void))global[186]) +/* 187 */ #define get_codelines ((const char * (*)(void))global[187]) +/* 188 */ #define get_patchlevel ((const int (*)(void))global[188]) +/* 189 */ #define get_module_name ((char * (*)(int))global[189]) +/* 190 */ #define isUserModeSet ((int (*)(struct UserNode *, char))global[190]) +/* 191 */ #define module_global_cmd_register_neonbackup ((void (*)(char *))global[191]) +/* 192 */ #define module_global_cmd_unregister_neonbackup ((void (*)(char *))global[192]) +/* 193 */ #define module_neonbackup_recover_chan ((void (*)(struct ChanNode *))global[193]) +/* 194 */ #define requestInvite ((void (*)(struct UserNode *, struct ChanNode *))global[194]) +/* 195 */ #define is_stable_revision ((const int (*)(void))global[195]) +/* 196 */ #define get_dev_revision ((const char * (*)(void))global[196]) +/* 197 */ #define bind_freeclient ((int (*)(freeclient_func_t *, int))global[197]) +/* 198 */ #define unbind_freeclient ((void (*)(freeclient_func_t *))global[198]) + +#define MODULE_HEADER(initfunc,startfunc,stopfunc) \ + void **global = NULL; \ + int module_id = 0; \ + int init_module(void **functions, int modid) { \ + global = functions; \ + module_id = modid; \ + return initfunc(); \ + } \ + void start_module(int type) { \ + startfunc(type); \ + } \ + void stop_module(int type) { \ + stopfunc(type); \ + } \ + int modversion() { \ + return MODULE_VERSION; \ + } + +#endif \ No newline at end of file diff --git a/src/modules/stats.mod/module.c b/src/modules/stats.mod/module.c new file mode 100644 index 0000000..4780077 --- /dev/null +++ b/src/modules/stats.mod/module.c @@ -0,0 +1,171 @@ +/* module.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#include "../module.h" +#include "../../timeq.h" +#include "../../ConfigParser.h" +#include "../../mysqlConn.h" +#include "../../ClientSocket.h" +#include "../../UserNode.h" + +#define STATS_UPDATE_SECONDS 1800 +#define STATS_UPDATE_HOST "neonserv.krypton-bouncer.de" +#define STATS_UPDATE_PORT 1675 +#define STATS_IDENTIFIER_LEN 10 +#define STATS_IDENTIFIER_POOL "abcdefghijklmnopqrstuvwxyz0123456789#*+-_.:;,!§$%&/()=?[]{}<>|@" +#define STATS_IDENTIFIER_POOL_SIZE 63 + +static TIMEQ_CALLBACK(stats_timer_callback); + +static int module_initialize() { + return 0; +} + +static void module_start(int type) { + if(!timeq_name_exists("stats")) + timeq_add_name("stats", 60, module_id, stats_timer_callback, NULL); +} + +static void module_stop(int type) { + timeq_del_name("stats"); +} + +static char *get_identifier() { + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `value` FROM `settings` WHERE `name` = 'identifier'"); + res = mysql_use(); + if(!(row = mysql_fetch_row(res))) { + srand(time(NULL)); + char identifier[STATS_IDENTIFIER_LEN+1]; + int i; + char *pool = STATS_IDENTIFIER_POOL; + for(i = 0; i < STATS_IDENTIFIER_LEN; i++) { + identifier[i] = pool[(rand() % STATS_IDENTIFIER_POOL_SIZE)]; + } + identifier[i] = 0; + printf_mysql_query("INSERT INTO `settings` (`name`, `value`) VALUES ('identifier', '%s')", escape_string(identifier)); + printf_mysql_query("SELECT `value` FROM `settings` WHERE `name` = 'identifier'"); + res = mysql_use(); + row = mysql_fetch_row(res); + if(!row) return NULL; + } + if(strlen(row[0]) < 10) return NULL; + return row[0]; +} + +static TIMEQ_CALLBACK(stats_timer_callback) { + timeq_add_name("stats", STATS_UPDATE_SECONDS, module_id, stats_timer_callback, NULL); + char tmp[200]; + char pkgbuf[1024]; + int pkgpos = 0; + char *modname = get_module_name(module_id); + char *bot_identifier = get_identifier(); + if(!bot_identifier) return; + //build update package + pkgpos += sprintf(pkgbuf + pkgpos, "nsupdate\n%s\n%s.%d %s\n", bot_identifier, NEONSERV_VERSION, get_patchlevel(), (strcmp(get_revision(), "") ? get_revision() : "-")); + sprintf(tmp, "modules/%s/hide_networkname", modname); + if(get_int_field(tmp)) + pkgpos += sprintf(pkgbuf + pkgpos, "*\n"); + else { + struct ClientSocket *bot; + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(bot->network_name) break; + } + if(bot) + pkgpos += sprintf(pkgbuf + pkgpos, "%s\n", bot->network_name); + else + pkgpos += sprintf(pkgbuf + pkgpos, "?\n"); + } + sprintf(tmp, "modules/%s/hide_botnick", modname); + if(get_int_field(tmp)) + pkgpos += sprintf(pkgbuf + pkgpos, "*\n"); + else { + sprintf(tmp, "modules/%s/use_bot", modname); + char *botname = get_string_field(tmp); + struct ClientSocket *bot, *bot1 = NULL, *bot2 = NULL, *bot3 = NULL, *bot4 = NULL; + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(botname && !stricmp(bot->nick, botname)) + bot1 = bot; + else if(bot->botid == 1 && (bot->flags & SOCKET_FLAG_PREFERRED)) + bot2 = bot; + else if((bot->flags & SOCKET_FLAG_PREFERRED)) + bot3 = bot; + else + bot4 = bot; + } + if(bot1) + bot = bot1; + else if(bot2) + bot = bot2; + else if(bot3) + bot = bot3; + else + bot = bot4; + if(bot) { + pkgpos += sprintf(pkgbuf + pkgpos, "%s!%s@%s %d\n", (bot->user ? bot->user->nick : "*"), (bot->user ? bot->user->ident : "*"), (bot->host ? bot->host : "*"), bot->port); + } else + pkgpos += sprintf(pkgbuf + pkgpos, "?\n"); + } + sprintf(tmp, "modules/%s/hide_chancount", modname); + if(get_int_field(tmp)) + pkgpos += sprintf(pkgbuf + pkgpos, "*\n"); + else { + int channel_count = getChannelCount(); + pkgpos += sprintf(pkgbuf + pkgpos, "%d\n", channel_count); + } + sprintf(tmp, "modules/%s/hide_usercount", modname); + if(get_int_field(tmp)) + pkgpos += sprintf(pkgbuf + pkgpos, "*\n"); + else { + int user_count = getUserCount(); + int chanuser_count = getChanUserCount(); + pkgpos += sprintf(pkgbuf + pkgpos, "%d %d\n", user_count, chanuser_count); + } + pkgpos += sprintf(pkgbuf + pkgpos, "%lu\n", getStartTime()); + pkgbuf[pkgpos] = 0; + //send package + #ifndef WIN32 + struct sockaddr_in si_other; + int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sock == -1) return; + memset((char *) &si_other, 0, sizeof(si_other)); + si_other.sin_family = AF_INET; + si_other.sin_port = htons(STATS_UPDATE_PORT); + if (!inet_aton(STATS_UPDATE_HOST, &si_other.sin_addr)) { + struct hostent *host = gethostbyname(STATS_UPDATE_HOST); + if (!host) return; + si_other.sin_addr = *(struct in_addr*)host->h_addr; + } + sendto(sock, pkgbuf, pkgpos, 0, &si_other, sizeof(si_other)); + #else + struct sockaddr_in si_other; + int sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sock == -1) return; + si_other.sin_family = AF_INET; + si_other.sin_port = htons(STATS_UPDATE_PORT); + si_other.sin_addr.s_addr = inet_addr(STATS_UPDATE_HOST); + if (si_other.sin_addr.s_addr == INADDR_NONE) { + struct hostent *host = gethostbyname(STATS_UPDATE_HOST); + if(!host) return; + memcpy(&(si_other.sin_addr), host->h_addr_list[0], 4); + } + sendto(sock, pkgbuf, pkgpos, 0, (struct sockaddr *) &si_other, sizeof(si_other)); + #endif + close(sock); +} + +MODULE_HEADER(module_initialize, module_start, module_stop); diff --git a/src/mutexDebug.c b/src/mutexDebug.c new file mode 100644 index 0000000..1b1e784 --- /dev/null +++ b/src/mutexDebug.c @@ -0,0 +1,190 @@ +/* mutexDebug.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#include "main.h" +#include "mutexDebug.h" + +#ifdef ENABLE_MUTEX_DEBUG + +struct MutexLockEvent { + int locked; + const char *file; + unsigned int line; + struct MutexLockEvent *next; +}; + +struct MutexLock { + int thread; + int count; + struct MutexLockEvent *first_event, *last_event; + struct MutexLock *prev, *next; +}; + +struct MutexNode { + pthread_mutex_t *mutex; + struct MutexLock *first_lock, *last_lock; + struct MutexNode *next; +}; + +static pthread_mutex_t synchronized; +static struct MutexNode *mutex_nodes = NULL; + +static struct MutexNode *getMutexNode(pthread_mutex_t *mutex, int create); +static void lockMutex(struct MutexNode *node, const char *file, unsigned int line); +static void unlockMutex(struct MutexNode *node, const char *file, unsigned int line); +static void mutex_replay(struct MutexNode *node); + +void xmutex(int lock, pthread_mutex_t *mutex, const char *file, unsigned int line) { + pthread_mutex_lock(&synchronized); + struct MutexNode *node = getMutexNode(mutex, 1); + if(lock) + lockMutex(node, file, line); + else + unlockMutex(node, file, line); + pthread_mutex_unlock(&synchronized); +} + +void initMutexDebug() { + THREAD_MUTEX_INIT(synchronized); +} + +static struct MutexNode *getMutexNode(pthread_mutex_t *mutex, int create) { + struct MutexNode *node; + for(node = mutex_nodes; node; node = node->next) { + if(node->mutex == mutex) + return node; + } + if(!create) + return NULL; + node = malloc(sizeof(*node)); + node->first_lock = NULL; + node->last_lock = NULL; + node->mutex = mutex; + node->next = mutex_nodes; + mutex_nodes = node; + return node; +} + +static void lockMutex(struct MutexNode *node, const char *file, unsigned int line) { + struct MutexLock *lock; + int thread = getCurrentThreadID(); + for(lock = node->first_lock; lock; lock = lock->next) { + if(lock->thread == thread) + break; + } + if(!lock) { + lock = malloc(sizeof(*lock)); + lock->thread = thread; + lock->count = 0; + lock->first_event = NULL; + lock->last_event = NULL; + lock->prev = node->last_lock; + lock->next = NULL; + node->last_lock = lock; + if(!node->first_lock) + node->first_lock = lock; + } + lock->count++; + //add event + struct MutexLockEvent *event = malloc(sizeof(*event)); + event->locked = 1; + event->file = file; + event->line = line; + event->next = NULL; + if(lock->last_event) { + lock->last_event->next = event; + lock->last_event = event; + } else { + lock->first_event = event; + lock->last_event = event; + } +} + +static void unlockMutex(struct MutexNode *node, const char *file, unsigned int line) { + struct MutexLock *lock; + int thread = getCurrentThreadID(); + for(lock = node->first_lock; lock; lock = lock->next) { + if(lock->thread == thread) + break; + } + if(!lock) + return; + lock->count--; + if(lock->count <= 0) { + //remove lock + if(lock->prev) + lock->prev->next = lock->next; + else + node->first_lock = lock->next; + if(lock->next) + lock->next->prev = lock->prev; + else + node->last_lock = lock->prev; + //recursive free all events + struct MutexLockEvent *event, *next_event; + for(event = lock->first_event; event; event = next_event) { + next_event = event->next; + free(event); + } + free(lock); + } else { + //add event + struct MutexLockEvent *event = malloc(sizeof(*event)); + event->locked = 0; + event->file = file; + event->line = line; + event->next = NULL; + if(lock->last_event) { + lock->last_event->next = event; + lock->last_event = event; + } else { + lock->first_event = event; + lock->last_event = event; + } + } +} + +void mutex_debug(pthread_mutex_t *mutex) { + //replay mutex events to stdout + struct MutexNode *node; + if(mutex) { + node = getMutexNode(mutex, 0); + if(!node) { + printf("[MUTEX_DEBUG] unknown mutex!\n"); + return; + } + mutex_replay(node); + } else { + for(node = mutex_nodes; node; node = node->next) { + mutex_replay(node); + } + } + printf("[MUTEX_DEBUG] end of mutex replay.\n"); +} + +static void mutex_replay(struct MutexNode *node) { + printf("[MUTEX_DEBUG] mutex replay:\n"); + struct MutexLock *lock; + struct MutexLockEvent *event; + for(lock = node->first_lock; lock; lock = lock->next) { + printf("[MUTEX_DEBUG] THREAD %d (%d locks):\n", lock->thread, lock->count); + for(event = lock->first_event; event; event = event->next) { + printf("[MUTEX_DEBUG] %s in %s:%d\n", (event->locked ? "lock " : "unlock"), event->file, event->line); + } + } +} + +#endif diff --git a/src/mutexDebug.h b/src/mutexDebug.h new file mode 100644 index 0000000..2b2a995 --- /dev/null +++ b/src/mutexDebug.h @@ -0,0 +1,29 @@ +/* mutexDebug.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _mutexdebug_h +#define _mutexdebug_h + +#ifndef DND_FUNCTIONS +#ifdef ENABLE_MUTEX_DEBUG +/* MODULAR ACCESSIBLE */ void xmutex(int lock, pthread_mutex_t *mutex, const char *file, unsigned int line); +/* MODULAR ACCESSIBLE */ void mutex_debug(pthread_mutex_t *mutex); + +void initMutexDebug(); + +#endif +#endif +#endif diff --git a/src/mysqlConn.c b/src/mysqlConn.c new file mode 100644 index 0000000..45ab193 --- /dev/null +++ b/src/mysqlConn.c @@ -0,0 +1,328 @@ +/* mysqlConn.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "mysqlConn.h" +#include "ConfigParser.h" +#include "tools.h" +#include "log.h" +#define DATABASE_VERSION "20" + +static void show_mysql_error(); + +struct mysql_conn_struct { + unsigned int tid; + MYSQL *mysql_conn; + struct used_result *used_results; + struct escaped_string *escaped_strings; + struct mysql_conn_struct *next; +}; + +struct used_result { + MYSQL_RES *result; + struct used_result *next; +}; + +struct escaped_string { + char *string; + struct escaped_string *next; +}; + +struct mysql_conn_struct *get_mysql_conn_struct(); + +struct mysql_conn_struct *mysql_conns = NULL; +static int mysql_serverport; +static char *mysql_host, *mysql_user, *mysql_pass, *mysql_base; + +#ifdef HAVE_THREADS +static pthread_mutex_t synchronized; +#endif + +static void check_mysql() { + MYSQL *mysql_conn = get_mysql_conn(); + int errid; + if((errid = mysql_ping(mysql_conn))) { + if(mysql_errno(mysql_conn) == CR_SERVER_GONE_ERROR) { + if(!mysql_real_connect(mysql_conn, mysql_host, mysql_user, mysql_pass, mysql_base, mysql_serverport, NULL, 0)) { + show_mysql_error(); + } + } else { + //mysql error + show_mysql_error(); + } + } +} + +MYSQL_RES *mysql_use() { + struct mysql_conn_struct *mysql_conn = get_mysql_conn_struct(); + MYSQL_RES *res = mysql_store_result(mysql_conn->mysql_conn); + struct used_result *result = malloc(sizeof(*result)); + if (!result) { + mysql_free_result(res); + return NULL; + } + result->result = res; + result->next = mysql_conn->used_results; + mysql_conn->used_results = result; + return res; +} + +void mysql_free() { + struct mysql_conn_struct *mysql_conn = get_mysql_conn_struct(); + if(!mysql_conn) return; + struct used_result *result, *next_result; + for(result = mysql_conn->used_results; result; result = next_result) { + next_result = result->next; + mysql_free_result(result->result); + free(result); + } + mysql_conn->used_results = NULL; + struct escaped_string *escaped, *next_escaped; + for(escaped = mysql_conn->escaped_strings; escaped; escaped = next_escaped) { + next_escaped = escaped->next; + free(escaped->string); + free(escaped); + } + mysql_conn->escaped_strings = NULL; +} + +int reload_mysql() { + char *new_mysql_host = get_string_field("MySQL.host"); + char *new_mysql_user = get_string_field("MySQL.user"); + char *new_mysql_pass = get_string_field("MySQL.pass"); + char *new_mysql_base = get_string_field("MySQL.base"); + if(!(new_mysql_host && new_mysql_user && new_mysql_pass && new_mysql_base)) + return 0; + + //replace login data + if(mysql_host) + free(mysql_host); + mysql_host = strdup(new_mysql_host); + + if(mysql_user) + free(mysql_user); + mysql_user = strdup(new_mysql_user); + + if(mysql_pass) + free(mysql_pass); + mysql_pass = strdup(new_mysql_pass); + + if(mysql_base) + free(mysql_base); + mysql_base = strdup(new_mysql_base); + + mysql_serverport = get_int_field("MySQL.port"); + if(!mysql_serverport) + mysql_serverport = 3306; + return 1; +} + +void init_mysql() { + THREAD_MUTEX_INIT(synchronized); + + MYSQL *mysql_conn = get_mysql_conn(); + + //check database version... + int version = 0; + if(!mysql_query(mysql_conn, "SELECT `database_version` FROM `version`")) { + MYSQL_RES *res = mysql_use(); + MYSQL_ROW row; + if((row = mysql_fetch_row(res))) { + version = atoi(row[0]); + } + } + if(!version) { + //CREATE DATABASE + FILE *f = fopen("database.sql", "r"); + mysql_set_server_option(mysql_conn, MYSQL_OPTION_MULTI_STATEMENTS_ON); + if (f) { + char line[512]; + char query_buffer[8192]; + int query_buffer_pos = 0; + while (fgets(line, sizeof(line), f)) { + query_buffer_pos += sprintf(query_buffer + query_buffer_pos, " %s", line); + if(line[(strlen(line) - 2)] == ';') { + if(mysql_query(mysql_conn, query_buffer)) + show_mysql_error(); + query_buffer_pos = 0; + } + } + fclose(f); + } else + printf_log("main", LOG_ERROR | LOG_MYSQL, "File not found: database.sql"); + f = fopen("database.defaults.sql", "r"); + if (f) { + char line[4096]; + char query_buffer[131072]; + int query_buffer_pos = 0; + while (fgets(line, sizeof(line), f)) { + query_buffer_pos += sprintf(query_buffer + query_buffer_pos, " %s", line); + if(line[(strlen(line) - 2)] == ';') { + if(mysql_query(mysql_conn, query_buffer)) + show_mysql_error(); + query_buffer_pos = 0; + } + } + fclose(f); + } else + printf_log("main", LOG_ERROR | LOG_MYSQL, "File not found: database.defaults.sql"); + do { + MYSQL_RES *res = mysql_store_result(mysql_conn); + mysql_free_result(res); + } while(!mysql_next_result(mysql_conn)); + mysql_set_server_option(mysql_conn, MYSQL_OPTION_MULTI_STATEMENTS_OFF); + mysql_query(mysql_conn, "INSERT INTO `version` (`database_version`) VALUES ('" DATABASE_VERSION "')"); + } + else if(version < atoi(DATABASE_VERSION)) { + //UPDATE DATABASE + FILE *f = fopen("database.upgrade.sql", "r"); + mysql_set_server_option(mysql_conn, MYSQL_OPTION_MULTI_STATEMENTS_ON); + if (f) { + char line[512]; + char query_buffer[8192]; + int query_buffer_pos = 0, use_querys = 0; + sprintf(query_buffer, "-- version: %d", version); + while (fgets(line, sizeof(line), f)) { + if(use_querys) { + query_buffer_pos += sprintf(query_buffer + query_buffer_pos, " %s", line); + if(line[strlen(line) - 1] == ';') { + mysql_query(mysql_conn, query_buffer); + query_buffer_pos = 0; + } + } else if(!stricmplen(query_buffer, line, strlen(query_buffer))) { + use_querys = 1; + } + } + if(query_buffer_pos) { + if(mysql_query(mysql_conn, query_buffer)) + show_mysql_error(); + } + fclose(f); + } else + printf_log("main", LOG_ERROR | LOG_MYSQL, "File not found: database.upgrade.sql"); + do { + MYSQL_RES *res = mysql_store_result(mysql_conn); + mysql_free_result(res); + } while(!mysql_next_result(mysql_conn)); + mysql_set_server_option(mysql_conn, MYSQL_OPTION_MULTI_STATEMENTS_OFF); + mysql_query(mysql_conn, "UPDATE `version` SET `database_version` = '" DATABASE_VERSION "'"); + } +} + +void free_mysql() { + struct mysql_conn_struct *mysql_conn, *next; + for(mysql_conn = mysql_conns; mysql_conn; mysql_conn = next) { + next = mysql_conn->next; + mysql_close(mysql_conn->mysql_conn); + free(mysql_conn); + } + mysql_conns = NULL; +} + +static void show_mysql_error() { + MYSQL *mysql_conn = get_mysql_conn(); + //show mysql_error() + printf_log("main", LOG_ERROR | LOG_MYSQL, "Error: %s\n", mysql_error(mysql_conn)); +} + +void printf_mysql_query(const char *text, ...) { + MYSQL *mysql_conn = get_mysql_conn(); + va_list arg_list; + char queryBuf[MYSQLMAXLEN]; + int pos; + queryBuf[0] = '\0'; + va_start(arg_list, text); + pos = vsnprintf(queryBuf, MYSQLMAXLEN - 2, text, arg_list); + va_end(arg_list); + if (pos < 0 || pos > (MYSQLMAXLEN - 2)) pos = MYSQLMAXLEN - 2; + queryBuf[pos] = '\0'; + printf_log("main", LOG_MYSQL, "%s\n", queryBuf); + if(mysql_query(mysql_conn, queryBuf)) { + check_mysql(); + if(mysql_query(mysql_conn, queryBuf)) { + show_mysql_error(); + } + } +} + +void printf_long_mysql_query(int len, const char *text, ...) { + MYSQL *mysql_conn = get_mysql_conn(); + va_list arg_list; + char queryBuf[len]; + int pos; + queryBuf[0] = '\0'; + va_start(arg_list, text); + pos = vsnprintf(queryBuf, len - 2, text, arg_list); + va_end(arg_list); + if (pos < 0 || pos > (len - 2)) pos = len - 2; + queryBuf[pos] = '\0'; + printf_log("main", LOG_MYSQL, "%s\n", queryBuf); + if(mysql_query(mysql_conn, queryBuf)) { + check_mysql(); + if(mysql_query(mysql_conn, queryBuf)) { + show_mysql_error(); + } + } +} + +char* escape_string(const char *str) { + struct mysql_conn_struct *mysql_conn = get_mysql_conn_struct(); + struct escaped_string *escapedstr = malloc(sizeof(*escapedstr)); + if (!escapedstr) { + return NULL; + } + char escaped[strlen(str)*2+1]; + mysql_real_escape_string(mysql_conn->mysql_conn, escaped, str, strlen(str)); + escapedstr->string = strdup(escaped); + escapedstr->next = mysql_conn->escaped_strings; + mysql_conn->escaped_strings = escapedstr; + return escapedstr->string; +} + +struct mysql_conn_struct *get_mysql_conn_struct() { + SYNCHRONIZE(synchronized); + struct mysql_conn_struct *mysql_conn; + unsigned int tid; + #ifdef HAVE_THREADS + tid = (unsigned int) pthread_self_tid(); + #else + tid = 1; + #endif + for(mysql_conn = mysql_conns; mysql_conn; mysql_conn = mysql_conn->next) { + if(mysql_conn->tid == tid) { + DESYNCHRONIZE(synchronized); + return mysql_conn; + } + } + mysql_conn = malloc(sizeof(*mysql_conn)); + mysql_conn->mysql_conn = mysql_init(NULL); + mysql_conn->tid = tid; + mysql_conn->used_results = NULL; + mysql_conn->escaped_strings = NULL; + mysql_conn->next = mysql_conns; + mysql_conns = mysql_conn; + if (!mysql_real_connect(mysql_conn->mysql_conn, mysql_host, mysql_user, mysql_pass, mysql_base, mysql_serverport, NULL, 0)) { + //error + show_mysql_error(); + } + DESYNCHRONIZE(synchronized); + return mysql_conn; +} + +MYSQL *get_mysql_conn() { + struct mysql_conn_struct *mysql_conn = get_mysql_conn_struct(); + return mysql_conn->mysql_conn; +} diff --git a/src/mysqlConn.h b/src/mysqlConn.h new file mode 100644 index 0000000..e85832f --- /dev/null +++ b/src/mysqlConn.h @@ -0,0 +1,41 @@ +/* mysqlConn.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _MySQLConn_h +#define _MySQLConn_h + +#include "main.h" +#include +#ifdef HAVE_MYSQL_ERRMSG_H +#include +#elif defined(HAVE_ERRMSG_H) +#include +#endif + +#define MYSQLMAXLEN 1024 + +#ifndef DND_FUNCTIONS +/* MODULAR ACCESSIBLE */ MYSQL_RES *mysql_use(); +/* MODULAR ACCESSIBLE */ void mysql_free(); +int reload_mysql(); +void init_mysql(); +void free_mysql(); +/* MODULAR ACCESSIBLE */ void printf_mysql_query(const char *text, ...) PRINTF_LIKE(1, 2); +/* MODULAR ACCESSIBLE */ void printf_long_mysql_query(int len, const char *text, ...) PRINTF_LIKE(2, 3); +/* MODULAR ACCESSIBLE */ char* escape_string(const char *str); +/* MODULAR ACCESSIBLE */ MYSQL *get_mysql_conn(); +#endif +#endif diff --git a/src/overall.h b/src/overall.h new file mode 100644 index 0000000..ff6f408 --- /dev/null +++ b/src/overall.h @@ -0,0 +1,174 @@ +/* overall.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#ifndef _overall_h +#define _overall_h +#include "../config.h" + +#define NEONSERV_VERSION "5.6" +#define VERSION_PATCHLEVEL 736 + +#include +#include +#include +#include +#ifdef WIN32 +#include +#include +#include +#else +#ifdef HAVE_FEATURES_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#ifdef SYNCHRONIZE +//some winnt machines do already have a SYNCHRONIZE macro defined... +#undef SYNCHRONIZE +#endif +#ifdef HAVE_THREADS +#include +#ifdef WIN32 +#define pthread_self_tid() pthread_self().p +#else +#define pthread_self_tid() pthread_self() +#endif +#ifdef PTHREAD_MUTEX_RECURSIVE_NP +#define PTHREAD_MUTEX_RECURSIVE_VAL PTHREAD_MUTEX_RECURSIVE_NP +#else +#define PTHREAD_MUTEX_RECURSIVE_VAL PTHREAD_MUTEX_RECURSIVE +#endif +#define THREAD_MUTEX_INIT(var) { \ + pthread_mutexattr_t mutex_attr; \ + pthread_mutexattr_init(&mutex_attr);\ + pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE_VAL);\ + pthread_mutex_init(&var, &mutex_attr); \ +} +#define THREAD_MUTEX_INIT_TYPE(var, type) { \ + pthread_mutexattr_t mutex_attr; \ + pthread_mutexattr_init(&mutex_attr);\ + pthread_mutexattr_settype(&mutex_attr, type);\ + pthread_mutex_init(&var, &mutex_attr); \ +} +#ifdef ENABLE_MUTEX_DEBUG +#include "mutexDebug.h" +#define SYNCHRONIZE(var) xmutex(1, &var, __FILE__, __LINE__); pthread_mutex_lock(&var) +#define DESYNCHRONIZE(var) xmutex(0, &var, __FILE__, __LINE__); pthread_mutex_unlock(&var) +#else +#define SYNCHRONIZE(var) pthread_mutex_lock(&var) +#define DESYNCHRONIZE(var) pthread_mutex_unlock(&var) +#endif +#else +#define THREAD_MUTEX_INIT(var) +#define SYNCHRONIZE(var) +#define DESYNCHRONIZE(var) +#ifdef ENABLE_MUTEX_DEBUG +#undef ENABLE_MUTEX_DEBUG +#endif +#endif + +#if __GNUC__ +#define PRINTF_LIKE(M,N) __attribute__((format (printf, M, N))) +#else +#define PRINTF_LIKE(M,N) +#endif + +#if __GNUC__ >= 2 +#define UNUSED_ARG(ARG) ARG __attribute__((unused)) +#elif defined(S_SPLINT_S) +#define UNUSED_ARG(ARG) /*@unused@*/ ARG +#define const /*@observer@*/ /*@temp@*/ +#else +#define UNUSED_ARG(ARG) ARG +#endif + +#define STRINGIFY_(x) #x +#define STRINGIFY(x) STRINGIFY_(x) + +#if defined(__GNUC__) +#if defined(__GNUC_PATCHLEVEL__) +#define COMPILER "GCC" " " STRINGIFY(__GNUC__) "." STRINGIFY(__GNUC_MINOR__) "." STRINGIFY(__GNUC_PATCHLEVEL__) +#else +#define COMPILER "GCC" " " STRINGIFY(__GNUC__) "." STRINGIFY(__GNUC_MINOR__) +#endif +#elif defined (__IMAGECRAFT__) +#define COMPILER "ICCAVR" +#else +#define COMPILER "Unknown" +#endif + +#ifdef ENABLE_MEMORY_DEBUG +#include "memoryDebug.h" +#endif + +#define SOCKET_SELECT_TIME 1 +#define SOCKET_RECONNECT_TIME 20 + +#define NICKLEN 30 +#define USERLEN 10 +#define AUTHLEN 32 +#define HOSTLEN 63 +#define REALLEN 50 +#define TOPICLEN 500 +#define CHANNELLEN 200 +#define MAXLEN 512 +#define MAXLOGLEN 1024 +#define TRIGGERLEN 50 +#define MAXNUMPARAMS 200 /* maximum number of parameters in one line */ +#define MAXLANGUAGES 5 +#define MAXMODES 6 +#define INVITE_TIMEOUT 30 +#define BOTWAR_DETECTION_TIME 7 +#define BOTWAR_DETECTION_EVENTS 6 +#define REWHO_TIMEOUT 10 /* wait 10 seconds before WHO an unauthed user again */ +#define CLEAR_CACHE_INTERVAL 10 + +//valid nick chars +#define VALID_NICK_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890{|}~[\\]^-_`" +//the first char is a little bit different +// 0 1 2 3 4 5 6 +// 1234567890123456789012345678901234567890123456789012345678 9012 62 +#define VALID_NICK_CHARS_FIRST "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~[\\]^_`" +#define VALID_NICK_CHARS_FIRST_LEN 62 + +#define TEMPUSER_LIST_INDEX VALID_NICK_CHARS_FIRST_LEN + +#define LOGLEVEL_INFO 0x01 +#define LOGLEVEL_ERROR 0x02 +#define LOGLEVEL_RAW 0x04 +#define LOGLEVEL_MYSQL 0x08 + +#define timeval_is_bigger(x,y) ((x.tv_sec > y.tv_sec) || (x.tv_sec == y.tv_sec && x.tv_usec > y.tv_usec)) + +#define MODSTATE_RELOAD 0x01 +#define MODSTATE_STARTSTOP 0x02 +#define MODSTATE_REBIND 0x03 + +#endif diff --git a/src/signal.c b/src/signal.c new file mode 100644 index 0000000..4e8f9a5 --- /dev/null +++ b/src/signal.c @@ -0,0 +1,96 @@ +/* signal.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#include "signal.h" +#include "bots.h" +#include "ChanNode.h" +#include "ClientSocket.h" +#include "IOHandler.h" +#include "ConfigParser.h" +#include "log.h" + +static void sigcrash(); +static void sigexit(); + +void sighandler(int signum) { + printf_log("main", LOG_INFO, "Received Signal %d\n", signum); + signal(signum, SIG_DFL); + switch(signum) { + case SIGABRT: + case SIGTERM: + case SIGINT: + //normal shutdown + sigexit(signum); + break; + case SIGSEGV: + case SIGFPE: + case SIGILL: + //crash + sigcrash(signum); + break; + } + #ifdef WIN32 + exit(signum); + #else + kill(getpid(), signum); + #endif +} + +static void sigcrash(int signum) { + char coregen[MAXLEN]; + coregen[0] = 0; + char *signame; + switch(signum) { + case SIGSEGV: + signame = "SIGSEGV"; + break; + case SIGFPE: + signame = "SIGFPE"; + break; + case SIGILL: + signame = "SIGILL"; + break; + default: + signame = "SIGUNKNOWN"; + break; + } + printf_log("main", LOG_ERROR, "NeonServ process crashed (%s)\n", signame); + #ifndef WIN32 + char gcore[50]; + sprintf(gcore, "gcore %u", getpid()); + int sysretn = system(gcore); //generate core file + sprintf(coregen, "core file generated. (%d)", sysretn); + printf_log("main", LOG_ERROR | LOG_INFO, "generated core file.\n"); + #endif + //close all bots + struct ClientSocket *bot; + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if((bot->flags & SOCKET_FLAG_CONNECTED)) { + iohandler_close(bot->iofd); + bot->flags &= ~(SOCKET_FLAG_CONNECTED | SOCKET_FLAG_READY); + bot->iofd = NULL; + } + } + printf_log("main", LOG_INFO, "hard shutdown...\n"); + usleep(2000000); + //hard restart + restart_bot(1); + exit(0); +} + +static void sigexit(int signum) { + stop_bot(); +} diff --git a/src/signal.h b/src/signal.h new file mode 100644 index 0000000..b59620a --- /dev/null +++ b/src/signal.h @@ -0,0 +1,24 @@ +/* signal.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _signal_h +#define _signal_h + +#include "main.h" + +void sighandler(int signum); + +#endif \ No newline at end of file diff --git a/src/statistics.c b/src/statistics.c new file mode 100644 index 0000000..5d102ca --- /dev/null +++ b/src/statistics.c @@ -0,0 +1,78 @@ +/* statistics.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#include "statistics.h" +#include "timeq.h" +#include "ConfigParser.h" +#include "ClientSocket.h" +#include "bots.h" +#include "UserNode.h" +#include "ChanNode.h" +#include "ChanUser.h" +#include "IRCParser.h" +#include "modcmd.h" + +int statistics_requested_lusers = 0; + +static TIMEQ_CALLBACK(main_statistics) { + int update_minutes = get_int_field("statistics.frequency"); + if(!update_minutes) update_minutes = 2; + timeq_add(update_minutes * 60, 0, main_statistics, NULL); + if(get_int_field("statistics.enable")) { + statistics_requested_lusers = 1; + if(get_int_field("statistics.include_lusers")) { + struct ClientSocket *bot, *lusersbot = NULL; + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(bot->flags & SOCKET_FLAG_PREFERRED) + lusersbot = bot; + } + bot = lusersbot; + if(bot == NULL) bot = getBots(SOCKET_FLAG_READY, NULL); + putsock(bot, "LUSERS"); + } else + statistics_update(); + } else { + statistics_privmsg = 0; + statistics_commands = 0; + } +} + +int statistics_update() { + if(get_int_field("statistics.enable") && statistics_requested_lusers && get_string_field("statistics.execute")) { + statistics_requested_lusers = 0; + char command[MAXLEN]; + /* parameters: + - visible users + - visible chanusers + - visible channels + - privmsg per minute + - commands per minute + - network users + - network channels + */ + sprintf(command, "%s %d %d %d %d %d %d %d", get_string_field("statistics.execute"), getUserCount(), getChanUserCount(), getChannelCount(), statistics_privmsg, statistics_commands, statistics_network_users, statistics_network_channels); + statistics_privmsg = 0; + statistics_commands = 0; + return system(command); + } + return -1; +} + +void init_statistics() { + int update_minutes = get_int_field("statistics.frequency"); + if(!update_minutes) update_minutes = 2; + timeq_add(update_minutes * 60 + 10, 0, main_statistics, NULL); +} diff --git a/src/statistics.h b/src/statistics.h new file mode 100644 index 0000000..79f7f4d --- /dev/null +++ b/src/statistics.h @@ -0,0 +1,25 @@ +/* statistics.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _statistics_h +#define _statistics_h + +#include "main.h" + +int statistics_update(); +void init_statistics(); + +#endif diff --git a/src/timeq.c b/src/timeq.c new file mode 100644 index 0000000..ec9ea3e --- /dev/null +++ b/src/timeq.c @@ -0,0 +1,149 @@ +/* timeq.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ + +#include "timeq.h" +#include "IOHandler.h" +#include "tools.h" +#include "log.h" + +static struct timeq_entry *timeq_events; +#ifdef HAVE_THREADS +static int pthread_mutex_initialized = 0; +static pthread_mutex_t synchronized; +#endif + +static IOHANDLER_CALLBACK(timeq_callback) { + struct timeq_entry *entry = event->iofd->data; + switch(event->type) { + case IOEVENT_TIMEOUT: + if(entry->name) { + free(entry->name); + entry->name = NULL; + } + entry->callback(entry->data); + entry->iofd = NULL; + timeq_del(entry); + break; + default: + break; + } +} + +struct timeq_entry* timeq_add(int seconds, int module_id, timeq_callback_t *callback, void *data) { + return timeq_uadd(seconds * 1000, module_id, callback, data); +} + +struct timeq_entry* timeq_uadd(int useconds, int module_id, timeq_callback_t *callback, void *data) { + struct timeval timeout; + struct timeq_entry *entry = malloc(sizeof(*entry)); + if (!entry) + { + printf_log("main", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return NULL; + } + #ifdef HAVE_THREADS + if(!pthread_mutex_initialized) { + THREAD_MUTEX_INIT(synchronized); + pthread_mutex_initialized = 1; + } + #endif + gettimeofday(&timeout, NULL); + SYNCHRONIZE(synchronized); + timeout.tv_usec += (useconds % 1000); + timeout.tv_sec += (useconds / 1000); + entry->iofd = iohandler_timer(timeout, timeq_callback); + entry->iofd->data = entry; + entry->module_id = module_id; + entry->callback = callback; + entry->data = data; + entry->name = NULL; + entry->next = timeq_events; + entry->prev = NULL; + if(timeq_events) + timeq_events->prev = entry; + timeq_events = entry; + DESYNCHRONIZE(synchronized); + return entry; +} + +struct timeq_entry* timeq_add_name(char *name, int seconds, int module_id, timeq_callback_t *callback, void *data) { + return timeq_uadd_name(name, seconds * 1000, module_id, callback, data); +} + +struct timeq_entry* timeq_uadd_name(char *name, int useconds, int module_id, timeq_callback_t *callback, void *data) { + struct timeq_entry *entry = timeq_uadd(useconds, module_id, callback, data); + entry->name = strdup(name); + return entry; +} + +int timeq_del(struct timeq_entry* entry) { + #ifdef HAVE_THREADS + if(!pthread_mutex_initialized) return 0; + #endif + SYNCHRONIZE(synchronized); + if(entry->next) + entry->next->prev = entry->prev; + if(entry->prev) + entry->prev->next = entry->next; + else + timeq_events = entry->next; + if(entry->name) + free(entry->name); + if(entry->iofd) + iohandler_close(entry->iofd); + free(entry); + DESYNCHRONIZE(synchronized); + return 1; +} + +int timeq_del_name(char *name) { + SYNCHRONIZE(synchronized); + struct timeq_entry *entry; + int removed = 0; + for(entry = timeq_events; entry; entry = entry->next) { + if(entry->name && !stricmp(entry->name, name)) { + removed = timeq_del(entry); + break; + } + } + DESYNCHRONIZE(synchronized); + return removed; +} + +int timeq_name_exists(char *name) { + SYNCHRONIZE(synchronized); + struct timeq_entry *centry; + for(centry = timeq_events; centry; centry = centry->next) { + if(centry->name && !stricmp(centry->name, name)) { + DESYNCHRONIZE(synchronized); + return 1; + } + } + DESYNCHRONIZE(synchronized); + return 0; +} + +void unregister_module_timers(int module_id) { + SYNCHRONIZE(synchronized); + struct timeq_entry *entry, *next_entry; + for(entry = timeq_events; entry; entry = next_entry) { + next_entry = entry->next; + if(entry->module_id == module_id) + timeq_del(entry); + } + DESYNCHRONIZE(synchronized); +} diff --git a/src/timeq.h b/src/timeq.h new file mode 100644 index 0000000..6f5546e --- /dev/null +++ b/src/timeq.h @@ -0,0 +1,47 @@ +/* timeq.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _timeq_h +#define _timeq_h + +#include "main.h" + +#define TIMEQ_CALLBACK(NAME) void NAME(UNUSED_ARG(void *data)) +typedef TIMEQ_CALLBACK(timeq_callback_t); + +struct IODescriptor; + +struct timeq_entry { + struct IODescriptor *iofd; + char *name; + int module_id; + timeq_callback_t *callback; + void *data; + + struct timeq_entry *prev, *next; +}; + +#ifndef DND_FUNCTIONS +/* MODULAR ACCESSIBLE */ struct timeq_entry* timeq_add(int seconds, int module_id, timeq_callback_t *callback, void *data); +/* MODULAR ACCESSIBLE */ struct timeq_entry* timeq_uadd(int useconds, int module_id, timeq_callback_t *callback, void *data); +/* MODULAR ACCESSIBLE */ struct timeq_entry* timeq_add_name(char *name, int seconds, int module_id, timeq_callback_t *callback, void *data); +/* MODULAR ACCESSIBLE */ struct timeq_entry* timeq_uadd_name(char *name, int useconds, int module_id, timeq_callback_t *callback, void *data); +/* MODULAR ACCESSIBLE */ int timeq_del(struct timeq_entry* entry); +/* MODULAR ACCESSIBLE */ int timeq_del_name(char *name); +/* MODULAR ACCESSIBLE */ int timeq_name_exists(char *name); +void unregister_module_timers(int module_id); +#endif +#endif diff --git a/src/tools.c b/src/tools.c new file mode 100644 index 0000000..0232671 --- /dev/null +++ b/src/tools.c @@ -0,0 +1,615 @@ +/* tools.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#include "tools.h" +#include "UserNode.h" +#include "ChanNode.h" +#include "lang.h" +#include "ClientSocket.h" +#include "IPNode.h" +#include "log.h" + +static const struct default_language_entry msgtab[] = { + {"TIME_MASK_2_ITEMS", "%s and %s"}, /* {ARGS: "2 days", "1 hour"} */ + {"TIME_MASK_3_ITEMS", "%s, %s and %s"}, /* {ARGS: "2 days", "1 hour", "20 minutes"} */ + {"TIME_YEAR", "year"}, + {"TIME_YEARS", "years"}, + {"TIME_MONTH", "month"}, + {"TIME_MONTHS", "months"}, + {"TIME_WEEK", "week"}, + {"TIME_WEEKS", "weeks"}, + {"TIME_DAY", "day"}, + {"TIME_DAYS", "days"}, + {"TIME_HOUR", "hour"}, + {"TIME_HOURS", "hours"}, + {"TIME_MINUTE", "minute"}, + {"TIME_MINUTES", "minutes"}, + {"TIME_SECOND", "second"}, + {"TIME_SECONDS", "seconds"}, + {NULL, NULL} +}; + +/* copied from IRCU 2.10.12 match.c */ +/* + * Compare if a given string (name) matches the given + * mask (which can contain wild cards: '*' - match any + * number of chars, '?' - match any single character. + * + * return 0, if match + * 1, if no match + * + * Originally by Douglas A Lewis (dalewis@acsu.buffalo.edu) + * Rewritten by Timothy Vogelsang (netski), net@astrolink.org + */ +int match(const char *mask, const char *name) +{ + const char *m = mask, *n = name; + const char *m_tmp = mask, *n_tmp = name; + int star_p; + + for (;;) switch (*m) { + case '\0': + if (!*n) + return 0; + backtrack: + if (m_tmp == mask) + return 1; + m = m_tmp; + n = ++n_tmp; + if (*n == '\0') + return 1; + break; + case '\\': + m++; + /* allow escaping to force capitalization */ + if (*m++ != *n++) + goto backtrack; + break; + case '*': case '?': + for (star_p = 0; ; m++) { + if (*m == '*') + star_p = 1; + else if (*m == '?') { + if (!*n++) + goto backtrack; + } else break; + } + if (star_p) { + if (!*m) + return 0; + else if (*m == '\\') { + m_tmp = ++m; + if (!*m) + return 1; + for (n_tmp = n; *n && *n != *m; n++) ; + } else { + m_tmp = m; + for (n_tmp = n; *n && tolower(*n) != tolower(*m); n++) ; + } + } + /* and fall through */ + default: + if (!*n) + return *m != '\0'; + if (tolower(*m) != tolower(*n)) + goto backtrack; + m++; + n++; + break; + } +} + + +//TABLES +struct Table *table_init(int width, int length, int flags) { + int row; + struct Table *table = malloc(sizeof(*table)); + table->contents = malloc(length * sizeof(*table->contents)); + for(row = 0; row < length; row++) { + table->contents[row] = calloc(width, sizeof(*table->contents[row])); + } + table->length = length; + table->width = width; + table->flags = flags; + table->col_flags = calloc(width, sizeof(int)); + table->entrys = 0; + table->maxwidth = calloc(width, sizeof(int)); + table->table_lines = NULL; + return table; +} + +int table_add(struct Table *table, char **entry) { + int col; + if(table->entrys == table->length) return 0; + for(col = 0; col < table->width; col++) { + table->contents[table->entrys][col] = ((table->flags & TABLE_FLAG_USE_POINTER) || !entry[col] ? entry[col] : strdup(entry[col])); + if(table->contents[table->entrys][col]) + table->col_flags[col] |= TABLE_FLAG_COL_CONTENTS; + if(entry[col] && strlen(entry[col]) > table->maxwidth[col]) + table->maxwidth[col] = strlen(entry[col]); + } + table->entrys++; + return 1; +} + +int table_change(struct Table *table, int row, char **entry) { + int col; + if(row >= table->length) return 0; + for(col = 0; col < table->width; col++) { + if(table->contents[row][col] && !(table->flags & TABLE_FLAG_USE_POINTER)) + free(table->contents[row][col]); + table->contents[row][col] = ((table->flags & TABLE_FLAG_USE_POINTER) || !entry[col] ? entry[col] : strdup(entry[col])); + if(table->contents[row][col]) + table->col_flags[col] |= TABLE_FLAG_COL_CONTENTS; + if(entry[col] && strlen(entry[col]) > table->maxwidth[col]) + table->maxwidth[col] = strlen(entry[col]); + } + return 1; +} + +int table_change_field(struct Table *table, int row, int col, char *entry) { + if(row >= table->length) return 0; + if(col >= table->width) return 0; + if(table->contents[row][col] && !(table->flags & TABLE_FLAG_USE_POINTER)) + free(table->contents[row][col]); + table->contents[row][col] = (((table->flags & TABLE_FLAG_USE_POINTER) || !entry) ? entry : strdup(entry)); + if(table->contents[row][col]) + table->col_flags[col] |= TABLE_FLAG_COL_CONTENTS; + if(entry && strlen(entry) > table->maxwidth[col]) + table->maxwidth[col] = strlen(entry); + return 1; +} + +int table_set_bold(struct Table *table, int collum, int bold) { + if(bold) + table->col_flags[collum] |= TABLE_FLAG_COL_BOLD; + else + table->col_flags[collum] &= ~TABLE_FLAG_COL_BOLD; + return 1; +} + +char **table_end(struct Table *table) { + int row, col, tablewidth = 0, pos,i,j,k; + if(!table->entrys) return NULL; + for(col = 0; col < table->width; col++) { + tablewidth += table->maxwidth[col]+1; + if(table->col_flags[col] & TABLE_FLAG_COL_BOLD) + tablewidth += 2; + } + table->table_lines = malloc(table->entrys * sizeof(table->table_lines)); + for(row = 0; row < table->entrys; row++) { + table->table_lines[row] = malloc(tablewidth * sizeof(*table->table_lines[row])); + pos = 0; + for(col = 0; col < table->width; col++) { + if(!(table->col_flags[col] & TABLE_FLAG_COL_CONTENTS)) continue; + if(table->col_flags[col] & TABLE_FLAG_COL_BOLD) + table->table_lines[row][pos++] = '\002'; + i = 0; + j = 0; + if(table->contents[row][col]) { + for(; i < strlen(table->contents[row][col]); i++) { + table->table_lines[row][pos++] = table->contents[row][col][i]; + if(table->contents[row][col][i] == '\002') j++; + else if(table->contents[row][col][i] == '\003') { + j++; + for(k = 1; k <= 2; k++) { + if(isdigit(table->contents[row][col][i+k])) + j++; + else + break; + } + } + } + } else if(table->col_flags[col] & TABLE_FLAG_COL_SKIP_NULL) + continue; + i -= j; + if(col < table->width-1) { + for(;i < table->maxwidth[col]; i++) { + table->table_lines[row][pos++] = ' '; + } + table->table_lines[row][pos++] = ' '; + } else + table->table_lines[row][pos++] = '\0'; + + if(table->col_flags[col] & TABLE_FLAG_COL_BOLD) + table->table_lines[row][pos++] = '\002'; + } + } + return table->table_lines; +} + +void table_free(struct Table *table) { + int row, col; + for(row = 0; row < table->length; row++) { + if(!(table->flags & TABLE_FLAG_USE_POINTER) && table->entrys > row) { + for(col = 0; col < table->width; col++) { + if(table->contents[row][col]) + free(table->contents[row][col]); + } + } + free(table->contents[row]); + } + free(table->contents); + free(table->col_flags); + free(table->maxwidth); + if(table->table_lines) { + for(row = 0; row < table->entrys; row++) { + free(table->table_lines[row]); + } + free(table->table_lines); + } + free(table); +} + +char* timeToStr(struct UserNode *user, int seconds, int items, char *buf) { + char item[items][MAXLEN]; + int tmp, citem = 0; + if(citem != items && seconds >= 31536000) { //60*60*24*365 = 31536000 + + tmp = seconds / 31536000; + sprintf(item[citem++], "%d %s", tmp, get_language_string(user, tmp == 1 ? "TIME_YEAR" : "TIME_YEARS")); + seconds -= tmp * 31536000; + } + if(citem != items && seconds >= 86400) { //60*60*24 = 86400 + tmp = seconds / 86400; + sprintf(item[citem++], "%d %s", tmp, get_language_string(user, tmp == 1 ? "TIME_DAY" : "TIME_DAYS")); + seconds -= tmp * 86400; + } + if(citem != items && seconds >= 3600) { //60*60 = 3600 + tmp = seconds / 3600; + sprintf(item[citem++], "%d %s", tmp, get_language_string(user, tmp == 1 ? "TIME_HOUR" : "TIME_HOURS")); + seconds -= tmp * 3600; + } + if(citem != items && seconds >= 60) { + tmp = seconds / 60; + sprintf(item[citem++], "%d %s", tmp, get_language_string(user, tmp == 1 ? "TIME_MINUTE" : "TIME_MINUTES")); + seconds -= tmp * 60; + } + if(citem != items && seconds >= 1) { + sprintf(item[citem++], "%d %s", seconds, get_language_string(user, seconds == 1 ? "TIME_SECOND" : "TIME_SECONDS")); + } + if(citem == 2) { + build_language_string(user, buf, "TIME_MASK_2_ITEMS", item[0], item[1]); + } else if(citem == 3) { + build_language_string(user, buf, "TIME_MASK_3_ITEMS", item[0], item[1], item[2]); + } else { + int i, ii, p = 0; + for(i = 0; i < citem; i++) { + for(ii = 0; ii < strlen(item[i]); ii++) { + buf[p++] = item[i][ii]; + } + buf[p++] = ' '; + } + buf[(p ? p-1 : 0)] = '\0'; + } + return buf; +} + +int strToTime(struct UserNode *user, char *str) { + /* + * y = year = 365 days + * M = month = 30 days + * w = week = 7 days + * d = day + * h = hour + * m = minute + * (s) = second + */ + int total_time = 0, cvalue; + char *p, tmpchar; + int unit_multiplikator; + while(*str) { + p = str; + while(*p && !isdigit(*p)) //skip leading chars + p++; + str = p; + while(*p && isdigit(*p)) //get the value + p++; + tmpchar = *p; + *p = '\0'; + cvalue = isdigit(*str) ? atoi(str) : 0; + *p = tmpchar; + while(*p == ' ') //skip spaces + p++; + str = p; + while(*p && !isdigit(*p)) //get the unit + p++; + tmpchar = *p; + *p = '\0'; + if(p - str > 1) { //unit has more than one char + if(!stricmp(str, "year") || !stricmp(str, "year") || !stricmp(str, get_language_string(user, "TIME_YEAR")) || !stricmp(str, get_language_string(user, "TIME_YEARS"))) + unit_multiplikator = 31536000; //60*60*24*365 = 31536000 + else if(!stricmp(str, "month") || !stricmp(str, "months") || !stricmp(str, get_language_string(user, "TIME_MONTH")) || !stricmp(str, get_language_string(user, "TIME_MONTHS"))) + unit_multiplikator = 2592000; //60*60*24*30 = 2592000 + else if(!stricmp(str, "week") || !stricmp(str, "weeks") || !stricmp(str, get_language_string(user, "TIME_WEEK")) || !stricmp(str, get_language_string(user, "TIME_WEEKS"))) + unit_multiplikator = 604800; //60*60*24*7 = 604800 + else if(!stricmp(str, "day") || !stricmp(str, "days") || !stricmp(str, get_language_string(user, "TIME_DAY")) || !stricmp(str, get_language_string(user, "TIME_DAYS"))) + unit_multiplikator = 86400; //60*60*24 = 86400 + else if(!stricmp(str, "hour") || !stricmp(str, "hours") || !stricmp(str, get_language_string(user, "TIME_HOUR")) || !stricmp(str, get_language_string(user, "TIME_HOURS"))) + unit_multiplikator = 3600; //60*60 = 3600 + else if(!stricmp(str, "minute") || !stricmp(str, "minutes") || !stricmp(str, "min") || !stricmp(str, "mins") || !stricmp(str, get_language_string(user, "TIME_MINUTE")) || !stricmp(str, get_language_string(user, "TIME_MINUTES"))) + unit_multiplikator = 60; + else + unit_multiplikator = 1; + } else { + switch(*str) { + case 'y': + unit_multiplikator = 31536000; //60*60*24*365 = 31536000 + break; + case 'M': + unit_multiplikator = 2592000; //60*60*24*30 = 2592000 + break; + case 'w': + unit_multiplikator = 604800; //60*60*24*7 = 604800 + break; + case 'd': + unit_multiplikator = 86400; //60*60*24 = 86400 + break; + case 'h': + unit_multiplikator = 3600; //60*60 = 3600 + break; + case 'm': + unit_multiplikator = 60; + break; + default: + unit_multiplikator = 1; + break; + } + } + total_time += (cvalue * unit_multiplikator); + *p = tmpchar; + str = p; + } + return total_time; +} + +int getCurrentSecondsOfDay() { + time_t now = time(0); + struct tm *timeofday = localtime(&now); + int seconds = 0; + seconds += timeofday->tm_hour * 3600; + seconds += timeofday->tm_min * 60; + seconds += timeofday->tm_sec; + return seconds; +} + +struct ModeBuffer* initModeBuffer(struct ClientSocket *client, struct ChanNode *chan) { + struct ModeBuffer *modeBuf = malloc(sizeof(*modeBuf)); + if(!modeBuf) { + printf_log("main", LOG_ERROR, "%s:%d malloc() failed", __FILE__, __LINE__); + return NULL; + } + modeBuf->client = client; + modeBuf->chan = chan; + modeBuf->addCount = 0; + modeBuf->delCount = 0; + return modeBuf; +} + +void modeBufferSet(struct ModeBuffer *modeBuf, int add, char mode, char *param) { + if(add) { + modeBuf->addModes[modeBuf->addCount] = mode; + modeBuf->addModesParams[modeBuf->addCount] = (param ? strdup(param) : NULL); + modeBuf->addCount++; + modeBuf->addModes[modeBuf->addCount] = '\0'; + } else { + modeBuf->delModes[modeBuf->delCount] = mode; + modeBuf->delModesParams[modeBuf->delCount] = (param ? strdup(param) : NULL); + modeBuf->delCount++; + modeBuf->delModes[modeBuf->delCount] = '\0'; + } + if(modeBuf->addCount + modeBuf->delCount == MAXMODES) + flushModeBuffer(modeBuf); +} + +void flushModeBuffer(struct ModeBuffer *modeBuf) { + char modeStr[MAXMODES+3]; + int modePos = 0; + char paramStr[MAXLEN]; + *paramStr = '\0'; + int paramPos = 0; + int i; + if(modeBuf->addCount) { + modeStr[modePos++] = '+'; + for(i = 0; i < modeBuf->addCount; i++) { + modeStr[modePos++] = modeBuf->addModes[i]; + if(modeBuf->addModesParams[i]) { + paramPos += sprintf(paramStr + paramPos, " %s", modeBuf->addModesParams[i]); + free(modeBuf->addModesParams[i]); + modeBuf->addModesParams[i] = NULL; + } + } + modeBuf->addCount = 0; + } + if(modeBuf->delCount) { + modeStr[modePos++] = '-'; + for(i = 0; i < modeBuf->delCount; i++) { + modeStr[modePos++] = modeBuf->delModes[i]; + if(modeBuf->delModesParams[i]) { + paramPos += sprintf(paramStr + paramPos, " %s", modeBuf->delModesParams[i]); + free(modeBuf->delModesParams[i]); + modeBuf->delModesParams[i] = NULL; + } + } + modeBuf->delCount = 0; + } + modeStr[modePos++] = '\0'; + putsock(modeBuf->client, "MODE %s %s%s", modeBuf->chan->name, modeStr, paramStr); +} + +void freeModeBuffer(struct ModeBuffer *modeBuf) { + if(modeBuf->addCount + modeBuf->delCount) + flushModeBuffer(modeBuf); + free(modeBuf); +} + +int is_ircmask(const char *text) { + while (*text && (isalnum((char)*text) || strchr("-_[]|\\`^{}?*", *text))) + text++; + if (*text++ != '!') + return 0; + while (*text && *text != '@' && !isspace((char)*text)) + text++; + if (*text++ != '@') + return 0; + while (*text && !isspace((char)*text)) + text++; + return !*text; +} + +char* generate_banmask(struct UserNode *user, char *buffer) { + char *userhost = user->host; + + if(isFakeHost(user->host)) { + sprintf(buffer, "*!*@%s", userhost); + return buffer; + } + + //check if the hostname has more than 4 connections (trusted host) + if(countUsersWithHost(userhost) > 4) { + sprintf(buffer, "*!%s@%s", user->ident, userhost); + return buffer; + } else { + sprintf(buffer, "*!*@%s", userhost); + return buffer; + } +} + +char* make_banmask(char *input, char* buffer) { + char *nick = NULL, *ident = NULL, *host = NULL; + char tmp[HOSTLEN]; + char *p; + if((p = strstr(input, "!"))) { + nick = input; + *p = '\0'; + ident = p+1; + if((p = strstr(ident, "@"))) { + *p = '\0'; + host = p+1; + } + } else if((p = strstr(input, "@"))) { + ident = input; + *p = '\0'; + host = p+1; + } else if((p = strstr(input, ".")) || (p = strstr(input, ":"))) { + host = input; + } else if(*input == '*' && input[1] != '\0' && !strstr(input+1, "*")) { + //AUTH MASK + p = getAuthFakehost(input+1); + if(p) + host = p; + else { + sprintf(tmp, "%s.*", input+1); + host = tmp; + } + } else { + struct UserNode *user = searchUserByNick(input); + if(user) + return generate_banmask(user, buffer); + else + nick = input; + } + if(nick && *nick == '\0') nick = NULL; + if(ident && *ident == '\0') ident = NULL; + if(host && *host == '\0') host = NULL; + sprintf(buffer, "%s!%s@%s", (nick ? nick : "*"), (ident ? ident : "*"), (host ? host : "*")); + return buffer; +} + +int isFakeHost(char *host) { + char *p1, *p2 = host; + + //find the last dot to identify if the hostmask is a fake host + while((p1 = strchr(p2, '.'))) { + p2 = p1 + 1; + } + //TLD database: http://www.iana.org/domains/root/db/ + //the longest TLD i found was 6 chars long (ignoring the stange exotic ones :D) + //but we even ignore '.museum' and '.travel' so we can say that the TLD of our mask needs to be less than 4 chars to be a real domain + return (strlen(p2+1) > 4); +} + +int mask_match(char *mask, struct UserNode *user) { + char usermask[NICKLEN+USERLEN+HOSTLEN+3]; + char matchmask[strlen(mask)+3]; + strcpy(matchmask, mask); + char *host = strchr(mask, '@'); + if(host) { + struct IPNode *ip = createIPNode(host); + int bits = (ip->flags & IPNODE_IS_IPV6 ? 128 : 32); + if((host = strchr(host, '/'))) { + bits = atoi(host+1); + } + if(ip && user->ip&& !ipmatch(user->ip, ip, bits)) { + host[1] = '*'; + host[2] = '\0'; + } + } + sprintf(usermask, "%s!%s@%s", user->nick, user->ident, user->host); + return match(matchmask, usermask); +} + +static unsigned long crc_table[256]; + +static void crc32_init() { + unsigned long crc; + int i, j; + for(i = 0; i < 256; i++) { + crc = i; + for(j = 8; j > 0; j--) { + if(crc & 1) + crc = (crc >> 1) ^ 0xEDB88320L; + else + crc >>= 1; + } + crc_table[i] = crc; + } +} + +unsigned long crc32(const char *text) { + register unsigned long crc = 0xFFFFFFFF; + unsigned int c, i = 0; + while((c = (unsigned int)text[i++]) != 0) + crc = ((crc >> 8) & 0x00FFFFFF) ^ crc_table[(crc^c) & 0xFF]; + return (crc^0xFFFFFFFF); +} + +int stricmp (const char *s1, const char *s2) { + return stricmplen(s1, s2, -1); +} + +int stricmplen(const char *s1, const char *s2, int len) { + if (s1 == NULL) + return (s2 == NULL ? 0 : -(*s2)); + if (s2 == NULL) + return *s1; + char c1, c2; + int i = 0; + while ((c1 = tolower(*s1)) == (c2 = tolower(*s2))) { + if (*s1 == '\0') + break; + i++; + s1++; + s2++; + if(len != -1 && i == len) break; + } + return c1 - c2; +} + +void init_tools() { + register_default_language_table(msgtab); + crc32_init(); +} diff --git a/src/tools.h b/src/tools.h new file mode 100644 index 0000000..bab14cb --- /dev/null +++ b/src/tools.h @@ -0,0 +1,101 @@ +/* tools.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _tools_h +#define _tools_h + +#include "main.h" + +#define TABLE_FLAG_USE_POINTER 0x01 +#define TABLE_FLAG_COL_BOLD 0x02 +#define TABLE_FLAG_COL_CONTENTS 0x04 +#define TABLE_FLAG_COL_SKIP_NULL 0x08 + +struct ClientSocket; +struct UserNode; +struct ChanNode; + +struct Table { + char ***contents; + int length; + int width; + int flags; + int *col_flags; + + int entrys; + int *maxwidth; + + char **table_lines; //we just store this to free it in table_free +}; + +struct ModeBuffer { + struct ClientSocket *client; + struct ChanNode *chan; + char addModes[MAXMODES]; + char delModes[MAXMODES]; + char *addModesParams[MAXMODES]; + char *delModesParams[MAXMODES]; + int addCount; + int delCount; +}; + +#define modeBufferSimpleMode(MODEBUF,ADD,MODE) modeBufferSet(MODEBUF, ADD, MODE, NULL) +#define modeBufferOp(MODEBUF,USER) modeBufferSet(MODEBUF, 1, 'o', USER) +#define modeBufferDeop(MODEBUF,USER) modeBufferSet(MODEBUF, 0, 'o', USER) +#define modeBufferHalfop(MODEBUF,USER) modeBufferSet(MODEBUF, 1, 'h', USER) +#define modeBufferDehalfop(MODEBUF,USER) modeBufferSet(MODEBUF, 0, 'h', USER) +#define modeBufferVoice(MODEBUF,USER) modeBufferSet(MODEBUF, 1, 'v', USER) +#define modeBufferDevoice(MODEBUF,USER) modeBufferSet(MODEBUF, 0, 'v', USER) +#define modeBufferBan(MODEBUF,MASK) modeBufferSet(MODEBUF, 1, 'b', MASK) +#define modeBufferUnban(MODEBUF,MASK) modeBufferSet(MODEBUF, 0, 'b', MASK) + +#ifndef DND_FUNCTIONS +/* MODULAR ACCESSIBLE */ int match(const char *mask, const char *name); + +/* MODULAR ACCESSIBLE */ struct Table *table_init(int width, int length, int flags); +/* MODULAR ACCESSIBLE */ int table_add(struct Table *table, char **entry); +/* MODULAR ACCESSIBLE */ int table_change(struct Table *table, int row, char **entry); +/* MODULAR ACCESSIBLE */ int table_change_field(struct Table *table, int row, int col, char *entry); +/* MODULAR ACCESSIBLE */ int table_set_bold(struct Table *table, int collum, int bold); +/* MODULAR ACCESSIBLE */ char **table_end(struct Table *table); +/* MODULAR ACCESSIBLE */ void table_free(struct Table *table); + +/* MODULAR ACCESSIBLE */ char* timeToStr(struct UserNode *user, int seconds, int items, char *buf); +/* MODULAR ACCESSIBLE */ int strToTime(struct UserNode *user, char *str); +/* MODULAR ACCESSIBLE */ int getCurrentSecondsOfDay(); + +/* MODULAR ACCESSIBLE */ struct ModeBuffer* initModeBuffer(struct ClientSocket *client, struct ChanNode *chan); +/* MODULAR ACCESSIBLE */ void modeBufferSet(struct ModeBuffer *modeBuf, int add, char mode, char *param); +/* MODULAR ACCESSIBLE */ void flushModeBuffer(struct ModeBuffer *modeBuf); +/* MODULAR ACCESSIBLE */ void freeModeBuffer(struct ModeBuffer *modeBuf); + +/* MODULAR ACCESSIBLE */ int is_ircmask(const char *text); + +/* MODULAR ACCESSIBLE */ char* generate_banmask(struct UserNode *user, char *buffer); +/* MODULAR ACCESSIBLE */ char* make_banmask(char *input, char* buffer); +/* MODULAR ACCESSIBLE */ int isFakeHost(char *host); + +/* MODULAR ACCESSIBLE */ int mask_match(char *mask, struct UserNode *user); + +/* MODULAR ACCESSIBLE */ unsigned long crc32(const char *text); + +/* MODULAR ACCESSIBLE */ int stricmp (const char *s1, const char *s2); +/* MODULAR ACCESSIBLE */ int stricmplen (const char *s1, const char *s2, int len); + +void init_tools(); + +#endif +#endif diff --git a/src/version.h b/src/version.h new file mode 100644 index 0000000..ec8c207 --- /dev/null +++ b/src/version.h @@ -0,0 +1,41 @@ +/* version.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _version_h +#define _version_h + +#include "main.h" + +#define MODULE_VERSION 7 + +#ifndef DND_FUNCTIONS +extern const char *compilation; +extern const char *creation; +extern const char *revision; +extern const char *codelines; +extern const int revision_master; +extern const char *dev_revision; +extern const int patchlevel; + +const char *get_compilation(); +const char *get_creation(); +const char *get_revision(); +const int is_stable_revision(); +const char *get_dev_revision(); +const char *get_codelines(); +const int get_patchlevel(); +#endif +#endif diff --git a/src/version.sh b/src/version.sh new file mode 100755 index 0000000..1e64d41 --- /dev/null +++ b/src/version.sh @@ -0,0 +1,103 @@ +#! /bin/sh +echo "Extracting version.c ..." + +if test -r version.c +then + compilation=`sed -n 's/^const char \*compilation = \"\(.*\)\";/\1/p' < version.c` + if test ! "$compilation" ; then compilation=0; fi +else + compilation=0 +fi + +compilation=`expr $compilation + 1` + +creation=`date | \ +awk '{if (NF == 6) \ + { print $1 " " $2 " " $3 " " $6 " at " $4 " " $5 } \ +else \ + { print $1 " " $2 " " $3 " " $7 " at " $4 " " $5 " " $6 }}'` + +codelines=`find . -type f -regex '\./.*\.h' -or -regex '\./.*\.c' |xargs cat|wc -l` + + +git_revision_id=`git rev-list -n 1 --pretty="format:%h" --header refs/heads/master | grep '^[0-9a-f]*$'` +if test "x$git_revision_id" = "x" ; then + git_revision="0" + git_commitcount="0" + git_is_stable="1" + git_dev_rev="" +else + git_commitcount=`git rev-list --oneline --first-parent refs/heads/master | wc -l | sed "s/[ \t]//g"` + git_revision="git-$git_revision_id" + + git_real_revision_id=`git rev-list -n 1 --pretty="format:%h" --header HEAD | grep '^[0-9a-f]*$'` + if test "$git_revision_id" = "$git_real_revision_id" ; then + git_is_stable="1" + git_dev_rev="" + else + git_is_stable="0" + git_dev_rev="git-$git_real_revision_id" + fi +fi + + +/bin/cat > version.c <. + */ +//Auto generated file! + +#include "version.h" + +const char *compilation = "$compilation"; +const char *creation = "$creation"; +const char *revision = "$git_revision"; +const char *codelines = "$codelines"; +const int revision_master = $git_is_stable; +const char *dev_revision = "$git_dev_rev"; + +const int patchlevel = ($git_commitcount ? ($git_commitcount - VERSION_PATCHLEVEL) : 0); + +const char *get_compilation() { + return compilation; +} + +const char *get_creation() { + return creation; +} + +const char *get_revision() { + return revision; +} + +const char *get_codelines() { + return codelines; +} + +const int is_stable_revision() { + return revision_master; +} + +const char *get_dev_revision() { + return dev_revision; +} + +const int get_patchlevel() { + return patchlevel; +} + +!SUB!THIS! +