diff --git a/config/mingw32/config.h b/config/mingw32/config.h new file mode 100644 index 00000000..d1910322 --- /dev/null +++ b/config/mingw32/config.h @@ -0,0 +1,110 @@ +/* src/config.h. Generated from config.h.in by configure. */ +/* include/config.h.in. Generated from configure.in by autoheader. */ + +/* Define to 1 if you have the `getaddrinfo' function. */ +/* #undef HAVE_GETADDRINFO */ + +/* Define to 1 if you have the `gethostbyname_r' function. */ +/* #undef HAVE_GETHOSTBYNAME_R */ + +/* Define to 1 if you have the `inet_ntoa' function. */ +/* #undef HAVE_INET_NTOA */ + +/* Define to 1 if you have the `inet_pton' function. */ +/* #undef HAVE_INET_PTON */ + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `localtime_r' function. */ +/* #undef HAVE_LOCALTIME_R */ + +/* Define to 1 if your system has a GNU libc compatible `malloc' function, and + to 0 otherwise. */ +#define HAVE_MALLOC 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `socket' function. */ +/* #undef HAVE_SOCKET */ + +/* Define to 1 if `stat' has the bug that it succeeds when given the + zero-length file name argument. */ +/* #undef HAVE_STAT_EMPTY_STRING_BUG */ + +/* Define to 1 if stdbool.h conforms to C99. */ +#define HAVE_STDBOOL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_SELECT_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_SOCKET_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if the system has the type `_Bool'. */ +#define HAVE__BOOL 1 + +/* Define to 1 if `lstat' dereferences a symlink specified with a trailing + slash. */ +/* #undef LSTAT_FOLLOWS_SLASHED_SYMLINK */ + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "gyunaev@ulduzsoft.com" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "libircclient" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "libircclient 1.3" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "libircclient" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "1.3" + +/* Define to the type of arg 1 for `select'. */ +#define SELECT_TYPE_ARG1 int + +/* Define to the type of args 2, 3 and 4 for `select'. */ +#define SELECT_TYPE_ARG234 (int *) + +/* Define to the type of arg 5 for `select'. */ +#define SELECT_TYPE_ARG5 (struct timeval *) + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define to 1 if you can safely include both and . */ +#define TIME_WITH_SYS_TIME 1 + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to rpl_malloc if the replacement function should be used. */ +/* #undef malloc */ + +/* Define to `unsigned int' if does not define. */ +/* #undef size_t */ diff --git a/config/mingw64/config.h b/config/mingw64/config.h new file mode 100644 index 00000000..d1910322 --- /dev/null +++ b/config/mingw64/config.h @@ -0,0 +1,110 @@ +/* src/config.h. Generated from config.h.in by configure. */ +/* include/config.h.in. Generated from configure.in by autoheader. */ + +/* Define to 1 if you have the `getaddrinfo' function. */ +/* #undef HAVE_GETADDRINFO */ + +/* Define to 1 if you have the `gethostbyname_r' function. */ +/* #undef HAVE_GETHOSTBYNAME_R */ + +/* Define to 1 if you have the `inet_ntoa' function. */ +/* #undef HAVE_INET_NTOA */ + +/* Define to 1 if you have the `inet_pton' function. */ +/* #undef HAVE_INET_PTON */ + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `localtime_r' function. */ +/* #undef HAVE_LOCALTIME_R */ + +/* Define to 1 if your system has a GNU libc compatible `malloc' function, and + to 0 otherwise. */ +#define HAVE_MALLOC 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `socket' function. */ +/* #undef HAVE_SOCKET */ + +/* Define to 1 if `stat' has the bug that it succeeds when given the + zero-length file name argument. */ +/* #undef HAVE_STAT_EMPTY_STRING_BUG */ + +/* Define to 1 if stdbool.h conforms to C99. */ +#define HAVE_STDBOOL_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_SELECT_H */ + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_SYS_SOCKET_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if the system has the type `_Bool'. */ +#define HAVE__BOOL 1 + +/* Define to 1 if `lstat' dereferences a symlink specified with a trailing + slash. */ +/* #undef LSTAT_FOLLOWS_SLASHED_SYMLINK */ + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "gyunaev@ulduzsoft.com" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "libircclient" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "libircclient 1.3" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "libircclient" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "1.3" + +/* Define to the type of arg 1 for `select'. */ +#define SELECT_TYPE_ARG1 int + +/* Define to the type of args 2, 3 and 4 for `select'. */ +#define SELECT_TYPE_ARG234 (int *) + +/* Define to the type of arg 5 for `select'. */ +#define SELECT_TYPE_ARG5 (struct timeval *) + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define to 1 if you can safely include both and . */ +#define TIME_WITH_SYS_TIME 1 + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to rpl_malloc if the replacement function should be used. */ +/* #undef malloc */ + +/* Define to `unsigned int' if does not define. */ +/* #undef size_t */ diff --git a/external/IRC/LICENSE b/external/IRC/LICENSE new file mode 100644 index 00000000..161a3d1d --- /dev/null +++ b/external/IRC/LICENSE @@ -0,0 +1,482 @@ + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library 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 Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "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 +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY 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 +LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/external/IRC/colors.c b/external/IRC/colors.c new file mode 100644 index 00000000..6e31b132 --- /dev/null +++ b/external/IRC/colors.c @@ -0,0 +1,388 @@ +/* + * Copyright (C) 2004-2012 George Yunaev gyunaev@ulduzsoft.com + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + */ + +#include + +#define LIBIRC_COLORPARSER_BOLD (1<<1) +#define LIBIRC_COLORPARSER_UNDERLINE (1<<2) +#define LIBIRC_COLORPARSER_REVERSE (1<<3) +#define LIBIRC_COLORPARSER_COLOR (1<<4) + +#define LIBIRC_COLORPARSER_MAXCOLORS 15 + + +static const char * color_replacement_table[] = +{ + "WHITE", + "BLACK", + "DARKBLUE", + "DARKGREEN", + "RED", + "BROWN", + "PURPLE", + "OLIVE", + "YELLOW", + "GREEN", + "TEAL", + "CYAN", + "BLUE", + "MAGENTA", + "DARKGRAY", + "LIGHTGRAY", + 0 +}; + + +static inline void libirc_colorparser_addorcat (char ** destline, unsigned int * destlen, const char * str) +{ + unsigned int len = strlen(str); + + if ( *destline ) + { + strcpy (*destline, str); + *destline += len; + } + else + *destlen += len; +} + + +static void libirc_colorparser_applymask (unsigned int * mask, + char ** destline, unsigned int * destlen, + unsigned int bitmask, const char * start, const char * end) +{ + if ( (*mask & bitmask) != 0 ) + { + *mask &= ~bitmask; + libirc_colorparser_addorcat (destline, destlen, end); + } + else + { + *mask |= bitmask; + libirc_colorparser_addorcat (destline, destlen, start); + } +} + + +static void libirc_colorparser_applycolor (unsigned int * mask, + char ** destline, unsigned int * destlen, + unsigned int colorid, unsigned int bgcolorid) +{ + const char * end = "[/COLOR]"; + char startbuf[64]; + + if ( bgcolorid != 0 ) + sprintf (startbuf, "[COLOR=%s/%s]", color_replacement_table[colorid], color_replacement_table[bgcolorid]); + else + sprintf (startbuf, "[COLOR=%s]", color_replacement_table[colorid]); + + if ( (*mask & LIBIRC_COLORPARSER_COLOR) != 0 ) + libirc_colorparser_addorcat (destline, destlen, end); + + *mask |= LIBIRC_COLORPARSER_COLOR; + libirc_colorparser_addorcat (destline, destlen, startbuf); +} + + +static void libirc_colorparser_closetags (unsigned int * mask, + char ** destline, unsigned int * destlen) +{ + if ( *mask & LIBIRC_COLORPARSER_BOLD ) + libirc_colorparser_applymask (mask, destline, destlen, LIBIRC_COLORPARSER_BOLD, 0, "[/B]"); + + if ( *mask & LIBIRC_COLORPARSER_UNDERLINE ) + libirc_colorparser_applymask (mask, destline, destlen, LIBIRC_COLORPARSER_UNDERLINE, 0, "[/U]"); + + if ( *mask & LIBIRC_COLORPARSER_REVERSE ) + libirc_colorparser_applymask (mask, destline, destlen, LIBIRC_COLORPARSER_REVERSE, 0, "[/I]"); + + if ( *mask & LIBIRC_COLORPARSER_COLOR ) + libirc_colorparser_applymask (mask, destline, destlen, LIBIRC_COLORPARSER_COLOR, 0, "[/COLOR]"); +} + + + +/* + * IRC to [code] color conversion. Or strip. + */ +static char * libirc_colorparser_irc2code (const char * source, int strip) +{ + unsigned int mask = 0, destlen = 0; + char * destline = 0, *d = 0; + const char *p; + int current_bg = 0; + + /* + * There will be two passes. First pass calculates the total length of + * the destination string. The second pass allocates memory for the string, + * and fills it. + */ + while ( destline == 0 ) // destline will be set after the 2nd pass + { + if ( destlen > 0 ) + { + // This is the 2nd pass; allocate memory. + if ( (destline = malloc (destlen)) == 0 ) + return 0; + + d = destline; + } + + for ( p = source; *p; p++ ) + { + switch (*p) + { + case 0x02: // bold + if ( strip ) + continue; + + libirc_colorparser_applymask (&mask, &d, &destlen, LIBIRC_COLORPARSER_BOLD, "[B]", "[/B]"); + break; + + case 0x1F: // underline + if ( strip ) + continue; + + libirc_colorparser_applymask (&mask, &d, &destlen, LIBIRC_COLORPARSER_UNDERLINE, "[U]", "[/U]"); + break; + + case 0x16: // reverse + if ( strip ) + continue; + + libirc_colorparser_applymask (&mask, &d, &destlen, LIBIRC_COLORPARSER_REVERSE, "[I]", "[/I]"); + break; + + case 0x0F: // reset colors + if ( strip ) + continue; + + libirc_colorparser_closetags (&mask, &d, &destlen); + break; + + case 0x03: // set color + if ( isdigit (p[1]) ) + { + // Parse + int bgcolor = -1, color = p[1] - 0x30; + p++; + + if ( isdigit (p[1]) ) + { + color = color * 10 + (p[1] - 0x30); + p++; + } + + // If there is a comma, search for the following + // background color + if ( p[1] == ',' && isdigit (p[2]) ) + { + bgcolor = p[2] - 0x30; + p += 2; + + if ( isdigit (p[1]) ) + { + bgcolor = bgcolor * 10 + (p[1] - 0x30); + p++; + } + } + + // Check for range + if ( color <= LIBIRC_COLORPARSER_MAXCOLORS + && bgcolor <= LIBIRC_COLORPARSER_MAXCOLORS ) + { + if ( strip ) + continue; + + if ( bgcolor != -1 ) + current_bg = bgcolor; + + libirc_colorparser_applycolor (&mask, &d, &destlen, color, current_bg); + } + } + break; + + default: + if ( destline ) + *d++ = *p; + else + destlen++; + break; + } + } + + // Close all the opened tags + libirc_colorparser_closetags (&mask, &d, &destlen); + destlen++; // for 0-terminator + } + + *d = '\0'; + return destline; +} + + +static int libirc_colorparser_colorlookup (const char * color) +{ + int i; + for ( i = 0; color_replacement_table[i]; i++ ) + if ( !strcmp (color, color_replacement_table[i]) ) + return i; + + return -1; +} + + +/* + * [code] to IRC color conversion. + */ +char * irc_color_convert_to_mirc (const char * source) +{ + unsigned int destlen = 0; + char * destline = 0, *d = 0; + const char *p1, *p2, *cur; + + /* + * There will be two passes. First pass calculates the total length of + * the destination string. The second pass allocates memory for the string, + * and fills it. + */ + while ( destline == 0 ) // destline will be set after the 2nd pass + { + if ( destlen > 0 ) + { + // This is the 2nd pass; allocate memory. + if ( (destline = malloc (destlen)) == 0 ) + return 0; + + d = destline; + } + + cur = source; + while ( (p1 = strchr (cur, '[')) != 0 ) + { + const char * replacedval = 0; + p2 = 0; + + // Check if the closing bracket is available after p1 + // and the tag length is suitable + if ( p1[1] != '\0' + && (p2 = strchr (p1, ']')) != 0 + && (p2 - p1) > 1 + && (p2 - p1) < 31 ) + { + // Get the tag + char tagbuf[32]; + int taglen = p2 - p1 - 1; + + memcpy (tagbuf, p1 + 1, taglen); + tagbuf[taglen] = '\0'; + + if ( !strcmp (tagbuf, "/COLOR") ) + replacedval = "\x0F"; + else if ( strstr (tagbuf, "COLOR=") == tagbuf ) + { + int color, bgcolor = -2; + char * bcol; + + bcol = strchr (tagbuf + 6, '/'); + + if ( bcol ) + { + *bcol++ = '\0'; + bgcolor = libirc_colorparser_colorlookup (bcol); + } + + color = libirc_colorparser_colorlookup (tagbuf + 6); + + if ( color != -1 && bgcolor == -2 ) + { + sprintf (tagbuf, "\x03%02d", color); + replacedval = tagbuf; + } + else if ( color != -1 && bgcolor >= 0 ) + { + sprintf (tagbuf, "\x03%02d,%02d", color, bgcolor); + replacedval = tagbuf; + } + } + else if ( !strcmp (tagbuf, "B") || !strcmp (tagbuf, "/B") ) + replacedval = "\x02"; + else if ( !strcmp (tagbuf, "U") || !strcmp (tagbuf, "/U") ) + replacedval = "\x1F"; + else if ( !strcmp (tagbuf, "I") || !strcmp (tagbuf, "/I") ) + replacedval = "\x16"; + } + + if ( replacedval ) + { + // add a part before the tag + int partlen = p1 - cur; + + if ( destline ) + { + memcpy (d, cur, partlen); + d += partlen; + } + else + destlen += partlen; + + // Add the replacement + libirc_colorparser_addorcat (&d, &destlen, replacedval); + + // And move the pointer + cur = p2 + 1; + } + else + { + // add a whole part before the end tag + int partlen; + + if ( !p2 ) + p2 = cur + strlen(cur); + + partlen = p2 - cur + 1; + + if ( destline ) + { + memcpy (d, cur, partlen); + d += partlen; + } + else + destlen += partlen; + + // And move the pointer + cur = p2 + 1; + } + } + + // Add the rest of string + libirc_colorparser_addorcat (&d, &destlen, cur); + destlen++; // for 0-terminator + } + + *d = '\0'; + return destline; +} + + +char * irc_color_strip_from_mirc (const char * message) +{ + return libirc_colorparser_irc2code (message, 1); +} + + +char * irc_color_convert_from_mirc (const char * message) +{ + return libirc_colorparser_irc2code (message, 0); +} diff --git a/external/IRC/dcc.c b/external/IRC/dcc.c new file mode 100644 index 00000000..00ccb5f6 --- /dev/null +++ b/external/IRC/dcc.c @@ -0,0 +1,892 @@ +/* + * Copyright (C) 2004-2012 George Yunaev gyunaev@ulduzsoft.com + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + */ + +#define LIBIRC_DCC_CHAT 1 +#define LIBIRC_DCC_SENDFILE 2 +#define LIBIRC_DCC_RECVFILE 3 + + +static irc_dcc_session_t * libirc_find_dcc_session (irc_session_t * session, irc_dcc_t dccid, int lock_list) +{ + irc_dcc_session_t * s, *found = 0; + + if ( lock_list ) + libirc_mutex_lock (&session->mutex_dcc); + + for ( s = session->dcc_sessions; s; s = s->next ) + { + if ( s->id == dccid ) + { + found = s; + break; + } + } + + if ( found == 0 && lock_list ) + libirc_mutex_unlock (&session->mutex_dcc); + + return found; +} + + +static void libirc_dcc_destroy_nolock (irc_session_t * session, irc_dcc_t dccid) +{ + irc_dcc_session_t * dcc = libirc_find_dcc_session (session, dccid, 0); + + if ( dcc ) + { + if ( dcc->sock >= 0 ) + socket_close (&dcc->sock); + + dcc->state = LIBIRC_STATE_REMOVED; + } +} + + +static void libirc_remove_dcc_session (irc_session_t * session, irc_dcc_session_t * dcc, int lock_list) +{ + if ( dcc->sock >= 0 ) + socket_close (&dcc->sock); + + if ( dcc->dccsend_file_fp ) + fclose (dcc->dccsend_file_fp); + + dcc->dccsend_file_fp = 0; + + libirc_mutex_destroy (&dcc->mutex_outbuf); + + if ( lock_list ) + libirc_mutex_lock (&session->mutex_dcc); + + if ( session->dcc_sessions != dcc ) + { + irc_dcc_session_t * s; + for ( s = session->dcc_sessions; s; s = s->next ) + { + if ( s->next == dcc ) + { + s->next = dcc->next; + break; + } + } + } + else + session->dcc_sessions = dcc->next; + + if ( lock_list ) + libirc_mutex_unlock (&session->mutex_dcc); + + free (dcc); +} + + +static void libirc_dcc_add_descriptors (irc_session_t * ircsession, fd_set *in_set, fd_set *out_set, int * maxfd) +{ + irc_dcc_session_t * dcc, *dcc_next; + time_t now = time (0); + + libirc_mutex_lock (&ircsession->mutex_dcc); + + // Preprocessing DCC list: + // - ask DCC send callbacks for data; + // - remove unused DCC structures + for ( dcc = ircsession->dcc_sessions; dcc; dcc = dcc_next ) + { + dcc_next = dcc->next; + + // Remove timed-out sessions + if ( (dcc->state == LIBIRC_STATE_CONNECTING + || dcc->state == LIBIRC_STATE_INIT + || dcc->state == LIBIRC_STATE_LISTENING) + && now - dcc->timeout > ircsession->dcc_timeout ) + { + // Inform the caller about DCC timeout. + // Do not inform when state is LIBIRC_STATE_INIT - session + // was initiated from someone else, and callbacks aren't set yet. + if ( dcc->state != LIBIRC_STATE_INIT ) + { + libirc_mutex_unlock (&ircsession->mutex_dcc); + + if ( dcc->cb ) + (*dcc->cb)(ircsession, dcc->id, LIBIRC_ERR_TIMEOUT, dcc->ctx, 0, 0); + + libirc_mutex_lock (&ircsession->mutex_dcc); + } + + libirc_remove_dcc_session (ircsession, dcc, 0); + } + + /* + * If we're sending file, and the output buffer is empty, we need + * to provide some data. + */ + if ( dcc->state == LIBIRC_STATE_CONNECTED + && dcc->dccmode == LIBIRC_DCC_SENDFILE + && dcc->dccsend_file_fp + && dcc->outgoing_offset == 0 ) + { + int len = fread (dcc->outgoing_buf, 1, sizeof (dcc->outgoing_buf), dcc->dccsend_file_fp); + + if ( len <= 0 ) + { + int err = (len < 0 ? LIBIRC_ERR_READ : 0); + + libirc_mutex_unlock (&ircsession->mutex_dcc); + + (*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, 0); + libirc_mutex_lock (&ircsession->mutex_dcc); + libirc_dcc_destroy_nolock (ircsession, dcc->id); + } + else + dcc->outgoing_offset = len; + } + + // Clean up unused sessions + if ( dcc->state == LIBIRC_STATE_REMOVED ) + libirc_remove_dcc_session (ircsession, dcc, 0); + } + + for ( dcc = ircsession->dcc_sessions; dcc; dcc = dcc->next ) + { + switch (dcc->state) + { + case LIBIRC_STATE_LISTENING: + // While listening, only in_set descriptor should be set + libirc_add_to_set (dcc->sock, in_set, maxfd); + break; + + case LIBIRC_STATE_CONNECTING: + // While connection, only out_set descriptor should be set + libirc_add_to_set (dcc->sock, out_set, maxfd); + break; + + case LIBIRC_STATE_CONNECTED: + // Add input descriptor if there is space in input buffer + // and it is DCC chat (during DCC send, there is nothing to recv) + if ( dcc->incoming_offset < sizeof(dcc->incoming_buf) - 1 ) + libirc_add_to_set (dcc->sock, in_set, maxfd); + + // Add output descriptor if there is something in output buffer + libirc_mutex_lock (&dcc->mutex_outbuf); + + if ( dcc->outgoing_offset > 0 ) + libirc_add_to_set (dcc->sock, out_set, maxfd); + + libirc_mutex_unlock (&dcc->mutex_outbuf); + break; + + case LIBIRC_STATE_CONFIRM_SIZE: + /* + * If we're receiving file, then WE should confirm the transferred + * part (so we have to sent data). But if we're sending the file, + * then RECEIVER should confirm the packet, so we have to receive + * data. + * + * We don't need to LOCK_DCC_OUTBUF - during file transfer, buffers + * can't change asynchronously. + */ + if ( dcc->dccmode == LIBIRC_DCC_RECVFILE && dcc->outgoing_offset > 0 ) + libirc_add_to_set (dcc->sock, out_set, maxfd); + + if ( dcc->dccmode == LIBIRC_DCC_SENDFILE && dcc->incoming_offset < 4 ) + libirc_add_to_set (dcc->sock, in_set, maxfd); + } + } + + libirc_mutex_unlock (&ircsession->mutex_dcc); +} + + +static void libirc_dcc_process_descriptors (irc_session_t * ircsession, fd_set *in_set, fd_set *out_set) +{ + irc_dcc_session_t * dcc; + + /* + * We need to use such a complex scheme here, because on every callback + * a number of DCC sessions could be destroyed. + */ + libirc_mutex_lock (&ircsession->mutex_dcc); + + for ( dcc = ircsession->dcc_sessions; dcc; dcc = dcc->next ) + { + if ( dcc->state == LIBIRC_STATE_LISTENING + && FD_ISSET (dcc->sock, in_set) ) + { + socklen_t len = sizeof(dcc->remote_addr); + int nsock, err = 0; + + // New connection is available; accept it. + if ( socket_accept (&dcc->sock, (socket_t*)&nsock, (struct sockaddr *) &dcc->remote_addr, &len) ) + err = LIBIRC_ERR_ACCEPT; + + // On success, change the active socket and change the state + if ( err == 0 ) + { + // close the listen socket, and replace it by a newly + // accepted + socket_close (&dcc->sock); + dcc->sock = nsock; + dcc->state = LIBIRC_STATE_CONNECTED; + } + + // If this is DCC chat, inform the caller about accept() + // success or failure. + // Otherwise (DCC send) there is no reason. + if ( dcc->dccmode == LIBIRC_DCC_CHAT ) + { + libirc_mutex_unlock (&ircsession->mutex_dcc); + (*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, 0); + libirc_mutex_lock (&ircsession->mutex_dcc); + } + + if ( err ) + libirc_dcc_destroy_nolock (ircsession, dcc->id); + } + + if ( dcc->state == LIBIRC_STATE_CONNECTING + && FD_ISSET (dcc->sock, out_set) ) + { + // Now we have to determine whether the socket is connected + // or the connect is failed + struct sockaddr_in saddr; + socklen_t slen = sizeof(saddr); + int err = 0; + + if ( getpeername (dcc->sock, (struct sockaddr*)&saddr, &slen) < 0 ) + err = LIBIRC_ERR_CONNECT; + + // On success, change the state + if ( err == 0 ) + dcc->state = LIBIRC_STATE_CONNECTED; + + // If this is DCC chat, inform the caller about connect() + // success or failure. + // Otherwise (DCC send) there is no reason. + if ( dcc->dccmode == LIBIRC_DCC_CHAT ) + { + libirc_mutex_unlock (&ircsession->mutex_dcc); + (*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, 0); + libirc_mutex_lock (&ircsession->mutex_dcc); + } + + if ( err ) + libirc_dcc_destroy_nolock (ircsession, dcc->id); + } + + if ( dcc->state == LIBIRC_STATE_CONNECTED + || dcc->state == LIBIRC_STATE_CONFIRM_SIZE ) + { + if ( FD_ISSET (dcc->sock, in_set) ) + { + int length, offset = 0, err = 0; + + unsigned int amount = sizeof (dcc->incoming_buf) - dcc->incoming_offset; + + length = socket_recv (&dcc->sock, dcc->incoming_buf + dcc->incoming_offset, amount); + + if ( length < 0 ) + { + err = LIBIRC_ERR_READ; + } + else if ( length == 0 ) + { + err = LIBIRC_ERR_CLOSED; + + if ( dcc->dccsend_file_fp ) + { + fclose (dcc->dccsend_file_fp); + dcc->dccsend_file_fp = 0; + } + } + else + { + dcc->incoming_offset += length; + + if ( dcc->dccmode != LIBIRC_DCC_CHAT ) + offset = dcc->incoming_offset; + else + offset = libirc_findcrorlf (dcc->incoming_buf, dcc->incoming_offset); + + /* + * In LIBIRC_STATE_CONFIRM_SIZE state we don't call any + * callbacks (except there is an error). We just receive + * the data, and compare it with the amount sent. + */ + if ( dcc->state == LIBIRC_STATE_CONFIRM_SIZE ) + { + if ( dcc->dccmode != LIBIRC_DCC_SENDFILE ) + abort(); + + if ( dcc->incoming_offset == 4 ) + { + // The order is big-endian + const unsigned char * bptr = (const unsigned char *) dcc->incoming_buf; + unsigned int received_size = (bptr[0] << 24) | (bptr[1] << 16) | (bptr[2] << 8) | bptr[3]; + + // Sent size confirmed + if ( dcc->file_confirm_offset == received_size ) + { + dcc->state = LIBIRC_STATE_CONNECTED; + dcc->incoming_offset = 0; + } + else + err = LIBIRC_ERR_WRITE; + } + } + else + { + /* + * If it is DCC_CHAT, we send a 0-terminated string + * (which is smaller than offset). Otherwise we send + * a full buffer. + */ + libirc_mutex_unlock (&ircsession->mutex_dcc); + + if ( dcc->dccmode != LIBIRC_DCC_CHAT ) + { + if ( dcc->dccmode != LIBIRC_DCC_RECVFILE ) + abort(); + + (*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, dcc->incoming_buf, offset); + + /* + * If the session is not terminated in callback, + * put the sent amount into the sent_packet_size_net_byteorder + */ + if ( dcc->state != LIBIRC_STATE_REMOVED ) + { + dcc->state = LIBIRC_STATE_CONFIRM_SIZE; + dcc->file_confirm_offset += offset; + + // Store as big endian + dcc->outgoing_buf[0] = (char) dcc->file_confirm_offset >> 24; + dcc->outgoing_buf[1] = (char) dcc->file_confirm_offset >> 16; + dcc->outgoing_buf[2] = (char) dcc->file_confirm_offset >> 8; + dcc->outgoing_buf[3] = (char) dcc->file_confirm_offset; + dcc->outgoing_offset = 4; + } + } + else + (*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, dcc->incoming_buf, strlen(dcc->incoming_buf)); + + libirc_mutex_lock (&ircsession->mutex_dcc); + + if ( dcc->incoming_offset - offset > 0 ) + memmove (dcc->incoming_buf, dcc->incoming_buf + offset, dcc->incoming_offset - offset); + + dcc->incoming_offset -= offset; + } + } + + /* + * If error arises somewhere above, we inform the caller + * of failure, and destroy this session. + */ + if ( err ) + { + libirc_mutex_unlock (&ircsession->mutex_dcc); + (*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, 0); + libirc_mutex_lock (&ircsession->mutex_dcc); + libirc_dcc_destroy_nolock (ircsession, dcc->id); + } + } + + /* + * Session might be closed (with sock = -1) after the in_set + * processing, so before out_set processing we should check + * for this case + */ + if ( dcc->state == LIBIRC_STATE_REMOVED ) + continue; + + /* + * Write bit set - we can send() something, and it won't block. + */ + if ( FD_ISSET (dcc->sock, out_set) ) + { + int length, offset, err = 0; + + /* + * Because in some cases outgoing_buf could be changed + * asynchronously (by another thread), we should lock + * it. + */ + libirc_mutex_lock (&dcc->mutex_outbuf); + + offset = dcc->outgoing_offset; + + if ( offset > 0 ) + { + length = socket_send (&dcc->sock, dcc->outgoing_buf, offset); + + if ( length < 0 ) + err = LIBIRC_ERR_WRITE; + else if ( length == 0 ) + err = LIBIRC_ERR_CLOSED; + else + { + /* + * If this was DCC_SENDFILE, and we just sent a packet, + * change the state to wait for confirmation (and store + * sent packet size) + */ + if ( dcc->state == LIBIRC_STATE_CONNECTED + && dcc->dccmode == LIBIRC_DCC_SENDFILE ) + { + dcc->file_confirm_offset += offset; + dcc->state = LIBIRC_STATE_CONFIRM_SIZE; + + libirc_mutex_unlock (&ircsession->mutex_dcc); + libirc_mutex_unlock (&dcc->mutex_outbuf); + (*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, offset); + libirc_mutex_lock (&ircsession->mutex_dcc); + libirc_mutex_lock (&dcc->mutex_outbuf); + } + + if ( dcc->outgoing_offset - length > 0 ) + memmove (dcc->outgoing_buf, dcc->outgoing_buf + length, dcc->outgoing_offset - length); + + dcc->outgoing_offset -= length; + + /* + * If we just sent the confirmation data, change state + * back. + */ + if ( dcc->state == LIBIRC_STATE_CONFIRM_SIZE + && dcc->dccmode == LIBIRC_DCC_RECVFILE + && dcc->outgoing_offset == 0 ) + { + /* + * If the file is already received, we should inform + * the caller, and close the session. + */ + if ( dcc->received_file_size == dcc->file_confirm_offset ) + { + libirc_mutex_unlock (&ircsession->mutex_dcc); + libirc_mutex_unlock (&dcc->mutex_outbuf); + (*dcc->cb)(ircsession, dcc->id, 0, dcc->ctx, 0, 0); + libirc_dcc_destroy_nolock (ircsession, dcc->id); + } + else + { + /* Continue to receive the file */ + dcc->state = LIBIRC_STATE_CONNECTED; + } + } + } + } + + libirc_mutex_unlock (&dcc->mutex_outbuf); + + /* + * If error arises somewhere above, we inform the caller + * of failure, and destroy this session. + */ + if ( err ) + { + libirc_mutex_unlock (&ircsession->mutex_dcc); + (*dcc->cb)(ircsession, dcc->id, err, dcc->ctx, 0, 0); + libirc_mutex_lock (&ircsession->mutex_dcc); + + libirc_dcc_destroy_nolock (ircsession, dcc->id); + } + } + } + } + + libirc_mutex_unlock (&ircsession->mutex_dcc); +} + + +static int libirc_new_dcc_session (irc_session_t * session, unsigned long ip, unsigned short port, int dccmode, void * ctx, irc_dcc_session_t ** pdcc) +{ + irc_dcc_session_t * dcc = malloc (sizeof(irc_dcc_session_t)); + + if ( !dcc ) + return LIBIRC_ERR_NOMEM; + + // setup + memset (dcc, 0, sizeof(irc_dcc_session_t)); + + dcc->dccsend_file_fp = 0; + + if ( libirc_mutex_init (&dcc->mutex_outbuf) ) + goto cleanup_exit_error; + + if ( socket_create (PF_INET, SOCK_STREAM, &dcc->sock) ) + goto cleanup_exit_error; + + if ( !ip ) + { + unsigned long arg = 1; + + setsockopt (dcc->sock, SOL_SOCKET, SO_REUSEADDR, (char*)&arg, sizeof(arg)); + +#if defined (ENABLE_IPV6) + if ( session->flags & SESSIONFL_USES_IPV6 ) + { + struct sockaddr_in6 saddr6; + + memset (&saddr6, 0, sizeof(saddr6)); + saddr6.sin6_family = AF_INET6; + memcpy (&saddr6.sin6_addr, &session->local_addr6, sizeof(session->local_addr6)); + saddr6.sin6_port = htons (0); + + if ( bind (dcc->sock, (struct sockaddr *) &saddr6, sizeof(saddr6)) < 0 ) + goto cleanup_exit_error; + } + else +#endif + { + struct sockaddr_in saddr; + memset (&saddr, 0, sizeof(saddr)); + saddr.sin_family = AF_INET; + memcpy (&saddr.sin_addr, &session->local_addr, sizeof(session->local_addr)); + saddr.sin_port = htons (0); + + if ( bind (dcc->sock, (struct sockaddr *) &saddr, sizeof(saddr)) < 0 ) + goto cleanup_exit_error; + } + + if ( listen (dcc->sock, 5) < 0 ) + goto cleanup_exit_error; + + dcc->state = LIBIRC_STATE_LISTENING; + } + else + { + // make socket non-blocking, so connect() call won't block + if ( socket_make_nonblocking (&dcc->sock) ) + goto cleanup_exit_error; + + memset (&dcc->remote_addr, 0, sizeof(dcc->remote_addr)); + dcc->remote_addr.sin_family = AF_INET; + dcc->remote_addr.sin_addr.s_addr = htonl (ip); // what idiot came up with idea to send IP address in host-byteorder? + dcc->remote_addr.sin_port = htons(port); + + dcc->state = LIBIRC_STATE_INIT; + } + + dcc->dccmode = dccmode; + dcc->ctx = ctx; + time (&dcc->timeout); + + // and store it + libirc_mutex_lock (&session->mutex_dcc); + + dcc->id = session->dcc_last_id++; + dcc->next = session->dcc_sessions; + session->dcc_sessions = dcc; + + libirc_mutex_unlock (&session->mutex_dcc); + + *pdcc = dcc; + return 0; + +cleanup_exit_error: + if ( dcc->sock >= 0 ) + socket_close (&dcc->sock); + + free (dcc); + return LIBIRC_ERR_SOCKET; +} + + +int irc_dcc_destroy (irc_session_t * session, irc_dcc_t dccid) +{ + // This function doesn't actually destroy the session; it just changes + // its state to "removed" and closes the socket. The memory is actually + // freed after the processing loop. + irc_dcc_session_t * dcc = libirc_find_dcc_session (session, dccid, 1); + + if ( !dcc ) + return 1; + + if ( dcc->sock >= 0 ) + socket_close (&dcc->sock); + + dcc->state = LIBIRC_STATE_REMOVED; + + libirc_mutex_unlock (&session->mutex_dcc); + return 0; +} + + +int irc_dcc_chat (irc_session_t * session, void * ctx, const char * nick, irc_dcc_callback_t callback, irc_dcc_t * dccid) +{ + struct sockaddr_in saddr; + socklen_t len = sizeof(saddr); + char cmdbuf[128], notbuf[128]; + irc_dcc_session_t * dcc; + int err; + + if ( session->state != LIBIRC_STATE_CONNECTED ) + { + session->lasterror = LIBIRC_ERR_STATE; + return 1; + } + + err = libirc_new_dcc_session (session, 0, 0, LIBIRC_DCC_CHAT, ctx, &dcc); + + if ( err ) + { + session->lasterror = err; + return 1; + } + + if ( getsockname (dcc->sock, (struct sockaddr*) &saddr, &len) < 0 ) + { + session->lasterror = LIBIRC_ERR_SOCKET; + libirc_remove_dcc_session (session, dcc, 1); + return 1; + } + + sprintf (notbuf, "DCC Chat (%s)", inet_ntoa (saddr.sin_addr)); + sprintf (cmdbuf, "DCC CHAT chat %lu %u", (unsigned long) ntohl (saddr.sin_addr.s_addr), ntohs (saddr.sin_port)); + + if ( irc_cmd_notice (session, nick, notbuf) + || irc_cmd_ctcp_request (session, nick, cmdbuf) ) + { + libirc_remove_dcc_session (session, dcc, 1); + return 1; + } + + *dccid = dcc->id; + dcc->cb = callback; + dcc->dccmode = LIBIRC_DCC_CHAT; + + return 0; +} + + +int irc_dcc_msg (irc_session_t * session, irc_dcc_t dccid, const char * text) +{ + irc_dcc_session_t * dcc = libirc_find_dcc_session (session, dccid, 1); + + if ( !dcc ) + return 1; + + if ( dcc->dccmode != LIBIRC_DCC_CHAT ) + { + session->lasterror = LIBIRC_ERR_INVAL; + libirc_mutex_unlock (&session->mutex_dcc); + return 1; + } + + if ( (strlen(text) + 2) >= (sizeof(dcc->outgoing_buf) - dcc->outgoing_offset) ) + { + session->lasterror = LIBIRC_ERR_NOMEM; + libirc_mutex_unlock (&session->mutex_dcc); + return 1; + } + + libirc_mutex_lock (&dcc->mutex_outbuf); + + strcpy (dcc->outgoing_buf + dcc->outgoing_offset, text); + dcc->outgoing_offset += strlen (text); + dcc->outgoing_buf[dcc->outgoing_offset++] = 0x0D; + dcc->outgoing_buf[dcc->outgoing_offset++] = 0x0A; + + libirc_mutex_unlock (&dcc->mutex_outbuf); + libirc_mutex_unlock (&session->mutex_dcc); + + return 0; +} + + +static void libirc_dcc_request (irc_session_t * session, const char * nick, const char * req) +{ + char filenamebuf[256]; + unsigned long ip, size; + unsigned short port; + + if ( sscanf (req, "DCC CHAT chat %lu %hu", &ip, &port) == 2 ) + { + if ( session->callbacks.event_dcc_chat_req ) + { + irc_dcc_session_t * dcc; + + int err = libirc_new_dcc_session (session, ip, port, LIBIRC_DCC_CHAT, 0, &dcc); + if ( err ) + { + session->lasterror = err; + return; + } + + (*session->callbacks.event_dcc_chat_req) (session, + nick, + inet_ntoa (dcc->remote_addr.sin_addr), + dcc->id); + } + + return; + } + else if ( sscanf (req, "DCC SEND %s %lu %hu %lu", filenamebuf, &ip, &port, &size) == 4 ) + { + if ( session->callbacks.event_dcc_send_req ) + { + irc_dcc_session_t * dcc; + + int err = libirc_new_dcc_session (session, ip, port, LIBIRC_DCC_RECVFILE, 0, &dcc); + if ( err ) + { + session->lasterror = err; + return; + } + + (*session->callbacks.event_dcc_send_req) (session, + nick, + inet_ntoa (dcc->remote_addr.sin_addr), + filenamebuf, + size, + dcc->id); + + dcc->received_file_size = size; + } + + return; + } +#if defined (ENABLE_DEBUG) + fprintf (stderr, "BUG: Unhandled DCC message: %s\n", req); + abort(); +#endif +} + + +int irc_dcc_accept (irc_session_t * session, irc_dcc_t dccid, void * ctx, irc_dcc_callback_t callback) +{ + irc_dcc_session_t * dcc = libirc_find_dcc_session (session, dccid, 1); + + if ( !dcc ) + return 1; + + if ( dcc->state != LIBIRC_STATE_INIT ) + { + session->lasterror = LIBIRC_ERR_STATE; + libirc_mutex_unlock (&session->mutex_dcc); + return 1; + } + + dcc->cb = callback; + dcc->ctx = ctx; + + // Initiate the connect + if ( socket_connect (&dcc->sock, (struct sockaddr *) &dcc->remote_addr, sizeof(dcc->remote_addr)) ) + { + libirc_dcc_destroy_nolock (session, dccid); + libirc_mutex_unlock (&session->mutex_dcc); + session->lasterror = LIBIRC_ERR_CONNECT; + return 1; + } + + dcc->state = LIBIRC_STATE_CONNECTING; + libirc_mutex_unlock (&session->mutex_dcc); + return 0; +} + + +int irc_dcc_decline (irc_session_t * session, irc_dcc_t dccid) +{ + irc_dcc_session_t * dcc = libirc_find_dcc_session (session, dccid, 1); + + if ( !dcc ) + return 1; + + if ( dcc->state != LIBIRC_STATE_INIT ) + { + session->lasterror = LIBIRC_ERR_STATE; + libirc_mutex_unlock (&session->mutex_dcc); + return 1; + } + + libirc_dcc_destroy_nolock (session, dccid); + libirc_mutex_unlock (&session->mutex_dcc); + return 0; +} + + +int irc_dcc_sendfile (irc_session_t * session, void * ctx, const char * nick, const char * filename, irc_dcc_callback_t callback, irc_dcc_t * dccid) +{ + struct sockaddr_in saddr; + socklen_t len = sizeof(saddr); + char cmdbuf[128], notbuf[128]; + irc_dcc_session_t * dcc; + const char * p; + int err; + long filesize; + + if ( !session || !dccid || !filename || !callback ) + { + session->lasterror = LIBIRC_ERR_INVAL; + return 1; + } + + if ( session->state != LIBIRC_STATE_CONNECTED ) + { + session->lasterror = LIBIRC_ERR_STATE; + return 1; + } + + if ( (err = libirc_new_dcc_session (session, 0, 0, LIBIRC_DCC_SENDFILE, ctx, &dcc)) != 0 ) + { + session->lasterror = err; + return 1; + } + + if ( (dcc->dccsend_file_fp = fopen (filename, "rb")) == 0 ) + { + libirc_remove_dcc_session (session, dcc, 1); + session->lasterror = LIBIRC_ERR_OPENFILE; + return 1; + } + + /* Get file length */ + if ( fseek (dcc->dccsend_file_fp, 0, SEEK_END) + || (filesize = ftell (dcc->dccsend_file_fp)) == -1 + || fseek (dcc->dccsend_file_fp, 0, SEEK_SET) ) + { + libirc_remove_dcc_session (session, dcc, 1); + session->lasterror = LIBIRC_ERR_NODCCSEND; + return 1; + } + + if ( getsockname (dcc->sock, (struct sockaddr*) &saddr, &len) < 0 ) + { + libirc_remove_dcc_session (session, dcc, 1); + session->lasterror = LIBIRC_ERR_SOCKET; + return 1; + } + + // Remove path from the filename + if ( (p = strrchr (filename, '\\')) == 0 + && (p = strrchr (filename, '/')) == 0 ) + p = filename; + else + p++; // skip directory slash + + sprintf (notbuf, "DCC Send %s (%s)", p, inet_ntoa (saddr.sin_addr)); + sprintf (cmdbuf, "DCC SEND %s %lu %u %ld", p, (unsigned long) ntohl (saddr.sin_addr.s_addr), ntohs (saddr.sin_port), filesize); + + if ( irc_cmd_notice (session, nick, notbuf) + || irc_cmd_ctcp_request (session, nick, cmdbuf) ) + { + libirc_remove_dcc_session (session, dcc, 1); + return 1; + } + + *dccid = dcc->id; + dcc->cb = callback; + + return 0; +} diff --git a/external/IRC/dcc.h b/external/IRC/dcc.h new file mode 100644 index 00000000..deb3355f --- /dev/null +++ b/external/IRC/dcc.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2004-2012 George Yunaev gyunaev@ulduzsoft.com + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + */ + +#ifndef INCLUDE_IRC_DCC_H +#define INCLUDE_IRC_DCC_H + + +/* + * This structure keeps the state of a single DCC connection. + */ +struct irc_dcc_session_s +{ + irc_dcc_session_t * next; + + irc_dcc_t id; + void * ctx; + socket_t sock; /*!< DCC socket */ + int dccmode; /*!< Boolean value to differ chat vs send + requests. Changes the cb behavior - when + it is chat, data is sent by lines with + stripped CRLFs. In file mode, the data + is sent as-is */ + int state; + time_t timeout; + + FILE * dccsend_file_fp; + unsigned int received_file_size; + unsigned int file_confirm_offset; + + struct sockaddr_in remote_addr; + + char incoming_buf[LIBIRC_DCC_BUFFER_SIZE]; + unsigned int incoming_offset; + + char outgoing_buf[LIBIRC_DCC_BUFFER_SIZE]; + unsigned int outgoing_offset; + port_mutex_t mutex_outbuf; + + irc_dcc_callback_t cb; +}; + + +#endif /* INCLUDE_IRC_DCC_H */ diff --git a/external/IRC/errors.c b/external/IRC/errors.c new file mode 100644 index 00000000..6533b898 --- /dev/null +++ b/external/IRC/errors.c @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2004-2012 George Yunaev gyunaev@ulduzsoft.com + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + */ + +static const char * libirc_strerror[LIBIRC_ERR_MAX] = +{ + "No error", + "Invalid argument", + "Host not resolved", + "Socket error", + "Could not connect", + "Remote connection closed", + "Out of memory", + "Could not accept new connection", + "Object not found", + "Could not DCC send this object", + "Read error", + "Write error", + "Illegal operation for this state", + "Timeout error", + "Could not open file", + "IRC session terminated", + "IPv6 not supported", + "SSL not supported", + "SSL initialization failed", + "SSL connection failed", + "SSL certificate verify failed", +}; + + +int irc_errno (irc_session_t * session) +{ + return session->lasterror; +} + + +const char * irc_strerror (int ircerrno) +{ + if ( ircerrno >= 0 && ircerrno < LIBIRC_ERR_MAX ) + return libirc_strerror[ircerrno]; + else + return "Invalid irc_errno value"; +} + diff --git a/external/IRC/libircclient.c b/external/IRC/libircclient.c new file mode 100644 index 00000000..caf16b1f --- /dev/null +++ b/external/IRC/libircclient.c @@ -0,0 +1,1258 @@ +/* + * Copyright (C) 2004-2012 George Yunaev gyunaev@ulduzsoft.com + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + */ + +#define IS_DEBUG_ENABLED(s) ((s)->options & LIBIRC_OPTION_DEBUG) + +#include "portable.c" +#include "sockets.c" + +#include "libircclient.h" +#include "session.h" + +#include "utils.c" +#include "errors.c" +#include "colors.c" +#include "dcc.c" +#include "ssl.c" + + +#ifdef _MSC_VER + /* + * The debugger of MSVC 2005 does not like strdup. + * It complains about heap corruption when free is called. + * Use _strdup instead. + */ + #undef strdup + #define strdup _strdup +#endif + + +irc_session_t * irc_create_session (irc_callbacks_t * callbacks) +{ + irc_session_t * session = malloc (sizeof(irc_session_t)); + + if ( !session ) + return 0; + + memset (session, 0, sizeof(irc_session_t)); + session->sock = -1; + + if ( libirc_mutex_init (&session->mutex_session) + || libirc_mutex_init (&session->mutex_dcc) ) + { + free (session); + return 0; + } + + session->dcc_last_id = 1; + session->dcc_timeout = 60; + + memcpy (&session->callbacks, callbacks, sizeof(irc_callbacks_t)); + + if ( !session->callbacks.event_ctcp_req ) + session->callbacks.event_ctcp_req = libirc_event_ctcp_internal; + + return session; +} + +static void free_ircsession_strings (irc_session_t * session) +{ + if ( session->realname ) + free (session->realname); + + if ( session->username ) + free (session->username); + + if ( session->nick ) + free (session->nick); + + if ( session->server ) + free (session->server); + + if ( session->server_password ) + free (session->server_password); + + session->realname = 0; + session->username = 0; + session->nick = 0; + session->server = 0; + session->server_password = 0; +} + +void irc_destroy_session (irc_session_t * session) +{ + free_ircsession_strings( session ); + + // The CTCP VERSION must be freed only now + if ( session->ctcp_version ) + free (session->ctcp_version); + + if ( session->sock >= 0 ) + socket_close (&session->sock); + +#if defined (ENABLE_THREADS) + libirc_mutex_destroy (&session->mutex_session); +#endif + +#if defined (ENABLE_SSL) + if ( session->ssl ) + SSL_free( session->ssl ); +#endif + + /* + * delete DCC data + * libirc_remove_dcc_session removes the DCC session from the list. + */ + while ( session->dcc_sessions ) + libirc_remove_dcc_session (session, session->dcc_sessions, 0); + + libirc_mutex_destroy (&session->mutex_dcc); + + free (session); +} + + +int irc_connect (irc_session_t * session, + const char * server, + unsigned short port, + const char * server_password, + const char * nick, + const char * username, + const char * realname) +{ + struct sockaddr_in saddr; + char * p; + + // Check and copy all the specified fields + if ( !server || !nick ) + { + session->lasterror = LIBIRC_ERR_INVAL; + return 1; + } + + if ( session->state != LIBIRC_STATE_INIT ) + { + session->lasterror = LIBIRC_ERR_STATE; + return 1; + } + + // Free the strings if defined; may be the case when the session is reused after the connection fails + free_ircsession_strings( session ); + + // Handle the server # prefix (SSL) + if ( server[0] == SSL_PREFIX ) + { +#if defined (ENABLE_SSL) + server++; + session->flags |= SESSIONFL_SSL_CONNECTION; +#else + session->lasterror = LIBIRC_ERR_SSL_NOT_SUPPORTED; + return 1; +#endif + } + + if ( username ) + session->username = strdup (username); + + if ( server_password ) + session->server_password = strdup (server_password); + + if ( realname ) + session->realname = strdup (realname); + + session->nick = strdup (nick); + session->server = strdup (server); + + // If port number is zero and server contains the port, parse it + if ( port == 0 && (p = strchr( session->server, ':' )) != 0 ) + { + // Terminate the string and parse the port number + *p++ = '\0'; + port = atoi( p ); + } + + // IPv4 address resolving + memset( &saddr, 0, sizeof(saddr) ); + saddr.sin_family = AF_INET; + saddr.sin_port = htons (port); + saddr.sin_addr.s_addr = inet_addr( session->server ); + + if ( saddr.sin_addr.s_addr == INADDR_NONE ) + { + struct hostent *hp; +#if defined HAVE_GETHOSTBYNAME_R + int tmp_errno; + struct hostent tmp_hostent; + char buf[2048]; + + if ( gethostbyname_r (session->server, &tmp_hostent, buf, sizeof(buf), &hp, &tmp_errno) ) + hp = 0; +#else + hp = gethostbyname (session->server); +#endif // HAVE_GETHOSTBYNAME_R + if ( !hp ) + { + session->lasterror = LIBIRC_ERR_RESOLV; + return 1; + } + + memcpy (&saddr.sin_addr, hp->h_addr, (size_t) hp->h_length); + } + + // create the IRC server socket + if ( socket_create( PF_INET, SOCK_STREAM, &session->sock) + || socket_make_nonblocking (&session->sock) ) + { + session->lasterror = LIBIRC_ERR_SOCKET; + return 1; + } + +#if defined (ENABLE_SSL) + // Init the SSL stuff + if ( session->flags & SESSIONFL_SSL_CONNECTION ) + { + int rc = ssl_init( session ); + + if ( rc != 0 ) + { + session->lasterror = rc; + return 1; + } + } +#endif + + // and connect to the IRC server + if ( socket_connect (&session->sock, (struct sockaddr *) &saddr, sizeof(saddr)) ) + { + session->lasterror = LIBIRC_ERR_CONNECT; + return 1; + } + + session->state = LIBIRC_STATE_CONNECTING; + session->flags = SESSIONFL_USES_IPV6; // reset in case of reconnect + return 0; +} + + +int irc_connect6 (irc_session_t * session, + const char * server, + unsigned short port, + const char * server_password, + const char * nick, + const char * username, + const char * realname) +{ +#if defined (ENABLE_IPV6) + struct sockaddr_in6 saddr; + struct addrinfo ainfo, *res = NULL; + char portStr[32], *p; +#if defined (_WIN32) + int addrlen = sizeof(saddr); + HMODULE hWsock; + getaddrinfo_ptr_t getaddrinfo_ptr; + freeaddrinfo_ptr_t freeaddrinfo_ptr; + int resolvesuccess = 0; +#endif + + // Check and copy all the specified fields + if ( !server || !nick ) + { + session->lasterror = LIBIRC_ERR_INVAL; + return 1; + } + + if ( session->state != LIBIRC_STATE_INIT ) + { + session->lasterror = LIBIRC_ERR_STATE; + return 1; + } + + // Free the strings if defined; may be the case when the session is reused after the connection fails + free_ircsession_strings( session ); + + // Handle the server # prefix (SSL) + if ( server[0] == SSL_PREFIX ) + { +#if defined (ENABLE_SSL) + server++; + session->flags |= SESSIONFL_SSL_CONNECTION; +#else + session->lasterror = LIBIRC_ERR_SSL_NOT_SUPPORTED; + return 1; +#endif + } + + if ( username ) + session->username = strdup (username); + + if ( server_password ) + session->server_password = strdup (server_password); + + if ( realname ) + session->realname = strdup (realname); + + session->nick = strdup (nick); + session->server = strdup (server); + + // If port number is zero and server contains the port, parse it + if ( port == 0 && (p = strchr( session->server, ':' )) != 0 ) + { + // Terminate the string and parse the port number + *p++ = '\0'; + port = atoi( p ); + } + + memset( &saddr, 0, sizeof(saddr) ); + saddr.sin6_family = AF_INET6; + saddr.sin6_port = htons (port); + + sprintf( portStr, "%u", (unsigned)port ); + +#if defined (_WIN32) + if ( WSAStringToAddressA( (LPSTR)session->server, AF_INET6, NULL, (struct sockaddr *)&saddr, &addrlen ) == SOCKET_ERROR ) + { + hWsock = LoadLibraryA("ws2_32"); + + if (hWsock) + { + /* Determine functions at runtime, because windows systems < XP do not + * support getaddrinfo. */ + getaddrinfo_ptr = (getaddrinfo_ptr_t)GetProcAddress(hWsock, "getaddrinfo"); + freeaddrinfo_ptr = (freeaddrinfo_ptr_t)GetProcAddress(hWsock, "freeaddrinfo"); + + if (getaddrinfo_ptr && freeaddrinfo_ptr) + { + memset(&ainfo, 0, sizeof(ainfo)); + ainfo.ai_family = AF_INET6; + ainfo.ai_socktype = SOCK_STREAM; + ainfo.ai_protocol = 0; + + if ( getaddrinfo_ptr(session->server, portStr, &ainfo, &res) == 0 && res ) + { + resolvesuccess = 1; + memcpy( &saddr, res->ai_addr, res->ai_addrlen ); + freeaddrinfo_ptr( res ); + } + } + FreeLibrary(hWsock); + } + if (!resolvesuccess) + { + session->lasterror = LIBIRC_ERR_RESOLV; + return 1; + } + } +#else + if ( inet_pton( AF_INET6, session->server, (void*) &saddr.sin6_addr ) <= 0 ) + { + memset( &ainfo, 0, sizeof(ainfo) ); + ainfo.ai_family = AF_INET6; + ainfo.ai_socktype = SOCK_STREAM; + ainfo.ai_protocol = 0; + + if ( getaddrinfo( session->server, portStr, &ainfo, &res ) || !res ) + { + session->lasterror = LIBIRC_ERR_RESOLV; + return 1; + } + + memcpy( &saddr, res->ai_addr, res->ai_addrlen ); + freeaddrinfo( res ); + } +#endif + + // create the IRC server socket + if ( socket_create( PF_INET6, SOCK_STREAM, &session->sock) + || socket_make_nonblocking (&session->sock) ) + { + session->lasterror = LIBIRC_ERR_SOCKET; + return 1; + } + +#if defined (ENABLE_SSL) + // Init the SSL stuff + if ( session->flags & SESSIONFL_SSL_CONNECTION ) + { + int rc = ssl_init( session ); + + if ( rc != 0 ) + return rc; + } +#endif + + // and connect to the IRC server + if ( socket_connect (&session->sock, (struct sockaddr *) &saddr, sizeof(saddr)) ) + { + session->lasterror = LIBIRC_ERR_CONNECT; + return 1; + } + + session->state = LIBIRC_STATE_CONNECTING; + session->flags = 0; // reset in case of reconnect + return 0; +#else + session->lasterror = LIBIRC_ERR_NOIPV6; + return 1; +#endif +} + + +int irc_is_connected (irc_session_t * session) +{ + return (session->state == LIBIRC_STATE_CONNECTED + || session->state == LIBIRC_STATE_CONNECTING) ? 1 : 0; +} + + +int irc_run (irc_session_t * session) +{ + if ( session->state != LIBIRC_STATE_CONNECTING ) + { + session->lasterror = LIBIRC_ERR_STATE; + return 1; + } + + while ( irc_is_connected(session) ) + { + struct timeval tv; + fd_set in_set, out_set; + int maxfd = 0; + + tv.tv_usec = 250000; + tv.tv_sec = 0; + + // Init sets + FD_ZERO (&in_set); + FD_ZERO (&out_set); + + irc_add_select_descriptors (session, &in_set, &out_set, &maxfd); + + if ( select (maxfd + 1, &in_set, &out_set, 0, &tv) < 0 ) + { + if ( socket_error() == EINTR ) + continue; + + session->lasterror = LIBIRC_ERR_TERMINATED; + return 1; + } + + if ( irc_process_select_descriptors (session, &in_set, &out_set) ) + return 1; + } + + return 0; +} + + +int irc_add_select_descriptors (irc_session_t * session, fd_set *in_set, fd_set *out_set, int * maxfd) +{ + if ( session->sock < 0 + || session->state == LIBIRC_STATE_INIT + || session->state == LIBIRC_STATE_DISCONNECTED ) + { + session->lasterror = LIBIRC_ERR_STATE; + return 1; + } + + libirc_mutex_lock (&session->mutex_session); + + switch (session->state) + { + case LIBIRC_STATE_CONNECTING: + // While connection, only out_set descriptor should be set + libirc_add_to_set (session->sock, out_set, maxfd); + break; + + case LIBIRC_STATE_CONNECTED: + // Add input descriptor if there is space in input buffer + if ( session->incoming_offset < (sizeof (session->incoming_buf) - 1) + || (session->flags & SESSIONFL_SSL_WRITE_WANTS_READ) != 0 ) + libirc_add_to_set (session->sock, in_set, maxfd); + + // Add output descriptor if there is something in output buffer + if ( libirc_findcrlf (session->outgoing_buf, session->outgoing_offset) > 0 + || (session->flags & SESSIONFL_SSL_READ_WANTS_WRITE) != 0 ) + libirc_add_to_set (session->sock, out_set, maxfd); + + break; + } + + libirc_mutex_unlock (&session->mutex_session); + + libirc_dcc_add_descriptors (session, in_set, out_set, maxfd); + return 0; +} + + +static void libirc_process_incoming_data (irc_session_t * session, size_t process_length) +{ + #define MAX_PARAMS_ALLOWED 10 + char buf[2*512], *p, *s; + const char * command = 0, *prefix = 0, *params[MAX_PARAMS_ALLOWED+1]; + int code = 0, paramindex = 0; + char *buf_end = buf + process_length; + + if ( process_length > sizeof(buf) ) + abort(); // should be impossible + + memcpy (buf, session->incoming_buf, process_length); + buf[process_length] = '\0'; + + memset ((char *)params, 0, sizeof(params)); + p = buf; + + /* + * From RFC 1459: + * ::= [':' ] + * ::= | [ '!' ] [ '@' ] + * ::= { } | + * ::= ' ' { ' ' } + * ::= [ ':' | ] + * ::= + * ::= + */ + + // Parse + if ( buf[0] == ':' ) + { + while ( *p && *p != ' ') + p++; + + *p++ = '\0'; + + // we use buf+1 to skip the leading colon + prefix = buf + 1; + + // If LIBIRC_OPTION_STRIPNICKS is set, we should 'clean up' nick + // right here + if ( session->options & LIBIRC_OPTION_STRIPNICKS ) + { + for ( s = buf + 1; *s; s++ ) + { + if ( *s == '@' || *s == '!' ) + { + *s = '\0'; + break; + } + } + } + } + + // Parse + if ( isdigit (p[0]) && isdigit (p[1]) && isdigit (p[2]) ) + { + p[3] = '\0'; + code = atoi (p); + p += 4; + } + else + { + s = p; + + while ( *p && *p != ' ') + p++; + + *p++ = '\0'; + + command = s; + } + + // Parse middle/params + while ( *p && paramindex < MAX_PARAMS_ALLOWED ) + { + // beginning from ':', this is the last param + if ( *p == ':' ) + { + params[paramindex++] = p + 1; // skip : + break; + } + + // Just a param + for ( s = p; *p && *p != ' '; p++ ) + ; + + params[paramindex++] = s; + + if ( !*p ) + break; + + *p++ = '\0'; + } + + // Handle PING/PONG + if ( command && !strncmp (command, "PING", buf_end - command) && params[0] ) + { + irc_send_raw (session, "PONG %s", params[0]); + return; + } + + // and dump + if ( code ) + { + // We use SESSIONFL_MOTD_RECEIVED flag to check whether it is the first + // RPL_ENDOFMOTD or ERR_NOMOTD after the connection. + if ( (code == 1 || code == 376 || code == 422) && !(session->flags & SESSIONFL_MOTD_RECEIVED ) ) + { + session->flags |= SESSIONFL_MOTD_RECEIVED; + + if ( session->callbacks.event_connect ) + (*session->callbacks.event_connect) (session, "CONNECT", prefix, params, paramindex); + } + + if ( session->callbacks.event_numeric ) + (*session->callbacks.event_numeric) (session, code, prefix, params, paramindex); + } + else + { + if ( !strncmp (command, "NICK", buf_end - command) ) + { + /* + * If we're changed our nick, we should save it. + */ + char nickbuf[256]; + + irc_target_get_nick (prefix, nickbuf, sizeof(nickbuf)); + + if ( !strncmp (nickbuf, session->nick, strlen(session->nick)) && paramindex > 0 ) + { + free (session->nick); + session->nick = strdup (params[0]); + } + + if ( session->callbacks.event_nick ) + (*session->callbacks.event_nick) (session, command, prefix, params, paramindex); + } + else if ( !strncmp (command, "QUIT", buf_end - command) ) + { + if ( session->callbacks.event_quit ) + (*session->callbacks.event_quit) (session, command, prefix, params, paramindex); + } + else if ( !strncmp (command, "JOIN", buf_end - command) ) + { + if ( session->callbacks.event_join ) + (*session->callbacks.event_join) (session, command, prefix, params, paramindex); + } + else if ( !strncmp (command, "PART", buf_end - command) ) + { + if ( session->callbacks.event_part ) + (*session->callbacks.event_part) (session, command, prefix, params, paramindex); + } + else if ( !strncmp (command, "MODE", buf_end - command) ) + { + if ( paramindex > 0 && !strncmp (params[0], session->nick, strlen(session->nick)) ) + { + params[0] = params[1]; + paramindex = 1; + + if ( session->callbacks.event_umode ) + (*session->callbacks.event_umode) (session, command, prefix, params, paramindex); + } + else + { + if ( session->callbacks.event_mode ) + (*session->callbacks.event_mode) (session, command, prefix, params, paramindex); + } + } + else if ( !strncmp (command, "TOPIC", buf_end - command) ) + { + if ( session->callbacks.event_topic ) + (*session->callbacks.event_topic) (session, command, prefix, params, paramindex); + } + else if ( !strncmp (command, "KICK", buf_end - command) ) + { + if ( session->callbacks.event_kick ) + (*session->callbacks.event_kick) (session, command, prefix, params, paramindex); + } + else if ( !strncmp (command, "PRIVMSG", buf_end - command) ) + { + if ( paramindex > 1 ) + { + size_t msglen = strlen (params[1]); + + /* + * Check for CTCP request (a CTCP message starts from 0x01 + * and ends by 0x01 + */ + if ( params[1][0] == 0x01 && params[1][msglen-1] == 0x01 ) + { + char ctcp_buf[128]; + + msglen -= 2; + if ( msglen > sizeof(ctcp_buf) - 1 ) + msglen = sizeof(ctcp_buf) - 1; + + memcpy (ctcp_buf, params[1] + 1, msglen); + ctcp_buf[msglen] = '\0'; + + if ( !strncasecmp(ctcp_buf, "DCC ", 4) ) + libirc_dcc_request (session, prefix, ctcp_buf); + else if ( !strncasecmp( ctcp_buf, "ACTION ", 7) + && session->callbacks.event_ctcp_action ) + { + params[1] = ctcp_buf + 7; // the length of "ACTION " + paramindex = 2; + + (*session->callbacks.event_ctcp_action) (session, "ACTION", prefix, params, paramindex); + } + else + { + params[0] = ctcp_buf; + paramindex = 1; + + if ( session->callbacks.event_ctcp_req ) + (*session->callbacks.event_ctcp_req) (session, "CTCP", prefix, params, paramindex); + } + } + else if ( !strncasecmp (params[0], session->nick, strlen(session->nick) ) ) + { + if ( session->callbacks.event_privmsg ) + (*session->callbacks.event_privmsg) (session, "PRIVMSG", prefix, params, paramindex); + } + else + { + if ( session->callbacks.event_channel ) + (*session->callbacks.event_channel) (session, "CHANNEL", prefix, params, paramindex); + } + } + } + else if ( !strncmp (command, "NOTICE", buf_end - command) ) + { + size_t msglen = strlen (params[1]); + + /* + * Check for CTCP request (a CTCP message starts from 0x01 + * and ends by 0x01 + */ + if ( paramindex > 1 && params[1][0] == 0x01 && params[1][msglen-1] == 0x01 ) + { + char ctcp_buf[512]; + + msglen -= 2; + if ( msglen > sizeof(ctcp_buf) - 1 ) + msglen = sizeof(ctcp_buf) - 1; + + memcpy (ctcp_buf, params[1] + 1, msglen); + ctcp_buf[msglen] = '\0'; + + params[0] = ctcp_buf; + paramindex = 1; + + if ( session->callbacks.event_ctcp_rep ) + (*session->callbacks.event_ctcp_rep) (session, "CTCP", prefix, params, paramindex); + } + else if ( !strncasecmp (params[0], session->nick, strlen(session->nick) ) ) + { + if ( session->callbacks.event_notice ) + (*session->callbacks.event_notice) (session, command, prefix, params, paramindex); + } else { + if ( session->callbacks.event_channel_notice ) + (*session->callbacks.event_channel_notice) (session, command, prefix, params, paramindex); + } + } + else if ( !strncmp (command, "INVITE", buf_end - command) ) + { + if ( session->callbacks.event_invite ) + (*session->callbacks.event_invite) (session, command, prefix, params, paramindex); + } + else if ( !strncmp (command, "KILL", buf_end - command) ) + { + ; /* ignore this event - not all servers generate this */ + } + else + { + /* + * The "unknown" event is triggered upon receipt of any number of + * unclassifiable miscellaneous messages, which aren't handled by + * the library. + */ + + if ( session->callbacks.event_unknown ) + (*session->callbacks.event_unknown) (session, command, prefix, params, paramindex); + } + } +} + + +int irc_process_select_descriptors (irc_session_t * session, fd_set *in_set, fd_set *out_set) +{ + char buf[256], hname[256]; + + if ( session->sock < 0 + || session->state == LIBIRC_STATE_INIT + || session->state == LIBIRC_STATE_DISCONNECTED ) + { + session->lasterror = LIBIRC_ERR_STATE; + return 1; + } + + session->lasterror = 0; + libirc_dcc_process_descriptors (session, in_set, out_set); + + // Handle "connection succeed" / "connection failed" + if ( session->state == LIBIRC_STATE_CONNECTING + && FD_ISSET (session->sock, out_set) ) + { + // Now we have to determine whether the socket is connected + // or the connect is failed + struct sockaddr_storage saddr, laddr; + socklen_t slen = sizeof(saddr); + socklen_t llen = sizeof(laddr); + + if ( getsockname (session->sock, (struct sockaddr*)&laddr, &llen) < 0 + || getpeername (session->sock, (struct sockaddr*)&saddr, &slen) < 0 ) + { + // connection failed + session->lasterror = LIBIRC_ERR_CONNECT; + session->state = LIBIRC_STATE_DISCONNECTED; + return 1; + } + + if (saddr.ss_family == AF_INET) + memcpy (&session->local_addr, &((struct sockaddr_in *)&laddr)->sin_addr, sizeof(struct in_addr)); + else + memcpy (&session->local_addr, &((struct sockaddr_in6 *)&laddr)->sin6_addr, sizeof(struct in6_addr)); + +#if defined (ENABLE_DEBUG) + if ( IS_DEBUG_ENABLED(session) ) + fprintf (stderr, "[DEBUG] Detected local address: %s\n", inet_ntoa(session->local_addr)); +#endif + + session->state = LIBIRC_STATE_CONNECTED; + + // Get the hostname + if ( gethostname (hname, sizeof(hname)) < 0 ) + strcpy (hname, "unknown"); + + // Prepare the data, which should be sent to the server + if ( session->server_password ) + { + snprintf (buf, sizeof(buf), "PASS %s", session->server_password); + irc_send_raw (session, buf); + } + + snprintf (buf, sizeof(buf), "NICK %s", session->nick); + irc_send_raw (session, buf); + + /* + * RFC 1459 states that "hostname and servername are normally + * ignored by the IRC server when the USER command comes from + * a directly connected client (for security reasons)", therefore + * we don't need them. + */ + snprintf (buf, sizeof(buf), "USER %s unknown unknown :%s", + session->username ? session->username : "nobody", + session->realname ? session->realname : "noname"); + irc_send_raw (session, buf); + + return 0; + } + + if ( session->state != LIBIRC_STATE_CONNECTED ) + { + session->lasterror = LIBIRC_ERR_STATE; + return 1; + } + + // Hey, we've got something to read! + if ( FD_ISSET (session->sock, in_set) ) + { + int offset, length = session_socket_read( session ); + + if ( length < 0 ) + { + if ( session->lasterror == 0 ) + session->lasterror = (length == 0 ? LIBIRC_ERR_CLOSED : LIBIRC_ERR_TERMINATED); + + session->state = LIBIRC_STATE_DISCONNECTED; + return 1; + } + + session->incoming_offset += length; + + // process the incoming data + while ( (offset = libirc_findcrlf (session->incoming_buf, session->incoming_offset)) > 0 ) + { +#if defined (ENABLE_DEBUG) + if ( IS_DEBUG_ENABLED(session) ) + libirc_dump_data ("RECV", session->incoming_buf, offset); +#endif + // parse the string + libirc_process_incoming_data (session, offset); + + offset = libirc_findcrlf_offset(session->incoming_buf, offset, session->incoming_offset); + + if ( session->incoming_offset - offset > 0 ) + memmove (session->incoming_buf, session->incoming_buf + offset, session->incoming_offset - offset); + + session->incoming_offset -= offset; + } + } + + // We can write a stored buffer + if ( FD_ISSET (session->sock, out_set) ) + { + int length; + + // Because outgoing_buf could be changed asynchronously, we should lock any change + libirc_mutex_lock (&session->mutex_session); + length = session_socket_write( session ); + + if ( length < 0 ) + { + if ( session->lasterror == 0 ) + session->lasterror = (length == 0 ? LIBIRC_ERR_CLOSED : LIBIRC_ERR_TERMINATED); + + session->state = LIBIRC_STATE_DISCONNECTED; + + libirc_mutex_unlock (&session->mutex_session); + return 1; + } + +#if defined (ENABLE_DEBUG) + if ( IS_DEBUG_ENABLED(session) ) + libirc_dump_data ("SEND", session->outgoing_buf, length); +#endif + + if ( length > 0 && session->outgoing_offset - length > 0 ) + memmove (session->outgoing_buf, session->outgoing_buf + length, session->outgoing_offset - length); + + session->outgoing_offset -= length; + libirc_mutex_unlock (&session->mutex_session); + } + + return 0; +} + + +int irc_send_raw (irc_session_t * session, const char * format, ...) +{ + char buf[1024]; + va_list va_alist; + + if ( session->state != LIBIRC_STATE_CONNECTED ) + { + session->lasterror = LIBIRC_ERR_STATE; + return 1; + } + + va_start (va_alist, format); + vsnprintf (buf, sizeof(buf), format, va_alist); + va_end (va_alist); + + libirc_mutex_lock (&session->mutex_session); + + if ( (strlen(buf) + 2) >= (sizeof(session->outgoing_buf) - session->outgoing_offset) ) + { + libirc_mutex_unlock (&session->mutex_session); + session->lasterror = LIBIRC_ERR_NOMEM; + return 1; + } + + strcpy (session->outgoing_buf + session->outgoing_offset, buf); + session->outgoing_offset += strlen (buf); + session->outgoing_buf[session->outgoing_offset++] = 0x0D; + session->outgoing_buf[session->outgoing_offset++] = 0x0A; + + libirc_mutex_unlock (&session->mutex_session); + return 0; +} + + +int irc_cmd_quit (irc_session_t * session, const char * reason) +{ + return irc_send_raw (session, "QUIT :%s", reason ? reason : "quit"); +} + + +int irc_cmd_join (irc_session_t * session, const char * channel, const char * key) +{ + if ( !channel ) + { + session->lasterror = LIBIRC_ERR_STATE; + return 1; + } + + if ( key ) + return irc_send_raw (session, "JOIN %s :%s", channel, key); + else + return irc_send_raw (session, "JOIN %s", channel); +} + + +int irc_cmd_part (irc_session_t * session, const char * channel) +{ + if ( !channel ) + { + session->lasterror = LIBIRC_ERR_STATE; + return 1; + } + + return irc_send_raw (session, "PART %s", channel); +} + + +int irc_cmd_topic (irc_session_t * session, const char * channel, const char * topic) +{ + if ( !channel ) + { + session->lasterror = LIBIRC_ERR_STATE; + return 1; + } + + if ( topic ) + return irc_send_raw (session, "TOPIC %s :%s", channel, topic); + else + return irc_send_raw (session, "TOPIC %s", channel); +} + +int irc_cmd_names (irc_session_t * session, const char * channel) +{ + if ( !channel ) + { + session->lasterror = LIBIRC_ERR_STATE; + return 1; + } + + return irc_send_raw (session, "NAMES %s", channel); +} + + +int irc_cmd_list (irc_session_t * session, const char * channel) +{ + if ( channel ) + return irc_send_raw (session, "LIST %s", channel); + else + return irc_send_raw (session, "LIST"); +} + + +int irc_cmd_invite (irc_session_t * session, const char * nick, const char * channel) +{ + if ( !channel || !nick ) + { + session->lasterror = LIBIRC_ERR_STATE; + return 1; + } + + return irc_send_raw (session, "INVITE %s %s", nick, channel); +} + + +int irc_cmd_kick (irc_session_t * session, const char * nick, const char * channel, const char * comment) +{ + if ( !channel || !nick ) + { + session->lasterror = LIBIRC_ERR_STATE; + return 1; + } + + if ( comment ) + return irc_send_raw (session, "KICK %s %s :%s", channel, nick, comment); + else + return irc_send_raw (session, "KICK %s %s", channel, nick); +} + + +int irc_cmd_msg (irc_session_t * session, const char * nch, const char * text) +{ + if ( !nch || !text ) + { + session->lasterror = LIBIRC_ERR_STATE; + return 1; + } + + return irc_send_raw (session, "PRIVMSG %s :%s", nch, text); +} + + +int irc_cmd_notice (irc_session_t * session, const char * nch, const char * text) +{ + if ( !nch || !text ) + { + session->lasterror = LIBIRC_ERR_STATE; + return 1; + } + + return irc_send_raw (session, "NOTICE %s :%s", nch, text); +} + +void irc_target_get_nick (const char * target, char *nick, size_t size) +{ + char *p = strstr (target, "!"); + unsigned int len; + + if ( p ) + len = p - target; + else + len = strlen (target); + + if ( len > size-1 ) + len = size - 1; + + memcpy (nick, target, len); + nick[len] = '\0'; +} + + +void irc_target_get_host (const char * target, char *host, size_t size) +{ + unsigned int len; + const char *p = strstr (target, "!"); + + if ( !p ) + p = target; + + len = strlen (p); + + if ( len > size-1 ) + len = size - 1; + + memcpy (host, p, len); + host[len] = '\0'; +} + + +int irc_cmd_ctcp_request (irc_session_t * session, const char * nick, const char * reply) +{ + if ( !nick || !reply ) + { + session->lasterror = LIBIRC_ERR_STATE; + return 1; + } + + return irc_send_raw (session, "PRIVMSG %s :\x01%s\x01", nick, reply); +} + + +int irc_cmd_ctcp_reply (irc_session_t * session, const char * nick, const char * reply) +{ + if ( !nick || !reply ) + { + session->lasterror = LIBIRC_ERR_STATE; + return 1; + } + + return irc_send_raw (session, "NOTICE %s :\x01%s\x01", nick, reply); +} + + +void irc_get_version (unsigned int * high, unsigned int * low) +{ + *high = LIBIRC_VERSION_HIGH; + *low = LIBIRC_VERSION_LOW; +} + + +void irc_set_ctx (irc_session_t * session, void * ctx) +{ + session->ctx = ctx; +} + + +void * irc_get_ctx (irc_session_t * session) +{ + return session->ctx; +} + + +void irc_set_ctcp_version (irc_session_t * session, const char * version) +{ + if ( session->ctcp_version ) + free(session->ctcp_version); + + session->ctcp_version = strdup(version); +} + + +void irc_disconnect (irc_session_t * session) +{ + if ( session->sock >= 0 ) + socket_close (&session->sock); + + session->sock = -1; + session->state = LIBIRC_STATE_INIT; +} + + +int irc_cmd_me (irc_session_t * session, const char * nch, const char * text) +{ + if ( !nch || !text ) + { + session->lasterror = LIBIRC_ERR_STATE; + return 1; + } + + return irc_send_raw (session, "PRIVMSG %s :\x01" "ACTION %s\x01", nch, text); +} + + +void irc_option_set (irc_session_t * session, unsigned int option) +{ + session->options |= option; +} + + +void irc_option_reset (irc_session_t * session, unsigned int option) +{ + session->options &= ~option; +} + + +int irc_cmd_channel_mode (irc_session_t * session, const char * channel, const char * mode) +{ + if ( !channel ) + { + session->lasterror = LIBIRC_ERR_INVAL; + return 1; + } + + if ( mode ) + return irc_send_raw (session, "MODE %s %s", channel, mode); + else + return irc_send_raw (session, "MODE %s", channel); +} + + +int irc_cmd_user_mode (irc_session_t * session, const char * mode) +{ + if ( mode ) + return irc_send_raw (session, "MODE %s %s", session->nick, mode); + else + return irc_send_raw (session, "MODE %s", session->nick); +} + + +int irc_cmd_nick (irc_session_t * session, const char * newnick) +{ + if ( !newnick ) + { + session->lasterror = LIBIRC_ERR_INVAL; + return 1; + } + + return irc_send_raw (session, "NICK %s", newnick); +} + +int irc_cmd_whois (irc_session_t * session, const char * nick) +{ + if ( !nick ) + { + session->lasterror = LIBIRC_ERR_INVAL; + return 1; + } + + return irc_send_raw (session, "WHOIS %s %s", nick, nick); +} diff --git a/external/IRC/params.h b/external/IRC/params.h new file mode 100644 index 00000000..2b57c530 --- /dev/null +++ b/external/IRC/params.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2004-2012 George Yunaev gyunaev@ulduzsoft.com + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + */ + +#ifndef INCLUDE_IRC_PARAMS_H +#define INCLUDE_IRC_PARAMS_H + + +#define LIBIRC_VERSION_HIGH 1 +#define LIBIRC_VERSION_LOW 8 + +#define LIBIRC_BUFFER_SIZE 1024 +#define LIBIRC_DCC_BUFFER_SIZE 1024 + +#define LIBIRC_STATE_INIT 0 +#define LIBIRC_STATE_LISTENING 1 +#define LIBIRC_STATE_CONNECTING 2 +#define LIBIRC_STATE_CONNECTED 3 +#define LIBIRC_STATE_DISCONNECTED 4 +#define LIBIRC_STATE_CONFIRM_SIZE 5 // Used only by DCC send to confirm the amount of sent data +#define LIBIRC_STATE_REMOVED 10 // this state is used only in DCC + + +#define SSL_PREFIX '#' + +#endif /* INCLUDE_IRC_PARAMS_H */ diff --git a/external/IRC/portable.c b/external/IRC/portable.c new file mode 100644 index 00000000..7b6f72bd --- /dev/null +++ b/external/IRC/portable.c @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2004-2012 George Yunaev gyunaev@ulduzsoft.com + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + */ + +#if !defined (_WIN32) + #include "config.h" + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #if defined (ENABLE_THREADS) + #include + typedef pthread_mutex_t port_mutex_t; + + #if !defined (PTHREAD_MUTEX_RECURSIVE) && defined (PTHREAD_MUTEX_RECURSIVE_NP) + #define PTHREAD_MUTEX_RECURSIVE PTHREAD_MUTEX_RECURSIVE_NP + #endif + #endif +#else + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #if defined (ENABLE_THREADS) + typedef CRITICAL_SECTION port_mutex_t; + #endif + + #define inline + #define snprintf _snprintf + #define vsnprintf _vsnprintf + #define strncasecmp _strnicmp +#endif + + +#if defined (ENABLE_SSL) + #include + #include + #include +#endif + + +#if defined (ENABLE_THREADS) +static inline int libirc_mutex_init (port_mutex_t * mutex) +{ +#if defined (_WIN32) + InitializeCriticalSection (mutex); + return 0; +#elif defined (PTHREAD_MUTEX_RECURSIVE) + pthread_mutexattr_t attr; + + return (pthread_mutexattr_init (&attr) + || pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE) + || pthread_mutex_init (mutex, &attr)); +#else /* !defined (PTHREAD_MUTEX_RECURSIVE) */ + + return pthread_mutex_init (mutex, 0); + +#endif /* defined (_WIN32) */ +} + + +static inline void libirc_mutex_destroy (port_mutex_t * mutex) +{ +#if defined (_WIN32) + DeleteCriticalSection (mutex); +#else + pthread_mutex_destroy (mutex); +#endif +} + + +static inline void libirc_mutex_lock (port_mutex_t * mutex) +{ +#if defined (_WIN32) + EnterCriticalSection (mutex); +#else + pthread_mutex_lock (mutex); +#endif +} + + +static inline void libirc_mutex_unlock (port_mutex_t * mutex) +{ +#if defined (_WIN32) + LeaveCriticalSection (mutex); +#else + pthread_mutex_unlock (mutex); +#endif +} + +#else + + typedef void * port_mutex_t; + + static inline int libirc_mutex_init (port_mutex_t * mutex) { return 0; } + static inline void libirc_mutex_destroy (port_mutex_t * mutex) {} + static inline void libirc_mutex_lock (port_mutex_t * mutex) {} + static inline void libirc_mutex_unlock (port_mutex_t * mutex) {} + +#endif + + +/* + * Stub for WIN32 dll to initialize winsock API + */ +#if defined (WIN32_DLL) +BOOL WINAPI DllMain (HINSTANCE hinstDll, DWORD fdwReason, LPVOID lpvReserved) +{ + WORD wVersionRequested = MAKEWORD (1, 1); + WSADATA wsaData; + + switch(fdwReason) + { + case DLL_PROCESS_ATTACH: + if ( WSAStartup (wVersionRequested, &wsaData) != 0 ) + return FALSE; + + DisableThreadLibraryCalls (hinstDll); + break; + + case DLL_PROCESS_DETACH: + WSACleanup(); + break; + } + + return TRUE; +} +#endif diff --git a/external/IRC/session.h b/external/IRC/session.h new file mode 100644 index 00000000..7e13c8f7 --- /dev/null +++ b/external/IRC/session.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2004-2012 George Yunaev gyunaev@ulduzsoft.com + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + */ + + +#ifndef INCLUDE_IRC_SESSION_H +#define INCLUDE_IRC_SESSION_H + + +#include "params.h" +#include "dcc.h" +#include "libirc_events.h" + + +// Session flags +#define SESSIONFL_MOTD_RECEIVED (0x00000001) +#define SESSIONFL_SSL_CONNECTION (0x00000002) +#define SESSIONFL_SSL_WRITE_WANTS_READ (0x00000004) +#define SESSIONFL_SSL_READ_WANTS_WRITE (0x00000008) +#define SESSIONFL_USES_IPV6 (0x00000010) + + + +struct irc_session_s +{ + void * ctx; + int dcc_timeout; + + int options; + int lasterror; + + char incoming_buf[LIBIRC_BUFFER_SIZE]; + unsigned int incoming_offset; + + char outgoing_buf[LIBIRC_BUFFER_SIZE]; + unsigned int outgoing_offset; + port_mutex_t mutex_session; + + socket_t sock; + int state; + int flags; + + char * server; + char * server_password; + char * realname; + char * username; + char * nick; + char * ctcp_version; + +#if defined( ENABLE_IPV6 ) + struct in6_addr local_addr6; +#endif + + struct in_addr local_addr; + irc_dcc_t dcc_last_id; + irc_dcc_session_t * dcc_sessions; + port_mutex_t mutex_dcc; + + irc_callbacks_t callbacks; + +#if defined (ENABLE_SSL) + SSL * ssl; +#endif + + +}; + + +#endif /* INCLUDE_IRC_SESSION_H */ diff --git a/external/IRC/sockets.c b/external/IRC/sockets.c new file mode 100644 index 00000000..83aca56f --- /dev/null +++ b/external/IRC/sockets.c @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2004-2012 George Yunaev gyunaev@ulduzsoft.com + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + */ + +/* + * The sockets interface was moved out to simplify going OpenSSL integration. + */ +#if !defined (_WIN32) + #include + #include + #include + #include + #include + + #define IS_SOCKET_ERROR(a) ((a)<0) + typedef int socket_t; + +#else + #include + #include + #include + + #define IS_SOCKET_ERROR(a) ((a)==SOCKET_ERROR) + +#if !defined(EWOULDBLOCK) + #define EWOULDBLOCK WSAEWOULDBLOCK +#endif +#if !defined(EINPROGRESS) + #define EINPROGRESS WSAEINPROGRESS +#endif +#if !defined(EINTR) + #define EINTR WSAEINTR +#endif +#if !defined(EAGAIN) + #define EAGAIN EWOULDBLOCK +#endif + + typedef SOCKET socket_t; + +#endif + +#ifndef INADDR_NONE + #define INADDR_NONE 0xFFFFFFFF +#endif + + +static int socket_error() +{ +#if !defined (_WIN32) + return errno; +#else + return WSAGetLastError(); +#endif +} + + +static int socket_create (int domain, int type, socket_t * sock) +{ + *sock = socket (domain, type, 0); + return IS_SOCKET_ERROR(*sock) ? 1 : 0; +} + + +static int socket_make_nonblocking (socket_t * sock) +{ +#if !defined (_WIN32) + return fcntl (*sock, F_SETFL, fcntl (*sock, F_GETFL,0 ) | O_NONBLOCK) != 0; +#else + unsigned long mode = 0; + return ioctlsocket (*sock, FIONBIO, &mode) == SOCKET_ERROR; +#endif +} + + +static int socket_close (socket_t * sock) +{ +#if !defined (_WIN32) + close (*sock); +#else + closesocket (*sock); +#endif + + *sock = -1; + return 0; +} + + +static int socket_connect (socket_t * sock, const struct sockaddr *saddr, socklen_t len) +{ + while ( 1 ) + { + if ( connect (*sock, saddr, len) < 0 ) + { + if ( socket_error() == EINTR ) + continue; + + if ( socket_error() != EINPROGRESS && socket_error() != EWOULDBLOCK ) + return 1; + } + + return 0; + } +} + + +static int socket_accept (socket_t * sock, socket_t * newsock, struct sockaddr *saddr, socklen_t * len) +{ + while ( IS_SOCKET_ERROR(*newsock = accept (*sock, saddr, len)) ) + { + if ( socket_error() == EINTR ) + continue; + + return 1; + } + + return 0; +} + + +static int socket_recv (socket_t * sock, void * buf, size_t len) +{ + int length; + + while ( (length = recv (*sock, buf, len, 0)) < 0 ) + { + int err = socket_error(); + + if ( err != EINTR && err != EAGAIN ) + break; + } + + return length; +} + + +static int socket_send (socket_t * sock, const void *buf, size_t len) +{ + int length; + + while ( (length = send (*sock, buf, len, 0)) < 0 ) + { + int err = socket_error(); + + if ( err != EINTR && err != EAGAIN ) + break; + } + + return length; +} diff --git a/external/IRC/ssl.c b/external/IRC/ssl.c new file mode 100644 index 00000000..5af92ccc --- /dev/null +++ b/external/IRC/ssl.c @@ -0,0 +1,390 @@ +/* + * Copyright (C) 2004-2012 George Yunaev gyunaev@ulduzsoft.com + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + */ + + +#if defined (ENABLE_SSL) + +// Nonzero if OpenSSL has been initialized +static SSL_CTX * ssl_context = 0; + +#if defined (_WIN32) +#include +// This array will store all of the mutexes available to OpenSSL +static CRITICAL_SECTION * mutex_buf = 0; + +// OpenSSL callback to utilize static locks +static void cb_openssl_locking_function( int mode, int n, const char * file, int line ) +{ + if ( mode & CRYPTO_LOCK) + EnterCriticalSection( &mutex_buf[n] ); + else + LeaveCriticalSection( &mutex_buf[n] ); +} + +// OpenSSL callback to get the thread ID +static unsigned long cb_openssl_id_function(void) +{ + return ((unsigned long) GetCurrentThreadId() ); +} + +static int alloc_mutexes( unsigned int total ) +{ + unsigned int i; + + // Enable thread safety in OpenSSL + mutex_buf = (CRITICAL_SECTION*) malloc( total * sizeof(CRITICAL_SECTION) ); + + if ( !mutex_buf ) + return -1; + + for ( i = 0; i < total; i++) + InitializeCriticalSection( &(mutex_buf[i]) ); + + return 0; +} + + +#else + +// This array will store all of the mutexes available to OpenSSL +static pthread_mutex_t * mutex_buf = 0; + +// OpenSSL callback to utilize static locks +static void cb_openssl_locking_function( int mode, int n, const char * file, int line ) +{ + (void)file; + (void)line; + + if ( mode & CRYPTO_LOCK) + pthread_mutex_lock( &mutex_buf[n] ); + else + pthread_mutex_unlock( &mutex_buf[n] ); +} + +// OpenSSL callback to get the thread ID +static unsigned long cb_openssl_id_function() +{ + return ((unsigned long) pthread_self() ); +} + +static int alloc_mutexes( unsigned int total ) +{ + unsigned i; + + // Enable thread safety in OpenSSL + mutex_buf = (pthread_mutex_t*) malloc( total * sizeof(pthread_mutex_t) ); + + if ( !mutex_buf ) + return -1; + + for ( i = 0; i < total; i++) + pthread_mutex_init( &(mutex_buf[i]), 0 ); + + return 0; +} + +#endif + +static int ssl_init_context( irc_session_t * session ) +{ + // Load the strings and init the library + SSL_load_error_strings(); + + // Enable thread safety in OpenSSL + if ( alloc_mutexes( CRYPTO_num_locks() ) ) + return LIBIRC_ERR_NOMEM; + + // Register our callbacks + CRYPTO_set_id_callback( cb_openssl_id_function ); + CRYPTO_set_locking_callback( cb_openssl_locking_function ); + + // Init it + if ( !SSL_library_init() ) + return LIBIRC_ERR_SSL_INIT_FAILED; + + if ( RAND_status() == 0 ) + return LIBIRC_ERR_SSL_INIT_FAILED; + + // Create an SSL context; currently a single context is used for all connections + ssl_context = SSL_CTX_new( SSLv23_method() ); + + if ( !ssl_context ) + return LIBIRC_ERR_SSL_INIT_FAILED; + + // Disable SSLv2 as it is unsecure + if ( (SSL_CTX_set_options( ssl_context, SSL_OP_NO_SSLv2) & SSL_OP_NO_SSLv2) == 0 ) + return LIBIRC_ERR_SSL_INIT_FAILED; + + // Enable only strong ciphers + if ( SSL_CTX_set_cipher_list( ssl_context, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH" ) != 1 ) + return LIBIRC_ERR_SSL_INIT_FAILED; + + // Set the verification + if ( session->options & LIBIRC_OPTION_SSL_NO_VERIFY ) + SSL_CTX_set_verify( ssl_context, SSL_VERIFY_NONE, 0 ); + else + SSL_CTX_set_verify( ssl_context, SSL_VERIFY_PEER, 0 ); + + // Disable session caching + SSL_CTX_set_session_cache_mode( ssl_context, SSL_SESS_CACHE_OFF ); + + // Enable SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER so we can move the buffer during sending + SSL_CTX_set_mode( ssl_context, SSL_CTX_get_mode(ssl_context) | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | SSL_MODE_ENABLE_PARTIAL_WRITE ); + + return 0; +} + + +#if defined (_WIN32) + #define SSLINIT_LOCK_MUTEX(a) WaitForSingleObject( a, INFINITE ) + #define SSLINIT_UNLOCK_MUTEX(a) ReleaseMutex( a ) +#else + #define SSLINIT_LOCK_MUTEX(a) pthread_mutex_lock( &a ) + #define SSLINIT_UNLOCK_MUTEX(a) pthread_mutex_unlock( &a ) +#endif + +// Initializes the SSL context. Must be called after the socket is created. +static int ssl_init( irc_session_t * session ) +{ + static int ssl_context_initialized = 0; + +#if defined (_WIN32) + static HANDLE initmutex = 0; + + // First time run? Create the mutex + if ( initmutex == 0 ) + { + HANDLE m = CreateMutex( 0, FALSE, 0 ); + + // Now we check if the mutex has already been created by another thread performing the init concurrently. + // If it was, we close our mutex and use the original one. This could be done synchronously by using the + // InterlockedCompareExchangePointer function. + if ( InterlockedCompareExchangePointer( &m, m, 0 ) != 0 ) + CloseHandle( m ); + } +#else + static pthread_mutex_t initmutex = PTHREAD_MUTEX_INITIALIZER; +#endif + + // This initialization needs to be performed only once. The problem is that it is called from + // irc_connect() and this function may be called simultaneously from different threads. So we have + // to use mutex on Linux because it allows static mutex initialization. Windows doesn't, so here + // we do the sabre dance around it. + SSLINIT_LOCK_MUTEX( initmutex ); + + if ( ssl_context_initialized == 0 ) + { + int res = ssl_init_context( session ); + + if ( res ) + { + SSLINIT_UNLOCK_MUTEX( initmutex ); + return res; + } + + ssl_context_initialized = 1; + } + + SSLINIT_UNLOCK_MUTEX( initmutex ); + + // Get the SSL context + session->ssl = SSL_new( ssl_context ); + + if ( !session->ssl ) + return LIBIRC_ERR_SSL_INIT_FAILED; + + // Let OpenSSL use our socket + if ( SSL_set_fd( session->ssl, session->sock) != 1 ) + return LIBIRC_ERR_SSL_INIT_FAILED; + + // Since we're connecting on our own, tell openssl about it + SSL_set_connect_state( session->ssl ); + + return 0; +} + +static void ssl_handle_error( irc_session_t * session, int ssl_error ) +{ + if ( ERR_GET_LIB(ssl_error) == ERR_LIB_SSL ) + { + if ( ERR_GET_REASON(ssl_error) == SSL_R_CERTIFICATE_VERIFY_FAILED ) + { + session->lasterror = LIBIRC_ERR_SSL_CERT_VERIFY_FAILED; + return; + } + + if ( ERR_GET_REASON(ssl_error) == SSL_R_UNKNOWN_PROTOCOL ) + { + session->lasterror = LIBIRC_ERR_CONNECT_SSL_FAILED; + return; + } + } + +#if defined (ENABLE_DEBUG) + if ( IS_DEBUG_ENABLED(session) ) + fprintf (stderr, "[DEBUG] SSL error: %s\n\t(%d, %d)\n", + ERR_error_string( ssl_error, NULL), ERR_GET_LIB( ssl_error), ERR_GET_REASON(ssl_error) ); +#endif +} + +static int ssl_recv( irc_session_t * session ) +{ + int count; + unsigned int amount = (sizeof (session->incoming_buf) - 1) - session->incoming_offset; + + ERR_clear_error(); + + // Read up to m_bufferLength bytes + count = SSL_read( session->ssl, session->incoming_buf + session->incoming_offset, amount ); + + if ( count > 0 ) + return count; + else if ( count == 0 ) + return -1; // remote connection closed + else + { + int ssl_error = SSL_get_error( session->ssl, count ); + + // Handle SSL error since not all of them are actually errors + switch ( ssl_error ) + { + case SSL_ERROR_WANT_READ: + // This is not really an error. We received something, but + // OpenSSL gave nothing to us because all it read was + // internal data. Repeat the same read. + return 0; + + case SSL_ERROR_WANT_WRITE: + // This is not really an error. We received something, but + // now OpenSSL needs to send the data before returning any + // data to us (like negotiations). This means we'd need + // to wait for WRITE event, but call SSL_read() again. + session->flags |= SESSIONFL_SSL_READ_WANTS_WRITE; + return 0; + } + + // This is an SSL error, handle it + ssl_handle_error( session, ERR_get_error() ); + } + + return -1; +} + + +static int ssl_send( irc_session_t * session ) +{ + int count; + ERR_clear_error(); + + count = SSL_write( session->ssl, session->outgoing_buf, session->outgoing_offset ); + + if ( count > 0 ) + return count; + else if ( count == 0 ) + return -1; + else + { + int ssl_error = SSL_get_error( session->ssl, count ); + + switch ( ssl_error ) + { + case SSL_ERROR_WANT_READ: + // This is not really an error. We sent some internal OpenSSL data, + // but now it needs to read more data before it can send anything. + // Thus we wait for READ event, but will call SSL_write() again. + session->flags |= SESSIONFL_SSL_WRITE_WANTS_READ; + return 0; + + case SSL_ERROR_WANT_WRITE: + // This is not really an error. We sent some data, but now OpenSSL + // wants to send some internal data before sending ours. + // Repeat the same write. + return 0; + } + + // This is an SSL error, handle it + ssl_handle_error( session, ERR_get_error() ); + } + + return -1; +} + +#endif + + +// Handles both SSL and non-SSL reads. +// Returns -1 in case there is an error and socket should be closed/connection terminated +// Returns 0 in case there is a temporary error and the call should be retried (SSL_WANTS_WRITE case) +// Returns a positive number if we actually read something +static int session_socket_read( irc_session_t * session ) +{ + int length; + +#if defined (ENABLE_SSL) + if ( session->ssl ) + { + // Yes, I know this is tricky + if ( session->flags & SESSIONFL_SSL_READ_WANTS_WRITE ) + { + session->flags &= ~SESSIONFL_SSL_READ_WANTS_WRITE; + ssl_send( session ); + return 0; + } + + return ssl_recv( session ); + } +#endif + + length = socket_recv( &session->sock, + session->incoming_buf + session->incoming_offset, + (sizeof (session->incoming_buf) - 1) - session->incoming_offset ); + + // There is no "retry" errors for regular sockets + if ( length <= 0 ) + return -1; + + return length; +} + +// Handles both SSL and non-SSL writes. +// Returns -1 in case there is an error and socket should be closed/connection terminated +// Returns 0 in case there is a temporary error and the call should be retried (SSL_WANTS_WRITE case) +// Returns a positive number if we actually sent something +static int session_socket_write( irc_session_t * session ) +{ + int length; + +#if defined (ENABLE_SSL) + if ( session->ssl ) + { + // Yep + if ( session->flags & SESSIONFL_SSL_WRITE_WANTS_READ ) + { + session->flags &= ~SESSIONFL_SSL_WRITE_WANTS_READ; + ssl_recv( session ); + return 0; + } + + return ssl_send( session ); + } +#endif + + length = socket_send (&session->sock, session->outgoing_buf, session->outgoing_offset); + + // There is no "retry" errors for regular sockets + if ( length <= 0 ) + return -1; + + return length; +} diff --git a/external/IRC/utils.c b/external/IRC/utils.c new file mode 100644 index 00000000..9305b918 --- /dev/null +++ b/external/IRC/utils.c @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2004-2012 George Yunaev gyunaev@ulduzsoft.com + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + */ + +static void libirc_add_to_set (int fd, fd_set *set, int * maxfd) +{ + FD_SET (fd, set); + + if ( *maxfd < fd ) + *maxfd = fd; +} + +#if defined (ENABLE_DEBUG) +static void libirc_dump_data (const char * prefix, const char * buf, unsigned int length) +{ + printf ("%s: ", prefix); + for ( ; length > 0; length -- ) + printf ("%c", *buf++); +} +#endif + + +/* + * Finds a separator (\x0D\x0A), which separates two lines. + */ +static int libirc_findcrlf (const char * buf, int length) +{ + int offset = 0; + for ( ; offset < length; offset++ ) + { + if ( buf[offset] == 0x0D && offset < length - 1 && buf[offset+1] == 0x0A ) + return offset; + if ( buf[offset] == 0x0A) + return offset; + } + + return 0; +} + +static int libirc_findcrlf_offset(const char *buf, int offset, const int length) +{ + for(; offset < length; offset++) + { + if(buf[offset] != 0x0D && buf[offset] != 0x0A) + { + break; + } + } + return offset; +} + +static int libirc_findcrorlf (char * buf, int length) +{ + int offset = 0; + for ( ; offset < length; offset++ ) + { + if ( buf[offset] == 0x0D || buf[offset] == 0x0A ) + { + buf[offset++] = '\0'; + + if ( offset < (length - 1) + && (buf[offset] == 0x0D || buf[offset] == 0x0A) ) + offset++; + + return offset; + } + } + + return 0; +} + + +static void libirc_event_ctcp_internal (irc_session_t * session, const char * event, const char * origin, const char ** params, unsigned int count) +{ + (void)event; + (void)count; + + if ( origin ) + { + char nickbuf[128], textbuf[256]; + irc_target_get_nick (origin, nickbuf, sizeof(nickbuf)); + + if ( strstr (params[0], "PING") == params[0] ) + irc_cmd_ctcp_reply (session, nickbuf, params[0]); + else if ( !strcmp (params[0], "VERSION") ) + { + if ( !session->ctcp_version ) + { + unsigned int high, low; + irc_get_version (&high, &low); + + snprintf (textbuf, sizeof (textbuf), "VERSION libircclient by Georgy Yunaev ver.%d.%d", high, low); + } + else + snprintf (textbuf, sizeof (textbuf), "VERSION %s", session->ctcp_version); + + irc_cmd_ctcp_reply (session, nickbuf, textbuf); + } + else if ( !strcmp (params[0], "FINGER") ) + { + sprintf (textbuf, "FINGER %s (%s) Idle 0 seconds", + session->username ? session->username : "nobody", + session->realname ? session->realname : "noname"); + + irc_cmd_ctcp_reply (session, nickbuf, textbuf); + } + else if ( !strcmp (params[0], "TIME") ) + { + time_t now = time(0); + +#if defined (ENABLE_THREADS) && defined (HAVE_LOCALTIME_R) + struct tm tmtmp, *ltime = localtime_r (&now, &tmtmp); +#else + struct tm * ltime = localtime (&now); +#endif + strftime (textbuf, sizeof(textbuf), "%a %b %d %H:%M:%S %Z %Y", ltime); + irc_cmd_ctcp_reply (session, nickbuf, textbuf); + } + } +} diff --git a/include/libirc_errors.h b/include/libirc_errors.h new file mode 100644 index 00000000..f627fbbc --- /dev/null +++ b/include/libirc_errors.h @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2004-2012 George Yunaev gyunaev@ulduzsoft.com + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + */ + +#ifndef INCLUDE_IRC_ERRORS_H +#define INCLUDE_IRC_ERRORS_H + +#ifndef IN_INCLUDE_LIBIRC_H + #error This file should not be included directly, include just libircclient.h +#endif + + +/*! brief No error + * \ingroup errorcodes + */ +#define LIBIRC_ERR_OK 0 + + +/*! \brief Invalid argument + * + * An invalid value was given for one of the arguments to a function. + * For example, supplying the NULL value for \a channel argument of + * irc_cmd_join() produces LIBIRC_ERR_INVAL error. You should fix the code. + * + * \ingroup errorcodes + */ +#define LIBIRC_ERR_INVAL 1 + + +/*! \brief Could not resolve host. + * + * The host name supplied for irc_connect() function could not be resolved + * into valid IP address. Usually means that host name is invalid. + * + * \ingroup errorcodes + */ +#define LIBIRC_ERR_RESOLV 2 + + +/*! \brief Could not create socket. + * + * The new socket could not be created or made non-blocking. Usually means + * that the server is out of resources, or (rarely :) a bug in libircclient. + * + * \ingroup errorcodes + */ +#define LIBIRC_ERR_SOCKET 3 + + +/*! \brief Could not connect. + * + * The socket could not connect to the IRC server, or to the destination DCC + * part. Usually means that either the IRC server is down or its address is + * invalid. For DCC the reason usually is the firewall on your or destination + * computer, which refuses DCC transfer. + * + * \sa irc_run irc_connect + * \ingroup errorcodes + */ +#define LIBIRC_ERR_CONNECT 4 + + +/*! \brief Connection closed by remote peer. + * + * The IRC connection was closed by the IRC server (which could mean that an + * IRC operator just have banned you from the server :)), or the DCC connection + * was closed by remote peer - for example, the other side just quits his mIrc. + * Usually it is not an error. + * + * \sa irc_run irc_connect irc_dcc_callback_t + * \ingroup errorcodes + */ +#define LIBIRC_ERR_CLOSED 5 + + +/*! \brief Out of memory + * + * There are two possible reasons for this error. First is that memory could + * not be allocated for libircclient use, and this error usually is fatal. + * Second reason is that the command queue (which keeps command ready to be + * sent to the IRC server) is full, and could not accept more commands yet. + * In this case you should just wait, and repeat the command later. + * + * \ingroup errorcodes + */ +#define LIBIRC_ERR_NOMEM 6 + + +/*! \brief Could not accept new connection + * + * A DCC chat/send connection from the remote peer could not be accepted. + * Either the connection was just terminated before it is accepted, or there + * is a bug in libircclient. + * + * \ingroup errorcodes + */ +#define LIBIRC_ERR_ACCEPT 7 + + +/*! \brief Could not send this + * + * A \a filename supplied to irc_dcc_sendfile() could not be sent. Either is + * is not a file (a directory or a socket, for example), or it is not readable. * + * + * \sa LIBIRC_ERR_OPENFILE + * \ingroup errorcodes + */ +#define LIBIRC_ERR_NODCCSEND 9 + + +/*! \brief Could not read DCC file or socket + * + * Either a DCC file could not be read (for example, was truncated during + * sending), or a DCC socket returns a read error, which usually means that + * the network connection is terminated. + * + * \ingroup errorcodes + */ +#define LIBIRC_ERR_READ 10 + + +/*! \brief Could not write DCC file or socket + * + * Either a DCC file could not be written (for example, there is no free space + * on disk), or a DCC socket returns a write error, which usually means that + * the network connection is terminated. + * + * \ingroup errorcodes + */ +#define LIBIRC_ERR_WRITE 11 + + +/*! \brief Invalid state + * + * The function is called when it is not allowed to be called. For example, + * irc_cmd_join() was called before the connection to IRC server succeed, and + * ::event_connect is called. + * + * \ingroup errorcodes + */ +#define LIBIRC_ERR_STATE 12 + + +/*! \brief Operation timed out + * + * The DCC request is timed out. + * There is a timer for each DCC request, which tracks connecting, accepting + * and non-accepted/declined DCC requests. For every request this timer + * is currently 60 seconds. If the DCC request was not connected, accepted + * or declined during this time, it will be terminated with this error. + * + * \ingroup errorcodes + */ +#define LIBIRC_ERR_TIMEOUT 13 + + +/*! \brief Could not open file for DCC send + * + * The file specified in irc_dcc_sendfile() could not be opened. + * + * \ingroup errorcodes + */ +#define LIBIRC_ERR_OPENFILE 14 + + +/*! \brief IRC server connection terminated + * + * The connection to the IRC server was terminated - possibly, by network + * error. Try to irc_connect() again. + * + * \ingroup errorcodes + */ +#define LIBIRC_ERR_TERMINATED 15 + + +/*! \brief IPv6 not supported + * + * The function which requires IPv6 support was called, but the IPv6 support was not compiled + * into the application + * + * \ingroup errorcodes + */ +#define LIBIRC_ERR_NOIPV6 16 + + +/*! \brief SSL not supported + * + * The SSL connection was required but the library was not compiled with SSL support + * + * \ingroup errorcodes + */ +#define LIBIRC_ERR_SSL_NOT_SUPPORTED 17 + + +/*! \brief SSL initialization failed + * + * The SSL connection was required but the library was not compiled with SSL support + * + * \ingroup errorcodes + */ +#define LIBIRC_ERR_SSL_INIT_FAILED 18 + + +/*! \brief SSL connection failed + * + * SSL handshare failed when attempting to connect to the server. Typically this means you're trying + * to use SSL but attempting to connect to a non-SSL port. + * \ingroup errorcodes + */ +#define LIBIRC_ERR_CONNECT_SSL_FAILED 19 + + +/*! \brief SSL certificate verify failed + * + * The server is using the self-signed certificate. Use LIBIRC_OPTION_SSL_NO_VERIFY option to connect to it. + * \ingroup errorcodes + */ +#define LIBIRC_ERR_SSL_CERT_VERIFY_FAILED 20 + + +// Internal max error value count. +// If you added more errors, add them to errors.c too! +#define LIBIRC_ERR_MAX 21 + +#endif /* INCLUDE_IRC_ERRORS_H */ diff --git a/include/libirc_events.h b/include/libirc_events.h new file mode 100644 index 00000000..15711f73 --- /dev/null +++ b/include/libirc_events.h @@ -0,0 +1,389 @@ +/* + * Copyright (C) 2004-2012 George Yunaev gyunaev@ulduzsoft.com + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + */ + + +#ifndef INCLUDE_IRC_EVENTS_H +#define INCLUDE_IRC_EVENTS_H + + +#ifndef IN_INCLUDE_LIBIRC_H + #error This file should not be included directly, include just libircclient.h +#endif + + + +/*! + * \fn typedef void (*irc_event_callback_t) (irc_session_t * session, const char * event, const char * origin, const char ** params, unsigned int count) + * \brief A most common event callback + * + * \param session the session, which generates an event + * \param event the text name of the event. Useful in case you use a single + * event handler for several events simultaneously. + * \param origin the originator of the event. See the note below. + * \param params a list of event params. Depending on the event nature, it + * could have zero or more params. The actual number of params + * is specified in count. None of the params can be NULL, but + * 'params' pointer itself could be NULL for some events. + * \param count the total number of params supplied. + * + * Every event generates a callback. This callback is generated by most events. + * Depending on the event nature, it can provide zero or more params. For each + * event, the number of provided params is fixed, and their meaning is + * described. + * + * Every event has origin, though the \a origin variable may be NULL, which + * means that event origin is unknown. The origin usually looks like + * nick!host\@ircserver, i.e. like tim!home\@irc.krasnogorsk.ru. Such origins + * can not be used in IRC commands, and need to be stripped (i.e. host and + * server part should be cut off) before using. This can be done either + * explicitly, by calling irc_target_get_nick(), or implicitly for all the + * events - by setting the #LIBIRC_OPTION_STRIPNICKS option with irc_option_set(). + * + * \ingroup events + */ +typedef void (*irc_event_callback_t) (irc_session_t * session, const char * event, const char * origin, const char ** params, unsigned int count); + + +/*! + * \fn typedef void (*irc_eventcode_callback_t) (irc_session_t * session, unsigned int event, const char * origin, const char ** params, unsigned int count) + * \brief A numeric event callback + * + * \param session the session, which generates an event + * \param event the numeric code of the event. Useful in case you use a + * single event handler for several events simultaneously. + * \param origin the originator of the event. See the note below. + * \param params a list of event params. Depending on the event nature, it + * could have zero or more params. The actual number of params + * is specified in count. None of the params can be NULL, but + * 'params' pointer itself could be NULL for some events. + * \param count the total number of params supplied. + * + * Most times in reply to your actions the IRC server generates numeric + * callbacks. Most of them are error codes, and some of them mark list start + * and list stop markers. Every code has its own set of params; for details + * you can either experiment, or read RFC 1459. + * + * Every event has origin, though the \a origin variable may be NULL, which + * means that event origin is unknown. The origin usually looks like + * nick!host\@ircserver, i.e. like tim!home\@irc.krasnogorsk.ru. Such origins + * can not be used in IRC commands, and need to be stripped (i.e. host and + * server part should be cut off) before using. This can be done either + * explicitly, by calling irc_target_get_nick(), or implicitly for all the + * events - by setting the #LIBIRC_OPTION_STRIPNICKS option with irc_option_set(). + * + * \ingroup events + */ +typedef void (*irc_eventcode_callback_t) (irc_session_t * session, unsigned int event, const char * origin, const char ** params, unsigned int count); + + +/*! + * \fn typedef void (*irc_event_dcc_chat_t) (irc_session_t * session, const char * nick, const char * addr, irc_dcc_t dccid) + * \brief A remote DCC CHAT request callback + * + * \param session the session, which generates an event + * \param nick the person who requested DCC CHAT with you. + * \param addr the person's IP address in decimal-dot notation. + * \param dccid an id associated with this request. Use it in calls to + * irc_dcc_accept() or irc_dcc_decline(). + * + * This callback is called when someone requests DCC CHAT with you. In respond + * you should call either irc_dcc_accept() to accept chat request, or + * irc_dcc_decline() to decline chat request. + * + * \sa irc_dcc_accept or irc_dcc_decline + * \ingroup events + */ +typedef void (*irc_event_dcc_chat_t) (irc_session_t * session, const char * nick, const char * addr, irc_dcc_t dccid); + + +/*! + * \fn typedef void (*irc_event_dcc_send_t) (irc_session_t * session, const char * nick, const char * addr, const char * filename, unsigned long size, irc_dcc_t dccid) + * \brief A remote DCC CHAT request callback + * + * \param session the session, which generates an event + * \param nick the person who requested DCC CHAT with you. + * \param addr the person's IP address in decimal-dot notation. + * \param filename the sent filename. + * \param size the filename size. + * \param dccid an id associated with this request. Use it in calls to + * irc_dcc_accept() or irc_dcc_decline(). + * + * This callback is called when someone wants to send a file to you using + * DCC SEND. As with chat, in respond you should call either irc_dcc_accept() + * to accept this request and receive the file, or irc_dcc_decline() to + * decline this request. + * + * \sa irc_dcc_accept or irc_dcc_decline + * \ingroup events + */ +typedef void (*irc_event_dcc_send_t) (irc_session_t * session, const char * nick, const char * addr, const char * filename, unsigned long size, irc_dcc_t dccid); + + +/*! \brief Event callbacks structure. + * + * All the communication with the IRC network is based on events. Generally + * speaking, event is anything generated by someone else in the network, + * or by the IRC server itself. "Someone sends you a message", "Someone + * has joined the channel", "Someone has quits IRC" - all these messages + * are events. + * + * Every event has its own event handler, which is called when the + * appropriate event is received. You don't have to define all the event + * handlers; define only the handlers for the events you need to intercept. + * + * Most event callbacks are the types of ::irc_event_callback_t. There are + * also events, which generate ::irc_eventcode_callback_t, + * ::irc_event_dcc_chat_t and ::irc_event_dcc_send_t callbacks. + * + * \ingroup events + */ +typedef struct +{ + /*! + * The "on_connect" event is triggered when the client successfully + * connects to the server, and could send commands to the server. + * No extra params supplied; \a params is 0. + */ + irc_event_callback_t event_connect; + + /*! + * The "nick" event is triggered when the client receives a NICK message, + * meaning that someone (including you) on a channel with the client has + * changed their nickname. + * + * \param origin the person, who changes the nick. Note that it can be you! + * \param params[0] mandatory, contains the new nick. + */ + irc_event_callback_t event_nick; + + /*! + * The "quit" event is triggered upon receipt of a QUIT message, which + * means that someone on a channel with the client has disconnected. + * + * \param origin the person, who is disconnected + * \param params[0] optional, contains the reason message (user-specified). + */ + irc_event_callback_t event_quit; + + /*! + * The "join" event is triggered upon receipt of a JOIN message, which + * means that someone has entered a channel that the client is on. + * + * \param origin the person, who joins the channel. By comparing it with + * your own nickname, you can check whether your JOIN + * command succeed. + * \param params[0] mandatory, contains the channel name. + */ + irc_event_callback_t event_join; + + /*! + * The "part" event is triggered upon receipt of a PART message, which + * means that someone has left a channel that the client is on. + * + * \param origin the person, who leaves the channel. By comparing it with + * your own nickname, you can check whether your PART + * command succeed. + * \param params[0] mandatory, contains the channel name. + * \param params[1] optional, contains the reason message (user-defined). + */ + irc_event_callback_t event_part; + + /*! + * The "mode" event is triggered upon receipt of a channel MODE message, + * which means that someone on a channel with the client has changed the + * channel's parameters. + * + * \param origin the person, who changed the channel mode. + * \param params[0] mandatory, contains the channel name. + * \param params[1] mandatory, contains the changed channel mode, like + * '+t', '-i' and so on. + * \param params[2] optional, contains the mode argument (for example, a + * key for +k mode, or user who got the channel operator status for + * +o mode) + */ + irc_event_callback_t event_mode; + + /*! + * The "umode" event is triggered upon receipt of a user MODE message, + * which means that your user mode has been changed. + * + * \param origin the person, who changed the channel mode. + * \param params[0] mandatory, contains the user changed mode, like + * '+t', '-i' and so on. + */ + irc_event_callback_t event_umode; + + /*! + * The "topic" event is triggered upon receipt of a TOPIC message, which + * means that someone on a channel with the client has changed the + * channel's topic. + * + * \param origin the person, who changes the channel topic. + * \param params[0] mandatory, contains the channel name. + * \param params[1] optional, contains the new topic. + */ + irc_event_callback_t event_topic; + + /*! + * The "kick" event is triggered upon receipt of a KICK message, which + * means that someone on a channel with the client (or possibly the + * client itself!) has been forcibly ejected. + * + * \param origin the person, who kicked the poor. + * \param params[0] mandatory, contains the channel name. + * \param params[0] optional, contains the nick of kicked person. + * \param params[1] optional, contains the kick text + */ + irc_event_callback_t event_kick; + + /*! + * The "channel" event is triggered upon receipt of a PRIVMSG message + * to an entire channel, which means that someone on a channel with + * the client has said something aloud. Your own messages don't trigger + * PRIVMSG event. + * + * \param origin the person, who generates the message. + * \param params[0] mandatory, contains the channel name. + * \param params[1] optional, contains the message text + */ + irc_event_callback_t event_channel; + + /*! + * The "privmsg" event is triggered upon receipt of a PRIVMSG message + * which is addressed to one or more clients, which means that someone + * is sending the client a private message. + * + * \param origin the person, who generates the message. + * \param params[0] mandatory, contains your nick. + * \param params[1] optional, contains the message text + */ + irc_event_callback_t event_privmsg; + + /*! + * The "notice" event is triggered upon receipt of a NOTICE message + * which means that someone has sent the client a public or private + * notice. According to RFC 1459, the only difference between NOTICE + * and PRIVMSG is that you should NEVER automatically reply to NOTICE + * messages. Unfortunately, this rule is frequently violated by IRC + * servers itself - for example, NICKSERV messages require reply, and + * are NOTICEs. + * + * \param origin the person, who generates the message. + * \param params[0] mandatory, contains the target nick name. + * \param params[1] optional, contains the message text + */ + irc_event_callback_t event_notice; + + /*! + * The "channel_notice" event is triggered upon receipt of a NOTICE + * message which means that someone has sent the client a public + * notice. According to RFC 1459, the only difference between NOTICE + * and PRIVMSG is that you should NEVER automatically reply to NOTICE + * messages. Unfortunately, this rule is frequently violated by IRC + * servers itself - for example, NICKSERV messages require reply, and + * are NOTICEs. + * + * \param origin the person, who generates the message. + * \param params[0] mandatory, contains the channel name. + * \param params[1] optional, contains the message text + */ + irc_event_callback_t event_channel_notice; + + /*! + * The "invite" event is triggered upon receipt of an INVITE message, + * which means that someone is permitting the client's entry into a +i + * channel. + * + * \param origin the person, who INVITEs you. + * \param params[0] mandatory, contains your nick. + * \param params[1] mandatory, contains the channel name you're invited into. + * + * \sa irc_cmd_invite irc_cmd_chanmode_invite + */ + irc_event_callback_t event_invite; + + /*! + * The "ctcp" event is triggered when the client receives the CTCP + * request. By default, the built-in CTCP request handler is used. The + * build-in handler automatically replies on most CTCP messages, so you + * will rarely need to override it. + * + * \param origin the person, who generates the message. + * \param params[0] mandatory, the complete CTCP message, including its + * arguments. + * + * Mirc generates PING, FINGER, VERSION, TIME and ACTION messages, + * check the source code of \c libirc_event_ctcp_internal function to + * see how to write your own CTCP request handler. Also you may find + * useful this question in FAQ: \ref faq4 + */ + irc_event_callback_t event_ctcp_req; + + /*! + * The "ctcp" event is triggered when the client receives the CTCP reply. + * + * \param origin the person, who generates the message. + * \param params[0] mandatory, the CTCP message itself with its arguments. + */ + irc_event_callback_t event_ctcp_rep; + + /*! + * The "action" event is triggered when the client receives the CTCP + * ACTION message. These messages usually looks like:\n + * \code + * [23:32:55] * Tim gonna sleep. + * \endcode + * + * \param origin the person, who generates the message. + * \param params[0] mandatory, the ACTION message. + */ + irc_event_callback_t event_ctcp_action; + + /*! + * The "unknown" event is triggered upon receipt of any number of + * unclassifiable miscellaneous messages, which aren't handled by the + * library. + */ + irc_event_callback_t event_unknown; + + /*! + * The "numeric" event is triggered upon receipt of any numeric response + * from the server. There is a lot of such responses, see the full list + * here: \ref rfcnumbers. + * + * See the params in ::irc_eventcode_callback_t specification. + */ + irc_eventcode_callback_t event_numeric; + + /*! + * The "dcc chat" event is triggered when someone requests a DCC CHAT from + * you. + * + * See the params in ::irc_event_dcc_chat_t specification. + */ + irc_event_dcc_chat_t event_dcc_chat_req; + + /*! + * The "dcc chat" event is triggered when someone wants to send a file + * to you via DCC SEND request. + * + * See the params in ::irc_event_dcc_send_t specification. + */ + irc_event_dcc_send_t event_dcc_send_req; + + +} irc_callbacks_t; + + +#endif /* INCLUDE_IRC_EVENTS_H */ diff --git a/include/libirc_options.h b/include/libirc_options.h new file mode 100644 index 00000000..7494f23d --- /dev/null +++ b/include/libirc_options.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2004-2012 George Yunaev gyunaev@ulduzsoft.com + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + */ + +#ifndef INCLUDE_IRC_OPTIONS_H +#define INCLUDE_IRC_OPTIONS_H + +#ifndef IN_INCLUDE_LIBIRC_H + #error This file should not be included directly, include just libircclient.h +#endif + +/*! + * enables additional debug output + * \ingroup options + */ +#define LIBIRC_OPTION_DEBUG (1 << 1) + +/*! \brief allows to strip origins automatically. + * + * For every IRC server event, the event origin is sent in standard form: + * nick!host\@ircserver, i.e. like tim!home\@irc.freenet.org. Such origins + * can not be used in IRC commands, and need to be stripped (i.e. host and + * server part should be cut off) before using. This can be done either + * explicitly, by calling irc_target_get_nick(), or implicitly for all the + * events - by setting this option with irc_option_set(). + * \ingroup options + */ +#define LIBIRC_OPTION_STRIPNICKS (1 << 2) + + +/*! \brief Disables the certificate verification for SSL connections + * + * By default the SSL connection authenticy is ensured by verifying that the certificate + * presented by the server is signed by a known trusted certificate authority. Since those + * typically cost money, some IRC servers use the self-signed certificates. They provide the + * benefits of the SSL connection but since they are not signed by the Certificate Authority, + * their authencity cannot be verified. This option, if set, disables the certificate + * verification - the library will accept any certificate presented by the server. + * + * This option must be set before the irc_connect function is called. + * \ingroup options + */ +#define LIBIRC_OPTION_SSL_NO_VERIFY (1 << 3) + + +#endif /* INCLUDE_IRC_OPTIONS_H */ diff --git a/include/libirc_rfcnumeric.h b/include/libirc_rfcnumeric.h new file mode 100644 index 00000000..02c13379 --- /dev/null +++ b/include/libirc_rfcnumeric.h @@ -0,0 +1,1255 @@ +/* + * Copyright (C) 2004-2012 George Yunaev gyunaev@ulduzsoft.com + * + * This library is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + */ + +/*! + * \file libirc_rfcnumeric.h + * \author Georgy Yunaev + * \version 1.0 + * \date 09.2004 + * \brief This file defines RFC numeric reply codes, which should be used in + * ::event_numeric callback. Every code also has a comment regarding its + * arguments. + */ + +#ifndef INCLUDE_IRC_RFCNUMERIC_H +#define INCLUDE_IRC_RFCNUMERIC_H + + +/*! \brief 001 Welcome to the Internet Relay Network + \!\\@\ + * + * The server sends replies 001 to 004 to a user upon successful registration. + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_WELCOME 001 + + +/*! \brief 002 Your host is \, running version \ + * + * The server sends replies 001 to 004 to a user upon successful registration. + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_YOURHOST 002 + + +/*! \brief 003 This server was created \ + * + * The server sends replies 001 to 004 to a user upon successful registration. + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_CREATED 003 + + +/*! \brief 004 \ \ \ + \ + * + * The server sends replies 001 to 004 to a user upon successful registration. + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_MYINFO 004 + + +/*! \brief 005 Try server \, port \ + * + * Sent by the server to a user to suggest an alternative server. This is often used when the connection is refused because the server is already full. + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_BOUNCE 005 + + +/*! \brief 302 :*1\ *( + * + * No description available in RFC + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_USERHOST 302 + + +/*! \brief 303 :*1\ *( + * + * No description available in RFC + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_ISON 303 + + +/*! \brief 301 \ :\ + * + * No description available in RFC + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_AWAY 301 + + +/*! \brief 305 :You are no longer marked as being away + * + * No description available in RFC + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_UNAWAY 305 + + +/*! \brief 306 :You have been marked as being away + * + * These replies are used with the AWAY command (if allowed). RPL_AWAY is sent to any client sending a PRIVMSG to a client which is away. RPL_AWAY is only sent by the server to which the client is connected. Replies RPL_UNAWAY and RPL_NOWAWAY are sent when the client removes and sets an AWAY message. + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_NOWAWAY 306 + + +/*! \brief 311 \ \ \ * :\ + * + * No description available in RFC + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_WHOISUSER 311 + + +/*! \brief 312 \ \ :\ + * + * No description available in RFC + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_WHOISSERVER 312 + + +/*! \brief 313 \ :is an IRC operator + * + * No description available in RFC + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_WHOISOPERATOR 313 + + +/*! \brief 317 \ \ :seconds idle + * + * No description available in RFC + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_WHOISIDLE 317 + + +/*! \brief 318 \ :End of WHOIS list + * + * No description available in RFC + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_ENDOFWHOIS 318 + + +/*! \brief 319 " :*( ( "\@" / "+" ) \ " " )" + * + * No description available in RFC + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_WHOISCHANNELS 319 + + +/*! \brief 314 \ \ \ * :\ + * + * No description available in RFC + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_WHOWASUSER 314 + + +/*! \brief 369 \ :End of WHOWAS + * + * When replying to a WHOWAS message, a server MUST use the replies RPL_WHOWASUSER, RPL_WHOISSERVER or ERR_WASNOSUCHNICK for each nickname in the presented list. At the end of all reply batches, there MUST be RPL_ENDOFWHOWAS (even if there was only one reply and it was an error). + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_ENDOFWHOWAS 369 + + +/*! \brief 322 \ \<# visible\> :\ + * + * No description available in RFC + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_LIST 322 + + +/*! \brief 323 :End of LIST + * + * Replies RPL_LIST, RPL_LISTEND mark the actual replies with data and end of the server's response to a LIST command. If there are no channels available to return, only the end reply MUST be sent. + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_LISTEND 323 + + +/*! \brief 325 \ \ + * + * No description available in RFC + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_UNIQOPIS 325 + + +/*! \brief 324 \ \ \ + * + * No description available in RFC + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_CHANNELMODEIS 324 + + +/*! \brief 331 \ :No topic is set + * + * No description available in RFC + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_NOTOPIC 331 + + +/*! \brief 332 \ :\ + * + * When sending a TOPIC message to determine the channel topic, one of two replies is sent. If the topic is set, RPL_TOPIC is sent back else RPL_NOTOPIC. + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_TOPIC 332 + + +/*! \brief 341 \ \ + * + * Returned by the server to indicate that the attempted INVITE message was successful and is being passed onto the end client. + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_INVITING 341 + + +/*! \brief 342 \ :Summoning user to IRC + * + * Returned by a server answering a SUMMON message to indicate that it is summoning that user. + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_SUMMONING 342 + + +/*! \brief 346 \ \ + * + * No description available in RFC + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_INVITELIST 346 + + +/*! \brief 347 \ :End of channel invite list + * + * When listing the 'invitations masks' for a given channel, a server is required to send the list back using the RPL_INVITELIST and RPL_ENDOFINVITELIST messages. A separate RPL_INVITELIST is sent for each active mask. After the masks have been listed (or if none present) a RPL_ENDOFINVITELIST MUST be sent. + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_ENDOFINVITELIST 347 + + +/*! \brief 348 \ \ + * + * No description available in RFC + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_EXCEPTLIST 348 + + +/*! \brief 349 \ :End of channel exception list + * + * When listing the 'exception masks' for a given channel, a server is required to send the list back using the RPL_EXCEPTLIST and RPL_ENDOFEXCEPTLIST messages. A separate RPL_EXCEPTLIST is sent for each active mask. After the masks have been listed (or if none present) a RPL_ENDOFEXCEPTLIST MUST be sent. + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_ENDOFEXCEPTLIST 349 + + +/*! \brief 351 \.\ \ :\ + * + * Reply by the server showing its version details. The \ is the version of the software being used (including any patchlevel revisions) and the \ is used to indicate if the server is running in "debug mode". The "comments" field may contain any comments about the version or further version details. + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_VERSION 351 + + +/*! \brief 352 \ \ \ \ \ + ( "H + * + * No description available in RFC + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_WHOREPLY 352 + + +/*! \brief 315 \ :End of WHO list + * + * The RPL_WHOREPLY and RPL_ENDOFWHO pair are used to answer a WHO message. The RPL_WHOREPLY is only sent if there is an appropriate match to the WHO query. If there is a list of parameters supplied with a WHO message, a RPL_ENDOFWHO MUST be sent after processing each list item with \ being the item. + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_ENDOFWHO 315 + + +/*! \brief 353 ( "= + * + * No description available in RFC + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_NAMREPLY 353 + + +/*! \brief 366 \ :End of NAMES list + * + * To reply to a NAMES message, a reply pair consisting of RPL_NAMREPLY and RPL_ENDOFNAMES is sent by the server back to the client. If there is no channel found as in the query, then only RPL_ENDOFNAMES is returned. The exception to this is when a NAMES message is sent with no parameters and all visible channels and contents are sent back in a series of RPL_NAMEREPLY messages with a RPL_ENDOFNAMES to mark the end. + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_ENDOFNAMES 366 + + +/*! \brief 364 \ \ :\ \ + * + * No description available in RFC + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_LINKS 364 + + +/*! \brief 365 \ :End of LINKS list + * + * In replying to the LINKS message, a server MUST send replies back using the RPL_LINKS numeric and mark the end of the list using an RPL_ENDOFLINKS reply. + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_ENDOFLINKS 365 + + +/*! \brief 367 \ \ + * + * No description available in RFC + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_BANLIST 367 + + +/*! \brief 368 \ :End of channel ban list + * + * When listing the active 'bans' for a given channel, a server is required to send the list back using the RPL_BANLIST and RPL_ENDOFBANLIST messages. A separate RPL_BANLIST is sent for each active banmask. After the banmasks have been listed (or if none present) a RPL_ENDOFBANLIST MUST be sent. + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_ENDOFBANLIST 368 + + +/*! \brief 371 :\ + * + * No description available in RFC + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_INFO 371 + + +/*! \brief 374 :End of INFO list + * + * A server responding to an INFO message is required to send all its 'info' in a series of RPL_INFO messages with a RPL_ENDOFINFO reply to indicate the end of the replies. + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_ENDOFINFO 374 + + +/*! \brief 375 :- \ Message of the day - + * + * No description available in RFC + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_MOTDSTART 375 + + +/*! \brief 372 :- \ + * + * No description available in RFC + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_MOTD 372 + + +/*! \brief 376 :End of MOTD command + * + * When responding to the MOTD message and the MOTD file is found, the file is displayed line by line, with each line no longer than 80 characters, using RPL_MOTD format replies. These MUST be surrounded by a RPL_MOTDSTART (before the RPL_MOTDs) and an RPL_ENDOFMOTD (after). + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_ENDOFMOTD 376 + + +/*! \brief 381 :You are now an IRC operator + * + * RPL_YOUREOPER is sent back to a client which has just successfully issued an OPER message and gained operator status. + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_YOUREOPER 381 + + +/*! \brief 382 \ :Rehashing + * + * If the REHASH option is used and an operator sends a REHASH message, an RPL_REHASHING is sent back to the operator. + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_REHASHING 382 + + +/*! \brief 383 You are service \ + * + * Sent by the server to a service upon successful registration. + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_YOURESERVICE 383 + + +/*! \brief 391 \ :\ + * + * When replying to the TIME message, a server MUST send the reply using the RPL_TIME format above. The string showing the time need only contain the correct day and time there. There is no further requirement for the time string. + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_TIME 391 + + +/*! \brief 392 :UserID Terminal Host + * + * No description available in RFC + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_USERSSTART 392 + + +/*! \brief 393 :\ \ \ + * + * No description available in RFC + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_USERS 393 + + +/*! \brief 394 :End of users + * + * No description available in RFC + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_ENDOFUSERS 394 + + +/*! \brief 395 :Nobody logged in + * + * If the USERS message is handled by a server, the replies RPL_USERSTART, RPL_USERS, RPL_ENDOFUSERS and RPL_NOUSERS are used. RPL_USERSSTART MUST be sent first, following by either a sequence of RPL_USERS or a single RPL_NOUSER. Following this is RPL_ENDOFUSERS. + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_NOUSERS 395 + + +/*! \brief 200 Link \ \ + \ V\ + \ \ + \ + * + * No description available in RFC + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_TRACELINK 200 + + +/*! \brief 201 Try. \ \ + * + * No description available in RFC + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_TRACECONNECTING 201 + + +/*! \brief 202 H.S. \ \ + * + * No description available in RFC + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_TRACEHANDSHAKE 202 + + +/*! \brief 203 ???? \ [\] + * + * No description available in RFC + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_TRACEUNKNOWN 203 + + +/*! \brief 204 Oper \ \ + * + * No description available in RFC + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_TRACEOPERATOR 204 + + +/*! \brief 205 User \ \ + * + * No description available in RFC + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_TRACEUSER 205 + + +/*! \brief 206 Serv \ \S \C \ + \\@\ V\ + * + * No description available in RFC + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_TRACESERVER 206 + + +/*! \brief 207 Service \ \ \ \ + * + * No description available in RFC + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_TRACESERVICE 207 + + +/*! \brief 208 \ 0 \ + * + * No description available in RFC + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_TRACENEWTYPE 208 + + +/*! \brief 209 Class \ \ + * + * No description available in RFC + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_TRACECLASS 209 + + +/*! \brief 261 File \ \ + * + * No description available in RFC + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_TRACELOG 261 + + +/*! \brief 262 \ \ :End of TRACE + * + * The RPL_TRACE* are all returned by the server in response to the TRACE message. How many are returned is dependent on the TRACE message and whether it was sent by an operator or not. There is no predefined order for which occurs first. Replies RPL_TRACEUNKNOWN, RPL_TRACECONNECTING and RPL_TRACEHANDSHAKE are all used for connections which have not been fully established and are either unknown, still attempting to connect or in the process of completing the 'server handshake'. RPL_TRACELINK is sent by any server which handles a TRACE message and has to pass it on to another server. The list of RPL_TRACELINKs sent in response to a TRACE command traversing the IRC network should reflect the actual connectivity of the servers themselves along that path. RPL_TRACENEWTYPE is to be used for any connection which does not fit in the other categories but is being displayed anyway. RPL_TRACEEND is sent to indicate the end of the list. + * + * \ingroup rfcnumbers + */ +#define LIBIRC_RFC_RPL_TRACEEND 262 + + +/*! \brief 211 \ \ \ + \ \ + \ \