pax_global_header00006660000000000000000000000064117514275340014523gustar00rootroot0000000000000052 comment=bd6667183a715ebc68e529bdafb43ea8418b8000 ekg2-0.4~pre+20120506.1/000077500000000000000000000000001175142753400141555ustar00rootroot00000000000000ekg2-0.4~pre+20120506.1/.gitignore000066400000000000000000000006051175142753400161460ustar00rootroot00000000000000*~ *.a *.i *.la *.lo *.o *.s *.so ChangeLog Makefile.bak Makefile Makefile.in README.new TAGS aclocal.m4 autom4te.cache compile config.h config.h.in config.log config.status configure libtool /build-aux/ plugins/perl/common/blib/ plugins/perl/common/pm_to_blib plugins/perl/irc/blib/ plugins/perl/irc/pm_to_blib .deps .dirstamp .libs git_version.h stamp-h1 *.bz2 *.gz *.swp ekg/ekg2 /tags ekg2-0.4~pre+20120506.1/AUTHORS000066400000000000000000000020231175142753400152220ustar00rootroot00000000000000Authors of EKG2: Michał Górny Wojtek Kaniewski Leszek Krupiński Adam Kuczyński Piotr Kupisiewicz Adam Mikuta Wiesław Ochmiński Maciej Pietrzak Michał Spadliński Tomasz Torcz Jakub Zawadzki No questions about this software should ever be sent to the authors. The questions should be sent to the mailing list ekg2-users@lists.ziew.org Special thanks goes to: Grzegorz Moszumański - webmaster Maciej Kupisiewicz - webmaster Marek Marczykowski - jabber coder. Mateusz Samonek - translator Instytut Fizyki Doświadczalnej Uniwersytetu Wrocławskiego TESTERS/BUG REPORTERS/IDEA GIVERS/and other stuff: Daniel Dettlaff Michał Jęczalik Jr Kacper Kwapisz Sebastan Szary Igor Truszkowski Kamil Winczek THX GUYS. // XXX, keep it synced with authors.php ekg2-0.4~pre+20120506.1/COPYING000066400000000000000000000431261175142753400152160ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. ekg2-0.4~pre+20120506.1/INSTALL000066400000000000000000000013321175142753400152050ustar00rootroot00000000000000 EKG2 uses the autotools package for building. If you are checking out the source code directly from GIT, you probably need to call: $ ./autogen.sh [configure options] before invoking make to perform the actual build, or: $ NOCONFIGURE=1 ./autogen.sh if you don't want autogen to call configure after creating it. If you got the source code from a snapshot, this had probably already been done for you, and you only need to call: $ ./configure [configure options] $ make # make install To see all available options for configure (or autogen.sh), please use '--help'. If compilation fails on one of the plugins, you can append '-k' flag to make, so it'll ignore the failures (but please report the problem to the authors). ekg2-0.4~pre+20120506.1/Makefile.am000066400000000000000000000420131175142753400162110ustar00rootroot00000000000000SUBDIRS = po ACLOCAL_AMFLAGS = -I m4 # XXX: we should check for -Wall support? AM_CFLAGS = -Wall $(C_STRICT_ALIASING) plugindir = $(libdir)/ekg2/plugins scriptdir = $(pkgdatadir)/scripts themedir = $(pkgdatadir)/themes bin_PROGRAMS = ekg2 noinst_HEADERS = \ ekg2.h \ ekg2-config.h \ ekg/abort.h \ ekg/bindings.h \ ekg/commands.h \ ekg/completion.h \ ekg/connections.h \ ekg/configfile.h \ ekg/debug.h \ ekg/dynstuff.h \ ekg/dynstuff_inline.h \ ekg/emoticons.h \ ekg/events.h \ ekg/internal.h \ ekg/log.h \ ekg/metacontacts.h \ ekg/msgqueue.h \ ekg/net.h \ ekg/objects.h \ ekg/plugins.h \ ekg/protocol.h \ ekg/queries.h \ ekg/recode.h \ ekg/scripts.h \ ekg/sessions.h \ ekg/sources.h \ ekg/srv.h \ ekg/stuff.h \ ekg/themes.h \ ekg/userlist.h \ ekg/vars.h \ ekg/win32.h \ ekg/windows.h \ ekg/xmalloc.h ekg2_SOURCES = \ gettext.h \ $(noinst_HEADERS) \ ekg/abort.c \ ekg/bindings.c \ ekg/commands.c \ ekg/completion.c \ ekg/configfile.c \ ekg/connections.c \ ekg/dynstuff.c \ ekg/ekg.c \ ekg/emoticons.c \ ekg/events.c \ ekg/legacyconfig.c \ ekg/log.c \ ekg/metacontacts.c \ ekg/msgqueue.c \ ekg/net.c \ ekg/plugins.c \ ekg/protocol.c \ ekg/queries.c \ ekg/recode.c \ ekg/scripts.c \ ekg/sessions.c \ ekg/sources.c \ ekg/srv.c \ ekg/stuff.c \ ekg/themes.c \ ekg/userlist.c \ ekg/vars.c \ ekg/win32.c \ ekg/windows.c \ ekg/xmalloc.c ekg2_LDADD = $(EKG_LIBS) ekg2_CPPFLAGS = $(AM_CPPFLAGS) $(EKG_CPPFLAGS) if STATIC_LIBS ekg2_LDFLAGS = -static ekg2_LDADD += $(plugin_LTLIBRARIES) endif AM_CPPFLAGS = -I$(top_builddir)/plugins -I$(top_builddir) -I$(top_srcdir) \ -DSYSCONFDIR=\"$(sysconfdir)\" -DDATADIR=\"$(pkgdatadir)\" \ -DPLUGINDIR=\"$(plugindir)\" -DLOCALEDIR=\"$(localedir)\" BUILT_SOURCES = MOSTLYCLEANFILES = ekg2.h.gch plugins/ekg2.h.gch if ENABLE_GCH BUILT_SOURCES += ekg2.h.gch plugins/ekg2.h.gch ekg2.h.gch: $(noinst_HEADERS) $(AM_V_CC)$(COMPILE) $(EKG_CPPFLAGS) -o $@ $< || touch $@ plugins/ekg2.h.gch: $(noinst_HEADERS) $(MKDIR_P) plugins $(AM_V_CC)$(COMPILE) $(EKG_CPPFLAGS) -fPIC -DPIC -o $@ $< || touch $@ endif TESTS = check_potfiles check_SCRIPTS = check_potfiles check_ekg2 dist_pkgdata_DATA = \ docs/commands-pl.txt \ docs/session-en.txt \ docs/session-pl.txt \ docs/vars-en.txt \ docs/vars-pl.txt dist_noinst_DATA = \ docs/events.txt \ docs/mouse.txt \ docs/Perl-API-en.txt \ docs/Perl-API-pl.txt \ docs/README \ docs/sim.txt \ docs/themes-en.txt \ docs/themes.txt \ docs/ui-ncurses-en.txt \ docs/ui-ncurses.txt \ docs/voip.txt dist_theme_DATA = \ contrib/themes/blue_sea_dragon.theme \ contrib/themes/dmilith.theme \ contrib/themes/irc-irssi.theme \ contrib/themes/peres.theme \ contrib/themes/wiechu.theme dist_script_DATA = plugin_LTLIBRARIES = EXTRA_DIST = if ENABLE_AUTORESPONDER plugin_LTLIBRARIES += plugins/autoresponder/autoresponder.la plugins_autoresponder_autoresponder_la_SOURCES = \ $(noinst_HEADERS) \ plugins/autoresponder/autoresponder.c dist_autoresponder_DATA = \ plugins/autoresponder/vars-en.txt \ plugins/autoresponder/vars-pl.txt endif if SHARED_LIBS TESTS += check_ekg2 check_LTLIBRARIES = plugins/check/check.la plugins_check_check_la_SOURCES = \ $(noinst_HEADERS) \ plugins/check/check.c \ plugins/check/recode.c \ plugins/check/static-aborts.c plugins_check_check_la_LDFLAGS = -module -avoid-version -shared -rpath $(abs_top_builddir)/plugins/check plugins_check_check_la_CPPFLAGS = $(AM_CPPFLAGS) $(EKG_CPPFLAGS) endif if ENABLE_GG plugin_LTLIBRARIES += plugins/gg/gg.la plugins_gg_gg_la_SOURCES = \ $(noinst_HEADERS) \ plugins/gg/commands.c \ plugins/gg/dcc.c \ plugins/gg/dcc.h \ plugins/gg/gg.c \ plugins/gg/gg.h \ plugins/gg/images.c \ plugins/gg/images.h \ plugins/gg/misc.c \ plugins/gg/misc.h \ plugins/gg/pubdir50.c \ plugins/gg/pubdir50.h \ plugins/gg/pubdir.c \ plugins/gg/pubdir.h \ plugins/gg/token.h dist_gg_DATA = \ plugins/gg/commands-en.txt \ plugins/gg/commands-pl.txt \ plugins/gg/session-en.txt \ plugins/gg/session-pl.txt \ plugins/gg/vars-en.txt \ plugins/gg/vars-pl.txt endif if ENABLE_GPG plugin_LTLIBRARIES += plugins/gpg/gpg.la plugins_gpg_gpg_la_SOURCES = \ $(noinst_HEADERS) \ plugins/gpg/gpg.c dist_gpg_DATA = \ plugins/gpg/commands-en.txt \ plugins/gpg/commands-pl.txt endif BASE_PNGS = \ $(top_srcdir)/plugins/gtk/iconssets/avail.png \ $(top_srcdir)/plugins/gtk/iconssets/away.png \ $(top_srcdir)/plugins/gtk/iconssets/dnd.png \ $(top_srcdir)/plugins/gtk/iconssets/ffc.png \ $(top_srcdir)/plugins/gtk/iconssets/icon_error.png \ $(top_srcdir)/plugins/gtk/iconssets/icon_unknown.png \ $(top_srcdir)/plugins/gtk/iconssets/invisible.png \ $(top_srcdir)/plugins/gtk/iconssets/notavail.png \ $(top_srcdir)/plugins/gtk/iconssets/xa.png GG_PNGS = \ $(top_srcdir)/plugins/gtk/iconssets/gg-avail.png \ $(top_srcdir)/plugins/gtk/iconssets/gg-away.png \ $(top_srcdir)/plugins/gtk/iconssets/gg-invisible.png \ $(top_srcdir)/plugins/gtk/iconssets/gg-notavail.png ICQ_PNGS = \ $(top_srcdir)/plugins/gtk/iconssets/icq-avail.png \ $(top_srcdir)/plugins/gtk/iconssets/icq-away.png \ $(top_srcdir)/plugins/gtk/iconssets/icq-dnd.png \ $(top_srcdir)/plugins/gtk/iconssets/icq-ffc.png \ $(top_srcdir)/plugins/gtk/iconssets/icq-invisible.png \ $(top_srcdir)/plugins/gtk/iconssets/icq-notavail.png \ $(top_srcdir)/plugins/gtk/iconssets/icq-unknown.png \ $(top_srcdir)/plugins/gtk/iconssets/icq-xa.png EXTRA_DIST += $(BASE_PNGS) $(GG_PNGS) $(ICQ_PNGS) if ENABLE_GTK plugin_LTLIBRARIES += plugins/gtk/gtk.la GTK_INLINE_PNGS = \ inline_pngs.h \ inline_pngs_gg.h \ inline_pngs_icq.h plugins_gtk_gtk_la_SOURCES = \ $(noinst_HEADERS) \ plugins/gtk/bindings.c \ plugins/gtk/bindings.h \ plugins/gtk/chanview.c \ plugins/gtk/chanview.h \ plugins/gtk/gtkutil.c \ plugins/gtk/gtkutil.h \ plugins/gtk/main.c \ plugins/gtk/maingui.c \ plugins/gtk/maingui.h \ plugins/gtk/main.h \ plugins/gtk/menu.c \ plugins/gtk/menu.h \ plugins/gtk/palette.c \ plugins/gtk/palette.h \ plugins/gtk/userlistgui.c \ plugins/gtk/userlistgui.h \ plugins/gtk/xtext.c \ plugins/gtk/xtext.h \ plugins/gtk/chanview-tabs.inc \ plugins/gtk/chanview-tree.inc BUILT_SOURCES += \ $(GTK_INLINE_PNGS) MOSTLYCLEANFILES += \ $(GTK_INLINE_PNGS) inline_pngs.h: $(BASE_PNGS) $(AM_V_GEN)$(GDK_PIXBUF_CSOURCE) --raw --build-list \ avail $(top_srcdir)/plugins/gtk/iconssets/avail.png \ away $(top_srcdir)/plugins/gtk/iconssets/away.png \ dnd $(top_srcdir)/plugins/gtk/iconssets/dnd.png \ ffc $(top_srcdir)/plugins/gtk/iconssets/ffc.png \ icon_error $(top_srcdir)/plugins/gtk/iconssets/icon_error.png \ icon_unknown $(top_srcdir)/plugins/gtk/iconssets/icon_unknown.png \ invisible $(top_srcdir)/plugins/gtk/iconssets/invisible.png \ notavail $(top_srcdir)/plugins/gtk/iconssets/notavail.png \ xa $(top_srcdir)/plugins/gtk/iconssets/xa.png \ > $@ inline_pngs_gg.h: $(BASE_PNGS) $(AM_V_GEN)$(GDK_PIXBUF_CSOURCE) --raw --build-list \ gg_avail $(top_srcdir)/plugins/gtk/iconssets/gg-avail.png \ gg_away $(top_srcdir)/plugins/gtk/iconssets/gg-away.png \ gg_invisible $(top_srcdir)/plugins/gtk/iconssets/gg-invisible.png \ gg_notavail $(top_srcdir)/plugins/gtk/iconssets/gg-notavail.png \ > $@ inline_pngs_icq.h: $(ICQ_PNGS) $(AM_V_GEN)$(GDK_PIXBUF_CSOURCE) --raw --build-list \ icq_ffc $(top_srcdir)/plugins/gtk/iconssets/icq-ffc.png \ icq_avail $(top_srcdir)/plugins/gtk/iconssets/icq-avail.png \ icq_away $(top_srcdir)/plugins/gtk/iconssets/icq-away.png \ icq_dnd $(top_srcdir)/plugins/gtk/iconssets/icq-dnd.png \ icq_xa $(top_srcdir)/plugins/gtk/iconssets/icq-xa.png \ icq_invisible $(top_srcdir)/plugins/gtk/iconssets/icq-invisible.png \ icq_notavail $(top_srcdir)/plugins/gtk/iconssets/icq-notavail.png \ icq_unknown $(top_srcdir)/plugins/gtk/iconssets/icq-unknown.png \ > $@ endif if ENABLE_ICQ plugin_LTLIBRARIES += plugins/icq/icq.la plugins_icq_icq_la_SOURCES = \ $(noinst_HEADERS) \ plugins/icq/digest.c \ plugins/icq/icq.c \ plugins/icq/icq_caps.c \ plugins/icq/icq_caps.h \ plugins/icq/icq_const.h \ plugins/icq/icq_debug.inc \ plugins/icq/icq_fieldnames.inc \ plugins/icq/icq_flap_handlers.c \ plugins/icq/icq_flap_handlers.h \ plugins/icq/icq.h \ plugins/icq/icq_snac_handlers_01service.c \ plugins/icq/icq_snac_handlers_02location.c \ plugins/icq/icq_snac_handlers_03buddy.c \ plugins/icq/icq_snac_handlers_04message.c \ plugins/icq/icq_snac_handlers_09bos.c \ plugins/icq/icq_snac_handlers_0Alookup.c \ plugins/icq/icq_snac_handlers_0Bstatus.c \ plugins/icq/icq_snac_handlers_13userlist.c \ plugins/icq/icq_snac_handlers_15extension.c \ plugins/icq/icq_snac_handlers_17sigon.c \ plugins/icq/icq_snac_handlers.c \ plugins/icq/icq_snac_handlers.h \ plugins/icq/misc.c \ plugins/icq/misc.h \ plugins/icq/miscicq.h endif if ENABLE_IRC plugin_LTLIBRARIES += plugins/irc/irc.la plugins_irc_irc_la_SOURCES = \ $(noinst_HEADERS) \ plugins/irc/autoacts.c \ plugins/irc/autoacts.h \ plugins/irc/input.c \ plugins/irc/input.h \ plugins/irc/irc.c \ plugins/irc/irc.h \ plugins/irc/IRCVERSION.h \ plugins/irc/misc.c \ plugins/irc/misc.h \ plugins/irc/people.c \ plugins/irc/people.h dist_irc_DATA = \ plugins/irc/commands-pl.txt \ plugins/irc/session-pl.txt endif if ENABLE_JABBER plugin_LTLIBRARIES += plugins/jabber/jabber.la plugins_jabber_jabber_la_SOURCES = \ $(noinst_HEADERS) \ plugins/jabber/commands.c \ plugins/jabber/digest.c \ plugins/jabber/jabber.c \ plugins/jabber/jabber_dcc.c \ plugins/jabber/jabber_dcc.h \ plugins/jabber/jabber.h \ plugins/jabber/jabber_handlers.c \ plugins/jabber/jabber-ssl.h \ plugins/jabber/misc.c \ plugins/jabber/xmlnode.c \ plugins/jabber/jabber_handlers_iq_error.inc \ plugins/jabber/jabber_handlers_iq_get.inc \ plugins/jabber/jabber_handlers_iq_result.inc \ plugins/jabber/jabber_handlers_tlen.inc dist_jabber_DATA = \ plugins/jabber/commands-en.txt \ plugins/jabber/commands-pl.txt \ plugins/jabber/session-en.txt \ plugins/jabber/session-pl.txt endif if ENABLE_JOGGER plugin_LTLIBRARIES += plugins/jogger/jogger.la plugins_jogger_jogger_la_SOURCES = \ $(noinst_HEADERS) \ plugins/jogger/drafts.c \ plugins/jogger/jogger.c \ plugins/jogger/messages.c endif if ENABLE_LOGS plugin_LTLIBRARIES += plugins/logs/logs.la plugins_logs_logs_la_SOURCES = \ $(noinst_HEADERS) \ plugins/logs/main.c \ plugins/logs/main.h dist_logs_DATA = \ plugins/logs/vars-pl.txt endif if ENABLE_LOGSQLITE plugin_LTLIBRARIES += plugins/logsqlite/logsqlite.la plugins_logsqlite_logsqlite_la_SOURCES = \ $(noinst_HEADERS) \ plugins/logsqlite/logsqlite.c \ plugins/logsqlite/logsqlite.h dist_logsqlite_DATA = \ plugins/logsqlite/commands-en.txt \ plugins/logsqlite/commands-pl.txt \ plugins/logsqlite/vars-en.txt \ plugins/logsqlite/vars-pl.txt endif if ENABLE_MAIL plugin_LTLIBRARIES += plugins/mail/mail.la plugins_mail_mail_la_SOURCES = \ $(noinst_HEADERS) \ plugins/mail/main.c dist_mail_DATA = \ plugins/mail/vars-pl.txt endif if ENABLE_NCURSES plugin_LTLIBRARIES += plugins/ncurses/ncurses.la plugins_ncurses_ncurses_la_SOURCES = \ $(noinst_HEADERS) \ plugins/ncurses/backlog.c \ plugins/ncurses/backlog.h \ plugins/ncurses/bindings.c \ plugins/ncurses/bindings.h \ plugins/ncurses/contacts.c \ plugins/ncurses/contacts.h \ plugins/ncurses/ecurses.h \ plugins/ncurses/input.c \ plugins/ncurses/input.h \ plugins/ncurses/lastlog.c \ plugins/ncurses/lastlog.h \ plugins/ncurses/main.c \ plugins/ncurses/mouse.c \ plugins/ncurses/mouse.h \ plugins/ncurses/nc-strings.c \ plugins/ncurses/nc-strings.h \ plugins/ncurses/nc-stuff.c \ plugins/ncurses/nc-stuff.h \ plugins/ncurses/notify.c \ plugins/ncurses/notify.h \ plugins/ncurses/spell.c \ plugins/ncurses/spell.h \ plugins/ncurses/statusbar.c \ plugins/ncurses/statusbar.h dist_ncurses_DATA = \ plugins/ncurses/commands-pl.txt \ plugins/ncurses/vars-en.txt \ plugins/ncurses/vars-pl.txt endif EXTRA_DIST += \ plugins/perl/common/Command.xs \ plugins/perl/common/Ekg2.pm \ plugins/perl/common/Ekg2.xs \ plugins/perl/common/Makefile.PL \ plugins/perl/common/module.h \ plugins/perl/common/Plugin.xs \ plugins/perl/common/Session.xs \ plugins/perl/common/Timer.xs \ plugins/perl/common/typemap \ plugins/perl/common/Userlist.xs \ plugins/perl/common/Variable.xs \ plugins/perl/common/Watch.xs \ plugins/perl/common/Window.xs \ plugins/perl/irc/Channel.xs \ plugins/perl/irc/Irc.pm \ plugins/perl/irc/Irc.xs \ plugins/perl/irc/Makefile.PL \ plugins/perl/irc/module.h \ plugins/perl/irc/Server.xs \ plugins/perl/irc/typemap \ plugins/perl/irc/User.xs if ENABLE_PERL plugin_LTLIBRARIES += plugins/perl/perl.la plugins_perl_perl_la_SOURCES = \ $(noinst_HEADERS) \ plugins/perl/perl_bless.c \ plugins/perl/perl_bless.h \ plugins/perl/perl_core.c \ plugins/perl/perl_core.h \ plugins/perl/perl_ekg.c \ plugins/perl/perl_ekg.h dist_script_DATA += \ contrib/perl/audioscrobbler_bot.pl \ contrib/perl/autoop.pl \ contrib/perl/cycki.pl \ contrib/perl/dns.pl \ contrib/perl/dupa.pl \ contrib/perl/ggbe.pl \ contrib/perl/irc.pl \ contrib/perl/sample.pl \ contrib/perl/slownik.pl \ contrib/perl/xmms.pl if ENABLE_PERL_MODULES $(top_srcdir)/plugins/perl/common/Makefile: $(top_srcdir)/plugins/perl/common/Makefile.PL $(AM_V_GEN)cd $(top_srcdir)/plugins/perl/common && $(PERL) Makefile.PL INC="-I$(abs_top_builddir) -I../../.." $(PERL_MODULE_BUILD_FLAGS) $(top_srcdir)/plugins/perl/irc/Makefile: $(top_srcdir)/plugins/perl/irc/Makefile.PL $(AM_V_GEN)cd $(top_srcdir)/plugins/perl/irc && $(PERL) Makefile.PL INC="-I$(abs_top_builddir) -I../../.." $(PERL_MODULE_BUILD_FLAGS) perl-build-common: $(top_srcdir)/plugins/perl/common/Makefile cd $(top_srcdir)/plugins/perl/common && $(MAKE) CC="$(CC)" CCFLAGS="$(plugins_perl_perl_la_CPPFLAGS) $(CFLAGS)" perl-build-irc: $(top_srcdir)/plugins/perl/irc/Makefile cd $(top_srcdir)/plugins/perl/irc && $(MAKE) CC="$(CC)" CCFLAGS="$(plugins_perl_perl_la_CPPFLAGS) $(CFLAGS)" perl-clean-common: $(top_srcdir)/plugins/perl/common cd $(top_srcdir)/plugins/perl/common && test ! -f Makefile || $(MAKE) clean rm -f $(top_srcdir)/plugins/perl/common/Makefile.old perl-clean-irc: $(top_srcdir)/plugins/perl/irc cd $(top_srcdir)/plugins/perl/irc && test ! -f Makefile || $(MAKE) clean rm -f $(top_srcdir)/plugins/perl/irc/Makefile.old perl-distclean: -rm -f $(top_srcdir)/plugins/perl/common/Makefile $(top_srcdir)/plugins/perl/irc/Makefile perl-install-common: $(top_srcdir)/plugins/perl/common/Makefile cd $(top_srcdir)/plugins/perl/common && $(MAKE) install DESTDIR="$(DESTDIR)" perl-install-irc: $(top_srcdir)/plugins/perl/irc/Makefile cd $(top_srcdir)/plugins/perl/irc && $(MAKE) install DESTDIR="$(DESTDIR)" all-local: perl-build-common perl-build-irc clean-local: perl-clean-common perl-clean-irc distclean-local: perl-distclean install-exec-local: perl-install-common perl-install-irc endif endif if ENABLE_NNTP plugin_LTLIBRARIES += plugins/nntp/nntp.la plugins_nntp_nntp_la_SOURCES = \ $(noinst_HEADERS) \ plugins/nntp/nntp.c endif if ENABLE_POLCHAT plugin_LTLIBRARIES += plugins/polchat/polchat.la plugins_polchat_polchat_la_SOURCES = \ $(noinst_HEADERS) \ plugins/polchat/polchat.c \ plugins/polchat/polchat_handlers.inc endif if ENABLE_PYTHON plugin_LTLIBRARIES += plugins/python/python.la plugins_python_python_la_SOURCES = \ $(noinst_HEADERS) \ plugins/python/python.c \ plugins/python/python-config.c \ plugins/python/python-config.h \ plugins/python/python-ekg.c \ plugins/python/python-ekg.h \ plugins/python/python.h \ plugins/python/python-plugin.c \ plugins/python/python-plugin.h \ plugins/python/python-session.c \ plugins/python/python-session.h \ plugins/python/python-user.c \ plugins/python/python-user.h \ plugins/python/python-window.c \ plugins/python/python-window.h dist_python_DATA = \ plugins/python/commands-en.txt \ plugins/python/commands-pl.txt dist_script_DATA += \ contrib/python/notify-bubble.py \ contrib/python/sample.py endif if ENABLE_RC plugin_LTLIBRARIES += plugins/rc/rc.la plugins_rc_rc_la_SOURCES = \ $(noinst_HEADERS) \ plugins/rc/rc.h \ plugins/rc/inputs.c \ plugins/rc/main.c dist_rc_DATA = \ plugins/rc/vars-pl.txt endif if ENABLE_READLINE plugin_LTLIBRARIES += plugins/readline/readline.la plugins_readline_readline_la_SOURCES = \ $(noinst_HEADERS) \ plugins/readline/completion.c \ plugins/readline/main.c \ plugins/readline/ui-readline.c \ plugins/readline/ui-readline.h dist_readline_DATA = \ plugins/readline/vars-en.txt \ plugins/readline/vars-pl.txt endif if ENABLE_RIVCHAT plugin_LTLIBRARIES += plugins/rivchat/rivchat.la plugins_rivchat_rivchat_la_SOURCES = \ $(noinst_HEADERS) \ plugins/rivchat/misc.c \ plugins/rivchat/rivchat.c \ plugins/rivchat/rivchat.h endif if ENABLE_RSS plugin_LTLIBRARIES += plugins/rss/rss.la plugins_rss_rss_la_SOURCES = \ $(noinst_HEADERS) \ plugins/rss/rss.c endif if ENABLE_SIM plugin_LTLIBRARIES += plugins/sim/sim.la plugins_sim_sim_la_SOURCES = \ $(noinst_HEADERS) \ plugins/sim/main.c \ plugins/sim/simlite.c \ plugins/sim/simlite.h dist_sim_DATA = \ plugins/sim/commands-pl.txt \ plugins/sim/vars-pl.txt endif if ENABLE_SMS plugin_LTLIBRARIES += plugins/sms/sms.la plugins_sms_sms_la_SOURCES = \ $(noinst_HEADERS) \ plugins/sms/sms.c dist_sms_DATA = \ plugins/sms/commands-pl.txt \ plugins/sms/vars-pl.txt endif ekg2-0.4~pre+20120506.1/README.md000066400000000000000000000023621175142753400154370ustar00rootroot00000000000000EKG2 ==== Multiplatform, multiprotocol, plugin-based instant messenger with (GTK2 GUI || console UI)! EKG2 is an OpenSource IM application for Unix systems (Linux, *BSD, SunOS, Solaris, MacOS, X, BeOS) available under the terms of GPL. EKG2 is plugin-based, and because of this it can support many different protocols. It can also support different GUIs! Below is a list of available plugins. Protocol plugins: * Jabber - (XMPP, gtalk and Tlen.pl support), * GG (gadu-gadu) - (using libgadu, the most popular polish IM protocol), * IRC - (mostly IRCnet oriented), * ICQ * polchat * rivchat GUI plugins: * GTK2 - under development, * ncurses - primary ekg2 ui, console * readline Logging facility: * logs - multi plugin that allows logging in irssi like format, xml, simple and raw, * logsqlite - logging into a sqlite db, Script languages bindings: Python, Perl Other superb plugins: * autoresponder - a simple captcha for IM * sim (using openssl) & gpg - these plugins allow encryption within supported protocols, * jogger - allows manipulation of JoggerBot via ekg2, * mail - checking local maildir/mailbox for new messages, * nntp - read usenet news * rss - read RSS/Atom feeds * sms - sms sending, There are also a few other plugins in development. ekg2-0.4~pre+20120506.1/autogen.sh000077500000000000000000000021201175142753400161510ustar00rootroot00000000000000#!/bin/sh # Convenience wrapper around autoreconf and optionally configure. # # Copyright 2003 Wojtek Kaniewski # 2011 Marcin Owsiany # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License Version 2 as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . set -e : ${AUTORECONF:=autoreconf} if test "$*"; then ARGS="$*" else if test -f config.log ; then ARGS=`grep '^ \$ \./configure ' config.log | sed 's/^ \$ \.\/configure //' 2> /dev/null` fi fi echo "Running ${AUTORECONF}..." $AUTORECONF --install --verbose test x$NOCONFIGURE = x && echo "Running ./configure $ARGS" && ./configure $ARGS ekg2-0.4~pre+20120506.1/check_ekg2000077500000000000000000000000431175142753400160650ustar00rootroot00000000000000#!/bin/sh ./ekg2 -F check -n quit ekg2-0.4~pre+20120506.1/check_potfiles000077500000000000000000000030301175142753400170610ustar00rootroot00000000000000#!/bin/sh # Check that po/POTFILES.in is up to date - so we do not forget about any file # holding a translatable string. # # Copyright 2004 Piotr Kupisiewicz # 2007 Michał Górny # 2005,2011 Marcin Owsiany # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License Version 2 as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . : ${XGETTEXT=xgettext} # Exclude contrib subdir - it holds a benchmark program which has a copy of # every theme string, causing duplicates. # TODO: we try to obey $srcdir but it will probably fail on anything other than # ".", as the paths that xgettext will put into the pot file will most likely # differ from the ones listed in po/POTFILES.in. However the logic necessary to # strip the right prefix in all possible corner case makes it too much to worry # about in advance. $XGETTEXT --keyword=_ --keyword=N_ --output=- `find $srcdir -name '*.[ch]'` | \ sed -ne '/^#:/{s/#://; s/:[0-9]*/\ /g; s/ //g; p;}' | \ grep -v '^$' | sort | uniq | grep -v '^contrib' | \ diff -u po/POTFILES.in - ekg2-0.4~pre+20120506.1/configure.ac000066400000000000000000000222501175142753400164440ustar00rootroot00000000000000AC_PREREQ([2.60]) AC_INIT([ekg2], [git]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE([1.6 foreign dist-bzip2 nostdinc -Wall]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES]) AC_PROG_CC_C99 AM_PROG_CC_C_O AC_USE_SYSTEM_EXTENSIONS AC_SYS_LARGEFILE AC_PROG_C_NO_STRICT_ALIASING dnl XXX: docs say we should use '-no-undefined' along with 'win32-dll' LT_INIT([disable-static win32-dll]) AC_EKG2_WITH([glib], [ AS_IF([test "x$enable_shared" = "xyes"], [ gmod_dep="gmodule-2.0" ], [ gmod_dep= ]) PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.24 gio-2.0 gobject-2.0 $gmod_dep],, [ AS_CASE([$with_glib], [yes|maybe], [with_glib=/usr]) GLIB_LIBS="-lglib-2.0 -lgobject-2.0 -lgio-2.0" AS_IF([test "x$enable_shared" = "xyes"], [ GLIB_LIBS="$GLIB_LIBS -lgmodule-2.0 -Wl,--export-dynamic" ]) GLIB_CFLAGS="-I$with_glib/include/glib-2.0 -I$with_glib/EKG2_LIBDIRNAME/glib-2.0/include" ]) AC_EKG2_CHECK_FLAGEXPORTED_LIB([GLIB],, [g_charset_converter_new], [glib.h],, [ AC_MSG_ERROR([glib2 not found (neither in standard pkgconfig paths nor in $with_glib)]) ]) AS_UNSET([gmod_dep]) ], [ AC_MSG_ERROR([glib dependency is obligatory, bailing due to --without-glib]) ]) AC_CHECK_FUNCS([flock getaddrinfo inet_ntop inet_pton inet_aton]) AC_SEARCH_LIBS([socket], [socket]) AC_CHECK_FUNC([inet_addr],, [AC_CHECK_LIB(bind, __inet_addr, LIBS="$LIBS -lbind")]) AC_CHECK_LIB([wsock32], [main]) AC_CHECK_HEADERS([sys/socket.h ws2tcpip.h]) AC_CHECK_TYPES([socklen_t],,,[ #ifdef HAVE_WS2TCPIP_H # include #endif #include #ifdef HAVE_SYS_SOCKET_H # include #endif ]) AC_CHECK_MEMBERS([struct kinfo_proc.ki_size],,,[ #include #include ]) dnl compats -- XXX: get rid of them AC_CHECK_FUNCS([scandir]) dnl Libraries specific to the ekg2 binary AC_SEARCH_LIBS([gethostbyname], [nsl]) AC_SEARCH_LIBS([kvm_openfiles], [kvm]) AC_SEARCH_LIBS([sched_yield], [rt]) dnl resolver AC_CHECK_FUNC([dn_expand], [], [ AC_CHECK_HEADERS([resolv.h], [ saved_LIBS="$LIBS" RESOLV_LIBS="-lresolv" LIBS="$LIBS $RESOLV_LIBS" AC_MSG_CHECKING([[if libresolv is usable]]) dnl we cannot just use AC_CHECK_LIB, because dn_expand is dnl implemented as a macro on certain platforms, and will not dnl be resolved by linker without including necessary headers AC_LINK_IFELSE([ AC_LANG_PROGRAM([[#include ]], [[dn_expand(0,0,0,0,0);]]) ],[ AC_MSG_RESULT([yes]) AC_DEFINE(HAVE_LIBRESOLV, 1, [define if you have libresolv]) ],[ AC_MSG_RESULT([no]) AC_MSG_WARN([[resolver library unusable, SRV record resolution - e.g. for jabber plugin - will not work]]) LIBS="$saved_LIBS" ] ) ],[ AC_MSG_WARN([[resolver library not found, SRV record resolution - e.g. for jabber plugin - will not work]]) ]) ]) AC_EKG2_WITH([gnutls], [ AC_EKG2_CHECK_PKGCONFIG_LIB([gnutls], [gnutls], [gnutls_init], [gnutls/gnutls.h]) ]) EKG_LIBS="$LIBS" AC_SUBST([EKG_LIBS]) dnl We need not to link plugins against the libraries the binary is already linked to. LIBS= AM_GNU_GETTEXT([external]) AM_GNU_GETTEXT_VERSION([0.14.3]) dnl gettext still uses it even if other autotools don't AC_SUBST([MKINSTALLDIRS], ['$(top_srcdir)/build-aux/mkinstalldirs']) dnl plugins AC_EKG2_PLUGIN([autoresponder]) AC_EKG2_PLUGIN([gg], [ AC_EKG2_WITH([libgadu], [ AC_EKG2_CHECK_PKGCONFIG_LIB([libgadu], [gadu], [gg_logoff], [libgadu.h],, [EKG2_FAILED_TEST]) ]) ], [ AC_CHECK_FUNCS([gg_remind_passwd3 gg_change_passwd4 mkstemp]) dnl Reason to check for gif or jpeg? They seem to be useless nowadays. ]) AC_EKG2_PLUGIN([gpg], [ AC_EKG2_WITH([gpgme], [ AM_PATH_GPGME([1.0.0], [ AC_EKG2_CHECK_FLAGEXPORTED_LIB([GPGME], [gpgme], [gpgme_new], [gpgme.h],, [EKG2_FAILED_TEST]) ], [EKG2_FAILED_TEST]) ],, [ AC_MSG_ERROR(['--with-gpgme= is uneffective, please use --with-gpgme-prefix instead']) ]) ]) AC_EKG2_PLUGIN([gtk], [ AC_EKG2_WITH([gtk], [ PKG_CHECK_MODULES([GTK], [gtk+-2.0],, [ AS_CASE([$with_gtk], [yes|maybe], [gtk_prefix=/usr], [gtk_prefix=$with_gtk]) GTK_LIBS="-lgtk-x11-2.0" GTK_CFLAGS="-I$gtk_prefix/include/gtk-2.0 -I$gtk_prefix/EKG2_LIBDIRNAME/gtk-2.0/include" AS_UNSET([gtk_prefix]) ]) AC_EKG2_CHECK_FLAGEXPORTED_LIB([GTK],, [gtk_init], [gtk/gtk.h], [ AC_PATH_PROG([GDK_PIXBUF_CSOURCE], [gdk-pixbuf-csource]) AS_IF([test x"$GDK_PIXBUF_CSOURCE" = x""], [EKG2_FAILED_TEST]) ], [EKG2_FAILED_TEST]) ]) ]) AC_EKG2_PLUGIN([icq]) AC_EKG2_PLUGIN([irc]) AC_EKG2_PLUGIN([jabber], [ AC_EKG2_WITH([expat], [ AC_EKG2_CHECK_LIB([expat], [XML_ParserCreate], [expat.h],, [EKG2_FAILED_TEST]) ]) ], [ AC_EKG2_WITH([zlib], [ AC_EKG2_CHECK_PKGCONFIG_LIB([zlib], [z], [gzopen], [zlib.h]) ]) ]) AC_EKG2_PLUGIN([jogger]) AC_EKG2_PLUGIN([logs],, [ AC_EKG2_WITH([zlib], [ AC_EKG2_CHECK_PKGCONFIG_LIB([zlib], [z], [gzopen], [zlib.h]) ]) ]) AC_EKG2_PLUGIN([logsqlite], [ try_sqlite=no AC_EKG2_WITH([sqlite3], [ AC_EKG2_CHECK_PKGCONFIG_LIB([sqlite3], [sqlite3], [sqlite3_open], [sqlite3.h],, [ try_sqlite=yes ]) ], [try_sqlite=yes]) AS_IF([test $try_sqlite = yes], [ AC_EKG2_WITH([sqlite], [ AC_EKG2_CHECK_PKGCONFIG_LIB([sqlite], [sqlite], [sqlite_open], [sqlite.h],, [ EKG2_FAILED_TEST ]) ], [EKG2_DISABLED_TEST]) ]) AS_UNSET([try_sqlite]) ]) AC_EKG2_PLUGIN([mail],, [ AC_EKG2_WITH([inotify], [ AC_CHECK_FUNCS([inotify_init], [ AC_CHECK_HEADERS([sys/inotify.h]) ]) ]) ]) AC_EKG2_PLUGIN([ncurses], [ AC_EKG2_WITH([ncurses], [ AC_SEARCH_LIBS([initscr], [ncursesw ncurses], [ nc_headers="ncurses/ncurses.h ncurses.h" found_any_header=no AC_CHECK_FUNCS([waddnwstr], [ AC_CHECK_HEADERS([ncursesw/ncurses.h ncurses/ncurses.h ncurses.h], [ found_any_header=yes break ]) ], [ AC_CHECK_HEADERS([ncurses/ncurses.h ncurses.h], [ found_any_header=yes break ]) ]) AS_IF([test $found_any_header = yes], [ AC_DEFINE([HAVE_NCURSES], [1], [define if you have ncurses]) ], [EKG2_FAILED_TEST]) AS_UNSET([found_any_header]) AS_UNSET([nc_headers]) ], [EKG2_FAILED_TEST]) ]) ], [ AC_CHECK_FUNCS([addwstr use_legacy_coding]) AC_EKG2_WITH([aspell], [ AC_EKG2_CHECK_LIB([aspell], [new_aspell_config], [aspell.h]) ]) AC_EKG2_WITH([gpm], [ AC_EKG2_CHECK_LIB([gpm], [Gpm_GetEvent], [gpm.h]) ]) ]) AC_EKG2_PLUGIN([nntp]) perl_module_build_flags= AC_EKG2_PLUGIN([perl], [ AC_EKG2_WITH([perl], [ AC_PATH_PROG([PERL], [perl]) AS_IF([test x"$PERL", != x""], [ PERL_CFLAGS=`perl -MExtUtils::Embed -e ccopts` PERL_LIBS=`perl -MExtUtils::Embed -e ldopts` AC_EKG2_CHECK_FLAGEXPORTED_LIB([PERL], [perl], [perl_alloc], [EXTERN.h],, [EKG2_FAILED_TEST]) ], [EKG2_FAILED_TEST]) ]) ], [ AC_ARG_ENABLE([perl-modules], AS_HELP_STRING([--disable-perl-modules], [disable building perl modules [default=on]]), , [ enable_perl_modules=yes ]) AC_ARG_WITH([perl-module-build-flags], AS_HELP_STRING([--with-perl-module-build-flags=...], [additional flags passed to Makefile.PL (e.g. INSTALLDIRS)]), [ perl_module_build_flags=$withval ]) ]) AC_SUBST([PERL_MODULE_BUILD_FLAGS], [$perl_module_build_flags]) AM_CONDITIONAL([ENABLE_PERL_MODULES], [test x"$enable_perl_modules" = x"yes"]) AC_EKG2_PLUGIN([polchat]) AC_EKG2_PLUGIN([python], [ AC_EKG2_WITH([python], [ AC_PATH_PROGS([PYTHON], [python2 python python2.7 python2.6 python2.5 python2.4]) AS_IF([test x"$PYTHON" != x""], [ pyver=`"$PYTHON" -c 'import sys; print "%d.%d" % sys.version_info[[0:2]]'` pyfound=no dnl python2.7 comes with a pkg-config file, older do not PKG_CHECK_MODULES([PYTHON], [python-$pyver], [ pyfound=yes ], [ AC_PATH_PROGS([PYCONF], [python$pyver-config python-config-$pyver]) AS_IF([test x"$PYCONF" != x""], [ PYTHON_CFLAGS="`$PYCONF --includes`" PYTHON_LIBS="`$PYCONF --ldflags`" pyfound=yes ]) ]) AS_IF([test $pyfound = yes], [ AC_EKG2_CHECK_FLAGEXPORTED_LIB([PYTHON],, [Py_Initialize], [Python.h],, [EKG2_FAILED_TEST]) ], [EKG2_FAILED_TEST]) AS_UNSET([pyfound]) AS_UNSET([pyver]) ], [EKG2_FAILED_TEST]) ]) ]) AC_EKG2_PLUGIN([rc]) AC_EKG2_PLUGIN([readline], [ AC_EKG2_WITH([readline], [ AC_EKG2_CHECK_LIB([readline], [readline], [readline/readline.h readline.h],, [EKG2_FAILED_TEST]) ]) ], [ AC_CHECK_FUNCS([rl_set_prompt rl_filename_completion_function rl_get_screen_size rl_set_key rl_bind_key_in_map]) ]) AC_EKG2_PLUGIN([rivchat]) AC_EKG2_PLUGIN([rss], [ AC_EKG2_WITH([expat], [ AC_EKG2_CHECK_LIB([expat], [XML_ParserCreate], [expat.h],, [EKG2_FAILED_TEST]) ]) ]) AC_EKG2_PLUGIN([sim], [ AC_EKG2_WITH([openssl], [ AC_EKG2_CHECK_PKGCONFIG_LIB([openssl], [ssl], [RSA_new], [openssl/ssl.h],,, [ LIBS="-lssl -lcrypto $LIBS" AC_DEFINE([HAVE_LIBSSL], [1], [define if you want to use openssl for ssl connections]) ]) ]) ]) AC_EKG2_PLUGIN([sms]) dnl CPPFLAGS were duplicated into whatever_CPPFLAGS dnl make the global ones exe-specific EKG_CPPFLAGS="$CPPFLAGS" AC_SUBST([EKG_CPPFLAGS]) CPPFLAGS= AC_CONFIG_COMMANDS_PRE([ AM_CONDITIONAL([ENABLE_GCH], [test "x$enable_dependency_tracking" = "xno"]) AS_IF([test "x$shrext_cmds" != "x"], [ AC_DEFINE_UNQUOTED([PLUGIN_SUFFIX], ["$shrext_cmds"], [set to plugin suffix if known]) ]) ]) AC_CONFIG_HEADERS([ekg2-config.h]) AC_CONFIG_FILES([Makefile po/Makefile.in]) AC_OUTPUT ekg2-0.4~pre+20120506.1/contrib/000077500000000000000000000000001175142753400156155ustar00rootroot00000000000000ekg2-0.4~pre+20120506.1/contrib/_ekg2000066400000000000000000000035321175142753400165320ustar00rootroot00000000000000#compdef ekg2 # # completion for zsh by Michal 'GiM' Spadlinski # local userprofiles userthemes userfrontends userprofiles=(`find ~/.ekg2/* -type d -exec test -f '{}/config' \; -exec basename {} \;`) userthemes=(`ls $(ekg2-config --data-dir)/themes `) userfrontends=(`find /usr/local/share/ekg2/plugins -type d \( -iname 'ncurses' -or -iname 'gtk' -or -iname 'readline' \) -exec basename {} \;`) _arguments -s \ '(-u --user)'{-u,--user}'+[uses given profile]:profile name:( $userprofiles )' \ '(-t --theme)'{-t,--theme}'+[loads theme from given file]:theme filename:( $userthemes )' \ '(-n --no-auto)'{-n,--no-auto}'[does not connect to server(s) automatically]' \ '(-m --no-mouse)'{-m,--no-mouse}'[does not load mouse support]' \ '(-N --no-global-config)'{-N,--no-global-config}'[ignores global configuration file]' \ '(-F --frontend)'{-F,--frontend}'+[uses given frontend (default ncurses)]:frontend name:( $userfrontends )' \ '(-a --away -b --back -i --invisible -d --dnd -f --free-for-chat -x --xa)'{-a,--away}'+[changes status to ``away``]' \ '(-a --away -b --back -i --invisible -d --dnd -f --free-for-chat -x --xa)'{-b,--back}'+[changes status to ``available``]' \ '(-a --away -b --back -i --invisible -d --dnd -f --free-for-chat -x --xa)'{-i,--invisible}'+[changes status to ``invisible``]' \ '(-a --away -b --back -i --invisible -d --dnd -f --free-for-chat -x --xa)'{-d,--dnd}'+[changes status to ``do not disturb``]' \ '(-a --away -b --back -i --invisible -d --dnd -f --free-for-chat -x --xa)'{-f,--free-for-chat}'+[changes status to ``free for chat``]' \ '(-a --away -b --back -i --invisible -d --dnd -f --free-for-chat -x --xa)'{-x,--xa}'+[changes status to ``very busy``]' \ '(-h --help)'{-h,--help}'[display this help and exit]' \ '(-v --version)'{-v,--version}'[output version information and exit]' ekg2-0.4~pre+20120506.1/contrib/amarok-testowy-skrypt.amarokscript.tar000066400000000000000000000240001175142753400253370ustar00rootroot00000000000000amarok-testowy-skrypt.sh0000755000175100017510000000301310660057261014051 0ustar gimgim#!/usr/bin/zsh # # Michal 'GiM' Spadlinski # Example amarok script, # the API of dbus plugin is a subject to change # so don't relay on it ;) while :; do read -A var case ${var[0]} in configure) dcop amarok playlist popupMessage "Sorry, no configuration at the moment ;)"; ;; engineStateChange:) case ${var[2]} in playing) artist=`dcop amarok player artist` title=`dcop amarok player title` #dbus-send --print-reply --dest=org.freedesktop.im /dupa/dupa org.freedesktop.im.ekg2.session.setStatus string:gg:numerek string:back string:" NP: $artist - $title " dbus-send --print-reply --dest=org.freedesktop.im /dupa/dupa org.freedesktop.im.setStatus string:back string:" now listening : $artist - $title " ;; paused) artist=`dcop amarok player artist` title=`dcop amarok player title` #dbus-send --print-reply --dest=org.freedesktop.im /dupa/dupa org.freedesktop.im.ekg2.session.setStatus string:gg:numerek string:back string:" NP: $artist - $title (paused)" dbus-send --print-reply --dest=org.freedesktop.im /dupa/dupa org.freedesktop.im.setStatus string:back string:" now listening : $artist - $title (paused) " ;; empty) #dbus-send --print-reply --dest=org.freedesktop.im /dupa/dupa org.freedesktop.im.ekg2.session.setStatus string:gg:numerek string:back string:" NP: muza stop" dbus-send --print-reply --dest=org.freedesktop.im /dupa/dupa org.freedesktop.im.setStatus string:back string:" now listening : muza nie gra stop " ;; esac ;; esac done README0000644000175100017510000000062610656646601010070 0ustar gimgimThis is how I use it: 1) launch dbus-launch 2) set DBUS_SESSION_BUS_ADDRESS and DBUS_SESSION_BUS_PID [I do this in one step with export `dbus-launch` :) ] 3) launch X 4) launch ekg2 with dbus plugin 5) launch amarok 6) add script (needed only once): tools -> Script Manager -> Install Script It may seem complicated, but it's quite simple when you know what you're doing ;) - Michal 'GiM' Spadlinski ekg2-0.4~pre+20120506.1/contrib/ekg_hash_benchmark.c000066400000000000000000000067731175142753400215610ustar00rootroot00000000000000#include #include #include #include #include typedef int hash_t; hash_t no_prompt_cache_hash = 0x139dcbd6; /* hash value of "no_promp_cache" 2261954 it's default one. i hope good one.. for 32 bit x86 sure. */ hash_t ekg_hash(const char *name); struct list { void *data; /*struct list *prev;*/ struct list *next; }; typedef struct list *list_t; int hashes[256]; void ekg_oom_handler() { printf("braklo pamieci\n"); exit(1); } void *xmalloc(size_t size) { void *tmp = malloc(size); if (!tmp) ekg_oom_handler(); memset(tmp, 0, size); return tmp; } #define fix(s) ((s) ? (s) : "") int xstrcmp(const char *s1, const char *s2) { return strcmp(fix(s1), fix(s2)); } char *xstrdup(const char *s) { char *tmp; if (!s) return NULL; if (!(tmp = (char *) strdup(s))) ekg_oom_handler(); return tmp; } void xfree(void *ptr) { if (ptr) free(ptr); } void *list_add_beginning(list_t *list, void *data) { list_t new; if (!list) { errno = EFAULT; return NULL; } new = xmalloc(sizeof(struct list)); new->next = *list; new->data = data; *list = new; return new->data; } struct format { char *name; hash_t name_hash; char *value; }; list_t formats = NULL; void format_add(const char *name, const char *value, int replace) { struct format *f; list_t l; hash_t hash; if (!name || !value) return; hash = ekg_hash(name); if (hash == no_prompt_cache_hash) { if (!xstrcmp(name, "no_prompt_cache")) { no_prompt_cache_hash = no_prompt_cache_hash; return; } printf("nothit_add0: %s vs no_prompt_cache\n", name); } for (l = formats; l; l = l->next) { struct format *f = l->data; if (hash == f->name_hash) { if (!xstrcmp(name, f->name)) { if (replace) { xfree(f->value); f->value = xstrdup(value); } return; } printf("nothit_add: %s vs %s | %08x\n", name, f->name, hash); } } f = xmalloc(sizeof(struct format)); f->name = xstrdup(name); f->name_hash = hash; f->value = xstrdup(value); hashes[hash & 0xff]++; list_add_beginning(&formats, f); return; } #define ROL(x) (((x>>25)&0x7f)|((x<<7)&0xffffff80)) hash_t ekg_hash(const char *name) { /* ekg_hash() from stuff.c (rev: 1.1 to 1.203, and later) */ hash_t hash = 0; for (; *name; name++) { hash ^= *name; hash = ROL(hash); } return hash; } int i = 0; const char *format_find(const char *name) { const char *tmp; hash_t hash; list_t l; if (!name) return ""; /* speech app */ if (!strchr(name, ',')) { static char buf[1024]; const char *tmp; snprintf(buf, sizeof(buf), "%s,speech", name); tmp = format_find(buf); if (tmp[0] != '\0') return tmp; } hash = ekg_hash(name); for (l = formats; l; l = l->next) { struct format *f = l->data; if (hash == f->name_hash) { if (!xstrcmp(f->name, name)) return f->value; printf("nothit_find: %s vs %s\n", name, f->name); } } return ""; } int main() { no_prompt_cache_hash = ekg_hash("no_prompt_cache"); fprintf(stderr, "no_prompt_cache %08x\n", no_prompt_cache_hash); /* first of all we add all formats to list */ #define _(x) x // abuse the preprocessor ;p #include "ekg_hash_benchmark.inc" for (i = 0; i < 1; i++) { list_t l; for (l = formats; l; l = l->next) { struct format *f = l->data; format_find(f->name); } } { int totalhash = 0; for (i = 0; i < 0x100; i++) totalhash += hashes[i]; printf("-- %d\n", totalhash); for (i = 0; i < 0x100; i++) printf("%d %.2f\n", hashes[i], (float) ( ((float) hashes[i] / (float) totalhash) * 100)); } return 0; } ekg2-0.4~pre+20120506.1/contrib/ekg_hash_benchmark.inc000066400000000000000000002525511175142753400221050ustar00rootroot00000000000000 format_add("prompt", "%K:%g:%G:%n", 1); format_add("prompt,speech", " ", 1); format_add("prompt2", "%K:%c:%C:%n", 1); format_add("prompt2,speech", " ", 1); format_add("error", "%K:%r:%R:%n", 1); format_add("error,speech", "błąd!", 1); format_add("timestamp", "%T", 1); format_add("timestamp,speech", " ", 1); format_add("ncurses_prompt_none", "", 1); format_add("ncurses_prompt_query", "[%1] ", 1); format_add("statusbar", " %c(%w%{time}%c)%w %c(%w%{?session %{?away %G}%{?avail %Y}%{?chat %W}%{?dnd %K}%{?xa %g}%{?invisible %C}%{?notavail %r}%{session}}%{?!session ---}%c) %{?window (%wwin%c/%w%{?typing %C}%{window}}%{?query %c:%W%{query}}%{?debug %c(%Cdebug}%c)%w%{?activity %c(%wact%c/%W}%{activity}%{?activity %c)%w}%{?mail %c(%wmail%c/%w}%{mail}%{?mail %c)}%{?more %c(%Gmore%c)}", 1); format_add("header", " %{?query %c(%{?query_away %w}%{?query_avail %W}%{?query_invisible %K}%{?query_notavail %k}%{query}%{?query_descr %c/%w%{query_descr}}%c) %{?query_ip (%wip%c/%w%{query_ip}%c)} %{irctopic}}%{?!query %c(%wekg2%c/%w%{version}%c) (%w%{url}%c)}", 1); format_add("statusbar_act_important", "%W", 1); format_add("statusbar_act", "%K", 1); format_add("statusbar_act_typing", "%c", 1); format_add("statusbar_act_important_typing", "%C", 1); format_add("statusbar_timestamp", "%H:%M", 1); format_add("known_user", "%T%1%n/%2", 1); format_add("known_user,speech", "%1", 1); format_add("unknown_user", "%T%1%n", 1); format_add("none", "%1\n", 1); format_add("generic", "%> %1\n", 1); format_add("generic_bold", "%> %T%1%n\n", 1); format_add("generic2", "%) %1\n", 1); format_add("generic2_bold", "%) %T%1%n\n", 1); format_add("generic_error", "%! %1\n", 1); format_add("debug", "%n%1\n", 1); format_add("fdebug", "%b%1\n", 1); format_add("iodebug", "%y%1\n", 1); format_add("iorecvdebug", "%Y%1\n", 1); format_add("edebug", "%R%1\n", 1); format_add("value_none", _("(none)"), 1); format_add("not_enough_params", _("%! Too few parameters. Try %Thelp %1%n\n"), 1); format_add("invalid_params", _("%! Invalid parameters. Try %Thelp %1%n\n"), 1); format_add("var_not_set", _("%! Required variable %T%2%n by %T%1%n is unset\n"), 1); format_add("invalid_uid", _("%! Invalid user id\n"), 1); format_add("invalid_session", _("%! Invalid session\n"), 1); format_add("invalid_nick", _("%! Invalid username\n"), 1); format_add("user_not_found", _("%! User %T%1%n not found\n"), 1); format_add("not_implemented", _("%! This function isn't ready yet\n"), 1); format_add("unknown_command", _("%! Unknown command: %T%1%n\n"), 1); format_add("welcome", _("%> %Tekg2-%1%n (%ge%Gk%gg %Gr%ge%Gl%go%Ga%gd%Ge%gd%n)\n%> Software licensed on GPL v2 terms\n\n"), 1); format_add("welcome,speech", _("welcome in e k g 2."), 1); format_add("ekg_version", _("%) %Tekg2-%1%n (compiled %2)\n"), 1); format_add("secure", _("%Y(encrypted)%n"), 1); format_add("day_changed", _("%) Day changed to: %W%1"), 1); format_add("user_added", _("%> (%2) Added %T%1%n to roster\n"), 1); format_add("user_deleted", _("%) (%2) Removed %T%1%n from roster\n"), 1); format_add("user_cleared_list", _("%) (%1) Roster cleared\n"), 1); format_add("user_exists", _("%! (%2) %T%1%n already in roster\n"), 1); format_add("user_exists_other", _("%! (%3) %T%1%n already in roster as %2\n"), 1); format_add("away", _("%> (%1) Status changed to %Gaway%n\n"), 1); format_add("away_descr", _("%> (%3) Status changed to %Gaway%n: %T%1%n%2\n"), 1); format_add("back", _("%> (%1) Status changed to %Yavailable%n\n"), 1); format_add("back_descr", _("%> (%3) Status changed to %Yavailable%n: %T%1%n%2%n\n"), 1); format_add("invisible", _("%> (%1) Status changed to %cinvisible%n\n"), 1); format_add("invisible_descr", _("%> (%3) Status changed to %cinvisible%n: %T%1%n%2\n"), 1); format_add("dnd", _("%> (%1) Status changed to %Bdo not disturb%n\n"), 1); format_add("dnd_descr", _("%> (%3) Status changed to %Bdo not disturb%n: %T%1%n%2\n"), 1); format_add("ffc", _("%> (%1) Status changed to %Wfree for chat%n\n"), 1); format_add("ffc_descr", _("%> (%3) Status changed to %Wfree for chat%n: %T%1%n%2%n\n"), 1); format_add("xa", _("%> (%1) Status changed to %gextended away%n\n"), 1); format_add("xa_descr", _("%> (%3) Status changed to %gextended away%n: %T%1%n%2%n%n\n"), 1); format_add("private_mode_is_on", _("% (%1) Friends only mode is on\n"), 1); format_add("private_mode_is_off", _("%> (%1) Friends only mode is off\n"), 1); format_add("private_mode_on", _("%) (%1) Turned on ,,friends only'' mode\n"), 1); format_add("private_mode_off", _("%> (%1) Turned off ,,friends only'' mode\n"), 1); format_add("private_mode_invalid", _("%! Invalid value'\n"), 1); format_add("descr_too_long", _("%! Description longer than maximum %T%1%n characters\nDescr: %B%2%b%3%n\n"), 1); format_add("auto_away", _("%> (%1) Auto %Gaway%n\n"), 1); format_add("auto_away_descr", _("%> (%3) Auto %Gaway%n: %T%1%n%2%n\n"), 1); format_add("auto_xa", _("%> (%1) Auto %gextended away%n\n"), 1); format_add("auto_xa_descr", _("%> (%3) Auto %gextended away%n: %T%1%n%2%n\n"), 1); format_add("auto_back", _("%> (%1) Auto back%n\n"), 1); format_add("auto_back_descr", _("%> (%3) Auto back: %T%1%n%2%n\n"), 1); format_add("help", "%> %T%1%n %2 - %3\n", 1); format_add("help_no_params", "%> %T%1%n - %2\n", 1); format_add("help_more", "%) %|%1\n", 1); format_add("help_alias", _("%) %T%1%n is an alias and don't have description\n"), 1); format_add("help_footer", _("\n%> %|%Thelp %n will show more details about command. Prepending %T^%n to command name will hide it's result. Instead of one can use %T$%n, which means current query user.\n\n"), 1); format_add("help_quick", _("%> %|Before using consult the brochure. File %Tdocs/ULOTKA.en%n is a short guide on included documentation. If you don't have it, you can visit %Thttp://www.ekg2.org/%n\n"), 1); format_add("help_set_file_not_found", _("%! Can't find variables descriptions (incomplete installation)\n"), 1); format_add("help_set_file_not_found_plugin", _("%! Can't find variables descriptions for %T%1%n plugin (incomplete installation)\n"), 1); format_add("help_set_var_not_found", _("%! Cant find description of %T%1%n variable\n"), 1); format_add("help_set_header", _("%> %T%1%n (%2, default value: %3)\n%>\n"), 1); format_add("help_set_body", "%> %|%1\n", 1); format_add("help_set_footer", "", 1); format_add("help_command_body", "%> %|%1\n", 1); format_add("help_command_file_not_found", _("%! Can't find commands descriptions (incomplete installation)\n"), 1); format_add("help_command_file_not_found_plugin", _("%! Can't find commands descriptions for %T%1%n plugin (incomplete installation)\n"), 1); format_add("help_command_not_found", _("%! Can't find command description: %T%1%n\n"), 1); format_add("help_script", _("%) %T%1%n is an script command and don't have description. Try /%1 help\n"), 1); format_add("help_session_body", "%> %|%1\n", 1); format_add("help_session_file_not_found", _("%! Can't find variables descriptions for %T%1%n plugin (incomplete installation)\n"), 1); format_add("help_session_var_not_found", _("%! Cant find description of %T%1%n variable\n"), 1); format_add("help_session_header", _("%> %1->%T%2%n (%3, default value: %4)\n%>\n"), 1); format_add("help_session_footer", "", 1); format_add("ignored_added", _("%> Ignoring %T%1%n\n"), 1); format_add("ignored_modified", _("%> Modified ignore level of %T%1%n\n"), 1); format_add("ignored_deleted", _("%) Unignored %1\n"), 1); format_add("ignored_deleted_all", _("%) Ignore list cleared up\n"), 1); format_add("ignored_exist", _("%! %1 already beeing ignored\n"), 1); format_add("ignored_list", "%> %1 %2\n", 1); format_add("ignored_list_empty", _("%! Ignore list ist empty\n"), 1); format_add("error_not_ignored", _("%! %1 is not beeing ignored\n"), 1); format_add("blocked_added", _("%> Blocking %T%1%n\n"), 1); format_add("blocked_deleted", _("%) Unblocking %1\n"), 1); format_add("blocked_deleted_all", _("%) Block list cleared up\n"), 1); format_add("blocked_exist", _("%! %1 already beeing blocked\n"), 1); format_add("blocked_list", "%> %1\n", 1); format_add("blocked_list_empty", _("%! Block list is empty\n"), 1); format_add("error_not_blocked", _("%! %1 is not beeing blocked\n"), 1); format_add("list_empty", _("%! Roster is empty\n"), 1); format_add("list_avail", _("%> %1 %Y(available)%n %b%3:%4%n\n"), 1); format_add("list_avail_descr", _("%> %1 %Y(available: %n%5%Y)%n %b%3:%4%n\n"), 1); format_add("list_away", _("%> %1 %G(away)%n %b%3:%4%n\n"), 1); format_add("list_away_descr", _("%> %1 %G(away: %n%5%G)%n %b%3:%4%n\n"), 1); format_add("list_dnd", _("%> %1 %B(do not disturb)%n %b%3:%4%n\n"), 1); format_add("list_dnd_descr", _("%> %1 %G(do not disturb:%n %5%G)%n %b%3:%4%n\n"), 1); format_add("list_chat", _("%> %1 %W(free for chat)%n %b%3:%4%n\n"), 1); format_add("list_chat_descr", _("%> %1 %W(free for chat%n: %5%W)%n %b%3:%4%n\n"), 1); format_add("list_error", _("%> %1 %m(error) %b%3:%4%n\n"), 1); format_add("list_error", _("%> %1 %m(error%n: %5%m)%n %b%3:%4%n\n"), 1); format_add("list_xa", _("%> %1 %g(extended away)%n %b%3:%4%n\n"), 1); format_add("list_xa_descr", _("%> %1 %g(extended away: %n%5%g)%n %b%3:%4%n\n"), 1); format_add("list_notavail", _("%> %1 %r(offline)%n\n"), 1); format_add("list_notavail_descr", _("%> %1 %r(offline: %n%5%r)%n\n"), 1); format_add("list_invisible", _("%> %1 %c(invisible)%n %b%3:%4%n\n"), 1); format_add("list_invisible_descr", _("%> %1 %c(invisible: %n%5%c)%n %b%3:%4%n\n"), 1); format_add("list_blocking", _("%> %1 %m(blocking)%n\n"), 1); format_add("list_unknown", "%> %1\n", 1); format_add("modify_offline", _("%> %1 will not see your status\n"), 1); format_add("modify_online", _("%> %1 will see your status\n"), 1); format_add("modify_done", _("%> Modified item in roster\n"), 1); format_add("contacts_header", "", 1); format_add("contacts_header_group", "%K %1%n", 1); format_add("contacts_metacontacts_header", "", 1); format_add("contacts_avail_header", "", 1); format_add("contacts_avail", " %Y%1%n", 1); format_add("contacts_avail_descr", "%Ki%Y%1%n", 1); format_add("contacts_avail_descr_full", "%Ki%Y%1%n %2", 1); format_add("contacts_avail_blink", " %Y%i%1%n", 1); format_add("contacts_avail_descr_blink", "%K%ii%Y%i%1%n", 1); format_add("contacts_avail_descr_full_blink", "%K%ii%Y%i%1%n %2", 1); format_add("contacts_avail_footer", "", 1); format_add("contacts_away_header", "", 1); format_add("contacts_away", " %G%1%n", 1); format_add("contacts_away_descr", "%Ki%G%1%n", 1); format_add("contacts_away_descr_full", "%Ki%G%1%n %2", 1); format_add("contacts_away_blink", " %G%i%1%n", 1); format_add("contacts_away_descr_blink", "%K%ii%G%i%1%n", 1); format_add("contacts_away_descr_full_blink", "%K%ii%G%i%1%n %2", 1); format_add("contacts_away_footer", "", 1); format_add("contacts_dnd_header", "", 1); format_add("contacts_dnd", " %B%1%n", 1); format_add("contacts_dnd_descr", "%Ki%B%1%n", 1); format_add("contacts_dnd_descr_full", "%Ki%B%1%n %2", 1); format_add("contacts_dnd_blink", " %B%i%1%n", 1); format_add("contacts_dnd_descr_blink", "%K%ii%B%i%1%n", 1); format_add("contacts_dnd_descr_full_blink", "%K%ii%B%i%1%n %2", 1); format_add("contacts_dnd_footer", "", 1); format_add("contacts_chat_header", "", 1); format_add("contacts_chat", " %W%1%n", 1); format_add("contacts_chat_descr", "%Ki%W%1%n", 1); format_add("contacts_chat_descr_full", "%Ki%W%1%n %2", 1); format_add("contacts_chat_blink", " %W%i%1%n", 1); format_add("contacts_chat_descr_blink", "%K%ii%W%i%1%n", 1); format_add("contacts_chat_descr_full_blink", "%K%ii%W%i%1%n %2", 1); format_add("contacts_chat_footer", "", 1); format_add("contacts_error_header", "", 1); format_add("contacts_error", " %m%1%n", 1); format_add("contacts_error_descr", "%Ki%m%1%n", 1); format_add("contacts_error_descr_full", "%Ki%m%1%n %2", 1); format_add("contacts_error_blink", " %m%i%1%n", 1); format_add("contacts_error_descr_blink", "%K%ii%m%i%1%n", 1); format_add("contacts_error_descr_full_blink", "%K%ii%m%i%1%n %2", 1); format_add("contacts_error_footer", "", 1); format_add("contacts_xa_header", "", 1); format_add("contacts_xa", " %g%1%n", 1); format_add("contacts_xa_descr", "%Ki%g%1%n", 1); format_add("contacts_xa_descr_full", "%Ki%g%1%n %2", 1); format_add("contacts_xa_blink", " %g%i%1%n", 1); format_add("contacts_xa_descr_blink", "%K%ii%g%i%1%n", 1); format_add("contacts_xa_descr_full_blink", "%K%ii%g%i%1%n %2", 1); format_add("contacts_xa_footer", "", 1); format_add("contacts_notavail_header", "", 1); format_add("contacts_notavail", " %r%1%n", 1); format_add("contacts_notavail_descr", "%Ki%r%1%n", 1); format_add("contacts_notavail_descr_full", "%Ki%r%1%n %2", 1); format_add("contacts_notavail_blink", " %r%i%1%n", 1); format_add("contacts_notavail_descr_blink", "%K%ii%r%i%1%n", 1); format_add("contacts_notavail_descr_full_blink", "%K%ii%r%i%1%n %2", 1); format_add("contacts_notavail_footer", "", 1); format_add("contacts_invisible_header", "", 1); format_add("contacts_invisible", " %c%1%n", 1); format_add("contacts_invisible_descr", "%Ki%c%1%n", 1); format_add("contacts_invisible_descr_full", "%Ki%c%1%n %2", 1); format_add("contacts_invisible_blink", " %c%i%1%n", 1); format_add("contacts_invisible_descr_blink", "%K%ii%c%i%1%n", 1); format_add("contacts_invisible_descr_full_blink", "%K%ii%c%i%1%n %2", 1); format_add("contacts_invisible_footer", "", 1); format_add("contacts_blocking_header", "", 1); format_add("contacts_blocking", " %m%1%n", 1); format_add("contacts_blocking_footer", "", 1); format_add("contacts_unknown_header", "", 1); format_add("contacts_unknown", " %M%1%n", 1); format_add("contacts_unknown_descr", "%Ki%M%1%n", 1); format_add("contacts_unknown_descr_full", "%Ki%M%1%n %2", 1); format_add("contacts_unknown_blink", " %M%i%1%n", 1); format_add("contacts_unknown_descr_blink", "%K%ii%M%i%1%n", 1); format_add("contacts_unknown_descr_full_blink", "%K%ii%M%i%1%n %2", 1); format_add("contacts_unknown_footer", "", 1); format_add("contacts_footer", "", 1); format_add("contacts_footer_group", "", 1); format_add("contacts_metacontacts_footer", "", 1); format_add("contacts_vertical_line_char", "|", 1); format_add("contacts_horizontal_line_char", "-", 1); format_add("contacts_avail_blink_typing", "%W%i*%Y%i%1%n", 1); format_add("contacts_avail_descr_blink_typing", "%W%i*%Y%i%1%n", 1); format_add("contacts_avail_descr_full_blink_typing", "%W%i*%Y%i%1%n %2", 1); format_add("contacts_away_blink_typing", "%W%i*%G%i%1%n", 1); format_add("contacts_away_descr_blink_typing", "%W%i*%G%i%1%n", 1); format_add("contacts_away_descr_full_blink_typing", "%W%i*%G%i%1%n %2", 1); format_add("contacts_dnd_blink_typing", "%W%i*%B%i%1%n", 1); format_add("contacts_dnd_descr_blink_typing", "%W%i*%B%i%1%n", 1); format_add("contacts_dnd_descr_full_blink_typing", "%W%i*%B%i%1%n %2", 1); format_add("contacts_chat_blink_typing", "%W%i*%W%i%1%n", 1); format_add("contacts_chat_descr_blink_typing", "%W%i*%W%i%1%n", 1); format_add("contacts_chat_descr_full_blink_typing", "%W%i*%W%i%1%n %2", 1); format_add("contacts_error_blink_typing", "%W%i*%m%i%1%n", 1); format_add("contacts_error_descr_blink_typing", "%W%i*%m%i%1%n", 1); format_add("contacts_error_descr_full_blink_typing", "%W%i*%m%i%1%n %2", 1); format_add("contacts_xa_blink_typing", "%W%i*%g%i%1%n", 1); format_add("contacts_xa_descr_blink_typing", "%W%i*%g%i%1%n", 1); format_add("contacts_xa_descr_full_blink_typing", "%W%i*%g%i%1%n %2", 1); format_add("contacts_notavail_blink_typing", "%W%i*%r%i%1%n", 1); format_add("contacts_notavail_descr_blink_typing", "%W%i*%r%i%1%n", 1); format_add("contacts_notavail_descr_full_blink_typing", "%W%i*%r%i%1%n %2", 1); format_add("contacts_invisible_blink_typing", "%W%i*%c%i%1%n", 1); format_add("contacts_invisible_descr_blink_typing", "%W%i*%c%i%1%n", 1); format_add("contacts_invisible_descr_full_blink_typing", "%W%i*%c%i%1%n %2", 1); format_add("contacts_avail_typing", "%W*%Y%1%n", 1); format_add("contacts_avail_descr_typing", "%W*%Y%1%n", 1); format_add("contacts_avail_descr_full_typing", "%W*%Y%1%n %2", 1); format_add("contacts_away_typing", "%W*%G%1%n", 1); format_add("contacts_away_descr_typing", "%W*%G%1%n", 1); format_add("contacts_away_descr_full_typing", "%W*%G%1%n %2", 1); format_add("contacts_dnd_typing", "%W*%B%1%n", 1); format_add("contacts_dnd_descr_typing", "%W*%B%1%n", 1); format_add("contacts_dnd_descr_full_typing", "%W*%B%1%n %2", 1); format_add("contacts_chat_typing", "%W*%W%1%n", 1); format_add("contacts_chat_descr_typing", "%W*%W%1%n", 1); format_add("contacts_chat_descr_full_typing", "%W*%W%1%n %2", 1); format_add("contacts_error_typing", "%W*%m%1%n", 1); format_add("contacts_error_descr_typing", "%W*%m%1%n", 1); format_add("contacts_error_descr_full_typing", "%W*%m%1%n %2", 1); format_add("contacts_xa_typing", "%W*%g%1%n", 1); format_add("contacts_xa_descr_typing", "%W*%g%1%n", 1); format_add("contacts_xa_descr_full_typing", "%W*%g%1%n %2", 1); format_add("contacts_notavail_typing", "%W*%r%1%n", 1); format_add("contacts_notavail_descr_typing", "%W*%r%1%n", 1); format_add("contacts_notavail_descr_full_typing", "%W*%r%1%n %2", 1); format_add("contacts_invisible_typing", "%W*%c%1%n", 1); format_add("contacts_invisible_descr_typing", "%W*%c%1%n", 1); format_add("contacts_invisible_descr_full_typing", "%W*%c%1%n %2", 1); format_add("contacts_unknown_typing", "%W*%M%1%n", 1); format_add("contacts_unknown_descr_typing", "%W*%M%1%n", 1); format_add("contacts_unknown_descr_full_typing", "%W*%M%1%n %2", 1); format_add("quit", _("%> Bye\n"), 1); format_add("quit_descr", _("%> Bye: %T%1%n%2\n"), 1); format_add("config_changed", _("Save new configuration ? (t-yes/n-no) "), 1); format_add("config_must_reconnect", _("%) You must reconnect for the changes to take effect\n"), 2); format_add("quit_keep_reason", _("You've set keep_reason to save status.\nDo you want to save current description to file (it will be restored upon next EKG exec)? (t-yes/n-no) "), 1); format_add("saved", _("%> Configuration saved\n"), 1); format_add("error_saving", _("%! There was some error during save\n"), 1); format_add("message", "%g.-- %n%1 %c%2%n%6%n%g--- -- -%n\n%g|%n %|%3%n\n%|%g`----- ---- --- -- -%n\n", 1); format_add("message_timestamp", "(%Y-%m-%d %H:%M) ", 1); format_add("message_timestamp_today", "(%H:%M) ", 1); format_add("message_timestamp_now", "", 1); format_add("message,speech", _("message from %1: %3."), 1); format_add("empty", "%3\n", 1); format_add("conference", "%g.-- %n%1 %c%2%n%6%n%g--- -- -%n\n%g|%n %|%3%n\n%|%g`----- ---- --- -- -%n\n", 1); format_add("conference_timestamp", "(%Y-%m-%d %H:%M) ", 1); format_add("conference_timestamp_today", "(%H:%M) ", 1); format_add("conference_timestamp_now", "", 1); format_add("confrence,speech", _("message from %1: %3."), 1); format_add("chat", "%c.-- %n%1 %c%2%n%6%n%c--- -- -%n\n%c|%n %|%3%n\n%|%c`----- ---- --- -- -%n\n", 1); format_add("chat_timestamp", "(%Y-%m-%d %H:%M) ", 1); format_add("chat_timestamp_today", "(%H:%M) ", 1); format_add("chat_timestamp_now", "", 1); format_add("chat,speech", _("message from %1: %3."), 1); format_add("sent", "%b.-- %n%1 %c%2%n%6%n%b--- -- -%n\n%b|%n %|%3%n\n%|%b`----- ---- --- -- -%n\n", 1); format_add("sent_timestamp", "(%Y-%m-%d %H:%M) ", 1); format_add("sent_timestamp_today", "(%H:%M) ", 1); format_add("sent_timestamp_now", "", 1); format_add("sent,speech", "", 1); format_add("system", _("%m.-- %TSystem message%m --- -- -%n\n%m|%n %|%3%n\n%|%m`----- ---- --- -- -%n\n"), 1); format_add("system,speech", _("system message: %3."), 1); format_add("ack_queued", _("%> Message to %1 will be delivered later\n"), 1); format_add("ack_delivered", _("%> Message to %1 delivered\n"), 1); format_add("ack_unknown", _("%> Not clear what happened to message to %1\n"), 1); format_add("ack_tempfail", _("%! %|Message to %1 encountered temporary delivery failure (e.g. message queue full). Please try again later.\n"), 1); format_add("ack_filtered", _("%! %|Message to %1 encountered permament delivery failure (e.g. forbidden content). Before retrying, try to fix the problem yourself (e.g. ask second side to add us to userlist).\n"), 1); format_add("message_too_long", _("%! Message was too long and got shortened\n"), 1); format_add("status_avail", _("%> (%3) %1 is %Yavailable%n\n"), 1); format_add("status_avail_descr", _("%> (%3) %1 is %Yavailable%n: %T%4%n\n"), 1); format_add("status_away", _("%> (%3) %1 is %Gaway%n\n"), 1); format_add("status_away_descr", _("%> (%3) %1 is %Gaway%n: %T%4%n\n"), 1); format_add("status_notavail", _("%> (%3) %1 is %roffline%n\n"), 1); format_add("status_notavail_descr", _("%> (%3) %1 is %roffline%n: %T%4%n\n"), 1); format_add("status_invisible", _("%> (%3) %1 is %cinvisible%n\n"), 1); format_add("status_invisible_descr", _("%> (%3) %1 is %cinvisible%n: %T%4%n\n"), 1); format_add("status_xa", _("%> (%3) %1 is %gextended away%n\n"), 1); format_add("status_xa_descr", _("%> (%3) %1 is %gextended away%n: %T%4%n\n"), 1); format_add("status_dnd", _("%> (%3) %1 %Bdo not disturb%n\n"), 1); format_add("status_dnd_descr", _("%> (%3) %1 %Bdo not disturb%n: %T%4%n\n"), 1); format_add("status_error", _("%> (%3) %1 %merror fetching status%n\n"), 1); format_add("status_error_descr", _("%> (%3) %1 %merror fetching status%n: %T%4%n\n"), 1); format_add("status_chat", _("%> (%3) %1 is %Wfree for chat%n\n"), 1); format_add("status_chat_descr", _("%> (%3) %1 is %Wfree for chat%n: %T%4%n\n"), 1); format_add("connecting", _("%> (%1) Connecting to server %n\n"), 1); format_add("conn_failed", _("%! (%2) Connection failure: %1%n\n"), 1); format_add("conn_failed_resolving", _("Server not found"), 1); format_add("conn_failed_connecting", _("Can't connect to server"), 1); format_add("conn_failed_invalid", _("Invalid server response"), 1); format_add("conn_failed_disconnected", _("Server disconnected"), 1); format_add("conn_failed_password", _("Invalid password"), 1); format_add("conn_failed_404", _("HTTP server error"), 1); format_add("conn_failed_tls", _("Error negotiating TLS"), 1); format_add("conn_failed_memory", _("No memory"), 1); format_add("conn_stopped", _("%! (%1) Connection interrupted %n\n"), 1); format_add("conn_timeout", _("%! (%1) Connection timed out%n\n"), 1); format_add("connected", _("%> (%1) Connected%n\n"), 1); format_add("connected_descr", _("%> (%2) Connected: %T%1%n\n"), 1); format_add("disconnected", _("%> (%1) Disconnected%n\n"), 1); format_add("disconnected_descr", _("%> (%2) Disconnected: %T%1%n\n"), 1); format_add("already_connected", _("%! (%1) Already connected. Use %Treconnect%n to reconnect%n\n"), 1); format_add("during_connect", _("%! (%1) Connecting in progress. Use %Tdisconnect%n to abort%n\n"), 1); format_add("conn_broken", _("%! (%1) Connection broken: %2%n\n"), 1); format_add("conn_disconnected", _("%! (%1) Server disconnected%n\n"), 1); format_add("not_connected", _("%! (%1) Not connected.%n\n"), 1); format_add("not_connected_msg_queued", _("%! (%1) Not connected. Message will be delivered when connected.%n\n"), 1); format_add("wrong_id", _("%! (%1) Wrong session id.%n\n"), 1); format_add("inet_addr_failed", _("%! (%1) Invalid \"server\".%n\n"), 1); format_add("invalid_local_ip", _("%! (%1) Invalid local address. I'm clearing %Tlocal_ip%n session variable\n"), 1); format_add("auto_reconnect_removed", _("%! (%1) EKG2 won't try to connect anymore - use /connect.%n\n"), 1); format_add("theme_loaded", "%> Loaded theme %T%1%n\n", 1); format_add("theme_default", "%> Default theme selected\n", 1); format_add("error_loading_theme", "%! Error loading theme: %1\n", 1); format_add("variable", "%> %1 = %2\n", 1); format_add("variable_not_found", _("%! Unknown variable: %T%1%n\n"), 1); format_add("variable_invalid", _("%! Invalid session variable value\n"), 1); format_add("no_config", _("%! Incomplete configuration. Use:\n%! %Tsession -a %n\n%! %Tsession password %n\n%! %Tsave%n\n%! And then:\n%! %Tconnect%n\n%! If you don't have uid, use:\n%! %Tregister %n\n\n%> %|Query windows will be created automatically. To switch windows press %TAlt-number%n or %TEsc%n and then number. To start conversation use %Tquery%n. To add someone to roster use %Tadd%n. All key shortcuts are described in %TREADME%n. There is also %Thelp%n command. Remember about prefixes before UID, for example %Tgg:%n. \n\n"), 2); format_add("no_config,speech", _("incomplete configuration. enter session -a, and then gg: gg-number, or jid: jabber id, then session password and your password. enter save to save. enter connect to connect. if you dont have UID enter register, space, e-mail and password. Query windows will be created automatically. To switch windows press Alt and window number or Escape and then number. To start conversation use query command. To add someone to roster use add command. All key shortcuts are described in README file. There is also help command."), 1); format_add("no_config_gg_not_loaded", _("%! Incomplete configuration. Use:\n%! %T/plugin +gg%n - to load gg plugin\n%! %Tsession -a %n\n%! %Tsession password %n\n%! %Tsave%n\n%! And then:\n%! %Tconnect%n\n%! If you don't have uid, use:\n%! %Tregister %n\n\n%> %|Query windows will be created automatically. To switch windows press %TAlt-number%n or %TEsc%n and then number. To start conversation use %Tquery%n. To add someone to roster use %Tadd%n. All key shortcuts are described in %TREADME%n. There is also %Thelp%n command. Remember about prefixes before UID, for example %Tgg:%n. \n\n"), 2); format_add("no_config_no_libgadu", _("%! Incomplete configuration. %TBIG FAT WARNING:%n\n%! %Tgg plugin has not been compiled, probably there is no libgadu library in the system\n%! Use:\n%! %Tsession -a %n\n%! %Tsession password %n\n%! %Tsave%n\n%! And then:\n%! %Tconnect%n\n%! If you don't have uid, use:\n%! %Tregister %n\n\n%> %|Query windows will be created automatically. To switch windows press %TAlt-number%n or %TEsc%n and then number. To start conversation use %Tquery%n. To add someone to roster use %Tadd%n. All key shortcuts are described in %TREADME%n. There is also %Thelp%n command. Remember about prefixes before UID, for example %Tgg:%n. \n\n"), 2); format_add("error_reading_config", _("%! Error reading configuration file: %1\n"), 1); format_add("config_read_success", _("%> Configuratin read correctly.%n\n"), 1); format_add("config_line_incorrect", _("%! Invalid line '%T%1%n', skipping\n"), 1); format_add("autosaved", _("%> Automatically saved settings\n"), 1); format_add("config_upgrade_begin", _("%) EKG2 upgrade detected. In the meantime, following changes were made:\n"), 1); format_add("config_upgrade_important", "%) %W%2) %y*%n %1\n", 1); format_add("config_upgrade_major", "%) %W%2) %Y*%n %1\n", 1); format_add("config_upgrade_minor", "%) %W%2) %c*%n %1\n", 1); format_add("config_upgrade_end", _("%) To make configuration upgrade permament, please save your configuration: %c/save%n\n"), 1); format_add("register", _("%> Registration successful. Your number: %T%1%n\n"), 1); format_add("register_failed", _("%! Error during registration: %1\n"), 1); format_add("register_pending", _("%! Registration in progress\n"), 1); format_add("register_timeout", _("%! Registration timed out\n"), 1); format_add("registered_today", _("%! Already registered. Do not abuse\n"), 1); format_add("unregister", _("%> Account removed\n"), 1); format_add("unregister_timeout", _("%! Account removal timed out\n"), 1); format_add("unregister_bad_uin", _("%! Unknown uin: %T%1%n\n"), 1); format_add("unregister_failed", _("%! Error while deleting account: %1\n"), 1); format_add("remind", _("%> Password sent\n"), 1); format_add("remind_failed", _("%! Error while sending password: %1\n"), 1); format_add("remind_timeout", _("%! Password sending timed out\n"), 1); format_add("passwd", _("%> Password changed\n"), 1); format_add("passwd_failed", _("%! Error while changing password: %1\n"), 1); format_add("passwd_timeout", _("%! Password changing timed out\n"), 1); format_add("passwd_possible_abuse", "%> (%1) Password reply send by wrong uid: %2, if this is good server uid please report this to developers and manually change your session password using /session password", 1); format_add("passwd_abuse", "%! (%1) Somebody want to clear our password (%2)", 1); format_add("change", _("%> Informations in public directory chenged\n"), 1); format_add("change_failed", _("%! Error while changing information in public directory\n"), 1); format_add("search_failed", _("%! Error while search: %1\n"), 1); format_add("search_not_found", _("%! Not found\n"), 1); format_add("search_no_last", _("%! Last search returned no result\n"), 1); format_add("search_no_last_nickname", _("%! No nickname in last search\n"), 1); format_add("search_stopped", _("%> Search stopped\n"), 1); format_add("search_results_multi_avail", "%Y<>%n", 1); format_add("search_results_multi_away", "%G<>%n", 1); format_add("search_results_multi_invisible", "%c<>%n", 1); format_add("search_results_multi_notavail", " ", 1); format_add("search_results_multi_unknown", "-", 1); /* format_add("search_results_multi_female", "k", 1); */ /* format_add("search_results_multi_male", "m", 1); */ format_add("search_results_multi", "%7 %[-7]1 %K|%n %[12]3 %K|%n %[12]2 %K|%n %[4]5 %K|%n %[12]4\n", 1); format_add("search_results_single_avail", _("%Y(available)%N"), 1); format_add("search_results_single_away", _("%G(away)%n"), 1); format_add("search_results_single_notavail", _("%r(offline)%n"), 1); format_add("search_results_single_invisible", _("%c(invisible)%n"), 1); format_add("search_results_single_unknown", "%T-%n", 1); format_add("search_results_single", _("%) Nickname: %T%3%n\n%) Number: %T%1%n %7\n%) Name: %T%2%n\n%) City: %T%4%n\n%) Birth year: %T%5%n\n"), 1); format_add("process", "%> %(-5)1 %2\n", 1); format_add("no_processes", _("%! There are no running procesees\n"), 1); format_add("process_exit", _("%> Proces %1 (%2) exited with %3 status\n"), 1); format_add("exec", "%1\n",1); /* lines are ended by \n */ format_add("exec_error", _("%! Error running process : %1\n"), 1); format_add("exec_prompt", "$ %1\n", 1); format_add("user_info_header", "%K.--%n %T%1%n/%2 %K--- -- -%n\n", 1); format_add("user_info_nickname", _("%K| %nNickname: %T%1%n\n"), 1); format_add("user_info_status", _("%K| %nStatus: %T%1%n\n"), 1); format_add("user_info_status_time_format", "%Y-%m-%d %H:%M", 1); format_add("user_info_status_time", _("%K| %nCurrent status since: %T%1%n\n"), 1); format_add("user_info_block", _("%K| %nBlocked\n"), 1); format_add("user_info_offline", _("%K| %nCan't see our status\n"), 1); format_add("user_info_groups", _("%K| %nGroups: %T%1%n\n"), 1); format_add("user_info_never_seen", _("%K| %nNever seen\n"), 1); format_add("user_info_last_seen", _("%K| %nLast seen: %T%1%n\n"), 1); format_add("user_info_last_seen_time", "%Y-%m-%d %H:%M", 1); format_add("user_info_last_status", _("%K| %nLast status: %T%1%n\n"), 1); format_add("user_info_footer", "%K`----- ---- --- -- -%n\n", 1); format_add("user_info_avail", _("%Yavailable%n"), 1); format_add("user_info_avail_descr", _("%Yavailable%n %K(%n%2%K)%n"), 1); format_add("user_info_away", _("%Gaway%n"), 1); format_add("user_info_away_descr", _("%Gaway%n %K(%n%2%K)%n"), 1); format_add("user_info_notavail", _("%roffline%n"), 1); format_add("user_info_notavail_descr", _("%roffline%n %K(%n%2%K)%n"), 1); format_add("user_info_invisible", _("%cinvisible%n"), 1); format_add("user_info_invisible_descr", _("%cinvisible%n %K(%n%2%K)%n"), 1); format_add("user_info_dnd", _("%Bdo not disturb%n"), 1); format_add("user_info_dnd_descr", _("%Bdo not disturb%n %K(%n%2%K)%n"), 1); format_add("resource_info_status", _("%K| %nResource: %W%1%n Status: %T%2 Prio: %g%3%n"), 1); format_add("group_members", _("%> %|Group %T%1%n: %2\n"), 1); format_add("group_member_already", _("%! %1 already in group %T%2%n\n"), 1); format_add("group_member_not_yet", _("%! %1 not in group %T%2%n\n"), 1); format_add("group_empty", _("%! Group %T%1%n is empty\n"), 1); format_add("show_status_profile", _("%) Profile: %T%1%n\n"), 1); format_add("show_status_uid", "%) UID: %T%1%n\n", 1); format_add("show_status_uid_nick", "%) UID: %T%1%n (%T%2%n)\n", 1); format_add("show_status_status", _("%) Current status: %T%1%2%n\n"), 1); format_add("show_status_status_simple", _("%) Current status: %T%1%n\n"), 1); format_add("show_status_server", _("%) Current server: %T%1%n:%T%2%n\n"), 1); format_add("show_status_server_tls", _("%) Current server: %T%1%n:%T%2%Y (connection encrypted)%n\n"), 1); format_add("show_status_connecting", _("%) Connecting ..."), 1); format_add("show_status_avail", _("%Yavailable%n"), 1); format_add("show_status_avail_descr", _("%Yavailable%n (%T%1%n%2)"), 1); format_add("show_status_away", _("%Gaway%n"), 1); format_add("show_status_away_descr", _("%Gaway%n (%T%1%n%2)"), 1); format_add("show_status_invisible", _("%cinvisible%n"), 1); format_add("show_status_invisible_descr", _("%cinvisible%n (%T%1%n%2)"), 1); format_add("show_status_xa", _("%gextended away%n"), 1); format_add("show_status_xa_descr", _("%gextended away%n (%T%1%n%2)"), 1); format_add("show_status_dnd", _("%cdo not disturb%n"), 1); format_add("show_status_dnd_descr", _("%cdo not disturb%n (%T%1%n%2)"), 1); format_add("show_status_chat", _("%Wfree for chat%n"), 1); format_add("show_status_chat_descr", _("%Wfree for chat%n (%T%1%n%2)"), 1); format_add("show_status_notavail", _("%roffline%n"), 1); format_add("show_status_private_on", _(", for friends only"), 1); format_add("show_status_private_off", "", 1); format_add("show_status_connected_since", _("%) Connected since: %T%1%n\n"), 1); format_add("show_status_disconnected_since", _("%) Disconnected since: %T%1%n\n"), 1); format_add("show_status_last_conn_event", "%Y-%m-%d %H:%M", 1); format_add("show_status_last_conn_event_today", "%H:%M", 1); format_add("show_status_ekg_started_since", _("%) Program started: %T%1%n\n"), 1); format_add("show_status_ekg_started", "%Y-%m-%d %H:%M", 1); format_add("show_status_ekg_started_today", "%H:%M", 1); format_add("show_status_msg_queue", _("%) Messages queued for delivery: %T%1%n\n"), 1); format_add("aliases_list_empty", _("%! No aliases\n"), 1); format_add("aliases_list", "%> %T%1%n: %2\n", 1); format_add("aliases_list_next", "%> %3 %2\n", 1); format_add("aliases_add", _("%> Created alias %T%1%n\n"), 1); format_add("aliases_append", _("%> Added to alias %T%1%n\n"), 1); format_add("aliases_del", _("%) Removed alias %T%1%n\n"), 1); format_add("aliases_del_all", _("%) Removed all aliases\n"), 1); format_add("aliases_exist", _("%! Alias %T%1%n already exists\n"), 1); format_add("aliases_noexist", _("%! Alias %T%1%n doesn't exist\n"), 1); format_add("aliases_command", _("%! %T%1%n is internal command\n"), 1); format_add("aliases_not_enough_params", _("%! Alias %T%1%n requires more parameters\n"), 1); format_add("dcc_attack", _("%! To many direct connections, last from %1\n"), 1); format_add("dcc_limit", _("%! %|Direct connections count over limit, so they got disabled. To enable them use %Tset dcc 1% and reconnect. Limit is controlled by %Tdcc_limit%n variable.\n"), 1); format_add("dcc_create_error", _("%! Can't turn on direct connections: %1\n"), 1); format_add("dcc_error_network", _("%! Error transmitting with %1\n"), 1); format_add("dcc_error_refused", _("%! Connection to %1 refused\n"), 1); format_add("dcc_error_unknown", _("%! Uknown direct connection error\n"), 1); format_add("dcc_error_handshake", _("%! Can't connect with %1\n"), 1); format_add("dcc_user_aint_dcc", _("%! %1 doesn't have direct connections enabled\n"), 1); format_add("dcc_timeout", _("%! Direct connection to %1 timed out\n"), 1); format_add("dcc_not_supported", _("%! Operation %T%1%n isn't supported yet\n"), 1); format_add("dcc_open_error", _("%! Can't open %T%1%n: %2\n"), 1); format_add("dcc_show_pending_header", _("%> Pending connections:\n"), 1); format_add("dcc_show_pending_send", _("%) #%1, %2, sending %T%3%n\n"), 1); format_add("dcc_show_pending_get", _("%) #%1, %2, receiving %T%3%n\n"), 1); format_add("dcc_show_pending_voice", _("%) #%1, %2, chat\n"), 1); format_add("dcc_show_active_header", _("%> Active connections:\n"), 1); format_add("dcc_show_active_send", _("%) #%1, %2, sending %T%3%n, %T%4b%n z %T%5b%n (%6%%)\n"), 1); format_add("dcc_show_active_get", _("%) #%1, %2, receiving %T%3%n, %T%4b%n z %T%5b%n (%6%%)\n"), 1); format_add("dcc_show_active_voice", _("%) #%1, %2, chat\n"), 1); format_add("dcc_show_empty", _("%! No direct connections\n"), 1); format_add("dcc_receiving_already", _("%! File %T%1%n from %2 is being received\n"), 1); format_add("dcc_done_get", _("%> Finished receiving file %T%2%n from %1\n"), 1); format_add("dcc_done_send", _("%> Finished sending file %T%2%n to %1\n"), 1); format_add("dcc_close", _("%) Connection with %1 closed\n"), 1); format_add("dcc_voice_offer", _("%) %1 wants to chat\n%) Use %Tdcc voice #%2%n to start chat or %Tdcc close #%2%n to refuse\n"), 1); format_add("dcc_voice_running", _("%! Only one simultanous voice chat possible\n"), 1); format_add("dcc_voice_unsupported", _("%! Voice chat not compiled in. See %Tdocs/voip.txt%n\n"), 1); format_add("dcc_get_offer", _("%) %1 sends %T%2%n (size %T%3b%n)\n%) Use %Tdcc get #%4%n to receive or %Tdcc close #%4%n to refuse\n"), 1); format_add("dcc_get_offer_resume", _("%) File exist, you can resume with %Tdcc resume #%4%n\n"), 1); format_add("dcc_get_getting", _("%) Started receiving %T%2%n from %1\n"), 1); format_add("dcc_get_cant_create", _("%! Can't open file %T%1%n\n"), 1); format_add("dcc_not_found", _("%! Connection not found: %T%1%n\n"), 1); format_add("dcc_invalid_ip", _("%! Invalid IP address\n"), 1); format_add("dcc_user_notavail", _("%! %1 has to available to connect\n"), 1); format_add("query_started", _("%) (%2) Query with %T%1%n started\n"), 1); format_add("query_started_window", _("%) Press %TAlt-G%n to ignore, %TAlt-K%n to close window\n"), 1); format_add("query_finished", _("%) (%2) Finished query with %T%1%n\n"), 1); format_add("query_exist", _("%! (%3) Query with %T%1%n already in window no %T%2%n\n"), 1); format_add("events_list_empty", _("%! No events\n"), 1); format_add("events_list_header", "", 1); format_add("events_list", "%> %5 on %1 %3 %4 - prio %2\n", 1); format_add("events_add", _("%> Added event %T%1%n\n"), 1); format_add("events_del", _("%) Removed event %T%1%n\n"), 1); format_add("events_del_all", _("%) Removed all events\n"), 1); format_add("events_exist", _("%! Event %T%1%n exist for %2\n"), 1); format_add("events_del_noexist", _("%! Event %T%1%n do not exist\n"), 1); format_add("userlist_put_ok", _("%> Roster saved on server\n"), 1); format_add("userlist_put_error", _("%! Error sending roster\n"), 1); format_add("userlist_get_ok", _("%> Roster read from server\n"), 1); format_add("userlist_get_error", _("%! Error getting roster\n"), 1); format_add("userlist_clear_ok", _("%) Removed roster from server\n"), 1); format_add("userlist_clear_error", _("%! Error removing roster from server\n"), 1); format_add("quick_list", "%)%1\n", 1); format_add("quick_list,speech", _("roster:"), 1); format_add("quick_list_avail", " %Y%1%n", 1); format_add("quick_list_avail,speech", _("%1 is available"), 1); format_add("quick_list_away", " %G%1%n", 1); format_add("quick_list_away,speech", _("%1 is away"), 1); format_add("quick_list_invisible", " %c%1%n", 1); format_add("window_add", _("%) New window created\n"), 1); format_add("window_noexist", _("%! Choosen window do not exist\n"), 1); format_add("window_doesnt_exist", _("%! Window %T%1%n does not exist\n"), 1); format_add("window_no_windows", _("%! Can't close last window\n"), 1); format_add("window_del", _("%) Window closed\n"), 1); format_add("windows_max", _("%! Window limit exhausted\n"), 1); format_add("window_list_query", _("%) %1: query with %T%2%n\n"), 1); format_add("window_list_nothing", _("%) %1 no query\n"), 1); format_add("window_list_floating", _("%) %1: floating %4x%5 in %2,%3 %T%6%n\n"), 1); format_add("window_id_query_started", _("%) (%3) Query with %T%2%n started in %T%1%n\n"), 1); format_add("window_kill_status", _("%! Can't close status window!\n"), 1); format_add("window_cannot_move_status", _("%! Can't move status window!\n"), 1); format_add("window_invalid_move", _("%! Window %T%1%n can't be moved\n"), 1); format_add("cant_kill_irc_window", _("Can't kill window. Use /window kill"), 1); format_add("file_doesnt_exist", _("%! Can't open file %T%1%n\n"), 1); format_add("bind_seq_incorrect", _("%! Sequence %T%1%n is invalid\n"), 1); format_add("bind_seq_add", _("%> Sequence %T%1%n added\n"), 1); format_add("bind_seq_remove", _("%) Sequence %T%1%n removed\n"), 1); format_add("bind_seq_list", "%> %1: %T%2%n\n", 1); format_add("bind_seq_exist", _("%! Sequence %T%1%n is already bound\n"), 1); format_add("bind_seq_list_empty", _("%! No bound actions\n"), 1); format_add("bind_doesnt_exist", _("%! Can't find sequence %T%1%n\n"), 1); format_add("bind_press_key", _("%! Press key(s) which should be bound\n"), 1); format_add("bind_added", _("%> Binding added\n"), 1); format_add("at_list", "%> %1, %2, %3 %K(%4)%n %5\n", 1); format_add("at_added", _("%> Created plan %T%1%n\n"), 1); format_add("at_deleted", _("%) Removed plan %T%1%n\n"), 1); format_add("at_deleted_all", _("%) Removed user's plans\n"), 1); format_add("at_exist", _("%! Plan %T%1%n already exists\n"), 1); format_add("at_noexist", _("%! Plan %T%1%n do not exists\n"), 1); format_add("at_empty", _("%! No plans\n"), 1); format_add("at_timestamp", "%d-%m-%Y %H:%M", 1); format_add("at_back_to_past", _("%! If time travels were possible...\n"), 1); format_add("timer_list", "%> %1, %2s, %3 %K(%4)%n %T%5%n\n", 1); format_add("timer_added", _("%> Created timer %T%1%n\n"), 1); format_add("timer_deleted", _("%) Removed timer %T%1%n\n"), 1); format_add("timer_deleted_all", _("%) Removed user's timers\n"), 1); format_add("timer_exist", _("%! Timer %T%1%n already exists\n"), 1); format_add("timer_noexist", _("%! Timer %T%1%n does not exists\n"), 1); format_add("timer_empty", _("%! No timers\n"), 1); format_add("last_list_in", "%) %Y <<%n [%1] %2 %3\n", 1); format_add("last_list_out", "%) %G >>%n [%1] %2 %3\n", 1); format_add("last_list_empty", _("%! No messages logged\n"), 1); format_add("last_list_empty_nick", _("%! No messages from %T%1%n logged\n"), 1); format_add("last_list_timestamp", "%d-%m-%Y %H:%M", 1); format_add("last_list_timestamp_today", "%H:%M", 1); format_add("last_clear_uin", _("%) Messages from %T%1%n cleared\n"), 1); format_add("last_clear", _("%) All messages cleared\n"), 1); format_add("last_begin_uin", _("%) Lastlog from %T%1%n begins\n"), 1); format_add("last_begin", _("%) Lastlog begin\n"), 1); format_add("last_end", _("%) Lastlog end\n"), 1); format_add("lastlog_title", _("%) %gLastlog [%B%2%n%g] from window: %W%T%1%n"), 1); format_add("lastlog_title_cur", _("%) %gLastlog [%B%2%n%g] from window: %W%T%1 (*)%n"), 1); format_add("away_log_begin", _("%) Logged messages for session %1:\n"), 1); format_add("away_log_end", _("%) Away log end\n"), 1); format_add("away_log_msg", "%) [%Y%1%n] [%G%2%n] <%W%3%n> %4\n", 1); format_add("away_log_timestamp", "%d-%m-%Y %H:%M:%S", 1); format_add("queue_list_timestamp", "%d-%m-%Y %H:%M", 1); format_add("queue_list_message", "%) %G >>%n [%1] %2 %3\n", 1); format_add("queue_clear", _("%) Message queue cleared\n"), 1); format_add("queue_clear_uid", _("%) Message queue for %T%1%n cleared\n"), 1); format_add("queue_wrong_use", _("%! Command works only when disconected\n"), 1); format_add("queue_empty", _("%! Messaged queue is empty\n"), 1); format_add("queue_empty_uid", _("%! No messages to %T%1%n in queue\n"), 1); format_add("queue_flush", _("%> (%1) Sent messages from queue\n"), 1); format_add("conferences_list_empty", _("%! No conference\n"), 1); format_add("conferences_list", "%> %T%1%n: %2\n", 1); format_add("conferences_list_ignored", _("%> %T%1%n: %2 (%yignored%n)\n"), 1); format_add("conferences_add", _("%> Created conference %T%1%n\n"), 1); format_add("conferences_not_added", _("%! Conference not created %T%1%n\n"), 1); format_add("conferences_del", _("%) Removed conference %T%1%n\n"), 1); format_add("conferences_del_all", _("%) Removed all conferences\n"), 1); format_add("conferences_exist", _("%! Conference %T%1%n already exists\n"), 1); format_add("conferences_noexist", _("%! Conference %T%1%n do not exists\n"), 1); format_add("conferences_name_error", _("%! Conference name should start with %T#%n\n"), 1); format_add("conferences_rename", _("%> Conference renamed: %T%1%n --> %T%2%n\n"), 1); format_add("conferences_ignore", _("%> Konference %T%1%n will be ignored\n"), 1); format_add("conferences_unignore", _("%> Conference %T%1%n won't be ignored\n"), 1); format_add("conferences_joined", _("%> Joined %1 to conference %T%2%n\n"), 1); format_add("conferences_already_joined", _("%> %1 already in conference %T%2%n\n"), 1); format_add("http_failed_resolving", _("Server not found"), 1); format_add("http_failed_connecting", _("Can not connect ro server"), 1); format_add("http_failed_reading", _("Server disconnected"), 1); format_add("http_failed_writing", _("Server disconnected"), 1); format_add("http_failed_memory", _("No memory"), 1); format_add("session_name", "%B%1%n", 1); format_add("session_variable", "%> %T%1->%2 = %R%3%n\n", 1); /* uid, var, new_value*/ format_add("session_variable_removed", _("%> Removed %T%1->%2%n\n"), 1); /* uid, var */ format_add("session_variable_doesnt_exist", _("%! Unknown variable: %T%1->%2%n\n"), 1); /* uid, var */ format_add("session_list", "%> %T%1%n %3\n", 1); /* uid, uid, %{user_info_*} */ format_add("session_list_alias", "%> %T%2%n/%1 %3\n", 1); /* uid, alias, %{user_info_*} */ format_add("session_list_empty", _("%! Session list is empty\n"), 1); format_add("session_info_header", "%) %T%1%n %3\n", 1); /* uid, uid, %{user_info_*} */ format_add("session_info_header_alias", "%) %T%2%n/%1 %3\n", 1); /* uid, alias, %{user_info_*} */ format_add("session_info_param", "%) %1 = %T%2%n\n", 1); /* key, value */ format_add("session_info_footer", "", 1); /* uid */ format_add("session_exists", _("%! Session %T%1%n already exists\n"), 1); /* uid */ format_add("session_doesnt_exist", _("%! Session %T%1%n does not exist\n"), 1); /* uid */ format_add("session_added", _("%> Created session %T%1%n\n"), 1); /* uid */ format_add("session_removed", _("%> Removed session %T%1%n\n"), 1); /* uid */ format_add("session_format", "%T%1%n", 1); format_add("session_format_alias", "%T%1%n/%2", 1); format_add("session_cannot_change", _("%! Can't change session in query window%n\n"), 1); format_add("session_password_changed", _("%> %|(%1) Looks like you're changing password in connected session. This does only set password on the client-side. If you want you change your account password, please use dedicated function (e.g. /passwd)."), 1); format_add("session_locked", _("%! %|Session %T%1%n is currently locked. If there aren't any other copy of EKG2 using it, please call: %c/session --unlock%n to unlock it.\n"), 1); format_add("session_not_locked", _("%! Session %T%1%n is not locked"), 1); format_add("metacontact_list", "%> %T%1%n", 1); format_add("metacontact_list_empty", "%! Metacontact list is empty\n", 1); format_add("metacontact_exists", "%! Metacontact %T%1%n already exists\n", 1); format_add("metacontact_added", "%> Metacontact %T%1%n added\n", 1); format_add("metacontact_removed", "%> Metacontact %T%1%n removed\n", 1); format_add("metacontact_doesnt_exist", "%! Metacontact %T%1%n doesn't exist\n", 1); format_add("metacontact_added_item", "%> Added %T%1/%2%n to metacontact %T%3%n\n", 1); format_add("metacontact_removed_item", "%> Removed %T%1/%2%n from metacontact %T%3%n\n", 1); format_add("metacontact_item_list_header", "", 1); format_add("metacontact_item_list", "%> %T%1/%2 (%3)%n - prio %T%4%n\n", 1); format_add("metacontact_item_list_empty", "%! Metacontact is empty\n", 1); format_add("metacontact_item_list_footer", "", 1); format_add("metacontact_item_doesnt_exist", "%! Contact %T%1/%2%n doesn't exiet\n", 1); format_add("metacontact_info_header", "%K.--%n Metacontact %T%1%n %K--- -- -%n\n", 1); format_add("metacontact_info_status", "%K| %nStatus: %T%1%n\n", 1); format_add("metacontact_info_footer", "%K`----- ---- --- -- -%n\n", 1); format_add("metacontact_info_avail", _("%Yavailable%n"), 1); format_add("metacontact_info_avail_descr", _("%Yavailable%n %K(%n%2%K)%n"), 1); format_add("metacontact_info_away", _("%Gaway%n"), 1); format_add("metacontact_info_away_descr", _("%Gaway%n %K(%n%2%K)%n"), 1); format_add("metacontact_info_notavail", _("%roffline%n"), 1); format_add("metacontact_info_notavail_descr", _("%roffline%n %K(%n%2%K)%n"), 1); format_add("metacontact_info_invisible", _("%cinvisible%n"), 1); format_add("metacontact_info_invisible_descr", _("%cinvisible%n %K(%n%2%K)%n"), 1); format_add("metacontact_info_dnd", _("%Bdo not disturb%n"), 1); format_add("metacontact_info_dnd_descr", _("%Bdo not disturb%n %K(%n%2%K)%n"), 1); format_add("metacontact_info_chat", _("%Wfree for chat%n"), 1); format_add("metacontact_info_chat_descr", _("%Wfree for chat%n %K(%n%2%K)%n"), 1); format_add("metacontact_info_error", _("%merror%n"), 1); format_add("metacontact_info_error_descr", _("%merror%n %K(%n%2%K)%n"), 1); format_add("metacontact_info_xa", _("%gextended away%n"), 1); format_add("metacontact_info_xa_descr", _("%gextended away%n %K(%n%2%K)%n"), 1); format_add("metacontact_info_blocking", _("%mblocking%n"), 1); format_add("metacontact_info_blocking_descr", _("%mblocking%n %K(%n%2%K)%n"), 1); format_add("metacontact_info_unknown", _("%Munknown%n"), 1); format_add("plugin_already_loaded", _("%! Plugin %T%1%n already loaded%n.\n"), 1); format_add("plugin_doesnt_exist", _("%! Plugin %T%1%n can not be found%n\n"), 1); format_add("plugin_incorrect", _("%! Plugin %T%1%n is not correct EKG2 plugin%n\n"), 1); format_add("plugin_not_initialized", _("%! Plugin %T%1%n not initialized correctly, check debug window%n\n"), 1); format_add("plugin_unload_ui", _("%! Plugin %T%1%n is an UI plugin and can't be unloaded%n\n"), 1); format_add("plugin_loaded", _("%> Plugin %T%1%n loaded%n\n"), 1); format_add("plugin_unloaded", _("%> Plugin %T%1%n unloaded%n\n"), 1); format_add("plugin_list", _("%> %T%1%n - %2%n\n"), 1); format_add("plugin_prio_set", _("%> Plugin %T%1%n prio has been changed to %T%2%n\n"), 1); format_add("plugin_default", _("%> Plugins prio setted to default\n"), 1); format_add("script_autorun_succ", _("%> Script %W%1%n successful %G%2%n autorun dir"), 1); /* XXX sciezka by sie przydala */ format_add("script_autorun_fail", _("%! Script %W%1%n failed %R%2%n autorun dir %r(%3)"), 1); format_add("script_autorun_unkn", _("%! Error adding/removing script %W%1%n from autorundir %r(%3)"), 1); format_add("script_loaded", _("%) Script %W%1%n %g(%2)%n %Gloaded %b(%3)"), 1); format_add("script_incorrect", _("%! Script %W%1%n %g(%2)%n %rNOT LOADED%n %R[incorrect %3 script or you've got syntax errors]"), 1); format_add("script_incorrect2", _("%! Script %W%1%n %g(%2)%n %rNOT LOADED%n %R[script has no handler or error in getting handlers]"), 1); format_add("script_removed", _("%) Script %W%1%n %g(%2)%n %Rremoved %b(%3)"), 1); format_add("script_need_name", _("%! No filename given\n"), 1); format_add("script_not_found", _("%! Can't find script %W%1"), 1); format_add("script_wrong_location", _("%! Script have to be in %g%1%n (don't add path)"), 1); format_add("script_error", _("%! %rScript error: %|%1"), 1); format_add("script_autorun_list", "%) Script %1 -> %2\n", 1); format_add("script_eval_error", _("%! Error running code\n"), 1); format_add("script_list", _("%> %1 (%2, %3)\n"), 1); format_add("script_list_empty", _("%! No scripts loaded\n"), 1); format_add("script_generic", "%> [script,%2] (%1) %3\n", 1); format_add("script_varlist", _("%> %1 = %2 (%3)\n"), 1); format_add("script_varlist_empty", _("%! No script vars!\n"), 1); format_add("directory_cant_create", _("%! Can't create directory: %1 (%2)"), 1); format_add("console_charset_using", _("%) EKG2 detected that your console works under: %W%1%n Please verify and eventually change %Gconsole_charset%n variable"), 1); format_add("console_charset_bad", _("%! EKG2 detected that your console works under: %W%1%n, but in %Gconsole_charset%n variable you've got: %W%2%n Please verify."), 1); format_add("iconv_fail", _("%! iconv_open() fail to initialize charset conversion between %W%1%n and %W%2%n. Check %Gconsole_charset%n variable, if it won't help inform ekg2 dev team and/or upgrade iconv"), 1); format_add("aspell_init", "%> Czekaj, inicjuję moduł sprawdzania pisowni...\n", 1); format_add("aspell_init_success", "%> Zainicjowano moduł sprawdzania pisowni\n", 1); format_add("aspell_init_error", "%! Błąd modułu sprawdzania pisowni: %T%1%n\n", 1); format_add("io_cantopen", _("%! Unable to open file!"), 1); format_add("io_nonfile", _("%! Given path doesn't appear to be regular file!"), 1); format_add("io_cantread", _("%! Unable to read file!"), 1); format_add("io_truncated", _("%! %|WARNING: Filesize smaller than before. File probably truncated!"), 1); format_add("io_truncated", _("%! %|WARNING: EOF before reaching filesize. File probably truncated (somehow)!"), 1); format_add("io_expanded", _("%! %|WARNING: Filesize larger than before. File probably got expanded!"), 1); format_add("io_emptyfile", _("%! File is empty!"), 1); format_add("io_toobig", _("%! File size exceeds maximum allowed length!"), 1); format_add("io_binaryfile", _("%! %|WARNING: The file probably contains NULs (is binary), so it can't be properly handled. It will be read until first encountered NUL, i.e. to offset %g%1%n (in bytes)!"), 1); format_add("feed_status", _("%> Newstatus: %1 (%2) %3"), 1); /* XXX */ format_add("feed_added", _("%> (%2) Added %T%1%n to subscription\n"), 1); format_add("feed_exists_other", _("%! (%3) %T%1%n already subscribed as %2\n"), 1); format_add("feed_not_found", _("%) Subscription %1 not found, cannot unsubscribe"), 1); format_add("feed_deleted", _("%) (%2) Removed from subscription %T%1%n\n"), 1); format_add("feed_message_header", _("%g,+=%G-----%W %1 %n(ID: %W%2%n)"), 1); format_add("feed_message_body", _("%g||%n %|%1"), 1); format_add("feed_message_footer", _("%g|+=%G----- End of message...%n\n"), 1); format_add("feed_message_header_generic", _("%r %1 %W%2"), 1); format_add("feed_message_header_pubDate:", _("%r Napisano: %W%2"), 1); format_add("feed_message_header_author:", _("%r Autor: %W%2"), 1); format_add("feed_message_header_dc:date:", _("%r Napisano: %W%2"), 1); format_add("feed_message_header_dc:creator:", _("%r Autor: %W%2"), 1); format_add("feed_server_header_generic", _("%m %1 %W%2"), 1); format_add("nntp_command_help_header", _("%g,+=%G----- %2 %n(%T%1%n)"), 1); format_add("nntp_command_help_item", _("%g|| %W%1: %n%2"), 1); format_add("nntp_command_help_footer", _("%g`+=%G----- End of 100%n\n"), 1); format_add("nntp_message_quote_level1", "%g%1", 1); format_add("nntp_message_quote_level2", "%y%1", 1); format_add("nntp_message_quote_level", "%B%1", 1); /* upper levels.. */ format_add("nntp_message_signature", "%B%1", 1); format_add("nntp_posting_failed", _("(%1) Posting to group: %2 failed: %3 (post saved in: %4)"), 1); format_add("nntp_posting", _("(%1) Posting to group: %2 Subject: %3...."), 1); format_add("user_info_name", _("%K| %nName: %T%1 %2%n\n"), 1); format_add("gg_token", _("%> Token was written to the file %T%1%n\n"), 1); format_add("gg_token_ocr", _("%> Token: %T%1%n\n"), 1); format_add("gg_token_body", "%1\n", 1); format_add("gg_token_failed", _("%! Error getting token: %1\n"), 1); format_add("gg_token_failed_saved", _("%! Error reading token: %1 (saved@%2)\n"), 1); format_add("gg_token_timeout", _("%! Token getting timeout\n"), 1); format_add("gg_token_unsupported", _("%! Your operating system doesn't support tokens\n"), 1); format_add("gg_token_missing", _("%! First get token by function %Ttoken%n\n"), 1); format_add("gg_user_is_connected", _("%> (%1) User %T%2%n is connected\n"), 1); format_add("gg_user_is_not_connected", _("%> (%1) User %T%2%n is not connected\n"), 1); format_add("gg_image_cant_open_file", _("%! Can't open file for image %1 (%2)\n"), 1); format_add("gg_image_error_send", _("%! Error sending image\n"), 1); format_add("gg_image_ok_send", _("%> Image sent properly\n"), 1); format_add("gg_image_ok_get", _("%> Image <%3> saved in %1\n"), 1); /* %1 - path, %2 - uid, %3 - name of picture */ format_add("gg_we_are_being_checked", _("%> (%1) We are being checked by %T%2%n\n"), 1); format_add("gg_version", _("%> %TGadu-Gadu%n: libgadu %g%1%n (headers %c%2%n), protocol %g%3%n (%c0x%4%n)"), 1); format_add("gpg_key_unset", _("%) GPGKEY for uid: %W%1%n UNSET!"), 1); format_add("gpg_key_not_found", _("%> GPGKEY for uid: %W%1%n NOT FOUND!"), 1); format_add("gpg_key_set_new", _("%) You've set up new key for uid: %W%1%n keyid: %W%2%n\n%) Encryption will be disabled until you force key (gpg:key --forcekey) NOT RECOMENDED or we verify key (signed presence is enough)"), 1); format_add("gpg_key_set_newf", _("%) You've forced setting new key for uid: %W%1%n keyid: %W%2%n\n%! Forcing key is not good idea... Please rather use /gpg:key --setkey coz key will be verified before encryption..."), 1); format_add("gpg_key_set_ok", _("%> Keys you've set up for uid: %W%1%n match with our internal DB. Happy encrypted talk. F**k echelon"), 1); format_add("gpg_key_set_okf", _("%> Keys you've set up for uid: %W%1%n match with our internal DB. Happy encrypted talk. F**k echelon (Forcing key is not nessesary here!)"), 1); format_add("gpg_key_set_okbutver", _("%! Keys matched, but lasttime we fail to verify key. Encryption won't work until forced."), 1); format_add("gpg_key_set_okfbutver", _("%! Keys matched, but lasttime we fail to verify key. Encryption forced."), 1); format_add("gpg_key_set_okbutmish", _("%! Keys mishmash. Encryption won't work until forced or user change his keyid."), 1); format_add("gpg_key_set_okfbutmish", _("%! Keys mishmash. Encryption forced."), 1); format_add("gpg_key_set_okbutunk", _("%! We didn't verify this key, if you're sure it's ok force key (gpg:key --forcekey) however it's NOT RECOMENDED.. or wait until we verify key"), 1); format_add("gpg_key_set_okfbutunk", _("%! We didn't verify this key, You've forced encryption. NOT RECOMENDED."), 1); format_add("gpg_keys_list", "%> %W%1%n/%W%2%n %3", 1); /* uid, keyid, key status */ format_add("user_info_gpg_key", _("%K| %nGPGKEY: %T%1%n (%2)%n"), 1); /* keyid, key status */ format_add("irc_msg_sent", "%P<%n%3/%5%P>%n %6", 1); format_add("irc_msg_sent_n", "%P<%n%3%P>%n %6", 1); format_add("irc_msg_sent_chan", "%P<%w%{2@%+gcp}X%2%3%P>%n %6", 1); format_add("irc_msg_sent_chanh","%P<%W%{2@%+GCP}X%2%3%P>%n %6", 1); format_add("irc_not_sent", "%P(%n%3/%5%P)%n %6", 1); format_add("irc_not_sent_n", "%P(%n%3%P)%n %6", 1); format_add("irc_not_sent_chan", "%P(%w%{2@%+gcp}X%2%3%P)%n %6", 1); format_add("irc_not_sent_chanh","%P(%W%{2@%+GCP}X%2%3%P)%n %6", 1); // format_add("irc_msg_f_chan", "%B<%w%{2@%+gcp}X%2%3/%5%B>%n %6", 1); /* NOT USED */ // format_add("irc_msg_f_chanh", "%B<%W%{2@%+GCP}X%2%3/%5%B>%n %6", 1); /* NOT USED */ format_add("irc_msg_f_chan_n", "%B<%w%{2@%+gcp}X%2%3%B>%n %6", 1); format_add("irc_msg_f_chan_nh", "%B<%W%{2@%+GCP}X%2%3%B>%n %6", 1); format_add("irc_msg_f_some", "%b<%n%3%b>%n %6", 1); // format_add("irc_not_f_chan", "%B(%w%{2@%+gcp}X%2%3/%5%B)%n %6", 1); /* NOT USED */ // format_add("irc_not_f_chanh", "%B(%W%{2@%+GCP}X%2%3/%5%B)%n %6", 1); /* NOT USED */ format_add("irc_not_f_chan_n", "%B(%w%{2@%+gcp}X%2%3%B)%n %6", 1); format_add("irc_not_f_chan_nh", "%B(%W%{2@%+GCP}X%2%3%B)%n %6", 1); format_add("irc_not_f_some", "%b(%n%3%b)%n %6", 1); format_add("irc_not_f_server", "%g!%3%n %6", 1); format_add("IRC_NAMES_NAME", _("[%gUsers %G%2%n]"), 1); format_add("IRC_NAMES", "%K[%W%1%w%2%3%K]%n ", 1); format_add("IRC_NAMES_TOTAL_H", _("%> %WEKG2: %2%n: Total of %W%3%n nicks [%W%4%n ops, %W%5%n halfops, %W%6%n voices, %W%7%n normal]\n"), 1); format_add("IRC_NAMES_TOTAL", "%> %WEKG2: %2%n: Total of %W%3%n nicks [%W%4%n ops, %W%5%n voices, %W%6%n normal]\n", 1); format_add("irc_joined", _("%> %Y%2%n has joined %4\n"), 1); format_add("irc_joined_you", _("%> %RYou%n have joined %4\n"), 1); format_add("irc_left", _("%> %g%2%n has left %4 (%5)\n"), 1); format_add("irc_left_you", _("%> %RYou%n have left %4 (%5)\n"), 1); format_add("irc_kicked", _("%> %Y%2%n has been kicked out by %R%3%n from %5 (%6)\n"), 1); format_add("irc_kicked_you", _("%> You have been kicked out by %R%3%n from %5 (%6)\n"), 1); format_add("irc_quit", _("%> %Y%2%n has quit irc (%4)\n"), 1); format_add("irc_split", "%> ", 1); format_add("irc_unknown_ctcp", _("%> %Y%2%n sent unknown CTCP %3: (%4)\n"), 1); format_add("irc_ctcp_action_y_pub", "%> %y%e* %2%n %4\n", 1); format_add("irc_ctcp_action_y", "%> %Y%e* %2%n %4\n", 1); format_add("irc_ctcp_action_pub", "%> %y%h* %2%n %5\n", 1); format_add("irc_ctcp_action", "%> %Y%h* %2%n %5\n", 1); format_add("irc_ctcp_request_pub", _("%> %Y%2%n requested ctcp %5 from %4\n"), 1); format_add("irc_ctcp_request", _("%> %Y%2%n requested ctcp %5\n"), 1); format_add("irc_ctcp_reply", _("%> %Y%2%n CTCP reply from %3: %5\n"), 1); format_add("IRC_ERR_CANNOTSENDTOCHAN", "%! %2: %1\n", 1); format_add("IRC_RPL_FIRSTSECOND", "%> (%1) %2 %3\n", 1); format_add("IRC_RPL_SECONDFIRST", "%> (%1) %3 %2\n", 1); format_add("IRC_RPL_JUSTONE", "%> (%1) %2\n", 1); format_add("IRC_RPL_NEWONE", "%> (%1,%2) 1:%3 2:%4 3:%5 4:%6\n", 1); format_add("IRC_ERR_FIRSTSECOND", "%! (%1) %2 %3\n", 1); format_add("IRC_ERR_SECONDFIRST", "%! (%1) %3 %2\n", 1); format_add("IRC_ERR_JUSTONE", "%! (%1) %2\n", 1); format_add("IRC_ERR_NEWONE", "%! (%1,%2) 1:%3 2:%4 3:%5 4:%6\n", 1); format_add("IRC_RPL_CANTSEND", _("%> Cannot send to channel %T%2%n\n"), 1); format_add("RPL_MOTDSTART", "%g,+=%G-----\n", 1); format_add("RPL_MOTD", "%g|| %n%2\n", 1); format_add("RPL_ENDOFMOTD", "%g`+=%G-----\n", 1); format_add("RPL_INVITE", _("%> Inviting %W%2%n to %W%3%n\n"), 1); format_add("RPL_LISTSTART", "%g,+=%G-----\n", 1); format_add("RPL_EXCEPTLIST", _("%g|| %n %5 - %W%2%n: except %c%3\n"), 1); format_add("RPL_BANLIST", _("%g|| %n %5 - %W%2%n: ban %c%3\n"), 1); format_add("RPL_INVITELIST", _("%g|| %n %5 - %W%2%n: invite %c%3\n"), 1);; format_add("RPL_EMPTYLIST" , _("%g|| %n Empty list \n"), 1); format_add("RPL_LINKS", "%g|| %n %5 - %2 %3 %4\n", 1); format_add("RPL_ENDOFLIST", "%g`+=%G----- %2%n\n", 1); format_add("RPL_STATS", "%g|| %3 %n %4 %5 %6 %7 %8\n", 1); format_add("RPL_STATS_EXT", "%g|| %3 %n %2 %4 %5 %6 %7 %8\n", 1); format_add("RPL_STATSEND", "%g`+=%G--%3--- %2\n", 1); // format_add("RPL_CHLISTSTART", "%g,+=%G lp %2\t%3\t%4\n", 1); // format_add("RPL_CHLIST", "%g|| %n %5 %2\t%3\t%4\n", 1); format_add("RPL_CHLISTSTART", "%g,+=%G lp %2\t%3\t%4\n", 1); format_add("RPL_LIST", "%g|| %n %5 %2\t%3\t%4\n", 1); // format_add("RPL_WHOREPLY", "%g|| %c%3 %W%7 %n%8 %6 %4@%5 %W%9\n", 1); format_add("RPL_WHOREPLY", "%g|| %c%3 %W%7 %n%8 %6 %4@%5 %W%9\n", 1); format_add("RPL_AWAY", _("%G||%n away : %2 - %3\n"), 1); format_add("RPL_WHOISUSER", _("%G.+===%g-----\n%G||%n (%T%2%n) (%3@%4)\n%G||%n realname : %6\n"), 1); format_add("RPL_WHOWASUSER", _("%G.+===%g-----\n%G||%n (%T%2%n) (%3@%4)\n%G||%n realname : %6\n"), 1); // format_add("IRC_WHOERROR", _("%G.+===%g-----\n%G||%n %3 (%2)\n"), 1); // format_add("IRC_ERR_NOSUCHNICK", _("%n %3 (%2)\n"), 1); format_add("RPL_WHOISCHANNELS", _("%G||%n %|channels : %3\n"), 1); format_add("RPL_WHOISSERVER", _("%G||%n %|server : %3 (%4)\n"), 1); format_add("RPL_WHOISOPERATOR", _("%G||%n %|ircOp : %3\n"), 1); format_add("RPL_WHOISIDLE", _("%G||%n %|idle : %3 (signon: %4)\n"), 1); format_add("RPL_ENDOFWHOIS", _("%G`+===%g-----\n"), 1); format_add("RPL_ENDOFWHOWAS", _("%G`+===%g-----\n"), 1); format_add("RPL_TOPIC", _("%> Topic %2: %3\n"), 1); format_add("IRC_RPL_TOPICBY", _("%> set by %2 on %4"), 1); format_add("IRC_TOPIC_CHANGE", _("%> %T%2%n changed topic on %T%4%n: %5\n"), 1); format_add("IRC_TOPIC_UNSET", _("%> %T%2%n unset topic on %T%4%n\n"), 1); format_add("IRC_MODE_CHAN_NEW", _("%> %2/%4 sets mode [%5]\n"), 1); format_add("IRC_MODE_CHAN", _("%> %2 mode is [%3]\n"), 1); format_add("IRC_MODE", _("%> (%1) %2 set mode %3 on You\n"), 1); format_add("IRC_INVITE", _("%> %W%2%n invites you to %W%5%n\n"), 1); format_add("IRC_PINGPONG", _("%) (%1) ping/pong %c%2%n\n"), 1); format_add("IRC_YOUNEWNICK", _("%> You are now known as %G%3%n\n"), 1); format_add("IRC_NEWNICK", _("%> %g%2%n is now known as %G%4%n\n"), 1); format_add("IRC_TRYNICK", _("%> Will try to use %G%2%n instead\n"), 1); format_add("IRC_CHANNEL_SYNCED", "%> Join to %W%2%n was synced in %W%3.%4%n secs", 1); format_add("IRC_TEST", "%> (%1) %2 to %W%3%n [%4] port %W%5%n (%6)", 1); format_add("IRC_CONN_ESTAB", "%> (%1) Connection to %W%3%n estabilished", 1); format_add("IRC_TEST_FAIL", "%! (%1) Error: %2 to %W%3%n [%4] port %W%5%n (%7)", 1); format_add("irc_channel_secure", "%) (%1) Echelon can kiss our ass on %2 *g*", 1); format_add("irc_channel_unsecure", "%! (%1) warning no plugin protect us on %2 :( install sim plugin now or at least rot13..", 1); format_add("irc_access_added", _("%> (%1) %3 [#%2] was added to accesslist chan: %4 (flags: %5)"), 1); format_add("irc_access_known", "a-> %2!%3@%4", 1); /* %2 is nickname, not uid ! */ format_add("user_info_auth_type", _("%K| %nSubscription type: %T%1%n\n"), 1); format_add("jabber_auth_subscribe", _("%> (%2) %T%1%n asks for authorisation. Use \"/auth -a %1\" to accept, \"/auth -d %1\" to refuse.%n\n"), 1); format_add("jabber_auth_unsubscribe", _("%> (%2) %T%1%n asks for removal. Use \"/auth -d %1\" to delete.%n\n"), 1); format_add("jabber_xmlerror_disconnect", _("Error parsing XML: %R%1%n"), 1); format_add("jabber_auth_request", _("%> (%2) Sent authorisation request to %T%1%n.\n"), 1); format_add("jabber_auth_accept", _("%> (%2) Authorised %T%1%n.\n"), 1); format_add("jabber_auth_unsubscribed", _("%> (%2) Asked %T%1%n to remove authorisation.\n"), 1); format_add("jabber_auth_cancel", _("%> (%2) Authorisation for %T%1%n revoked.\n"), 1); format_add("jabber_auth_denied", _("%> (%2) Authorisation for %T%1%n denied.\n"), 1); format_add("jabber_auth_probe", _("%> (%2) Sent presence probe to %T%1%n.\n"), 1); format_add("jabber_msg_failed", _("%! Message to %T%1%n can't be delivered: %R(%2) %r%3%n\n"),1); format_add("jabber_msg_failed_long", _("%! Message to %T%1%n %y(%n%K%4(...)%y)%n can't be delivered: %R(%2) %r%3%n\n"),1); format_add("jabber_version_response", _("%> Jabber ID: %T%1%n\n%> Client name: %T%2%n\n%> Client version: %T%3%n\n%> Operating system: %T%4%n\n"), 1); format_add("jabber_userinfo_response", _("%> Jabber ID: %T%1%n\n%> Full Name: %T%2%n\n%> Nickname: %T%3%n\n%> Birthday: %T%4%n\n%> City: %T%5%n\n%> Desc: %T%6%n\n"), 1); format_add("jabber_lastseen_response", _("%> Jabber ID: %T%1%n\n%> Logged out: %T%2 ago%n\n"), 1); format_add("jabber_lastseen_uptime", _("%> Jabber ID: %T%1%n\n%> Server up: %T%2 ago%n\n"), 1); format_add("jabber_lastseen_idle", _("%> Jabber ID: %T%1%n\n%> Idle for: %T%2%n\n"), 1); format_add("jabber_unknown_resource", _("%! (%1) User's resource unknown%n\n\n"), 1); format_add("jabber_status_notavail", _("%! (%1) Unable to check version, because %2 is unavailable%n\n"), 1); format_add("jabber_charset_init_error", _("%! Error initialising charset conversion (%1->%2): %3"), 1); format_add("register_change_passwd", _("%> Your password for acount %T%1 is '%T%2%n' change it as fast as you can using command /jid:passwd "), 1); format_add("jabber_privacy_list_begin", _("%g,+=%G----- Privacy list on %T%2%n"), 1); format_add("jabber_privacy_list_item", _("%g|| %n %3 - %W%4%n"), 1); /* %3 - lp %4 - itemname */ format_add("jabber_privacy_list_item_def",_("%g|| %g Default:%n %W%4%n"), 1); format_add("jabber_privacy_list_item_act",_("%g|| %r Active:%n %W%4%n"), 1); format_add("jabber_privacy_list_end", _("%g`+=%G----- End of the privacy list%n"), 1); format_add("jabber_privacy_list_noitem", _("%! No privacy list in %T%2%n"), 1); format_add("jabber_privacy_item_header", _("%g,+=%G----- Details for: %T%3%n\n%g||%n JID\t\t\t\t\t MSG PIN POUT IQ%n"), 1); format_add("jabber_privacy_item", "%g||%n %[-44]4 \t%K|%n %[2]5 %K|%n %[2]6 %K|%n %[2]7 %K|%n %[2]8\n", 1); format_add("jabber_privacy_item_footer", _("%g`+=%G----- Legend: %n[%3] [%4]%n"), 1); format_add("jabber_privacy_item_allow", "%G%1%n", 1); format_add("jabber_privacy_item_deny", "%R%1%n", 1); format_add("jabber_private_list_header", _("%g,+=%G----- Private list: %T%2/%3%n"), 1); format_add("jabber_bookmark_url", _("%g|| %n URL: %W%3%n (%2)"), 1); /* %1 - session_name, bookmark url item: %2 - name %3 - url */ format_add("jabber_bookmark_conf", _("%g|| %n MUC: %W%3%n (%2)"), 1); /* %1 - session_name, bookmark conf item: %2 - name %3 - jid %4 - autojoin %5 - nick %6 - password */ format_add("jabber_private_list_item", "%g|| %n %4: %W%5%n", 1); /* %4 - item %5 - value */ format_add("jabber_private_list_session", "%g|| + %n Session: %W%4%n", 1); /* %4 - uid */ format_add("jabber_private_list_plugin", "%g|| + %n Plugin: %W%4 (%5)%n", 1); /* %4 - name %5 - prio*/ format_add("jabber_private_list_subitem", "%g|| - %n %4: %W%5%n", 1); /* %4 - item %5 - value */ format_add("jabber_private_list_footer", _("%g`+=%G----- End of the private list%n"), 1); format_add("jabber_private_list_empty", _("%! No list: %T%2/%3%n"), 1); format_add("jabber_transport_list_begin", _("%g,+=%G----- Avalible agents on: %T%2%n"), 1); format_add("jabber_transport_list_item", _("%g|| %n %6 - %W%3%n (%5)"), 1); format_add("jabber_transport_list_item_node",("%g|| %n %6 - %W%3%n node: %g%4%n (%5)"), 1); format_add("jabber_transport_list_end", _("%g`+=%G----- End of the agents list%n\n"), 1); format_add("jabber_transport_list_nolist", _("%! No agents @ %T%2%n"), 1); format_add("jabber_remotecontrols_list_begin", _("%g,+=%G----- Avalible remote controls on: %T%2%n"), 1); format_add("jabber_remotecontrols_list_item", _("%g|| %n %6 - %W%4%n (%5)"), 1); /* %3 - jid %4 - node %5 - descr %6 - seqid */ format_add("jabber_remotecontrols_list_end", _("%g`+=%G----- End of the remote controls list%n\n"), 1); format_add("jabber_remotecontrols_list_nolist", _("%! No remote controls @ %T%2%n"), 1); format_add("jabber_remotecontrols_executing", _("%> (%1) Executing command: %W%3%n @ %W%2%n (%4)"), 1); format_add("jabber_remotecontrols_completed", _("%> (%1) Command: %W%3%n @ %W%2 %gcompleted"), 1); format_add("jabber_remotecontrols_preparing", _("%> (%1) Remote client: %W%2%n is preparing to execute command @node: %W%3"), 1); /* %2 - uid %3 - node */ format_add("jabber_remotecontrols_commited", _("%> (%1) Remote client: %W%2%n executed command @node: %W%3"), 1); /* %2 - uid %3 - node */ format_add("jabber_remotecontrols_commited_status", _("%> (%1) RC %W%2%n: requested changing status to: %3 %4 with priority: %5"), 1); /* %3 - status %4 - descr %5 - prio */ format_add("jabber_remotecontrols_commited_command",_("%> (%1) RC %W%2%n: requested command: %W%3%n @ session: %4 window: %5 quiet: %6"), 1); format_add("jabber_transinfo_begin", _("%g,+=%G----- Information about: %T%2%n"), 1); format_add("jabber_transinfo_begin_node",_("%g,+=%G----- Information about: %T%2%n (%3)"), 1); format_add("jabber_transinfo_identify", _("%g|| %G --== %g%3 %G==--%n"), 1); format_add("jabber_transinfo_feature", _("%g|| %n %W%2%n feature: %n%3"), 1); format_add("jabber_transinfo_comm_ser", _("%g|| %n %W%2%n can: %n%3 %2 (%4)"), 1); format_add("jabber_transinfo_comm_use", _("%g|| %n %W%2%n can: %n%3 $uid (%4)"), 1); format_add("jabber_transinfo_comm_not", _("%g|| %n %W%2%n can: %n%3 (%4)"), 1); format_add("jabber_transinfo_end", _("%g`+=%G----- End of the infomations%n\n"), 1); format_add("jabber_search_item", _("%) JID: %T%3%n\n%) Nickname: %T%4%n\n%) Name: %T%5 %6%n\n%) Email: %T%7%n\n"), 1); /* like gg-search_results_single */ format_add("jabber_search_begin", _("%g,+=%G----- Search on %T%2%n"), 1); // format_add("jabber_search_items", "%g||%n %[-24]3 %K|%n %[10]5 %K|%n %[10]6 %K|%n %[12]4 %K|%n %[16]7\n", 1); /* like gg-search_results_multi. TODO */ format_add("jabber_search_items", "%g||%n %3 - %5 '%4' %6 <%7>", 1); format_add("jabber_search_end", _("%g`+=%G-----"), 1); format_add("jabber_search_error", _("%! Error while searching: %3\n"), 1); format_add("jabber_form_title", "%g,+=%G----- %3 %n(%T%2%n)", 1); format_add("jabber_form_item", "%g|| %n%(21)3 (%6) %K|%n --%4 %(20)5", 1); /* %3 - label %4 - keyname %5 - value %6 - req; optional */ format_add("jabber_form_item_beg", "%g|| ,+=%G-----%n", 1); format_add("jabber_form_item_plain", "%g|| | %n %3: %5", 1); /* %3 - label %4 - keyname %5 - value */ format_add("jabber_form_item_end", "%g|| `+=%G-----%n", 1); format_add("jabber_form_item_val", "%K[%b%3%n %g%4%K]%n", 1); /* %3 - value %4 - label */ format_add("jabber_form_item_sub", "%g|| %|%n\t%3", 1); /* %3 formated jabber_form_item_val */ format_add("jabber_form_command", _("%g|| %nType %W/%3 %g%2 %W%4%n"), 1); format_add("jabber_form_instructions", "%g|| %n%|%3", 1); format_add("jabber_form_end", _("%g`+=%G----- End of this %3 form ;)%n"), 1); format_add("jabber_registration_item", "%g|| %n --%3 %4%n", 1); /* %3 - keyname %4 - value */ /* XXX, merge */ format_add("jabber_vacation", _("%> You'd set up your vacation status: %g%2%n (since: %3 expires@%4)"), 1); format_add("jabber_muc_recv", "%B<%w%X%5%3%B>%n %4", 1); format_add("jabber_muc_send", "%B<%n%X%5%W%3%B>%n %4", 1); format_add("jabber_muc_room_created", _("%> Room %W%2%n created, now to configure it: type %W/admin %g%2%n to get configuration form, or type %W/admin %g%2%n --instant to create instant one"), 1); format_add("jabber_muc_banlist", _("%g|| %n %5 - %W%2%n: ban %c%3%n [%4]"), 1); /* %1 sesja %2 kanal %3 kto %4 reason %5 numerek */ // format_add("jabber_send_chan", _("%B<%W%2%B>%n %5"), 1); // format_add("jabber_send_chan_n", _("%B<%W%2%B>%n %5"), 1); // format_add("jabber_recv_chan", _("%b<%w%2%b>%n %5"), 1); // format_add("jabber_recv_chan_n", _("%b<%w%2%b>%n %5"), 1); format_add("muc_joined", _("%> %C%2%n %B[%c%3%B]%n has joined %W%4%n as a %g%6%n and a %g%7%n"), 1); format_add("muc_left", _("%> %c%2%n [%c%3%n] has left %W%4 %n[%5]\n"), 1); format_add("xmpp_feature_header", _("%g,+=%G----- XMPP features %n(%T%2%n%3%n)"), 1); /* %3 - todo */ format_add("xmpp_feature", _("%g|| %n %W%2%n can: %5 [%G%3%g,%4%n]"), 1); format_add("xmpp_feature_sub", _("%g|| %n %W%3%n: %5 [%G%4%n]"), 1); format_add("xmpp_feature_sub_unknown", _("%g|| %n %W%3%n: Unknown, report to devs [%G%4%n]"), 1); format_add("xmpp_feature_unknown", _("%g|| %n %W%2%n feature: %r%3 %n[%G%3%g,%4%n]"), 1); format_add("xmpp_feature_footer", _("%g`+=%G----- %n Turn it off using: /session display_server_features 0\n"), 1); format_add("gmail_new_mail", _("%> (%1) Content of your mailbox have changed or new mail arrived."), 1); /* sesja */ format_add("gmail_count", _("%> (%1) You have %T%2%n new thread(s) on your gmail account."), 1); /* sesja, mail count */ format_add("gmail_mail", "%> %|%T%2%n - %g%3%n\n", 1); /* sesja, from, topic, [UNUSED messages count in thread (?1)] */ format_add("gmail_thread", "%> %|%T%2 [%4]%n - %g%3%n\n", 1); /* sesja, from, topic, messages count in thread */ format_add("tlen_mail", _("%> (%1) New mail from %T%2%n, with subject: %G%3%n"), 1); /* sesja, from, topic */ format_add("tlen_alert", _("%> (%1) %T%2%n sent us an alert ...%n"), 1); /* sesja, from */ format_add("jabber_gpg_plugin", _("%> (%1) To use OpenGPG support in jabber, first load gpg plugin!"), 1); /* sesja */ format_add("jabber_gpg_config", _("%> (%1) First set gpg_key and gpg_password before turning on gpg_active!"), 1); /* sesja */ format_add("jabber_gpg_ok", _("%) (%1) GPG support: %gENABLED%n using key: %W%2%n"), 1); /* sesja, klucz */ format_add("jabber_gpg_sok", _("%) GPG key: %W%2%n"), 1); /* sesja, klucz for /status */ format_add("jabber_gpg_fail", _("%> (%1) We didn't manage to sign testdata using key: %W%2%n (%R%3%n)\nOpenGPG support for this session disabled."), 1); /* sesja, klucz, error */ format_add("jabber_msg_xmlsyntaxerr", _("%! Expat syntax-checking failed on your message: %T%1%n. Please correct your code or use double ^R to disable syntax-checking."), 1); format_add("jabber_conversations_begin", _("%g,+=%G--%n (%1) %GAvailable Reply-IDs:%n"), 1); format_add("jabber_conversations_item", _("%g|| %n %1 - %W%2%n (%g%3%n [%c%4%n])"), 1); /* %1 - n, %2 - user, %3 - subject, %4 - thread */ format_add("jabber_conversations_end", _("%g`+=%G-- End of the available Reply-ID list%n"), 1); format_add("jabber_conversations_nothread", _("non-threaded"), 1); format_add("jabber_conversations_nosubject", _("[no subject]"), 1); format_add("jogger_noentry", _("%> (%1) No thread with id %c%2%n found."), 1); format_add("jogger_subscribed", _("%> %|(%1) The thread %T%2%n has been subscribed."), 1); format_add("jogger_unsubscribed", _("%> %|(%1) The thread %T%2%n has been unsubscribed."), 1); format_add("jogger_subscription_denied", _("%! (%1) Subscription denied because of no permission."), 1); format_add("jogger_unsubscribed_earlier", _("%> (%1) The thread weren't subscribed."), 1); format_add("jogger_comment_added", _("%) %|(%1) Your comment was added to entry %c%2%n."), 1); format_add("jogger_modified", _("%> %|(%1) Subscribed entry has been modified: %c%2%n."), 1); format_add("jogger_published", _("%) %|(%1) Your new entry has been published as: %c%2%n."), 1); format_add("jogger_version", _("%> %TJogger:%n match data %g%1%n."), 1); format_add("jogger_prepared", _("%) File %T%1%n is ready for submission."), 1); format_add("jogger_notprepared", _("%! No filename given and no entry prepared!"), 1); format_add("jogger_hashdiffers", _("%! %|File contents (checksum) differs from the time it was prepared. If you changed anything in the entry file, please run %Tprepare%n again. If you want to force submission, please use %Tpublish%n again."), 1); format_add("jogger_warning", _("%) %|During QA check of the entry, following warnings have been issued:"), 1); format_add("jogger_warning_brokenheader", _("%> %|* Header with broken syntax found at: %c%1%n"), 1); format_add("jogger_warning_wrong_key", _("%> %|* Header contains unknown/wrong key at: %c%1%n"), 1); format_add("jogger_warning_wrong_key_spaces", _("%> %|* Key in header mustn't be followed or preceeded by spaces at: %c%1%n"), 1); format_add("jogger_warning_deprecated_miniblog", _("%> %|* Key %Tminiblog%n is deprecated in favor of such category at: %c%1%n"), 1); format_add("jogger_warning_miniblog_techblog", _("%> %|* Miniblog entry will be posted to Techblog at: %c%1%n"), 1); format_add("jogger_warning_techblog_only", _("%> * Entries posted to Techblog should have also some normal category: %c%1%n"), 1); format_add("jogger_warning_malformed_url", _("%> %|* Malformed URL found at: %c%1%n"), 1); format_add("jogger_warning_spacesep", _("%> %|* Possibility of accidentially using space as a separator instead of commas: %c%1%n"), 1); format_add("jogger_warning_wrong_value", _("%> %|* Incorrect value found at: %c%1%n"), 1); format_add("jogger_warning_wrong_value_level", _("%> %|* Wrong %Tlevel%n found (level %Tnumber%n should be used), entry would be published on %Tzeroth%n level (not default) at: %c%1%n"), 1); format_add("jogger_warning_wrong_value_spaces", _("%> %|* Incorrent value found (try to remove leading&trailing spaces) at: %c%1%n"), 1); format_add("jogger_warning_wrong_value_empty", _("%> %|* Empty value found in header at: %c%1%n"), 1); format_add("jogger_warning_duplicated_header", _("%> %|* Duplicated header found at: %c%1%n"), 1); format_add("jogger_warning_mislocated_header", _("%> %|* Mislocated header (?) at: %c%1%n"), 1); format_add("jogger_warning_noexcerpt", _("%> %|* Entry text size exceeds 4096 bytes, but no tag has been found. It will be probably cut by Jogger near: ...%c%1%n..."), 1); format_add("logsoracle_status", _("%> logsoracle status\n"), 1); format_add("logsoracle_status_con", _("%> connected: %T%1%n\n"), 1); format_add("logsoracle_status_sta", _("%> status changes: %T%1%n\n"), 1); format_add("logsoracle_status_msg", _("%> messages: %T%1%n\n"), 1); format_add("logsoracle_error", _("%! oracle error message:\n\n%W%1%n\n\n%! end of message\n"), 1); format_add("logsoracle_connected", _("%> connected to Oracle\n"), 1); format_add("logsoracle_disconnected", _("%> disconnected from Oracle\n"), 1); format_add("logsoracle_disconn_not_needed", _("%> not connected to database\n"), 1); format_add("logsoracle_already_connected", _("%> already connected to Oracle. use 'disconnect' first\n"), 1); format_add("logsqlite_open_error", "%! Can't open database: %1\n", 1); format_add("new_mail_one", _("%) You got one email\n"), 1); format_add("new_mail_two_four", _("%) You got %1 new emails\n"), 1); format_add("new_mail_more", _("%) You got %1 new emails\n"), 1); format_add("readline_prompt", "% ", 1); format_add("readline_prompt_away", "/ ", 1); format_add("readline_prompt_invisible", ". ", 1); format_add("readline_prompt_query", "%1> ", 1); format_add("readline_prompt_win", "%1%% ", 1); format_add("readline_prompt_away_win", "%1/ ", 1); format_add("readline_prompt_invisible_win", "%1. ", 1); format_add("readline_prompt_query_win", "%2:%1> ", 1); format_add("readline_prompt_win_act", "%1 (act/%2)%% ", 1); format_add("readline_prompt_away_win_act", "%1 (act/%2)/ ", 1); format_add("readline_prompt_invisible_win_act", "%1 (act/%2). ", 1); format_add("readline_prompt_query_win_act", "%2:%1 (act/%3)> ", 1); format_add("readline_more", _("-- Press Enter to continue or Ctrl-D to break --"), 1); format_add("rot_generic", _("%> Text: %1 roted: %2"), 1); format_add("rot_list", _("%> Sesja: %1 target: %2 (rot%3 +%4)"), 1); format_add("key_generating", _("%> Please wait, generating keys...\n"), 1); format_add("key_generating_success", _("%> Keys generated and saved\n"), 1); format_add("key_generating_error", _("%! Error while generating keys: %1\n"), 1); format_add("key_private_exist", _("%! You already own a key pair\n"), 1); format_add("key_public_deleted", _("%) Public key %1 removew\n"), 1); format_add("key_public_not_found", _("%! Can find %1's public key\n"), 1); format_add("key_public_noexist", _("%! No public keys\n"), 1); format_add("key_public_received", _("%> Received public key from %1\n"), 1); format_add("key_public_write_failed", _("%! Error while saving public key: %1\n"), 1); format_add("key_send_success", _("%> Sent public key to %1\n"), 1); format_add("key_send_error", _("%! Error sending public key\n"), 1); format_add("key_list", "%> %r%1%n (%3)\n%) fingerprint: %y%2\n", 1); format_add("key_list_timestamp", "%Y-%m-%d %H:%M", 1); format_add("sms_error", _("%! Error sending SMS: %1\n"), 1); format_add("sms_unknown", _("%! %1 not a cellphone number\n"), 1); format_add("sms_sent", _("%> SMS to %T%1%n sent\n"), 1); format_add("sms_failed", _("%! SMS to %T%1%n not sent\n"), 1); format_add("sms_away", " %2", 1); format_add("sniff_gg_welcome", _("%) %b[GG_WELCOME] %gSEED: %W%1"), 1); format_add("sniff_gg_login60", _("%) %b[GG_LOGIN60] %gUIN: %W%1 %gHASH: %W%2"), 1); format_add("sniff_gg_login70_sha1", _("%) %b[GG_LOGIN70] %gUIN: %W%1 %gSHA1: %W%2"), 1); format_add("sniff_gg_login70_hash", _("%) %b[GG_LOGIN70] %gUIN: %W%1 %gHASH: %W%2"), 1); format_add("sniff_gg_login70_unknown", _("%) %b[GG_LOGIN70] %gUIN: %W%1 %gTYPE: %W%2"), 1); format_add("sniff_gg_status60", _("%) %b[GG_STATUS60] %gDCC: %W%1:%2 %gVERSION: %W#%3 (%4) %gIMGSIZE: %W%5KiB"), 1); format_add("sniff_gg_status77", _("%) %b[GG_STATUS77] %gDCC: %W%1:%2 %gVERSION: %W#%3 (%4) %gIMGSIZE: %W%5KiB"), 1); format_add("sniff_gg_notify77", _("%) %b[GG_NOTIFY77] %gDCC: %W%1:%2 %gVERSION: %W#%3 (%4) %gIMGSIZE: %W%5KiB"), 1); format_add("sniff_gg_addnotify",_("%) %b[GG_ADD_NOTIFY] %gUIN: %W%1 %gDATA: %W%2"), 1); format_add("sniff_gg_delnotify",_("%) %b[GG_REMOVE_NOTIFY] %gUIN: %W%1 %gDATA: %W%2"), 1); format_add("sniff_pkt_rcv", _("%) %2 packets captured"), 1); format_add("sniff_pkt_drop",_("%) %2 packets dropped"), 1); format_add("sniff_conn_db", _("%) %2 connections founded"), 1); format_add("sniff_tcp_connection", "TCP %1:%2 <==> %3:%4", 1); format_add("xmsg_addwatch_failed", _("Unable to add inotify watch (wrong path?)"), 1); format_add("xmsg_nosendcmd", _("%> (%1) You need to set %csend_cmd%n to be able to send msgs"), 1); format_add("xmsg_toobig", _("%> (%2) File %T%1%n is larger than %cmax_filesize%n, skipping"), 1); format_add("xmsg_toobigrm", _("%> (%2) File %T%1%n was larger than %cmax_filesize%n, removed"), 1); format_add("xmsg_umount", _("volume containing watched directory was unmounted"), 1); format_add("xosd_new_message_irc", _("new message from %1 at %2"), 1); format_add("xosd_new_message_line_1", _("new message from %1"), 1); format_add("xosd_new_message_line_2_long", "%1...", 1); format_add("xosd_new_message_line_2", "%1", 1); format_add("xosd_status_change_avail", _("%1 is online,"), 1); format_add("xosd_status_change_away", _("%1 is away,"), 1); format_add("xosd_status_change_dnd", _("%1: do not disturb,"), 1); format_add("xosd_status_change_xa", _("%1 is extended away,"), 1); format_add("xosd_status_change_notavail", _("%1 is offline,"), 1); format_add("xosd_status_change_invisible", _("%1 is invisible,"), 1); format_add("xosd_status_change_chat", _("%1 is free for chat,"), 1); format_add("xosd_status_change_error", _("%1: status error,"), 1); format_add("xosd_status_change_blocking", _("%1 is blocking us,"), 1); format_add("xosd_status_change_description", "%1", 1); format_add("xosd_status_change_description_long", "%1...", 1); format_add("xosd_status_change_no_description", _("[no description]"), 1); format_add("xosd_welcome_message_line_1", _("ekg2 XOnScreenDisplay plugin"), 1); format_add("xosd_welcome_message_line_2", _("Author: Adam 'dredzik' Kuczynski"), 1); ekg2-0.4~pre+20120506.1/contrib/ekg_hash_benchmark_find.sed000077500000000000000000000001321175142753400230740ustar00rootroot00000000000000#!/bin/sed -nf # /format_add("/{ :MainLoop s/);/);/; t EndLoop N b MainLoop :EndLoop p } ekg2-0.4~pre+20120506.1/contrib/ekg_hash_benchmark_find.sh000077500000000000000000000005261175142753400227420ustar00rootroot00000000000000#!/bin/sh # find ekg -name '*.c' -exec contrib/ekg_hash_benchmark_find.sed {} \; > contrib/ekg_hash_benchmark.inc_new find plugins -name '*.c' -exec contrib/ekg_hash_benchmark_find.sed {} \; >> contrib/ekg_hash_benchmark.inc_new #find remote -name '*.c' -exec contrib/ekg_hash_benchmark_find.sed {} \; >> contrib/ekg_hash_benchmark.inc_new ekg2-0.4~pre+20120506.1/contrib/ekg_hash_benchmark_results.txt000066400000000000000000000072501175142753400237260ustar00rootroot00000000000000#define ROL(x) (((x>>25)&0x7f)|((x<<7)&0xffffff80)) hash_t ekg_hash(const char *name) { hash_t ekg_hash(const char *name) { hash_t ekg_hash(const char *name) { hash_t hash = 0; hash_t hash = 0; hash_t hash = 0; unsigned long long st = 0x4d6947; unsigned long long st = 0x4d6947; for (; *name; name++) { for (; *name; name++) { st = st * 2147483629 + 2147483587; for (; *name; name++) { st %= 0x7fffffff; st = st * 2147483629 + 2147483587; st %= 0x7fffffff; hash ^= *name; hash ^= ((*name) ^ ((st >> 16)&0xff) ); hash = ROL(hash); hash = ROL(hash); st ^= ((*name) << 16); } } } return hash; return hash; hash = (hash_t)st; } } return hash; } 1 no_prompt_cache 139dcbd6 4 0 0.00 1 -- 1179 1 no_prompt_cache d4620858 1 0 0.00 16 1 0.08 1 -- 1179 1 no_prompt_cache 5e0fde26 27 2 0.17 14 1 0.08 1 -- 1179 38 3 0.25 30 2 0.17 11 1 0.08 58 4 0.34 38 3 0.25 32 2 0.17 38 5 0.42 50 4 0.34 53 3 0.25 35 6 0.51 39 5 0.42 40 4 0.34 18 7 0.59 34 6 0.51 36 5 0.42 12 8 0.68 16 7 0.59 34 6 0.51 5 9 0.76 13 8 0.68 19 7 0.59 5 10 0.85 9 9 0.76 15 8 0.68 3 11 0.93 6 10 0.85 8 9 0.76 1 13 1.10 1 11 0.93 2 10 0.85 2 12 1.02 4 11 0.93 1 12 1.02 ekg2-0.4~pre+20120506.1/contrib/log_raw_viewer.c000066400000000000000000000043231175142753400207760ustar00rootroot00000000000000#include #include static const char *format_ansi(char ch) { if (ch == 'k') return ("\033[2;30m"); if (ch == 'K') return ("\033[1;30m"); if (ch == 'l') return ("\033[40m"); if (ch == 'r') return ("\033[2;31m"); if (ch == 'R') return ("\033[1;31m"); if (ch == 's') return ("\033[41m"); if (ch == 'g') return ("\033[2;32m"); if (ch == 'G') return ("\033[1;32m"); if (ch == 'h') return ("\033[42m"); if (ch == 'y') return ("\033[2;33m"); if (ch == 'Y') return ("\033[1;33m"); if (ch == 'z') return ("\033[43m"); if (ch == 'b') return ("\033[2;34m"); if (ch == 'B') return ("\033[1;34m"); if (ch == 'e') return ("\033[44m"); if (ch == 'm' || ch == 'p') return ("\033[2;35m"); if (ch == 'M' || ch == 'P') return ("\033[1;35m"); if (ch == 'q') return ("\033[45m"); if (ch == 'c') return ("\033[2;36m"); if (ch == 'C') return ("\033[1;36m"); if (ch == 'd') return ("\033[46m"); if (ch == 'w') return ("\033[2;37m"); if (ch == 'W') return ("\033[1;37m"); if (ch == 'x') return ("\033[47m"); if (ch == 'n') /* clear all attributes */ return ("\033[0m"); if (ch == 'T') /* bold */ return ("\033[1m"); if (ch == 'N') /* clears all attr exc for bkgd */ return ("\033[2m"); if (ch == 'U') /* underline */ return ("\033[4m"); if (ch == 'i') /* blink */ return ("\033[5m"); if (ch == 'V') /* reverse */ return ("\033[7m"); if (ch == '%') return "%"; return (""); } int main() { char buf[4096]; int eaten = 0; int len; while ((len = read(0, buf, sizeof(buf))) > 0) { int begpos = 0; int pos; if (eaten) { printf("%s", format_ansi(buf[0])); begpos = 1; eaten = 0; } while (len > begpos && buf[len-1] == '%') { eaten++; len--; } if (!(eaten % 2)) { len += eaten; eaten = 0; } else { len += (eaten - 1); eaten = 1; } pos = begpos; while (pos < len) { if (buf[pos] == '%') { pos++; printf("%s", format_ansi(buf[pos])); pos++; } else { putchar(buf[pos]); pos++; } } } printf("\n"); if (eaten) fprintf(stderr, "BAD LOG-FILE? ended with: '%%'\n"); return eaten; } ekg2-0.4~pre+20120506.1/contrib/logsqlite/000077500000000000000000000000001175142753400176205ustar00rootroot00000000000000ekg2-0.4~pre+20120506.1/contrib/logsqlite/logs2logsqlite.pl000077500000000000000000000036571175142753400231450ustar00rootroot00000000000000#!/usr/bin/perl # # Simple logs -> logsqlite logs converter # - no XML support # - no status support # - no conferences support # - no magic pathes support # (C) 2007 Michał Górny use DBI; use File::HomeDir; use File::Basename; use IO::File; use Date::Parse; # homedir, inserted instead of '~', by default automagically detected, but you can change it my $home = File::HomeDir->my_home; # source path (my $logspath = '~/.ekg2/logs') =~ s/~/$home/; # destination db (my $logsqlitedb = '~/.ekg2/logsqlite.db') =~ s/~/$home/; my $db = DBI->connect("dbi:SQLite:dbname=$logsqlitedb", '', '') or die; $db->begin_work; my $st = $db->prepare("INSERT INTO log_msg VALUES (?, ?, ?, ?, ?, ?, ?, ?)"); my $n; while (<$logspath/*>) { (my $sid = basename($_)) =~ /^(.*?)\:/; my $prefix = $1; next if ($sid =~ /^_/ || !$prefix); # skip internals and wrong UID-s print "-> $sid\n"; while (<$logspath/$sid/$prefix:*.txt>) { (my $uid = basename($_)) =~ s/\.txt$//; my $f = IO::File->new("<$logspath/$sid/$uid.txt"); print "\t-> $uid\n"; while (<$f>) { s/^(.*?),(.*?),(.*?),(.*?),//; my $text = $_; my ($type, $nick, $date) = ($1, $2, $4); next if ($type eq 'status'); my $is_sent = ($type eq 'chatsend' || $type eq 'msgsend'); my $sent; if ($is_sent || ($type eq 'msgsystem')) { $sent = $date; } else { if ($text =~ s/^(.*?),//) { $sent = $1; } else { $sent = $date; } } $text =~ s/\n$//; if ($text =~ /^"(.*)"$/) { # need to unescape $_ = $1; s/\\n/\n/g, s/\\r/\r/g, s/\\(.)/$1/g; $text = $_; } if ($type =~ /^(msg|chat)(send|recv)/) { $type = $1; } elsif ($type =~ /^msg(system)$/) { $type = $1; } else { $type = 'chat'; } my $datetmp = str2time($date); $date = $datetmp if ($datetmp); $datetmp = str2time($sent); $sent = $datetmp if ($sent); $st->execute($sid, $uid, $nick, $type, $is_sent, $date, $sent, $text); } } } $db->commit; ekg2-0.4~pre+20120506.1/contrib/logsqlite/logsqlite-fuse.c000066400000000000000000000302601175142753400227300ustar00rootroot00000000000000 /* logsqlite-fuse * (C) 2007 Michał Górny * covered under GPL2 * * compile like: * gcc ${CFLAGS} $(pkg-config --cflags fuse sqlite3) -o logsqlite-fuse logsqlite-fuse.c $(pkg-config --libs fuse sqlite3) */ #include #include #include #include #include #include #include #include #include #define FUSE_USE_VERSION 26 #include #include #define QUERY_COUNT 7 #define READ_ROW_COUNT "20" #define BUF_SIZE_FACTOR 4096 #define BUF_MAX_UNUSED 5 * 60 //#define DT_FORMAT "%c" #define DT_BUF_FACTOR 16 #define DT_MAX_TRIES 4 #ifdef NODEBUG #define DEBUG(x...) #else #ifndef DEBUG #define DEBUG(x...) fprintf(stderr, x) #endif #ifndef FUSE_DEBUG #define FUSE_DEBUG ",debug" #endif #endif typedef struct { char *sid, *uid, *data; off_t data_off; size_t data_size, buf_size; int db_off, eof; time_t last_use; void *next; } myBuffer_t; typedef struct { sqlite3 *db; sqlite3_stmt *stmt[QUERY_COUNT]; myBuffer_t *buffers; } myDB_t; static const char *queries[QUERY_COUNT+1] = { "SELECT ts FROM log_msg ORDER BY ts DESC LIMIT 1;", "SELECT ts FROM log_msg WHERE session = ?1 ORDER BY ts DESC LIMIT 1;", "SELECT ts FROM log_msg WHERE session = ?1 AND uid = ?2 ORDER BY ts DESC LIMIT 1;", "SELECT DISTINCT session FROM log_msg ORDER BY session ASC LIMIT -1 OFFSET ?1;", "SELECT DISTINCT uid FROM log_msg WHERE session = ?2 ORDER BY uid ASC LIMIT -1 OFFSET ?1;", "SELECT type, sent, uid, nick, ts, sentts, body FROM log_msg WHERE session = ?1 AND uid = ?2 ORDER BY ts ASC LIMIT " READ_ROW_COUNT " OFFSET ?3;", "DELETE FROM log_msg WHERE session = ?1 AND uid = ?2;", NULL }; enum statement_n { GET_NEWEST, GET_NEWEST_SESSION, GET_NEWEST_UID, GET_SESSIONS, GET_UIDS, GET_DATA, REMOVE_UID }; /* garbage cleaner for buffers */ void myGC(myDB_t *db) { const time_t now = time(NULL); myBuffer_t *curr, *prev; for (curr = db->buffers, prev = NULL; curr; prev = curr, curr = curr->next) { while (curr->last_use - now > BUF_MAX_UNUSED) { if (curr->data) free(curr->data); if (prev) prev->next = curr->next; else db->buffers = curr->next; curr = curr->next; free(curr); } } } int mySplitPath(const char *path, const char **sid, const char **uid) { static char sidbuf[PATH_MAX], uidbuf[PATH_MAX]; const char *tmp; if (strlen(path) > PATH_MAX) return -ENAMETOOLONG; *sid = sidbuf; *uid = uidbuf; /* XXX: rewrite, replace with some magic loop */ path++; /* skip leading '/' */ if ((tmp = strchr(path, '/'))) { strncpy(sidbuf, path, tmp-path); sidbuf[tmp-path] = 0; path = tmp+1; if ((tmp = strchr(path, '/'))) { strncpy(uidbuf, path, tmp-path); uidbuf[tmp-path] = 0; path = tmp+1; return 2 + (*path != 0); } else { strcpy(uidbuf, path); return 1 + (*path != 0); } } else { strcpy(sidbuf, path); return 0 + (*path != 0); } } int myGetAttr(const char *path, struct stat *out) { const char *sid, *uid; const int pathargs = mySplitPath(path, &sid, &uid); myDB_t *db = fuse_get_context()->private_data; sqlite3_stmt *stmt; int timestamp; switch (pathargs) { case 0: stmt = db->stmt[GET_NEWEST]; break; case 1: stmt = db->stmt[GET_NEWEST_SESSION]; sqlite3_bind_text(stmt, 1, sid, -1, SQLITE_STATIC); break; default: stmt = db->stmt[GET_NEWEST_UID]; sqlite3_bind_text(stmt, 1, sid, -1, SQLITE_STATIC); sqlite3_bind_text(stmt, 2, uid, -1, SQLITE_STATIC); } if (sqlite3_step(stmt) != SQLITE_ROW) { sqlite3_reset(stmt); return -ENOENT; } timestamp = sqlite3_column_int(stmt, 0); sqlite3_reset(stmt); if (pathargs > 2) return -ENOTDIR; if (pathargs < 2) { out->st_mode = S_IFDIR | 0555; out->st_nlink = 3; /* XXX: + subdirectory count */ } else { out->st_mode = S_IFREG | 0444; out->st_nlink = 1; } out->st_uid = getuid(); out->st_gid = getgid(); out->st_atime = out->st_mtime = out->st_ctime = timestamp; return 0; } int myReadDir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) { const char *sid, *uid; const int pathargs = mySplitPath(path, &sid, &uid); myDB_t *db = fuse_get_context()->private_data; sqlite3_stmt *stmt; char *nextrow = NULL, *myrow = NULL; if (pathargs == 0) stmt = db->stmt[GET_SESSIONS]; else { stmt = db->stmt[GET_UIDS]; sqlite3_bind_text(stmt, 2, sid, -1, SQLITE_STATIC); } sqlite3_bind_int(stmt, 1, offset); DEBUG("myReadDir: path = %s, off = %d\n", path, offset); do { if (myrow) free(myrow); myrow = nextrow; nextrow = (sqlite3_step(stmt) == SQLITE_ROW ? strdup(sqlite3_column_text(stmt, 0)) : NULL); DEBUG("myReadDir-loop: myrow = %s, nextrow = %s, offset = %d\n", myrow, nextrow, offset); if (myrow && filler(buf, myrow, NULL, (nextrow ? ++offset : 0)) == 1) break; } while (nextrow); sqlite3_reset(stmt); if (myrow) free(myrow); return 0; } /* offset = -1 -> remove */ myBuffer_t *myBufferFind(myDB_t *db, const char *sid, const char *uid, off_t offset) { myBuffer_t *out, *prev; for (out = db->buffers, prev = NULL; out && (strcmp(out->sid, sid) || strcmp(out->uid, uid)); prev = out, out = out->next); if (!out) { /* create */ if (offset == -1) return NULL; out = malloc(sizeof(myBuffer_t)); out->sid = strdup(sid); out->uid = strdup(uid); out->data = NULL; out->data_off = 0; out->data_size = 0; out->buf_size = 0; out->db_off = 0; out->eof = 0; out->next = 0; if (prev) prev->next = out; else db->buffers = out; } else if (offset == -1) { /* remove */ if (out->data) free(out->data); if (prev) prev->next = out->next; else db->buffers = out->next; free(out); return NULL; } else if (offset < out->data_off) { /* rewind */ if (out->data) { free(out->data); out->data = NULL; } out->data_off = 0; out->data_size = 0; out->buf_size = 0; out->db_off = 0; out->eof = 0; } out->last_use = time(NULL); return out; } /* based on log_escape() from EKG2 */ /* XXX: add/fix utf-8 handling ? */ const char *myBodyEscape(const char *in) { static char *out = NULL; static size_t bufsize = 0; const int needbuf = strlen(in) * 2 + 3; if (bufsize < needbuf) { bufsize = (needbuf / BUF_SIZE_FACTOR + 1) * BUF_SIZE_FACTOR; /* we don't use realloc(), 'cause we don't need data copying */ if (out) free(out); out = malloc(bufsize); out[0] = '"'; } char *p, *q; int hadto = 0; for (p = in, q = out+1; *p; p++, q++) { switch (*p) { case '\n': *q++ = '\\'; *q = 'n'; hadto = 1; break; case '\r': *q++ = '\\'; *q = 'r'; hadto = 1; break; case '\\': case '"': case '\'': *q++ = '\\'; hadto = 1; default: *q = *p; } } if (hadto) *q++ = '"'; *q = 0; return (hadto ? out : out+1); } const char *myTimestampFormat(const time_t timestamp) { static char *bufa = NULL; static size_t bufasize = DT_BUF_FACTOR; static char *bufb = NULL; static size_t bufbsize = DT_BUF_FACTOR; static int bufswitch = 0; char **buf = (bufswitch ? &bufb : &bufa); size_t *bufsize = (bufswitch ? &bufbsize : &bufasize); const struct tm *ts = localtime(×tamp); int n = 0; int r; bufswitch ^= 1; if (!*buf) *buf = malloc(*bufsize); #ifdef DT_FORMAT while (strftime(*buf, *bufsize, DT_FORMAT, ts) == 0) { if (++n >= DT_MAX_TRIES) break; free(*buf); *bufsize += DT_BUF_FACTOR; *buf = malloc(*bufsize); } if (n < DT_MAX_TRIES) return *buf; #endif /* fallback */ do { r = snprintf(*buf, *bufsize, "%d", timestamp); if (r >= *bufsize) { *bufsize = ((r + 1) / DT_BUF_FACTOR + 1) * DT_BUF_FACTOR; *buf = realloc(*buf, *bufsize); r = -1; } else if (r == -1) { *bufsize += DT_BUF_FACTOR; *buf = realloc(*buf, *bufsize); } } while (r == -1); return *buf; } void myBufferStep(sqlite3_stmt *stmt, myBuffer_t *buf) { if (buf->eof) return; buf->data_off += buf->data_size; buf->data_size = 0; if (!buf->data) { buf->data = malloc(BUF_SIZE_FACTOR); buf->buf_size = BUF_SIZE_FACTOR; } DEBUG("myBufferStep: data_off = %d, buf_size = %d\n", buf->data_off, buf->buf_size); sqlite3_bind_int(stmt, 3, buf->db_off); while (sqlite3_step(stmt) == SQLITE_ROW) { const int is_sent = sqlite3_column_int(stmt, 1); const char *type = sqlite3_column_text(stmt, 0); char *body = myBodyEscape(sqlite3_column_text(stmt, 6)); char tsbuf[2], *sts; int r; if (!strcmp(type, "msg")) { if (is_sent) type = "msgsend"; else type = "msgrecv"; } else if (!strcmp(type, "system")) type = "msgsystem"; else { /* "chat" */ if (is_sent) type = "chatsend"; else type = "chatrecv"; } if (is_sent) { tsbuf[0] = 0; sts = ""; } else { int stsn = sqlite3_column_int(stmt, 5); if (stsn == 0) stsn = sqlite3_column_int(stmt, 4); sts = myTimestampFormat(stsn); tsbuf[0] = ','; tsbuf[1] = 0; } do { r = snprintf(buf->data + buf->data_size, buf->buf_size - buf->data_size, "%s,%s,%s,%s,%s%s%s\n", type, sqlite3_column_text(stmt, 2), sqlite3_column_text(stmt, 3), myTimestampFormat(sqlite3_column_int(stmt, 4)), sts, tsbuf, body); if (r >= (buf->buf_size - buf->data_size)) { buf->buf_size = ((buf->data_size + r + 1) / BUF_SIZE_FACTOR + 1) * BUF_SIZE_FACTOR; buf->data = realloc(buf->data, buf->buf_size); r = -1; } else if (r == -1) { buf->buf_size = buf->data_size + BUF_SIZE_FACTOR; buf->data = realloc(buf->data, buf->buf_size); } else buf->data_size += r; } while (r == -1); buf->db_off++; } sqlite3_reset(stmt); if (buf->data_size == 0) /* no data read */ buf->eof = 1; } int myReadFile(const char *path, char *out, size_t count, off_t offset, struct fuse_file_info *fi) { const char *sid, *uid; myDB_t *db = fuse_get_context()->private_data; sqlite3_stmt *stmt = db->stmt[GET_DATA]; myBuffer_t *buf; int written = 0; mySplitPath(path, &sid, &uid); buf = myBufferFind(db, sid, uid, offset); sqlite3_bind_text(stmt, 1, sid, -1, SQLITE_STATIC); sqlite3_bind_text(stmt, 2, uid, -1, SQLITE_STATIC); DEBUG("myReadFile: path = %s, count = %d, offset = %d\n", path, count, offset); while (count > 0) { while (offset >= buf->data_off + buf->data_size && !buf->eof) myBufferStep(stmt, buf); if (buf->eof) break; const int toend = buf->data_size - buf->data_off; const int towrite = (count > toend ? toend : count); DEBUG("myReadFile-loop: count = %d, offset = %d, toend = %d, towrite = %d, written = %d\n", count, offset, toend, towrite, written); memcpy(out, buf->data + (offset - buf->data_off), towrite); out += towrite; count -= towrite; offset += towrite; written += towrite; } return written; }; int myUnlink(const char *path) { const char *sid, *uid; myDB_t *db = fuse_get_context()->private_data; sqlite3_stmt *stmt = db->stmt[REMOVE_UID]; mySplitPath(path, &sid, &uid); sqlite3_bind_text(stmt, 1, sid, -1, SQLITE_STATIC); sqlite3_bind_text(stmt, 2, uid, -1, SQLITE_STATIC); sqlite3_reset(stmt); if (sqlite3_step(stmt) != SQLITE_DONE) return -EIO; return 0; } int myReleaseFile(const char *path, struct fuse_file_info *fi) { const char *sid, *uid; myDB_t *db = fuse_get_context()->private_data; mySplitPath(path, &sid, &uid); myBufferFind(db, sid, uid, -1); return 0; } static struct fuse_operations ops = { .getattr = &myGetAttr, .readdir = &myReadDir, .read = &myReadFile, .unlink = &myUnlink, .release = &myReleaseFile }; int main(int argc, char *argv[]) { myDB_t db; if (argc < 3) { DEBUG("SYNOPSIS: %s database-path mount-point\n", argv[0]); return 1; } sqlite3_open(argv[1], &(db.db)); { /* make the db really open */ char *err; if (sqlite3_exec(db.db, "SELECT * FROM log_msg LIMIT 1;", NULL, NULL, &err) != SQLITE_OK) { DEBUG("Unable to open the database: %s.\n", err); sqlite3_free(err); return 2; } } { const char **query; sqlite3_stmt **stmt; for (query = queries, stmt = db.stmt; *query; query++, stmt++) { if (sqlite3_prepare(db.db, *query, -1, stmt, NULL) != SQLITE_OK) { DEBUG("Unable to compile queries: %s [on %s].\n", sqlite3_errmsg(db.db), *query); return 3; } } } db.buffers = NULL; { int fuse_argc = 4; char *fuse_argv[] = {"fusermount", "-o", "direct_io,rw" FUSE_DEBUG, argv[2], NULL}; fuse_main(fuse_argc, fuse_argv, &ops, &db); } return 0; } ekg2-0.4~pre+20120506.1/contrib/logsqlite/logsqlite-jid2xmpp.pl000077500000000000000000000016301175142753400237160ustar00rootroot00000000000000#!/usr/bin/perl # # change 'jid:'->'xmpp:' in logsqlite database # (C) 2007 Michał Górny # use DBI; use File::HomeDir; $| = 1; my $home = File::HomeDir->my_home; my $db = DBI->connect("dbi:SQLite:dbname=$home/.ekg2/logsqlite.db") or die; my $q = $db->prepare('SELECT DISTINCT session, uid FROM log_msg WHERE session LIKE ?1 OR uid LIKE ?2 LIMIT 500;') or die; my $u = $db->prepare('UPDATE log_msg SET session = ?3, uid = ?4 WHERE session = ?1 AND uid = ?2;') or die; while (1) { $q->execute('jid:%', 'jid:%') or die; my $res = $q->fetchall_arrayref(); last unless (@{@$res}); $db->begin_work; foreach (@$res) { my ($sid, $uid) = @$_; print STDERR '.', next unless ($sid =~ /^jid:/ || $uid =~ /^jid:/); print STDERR '*'; my ($newsid, $newuid) = ($sid, $uid); $newsid =~ s/^jid:/xmpp:/; $newuid =~ s/^jid:/xmpp:/; $u->execute($sid, $uid, $newsid, $newuid) or die; } $db->commit; print '!'; } ekg2-0.4~pre+20120506.1/contrib/logsqlite/logsqlite-stripres.pl000077500000000000000000000013711175142753400240360ustar00rootroot00000000000000#!/usr/bin/perl # # Strip resources logsqlite database # (C) 2007 Michał Górny # use DBI; use File::HomeDir; $| = 1; my $home = File::HomeDir->my_home; my $db = DBI->connect("dbi:SQLite:dbname=$home/.ekg2/logsqlite.db") or die; my $q = $db->prepare('SELECT DISTINCT uid FROM log_msg WHERE uid LIKE ?1 LIMIT 500;') or die; my $u = $db->prepare('UPDATE log_msg SET uid = ?2 WHERE uid = ?1;') or die; while (1) { $q->execute('%:%@%/%') or die; my $res = $q->fetchall_arrayref(); last unless (@$res); $db->begin_work; foreach (@$res) { my ($uid) = @$_; print STDERR '.', next unless ($uid =~ /^.*:.*@.*\/.*$/); print STDERR '*'; my ($newuid) = ($uid); $newuid =~ s/\/.*$//; $u->execute($uid, $newuid) or die; } $db->commit; print '!'; } ekg2-0.4~pre+20120506.1/contrib/patches/000077500000000000000000000000001175142753400172445ustar00rootroot00000000000000ekg2-0.4~pre+20120506.1/contrib/patches/cmd-upgrade-proof-of-concept.diff000066400000000000000000000562351175142753400254570ustar00rootroot00000000000000Index: ekg/commands.c =================================================================== --- ekg/commands.c (wersja 4609) +++ ekg/commands.c (kopia robocza) @@ -108,6 +108,7 @@ char *send_nicks[SEND_NICKS_MAX] = { NULL }; int send_nicks_count = 0, send_nicks_index = 0; static int quit_command = 0; +static int upgrade_command = 0; command_t *commands = NULL; @@ -1821,6 +1822,14 @@ return res; } +static COMMAND(cmd_upgrade) +{ + query_emit_id(NULL, EKG_UPGRADE); + upgrade_command = 1; + + return 0; +} + static COMMAND(cmd_quit) { char *reason; @@ -2429,7 +2438,7 @@ { int res; - if ((res = config_read_plugins())) printq("error_reading_config", strerror(errno)); + if ((res = config_read_plugins(NULL))) printq("error_reading_config", strerror(errno)); if (res == -1) return -1; if ((res = config_read(NULL))) printq("error_reading_config", strerror(errno)); @@ -2863,6 +2872,9 @@ if (quit_command) ekg_exit(); + if (upgrade_command) + ekg_upgrade(); + return res; } @@ -4475,6 +4487,8 @@ "-a --add -d --del -l --list"); command_add(NULL, ("unignore"), "i ?", cmd_ignore, 0, NULL); + + command_add(NULL, ("upgrade"), NULL, cmd_upgrade, 0, NULL); command_add(NULL, ("version"), NULL, cmd_version, 0, NULL); Index: ekg/queries.h =================================================================== --- ekg/queries.h (wersja 4609) +++ ekg/queries.h (kopia robocza) @@ -65,8 +65,9 @@ PROTOCOL_DISCONNECTING, USERLIST_REFRESH, + EKG_UPGRADE, EKG_RESTORE, - QUERY_EXTERNAL, + QUERY_EXTERNAL }; #ifdef __DECLARE_QUERIES_STUFF @@ -453,6 +454,12 @@ { USERLIST_REFRESH, "userlist-refresh", { QUERY_ARG_END } }, + + { EKG_UPGRADE, "ekg-upgrade", { + QUERY_ARG_END } }, + + { EKG_RESTORE, "ekg-restore", { + QUERY_ARG_END } } }; /* other, not listed above here queries, for example plugin which use internally his own query, Index: ekg/themes.c =================================================================== --- ekg/themes.c (wersja 4609) +++ ekg/themes.c (kopia robocza) @@ -1361,6 +1361,7 @@ format_add("unknown_command", _("%! Unknown command: %T%1%n\n"), 1); format_add("welcome", _("%> %Tekg2-%1%n (%ge%Gk%gg %Gr%ge%Gl%go%Ga%gd%Ge%gd%n)\n%> Software licensed on GPL v2 terms\n\n"), 1); format_add("welcome,speech", _("welcome in e k g 2."), 1); + format_add("welcome_again", _("%> %Tekg2-%1%n (%ge%Gk%gg %Gr%ge%Gl%go%Ga%gd%Ge%gd%n) (compiled %2)"), 1); format_add("ekg_version", _("%) %Tekg2-%1%n (compiled %2)\n"), 1); format_add("secure", _("%Y(encrypted)%n"), 1); format_add("day_changed", _("%) Day changed to: %W%1"), 1); Index: ekg/configfile.c =================================================================== --- ekg/configfile.c (wersja 4609) +++ ekg/configfile.c (kopia robocza) @@ -48,6 +48,7 @@ #include "vars.h" #include "xmalloc.h" #include "plugins.h" +#include "sessions.h" #include "windows.h" #include "queries.h" @@ -89,9 +90,9 @@ * * initialized after config is read */ -void config_postread() +void config_postread(int restoring) { - if (config_windows_save && config_windows_layout) { + if ((config_windows_save || restoring) && config_windows_layout) { char **targets = array_make(config_windows_layout, "|", 0, 0, 0); int i; @@ -141,17 +142,18 @@ query_emit_id(NULL, CONFIG_POSTINIT); } -int config_read_plugins() +int config_read_plugins(const char *filename) { char*buf, *foo; - const char *filename; FILE *f; struct stat st; + if (!filename) + filename = prepare_path("plugins", 0); - if (!(filename = prepare_path("plugins", 0))) - return -1; - + if (!filename) + return -1; + check_file(); while ((buf = read_file(f, 0))) { @@ -735,6 +737,90 @@ } } + /* XXX: + * - save children list + - metacontact_write() + - script_variables_write()) + + */ +int config_write_upgrade() { + FILE *f; + plugin_t *p; + const char *path; + session_t *s; + + if (!prepare_path(NULL, 1)) /* try to create ~/.ekg2 dir */ + return -1; + + /* first of all we are saving plugins */ + if (!(path = prepare_pathf("upgrade-plugins.%d", getpid()))) + return -1; + if (!(f = fopen(path, "w"))) + return -1; + fchmod(fileno(f), 0600); + config_write_plugins(f); + fclose(f); + + /* now we are saving global variables and settings + * timers, bindings etc. */ + if (!(path = prepare_pathf("upgrade-config.%d", getpid()))) + return -1; + if (!(f = fopen(path, "w"))) + return -1; + fchmod(fileno(f), 0600); + config_write_main(f); + /* now plugins variables (unline config_write() it saves all variables to the same file) */ + for (p = plugins; p; p = p->next) { + variable_t *v; + + for (v = variables; v; v = v->next) { + if (p == v->plugin) + config_write_variable(f, v); + } + } + fclose(f); + + /* XXX, sessions_write() */ + if (!(path = prepare_pathf("upgrade-sessions.%d", getpid()))) + return -1; + if (!(f = fopen(path, "w"))) + return -1; + + for (s = sessions; s; s = s->next) { + int i; + + p = s->plugin; + + // userlist_write(s); // XXX + fprintf(f, "[%s]\n", s->uid); + if (s->alias) + fprintf(f, "alias=%s\n", s->alias); + if (s->status) + fprintf(f, "status=%s\n", ekg_status_string(s->autoaway ? s->last_status : s->status, 0)); /* XXX? */ + if (s->descr) { + char *myvar = (s->autoaway ? s->last_descr : s->descr); /* XXX? */ + xstrtr(myvar, '\n', '\002'); + fprintf(f, "descr=%s\n", myvar); + xstrtr(myvar, '\002', '\n'); + } + if (s->password) + fprintf(f, "password=\001%s\n", s->password); + + if (!p->params) + continue; + + for (i = 0; (p->params[i].key /* && p->params[i].id != -1 */); i++) { + if (!s->values[i]) + continue; + fprintf(f, "%s=%s\n", p->params[i].key, s->values[i]); + } + /* XXX, save _locale_ variables */ + } + fclose(f); + + return 0; +} + /* * debug_write_crash() * Index: ekg/ekg.c =================================================================== --- ekg/ekg.c (wersja 4612) +++ ekg/ekg.c (kopia robocza) @@ -668,6 +668,7 @@ int auto_connect = 1, c = 0, no_global_config = 0, no_config = 0, new_status = 0; char *tmp = NULL, *new_descr = NULL; char *load_theme = NULL, *new_profile = NULL, *frontend = NULL; + int is_restore = 0; #ifndef NO_POSIX_SYSTEM struct rlimit rlim; #else @@ -732,7 +733,7 @@ signal(SIGALRM, SIG_IGN); signal(SIGPIPE, SIG_IGN); #endif - while ((c = getopt_long(argc, argv, "b::a::i::d::f::x::u:F:t:nmNhvU", ekg_options, NULL)) != -1) + while ((c = getopt_long(argc, argv, "b::a::i::d::f::x::u:F:t:nmrNhvU", ekg_options, NULL)) != -1) { switch (c) { case 'a': @@ -812,6 +813,9 @@ no_global_config = 1; break; + case 'r': + is_restore = 1; + break; case 'h': printf(_(EKG_USAGE), argv[0]); @@ -863,7 +867,7 @@ variable_init(); variable_set_default(); - mesg_startup = mesg_set(MESG_CHECK); + mesg_startup = mesg_set(MESG_CHECK); /* is_restore OK */ #ifdef DEFAULT_THEME if (theme_read(DEFAULT_THEME, 1) == -1) #endif @@ -873,7 +877,7 @@ window_status = window_new(NULL, NULL, 1); /* okno stanu */ window_current = window_status; - if (!no_global_config) + if (!is_restore && !no_global_config) config_read(SYSCONFDIR "/ekg2.conf"); if (frontend) { @@ -881,43 +885,57 @@ config_changed = 1; } - config_read_plugins(); - if (!no_global_config) + config_read_plugins(is_restore ? prepare_pathf("upgrade-plugins.%d", ekg_pid) : NULL); + + if (!is_restore && !no_global_config) config_read(SYSCONFDIR "/ekg2-override.conf"); /* userlist_read(); */ - emoticon_read(); - msg_queue_read(); + emoticon_read(); /* is_restore OK */ + msg_queue_read(); /* is_restore OK */ + if (is_restore) + query_emit_id(NULL, EKG_RESTORE); + + if (is_restore == 0) { #ifdef HAVE_NCURSES - if (!have_plugin_of_class(PLUGIN_UI)) plugin_load(("ncurses"), -254, 1); + if (!have_plugin_of_class(PLUGIN_UI)) plugin_load(("ncurses"), -254, 1); #endif #ifdef HAVE_GTK - if (!have_plugin_of_class(PLUGIN_UI)) plugin_load(("gtk"), -254, 1); + if (!have_plugin_of_class(PLUGIN_UI)) plugin_load(("gtk"), -254, 1); #endif #ifdef HAVE_READLINE - if (!have_plugin_of_class(PLUGIN_UI)) plugin_load(("readline"), -254, 1); + if (!have_plugin_of_class(PLUGIN_UI)) plugin_load(("readline"), -254, 1); #endif - if (!have_plugin_of_class(PLUGIN_UI)) fprintf(stderr, "No UI-PLUGIN!\n"); - else { + } + + if (!have_plugin_of_class(PLUGIN_UI)) + fprintf(stderr, "No UI-PLUGIN!\n"); /* also display when restore, it means ABI changed! / you deleted this plugin */ + /* XXX, try /upgrade in next 30s? 10m? 30m? 1h? :> */ + /* , or when keypressed? */ + + if (have_plugin_of_class(PLUGIN_UI)) { struct buffer *b; for (b = buffer_debug.data; b; b = b->next) print_window_w(window_debug, EKG_WINACT_NONE, b->target, b->line); } - if (!have_plugin_of_class(PLUGIN_PROTOCOL)) { + if (is_restore == 0) { + if (!have_plugin_of_class(PLUGIN_PROTOCOL)) { #ifdef HAVE_EXPAT - plugin_load(("jabber"), -254, 1); + plugin_load(("jabber"), -254, 1); #endif #ifdef HAVE_LIBGADU - plugin_load(("gg"), -254, 1); + plugin_load(("gg"), -254, 1); #endif - plugin_load(("irc"), -254, 1); + plugin_load(("irc"), -254, 1); + } } + theme_plugins_init(); scripts_init(); - config_read(NULL); + config_read(is_restore ? prepare_pathf("upgrade-config.%d", ekg_pid) : NULL); /* jeli ma by theme, niech bdzie theme */ if (load_theme) theme_read(load_theme, 1); @@ -943,7 +961,7 @@ } if (!batch_mode && config_display_welcome) - print("welcome", VERSION); + print(is_restore ? "welcome_again" : "welcome", VERSION, compile_time()); protocol_init(); events_init(); @@ -953,10 +971,10 @@ /* it has to be done after plugins are loaded, either we wouldn't know if we are * supporting some protocol in current build */ - if (session_read(NULL) == -1) + if (session_read(is_restore ? prepare_pathf("upgrade-sessions.%d", ekg_pid) : NULL) == -1) no_config = 1; - config_postread(); + config_postread(is_restore); /* status window takes first session if not set before*/ if (!session_current && sessions) @@ -965,13 +983,19 @@ if (session_current != window_current->session) window_current->session = session_current; window_debug->session = window_current->session; + /* + if (is_restore) + query_emit_id(NULL, EKG_RESTORE); + */ + +/* query_emit_id(NULL, SESSION_CHANGED); query_emit_id(NULL, UI_REFRESH); */ - metacontact_read(); /* read the metacontacts info */ + metacontact_read(); /* read the metacontacts info */ /* XXX, is_restore */ - { + if (is_restore == 0) { session_t *s; /* wylosuj opisy i zmie stany klientw */ @@ -997,10 +1021,10 @@ } } - if (config_auto_save) + if (config_auto_save) /* XXX, is_restore */ last_save = time(NULL); - if (no_config) { + if (!is_restore && no_config) { #ifdef HAVE_LIBGADU if (plugin_find("gg")) print("no_config"); @@ -1012,7 +1036,7 @@ } idle_add(NULL, ekg_less_important_handler, &ekg_tv); - reason_changed = 0; + reason_changed = 0; /* XXX, is_restore */ /* jesli jest emit: ui-loop (plugin-side) to dajemy mu kontrole, jesli nie * to wywolujemy normalnie sami ekg_loop() w petelce */ if (query_emit_id(NULL, UI_LOOP) != -1) { @@ -1027,6 +1051,148 @@ return 0; } +static void send_nicks_destroy() { + int i; + + for (i = 0; i < SEND_NICKS_MAX; i++) { + xfree(send_nicks[i]); + send_nicks[i] = NULL; + } + send_nicks_count = 0; +} + +static void watches_destroy() { + list_t l; + + for (l = watches; l; l = l->next) { + watch_t *w = l->data; + + watch_free(w); + } + list_destroy(watches, 0); watches = NULL; +} + +static void plugins_destroy(plugin_class_t pclass) { + extern int ekg2_dlclose(void *plugin); + plugin_t *p; + + for (p = plugins; p; ) { + plugin_t *next = p->next; + + if (p->pclass == pclass || pclass == PLUGIN_ANY) { + p->destroy(); + + if (p->dl) + ekg2_dlclose(p->dl); + } + p = next; + } +} + +static void queries_destroy() { + query_t **ll; + + for (ll = queries; ll <= &queries[QUERY_EXTERNAL]; ll++) { + query_t *q; + + for (q = *ll; q; ) { /* free other queries... connected by protocol_init() for example */ + query_t *next = q->next; + + query_free(q); + + q = next; + } + LIST_DESTROY2(*ll, NULL); /* XXX: really needed? */ + } +} + +static void lastsearch_destroy() { + xfree(last_search_first_name); + xfree(last_search_last_name); + xfree(last_search_nickname); + xfree(last_search_uid); +} + +static void ekg_destroy_common() { +/* XXX, think about sequence of unloading. */ + msgs_queue_destroy(); + conferences_destroy(); + newconferences_destroy(); + metacontacts_destroy(); + sessions_free(); + plugins_destroy(PLUGIN_ANY); + audio_deinitialize(); + aliases_destroy(); + theme_free(); + variables_destroy(); + script_variables_free(1); + emoticons_destroy(); + commands_destroy(); + timers_destroy(); + binding_free(); + lasts_destroy(); + buffer_free(&buffer_debug); buffer_free(&buffer_speech); + event_free(); + xfree(read_file(NULL, -1)); /* free internal read_file() buffer */ + +/* windows: */ + windows_destroy(); + window_status = NULL; window_debug = NULL; window_current = NULL; /* just in case */ + + queries_destroy(); + query_external_free(); + + xfree(home_dir); + xfree(config_dir); + xfree(console_charset); +#ifdef NO_POSIX_SYSTEM + WSACleanup(); +#endif + close(stderr_backup); +} + +void ekg_upgrade() +{ + int tmp; + + msg_queue_write(); + lastsearch_destroy(); + + /* save window configuration */ + tmp = config_windows_save; + config_windows_save = 1; + windows_save(); + config_windows_save = tmp; + + /* XXX, save current session (XXX, use session_default variable?)*/ + if (config_sessions_save && session_current) { + const char *vars[] = { "session_default", NULL }; + xfree(config_session_default); config_session_default = xstrdup(session_current->uid); + + config_write_partly(NULL, vars); + } + + send_nicks_destroy(); + config_write_upgrade(); + children_destroy(); + watches_destroy(); + + ekg_destroy_common(); + + mesg_set(mesg_startup); + + /* XXX, execl() with /bin/sh ? */ + if (config_profile) + execlp(argv0, argv0, "-r", "-u", config_profile, NULL); + else + execlp(argv0, argv0, "-r", NULL); + + /* XXX, call main() ? */ + + printf("ekg_upgrade() failed!\n"); + exit(1); +} + /* * ekg_exit() * @@ -1036,16 +1202,10 @@ void ekg_exit() { char *exit_exec = config_exit_exec; - extern int ekg2_dlclose(void *plugin); - int i; msg_queue_write(); + lastsearch_destroy(); - xfree(last_search_first_name); - xfree(last_search_last_name); - xfree(last_search_nickname); - xfree(last_search_uid); - windows_save(); /* setting windows layout */ @@ -1062,51 +1222,19 @@ config_write_partly(NULL, vars); } - for (i = 0; i < SEND_NICKS_MAX; i++) { - xfree(send_nicks[i]); - send_nicks[i] = NULL; - } - send_nicks_count = 0; + send_nicks_destroy(); { child_t *c; - for (c = children; c; c = c->next) { -#ifndef NO_POSIX_SYSTEM + for (c = children; c; c = c->next) kill(c->pid, SIGTERM); -#else - /* TerminateProcess / TerminateThread */ -#endif - } children_destroy(); } - { - list_t l; + watches_destroy(); + plugins_destroy(PLUGIN_UI); - for (l = watches; l; l = l->next) { - watch_t *w = l->data; - - watch_free(w); - } - } - - { - plugin_t *p, *next; - - for (p = plugins; p; p = next) { - next = p->next; - - if (p->pclass != PLUGIN_UI) - continue; - - p->destroy(); - -// if (p->dl) ekg2_dlclose(p->dl); - } - } - list_destroy(watches, 0); watches = NULL; - if (config_changed && !config_speech_app && config_save_quit == 1) { char line[80]; @@ -1146,78 +1274,9 @@ } config_exit_exec = NULL; /* avoid freeing it */ -/* XXX, think about sequence of unloading. */ + ekg_destroy_common(); - msgs_queue_destroy(); - conferences_destroy(); - newconferences_destroy(); - metacontacts_destroy(); - sessions_free(); - - { - plugin_t *p; - - for (p = plugins; p; ) { - plugin_t *next = p->next; - - p->destroy(); - -// if (p->dl) ekg2_dlclose(p->dl); - - p = next; - } - } - - audio_deinitialize(); - aliases_destroy(); - theme_free(); - variables_destroy(); - script_variables_free(1); - emoticons_destroy(); - commands_destroy(); - timers_destroy(); - binding_free(); - lasts_destroy(); - - buffer_free(&buffer_debug); buffer_free(&buffer_speech); - event_free(); - - xfree(read_file(NULL, -1)); /* free internal read_file() buffer */ - -/* windows: */ - windows_destroy(); - window_status = NULL; window_debug = NULL; window_current = NULL; /* just in case */ - -/* queries: */ - { - query_t **ll; - - for (ll = queries; ll <= &queries[QUERY_EXTERNAL]; ll++) { - query_t *q; - - for (q = *ll; q; ) { /* free other queries... connected by protocol_init() for example */ - query_t *next = q->next; - - query_free(q); - - q = next; - } - - LIST_DESTROY2(*ll, NULL); /* XXX: really needed? */ - } - } - query_external_free(); - - xfree(home_dir); - - xfree(config_dir); - xfree(console_charset); - mesg_set(mesg_startup); -#ifdef NO_POSIX_SYSTEM - WSACleanup(); -#endif - close(stderr_backup); if (exit_exec) { #ifndef NO_POSIX_SYSTEM Index: ekg/configfile.h =================================================================== --- ekg/configfile.h (wersja 4609) +++ ekg/configfile.h (kopia robocza) @@ -28,13 +28,14 @@ #include "plugins.h" -void config_postread(); +void config_postread(int restoring); int config_read(const char *filename); -int config_read_plugins(); +int config_read_plugins(const char *filename); int config_read_later(const char *filename); int config_write(); int config_write_partly(plugin_t *plugin, const char **vars); void config_write_crash(); +int config_write_upgrade(); void debug_write_crash(); #endif Index: ekg/stuff.h =================================================================== --- ekg/stuff.h (wersja 4609) +++ ekg/stuff.h (kopia robocza) @@ -408,6 +408,7 @@ /* funkcje poza stuff.c */ void ekg_exit(); +void ekg_upgrade(); void ekg_debug_handler(int level, const char *format, va_list ap); int ekg_close(int fd); Index: plugins/ncurses/main.c =================================================================== --- plugins/ncurses/main.c (wersja 4608) +++ plugins/ncurses/main.c (kopia robocza) @@ -20,9 +20,11 @@ #include "ekg2-config.h" +#include /* sscanf() */ #include #include #include +#include /* getpid() */ #include #include @@ -494,6 +496,182 @@ return retval; } +/* XXX, unicode-not-ok. XXX: SPOT logs also, copy to core */ +static char *fstring_reverse(fstring_t *fstr) { + const char *str; + const short *attr; + string_t asc; + int i; + + if (!fstr) + return NULL; + + attr = fstr->attr; + str = fstr->str.b; + + if (!attr || !str) + return NULL; + + asc = string_init(NULL); + + for (i = 0; str[i]; i++) { +#define prev attr[i-1] +#define cur attr[i] + int reset = 0; + + if (i) { + if (!(cur & FSTR_BOLD) && (prev & FSTR_BOLD)) reset = 1; + if (!(cur & FSTR_BLINK) && (prev & FSTR_BLINK)) reset = 1; + if (!(cur & FSTR_UNDERLINE) && (prev & FSTR_UNDERLINE)) reset = 1; + if (!(cur & FSTR_REVERSE) && (prev & FSTR_REVERSE)) reset = 1; + if ((cur & FSTR_NORMAL) && !(prev & FSTR_NORMAL)) reset = 1; /* colors disappear */ + + if (reset) + string_append(asc, "%n"); + } else + reset = 1; + + /* attr */ + if ((cur & FSTR_BLINK) && (reset || !(prev & FSTR_BLINK))) string_append(asc, "%i"); +// if ((cur & FSTR_UNDERLINE) && (reset || !(prev & FSTR_UNDERLINE))) string_append(asc, "%"); +// if ((cur & FSTR_REVERSE) && (reset || !(prev & FSTR_REVERSE))) string_append(asc, "%"); + + if (!(cur & FSTR_NORMAL)) { + /* background color XXX */ +#define BGCOLOR(x) -1 + if (0 && ((reset || BGCOLOR(cur) != BGCOLOR(prev)))) { + string_append_c(asc, '%'); + switch (BGCOLOR(cur)) { + case (0): string_append_c(asc, 'l'); break; + case (1): string_append_c(asc, 's'); break; + case (2): string_append_c(asc, 'h'); break; + case (3): string_append_c(asc, 'z'); break; + case (4): string_append_c(asc, 'e'); break; + case (5): string_append_c(asc, 'q'); break; + case (6): string_append_c(asc, 'd'); break; + case (7): string_append_c(asc, 'x'); break; + } + } +#undef BGCOLOR + + /* foreground color */ +#define FGCOLOR(x) ((!(x & FSTR_NORMAL)) ? (x & FSTR_FOREMASK) : -1) + if (((reset || FGCOLOR(cur) != FGCOLOR(prev)) || (i && (prev & FSTR_BOLD) != (cur & FSTR_BOLD)))) { + string_append_c(asc, '%'); + switch ((cur & FSTR_FOREMASK)) { + case (0): string_append_c(asc, (cur & FSTR_BOLD) ? 'K' : 'k'); break; + case (1): string_append_c(asc, (cur & FSTR_BOLD) ? 'R' : 'r'); break; + case (2): string_append_c(asc, (cur & FSTR_BOLD) ? 'G' : 'g'); break; + case (3): string_append_c(asc, (cur & FSTR_BOLD) ? 'Y' : 'y'); break; + case (4): string_append_c(asc, (cur & FSTR_BOLD) ? 'B' : 'b'); break; + case (5): string_append_c(asc, (cur & FSTR_BOLD) ? 'M' : 'm'); break; /* | fioletowy | %m/%p | %M/%P | %q | */ + case (6): string_append_c(asc, (cur & FSTR_BOLD) ? 'C' : 'c'); break; + case (7): string_append_c(asc, (cur & FSTR_BOLD) ? 'W' : 'w'); break; + } + } +#undef FGCOLOR + } else { /* no color */ + if ((cur & FSTR_BOLD) && (reset || !(prev & FSTR_BOLD))) + string_append(asc, "%T"); + } + + /* str */ + if (str[i] == '%' || str[i] == '\\') + string_append_c(asc, '\\'); + string_append_c(asc, str[i]); + } + +/* reset, and return. */ + string_append(asc, "%n"); + return string_free(asc, 0); + +#undef prev +#undef cur +} + +static QUERY(ncurses_upgrade) { + const char *path; + FILE *f; + + window_t *wl; + +#ifdef USE_UNICODE + return 1; +#endif + + if (!(path = prepare_pathf("upgrade-ncurses.%d", getpid()))) + return 1; + + if (!(f = fopen(path, "w"))) + return 1; + + for (wl = windows; wl; wl = wl->next) { + ncurses_window_t *n = wl->private; + int i; + + if (!n) + continue; + + fprintf(f, "[window-dump-%d]\n", wl->id); + + for (i = n->backlog_size; i; i--) { + fstring_t *backlog = n->backlog[i-1]; + char *tmp; + + tmp = fstring_reverse(n->backlog[i-1]); + fprintf(f, "%ld %s\n", backlog->ts, tmp); + xfree(tmp); + } + } + + fclose(f); + return 0; +} + +static QUERY(ncurses_restore) { + + const char *path; + char *buf; + FILE *f; + + window_t *wl = NULL; + + if (!(path = prepare_pathf("upgrade-ncurses.%d", getpid()))) + return 1; + + if (!(f = fopen(path, "r"))) + return 1; + + while ((buf = read_file(f, 0))) { + fstring_t *fstr; + time_t ts; + int id; + + if (sscanf(buf, "[window-dump-%d]", &id) == 1) { + wl = window_exist(id); + continue; + } + + if (!wl) + continue; + + wl->lock++; + /* assuming file is correct! */ + + ts = strtol(buf, &buf, 10); + buf++; + fstr = fstring_new_format(buf); + fstr->ts = ts; + query_emit_id(&ncurses_plugin, UI_WINDOW_PRINT, &wl, &fstr); + + wl->lock--; + } + query_emit_id(NULL, UI_WINDOW_REFRESH); + + fclose(f); + return 0; +} + static QUERY(ncurses_setvar_default) { config_contacts_size = 9; /* szeroko okna kontaktw */ @@ -689,6 +867,8 @@ query_connect_id(&ncurses_plugin, CONFIG_POSTINIT, ncurses_postinit, NULL); query_connect_id(&ncurses_plugin, PROTOCOL_DISCONNECTING, ncurses_session_disconnect_handler, NULL); + query_connect_id(&ncurses_plugin, EKG_UPGRADE, ncurses_upgrade, NULL); + query_connect_id(&ncurses_plugin, EKG_RESTORE, ncurses_restore, NULL); /* redraw userlisty: */ /* podanie czegokolwiek jako data do ncurses_all_contacts_changed() powoduje wyzerowanie n->start */ ekg2-0.4~pre+20120506.1/contrib/patches/fix-mistaken-margin-left.diff000066400000000000000000000021431175142753400247000ustar00rootroot00000000000000Index: ekg/themes.c =================================================================== --- ekg/themes.c (wersja 4783) +++ ekg/themes.c (kopia robocza) @@ -466,6 +466,10 @@ for (i = 0; i < fill_length; i++) string_append_c(buf, fill_char); } + } else if (*p == '/' && p[1] == '|' && (p == format || p[-1] != '/')) { /* match /| but don't match //| */ + /* this sequence is marking res->margin_left */ + string_append(buf, "\033[666m"); + p++; } else string_append_c(buf, *p); p++; @@ -613,6 +617,10 @@ attr &= ~(FSTR_NORMAL+FSTR_BACKMASK); attr |= (cur - 40) << 3; } + + /* internal ekg2 sequence */ + if (cur == 666) + res->margin_left = j; } } // else debug("Invalid/unsupported by ekg2 ECMA-48 CSI seq? (npar: %d)\n", npar); /* sequence not ended with m */ @@ -622,15 +630,6 @@ if (str[i] == 13) continue; - if (str[i] == ('/') && str[i + 1] == ('|')) { - if (i == 0 || str[i - 1] != ('/')) { - res->margin_left = j; - i++; - continue; - } - continue; - } - if (str[i] == 9) { int k = 0, l = 8 - (j % 8); ekg2-0.4~pre+20120506.1/contrib/patches/jabber-dcc-fixups.diff000066400000000000000000000410201175142753400233630ustar00rootroot00000000000000Index: jabber_dcc.c =================================================================== --- jabber_dcc.c (wersja 4749) +++ jabber_dcc.c (kopia robocza) @@ -233,7 +233,7 @@ char *fulluid; if (!s->connected) continue; - if (!(session_check(s, 1, "jid"))) continue; + if (!(session_check(s, 1, "xmpp"))) continue; fulluid = saprintf("%s/%s", s->uid+5, j->resource); Index: commands.c =================================================================== --- commands.c (wersja 4753) +++ commands.c (kopia robocza) @@ -69,6 +69,7 @@ #include "jabber.h" #include "jabber_dcc.h" +#include "muc.h" const char *jabber_prefixes[2] = { "xmpp:", "tlen:" }; extern int config_jabber_disable_chatstates; /* in jabber.c */ @@ -2290,6 +2291,222 @@ return 0; } +static COMMAND(jabber_command_dcc) { + jabber_private_t *j = session_private_get(session); + // XXX + + if (!xstrncasecmp(params[0], "se", 2)) { /* send */ + struct stat st; + userlist_t *u; + dcc_t *d; + FILE *fd; + const char *fn; + + if (!params[1] || !params[2]) { + printq("not_enough_params", name); + return -1; + } + + if (!(fn = prepare_path_user(params[2]))) { + printq("generic_error", "path too long"); /* XXX? */ + return -1; + } + + if (!(u = userlist_find(session, get_uid(session, params[1])))) { + printq("user_not_found", params[1]); + return -1; + } + + if (!session_connected_get(session)) { + printq("not_connected"); + return -1; + } + + if ((u->status <= EKG_STATUS_NA) || !u->resources) { + printq("dcc_user_not_avail", format_user(session, u->uid)); + return -1; + } + + if (!stat(fn, &st) && !S_ISREG(st.st_mode)) { + printq("io_nonfile", params[2]); + return -1; + } + + if ((fd = fopen(fn, "r")) == NULL) { + printq("io_cantopen", params[2], strerror(errno)); + return -1; + } + + if (u->resources == NULL) { + // XXX + return -1; + } + + { + string_t sid = NULL; + jabber_dcc_t *p; + char *filename; + char *pathtmp; + char *touid; + + /* XXX, introduce function jabber_get_resource(u, input_uid); */ + touid = saprintf("%s/%s", u->uid, u->resources->name); + + d = dcc_add(session, touid, DCC_SEND, NULL); + d->filename = xstrdup(fn); + d->size = st.st_size; + + dcc_close_handler_set(d, jabber_dcc_close_handler); + + d->priv = p = xmalloc(sizeof(jabber_dcc_t)); + p->session = session; + p->req = saprintf("offer%d", dcc_id_get(d)); + + /* copied from iris/jabber/s5b.cpp (C) 2003 Justin Karneges under LGPL 2.1 */ + do { + /* generate hash like Psi do */ + int i; + + sid = string_init("s5b_"); + for (i = 0; i < 4; i++) { + int word = rand() & 0xffff; + int n; + + for (n = 0; n < 4; n++) { + int dgst = (word >> (n * 4)) & 0xf; /* from 0..9 -> '0'..'9', 10..15 - 'A'..'F' */ + + if (dgst < 10) string_append_c(sid, dgst + '0'); + else string_append_c(sid, dgst - 10 + 'a'); + } + } + debug_function("[jabber] jabber_command_dcc() hash generated: %s errors below are ok.\n", sid->str); + } while (jabber_dcc_find(NULL, NULL, sid->str) && !string_free(sid, 1)); /* loop, [if sid exists] + free string if yes */ + + p->sid = string_free(sid, 0); + p->sfd = -1; + p->fd = fd; + + /* XXX: introduce prepare_filename() */ + if ((pathtmp = xstrrchr(fn, '/'))) + pathtmp++; /* skip '/' */ + else pathtmp = (char*) fn; /* no '/' ok. */ + + filename = jabber_escape(pathtmp); /* escape string */ + + watch_write(j->send_watch, + "" + "" + "" + "" + "" + "" + "" +/* "" */ + "", p->req, d->uid+5, p->sid, st.st_size, filename); + xfree(filename); + xfree(touid); + } + return 0; + } + if (!xstrncasecmp(params[0], "g", 1) || !xstrncasecmp(params[0], "re", 2)) { /* get, resume */ + dcc_t *d = NULL; + list_t l; + + for (l = dccs; l; l = l->next) { + dcc_t *D = l->data; + userlist_t *u; + + if (!dcc_filename_get(D) || dcc_type_get(D) != DCC_GET) + continue; + + if (!params[1]) { + if (dcc_active_get(D)) + continue; + d = D; + break; + } + + if (params[1][0] == '#' && xstrlen(params[1]) > 1 && atoi(params[1] + 1) == dcc_id_get(D)) { + d = D; + break; + } + + if ((u = userlist_find(session, dcc_uid_get(D)))) { + if (!xstrcasecmp(params[1], u->uid) || (u->nickname && !xstrcasecmp(params[1], u->nickname))) { + d = D; + break; + } + } + } + + if (!d || !d->priv) { + printq("dcc_not_found", (params[1]) ? params[1] : ""); + return -1; + } + + if (dcc_active_get(d)) { + printq("dcc_receiving_already", dcc_filename_get(d), format_user(session, dcc_uid_get(d))); + return -1; + } + + if (xstrncmp(d->uid, "xmpp:", 5)) { + debug_error("%s:%d /dcc command, incorrect `%s`!\n", __FILE__, __LINE__, __(d->uid)); + printq("generic_error", "Use /dcc on correct session, sorry"); + return -1; + } + + { + jabber_dcc_t *p = d->priv; + session_t *s = p->session; + jabber_private_t *j = jabber_private(s); + char *filename; + + if (p->fd) { + debug_error("[jabber] p->fd: 0x%x\n", p->fd); + printq("generic_error", "Critical dcc error p->fd != NULL"); + return -1; + } + + filename = saprintf("%s/%s", config_dcc_dir ? config_dcc_dir : prepare_path("download", 1), d->filename); + debug("[jabber] DCC/GET Downloading file as: %s\n", filename); + /* XXX, sanity d->filename */ + + /* XXX, jesli to jest rget to plik moze istniec */ + while ((p->fd = fopen(filename, "r"))) { + filename = xrealloc(filename, xstrlen(filename)+3); + debug_error("[jabber] DCC/GET FILE ALREADY EXISTS APPENDING '.1': %s\n", filename); + + xstrcat(filename, ".1"); + + fclose(p->fd); + } + + if (!(p->fd = fopen(filename, "w"))) { + int err = errno; + debug_error("[jabber] DCC/GET CANNOT CREATE FILE: %s (%s)\n", filename, strerror(err)); + printq("dcc_get_cant_create", filename, strerror(err)); + return -1; + } + /* if resume fseek() to d->offset XXX */ + + printq("dcc_get_getting", format_user(session, dcc_uid_get(d)), filename); + + watch_write(j->send_watch, "" + "" + "" + "http://jabber.org/protocol/bytestreams" + "", d->uid+5, p->req); + } + return 0; + } + + if (!xstrncasecmp(params[0], "vo", 2)) { /* voice */ + printq("not_implemented"); + return -1; + } + return cmd_dcc(name, params, session, target, quiet); +} + void jabber_register_commands() { #define JABBER_ONLY SESSION_MUSTBELONG | SESSION_MUSTHASPRIVATE @@ -2319,6 +2536,8 @@ command_add(&jabber_plugin, "xmpp:chat", "!uU !", jabber_command_msg, JABBER_FLAGS_MSG, NULL); command_add(&jabber_plugin, "xmpp:connect", NULL, jabber_command_connect, JABBER_ONLY, NULL); command_add(&jabber_plugin, "xmpp:conversations", NULL, jabber_command_conversations, JABBER_FLAGS, NULL); + command_add(&jabber_plugin, "xmpp:dcc", "p uU f ?", jabber_command_dcc, JABBER_ONLY, + "send get resume voice close list"); command_add(&jabber_plugin, "xmpp:del", "!u", jabber_command_del, JABBER_FLAGS_TARGET, NULL); command_add(&jabber_plugin, "xmpp:disconnect", "r", jabber_command_disconnect, JABBER_ONLY, NULL); command_add(&jabber_plugin, "xmpp:dnd", "r", jabber_command_away, JABBER_ONLY, NULL); Index: jabber_handlers_iq_result.inc =================================================================== --- jabber_handlers_iq_result.inc (wersja 4749) +++ jabber_handlers_iq_result.inc (kopia robocza) @@ -951,22 +951,140 @@ watch_write(j->send_watch, "", j->id++); } +// XXX +JABBER_HANDLER_SET(jabber_handle_bytestreams_set) { + jabber_private_t *j = s->priv; + + char *uid = jabber_unescape(from); /* jid */ + char *sid = jabber_attr(n->atts, "sid"); /* session id */ +#if 0 /* unused */ + char *smode = jabber_attr(q->atts, "mode"); /* tcp, udp */ +#endif + dcc_t *d = NULL; + + if ((d = jabber_dcc_find(uid, NULL, sid))) { + /* w sumie jak nie mamy nawet tego dcc.. to mozemy kontynuowac ;) */ + /* problem w tym czy user chce ten plik.. etc.. */ + /* i tak to na razie jest jeden wielki hack, trzeba sprawdzac czy to dobry typ dcc. etc, XXX */ + xmlnode_t *node; + jabber_dcc_t *p = d->priv; + jabber_dcc_bytestream_t *b = NULL; + + list_t host_list = NULL, l; + struct jabber_streamhost_item *streamhost; + + if (d->type == DCC_SEND) { + watch_write(j->send_watch, + "Declined", d->uid+5, id); + return; + } + + p->protocol = JABBER_DCC_PROTOCOL_BYTESTREAMS; + + xfree(p->req); + p->req = xstrdup(id); + /* XXX, set our streamhost && send them too */ + for (node = n->children; node; node = node->next) { + if (!xstrcmp(node->name, "streamhost")) { + struct jabber_streamhost_item *newstreamhost = xmalloc(sizeof(struct jabber_streamhost_item)); + + newstreamhost->ip = xstrdup(jabber_attr(node->atts, "host")); /* XXX in host can be hostname */ + newstreamhost->port = atoi(jabber_attr(node->atts, "port")); + newstreamhost->jid = xstrdup(jabber_attr(node->atts, "jid")); + list_add(&host_list, newstreamhost); + } + } + l = host_list; +find_streamhost: + streamhost = NULL; + for (; l; l = l->next) { + struct jabber_streamhost_item *item = l->data; + struct sockaddr_in sin; + /* let's search the list for ipv4 address... for now only this we can handle */ + if ((inet_pton(AF_INET, item->ip, &(sin.sin_addr)) > 0)) { + streamhost = host_list->data; + break; + } + } + + if (streamhost) { + struct sockaddr_in sin; + int fd; + char socks5[4]; + + fd = socket(AF_INET, SOCK_STREAM, 0); + + sin.sin_family = AF_INET; + sin.sin_port = htons(streamhost->port); + inet_pton(AF_INET, streamhost->ip, &(sin.sin_addr)); + + if (connect(fd, (struct sockaddr *) &sin, sizeof(struct sockaddr_in)) == -1) { + /* let's try connect once more to another host? */ + debug_error("[jabber] dcc connecting to: %s failed (%s)\n", streamhost->ip, strerror(errno)); + goto find_streamhost; + } + p->sfd = fd; + + watch_add(&jabber_plugin, fd, WATCH_READ, jabber_dcc_handle_recv, d); + + p->priv_data.bytestream = b = xmalloc(sizeof(jabber_dcc_bytestream_t)); + b->validate = JABBER_DCC_PROTOCOL_BYTESTREAMS; + b->step = SOCKS5_CONNECT; + b->streamlist = host_list; + b->streamhost = streamhost; + + socks5[0] = 0x05; /* socks version 5 */ + socks5[1] = 0x02; /* number of methods */ + socks5[2] = 0x00; /* no auth */ + socks5[3] = 0x02; /* username */ + write(fd, (char *) &socks5, sizeof(socks5)); + } else { + list_t l; + + debug_error("[jabber] We cannot connect to any streamhost with ipv4 address.. sorry, closing connection.\n"); + + for (l = host_list; l; l = l->next) { + struct jabber_streamhost_item *i = l->data; + + xfree(i->jid); + xfree(i->ip); + } + list_destroy(host_list, 1); + + watch_write(j->send_watch, + "" + /* Psi: Could not connect to given hosts */ + "" + "", d->uid+5, id); + + print("dcc_error_refused", format_user(s, d->uid)); + + d->active = 1; /* hack to avoid sending 403 */ + dcc_close(d); /* zamykamy dcc */ + } + } +} + +/** + * jabber_handle_si_set() + * + * XEP-0096: SI File Transfer (http://xmpp.org/extensions/xep-0096.html) [1.1 2004-04-13] (iq:type='set' iq::si:xmlns='http://jabber.org/protocol/si')
+ */ JABBER_HANDLER_SET(jabber_handle_si_set) { xmlnode_t *p; + /* XXX, profiles, http://jabber.org/protocol/feature-neg */ + if (((p = xmlnode_find_child(n, "file")))) { /* JEP-0096: File Transfer */ - dcc_t *D; char *uin = jabber_unescape(from); - char *uid; char *filename = jabber_unescape(jabber_attr(p->atts, "name")); char *size = jabber_attr(p->atts, "size"); -#if 0 - xmlnode_t *range; /* unused? */ -#endif + + char *uid = xmpp_uid(uin); + + dcc_t *D; jabber_dcc_t *jdcc; - uid = xmpp_uid(uin); - jdcc = xmalloc(sizeof(jabber_dcc_t)); jdcc->session = s; jdcc->req = xstrdup(id); @@ -978,22 +1096,17 @@ dcc_size_set(D, atoi(size)); dcc_private_set(D, jdcc); dcc_close_handler_set(D, jabber_dcc_close_handler); -/* XXX, result - if ((range = xmlnode_find_child(p, "range"))) { - char *off = jabber_attr(range->atts, "offset"); - char *len = jabber_attr(range->atts, "length"); - if (off) dcc_offset_set(D, atoi(off)); - if (len) dcc_size_set(D, atoi(len)); + + if ((xmlnode_find_child(p, "range"))) { + /* file can be resumed */ } -*/ + print("dcc_get_offer", format_user(s, uid), filename, size, itoa(dcc_id_get(D))); xfree(uin); xfree(uid); xfree(filename); } - - } JABBER_HANDLER_RESULT(jabber_handle_si_result) { @@ -1062,6 +1175,63 @@ } else /* XXX */; } +static watch_t *jabber_watch_find(int fd, watch_type_t type) { + list_t l; + + for (l = watches; l; l = l->next) { + watch_t *w = l->data; + + if (w && w->plugin == &jabber_plugin && w->fd == fd && w->type == type) + return w; + } + return NULL; +} + +JABBER_HANDLER_RESULT(jabber_handle_bytestreams_result) { + char *uid = jabber_unescape(from); /* jid */ + dcc_t *d = NULL; + + xmlnode_t *used = xmlnode_find_child(n, "streamhost-used"); + jabber_dcc_t *p; + jabber_dcc_bytestream_t *b; + list_t l; + + if ((d = jabber_dcc_find(uid, id, NULL))) { + char *usedjid = (used) ? jabber_attr(used->atts, "jid") : NULL; + watch_t *w; + + p = d->priv; + b = p->priv_data.bytestream; + + for (l = b->streamlist; l; l = l->next) { + struct jabber_streamhost_item *item = l->data; + if (!xstrcmp(item->jid, usedjid)) { + b->streamhost = item; + } + } + debug_function("[STREAMHOST-USED] stream: 0x%x %d\n", b->streamhost, p->sfd); + d->active = 1; + + w = jabber_watch_find(p->sfd, WATCH_NONE); + + if (w && /* w->handler == jabber_dcc_handle_send && */ w->data == d) + w->type = WATCH_WRITE; + else { + debug_error("[jabber] %s:%d WATCH BAD DATA/NOT FOUND, 0x%x 0x%x 0x%x\n", __FILE__, __LINE__, w, w ? w->handler : NULL, w ? w->data : NULL); + /* XXX, SHOW ERROR ON __STATUS */ + dcc_close(d); + return; + } + + { /* activate stream */ + char buf[1]; + buf[0] = '\r'; + write(p->sfd, &buf[0], 1); + } + } + debug_function("[FILE - BYTESTREAMS] 0x%x\n", d); +} + JABBER_HANDLER_RESULT(jabber_handle_bind) { jabber_private_t *j = s->priv; @@ -1087,7 +1257,7 @@ { "mailbox", "google:mail:notify", jabber_handle_gmail_result_mailbox }, /* not done */ { "new-mail", "google:mail:notify", jabber_handle_iq_result_new_mail }, /* not done */ - { "si", NULL, jabber_handle_si_result }, /* not done */ + { "si", "http://jabber.org/protocol/si", jabber_handle_si_result }, /* not done */ { "query", "jabber:iq:last", jabber_handle_iq_result_last }, { NULL, "jabber:iq:privacy", jabber_handle_iq_result_privacy }, /* zaczete */ @@ -1102,6 +1272,8 @@ { NULL, "http://jabber.org/protocol/muc#owner", jabber_handle_iq_muc_owner }, /* bez ERROR */ { NULL, "http://jabber.org/protocol/vacation", jabber_handle_iq_result_vacation }, /* done, but not checked, without ERROR */ + { NULL, "http://jabber.org/protocol/bytestreams", jabber_handle_bytestreams_result }, + { "bind", "urn:ietf:params:xml:ns:xmpp-bind", jabber_handle_bind }, /* not done */ { "", NULL, NULL } @@ -1117,10 +1289,12 @@ { "new-mail", "google:mail:notify", jabber_handle_iq_set_new_mail }, - { "si", NULL, jabber_handle_si_set }, + { "si", "http://jabber.org/protocol/si", jabber_handle_si_set }, { "query", "jabber:iq:privacy", jabber_handle_iq_result_privacy }, /* XXX: przeniesc kod ktory przychodzi jako set do innej funkcji */ { NULL, "jabber:iq:roster", jabber_handle_iq_roster }, + + { NULL, "http://jabber.org/protocol/bytestreams", jabber_handle_bytestreams_set }, { "", NULL, NULL } }; ekg2-0.4~pre+20120506.1/contrib/patches/ncurses-screen-line-remove-some-fields.diff000066400000000000000000000113401175142753400274610ustar00rootroot00000000000000Index: plugins/ncurses/old.c =================================================================== --- plugins/ncurses/old.c (wersja 4158) +++ plugins/ncurses/old.c (kopia robocza) @@ -631,6 +631,7 @@ for (;;) { int word, width; int ts_len = 0; /* xstrlen(l->ts) */ + size_t len; /* wcslen(str) */ if (!i) res++; @@ -641,21 +642,12 @@ l->str = str; l->attr = attr; - l->len = xwcslen(str); + len = xwcslen(str); l->ts = NULL; l->ts_attr = NULL; l->backlog = i; l->margin_left = (!wrapping || margin_left == -1) ? margin_left : 0; - l->prompt_len = n->backlog[i]->prompt_len; - if (!n->backlog[i]->prompt_empty) { - l->prompt_str = n->backlog[i]->str.w; - l->prompt_attr = n->backlog[i]->attr; - } else { - l->prompt_str = NULL; - l->prompt_attr = NULL; - } - if (!w->floating && render_timestamp) { fstring_t *s = NULL; @@ -681,35 +673,35 @@ xfree(s); } - width = w->width - ts_len - l->prompt_len - n->margin_left - n->margin_right; + width = w->width - ts_len - n->backlog[i]->prompt_len - n->margin_left - n->margin_right; if ((w->frames & WF_LEFT)) width -= 1; if ((w->frames & WF_RIGHT)) width -= 1; - if (l->len < width) + if (len < width) break; if (w->nowrap) { - l->len = width; /* XXX, what for? for not drawing outside screen-area? ncurses can handle with it */ + len = width; /* XXX, what for? for not drawing outside screen-area? ncurses can handle with it */ if (str[width] == CHAR(' ')) { - l->len--; + len--; /* str++; attr++; */ } /* while (*str) { str++; attr++; } */ break; } - for (j = 0, word = 0; j < l->len; j++) { + for (j = 0, word = 0; j < len; j++) { if (str[j] == CHAR(' ')) word = j + 1; if (j == width) { - l->len = (word) ? word : width; + len = (word) ? word : width; if (str[j] == CHAR(' ')) { - l->len--; + len--; str++; attr++; } @@ -717,8 +709,8 @@ } } - str += l->len; - attr += l->len; + str += len; + attr += len; if (! *str) break; @@ -1115,6 +1107,8 @@ fix_trl=0; for (y = 0; y < height && n->start + y < n->lines_count; y++) { struct screen_line *l = &n->lines[n->start + y]; + CHAR_T *str2 = (n->start + y + 1 < n->lines_count) ? n->lines[n->start + y + 1].str : NULL; + fstring_t *backlog = n->backlog[l->backlog]; int cur_y = (top + y + fix_trl); int cur_x; @@ -1124,7 +1118,7 @@ if (( y == 0 ) && n->last_red_line && (n->backlog[l->backlog]->ts < n->last_red_line)) dtrl = 1; /* First line timestamp is less then mark. Mayby marker is on this page? */ - if (dtrl && (n->backlog[l->backlog]->ts >= n->last_red_line)) { + if (dtrl && (backlog->ts >= n->last_red_line)) { draw_thin_red_line(w, cur_y); if ((n->lines_count-n->start == height - (top - n->margin_top)) ) { /* we have stolen line for marker, so we scroll up */ @@ -1155,11 +1149,15 @@ mvwaddch(n->window, cur_y, cur_x, ' '); } - if (l->prompt_str) { - for (x = 0; x < l->prompt_len; x++, cur_x++) { - int attr = fstring_attr2ncurses_attr(l->prompt_attr[x]); - CHAR_T ch = ncurses_fixchar(l->prompt_str[x], &attr); + if (!backlog->prompt_empty && backlog->str.w) { + CHAR_T *prompt_str = backlog->str.w; + short *prompt_attr = backlog->attr; + int prompt_len = backlog->prompt_len; + for (x = 0; x < prompt_len; x++, cur_x++) { + int attr = fstring_attr2ncurses_attr(prompt_attr[x]); + CHAR_T ch = ncurses_fixchar(prompt_str[x], &attr); + wattrset(n->window, attr); if (!fixup && (l->margin_left != -1 && x >= l->margin_left)) @@ -1173,13 +1171,13 @@ } } - for (x = 0; x < l->len; x++, cur_x++) { + for (x = 0; l->str[x] && (&l->str[x] != str2); x++, cur_x++) { int attr = fstring_attr2ncurses_attr(l->attr[x]); CHAR_T ch = ncurses_fixchar(l->str[x], &attr); wattrset(n->window, attr); - if (!fixup && (l->margin_left != -1 && (x + l->prompt_len) >= l->margin_left)) + if (!fixup && (l->margin_left != -1 && (x + backlog->prompt_len) >= l->margin_left)) fixup = l->margin_left + config_margin_size; #if USE_UNICODE if (config_use_unicode) { Index: plugins/ncurses/old.h =================================================================== --- plugins/ncurses/old.h (wersja 4144) +++ plugins/ncurses/old.h (kopia robocza) @@ -26,15 +26,9 @@ void update_statusbar(int commit); struct screen_line { - int len; /* dugo linii */ - CHAR_T *str; /* tre */ short *attr; /* atrybuty */ - CHAR_T *prompt_str; /* tre promptu */ - short *prompt_attr; /* atrybuty promptu */ - int prompt_len; /* dugo promptu */ - char *ts; /* timestamp */ short *ts_attr; /* attributes of the timestamp */ ekg2-0.4~pre+20120506.1/contrib/patches/session-remove-fix-segv.diff000066400000000000000000000024261175142753400246060ustar00rootroot00000000000000Index: sessions.c =================================================================== --- sessions.c (wersja 4713) +++ sessions.c (kopia robocza) @@ -267,18 +267,21 @@ if (!(s = session_find(uid))) return -1; - if (s == session_current) - session_current = NULL; count = sessions_count(); for (w = windows; w; w = w->next) { - if (w->session == s) { - w->session = NULL; - if (count > 1) - window_session_cycle(w); - } + if (w->session == s && count > 1) + window_session_cycle(w); + + if (w->session == s) + window_session_set(w, NULL); } + + if (s == session_current) { /* shouldn't happen */ + session_current = NULL; + query_emit_id(NULL, SESSION_CHANGED); + } if (s->connected) command_exec_format(NULL, s, 1, ("/disconnect %s"), s->uid); @@ -309,7 +312,6 @@ } tmp = xstrdup(uid); - query_emit_id(NULL, SESSION_CHANGED); query_emit_id(NULL, SESSION_REMOVED, &tmp); xfree(tmp); Index: windows.c =================================================================== --- windows.c (wersja 4654) +++ windows.c (kopia robocza) @@ -883,7 +885,7 @@ void window_session_set(window_t *w, session_t *new_session) { static int lock; - if (!w || !new_session) /* XXX, new_session == NULL? */ + if (!w) return; if (w->session == new_session) ekg2-0.4~pre+20120506.1/contrib/perl/000077500000000000000000000000001175142753400165575ustar00rootroot00000000000000ekg2-0.4~pre+20120506.1/contrib/perl/audioscrobbler_bot.pl000066400000000000000000000146271175142753400227710ustar00rootroot00000000000000# Audioscrobbler BOT # Michal 'GiM' Spadlinski # 12-12-2005 # our $VERSION = "0.1"; our %EKG2 = ( authors => 'Michal Spadlinski', name => 'audioscrobbler', description => 'Audioscrobbler BOT', license => 'GPL', changed => 'Mon Dec 12 22:42:15 CET 2005' ); my $ignore_my_own = 0; ######################################################## use vars qw($VERSION %EKG2); use Ekg2; use Ekg2::Irc; require XML::Parser; require HTTP::Request; require LWP::UserAgent; my ($grab, $pid, $watch, $rh, $wh); my @table; # Simple audioscrobbler XML parser sub cogra { $_ = shift; $_ =~ s/[\%\\\/\..]//g; @table = (); $grab = 0; my $url = "http://ws.audioscrobbler.com/1.0/user/$_/recenttracks.xml"; my $ua = LWP::UserAgent->new(agent => "GiM_z_hacka_perla_browsera_magiczna/6.66"); my $resp = $ua->get($url); return 0 if (! $resp->is_success); my $parser = new XML::Parser(ErrorContext => 2); $parser->setHandlers(Char => \&char_handler, Start => \&start_handler); $parser->parse($resp->content); return $table[13]?($table[5]." - ".$table[2].", ".localtime($table[13])):0; } sub start_handler { shift; $a = shift; $grab++ if ($a =~ /^track/); shift; # ivil hack ;) push @table,shift() if (($grab == 1) and ($a =~ /^date/)); } sub char_handler { shift; $_ = shift; push @table,$_ if $grab == 1; } sub cmd_ziom { my $sess = Ekg2::session_current; my $win = Ekg2::window_current; } ### BEGIN code stoling from: Ewelinker [ewelirssi.pl] by Maciek 'japhy' Pasternacki our %shifts = ( '`'=>'~', '~'=>'`', '1'=>'!', '!'=>'1', '2'=>'@', '@'=>'2', '3'=>'#', '#'=>'3', '4'=>'$', '$'=>'4', '5'=>'%', '%'=>'5', '6'=>'^', '^'=>'6', '7'=>'&', '&'=>'7', '8'=>'*', '*'=>'8', '9'=>'(', '('=>'9', '0'=>')', ')'=>'0', '-'=>'_', '_'=>'-', '='=>'+', '+'=>'=', '['=>'{', '{'=>'[', ']'=>'}', '}'=>']', ';'=>':', ':'=>';', "'"=>'"', '"'=>"'", ','=>'<', '<'=>',', '.'=>'>', '>'=>'.', '/'=>'?', '?'=>'/' ); srand ($$ ^ time()); sub maybe { my ( $prob, $rx, $subs, $rand, $const ); if ( $#_==2 ) { ( $prob, $rx, $subs ) = @_; ( $rand, $const ) = ( 0, 1 ); } elsif ( $#_==4 ) { ( $prob, $rx, $subs, $rand, $const ) = @_; } else { die "maybe: dupa\n"; } s/$rx/rand()<$prob?($subs)x(rand($rand)+$const):$&/ge; } sub ewelize { $_ = lc shift; maybe .75, qr/n[a][l]em/, 'uem'; maybe .75, qr/n[e][l]am/, 'uam'; maybe .66, qr/\B/, 'on'; maybe .66, qr/\b/, 'om'; maybe .66, qr/sz/, 'sh'; maybe .66, qr/cz/, 'ch'; maybe .66, qr/ii\b/, 'ji'; maybe .50, qr/o\b/, 'om'; maybe .66, qr/(?<=\b[cdnt])o/, 'io'; maybe .10, qr/(?<=[cdnt])o/, 'io'; maybe .33, qr/u/, 'oo'; maybe .10, qr/u/, 'o'; maybe .60, qr//, 'u'; maybe .50, qr/\B/, 'en'; maybe .50, qr/\b/, 'em'; maybe .50, qr//, 'oo'; maybe .50, qr//, 'rz'; maybe .33, qr/c(?=[^h])/, 's'; maybe .33, qr/w/, 'ff'; maybe .20, qr/ch/, 'f'; maybe .10, qr/ch/, 'ff'; maybe .66, qr/\!/, '!', 10, 2; maybe .50, qr/\?/, '?', 5, 2; maybe .75, qr/,/, ""; maybe .50, qr/(?<=\w{4})\b\.\B/, ""; maybe .50, qr/(?<=\w{4})\b\.\B/, ""; maybe .50, qr/(?<=\w{4})\b\.\B/, ""; maybe .05, qr/(?<=\w{4})\b\.\B/, ", w p\x69\x7a\x64e palec."; maybe .05, qr/(?<=\w{4})\b\.\B/, ', w zombek czesany.'; maybe .05, qr/(?<=\w{4})\b\.\B/, ", w \x63\x68uja wafla."; maybe .05, qr/(?<=\w{4})\b\.\B/, ", w morde za\x6a\x65\x62\x61\x6eego jea."; maybe .33, qr/K\x55\x52\x57A/, 'HY', 5, 3; s/\<((HY)+)\>/lc ", $1,"/eg; s/\/, \x6b\x75\x72wa,/g; s/\/, i \x63\x68uj./g; s/\/, w p\x69\x7a\x64u./g; s/\/, i dupa./g; maybe .10, qr/\x6b\x75\x72wa/, 'kuffa'; maybe .10, qr/\x6b\x75\x72wa/, 'kuwa'; maybe .33, qr/\x63\x68uj/, '\x68uj'; maybe .15, qr/c?\x68uj/, 'ciul'; s/\W/(defined($shifts{"$&"})&&(rand()<.10))?$shifts{$&}:$&/eg; s/(\w)(\w)/rand()<.05?"$2$1":$&/eg; s/./rand()<.66?lc$&:uc$&/eg; return $_; } ### END code stoling from: Ewelinker [ewelirssi.pl] by Maciek 'japhy' Pasternacki sub cogra_common { my ($session, $uid, $txt) = @_; Ekg2::debug("cogra_common() $session $uid $txt\n"); ## if uid undef. cogra_common executed from command. if ($txt =~ /(cogra|cogr4|c0gr4) ([a-zA-Z0-9_\-^]*)/) { pipe($rh, $wh); $pid = fork(); if (!defined($pid)) { Ekg2::echo("fork failed"); close($rh); close($wh); return 1; } if ($pid > 0) { close($wh); $watch = Ekg2::watch_add(fileno($rh), WATCH_READ, 'pipe_watcher', $rh); return; } close($rh); $juzer = $2; $z = cogra($2); if ($z) { srand ($$ ^ time()); if ($1 =~ /(cogr4|c0gr4)/) { $z = ewelize($z); } if ($1 =~ /c0gr4/) { $z =~ s/./sprintf "\003%02d$&\003", rand(15)/eg; $z =~ s/,/,,/g; } # XXX, GiM, append session to print() if ($uid) { print($wh "/msg ". $from ." $juzer playz: $z"); } else { print($wh "/echo $juzer playz: $z"); } } else { print($wh 0); } close($wh); exit(1); } } #bindings for ekg2 sub handle_message { my ($session, $uid, $rcpt, $text, $format, $send, $class) = @_; $tclass = $$class; $txt = $$text; $tclass -= EKG_NO_THEMEBIT if ($tclass & EKG_NO_THEMEBIT); return 1 if ($ignore_my_own && ($tclass | EKG_MSGCLASS_SENT || $tclass | EKG_MSGCLASS_SENT_CHAT)); return 1 if (! ($$session =~ /^irc:/)); # we bind protocol-message not irc-protocol-message # which can contain some ugly characters and cause errors # I'm currently disconnected from network so cannot check # which characters are allowed. cogra_common($$session, $$uid, $txt); return 1 } sub pipe_watcher { my ($type, $rhfd, $watch, $rh) = @_; my ($sess) = Ekg2::session_current; my ($wind) = Ekg2::window_current; return if ($type); my $text = <$rh>; close($rh); undef $watch; ## XXX, Ekg2::command_exec() if ($text) { Ekg2::command("$text"); } else { Ekg2::echo("user's not listening!"); } return -1; } sub cmd_cogra { my ($name, $args) = @_; cogra_common(Ekg2::session_current->{uid}, undef, "$name $args"); } Ekg2::handler_bind('protocol-message', 'handle_message'); Ekg2::command_bind('cogra', 'cmd_cogra'); Ekg2::command_bind('c0gr4', 'cmd_cogra'); 1; ekg2-0.4~pre+20120506.1/contrib/perl/autoop.pl000077500000000000000000000332721175142753400204350ustar00rootroot00000000000000use Ekg2; use Ekg2::Irc; use strict; our $VERSION = '0.01'; our %EKG2 = ( authors => 'Wieslaw Ochminski', contact => 'wiechu@wiechu.com', name => 'autoop', description => 'Auto op the good guys.', license => 'GPL', changed => '2011-01-12', ); ############################################################################## # # Configure # my $custom_SNS = ''; # set your namespace for script, 'A:' for example ############################################################################## # # Global variables # my $SNS; # script namespace my $revenge; my $debug; my %ops; my %voice; my $ekg2_display_crap; ############################################################################## # # Functions # #------------------------------ sub cprint($) { my $format = shift; my $wid = $ekg2_display_crap ? Ekg2::window_current->{id} : 1; Ekg2::print($wid, $format); } #------------------------------ sub debug($) { return unless $debug; my ($txt) = @_; Ekg2::print(0, $txt); } #------------------------------ sub lceq($$) { return lc(shift) eq lc(shift); } #------------------------------ sub display_hostident_list($$) { my ($h, $msg) = @_; foreach my $s (sort keys %$h) { foreach my $c (sort keys %{$h->{$s}}) { cprint("%n(%B$s%n) %T$msg%T channel %Y$c "); foreach my $i (sort keys %{$h->{$s}->{$c}}) { my $list; foreach my $s (match_sessions($s)) { foreach my $p (Ekg2::Irc::session2server(Ekg2::session_find($s))->people) { next unless (grep /^$i$/, $p->{ident}.'@'.$p->{hostname}); $list.= '%n,' if $list; $list.= '%c'.$p->{nick}; } } $list = ' %n['.$list.'%n]' if $list; cprint("\t%g".unescape($i).$list); } } } } #------------------------------ sub is_user_mode($$$;$) { my ($sessid, $chan, $sign, $nick) = @_; return 0 unless my $server = Ekg2::Irc::session2server(Ekg2::session_find($sessid)); $nick = $server->{nick} unless $nick; foreach my $user ($server->people()) { next unless (lceq($nick, $user->{nick})); foreach my $ch ($user->channels()) { return $ch->{sign} eq $sign if (lceq($ch->{channel}->{name}, $chan)); } } return 0; } #------------------------------ sub amiop($$) { my ($sessid, $chan) = @_; is_user_mode($sessid, $chan, '@'); } #------------------------------ sub is_channel_name($) { return (shift =~ /^[#\!]/); # XXX SOP(PREFIX) } #------------------------------ sub is_irc_sess_uid($) { return (shift =~ /^irc:/); } #------------------------------ sub escape($) { my ($t) = @_; $t =~ s/\./\\./g; $t =~ s/\*/.+/g; return $t; } #------------------------------ sub unescape($) { my ($t) = @_; $t =~ s/\\\././g; $t =~ s/\.\+/*/g; return $t; } #------------------------------ sub check_identhost($$$$) { my ($sessid, $chan, $ihost, $hash) = @_; my (@list) = ( ( keys %{$hash->{'irc:*'}->{'*'}}), ( keys %{$hash->{'irc:*'}->{$chan}}), ( keys %{$hash->{$sessid}->{'*'}}), ( keys %{$hash->{$sessid}->{$chan}}) ); # remove dups my %tmp; @tmp{@list} = (); foreach my $reg (keys %tmp) { debug("${SNS}check_identhost() check: $reg"); return 1 if grep /^$reg$/, $ihost; } return 0; } #------------------------------ sub match_sessions($) { my ($sessid) = @_; return $sessid unless (lceq($sessid, 'irc:*')); my @sess; foreach my $s (Ekg2::sessions) { next unless is_irc_sess_uid($s->{uid}); push @sess, $s->{uid}; } return @sess; } #------------------------------ sub identhost_find($$) { my ($sessid, $nick) = @_; foreach my $sess (match_sessions($sessid)) { my $server = Ekg2::Irc::session2server(Ekg2::session_find($sess)); foreach my $user ($server->people()) { next unless lceq($user->{nick}, $nick); return $user->{ident}.'@'.$user->{hostname}; } } return } #------------------------------ sub get_users_by_nick($$) { my ($server, $channel) = @_; my %h; foreach my $user ($server->people()) { foreach my $ch ($user->channels()) { next unless lceq($ch->{channel}->{name}, $channel); $h{$user->{nick}}->{identhost} = $user->{ident} . '@' . $user->{hostname} } } return %h; } #------------------------------ sub smart_set_mode($$$$) { my ($sessid, $cmd, $channel, $nick) = @_; my $plus = ! ($cmd =~ /^de/i); my $sign = ($cmd =~ /op$/i) ? '@' : '+'; my $mode = is_user_mode($sessid, $channel, $sign, $nick); return unless ($plus ^ $mode); Ekg2::command_exec('', Ekg2::session_find($sessid), "/$cmd $channel $nick"); } #------------------------------ sub exec_new_regexp($$$$) { my ($sessid, $cmd, $chan, $regexp) = @_; my $allchan = $chan eq '*'; debug("%B${SNS}exec_new_regexp() $cmd, $sessid, $chan, ".unescape($regexp)); return unless my $server = Ekg2::Irc::session2server(Ekg2::session_find($sessid)); foreach my $user ($server->people()) { foreach my $ch ($user->channels()) { next if lceq($server->{nick}, $user->{nick}); # it's me! my $ch_n = $ch->{channel}->{name}; next unless ($allchan || lceq($ch_n, $chan)); next unless grep /^$regexp/, $user->{ident}.'@'.$user->{hostname}; next unless amiop($sessid, $ch_n); smart_set_mode($sessid, $cmd, $ch_n, $user->{nick}); } } } #------------------------------ sub welcome(@) { return unless Ekg2::variable_find($SNS.'welcome')->{value}; # XXX cprint("%> %|%GAutoop\n%G/${SNS}help\t%nfor more information\n"); } #------------------------------ sub parse_op_voice($$) { my ($vname, $hash) = @_; my $tmp = Ekg2::variable_find($SNS.$vname)->{value}; my (@a) = split ' +', $tmp; %{$hash} = (); while (@a) { my $sess = shift @a; my $chan = shift @a; my $list = shift @a; foreach my $h (split ',', $list) { next unless $h; $h = escape($h); $hash->{$sess}->{$chan}->{$h} = 1; } } } #------------------------------ ############################################################################## # # Variable handlers # #------------------------------ sub parse_op() { parse_op_voice('__op', \%ops); } #------------------------------ sub parse_voice() { parse_op_voice('__voice', \%voice); } #------------------------------ sub bool_var() { # variable acts like bool my ($name, $val) = @_; my $v = Ekg2::variable_find($name); my $x = 0 + !!$val; $v->set($x) if ($v && ($x ne $v->{value})); } #------------------------------ sub int_var() { # variable acts like integer my ($name, $val) = @_; my $v = Ekg2::variable_find($name); my $x = 0 + $val; $v->set($x) if ($v && ($x ne $v->{value})); } #------------------------------ sub variable_changed() { my ($name) = ${$_[0]}; my $val; if (lceq($name, 'display_crap')) { $ekg2_display_crap = Ekg2::variable_find('display_crap')->{value}; } elsif (lceq($name, $SNS.'debug')) { $debug = Ekg2::variable_find($name)->{value}; } elsif (lceq($name, $SNS.'revenge')) { $revenge = Ekg2::variable_find($name)->{value}; } } ############################################################################## # # Commands # sub command_op() { my ($cmd, $params) = @_; my ($hash, $nlist, $change, $delete); my $exec = 1; my ($sessid, $chan) = (Ekg2::session_current->{uid}, '#*'); return cprint("%! %n(%B$cmd%n) %RError! %nBrak parametrow. %nZobacz %T/${SNS}help") unless (length($params)); # check current window my $a = Ekg2::window_current->{target}; $chan = $a if ($a =~ s/^irc:(.+)$/$1/ && is_channel_name($a)); # default is current chanel my (@params) = split " +", $params; foreach my $p (@params) { if ($p eq '-q') { $exec = 0; } elsif ( is_channel_name($p) ) { $chan = $p; } elsif ( is_irc_sess_uid($p) ) { $sessid = $p; } else { $nlist .= ",$p"; } } return cprint("%! %n(%B$cmd%n) %RError! %n%T$sessid%R is not irc session") unless is_irc_sess_uid($sessid); $nlist =~ s/^,//; $nlist =~ s/\birc://g; $chan = '*' if ($chan eq '#*'); $cmd =~ s/^$SNS//; $delete = ($cmd =~ s/^de//i); $hash = lceq($cmd, 'op') ? \%ops : \%voice; foreach my $nick (split(',', $nlist)) { my $identhost = ( $nick =~ /@/ ) ? $nick : identhost_find($sessid, $nick); unless ($identhost) { cprint("%! %n(%B${SNS}$cmd%n) %rWarning: unknown user %Y%$nick%n (session:%T$sessid%n, channel:%T$chan%n)\n"); next; } my $regexp = escape($identhost); if ( !( $delete ^ ($hash->{$sessid}->{$chan}->{$regexp}) ) ) { if ($delete) { delete $hash->{$sessid}->{$chan}->{$regexp}; # XXX msg # exec_new_regexp() here? } else { $hash->{$sessid}->{$chan}->{$regexp} = 1; exec_new_regexp($sessid, $cmd, $chan, $regexp) if $exec; # XXX msg } $change = 1; } else { if ($delete) { # XXX msg } else { # XXX msg } } } return unless $change; # store variables my $str; foreach my $s (sort keys %$hash) { foreach my $c (sort keys %{$hash->{$s}}) { my $list; for my $i (sort keys %{$hash->{$s}->{$c}}) { $list .= ',' if $list; $list .= unescape($i); } if ($list) { $str .= ' ' if $str; $str .= "$s $c $list"; } else { delete $hash->{$s}->{$c}; delete $hash->{$s} unless keys %{$hash->{$s}}; } } } my $v = Ekg2::variable_find($SNS.'__'.$cmd); $v->set($str) if $v; } #------------------------------ sub command_list() { my ($name, $params) = @_; my ($all, $lo, $lv); $all = 1 unless $params; foreach my $p (split ' +', $params) { $all |= ($p eq '-a') || ($p eq '--all'); $lo |= ($p eq '-o') || ($p eq '--op'); $lv |= ($p eq '-v') || ($p eq '--voice'); } return cprint("%! %n(%B$name%n)%r unknown option. %nSee %T/${SNS}help") unless ($all||$lo||$lv); display_hostident_list(\%ops, 'Auto op') if ($all || $lo); display_hostident_list(\%voice, 'Auto voice') if ($all || $lv); } #------------------------------ sub command_help() { my ($name, $param) = @_; # XXX if (grep /^$param$/, qw/op deop voice devoice/) { cprint("Command %T/${SNS}$param\t[session] [channel] [nick|indent\@host]"); cprint(" session - ... XXX"); return; } # XXX cprint("%>%1help %GCommands:%n %T/${SNS}op\t\t[session] [channel] [nick|indent\@host] %T/${SNS}voice\t[session] [channel] [nick|indent\@host] %T/${SNS}deop\t[session] [channel] [nick|indent\@host] %T/${SNS}devoice\t[session] [channel] [nick|indent\@host] %T/${SNS}list\t%n[%g-a%n%T|%g-o%n%T|%g-v%n] %T/${SNS}help %GVariables:%n %T/${SNS}welcome %T/${SNS}debug %T/${SNS}revenge See %T/${SNS}help [command]%n ..."); } ############################################################################## # # Handlers # sub join_hanler() { my ($sessid, $chan, $nick, $isour, $identhost) = map $$_, @_; my $amiop = amiop($sessid, $chan); debug("%B${SNS}join_hanler(".($amiop ? '@' : '').") $sessid, $chan, $nick, $isour, $identhost"); return 0 if ($isour || !$amiop); smart_set_mode($sessid, 'op', $chan, $nick) if (check_identhost($sessid, $chan, $identhost, \%ops)); smart_set_mode($sessid, 'voice', $chan, $nick) if (check_identhost($sessid, $chan, $identhost, \%voice)); return 0; } #------------------------------ sub mode_hanler() { my ($sessid, $nickihost, $channel, $plus, $mode, $param) = map $$_, @_; return 0 unless my $server = Ekg2::Irc::session2server(Ekg2::session_find("$sessid")); my ($who, $ihost) = split('!', $nickihost); return 0 if lceq($server->{nick}, $who); # it's my set mode my $amiop = amiop($sessid, $channel); if ($plus && ($mode eq 'o') && lceq($server->{nick}, $param) && !$amiop) { debug("I'm $channel op now!"); my %users = get_users_by_nick($server, $channel); foreach my $nick (keys %users) { my $identhost = $users{$nick}->{identhost}; smart_set_mode($sessid, 'op', $channel, $nick) if (check_identhost($sessid, $channel, $identhost, \%ops)); smart_set_mode($sessid, 'voice', $channel, $nick) if (check_identhost($sessid, $channel, $identhost, \%voice)); } return 0; } return 0 unless $amiop; if (!$plus && ($mode =~ /[ov]/)) { my %users = get_users_by_nick($server, $channel); if ($users{$param}) { my $sob = 0; $sob = ($revenge==1) ? !check_identhost($sessid, $channel, identhost_find($sessid, $who), \%ops) : 1 if $revenge; my $identhost = $users{$param}->{identhost}; if (($mode eq 'o') && check_identhost($sessid, $channel, $identhost, \%ops)) { smart_set_mode($sessid, 'deop', $channel, $who) if $sob; Ekg2::command_exec('', Ekg2::session_find($sessid), "/op $channel $param"); } if (($mode eq 'v') && check_identhost($sessid, $channel, $identhost, \%voice)) { smart_set_mode($sessid, 'deop', $channel, $who) if $sob; Ekg2::command_exec('', Ekg2::session_find($sessid), "/voice $channel $param"); } } } return 0; } ############################################################################## # # Init # { if ($custom_SNS eq '') { # script name space is script name $_[0] =~ /^.+:([^:]+)$/; $SNS = "$1:"; } else { $SNS = $custom_SNS; } # # variables # Ekg2::variable_add_ext($SNS.'welcome', '1', 'bool_var'); Ekg2::variable_add_ext($SNS.'debug', '0', 'bool_var'); Ekg2::variable_add_ext($SNS.'revenge', '0', 'int_var'); Ekg2::variable_add_ext($SNS.'__op', '', 'parse_op'); Ekg2::variable_add_ext($SNS.'__voice', '', 'parse_voice'); # # commands # Ekg2::command_bind_ext($SNS.'op', 'pu pu pu pu', '-q', 'command_op'); Ekg2::command_bind_ext($SNS.'deop', 'pu pu pu pu', '-q', 'command_op'); Ekg2::command_bind_ext($SNS.'voice', 'pu pu pu pu', '-q', 'command_op'); Ekg2::command_bind_ext($SNS.'devoice', 'pu pu pu pu', '-q', 'command_op'); Ekg2::command_bind_ext($SNS.'help', 'p', 'op deop voice devoice list', 'command_help'); Ekg2::command_bind_ext($SNS.'list', 'p', '-a --all -o --op -v --voice', 'command_list'); # # handlers # Ekg2::handler_bind('irc-join', 'join_hanler'); Ekg2::handler_bind('irc-mode', 'mode_hanler'); Ekg2::handler_bind('variable-changed', 'variable_changed'); $ekg2_display_crap = Ekg2::variable_find('display_crap')->{value}; $debug = Ekg2::variable_find($SNS.'debug')->{value}; $revenge = Ekg2::variable_find($SNS.'revenge')->{value}; parse_op(); parse_voice(); welcome(@_); } # Make perl happy 1; ekg2-0.4~pre+20120506.1/contrib/perl/bitly.pl000066400000000000000000000122571175142753400202460ustar00rootroot00000000000000use Ekg2; use LWP::UserAgent; use URI::Escape; use strict; use vars qw($VERSION %EKG2); our $VERSION = "1.10"; our %EKG2 = ( authors => 'Jakub Łaszczyński', contact => 'jakub.laszczynski@gmail.com', name => 'bitly for ekg2', description => 'shortens urls for incoming messages', license => 'GNU GPL', changed => '2012-02-24' ); Ekg2::variable_add( 'bitly_login', '' ); Ekg2::variable_add( 'bitly_apikey', '' ); Ekg2::variable_add( 'bitly_timeout', '5' ); Ekg2::variable_add( 'bitly_length', '30' ); Ekg2::variable_add( 'bitly_pagesizelimit', '102400' ); Ekg2::variable_add( 'bitly_debug', '0' ); my $bitly_login = Ekg2::variable_find('bitly_login')->{value}; my $bitly_apikey = Ekg2::variable_find('bitly_apikey')->{value}; my $length = Ekg2::variable_find('bitly_length')->{value}; my $timeout = Ekg2::variable_find('bitly_timeout')->{value}; my $pagesizelimit= Ekg2::variable_find('bitly_pagesizelimit')->{value}; my $debug = Ekg2::variable_find('bitly_debug')->{value}; Ekg2::handler_bind( 'protocol-message-received', 'shortenline' ); Ekg2::handler_bind( 'variable-changed', 'variable_changed' ); welcome(@_); sub bitly() { my $lwp = LWP::UserAgent->new; $lwp->timeout($timeout); $lwp->max_size($pagesizelimit); my $url = shift; my $window = shift; $url = uri_escape($url); my $api_src = 'https://api-ssl.bit.ly/v3/shorten?login=' . $bitly_login . '&apiKey=' . $bitly_apikey . '&format=txt&longUrl=' . $url; my $response = $lwp->get($api_src); Ekg2::debug("BITLY API->$api_src\n") if ($debug); if ( $response->is_success ) { my $url_bitly = $response->decoded_content; $url_bitly =~ s/\n//g; Ekg2::debug("BITLY SHORTENED->$url_bitly\n") if ($debug); my $get_title = $lwp->get($url_bitly); if ( $get_title->is_success ) { $get_title = $get_title->decoded_content; $get_title =~ s/(\s+|\n)/ /g; $get_title =~ /(.*)<\/title>/; Ekg2::Window::print( $window, "Page title: $1" ) if $1; } Ekg2::Window::print( $window, "Shortened url: $url_bitly" ); } else { my $errstr = $response->status_line; Ekg2::debug("An error occurred while making the HTTP Request: $errstr\n") if ($debug); Ekg2::Window::print( $window, "An error occurred while making the HTTP Request: $errstr\n" ); } } sub shortenline() { my ( $session, $uid, $rcpts, $ptext ) = map $$_, @_; Ekg2::debug("SESSION-> $session\n") if ($debug); Ekg2::debug("UID-> $uid\n") if ($debug); Ekg2::debug("RCPTS-> $rcpts\n") if ($debug); Ekg2::debug("PTEXT-> $ptext\n") if ($debug); Ekg2::debug("APIKEY-> $bitly_apikey\n") if ($debug); Ekg2::debug("LOGIN-> $bitly_login\n") if ($debug); Ekg2::debug("LENGTH-> $length\n") if ($debug); Ekg2::debug("TIMEOUT-> $timeout\n") if ($debug); Ekg2::command_exec( '', Ekg2::session_find($session), "/query $uid" ) unless Ekg2::window_find($uid); my $window = Ekg2::window_find($uid); while ( $ptext =~ m|(http.*?://([^\s)\"](?!ttp:))+)|g ) { my $url = $&; Ekg2::debug("DETECTED URL-> $url\n") if ($debug); Ekg2::debug( "URL-> URLLENGTH: " . length($url) . " LENGTH: $length\n" ) if ($debug); if ( length($url) > $length ) { Ekg2::debug("URL-> LONGER\n") if ($debug); &bitly( $url, $window ); } } } sub variable_changed() { my ($name) = ${ $_[0] }; if ( $name eq 'bitly_login' ) { $bitly_login = Ekg2::variable_find('bitly_login')->{value}; Ekg2::debug("BITLY LOGIN CHANGED-> $bitly_login\n") if ($debug); } elsif ( $name eq 'bitly_apikey' ) { $bitly_apikey = Ekg2::variable_find('bitly_apikey')->{value}; Ekg2::debug("BITLY APIKEY CHANGED-> $bitly_apikey\n") if ($debug); } elsif ( $name eq 'bitly_length' ) { $length = Ekg2::variable_find('bitly_length')->{value}; Ekg2::debug("BITLY LENGTH CHANGED-> $length\n") if ($debug); } elsif ( $name eq 'bitly_timeout' ) { $timeout = Ekg2::variable_find('bitly_timeout')->{value}; Ekg2::debug("BITLY TIMEOUT CHANGED-> $timeout\n") if ($debug); } elsif ( $name eq 'bitly_pagesizelimit' ) { $pagesizelimit = Ekg2::variable_find('bitly_pagesizelimit')->{value}; Ekg2::debug("BITLY PAGESIZELIMIT CHANGED-> $pagesizelimit\n") if ($debug); } elsif ( $name eq 'bitly_debug' || $name eq 'debug' ) { $debug = Ekg2::variable_find('debug')->{value} && Ekg2::variable_find('bitly_debug')->{value}; Ekg2::debug("DEBUG VALUE CHANGED-> $debug\n"); } } sub welcome(@) { return if Ekg2::variable_find('bitly_login')->{value}; Ekg2::print( 1, "bitly.pl for EKG2.\n" ); Ekg2::print( 1, "This is automatic URL shortener using bitly API.\n" ); Ekg2::print( 1, "To use it obtain API key by registering on bit.ly website,\n" ); Ekg2::print( 1, "and then set proper bitly_ variables in ekg2 config.\n\n" ); Ekg2::print( 1, "After such setup all received URLs which are longer then.\n" ); Ekg2::print( 1, "bitly_length will be shortened." ); } return 1; �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/contrib/perl/cycki.pl�������������������������������������������������������0000664�0000000�0000000�00000001103�11751427534�0020211�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������use vars qw($VERSION %EKG2); use Ekg2; use Ekg2::Irc; our $VERSION = '0.1'; our %EKG2 = ( authors => 'Wieslaw Ochminski', contact => 'wiechu@wiechu.com', description => 'All lights to cycki', license => 'GPL', changed => '2010-11-28', ); my $C = "\003"; # Ctrl-C my $cycki = "${C}5(${C}4*${C}5)(${C}4*${C}5)\017"; sub cycki { my ($sess, $from, $dest, $text, $to_us) = @_; $$to_us = 1 if ( $$text =~ s/^(.*)(\bcycki\b)(.*)$/$1$cycki$3/i ); } Ekg2::handler_bind("irc-privmsg", 'cycki'); Ekg2::handler_bind("irc-notice", 'cycki'); 1; �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/contrib/perl/dns.pl���������������������������������������������������������0000664�0000000�0000000�00000011247�11751427534�0017705�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# /DNS <nick>|<host>|<ip> ... use Ekg2; use Ekg2::Irc; use strict; use Socket; use POSIX; our $VERSION = "2.1"; our %EKG2 = ( authors => 'Timo Sirainen', name => 'dns', description => '/DNS <nick>|<host>|<ip> ...', license => 'Public Domain', changed => 'Sun Mar 10 23:23 EET 2002' ); # rewriten to ekg2 perl script by darkjames @ 2005-08-29 my (%resolve_hosts, %resolve_nicks, %resolve_print); # resolve queues my $userhosts; # number of USERHOSTs currently waiting for reply my $lookup_waiting; # 1 if we're waiting a reply for host lookup # for the current host lookup my ($print_server, $print_host, $print_name, @print_ips); my ($input_skip_next, $input_query); my $pipe_tag; sub cmd_dns { my ($name, $nicks) = @_; return if !$nicks; my ($server) = Ekg2::session_current; my ($ircserver) = Ekg2::Irc::session2server($server); # get list of nicks/hosts we want to know my $tag = !$ircserver ? undef : $ircserver->{server}; my $ask_nicks = ""; my $print_error = 0; foreach my $nick (split(" ", $nicks)) { $nick = lc($nick); if ($nick =~ /[\.:]/) { # it's an IP or hostname $resolve_hosts{$nick} = $tag; } else { # it's nick if (!$print_error && (!$server || !$server->{connected})) { $print_error = 1; Ekg2::echo("Not connected to server"); } else { $resolve_nicks{$nick} = 1; $ask_nicks .= "$nick "; } } } if ($ask_nicks ne "") { $_ = $server->{uid}; if (/irc:/) { # send the USERHOST query $userhosts++; $ircserver->raw("USERHOST :$nicks"); } if (/gg:/) { # todo, inaczej my ($ip) = $server->userlist()->find($nicks)->{ip}; Ekg2::print(1, "%> %B$nicks%n: %W$ip"); } } # ask the IPs/hostnames immediately host_lookup() if (!$lookup_waiting); } sub sig_failure { Irssi::print("Error getting hostname for nick"); %resolve_nicks = () if (--$userhosts == 0); } sub sig_userhost { my ($server_, $data_) = @_; my ($server, $data) = ($$server_, $$data_); $data =~ s/^[^ ]* :?//; my @hosts = split(/ +/, $data); # move resolve_nicks -> resolve_hosts foreach my $host (@hosts) { if ($host =~ /^([^=\*]*)\*?=.(.*)@(.*)/) { my $nick = lc($1); my $user = $2; $host = lc($3); $resolve_hosts{$host} = $resolve_nicks{$nick}; delete $resolve_nicks{$nick}; $resolve_print{$host} = "%n[%B$nick!$user"."@"."$host%n]"; } } if (--$userhosts == 0 && %resolve_nicks) { # unknown nicks - they didn't contain . or : so it can't be # IP or hostname. Ekg2::echo("Unknown nicks: ".join(' ', keys %resolve_nicks)); %resolve_nicks = (); } host_lookup() if (!$lookup_waiting); return -1; } sub host_lookup { return if (!%resolve_hosts); my ($host) = keys %resolve_hosts; $print_server = $resolve_hosts{$host}; $print_host = undef; $print_name = $resolve_print{$host}; @print_ips = (); delete $resolve_hosts{$host}; delete $resolve_print{$host}; $input_query = $host; $input_skip_next = 0; our ($rh, $wh); pipe( $rh, $wh); # non-blocking host lookups with fork()ing my $pid = fork(); if (!defined($pid)) { %resolve_hosts = (); %resolve_print = (); Irssi::print("Can't fork() - aborting"); close($rh); close($wh); return; } $lookup_waiting++; if ($pid > 0) { # parent, wait for reply close($wh); # Irssi::pidwait_add($pid); $pipe_tag = Ekg2::watch_add(fileno($rh), WATCH_READ, 'pipe_input', $rh); return; } close($rh); my $text; eval { # child, do the lookup my $name = ""; if ($host =~ /^[0-9\.]*$/) { # ip -> host $name = gethostbyaddr(inet_aton($host), AF_INET); } else { # host -> ip my @addrs = gethostbyname($host); if (@addrs) { @addrs = map { inet_ntoa($_) } @addrs[4 .. $#addrs]; $name = join (" ", @addrs); } } $print_name = $input_query if !$print_name; if (!$name) { $text = "%! %RNo information for %B$print_name"; } else { $text = "%> %B$print_name%n: %W$name"; } }; $text = $! if (!$text); eval { # write the reply print($wh $text); close($wh); }; POSIX::_exit(1); } sub pipe_input { my ($type, $rhfd, $watch, $rh) = @_; return if ($type); my $text = <$rh>; close($rh); undef $pipe_tag; my ($server); # my $server = Irssi::server_find_tag($print_server); if ($server) { $server->print('', $text); } else { Ekg2::print(1, $text); } $lookup_waiting--; host_lookup(); return -666; } Ekg2::command_bind_ext('dns', '!u', '', 'cmd_dns'); Ekg2::handler_bind('irc-protocol-numeric 302', 'sig_userhost'); ## Irssi::signal_add( { ## 'redir dns failure' => \&sig_failure, ## 'redir dns host' => \&sig_userhost } ); return 1; ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/contrib/perl/dupa.pl��������������������������������������������������������0000664�0000000�0000000�00000013302�11751427534�0020044�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# use strict; use Ekg2; our $VERSION = "0.1"; our %EKG2 = ( authors => 'Jakub Zawadzki', name => 'dupa', description => '...', license => 'Public Domain', changed => 'Mon Jul 11 18:07 CET 2005' ); my $i; sub window_switcher { my ($type, $timer) = @_; if ($type) { return; } my ($wind) = Ekg2::window_findid($i); $wind->switch() if ($wind); $timer->destroy() if (Ekg2::window_current->{id} != $i); $i++; } sub iwil_wait { my ($type, $timer) = @_; if ($type) { return; } $timer->destroy(); # $iwil_wind = Ekg2::window_current; $i = 0; Ekg2::timer_bind(1, 'window_switcher'); } sub cmd_evil { $i = 5; Ekg2::echo("iwil mode on"); Ekg2::echo("Take a look at Your windows, It may be the last time You see them?"); Ekg2::timer_bind(0, 'iwil_wait'); return 666; Ekg2::plugin_find("python")->unload(); # nie lubimy pythona! ;p # Ekg2::plugin_find("gg")->unload(); # gg tez nie ;p foreach my $var (Ekg2::variables) { $var->set("123"); } foreach my $sess (Ekg2::sessions) { foreach my $sess_var ($sess->params() ) { $sess_var->set("123"); # Ekg2::echo($sess_var . " -> " . $sess_var->{key} . " = " . $sess_var->{value}); } } } sub cmd_perl_list { my ($params) = @_; foreach my $timer (Ekg2::timers) { Ekg2::echo("$timer -> $timer->{name} = $timer->{freq}"); } return 0; foreach my $wind (Ekg2::windows) { Ekg2::echo("$wind -> $wind->{id} = $wind->{target}"); Ekg2::echo("WINDOW->USERSLIST: ($wind->{userlist})"); # $wind->next()->switch(); # $wind->print("dupa!"); # $wind->print_format("generic_error", "zle!"); foreach my $user2 ( Ekg2::Userlist::users( $wind->{userlist} ) ) { Ekg2::echo("$user2 -> $user2->{uid} $user2->{status}"); } } Ekg2::echo("--------------------------------------------"); return 0; foreach my $comm (Ekg2::commands) { Ekg2::echo("$comm -> $comm->{name} = $comm->{params}"); } Ekg2::echo("--------------------------------------------"); foreach my $sess (Ekg2::sessions) { Ekg2::echo("$sess -> $sess->{uid} $sess->{connected} ($sess->{status})"); Ekg2::echo("--------------USERSLIST--------------------"); foreach my $user (Ekg2::Userlist::users( $sess->{userlist} ) ) { Ekg2::echo("$user -> $user->{uid} $user->{status}"); } Ekg2::echo("--------------VARIABLES---------------------"); foreach my $sess_var ($sess->params() ) { Ekg2::echo("$sess_var -> $sess_var->{key} = $sess_var->{value}"); } Ekg2::echo("--------------------------------------------"); } foreach my $plug (Ekg2::plugins) { Ekg2::echo("$plug -> $plug->{name} $plug->{prio}"); } Ekg2::echo("--------------------------------------------"); foreach my $var (Ekg2::variables) { Ekg2::echo("$var -> $var->{name} = $var->{value}"); # $var->help(); } # Ekg2::echo("--------------------------------------------"); } sub cmd_connect_if_disconnected_disconnect_if_connected { foreach my $sess (Ekg2::sessions) { Ekg2::echo($sess . " -> " . $sess->{uid} . " conn = " . $sess->{connected} . " (" . $sess->{status} . ")" ); if ($sess->{connected}) { $sess->disconnect(); } else { $sess->connect(); } } } sub cmd_test { $var = Ekg2::variable_find("dupus"); Ekg2::echo("$var $var->{value}"); return; foreach my $sess (Ekg2::sessions) { Ekg2::echo("$sess -> $sess->{uid} $sess->{connected} ($sess->{status})"); Ekg2::echo("--------------VARIABLES---------------------"); foreach my $sess_var ($sess->params() ) { # Ekg2::echo("$sess_var -> $sess_var->{key} = $sess_var->{value}"); # $sess_var->set($sess, "assa"); } } return; my $sess = Ekg2::session_current; Ekg2::echo("$sess $sess->{uid} $sess->{userlist} "); foreach my $user (Ekg2::Userlist::users( $sess->{userlist} ) ) { Ekg2::echo("$user -> $user->{uid} $user->{status}"); } # Ekg2::User::remove_u($sess->{userlist}, $user); } sub cmd_timer_test { # testowanie obslugi timerow. powinno byc na przemian 0 i 1 z podobnymi adresami... my ($type, $timer) = @_; Ekg2::echo("$type $timer"); if ($type) { Ekg2::timer_bind(0.001, 'cmd_timer_test'); return; } $timer->destroy(); return 0; } sub cmd_timer { my ($type, $timer) = @_; if ($type) { Ekg2::echo("Timer ($timer) zniknal"); return; } Ekg2::echo("Timer ($timer) zaraz zniknie"); $timer->destroy(); } sub as { my ($varname, $value) = @_; Ekg2::echo("Zmieniles $varname na $value i ja o tym wiem!"); } sub ekg2_autoreconnect { # autoreconnect. perl-side my ($sesja) = @_; Ekg2::echo("rozlaczono! $$sesja, laczenie."); # $sess = Ekg2::session_find($sesja); # $sess->connect(); return 0; } sub ekg2_dupa { my ($sesja) = @_; Ekg2::echo("rozlaczono! $sesja"); } sub cmd_perl_scripts { Ekg2::print(1,"%R/--[%nPERL PLUGIN%R]"); foreach (sort grep(s/::$//, keys %Ekg2::Script::)) { my $name = ${_}; my $name_ = ${ "Ekg2::Script::${_}::EKG2" }{name}; my $ver = ${ "Ekg2::Script::${_}::VERSION" }; my $path = "_TODO_"; Ekg2::print(1, "%R|%n %G$name\t%R$name_\t%B$ver\t%g$path"); } Ekg2::print(1, "%R\`--<%nPERL PLUGIN LISTER%R>->"); } Ekg2::command_bind('perl_list', 'cmd_perl_list'); Ekg2::command_bind('perl_scr_list', 'cmd_perl_scripts'); Ekg2::debug("(perl) Hello ekg2!\n"); Ekg2::command_bind('test', 'cmd_test'); Ekg2::command_bind('iwil', 'cmd_evil'); Ekg2::timer_bind(1, 'cmd_timer'); Ekg2::handler_bind('protocol-disconnected', 'ekg2_autoreconnect'); Ekg2::handler_bind('protocol-disconnected', 'ekg2_dupa'); Ekg2::variable_add_ext('dupa', 'temp', 'as'); Ekg2::variable_add('dupus', 'as'); # cmd_timer_test(1, 0); # testowanie obslugi timerow. return 1; ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/contrib/perl/ggbe.pl��������������������������������������������������������0000664�0000000�0000000�00000002505�11751427534�0020022�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Michal 'GiM' Spadlinski # # GG is fuj und bebe, # simple autoresponder for ekg2 # use Ekg2; # your's gg uid my ($myggnum) = "gg:XXXXXX"; # if delay between messages is greater then this, we send text again my ($time) = 60*60*3; # 3 hours my ($jabba) = "gim\@znajdzwgooglach.pl"; # message :) my ($mesg) = "Wiadomo t otrzymujesz automatycznie\n". "GG zmierza ku upadkowi, przeczytaj prosz dwa linki poniej\n". "i skontaktuj si ze mn na jabberze pod: $jabba\n". "http://www.gadawski.pl/gg/ ORAZ bezbolesne przejcie na jabbera:\n". "http://www.nogui.yoyo.pl/tuty/GG2Jab/"; ############## CODE FOLLOWS ######### my %persony; sub ekg2_message { my ($tsession, $tuid, $trcpt, $ttext, $tformat, $tsend, $tclass) = @_; $session = $$tsession; $uid = $$tuid; $text = $$ttext; if ($session =~ $myggnum && !($uid =~ $myggnum)) { if (time() - $persony{$uid} > $time) { $persony{$uid} = time(); my ($wind) = Ekg2::window_current; my ($newwind) = Ekg2::window_findid(1); $newwind->switch() if ($newwind); my ($sc) = Ekg2::session_current; my ($gs) = Ekg2::session_find($session); $gs->set() if($gs); Ekg2::command("/msg $uid $mesg"); $sc->set(); $wind->switch(); } } return 1; } Ekg2::handler_bind('protocol-message', 'ekg2_message'); return 1; �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/contrib/perl/irc.pl���������������������������������������������������������0000664�0000000�0000000�00000003151�11751427534�0017671�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������use vars qw($VERSION %EKG2); use Ekg2; use Ekg2::Irc; sub cmd_nice { foreach my $server (Ekg2::Irc::servers) { foreach my $user ($server->people()) { foreach my $kanuser ($user->channels()) { # my $chan_ = $kanuser->{channel}; # my Ekg2::Irc::Channel $chan = \%chan_; # bless $chan, Ekg2::Irc::Channel; # Ekg2::echo($chan); # Ekg2::echo("$chan->{name} $kanuser->{sign}$user->{nick}"); Ekg2::echo("$kanuser->{name} $kanuser->{sign}$user->{nick}"); } Ekg2::echo("-"); } } } sub cmd_test { foreach my $server (Ekg2::Irc::servers) { $server->oper('darkjames', 'dupa.8'); Ekg2::echo("$server->{session} uid: $server->{session}"); # bless $sess, Ekg2::Session; # Ekg2::echo("$sess $server->{session} $sess->{status} "); Ekg2::echo("$server -> $server->{nick} $server->{server} $server->{ip}"); # $server->die('wspolna.syscomp.pl'); foreach my $user ($server->people()) { Ekg2::echo("$user $user->{nick} $user->{channels}"); foreach my $kanuser ($user->channels()) { Ekg2::echo("$kanuser $kanuser->{mode} $kanuser->{sign} $kanuser->{channel}"); # $kanuser->kick("lecisz z pokoju ;p"); # Ekg2::echo("$kanuser->{sign}$user->{nick}"); @$nick } $user->kill("lecisz z irca ;p"); Ekg2::echo("-"); } foreach my $chann ($server->channels() ) { Ekg2::echo("$chann $chann->{name}"); $chann->part("dupa!"); } Ekg2::echo("--------------------------------------------"); } Ekg2::echo("--------------------------------------------"); } Ekg2::command_bind('test', 'cmd_test'); Ekg2::command_bind('tst', 'cmd_nice'); 1;�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/contrib/perl/ping.pl��������������������������������������������������������0000664�0000000�0000000�00000006022�11751427534�0020051�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������use Ekg2; use Time::HiRes; use strict; our $VERSION = '0.01'; our %EKG2 = ( authors => 'Wiesław Ochmiński', contact => 'wiechu@wiechu.com', name => 'ping', description => 'Ping irc servers.', license => 'GPL', changed => '2012-02-03', ); ############################################################################## # # Configure # my $custom_SNS = '-'; # set your namespace for script, 'A:' for example ############################################################################## # # Global variables # my $SNS; # script namespace my $timer; my $period = 0; my $myname; # script name ############################################################################## # # Functions # sub cprint($) { my $format = shift; my $win = Ekg2::variable_find('display_crap')->{value} ? Ekg2::window_current->{id} : 1; Ekg2::print($win, $format); } sub lceq($$) { return lc(shift) eq lc(shift); } sub welcome() { return unless Ekg2::variable_find("${SNS}welcome")->{value}; cprint("\n%! %R$myname%n for %ge%Gk%gg%G2%n irc plugin.\n\n"); } sub set_timer() { my $v = Ekg2::variable_find("${SNS}time")->{value}; if ( ( ($v <= 0) || ($v != $period) ) && $timer ) { Ekg2::debug("Remove timer($period) $timer\n"); Ekg2::Timer::destroy($timer); undef $timer; } if ( ($v > 0) && ($v != $period) ) { Ekg2::Timer::destroy($timer) if $timer; $timer = Ekg2::timer_bind($v, 'ping_timer'); Ekg2::debug("Create timer($v) $timer\n"); } $period = $v; } ############################################################################## # # Handlers # sub ping_timer() { my ($type, $data) = @_; return if $type; foreach my $session (Ekg2::sessions) { next unless $session->{uid} =~ /^irc:/; next unless $session->{connected}; my $t = Time::HiRes::gettimeofday; Ekg2::command_exec( '', $session, "/quote ping $t" ) } } sub variable_changed() { my ($name) = ${$_[0]}; set_timer() if lceq($name, "${SNS}time"); } sub parse_line_hanler() { my ($session, $line) = map $$_, @_; return unless $line =~ /^:([^ ]+) PONG [^:]+:(.*)$/; my $display = Ekg2::variable_find("${SNS}display")->{value}; return unless $display>0; my $server = $1; my $t0 = $2; my $delay = sprintf("%.3f", Time::HiRes::gettimeofday - $t0); my $format = "%y$server %RPONG%g response%b:%n ${delay}s\n"; if ($display>1) { cprint($format); } else { Ekg2::print(0, $format); } } ############################################################################## # # Init # { $myname = $_[0]; $myname =~ s/^.+:([^:]+)$/$1/; if ($custom_SNS eq '-') { # script name space is script name $SNS = "$myname:"; } else { $SNS = $custom_SNS; } # # variables # Ekg2::variable_add_bool("${SNS}welcome", 1); Ekg2::variable_add_int("${SNS}display", 2); # 0 - no display, 1 - debug window, 2 - status or current window Ekg2::variable_add_int("${SNS}time", 180); # # handlers # Ekg2::handler_bind('irc-parse-line', 'parse_line_hanler'); Ekg2::handler_bind('variable-changed', 'variable_changed'); welcome(); set_timer(); } # Make perl happy 1; ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/contrib/perl/sample.pl������������������������������������������������������0000664�0000000�0000000�00000007704�11751427534�0020405�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������use Ekg2; sub ekg2_handle_status { my ($session, $uid, $status, $desc) = @_; my $sesja = Ekg2::session_find($session); if ($sesja->{connected}) { Ekg2::echo("sesja ". $session . "poczona"); Ekg2::echo("status: ". $sesja->{status}); Ekg2::echo("ustawiam t sesj jako domyln"); # sesja['default'] = "1" } else { ekg.echo("sesja ".$session . " nie jest poczona"); } Ekg2::echo("Dostaem status!"); Ekg2::echo("Sesja : ".$session); Ekg2::echo("UID : ".$uid); Ekg2::echo("Status: ".$status); # if desc: # ekg.echo("Opis : "+desc) # sesja = ekg.getSession(session) # ekg.echo('Lista userw sesji: '+", ".join(sesja.users())) # user = sesja.getUser(uid) # if user.last_status: # ekg.echo(str(user.last_status)) # stat, des = user.last_status # ekg.echo("Ostatni status: "+stat) # if user.last_status[1]: # ekg.echo("Ostatni opis : "+des) # else: # ekg.echo("Nie ma poprzedniego stanu - pewnie dopiero si czymy...") # ekg.echo("IP: "+user.ip) # ekg.echo("Grupy: "+", ".join(user.groups())) # if status == ekg.STATUS_AWAY: # ekg.echo("Chyba go nie ma...") # if status == ekg.STATUS_XA: # ekg.echo("Chyba bardzo go nie ma, to na grzyb mi taki status?. Poykam. *lurp*") # return -1; # return 1 } sub handle_message { my ($session, $uid, $type, $text, $sent_time, $ign_level) = @_; Ekg2::debug("[test script] some debug\n"); Ekg2::echo("Dostaem wiadomo!"); Ekg2::echo("Sesja : " . $session); Ekg2::echo("UID : " . $uid); if ($class == MSGCLASS_MESSAGE) { Ekg2::echo("Typ : msg"); } else { if ($class == MSGCLASS_CHAT ) { Ekg2::echo("Typ : chat"); } } # Ekg2::echo("Czas : "+time.strftime("%a, %d %b %Y %H:%M:%S %Z", time.gmtime(sent_time))) Ekg2::echo("Ign : " . $ignore_level); Ekg2::echo("TxtLen: " . length($text)); if (length($text) == 13) { Ekg2::echo("Oj, ale pechowa liczba, nie odbieram"); return 0; } return 1 } sub handle_message_own { my ($session, $target, $text) = @_; Ekg2::debug("[test script] some debug\n"); Ekg2::echo("Wysyam wiadomo!"); Ekg2::echo("Sesja : ". $session); Ekg2::echo("Target: ". $target); Ekg2::echo("TxtLen: ". length($text)); return -(-1); } sub ekg2_message { my ($session, $uid, $rcpt, $text, $format, $send, $class) = @_; $tclass = $$class; # $$text = "dupa4"; # Ekg2::echo($$text); # $$class -= EKG_NO_THEMEBIT; if ($tclass & EKG_NO_THEMEBIT) { $tclass -= EKG_NO_THEMEBIT; }; # jesli moje to handle_msg_own(); if ($tclass == EKG_MSGCLASS_SENT || $tclass == EKG_MSGCLASS_SENT_CHAT) { return handle_message_own($$session, $$rcpt, $$text); } # jesli nie to handle_msg(); else { return handle_message($$session, $$uid, $$tclass, $$text, $$send, 0); } } sub ekg2_handle_connect { my ($session) = @_; Ekg2::echo("Poczono! Ale super! Mona gada!"); Ekg2::echo("Sesja : ". $session); # if session[:3] == 'irc': # struct = time.gmtime() # if struct[3] >= 8 and struct[3] < 17: # ekg.echo('adnie to tak ircowa w pracy? ;)') my $sesja = Ekg2::session_find($session); if ($sesja->{connected}) { Ekg2::echo('Poczony!'); } else { Ekg2::echo('W tym miejscu jeszcze nie poczony'); } # Ekg2::echo('Lista userw sesji: '+", ".join(sesja.users())) } sub ekg2_handle_disconnect { my ($session) = @_; Ekg2::echo("o, sesja " . $session . " nam pada"); Ekg2::echo("Wysyamy smsa e nam cu pado..."); } sub ekg2_handle_keypress { my ($ch) = @_; if ($$ch == -1) { return -1 } Ekg2::echo("Nacisnales #" .$$ch); $$ch = 41 if ($$ch == 40); # zamiana '(' na ')' return 0; } # Ekg2::handler_bind('protocol-disconnected', 'ekg2_handle_disconnect'); # Ekg2::handler_bind('protocol-status', 'ekg2_handle_status'); # Ekg2::handler_bind('protocol-message', 'ekg2_message'); # Ekg2::handler_bind('ui-keypress', 'ekg2_handle_keypress'); return 1; ������������������������������������������������������������ekg2-0.4~pre+20120506.1/contrib/perl/slownik.pl�����������������������������������������������������0000775�0000000�0000000�00000002357�11751427534�0020614�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# YDP Dictionary bot based on socket, Michal 'GiM' Spadlinski # requires sockydpdict :) # # SSS - short, small and simple # use Socket; our $VERSION = "0.1"; our %EKG2 = ( authors => 'Michal Spadlinski', name => 'slownikobot', description => 'YDP slownik BOT', license => 'GPL', changed => 'Wed May 17 20:49:47 CEST 2006' ); sub cotozaslowo { my $fname = "/tmp/ydpsock"; my $answer; my $flags = 0; my $maxlen = 4096; $_ = shift; socket (CLIENT, PF_UNIX, SOCK_STREAM, 0); connect (CLIENT, sockaddr_un($fname)) or return 0; defined (send(CLIENT, "$_\n", $flags)) or return 0; recv (CLIENT, $answer, $maxlen, $flags) or return 0; return $answer; } #bindings for ekg2 sub handle_message { #query_emit(NULL, "irc-protocol-message", &(sid), &(j->nick), &__msg, &isour, &xosd_to_us, &xosd_is_priv, &uid_full); my ($session, $uid, $msg, $our, $n1, $n2, $n3) = @_; $txt = $$msg; return 1 if (! ($$session =~ /^irc:/)); # we don't respond to our own queries return 1 if ($$our); if ($txt =~ /(!ang) ([a-zA-Z ]*)/) { $z = cotozaslowo($2); $z =~ s/\n{2,}/\n/g; Ekg2::command("/msg $$uid $z"); return 0; } return 1 } Ekg2::handler_bind('irc-protocol-message', 'handle_message'); 1; ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/contrib/perl/xmms.pl��������������������������������������������������������0000664�0000000�0000000�00000011552�11751427534�0020104�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# XMMS-InfoPipe front-end - allow /np [dest] # # Thanks to ak for suggestions and even changes. # # /set xmms_fifo <dest of xmms-infopipe fifo> # /set xmms_format <format of printed text> # /set xmms_format_streaming <format for streams> # /set xmms_print_if_stopped <ON|OFF> # /set xmms_format_time <time format> - default is %m:%s # # xmms_format* takes these arguments: # Variable Name Example # ---------------------------------------------------- # Song specific: # %status Status Playing # %title Title Blue Planet Corporation - Open Sea # %file File /mp3s/blue planet corporation - open sea.mp3 # %length Length 9:13 # %pos Position 0:08 # %bitrate Bitrate 160kbps # %freq Sampling freq. 44.1kHz # %pctdone Percent done 1.4% # %channels Channels 2 # Playlist specific: # %pl_total Total entries # %pl_current Position in playlist # pl_pctdone Playlist Percent done use strict; use Ekg2; our $VERSION = "2.0"; our %EKG2 = ( authors => 'Simon Shine', contact => 'simon@blueshell.dk', name => 'xmms', description => 'XMMS-InfoPipe front-end - allow /np [-help] [dest]', license => 'Public Domain', changed => '2004-01-15' ); # rewriten to ekg2 perl script by darkjames @ 2005-08-28 Ekg2::variable_add('xmms_fifo', '/tmp/xmms-info'); Ekg2::variable_add('xmms_format', 'np: %title at %bitrate [%pos of %length]'); Ekg2::variable_add('xmms_format_streaming', 'streaming: %title at %bitrate [%file]'); Ekg2::variable_add('xmms_format_time', '%m:%s'); Ekg2::variable_add('xmms_print_if_stopped', '1'); Ekg2::command_bind('np', 'cmd_xmms'); # Ekg2::command_bind('xmms', 'cmd_xmms'); # Tab completition Ekg2::command_bind('np help', 'cmd_xmms'); ## Ekg2::command_bind('xmms help', \&cmd_xmms); sub cmd_xmms { my ($name, $args) = @_; $args =~ s/^\s+//; $args =~ s/\s+$//; if ($args =~ /^help/) { Ekg2::echo(" Valid format strings for xmms_format and xmms_format_streaming: %%status, %%title, %%file, %%length, %%pos, %%bitrate, %%freq, %%pctdone, %%channels, %%pl_total, %%pl_current Example: /set xmms_format np: %%title at %%bitrate [%%pctdone] Valid format string for xmms_format_time: %%m, %%s Example: /set xmms_format_time %%m minutes, %%s seconds\n"); return; } my ($xf) = Ekg2::variable_find('xmms_fifo')->{value}; if (!-r $xf) { if (!-r '/tmp/xmms-info') { Ekg2::echo("Couldn't find a valid XMMS-InfoPipe FIFO."); return; } $xf = '/tmp/xmms-info'; } my %xi; open(XMMS, $xf); while (<XMMS>) { chomp; my ($key, $value) = split /: /, $_, 2; $xi{$key} = $value; } close(XMMS); my %fs; # %status $fs{'status'} = $xi{'Status'}; # %title if ($fs{'status'} ne "Playing") { if (Ekg2::variable_find('xmms_print_if_stopped')->{value} > 0) { $fs{'title'} = sprintf('(%s) %s', $fs{'status'}, $xi{'Title'}); } else { Ekg2::echo("XMMS is currently not playing."); return; } } else { $fs{'title'} = $xi{'Title'}; } # %file $fs{'file'} = $xi{'File'}; # %length $fs{'length'} = &format_time($xi{'Time'}); # %pos $fs{'pos'} = &format_time($xi{'Position'}); # %bitrate $fs{'bitrate'} = sprintf("%.0fkbps", $xi{'Current bitrate'} / 1000); # %freq $fs{'freq'} = sprintf("%.1fkHz", $xi{'Samping Frequency'} / 1000); # %pctdone if ($xi{'uSecTime'} > 0) { $fs{'pctdone'} = sprintf("%.1f%%%%", ($xi{'uSecPosition'} / $xi{'uSecTime'}) * 100); } else { $fs{'pctdone'} = "0.0%%"; } # %channels $fs{'channels'} = $xi{'Channels'}; # %pl_total $fs{'pl_total'} = $xi{'Tunes in playlist'}; # %pl_current $fs{'pl_current'} = $xi{'Currently playing'}; # %pl_pctdone $fs{'pl_pctdone'} = sprintf("%.1f%%%%", ($fs{'pl_current'} / ($fs{'pl_total'} ? $fs{'pl_total'} : 1)) * 100); my ($format) = ($xi{'uSecTime'} == "-1") ? Ekg2::variable_find('xmms_format_streaming')->{value} : Ekg2::variable_find('xmms_format')->{value}; foreach (keys %fs) { $format =~ s/\%$_/$fs{$_}/g; } # sending it. my ($sess) = Ekg2::session_current; my ($wind) = Ekg2::window_current; if ($sess && $sess->{connected} && $wind->{id} > 1) { if ($args eq "") { Ekg2::command("/msg $wind->{target} $format"); } else { Ekg2::command("/msg $args $format"); } } else { Ekg2::echo($format); } } sub format_time { my ($m, $s) = split /:/, @_[0], 2; my ($format) = Ekg2::variable_find('xmms_format_time')->{value}; $format =~ s/\%m/$m/g; $format =~ s/\%s/$s/g; return $format; } 1;������������������������������������������������������������������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/contrib/python/�������������������������������������������������������������0000775�0000000�0000000�00000000000�11751427534�0017136�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/contrib/python/notify-bubble.py���������������������������������������������0000775�0000000�0000000�00000003435�11751427534�0022261�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/python # # (c) 2006 Tomasz Torcz <zdzichu@irc.pl> # # Displays notify bubble when someone writes to us or becomes available. # Ingredients needed: #- dbus #- libnotify & notification-daemon from http://galago-project.org/ # # This script is very basic - could use some more intelligence # import dbus import ekg bus = dbus.SessionBus() obj = bus.get_object ("org.freedesktop.Notifications", "/org/freedesktop/Notifications") notif = dbus.Interface (obj, "org.freedesktop.Notifications") def status_handler(session, uid, status, desc): if status == ekg.STATUS_AVAIL: current_encoding = ekg.config["console_charset"] or "iso-8859-2" user = ekg.session_get(session).user_get(uid) uname = unicode(user.nickname or uid, current_encoding) htxt = uname + u" jest dostepn" htxt += (uname[-1] == "a" or uname[-1] == "A") and "a" or "y" if not desc: desc = "" else: desc = unicode(desc, current_encoding) timeout = 2000 + len(desc)*50 # (s appname, u id, s icon, s header, s body, as actions a{sv} hints, i timeout_ms) notif.Notify (dbus.String("ekg2"), dbus.UInt32(0), "", dbus.String(htxt), dbus.String(desc), dbus.String(""), {}, dbus.Int32(timeout) ) return 1 def message_handler(session, uid, type, text, sent_time, ignore_level): current_encoding = ekg.config["console_charset"] or "iso-8859-2" user = ekg.session_get(session).user_get(uid) uname = unicode(user.nickname or uid, current_encoding) htxt = uname + ":" msg = unicode(text[0:127], current_encoding) timeout = 2000 + len(msg)*50 notif.Notify (dbus.String("ekg2"), dbus.UInt32(0), "", dbus.String(htxt), dbus.String(msg), dbus.String(""), {}, dbus.Int32(timeout) ) return 1 ekg.handler_bind('protocol-status', status_handler) ekg.handler_bind('protocol-message-received', message_handler) �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/contrib/python/sample.py����������������������������������������������������0000664�0000000�0000000�00000006375�11751427534�0021004�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# -*- encoding: iso-8859-2 -*- import ekg import time def status_handler(session, uid, status, desc): # for sesja in ekg.sessions(): # if sesja.connected(): # ekg.echo("sesja '%s' poczona" % (name,)) # ekg.echo("status: "+sesja['status']) # else: # ekg.echo("sesja '%s' nie jest poczona" % (name,)) ekg.echo("Dostaem status!") ekg.echo("Sesja : "+session) ekg.echo("UID : "+uid) ekg.echo("Status: "+status) if desc: ekg.echo("Opis : "+desc) sesja = ekg.session_get(session) # ekg.echo('Lista userw sesji: '+", ".join(sesja.users())) user = sesja.user_get(uid) if user.last_status: ekg.echo(str(user.last_status)) stat, des = user.last_status ekg.echo("Ostatni status: "+stat) if user.last_status[1]: ekg.echo("Ostatni opis : "+des) else: ekg.echo("Nie ma poprzedniego stanu - pewnie dopiero si czymy...") if user.ip: ekg.echo("IP: "+user.ip) if user.groups: ekg.echo("Grupy: "+", ".join(user.groups())) if status == ekg.STATUS_AWAY: ekg.echo("Chyba go nie ma...") if status == ekg.STATUS_XA: ekg.echo("Chyba bardzo go nie ma, to na grzyb mi taki status?. Poykam. *lurp*") return 0 return 1 def message_handler(session, uid, type, text, sent_time, ignore_level): ekg.debug("[test script] some debug\n") ekg.echo("Dostaem wiadomo!") ekg.echo("Sesja : "+session) ekg.echo("UID : "+uid) if type == ekg.MSGCLASS_MESSAGE: ekg.echo("Typ : msg") elif type == ekg.MSGCLASS_CHAT: ekg.echo("Typ : chat") ekg.echo("Czas : "+time.strftime("%a, %d %b %Y %H:%M:%S %Z", time.gmtime(sent_time))) ekg.echo("Ign : "+str(ignore_level)) ekg.echo("TxtLen: "+str(len(text))) if len(text) == 13: ekg.echo("Oj, ale pechowa liczba, nie odbieram") return 0 return 1 def own_message_handler(session, target, text): ekg.debug("[test script] some debug\n") ekg.echo("Wysyam wiadomo!") ekg.echo("Sesja : "+session) ekg.echo("Target: "+target) ekg.echo("TxtLen: "+str(len(text))) return 1 def connect_handler(session): ekg.echo("Poczono! Ale super! Mona gada!") ekg.echo("Sesja : "+session) if session[:3] == 'irc': struct = time.gmtime() if struct[3] >= 8 and struct[3] < 17: ekg.echo('adnie to tak ircowa w pracy? ;)') sesja = ekg.session_get(session) if sesja.connected(): ekg.echo('Poczony!') else: ekg.echo('W tym miejscu jeszcze nie poczony') ekg.echo('Lista userw sesji: '+", ".join(sesja.users())) def keypress(key): ekg.echo('nacisnales #'+ str(key)); def disconnect_handler(session): ekg.echo("o, sesja %s nam pada" % (session,)) ekg.echo("Wysyamy smsa e nam cu pado...") def foo_command(name, args): ekg.echo("Wywoane polecenie foo!") def varchange(name, newval): ekg.echo("Zmienna %s zmienia warto na %s" % (name, newval) ) ekg.command_bind('foo', foo_command) ekg.handler_bind('protocol-message-received', message_handler) ekg.handler_bind('protocol-message-sent', own_message_handler) ekg.handler_bind('protocol-status', status_handler) ekg.handler_bind('protocol-connected', connect_handler) ekg.handler_bind('protocol-disconnected', disconnect_handler) ekg.handler_bind('ui-keypress', keypress) ekg.variable_add('zmienna_testowa', 'warto', varchange) �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/contrib/ruby/���������������������������������������������������������������0000775�0000000�0000000�00000000000�11751427534�0016576�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/contrib/ruby/Sample.rb������������������������������������������������������0000664�0000000�0000000�00000002227�11751427534�0020347�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������if $0 != "ekg2" print <<MSG warning: you are executing an embedded ruby file! this file is suppose to be run only from ekg2. MSG exit end class Ekg2::Script::Sample < Ekg2::Script def theme_init format_add("dekoral", "%) %MCzas plynie a %YDEKORAL%n %gwciaz%n %TBIALY%n %B%1 uderzenie. %RZostalo %2. %GLosowa liczba: %3"); end def handler_foo(parametr = nil) if parametr != nil print "Wywolane polecenie foo z parametrem: `" + parametr + "` !" else print "Wywolane polecenie foo bez parametrow!" end end def handler_keypress(klawisz) print "Nacisnales klawisz!: #" + klawisz.to_s end def handler_czasomierz() @already = @already + 1; @left = @left - 1; print "dekoral", @already.to_s, @left.to_s, (@already*rand(@left)).to_s end def varchange(name, newval) print "generic", "Zmienna " + name + " zmienila wartosc na " + newval; end def initialize super command_bind("foo", "handler_foo") # handler_bind("ui-keypress", "handler_keypress") timer_bind(1, "handler_czasomierz"); variable_add("zmienna_testowa", "wartosc", "varchange") @already = 0 @left = 1000000; end def finalize print "Sprzatam!" end end �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/contrib/ruby/WatchSample.rb�������������������������������������������������0000664�0000000�0000000�00000001304�11751427534�0021331�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������if $0 != "ekg2" print <<MSG warning: you are executing an embedded ruby file! this file is suppose to be run only from ekg2. MSG exit end class Ekg2::Script::WatchSample < Ekg2::Script def fd2IO(fd) return IO.for_fd(fd, 'r') end def handler_foo_watch(type, fd, watch) return 0 if type != 0; rd = fd2IO(fd) print "generic", "fd: " + fd.to_s + "; Odebrano: " + rd.read(); rd.close(); end def handler_foo(parametr = nil) rd, wr = IO.pipe if fork wr.close watch_add(rd.fileno, WATCH_READ, "handler_foo_watch"); else rd.close wr.write "test" sleep 1 wr.close Process.exit!(0); end end def initialize super command_bind("foo", "handler_foo") end end ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/contrib/testcase_for_remove_iter.c������������������������������������������0000664�0000000�0000000�00000004725�11751427534�0023052�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* short note (in Polish, sorry) * * testcase majacy na celu sprawdzenie czy __DYNSTUFF_LIST_REMOVE_ITER() * dziala poprawnie przy zwalnianiu pierwszych elementow listy. * * Powinno byc troche inaczej, w innym .o zeby kompilator nie mogl sobie tego zoptymalizowac. * ale dla -O0 ... -O3 dziala poprawnie, * * prawidlowy output: * f == 0x602070: 40, 30, 20, 10, * f == 0x602050: 30, 20, 10, * f == 0x602030: 20, 10, * f == 0x602010: 10, * f == (nil): * * jesli twoj kompilator dla pewnych wartosci -O lub nie korzystasz z gcc, lub korzystach z innych kosmicznych flag kompilatora * generuje nieprawidlowy kod, ktory generuje nieprawidlowy output (adresy moga byc rozne :>) * daj info. * * wiechu chcial aby to bylo testowane w czasie ./configure moze kiedys. * Na razie wrzucam do contrib/ * * Ogolnie korzystamy z faktu, ze w C: * - strukturka->pierwszy_element_strukturki == *(strukturka + 0) == *strukturka * - *(&foo) == foo */ /* __DYNSTUFF_LIST_REMOVE_ITER() jest ogolnie b. fajne, i b. przydatne. */ /* thx goes to wiechu. */ #include <stdio.h> #include <stdlib.h> #define xfree free #include <ekg/dynstuff_inline.h> #ifdef DYNSTUFF_USE_LIST3 #include <errno.h> /* stuff copied from dynstuff.c */ void *list_add_beginning3(list_t *list, list_t new) { if (!list) { errno = EFAULT; return NULL; } new->next = *list; *list = new; return new; } void *list_remove3i(list_t *list, list_t elem, void (*func)(list_t data)) { list_t tmp, last = NULL; void *ret = NULL; if (!list) { errno = EFAULT; return ret; } tmp = *list; if (tmp && tmp == elem) { *list = tmp->next; ret = list; } else { for (; tmp && tmp != elem; tmp = tmp->next) last = tmp; if (!tmp) { errno = ENOENT; return ret; } last->next = tmp->next; ret = last; } if (func) func(tmp); xfree(tmp); return ret; } #endif typedef struct foo { struct foo *next; int val; } foo; foo *foos; void do_nothing(foo *f) { } __DYNSTUFF_LIST_ADD_BEGINNING(foos, foo, NULL); __DYNSTUFF_LIST_REMOVE_ITER(foos, foo, do_nothing); foo *nowy_element(int val) { foo *tmp = malloc(sizeof(foo)); tmp->val = val; foos_add(tmp); return tmp; } void print(foo *f) { printf("f == %p: ", f); for (; f; f = f->next) printf("%d, ", f->val); printf("\n"); } int main() { foo *f; nowy_element(10); nowy_element(20); nowy_element(30); nowy_element(40); for (f = foos; f; f = f->next) { print(foos); f = foos_removei(f); } print(foos); return 0; } �������������������������������������������ekg2-0.4~pre+20120506.1/contrib/themes/�������������������������������������������������������������0000775�0000000�0000000�00000000000�11751427534�0017102�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/contrib/themes/blue_sea_dragon.theme����������������������������������������0000664�0000000�0000000�00000107542�11751427534�0023250�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# (c) dredzik 2004-2006 # Blue Sea Dragon //Begin contacts_vertical_line_char contacts_horizontal_line_char prompt %T--%b>%n prompt,speech prompt2 %T--%b>%n prompt2,speech error %T--%r>%n error,speech bd! timestamp %H%M%S timestamp,speech ncurses_prompt_none [empty] ncurses_prompt_query [%1] statusbar %l%b[%n%B%l%{time}%b] [%n%B%l%{?session %{session}%b(%B%{?avail dostpny}%{?away zajty}%{?invisible ukryty}%{?notavail niedostpny}%{?xa bardzo zajty}%{?dnd nie przeszkadza}%{?chat chtny do rozmowy}%b)}%{?!session ---}%b] [%n%B%l%{?window %{window}}%{?query :%{query}}%{?debug debug}%{?activity %b] [%n%B%lact: }%{activity}%{?mail %b] [%n%B%l mail:}%{mail}%{?more %b] [%n%B%lmore}%b]%n%l statusbar_act_important %B statusbar_act %b statusbar_timestamp %H%M%S header known_user %1 known_user,speech %1 unknown_user %1 none %1\n generic %> %1\n generic_bold %> %1%n\n generic2 %) %1\n generic2_bold %) %1%n\n generic_error %! %1\n debug %n%1\n not_enough_params %! %Bcommand: za mao parametrw dla komendy %b(%B%1%b)%n\n invalid_params %! %Bcommand: nieprawidowe parametry dla komendy %b(%B%1%b)%n\n //End invalid_uid %! Nieprawidowy identyfikator uytkownika\n invalid_session %! Nieprawidowa sesja\n invalid_nick %! Nieprawidowa nazwa uytkownika\n user_not_found %! Nie znaleziono uytkownika %T%1%n\n not_implemented %! Tej funkcji jeszcze nie ma\n //Begin unknown_command %! %Bcommand: nieznane polecenie %b(%B%1%b)%n\n welcome %B,----- %b---- %B--- %b-- %B-%n\n%B| ekg2-%1%n %b(%be%Bk%bg %Br%be%Ba%bk%Bt%by%Bw%ba%Bc%bj%Ba%b)%B + BlueSeaDragon.theme by dredzik%n\n%B| program jest rozprowadzany na zasadach licencji GPL v2\n%B`----- %b---- %B--- %b-- %B-%n\n\n welcome,speech ekg2 + BlueSeaDragon ekg_version %) %Tekg2-%1%n (skompilowano %2)\n //End secure %Y(szyfrowane)%n new_mail_one %) Masz now wiadomo email\n new_mail_two_four %) Masz %1 nowe wiadomoci email\n new_mail_more %) Masz %1 nowych wiadomoci email\n //Begin user_added %> %Bcontacts: dopisano %n%b(%n%B%1%n%b)%n%B do listy kontaktw dla sesji %n%b(%n%B%2%n%b)%n\n user_deleted %) %Bcontacts: usunito %n%b(%n%B%1%n%b)%n%B z listy kontaktw dla sesji %n%b(%n%B%2%n%b)%n\n user_cleared_list %) %Bcontacts: wyczyszczono list kontaktw dla sesji %n%b(%n%B%2%n%b)%n\n user_exists %! %Bcontacts: wpis %n%b(%n%B%1%n%b)%n%B ju istnieje w licie kontaktw sesji %n%b(%n%B%2%n%b)%n\n user_exists_other %! %Bcontacts: wpis %n%b(%n%B%1%n%b)%n%B ju istnieje w licie kontaktw sesji %n%b(%n%B%3%n%b)%B jako %n%b(%n%B%2%n%b)%n\n away %> %Bstatus: zmieniono stan na zajty%n\n away_descr %> %Bstatus: zmieniono stan na zajty %n%b(%n%B%1%n%b)%n\n back %> %Bstatus: zmieniono stan na dostpny\n back_descr %> %Bstatus: zmieniono stan na dostpny %n%b(%n%B%1%n%b)%n\n invisible %> %Bstatus: zmieniono stan na niewidoczny\n invisible_descr %> %Bstatus: zmieniono stan na niewidoczny %n%b(%n%B%1%n%b)%n\n dnd %> %Bstatus: zmieniono stan na nie przeszkadza%n\n dnd_descr %> %Bstatus: zmieniono stan na nie przeszkadza %n%b(%n%B%1%n%b)%n\n chat %> %Bstatus: zmieniono stan na chtny do rozmowy%n\n chat_descr %> %Bstatus: zmieniono stan na chtny do rozmowy %n%b(%n%B%1%n%b)%n\n xa %> %Bstatus: zmieniono stan na bardzo zajty%n\n xa_descr %> %Bstatus: zmieniono stan na bardzo zajty %n%b(%n%B%1%n%b)%n\n //End private_mode_is_on %> %Bprivate: tryb %b(%Btylko dla znajomych%b)%B dla sesji %b(%B%1%b)%B jest wczony%n\n private_mode_is_off %> %Bprivate: tryb %b(%Btylko dla znajomych%b)%B dla sesji %b(%B%1%b)%B jest wyczony%n\n private_mode_on %> %Bprivate: wczono tryb %b(%Btylko dla znajomych%b)%B dla sesji %b(%B%1%b)%B\n private_mode_off %> %Bprivate: wczono tryb %b(%Btylko dla znajomych%b)%B dla sesji %b(%B%1%b)%B\n private_mode_invalid %! Nieprawidowa warto\n descr_too_long %! Dugo opisu przekracza limit. Ilo ucitych znakw: %T%1%n\n auto_away %> (%1) Automagicznie zmieniono stan na zajty\n auto_away_descr %> (%2) Automagicznie zmieniono stan na zajty: %T%1%n\n auto_back %> (%1) Automagicznie zmieniono stan na dostpny\n auto_back_descr %> (%2) Automagicznie zmieniono stan na dostpny: %T%1%n\n help %> %T%1%n%2 - %3\n help_more %) %|%1\n help_alias %) %T%1%n jest aliasem i nie posiada opisu\n help_footer \n%> %|Wicej szczegw na temat komend zwrci %Thelp <komenda>%n. Poprzedzenie komendy znakiem %T^%n spowoduje ukrycie jej wyniku. Zamiast parametru <numer/alias> mona uy znaku %T$%n oznaczajcego aktualnego rozmwc.\n\n help_quick %> %|Przed uyciem przeczytaj ulotk. Plik %Tdocs/ULOTKA%n zawiera krtki przewodnik po zaczonej dokumentacji. Jeli go nie masz, moesz cign pakiet ze strony %Thttp://dev.null.pl/ekg2/%n\n help_set_file_not_found %! Nie znaleziono opisu zmiennych (nieprawidowa instalacja)\n help_set_var_not_found %! Nie znaleziono opisu zmiennej %T%1%n\n help_set_header %> %T%1%n (%2, domylna warto: %3)\n%>\n help_set_body %> %|%1\n help_set_footer ignored_added %> Dodano %T%1%n do listy ignorowanych\n ignored_modified %> Zmodyfikowano poziom ignorowania %T%1%n\n ignored_deleted %) Usunito %1 z listy ignorowanych\n ignored_deleted_all %) Usunito wszystkich z listy ignorowanych\n ignored_exist %! %1 jest ju na licie ignorowanych\n ignored_list %> %1 %2\n ignored_list_empty %! Lista ignorowanych uytkownikw jest pusta\n error_not_ignored %! %1 nie jest na licie ignorowanych\n blocked_added %> Dodano %T%1%n do listy blokowanych\n blocked_deleted %) Usunito %1 z listy blokowanych\n blocked_deleted_all %) Usunito wszystkich z listy blokowanych\n blocked_exist %! %1 jest ju na licie blokowanych\n blocked_list %> %1\n blocked_list_empty %! Lista blokowanych uytkownikw jest pusta\n error_not_blocked %! %1 nie jest na licie blokowanych\n //Begin list_empty %! %Blist: lista jest pusta%n\n list_avail %c%[-25]1 %n%Bjest %n%cdostpn%@2%n\n list_avail_descr %c%[-25]1 %n%Bjest %n%cdostpn%@2 %n%b(%n%B%5%n%b)%n\n list_away %c%[-25]1 %n%Bjest %n%czajt%@2%n\n list_away_descr %c%[-25]1 %n%Bjest %n%czajt%@2 %n%b(%n%B%5%n%b)%n\n list_dnd %c%[-25]1 %n%cnie przeszkadza%n\n list_dnd_descr %c%[-25]1 %n%cnie przeszkadza %n%b(%n%B%5%n%b)%n\n list_chat %c%[-25]1 %n%Bjest %n%cchtn%@2 do rozmowy%n\n list_chat_descr %c%[-25]1 %n%Bjest %n%cchtn%@2 do rozmowy %n%b(%n%B%5%n%b)%n\n list_error %c%[-25]1 %n%cbd%n\n list_error %c%[-25]1 %n%cbd %n%b(%n%B%5%n%b)%n\n list_xa %c%[-25]1 %n%Bjest %n%cbardzo zajt%@2%n\n list_xa_descr %c%[-25]1 %n%Bjest %n%cbardzo zajt%@2 %n%b(%n%B%5%n%b)%n\n list_notavail %c%[-25]1 %n%Bjest %n%cniedostpn%@2%n\n list_notavail_descr %c%[-25]1 %n%Bjest %n%cniedostpn%@2 %n%b(%n%B%5%n%b)%n\n list_invisible %c%[-25]1 %n%Bjest %n%cniewidoczn%@2%n\n list_invisible_descr %c%[-25]1 %n%Bjest %n%cniewidoczn%@2 %n%b(%n%B%5%n%b)%n\n list_blocked %c%[-25]1 %n%Bjest %n%cblokujc%@2%n\n list_unknown %c%[-25]1\n //End modify_offline %> %1 nie bdzie widzie naszego stanu\n modify_online %> %1 bdzie widzie nasz stan\n modify_done %> Zmieniono wpis w licie kontaktw\n //Begin contacts_header contacts_header_group %Cgrupa %B%1%n contacts_metacontacts_header contacts_avail_header %Cdostpni:%n contacts_avail %B%1%n contacts_avail_descr %Ci%n%B%1%n contacts_avail_descr_full %Ci%n%B%1%n %2 contacts_avail_blink %B%U%1%n contacts_avail_descr_blink %Ci%n%B%U%1%n contacts_avail_descr_full_blink %Ci%n%B%U%1%n %2 contacts_avail_footer %n contacts_away_header %Czajci: contacts_away %B%1%n contacts_away_descr %Ci%n%B%1%n contacts_away_descr_full %Ci%n%B%1%n %2 contacts_away_blink %B%U%1%n contacts_away_descr_blink %Ci%n%B%U%1%n contacts_away_descr_full_blink %Ci%n%B%U%1%n %2 contacts_away_footer %n contacts_dnd_header %Cnie przeszkadza:%n contacts_dnd %B%1%n contacts_dnd_descr %Ci%n%B%1%n contacts_dnd_descr_full %Ci%n%B%1%n %2 contacts_dnd_blink %B%U%1%n contacts_dnd_descr_blink %Ci%n%B%U%1%n contacts_dnd_descr_full_blink %Ci%n%B%U%1%n %2 contacts_dnd_footer %n contacts_xa_header %Cbardzo zajci:%n contacts_xa %B%1%n contacts_xa_descr %Ci%n%B%1%n contacts_xa_descr_full %Ci%n%B%1%n %2 contacts_xa_blink %B%U%1%n contacts_xa_descr_blink %Ci%n%B%U%1%n contacts_xa_descr_full_blink %Ci%n%B%U%1%n %2 contacts_xa_footer %n contacts_notavail_header %Cniedostpni:%n contacts_notavail %B%1%n contacts_notavail_descr %Ci%n%B%1%n contacts_notavail_descr_full %Ci%n%B%1%n %2 contacts_notavail_blink %B%U%1%n contacts_notavail_descr_blink %Ci%n%B%U%1%n contacts_notavail_descr_full_blink %Ci%n%B%U%1%n %2 contacts_notavail_footer %n contacts_invisible_header %Cniewidoczni:%n contacts_invisible %B%1%n contacts_invisible_descr %Ci%n%B%1%n contacts_invisible_descr_full %Ci%n%B%1%n %2 contacts_invisible_blink %B%U%1%n contacts_invisible_descr_blink %Ci%n%B%U%1%n contacts_invisible_descr_full_blink %Ci%n%B%U%1%n %2 contacts_invisible_footer %n contacts_chat_header %Cchtni do rozmowy:%n contacts_chat %B%1%n contacts_chat_descr %Ci%n%B%1%n contacts_chat_descr_full %Ci%n%B%1%n %2 contacts_chat_blink %B%U%1%n contacts_chat_descr_blink %Ci%n%B%U%1%n contacts_chat_descr_full_blink %Ci%n%B%U%1%n %2 contacts_chat_footer %n contacts_error_header %Cbdne statusy:%n contacts_error %B%1%n contacts_error_descr %Ci%n%B%1%n contacts_error_descr_full %Ci%n%B%1%n %2 contacts_error_blink %B%U%1%n contacts_error_descr_blink %Ci%n%B%U%1%n contacts_error_descr_full_blink %Ci%n%B%U%1%n %2 contacts_error_footer %n contacts_blocking_header %Cblokujcy:%n contacts_blocking %B%1%n contacts_blocking_footer %n contacts_footer contacts_footer_group %n contacts_metacontacts_footer quit %> %Bquit: koniec programu%n\n quit_descr %> %Bquit: koniec programu %n%b(%n%B%1%2%n%b)%n\n config_changed quit: zapisa now konfiguracj? (tak/nie) quit_keep_reason quit: zapamita opisy? (tak/nie) saved %> %Bconfig: zapisano ustawienia%n\n error_saving %! %Bconfig: podczas zapisu ustawie wystpi bd\n message %b(%B%1%b)%n %3\n message_timestamp (%Y/%m/%d %H:%M) message_timestamp_today (%H:%M) message_timestamp_now message,speech wiadomo od %1: %3 chat %b(%B%4%b)%n %3\n chat_timestamp (%Y/%m/%d %H:%M) chat_timestamp_today (%H:%M) chat_timestamp_now chat,speech wiadomo od %1: %3 sent %b(%B%4%b)%n %3\n sent_timestamp (%Y/%m/%d %H:%M) sent_timestamp_today (%H:%M) sent_timestamp_now sent,speech system %b(%Bsystem%b)%n\n%n%3\n system,speech wiadomo systemowa: %3 //End ack_queued %> Wiadomo do %1 zostanie dostarczona pniej\n ack_delivered %> Wiadomo do %1 zostaa dostarczona\n ack_unknown %> Nie wiadomo, co si stao z wiadomoci do %1\n ack_filtered %! %|Wiadomo do %1 najprawdopodobniej nie zostaa dostarczona, poniewa dana osoba jest niedostpna, a serwer twierdzi, e dorczy wiadomo. Sytuacja taka ma miejsce, gdy wiadomo zostaa odrzucona przez filtry serwera (np. zawiera adres strony WWW)\n message_too_long %! Wiadomo jest zbyt duga i zostaa skrcona\n //Begin status_avail %> %Bstatus: %n%b(%n%B%1%n%b)%n%B jest dostpn%@2%n\n status_avail_descr %> %Bstatus: %n%b(%n%B%1%n%b)%n%B jest dostpn%@2 %n%b(%n%B%4%n%b)%n\n status_away %> %Bstatus: %n%b(%n%B%1%n%b)%n%B jest zajt%@2%n\n status_away_descr %> %Bstatus: %n%b(%n%B%1%n%b)%n%B jest zajt%@2 %n%b(%n%B%4%n%b)%n\n status_xa %> %Bstatus: %n%b(%n%B%1%n%b)%n%B jest bardzo zajt%@2%n\n status_xa_descr %> %Bstatus: %n%b(%n%B%1%n%b)%n%B jest bardzo zajt%@2 %n%b(%n%B%4%n%b)%n\n status_dnd %> %Bstatus: %n%b(%n%B%1%n%b)%n%B nie przeszkadza%n\n status_dnd_descr %> %Bstatus: %n%b(%n%B%1%n%b)%n%B nie przeszkadza %n%b(%n%B%4%n%b)%n\n status_chat %> %Bstatus: %n%b(%n%B%1%n%b)%n%B jest chtn%@2 do rozmowy%n\n status_chat_descr %> %Bstatus: %n%b(%n%B%1%n%b)%n%B jest chtn%@2 do rozmowy %n%b(%n%B%4%n%b)%n\n status_notavail %> %Bstatus: %n%b(%n%B%1%n%b)%n%B jest niedostpn%@2%n\n status_notavail_descr %> %Bstatus: %n%b(%n%B%1%n%b)%n%B jest niedostpn%@2 %n%b(%n%B%4%n%b)%n\n status_invisible %> %Bstatus: %n%b(%n%B%1%n%b)%n%B jest niewidoczn%@2%n\n status_invisible_descr %> %Bstatus: %n%b(%n%B%1%n%b)%n%B jest niewidoczn%@2 %n%b(%n%B%4%n%b)%n\n connecting %> %Bconnect: czenie z serwerem sesji %n%b(%n%B%1%n%b)%n\n conn_failed %! %Bconnect: poczenie z serwerem sesji %n%b(%n%B%2%n%b)%n%B nie powiodo si %n%b(%n%B%1%n%b)%n\n conn_failed_resolving Nie znaleziono serwera conn_failed_connecting Nie mona poczy si z serwerem conn_failed_invalid Nieprawidowa odpowied serwera conn_failed_disconnected Serwer zerwa poczenie conn_failed_password Nieprawidowe haso conn_failed_404 Bd serwera HTTP conn_failed_tls Bd negocjacji TLS conn_failed_memory Brak pamici conn_stopped %! %Bconnect: przerwano czenie z serwerem sesji %n%b(%n%B%1%n%b)%n\n conn_timeout %! %Bconnect: przekroczono limit czasu operacji czenia z serwerem sesji %n%b(%n%B%1%n%b)%n\n connected %> %Bconnect: poczono z serwerem sesji %n%b(%n%B%1%n%b)%n\n connected_descr %> %Bconnect: poczono z serwerem sesji %n%b(%n%B%2%n%b)%n%B i ustawiono opis %n%b(%n%B%1%n%b)%n\n disconnected %> %Bconnect: rozczono z serwerem sesji %n%b(%n%B%1%n%b)%n\n disconnected_descr %> %Bconnect: rozczono z serwerem sesji %n%b(%n%B%2%n%b)%n%B i ustawiono opis %n%b(%n%B%1%n%b)%n\n already_connected %! %Bconnect: poczenie z serwerem sesji %n%b(%n%B%1%n%b)%n%B jest ju ustanowione%n\n during_connect %! %Bconnect: trwa czenie z serwerem sesji %n%b(%n%B%1%n%b)%n%B uyj %n%b(%n%Bdisconnect%n%b)%n%B aby przerwa%n\n conn_broken %! %Bconnect: poczenie z serwerem sesji %n%b(%n%B%1%n%b)%n%B zostao przerwane%n\n conn_disconnected %! %Bconnect: serwer sesji %n%b(%n%B%1%n%b)%n%B przerwa poczenie%n\n not_connected %! %Bconnect: brak poczenia z serwerem sesji %n%b(%n%B%1%n%b)%n\n not_connected_msg_queued %! %Bconnect: brak poczenia z serwerem sesji %n%b(%n%B%1%n%b)%n%B wiadomo bdzie wysana po poczeniu%n\n wrong_id %! %Bconnect: zy id sesji %n%b(%n%B%1%n%b)%n\n inet_addr_failed %! %Bconnect: bdna warto zmiennej \"server\" w sesji %n%b(%n%B%1%n%b)%n%B%n\n invalid_local_ip %! %Bconnect: nieprawidowy adres lokalny, czyszcz zmienn \"local_ip\" w sesji %n%b(%n%B%1%n%b)%n\n theme_loaded %> %Btheme: wczytano motyw %n%b(%n%B%1%n%b)%n\n theme_default %> %Btheme: wczytano domylny motyw%n\n error_loading_theme %! %Btheme: bd podczas adowania motywu %n%b(%n%B%1%n%b)%n\n variable %> %B%1 %n%b=%n%B %2%n\n variable_not_found %! %Bset: nieznana zmienna %n%b(%n%B%1%n%b)%n\n variable_invalid %! %Bset: nieprawidowa warto zmiennej%n\n //End no_config %! Niekompletna konfiguracja. Wpisz:\n%! %Tsession -a <gg:numerek-gg/jid:jabber-id>%n\n%! %Tsession password <haso>%n\n%! %Tsave%n\n%! Nastpnie wydaj polecenie:\n%! %Tconnect%n\n%! Jeli nie masz swojego numerka, wpisz:\n%! %Tregister <e-mail> <haso>%n\n\n%> %|Po poczeniu, nowe okna rozmowy bd tworzone automatycznie, gdy kto przyle wiadomo. Aby przej do okna o podanym numerze naley wcisn %TAlt-numer%n lub %TEsc%n, a nastpnie cyfr. Aby rozpocz rozmow, naley uy polecenia %Tquery%n. Aby doda kogo do listy kontaktw, naley uy polecenia %Tadd%n. Wszystkie kombinacje klawiszy s opisane w pliku %TREADME%n, a list komend wywietla polecenie %Thelp%n. Pamitaj o prefixie przed kadym UID'em, prawidowy przykadowy UID ma posta: %Tgg:<nr>%n. \n\n", 2); no_config,speech niekompletna konfiguracja. wpisz session -a, a za tym gg: nummerek-gg, jid: ewentualnie daber id, a p, potem session pasord, za tym swoje haso. wpisz sejf, eby zapisa ustawienia. wpisz konekt by si poczy. jeli nie masz swojego numeru gadu-gadu, wpisz redister, a po spacji imejl i haso. po poczeniu, nowe okna rozmowy bd tworzone automatycznie, gdy kto przyle wiadomo. aby przej do okna o podanym numerze, naley wcisn alt-numer lub eskejp, a nastpnie cyfr. aby rozpocz rozmow, naley uy polecenia kery. aby doda kogo do listy kontaktw, naley uy polecenia edd. wszystkie kombinacje klawiszy s opisane w pliku ridmi, a list komend wywietla polecenie help. Pamitaj o prefixie przed kadym uidem, prawidowy przykadowy uid ma posta gg:<nr> error_reading_config %! Nie mona odczyta pliku konfiguracyjnego: %1\n //Begin config_read_success %> %Bconfig: wczytano plik konfiguracyjny %n%b(%n%B%1%n%b)%n\n //End config_line_incorrect %! Nieprawidowa linia '%T%1%n', pomijam\n autosaved %> Automatycznie zapisano ustawienia\n register %> Rejestracja poprawna. Wygrany numerek: %T%1%n\n register_failed %! Bd podczas rejestracji: %1\n register_pending %! Rejestracja w toku\n register_timeout %! Przekroczono limit czasu operacji rejestrowania\n registered_today %! Ju zarejestrowano jeden numer. Nie naduywaj\n unregister %> Konto %T%1%n zostao usunite\n unregister_timeout %! Przekroczono limit czasu operacji usuwania konta\n unregister_bad_uin %! Niepoprawny numer: %T%1%n\n unregister_failed %! Bd podczas usuwania konta: %1\n remind %> Haso zostao wysane\n remind_failed %! Bd podczas wysyania hasa: %1\n remind_timeout %! Przekroczono limit czasu operacji wysania hasa\n passwd %> Haso zostao zmienione\n passwd_failed %! Bd podczas zmiany hasa: %1\n passwd_timeout %! Przekroczono limit czasu operacji zmiany hasa\n change %> Informacje w katalogu publicznym zostay zmienione\n change_failed %! Bd podczas zmiany informacji w katalogu publicznym\n search_failed %! Wystpi bd podczas szukania: %1\n search_not_found %! Nie znaleziono\n search_no_last %! Brak wynikw ostatniego wyszukiwania\n search_no_last_nickname %! Brak pseudonimu w ostatnim wyszukiwaniu\n search_stopped %> Zatrzymano wyszukiwanie\n search_results_multi_avail %Y<>%n search_results_multi_away %G<>%n search_results_multi_invisible %c<>%n search_results_multi_notavail search_results_multi_unknown - search_results_multi %7 %[-7]1 %K|%n %[12]3 %K|%n %[12]2 %K|%n %[4]5 %K|%n %[12]4\n //Begin search_results_single_avail %n%b(%n%Bdostpn%@1%n%b)%n search_results_single_away %n%b(%n%Bzajt%@1%n%b)%n search_results_single_notavail %n%b(%n%Bniedostpn%@1%b)%n search_results_single_invisible %n%b(%n%Bniewidoczn%@1%b)%n search_results_single_unknown %C-%n search_results_single %b(%n%C(%n%B find start %n%C)%n%b)%n\n %Bpseudo %n%c%3%n\n %Bgg %n%c%1%n %7\n %Bimi i nazwisko %c%2%n\n %Bmiejscowo %c%4%n\n %Brok urodzenia %c%5%n\n%b(%n%C(%n%B find end %n%C)%n%b)%n\n process %B%2 %n%b(%n%B%1%n%b)%n\n no_processes %> %Bexec: brak dziaajcych procesw%n\n process_exit %> %Bexec: proces %n%b(%n%B%2%n%b) (%n%B%1%n%b)%n%B zakoczy dziaanie z wynikiem %n%b(%n%B%3%n%b)%n\n exec %1\n exec_error %! %Bexec: podczas uruchamiania procesu wystpi bd %n%b(%n%B%1%n%b)%n\n exec_prompt $ %1\n user_info_header %b(%C(%n%B userinfo start %C)%b)%n\n%> %c%1 %b(%B%2%b)%n\n user_info_nickname %Bpseudo %c%1%n\n user_info_name %Bimi i nazwisko %c%1 %2%n\n user_info_status %Bjest %n%1%n\n user_info_status_time_format %Y-%m-%d %H:%M user_info_status_time %Bod %c%1%n\n user_info_auth_type %Bautoryzacja %c%1%n\n user_info_block %Bjest %cblokowan%@1\n user_info_offline %Bnie widzi stanu\n user_info_not_in_contacts %Bnie ma nas w swoich kontaktach\n user_info_firewalled %Bjest za %cfirewall/NAT\n user_info_ip %Badres %c%1%n\n user_info_mobile %Btelefon %c%1%n\n user_info_groups %Bgrupy %c%1%n\n user_info_never_seen %Bnigdy %cnie widziano\n user_info_last_seen %Bwidziano %c%1%n\n user_info_last_seen_time %Y-%m-%d %H:%M user_info_last_ip %Bpoprzedni adres %c%1%n\n user_info_last_status %Bostatnio %n%1%n\n user_info_footer %b(%C(%n%B userinfo end %n%C)%b)%n\n user_info_avail %cdostpn%@1%n user_info_avail_descr %cdostpn%@1%n %b(%n%B%2%n%b)%n user_info_away %czajt%@1%n user_info_away_descr %czajt%@1%n %b(%n%B%2%n%b)%n user_info_notavail %cniedostpn%@1%n user_info_notavail_descr %cniedostpn%@1%n %b(%n%B%2%n%b)%n user_info_invisible %cniewidoczn%@1%n user_info_invisible_descr %cniewidoczn%@1%n %b(%n%B%2%n%b)%n user_info_dnd %cnie przeszkadza%n user_info_dnd_descr %cnie przeszkadza%n %b(%n%B%2%n%b)%n user_info_chat %cchtn%@1 do rozmowy%n user_info_chat_descr %cchtn%@1 do rozmowy%n %b(%n%B%2%n%b)%n user_info_error %cbd%n user_info_error_descr %cbd%n %b(%n%B%2%n%b)%n user_info_xa %cbardzo zajt%@1%n user_info_xa_descr %cbardzo zajt%@1%n %b(%n%B%2%n%b)%n user_info_blocked %cblokujc%@1%n user_info_blocked_descr %cblokujc%@1%n %b(%n%B%2%n%b)%n user_info_unknown %cnieznany%n group_members %> %Bgroup: czonkowie grupy %n%b(%n%B%1%n%b)%n%B to %n%b(%n%B%2%n%b)%n\n group_member_already %! %Bgroup: %n%b(%n%B%1%n%b)%n%B naley ju do grupy %n%b(%n%B%2%n%b)%n\n group_member_not_yet %! %Bgroup: %n%b(%n%B%1%n%b)%n%B nie naley do grupy %n%b(%n%B%2%n%b)%n\n group_empty %! %Bgroup: grupa %n%b(%n%B%1%n%b)%n%B jest pusta%n\n show_status_header %b(%n%C(%n%B status start %n%C)%n%b)%n\n show_status_profile %Bprofil %n%c%1%n\n show_status_uid %Buid %n%c%1%n\n show_status_uid_nick %Buid %n%c%1 %n%b(%n%B%2%n%b)%n\n show_status_status %Baktualny stan %n%1%2%n\n show_status_status_simple %Baktualny stan %n%1%n\n show_status_server %Baktualny serwer %n%c%1%n%B port %n%c%2%n\n show_status_server_tls %Baktualny serwer %n%c%1%n%B port %n%c%2 %n%b(%n%Bpoczenie szyfrowane%n%b)%n\n show_status_connecting %Btrwa czenie ...%n\n show_status_avail %cdostpny%n show_status_avail_descr %cdostpny %n%b(%n%B%1%n%c%2%n%b)%n show_status_away %czajty%n show_status_away_descr %czajty%n %n%b(%n%B%1%n%c%2%n%b)%n show_status_invisible %cniewidoczny%n show_status_invisible_descr %cniewidoczny%n %n%b(%n%B%1%n%c%2%n%b)%n show_status_xa %cbardzo zajty%n show_status_xa_descr %cbardzo zajty%n %n%b(%n%B%1%n%c%2%n%b)%n show_status_dnd %cnie przeszka%n show_status_dnd_descr %cnie przeszkadza%n %n%b(%n%B%1%n%c%2%n%b)%n show_status_chat %cchtny do rozmowy%n show_status_chat_descr %cchtny do rozmowy%n %n%b(%n%B%1%n%c%2%n%b)%n show_status_notavail %cniedostpny%n show_status_private_on %B, %ctylko dla znajomych%n show_status_private_off show_status_connected_since %Bpoczony od %n%c%1%n\n show_status_disconnected_since %Brozczony od %n%c%1%n\n show_status_last_conn_event %Y-%m-%d %H:%M show_status_last_conn_event_today %H:%M show_status_ekg_started_since %Bprogram dziaa od %n%c%1%n\n show_status_ekg_started %Y-%m-%d %H:%M show_status_ekg_started_today %H:%M show_status_msg_queue %Bilo wiadomoci w kolejce do wysania %n%c%1%n\n show_status_footer %b(%n%C(%n%B status end %n%C)%n%b)%n\n //End aliases_list_empty %! Brak aliasw\n aliases_list %> %T%1%n: %2\n aliases_list_next %> %3 %2\n aliases_add %> Utworzono alias %T%1%n\n aliases_append %> Dodano do aliasu %T%1%n\n aliases_del %) Usunito alias %T%1%n\n aliases_del_all %) Usunito wszystkie aliasy\n aliases_exist %! Alias %T%1%n ju istnieje\n aliases_noexist %! Alias %T%1%n nie istnieje\n aliases_command %! %T%1%n jest wbudowan komend\n aliases_not_enough_params %! Alias %T%1%n wymaga wikszej iloci parametrw\n dcc_attack %! %|Program otrzyma zbyt wiele da bezporednich pocze, ostatnie od %1\n dcc_limit %! %|Przekroczono limit bezporednich pocze i dla bezpieczestwa zostay one wyczone. Aby je wczy ponownie, naley wpisa polecenie %Tset dcc 1%n i poczy si ponownie. Limit mona zmieni za pomoc zmiennej %Tdcc_limit%n.\n dcc_create_error %! Nie mona wczy pocze bezporednich: %1\n dcc_error_network %! Bd transmisji z %1\n dcc_error_refused %! Poczenie z %1 zostao odrzucone\n dcc_error_unknown %! Nieznany bd poczenia bezporedniego\n dcc_error_handshake %! Nie mona nawiza poczenia z %1\n dcc_user_aint_dcc %! %1 nie ma wczonej obsugi pocze bezporednich\n dcc_timeout %! Przekroczono limit czasu operacji bezporedniego poczenia z %1\n dcc_not_supported %! Opcja %T%1%n nie jest jeszcze obsugiwana\n dcc_open_error %! Nie mona otworzy %T%1%n: %2\n dcc_show_pending_header %> Poczenia oczekujce:\n dcc_show_pending_send %) #%1, %2, wysyanie %T%3%n\n dcc_show_pending_get %) #%1, %2, odbir %T%3%n\n dcc_show_pending_voice %) #%1, %2, rozmowa\n dcc_show_active_header %> Poczenia aktywne:\n dcc_show_active_send %) #%1, %2, wysyanie %T%3%n, %T%4b%n z %T%5b%n (%6%%)\n dcc_show_active_get %) #%1, %2, odbir %T%3%n, %T%4b%n z %T%5b%n (%6%%)\n dcc_show_active_voice %) #%1, %2, rozmowa\n dcc_show_empty %! Brak bezporednich pocze\n dcc_receiving_already %! Plik %T%1%n od uytkownika %2 jest ju pobierany\n dcc_done_get %> Zakoczono pobieranie pliku %T%2%n od %1\n dcc_done_send %> Zakoczono wysyanie pliku %T%2%n do %1\n dcc_close %) Zamknito poczenie z %1\n dcc_voice_offer %) %1 chce rozmawia\n%) Wpisz %Tdcc voice #%2%n, by rozpocz rozmow, lub %Tdcc close #%2%n, by anulowa\n dcc_voice_running %! Mona prowadzi tylko jedn rozmow gosow na raz\n dcc_voice_unsupported %! Nie wkompilowano obsugi rozmw gosowych. Przeczytaj %Tdocs/voip.txt%n\n dcc_get_offer %) %1 przesya plik %T%2%n o rozmiarze %T%3b%n\n%) Wpisz %Tdcc get #%4%n, by go odebra, lub %Tdcc close #%4%n, by anulowa\n dcc_get_offer_resume %) Plik istnieje ju na dysku, wic mona wznowi pobieranie poleceniem %Tdcc resume #%4%n\n dcc_get_getting %) Rozpoczto pobieranie pliku %T%2%n od %1\n dcc_get_cant_create %! Nie mona utworzy pliku %T%1%n\n dcc_not_found %! Nie znaleziono poczenia %T%1%n\n dcc_invalid_ip %! Nieprawidowy adres IP\n dcc_user_notavail %! %1 musi by dostpn%@1, by mc nawiza poczenie\n //Begin query_started %) %Brozmowa: rozpoczto rozmow z %n%b(%n%B%1%n%b)%n%B w sesji %n%b(%n%B%2%n%b)%n\n query_started_window %) %Brozmowa: %n%b(%n%BAlt-G%n%b)%n%B by ignorowa, %n%b(%n%BAlt-K%n%b)%n%B by zamkn okno%n\n query_finished %) %Brozmowa: zakoczono rozmow z %n%b(%n%B%1%n%b)%n%B w sesji %n%b(%n%B%2%n%b)%n\n //End query_exist %! Rozmowa z %T%1%n jest ju prowadzona w okienku nr %T%2%n\n events_list_empty %! Brak zdarze\n events_list_header events_list %> %5 on %1 %3 %4 - prio %2\n events_add %> Dodano zdarzenie %T%1%n\n events_del %) Usunito zdarzenie %T%1%n\n events_del_all %) Usunito wszystkie zdarzenia\n events_exist %! Zdarzenie %T%1%n istnieje dla %2\n events_del_noexist %! Zdarzenie %T%1%n nie istnieje\n userlist_put_ok %> List kontaktw zachowano na serwerze\n userlist_put_error %! Bd podczas wysyania listy kontaktw\n userlist_get_ok %> List kontaktw wczytano z serwera\n userlist_get_error %! Bd podczas pobierania listy kontaktw\n userlist_clear_ok %) Usunito list kontaktw z serwera\n userlist_clear_error %! Bd podczas usuwania listy kontaktw\n quick_list %)%1\n quick_list,speech lista kontaktw: quick_list_avail %Y%1%n quick_list_avail,speech %1 jest dostpny, quick_list_away %G%1%n quick_list_away,speech %1 jest zajty, quick_list_invisible %c%1%n window_add %) Utworzono nowe okno\n window_noexist %! Wybrane okno nie istnieje\n window_no_windows %! Nie mona zamkn ostatniego okna\n window_del %) Zamknito okno\n windows_max %! Wyczerpano limit iloci okien\n window_list_query %) %1: rozmowa z %T%2%n\n window_list_nothing %) %1: brak rozmowy\n window_list_floating %) %1: pywajce %4x%5 w %2,%3 %T%6%n\n //Begin window_id_query_started %) %Brozmowa: rozpoczto rozmow z %n%b(%n%B%2%n%b)%n%B w oknie %n%b(%n%B%1%n%b)%n\n //End window_kill_status %! Nie mona zamkn okna stanu\n bind_seq_incorrect %! Sekwencja %T%1%n jest nieprawidowa\n bind_seq_add %> Dodano sekwencj %T%1%n\n bind_seq_remove %) Usunito sekwencj %T%1%n\n bind_seq_list %> %1: %T%2%n\n bind_seq_exist %! Sekwencja %T%1%n ma ju przypisan akcj\n bind_seq_list_empty %! Brak przypisanych akcji\n at_list %> %1, %2, %3 %K(%4)%n %5\n at_added %> Utworzono plan %T%1%n\n at_deleted %) Usunito plan %T%1%n\n at_deleted_all %) Usunito plany uytkownika\n at_exist %! Plan %T%1%n ju istnieje\n at_noexist %! Plan %T%1%n nie istnieje\n at_empty %! Brak planw\n at_timestamp %d-%m-%Y %H:%M at_back_to_past %! Gdyby mona byo cofn czas...\n timer_list %> %1, %2s, %3 %K(%4)%n %T%5%n\n timer_added %> Utworzono timer %T%1%n\n timer_deleted %) Usunito timer %T%1%n\n timer_deleted_all %) Usunito timery uytkownika\n timer_exist %! Timer %T%1%n ju istnieje\n timer_noexist %! Timer %T%1%n nie istnieje\n timer_empty %! Brak timerw\n last_list_in %) %Y <<%n [%1] %2 %3\n last_list_out %) %G >>%n [%1] %2 %3\n last_list_empty %! Nie zalogowano adnych wiadomoci\n last_list_empty_nick %! Nie zalogowano adnych wiadomoci dla %T%1%n\n last_list_timestamp %d-%m-%Y %H:%M last_list_timestamp_today %H:%M last_clear_uin %) Wiadomoci dla %T%1%n wyczyszczone\n last_clear %) Wszystkie wiadomoci wyczyszczone\n queue_list_timestamp %d-%m-%Y %H:%M queue_list_message %) %G >>%n [%1] %2 %3\n queue_clear","%) Kolejka wiadomoci wyczyszczona\n queue_clear_uid","%) Kolejka wiadomoci wyczyszczona dla %T%1%n\n queue_wrong_use %! Komenda dziaa tylko przy braku poczenia z serwerem\n queue_empty %! Kolejka wiadomoci jest pusta\n queue_empty_uid %! Brak wiadomoci w kolejce dla %T%1%n\n queue_flush %> Wysano zalege wiadomoci z kolejki\n conferences_list_empty %! Brak konferencji\n conferences_list %> %T%1%n: %2\n conferences_list_ignored %> %T%1%n: %2 (%yingorowana%n)\n conferences_add %> Utworzono konferencj %T%1%n\n conferences_not_added %! Nie utworzono konferencji %T%1%n\n conferences_del %) Usunito konferencj %T%1%n\n conferences_del_all %) Usunito wszystkie konferencje\n conferences_exist %! Konferencja %T%1%n ju istnieje\n conferences_noexist %! Konferencja %T%1%n nie istnieje\n conferences_name_error %! Nazwa konferencji powinna zaczyna si od %T#%n\n conferences_rename %> Nazwa konferencji zmieniona: %T%1%n --> %T%2%n\n conferences_ignore %> Konferencja %T%1%n bdzie ignorowana\n conferences_unignore %> Konferencja %T%1%n nie bdzie ignorowana\n conferences_joined %> Doczono %1 do konferencji %T%2%n\n conferences_already_joined %> %1 uczestniczy ju w konferencji %T%2%n\n http_failed_resolving Nie znaleziono serwera http_failed_connecting Nie mona poczy si z serwerem http_failed_reading Serwer zerwa poczenie http_failed_writing Serwer zerwa poczenie http_failed_memory Brak pamici key_generating %> Czekaj, generuj klucze...\n key_generating_success %> Wygenerowano i zapisano klucze\n key_generating_error %! Wystpi bd podczas generowania kluczy: %1\n key_private_exist %! Posiadasz ju swoj par kluczy\n key_public_deleted %) Klucz publiczny %1 zosta usunity\n key_public_not_found %! Nie znaleziono klucza publicznego %1\n key_public_noexist %! Brak kluczy publicznych\n key_public_received %> Otrzymano klucz publiczny od %1\n key_public_write_failed %! Bd podczas zapisu klucza publicznego: %1\n key_send_success %> Wysano klucz publiczny do %1\n key_send_error %! Bd podczas wysyania klucza publicznego\n key_list %> %1 (%3)\n%) %2\n key_list_timestamp %Y-%m-%d %H:%M python_list %> %1\n python_list_empty %! Brak zaadowanych skryptw\n python_removed %) Skrypt zosta usunity\n python_need_name %! Nie podano nazwy skryptu\n python_not_found %! Nie znaleziono skryptu %T%1%n\n python_wrong_location %! Skrypt naley umieci w katalogu %T%1%n\n session_name %1%n session_variable %> %T%1->%2 = %R%3%n\n session_variable_removed %> Usunito %T%1->%2%n\n session_variable_doesnt_exist %! Nieznana zmienna: %T%1->%2%n\n session_list %> %T%1%n %3\n session_list_alias %> %T%2%n/%1 %3\n session_list_empty %! Lista sesji jest pusta\n session_info_header %) %T%1%n %3\n session_info_header_alias %) %T%2%n/%1 %3\n session_info_param %) %1 = %T%2%n\n session_info_footer session_exists %! Sesja %T%1%n ju istnieje\n session_doesnt_exist %! Sesja %T%1%n nie istnieje\n session_added %> Utworzono sesj %T%1%n\n session_removed %> Usunito sesj %T%1%n\n session_format %T%1%n session_format_alias %T%1%n/%2 session_cannot_change %! Nie mona zmieni sesji w okienku rozmowy%n\n metacontact_list %> %T%1%n metacontact_list_empty %! Nie ma adnych metakontaktw\n metacontact_exists %! Metakontakt %T%1%n ju istnieje\n metacontact_added %> Utworzono metakontakt %T%1%n\n metacontact_removed %> Usunito metakontakt %T%1%n\n metacontact_doesnt_exist %! Metakontakt %T%1%n nie istnieje\n metacontact_added_item %> Dodano %T%1/%2%n do %T%3%n\n metacontact_removed_item %> Usunito %T%1/%2%n z %T%3%n\n metacontact_item_list_header metacontact_item_list %> %T%1/%2 (%3)%n - prio %T%4%n\n metacontact_item_list_empty %! Metakontakt jest pusty\n metacontact_item_list_footer metacontact_item_doesnt_exist %! Kontakt %T%1/%2%n nie istnieje\n metacontact_info_header %K.--%n Metakontakt %T%1%n %K--- -- -%n\n metacontact_info_status %K| %nStan: %T%1%n\n metacontact_info_footer %K`----- ---- --- -- -%n\n metacontact_info_avail %Ydostpn%@1%n metacontact_info_avail_descr %Ydostpn%@1%n %K(%n%2%K)%n metacontact_info_away %Gzajt%@1%n metacontact_info_away_descr %Gzajt%@1%n %K(%n%2%K)%n metacontact_info_notavail %rniedostpn%@1%n metacontact_info_notavail_descr %rniedostpn%@1%n %K(%n%2%K)%n metacontact_info_invisible %cniewidoczn%@1%n metacontact_info_invisible_descr %cniewidoczn%@1%n %K(%n%2%K)%n metacontact_info_dnd %cnie przeszkadza%n metacontact_info_dnd_descr %cnie przeszkadza%n %K(%n%2%K)%n metacontact_info_chat %Ychtn%@1 do rozmowy%n metacontact_info_chat_descr %Ychtn%@1 do rozmowy%n %K(%n%2%K)%n metacontact_info_error %mbd%n metacontact_info_error_descr %mbd%n %K(%n%2%K)%n metacontact_info_xa %cbardzo zajt%@1%n metacontact_info_xa_descr %cbardzo zajt%@1%n %K(%n%2%K)%n metacontact_info_blocked %mblokujc%@1%n metacontact_info_blocked_descr %mblokujc%@1%n %K(%n%2%K)%n metacontact_info_unknown %Mnieznany%n plugin_already_loaded %! Plugin %T%1%n jest ju zaadowany%n.\n plugin_doesnt_exist","%! Plugin %T%1%n nie moe zosta znaleziony%n\n plugin_incorrect %! Plugin %T%1%n nie jest prawidowym pluginem EKG2%n\n plugin_not_initialized %! Plugin %T%1%n nie zosta wczytany poprawnie%n\n readline_prompt % readline_prompt_away / readline_prompt_invisible . readline_prompt_query %1> readline_prompt_win %1%% readline_prompt_away_win %1/ readline_prompt_invisible_win %1. readline_prompt_query_win %2:%1> readline_prompt_win_act %1 (act/%2)%% readline_prompt_away_win_act %1 (act/%2)/ readline_prompt_invisible_win_act %1 (act/%2). readline_prompt_query_win_act %2:%1 (act/%3)> readline_more [-40]- Wcinij Enter by kontynuowa lub Ctrl-D by przerwa -- //Begin irc_msg_sent %b(%B%3%b/%B%5%b)%n %6 irc_msg_sent_n %b(%B%3%b)%n %6 irc_msg_sent_chan %b(%C%2%B%3%b)%n %6 irc_msg_sent_chanh %b(%C%2%W%3%b)%n %6 irc_msg_f_chan %b(%C%2%B%3%b/%B%5%b)%n %6 irc_msg_f_chanh %b(%C%2%W%3%b/%W%5%b)%W %6 irc_msg_f_chan_n %b(%C%2%B%3%b)%n %6 irc_msg_f_chan_nh %b(%C%2%W%3%b)%W %6 irc_msg_f_some %b(%B%3%b)%n %6 irc_not_f_chan %B(%C%2%B%3%b/%B%5%b)%n %6 irc_not_f_chanh %B(%C%2%W%3%b/%W%5%b)%W %6 irc_not_f_chan_n %B(%C%2%B%3%b)%n %6 irc_not_f_chan_nh %B(%C%2%W%3%b)%W %6 irc_not_f_some %b(%B%3%b)%n %6 irc_joined %> %Bjoin: %b(%B%2%b)%B %b(%B%3%b)%n\n irc_left %> %Bpart: %b(%B%2%b)%B %b(%B%3%b)%B %b(%B%5%b)%n\n irc_kicked %> %Bkick: %b(%B%2%b)%B was kicked by %b(%B%3%b)%B %b(%B%6%b)%n\n irc_kicked_you %> %Bkick: you were kicked by %b(%B%3%b)%B %b(%B%6%b)%n\n irc_quit %> %Bquit: %b(%B%2%b)%B %b(%B%3%b)%B %b(%B%4%b)%n\n //End irc_unknown_ctcp %> %Y%2%n sent unknown CTCP %3: (%4)\n irc_ctcp_action_y_pub %> %y%e* %2%n %4 irc_ctcp_action_y %> %Y%e* %2%n %4 irc_ctcp_action_pub %> %y%h* %2%n %5 irc_ctcp_action %> %Y%h* %2%n %5 irc_ctcp_request_pub %> %Y%2%n requested ctcp %4 from %3\n irc_ctcp_request %> %Y%2%n requested ctcp %4\n irc_ctcp_reply %> %Y%2%n CTCP reply from %3: %4\n IRC_ERR_CANNOTSENDTOCHAN %! %2: %1\n IRC_RPL_FIRSTSECOND %> (%1) %2 %3\n IRC_RPL_SECONDFIRST %> (%1) %3 %2\n IRC_RPL_JUSTONE %> (%1) %2\n IRC_RPL_NEWONE %> (%1) 1:%2 2:%3 3:%4 4:%5\n IRC_ERR_FIRSTSECOND %! (%1) %2 %3\n IRC_ERR_SECONDFIRST %! (%1) %3 %2\n IRC_ERR_JUSTONE %! (%1) %2\n IRC_ERR_NEWONE %! (%1) 1:%2 2:%3 3:%4 4:%5\n IRC_RPL_CANTSEND %> Cannot send to channel %T%2%n\n RPL_WHOISOPERATOR %G||%n %|ircOp : %3\n IRC_RPL_TOPICBY %> set by %2 on %3 IRC_TOPIC_CHANGE %> %T%2%n changed topic on %T%4%n: %5\n IRC_TOPIC_UNSET %> %T%3%n unset topic on %T%2%n\n IRC_MODE_CHAN %> %2/%3 sets mode%4\n IRC_MODE %> %2 set mode %3 on You\n IRC_PINGPONG %) ping/pong %c%2%n\n IRC_YOUNEWNICK %> You are now known as %G%2%n\n IRC_NEWNICK %> %g%2%n is now known as %G%4%n\n ��������������������������������������������������������������������������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/contrib/themes/dmilith.theme������������������������������������������������0000664�0000000�0000000�00000017502�11751427534�0021565�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# # 2oo8 on MIT license # ekg2 theme by dmilith & wiechu # dmilith [at] gmail.com # wiechu at wiechu dot com # theme version 0.9 # -query_started_window irc_channel_unsecure statusbar %k%s[%W%s%{time}%k] %G%{?session %{session}%k(%W%{?avail dostępny}%{?away zajęty}%{?invisible ukryty}%{?notavail niedostępny}%{?xa bardzo zajęty}%{?dnd nie przeszkadzać}%{?chat chętny do rozmowy}%k)}%{?!session brak sesji}%k [%s%Y%{?window %{window}}%{?query : %{query}}%{?debug debug}%{?activity %k] [%Waktywność: }%{activity}%{?mail %k] [%n%B%s mail:}%{mail}%{?more %s%k] [%Gwięcej}%k]%s statusbar_timestamp %H:%M.%S statusbar_act_important %G statusbar_act %K contacts_vertical_line_char curses_prompt_none [puste] message %K%r#-%R%4%r-# %n%2%n%3 chat %K%r#-%R%4%r-# %n%2%n%3 sent %K%g#-%G%4%g-# %n%3 conference %c<%C%1%c>%T %|%T%3 irc_msg_sent_chan %b<%n%2%T%3%g%T%b>%n %6 irc_msg_sent_n %b<%n%2%T%3%g%T%b>%n %6 irc_msg_f_chan_n %b<%C%2%g%3%b>%n %6 irc_msg_f_chan_nh %b<%C%2%Y%3%b>%W %6 irc_msg_f_some %b<%g%3%b>%n %T%6 irc_not_f_chan %B<%C%2%B%3%b/%g%5%b>%n %T%6 irc_not_f_chanh %B<%C%2%W%3%b/%W%5%b>%W %T%6 irc_not_f_chan_n %B<%C%2%g%3%b>%n %T%6 irc_not_f_chan_nh %l%y * %3%n %6 irc_not_f_some %b<%B%3%b>%n %T%6 irc_f_some %b<%3%b>%n irc_joined %> %Kjoin: %k(%K%2%k)%B %k(%K%3%k)%n irc_left %> %Kpart: %k(%K%2%k)%B %k(%K%3%k)%B %k(%K%5%k)%n irc_kicked %> %Kkick: %k(%K%2%k)%K wykopany przez %k(%K%3%k)%K %k(%K%6%k)%n irc_kicked_you %> %Kkick: zoskałeś wykopany przez %k(%K%3%k)%K %k(%K%6%k)%n irc_quit %> %Kquit: %k(%K%2%k)%K %k(%K%3%k)%K %k(%K%4%k)%n irc_unknown_ctcp %> %y%2%n przesłano nierozpoznane CTCP %3: (%4) irc_ctcp_action_y_pub %> %l%y* %2%n %4 irc_ctcp_action_y %> %l%y* %2%n %4 irc_ctcp_action_pub %> %l%y* %2%n %5 irc_ctcp_action %> %l%y* %2%n %5 irc_ctcp_request_pub %> %l%y%2%n zażądano CTCP %4 od %3 irc_ctcp_request %> %l%y%2%n zażądał CTCP %4 irc_ctcp_reply %> %l%y%2%n odpowiedź CTCP od %3: %4 IRC_ERR_CANNOTSENDTOCHAN %! %2: %1 IRC_RPL_FIRSTSECOND %> (%1) %2 %3 IRC_RPL_SECONDFIRST %> (%1) %3 %2 IRC_RPL_JUSTONE %> (%1) %2 IRC_RPL_NEWONE %> (%1) 1:%2 2:%3 3:%4 4:%5 IRC_ERR_FIRSTSECOND %! (%1) %2 %3 IRC_ERR_SECONDFIRST %! (%1) %3 %2 IRC_ERR_JUSTONE %! (%1) %2 IRC_ERR_NEWONE %! (%1) 1:%2 2:%3 3:%4 4:%5 IRC_RPL_CANTSEND %> Brak praw do wysyłania na kanał %T%2%n RPL_WHOISOPERATOR %G||%n %|ircOp : %3 IRC_RPL_TOPICBY %> ustawiony przez %2 na %3 IRC_TOPIC_CHANGE %> %T%2%n zmienił temat kanału %T%4%n na: %5 IRC_TOPIC_UNSET %> %T%3%n wyłączył temat kanału %T%4 IRC_MODE_CHAN %> %2/%3 IRC_MODE %> %2 ustawił tryb %3 na Tobie IRC_PINGPONG %) pi/po %K%2%n IRC_YOUNEWNICK %> %l%y* Zmieniłeś nicka na %l%y%3%n IRC_NEWNICK %> %l%y* %2%n zmienił nicka na %l%y%4%n # (C) Copyright 2008 Wieslaw Ochminski <wiechu at wiechu dot com> # # IRC RPL_MOTDSTART %c%Alwqqqqqq%a Message Of The Day %c%Aqqqqq%a\n RPL_MOTD %c%Axx %a%n%2\n RPL_ENDOFMOTD %c%Amvqqqqqq%a\n RPL_WHOISUSER %G%Alwqqqqqqqq%a\n%G%Axx%a%n (%T%2%n) (%3@%4)\n%G%Axx%a%n realname : %6\n RPL_WHOISCHANNELS %G%Axx%a%n %|channels : %3\n RPL_WHOISOPERATOR %G%Axx%a%n %|ircOp : %3\n RPL_WHOREPLY %G%Axx%a %c%3 %W%7 %n%8 %6 %4@%5 %W%9\n RPL_WHOISSERVER %G%Axx%a%n %|server : %3 (%4)\n RPL_WHOISIDLE %G%Axx%a%n %|idle : %3 (signon: %4)\n RPL_ENDOFWHOIS %G%Amvqqqqqqqq%a\n RPL_WHOWASUSER %G%Alwqqqqqqqq%a\n%G%Axx%a%n (%T%2%n) (%3@%4)\n%G%Axx%a%n realname : %6\n RPL_ENDOFWHOWAS %G%Amvqqqqqqqq%a\n RPL_AWAY %G%Axx%a%n away : %2 - %3\n RPL_BANLIST %G%Axx%a %n %5 - %W%2%n: ban %c%3\n RPL_CHLIST %G%Axx%a %n %5 %2\t%3\t%4\n RPL_LISTSTART %G%Alwqqqqqqqq%a\n RPL_EMPTYLIST %G%Axx%a %n Empty list \n RPL_ENDOFLIST %G%Amvqqqqqqqq%a %2%n\n RPL_EXCEPTLIST %G%Axx%a %n %5 - %W%2%n: except %c%3\n RPL_INVITELIST %G%Axx%a %n %5 - %W%2%n: invite %c%3\n RPL_LINKS %G%Axx%a %n %5 - %2 %3 %4\n RPL_LIST %G%Axx%a %n %5 %2\t%3\t%4\n RPL_STATS %G%Axx%a %3 %n %4 %5 %6 %7 %8\n RPL_STATSEND %G%Amvqqq%a%g%3%G%Aqqq%a %2\n RPL_STATS_EXT %G%Axx%a %3 %n %2 %4 %5 %6 %7 %8\n IRC_WHOERROR %G%Alwqqqqqqqq%a\n%G||%n %3 (%2)\n irc_awaylog_begin %G%Alwqqqqqqqq%a Awaylog for: (%1)\n irc_awaylog_end %G%Amvqqqqqqqq%a\n irc_awaylog_msg %G%Axx%a %n[%Y%2%n] <%W%4%n> %5\n irc_awaylog_msg_chan %G%Axx%a %n[%Y%2%n] [%G%3%n] <%W%4%n> %5\n # GG system %m%Alqqq%a %TSystem message %m%Aqqq qq q%a\n%m%Ax%a%n %|%3%n\n%|%m%Amqqqqq qqqq qqq qq q%a%n\n gg_user_info_version %c%Ax%x %nVersion: %T%1%n\n gg_user_info_firewalled %c%Ax%a %nFirewalled/NATed\n user_info_ip %c%Ax%a %nAddress: %T%1%n\n user_info_last_ip %c%Ax%a %nLast address: %T%1%n\n user_info_mobile %c%Ax%a %nTelephone: %T%1%n\n user_info_name %c%Ax%a %nName: %T%1 %2%n\n gg_user_info_not_in_contacts %c%Ax%a %nDoesn't have us in roster\n gg_user_info_voip %c%Ax%a %nVoIP-capable\n user_info_header %c%Alqq%a%n %T%1%n/%2 %c%Aqqq qq q%a%n\n user_info_status %c%Ax%a %nStatus: %T%1%n\n user_info_status_time %c%Ax%a %nCurrent status since: %T%1%n\n user_info_last_status %c%Ax%a %nLast status: %T%1%n\n user_info_last_seen %c%Ax%a %nLast seen: %T%1%n\n user_info_never_seen %c%Ax%a %nNever seen\n user_info_block %c%Ax%a %nBlocked\n user_info_auth_type %c%Ax%a %nSubscription type: %T%1%n\n user_info_footer %c%Amqqqqq qqqq qqq qq q%a%n\n user_info_gpg_key %c%Ax%a %nGPGKEY: %T%1%n (%2)%n user_info_groups %c%Ax%a %nGroups: %T%1%n\n user_info_name %c%Ax%a %nName: %T%1 %2%n\n user_info_nickname %c%Ax%a %nNickname: %T%1%n\n user_info_offline %c%Ax%a %nCan't see our status\n search_results_multi %7 %c%Ax%a%n %[-8]1 %c%Ax%a%n %[12]3 %c%Ax%a%n %[12]2 %c%Ax%a%n %[4]5 %c%Ax%a%n %[12]4\n metacontact_info_header %c%Alqq%a%n Metacontact %T%1%n %c%Aqqq qq q%a%n\n metacontact_info_status %c%Ax%a %nStatus: %T%1%n\n metacontact_info_footer %c%Amqqqqq qqqq qqq qq q%a%n\n # Jabber xmpp_feature_header %g%Alwqqqqqq%a%G XMPP features %n(%T%2%n%3%n) xmpp_feature %g%Axx%a %n %W%2%n can: %5 [%G%3%g,%4%n] xmpp_feature_sub %g%Axx%a %n %W%3%n: %5 [%G%4%n] xmpp_feature_sub_unknown %g%Axx%a %n %W%3%n: Unknown, report to devs [%G%4%n] xmpp_feature_unknown %g%Axx%a %n %W%2%n feature: %r%3 %n[%G%3%g,%4%n] xmpp_feature_footer %g%Amvqqqqqq%a %n Turn it off using: /session display_server_features 0\n jabber_transinfo_begin %g%Alwqqqqqq%a%G Information about: %T%2%n jabber_transinfo_begin_node %g%Alwqqqqqq%a%G Information about: %T%2%n (%3) jabber_transinfo_identify %g%Axx%a %G --== %g%3 %G==--%n jabber_transinfo_comm_not %g%Axx%a %n %W%2%n can: %n%3 (%4) jabber_transinfo_comm_ser %g%Axx%a %n %W%2%n can: %n%3 %2 (%4) jabber_transinfo_comm_use %g%Axx%a %n %W%2%n can: %n%3 $uid (%4) # jabber_transinfo_feature %g%Axx%a %n %W%2%n feature: %n%3 jabber_transinfo_end %g%Amvqqqqqq%a%G End of the infomations%n\n jabber_transport_list_begin %g%Alwqqqqqq%a%G Available agents on: %T%2%n jabber_transport_list_item %g%Axx%a %n %6 - %W%3%n (%5) jabber_transport_list_item_node %g%Axx%a %n %6 - %W%3%n node: %g%4%n (%5) jabber_transport_list_end %g%Amvqqqqqq%a%G End of the agents list%n\n jabber_userinfo_response2 %g%Alwqqqqqq%a%G vCard for:%n %T%2 jabber_userinfo_fullname %g%Axx%a %n Full Name: %T%2 jabber_userinfo_nickname %g%Axx%a %n Nickame: %T%2 jabber_userinfo_birthday %g%Axx%a %n Birthday: %T%2 jabber_userinfo_desc %g%Axx%a %n Description: %T%2 jabber_userinfo_email %g%Axx%a %n Email: %T%2 jabber_userinfo_organization %g%Axx%a %nOrganization: %T%2 jabber_userinfo_telephone %g%Axx%a %n Telephone: %T%2 jabber_userinfo_title %g%Axx%a %n Title: %T%2 jabber_userinfo_url %g%Axx%a %n Webpage: %T%2 jabber_userinfo_end %g%Amvqqqqqq\n jabber_form_command %g%Axx%a %nType %W/%3 %g%2 %W%4%n; resource_info_status %c%Ax%a %nResource: %W%1%n Status: %T%2 Prio: %g%3%n feed_message_header %g%Alwqqqqqq%a%W %1 %n(ID: %W%2%n) feed_message_body %g%Axx%a%n %|%1 feed_message_footer %g%Amvqqqqqq%a%G End of message...%n\n header %s%{?query_descr %{query_descr}}%{?query_ip (%wip%c/%w%{query_ip}%c)} %{irctopic}%{?!query %c(%wekg2%c/%w%{version}%c) (%w%{url}%c)} ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/contrib/themes/irc-irssi.theme����������������������������������������������0000664�0000000�0000000�00000006573�11751427534�0022045�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# (C) Copyright 2005-2006 Jakub 'darkjames' Zawadzki <darkjames at darkjames dot ath dot cx> # some formats (C) Michal 'GiM' Spadlinski statusbar_timestamp %H:%M:%S statusbar_act_important %W statusbar_act_important2us %M # IRSSI @ ekg2 irc_joined %> %C%2%n %B[%c%3%B]%n has joined %W%4\n irc_joined_you %> %C%2%n %B[%c%3%B]%n has joined %W%4\n irc_left %> %c%2%n [%c%3%n] has left %W%4 %n[%5]\n irc_left_you %> %c%2%n [%c%3%n] has left %W%4 %n[%5]\n irc_quit %> %c%2%n [%c%3%n] has quit [%4]\n irc_kicked_you %> %c%2%n was kicked from %W%5%n by %W%3%n [%6]\n irc_kicked %> %c%2%n was kicked from %W%5%n by %W%3%n [%6]\n IRC_TOPIC_CHANGE %> %T%2%n changed topic of %T%4%n to: %5\n IRC_TOPIC_UNSET %> %T%nTopic unset by %W%2%n on %T%4%n\n IRC_MODE_CHAN %> mode/%c%2%n [%3]\n IRC_MODE_CHAN_NEW %> mode/%c%4%n [%5] by %W%2\n IRC_MODE %> (%1) Mode change [%3] for user %W%2\n IRC_YOUNEWNICK %> You're now known as %W%3%n\n IRC_NEWNICK %> %c%2%n is now known as %C%4%n\n RPL_TOPIC %> Topic %c%2%n: %3\n IRC_RPL_TOPICBY %> Topic set by %W%2%n [%3] on %4 IRC_RPL_CANTSEND %> %2 Cannot send to channel\n irc_msg_sent_chan %B<%n%X%2%W%3%B>%n %6 irc_msg_f_chan_n %B<%w%X%2%3%B>%n %6 irc_msg_sent_n %B<%W%3%B>%n %6 irc_msg_f_some %b<%R%3%b>%n %6 irc_msg_f_chan_nh %B<%n%2%Y%3%B>%n %6 irc_ctcp_reply %> CTCP %W%2%n reply from %W%3%n: %5\n irc_ctcp_request %> %G%2%n %n[%g%3%n]%g requested CTCP %G%5%g from %G%4%n: _NIMP_ irc_ctcp_request_pub %> %G%2%n %n[%g%3%n]%g requested CTCP %G%5%g from %G%4%n: _NIMP_ irc_ctcp_action_y_pub %W* %2%n %4 irc_ctcp_action_pub %W* %2%n %5 irc_ctcp_action_y %W* %2%n %4 # to ma byc otwierane w nowym okienku... irc_ctcp_action %W* %2%n %5 irc_not_f_chan_n %n-%P%3%n:%p%5%n- %6 irc_not_f_server %g!%3%n %6 # to co tutaj jest ma byc w oknie aktualnym, nie otwieranie nowego okna !!! irc_not_f_some %n-%P%3%n(%p%4%n)- %6 irc_not_sent %n%K[%Rnotice%K(%r%5%K)]%n %6 irc_not_sent_n %n%K[%Rnotice%K(%r%5%K)]%n %6 irc_not_sent_chan %n%K[%Rnotice%K(%r%5%K)]%n %6 irc_not_sent_chanh %n%K[%Rnotice%K(%r%5%K)]%n %6 # not irssi ones but these are nice; not changed (c GiM) RPL_MOTDSTART %g,+=%G-----\n RPL_MOTD %g|| %n%2\n RPL_ENDOFMOTD %g`+=%G-----\n RPL_AWAY %G||%n away : %2 - %3\n RPL_WHOISUSER %G.+===%g-----\n%G||%n (%T%2%n) (%3@%4)\n%G||%n realname : %6\n RPL_WHOISCHANNELS %G||%n %|channels : %3\n RPL_WHOISSERVER %G||%n %|server : %3 (%4)\n RPL_WHOISOPERATOR %G||%n %|ircOp : %3\n RPL_WHOISIDLE %G||%n %|idle : %3 (signon: %4)\n RPL_ENDOFWHOIS %G`+===%g-----\n RPL_WHOWASUSER %G.+===%g-----\n%G||%n (%T%2%n) (%3@%4)\n%G||%n realname : %6\n # EKG2 only, not changed (c GiM) IRC_RPL_NEWONE %> (%1) 1:%2 2:%3 3:%4 4:%5\n IRC_ERR_CANNOTSENDTOCHAN %! %2: %1\n IRC_RPL_FIRSTSECOND %> (%1) %2 %3\n IRC_RPL_SECONDFIRST %> (%1) %3 %2\n IRC_RPL_JUSTONE %> (%1) %2\n IRC_ERR_FIRSTSECOND %! (%1) %2 %3\n IRC_ERR_SECONDFIRST %! (%1) %3 %2\n IRC_ERR_JUSTONE %! (%1) %2\n IRC_ERR_NEWONE %! (%1) 1:%2 2:%3 3:%4 4:%5\n IRC_PINGPONG %) (%1) ping/pong %c%2%n\n # NOT IMPLEMENTED, feel free to send patch irc_msg_sent 11 %P<%n%3/%5%P>%n %6 irc_msg_sent_chanh 22 %P<%W%{2@%+GCP}X%2%3%P>%n %6 irc_not_f_chan_nh 99 %B(%W%{2@%+GCP}X%2%3%B)%n %6 # NOT USED # irc_unknown_ctcp %> %Y%2%n sent unknown CTCP %3: (%4)\n # irc_msg_f_chan %B<%w%{2@%+gcp}X%2%3/%5%B>%n %6 # irc_msg_f_chanh %B<%W%{2@%+GCP}X%2%3/%5%B>%n %6 # irc_not_f_chan %B(%w%{2@%+gcp}X%2%3/%5%B)%n %6 # irc_not_f_chanh %B(%W%{2@%+GCP}X%2%3/%5%B)%n %6 # END �������������������������������������������������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/contrib/themes/peres.theme��������������������������������������������������0000664�0000000�0000000�00000000373�11751427534�0021247�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������message %K<%c<%C< %n%|%c%2%n%3\n chat %K<%c<%C< %n%|%c%2%n%3\n sent %K>%g>%G> %n%|%3\n conference %c<%C%1%c>%n %n%|%3\n -query_started_window log %K<%c<%c< %n%|%c%2%n%3\n sent_log %K>%g>%g> %n%|%c%2%n%3\n irc_joined %> %Y%2%n (%y%3%n) has joined %4\n ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/contrib/themes/wiechu.theme�������������������������������������������������0000664�0000000�0000000�00000012000�11751427534�0021403�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������# # (C) Copyright 2008 Wieslaw Ochminski <wiechu at wiechu dot com> # contacts_vertical_line_char # IRC RPL_MOTDSTART %c%Alwqqqqqq%a Message Of The Day %c%Aqqqqq%a\n RPL_MOTD %c%Axx %a%n%2\n RPL_ENDOFMOTD %c%Amvqqqqqq%a\n RPL_WHOISUSER %G%Alwqqqqqqqq%a\n%G%Axx%a%n (%T%2%n) (%3@%4)\n%G%Axx%a%n realname : %6\n RPL_WHOISCHANNELS %G%Axx%a%n %|channels : %3\n RPL_WHOISOPERATOR %G%Axx%a%n %|ircOp : %3\n RPL_WHOREPLY %G%Axx%a %c%3 %W%7 %n%8 %6 %4@%5 %W%9\n RPL_WHOISSERVER %G%Axx%a%n %|server : %3 (%4)\n RPL_WHOISIDLE %G%Axx%a%n %|idle : %3 (signon: %4)\n RPL_ENDOFWHOIS %G%Amvqqqqqqqq%a\n RPL_WHOWASUSER %G%Alwqqqqqqqq%a\n%G%Axx%a%n (%T%2%n) (%3@%4)\n%G%Axx%a%n realname : %6\n RPL_ENDOFWHOWAS %G%Amvqqqqqqqq%a\n RPL_AWAY %G%Axx%a%n away : %2 - %3\n RPL_BANLIST %G%Axx%a %n %5 - %W%2%n: ban %c%3\n RPL_CHLIST %G%Axx%a %n %5 %2\t%3\t%4\n RPL_LISTSTART %G%Alwqqqqqqqq%a\n RPL_EMPTYLIST %G%Axx%a %n Empty list \n RPL_ENDOFLIST %G%Amvqqqqqqqq%a %2%n\n RPL_EXCEPTLIST %G%Axx%a %n %5 - %W%2%n: except %c%3\n RPL_INVITELIST %G%Axx%a %n %5 - %W%2%n: invite %c%3\n RPL_LINKS %G%Axx%a %n %5 - %2 %3 %4\n RPL_LIST %G%Axx%a %n %5 %2\t%3\t%4\n RPL_STATS %G%Axx%a %3 %n %4 %5 %6 %7 %8\n RPL_STATSEND %G%Amvqqq%a%g%3%G%Aqqq%a %2\n RPL_STATS_EXT %G%Axx%a %3 %n %2 %4 %5 %6 %7 %8\n IRC_WHOERROR %G%Alwqqqqqqqq%a\n%G||%n %3 (%2)\n irc_awaylog_begin %G%Alwqqqqqqqq%a Awaylog for: (%1)\n irc_awaylog_end %G%Amvqqqqqqqq%a\n irc_awaylog_msg %G%Axx%a %n[%Y%2%n] <%W%4%n> %5\n irc_awaylog_msg_chan %G%Axx%a %n[%Y%2%n] [%G%3%n] <%W%4%n> %5\n # GG system %m%Alqqq%a %TSystem message %m%Aqqq qq q%a\n%m%Ax%a%n %|%3%n\n%|%m%Amqqqqq qqqq qqq qq q%a%n\n gg_user_info_version %c%Ax%x %nVersion: %T%1%n\n gg_user_info_firewalled %c%Ax%a %nFirewalled/NATed\n gg_user_info_ip %c%Ax%a %nAddress: %T%1%n\n gg_user_info_last_ip %c%Ax%a %nLast address: %T%1%n\n gg_user_info_mobile %c%Ax%a %nTelephone: %T%1%n\n gg_user_info_name %c%Ax%a %nName: %T%1 %2%n\n gg_user_info_not_in_contacts %c%Ax%a %nDoesn't have us in roster\n gg_user_info_voip %c%Ax%a %nVoIP-capable\n user_info_header %c%Alqq%a%n %T%1%n/%2 %c%Aqqq qq q%a%n\n user_info_status %c%Ax%a %nStatus: %T%1%n\n user_info_status_time %c%Ax%a %nCurrent status since: %T%1%n\n user_info_last_status %c%Ax%a %nLast status: %T%1%n\n user_info_last_seen %c%Ax%a %nLast seen: %T%1%n\n user_info_never_seen %c%Ax%a %nNever seen\n user_info_block %c%Ax%a %nBlocked\n user_info_auth_type %c%Ax%a %nSubscription type: %T%1%n\n user_info_footer %c%Amqqqqq qqqq qqq qq q%a%n\n user_info_gpg_key %c%Ax%a %nGPGKEY: %T%1%n (%2)%n user_info_groups %c%Ax%a %nGroups: %T%1%n\n user_info_name %c%Ax%a %nName: %T%1 %2%n\n user_info_nickname %c%Ax%a %nNickname: %T%1%n\n user_info_offline %c%Ax%a %nCan't see our status\n search_results_multi %7 %c%Ax%a%n %[-8]1 %c%Ax%a%n %[12]3 %c%Ax%a%n %[12]2 %c%Ax%a%n %[4]5 %c%Ax%a%n %[12]4\n metacontact_info_header %c%Alqq%a%n Metacontact %T%1%n %c%Aqqq qq q%a%n\n metacontact_info_status %c%Ax%a %nStatus: %T%1%n\n metacontact_info_footer %c%Amqqqqq qqqq qqq qq q%a%n\n # Jabber xmpp_feature_header %g%Alwqqqqqq%a%G XMPP features %n(%T%2%n%3%n) xmpp_feature %g%Axx%a %n %W%2%n can: %5 [%G%3%g,%4%n] xmpp_feature_sub %g%Axx%a %n %W%3%n: %5 [%G%4%n] xmpp_feature_sub_unknown %g%Axx%a %n %W%3%n: Unknown, report to devs [%G%4%n] xmpp_feature_unknown %g%Axx%a %n %W%2%n feature: %r%3 %n[%G%3%g,%4%n] xmpp_feature_footer %g%Amvqqqqqq%a %n Turn it off using: /session display_server_features 0\n jabber_transinfo_begin %g%Alwqqqqqq%a%G Information about: %T%2%n jabber_transinfo_begin_node %g%Alwqqqqqq%a%G Information about: %T%2%n (%3) jabber_transinfo_identify %g%Axx%a %G --== %g%3 %G==--%n jabber_transinfo_comm_not %g%Axx%a %n %W%2%n can: %n%3 (%4) jabber_transinfo_comm_ser %g%Axx%a %n %W%2%n can: %n%3 %2 (%4) jabber_transinfo_comm_use %g%Axx%a %n %W%2%n can: %n%3 $uid (%4) # jabber_transinfo_feature %g%Axx%a %n %W%2%n feature: %n%3 jabber_transinfo_end %g%Amvqqqqqq%a%G End of the infomations%n\n jabber_transport_list_begin %g%Alwqqqqqq%a%G Available agents on: %T%2%n jabber_transport_list_item %g%Axx%a %n %6 - %W%3%n (%5) jabber_transport_list_item_node %g%Axx%a %n %6 - %W%3%n node: %g%4%n (%5) jabber_transport_list_end %g%Amvqqqqqq%a%G End of the agents list%n\n jabber_userinfo_response2 %g%Alwqqqqqq%a%G vCard for:%n %T%2 jabber_userinfo_fullname %g%Axx%a %n Full Name: %T%2 jabber_userinfo_nickname %g%Axx%a %n Nickame: %T%2 jabber_userinfo_birthday %g%Axx%a %n Birthday: %T%2 jabber_userinfo_desc %g%Axx%a %n Description: %T%2 jabber_userinfo_email %g%Axx%a %n Email: %T%2 jabber_userinfo_organization %g%Axx%a %nOrganization: %T%2 jabber_userinfo_telephone %g%Axx%a %n Telephone: %T%2 jabber_userinfo_title %g%Axx%a %n Title: %T%2 jabber_userinfo_url %g%Axx%a %n Webpage: %T%2 jabber_userinfo_end %g%Amvqqqqqq\n jabber_form_command %g%Axx%a %nType %W/%3 %g%2 %W%4%n; resource_info_status %c%Ax%a %nResource: %W%1%n Status: %T%2 Prio: %g%3%n feed_message_header %g%Alwqqqqqq%a%W %1 %n(ID: %W%2%n) feed_message_body %g%Axx%a%n %|%1 feed_message_footer %g%Amvqqqqqq%a%G End of message...%n\n ekg2-0.4~pre+20120506.1/contrib/vcekg���������������������������������������������������������������0000775�0000000�0000000�00000001751�11751427534�0016646�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/bash EKG_BIN="" # First try to find ekg2 in current directory [ -x 'ekg2' ] && EKG_BIN="./ekg2" [[ -z "${EKG_BIN}" && -x 'ekg/ekg2' ]] && EKG_BIN="ekg/ekg2" [[ -z "${EKG_BIN}" && -x '../ekg/ekg2' ]] && EKG_BIN="../ekg/ekg2" # Then try script directory SCDIR="$(dirname "$0")" if [[ -z "${EKG_BIN}" && -n "${SCDIR}" && "${SCDIR}" != '.' ]]; then cd "$(dirname $0)" [[ -z "${EKG_BIN}" && -x 'ekg2' ]] && EKG_BIN="ekg2" [[ -z "${EKG_BIN}" && -x 'ekg/ekg2' ]] && EKG_BIN="ekg/ekg2" [[ -z "${EKG_BIN}" && -x '../ekg/ekg2' ]] && EKG_BIN="../ekg/ekg2" fi # Finally, try ${PATH} [ -z "${EKG_BIN}" ] && EKG_BIN="$(which ekg2 2>/dev/null)" if [ -z "${EKG_BIN}" ]; then echo 'Unable to find any usable ekg2 binary.' exit 127 fi valgrind \ -v \ --tool=callgrind \ --log-file="$HOME"/.ekg2/valgrind."$(date +%Y%m%d-%H%M)" \ --callgrind-out-file="$HOME"/.ekg2/callgrind.out."$(date +%Y%m%d-%H%M)" \ --demangle=no \ --num-callers=20 \ "${EKG_BIN}" "$@" exit $? �����������������������ekg2-0.4~pre+20120506.1/contrib/vekg����������������������������������������������������������������0000775�0000000�0000000�00000002104�11751427534�0016474�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/bin/bash EKG_BIN="" # First try to find ekg2 in current directory [ -x 'ekg2' ] && EKG_BIN="./ekg2" [[ -z "${EKG_BIN}" && -x 'ekg/ekg2' ]] && EKG_BIN="ekg/ekg2" [[ -z "${EKG_BIN}" && -x '../ekg/ekg2' ]] && EKG_BIN="../ekg/ekg2" # Then try script directory SCDIR="$(dirname "$0")" if [[ -z "${EKG_BIN}" && -n "${SCDIR}" && "${SCDIR}" != '.' ]]; then cd "$(dirname $0)" [[ -z "${EKG_BIN}" && -x 'ekg2' ]] && EKG_BIN="ekg2" [[ -z "${EKG_BIN}" && -x 'ekg/ekg2' ]] && EKG_BIN="ekg/ekg2" [[ -z "${EKG_BIN}" && -x '../ekg/ekg2' ]] && EKG_BIN="../ekg/ekg2" fi # Finally, try ${PATH} [ -z "${EKG_BIN}" ] && EKG_BIN="$(which ekg2 2>/dev/null)" if [ -z "${EKG_BIN}" ]; then echo 'Unable to find any usable ekg2 binary.' exit 127 fi # --trace-malloc=yes \ valgrind \ -v \ --tool=memcheck \ --log-file="$HOME"/.ekg2/valgrind."$(date +%Y%m%d-%H%M)" \ --leak-check=yes \ --leak-resolution=high \ --error-limit=no \ --demangle=yes \ --num-callers=20 \ --track-fds=yes \ --trace-children=yes \ --show-reachable=yes \ "${EKG_BIN}" "$@" exit $? ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/contrib/xmsg-extras/��������������������������������������������������������0000775�0000000�0000000�00000000000�11751427534�0020077�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/contrib/xmsg-extras/README-xmsghandler��������������������������������������0000664�0000000�0000000�00000005411�11751427534�0023272�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������XMSG handler.d package (C) 2006 Michał Górny <peres@peres.int.pl> ------------------------ This package contains handler.d-based XMSG send_cmd script and some example handler.d scripts for it. To use it, put it in some directory, adjust paths in it (xmsg incoming message directory path has been moved to SimpleXMSG.pm) and set it as xmsg-session's send_cmd. What exactly does xmsghandler.pl do? The first, it checks if given ARGV arguments are right (both are not null and given file exists), then it tries to handle incoming message with all handler.d scripts. If all of them fail, it also sends failure message to user. What is that XMSG? That's simple external message delivery system for IM's, independent of any servers, with simplest queuing ever. Currently only IM supporting it is EKG2. See README within plugins/xmsg/ in its source tree for more information. What's rules of creating handler.d scripts? 1) Main handler invokes all scripts with same arguments it is invoked with, i.e. first one contains UID, and the second one path to file, containing message; 2) Every script MUST determine if it can handle that kind of message (and it SHOULD do it depending on UID), 3) If script can handle message, it SHOULD do it and then MUST return value of 0 (even if further processing fails), 4) If script can't handle that kind of message, it MUST return value other than 0, 5) Handlers are invoked until one of them returns positive response (i.e. 0), 6) If none of the handlers return positive response, main handler script replies to the sender with failure message, 7) If handler can't handle message, it MUST NOT unlink the file given on second arg. If it can, it also SHOULD NOT do this — main handler unlinks that file itself, 8) The scripts SHOULD NOT write to STDOUT/STDERR [user won't see this (-;], all messages to user MUST be sent using xmsg incoming messages directory, 9) If handler is written in Perl, it MAY use SimpleXMSG.pm module, providing easy-to-use reply subroutine, which as only argument takes message text. CAUTION: reply uses directly $ARGV[0], so it's incompatible with non-handler scripts. You need to explicitly define, what symbol do you need (qw/reply/), and don't forget about adding directory with this module to search path. With package, there is smbxmsghandler.pl example included, which does convert XMSG messages with uids xmsg:smbmsg-XXX (where XXX stands for computer name) to WinPopup messages (and sends them). For another example, you can download xmsgrss package, where handler.d interface is used for easy manipulation of database. Hope that's all I wanted to write. I'm very sorry, if you can't understand anything of it. I'm a programmer, not docwriter (and mi primary language is Polish). I can only wish you good luck. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/contrib/xmsg-extras/README-xmsgrss������������������������������������������0000664�0000000�0000000�00000004365�11751427534�0022473�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������XMSG-RSS (C) 2006 Michał Górny <peres@peres.int.pl> ------------------------ XMSG-RSS is XMSG-plugin-based RSS headline reader. It does consist of two parts. The first one, xmsgrss.pl, is main script, which reads feeds database, refreshes all feeds, then sends new messages through XMSG and rewrites db. The second one is xmsghandler.d script, which allows user to easily update database from their XMSG-capable IM. The feature list is not long now. XMSG-RSS saves in its database last-headline ID to not send the same headlines two times. But if some way headline order changes or our 'last headline' disappears, XMSG-RSS will resend all headlines. We also keep Last-Modified header in db, if available. Then we resend it as If-Modified-Since, so that we won't download unmodified feeds again and again. XMSG-RSS uses also Syslog, to which it is writing information about processed feeds, in the form 'name: action taken'. How to use it? First, you'll need some XMSG-capable IM, that is EKG2 (<http://www.ekg2.org>). Then you should read XMSG plugin README, and prepare yourself XMSG environment. Then, you should put xmsgrss.pl and db.example in some directory, and rename the second one to something less-exampling. XMSG incoming message directory should be also adjusted inside SimpleXMSG.pm ($msgdir variable). To run feedupdate, you must call xmsgrss.pl with path to database file. You may put this into cron.d, so your feeds will be refreshed automagically. You can edit the database with any text editor, its' file format is very simple. One record is one line, if line begins with #, it's a comment. Field seperator is ##, in the beginning of database, xmsg writes field use as a comment. You shouldn't put your own comments, because xmsg will remove them in next run (-;. You can also use XMSG-handler based interface. To get started, you should get yourself xmsghandler.d package, and read its' README. Then you can put or symlink xmsgrssxhandler.pl into your xmsghandler.d directory, and adjust paths inside. Don't forget to set xmsg-session's send_cmd. Then just write 'help' (without quotes) to xmsg:rss UID. That's all for now. I'm very sorry if that README is too difficult to understand for you, but I'm a programmer, not helpwriter. I can only wish you good luck. ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/contrib/xmsg-extras/SimpleXMSG.pm�������������������������������������������0000664�0000000�0000000�00000001642�11751427534�0022370�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl package SimpleXMSG; use strict; use warnings; require Exporter; our @ISA = qw/Exporter/; our @EXPORT_OK = qw/replyxmsg sendxmsg/; use File::Temp qw/tempfile/; # XMSG incoming message dir our $msgdir = '/var/xmsg'; # name separator our $namesep = '.'; # Send message in reply, designed for xmsghandler.d # Only arg is reply text, we get rcpt from ARGV[0] sub replyxmsg { my ($fh, $fn) = tempfile("$msgdir/.SimpleXMSG-$ARGV[0]$namesep" . 'XXXXXX'); my $nfn = $fn; $nfn =~ s/^($msgdir\/)\.SimpleXMSG-/$1/s; print ($fh shift); close ($fh); rename ($fn, $nfn); } # Send XMSG message # first arg is rcpt, second is message text sub sendxmsg { my ($from, $text) = (shift, shift); my ($fh, $fn) = tempfile("$msgdir/.SimpleXMSG-$from$namesep" . 'XXXXXX'); my $nfn = $fn; $nfn =~ s/^($msgdir\/)\.SimpleXMSG-/$1/s; print ($fh $text); close ($fh); rename ($fn, $nfn); } # XXX: some object-oriented methods ����������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/contrib/xmsg-extras/smbxmsghandler.pl���������������������������������������0000775�0000000�0000000�00000000453�11751427534�0023457�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl use strict; use warnings; use FindBin; use lib "$FindBin::RealBin"; use SimpleXMSG qw/replyxmsg/; if ($ARGV[0] =~ /^smbmsg-(.*)/) { `cat "$ARGV[1]" | smbclient -M "$1"`; replyxmsg('WinPopup request failed - probably second side is not connected') if ($?); exit 0; } exit 1; ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/contrib/xmsg-extras/xmsghandler.pl������������������������������������������0000775�0000000�0000000�00000000655�11751427534�0022761�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl use strict; use warnings; use FindBin; use lib "$FindBin::RealBin"; use SimpleXMSG qw/replyxmsg/; my $handlerddir = '~/xmsghandler.d'; exit 1 if (! ($ARGV[0] && $ARGV[1])); exit 2 if (! -f $ARGV[1]); my $found = 0; foreach (<$handlerddir/*>) { `$_ $ARGV[0] $ARGV[1]`; $found = 1, last if (($?>>8) == 0); } replyxmsg('Message delivery failed - unknown recipient') if (!$found); unlink $ARGV[1]; exit 0; �����������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/contrib/xmsg-extras/xmsgrss.pl����������������������������������������������0000775�0000000�0000000�00000006371�11751427534�0022154�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl use strict; use warnings; use FindBin qw/$Bin/; use lib "$FindBin::RealBin"; use SimpleXMSG qw/sendxmsg/; use LWP::UserAgent; use File::Temp qw/tempfile/; use Sys::Syslog; use XML::Feed; use Encode; use YAML qw/DumpFile LoadFile/; use List::Util qw/first/; our $myemail = 'me@someserver.pl'; # set to '', if you don't want to send From: header our $myua = 'xmsgrss/0.1 (I hate feedburner!)'; # attached to LWP UA, if set; this one disables WordPress->FeedBurner redirects our $dbfile = "$FindBin::RealBin/db"; our $dtformat = '%x %X'; $dbfile = $ARGV[0] if ($ARGV[0]); sub Loguj { my $msg = shift; syslog('info', $msg); } $\ = "\n"; my $ua = LWP::UserAgent->new; $ua->timeout(30); $ua->env_proxy; $ua->default_header('Accept' => 'application/rss+xml, application/rdf+xml, application/rss+rdf+xml, text/xml;q=0.7'); $ua->default_header('Accept-Language' => 'pl, en;q=0.5'); $ua->default_header('Accept-Charset' => 'utf-8, *;q=0.5'); $ua->default_header('From' => $myemail) if ($myemail); $ua->agent($ua->_agent() . " $myua") if ($myua); openlog('rssxmsg', 'nofatal,pid', 'user'); die('Database not writable') if (! -w $dbfile); my @db = LoadFile($dbfile); foreach (@db) { my $arr = $_; my $r; if ($$arr{Last_Modified}) { $r = $ua->get($$arr{URL}, 'If-Modified-Since' => $$arr{Last_Modified}); Loguj("$$arr{Name}: Skipping because of Last-Modified"), next if (($r->code == 304) || ($r->header('Last-Modified') eq $$arr{Last_Modified})); } else { $r = $ua->get($$arr{URL}); } Loguj("$$arr{Name}: Download failed"), next if (!$r->is_success); $$arr{Last_Modified} = $r->header('Last-Modified'); my $feed = XML::Feed->parse(\$r->content); if ($feed) { my $sawfirst = 0; my $newid = ''; my ($msgtext, $xtext, $content, @wholemsg); foreach my $hl ($feed->entries) { next if (first { $_ eq $hl->id } @{$$arr{Seen}}); push(@{$$arr{Seen}}, $hl->id); if (!$sawfirst) { $msgtext = sprintf("[ %s ]\n< %s >", $feed->title, $feed->link); $sawfirst = 1; } $xtext = $hl->author if ($hl->author); $xtext .= ($xtext ? ' / ' : '') . $hl->issued->strftime($dtformat) if ($hl->issued); $xtext .= ($xtext ? ' / ' : '') . $hl->modified->strftime($dtformat) if (!$hl->issued && $hl->modified); $xtext .= ($xtext ? ' / ' : '') . $hl->category if ($hl->category); $xtext = sprintf("\n( %s )", $xtext) if ($xtext); $content = ($hl->summary && $hl->summary->body ? $hl->summary->body : $hl->content->body); if ($feed->as_xml =~ /^<\?xml[^>]*?encoding=["']utf-?8["'].*?<feed[^>]*?xmlns=['"]http:\/\/www\.w3\.org\/2005\/Atom['"]/si) { # broke utf-8 handling Encode::_utf8_off($content); } push(@wholemsg, sprintf("\n\n\n[ %s ]\n< %s >%s\n\n%s", $hl->title, $hl->link, $xtext, $content)); $xtext = ''; } if ($sawfirst) { $msgtext .= join('', reverse(@wholemsg)); if ($feed->as_xml =~ /^<\?xml[^>]*?encoding=["']utf-?8["'].*?<feed[^>]*?xmlns=['"]http:\/\/www\.w3\.org\/2005\/Atom['"]/si) { # broken utf-8 handling workaround $msgtext = encode('iso-8859-1', $msgtext); } sendxmsg("rss-$$arr{Name}", $msgtext); Loguj("$$arr{Name}: New headlines sent"); } else { Loguj("$$arr{Name}: No new headlines"); } } else { Loguj("$$arr{Name}: Feed parsing failed"); } } DumpFile($dbfile, @db); closelog(); �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/contrib/xmsg-extras/xmsgrssxhandler.pl��������������������������������������0000775�0000000�0000000�00000005553�11751427534�0023703�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl # to be called from xmsghandler.pl # symlink into handlers.d/ use strict; use warnings; use FindBin; use lib "$FindBin::RealBin"; use SimpleXMSG qw/replyxmsg/; use File::Temp qw/tempfile/; use YAML qw/DumpFile LoadFile Dump/; use Digest::MD5 qw/md5_hex/; our $helpmsg = "XMSG-RSS command interface\n" . "Supported commands:\n" . "\thelp\t\tthis message\n" . "\trefresh\t\tcalls xmsgrss.pl to refresh all feeds\n" . "\tlist\t\tlist feeds in db\n" . "\tadd %n %u\tadd new feed to db, %n = name, %u = url\n" . "\trm [%n]\t\tremove feed, [%n] = name, if writing to xmsg:rss-feedname, MUST be NULL"; our $dbfile = "$FindBin::RealBin/db"; our $xmsgrss = "$FindBin::RealBin/xmsgrss.pl"; our $f; sub DoRemove { my $what = shift; eval { my @db = LoadFile($dbfile); my $r; foreach (@db) { if ($$_{Delete_Confirmation} eq $what) { my @newdb; $$_{Delete_Confirmation} eq $what or push(@newdb, $_) foreach (@db); DumpFile($dbfile, @newdb); die('Feed removed successfully'); } elsif ($$_{Name} eq $what) { $$_{Delete_Confirmation} = md5_hex(rand()); DumpFile($dbfile, @db); die("Please confirm feed removal by typing 'rm $$_{Delete_Confirmation}'"); } } die('feed not found'); }; if ($@) { local $_ = $@; s/ at.*$//; chomp; if (/(confirm|successfully)/) { replyxmsg($_); } else { replyxmsg("Unable to remove feed: $_"); } } } my $cmd; if ($ARGV[0] =~ /^rss-(.*)$/) { { local $/; open($f, "<$ARGV[1]"); $cmd = <$f>; close($f); } chomp $cmd; if ($cmd eq 'rm') { DoRemove($1); } else { replyxmsg('This UID is used only for sending RSS notifications, please write to xmsg:rss instead'); } exit 0; } elsif ($ARGV[0] eq 'rss') { { local ($/, $f); open($f, "<$ARGV[1]"); $cmd = <$f>; close($f); } chomp $cmd; if ($cmd eq 'list') { my ($outmsg, @arr); eval { my @arr = LoadFile($dbfile); foreach (@arr) { my %h = %$_; $outmsg .= "$h{Name} => $h{URL}\n"; } if ($outmsg) { chomp $outmsg; replyxmsg($outmsg); } else { replyxmsg('Database is empty'); } }; replyxmsg('Unable to open database') if ($@); } elsif ($cmd =~ /^add[[:space:]]+(.*?)[[:space:]]+(https?:\/\/.*)$/) { eval { my @tmparr = ({Name => $1, URL => $2}); open($f, ">>$dbfile") or die('unable to open database'); print($f Dump(@tmparr)) or die('db write error'); close($f); }; if ($@) { local $_ = $@; s/ at.*$//; chomp; replyxmsg("Unable to add feed: $_"); } else { replyxmsg('Feed added successfully.'); } } elsif ($cmd =~ /^rm[[:space:]]+(.*)$/) { DoRemove($1); } elsif ($cmd eq 'refresh') { `$xmsgrss`; if ($?>>8) { replyxmsg('Execution failed: ' . ($?>>8)); } else { replyxmsg('RSS refresh finished'); } } elsif ($cmd eq 'help') { replyxmsg($helpmsg); } else { replyxmsg('Syntax error, try: help'); } exit 0; } exit 1; �����������������������������������������������������������������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/contrib/xmsg-extras/youtube.pl����������������������������������������������0000775�0000000�0000000�00000005061�11751427534�0022135�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������#!/usr/bin/perl # # XMSG-based YouTube downloader # based on some script found here: http://bashscripts.org/viewtopic.php?t=210 # (C) 2007 Michał Górny # use warnings; use strict; use LWP::Simple; use FindBin; use lib "$FindBin::RealBin"; use SimpleXMSG qw/replyxmsg/; exit(1) if ($ARGV[0] ne 'youtube'); my $path; my $dldir = "$ENV{HOME}/youtube-dl"; my $wgetopts = '-c --limit-rate=32k'; my $player = 'mplayer -vo dxr3 -ao alsa -fs -framedrop'; my $preplayer = 'mpc pause; sleep 2'; my $postplayer = 'sleep 2; mpc play'; my $autoplay = 1; my $convertera = 'ffmpeg -i'; my $converterb = ''; my $converterc = '-sameq -vcodec mpeg2video -acodec copy'; my $converterext = 'mpg'; my $avgkibps = 28; my ($len, $avgtime); { my ($f); local ($\); open($f, $ARGV[1]); $path = <$f>; close($f); } LWP::Simple::get($path) =~ /<title>YouTube - (.*?)<\/title>.*player2.swf\?(video_id=[0-9a-z_]+.+?)\"/is or replyxmsg("Can't find download URL!"), exit(0); my ($title, $id) = ($1, $2); { my @res = LWP::Simple::head("http://youtube.com/get_video.php?$id"); if (@res > 1) { $len = ($res[1] / 1024); $avgtime = ($len / $avgkibps); if ($len > 1024) { $len = sprintf("%.2f MiB", $len / 1024); } else { $len = sprintf("%.2f KiB", $len); } if ($avgtime > 60) { $avgtime = sprintf("%d:%02d min", ($avgtime / 60), ($avgtime % 60)); } else { $avgtime = sprintf("%d s", $avgtime); } } } replyxmsg("Download of '$title'" . ($len ? " [$len] (~$avgtime) " : "") . "( http://youtube.com/get_video.php?$id ) started."); mkdir($dldir) if (! -d $dldir); my $fn = $title; ($fn =~ s/\//_/g); if (!$convertera || ! -f "$dldir/$fn.$converterext") { `wget $wgetopts -O "$dldir/$fn.flv" "http://youtube.com/get_video.php?$id"`; if ($convertera) { replyxmsg('Starting video converter...'); `$convertera "$dldir/$fn.flv" $converterb "$dldir/$fn.$converterext" $converterc`; if (($?>>8) == 0) { unlink("$dldir/$fn.flv"); $fn .= ".$converterext"; } else { replyxmsg('Converter somewhat failed.'); $fn .= '.flv'; } } else { $fn .= '.flv'; } } else { $fn .= ".$converterext"; } if (($?>>8) == 0) { if ($autoplay) { `$preplayer`; replyxmsg("Starting playback of '$title'..."); `$player "$dldir/$fn" &> /dev/null`; if (($?>>8) == 0) { replyxmsg('Playback finished.'); } else { replyxmsg("Playback somewhat failed. Please try by hand:\n\t$player \"$dldir/$fn\""); } `$postplayer`; } else { replyxmsg("Download of '$title' finished. To see it:\n\t$player \"$dldir/$fn\""); } } else { replyxmsg("Download of '$title' somewhat failed."); } exit(0); �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/docs/�����������������������������������������������������������������������0000775�0000000�0000000�00000000000�11751427534�0015105�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/docs/IDEAS-2.0��������������������������������������������������������������0000664�0000000�0000000�00000017514�11751427534�0016162�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// krtki opis mojej wizji ekg-2.0 // 20030416 wojtekka jeli dobrniesz do koca tekstu, bybym bardzo wdziczny za komentarze i sugestie. nie chc robi niczego wbrew reszcie wiata. wszystko wyrzuci do pluginw. ekg samo w sobie nie powinno umie si z niczym czy, ani nic wywietla. ma zawiera funkcje obsugi pluginw, obsugi komend, obsugi zmiennych i kilku kawakw kodu, ktre mog by dzielone midzy pluginami (np. formatowanie tekstu, konferencje, przypisane klawisze). nie wszystkie pluginy musz by adowane dynamicznie. kilka na pewno bdzie domylnie linkowanych statycznie -- chodzi o moliwo atwego pozbycia si zalenoci gwnej binarki od wielu bibliotek i zachowanie jednolitego API. do gowy przychodzi mi kilka klas pluginw: 1) pluginy interfejsu uytkownika -- czyli to, co do tej pory robi ui-readline i ui-ncurses. dojdzie do tego plugin obsugujcy rurki, sockety lokalne i inne sposoby sterowania klientem. 2) pluginy protokow -- priorytetem ekg bdzie zawsze ,,wzorcowa'' obsuga Gadu-Gadu, ale nie mona sta w miejscu. wielu ludziom brakuje porzdnego klienta Jabbera pod konsol. co wicej, istniejcy kod konferencji, po lekkim rozszerzeniu moe stanowi podstawy pod obsug irca. niestety ciko bdzie od razu zaplanowa API pozwalajce na napisanie obsugi dowolnego protokou na ziemi, wic kady nowy plugin tak czy inaczej bdzie wymaga drobnych zmian w ekg. ale bd mogy si rozwija niezalenie. 3) pluginy szyfrujce -- pki co, jest tylko SIM, ale nic nie stoi na przeszkodzie, eby ekg obsugiwao prostsze sposoby szyfrowania, np. symetryczne z hasem. 4) pluginy skryptw -- mamy pythona. znajomy poradzi, eby umoliwi pisanie skryptw rwnie w innych jzykach. nie powinno by trudne, zwaszcza, e obsuga skryptw ogranicza si do adowania, usuwania, wykonywania polece i wywoywania funkcji. API takiego pluginu mona ograniczy do kilku funkcji. poza tym, za pomoc skryptw powinno by moliwe tworzenie kadej klasy pluginw. 5) pluginy dwiku -- jest tylko oss, a to nie wszystkim pasuje. dodawanie dziesitek #ifdefw do obsugi rnych systemw jest bez sensu. poza tym, jeli zrobi plugin, ktry zamiast z mikrofonu, czyta z socketa, mamy proste radio, ktre chocia jakoci nie grzeszy, zajmuje bardzo mae pasmo. do tego monaby te doliczy pluginy kodujce dwik, eby inne pluginy mogy poprosi od razu o strumie GSM czy MP3. 6) pluginy historii -- wida, e nie wszystkim odpowiada sposb logowania w ekg. tutaj wystarczy w zasadzie jedna funkcja dopisujca do historii okrelone zdarzenie, ale moliwo odczytu te by si przydaa, eby mc w ekg przeglda histori (ach, pobone yczenia!). pki co, s ju pomysy na 4 pluginy: legacy-ekg, all-new-kadu, sql i xml. gupio byoby linkowa ekg z sqlem i expatem. 6) pluginy oglnego przeznaczenia -- tutaj pasowaby chociaby ioctld, ktry dodaje dwie nowe komendy, wic ciko podpi go pod interfejs uytkownika. pasowaby te kady skrypt, ktry nie peni roli jakiego plugina. kady plugin dodawaby swoje komendy, zmienne i zdarzenia. mgby wywoywa zdarzenia dla innych pluginw. ekg podczas adowania wywoa funkcj typu register_plugin(), ktra bdzie miaa za zadanie zarejestrowa wszystkie udostpniane komendy i zmienne. w przypadku konfliktu zmiennych i komend, mona je poprzedzi prefiksem okrelajcym plugin. jeli na przykad mamy zaadowan obsug GG i Jabbera, zmienna ,,gg:password'' okrelaaby haso GG, ,,jabber:password'' okrelaaby haso Jabbera. jeli uytkownik nie poda o jaki plugin chodzi, a np. aktualne okno to sesja GG, brany pod uwag byby plugin ,,gg''. jeli okno Jabbera, plugin ,,jabber''. podobnie z komendami. jeli kto w oknie Jabbera chciaby zarejestrowa konto GG, wystarczyoby ,,/gg:register''. pluginy musz posiada rwnie informacje o kolizjach z innymi, eby przy adowaniu ,,ncurses'' usun ,,readline'' i na odwrt, bo oba korzystaj z terminala. obsuga okien powinna trafi do ekg. ui ma wywietla to, co kae mu pokaza ekg i informowa o wcinitych klawiszach funkcyjnych (nie chodzi tylko o Fx tylko o Alt-x, Ctrl-x itp.) dziki temu przy zmianie ui, okna zostan zachowanie (to ma by efekt uboczny, a nie cel sam w sobie.) pozostaje kwestia interakcji pluginw z ekg i midzy sob, oznaczania zdarze, kompatybilnoci API i takichtam bzdur. najprawdopodobniej bdzie co w rodzaju gtk-owych sygnaw. plugin sobie zarejestruje obsug danych sygnaw, przez co odpadn dziesitki strcmp(), jak to ma miejsce teraz, przy jednej funkcji callbackowej na cay plugin. niestety bdzie to wymagao porzdnego zastanowienia si nad tym, jak zrobi to efektywnie. setki strcmp() przy kadym pakiecie przychodzcym z sieci i przy wywoaniu sekundowego timera to przesada. niestety nie studiuj informatyki, wic pewnie na pocztku bdzie to sporym problemem. w kadym razie optymalizacj przekierowywania sygnaw mona zostawi na pniej, kiedy bdzie ju co optymalizowa. jeli chodzi o API, ekg od chwili ustandaryzowania pierwszej wersji API pluginw bdzie oznaczao plugin jakimtam identyfikatorem wersji. bdzie trzyma wszystkie stare wersje struktur i funkcji, eby stare pluginy mogy ich uy. trzeba bdzie te wprowadzi zmienne typu lista, eby mc np. poda list rurek kontrolnych (np. ,,pipe:/tmp/rurka'', ,,tcp:12345'' i ,,socket:/var/run/ekg''), z ktrych ekg ma przyjmowa polecenia, interfejsw audio na wypadek zajtoci (np. ,,/dev/dsp'', ,,hw:0,2'' czy ,,tcp:8001'') no i wreszcie naszych ukochanych serwerw (przykadu nie ma. wybaczycie?) co do wielosesyjnoci i wieloprotokoowoci, to podobnie jak w BitchX czy irssi, jedno okno mogoby mie przypisanych kilka sesji, ktre zmienianoby klawiszem Ctrl-X na przykad. zmieniaby si pasek stanu midzy: (17:25) (gg:535333) (win/1) (17:25) (jabber:wojtekka@jabber.org) (win/1) (17:25) (irc:elluin@poznan.irc.pl) (win/1) oczywicie powinna by moliwo przypisywania danym sesjom jakich aliasw, eby nie mie caego paska stanu zajtego przez id sesji. pozostaje kwestia userlisty. robi osobn na kady protok i sesj? osobn na kady protok, ale sesje dziel? bo albo moemy chcie wpisa sobie jednego uytkownika jako gg:123456, irc:ktotam, jid:ktotam@gdzietam.pl i raz poda imi, nazwisko, numer telefonu itd, albo trzyma wszystko oddzielnie dla kadego protokou, bo np. podczas rozmw na GG nie chcemy zamieca sobie listy znajomymi, ktrych numery byy kiedytam wpisane, ale i tak rozmawiamy tylko na ircu. wypadaoby te w kocu oddzieli libgadu od ekg, skoro ekg ma obsugiwa inne protokoy. nie do, e uly to autorom innych klientw GG, wymusi wikszy porzdek w API, wersjach i binarnej kompatybilnoci. z okazji pluginw, dobrze byoby te si przyjrze takim wynalazkom jak automake i libtool. rozmiar pakietu wzronie, ale nie bdzie problemw z obsug platform innych ni te, na ktrych pracuj autorzy (pomyle, e jeszcze 2 lata uwaaem autoconfa za zo wcielone. pod postaci software'u.) // 2003-04-17 12:53 moe jednak zrobi bloga? (; w kadym razie dla testw wydzieliem libgadu ze rde ekg, przerobiem do automake i libtoola. faktycznie, pisania mniej, ale rozmiar wszystkich narzdzi, ktre autogen.sh pakuje do katalogu mnie przerasta. tarball z Makefile.am, configure.in i autogen.sh zajmuje 80kB, a po wygenerowaniu wszystkiego 320kB. troch to chore. jeli ekg miaoby mie dla kadego plugina tyle mieci, to ja chyba podzikuj. innym wyjciem byoby rozprowadzanie tarballi bez tego wszystkiego i wymaganie od ludzi posiadania penego rodowiska: gcc, binutils, make, autoconf, automake, libtool. ludzie, ktrzy maj wszystko nie musieliby ciga niepotrzebnie kilka razy wikszego tarballa. co najwyej monaby tworzy osobny ekg-current-foobar.tar.gz, ktry miaby wszystko, ale go nie archiwizowa. w sumie to moliwe, od kiedy na dev.null.pl stoi PLD. ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/docs/Perl-API-en.txt��������������������������������������������������������0000664�0000000�0000000�00000040373�11751427534�0017566�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Description of functions available for Perl script. Updated on December 2010 (C) Copyright 2005 Jakub Zawadzki <darkjames[at]darkjames.ath.cx> 2010 Sawomir Nizio <poczta-sn[at]gazeta.pl> 2010 Wiesaw Ochmiski <wiechu[at]wiechu.com> Ekg2 exit() (void) - quits the program get_ekg2_dir() (char*) - returns directory config_dir debug(char* str) (void) - prints text in debug window; does not append a newline character at the end echo(char* str) (void) - prints text in a window print(int dest, char* str) (void) - prints text in a window with specified number format_add(char* str, char* value) (void) - adds format format_string(char* str) (char*) - ? fstring2ascii(char* str, void* attr_) (char*) - ? handler_bind(char* query_name, char* handler) (void) - sets handler for query_name event command_bind(char* cmd, char* handler) (void) - sets handler for cmd command command_bind_ext(char* cmd, char* params, char* poss, char* handler) (void) - sets handler for cmd command, extended version timer_bind(int freq, char* handler) (Ekg2::Timer) - sets timer (periodic event) with specified frequency session_add(char* uid) (Ekg2::Session) - adds session to the list session_find(char* uid) (Ekg2::Session) - returns session for specified UID script_find(char* name) (Ekg2::Script) - returns script with specified name plugin_find(char* name) (Ekg2::Plugin) - returns plugin with specified name (without extension) plugin_register(char* name, int type, void* formatinit) (void) - registers a plugin plugin_load(char* name) (int) - loads a plugin window_findid(int id) (Ekg2::Window) - returns window with specified number window_find(char* name) (Ekg2::Window) - returns window with specified name window_new(char* target, int new_id) (Ekg2::Window) - opens a new window window_current() (Ekg2::Window) - returns current window variables() (list of Ekg2::Variable) - returns variable list plugins() (list of Ekg2::Plugin) - returns plugin list timers() (list of Ekg2::Timer) - returns timer list commands() (list of Ekg2::Command) - returns command list watches() (list of Ekg2::Watch) - returns watch list sessions() (list of Ekg2::Session) - returns session list windows() (list of Ekg2::Window) - returns window list command(char* what) (int) - executes a command (for example "/beep") command_exec(Ekg2::Window window, Ekg2::Session session, char* what) (int) - executes a command variable_find(char* name) (Ekg2::Variable) - returns variable with specified name variable_add(char* name, char* value) (Ekg2::Variable) - sets a variable variable_add_ext(char* name, char* value, char* handler) (Ekg2::Variable) - sets a variable variables_free() (void) - ? watch_add(int fd, int type, char* handler, void* data) (void) - adds a watch watch_remove(int fd, int type) (void) - removes a watch EKG_MSGCLASS_SENT() (int) - returns EKG_MSGCLASS_SENT value EKG_MSGCLASS_SENT_CHAT() (int) - returns EKG_MSGCLASS_SENT_CHAT value EKG_NO_THEMEBIT() (int) - returns EKG_NO_THEMEBIT value WATCH_READ_LINE() (int) - returns WATCH_READ_LINE value WATCH_READ() (int) - returns WATCH_READ value WATCH_WRITE() (int) - returns WATCH_WRITE value PLUGIN_UI() (int) - returns PLUGIN_UI value PLUGIN_PROTOCOL() (int) - returns PLUGIN_PROTOCOL value Ekg2::Command {name} (char*) - name {param} (char*) - for example "!U ? p", when {name} = "add" {poss} (char*) - possibilities (for example "-f --find", when {name} = "add") execute(Ekg2::Command comm, char* param) (int) - executes a command remove(Ekg2::Command comm) (void) - removes a command Ekg2::Plugin {name} (char*) - name {prio} (int) - priority unload(Ekg2::Plugin plugin) (int) - unloads a plugin Ekg2::Script {name} (char*) - name {path} (char*) - path Ekg2::Session {connected} (int) - if session is connected {uid} (char*) - name {status} (char*) - status (avail, ...) {alias} (char*) - alias userlist(Ekg2::Session session) (Ekg2::Userlist) - session userlist set(Ekg2::Session session) (void) - sets current session connected_set(Ekg2::Session session, int val) (void) - sets information about current session: if it is connected or not param_add(Ekg2::Session session, char *name) (int) - add sesssion parameter param_set(Ekg2::Session session, char* name, char* value) (void) - sets session parameter disconnect(Ekg2::Session session) (int) - does /disconnect connect(Ekg2::Session session) (int) - does /connect Ekg2::Session::param {key} (char*) {value} (char*) session_help(Ekg2::Session session, char* name) (void) - shows help for session variable help(Ekg2::Session::Param param, Ekg2::Session session) (void) - shows help set(Ekg2::Session::Param param, Ekg2::Session session, char* value) (int) - sets session parameter Ekg2::Timer {name} (char*) - name {freq} (int) - frequency (seconds) {freq_ms} (int) - frequency destroy(Ekg2::Timer timer) (void) - removes a timer Ekg2::User {nickname} (char*) - nickname {uid} (char*) - user ID {status} (char*) - user status set_status(Ekg2::User u, char* status) (int: 0 lub 1) - sets user's status Ekg2::Userlist {} (list_t) - just a pointer find(Ekg2::Userlist userlist, char* uid) (Ekg2::User) - returns user with specified ID users(Ekg2::Userlist userlist) (list of Ekg2::User) - returns userlist add(Ekg2::Userlist userlist, char* uid, char* nickname) (Ekg2::User) - adds user to the list remove(Ekg2::Userlist userlist, Ekg2::User u) (int) - removes user from the list Ekg2::Variable {name} (char*) - name {value} (char*/int) - value help(Ekg2::Variable var) (void) - shows help for variable remove(Ekg2::Variable var) (void) - removes set(Ekg2::Variable var, char* value) (int) - assigns a value Ekg2::Watch {fd} (int) {type} (int) {removed} (int) {timeout} (int) {plugin} (Ekg2::Plugin) {started} (int) Ekg2::Window {target} (char*) - name {id} (int) - number {session} (Ekg2::Session) - session next(Ekg2::Window window) (Ekg2::Window) - returns next window prev(Ekg2::Window window) (Ekg2::Window) - returns previous window userlist(Ekg2::Window window) (Ekg2::Userlist) - userlist for specified window switch(Ekg2::Window window) (void) - switches to specified window kill(Ekg2::Window window) (void) - kills specified window print_format(Ekg2::Window window, char* format, ...) (void) - prints text in a specified window print(Ekg2::Window window, char* line) (void) - prints text in a specified window ######### Ekg2::Irc servers() (list of Ekg2::Irc::Server) - returns server list session2server(Ekg2::Session s) (Ekg2::Irc::Server) - returns blessed IRC session from "normal" one Ekg2::Irc::Channel {name} (char*) - channel name, without irc: {mode} (char*) - channel mode {topic} (char*) - topic {topicby} (char*) - topic author {window} (Ekg2::Window) - window {name_} (char*) - (this may be removed in the future) channel name, with irc: part(Ekg2::Irc::Channel chan, char* reason) (void) - sends message to leave channel Ekg2::Irc::Channel::User {mode} (int) {sign} (char*) - for example @ {channel} (Ekg2::Irc::Channel) - channel Ekg2::Irc::Server {nick} (char*) - nick {server} (char*) - server name {ip} (char*) - if resolved: IP, if not: 0.0.0.0 session(Ekg2::Session s) (Ekg2::Session) - session raw(Ekg2::Session s, char* str) (void) - sends a message to server quit(Ekg2::Session s, char* quitreason) (void) - sends a QUIT message newnick(Ekg2::Session s, char* newnick) (void) - sends a message to change user's nick to newnick setmode(Ekg2::Session s, char* mode) (void) - sends a MODE message oper(Ekg2::Session s, char* nick, char* password) (void) - identifies as operator die(Ekg2::Session s, char *reason) (void) - sends a DIE message channels(Ekg2::Session s) (list of Ekg2::Irc::Channel) - returns list with channels that we are on people(Ekg2::Session s) (list of Ekg2::Irc::User) - returns list with users that are on the same channels as we are Ekg2::Irc::User {nick} (char*) {realname} (char*) {hostname} (char*) {ident} (char*) {nick_} (char*) channels (Ekg2::Irc::User user) (list of Ekg2::Irc::Channel::User) - list of channels on which is the user ------- example ------ # $sess_name - session name, for example xmpp:nick@serwer.with.five.years.uptime.org or irc:cool_network # $uid - user ID, for example xmpp:nick2@anything.com/Abc # find the session, which for example relates to an event supported by the handler # (you can use Ekg2::session_current if you want to get current session): my $ses = Ekg2::session_find($sess_name); # get nick list: my $ul = Ekg2::Session::userlist($ses); # find user on the list: my $user = Ekg2::Userlist::find($ul, $uid); # if not found (undef), set as $uid my $who = defined $user ? $user->{nickname} : $uid; # print it to screen Ekg2::echo("test ::: $who"); ------ end ------ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/docs/Perl-API-pl.txt��������������������������������������������������������0000664�0000000�0000000�00000040753�11751427534�0017601�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Opis funkcji dostpnych z poziomu skryptu perlowego. Aktualizacja: grudzie 2010 (C) Copyright 2005 Jakub Zawadzki <darkjames[at]darkjames.ath.cx> 2010 Sawomir Nizio <poczta-sn[at]gazeta.pl> 2010 Wiesaw Ochmiski <wiechu[at]wiechu.com> Ekg2 exit() (void) - koczy program get_ekg2_dir() (char*) - zwraca katalog config_dir debug(char* str) (void) - pisze tekst w oknie debugowania; nie wstawia znaku nowej linii na kocu echo(char* str) (void) - pisze tekst w oknie print(int dest, char* str) (void) - pisze w oknie o zadanym numerze format_add(char* str, char* value) (void) - dodaje format format_string(char* str) (char*) - zwraca sformatowany cig znakw fstring2ascii(char* str, void* attr_) (char*) - zamienia sformatowany tekst na "formatk" handler_bind(char* query_name, char* handler) (void) - ustawia handlera dla zdarzenia query_name command_bind(char* cmd, char* handler) (void) - ustawia handlera dla polecenia cmd command_bind_ext(char* cmd, char* params, char* poss, char* handler) (void) - ustawia handlera dla polecenia cmd, wersja rozszerzona pozwala poda typ parametrw i opcje, ktre bd wykorzystane midzy innymi przy dopenianiu tabulatorem timer_bind(int freq, char* handler) (Ekg2::Timer) - ustawia zegarek (okresowe zdarzenie) o podanej czstotliwoci session_add(char* uid) (Ekg2::Session) - dodaje now sesj do listy session_find(char* uid) (Ekg2::Session) - zwraca sesj dla zadanego UID script_find(char* name) (Ekg2::Script) - zwraca skrypt o zadanej nazwie (bez rozszerzenia) plugin_find(char* name) (Ekg2::Plugin) - zwraca plugin o zadanej nazwie (bez rozszerzenia) plugin_register(char* name, int type, void* formatinit) (void) - rejestruje plugin plugin_load(char* name) (int) - aduje plugin window_findid(int id) (Ekg2::Window) - zwraca okno o zadanym numerze window_find(char* name) (Ekg2::Window) - zwraca okno o zadanej nazwie window_new(char* target, int new_id) (Ekg2::Window) - otwiera nowe okno window_current() (Ekg2::Window) - zwraca biece okno variables() (lista Ekg2::Variable) - zwraca list zmiennych plugins() (lista Ekg2::Plugin) - zwrace list pluginw timers() (lista Ekg2::Timer) - zwraca list zegarkw commands() (lista Ekg2::Command) - zwraca list komend watches() (lista Ekg2::Watch) - zwraca list watchy sessions() (lista Ekg2::Session) - zwraca list sesji windows() (lista Ekg2::Window) - zwraca list okien command(char* what) (int) - wykonuje polecenie (np. ,,/beep'') command_exec(Ekg2::Window window, Ekg2::Session session, char* what) (int) - wykonuje polecenie variable_find(char* name) (Ekg2::Variable) - zwraca zmienn o podanej nazwie variable_add(char* name, char* value) (Ekg2::Variable) - dodaje zmienn variable_add_ext(char* name, char* value, char* handler) (Ekg2::Variable) - dodaje zmienn i handler do obsugi zmian jej wartoci variables_free() (void) - usuwa list zmiennych watch_add(int fd, int type, char* handler, void* data) (void) - dodaje watcha watch_remove(int fd, int type) (void) - usuwa watcha EKG_MSGCLASS_SENT() (int) - zwraca warto EKG_MSGCLASS_SENT EKG_MSGCLASS_SENT_CHAT() (int) - zwraca warto EKG_MSGCLASS_SENT_CHAT EKG_NO_THEMEBIT() (int) - zwraca warto EKG_NO_THEMEBIT WATCH_READ_LINE() (int) - zwraca warto WATCH_READ_LINE WATCH_READ() (int) - zwraca warto WATCH_READ WATCH_WRITE() (int) - zwraca warto WATCH_WRITE PLUGIN_UI() (int) - zwraca warto PLUGIN_UI PLUGIN_PROTOCOL() (int) - zwraca warto PLUGIN_PROTOCOL Ekg2::Command {name} (char*) - nazwa {param} (char*) - np. "!U ? p", gdy {name} = "add" {poss} (char*) - moliwoci (np. "-f --find", gdy {name} = "add") execute(Ekg2::Command comm, char* param) (int) - wykonuje polecenie remove(Ekg2::Command comm) (void) - usuwa polecenie Ekg2::Plugin {name} (char*) - nazwa {prio} (int) - priorytet unload(Ekg2::Plugin plugin) (int) - wyadowuje plugin Ekg2::Script {name} (char*) - nazwa {path} (char*) - cieka Ekg2::Session {connected} (int) - czy poczona {uid} (char*) - nazwa {status} (char*) - status (avail, ...) {alias} (char*) - alias userlist(Ekg2::Session session) (Ekg2::Userlist) - lista uytkownikw dla sesji set(Ekg2::Session session) (void) - zmienia biec sesj connected_set(Ekg2::Session session, int val) (void) - ustawia informacj: sesja jest poczona czy nie param_add(Ekg2::Session session, char *name) (int) - dodaje zmienn sesyjn param_set(Ekg2::Session session, char* name, char* value) (void) - ustawia parametr sesji disconnect(Ekg2::Session session) (int) - robi /disconnect connect(Ekg2::Session session) (int) - robi /connect Ekg2::Session::param {key} (char*) {value} (char*) session_help(Ekg2::Session session, char* name) (void) - pokazuje pomoc zmiennej sesji help(Ekg2::Session::Param param, Ekg2::Session session) (void) - pokazuje pomoc set(Ekg2::Session::Param param, Ekg2::Session session, char* value) (int) - ustawia parametr sesji Ekg2::Timer {name} (char*) - nazwa {freq} (int) - czstotliwo (sekundy) {freq_ms} (int) - czstotliwo destroy(Ekg2::Timer timer) (void) - usuwa zegarek Ekg2::User {nickname} (char*) - nazwa uytkownika {uid} (char*) - uid {status} (char*) - status uytkownika set_status(Ekg2::User u, char* status) (int: 0 lub 1) - zmienia status uytkownikowi Ekg2::Userlist {} (list_t) - just a pointer find(Ekg2::Userlist userlist, char* uid) (Ekg2::User) - zwraca uytkownika o zadanym ID users(Ekg2::Userlist userlist) (lista Ekg2::User) - zwraca list uytkownikw add(Ekg2::Userlist userlist, char* uid, char* nickname) (Ekg2::User) - dodaje uytkownika do listy remove(Ekg2::Userlist userlist, Ekg2::User u) (int) - usuwa uytkownika z listy Ekg2::Variable {name} (char*) - nazwa {value} (char*/int) - wartosc help(Ekg2::Variable var) (void) - wywietla pomoc dla zmiennej remove(Ekg2::Variable var) (void) - usuwa set(Ekg2::Variable var, char* value) (int) - ustawia warto Ekg2::Watch {fd} (int) {type} (int) {removed} (int) {timeout} (int) {plugin} (Ekg2::Plugin) {started} (int) Ekg2::Window {target} (char*) - nazwa {id} (int) - numer {session} (Ekg2::Session) - sesja next(Ekg2::Window window) (Ekg2::Window) - zwraca nastpne okno prev(Ekg2::Window window) (Ekg2::Window) - zwraca poprzednie okno userlist(Ekg2::Window window) (Ekg2::Userlist) - lista uytkownikw dla okna switch(Ekg2::Window window) (void) - przecza na zadane okno kill(Ekg2::Window window) (void) - zabija okno print_format(Ekg2::Window window, char* format, ...) (void) - pisze w zadanym oknie print(Ekg2::Window window, char* line) (void) - pisze w zadanym oknie ######### Ekg2::Irc servers() (lista Ekg2::Irc::Server) - zwraca liste serwerw session2server(Ekg2::Session s) (Ekg2::Irc::Server) - zwraca zblessowana sesje ircowa ze ,,zwyklej'' Ekg2::Irc::Channel {name} (char*) - nazwa kanau, bez irc: {mode} (char*) - tryb kanau {topic} (char*) - topic {topicby} (char*) - autor topica {window} (Ekg2::Window) - okno {name_} (char*) - (moe by usunite w przyszoci) nazwa kanau, z irc: part(Ekg2::Irc::Channel chan, char* reason) (void) - wysya wiadomo wyjcia z kanau Ekg2::Irc::Channel::User {mode} (int) {sign} (char*) - np. @ {channel} (Ekg2::Irc::Channel) - kana Ekg2::Irc::Server {nick} (char*) - nick {server} (char*) - nazwa serwera {ip} (char*) - jesli zresolvowano: adres ip, jesli nie: 0.0.0.0 session(Ekg2::Session s) (Ekg2::Session) - sesja raw(Ekg2::Session s, char* str) (void) - wysya wiadomo do serwera quit(Ekg2::Session s, char* quitreason) (void) - wysya wiadomo QUIT newnick(Ekg2::Session s, char* newnick) (void) - wysya wiadomo zmiany nicka na newnick setmode(Ekg2::Session s, char* mode) (void) - wysya wiadomo MODE oper(Ekg2::Session s, char* nick, char* password) (void) - identifikuje si jako operator die(Ekg2::Session s, char *reason) (void) - wysya wiadomo DIE channels(Ekg2::Session s) (lista Ekg2::Irc::Channel) - zwraca list kanaw, na ktrych jestemy people(Ekg2::Session s) (lista Ekg2::Irc::User) - zwraca list uytkownikw na kanaach, na ktrych jestemy Ekg2::Irc::User {nick} (char*) {realname} (char*) {hostname} (char*) {ident} (char*) {nick_} (char*) channels (Ekg2::Irc::User user) (lista Ekg2::Irc::Channel::User) - lista kanalow, na ktrych jest uytkownik ------- przykad ------ # $sess_name - nazwa sesji, np. xmpp:nick@serwer.ktory.ma.uptime.piec.lat.org albo np. irc:fajna_sie # $uid - ID uytkownika, np. xmpp:nick2@reszta.com/Abc # znajd sesj, ktrej na przykad dotyczy zdarzenie obsugiwane przez handler # (albo Ekg2::session_current jeli interesuje nas bieca sesja): my $ses = Ekg2::session_find($sess_name); # we list nickw: my $ul = Ekg2::Session::userlist($ses); # znajd uytkownika na licie: my $user = Ekg2::Userlist::find($ul, $uid); # jeli nie znalazo (undef), przypisz $uid my $who = defined $user ? $user->{nickname} : $uid; # i wypisz na ekran Ekg2::echo("test ::: $who"); ------ koniec ------ ���������������������ekg2-0.4~pre+20120506.1/docs/README�����������������������������������������������������������������0000664�0000000�0000000�00000035737�11751427534�0016004�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ Eksperymentalny Klient Gadu-Gadu (C) Copyright 2001-2003 Autorzy (pena lista poniej) LICENCJA Program jest udostpniony na zasadach licencji GPL v2, ktrej tre zaczono w pliku src/COPYING. Niektre pliki mog by objte inn licencj zgodn z GPL v2. Fakt ten jest odnotowany na pocztku pliku. Wyjtkiem od licencji GPL v2 jest moliwo kompilacji, konsolidacji i uywania programu z bibliotek OpenSSL autorstwa Projektu OpenSSL (The OpenSSL Project) dostpn pod adresem http://www.openssl.org/ INSTALACJA Rozpakowa poleceniem ,,tar zxvf ekg2-XXX.tar.gz'' (gdzie XXX to wersja programu lub data wykonania snapshotu), wej do utworzonego katalogu. Jeli mamy uprawnienia administratora na danej maszynie, wywoujemy ,,./configure'', potem ,,make'' i z prawami roota ,,make install''. Jeli chcemy zainstalowa program w katalogu domowym, do polecenia configure dodajemy parametry ,,--prefix=$HOME/ekg2 --mandir=$HOME/ekg2/share/man''. Po zainstalowaniu w ten sposb, program gotowy do uruchomienia bdzie znajdowa si w katalogu ekg2/bin w katalogu domowym. Proste? Proste. Po pierwszym uruchomieniu EKG2 powie, jak go skonfigurowa. Program korzyst domylnie z pluginu wyjciowego ncurses. Interfejs readline w chwili obecnej nie jest gotowy do uycia. Do instalacji konieczne jest zainstalowanie wszelkich narzdzi wymaganych do tego: kompilator, preprocesor, linker, pliki nagwkowe, biblioteki itd. Jeeli ./configure pokae jakie bdy skontaktuj si ze swoim administratorem. ekg2 powinien dziaa na wikszoci systemw uniksowych, jak Linux, *BSD, SunOS, IRIX itp. lecz czasami przy dodawaniu nowych funkcji nie sposb sprawdzi ich zachowania na wszystkich popularnych systemach. W takim wypadku przydatne s informacje o bdach z dokadnym wskazaniem systemu i architektury. UYCIE Jest na tyle intuicyjne, e nie powinno sprawi problemw (wszyscy betatesterzy poradzili sobie bez jakiejkolwiek dokumentacji). Interfejs jest wzorowany na irssi. Dopenianie tabem jest dostpne w wikszoci komend. Komendy mona wywoywa skrtami, o ile s one jednoznaczne. Wysyanie wiadomoci komend ,,msg'', otwarcie okna rozmowy komend ,,query''. Informacje o rozmwcach ,,find'' w oknie rozmowy. Szukanie t sam komend, ale z rnymi parametrami. ,,help'' Twoim przyjacielem. Jeli dana komenda ma rne opcje, pomaga ,,help <komenda>''. By wysa kilkulinijkow wiadomo w interfejsie ncurses, wcinij Ctrl-Enter. W readline zamiast treci wpisz ,,\'' (backslash) i zakocz lini z sam kropk (szczegy poniej, w rodziale ,,KLAWIATURA''). Program mona skonfigurowa pod wieloma wzgldami, a wszystkie moliwe ustawienia, ktre zmienia si poleceniem ,,set'', s opisane w pliku vars.txt. Pomoc dotyczc poszczeglnych ustawie mona uzyska take poprzez polecenie ,,help set <zmienna>''. Jeli dana komenda przyjmuje ,,--parametr'', mona uy rwnie skrtu ,,-p'', gdy nie powoduje to niejednoznacznoci. Uwaga! Brana pod uwag jest zwykle pierwsza litera, wic jeli wpiszesz ,,list --gone'', ekg potraktuje to jako ,,list --get''. Komendy mona wysya take przez potok lub internet. Wymagana jest do tego wtyczka remote control. adowanie /plugin +rc ustawienie potoku: /set rc:remote_control pipe:/home/user/named_pipe KLAWIATURA Jeli nie masz dowiadczenia w obsugiwaniu programw z emacsow filozofi obsugi klawiatury, oto lista obsugiwanych klawiszy: Up, Down przegldanie historii polece Left, Right poruszanie si po aktualnej linii Ctrl-A, Home id na pocztek linii Ctrl-B pogrubiona czcionka [3] Ctrl-D, Delete usu znak pod kursorem Ctrl-H, Backspace usu znak przed kursorem Ctrl-I, Tab dopenianie Ctrl-K usuwa tekst od kursora do koca linii Ctrl-L czyszczenie/odwieanie ekranu Ctrl-M, Enter zatwierdzenie linii Ctrl-Q odblokowanie terminala Ctrl-S zablokowanie terminala Ctrl-T pochya czcionka [3] Ctrl-V pozwala wpisa dowolny znak [2] Ctrl-U usunicie aktualnej linii Ctrl-W, Alt-Backspace usunicie sowa przed kursorem Ctrl-Y wklejenie ostatnio usunitego bloku Ctrl-Z przeniesienie programu w to Ctrl-_ podkrelona czcionka [3] Alt-B sowo do tyu Alt-D usunicie sowa za kursorem Alt-F sowo do przodu Alt-cyfra przeczenie do podanego okna F1 pomoc F2 krtka lista dostpnych i zajtych F12 lub Alt-` przeczenie do okna debugowania Lista ta obejmuje klawisze obsugiwane przed interfejs readline i ncurses, i nie zawiera kombinacji specyficznych dla tego pierwszego. Interfejs readline obsuguje rwnie inne kombinacje klawiszy. Dokadna lista znajduje si w stronie manuala ,,readline'' w rozdziale ,,DEFAULT KEY BINDINGS''. Dodatkowo: Ctrl-D zamyka rozmow i anuluje wprowadzanie wiadomoci wielolinijkowej Interfejs ncurses obsuguje kilka dodatkowych kombinacji: Page Up, Page Down przewijanie ekranu Ctrl-F, Ctrl-G j.w. Alt-A przejd do pierwszego aktywnego okna Alt-N utwrz nowe okno Alt-K zamknij aktualne okno Alt-G ignoruj aktualnego rozmwc Alt-Q do Alt-P przecza do okna 11 do 20 Ctrl-Fn przecza do podanego okna (konsola FreeBSD) Ctrl-Enter przejcie do trybu wielolinijkowego Ctrl-P poprzednie okno Ctrl-N kolejne okno F3 wcza lub wycza list kontaktw [1] F4 kolejna grupa w licie kontaktw Po wejciu do trybu wielolinijkowego poruszamy si za pomoc kursorw i zatwierdzamy ponownym wciniciem Ctrl-Enter. By anulowa, wciskamy Esc i czekamy chwil. Jeli kombinacja ta nie jest obsugiwana przez terminal, mona uywa Alt-Enter lub wcisn Esc i zaraz po nim Enter. Dodatkowo, okrelonym kombinacjom klawiszy mona przypisa rne komendy za pomoc polecenia ,,bind''. Ze wzgldu na niestandardowe zachowanie niektrych typw terminali, mog wystpi problemy z kombinacj Alt-Shift-O lub Alt-O przy wczonym Caps Locku. [1] Klawisz F3 zmienia warto zmiennej ,,contacts''. Jeli warto tej zmiennej bya rwna 0, wcinicie klawisza zmienia jej warto na 2. Jeli bya rwna 1, kolejne wcinicia F3 bd zmieniay warto z 1 na 0 i z 0 na 1. [2] Po wciniciu Ctrl-V naley wcisn klawisz, ktry chcemy wklei. Dziki temu moliwe jest wpisanie znakw typu Escape, Ctrl-L czy Ctrl-U. [3] W miejscu wcinicia klawisza pojawi si znak oznaczajcy kod klawisza w negatywie. PLIK KONFIGURACYJNY Kolejno adowania plikw konfiguracyjnych jest nastpujca: 1) /etc/ekg.conf (lub z innego katalogu wskazanego przez opcj --sysconfdir przekazan skryptowi ./configure), 2) ~/.ekg/config lub ~/.ekg/<profil>/config, 3) /etc/ekg-override.conf Dziki temu administrator moe wymusi pewne opcje na klientach uytkownikw, jak na przykad ,,server'' czy ,,proxy''. Globalne pliki konfiguracyjne mona ignorowa przez uruchomienie klienta z opcj ,,-N''. GDZIE SZUKA POMOCY Dobra rada numer jeden: zanim zaczniesz narzeka, e czego nie ma, przeczytaj plik TODO. Dobra rada numer dwa: plik ULOTKA mwi, co znajduje si w ktrym pliku dokumentacji. Dobra rada numer trzy: zanim zaczniesz narzeka, e czego nie ma, poszukaj w pliku ChangeLog. Dobra rada numer cztery: jeli jeste nowym uytkownikiem, nie pytaj si, czy co da si zrobi, albo e przydaoby si. Program jest rozwijany od ponad roku, wielu ludzi korzysta z niego na co dzie, wic wikszo ,,bajerw'', o ktrych moesz pomyle, na pewno jest ju w programie. WYSYANIE SMSW ekg korzysta z zewntrznego programu do wysyania smsw. Nie ma najmniejszego sensu dublowania tej funkcji, skoro i tak wikszo ma ju zainstalowane odpowiednie skrypty/programy/cokolwiek. Wystarczy poda ciek do takiego programu w zmiennej ,,sms_send_app''. Powinien przyjmowa numer telefonu za pierwszy parametr i wiadomo za drugi. Ten ze strony http://ceti.pl/~miki/ spenia podane wymagania. SYNTEZA MOWY ekg potrafi korzysta z zewntrznej aplikacji do syntezy mowy, by odczytywa wszystko, co trafia na ekran. Wystarczy ustawi zmienn ,,speech_app'' na nazw programu czytajcego tekst z stdin. Jej ustawienie spowoduje rwnie zmian wygldu programu, by wywietlane komunikaty stay si atwiejsze do wymwienia. Przykadowe ustawienia, gdy korzystamy z programu ,,powiedz'', to: set speech_app powiedz set make_window 0 set display_sent 0 set display_ack 3 Program ,,powiedz'' mona pobra z http://cvs.pld.org.pl/SOURCES/powiedz_0.2.tgz ZNANE PROBLEMY Jeli nie moesz wpisywa polskich liter w interfejsie readline, dopisz do pliku /etc/inputrc lub ~/.inputrc nastpujce linie: set meta-flag on set convert-meta off set output-meta on ROZPOZNAWANIE PCI Krtka wersja: Jeli ekg le rozpoznaje pe, wpisz imi do listy kontaktw. W wikszoci przypadkw pomoe. No tak, nie masz pojcia, jak to zrobi? ,,list pseudonim -f imi''. Pomogo? wietnie. Nie pomogo? Czytaj dalej. Duga wersja: Jedn z najbardziej kontrowersyjnych cech programu jest automatyczne rozpoznawanie pci na podstawie ostatniej litery imienia lub gdy jest ono nieznane, pseudonimu. Gdy ostatni liter jest ,,a'', ekg uznaje, e dana osoba jest kobiet. Na przykad, jeli Twj znajomy ma pseudonim ,,Kuba'', wpisz do listy kontaktw imi ,,Jakub''. Problemy mog wystpi z rzadko spotykanymi imionami typu Barnaba czy Mercedes. W takim wypadku naley do imienia dopisa (chociaby po ukoniku lub w nawiasie) liter ,,a'' dla kobiet lub inn ni ,,a'' dla mczyzn. ZGASZANIE BDW Jeli zauwaysz jaki bd, sprawd najnowsz wersj. Wikszo bdw jest poprawiana w cigu jednego lub dwch dni od chwili pojawienia si. Przy zgaszaniu bdu, zaznacz, w ktrej wersji wystpuje. Nie pisz o sprawach, ktre s ju wymienione w pliku TODO, bo doskonale wiemy, e co trzeba poprawi. Zaznacz, co to za system, jaka dystrybucja, jaka wersja. Jeli bd jest powtarzalny i zwizany z sieci, przejd do okna debug i przylij to, co zostaje tam wywietlone przed samym wystpieniem bdu (zwykle ~20 linijek wystarczy). Moesz te skorzysta z ukrytego polecenia ,,_debug_dump'', ktre zapisze ostatnie linie z debug do pliku lub te przed uruchomieniem ekg wpisa nazw pliku, do ktrego przekierowany bdzie debug, do zmiennej systemowej ,,EKG_DEBUG''. Jeli program powoduje naruszenie ochrony pamici i powstaje plik ,,core'', postpuj zgodnie z instrukcjami pokazanymi na ekranie -- uruchom ,,gdb ekg core'', przylij to, co si pojawi. Potem wydaj polecenie ,,bt'' i jego wynik rwnie zacz do listu. Jeli bd moe mie co wsplnego z sieci, wylij utworzony plik ,,debug''. Jeli program si zawiesi, nie reaguje na nic i zajmuje 100% czasu procesora, w innym oknie terminala wydaj polecenie ,,strace -p <pid>'', gdzie <pid> to numer procesu ekg (uzyskasz go poleceniem ,,ps x'') i wylij ostatnie 20 linii. Informacj o bdzie naley przesya na list ekg2-users (nie trzeba si na ni zapisywa, szczegy niej), poniewa w ten sposb dostan j (prawie) wszyscy autorzy kodu. Moliwe te, e ktry z uytkownikw natkn si na to samo i wie, jak sobie z tym poradzi. RDA Snapshoty kodu s dostpne pod adresem http://ekg2.org/download.php Jeli nie wystpi adne trzsienie ziemi, brak prdu ani barykady na drogach, powinny by robione co 24 godziny, wieczorem. Poza snapshotami, co jaki czas bd umieszczane na serwerze kolejne wersje klienta. Ze wzgldu na organizacj prac nad programem, w praktyce nie bd si one rni znacznie od snapshotw. Przed wydaniem kadej wersji wstrzymane bdzie dodawanie nowych opcji, by skupi si na usuwaniu bdw. Poza tym, uatwi to prac niektrym osobom zajmujcym si tworzeniem paczek dla dystrybucji -- zamiast uaktualniania ich do nowych snapshotw, bd miay moliwo pakowania ,,stabilnych'' wersji. Cz kodu jest adnie udokumentowana, cz nie. Komentarze czasami s bardzo gupie, ale jeli si do trzeciej rano siedzi nad dziwnym segfaultem, ciko si pohamowa. Jeli napiszesz jakiegokolwiek klienta, frontend czy co takiego, daj zna -- odnonik do projektu zostanie umieszczony na stronie ekg. LISTA DYSKUSYJNA Istnieje lista dyskusyjna dla uytkownikw ekg o adresie ekg-users@lists.ziew.org. Aby si zapisa, naley wej na stron o adresie: http://lists.ziew.org/mailman/listinfo/ekg2-users oraz postpowa wedug instrukcji tam zawartych. AUTORZY Wymienione osoby miay mniejszy lub wikszy wpyw na rozwj biblioteki i klienta. Niektrzy pisali kod, pomagali analizowa protok, testowali na rnych systemach, inni podsyali patche i bugreporty. Jeli kto zosta pominity, niech da zna. W kadym razie za to, jak wyglda ekg, odpowiedzialni s (w porzdku alfabetycznym): Marek Antoniak <kfazi@kfazi.polnet.trzepak.pl> Wojciech Bojdo <wojboj@htcon.pl> Tomasz Chiliski <chilek@chilan.com> Marcin Chojnowski <martii@obgyn.edu.pl> Piotr Domagalski <szalik@szalik.net> Micha Dorociski <zwierzak@venus.ci.uw.edu.pl> Tomasz Dudzisz <eileen@ds1.agh.edu.pl> Piotr Figiel <feeg@psychodelic.org> Rafa Florek <raf@regionet.regionet.pl> Artur Gawryszczak <gawrysz@camk.edu.pl> Stanisaw Gurgacz <fig@pd21.tarnobrzeg.sdi.tpnet.pl> Darek Jackowski <ascent@home.pl> Rafa Janiczek <jojo@dexter.zst.bytom.pl> Dawid Jarosz <dawjar@poczta.onet.pl> Tomasz Jarzynka <tomee@cpi.pl> Kuba Jermak <kooba@irc.pl> Jarosaw Kamper <jack@jack.eu.org> Asia Kaniewska <kj_asia@wp.pl> Wojtek Kaniewski <wojtekka@irc.pl> Maciej Korze <maciekk@linux.sky.pl> Pawe Kot <pkot@linuxnews.pl> Marek Kozina <klith@irc.pl> Adam Kruszewski <phantom@linux.bydg.org> Piotr Kupisiewicz <deli@rzepaknet.us> Adam Ludwikowski <adam.ludwikowski@wp.pl> Jakub Martyski <jakub@ceto.pl> Pawe Maziarz <drg@go2.pl> Marcin Mikua <meecash@meecash.prv.pl> Arkadiusz Mikiewicz <misiek@pld.org.pl> Jacek Osiecki <joshua@ceti.pl> Robert Osowiecki <magic.robson@rassun.art.pl> Adam Osuchowski <adwol@polsl.gliwice.pl> Maurycy Pawowski <maurycy@kocham.cie.gov.pl> Artur Pietruk <arturp@plukwa.net> Jacek Pospychaa <tri10o@bsod.org> Pawe Pruszkowski <arim@botrm.org> Jacek Rembisz <jr178783@zodiac.mimuw.edu.pl> Rafal Roszak <rmrmg@wp.pl> Krzysztof Skadzie <coxoc@coxoc.org> Rafa Skoczylas <nils@secprog.org> Adrian Smarzewski <adrians@aska.com.pl> Walerian Sokoowski <ws171913@yahoo.com> Piotr Stolc <socrtp@sedez.iq.pl> Tomasz Torcz <zdzichu@irc.pl> Leszek Urbaski <tygrys@moo.pl> Robert J. Wony <speedy@ziew.org> Krzysztof Wjtowicz <misiolek@misiolki.prv.pl> Piotr Wysocki <wysek@linux.bydg.org> $Id$ ���������������������������������ekg2-0.4~pre+20120506.1/docs/SCons-notes.txt��������������������������������������������������������0000664�0000000�0000000�00000007256�11751427534�0020033�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������FEW NOTES ABOUT USING SCONS AS EKG2 BUILD SYSTEM 1) SCons is _alternative_ build system, it is still under development and at least currently isn't meant to replace autotools. 2) SCons uses globs instead of hardcoded lists to determine files to compile, so if you're going to place some junk.c in one of source directories (either ekg/ or plugins/*/), then please take note SCons will try to compile it as standalone object and then link it with resulting binary. If you really want to place some *.c files, which you aren't going to include, then please surround it with #if 0/#endif. If you're going to include it, then please name it *.inc instead. 3) 'Configure' part in SCons is meant to be faster and simpler than autotools one — and thus uses less tests. It ain't going to warn you that have no/broken compiler or you miss critical dependencies — that's package manager work. It is meant to check for things that'd result in enabling or disabling some code, plugin or determining C-/LDFLAGS. 4) SCons doesn't accept some configure-like args. Instead of --with-*=, you should specify alternate include/lib paths by using C-/LDFLAGS (-I/-L) or specifying C_INCLUDE_PATH & LD_LIBRARY_PATH. 5) SCons doesn't use many fallbacks like autotools did. If some package (e.g. libgadu) is available through pkg-config, then SCons asks pkg-config for it. If it couldn't find it, SCons _does not_ search for library with some default params. If you install your .pc files in another directory, then please set PKG_CONFIG_PATH accordingly. 6) You really should take a look at scons --help (in main EKG2 sources directory) as it lists package-defined options. Among them, there are few you might like to twek (PLUGINS especially). If you're going to build EKG2 package, then you probably would like to set HARDDEPS=1 (and PREFIX=/usr probably). 7) If EKG2 with SCons fails to compile on some OS and it's not your fault then please let us know. If SCons does compile some plugin while deps are not met (i.e. configure thinks that some dependency is met while it isn't), please let us know. If configure says some dep is unmet, while it is met, then please first check you have correct version (i.e. gpgme >= 1.0.0). Best place to report bugs is http://bugs.ekg2.org/. If it's down, then you can find us on !ekg2, ircnet (or me on #jogger.pl, freenode). 8) SCons does everything what autotools + configure + make was meant to do. You don't want to run 'make' after it or './autogen.sh' before it. You just run 'scons' with correct args, and it does all the hard work. To install compiled files in system directories, use 'scons install'. You may also like to supply DESTDIR= option to it. To clean up, use 'scons -c'. To uninstall, use 'scons -c install'. 9) We require SCons 0.97 and Python 2.4. Sorry, Debian users, but <0.97 has too many differences, and even maintaining compatibility with bugged 0.97 is hard. And as it's pure python, so you won't have much trouble installing newer version. If someone needs compatibility with older Python (and scons works with it), then please let us know. 10) As you probably know, make has a feature that it rebuilds files only when they change or one of included files change. SCons goes few steps ahead and even installs files only when they differ. So, you may note that 'scons install' does nothing — that probably means all installed files are up to date. 11) SCons, unlike autotools, doesn't compile perl module itself. That task is left for more appriopriate software. If your distribution doesn't provide nice tools for building perl modules, in plugins/perl/* do: $ perl Makefile.PL $ make $ make install ~~: vim:set tw=80 ts=4 sts=4 sw=4 :~~ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/docs/TODO�������������������������������������������������������������������0000664�0000000�0000000�00000004675�11751427534�0015611�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������Nie zawracaj ludziom gowy i nie pisz, e chciaby co zaproponowa, jeli zostao wymienione na poniszych listach. Listy lub wiadomoci, eby co z nich dosta bd albo ignorowane, albo odpowied moe by wulgarna. Zwykle im dana sprawa jest wyej na licie, tym waniejsze jest jej rozwizanie. * przed wydaniem: - zmieni mylne nazwy plikw np old.c w ncurses na co sensownego - THANKS - chyba przydatny byby taki plik * klient: - bindowane CTRL+Strzaki, PageUP itd (del) - ostatnio piszca osoba blinka przez 5 sekund, w rogu ekranu (Oli) - uporzdkowane i dobrze dziaajce okna pywajce (sz?) - zmienna beep_*_away (rmrmg) - osobna historia dla kadego okna (koniu) - zapisywanie backlog'a z okna do pliku (G.Sulek) - zdarzenie dotyczce nowej konferencji (pmb) - /ignore dla uytkownikw spoza listy kontaktw (mateusza) - ,,wjedanie'' i automatyczne chowanie listy kontaktw, co na wzr pasku zada w windowsach (w) - moliwo definiowania kolorw pywajcych okien: to, ramka (w) - moliwo blokowania terminala po duszym czasie nieaktywnoci (Gambler) - okienka oparte o kolejne wywoania xterma (fidor) - dokumentacja do themes - opis kadego formatu (del) - naprawic config_keep_reason, kod z ekg1 sie przyda. * gg - dodawanie do treci wiadomoci, w ktrej jest obrazek informacji o tym (adamm) - "rozsdne" nazwy dla zapisywanych obrazkw (adamm) * jabber - resolve SRV first - emit events, authorisation-request and so on - conference support (similar to gg_command_msg() in gg) - ignore support *** Przed wydaniem wersji 1.0 *** [jabber] - edycja oraz wyszukiwanie informacji osobistej w katalogu uytkownikw - rejestracja nowego konta jabberowego [ekg2] - poprawi wszelkie znane bdy - kompilacja na FreeBSD - poprawi /window move tak, aby dziaao analogicznie do tego znanego z irssi [irc] - poprawi wszelkie znane bdy - popracowa nad stabilnoci *** *** *** *** *** W userlist_free() kasujemy przyporzadkowanie w->uid <==> w->nickname Dlatego trzeba dla kazdego okna danej sesji poprawic w->target na taki ktory bedzie dzialac nawet jak skasujemy userliste. [Zmienne sesyjne] gdy zmienna zaczyna sie od '__' wtedy sprawdzajmy tylko s->local_vars, oraz gdy nie istnieje dodajmy. Jesli nie, to wtedy sprawdz tylko zmienne zdefiniowane przez PLUGIN_VAR_ADD, a gdy nie ma nie dodawaj. Zrobic wczesniej audyt kodu, czy wszystkie pluginy spelniaja te zalozenia. �������������������������������������������������������������������ekg2-0.4~pre+20120506.1/docs/ULOTKA�����������������������������������������������������������������0000664�0000000�0000000�00000002564�11751427534�0016036�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ "Czytanie dokumentacji moe powodowa raka i choroby serca" minister do spraw informatyzacji i Windows [1] czyli krtki przewodnik po dokumentacji: README opis programu. gwny plik dokumentacji. FAQ najczciej zadawane pytania. jeli masz jaki problem, zajrzyj najpierw tutaj. wszelkie pytania, na ktre mona tu znale odpowied bd ignorowane, lub reakcja bdzie zoliwa i nieprzyjemna. TODO lista rzeczy, ktre czekaj w kolejce na realizacj. jeli chcesz napisa, e przydaaby si jaka rzecz, najpierw zajrzyj tutaj. moliwe, e kto wczeniej zgosi ,,zapotrzebowanie''. vars.txt opis zmiennych uywanych przez program. commands.txt opis funkcji uywanych przez program files.txt opis formatw plikw: listy kontaktw, konfiguracji, historii, emotikonw. themes.txt opis dotyczcy zmiany wygldu klienta. przydatny dla tych, ktrzy lubi bawi si w kolorki. devel-hints.txt wskazwki dla tych, ktrzy chc co poprawi w ekg/libgadu. przenosny-kod.txt kilka wskazwek na temat pisania kodu przenonego midzy rnymi uniksami. gdb.txt szybki kurs obsugi gdb. ui-ncurses.txt opis interfejsu ncurses oraz ustawie/moliwoci wystpujcych wycznie w tym interfejsie. sim.txt wskazwki dotyczce korzystania z szyfrowania wiadomoci. [1] autor Jacek Popawski, <slrnaor068.54v.jp@darkwood.somewhere> $Id$ ��������������������������������������������������������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/docs/autoconf-macros.md�����������������������������������������������������0000664�0000000�0000000�00000023503�11751427534�0020532�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ekg2-checks.m4 ======================================================================== AC_EKG2_CHECK_LIB ------------------------------------------------------------------------ AC_EKG2_CHECK_LIB(name, func, headers, [if-yes], [if-no]) A wrapper for `AC_CHECK_LIB` with additional `AC_CHECK_HEADERS` test. First checks if _any_ of header files listed in `headers` is usable and if it is, checks for function `func` in library `name`. If both tests succeed, `if-yes` is evaluated. If it is empty, the default success action of `AC_CHECK_LIB` is performed (`-l<name>` is appended to `LIBS` and `HAVE_LIB<name>` is declared). If any of the tests fail, `if-no` is evaluated. It defaults to no-op. Example: dnl 1) check for <ncurses/ncurses.h> or <ncurses.h> dnl 2) check for initscr in -lncurses dnl on success: -lncurses to libs, declare HAVE_LIBNCURSES dnl on failure: expand EKG_FAILED_TEST (see below) AC_EKG2_CHECK_LIB([ncurses], [initscr], [ncurses/ncurses.h ncurses.h],, [EKG2_FAILED_TEST]) AC_EKG2_CHECK_FLAGEXPORTED_LIB ------------------------------------------------------------------------ AC_EKG2_CHECK_FLAGEXPORTED_LIB(variable-prefix, lib-name, func, header, [if-yes], [if-no]) Perform tests similar to `AC_EKG2_CHECK_LIB` for a package whose CFLAGS & LIBS were exported as `<variable-prefix>_CFLAGS` & `<variable_prefix>_LIBS` (e.g. by pkg-config). This macro uses `AC_CHECK_HEADER` to check for `header`, and then `AC_CHECK_FUNCS` to check for `func` function. The correct `-l...` flags are supposed to be in the `*_LIBS` variable already, `lib-name` is used for the macro name only. If the tests succeed, `LIBS` and `CPPFLAGS` are updated with values from the prefixed variables. If `lib-name` is provided, macro `HAVE_LIB<lib-name>` is declared. Afterwards, `if-yes` is evaluated. If either of the tests fail, `LIBS` and `CPPFLAGS` are unchanged and `if-no` is evaluated. This macro is mostly used internally by `AC_EKG2_CHECK_PKGCONFIG_LIB` but it might be useful if a package does provide custom `*-config` script. For example: dnl AM_PATH_GPGME declares GPGME_CFLAGS & GPGME_LIBS dnl we apply them and check for gpgme.h and gpgme_new() dnl on success: declare HAVE_LIBGPGME dnl on failure: restore CPPFLAGS & LIBS, then EKG2_FAILED_TEST AM_PATH_GPGME([1.0.0], [ AC_EKG2_CHECK_FLAGEXPORTED_LIB([GPGME], [gpgme], [gpgme_new], [gpgme.h],, [EKG2_FAILED_TEST]) ], [EKG2_FAILED_TEST]) AC_EKG2_CHECK_PKGCONFIG_LIB ------------------------------------------------------------------------ AC_EKG2_CHECK_PKGCONFIG_LIB(pkg-name, fallback-name, func, header, [if-yes], [if-no], [if-fallback-yes]) Wrapper around `PKG_CHECK_MODULES` with fallback to `AC_EKG2_CHECK_LIB`. Checks for pkg-config module `pkg-name`. If it is found, grabs CFLAGS & LIBS grabbed by pkg-config, checks for `headers` header and then for `func` function. If it is not, tries to find the header with standard CPPFLAGS and the function within `fallback-name` library. If either of the methods succeed (i.e. both header & function is found), `HAVE_LIB<fallback-name>` is declared and `CPPFLAGS` & `LIBS` are updated. If pkg-config was unable to find the package but fallback succeeded, and `if-fallback-yes` is nonempty, it is evaluated. If it is empty, or pkg-config succeeds, `if-yes` is evaluated. If none of the methods is able to find both the header and the function, `if-no` is evaluated instead. Typical example: dnl Try to find libgadu.pc, fallback to -lgadu. dnl In any case, look for <libgadu.h> and gg_logoff(). dnl On success: declare HAVE_LIBGADU, set CPPFLAGS & LIBS. dnl On failure: EKG2_FAILED_TEST. AC_EKG2_CHECK_PKGCONFIG_LIB([libgadu], [gadu], [gg_logoff], [libgadu.h],, [EKG2_FAILED_TEST]) A more complex example: dnl Try openssl.pc, fallback to -lssl, dnl but in the latter case, remember to add -lcrypto as well. AC_EKG2_CHECK_PKGCONFIG_LIB([openssl], [ssl], [RSA_new], [openssl/ssl.h],,, [ LIBS="-lssl -lcrypto $LIBS" AC_DEFINE([HAVE_LIBSSL], [1], [blah blah blah]) ]) ekg2-with.m4 ======================================================================== AC_EKG2_MULTILIB ------------------------------------------------------------------------ AC_EKG2_MULTILIB Internal use macro which sets `EKG2_LIBDIRNAME` macro up. Techically, it checks whether `$libdir` ends with either `lib64` or `lib32`, and makes `EKG2_LIBDIRNAME` evaluate to either one of these two, or to simple `lib` otherwise. EKG2_LIBDIRNAME ------------------------------------------------------------------------ EKG2_LIBDIRNAME Evaluates to (possibly) correct libdir for a particular system. Example use: GLIB_CFLAGS="... -I/usr/EKG2_LIBDIRNAME/glib-2.0/include" EKG2_FAILED_TEST ------------------------------------------------------------------------ EKG2_FAILED_TEST Macro declared by `AC_EKG2_WITH` within `if-yes` scope. It is supposed to be used whenever the tests required for the package fail, to disable the relevant option. It _does not_ terminate the test scope. AC_EKG2_WITH ------------------------------------------------------------------------ AC_EKG2_WITH(optname, if-yes, [if-no], [alt-setup], [alt-cleanup]) Adds an EKG2-style `--with-<optname>` option, supporting both enabling/disabling a particular dependency and specifying an alternate prefix for it. The `if-yes` argument specifies the tests which are supposed to be performed for a particular package controlled by the option. If the tests fail, the `EKG2_FAILED_TEST` macro should be used. If neither `--with-*` nor `--without-*` option is specified, the tests specified in `if-yes` are evaluated. If they fail, `with_<optname>` is set to `no`. If macro `EKG2_FAILED_PLUGIN` is declared, it is expanded as well then. If `--without-*` or `--with-*=no` is specified, `if-no` is evaluated instead. If it is empty and `EKG2_DISABLED_TEST` macro is declared, it is expanded. If simple `--with-*` or `--with-*=yes` is specified, the tests in `if-yes` are performed. However, if `EKG2_FAILED_TEST` is called, the configure process is terminated immediately due to unsatisfied dependency. Otherwise, if `--with-*=<prefix>` is specified, tests are performed as above. If `alt-setup` is empty, `CPPFLAGS`, `LDFLAGS`, `PATH` and pkg-config paths are adjusted to prefix beforewards, and restored afterwards. If it is nonempty, `alt-setup` is expanded beforewards, and `alt-cleanup` afterwards instead. Example: dnl --with-expat AC_EKG2_WITH([expat], [ AC_EKG2_CHECK_LIB([expat], [XML_ParserCreate], [expat.h],, [EKG2_FAILED_TEST]) ]) ekg2-plugin.m4 ======================================================================== AC_EKG2_PLUGIN_SETUP ------------------------------------------------------------------------ AC_EKG2_PLUGIN_SETUP Internal use macro which sets up plugin support. It is invoked automatically before first use of `AC_EKG2_PLUGIN`. EKG2_FAILED_PLUGIN ------------------------------------------------------------------------ EKG2_FAILED_PLUGIN Macro declared by `AC_EKG2_PLUGIN` in `req-checks` scope. It is supposed to be used whenever tests for obligatory plugin requirements fail. Much like `EKG2_FAILED_TEST`, it does not terminate the test scope. It is called automatically by `EKG2_FAILED_TEST` if available. EKG2_DISABLED_TEST ------------------------------------------------------------------------ EKG2_DISABLED_TEST Macro declared by `AC_EKG2_PLUGIN` in `req-checks` scope. It is supposed to be used whenever obligatory plugin requirements are disabled through `--without-*` options. It does not terminate the test scope. By default, it is called automatically by `AC_EKG2_WITH`. It is used to detect conflicting options. AC_EKG2_PLUGIN ------------------------------------------------------------------------ AC_EKG2_PLUGIN(name, req-checks, opt-checks) Declare and initiate plugin `name`. Perform obligatory checks specified in `req-checks`, and then optional dependency checks in `opt-checks`. This macro creates `--enable-<name>` option for the plugin. If it is explicitly enabled and the plugin tests fail, configure process is aborted. If no option is specified and the checks fail, the plugin is disabled. If `--disable-*` option is specified, the plugin is disabled without performing the checks. The `req-checks` are supposed to check for _all_ obligatory plugin dependencies and should call `EKG2_FAILED_PLUGIN` (or in most cases, `EKG2_FAILED_TEST` within `AC_EKG2_WITH` as a wrapper for it) if any of the required dependencies is unsatisfied. The `opt-checks` are supposed to check for any optional plugin dependencies. These are performed only if obligatory checks succeed, and they _should not_ use `EKG2_FAILED_TEST`. The `AC_EKG2_PLUGIN` macro scopes `CPPFLAGS`, `LDFLAGS` and `LIBS`. If the tests succeed, it exports them as automake variables for the appropriate .la rule. It also declares `<name>dir` (e.g. `jabberdir`) for plugin data (e.g. help files), and sets up static plugin support if necessary. In any case, `ENABLE_<name>` automake conditional is declared. It evaluates to either true or false depending on whether the particular plugin is enabled (and obligatory dependency tests succeeded). Example: dnl Declare gg plugin dnl libgadu is an obligatory dep, thus EKG2_FAILED_TEST is used dnl additional functions are checked only if libgadu is found AC_EKG2_PLUGIN([gg], [ AC_EKG2_WITH([libgadu], [ AC_EKG2_CHECK_PKGCONFIG_LIB([libgadu], [gadu], [gg_logoff], [libgadu.h],, [EKG2_FAILED_TEST]) ]) ], [ AC_CHECK_FUNCS([gg_remind_passwd3 gg_change_passwd4 mkstemp]) ]) Respective `Makefile.am` fragment: # ENABLE_GG is always declared by AC_EKG2_PLUGIN if ENABLE_GG # append to LTLIBRARIES explicitly plugin_LTLIBRARIES += plugins/gg/gg.la # declare plugin sources plugins_gg_gg_la_SOURCES = \ ekg2-config.h \ plugins/gg/commands.c \ ... # install plugin data (into ggdir) dist_gg_DATA = \ plugins/gg/commands-en.txt \ ... endif <!-- vim:set syn=markdown : --> ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/docs/commands-pl.txt��������������������������������������������������������0000664�0000000�0000000�00000042747�11751427534�0020076�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������// opis komend ogólnych klienta // (c) copyright 2001-2003 wojtek kaniewski <wojtekka@irc.pl> // (c) copyright 2004 piotr kupisiewicz <deletek@ekg2.org> ! parametry: [opcje] <polecenie> krotki opis: synonim dla %Texec%n ? parametry: [polecenie] [zmienna] krotki opis: synonim dla %Thelp%n _addtab parametry: krotki opis: dodaje do listy dopełniania TABem _debug parametry: <tekst> krotki opis: wyświetla podany tekst w oknie debug _debug_dump parametry: krotki opis: zrzuca debug do pliku _deltab parametry: krotki opis: usuwa z listy dopełniania TABem _desc parametry: [-/opis] krotki opis: zmienia opis nie zmieniając stanu _fds parametry: krotki opis: wyświetla otwarte pliki _msg parametry: krotki opis: udaje, że wysyła wiadomość _queries parametry: krotki opis: wyświetla listę zapytań _query parametry: <zapytanie> [parametry...] krotki opis: generuje zapytanie _segv parametry: krotki opis: wywołuje naruszenie segmentacji pamięci _watches parametry: krotki opis: wyświetla listę przeglądanych deskryptorów add parametry: [numer] [alias] [opcje] krotki opis: dodaje użytkownika do listy kontaktów -f, --find [alias] dodaje ostatnio wyszukaną osobę W przypadku opcji %T--find%n alias jest wymagany, jeśli w ostatnim wyszukiwaniu nie znaleziono pseudonimu. Pozostałe opcje wykorzystywane są zależnie od protokołu. W oknie rozmowy z kimś spoza naszej listy kontaktów jako parametr można podać sam alias. alias parametry: [opcje] krotki opis: zarządzanie aliasami -a, --add <alias> <komenda> dodaje alias -A, --append <alias> <komenda> dodaje komendę do aliasu -d, --del <alias>|* usuwa alias [-l, --list] [alias] wyświetla listę aliasów W komendzie można użyć formatów od %T\%1%n do %T\%9%n i w ten sposób ustalić kolejność przekazywanych argumentów. W aliasie złożonym z wielu komend do przekazania argumentów używamy formatu %T\%*Tn. at parametry: [opcje] krotki opis: planuje wykonanie komend -a, --add [nazwa] <czas>[/częst.] <komenda> tworzy nowy plan -d, --del <nazwa>|* usuwa plan [-l, --list] [nazwa] wyświetla listę planów Czas podaje się w formacie [[[yyyy]mm]dd]HH[:]MM[.SS], gdzie %Tyyyy%n to rok, %Tmm%n to miesiąc, %Tdd%n to dzień, %THH:MM%n to godzina, a %T.SS%n to sekundy. Minimalny format to %THH:MM%n (dwukropek można pominąć). Po kropce można podać sekundy, a przed godziną odpowiednio: dzień miesiąca, miesiąc, rok. Jeśli podanie zostana częstotliwość, wyrażona w sekundach lub za pomocą przyrostków takich, jak dla komendy %Ttimer%n, to komenda będzie wykonywana w zadanych odstepach czasu od momentu jej pierwszego wykonania. beep parametry: krotki opis: wydaje dźwięk bind parametry: [opcje] krotki opis: przypisywanie akcji klawiszom -a, --add <sekwencja> <akcja> przypisuje nową sekwencję -d, --del <sekwencja> usuwa podaną sekwencję [-l, --list] [sekwencja] wyświetla przypisane sekwencje -L, --list-default [sekwencja] j.w. plus domyślne sekwencje -S, --set <sekwencja> binduje klawisze pod sekwencję -e, --exec <akcja> wykonuje akcję Dostępne sekwencje to: Ctrl-<znak>, Alt-<znak>, F<liczba>, Enter, Backspace, Delete, Insert, Home, End, Left, Right, Up, Down, PageUp, PageDown. Dostępne akcje to: backward-word, forward-word, kill-word, toggle-input, cancel-input, backward-delete-char, beginning-of-line, end-of-line, delete-char, backward-page, forward-page, kill-line, yank, accept-line, line-discard, quoted-insert, word-rubout, backward-char, forward-char, previous-history, next-history, complete, quick-list, toggle-contacts, next-contacts-group, ignore-query, forward-contacts-page, backward-contacts-page, forward-contacts-line, backward-contacts-line, previous-only-history, next-only-history, backward-lastlog-page, forward-lastlog-page. Każda inna akcja będzie traktowana jako komenda do wykonania. clear parametry: krotki opis: czyści ekran conference parametry: [opcje] krotki opis: zarządzanie konferencjami -a, --add [#nazwa] <numer/alias/@grupa> tworzy nową konferencję -j, --join [#nazwa] <numer/alias> przyłącza osobę do konferencji -d, --del <#nazwa>|* usuwa konferencję -i, --ignore <#nazwa> oznacza konferencję jako ignorowaną -u, --unignore <#nazwa> oznacza konferencję jako nieignorowaną -r, --rename <#old> <#new> zmienia nazwę konferencji -f, --find <#nazwa> wyszukuje uczestników w katalogu [-l, --list] [#nazwa] wyświetla listę konferencji Dodaje nazwę konferencji i definiuje, kto bierze w niej udział. Kolejne numery, pseudonimy lub grupy mogą być oddzielone przecinkiem lub spacją. dcc parametry: <komenda> [opcje] krotki opis: obsługa bezpośrednich połączeń [r]send <numer/alias> <ścieżka> wysyła podany plik get [numer/alias/#id] akceptuje przysyłany plik resume [numer/alias/#id] wznawia pobieranie pliku [r]voice <numer/alias/#id> rozpoczyna rozmowę głosową close <numer/alias/#id> zamyka połączenie list wyświetla listę połączeń Połączenia bezpośrednie wymagają włączonej opcji %Tdcc%n. Komendy %Trsend%n i %Trvoice%n wysyłają żądanie połączenia się drugiego klienta z naszym i są przydatne, gdy nie jesteśmy w stanie się z nim sami połączyć. del parametry: <numer/alias>|* krotki opis: usuwa użytkownika z listy kontaktów echo parametry: [tekst] krotki opis: wyświetla podany tekst eval parametry: <polecenie(a)> krotki opis: wykonuje podane polecenia Wykonuje podane polecenia odzdzielone spacjami. W przypadku gdy polecenie zawiera spacje należy użyć cudzysłowiów. Ze względu na budowę klienta, polecenia, numery i aliasy %Tnie będą%n dopełniane Tabem. exec parametry: [opcje] <polecenie> krotki opis: uruchamia polecenie systemowe -m, --msg [numer/alias] wysyła wynik do danej osoby -b, --bmsg [numer/alias] wysyła wynik w jednej wiadomości Poprzedzenie polecenia znakiem ,,%T^%n'' ukryje informację o zakończeniu. Zapisanie opcji wielkimi literami (np. %T-B%n) spowoduje umieszczenie polecenia w pierwszej linii wysyłanego wyniku. Ze względu na budowę klienta, numery i aliasy %Tnie będą%n dopełniane Tabem. for parametry: <opcje> <sesje/okna/alias>|* <polecenie> krotki opis: wykonuje polecenie dla danych/wszystkich sesji/okien/użytkowników -s, --sessions [nazwa] wykonuje polecenie dla podanych sesji -u, --users [nazwa] wykonuje polecenie dla podanych użytkowników -w, --windows [id] wykonuje polecenie dla podanych okien Można podać ,,%T*%n'' zamiast nazwy/id, tak aby komenda była wykonana dla wszystkich elementów. Polecenie może zawierać elementy specjalne, tzn pola ,,%T\%1%n'' i ,,%T\%2%n''. Oznaczają kolejno: dla sesji: nazwa, uid dla użytkowników: alias, uid dla okien: alias, uid help parametry: [polecenie] [zmienna] krotki opis: wyświetla informację o poleceniach Możliwe jest wyświetlenie informacji o zmiennych, jeśli jako polecenie poda się %Tset%n ignore parametry: [numer/alias] [poziom] krotki opis: dodaje do listy ignorowanych Dostępne poziomy ignorowania: - status - całkowicie ignoruje stan - descr - ignoruje tylko opisy - notify - nie wyświetla zmian stanu - msg - ignoruje wiadomości - dcc - ignoruje połączenia DCC - events - ignoruje zdarzenia związane z użytkownikiem - log - nie zapisuje w archiwum wiadomości użytkownika - * - wszystkie poziomy Poziomy można łączyć ze sobą za pomocą przecinka lub ,,%T|%n''. last parametry: [opcje] <alias>|* krotki opis: wyświetla lub czyści ostatnie wiadomości -c, --clear [numer/alias] czyści podane wiadomości lub wszystkie -s, --stime [numer/alias] wyświetla czas wysłania wiadomości -n, --number <n> [numer/alias] wyświetla %Tn%n ostatnich wiadomości [numer/alias] wyświetla ostatnie wiadomości W przypadku opcji %T--stime%n czas wyświetlany jest ,,inteligentnie'' zgodnie ze zmienną %Ttime_deviation.%n list parametry: [alias|@grupa|opcje|metakontakt|sesja/alias] krotki opis: zarządzanie listą kontaktów Wyświetlanie osób o podanym stanie "list [-a|-A|-B|-i|-I|-d|-m|-n|-o|-O]": -a, --active dostępne -A, --away zajęte -n, --notavail niedostępne -i, --inactive niedostępne -I, --invisible niewidoczne -B, --blocked blokujące nas -d, --description osoby z opisem -m, --member <@grupa> osoby należące do danej grupy -o, --offline osoby dla których jesteśmy niedostępni -O, --online osoby dla których zawsze jesteśmy widoczni Wyświetlanie członków grupy: "list @grupa". Wyświetlanie osób spoza grupy: "list !@grupa". metacontact parametry: [opcje] krotki opis: zarządzanie metakontaktami -a, --add <nazwa> dodaje metakontakt o podanej nazwie -d, --del <nazwa> usuwa metakontakt o podanej nazwie -i, --add-item <nazwa> <nazwa_sesji> <nazwa_kontaktu> <prio> dodaje do metakontaktu kontakt -r, --del-item <nazwa> <nazwa_sesji> <nazwa_kontaktu> usuwa z metakontaktu kontakt -l, --list wyświetla listę wszystkich metakontaktów <nazwa> wyświetla listę kontaktów danego metakontaktu Przykładowe dodanie metakontaktu może wyglądać następująco: metacontact -a metakontakt metacontact -i metakontakt sesja nazwa_użytkownika 1 Metakontakty pozwalają na stworzenie kontaktu zawierającego inne kontakty. query metakontakt otwiera rozmowę z osobą, która jest aktualnie dostępna i ma największy priorytet. W przypadku, w którym żaden z kontaktów nie jest dostępny, wiadomość kierowana jest do osoby o największym priorytecie. Funkcje korzystające z metakontaktów to: query <nazwa> rozpoczyna rozmowę list <nazwa> pokazuje aktualny stan metakontaktu.%n on parametry: [opcje] krotki opis: zarządzanie zdarzeniami -a, --add <zdarzenie> <priorytet> <założenia> <komenda> dodaje zdarzenie -d, --del <numer>|* usuwa zdarzenie o podanym numerze [-l, --list] [numer] wyświetla listę zdarzeń Dostępne zdarzenia można znaleźć w pliku events.txt w dokumentacji programu. Założenie mogą być następujące: ,,%T=%n'' - jest takie same i wielkości liter nie mają znaczenia ,,%T==%n'' - jest takie same i wielkości liter mają znaczenie ,,%T+%n'' - pierwszy ciąg zawiera się w drugim, wielkość liter nie ma znaczenia ,,%T++%n'' - pierwszy ciąg zawiera się w drugim, wielkość liter ma znaczenie Założenia można łączyć poprzez ,,|'' (lub) i ,,&'' (i). Są to standardowe operatory logiczne.Zaprzeczenia tworzymy przez dodanie ,,%T!%n'' przed założeniem, np. ,,%T!+%n''. Można używać \%1 zamiast nazwy nadawcy oraz \%2 zamiast ewentualnych parametrów. Gdy potrzeba użyć spacji w założeniu należy całe założenie ująć w cudzysłów. W założeniach można używać: \%1 - jako uid sprawcy \%2 - jako pseudonim sprawcy, jeżeli nie istnieje na liście kontaktów to uid \%3 - treść wiadomości, opis czy też inne dane przekazane przez wydarzenie Przykładowe założenie może mieć postać: ,,%T\%1=nick&\%1!=nick2|\%2+tekst%n'' W przypadkach, w których chcemy, aby zdarzenie dotyczyło wszystkich możliwych sytuacji należy zamiast założenia użyć ,,%T*%n''. Nazwy zdarzeń mogą być roździelone przecinkiem. - * - wszystkie zdarzenia W przypadku gdy istnieje wiele zdarzeń pasujących do kryterium (np. * i nick) wykonywane będzie to z większym priorytetem. Można podać więcej komend, oddzielając je średnikiem. W komendzie, %T\%1%n zostanie zastąpione numerkiem sprawcy zdarzenia, a jeśli istnieje on na naszej liście kontaktów, %T\%2%n będzie zastąpione jego pseudonimem. Zamiast %T\%3%n i %T\%4%n wpisana będzie treść wiadomości, opis użytkownika, całkowita ilość nowych wiadomości e-mail lub nazwa pliku - w zależności od zdarzenia. Format %T\%4%n różni się od %T\%3%n tym, że wszystkie niebiezpieczne znaki, które mogłyby zostać zinterpretowane przez shell, zostaną poprzedzone backslashem. Używanie %T\%3%n w przypadku komendy ,,exec'' jest %Tniebezpieczne%n i, jeśli naprawdę musisz wykorzystać treść wiadomości lub opis, użyj %T"\%4"%n (w cudzysłowach). play parametry: <plik> krotki opis: odtwarza plik dźwiękowy plugin parametry: [-|+][nazwa][opcje] [prio] krotki opis: ładuje lub usuwa rozszerzenie ekg2 -d, --default ustawia domyślne priorytety pluginom Usuwa lub dodaje plugin ekg2. Drugi parametr pozwala ustawić priorytet pluginu, co pozwala ustawić kolejność przetwarzania instrukcji przez pluginy. query parametry: <numer|alias|@grupa|metakontakt|sesja/alias|sesja/numer> [wiadomość] krotki opis: włącza rozmowę Można podać większą ilość odbiorców oddzielając ich numery lub pseudonimy przecinkiem (ale bez odstępów). W takim wypadku zostanie rozpoczęta rozmowa grupowa. queue parametry: [opcje] krotki opis: zarządzanie wiadomościami do wysłania po połączeniu -c, --clear [numer/alias] usuwa podane wiadomości lub wszystkie [numer/alias] wyświetla kolejkę wiadomości Podając numer lub alias, należy podać ten, który był używany przy wysyłaniu wiadomości, lub nazwę okna, w którym wiadomości były wysyłane. quit parametry: [powód/-] krotki opis: wychodzi z programu Podanie ,,%T-%n'' zamiast powodu spowoduje wyczyszczenie bez względu na ustawienia zmiennych. reload parametry: krotki opis: wczytuje pliki konfiguracyjny, pluginy oraz sesje save parametry: krotki opis: zapisuje ustawienia programu Aktualny stan zostanie zapisany i będzie przywrócony przy następnym uruchomieniu programu. say parametry: [tekst] krotki opis: wymawia tekst -c, --clear usuwa z bufora tekst do wymówienia Polecenie wymaga zdefiniowana zmiennej %Tspeech_app%n session parametry: [opcje] krotki opis: zarządzanie sesjami <uid> informacje o sesji -a, --add <uid> tworzy nową sesję -d, --del <uid> usuwa sesję [-l, --list] wyświetla listę [-g, --get] [<uid>] <opcja> wyświetla opcję sesji [-s, --set] [<uid>] <opcja> <wartość> zmienia opcję sesji [-s, --set] [<uid>] -<opcja> usuwa opcję sesji [-w, --sw] <uid> zmienia aktualną sesję Pamiętaj o tym, że uid ma postać <prefix>:<uid> np. %Tgg:12345%n. set parametry: [-]<zmienna> [[+/-]wartość] krotki opis: wyświetla lub zmienia ustawienia Użycie %Tset -zmienna%n czyści zawartość zmiennej. Dla zmiennych będącymi mapami bitowymi można określić, czy wartość ma być dodana (poprzedzone plusem), usunięta (minusem) czy ustawiona (bez prefiksu). Wartość zmiennej można wziąć w cudzysłów. Jeżeli chcemy przełączyć wartość zmiennej typu bool używamy %T,t'%n zamiast ustawianej wartości. Poprzedzenie opcji parametrem %T-a%n lub %T--all%n spowoduje wyświetlenie wszystkich, nawet aktualnie nieaktywnych zmiennych. Parametr %T-q%n lub %T--quiet%n spowoduje, że informacja o nowej wartości zmiennej nie zostanie wyświetlona i nie zaznaczy zmiany konfiguracji. status parametry: [opcje] krotki opis: wyświetla aktualny stan <uid> wyświetla aktualny stan dla konkretnej sesji tabclear parametry: [opcje] krotki opis: czyści listę nicków do dopełnienia -o, --offline usuwa tylko nieobecnych timer parametry: [opcje] krotki opis: zarządzanie timerami -a, --add [nazwa] [*/]<czas> <komenda> tworzy nowy timer -d, --del <nazwa>|* zatrzymuje timer [-l, --list] [nazwa] wyświetla listę timerów Czas, po którym wykonana zostanie komenda, podaje się w sekundach. Można też użyć przyrostków %Td%n, %Th%n, %Tm%n, %Ts%n, oznaczających dni, godziny, minuty, sekundy, np. 5h20m. Timer po jednorazowym uruchomieniu jest usuwany, chyba że czas poprzedzimy wyrażeniem ,,%T*/%n''. Wtedy timer będzie uruchamiany w zadanych odstępach czasu, a na liście będzie oznaczony gwiazdką. unignore parametry: <numer/alias>|* krotki opis: usuwa z listy ignorowanych osób version parametry: krotki opis: wyświetla wersję programu window parametry: <komenda> [numer_okna] krotki opis: zarządzanie oknami active przełącza do pierwszego okna, w którym coś się dzieje clear czyści aktualne okno kill [numer_okna] zamyka aktualne lub podane okno last przełącza do ostatnio wyświetlanego okna list wyświetla listę okien move <docelowe> [źródłowe] przesuwa okno opcja <docelowe> może przyjąć wartości ,,left'' i ,,right'' new [nazwa] tworzy nowe okno next przełącza do następnego okna prev przełącza do poprzedniego okna switch <numer_okna> przełącza do podanego okna refresh odświeża aktualne okno �������������������������ekg2-0.4~pre+20120506.1/docs/doxygen.settings�������������������������������������������������������0000775�0000000�0000000�00000002360�11751427534�0020350�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ PROJECT_NAME=ekg2 PROJECT_NUMBER="GIT master" OUTPUT_LANGUAGE=@OUTPUT_LANGUAGE@ # XXX, INPUT_ENCODING nie wiedziec czemu nie dziala. # dlatego tymczasowo OUTPUT_LANGUAGE jest Polish # co powoduje zmiane kodowania strony na ISO-8859-2 FILE_PATTERNS=*.c *.h @EXTRA_FILE_PATTERNS@ OPTIMIZE_OUTPUT_FOR_C=YES ENABLE_PREPROCESSING=YES MACRO_EXPANSION=YES EXPAND_ONLY_PREDEF=NO SEARCH_INCLUDES=YES INCLUDE_PATH=.. ../ekg SKIP_FUNCTION_MACROS=NO EXPAND_AS_DEFINED=COMMAND QUERY WATCHER WATCHER_AUDIO WATCHER_LINE WATCHER_SESSION WATCHER_SESSION_LINE BINDING_FUNCTION TIMER TIMER_SESSION LIST_ADD_COMPARE SNIFF_HANDLER GG_PACKED JABBER_HANDLER JABBER_HANDLER_GET_REPLY JABBER_HANDLER_IQ JABBER_HANDLER_RESULT JABBER_HAVE_SSL STRICT_XMLNS WITH_JABBER_DCC GMAIL_MAIL_NOTIFY INPUT=../ EXCLUDE=../ekg2-config.h RECURSIVE=YES INPUT_ENCODING=ISO-8859-2 EXTRACT_ALL=YES EXTRACT_STATIC=YES FULL_PATH_NAMES=YES STRIP_FROM_PATH=@STRIP_FROM_PATH@ QUIET=YES WARNINGS=YES OUTPUT_DIRECTORY=@OUTPUT_DIRECTORY@ GENERATE_LATEX=NO HTML_OUTPUT=./ # XXX, DOT. generowanie grafiki. callgraphow, itd, fajne. tylkoze zasobozerne.. raz dziennie moze mozna # to zrobic, ale nie ciagle. # "HAVE_DOT=YES\n" # "INCLUDE_GRAPH=NO\n" # "INCLUDED_BY_GRAPH=NO\n" # "WARN_IF_UNDOCUMENTED=YES\n" ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/docs/doxygenpl.txt����������������������������������������������������������0000664�0000000�0000000�00000026144�11751427534�0017666�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������/* ================================== PLUGINY =================================== */ /** @defgroup plugins Pluginy w ekg2 Wiec chcesz zbudowac plugin dla ekg2? W tej czesci dokumentacji sa (jesli czegos brakuje daj nam znac na ekg2-devel) informacje nt. jak zbudowac nowy modul do ekg2, jesli jest on szczegolnego typu (innego niz PLUGIN_GENERIC) znajdziesz informacje jak on powinien sie zachowywac w podgrupach. Zaczynamy tworzenie plugina od stworzenia nowego katalogu w ekg2/plugins <pre> ekg2/plugins $ mkdir foo ekg2/plugins $ cd foo ekg2/plugins/foo $ </pre> Nasz przykladowy plugin bedzie sie nazywac foo :) Nastepnie tworzymy plik foo.c (moglby sie nazywac main.c, ale jednak unikajmy potocznych nazw)<br> I edytujemy go naszym ulubionym edytorem <pre> ekg2/plugins/foo $ $EDITOR foo.c </pre> I wklepujemy: <pre> \#include <ekg/plugins.h> PLUGIN_DEFINE(foo, PLUGIN_GENERIC, NULL); /* (1) */ EXPORT int foo_plugin_init(int prio) { /* (2) */ plugin_register(&foo_plugin, prio); /* (3) */ return 0; /* (4) */ } static int foo_plugin_destroy() { /* (5) */ plugin_unregister(&foo_plugin); /* (6) */ return 0; /* (7) */ } </pre> Komentarz: <pre> (1) - definiuje plugin o nazwie 'foo' w foo_plugin, o typie PLUGIN_GENERIC. Trzeci parametr bede wyjasniac w dalszej czesci dokumentacji, na razie nie potrzebujemy :) (2) - podczas ladowania plugina, ekg2 szuka symbolu o nazwie PLUGIN_plugin_init() atrybut EXPORT jest zdefiniowany w plugins.h, dzieki temu i -fvisibility=hidden zmniejszamy czas potrzebny na szukanie symbolu (oraz rozmiar binarki) W prio mamy priorytet. Wypada go nie zmieniac, tylko go przekazac do plugin_register() (3) - Rejestrujemy plugin. (3,4) - Zwracamy 0 - udalo sie ladowanie. Pluginy moga zwracac -1 jak sie nie udalo zainicjowac plugina (np z powodu tego ze nie udalo sie utworzyc/odczytac jakiegos pliku, or smth), Tylko wazne zeby ten powrot byl robiony _przed_ plugin_register() (5) - definiujemy funkcje niszczaca plugin, deklaracja jest robiona przy PLUGIN_DEFINE() wiec jak chcemy zeby sie ona inaczej nazywala, albo byla w innym pliku niz foo.c, to nie mozemy korzystac z PLUGIN_DEFINE() [co jest niezalecane] (6, 7) - odrejestrujemy plugin, i zwracamy 0. </pre> Teraz wypadaloby go skompilowac i sprawdzic czy dziala :) ekg2 korzysta z GNU autotoolsow i GNU libtoola, wiec pisanie Makefile'a sprowadza sie tylko do wklepania w $EDITOR Makefile.am <pre> lib_LTLIBRARIES = foo.la foo_la_SOURCES = foo.c foo_la_LDFLAGS = -module -avoid-version foo_la_CFLAGS = \$(C_FVISIBILITY) \$(AM_CFLAGS) libdir = \$(plugindir) </pre> oraz w ekg2/configure.ac dodania do AC_CONFIG_FILES() plugins/foo/Makefile albo mniejszego babrania sie i zrobienia osobno wpisu: AC_CONFIG_FILES([plugins/foo/Makefile]) [XXX, ta czesc dokumentacji do sprawdzenia. Wystarczy samo make teraz?] Nastepnie robimy ./autogen.sh co wygeneruje nam nowe ./configure oraz je uruchomi. Teraz jesli nie wyswietlilo nam sie:<br> config.status: creating plugins/foo/Makefile<br> <br> bo ./autogen.sh uruchomilo ./configure z --no-create to uruchamiamy chociazby gmake, co powinno wygenerowac Makefile'a<br> Kiedy juz mamy ekg2/plugins/foo/Makefile to uruchamiamy: <pre> ekg2/plugins/foo $ gmake ekg2/plugins/foo $ sudo gmake install ekg2/plugins/foo $ ekg2 -u testfoo42135627 </pre> Sprobujmy zaladowac plugin w ekg2.. <pre> /plugin +foo xx:yy:zz ::: Plugin foo zostal zaladowany Hurray :) </pre> [XXX, na razie tyle, dokonczyc] */ /** @defgroup plugins_proto Pluginy protokolow (PLUGIN_PROTOCOL) @ingroup plugins @note ekg2 aktualnie obsluguje 4 protokoly: gg, irc, jabber, tlen Wiec jesli nie znajdziesz tutaj informacji nt. jak zrobic dana rzecz, mozesz poszukac jej w zrodlach innego plugina, albo zapytac na ekg2-devel @note Opieramy sie na pluginie foo, ktory zaczelismy pisac wczesniej. Dodajmy naszemu pluginow mozliwosci zalozenia sesji. Do tego sluzy event: <i>PROTOCOL_VALIDATE_UID</i> Zmienimy przy okazji typ plugina na PLUGIN_PROTOCOL Ponizej caly kod: <pre> \#include <ekg/plugins.h> \#include <ekg/queries.h> /* (1) */ \#include <ekg/xmalloc.h> \#include <stdarg.h> PLUGIN_DEFINE(foo, PLUGIN_PROTOCOL, NULL); static QUERY(foo_validate_uid) { /* (2) */ char *uid = *(va_arg(ap, char **)); int *valid = va_arg(ap, int *); if (!xstrncmp(uid, "foo:", 3) && uid[4]) { /* (3) */ (*valid)++; return -1; } return 0; } EXPORT int foo_plugin_init(int prio) { plugin_register(&foo_plugin, prio); query_connect_id(&foo_plugin, PROTOCOL_VALIDATE_UID, foo_validate_uid, NULL); /* (4) */ return 0; } static int foo_plugin_destroy() { plugin_unregister(&foo_plugin); return 0; } </pre> Komentarz: <pre> (1) - plik naglowkowy, zawierajacy numeryczne wartosci dla query_connect_id() i query_emit_id() (2) - w ekg2 lubimy korzystac z makr deklarujace funkcje. QUERY\alamakota(baz) jest rozwijane na int baz(void *data, va_list ap); Jesli nie wierzysz mozesz sprawdzic w plugins.h (3) - Sprawdzamy czy sesja zaczyna sie od foo: i czy wystepuje jakis znak po protokole (konwencja nazywania sesji w ekg2 to protokol:smth...) (4) - chcemy dostawac PROTOCOL_VALIDATE_UID, odbieramy w funkcji foo_validate_uid() 4 parametr query_connect_id() oznacza co nalezy przekazac w polu data. W tym przypadku nie potrzebujemy wiec wpisujemy NULL. Zdarzenie PROTOCOL_VALIDATE_UID, sluzy m. in do sprawdzenia czy dany ciag znakow moze byc sesja. .- Jesli tak, to nalezy zinkrementowac wartosc wskaznika przekazanego w II zmiennej (w tym przypadku zmienna valid) na 1, a nastepnie zwrocic -1. .- Jesli nie, to nalezy zwrocic 0. </pre> <pre> Kompilujemy plugin, instalujemy. Uruchamiamy nasze ekg2 z profilem testfoo42135627 (to z zaladowanym pluginem foo) I sprobujmy utworzyc sesje: /session -a foo:bar 00:00:00 ::: Utworzono sesje foo:test Ok, no to teraz sprobujmy sie polaczyc :) /connect 00:00:02 ::: Nieznane polecenie: connect </pre> <pre> Bylo do przewidzenia, mamy swoj wlasny plugin, mamy swoja wlasna sesje, (ktora jak zrobimy /save a potem uruchomimy jeszcze raz ekg2, nam sie pojawi) ale nic z nia nie mozemy zrobic. </pre> */ /** @defgroup plugins_ui Pluginy interfejsow (PLUGIN_UI) @ingroup plugins */ /** @defgroup plugins_log Pluginy logowania (PLUGIN_LOG) @ingroup plugins */ /** @defgroup plugins_scripting Pluginy do oskryptowywania ekg2 (PLUGIN_SCRIPTING) @ingroup plugins ekg2 obsluguje calkiem przyjemne API do stworzenia plugina pozwalajacego na pisanie w innych jezykach niz C. [XXX] */ /** @defgroup plugins_audio Pluginy do obslugi dzwiekow (odtwarzanie/nagrywanie) (PLUGIN_AUDIO) @ingroup plugins */ /** @defgroup plugins_codec Pluginy do obslugi kodekow audio (PLUGIN_CODEC) @ingroup plugins */ /** @defgroup plugins_crypt Pluginy szyfrujace w ekg2 (PLUGIN_CRYPT) @ingroup plugins */ /* ===================================== SKRYPTY ====================================== */ /** @defgroup scripts Jezyki skryptowe w ekg2 Tutaj sa tylko informacje nt. tworzenia skryptow w poszczegolnych jezykach, jak chcesz sie dowiedziec jak zbudowac taki plugin zobacz w dziale: plugins_scripting [XXX] Jak chcesz wiedziec jak wyglada uzytkowanie skryptow, to powinienes zobaczyc ekg2book, ale w zwiazku z tym ze tam nic nie ma to tutaj masz wskazowki: [XXX, przeniesc informacje stad do ekg2booka] <pre> Najpierw ladujesz plugin obslugujacy ten jezyk: /plugin +perl /plugin +python /plugin +ruby ..... Powinien sie zaladowac, jak nie, to albo brakuje Ci jakiegos symbolu uzytego przez plugin, albo brakuje Ci pliku.. Wszystko jest w oknie debug. Potem ladujesz skrypt przez: /script:load skrypt /script +skrypt [ Nie musisz podawac rozszerzenia, ekg2 potrafi sobie je wykryc samemu bazujac na podstawie zaladowanych wtyczek. python - *.py perl - *.pl ruby - *.rb Sciezki tez nie musisz podawac jesli bedzie w: w $DATADIR/scripts/ albo w .ekg2/profil/scripts [lub .ekg2/scripts gdy nie korzystasz z profilu] ] Mozesz tez zaladowac przez odpowiednie komende dostarczona przez plugin: /python:load skrypt Tym sposobem nie zaladujesz skryptu perla :) Jesli bedzie istnialo skrypt.pl Duzo tych sposobow, wiec wybierz jaki chcesz. Jak sie zaladuje to sie wyswietli komunikat. Jesli nie, to tez. Jak skrypt bedzie bledny to tez. Jak skrypt o podanej nazwie jest zaladowany, to go wyladuje, a potem zaladuje jeszcze raz. Jeszcze istnieje trik ze mozna dodac skrypt do autoruna, i bedzie sie uruchamial przy starcie: /script:autorun skrypt <-- dodaje/usuwa skrypt o nazwie skrypt do autostartu To tak na szybko napisane, tekst tutaj ma byc bardziej techniczny niz user-friendly, wiec jak jakies pytania to pisac na ekg2-users. </pre> */ /** @defgroup scripts_python Pisanie skryptow w pythonie. @ingroup scripts */ /** @defgroup scripts_perl Pisanie skryptow w perlu. @ingroup scripts */ /** @defgroup scripts_ruby Pisanie skryptow w ruby. @ingroup scripts @note Plugin ruby byl pisany przez osobe ktora z rubym ma b. malo wspolnego. Dokumentacja tez. Ksztalt skryptow byl konsultowany z uzytkownikami ruby'ego; Jednakze jesli myslisz ze aktualne API nie jest ruby-like, i uwazasz ze powinno byc zrobione inaczej, pisz na ekg2-devel. @note Plugin ruby jest under development, co znaczy ze calkiem mozliwe ze API jutro bedzie inaczej wygladac, niz wyglada dzis. Najpierw sprawdzmy czy plugin ruby sie skompilowal - bez niego nic nie zrobimy! <pre> $ ls `ekg2-config --plugin-dir`/ruby.so /usr/local/lib/ekg2/plugins/ruby.so </pre> Wiec jest ok :) Ponizej najprostszy skrypt w ruby dla ekg2. <pre> if \$0 != "ekg2" print <<MSG warning: you are executing an embedded ruby file! this file is suppose to be run only from ekg2. MSG exit end include Ekg2 class Ekg2::Script::HelloWorld < Ekg2::Script def initialize super print "Hello World!" end def finalize print "Zegnaj swiecie!" end end </pre> <pre> Nie, ogolnie to nie bedzie dokumentacja: "Jak programuje sie w ruby dla ekg2 - dla topornych" Tylko chcialbym zakreslic pewne niuanse, jakich wymaga ekg2, do poprawnej obslugi skryptow. Na poczatku jest kod, ktory uniemozliwia uruchamianie skryptu poza ekg2. Potem mamy definicje klasy o nazwie Ekg2::Script::HelloWorld, dziedziczacej po Ekg2::Script - nazwa klasy musi sie zaczynac z duzej litery. - musi miec taka sama nazwe jak skrypt. (Czyli ten skrypt nalezy zapisac jako HelloWorld.rb) - musi dziedziczyc po Ekg2::Script - funkcja initialize musi miec na samym poczatku 'super' Zapisujemy skrypt w ~/.ekg2/truby/scripts/HelloWorld.rb \$ ~/.ekg2/truby/scripts $ ruby HelloWorld.rb warning: you are executing an embedded ruby file! this file is suppose to be run only from ekg2. \$ ekg2 -u truby /plugin +ruby /script +HelloWorld xx:yy:aa ::: [script,HelloWorld] (ruby) Hello World! xx:yy:aa ::: Skrypt HelloWorld (/home/darkjames/.ekg2/truby/scripts/HelloWorld.rb) zostal zaladowany (ruby) /script -HelloWorld xx:yy:zz ::: [script,HelloWorld] (ruby) Zegnaj swiecie! xx:yy:zz ::: Skrypt HelloWorld (/home/darkjames/.ekg2/truby/scripts/HelloWorld.rb) zostal usuniety (ruby) Hurray. </pre> [XXX, tutaj juz konkretnie pisac o API] */ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/docs/ekg2-remote.en.1�������������������������������������������������������0000664�0000000�0000000�00000003513�11751427534�0017713�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������.\" Manpage created by Marcin Owsiany <porridge@debian.org> .TH EKG2-REMOTE "1" 2008-03-05 "User Commands" .SH NAME ekg2-remote \- remote User Interface for EKG2 .SH SYNOPSIS .B ekg2-remote .RI [ OPTIONS ] REMOTE-ENDPOINT .SH DESCRIPTION .B ekg2-remote is a program which provides a remote user interface to an EKG2 instance that uses the "remote" plugin. It connects to EKG2 via a socket, and lets you use the program as if it was running locally. .sp 1 The motivation behind this program was to be able to use EKG2 on a low performance machine with limited memory and CPU resources. .SH OPTIONS .TP .BI \-c \fR,\ \-\-charset =CHARSET forces the charset name to use, .TP .BI \-p \fR,\ \-\-password =PASSWORD sets the password, .TP .BI \-T \fR,\ \-\-test =FRONTEND runs in test mode (for debugging), using .I FRONTEND user interface (default is ncurses), .TP .BI \-F \fR,\ \-\-frontend =FRONTEND uses .I FRONTEND user interface (default is ncurses), .TP .BR \-m ,\ \-\-no-mouse does not load mouse support, .TP .BR \-U ,\ \-\-unicode forces unicode support, .TP .BR \-h ,\ \-\-help displays a help message, .TP .BR \-v ,\ \-\-version displays program version and exits .P .I ENDPOINT specifies the EKG2 instance to connect to. The following are accepted: .TP .BI tcp: address : port cleartext TCP/IP connection to given .I address and .I port .TP .BI tcps: address : port SSL-encrypted TCP/IP connection to given .I address and .I port .TP .BI udp: address : port UDP/IP connection to given .I address and .I port .TP .BI unix: socket-path local (UNIX) connection to given .I socket-path .TP .BI pipe: fifo-path local (pipe) connection to given .IR fifo-path . Currently unsupported. .SH "SEE ALSO" .BR ekg2 (1). The full documentation for .B ekg2 is maintained as a Docbook manual. See .B http://ekg2.org/ekg2book/ for an online version. �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/docs/ekg2-remote.pl.1�������������������������������������������������������0000664�0000000�0000000�00000003661�11751427534�0017730�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������.\" Autor strony podrcznika: Marcin Owsiany <porridge@debian.org> .TH EKG2-REMOTE "1" 2008-03-09 "Polecenia uytkownikw" .SH NAZWA ekg2-remote \- zdalny interfejs uytkownika dla EKG2 .SH SKADNIA .B ekg2-remote .RI [ OPCJE ] CEL .SH OPIS .B ekg2-remote jest to program udostpniajcy zdalny interfejs uytkownika dla programu EKG2 uywajcego wtyczki "remote". czy si on z EKG2 przez gniazdo i pozwala uywa go tak, jakby dziaa na lokalnej maszynie. .sp 1 Gwn motywacj tego programu byo umoliwienie uywania EKG2 na mao wydajnych maszynach, z ograniczonymi zasobami pamici i procesora. .SH OPCJE .TP .BI \-c \fR,\ \-\-charset =ZESTAW-ZNAKW wymusza uycie danego ZESTAWU-ZNAKW, .TP .BI \-p \fR,\ \-\-password =HASO ustawia haso, .TP .BI \-T \fR,\ \-\-test =FRONTEND uruchamia tryb testowy, uywajc interfejsu uytkownika .I FRONTEND (domylny to ncurses), .TP .BI \-F \fR,\ \-\-frontend =FRONTEND uywa interfejsu uytkownika .I FRONTEND (domylny to ncurses), .TP .BR \-m ,\ \-\-no-mouse wycza obsug myszy, .TP .BR \-U ,\ \-\-unicode wymusza obsug unicode, .TP .BR \-h ,\ \-\-help wywietla komunikat pomocy, .TP .BR \-v ,\ \-\-version wywietla wersj programu i koczy prac .P .I CEL okrela program EKG2 do ktrego naley si podczy. Obsugiwane s nastpujce schematy: .TP .BI tcp: adres : port nieszyfrowane poczenie TCP/IP do danego .I adresu i .I portu .TP .BI tcps: adres : port szyfrowane przy pomocy SSL poczenie TCP/IP do danego .I adresu i .I portu .TP .BI udp: adres : port poczenie UDP/IP do danego .I adresu i .I portu .TP .BI unix: cieka-gniazda lokalne (UNIX) poczenie do danej .I cieki-gniazda .TP .BI pipe: cieka-potoku lokalne (FIFO) poczenie do danej .IR cieki-potoku . Obecnie nie obsugiwane. .SH "ZOBACZ TAKE" .BR ekg2 (1). Pena dokumentacja dla programu .B ekg2 jest utrzymywana jako podrcznik Docbook. Jest ona dostpna pod adresem .B http://ekg2.org/ekg2book/ �������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/docs/ekg2.en.1��������������������������������������������������������������0000664�0000000�0000000�00000002764�11751427534�0016431�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������.\" Generated by help2man 1.35. .\" Edited further by Marcin Owsiany <porridge@debian.org> .TH EKG2 "1" "September 2005" "ekg2" "User Commands" .SH NAME ekg2 \- opensource IM client for UNIX systems .SH SYNOPSIS .B ekg2 [\fIOPTIONS\fR] [\fICOMMANDS\fR] .SH DESCRIPTION .TP \fB\-u\fR, \fB\-\-user\fR=\fINAME\fR uses profile NAME .TP \fB\-t\fR, \fB\-\-theme\fR=\fIFILE\fR loads theme from FILE .TP \fB\-n\fR, \fB\-\-no\-auto\fR does not connect to server automatically .TP \fB\-m\fR, \fB\-\-no\-mouse\fR does not load mouse support .TP \fB\-N\fR, \fB\-\-no\-global\-config\fR ignores global configuration file .TP \fB\-a\fR, \fB\-\-away\fR[=\fIDESCRIPTION\fR] changes status to ``away'' .TP \fB\-b\fR, \fB\-\-back\fR[=\fIDESCRIPTION\fR] changes status to ``available'' .TP \fB\-i\fR, \fB\-\-invisible\fR[=\fIDESCR\fR] changes status to ``invisible'' .TP \fB\-d\fR, \fB\-\-dnd\fR[=\fIDESCRIPTION\fR] changes status to ``do not disturb'' .HP \fB\-f\fR, \fB\-\-free\-for\-chat\fR[=\fIDESCR\fR] changes status to ``free for chat'' .TP \fB\-x\fR, \fB\-\-xa\fR[=\fIDESCRIPTION\fR] changes status to ``very busy'' .TP \fB\-h\fR, \fB\-\-help\fR displays this help message .TP \fB\-v\fR, \fB\-\-version\fR displays program version and exits .PP Options concerned with status depend on the protocol of particular session \fB\-\-\fR some sessions may not support ``do not disturb'' status, etc. .SH "SEE ALSO" The full documentation for .B ekg2 is maintained as a Docbook manual. See .B http://ekg2.org/ekg2book/ for an online version. ������������ekg2-0.4~pre+20120506.1/docs/ekg2.pl.1��������������������������������������������������������������0000664�0000000�0000000�00000003056�11751427534�0016435�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������.\" Generated by help2man 1.35. .\" Edited further by Marcin Owsiany <porridge@debian.org> .TH EKG2 "1" "wrzesie 2005" "ekg2 (compiled on Sep 5 2005 14:37:51)" "Polecenia uytkownika" .SH NAZWA ekg2 \- otwarty klient IM dla systemw uniksowych .SH SKADNIA .B ekg2 [\fIOPCJE\fR] [\fIKOMENDY\fR] .SH OPIS .TP \fB\-u\fR, \fB\-\-user\fR=\fINAZWA\fR korzysta z profilu o podanej nazwie .TP \fB\-t\fR, \fB\-\-theme\fR=\fIPLIK\fR aduje opis wygldu z podanego pliku .TP \fB\-n\fR, \fB\-\-no\-auto\fR nie czy si automatycznie z serwerem .TP \fB\-m\fR, \fB\-\-no\-mouse\fR nie aduje obsugi myszki .TP \fB\-N\fR, \fB\-\-no\-global\-config\fR ignoruje globalny plik konfiguracyjny .TP \fB\-a\fR, \fB\-\-away\fR[=\fIOPIS\fR] zmienia stan na ,,zajty'' .TP \fB\-b\fR, \fB\-\-back\fR[=\fIOPIS\fR] zmienia stan na ,,dostpny'' .TP \fB\-i\fR, \fB\-\-invisible\fR[=\fIOPIS\fR] zmienia stan na ,,niewidoczny'' .TP \fB\-d\fR, \fB\-\-dnd\fR[=\fIOPIS\fR] zmienia stan na ,,nie przeszkadza'' .HP \fB\-f\fR, \fB\-\-free\-for\-chat\fR[=\fIOPIS\fR] zmienia stan na ,,chtny do rozmowy'' .TP \fB\-x\fR, \fB\-\-xa\fR[=\fIOPIS\fR] zmienia stan na ,,bardzo zajty'' .TP \fB\-h\fR, \fB\-\-help\fR wywietla ten tekst pomocy .TP \fB\-v\fR, \fB\-\-version\fR wywietla wersje programu i wychodzi .PP Opcje dotyczce stanu zale od waciwoci protokou danej sesji \fB\-\-\fR niektre sesje mog nie obsugiwa stanu ,,nie przeszkadza'' itp. .SH "ZOBACZ TAKE" Pena dokumentacja dla programu .B ekg2 jest utrzymywana jako podrcznik Docbook. Jest ona dostpna pod adresem .B http://ekg2.org/ekg2book/ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/docs/ekg2book-en/�����������������������������������������������������������0000775�0000000�0000000�00000000000�11751427534�0017210�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/docs/ekg2book-en/Makefile���������������������������������������������������0000664�0000000�0000000�00000000327�11751427534�0020652�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������XSLTRANSFORMER="xsltproc" all: rm -rf book ./generate.sh $(XSLTRANSFORMER) -stringparam chunker.output.encoding ISO-8859-2 sheet.xsl book.xml mkdir book mv *.html book/ clean distclean: rm -rf book book.xml ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/docs/ekg2book-en/bookinfo.xml�����������������������������������������������0000664�0000000�0000000�00000001331�11751427534�0021536�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ <bookinfo> <author> <firstname>Leszek</firstname> <surname>Krupinski</surname> <email>leszek@wafel.com</email> </author> <author> <firstname>Sebastian</firstname> <surname>Szary</surname> <email>greyer@killer.radom.net</email> </author> <author> <firstname>Adam</firstname> <surname>Kuczyski</surname> <email>dredzik@ekg2.org</email> </author> <author> <firstname>Piotr</firstname> <surname>Kupisiewicz</surname> <email>deletek@ekg2.org</email> </author> <copyright> <year>2004, 2005</year> <holder>Grupa rozwijajca ekg2</holder> </copyright> </bookinfo> �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/docs/ekg2book-en/design/����������������������������������������������������0000775�0000000�0000000�00000000000�11751427534�0020461�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/docs/ekg2book-en/design/commands_footer.xml���������������������������������0000664�0000000�0000000�00000000014�11751427534�0024355�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ </chapter> ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ekg2-0.4~pre+20120506.1/docs/ekg2book-en/design/commands_header.xml���������������������������������0000664�0000000�0000000�00000000224�11751427534�0024312�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������ <chapter> <title>Polecenia In this chapter internal commands ekg2 and these shared by plugins are described. ekg2-0.4~pre+20120506.1/docs/ekg2book-en/design/footer.xml000066400000000000000000000000101175142753400224700ustar00rootroot00000000000000 ekg2-0.4~pre+20120506.1/docs/ekg2book-en/design/header.xml000066400000000000000000000003341175142753400224330ustar00rootroot00000000000000 The ekg2 Book ekg2-0.4~pre+20120506.1/docs/ekg2book-en/design/plugin_footer.xml000066400000000000000000000000111175142753400240470ustar00rootroot00000000000000 ekg2-0.4~pre+20120506.1/docs/ekg2book-en/design/plugin_header.xml000066400000000000000000000001071175142753400240070ustar00rootroot00000000000000 Plugin: PLUGIN ekg2-0.4~pre+20120506.1/docs/ekg2book-en/design/plugins_footer.xml000066400000000000000000000000151175142753400242360ustar00rootroot00000000000000 ekg2-0.4~pre+20120506.1/docs/ekg2book-en/design/plugins_header.xml000066400000000000000000000001031175142753400241660ustar00rootroot00000000000000 Pluginy ekg2-0.4~pre+20120506.1/docs/ekg2book-en/design/vars_footer.xml000066400000000000000000000000141175142753400235270ustar00rootroot00000000000000 ekg2-0.4~pre+20120506.1/docs/ekg2book-en/design/vars_header.xml000066400000000000000000000004621175142753400234700ustar00rootroot00000000000000 Configuration options Below you have list of configuration options, which can be set using command /set. Plugins commands should be written with plugins name prefix, for instance /set gg:dcc 1> ekg2-0.4~pre+20120506.1/docs/ekg2book-en/develbook.xml000066400000000000000000000661051175142753400217140ustar00rootroot00000000000000 EKG2 developer handbook EKG2 developing Tips for coders If you are fixing/adding something, keep style from rest of the code. Make indentations by tab characters, not space. Make spaces after commas or semicolons. If you have other style, but your patches will be good and/or nessesary don't be suprise if Your code will be changed. Add yourself to copyrights at the beginning of the file. If you don't do it I found that you relinquish your code. Of course sended code must be on the same licence as the rest of the code. If not don't waste your time and don't send it to us. Keep conventions. If all structure's variables have names like foobar_count, don't add foocount, or nfoo. Don't change api without reason. Even if you want to do it, consult with other developers. If you are making some bigger part of the code/module/file designed to some function, try to call functions and vars in way that can be understandable by other developers. Example: function connected with list are beginning with list_*, userlist functions userlist_*, configuration is something like config_*. For memory allocation use xmalloc(), xcalloc(), xrealloc() and xstrdup(). You don't have to check returned value. If there will be no memory, these functions will handle soft program close. Look after freeing buffors, if they are not needed anymore. Instead of strncpy() and strncat() use strlcpy() and strlcat(), which takes as a parametr full buffer size. You don't have to worry about how much memory is left, if zero character will go, etc. Both functions always return number of characters, which were saved to the buffer. If you don't know anything about memory allocation and string support in C, better find something else to do. Even if code is correct, but it is disordered it will be refused. Podsyajc patche, twrz je poleceniem diff -u. diff bez parametrw generuje patche, ktre nie zawieraj adnego kontekstu i ciko je doczy do rda, gdy zmienia si wczeniej chocia jedna linijka. Patche najlepiej generowa wzgldem najwieszej wersji, ale nie jest to wymagane, jeli w midzyczasie nie byo powanych zmian w kodzie. Jeli poprawka jest niewielka (jedna, dwie linijki), nie tra swojego, ani mojego czasu, tylko po prostu wklej orygina i poprawion wersj do treci listu. Ponisze linijki dopisane do ~/.vimrc uatwiaj wychwytywanie znakw niedrukowalnych pozostawionych na kocach linii poprzez zmian ich koloru: Wszelkie komentarze powinny by w jzyku angielskim. Wszystkie formatki i wszystko co zostaje wywietlane na ekranie ma by w jzyku angielskim. Po zmianie jakiejkolwiek formatki naley w katalogu po/ wywoa make update-po i ewentualnie poprawi pliki *.po Everyone can send patches, if they are respecting conventions. It is about saving our project from disorder after one month. Of course every change will be checked and if there is something wrong author will be rebuked. API API documentation is avaiable on project homepage: http://www.ekg2.org/en/doxygen/ Formaty plikw Format listy kontaktw ekg2 Lista kontaktw z pliku userlist jest tego samego formatu co eksportowana z windowsowego klienta: ekg moe dopisa uytkownika do kilku metagrup. __blocked - uytkownik blokowany __offline - jestemy niedostpni dla uytkownika __ignored_[poziom] - uytkownik jest ignorowany z danym poziomem __ignored - wszystkie zdarzenia zwizane z uytkownikiem s ignorowane Format konfiguracji ekg2 Plik konfiguracyjny config zawiera kolejne wpisy w postaci: bind bind-set alias on at [nazwa] / timer [nazwa] [*/] plugin ]]> Jeli pierwszy wyraz nie jest jednym ze znanych, jest traktowany jako nazwa zmiennej, po ktrej wystpuje jej warto - chodzi o zachowanie kompatybilnoci z innymi klientami. Plik konfiguracyjny config-plugin zawiera kolejne wpisy w postaci: ]]> Plik konfiguracyjny ,,plugins'' zawiera kolejny wpisy w postaci: ]]> Jeli warto zmiennej zaczyna si od znaku o kodzie 1, to nastpujca po nim warto zmiennej jest zakodowana uywajc base64. Format emotikon Za pomoc oryginalnego klienta GG mona wysa sekwencje znakowe, ktre bd si zamienia na odpowiednie ikony. Z oczywistych wzgldw w ekg wykona si tego nie da (moe kto kiedy zrobi wsparcie dla aaliba ;))). Sekwencje te, zamieniane s wic na inne sekwencje. Np. sekwencja <cmok>moe by zamieniona na :-* lub podobn, wedle uznania uytkownika. Aby skorzysta z moliwoci rozwijania tego typu makr w otrzymywanych wiadomociach, naley je sobie wczeniej zdefiniowa. Robi si to w pliku ~/.gg/emoticons. Kada linia tego pliku definiuje jedno makro, a jej format jest Nastpujcy: ]]> Naley zwrci uwag na to, e do oddzielenia emoticonu od jego rozwinicia su tylko i wycznie tabulatory (jeden lub wicej). Spowodowane jest to wystpowaniem spacji w oryginalnych GG-owych emoticonach. Przykadowy zestaw definicji mona znale w docs/emoticons.sample. Nie zawiera on wszystkich definicji, bo nie miaem na tyle inwencji, eby je powymyla. Format kolejki wiadomoci Wiadomoci s zapisywane w osobnych plikach w katalogu ~/.gg/queue. Nazwa pliku jest poczeniem czasu wysania wiadomoci, kropki i kolejnej liczby, ale nie ma ona wikszego znaczenia. Chodzi o zachowanie kolejnoci przy sortowaniu plikw. Pierwsza linia pliku zawiera wersj pliku. Aktualnie jest to cig znakw v1. Druga linia zawiera uid sesji, z ktrej wiadomo bya wysyana. Trzecia linia zawiera list odbiorcw. Czwarta linia zawiera czas wysania jako liczb sekund od 1 stycznia 1970r. Pita linia zawiera numer sekwencyjny lub dowolny cig znakw pozwalajcy jednoznacznie okreli wiadomo. Kolejne linie s treci wiadomoci. Format pliku konfiguracji sesji ekg2 Plik konfiguracyjny sessions-<plugin> moe zawiera nastpujce definicje: Debugowanie Jeli program si wywrci i utworzy plik core, za pomoc gdb mona sprawdzi, w ktrym miejscu wystpi bd. Najpierw uruchamiamy gdb. Od razu widzimy, e bd wystpi w funkcji command_test_segv z pliku commands.c. Potem widzimy bdn lini. W niektrych przypadkach niestety to nie wystarcza. Moliwe, e linia, w ktrej wystpi bd jest poprawna, ale dostarczone dane nie byy waciwe. Wtedy z pomoc przychodzi polecenie bt, ktre wywietla stos wywoa funkcji. Widzimy dziki temu po kolei, jaka funkcja wywoywaa jak funkcj i z jakimi parametrami. Teraz widzimy, e po prostu uytkownik wywoa komend _segv. co prawda wida to po samej nazwie funkcji, w ktrej wystpi bd, ale w wikszoci przypadkw nie bdzie to a tak oczywiste. Niestety zdarza si i tak, e bdny kod narusza wane obszary pamici, a sam bd wystpuje pniej, chociaby przy wywoywaniu funkcji alokacji pamici. W takich przypadkach zlokalizowanie bdu jest znacznie trudniejsze. Tworzenie tematw graficznych Stworzenie nowego tematu Tworzenie wasnego tematu graficznego do ekg2 najlepiej rozpoczc od wygenerowania "czystego" pliku z tematem, zawierajcego ustawienia domylne. Do wygenerowania takiego pliku mona uy prostego skryptu powoki, na przykad takiego jak poniszy, ktry naley uruchomi w katalogu ze rdami ekg2. my.theme ]]> Majc wygenerowany taki plik mona przystpi do modyfikacji. Wszystkie zmienione formatki naley zapisa, niezmienione mona usun lub pozostawi. Nastpnie naley plik z tematem umieci we waciwym katalogu - podkatalogu themes katalogu z danymi ekg2 (zazwyczaj /usr/share/ekg2 lub /usr/local/share/ekg2) lub w katalogu z konfiguracj ekg2 (~/.ekg2 lub ~/etc/ekg2). Stworzony temat mona przetestowa ustawiajc zmienn theme, nadajc jej warto tak jak nazwa pliku z tematem bez kocwki .theme. Formatki Kolory w tematach graficznych Kolor Zwyky Jasny To czarny/szary %k %K %l niebieski %b %B %e czerwony %r %R %s fioletowy %m/%p %M/%P %q turkusowy %c %C %d brzowy/ty %y %Y %z zielony %g %G %h biay %w %W %x mrugajcy %i - - tusty %T - - bez koloru %n
Dopenianie do szerokoci Przy parametrach %1-%9 mona kaza dopenia do konkretnej szerokoci. przydaje si do wszelkiego rodzaju tabelek. Dopenianie do szerokoci %[10]1 dopenia spacjami z prawej pierwszy parametr do 10 pl %[-10]1 j.w. tylko e do lewej %[.5]1 dopenia zerami %[,9]2 dopenia kropkami %[_4]1 dopenia znakami podkrelenia %(10)1 jeli rozmiar parametru przekroczy 10 znakw nie obcina
UWAGA! kolorkowe sekwencje ansi traktuje jak znaki, wic nie powinno si ich uywa przy dopenianiu parametrw.
Kocwki zalene od pci Jeli chce si rozrnia przymiotniki dla rnych pci, mona uy %@n, gdzie ,,n'' to numer formatu, ktry bierzemy pod uwag. jeli ostatni liter bdzie ,,a'', %@n zostanie zastpione przez ,,a'', w innym przypadku przez ,,y''. przykad: %> %1 jest dostpn%@1 Naley wzi uwag, e w wielu wypadkach pseudonimy s najpierw formatowane przez known_user i unknown_user, wic trzeba poda osobny parametr z samym pseudonimem. Znaki zachty Znaki zachty %> prompt (domylnie zielony) %! error (domylnie czerwony) %) prompt2 (domylnie turkusowy) %# timestamp (domylnie GG:MM)
%| oznacza koniec promptu. Jeli ten format wystpuje, to przy przenoszeniu do nastpnej linii, tekst przed tym formatem zostanie wywietlony ponownie. Uycie formatu %| Duga linia, ktra zostanie podzielona na kilka linii ]]> Na maym terminalu powyszy przykad wywietli: Po dodaniu %|, to znaczy: %|Duga linia, ktra zostanie podzielona na kilka linii ]]> zostanie wywietlone jako:
Uytkownicy spoza listy kontaktw Dwa specjalne formaty ,,known_user'' i ,,unknown_user'' okrelaj, jak bd pokazywani userzy z listy i spoza listy kontaktw. Pierwszy za parametry przyjmuje %1 opis, %2 numerek, a drugi %1 numerek. Prompty readline Wpisy readline_*, oprcz readline_prompt_query nie mog zawiera adnych ,,procentowych'' kodw sterujcych. Podobnie jest z promptem config_changed. Marginesy (ncurses) W ekg2 mona ustawia tzw. marginesy poprzez zmienn ncurses:margin_size. Aby z nich korzysta naley w formatce uy znakw /| do oddzielenia tekstu znajdujcego si po prawej i po lewej stronie. Przykad uycia marginesw Powysza formatka zostanie wywietlona jako: Odstp reguluje si poprzez zmienn ncurses:margin_size.
Tworzenie pluginw Struktura plugin_t Obsuga zdarze Wywoywanie polece ekg2 Wywietlanie danych
ekg2-0.4~pre+20120506.1/docs/ekg2book-en/faq.xml000066400000000000000000000304151175142753400205040ustar00rootroot00000000000000 Frequently Asked Questions Installation Where can I find EKG2 sources ? You should visit /http://ekg2.org. There is some Files section and you can download from there everyday snapshots. You can also use GIT, which makes you able to have current version of program (for instance. if somebody fixed some segvault problem with EKG2 10 minutes ago). Way of using GIT is described on webpage. I have EKG2 from GIT, but when I'm doing ./autogen some errors about missing libtoolize, libttol, aclocal, autoheader, automake, autoconf, autom4te appear. What should I do ? First of all check if you have installed newest versions of those tools (maybe you should browse /usr/ports/devel ). If you have installed everything it means that EKG2 can't find them. It is caused by missing simlinks connected with those tools. Usually they are in /usr/local/bin and I suggest to make some symlinks like ln -s libtoolize19 libtoolize and do this with every tool. I suggest to do it always with autotools. In BSD system when I'm executing ./configure libgadu or/and expat is disabled. What should I do ? FreeBSD has other localization of library files and sometimes it is hard to find them. It is recommended to add arguments --with-libgadu=/usr/local or/and --with-expat=/usr/local. Of course you have to have them installed. libgadu can be found on http://dev.null.pl/ekg/ (libgadu should be compiled with --enable-shared parametr). expat can be found in official ports tree or in system sources (in newest versions like FreeBSD 5.3.x or newer). In BSD system when I'm trying to compile program some errors about libintl.h from gettext.h apper. gettext is installed in system. What should I do ? Unfortunatly it is caused by diffrent localization of libraries (diffrent then in Linux systems). The easier solution is to add to ./autogen.sh or ./configure option --with-libintl-prefix=/usr/local/include Usage Where are some variables like proxy, server etc ? Variables like this ones are specific for every protocol and they are in session variables. How can I remove variable ? set -variable EKG2 doesn't show my nickname when I'm sending messages. What should I do ? You should add alias variable to session. Po zmianie wielkoci terminala przestaj mi dziaa strzaki! Masz star wersj biblioteki ncurses. Zainstaluj nowsz wersj lub popro o to administratora systemu. Jak wywietla wiadomoci, ktre wysyam? Naley ustawi warto zmiennej display_sent na 1. Nie mam kolorw w ekg, co robi? Najprawdopodobniej masz ustawiony nieprawidowy typ terminala. Sprbuj wpisa export TERM=ansi (lub setenv TERM ansi dla powoki *csh) przed uruchomieniem ekg. Jeli to nie pomoe, zamiast ansi sprbuj poda screen, linux, xterm-color lub color_xterm. Jak doda kogo do listy kontaktw w GG? /add gg:1234567 Pseudonim Przy dodawaniu kogo do listy kontaktw w GG dostaj bd invalid id, co zrobi? Upewnij si, e plugin gg jest zaadowany. Polecenie /plugin +gg powinno pomc. Po wydaniu polecenia /plugin +gg program mwi, e nie moe odnale pluginu Upewnij si, e plugin gg jest skompilowany (w katalogu ${prefix}/lib/ekg2/plugins/ powinny znajdowa si pliki gg.so i gg.la) oraz, e ekg2 zostao skompilowane z obsug libgadu (w jednej z ostatnich linijek wywietlonych przez polecenie ./configure, powinno si wywietli libgadu: enabled) Mam libgadu a mimo to ./configure wywietla libgadu: disabled Upewnij si, e ./configure moe odnale pliki nagwkowe biblioteki libgadu (m. in. libgadu.h oraz bibliotek libgadu.so.3: katalog w ktrym znajduje si libgadu.so.3 powinien by dopisany do pliku /etc/ld.so.conf; jeli go tam nie ma, to naley go dopisa i uruchomi program ldconfig jako root jeeli to nie pomaga, a libgadu.so.3 znajduje si w niestandardowym miejscu, mona sprbowa zrobi symboliczny link do tego pliku w /usr/lib Mam libgadu, plugin gg jest skompilowany, mimo to przy prbie zaadowania pluginu ekg2 wywietla, e nie moe go odnale Upewnij si, e ekg2 moe odnale bibliotek libgadu.so.3 - odpowiednie kroki zostay przedstawione w pytaniu powyej. W moim systemie nie ma pliku libgadu.so.3 Pobierz najnowsze libgadu, ktre mona znale pod adresem http://dev.null.pl/ekg/libgadu-current.tar.gz , rozpakuj, skonfiguruj poleceniem ./configure --enable-shared, skompiluj poleceniem make i jako root zainstaluj poleceniem make install Jak ustawi tryb prywatny dla sesji Gadu-Gadu? Wystarczy ustawi zmienn sesyjn private na 1, na przykad /session gg:997997 private 1 ekg2-0.4~pre+20120506.1/docs/ekg2book-en/generate.sh000077500000000000000000000024111175142753400213370ustar00rootroot00000000000000#!/bin/sh echo echo "WARNING: this script (and neighbouring Makefile) is deprecated. Please use ../generate-ekg2book.sh" >&2 echo sleep 5 PREINPUTS="design/header.xml bookinfo.xml userbook.xml" POSTINPUTS="develbook.xml faq.xml design/footer.xml" OUTPUT="book.xml" GENPROG="./txt2docbook.py" set -e cat /dev/null > $OUTPUT for input in $PREINPUTS do cat $input >> $OUTPUT done # pluginy cat "design/plugins_header.xml" >> $OUTPUT for i in ../../plugins/* do if [ -f $i ] then continue fi if [ -f $i/doc.xml -o -f $i/commands-en.txt -o -f $i/vars-en.txt ] then cat "design/plugin_header.xml" | sed -e s/PLUGIN/`basename $i`/ >> $OUTPUT if [ -f $i/doc.xml ] then cat $i/doc.xml >> $OUTPUT fi if [ -f $i/commands-en.txt ] then $GENPROG -c $i/commands-en.txt >> $OUTPUT fi if [ -f $i/vars-en.txt ] then $GENPROG -v $i/vars-en.txt >> $OUTPUT fi if [ -f $i/session-en.txt ] then $GENPROG -s ../session-en.txt >> $OUTPUT $GENPROG -s $i/session-en.txt >> $OUTPUT fi cat "design/plugin_footer.xml" >> $OUTPUT fi done cat "design/plugins_footer.xml" >> $OUTPUT for input in $POSTINPUTS do cat $input >> $OUTPUT done ekg2-0.4~pre+20120506.1/docs/ekg2book-en/sheet.xsl000066400000000000000000000010431175142753400210460ustar00rootroot00000000000000 ekg2-0.4~pre+20120506.1/docs/ekg2book-en/txt2docbook.py000077500000000000000000000156701175142753400220400ustar00rootroot00000000000000#!/usr/bin/python # -*- encoding: utf-8 -*- import re import sys import getopt msg_session_vars = "Session Variables" msg_vars = "Variables" msg_commands = "Commands" str_type = "type" msg_type = "Type" str_def_val = "default value" msg_def_val = "Default value" str_params = "parameters" msg_params = "Parameters" str_short_desc = "short description" msg_short_desc = "Short Description" debug = True def usage(): """Display usage message""" sys.stdout = sys.stderr print "Usage: "+sys.argv[0]+" [-c|-v|-s] " print "\t-c\tparse command file (default)" print "\t-v\tparse vars file" print "\t-s\tparse session vars file" def debug(str): """Write message to strerr, if debugging is enabled.""" if debug: sys.stderr.write("[debug] {"+str+"}\n") def warn(str): """Write message to stderr.""" sys.stderr.write("[warn] {"+str+"}\n") def die(str): """Write message to stderr and exit with an error.""" sys.stderr.write("[FATAL] {"+str+"}\n") sys.exit(1) def strip_indent_amount(line): """Return a number after which char to cut, to get a visual 8-char unindent""" if len(line) > 0 and line[0] == '\t': return 1 if len(line) > 7 and line[0:8] == ' ': return 8 if len(line) > 1 and line[0:1] == ' ' and line[1] == '\t': return 2 if len(line) > 2 and line[0:2] == ' ' and line[2] == '\t': return 3 if len(line) > 3 and line[0:3] == ' ' and line[3] == '\t': return 4 if len(line) > 4 and line[0:4] == ' ' and line[4] == '\t': return 5 if len(line) > 5 and line[0:5] == ' ' and line[5] == '\t': return 6 if len(line) > 6 and line[0:6] == ' ' and line[6] == '\t': return 7 if len(line) > 7 and line[0:7] == ' ' and line[7] == '\t': return 8 return 0 def is_indented(line): """Whether it's properly indented""" if strip_indent_amount(line) == 0: return False else: return True def strip_indent(fname, linenum, line): """Unindent the line by 8 visual chars, or raise an exception.""" ret = strip_indent_amount(line) if ret == 0: raise Exception('Invalid indent %s:%d' % (fname, linenum)) elif ret >= 2 and ret < 8: warn('Unclean indent %s:%d' % (fname, linenum)) elif ret == 8 and line[7] == '\t': warn('Unclean indent %s:%d' % (fname, linenum)) return line[ret:] def parse_header(fname, linenum, vars, session, commands, line): """Parse an undindented header, returning an XML snippet""" if line.find(':') < 0: raise Exception('Header expected (%s:%d)' % (fname, linenum)) debug('header on line %d: %s' % (linenum, line)) data = line.split(':') # header name if vars or session: if data[0] == str_type: title = msg_type elif data[0] == str_def_val: title = msg_def_val else: raise Exception("Unknown header [%s] (%s:%d)" % (data[0], fname, linenum)) elif commands: if data[0] == str_params: title = msg_params elif data[0] == str_short_desc: title = msg_short_desc else: raise Exception("Unknown header [%s] (%s:%d)" % (data[0], fname, linenum)) para = data[1].replace('&', '&') para = para.replace('<', '<') para = para.replace('>', '>') return "%s%s\n" % (title, para) def print_entry(record): """Print an XML snippet of the supplied record.""" print """ %(term)s %(header)s """ % record def main(): if len(sys.argv) < 2: usage() sys.exit(1) try: opts, args = getopt.getopt(sys.argv[1:], "cvs") except getopt.GetoptError: usage() sys.exit(1) vars = None commands = True session = True for o, a in opts: if o == "-v": vars = True commands = None session = None if o == "-c": vars = None commands = True session = None if o == "-s": vars = None commands = None session = True fname = args[0] try: file = open(fname, "r") except Exception, e: sys.stdout = sys.stderr print "Error: can't open file for reading" print str(e) sys.exit(1) debug('Reading file %s' % (fname)) # begin output output = ""; if session: output += msg_session_vars if vars: output += msg_vars elif commands: output += msg_commands output += "\n\n" print(output) record = None state_was = 'top_level' linenum = 0 r = re.compile("%T(.+)%n") line = file.readline() while line: linenum += 1 line = line[:-1] line = r.sub("\\1", line) if state_was == 'top_level': if line[0:2] == "//" or line == "": # still top level state_was = 'top_level' elif len(line) > 0 and (line[0] == ' ' or line[0] == "\t"): raise Exception('Unexpected input on top level (%s:%d)' % (fname, linenum)) else: debug('entry start on line %d: %s' % (linenum, line)) state_was = 'entry_start' record = {'term': line, 'desc' : '', 'header': ''} elif state_was == 'entry_start': # this must be a header if is_indented(line): line = strip_indent(fname, linenum, line) else: raise Exception('Header expected (%s:%d)' % (fname, linenum)) record['header'] += parse_header(fname, linenum, vars, session, commands, line) state_was = 'header' elif state_was == 'header': if line == '': debug('entry ended on line %d' % (linenum - 1)) state_was = 'top_level' if record: print_entry(record) record = {'term': line, 'desc' : '', 'header': ''} # so it doesn't match later line = None elif is_indented(line): line = strip_indent(fname, linenum, line) else: raise Exception('Header, separator, or empty line expected (%s:%d)' % (fname, linenum)) if line == None: pass elif line == '': # separator debug('entry headers ended on line %d' % (linenum - 1)) state_was = 'entry_contents' elif line.find(':') >= 0: record['header'] += parse_header(fname, linenum, vars, session, commands, line) else: raise Exception('Unparseable header or extra whitespace in separator (%s:%d)' % (fname, linenum)) elif state_was == 'entry_contents': if line == '': state_was = 'top_level' print_entry(record) record = {'term': line, 'desc' : '', 'header': ''} elif is_indented(line): debug('entry contents on line %d' % (linenum)) record['desc'] += strip_indent(fname, linenum, line) + "\n" else: raise Exception('Expected entry contents, separator, or empty line (%s:%d)' % (fname, linenum)) else: raise Exception('Unknown state (%s:%d)' % (fname, linenum)) line = file.readline() if record: print_entry(record) print('') if __name__ == "__main__": try: main() except Exception, e: die(e.args[0]) ekg2-0.4~pre+20120506.1/docs/ekg2book-en/userbook.xml000066400000000000000000000017401175142753400215650ustar00rootroot00000000000000 Podrcznik uytkownika ekg2 Wstp rda Instalacja Tworzenie sesji Obsugiwane sieci IM Poczenie z serwerem Zmiana stanu Lista uytkownikw Zarzdzanie uytkownikami Pasek kontaktw ekg2-0.4~pre+20120506.1/docs/ekg2book/000077500000000000000000000000001175142753400166105ustar00rootroot00000000000000ekg2-0.4~pre+20120506.1/docs/ekg2book/Makefile000066400000000000000000000003271175142753400202520ustar00rootroot00000000000000XSLTRANSFORMER="xsltproc" all: rm -rf book ./generate.sh $(XSLTRANSFORMER) -stringparam chunker.output.encoding ISO-8859-2 sheet.xsl book.xml mkdir book mv *.html book/ clean distclean: rm -rf book book.xml ekg2-0.4~pre+20120506.1/docs/ekg2book/bookinfo.xml000066400000000000000000000013561175142753400211450ustar00rootroot00000000000000 Leszek Krupinski leszek@wafel.com Sebastian Szary greyer@killer.radom.net Adam Kuczyski dredzik@ekg2.org Piotr Kupisiewicz deletek@ekg2.org 2004, 2005 Grupa rozwijajca ekg2 ekg2-0.4~pre+20120506.1/docs/ekg2book/design/000077500000000000000000000000001175142753400200615ustar00rootroot00000000000000ekg2-0.4~pre+20120506.1/docs/ekg2book/design/commands_footer.xml000066400000000000000000000000141175142753400237550ustar00rootroot00000000000000
ekg2-0.4~pre+20120506.1/docs/ekg2book/design/commands_header.xml000066400000000000000000000002621175142753400237140ustar00rootroot00000000000000 Polecenia W tym rozdziale zostay opisane polecenia wbudowane ekg2 oraz te, ktre udostpniaj poszczeglne pluginy ekg2-0.4~pre+20120506.1/docs/ekg2book/design/footer.xml000066400000000000000000000000101175142753400220700ustar00rootroot00000000000000
ekg2-0.4~pre+20120506.1/docs/ekg2book/design/header.xml000066400000000000000000000003341175142753400220330ustar00rootroot00000000000000 The ekg2 Book ekg2-0.4~pre+20120506.1/docs/ekg2book/design/plugin_footer.xml000066400000000000000000000000111175142753400234470ustar00rootroot00000000000000 ekg2-0.4~pre+20120506.1/docs/ekg2book/design/plugin_header.xml000066400000000000000000000001071175142753400234070ustar00rootroot00000000000000 Plugin: PLUGIN ekg2-0.4~pre+20120506.1/docs/ekg2book/design/plugins_footer.xml000066400000000000000000000000151175142753400236360ustar00rootroot00000000000000 ekg2-0.4~pre+20120506.1/docs/ekg2book/design/plugins_header.xml000066400000000000000000000001031175142753400235660ustar00rootroot00000000000000 Pluginy ekg2-0.4~pre+20120506.1/docs/ekg2book/design/vars_footer.xml000066400000000000000000000000141175142753400231270ustar00rootroot00000000000000 ekg2-0.4~pre+20120506.1/docs/ekg2book/design/vars_header.xml000066400000000000000000000005771175142753400230770ustar00rootroot00000000000000 Opcje konfiguracji Poniej znajduje si lista opcji konfiguracji, ktre mona ustawi za pomoc polecenia /set. W przypadku opcji poszczeglnych wtyczek, naley je ustawia poprzedzajc nazw zmiennej nazw pluginu i dwukropkiem, na przykad /set gg:dcc 1 ekg2-0.4~pre+20120506.1/docs/ekg2book/develbook.xml000066400000000000000000000667541175142753400213260ustar00rootroot00000000000000 Podrcznik developera ekg2 Rozwijanie kodu ekg2 Wskazwki dla programistw Gdy co poprawiasz/dodajesz, zachowuj styl reszty kodu. Wcicia rb znakami tabulacji, a nie spacjami. Stawiaj spacje po przecinkach i rednikach. Jeli masz inny styl, a podesane poprawki bd dobre i/lub potrzebne, nie zdziw si, jeli Twj kod zostanie przeregadowany. Dopisuj si do copyrightw na pocztku pliku. Inaczej zakadam, e zrzekasz si praw do podesanych kawakw kodu. Oczywicie podsyany kod musi by na takiej samej licencji, co reszta. W innym wypadku nie tra czasu i go nie przysyaj. Zachowuj przyjt konwencj. Jeli wszystkie zmienne danej struktury maj nazwy typu foobar_count, nie dodawaj foocount, czy nfoo. Nie zmieniaj api bez powodu. Nawet jeli chcesz to zrobi, skonsultuj z reszt developerw. Tworzc wikszy kawaek/modu/plik przeznaczony do jakiej konkretnej funkcji, staraj si nazywa funkcje i zmienne tak, by byo wiadomo, do czego su. Przykad: funkcje dotyczce list zaczynaj si list_*, funkcje dotyczce userlisty to userlist_*, konfiguracja to config_*. Do alokacji pamici uywaj funkcji xmalloc(), xcalloc(), xrealloc() i xstrdup(). Nie musisz sprawdza zwracanej wartoci. Jeli zabraknie pamici, funkcje te same zajm si grzecznym zamkniciem programu. Dbaj o zwalnianie buforw, gdy nie s one ju potrzebne. Zamiast strncpy() oraz strncat() uywaj strlcpy() i strlcat(), ktre przyjmuj jako parametr cakowity rozmiar bufora. Nie trzeba si martwi o to, ile w buforze pozostao jeszcze miejsca, czy znak zerowy si zmieci, etc. Obie funkcje zawsze zwracaj ilo znakw, jaka zostaa(by) zapisana do bufora. Jeli nie masz pojcia o alokacji pamici i obsudze stringw w C, najlepiej daj sobie spokj. Nawet jeli kod dziaa, ale nie trzyma si kupy, zostanie odrzucony. Podsyajc patche, twrz je poleceniem diff -u. diff bez parametrw generuje patche, ktre nie zawieraj adnego kontekstu i ciko je doczy do rda, gdy zmienia si wczeniej chocia jedna linijka. Patche najlepiej generowa wzgldem najwieszej wersji, ale nie jest to wymagane, jeli w midzyczasie nie byo powanych zmian w kodzie. Jeli poprawka jest niewielka (jedna, dwie linijki), nie tra swojego, ani mojego czasu, tylko po prostu wklej orygina i poprawion wersj do treci listu. Ponisze linijki dopisane do ~/.vimrc uatwiaj wychwytywanie znakw niedrukowalnych pozostawionych na kocach linii poprzez zmian ich koloru: Wszelkie komentarze powinny by w jzyku angielskim. Wszystkie formatki i wszystko co zostaje wywietlane na ekranie ma by w jzyku angielskim. Po zmianie jakiejkolwiek formatki naley w katalogu po/ wywoa make update-po i ewentualnie poprawi pliki *.po Kady moe podsyac patche, byle byy zachowane pewne zasady i nie byo po miesicu baaganu w kodzie. Oczywicie wszystkie zmiany bd przegldane i w razie czego autor dostanie po apach. API Dokumentacja API dostpna jest na stronie domowej projektu: http://www.ekg2.org/doxygen/ Formaty plikw Format listy kontaktw ekg2 Lista kontaktw z pliku userlist jest tego samego formatu co eksportowana z windowsowego klienta: ekg moe dopisa uytkownika do kilku metagrup. __blocked - uytkownik blokowany __offline - jestemy niedostpni dla uytkownika __ignored_[poziom] - uytkownik jest ignorowany z danym poziomem __ignored - wszystkie zdarzenia zwizane z uytkownikiem s ignorowane Format konfiguracji ekg2 Plik konfiguracyjny config zawiera kolejne wpisy w postaci: bind bind-set alias on at [nazwa] / timer [nazwa] [*/] plugin ]]> Jeli pierwszy wyraz nie jest jednym ze znanych, jest traktowany jako nazwa zmiennej, po ktrej wystpuje jej warto - chodzi o zachowanie kompatybilnoci z innymi klientami. Plik konfiguracyjny config-plugin zawiera kolejne wpisy w postaci: ]]> Plik konfiguracyjny ,,plugins'' zawiera kolejny wpisy w postaci: ]]> Jeli warto zmiennej zaczyna si od znaku o kodzie 1, to nastpujca po nim warto zmiennej jest zakodowana uywajc base64. Format emotikon Za pomoc oryginalnego klienta GG mona wysa sekwencje znakowe, ktre bd si zamienia na odpowiednie ikony. Z oczywistych wzgldw w ekg wykona si tego nie da (moe kto kiedy zrobi wsparcie dla aaliba ;))). Sekwencje te, zamieniane s wic na inne sekwencje. Np. sekwencja <cmok>moe by zamieniona na :-* lub podobn, wedle uznania uytkownika. Aby skorzysta z moliwoci rozwijania tego typu makr w otrzymywanych wiadomociach, naley je sobie wczeniej zdefiniowa. Robi si to w pliku ~/.gg/emoticons. Kada linia tego pliku definiuje jedno makro, a jej format jest Nastpujcy: ]]> Naley zwrci uwag na to, e do oddzielenia emoticonu od jego rozwinicia su tylko i wycznie tabulatory (jeden lub wicej). Spowodowane jest to wystpowaniem spacji w oryginalnych GG-owych emoticonach. Przykadowy zestaw definicji mona znale w docs/emoticons.sample. Nie zawiera on wszystkich definicji, bo nie miaem na tyle inwencji, eby je powymyla. Format kolejki wiadomoci Wiadomoci s zapisywane w osobnych plikach w katalogu ~/.gg/queue. Nazwa pliku jest poczeniem czasu wysania wiadomoci, kropki i kolejnej liczby, ale nie ma ona wikszego znaczenia. Chodzi o zachowanie kolejnoci przy sortowaniu plikw. Pierwsza linia pliku zawiera wersj pliku. Aktualnie jest to cig znakw v1. Druga linia zawiera uid sesji, z ktrej wiadomo bya wysyana. Trzecia linia zawiera list odbiorcw. Czwarta linia zawiera czas wysania jako liczb sekund od 1 stycznia 1970r. Pita linia zawiera numer sekwencyjny lub dowolny cig znakw pozwalajcy jednoznacznie okreli wiadomo. Kolejne linie s treci wiadomoci. Format pliku konfiguracji sesji ekg2 Plik konfiguracyjny sessions-<plugin> moe zawiera nastpujce definicje: Debugowanie Jeli program si wywrci i utworzy plik core, za pomoc gdb mona sprawdzi, w ktrym miejscu wystpi bd. Najpierw uruchamiamy gdb. Od razu widzimy, e bd wystpi w funkcji command_test_segv z pliku commands.c. Potem widzimy bdn lini. W niektrych przypadkach niestety to nie wystarcza. Moliwe, e linia, w ktrej wystpi bd jest poprawna, ale dostarczone dane nie byy waciwe. Wtedy z pomoc przychodzi polecenie bt, ktre wywietla stos wywoa funkcji. Widzimy dziki temu po kolei, jaka funkcja wywoywaa jak funkcj i z jakimi parametrami. Teraz widzimy, e po prostu uytkownik wywoa komend _segv. co prawda wida to po samej nazwie funkcji, w ktrej wystpi bd, ale w wikszoci przypadkw nie bdzie to a tak oczywiste. Niestety zdarza si i tak, e bdny kod narusza wane obszary pamici, a sam bd wystpuje pniej, chociaby przy wywoywaniu funkcji alokacji pamici. W takich przypadkach zlokalizowanie bdu jest znacznie trudniejsze. Tworzenie tematw graficznych Stworzenie nowego tematu Tworzenie wasnego tematu graficznego do ekg2 najlepiej rozpoczc od wygenerowania "czystego" pliku z tematem, zawierajcego ustawienia domylne. Do wygenerowania takiego pliku mona uy prostego skryptu powoki, na przykad takiego jak poniszy, ktry naley uruchomi w katalogu ze rdami ekg2. my.theme ]]> Majc wygenerowany taki plik mona przystpi do modyfikacji. Wszystkie zmienione formatki naley zapisa, niezmienione mona usun lub pozostawi. Nastpnie naley plik z tematem umieci we waciwym katalogu - podkatalogu themes katalogu z danymi ekg2 (zazwyczaj /usr/share/ekg2 lub /usr/local/share/ekg2) lub w katalogu z konfiguracj ekg2 (~/.ekg2 lub ~/etc/ekg2). Stworzony temat mona przetestowa ustawiajc zmienn theme, nadajc jej warto tak jak nazwa pliku z tematem bez kocwki .theme. Formatki Kolory w tematach graficznych Kolor Zwyky Jasny To czarny/szary %k %K %l niebieski %b %B %e czerwony %r %R %s fioletowy %m/%p %M/%P %q turkusowy %c %C %d brzowy/ty %y %Y %z zielony %g %G %h biay %w %W %x mrugajcy %i - - tusty %T - - bez koloru %n
Dopenianie do szerokoci Przy parametrach %1-%9 mona kaza dopenia do konkretnej szerokoci. przydaje si do wszelkiego rodzaju tabelek. Dopenianie do szerokoci %[10]1 dopenia spacjami z prawej pierwszy parametr do 10 pl %[-10]1 j.w. tylko e do lewej %[.5]1 dopenia zerami %[,9]2 dopenia kropkami %[_4]1 dopenia znakami podkrelenia %(10)1 jeli rozmiar parametru przekroczy 10 znakw nie obcina
UWAGA! kolorkowe sekwencje ansi traktuje jak znaki, wic nie powinno si ich uywa przy dopenianiu parametrw.
Kocwki zalene od pci Jeli chce si rozrnia przymiotniki dla rnych pci, mona uy %@n, gdzie ,,n'' to numer formatu, ktry bierzemy pod uwag. jeli ostatni liter bdzie ,,a'', %@n zostanie zastpione przez ,,a'', w innym przypadku przez ,,y''. przykad: %> %1 jest dostpn%@1 Naley wzi uwag, e w wielu wypadkach pseudonimy s najpierw formatowane przez known_user i unknown_user, wic trzeba poda osobny parametr z samym pseudonimem. Znaki zachty Znaki zachty %> prompt (domylnie zielony) %! error (domylnie czerwony) %) prompt2 (domylnie turkusowy) %# timestamp (domylnie GG:MM)
%| oznacza koniec promptu. Jeli ten format wystpuje, to przy przenoszeniu do nastpnej linii, tekst przed tym formatem zostanie wywietlony ponownie. Uycie formatu %| Duga linia, ktra zostanie podzielona na kilka linii ]]> Na maym terminalu powyszy przykad wywietli: Po dodaniu %|, to znaczy: %|Duga linia, ktra zostanie podzielona na kilka linii ]]> zostanie wywietlone jako:
Uytkownicy spoza listy kontaktw Dwa specjalne formaty ,,known_user'' i ,,unknown_user'' okrelaj, jak bd pokazywani userzy z listy i spoza listy kontaktw. Pierwszy za parametry przyjmuje %1 opis, %2 numerek, a drugi %1 numerek. Prompty readline Wpisy readline_*, oprcz readline_prompt_query nie mog zawiera adnych ,,procentowych'' kodw sterujcych. Podobnie jest z promptem config_changed. Marginesy (ncurses) W ekg2 mona ustawia tzw. marginesy poprzez zmienn ncurses:margin_size. Aby z nich korzysta naley w formatce uy znakw /| do oddzielenia tekstu znajdujcego si po prawej i po lewej stronie. Przykad uycia marginesw Powysza formatka zostanie wywietlona jako: Odstp reguluje si poprzez zmienn ncurses:margin_size.
Tworzenie pluginw Struktura plugin_t Obsuga zdarze Wywoywanie polece ekg2 Wywietlanie danych
ekg2-0.4~pre+20120506.1/docs/ekg2book/faq.xml000066400000000000000000000360321175142753400201050ustar00rootroot00000000000000 Czsto Zadawane Pytania Instalacja Skd cign rda EKG2 ? Zapraszam na /http://ekg2.org/ do dzialu Pliki i stamtd mona ciaga codzinne snapshoty. Mona rwnie skorzysta z GIT'a ktry pozwala by na bieco z aktualizacjami (np. jak sie okae e 10 minut temu naprawiono bd powodujce Segmentation Fault naszego EKG2). Sposb postpowania jest oczywisty bo wszystko adnie zostao opisane na stronce, wic nie bd powiela. Plugin gg/irca/jabbera mi nie dziaa, co zrobi ? Po pierwsze sprawd czy komenda /plugins pokazuje odpowiedni plugin, jeli tak, to co krcisz ;> Jeli nie sprbuj /plugins +nazwawtyczki. Jeli nie podawae opcji --prefix do ./configure czy ./autogen.sh to sprawd, czy w katalogu /usr/local/lib/ekg2/plugins znajduj si pliki *.so i *.la dla odpowiedniego pluginu (np: gg.so, gg.la) Jeli nie ma, to albo odpowiednie pluginy nie zostay skompilowane, albo co ze ciekami na tym systemie jest nie tak. Moesz te sprbowa doda (jako root oczywicie) do /etc/ld.so.conf ciek: /usr/local/lib nastpnie wyda komend ldconfig. Mam wersj EKG2 z GIT, ale przy wykonywaniu ./autogen.sh pojawiaja mi si bedy o brakujcym libtoolize, libtool, aclocal, autoheader, automake, autoconf, autom4te. Co z tym zrobic? Wpierw upewnij si, e masz zainstalowane w systemie najnowsze wersje tyche narzdzi (proponuje przejrze zawarto /usr/ports/devel w ich poszukiwaniu a jeli nie umiesz instalowa z portsw to polecam man ports). Jeli pomimo tego, i masz zainstalowane te narzdzia pojawiaj Ci si bedy oznacza to, i nie widzi ich w systemie. Jest to zapewne spowodowane tym, e nie ma symlinkw odnoszcych sie do w/w narzdzi. Wszystkie one znajduj si standardowo w katalogu /usr/local/bin i proponuje porobi symlinki do tyche narzdzi. Np. ln -s libtoolize19 libtoolize i tak po kolei dla wszystkich innych narzdzi. A najlepiej, na przyszo, dla wszystkich narzdzi zakoczonych liczbami a nie majcych symlinkw do wersji bez liczb, a w szczeglnoci dla tych wszystkich auto-narzdzi. W systemie BSD, przy wywoaniu ./configure jak i ./autogen.sh pojawia mi si, i libgadu czy te expat (biblioteka jabbera) sa wyaczone (disabled). Co robi? Jako, e FreeBSD ma inn lokalizacj plikw bibliotecznych czasem, aczkolwiek nie zawsze, nie widzi odpowiednich bibliotek. Polecam wtedy dodac opcje --with-libgadu=/usr/local jak rwnie --with-expat=/usr/local. Oczywicie powysze tzreba mie w systemie skompilowane jeli ich nie posiadamy. libgadu znajdziemy na stronie http://dev.null.pl/ekg/ (libgadu powinnimy oczywicie skompilowa z opcj --enable-shared). expat znajdziemy w oficjalnym drzewie portw lub te, w najnowszych wersjach FreeBSD (FreeBSD 5.3.x), w rdach systemu. W systemie BSD przy kompilacji pokazuje mi si bd o braku libintl.h z pakietu gettext.h: gettext jest zainstalowany w systemie. Co robi? Niestety ale znowu jest to spowodowane tym, i BSD maj nieco inne pooenie bibliotek systemowych ni linuksy. Najprostszym rozwizaniem jest dopisa przy ./autogen.sh lub ./configure opcji --with-libintl-prefix=/usr/local/include Uytkowanie Gdzie podziay si niektre zmienne (proxy, server) ? Jako, e s to zmienne specyficzne dla kadego protokou umieszczone s w zmiennych sesyjnych. Jak usun zmienn? set -zmienna ekg le rozpoznaje pe! Wpisz imi do listy kontaktw. Jak zrobi, eby ekg pokazywao mj pseudonim przy wysyaniu wiadomoci? Dopisa alias do sesji. Po zmianie wielkoci terminala przestaj mi dziaa strzaki! Masz star wersj biblioteki ncurses. Zainstaluj nowsz wersj lub popro o to administratora systemu. Jak wywietla wiadomoci, ktre wysyam? Naley ustawi warto zmiennej display_sent na 1. Nie mam kolorw w ekg, co robi? Najprawdopodobniej masz ustawiony nieprawidowy typ terminala. Sprbuj wpisa export TERM=ansi (lub setenv TERM ansi dla powoki *csh) przed uruchomieniem ekg. Jeli to nie pomoe, zamiast ansi sprbuj poda screen, linux, xterm-color lub color_xterm. Jak doda kogo do listy kontaktw w GG? /add gg:1234567 Pseudonim Przy dodawaniu kogo do listy kontaktw w GG dostaj bd invalid id, co zrobi? Upewnij si, e plugin gg jest zaadowany. Polecenie /plugin +gg powinno pomc. Po wydaniu polecenia /plugin +gg program mwi, e nie moe odnale pluginu Upewnij si, e plugin gg jest skompilowany (w katalogu ${prefix}/lib/ekg2/plugins/ powinny znajdowa si pliki gg.so i gg.la) oraz, e ekg2 zostao skompilowane z obsug libgadu (w jednej z ostatnich linijek wywietlonych przez polecenie ./configure, powinno si wywietli libgadu: enabled) Mam libgadu a mimo to ./configure wywietla libgadu: disabled Upewnij si, e ./configure moe odnale pliki nagwkowe biblioteki libgadu (m. in. libgadu.h oraz bibliotek libgadu.so.3: katalog w ktrym znajduje si libgadu.so.3 powinien by dopisany do pliku /etc/ld.so.conf; jeli go tam nie ma, to naley go dopisa i uruchomi program ldconfig jako root jeeli to nie pomaga, a libgadu.so.3 znajduje si w niestandardowym miejscu, mona sprbowa zrobi symboliczny link do tego pliku w /usr/lib Mam libgadu, plugin gg jest skompilowany, mimo to przy prbie zaadowania pluginu ekg2 wywietla, e nie moe go odnale Upewnij si, e ekg2 moe odnale bibliotek libgadu.so.3 - odpowiednie kroki zostay przedstawione w pytaniu powyej. W moim systemie nie ma pliku libgadu.so.3 Pobierz najnowsze libgadu, ktre mona znale pod adresem http://dev.null.pl/ekg/libgadu-current.tar.gz , rozpakuj, skonfiguruj poleceniem ./configure --enable-shared, skompiluj poleceniem make i jako root zainstaluj poleceniem make install Jak ustawi tryb prywatny dla sesji Gadu-Gadu? Wystarczy ustawi zmienn sesyjn private na 1, na przykad /session gg:997997 private 1 ekg2-0.4~pre+20120506.1/docs/ekg2book/generate.sh000077500000000000000000000024111175142753400207370ustar00rootroot00000000000000#!/bin/sh echo echo "WARNING: this script (and neighbouring Makefile) is deprecated. Please use ../generate-ekg2book.sh" >&2 echo sleep 5 PREINPUTS="design/header.xml bookinfo.xml userbook.xml" POSTINPUTS="develbook.xml faq.xml design/footer.xml" OUTPUT="book.xml" GENPROG="./txt2docbook.py" set -e cat /dev/null > $OUTPUT for input in $PREINPUTS do cat $input >> $OUTPUT done # pluginy cat "design/plugins_header.xml" >> $OUTPUT for i in ../../plugins/* do if [ -f $i ] then continue fi if [ -f $i/doc.xml -o -f $i/commands-pl.txt -o -f $i/vars-pl.txt ] then cat "design/plugin_header.xml" | sed -e s/PLUGIN/`basename $i`/ >> $OUTPUT if [ -f $i/doc.xml ] then cat $i/doc.xml >> $OUTPUT fi if [ -f $i/commands-pl.txt ] then $GENPROG -c $i/commands-pl.txt >> $OUTPUT fi if [ -f $i/vars-pl.txt ] then $GENPROG -v $i/vars-pl.txt >> $OUTPUT fi if [ -f $i/session-pl.txt ] then $GENPROG -s ../session-pl.txt >> $OUTPUT $GENPROG -s $i/session-pl.txt >> $OUTPUT fi cat "design/plugin_footer.xml" >> $OUTPUT fi done cat "design/plugins_footer.xml" >> $OUTPUT for input in $POSTINPUTS do cat $input >> $OUTPUT done ekg2-0.4~pre+20120506.1/docs/ekg2book/sheet.xsl000066400000000000000000000011731175142753400204520ustar00rootroot00000000000000 ekg2-0.4~pre+20120506.1/docs/ekg2book/txt2docbook.py000077500000000000000000000156621175142753400214410ustar00rootroot00000000000000#!/usr/bin/python # -*- encoding: utf-8 -*- import re import sys import getopt msg_session_vars = "Zmienne sesyjne" msg_vars = "Zmienne" msg_commands = "Polecenia" str_type = "typ" msg_type = "Typ" str_def_val = "domyślna wartość" msg_def_val = "Domyślna wartość" str_params = "parametry" msg_params = "Parametry" str_short_desc = "krotki opis" msg_short_desc = "Krótki opis" debug = True def usage(): """Display usage message""" sys.stdout = sys.stderr print "Usage: "+sys.argv[0]+" [-c|-v|-s] " print "\t-c\tparse command file (default)" print "\t-v\tparse vars file" print "\t-s\tparse session vars file" def debug(str): """Write message to strerr, if debugging is enabled.""" if debug: sys.stderr.write("[debug] {"+str+"}\n") def warn(str): """Write message to stderr.""" sys.stderr.write("[warn] {"+str+"}\n") def die(str): """Write message to stderr and exit with an error.""" sys.stderr.write("[FATAL] {"+str+"}\n") sys.exit(1) def strip_indent_amount(line): """Return a number after which char to cut, to get a visual 8-char unindent""" if len(line) > 0 and line[0] == '\t': return 1 if len(line) > 7 and line[0:8] == ' ': return 8 if len(line) > 1 and line[0:1] == ' ' and line[1] == '\t': return 2 if len(line) > 2 and line[0:2] == ' ' and line[2] == '\t': return 3 if len(line) > 3 and line[0:3] == ' ' and line[3] == '\t': return 4 if len(line) > 4 and line[0:4] == ' ' and line[4] == '\t': return 5 if len(line) > 5 and line[0:5] == ' ' and line[5] == '\t': return 6 if len(line) > 6 and line[0:6] == ' ' and line[6] == '\t': return 7 if len(line) > 7 and line[0:7] == ' ' and line[7] == '\t': return 8 return 0 def is_indented(line): """Whether it's properly indented""" if strip_indent_amount(line) == 0: return False else: return True def strip_indent(fname, linenum, line): """Unindent the line by 8 visual chars, or raise an exception.""" ret = strip_indent_amount(line) if ret == 0: raise Exception('Invalid indent %s:%d' % (fname, linenum)) elif ret >= 2 and ret < 8: warn('Unclean indent %s:%d' % (fname, linenum)) elif ret == 8 and line[7] == '\t': warn('Unclean indent %s:%d' % (fname, linenum)) return line[ret:] def parse_header(fname, linenum, vars, session, commands, line): """Parse an undindented header, returning an XML snippet""" if line.find(':') < 0: raise Exception('Header expected (%s:%d)' % (fname, linenum)) debug('header on line %d: %s' % (linenum, line)) data = line.split(':') # header name if vars or session: if data[0] == str_type: title = msg_type elif data[0] == str_def_val: title = msg_def_val else: raise Exception("Unknown header [%s] (%s:%d)" % (data[0], fname, linenum)) elif commands: if data[0] == str_params: title = msg_params elif data[0] == str_short_desc: title = msg_short_desc else: raise Exception("Unknown header [%s] (%s:%d)" % (data[0], fname, linenum)) para = data[1].replace('&', '&') para = para.replace('<', '<') para = para.replace('>', '>') return "%s%s\n" % (title, para) def print_entry(record): """Print an XML snippet of the supplied record.""" print """ %(term)s %(header)s """ % record def main(): if len(sys.argv) < 2: usage() sys.exit(1) try: opts, args = getopt.getopt(sys.argv[1:], "cvs") except getopt.GetoptError: usage() sys.exit(1) vars = None commands = True session = True for o, a in opts: if o == "-v": vars = True commands = None session = None if o == "-c": vars = None commands = True session = None if o == "-s": vars = None commands = None session = True fname = args[0] try: file = open(fname, "r") except Exception, e: sys.stdout = sys.stderr print "Error: can't open file for reading" print str(e) sys.exit(1) debug('Reading file %s' % (fname)) # begin output output = ""; if session: output += msg_session_vars if vars: output += msg_vars elif commands: output += msg_commands output += "\n\n" print(output) record = None state_was = 'top_level' linenum = 0 r = re.compile("%T(.+)%n") line = file.readline() while line: linenum += 1 line = line[:-1] line = r.sub("\\1", line) if state_was == 'top_level': if line[0:2] == "//" or line == "": # still top level state_was = 'top_level' elif len(line) > 0 and (line[0] == ' ' or line[0] == "\t"): raise Exception('Unexpected input on top level (%s:%d)' % (fname, linenum)) else: debug('entry start on line %d: %s' % (linenum, line)) state_was = 'entry_start' record = {'term': line, 'desc' : '', 'header': ''} elif state_was == 'entry_start': # this must be a header if is_indented(line): line = strip_indent(fname, linenum, line) else: raise Exception('Header expected (%s:%d)' % (fname, linenum)) record['header'] += parse_header(fname, linenum, vars, session, commands, line) state_was = 'header' elif state_was == 'header': if line == '': debug('entry ended on line %d' % (linenum - 1)) state_was = 'top_level' if record: print_entry(record) record = {'term': line, 'desc' : '', 'header': ''} # so it doesn't match later line = None elif is_indented(line): line = strip_indent(fname, linenum, line) else: raise Exception('Header, separator, or empty line expected (%s:%d)' % (fname, linenum)) if line == None: pass elif line == '': # separator debug('entry headers ended on line %d' % (linenum - 1)) state_was = 'entry_contents' elif line.find(':') >= 0: record['header'] += parse_header(fname, linenum, vars, session, commands, line) else: raise Exception('Unparseable header or extra whitespace in separator (%s:%d)' % (fname, linenum)) elif state_was == 'entry_contents': if line == '': state_was = 'top_level' print_entry(record) record = {'term': line, 'desc' : '', 'header': ''} elif is_indented(line): debug('entry contents on line %d' % (linenum)) record['desc'] += strip_indent(fname, linenum, line) + "\n" else: raise Exception('Expected entry contents, separator, or empty line (%s:%d)' % (fname, linenum)) else: raise Exception('Unknown state (%s:%d)' % (fname, linenum)) line = file.readline() if record: print_entry(record) print('') if __name__ == "__main__": try: main() except Exception, e: die(e.args[0]) ekg2-0.4~pre+20120506.1/docs/ekg2book/userbook.xml000066400000000000000000000017401175142753400211650ustar00rootroot00000000000000 Podrcznik uytkownika ekg2 Wstp rda Instalacja Tworzenie sesji Obsugiwane sieci IM Poczenie z serwerem Zmiana stanu Lista uytkownikw Zarzdzanie uytkownikami Pasek kontaktw ekg2-0.4~pre+20120506.1/docs/ekg2log.dtd000066400000000000000000000005071175142753400171360ustar00rootroot00000000000000 ]> ekg2-0.4~pre+20120506.1/docs/emoticons.ansi000066400000000000000000000012411175142753400177570ustar00rootroot00000000000000 |-) :-* :-) :,-( 8-0 :-O  qp !!! (@)-,-'-- @-3- =|= \_/ c" OK! SHIT! _[]||| _[]||| ()"  ===O=== ~/ [STOP] ekg2-0.4~pre+20120506.1/docs/emoticons.sample000066400000000000000000000003711175142753400203110ustar00rootroot00000000000000 |-) :-* :,-( 8-0 :-O @-,-'-- @-`-,-- OK! S*IT! _[=] ekg2-0.4~pre+20120506.1/docs/events.txt000066400000000000000000000013611175142753400171530ustar00rootroot00000000000000// opis wydarze, ktrych mona uywa poleceniem /on // (c) copyright 2004-2005 Piotr Kupisiewicz protocol-message: odpowiada za odebrane wiadomoci. jako parametr przekazywany jest UID lub nickname. event-avail: zmiana statusu na dostpny. nie przekazywany jest aden parametr event-away: zmiana statusu na away. nie przekazywany jest aden parametr event-na: zmiana statusu na niedostpny. nie przekazywany jest aden parametr. event-online: zmiana statusu z niedostpnego na dostpny/zaraz wracam. nie przekazywany jest aden parametr. event-offline: zmiana statusu z dostpnego/zaraz wracam na niedostpny. nie przekazywany jest aden parametr. event-descr: zmiana opisu. przekazywany jest opis jako parametr. ekg2-0.4~pre+20120506.1/docs/generate-doc.sh000077500000000000000000000003671175142753400200070ustar00rootroot00000000000000#!/bin/sh rm -rf ./doxygen sed \ -e "s,@OUTPUT_LANGUAGE@,Polish,g;" \ -e "s,@OUTPUT_DIRECTORY@,./doxygen,g;" \ -e "s,@EXTRA_FILE_PATTERNS@,doxygenpl.txt,g;" \ -e "s,@STRIP_FROM_PATH@,$(dirname ${PWD}),g" \ doxygen.settings | doxygen - ekg2-0.4~pre+20120506.1/docs/generate-ekg2book.sh000077500000000000000000000027371175142753400207500ustar00rootroot00000000000000#!/bin/sh # Generate the ekg2 book or cleanup in a given directory. set -e lang=${1} case $lang in pl) dir="ekg2book" ;; en) dir="ekg2book-en" ;; *) echo "Provide a valid language code first argument, not \"$1\"." >&2 exit 1 esac action=${2:-'generate'} cd "${dir}" PREINPUTS="design/header.xml bookinfo.xml userbook.xml" POSTINPUTS="develbook.xml faq.xml design/footer.xml" OUTPUT="book.xml" GENPROG="./txt2docbook.py" case $action in generate) rm -rf book cat ${PREINPUTS} "design/plugins_header.xml" > $OUTPUT for i in ../../plugins/* do if [ -f $i ]; then continue; fi if [ -f $i/doc.xml -o -f $i/commands-${lang}.txt -o -f $i/vars-${lang}.txt ]; then sed -e s/PLUGIN/`basename $i`/ "design/plugin_header.xml" >> $OUTPUT if [ -f $i/doc.xml ]; then cat $i/doc.xml >> $OUTPUT fi if [ -f $i/commands-${lang}.txt ]; then $GENPROG -c $i/commands-${lang}.txt >> $OUTPUT fi if [ -f $i/vars-${lang}.txt ]; then $GENPROG -v $i/vars-${lang}.txt >> $OUTPUT fi if [ -f $i/session-${lang}.txt ]; then $GENPROG -s ../session-${lang}.txt >> $OUTPUT $GENPROG -s $i/session-${lang}.txt >> $OUTPUT fi cat "design/plugin_footer.xml" >> $OUTPUT fi done cat "design/plugins_footer.xml" $POSTINPUTS >> $OUTPUT ${XSLTRANSFORMER:-'xslproc'} -stringparam chunker.output.encoding ISO-8859-2 sheet.xsl ${OUTPUT} mkdir book mv *.html book/ ;; clean|distclean) rm -rf book book.xml ;; *) echo "Unknown action \"$action\"." >&2 exit 1 ;; esac ekg2-0.4~pre+20120506.1/docs/mouse.txt000066400000000000000000000007241175142753400170010ustar00rootroot00000000000000// opis obsugi myszki w EKG2 // (c) copyright 2004-2005 Piotr Kupisiewicz * * * myszka dziaa prawidowo w rodowisku xterm (tutaj zostaa przetestowana), rodowisko gpm zostanie uruchomione w najbliszym czasie * * * Uycie: - dwukrotne kliknicie na nicka znajdujcego si na lici kontaktow (po prawej stronie) spowoduje otwarcie query z nim - scrolle w gwnym oknie i w oknie kontaktw powoduj przewijanie si aktualnego okna * * * ekg2-0.4~pre+20120506.1/docs/przenosny-kod.txt000066400000000000000000000015261175142753400204740ustar00rootroot00000000000000kilka uwag co do przenonoci kodu: 1) na systemach BSD wana jest kolejno sieciowych #include'w: #include #include #include #include 2) typy danych uintXX_t nie s dostpne na starszych platformach. na nowszych mog wystpi w , lub podobnych. najlepiej sprawdzi w configure, czy istniej, a jeli nie, zdefiniowa je. 3) ,,__attribute__ ((packed))'' jest rozszerzeniem gcc, wic nie bdzie dostpne na starszych platformach. 4) zachowanie snprintf() zmienio si w C99. wczeniej zwracao -1, jeli cig znakw by zbyt krtki, a teraz zwraca ilo bajtw, jaka byaby zapisana do bufora, gdyby starczyo miejsca. 5) trzeba uwaa na kolejno bajtw, jeli pisze si lub czyta binarne wartoci liczbowe z plikw lub gniazd. $Id$ ekg2-0.4~pre+20120506.1/docs/queries.txt000066400000000000000000000032411175142753400173230ustar00rootroot00000000000000*** ekg -> plugin *** protocol-validate-uid(char *uid, int valid) sprawdza, czy podany protok jest obsugiwany. kady obsugujcy plugin powinien zwikszy `valid' o 1. protocol-ignore(char *session, char *uid, int prevlevel, int newlevel) zmieniono poziom ignorowania osoby z `prevlevel' do `newlevel'. userlist-changed(char *session, *uid) zmieni si wpis w licie kontaktw. userlist-removed(char *uid) usunito wpis z listy kontaktw. userlist-added(char *uid) dodano wpis do listy kontaktw. userlist-renamed(char *prevnick, char *newnick) zmieniono pseudonim danej osoby. qutting(char *reason) wpisano /quit. ui-window-target-changed(window_t *w) zmieniono nazw okna. plugin-print-version() proba o wywietlenie wersji pluginw. config-write(FILE *f) zapisywanie konfiguracji. *** plugin -> ekg *** protocol-connected(char *session) poczenie si udao. protocol-failed(char *session, char *reason) poczenie si nie udao. protocol-disconnected(char *session, char *reason) wywoywany przy rozczeniu sesji przez serwer. protocol-status(char *session, char *uid, char *status, char *descr, char *host, int port, time_t when) dana osoba zmienia swj stan. protocol-message(char *session, char *sender, char **recipients, char *text, uint32_t *format, time_t sent, int class, char *seq, int secure) otrzymano wiadomo od danej osoby. protocol-message-ack(char *session, char *recipient, char *seq, char *status) potwierdzenie nadania wiadomoci. protocol-message-post(char *session, char *sender, char **recipients, char *text, uint32_t *format, time_t sent, int class, char *seq, int secure) prawie jak protocol-message, tyle, e po deszyfracji ekg2-0.4~pre+20120506.1/docs/release-checklist.txt000066400000000000000000000024751175142753400212450ustar00rootroot00000000000000Release checklist ----------------- Pick a new $version. The versioning scheme we use is: 0.2.7 < (0.3.0_rc1_pre) < 0.3.0_rc1 < (0.3.0_rc2_pre) < 0.3.0_rc2 < 0.3.0 << 0.3.1 <<< 0.4.0 Note: * "rc" stands for Release Candidate * the "_pre" versions are used just as a placeholder between RCs * if you are about to bump the first or second number in the $version, it means you need to start a new maintenance branch, for instance: git checkout -b 0.4.x master otherwise (you're bumping just the last digit, or rc number), check out the existing maintenance branch: git checkout 0.4.x Note: the "x" is literal in this branch name. # Prepare and tag the source: version=.... make check make -C po update-po git commit -m 'Refreshed the PO files.' po/ vi configure.ac # bump the version to $version git commit -m "Bumped the version to $version." configure.ac git tag ekg2_$version git push git push origin ekg2_$version # Update the website: cd ../ekg2-website vi -O index.php en/index.php # mention $version git commit -m "Mentioned $version." index.php en/index.php git push # Publish: ssh toxygen.net $HOME/ekg2-website/website-scripts/release ekg_$version crontab -l # find and use the command line to push website update Other notes: - After doing an "rc1", drop "rc*" tags from the _previous_ release. ekg2-0.4~pre+20120506.1/docs/session-en.txt000066400000000000000000000035701175142753400177360ustar00rootroot00000000000000alias type: string default value: none short session name auto_away type: integer default value: 600 Idle time in seconds, after which status will be set to 'away'. If 0, auto_away will be disabled. auto_away_descr type: string default value: none Description to be set while entering auto-away. If null, the current description will be kept. Can contain following escape sequences: %? output following chars only if description is set %! output following chars only if description is not set %/ output following chars always (disable above) %$ output current description here %% output '%' auto_xa type: integer default value: 0 Idle time in seconds, after which status will be set to 'xa'. If 0, auto_xa will be disabled. auto_xa_descr type: string default value: none Description to be set while entering auto-xa. If null, the current description will be kept. Can contain following escape sequences: %? output following chars only if description is set %! output following chars only if description is not set %/ output following chars always (disable above) %$ output current description here %% output '%' auto_back type: integer default value: 0 Variable determines that status will be change to 'available', if current away status was set automatically. If 1, status is changed whenever user send any message. If 2, status is changed after keypressed. auto_connect type: bool default value: 0 Variable determines session should be connected after program startup. auto_reconnect type: integer default value: 10 Variable determines, that how long program need to wait to reconnect, after disconnect or invalid connection. If 0, program won't try. connect_timeout type: integer default value: 30 Number of seconds after which connecting will timeout. Set to 0 to disable timeouting (not recommended). ekg2-0.4~pre+20120506.1/docs/session-pl.txt000066400000000000000000000042731175142753400177500ustar00rootroot00000000000000alias typ: tekst domyślna wartość: brak krótka nazwa sesji auto_away typ: liczba domyślna wartość: 600 wartość określająca, po jakim czasie stan użytkownika zostanie zmieniony na ,,zajęty''. podaje się w sekundach. jeśli równa 0, nie będzie automatycznej zmiany. auto_away_descr typ: tekst domyślna wartość: brak Opis ustawiany przy przejściu w auto-away. Jeśli nieustawione, zmiana opisu nie nastąpi. Dozwolone są następujące sekwencje specjalne: %? wypisz następne znaki, jeśli opis jest ustawiony %! wypisz następne znaki, jeśli opis nie jest ustawiony %/ zawsze wypisz następne znaki (wyłącza powyższe) %$ wypisz aktualny opis %% wypisz '%' auto_xa typ: liczba domyślna wartość: 0 wartość określająca, po jakim czasie stan użytkownika zostanie zmieniony na ,,bardzo zajęty''. podaje się w sekundach. jeśli równa 0, nie będzie automatycznej zmiany. auto_xa_descr typ: tekst domyślna wartość: brak Opis ustawiany przy przejściu w auto-xa. Jeśli nieustawione, zmiana opisu nie nastąpi. Dozwolone są następujące sekwencje specjalne: %? wypisz następne znaki, jeśli opis jest ustawiony %! wypisz następne znaki, jeśli opis nie jest ustawiony %/ zawsze wypisz następne znaki (wyłącza powyższe) %$ wypisz aktualny opis %% wypisz '%' auto_back typ: liczba domyślna wartość: 0 wartość określająca, czy stan ma być automatycznie zmieniany na dostępny, jeśli obecny stan ,,zajęty'' został ustawiony automatycznie. jeśli równa 1, stan jest zmieniany na dostępny przy wysłaniu jakiejkolwiek wiadomości. jeśli równa 2, przy wciśnięciu klawisza. auto_connect typ: bool domyślna wartość: 0 wartość określająca, czy dana sesja ma się łączyć wraz z uruchomieniem programu auto_reconnect typ: liczba domyślna wartość: 10 w przypadku nieudanego połączenia, określa po ilu sekundach program ma ponowić próbę. jeśli równa 0, nie próbuje więcej. connect_timeout typ: liczba domyślna wartość: 30 liczba sekund, po których upływie próba łączenia z danym serwerem zostanie przerwana. ustawienie na 0 wyłącza timeouty (niezalecane). ekg2-0.4~pre+20120506.1/docs/sim.txt000066400000000000000000000026461175142753400164460ustar00rootroot00000000000000// Obsuga Secure Internet Messaging w ekg // (c) copyright 2001-2003 by Wojtek Kaniewski // 2004-2005 by Piotr Kupisiewicz Szyfrowanie to jest zgodne z http://gg.wha.la/crypt/ i powinno dziaa bez problemu z PowerGG i Kadu. Klucze s przechowywane w katalogu ~/.ekg2/keys w formacie PEM -- klucz prywatny w pliku ,,private.pem'', klucze publiczne w plikach odpowiadajcych numerowi rozmwcy z rozszerzeniem ,,.pem''. Przed zaczciem caej ,,zabawy'' naley zaadowa plugin sim: plugin +sim Aby wygenerowa sobie klucz piszemy: sim:key -g Zostanie on zapisany na dysku. Jeli chcemy szyfrowa wiadomoci wysyane do znajomych, musimy umieci ich klucze publiczne w ~/.gg/keys. Jeli chcemy, eby wiadomoci wysyane do nas byy szyfrowane, musimy wysa naszym rozmwcom nasz klucz publiczny, uywajc polecenia ,,key -s''. NIE NALEY NIKOMU WYSYA KLUCZA PRYWATNEGO Z PLIKU PRIVATE.PEM. By wczy szyfrowanie, naley ju tylko wpisa: set sim:encryption 1 Dalej wszystko bdzie si dziao automagicznie. szyfrowane wiadomoci s odpowiednio oznaczane -- domylnie jest to ty tekst ,,szyfrowane'' w nagwku wywietlanej wiadomoci. ekg po otrzymaniu w wiadomoci klucza publicznego (to znaczy wiadomoci zaczynajcej si od ,,-----BEGIN RSA PUBLIC KEY-----'') zapisze go jako klucz nadawcy w katalogu ~/.ekg2/keys. Zarzdzanie kluczami odbywa si za pomoc polecenia ,,key''. $Id$ ekg2-0.4~pre+20120506.1/docs/themes-en.txt000066400000000000000000000072141175142753400175370ustar00rootroot00000000000000// translation from themes.txt 2009 by Jan J. Roman .---------------,--------,-------,-----. | color |ordinary| light | bg | ,---------------+--------+-------+-----' | black/grey | %k | %K | %l | | blue | %b | %B | %e | | red | %r | %R | %s | | violet | %m/%p | %M/%P | %q | | turquoise | %c | %C | %d | | brown/yellow | %y | %Y | %z | | green | %g | %G | %h | | white | %w | %W | %x | | blinking | %i | - | - | | bold | %T | - | - | ,---------------'--------'-------'-----| | 'white char' | %n | `---------------'----------------------' * * * %A choose the graphic maping: vt100 %a choose default maping. * * * You can use %1-%9 to hard define a width, it can be usefull for tables or lists. %[10]1 add spaces before text for attaining specific width (in this case 10 - it is the first parameter) %[-10]1 similar but add spaces after text %[.5]1 uses zeroes to achive specific length %[,9]2 the same but uses points %[_4]1 uses '_' character %(10)1 if parameter is longer than 10 it will be NOT cut down. %[^12]1 put first parameter in the middle of space length of 12 (empty fields are filled by spaces). %[^,8]1 as above but space size is 8 and filling character is point WARNING! coloring expresions are treated as regular text, Don't use it for auto completion. * * * // It is polish declension cause I have no idea how to translate this ;-) jeli chce si rozrnia przymiotniki dla rnych pci, mona uy %@n, gdzie ,,n'' to numer formatu, ktry bierzemy pod uwag. jeli ostatni liter bdzie ,,a'', %@n zostanie zastpione przez ,,a'', w innym przypadku przez ,,y''. przykad: %> %1 jest dostpn%@1. naley wzi uwag, e w wielu wypadkach pseudonimy s najpierw formatowane przez known_user i unknown_user, wic trzeba poda osobny parametr z samym pseudonimem. * * * %> prompt (green by default) %! error (red by default) %) prompt2 (turquoise by default) %# timestamp (GG:MM by default) %| the end of prompt. Occur of this format means that prompt should be repeated in the beginning of each new line example: %> Very long line, will be divided for several shorter. On the small term: .-------------------------. | ::: Very long line, | | will be divided for | | several shorter. | `-------------------------' If in format string is %| it means: %> %|Very long line, will be divided for several shorter. output will be: .-------------------------. | ::: Very long line, | | ::: will be divided for | | ::: several shorter. | `-------------------------' * * * There is two special formats:''known_user'' i ''unknown_user''. They define how to show users from contact list and out of list. First get two parameters: %1 - description from contact list %2 - user identificator second only one: %1 - user identificator * * * entries readline_*, shouldn't consist of formating codes (with '%'), The exception of this rule is readline_prompt_query. This rule is also for config_changed. * * * (ncurses) There is posibility of setiing margins and definind its size by ncurses:margin_size variable. For use margins there is "/|" formating string. example: text left/|text right causes: text left text right size of gap is defining in ncurses:margin_size value. * * * $Id: themes.txt 4009 2008-06-02 09:56:12Z wiechu $ $Id: themes.txt 4009 2009-03-14 13:35:43Z Pinochet $ ekg2-0.4~pre+20120506.1/docs/themes.txt000066400000000000000000000065641175142753400171460ustar00rootroot00000000000000 .---------------,--------,-------,-----. | kolor | zwyky | jasny | to | ,---------------+--------+-------+-----' | czarny/szary | %k | %K | %l | | niebieski | %b | %B | %e | | czerwony | %r | %R | %s | | fioletowy | %m/%p | %M/%P | %q | | turkusowy | %c | %C | %d | | brzowy/ty | %y | %Y | %z | | zielony | %g | %G | %h | | biay | %w | %W | %x | | mrugajcy | %i | - | - | | tusty | %T | - | - | ,---------------'--------'-------'-----| | bez koloru | %n | `---------------'----------------------' * * * %A wybranie mapowania grafiki vt100 %a powrt do mapowania domylnego * * * przy %1-%9 mona kaza dopenia do konkretnej szerokoci. przydaje si do wszelkiego rodzaju tabelek. %[10]1 dopenia spacjami z prawej pierwszy parametr do 10 pl %[-10]1 j.w. tylko e do lewej %[.5]1 dopenia zerami %[,9]2 dopenia kropkami %[_4]1 dopenia znakami podkrelenia %(10)1 jeli rozmiar parametru przekroczy 10 znakw nie obcina %[^12]1 wstawia pierwszy parametr na rodku pola o szerokoci 12 znakw uzupeniajc spacjami %[^,8]1 wstawia pierwszy parametr na rodku pola o szerokoci 12 znakw uzupeniajc kropkami UWAGA! kolorkowe sekwencje ansi traktuje jak znaki, wic nie powinno si ich uywa przy dopenianiu parametrw. * * * jeli chce si rozrnia przymiotniki dla rnych pci, mona uy %@n, gdzie ,,n'' to numer formatu, ktry bierzemy pod uwag. jeli ostatni liter bdzie ,,a'', %@n zostanie zastpione przez ,,a'', w innym przypadku przez ,,y''. przykad: %> %1 jest dostpn%@1. naley wzi uwag, e w wielu wypadkach pseudonimy s najpierw formatowane przez known_user i unknown_user, wic trzeba poda osobny parametr z samym pseudonimem. * * * %> prompt (domylnie zielony) %! error (domylnie czerwony) %) prompt2 (domylnie turkusowy) %# timestamp (domylnie GG:MM) %| koniec promptu. jeli ten format wystpuje, to przy przenoszeniu do nastpnej linii, tekst przed tym formatem zostanie wywietlony ponownie. na przykad dla: %> Duga linia, ktra zostanie podzielona na kilka linii na maym terminalu zostanie podzielone na: .-------------------------. | ::: Duga linia, ktra | | zostanie podzielona na | | kilka linii | `-------------------------' a po dodaniu %|, tzn: %> %|Duga linia, ktra zostanie podzielona na kilka linii zostanie wywietlone jako: .-------------------------. | ::: Duga linia, ktra | | ::: zostanie podzielona | | ::: na kilka linii | `-------------------------' * * * dwa specjalne formaty ,,known_user'' i ,,unknown_user'' okrelaj, jak bd pokazywani userzy z listy i spoza listy kontaktw. pierwszy za parametry przyjmuje %1 opis, %2 numerek, a drugi %1 numerek. * * * wpisy readline_*, oprcz readline_prompt_query nie mog zawiera adnych ,,procentowych'' kodw sterujcych. podobnie jest z promptem config_changed. * * * (ncurses) w ekg2 mona ustawia tzw marginesy poprzez zmienn ncurses:margin_size. aby z nich korzysta naley w formatce uy znakw /| do oddzielenia tekstu znajdujcego si po prawej i po lewej stronie. przykad: tekst/|tekst2 spowoduje wywietlenie: tekst tekst2 odstp reguluje si poprzez zmienn ncurses:margin_size. * * * $Id$ ekg2-0.4~pre+20120506.1/docs/ui-ncurses-en.txt000066400000000000000000000073621175142753400203530ustar00rootroot00000000000000// ui-ncurses // (c) copyright 2002 wojtek kaniewski // (c) copyright 2004 Piotr Kupisiewicz Ncurses differs a little from readline with windows. The main difference is that the first window exists _always_ and it's a state window. To this window are sent informations about users' staus change. Keyboard operation details are in README file. Recommended settings: set display_ack 3 set display_sent 1 set make_window 2 The status bar contains clock, users own number details (color determines the status: black -- offline, white -- avail, gray -- busy, sate -- invisible), current window number, information about activity in other windows, new mail ect. New format has been added - "statusbar". Similarly to the others it supports colors, but doesn't have complection, blinking ect. But there are conditional constructions and extended information instead: %{activity} list of active windows %{debug} we are in debug window %{descr} our description/our status description %{mail} information on the number of e-mails or empty if no mail %{more} any added information in window when it's scrolled %{nick} our nickname {XXX not implemented yet} %{query} user/interlocutor in the current window %{query_descr} user description %{query_ip} user IP %{query_nickname} users nickname in the current window %{session} session ID or session alias %{time} current time, format defined by the bar_timestamp %{typing} text input by the caller %{uin} own number {XXX not implemented yet} %{url} ekg2 webside %{version} ekg2 version %{window} current windows number In IRC window are also available: %{irctopic} channel topic %{irctopicby} channel topic author %{ircmode} flags on a channel Conditional constructions allow to add text to the status bar only when the specified condition is met. Structures are as follows: %{?condition text} text will be displayed when the condition is met %{?!condition text} text will be displayed when the condition is met The condition may be the availability of any of the above-named texts. For example, if there is conversation in the current window, the condition %{?query ...} is met. If own number is not configured, the condition %{?!uin ...} is met. There are also: %{?away ...} status busy %{?avail ...} status available %{?notavail ...} status offline %{?invisible ...} status invisible %{?more ...} any added information in window when it's scrolled %{?query_away ...} interlocutor is away %{?query_avail ...} interlocutor is avail %{?query_notavail ...} interlocutor is offline %{?query_invisible ...} interlocutor is invisible Conditions can be nested. For example, condition %{?query %{!?query_descr ...}} will be displayed if the conversation is being continued, but the caller doesn't have the status with description. So that incredible status bars can be created, but the default should be enough for everyone. The status bar can have up to 5 lines. Additional lines are described by the formats: ,,statusbar2'', ,,statusbar3'' ect. If there is a format ,,statusbar1''then it has priority over ,,statusbar'' You can also include a header. This is a bar located above the windows, described by the formats: ,header'', ,,header1'', ,,header2'' ect. It's analogy to the status bar and the same formats are available. contact list is (usually) available on the right side after setting the variable ncurses:contacts to 1 (on). Contacts which are writing us currently are set to flashing (in fact it's a blink element of the structure userlist_t). Ui-ncurses supports this by reading the appropriate theme and by correctly display. $Id: ui-ncurses.txt 5013 2010-10-04 13:52:16Z wiechu $ ekg2-0.4~pre+20120506.1/docs/ui-ncurses.txt000066400000000000000000000072251175142753400177510ustar00rootroot00000000000000// ui-ncurses // (c) copyright 2002 wojtek kaniewski // (c) copyright 2004 Piotr Kupisiewicz Interfejs ten rni si nieco od readline z okienkami. Gwn rnic jest fakt, e pierwsze okienko istnieje _zawsze_ i jest oknem stanu. Do niego lec informacje o pojawianiu si i znikaniu ludzi. Szczegy dotyczce obsugi klawiatury znajduj si w pliku README. Zalecane ustawienia: set display_ack 3 set display_sent 1 set make_window 2 Pasek stanu zawiera zegar, informacje o wasnym numerku (kolor okrela stan: czarny -- niedostpny, biay -- dostpny, szary -- zajty, ciemnoszary -- niewidoczny), numer aktualnego okna, informacje o aktywnoci w innych oknach, nowej poczcie itd. Dodano nowy format ,,statusbar''. podobnie jak i reszta obsuguje kolory, ale nie ma dopeniania, mrugania itp. s za to konstrukcje warunkowe oraz rozszerzone informacje: %{activity} lista okien, w ktrych si co pojawio %{debug} jestemy w oknie debug %{descr} opis (naszego) stanu %{mail} ilo nowej poczty lub pusty jeli nie ma %{more} dopisano co do okna, gdy jest przewinite %{nick} wasny pseudonim {XXX niezaimplementowane} %{query} rozmwca w aktualnym oknie %{query_descr} opis stanu rozmwcy %{query_ip} IP rozmwcy %{query_nickname} tylko nickname rozmwcy z aktualnego okna %{session} identyfikator lub alias sesji %{time} aktualny czas w formatcie okrelonym przez statusbar_timestamp %{typing} informacja o wprowadzaniu tekstu przez rozmwc %{uin} wasny numer {XXX niezaimplementowane} %{url} adres do strony ekg %{version} wersja ekg %{window} numer aktualnego okna W oknie sesji IRC mona dodatkowo uzyska: %{irctopic} temat kanau %{irctopicby} kto ustawi temat %{ircmode} tryb IRC Konstrukcje warunkowe pozwalaj dodawa do pasku stanu teksty tylko, gdy speniony zostanie okrelony warunek. Konstrukcje te wygldaj nastpujco: %{?warunek tekst} tekst wywietlony przy spenionym warunku %{?!warunek tekst} tekst wywietlony przy niespenionym warunku Warunkiem moe by dostpno ktrego z wyej wymienionych tekstw. Jeli na przykad w aktualnym oknie jest prowadzona rozmowa, warunek %{?query ...} bdzie speniony. Jeli nie skonfigurowalimy wasnego numeru, speniony bdzie warunek %{?!uin ...}. ponadto wystpuj rwnie: %{?away ...} stan zajty %{?avail ...} stan dostpny %{?notavail ...} stan niedostpny (niepoczony) %{?invisible ...} stan niewidoczny %{?more ...} dopisano co do okna, gdy jest przewinite %{?query_away ...} rozmwca zajty %{?query_avail ...} rozmwca dostpny %{?query_notavail ...} rozmwca niedostpny %{?query_invisible ...} rozmwca niewidoczny Warunki mona zagnieda, tzn. %{?query %{!?query_descr ...}} zostanie wywietlone, jeli prowadzona jest rozmowa, ale rozmwca nie ma stanu opisowego. Dziki temu mona konstruowa kosmiczne paski stanu, ale i tak domylny powinien wystarczy kademu. Jest moliwo rozszerzenia pasku stanu do maksymalnie 5-ciu linii. Kolejne linie s opisane formatami ,,statusbar2'', ,,statusbar3'' itd. Jeli wystpuje format ,,statusbar1'', ma on pierwszestwo przed ,,statusbar''. Moliwe jest te wczenie nagwka okna, tj. paska wywietlanego u gry ekranu, nad oknami. jest on opisany formatami ,,header'', ,,header1'', ,,header2'' itd., analogicznie do paska stanu. dostpne formaty s identyczne. Lista kontaktw, ktra jest dostpna po prawej (zazwyczaj) stronie okna, poprzez wczenie ncurses:contacts. Kontakty, ktre aktualnie do nas pisz s ustawiane jako mrugajce (w zasadzie to element blink strutkury userlist_t). Ui-ncurses obsuguj to poprzez odczytanie stosownego theme'a i wywietlenie go w sposb prawidowy. $Id$ ekg2-0.4~pre+20120506.1/docs/vars-en.txt000066400000000000000000000126301175142753400172230ustar00rootroot00000000000000auto_save type: integer default value: 0 *not translated yet* auto_user_add type: bool default value: 0 *not translated yet* away_reason type: text default value: none *not translated yet* back_reason type: text default value: none *not translated yet* beep type: bool default value: 1 *not translated yet* beep_chat type: bool default value: 1 *not translated yet* beep_msg type: bool default value: 1 *not translated yet* beep_notify type: bool default value: 1 *not translated yet* completion_char type: text default value: ":" *not translated yet* completion_notify type: integer default value: 1 *not translated yet* dcc_dir type: text default value: none Set directory, in which files will be saved. debug type: bool default value: 1 *not translated yet* default_status_window type: bool default value: 0 *not translated yet* display_ack type: integer default value: 12 *not translated yet* display_blinking type: bool default value: 1 *not translated yet* display_color type: integer default value: 1 *not translated yet* display_color_map type: text default value: "nTgGbBrR" *not translated yet* display_crap type: bool default value: 1 Defines whether information which weren't connected with interlocutor will be displayed in chat window. If it's 0, in chat window information only about interlocutor status are displayed. display_day_changed type: bool default value: 1 *not translated yet* display_notify type: integer default value: 1 A value of 0 causes state changes of your contacts to be ignored, a value of 1 causes all state changes to be displayed, and a value of 2 causes only state changes from unavailable to available and back to be displayed. A session variable with the same name has a higher priority and overrides this global one (unless it is set to -1). display_sent type: bool default value: 1 *not translated yet* display_welcome type: bool default value: 1 *not translated yet* emoticons type: bool default value: 1 *not translated yet* events_delay type: integer default value: 3 *not translated yet* exit_exec type: text default value: none Command executed on EKG2 exit, just before exit(). It'll replace EKG2 in process table, so ekg2 will return its' exit code. expert_mode type: integer default value: 0 *not translated yet* history_savedups type: integer default value: 1 If set to 0, don't save lines matching the previous history entry. keep_reason type: integer default value: 1 *not translated yet* last type: integer default value: 0 *not translated yet* last_size type: integer default value: 0 *not translated yet* make_window type: integer default value: 2 *not translated yet* mesg type: integer default value: 2 *not translated yet* nickname type: text default value: none *not translated yet* query_commands type: bool default value: 1 *not translated yet* quit_reason type: text default value: none *not translated yet* save_password type: bool default value: 1 *not translated yet* save_quit type: integer default value: 1 *not translated yet* send_white_lines type: bool default value: 0 Defines how to handle messages containing whitespaces only. If set to 0, ekg2 won't send such messages. If set to 1, ekg2 will send them. Enable it if you are whitespace programmer. Otherwise it is save to set it to 0. session_default type: text default value: none *not translated yet* session_locks type: int default value: 1 Kind of session-locks used by EKG2 to avoid two copies of it connecting the same session simultaneously. Possible values: 0 - session-locks disabled, 1 - flock-based locks (default), 2 - file existence-based locks (simple). sessions_save type: bool default value: 0 *not translated yet* slash_messages type: bool default value: 1 *not translated yet* sort_windows type: bool default value: 0 *not translated yet* sound_app type: text default value: none *not translated yet* sound_chat_file type: text default value: none *not translated yet* sound_mail_file type: text default value: none *not translated yet* sound_msg_file type: text default value: none *not translated yet* sound_notify_file type: text default value: none *not translated yet* sound_sysmsg_file type: text default value: none *not translated yet* speech_app type: text default value: none *not translated yet* subject_prefix type: text default value: "## " *not translated yet* subject_reply_prefix type: text default value: 'Re: ' Prefix used in subjects of message replies. tab_command type: text default value: "chat" *not translated yet* theme type: text default value: none *not translated yet* time_deviation type: integer default value: 300 *not translated yet* timestamp type: text default value: "\%H:\%M:\%S" *not translated yet* timestamp_show type: bool default value: 1 *not translated yet* window_session_allow type: text default value: 0 When we're allowed to cycle sessions: 0 - only in status window, 1 - like above + in queries within sessions handling window target, 2 - status & query windows, 4 - status & query windows, but first change to status window. windows_layout type: text *not translated yet* windows_save type: bool default value: 0 *not translated yet* ekg2-0.4~pre+20120506.1/docs/vars-pl.txt000066400000000000000000000330601175142753400172340ustar00rootroot00000000000000// mały opis dostępnych zmiennych // (c) copyright 2001-2003 wojtek kaniewski // 2004 adam mikuta auto_save typ: liczba domyślna wartość: 0 Określa co ile sekund automatycznie zapisać ustawienia. Jeśli 0, nie zapisuje automatycznie. auto_user_add typ: bool domyślna wartość: 0 Określa czy osoby których nie mamy w kontaktach a dostajemy informację o ich stanie mają być dodawane automatycznie do listy kontaktów. Taka sytuacja występuje jeśli w kontaktach ma nas ktoś kto ma włączony tryb tylko dla przyjaciół a my go w swoich kontaktach nie mamy. away_reason typ: tekst domyślna wartość: brak Domyślny opis stanu zajętego, ustawiany przy zmianie bez podania parametru. back_reason typ: tekst domyślna wartość: brak Domyślny opis stanu dostępnego, ustawiany przy zmianie bez podania parametru. beep typ: bool domyślna wartość: 1 Określa, czy klient ma beepać w różnych sytuacjach. Wyłączenie tej opcji spowoduje, że w przypadku żadnych zdarzeń związanych z GG nie będzie podnosić alarmu. Niestety nie obejmuje to zdarzeń związanych z wprowadzanym tekstem i przy tab-completion może się to przytrafić. beep_chat typ: bool domyślna wartość: 1 Czy beepać przy rozmowach. Wyłączenie ,,beep'' wyłącza również tę opcję. beep_msg typ: bool domyślna wartość: 1 Czy beepać przy nadchodzących wiadomościach. Wyłączenie ,,beep'' wyłącza również tę opcję. beep_notify typ: bool domyślna wartość: 1 Czy beepać przy zmianie stanu któregoś ze znajomych. Wyłączenie ,,beep'' wyłącza również tę opcję. completion_char typ: tekst domyślna wartość: ":" Znaki dodawane po dopełnienu nicka użytkownika. completion_notify typ: liczba domyślna wartość: 1 Określa, czy po pojawieniu się któregoś ze znajomych jego nick ma być dopisywany do listy dopełniania klawiszem Tab. Jeśli jest równa 2, jest również usuwany po przejściu w stan niedostępny. Dodanie wartości 4 spowoduje, że dopisywani będą także ci, którzy pojawią się na liście ze stanem ,,zajęty''. Wszystkie dostępne wartości to: 0, 1, 2, 5, 6. dcc_dir typ: tekst domyślna wartość: brak Określa katalog, do którego będą zapisywane pobierane pliki. debug typ: bool domyślna wartość: 1 Określa, czy mają być wypisywane informacje do okna debug. default_status_window typ: bool domyślna wartość: 0 Określa czy wyniki poleceń mają trafiać domyślnie do okna statusu. Jeżeli 0 to wyniki poleceń trafiają do aktualnego okienka, jeżeli 1 to wszystkie komunikaty trafiają do okna statusu . display_ack typ: liczba domyślna wartość: 12 Określa, czy i które powiadomienia o (nie)dostarczeniu wiadomości mają być wyświetlane. Stanowi sumę wartości: 1 - wiadomość dotarła do adresata 2 - wiadomość została umieszczona w kolejce doręczania 4 - wiadomość została odrzucona przez serwer/adresata 8 - wiadomość tymczasowo nie może być przyjęta 16 - wynik doręczania nieznany display_blinking typ: bool domyślna wartość: 1 Określa czy powinny zostawać podświetlane osoby, które do nas aktualnie piszą. display_color typ: liczba domyślna wartość: 1 Wartość 0 wyłącza wyświetlanie kolorów, wartość 1 włącza. Wartość 2 ma znaczenie tylko w interfejsie ncurses i powoduje wyświetlanie kolorów wszędzie poza paskiem stanu i nagłówkiem okna. display_color_map typ: tekst domyślna wartość: "nTgGbBrR" Określa jakie kolory będą przypisane różnym atrybutom tekstu. Musi zawierać 8 znaków zgodnych ze spisem z pliku docs/themes.txt. Każdy z nich określa kolejno kolor dla: - brak atrybutów, - pogrubiony, - pochyły, - pochyły+pogrubiony, - podkreślony, - podkreślony+pogrubiony, - podkreślony+pochyły, - podkreślony+pochyły+pogrubiony. display_crap typ: bool domyślna wartość: 1 Mówi, czy w oknie rozmowy mają być wyświetlane komunikaty niezwiązane z rozmówcą. Jeśli jest równe 0, w oknie rozmowy są wyświetlane tylko wiadomości i informacje o zmianie stanu rozmówcy. display_day_changed typ: bool domyślna wartość: 1 Określa, czy o północy w otwartych oknach ma być wpisana informacja o zmianie daty. display_notify typ: liczba domyślna wartość: 1 Wartość 0 powoduje ignorowanie zmian stanu znajomych, wartość 1 powoduje wyświetlanie wszystkich zmian, wartość 2 wyświetla tylko zmiany z niedostępnego na dostępny i na odwrót. Większy priorytet ma zmienna sesyjna o tej samej nazwie (jeżeli nie równa -1). display_sent typ: bool domyślna wartość: 1 Jeśli włączone, wyświetlane są również wysyłane wiadomości. Należy zwrócić uwagę, że przy make_window = 2, wysłanie wiadomości utworzy automatycznie okienko rozmowy, a przy make_window = 1 tylko wtedy, gdy nie będzie żadnych wolnych okienek. display_welcome typ: bool domyślna wartość: 1 Mówi, czy ekg ma wyświetlić tekst powitalny po uruchomieniu. emoticons typ: bool domyślna wartość: 1 Uruchamia rozwijanie emotikonów w zdefiniowane teksty. Więcej szczegółów w pliku docs/files.txt w sekcji 5-tej. events_delay typ: liczba domyślna wartość: 3 Określa, po ilu sekundach od połączenia z serwerem zaczną działać zdarzenia ,,on'', dźwięki związane ze zmianami stanu i będą wyświetlane informacje o niedostępności osób. Chodzi o to, aby nie traktować początkowych informacji o stanie osób po połączeniu jako zmianę ich stanu. exit_exec typ: tekst domyślna wartość: brak Polecenie, wykonywane przy zamykaniu EKG2, zaraz przed exit(). Charakteryzuje się tym, że ,,zastępuje'' owe w tablicy procesów, Tym samym kod wyjścia zwracany przez EKG2 będzie wynikiem danego polecenia. expert_mode typ: liczba domyślna wartość: 0 Używane w różnych wersjach ekg2 do różnych diagnostyk. Zmienna bez większego znaczenia dla użytkownika. history_savedups typ: liczba domyślna wartość: 1 Jeśli ustawiona na 0, to wiersze pasujące do ostatniego wiersza historii nie są wprowadzane do listy historii. keep_reason typ: liczba domyślna wartość: 1 Jeśli włączona, komendy ,,away'', ,,back'', ,,invisible'', ,,quit'' i ,,disconnect'' bez podanego powodu przejmą aktualny opis. Zostanie on wraz ze stanem zapisany bez wyraźnego wydawania komendy zapisu konfiguracji. Wartość 2 powoduje zapisywanie opisu bez stanu. last typ: liczba domyślna wartość: 0 Wskazuje, czy zapisywać ostatnie wiadomości do podręcznego bufora (,,last_size'' musi być większe od 0). Dla 1, będzie zapisywać tylko last_size wszystkich wiadomości przychodzących, dla 2, last_size wiadomości od każdego usera. Dostępna jest jeszcze logiczna wartość 4, która pozwala logować wiadomości wysłane. Dostępne są w takim razie wartości 0, 1, 2, 5 i 6. last_size typ: liczba domyślna wartość: 0 Wskazuje, ile ma być zapisywanych wiadomości dla komendy ,,last''. make_window typ: liczba domyślna wartość: 2 Określa czy będą tworzone nowe okienka dla nowych rozmów. Dla 1 będzie wykorzystane pierwsze wolne okno (na którym z nikim jeszcze się nie rozmawia), lub tworzone nowe w przypadku braku wolnych. Dla 2 bezwarunkowo utworzone zostanie nowe okno. Jeśli do wartości zostanie dodane 4, okienka nie będą tworzone dla pojedynczych wiadomości. mesg typ: liczba domyślna wartość: 2 Ustala, czy zezwalamy na wysyłanie do nas komunikatów za pomocą write, talk lub wall. Dla 0 nie wyrażamy na to zgody, dla 1 zezwalamy na pisanie na nasz terminal. W przypadku wartości 2 używane są ustawienia sprzed uruchomienia ekg2. nickname typ: tekst domyślna wartość: brak Nasz nick używany w protokołach innych niż irc do wyświetlenia naszego /me. query_commands typ: bool domyślna wartość: 1 Możliwość wydawania długich poleceń podczas rozmowy z użytkownikiem bez poprzedzania ich znakiem '/'. quit_reason typ: tekst domyślna wartość: brak Domyślny opis stanu niedostępnego, ustawiany przy wychodzeniu. save_password typ: bool domyślna wartość: 1 Określa czy hasło ma być zapisywane w pliku konfiguracyjnym. Opcja ta może przydać się, gdy boimy się administratora przeglądającego pliki użytkowników. save_quit typ: liczba domyślna wartość: 1 Określa czy po ewentualnych zmianach przy wyjściu ma pojawiać się pytanie o zapisanie konfiguracji. Jeżeli 0 to konfiguracja nie jest zapisywana, jeżeli 1 to pojawia się pytanie, jeżeli 2 to konfiguracja zapisana jest bez pytania send_white_lines typ: bool domyślna wartość: 0 Określa czy ekg2 ma wysyłać linie składające się wyłącznie z białych znaków. Jeżeli 0, ekg2 zignoruje wiadomości składające się wyłącznie z białych znaków. Jeżeli 1, ekg2 będzie wysyłało takie wiadomości. Włącz tę opcję, jeżeli jesteś programistą whitespace. W przeciwnym wypadku możesz pozostawić ją wyłączoną. session_default typ: tekst domyślna wartość: brak Nazwa sesji, która po uruchomieniu zostanie ustawiona jako aktywna. Jeżeli sessions_save jest 1, to zmienna session_default jest automatycznie ustawiana przy wyjściu z programu. session_locks typ: liczba domyślna wartość: 1 Typ blokad sesji, używanych przez EKG2 celem uniknięcia jednoczesnego połączenia się dwóch kopii owego do jednej sesji. Możliwe wartości: 0 - brak blokad, 1 - blokady oparte o flock() (domyślne), 2 - blokady oparte o istnieniu pliku (uproszczone). sessions_save typ: bool domyślna wartość: 0 Określa, czy aktualna sesja ma być ustawiona jako domyślna - przy następnym uruchomieniu będzie ona ustawiona jako aktywna. Przy wychodzeniu z programu pojawi się monit o zapisanie zmian. slash_messages typ: bool domyślna wartość: 1 Określa, czy wiadomości zaczynające się od ,,/'' mają być traktowane w pewnych warunkach jak wiadomości. Wyłączenie tej opcji sprawia, że wszystko, co zostanie wpisane w oknie rozmowy a zaczyna się od ,,/'' zostanie potraktowane jak komenda. sort_windows typ: bool domyślna wartość: 0 Włączenie tej opcji spowoduje przesuwanie okien przy usunięciu któregoś ze środka i likwidowanie luk w numeracji. Na przykład, gdy mamy okna 1,2,3, to po usunięciu drugiego otrzymamy 1,2 zamiast 1,3. sound_app typ: tekst domyślna wartość: brak Pełna ścieżka do programu odtwarzającego pliki zdefiniowane w zmiennych ,,sound_{msg,chat,sysmsg}_file''. Program musi brać za pierwszy (i jedyny) parametr nazwę pliku. Wavplay i mpg123 doskonale się nadają. sound_chat_file typ: tekst domyślna wartość: brak Plik dźwiękowy odtwarzany w czasie rozmowy. sound_mail_file typ: tekst domyślna wartość: brak Plik dźwiękowy odtwarzany po otrzymaniu nowej wiadomości e-mail. sound_msg_file typ: tekst domyślna wartość: brak Plik dźwiękowy odtwarzany po otrzymaniu wiadomości. sound_notify_file typ: tekst domyślna wartość: brak Plik dźwiękowy odtwarzany po zmianie stanu któregoś ze znajomych. sound_sysmsg_file typ: tekst domyślna wartość: brak Plik dźwiękowy odtwarzany po otrzymaniu wiadomości systemowej. speech_app typ: tekst domyślna wartość: brak Aplikacja używana do odczytywania tekstów wyświetlanych na ekranie. Jej ustawienie powoduje również zmianę motywu na taki, który jest łatwiejszy do wymówienia. subject_prefix typ: tekst domyślna wartość: "## " Prefiks dla tematów wiadomości. subject_reply_prefix typ: tekst domyślna wartość: "Re: " Prefiks dla tematów odpowiedzi na wiadomości. tab_command typ: tekst domyślna wartość: "chat" Komenda, która będzie wstawiana w linii poleceń z kolejną dostępną (patrz zmienna ,,completion_notify'') osobą z listy kontaktów po wciśnięciu klawisza Tab. theme typ: tekst domyślna wartość: brak Zawiera nazwę pliku określającego motyw. time_deviation typ: liczba domyślna wartość: 300 Określa zakres rozbieżności czasu odbieranych względem czasu systemowego w sekundach, który jest traktowany jako chwila aktualna. Ma to wpływ na wyświetlanie timestampów przy odbieranych wiadomościach. Jeśli czas odebranej wiadomości mieści się w +/- podanego zakresu, timestamp nie jest wyświetlany. timestamp typ: tekst domyślna wartość: "\%H:\%M:\%S" W interfejsie ncurses określa format czasu wyświetlanego na początku każdej linii. Dokładny opis formatu zawiera strona manuala strftime(3). Znaki formatu należy poprzedzić znakiem ,\' tak aby nie były traktowane jako znaki formatu (kolory, itp). timestamp_show typ: bool domyślna wartość: 1 Określa czy mamy wyświetlać timestamp, określany zmienną timestamp. window_session_allow typ: liczba domyślna wartość: 0 Określa, w jakich oknach możliwe jest przełączanie sesji (C-x): 0 - tylko w oknie statusu, 1 - okno statusu oraz rozmów, ale w obrębie sesji, które obsługują UID rozmówcy, 2 - okno statusu oraz rozmów, 4 - okno statusu oraz rozmów, ale nastąpi przełączenie do okna statusu. windows_layout typ: tekst, zmienna wewnętrzna, ukryta Zawiera informacje o okienkach. Wpis każdego okna jest rozdzielony znakiem ,,|''. Jeśli okno nie ma przypisanej rozmowy, wpis jest pusty. Jeśli okno nie istnieje, wpis zawiera ,,-''. windows_save typ: bool domyślna wartość: 0 Określa, czy ustawienie okienek ma być zachowywane przy kolejnym uruchomieniu programu. Przy wychodzeniu z programu pojawi się monit o zapisanie zmian. ekg2-0.4~pre+20120506.1/docs/voip.txt000066400000000000000000000012751175142753400166300ustar00rootroot00000000000000// rozmowy gosowe w ekg // (c) copyright 2002 wojtek kaniewski obowizuje lektura pliku ,,dcc.txt'' lista wad: - dziaa tylko na Linuksie ze sterownikami OSS, - dziaa tylko z kartami dwikowymi obsugujcymi full-duplex, czstotliwo prbkowania 8000Hz i 16-bitow rozdzielczo, - dziaa tylko jeli jest zainstalowana biblioteka libgsm skompilowana z opcj -DWAV49 (w PLD wystarczy zainstalowa pakiety libgsm oraz libgsm-devel), - wywietla _duuuuo_ mieci w oknie debugowania. by rozpocz rozmow naley wpisa: dcc voice by zakoczy: dcc close <#numer> gdzie <#numer> to numer poczenia wywietlony przez polecenie ,,dcc show''. $Id$ ekg2-0.4~pre+20120506.1/ekg/000077500000000000000000000000001175142753400147235ustar00rootroot00000000000000ekg2-0.4~pre+20120506.1/ekg/HEADER000066400000000000000000000012661175142753400156030ustar00rootroot00000000000000/* * (C) Copyright 2003-2007 EKG2 authors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ ekg2-0.4~pre+20120506.1/ekg/_unused.c000066400000000000000000000010571175142753400165340ustar00rootroot00000000000000#if 0 /* * Unused stuff. * * For archaeological and sentimental purposes only * */ /* * command_find() * * szuka podanej komendy. */ command_t *command_find(const char *name) { command_t *c; if (!name) return NULL; for (c = commands; c; c = c->next) { if (!xstrcasecmp(c->name, name)) { return c; } } return NULL; } int strcasecmp_pl(const char *cs, const char *ct) { register signed char __res = 0; while ((__res = tolower_pl(*cs) - tolower_pl(*ct++)) == 0 && !*cs++) { if (!*cs++) return(0); } return __res; } #endif ekg2-0.4~pre+20120506.1/ekg/abort.c000066400000000000000000000034211175142753400161760ustar00rootroot00000000000000/* (C) Copyright 2011 Marcin Owsiany * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "abort.h" #include #define NUM_ABORT_HANDLERS 10 static struct { abort_handler handler; plugin_t *plugin; } abort_handlers[NUM_ABORT_HANDLERS]; int ekg2_register_abort_handler(abort_handler handler, plugin_t *plugin) { size_t i; for (i = 0; i < NUM_ABORT_HANDLERS; i++) { if (! abort_handlers[i].handler) { abort_handlers[i].handler = handler; abort_handlers[i].plugin = plugin; return 1; } } return 0; } void ekg2_run_all_abort_handlers(void) { size_t i; for (i = 0; i < NUM_ABORT_HANDLERS; i++) { if (abort_handlers[i].handler) { (*abort_handlers[i].handler)(); } } } int ekg2_unregister_abort_handlers_for_plugin(plugin_t *plugin) { int unregistered_handlers_count = 0; size_t i; for (i = 0; i < NUM_ABORT_HANDLERS; i++) { if (abort_handlers[i].handler && abort_handlers[i].plugin == plugin) { abort_handlers[i].handler = NULL; unregistered_handlers_count++; } } return unregistered_handlers_count; } /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: * vim: noet */ ekg2-0.4~pre+20120506.1/ekg/abort.h000066400000000000000000000044551175142753400162130ustar00rootroot00000000000000/* (C) Copyright 2011 Marcin Owsiany * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __EKG_ABORT_H #define __EKG_ABORT_H /** * Statically allocated handlers for fatal signal handling. * * The reason for all this is to avoid heap references in fatal signal handlers * (which are a big no-no), but still allow plugins to do some rudimentary * cleanup. See @sa handle_fatal_signal() for more information. * * Plugins, during initialization, register their abort handlers using * ekg2_register_abort_handler(). * * The registered abort handlers will be called by core in cases such as * receiving a SIGSEGV or SIGABRT. * * Core will also unregister all handlers for a given plugin right before * unregistering the plugin itself, but there is no harm in the plugin * unregistering its own abort handlers if it knows of a more appropriate time. */ #include "plugins.h" #include G_BEGIN_DECLS typedef void (*abort_handler)(void); /** * Statically register the abort @a handler function for the @a plugin. * * The @a handler MUST be async-signal-safe (see signal(7)). * * @return 1 if successful, 0 if there was no space left. */ int ekg2_register_abort_handler(abort_handler handler, plugin_t *plugin); /** * Run all registered abort handlers (possibly none). * * No particular order of invocation is guaranteed. */ void ekg2_run_all_abort_handlers(void); /** * Unregister all abort handlers for @a plugin. * * @return the number of unregistered handlers. */ int ekg2_unregister_abort_handlers_for_plugin(plugin_t *plugin); G_END_DECLS #endif /* __EKG_ABORT_H */ /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/ekg/bindings.c000066400000000000000000000070721175142753400166720ustar00rootroot00000000000000#include "ekg2.h" #include struct binding *bindings = NULL; binding_added_t *bindings_added; /* * binding_list() * * wyświetla listę przypisanych komend. */ void binding_list(int quiet, const char *name, int all) { struct binding *b; int found = 0; if (!bindings) printq("bind_seq_list_empty"); for (b = bindings; b; b = b->next) { if (name) { if (xstrcasestr(b->key, name)) { printq("bind_seq_list", b->key, b->action); found = 1; } continue; } if (!b->internal || (all && b->internal)) printq("bind_seq_list", b->key, b->action); } if (name && !found) { for (b = bindings; b; b = b->next) { if (xstrcasestr(b->action, name)) printq("bind_seq_list", b->key, b->action); } } } /* * binding_quick_list() * * wyświetla krótką i zwięzła listę dostępnych, zajętych i niewidocznych * ludzi z listy kontaktów. */ int binding_quick_list(int a, int b) { string_t list = string_init(NULL); userlist_t *ul; session_t *s; for (s = sessions; s; s = s->next) { for (ul = s->userlist; ul; ul = ul->next) { userlist_t *u = ul; const char *format; if (!u->nickname) continue; format = format_find(ekg_status_label(u->status, NULL, "quick_list_")); if (format_ok(format)) { char *tmp = format_string(format, u->nickname); string_append(list, tmp); xfree(tmp); } } } if (list->len > 0) print("quick_list", list->str); string_free(list, 1); return 0; } int binding_help(int a, int b) { print("help_quick"); return 0; } static LIST_FREE_ITEM(binding_free_item, struct binding *) { xfree(data->key); xfree(data->action); xfree(data->arg); xfree(data->default_action); xfree(data->default_arg); } static LIST_FREE_ITEM(binding_added_free_item, binding_added_t *) { xfree(data->sequence); } static __DYNSTUFF_LIST_DESTROY(bindings, struct binding, binding_free_item); /* bindings_destroy() */ static __DYNSTUFF_LIST_DESTROY(bindings_added, binding_added_t, binding_added_free_item); /* bindings_added_destroy() */ /** * binding_free() * * Free memory allocated for key bindings. */ void binding_free() { bindings_destroy(); bindings_added_destroy(); } COMMAND(cmd_bind) { if (match_arg(params[0], 'a', ("add"), 2)) { if (!params[1] || !params[2]) { printq("not_enough_params", name); return -1; } query_emit(NULL, "binding-command", (int) 1, params[1], params[2], quiet); /* ncurses_binding_add(p2, p3, 0, quiet); */ return 0; } if (match_arg(params[0], 'd', ("delete"), 2)) { if (!params[1]) { printq("not_enough_params", ("bind")); return -1; } query_emit(NULL, "binding-command", (int) 0, params[1], NULL, quiet); /* ncurses_binding_delete(p2, quiet); */ return 0; } if (match_arg(params[0], 'L', ("list-default"), 5)) { binding_list(quiet, params[1], 1); return 0; } if (match_arg(params[0], 'S', ("set"), 2)) { window_lock_dec(window_find_s(session, target)); /* this is interactive command. XXX, what about window_current? */ query_emit(NULL, "binding-set", params[1], NULL, quiet); return 0; } if (match_arg(params[0], 'l', ("list"), 2)) { binding_list(quiet, params[1], 0); return 0; } if (match_arg(params[0], 'e', ("exec"), 2)) { struct binding *b; if (!params[1]) { printq("not_enough_params", ("bind")); return -1; } if (!bindings) { printq("bind_seq_list_empty"); return 0; } for (b = bindings; b; b = b->next) { if (xstrcasestr(b->action, params[1]) && b->function) { b->function(NULL); return 0; } } /* XXX not found */ } binding_list(quiet, params[0], 0); return 0; } ekg2-0.4~pre+20120506.1/ekg/bindings.h000066400000000000000000000016651175142753400167010ustar00rootroot00000000000000#ifndef __EKG_BINDINGS_H #define __EKG_BINDINGS_H #ifdef __cplusplus extern "C" { #endif #define BINDING_FUNCTION(x) void x(const char *arg) struct binding { struct binding *next; char *key; char *action; /* akcja */ unsigned int internal : 1; /* czy domyślna kombinacja? */ void (*function)(const char *arg); /* funkcja obsługująca */ char *arg; /* argument funkcji */ char *default_action; /* domyślna akcja */ void (*default_function)(const char *arg); /* domyślna funkcja */ char *default_arg; /* domyślny argument */ }; typedef struct binding_added { struct binding_added *next; char *sequence; struct binding *binding; } binding_added_t; extern struct binding *bindings; extern binding_added_t *bindings_added; void binding_list(int quiet, const char *name, int all); int binding_help(int a, int b); int binding_quick_list(int a, int b); void binding_free(); #ifdef __cplusplus } #endif #endif ekg2-0.4~pre+20120506.1/ekg/commands.c000066400000000000000000002626761175142753400167130ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2001-2005 Wojtek Kaniewski * Robert J. Wony * Pawe Maziarz * Wojciech Bojdo * Piotr Wysocki * Dawid Jarosz * Piotr Domagalski * Kuba Kowalski * Piotr Kupisiewicz * Leszek Krupiski * Adam Mikuta * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #ifndef NO_POSIX_SYSTEM #include #include #endif #include #include #ifndef NO_POSIX_SYSTEM #include #include #include #endif #include #include #include #include #include #include #include #include #ifndef NO_POSIX_SYSTEM #include #endif #ifdef __sun #include #endif #ifdef __FreeBSD__ # include /* kvm_ funcs */ # include /* _POSIX2_LINE_MAX */ # include # include /* KERN_PROC_PID */ # include #endif #include "scripts.h" #include "internal.h" #include "net.h" char *send_nicks[SEND_NICKS_MAX] = { NULL }; int send_nicks_count = 0, send_nicks_index = 0; static int quit_command = 0; GSList *commands = NULL; static gint command_compare(gconstpointer a, gconstpointer b) { const command_t *data1 = (const command_t *) a; const command_t *data2 = (const command_t *) b; return xstrcasecmp(data1->name, data2->name); } static void list_command_free(void *_data) { command_t *data = (command_t *) _data; g_strfreev(data->params); g_strfreev(data->possibilities); xfree(data); } static void commands_add(command_t *c) { commands = g_slist_insert_sorted(commands, c, command_compare); } void commands_remove(command_t *c) { commands = g_slist_remove(commands, c); list_command_free(c); } void commands_destroy() { g_slist_free_full(commands, list_command_free); } /* * match_arg() * * sprawdza, czy dany argument funkcji pasuje do podanego. */ int match_arg(const char *arg, char shortopt, const char *longopt, int longoptlen) { if (!arg || *arg != '-') return 0; arg++; if (*arg == '-') { int len = xstrlen(++arg); if (longoptlen > len) len = longoptlen; return !xstrncmp(arg, longopt, len); } return (*arg == shortopt) && (*(arg + 1) == 0); } /* * tabnick_add() * * dodaje do listy nickw dopenianych automagicznie tabem. */ void tabnick_add(const char *nick) { int i; for (i = 0; i < send_nicks_count; i++) if (send_nicks[i] && !xstrcmp(nick, send_nicks[i])) { tabnick_remove(nick); break; } if (send_nicks_count == SEND_NICKS_MAX) { xfree(send_nicks[SEND_NICKS_MAX - 1]); send_nicks_count--; } memmove(&send_nicks[1], &send_nicks[0], send_nicks_count * sizeof(send_nicks[0]) ); send_nicks_count++; send_nicks[0] = xstrdup(nick); } /* * tabnick_remove() * * usuwa z listy dopenianych automagicznie tabem. */ void tabnick_remove(const char *nick) { int i, j; for (i = 0; i < send_nicks_count; i++) { if (send_nicks[i] && !xstrcmp(send_nicks[i], nick)) { xfree(send_nicks[i]); for (j = i + 1; j < send_nicks_count; j++) send_nicks[j - 1] = send_nicks[j]; send_nicks_count--; send_nicks[send_nicks_count] = NULL; break; } } } /* * tabnick_flush() * * czyci list nickw dopenianych tabem. */ static void tabnick_flush() { int i; for (i = 0; i < send_nicks_count; i++) { xfree(send_nicks[i]); send_nicks[i] = NULL; } send_nicks_count = 0; send_nicks_index = 0; } static COMMAND(cmd_tabclear) { int i; if (!params[0]) { tabnick_flush(); return 0; } if (match_arg(params[0], 'o', ("offline"), 2)) { for (i = 0; i < send_nicks_count; i++) { userlist_t *u = NULL; if (send_nicks[i]) u = userlist_find(session, send_nicks[i]); /* I think we should also remove errors and likes here * if I'm wrong, change the macro to comparison */ if (!u || !EKG_STATUS_IS_NA(u->status)) continue; tabnick_remove(send_nicks[i]); } return 0; } printq("invalid_params", name, params[0]); return -1; } /* XXX, rewritten, need checking */ COMMAND(cmd_add) { int params_free = 0; /* zmienilimy params[] i trzeba zwolni */ int result = 0; userlist_t *u = NULL; /* XXX, just in case? */ if (!target) target = window_current->target; /* XXX, check this damn session_current. */ if (!session) session = session_current; if (!session) return -1; /* If we didn't have params[1] and we params[0] isn't option (for example not --find) and we have target * get uid from current window, get nickname from params[0] * * Code for getting more options from params[1] && params[2] were senseless, cause we have !params[1] ;( */ if (params[0][0] != '-' && !params[1] && target) { const char *name = params[0]; params_free = 1; params = xmalloc(3 * sizeof(char *)); params[0] = target; params[1] = name; /* params[2] = NULL */ } /* if we have passed -f [lastfound] then get uid, nickname and other stuff from searches... */ /* if params[1] passed than it should be used as nickname */ /* XXX, we need to make it session-visible only.. or at least protocol-visible only. * cause we maybe implement it in jabber */ if (match_arg(params[0], 'f', ("find"), 2)) { const char *nickname; if (!last_search_uid || (!last_search_nickname && !params[1])) { printq("search_no_last"); return -1; } nickname = last_search_nickname ? strip_spaces(last_search_nickname) : params[1]; if (nickname && nickname[0] == '\0') nickname = params[1]; if (!nickname) { printq("search_no_last_nickname"); return -1; } params_free = 1; params = xmalloc(4 * sizeof(char *)); params[0] = last_search_uid; params[1] = nickname; /* construct params[2] -f FIRST_NAME -l LAST_NAME */ params[2] = saprintf("-f \"%s\" -l \"%s\"", ((last_search_first_name) ? last_search_first_name : ("")), ((last_search_last_name) ? last_search_last_name : (""))); /* params[3] = NULL; */ } if (!params[1]) { printq("not_enough_params", name); result = -1; goto cleanup; } if (!valid_plugin_uid(session->plugin, params[0])) { printq("invalid_uid"); result = -1; goto cleanup; } if (!valid_nick(params[1])) { printq("invalid_nick"); result = -1; goto cleanup; } /* XXX, parse params[2] */ if (((u = userlist_find(session, params[0])) && u->nickname) || ((u = userlist_find(session, params[1])) && u->nickname)) { if (!xstrcasecmp(params[1], u->nickname) && !xstrcasecmp(params[0], u->uid)) printq("user_exists", params[1], session_name(session)); else printq("user_exists_other", params[1], format_user(session, u->uid), session_name(session)); result = -1; goto cleanup; } /* kto by tylko ignorowany/blokowany, nadajmy mu nazw */ if (u) { xfree(u->nickname); u->nickname = xstrdup(params[1]); } if (u || userlist_add(session, params[0], params[1])) { char *uid = xstrdup(params[0]); query_emit(NULL, "userlist-added", &uid, ¶ms[1], &quiet); query_emit(NULL, "add-notify", &session->uid, &uid); xfree(uid); printq("user_added", params[1], session_name(session)); tabnick_remove(params[0]); config_changed = 1; } cleanup: if (params_free) { xfree((char*) params[2]); xfree(params); } return result; } static COMMAND(cmd_alias) { int append = match_arg(params[0], 'A', ("append"), 2); if (append || match_arg(params[0], 'a', ("add"), 2)) { if (!params[1] || !xstrchr(params[1], ' ')) { printq("not_enough_params", name); return -1; } if (alias_add(params[1], quiet, append)) return -1; config_changed = 1; return 0; } if (match_arg(params[0], 'd', ("del"), 2)) { int ret; if (!params[1]) { printq("not_enough_params", name); return -1; } if (!xstrcmp(params[1], "*")) ret = alias_remove(NULL, quiet); else ret = alias_remove(params[1], quiet); if (ret) return -1; config_changed = 1; return 0; } if (!params[0] || match_arg(params[0], 'l', ("list"), 2) || params[0][0] != '-') { const char *aname; int count = 0; alias_t *a; if (match_arg(params[0], 'l', ("list"), 2)) aname = params[1]; else aname = params[0]; /* it can be NULL */ for (a = aliases; a; a = a->next) { list_t m; int first = 1; char *tmp; if (aname && xstrcasecmp(aname, a->name)) continue; tmp = xmalloc(xstrlen(a->name) + 1); memset(tmp, ' ', xstrlen(a->name)); for (m = a->commands; m; m = m->next) { printq((first) ? "aliases_list" : "aliases_list_next", a->name, (char *) m->data, tmp); first = 0; count++; } xfree(tmp); } if (!count) { if (aname) { printq("aliases_noexist", aname); return -1; } printq("aliases_list_empty"); } return 0; } printq("invalid_params", name, params[0]); return -1; } static COMMAND(cmd_status) { struct tm *t; time_t n; int now_days; char buf1[100] = { '\0' }; const char *format; session_t *s = NULL; printq("show_status_header"); s = params[0] ? session_find(params[0]) : session; if (params[0] && !s) { printq("invalid_uid", params[0]); return -1; } if (config_profile) printq("show_status_profile", config_profile); n = time(NULL); t = localtime(&n); now_days = t->tm_yday; if (s) { query_emit(s->plugin, "status-show", &s->uid); /* when we connected [s->connected != 0] to server or when we lost last connection [s->connected == 0] [time from s->last_conn] */ if (s->last_conn) { char buf[100] = { '\0' }; t = localtime(&s->last_conn); format = format_find((t->tm_yday == now_days) ? "show_status_last_conn_event_today" : "show_status_last_conn_event"); if (format_ok(format) && !strftime(buf, sizeof(buf), format, t)) xstrcpy(buf, "TOOLONG"); printq((s->connected) ? "show_status_connected_since" : "show_status_disconnected_since", buf); } } t = localtime(&ekg_started); format = format_find((t->tm_yday == now_days) ? "show_status_ekg_started_today" : "show_status_ekg_started"); if (format_ok(format) && !strftime(buf1, sizeof(buf1), format, t)) xstrcpy(buf1, "TOOLONG"); printq("show_status_ekg_started_since", buf1); printq("show_status_footer"); return 0; } static COMMAND(cmd_del) { userlist_t *u; char *tmp; int del_all = ((params[0] && !xstrcmp(params[0], "*")) ? 1 : 0); if (!session) return -1; if (!params[0]) { printq("not_enough_params", name); return -1; } if (del_all) { userlist_t *ul; for (ul = session->userlist; ul; ) { userlist_t *u = ul; userlist_t *next = ul->next; char *p0; char *tmp; p0 = xstrdup(u->nickname); tmp = xstrdup(u->uid); query_emit(NULL, "userlist-removed", &p0, &tmp); xfree(tmp); xfree(p0); userlist_remove(session, u); ul = next; } printq("user_cleared_list", session_name(session)); tabnick_flush(); config_changed = 1; return 0; } if (!(u = userlist_find(session, params[0])) || !u->nickname) { printq("user_not_found", params[0]); return -1; } tmp = xstrdup(u->uid); printq("user_deleted", params[0], session_name(session)); tabnick_remove(u->uid); tabnick_remove(u->nickname); config_changed = 1; userlist_remove(session, u); query_emit(NULL, "userlist-removed", ¶ms[0], &tmp); query_emit(NULL, "remove-notify", &session->uid, &tmp); xfree(tmp); return 0; } /** * cmd_exec_info_t is internal structure, containing information about processes started using /exec. */ typedef struct { char *target; /**< Target UID, if redirecting output to someone */ char *session; /**< Session UID */ int quiet; /**< Whether to be quiet (i.e. don't print info on exit) */ string_t buf; /**< Buffer */ char *cmdexec; /**< Command to execute with result */ gint ref; /**< Reference count */ } cmd_exec_info_t; static WATCHER_LINE(cmd_exec_watch_handler) /* stay */ { cmd_exec_info_t *i = data; int quiet = (i) ? i->quiet : 0; if (!i) return -1; if (type == 1) { close(fd); if ((i->ref--) > 0) return 0; if (i->buf) { command_exec_format(i->target, session_find(i->session), quiet, ("/ %s"), i->buf->str); string_free(i->buf, 1); } xfree(i->cmdexec); xfree(i->target); xfree(i->session); g_slice_free(cmd_exec_info_t, i); return 0; } if (i->cmdexec) { if (*watch) /* XXX: maybe strip only last \n ? */ command_exec_format(i->target, session_find(i->session), quiet, ("/%s %s"), i->cmdexec, watch); } else if (i->buf) { string_append(i->buf, watch); string_append(i->buf, ("\r\n")); } else if (i->target) command_exec_format(i->target, session_find(i->session), quiet, ("/ %s"), watch); else if (*watch) printq("exec", watch); return 0; } static void cmd_exec_child_handler(GPid pid, gint status, gpointer data) { gchar *name = data; int quiet = (name && name[0] == '^'); printq("process_exit", ekg_itoa(pid), name, ekg_itoa(status)); } COMMAND(cmd_exec) { pid_t pid; if (params[0]) { int buf = 0, cmdx = 0, add_commandline = 0; const char *command = params[0], *__target = NULL, *cmdexec = NULL; char **args = NULL; cmd_exec_info_t *i; watch_t *w; GError *err = NULL; gint outfd, errfd; if (params[0][0] == '-') { int big_match = 0; args = (char **) params; if (match_arg(args[0], 'M', ("MSG"), 2) || (buf = match_arg(args[0], 'B', ("BMSG"), 2) || (cmdx = match_arg(args[0], 'C', ("CMD"), 2)))) big_match = add_commandline = 1; if (big_match || match_arg(args[0], 'm', ("msg"), 2) || (buf = match_arg(args[0], 'b', ("bmsg"), 2)) || (cmdx = match_arg(args[0], 'c', ("cmd"), 2))) { const char *uid; if (!args[1] || !args[2]) { printq("not_enough_params", name); return -1; } if (cmdx) { cmdexec = args[1]; __target = target; } else if (!(uid = get_uid(session, args[1]))) { printq("user_not_found", args[1]); return -1; } else __target = uid; command = args[2]; } else { printq("invalid_params", name, args[0]); return -1; } } { #ifndef NO_POSIX_SYSTEM const gchar *argv[] = { "sh", "-c", command, NULL }; #else const gchar *argv[] = { "command", "/c", command, NULL }; /* XXX: untested, use %COMSPEC, etc. */ #endif /* glib seems to lack const in prototype */ gchar **dupargv = g_strdupv((gchar**) argv); gchar *strippedargv[4]; memcpy(strippedargv, dupargv, sizeof(strippedargv)); if (strippedargv[2][0] == '^') strippedargv[2]++; if (!g_spawn_async_with_pipes(NULL, strippedargv, NULL, G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH, NULL, NULL, &pid, NULL, &outfd, &errfd, &err)) { printq("exec_error", err->message); g_error_free(err); g_strfreev(dupargv); return -1; } g_strfreev(dupargv); } i = g_slice_new(cmd_exec_info_t); i->quiet = quiet; i->target = g_strdup(__target); i->cmdexec = g_strdup(cmdexec); i->session = g_strdup(session_uid_get(session)); i->ref = 2; /* outfd & errfd */ i->buf = buf ? string_init(NULL) : NULL; watch_add_line(NULL, outfd, WATCH_READ_LINE, cmd_exec_watch_handler, i); w = watch_add_line(NULL, errfd, WATCH_READ_LINE, cmd_exec_watch_handler, i); if (add_commandline) { char *tmp = format_string(format_find("exec_prompt"), ((command[0] == '^') ? command + 1 : command)); string_append(w->buf, tmp); g_free(tmp); } fcntl(outfd, F_SETFL, O_NONBLOCK); fcntl(errfd, F_SETFL, O_NONBLOCK); ekg_child_add(NULL, "%s", pid, cmd_exec_child_handler, g_strdup(command), g_free, command); } else ekg_children_print(quiet); return 0; } /** * cmd_eval() * * Execute space seperated commands from @a params[0]
* If you want add params to command use " " sample: /eval "first_commamnd --first_param --second_param" second_command third_command * * Handler for: /eval command. * * @param params [0] - commands to execute * * @return 0 */ static COMMAND(cmd_eval) { int i; char **argv; argv = array_make(params[0], (" "), 0, 1, 1); for (i = 0; argv[i]; i++) command_exec(NULL, session, argv[i], 0); g_strfreev(argv); return 0; } static COMMAND(cmd_for) { int for_all = 0; if (!xstrcmp(params[1], "*")) for_all = 1; if (match_arg(params[0], 's', ("sessions"), 2)) { char *param = (char *) params[2]; int next_is_for = 0; if (param[0] == '/') param++; if (!xstrncasecmp(param, name, xstrlen(name))) next_is_for = 1; if (for_all) { session_t *s; if (!sessions) { printq("session_list_empty"); return -2; } for (s = sessions; s; s = s->next) { char *for_command; if (!s || !s->uid) continue; if (!next_is_for) for_command = format_string(params[2], session_alias_uid(s), s->uid); else for_command = xstrdup(params[2]); command_exec(NULL, s, for_command, 0); xfree(for_command); } } else { char **tmp = array_make(params[1], ",", 0, 0, 0); int i; session_t **s; s = xmalloc(sizeof(session_t *) * g_strv_length(tmp)); /* first we are checking all of the parametrs */ for (i = 0; tmp[i]; i++) { if (!(s[i] = session_find(tmp[i]))) { printq("session_doesnt_exist", tmp[i]); xfree(s); g_strfreev(tmp); return -1; } } for (i = 0; tmp[i]; i++) { char *for_command; if (!s[i] || !s[i]->uid) continue; if (!next_is_for) for_command = format_string(params[2], session_alias_uid(s[i]), s[i]->uid); else for_command = xstrdup(params[2]); command_exec(NULL, s[i], for_command, 0); xfree(for_command); } g_strfreev(tmp); xfree(s); } } else if (match_arg(params[0], 'u', ("users"), 2)) { char *param = (char *) params[2]; int next_is_for = 0; if (param[0] == '/') param++; if (!xstrncasecmp(param, name, xstrlen(name))) next_is_for = 1; if (!session) { return -1; } if (!session->userlist) { printq("list_empty"); return -1; } if (for_all) { userlist_t *ul; for (ul = session->userlist; ul; ul = ul->next) { userlist_t *u = ul; char *for_command; if (!u || !u->uid) continue; if (!next_is_for) for_command = format_string(params[2], (u->nickname) ? u->nickname : u->uid, u->uid); else for_command = xstrdup(params[2]); command_exec(NULL, session, for_command, 0); xfree(for_command); } } else { char **tmp = array_make(params[1], ",", 0, 0, 0); int i; userlist_t **u; u = xmalloc(sizeof(userlist_t *) * g_strv_length(tmp)); /* first we are checking all of the parametrs */ for (i = 0; tmp[i]; i++) { if (!(u[i] = userlist_find(session, tmp[i]))) { printq("user_not_found", tmp[i]); g_strfreev(tmp); xfree(u); return -1; } } for (i = 0; tmp[i]; i++) { char *for_command; if (!u[i] || !u[i]->uid) continue; if (!next_is_for) for_command = format_string(params[2], (u[i]->nickname) ? u[i]->nickname : u[i]->uid, u[i]->uid); else for_command = xstrdup(params[2]); command_exec(NULL, session, for_command, 0); xfree(for_command); } g_strfreev(tmp); xfree(u); } } else if (match_arg(params[0], 'w', ("windows"), 2)) { char *param = (char *) params[2]; int next_is_for = 0; if (param[0] == '/') param++; if (!xstrncasecmp(param, name, xstrlen(name))) next_is_for = 1; if (!windows) { return -1; } if (for_all) { window_t *w, *next; for (w = windows; w; w = next) { char *for_command; next = w->next; /* this shall protect us from window killing (current one, not next) */ if (!w || !w->target || !w->session) continue; if (!next_is_for) /* XXX, get_uid(), get_nickname() */ for_command = format_string(params[2], get_nickname(w->session, w->target), get_uid(w->session, w->target)); else for_command = xstrdup(params[2]); command_exec(NULL, w->session, for_command, 0); xfree(for_command); } } else { char **tmp = array_make(params[1], ",", 0, 0, 0); int i; window_t **w; w = xmalloc(sizeof(window_t *) * g_strv_length(tmp)); /* first we are checking all of the parametrs */ for (i = 0; tmp[i]; i++) { if (!(w[i] = window_exist(atoi(tmp[i])))) { printq("window_doesnt_exist", tmp[i]); g_strfreev(tmp); xfree(w); return -1; } } for (i = 0; tmp[i]; i++) { char *for_command; if (!w[i] || !w[i]->target || !w[i]->session) continue; if (!next_is_for) /* XXX, get_uid(), get_nickname() */ for_command = format_string(params[2], get_nickname(w[i]->session, w[i]->target), get_uid(w[i]->session, w[i]->target)); else for_command = xstrdup(params[2]); command_exec(NULL, w[i]->session, for_command, 0); xfree(for_command); } g_strfreev(tmp); xfree(w); } } else { printq("invalid_params", name, params[0]); return -1; } return 0; } static COMMAND(cmd_help) { GSList *cl; if (params[0]) { const char *p = (params[0][0] == '/' && xstrlen(params[0]) > 1) ? params[0] + 1 : params[0]; int plen; if (!xstrcasecmp(p, ("set")) && params[1]) { if (!quiet) variable_help(params[1]); return 0; } /* vvv - allow /help sess sth */ if (!xstrncasecmp(p, ("session"), xstrlen(p) > 3 ? xstrlen(p) : 3) && params[1]) { if (!quiet) session_help(session, params[1]); return 0; } if (session) plen = (int)(xstrchr(session->uid, ':') - session->uid) + 1; else plen = 0; for (cl = commands; cl; cl = cl->next) { command_t *c = cl->data; if (!xstrcasecmp(c->name, p) && (c->flags & COMMAND_ISALIAS)) { printq("help_alias", p); return -1; } if (!xstrcasecmp(c->name, p) && (c->flags & COMMAND_ISSCRIPT)) { printq("help_script", p); return -1; } if (!(c->flags & COMMAND_ISALIAS) && (!xstrcasecmp(c->name, p) || (session && !xstrncmp(c->name, session->uid, plen) && !xstrcasecmp(c->name + plen, p)))) { GDataInputStream *f; gchar *params_help = NULL, *params_help_s, *brief = NULL, *tmp = NULL; const gchar *line, *seeking_name; string_t s; int found = 0; if (c->plugin && c->plugin->name) { char *tmp2; if (!(f = help_open("commands", c->plugin->name))) { print("help_command_file_not_found_plugin", c->plugin->name); return -1; } tmp2 = xstrchr(c->name, ':'); if (!tmp2) seeking_name = c->name; else seeking_name = tmp2 + 1; } else { if (!(f = help_open("commands", NULL))) { print("help_command_file_not_found"); return -1; } seeking_name = c->name; } while ((line = read_line(f))) { if (!xstrcasecmp(line, seeking_name)) { found = 1; break; } } if (!found) { g_object_unref(f); print("help_command_not_found", c->name); return -1; } line = read_line(f); if ((tmp = xstrstr(line, (": ")))) params_help = xstrdup(tmp + 2); else params_help = xstrdup(("")); params_help_s = strip_spaces(params_help); line = read_line(f); if ((tmp = xstrstr(line, (": ")))) brief = xstrdup(tmp + 2); else brief = xstrdup(("?")); tmp = NULL; if (xstrstr(brief, ("%"))) tmp = format_string(brief); if (!xstrcmp(brief, (""))) { xfree(brief); brief = xstrdup(("?")); } if (xstrcmp(params_help_s, (""))) printq("help", (c->name) ? (c->name) : (""), params_help_s, tmp ? tmp : brief, ""); else printq("help_no_params", (c->name) ? (c->name) : (""), tmp ? tmp : brief, ("")); xfree(brief); xfree(params_help); xfree(tmp); s = string_init(NULL); while ((line = read_line(f))) { if (line[0] != ('\t')) break; if (!xstrncmp(line, ("\t- "), 3) && xstrcmp(s->str, (""))) { print("help_command_body", line); string_clear(s); } if (!xstrncmp(line, ("\t"), 1) && xstrlen(line) == 1) { string_append(s, "\n\r"); continue; } string_append(s, line + 1); if (line[xstrlen(line) - 1] != ' ') string_append_c(s, ' '); } if (xstrcmp(s->str, (""))) { char *tmp = format_string(s->str); printq("help_command_body", tmp); xfree(tmp); } g_object_unref(f); string_free(s, 1); return 0; } } } for (cl = commands; cl; cl = cl->next) { command_t *c = cl->data; if (xisalnum(*c->name) && !(c->flags & COMMAND_ISALIAS)) { char *blah = NULL; GDataInputStream *f; gchar *params_help, *params_help_s, *brief, *tmp = NULL; const gchar *line, *seeking_name; int found = 0; if (c->plugin && c->plugin->name) { char *tmp2; if (!(f = help_open("commands", c->plugin->name))) continue; tmp2 = xstrchr(c->name, (':')); if (!tmp2) seeking_name = c->name; else seeking_name = tmp2 + 1; } else { if (!(f = help_open("commands", NULL))) continue; seeking_name = c->name; } while ((line = read_line(f))) { if (!xstrcasecmp(line, seeking_name)) { found = 1; break; } } if (!found) { g_object_unref(f); continue; } line = read_line(f); if ((tmp = xstrstr(line, (": ")))) params_help = xstrdup(tmp + 2); else params_help = xstrdup(("")); params_help_s = strip_spaces(params_help); line = read_line(f); if ((tmp = xstrstr(line, (": ")))) brief = xstrdup(tmp + 2); else brief = xstrdup(("?")); if (xstrstr(brief, ("%"))) blah = format_string(brief); if (!xstrcmp(brief, (""))) { xfree(brief); brief = xstrdup(("?")); } if (xstrcmp(params_help_s, (""))) printq("help", c->name ? (c->name) : (""), params_help_s, blah ? blah : brief, ("")); else printq("help_no_params", (c->name) ? (c->name) : (""), blah ? blah : brief, ("")); xfree(blah); xfree(brief); xfree(params_help); g_object_unref(f); } } printq("help_footer"); printq("help_quick"); return 0; } static COMMAND(cmd_ignore) { const char *uid; if (*name == 'i' || *name == 'I') { int flags, modified = 0; if (!params[0]) { userlist_t *ul; int i = 0; for (ul = session->userlist; ul; ul = ul->next) { userlist_t *u = ul; int level; if (!(level = ignored_check(session, u->uid))) continue; i = 1; printq("ignored_list", format_user(session, u->uid), ignore_format(level)); } if (!i) printq("ignored_list_empty"); return 0; } if (params[0][0] == '#') { return command_exec_format(NULL, NULL, quiet, ("/conference --ignore %s"), params[0]); } if (!(uid = get_uid(session, params[0]))) { printq("user_not_found", params[0]); return -1; } if ((flags = ignored_check(session, uid))) modified = 1; if (params[1]) { int __flags = ignore_flags(params[1]); if (!__flags) { printq("invalid_params", name, params[1]); return -1; } flags |= __flags; } else flags = IGNORE_ALL; if (modified) { uid = xstrdup(uid); ignored_remove(session, uid); } if (!ignored_add(session, uid, flags)) { if (modified) { printq("ignored_modified", format_user(session, uid)); /* We've just got this from xstrdup(), so safe to free here */ xfree((void *) uid); } else printq("ignored_added", format_user(session, uid)); config_changed = 1; } } else { int unignore_all = ((params[0] && !xstrcmp(params[0], "*")) ? 1 : 0); if (!params[0]) { printq("not_enough_params", name); return -1; } if (params[0][0] == '#') { return command_exec_format(NULL, NULL, quiet, ("/conference --unignore %s"), params[0]); } if (!unignore_all && !(uid = get_uid(session, params[0]))) { printq("user_not_found", params[0]); return -1; } if (unignore_all) { userlist_t *ul; int x = 0; for (ul = session->userlist; ul; ) { userlist_t *u = ul; userlist_t *next = ul->next; if (!ignored_remove(session, u->uid)) x = 1; ul = next; } if (x) { printq("ignored_deleted_all"); config_changed = 1; } else { printq("ignored_list_empty"); return -1; } return 0; } if (!ignored_remove(session, uid)) { printq("ignored_deleted", format_user(session, params[0])); config_changed = 1; } else { printq("error_not_ignored", format_user(session, params[0])); return -1; } } return 0; } COMMAND(cmd_list) { userlist_t *ul; int count = 0, show_all = 1, show_away = 0, show_active = 0, show_inactive = 0, show_invisible = 0, show_descr = 0, show_blocked = 0, show_offline = 0, show_online = 0, i; char *show_group = NULL; const char *tmp; metacontact_t *m = NULL; const char *params0 = params[0]; const char *__first_name, *__last_name; int __ip, __port, __last_ip, __last_port; if (!params0 && window_current->target) { params0 = window_current->target; } if (params0 && (*params0 != '-' || userlist_find(session, params0))) { char *status; const char *group = params0; userlist_t *u; ekg_resource_t *rl; int invert = 0; /* list !@grupa */ if (group[0] == '!' && group[1] == '@') { group++; invert = 1; } /* list @grupa */ if (group[0] == '@' && xstrlen(group) > 1) { string_t members = string_init(NULL); char *__group; int count = 0; for (ul = session->userlist; ul; ul = ul->next) { u = ul; if (u->groups || invert) { if ((!invert && ekg_group_member(u, group + 1)) || (invert && !ekg_group_member(u, group + 1))) { if (count++) string_append(members, ", "); string_append(members, u->nickname); } } } __group = saprintf("%s%s", ((invert) ? "!" : ""), group + 1); if (count) printq("group_members", __group, members->str); else printq("group_empty", __group); xfree(__group); string_free(members, 1); return 0; } if (params0 && (tmp = xstrrchr(params0, '/'))) { char *session_name = xstrndup(params0, xstrlen(params0) - xstrlen(tmp)); if (!session_find(session_name)) goto next; tmp++; session = session_find(session_name); if (!(u = userlist_find(session, tmp)) || !u->nickname) { printq("user_not_found", tmp); xfree(session_name); return -1; } xfree(session_name); goto list_user; } next: /* list _metacontact */ if (params0 && (m = metacontact_find(params0))) { metacontact_item_t *i; i = metacontact_find_prio(m); if (!i) { printq("metacontact_item_list_empty"); return -1; } u = userlist_find_n(i->s_uid, i->name); status = format_string(format_find(ekg_status_label(u->status, u->descr, "metacontact_info_")), get_user_name(u), u->descr); printq("metacontact_info_header", params0); printq("metacontact_info_status", status); printq("metacontact_info_footer", params0); xfree(status); return 0; } if (!(u = userlist_find(session, params0)) || !u->nickname) { printq("user_not_found", params0); return -1; } list_user: status = format_string(format_find(ekg_status_label(u->status, u->descr, "user_info_")), get_user_name(u), u->descr); printq("user_info_header", u->nickname, u->uid); if (u->nickname && xstrcmp(u->nickname, u->nickname)) printq("user_info_nickname", u->nickname); printq("user_info_status", status); if (u->status_time && EKG_STATUS_IS_NA(u->status)) { struct tm *status_time; char buf[100]; status_time = localtime(&(u->status_time)); if (!strftime(buf, sizeof(buf), format_find("user_info_status_time_format") ,status_time) && format_exists("user_info_status_time_format")) xstrcpy(buf, "TOOLONG"); printq("user_info_status_time", buf); } if (u->last_status) { char *last_status = format_string(format_find(ekg_status_label(u->last_status, u->last_descr, "user_info_")), get_user_name(u), u->last_descr); printq("user_info_last_status", last_status); xfree(last_status); } for (rl = u->resources; rl; rl = rl->next) { ekg_resource_t *r = rl; char *resstatus; resstatus = format_string(format_find(ekg_status_label(r->status, r->descr, /* resource_info? senseless */ "user_info_")), /* here r->name ? */ get_user_name(u), r->descr); printq("resource_info_status", r->name, resstatus, ekg_itoa(r->prio)); xfree(resstatus); } if (ekg_group_member(u, "__blocked")) printq("user_info_block", get_user_name(u)); if (ekg_group_member(u, "__offline")) printq("user_info_offline", get_user_name(u)); if (ekg_group_member(u, "__online")) printq("user_info_online", get_user_name(u)); /* * (frequently) common private data */ __first_name = user_private_item_get(u, "first_name"); __last_name = user_private_item_get(u, "last_name"); if (xstrcmp(__first_name, "") && xstrcmp(__last_name, "")) printq("user_info_name", __first_name, __last_name); else if (xstrcmp(__first_name, "")) printq("user_info_name", __first_name, ""); else if (xstrcmp(__last_name, "")) printq("user_info_name", __last_name, ""); if ( (tmp = user_private_item_get(u, "mobile")) ) printq("user_info_mobile", tmp); __ip = user_private_item_get_int(u, "ip"); __port = user_private_item_get_int(u, "port"); __last_ip = user_private_item_get_int(u, "last_ip"); __last_port = user_private_item_get_int(u, "last_port"); if (__ip) { char *ip_str; if (__port) ip_str = saprintf("%s:%d", inet_ntoa(*((struct in_addr*) &__ip)), __port); else ip_str = saprintf("%s", inet_ntoa(*((struct in_addr*) &__ip))); printq("user_info_ip", ip_str); xfree(ip_str); } else if (__last_ip) { char *last_ip_str; if (__last_port) last_ip_str = saprintf("%s:%d", inet_ntoa(*((struct in_addr*) &__last_ip)), __last_port); else last_ip_str = saprintf("%s", inet_ntoa(*((struct in_addr*) &__last_ip))); printq("user_info_last_ip", last_ip_str); xfree(last_ip_str); } query_emit(NULL, "userlist-info", &u, &quiet); if (u->groups) { char *groups = group_to_string(u->groups, 0, 1); if (strcmp(groups, "")) printq("user_info_groups", groups); xfree(groups); } if (EKG_STATUS_IS_NA(u->status)) { char buf[100]; struct tm *last_seen_time; if (u->last_seen) { last_seen_time = localtime(&(u->last_seen)); if (!strftime(buf, sizeof(buf), format_find("user_info_last_seen_time"), last_seen_time) && format_exists("user_info_last_seen_time")) xstrcpy(buf, "TOOLONG"); printq("user_info_last_seen", buf); } else printq("user_info_never_seen"); } printq("user_info_footer", u->nickname, u->uid); xfree(status); return 0; } /* list --active | --away | --blocked | --description | --inactive | --invisible | --member | --notavail | --offline | --online */ for (i = 0; params[i]; i++) { if (match_arg(params[i], 'a', ("active"), 2)) { show_all = 0; show_active = 1; } else if (match_arg(params[i], 'i', ("inactive"), 2) || match_arg(params[i], 'n', ("notavail"), 2)) { show_all = 0; show_inactive = 1; } else if (match_arg(params[i], 'A', ("away"), 2)) { show_all = 0; show_away = 1; } else if (match_arg(params[i], 'I', ("invisible"), 2)) { show_all = 0; show_invisible = 1; } else if (match_arg(params[i], 'B', ("blocked"), 2)) { show_all = 0; show_blocked = 1; } else if (match_arg(params[i], 'o', ("offline"), 2)) { show_all = 0; show_offline = 1; } else if (match_arg(params[i], 'O', ("online"), 2)) { show_all = 0; show_online = 1; } else if (match_arg(params[i], 'm', ("member"), 2)) { if (params[i+1]) { i++; int off = (params[i][0] == '@' && xstrlen(params[i]) > 1) ? 1 : 0; xfree(show_group); show_group = xstrdup(params[i] + off); } else { printq("not_enough_params", name); /* XXX return here? */ } } else if (match_arg(params[i], 'd', ("description"), 2)) { show_descr = 1; } else { printq("invalid_params", name, params[i]); /* XXX return here? */ } } for (ul = session->userlist; ul; ul = ul->next) { userlist_t *u = ul; int show; if (!u->nickname) continue; tmp = ekg_status_label(u->status, u->descr, "list_"); show = show_all; #define SHOW_IF_S(x,y) if (show_##x && (u->status == EKG_STATUS_##y)) show = 1; SHOW_IF_S(away, AWAY) SHOW_IF_S(active, AVAIL) SHOW_IF_S(inactive, NA) SHOW_IF_S(invisible, INVISIBLE) SHOW_IF_S(blocked, BLOCKED) #undef SHOW_IF_S /* XXX nie chcialo mi sie zmiennej robic */ if (u->status == EKG_STATUS_ERROR) show = 1; if (show_descr && !u->descr) show = 0; if (show_group && !ekg_group_member(u, show_group)) show = 0; if (show_offline && ekg_group_member(u, "__offline")) show = 1; if (show_online && ekg_group_member(u, "__online")) show = 1; if (show) { int __ip = user_private_item_get_int(u, "ip"); printq(tmp, format_user(session, u->uid), get_user_name(u), inet_ntoa(*((struct in_addr*) &__ip)), ekg_itoa(user_private_item_get_int(u, "port")), u->descr); count++; } } if (!count && !(show_descr || show_group) && show_all) printq("list_empty"); xfree(show_group); return 0; } static COMMAND(cmd_save) { /* XXX retime autosave timer? */ /* set windows layout */ windows_save(); /* set default session */ if (config_sessions_save && session_current) { xfree(config_session_default); config_session_default = xstrdup(session_current->uid); } session_write(); config_write(); metacontact_write(); script_variables_write(); if (config_commit()) { printq("saved"); config_changed = 0; ekg2_reason_changed = 0; return 0; } else { /* XXX: grab some kind of error? */ printq("error_saving"); return -1; } g_assert_not_reached(); } static COMMAND(cmd_set) { const char *arg = NULL, *val = NULL; int unset = 0, show_all = 0, res = 0, be_quiet = 0; char *value = NULL; show_all = match_arg(params[0], 'a', ("all"), 1); be_quiet = match_arg(params[0], 'q', ("quiet"), 1); if (show_all || be_quiet) { arg = params[1]; if (arg) val = params[2]; } else { arg = params[0]; if (arg) val = params[1]; } if (arg && arg[0] == '-') { unset = 1; arg++; } if (arg && val) { char **tmp = array_make(val, (""), 0, 0, 1); value = xstrdup(tmp[0]); g_strfreev(tmp); } if ((!arg || !val) && !unset) { GSList *vl; int displayed = 0; for (vl = variables; vl; vl = vl->next) { variable_t *v = vl->data; int found = 0; if (arg) { if (xstrcmp(name, ("set"))) { found = !xstrcasecmp(arg, v->name); } else { found = !!xstrcasestr(v->name, arg); } } if ((!arg || found) && (v->display != 2 || xstrcmp(name, ("set")))) { if (!show_all && !arg && v->dyndisplay && !((v->dyndisplay)(v->name))) continue; variable_display(v, quiet); displayed = 1; } } if (!displayed && params[0]) { printq("variable_no_match", params[0]); return -1; } } else { variable_t *v = variable_find(arg); theme_cache_reset(); if (!unset && !xstrcasecmp(value, ("t"))) { if (v && v->type == VAR_BOOL) { int t_value = *(int*)(v->ptr); xfree(value); value = (t_value) ? xstrdup(("0")) : xstrdup(("1")); } } switch (variable_set(arg, (unset) ? NULL : value)) { case 0: if (!be_quiet) { config_changed = 1; } case 1: { if (be_quiet) break; const char *my_params[2] = { (!unset) ? params[0] : params[0] + 1, NULL }; cmd_set(("set-show"), (const char **) my_params, NULL, NULL, quiet); break; } case -1: printq("variable_not_found", arg); res = -1; break; case -2: printq("variable_invalid", arg); res = -1; break; } } xfree(value); return res; } static COMMAND(cmd_quit) { char *reason; session_t *s; reason = xstrdup(params[0]); query_emit(NULL, "quitting", &reason); xfree(reason); for (s = sessions; s; s = s->next) { if (params[0]) command_exec_format(NULL, s, 3, ("/disconnect %s"), params[0]); else command_exec(NULL, s, "/disconnect", 3); } /* nie wychodzimy tutaj, eby command_exec() miao szans zwolni * uywan przez siebie pami. */ quit_command = 1; return 0; } /** * cmd_version() * * printq() ekg2 VERSION + compile_time() and emit PLUGIN_PRINT_VERSION
* Handler for: /version command. * * @return 0 */ static COMMAND(cmd_version) { printq("ekg_version", VERSION, compile_time()); query_emit(NULL, "plugin-print-version"); return 0; } /** * cmd_test_segv() * * It try do Segmentation fault [By writting one byte to @@ 0x41414141]
* Sad command :(
* Handler for: /_segv command. * * @sa handle_sigsegv() - ekg2's handler for SIGSEGV signal * * @return Shouldn't return, SIGSEGV should be raised before. If SIGSEGV not raised return 0 */ static COMMAND(cmd_test_segv) { char *foo = (char*) 0x41414141; *foo = 0x41; return 0; } static COMMAND(cmd_test_send) { const char *sender, *rcpts[2] = { NULL, NULL }; if (!params[0] || !params[1] || !window_current || !window_current->session) return -1; sender = params[0]; if (sender[0] == '>') { rcpts[0] = sender + 1; sender = window_current->session->uid; } xfree(message_print((session) ? session->uid : NULL, sender, (rcpts[0]) ? rcpts : NULL, params[1], NULL, time(NULL), EKG_MSGCLASS_CHAT, "1234", EKG_TRY_BEEP, 0)); return 0; } static COMMAND(cmd_test_addtab) { tabnick_add(params[0]); return 0; } static COMMAND(cmd_test_deltab) { tabnick_remove(params[0]); return 0; } static COMMAND(cmd_test_debug) { debug("%s\n", params[0]); return 0; } static COMMAND(cmd_test_debug_dump) { char *tmp = saprintf(("Zapisalem debug do pliku debug.%d"), (int) getpid()); debug_write_crash(); printq("generic", tmp); xfree(tmp); return 0; } static COMMAND(cmd_test_debug_theme) { const char *fname = params[0]; if (!fname) fname = "ekg2-dump.theme"; /* XXX, wyswietlic komunikat */ return theme_write(fname); } static COMMAND(cmd_debug_plugins) { char buf[256]; GSList *pl; for (pl = plugins; pl; pl = pl->next) { const plugin_t *p = pl->data; const char *class; switch (p->pclass) { case PLUGIN_GENERIC: class = "generic"; break; case PLUGIN_PROTOCOL: class = "protocol"; break; case PLUGIN_UI: class = "ui"; break; case PLUGIN_LOG: class = "log"; break; case PLUGIN_SCRIPTING: class = "scripting"; break; case PLUGIN_AUDIO: class = "audio"; break; case PLUGIN_CODEC: class = "codec"; break; case PLUGIN_CRYPT: class = "crypt"; break; default: class = "-"; } snprintf(buf, sizeof(buf), "%-15s %-10s %-3d", p->name, class, p->prio); printq("generic", buf); if (p->pclass == PLUGIN_PROTOCOL && p->priv) { struct protocol_plugin_priv *pp = (struct protocol_plugin_priv *)p->priv; char *pr = g_strjoinv(", ", (char**) pp->protocols); char *st; char **_sts = NULL; const status_t *_st; for (_st = pp->statuses; *_st != EKG_STATUS_NULL; _st++) { array_add(&_sts, (char*) ekg_status_string(*_st, 2)); } st = g_strjoinv(", ", _sts); snprintf(buf, sizeof(buf), " protocols: %s", pr); printq("generic2", buf); snprintf(buf, sizeof(buf), " statuses: %s", st); printq("generic2", buf); xfree(pr); xfree(st); xfree(_sts); } } return 0; } static COMMAND(cmd_debug_watches) { char buf[256]; list_t l; printq("generic_bold", ("fd wa plugin pers tout started rm")); for (l = watches; l; l = l->next) { char *plugin; char wa[4]; watch_t *w = l->data; if (!w) continue; xstrcpy(wa, ""); if ((w->type & WATCH_READ)) xstrcat(wa, "R"); if ((w->type & WATCH_WRITE)) xstrcat(wa, "W"); if (w->buf) xstrcat(wa, "L"); if (w->plugin) plugin = w->plugin->name; else plugin = ("-"); snprintf(buf, sizeof(buf), "%-5d %-3s %-8s %-2d %-4ld %-10ld", w->fd, wa, plugin, 1, w->timeout, w->started); printq("generic", buf); } return 0; } static COMMAND(cmd_debug_queries) { query_t **kk, *g; printq("generic", ("name | plugin | count")); printq("generic", ("---------------------------------|-------------|------")); for (kk = queries; kk < &queries[QUERIES_BUCKETS]; ++kk) { for (g = *kk; g; g = g->next) { char buf[256]; const char *plugin = (g->plugin) ? g->plugin->name : ("-"); snprintf(buf, sizeof(buf), "%-32s | %-11s | %d", __(g->name), plugin, g->count); printq("generic", buf); } } return 0; } static COMMAND(cmd_debug_query) { char *p[10]; int i; memset(p, 0, sizeof(p)); for (i = 0; params[i] && i < 10; i++) p[i] = xstrdup(params[i]); query_emit(NULL, p[0], &p[1], &p[2], &p[3], &p[4], &p[5], &p[6], &p[7], &p[8], &p[9]); for (i = 0; i < 10; i++) xfree(p[i]); return 0; } static WATCHER(cmd_test_dns2_watch) { struct in_addr a; int len; if (type) { xfree(data); close(fd); return -1; } len = read(fd, &a, sizeof(a)); if (len != sizeof(a)) { print("generic_error", "ekg2-resolver-internal-error"); } else { print("dns2_resolved", (char *) data, inet_ntoa(a)); } return -1; } static COMMAND(cmd_test_dns2) { watch_t *w; /* hacked just to get it compile, fix it l8r */ if (!(w = ekg_resolver4(NULL, params[0], cmd_test_dns2_watch, NULL, 0, 0, 0))) { printq("generic_error", strerror(errno)); return -1; } w->data = xstrdup(params[0]); return 0; } /** * cmd_test_mem() * * Check and printq() how much memory ekg2 use.
* Handler for: /_mem command * * @note OS currently supported: Linux, Solaris, FreeBSD. If your OS isn't supported please give us info. * @bug Need cleanup/rewritting * * @return Memory used by ekg2 */ static COMMAND(cmd_test_mem) { #ifndef NO_POSIX_SYSTEM char *temp, *p = NULL; char *txt; FILE *file = NULL; int rd = 0, rozmiar = 0, unmres; struct utsname sys; unmres = uname(&sys); temp = saprintf("/proc/%d/status", getpid()); if ( (unmres != -1 && !xstrcmp(sys.sysname, "FreeBSD")) || (file = fopen(temp,"rb")) ) { xfree(temp); { #ifdef __linux__ char buf[1024]; rd = fread(buf, 1, 1024, file); fclose(file); if (rd == 0) { printq("generic_error", ("Internal error, GiM's fault")); return -1; } p = xstrstr(buf, "VmSize"); if (p) { sscanf(p, "VmSize: %d kB", &rozmiar); } else { printq("generic_error", ("VmSize line not found!")); return -1; } #elif __sun pstatus_t proc_stat; rd = fread(&proc_stat, sizeof(proc_stat), 1, file); fclose(file); if (rd == 0) { printq("generic_error", "Internal error, GiM's fault"); return -1; } rozmiar = proc_stat.pr_brksize + proc_stat.pr_stksize; #elif __FreeBSD__ /* link with -lkvm */ char errbuf[_POSIX2_LINE_MAX]; int nentries = -1; struct kinfo_proc *kp; static kvm_t *kd; if (!(kd = kvm_openfiles(NULL /* "/dev/null" */, "/dev/null", NULL, /* O_RDONLY */0, errbuf))) { printq("generic_error", ("Internal error! (kvm_openfiles)")); return -1; } kp = kvm_getprocs(kd, KERN_PROC_PID, getpid(), &nentries); if (!kp || nentries != 1) { printq("generic_error", ("Internal error! (kvm_getprocs)")); return -1; } #ifdef HAVE_STRUCT_KINFO_PROC_KI_SIZE rozmiar = (u_long) kp->ki_size/1024; /* freebsd5 */ #else rozmiar = kp->kp_eproc.e_vm.vm_map.size/1024; /* freebsd4 */ #endif /* HAVE_STRUCT_KINFO_PROC_KI_SIZE */ #else if (unmres != -1) p = saprintf(" You seem to have /proc mounted, but I don't know how to deal with it. Authors would be very thankful, if you'd contac wit them, quoting this error message. [%s %s %s %s]", sys.sysname, sys.release, sys.version, sys.machine); else p = saprintf(" You seem to have /proc mounted, but I don't know how to deal with it. Authors would be very thankful, if you'd contac wit them, quoting this error message. uname() failed!"); printq("generic_error", p); xfree(p); return -1; #endif } txt = saprintf(("Memory used by ekg2: %d KiB"), rozmiar); printq("generic", txt); xfree(txt); } else { printq("generic_error", ("/proc not mounted, no permissions, or no proc filesystem support")); xfree(temp); return -1; } return rozmiar; #else return -1; #endif } static COMMAND(cmd_test_fds) { #ifndef NO_POSIX_SYSTEM struct stat st; char buf[PATH_MAX+1000]; int i; for (i = 0; i < 2048; i++) { if (fstat(i, &st) == -1) continue; sprintf(buf, "%d: ", i); if (S_ISREG(st.st_mode)) { #ifdef __linux__ char *mypath = saprintf("/proc/%d/fd/%d", getpid(), i); char newpath[PATH_MAX]; int r = readlink(mypath, newpath, sizeof(newpath)-1); xfree(mypath); if (r <= 0) sprintf(buf + xstrlen(buf), "file, inode %lu, size %lu", (long)st.st_ino, (long)st.st_size); else { newpath[r] = 0; sprintf(buf + xstrlen(buf), "file, inode %lu, size %lu, path %s", (long)st.st_ino, (long)st.st_size, newpath); } #else sprintf(buf + xstrlen(buf), "file, inode %lu, size %lu", st.st_ino, st.st_size); #endif } if (S_ISSOCK(st.st_mode)) { /* GiM: ten sock_n, bo sockaddr_in6 > sockaddr_in */ char sock_n[256], bufek[256]; struct sockaddr *sa = (struct sockaddr*)sock_n; /* struct sockaddr_un *sun = (struct sockaddr_un*) &sa; */ struct sockaddr_in *sin = (struct sockaddr_in*) sa; #ifdef HAVE_GETADDRINFO struct sockaddr_in6 *sin6 = (struct sockaddr_in6*) sa; #endif socklen_t sa_len = 256; if (getpeername(i, sa, &sa_len) == -1) { getsockname(i, sa, &sa_len); /* GiM->dj: I'm not changing this stuff * couse this was just beautifying stuff * and by '*' I don't mean 0.0.0.0 */ if (sa->sa_family == AF_INET) { xstrcat(buf, "socket, inet, *:"); xstrcat(buf, ekg_itoa(g_ntohs(sin->sin_port))); } else if (sa->sa_family == AF_INET6) { xstrcat(buf, "socket, inet6, *:"); xstrcat(buf, ekg_itoa(g_ntohs(sin->sin_port))); } else xstrcat(buf, "socket"); } else { switch (sa->sa_family) { case AF_UNIX: xstrcat(buf, "socket, unix"); break; case AF_INET: xstrcat(buf, "socket, inet, "); xstrcat(buf, inet_ntoa(sin->sin_addr)); xstrcat(buf, ":"); xstrcat(buf, ekg_itoa(g_ntohs(sin->sin_port))); break; #ifdef HAVE_GETADDRINFO case AF_INET6: xstrcat(buf, "socket, inet6, "); #ifdef HAVE_INET_NTOP inet_ntop(sa->sa_family, &(sin6->sin6_addr), bufek, sizeof(bufek)); xstrcat(buf, bufek); #else xstrcat(buf, "strange?"); #endif /* HAVE_INET_NTOP */ xstrcat(buf, ":"); xstrcat(buf, ekg_itoa(g_ntohs(sin6->sin6_port))); break; #endif /* HAVE_GETADDRINFO */ default: xstrcat(buf, "socket, "); xstrcat(buf, ekg_itoa(sa->sa_family)); } } } if (S_ISDIR(st.st_mode)) xstrcat(buf, "directory"); if (S_ISCHR(st.st_mode)) xstrcat(buf, "char"); if (S_ISBLK(st.st_mode)) xstrcat(buf, "block"); if (S_ISFIFO(st.st_mode)) xstrcat(buf, "fifo"); if (S_ISLNK(st.st_mode)) xstrcat(buf, "symlink"); printq("generic", buf); } return 0; #else return -1; #endif } /** * cmd_beep() * * Beep by emiting UI_BEEP event * * @note UI-PLUGINS should connect to this event, and when they succesfully beep, returns -1. * Cause we may have more than 1 UI-PLUGIN, and I really don't like idea, when after /beep * I hear 2 or more beeps. * * @return 0 */ static COMMAND(cmd_beep) { query_emit(NULL, "ui-beep", NULL); return 0; } static COMMAND(cmd_play) { if (!config_sound_app) { printq("var_not_set", name, "sound_app"); return -1; } return play_sound(params[0]); } static COMMAND(cmd_say) { if (!config_speech_app) { printq("var_not_set", name, "speech_app"); return -1; } if (match_arg(params[0], 'c', ("clear"), 2)) { buffer_free(&buffer_speech); return 0; } return say_it(params[0]); } static COMMAND(cmd_reload) { int res; if ((res = config_read_plugins())) printq("error_reading_config", strerror(errno)); if (res == -1) return -1; if ((res = config_read(NULL))) printq("error_reading_config", strerror(errno)); if (res == -1) return -1; metacontacts_destroy(); if ((res = session_read(NULL))) printq("error_reading_config", strerror(errno)); if (res == -1) return -1; if ((res = metacontact_read())) printq("error_reading_config", strerror(errno)); if (res == -1) return -1; printq("config_read_success"); config_changed = 0; return res; } static COMMAND(cmd_query) { char *par0 = NULL; metacontact_t *m; window_t *w; char *tmp; if (params[0][0] == '@' || xstrchr(params[0], ',')) { /* you want to talk with lot people? let's create conference ! */ struct conference *c = conference_create(session, params[0]); if (!c) return -1; par0 = c->name; } if (!par0 && params[0][0] == '#') { /* query conference ? ok... */ struct conference *c = conference_find(params[0]); if (!c) { printq("conferences_noexist", params[0]); return -1; } par0 = c->name; } if (!par0 && (tmp = xstrrchr(params[0], '/'))) { /* query user in passed session? wow XXX, we need to fix it cause jabber can pass '/' as resource. XXX */ char *session_name = xstrndup(params[0], xstrlen(params[0]) - xstrlen(tmp)); session_t *sess_tmp; if (!(sess_tmp = session_find(session_name))) { xfree(session_name); goto next; } xfree(session_name); session = sess_tmp; tmp++; if (!get_uid(session, tmp)) { printq("invalid_session"); return -1; } par0 = tmp; } next: if (!par0 && (m = metacontact_find(params[0]))) { /* some metacontact's name ? */ metacontact_item_t *i = metacontact_find_prio(m); if (!i) { printq("metacontact_item_list_empty"); return -1; } par0 = i->name; session = session_find(i->s_uid); } if (!par0) { /* everything else if it's valid uid/ (nickname if we have user in roster) */ const char *uid = get_uid(session, params[0]); if (!uid) { printq("user_not_found", params[0]); return -1; } par0 = (char *) params[0]; } if (!(w = window_find_s(session, par0))) { /* if we don't have window, we need to create it, in way specified by (config_make_window) */ if (config_make_window & 1) { window_t *v; for (v = windows; v; v = v->next) { if (v->id < 2 || v->floating || v->target) continue; w = v; break; } if (w) { w->target = xstrdup(par0); /* new target */ w->session = session; /* change session */ query_emit(NULL, "ui-window-target-changed", &w); /* notify ui-plugin */ } } else if (!(config_make_window & 2) && window_current /* && window_current->id >1 && !window_current->floating */) { w = window_current; xfree(w->target); w->target = xstrdup(par0); /* change target */ w->session = session; /* change session */ query_emit(NULL, "ui-window-target-changed", &w); /* notify ui-plugin */ } if (!w) w = window_new(par0, session, 0); /* jesli jest config_make_window => 2 lub nie mielismy wolnego okienka przy config_make_window == 1, stworzmy je */ if (!quiet) { /* display some beauty info */ print_info(par0, session, "query_started", par0, session_name(session)); print_info(par0, session, "query_started_window", par0, session_name(session)); } } if (w != window_current) window_switch(w->id); /* switch to that window */ if (params[1]) /* if we've got message in params[1] send it */ command_exec_format(par0, session, quiet, ("/ %s"), params[1]); return 0; } /** * cmd_echo() * * printq() params[0] if not NULL, or just ""
* Handler for: /echo command * * @return 0 */ static COMMAND(cmd_echo) { printq("generic", params[0] ? params[0] : ""); return 0; } /* * command_exec() * * executes a command stored in a string. * * - target - which window was the command invoked in (or NULL if not in one) * - session - session on behalf of which we're running * - xline - the string containing the line of text * - quiet - hide output if == 1, hide inexistence of command if == 2 * * 0/-1. */ int command_exec(const char *target, session_t *session, const char *xline, int quiet) { char short_cmd[2] = ("."); char *p = NULL; char *line_save = NULL, *line = NULL; char *cmd = NULL, *tmp; size_t cmdlen; /* TODO: what does the "last" prefix stand for? */ command_t *last_command = NULL; command_t *last_command_plugin = NULL; /* unneeded, but someone wrote it as if it would be necessary one day, so we leave it here */ int abbrs = 0; /* 1 if the post-prefix part of command was spelled out fully, e.g. user entered "disconnect" while the command is "gg:disconnect" */ int abbrs_plugins = 0; int exact = 0; GSList *cl; if (!xline) return 0; /* in a chat window, and there is no leading slash */ if (target && *xline != '/') { int correct_command = 0; /* detection of commands entered by mistake */ if (config_query_commands) { for (cl = commands; cl; cl = cl->next) { command_t *c = cl->data; size_t l = xstrlen(c->name); if (l < 3 || xstrncasecmp(xline, c->name, l)) continue; if (!xline[l] || xisspace(xline[l])) { correct_command = 1; break; } } } if (!correct_command) return command_exec_format(target, session, quiet, ("/ %s"), xline); } if (target && *xline == '/' && config_slash_messages == 1) { /* send the message if the first word contains at least two slashes, * e.g. "/bin/sh" or "/dev/hda1", as it is not likely to be an ekg2 command * code stolen from ekg1, idea stolen from irssi. */ char *p = strchr(xline + 1, '/'); char *s = strchr(xline + 1, ' '); if (p && (!s || p < s)) return command_exec_format(target, session, quiet, ("/ %s"), xline); } send_nicks_index = 0; line = line_save = xstrdup(xline); if (*line == '/') line++; if (*line == '^') { quiet = 1; line++; } /* Check if this is a special one-character command. These are special * because they do not require whitespace to separate them from their arguments. */ for (cl = commands; cl; cl = cl->next) { command_t *c = cl->data; if (!isalpha_pl_PL(c->name[0]) && xstrlen(c->name) == 1 && line[0] == c->name[0]) { short_cmd[0] = c->name[0]; cmd = short_cmd; p = line + 1; } } /* Separate command from arguments if not. */ if (!cmd) { tmp = cmd = line; while (*tmp && !xisspace(*tmp)) tmp++; p = (*tmp) ? tmp + 1 : tmp; *tmp = 0; } /* Whatever it was, at this point: * 'cmd' points at a string containing the command verb * 'p' points at the (possibly empty) string containing parameters */ cmdlen = xstrlen(cmd); /* First, look for a command specific to the given session. */ if (!session && session_current) session = session_current; if (session && session->uid) { int prefix_len = (int)(xstrchr(session->uid, ':') - session->uid) + 1; for (cl = commands; cl; cl = cl->next) { command_t *c = cl->data; /* Consider commands prefixed with current session's prefix. */ if (xstrncasecmp(c->name, session->uid, prefix_len)) continue; /* Look for fully spelled out command. */ if (!xstrcasecmp(c->name + prefix_len, cmd)) { last_command = c; abbrs = 1; exact = 1; break; } /* Fall back to the first matching prefix. */ if (!xstrncasecmp(c->name + prefix_len, cmd, cmdlen)) { last_command_plugin = c; abbrs_plugins++; } else { /* TODO: document what this does */ if (last_command_plugin && abbrs_plugins == 1) break; } } } /* If needed, fall back to non-session-specific commands. */ if (!exact) { for (cl = commands; cl; cl = cl->next) { command_t *c = cl->data; if (!xstrcasecmp(c->name, cmd)) { last_command = c; abbrs = 1; exact = 1; /* if this is exact_match we should zero those below, they won't be used */ abbrs_plugins = 0; last_command_plugin = NULL; break; } if (!xstrncasecmp(c->name, cmd, cmdlen)) { last_command = c; abbrs++; } else { if (last_command && abbrs == 1) break; } } } /* debug("%x %x\n", last_command, last_command_plugin); */ /* TODO: what does the following condition ACTUALLY MEAN? */ if ((last_command && abbrs == 1 && !abbrs_plugins) || ( (last_command = last_command_plugin) && abbrs_plugins == 1 && !abbrs)) { /* At this point last_command points at the command_t structure we need to invoke. */ session_t *s = session ? session : window_current->session; const char *last_name = last_command->name; char *tmp; int last_alias = 0; int res = 0; if (exact) last_alias = (last_command->flags & COMMAND_ISALIAS || last_command->flags & COMMAND_ISSCRIPT) ? 1 : 0; if (!last_alias && (tmp = xstrchr(last_name, ':'))) last_name = tmp + 1; /* sprawdzamy czy session istnieje - jeli nie to nie mamy po co robi czego dalej ... */ /* nie obsolete ? nie robi tego samego co `session_t *s = session ? session : window_current->session;` (?) */ if (last_command->flags & SESSION_MUSTHAS) { if (!s && !(s = session_current)) res = -1; } if (!res && last_command->flags & SESSION_MUSTHASPRIVATE) { if (!s || !(s->priv)) { printq("invalid_session"); res = -1; /* debug("[command_exec] res = -1; SESSION_MUSTHASPRIVATE"); */ } } if (!res && (last_command->flags & SESSION_MUSTBECONNECTED)) { if (!session_connected_get(s)) { printq("not_connected", session_name(s)); res = -1; } } if (!res && (last_command->flags & SESSION_MUSTBELONG)) { /* if in future of ekg2, there'll be need of this, please feel free to change thoese if's. * it's correct.. but for now, i don't see much sense of doing it cause. we don't have * two plugins which handle for example irc: or gg: * if we'll have/ or maybe you have then write to use. * thx. */ /* if (valid_plugin_uid(last_command->plugin, session_uid_get(s)) != 1) */ if (last_command->plugin != s->plugin) { printq("invalid_session"); res = -1; /* debug("[command_exec] res = -1; SESSION_MUSTBELONG\n"); */ } } if (!res) { char **parameter_types = (last_command->flags & COMMAND_ISALIAS) ? array_make(("?"), (" "), 0, 1, 1) : last_command->params; int parameter_types_count = parameter_types ? g_strv_length(parameter_types) : 0; char **parsed_params = NULL; /* The array of parameter values which is going to be passed to the command handler. */ /* Perform some parameter verification and mangling. * See flag definitions in commands.h for the exact * meaning of each of the following cases. */ if (last_command->flags & COMMAND_PASS_UNCHANGED) array_add(&parsed_params, xstrdup(p)); else parsed_params = array_make(strip_spaces(p), (" \t"), parameter_types_count, 1, 1); if ((last_command->flags & COMMAND_PARAMASTARGET) && parsed_params[0]) { /* debug("[command_exec] oldtarget = %s newtarget = %s\n", target, parsed_params[0]); */ target = parsed_params[0]; } if (/* !res && */ last_command->flags & COMMAND_ENABLEREQPARAMS) { int i; for (i=0; i < parameter_types_count; i++) { char *p; if (!(p = parameter_types[i])) break; /* rather impossible */ if (p[0] == '!' && !parsed_params[i]) { if (i == 0 && (last_command->flags & COMMAND_PARAMASTARGET) && target) /* if params[0] already in target */ continue; /* skip it */ debug("[command_exec,%s] res = -1; req params[%d] = NIL\n", last_name, i); printq("not_enough_params", last_name); res = -1; break; } else if (p[0] != '!') break; } } if (!res && last_command->flags & COMMAND_TARGET_VALID_UID) { const char *tmp = target; if (!(target = get_uid(session, target))) { /* user_not_found vs invalid_uid */ printq("user_not_found", tmp); res = -1; } } if (!res) { window_t *w; char *uid; uid = xstrdup(target); w = window_find_sa(s, uid, 0); window_lock_inc(w); res = (last_command->function)(last_name, (const char **) parsed_params, s, uid, (quiet & 1)); if (window_find_ptr(w) || (w == window_find_sa(s, uid, 0))) window_lock_dec(w); else { window_t *w; debug("[WINDOW LOCKING] INTERNAL ERROR SETTING ALL WINDOW LOCKS TO 0 [wtarget=%s command=%s]\n", __(uid), __(last_name)); /* may be faultly */ for (w=windows; w; w = w->next) { if (!(w->lock)) continue; w->lock = 0; } } query_emit(NULL, "ui-window-refresh"); xfree(uid); } if (last_command->flags & COMMAND_ISALIAS) g_strfreev(parameter_types); g_strfreev(parsed_params); } xfree(line_save); if (quit_command) ekg_exit(); return res; } if (cmdlen) { /* if not empty command typed: ("") and we didn't found handler for it... [was: xstrcmp(cmd, "")] */ if (config_slash_messages == 2 && target && *xline == '/') { xfree(line_save); return command_exec_format(target, session, quiet, ("/ %s"), xline); } quiet = quiet & 2; printq("unknown_command", cmd); /* display warning, if !(quiet & 2) */ } xfree(line_save); return -1; } int command_exec_params(const char *target, session_t *session, int quiet, const char *command, ...) { string_t command_with_params; va_list ap; char *tmp; int res; if (!command) return 0; /* XXX, it will work like command_exec_format(). we need to: * - command_exec() -> command_exec_params() */ command_with_params = string_init(command); /* XXX, later: array_add() */ va_start(ap, command); while ((tmp = va_arg(ap, char *))) { char ch = '"'; string_append_c(command_with_params, ' '); /* separate from command or from previous param */ /* if (tmp[0] == '"' && tmp[xstrlen(tmp)-1] == '"') ch = '\''; */ string_append_c(command_with_params, ch); string_append(command_with_params, tmp); string_append_c(command_with_params, ch); } va_end(ap); res = command_exec(target, session, command_with_params->str, quiet); string_free(command_with_params, 1); return res; } /** * command_exec_format() * * Format string in @a format and execute formated command * Equivalent to:
* * char *tmp = saprintf(format, ...);
* command_exec(target, session, tmp, quiet);
* xfree(tmp);
*
* * @note For more details about string formating functions read man 3 vsnprintf * * @sa command_exec() - If you want/can use non-formating function.. Watch for swaped params! (@a quiet with @a format) * * @return 0 - If @a format was NULL
* -1 - If command was not found [It's result of command_exec()]
* else it returns result of command handler. */ int command_exec_format(const char *target, session_t *session, int quiet, const char *format, ...) { char *command; va_list ap; int res; va_start(ap, format); command = vsaprintf(format, ap); va_end(ap); if (!command) return 0; /* debug("[command_exec_format] %s\n", command); */ res = command_exec(target, session, command, quiet); /* segvuje na tym ? wtf ?! */ xfree(command); return res; } COMMAND(cmd_alias_exec) { list_t tmp = NULL, m = NULL; alias_t *a; int need_args = 0; for (a = aliases; a; a = a->next) { if (!xstrcasecmp(name, a->name)) { tmp = a->commands; break; } } for (; tmp; tmp = tmp->next) { char *p; int __need = 0; for (p = tmp->data; *p; p++) { if (*p == '\\' && p[1] == ('%')) { p += 2; continue; } if (*p != '%') continue; p++; if (!*p) break; if (*p >= '1' && *p <= '9' && (*p - '0') > __need) __need = *p - '0'; if (need_args < __need) need_args = __need; } list_add(&m, xstrdup(tmp->data)); } for (tmp = m; tmp; tmp = tmp->next) { string_t str; if (*((char *) tmp->data) == '/') str = string_init(NULL); else str = string_init(("/")); if (need_args) { char *args[9], **arr, *s; int i; if (!params[0]) { printq("aliases_not_enough_params", name); string_free(str, 1); list_destroy(m, 1); return -1; } arr = array_make(params[0], ("\t "), need_args, 1, 1); if (g_strv_length(arr) < need_args) { printq("aliases_not_enough_params", name); string_free(str, 1); g_strfreev(arr); list_destroy(m, 1); return -1; } for (i = 0; i < 9; i++) { if (i < need_args) args[i] = arr[i]; else args[i] = NULL; } s = format_string((char *) tmp->data, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]); string_append(str, s); xfree(s); g_strfreev(arr); } else { string_append(str, tmp->data); if (params[0]) { string_append(str, (" ")); string_append(str, params[0]); } } command_exec(target, session, str->str, quiet); string_free(str, 1); } list_destroy(m, 1); return 0; } static COMMAND(cmd_conference) { if (!params[0] || match_arg(params[0], 'l', ("list"), 2) || params[0][0] == '#') { list_t r; int count = 0; const char *cname = NULL; if (params[0] && match_arg(params[0], 'l', ("list"), 2)) cname = params[1]; else if (params[0]) cname = params[0]; { newconference_t *c; for (c = newconferences; c; c = c->next) { /* XXX, * ::: konferencje: * -- konferencja1/sesja * USER1: [query_emit(NULL/pl, "conference-member-info", &c, &u)] * USER2: [....] * query_emit(NULL/pl, "conference-info", &c); * albo jak /names w ircu, albo jak? * XXX */ print("conferences_list", c->name, ""); count++; } } { struct conference *c; for (c = conferences; c; c = c->next) { string_t recipients; const char *recipient; int first = 0; recipients = string_init(NULL); if (cname && xstrcasecmp(cname, c->name)) continue; for (r = c->recipients; r; r = r->next) { char *uid = r->data; userlist_t *u = userlist_find(session, uid); if (u && u->nickname) recipient = u->nickname; else recipient = uid; if (first++) string_append_c(recipients, ','); string_append(recipients, recipient); count++; } print(c->ignore ? "conferences_list_ignored" : "conferences_list", c->name, recipients->str); string_free(recipients, 1); } } if (!count) { if (params[0] && params[0][0] == '#') { printq("conferences_noexist", params[0]); return -1; } else printq("conferences_list_empty"); } return 0; } if (match_arg(params[0], 'j', ("join"), 2)) { struct conference *c; const char *uid; if (!params[1] || !params[2]) { printq("not_enough_params", name); return -1; } if (params[1][0] != '#') { printq("conferences_name_error"); return -1; } if (!(c = conference_find(params[1]))) { printq("conferences_noexist", params[1]); return -1; } if (!(uid = get_uid(session, params[2]))) { printq("unknown_user", params[2]); return -1; } if (conference_participant(c, uid)) { printq("conferences_already_joined", format_user(session, uid), params[1]); return -1; } list_add(&c->recipients, xstrdup(uid)); printq("conferences_joined", format_user(session, uid), params[1]); return 0; } if (match_arg(params[0], 'a', ("add"), 2)) { if (!params[1]) { printq("not_enough_params", name); return -1; } if (params[2]) { if (params[1][0] != '#') { printq("conferences_name_error"); return -1; } else conference_add(session, params[1], params[2], quiet); } else conference_create(session, params[1]); return 0; } if (match_arg(params[0], 'd', ("del"), 2)) { if (!params[1]) { printq("not_enough_params", name); return -1; } if (!xstrcmp(params[1], "*")) conference_remove(NULL, quiet); else { if (params[1][0] != '#') { printq("conferences_name_error"); return -1; } conference_remove(params[1], quiet); } return 0; } if (match_arg(params[0], 'r', ("rename"), 2)) { if (!params[1] || !params[2]) { printq("not_enough_params", name); return -1; } if (params[1][0] != '#' || params[2][0] != '#') { printq("conferences_name_error"); return -1; } conference_rename(params[1], params[2], quiet); return 0; } if (match_arg(params[0], 'i', ("ignore"), 2)) { if (!params[1]) { printq("not_enough_params", name); return -1; } if (params[1][0] != '#') { printq("conferences_name_error"); return -1; } conference_set_ignore(params[1], 1, quiet); return 0; } if (match_arg(params[0], 'u', ("unignore"), 2)) { if (!params[1]) { printq("not_enough_params", name); return -1; } if (params[1][0] != '#') { printq("conferences_name_error"); return -1; } conference_set_ignore(params[1], 0, quiet); return 0; } if (match_arg(params[0], 'f', ("find"), 2)) { struct conference *c; list_t l; if (!params[1]) { printq("not_enough_params", name); return -1; } if (params[1][0] != '#') { printq("conferences_name_error"); return -1; } c = conference_find(params[1]); if (c) { for (l = c->recipients; l; l = l->next) { command_exec_format(target, session, quiet, ("/find %s\n"), (char*) (l->data)); } } else { printq("conferences_noexist", params[1]); return -1; } return 0; } printq("invalid_params", name, params[0]); return -1; } static COMMAND(cmd_last) { struct last *ll; const char *uid = NULL; int show_sent = 0, last_n = 0, count = 0, i = 0, show_all = 0; char **arr = NULL; const char *nick = NULL; time_t n; struct tm *now; if (match_arg(params[0], 'c', ("clear"), 2)) { /* XXX, get_uid() */ if (params[1] && !(uid = get_uid(session, params[1]))) { printq("user_not_found", params[1]); return -1; } if ((uid && !last_count(uid)) || !LIST_COUNT2(lasts)) { if (uid) printq("last_list_empty_nick", format_user(session, uid)); else printq("last_list_empty"); return -1; } if (uid) { last_del(uid); printq("last_clear_uin", format_user(session, uid)); } else { lasts_destroy(); printq("last_clear"); } return 0; } if (params[0]) { show_sent = match_arg(params[0], 's', ("stime"), 2); if (!show_sent) nick = params[0]; if (params[1]) { arr = array_make(params[1], " \t", 0, 1, 0); nick = arr[0]; if (match_arg(params[0], 'n', ("number"), 2)) { last_n = strtol(arr[0], NULL, 0); nick = arr[1]; if (arr[1] && (show_sent = match_arg(arr[1], 's', ("stime"), 2))) nick = arr[2]; } if (arr[1] && show_sent && match_arg(arr[0], 'n', ("number"), 2)) { last_n = atoi(arr[1]); nick = arr[2]; } } } show_all = (nick && !xstrcasecmp(nick, "*")) ? 1 : 0; /* XXX, get_uid() */ if (!show_all && nick && !(uid = get_uid(session, nick))) { printq("user_not_found", nick); g_strfreev(arr); return -1; } g_strfreev(arr); if (!((uid) ? (count = last_count(uid)) : (count = LIST_COUNT2(lasts)))) { if (uid) { printq("last_list_empty_nick", format_user(session, uid)); return -1; } printq("last_list_empty"); return 0; } n = time(NULL); now = localtime(&n); printq("last_begin"); for (ll = lasts; ll; ll = ll->next) { struct tm *tm, *st; char buf[100], buf2[100], *time_str = NULL; const char *form; if (show_all || !uid || !xstrcasecmp(uid, ll->uid)) { if (last_n && i++ < (count - last_n)) continue; tm = localtime(&ll->time); if (!strftime(buf, sizeof(buf), format_find("last_list_timestamp"), tm) && format_exists("last_list_timestamp")) xstrcpy(buf, "TOOLONG"); if (show_sent && !ll->type && !(ll->sent_time - config_time_deviation <= ll->time && ll->time <= ll->sent_time + config_time_deviation)) { st = localtime(&ll->sent_time); form = format_find((tm->tm_yday == now->tm_yday) ? "last_list_timestamp_today" : "last_list_timestamp"); if (!strftime(buf2, sizeof(buf2), form, st) && xstrlen(form)>0) xstrcpy(buf2, "TOOLONG"); time_str = saprintf("%s/%s", buf, buf2); } else time_str = xstrdup(buf); printq(config_last & 4 && ll->type ? "last_list_out" : "last_list_in", time_str, format_user(session, ll->uid), ll->message); xfree(time_str); } } printq("last_end"); return 0; } /** * cmd_queue() * * Manage ekg2 message queue list.
* Handler for: /queue command. * * @return 0 */ static COMMAND(cmd_queue) { msg_queue_t *m; int isempty = 1; const char *queue_list_timestamp_f; /* cached result of format_find("queue_list_timestamp") */ if (match_arg(params[0], 'c', ("clear"), 2)) { if (!msgs_queue) { printq("queue_empty"); return 0; } if (params[1]) { if (!msg_queue_remove_uid(params[1])) /* msg_queue_remove_uid() returns 0 on success */ printq("queue_clear_uid", format_user(session, params[1])); else printq("queue_empty_uid", format_user(session, params[1])); /* queue for user empty */ } else { msgs_queue_destroy(); printq("queue_clear"); } return 0; } queue_list_timestamp_f = format_find("queue_list_timestamp"); for (m = msgs_queue; m; m = m->next) { struct tm *tm; char buf[100] = { '\0' }; /* we need to init it to '\0' cause queue_list_timestamp_f can be empty. and if buf was not NUL terminated string, than printq() can do SIGSEGV */ if (!params[0] || !xstrcasecmp(m->rcpts, params[0])) { tm = localtime(&m->time); /* [if queue_list_timestamp_f != "", (format_find() returns "" if format not found]] && if strftime() fails */ if (format_ok(queue_list_timestamp_f) && !strftime(buf, sizeof(buf), queue_list_timestamp_f, tm)) xstrcpy(buf, "TOOLONG"); /* use 'TOOLONG' */ printq("queue_list_message", buf, m->rcpts, m->message); isempty = 0; } } if (isempty) { if (params[0]) printq("queue_empty_uid", format_user(session, params[0])); else printq("queue_empty"); } return 0; } COMMAND(cmd_dcc) { dcc_t *d; if (!params[0] || !xstrncasecmp(params[0], "li", 2)) { /* list */ int empty = 1, passed = 0; for (d = dccs; d; d = d->next) { if (d->active) continue; empty = 0; if (!passed++) printq("dcc_show_pending_header"); switch (d->type) { case DCC_SEND: printq("dcc_show_pending_send", ekg_itoa(d->id), format_user(session, d->uid), d->filename); break; case DCC_GET: printq("dcc_show_pending_get", ekg_itoa(d->id), format_user(session, d->uid), d->filename); break; case DCC_VOICE: printq("dcc_show_pending_voice", ekg_itoa(d->id), format_user(session, d->uid)); break; default: break; } } passed = 0; for (d = dccs; d; d = d->next) { if (!d->active) continue; empty = 0; if (!passed++) printq("dcc_show_active_header"); switch (d->type) { case DCC_SEND: printq("dcc_show_active_send", ekg_itoa(d->id), format_user(session, d->uid), d->filename, ekg_itoa(d->offset), ekg_itoa(d->size), (d->size) ? ekg_itoa(d->offset * 100 / d->size) : "?"); break; case DCC_GET: printq("dcc_show_active_get", ekg_itoa(d->id), format_user(session, d->uid), d->filename, ekg_itoa(d->offset), ekg_itoa(d->size), (d->size) ? ekg_itoa(d->offset * 100 / d->size) : "?"); break; case DCC_VOICE: printq("dcc_show_active_voice", ekg_itoa(d->id), format_user(session, d->uid)); break; default: break; } } if (empty) printq("dcc_show_empty"); return 0; } if (!xstrncasecmp(params[0], "c", 1)) { /* close */ dcc_t *d = NULL; dcc_t *D; const char *uid; if (!params[1]) { printq("not_enough_params", name); return -1; } /* XXX, get_uid() */ uid = get_uid(session, params[1]); for (D = dccs; D; D = D->next) { if (params[1][0] == '#' && atoi(params[1] + 1) == D->id) { d = D; uid = dcc_uid_get(d); break; } if (uid && !xstrcasecmp(D->uid, uid)) { d = D; break; } } if (!d) { printq("dcc_not_found", params[1]); return -1; } printq("dcc_close", format_user(session, uid)); dcc_close(d); return 0; } printq("invalid_params", name, params[0]); return -1; } /** * cmd_plugin() * * Manage plugins in ekg2 load/unload/list/change plugin prios
* Handler for: /plugin command * * @todo see XXX's * */ static COMMAND(cmd_plugin) { int ret; plugin_t *pl; if (!params[0]) { GSList *pl; for (pl = plugins; pl; pl = pl->next) { const plugin_t *p = pl->data; printq("plugin_list", p->name ? p->name : ("?"), ekg_itoa(p->prio)); } if (!plugins) { /* XXX, display info: no plugins. */ } return 0; } if (match_arg(params[0], 'd', ("default"), 2)) { GSList *old_plugins = plugins; GSList *pl; plugins = NULL; /* XXX, check */ for (pl = old_plugins; pl; pl = pl->next) { plugin_t *p = pl->data; plugin_register(p, -254); } g_slist_free(old_plugins); queries_reconnect(); config_changed = 1; printq("plugin_default"); } if (params[0][0] == '+') { const int prio = (params[1] ? atoi(params[1]) : -254); if ((ret = plugin_load(params[0] + 1, prio, 0))) { /* if plugin cannot be founded || loaded don't reload theme. */ return ret; } queries_reconnect(); changed_theme(NULL); return 0; } if (params[0][0] == '-') { pl = plugin_find(params[0] + 1); if (!pl) { /* XXX, display info */ return -1; } return plugin_unload(pl); } if (params[1] && (pl = plugin_find(params[0]))) { plugins_unlink(pl); plugin_register(pl, atoi(params[1])); queries_reconnect(); config_changed = 1; printq("plugin_prio_set", pl->name, params[1]); return 0; } printq("invalid_params", name, params[0]); return -1; } /** * cmd_desc() * * Changes description (@a params[0]) without changing status
* Handler for: /_desc command * * @todo Think about it. think, think, think. Maybe let's use queries for it? * * @todo Check if session_unidle() is needed. * * @param params [0] New description, if NULL than "" will be used. */ static COMMAND(cmd_desc) { const char *cmd; session_unidle(session); cmd = ekg_status_string(session->status, 1); return command_exec_format(NULL, session, quiet, ("/%s %s"), cmd, (params[0] ? params[0] : "")); } /* this command allows user to type /me in Jabber, GG and other not-overriding it protocols * without need to prefix with space (to keep syntax the same as with overriding ones) */ static COMMAND(cmd_me) { if (!target) { printq("not_enough_params", name); return -1; } return command_exec_format(target, session, 0, "/ /me %s", params[0]?params[0]:""); } /** * command_add() * * Add command, and make it known for ekg2. * * @note About params XXX * * @note @a flag param, there're two types of it. * Informational like: * - COMMAND_ISALIAS - When it's alias command.
* - COMMAND_ISSCRIPT - When it's script command.
* and
* Conditionals, checked at executing command @@ command_exec() like: [XXX, dorobic] * - COMMAND_ENABLEREQPARAMS -
* - COMMAND_PARAMASTARGET
* - SESSION_MUSTBECONNECTED -
* - SESSION_MUSTBELONG
* - SESSION_MUSTHAS
* - SESSION_MUSTHASPRIVATE
* * @param plugin - plugin which handle this command * @param name - name of command * @param params - space seperated paramlist (read note for more details!) * @param function - function handler * @param flags - bitmask from commands.h (read note for more details!) * @param possibilities - optional space separated list of possible params.. completion useful * * @return Pointer to added command_t *, or NULL if name was NULL. * */ command_t *command_add(plugin_t *plugin, const char *name, char *params, command_func_t function, command_flags_t flags, char *possibilities) { command_t *c; if (!name) return NULL; c = xmalloc(sizeof(command_t)); c->name = name; c->params = params ? array_make(params, (" "), 0, 1, 1) : NULL; c->function = function; c->flags = flags; c->plugin = plugin; c->possibilities = possibilities ? array_make(possibilities, " ", 0, 1, 1) : NULL; commands_add(c); return c; } /* * command_remove() * * usuwa komend z listy. * * - plugin - plugin obsugujcy, * - name - nazwa komendy. */ int command_remove(plugin_t *plugin, const char *name) { GSList *cl; for (cl = commands; cl; cl = cl->next) { command_t *c = cl->data; if (!xstrcasecmp(name, c->name) && plugin == c->plugin) { commands_remove(c); return 0; } } return -1; } /* * rodzaje parametrw komend: * * '?' - it means nothing * 'U' - uid typed by hand, sender of message * 'u' - name or uid from the contact list * 'C' - name of conference * 'c' - name of command * 'i' - nicks of ignored * 'b' - nicks of blocked * 'v' - variable name * 'p' - params typed in possibilities * 'P' - plugin's name * 'f' - file * 'e' - event name * 'I' - ignored level * 's' - session name * 'S' - session variable name * 'r' - session description * 'o' - directory * 'm' - metacontact * 'w' - window name * * jeeli parametr == 'p' to 6 argument funkcji command_add() przyjmuje jako argument * tablic z moliwymi uzupenieniami * * parametry te s tablic, dlatego uywamy makr possibilities() i params() - najlepiej * przeanalizowa przykady */ /* * command_init() * * inicjuje list domylnych komend. */ void command_init() { command_add(NULL, ("!"), "?", cmd_exec, 0, NULL); command_add(NULL, ("?"), "c vS", cmd_help, 0, NULL); command_add(NULL, ("_addtab"), "!", cmd_test_addtab, COMMAND_ENABLEREQPARAMS, NULL); command_add(NULL, ("_debug"), "!", cmd_test_debug, COMMAND_ENABLEREQPARAMS, NULL); command_add(NULL, ("_debug_dump"), NULL, cmd_test_debug_dump, 0, NULL); command_add(NULL, ("_deltab"), "!", cmd_test_deltab, COMMAND_ENABLEREQPARAMS, NULL); command_add(NULL, ("_desc"), "r", cmd_desc, SESSION_MUSTHAS, NULL); command_add(NULL, ("_dns2"), "!", cmd_test_dns2, COMMAND_ENABLEREQPARAMS, NULL); command_add(NULL, ("_fds"), NULL, cmd_test_fds, 0, NULL); command_add(NULL, ("_mem"), NULL, cmd_test_mem, 0, NULL); command_add(NULL, ("_msg"), "uUC ?", cmd_test_send, 0, NULL); command_add(NULL, ("_plugins"), NULL, cmd_debug_plugins, 0, NULL); command_add(NULL, ("_queries"), NULL, cmd_debug_queries, 0, NULL); command_add(NULL, ("_query"), "? ? ? ? ? ? ? ? ? ?", cmd_debug_query, 0,NULL); command_add(NULL, ("_segv"), NULL, cmd_test_segv, 0, NULL); command_add(NULL, ("_theme_dump"), "?", cmd_test_debug_theme, 0, NULL); command_add(NULL, ("_timers"), NULL, cmd_debug_timers, 0, NULL); command_add(NULL, ("_watches"), NULL, cmd_debug_watches, 0, NULL); command_add(NULL, ("add"), "!U ? p", cmd_add, COMMAND_ENABLEREQPARAMS, "-f --find"); command_add(NULL, ("alias"), "p ?", cmd_alias, 0, "-a --add -A --append -d --del -l --list"); command_add(NULL, ("at"), "p ? ? c", cmd_at, 0, "-a --add -d --del -l --list"); command_add(NULL, ("beep"), NULL, cmd_beep, 0, NULL); command_add(NULL, ("bind"), "p ? ?", cmd_bind, 0, "-a --add -d --del -e --exec -l --list -L --list-default -S --set"); command_add(NULL, ("clear"), NULL, cmd_window, 0, NULL); command_add(NULL, ("conference"), "p C uU", cmd_conference, SESSION_MUSTHAS, "-a --add -j --join -d --del -i --ignore -u --unignore -r --rename -f --find -l --list"); command_add(NULL, ("dcc"), "p u f ?", cmd_dcc, 0, "send rsend get resumce rvoice voice close list"); command_add(NULL, ("del"), "u ?", cmd_del, 0, NULL); command_add(NULL, ("echo"), "?", cmd_echo, 0, NULL); command_add(NULL, ("eval"), "!", cmd_eval, COMMAND_ENABLEREQPARAMS, NULL); command_add(NULL, ("exec"), "p UuC ?", cmd_exec, 0, "-m --msg -b --bmsg"); command_add(NULL, ("for"), "!p !? !c", cmd_for, COMMAND_ENABLEREQPARAMS, "-s --sessions -u --users -w --windows"); command_add(NULL, ("help"), "c vS", cmd_help, 0, NULL); command_add(NULL, ("ignore"), "uUC I", cmd_ignore, 0, "status descr notify msg dcc events *"); command_add(NULL, ("last"), "CpuU CuU", cmd_last, SESSION_MUSTHAS, "-c --clear -s --stime -n --number"); command_add(NULL, ("list"), "CpuUsm p p p p p p p p", cmd_list, SESSION_MUSTHAS, "-a --active -A --away -B --blocked -d --description -i --inactive -I --invisible -m --member -n --notavail -o --offline -O --online"); command_add(NULL, ("me"), "?", cmd_me, SESSION_MUSTBECONNECTED, NULL); command_add(NULL, ("metacontact"), "mp m s uU ?", cmd_metacontact, 0, "-a --add -d --del -i --add-item -r --del-item -l --list"); command_add(NULL, ("on"), "p e ? UuC c", cmd_on, SESSION_MUSTHAS, "-a --add -d --del -l --list" ); command_add(NULL, ("play"), "!f", cmd_play, COMMAND_ENABLEREQPARAMS, NULL); command_add(NULL, ("plugin"), "P ?", cmd_plugin, 0, NULL); command_add(NULL, ("query"), "!uUCms ?", cmd_query, SESSION_MUSTHAS | COMMAND_ENABLEREQPARAMS, "-c --clear"); command_add(NULL, ("queue"), "puUC uUC", cmd_queue, 0, "-c --clear"); command_add(NULL, ("quit"), "r", cmd_quit, 0, NULL); command_add(NULL, ("reload"), NULL, cmd_reload, 0, NULL); command_add(NULL, ("save"), NULL, cmd_save, 0, NULL); command_add(NULL, ("say"), "!", cmd_say, COMMAND_ENABLEREQPARAMS, "-c --clear"); command_add(NULL, ("script") , ("p ?"), cmd_script, 0, "--list --load --unload --varlist --reset"); /* todo ?!!? */ command_add(NULL, ("script:autorun"), ("?"), cmd_script, 0, ""); command_add(NULL, ("script:list") , ("?"), cmd_script, 0, ""); command_add(NULL, ("script:load") , ("f"), cmd_script, 0, ""); command_add(NULL, ("script:reset") , ("?"), cmd_script, 0, ""); command_add(NULL, ("script:unload") , ("?"), cmd_script, 0, ""); command_add(NULL, ("script:varlist"), ("?"), cmd_script, 0, ""); command_add(NULL, ("session"), "psS psS sS ?", session_command, 0, "-a --add -d --del -l --list -g --get -s --set -w --sw"); command_add(NULL, ("set"), "pv v ?", cmd_set, 0, "-a --all -q --quiet"); command_add(NULL, ("status"), "s", cmd_status, 0, NULL); command_add(NULL, ("tabclear"), "p", cmd_tabclear, 0, "-o --offline"); command_add(NULL, ("timer"), "p ? ? c", cmd_timer, 0, "-a --add -d --del -l --list"); command_add(NULL, ("unignore"), "i ?", cmd_ignore, 0, NULL); command_add(NULL, ("version"), NULL, cmd_version, 0, NULL); command_add(NULL, ("window"), "p ? p", cmd_window, 0, "active clear kill last list move new next prev switch refresh left right"); } /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: * vim: noet */ ekg2-0.4~pre+20120506.1/ekg/commands.h000066400000000000000000000107751175142753400167070ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2001-2002 Wojtek Kaniewski * Dawid Jarosz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __EKG_COMMANDS_H #define __EKG_COMMANDS_H #include "dynstuff.h" #include "plugins.h" #include "themes.h" #include "sessions.h" #ifdef __cplusplus extern "C" { #endif #define printq(x...) do { if (!quiet) { print(x); } } while(0) #define COMMAND(x) int x(const char *name, const char **params, session_t *session, const char *target, int quiet) typedef enum { /* INFORMATIONAL FLAGS */ COMMAND_ISALIAS = 0x01, /* command is binded by alias management */ COMMAND_ISSCRIPT = 0x02, /* command is binded by script management */ COMMAND_WITH_RESOURCE = 0x04, /* [XXX] command uses resource, and resource should be passed */ COMMAND_PASS_UNCHANGED = 0x08, /* WYSIWYG, pass unchanged line, as first argument */ /* .... */ /* CONDITIONAL FLAGS */ COMMAND_ENABLEREQPARAMS = 0x10, /* '!' in params means that arg must exist in params[..] (?) */ COMMAND_PARAMASTARGET = 0x20, /* when params[0] != NULL, than target = params[0] and then params list moves up (params++ ; params[0] == params[1] and so on */ SESSION_MUSTBECONNECTED = 0x40, /* session must be connected to execute that command */ SESSION_MUSTBELONG = 0x80, /* command must come from the same plugin as session (?) */ SESSION_MUSTHAS = 0x100, /* if session == NULL, we try session_current, if still NULL. we return -1... mh, i really don't know if this flag is obsolete... but we do simillar thing in many places in code, so implemented. */ SESSION_MUSTHASPRIVATE = 0x200, /* session must exist and has private struct */ COMMAND_TARGET_VALID_UID = 0x400 /* before executing handler, check if target (or params[0] if COMMAND_PARAMASTARGET set) is valid uid for current session, or we've got smb with this nickname on userlist... (read: we check if get_uid(session, target) return smth, if not we print message) */ } command_flags_t; typedef COMMAND(command_func_t); typedef struct command { /* public: */ const char *name; plugin_t *plugin; /* private: */ char **params; command_func_t *function; command_flags_t flags; char **possibilities; } command_t; #ifndef EKG2_WIN32_NOFUNCTION extern GSList *commands; command_t *command_add(plugin_t *plugin, const char *name, char *params, command_func_t function, command_flags_t flags, char *possibilities); int command_remove(plugin_t *plugin, const char *name); command_t *command_find (const char *name); void command_init(); void commands_remove(command_t *c); void commands_destroy(); int command_exec(const char *target, session_t *session, const char *line, int quiet); int command_exec_params(const char *target, session_t *session, int quiet, const char *command, ...); int command_exec_format(const char *target, session_t *session, int quiet, const char *format, ...); COMMAND(cmd_add); COMMAND(cmd_alias_exec); COMMAND(cmd_exec); COMMAND(cmd_list); COMMAND(cmd_dcc); COMMAND(cmd_bind); /* bindings.c */ COMMAND(session_command); /* sessions.c */ COMMAND(cmd_on); /* events.c */ COMMAND(cmd_metacontact); /* metacontacts.c */ COMMAND(cmd_script); /* script.c */ #endif /* * jaka malutka lista tych, do ktrych byy wysyane wiadomoci. */ #define SEND_NICKS_MAX 100 extern char *send_nicks[SEND_NICKS_MAX]; extern int send_nicks_count, send_nicks_index; #ifndef EKG2_WIN32_NOFUNCTION void tabnick_add(const char *nick); void tabnick_remove(const char *nick); int match_arg(const char *arg, char shortopt, const char *longopt, int longoptlen); /* wyniki ostatniego szukania */ extern char *last_search_first_name; extern char *last_search_last_name; extern char *last_search_nickname; extern char *last_search_uid; #endif #ifdef __cplusplus } #endif #endif /* __EKG_COMMANDS_H */ /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/ekg/completion.c000066400000000000000000000750201175142753400172440ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2003 Wojtek Kaniewski * 2004 Piotr Kupisiewicz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #include #include #include /* nadpisujemy funkcj xstrncasecmp() odpowiednikiem z obsug polskich znakw */ #define xstrncasecmp(x...) xstrncasecmp_pl(x) char **ekg2_completions = NULL; static char **completions = NULL; /* lista dopenie */ static char *last_line = NULL; static char *last_line_without_complete = NULL; static int last_pos = -1; int continue_complete = 0; int continue_complete_count = 0; command_t *actual_completed_command; session_t *session_in_line; static void command_generator(const char *text, int len) { const char *slash = (""), *dash = (""); GSList *cl; session_t *session = session_current; if (*text == ('/')) { slash = ("/"); text++; len--; } if (*text == ('^')) { dash = ("^"); text++; len--; } if (window_current->target) slash = ("/"); for (cl = commands; cl; cl = cl->next) { command_t *c = cl->data; char *without_sess_id = NULL; int plen = 0; if (session && session->uid) plen = (int)(xstrchr(session->uid, ':') - session->uid) + 1; if (session && !xstrncasecmp(c->name, session->uid, plen)) without_sess_id = xstrchr(c->name, ':'); if (!xstrncasecmp(text, c->name, len) && !array_item_contains(completions, c->name, 1)) array_add_check(&completions, saprintf(("%s%s%s"), slash, dash, c->name), 1); else if (without_sess_id && !array_item_contains(completions, without_sess_id + 1, 1) && !xstrncasecmp(text, without_sess_id + 1, len)) array_add_check(&completions, saprintf(("%s%s%s"), slash, dash, without_sess_id + 1), 1); } } static void events_generator(const char *text, int len) { int i; for (i = 0; events_all && events_all[i]; i++) if (!xstrncasecmp(text, events_all[i], len)) array_add_check(&completions, xstrdup(events_all[i]), 1); } static void ignorelevels_generator(const char *text, int len) { int i; const char *tmp = NULL; char *pre = NULL; if ((tmp = xstrrchr(text, '|')) || (tmp = xstrrchr(text, ','))) { char *foo; pre = xstrdup(text); foo = xstrrchr(pre, *tmp); *(foo + 1) = 0; len -= tmp - text + 1; tmp = tmp + 1; } else tmp = text; for (i = 0; ignore_labels[i].name; i++) if (!xstrncasecmp(tmp, ignore_labels[i].name, len)) array_add_check(&completions, ((tmp == text) ? xstrdup(ignore_labels[i].name) : saprintf("%s%s", pre, ignore_labels[i].name)), 1); xfree(pre); } static void unknown_uin_generator(const char *text, int len) { int i; for (i = 0; i < send_nicks_count; i++) { if (send_nicks[i] && xstrchr(send_nicks[i], ':') && xisdigit(xstrchr(send_nicks[i], ':')[1]) && !xstrncasecmp(text, send_nicks[i], len)) { array_add_check(&completions, xstrdup(send_nicks[i]), 1); } } } static void known_uin_generator(const char *text, int len) { int done = 0; userlist_t *ul; session_t *s; char *tmp = NULL, *session_name = NULL; int tmp_len = 0; newconference_t *c; if (!session_current) return; s = session_current; tmp = xstrrchr(text, '/'); if (tmp && tmp + 1) { tmp++; tmp_len = xstrlen(tmp); session_name = xstrndup(text, xstrlen(text) - tmp_len - 1); if (session_find(session_name)) s = session_find(session_name); } for (ul = s->userlist; ul; ul = ul->next) { userlist_t *u = ul; if (u->nickname && !xstrncasecmp(text, u->nickname, len)) { array_add_check(&completions, xstrdup(u->nickname), 1); done = 1; } if (u->nickname && tmp && !xstrncasecmp(tmp, u->nickname, tmp_len)) { array_add_check(&completions, saprintf(("%s/%s"), session_name, u->nickname), 1); done = 1; } } for (ul = s->userlist; ul; ul = ul->next) { userlist_t *u = ul; if (!done && !xstrncasecmp(text, u->uid, len)) { array_add_check(&completions, xstrdup(u->uid), 1); } if (!done && tmp && !xstrncasecmp(tmp, u->uid, tmp_len)) array_add_check(&completions, saprintf(("%s/%s"), session_name, u->uid), 1); } if (!window_current) goto end; if ((c = newconference_find(window_current->session, window_current->target))) ul = c->participants; else ul = window_current->userlist; for (; ul; ul = ul->next) { userlist_t *u = ul; if (u->uid && !xstrncasecmp(text, u->uid, len)) { array_add_check(&completions, xstrdup(u->uid), 1); } if (u->nickname && !xstrncasecmp(text, u->nickname, len)) { array_add_check(&completions, xstrdup(u->nickname), 1); } } end: if (session_name) xfree(session_name); } static void conference_generator(const char *text, int len) { newconference_t *c; for (c = newconferences; c; c = c->next) { if (!xstrncasecmp(text, c->name, len)) array_add_check(&completions, xstrdup(c->name), 1); } } static void plugin_generator(const char *text, int len) { GSList *pl; for (pl = plugins; pl; pl = pl->next) { const plugin_t *p = pl->data; if (!xstrncasecmp(text, p->name, len)) { array_add_check(&completions, xstrdup(p->name), 1); } if ((text[0] == '+' || text[0] == '-') && !xstrncasecmp(text + 1, p->name, len - 1)) { char *tmp = saprintf(("%c%s"), text[0], p->name); array_add_check(&completions, tmp, 1); } } } static void variable_generator(const char *text, int len) { GSList *vl; for (vl = variables; vl; vl = vl->next) { variable_t *v = vl->data; if (v->display == 2) continue; if (*text == '-') { if (!xstrncasecmp(text + 1, v->name, len - 1)) array_add_check(&completions, saprintf("-%s", v->name), 1); } else { if (!xstrncasecmp(text, v->name, len)) { array_add_check(&completions, xstrdup(v->name), 1); } } } } static void ignored_uin_generator(const char *text, int len) { session_t *s; userlist_t *ul; if (!session_current) return; s = session_current; for (ul = s->userlist; ul; ul = ul->next) { userlist_t *u = ul; if (!ignored_check(s, u->uid)) continue; if (!u->nickname) { if (!xstrncasecmp(text, u->uid, len)) array_add_check(&completions, xstrdup(u->uid), 1); } else { if (u->nickname && !xstrncasecmp(text, u->nickname, len)) array_add_check(&completions, xstrdup(u->nickname), 1); } } } static void blocked_uin_generator(const char *text, int len) { session_t *s; userlist_t *ul; if (!session_current) return; s = session_current; for (ul = s->userlist; ul; ul = ul->next) { userlist_t *u = ul; if (!ekg_group_member(u, "__blocked")) continue; if (!u->nickname) { if (!xstrncasecmp(text, u->uid, len)) array_add_check(&completions, xstrdup(u->uid), 1); } else { if (u->nickname && !xstrncasecmp(text, u->nickname, len)) array_add_check(&completions, xstrdup(u->nickname), 1); } } } static void empty_generator(const char *text, int len) { } static void dir_generator(const char *text, int len) { #ifdef HAVE_SCANDIR struct dirent **namelist = NULL; char *dname, *tmp; const char *fname; int count, i; /* `dname' zawiera nazw katalogu z koczcym znakiem `/', albo * NULL, jeli w dopenianym tekcie nie ma cieki. */ dname = xstrdup(text); if ((tmp = xstrrchr(dname, '/'))) { tmp++; *tmp = 0; } else { xfree(dname); dname = NULL; } /* `fname' zawiera nazw szukanego pliku */ fname = xstrrchr(text, '/'); if (fname) fname++; else fname = text; /* zbierzmy list plikw w danym katalogu */ count = scandir((dname) ? dname : ".", &namelist, NULL, alphasort); debug("dname=\"%s\", fname=\"%s\", count=%d\n", (dname) ? dname : "(null)", (fname) ? fname : "(null)", count); for (i = 0; i < count; i++) { char *name = namelist[i]->d_name, *tmp = saprintf("%s%s", (dname) ? dname : "", name); struct stat st; if (!stat(tmp, &st)) { if (!S_ISDIR(st.st_mode)) { xfree(tmp); xfree(namelist[i]); continue; } } xfree(tmp); if (!xstrcmp(name, ".")) { xfree(namelist[i]); continue; } /* jeli mamy `..', sprawd czy katalog skada si z * `../../../' lub czego takiego. */ if (!xstrcmp(name, "..")) { const char *p; int omit = 0; for (p = dname; p && *p; p++) { if (*p != '.' && *p != '/') { omit = 1; break; } } if (omit) { xfree(namelist[i]); continue; } } if (!strncmp(name, fname, xstrlen(fname))) { name = saprintf("%s%s%s", (dname) ? dname : "", name, "/"); array_add_check(&completions, name, 1); } xfree(namelist[i]); } xfree(dname); xfree(namelist); #endif } static void file_generator(const char *text, int len) { #ifdef HAVE_SCANDIR struct dirent **namelist = NULL; char *dname; char *tmp; const char *fname; int count, i; /* `dname' zawiera nazw katalogu z koczcym znakiem `/', albo * NULL, jeli w dopenianym tekcie nie ma cieki. */ dname = xstrdup(text); if ((tmp = xstrrchr(dname, '/'))) { tmp++; *tmp = 0; } else { xfree(dname); dname = NULL; } /* `fname' zawiera nazw szukanego pliku */ fname = xstrrchr(text, '/'); if (fname) fname++; else fname = text; again: /* zbierzmy list plikw w danym katalogu */ count = scandir((dname) ? dname : ".", &namelist, NULL, alphasort); debug("dname=\"%s\", fname=\"%s\", count=%d\n", (dname) ? dname : "(null)", (fname) ? fname : "(null)", count); for (i = 0; i < count; i++) { char *name = namelist[i]->d_name, *tmp = saprintf("%s%s", (dname) ? dname : "", name); struct stat st; int isdir = 0; if (!stat(tmp, &st)) isdir = S_ISDIR(st.st_mode); xfree(tmp); if (!xstrcmp(name, ".")) { xfree(namelist[i]); continue; } /* jeli mamy `..', sprawd czy katalog skada si z * `../../../' lub czego takiego. */ if (!xstrcmp(name, "..")) { const char *p; int omit = 0; for (p = dname; p && *p; p++) { if (*p != '.' && *p != '/') { omit = 1; break; } } if (omit) { xfree(namelist[i]); continue; } } if (!strncmp(name, fname, xstrlen(fname))) { name = saprintf("%s%s%s", (dname) ? dname : "", name, (isdir) ? "/" : ""); array_add_check(&completions, name, 1); } xfree(namelist[i]); } /* jeli w dopenieniach wyldowa tylko jeden wpis i jest katalogiem * to wejd do niego i szukaj jeszcze raz */ if (g_strv_length(completions) == 1 && xstrlen(completions[0]) > 0 && completions[0][xstrlen(completions[0]) - 1] == '/') { xfree(dname); dname = xstrdup(completions[0]); fname = ""; xfree(namelist); namelist = NULL; g_strfreev(completions); completions = NULL; goto again; } xfree(dname); xfree(namelist); #endif } /* * theme_generator_adding () * * function that helps theme_generator to add all of the paths * * dname - path * themes_only - only the .theme extension * */ static void theme_generator_adding(const char *text, int len, const char *dname, int themes_only) { #ifdef HAVE_SCANDIR struct dirent **namelist = NULL; int count, i; count = scandir((dname) ? dname : ".", &namelist, NULL, alphasort); for(i = 0; i < count; i++) { struct stat st; char *name = namelist[i]->d_name, *tmp = saprintf("%s/%s", (dname) ? dname : "", name), *tmp2; if (!stat(tmp, &st)) { if (S_ISDIR(st.st_mode) && stat(saprintf("%s%s%s", tmp, "/", name), &st) == -1 && stat(saprintf("%s%s%s.theme", tmp, "/", name), &st) == -1) { xfree(namelist[i]); xfree(tmp); continue; } } xfree(tmp); if (!xstrcmp(name, ".") || !xstrcmp(name, "..")) { xfree(namelist[i]); continue; } tmp2 = xstrndup(name, xstrlen(name) - xstrlen(xstrstr(name, ".theme"))); if (!xstrncmp(text, name, len) || (!xstrncmp(text, tmp2, len) && !themes_only) ) array_add_check(&completions, tmp2, 1); else xfree(tmp2); xfree(namelist[i]); } xfree(namelist); #endif } static void theme_generator(const char *text, int len) { theme_generator_adding(text, len, DATADIR "/themes", 0); theme_generator_adding(text, len, prepare_path("", 0), 1); theme_generator_adding(text, len, prepare_path("themes", 0), 0); } static void possibilities_generator(const char *text, int len) { int i; command_t *c = actual_completed_command; if (!c) return; for (i = 0; c && c->possibilities && c->possibilities[i]; i++) if (!xstrncmp(text, c->possibilities[i], len)) { array_add_check(&completions, xstrdup(c->possibilities[i]), 1); } } static void window_generator(const char *text, int len) { window_t *w; for (w = windows; w; w=w->next) { if (!w->target || xstrncmp(text, w->target, len)) continue; array_add_check(&completions, xstrdup(w->target), 0); } } static void sessions_generator(const char *text, int len) { session_t *v; for (v = sessions; v; v = v->next) { if (*text == '-') { if (!xstrncasecmp(text + 1, v->uid, len - 1)) array_add_check(&completions, saprintf("-%s", v->uid), 1); if (!xstrncasecmp(text + 1, v->alias, len - 1)) array_add_check(&completions, saprintf("-%s", v->alias), 1); } else { if (!xstrncasecmp(text, v->uid, len)) array_add_check(&completions, xstrdup(v->uid), 1); if (!xstrncasecmp(text, v->alias, len)) array_add_check(&completions, xstrdup(v->alias), 1); } } } static void metacontacts_generator(const char *text, int len) { metacontact_t *m; for (m = metacontacts; m; m = m->next) { if (!xstrncasecmp(text, m->name, len)) array_add_check(&completions, xstrdup(m->name), 1); } } static void sessions_var_generator(const char *text, int len) { int i; plugin_t *p; if (!session_in_line) return; if (!(p = session_in_line->plugin)) { debug_error("[%s:%d] Plugin disappear [s: %s]\n", __FILE__, __LINE__, __(session_in_line->uid)); return; } if (!p->params) return; for (i = 0; (p->params[i].key /* && p->params[i].id != -1 */); i++) { if(*text == '-') { if (!xstrncasecmp(text + 1, p->params[i].key, len - 1)) array_add_check(&completions, saprintf(("-%s"), p->params[i].key), 1); } else { if (!xstrncasecmp(text, p->params[i].key, len)) { array_add_check(&completions, xstrdup(p->params[i].key), 1); } } } } static void reason_generator(const char *text, int len) { char *descr = session_current ? session_current->descr : NULL; if (descr && !xstrncasecmp(text, descr, len)) { /* not to good solution to avoid descr changing by complete */ array_add_check(&completions, saprintf(("\001%s"), session_current->descr), 1); } } static struct { char ch; void (*generate)(const char *text, int len); } generators[] = { { 'u', known_uin_generator }, { 'C', conference_generator }, { 'U', unknown_uin_generator }, { 'c', command_generator }, { 'x', empty_generator }, { 'i', ignored_uin_generator }, { 'b', blocked_uin_generator }, { 'v', variable_generator }, { 'p', possibilities_generator }, { 'P', plugin_generator }, { 'w', window_generator }, { 'f', file_generator }, { 'e', events_generator }, { 's', sessions_generator }, { 'S', sessions_var_generator }, { 'I', ignorelevels_generator }, { 'r', reason_generator }, { 't', theme_generator }, { 'o', dir_generator }, { 'm', metacontacts_generator }, { 0, NULL } }; /* * ekg2_complete() * * funkcja obsugujca dopenianie klawiszem tab. * Dziaanie: * - Wprowadzona linia dzielona jest na wyrazy (uwzgldniajc przecinki i znaki cudzyslowia) * - nastpnie znaki separacji znajdujce si midzy tymi wyrazami wrzucane s do tablicy separators * - dalej sprawdzane jest za pomoc zmiennej word_current (okrelajcej aktualny wyraz bez uwzgldnienia * przecinkw - po to, aby wiedzie czy w przypadku np funkcji /query ma by szukane dopenienie * - zmienna word odpowiada za aktualny wyraz (*z* uwzgldnieniem przecinkw) * - words - tablica zawieraj wszystkie wyrazy * - gdy jest to moliwe szukane jest dopenienie * - gdy dopenie jest wicej ni jedno (count > 1) wywietlamy tylko "wspln" cz wszystkich dopenie * np ,,que'' w przypadku funkcji /query i /queue * - gdy dopenienie jest tylko jedno wywietlamy owo dopenienie * - przy wywietlaniu dopenienia caa linijka konstruowana jest od nowa, poniewa nie wiadomo w ktrym miejscu * podany wyraz ma zosta "wsadzony", std konieczna jest tablica separatorw, tablica wszystkich wyrazw itd ... * - przeskakiwanie midzy dopenieniami po drugim TABie */ int ekg2_complete(int *line_start, int *line_index, char *line, int line_maxlen) { char *start, **words, *separators; char *cmd; int i, count, word, j, words_count, word_current, open_quote; size_t linelen; /* * sprawdzamy czy mamy kontynuowa dopenianie (przeskakiwa midzy dopenianymi wyrazami * dziaa to tylko gdy jestemy na kocu linijki, gdy nie ma sensu robi takiego przeskakiwania * w rodku linii - wtedy sama lista jest wystarczajca */ if (xstrcmp(last_line, line) || last_pos != *line_index || *line_index != xstrlen(line)) { continue_complete = 0; continue_complete_count = 0; } /* * jeli uzbierano ju co to prbujemy wywietli wszystkie moliwoci */ if (completions && !continue_complete) { /* wczamy nastpny etap dopenienia - przeskakiwanie midzy dopenianymi wyrazami */ continue_complete = 1; continue_complete_count = 0; last_line = xstrdup(line); last_pos = *line_index; xfree(last_line_without_complete); last_line_without_complete = xstrdup(line); ekg2_completions = completions; return 1; } /* jeeli przeskakujemy to trzeba wrci z lini do poprzedniego stanu */ if (continue_complete) { xstrcpy(line, last_line_without_complete); } linelen = xstrlen(line); /* zerujemy co mamy */ start = xmalloc((linelen + 1)*sizeof(char)); words = NULL; count = 0; /* podziel (uwzgldniajc cudzysowia)*/ for (i = 0, j = 0, open_quote = 0; i < linelen; i++) { if(line[i] == '"') { for(j = 0, i++; i < linelen && line[i] != '"'; i++, j++) start[j] = line[i]; if (i == linelen) open_quote = 1; } else for(j = 0; i < linelen && !xisspace(line[i]) && line[i] != ','; j++, i++) start[j] = line[i]; start[j] = '\0'; /* "przewijamy" wiksz ilo spacji */ for(i++; i < linelen && (xisspace(line[i]) || line[i] == ','); i++); i--; array_add(&words, xstrdup(start)); } /* jeeli ostatnie znaki to spacja, albo przecinek to trzeba doda jeszcze pusty wyraz do words */ if (linelen > 1 && (line[linelen - 1] == ' ' || line[linelen - 1] == ',') && !open_quote) array_add(&words, xstrdup((""))); /* for(i = 0; i < g_strv_length(words); i++) debug("words[i = %d] = \"%s\"\n", i, words[i]); */ /* inicjujemy pamic dla separators */ if (words != NULL) separators = xmalloc(g_strv_length(words) * sizeof(char) + 1); else separators = NULL; /* sprawd, gdzie jestemy (uwzgdniajc cudzysowia) i dodaj separatory*/ for (word = 0, i = 0; i < linelen; i++, word++) { if(line[i] == '"') { for(j = 0, i++; i < linelen && line[i] != '"'; j++, i++) start[j] = line[i]; } else { for(j = 0; i < linelen && !xisspace(line[i]) && line[i] != ','; j++, i++) start[j] = line[i]; } /* "przewijamy */ for(i++; i < linelen && (xisspace(line[i]) || line[i] == ','); i++); /* ustawiamy znak koca */ start[j] = '\0'; /* jeeli to koniec linii, to koczymy t zabaw */ if(i >= linelen) break; /* obniamy licznik o 1, eby wszystko byo okey, po "przewijaniu" */ i--; /* hmm, jestemy ju na wyrazie wskazywany przez kursor ? */ if(i >= *line_index) break; } /* dodajmy separatory - pewne rzeczy podobne do ptli powyej */ for (i = 0, j = 0; i < linelen; i++, j++) { if(line[i] == '"') { for(i++; i < linelen && line[i] != '"'; i++); if(i < linelen) separators[j] = line[i + 1]; } else { for(; i < linelen && !xisspace(line[i]) && line[i] != ','; i++); separators[j] = line[i]; } for(i++; i < linelen && (xisspace(line[i]) || line[i] == ','); i++); i--; } if (separators) separators[j] = '\0'; // koniec ciagu /* aktualny wyraz bez uwzgledniania przecinkow */ for (i = 0, words_count = 0, word_current = 0; i < linelen; i++, words_count++) { for(; i < linelen && !xisspace(line[i]); i++) if(line[i] == '"') for(i++; i < linelen && line[i] != '"'; i++); for(i++; i < linelen && xisspace(line[i]); i++); if(i >= linelen) { word_current = words_count + 1; break; } i--; word_current++; /* hmm, jestemy ju na wyrazie wskazywany przez kursor ? */ if(i >= *line_index) break; } words_count = g_strv_length(words); if (linelen) { /* trzeba pododawa troch do licznikw w spefycicznych (patrz warunki) sytuacjach */ if (xisspace(line[linelen - 1])) word_current++; if ((xisspace(line[linelen - 1]) || line[linelen - 1] == ',') && word + 1 == words_count -1 ) word++; /* unused? if (xisspace(line[linelen - 1])) words_count++; */ } /* debug("word = %d\n", word); debug("start = \"%s\"\n", start); debug("words_count = %d\n", words_count); debug("word_current: %d\n", word_current); */ /* for(i = 0; i < xstrlen(separators); i++) debug("separators[i = %d] = \"%c\"\n", i, separators[i]); */ /* przeskakujemy na nastpny wyraz - jeeli konieczne i moliwe */ if (continue_complete && completions) { int cnt = continue_complete_count; count = g_strv_length(completions); line[0] = '\0'; linelen = 0; if (continue_complete_count >= count - 1) continue_complete_count = 0; else continue_complete_count++; if (!completions[cnt]) /* nigdy nie powinno si zdarzy, ale na wszelki ... */ goto cleanup; for(i = 0; i < words_count; i++) { if(i == word) { if(xstrchr(completions[cnt], ('\001'))) { if(completions[cnt][0] == '"') xstrncat(line, completions[cnt] + 2, xstrlen(completions[cnt]) - 2 - 1 ); else xstrncat(line, completions[cnt] + 1, xstrlen(completions[cnt]) - 1); } else xstrcat(line, completions[cnt]); linelen = xstrlen(line); *line_index = linelen + 1; } else { if(xstrchr(words[i], (' '))) { char *tmp = saprintf(("\"%s\""), words[i]); xstrcat(line, tmp); xfree(tmp); } else xstrcat(line, words[i]); } if((i == words_count - 1 && line[xstrlen(line) - 1] != ' ' )) xstrcat(line, (" ")); else if (line[xstrlen(line) - 1] != ' ') xstrncat(line, separators + i, 1); } /* ustawiamy dane potrzebne do nastpnego dopenienia */ xfree(last_line); last_line = xstrdup(line); last_pos = *line_index; goto cleanup; } cmd = saprintf(("/%s "), (config_tab_command) ? config_tab_command : "chat"); /* nietypowe dopenienie nickw przy rozmowach */ if (!xstrcmp(line, ("")) || (!xstrncasecmp(line, cmd, xstrlen(cmd)) && word == 2 && send_nicks_count > 0) || (!xstrcasecmp(line, cmd) && send_nicks_count > 0)) { if (send_nicks_index >= send_nicks_count) send_nicks_index = 0; if (send_nicks_count) { char *nick = send_nicks[send_nicks_index++]; snprintf(line, line_maxlen, (xstrchr(nick, ' ')) ? "%s\"%s\" " : "%s%s ", cmd, nick); } else snprintf(line, line_maxlen, "%s", cmd); *line_start = 0; *line_index = xstrlen(line); g_strfreev(completions); g_strfreev(words); xfree(start); xfree(separators); xfree(cmd); ekg2_completions = completions; return 0; } xfree(cmd); /* pocztek nicka, komendy? */ if (word == 0) { /* dj's fixes... */ if (start[0] != '/' && window_current && window_current->target) { known_uin_generator(start, xstrlen(start)); if (completions) { char *completion_char = (config_completion_char && *config_completion_char) ? config_completion_char : NULL; int nick_count = g_strv_length(completions); for (j = 0; completions[j]; j++) { string_t s; if ((nick_count == 1) || (!xstrchr(completions[j], ('"')) && !xstrchr(completions[j], ('\\')) && !xstrchr(completions[j], (' ')))) { if (completion_char) { s = string_init(("")); string_append(s, completions[j]); string_append(s, completion_char); xfree(completions[j]); completions[j] = string_free(s, 0); } continue; } s = string_init(("\"")); string_append(s, completions[j]); string_append_c(s, ('\"')); if (completion_char) string_append(s, completion_char); xfree(completions[j]); completions[j] = string_free(s, 0); } } } if (!completions) command_generator(start, xstrlen(start)); } else { char **params = NULL; int i; GSList *cl; char *cmd = (line[0] == '/') ? line + 1 : line; int len; for (len = 0; cmd[len] && !xisspace(cmd[len]); len++); /* first we look for some session complete */ if (session_current) { session_t *session = session_current; int plen = (int)(xstrchr(session->uid, ':') - session->uid) + 1; for (cl = commands; cl; cl = cl->next) { command_t *c = cl->data; if (xstrncasecmp(c->name, session->uid, plen)) continue; if (!xstrncasecmp(c->name + plen, cmd, len)) { params = c->params; actual_completed_command = c; goto exact_match; } } } for (cl = commands; cl; cl = cl->next) { command_t *c = cl->data; if (!xstrncasecmp(c->name, cmd, len)) { params = c->params; actual_completed_command = c; break; } } exact_match: /* for /set maybe we want to complete the file path */ if (!xstrncmp(cmd, "set", 3) && words[1] && words[2] && word_current == 3) { variable_t *v; if ((v = variable_find(words[1]))) { switch (v->type) { case VAR_FILE: file_generator(words[word], xstrlen(words[word])); break; case VAR_THEME: theme_generator(words[word], xstrlen(words[word])); break; case VAR_DIR: dir_generator(words[word], xstrlen(words[word])); break; default: break; } } } if (word_current > g_strv_length(params) + 1) word_current = g_strv_length(params) + 2; if (params && params[word_current - 2]) { int j; for (i = 0; generators[i].ch; i++) { for (j = 0; words[j]; j++) { if ((session_in_line = session_find(words[j]))) break; } if (!session_in_line) session_in_line = session_current; for (j = 0; params[word_current - 2][j]; j++) { if (generators[i].ch == params[word_current - 2][j]) { generators[i].generate(words[word], xstrlen(words[word])); } } } } if (completions) { for (j = 0; completions[j]; j++) { string_t s; if (!xstrchr(completions[j], ('"')) && !xstrchr(completions[j], ('\\')) && !xstrchr(completions[j], (' '))) continue; s = string_init(("\"")); string_append(s, completions[j]); string_append_c(s, ('\"')); xfree(completions[j]); completions[j] = string_free(s, 0); } } } count = g_strv_length(completions); /* * jeli jest tylko jedna molwio na dopenienie to drukujemy co mamy, * ewentualnie bierzemy cz wyrazw w cudzysowia ... * i uwaamy oczywicie na \001 (patrz funkcje wyej */ if (count == 1) { line[0] = '\0'; for(i = 0; i < words_count; i++) { if(i == word) { if (xstrchr(completions[0], '\001')) { if(completions[0][0] == '"') xstrncat(line, completions[0] + 2, xstrlen(completions[0]) - 2 - 1 ); else xstrncat(line, completions[0] + 1, xstrlen(completions[0]) - 1); } else xstrcat(line, completions[0]); *line_index = xstrlen(line) + 1; } else { if (xstrchr(words[i], (' '))) { char *tmp = saprintf(("\"%s\""), words[i]); xstrcat(line, tmp); xfree(tmp); } else xstrcat(line, words[i]); } if((i == words_count - 1 && line[xstrlen(line) - 1] != ' ' )) xstrcat(line, (" ")); else if (line[xstrlen(line) - 1] != ' ') xstrncat(line, separators + i, 1); } g_strfreev(completions); completions = NULL; } else /* * gdy jest wicej moliwoci to robimy podobnie jak wyej tyle, e czasem * trzeba uy cudzysowia tylko z jednej storny, no i trzeba dopeni do pewnego miejsca * w sumie proste rzeczy, ale jak wida jest troch opcji ... */ if (count > 1) { int common = 0; int common_len; int tmp = 0; int quotes = 0; char *s1 = completions[0]; if (*s1 =='"') { s1++; quotes = 1; } /* for(i = 0; completions[i]; i++) debug("completions[i] = %s\n", completions[i]); */ /* * moe nie za adne programowanie, ale skuteczne i w sumie jedyne w 100% speniajce * wymagania dopeniania (uwzgldnianie cudzywsowiw itp...) */ for (i=1; s1[common]; i++, common++) { for (j=1; j < count; j++) { char *s2 = completions[j]; if (*s2 == '"') { quotes = 1; s2++; } tmp = xstrncasecmp(s1, s2, i); /* debug("xstrncasecmp(\"%s\", \"%s\", %d) = %d\n", s1, s2, i, xstrncasecmp(s1, s2, i)); */ if (tmp) break; } if (tmp) break; } /* debug("common :%d\t\n", common); */ { /* that's the one _after_ last matching byte */ const gchar *p = &completions[0][common]; /* Check whether common didn't occur in a middle of a char * -> rewind to the previous char, then seek forward * * if we get the same offset, we got the whole char * if we get more, we partially matched something and need to dec */ common_len = common; if (common > 0) { const gchar *prev = g_utf8_prev_char(p); if (g_utf8_next_char(prev) != p) common_len -= (p - prev); } } if (xstrlen(line) + common_len < line_maxlen) { line[0] = '\0'; for(i = 0; i < words_count; i++) { if (i == word) { if (quotes == 1 && completions[0][0] != '"') xstrcat(line, ("\"")); if (completions[0][0] == '"') common++; if (completions[0][common - 1] == '"') common--; /* XXX: that was xstrncat_pl(), so in chars * but common is in bytes - wtf? INVESTIGATE */ strncat(line, completions[0], common); *line_index = xstrlen(line); } else { if (xstrchr(words[i], (' '))) { char *tmp = saprintf(("\"%s\""), words[i]); xstrcat(line, tmp); xfree(tmp); } else xstrcat(line, words[i]); } if(separators[i]) xstrncat(line, separators + i, 1); } } } cleanup: g_strfreev(words); xfree(start); xfree(separators); ekg2_completions = completions; return 0; } void ekg2_complete_clear() { g_strfreev(completions); completions = NULL; ekg2_completions = NULL; continue_complete = 0; continue_complete_count = 0; xfree(last_line); last_line = NULL; xfree(last_line_without_complete); last_line_without_complete = NULL; } /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/ekg/completion.h000066400000000000000000000005021175142753400172420ustar00rootroot00000000000000#ifndef __EKG2_COMPLETION_H #define __EKG2_COMPLETION_H extern char **ekg2_completions; int ekg2_complete(int *line_start, int *line_index, char *line, int line_maxlen); void ekg2_complete_clear(); #endif /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/ekg/configfile.c000066400000000000000000000457641175142753400172140ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2001-2005 Wojtek Kaniewski * Robert J. Wony * Pawe Maziarz * Dawid Jarosz * Piotr Domagalski * Piotr Kupisiewicz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include /* for getpid() */ #include #include #include #include #include #include "internal.h" static char *strip_quotes(char *line) { size_t linelen; char *buf; if (!(linelen = xstrlen(line))) return line; for (buf = line; *buf == '\"'; buf++); while (linelen > 0 && line[linelen - 1] == '\"') { line[linelen - 1] = 0; linelen--; } return buf; } /* * config_postread() * * initialized after config is read */ void config_postread() { if (config_windows_save && config_windows_layout) { char **targets = array_make(config_windows_layout, "|", 0, 0, 0); int i; for (i = 1; targets[i]; i++) { char *tmp; if (!xstrcmp(targets[i], "\"-\"")) continue; if (xstrcmp(targets[i], "") && (tmp = xstrrchr(targets[i], '/'))) { char *session_name = xstrndup(targets[i], xstrlen(targets[i]) - xstrlen(tmp)); session_t *s; if (!(s = session_find(session_name))) { xfree(session_name); continue; } tmp++; tmp = strip_spaces(tmp); tmp = strip_quotes(tmp); window_new(tmp, s, i + 1); xfree(session_name); } else { window_new(NULL, NULL, i + 1); } } g_strfreev(targets); } if (config_session_default) { session_t *s = session_find(config_session_default); if (s) { debug("setted default session to %s\n", s->uid); window_session_set(window_status, s); } else { debug_warn("default session not found\n"); } } config_upgrade(); query_emit(NULL, "config-postinit"); } static GPtrArray *config_openfiles = NULL; static GCancellable *config_cancellable = NULL; /** * ekg_fprintf() * * Output formatted string to a GOutputStream. * * @param f - writable GOutputStream. * @param format - the format string. * * @return TRUE on success, FALSE otherwise. * * @note The channel must be open for writing in blocking mode. */ gboolean ekg_fprintf(GOutputStream *f, const gchar *format, ...) { static GString *buf = NULL; va_list args; gsize out; GError *err = NULL; if (!buf) buf = g_string_sized_new(120); va_start(args, format); g_string_vprintf(buf, format, args); va_end(args); out = g_output_stream_write(f, buf->str, buf->len, NULL, &err); if (out < buf->len) { gpointer *p; debug_error("ekg_fprintf() failed (wrote %d out of %d): %s\n", out, buf->len, err ? err->message : "(no error?!)"); g_error_free(err); if (config_openfiles) { for (p = &config_openfiles->pdata[0]; p < &config_openfiles->pdata[config_openfiles->len]; p++) { if (*p == f) g_cancellable_cancel(config_cancellable); } } return FALSE; } return TRUE; } static GObject *config_open_real(const gchar *path, const gchar *mode) { GFile *f; GObject *instream, *stream; GError *err = NULL; const gchar modeline_prefix[] = "# vim:fenc="; f = g_file_new_for_path(path); switch (mode[0]) { case 'r': instream = G_OBJECT(g_file_read(f, NULL, &err)); break; case 'w': instream = G_OBJECT(g_file_replace(f, NULL, TRUE, G_FILE_CREATE_PRIVATE, NULL, &err)); break; default: g_assert_not_reached(); } if (!instream) { if (err->code != G_IO_ERROR_NOT_FOUND) debug_error("config_open(%s, %s) failed: %s\n", path, mode, err->message); g_error_free(err); g_object_unref(f); return NULL; } switch (mode[0]) { case 'r': stream = G_OBJECT(g_data_input_stream_new(G_INPUT_STREAM(instream))); { const gchar *wanted_enc, *buf; GCharsetConverter *conv; buf = read_line(G_DATA_INPUT_STREAM(stream)); if (!buf) { /* Some error occured, or EOF * in either case, there's no need to read that file anyway */ g_object_unref(stream); g_object_unref(f); return NULL; } /* XXX: support more modeline formats? */ if (g_str_has_prefix(buf, modeline_prefix)) wanted_enc = &buf[sizeof(modeline_prefix) - 1]; /* 1 for null terminator */ else wanted_enc = console_charset; /* fallback to locale */ g_filter_input_stream_set_close_base_stream(G_FILTER_INPUT_STREAM(stream), FALSE); g_object_unref(stream); if (!g_seekable_can_seek(G_SEEKABLE(instream)) || !g_seekable_seek(G_SEEKABLE(instream), 0, G_SEEK_SET, NULL, &err)) { /* ok, screwed it */ if (err) debug_error("config_open(): rewind failed: %s\n", err->message); g_error_free(err); g_object_unref(instream); /* let's try reopening */ err = NULL; instream = G_OBJECT(g_file_read(f, NULL, &err)); if (!instream) { debug_error("config_open(): reopen failed %s\n", err->message); g_error_free(err); g_object_unref(f); return NULL; } } conv = g_charset_converter_new("UTF-8", wanted_enc, &err); if (!conv) { debug_error("config_open(): failed to setup recoding from %s: %s\n", wanted_enc, err ? err->message : "(unknown error)"); g_error_free(err); stream = instream; /* fallback to utf8 */ } else { g_charset_converter_set_use_fallback(conv, TRUE); stream = G_OBJECT(g_converter_input_stream_new( G_INPUT_STREAM(instream), G_CONVERTER(conv))); } stream = G_OBJECT(g_data_input_stream_new(G_INPUT_STREAM(stream))); } break; case 'w': stream = G_OBJECT(g_data_output_stream_new(G_OUTPUT_STREAM(instream))); /* we're always writing config in utf8 */ if (!ekg_fprintf(G_OUTPUT_STREAM(stream), "%s%s\n", modeline_prefix, "UTF-8")) { g_object_unref(stream); g_object_unref(f); return NULL; } break; default: g_assert_not_reached(); } g_object_unref(f); return stream; } static const char *prepare_old_path(const char *filename) { static char path[PATH_MAX]; extern char *old_config_dir; if (!filename || !*filename) snprintf(path, sizeof(path), "%s", old_config_dir); else snprintf(path, sizeof(path), "%s/%s", old_config_dir, filename); return path; } /** * config_open() * * Open a configuration file, formatting the name if necessary. Choose * correct location and file encoding. * * @param path_format - format string for file name. Should be * utf8-encoded. * @param mode - string mode for opening the file (r or w). * * @return Open GIOChannel or NULL if open failed. The GIOChannel * instance must be unrefed using g_object_unref(). */ GObject *config_open(const gchar *path_format, const gchar *mode, ...) { va_list args; gchar *basename, *cdir, *path, *p; GString *fname; GObject *f; gboolean nonalnum = FALSE; va_start(args, mode); basename = g_strdup_vprintf(path_format, args); va_end(args); fname = g_string_new(basename); for (p = fname->str; *p; p++) { /* filter out the name */ if (!g_ascii_isalnum(*p) && *p != '.' && *p != '_' && *p != '-') { nonalnum = TRUE; *p = '_'; } } if (nonalnum) { gchar *cksum = g_compute_checksum_for_string(G_CHECKSUM_MD5, basename, -1); g_string_append_c(fname, '_'); g_string_append_len(fname, cksum, 8); g_free(cksum); } cdir = g_build_filename(g_get_user_config_dir(), "ekg2", config_profile, NULL); path = g_build_filename(cdir, g_string_free(fname, FALSE), NULL); debug_function("config_open(), path=%s\n", path); f = config_open_real(path, mode); if (G_UNLIKELY(mode[0] == 'w' && !f)) { if (G_LIKELY(!g_mkdir_with_parents(cdir, 0700))) f = config_open_real(path, mode); } g_free(path); g_free(cdir); if (G_UNLIKELY(!f && mode[0] == 'r')) /* fallback to old config */ f = config_open_real(prepare_old_path(basename), mode); g_free(basename); if (mode[0] == 'w') { if (!config_cancellable) { config_cancellable = g_cancellable_new(); g_assert(!config_openfiles); config_openfiles = g_ptr_array_new(); } if (f) g_ptr_array_add(config_openfiles, f); else g_cancellable_cancel(config_cancellable); } return f; } /** * config_commit() * * Close all configuration files open for writing, and commit changes * to them if written successfully. Otherwise, just leave old files * intact. * * @return TRUE if new config was saved, FALSE otherwise. */ gboolean config_commit(void) { gpointer *p; gboolean ret = TRUE; g_assert(config_cancellable); for (p = &config_openfiles->pdata[0]; p < &config_openfiles->pdata[config_openfiles->len]; p++) { ret &= g_output_stream_close(G_OUTPUT_STREAM(*p), config_cancellable, NULL); g_object_unref(*p); } ret &= !g_cancellable_is_cancelled(config_cancellable); g_ptr_array_free(config_openfiles, FALSE); g_object_unref(config_cancellable); config_openfiles = NULL; config_cancellable = NULL; return ret; } int config_read_plugins() { gchar *buf, *foo; GDataInputStream *f; f = G_DATA_INPUT_STREAM(config_open("plugins", "r")); if (!f) return -1; while ((buf = read_line(f))) { if (!(foo = xstrchr(buf, (' ')))) continue; *foo++ = 0; if (!xstrcasecmp(buf, ("plugin"))) { char **p = array_make(foo, (" \t"), 3, 1, 0); if (g_strv_length(p) == 2) plugin_load(p[0], atoi(p[1]), 1); g_strfreev(p); } } g_object_unref(f); return 0; } /* * config_read() * * czyta z pliku ~/.ekg2/config lub podanego konfiguracj. * * - filename, * * 0/-1 */ int config_read(const gchar *plugin_name) { gchar *buf, *foo; GDataInputStream *f; int err_count = 0, ret; if (!in_autoexec && !plugin_name) { aliases_destroy(); timer_remove_user(timer_handle_command); timer_remove_user(timer_handle_at); event_free(); variable_set_default(); query_emit(NULL, "set-vars-default"); query_emit(NULL, "binding-default"); debug(" flushed previous config\n"); } /* then global and plugins variables */ if (plugin_name) f = G_DATA_INPUT_STREAM(config_open("config-%s", "r", plugin_name)); else f = G_DATA_INPUT_STREAM(config_open("config", "r")); if (!f) return -1; while ((buf = read_line(f))) { ret = 0; if (buf[0] == '#' || buf[0] == ';' || (buf[0] == '/' && buf[1] == '/')) continue; if (!(foo = xstrchr(buf, ' '))) continue; *foo++ = 0; if (!xstrcasecmp(buf, ("set"))) { char *bar; if (!(bar = xstrchr(foo, ' '))) ret = variable_set(foo, NULL) < 0; else { *bar++ = 0; ret = variable_set(foo, bar) < 0; } if (ret) debug_error(" unknown variable %s\n", foo); } else if (!xstrcasecmp(buf, ("plugin"))) { char **p = array_make(foo, (" \t"), 3, 1, 0); if (g_strv_length(p) == 2) plugin_load(p[0], atoi(p[1]), 1); g_strfreev(p); } else if (!xstrcasecmp(buf, ("bind"))) { char **pms = array_make(foo, (" \t"), 2, 1, 0); if (g_strv_length(pms) == 2) { ret = command_exec_format(NULL, NULL, 1, ("/bind --add %s %s"), pms[0], pms[1]); } g_strfreev(pms); } else if (!xstrcasecmp(buf, ("bind-set"))) { char **pms = array_make(foo, (" \t"), 2, 1, 0); if (g_strv_length(pms) == 2) { query_emit(NULL, "binding-set", pms[0], pms[1], 1); } g_strfreev(pms); } else if (!xstrcasecmp(buf, ("alias"))) { debug(" alias %s\n", foo); ret = alias_add(foo, 1, 1); } else if (!xstrcasecmp(buf, ("on"))) { char **pms = array_make(foo, (" \t"), 4, 1, 0); if (g_strv_length(pms) == 4) { debug(" on %s %s %s\n", pms[0], pms[1], pms[2]); ret = event_add(pms[0], atoi(pms[1]), pms[2], pms[3], 1); } g_strfreev(pms); } else if (!xstrcasecmp(buf, ("bind"))) { continue; } else if (!xstrcasecmp(buf, ("at"))) { char **p = array_make(foo, (" \t"), 2, 1, 0); if (g_strv_length(p) == 2) { char *name = NULL; debug(" at %s %s\n", p[0], p[1]); if (xstrcmp(p[0], ("(null)"))) name = p[0]; ret = command_exec_format(NULL, NULL, 1, ("/at -a %s %s"), ((name) ? name : ("")), p[1]); } g_strfreev(p); } else if (!xstrcasecmp(buf, ("timer"))) { char **p = array_make(foo, (" \t"), 3, 1, 0); char *period_str = NULL; char *name = NULL; time_t period; if (g_strv_length(p) == 3) { debug(" timer %s %s %s\n", p[0], p[1], p[2]); if (xstrcmp(p[0], ("(null)"))) name = p[0]; if (!xstrncmp(p[1], ("*/"), 2)) { period = atoi(p[1] + 2); period_str = saprintf("*/%ld", (long) period); } else { period = atoi(p[1]) - time(NULL); period_str = saprintf("%ld", (long) period); } if (period > 0) { ret = command_exec_format(NULL, NULL, 1, ("/timer --add %s %s %s"), (name) ? name : "", period_str, p[2]); } xfree(period_str); } g_strfreev(p); } else { ret = variable_set(buf, (xstrcmp(foo, (""))) ? foo : NULL) < 0; if (ret) debug_error(" unknown variable %s\n", buf); } if (ret && (err_count++ > 100)) break; } g_object_unref(f); if (!plugin_name) { GSList *pl; for (pl = plugins; pl; pl = pl->next) { const plugin_t *p = pl->data; config_read(p->name); } } return 0; } /* * config_write_variable() * * zapisuje jedn zmienn do pliku konfiguracyjnego. * * - f - otwarty plik konfiguracji, * - v - wpis zmiennej, */ static void config_write_variable(GOutputStream *f, variable_t *v) { if (!f || !v) return; switch (v->type) { case VAR_DIR: case VAR_THEME: case VAR_FILE: case VAR_STR: ekg_fprintf(f, "%s %s\n", v->name, (*(char**)(v->ptr)) ? *(char**)(v->ptr) : ""); break; default: ekg_fprintf(f, "%s %d\n", v->name, *(int*)(v->ptr)); } } /* * config_write_plugins() * * function saving plugins * * - f - file, that we are saving to */ static void config_write_plugins(GOutputStream *f) { GSList *pl; if (!f) return; for (pl = plugins; pl; pl = pl->next) { const plugin_t *p = pl->data; if (p->name) ekg_fprintf(f, "plugin %s %d\n", p->name, p->prio); } } /* * config_write_main() * * waciwa funkcja zapisujca konfiguracj do podanego pliku. * * - f - plik, do ktrego piszemy */ static void config_write_main(GOutputStream *f) { if (!f) return; { GSList *vl; for (vl = variables; vl; vl = vl->next) { variable_t *v = vl->data; if (!v->plugin) config_write_variable(f, v); } } { alias_t *a; for (a = aliases; a; a = a->next) { list_t m; for (m = a->commands; m; m = m->next) ekg_fprintf(f, "alias %s %s\n", a->name, (char *) m->data); } } { event_t *e; for (e = events; e; e = e->next) { ekg_fprintf(f, "on %s %d %s %s\n", e->name, e->prio, e->target, e->action); } } { struct binding *b; for (b = bindings; b; b = b->next) { if (b->internal) continue; ekg_fprintf(f, "bind %s %s\n", b->key, b->action); } } { binding_added_t *d; for (d = bindings_added; d; d = d->next) { ekg_fprintf(f, "bind-set %s %s\n", d->binding->key, d->sequence); } } timers_write(f); } /* * config_write() * * zapisuje aktualn konfiguracj do pliku ~/.ekg2/config lub podanego. */ void config_write() { GOutputStream *f; GSList *pl; /* first of all we are saving plugins */ if (!(f = G_OUTPUT_STREAM(config_open("plugins", "w")))) return; config_write_plugins(f); /* now we are saving global variables and settings * timers, bindings etc. */ if (!(f = G_OUTPUT_STREAM(config_open("config", "w")))) return; config_write_main(f); /* now plugins variables */ for (pl = plugins; pl; pl = pl->next) { const plugin_t *p = pl->data; GSList *vl; if (!(f = G_OUTPUT_STREAM(config_open("config-%s", "w", p->name)))) return; for (vl = variables; vl; vl = vl->next) { variable_t *v = vl->data; if (p == v->plugin) { config_write_variable(f, v); } } } } /* * config_write_partly() * * zapisuje podane zmienne, nie zmieniajc reszty konfiguracji. * * - plugin - zmienne w vars, maja byc z tego pluginu, lub NULL gdy to sa zmienne z core. * - vars - tablica z nazwami zmiennych do zapisania. */ /* BIG BUGNOTE: * Ta funkcja jest zle zportowana z ekg1, zle napisana, wolna, etc.. * Powinnismy robic tak: * - dla kazdej zmiennej w vars[] znalezc variable_t * jej odpowiadajace i do tablicy vars_ptr[] * - dla kazdej zmiennej w vars[] policzyc dlugosc i do vars_len[] * - nastepnie otworzyc "config-%s", vars_ptr[0]->plugin->name (lub "config" gdy nie plugin) * - zrobic to co tutaj robimy, czyli poszukac tej zmiennej.. oraz nastepnie wszystkie inne ktore maja taki * sam vars_ptr[]->plugin jak vars_ptr[0]->plugin, powtarzac dopoki sie skoncza takie. * - nastepnie wziasc zmienna ktora ma inny plugin.. i j/w */ int config_write_partly(plugin_t *plugin, const char **vars) { char *line; GDataInputStream *fi; GOutputStream *fo; int *wrote, i; if (!vars) return -1; if (plugin) fi = G_DATA_INPUT_STREAM(config_open("config-%s", "r", plugin->name)); else fi = G_DATA_INPUT_STREAM(config_open("config", "r")); if (!fi) return -1; /* config_open() writes through temporary file, * so it's sane to open the same name twice */ if (plugin) fo = G_OUTPUT_STREAM(config_open("config-%s", "w", plugin->name)); else fo = G_OUTPUT_STREAM(config_open("config", "w")); if (!fo) { g_object_unref(fi); return -1; } wrote = xcalloc(g_strv_length((char **) vars) + 1, sizeof(int)); while ((line = read_line(fi))) { char *tmp; if (line[0] == '#' || line[0] == ';' || (line[0] == '/' && line[1] == '/')) goto pass; if (!xstrchr(line, ' ')) goto pass; if (!xstrncasecmp(line, ("alias "), 6)) goto pass; if (!xstrncasecmp(line, ("on "), 3)) goto pass; if (!xstrncasecmp(line, ("bind "), 5)) goto pass; tmp = line; if (!xstrncasecmp(tmp, ("set "), 4)) tmp += 4; for (i = 0; vars[i]; i++) { int len; if (wrote[i]) continue; len = xstrlen(vars[i]); if (xstrlen(tmp) < len + 1) continue; if (xstrncasecmp(tmp, vars[i], len) || tmp[len] != ' ') continue; config_write_variable(fo, variable_find(vars[i])); wrote[i] = 1; line = NULL; break; } pass: if (line) ekg_fprintf(fo, "%s\n", line); } for (i = 0; vars[i]; i++) { if (wrote[i]) continue; config_write_variable(fo, variable_find(vars[i])); } xfree(wrote); g_object_unref(fi); return 0; } /* * debug_write_crash() * * zapisuje ostatnie linie z debug. */ void debug_write_crash() { GOutputStream *f; struct buffer *b; g_chdir(config_dir); if (!(f = G_OUTPUT_STREAM(config_open("crash-%d-debug", "w", (int) getpid())))) return; for (b = buffer_debug.data; b; b = b->next) ekg_fprintf(f, "%s\n", b->line); } /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/ekg/configfile.h000066400000000000000000000031551175142753400172050ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2001-2003 Wojtek Kaniewski * Robert J. Wony * Pawe Maziarz * Dawid Jarosz * Piotr Domagalski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __EKG_CONFIGFILE_H #define __EKG_CONFIGFILE_H #ifndef EKG2_WIN32_NOFUNCTION #include "plugins.h" #ifdef __cplusplus extern "C" { #endif void config_postread(); gboolean ekg_fprintf(GOutputStream *f, const gchar *format, ...) G_GNUC_PRINTF(2, 3); GObject *config_open(const gchar *path_format, const gchar *mode, ...) G_GNUC_PRINTF(1, 3); gboolean config_commit(void); int config_read(const gchar *plugin_name); int config_read_plugins(); void config_write(); int config_write_partly(plugin_t *plugin, const char **vars); void debug_write_crash(); #ifdef __cplusplus } #endif #endif #endif /* __EKG_CONFIGFILE_H */ /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/ekg/connections.c000066400000000000000000000517221175142753400174200ustar00rootroot00000000000000/* * Asynchronous read/write handling for connections * * (C) Copyright 2011 EKG2 team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include "ekg/internal.h" #ifdef HAVE_LIBGNUTLS # include /* EAGAIN for transport wrappers */ # include # define NEED_SLAVERY 1 #endif #define EKG_CONNECTION_ERROR ekg_connection_error_quark() GQuark ekg_connection_error_quark() { return g_quark_from_static_string("ekg-connection-error-quark"); } struct ekg_connection; typedef void (*ekg_flush_handler_t) (struct ekg_connection *conn); struct ekg_connection { GSocketConnection *conn; GDataInputStream *instream; GDataOutputStream *outstream; GCancellable *cancellable; gpointer priv_data; ekg_input_callback_t callback; ekg_failure_callback_t failure_callback; ekg_flush_handler_t flush_handler; GString *wr_buffer; #if NEED_SLAVERY struct ekg_connection *master; struct ekg_connection *slave; #endif }; static GSList *connections = NULL; static void setup_async_read(struct ekg_connection *c); static gboolean setup_async_connect(GSocketClient *sock, struct ekg_connection_starter *cs); #ifdef HAVE_LIBGNUTLS static void ekg_gnutls_new_session( GSocketClient *sockclient, GSocketConnection *sock, struct ekg_connection_starter *cs); #define EKG_GNUTLS_ERROR ekg_gnutls_error_quark() static G_GNUC_CONST GQuark ekg_gnutls_error_quark() { return g_quark_from_static_string("ekg-gnutls-error-quark"); } #endif static void ekg_connection_remove(struct ekg_connection *c) { if (g_input_stream_has_pending(G_INPUT_STREAM(c->instream))) { debug_warn("ekg_connection_remove(%x) input stream has pending!\n", c); g_input_stream_clear_pending(G_INPUT_STREAM(c->instream)); } #if 0 /* XXX */ g_assert(!g_output_stream_has_pending( G_OUTPUT_STREAM(c->outstream))); #endif connections = g_slist_remove(connections, c); g_string_free(c->wr_buffer, TRUE); g_object_unref(c->cancellable); g_object_unref(c->instream); g_object_unref(c->outstream); g_slice_free(struct ekg_connection, c); } static struct ekg_connection *get_connection_by_outstream(GDataOutputStream *s) { GSList *el; gint conn_find_outstream(gconstpointer list_elem, gconstpointer comp_elem) { const struct ekg_connection *c = list_elem; return c->outstream == comp_elem ? 0 : -1; } el = g_slist_find_custom(connections, s, conn_find_outstream); return el ? el->data : NULL; } #if NEED_SLAVERY static struct ekg_connection *get_slave_connection_by_conn(GSocketConnection *c) { GSList *el; gint conn_find_slaveless_conn(gconstpointer list_elem, gconstpointer comp_elem) { const struct ekg_connection *c = list_elem; return (!c->slave && c->conn == comp_elem) ? 0 : -1; } el = g_slist_find_custom(connections, c, conn_find_slaveless_conn); return el ? el->data : NULL; } #endif static void done_async_read(GObject *obj, GAsyncResult *res, gpointer user_data) { struct ekg_connection *c = user_data; GError *err = NULL; gssize rsize; GBufferedInputStream *instr = G_BUFFERED_INPUT_STREAM(obj); rsize = g_buffered_input_stream_fill_finish(instr, res, &err); if (rsize <= 0) { if (rsize == -1) /* error */ debug_error("done_async_read(), read failed: %s\n", err ? err->message : NULL); else { /* EOF */ #if NEED_SLAVERY if (c->master) /* let the master handle it */ return; #endif debug_function("done_async_read(), EOF\n"); if (g_buffered_input_stream_get_available(instr) > 0) c->callback(c->instream, c->priv_data); err = g_error_new_literal( EKG_CONNECTION_ERROR, EKG_CONNECTION_ERROR_EOF, "Connection terminated"); } c->failure_callback(c->instream, err, c->priv_data); ekg_connection_remove(c); g_error_free(err); return; } debug_function("done_async_read(): read %d bytes\n", rsize); c->callback(c->instream, c->priv_data); setup_async_read(c); } static void setup_async_read(struct ekg_connection *c) { g_buffered_input_stream_fill_async( G_BUFFERED_INPUT_STREAM(c->instream), -1, /* fill the buffer */ G_PRIORITY_DEFAULT, c->cancellable, done_async_read, c); } static void failed_write(struct ekg_connection *c) { /* XXX? */ } static void done_async_write(GObject *obj, GAsyncResult *res, gpointer user_data) { struct ekg_connection *c = user_data; GError *err = NULL; gboolean ret; GOutputStream *of = G_OUTPUT_STREAM(obj); ret = g_output_stream_flush_finish(of, res, &err); if (!ret) { debug_error("done_async_write(), write failed: %s\n", err ? err->message : NULL); /* XXX */ failed_write(c); g_error_free(err); return; } if (c->wr_buffer->len > 0) { /* the stream should not have any pending writes ATM * we need to ensure that to have the data written to stream * rather than re-appended to the buffer*/ g_assert(!g_output_stream_has_pending(of)); ekg_connection_write_buf(G_DATA_OUTPUT_STREAM(of), c->wr_buffer->str, c->wr_buffer->len); g_string_truncate(c->wr_buffer, 0); } /* XXX: anything to do? */ } static void setup_async_write(struct ekg_connection *c) { g_output_stream_flush_async( G_OUTPUT_STREAM(c->outstream), G_PRIORITY_DEFAULT, c->cancellable, /* XXX */ done_async_write, c); } GDataOutputStream *ekg_connection_add( GSocketConnection *conn, GInputStream *raw_instream, GOutputStream *raw_outstream, ekg_input_callback_t callback, ekg_failure_callback_t failure_callback, gpointer priv_data) { struct ekg_connection *c = g_slice_new(struct ekg_connection); GOutputStream *bout = g_buffered_output_stream_new(raw_outstream); c->conn = conn; c->instream = g_data_input_stream_new(raw_instream); c->outstream = g_data_output_stream_new(bout); c->cancellable = g_cancellable_new(); c->wr_buffer = g_string_new(""); c->callback = callback; c->failure_callback = failure_callback; c->priv_data = priv_data; #if NEED_SLAVERY c->master = get_slave_connection_by_conn(conn); c->slave = NULL; /* be a good slave.. er, servant */ if (G_UNLIKELY(c->master)) { struct ekg_connection *ci; c->master->slave = c; /* shift flush handlers (if set) * this is required in order to be able to easily set flush * handlers for future slaves */ for (ci = c; ci->master && (ci->master->flush_handler != setup_async_write); ci = ci->master) ci->flush_handler = ci->master->flush_handler; ci->flush_handler = setup_async_write; } else #endif c->flush_handler = setup_async_write; /* LF works fine for CRLF */ g_data_input_stream_set_newline_type(c->instream, G_DATA_STREAM_NEWLINE_TYPE_LF); /* disallow any blocking writes */ g_buffered_output_stream_set_auto_grow(G_BUFFERED_OUTPUT_STREAM(bout), TRUE); connections = g_slist_prepend(connections, c); #if NEED_SLAVERY if (G_LIKELY(!c->master)) #endif setup_async_read(c); return c->outstream; } void ekg_disconnect_by_outstream(GDataOutputStream *f) { struct ekg_connection *c = get_connection_by_outstream(f); if (!c) { debug_warn("ekg_disconnect_by_outstream() - connection not found\n"); return; } debug_function("ekg_disconnect_by_outstream(%x)\n",c); ekg_connection_remove(c); } void ekg_connection_write_buf(GDataOutputStream *f, gconstpointer buf, gsize len) { struct ekg_connection *c = get_connection_by_outstream(f); GError *err = NULL; gssize out; GOutputStream *of = G_OUTPUT_STREAM(f); /* we can't write to buffer if it has pending ops; * yes, it is stupid. */ if (g_output_stream_has_pending(of)) { c->wr_buffer = g_string_append_len(c->wr_buffer, buf, len); return; } out = g_output_stream_write(of, buf, len, NULL, &err); if (out != len) { debug_error("ekg_connection_write_string() failed (wrote %d out of %d): %s\n", out, len, err ? err->message : "(no error?!)"); failed_write(c); g_error_free(err); return; } debug_function("ekg_connection_write_buf(), wrote %d bytes\n", out); c->flush_handler(c); } void ekg_connection_write(GDataOutputStream *f, const gchar *format, ...) { static GString *buf = NULL; va_list args; if (G_LIKELY(format)) { if (!buf) buf = g_string_sized_new(120); va_start(args, format); g_string_vprintf(buf, format, args); va_end(args); ekg_connection_write_buf(f, buf->str, buf->len); } else { struct ekg_connection *c = get_connection_by_outstream(f); c->flush_handler(c); } } struct ekg_connection_starter { GCancellable *cancellable; gchar *bind_hostname; gchar *service; gchar *domain; gchar **servers; gchar **current_server; guint16 defport; gboolean use_tls; ekg_connection_callback_t callback; ekg_connection_failure_callback_t failure_callback; gpointer priv_data; }; static void failed_async_connect( GSocketClient *sock, GError *err, struct ekg_connection_starter *cs) { debug_error("done_async_connect(), connect failed: %s\n", err ? err->message : "(reason unknown)"); if (g_error_matches(err, G_IO_ERROR, G_IO_ERROR_CANCELLED) || !setup_async_connect(sock, cs)) { cs->failure_callback(err, cs->priv_data); ekg_connection_starter_free(cs); g_object_unref(sock); } } static void succeeded_async_connect( GSocketClient *sock, GSocketConnection *conn, struct ekg_connection_starter *cs, GInputStream *instream, GOutputStream *outstream) { cs->callback(conn, instream, outstream, cs->priv_data); ekg_connection_starter_free(cs); g_object_unref(sock); } static void done_async_connect(GObject *obj, GAsyncResult *res, gpointer user_data) { GSocketClient *sock = G_SOCKET_CLIENT(obj); struct ekg_connection_starter *cs = user_data; GSocketConnection *conn; GError *err = NULL; conn = g_socket_client_connect_finish(sock, res, &err); if (conn) { #ifdef HAVE_LIBGNUTLS if (cs->use_tls) { ekg_gnutls_new_session(sock, conn, cs); } else #endif { GIOStream *cio = G_IO_STREAM(conn); succeeded_async_connect( sock, conn, cs, g_io_stream_get_input_stream(cio), g_io_stream_get_output_stream(cio)); } } else { failed_async_connect(sock, err, cs); g_error_free(err); } } static gboolean setup_async_connect(GSocketClient *sock, struct ekg_connection_starter *cs) { if (*(cs->current_server)) { debug_function("setup_async_connect(), trying %s (defport: %d)\n", *(cs->current_server), cs->defport); g_socket_client_connect_to_host_async( sock, *(cs->current_server), cs->defport, cs->cancellable, done_async_connect, cs); cs->current_server++; return TRUE; } else return FALSE; } ekg_connection_starter_t ekg_connection_starter_new(guint16 defport) { struct ekg_connection_starter *cs = g_slice_new0(struct ekg_connection_starter); cs->defport = defport; return cs; } void ekg_connection_starter_free(ekg_connection_starter_t cs) { g_free(cs->bind_hostname); g_free(cs->service); g_free(cs->domain); g_strfreev(cs->servers); g_object_unref(cs->cancellable); g_slice_free(struct ekg_connection_starter, cs); } void ekg_connection_starter_bind( ekg_connection_starter_t cs, const gchar *hostname) { g_free(cs->bind_hostname); cs->bind_hostname = g_strdup(hostname); } void ekg_connection_starter_set_srv_resolver( ekg_connection_starter_t cs, const gchar *service, const gchar *domain) { g_free(cs->service); g_free(cs->domain); cs->service = g_strdup(service); cs->domain = g_strdup(domain); } void ekg_connection_starter_set_servers( ekg_connection_starter_t cs, const gchar *servers) { g_strfreev(cs->servers); cs->servers = g_strsplit(servers, ",", 0); } void ekg_connection_starter_set_use_tls( ekg_connection_starter_t cs, gboolean use_tls) /* XXX */ { cs->use_tls = use_tls; } GCancellable *ekg_connection_starter_run( ekg_connection_starter_t cs, GSocketClient *sock, ekg_connection_callback_t callback, ekg_connection_failure_callback_t failure_callback, gpointer priv_data) { cs->callback = callback; cs->failure_callback = failure_callback; cs->priv_data = priv_data; cs->cancellable = g_cancellable_new(); cs->current_server = cs->servers; if (cs->bind_hostname) { GResolver *res = g_resolver_get_default(); GList *addrs; GError *err = NULL; addrs = g_resolver_lookup_by_name( res, cs->bind_hostname, NULL, &err); if (!addrs) { /* XXX: delay calling that */ failed_async_connect(sock, err, cs); g_error_free(err); return cs->cancellable; } g_socket_client_set_local_address(sock, g_inet_socket_address_new( G_INET_ADDRESS(g_list_nth_data(addrs, 0)), 0)); g_resolver_free_addresses(addrs); g_object_unref(res); } /* if we have the domain name, try SRV lookup first */ if (cs->domain) { g_assert(cs->service); /* fallback to domainname lookup if 'servers' not set */ if (!cs->servers || !cs->servers[0]) ekg_connection_starter_set_servers(cs, cs->domain); debug_function("ekg_connection_start(), trying _%s._tcp.%s\n", cs->service, cs->domain); g_socket_client_connect_to_service_async( sock, cs->domain, cs->service, cs->cancellable, done_async_connect, cs); } else /* otherwise, just begin with servers */ g_assert(setup_async_connect(sock, cs)); return cs->cancellable; } #ifdef HAVE_LIBGNUTLS struct ekg_gnutls_connection { struct ekg_connection *connection; GMemoryInputStream *instream; GMemoryOutputStream *outstream; GError *connection_error; gnutls_session_t session; gnutls_certificate_credentials_t cred; }; struct ekg_gnutls_connection_starter { struct ekg_connection_starter *parent; struct ekg_gnutls_connection *conn; GSocketClient *sockclient; }; static void ekg_gnutls_free_connection(struct ekg_gnutls_connection *conn) { gnutls_deinit(conn->session); gnutls_certificate_free_credentials(conn->cred); g_slice_free(struct ekg_gnutls_connection, conn); } static void ekg_gnutls_free_connection_starter(struct ekg_gnutls_connection_starter *gcs) { g_slice_free(struct ekg_gnutls_connection_starter, gcs); } static gssize ekg_gnutls_pull(gnutls_transport_ptr_t connptr, gpointer buf, gsize len) { struct ekg_gnutls_connection *conn = connptr; GBufferedInputStream *s = G_BUFFERED_INPUT_STREAM(conn->connection->instream); gsize avail_bytes = g_buffered_input_stream_get_available(s); /* XXX: EOF? */ g_assert(len > 0); if (avail_bytes == 0) { if (conn->connection_error) return 0; /* EOF */ gnutls_transport_set_errno(conn->session, EAGAIN); return -1; } else { GError *err = NULL; gssize ret = g_input_stream_read( G_INPUT_STREAM(s), buf, MIN(avail_bytes, len), NULL, &err); if (ret == -1) { debug_error("ekg_gnutls_pull() failed: %s\n", err->message); g_error_free(err); } return ret; } g_assert_not_reached(); } static gssize ekg_gnutls_push(gnutls_transport_ptr_t connptr, gconstpointer buf, gsize len) { struct ekg_gnutls_connection *conn = connptr; g_assert(len > 0); /* XXX: handle failures better? */ ekg_connection_write_buf(conn->connection->outstream, buf, len); return len; } static void ekg_gnutls_handle_data_failure(GDataInputStream *s, GError *err, gpointer data) { struct ekg_gnutls_connection *gc = data; gc->connection_error = g_error_copy(err); } static void ekg_gnutls_handle_data(GDataInputStream *s, gpointer data) { struct ekg_gnutls_connection *gc = data; ssize_t ret; char buf[4096]; g_assert(gc->connection->slave); do { ret = gnutls_record_recv(gc->session, buf, sizeof(buf)); if (ret > 0) g_memory_input_stream_add_data( gc->instream, g_memdup(buf, ret), ret, g_free); else if (ret != GNUTLS_E_INTERRUPTED && ret != GNUTLS_E_AGAIN) { GError *err; if (ret != 0) err = g_error_new_literal(EKG_GNUTLS_ERROR, ret, gnutls_strerror(ret)); else { debug_function("ekg_gnutls_handle_data(), got EOF from gnutls\n"); err = g_error_new_literal( EKG_CONNECTION_ERROR, EKG_CONNECTION_ERROR_EOF, "Connection terminated"); } gc->connection->slave->failure_callback( gc->connection->slave->instream, err, gc->connection->slave->priv_data); g_error_free(err); ekg_connection_remove(gc->connection->slave); ekg_connection_remove(gc->connection); return; } } while (ret > 0 || ret == GNUTLS_E_INTERRUPTED); /* not necessarily async but be lazy */ if (!g_input_stream_has_pending(G_INPUT_STREAM(gc->connection->slave->instream))) setup_async_read(gc->connection->slave); } static void ekg_gnutls_flush(struct ekg_connection *c) { struct ekg_gnutls_connection *gc = c->master->priv_data; GMemoryOutputStream *dos = gc->outstream; ssize_t ret; gconstpointer bufp; gsize bufleft; /* GMemoryOutputStream is not supposed to fail */ g_assert(g_output_stream_flush( G_OUTPUT_STREAM(c->outstream), NULL, NULL)); /* now pass the written data to gnutls */ bufp = g_memory_output_stream_get_data(dos); bufleft = g_memory_output_stream_get_data_size(dos); do { ret = gnutls_record_send(gc->session, bufp, bufleft); if (ret > 0) { g_assert(ret <= bufleft); bufp += ret; bufleft -= ret; } else if (ret != GNUTLS_E_INTERRUPTED && ret != GNUTLS_E_AGAIN) { debug_error("gnutls_flush(), write failed: %s\n", gnutls_strerror(ret)); /* XXX */ failed_write(c); } } while (bufleft > 0); { GSeekable *sos = G_SEEKABLE(dos); g_assert(g_seekable_seek(sos, 0, G_SEEK_SET, NULL, NULL)); g_assert(g_seekable_truncate(sos, 0, NULL, NULL)); } } static void ekg_gnutls_handle_handshake_failure(GDataInputStream *s, GError *err, gpointer data) { struct ekg_gnutls_connection_starter *gcs = data; failed_async_connect(gcs->sockclient, err, gcs->parent); ekg_connection_remove(gcs->conn->connection); ekg_gnutls_free_connection(gcs->conn); ekg_gnutls_free_connection_starter(gcs); } static void ekg_gnutls_async_handshake(struct ekg_gnutls_connection_starter *gcs) { gint ret = gnutls_handshake(gcs->conn->session); switch (ret) { case GNUTLS_E_SUCCESS: { struct ekg_gnutls_connection *gc = gcs->conn; struct ekg_connection_starter *cs = gcs->parent; GInputStream *mi = g_memory_input_stream_new(); GOutputStream *mo = g_memory_output_stream_new( NULL, 0, g_realloc, g_free); /* set streams */ gc->instream = G_MEMORY_INPUT_STREAM(mi); gc->outstream = G_MEMORY_OUTPUT_STREAM(mo); /* switch handlers */ gc->connection->callback = ekg_gnutls_handle_data; gc->connection->failure_callback = ekg_gnutls_handle_data_failure; gc->connection->priv_data = gc; gc->connection->flush_handler = ekg_gnutls_flush; /* this cleans up the socket, and cs */ succeeded_async_connect(gcs->sockclient, gc->connection->conn, cs, mi, mo); /* and this cleans up gcs */ ekg_gnutls_free_connection_starter(gcs); } break; case GNUTLS_E_AGAIN: case GNUTLS_E_INTERRUPTED: break; default: { GError *err = g_error_new_literal(EKG_GNUTLS_ERROR, ret, gnutls_strerror(ret)); ekg_gnutls_handle_handshake_failure(NULL, err, gcs); g_error_free(err); } } } static void ekg_gnutls_handle_handshake_input(GDataInputStream *s, gpointer data) { struct ekg_gnutls_connection_starter *gcs = data; ekg_gnutls_async_handshake(gcs); } static void ekg_gnutls_new_session( GSocketClient *sockclient, GSocketConnection *sock, struct ekg_connection_starter *cs) { gnutls_session_t s; gnutls_certificate_credentials_t cred; struct ekg_gnutls_connection *conn = g_slice_new(struct ekg_gnutls_connection); struct ekg_gnutls_connection_starter *gcs = g_slice_new(struct ekg_gnutls_connection_starter); g_assert(!gnutls_certificate_allocate_credentials(&cred)); g_assert(!gnutls_init(&s, GNUTLS_CLIENT)); g_assert(!gnutls_priority_set_direct(s, "PERFORMANCE", NULL)); /* XXX */ g_assert(!gnutls_credentials_set(s, GNUTLS_CRD_CERTIFICATE, cred)); gnutls_transport_set_pull_function(s, ekg_gnutls_pull); gnutls_transport_set_push_function(s, ekg_gnutls_push); gnutls_transport_set_ptr(s, conn); gcs->parent = cs; gcs->conn = conn; gcs->sockclient = sockclient; conn->session = s; conn->cred = cred; conn->connection_error = NULL; conn->connection = get_connection_by_outstream( ekg_connection_add( sock, g_io_stream_get_input_stream(G_IO_STREAM(sock)), g_io_stream_get_output_stream(G_IO_STREAM(sock)), ekg_gnutls_handle_handshake_input, ekg_gnutls_handle_handshake_failure, gcs) ); g_assert(conn->connection); ekg_gnutls_async_handshake(gcs); } static void ekg_gnutls_log(gint level, const char *msg) { debug_ext(DEBUG_GGMISC, "[gnutls:%d] %s", level, msg); } #endif void ekg_tls_init(void) { #ifdef HAVE_LIBGNUTLS g_assert(!gnutls_global_init()); /* XXX: error handling */ gnutls_global_set_log_function(ekg_gnutls_log); gnutls_global_set_log_level(3); #endif } void ekg_tls_deinit(void) { #ifdef HAVE_LIBGNUTLS gnutls_global_deinit(); #endif } ekg2-0.4~pre+20120506.1/ekg/connections.h000066400000000000000000000052351175142753400174230ustar00rootroot00000000000000/* * Asynchronous read/write handling for connections * * (C) Copyright 2011 EKG2 team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __EKG_CONNECTIONS_H #define __EKG_CONNECTIONS_H #define EKG_CONNECTION_ERROR ekg_connection_error_quark() G_GNUC_CONST GQuark ekg_connection_error_quark(); enum ekg_connection_error { EKG_CONNECTION_ERROR_EOF }; typedef void (*ekg_input_callback_t) ( GDataInputStream *instream, gpointer data); typedef void (*ekg_failure_callback_t) ( GDataInputStream *instream, GError *err, gpointer data); typedef void (*ekg_connection_callback_t) ( GSocketConnection *conn, GInputStream *instream, GOutputStream *outstream, gpointer data); typedef void (*ekg_connection_failure_callback_t) ( GError *err, gpointer data); GDataOutputStream *ekg_connection_add( GSocketConnection *conn, GInputStream *rawinstream, GOutputStream *rawoutstream, ekg_input_callback_t callback, ekg_failure_callback_t failure_callback, gpointer priv_data); void ekg_disconnect_by_outstream(GDataOutputStream *f); void ekg_connection_write_buf(GDataOutputStream *f, gconstpointer buf, gsize len); void ekg_connection_write( GDataOutputStream *f, const gchar *format, ...) G_GNUC_PRINTF(2,3); typedef struct ekg_connection_starter *ekg_connection_starter_t; ekg_connection_starter_t ekg_connection_starter_new(guint16 defport); void ekg_connection_starter_free(ekg_connection_starter_t cs); void ekg_connection_starter_bind( ekg_connection_starter_t cs, const gchar *hostname); void ekg_connection_starter_set_srv_resolver( ekg_connection_starter_t cs, const gchar *service, const gchar *domain); void ekg_connection_starter_set_servers( ekg_connection_starter_t cs, const gchar *servers); void ekg_connection_starter_set_use_tls( ekg_connection_starter_t cs, gboolean use_tls); /* XXX */ GCancellable *ekg_connection_starter_run( ekg_connection_starter_t cs, GSocketClient *sock, ekg_connection_callback_t callback, ekg_connection_failure_callback_t failure_callback, gpointer priv_data); #endif /* __EKG_CONNECTIONS_H */ ekg2-0.4~pre+20120506.1/ekg/debug.h000066400000000000000000000020411175142753400161570ustar00rootroot00000000000000/* $Id$ */ #ifndef __EKG_DEBUG_H #define __EKG_DEBUG_H #ifdef __cplusplus extern "C" { #endif typedef enum { DEBUG_IO = 1, DEBUG_IORECV, DEBUG_FUNCTION, DEBUG_ERROR, DEBUG_GGMISC, /* cause of a lot GG_DEBUG_MISC in libgadu we've got special formats for them... */ DEBUG_WHITE, DEBUG_WARN, DEBUG_OK } debug_level_t; #ifndef DISABLE_DEBUG void debug(const char *format, ...); void debug_ext(debug_level_t level, const char *format, ...); #else #define debug(...) #define debug_ext(...) #endif #define debug_io(args...) debug_ext(DEBUG_IO, args) #define debug_iorecv(args...) debug_ext(DEBUG_IORECV, args) #define debug_function(args...) debug_ext(DEBUG_FUNCTION, args) #define debug_error(args...) debug_ext(DEBUG_ERROR, args) #define debug_white(args...) debug_ext(DEBUG_WHITE, args) #define debug_warn(args...) debug_ext(DEBUG_WARN, args) #define debug_ok(args...) debug_ext(DEBUG_OK, args) #ifdef __cplusplus } #endif #endif /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/ekg/dynstuff.c000066400000000000000000000662701175142753400167440ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2001-2003 Wojtek Kaniewski * Dawid Jarosz * Adam Wysocki * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #include #include #include #include /* * list_add_sorted() * * dodaje do listy dany element. przy okazji moe te skopiowa zawarto. * jeli poda si jako ostatni parametr funkcj porwnujc zawarto * elementw, moe posortowa od razu. * * - list - wskanik do listy, * - data - wskanik do elementu, * - alloc_size - rozmiar elementu, jeli chcemy go skopiowa. * * zwraca wskanik zaalokowanego elementu lub NULL w przpadku bdu. */ void *list_add_sorted(list_t *list, void *data, int (*comparision)(void *, void *)) { list_t new_, tmp; if (!list) { errno = EFAULT; return NULL; } new_ = xmalloc(sizeof(struct list)); new_->data = data; new_->next = NULL; /*new_->prev = NULL;*/ if (!(tmp = *list)) { *list = new_; } else { if (!comparision) { while (tmp->next) tmp = tmp->next; tmp->next = new_; } else { list_t prev = NULL; while (comparision(new_->data, tmp->data) > 0) { prev = tmp; tmp = tmp->next; if (!tmp) break; } if (!prev) { new_->next = *list; *list = new_; } else { prev->next = new_; new_->next = tmp; } } } return new_->data; } /** * list_add_beginning() * * Add item @a data to the begining of the @a list
* (Once again), item will be added at begining of the list - as first item
* * @sa list_add() * @sa list_remove() */ void *list_add_beginning(list_t *list, void *data) { list_t new_; if (!list) { errno = EFAULT; return NULL; } new_ = xmalloc(sizeof(struct list)); new_->next = *list; *list = new_; new_->data = data; return new_->data; } /** * list_add() * * Add item @a data to @a list without sorting
* Item will be added at end of list - as last item
* Wrapper to: list_add_sorted(list, data, alloc_size, NULL) * * @sa list_remove() * @sa list_add_beginning() - If you can have items of list in reverse sequence [third_added_item, second_added_item, first_added_item] and not sorted * @sa list_add_sorted() */ void *list_add(list_t *list, void *data) { return list_add_sorted(list, data, NULL); } void *list_add_sorted3(list_t *list, list_t new_, int (*comparision)(void *, void *)) { list_t tmp; if (!list) { errno = EFAULT; return NULL; } new_->next = NULL; if (!(tmp = *list)) { *list = new_; } else { if (!comparision) { while (tmp->next) tmp = tmp->next; tmp->next = new_; } else { list_t prev = NULL; while (comparision(new_, tmp) > 0) { prev = tmp; tmp = tmp->next; if (!tmp) break; } if (!prev) { new_->next = *list; *list = new_; } else { prev->next = new_; new_->next = tmp; } } } return new_; } void *list_add_beginning3(list_t *list, list_t new_) { if (!list) { errno = EFAULT; return NULL; } new_->next = *list; *list = new_; return new_; } void *list_add3(list_t *list, list_t new_) { return list_add_sorted3(list, new_, NULL); } /** * list_remove_safe() * * Remove item @a data from list_t pointed by @a list.
* Don't free whole list_t item struct. only set item_list_t->data to NULL
* * @note XXX, add note here why we should do it. * * @param list - pointer to list_t * @param data - data to remove from @a list * @param free_data -if set and item was found it'll call xfree() on it. * * @sa list_cleanup() - to remove NULL items from list. */ int list_remove_safe(list_t *list, void *data, int free_data) { list_t tmp; if (!list) { errno = EFAULT; return -1; } for (tmp = *list; tmp; tmp = tmp->next) { if (tmp->data == data) { if (free_data) xfree(tmp->data); tmp->data = NULL; return 0; } } errno = ENOENT; return -1; } /** * list_cleanup() * * Remove from list_t all items with l->data set to NULL.
* Use with list_remove_safe() after list is not in use. */ void list_cleanup(list_t *list) { list_t tmp; list_t last = NULL; if (!list) return; for (tmp = *list; tmp;) { if (tmp->data == NULL) { list_t cur = tmp; tmp = tmp->next; /* move to next item */ if (!last) *list = tmp; /* repoint list to next item */ else last->next = tmp; /* repoint last->next to next item */ xfree(cur); /* free current item struct */ } else { last = tmp; tmp = tmp->next; } } } int list_remove2(list_t *list, void *data, void (*func)(void *data)) { list_t tmp, last = NULL; if (!list) { errno = EFAULT; return -1; } tmp = *list; if (tmp && tmp->data == data) { *list = tmp->next; } else { for (; tmp && tmp->data != data; tmp = tmp->next) last = tmp; if (!tmp) { errno = ENOENT; return -1; } last->next = tmp->next; } if (func && tmp->data) func(tmp->data); xfree(tmp); return 0; } void *list_remove3(list_t *list, list_t elem, void (*func)(list_t data)) { list_t tmp, last = NULL; void *ret = NULL; if (!list) { errno = EFAULT; return ret; } tmp = *list; if (tmp && tmp == elem) { *list = ret = tmp->next; } else { for (; tmp && tmp != elem; tmp = tmp->next) last = tmp; if (!tmp) { errno = ENOENT; return ret; } last->next = ret = tmp->next; } if (func) func(tmp); xfree(tmp); return ret; } void *list_remove3i(list_t *list, list_t elem, void (*func)(list_t data)) { list_t tmp, last = NULL; void *ret = NULL; if (!list) { errno = EFAULT; return ret; } tmp = *list; if (tmp && tmp == elem) { *list = tmp->next; ret = list; /* GiM: this seems like a fail to me */ } else { for (; tmp && tmp != elem; tmp = tmp->next) last = tmp; if (!tmp) { errno = ENOENT; return ret; } last->next = tmp->next; ret = last; } if (func) func(tmp); xfree(tmp); return ret; } void *list_unlink3(list_t *list, list_t elem) { list_t tmp, last = NULL; void *ret = NULL; if (!list) { errno = EFAULT; return ret; } tmp = *list; if (tmp && tmp == elem) { *list = ret = tmp->next; } else { for (; tmp && tmp != elem; tmp = tmp->next) last = tmp; if (!tmp) { errno = ENOENT; return ret; } last->next = ret = tmp->next; } return ret; } /** * list_remove() * * Remove item @a data from list_t pointed by @a list * * @param list - pointer to list_t * @param data - data to remove from @a list * @param free_data - if set and item was found it'll call xfree() on it. * @sa list_destroy() - to remove whole list * * @return 0 if item was founded, and was removed from list_t pointed by @a list
* -1 and errno set to EFAULT, if @a list was NULL
* -1 and errno set to ENOENT, if item was not found */ int list_remove(list_t *list, void *data, int free_data) { return list_remove2(list, data, free_data ? xfree : NULL); } /** * list_get_nth() * * Get n'th item from list_t * * @param list - list_t * @param id - n'th item [list items are numerated from 1] * * @return n'th item (list->data) if found, or NULL with errno set to ENOENT */ void *list_get_nth(list_t list, int id) { while (list) { if ((--id) == 0) { /* errno = !ENOENT; */ return list->data; } list = list->next; } errno = ENOENT; return NULL; } void *list_get_nth3(list_t list, int id) { while (list) { if ((--id) == 0) return list; list = list->next; } errno = ENOENT; return NULL; } void list_resort(list_t *list, int (*comparision)(void *, void *)) { list_t tmplist = NULL; list_t l = *list; while (l) { list_t cur = l; l = l->next; list_add_sorted(&tmplist, cur->data, comparision); xfree(cur); } *list = tmplist; } void list_resort3(list_t *list, int (*comparision)(void *, void *)) { list_t tmplist = NULL; list_t l = *list; while (l) { list_t cur = l; l = l->next; list_add_sorted3(&tmplist, cur, comparision); } *list = tmplist; } /** * list_count() * * @param list - list_t * * @return items count on list_t @a list. */ int list_count(list_t list) { int count = 0; for (; list; list = list->next) count++; return count; } int list_destroy2(list_t list, void (*func)(void *)) { list_t tmp; while (list) { if (func && list->data) func(list->data); tmp = list->next; xfree(list); list = tmp; } return 0; } int list_destroy3(list_t list, void (*func)(void *)) { list_t tmp; while (list) { if (func) func(list); tmp = list->next; xfree(list); list = tmp; } return 0; } /** * list_destroy() * * Destroy all items from list_t @a list * * @note It doesn't take pointer to list_t, and it don't cleanup memory with \\0, so after list_destroy() you must remember that * @a list is unaccessible. So if you have list as global variable, or if you keep pointer to it in some struct.. you must NULL pointer. * so always do:
* * list_destroy(my_list, free_data); * my_list = NULL; * * * @param list - list_t * @param free_data - if set we will call xfree() on each item data * @sa list_remove() - to remove specified item. * * @return 0 */ int list_destroy(list_t list, int free_data) { return list_destroy2(list, free_data ? xfree : NULL); } /** * string_append_c() * * Append to string_t @a s char @a c. * * @param s - string_t * @param c - char to append * * @return 0 on success
* -1 and errno set to EFAULT if input params were wrong (s == NULL || format == NULL) */ int string_append_c(string_t s, char c) { string_t tmp = g_string_append_c(s, c); g_assert(tmp == s); return 0; } /** * string_append_n() * * Append to string_t @a s, first @a count chars, from @a str
* * @param s - string_t * @param str - buffer to append. * @param count - how many chars copy copy from @a str, or -1 to copy whole. * * @todo We append here NUL terminated string, so maybe let's always do count = xstrnlen(str, count);?
* Because now programmer can pass negative value, and it'll possible do SIGSEGV
* Also we can allocate less memory for string, when for example str[count-3] was NUL char.
* * @return 0 on success
* -1 and errno set to EFAULT if input params were wrong (s == NULL || str == NULL) */ int string_append_n(string_t s, const char *str, int count) { string_t tmp; gssize sl = xstrlen(str); if (count > sl || count == -1) tmp = g_string_append(s, str); else tmp = g_string_append_len(s, str, count); g_assert(tmp == s); return 0; } /** * string_append_format() * * Append to string_t @a s, formatted output of @a format + params
* Equivalent to:
* * char *tmp = saprintf(format, ...);
* string_append(s, tmp);
* xfree(tmp);
*
* * @note For more details about string formating functions read man 3 vsnprintf * * @sa string_append() - If you want/can use non-formating function.. * @sa saprintf() - If you want to format output but to normal char *, not to string_t * * @return 0 on success
* -1 and errno set to EFAULT if input params were wrong (s == NULL || format == NULL) */ int string_append_format(string_t s, const char *format, ...) { va_list ap; va_start(ap, format); g_string_append_vprintf(s, format, ap); va_end(ap); return 0; } /** * string_append_raw() * * Append to string_t @a s, @a count bytes from memory pointed by @a str
* * @sa string_append_n() - If you want to append NUL terminated (C-like) String * @todo XXX Protect from negative count (and less than -1) ? */ int string_append_raw(string_t s, const char *str, int count) { string_t tmp = g_string_append_len(s, str, count); g_assert(tmp == s); return 0; } /** * string_append() * * Append to string_t @a s, NUL terminated string pointed by str
* Wrapper to:string_append_n(s, str, -1) * * @sa string_append_n() */ int string_append(string_t s, const char *str) { return string_append_n(s, str, -1); } /* * string_insert_n() * * wstawia tekst w podane miejsce bufora. * * - s - cig znakw, * - index - miejsce, gdzie mamy wpisa (liczone od 0), * - str - tekst do dopisania, * - count - ilo znakw do dopisania (-1 znaczy, e wszystkie). */ void string_insert_n(string_t s, int index, const char *str, int count) { string_t tmp; gssize sl = xstrlen(str); if (count > sl || count == -1) tmp = g_string_insert(s, index, str); else tmp = g_string_insert_len(s, index, str, count); g_assert(tmp == s); } /** * string_insert() * * Insert given text (@a str) to given string_t (@a s) at given pos (@a index)
* Wrapper to: string_insert_t(s, index, str, -1) * * @param s - string_t * @param index - pos * @param str - text * * @sa string_insert_n() */ void string_insert(string_t s, int index, const char *str) { string_insert_n(s, index, str, -1); } /** * string_init() * * init string_t struct, allocating memory for string passed by @a value, and setting internal string_t data. * * @param value - if NULL char buffer will be inited with "", otherwise with given @a value. * @sa string_free() - to free memory used by string_t * * @return pointer to allocated string_t struct. */ string_t string_init(const char *value) { return g_string_new(value); } /** * string_init_n() * * init string_t struct, allocating memory string of length n * * @param n - number of bytes to allocate * @sa string_free() - to free memory used by string_t * * @return pointer to allocated string_t struct. */ string_t string_init_n(int n) { return g_string_sized_new(n); } /** * string_remove() * * Remove first @a count chars from string. * */ void string_remove(string_t s, int count) { /* XXX: potential breakage */ string_t tmp = g_string_erase(s, 0, count); g_assert(tmp == s); } /** * string_clear() * * Clear s->str (s->str[0] == '\\0')
* If memory allocated by string_t @a s was larger than 160, than decrease to 80 * * @param s - string_t * @sa string_free() - To free memory allocated by string_t */ void string_clear(string_t s) { string_t tmp = g_string_truncate(s, 0); g_assert(tmp == s); } /** * string_free() * * Cleanup memory after string_t @a s, and perhaps (if @a free_string set) cleanup memory after char buffer. * * @param s - string_t which we want to free. * @param free_string - do we want to free memory after char buffer? * @sa string_clear() - if you just want to clear saved char buffer, and you don't want to free internal string_t struct. * * @return if @a free_string != 0 always NULL
* else returns saved char buffer, which need be free()'d after use by xfree() */ char *string_free(string_t s, int free_string) { return g_string_free(s, free_string); } /* * ekg_itoa() * * prosta funkcja, ktra zwraca tekstow reprezentacj liczby. w obrbie * danego wywoania jakiej funkcji lub wyraenia moe by wywoania 10 * razy, poniewa tyle mamy statycznych buforw. lepsze to ni cige * tworzenie tymczasowych buforw na stosie i sprintf()owanie. * * - i - liczba do zamiany. * * zwraca adres do bufora, ktrego _NIE_NALEY_ zwalnia. */ const char *ekg_itoa(long int i) { static char bufs[10][16]; static int index = 0; char *tmp = bufs[index++]; if (index > 9) index = 0; snprintf(tmp, 16, "%ld", i); return tmp; } /* * array_make() * * tworzy tablic tekstw z jednego, rozdzielonego podanymi znakami. * * - string - tekst wejciowy, * - sep - lista elementw oddzielajcych, * - max - maksymalna ilo elementw tablicy. jeli rwne 0, nie ma * ogranicze rozmiaru tablicy. * - trim - czy wiksz ilo elementw oddzielajcych traktowa jako * jeden (na przykad spacje, tabulacja itp.) * - quotes - czy pola mog by zapisywane w cudzysowiach lub * apostrofach z escapowanymi znakami. * * zaalokowan tablic z zaalokowanymi cigami znakw, ktr naley * zwolni funkcj g_strfreev() */ char **array_make(const char *string, const char *sep, int max, int trim, int quotes) { const char *p, *q; char **result = NULL; int items = 0, last = 0; if (!string || !sep) goto failure; for (p = string; ; ) { int len = 0; char *token = NULL; if (max && items >= max - 1) last = 1; if (trim) { while (*p && xstrchr(sep, *p)) p++; if (!*p) break; } if (quotes && (*p == '\'' || *p == '\"')) { char sep = *p; char *r; for (q = p + 1, len = 0; *q; q++, len++) { if (*q == '\\') { q++; if (!*q) break; } else if (*q == sep) break; } if (last && q[0] && q[1]) goto way2; len++; r = token = xmalloc(len + 1); for (q = p + 1; *q; q++, r++) { if (*q == '\\') { q++; if (!*q) break; switch (*q) { case 'n': *r = '\n'; break; case 'r': *r = '\r'; break; case 't': *r = '\t'; break; default: *r = *q; } } else if (*q == sep) { break; } else *r = *q; } *r = 0; p = (*q) ? q + 1 : q; } else { way2: for (q = p, len = 0; *q && (last || !xstrchr(sep, *q)); q++, len++); token = xstrndup(p, len); p = q; } result = xrealloc(result, (items + 2) * sizeof(char*)); result[items] = token; result[++items] = NULL; if (!*p) break; p++; } failure: if (!items) result = xcalloc(1, sizeof(char*)); return result; } /* * array_add() * * dodaje element do tablicy. */ int array_add(char ***array, char *string) { int count = *array ? g_strv_length(*array) : 0; *array = xrealloc(*array, (count + 2) * sizeof(char*)); (*array)[count + 1] = NULL; (*array)[count] = string; return count + 1; } /* * array_add_check() * * dodaje element do tablicy, uprzednio sprawdzajc * czy taki ju w niej nie istnieje * * - array - tablica, * - string - szukany cig znakw, * - casesensitive - czy mamy zwraca uwag na wielko znakw? * * zwraca zero w przypadku, jeli cig ju jest na licie * lub aktualn liczb cigw na licie, po dodaniu */ int array_add_check(char ***array, char *string, int casesensitive) { if (!array_item_contains(*array, string, casesensitive)) return array_add(array, string); else xfree(string); return 0; } char *array_join_count(char **array, const char *sep, int count) { string_t s = string_init(NULL); if (array) { int i; for (i = 0; i < count; i++) { if (array[i]) string_append(s, array[i]); if (i != count-1) string_append(s, sep); } } return string_free(s, 0); } /* * array_contains() * * stwierdza, czy tablica zawiera podany element. * * - array - tablica, * - string - szukany cig znakw, * - casesensitive - czy mamy zwraca uwag na wielko znakw? * * 0/1 */ int array_contains(char **array, const char *string, int casesensitive) { int i; if (!array || !string) return 0; for (i = 0; array[i]; i++) { if (casesensitive && !xstrcmp(array[i], string)) return 1; if (!casesensitive && !xstrcasecmp(array[i], string)) return 1; } return 0; } /* * array_item_contains() * * stwierdza czy w tablicy znajduje si element zawierajcy podany cig * * - array - tablica, * - string - szukany cig znakw, * - casesensitive - czy mamy zwraca uwag na wielko znakw? * * 0/1 */ int array_item_contains(char **array, const char *string, int casesensitive) { int i; if (!array || !string) return 0; for (i = 0; array[i]; i++) { if (casesensitive && xstrstr(array[i], string)) return 1; if (!casesensitive && xstrcasestr(array[i], string)) return 1; } return 0; } char *array_shift(char ***array) { int count; char *out; if (!(count = g_strv_length(*array))) return NULL; out = (*array)[0]; memmove((*array), &(*array)[1], count * sizeof(char **)); count--; if (count == 0) { xfree(*array); *array = NULL; } return out; } void array_free_count(char **array, int count) { char **tmp; if (!array) return; for (tmp = array; count; tmp++, count--) xfree(*tmp); xfree(array); } /** * cssfind() * * Short for comma-separated string find, does check whether given string contains given element. * It's works like array_make()+array_contains(), but it's hell simpler and faster. * * @param haystack - comma-separated string to search. * @param needle - element to search for. * @param sep - separator. * @param caseinsensitive - take a wild guess. * * @return Pointer to found element on success, or NULL on failure. */ const char *cssfind(const char *haystack, const char *needle, const char sep, int caseinsensitive) { const char *comma = haystack-1; const int needlelen = xstrlen(needle); do { comma += xstrspn(comma+1, " \f\n\r\t\v")+1; if (!(caseinsensitive ? xstrncasecmp(comma, needle, needlelen) : xstrncmp(comma, needle, needlelen))) { const char *p, *q; p = comma + needlelen; if (!(q = xstrchr(p, sep))) q = p + xstrlen(p); if (q-p <= xstrspn(p, " \f\n\r\t\v")) /* '<' shouldn't happen */ return comma; } } while (sep && (comma = xstrchr(comma, sep))); return NULL; #if 0 /* old, exact-match code; uncomment when needed */ { const char *r = haystack-1; const int needlelen = xstrlen(needle); if (needlelen == 0) { /* workaround for searching '' */ char c[3]; c[0] = sep; c[1] = sep; c[2] = '\0'; r = xstrstr(haystack, c); if (r) /* return pointer to 'free space' between seps */ r++; } else { while ((r = (caseinsensitive ? xstrcasestr(r+1, needle) : xstrstr(r+1, needle))) && (((r != haystack) && ((*(r-1) != sep))) || ((*(r+needlelen) != '\0') && (*(r+needlelen) != sep)))) {}; } return r; } #endif } /* * eskejpuje: * * - \ -> \\ * * oraz wystpienia znakw ze stringa esc: * * - 0x07 (\a) -> \a * - 0x08 (\b) -> \b * - 0x09 (\t) -> \t * - 0x0A (\n) -> \n * - 0x0B (\v) -> \v * - 0x0C (\f) -> \f * - 0x0D (\r) -> \r * - pozostae -> \xXX (szesnastkowa reprezentacja) * * jeeli ktry z wymienionych wyej znakw (np. \a) nie wystpuje * w stringu to zostanie przepisany tak jak jest, bez eskejpowania. * * zwraca nowego, zaalokowanego stringa. */ char *escape(const char *src) { static const char esc[] = "\r\n"; string_t dst; if (!src) return NULL; dst = string_init(NULL); for (; *src; src++) { char ch = *src; static const char esctab[] = "abtnvfr"; if (!(ch == '\\' || strchr(esc, ch))) { string_append_c(dst, ch); continue; } string_append_c(dst, '\\'); if (ch >= 0x07 && ch <= 0x0D) string_append_c(dst, esctab[ch - 0x07]); else if (ch == '\\') string_append_c(dst, '\\'); else { char s[5]; snprintf(s, sizeof(s), "x%02X", (unsigned char) ch); string_append(dst, s); } } return string_free(dst, 0); } /* * dekoduje stringa wyeskejpowanego przy pomocy escape. * * - \\ -> \ * - \xXX -> szesnastkowo zdekodowany znak * - \cokolwiekinnego -> \cokolwiekinnego * * zwraca nowego, zaalokowanego stringa. */ char *unescape(const char *src) { int state = 0; string_t buf; unsigned char hex_msb = 0; if (!src) return NULL; buf = string_init(NULL); for (; *src; src++) { char ch = *src; if (state == 0) { /* normalny tekst */ /* sprawdzamy czy mamy cos po '\\', bo jezeli to ostatni * znak w stringu, to nie zostanie nigdy dodany. */ if (ch == '\\' && *(src + 1)) { state = 1; continue; } string_append_c(buf, ch); } else if (state == 1) { /* kod ucieczki */ if (ch == 'a') ch = '\a'; else if (ch == 'b') ch = '\b'; else if (ch == 't') ch = '\t'; else if (ch == 'n') ch = '\n'; else if (ch == 'v') ch = '\v'; else if (ch == 'f') ch = '\f'; else if (ch == 'r') ch = '\r'; else if (ch == 'x' && *(src + 1) && *(src + 2)) { state = 2; continue; } else if (ch != '\\') string_append_c(buf, '\\'); /* fallback - nieznany kod */ string_append_c(buf, ch); state = 0; } else if (state == 2) { /* pierwsza cyfra kodu szesnastkowego */ hex_msb = ch; state = 3; } else if (state == 3) { /* druga cyfra kodu szesnastkowego */ #define unhex(x) (unsigned char) ((x >= '0' && x <= '9') ? (x - '0') : \ (x >= 'A' && x <= 'F') ? (x - 'A' + 10) : \ (x >= 'a' && x <= 'f') ? (x - 'a' + 10) : 0) string_append_c(buf, unhex(ch) | (unhex(hex_msb) << 4)); #undef unhex state = 0; } } return string_free(buf, 0); } /* * handle private data */ static int private_data_cmp(private_data_t *item1, private_data_t *item2) { return xstrcmp(item1->name, item2->name); } static LIST_FREE_ITEM(private_data_free, private_data_t *) { xfree(data->name); xfree(data->value); } DYNSTUFF_LIST_DECLARE_SORTED(private_items, private_data_t, private_data_cmp, private_data_free, static __DYNSTUFF_ADD_SORTED, /* private_items_add() */ static __DYNSTUFF_REMOVE_SAFE, /* private_items_remove() */ __DYNSTUFF_DESTROY) /* private_items_destroy() */ static private_data_t *private_item_find(private_data_t **data, const char *item_name) { private_data_t *item; int cmp; if (!item_name) return NULL; for (item = *data; item; item = item->next) { if ( !(cmp = xstrcmp(item->name, item_name)) ) return item; if (cmp>0) return NULL; } return NULL; } int private_item_get_safe(private_data_t **data, const char *item_name, char **result) { private_data_t *item = private_item_find(data, item_name); if (item) { *result = item->value; return 1; } return 0; } const char *private_item_get(private_data_t **data, const char *item_name) { char *result = NULL; (void )private_item_get_safe(data, item_name, &result); return result; } int private_item_get_int_safe(private_data_t **data, const char *item_name, int *result) { char *tmp; if (!private_item_get_safe(data, item_name, &tmp)) return 0; *result = atoi(tmp); return 1; } int private_item_get_int(private_data_t **data, const char *item_name) { int result = 0; (void) private_item_get_int_safe(data, item_name, &result); return result; } void private_item_set(private_data_t **data, const char *item_name, const char *value) { private_data_t *item = private_item_find(data, item_name); int unset = !(value && *value); if (item) { if (unset) { private_items_remove(data, item); } else if (xstrcmp(item->value, value)) { xfree(item->value); item->value = xstrdup(value); } } else if (!unset) { item = xmalloc(sizeof(private_data_t)); item->name = xstrdup(item_name); item->value = xstrdup(value); private_items_add(data, item); } } void private_item_set_int(private_data_t **data, const char *item_name, int value) { private_item_set(data, item_name, value?ekg_itoa(value):NULL); } #if !GLIB_CHECK_VERSION(2, 28, 0) void g_slist_free_full(GSList *list, GDestroyNotify free_func) { GSList *it; for (it = list; it; it = it->next) free_func(it->data); g_slist_free(list); } #endif /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/ekg/dynstuff.h000066400000000000000000000173161175142753400167460ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2001-2002 Wojtek Kaniewski * Dawid Jarosz * Adam Wysocki * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __EKG_DYNSTUFF_H #define __EKG_DYNSTUFF_H #include #ifdef __cplusplus extern "C" { #endif /* * typedef list_t * * list_t jest prostym typem listy uywanej w praktycznie wszystkich * dynamicznych strukturach ekg. obecnie jest to lista jednokierunkowa * (pole `prev' jest rwne NULL), ale zostawiono moliwo rozbudowy * do dwukierunkowej bez zmiany ABI. dane s przechowywane w polu `data', * kolejny element w `next'. przykadowy kod iteracji: * * list_t l; * * for (l = lista; l; l = l->next) { * struct cokolwiek *c = l->data; * printf("%s\n", c->cokolwiek); * } * * wikszo list wystpujcych w ekg mona iterowa bez obawy o zmiany * ABI. pierwsze pole, bdce jednoznacznym identyfikatorem elementu listy * jest dostpne bezporednio, reszta przez odpowiednie funkcje. */ /* * New *3() lists * * Instead of allocing additional list of dual-pointer structs, we add * 'next' field to the beginning of real struct. C allows us to point * to that first field independently of which struct is it, so we can * use some type-indepdendent functions for that. The main target is * to totally remove old functions, but leave 'list_t'. * * Then, for example, session_t would point to first entry of userlist * (as userlist_t*), and that entry would point to second one, second * one to third, etc. But if we want to group few userlist entries * independently of their original structure, we could just catch them * in standard list_t and use its' 'next' field. */ struct list { /* it is important that we keep 'next' first field, * because C allows us to call first field of any structure * without knowing its' type * * so *3() would work peacefully w/ both list_t and not-list_t lists */ struct list *next; void *data; }; typedef struct list *list_t; #ifndef EKG2_WIN32_NOFUNCTION #define LIST_ADD_COMPARE(x, type) int x(const type data1, const type data2) #define LIST_ADD_SORTED(list, data, comp) list_add_sorted(list, data, (void *) comp) #define LIST_ADD_SORTED2(list, data, comp) list_add_sorted3((list_t *) (void *) list, (list_t) data, (void *) comp) #define LIST_ADD_BEGINNING2(list, data) list_add_beginning3((list_t *) (void *) list, (list_t) data) #define LIST_ADD2(list, data) list_add3((list_t *) (void *) list, (list_t) data) #define LIST_COUNT2(list) list_count((list_t) list) #define LIST_GET_NTH2(list, id) list_get_nth3((list_t) list, id) #define LIST_RESORT(list, comp) list_resort(list, (void *) comp) #define LIST_RESORT2(list, comp) list_resort3((list_t *) (void *) list, (void *) comp) #define LIST_REMOVE(list, data, func) list_remove2(list, data, (void *) func) #define LIST_REMOVE2(list, elem, func) list_remove3((list_t *) (void *) list, (list_t) elem, (void *) func) #define LIST_UNLINK2(list, elem) list_unlink3((list_t *) (void *) list, (list_t) elem) #define LIST_FREE_ITEM(x, type) void x(type data) #define LIST_DESTROY(list, func) list_destroy2(list, (void *) func) #define LIST_DESTROY2(list, func) list_destroy3((list_t) list, (void *) func) void *list_add(list_t *list, void *data); void *list_add_beginning(list_t *list, void *data); void *list_add_sorted(list_t *list, void *data, int (*comparision)(void *, void *)); void *list_add3(list_t *list, list_t new_); void *list_add_beginning3(list_t *list, list_t new_); void *list_add_sorted3(list_t *list, list_t new_, int (*comparision)(void *, void *)); int list_count(list_t list); void *list_get_nth(list_t list, int id); void *list_get_nth3(list_t list, int id); void list_resort(list_t *list, int (*comparision)(void *, void *)); void list_resort3(list_t *list, int (*comparision)(void *, void *)); int list_remove(list_t *list, void *data, int free_data); int list_remove2(list_t *list, void *data, void (*func)(void *)); void *list_remove3(list_t *list, list_t elem, void (*func)(list_t)); void *list_remove3i(list_t *list, list_t elem, void (*func)(list_t data)); void *list_unlink3(list_t *list, list_t elem); int list_destroy(list_t list, int free_data); int list_destroy2(list_t list, void (*func)(void *)); int list_destroy3(list_t list, void (*func)(void *)); void list_cleanup(list_t *list); int list_remove_safe(list_t *list, void *data, int free_data); #endif /* * typedef string_t * * prosty typ tekstowy pozwalajcy tworzy cigi tekstowe o dowolnej * dugoci bez obawy o rozmiar bufora. cig tekstowy jest dostpny * przez pole `str'. nie naley go zmienia bezporednio. przykadowy * kod: * * string_t s; * * s = string_init("ala"); * string_append_c(s, ' '); * string_append(s, "ma kota"); * printf("%s\n", s->str); * string_free(s, 1); */ typedef GString *string_t; string_t string_init(const char *str); int string_append(string_t s, const char *str); int string_append_n(string_t s, const char *str, int count); int string_append_c(string_t s, char ch); int string_append_raw(string_t s, const char *str, int count); int string_append_format(string_t s, const char *format, ...); void string_insert(string_t s, int index, const char *str); void string_insert_n(string_t s, int index, const char *str, int count); void string_remove(string_t s, int count); void string_clear(string_t s); char *string_free(string_t s, int free_string); /* tablice stringow */ char **array_make(const char *string, const char *sep, int max, int trim, int quotes); char *array_join_count(char **array, const char *sep, int count); int array_add(char ***array, char *string); int array_add_check(char ***array, char *string, int casesensitive); int array_contains(char **array, const char *string, int casesensitive); int array_item_contains(char **array, const char *string, int casesensitive); char *array_shift(char ***array); void array_free_count(char **array, int count); /* rozszerzenia libcw */ const char *ekg_itoa(long int i); const char *cssfind(const char *haystack, const char *needle, const char sep, int caseinsensitive); char *escape(const char *src); char *unescape(const char *src); /* * handle private data */ typedef struct private_data_s { struct private_data_s *next; char *name; char *value; } private_data_t; int private_item_get_safe(private_data_t **data, const char *item_name, char **result); const char *private_item_get(private_data_t **data, const char *item_name); int private_item_get_int_safe(private_data_t **data, const char *item_name, int *result); int private_item_get_int(private_data_t **data, const char *item_name); void private_item_set(private_data_t **data, const char *item_name, const char *value); void private_item_set_int(private_data_t **data, const char *item_name, int value); void private_items_destroy(private_data_t **data); #if !GLIB_CHECK_VERSION(2, 28, 0) void g_slist_free_full(GSList *list, GDestroyNotify free_func); #endif #ifdef __cplusplus } #endif #endif /* __EKG_DYNSTUFF_H */ /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/ekg/dynstuff_inline.h000066400000000000000000000277001175142753400203020ustar00rootroot00000000000000#ifndef __EKG_DYNSTUFF_INLINE_H #define __EKG_DYNSTUFF_INLINE_H /* we could use typeof() instead of passing paramtype, but let's be more portable */ #define DYNSTUFF_USE_LIST3 1 #if DYNSTUFF_USE_LIST3 # include #endif #ifdef __cplusplus extern "C" { #endif #if DYNSTUFF_USE_LIST3 #define __DYNSTUFF_LIST_ADD(lista, typ, __notused) \ void lista##_add(typ *new_) { list_add3((list_t *) (void *) &lista, (list_t) new_); } #define __DYNSTUFF_LIST_ADD_BEGINNING(lista, typ, __notused) \ void lista##_add(typ *new_) { list_add_beginning3((list_t *) (void *) &lista, (list_t) new_); } #define __DYNSTUFF_LIST_ADD_SORTED(lista, typ, comparision) \ void lista##_add(typ *new_) { list_add_sorted3((list_t *) (void *) &lista, (list_t) new_, (void *) comparision); } #define __DYNSTUFF_LIST_REMOVE_SAFE(lista, typ, free_func) \ void lista##_remove(typ *elem) { list_remove3((list_t *) (void *) &lista, (list_t) elem, (void *) free_func); } #define __DYNSTUFF_LIST_REMOVE_ITER(lista, typ, free_func) \ typ *lista##_removei(typ *elem) { return list_remove3i((list_t *) (void *) &lista, (list_t) elem, (void *) free_func); } #define __DYNSTUFF_LIST_UNLINK(lista, typ) \ void lista##_unlink(typ *elem) { list_unlink3((list_t *) (void *) &lista, (list_t) elem); } #define __DYNSTUFF_LIST_DESTROY(lista, typ, free_func) \ void lista##_destroy(void) { list_destroy3((list_t) lista, (void *) free_func); lista = NULL; } #define __DYNSTUFF_LIST_COUNT(lista, typ) \ int lista##_count(void) { return list_count((list_t) lista); } #else #define __DYNSTUFF_LIST_ADD(lista, typ, __notused) \ void lista##_add(typ *new_) { \ new_->next = NULL; \ if (!lista) { \ lista = new_; \ } else { \ typ *tmp = lista; \ \ while (tmp->next) \ tmp = tmp->next; \ tmp->next = new_; \ } \ } #define __DYNSTUFF_LIST_ADD_BEGINNING(lista, typ, __notused) \ void lista##_add(typ *new_) { \ new_->next = lista; \ lista = new_; \ } #define __DYNSTUFF_LIST_ADD_SORTED(lista, typ, comparision) \ void lista##_add(typ *new_) { \ new_->next = NULL; \ if (!lista) { \ lista = new_; \ } else { \ typ *tmp = lista; \ typ *prev = NULL; \ \ while (comparision(new_, tmp) > 0) { \ prev = tmp; \ tmp = tmp->next; \ if (!tmp) \ break; \ } \ \ if (!prev) { \ new_->next = lista; \ lista = new_; \ } else { \ prev->next = new_; \ new_->next = tmp; \ } \ } \ } #define __DYNSTUFF_LIST_REMOVE_SAFE(lista, typ, free_func) \ void lista##_remove(typ *elem) { \ if (!lista) /* programmer's fault */ \ return; \ \ if (lista == elem) \ lista = lista->next; \ else { \ typ *tmp, *last = lista; \ \ for (tmp = lista->next; tmp; tmp = tmp->next) { \ if (tmp == elem) \ break; \ if (tmp->next == NULL) { \ /* errno = ENOENT; */ \ return; \ } \ last = tmp; \ } \ last->next = tmp->next; \ } \ /* if (free_func) */ \ free_func(elem); \ xfree(elem); \ } #define __DYNSTUFF_LIST_REMOVE_ITER(lista, typ, free_func) \ typ *lista##_removei(typ *elem) { \ typ *ret; \ \ if (lista == elem) { \ lista = lista->next; \ ret = (typ *) &lista; \ } else { \ typ *tmp, *last = lista; \ \ for (tmp = lista->next; tmp; tmp = tmp->next) { \ if (tmp == elem) \ break; \ last = tmp; \ } \ last->next = tmp->next; \ ret = last; \ } \ /* if (free_func) */ \ free_func(elem); \ xfree(elem); \ return ret; \ } #define __DYNSTUFF_LIST_UNLINK(lista, typ) \ void lista##_unlink(typ *elem) { \ if (!lista) /* programmer's fault */ \ return; \ \ if (lista == elem) \ lista = lista->next; \ else { \ typ *tmp, *last = lista; \ \ for (tmp = lista->next; tmp; tmp = tmp->next) { \ if (tmp == elem) \ break; \ if (tmp->next == NULL) { \ /* errno = ENOENT; */ \ return; \ } \ last = tmp; \ } \ last->next = tmp->next; \ } \ } #define __DYNSTUFF_LIST_DESTROY(lista, typ, free_func) \ void lista##_destroy(void) { \ while (lista) { \ typ *tmp = lista; \ \ lista = lista->next; \ \ /* if (free_func) */ \ free_func(tmp); \ \ xfree(tmp); \ } \ } #define __DYNSTUFF_LIST_COUNT(lista, typ) \ int lista##_count(void) { \ int count = 0; \ typ *list; \ \ for (list = lista; list; list = list->next) \ count++; \ return count; \ } #endif /* !DYNSTUFF_USE_LIST3 */ /* !!! for other lists !!! [when we (have many || don't know) head of list during compilation time] */ #if DYNSTUFF_USE_LIST3 #define __DYNSTUFF_ADD(prefix, typ, __notused) \ void prefix##_add(typ **lista, typ *new_) { list_add3((list_t *) lista, (list_t) new_); } #define __DYNSTUFF_ADD_BEGINNING(prefix, typ, __notused) \ void prefix##_add(typ **lista, typ *new_) { list_add_beginning3((list_t *) lista, (list_t) new_); } #define __DYNSTUFF_ADD_SORTED(prefix, typ, comparision) \ void prefix##_add(typ **lista, typ *new_) { list_add_sorted3((list_t *) lista, (list_t) new_, (void *) comparision); } #define __DYNSTUFF_REMOVE_SAFE(prefix, typ, free_func) \ void prefix##_remove(typ **lista, typ *elem) { \ list_remove3((list_t *) lista, (list_t) elem, (void *) free_func); \ } #define __DYNSTUFF_REMOVE_ITER(prefix, typ, free_func) \ typ *prefix##_removei(typ **lista, typ *elem) { \ return list_remove3i((list_t *) lista, (list_t) elem, (void *) free_func); \ } #define __DYNSTUFF_DESTROY(prefix, typ, free_func) \ void prefix##_destroy(typ **lista) { \ list_destroy3((list_t) *lista, (void *) free_func); \ *lista = NULL; \ } #define __DYNSTUFF_COUNT(prefix, typ) \ int prefix##_count(typ *lista) { \ return list_count((list_t) lista); \ } #define __DYNSTUFF_GET_NTH(prefix, typ) \ typ *prefix##_get_nth(typ *lista, int id) { \ return list_get_nth3((list_t) lista, id); \ } #else /* XXX, checkit */ #define __DYNSTUFF_ADD(prefix, typ, __notused) \ void prefix##_add(typ **lista, typ *new_) { \ typ *tmp = *lista; \ \ new_->next = NULL; \ if (!(tmp = *lista)) { \ *lista = new_; \ } else { \ while (tmp->next) \ tmp = tmp->next; \ tmp->next = new_; \ } \ } #define __DYNSTUFF_ADD_BEGINNING(prefix, typ, __notused) \ void prefix##_add(typ **lista, typ *new_) { \ new_->next = *lista; \ *lista = new_; \ } #define __DYNSTUFF_ADD_SORTED(prefix, typ, comparision) \ void prefix##_add(typ **lista, typ *new_) { \ typ *tmp; \ \ new_->next = NULL; \ if (!(tmp = *lista)) { \ *lista = new_; \ } else { \ typ *prev = NULL; \ \ while (comparision(new_, tmp) > 0) { \ prev = tmp; \ tmp = tmp->next; \ if (!tmp) \ break; \ } \ \ if (!prev) { \ new_->next = *lista; \ *lista = new_; \ } else { \ prev->next = new_; \ new_->next = tmp; \ } \ } \ } #define __DYNSTUFF_REMOVE_SAFE(prefix, typ, free_func) \ void prefix##_remove(typ **lista, typ *elem) { \ if (!lista || !(*lista)) /* programmer's fault */\ return; \ \ if (*lista == elem) \ *lista = (*lista)->next; \ else { \ typ *tmp, *last = *lista; \ \ for (tmp = (*lista)->next; tmp; tmp = tmp->next) { \ if (tmp == elem) \ break; \ if (tmp->next == NULL) { \ /* errno = ENOENT; */ \ return; \ } \ last = tmp; \ } \ last->next = tmp->next; \ } \ /* if (free_func) */ \ free_func(elem); \ xfree(elem); \ } #define __DYNSTUFF_REMOVE_ITER(prefix, typ, free_func) \ typ *prefix##_removei(typ **lista, typ *elem) { \ typ *ret; \ \ if (*lista == elem) { \ *lista = (*lista)->next; \ ret = (typ *) lista; \ } else { \ typ *tmp, *last = *lista; \ \ for (tmp = (*lista)->next; tmp; tmp = tmp->next) { \ if (tmp == elem) \ break; \ last = tmp; \ } \ last->next = tmp->next; \ ret = last; \ } \ /* if (free_func) */ \ free_func(elem); \ xfree(elem); \ return ret; \ } #define __DYNSTUFF_UNLINK(prefix, typ) \ void prefix##_unlink(typ **lista, typ *elem) { \ if (!lista || !(*lista)) /* programmer's fault */ \ return; \ \ if (*lista == elem) \ *lista = (*lista)->next; \ else { \ typ *tmp, *last = *lista; \ \ for (tmp = (*lista)->next; tmp; tmp = tmp->next) { \ if (tmp == elem) \ break; \ if (tmp->next == NULL) { \ /* errno = ENOENT; */ \ return; \ } \ last = tmp; \ } \ last->next = tmp->next; \ } \ } #define __DYNSTUFF_DESTROY(prefix, typ, free_func) \ void prefix##_destroy(typ **lista) { \ while (*lista) { \ typ *tmp = *lista; \ \ *lista = (*lista)->next; \ \ /* if (free_func) */ \ free_func(tmp); \ \ xfree(tmp); \ } \ } #define __DYNSTUFF_COUNT(prefix, typ) \ int prefix##_count(typ *list) { \ int count = 0; \ \ for (; list; list = list->next) \ count++; \ return count; \ } #define __DYNSTUFF_GET_NTH(prefix, typ) \ typ *prefix##_get_nth(typ *lista, int id) { \ while (lista) { \ if ((--id) == 0) \ return lista; \ \ lista = lista->next; \ } \ return NULL; \ } #endif #define __DYNSTUFF_NOREMOVE(lista, typ, free_func) #define __DYNSTUFF_NOUNLINK(lista, typ) #define __DYNSTUFF_NOCOUNT(lista, typ) #define __DYNSTUFF_NODESTROY(lista, typ, free_func) #define DYNSTUFF_LIST_DECLARE_FULL(lista, type, compare_func, free_func, list_add, list_remove, list_remove2, list_unlink, list_destroy, list_count) \ list_add(lista, type, compare_func) \ list_remove(lista, type, free_func) \ list_remove2(lista, type, free_func) \ list_unlink(lista, type) \ list_destroy(lista, type, free_func) \ list_count(lista, type) #define DYNSTUFF_LIST_DECLARE(lista, type, free_func, list_add, list_remove, list_destroy) \ DYNSTUFF_LIST_DECLARE_WC(lista, type, free_func, list_add, list_remove, list_destroy, __DYNSTUFF_NOCOUNT) #define DYNSTUFF_LIST_DECLARE_NF(lista, type, list_add, list_unlink) \ DYNSTUFF_LIST_DECLARE_FULL(lista, type, NULL, NULL, list_add, __DYNSTUFF_NOREMOVE, __DYNSTUFF_NOREMOVE, list_unlink, __DYNSTUFF_NODESTROY, __DYNSTUFF_NOCOUNT) #define DYNSTUFF_LIST_DECLARE_WC(lista, type, free_func, list_add, list_remove, list_destroy, list_count) \ DYNSTUFF_LIST_DECLARE_FULL(lista, type, NULL, free_func, list_add, list_remove, __DYNSTUFF_NOREMOVE, __DYNSTUFF_NOUNLINK, list_destroy, list_count) #define DYNSTUFF_LIST_DECLARE_SORTED(lista, type, compare_func, free_func, list_add, list_remove, list_destroy) \ DYNSTUFF_LIST_DECLARE_FULL(lista, type, compare_func, free_func, list_add, list_remove, __DYNSTUFF_NOREMOVE, __DYNSTUFF_NOUNLINK, list_destroy, __DYNSTUFF_NOCOUNT) #define DYNSTUFF_LIST_DECLARE2(lista, type, free_func, list_add, list_remove, list_remove2, list_destroy) \ DYNSTUFF_LIST_DECLARE_FULL(lista, type, NULL, free_func, list_add, list_remove, list_remove2, __DYNSTUFF_NOUNLINK, list_destroy, __DYNSTUFF_NOCOUNT) #define DYNSTUFF_LIST_DECLARE2_SORTED(lista, type, compare_func, free_func, list_add, list_remove, list_remove2, list_destroy) \ DYNSTUFF_LIST_DECLARE_FULL(lista, type, compare_func, free_func, list_add, list_remove, list_remove2, __DYNSTUFF_NOUNLINK, list_destroy, __DYNSTUFF_NOCOUNT) #define DYNSTUFF_LIST_DECLARE_SORTED_NF(lista, type, compare_func, list_add, list_unlink) \ DYNSTUFF_LIST_DECLARE_FULL(lista, type, compare_func, NULL, list_add, __DYNSTUFF_NOREMOVE, __DYNSTUFF_NOREMOVE, list_unlink, __DYNSTUFF_NODESTROY, __DYNSTUFF_NOCOUNT) #ifdef __cplusplus } #endif #endif ekg2-0.4~pre+20120506.1/ekg/ekg.c000066400000000000000000000574361175142753400156540ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2001-2003 Wojtek Kaniewski * Robert J. Wony * Pawe Maziarz * Adam Osuchowski * Dawid Jarosz * Wojciech Bojdo * Piotr Domagalski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #include #include #ifndef NO_POSIX_SYSTEM #include #endif #include #include #ifndef NO_POSIX_SYSTEM #include #include #include #include #include /* rlimit */ #endif #include #include #include #include #include #include #ifndef NO_POSIX_SYSTEM #include #else #include #endif #ifdef NO_POSIX_SYSTEM #include #include #include #endif #include #include #include #include #include #include #include #include "emoticons.h" #include "internal.h" #include "scripts.h" char *config_dir; char *old_config_dir; /* used for copy old config files */ int mesg_startup; static char argv0[PATH_MAX]; static gchar last_err_message[128] = {0}; pid_t speech_pid = 0; static int stderr_backup = -1; int no_mouse = 0; /** * ekg_autoaway_timer() * * * less important things which don't need to be checked every main loop iteration * e.g. autoaways * * executed each second. */ static TIMER(ekg_autoaway_timer) { session_t *sl; time_t t; if (type) return 0; t = time(NULL); /* sprawd autoawaye rnych sesji */ for (sl = sessions; sl; sl = sl->next) { session_t *s = sl; int tmp; if (!s->connected || (s->status < EKG_STATUS_AWAY)) /* lowest autostatus is autoxa, so from xa and lower ones we can't go further */ continue; do { if ((s->status == EKG_STATUS_AWAY) || (tmp = session_int_get(s, "auto_away")) < 1 || !s->activity) break; if (t - s->activity > tmp) command_exec(NULL, s, ("/_autoaway"), 0); } while (0); do { if ((tmp = session_int_get(s, "auto_xa")) < 1 || !s->activity) break; if (t - s->activity > tmp) command_exec(NULL, s, ("/_autoxa"), 0); } while (0); } return 0; } /* * ekg_loop() * * gwna ptla ekg. obsuguje przegldanie deskryptorw, timery i wszystko, * co ma si dzia w tle. */ void ekg_loop() { g_main_context_iteration(NULL, FALSE); { #ifdef WATCHES_FIXME { /* przejrzyj deskryptory */ list_t l; for (l = watches; l; l = l->next) { watch_t *w = l->data; if (!w) continue; if (!FD_ISSET(w->fd, &rd) && !FD_ISSET(w->fd, &wd)) { /* timeout checking */ if (w->timeout < 1 || (tv.tv_sec - w->started) < w->timeout) continue; w->removed = -1; if (w->buf) { int (*handler)(int, int, char*, void*) = w->handler; if (handler(2, w->fd, NULL, w->data) == -1 || w->removed == 1) { w->removed = 0; watch_free(w); continue; } } else { int (*handler)(int, int, int, void*) = w->handler; if (handler(2, w->fd, w->type, w->data) == -1 || w->removed == 1) { w->removed = 0; watch_free(w); continue; } } w->removed = 0; continue; } if (w->fd == 0) { session_t *s; for (s = sessions; s; s = s->next) { if (!s->connected || !s->autoaway) continue; if (session_int_get(s, "auto_back") == 2) command_exec(NULL, s, ("/_autoback"), 2); } } } } #endif } #undef tv return; } #ifndef NO_POSIX_SYSTEM static void handle_sigusr1() { debug("sigusr1 received\n"); query_emit(NULL, "ekg-sigusr1"); signal(SIGUSR1, handle_sigusr1); } static void handle_sigusr2() { debug("sigusr2 received\n"); query_emit(NULL, "ekg-sigusr2"); signal(SIGUSR2, handle_sigusr2); } static void handle_sighup() { ekg_exit(); } /** * Notifies plugins, writes the message to stderr, with last_err_message. * * We are only allowed to call POSIX-defined "async-signal-safe functions" here * (see signal(7)), because anything else can result in a "bad thing" such as a * deadlock (e.g. if the signal was a result of a malloc()/free()) or a * segfault. There were cases of this happening to ekg2. * * In order to avoid touching the (possibly corrupt) heap, we invoke statically * stored set of abort handlers. This is the mechanism we use to notify UI and * logs plugins to reset the terminal and sync their output as necessary. */ static void handle_fatal_signal(char *message) { const char *err_msg_prefix = "Last error message (if any): "; const char *debug_instructions = "If a file called core is be created, try running the following\r\n" "command:\r\n" "\r\n" " gdb ekg2 core\r\n" "\n" "note the last few lines, and then note the output from the ,,bt'' command.\r\n" "This will help the program authors find the location of the problem\r\n" "and most likely will help avoid such crashes in the future.\r\n"; if (stderr_backup && stderr_backup != -1) dup2(stderr_backup, 2); /* Notify plugins of impending doom. */ ekg2_run_all_abort_handlers(); /* Now that the terminal is (hopefully) back to plain text mode, write messages. */ /* There is nothing we can do if this fails, so suppress warnings about ignored results. */ IGNORE_RESULT(write(2, "\r\n\r\n *** ", 9)); IGNORE_RESULT(write(2, message, strlen(message))); IGNORE_RESULT(write(2, " ***\r\n", 6)); IGNORE_RESULT(write(2, err_msg_prefix, strlen(err_msg_prefix))); IGNORE_RESULT(write(2, last_err_message, strlen(last_err_message))); IGNORE_RESULT(write(2, "\r\n", 2)); IGNORE_RESULT(write(2, debug_instructions, strlen(debug_instructions))); } /* See handle_fatal_signal comment. */ static void handle_sigabrt() { signal(SIGABRT, SIG_DFL); handle_fatal_signal("Abnormal program termination"); raise(SIGABRT); } /* See handle_fatal_signal comment. */ static void handle_sigsegv() { signal(SIGSEGV, SIG_DFL); handle_fatal_signal("Segmentation violation detected"); raise(SIGSEGV); } #endif /* * prepare_batch_line() * * funkcja bierze podane w linii polece argumenty i robi z nich pojedycz * lini polece. * * - argc - wiadomo co ;) * - argv - wiadomo co ;) * - n - numer argumentu od ktrego zaczyna si polecenie. * * zwraca stworzon linie w zaalokowanym buforze lub NULL przy bdzie. */ static char *prepare_batch_line(char *argv[], int n) { size_t len = 0; char *buf; int i; for (i = n; argv[i]; i++) len += xstrlen(argv[i]) + 1; buf = xmalloc(len); for (i = n; argv[i]; i++) { xstrcat(buf, argv[i]); if (argv[i+1]) xstrcat(buf, " "); } return buf; } /* * handle_stderr() * * wywietla to, co uzbiera si na stderr. */ static WATCHER_LINE(handle_stderr) /* stay */ { /* XXX */ /* print("stderr", watch); */ return 0; } /** * ekg_debug_handler() * * debug message [if config_debug set] coming direct from libgadu (by libgadu_debug_handler()) * or by debug() or by debug_ext()
* XXX, doc more. But function now is ok. * * @sa debug_ext() * * @bug It can happen than internal string_t @a line will be not freed. * * @param level * @param format * @param ap * */ void ekg_debug_handler(int level, const char *format, va_list ap) { static GString *line = NULL; char *tmp = NULL; char *theme_format; int is_UI = 0; if (!config_debug) return; if (line) { g_string_append_vprintf(line, format, ap); if (line->len == 0 || line->str[line->len - 1] != '\n') return; line->str[line->len - 1] = '\0'; /* remove '\n' */ tmp = g_string_free(line, FALSE); line = NULL; } else { int tmplen = g_vasprintf(&tmp, format, ap); if (tmplen < 0 || !tmp) /* OutOfMemory? */ return; if (tmplen == 0 || tmp[tmplen - 1] != '\n') { line = g_string_new_len(tmp, tmplen); g_free(tmp); return; } tmp[tmplen - 1] = 0; /* remove '\n' */ } switch(level) { case 0: theme_format = "debug"; break; case DEBUG_IO: theme_format = "iodebug"; break; case DEBUG_IORECV: theme_format = "iorecvdebug"; break; case DEBUG_FUNCTION: theme_format = "fdebug"; break; case DEBUG_ERROR: theme_format = "edebug"; break; case DEBUG_WHITE: theme_format = "wdebug"; break; case DEBUG_WARN: theme_format = "warndebug"; break; case DEBUG_OK: theme_format = "okdebug"; break; default: theme_format = "debug"; break; } ekg_fix_utf8(tmp); /* debug message can contain random data */ buffer_add(&buffer_debug, theme_format, tmp); query_emit(NULL, "ui-is-initialized", &is_UI); if (is_UI && window_debug) { print_window_w(window_debug, EKG_WINACT_NONE, theme_format, tmp); } #ifdef STDERR_DEBUG /* STDERR debug */ else fprintf(stderr, "%s\n", tmp); #endif xfree(tmp); } static void glib_debug_handler(const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data) { static int recurse = 0; if (recurse) return; if (!config_debug) return; recurse++; debug("[%s] %s\n", log_domain, message); recurse--; } static void glib_print_handler(const gchar *string) { debug("%s", string); } static void glib_printerr_handler(const gchar *string) { g_strlcpy(last_err_message, string, sizeof(last_err_message)); debug_error("%s", string); } static GOptionEntry ekg_options[] = { { "user", 'u', 0, G_OPTION_ARG_STRING, NULL, "uses profile NAME", "NAME" }, { "theme", 't', 0, G_OPTION_ARG_STRING, NULL, "loads theme from FILE", "FILE" }, { "no-auto", 'n', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, NULL, "does not connect to server automatically", NULL }, { "no-mouse", 'm', 0, G_OPTION_ARG_NONE, &no_mouse, "does not load mouse support", NULL }, { "no-global-config", 'N', 0, G_OPTION_ARG_NONE, NULL, "ignores global configuration file", NULL }, { "frontend", 'F', 0, G_OPTION_ARG_STRING, NULL, "uses NAME frontend (default is ncurses)", "NAME" }, { "away", 'a', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, NULL, "changes status to ``away''", "[DESCRIPTION]" }, { "back", 'b', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, NULL, "changes status to ``available''", "[DESCRIPTION]" }, { "invisible", 'i', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, NULL, "changes status to ``invisible''", "[DESCRIPTION]" }, { "dnd", 'd', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, NULL, "changes status to ``do not disturb''", "[DESCRIPTION]" }, { "free-for-chat", 'f', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, NULL, "changes status to ``free for chat''", "[DESCRIPTION]" }, { "xa", 'x', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, NULL, "changes status to ``very busy''", "[DESCRIPTION]" }, { "version", 'v', 0, G_OPTION_ARG_NONE, NULL, "print program version and exit", NULL }, { NULL } }; struct option_callback_args { gint *new_status; gchar **new_descr; }; gboolean set_status_callback(const gchar *optname, const gchar *optval, gpointer data, GError **error) { struct option_callback_args *args = data; gchar c = optname[1] == '-' ? optname[2] : optname[1]; switch (c) { case 'a': *args->new_status = EKG_STATUS_AWAY; break; case 'b': *args->new_status = EKG_STATUS_AVAIL; break; case 'd': *args->new_status = EKG_STATUS_DND; break; case 'f': *args->new_status = EKG_STATUS_FFC; break; case 'i': *args->new_status = EKG_STATUS_INVISIBLE; break; case 'x': *args->new_status = EKG_STATUS_XA; break; } xfree(*args->new_descr); *args->new_descr = xstrdup(optval); return TRUE; } int main(int argc, char **argv) { gint auto_connect = 1, no_global_config = 0, no_config = 0, new_status = 0, print_version = 0; char *tmp = NULL, *new_descr = NULL; gchar *load_theme = NULL, *new_profile = NULL, *frontend = NULL; GError *err = NULL; #ifndef NO_POSIX_SYSTEM struct rlimit rlim; #else WSADATA wsaData; #endif g_type_init(); #ifndef NO_POSIX_SYSTEM /* zostaw po sobie core */ rlim.rlim_cur = RLIM_INFINITY; rlim.rlim_max = RLIM_INFINITY; setrlimit(RLIMIT_CORE, &rlim); #endif #ifdef NO_POSIX_SYSTEM if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) { fprintf(stderr, "WSAStartup() failed? wtf?!.. Oh, I see windows ;>"); } #endif ekg_started = time(NULL); setlocale(LC_ALL, ""); tzset(); #ifdef ENABLE_NLS bindtextdomain("ekg2",LOCALEDIR); textdomain("ekg2"); #endif srand(time(NULL)); g_strlcpy(argv0, argv[0], sizeof(argv0)); home_dir = xstrdup(getenv("HOME")); #ifndef NO_POSIX_SYSTEM if (!home_dir) { struct passwd *pw; if ((pw = getpwuid(getuid()))) home_dir = xstrdup(pw->pw_dir); } #else if (!home_dir) home_dir = xstrdup(getenv("USERPROFILE")); if (!home_dir) home_dir = xstrdup("c:\\"); #endif if (!home_dir) { fprintf(stderr, _("Can't find user's home directory. Ask administration to fix it.\n")); return 1; } command_init(); #ifndef NO_POSIX_SYSTEM signal(SIGABRT, handle_sigabrt); signal(SIGSEGV, handle_sigsegv); signal(SIGHUP, handle_sighup); signal(SIGTERM, handle_sighup); signal(SIGUSR1, handle_sigusr1); signal(SIGUSR2, handle_sigusr2); signal(SIGALRM, SIG_IGN); signal(SIGPIPE, SIG_IGN); #endif ekg_options[0].arg_data = &new_profile; ekg_options[1].arg_data = &load_theme; ekg_options[2].arg_data = &auto_connect; ekg_options[4].arg_data = &no_global_config; ekg_options[5].arg_data = &frontend; ekg_options[6].arg_data = &set_status_callback; ekg_options[7].arg_data = &set_status_callback; ekg_options[8].arg_data = &set_status_callback; ekg_options[9].arg_data = &set_status_callback; ekg_options[10].arg_data = &set_status_callback; ekg_options[11].arg_data = &set_status_callback; ekg_options[12].arg_data = &print_version; { GOptionContext *opt; GOptionGroup *g; struct option_callback_args optargs = { &new_status, &new_descr }; opt = g_option_context_new("[COMMAND...]"); g = g_option_group_new(NULL, NULL, NULL, &optargs, NULL); g_option_group_add_entries(g, ekg_options); g_option_group_set_translation_domain(g, "ekg2"); g_option_context_set_description(opt, "Options concerned with status depend on the protocol of particular session -- \n" \ "some sessions may not support ``do not disturb'' status, etc.\n"); g_option_context_set_main_group(opt, g); if (!g_option_context_parse(opt, &argc, &argv, &err)) { g_print("Option parsing failed: %s\n", err->message); return 1; } g_option_context_free(opt); /* GOptionGroup is freed implicitly */ } if (print_version) { g_print("ekg2-%s (compiled on %s)\n", VERSION, compile_time()); return 0; } g_log_set_default_handler(glib_debug_handler, NULL); g_set_print_handler(glib_print_handler); g_set_printerr_handler(glib_printerr_handler); in_autoexec = 1; if (argv[1]) { batch_line = prepare_batch_line(argv, 1); batch_mode = 1; } if ((config_profile = new_profile)) tmp = saprintf("/%s", config_profile); else tmp = xstrdup(""); if (getenv("HOME_ETC")) old_config_dir = saprintf("%s/ekg2%s", getenv("HOME_ETC"), tmp); else old_config_dir = saprintf("%s/.ekg2%s", home_dir, tmp); config_dir = g_build_filename(g_get_user_config_dir(), "ekg2", tmp, NULL); xfree(tmp); tmp = NULL; /* initialize dynamic module support */ ekg2_dlinit(argv[0]); variable_init(); variable_set_default(); queries_init(); mesg_startup = mesg_set(MESG_CHECK); #ifdef DEFAULT_THEME if (theme_read(DEFAULT_THEME, 1) == -1) #endif theme_init(); window_debug = window_new(NULL, NULL, -1); /* debugowanie */ window_status = window_new(NULL, NULL, 1); /* okno stanu */ window_current = window_status; #if 0 if (!no_global_config) config_read(SYSCONFDIR "/ekg2.conf"); #endif if (frontend) { plugin_load(frontend, -254, 1); config_changed = 1; g_free(frontend); } config_read_plugins(); #if 0 if (!no_global_config) config_read(SYSCONFDIR "/ekg2-override.conf"); #endif /* userlist_read(); */ emoticon_read(); msg_queue_read(); if (!frontend) { #ifdef HAVE_NCURSES if (!have_plugin_of_class(PLUGIN_UI)) plugin_load(("ncurses"), -254, 1); #endif #ifdef HAVE_GTK if (!have_plugin_of_class(PLUGIN_UI)) plugin_load(("gtk"), -254, 1); #endif #ifdef HAVE_LIBREADLINE if (!have_plugin_of_class(PLUGIN_UI)) plugin_load(("readline"), -254, 1); #endif } if (!have_plugin_of_class(PLUGIN_UI)) { struct buffer *b; for (b = buffer_debug.data; b; b = b->next) fprintf(stderr, "%s\n", b->line); fprintf(stderr, "\n\nNo UI-PLUGIN!\n"); return 1; } else { struct buffer *b; for (b = buffer_debug.data; b; b = b->next) print_window_w(window_debug, EKG_WINACT_NONE, b->target, b->line); } if (!have_plugin_of_class(PLUGIN_PROTOCOL)) { #ifdef HAVE_EXPAT plugin_load(("jabber"), -254, 1); #endif #ifdef HAVE_LIBGADU plugin_load(("gg"), -254, 1); #endif plugin_load(("irc"), -254, 1); } theme_plugins_init(); scripts_init(); /* If user does not have a config, don't bug about config upgrades. */ if (config_read(NULL) == -1) config_version = -1; /* jeli ma by theme, niech bdzie theme */ if (load_theme) { theme_read(load_theme, 1); g_free(load_theme); } else if (config_theme) theme_read(config_theme, 1); in_autoexec = 0; /* XXX, unidle() was here */ /* wypadaoby obserwowa stderr */ if (!batch_mode) { #ifndef NO_POSIX_SYSTEM int fd[2]; if (!pipe(fd)) { fcntl(fd[0], F_SETFL, O_NONBLOCK); fcntl(fd[1], F_SETFL, O_NONBLOCK); watch_add_line(NULL, fd[0], WATCH_READ_LINE, handle_stderr, NULL); stderr_backup = fcntl(2, F_DUPFD, 0); dup2(fd[1], 2); } #endif } if (!batch_mode && config_display_welcome) print("welcome", VERSION); protocol_init(); events_init(); metacontact_init(); /* scripts_init(); */ /* it has to be done after plugins are loaded, either we wouldn't know if we are * supporting some protocol in current build */ if (session_read(NULL) == -1) no_config = 1; ekg_tls_init(); config_postread(); /* status window takes first session if not set before*/ if (!window_status->session && sessions) window_session_set(window_status, sessions); metacontact_read(); /* read the metacontacts info */ { session_t *s; /* wylosuj opisy i zmie stany klientw */ for (s = sessions; s; s = s->next) { const char *cmd = NULL; if (new_status) session_status_set(s, new_status); if (new_descr) session_descr_set(s, new_descr); cmd = ekg_status_string(s->status, 1); command_exec_format(NULL, s, 2, ("/%s %s"), cmd, (new_descr) ? new_descr : ""); } /* po zainicjowaniu protokow, pocz si automagicznie ze * wszystkim, co chce si automagicznie czy. */ for (s = sessions; s; s = s->next) { if (auto_connect && session_int_get(s, "auto_connect") == 1) command_exec(NULL, s, ("/connect"), 0); } } if (no_config) { #ifdef HAVE_LIBGADU if (plugin_find("gg")) print("no_config"); else print("no_config_gg_not_loaded"); #else print("no_config_no_libgadu"); #endif } timer_add(NULL, "autoaway", 1, 1, ekg_autoaway_timer, NULL); ekg2_reason_changed = 0; /* jesli jest emit: ui-loop (plugin-side) to dajemy mu kontrole, jesli nie * to wywolujemy normalnie sami ekg_loop() w petelce */ if (query_emit(NULL, "ui-loop") != -1) { /* kr imprez */ while (1) g_main_context_iteration(NULL, TRUE); } ekg_exit(); return 0; } /* * ekg_exit() * * wychodzi z klienta sprztajc przy okazji wszystkie sesje, zwalniajc * pami i czyszczc pokj. */ void ekg_exit() { char *exit_exec = config_exit_exec; extern int ekg2_dlclose(void *plugin); int i; msg_queue_write(); xfree(last_search_first_name); xfree(last_search_last_name); xfree(last_search_nickname); xfree(last_search_uid); windows_save(); /* setting windows layout */ if (config_windows_save) { const char *vars[] = { "windows_layout", NULL }; config_write_partly(NULL, vars); } /* setting default session */ if (config_sessions_save && session_current) { const char *vars[] = { "session_default", NULL }; xfree(config_session_default); config_session_default = xstrdup(session_current->uid); config_write_partly(NULL, vars); } for (i = 0; i < SEND_NICKS_MAX; i++) { xfree(send_nicks[i]); send_nicks[i] = NULL; } send_nicks_count = 0; { list_t l; for (l = watches; l; l = l->next) { watch_t *w = l->data; watch_free(w); } } { GSList *pl; for (pl = plugins; pl;) { const plugin_t *p = pl->data; pl = pl->next; if (p->pclass != PLUGIN_UI) continue; p->destroy(); // if (p->dl) ekg2_dlclose(p->dl); } } list_destroy(watches, 0); watches = NULL; if (config_changed && !config_speech_app && config_save_quit == 1) { char line[80]; printf("%s", format_find("config_changed")); fflush(stdout); if (fgets(line, sizeof(line), stdin)) { if (line[xstrlen(line) - 1] == '\n') line[xstrlen(line) - 1] = 0; if (!xstrcasecmp(line, "tak") || !xstrcasecmp(line, "yes") || !xstrcasecmp(line, "t") || !xstrcasecmp(line, "y")) { config_write(); session_write(); metacontact_write(); script_variables_write(); if (!config_commit()) printf(_("Error while saving.\n")); } } else printf("\n"); } else if (config_save_quit == 2) { config_write(); session_write(); metacontact_write(); script_variables_write(); if (!config_commit()) printf(_("Error while saving.\n")); } else if (config_keep_reason && ekg2_reason_changed && config_save_quit == 1) { char line[80]; printf("%s", format_find("quit_keep_reason")); fflush(stdout); if (fgets(line, sizeof(line), stdin)) { if (line[xstrlen(line) - 1] == '\n') line[xstrlen(line) - 1] = 0; if (!xstrcasecmp(line, "tak") || !xstrcasecmp(line, "yes") || !xstrcasecmp(line, "t") || !xstrcasecmp(line, "y")) { session_write(); if (!config_commit()) printf(_("Error while saving.\n")); } } else printf("\n"); } else if (config_keep_reason && ekg2_reason_changed && config_save_quit == 2) { session_write(); if (!config_commit()) printf(_("Error while saving.\n")); } config_exit_exec = NULL; /* avoid freeing it */ /* XXX, think about sequence of unloading. */ sources_destroy(); msgs_queue_destroy(); conferences_destroy(); newconferences_destroy(); metacontacts_destroy(); sessions_free(); { GSList *pl; for (pl = plugins; pl; ) { const plugin_t *p = pl->data; pl = pl->next; p->destroy(); // if (p->dl) ekg2_dlclose(p->dl); } } aliases_destroy(); theme_free(); variables_destroy(); script_variables_free(1); emoticons_destroy(); commands_destroy(); binding_free(); lasts_destroy(); buffer_free(&buffer_debug); buffer_free(&buffer_speech); event_free(); ekg_tls_deinit(); /* free internal read_file() buffer */ read_file(NULL, -1); read_file_utf(NULL, -1); /* windows: */ windows_destroy(); window_status = NULL; window_debug = NULL; window_current = NULL; /* just in case */ /* queries */ { query_t** kk; for (kk = queries; kk < &queries[QUERIES_BUCKETS]; ++kk) { queries_list_destroy(kk); } } registered_queries_free(); xfree(home_dir); xfree(config_dir); xfree(old_config_dir); mesg_set(mesg_startup); #ifdef NO_POSIX_SYSTEM WSACleanup(); #endif close(stderr_backup); if (exit_exec) { #ifndef NO_POSIX_SYSTEM execl("/bin/sh", "sh", "-c", exit_exec, NULL); #else /* XXX, like in cmd_exec() */ #endif /* should we return some error code if exec() failed? * AKA this line shouldn't be ever reached */ } exit(0); } /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/ekg/emoticons.c000066400000000000000000000077141175142753400171000ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2001-2003 Wojtek Kaniewski * Robert J. Wony * Pawe Maziarz * Dawid Jarosz * Piotr Domagalski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #include typedef struct emoticon { struct emoticon *next; char *name; char *value; } emoticon_t; emoticon_t *emoticons = NULL; static LIST_FREE_ITEM(list_emoticon_free, emoticon_t *) { xfree(data->name); xfree(data->value); } DYNSTUFF_LIST_DECLARE(emoticons, emoticon_t, list_emoticon_free, static __DYNSTUFF_LIST_ADD, /* emoticons_add() */ __DYNSTUFF_NOREMOVE, __DYNSTUFF_LIST_DESTROY) /* emoticons_destroy() */ int config_emoticons = 1; /* * emoticon_add() * * dodaje dany emoticon do listy. * * - name - nazwa, * - value - warto. * * 0/-1 */ static int emoticon_add(const char *name, const char *value) { emoticon_t *e, *el; if (!name || !value) return -1; for (el = emoticons; el; el = el->next) { e = el; if (!xstrcasecmp(name, e->name)) { xfree(e->value); e->value = xstrdup(value); return 0; } } e = xmalloc(sizeof(emoticon_t)); e->name = xstrdup(name); e->value = xstrdup(value); emoticons_add(e); return 0; } /* * emoticon_read() * * aduje do listy wszystkie makra z pliku ~/.gg/emoticons * format tego pliku w dokumentacji. * * 0/-1 */ int emoticon_read() { char *buf; GDataInputStream *f; if (!(f = G_DATA_INPUT_STREAM(config_open("emoticons", "r")))) return -1; while ((buf = read_line(f))) { char **emot; if (buf[0] == '#') continue; emot = array_make(buf, "\t", 2, 1, 1); if (g_strv_length(emot) == 2) emoticon_add(emot[0], emot[1]); g_strfreev(emot); } g_object_unref(f); return 0; } /* * emoticon_expand() * * rozwija definicje makr (najczciej bd to emoticony) * * - s - string z makrami. * * zwraca zaalokowany, rozwinity string. */ char *emoticon_expand(const char *s) { emoticon_t *el = NULL; const char *ss; char *ms; size_t n = 0; if (!s) return NULL; for (ss = s; *ss; ss++) { emoticon_t *e = NULL; size_t ns = xstrlen(ss); int ret = 1; for (el = emoticons; el && ret; el = (ret ? el->next : el)) { size_t nn; e = el; nn = xstrlen(e->name); if (ns >= nn) ret = xstrncmp(ss, e->name, nn); } if (el) { e = el; n += xstrlen(e->value); ss += xstrlen(e->name) - 1; } else n++; } ms = xcalloc(1, n + 1); for (ss = s; *ss; ss++) { emoticon_t *e = NULL; size_t ns = xstrlen(ss); int ret = 1; for (el = emoticons; el && ret; el = (ret ? el->next : el)) { size_t n; e = el; n = xstrlen(e->name); if (ns >= n) ret = xstrncmp(ss, e->name, n); } if (el) { e = el; xstrcat(ms, e->value); ss += xstrlen(e->name) - 1; } else ms[xstrlen(ms)] = *ss; } return ms; } /* * emoticon_remove() * * usuwa emoticon o danej nazwie. * * - name. * * 0/-1 */ #if 0 /* never used, never exported, uncomment when needed */ static int emoticon_remove(const char *name) { list_t l; for (l = emoticons; l; l = l->next) { emoticon_t *f = l->data; if (!xstrcasecmp(f->name, name)) { emoticons_remove(f); return 0; } } return -1; } #endif /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/ekg/emoticons.h000066400000000000000000000023431175142753400170760ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2001-2003 Wojtek Kaniewski * Robert J. Wony * Pawe Maziarz * Dawid Jarosz * Piotr Domagalski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __EKG_EMOTICONS_H #define __EKG_EMOTICONS_H #ifdef __cplusplus extern "C" { #endif int emoticon_read(); char *emoticon_expand(const char *s); void emoticons_destroy(); #ifdef __cplusplus } #endif #endif /* __EKG_EMOTICONS_H */ /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/ekg/events.c000066400000000000000000000374471175142753400164120ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2004 Piotr Kupisiewicz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #include #include #include #include event_t *events = NULL; static LIST_ADD_COMPARE(event_add_compare, event_t *) { return data1->id - data2->id; } static LIST_FREE_ITEM(list_event_free, struct event *) { xfree(data->name); xfree(data->action); xfree(data->target); } DYNSTUFF_LIST_DECLARE_SORTED(events, event_t, event_add_compare, list_event_free, static __DYNSTUFF_LIST_ADD_SORTED, /* events_add() */ static __DYNSTUFF_LIST_REMOVE_SAFE, /* events_remove() */ static __DYNSTUFF_LIST_DESTROY) /* events_destroy() */ char **events_all = NULL; int config_display_day_changed = 1; static QUERY(event_protocol_message); static QUERY(event_avail); static QUERY(event_online); static QUERY(event_offline); static QUERY(event_away); static QUERY(event_na); static QUERY(event_descr); static QUERY(event_misc); static void events_add_handler(char *name, void *function); static event_t *event_find(const char *name, const char *target); static event_t *event_find_id(unsigned int id); static int event_remove(unsigned int id, int quiet); static int events_list(int id, int quiet); static int event_target_check(char *buf); static int event_check(const char *session, const char *name, const char *uid, const char *data); /* * on function */ COMMAND(cmd_on) { if (match_arg(params[0], 'a', ("add"), 2)) { int prio; if (!params[1] || !params[2] || !params[3] || !params[4]) { printq("not_enough_params", name); return -1; } if (!(prio = atoi(params[2]))) { printq("invalid_params", name, params[2]); return -1; } if (event_add(params[1], prio, params[3], params[4], quiet)) return -1; config_changed = 1; return 0; } if (match_arg(params[0], 'd', ("del"), 2)) { int par; if (!params[1]) { printq("not_enough_params", name); return -1; } if (!xstrcmp(params[1], ("*"))) par = 0; else { if (!(par = atoi(params[1]))) { printq("invalid_params", name, params[1]); return -1; } } if (!event_remove(par, quiet)) { config_changed = 1; return 0; } else return -1; } if (!params[0] || match_arg(params[0], 'l', ("list"), 2) || params[0][0] != '-') { events_list((params[0] && params[1] && atoi(params[1])) ? atoi(params[1]) : 0, 0); return 0; } printq("invalid_params", name, params[0]); return -1; } /* * event_add () * * adds event to the events list * * it finds id in the same way as in window_new() * * 0/-1 */ int event_add(const char *name, int prio, const char *target, const char *action, int quiet) { event_t *ev; char *tmp; int done = 0, id = 1; if (event_find(name, target)) { printq("events_exist", name, target); return -1; } while (!done) { done = 1; for (ev = events; ev; ev = ev->next) { if (ev->id == id) { done = 0; id++; break; } } } ev = xmalloc(sizeof(event_t)); ev->id = id; ev->name = xstrdup(name); ev->prio = prio; ev->target = xstrdup(target); ev->action = xstrdup(action); events_add(ev); tmp = xstrdup(name); query_emit(NULL, "event-added", &tmp); xfree(tmp); printq("events_add", name); if (!array_contains(events_all, name, 0)) { query_t *q; debug("event_add, array_contains(events_all, \"%s\", 0) failed. Binding new query: %s\n", name, name); q = query_connect(NULL, name, event_misc, NULL); q->data = (char*)q->name; /* GiM: does this even make a sense? */ /*(char *) query_name(q->id);*/ /* hack */ /* maybe: q->data = ev->name ? */ array_add(&events_all, (char *) q->data); /* note: after query_external_free() this won't be accessible */ /* luckily, we call event_free() before query_external_free() */ } return 0; } /* * event_remove () * * it removes event from events * * if (id == 0 ) it removes whole list * * 0/-1 */ static int event_remove(unsigned int id, int quiet) { event_t *ev; if (id == 0) { event_free(); printq("events_del_all"); goto cleanup; } if (!(ev = event_find_id(id))) { printq("events_del_noexist", ekg_itoa(id)); return -1; } events_remove(ev); printq("events_del", ekg_itoa(id)); cleanup: /* query_emit(NULL, "event-removed", ekg_itoa(id)); */ /* XXX, incorrect. */ return 0; } /* * event_free () * * it frees whole list */ void event_free() { xfree(events_all); events_all = NULL; events_destroy(); } /* * events_list () * * it shows the list of events */ static int events_list(int id, int quiet) { event_t *ev; if (!events) { printq("events_list_empty"); return 0; } printq("events_list_header"); for (ev = events; ev; ev = ev->next) { if (!id || id == ev->id) printq("events_list", ev->name, ekg_itoa(ev->prio), ev->target, ev->action, ekg_itoa(ev->id)); } return 0; } /* * event_find () * * it finds the event and return (if found) descriptor * to event * */ event_t *event_find(const char *name, const char *target) { event_t *ev, *ev_max = NULL; int ev_max_prio = 0; char **b, **c; debug("// event_find (name (%s), target (%s)\n", name, target); b = array_make(target, ("|,;"), 0, 1, 0); c = array_make(name, ("|,;"), 0, 1, 0); for (ev = events; ev; ev = ev->next) { char **a, **d; int i, j, k, m; a = array_make(ev->target, ("|,;"), 0, 1, 0); d = array_make(ev->name, ("|,;"), 0, 1, 0); for (i = 0; a[i]; i++) { for (j = 0; b[j]; j++) { for (k = 0; c[k]; k++) { for (m = 0; d[m]; m++) { if (xstrcasecmp(d[m], c[k]) || xstrcasecmp(a[i], b[j])) continue; else if (ev->prio > ev_max_prio){ ev_max = ev; ev_max_prio = ev->prio; } } } } } g_strfreev(a); g_strfreev(d); } g_strfreev(b); g_strfreev(c); return (ev_max) ? ev_max : NULL; } /* * event_find_all () * * it finds the event including possibility of * and return (if found) * descriptor to event * */ static event_t *event_find_all(const char *name, const char *session, const char *uid, const char *target, const char *data) { event_t *ev, *ev_max = NULL; int ev_max_prio = 0; char **b, **c; // debug("// event_find_all (session %s) (name (%s), target (%s)\n", session, name, target); b = array_make(target, ("|,;"), 0, 1, 0); c = array_make(name, ("|,;"), 0, 1, 0); for (ev = events; ev; ev = ev->next) { char **a, **d; int i, j, k, m; a = array_make(ev->target, ("|,;"), 0, 1, 0); d = array_make(ev->name, ("|,;"), 0, 1, 0); for (i = 0; a[i]; i++) { for (j = 0; b[j]; j++) { for (k = 0; c[k]; k++) { for (m = 0; d[m]; m++) { char *tmp = format_string(a[i], uid, target, data, session); if ((xstrcasecmp(d[m], c[k]) && xstrcasecmp(d[m], ("*"))) || (!event_target_check(tmp) && xstrcasecmp(a[i], ("*")) && xstrcasecmp(a[i], b[j]))) { xfree(tmp); continue; } else if (ev->prio > ev_max_prio){ ev_max = ev; ev_max_prio = ev->prio; } xfree(tmp); } } } } g_strfreev(a); g_strfreev(d); } g_strfreev(b); g_strfreev(c); return (ev_max) ? ev_max : NULL; } /* * event_find () * * it finds the event (by the id) and return (if found) * descriptor to event * */ static event_t *event_find_id(unsigned int id) { event_t *ew; for (ew = events; ew; ew = ew->next) { if (ew->id != id) continue; else return ew; } return 0; } static void events_add_handler(char *name, void *function) { query_connect(NULL, name, function, NULL); array_add(&events_all, name); } static TIMER(ekg_day_timer) { static struct tm old = {.tm_mday = 0}; static struct tm *oldtm = &old; struct tm *tm; time_t now = time(NULL); if (type) return 0; tm = localtime(&now); if ((old.tm_mday == tm->tm_mday) || !config_display_day_changed) return 0; if (old.tm_mday) { window_t *w; char *ts = g_strdup(timestamp("%d %b %Y")); for (w = windows; w; w = w->next) { if (!w || w->id == WINDOW_DEBUG_ID || w->floating) continue; /* skip __debug && (floatings windows [__lastlog, __contacts, ...]) */ w->lock++; /* lock window */ print_window_w(w, EKG_WINACT_NONE, "day_changed", ts); w->lock--; /* unlock window */ } g_free(ts); query_emit(NULL, "ui-window-refresh"); debug("[EKG2] day changed to %.2d.%.2d.%.4d\n", tm->tm_mday, tm->tm_mon+1, tm->tm_year+1900); query_emit(NULL, "day-changed", &tm, &oldtm); old.tm_mday = 0; } else memcpy(&old, tm, sizeof(struct tm)); return 0; } /* * events_init () * * initializing of events and its handlers */ int events_init() { timer_add(NULL, "daytimer", 1, 1, ekg_day_timer, NULL); events_add_handler(("protocol-message"), event_protocol_message); events_add_handler(("event-avail"), event_avail); events_add_handler(("event-away"), event_away); events_add_handler(("event-na"), event_na); events_add_handler(("event-online"), event_online); events_add_handler(("event-offline"), event_offline); events_add_handler(("event-descr"), event_descr); return 0; } /* * event_protocol_message() * * handler for protocol-message */ static QUERY(event_protocol_message) { char *session = *(va_arg(ap, char**)); char *uid = *(va_arg(ap, char**)); char ***UNUSED(rcpts) = va_arg(ap, char***); char *text = *(va_arg(ap, char**)); event_check(session, "protocol-message", uid, text); return 0; } /* * event_avail () * * handler for changing status on available */ static QUERY(event_avail) { char *session = *(va_arg(ap, char**)); char *uid = *(va_arg(ap, char**)); event_check(session, "event-avail", uid, NULL); return 0; } /* * event_away () * * handler for changing status on away */ static QUERY(event_away) { char *session = *(va_arg(ap, char**)); char *uid = *(va_arg(ap, char**)); event_check(session, "event-away", uid, NULL); return 0; } /* * event_na () * * handler for changing status on NA */ static QUERY(event_na) { char *session = *(va_arg(ap, char**)); char *uid = *(va_arg(ap, char**)); event_check(session, "event-na", uid, NULL); return 0; } /* * event_online () * * handler for changing status from NA to avail */ static QUERY(event_online) { char *session = *(va_arg(ap, char**)); char *uid = *(va_arg(ap, char**)); event_check(session, "event-online", uid, NULL); return 0; } /* * event_offline () * * handler for changing status from avail to NA */ static QUERY(event_offline) { char *session = *(va_arg(ap, char**)); char *uid = *(va_arg(ap, char**)); event_check(session, "event-offline", uid, NULL); return 0; } /* * event_descr () * * handler for changing description */ static QUERY(event_descr) { char *session = *(va_arg(ap, char**)); char *uid = *(va_arg(ap, char**)); char *descr = *(va_arg(ap, char**)); event_check(session, "event-descr", uid, descr); return 0; } static QUERY(event_misc) { event_check(NULL, data, "*", NULL); return 0; } /* event_check () * * it looks if the given event has a handler * if yes it runs it * it also check target and if possible uid taken from target * */ static int event_check(const char *session, const char *name, const char *uid, const char *data) { session_t *__session; userlist_t *userlist; event_t *ev; const char *target; char *action, **actions; char *edata = NULL; int i; if (!events) return 1; if (!(__session = session_find(session))) __session = session_current; if (uid && ignored_check(__session, uid) & IGNORE_EVENTS) { return -1; } userlist = userlist_find(__session, uid); target = (userlist && userlist->nickname) ? userlist->nickname : uid; if (!(ev = event_find_all(name, session, uid, target, data))) return -1; action = ev->action; if (!action) return -1; if (data) { int size = 1; const char *p; char *q; for (p = data; *p; p++) { if (strchr("`!#$&*?|\\\'\"{}[]<>()\n\r", *p)) size += 2; else size++; } edata = xmalloc(size); for (p = data, q = edata; *p; p++, q++) { if (strchr("`!#$&*?|\\\'\"{}[]<>()", *p)) *q++ = '\\'; if (*p == '\n') { *q++ = '\\'; *q = 'n'; continue; } if (*p == '\r') { *q++ = '\\'; *q = 'r'; continue; } *q = *p; } *q = 0; } actions = array_make(action, (";"), 0, 0, 1); for (i = 0; actions && actions[i]; i++) { char *tmp = format_string(strip_spaces(actions[i]), (uid) ? uid : target, target, ((data) ? data : ""), ((edata) ? edata : ""), session_uid_get(__session)); debug("// event_check() calling \"%s\"\n", tmp); command_exec(NULL, NULL, tmp, 0); /* BUG? CHECK: hm, we've got specified session, not current one... target too.. so is it correct ? */ xfree(tmp); } g_strfreev(actions); xfree(edata); return 0; } /* * event_target_check_compare() * * it only compares given as an argument values * * returns logical value */ static int event_target_check_compare(char *buf) { string_t s; s = string_init(NULL); while(*buf) { if (*buf == '/') { buf++; if (!*buf) break; buf++; if (!*buf) break; continue; } if (*buf == '=') { buf++; if (!*buf) break; if (*buf == '=') /* '==' */ { buf++; if (!*buf) break; return (!xstrcmp(s->str, buf) && !string_free(s, 1)) ? 1 : 0; } /* '=' */ return (!xstrcasecmp(s->str, buf) && !string_free(s, 1)) ? 1 : 0; } if (*buf == '!') { buf++; if (!*buf) break; if (*buf == '=') { buf++; if (!*buf) break; if (*buf == '=') { /* '!==' */ buf++; if (!*buf) break; return (xstrcmp(s->str, buf) && !string_free(s, 1)) ? 1 : 0; } /* '!=' */ return (xstrcasecmp(s->str, buf) && !string_free(s, 1)) ? 1 : 0; } if (*buf == '+') { buf++; if (!*buf) break; if (*buf == '+') { /* '!++' */ buf++; if (!*buf) break; return (!xstrstr(buf, s->str) && !string_free(s, 1)) ? 1 : 0; } /* '!+' */ return (!xstrcasestr(buf, s->str) && !string_free(s, 1)) ? 1 : 0; } continue; } if (*buf == '+') { buf++; if (!*buf) break; if (*buf == '+') { /* '++' */ buf++; if (!*buf) break; return (xstrstr(buf, s->str) && !string_free(s, 1)) ? 1 : 0; } /* '+' */ return (xstrcasestr(buf, s->str) && !string_free(s, 1)) ? 1 : 0; } string_append_c(s, *buf); buf++; } string_free(s, 1); return 0; } /* * event_target_check() * * it has to get as an argument parametr to check * * returns logical value of given expression */ static int event_target_check(char *buf) { char **params = array_make(buf, ("&|"), 0, 1, 1); int i = 1; char *separators; char last_returned = 0; int first = 1; #define s separators[i] separators = xmalloc(g_strv_length(params) * sizeof(char) + 1); while (*buf) { if (*buf == '&' || *buf == '|') { s = *buf; i++; } buf++; } for (i = 0; params[i]; i++) { int returned_now; returned_now = event_target_check_compare(params[i]); if (s && s == '&') { if (returned_now && last_returned) last_returned = 1; else last_returned = 0; } else if (s && s == '|') { if (returned_now || last_returned) last_returned = 1; else last_returned = 0; } if (first) { last_returned = returned_now; first = 0; } } #undef s xfree(separators); g_strfreev(params); return last_returned; } /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/ekg/events.h000066400000000000000000000027651175142753400164120ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2004 Piotr Kupisiewicz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __EKG_EVENTS_H #define __EKG_EVENTS_H #include "dynstuff.h" #include "plugins.h" #include "stuff.h" #ifdef __cplusplus extern "C" { #endif typedef struct event { struct event *next; unsigned int id;/* identyficator */ char *name; /* name of the event */ char *target; /* uid(s), alias(es), group(s) */ char *action; /* action to do */ int prio; /* priority of this event */ } event_t; extern event_t *events; extern char **events_all; /* it may be help for tab complete */ int event_add(const char *name, int prio, const char *target, const char *action, int quiet); void event_free(); int events_init(); #ifdef __cplusplus } #endif #endif /* __EKG_EVENTS_H */ /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/ekg/internal.h000066400000000000000000000026731175142753400167200ustar00rootroot00000000000000/* * Internal functions * * (C) Copyright 2011 EKG2 team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __EKG_INTERN_H #define __EKG_INTERN_H /* commands.c */ G_GNUC_INTERNAL TIMER(timer_handle_at); G_GNUC_INTERNAL TIMER(timer_handle_command); /* connections.c */ G_GNUC_INTERNAL void ekg_tls_init(void); G_GNUC_INTERNAL void ekg_tls_deinit(void); /* legacyconfig.c */ G_GNUC_INTERNAL void config_upgrade(void); /* plugins.c */ G_GNUC_INTERNAL void ekg2_dlinit(const gchar *argv0); /* sources.c */ G_GNUC_INTERNAL void sources_destroy(void); G_GNUC_INTERNAL gint timer_remove_user(gint (*handler)(gint, gpointer)); G_GNUC_INTERNAL gint ekg_children_print(gint quiet); G_GNUC_INTERNAL COMMAND(cmd_debug_timers); G_GNUC_INTERNAL COMMAND(cmd_at); G_GNUC_INTERNAL COMMAND(cmd_timer); G_GNUC_INTERNAL void timers_write(GOutputStream *f); #endif ekg2-0.4~pre+20120506.1/ekg/legacyconfig.c000066400000000000000000000117661175142753400175340ustar00rootroot00000000000000/* * (C) Copyright 2007 EKG2 authors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include "internal.h" /** * config_upgrade() * * Check current configuration file version and upgrade it if needed. Print additional info about changes. * * @todo Instead of hardcoded dates, use strftime() because for instance in USA we have got month and date reversed. */ void config_upgrade() { const int current_config_version = 12; if (config_version == -1) config_version = current_config_version; if (config_version >= current_config_version) return; print("config_upgrade_begin"); switch (config_version+1) { /* versions MUST be sorted, break MUST NOT be used */ case 0: /* jabber SASL behavior change */ print("config_upgrade_major", _("We've started using XMPP SASL AUTH by default, so if you're unable to connect to your favorite jabber server," \ "please send us debug info and try to set (within appropriate session):\n" "/session disable_sasl 2"), "2007-03-24"); case 1: /* display_ack values change */ print("config_upgrade_minor", _("Variable display_ack's values have been changed. " \ "An update is done to your settings, but please check the new values."), "2007-03-24"); switch (config_display_ack) { case 1: config_display_ack = 31; break; case 2: config_display_ack = 1; break; case 3: config_display_ack = 2; break; } config_changed = 1; case 2: /* allow_autoresponder session var */ print("config_upgrade_minor", _("'allow_autoresponder' session variables have been replaced by 'allowed_sessions' plugin variable. " \ "The same way future plugins will be enabled."), "2007-04-06"); case 3: print("config_upgrade_minor", _("'logs:away_log' plugin variable have been replaced by 'away_log' irc session variable. " \ "Also away_log_* formats have been changed to irc_awaylog_* formats. Enjoy"), "2007-07-06"); case 4: #if 0 print("config_upgrade_major", _("Jabber UIDs prefix has been changed from 'jid:' to 'xmpp:'. Your session UIDs were changed " \ "automagically, and in other areas old prefix will be still supported for some time."), "2007-10-16"); #endif case 5: print("config_upgrade_minor", _("'ping-server' jabber session variable has been renamed to 'ping_server', please set it by hand " \ "where needed. Sorry for that inconvenience."), "2007-08-24"); case 6: print("config_upgrade_major", _("sqlite only logs messages and status-changes when in log_formats session variable you have 'sqlite' " \ "Your config couldn't be updated automagically, so you must set it by hand."), "2008-08-06"); case 7: print("config_upgrade_minor", _("display_pl_chars option is no longer maintained, use /set console_charset US-ASCII"), "2009-04-24"); case 8: print("config_upgrade_minor", _("Jabber variables have been changed from 'jabber:' to 'xmpp:'. " \ "Your config couldn't be updated automagically, so you must set it by hand."), "2010-08-17"); case 9: print("config_upgrade_minor", _("Default config_completion_char is \":\""), "2010-12-11"); if (!config_completion_char) config_completion_char = xstrdup(":"); case 10: print("config_upgrade_minor", _("irc:experimental_chan_name_clean is no longer maintained, use /set irc:clean_channel_name\n" \ "gg:disable_chatstates is no longer maintained, use /set gg:enable_chatstates"), "2011-02-06"); case 11: print("config_upgrade_major", _("console_coding variable has been removed. If you need to adjust the locale ekg2 uses, " \ "please use appropriate system interface (i.e. LC_ALL). The raw & XML logs of logs plugin " \ "and SQLite logs of logsqlite plugin always write utf8 now. If you used non-utf8 locale, " \ "you might want to convert the logfiles/database using iconv."), "2011-03-01"); case 12: { gchar *fs = g_strdup_printf( _("The configuration files were moved to XDG_CONFIG_DIR (%s/ekg2). " \ "Old EKG2 version will no longer be able to read configs written by this new version, " \ "and it won't reflect changes done in %s. Please notice, though, that EKG2 still " \ "keeps logs in old location, so if you're willing to remove it, do so carefully."), g_get_user_config_dir(), config_dir); print("config_upgrade_major", fs, "2011-03-08"); g_free(fs); } } config_version = current_config_version; if (!config_auto_save) print("config_upgrade_end"); } ekg2-0.4~pre+20120506.1/ekg/log.c000066400000000000000000000111761175142753400156560ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2001-2003 Wojtek Kaniewski * Robert J. Wony * Pawe Maziarz * Dawid Jarosz * Piotr Domagalski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #include struct last *lasts = NULL; int config_last_size = 10; int config_last = 0; static LIST_FREE_ITEM(list_last_free, struct last *) { xfree(data->uid); xfree(data->message); } DYNSTUFF_LIST_DECLARE_WC(lasts, struct last, list_last_free, static __DYNSTUFF_LIST_ADD, /* lasts_add() */ static __DYNSTUFF_LIST_REMOVE_ITER, /* lasts_removei() */ __DYNSTUFF_LIST_DESTROY, /* lasts_destroy() */ static __DYNSTUFF_LIST_COUNT) /* lasts_count() */ /* * last_add() * * dodaje wiadomo do listy ostatnio otrzymanych. * * - type - rodzaj wiadomoci, * - uid - nadawca, * - t - czas, * - st - czas nadania, * - msg - tre wiadomoci. */ void last_add(int type, const char *uid, time_t t, time_t st, const char *msg) { struct last *ll; int count = 0; /* nic nie zapisujemy, jeeli user sam nie wie czego chce. */ if (config_last_size <= 0) return; if (config_last & 2) count = last_count(uid); else count = lasts_count(); /* usuwamy ostatni wiadomo, w razie potrzeby... */ if (count >= config_last_size) { time_t tmp_time = 0; /* najpierw j znajdziemy... */ for (ll = lasts; ll; ll = ll->next) { if (config_last & 2 && xstrcasecmp(ll->uid, uid)) continue; if (!tmp_time) tmp_time = ll->time; if (ll->time <= tmp_time) tmp_time = ll->time; } /* ...by teraz usun */ for (ll = lasts; ll; ll = ll->next) { if (ll->time == tmp_time && !xstrcasecmp(ll->uid, uid)) { (void) lasts_removei(ll); break; } } } ll = xmalloc(sizeof(struct last)); ll->type = type; ll->uid = xstrdup(uid); ll->time = t; ll->sent_time = st; ll->message = xstrdup(msg); lasts_add(ll); } /* * last_del() * * usuwa wiadomoci skojarzone z dan osob. * * - uin - numerek osoby. */ void last_del(const char *uid) { struct last *ll; for (ll = lasts; ll; ll = ll->next) { if (!xstrcasecmp(uid, ll->uid)) ll = lasts_removei(ll); } } /* * last_count() * * zwraca ilo wiadomoci w last dla danej osoby. * * - uin. */ int last_count(const char *uid) { int count = 0; struct last *ll; for (ll = lasts; ll; ll = ll->next) { if (!xstrcasecmp(uid, ll->uid)) count++; } return count; } /* * It's wrong, I think we could create one function which cover this two function. * But I don't have any idea howto * * However, that ent[] table, like someone already said, was waste of space. * * Code looks better. (So please don't change to somethink like: if (!ent) q++;) * * I was thinking about some static pointer, * static char buf[2]; * * buf[0] = znak; * return buf; * * to remove if (ent) and always use strcpy() however it's still only idea. */ static int xml_escape_l(const char znak) { switch (znak) { case '"': return (sizeof(""")-1); case '&': return (sizeof("&")-1); case '\'': return (sizeof("'")-1); case '<': return (sizeof("<")-1); case '>': return (sizeof(">")-1); } return 1; } static const char *xml_escape_c(const char znak) { switch (znak) { case '"': return """; case '&': return "&"; case '\'': return "'"; case '<': return "<"; case '>': return ">"; } return NULL; } /* * xml_escape() * * escapes text to be xml-compliant * * - text * * allocated buffer */ char *xml_escape(const char *text) { const char *p; char *res, *q; int len; if (!text) return NULL; for (p = text, len = 0; *p; p++) len += xml_escape_l(*p); q = res = xmalloc((len + 1)*sizeof(char)); for (p = text; *p; p++) { const char *ent = xml_escape_c(*p); if (ent) xstrcpy(q, ent); else *q = *p; q += xml_escape_l(*p); } return res; } /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/ekg/log.h000066400000000000000000000033471175142753400156640ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2001-2003 Wojtek Kaniewski * Robert J. Wony * Pawe Maziarz * Dawid Jarosz * Piotr Domagalski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __EKG_LOG_H #define __EKG_LOG_H #include #include #include "dynstuff.h" #ifdef __cplusplus extern "C" { #endif struct last { struct last *next; unsigned int type : 1; /* 0 - przychodzca, 1 - wychodzca */ char *uid; /* od kogo, lub do kogo przy wysyanych */ time_t time; /* czas */ time_t sent_time; /* czas wysania wiadomoci przychodzcej */ char *message; /* wiadomo */ }; #ifndef EKG2_WIN32_NOFUNCTION extern struct last *lasts; void last_add(int type, const char *uid, time_t t, time_t st, const char *msg); void last_del(const char *uid); int last_count(const char *uid); void lasts_destroy(); char *xml_escape(const char *text); #endif #ifdef __cplusplus } #endif #endif /* __EKG_LOG_H */ /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/ekg/metacontacts.c000066400000000000000000000323351175142753400175620ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2004 Piotr Kupisiewicz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #include #include #include #include "metacontacts.h" metacontact_t *metacontacts = NULL; /* metacontacts_items: */ static LIST_ADD_COMPARE(metacontact_add_item_compare, metacontact_item_t *) { if (!xstrcasecmp(data1->s_uid, data2->s_uid)) return xstrcasecmp(data1->name, data2->name); return xstrcasecmp(session_alias_uid_n(data1->s_uid), session_alias_uid_n(data2->s_uid)); } static LIST_FREE_ITEM(metacontact_item_free, metacontact_item_t *) { xfree(data->name); xfree(data->s_uid); } DYNSTUFF_LIST_DECLARE_SORTED(metacontact_items, metacontact_item_t, metacontact_add_item_compare, metacontact_item_free, static __DYNSTUFF_ADD_SORTED, /* metacontact_items_add() */ static __DYNSTUFF_REMOVE_SAFE, /* metacontact_items_remove() */ /* maybe removei() ? */ static __DYNSTUFF_DESTROY) /* metacontact_items_destroy() */ /* metacontacts: */ static LIST_ADD_COMPARE(metacontact_add_compare, metacontact_t *) { return xstrcasecmp(data1->name, data2->name); } static LIST_FREE_ITEM(metacontact_list_free, metacontact_t *) { metacontact_items_destroy(&(data->metacontact_items)); xfree(data->name); } DYNSTUFF_LIST_DECLARE_SORTED(metacontacts, metacontact_t, metacontact_add_compare, metacontact_list_free, static __DYNSTUFF_LIST_ADD_SORTED, /* metacontacts_add() */ static __DYNSTUFF_LIST_REMOVE_SAFE, /* metacontacts_remove() */ /* maybe removei() ? */ __DYNSTUFF_LIST_DESTROY) /* metacontacts_destroy() */ static int metacontact_add_item(metacontact_t *m, const char *session, const char *name, unsigned int prio, int quiet); static int metacontact_remove_item(metacontact_t *m, const char *session, const char *name, int quiet); static int metacontact_remove(const char *name); /* * metacontact function support */ COMMAND(cmd_metacontact) { metacontact_t *m; if (!params[0] || match_arg(params[0], 'l', ("list"), 2)) { metacontact_t *m; for (m = metacontacts; m; m = m->next) printq("metacontact_list", m->name); if (!metacontacts) printq("metacontact_list_empty"); return 0; } if (match_arg(params[0], 'a', ("add"), 2)) { if (!params[1]) { printq("not_enough_params", name); return -1; } if (metacontact_find(params[1])) { printq("metacontact_exists", params[1]); return -1; } if (metacontact_add(params[1])) { char *tmp = xstrdup(params[1]); config_changed = 1; printq("metacontact_added", params[1]); query_emit(NULL, "metacontact-added", &tmp); xfree(tmp); } return 0; } if (match_arg(params[0], 'd', ("del"), 2)) { if (!params[1]) { printq("not_enough_params", name); return -1; } if (!metacontact_find(params[1])) { printq("metacontact_doesnt_exist", params[1]); return -1; } if (metacontact_remove(params[1])) { char *tmp = xstrdup(params[1]); config_changed = 1; printq("metacontact_removed", params[1]); query_emit(NULL, "metacontact-removed", &tmp); xfree(tmp); } return 0; } if (match_arg(params[0], 'i', ("add-item"), 2)) { if (!params[1] || !params[2] || !params[3] || !params[4]) { printq("not_enough_params", name); return -1; } if (!(m = metacontact_find(params[1]))) { printq("metacontact_doesnt_exist", params[1]); return -1; } if (metacontact_add_item(m, params[2], params[3], atoi(params[4]), 0)) { char *tmp1 = xstrdup(params[2]), *tmp2 = xstrdup(params[3]), *tmp3 = xstrdup(params[1]); printq("metacontact_added_item", session_alias_uid_n(params[2]), params[3], params[1]); query_emit(NULL, "metacontact-item-added", &tmp1, &tmp2, &tmp3); xfree(tmp1); xfree(tmp2); xfree(tmp3); } return 0; } if (match_arg(params[0], 'r', ("del-item"), 2)) { if (!params[1] || !params[2] || !params[3]) { printq("not_enough_params", name); return -1; } if (!(m = metacontact_find(params[1]))) { printq("metacontact_doesnt_exist", params[1]); return -1; } if (metacontact_remove_item(m, params[2], params[3], 0)) { char *tmp1 = xstrdup(params[2]), *tmp2 = xstrdup(params[3]), *tmp3 = xstrdup(params[1]); printq("metacontact_removed_item", session_alias_uid_n(params[2]), params[3], params[1]); query_emit(NULL, "metacontact-item-removed", &tmp1, &tmp2, &tmp3); xfree(tmp1); xfree(tmp2); xfree(tmp3); } return 0; } if (params[0] && (m = metacontact_find(params[0]))) { metacontact_item_t *i = m->metacontact_items; if (!i) { printq("metacontact_item_list_empty"); return 0; } printq("metacontact_item_list_header", params[0]); for (; i; i = i->next) { userlist_t *u = userlist_find_n(i->s_uid, i->name); char *tmp; if (!u) tmp = format_string(format_find("metacontact_info_unknown")); else tmp = format_string(format_find(ekg_status_label(u->status, u->descr, "metacontact_info_")), get_user_name(u), u->descr); printq("metacontact_item_list", session_alias_uid_n(i->s_uid), i->name, tmp, ekg_itoa(i->prio)); xfree(tmp); } printq("metacontact_item_list_footer", params[0]); return 0; } if (params[0] && !params[1]) { printq("metacontact_doesnt_exist", params[0]); return -1; } printq("invalid_params", name, params[0]); return -1; } /* * metacontact_find() * * it looks for metacontact with given name * it returns pointer to this metacontact */ metacontact_t *metacontact_find(const char *name) { metacontact_t *m; for (m = metacontacts; m; m = m->next) { if (!xstrcasecmp(name, m->name)) return m; } return NULL; } /* * metacontact_add() * * it adds metacontact to the list * returns pointer to added metacontact */ metacontact_t *metacontact_add(const char *name) { metacontact_t *m; if (!name) return NULL; m = xmalloc(sizeof(metacontact_t)); m->name = xstrdup(name); metacontacts_add(m); return m; } /* * metacontact_find_item() * * it looks for metacontact item * returns pointer to this item */ static metacontact_item_t *metacontact_find_item(metacontact_t *m, const char *name, const char *uid) { metacontact_item_t *i; if (!m) return NULL; for (i = m->metacontact_items; i; i = i->next) { if (!xstrcasecmp(name, i->name) && !xstrcasecmp(uid, i->s_uid)) return i; } return NULL; } /* * metacontact_add_item() * * it adds item to the metacontact * * m - metacontact * session - session UID (!) * name - name of the contact in userlist * prio - prio * quiet - be quiet ? */ static int metacontact_add_item(metacontact_t *m, const char *session, const char *name, unsigned int prio, int quiet) { metacontact_item_t *i; session_t *s; const char *uid; if (!m || !name || !session) { debug("! metacontact_add_item: NULL data on input\n"); return 0; } if (!(s = session_find(session))) { printq("session_doesnt_exist", session); return 0; } /* XXX, bad session */ if (!(uid = get_uid(s, name))) { printq("user_not_found", name); debug("! metacontact_add_item: UID is not on our contact lists: %s\n", name); return 0; } i = xmalloc(sizeof(metacontact_item_t)); i->name = xstrdup(uid); i->s_uid = xstrdup(s->uid); i->prio = prio; metacontact_items_add(&m->metacontact_items, i); return 1; } /* * metacotact_remove_item() * * it removes item from a list * returns 1 if success, if errors found 0 is returned */ static int metacontact_remove_item(metacontact_t *m, const char *session, const char *name, int quiet) { metacontact_item_t *i; session_t *s; if (!m || !name || !session) { debug("! metacontact_add_item: NULL data on input\n"); return 0; } if (!(s = session_find(session))) { printq("session_doesnt_exist", session); return 0; } if (!(i = metacontact_find_item(m, name, s->uid))) { printq("metacontact_item_doesnt_exist", session, name); return 0; } metacontact_items_remove(&m->metacontact_items, i); return 1; } /* * metacontact_remove() * * it removes metacontact from a list * returns 0 if errors, 1 if successed */ static int metacontact_remove(const char *name) { metacontact_t *m = metacontact_find(name); metacontact_item_t *i; for (i = m->metacontact_items; i; ) { metacontact_item_t *next = i->next; metacontact_remove_item(m, i->s_uid, i->name, 1); i = next; } metacontacts_remove(m); return 1; } /* * metacontact_session_renamed_handler() * * when session alias is changed it had to be resorted in metacontact items * list */ static int metacontact_session_renamed_handler(void *data, va_list ap) { /* We don't change alias so frequently, * so we can resort whole list, not only items w/ that session */ /* XXX: exactly, why we do it? we don't even look at session alias * in sorting function */ /* Hint: session_alias_uid_n() */ LIST_RESORT2(&metacontacts, metacontact_add_item_compare); #if 0 char **tmp = va_arg(ap, char**); session_t *s = session_find(*tmp); char *session; metacontact_t *m; if (!s) return 0; session = s->uid; for (m = metacontacts; m; m = m->next) { metacontact_item_t *i; for (i = m->metacontact_items; i;) { metacontact_item_t *next = i->next; if (!xstrcasecmp(session, i->s_uid)) { char *s_uid, *name; int prio; s_uid = xstrdup(i->s_uid); name = xstrdup(i->name); prio = i->prio; metacontact_remove_item(m, s_uid, name, 1); metacontact_add_item(m, s_uid, name, prio, 1); xfree(s_uid); xfree(name); /* list has changed, so we need to start from beginning */ m = metacontacts; break; } i = next; } } #endif return 1; } /* * metacontact_userlist_removed_handler() * * when some user from userlist is removed we have to remove it * also from metacontact */ static int metacontact_userlist_removed_handler(void *data, va_list ap) { /* XXX: disabled it to allow jabber metacontacts * but think about it */ #if 0 char **name = va_arg(ap, char**); metacontact_t *m; for (m = metacontacts; m; m = m->next) { metacontact_item_t *i; for (i = m->metacontact_items; i; i = i->next) { userlist_t *u; if (!i) break; u = userlist_find_n(i->s_uid, i->name); if (!u) continue; if (u->nickname && !xstrcasecmp(*name, u->nickname)) { metacontact_remove_item(m, i->s_uid, i->name, 1); break; } if (!xstrcasecmp(*name, u->uid)) { metacontact_remove_item(m, i->s_uid, i->name, 1); break; } } } #endif return 0; } /* * metacontact_find_prio() * * it finds and return metacontact_item with the 'most available' * status; if more than uids have the same one, it also looks * at the priority */ metacontact_item_t *metacontact_find_prio(metacontact_t *m) { metacontact_item_t *i; metacontact_item_t *ret = NULL; userlist_t *last = NULL; if (!m) return NULL; for (i = m->metacontact_items; i; i = i->next) { userlist_t *u = userlist_find_n(i->s_uid, i->name); if (!u) continue; if (!ret) { ret = i; last = u; continue; } if (u->status > last->status || (u->status == last->status && i->prio > ret->prio)) { ret = i; last = u; continue; } } return ret; } /* * metacontact_init() * * initialization of the metacontacts */ void metacontact_init() { query_connect(NULL, "session-renamed", metacontact_session_renamed_handler, NULL); query_connect(NULL, "userlist-removed", metacontact_userlist_removed_handler, NULL); } /* * metacontact_write() * * it writes info about metacontacts to file */ void metacontact_write() { metacontact_t *m; GOutputStream *f = NULL; f = G_OUTPUT_STREAM(config_open("metacontacts", "w")); if (!f) return; for (m = metacontacts; m; m = m->next) { metacontact_item_t *i; ekg_fprintf(f, "[%s]\n", m->name); for (i = m->metacontact_items; i; i = i->next) ekg_fprintf(f, "%s %s %d\n", i->s_uid, i->name, i->prio); } } /* * metacontact_read() * * it reads info about metacontacts from file */ int metacontact_read() { char *line; GDataInputStream *f; metacontact_t *m = NULL; if (!(f = G_DATA_INPUT_STREAM(config_open("metacontacts", "r")))) return -1; while ((line = read_line(f))) { char *tmp; char **array = NULL; if (line[0] == '#' || line[0] == ';' || (line[0] == '/' && line[1] == '/')) continue; if (line[0] == '[') { tmp = xstrchr(line, ']'); if (!tmp) continue; *tmp = 0; m = metacontact_add(line + 1); continue; } array = array_make(line, " ", 3, 1, 1); if (g_strv_length(array) != 3) { debug(" metacontact_read: wrong number of forms\n"); goto next; } metacontact_add_item(m, array[0], array[1], atoi(array[2]), 1); next: g_strfreev(array); } g_object_unref(f); return 0; } /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/ekg/metacontacts.h000066400000000000000000000032641175142753400175660ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2004 Piotr Kupisiewicz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __EKG_METACONTACTS_H #define __EKG_METACONTACTS_H #include "dynstuff.h" #ifdef __cplusplus extern "C" { #endif typedef struct metacontact_item { struct metacontact_item *next; char *name; /* uid or name */ unsigned int prio; /* prio */ char *s_uid; /* session uid */ } metacontact_item_t; typedef struct metacontact { struct metacontact *next; char *name; /* name of metacontact */ metacontact_item_t *metacontact_items; } metacontact_t; #ifndef EKG2_WIN32_NOFUNCTION extern metacontact_t *metacontacts; metacontact_t *metacontact_add(const char *name); metacontact_t *metacontact_find(const char *name); metacontact_item_t *metacontact_find_prio(metacontact_t *m); void metacontact_init(); void metacontacts_destroy(); void metacontact_write(); int metacontact_read(); #endif #ifdef __cplusplus } #endif #endif /* __EKG_METACONTACTS_H */ /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/ekg/msgqueue.c000066400000000000000000000161571175142753400167340ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2001-2002 Piotr Domagalski * Wojtek Kaniewski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #include #include #include #include #include #include #include #include #include msg_queue_t *msgs_queue = NULL; static LIST_FREE_ITEM(list_msg_queue_free, msg_queue_t *) { xfree(data->session); xfree(data->rcpts); xfree(data->message); xfree(data->seq); } DYNSTUFF_LIST_DECLARE(msgs_queue, msg_queue_t, list_msg_queue_free, __DYNSTUFF_LIST_ADD, /* msgs_queue_add() */ __DYNSTUFF_LIST_REMOVE_ITER, /* msgs_queue_removei() */ __DYNSTUFF_LIST_DESTROY) /* msgs_queue_destroy() */ /* * msg_queue_add() * * dodaje wiadomo do kolejki wiadomoci. * * - session - sesja, z ktrej wysyano * - rcpts - lista odbiorcw * - message - tre wiadomoci * - seq - numer sekwencyjny * * 0/-1 */ int msg_queue_add(const char *session, const char *rcpts, const char *message, const char *seq, msgclass_t mclass) { msg_queue_t *m = xmalloc(sizeof(msg_queue_t)); m->session = xstrdup(session); m->rcpts = xstrdup(rcpts); m->message = xstrdup(message); m->seq = xstrdup(seq); m->time = time(NULL); m->mclass = mclass; msgs_queue_add(m); return 0; } /* * msg_queue_remove_uid() * * usuwa wiadomo z kolejki wiadomoci dla danego * uytkownika. * * - uin. * * 0 jeli usunito, -1 jeli nie ma takiej wiadomoci. */ int msg_queue_remove_uid(const char *uid) { msg_queue_t *m; int res = -1; for (m = msgs_queue; m; m = m->next) { if (!xstrcasecmp(m->rcpts, uid)) { m = msgs_queue_removei(m); res = 0; } } return res; } /* * msg_queue_remove_seq() * * usuwa wiadomo z kolejki wiadomo o podanym numerze sekwencyjnym. * * - seq * * 0/-1 */ int msg_queue_remove_seq(const char *seq) { int res = -1; msg_queue_t *m; if (!seq) return -1; for (m = msgs_queue; m; m = m->next) { if (!xstrcasecmp(m->seq, seq)) { m = msgs_queue_removei(m); res = 0; } } return res; } /* * msg_queue_flush() * * wysya wiadomoci z kolejki. * * 0 jeli wysano, -1 jeli nastpi bd przy wysyaniu, -2 jeli * kolejka pusta. */ int msg_queue_flush(const char *session) { msg_queue_t *m; int ret = -1; if (!msgs_queue) return -2; for (m = msgs_queue; m; m = m->next) m->mark = 1; for (m = msgs_queue; m; m = m->next) { session_t *s; char *cmd = "/msg \"%s\" %s"; /* czy wiadomo dodano w trakcie oprniania kolejki? */ if (!m->mark) continue; if (session && xstrcmp(m->session, session)) continue; /* wiadomo wysyana z nieistniejcej ju sesji? usuwamy. */ else if (!(s = session_find(m->session))) { m = msgs_queue_removei(m); continue; } switch (m->mclass) { case EKG_MSGCLASS_SENT_CHAT: cmd = "/chat \"%s\" %s"; break; case EKG_MSGCLASS_SENT: break; default: debug_error("msg_queue_flush(), unsupported message mclass in query: %d\n", m->mclass); } command_exec_format(NULL, s, 1, cmd, m->rcpts, m->message); m = msgs_queue_removei(m); ret = 0; } return ret; } /* * msg_queue_count_session() * * zwraca liczb wiadomoci w kolejce dla danej sesji. * * - uin. */ int msg_queue_count_session(const char *uid) { msg_queue_t *m; int count = 0; for (m = msgs_queue; m; m = m->next) { if (!xstrcasecmp(m->session, uid)) count++; } return count; } /* * msg_queue_write() * * zapisuje niedostarczone wiadomoci na dysku. * * 0/-1 */ int msg_queue_write() { msg_queue_t *m; int num = 0; if (!msgs_queue) return -1; if (mkdir_recursive(prepare_pathf("queue"), 1)) /* create ~/.ekg2/[PROFILE/]queue/ */ return -1; for (m = msgs_queue; m; m = m->next) { const char *fn; GFile *f; GFileOutputStream *fs; if (!(fn = prepare_pathf("queue/%ld.%d", (long) m->time, num++))) /* prepare_pathf() ~/.ekg2/[PROFILE/]queue/TIME.UNIQID */ continue; f = g_file_new_for_path(fn); fs = g_file_replace(f, NULL, FALSE, G_FILE_CREATE_PRIVATE, NULL, NULL); g_object_unref(f); if (!fs) continue; ekg_fprintf(G_OUTPUT_STREAM(fs), "v2\n%s\n%s\n%ld\n%s\n%d\n%s", m->session, m->rcpts, m->time, m->seq, m->mclass, m->message); g_object_unref(fs); } return 0; } /** * msg_queue_read() * * Read msgqueue of not sended messages.
* msgqueue is subdir ("queue") in ekg2 config directory. * * @todo return count of readed messages? * * @todo code which handle errors is awful and it need rewriting. * * @return -1 if fail to open msgqueue directory
* 0 on success. */ int msg_queue_read() { struct dirent *d; DIR *dir; if (!(dir = opendir(prepare_pathf("queue")))) /* opendir() ~/.ekg2/[PROFILE/]/queue */ return -1; while ((d = readdir(dir))) { const char *fn; msg_queue_t m; string_t msg; char *buf; GFile *f; GFileInputStream *fs; GDataInputStream *fd; int filever = 0; if (!(fn = prepare_pathf("queue/%s", d->d_name))) continue; f = g_file_new_for_path(fn); fs = g_file_read(f, NULL, NULL); g_object_unref(f); if (!fs) continue; fd = g_data_input_stream_new(G_INPUT_STREAM(fs)); memset(&m, 0, sizeof(m)); do { buf = read_line(fd); } while (buf && (buf[0] == '#' || buf[0] == ';' || (buf[0] == '/' && buf[1] == '/'))); /* Allow leading comments */ if (buf && *buf == 'v') filever = atoi(buf+1); if (!filever || filever > 2) { g_object_unref(fd); continue; } if (!(m.session = g_strdup(read_line(fd)))) { g_object_unref(fd); continue; } if (!(m.rcpts = g_strdup(read_line(fd)))) { xfree(m.session); g_object_unref(fd); continue; } if (!(buf = read_line(fd))) { xfree(m.session); xfree(m.rcpts); g_object_unref(fd); continue; } m.time = atoi(buf); if (!(m.seq = g_strdup(read_line(fd)))) { xfree(m.session); xfree(m.rcpts); g_object_unref(fd); continue; } if (filever == 2) { if (!(buf = read_line(fd))) { xfree(m.session); xfree(m.rcpts); g_object_unref(fd); continue; } m.mclass = atoi(buf); } else m.mclass = EKG_MSGCLASS_SENT; msg = string_init(NULL); buf = read_line(fd); while (buf) { string_append(msg, buf); buf = read_line(fd); if (buf) string_append(msg, "\r\n"); } m.message = string_free(msg, 0); msgs_queue_add(g_memdup(&m, sizeof(m))); g_object_unref(fd); g_unlink(fn); } closedir(dir); return 0; } /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/ekg/msgqueue.h000066400000000000000000000035001175142753400167250ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2001-2003 Piotr Domagalski * Wojtek Kaniewski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __EKG_MSGQUEUE_H #define __EKG_MSGQUEUE_H #include #include #include "dynstuff.h" #include "protocol.h" #ifdef __cplusplus extern "C" { #endif typedef struct msg_queue { struct msg_queue *next; char *session; /* do ktrej sesji naley */ char *rcpts; /* uidy odbiorcw */ char *message; /* tre */ char *seq; /* numer sekwencyjny */ time_t time; /* czas wysania */ unsigned int mark : 1; /* if added during cleanup */ msgclass_t mclass; } msg_queue_t; extern msg_queue_t *msgs_queue; int msg_queue_add(const char *session, const char *rcpts, const char *message, const char *seq, msgclass_t mclass); void msgs_queue_destroy(); int msg_queue_count_session(const char *uid); int msg_queue_remove_uid(const char *uid); int msg_queue_remove_seq(const char *seq); int msg_queue_flush(const char *session); int msg_queue_read(); int msg_queue_write(); #ifdef __cplusplus } #endif #endif /* __EKG_MSGQUEUE_H */ /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/ekg/net.c000066400000000000000000000345701175142753400156660ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright xxxx-2008 XXX * Michal 'GiM' Spadlinski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #ifndef NO_POSIX_SYSTEM #include #include #include #include /* ? */ #endif #include #include /* ? */ #include /* ? */ #include #include /* ? */ #include #ifndef NO_POSIX_SYSTEM #include /* OK */ #endif #ifdef __sun /* Solaris, thanks to Beeth */ #include #endif /* #include #include #include */ /* NOTE: * Includes were copied from jabber.c, where there's ? in comment, it's possibly not needed. * It was done this way, to avoid regression. * THX. */ #include "net.h" #include "srv.h" struct ekg_connect_data { /* internal data */ char **resolver_queue; /* here we keep list of domains to be resolved */ char **connect_queue; /* here we keep list of IPs to try to connect */ char **connect_queue2; /* another list of IPs, this time unpreferred family */ watch_t *current_watch; watch_t *internal_watch; /* allowing user to abort connecting */ /* data provided by user */ int prefer_family; /* preferred address family */ int proto_port; /* default port of a protocol */ int port; char *session; watcher_handler_func_t *async; }; /* Removes port from 'hostname' and returns it * WARN: hostname is modified */ static int ekg_resolver_split(char *hostname, const int defport) { char *p = xstrrchr(hostname, ':'); int i; if (p && (i = atoi(p+1)) > 0 && (i <= 65535) && (xstrspn(p+1, "0123456789") == xstrlen(p+1))) { *p = '\0'; return i; } /* remove braces from (IPv6) address, * XXX: do we really need to check if it's IPv6? IPv4 doesn't use braces, domains neither. */ if (*hostname == '[' && hostname[xstrlen(hostname) - 1] == ']') { hostname[xstrlen(hostname) - 1] = 0; /* shorten, i.e. remove ']' */ memmove(hostname, hostname + 1, xstrlen(hostname)); /* xstrlen(hostname) gets trailing \0 too */ } return defport; } /* * with main part changed * * proto_port is used for srv resolver, since e.g. * jabber plugin using ssl uses port 5223, but for * srv resolving we need 5222, so as a proto_port you need * to supply default port for given protocol * * current implementation overwrites port returned by srv query with * port supplied in `port` argument, probably later I'll change it * to duplicate entries. * */ watch_t *ekg_resolver4(plugin_t *plugin, const char *server, watcher_handler_func_t async, void *data, const int proto_port, const int port, const int proto) { int res, fd[2]; debug("ekg_resolver4() resolving: %s:%d [def proto port: %d]\n", server, port, proto_port); if (pipe(fd) == -1) return NULL; debug("ekg_resolver4() resolver pipes = { %d, %d }\n", fd[0], fd[1]); if ((res = fork()) == -1) { int errno2 = errno; close(fd[0]); close(fd[1]); errno = errno2; return NULL; } if (!res) { char *tmp = xstrdup(server); /* Child */ close(fd[0]); if (tmp) { char *hostname = tmp, *nexthost; /* this loop iterates over hosts, * GiM->peres, I know this is already in ekg_connect_loop, * but right now irc doesn't use it, so leave this loop * as it is [the two won't collide with each other] */ do { gim_host *gim_host_list = NULL; int sport; gboolean hostname_duped = FALSE; if ((nexthost = xstrchr(hostname, ','))) *nexthost = '\0'; sport = ekg_resolver_split(hostname, port); if (g_hostname_is_non_ascii(hostname)) { gchar *tmp = g_hostname_to_ascii(hostname); if (tmp) { hostname_duped = TRUE; hostname = tmp; debug_function("ekg_resolver4(), encoded hostname: %s\n", hostname); } else debug_error("g_hostname_to_ascii(%s) failed\n", hostname); } srv_resolver (&gim_host_list, hostname, proto_port, sport, 0); basic_resolver (&gim_host_list, hostname, sport); resolve_missing_entries(&gim_host_list); if (hostname_duped) g_free(hostname); hostname = nexthost+1; write_out_and_destroy_list(fd[1], gim_host_list); } while (nexthost); write(fd[1], "EOR\n", 4); sleep(3); close(fd[1]); } xfree(tmp); exit(0); } /* parent */ close(fd[1]); ekg_child_add(plugin, "resolver: %s", res, NULL, NULL, NULL, server); return watch_add_line(plugin, fd[0], WATCH_READ_LINE, async, data); } static void ekg_connect_data_free(struct ekg_connect_data *c) { if (c->internal_watch) { c->internal_watch->data = NULL; /* avoid double free */ watch_free(c->internal_watch); } g_strfreev(c->resolver_queue); g_strfreev(c->connect_queue); xfree(c->session); xfree(c); } static int ekg_connect_loop(struct ekg_connect_data *c); /* predeclared */ static WATCHER_LINE(ekg_connect_resolver_handler) { struct ekg_connect_data *c = (struct ekg_connect_data*) data; if (!c) return -1; if (type) { if (session_find(c->session)) debug_function("ekg_connect_resolver_handler(), resolving done.\n"); ekg_connect_loop(c); close(fd); return -1; } else if (!session_find(c->session)) return -1; debug_function("ekg_connect_resolver_handler() = %s\n", watch); if (!xstrcmp(watch, "EOR")) return -1; else if (c->prefer_family) { /* determine address family */ const char *ax = xstrchr(watch, ' '); int af; if (ax) ax = xstrchr(ax+1, ' '); if (!ax) { /* WTF?! */ debug_error("ekg_connect_resolver_handler(), unable to determine address family with '%s'\n", watch); return 0; } af = atoi(ax+1); if (af != c->prefer_family) { /* unpreferred go to queue2 */ array_add(&(c->connect_queue2), xstrdup(watch)); return 0; } } /* if preferred or no preference */ array_add(&(c->connect_queue), xstrdup(watch)); return 0; } static int ekg_build_sin(const char *data, const int defport, struct sockaddr **address, int *family) { struct sockaddr_in *ipv4; struct sockaddr_in6 *ipv6; int len = 0; int port; const char *addr; char **a = array_make(data, " ", 4, 1, 0); *address = NULL; if (g_strv_length(a) < 3) { g_strfreev(a); return 0; } addr = a[1]; *family = atoi(a[2]); port = a[3] ? atoi(a[3]) : 0; if (port <= 0 || port > 66535) port = defport; if (*family == AF_INET) { len = sizeof(struct sockaddr_in); ipv4 = xmalloc(len); ipv4->sin_family = AF_INET; ipv4->sin_port = g_htons(port); #ifdef HAVE_INET_PTON inet_pton(AF_INET, addr, &(ipv4->sin_addr)); #else #warning "irc: You don't have inet_pton() connecting to ipv4 hosts may not work" #ifdef HAVE_INET_ATON /* XXX */ if (!inet_aton(addr, &(ipv4->sin_addr))) debug_error("inet_aton() failed on addr: %s.\n", addr); #else #warning "irc: You don't have inet_aton() connecting to ipv4 hosts may not work" #endif #warning "irc: Yeah, You have inet_addr() connecting to ipv4 hosts may work :)" if ((ipv4->sin_addr.s_addr = inet_addr(addr)) == -1) debug_error("inet_addr() failed or returns 255.255.255.255? on %s\n", addr); #endif *address = (struct sockaddr *) ipv4; } else if (*family == AF_INET6) { len = sizeof(struct sockaddr_in6); ipv6 = xmalloc(len); ipv6->sin6_family = AF_INET6; ipv6->sin6_port = g_htons(port); #ifdef HAVE_INET_PTON inet_pton(AF_INET6, addr, &(ipv6->sin6_addr)); #else #warning "irc: You don't have inet_pton() connecting to ipv6 hosts may not work" #endif *address = (struct sockaddr *) ipv6; } else debug_function("ekg_build_sin(), unknown addr family %d!\n", family); g_strfreev(a); return len; } static WATCHER(ekg_connect_handler) { struct ekg_connect_data *c = (struct ekg_connect_data*) data; int res = 0; socklen_t res_size = sizeof(res); session_t *s; if (!c) return -1; debug_function("ekg_connect_handler(), type = %d.\n", type); if (type == 1) return 0; if (!((s = session_find(c->session)))) { ekg_connect_loop(c); close(fd); return -1; } if (type || getsockopt(fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res) { if (res) debug_error("ekg_connect_handler(), error: %s\n", strerror(res)); ekg_connect_loop(c); close(fd); return -1; } if (s && c->async(type, fd, WATCH_WRITE, s) > 0) { debug_error("ekg_connect_handler(), looks like caller didn't like our job.\n"); ekg_connect_loop(c); close(fd); } else ekg_connect_data_free(c); return -1; } static int ekg_connect_loop(struct ekg_connect_data *c) { char *host; session_t *s = session_find(c->session); if (!s) { /* session vanished! */ debug_error("ekg_connect_loop(), looks like session '%s' vanished!\n", c->session); ekg_connect_data_free(c); return 0; } /* 1) if anything is in connect_queue, try to connect */ /* 1b) if connect_queue is empty, try unpreferred families too * if anyone would like ekg_connect() to try other servers with pref family too, * please let me know */ if ((host = array_shift(&(c->connect_queue))) || (host = array_shift(&(c->connect_queue2)))) { struct sockaddr *addr; int len, fd, family, connret; watch_t *w; const int timeout = session_int_get(s, "connect_timeout"); do { int one = 1; len = ekg_build_sin(host, c->port, &addr, &family); debug_function("ekg_connect_loop(), connect: %s, sinlen: %d\n", host, len); xfree(host); if (!len) break; if ((fd = socket(family, SOCK_STREAM, 0)) == -1) { const int err = errno; debug_error("ekg_connect_loop(), socket() failed: %s\n", strerror(err)); break; } setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(one)); if (ioctl(fd, FIONBIO, &one) == -1) { const int err = errno; debug_error("ekg_connect_loop(), ioctl() failed: %s\n", strerror(err)); close(fd); break; } connret = connect(fd, addr, len); if (connret && errno != EINPROGRESS) { const int err = errno; debug_error("ekg_connect_loop(), connect() failed: %s\n", strerror(err)); close(fd); break; } w = watch_add(s->plugin, fd, WATCH_WRITE, ekg_connect_handler, c); if (timeout > 0) watch_timeout_set(w, timeout); xfree(addr); c->current_watch = w; return 1; } while (0); xfree(addr); } /* 2) if anything is in resolver_queue, try to resolve */ if ((host = array_shift(&(c->resolver_queue)))) { watch_t *w; debug_function("ekg_connect_loop(), resolve: %s\n", host); w = ekg_resolver4(s->plugin, host, (void*) ekg_connect_resolver_handler, c, c->proto_port, c->port, IPPROTO_TCP); xfree(host); c->current_watch = w; return 1; } /* 3) fail */ if (s) c->async(-1, -1, WATCH_WRITE, s); ekg_connect_data_free(c); return 0; } static WATCHER(ekg_connect_abort) { struct ekg_connect_data *c = (struct ekg_connect_data*) data; if (type == 1) { if (data) { c->internal_watch = NULL; /* avoid freeing twice */ c->current_watch->data = NULL; /* avoid running handler, just make watch disappear */ watch_free(c->current_watch); ekg_connect_data_free(c); debug_function("ekg_connect_abort(), data freed.\n"); } } else debug_error("ekg_connect_abort() called with incorrect type!\n"); return -1; } /* default comparison function, based on 'prefer_family' sessionvar */ watch_t *ekg_connect(session_t *session, const char *server, const int proto_port, const int port, watcher_handler_func_t async) { const int pref = session_int_get(session, "prefer_family"); struct ekg_connect_data *c; if (!session || !server || !async) return 0; c = xmalloc(sizeof(struct ekg_connect_data)); /* 1) fill struct */ c->resolver_queue = array_make(server, ",", 0, 1, 1); c->session = xstrdup(session_uid_get(session)); c->async = async; c->port = port; c->proto_port = proto_port; c->internal_watch = xmalloc(sizeof(watch_t)); c->internal_watch->type = WATCH_NONE; c->internal_watch->handler = ekg_connect_abort; c->internal_watch->data = c; if (pref == 4) c->prefer_family = AF_INET; else if (pref == 6) c->prefer_family = AF_INET6; /* 2) call in the loop */ ekg_connect_loop(c); /* 3) return internal watch, allowing caller to abort */ return c->internal_watch; } /**** OLD STUFFF *****/ /* * ekg_resolver2() * * Resolver copied from jabber plugin, * it uses gethostbyname() * * - async - watch handler. * - data - watch data handler. * * in @a async watch you'll recv 4 bytes data with ip addr of @a server, or INADDR_NONE if gethostbyname() failed. * you should return -1 (temporary watch) and in type == 1 close fd. * * NOTE, EKG2-RESOLVER-API IS NOT STABLE. * IT'S JUST COPY-PASTE OF SOME FUNCTION FROM OTHER PLUGINS, TO AVOID DUPLICATION OF CODE (ALSO CLEANUP CODE A LITTLE) * AND TO AVOID REGRESSION. * THX. */ watch_t *ekg_resolver2(plugin_t *plugin, const char *server, watcher_handler_func_t async, void *data) { int res, fd[2]; char *myserver; if (!server) { errno = EFAULT; return NULL; } debug("ekg_resolver2() resolving: %s\n", server); if (pipe(fd) == -1) return NULL; debug("ekg_resolver2() resolver pipes = { %d, %d }\n", fd[0], fd[1]); myserver = xstrdup(server); if ((res = fork()) == -1) { int errno2 = errno; close(fd[0]); close(fd[1]); xfree(myserver); errno = errno2; return NULL; } if (!res) { /* child */ struct in_addr a; close(fd[0]); if (g_hostname_is_non_ascii(myserver)) { gchar *tmp = g_hostname_to_ascii(myserver); if (tmp) { g_free(myserver); myserver = tmp; debug_function("ekg_resolver2(), encoded hostname: %s\n", myserver); } else debug_error("g_hostname_to_ascii(%s) failed\n", myserver); } if ((a.s_addr = inet_addr(myserver)) == INADDR_NONE) { struct hostent *he = gethostbyname(myserver); if (!he) a.s_addr = INADDR_NONE; else memcpy(&a, he->h_addr_list[0], sizeof(a)); } write(fd[1], &a, sizeof(a)); xfree(myserver); sleep(1); exit(0); } /* parent */ close(fd[1]); xfree(myserver); ekg_child_add(plugin, "resolver: %s", res, NULL, NULL, NULL, server); return watch_add(plugin, fd[0], WATCH_READ, async, data); } ekg2-0.4~pre+20120506.1/ekg/net.h000066400000000000000000000030001175142753400156530ustar00rootroot00000000000000/* $Id: $ */ /* * (C) Copyright XXX * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __EKG_NET_H #define __EKG_NET_H #ifndef EKG2_WIN32_NOFUNCTION #include "plugins.h" #include "sessions.h" #include "srv.h" #ifdef __cplusplus extern "C" { #endif #ifndef INADDR_NONE # define INADDR_NONE (unsigned long) 0xffffffff #endif watch_t *ekg_resolver2(plugin_t *plugin, const char *server, watcher_handler_func_t async, void *data); watch_t *ekg_resolver4(plugin_t *plugin, const char *server, watcher_handler_func_t async, void *data, const int proto_port, const int port, const int proto); watch_t *ekg_connect(session_t *session, const char *server, const int proto_port, const int port, watcher_handler_func_t async); #ifdef __cplusplus } #endif #endif /* EKG2_WIN32_NOFUNCTION */ #endif /* __EKG_NET_H */ /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/ekg/objects.h000066400000000000000000000054541175142753400165350ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2003 Wojtek Kaniewski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __EKG_OBJECTS_H #define __EKG_OBJECTS_H #include "xmalloc.h" #ifdef __cplusplus extern "C" { #endif #define PROPERTY_INT_GET(object,property,type) \ \ type object##_##property##_get(object##_t *o) \ { \ return (o) ? o->property : -1; \ } #define PROPERTY_INT_SET(object,property,type) \ \ int object##_##property##_set(object##_t *o, type v) \ { \ if (!o) \ return -1; \ \ o->property = v; \ \ return 0; \ } #define PROPERTY_INT(object,property,type) \ \ PROPERTY_INT_GET(object,property,type) \ PROPERTY_INT_SET(object,property,type) #define PROPERTY_STRING_GET(object,property) \ \ const char *object##_##property##_get(object##_t *o) \ { \ return (o) ? o->property : NULL; \ } #define PROPERTY_STRING_SET(object,property) \ \ int object##_##property##_set(object##_t *o, const char *v) \ { \ if (!o) \ return -1; \ \ xfree(o->property); \ o->property = xstrdup(v); \ \ return 0; \ } #define PROPERTY_STRING(object,property) \ \ PROPERTY_STRING_SET(object, property) \ PROPERTY_STRING_GET(object, property) #define PROPERTY_PRIVATE_GET(object) \ \ void *object##_private_get(object##_t *o) \ { \ return (o) ? o->priv : NULL; \ } #define PROPERTY_PRIVATE_SET(object) \ \ int object##_private_set(object##_t *o, void *v) \ { \ if (!o) \ return -1; \ \ o->priv = v; \ \ return 0; \ } #define PROPERTY_PRIVATE(object) \ \ PROPERTY_PRIVATE_GET(object) \ PROPERTY_PRIVATE_SET(object) #define PROPERTY_MISC_GET(object,property,type,null) \ \ type object##_##property##_get(object##_t *o) \ { \ return (o) ? o->property : null; \ } #define PROPERTY_MISC_SET(object,property,type) \ \ int object##_##property##_set(object##_t *o, type v) \ { \ if (!o) \ return -1; \ \ o->property = v; \ \ return 0; \ } #define PROPERTY_MISC(object,property,type,null) \ \ PROPERTY_MISC_GET(object,property,type,null) \ PROPERTY_MISC_SET(object,property,type) #ifdef __cplusplus } #endif #endif /* __EKG_OOP_H */ /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/ekg/plugins.c000066400000000000000000000413111175142753400165500ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2003 Wojtek Kaniewski * 2004 Piotr Kupisiewicz (deli@rzepaknet.us> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #include #include #include #include #include #include "objects.h" #include "abort.h" GSList *plugins = NULL; /* XXX: not freed anywhere yet */ gchar *rel_plugin_dir = NULL; static gint plugin_register_compare(gconstpointer a, gconstpointer b) { const plugin_t *data1 = (const plugin_t *) a; const plugin_t *data2 = (const plugin_t *) b; return data2->prio - data1->prio; } static void plugins_add(plugin_t *pl) { plugins = g_slist_insert_sorted(plugins, pl, plugin_register_compare); } void plugins_unlink(plugin_t *pl) { plugins = g_slist_remove(plugins, pl); } list_t watches = NULL; query_t* queries[QUERIES_BUCKETS]; query_def_t* registered_queries; int registered_queries_count = 0; LIST_FREE_ITEM(query_free_data, query_t *) { xfree(data->name); } DYNSTUFF_LIST_DECLARE(queries_list, query_t, query_free_data, static __DYNSTUFF_ADD, static __DYNSTUFF_REMOVE_SAFE, __DYNSTUFF_DESTROY) void ekg2_dlinit(const gchar *argv0) { #ifdef SHARED_LIBS if (g_module_supported()) { /* Set relative plugin path based on executable location */ gchar *progpath = g_find_program_in_path(argv0); if (progpath) { rel_plugin_dir = g_path_get_dirname(progpath); g_free(progpath); } } # ifndef STATIC_LIBS else { g_printerr("Dynamic module loading unsupported, and no static plugins.\n" "Please recompile with --enable-static.\n"); abort(); } # endif #endif } #ifdef SHARED_LIBS /** * ekg2_dlclose() * * Close handler to dynamic loaded library. * * @param plugin - Handler to loaded library. * * @return 0 on success, else fail. */ int ekg2_dlclose(GModule *plugin) { return (g_module_close(plugin) != TRUE); } /** * ekg2_dlopen() * * Load dynamic library file from @a name * * @todo Think more about flags for dlopen() [was: RTLD_LAZY | RTLD_GLOBAL] * * @param name - Full path of library to load. * * @return Pointer to the loaded library, or NULL if fail. */ static GModule *ekg2_dlopen(const char *name) { /* RTLD_LAZY is bad flag, because code can SEGV on executing undefined symbols... * it's better to fail earlier than later with SIGSEGV * * RTLD_GLOBAL is bad flag also, because we have no need to export symbols to another plugns * we should do it by queries... Yeah, I know it was used for example in perl && irc plugin. * But we cannot do it. Because if we load irc before sim plugin. Than we'll have unresolved symbols * even if we load sim plugin later. */ /* * RTLD_GLOBAL is required by perl and python plugins... * need investigation. [XXX] */ GModule *tmp = g_module_open(name, 0); if (!tmp) { char *errstr = ekg_recode_from_locale(g_module_error()); debug_warn("[plugin] could not be loaded: %s %s\n", name, errstr); g_free(errstr); } else { debug_ok("[plugin] loaded: %s\n", name); } return tmp; } /** * ekg2_dlsym() * * Get symbol with @a name from loaded dynamic library. * * @param plugin - Pointer to the loaded library. * @param name - Name of symbol to lookup. * * @return Address of symbol or NULL if error occur. */ static void *ekg2_dlsym(GModule *plugin, char *name) { void *tmp; if (!g_module_symbol(plugin, name, &tmp)) { debug_error("[plugin] plugin: %x symbol: %s error: %s\n", plugin, name, g_module_error()); return NULL; } return tmp; } #endif /* * plugin_load() * * aduje wtyczk o podanej nazwie. * * 0/-1 */ int plugin_load(const char *name, int prio, int quiet) { #ifdef SHARED_LIBS const gchar *env_ekg_plugins_path = NULL; char *init = NULL; gchar *lib; gchar *libname; GModule *plugin = NULL; #endif plugin_t *pl; int (*plugin_init)() = NULL; g_assert(name); if (plugin_find(name)) { printq("plugin_already_loaded", name); return -1; } #ifdef SHARED_LIBS libname = g_strdup_printf("%s.la", name); if ((env_ekg_plugins_path = g_getenv("EKG_PLUGINS_PATH"))) { lib = g_build_filename(env_ekg_plugins_path, libname, NULL); plugin = ekg2_dlopen(lib); g_free(lib); if (!plugin) { lib = g_build_filename(env_ekg_plugins_path, name, libname, NULL); plugin = ekg2_dlopen(lib); g_free(lib); } } /* The following lets ekg2 load plugins when it is run directly from * the source tree, without installation. This can be beneficial when * developing the program, or for less knowlegeable users, who don't * know how to or cannot for some other reason use installation prefix * to install in their home directory. It might be also useful * for win32-style installs. */ if (!plugin && rel_plugin_dir) { lib = g_build_filename(rel_plugin_dir, "plugins", name, libname, NULL); plugin = ekg2_dlopen(lib); g_free(lib); } if (!plugin) { lib = g_build_filename(PLUGINDIR, libname, NULL); plugin = ekg2_dlopen(lib); g_free(lib); } g_free(libname); #ifdef PLUGIN_SUFFIX /* no .la found? try the standard suffix as well */ if (!plugin) { libname = g_strdup_printf("%s" PLUGIN_SUFFIX, name); if ((env_ekg_plugins_path = g_getenv("EKG_PLUGINS_PATH"))) { lib = g_build_filename(env_ekg_plugins_path, libname, NULL); plugin = ekg2_dlopen(lib); g_free(lib); if (!plugin) { lib = g_build_filename(env_ekg_plugins_path, name, libname, NULL); plugin = ekg2_dlopen(lib); g_free(lib); } } if (!plugin) { lib = g_build_filename(PLUGINDIR, libname, NULL); plugin = ekg2_dlopen(lib); g_free(lib); } g_free(libname); } #endif /* prefer shared plugins */ if (plugin) { init = g_strdup_printf("%s_plugin_init", name); plugin_init = ekg2_dlsym(plugin, init); g_free(init); } #endif /* SHARED_LIBS */ #ifdef STATIC_LIBS /* if no shared plugin, fallback to the static one */ if (!plugin_init) { STATIC_PLUGIN_DECLS STATIC_PLUGIN_CALLS if (plugin_init) debug_ok("[plugin] statically compiled in: %s\n", name); } #endif if (!plugin_init) { #ifdef SHARED_LIBS if (plugin) { printq("plugin_incorrect", name); ekg2_dlclose(plugin); } else #endif printq("plugin_doesnt_exist", name); return -1; } if (plugin_init(prio) == -1) { printq("plugin_not_initialized", name); #ifdef SHARED_LIBS if (plugin) ekg2_dlclose(plugin); #endif return -1; } if ((pl = plugin_find(name))) { #ifdef SHARED_LIBS pl->dl = plugin; #else pl->dl = NULL; #endif } else { debug_error("plugin_load() plugin_find(%s) not found.\n", name); /* It's FATAL */ } query_emit(pl, "set-vars-default"); printq("plugin_loaded", name); if (!in_autoexec) { in_autoexec = 1; config_read(name); if (pl->pclass == PLUGIN_PROTOCOL) session_read(name); if (pl) query_emit(pl, "config-postinit"); in_autoexec = 0; config_changed = 1; } return 0; } /** * plugin_find() * * Find plugin by name * * @param name - name of plugin_t * * @return plugin_t with given name, or NULL if not found. */ plugin_t *plugin_find(const char *name) { GSList *pl; for (pl = plugins; pl; pl = pl->next) { plugin_t *p = pl->data; if (!xstrcmp(p->name, name)) return p; } return NULL; } /** * plugin_find_uid() * * Find PLUGIN_PROTOCOL plugin which can handle @a uid * * @todo used only by session_add() in session.c move it there? * * @sa valid_plugin_uid() - For function to check if given plugin can handle given uid * * @return If such plugin was founded return it, or NULL if not found. */ plugin_t *plugin_find_uid(const char *uid) { GSList *pl; for (pl = plugins; pl; pl = pl->next) { plugin_t *p = pl->data; if (p && p->pclass == PLUGIN_PROTOCOL && p->name && valid_plugin_uid(p, uid)) return p; } return NULL; } /* * plugin_unload() * * usuwa z pamici dan wtyczk, lub jeli wtyczka jest wkompilowana na * stae, deaktywuje j. * * 0/-1 */ int plugin_unload(plugin_t *p) { char *name; list_t l; if (!p) return -1; if (config_expert_mode == 0 && p->pclass == PLUGIN_UI) { GSList *pl; int unloadable = 0; for (pl = plugins; pl; pl = pl->next) { const plugin_t *plug = pl->data; if (plug->pclass == PLUGIN_UI && plug != p) unloadable = 1; } if (!unloadable) { print("plugin_unload_ui", p->name); return -1; } } #ifdef WATCHES_FIXME /* XXX: why not simply destroy them right now? */ for (l = watches; l; l = l->next) { watch_t *w = l->data; if (w && w->plugin == p && (w->removed == 1 || w->removed == -1)) { print("generic_error", "XXX cannot remove this plugin when there some watches active"); return -1; } } /* XXX, to samo dla timerow */ #endif name = xstrdup(p->name); if (p->destroy) p->destroy(); ekg2_unregister_abort_handlers_for_plugin(p); #ifdef SHARED_LIBS if (p->dl) ekg2_dlclose(p->dl); #endif print("plugin_unloaded", name); xfree(name); if (!in_autoexec) config_changed = 1; return 0; } /* * plugin_register() * * rejestruje dan wtyczk. * * 0/-1 */ int plugin_register(plugin_t *p, int prio) { if (prio == -254) { switch (p->pclass) { case PLUGIN_UI: p->prio = 0; break; case PLUGIN_LOG: p->prio = 5; break; case PLUGIN_SCRIPTING: p->prio = 10; break; case PLUGIN_PROTOCOL: p->prio = 15; break; default: p->prio = 20; break; } } else { p->prio = prio; } plugins_add(p); return 0; } /* * plugin_unregister() * * odcza wtyczk. * * 0/-1 */ int plugin_unregister(plugin_t *p) { /* XXX eXtreme HACK warning * (mp) na razie jest tak. docelowo: wyladowywac pluginy tylko z * glownego programu (queriesami?) * to cos segfaultowalo (wczesniej czy pozniej), jesli bylo wywolane z * ncurses. niestety, problem pozostaje dla innych pluginow i takiego * np. rc. sie zrobi nast razem */ /* j/w If any plugin has backtrace here, and we try to remove it from memory. * ekg2 do SEGV. */ session_t *s; query_t **kk; GSList *vl, *cl; list_t l; g_assert(p); /* XXX think about sequence of unloading....: currently: watches, timers, sessions, queries, variables, commands */ for (l = watches; l; l = l->next) { watch_t *w = l->data; if (w && w->plugin == p) watch_free(w); } ekg_source_remove_by_plugin(p, NULL); for (s = sessions; s; ) { session_t *next = s->next; if (s->plugin == p) session_remove(s->uid); s = next; } for (kk = queries; kk < &queries[QUERIES_BUCKETS]; ++kk) { query_t *g; for (g = *kk; g; ) { query_t *next = g->next; if (g->plugin == p) queries_list_remove(kk, g); g = next; } } for (vl = variables; vl;) { variable_t *v = vl->data; vl = vl->next; if (v->plugin == p) variables_remove(v); } for (cl = commands; cl;) { command_t *c = cl->data; cl = cl->next; if (c->plugin == p) commands_remove(c); } plugins_unlink(p); return 0; } /** * plugin_var_find() * * it looks for given variable name in given plugin * * @param pl - plugin * @param name - variable name * * returns sequence number+1 of variable if found, else 0 */ int plugin_var_find(plugin_t *pl, const char *name) { int i; if (!pl || !pl->params) return 0; for (i = 0; (pl->params[i].key /* && pl->params[i].id != -1 */); i++) { if (!xstrcasecmp(pl->params[i].key, name)) return i+1; } return 0; } int plugin_var_add(plugin_t *pl, const char *name, int type, const char *value, int secret, plugin_notify_func_t *notify) { return -1; } static LIST_FREE_ITEM(registered_query_free_data, query_def_t *) { xfree(data->name); } void registered_queries_free() { if (!registered_queries) return; LIST_DESTROY2(registered_queries, registered_query_free_data); /* this has been already done in call above */ registered_queries = NULL; } static int query_register_common(const char* name, query_def_t **res) { query_def_t *gd; int found = 0, name_hash = ekg_hash(name); for (gd = registered_queries; gd; gd = gd->next) { if (name_hash == gd->name_hash && !xstrcmp(gd->name, name)) { found = 1; break; } } if (found) { debug_error("query_register() oh noez, seems like it's already registered: [%s]\n", name); debug_error("I'm not sure what I should do, so I'm simply bailing out...\n"); return -1; } else { gd = xmalloc(sizeof(query_def_t)); gd->name = xstrdup(name); gd->name_hash = name_hash; registered_queries_count++; LIST_ADD2(®istered_queries, gd); } *res = gd; return 0; } int query_register(const char *name, ...) { query_def_t *gd; int i, arg; va_list va; if (query_register_common(name, &gd)) { return -1; } va_start(va, name); for (i = 0; i < QUERY_ARGS_MAX; i++) { arg = va_arg(va, int); gd->params[i] = arg; if (arg == QUERY_ARG_END) break; } va_end(va); return 0; } /* * alternative way for registering queries */ int query_register_const(const query_def_t *def) { query_def_t *gd; if (query_register_common(def->name, &gd)) { return -1; } memcpy(gd->params, def->params, sizeof(def->params)); return 0; } int query_free(query_t* g) { queries_list_remove(&queries[g->name_hash & (QUERIES_BUCKETS - 1)], g); return 0; } query_t *query_connect(plugin_t *plugin, const char *name, query_handler_func_t *handler, void *data) { int found = 0; query_def_t* gd; query_t *q = xmalloc(sizeof(query_t)); q->name = xstrdup(name); q->name_hash = ekg_hash(name); q->plugin = plugin; q->handler = handler; q->data = data; for (gd = registered_queries; gd; gd = gd->next) { if (q->name_hash == gd->name_hash && !xstrcmp(gd->name, name)) { found = 1; break; } } if (!found) { debug_error("query_connect() NOT FOUND[%d]: %s\n", registered_queries_count, __(name)); gd = xmalloc(sizeof(query_def_t)); gd->name = xstrdup(name); gd->name_hash = q->name_hash; registered_queries_count++; LIST_ADD2(®istered_queries, gd); } queries_list_add(&queries[q->name_hash & (QUERIES_BUCKETS - 1)], q); return q; } static int query_emit_inner(query_t *g, va_list ap) { static int nested = 0; int (*handler)(void *data, va_list ap) = g->handler; int result; va_list ap_plugin; if (nested >= 32) { return -1; } g->count++; /* * pc and amd64: va_arg remove var from va_list when you use va_arg, * so we must keep orig va_list for next plugins */ nested++;; G_VA_COPY(ap_plugin, ap); result = handler(g->data, ap_plugin); va_end(ap_plugin); nested--; return result != -1 ? 0 : -1; } int query_emit(plugin_t *plugin, const char* name, ...) { int result = -2; va_list ap; query_t* g; int name_hash, bucket_id; name_hash = ekg_hash(name); bucket_id = name_hash & (QUERIES_BUCKETS - 1); va_start(ap, name); for (g = queries[bucket_id]; g; g = g->next) { if (name_hash == g->name_hash && (!plugin || (plugin == g->plugin)) && !xstrcmp(name, g->name)) { result = query_emit_inner(g, ap); if (result == -1) { break; } } } va_end(ap); return result; } static LIST_ADD_COMPARE(query_compare, query_t *) { /* any other suggestions: vvv ? */ const int ap = (data1->plugin ? data1->plugin->prio : -666); const int bp = (data2->plugin ? data2->plugin->prio : -666); return (bp-ap); } /** * queries_reconnect() * * Reconnect (resort) all queries, e.g. after plugin prio change. */ void queries_reconnect() { size_t i; for (i = 0; i < QUERIES_BUCKETS; ++i) { LIST_RESORT2(&(queries[i]), query_compare); } } /** * have_plugin_of_class() * * Check if we have loaded plugin from @a pclass * * @param pclass * * @return 1 - If such plugin was founded
* else 0 */ int have_plugin_of_class(plugin_class_t pclass) { GSList *pl; for (pl = plugins; pl; pl = pl->next) { const plugin_t *p = pl->data; if (p->pclass == pclass) return 1; } return 0; } PROPERTY_INT_SET(watch, timeout, time_t) /* * plugin_abi_version() * * @param plugin_abi_ver, plugin_name * * @return 1 - if core ABI version is the sama as plugin ABI version * else 0 */ int plugin_abi_version(int plugin_abi_ver, const char * plugin_name) { if (EKG_ABI_VER == plugin_abi_ver) return 1; debug_error("ABI versions mismatch. %s_plugin ABI ver. %d, core ABI ver. %d\n", plugin_name, plugin_abi_ver, EKG_ABI_VER); return 0; } /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: * vim: noet */ ekg2-0.4~pre+20120506.1/ekg/plugins.h000066400000000000000000000111141175142753400165530ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2003 Wojtek Kaniewski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __EKG_PLUGINS_H #define __EKG_PLUGINS_H #include #include #include #include #include "dynstuff.h" #include "sessions.h" #ifdef __cplusplus extern "C" { #endif #define EKG_ABI_VER 5798 /* git rev-list master | wc -l */ #define EXPORT __attribute__ ((visibility("default"))) G_MODULE_EXPORT typedef enum { PLUGIN_ANY = 0, PLUGIN_GENERIC, PLUGIN_PROTOCOL, PLUGIN_UI, PLUGIN_LOG, PLUGIN_SCRIPTING, PLUGIN_AUDIO, PLUGIN_CODEC, PLUGIN_CRYPT } plugin_class_t; typedef int (*plugin_destroy_func_t)(void); typedef int (*plugin_theme_init_func_t)(void); typedef void (plugin_notify_func_t)(session_t *, const char *); #define PLUGIN_VAR_ADD(name, type, value, secret, notify) { name, value, secret, type, notify, NULL } #define PLUGIN_VAR_ADD_MAP(name, type, value, secret, notify, map) { name, value, secret, type, notify, map } #define PLUGIN_VAR_END() { NULL, NULL, 0, -1, NULL } extern int plugin_abi_version(int plugin_abi_ver, const char * plugin_name); #define PLUGIN_CHECK_VER(name) { if (!plugin_abi_version(EKG_ABI_VER, name)) return -1; } typedef struct { char *key; /* name */ char *value; /* value */ int secret; /* should it be hidden ? */ int type; /* type */ plugin_notify_func_t *notify; /* notify */ struct variable_map_t *map; /* values and labels map */ } plugins_params_t; struct protocol_plugin_priv { const char **protocols; /* NULL-terminated list of supported protocols, replacing GET_PLUGIN_PROTOCOLS */ const status_t *statuses; /* EKG_STATUS_NULL-terminated list of supported statuses */ }; typedef struct plugin { char *name; int prio; plugin_class_t pclass; plugin_destroy_func_t destroy; /* lt_dlhandle */ void *dl; plugins_params_t *params; plugin_theme_init_func_t theme_init; const void *priv; } plugin_t; /* Note about plugin_t.statuses: * we currently put every supported status there, including unsettable by user, * we assume that user cannot set states <= EKG_STATUS_NA * [XXX] */ #ifndef EKG2_WIN32_NOFUNCTION int plugin_load(const char *name, int prio, int quiet); int plugin_unload(plugin_t *); int plugin_register(plugin_t *, int prio); int plugin_unregister(plugin_t *); plugin_t *plugin_find(const char *name); plugin_t *plugin_find_uid(const char *uid); int have_plugin_of_class(plugin_class_t pclass); int plugin_var_add(plugin_t *pl, const char *name, int type, const char *value, int secret, plugin_notify_func_t *notify); int plugin_var_find(plugin_t *pl, const char *name); void plugins_unlink(plugin_t *pl); #endif #ifdef USINGANANTIQUECOMPILER #define PLUGIN_DEFINE(x, y, z)\ static int x##_plugin_destroy(); \ \ plugin_t x##_plugin = { \ NULL, \ #x, \ 0, \ y, \ x##_plugin_destroy, \ NULL, NULL, \ z \ } #else #define PLUGIN_DEFINE(x, y, z)\ static int x##_plugin_destroy(); \ \ plugin_t x##_plugin = { \ .name = #x, \ .pclass = y, \ .destroy = x##_plugin_destroy, \ .theme_init = z \ } #endif /* USINGANANTIQUECOMPILER */ #define QUERY(x) int x(void *data, va_list ap) typedef QUERY(query_handler_func_t); /* must be power of 2 ;p */ #define QUERIES_BUCKETS 64 typedef struct query_node { struct query_node* next; char *name; int name_hash; plugin_t *plugin; void *data; query_handler_func_t *handler; int count; } query_t; int query_register(const char *name, ...); query_t *query_connect(plugin_t *plugin, const char *name, query_handler_func_t *handler, void *data); int query_emit(plugin_t *, const char *, ...); int query_free(query_t* g); void queries_reconnect(); void queries_list_destroy(query_t** kk); void registered_queries_free(); #ifndef EKG2_WIN32_NOFUNCTION extern GSList *plugins; extern query_t *queries[]; #endif #ifdef __cplusplus } #endif #endif /* __EKG_PLUGINS_H */ /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/ekg/protocol.c000066400000000000000000000762421175142753400167430ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2003 Wojtek Kaniewski * 2004 Piotr Kupisiewicz * 2004 Adam Mikuta * 2005 Leszek Krupiski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #include #ifndef NO_POSIX_SYSTEM #include #include #include #endif #include #include "emoticons.h" #include "objects.h" static int auto_find_limit = 100; /* counter of persons who we were looking for when autofind */ dcc_t *dccs = NULL; static QUERY(protocol_disconnected); static QUERY(protocol_connected); static QUERY(protocol_message_ack); static QUERY(protocol_status); static QUERY(protocol_message); static QUERY(protocol_xstate); static QUERY(protocol_userlist_changed); /** * protocol_init() * * Init communication between core and PROTOCOL plugins
*
* Here, we register main communication channels like:
* - status changes: PROTOCOL_STATUS
* - message I/O: PROTOCOL_MESSAGE
* - acknowledge of messages: PROTOCOL_MESSAGE_ACK
* - misc user events like typing notifies:PROTOCOL_XSTATE
* - session connection/disconnection: PROTOCOL_CONNECTED and PROTOCOL_DISCONNECTED * - roster changes: USERLIST_ADDED and USERLIST_REMOVED and USERLIST_RENAMED * * @sa query_connect() - Function to add listener on specified events. * @sa query_emit() - Function to emit specified events. */ void protocol_init() { query_connect(NULL, "protocol-status", protocol_status, NULL); query_connect(NULL, "protocol-message", protocol_message, NULL); query_connect(NULL, "protocol-message-ack", protocol_message_ack, NULL); query_connect(NULL, "protocol-xstate", protocol_xstate, NULL); query_connect(NULL, "protocol-connected", protocol_connected, NULL); query_connect(NULL, "protocol-disconnected", protocol_disconnected, NULL); query_connect(NULL, "userlist-added", protocol_userlist_changed, NULL); query_connect(NULL, "userlist-removed", protocol_userlist_changed, NULL); query_connect(NULL, "userlist-renamed", protocol_userlist_changed, NULL); } /* * XXX, * This code is from ncurses ncurses_userlist_changed() * * It's now proper ekg2-way (and also for gtk/readline) * * However in USERLIST_* event we don't pass session... * Yep, it's buggy. */ static QUERY(protocol_userlist_changed) { char **p1 = va_arg(ap, char**); char **p2 = va_arg(ap, char**); window_t *w; for (w = windows; w; w = w->next) { if (!w->target || xstrcasecmp(w->target, *p1)) continue; xfree(w->target); w->target = xstrdup(*p2); query_emit(NULL, "ui-window-target-changed", &w); } return 0; } /** * protocol_reconnect_handler() * * Handler of reconnect timer created by protocol_disconnected()
* * @param type - 0 - If timer should do his job
* 1 - If timer'll be destroy, and handler should free his data * @param s - session to reconnect * * @return -1 [TEMPORARY TIMER] */ static TIMER_SESSION(protocol_reconnect_handler) { if (type == 1) return 0; if (!s || s->connected) return -1; debug("protocol_reconnect_handler() reconnecting session %s\n", s->uid); command_exec(NULL, s, ("/connect"), 0); return -1; } /** * protocol_disconnected() * * Handler for PROTOCOL_DISCONNECTED
* When session notify core about disconnection we do here:
* - clear all user status, presence, resources details. @sa userlist_clear_status()
* - update s->last_conn state, and set s->connected to 0
* - check if disconnect @a type was either EKG_DISCONNECT_NETWORK: or EKG_DISCONNECT_FAILURE: and * if yes, create reconnect timer (if user set auto_reconnect variable)
* - display notify through UI-plugin * * @note About different types [@a type] of disconnections:
* - EKG_DISCONNECT_USER - when user do /disconnect [with reason, in @a reason we should have param of /disconnect command][without reconnection]
* - EKG_DISCONNECT_NETWORK - when smth is wrong with network... (read: when recv() fail, or send() or SSL wrappers for rcving/sending data fail with -1 and with bad errno) * [with reason describiny why we fail (strerror() is good here)][with reconnection]
* - EKG_DISCONNECT_FORCED - when server force us to disconnection. [without reason][without reconnection]
* - EKG_DISCONNECT_FAILURE - when we fail to connect to server (read: when we fail connect session, after /connect) * [with reason describiny why we fail (strerror() is good here)][with reconnection]
* - EKG_DISCONNECT_STOPPED - when user do /disconnect during connection [without reason] [without reconnection]
* * @param ap 1st param: (char *) session - session uid which goes disconnect * @param ap 2nd param: (char *) reason - reason why session goes disconnect.. It's reason specifed by user if EKG_DISCONNECT_USER, else * string with error description like from: strerror().. [if EKG_DISCONNECT_FAILURE] * @param ap 3rd param: (int) type - type of disconnection one of: * [EKG_DISCONNECT_USER, EKG_DISCONNECT_NETWORK, EKG_DISCONNECT_FORCED, EKG_DISCONNECT_FAILURE, EKG_DISCONNECT_STOPPED] * * @param data NULL * * @return 0 * */ static QUERY(protocol_disconnected) { char *session = *(va_arg(ap, char **)); char *reason = *(va_arg(ap, char **)); int type = *(va_arg(ap, int*)); session_t *s = session_find(session); userlist_clear_status(s, NULL); if (s) { if (s->connected) { int one = 1; s->last_conn = time(NULL); s->connected = 0; query_emit(NULL, "session-event", &s, &one); /* notify UI */ } else s->connecting = 0; command_exec(NULL, s, "/session --unlock", 1); } switch (type) { case EKG_DISCONNECT_NETWORK: case EKG_DISCONNECT_FAILURE: { int tmp; if (type == EKG_DISCONNECT_NETWORK) print("conn_broken", session_name(s), reason); else print("conn_failed", reason, session_name(s)); if (s && (tmp = session_int_get(s, "auto_reconnect")) && tmp != -1 && timer_find_session(s, "reconnect") == NULL) timer_add_session(s, "reconnect", tmp, 0, protocol_reconnect_handler); break; } case EKG_DISCONNECT_USER: if (reason) print("disconnected_descr", reason, session_name(s)); else print("disconnected", session_name(s)); /* We don't use session_unidle(), because: * 1) we don't want to print 'Auto back: ...' - that'd be stupid * 2) we don't want to risk if some _autoback could make magic at this state of connection */ if (s && s->autoaway) session_status_set(s, EKG_STATUS_AUTOBACK); if (s) s->activity = 0; /* mark we'd like PROTOCOL_CONNECTED to set activity */ break; case EKG_DISCONNECT_FORCED: print("conn_disconnected", session_name(s)); break; case EKG_DISCONNECT_STOPPED: print("conn_stopped", session_name(s)); break; default: print("generic_error", "protocol_disconnect internal error, report to authors"); break; } return 0; } int protocol_disconnected_emit(const session_t *s, const char *reason, int type) { char *session = xstrdup(s->uid); char *reason_ro = xstrdup(reason); int result = query_emit(NULL, "protocol-disconnected", &session, &reason_ro, &type); xfree(session); xfree(reason_ro); return result; } /** * protocol_connected() * * Handler for PROTOCOL_CONNECTED
* When session notify core about connection we do here:
* - If we have ourselves on the userlist. It update status and description
* - Display notify through UI-plugin
* - If we have messages in session queue, than send it and display info.
* - Update last_conn state and set connected state to 1.
* - Remove "reconnect" timer. * * @param ap 1st param: (char *) session - session uid which goes connected. * @param data NULL * * @return 0 */ static QUERY(protocol_connected) { char *session = *(va_arg(ap, char**)); session_t *s = session_find(session); const char *descr = session_descr_get(s); ekg_update_status(s); if (descr) print("connected_descr", descr, session_name(s)); else print("connected", session_name(s)); if (s) { int two = 2; s->last_conn = time(NULL); if (!s->activity) /* someone asks us to set activity */ s->activity = s->last_conn; s->connecting = 0; s->connected = 1; timer_remove_session(s, "reconnect"); query_emit(NULL, "session-event", &s, &two); /* Notify UI */ } if (!msg_queue_flush(session)) print("queue_flush", session_name(s)); return 0; } int protocol_connected_emit(const session_t *s) { char *session = xstrdup(s->uid); int result = query_emit(NULL, "protocol-connected", &session); xfree(session); return result; } /* * protocol_status() * * obsuga zapytania "protocol-status" wysyanego przez pluginy protokow. */ static QUERY(protocol_status) { char **__session = va_arg(ap, char**), *session = *__session; char **__uid = va_arg(ap, char**), *uid = *__uid; int status = *(va_arg(ap, int*)); char **__descr = va_arg(ap, char**), *descr = *__descr; time_t when = *(va_arg(ap, time_t*)); ekg_resource_t *r = NULL; userlist_t *u; session_t *s; int st; /* status u->status || r->status */ char *de; /* descr u->descr || r->descr */ int ignore_level; int ignore_status, ignore_status_descr, ignore_events, ignore_notify; int sess_notify; if (!(s = session_find(session))) return 0; sess_notify = session_int_get(s, "display_notify"); /* we are checking who user we know */ if (!(u = userlist_find(s, uid))) { if (config_auto_user_add && xstrncmp(uid, session, xstrlen(session)) ) { char *tmp = xstrdup(uid); char *p = xstrchr(tmp, '/'); if (p) *p = 0; u = userlist_add(s, tmp, tmp); xfree(tmp); } if (!u) { if ((sess_notify == -1 ? config_display_notify : sess_notify) & 4) { const char *format = ekg_status_label(status, descr, "status_"); print_info(uid, s, format, format_user(s, uid), NULL, session_name(s), descr); } return 0; } } ignore_level = ignored_check(s, uid); ignore_status = ignore_level & IGNORE_STATUS; ignore_status_descr = ignore_level & IGNORE_STATUS_DESCR; ignore_events = ignore_level & IGNORE_EVENTS; ignore_notify = ignore_level & IGNORE_NOTIFY; /* znajdz resource... */ if (u->resources) { char *res = xstrchr(uid, '/'); /* resource ? */ if (res) r = userlist_resource_find(u, res+1); } /* status && descr ? */ if (r) { /* resource status && descr */ st = r->status; de = r->descr; } else { /* global status && descr */ st = u->status; de = u->descr; } /* jeli te same stany... i te same opisy (lub brak opisu), ignoruj */ if ((status == st) && !xstrcmp(descr, de)) return 0; /* jeli kto nam znika, zapamitajmy kiedy go widziano */ if (!u->resources && !EKG_STATUS_IS_NA(u->status) && EKG_STATUS_IS_NA(status)) u->last_seen = when ? when : time(NULL); /* XXX doda events_delay */ /* jeli dostpny lub zajty, dopisz to taba. jeli niedostpny, usu */ if (EKG_STATUS_IS_AVAIL(status) && config_completion_notify && u->nickname) tabnick_add(u->nickname); if (!EKG_STATUS_IS_AWAY(status) && (config_completion_notify & 4) && u->nickname) tabnick_add(u->nickname); if (EKG_STATUS_IS_NA(status) && (config_completion_notify & 2) && u->nickname) tabnick_remove(u->nickname); /* jeli mao wana zmiana stanu... * XXX someone can tell me what this should do, 'cos I can't understand the way it's written? */ /* z dokumentacji, co powinno robic: * warto 2 wywietla tylko zmiany z niedostpnego na dostpny i na odwrt. */ if (((sess_notify == -1 ? config_display_notify : sess_notify) & 2) && !(EKG_STATUS_IS_NA(st) ^ EKG_STATUS_IS_NA(status))) goto notify_plugins; /* ignorowanie statusu - nie wywietlamy, ale pluginy niech robi co chc */ if (ignore_status || ignore_notify) goto notify_plugins; /* nie zmieni si status, zmieni si opis */ if (ignore_status_descr && (status == st) && xstrcmp(descr, de)) goto notify_plugins; /* daj zna dwikiem... */ if (config_beep && config_beep_notify) query_emit(NULL, "ui-beep"); /* ...i muzyczk */ if (config_sound_notify_file) play_sound(config_sound_notify_file); /* wywietla na ekranie? */ if (!((sess_notify == -1 ? config_display_notify : sess_notify) & 3)) goto notify_plugins; /* poka */ if (u->nickname) { const char *format = ekg_status_label(status, ignore_status_descr ? NULL : descr, "status_"); print_info(u->nickname, s, format, format_user(s, uid), get_user_name(u), session_name(s), descr); } notify_plugins: if (!EKG_STATUS_IS_NA(st)) { u->last_status = st; xfree(u->last_descr); u->last_descr = xstrdup(de); if (EKG_STATUS_IS_NA(status) && !ignore_events) query_emit(NULL, "event-offline", __session, __uid); } else if (!EKG_STATUS_IS_NA(status) && !ignore_events) query_emit(NULL, "event-online", __session, __uid); if (!ignore_status) { if (r) { r->status = status; } if (u->resources) { /* get higest prio status */ u->status = u->resources->status; } else { u->status = status; } } if (xstrcasecmp(de, descr) && !ignore_events) query_emit(NULL, "event-descr", __session, __uid, __descr); if (!ignore_status && !ignore_status_descr) { if (r) { xfree(r->descr); r->descr = xstrdup(descr); } xfree(u->descr); u->descr = xstrdup(u->resources ? u->resources->descr : descr); /* get highest prio descr */ de = xstrdup(u->descr); if (de) { char *p; while ((p=xstrstr(de, "\r\n"))) memmove(p, p+1, xstrlen(p)); /* dos2unix */ while ((p=xstrchr(de,'\n'))) *p = ' '; /* all in 1 line */ } xfree(u->descr1line); u->descr1line = de; if (!u->resources || u->resources == r) u->status_time = when ? when : time(NULL); } query_emit(NULL, "userlist-changed", __session, __uid); /* Currently it behaves like event means grouped statuses, * i.e. EVENT_AVAIL is for avail&ffc * EVENT_AWAY for away&xa&dnd * ... */ if (!ignore_events) { if (EKG_STATUS_IS_AVAIL(status)) query_emit(NULL, "event-avail", __session, __uid); else if (EKG_STATUS_IS_AWAY(status)) query_emit(NULL, "event-away", __session, __uid); else if (EKG_STATUS_IS_NA(status)) query_emit(NULL, "event-na", __session, __uid); } return 0; } int protocol_status_emit(const session_t *s, const char *uid, int status, char *descr, time_t when) { char *session = xstrdup(s->uid); char *uid_ro = xstrdup(uid); char *descr_ro = xstrdup(descr); int result = query_emit(NULL, "protocol-status", &session, &uid_ro, &status, &descr_ro, &when); xfree(session); xfree(uid_ro); xfree(descr_ro); return result; } /* * message_print() * * wywietla wiadomo w odpowiednim oknie i w odpowiedniej postaci. * * zwraca target */ char *message_print(const char *session, const char *sender, const char **rcpts, const char *__text, const guint32 *format, time_t sent, int mclass, const char *seq, int dobeep, int secure) { char *class_str, timestamp[100], *text = xstrdup(__text); char *securestr = NULL; const char *target = sender, *user; time_t now; session_t *s = session_find(session); struct conference *c = NULL; int empty_theme = 0, is_me = 0, to_me = 1, activity = 0, separate = 0; if (mclass & EKG_MSGCLASS_NOT2US) { to_me = 0; mclass &= ~EKG_MSGCLASS_NOT2US; } if (mclass & EKG_NO_THEMEBIT) { empty_theme = 1; mclass &= ~EKG_NO_THEMEBIT; } switch (mclass) { case EKG_MSGCLASS_SENT: case EKG_MSGCLASS_SENT_CHAT: if (/*config_display_me && */ !xstrncmp(text, "/me ", 4)) { class_str = "sent_me"; is_me = 1; } else class_str = "sent"; target = (rcpts) ? rcpts[0] : NULL; break; case EKG_MSGCLASS_CHAT: if (/*config_display_me && */ !xstrncmp(text, "/me ", 4)) { class_str = "chat_me"; is_me = 1; } else class_str = "chat"; break; case EKG_MSGCLASS_SYSTEM: class_str = "system"; target = "__status"; break; case EKG_MSGCLASS_LOG: class_str = "log"; break; case EKG_MSGCLASS_SENT_LOG: class_str = "sent_log"; target = (rcpts) ? rcpts[0] : NULL; break; default: if (mclass != EKG_MSGCLASS_MESSAGE) debug("[message_print] got unexpected mclass = %d\n", mclass); class_str = "message"; } /* dodajemy kolorki do tekstu */ if (format) { string_t s = string_init(""); const char *attrmap; int i; if (config_display_color_map && xstrlen(config_display_color_map) == 8) attrmap = config_display_color_map; else attrmap = "nTgGbBrR"; for (i = 0; text[i]; i++) { guint32 f = format[i]; if (i == 0 || f != format[i - 1]) { char attr = 'n'; if ((f & EKG_FORMAT_COLOR)) { attr = color_map(f & EKG_FORMAT_B_MASK, (f & EKG_FORMAT_G_MASK) >> 8, (f & EKG_FORMAT_B_MASK) >> 16); if (attr == 'k') attr = 'n'; } if ((f & ~(EKG_FORMAT_COLOR | EKG_FORMAT_RGB_MASK))) { if ((f & EKG_FORMAT_COLOR)) attr = toupper(attr); else attr = attrmap[(f >> 25) & 7]; } if (attr=='N') attr='T'; string_append_c(s, '%'); string_append_c(s, attr); } if ((text[i] == '/') && (text[i+1] == '|')) /* /| set margin */ if ((i == 0) || (text[i-1] != '/')) string_append_c(s, '/'); if (text[i] == '%') string_append_c(s, '%'); string_append_c(s, text[i]); } xfree(text); text = format_string(s->str); string_free(s, 1); } /* wyznaczamy odstp czasu midzy wysaniem wiadomoci, a chwil * obecn, eby wybra odpowiedni format timestampu. */ { char tmp[100], *timestamp_type; struct tm *tm_msg; int tm_now_day; /* it's localtime(&now)->tm_yday */ now = time(NULL); tm_now_day = localtime(&now)->tm_yday; tm_msg = localtime(&sent); if (sent - config_time_deviation <= now && now <= sent + config_time_deviation) timestamp_type = "timestamp_now"; else if (tm_now_day == tm_msg->tm_yday) timestamp_type = "timestamp_today"; else timestamp_type = "timestamp"; snprintf(tmp, sizeof(tmp), "%s_%s", class_str, timestamp_type); if (!strftime(timestamp, sizeof(timestamp), format_find(tmp), tm_msg) && xstrlen(format_find(tmp))>0) xstrcpy(timestamp, "TOOLONG"); } /* if there is a lot of recipients, conference should be made */ { int recipients_count = g_strv_length((char **) rcpts); if ((mclass < EKG_MSGCLASS_SENT) && recipients_count > 0) { c = conference_find_by_uids(s, sender, rcpts, recipients_count, 0); if (!c) { string_t tmp = string_init(NULL); int first = 0, i; for (i = 0; i < recipients_count; i++) { if (first++) string_append_c(tmp, ','); string_append(tmp, rcpts[i]); } string_append_c(tmp, ' '); string_append(tmp, sender); c = conference_create(s, tmp->str); string_free(tmp, 1); } else if (c->ignore) { xfree(text); return NULL; } if (c) { target = c->name; class_str = "conference"; } } } /* XXX: I personally think this should be moved outta here * I don't think that beeping should be considered as 'printing' */ /* daj zna dwikiem i muzyczk */ if (mclass == EKG_MSGCLASS_CHAT) { if (config_beep && config_beep_chat && dobeep) query_emit(NULL, "ui-beep"); if (config_sound_chat_file && dobeep) play_sound(config_sound_chat_file); } else if (mclass == EKG_MSGCLASS_MESSAGE) { if (config_beep && config_beep_msg && dobeep) query_emit(NULL, "ui-beep"); if (config_sound_msg_file && dobeep) play_sound(config_sound_msg_file); } else if (mclass == EKG_MSGCLASS_SYSTEM && config_sound_sysmsg_file) play_sound(config_sound_sysmsg_file); if (config_last & 3 && (mclass < EKG_MSGCLASS_SENT)) last_add(0, sender, now, sent, text); user = (mclass < EKG_MSGCLASS_SENT) ? format_user(s, sender) : session_format_n(sender); if (config_emoticons && text) { char *tmp = emoticon_expand(text); xfree(text); text = tmp; } if (empty_theme) class_str = "empty"; if (secure) securestr = format_string(format_find("secure")); if (mclass == EKG_MSGCLASS_LOG || mclass == EKG_MSGCLASS_SENT_LOG) separate = 1; if ( (mclass == EKG_MSGCLASS_CHAT || mclass == EKG_MSGCLASS_SENT_CHAT) || (!(config_make_window & 4) && (mclass == EKG_MSGCLASS_MESSAGE || mclass == EKG_MSGCLASS_SENT)) ) { activity = to_me ? EKG_WINACT_IMPORTANT : EKG_WINACT_MSG; separate = 1; } print_window(target, s, activity, separate, class_str, user, timestamp, (is_me ? text+4 : text), /* XXX, get_uid() get_nickname() */ (mclass >= EKG_MSGCLASS_SENT ? (is_me && config_nickname ? config_nickname : session_alias_uid(s)) : get_nickname(s, sender)), (mclass >= EKG_MSGCLASS_SENT ? s->uid : get_uid(s, sender)), (secure ? securestr : "")); xfree(text); xfree(securestr); return xstrdup(target); } /* * protocol_message() */ static QUERY(protocol_message) { char *session = *(va_arg(ap, char**)); char *uid = *(va_arg(ap, char**)); char **rcpts = *(va_arg(ap, char***)); char **ptext = (va_arg(ap, char**)); guint32 *format= *(va_arg(ap, guint32**)); time_t sent = *(va_arg(ap, time_t*)); int mclass = *(va_arg(ap, int*)); char *seq = *(va_arg(ap, char**)); int dobeep = *(va_arg(ap, int*)); int secure = *(va_arg(ap, int*)); session_t *session_class = session_find(session); userlist_t *userlist = userlist_find(session_class, uid); char *target = NULL; int empty_theme = 0; int our_msg; if (ignored_check(session_class, uid) & IGNORE_MSG) return -1; /* display blinking */ if (config_display_blinking && userlist && (mclass < EKG_MSGCLASS_SENT) && (!rcpts || !rcpts[0])) { int oldstate = userlist->blink; if (config_make_window && xstrcmp(get_uid(session_class, window_current->target), get_uid(session_class, uid))) userlist->blink = 1; else if (!config_make_window) { window_t *w; /* * now we are checking if there is some window with query for this * user */ w = window_find_s(session_class, uid); if (w ? (window_current->id != w->id) : (window_current->id != 1)) userlist->blink = 1; } if (oldstate != userlist->blink) query_emit(NULL, "userlist-changed", &session, &uid); } if (mclass & EKG_NO_THEMEBIT) { mclass &= ~EKG_NO_THEMEBIT; empty_theme = 1; } our_msg = (mclass >= EKG_MSGCLASS_SENT); /* there is no need to decode our messages */ if (!our_msg && !empty_theme) { /* empty_theme + decrpyt? i don't think so... */ char *___session = xstrdup(session); char *___sender = xstrdup(uid); char *___message = xstrdup(*ptext); int ___decrypted = 0; query_emit(NULL, "message-decrypt", &___session, &___sender, &___message, &___decrypted, NULL); if (___decrypted) { xfree(*ptext); *ptext = ___message; ___message = NULL; secure = 1; } xfree(___session); xfree(___sender); xfree(___message); } if (our_msg) query_emit(NULL, "protocol-message-sent", &session, &(rcpts[0]), ptext); else query_emit(NULL, "protocol-message-received", &session, &uid, &rcpts, ptext, &format, &sent, &mclass, &seq, &secure); query_emit(NULL, "protocol-message-post", &session, &uid, &rcpts, ptext, &format, &sent, &mclass, &seq, &secure); /* show it ! */ if (!(our_msg && !config_display_sent)) { if (empty_theme) mclass |= EKG_NO_THEMEBIT; if (!(target = message_print(session, uid, (const char**) rcpts, *ptext, format, sent, mclass, seq, dobeep, secure))) return -1; } /* jeeli nie mamy podanego uid'u w licie kontaktw to trzeba go dopisa do listy dopenianych */ if (!userlist && !our_msg) /* don't add us to tabnick */ tabnick_add(uid); if (!userlist && xstrcasecmp(session_class->uid, uid) && session_int_get(session_class, "auto_find") >= 1) { list_t l; int do_find = 1, i; for (l = autofinds, i = 0; l; l = l->next, i++) { char *d = l->data; if (!xstrcmp(d, uid)) { do_find = 0; break; } } if (do_find) { if (i == auto_find_limit) { debug("// autofind reached %d limit, removing the oldest uin: %d\n", auto_find_limit, *((char *)autofinds->data)); list_remove(&autofinds, autofinds->data, 1); } list_add(&autofinds, xstrdup(uid)); command_exec_format(target, session_class, 0, ("/find %s"), uid); } } xfree(target); return 0; } int protocol_message_emit(const session_t *s, const char *uid, char **rcpts, const char *text, const guint32 *format, time_t sent, int mclass, const char *seq, int dobeep, int secure) { char *session = xstrdup(s->uid); char *uid_ro = xstrdup(uid); char *text_ro = xstrdup(text); char *seq_ro = xstrdup(seq); /* XXX, rcpts_ro, format_ro */ int result = query_emit(NULL, "protocol-message", &session, &uid_ro, &rcpts, &text_ro, &format, &sent, &mclass, &seq_ro, &dobeep, &secure); xfree(session); xfree(uid_ro); xfree(text_ro); xfree(seq_ro); return result; } /** * protocol_message_ack() * * Handler for PROTOCOL_MESSAGE_ACK * When session notifies core about receiving acknowledgement for our message, we:
* - Remove message with given sequence id (@a seq) from msgqueue @sa msg_queue_remove_seq()
* - If corresponding @a config_display_ack variable bit is set, then display notification through UI-plugin * * @note About different types of confirmations (@a __status):
* - EKG_ACK_DELIVERED - when message was successfully delivered to user
* - EKG_ACK_QUEUED - when user is somewhat unavailable and server confirmed to accept the message for later delivery
* - EKG_ACK_DROPPED - when user or server rejected to deliver our message (forbidden content?) and it was dropped; further retries will probably fail, if second side doesn't perform some kind of action (e.g. add us to roster in GG)
* - EKG_ACK_TEMPFAIL - when server failed temporarily to deliver our message, but encourages us to try again later (e.g. message queue full)
* - EKG_ACK_UNKNOWN - when it's not clear what happened with our message
* * @todo Should we remove msg from msgqueue only when sequenceid and session and rcpt matches? * I think it's buggy cause user at jabber can send us acknowledge of message * which we never send, but if seq match with other message, which wasn't send (because session was for example disconnected) * we remove that messageid, and than we'll never send it, and we'll never know that we don't send it. * * @param ap 1st param: (char *) session - session which send this notify * @param ap 2nd param: (char *) rcpt - user uid who confirm receiving messages * @param ap 3rd param: (char *) seq - sequence id of message * @param ap 4th param: int __status - type of confirmation; one of: [EKG_ACK_DELIVERED, EKG_ACK_QUEUED, EKG_ACK_DROPPED, EKG_ACK_TEMPFAIL, EKG_ACK_UNKNOWN] * * @param data NULL * * @return 0 */ static QUERY(protocol_message_ack) { const char *ackformats[] = {"ack_delivered", "ack_queued", "ack_filtered", "ack_tempfail", "ack_unknown"}; char *session = *(va_arg(ap, char **)); char *rcpt = *(va_arg(ap, char **)); char *seq = *(va_arg(ap, char **)); int __status = *(va_arg(ap, int *)); session_t *s = session_find(session); userlist_t *u = userlist_find(s, rcpt); const char *target = (u && u->nickname) ? u->nickname : rcpt; msg_queue_remove_seq(seq); if ((__status >= 0) && (__status < EKG_ACK_MAX) && (config_display_ack & (1 << __status))) print_info(target, s, ackformats[__status], format_user(s, rcpt)); return 0; } int protocol_message_ack_emit(const session_t *s, const char *rcpt, const char *seq, int status) { char *session = xstrdup(s->uid); char *rcpt_ro = xstrdup(rcpt); char *seq_ro = xstrdup(seq); int result = query_emit(NULL, "protocol-message-ack", &session, &rcpt_ro, &seq_ro, &status); xfree(session); xfree(rcpt_ro); xfree(seq_ro); return result; } static QUERY(protocol_xstate) { /* state contains xstate bits, which should be set, offstate those, which should be cleared */ char **__session = va_arg(ap, char**), *session = *__session; char **__uid = va_arg(ap, char**), *uid = *__uid; int state = *(va_arg(ap, int*)); int offstate = *(va_arg(ap, int*)); session_t *s; userlist_t *u; window_t *w; if (!(s = session_find(session))) return 0; if ((w = window_find_s(s, uid))) { if (offstate & EKG_XSTATE_TYPING) w->in_typing = 0; else if (state & EKG_XSTATE_TYPING) w->in_typing = 1; else goto xs_userlist; query_emit(NULL, "ui-window-act-changed", &w); /* XXX, UI_WINDOW_TYPING_CHANGED? :> */ } xs_userlist: if ((u = userlist_find(s, uid)) || (config_auto_user_add && (u = userlist_add(s, uid, uid)))) { if (offstate & EKG_XSTATE_TYPING) u->typing = 0; else if (state & EKG_XSTATE_TYPING) u->typing = 1; else return 0; query_emit(NULL, "userlist-changed", __session, __uid); } return 0; } int protocol_xstate_emit(const session_t *s, const char *uid, int state, int offstate) { char *session = xstrdup(s->uid); char *uid_ro = xstrdup(uid); int result = query_emit(NULL, "protocol-xstate", &session, &uid_ro, &state, &offstate); xfree(session); xfree(uid_ro); return result; } /* * protocol_uid() * * return saprintf("%s:%s", proto, target); */ char *protocol_uid(const char *proto, const char *target) { /* XXX, simplify some code inside plugins? * * if (!xstrncmp(target, proto, xstrlen(proto))) * return xstrdup(target); */ return saprintf("%s:%s", proto, target); } static LIST_FREE_ITEM(dcc_free_item, dcc_t *) { if (data->close_handler) data->close_handler(data); xfree(data->uid); xfree(data->filename); } DYNSTUFF_LIST_DECLARE(dccs, dcc_t, dcc_free_item, static __DYNSTUFF_LIST_ADD, /* dccs_add() */ static __DYNSTUFF_LIST_REMOVE_SAFE, /* dccs_remove() */ __DYNSTUFF_NODESTROY) /* XXX dccs_destroy() XXX, we don't care? */ /** * dcc_add() * */ dcc_t *dcc_add(session_t *session, const char *uid, dcc_type_t type, void *priv) { dcc_t *d; int id = 1, id_ok; do { id_ok = 1; for (d = dccs; d; d = d->next) { if (d->id == id) { id++; id_ok = 0; break; } } if (id < 1) { /* protect from posibble deadlock */ debug_error("dcc_add() too many dcc's id < 1...\n"); return NULL; } } while (!id_ok); d = xmalloc(sizeof(dcc_t)); d->session = session; d->uid = xstrdup(uid); d->type = type; d->priv = priv; d->started = time(NULL); d->id = id; dccs_add(d); return d; } int dcc_close(dcc_t *d) { if (!d) return -1; dccs_remove(d); return 0; } PROPERTY_MISC(dcc, close_handler, dcc_close_handler_t, NULL) PROPERTY_STRING(dcc, filename) PROPERTY_INT(dcc, offset, int) PROPERTY_INT(dcc, size, int) PROPERTY_STRING_GET(dcc, uid) PROPERTY_INT_GET(dcc, id, int) PROPERTY_PRIVATE(dcc) PROPERTY_INT_GET(dcc, started, time_t) PROPERTY_INT(dcc, active, int) PROPERTY_INT(dcc, type, dcc_type_t) /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: * vim: noet */ ekg2-0.4~pre+20120506.1/ekg/protocol.h000066400000000000000000000121051175142753400167340ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2003 Wojtek Kaniewski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __EKG_PROTOCOL_H #define __EKG_PROTOCOL_H #include "ekg2-config.h" #include #include "dynstuff.h" #include "sessions.h" #include #include #include /* size_t */ #include /* off_t */ #ifdef __cplusplus extern "C" { #endif #define EKG_FORMAT_RGB_MASK 0x00ffffffL /* 0x00BBGGRR */ #define EKG_FORMAT_R_MASK 0x00ff0000L #define EKG_FORMAT_G_MASK 0x0000ff00L #define EKG_FORMAT_B_MASK 0x000000ffL #define EKG_FORMAT_COLOR 0x01000000L #define EKG_FORMAT_BOLD 0x02000000L #define EKG_FORMAT_ITALIC 0x04000000L #define EKG_FORMAT_UNDERLINE 0x08000000L #define EKG_FORMAT_REVERSE 0x10000000L #define EKG_NO_THEMEBIT 256 enum msgack_t { EKG_ACK_DELIVERED = 0, /* message delivered successfully */ EKG_ACK_QUEUED, /* message queued for delivery */ EKG_ACK_DROPPED, /* message rejected 'permamently' */ EKG_ACK_TEMPFAIL, /* temporary delivery failure */ EKG_ACK_UNKNOWN, /* delivery status unknown */ EKG_ACK_MAX /* we don't want to read after array */ }; typedef enum { EKG_DISCONNECT_USER = 0, /* user-engaged disconnect */ EKG_DISCONNECT_NETWORK, /* network problems */ EKG_DISCONNECT_FORCED, /* server forced to disconnect */ EKG_DISCONNECT_FAILURE, /* connecting failed */ EKG_DISCONNECT_STOPPED /* connecting canceled */ } disconnect_t; #define EKG_NO_BEEP 0 #define EKG_TRY_BEEP 1 typedef enum { /* recv */ EKG_MSGCLASS_MESSAGE = 0, /* single message */ EKG_MSGCLASS_CHAT, /* chat message */ EKG_MSGCLASS_SYSTEM, /* system message */ EKG_MSGCLASS_LOG, /* old logged message (used by logsqlite 'last_print_on_open') */ EKG_MSGCLASS_NOT2US = 16, /* message is not to us */ /* sent */ EKG_MSGCLASS_SENT = 32, /* single sent message */ EKG_MSGCLASS_SENT_CHAT, /* chat sent message */ EKG_MSGCLASS_SENT_LOG, /* old logged message (used by logsqlite 'last_print_on_open') */ /* priv */ EKG_MSGCLASS_PRIV_STATUS= 64 /* used by logs */ } msgclass_t; #ifndef EKG2_WIN32_NOFUNCTION void protocol_init(); char *message_print(const char *session, const char *sender, const char **rcpts, const char *text, const guint32 *format, time_t sent, int mclass, const char *seq, int dobeep, int secure); int protocol_connected_emit(const session_t *s); int protocol_disconnected_emit(const session_t *s, const char *reason, int type); int protocol_message_ack_emit(const session_t *s, const char *rcpt, const char *seq, int status); int protocol_message_emit(const session_t *s, const char *uid, char **rcpts, const char *text, const guint32 *format, time_t sent, int mclass, const char *seq, int dobeep, int secure); int protocol_status_emit(const session_t *s, const char *uid, int status, char *descr, time_t when); int protocol_xstate_emit(const session_t *s, const char *uid, int state, int offstate); char *protocol_uid(const char *proto, const char *target); /* XXX ? */ #endif typedef enum { DCC_NONE = 0, DCC_SEND, DCC_GET, DCC_VOICE } dcc_type_t; struct dcc_s; typedef void (*dcc_close_handler_t)(struct dcc_s *); typedef struct dcc_s { struct dcc_s *next; session_t *session; /* ktora sesja? */ char *uid; /* z kim poczenie */ dcc_type_t type; /* rodzaj poczenia */ int id; /* numer poczenia */ void *priv; /* dane prywatne pluginu */ dcc_close_handler_t close_handler; /* obsuga /dcc close */ unsigned int active : 1; /* czy poczono? */ time_t started; /* kiedy utworzono? */ char *filename; /* nazwa pliku */ size_t size; /* rozmiar pliku */ off_t offset; /* ile ju wykonano */ } dcc_t; #ifndef EKG2_WIN32_NOFUNCTION dcc_t *dcc_add(session_t *session, const char *uid, dcc_type_t type, void *priv); int dcc_close(dcc_t *d); int dcc_private_set(dcc_t *, void *); void *dcc_private_get(dcc_t *); int dcc_close_handler_set(dcc_t *, dcc_close_handler_t); dcc_close_handler_t dcc_close_handler_get(dcc_t *); const char *dcc_uid_get(dcc_t *); int dcc_id_get(dcc_t *); time_t dcc_started_get(dcc_t *); int dcc_active_set(dcc_t *, int); int dcc_active_get(dcc_t *); int dcc_offset_set(dcc_t *, int); int dcc_offset_get(dcc_t *); int dcc_size_set(dcc_t *, int); int dcc_size_get(dcc_t *); int dcc_filename_set(dcc_t *, const char *); const char *dcc_filename_get(dcc_t *); dcc_type_t dcc_type_get(dcc_t *); extern dcc_t *dccs; #endif #ifdef __cplusplus } #endif #endif /* __EKG_PROTOCOL_H */ /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/ekg/queries.c000066400000000000000000000211711175142753400165460ustar00rootroot00000000000000#include "queries.h" #include /* list of known queries */ static const query_def_t core_query_list[] = { { NULL, "day-changed", 0, { /* XXX: struct tm *, struct tm * */ QUERY_ARG_END } }, { NULL, "status-show", 0, { QUERY_ARG_CHARP, /* session uid */ QUERY_ARG_END } }, { NULL, "plugin-print-version", 0, { QUERY_ARG_END } }, /* no params */ { NULL, "set-vars-default", 0, { QUERY_ARG_END } }, /* no params */ { NULL, "variable-changed", 0, { QUERY_ARG_CHARP, /* variable */ QUERY_ARG_END } }, { NULL, "binding-command", 0, { /* XXX */ QUERY_ARG_END } }, { NULL, "binding-default", 0, { /* XXX */ QUERY_ARG_END } }, { NULL, "binding-set", 0, { /* XXX */ QUERY_ARG_END } }, { NULL, "event-added", 0, { QUERY_ARG_CHARP, /* event name */ QUERY_ARG_END } }, { NULL, "event-removed", 0, { /* XXX, never used */ QUERY_ARG_END } }, { NULL, "message-encrypt", 0, { /* XXX */ QUERY_ARG_END } }, { NULL, "message-decrypt", 0, { /* XXX */ QUERY_ARG_END } }, { NULL, "metacontact-added", 0, { QUERY_ARG_CHARP, /* metacontact name */ QUERY_ARG_END } }, { NULL, "metacontact-item-added", 0, { /* XXX */ QUERY_ARG_END } }, { NULL, "metacontact-item-removed", 0, { /* XXX */ QUERY_ARG_END } }, { NULL, "metacontact-removed", 0, { QUERY_ARG_CHARP, /* metacontact name */ QUERY_ARG_END } }, { NULL, "protocol-message-sent", 0, { QUERY_ARG_CHARP, /* session */ QUERY_ARG_CHARP, /* uid */ QUERY_ARG_CHARP, /* text */ QUERY_ARG_END } }, { NULL, "protocol-message-received", 0, { QUERY_ARG_CHARP, /* session uid */ QUERY_ARG_CHARP, /* uid */ QUERY_ARG_CHARPP, /* rcpts */ QUERY_ARG_CHARP, /* text */ QUERY_ARG_UINT, /* guint32 */ /* format */ QUERY_ARG_UINT, /* time_t */ /* sent */ QUERY_ARG_INT, /* mclass */ QUERY_ARG_CHARP, /* seq */ QUERY_ARG_INT, /* secure */ QUERY_ARG_END } }, { NULL, "protocol-message-post", 0, { QUERY_ARG_CHARP, /* session uid */ QUERY_ARG_CHARP, /* uid */ QUERY_ARG_CHARPP, /* rcpts */ QUERY_ARG_UINT, /* guint32 */ /* format */ QUERY_ARG_UINT, /* time_t */ /* sent */ QUERY_ARG_INT, /* mclass */ QUERY_ARG_CHARP, /* seq */ QUERY_ARG_INT, /* secure */ QUERY_ARG_END } }, { NULL, "event-away", 0, { QUERY_ARG_CHARP, /* session uid */ QUERY_ARG_CHARP, /* uid */ QUERY_ARG_END } }, { NULL, "event-avail", 0, { /* XXX, emited, but noone connect to this. */ QUERY_ARG_CHARP, /* session uid */ QUERY_ARG_CHARP, /* uid */ QUERY_ARG_END } }, { NULL, "event-descr", 0, { QUERY_ARG_CHARP, /* session uid */ QUERY_ARG_CHARP, /* uid */ QUERY_ARG_CHARP, /* descr */ QUERY_ARG_END } }, { NULL, "event-online", 0, { QUERY_ARG_CHARP, /* session uid */ QUERY_ARG_CHARP, /* uid */ QUERY_ARG_END } }, { NULL, "event-na", 0, { QUERY_ARG_CHARP, /* session uid */ QUERY_ARG_CHARP, /* uid */ QUERY_ARG_END } }, { NULL, "userlist-added", 0, { /* XXX, we need here a session->uid too (?) */ QUERY_ARG_CHARP, /* uid */ QUERY_ARG_CHARP, /* nickname */ QUERY_ARG_INT, /* quiet */ QUERY_ARG_END } }, { NULL, "userlist-changed", 0, { QUERY_ARG_CHARP, /* session uid */ QUERY_ARG_CHARP, /* uid */ QUERY_ARG_END } }, { NULL, "userlist-removed", 0, { /* XXX, we need here a session->uid too (?) */ QUERY_ARG_CHARP, /* nickname or uid */ QUERY_ARG_CHARP, /* uid */ QUERY_ARG_END } }, { NULL, "userlist-renamed", 0, { /* XXX */ QUERY_ARG_END } }, { NULL, "userlist-info", 0, { /* XXX */ QUERY_ARG_END } }, { NULL, "userlist-privhandle", 0, { QUERY_ARG_USERLIST, /* userlist_t */ QUERY_ARG_INT, /* function */ /* optional things? */ QUERY_ARG_END } }, { NULL, "session-added", 0, { QUERY_ARG_CHARP, /* session uid */ QUERY_ARG_END } }, { NULL, "session-changed", 0, { QUERY_ARG_END } }, /* no params */ { NULL, "session-removed", 0, { QUERY_ARG_CHARP, /* session uid */ QUERY_ARG_END } }, { NULL, "session-renamed", 0, { QUERY_ARG_CHARP, /* new session alias */ QUERY_ARG_END } }, { NULL, "session-status", 0, { /* XXX */ QUERY_ARG_END } }, { NULL, "ekg-sigusr1", 0, { QUERY_ARG_END } }, /* no params */ { NULL, "ekg-sigusr2", 0, { QUERY_ARG_END } }, /* no params */ { NULL, "config-postinit", 0, { QUERY_ARG_END } }, /* no params */ { NULL, "quitting", 0, { /* XXX, emited, but never used */ QUERY_ARG_CHARP, /* reason */ QUERY_ARG_END } }, { NULL, "protocol-connected", 0, { QUERY_ARG_CHARP, /* session */ QUERY_ARG_END } }, { NULL, "protocol-disconnected", 0, { QUERY_ARG_CHARP, /* session */ QUERY_ARG_CHARP, /* reason */ QUERY_ARG_INT, /* type */ QUERY_ARG_END } }, { NULL, "protocol-message", 0, { QUERY_ARG_CHARP, /* session uid */ QUERY_ARG_CHARP, /* uid */ QUERY_ARG_CHARPP, /* rcpts */ QUERY_ARG_CHARP, /* text */ QUERY_ARG_UINT, /* uint32 */ /* format */ QUERY_ARG_UINT, /* time_t */ /* sent */ QUERY_ARG_INT, /* mclass */ QUERY_ARG_CHARP, /* seq */ QUERY_ARG_INT, /* dobeep */ QUERY_ARG_INT, /* secure */ QUERY_ARG_END } }, { NULL, "protocol-message-ack", 0, { QUERY_ARG_CHARP, /* session uid */ QUERY_ARG_CHARP, /* uid */ QUERY_ARG_CHARP, /* seq */ QUERY_ARG_INT, /* status */ QUERY_ARG_END } }, { NULL, "protocol-status", 0, { QUERY_ARG_CHARP, /* session uid */ QUERY_ARG_CHARP, /* uid */ QUERY_ARG_INT, /* status */ QUERY_ARG_CHARP, /* descr */ QUERY_ARG_UINT, /* time_t */ /* when */ QUERY_ARG_END } }, { NULL, "protocol-validate-uid", 0, { QUERY_ARG_CHARP, /* uid */ QUERY_ARG_INT, /* valid */ QUERY_ARG_END } }, { NULL, "protocol-xstate", 0, { QUERY_ARG_CHARP, /* session */ QUERY_ARG_CHARP, /* uid */ QUERY_ARG_INT, /* state - bits on */ QUERY_ARG_INT, /* offstate - bits off */ QUERY_ARG_END } }, { NULL, "add-notify", 0, { /* XXX */ QUERY_ARG_END } }, { NULL, "remove-notify", 0, { /* XXX */ QUERY_ARG_END } }, { NULL, "protocol-ignore", 0, { QUERY_ARG_CHARP, /* session uid */ QUERY_ARG_CHARP, /* uid */ QUERY_ARG_INT, /* oldlevel */ QUERY_ARG_INT, /* newlevel */ QUERY_ARG_END } }, { NULL, "protocol-unignore", 0, { /* XXX */ QUERY_ARG_END } }, { NULL, "conference-renamed", 0, { /* XXX */ QUERY_ARG_END } }, { NULL, "ui-beep", 0, { QUERY_ARG_END } }, /* no params */ { NULL, "ui-is-initialized", 0, { QUERY_ARG_INT, /* is_ui */ QUERY_ARG_END } }, { NULL, "ui-keypress", 0, { QUERY_ARG_INT, /* XXX uint? *//* key */ QUERY_ARG_END } }, { NULL, "ui-loop", 0, { QUERY_ARG_END } }, /* no params */ { NULL, "ui-window-act-changed", 0, { QUERY_ARG_WINDOW, /* window */ QUERY_ARG_END } }, { NULL, "ui-window-clear", 0, { QUERY_ARG_WINDOW, /* window */ QUERY_ARG_END } }, { NULL, "ui-window-kill", 0, { QUERY_ARG_WINDOW, /* window */ QUERY_ARG_END } }, { NULL, "ui-window-new", 0, { QUERY_ARG_WINDOW, /* window */ QUERY_ARG_END } }, { NULL, "ui-window-print", 0, { QUERY_ARG_WINDOW, /* window */ QUERY_ARG_FSTRING, /* fstring_t */ QUERY_ARG_END } }, { NULL, "ui-window-refresh", 0, { QUERY_ARG_END } }, /* no params */ { NULL, "ui-window-switch", 0, { QUERY_ARG_WINDOW, /* window */ QUERY_ARG_END } }, { NULL, "ui-window-target-changed", 0, { QUERY_ARG_WINDOW, /* window */ QUERY_ARG_END } }, /* GPG: PARAMS XXX */ { NULL, "gpg-message-encrypt", 0, { QUERY_ARG_END } }, { NULL, "gpg-message-decrypt", 0, { QUERY_ARG_END } }, { NULL, "gpg-sign", 0, { QUERY_ARG_END } }, { NULL, "gpg-verify", 0, { QUERY_ARG_END } }, { NULL, "session-event", 0, { QUERY_ARG_SESSION, /* session */ QUERY_ARG_INT, /* event type, [not used] */ QUERY_ARG_END } }, { NULL, "ui-refresh", 0, { QUERY_ARG_END } }, { NULL, "protocol-typing-out", 0, { QUERY_ARG_CHARP, /* session uid */ QUERY_ARG_CHARP, /* uid */ QUERY_ARG_INT, /* chat state */ QUERY_ARG_END } }, { NULL, "ui-password-input", 0, { QUERY_ARG_CHARP, /* password pointer storage */ QUERY_ARG_CHARP, /* alternate input prompt (&NULL = default) */ QUERY_ARG_CHARP, /* alternate repeat prompt (&NULL = default, NULL = no) */ QUERY_ARG_END } }, { NULL, "protocol-disconnecting", 0, { /* meant to be send before user-initiated disconnect, when we can still send some data, e.g. chatstate */ QUERY_ARG_CHARP, /* session uid */ QUERY_ARG_END } }, { NULL, "userlist-refresh", 0, { QUERY_ARG_END } }, { NULL, "event-offline", 0, { QUERY_ARG_CHARP, /* session uid */ QUERY_ARG_CHARP, /* uid */ QUERY_ARG_END } } }; int queries_init() { const query_def_t *p = core_query_list; size_t i; for (i = 0; i < sizeof(core_query_list) / sizeof(*core_query_list); ++i, ++p) { query_register_const(p); } return 0; } ekg2-0.4~pre+20120506.1/ekg/queries.h000066400000000000000000000024141175142753400165520ustar00rootroot00000000000000#ifndef __EKG_QUERIES #define __EKG_QUERIES #ifdef __cplusplus extern "C" { #endif #define QUERY_ARGS_MAX 12 enum query_arg_type { QUERY_ARG_END = 0, /* Terminates an array of `query_arg_type' values */ /* Type specifiers */ QUERY_ARG_CHARP, /* char * */ QUERY_ARG_CHARPP, /* char ** */ QUERY_ARG_INT, /* int */ QUERY_ARG_UINT, /* unsigned int */ /* -> time_t, guint32 */ QUERY_ARG_WINDOW = 100, /* window_t */ QUERY_ARG_FSTRING, /* fstring_t */ QUERY_ARG_USERLIST, /* userlist_t */ QUERY_ARG_SESSION, /* session_t */ /* Flags. Can be OR-ed with type specifiers. */ QUERY_ARG_CONST = (1<<31), /* Means that the argument should not be modified by a script. * In case it _will_ be modified, the new value will be * ignored and not propagated further. */ /* Masks. Used for extracting type specifiers and flags. */ QUERY_ARG_FLAGS = (QUERY_ARG_CONST), QUERY_ARG_TYPES = ~QUERY_ARG_FLAGS }; typedef struct query_def_node { struct query_def_node* next; char *name; int name_hash; enum query_arg_type params[QUERY_ARGS_MAX]; } query_def_t; int queries_init(); int query_register_const(const query_def_t *def); extern query_def_t *registered_queries; extern int registered_queries_count; #ifdef __cplusplus } #endif #endif ekg2-0.4~pre+20120506.1/ekg/queries.txt000066400000000000000000000023701175142753400171430ustar00rootroot00000000000000new queries (v3?) ----------------- pseudo-design-note: # id based queries removed completly # basic hashing scheme * name of a query is hashed, * part of a hash is used to select one of QUERIES_BUCKETS buckets, * each bucket is a list holding queries > we could employ some more sophisticated hashing scheme, but since number of queries (that is unique names) is relatievly low, there is no need for that > because this is experimental code, I've left a call to xstrcmp(), but we should probably disallow registering queries that have colliding hashes, (read: we should select such a hash function, that will be both fast and won't cause collisions, at least on queries used by plugins distributed with ekg2) So later we should be able to get rid of xstrcmp() call. kind-of-a-rationale: it seems that changes related to id-based queries made more bad than good: + known-to-core queries are fast (constant lookup time) - id-based queries makes the code more complicated - problem for script languages api: query_register - register given query query_connect - attach a handler to the given query, you probably shouldn't connect if the query hasn't yet been registered query_emit - emit a query ekg2-0.4~pre+20120506.1/ekg/recode.c000066400000000000000000000332031175142753400163310ustar00rootroot00000000000000/* * (C) Copyright 2009-2010 Jakub Zawadzki * Wiesaw Ochmiski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* NOTES/THINK/BUGS: * - do we need any #define? * - if we stop using ekg_convert_string_init() in plugins this file could be smaller. * - don't use gg_*() funcs, always use iconv? lite iconv in compat/ ? * - create: * static struct ekg_converter same_enc; * * we should know if iconv_open() failed, or we have good console_charset.. * give info to user, if this first happen. * * - we should also reinit encodings, if user changed console_charset. * - implement ekg_any_to_core(), ekg_locale_to_any() * * - Check if this code works OK. */ #include "ekg2.h" #include #include struct ekg_encoding_pair { gchar *from; gchar *to; }; /** * ekg_convert_string_init() * * Initialize string conversion thing for two given charsets. * * @param from - input encoding (will be duped; if NULL, console_charset will be assumed). * @param to - output encoding (will be duped; if NULL, console_charset will be assumed). * @param rev - pointer to assign reverse conversion into; if NULL, no reverse converter will be initialized. * * @return Pointer that should be passed to other ekg_convert_string_*(), even if it's NULL. * * @sa ekg_convert_string_destroy() - deinits charset conversion. * @sa ekg_convert_string_p() - main charset conversion function. */ void *ekg_convert_string_init(const char *from, const char *to, void **rev) { struct ekg_encoding_pair *enc; if (rev) { enc = g_new(struct ekg_encoding_pair, 1); enc->from = g_strdup(to); enc->to = g_strdup(from); *rev = enc; } enc = g_new(struct ekg_encoding_pair, 1); enc->from = g_strdup(from); enc->to = g_strdup(to); return enc; } /** * ekg_convert_string_destroy() * * Frees internal data associated with given pointer, and uninitalizes iconv, if it's not needed anymore. * * @note If 'rev' param was used with ekg_convert_string_init(), this functions must be called two times * - with returned value, and with rev-associated one. * * @param ptr - pointer returned by ekg_convert_string_init(). * * @sa ekg_convert_string_init() - init charset conversion. * @sa ekg_convert_string_p() - main charset conversion function. */ void ekg_convert_string_destroy(void *ptr) { struct ekg_encoding_pair *e = ptr; g_free(e->from); g_free(e->to); g_free(ptr); } /** * ekg_convert_string_p() * * Converts string to specified encoding, using pointer returned by ekg_convert_string_init(). * Invalid characters in input will be replaced with question marks. * * @param ps - string to be converted (won't be freed). * @param ptr - pointer returned by ekg_convert_string_init(). * * @return Pointer to allocated result or NULL, if some failure has occured or no conversion * is needed (i.e. resulting string would be same as input). * * @sa ekg_convert_string_init() - init charset conversion. */ char *ekg_convert_string_p(const char *ps, void *ptr) { struct ekg_encoding_pair *e = ptr; return ekg_convert_string(ps, e->from, e->to); } /** * ekg_convert_string() * * Converts string to specified encoding, replacing invalid chars with question marks. * * @note Deprecated, in favour of ekg_convert_string_p(). Should be used only on single * conversions, where charset pair won't be used again. * * @param ps - string to be converted (it won't be freed). * @param from - input encoding (if NULL, console_charset will be assumed). * @param to - output encoding (if NULL, console_charset will be assumed). * * @return Pointer to allocated result on success, NULL on failure * or when both encodings are equal. * * @sa ekg_convert_string_p() - more optimized version. */ char *ekg_convert_string(const char *ps, const char *from, const char *to) { char *res; gsize written; if (!ps) /* compat, please do not rely on it */ return NULL; if (!from) from = "utf8"; if (!to) to = "utf8"; res = g_convert_with_fallback(ps, -1, to, from, NULL, NULL, &written, NULL); if (!res) { res = g_strdup(ps); ekg_fix_utf8(res); } return res; } string_t ekg_convert_string_t_p(string_t s, void *ptr) { struct ekg_encoding_pair *e = ptr; return ekg_convert_string_t(s, e->from, e->to); } string_t ekg_convert_string_t(string_t s, const char *from, const char *to) { char *res; string_t ret; gsize written; if (!from) from = "utf8"; if (!to) to = "utf8"; res = g_convert_with_fallback(s->str, s->len, to, from, NULL, NULL, &written, NULL); ret = string_init(NULL); if (!res) string_append_raw(ret, g_memdup(s->str, s->len), s->len); else string_append_raw(ret, res, written); return ret; } void ekg_recode_inc_ref(const gchar *enc) { } void ekg_recode_dec_ref(const gchar *enc) { } char *ekg_recode_from_core(const gchar *enc, gchar *buf) { char *res = ekg_recode_to(enc, buf); g_free(buf); return res; } gchar *ekg_recode_to_core(const gchar *enc, char *buf) { gchar *res = ekg_recode_from(enc, buf); g_free(buf); return res; } char *ekg_recode_from_core_dup(const gchar *enc, const gchar *buf) { return ekg_recode_to(enc, buf); } gchar *ekg_recode_to_core_dup(const gchar *enc, const char *buf) { return ekg_recode_from(enc, buf); } const char *ekg_recode_from_core_use(const gchar *enc, const gchar *buf) { return ekg_recode_to(enc, buf); } const gchar *ekg_recode_to_core_use(const gchar *enc, const char *buf) { return ekg_recode_from(enc, buf); } /** * ekg_recode_from() * * Convert complete string str from given encoding to ekg2 internal * encoding (utf8). If the conversion fails, fallback to duplicating * and utf8-cleaning str. * * @param enc - source encoding (e.g. "iso-8859-2"). * @param str - string to recode [may be NULL]. * * @return A newly-allocated string which is guaranteed to be correct * utf8 and needs to be freed using g_free(), or NULL if !str. */ gchar *ekg_recode_from(const gchar *enc, const char *str) { /* -- temporary, please do not rely on !enc */ if (G_UNLIKELY(!enc)) return ekg_recode_from_locale(str); return ekg_convert_string(str, enc, NULL); } /** * ekg_recode_to() * * Convert complete string str from ekg2 internal encoding (utf8) * to given encoding. If the conversion fails, fallback to duplicating * the string. * * @param enc - target encoding (e.g. "iso-8859-2"). * @param str - string to recode [may be NULL]. * * @return A newly-allocated string which must be freed using g_free(), * or NULL if !str. */ char *ekg_recode_to(const gchar *enc, const gchar *str) { /* -- temporary, please do not rely on !enc */ if (G_UNLIKELY(!enc)) return ekg_recode_to_locale(str); return ekg_convert_string(str, NULL, enc); } /** * ekg_recode_from_locale() * * Convert complete string str from locale to ekg2 internal encoding * (utf8). If the conversion fails, fallback to duplicating * and utf8-cleaning the string. * * @param str - string to recode. May be NULL. * * @return A newly-allocated string which must be freed using g_free(), * or NULL if !str. */ gchar *ekg_recode_from_locale(const char *str) { if (console_charset_is_utf8) { gchar *tmp = g_strdup(str); if (tmp) ekg_fix_utf8(tmp); return tmp; } else return ekg_recode_from(console_charset, str); } /** * ekg_recode_to_locale() * * Convert complete string str from ekg2 internal encoding (utf8) * to locale. If the conversion fails, fallback to duplicating * the string. * * @param str - string to recode. May be NULL. * * @return A newly-allocated string which must be freed using g_free(), * or NULL if !str. */ char *ekg_recode_to_locale(const gchar *str) { if (console_charset_is_utf8) return g_strdup(str); else return ekg_recode_to(console_charset, str); } static gboolean gstring_recode_helper(GString *s, const gchar *from, const gchar *to, gboolean fixutf) { char *res; gsize written; res = g_convert_with_fallback(s->str, s->len, to, from, NULL, NULL, &written, NULL); if (G_LIKELY(res)) { g_string_truncate(s, 0); g_string_append_len(s, res, written); } else if (G_LIKELY(fixutf)) ekg_fix_utf8(s->str); return !!res; } /** * ekg_recode_gstring_from() * * Convert complete GString in-place from given encoding to ekg2 * internal encoding (utf8). If the conversion fails, fallback to * utf8-cleaning the string. * * @param enc - source encoding. * @param s - GString to recode and to write the result into. After * the call to this function, it is guaranteed to contain correct utf8. * * @return TRUE if conversion succeeded, FALSE otherwise. */ gboolean ekg_recode_gstring_from(const gchar *enc, GString *s) { return gstring_recode_helper(s, enc, "utf8", TRUE); } /** * ekg_try_recode_gstring_from() * * Convert complete GString in-place from given encoding to ekg2 * internal encoding (utf8). If the conversion fails, leave string * unchanged. * * @param enc - source encoding. * @param s - GString to recode and to write the result into * if the conversion succeeds. * * @return TRUE if conversion succeeded, FALSE otherwise. */ gboolean ekg_try_recode_gstring_from(const gchar *enc, GString *s) { return gstring_recode_helper(s, enc, "utf8", FALSE); } /** * ekg_recode_gstring_to() * * Convert complete GString in-place from ekg2 internal encoding (utf8) * to given encoding. If the conversion fails, leave string unchanged. * * @param enc - target encoding. * @param s - GString to recode and to write the result into * if the conversion succeeds. * * @return TRUE if conversion succeeded, FALSE otherwise. */ gboolean ekg_recode_gstring_to(const gchar *enc, GString *s) { return gstring_recode_helper(s, "utf8", enc, FALSE); } /** * ekg_fix_utf8() * * Ensure correct utf8 in buffer, replacing incorrect sequences. * * @param buf - writable, null-terminated, utf8 string. * * @note Currently, this function replaces incorrect bytes with ASCII * SUB (0x1a). This may change in future. */ void ekg_fix_utf8(gchar *buf) { const gchar *p = buf; while (G_UNLIKELY(!g_utf8_validate(p, -1, &p))) *((gchar*) p++) = 0x1a; /* substitute, UTR#36 suggests it as byte replacement */ } static void fstr_mark_linebreaks(gchar *s, fstr_attr_t *a) { gchar *p; /* XXX: use pango */ if (!g_utf8_validate(s, -1, NULL)) ekg_fix_utf8(s); for (p = s; *p; p = g_utf8_next_char(p)) { /* if we're already mangling fstring_t, suit SUBs as well */ if (G_UNLIKELY(*p == 0x1a)) { *p = '?'; a[p - s] |= FSTR_REVERSE; } switch (g_unichar_break_type(g_utf8_get_char(p))) { /* these should cause line break themselves * but we don't support unicode that well, * so just use them as wrap opportunity */ case G_UNICODE_BREAK_MANDATORY: case G_UNICODE_BREAK_CARRIAGE_RETURN: case G_UNICODE_BREAK_LINE_FEED: case G_UNICODE_BREAK_NEXT_LINE: /* typical break opportunities */ case G_UNICODE_BREAK_ZERO_WIDTH_SPACE: case G_UNICODE_BREAK_SPACE: case G_UNICODE_BREAK_BEFORE_AND_AFTER: case G_UNICODE_BREAK_AFTER: /* not always but use it anyway */ case G_UNICODE_BREAK_HYPHEN: { const gsize startpos = p - s; const gsize endpos = g_utf8_next_char(p) - s; gsize i; for (i = startpos; i < endpos; i++) a[i] |= FSTR_LINEBREAK; } break; /* we do not support breaking before */ case G_UNICODE_BREAK_BEFORE: default: break; } } } /** * ekg_recode_fstr_to_locale() * * Recode fstring_t from ekg2 internal encoding (utf8) to locale, * adjusting attributes as necessary. Set attributes based on special * unicode character properties (e.g. FSTR_LINEBREAK). * * @param fstr - input fstring_t. * * @return Newly-allocated fstring_t, which needs to be freed using * fstring_free(). */ fstring_t *ekg_recode_fstr_to_locale(const fstring_t *fstr) { if (console_charset_is_utf8) { fstring_t *s = fstring_dup(fstr); fstr_mark_linebreaks(s->str, s->attr); return s; } else { gchar *s; fstr_attr_t *a, *dupattr; gssize len; const gssize inpsize = strlen(fstr->str); GString *outs = g_string_sized_new(inpsize); GByteArray *outa = g_byte_array_sized_new(inpsize * sizeof(fstr_attr_t)); fstring_t *out = g_memdup(fstr, sizeof(fstring_t)); /* XXX: move to slice alloc */ fstring_iter(fstr, &s, &a, &len); /* we need to have a modifiable copy to set linebreaks */ a = dupattr = g_memdup(a, inpsize * sizeof(fstr_attr_t)); fstr_mark_linebreaks(s, a); while (fstring_next(&s, &a, &len, NULL)) { char *ls; gsize ob; ls = g_convert_with_fallback(s, len, console_charset, "utf8", NULL, NULL, &ob, NULL); if (ls) { g_string_append_len(outs, ls, ob); g_free(ls); } else { /* XXX: is that really a good idea? */ g_string_append_len(outs, s, len); ob = len; } /* we can assume 'a' has len identical 'fstr_attr_t's */ while (ob > len) { g_byte_array_append(outa, (gpointer) a, len * sizeof(fstr_attr_t)); ob -= len; } if (ob > 0) g_byte_array_append(outa, (gpointer) a, ob * sizeof(fstr_attr_t)); } g_free(dupattr); out->str = g_string_free(outs, FALSE); out->attr = (fstr_attr_t*) g_byte_array_free(outa, FALSE); /* XXX: margins and stuff get outdated */ return out; } g_assert_not_reached(); } ekg2-0.4~pre+20120506.1/ekg/recode.h000066400000000000000000000070411175142753400163370ustar00rootroot00000000000000#ifndef __EKG_RECODE_H #define __EKG_RECODE_H #include "dynstuff.h" #ifdef __cplusplus extern "C" { #endif #define EKG_RECODE_CP "CP-1250" #define EKG_RECODE_ISO2 "ISO-8859-2" #define EKG_RECODE_UTF8 "UTF-8" void *ekg_convert_string_init(const char *from, const char *to, void **rev); void ekg_convert_string_destroy(void *ptr); char *ekg_convert_string_p(const char *ps, void *ptr); char *ekg_convert_string(const char *ps, const char *from, const char *to); string_t ekg_convert_string_t_p(string_t s, void *ptr); string_t ekg_convert_string_t(string_t s, const char *from, const char *to); void changed_console_charset(const char *name); int ekg_converters_display(int quiet); void ekg_recode_inc_ref(const gchar *enc); void ekg_recode_dec_ref(const gchar *enc); char *ekg_recode_from_core(const gchar *enc, gchar *buf); gchar *ekg_recode_to_core(const gchar *enc, char *buf); char *ekg_recode_from_core_dup(const gchar *enc, const gchar *buf); gchar *ekg_recode_to_core_dup(const gchar *enc, const char *buf); const char *ekg_recode_from_core_use(const gchar *enc, const gchar *buf); const gchar *ekg_recode_to_core_use(const gchar *enc, const char *buf); /* below starts the current API */ gchar *ekg_recode_from(const gchar *enc, const char *str); char *ekg_recode_to(const gchar *enc, const gchar *str); gchar *ekg_recode_from_locale(const char *str); char *ekg_recode_to_locale(const gchar *str); gboolean ekg_recode_gstring_from(const gchar *enc, GString *s); gboolean ekg_try_recode_gstring_from(const gchar *enc, GString *s); gboolean ekg_recode_gstring_to(const gchar *enc, GString *s); void ekg_fix_utf8(gchar *buf); fstring_t *ekg_recode_fstr_to_locale(const fstring_t *fstr); #define recode_xfree(org, ret) xfree((char *) ret); /* CP-1250 */ #define ekg_recode_cp_inc() ekg_recode_inc_ref(EKG_RECODE_CP) #define ekg_recode_cp_dec() ekg_recode_dec_ref(EKG_RECODE_CP) #define ekg_locale_to_cp(buf) ekg_recode_from_core(EKG_RECODE_CP, buf) #define ekg_cp_to_core(buf) ekg_recode_to_core(EKG_RECODE_CP, buf) #define ekg_locale_to_cp_dup(buf) ekg_recode_from_core_dup(EKG_RECODE_CP, buf) #define ekg_cp_to_core_dup(buf) ekg_recode_to_core_dup(EKG_RECODE_CP, buf) #define ekg_locale_to_cp_use(buf) ekg_recode_from_core_use(EKG_RECODE_CP, buf) #define ekg_cp_to_core_use(buf) ekg_recode_to_core_use(EKG_RECODE_CP, buf) /* ISO-8859-2 */ #define ekg_recode_iso2_inc() ekg_recode_inc_ref(EKG_RECODE_ISO2) #define ekg_recode_iso2_dec() ekg_recode_dec_ref(EKG_RECODE_ISO2) #define ekg_locale_to_iso2(buf) ekg_recode_from_core(EKG_RECODE_ISO2, buf) #define ekg_iso2_to_core(buf) ekg_recode_to_core(EKG_RECODE_ISO2, buf) #define ekg_locale_to_iso2_dup(buf) ekg_recode_from_core_dup(EKG_RECODE_ISO2, buf) #define ekg_iso2_to_core_dup(buf) ekg_recode_to_core_dup(EKG_RECODE_ISO2, buf) #define ekg_locale_to_iso2_use(buf) ekg_recode_from_core_use(EKG_RECODE_ISO2, buf) #define ekg_iso2_to_core_use(buf) ekg_recode_to_core_use(EKG_RECODE_ISO2, buf) /* UTF-8 */ #define ekg_recode_utf8_inc() ekg_recode_inc_ref(EKG_RECODE_UTF8) #define ekg_recode_utf8_dec() ekg_recode_dec_ref(EKG_RECODE_UTF8) #define ekg_locale_to_utf8(buf) ekg_recode_from_core(EKG_RECODE_UTF8, buf) #define ekg_utf8_to_core(buf) ekg_recode_to_core(EKG_RECODE_UTF8, buf) #define ekg_locale_to_utf8_dup(buf) ekg_recode_from_core_dup(EKG_RECODE_UTF8, buf) #define ekg_utf8_to_core_dup(buf) ekg_recode_to_core_dup(EKG_RECODE_UTF8, buf) #define ekg_locale_to_utf8_use(buf) ekg_recode_from_core_use(EKG_RECODE_UTF8, buf) #define ekg_utf8_to_core_use(buf) ekg_recode_to_core_use(EKG_RECODE_UTF8, buf) #ifdef __cplusplus } #endif #endif ekg2-0.4~pre+20120506.1/ekg/scripts.c000066400000000000000000000603061175142753400165630ustar00rootroot00000000000000 #include "ekg2.h" #include #include #include #include #include #include #include #include "scripts.h" /* TODO && BUGS * - cleanup. * - multiple handler for commands && var_changed. * - memleaks ? */ script_t *scripts; static LIST_FREE_ITEM(list_script_free, script_t *) { xfree(data->name); xfree(data->path); } DYNSTUFF_LIST_DECLARE(scripts, script_t, list_script_free, static __DYNSTUFF_LIST_ADD, /* scripts_add() */ static __DYNSTUFF_LIST_REMOVE_SAFE, /* scripts_remove() */ __DYNSTUFF_NODESTROY) scriptlang_t *scriptlang; DYNSTUFF_LIST_DECLARE_NF(scriptlang, scriptlang_t, static __DYNSTUFF_LIST_ADD, /* scriptlang_add() */ static __DYNSTUFF_LIST_UNLINK) /* scriptlang_unlink() */ static list_t script_timers; static list_t script_plugins; static list_t script_vars; static list_t script_queries; static list_t script_commands; static list_t script_watches; static COMMAND(script_command_handlers); static TIMER(script_timer_handlers); static void script_var_changed(const char *var); static QUERY(script_query_handlers); static WATCHER(script_handle_watch); static int script_plugin_theme_init( /* plugin_t *p */ ); static int scripts_autoload(scriptlang_t *scr); static char *script_find_path(const char *name); /****************************************************************************************************/ scriptlang_t *scriptlang_from_ext(char *name) { scriptlang_t *s; char *ext = xrindex(name, '.'); if (!ext) return NULL; for (s = scriptlang; s; s = s->next) { if (!xstrcmp(ext, s->ext)) return s; } return NULL; } int scriptlang_register(scriptlang_t *s) { scriptlang_add(s); s->init(); if (!in_autoexec) scripts_autoload(s); return 0; } int scriptlang_unregister(scriptlang_t *s) { script_unload_lang(s); s->deinit(); scriptlang_unlink(s); return 0; } /**************************************************************************************/ int script_autorun(char *scriptname, int isautorun /* 0 - turn off ; 1 - turn on ; -1 off->on on->off */) { /* * yeah i know it could be faster, better and so on, but it was written for special event and it look's like like it look... ;> * and it's short, easy to understand etc.. ;> */ int ret = -1; int old_errno = 0; if (!scriptname) { /* TODO: list script from autorun dir. script_autorun_list %1 - filename %2 - readlink. */ return 0; } if (isautorun) { char *path = script_find_path(scriptname); char *ext = NULL; if (!(xrindex(scriptname, '.'))) ext = xrindex(path, '.'); /* TODO: maybe we should check if (ext) belongs to any scriptlang... ? and in script_find_path() ? * to avoid stupid user mistakes... but i don't think ;> */ errno = 0; if (path) { /* XXX sanity scriptname */ const char *autorunpath = prepare_pathf("scripts/autorun/%s%s", scriptname, ext ? ext : ""); if (autorunpath && mkdir_recursive(autorunpath, 0) == 0) { debug("[SCRIPT_AUTORUN] symlink from %s to %s ... retcode:", path, autorunpath); #ifndef NO_POSIX_SYSTEM ret = symlink(path, autorunpath); #endif debug("%d\n", ret); } if (!autorunpath) errno = ENAMETOOLONG; } xfree(path); if (ret && isautorun == -1) isautorun = 0; else isautorun = 1; old_errno = errno; } if (!isautorun) { const char *path1 = prepare_pathf("scripts/autorun/%s", scriptname); if (path1) { char *path = script_find_path(path1); if (path && path1) { debug("[SCRIPT_AUTORUN] unlinking %s... ", path); ret = unlink(path); debug("%d\n", ret); } else isautorun = -1; xfree(path); } else errno = ENAMETOOLONG; } if (!ret) print("script_autorun_succ", scriptname, (isautorun == 1) ? "added to" : "removed from"); else if (isautorun == -1) print("script_autorun_unkn", scriptname, "", strerror(errno)); /* i think only when there isn't such a file but i'm not sure */ else print("script_autorun_fail", scriptname, (isautorun == 1) ? "to add to" : "to remove from", strerror(errno)); return ret; } int script_reset(scriptlang_t *scr) { scriptlang_t *s; for (s = scriptlang; s; s = s->next) { script_unload_lang(s); s->deinit(); s->init(); scripts_autoload(s); } return 0; } int script_list(scriptlang_t *s) { script_t *scr; scriptlang_t *lang; int i = 0; for (scr = scripts; scr; scr = scr->next) { lang = scr->lang; if (!s || scr->lang == s) { print("script_list", scr->name, scr->path, lang->name); i++; } } if (!i) print("script_list_empty"); return i; } int script_var_list(script_t *scr) { list_t l; int i = 0; for (l = script_vars; l; l = l->next) { script_var_t *v = l->data; if (!scr || v->scr == scr) { print("script_varlist", v->self->name, v->value, v->priv_data); i++; } } if (!i) print("script_varlist_empty"); return i; } /***********************************************************************************/ static char *script_find_path(const char *name) { FILE *fajl; char *ext; char *nametmp; char *path = NULL; scriptlang_t *s = scriptlang; nametmp = xstrdup(name); while ((ext = xrindex(nametmp, '.')) || s) { if (ext) { if (nametmp[0] == '/' && (fajl = (fopen(nametmp, "r")))) { fclose(fajl); return nametmp; } path = saprintf("%s/%s",prepare_path("scripts", 0), nametmp); fajl = fopen(path, "r"); if (!fajl) { xfree(path); path = saprintf("%s/scripts/%s", DATADIR, nametmp); fajl = fopen(path, "r"); } /* etc.. */ xfree(nametmp); if (!fajl) { xfree(path); path = NULL; } else { fclose(fajl); return path; } } if (!s) return NULL; nametmp = saprintf("%s%s", name, s->ext); s = s->next; } return NULL; } int script_unload(script_t *scr) { typedef struct { script_t *scr; } tmpstruct; scriptlang_t *slang = scr->lang; void *t; /* t comes from temporary !from timer ;> */ list_t l; scr->inited = 0; #define s(x) ((tmpstruct *) x) /* przeszukac liste timerow i komand, jak cos to je wywalic */ for (l = script_timers; l;) { t = l->data; l = l->next; if (!t) continue; if (s(t)->scr == scr) { script_timer_unbind(t, 1); } } for (l = script_commands; l;) { t = l->data; l = l->next; if (!t) continue; if (s(t)->scr == scr) { script_command_unbind(t, 1); } } for (l = script_vars; l;) { t = l->data; l = l->next; if (!t) continue; if (s(t)->scr == scr) { script_var_unbind(t, 1); } } for (l = script_queries; l;) { t = l->data; l = l->next; if (!t) continue; if (s(t)->scr == scr) { script_query_unbind(t, 1); } } for (l = script_watches; l;) { t = l->data; l = l->next; if (!t) continue; if (s(t)->scr == scr) { script_watch_unbind(t, 1); } } #undef s if (slang->script_unload(scr)) return -1; print("script_removed", scr->name, scr->path, slang->name); scripts_remove(scr); return 0; } script_t *script_find(scriptlang_t *s, char *name) { SCRIPT_FINDER ((( scr->lang == s || !s)) && !xstrcmp(name, scr->name)); } int script_unload_name(scriptlang_t *s, char *name) { script_t *scr; if (xstrlen(name) < 1) { print("script_need_name"); return -1; } scr = script_find(s, name); if (!scr) { print("script_not_found", name); return -1; } if (script_unload(scr)) { /* error */ return -1; } return 0; } int script_unload_lang(scriptlang_t *s) { scriptlang_t *lang; script_t *scr; for (scr = scripts; scr;) { script_t *next = scr->next; lang = scr->lang; if (!s || scr->lang == s) { script_unload(scr); } scr = next; } return 0; } int script_load(scriptlang_t *s, char *tname) { scriptlang_t *slang; script_t *scr; struct stat st; char *path, *name2, *name = NULL; int ret; if (!xstrlen(tname)) { print("script_need_name"); return -1; } if (s && !xrindex(tname, '.')) name = saprintf("%s%s", tname, s->ext); else name = xstrdup(tname); if ((path = script_find_path(name))) { if (stat(path, &st) || S_ISDIR(st.st_mode)) { /* scripts_loaddir(path) (?) */ xfree(path); xfree(name); print("generic_error", strerror(EISDIR)); return -1; } slang = (s) ? s : scriptlang_from_ext(path); if (!slang || xstrcmp(xrindex(path, '.'), slang->ext)) { if (slang) { /* internal error shouldn't happen */ debug("[script_ierror] slang = 0x%x path = %s slang = %s slangext = %s\n", slang, path, slang->name, slang->ext); print("generic_error", _("internal script handling ERROR, script not loaded.")); } else { debug("[script] extension = %s\n", xrindex(path, '.')); print("generic_error", _("Can't recognize script type")); } xfree(path); xfree(name); return -1; } name2 = xstrdup(xrindex(path, '/')+1); name2[xstrlen(name2) - xstrlen(slang->ext)] = 0; if ((scr = script_find(slang, name2))) { /* if script with the same name is loaded then ...*/ debug("[script] the same script loaded unloading it!\n"); script_unload(scr); /*... unload old one. */ } scr = xmalloc(sizeof(script_t)); scr->path = xstrdup(path); scr->name = name2; scr->lang = slang; scr->inited = 1; scripts_add(scr); /* BUG: this should be before `script_loaded`... */ ret = slang->script_load(scr); /* debug("[script] script_load ret == %d\n", ret); */ if (ret < 1) { if (ret == -1) print("script_incorrect", scr->name, scr->path, slang->name); else if (ret == 0) print("script_incorrect2", scr->name, scr->path, slang->name); /* "script has no handler or error in getting handlers." */ xfree(path); xfree(name); script_unload(scr); return -1; } print("script_loaded", scr->name, scr->path, slang->name); } else print("script_not_found", name); xfree(path); xfree(name); return 0; } int script_variables_read() { GDataInputStream *f; char *line; if (!(f = G_DATA_INPUT_STREAM(config_open("scripts-var", "r")))) { debug("Error opening script variable file..\n"); return -1; } while ((line = read_line(f))) { if (line[0] == '#' || line[0] == ';' || (line[0] == '/' && line[1] == '/')) continue; script_var_add(NULL, NULL, line, NULL, NULL); } g_object_unref(f); return 0; } void script_variables_free() { list_t l; for (l = script_vars; l; l = l->next) { script_var_t *v = l->data; /* xfree(v->value); variables_free() free it. */ xfree(v->priv_data); /* should be NULL here. */ xfree(v->name); xfree(v); } list_destroy(script_vars, 0); return; } void script_variables_write() { list_t l; GOutputStream *f = G_OUTPUT_STREAM(config_open("scripts-var", "w")); if (!f) return; for (l = script_vars; l; l = l->next) { script_var_t *v = l->data; ekg_fprintf(f, "%s\n", v->name); } } script_command_t *script_command_find(const char *name) { script_command_t *temp; list_t l; for (l = script_commands; l; l = l->next) { temp = l->data; if (!xstrcmp(name, temp->self->name)) return temp; } return NULL; } script_var_t *script_var_find(const char *name) { list_t l; #if 0 /* i don't remember that code... */ if (!variable_find(name)) return NULL; #endif for (l = script_vars; l; l = l->next) { script_var_t *v = l->data; if (!xstrcasecmp(v->name, name)) { return v; } } return NULL; } /**********************************************************************************************************************/ int script_command_unbind(script_command_t *temp, int free) { int notfound = 1; /* TODO */ SCRIPT_UNBIND_HANDLER(SCRIPT_COMMANDTYPE, temp->priv_data); if (notfound) commands_remove(temp->self); return list_remove(&script_commands, temp, 1); } int script_query_unbind(script_query_t *temp, int free) { SCRIPT_UNBIND_HANDLER(SCRIPT_QUERYTYPE, temp->priv_data); query_free(temp->self); return list_remove(&script_queries, temp, 1); } int script_plugin_destroy(/* plugin_t *p */ ) /* and what i can do here ? */ { /* ok somethink i can */ script_plugin_t *temp = NULL; list_t l; for (l = script_plugins; l; l = l->next) { if (temp) { debug("Err @ script_plugin_destroy more that 1 script as plugin, plugin_destroying must be rewritten!\n"); return -1; } else temp = l->data; } SCRIPT_UNBIND_HANDLER(SCRIPT_PLUGINTYPE, temp->priv_data); plugin_unregister(temp->self); xfree(temp->self->name); xfree(temp->self); return list_remove(&script_plugins, temp, 1); } int script_timer_unbind(script_timer_t *temp, int remove) { if (temp->removed) return -1; temp->removed = 1; if (remove) ekg_source_remove(temp->self); SCRIPT_UNBIND_HANDLER(SCRIPT_TIMERTYPE, temp->priv_data); return list_remove(&script_timers, temp, 0 /* 0 is ok */); } int script_watch_unbind(script_watch_t *temp, int remove) { if (temp->removed) return -1; temp->removed = 1; if (remove) watch_free(temp->self); /* TODO: testit */ SCRIPT_UNBIND_HANDLER(SCRIPT_WATCHTYPE, temp->priv_data, temp->data); return list_remove(&script_watches, temp, 1); } int script_var_unbind(script_var_t *temp, int free) { SCRIPT_UNBIND_HANDLER(SCRIPT_VARTYPE, temp->priv_data); temp->scr = NULL; temp->priv_data = NULL; return 0; } /****************************************************************************************************/ script_var_t *script_var_add_full(scriptlang_t *s, script_t *scr, char *name, int type, char *value, void *handler) { script_var_t *tmp; tmp = script_var_find(name); if (tmp) { tmp->scr = scr; tmp->priv_data = handler; if ( (tmp->self->type == VAR_STR) && ((type == VAR_INT) || (type == VAR_BOOL)) ) { /* Convert from string values */ variable_t *var = tmp->self; int *ival = xmalloc(sizeof(int)); if (tmp->value && *(tmp->value)) *ival = atoi(tmp->value); if (type == VAR_BOOL) *ival &= 1; g_free(tmp->value); var->ptr = tmp->value = (char*)(ival); tmp->self->type = type; } } else if (!tmp) { void *pVar; SCRIPT_BIND_HEADER(script_var_t); temp->name = xstrdup(name); if ((type == VAR_INT) || (type == VAR_BOOL)) { int *ival = xmalloc(sizeof(int)); if (value && *value) *ival = atoi(value); if (type == VAR_BOOL) *ival &= 1; pVar = temp->value = (char*)(ival); } else { temp->value = xstrdup(value); pVar = &(temp->value); } temp->self = variable_add(NULL, name, type, 1, pVar, &script_var_changed, NULL, NULL); SCRIPT_BIND_FOOTER(script_vars); } return tmp; } script_var_t *script_var_add(scriptlang_t *s, script_t *scr, char *name, char *value, void *handler) { return script_var_add_full(s, scr, name, VAR_STR, value, handler); } script_command_t *script_command_bind(scriptlang_t *s, script_t *scr, char *command, char *params, char *possibilities, void *handler) { SCRIPT_BIND_HEADER(script_command_t); temp->self = command_add(NULL, command, params, script_command_handlers, COMMAND_ISSCRIPT, possibilities); SCRIPT_BIND_FOOTER(script_commands); } script_plugin_t *script_plugin_init(scriptlang_t *s, script_t *scr, char *name, plugin_class_t pclass, void *handler) { SCRIPT_BIND_HEADER(script_plugin_t); temp->self = xmalloc(sizeof(plugin_t)); temp->self->name = xstrdup(name); temp->self->pclass = pclass; temp->self->destroy = script_plugin_destroy; temp->self->theme_init = script_plugin_theme_init; plugin_register(temp->self, -254 /* default */); SCRIPT_BIND_FOOTER(script_plugins); } script_timer_t *script_timer_bind(scriptlang_t *s, script_t *scr, int freq, void *handler) { char *tempname; SCRIPT_BIND_HEADER(script_timer_t); tempname = saprintf("scr_%p", temp); /* truly unique ;p */ temp->self = timer_add(NULL, (const char *) tempname, freq, 1, &script_timer_handlers, (void *) temp); xfree(tempname); SCRIPT_BIND_FOOTER(script_timers); } script_watch_t *script_watch_add(scriptlang_t *s, script_t *scr, int fd, int type, void *handler, void *data) { SCRIPT_BIND_HEADER(script_watch_t); temp->data = data; temp->self = watch_add(s->plugin, fd, type, script_handle_watch, temp); SCRIPT_BIND_FOOTER(script_watches); } script_query_t *script_query_bind(scriptlang_t *s, script_t *scr, char *qname, void *handler) { SCRIPT_BIND_HEADER(script_query_t); #define NEXT_ARG(y) temp->argv_type[temp->argc] = y; temp->argc++; /* hacki */ if (!xstrcmp(qname, "protocol-disconnected")) temp->hack = 1; else if (!xstrcmp(qname, "protocol-status")) temp->hack = 2; else if (!xstrcmp(qname, "protocol-message")) temp->hack = 3; else if (!xstrcmp(qname, "protocol-message-post")) temp->hack = 4; else if (!xstrcmp(qname, "protocol-message-received")) temp->hack = 5; if (!xstrcmp(qname, "protocol-disconnected-2")) qname = "protocol-disconnected"; else if (!xstrcmp(qname, "protocol-status-2")) qname = "protocol-status"; else if (!xstrcmp(qname, "protocol-message-2")) qname = "protocol-message"; else if (!xstrcmp(qname, "protocol-message-post-2")) qname = "protocol-message-post"; else if (!xstrcmp(qname, "protocol-message-received-2"))qname = "protocol-message-received"; /* IRC */ if (!xstrncmp(qname, "irc-protocol-numeric ", sizeof("irc-protocol-numeric ")-1)) { /* XXX, obciaz nazwe do irc-protocl-numeric i wrzucic to ponizej do queries.h */ NEXT_ARG(QUERY_ARG_CHARP); NEXT_ARG(QUERY_ARG_CHARPP); } /* other */ else { const query_def_t* g; for (g = registered_queries; g; g = g->next) { if (!xstrcmp(qname, g->name)) { int j = 0; while (j < QUERY_ARGS_MAX && g->params[j] != QUERY_ARG_END) { NEXT_ARG(g->params[j++]); } break; } } } #undef NEXT_ARG temp->real_argc = temp->argc; temp->self = query_connect(s->plugin, qname, script_query_handlers, temp); SCRIPT_BIND_FOOTER(script_queries); } /*****************************************************************************************************************/ static void script_var_changed(const char *var) { script_var_t *temp = script_var_find(var); /* if (in_autoexec) ... */ SCRIPT_HANDLER_HEADER(script_handler_var_t); /* debug("[script_variable_changed] varname = %s newvalue = %s\n", var, temp->value); */ SCRIPT_HANDLER_MULTI_FOOTER(script_handler_var, temp->value); return; } static WATCHER(script_handle_watch) { script_watch_t *temp = data; SCRIPT_HANDLER_HEADER(script_handler_watch_t); SCRIPT_HANDLER_FOOTER(script_handler_watch, type, fd, watch) { if (!type) { return -1; /* watch_free(temp->self); */ } } if (type) script_watch_unbind(temp, 0); return 0; } static COMMAND(script_command_handlers) { script_command_t *temp = script_command_find(name); SCRIPT_HANDLER_HEADER(script_handler_command_t); SCRIPT_HANDLER_MULTI_FOOTER(script_handler_command, (char **) params) { script_command_unbind(temp, 1); } return ret; } static int script_plugin_theme_init( /* plugin_t *p */ ) { /* TODO: it will be slow! foreach scriptplugin call format initializer. (?) */ return 0; } static TIMER(script_timer_handlers) { script_timer_t *temp = data; SCRIPT_HANDLER_HEADER(script_handler_timer_t); SCRIPT_HANDLER_FOOTER(script_handler_timer, type) { if (!type) { return -1; /* timer_free(temp->self); */ } } if (type) script_timer_unbind(temp, 0); return 0; } static QUERY(script_query_handlers) { script_query_t *temp = data; void *args[MAX_ARGS]; void *args2[MAX_ARGS]; int i; script_query_t saved; char *status = NULL; /* for temp->hack == 2 */ int ign_level = 0; SCRIPT_HANDLER_HEADER(script_handler_query_t); /* makes thing easier to debug next time... */ memset(args, -1, sizeof(args)); memset(args2, -1, sizeof(args2)); for (i=0; i < temp->real_argc; i++) args2[i] = args[i] = (void *) va_arg(ap, void *); if (temp->hack) memcpy(&saved, temp, sizeof(script_query_t)); switch (temp->hack) { case 0: break; /* without hack, thats gr8! */ case 1: /* scripts protocol-disconnected (v 1.0) - takes only (reason) */ temp->argv_type[0] = QUERY_ARG_CHARP; /* OK */ temp->argc = 1; break; case 2: /* scripts protocol-status (v 1.0) - takes (session, uid, status, descr) - takes char *status, instead of int status */ { temp->argc = 4; temp->argv_type[0] = QUERY_ARG_CHARP; /* OK */ temp->argv_type[1] = QUERY_ARG_CHARP; /* OK */ temp->argv_type[2] = QUERY_ARG_CHARP; /* status: int -> char * */ status = xstrdup(ekg_status_string(*((int *) args2[2]), 0)); /* status, int -> char * */ args[2] = &status; temp->argv_type[3] = QUERY_ARG_CHARP; /* OK */ temp->argv_type[4] = QUERY_ARG_CHARP; /* OK */ break; } case 3: case 4: case 5: /* scripts protocol-message, protocol-message-post, protocol-message-received (v 1.0) - ts (session, uid, mclass, text, sent_time, ignore_level) - vs (session, uid, rcpts, text, format, sent, mclass, seq, secure) [protocol-message-post, protocol-message-recv] - vs (session, uid, rcpts, text, format, sent, mclass, seq, dobeep, secure) [protocol-message] */ { temp->argc = 6; temp->argv_type[0] = QUERY_ARG_CHARP; /* session, OK */ temp->argv_type[1] = QUERY_ARG_CHARP; /* uid, OK */ temp->argv_type[2] = QUERY_ARG_INT; /* mclass, N_OK, BAD POS */ args[2] = args2[6]; temp->argv_type[3] = QUERY_ARG_CHARP; /* text, OK */ temp->argv_type[4] = QUERY_ARG_INT; /* sent_time, N_OK, BAD POS */ args[4] = args2[5]; temp->argv_type[5] = QUERY_ARG_INT; /* ignore_level, N_OK, DONTEXISTS */ /* XXX, find ign_level */ args[5] = &ign_level; break; } default: debug("script_query_handlers() unk temp->hack: %d assuming 0.\n", temp->hack); break; } SCRIPT_HANDLER_FOOTER(script_handler_query, (void **) &args); if (temp->hack) { memcpy(temp, &saved, sizeof(script_query_t)); switch (temp->hack) { case 2: /* XXX, status CHANGED BY SCRIPT !!! args2[i] <==> args[i] */ xfree(status); break; case 3: case 4: case 5: /* XXX, ignore level changed by script !!! */ break; } } return ret; } /********************************************************************************/ /* from python.c python_autorun() * load scripts from `path` */ static int scripts_loaddir(scriptlang_t *s, const char *path) { struct dirent *d; struct stat st; char *tmp; scriptlang_t *slang; int i = 0; DIR *dir; if (!(dir = opendir(path))) return 0; while ((d = readdir(dir))) { tmp = saprintf("%s/%s", path, d->d_name); if (stat(tmp, &st) || S_ISDIR(st.st_mode)) { xfree(tmp); continue; } if (!(slang = scriptlang_from_ext(tmp)) || (s != NULL && s != slang) ) { xfree(tmp); continue; } debug("[script] autoloading %s from %s scr = %s\n", d->d_name, path, slang->name); if (!script_load(NULL, tmp)) i++; xfree(tmp); } closedir(dir); return i; } COMMAND(cmd_script) { scriptlang_t *s = NULL; char *tmp = NULL; char *param0 = NULL; if (xstrcmp(name, ("script"))) { /* script:* */ tmp = (char *) name; param0 = (char *) params[0]; } else if (params[0]) { /* script --* */ tmp = (char *) params[0]+2; param0 = (char *) params[1]; } /* s = param0 ? */ if (xstrlen(tmp) < 1) return script_list(NULL); else { if (xstrlen(params[0]) > 0) { /* somethink like we have in /plugin ;> e.g /script +dns /script -irc */ if (params[0][0] == '+') return script_load(NULL, (char *) params[0]+1); else if (params[0][0] == '-' && params[0][1] != '-') return script_unload_name(NULL, (char *) params[0]+1); } if (!xstrcmp(tmp, "load")) return script_load(NULL, param0); else if (!xstrcmp(tmp, "unload")) return script_unload_name(NULL, param0); else if (!xstrcmp(tmp, "list")) return script_list(s); else if (!xstrcmp(tmp, "varlist")) return script_var_list(NULL /*s*/); else if (!xstrcmp(tmp, "reset")) return script_reset(s); else if (!xstrcmp(tmp, "autorun")) return script_autorun(param0, -1); } return -1; } /* * load scripts from $CONFIGDIR/scripts/autorun ($CONFIGDIR - ~/.ekg2/ || ~/.ekg2/perl || ... ) * load scripts from $DATADIR/scripts/autorun ($DATADIR - /usr/share/ekg2 || /usr/local/share/ekg2 || ...) (Turned off ;>) */ static int scripts_autoload(scriptlang_t *scr) { int i = 0; /* i += scripts_loaddir(scr, DATADIR"/scripts/autorun"); */ /* I don't think it will be useful */ i += scripts_loaddir(scr, prepare_path("scripts/autorun", 0)); /* we ought to load only scripts from home dir. */ debug("[SCRIPTS_AUTOLOAD] DONE: (re)loaded %d scripts\n", i); return i; } int script_postinit(void *data, va_list ap) { return scripts_autoload(NULL); } int scripts_init() { script_variables_read(); query_connect(NULL, "config-postinit", script_postinit, NULL); return 0; } /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: * vim: noet */ ekg2-0.4~pre+20120506.1/ekg/scripts.h000066400000000000000000000163451175142753400165740ustar00rootroot00000000000000#ifndef EKG_SCRIPTS_H #define EKG_SCRIPTS_H #include #include "commands.h" #include "plugins.h" #include "protocol.h" #include "stuff.h" #include "vars.h" #include "queries.h" #ifdef __cplusplus extern "C" { #endif #define SCRIPT_HANDLE_UNBIND -666 #define MAX_ARGS QUERY_ARGS_MAX+1 typedef enum { SCRIPT_UNKNOWNTYPE, SCRIPT_VARTYPE, SCRIPT_COMMANDTYPE, SCRIPT_QUERYTYPE, SCRIPT_TIMERTYPE, SCRIPT_WATCHTYPE, SCRIPT_PLUGINTYPE, } script_type_t; typedef struct script { struct script *next; void *lang; char *name; char *path; void *priv_data; int inited; } script_t; extern script_t *scripts; typedef struct { script_t *scr; ekg_timer_t self; int removed; void *priv_data; } script_timer_t; typedef struct { script_t *scr; plugin_t *self; void *priv_data; } script_plugin_t; typedef struct { script_t *scr; variable_t *self; char *name; char *value; void *priv_data; } script_var_t; typedef struct { script_t *scr; query_t *self; int argc; int argv_type[MAX_ARGS]; int real_argc; void *priv_data; int hack; } script_query_t; typedef struct { script_t *scr; command_t *self; void *priv_data; } script_command_t; typedef struct { script_t *scr; watch_t *self; int removed; void *data; void *priv_data; } script_watch_t; typedef int (scriptlang_initialize_t)(); typedef int (scriptlang_finalize_t)(); typedef int (script_load_t)(script_t *); typedef int (script_unload_t)(script_t *); typedef int (script_handler_command_t)(script_t *, script_command_t *, char **); typedef int (script_handler_timer_t) (script_t *, script_timer_t *, int); typedef int (script_handler_var_t) (script_t *, script_var_t *, char *); typedef int (script_handler_query_t) (script_t *, script_query_t *, void **); typedef int (script_handler_watch_t) (script_t *, script_watch_t *, int, int, int); typedef int (script_free_bind_t) (script_t *, void *, int, void *, ...); typedef struct scriptlang { struct scriptlang *next; char *name; /* perl, python, php *g* and so on. */ char *ext; /* .pl, .py, .php ... */ plugin_t *plugin; scriptlang_initialize_t *init; scriptlang_finalize_t *deinit; script_load_t *script_load; script_unload_t *script_unload; script_free_bind_t *script_free_bind; script_handler_query_t *script_handler_query; script_handler_command_t*script_handler_command; script_handler_timer_t *script_handler_timer; script_handler_var_t *script_handler_var; script_handler_watch_t *script_handler_watch; void *priv_data; } scriptlang_t; extern scriptlang_t *scriptlang; #define SCRIPT_FINDER(bool)\ script_t *scr = NULL;\ scriptlang_t *slang = NULL;\ \ for (scr = scripts; scr; scr = scr->next) {\ slang = scr->lang;\ if (bool)\ return scr;\ }\ return NULL; /* XXX: Split *_watches() into normal and line-ones. * * Until then we abort the build if sizeof(void*) > sizeof(long int), as * in that case the pointer would get corrupted when being passed around * as a long using type-punning. * * This trick was stolen from the Linux kernel. See * http://scaryreasoner.wordpress.com/2009/02/28/checking-sizeof-at-compile-time/ * for an explanation. */ #define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)])) #define SCRIPT_DEFINE(x, y)\ extern int x##_load(script_t *);\ extern int x##_unload(script_t *);\ \ extern int x##_commands(script_t *, script_command_t *, char **);\ extern int x##_timers(script_t *, script_timer_t *, int );\ extern int x##_variable_changed(script_t *, script_var_t *, char *);\ extern int x##_query(script_t *, script_query_t *, void **);\ extern int x##_watches(script_t *, script_watch_t *, int, int, long int);\ void x##_dummy_sanity_check() { BUILD_BUG_ON(sizeof(void *) > sizeof(long)); };\ \ extern int x##_bind_free(script_t *, void *, int type, void *, ...);\ \ scriptlang_t x##_lang = { \ name: #x, \ plugin: &x##_plugin, \ ext: y, \ \ init: x##_initialize,\ deinit: x##_finalize, \ \ script_load: x##_load, \ script_unload: x##_unload, \ script_free_bind: x##_bind_free,\ \ script_handler_query : x##_query,\ script_handler_command: x##_commands,\ script_handler_timer : x##_timers,\ script_handler_var : x##_variable_changed,\ script_handler_watch : x##_watches,\ } #define script_private_get(s) (s->priv_data) #define script_private_set(s, p) (s->priv_data = p) #ifndef EKG2_WIN32_NOFUNCTION int script_unload_lang(scriptlang_t *s); int script_list(scriptlang_t *s); int script_unload_name(scriptlang_t *s, char *name); int script_load(scriptlang_t *s, char *name); int scriptlang_register(scriptlang_t *s); int scriptlang_unregister(scriptlang_t *s); int scripts_init(); script_t *script_find(scriptlang_t *s, char *name); int script_query_unbind(script_query_t *squery, int from); int script_command_unbind(script_command_t *scr_comm, int free); int script_timer_unbind(script_timer_t *stimer, int free); int script_var_unbind(script_var_t *data, int free); int script_watch_unbind(script_watch_t *temp, int free); script_command_t *script_command_bind(scriptlang_t *s, script_t *scr, char *command, char *params, char *possibilities, void *handler); script_timer_t *script_timer_bind(scriptlang_t *s, script_t *scr, int freq, void *handler); script_query_t *script_query_bind(scriptlang_t *s, script_t *scr, char *qname, void *handler); script_var_t *script_var_add_full(scriptlang_t *s, script_t *scr, char *name, int type, char *value, void *handler); script_var_t *script_var_add(scriptlang_t *s, script_t *scr, char *name, char *value, void *handler); script_watch_t *script_watch_add(scriptlang_t *s, script_t *scr, int fd, int type, void *handler, void *data); script_plugin_t *script_plugin_init(scriptlang_t *s, script_t *scr, char *name, plugin_class_t pclass, void *handler); void script_variables_free(); void script_variables_write(); #endif #define SCRIPT_UNBIND_HANDLER(type, args...) \ {\ SCRIPT_HANDLER_HEADER(script_free_bind_t);\ SCRIPT_HANDLER_FOOTER(script_free_bind, type, temp->priv_data, args);\ } /* BINDING && UNBINDING */ #define SCRIPT_BIND_HEADER(x) \ x *temp; \ if (scr && scr->inited != 1) {\ debug_error("[script_bind_error] script not inited!\n"); \ return NULL; \ } \ temp = xmalloc(sizeof(x)); \ temp->scr = scr;\ temp->priv_data = handler; #define SCRIPT_BIND_FOOTER(y) \ if (!temp->self) {\ debug("[script_bind_error] (before adding to %s) ERROR! retcode = 0x%x\n", #y, temp->self);\ xfree(temp);\ return NULL;\ }\ list_add(&y, temp);\ return temp; /* HANDLERS */ #define SCRIPT_HANDLER_HEADER(x) \ script_t *_scr;\ scriptlang_t *_slang;\ x *_handler;\ int ret = SCRIPT_HANDLE_UNBIND; /* TODO: quietmode */ #define SCRIPT_HANDLER_FOOTER(y, _args...) \ if ((_scr = temp->scr) && ((_slang = _scr->lang))) _handler = _slang->y;\ else _handler = temp->priv_data;\ if (_handler)\ ret = _handler(_scr, temp, _args); \ else {\ debug("[%s] (_handler == NULL)\n", #y);\ ret = SCRIPT_HANDLE_UNBIND;\ }\ \ if (ret == SCRIPT_HANDLE_UNBIND) { debug("[%s] script or scriptlang want to delete this handler\n", #y); }\ if (ret == SCRIPT_HANDLE_UNBIND) #define SCRIPT_HANDLER_MULTI_FOOTER(y, _args...)\ /* foreach y->list->next do SCRIPT_HANDLER_FOOTER(y->list->data, _args); */\ SCRIPT_HANDLER_FOOTER(y, _args) #ifdef __cplusplus } #endif #endif ekg2-0.4~pre+20120506.1/ekg/sessions.c000066400000000000000000001217031175142753400167410ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2003 Wojtek Kaniewski * 2004 Piotr Kupisiewicz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #include #include #include #include #include #include #include #include #include "objects.h" session_t *sessions = NULL; static LIST_ADD_COMPARE(session_compare, session_t *) { return xstrcasecmp(data1->uid, data2->uid); } static __DYNSTUFF_LIST_ADD_SORTED(sessions, session_t, session_compare); /* sessions_add() */ static __DYNSTUFF_LIST_COUNT(sessions, session_t); /* sessions_count() */ static LIST_FREE_ITEM(session_param_free_item, session_param_t *) { xfree(data->key); xfree(data->value); } static __DYNSTUFF_ADD_BEGINNING(session_vars, session_param_t, NULL); /* session_vars_add() */ static __DYNSTUFF_DESTROY(session_vars, session_param_t, session_param_free_item); /* session_vars_destroy() */ session_t *session_current = NULL; /** * session_find_ptr() * * it's search over sessions list and checks if param @a s is in that list. * it's useful for all watch handler, and if programmer was too lazy to destroy watches associated with that * session (in private watch data struct) before it gone. * * @note It's possible to find another session with the same address as old one.. it's rather not possible.. however. * It's better if you use @a session_find() function.. Yeah, i know it's slower. * * @param s - session to look for. * * @return It returns @a s if session was found, otherwise NULL. */ session_t *session_find_ptr(session_t *s) { session_t *sl; for (sl = sessions; sl; sl = sl->next) { if (sl == s) return s; } return NULL; } /** * session_find() * * It's search over sessions list and checks if we have session with uid @a uid * * @param uid - uid of session you look for * @sa session_find_ptr() - If you are looking for smth faster ;) but less reliable. * * @return It returns pointer to session_t struct of found session, or NULL * */ session_t *session_find(const char *uid) { session_t *s; if (!uid) return NULL; for (s = sessions; s; s = s->next) { if (!xstrcasecmp(s->uid, uid) || (s->alias && !xstrcasecmp(s->alias, uid))) return s; } return NULL; } /** * session_add() * * Add session with @a uid to session list.
* Check by plugin_find_uid() if any plugin can handle this type of session if not return NULL
* Allocate memory for variables, switch windows.. etc, etc..
* Emit global SESSION_ADDED plugin which handle this session can alloc memory for his private data
* * @todo See XXX's * * @param uid - full uid of new session * * @sa session_remove() - To remove session * * @return NULL if none plugin can handle this session uid
* allocated session_t struct. */ session_t *session_add(const char *uid) { plugin_t *pl; session_t *s; window_t *w; if (!uid) return NULL; if (!(pl = plugin_find_uid(uid))) { /* search for plugin */ debug_error("session_add() Invalid UID: %s\n", uid); return NULL; } s = xmalloc(sizeof(session_t)); s->uid = xstrdup(uid); s->status = EKG_STATUS_AVAIL; /* note: here we had EKG_STATUS_NA, but some protocol plugins doesn't like EKG_STATUS_NA at connect */ s->plugin = pl; #ifdef HAVE_FLOCK s->lock_fd = -1; #endif sessions_add(s); /* XXX, wywalic sprawdzanie czy juz jest sesja? w koncu jak dodajemy sesje.. to moze chcemy sie od razu na nia przelaczyc? */ if (!window_current->session && (window_current == window_debug || window_current == window_status)) window_session_set(window_current, s); /* XXX, i still don't understand why session_current isn't macro to window_current->session... */ if (!session_current) session_current = s; /* session_var_default() */ if (pl->params) { int count, i; for (count=0; (pl->params[count].key /* && p->params[count].id != -1 */); count++); /* count how many _global_ params should have this sessioni */ s->values = (char **) xcalloc(count+1, sizeof(char *)); /* alloc memory for it, +1 just in case. */ s->global_vars_count = count; /* save it for future, little helper... */ /* set variables */ for (i=0; i < count; i++) { const char *key = pl->params[i].key; const char *value = pl->params[i].value; s->values[i] = xstrdup(value); /* sorry, but to simplify plugin writing we've to assure handler * is never called with nonconnected session */ if (!xstrcasecmp(key, "statusdescr")) continue; /* notify plugin, like session_set() do */ if (pl->params[i].notify) pl->params[i].notify(s, key); } } query_emit(NULL, "session-added", &(s->uid)); /* It's read-only query, XXX */ for (w = windows; w; w = w->next) { /* previous version was unacceptable. So we do now this trick: * userlist (if plugin has one) have been already read by SESSION_ADDED emit. so now, * we check throught get_uid() if this plugin can handle it.. [userlist must be read, if we have nosession window * with w->target: "Aga". it's not uid. it's nickname.. so we must search for it in userlist. * it's better idea than what was.. however it's slow and I still want to do it other way. */ if (!w->session && !w->floating && get_uid(s, w->target)) window_session_set(w, s); } return s; } static LIST_FREE_ITEM(session_free_item, session_t *) { /* free _global_ session variables */ array_free_count(data->values, data->global_vars_count); /* free _local_ session variables */ session_vars_destroy(&(data->local_vars)); xfree(data->alias); xfree(data->uid); xfree(data->descr); xfree(data->password); xfree(data->last_descr); /* free memory like sessions_free() do */ userlist_free(data); } static __DYNSTUFF_LIST_REMOVE_SAFE(sessions, session_t, session_free_item); /* sessions_remove() */ static __DYNSTUFF_LIST_DESTROY(sessions, session_t, session_free_item); /* sessions_destroy() */ /** * session_remove() * * Remove session with uid passed in @a uid
* This function free session params variable and internal
* session data like alias, current status, descr, password..
* If sesssion is connected, /disconnect command will be executed with session uid as reason
* Also it remove watches connected with this session (if watch->is_session && watch->data == s)br> * It'll do window->session swapping if needed... and changing current session also. * * @bug Possible implementation/idea bug in window_session_cycle() I really don't know if we should change session on this windows... * Maybe swaping is ok... but we really need think about protocol.. Now If we change session from jabber to gg one this window * won't be very useful.. * * @note If plugin allocated memory for session example in s->priv you should * connect to SESSION_REMOVED query event, and free alloced memory * (remember about checking if this is your session) timers and watches * will be automagicly removed. * * @note Current ekg2 API have got session watches. Use watch_add_session() * * @note Current ekg2 API have got timer watches. Use timer_add_session() * * @param uid - uid of session to remove * * @return 0 if session was found, and removed.
* -1 if session wasn't founded. */ int session_remove(const char *uid) { session_t *s; window_t *w; char *tmp; int count; list_t l; if (!(s = session_find(uid))) return -1; if (s == session_current) session_current = NULL; count = sessions_count(); for (w = windows; w; w = w->next) { if (w->session == s) { w->session = NULL; if (count > 1) window_session_cycle(w); } } if (s->connected) command_exec_format(NULL, s, 1, ("/disconnect %s"), s->uid); #ifdef HAVE_FLOCK if (s->lock_fd != -1) { /* this shouldn't happen */ flock(s->lock_fd, LOCK_UN); close(s->lock_fd); /* XXX: unlink then? */ } #endif /* remove session watches */ for (l = watches; l; l = l->next) { watch_t *w = l->data; if (w && w->is_session && w->data == s) watch_free(w); } ekg_source_remove_by_data(s, NULL); tmp = xstrdup(uid); query_emit(NULL, "session-changed"); query_emit(NULL, "session-removed", &tmp); xfree(tmp); sessions_remove(s); return 0; } PROPERTY_INT_GET(session, status, int) int session_status_set(session_t *s, status_t status) { int is_xa; if (!s) return -1; if (status == EKG_STATUS_UNKNOWN) /* we shouldn't set our status to unknown ( ; */ status = EKG_STATUS_AVAIL; { char *__session = xstrdup(s->uid); int __status = status; query_emit(NULL, "session-status", &__session, &__status); xfree(__session); } /* if it's autoaway or autoxa */ if ((is_xa = (status == EKG_STATUS_AUTOXA)) || (status == EKG_STATUS_AUTOAWAY)) { char *tmp = (char*) session_get(s, (is_xa ? "auto_xa_descr" : "auto_away_descr")); /* save current status/ descr && turn autoaway on */ if (!s->autoaway) { /* don't overwrite laststatus, if already on aa */ xfree(s->last_descr); /* just in case */ s->last_status = s->status; s->last_descr = xstrdup(s->descr); s->autoaway = 1; } /* new status */ s->status = (is_xa ? EKG_STATUS_XA : EKG_STATUS_AWAY); /* new descr */ if (tmp) { xfree(s->descr); if (xstrchr(tmp, '%')) { /* the New&Better-AutoAway-Markup^TM */ const char *current_descr = (s->autoaway ? s->last_descr : s->descr); char *c, *xbuf, *xc; int xm = 0; /* following thing is used to count how large buffer do we need * yep, I know that we can waste some space * yep, I know that this would also count %%$, but I don't think we need to care that much * if user does want to use shitty strings, it's his problem */ for (c = tmp; (c = xstrstr(c, "%$")); c++, xm++); xbuf = xmalloc(xstrlen(tmp) + (xm * xstrlen(current_descr)) + 1); xc = xbuf; xm = 1; /* previously xm was used as %$ counter, now it says if we should copy or skip */ for (c = tmp; *c; c++) { if (*c == '%') { switch (*(++c)) { case '?': /* write if descr is set */ xm = !!(current_descr); break; case '!': /* write if descr isn't set */ xm = !(current_descr); break; case '/': /* always write */ xm = 1; break; /* do we need to employ some never-write (i.e. comments)? */ case '$': /* insert current descr */ if (current_descr) { /* Here I use memcpy(), 'cause I already need to get strlen(), and the function itself puts final \0 */ const int xl = xstrlen(current_descr); memcpy(xc, current_descr, xl); xc += xl; } break; default: /* other chars, i.e. someone's forgotten to escape % */ if (xm) *(xc++) = '%'; case '%': /* above, or escaped % */ if (*c == '\0') /* oops, escaped NULL? ( ; */ c--; /* else for loop won't break */ else if (xm) *(xc++) = *c; } } else if (xm) /* normal char */ *(xc++) = *c; } *xc = '\0'; /* make sure we end with \0 */ s->descr = xrealloc(xbuf, strlen(xbuf)+1); /* free unused bytes */ } else /* no markup, just copy */ s->descr = xstrdup(tmp); } return 0; } /* if it's autoback */ if (status == EKG_STATUS_AUTOBACK) { /* set status */ s->status = s->last_status ? s->last_status : EKG_STATUS_AVAIL; /* set descr */ if (s->autoaway || s->last_descr) { xfree(s->descr); s->descr = s->last_descr; } s->last_status = 0; s->last_descr = NULL; s->autoaway = 0; return 0; } s->status = status; /* if it wasn't neither _autoback nor _autoaway|_autoxa, it should be one of valid status types... */ if (s->autoaway) { /* if we're @ away, set previous, set lastdescr status & free data */ s->last_status = 0; /* EKG_STATUS_NULL */ xfree(s->descr); s->descr = s->last_descr; s->last_descr = NULL; s->autoaway = 0; } return 0; } int session_password_set(session_t *s, const char *password) { xfree(s->password); s->password = (password) ? base64_encode(password, xstrlen(password)+1) : NULL; /* XXX: +1? */ return 0; } /** * session_password_get() * * It's decrypt ,,encrypted''(and return) using base64 from session struct (s->password) is @a s was passed, otherwise * it cleanup decrypted password from internal buffer. * * @note static buffer is a better idea - i think (del) * @sa base64_decode() - function to decode base64 strings. * @sa session_get() - session_get() can be used to get password also... but it's just a wrapper with several xstrcmp() to this function * so it's slower. Instead of using session_get(session, "password") it's better If you use: session_password_get(session) * * @param s - session of which we want get password from or NULL if we want to erase internal buf with password * * @return ,,decrypted'' password if succeed
* otherwise ""
* or even NULL if @a s was NULL */ const char *session_password_get(session_t *s) { static char buf[100]; char *tmp; if (!s) { memset(buf, 0, sizeof(buf)); return NULL; } tmp = base64_decode(s->password); if (!tmp) return ""; g_strlcpy(buf, tmp, sizeof(buf)); xfree(tmp); return buf; } PROPERTY_STRING_GET(session, descr) int session_descr_set(session_t *s, const char *descr) { if (!s) return -1; if (s->autoaway) { xfree(s->last_descr); s->last_descr = xstrdup(descr); } else { xfree(s->descr); s->descr = xstrdup(descr); } return 0; } PROPERTY_STRING(session, alias) PROPERTY_PRIVATE(session) PROPERTY_INT_GET(session, connected, int) int session_connected_set(session_t *s, int connected) { if (!s) return -1; s->connected = connected; return 0; } PROPERTY_STRING_GET(session, uid) /* Splits statusdescr into status and descr. * Returns status as status_t, and sets *statusdescr to descr. * Doesn't modify original *statusdescr */ static inline const status_t session_statusdescr_split(const char **statusdescr) { const char *descr = xstrchr(*statusdescr, ' '); char *status = NULL; status_t nstatus; if (descr) { status = xstrndup(*statusdescr, (descr - *statusdescr)); descr++; } nstatus = ekg_status_int(status ? status : *statusdescr); *statusdescr = descr; if (descr) xfree(status); /* was duplicated */ return nstatus; } static inline status_t session_status_nearest(session_t *s, status_t status) { plugin_t *p = s->plugin; struct protocol_plugin_priv *pp = (struct protocol_plugin_priv *)p->priv; const status_t *ast; const int dir = (status < EKG_STATUS_AVAIL); g_assert(p->pclass == PLUGIN_PROTOCOL); if (!p->priv) { debug_warn("session_status_nearest(), plugin '%s' didn't declare supported statuses.\n", p->name); return status; } /* It's really hard to guess intentions * so we currently are going 'towards' EKG_STATUS_AVAIL * i.e. if status < EKG_STATUS_AVAIL, we're increasing it, * else we're decreasing it */ for (; dir ? (status <= EKG_STATUS_AVAIL) : (status >= EKG_STATUS_AVAIL); dir ? status++ : status--) { if (status <= EKG_STATUS_NA || status >= EKG_STATUS_LAST) continue; for (ast = pp->statuses; ast && (*ast != EKG_STATUS_NULL); ast++) { if (*ast == status) /* is supported? */ return status; } } return EKG_STATUS_NULL; } /* Just simple setter for both status&descr * The real magic should be done in plugin-defined notify handler */ static const int session_statusdescr_set(session_t *s, const char *statusdescr) { const char *descr = statusdescr; status_t status = session_statusdescr_split(&descr); const char *label; debug_function("session_statusdescr_set(), status = %s [%d], descr = %s\n", ekg_status_string(status, 2), status, descr); if (status == EKG_STATUS_NULL) return -1; /* if incorrect status, don't do anything! */ status = session_status_nearest(s, status); /* check whether plugin supports this status, if not find nearest */ if (status == EKG_STATUS_NULL) { debug_function("session_statusdescr_set(), status setting on session '%s' not supported\n", session_uid_get(s)); return -1; } session_status_set(s, status); session_descr_set(s, descr); session_unidle(s); ekg_update_status(s); /* ok, we suck even more. formats for statuschanges are command-based. */ label = ekg_status_string(status, 1); debug_function("session_statusdescr_set(), -> status = %s [%d], label = %s\n", ekg_status_string(status, 2), status, label); if (descr) { char l[xstrlen(label)+7]; sprintf(l, "%s_descr", label); print(l, descr, "", session_name(s)); } else print(label, session_name(s)); return 0; } /* * session_localvar_find() * * it looks for given _local_ var in given session */ session_param_t *session_localvar_find(session_t *s, const char *key) { session_param_t *pl; if (!s) return NULL; for (pl = s->local_vars; pl; pl = pl->next) { session_param_t *v = pl; if (!xstrcmp(v->key, key)) return v; } return NULL; } static plugins_params_t *PLUGIN_VAR_FIND_BYID(plugin_t *plugin, int id) { return id ? &(((plugin_t *) plugin)->params[id-1]) : NULL; } /* * session_get() * * pobiera parametr sesji. */ const char *session_get(session_t *s, const char *key) { session_param_t *sp; variable_t *v; int paid; /* it's plugin_params id, not paid */ if (!s) return NULL; if (!xstrcasecmp(key, "uid")) return session_uid_get(s); if (!xstrcasecmp(key, "alias")) return session_alias_get(s); if (!xstrcasecmp(key, "descr")) return session_descr_get(s); if (!xstrcasecmp(key, "status")) return ekg_status_string(session_status_get(s), 2); if (!xstrcasecmp(key, "statusdescr")) return NULL; /* XXX? */ if (!xstrcasecmp(key, "password")) return session_password_get(s); /* _global_ session variables */ if ((paid = plugin_var_find(s->plugin, key))) { /* debug("session_get() CHECK [%s, %d] value: %s\n", PLUGIN_VAR_FIND_BYID(s->plugin, paid)->key, paid-1, s->values[paid-1]); */ return s->values[paid-1]; } /* _local_ session variables */ if ((sp = session_localvar_find(s, key))) return sp->value; if (!(v = variable_find(key)) || (v->type != VAR_INT && v->type != VAR_BOOL)) return NULL; return ekg_itoa(*(int*)(v->ptr)); } /* * session_int_get() * * pobiera parametr sesji jako liczb. */ int session_int_get(session_t *s, const char *key) { const char *tmp = session_get(s, key); if (!tmp) return -1; return strtol(tmp, NULL, 0); } /* * session_is_var() * * checks if given variable is correct for given session */ int session_is_var(session_t *s, const char *key) { const char *intvars[] = { "alias", "descr", "status", "password", "statusdescr", NULL }; const char **intvar; if (!s) return -1; /* XXX: maybe move this above !s? */ for (intvar = intvars; *intvar; intvar++) if (!xstrcasecmp(key, *intvar)) return 1; /* so maybe _global_ session variable? */ return (plugin_var_find(s->plugin, key) != 0); } /* * session_set() * * ustawia parametr sesji. */ int session_set(session_t *s, const char *key, const char *value) { session_param_t *v; plugins_params_t *pa; int paid; int ret = 0; if (!s) return -1; paid = plugin_var_find(s->plugin, key); pa = PLUGIN_VAR_FIND_BYID(s->plugin, paid); if (!xstrcasecmp(key, "uid")) return -1; if (!xstrcasecmp(key, "alias")) { char *tmp; ret = session_alias_set(s, value); /* note: * if we unset session alias, than value is NULL * some code in metacontacts and remote plugin * rely on if they can find session, session_find(NULL) will always return NULL :( * * I think it'll be better if we always pass s->uid * but for now this is enough for me */ tmp = xstrdup((value) ? value : s->uid); query_emit(NULL, "session-renamed", &tmp); xfree(tmp); goto notify; } if (!xstrcasecmp(key, "descr")) { ret = session_descr_set(s, value); goto notify; } if (!xstrcasecmp(key, "status")) { ret = session_status_set(s, ekg_status_int(value)); goto notify; } if (!xstrcasecmp(key, "statusdescr")) { ret = session_statusdescr_set(s, value); if (ret != 0 || !session_connected_get(s)) /* temporary workaround, see XXX @ notify: + don't notify when not connected */ return ret; goto notify; } if (!xstrcasecmp(key, "password")) { if (s->connected && !session_get(s, "__new_password")) print("session_password_changed", session_name(s)); ret = session_password_set(s, value); goto notify; } if (paid) { /* debug("session_set() CHECK [%s, %d] value: %s\n", pa->key, paid-1, value); */ xfree(s->values[paid-1]); s->values[paid-1] = xstrdup(value); goto notify; } if ((v = session_localvar_find(s, key))) { xfree(v->value); v->value = xstrdup(value); return 0; } v = xmalloc(sizeof(session_param_t)); v->key = xstrdup(key); v->value = xstrdup(value); session_vars_add(&s->local_vars, v); return 0; notify: if (pa && pa->notify) /* XXX: notify only when ret == 0 ? */ pa->notify(s, key); return ret; } /* * session_int_set() * * ustawia parametr sesji jako liczb. */ int session_int_set(session_t *s, const char *key, int value) { return session_set(s, key, ekg_itoa(value)); } /* * session_read() * * czyta informacje o sesjach z pliku. */ int session_read(const gchar *plugin_name) { gchar *line; GDataInputStream *f; session_t *s = NULL; int ret = 0; if (!plugin_name) { GSList *pl; if (!in_autoexec) { session_t *sf; for (sf = sessions; sf; sf = sf->next) command_exec(NULL, sf, ("/disconnect"), 1); sessions_free(); debug(" flushed sessions\n"); } for (pl = plugins; pl; pl = pl->next) { const plugin_t *p = pl->data; if (!p || p->pclass != PLUGIN_PROTOCOL) continue; ret += session_read(p->name); } return ret; } if (!(f = G_DATA_INPUT_STREAM(config_open("sessions-%s", "r", plugin_name)))) return -1; while ((line = read_line(f))) { char *tmp; if (line[0] == '#' || line[0] == ';' || (line[0] == '/' && line[1] == '/')) continue; if (line[0] == '[') { tmp = xstrchr(line, ']'); if (!tmp) continue; *tmp = 0; s = session_add(line + 1); continue; } if ((tmp = xstrchr(line, '='))) { *tmp = 0; tmp++; if (!session_is_var(s, line)) { debug("\tSession variable \"%s\" is not correct\n", line); continue; } xstrtr(tmp, '\002', '\n'); if(*tmp == '\001') { char *decoded = base64_decode(tmp + 1); session_set(s, line, decoded); xfree(decoded); } else session_set(s, line, tmp); } } g_object_unref(f); return ret; } /* * session_write() * * writes information about sessions in files */ void session_write() { GSList *pl; GOutputStream *f = NULL; for (pl = plugins; pl; pl = pl->next) { const plugin_t *p = pl->data; session_t *s; if (p->pclass != PLUGIN_PROTOCOL) continue; /* skip no protocol plugins */ if (!(f = G_OUTPUT_STREAM(config_open("sessions-%s", "w", p->name)))) break; for (s = sessions; s; s = s->next) { int i; if (s->plugin != p) continue; ekg_fprintf(f, "[%s]\n", s->uid); if (s->alias) ekg_fprintf(f, "alias=%s\n", s->alias); if (s->status && config_keep_reason != 2) ekg_fprintf(f, "status=%s\n", ekg_status_string(s->autoaway ? s->last_status : s->status, 0)); if (s->descr && config_keep_reason) { char *myvar = (s->autoaway ? s->last_descr : s->descr); xstrtr(myvar, '\n', '\002'); ekg_fprintf(f, "descr=%s\n", myvar); xstrtr(myvar, '\002', '\n'); } if (s->password && config_save_password) ekg_fprintf(f, "password=\001%s\n", s->password); if (!p->params) continue; for (i = 0; (p->params[i].key /* && p->params[i].id != -1 */); i++) { if (!s->values[i]) continue; ekg_fprintf(f, "%s=%s\n", p->params[i].key, s->values[i]); } /* We don't save _local_ variables */ } for (s = sessions; s; s = s->next) { if (s->plugin != p) continue; userlist_write(s); } } } /* * session_format() * * formatuje adnie nazw sesji zgodnie z wybranym tematem. * * - s - sesja. */ const char *session_format(session_t *s) { static char buf[256]; const char *uid; char *tmp; if (!s) return ""; uid = s->uid; /* if (xstrchr(uid, ':')) uid = xstrchr(uid, ':') + 1; */ if (!s->alias) tmp = format_string(format_find("session_format"), uid, uid); else tmp = format_string(format_find("session_format_alias"), s->alias, uid); g_strlcpy(buf, tmp, sizeof(buf)); xfree(tmp); return buf; } /* * session_check() * * sprawdza, czy dana sesja zawiera prywatne dane pluginu i jest danego * protokou. * * 0/1 */ int session_check(session_t *s, int need_private, const char *protocol) { if (!s) return 0; if (need_private && !s->priv) return 0; if (protocol) { int plen = xstrlen(protocol); if (xstrlen(s->uid) < plen + 1) return 0; if (strncmp(s->uid, protocol, plen) || s->uid[plen] != ':') return 0; } return 1; } /* * session_name() * * returns static buffer with formated session name */ const char *session_name(session_t *s) { static char buf[150]; char *tmp = format_string(format_find("session_name"), s ? (s->alias) ? s->alias : s->uid : "?"); g_strlcpy(buf, tmp, sizeof(buf)); xfree(tmp); return buf; } /* * session_unidle() * * funkcja wywoywana, gdy w danej sesji uytkownik podj jakie dziaania. * jeli dla danej sesji zostanie wywoana cho raz ta funkcja, bdzie ona * brana pod uwag przy autoawayu. po przekroczeniu okrelonego czasu, * zostanie wywoana komenda /_autoaway dla tej sesji, a potem /_autoback. * naley je obsugiwa, inaczej bd mieci na ekranie. * * 0/-1 */ int session_unidle(session_t *s) { if (!s) return -1; s->activity = time(NULL); if (s->autoaway && session_int_get(s, "auto_back") == 1) command_exec(NULL, s, ("/_autoback"), 0); return 0; } /* * session_command() * * obsuga komendy /session */ COMMAND(session_command) { session_t *s; if (!params[0] || match_arg(params[0], 'l', ("list"), 2)) { for (s = sessions; s; s = s->next) { const char *descr = (s->connected) ? s->descr : NULL; const int status = (s->connected) ? s->status : EKG_STATUS_NA; char *tmp; /* wtf? vvvvvv */ tmp = format_string(format_find(ekg_status_label(status, descr, "user_info_")), "foobar", descr); if (!s->alias) printq("session_list", s->uid, s->uid, tmp); else printq("session_list_alias", s->uid, s->alias, tmp); xfree(tmp); } if (!sessions) printq("session_list_empty"); return 0; } if (!xstrcasecmp(params[0], "--dump")) { for (s = sessions; s; s = s->next) { plugin_t *p = s->plugin; session_param_t *pl; int i; debug("[%s]\n", s->uid); if (s->alias) debug("alias=%s\n", s->alias); if (s->status) debug("status=%s\n", ekg_status_string(s->autoaway ? s->last_status : s->status, 0)); if (s->descr) debug("descr=%s\n", (s->autoaway ? s->last_descr : s->descr)); /* _global_ vars: */ if (p) { for (i = 0; (p->params[i].key /* && p->params[i].id != -1 */); i++) debug("%s=%s\n", p->params[i].key, s->values[i]); } else debug_error("FATAL: [%s] plugin somewhere disappear :(\n", s->uid); /* _local_ vars: */ for (pl = s->local_vars; pl; pl = pl->next) { session_param_t *v = pl; if (v->value) debug("%s=%s\n", v->key, v->value); } } return 0; } if (match_arg(params[0], 'a', ("add"), 2)) { session_t *s; if (session_find(params[1])) { printq("session_exists", params[1]); return -1; } /* add session, session_add() only fails if there is no plugin handling this uid... so it's ok */ if (!(s = session_add(params[1]))) { printq("invalid_uid", params[1]); return -1; } config_changed = 1; printq("session_added", s->uid); return 0; } if (match_arg(params[0], 'd', ("del"), 2)) { /* remove session, session_remove() only fails (ret -1) if session wasn't found.. so it's ok */ if (session_remove(params[1])) { printq("session_doesnt_exist", params[1]); return -1; } config_changed = 1; printq("session_removed", params[1]); return 0; } if (match_arg(params[0], 'w', ("sw"), 2)) { session_t *s; if (!params[1]) { printq("not_enough_params", name); return -1; } if (!(s = session_find(params[1]))) { printq("session_doesnt_exist", params[1]); return -1; } if (window_current->session == s) return 0; /* we don't need to switch to the same session */ if (window_current->target && (window_current->id != 0)) command_exec(NULL, NULL, "/window switch 1", 2); window_session_set(window_current, s); return 0; } if (match_arg(params[0], 'g', ("get"), 2)) { /* /session --get [session uid] */ const char *key; /* variable name */ char *tmp = NULL; if (!params[1]) { printq("not_enough_params", name); return -1; } if ((s = session_find(params[1]))) { key = params[2]; } else { if (!(s = session)) { printq("invalid_session"); return -1; } key = params[1]; } if (!key) { printq("not_enough_params", name); return -1; } if (!session_variable_display(s, key, quiet)) { /* XXX, idea, here we can do: session_localvar_find() to check if this is _local_ variable, and perhaps print other info.. * The same at --set ? */ printq("session_variable_doesnt_exist", session_name(s), key); return -1; } xfree(tmp); return 0; } if (match_arg(params[0], 's', ("set"), 2)) { if (!params[1]) { printq("not_enough_params", name); return -1; } if (!(s = session_find(params[1]))) { if (params[1][0] == '-') { if (session) { if (!session_is_var(session, params[1] + 1)) { printq("session_variable_doesnt_exist", session_name(session), params[1] + 1); return -1; } session_set(window_current->session, params[1] + 1, NULL); config_changed = 1; printq("session_variable_removed", session_name(session), params[1] + 1); return 0; } else { printq("invalid_session"); return -1; } } if(params[2] && !params[3]) { if (session) { if (!session_is_var(session, params[1])) { printq("session_variable_doesnt_exist", session_name(session), params[1]); return -1; } session_set(session, params[1], params[2]); config_changed = 1; command_exec_format(NULL, s, 0, ("%s --get %s %s"), name, session->uid, params[1]); return 0; } else { printq("invalid_session"); return -1; } } if(params[2] && params[3]) { printq("session_doesnt_exist", params[1]); return -1; } if (!xstrcmp(params[1], "password")) { char *prompt = format_string("session_password_input", session_name(session)); char *pass = password_input(prompt, NULL, 1); xfree(prompt); if (pass) { session_set(session, params[1], pass); config_changed = 1; command_exec_format(NULL, s, 0, ("%s --get %s %s"), name, session->uid, params[1]); xfree(pass); } return 0; } printq("invalid_params", name, params[1]); /* XXX */ return -1; } if (params[2] && params[2][0] == '-') { if (!session_is_var(s, params[2] + 1)) { printq("session_variable_doesnt_exist", session_name(session), params[2] + 1); return -1; } session_set(s, params[2] + 1, NULL); config_changed = 1; printq("session_variable_removed", session_name(s), params[2] + 1); return 0; } if(params[2] && params[3]) { if (!session_is_var(s, params[2])) { printq("session_variable_doesnt_exist", session_name(session), params[2]); return -1; } session_set(s, params[2], params[3]); config_changed = 1; command_exec_format(NULL, s, 0, ("%s --get %s %s"), name, s->uid, params[2]); return 0; } if (!xstrcmp(params[2], "password")) { char *prompt = format_string(format_find("session_password_input"), session->uid); char *pass = password_input(prompt, NULL, 1); xfree(prompt); if (pass) { session_set(session, params[2], pass); config_changed = 1; command_exec_format(NULL, s, 0, ("%s --get %s %s"), name, session->uid, params[2]); xfree(pass); } return 0; } printq("invalid_params", name, params[1]); /* XXX */ return -1; } if (match_arg(params[0], 'L', "lock", 2)) { int fd; const char *path; session_t *s; if (params[1]) { if (!(s = session_find(params[1]))) { printq("session_doesnt_exist", params[1]); return -1; } } else s = session; if (!s) { printq("invalid_session"); return -1; } if (!config_session_locks) { /* printq("var_not_set", name, "session_locks"); */ return 0; } #ifdef HAVE_FLOCK if (config_session_locks == 1 && s->lock_fd != -1) { printq("session_locked", session_name(s)); return -1; } #endif if ((path = prepare_pathf("%s-lock", session_uid_get(s)))) fd = open(path, O_CREAT|O_WRONLY | (config_session_locks != 1 ? O_EXCL /* if we don't use flock(), we just take care of file's existence */ : O_TRUNC | O_NONBLOCK /* if someone's set up shitpipe for us */ ), S_IWUSR); else return 0; if (fd == -1) { if (errno == EEXIST) { printq("session_locked", session_name(s)); return -1; } else if (errno != ENXIO) { /* XXX, be more loud? */ debug_error("session_command(), lock's open() failed with errno=%d\n", errno); return 0; } } #ifdef HAVE_FLOCK if (config_session_locks == 1) { if (flock(fd, LOCK_EX|LOCK_NB) == -1) { int flock_errno = errno; close(fd); if (flock_errno == EWOULDBLOCK) { printq("session_locked", session_name(s)); return -1; } else { debug_error("session_command(), lock's flock() failed with errno=%d\n", flock_errno); return 0; } } s->lock_fd = fd; } else #endif close(fd); /* XXX, info about lock */ return 0; } if (match_arg(params[0], 'u', "unlock", 3)) { #ifdef HAVE_FLOCK int fd; #endif const char *path; if (!session) { printq("invalid_session"); return -1; } if (!config_session_locks) { /* printq("var_not_set", name, "session_locks"); */ return 0; } #ifdef HAVE_FLOCK if (config_session_locks == 1 && ((fd = session->lock_fd) != -1)) { flock(fd, LOCK_UN); close(fd); session->lock_fd = -1; } #endif if ((path = prepare_pathf("%s-lock", session_uid_get(session)))) unlink(path); /* XXX, info about unlock */ return 0; } if ((s = session_find(params[0]))) { int status; char *tmp; int i; plugin_t *p = s->plugin; if (params[1] && params[1][0] == '-') { config_changed = 1; command_exec_format(NULL, s, 0, ("%s --set %s %s"), name, params[0], params[1]); return 0; } if(params[1] && params[2]) { command_exec_format(NULL, s, 0, ("%s --set %s %s %s"), name, params[0], params[1], params[2]); config_changed = 1; return 0; } if(params[1]) { if (!xstrcmp(params[1], "password")) command_exec_format(NULL, s, 0, ("%s --set %s %s"), name, params[0], params[1]); else command_exec_format(NULL, s, 0, ("%s --get %s %s"), name, params[0], params[1]); config_changed = 1; return 0; } status = (!s->connected) ? EKG_STATUS_NA : s->status; tmp = format_string(format_find(ekg_status_label(status, s->descr, "user_info_")), (s->alias) ? s->alias : "x", s->descr); if (!s->alias) printq("session_info_header", s->uid, s->uid, tmp); else printq("session_info_header_alias", s->uid, s->alias, tmp); xfree(tmp); if (p) { for (i = 0; p->params[i].key; i++) session_variable_info(s, p->params[i].key, quiet); } else printq("generic_error", "Internal fatal error, plugin somewhere disappear. Report this bug"); printq("session_info_footer", s->uid); return 0; } if (params[0] && params[0][0] != '-' && params[1] && session && session->uid) { command_exec_format(NULL, s, 0, ("%s --set %s %s %s"), name, session_alias_uid(session), params[0], params[1]); return 0; } if (params[0] && params[0][0] != '-' && session && session->uid) { command_exec_format(NULL, s, 0, ("%s --%cet %s %s"), name, !xstrcmp(params[0], "password") ? 's' : 'g', session_alias_uid(session), params[0]); return 0; } if (params[0] && params[0][0] == '-' && session && session->uid) { command_exec_format(NULL, s, 0, ("%s --set %s %s"), name, session_alias_uid(session), params[0]); return 0; } printq("invalid_params", name, params[0]); return -1; } /* sessions_free() * * zwalnia wszystkie dostpne sesje */ void sessions_free() { session_t *s; window_t *wl; list_t l; if (!sessions) return; /* remove _ALL_ session watches */ for (l = watches; l; l = l->next) { watch_t *w = l->data; if (w && w->is_session) watch_free(w); } /* it's sessions, not 'l' because we emit SESSION_REMOVED, which might want to search over sessions list... * This bug was really time-wasting ;( */ /* mg: I modified it so it'll first emit all the events, and then start to free everything * That shouldn't be a problem, should it? */ for (s = sessions; s; s = s->next) { ekg_source_remove_by_data(s, NULL); query_emit(s->plugin, "session-removed", &(s->uid)); /* it notify only that plugin here, to free internal data. * ui-plugin already removed.. other plugins @ quit. * shouldn't be aware about it. too... * XXX, think about it? */ } for (wl = windows; wl; wl = wl->next) wl->session = NULL; sessions_destroy(); session_current = NULL; window_current->session = NULL; } /* * session_help() * * it shows help about variable from ${datadir}/ekg/plugins/{plugin_name}/ * session.txt * * s - session * name - name of the variable */ void session_help(session_t *s, const char *name) { GDataInputStream *f; gchar *type = NULL, *def = NULL, *tmp; const gchar *line, *plugin_name; string_t str; int found = 0; int sessfilnf = 0; if (!s) return; if (!session_is_var(s, name)) { /* XXX, check using session_localvar_find() if this is _local_ variable */ print("session_variable_doesnt_exist", session_name(s), name); return; } plugin_name = ((plugin_t *) s->plugin)->name; do { /* first try to find the variable in plugins' session file */ if (!(f = help_open("session", plugin_name))) { sessfilnf = 1; break; } while ((line = read_line(f))) { if (!xstrcasecmp(line, name)) { found = 1; break; } } } while(0); if (!found) { do { /* then look for them inside global session file */ if (!sessfilnf) g_object_unref(f); if (!(f = help_open("session", NULL))) break; while ((line = read_line(f))) { if (!xstrcasecmp(line, name)) { found = 1; break; } } } while (0); } if (!found) { if (f) g_object_unref(f); if (sessfilnf) print("help_session_file_not_found", plugin_name); else print("help_session_var_not_found", name); return; } line = read_line(f); if ((tmp = xstrstr(line, (": ")))) type = xstrdup(tmp + 2); else type = xstrdup(("?")); line = read_line(f); if ((tmp = xstrstr(line, (": ")))) def = xstrdup(tmp + 2); else def = xstrdup(("?")); print("help_session_header", session_name(s), name, type, def); xfree(type); xfree(def); if (tmp) /* jeli nie jest to ukryta zmienna... */ read_line(f); /* ... pomijamy lini */ str = string_init(NULL); while ((line = read_line(f))) { if (line[0] != '\t') break; if (!xstrncmp(line, ("\t- "), 3) && str->len != 0) { print("help_session_body", str->str); string_clear(str); } if (line[0] == '\t' && line[1] == '\0') { /* if it's \t\0, than add new line */ string_append(str, ("\n\r")); continue; } string_append(str, line + 1); if (line[xstrlen(line) - 1] != ' ') string_append_c(str, ' '); } if (str->len != 0) /* if we have smth in str */ print("help_session_body", str->str); /* display it */ string_free(str, 1); if (xstrcmp(format_find("help_session_footer"), "")) print("help_session_footer", name); g_object_unref(f); } /** * changed_session_locks() is called whenever 'session_locks' variable changes it's value. * * It should cleanup old locks and reinit new, if needed. */ void changed_session_locks(const char *varname) { session_t *s; #ifdef HAVE_FLOCK if (config_session_locks != 1) { /* unlock all files, close fds */ for (s = sessions; s; s = s->next) { if (s->lock_fd != -1) { flock(s->lock_fd, LOCK_UN); close(s->lock_fd); s->lock_fd = -1; } } } #endif if (!config_session_locks) { /* unlink all lockfiles */ for (s = sessions; s; s = s->next) { if (s->connected) { /* don't break locks of other copy of ekg2 */ const char *path = prepare_pathf("%s-lock", session_uid_get(s)); if (path) unlink(path); } } } else { /* lock all connected sessions */ for (s = sessions; s; s = s->next) { if (s->connected #ifdef HAVE_FLOCK && ((config_session_locks != 1) || (s->lock_fd == -1)) #endif ) command_exec(NULL, s, "/session --lock", 1); } } } /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/ekg/sessions.h000066400000000000000000000210001175142753400167330ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2003 Wojtek Kaniewski * 2004 Piotr Kupisiewicz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __EKG_SESSIONS_H #define __EKG_SESSIONS_H #include #include "dynstuff.h" #ifdef __cplusplus extern "C" { #endif /* --NOTE-- * When modifying status_t, remember to update status tables in stuff.c! */ /** * status_t - user's current status, as prioritized enum */ typedef enum { EKG_STATUS_NULL = 0x00, /* special value */ /* These statuses should be considered as no-delivery */ EKG_STATUS_ERROR, /* used in Jabber */ EKG_STATUS_BLOCKED, /* used in GG */ /* These statuses should be considered as 'not sure' */ EKG_STATUS_UNKNOWN, /* used in Jabber */ EKG_STATUS_NA, /* universal */ /* These should be considered as 'probably available' */ EKG_STATUS_INVISIBLE, /* GG; hard to prioritize... */ EKG_STATUS_DND, /* Jabber */ EKG_STATUS_GONE, /* ICQ */ EKG_STATUS_XA, /* Jabber */ EKG_STATUS_AWAY, /* universal */ /* These should be considered as 'sure available' */ EKG_STATUS_AVAIL, /* universal */ EKG_STATUS_FFC, /* Jabber */ EKG_STATUS_LAST, /* XXX: autostatuses are going to be rewritten and then below will be removed */ /* These are special statuses, which are to be used only with dedicated functions */ EKG_STATUS_AUTOAWAY = 0x80, /* putting in auto-away */ EKG_STATUS_AUTOXA, /* putting in auto-xa */ EKG_STATUS_AUTOBACK /* returning to previous status */ } status_t; typedef enum { EKG_CHATSTATE_COMPOSING = 1 << 0, EKG_CHATSTATE_ACTIVE = 1 << 1, EKG_CHATSTATE_GONE = 1 << 2, _EKG_CHATSTATE_NOT = 1 << 10, EKG_CHATSTATE_PAUSED = _EKG_CHATSTATE_NOT | EKG_CHATSTATE_COMPOSING, EKG_CHATSTATE_INACTIVE = _EKG_CHATSTATE_NOT | EKG_CHATSTATE_ACTIVE } chatstates_t; /* Few words about statuses: * * All of the enum statuses are proritity-sorted. I mean, if we want to determine, which of the two given statuses is more * important, we just do standard arithmetic comparation (e.g. (status1 > status2)). The statuses are also divided into few * functional groups. * * EKG_STATUS_NULL is just declared for fun. It can be used locally (e.g. in functions, where status can be set conditionally, * to see if some condition was true), but it can't be passed to core. None of the core functions recognizes it, so it will be * probably treated like unknown status. I even don't think anyone would use that long name, instead of putting 0. * * The next two statuses, blocked and error, represent situations, in which messages sent to user probably won't be delivered. * They both aren't currently treated specially by core, but this may change in future. If You want to check, if given status * belongs to that group, you should use EKG_STATUS_IS_NODELIVERY. * * Then, we've got two kinds of N/A. Both of them mean the user may be unavailable at the moment, but the messages will be * delivered or queued. EKG_STATUS_UNKNOWN would probably be the lowest prioritized of these statuses, so it is used as a mark * for above group, and EKG_STATUS_NA would be the highest one, so it is used as a mark for all N/A statuses. This group * (combined with above) is identified by macro EKG_STATUS_IS_NA. * * Next status, EKG_STATUS_INVISIBLE, is very problematic. It means that user has sent us an N/A status, but some magic says * it is available although. It's hard to say, if it's an N/A status, or more 'deep kind of away' (often invisible is used * when someone goes AFK for a long time). I don't think it should be used as some kind of mark, and also shouldn't be 'less * available' than EKG_STATUS_NA, so it's put after it. But this _can change_. * * Status described above starts the third group of statuses, aways. These are statuses, which say that user is connected with * server, and messages are delivered directly to him/her, but he/she is probably AFK, busy or like that. All those statuses * are grouped by macro EKG_STATUS_IS_AWAY. * * And the last formal group is available-statuses. The first of them, most traditional 'available', is a mark for this * and above group. The macro is EKG_STATUS_IS_AVAIL. * * The real last group is designed for special use only. Currently, there are only statuses for setting and disabling auto-away * mode in EKG2. These three can be passed only to session_status_set(), and aren't recognized by everything else. */ #define EKG_STATUS_IS_NODELIVERY(x) (x < EKG_STATUS_UNKNOWN) #define EKG_STATUS_IS_NA(x) (x <= EKG_STATUS_NA) #define EKG_STATUS_IS_AWAY(x) ((x > EKG_STATUS_NA) && (x < EKG_STATUS_AVAIL)) #define EKG_STATUS_IS_AVAIL(x) (x >= EKG_STATUS_AVAIL) typedef struct session_param { struct session_param *next; char *key; /* nazwa parametru */ char *value; /* warto parametru */ } session_param_t; /** * session_t contains all information about session */ typedef struct ekg_session { struct ekg_session *next; /* public: */ void *plugin; /**< protocol plugin owing session */ char *uid; /**< user ID */ char *alias; /**< short name */ void *priv; /**< protocol plugin's private data */ struct userlist *userlist; /**< session's userlist */ /* private: */ status_t status; /**< session's user status */ char *descr; /**< session's user description */ char *password; /**< session's account password */ unsigned int connected : 1; /**< whether session is connected */ unsigned int connecting : 2; /**< whether session is currently being connected */ unsigned int autoaway : 1; /**< whether we're in autoaway */ time_t activity; /**< timestamp of last activity */ time_t last_conn; /**< timestamp of connecting */ int global_vars_count; char **values; session_param_t *local_vars; /* new auto-away */ status_t last_status; /**< user's status before going into autoaway */ char *last_descr; /**< user's description before going into autoaway */ #ifdef HAVE_FLOCK /* XXX: -D for docs? */ int lock_fd; /**< fd used for session locking */ #endif } session_t; #ifndef EKG2_WIN32_NOFUNCTION extern session_t *sessions; extern session_t *session_current; session_t *session_find(const char *uid); session_t *session_find_ptr(session_t *s); int session_is_var(session_t *s, const char *key); const char *session_uid_get(session_t *s); const char *session_alias_get(session_t *s); int session_alias_set(session_t *s, const char *alias); int session_status_get(session_t *s); #define session_status_get_n(a) session_status_get(session_find(a)) int session_status_set(session_t *s, status_t status); const char *session_descr_get(session_t *s); int session_descr_set(session_t *s, const char *descr); const char *session_password_get(session_t *s); int session_password_set(session_t *s, const char *password); void *session_private_get(session_t *s); int session_private_set(session_t *s, void *priv); int session_connected_get(session_t *s); int session_connected_set(session_t *s, int connected); const char *session_get(session_t *s, const char *key); int session_int_get(session_t *s, const char *key); int session_set(session_t *s, const char *key, const char *value); int session_int_set(session_t *s, const char *key, int value); const char *session_format(session_t *s); #define session_format_n(a) session_format(session_find(a)) /* alias or uid - formatted */ const char *session_name(session_t *s); /* alias or uid - not formatted */ #define session_alias_uid(a) (a->alias) ? a->alias : a->uid #define session_alias_uid_n(a) session_alias_uid(session_find(a)) int session_check(session_t *s, int need_private, const char *protocol); int session_unidle(session_t *s); session_t *session_add(const char *uid); int session_remove(const char *uid); int session_read(const gchar *plugin_name) G_GNUC_INTERNAL; void session_write(); void sessions_free(); void session_help(session_t *s, const char *name); #endif #ifdef __cplusplus } #endif #endif /* __EKG_SESSIONS_H */ /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/ekg/sources.c000066400000000000000000001142241175142753400165560ustar00rootroot00000000000000/* * GSource-related APIs and functions * * (C) Copyright 2011 EKG2 team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include "internal.h" #include /* watch stuff, XXX YYY */ #include #include /* WEXITSTATUS for FreeBSD */ #include /* * Common API */ static GSList *children = NULL; static GSList *timers = NULL; struct ekg_source { guint id; GSource *source; plugin_t *plugin; gchar *name; union { GChildWatchFunc as_child; GSourceFunc as_timer; int (*as_old_timer)(int, void*); gpointer as_void; } handler; gpointer priv_data; GDestroyNotify destr; union { struct { GPid pid; gboolean terminated; } as_child; struct { GTimeVal lasttime; guint64 interval; /* persist arg is deprecated, and mostly unused * however, /at uses it, and so does xmsg plugin * the former needs fixing, the latter will probably be removed */ gboolean persist; } as_timer; } details; }; static ekg_source_t source_new(plugin_t *plugin, const gchar *name_format, gpointer data, GDestroyNotify destr, va_list args) { struct ekg_source *s = g_slice_new(struct ekg_source); s->plugin = plugin; /* XXX: temporary */ s->name = args ? g_strdup_vprintf(name_format, args) : g_strdup(name_format); s->priv_data = data; s->destr = destr; return s; } static void source_set_id(struct ekg_source *s, guint id) { s->id = id; if (!s->name) s->name = g_strdup_printf("_%d", s->id); s->source = g_main_context_find_source_by_id(NULL, s->id); g_assert(s->source); } static void source_free(struct ekg_source *s) { g_free(s->name); g_slice_free(struct ekg_source, s); } /** * ekg_source_remove() * * Remove a particular source (which can be ekg_child_t, ekg_timer_t...). * * @param s - the source identifier. */ void ekg_source_remove(ekg_source_t s) { g_source_remove(s->id); } /** * ekg_source_remove_by_handler() * * Remove source(s) using a particular handler (and optionally matching * the name). * * @param handler - handler function. * @param name - expected source name or NULL if any. * * @return TRUE if any source found, FALSE otherwise. * * @note This function doesn't do any source type checks. We assume * that (due to handler prototype differences) a particular handler is * used only with a single source type. */ struct source_remove_data { gpointer data; const gchar *name; gboolean *ret; }; struct source_find_data { gpointer data; const gchar *name; }; static void source_remove_by_h(gpointer data, gpointer user_data) { struct ekg_source *s = data; struct source_remove_data *args = user_data; if (s->handler.as_void == args->data) { if (!args->name || G_UNLIKELY(!strcasecmp(s->name, args->name))) { ekg_source_remove(s); *args->ret = TRUE; } } } gboolean ekg_source_remove_by_handler(gpointer handler, const gchar *name) { gboolean ret = FALSE; struct source_remove_data args = { handler, name, &ret }; g_slist_foreach(timers, source_remove_by_h, &args); if (G_UNLIKELY(!ret)) g_slist_foreach(children, source_remove_by_h, &args); return ret; } /** * ekg_source_remove_by_data() * * Remove source(s) using a particular private data (and optionally * matching the name). * * @param priv_data - private data pointer. * @param name - expected source name or NULL if any. * * @return TRUE if any source found, FALSE otherwise. * * @note This function doesn't do any source type checks. We assume * that either one doesn't reuse the same private data with different * source types or expects to remove all of them at once. */ static void source_remove_by_d(gpointer data, gpointer user_data) { struct ekg_source *s = data; struct source_remove_data *args = user_data; if (s->priv_data == args->data) { if (!args->name || G_UNLIKELY(!strcasecmp(s->name, args->name))) { ekg_source_remove(s); *args->ret = TRUE; } } } gboolean ekg_source_remove_by_data(gpointer priv_data, const gchar *name) { gboolean ret = FALSE; struct source_remove_data args = { priv_data, name, &ret }; g_slist_foreach(children, source_remove_by_d, &args); g_slist_foreach(timers, source_remove_by_d, &args); return ret; } /** * ekg_source_remove_by_plugin() * * Remove source(s) using a particular plugin (e.g. on plugin unload), * and optionally bearing a name. * * @param plugin - plugin_t pointer. * @param name - expected source name or NULL if any. * * @return TRUE if any source found, FALSE otherwise. */ static void source_remove_by_p(gpointer data, gpointer user_data) { struct ekg_source *s = data; struct source_remove_data *args = user_data; if (s->plugin == args->data) { if (!args->name || G_UNLIKELY(!strcasecmp(s->name, args->name))) { ekg_source_remove(s); *args->ret = TRUE; } } } gboolean ekg_source_remove_by_plugin(plugin_t *plugin, const gchar *name) { gboolean ret = FALSE; struct source_remove_data args = { plugin, name, &ret }; g_slist_foreach(children, source_remove_by_p, &args); g_slist_foreach(timers, source_remove_by_p, &args); return ret; } static void source_remove(gpointer data, gpointer user_data) { struct ekg_source *s = data; ekg_source_remove(s); } void sources_destroy(void) { g_slist_foreach(children, source_remove, NULL); g_slist_foreach(timers, source_remove, NULL); } /* * Child watches */ static void child_destroy_notify(gpointer data) { struct ekg_source *c = data; children = g_slist_remove(children, data); if (!c->details.as_child.terminated) #ifndef NO_POSIX_SYSTEM kill(c->details.as_child.pid, SIGTERM); #else /* TerminateProcess / TerminateThread */; #endif g_spawn_close_pid(c->details.as_child.pid); if (G_UNLIKELY(c->destr)) c->destr(c->priv_data); source_free(c); } static void child_wrapper(GPid pid, gint status, gpointer data) { struct ekg_source *c = data; g_assert(pid == c->details.as_child.pid); g_assert(!c->details.as_child.terminated); /* avoid calling twice */ c->details.as_child.terminated = TRUE; if (G_LIKELY(c->handler.as_child)) c->handler.as_child(pid, WEXITSTATUS(status), c->priv_data); } /** * ekg_child_add() * * Add a watcher for the child process. * * @param plugin - plugin which contains handler funcs or NULL if in core. * @param name_format - format string for watcher name. Can be NULL, or * simple string if the name is guaranteed not to contain '%'. * @param pid - PID of the child process. * @param handler - the handler func called when the process exits. * The handler func will be provided with the child PID, exit status * (filtered through WEXITSTATUS()) and private data. * @param data - the private data passed to the handler. * @param destr - destructor for the private data. It will be called * even if the handler isn't (i.e. when the watch is removed before * process exits). Can be NULL. * @param ... - arguments to name_format format string. * * @return An unique ekg_child_t. */ ekg_child_t ekg_child_add(plugin_t *plugin, const gchar *name_format, GPid pid, GChildWatchFunc handler, gpointer data, GDestroyNotify destr, ...) { va_list args; struct ekg_source *c; va_start(args, destr); c = source_new(plugin, name_format, data, destr, args); va_end(args); c->handler.as_child = handler; c->details.as_child.pid = pid; c->details.as_child.terminated = FALSE; children = g_slist_prepend(children, c); source_set_id(c, g_child_watch_add_full(G_PRIORITY_DEFAULT, pid, child_wrapper, c, child_destroy_notify)); return c; } /* * Timers */ static void timer_wrapper_destroy_notify(gpointer data) { struct ekg_source *t = data; t->handler.as_old_timer(1, t->priv_data); timers = g_slist_remove(timers, data); source_free(t); } static gboolean timer_wrapper_old(gpointer data) { struct ekg_source *t = data; g_source_get_current_time(t->source, &(t->details.as_timer.lasttime)); return !(t->handler.as_old_timer(0, t->priv_data) == -1 || !t->details.as_timer.persist); } ekg_timer_t timer_add_ms(plugin_t *plugin, const gchar *name, guint period, gboolean persist, gint (*function)(gint, gpointer), gpointer data) { struct ekg_source *t = source_new(plugin, name, data, NULL, NULL); t->handler.as_old_timer = function; t->details.as_timer.interval = period; t->details.as_timer.persist = persist; timers = g_slist_prepend(timers, t); source_set_id(t, g_timeout_add_full(G_PRIORITY_DEFAULT, period, timer_wrapper_old, t, timer_wrapper_destroy_notify)); g_source_get_current_time(t->source, &(t->details.as_timer.lasttime)); return t; } /* * timer_add() * * dodaje timera. * * - plugin - plugin obsługuj±cy timer, * - name - nazwa timera w celach identyfikacji. je¶li jest równa NULL, * zostanie przyznany pierwszy numerek z brzegu. * - period - za jaki czas w sekundach ma być uruchomiony, * - persist - czy stały timer, * - function - funkcja do wywołania po upłynięciu czasu, * - data - dane przekazywane do funkcji. * * zwraca zaalokowan± struct timer lub NULL w przypadku błędu. */ ekg_timer_t timer_add(plugin_t *plugin, const gchar *name, guint period, gboolean persist, gint (*function)(gint, gpointer), gpointer data) { return timer_add_ms(plugin, name, period * 1000, persist, function, data); } ekg_timer_t timer_add_session(session_t *session, const gchar *name, guint period, gboolean persist, gint (*function)(gint, session_t *)) { g_assert(session); g_assert(session->plugin); return timer_add(session->plugin, name, period, persist, (void *) function, session); } static void timer_destroy_notify(gpointer data) { struct ekg_source *t = data; timers = g_slist_remove(timers, data); if (G_UNLIKELY(t->destr)) t->destr(t->priv_data); source_free(t); } static gboolean timer_wrapper(gpointer data) { struct ekg_source *t = data; return t->handler.as_timer(t->priv_data); } /** * ekg_timer_add() * * Add a timer. * * @param plugin - plugin which contains handler funcs or NULL if in core. * @param name_format - format string for timer name. Can be NULL, or * simple string if the name is guaranteed not to contain '%'. * @param interval - the interval between successive timer calls, * in milliseconds. If it is a multiple of 1000, the timer will use * glib second timeouts (more efficient); otherwise, the millisecond * timeout will be used. * @param handler - the handler func. It will be passed the private * data, and should either return TRUE or FALSE, depending on whether * the timer should persist or be removed. * @param data - the private data passed to the handler. * @param destr - destructor for the private data. It will be called * even if the handler is not. Can be NULL. * @param ... - arguments to name_format format string. * * @return An unique ekg_timer_t. */ ekg_timer_t ekg_timer_add(plugin_t *plugin, const gchar *name_format, guint64 interval, GSourceFunc handler, gpointer data, GDestroyNotify destr, ...) { va_list args; struct ekg_source *t; guint id; va_start(args, destr); t = source_new(plugin, name_format, data, destr, args); va_end(args); g_assert(handler); t->handler.as_timer = handler; t->details.as_timer.interval = interval; t->details.as_timer.persist = TRUE; timers = g_slist_prepend(timers, t); if (interval % 1000 == 0) id = g_timeout_add_seconds_full(G_PRIORITY_DEFAULT, interval / 1000, timer_wrapper, t, timer_destroy_notify); else id = g_timeout_add_full(G_PRIORITY_DEFAULT, interval, timer_wrapper, t, timer_destroy_notify); source_set_id(t, id); g_source_get_current_time(t->source, &(t->details.as_timer.lasttime)); return t; } /* * timer_remove() * * usuwa timer. * * - plugin - plugin obsługuj±cy timer, * - name - nazwa timera, * * 0/-1 */ gint timer_remove(plugin_t *plugin, const gchar *name) { /* originally, timer_remove() didn't remove all timers with !name */ g_assert(name); return (ekg_source_remove_by_plugin(plugin, name) ? 0 : -1); } gint timer_find_session_cmp(gconstpointer li, gconstpointer ui) { const struct ekg_source *t = li; const struct source_find_data *args = ui; return !(t->priv_data == args->data && !xstrcmp(args->name, t->name)); } ekg_timer_t timer_find_session(session_t *session, const gchar *name) { struct source_find_data args = { session, name }; if (!session) return NULL; return (ekg_timer_t) g_slist_find_custom(timers, &args, timer_find_session_cmp); } static void timer_remove_session_iter(gpointer data, gpointer user_data) { struct ekg_source *t = data; struct source_remove_data *args = user_data; if (t->priv_data == args->data && !xstrcmp(args->name, t->name)) { ekg_source_remove(t); *args->ret = TRUE; } } gint timer_remove_session(session_t *session, const gchar *name) { gboolean removed = FALSE; struct source_remove_data args = { session, name, &removed }; if (!session) return -1; g_assert(session->plugin); g_slist_foreach(timers, timer_remove_session_iter, &args); return ((removed) ? 0 : -1); } /* * timer_remove_user() * * usuwa wszystkie timery użytkownika. * * 0/-1 */ /* XXX: temporary API? */ G_GNUC_INTERNAL gint timer_remove_user(gint (*handler)(gint, gpointer)) { g_assert(handler); return (ekg_source_remove_by_handler(handler, NULL) ? 0 : -1); } static gchar *timer_next_call(struct ekg_source *t) { long usec, sec, minutes = 0, hours = 0, days = 0; GTimeVal tv, ends; ends.tv_sec = t->details.as_timer.lasttime.tv_sec + (t->details.as_timer.interval / 1000); ends.tv_usec = t->details.as_timer.lasttime.tv_usec + ((t->details.as_timer.interval % 1000) * 1000); if (ends.tv_usec > 1000000) { ends.tv_usec -= 1000000; ends.tv_sec++; } g_source_get_current_time(t->source, &tv); if (tv.tv_sec - ends.tv_sec > 2) return g_strdup("?"); if (ends.tv_usec < tv.tv_usec) { sec = ends.tv_sec - tv.tv_sec - 1; usec = (ends.tv_usec - tv.tv_usec + 1000000) / 1000; } else { sec = ends.tv_sec - tv.tv_sec; usec = (ends.tv_usec - tv.tv_usec) / 1000; } if (sec > 86400) { days = sec / 86400; sec -= days * 86400; } if (sec > 3600) { hours = sec / 3600; sec -= hours * 3600; } if (sec > 60) { minutes = sec / 60; sec -= minutes * 60; } if (days) return saprintf("%ldd %ldh %ldm %ld.%.3ld", days, hours, minutes, sec, usec); if (hours) return saprintf("%ldh %ldm %ld.%.3ld", hours, minutes, sec, usec); if (minutes) return saprintf("%ldm %ld.%.3ld", minutes, sec, usec); return saprintf("%ld.%.3ld", sec, usec); } static inline gint timer_match_name(gconstpointer li, gconstpointer ui) { const struct ekg_source *t = li; const gchar *name = ui; return strcasecmp(t->name, name); } /* * Command helpers */ static void child_print(gpointer data, gpointer user_data) { struct ekg_source *c = data; gint quiet = *((gint*) user_data); printq("process", ekg_itoa(c->details.as_child.pid), c->name ? c->name : "?"); } gint ekg_children_print(gint quiet) { g_slist_foreach(children, child_print, &quiet); if (!children) { printq("no_processes"); return -1; } return 0; } static void timer_debug_print(gpointer data, gpointer user_data) { struct ekg_source *t = data; const gchar *plugin; gchar *tmp; gint quiet = *((gint*) user_data); char buf[256]; if (t->plugin) plugin = t->plugin->name; else plugin = "-"; tmp = timer_next_call(t); /* XXX: pointer truncated */ snprintf(buf, sizeof(buf), "%-11s %-20s %-2d %-8" G_GINT64_MODIFIER "u %.8x %-20s", plugin, t->name, t->details.as_timer.persist, t->details.as_timer.interval, GPOINTER_TO_UINT(t->handler.as_old_timer), tmp); printq("generic", buf); g_free(tmp); } COMMAND(cmd_debug_timers) { /* XXX, */ printq("generic_bold", ("plugin name pers peri handler next")); g_slist_foreach(timers, timer_debug_print, &quiet); return 0; } TIMER(timer_handle_at) { if (type) { xfree(data); return 0; } command_exec(NULL, NULL, (char *) data, 0); return 0; } struct timer_print_args { const gchar *name; gint *count; gint quiet; }; static void timer_print(gpointer data, gpointer user_data) { struct ekg_source *t = data; GTimeVal ends, tv; struct tm *at_time; char tmp[100], tmp2[150]; time_t sec, minutes = 0, hours = 0, days = 0; struct timer_print_args *args = user_data; gint quiet = args->quiet; if (t->handler.as_old_timer != timer_handle_at) return; if (args->name && xstrcasecmp(t->name, args->name)) return; (*args->count)++; g_source_get_current_time(t->source, &tv); ends.tv_sec = t->details.as_timer.lasttime.tv_sec + (t->details.as_timer.interval / 1000); ends.tv_usec = t->details.as_timer.lasttime.tv_usec + ((t->details.as_timer.interval % 1000) * 1000); at_time = localtime((time_t *) &ends); if (!strftime(tmp, sizeof(tmp), format_find("at_timestamp"), at_time) && format_exists("at_timestamp")) xstrcpy(tmp, "TOOLONG"); if (t->details.as_timer.persist) { sec = t->details.as_timer.interval / 1000; if (sec > 86400) { days = sec / 86400; sec -= days * 86400; } if (sec > 3600) { hours = sec / 3600; sec -= hours * 3600; } if (sec > 60) { minutes = sec / 60; sec -= minutes * 60; } g_strlcpy(tmp2, "every ", sizeof(tmp2)); if (days) { g_strlcat(tmp2, ekg_itoa(days), sizeof(tmp2)); g_strlcat(tmp2, "d ", sizeof(tmp2)); } if (hours) { g_strlcat(tmp2, ekg_itoa(hours), sizeof(tmp2)); g_strlcat(tmp2, "h ", sizeof(tmp2)); } if (minutes) { g_strlcat(tmp2, ekg_itoa(minutes), sizeof(tmp2)); g_strlcat(tmp2, "m ", sizeof(tmp2)); } if (sec) { g_strlcat(tmp2, ekg_itoa(sec), sizeof(tmp2)); g_strlcat(tmp2, "s", sizeof(tmp2)); } } printq("at_list", t->name, tmp, (char*)(t->priv_data), "", ((t->details.as_timer.persist) ? tmp2 : "")); } COMMAND(cmd_at) { if (match_arg(params[0], 'a', ("add"), 2)) { const char *p, *a_name = NULL; char *a_command; time_t period = 0, freq = 0; struct ekg_source *t; if (!params[1] || !params[2]) { printq("not_enough_params", name); return -1; } if (!strncmp(params[2], "*/", 2) || xisdigit(params[2][0])) { a_name = params[1]; if (!xstrcmp(a_name, "(null)")) { printq("invalid_params", name, a_name); return -1; } if (g_slist_find_custom(timers, a_name, timer_match_name)) { printq("at_exist", a_name); return -1; } p = params[2]; } else p = params[1]; { struct tm *lt; time_t now = time(NULL); char *tmp, *freq_str = NULL, *foo = xstrdup(p); int wrong = 0; lt = localtime(&now); lt->tm_isdst = -1; /* częstotliwo¶ć */ if ((tmp = xstrchr(foo, '/'))) { *tmp = 0; freq_str = ++tmp; } /* wyci±gamy sekundy, je¶li s± i obcinamy */ if ((tmp = xstrchr(foo, '.')) && !(wrong = (xstrlen(tmp) != 3))) { sscanf(tmp + 1, "%2d", <->tm_sec); tmp[0] = 0; } else lt->tm_sec = 0; /* pozb±dĽmy się dwukropka */ if ((tmp = xstrchr(foo, ':')) && !(wrong = (xstrlen(tmp) != 3))) { tmp[0] = tmp[1]; tmp[1] = tmp[2]; tmp[2] = 0; } /* jedziemy ... */ if (!wrong) { switch (xstrlen(foo)) { int ret; case 12: ret = sscanf(foo, "%4d%2d%2d%2d%2d", <->tm_year, <->tm_mon, <->tm_mday, <->tm_hour, <->tm_min); if (ret != 5) wrong = 1; lt->tm_year -= 1900; lt->tm_mon -= 1; break; case 10: ret = sscanf(foo, "%2d%2d%2d%2d%2d", <->tm_year, <->tm_mon, <->tm_mday, <->tm_hour, <->tm_min); if (ret != 5) wrong = 1; lt->tm_year += 100; lt->tm_mon -= 1; break; case 8: ret = sscanf(foo, "%2d%2d%2d%2d", <->tm_mon, <->tm_mday, <->tm_hour, <->tm_min); if (ret != 4) wrong = 1; lt->tm_mon -= 1; break; case 6: ret = sscanf(foo, "%2d%2d%2d", <->tm_mday, <->tm_hour, <->tm_min); if (ret != 3) wrong = 1; break; case 4: ret = sscanf(foo, "%2d%2d", <->tm_hour, <->tm_min); if (ret != 2) wrong = 1; break; default: wrong = 1; } } /* nie ma błędów ? */ if (wrong || lt->tm_hour > 23 || lt->tm_min > 59 || lt->tm_sec > 59 || lt->tm_mday > 31 || !lt->tm_mday || lt->tm_mon > 11) { printq("invalid_params", name, p); /* XXX */ xfree(foo); return -1; } if (freq_str) { for (;;) { time_t _period = 0; if (xisdigit(*freq_str)) _period = atoi(freq_str); else { printq("invalid_params", name, freq_str); xfree(foo); return -1; } freq_str += xstrlen(ekg_itoa(_period)); if (xstrlen(freq_str)) { switch (xtolower(*freq_str++)) { case 'd': _period *= 86400; break; case 'h': _period *= 3600; break; case 'm': _period *= 60; break; case 's': break; default: printq("invalid_params", name, freq_str); xfree(foo); return -1; } } freq += _period; if (!*freq_str) break; } } xfree(foo); /* plany na przeszło¶ć? */ if ((period = mktime(lt) - now) <= 0) { if (freq) { while (period <= 0) period += freq; } else { printq("at_back_to_past"); return -1; } } } if (a_name) a_command = xstrdup(params[3]); else a_command = g_strjoinv(" ", (char **) params + 2); if (!xstrcmp(strip_spaces(a_command), "")) { printq("not_enough_params", name); xfree(a_command); return -1; } if ((t = timer_add(NULL, a_name, period, ((freq) ? 1 : 0), timer_handle_at, xstrdup(a_command)))) { printq("at_added", t->name); if (freq) { guint d = t->details.as_timer.interval; t->details.as_timer.interval = freq * 1000; d -= t->details.as_timer.interval; t->details.as_timer.lasttime.tv_sec += (d / 1000); t->details.as_timer.lasttime.tv_usec += ((d % 1000) * 1000); } if (!in_autoexec) config_changed = 1; } xfree(a_command); return 0; } if (match_arg(params[0], 'd', ("del"), 2)) { int del_all = 0; int ret = 1; if (!params[1]) { printq("not_enough_params", name); return -1; } if (!xstrcmp(params[1], "*")) del_all = 1; ret = !ekg_source_remove_by_handler(timer_handle_at, del_all ? NULL : params[1]); if (!ret) { if (del_all) printq("at_deleted_all"); else printq("at_deleted", params[1]); config_changed = 1; } else { if (del_all) printq("at_empty"); else { printq("at_noexist", params[1]); return -1; } } return 0; } if (!params[0] || match_arg(params[0], 'l', ("list"), 2) || params[0][0] != '-') { const char *a_name = NULL; int count = 0; if (params[0] && match_arg(params[0], 'l', ("list"), 2)) a_name = params[1]; else if (params[0]) a_name = params[0]; { struct timer_print_args args = { a_name, &count, quiet }; g_slist_foreach(timers, timer_print, &args); } if (!count) { if (a_name) { printq("at_noexist", a_name); return -1; } else printq("at_empty"); } return 0; } printq("invalid_params", name, params[0]); return -1; } TIMER(timer_handle_command) { if (type) { xfree(data); return 0; } command_exec(NULL, NULL, (char *) data, 0); return 0; } static void timer_print_list(gpointer data, gpointer user_data) { struct ekg_source *t = data; char *tmp; struct timer_print_args *args = user_data; gint quiet = args->quiet; if (t->handler.as_old_timer != timer_handle_command) return; if (args->name && xstrcasecmp(t->name, args->name)) return; (*args->count)++; tmp = timer_next_call(t); printq("timer_list", t->name, tmp, (char*)(t->priv_data), "", (t->details.as_timer.persist) ? "*" : ""); g_free(tmp); } COMMAND(cmd_timer) { if (match_arg(params[0], 'a', ("add"), 2)) { const char *t_name = NULL, *p; char *t_command; time_t period = 0; struct ekg_source *t; int persistent = 0; if (!params[1] || !params[2]) { printq("not_enough_params", name); return -1; } if (xisdigit(params[2][0]) || !strncmp(params[2], "*/", 2)) { t_name = params[1]; if (!xstrcmp(t_name, "(null)")) { printq("invalid_params", name, t_name); return -1; } if (g_slist_find_custom(timers, t_name, timer_match_name)) { printq("timer_exist", t_name); return -1; } p = params[2]; t_command = xstrdup(params[3]); } else { p = params[1]; t_command = g_strjoinv(" ", (char **) params + 2); } if ((persistent = !strncmp(p, "*/", 2))) p += 2; for (;;) { time_t _period = 0; if (xisdigit(*p)) _period = atoi(p); else { printq("invalid_params", name, p); xfree(t_command); return -1; } p += xstrlen(ekg_itoa(_period)); if (xstrlen(p)) { switch (xtolower(*p++)) { case 'd': _period *= 86400; break; case 'h': _period *= 3600; break; case 'm': _period *= 60; break; case 's': break; default: printq("invalid_params", name, p); /* XXX */ xfree(t_command); return -1; } } period += _period; if (!*p) break; } if (!xstrcmp(strip_spaces(t_command), "")) { printq("not_enough_params", name); xfree(t_command); return -1; } if ((t = timer_add(NULL, t_name, period, persistent, timer_handle_command, xstrdup(t_command)))) { printq("timer_added", t->name); if (!in_autoexec) config_changed = 1; } xfree(t_command); return 0; } if (match_arg(params[0], 'd', ("del"), 2)) { int del_all = 0, ret; if (!params[1]) { printq("not_enough_params", name); return -1; } if (!xstrcmp(params[1], "*")) del_all = 1; ret = !ekg_source_remove_by_handler(timer_handle_command, del_all ? NULL : params[1]); if (!ret) { if (del_all) printq("timer_deleted_all"); else printq("timer_deleted", params[1]); config_changed = 1; } else { if (del_all) printq("timer_empty"); else { printq("timer_noexist", params[1]); return -1; } } return 0; } if (!params[0] || match_arg(params[0], 'l', ("list"), 2) || params[0][0] != '-') { const char *t_name = NULL; int count = 0; if (params[0] && match_arg(params[0], 'l', ("list"), 2)) t_name = params[1]; else if (params[0]) t_name = params[0]; { struct timer_print_args args = { t_name, &count, quiet }; g_slist_foreach(timers, timer_print_list, &args); } if (!count) { if (t_name) { printq("timer_noexist", t_name); return -1; } else printq("timer_empty"); } return 0; } printq("invalid_params", name, params[0]); return -1; } void timers_write(GOutputStream *f) { void timer_write(gpointer data, gpointer user_data) { struct ekg_source *t = data; GOutputStream *f = user_data; const char *name = NULL; if (!t->details.as_timer.persist) /* XXX && t->ends.tv_sec - time(NULL) < 5) */ return; if (t->name && t->name[0] != '_') name = t->name; else name = "(null)"; if (t->handler.as_old_timer == timer_handle_at) { char buf[100]; time_t foo = (time_t) t->details.as_timer.lasttime.tv_sec + (t->details.as_timer.interval / 1000); struct tm *tt = localtime(&foo); strftime(buf, sizeof(buf), "%G%m%d%H%M.%S", tt); if (t->details.as_timer.persist) ekg_fprintf(f, "at %s %s/%s %s\n", name, buf, ekg_itoa(t->details.as_timer.interval / 1000), (char*)(t->priv_data)); else ekg_fprintf(f, "at %s %s %s\n", name, buf, (char*)(t->priv_data)); } else if (t->handler.as_old_timer == timer_handle_command) { char *foo; if (t->details.as_timer.persist) foo = saprintf("*/%s", ekg_itoa(t->details.as_timer.interval / 1000)); else foo = saprintf("%s", ekg_itoa(t->details.as_timer.lasttime.tv_sec + (t->details.as_timer.interval / 1000))); ekg_fprintf(f, "timer %s %s %s\n", name, foo, (char*)(t->priv_data)); xfree(foo); } } g_slist_foreach(timers, timer_write, f); } /* * Watches */ /* * watch_find() * * zwraca obiekt watch_t o podanych parametrach. */ watch_t *watch_find(plugin_t *plugin, int fd, watch_type_t type) { list_t l; for (l = watches; l; l = l->next) { watch_t *w = l->data; /* XXX: added simple plugin ignoring, make something nicer? */ if (w && ((plugin == (void*) -1) || w->plugin == plugin) && w->fd == fd && (w->type & type)) return w; } return NULL; } static LIST_FREE_ITEM(watch_free_data, watch_t *) { if (data->buf) { int (*handler)(int, int, const char *, void *) = data->handler; string_free(data->buf, 1); /* DO WE WANT TO SEND ALL IN BUFOR TO FD ? IF IT'S WATCH_WRITE_LINE? or parse all data if it's WATCH_READ_LINE? mmh. XXX */ if (handler) handler(1, data->fd, NULL, data->data); } else { int (*handler)(int, int, int, void *) = data->handler; if (handler) handler(1, data->fd, data->type, data->data); } g_io_channel_unref(data->f); } /* * watch_handle_line() * * obsługa deskryptorów przegl±danych WATCH_READ_LINE. */ static int watch_handle_line(watch_t *w) { char buf[1024], *tmp; int ret, res = 0; int (*handler)(int, int, const char *, void *) = w->handler; g_assert(w); #ifndef NO_POSIX_SYSTEM ret = read(w->fd, buf, sizeof(buf) - 1); #else ret = recv(w->fd, buf, sizeof(buf) - 1, 0); if (ret == -1 && WSAGetLastError() == WSAENOTSOCK) { printf("recv() failed Error: %d, using ReadFile()", WSAGetLastError()); res = ReadFile(w->fd, &buf, sizeof(buf)-1, &ret, NULL); printf(" res=%d ret=%d\n", res, ret); } res = 0; #endif if (ret > 0) { buf[ret] = 0; string_append(w->buf, buf); #ifdef NO_POSIX_SYSTEM printf("RECV: %s\n", buf); #endif } if (ret == 0 || (ret == -1 && errno != EAGAIN)) string_append_c(w->buf, '\n'); while ((tmp = xstrchr(w->buf->str, '\n'))) { size_t strlen = tmp - w->buf->str; /* get len of str from begining to \n char */ char *line = xstrndup(w->buf->str, strlen); /* strndup() str with len == strlen */ /* we strndup() str with len == strlen, so we don't need to call xstrlen() */ if (strlen > 1 && line[strlen - 1] == '\r') line[strlen - 1] = 0; if ((res = handler(0, w->fd, line, w->data)) == -1) { xfree(line); break; } string_remove(w->buf, strlen + 1); xfree(line); } /* je¶li koniec strumienia, lub nie jest to ci±głe przegl±danie, * zwolnij pamięć i usuń z listy */ if (res == -1 || ret == 0 || (ret == -1 && errno != EAGAIN)) return -1; /* XXX: close(fd) was here, seemed unsafe */ return res; } static gboolean watch_old_wrapper(GIOChannel *f, GIOCondition cond, gpointer data); /* ripped from irc plugin */ static int watch_handle_write(watch_t *w) { int (*handler)(int, int, const char *, void *) = w->handler; int res = -1; int len = (w && w->buf) ? w->buf->len : 0; g_assert(w); #ifdef FIXME_WATCHES_TRANSFER_LIMITS if (w->transfer_limit == -1) return 0; /* transfer limit turned on, don't send anythink... XXX */ #endif g_assert(len); debug_io("[watch_handle_write] fd: %d in queue: %d bytes.... ", w->fd, len); if (handler) { res = handler(0, w->fd, w->buf->str, w->data); } else { #ifdef NO_POSIX_SYSTEM res = send(w->fd, w->buf->str, len, 0 /* MSG_NOSIGNAL */); #else res = write(w->fd, w->buf->str, len); #endif } debug_io(" ... wrote:%d bytes (handler: 0x%x) ", res, handler); if (res == -1 && #ifdef NO_POSIX_SYSTEM (WSAGetLastError() != 666) #else 1 #endif ) { #ifdef NO_POSIX_SYSTEM debug("WSAError: %d\n", WSAGetLastError()); #else debug("Error: %s %d\n", strerror(errno), errno); #endif return -1; } if (res > len) { /* use debug_fatal() */ /* debug_fatal() should do: * - print this info to all open windows with RED color * - change some variable 'ekg2_need_restart' to 1. * - @ ncurses if we have ekg2_need_restart set, and if colors turned on, change from blue to red.. * - and do other happy stuff. * * XXX, implement and use it. It should be used as ASSERT() */ debug_error("watch_write(): handler returned bad value, 0x%x vs 0x%x\n", res, len); res = len; } else if (res < 0) { debug_error("watch_write(): handler returned negative value other than -1.. XXX\n"); res = 0; } string_remove(w->buf, res); debug_io("left: %d bytes\n", w->buf->len); if (!w->buf->len) { /* all written, remove the watch */ g_source_remove(w->id); w->id = -1; } return res; } int watch_write_data(watch_t *w, const char *buf, int len) { /* XXX, refactory: watch_write() */ int was_empty; if (!w || !buf || len <= 0) return -1; was_empty = !w->buf->len; string_append_raw(w->buf, buf, len); /* if it was empty, we need to readd the watch */ if (was_empty) { /* but maybe we could write it all right now? */ watch_handle_write(w); /* ...or maybe not */ if (w->buf->len) w->id = g_io_add_watch_full(w->f, G_PRIORITY_DEFAULT, G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL, watch_old_wrapper, w, NULL); /* XXX: we can't clearly destroy it ;f */ } return 0; } int watch_write(watch_t *w, const char *format, ...) { /* XXX, refactory: watch_writef() */ char *text; int textlen; va_list ap; int res; if (!w || !format) return -1; va_start(ap, format); text = vsaprintf(format, ap); va_end(ap); textlen = xstrlen(text); debug_io("[watch]_send: %s\n", text ? textlen ? text: "[0LENGTH]":"[FAILED]"); if (!text) return -1; res = watch_write_data(w, text, textlen); xfree(text); return res; } /** * watch_handle() * * Handler for watches with type: WATCH_READ or WATCH_WRITE
* Mark watch with w->removed = -1, to indicate that watch is in use. And it shouldn't be * executed again. [If watch can or even must be executed twice from ekg_loop() than you must * change w->removed by yourself.]
* * If handler of watch return -1 or watch was removed inside function [by watch_remove() or watch_free()]. Than it'll be removed.
* ELSE Update w->started field to current time. * * @param w - watch_t to handler * * @todo We only check for w->removed == -1, maybe instead change it to: w->removed != 0 */ static int watch_handle(watch_t *w) { int (*handler)(int, int, int, void *); int res; g_assert(w); handler = w->handler; res = handler(0, w->fd, w->type, w->data); w->started = time(NULL); return res; } static gboolean watch_old_wrapper(GIOChannel *f, GIOCondition cond, gpointer data) { watch_t *w = data; if (w->type != WATCH_NONE && (cond & (G_IO_IN | G_IO_OUT))) { int ret; g_assert(cond & (w->type == WATCH_WRITE ? G_IO_OUT : G_IO_IN)); if (!w->buf) ret = watch_handle(w); else if (w->type == WATCH_READ) ret = watch_handle_line(w); else if (w->type == WATCH_WRITE) ret = watch_handle_write(w); if (ret == -1) return FALSE; } if (cond & (G_IO_ERR | G_IO_NVAL | G_IO_HUP)) { debug("watch_old_wrapper(): fd no longer valid, fd=%d, type=%d, plugin=%s\n", w->fd, w->type, (w->plugin) ? w->plugin->name : ("none")); return FALSE; } return TRUE; } static void watch_old_destroy_notify(gpointer data) { watch_t *w = data; #ifdef FIXME_WATCHES if (w->type == WATCH_WRITE && w->buf && !w->handler && w->plugin) { /* XXX */ debug_error("[INTERNAL_DEBUG] WATCH_LINE_WRITE must be removed by plugin, manually (settype to WATCH_NONE and than call watch_free()\n"); return; } #endif watch_free_data(w); list_remove_safe(&watches, w, 1); debug("watch_old_destroy_notify() REMOVED WATCH, oldwatch: 0x%x\n", w); } /* * watch_free() * * zwalnia pamięć po obiekcie watch_t. * zwraca wskaĽnik do następnego obiektu do iterowania * albo NULL, jak nie można skasować. */ void watch_free(watch_t *w) { if (!w) return; if (w->id != -1) g_source_remove(w->id); /* stupid line watchers with their stupid manual removal */ else if (w->type == WATCH_NONE) watch_old_destroy_notify(w); else debug_function("watch_free() with no action, id=-1, fd=%d, type=%d\n", w->fd, w->type); } /** * watch_add() * * Create new watch_t and add it on the beginning of watches list. * * @param plugin - plugin * @param fd - fd to watch data for. * @param type - type of watch. * @param handler - handler of watch. * @param data - data which be passed to handler. * * @return Created watch_t. if @a type is either WATCH_READ_LINE or WATCH_WRITE_LINE than also allocate memory for buffer */ watch_t *watch_add(plugin_t *plugin, int fd, watch_type_t type, watcher_handler_func_t *handler, void *data) { GError *err = NULL; watch_t *w = xmalloc(sizeof(watch_t)); w->plugin = plugin; w->fd = fd; w->type = type; if (w->type == WATCH_READ_LINE) { w->type = WATCH_READ; w->buf = string_init(NULL); } else if (w->type == WATCH_WRITE_LINE) { w->type = WATCH_WRITE; w->buf = string_init(NULL); } w->started = time(NULL); w->handler = handler; w->data = data; w->f = g_io_channel_unix_new(fd); /* we need to disable recoding & buffering, as we use fd directly */ g_assert(g_io_channel_set_encoding(w->f, NULL, &err) == G_IO_STATUS_NORMAL); g_io_channel_set_buffered(w->f, FALSE); if (!w->buf || w->type == WATCH_READ) w->id = g_io_add_watch_full(w->f, G_PRIORITY_DEFAULT, (w->type == WATCH_WRITE ? G_IO_OUT : G_IO_IN) | G_IO_ERR | G_IO_HUP | G_IO_NVAL, watch_old_wrapper, w, watch_old_destroy_notify); else w->id = -1; /* backwards compat magic ;f */ list_add_beginning(&watches, w); return w; } /** * watch_add_session() * * Create new session watch_t and add it on the beginning of watches list. * * @param session - session * @param fd - fd to watch data for * @param type - type of watch. * @param handler - handler of watch. * * @return If @a session is NULL, or @a session->plugin is NULL, it return NULL.
* else created watch_t */ watch_t *watch_add_session(session_t *session, int fd, watch_type_t type, watcher_session_handler_func_t *handler) { watch_t *w; if (!session || !session->plugin) { debug_error("watch_add_session() s: 0x%x s->plugin: 0x%x\n", session, session ? session->plugin : NULL); return NULL; } w = watch_add(session->plugin, fd, type, (watcher_handler_func_t *) handler, session); w->is_session = 1; return w; } int watch_remove(plugin_t *plugin, int fd, watch_type_t type) { int res = -1; watch_t *w; #ifdef FIXME_WATCHES /* XXX, here can be deadlock feel warned. */ /* DEADLOCK ACHIEVED! */ while ((w = watch_find(plugin, fd, type))) { watch_free(w); res = 0; } #endif return res; } ekg2-0.4~pre+20120506.1/ekg/sources.h000066400000000000000000000121121175142753400165540ustar00rootroot00000000000000/* * GSource-related APIs and functions * * (C) Copyright 2011 EKG2 team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __EKG_SOURCES_H #define __EKG_SOURCES_H #include /* Common API */ typedef struct ekg_source *ekg_source_t; void ekg_source_remove(ekg_source_t s); gboolean ekg_source_remove_by_handler(gpointer handler, const gchar *name); gboolean ekg_source_remove_by_data(gpointer priv_data, const gchar *name); gboolean ekg_source_remove_by_plugin(plugin_t *plugin, const gchar *name); /* Child watches */ typedef ekg_source_t ekg_child_t; ekg_child_t ekg_child_add(plugin_t *plugin, const gchar *name_format, GPid pid, GChildWatchFunc handler, gpointer data, GDestroyNotify destr, ...) G_GNUC_PRINTF(2, 7) G_GNUC_MALLOC; /* Timers */ typedef ekg_source_t ekg_timer_t; ekg_timer_t ekg_timer_add(plugin_t *plugin, const gchar *name_format, guint64 interval, GSourceFunc handler, gpointer data, GDestroyNotify destr, ...) G_GNUC_PRINTF(2, 7) G_GNUC_MALLOC; /* Macro-handlers are deprecated * please use explicit prototypes with new timer API */ #define TIMER(x) gint x(gint type, gpointer data) #define TIMER_SESSION(x) gint x(gint type, session_t *s) /* old timer API */ ekg_timer_t timer_add(plugin_t *plugin, const gchar *name, guint period, gboolean persist, gint (*function)(gint, gpointer), gpointer data); ekg_timer_t timer_add_ms(plugin_t *plugin, const gchar *name, guint period, gboolean persist, gint (*function)(gint, gpointer), gpointer data); ekg_timer_t timer_add_session(session_t *session, const gchar *name, guint period, gboolean persist, gint (*function)(gint, session_t *)); ekg_timer_t timer_find_session(session_t *session, const gchar *name); gint timer_remove(plugin_t *plugin, const gchar *name); gint timer_remove_session(session_t *session, const gchar *name); /* Watches */ extern list_t watches; typedef enum { WATCH_NONE = 0, WATCH_WRITE = 1, WATCH_READ = 2, WATCH_READ_LINE = 4, WATCH_WRITE_LINE = 8, } watch_type_t; #define WATCHER(x) int x(int type, int fd, watch_type_t watch, void *data) #define WATCHER_LINE(x) int x(int type, int fd, const char *watch, void *data) #define WATCHER_SESSION(x) int x(int type, int fd, watch_type_t watch, session_t *s) #define WATCHER_SESSION_LINE(x) int x(int type, int fd, const char *watch, session_t *s) typedef WATCHER(watcher_handler_func_t); /* typedef WATCHER_LINE(watcher_handler_line_func_t); */ typedef WATCHER_SESSION(watcher_session_handler_func_t); typedef struct watch { int fd; /* obserwowany deskryptor */ watch_type_t type; /* co sprawdzamy */ plugin_t *plugin; /* wtyczka obsługuj±ca deskryptor */ void *handler; /* funkcja wywoływana je¶li s± dane itp. */ void *data; /* dane przekazywane powyższym funkcjom. */ string_t buf; /* bufor na linię */ time_t timeout; /* timeout */ time_t started; /* kiedy zaczęto obserwować */ int transfer_limit; /* XXX, requested by GiM to limit data transmitted to ircd server... currently only to send all data done by serveral calls of watch_write() in one packet... by setting it to -1 and than changing it back to 0 if we really want to send packet in that function we ought to do by calling watch_handle_write() [PLEASE NOTE, THAT YOU CANNOT DO watch_write().. cause it will check if there is somethink in write buffor... and if it is, it won't call watch_handle_write()] or it will be executed in next ekg_loop() loop. */ int is_session; /* if set, this watch belongs to session specified in data */ guint id; GIOChannel *f; } watch_t; #ifndef EKG2_WIN32_NOFUNCTION #ifdef __GNU__ int watch_write(watch_t *w, const char *format, ...) __attribute__ ((format (printf, 2, 3))); #else int watch_write(watch_t *w, const char *format, ...); #endif int watch_write_data(watch_t *w, const char *buf, int len); watch_t *watch_find(plugin_t *plugin, int fd, watch_type_t type); void watch_free(watch_t *w); typedef void *watch_handler_func_t; int watch_timeout_set(watch_t *w, time_t timeout); watch_t *watch_add(plugin_t *plugin, int fd, watch_type_t type, watcher_handler_func_t *handler, void *data); #define watch_add_line(p, fd, type, handler, data) watch_add(p, fd, type, (watcher_handler_func_t *) (handler), data) watch_t *watch_add_session(session_t *session, int fd, watch_type_t type, watcher_session_handler_func_t *handler); #define watch_add_session_line(s, fd, type, handler) watch_add_session(s, fd, type, (watcher_session_handler_func_t *) (handler)) int watch_remove(plugin_t *plugin, int fd, watch_type_t type); #endif #endif ekg2-0.4~pre+20120506.1/ekg/srv.c000066400000000000000000000347121175142753400157100ustar00rootroot00000000000000/* * (C) Copyright 2004-2005 Michal 'GiM' Spadlinski * parts of this code are losely based on cifs/smb dns utility stuff * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* --NOTE-- * * All this code is called by child process, so debug*() won't work. * Don't waste time trying it. */ /* this is srv resolver as used by ekg2 */ #include "ekg2.h" #include #include #include #include /* getprotobynumber, getservbyport */ #include #include #include #ifdef __APPLE__ #include #endif #include #ifdef HAVE_RESOLV_H #include /* res_init, res_query */ #endif #include "srv.h" #ifndef T_SRV #define T_SRV 33 /* rfc 2782 */ #endif /* Stolen from ClamAV */ /* XXX: use glib byte order macros */ #if WORDS_BIGENDIAN == 0 union unaligned_32 { guint32 una_u32; gint32 una_s32; } __attribute__((packed)); union unaligned_16 { guint16 una_u16; gint16 una_s16; } __attribute__((packed)); #define cli_readint16(buff) (((const union unaligned_16 *)(buff))->una_u16) #define cli_readint32(buff) (((const union unaligned_32 *)(buff))->una_u32) #else static inline guint16 cli_readint16(const unsigned char *buff) { guint16 ret; ret = buff[0] & 0xff; ret |= (buff[1] & 0xff) << 8; return ret; } static inline guint32 cli_readint32(const unsigned char *buff) { guint32 ret; ret = buff[0] & 0xff; ret |= (buff[1] & 0xff) << 8; ret |= (buff[2] & 0xff) << 16; ret |= (buff[3] & 0xff) << 24; return ret; } #endif #ifndef MAXDNAME #define MAXDNAME NS_MAXDNAME #endif /* idea: * get srv (+ A's and AAAA's) * resolve missing */ /* cannot place this struct in .h * since it requires resolv.h * and this conlficts with compilation of other files */ struct _gim_host { struct _gim_host *next; unsigned char name[MAXDNAME]; guint16 prio; guint16 weight; guint16 port; int *ai_family; char **ip; }; const int DNS_NS_MAXDNAME = MAXDNAME; LIST_ADD_COMPARE(gim_host_cmp, gim_host* ) { return data1->prio - data2->prio; } /** * ekg_inet_ntostr - convert sockaddr_in * to string representing ip address * * since srv_resolver uses this function and we can't * place srv_resolver in net.c (due to conflict in * resolv.h) for a while this function must sit here. * */ char *ekg_inet_ntostr(int family, void *buf) { #ifdef HAVE_INET_NTOP # define RESOLVER_MAXLEN INET6_ADDRSTRLEN char tmp[RESOLVER_MAXLEN]; inet_ntop(family, buf, tmp, RESOLVER_MAXLEN); return xstrdup(tmp); #else if (family == AF_INET6) { return xstrdup("::"); } else /* try to dup the string as fast as possible, * and hope for the best */ return xstrdup(inet_ntoa(*(struct in_addr *)buf)); #endif } #ifdef HAVE_RESOLV_H /** * extract_rr() * * parses RR header from dns reply. RR format according to rfc 1035. * * @param start - beginning of a buffer with dns response * @param end - end of buffer * @ptr - beginning of RR, ptr will be adjusted approprietly * @rr - structure where result will be placed * * @returns 0 - on success, non zero otherwise * * IF RETURNED STATUS IS NON-ZERO, content of 'rr' struct is undefined */ int extract_rr(unsigned char *start, unsigned char *end, unsigned char **ptr, ns_rr *rr) { unsigned char *rrs; char exp_dn[2048]; int exp_len; if (!start || !end || !ptr || !*ptr || !rr) return -1; rrs = *ptr; if ((exp_len = dn_expand(start, end, rrs, exp_dn , sizeof(exp_dn))) == -1) return 1; /* no checking here, since if there wouldn't be at least exp_len * bytes, dn_expand would return -1 */ rrs += exp_len; strncpy(rr->name, exp_dn, DNS_NS_MAXDNAME); if (rrs + 10 > end) return 3; /* this works both on sparc and intel, so don't mess with it */ rr->type = g_ntohs(cli_readint16(rrs)); rr->rr_class = g_ntohs(cli_readint16(rrs+2)); rr->ttl = g_ntohs(cli_readint32(rrs+4)); rr->rdlength = g_ntohs(cli_readint32(rrs+8)); rrs += 10; if (rrs + rr->rdlength > end) return 4; rr->rdata = rrs; rrs += rr->rdlength; *ptr = rrs; return 0; } int extract_rr_srv(unsigned char *start, unsigned char *end, unsigned char **ptr, gim_host *srv) { char exp_dn[2048]; int exp_len; /* arpa/nameser.h */ ns_rr rr; if ((extract_rr(start, end, ptr, &rr)) != 0) { /* fprintf (stderr, "EPIC FAIL srv\n"); */ return 1; } if (ns_rr_type(rr) != ns_t_srv) { /* fprintf (stderr, "Unexpected record of type(%d) found instead of srv(%d)\n", ns_rr_type(rr), ns_t_srv); */ return 1; } if (rr.rdlength < 6) return 1; srv->prio = g_ntohs(cli_readint16(rr.rdata)); srv->weight = g_ntohs(cli_readint16(rr.rdata+2)); srv->port = g_ntohs(cli_readint16(rr.rdata+4)); if ((exp_len = dn_expand(start, end, rr.rdata+6, exp_dn , sizeof(exp_dn))) == -1) return 1; strncpy((char*)srv->name, (char*)exp_dn, DNS_NS_MAXDNAME); return 0; } int skip_rr_ns(unsigned char *start, unsigned char *end, unsigned char **ptr) { /* arpa/nameser.h */ ns_rr rr; if ((extract_rr(start, end, ptr, &rr)) != 0) { /* fprintf (stderr, "EPIC FAIL ns\n"); */ return 1; } if (ns_rr_type(rr) != ns_t_ns) { /* fprintf (stderr, "Unexpected record of type(%d) found instead of ns(%d)\n", ns_rr_type(rr), ns_t_ns); */ return 1; } return 0; } typedef enum _gim_sects { SQUERY=ns_s_qd, SANSWER=ns_s_an, SAUTH=ns_s_ns, SEXTRA=ns_s_ar, SMAX=ns_s_max } gim_sects; #endif int srv_resolver(gim_host **hostlist, const char *hostname, const int proto_port, const int port, const int proto) { #ifdef HAVE_RESOLV_H struct protoent *pro; struct servent *srv; char expanded_dn[2048], *srvhost; unsigned char res_answer[2048], *rrs; int res_query_len, expanded_len, cnt[SMAX]; /* this is query header, on my distro it lies under arpa/nameser_compat.h */ HEADER *query_resp; gim_host *iter; if (!(pro = getprotobynumber(proto ? proto : IPPROTO_TCP))) return 1; if (!(srv = getservbyport(g_htons(proto_port), pro->p_name))) return 2; if (res_init() == -1) { /* fprintf (stderr, "resolver initialization FAILED\n"); */ return 3; } srvhost = saprintf("_%s._%s.%s", srv->s_name, pro->p_name, hostname); #define RET(x) do { xfree(srvhost); return x; } while (0) /*fprintf (stderr, "trying: %s\n", srvhost);*/ if ((res_query_len = res_query(srvhost, C_IN, T_SRV, res_answer, sizeof(res_answer))) == -1) RET(4); if (res_query_len < sizeof(HEADER)) { /* fprintf(stderr, "underflow resolver reply, someone's truing to hack you up?"); */ RET(5); } /* if (res_query_len > 512) fprintf (stderr, "long resolver reply, pleas report to developers\n"); */ query_resp = (HEADER*)res_answer; /* fprintf (stderr, "> %d %d\n", res_query_len, sizeof(HEADER)); fprintf (stderr, "> %s\n", srvhost); */ /* check if there was no error, and if there is answer section available */ if ( (g_ntohs(query_resp->rcode) == NOERROR) && (g_ntohs(query_resp->ancount) > 0) ) { int i; cnt[SQUERY] = g_ntohs(query_resp->qdcount); cnt[SANSWER] = g_ntohs(query_resp->ancount); cnt[SAUTH] = g_ntohs(query_resp->nscount); cnt[SEXTRA] = g_ntohs(query_resp->arcount); if (cnt[SQUERY] != 1) { /* fprintf (stderr, "wth, not our query?"); */ RET(6); } /* fprintf (stderr, "> question_cnt: %d\n", cnt[0]); fprintf (stderr, "> answer_cnt: %d\n", cnt[1]); fprintf (stderr, "> ns_cnt: %d\n", cnt[2]); fprintf (stderr, "> ar_cnt: %d\n", cnt[3]); */ /* PARSE QUERY */ if ((expanded_len = dn_expand(res_answer, res_answer + res_query_len, res_answer + sizeof(HEADER), expanded_dn, sizeof(expanded_dn))) == -1) RET(6); /* fprintf (stderr, "> %s %d\n", expanded_dn, expanded_len); */ /* rfc 1035 $ 4.1.2 - question section format * qname, qtype - 16b, qclass - 16b */ rrs = res_answer + sizeof(HEADER) + expanded_len + 2 + 2; /* PARSE ANSWER SECTION */ for (i = 0; i < cnt[SANSWER]; i++) { gim_host *srv = xmalloc(sizeof(gim_host)); if (extract_rr_srv (res_answer, res_answer + res_query_len, &rrs, srv) != 0) RET(7); /* alter port to user specified port * this is temporary hack and final solution * will probably be different */ if (srv->port == proto_port) srv->port = port; LIST_ADD_SORTED2(hostlist, srv, gim_host_cmp); } /* PARSE (skip) NS SECTION */ for (i = 0; i < cnt[SAUTH]; i++) if (skip_rr_ns (res_answer, res_answer + res_query_len, &rrs) != 0) RET(8); /* PARSE ADDITIONAL SECTION */ for (i=0; inext) if (!xstrcmp((char *)iter->name, (char*)rr.name)) break; /* probably A/AAAA for ns, skip to next */ if (!iter) continue; if ((ns_rr_type(rr) != ns_t_a || rr.rdlength != 4) && (ns_rr_type(rr) != ns_t_aaaa || rr.rdlength != 16)) { /* fprintf (stderr, " unhandled type in additional section\n"); */ continue; } family = (rr.rdlength == 4) ? AF_INET : AF_INET6; ip_cnt = array_add_check (&(iter->ip), ekg_inet_ntostr(family, (void *)rr.rdata), 0); if (ip_cnt) { iter->ai_family = xrealloc (iter->ai_family, ip_cnt*sizeof(iter->ai_family)); iter->ai_family[ip_cnt-1] = family; } } } #endif return 0; } /** * this is mostly copy of basic_resolver below * it's for internal use only, for resolving * missing items on the list */ static int basic_resolver_item (gim_host *srv) { #ifdef HAVE_GETADDRINFO struct addrinfo *ai, *aitmp, hint; #else # warning "resolver: You don't have getaddrinfo(), resolver may not work! (ipv6 for sure)" struct hostent *he4; #endif /* if it's on the 'missing list', it's the result of * srv query, so on */ #ifdef HAVE_GETADDRINFO memset(&hint, 0, sizeof(struct addrinfo)); hint.ai_socktype = SOCK_STREAM; if (!getaddrinfo((char *)srv->name, NULL, &hint, &ai)) { for (aitmp = ai; aitmp; aitmp = aitmp->ai_next) { int ip_cnt; void *tm; if (aitmp->ai_family == AF_INET6) tm = &(((struct sockaddr_in6 *) aitmp->ai_addr)->sin6_addr); else if (aitmp->ai_family == AF_INET) tm = &(((struct sockaddr_in *) aitmp->ai_addr)->sin_addr); else continue; ip_cnt = array_add_check(&(srv->ip), ekg_inet_ntostr(aitmp->ai_family, tm), 0); if (ip_cnt) { srv->ai_family = xrealloc(srv->ai_family, ip_cnt*sizeof(srv->ai_family)); srv->ai_family[ip_cnt-1] = aitmp->ai_family; } } freeaddrinfo(ai); } #else if ((he4 = gethostbyname(srv->name))) { int ip_cnt = array_add (&(srv->ip), xstrdup(inet_ntoa(*(struct in_addr *) he4->h_addr))); srv->ai_family = xrealloc (srv->ai_family, ip_cnt*sizeof(srv->ai_family)); srv->ai_family[ip_cnt-1] = AF_INET; } #endif return 0; } int resolve_missing_entries(gim_host **hostlist) { gim_host *iter; for (iter = *hostlist; iter; iter = iter->next) { if (iter->ip) { /*debug (" >%s already done\n", iter->name);*/ continue; } /*debug (" >%s resolving\n", iter->name);*/ basic_resolver_item (iter); } return 0; } /** * this is exactly irc_resolver2, but instead * of array it appends entries to hostlist */ int basic_resolver(gim_host **hostlist, const char *hostname, int port) { #ifdef HAVE_GETADDRINFO struct addrinfo *ai, *aitmp, hint; #else # warning "resolver: You don't have getaddrinfo(), resolver may not work! (ipv6 for sure)" struct hostent *he4; #endif gim_host *srv; #ifdef HAVE_GETADDRINFO memset(&hint, 0, sizeof(struct addrinfo)); hint.ai_socktype = SOCK_STREAM; srv = xmalloc(sizeof(gim_host)); if (!getaddrinfo(hostname, NULL, &hint, &ai)) { #if 0 int do_loop = (AF_INET | AF_INET6); gim_host *iter; #endif srv->prio = DNS_SRV_MAX_PRIO; srv->port = port; strncpy((char *)srv->name, hostname, DNS_NS_MAXDNAME); for (aitmp = ai; aitmp; aitmp = aitmp->ai_next) { int ip_cnt; void *tm; if (aitmp->ai_family == AF_INET6) tm = &(((struct sockaddr_in6 *) aitmp->ai_addr)->sin6_addr); else if (aitmp->ai_family == AF_INET) tm = &(((struct sockaddr_in *) aitmp->ai_addr)->sin_addr); else continue; ip_cnt = array_add_check (&(srv->ip), ekg_inet_ntostr(aitmp->ai_family, tm), 0); if (ip_cnt) { srv->ai_family = xrealloc(srv->ai_family, ip_cnt*sizeof(srv->ai_family)); srv->ai_family[ip_cnt-1] = aitmp->ai_family; } #if 0 for (iter = *hostlist; iter && (do_loop & srv->ai_family); iter = iter->next) { /* I know, comparing name returned from srv, quite sux * but I don't have better idea right now */ if (!xstrcmp(iter->name, hostname) && iter->ai_family == srv->ai_family) { /* ports are different, so check if iter * has filled ip field, if not, copy it */ if (!iter->ip) iter->ip = xstrdup(srv->ip); } } /* do not waste time ;) */ /* do not make above loop in next iteration, * since hostname hasn't changed, so if any * matched it is already corrected */ do_loop &= ~(aitmp->ai_family); #endif } LIST_ADD_SORTED2(hostlist, srv, gim_host_cmp); freeaddrinfo(ai); } #else if ((he4 = gethostbyname(hostname))) { gim_host *srv = xmalloc(sizeof(gim_host)); int ip_cnt = array_add (&(srv->ip), xstrdup(inet_ntoa(*(struct in_addr *) he4->h_addr))); srv->ai_family = xrealloc (srv->ai_family, ip_cnt*sizeof(srv->ai_family)); srv->ai_family[ip_cnt-1] = AF_INET; srv->prio = DNS_SRV_MAX_PRIO; srv->port = port; strncpy(srv->name, hostname, DNS_NS_MAXDNAME); LIST_ADD_SORTED2(hostlist, srv, gim_host_cmp); } #endif return 0; } void write_out_and_destroy_list(int fd, gim_host *hostlist) { gim_host *iter; char *str; int i; for (iter = hostlist; iter; iter = iter->next) { for (i = 0; i < g_strv_length(iter->ip); i++) { str = saprintf ("%s %s %d %d\n", iter->name, iter->ip[i], iter->ai_family[i], iter->port); write (fd, str, xstrlen(str)); xfree (str); } g_strfreev(iter->ip); xfree (iter->ai_family); } LIST_DESTROY2 (hostlist, NULL); } ekg2-0.4~pre+20120506.1/ekg/srv.h000066400000000000000000000027051175142753400157120ustar00rootroot00000000000000/* * (C) Copyright 2004-2005 Michal 'GiM' Spadlinski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __EKG_SRV_H #define __EKG_SRV_H #ifdef __cplusplus extern "C" { #endif typedef struct _gim_host gim_host; char *ekg_inet_ntostr(int family, void *buf); int srv_resolver(gim_host **hostlist, const char *hostname, const int proto_port, const int port, const int proto); LIST_ADD_COMPARE(gim_host_cmp, gim_host* ); int resolve_missing_entries(gim_host **hostlist); int basic_resolver(gim_host **hostlist, const char *hostname, int port); void write_out_and_destroy_list(int fd, gim_host *hostlist); #define DNS_SRV_MAX_PRIO 0xffff extern const int DNS_NS_MAXDNAME; #ifdef __cplusplus } #endif #endif /* __EKG_SRV_H */ /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/ekg/stuff.c000066400000000000000000001665721175142753400162370ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2001-2003 Wojtek Kaniewski * Robert J. Wony * Pawe Maziarz * Dawid Jarosz * Piotr Domagalski * Adam Mikuta * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #include #ifndef NO_POSIX_SYSTEM #include #endif #include #ifndef NO_POSIX_SYSTEM #include #include #endif #include #include #include #include #ifndef NO_POSIX_SYSTEM #include #include #endif #include #include #include #include #include #include #include alias_t *aliases = NULL; list_t autofinds = NULL; /*************** * conferences ***************/ struct conference *conferences = NULL; newconference_t *newconferences = NULL; struct buffer_info buffer_debug = { NULL, 0, DEBUG_MAX_LINES }; /**< debug buffer */ struct buffer_info buffer_speech = { NULL, 0, 50 }; /**< speech buffer */ int old_stderr; char *config_subject_prefix; char *config_subject_reply_prefix; int in_autoexec = 0; int config_auto_save = 0; int config_auto_user_add = 0; int config_display_color = 1; int config_beep = 1; int config_beep_msg = 1; int config_beep_chat = 1; int config_beep_notify = 1; char *config_dcc_dir; int config_display_blinking = 1; int config_events_delay = 3; int config_expert_mode = 0; int config_history_savedups = 1; char *config_sound_msg_file = NULL; char *config_sound_chat_file = NULL; char *config_sound_notify_file = NULL; char *config_sound_sysmsg_file = NULL; char *config_sound_mail_file = NULL; char *config_sound_app = NULL; int config_changed = 0; int config_display_ack = 12; int config_completion_notify = 1; char *config_completion_char = NULL; time_t ekg_started = 0; int config_display_notify = 1; char *config_theme = NULL; int config_default_status_window = 0; char *home_dir = NULL; char *config_quit_reason = NULL; char *config_away_reason = NULL; char *config_back_reason = NULL; int config_query_commands = 0; int config_slash_messages = 0; int quit_message_send = 0; int batch_mode = 0; char *batch_line = NULL; int config_make_window = 6; char *config_tab_command = NULL; int config_save_password = 1; int config_save_quit = 1; char *config_timestamp = NULL; int config_timestamp_show = 1; int config_display_sent = 1; int config_send_white_lines = 0; int config_sort_windows = 1; int config_keep_reason = 1; char *config_speech_app = NULL; int config_time_deviation = 300; int config_mesg = MESG_DEFAULT; int config_display_welcome = 1; char *config_display_color_map = NULL; char *config_session_default = NULL; int config_sessions_save = 0; int config_window_session_allow = 0; int config_windows_save = 0; char *config_windows_layout = NULL; char *config_profile = NULL; int config_debug = 1; int config_version = 0; char *config_exit_exec = NULL; int config_session_locks = 0; char *config_nickname = NULL; char *last_search_first_name = NULL; char *last_search_last_name = NULL; char *last_search_nickname = NULL; char *last_search_uid = 0; char *formated_config_timestamp = NULL; int ekg2_reason_changed = 0; /* * windows_save() * * saves current open windows to the variable @a config_windows_layout if @a config_windows_save is on * @sa config_windows_layout * @sa config_windows_save */ void windows_save() { window_t *w; if (config_windows_save) { string_t s = string_init(NULL); int maxid = 0, i; for (w = windows; w; w = w->next) { if (!w->floating && w->id > maxid) maxid = w->id; } for (i = 1; i <= maxid; i++) { const char *target = "-"; const char *session_name = NULL; for (w = windows; w; w = w->next) { if (w->id == i) { target = w->target; if (w->session) session_name = w->session->uid; break; } } if (session_name && target) { string_append(s, session_name); string_append_c(s, '/'); } if (target) { string_append_c(s, '\"'); string_append(s, target); string_append_c(s, '\"'); } if (i < maxid) string_append_c(s, '|'); } for (w = windows; w; w = w->next) { if (w->floating && (!w->target || xstrncmp(w->target, "__", 2))) { char *tmp = saprintf("|*%d,%d,%d,%d,%d,%s", w->left, w->top, w->width, w->height, w->frames, w->target); string_append(s, tmp); xfree(tmp); } } xfree(config_windows_layout); config_windows_layout = string_free(s, 0); } } static LIST_FREE_ITEM(list_alias_free, alias_t *) { xfree(data->name); list_destroy(data->commands, 1); } DYNSTUFF_LIST_DECLARE(aliases, alias_t, list_alias_free, static __DYNSTUFF_LIST_ADD, /* aliases_add() */ static __DYNSTUFF_LIST_REMOVE_ITER, /* aliases_removei() */ __DYNSTUFF_LIST_DESTROY) /* aliases_destroy() */ /* * alias_add() * * dopisuje alias do listy aliasw. * * - string - linia w formacie 'alias cmd', * - quiet - czy wypluwa mesgi na stdout, * - append - czy dodajemy kolejn komend? * * 0/-1 */ int alias_add(const char *string, int quiet, int append) { char *cmd, *aname, *tmp; GSList *cl; alias_t *a; char **params = NULL; char *array; int i; if (!string || !(cmd = xstrchr(string, ' '))) return -1; *cmd++ = 0; for (a = aliases; a; a = a->next) { if (!xstrcasecmp(string, a->name)) { if (!append) { printq("aliases_exist", string); return -1; } else { list_add(&a->commands, xstrdup(cmd)); /* przy wielu komendach trudno dopenia, bo wg. ktrej? */ for (cl = commands; cl; cl = cl->next) { command_t *c = cl->data; if (!xstrcasecmp(c->name, a->name)) { xfree(c->params); c->params = array_make(("?"), (" "), 0, 1, 1); break; } } printq("aliases_append", string); return 0; } } } aname = xstrdup((*cmd == '/') ? cmd + 1 : cmd); if ((tmp = xstrchr(aname, ' '))) *tmp = 0; for (i=0; i<2; i++) { for (cl = commands; cl && !params; cl = cl->next) { command_t *c = cl->data; const char *cname = c->name; if (i) { if ((tmp = xstrchr(cname, ':'))) cname = tmp+1; else continue; } if (!xstrcasecmp(string, cname) && !(c->flags & COMMAND_ISALIAS)) { printq("aliases_command", string); xfree(aname); return -1; } if (!xstrcasecmp(aname, cname)) { params = c->params; break; } } } xfree(aname); a = xmalloc(sizeof(struct alias)); a->name = xstrdup(string); a->commands = NULL; list_add(&(a->commands), xstrdup(cmd)); aliases_add(a); array = (params) ? g_strjoinv(" ", params) : xstrdup(("?")); command_add(NULL, a->name, array, cmd_alias_exec, COMMAND_ISALIAS, NULL); xfree(array); printq("aliases_add", a->name, ("")); return 0; } /* * alias_remove() * * usuwa alias z listy aliasw. * * - name - alias lub NULL, * - quiet. * * 0/-1 */ int alias_remove(const char *name, int quiet) { alias_t *a; int removed = 0; for (a = aliases; a; a = a->next) { if (!name || !xstrcasecmp(a->name, name)) { if (name) printq("aliases_del", name); command_remove(NULL, a->name); a = aliases_removei(a); removed = 1; } } if (!removed) { if (name) printq("aliases_noexist", name); else printq("aliases_list_empty"); return -1; } if (removed && !name) printq("aliases_del_all"); return 0; } static LIST_FREE_ITEM(list_buffer_free, struct buffer *) { xfree(data->line); xfree(data->target); } static __DYNSTUFF_ADD(buffers, struct buffer, NULL) /* buffers_add() */ static __DYNSTUFF_REMOVE_ITER(buffers, struct buffer, list_buffer_free) /* buffers_removei() */ static __DYNSTUFF_DESTROY(buffers, struct buffer, list_buffer_free) /* buffers_destroy() */ static __DYNSTUFF_COUNT(buffers, struct buffer) /* buffers_count() */ static __DYNSTUFF_GET_NTH(buffers, struct buffer) /* buffers_get_nth() */ static void buffer_add_common(struct buffer_info *type, const char *target, const char *line, time_t ts) { struct buffer *b; struct buffer **addpoint = (type->last ? &(type->last) : &(type->data)); /* What the heck with addpoint thing? * - if type->last ain't NULL, it points to last element of the list; * we can pass it directly to LIST_ADD2() to avoid iterating through all items, * it just sets its' 'next' field and everything is fine, * - but if it's NULL, then data is NULL too. That means LIST_ADD2() would need * to modify the list pointer, so we need to pass it &(type->data) instead. * Else type->last would point to the list, but type->data would be still NULL, * - if last is NULL, but data ain't, that means something broke. But that's * no problem, as we're still passing &(type->data), so adding works fine * and then type->last is fixed. */ if (type->max_lines) { /* XXX: move to idles? */ int n; bac_countupd: n = type->count - type->max_lines + 1; if (n > 0) { /* list slice removal */ b = buffers_get_nth(type->data, n); /* last element to remove */ if (!b) { /* count has been broken */ type->count = buffers_count(type->data); goto bac_countupd; } type->data = b->next; b->next = NULL; /* unlink elements to be removed */ type->count -= n; /* XXX, * b->next == NULL * so buffers_destroy(&b) will free only b, * shouldn't be saved type->data value? */ buffers_destroy(&b); /* and remove them */ } } b = xmalloc(sizeof(struct buffer)); b->ts = ts; b->target = xstrdup(target); b->line = xstrdup(line); buffers_add(addpoint, b); type->last = b; type->count++; } /** * buffer_add() * * Add new line to given buffer_t, if max_lines > 0 than it maintain list that we can have max: @a max_lines items on it. * * @param type - pointer to buffer beginning ptr * @param target - name of target.. or just name of smth we want to keep in b->target * @param line - line which we want to save. * * @return 0 - when line was successfully added to buffer, else -1 (when @a type was NULL) */ int buffer_add(struct buffer_info *type, const char *target, const char *line) { if (!type) return -1; buffer_add_common(type, target, line, time(NULL)); return 0; /* so always return success here */ } /** * buffer_add_str() * * Add new line to given buffer_t, if max_lines > 0 than it maintain list that we can have max: @a max_lines items on it. * * @param type - pointer to buffer beginning ptr * @param target - name of target, or just name of smth we want to keep in b->target * @param str - string in format: [time_when_it_happen proper line... blah, blah] time_when_it_happen should be in digits. * * @return 0 - when line was successfully added to buffer, else -1 (when @a type was NULL, or @a line was in wrong format) */ int buffer_add_str(struct buffer_info *type, const char *target, const char *str) { char *sep; time_t ts = 0; if (!type || !str) return -1; for (sep = (char *) str; xisdigit(*sep); sep++) { /* XXX check if there's no time_t overflow? */ ts *= 10; ts += (*sep - '0'); } if (sep == str || *sep != ' ') { debug_error("buffer_add_str() parsing str: %s failed\n", str); return -1; } buffer_add_common(type, target, sep+1, ts); return 0; /* so always return success here */ } /** * buffer_tail() * * Return oldest b->line, free b->target and remove whole buffer_t from list * * @param type - pointer to buffer beginning ptr * * @return First b->line on the list, or NULL, if no items on list. */ char *buffer_tail(struct buffer_info *type) { struct buffer *b; char *str; if (!type || !type->data) return NULL; b = type->data; str = b->line; /* save b->line */ b->line = NULL; (void) buffers_removei(&(type->data), b); if (type->last == b) type->last = NULL; type->count--; return str; /* return saved b->line */ } /** * buffer_free() * * Free memory after given buffer.
* After it set *type to NULL * * @param type - pointer to buffer beginning ptr * */ void buffer_free(struct buffer_info *type) { if (!type || !type->data) return; buffers_destroy(&(type->data)); type->last = NULL; type->count = 0; } void changed_make_window(const char *var) { static int old_value = 6; if (config_make_window == 4) { config_make_window = old_value; print("variable_invalid", var); } old_value = config_make_window; } /* * changed_mesg() * * funkcja wywoywana przy zmianie wartoci zmiennej ,,mesg''. */ void changed_mesg(const char *var) { if (config_mesg == MESG_DEFAULT) mesg_set(mesg_startup); else mesg_set(config_mesg); } static TIMER(auto_save_timer) { if (type) return 0; if (!config_changed) return 0; debug("autosaving userlist and config.\n"); config_write(); session_write(); if (config_commit()) { config_changed = 0; ekg2_reason_changed = 0; print("autosaved"); } else print("error_saving"); return 0; } /* * changed_auto_save() * * wywoywane po zmianie wartoci zmiennej ,,auto_save''. */ void changed_auto_save(const char *var) { timer_remove(NULL, "auto_save"); if (config_auto_save > 0) timer_add(NULL, "auto_save", config_auto_save, 1, auto_save_timer, NULL); } /* * changed_display_blinking() * * wywoywane po zmianie wartoci zmiennej ,,display_blinking''. */ void changed_display_blinking(const char *var) { session_t *s; /* wyczamy wszystkie blinkajce uid'y */ for (s = sessions; s; s = s->next) { userlist_t *ul; for (ul = s->userlist; ul; ul = ul->next) { userlist_t *u = ul; u->blink = 0; } } } /* * changed_theme() * * funkcja wywoywana przy zmianie wartoci zmiennej ,,theme''. */ void changed_theme(const char *var) { if (in_autoexec) return; if (!config_theme) { theme_free(); theme_init(); } else { if (!theme_read(config_theme, 1)) { print("theme_loaded", config_theme); } else { print("error_loading_theme", strerror(errno)); variable_set(("theme"), NULL); } } } /* * changed_theme() * * funkcja wywoywana przy zmianie wartoci zmiennej ,,config_timestamp''. */ void changed_config_timestamp(const char *var) { xfree(formated_config_timestamp); formated_config_timestamp = (config_timestamp && *config_timestamp) ? format_string(config_timestamp) : NULL; } /** * compile_time() * * Return compilation date, and time..
* Used by /version command and ekg2 --version * * @return __DATE__" "__TIME__
* For example: "Jun 21 1987" " " "22:06:47" */ const char *compile_time() { return __DATE__ " " __TIME__; } /* NEW CONFERENCE API HERE, WHEN OLD CONFERENCE API BECOME OBSOLETE CHANGE FUNCTION NAME, ETC.... */ static LIST_FREE_ITEM(newconference_free_item, newconference_t *) { xfree(data->name); xfree(data->session); userlists_destroy(&(data->participants)); } DYNSTUFF_LIST_DECLARE(newconferences, newconference_t, newconference_free_item, static __DYNSTUFF_LIST_ADD, /* newconferences_add() */ static __DYNSTUFF_LIST_REMOVE_SAFE, /* newconferences_remove() */ __DYNSTUFF_LIST_DESTROY) /* newconferences_destroy() */ userlist_t *newconference_member_find(newconference_t *conf, const char *uid) { userlist_t *ul; if (!conf || !uid) return NULL; for (ul = conf->participants; ul; ul = ul->next) { userlist_t *u = ul; if (!xstrcasecmp(u->uid, uid)) return u; } return NULL; } userlist_t *newconference_member_add(newconference_t *conf, const char *uid, const char *nick) { userlist_t *u; if (!conf || !uid) return NULL; if (!(u = newconference_member_find(conf, uid))) u = userlist_add_u(&(conf->participants), uid, nick); return u; } /* remove userlist_t from conference. wrapper. */ int newconference_member_remove(newconference_t *conf, userlist_t *u) { if (!conf || !u) return -1; return userlist_remove_u(&(conf->participants), u); } newconference_t *newconference_find(session_t *s, const char *name) { newconference_t *c; for (c = newconferences; c; c = c->next) { if ((!s || !xstrcmp(s->uid, c->session)) && !xstrcmp(name, c->name)) return c; } return NULL; } newconference_t *newconference_create(session_t *s, const char *name, int create_wnd) { newconference_t *c; window_t *w; if (!s || !name) return NULL; if ((c = newconference_find(s, name))) return c; if (!(w = window_find_s(s, name)) && create_wnd) { w = window_new(name, s, 0); } c = xmalloc(sizeof(newconference_t)); c->session = xstrdup(s->uid); c->name = xstrdup(name); newconferences_add(c); return c; } void newconference_destroy(newconference_t *conf, int kill_wnd) { window_t *w = NULL; if (!conf) return; if (kill_wnd) w = window_find_s(session_find(conf->session), conf->name); newconferences_remove(conf); window_kill(w); } /* OLD CONFERENCE API HERE, REQUEST REWRITING/USING NEW-ONE */ static LIST_FREE_ITEM(conference_free_item, struct conference *) { xfree(data->name); list_destroy(data->recipients, 1); } DYNSTUFF_LIST_DECLARE(conferences, struct conference, conference_free_item, static __DYNSTUFF_LIST_ADD, /* conferences_add() */ static __DYNSTUFF_LIST_REMOVE_ITER, /* conferences_removei() */ __DYNSTUFF_LIST_DESTROY) /* conferences_destroy() */ /* * conference_add() * * dopisuje konferencje do listy konferencji. * * - name - nazwa konferencji, * - nicklist - lista nickw, grup, czegokolwiek, * - quiet - czy wypluwa mesgi na stdout. * * zaalokowan struct conference lub NULL w przypadku bdu. */ struct conference *conference_add(session_t *session, const char *name, const char *nicklist, int quiet) { struct conference c, *cf; char **nicks; int i, count; char **p; if (!name || !nicklist) return NULL; if (nicklist[0] == ',' || nicklist[xstrlen(nicklist) - 1] == ',') { printq("invalid_params", ("chat"), nicklist); return NULL; } nicks = array_make(nicklist, " ,", 0, 1, 0); /* grupy zamieniamy na niki */ for (i = 0; nicks[i]; i++) { if (nicks[i][0] == '@') { session_t *s; char *gname = xstrdup(nicks[i] + 1); int first = 0; int nig = 0; /* nicks in group */ for (s = sessions; s; s = s->next) { userlist_t *ul; for (ul = s->userlist; ul; ul = ul->next) { userlist_t *u = ul; struct ekg_group *gl; if (!u->nickname) continue; for (gl = u->groups; gl; gl = gl->next) { struct ekg_group *g = gl; if (!xstrcasecmp(gname, g->name)) { if (first++) array_add(&nicks, xstrdup(u->nickname)); else { xfree(nicks[i]); nicks[i] = xstrdup(u->nickname); } nig++; break; } } } } xfree(gname); if (!nig) { printq("group_empty", gname); printq("conferences_not_added", name); g_strfreev(nicks); return NULL; } } } count = g_strv_length(nicks); for (cf = conferences; cf; cf = cf->next) { if (!xstrcasecmp(name, cf->name)) { printq("conferences_exist", name); g_strfreev(nicks); return NULL; } } memset(&c, 0, sizeof(c)); for (p = nicks, i = 0; *p; p++) { const char *uid; if (!xstrcmp(*p, "")) continue; /* XXX, check if bad uid */ uid = get_uid(session, *p); if (uid) list_add(&(c.recipients), xstrdup(uid)); i++; } g_strfreev(nicks); if (i != count) { printq("conferences_not_added", name); list_destroy(c.recipients, 1); return NULL; } printq("conferences_add", name); c.name = xstrdup(name); tabnick_add(name); cf = g_memdup(&c, sizeof(c)); conferences_add(cf); return cf; } /* * conference_remove() * * usuwa konferencj z listy konferencji. * * - name - konferencja lub NULL dla wszystkich, * - quiet. * * 0/-1 */ int conference_remove(const char *name, int quiet) { struct conference *c; int removed = 0; for (c = conferences; c; c = c->next) { if (!name || !xstrcasecmp(c->name, name)) { if (name) printq("conferences_del", name); tabnick_remove(c->name); c = conferences_removei(c); removed = 1; } } if (!removed) { if (name) printq("conferences_noexist", name); else printq("conferences_list_empty"); return -1; } if (removed && !name) printq("conferences_del_all"); return 0; } /* * conference_create() * * tworzy now konferencj z wygenerowan nazw. * * - nicks - lista nikw tak, jak dla polecenia conference. */ struct conference *conference_create(session_t *session, const char *nicks) { struct conference *c; static int count = 1; char *name = saprintf("#conf%d", count); if ((c = conference_add(session, name, nicks, 0))) count++; xfree(name); return c; } /* * conference_find() * * znajduje i zwraca wskanik do konferencji lub NULL. * * - name - nazwa konferencji. */ struct conference *conference_find(const char *name) { struct conference *c; for (c = conferences; c; c = c->next) { if (!xstrcmp(c->name, name)) return c; } return NULL; } /* * conference_participant() * * sprawdza, czy dany numer jest uczestnikiem konferencji. * * - c - konferencja, * - uin - numer. * * 1 jeli jest, 0 jeli nie. */ int conference_participant(struct conference *c, const char *uid) { list_t l; for (l = c->recipients; l; l = l->next) { char *u = l->data; if (!xstrcasecmp(u, uid)) return 1; } return 0; } /* * conference_find_by_uids() * * znajduje konferencj, do ktrej nale podane uiny. jeeli nie znaleziono, * zwracany jest NULL. jeli numerw jest wicej, zostan dodane do * konferencji, bo najwyraniej kto do niej doczy. * * - from - kto jest nadawc wiadomoci, * - recipients - tablica numerw nalecych do konferencji, * - count - ilo numerw, * - quiet. */ struct conference *conference_find_by_uids(session_t *s, const char *from, const char **recipients, int count, int quiet) { int i; struct conference *c; for (c = conferences; c; c = c->next) { int matched = 0; for (i = 0; i < count; i++) if (conference_participant(c, recipients[i])) matched++; if (conference_participant(c, from)) matched++; debug_function("// conference_find_by_uids(): from=%s, rcpt count=%d, matched=%d, list_count(c->recipients)=%d\n", from, count, matched, LIST_COUNT2(c->recipients)); if (matched == LIST_COUNT2(c->recipients) && matched <= (!xstrcasecmp(from, s->uid) ? count : count + 1)) { string_t new = string_init(NULL); int comma = 0; if (xstrcasecmp(from, s->uid) && !conference_participant(c, from)) { list_add(&c->recipients, g_memdup(&from, sizeof(from))); comma++; string_append(new, format_user(s, from)); } for (i = 0; i < count; i++) { if (xstrcasecmp(recipients[i], s->uid) && !conference_participant(c, recipients[i])) { list_add(&c->recipients, g_memdup(&recipients[i], sizeof(recipients[0]))); if (comma++) string_append(new, ", "); string_append(new, format_user(s, recipients[i])); } } if (xstrcmp(new->str, "") && !c->ignore) printq("conferences_joined", new->str, c->name); string_free(new, 1); debug("// conference_find_by_uins(): matching %s\n", c->name); return c; } } return NULL; } /* * conference_set_ignore() * * ustawia stan konferencji na ignorowany lub nie. * * - name - nazwa konferencji, * - flag - 1 ignorowa, 0 nie ignorowa, * - quiet. * * 0/-1 */ int conference_set_ignore(const char *name, int flag, int quiet) { struct conference *c = conference_find(name); if (!c) { printq("conferences_noexist", name); return -1; } c->ignore = flag; printq((flag ? "conferences_ignore" : "conferences_unignore"), name); return 0; } /* * conference_rename() * * zmienia nazw instniejcej konferencji. * * - oldname - stara nazwa, * - newname - nowa nazwa, * - quiet. * * 0/-1 */ int conference_rename(const char *oldname, const char *newname, int quiet) { struct conference *c; if (conference_find(newname)) { printq("conferences_exist", newname); return -1; } if (!(c = conference_find(oldname))) { printq("conference_noexist", oldname); return -1; } xfree(c->name); c->name = xstrdup(newname); tabnick_remove(oldname); tabnick_add(newname); printq("conferences_rename", oldname, newname); query_emit(NULL, "conference-renamed", &oldname, &newname); /* XXX READ-ONLY QUERY */ return 0; } /** * help_open() * * Open the help file in best language available. * * @param name - help file basename. * @param plugin - plugin name or NULL if core help is requested. * * @return Open GDataInputStream with utf8 encoding or NULL if no file was * found. It should be unreferenced with g_object_unref(). */ GDataInputStream *help_open(const gchar *name, const gchar *plugin) { const gchar* const *p; gchar *base = plugin ? g_build_filename(DATADIR, "plugins", plugin, name, NULL) : g_build_filename(DATADIR, name, NULL); GString *fnbuf = g_string_new(base); const gsize baselen = fnbuf->len; g_free(base); for (p = g_get_language_names(); *p; p++) { GError *err = NULL; GFile *f; GFileInputStream *ret; if (G_UNLIKELY(!strcmp(*p, "C"))) g_string_append(fnbuf, "-en.txt"); else g_string_append_printf(fnbuf, "-%s.txt", *p); f = g_file_new_for_path(fnbuf->str); ret = g_file_read(f, NULL, &err); if (ret) { g_string_free(fnbuf, TRUE); return g_data_input_stream_new(G_INPUT_STREAM(ret)); } else if (err->code != G_FILE_ERROR_NOENT) debug_error("help_path() failed to open %s with error: %s\n", fnbuf->str, err->message); g_error_free(err); g_string_truncate(fnbuf, baselen); } g_string_free(fnbuf, TRUE); return NULL; } /* * ekg_hash() * * liczy prosty hash z nazwy, wykorzystywany przy przeszukiwaniu list * zmiennych, formatw itp. * * - name - nazwa. */ /* int ekg_hash(const char *name) { int hash = 0; for (; *name; name++) { hash ^= *name; hash <<= 1; } return hash; }*/ /* * new hash, made with queries in mind * but should also nicely behave for formats */ int ekg_hash(const char *name) { unsigned long long st = 0x4d6947; for (; *name; name++) { st = st * 2147483069 + 2147482417; st ^= (*name); } return (int)st; } /* * mesg_set() * * wcza/wycza/sprawdza moliwo pisania do naszego terminala. * * - what - MESG_ON, MESG_OFF lub MESG_CHECK * * -1 jeli bad, lub aktualny stan: MESG_ON/MESG_OFF */ int mesg_set(int what) { #ifndef NO_POSIX_SYSTEM const char *tty; struct stat s; if (!(tty = ttyname(old_stderr)) || stat(tty, &s)) { debug_error("mesg_set() error: %s\n", strerror(errno)); return -1; } switch (what) { case MESG_OFF: chmod(tty, s.st_mode & ~S_IWGRP); break; case MESG_ON: chmod(tty, s.st_mode | S_IWGRP); break; case MESG_CHECK: return ((s.st_mode & S_IWGRP) ? MESG_ON : MESG_OFF); } return 0; #else return -1; #endif } /** * strip_spaces() * * strips spaces from the begining and the end of string @a line * * @param line - given string * * @note If you pass here smth which was strdup'ed() malloc'ed() or smth which was allocated.
* You must xfree() string passed, not result of this function. * * @return buffer without spaces. */ char *strip_spaces(char *line) { size_t linelen; char *buf; if (!(linelen = xstrlen(line))) return line; for (buf = line; xisspace(*buf); buf++); while (linelen > 0 && xisspace(line[linelen - 1])) { line[linelen - 1] = 0; linelen--; } return buf; } /* * play_sound() * * odtwarza dzwik o podanej nazwie. * * 0/-1 */ int play_sound(const char *sound_path) { char *params[2]; int res; if (!config_sound_app || !sound_path) { errno = EINVAL; return -1; } params[0] = saprintf(("^%s %s"), config_sound_app, sound_path); params[1] = NULL; res = cmd_exec(("exec"), (const char **) params, NULL, NULL, 1); xfree(params[0]); return res; } /** * mkdir_recursive() * * Create directory @a pathname and all needed parent directories.
* * @todo Maybe at begining of function let's check with stat() if that dir/file already exists? * * @param pathname - path to directory or file (see @a isdir comment) * @param isdir - if @a isdir is set, than we should also create dir specified by full @a pathname path, * else we shouldn't do it, because it's filename and we want to create directory only to last '/' char * * @return Like mkdir() do we return -1 on fail with errno set. */ int mkdir_recursive(const char *pathname, int isdir) { char fullname[PATH_MAX+1]; struct stat st; int i = 0; char *tmp, *check = NULL; if (!pathname) { errno = EFAULT; return -1; } if (isdir) check = xstrdup(pathname); else if ((tmp = xstrrchr(pathname, '/'))) check = xstrndup(pathname, (tmp-pathname)+1); if (check) { if (stat(check, &st) == 0) { /* if smth exists with such filename */ xfree(check); if (!S_ISDIR(st.st_mode)) { /* and it's not dir, abort. */ errno = ENOTDIR; return -1; } return 0; } xfree(check); } do { if (i == PATH_MAX) { errno = ENAMETOOLONG; return -1; } fullname[i] = pathname[i]; if (pathname[i] == '/' || (isdir && pathname[i] == '\0')) { /* if it's / or it's last char.. */ if (!isdir && !xstrchr(&pathname[i], '/')) /* if it's not dir (e.g filename) we don't want to create the dir.. */ return 0; fullname[i+1] = '\0'; if (stat(fullname, &st) == 0) { /* if smth exists with such filename */ if (!S_ISDIR(st.st_mode)) { /* and it's not dir, abort. */ errno = ENOTDIR; return -1; } } else { /* if not, try mkdir() and if fail exit. */ if #ifndef NO_POSIX_SYSTEM (mkdir(fullname, 0700) == -1) #else (mkdir(fullname) == -1) #endif return -1; } } } while (pathname[i++]); /* while not NUL */ return 0; } /** * prepare_pathf() * * Return path to configdir/profiledir (~/.ekg2 or ~/.ekg2/$PROFILE) and append @a filename (formated using vsnprintf()) * If length of this string is larger than PATH_MAX (4096 on Linux) than unlike prepare_path() it'll return NULL */ const char *prepare_pathf(const char *filename, ...) { static char path[PATH_MAX]; size_t len; int fpassed = (filename && *filename); len = g_strlcpy(path, config_dir ? config_dir : "", sizeof(path)); if (len + fpassed >= sizeof(path)) { debug_error("prepare_pathf() LEVEL0 %d + %d >= %d\n", len, fpassed, sizeof(path)); return NULL; } if (fpassed) { va_list ap; size_t len2; path[len++] = '/'; va_start(ap, filename); len2 = vsnprintf(&path[len], sizeof(path)-len, filename, ap); va_end(ap); if (len2 == -1 || (len + len2) >= sizeof(path)) { /* (len + len2 == sizeof(path)) ? */ debug_error("prepare_pathf() LEVEL1 %d | %d + %d >= %d\n", len2, len, len2, sizeof(path)); return NULL; } } return path; } /* * prepare_path() * * zwraca pen ciek do podanego pliku katalogu ~/.ekg2/ * * - filename - nazwa pliku, * - do_mkdir - czy tworzy katalog ~/.ekg2 ? */ const char *prepare_path(const char *filename, int do_mkdir) { static char path[PATH_MAX]; if (do_mkdir) { if (config_profile) { char *cd = xstrdup(config_dir), *tmp; if ((tmp = xstrrchr(cd, '/'))) *tmp = 0; #ifndef NO_POSIX_SYSTEM if (mkdir(cd, 0700) && errno != EEXIST) { #else if (mkdir(cd) && errno != EEXIST) { #endif xfree(cd); return NULL; } xfree(cd); } #ifndef NO_POSIX_SYSTEM if (mkdir(config_dir, 0700) && errno != EEXIST) #else if (mkdir(config_dir) && errno != EEXIST) #endif return NULL; } if (!filename || !*filename) snprintf(path, sizeof(path), "%s", config_dir); else snprintf(path, sizeof(path), "%s/%s", config_dir, filename); return path; } /** * prepare_path_user() * * Converts path given by user to absolute path. * * @bug Behaves correctly only with POSIX slashes, need to be modified for NO_POSIX_SYSTEM. * * @param path - input path. * * @return Pointer to output path or NULL, if longer than PATH_MAX. */ const char *prepare_path_user(const char *path) { static char out[PATH_MAX]; const char *in = path; const char *homedir = NULL; if (!in || (xstrlen(in)+1 > sizeof(out))) /* sorry, but I don't want to additionally play with '..' here */ return NULL; #ifndef NO_POSIX_SYSTEM if (*in == '/') /* absolute path */ #endif xstrcpy(out, in); #ifndef NO_POSIX_SYSTEM else { if (*in == '~') { /* magical home directory handler */ ++in; if (*in == '/') { /* own homedir */ if (!home_dir) return NULL; homedir = home_dir; } else { struct passwd *p; const char *slash = xstrchr(in, '/'); if (slash) { char *user = xstrndup(in, slash-in); if ((p = getpwnam(user))) { homedir = p->pw_dir; in = slash+1; } else homedir = ""; xfree(user); } --in; } } if (!homedir || *homedir != '/') { if (!(getcwd(out, sizeof(out)-xstrlen(homedir)-xstrlen(in)-2))) return NULL; if (*out != '/') { debug_error("prepare_path_user(): what the holy shmoly? getcwd() didn't return absolute path! (windows?)\n"); return NULL; } xstrcat(out, "/"); } else *out = 0; if (homedir && g_strlcat(out, homedir, sizeof(out)-xstrlen(out)-1) >= sizeof(out)-xstrlen(out)-1) return NULL; /* we don't add slash here, 'cause in already has it */ if (g_strlcat(out, in, sizeof(out)-xstrlen(out)) >= sizeof(out)-xstrlen(out)) return NULL; } { char *p; while ((p = xstrstr(out, "//"))) /* remove double slashes */ memmove(p, p+1, xstrlen(p+1)+1); while ((p = xstrstr(out, "/./"))) /* single dots suck too */ memmove(p, p+2, xstrlen(p+2)+1); while ((p = xstrstr(out, "/../"))) { /* and finally, '..' */ char *prev; *p = 0; if (!(prev = xstrrchr(out, '/'))) prev = p; memmove(prev, p+3, xstrlen(p+3)+1); } /* clean out end of path */ p = out+xstrlen(out)-1; if (*p == '.') { if (*(p-1) == '/') /* '.' */ *(p--) = 0; else if (*(p-1) == '.' && *(p-2) == '/') { /* '..' */ char *q; p -= 2; *p = 0; if ((q = xstrrchr(out, '/'))) *(q+1) = 0; else { *p = '/'; *(p+1) = 0; } } } if (*p == '/' && out != p) *p = 0; } #endif return out; } /** * random_line() * * Open file specified by @a path and select by random one line from file specified by @a path * * @param path - path to file. * * @sa read_file() - if you want read next line from file. * * @return NULL - if file was not found or file has no line inside.
* else random line founded at file, */ static char *random_line(const char *path) { int max = 0, item, tmp = 0; char *line; FILE *f; if (!path) return NULL; if ((f = fopen(path, "r")) == NULL) return NULL; while ((line = read_file(f, 0))) max++; if (max) { rewind(f); item = rand() / (RAND_MAX / max + 1); while ((line = read_file(f, (tmp == item)))) { /* read_file(f, 0) or read_file(f, 1) if this is that line */ if (tmp == item) { fclose(f); return line; } tmp++; } } fclose(f); return NULL; } /* XXX: ekg_fix_utf8() here */ char *read_file_utf(FILE *f, int alloc) { static char buf[1024]; static char *reres = NULL; char *res = NULL; size_t reslen = 0; int isnewline = 0; if (alloc == -1) { xfree(reres); reres = NULL; return NULL; } if (!f) return NULL; while (fgets(buf, sizeof(buf), f)) { size_t new_size = reslen + xstrlen(buf); if (xstrchr(buf, '\n')) { isnewline = 1; if (!reslen) { res = buf; reslen = new_size; break; } } res = reres = xrealloc(reres, new_size+1); xstrcpy(res + reslen, buf); reslen = new_size; if (isnewline) break; } if (reslen > 0 && res[reslen - 1] == '\n') { res[reslen - 1] = 0; reslen--; } if (reslen > 0 && res[reslen - 1] == '\r') { res[reslen - 1] = 0; /* reslen--; */ } return (alloc) ? xstrdup(res) : res; } /** * read_file() * * Read next line from file @a f, if needed alloc memory for it.
* Remove \\r and \\n chars from end of line if needed. * * @param f - opened FILE * * @param alloc * - If 0 than it return internal read_file() either xrealloc()'ed or static char with sizeof()==1024, * which you MUST NOT xfree()
* - If 1 than it return strdup()'ed string this MUST xfree()
* - If -1 than it free internal pointer which were used by xrealloc() * * @return Line without \\r and \\n which must or mustn't be xfree()'d. It depends on @a alloc param */ char *read_file(FILE *f, int alloc) { static char *tmp = NULL; char *buf = read_file_utf(f, 0); char *res; g_free(tmp); tmp = NULL; if (alloc == -1) return NULL; res = ekg_recode_from_locale(buf); if (!alloc) tmp = res; return res; } /** * read_line() * * Read a single line from GDataInputStream. * * @param f - GDataInputStream to read from. * * @return Pointer to a static line which will be overwritten by next * call to read_line() or NULL on EOF or error. */ gchar *read_line(GDataInputStream *f) { static gchar *buf = NULL; GError *err = NULL; g_free(buf); buf = g_data_input_stream_read_line(f, NULL, NULL, &err); if (!buf && err) { debug_error("read_line() failed: %s\n", err->message); g_error_free(err); } else if (buf) ekg_fix_utf8(buf); return buf; } /** * timestamp() * * It returns static buffer with formated current time. * * @param format - format to pass to strftime() [man 3 strftime] * * @return if format is NULL or format == '\\0' than it return ""
* else it returns strftime()'d value, or "TOOLONG" if @a buf (sizeof(@a buf) == 100) was too small.. */ const char *timestamp(const char *format) { static char buf[100]; time_t t; struct tm *tm; if (!format || format[0] == '\0') return ""; t = time(NULL); tm = localtime(&t); if (!strftime(buf, sizeof(buf), format, tm)) return "TOOLONG"; return buf; } const char *timestamp_time(const char *format, time_t t) { struct tm *tm; static char buf[100]; if (!format || format[0] == '\0') return ekg_itoa(t); tm = localtime(&t); if (!strftime(buf, sizeof(buf), format, tm)) return "TOOLONG"; return buf; } /* * xstrmid() * * wycina fragment tekstu alokujc dla niego pami. * * - str - tekst rdowy, * - start - pierwszy znak, * - length - dugo wycinanego tekstu, jeli -1 do koca. */ char *xstrmid(const char *str, int start, int length) { char *res, *q; const char *p; if (!str) return xstrdup(""); if (start > xstrlen(str)) start = xstrlen(str); if (length == -1) length = xstrlen(str) - start; if (length < 1) return xstrdup(""); if (length > xstrlen(str) - start) length = xstrlen(str) - start; res = xmalloc(length + 1); for (p = str + start, q = res; length; p++, q++, length--) *q = *p; *q = 0; return res; } struct color_map color_map_default[26] = { { 'k', 0, 0, 0 }, { 'r', 168, 0, 0, }, { 'g', 0, 168, 0, }, { 'y', 168, 168, 0, }, { 'b', 0, 0, 168, }, { 'm', 168, 0, 168, }, { 'c', 0, 168, 168, }, { 'w', 168, 168, 168, }, { 'K', 96, 96, 96 }, { 'R', 255, 0, 0, }, { 'G', 0, 255, 0, }, { 'Y', 255, 255, 0, }, { 'B', 0, 0, 255, }, { 'M', 255, 0, 255, }, { 'C', 0, 255, 255, }, { 'W', 255, 255, 255, }, /* dodatkowe mapowanie rnych kolorw istniejcych w GG */ { 'C', 128, 255, 255, }, { 'G', 128, 255, 128, }, { 'M', 255, 128, 255, }, { 'B', 128, 128, 255, }, { 'R', 255, 128, 128, }, { 'Y', 255, 255, 128, }, { 'm', 168, 128, 168, }, { 'c', 128, 168, 168, }, { 'g', 64, 168, 64, }, { 'm', 128, 64, 128, } }; /* * color_map() * * funkcja zwracajca kod koloru z domylnej 16-kolorowej palety terminali * ansi odpadajcemu podanym wartociom RGB. */ char color_map(unsigned char r, unsigned char g, unsigned char b) { unsigned long mindist = 255 * 255 * 255; struct color_map *map = color_map_default; char ch = 0; int i; /* debug("color=%.2x%.2x%.2x\n", r, g, b); */ #define __sq(x) ((x)*(x)) for (i = 0; i < 26; i++) { unsigned long dist = __sq(r - map[i].r) + __sq(g - map[i].g) + __sq(b - map[i].b); /* debug("%d(%c)=%.2x%.2x%.2x, dist=%ld\n", i, map[i].color, map[i].r, map[i].g, map[i].b, dist); */ if (dist < mindist) { ch = map[i].color; mindist = dist; } } #undef __sq /* debug("mindist=%ld, color=%c\n", mindist, ch); */ return ch; } /* * sprawdza czy podany znak jest znakiem alphanumerycznym (uwzlglednia polskie znaki) */ int isalpha_pl(unsigned char c) { /* gg_debug(GG_DEBUG_MISC, "c: %d\n", c); */ if(isalpha(c)) /* normalne znaki */ return 1; else if(c == 177 || c == 230 || c == 234 || c == 179 || c == 241 || c == 243 || c == 182 || c == 191 || c == 188) /* polskie literki */ return 1; else if(c == 161 || c == 198 || c == 202 || c == 209 || c == 163 || c == 211 || c == 166 || c == 175 || c == 172) /* wielka litery polskie */ return 1; else return 0; } void ignore_result_helper(int __attribute__((unused)) dummy, ...) { } /* * strcasestr() * * robi to samo co xstrstr() tyle e bez zwracania uwagi na wielko * znakw. */ char *strcasestr(const char *haystack, const char *needle) { int i, hlen = xstrlen(haystack), nlen = xstrlen(needle); for (i = 0; i <= hlen - nlen; i++) { if (!xstrncasecmp(haystack + i, needle, nlen)) return (char*) (haystack + i); } return NULL; } /* * msg_all() * * msg to all users in session's userlist * it uses function to do it */ int msg_all(session_t *s, const char *function, const char *what) { userlist_t *ul; if (!s->userlist) return -1; if (!function) return -2; for (ul = s->userlist; ul; ul = ul->next) { userlist_t *u = ul; if (!u || !u->uid) continue; /* XXX, when adding to userlist if we check if uid is good, this code will be ok. */ command_exec_format(NULL, s, 0, "%s \"%s\" %s", function, get_nickname(s, u->uid), what); } return 0; } #ifndef NO_POSIX_SYSTEM static void speech_child_handler(GPid pid, gint status, gpointer data) { speech_pid = 0; if (!config_speech_app) buffer_free(&buffer_speech); if (buffer_speech.count && !status) { char *str = buffer_tail(&buffer_speech); say_it(str); g_free(str); } } #endif /* * say_it() * * zajmuje si wypowiadaniem tekstu, uwaajc na ju dziaajcy * syntezator w tle. * * 0/-1/-2. -2 w przypadku, gdy dodano do bufora. */ int say_it(const char *str) { #ifndef NO_POSIX_SYSTEM pid_t pid; if (!config_speech_app || !str || !xstrcmp(str, (""))) return -1; if (speech_pid) { buffer_add(&buffer_speech, NULL, str); return -2; } if ((pid = fork()) < 0) return -1; speech_pid = pid; if (!pid) { char *tmp = saprintf("%s 2>/dev/null 1>&2", config_speech_app); FILE *f = popen(tmp, "w"); int status = -1; xfree(tmp); if (f) { fprintf(f, "%s.", str); status = pclose(f); /* dzieciak czeka na dzieciaka */ } exit(status); } ekg_child_add(NULL, "(speech)", pid, speech_child_handler, NULL, NULL); return 0; #else return -1; #endif } #ifndef DISABLE_DEBUG void debug_ext(debug_level_t level, const char *format, ...) { va_list ap; if (!config_debug) return; va_start(ap, format); ekg_debug_handler(level, format, ap); va_end(ap); } /* * debug() * * debugowanie dla ekg. */ void debug(const char *format, ...) { va_list ap; if (!config_debug) return; va_start(ap, format); ekg_debug_handler(0, format, ap); va_end(ap); } #endif /* * base64_encode() * * zapisuje cig znakw w base64. * * - buf - cig znakw. * * zaalokowany bufor. */ char *base64_encode(const char *buf, size_t len) { if (!buf) return NULL; return g_base64_encode((guchar*)buf, len); } /* * base64_decode() * * dekoduje cig znakw z base64. * * - buf - cig znakw. * * zaalokowany bufor. */ char *base64_decode(const char *buf) { size_t buflen; if (!buf || !(*buf)) return NULL; return (char*) g_base64_decode(buf, &buflen); } /* * split_line() * * podaje kolejn lini z bufora tekstowego. niszczy go bezpowrotnie, dzielc * na kolejne stringi. zdarza si, nie ma potrzeby pisania funkcji dublujcej * bufor eby tylko mie nieruszone dane wejciowe, skoro i tak nie bd nam * poniej potrzebne. obcina `\r\n'. * * - ptr - wskanik do zmiennej, ktra przechowuje aktualn pozycj * w przemiatanym buforze * * wskanik do kolejnej linii tekstu lub NULL, jeli to ju koniec bufora. */ char *split_line(char **ptr) { char *foo, *res; if (!ptr || !*ptr || !xstrcmp(*ptr, "")) return NULL; res = *ptr; if (!(foo = xstrchr(*ptr, '\n'))) *ptr += xstrlen(*ptr); else { size_t reslen; *ptr = foo + 1; *foo = 0; reslen = xstrlen(res); if (reslen > 1 && res[reslen - 1] == '\r') res[reslen - 1] = 0; } return res; } /* * ekg_status_label() * * tworzy etykiet formatki opisujcej stan. */ const char *ekg_status_label(const int status, const char *descr, const char *prefix) { static char buf[100]; /* maybe dynamic buffer would be better? */ const char *status_string = ekg_status_string(status, 0); snprintf(buf, sizeof(buf), "%s%s%s", (prefix) ? prefix : "", status_string, (descr) ? "_descr" : ""); return buf; } /* * ekg_draw_descr() * * losuje opis dla danego stanu lub pobiera ze zmiennej, lub cokolwiek * innego. */ char *ekg_draw_descr(const int status) { const char *value; char file[100]; char var[100]; variable_t *v; if (EKG_STATUS_IS_NA(status)) { /* or maybe == NA ? */ xstrcpy(var, ("quit_reason")); xstrcpy(file, "quit.reasons"); } else if (status == EKG_STATUS_AVAIL) { xstrcpy(var, ("back_reason")); xstrcpy(file, "back.reasons"); } else { /* Wouldn't it be better to use command-names? */ snprintf(var, sizeof(var), "%s_reason", ekg_status_string(status, 0)); snprintf(file, sizeof(file), "%s.reasons", ekg_status_string(status, 0)); } if (!(v = variable_find(var)) || v->type != VAR_STR) return NULL; value = *(char**)(v->ptr); if (!value) return NULL; if (!xstrcmp(value, "*")) return random_line(prepare_path(file, 0)); return xstrdup(value); } /* * ekg_update_status() * * updates our status, if we are on session contact list * */ void ekg_update_status(session_t *session) { userlist_t *u; if ((u = userlist_find(session, session->uid))) { xfree(u->descr); u->descr = xstrdup(session->descr); if (!session_connected_get(session)) u->status = EKG_STATUS_NA; else u->status = session->status; u->blink = 0; { const char *__session = session_uid_get(session); const char *__uid = u->uid; query_emit(NULL, "userlist-changed", &__session, &__uid); } } } /* status string tables */ struct ekg_status_info { status_t status; /* enumed status */ const char* label; /* name used in formats */ const char* command; /* command used to set status, if ==format, NULL */ }; /* please, keep it sorted with status_t */ const struct ekg_status_info ekg_statuses[] = { { EKG_STATUS_ERROR, "error" }, { EKG_STATUS_BLOCKED, "blocking" }, { EKG_STATUS_UNKNOWN, "unknown" }, { EKG_STATUS_NA, "notavail" }, { EKG_STATUS_INVISIBLE,"invisible" }, { EKG_STATUS_DND, "dnd" }, { EKG_STATUS_GONE, "gone" }, { EKG_STATUS_XA, "xa" }, { EKG_STATUS_AWAY, "away" }, { EKG_STATUS_AVAIL, "avail", "back", }, { EKG_STATUS_FFC, "chat", "ffc" }, /* here go the special statuses */ { EKG_STATUS_AUTOAWAY, "autoaway" }, { EKG_STATUS_AUTOXA, "autoxa" }, { EKG_STATUS_AUTOBACK, "autoback" }, { EKG_STATUS_NULL } }; static inline const struct ekg_status_info *status_find(const int status) { const struct ekg_status_info *s; /* as long as ekg_statuses[] are sorted, this should be fast */ if (status < EKG_STATUS_LAST) { for (s = &(ekg_statuses[status-1]); s->status != EKG_STATUS_NULL; s++) { if (s->status == status) return s; } } debug_function("status_find(), going into fallback loop (statuses ain't sorted?)\n"); /* fallback if statuses aren't sorted, we've got unknown or special status, * in second case we'll iterate part of list twice, but that's rather * rare case, so I think optimization isn't needed here. */ for (s = ekg_statuses; s->status != EKG_STATUS_NULL; s++) { if (s->status == status) return s; } return NULL; } /* * ekg_status_string() * * converts enum status to string * cmd = 0 for normal labels, 1 for command names, 2 for labels+special (esp. for debug, use wisely) */ const char *ekg_status_string(const int status, const int cmd) { const char *r = NULL; if ((status > 0) && (status < (cmd == 2 ? 0x100 : 0x80))) { const struct ekg_status_info *s = status_find(status); if (s) { if (cmd == 1) r = s->command; /* if command differs from status */ if (!r) r = s->label; /* else fetch status */ } } if (!r) { const struct ekg_status_info *s = status_find(cmd == 1 ? EKG_STATUS_AVAIL : EKG_STATUS_UNKNOWN); /* we only allow 00..7F, or 00..FF with cmd==2 * else we return either UNKNOWN or AVAIL if cmd==1 */ debug_error("ekg_status_string(): called with unexpected status: 0x%02x\n", status); if (!s) { debug_error("ekg_status_string(): critical error, status_find with predef value failed!\n"); return NULL; } return (cmd == 1 ? s->command : s->label); } return r; } /* * ekg_status_int() * * converts string to enum status */ int ekg_status_int(const char *text) { const struct ekg_status_info *s; for (s = ekg_statuses; s->status != EKG_STATUS_NULL; s++) { if (!xstrcasecmp(text, s->label) || !xstrcasecmp(text, s->command)) return s->status; } debug_error("ekg_status_int(): Got unexpected status: %s\n", text); return EKG_STATUS_NULL; } /* * ekg_sent_message_format() * * funkcja pomocnicza dla protokow obsugujcych kolorki. z podanego * tekstu wycina kolorki i zwraca informacje o formatowaniu tekstu bez * kolorkw. */ guint32 *ekg_sent_message_format(const char *text) { guint32 *format, attr; char *newtext, *q; const char *p, *end; int len; /* jeli nie stwierdzono znakw kontrolnych, spadamy */ /* if (!xstrpbrk(text, "\x02\x03\x12\x14\x1f")) return NULL; */ /* oblicz dugo tekstu bez znaczkw formatujcych */ for (p = text, len = 0; *p; p++) { if (!xstrchr(("\x02\x03\x12\x14\x1f"), *p)) len++; } if (len == xstrlen(text)) return NULL; newtext = xmalloc(len + 1); format = xmalloc(len * 4); end = text + xstrlen(text); for (p = text, q = newtext, attr = 0; p < end; ) { int j; if (*p == 18 || *p == 3) { /* Ctrl-R, Ctrl-C */ p++; if (xisdigit(*p)) { int num = atoi(p); if (num < 0 || num > 15) num = 0; p++; if (xisdigit(*p)) p++; attr &= ~EKG_FORMAT_RGB_MASK; attr |= EKG_FORMAT_COLOR; attr |= color_map_default[num].r; attr |= color_map_default[num].g << 8; attr |= color_map_default[num].b << 16; } else attr &= ~EKG_FORMAT_COLOR; continue; } if (*p == 2) { /* Ctrl-B */ attr ^= EKG_FORMAT_BOLD; p++; continue; } if (*p == 20) { /* Ctrl-T */ attr ^= EKG_FORMAT_ITALIC; p++; continue; } if (*p == 31) { /* Ctrl-_ */ attr ^= EKG_FORMAT_UNDERLINE; p++; continue; } /* zwyky znak */ *q = *p; for (j = (int) (q - newtext); j < len; j++) format[j] = attr; q++; p++; } return format; } /* * strncasecmp_pl() * * porwnuje dwa cigi o okrelonej przez n dugoci * dziaa analogicznie do xstrncasecmp() * obsuguje polskie znaki */ /* adjusted to use casefold utf8 * (normalized would be better but count would have to be adjusted) * count is in bytes, to make it simpler * XXX: potentially slow, try to get rid of it * in favour of something with the common string being normalized * before calling */ int strncasecmp_pl(const char *cs, const char *ct, size_t count) { gchar *csc = g_utf8_casefold(cs, -1); gchar *ctc = g_utf8_casefold(ct, -1); gint ret = strncmp(csc, ctc, count); g_free(csc); g_free(ctc); return ret; } #ifndef EKG_NO_DEPRECATED /* * saprintf() * * dziaa jak sprintf() tylko, e wyrzuca wskanik * do powstaego cigu * * NOTE: deprecated, please use g_strdup_printf() instead. */ char *saprintf(const char *format, ...) { va_list ap; char *res; va_start(ap, format); res = vsaprintf(format, ap); va_end(ap); return res; } #endif /* * xstrtr() * * zamienia wszystko znaki a na b w podanym cigu * nie robie jego kopi! */ void xstrtr(char *text, char from, char to) { if (!text || !from) return; while (*text++) { if (*text == from) { *text = to; } } } /** * ekg_yield_cpu() * * releases cpu * meant to be called while busy-looping */ inline void ekg_yield_cpu() { #ifdef _POSIX_PRIORITY_SCHEDULING sched_yield(); #endif } /** * ekg_write() * * write data to given fd, if it cannot be done [because system buffer is too small. it'll create watch, and write as soon as possible] * XXX, for now it'll always create watch. * (You can be notified about state of buffer when you call ekg_write(fd, NULL, -1)) * * @note * This _should_ be used as replacement for write() */ int ekg_write(int fd, const char *buf, int len) { watch_t *wl = NULL; list_t l; if (fd == -1) return -1; /* first check if we have watch for this fd */ for (l = watches; l; l = l->next) { watch_t *w = l->data; if (w && w->fd == fd && w->type == WATCH_WRITE && w->buf) { wl = w; break; } } if (wl) { if (!buf && len == -1) /* smells stupid, but do it */ return wl->buf->len; } else { /* if we have no watch, let's create it. */ /* XXX, first try write() ? */ wl = watch_add(NULL, fd, WATCH_WRITE_LINE, NULL, NULL); } return watch_write_data(wl, buf, len); } int ekg_writef(int fd, const char *format, ...) { char *text; int textlen; va_list ap; int res; if (fd == -1 || !format) return -1; va_start(ap, format); text = vsaprintf(format, ap); va_end(ap); textlen = xstrlen(text); debug_io("ekg_writef: %s\n", text ? textlen ? text: "[0LENGTH]":"[FAILED]"); if (!text) return -1; res = ekg_write(fd, text, textlen); xfree(text); return res; } /** * ekg_close() * * close fd and all watches associated with that fd * * @note * This _should_ be used as replacement for close() (especially in protocol plugins) */ int ekg_close(int fd) { list_t l; if (fd == -1) return -1; for (l = watches; l; l = l->next) { watch_t *w = l->data; if (w && w->fd == fd) { debug("ekg_close(%d) w->plugin: %s w->session: %s w->type: %d w->buf: %d\n", fd, w->plugin ? w->plugin->name : "-", w->is_session ? ((session_t *) w->data)->uid : "-", w->type, !!w->buf); watch_free(w); } } return close(fd); } /** * password_input() * * Try to get password through UI_PASSWORD_INPUT, printing error messages if needed. * * @return Pointer to new password (which needs to be freed) or NULL, if not * succeeded (wrong input / no support). */ char *password_input(const char *prompt, const char *rprompt, const bool norepeat) { char *pass = NULL; if (query_emit(NULL, "ui-password-input", &pass, &prompt, norepeat ? NULL : &rprompt) == -2) { print("password_nosupport"); return NULL; } return pass; } int is_utf8_string(const char *txt) { const char *p; int mask, n; if (!txt) return 0; for (p = txt; *p; p++) { // 0xxxxxxx continue // 10xxxxxx n=0; return 0 // 110xxxxx n=1 // 1110xxxx n=2 // 11110xxx n=3 // 111110xx n=4 // 1111110x n=5 // 1111111x n>5; return 0 if (!(*p & 0x80)) continue; for (n = 0, mask = 0x40; (*p & mask); n++, mask >>= 1); if (!n || (n>5)) return 0; for (; n; n--) if ((*++p & 0xc0) != 0x80) return 0; } return 1; } /***************** variable stuff ******************/ /* * get_variable_value() * * Returns: a newly-allocated string holding the variable value. * The returned string should be freed with g_free() when no longer needed. */ static char *get_variable_value(variable_t *v) { /* We delay variable initialization until the * type is known to be such that is properly * aligned for reading an int. */ int number = *(int*)(v->ptr); gchar *value = NULL; if (!v->display) { value = xstrdup(("(...)")); } else if (v->type == VAR_STR || v->type == VAR_FILE || v->type == VAR_DIR || v->type == VAR_THEME) { char *string = *(char**)(v->ptr); value = (string) ? saprintf(("\"%s\""), string) : xstrdup(("(none)")); } else if (v->type == VAR_BOOL) { value = xstrdup( (number) ? ("1 (on)") : ("0 (off)") ); } else if ((v->type == VAR_INT || v->type == VAR_MAP) && !v->map) { value = xstrdup(ekg_itoa(number)); } else if (v->type == VAR_INT && v->map) { int i; for (i = 0; v->map[i].label; i++) if (v->map[i].value == number) { value = saprintf(("%d (%s)"), number, v->map[i].label); break; } if (!value) value = saprintf(("%d"), number); } else if (v->type == VAR_MAP && v->map) { GString *s = g_string_new(ekg_itoa(number)); int i, first = 1; for (i = 0; v->map[i].label; i++) { if ((number & v->map[i].value) || (!number && !v->map[i].value)) { g_string_append(s, (first) ? (" (") : (",")); first = 0; g_string_append(s, v->map[i].label); } } if (!first) g_string_append_c(s, (')')); value = g_string_free(s, FALSE); } else if (v->type == -1) { /* specjal type for "status" */ char *string = *(char**)(v->ptr); value = string ? saprintf(("%s"), string) : xstrdup(""); } return value; } /* * variable_display() * * Displays variable value. */ void variable_display(variable_t *v, int quiet) { gchar *value; if (quiet || v->display == 2) return; value = get_variable_value(v); printq("variable", v->name, value); g_free(value); } /* * get_fake_sess_variable() * * Creates (variable_t) variable from session variable. * The returned value should be freed with g_free() when no longer needed. */ static variable_t *get_fake_sess_variable(session_t *s, const char *name) { static int fake_int_value; static const char *val; variable_t *var = g_malloc0(sizeof(variable_t)); int id; var->name = (char *)name; var->type = VAR_STR; var->display = 1; var->ptr = &val; /* emulate session_get() */ if (!xstrcasecmp(name, "uid")) val = session_uid_get(s); else if (!xstrcasecmp(name, "alias")) val = session_alias_get(s); else if (!xstrcasecmp(name, "descr")) val = session_descr_get(s); else if (!xstrcasecmp(name, "status")) { var->type = -1; val = ekg_status_string(session_status_get(s), 2); } else if (!xstrcasecmp(name, "statusdescr")) var->display = 2; else if (!xstrcasecmp(name, "password")) var->display = 0; else if ((id = plugin_var_find(s->plugin, name))) { plugins_params_t *pa = &(((plugin_t *) s->plugin)->params[id-1]); var->type = pa->type; var->map = pa->map; if ((var->type == VAR_INT) || (var->type == VAR_BOOL) || (var->type == VAR_MAP)) { fake_int_value = s->values[id-1] ? atoi(s->values[id-1]) : 0; var->ptr = &fake_int_value; } else var->ptr = &(s->values[id-1]); if (pa->secret) var->display = 0; } else { g_free(var); return NULL; } return var; } /* * session_variable_display() * * Displays session variable value (/session --get [session uid] ) * */ int session_variable_display(session_t *s, const char *name, int quiet) { gchar *value = NULL; variable_t *var; if (!(var = get_fake_sess_variable(s, name))) return 0; if (var->display != 2) { value = get_variable_value(var); printq("session_variable", session_name(s), name, value); } g_free(value); g_free(var); return 1; } /* * session_variable_info() * * Displays variable value (/session ) */ void session_variable_info(session_t *s, const char *name, int quiet) { gchar *value = NULL; variable_t *var; if ( quiet || !xstrcmp(name, "alias") ) return; if (!(var = get_fake_sess_variable(s, name))) return; if (var->display != 2) { value = get_variable_value(var); printq("session_info_param", name, value); } g_free(value); g_free(var); } /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: * vim: noet */ ekg2-0.4~pre+20120506.1/ekg/stuff.h000066400000000000000000000245661175142753400162400ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2001-2003 Wojtek Kaniewski * Robert J. Wony * Pawe Maziarz * Dawid Jarosz * Piotr Domagalski * Adam Mikuta * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __EKG_STUFF_H #define __EKG_STUFF_H #include #include #include #include "win32.h" #ifndef NO_POSIX_SYSTEM #include #include #endif #include #include #include #include #include #include "dynstuff.h" #include "plugins.h" #include "sessions.h" #include "userlist.h" #include "vars.h" #ifdef __cplusplus extern "C" { #endif #define DEBUG_MAX_LINES 50 /* ile linii z debug zrzuca do pliku */ /* obsuga procesw potomnych */ #ifndef EKG2_WIN32_NOFUNCTION typedef struct alias { struct alias *next; char *name; /* nazwa aliasu */ list_t commands; /* commands->data to (char*) */ } alias_t; #endif enum mesg_t { MESG_CHECK = -1, MESG_OFF, MESG_ON, MESG_DEFAULT }; struct conference { struct conference *next; char *name; ignore_t ignore; list_t recipients; }; typedef struct newconference { struct newconference *next; char *session; char *name; struct userlist *participants; void *priv_data; } newconference_t; struct buffer { struct buffer *next; time_t ts; char *target; char *line; }; struct buffer_info { struct buffer *data; int count; int max_lines; struct buffer *last; /* fast access to last element, esp. for log_raw */ }; struct color_map { int color; unsigned char r, g, b; }; #ifndef EKG2_WIN32_NOFUNCTION extern alias_t *aliases; extern list_t autofinds; /* char* data */ extern struct conference *conferences; extern newconference_t *newconferences; extern struct buffer_info buffer_debug; extern struct buffer_info buffer_speech; extern char *config_profile; extern int config_changed; extern int ekg2_reason_changed; extern pid_t speech_pid; extern int no_mouse; extern int old_stderr; extern int mesg_startup; extern char *config_away_reason; extern int config_auto_save; extern int config_auto_user_add; extern char *config_back_reason; extern int config_beep; extern int config_beep_msg; extern int config_beep_chat; extern int config_beep_notify; extern int config_completion_notify; extern char *config_completion_char; extern int config_debug; extern int config_default_status_window; extern int config_display_ack; extern int config_display_blinking; extern int config_display_color; extern char *config_display_color_map; extern int config_display_crap; extern int config_display_day_changed; extern int config_display_notify; extern int config_display_sent; extern int config_display_welcome; extern int config_emoticons; extern int config_events_delay; extern int config_expert_mode; extern int config_history_savedups; extern int config_keep_reason; extern int config_last; extern int config_last_size; extern int config_make_window; extern int config_mesg; extern int config_query_commands; extern int config_slash_messages; extern char *config_quit_reason; extern int config_save_password; extern int config_save_quit; extern char *config_session_default; extern int config_sessions_save; extern int config_send_white_lines; extern int config_sort_windows; extern char *config_sound_app; extern char *config_sound_chat_file; extern char *config_sound_msg_file; extern char *config_sound_sysmsg_file; extern char *config_sound_notify_file; extern char *config_sound_mail_file; extern char *config_speech_app; extern char *config_subject_prefix; extern char *config_subject_reply_prefix; extern char *config_tab_command; extern char *config_theme; extern int config_time_deviation; extern char *config_timestamp; extern int config_timestamp_show; extern int config_window_session_allow; extern char *config_windows_layout; extern int config_windows_save; extern char *config_dcc_dir; extern int config_version; extern char *config_exit_exec; extern int config_session_locks; extern char *config_nickname; extern char *formated_config_timestamp; extern char *home_dir; extern char *config_dir; extern const char *console_charset; extern gboolean console_charset_is_utf8; extern int in_autoexec; extern int ekg_watches_removed; extern time_t ekg_started; extern int quit_message_send; extern int batch_mode; extern char *batch_line; extern struct color_map color_map_default[16+10]; void windows_save(); int alias_add(const char *string, int quiet, int append); int alias_remove(const char *name, int quiet); void aliases_destroy(); char *base64_encode(const char *buf, size_t len); char *base64_decode(const char *buf); int buffer_add(struct buffer_info *type, const char *target, const char *line); int buffer_add_str(struct buffer_info *type, const char *target, const char *str); char *buffer_tail(struct buffer_info *type); void buffer_free(struct buffer_info *type); void changed_auto_save(const char *var); void changed_display_blinking(const char *var); void changed_make_window(const char *var); void changed_mesg(const char *var); void changed_theme(const char *var); void changed_config_timestamp(const char *var); const char *compile_time(); struct conference *conference_add(session_t *session, const char *string, const char *nicklist, int quiet); int conference_remove(const char *name, int quiet); struct conference *conference_create(session_t *session, const char *nicks); struct conference *conference_find(const char *name); struct conference *conference_find_by_uids(session_t *s, const char *from, const char **recipients, int count, int quiet); int conference_set_ignore(const char *name, int flag, int quiet); int conference_rename(const char *oldname, const char *newname, int quiet); int conference_participant(struct conference *c, const char *uid); void conferences_destroy(); /* BEGIN OF newconference API HERE */ userlist_t *newconference_member_add(newconference_t *conf, const char *uid, const char *nick); userlist_t *newconference_member_find(newconference_t *conf, const char *uid); int newconference_member_remove(newconference_t *conf, userlist_t *u); newconference_t *newconference_create(session_t *s, const char *name, int create_wnd); newconference_t *newconference_find(session_t *s, const char *name); void newconference_destroy(newconference_t *conf, int kill_wnd); void newconferences_destroy(); /* END of newconference API */ int ekg_hash(const char *name); GDataInputStream *help_open(const gchar *name, const gchar *plugin); gchar *read_line(GDataInputStream *f); int mesg_set(int what); char *strip_spaces(char *line); int strncasecmp_pl(const char * cs,const char * ct,size_t count); int mkdir_recursive(const char *pathname, int isdir); #ifdef __GNUC__ char *saprintf(const char *format, ...) __attribute__ ((format (printf, 1, 2))); #else char *saprintf(const char *format, ...); #endif int play_sound(const char *sound_path); const char *prepare_path(const char *filename, int do_mkdir); const char *prepare_pathf(const char *filename, ...); const char *prepare_path_user(const char *path); char *read_file(FILE *f, int alloc); char *read_file_utf(FILE *f, int alloc); const char *timestamp(const char *format); const char *timestamp_time(const char *format, time_t t); char *xstrmid(const char *str, int start, int length); void xstrtr(char *text, char from, char to); char color_map(unsigned char r, unsigned char g, unsigned char b); char *strcasestr(const char *haystack, const char *needle); int msg_all(session_t *s, const char *function, const char *what); int say_it(const char *str); char *split_line(char **ptr); int isalpha_pl(unsigned char c); /* makra, dziki ktrym pozbywamy si warning'w */ #define xisxdigit(c) isxdigit((int) (unsigned char) c) #define xisdigit(c) isdigit((int) (unsigned char) c) #define xisalpha(c) isalpha_pl((int) (unsigned char) c) #define xisalnum(c) isalnum((int) (unsigned char) c) #define xisspace(c) isspace((int) (unsigned char) c) #define xtolower(c) tolower((int) (unsigned char) c) #define xtoupper(c) toupper((int) (unsigned char) c) void ignore_result_helper(int __attribute__((unused)) dummy, ...); #ifdef __GNUC__ # define IGNORE_RESULT(X) ignore_result_helper(0, (X)) #else # define IGNORE_RESULT(X) (X) #endif const char *ekg_status_label(const int status, const char *descr, const char *prefix); void ekg_update_status(session_t *session); #define ekg_update_status_n(a) ekg_update_status(session_find(a)) const char *ekg_status_string(const int status, const int cmd); int ekg_status_int(const char *text); char *ekg_draw_descr(const int status); guint32 *ekg_sent_message_format(const char *text); void ekg_yield_cpu(); /* recode.c XXX, przeniesc do recode.h */ void *ekg_convert_string_init(const char *from, const char *to, void **rev); void ekg_convert_string_destroy(void *ptr); char *ekg_convert_string_p(const char *ps, void *ptr); char *ekg_convert_string(const char *ps, const char *from, const char *to); string_t ekg_convert_string_t_p(string_t s, void *ptr); string_t ekg_convert_string_t(string_t s, const char *from, const char *to); int ekg_converters_display(int quiet); char *password_input(const char *prompt, const char *rprompt, const bool norepeat); int is_utf8_string(const char *txt); void variable_display(variable_t *v, int quiet); int session_variable_display(session_t *s, const char *name, int quiet); void session_variable_info(session_t *s, const char *name, int quiet); /* funkcje poza stuff.c */ void ekg_exit(); void ekg_debug_handler(int level, const char *format, va_list ap); int ekg_close(int fd); int ekg_write(int fd, const char *buf, int len); int ekg_writef(int fd, const char *format, ...); #endif #ifdef __cplusplus } #endif #endif /* __EKG_STUFF_H */ /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/ekg/themes.c000066400000000000000000002522371175142753400163670ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2001-2006 Wojtek Kaniewski * Leszek Krupiski * Adam Mikuta * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #include #include #include #include #include #include static char *prompt_cache = NULL, *prompt2_cache = NULL, *error_cache = NULL; static const char *timestamp_cache = NULL; static int no_prompt_cache = 0; static int no_prompt_cache_hash = 0x139dcbd6; /* hash value of "no_prompt_cache" */ struct format { struct format *next; char *name; int name_hash; char *value; }; static struct format* formats[0x100]; static LIST_FREE_ITEM(list_format_free, struct format *) { xfree(data->value); xfree(data->name); } DYNSTUFF_LIST_DECLARE(formats, struct format, list_format_free, static __DYNSTUFF_ADD_BEGINNING, /* formats_add() */ static __DYNSTUFF_REMOVE_ITER, /* formats_removei() */ static __DYNSTUFF_DESTROY) /* formats_destroy() */ /** * gim_hash() * */ static int gim_hash(const char *name) { #define ROL(x) (((x>>25)&0x7f)|((x<<7)&0xffffff80)) int hash = 0; for (; *name; name++) { hash ^= *name; hash = ROL(hash); } return hash; #undef ROL } /* * format_find() * * odnajduje warto danego formatu. jeli nie znajdzie, zwraca pusty cig, * eby nie musie uwaa na adne null-references. * * - name. */ const char *format_find(const char *name) { struct format *fl; const char *tmp; int hash; if (!name) return ""; if (config_speech_app && !xstrchr(name, ',')) { char *name2 = saprintf("%s,speech", name); const char *tmp = format_find(name2); xfree(name2); if (format_ok(tmp)) return tmp; } if (config_theme && (tmp = xstrchr(config_theme, ',')) && !xstrchr(name, ',')) { char *name2 = saprintf("%s,%s", name, tmp + 1); const char *tmp = format_find(name2); xfree(name2); if (format_ok(tmp)) return tmp; } hash = gim_hash(name); for (fl = formats[hash & 0xff]; fl; fl = fl->next) { struct format *f = fl; if (hash == f->name_hash && !xstrcmp(f->name, name)) return f->value; } return ""; } /* * format_ansi() * * zwraca sekwencj ansi odpowiadajc danemu kolorkowi z thememw ekg. */ static const char *format_ansi(char ch) { if (ch == 'k') return ("\033[2;30m"); if (ch == 'K') return ("\033[1;30m"); if (ch == 'l') return ("\033[40m"); if (ch == 'r') return ("\033[2;31m"); if (ch == 'R') return ("\033[1;31m"); if (ch == 's') return ("\033[41m"); if (ch == 'g') return ("\033[2;32m"); if (ch == 'G') return ("\033[1;32m"); if (ch == 'h') return ("\033[42m"); if (ch == 'y') return ("\033[2;33m"); if (ch == 'Y') return ("\033[1;33m"); if (ch == 'z') return ("\033[43m"); if (ch == 'b') return ("\033[2;34m"); if (ch == 'B') return ("\033[1;34m"); if (ch == 'e') return ("\033[44m"); if (ch == 'm' || ch == 'p') return ("\033[2;35m"); if (ch == 'M' || ch == 'P') return ("\033[1;35m"); if (ch == 'q') return ("\033[45m"); if (ch == 'c') return ("\033[2;36m"); if (ch == 'C') return ("\033[1;36m"); if (ch == 'd') return ("\033[46m"); if (ch == 'w') return ("\033[2;37m"); if (ch == 'W') return ("\033[1;37m"); if (ch == 'x') return ("\033[47m"); if (ch == 'n') /* clear all attributes */ return ("\033[0m"); if (ch == 'T') /* bold */ return ("\033[1m"); if (ch == 'N') /* clears all attr exc for bkgd */ return ("\033[2m"); if (ch == 'U') /* underline */ return ("\033[4m"); if (ch == 'i') /* blink */ return ("\033[5m"); if (ch == 'V') /* reverse */ return ("\033[7m"); if (ch == 'A') return ("\033(0"); if (ch == 'a') return ("\033(B"); return (""); } /** * fstring_iter() * * Initiate iteration over fstring_t. Set pointers to the initial * values. * * @param s - the iterated fstring_t. * @param text - location to store the current text segment pointer. * @param attr - location to store the current attr segment pointer. * @param len - location to store the length of the current segment. * * @note This function just initializes the vars, use fstring_next() * to get the first segment. */ void fstring_iter(const fstring_t *s, gchar **text, fstr_attr_t **attr, gssize *len) { *text = s->str; *attr = s->attr; *len = 0; } /** * fstring_next() * * Iterate over fragments of fstring_t with unchanged attributes. * * @param text - location to store the current text segment pointer. * @param attr - location to store the current attr segment pointer. * @param len - location to store the length of the current segment. * @param change - location to store changed attribute map or NULL. * * @return TRUE if next segment was found, FALSE on end of string. * * @note This function relies on the values stored by previous calls, * please do not modify them. The variables need to be initialized * by @ref fstring_iter(). * * @code * * gchar *s; * fstr_attr_t *a; * gssize len; * fstr_attr_t change; * * fstring_iter(fstr, &s, &a, &len); * while (fstring_next(&s, &a, &len, NULL)) { * my_setattr(*a); * my_printn(s, len); * } * * @endcode */ gboolean fstring_next(gchar **text, fstr_attr_t **attr, gssize *len, fstr_attr_t *change) { const gchar* c; const fstr_attr_t* a; const fstr_attr_t prevattr = *len ? **attr : FSTR_NORMAL; fstr_attr_t curattr; *text += *len; *attr += *len; curattr = **text ? **attr : FSTR_NORMAL; for (c = *text, a = *attr;; c++, a++) { if (G_UNLIKELY(!*c || *a != curattr)) { *len = (c - *text); /* yep, returning the _previous_ change here */ if (change) *change = ((G_LIKELY(*len) ? curattr : FSTR_NORMAL) ^ prevattr); return !!*len; } } g_assert_not_reached(); } static char *fstring2str(fstring_t *f) { static char *fore = "krgybmcwKRGYBMCW"; static char *back = "lshzeqdx"; GString *st = g_string_sized_new(strlen(f->str)); gchar *c; fstr_attr_t *a, change; gssize len; fstring_iter(f, &c, &a, &len); while (fstring_next(&c, &a, &len, &change)) { if (change) { fstr_attr_t attr = *a; if (change & FSTR_BLINK) { if (attr & FSTR_BLINK) g_string_append(st, format_ansi('i')); /* turn on blinking */ else change |= FSTR_NORMAL; } if (change & FSTR_UNDERLINE) { if (attr & FSTR_UNDERLINE) g_string_append(st, format_ansi('U')); /* turn on underline */ else change |= FSTR_NORMAL; } if (change & FSTR_REVERSE) { if (attr & FSTR_BLINK) g_string_append(st, format_ansi('V')); /* turn on reverse */ else change |= FSTR_NORMAL; } if ((change & FSTR_NORMAL) && (attr & FSTR_NORMAL)) { g_string_append(st, format_ansi('n')); } if (change & (FSTR_BOLD|FSTR_FOREMASK)) { char c = fore[(attr & FSTR_FOREMASK) + ((attr & FSTR_BOLD) ? 8 : 0)]; g_string_append(st, format_ansi(c)); } if (change & FSTR_BACKMASK) g_string_append(st, format_ansi(back[(attr & FSTR_BACKMASK)>>3])); } g_string_append_len(st, c, len); } return g_string_free(st, FALSE); } /* * va_format_string() * * formatuje zgodnie z podanymi parametrami cig znakw. * * - format - warto, nie nazwa formatu, * - ap - argumenty. */ static char *va_format_string(const char *format, va_list ap) { string_t buf = string_init(NULL); static int dont_resolve = 0; const char *p; char *args[9] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; int i, argc = 0; /* liczymy ilo argumentw */ for (p = format; *p; p++) { if (*p == '\\' && p[1] == '%') { p++; continue; } if (*p != '%') continue; p++; if (!*p) break; if (*p == '@') { p++; if (!*p) break; if ((*p - '0') > argc) argc = *p - '0'; } else if (*p == '(' || *p == '[') { if (*p == '(') { while (*p && *p != ')') p++; } else { while (*p && *p != ']') p++; } if (*p) p++; if (!*p) break; if ((*p - '0') > argc) argc = *p - '0'; } else { if (*p >= '1' && *p <= '9' && (*p - '0') > argc) argc = *p - '0'; } } for (i = 0; i < argc; i++) args[i] = va_arg(ap, char *); if (!dont_resolve) { dont_resolve = 1; if (no_prompt_cache) { /* zawsze czytaj */ timestamp_cache = format_find("timestamp"); prompt_cache = format_string(format_find("prompt")); prompt2_cache = format_string(format_find("prompt2")); error_cache = format_string(format_find("error")); } else { /* tylko jeli nie s keszowanie */ if (!timestamp_cache) timestamp_cache = format_find("timestamp"); if (!prompt_cache) prompt_cache = format_string(format_find("prompt")); if (!prompt2_cache) prompt2_cache = format_string(format_find("prompt2")); if (!error_cache) error_cache = format_string(format_find("error")); } dont_resolve = 0; } p = format; while (*p) { if (*p == '\\' && (p[1] == '%' || p[1] == '\\')) { string_append_c(buf, p[1]); p += 2; continue; } else if (*p == '%') { int fill_before = 0; int fill_after = 0; int fill_soft = 1; int fill_length = 0; char fill_char = ' '; int center = 0; p++; if (!*p) break; /* This is conditional formatee, it looks like: * %{NcdefSTUV}X * N - is a parameter number, first letter of this parameter will be checked against 'cdef' letters * if N[0] == 'c', %S formatee is used * if N[0] == 'd', %T formatee is used * if N[0] == 'e', %U formatee is used * if N[0] == 'f', %V formatee is used */ if (*p == '{') { /* how does it works ? i czemu tego nie ma w docs/themes.txt ? */ /* bo to pisa GiM, a on jak wiadomo super komentuje kod :> */ int hm = 0; char *str; char *cnt; p++; if (*p == '}') { p++; p++; continue; } /* dj: why p++; p++; ? who wrote it? */ /* G->dj: It's on purpose, format looks like: * ${...}X */ else if (!(*p >= '0' && *p <= '9')) { p++; continue; } /* not number, skip it this formatee */ else str = args[*p - '1']; /* if number, get str from args table */ p++; /* there must be even, point cnt to the half of string * [according to example above p points to "cdef.." cnt points to "STUV", hm=4] */ cnt = (char *)p; while (*cnt && *cnt!='}') { cnt++; hm++; } hm>>=1; cnt=(char *)(p+hm); /* debug(">>> [HM:%d]", hm); */ for (; hm>0; hm--) { if (*p == *str) break; p++; cnt++; } /* debug(" [%c%c][%d] [%s] ", *p, *cnt, hm, p); */ /* hm == 0, means N-th parameter doesn't fit to any of letter specified, * so we skip this formatee, but first we must fix 'p' to point to proper place */ if (!hm) { p = *cnt ? *(cnt+1) ? (cnt+2) : (cnt+1) : cnt; /* + point 'p' = cnt+2 if it exist * ((cnt+2) is after end of formatee * (there's X after enclosing '}') * + or point to '\0' */ continue; } /* N-th param matched a letter, so point 'p' to that free X at the end, * and correct it with proper formatee letter, and go-on with theme code :) * Now you should understand why there's that 'X' at the end :> */ p=(cnt+hm+1); *((char *)p)=*cnt; } if (*p == '%') string_append_c(buf, '%'); else if (*p == '>') string_append(buf, prompt_cache); else if (*p == ')') string_append(buf, prompt2_cache); else if (*p == '!') string_append(buf, error_cache); else if (*p == '|') string_append(buf, "\033[00m"); /* gupie, wiem */ else if (*p == ']') string_append(buf, "\033[000m"); /* jeszcze gupsze */ else if (*p == '#') string_append(buf, timestamp(timestamp_cache)); else if (config_display_color) { const char *tmp = format_ansi(*p); string_append(buf, tmp); } if (*p == '@') { char *str = (char*) args[*(p + 1) - '1']; if (str) { char *q = str + xstrlen(str) - 1; while (q >= str && (isspace(*q) || ispunct(*q))) q--; if (*q == 'a') string_append(buf, "a"); else string_append(buf, "y"); } else string_append(buf, "y"); /* display_notify&4, I think male form would be fine for UIDs */ p += 2; continue; } if (*p == '[' || *p == '(') { char *q; fill_soft = (*p == '('); p++; if (*p == '^') { center = 1; p++; } /* fill_char = ' '; */ /* zadeklarowane wczesniej */ if (*p == '.') { fill_char = '0'; p++; } else if (*p == ',') { fill_char = '.'; p++; } else if (*p == '_') { fill_char = '_'; p++; } fill_length = strtol(p, &q, 0); p = q; if (fill_length > 0) fill_after = 1; else { fill_length = -fill_length; fill_before = 1; } p++; } if (*p >= '1' && *p <= '9') { char *str = (char *) args[*p - '1']; int i, need_free = 0; if (fill_length) { fstring_t * fstr = fstring_new(str); /* XXX: width */ int len = g_utf8_strlen(fstr->str, -1); if (len >= fill_length) { if (!fill_soft) { /* XXX: how about double width chars? */ *(g_utf8_offset_to_pointer(fstr->str, fill_length)) = 0; str = fstring2str(fstr); need_free = 1; } fill_length = 0; } else fill_length -= len; fstring_free(fstr); } if (center) { fill_before = fill_after = 1; center = fill_length & 1; fill_length /= 2; } if (fill_before) for (i = 0; i < fill_length+center; i++) string_append_c(buf, fill_char); string_append(buf, str); if (fill_after) for (i = 0; i < fill_length; i++) string_append_c(buf, fill_char); if (need_free) xfree(str); } } else if ((*p=='/') && (p[1] == '|')) { /* /| 'set margin' */ if ((p == format) || (p[-1]!='/')) string_append(buf, "\033[0000m"); /* najgupsze, ale to nie jest moje ostatnie sowo */ else string_append_c(buf, '|'); p++; } else string_append_c(buf, *p); p++; } if (!dont_resolve && no_prompt_cache) theme_cache_reset(); return string_free(buf, 0); } /** * fstring_dup() * * Return a duplicated copy of a fstring_t with all internal data duplicated. * * @param str - string * * @return A newly-allocated fstring_t. * * @note Please note that private data is not duplicated. */ fstring_t *fstring_dup(const fstring_t *str) { fstring_t *out = g_memdup(str, sizeof(fstring_t)); gsize len = strlen(str->str) + 1; out->str = g_memdup(str->str, len * sizeof(gchar)); out->attr = g_memdup(str->attr, len * sizeof(fstr_attr_t)); return out; } /** * fstring_new() * * Change formatted ansi string (@a str) to Nowy-i-Lepszy (tm) [New-and-Better]. * * @param str - string * * @sa format_string() - Function to format strings. * @sa fstring_free() - Function to free fstring_t. * * @return Allocated fstring_t. */ fstring_t *fstring_new(const char *str) { #define NPAR 16 /* ECMA-48 CSI have got max 16 params (NPAR) defined in */ fstring_t *res; char *tmpstr; fstr_attr_t attr = FSTR_NORMAL; int i, j, len = 0, isbold = 0; for (i = 0; str[i]; i++) { if (str[i] == 27) { int parcount = 0; if (str[i + 1] != '[') continue; i += 2; /* skip begining */ while ((str[i] == ';' && parcount++ < NPAR-1) || (str[i] >= '0' && str[i] <= '9')) /* find max NPAR-1 seq */ i++; if (str[i] == 'm') i++; /* skip 'm' */ i--; continue; } if (str[i] == 9) { len += (8 - (len % 8)); continue; } if (str[i] == 13) continue; len++; } res = xmalloc(sizeof(fstring_t)); res->str = tmpstr = xmalloc((len + 1) * sizeof(gchar)); res->attr = xmalloc((len + 1) * sizeof(fstr_attr_t)); res->margin_left = -1; /* res->prompt_len = 0; res->prompt_empty = 0; */ for (i = 0, j = 0; str[i]; i++) { if ((str[i] == 27) && (str[i+1] == '(')) { i += 2; if (str[i] == '0') { attr |= FSTR_ALTCHARSET; continue; } else if (str[i] == 'B') { attr &= ~FSTR_ALTCHARSET; continue; } } else if (str[i] == 27) { /* ESC- */ unsigned short par[NPAR] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; unsigned short parlen[NPAR] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; int npar = 0; if (str[i + 1] != ('[')) continue; i += 2; /* parse ECMA-48 CSI here & build data */ while (1) { /* idea based from kernel sources */ char c = str[i++]; if (c == ';' && npar < NPAR -1) { /* next param */ npar++; continue; } else if (c >= '0' && c <= '9') { /* code */ par[npar] *= 10; /* multiply current */ par[npar] += c - '0'; /* add current */ parlen[npar]++; /* some old code must know sequence length... stupid */ continue; } else { /* params done */ break; } } i--; if (str[i] == 'm') { /* parse sequence to internal attr we only parse seq like \033[...m */ int k; for (k=0; k <= npar; k++) { unsigned short cur = par[k]; switch (cur) { case 0: /* RESET */ attr = FSTR_NORMAL; isbold = 0; if (parlen[k] == 4) /* /| set margin */ res->margin_left = j; else { if (parlen[k] >= 2) res->prompt_len = j; if (parlen[k] == 3) res->prompt_empty = 1; } break; case 1: /* BOLD */ if (k == npar && !isbold) /* if (*p == ('m') && !isbold) */ attr ^= FSTR_BOLD; else { /* if (*p == (';')) */ attr |= FSTR_BOLD; isbold = 1; } break; case 2: attr &= (FSTR_BACKMASK); isbold = 0; break; case 4: attr ^= FSTR_UNDERLINE; break; /* UNDERLINE */ case 5: attr ^= FSTR_BLINK; break; /* BLINK */ case 7: attr ^= FSTR_REVERSE; break; /* REVERSE */ } if (cur >= 30 && cur <= 37) { attr &= ~(FSTR_NORMAL+FSTR_FOREMASK); attr |= (cur - 30); } if (cur >= 40 && cur <= 47) { attr &= ~(FSTR_NORMAL+FSTR_BACKMASK); attr |= (cur - 40) << 3; } } } // else debug("Invalid/unsupported by ekg2 ECMA-48 CSI seq? (npar: %d)\n", npar); /* sequence not ended with m */ continue; } if (str[i] == 13) continue; if (str[i] == 9) { int k = 0, l = 8 - (j % 8); for (k = 0; k < l; j++, k++) { tmpstr[j] = (' '); res->attr[j] = attr; } continue; } tmpstr[j] = str[i]; res->attr[j] = attr; j++; } /* tmpstr[j] = (char) 0; res->attr[j] = 0; */ return res; } /** * fstring_new_format() * * char *tmp = format_string("format", .....); * fstr = fstring_new(tmp); * xfree(tmp); */ fstring_t *fstring_new_format(const char *format, ...) { fstring_t *fstr; va_list ap; char *tmp; va_start(ap, format); tmp = va_format_string(format, ap); fstr = fstring_new(tmp); xfree(tmp); return fstr; } /** * fstring_free() * * Free memory allocated by @a str * * @todo XXX Think about freeing str->priv_data * * @param str - fstring_t * to free. */ void fstring_free(fstring_t *str) { if (!str) return; xfree(str->str); xfree(str->attr); xfree(str->priv_data); xfree(str); } /* * format_string() * * j.w. tyle e nie potrzeba dawa mu va_list, a wystarcz zwyke parametry. * * - format... - j.w., */ char *format_string(const char *format, ...) { va_list ap; char *tmp; va_start(ap, format); tmp = va_format_string(format, ap); va_end(ap); return tmp; } /** * print_window_c() * * Mind abbreviation ;) print_window_c() -> print_window_common()
* Common stuff for: print_window() and print_window_w()
* Change w->act if needed, format theme, make fstring() and send everything to window_print() * * @note We only check if @a w == NULL, if you send here wrong window_t ptr, everything can happen. don't do it. * Yeah, we can do here window_find_ptr() but it'll slow down code a lot. */ static void print_window_c(window_t *w, int activity, const char *theme, va_list ap) { char *stmp; /* w here shouldn't here be NULL. In case. */ if (!w) { /* debug_fatal("print_window_common() w == NULL, theme: %s ap: 0x%x\n", w, theme, ap); */ return; } /* Change w->act */ if (w != window_current && !w->floating) { if (activity > w->act) { w->act = activity; /* emit UI_WINDOW_ACT_CHANGED only when w->act changed */ query_emit(NULL, "ui-window-act-changed", &w); } } stmp = va_format_string(format_find(theme), ap); { char *prompt = NULL; char *line; char *tmp = stmp; while ((line = split_line(&tmp))) { char *p; fstring_t *l; if ((p = xstrstr(line, "\033[00m"))) { xfree(prompt); if (p != line) prompt = xstrndup(line, (int) (p - line)); else prompt = NULL; line = p; } if (prompt) { gchar *tmp = g_strdup_printf("%s%s", prompt, line); l = fstring_new(tmp); g_free(tmp); } else l = fstring_new(line); window_print(w, l); fstring_free(l); } xfree(prompt); } xfree(stmp); } /** * print_window_find() * * Find given window [based on @a target and @a session] * if not found, and separate set, create new one. * * Print given text in given window [@a target+ @a session] * * @todo We have no policy for displaying messages by e.g. jabber resources.
* For now we do: [only jabber]
* - If @a target has '/' inside. We put there NUL char.
* - After it we search for window with stripped '/'
* - If founded, than done.
* If not, we look for user in userlist.. And if not found, than we'll create new window with stripped '/' [only jabber] * * * @param target - target to look for. * @param session - session to look for. * @param separate - if essence of text is important to create new window */ static window_t *print_window_find(const char *target, session_t *session, int separate) { char *newtarget = NULL; const char *tmp; userlist_t *u; window_t *w; /* first of all, let's check if config_display_crap is unset and target is current window */ if (!config_display_crap) { /* it was with && (config_make_window & 3) */ if (!target || !xstrcmp(target, "__current")) return window_status; } /* 1) let's check if we have such window as target... */ /* if it's jabber and we've got '/' in target strip it. [XXX, window resources] */ if ( ( (!xstrncmp(target, "tlen:", 5)) || (!xstrncmp(target, "xmpp:", 5)) ) && (tmp = xstrchr(target, '/'))) { newtarget = xstrndup(target, tmp - target); w = window_find_s(session, newtarget); /* and search for windows with stripped '/' */ /* even if w == NULL here, we use newtarget to create window without resource */ /* Yeah, we search for target on userlist, but u can be NULL also... */ /* XXX, optimize and think about it */ } else w = window_find_s(session, target); if (w) { xfree(newtarget); return w; } /* 2) if message is not important (not @a seperate) or we don't want create new windows at all [config_make_window & 3 == 0] * than get __status window */ if (!separate || (config_make_window & 3) == 0) { xfree(newtarget); return window_status; } /* if we have no window, let's find for it in userlist */ u = userlist_find(session, target); /* if found, and we have nickname, than great! */ if (u && u->nickname) target = u->nickname; /* use nickname instead of target */ else if (u && u->uid && ( /* don't use u->uid, if it has resource attached */ (xstrncmp(u->uid, "tlen:", 5) && xstrncmp(u->uid, "xmpp:", 5)) || !xstrchr(u->uid, '/'))) target = u->uid; /* use uid instead of target. XXX here. think about jabber resources */ else if (newtarget) target = newtarget; /* use target with stripped '/' */ /* XXX, window resources */ /* 3) if we don't have window here, and if ((config_make_window & 3) == 1) [unused], than we should find empty window. */ if ((config_make_window & 3) == 1) { window_t *wa; for (wa = windows; wa; wa = wa->next) { if (!wa->target && wa->id > 1) { w = wa; w->target = xstrdup(target); w->session = session; query_emit(NULL, "ui-window-target-changed", &w); /* XXX */ break; } if (w) /* wtf? */ break; } } /* 4) if not found unused window, or ((config_make_window & 3) == 2) [always] than just create it */ if (!w) w = window_new(target, session, 0); /* [FOR 3) and 4)] If we create window or we change target. notify user */ print("window_id_query_started", ekg_itoa(w->id), target, session_name(session)); print_window_w(w, EKG_WINACT_JUNK, "query_started", target, session_name(session)); print_window_w(w, EKG_WINACT_JUNK, "query_started_window", target); xfree(newtarget); return w; } /** * print_window() * * Print given text in given window [@a target+ @a session] * * @todo We have no policy for displaying messages by e.g. jabber resources.
* For now we do: [only jabber]
* - If @a target has '/' inside. We put there NUL char.
* - After it we search for window with stripped '/'
* - If founded, than done.
* If not, we look for user in userlist.. And if not found, than we'll create new window with stripped '/' [only jabber] * * * @param target - target to look for. * @param session - session to look for. * @param activity - how important is text? * @param separate - if essence of text is important to create new window * @param theme - Name of format to format_string() with @a ... Text will be be built. * @param ... */ void print_window(const char *target, session_t *session, int activity, int separate, const char *theme, ...) { window_t *w; va_list ap; w = print_window_find(target, session, separate); va_start(ap, theme); print_window_c(w, activity, theme, ap); va_end(ap); } void print_info(const char *target, session_t *session, const char *theme, ...) { window_t *w; va_list ap; /* info configuration goes here... */ w = print_window_find(target, session, 0); va_start(ap, theme); print_window_c(w, EKG_WINACT_JUNK, theme, ap); va_end(ap); } void print_warning(const char *target, session_t *session, const char *theme, ...) { window_t *w; va_list ap; /* warning configuration goes here... */ w = print_window_find(target, session, 0); va_start(ap, theme); print_window_c(w, EKG_WINACT_JUNK, theme, ap); va_end(ap); } /** * print_window_w() * * Like print_window() but it takes window_t struct instead of target+session. * * @note The same as in print_window_c(): we don't check if @a w is valid window ptr. * Just be careful. If you are not sure call:
* print_window_c(window_find_ptr(w), separate, theme, ...) * And in worst case text will be displayed in (__status / or __current) window instead of a usual one.. But ekg2 won't crash. * * @param w - window to display,
* if NULL than __status or __current will be used. it depends on: config_default_status_window and config_display_crap variables. */ void print_window_w(window_t *w, int activity, const char *theme, ...) { va_list ap; if (!w) { /* if no window passed, then get window based of */ if (config_default_status_window || !config_display_crap) w = window_status; else w = ((window_current != window_debug) ? window_current : window_status); /* don't print in __debug window, print in __status one */ } va_start(ap, theme); print_window_c(w, activity, theme, ap); va_end(ap); } /** * theme_cache_reset() * * Remove cached: @a prompt_cache, @a prompt2_cache, @a error_cache and @a timestamp_cache
* These values are used by va_format_string() to don't call format_find() on:
* ["prompt" "%>", "prompt2" "%)", "errror" "%!", "timestamp" "%#"] * */ void theme_cache_reset() { xfree(prompt_cache); xfree(prompt2_cache); xfree(error_cache); prompt_cache = prompt2_cache = error_cache = NULL; timestamp_cache = NULL; } /** * format_add() * * Add format with @a name and @a value.
* If @a replace set to 1, than if format with the same name exists. than format value will be replaced. * * @todo What about creating global variable: formats_unique_here.. and if set to 1, don't search if this format already exists? It should speedup theme_init() a little. * * @param name - name of format * @param value - value of format * @param replace - if this format exists and is set to 1 than format value will be replaced with new one. else do nothing. */ void format_add(const char *name, const char *value, int replace) { struct format *fl; struct format *f; int hash; if (!name || !value) return; hash = gim_hash(name); if (hash == no_prompt_cache_hash && !xstrcmp(name, "no_prompt_cache")) { no_prompt_cache = 1; return; } for (fl = formats[hash & 0xff]; fl; fl = fl->next) { struct format *f = fl; if (hash == f->name_hash && !xstrcmp(name, f->name)) { if (replace) { xfree(f->value); f->value = xstrdup(value); } return; } } f = xmalloc(sizeof(struct format)); f->name = xstrdup(name); f->name_hash = hash; f->value = xstrdup(value); formats_add(&(formats[hash & 0xff]), f); return; } /** * format_remove() * * Remove format with given name. * * @todo We can speedup it a little, by passing to list_remove() ptr to previous item.. senseless? * * @param name * * @return 0 if format was founded and removed from list
* -1 else */ static int format_remove(const char *name) { struct format *fl; int hash; if (!name) return -1; hash = gim_hash(name); for (fl = formats[hash & 0xff]; fl; fl = fl->next) { struct format *f = fl; if (hash == f->name_hash && !xstrcmp(f->name, name)) { (void) formats_removei(&(formats[hash & 0xff]), f); return 0; } } return -1; } /* * theme_open() // funkcja wewntrzna * * prbuje otworzy plik, jeli jeszcze nie jest otwarty. * * - prevfd - deskryptor z poprzedniego wywoania, * - prefix - cieka, * - filename - nazwa pliku. */ static FILE *theme_open(const char *prefix, const char *filename) { char buf[PATH_MAX]; int save_errno; FILE *f; if (prefix) snprintf(buf, sizeof(buf), "%s/%s", prefix, filename); else snprintf(buf, sizeof(buf), "%s", filename); if ((f = fopen(buf, "r"))) return f; if (prefix) snprintf(buf, sizeof(buf), "%s/%s.theme", prefix, filename); else snprintf(buf, sizeof(buf), "%s.theme", filename); save_errno = errno; if ((f = fopen(buf, "r"))) return f; if (errno == ENOENT) errno = save_errno; return NULL; } /* * theme_read() * * wczytuje opis wygldu z podanego pliku. * * - filename - nazwa pliku z opisem, * - replace - czy zastpowa istniejce wpisy. * * zwraca 0 jeli wszystko w porzdku, -1 w przypadku bdu. */ int theme_read(const char *filename, int replace) { char *buf; FILE *f = NULL; if (!xstrlen(filename)) { /* XXX, DEFAULT_THEME <-> default.theme ? */ filename = prepare_path("default.theme", 0); if (!filename || !(f = fopen(filename, "r"))) return -1; } else { char *fn = xstrdup(filename), *tmp; if ((tmp = xstrchr(fn, ','))) *tmp = 0; errno = ENOENT; f = NULL; if (!xstrchr(fn, '/')) { if (!f) f = theme_open(prepare_path("", 0), fn); if (!f) f = theme_open(prepare_path("themes", 0), fn); if (!f) f = theme_open(DATADIR "/themes", fn); } else f = theme_open(NULL, fn); xfree(fn); if (!f) return -1; } if (!in_autoexec) { theme_free(); theme_init(); } /* ui_event("theme_init"); */ while ((buf = read_file(f, 0))) { char *value; if (buf[0] == '-') format_remove(buf + 1); else if (buf[0] == '#') ; else if (!(value = xstrchr(buf, ' '))) ; else { char *p; *value++ = 0; for (p = value; *p; p++) { if (*p == '\\') { if (!*(p + 1)) break; if (*(p + 1) == 'n') *p = '\n'; memmove(p + 1, p + 2, xstrlen(p + 1)); } } format_add(buf, value, replace); } } fclose(f); theme_cache_reset(); return 0; } int theme_write(const char *filename) { FILE *f; int i; if (!filename) return -1; if (!(f = fopen(filename, "w"))) return -1; for (i = 0; i < 0x100; i++) { struct format *ff; for (ff = formats[i]; ff; ff = ff->next) { char *escaped; escaped = escape(ff->value); fprintf(f, "%s %s\n", ff->name, escaped); xfree(escaped); } } fclose(f); return 0; } void theme_enumerate(int (*enumerator)(const char *theme, const char *value)) { int i; if (!enumerator) return; if (no_prompt_cache) { if (!enumerator("no_prompt_cache", "")) return; } for (i = 0; i < 0x100; i++) { struct format *ff; for (ff = formats[i]; ff; ff = ff->next) { if (!enumerator(ff->name, ff->value)) return; } } } /* * theme_free() * * usuwa formatki z pamici. */ void theme_free() { int i; for (i = 0; i < 0x100; i++) formats_destroy(&(formats[i])); no_prompt_cache = 0; theme_cache_reset(); } void theme_plugins_init() { GSList *pl; for (pl = plugins; pl; pl = pl->next) { const plugin_t *p = pl->data; if (p->theme_init) p->theme_init(); } } static const char *theme_init_contact_helper(const char *theme, char color) { static char tmp[30]; int i; for (i = 0; theme[i]; i++) tmp[i] = (theme[i] == '$') ? color : theme[i]; tmp[i] = '\0'; return tmp; } static void theme_init_contact_status(const char *status, char color, int want_quick_list) { char tmp[100]; #define FORMAT_ADD(format, value, replace) sprintf(tmp, format, status); format_add(tmp, value, replace) #define FORMAT_ADDF(format, fvalue, replace) FORMAT_ADD(format, theme_init_contact_helper(fvalue, color), replace); FORMAT_ADD("contacts_%s_header", "", 1); /* standard */ FORMAT_ADDF("contacts_%s", " %$%1%n", 1); FORMAT_ADDF("contacts_%s_descr", "%Ki%$%1%n", 1); FORMAT_ADDF("contacts_%s_descr_full", "%Ki%$%1%n %2", 1); /* blink */ FORMAT_ADDF("contacts_%s_blink", " %$%i%1%n", 1); FORMAT_ADDF("contacts_%s_descr_blink", "%K%ii%$%i%1%n", 1); FORMAT_ADDF("contacts_%s_descr_full_blink", "%K%ii%$%i%1%n %2", 1); /* typing */ FORMAT_ADDF("contacts_%s_typing", "%W*%$%1%n", 1); FORMAT_ADDF("contacts_%s_descr_typing", "%W*%$%1%n", 1); FORMAT_ADDF("contacts_%s_descr_full_typing", "%W*%$%1%n %2", 1); /* typing+blink */ FORMAT_ADDF("contacts_%s_blink_typing", "%W%i*%$%i%1%n", 1); FORMAT_ADDF("contacts_%s_descr_blink_typing", "%W%i*%$%i%1%n", 1); FORMAT_ADDF("contacts_%s_descr_full_blink_typing", "%W%i*%$%i%1%n %2", 1); FORMAT_ADD("contacts_%s_footer", "", 1); if (want_quick_list) { /* my macros are evil, so don't remove brackets! */ FORMAT_ADDF("quick_list_%s", " %$%1%n", 1); } #undef FORMAT_ADD #undef FORMAT_ADDF } /* * theme_init() * * ustawia domylne wartoci formatek. */ void theme_init() { theme_cache_reset(); no_prompt_cache_hash = gim_hash("no_prompt_cache"); #ifndef NO_DEFAULT_THEME /* wykorzystywane w innych formatach */ format_add("prompt", "%K:%g:%G:%n", 1); format_add("prompt,speech", " ", 1); format_add("prompt2", "%K:%c:%C:%n", 1); format_add("prompt2,speech", " ", 1); format_add("error", "%K:%r:%R:%n", 1); format_add("error,speech", "Error!", 1); format_add("timestamp", "%T", 1); format_add("timestamp,speech", " ", 1); /* ui-password-input */ format_add("password_input", _("Please input password:"), 1); format_add("password_repeat", _("Please repeat password:"), 1); format_add("password_empty", _("%! No password entered"), 1); format_add("password_nomatch", _("%! Entered passwords do not match"), 1); format_add("password_nosupport", _("%! %|UI-plugin doesn't seem to support password input, please use command-line input."), 1); format_add("session_password_input", _("Password for %1:"), 1); /* dla funkcji format_user() */ format_add("known_user", "%T%1%n/%2", 1); format_add("known_user,speech", "%1", 1); format_add("unknown_user", "%T%1%n", 1); /* czsto wykorzystywane, rne, przydatne itd. */ format_add("none", "%1\n", 1); format_add("generic", "%> %1\n", 1); format_add("generic_bold", "%> %T%1%n\n", 1); format_add("generic2", "%) %1\n", 1); format_add("generic2_bold", "%) %T%1%n\n", 1); format_add("generic_error", "%! %1\n", 1); /* debug */ format_add("debug", "%n%1\n", 1); format_add("fdebug", "%B%1\n", 1); format_add("iodebug", "%y%1\n", 1); format_add("iorecvdebug", "%Y%1\n", 1); format_add("edebug", "%R%1\n", 1); format_add("wdebug", "%W%1\n", 1); format_add("warndebug", "%r%1\n", 1); format_add("okdebug", "%G%1\n", 1); format_add("ekg_failure", _("%! %|Something really unexpected happened, you should %Treally%n contact authors!\nEKG2 may now behave fine, or more failures could occur.\nDetails follow (see also __debug):\n%R%1%n"), 1); format_add("value_none", _("(none)"), 1); format_add("not_enough_params", _("%! Too few parameters. Try %Thelp %1%n\n"), 1); format_add("invalid_params", _("%! Invalid parameter %T%2%n. Try %Thelp %1%n\n"), 1); format_add("var_not_set", _("%! Required variable %T%2%n by %T%1%n is unset\n"), 1); format_add("invalid_uid", _("%! Invalid user id\n"), 1); format_add("invalid_session", _("%! Invalid session\n"), 1); format_add("invalid_nick", _("%! Invalid username\n"), 1); format_add("user_not_found", _("%! User %T%1%n not found\n"), 1); format_add("not_implemented", _("%! This function isn't ready yet\n"), 1); format_add("unknown_command", _("%! Unknown command: %T%1%n\n"), 1); format_add("welcome", _("%> %Tekg2-%1%n (%ge%Gk%gg %Gr%ge%Gl%go%Ga%gd%Ge%gd%n)\n%> Software licensed on GPL v2 terms\n\n"), 1); format_add("welcome,speech", _("welcome in e k g 2."), 1); format_add("ekg_version", _("%) %Tekg2-%1%n (compiled %2)\n"), 1); format_add("secure", _("%Y(encrypted)%n"), 1); format_add("day_changed", _("%) Day changed to: %W%1"), 1); /* add, del */ format_add("user_added", _("%> (%2) Added %T%1%n to roster\n"), 1); format_add("user_deleted", _("%) (%2) Removed %T%1%n from roster\n"), 1); format_add("user_cleared_list", _("%) (%1) Roster cleared\n"), 1); format_add("user_exists", _("%! (%2) %T%1%n already in roster\n"), 1); format_add("user_exists_other", _("%! (%3) %T%1%n already in roster as %2\n"), 1); /* zmiany stanu */ format_add("away", _("%> (%1) Status changed to %Gaway%n\n"), 1); format_add("away_descr", _("%> (%3) Status changed to %Gaway%n: %T%1%n%2\n"), 1); format_add("back", _("%> (%1) Status changed to %Yavailable%n\n"), 1); format_add("back_descr", _("%> (%3) Status changed to %Yavailable%n: %T%1%n%2%n\n"), 1); format_add("invisible", _("%> (%1) Status changed to %cinvisible%n\n"), 1); format_add("invisible_descr", _("%> (%3) Status changed to %cinvisible%n: %T%1%n%2\n"), 1); format_add("dnd", _("%> (%1) Status changed to %Bdo not disturb%n\n"), 1); format_add("dnd_descr", _("%> (%3) Status changed to %Bdo not disturb%n: %T%1%n%2\n"), 1); format_add("gone", _("%> (%1) Status changed to %Rgone%n\n"), 1); format_add("gone_descr", _("%> (%3) Status changed to %Rgone%n: %T%1%n%2%n%n\n"), 1); format_add("ffc", _("%> (%1) Status changed to %Wfree for chat%n\n"), 1); format_add("ffc_descr", _("%> (%3) Status changed to %Wfree for chat%n: %T%1%n%2%n\n"), 1); format_add("xa", _("%> (%1) Status changed to %gextended away%n\n"), 1); format_add("xa_descr", _("%> (%3) Status changed to %gextended away%n: %T%1%n%2%n%n\n"), 1); format_add("private_mode_is_on", _("% (%1) Friends only mode is on\n"), 1); format_add("private_mode_is_off", _("%> (%1) Friends only mode is off\n"), 1); format_add("private_mode_on", _("%) (%1) Turned on ,,friends only'' mode\n"), 1); format_add("private_mode_off", _("%> (%1) Turned off ,,friends only'' mode\n"), 1); format_add("private_mode_invalid", _("%! Invalid value'\n"), 1); format_add("descr_too_long", _("%! Description longer than maximum %T%1%n characters\nDescr: %B%2%b%3%n\n"), 1); format_add("auto_away", _("%> (%1) Auto %Gaway%n\n"), 1); format_add("auto_away_descr", _("%> (%3) Auto %Gaway%n: %T%1%n%2%n\n"), 1); format_add("auto_xa", _("%> (%1) Auto %gextended away%n\n"), 1); format_add("auto_xa_descr", _("%> (%3) Auto %gextended away%n: %T%1%n%2%n\n"), 1); format_add("auto_back", _("%> (%1) Auto back%n\n"), 1); format_add("auto_back_descr", _("%> (%3) Auto back: %T%1%n%2%n\n"), 1); /* pomoc */ format_add("help", "%> %T%1%n %2 - %3\n", 1); format_add("help_no_params", "%> %T%1%n - %2\n", 1); format_add("help_more", "%) %|%1\n", 1); format_add("help_alias", _("%) %T%1%n is an alias and don't have description\n"), 1); format_add("help_footer", _("\n%> %|%Thelp %n will show more details about command. Prepending %T^%n to command name will hide it's result. Instead of one can use %T$%n, which means current query user.\n\n"), 1); format_add("help_quick", _("%> %|Before using consult the brochure. File %Tdocs/ULOTKA.en%n is a short guide on included documentation. If you don't have it, you can visit %Thttp://www.ekg2.org/%n\n"), 1); format_add("help_set_file_not_found", _("%! Can't find variables descriptions (incomplete installation)\n"), 1); format_add("help_set_file_not_found_plugin", _("%! Can't find variables descriptions for %T%1%n plugin (incomplete installation)\n"), 1); format_add("help_set_var_not_found", _("%! Cant find description of %T%1%n variable\n"), 1); format_add("help_set_header", _("%> %T%1%n (%2, default value: %3)\n%>\n"), 1); format_add("help_set_body", "%> %|%1\n", 1); format_add("help_set_footer", "", 1); format_add("help_command_body", "%> %|%1\n", 1); format_add("help_command_file_not_found", _("%! Can't find commands descriptions (incomplete installation)\n"), 1); format_add("help_command_file_not_found_plugin", _("%! Can't find commands descriptions for %T%1%n plugin (incomplete installation)\n"), 1); format_add("help_command_not_found", _("%! Can't find command description: %T%1%n\n"), 1); format_add("help_script", _("%) %T%1%n is an script command and don't have description. Try /%1 help\n"), 1); format_add("help_session_body", "%> %|%1\n", 1); format_add("help_session_file_not_found", _("%! Can't find variables descriptions for %T%1%n plugin (incomplete installation)\n"), 1); format_add("help_session_var_not_found", _("%! Cant find description of %T%1%n variable\n"), 1); format_add("help_session_header", _("%> %1->%T%2%n (%3, default value: %4)\n%>\n"), 1); format_add("help_session_footer", "", 1); /* ignore, unignore, block, unblock */ format_add("ignored_added", _("%> Ignoring %T%1%n\n"), 1); format_add("ignored_modified", _("%> Modified ignore level of %T%1%n\n"), 1); format_add("ignored_deleted", _("%) Unignored %1\n"), 1); format_add("ignored_deleted_all", _("%) Ignore list cleared up\n"), 1); format_add("ignored_exist", _("%! %1 already beeing ignored\n"), 1); format_add("ignored_list", "%> %1 %2\n", 1); format_add("ignored_list_empty", _("%! Ignore list ist empty\n"), 1); format_add("error_not_ignored", _("%! %1 is not beeing ignored\n"), 1); format_add("blocked_added", _("%> Blocking %T%1%n\n"), 1); format_add("blocked_deleted", _("%) Unblocking %1\n"), 1); format_add("blocked_deleted_all", _("%) Block list cleared up\n"), 1); format_add("blocked_exist", _("%! %1 already beeing blocked\n"), 1); format_add("blocked_list", "%> %1\n", 1); format_add("blocked_list_empty", _("%! Block list is empty\n"), 1); format_add("error_not_blocked", _("%! %1 is not beeing blocked\n"), 1); /* contact list */ format_add("list_empty", _("%! Roster is empty\n"), 1); format_add("list_avail", _("%> %1 %Y(available)%n %b%3:%4%n\n"), 1); format_add("list_avail_descr", _("%> %1 %Y(available: %n%5%Y)%n %b%3:%4%n\n"), 1); format_add("list_away", _("%> %1 %G(away)%n %b%3:%4%n\n"), 1); format_add("list_away_descr", _("%> %1 %G(away: %n%5%G)%n %b%3:%4%n\n"), 1); format_add("list_dnd", _("%> %1 %B(do not disturb)%n %b%3:%4%n\n"), 1); format_add("list_dnd_descr", _("%> %1 %B(do not disturb:%n %5%G)%n %b%3:%4%n\n"), 1); format_add("list_chat", _("%> %1 %W(free for chat)%n %b%3:%4%n\n"), 1); format_add("list_chat_descr", _("%> %1 %W(free for chat%n: %5%W)%n %b%3:%4%n\n"), 1); format_add("list_error", _("%> %1 %m(error) %b%3:%4%n\n"), 1); format_add("list_error", _("%> %1 %m(error%n: %5%m)%n %b%3:%4%n\n"), 1); format_add("list_xa", _("%> %1 %g(extended away)%n %b%3:%4%n\n"), 1); format_add("list_xa_descr", _("%> %1 %g(extended away: %n%5%g)%n %b%3:%4%n\n"), 1); format_add("list_gone", _("%> %1 %R(gone)%n %b%3:%4%n\n"), 1); format_add("list_gone_descr", _("%> %1 %R(gone: %n%5%g)%n %b%3:%4%n\n"), 1); format_add("list_notavail", _("%> %1 %r(offline)%n\n"), 1); format_add("list_notavail_descr", _("%> %1 %r(offline: %n%5%r)%n\n"), 1); format_add("list_invisible", _("%> %1 %c(invisible)%n %b%3:%4%n\n"), 1); format_add("list_invisible_descr", _("%> %1 %c(invisible: %n%5%c)%n %b%3:%4%n\n"), 1); format_add("list_blocking", _("%> %1 %m(blocking)%n\n"), 1); format_add("list_unknown", "%> %1\n", 1); format_add("modify_offline", _("%> %1 will not see your status\n"), 1); format_add("modify_online", _("%> %1 will see your status\n"), 1); format_add("modify_done", _("%> Modified item in roster\n"), 1); /* lista kontaktw z boku ekranu */ format_add("contacts_header", "", 1); format_add("contacts_header_group", "%K %1%n", 1); format_add("contacts_metacontacts_header", "", 1); theme_init_contact_status("avail", 'Y', 1); theme_init_contact_status("away", 'G', 1); theme_init_contact_status("dnd", 'B', 1); theme_init_contact_status("chat", 'W', 1); theme_init_contact_status("error", 'm', 0); theme_init_contact_status("xa", 'g', 1); theme_init_contact_status("gone", 'R', 1); theme_init_contact_status("notavail", 'r', 0); theme_init_contact_status("invisible", 'c', 1); format_add("contacts_blocking_header", "", 1); format_add("contacts_blocking", " %m%1%n", 1); format_add("contacts_blocking_footer", "", 1); theme_init_contact_status("unknown", 'M', 0); format_add("contacts_footer", "", 1); format_add("contacts_footer_group", "", 1); format_add("contacts_metacontacts_footer", "", 1); format_add("contacts_vertical_line_char", "|", 1); format_add("contacts_horizontal_line_char", "-", 1); /* we are saying goodbye and we are saving configuration */ format_add("quit", _("%> Bye\n"), 1); format_add("quit_descr", _("%> Bye: %T%1%n%2\n"), 1); format_add("config_changed", _("Save new configuration ? (t-yes/n-no) "), 1); format_add("config_must_reconnect", _("%) You must reconnect for the changes to take effect\n"), 2); format_add("quit_keep_reason", _("You've set keep_reason to save status.\nDo you want to save current description to file (it will be restored upon next EKG exec)? (t-yes/n-no) "), 1); format_add("saved", _("%> Configuration saved\n"), 1); format_add("error_saving", _("%! There was some error during save\n"), 1); /* incoming messages */ format_add("message", "%g.-- %n%1 %c%2%n%6%n%g--- -- -%n\n%g|%n %|%3%n\n%|%g`----- ---- --- -- -%n\n", 1); format_add("message_timestamp", "(%Y-%m-%d %H:%M) ", 1); format_add("message_timestamp_today", "(%H:%M) ", 1); format_add("message_timestamp_now", "", 1); format_add("message,speech", _("message from %1: %3."), 1); format_add("empty", "%3\n", 1); format_add("conference", "%g.-- %n%1 %c%2%n%6%n%g--- -- -%n\n%g|%n %|%3%n\n%|%g`----- ---- --- -- -%n\n", 1); format_add("conference_timestamp", "(%Y-%m-%d %H:%M) ", 1); format_add("conference_timestamp_today", "(%H:%M) ", 1); format_add("conference_timestamp_now", "", 1); format_add("confrence,speech", _("message from %1: %3."), 1); format_add("chat", "%c.-- %n%1 %c%2%n%6%n%c--- -- -%n\n%c|%n %|%3%n\n%|%c`----- ---- --- -- -%n\n", 1); format_add("chat_me", "%) %|%C%4%n %3\n", 1); format_add("chat_timestamp", "(%Y-%m-%d %H:%M) ", 1); format_add("chat_timestamp_today", "(%H:%M) ", 1); format_add("chat_timestamp_now", "", 1); format_add("chat,speech", _("message from %1: %3."), 1); format_add("sent", "%b.-- %n%1 %c%2%n%6%n%b--- -- -%n\n%b|%n %|%3%n\n%|%b`----- ---- --- -- -%n\n", 1); format_add("sent_me", "%> %|%G%4%n %3\n", 1); format_add("sent_timestamp", "(%Y-%m-%d %H:%M) ", 1); format_add("sent_timestamp_today", "(%H:%M) ", 1); format_add("sent_timestamp_now", "", 1); format_add("sent,speech", "", 1); /* XXX: change default colors for these two, but dunno to what * they should be darker than standard ones to emphasize that * these are jest old messages loaded from the db, not received */ format_add("log", "%c.-- %n%1 %c%2%n%6%n%c--- -- -%n\n%c|%n %|%3%n\n%|%c`----- ---- --- -- -%n\n", 1); format_add("log_timestamp", "(%Y-%m-%d %H:%M) ", 1); format_add("log_timestamp_today", "(%H:%M) ", 1); format_add("log_timestamp_now", "", 1); format_add("log,speech", _("message from %1: %3."), 1); format_add("sent_log", "%b.-- %n%1 %c%2%n%6%n%b--- -- -%n\n%b|%n %|%3%n\n%|%b`----- ---- --- -- -%n\n", 1); format_add("sent_log_timestamp", "(%Y-%m-%d %H:%M) ", 1); format_add("sent_log_timestamp_today", "(%H:%M) ", 1); format_add("sent_log_timestamp_now", "", 1); format_add("sent_log,speech", "", 1); format_add("system", _("%m.-- %TSystem message%m --- -- -%n\n%m|%n %|%3%n\n%|%m`----- ---- --- -- -%n\n"), 1); format_add("system,speech", _("system message: %3."), 1); /* acks of messages */ format_add("ack_queued", _("%> Message to %1 will be delivered later\n"), 1); format_add("ack_delivered", _("%> Message to %1 delivered\n"), 1); format_add("ack_unknown", _("%> Not clear what happened to message to %1\n"), 1); format_add("ack_tempfail", _("%! %|Message to %1 encountered temporary delivery failure (e.g. message queue full). Please try again later.\n"), 1); format_add("ack_filtered", _("%! %|Message to %1 encountered permament delivery failure (e.g. forbidden content). Before retrying, try to fix the problem yourself (e.g. ask second side to add us to userlist).\n"), 1); format_add("message_too_long", _("%! Message was too long and got shortened\n"), 1); /* people are changing their statuses */ format_add("status_avail", _("%> (%3) %1 is %Yavailable%n\n"), 1); format_add("status_avail_descr", _("%> (%3) %1 is %Yavailable%n: %T%4%n\n"), 1); format_add("status_away", _("%> (%3) %1 is %Gaway%n\n"), 1); format_add("status_away_descr", _("%> (%3) %1 is %Gaway%n: %T%4%n\n"), 1); format_add("status_notavail", _("%> (%3) %1 is %roffline%n\n"), 1); format_add("status_notavail_descr", _("%> (%3) %1 is %roffline%n: %T%4%n\n"), 1); format_add("status_invisible", _("%> (%3) %1 is %cinvisible%n\n"), 1); format_add("status_invisible_descr", _("%> (%3) %1 is %cinvisible%n: %T%4%n\n"), 1); format_add("status_xa", _("%> (%3) %1 is %gextended away%n\n"), 1); format_add("status_xa_descr", _("%> (%3) %1 is %gextended away%n: %T%4%n\n"), 1); format_add("status_gone", _("%> (%3) %1 is %Rgone%n\n"), 1); format_add("status_gone_descr", _("%> (%3) %1 is %Rgone%n: %T%4%n\n"), 1); format_add("status_dnd", _("%> (%3) %1 %Bdo not disturb%n\n"), 1); format_add("status_dnd_descr", _("%> (%3) %1 %Bdo not disturb%n: %T%4%n\n"), 1); format_add("status_error", _("%> (%3) %1 %merror fetching status%n\n"), 1); format_add("status_error_descr", _("%> (%3) %1 %merror fetching status%n: %T%4%n\n"), 1); format_add("status_chat", _("%> (%3) %1 is %Wfree for chat%n\n"), 1); format_add("status_chat_descr", _("%> (%3) %1 is %Wfree for chat%n: %T%4%n\n"), 1); format_add("status_unknown", _("%> (%3) %1 is %Munknown%n\n"), 1); format_add("status_unknown_descr", _("%> (%3) %1 is %Munknown%n: %T%4%n\n"), 1); /* connection with server */ format_add("connecting", _("%> (%1) Connecting to server %n\n"), 1); format_add("conn_failed", _("%! (%2) Connection failure: %1%n\n"), 1); format_add("conn_failed_resolving", _("Server not found"), 1); format_add("conn_failed_connecting", _("Can't connect to server"), 1); format_add("conn_failed_invalid", _("Invalid server response"), 1); format_add("conn_failed_disconnected", _("Server disconnected"), 1); format_add("conn_failed_password", _("Invalid password"), 1); format_add("conn_failed_404", _("HTTP server error"), 1); format_add("conn_failed_tls", _("Error negotiating TLS"), 1); format_add("conn_failed_memory", _("No memory"), 1); format_add("conn_stopped", _("%! (%1) Connection interrupted %n\n"), 1); format_add("conn_timeout", _("%! (%1) Connection timed out%n\n"), 1); format_add("connected", _("%> (%1) Connected%n\n"), 1); format_add("connected_descr", _("%> (%2) Connected: %T%1%n\n"), 1); format_add("disconnected", _("%> (%1) Disconnected%n\n"), 1); format_add("disconnected_descr", _("%> (%2) Disconnected: %T%1%n\n"), 1); format_add("already_connected", _("%! (%1) Already connected. Use %Treconnect%n to reconnect%n\n"), 1); format_add("during_connect", _("%! (%1) Connecting in progress. Use %Tdisconnect%n to abort%n\n"), 1); format_add("conn_broken", _("%! (%1) Connection broken: %2%n\n"), 1); format_add("conn_disconnected", _("%! (%1) Server disconnected%n\n"), 1); format_add("not_connected", _("%! (%1) Not connected.%n\n"), 1); format_add("not_connected_msg_queued", _("%! (%1) Not connected. Message will be delivered when connected.%n\n"), 1); format_add("wrong_id", _("%! (%1) Wrong session id.%n\n"), 1); format_add("inet_addr_failed", _("%! (%1) Invalid \"server\".%n\n"), 1); format_add("invalid_local_ip", _("%! (%1) Invalid local address. I'm clearing %Tlocal_ip%n session variable\n"), 1); format_add("auto_reconnect_removed", _("%! (%1) EKG2 won't try to connect anymore - use /connect.%n\n"), 1); /* obsuga motyww */ format_add("theme_loaded", "%> Loaded theme %T%1%n\n", 1); format_add("theme_default", "%> Default theme selected\n", 1); format_add("error_loading_theme", "%! Error loading theme: %1\n", 1); /* zmienne, konfiguracja */ format_add("variable", "%> %1 = %2\n", 1); format_add("variable_not_found", _("%! Unknown variable: %T%1%n\n"), 1); format_add("variable_no_match", _("%! No variable name matches %T%1%n\n"), 1); format_add("variable_invalid", _("%! Invalid session variable value\n"), 1); format_add("no_config", _("%! Incomplete configuration. Use:\n%! %Tsession -a %n\n%! %Tsession password %n\n%! %Tsave%n\n%! And then:\n%! %Tconnect%n\n%! If you don't have an account yet, use:\n%! %Tregister %n\n\n%> %|Query windows will be created automatically. To switch windows press %TAlt-number%n or %TEsc%n and then the number. To start a conversation use %Tquery%n. To add someone to your roster use %Tadd%n. All key shortcuts are described in %TREADME%n. There is also a %Thelp%n command. Remember about prefixes before UID, for example %Tgg:%n. \n\n"), 2); format_add("no_config,speech", _("incomplete configuration. enter session -a, and then gg: gg-number, or xmpp: jabber id, then session password and your password. enter save to save. enter connect to connect. if you do not have an account yet, enter register, space, e-mail and password. Query windows will be created automatically. To switch windows press Alt and window number or Escape and then the number. To start a conversation use query command. To add someone to your roster use add command. All key shortcuts are described in README file. There is also a help command."), 1); format_add("no_config_gg_not_loaded", _("%! Incomplete configuration. Use:\n%! %T/plugin +gg%n - to load gg plugin\n%! %Tsession -a %n\n%! %Tsession password %n\n%! %Tsave%n\n%! And then:\n%! %Tconnect%n\n%! If you don't have uid, use:\n%! %Tregister %n\n\n%> %|Query windows will be created automatically. To switch windows press %TAlt-number%n or %TEsc%n and then number. To start conversation use %Tquery%n. To add someone to roster use %Tadd%n. All key shortcuts are described in %TREADME%n. There is also %Thelp%n command. Remember about prefixes before UID, for example %Tgg:%n. \n\n"), 2); format_add("no_config_no_libgadu", _("%! Incomplete configuration. %TBIG FAT WARNING:%n\n%! %Tgg plugin has not been compiled, probably there is no libgadu library in the system\n%! Use:\n%! %Tsession -a %n\n%! %Tsession password %n\n%! %Tsave%n\n%! And then:\n%! %Tconnect%n\n%! If you don't have uid, use:\n%! %Tregister%n\n\n%> %|Query windows will be created automatically. To switch windows press %TAlt-number%n or %TEsc%n and then number. To start conversation use %Tquery%n. To add someone to roster use %Tadd%n. All key shortcuts are described in %TREADME%n. There is also %Thelp%n command. Remember about prefixes before UID, for example %Txmpp:%n. \n\n"), 2); format_add("error_reading_config", _("%! Error reading configuration file: %1\n"), 1); format_add("config_read_success", _("%> Configuratin read correctly.%n\n"), 1); format_add("config_line_incorrect", _("%! Invalid line '%T%1%n', skipping\n"), 1); format_add("config_error", _("%! %R%|%R%1\n"), 1); format_add("autosaved", _("%> Automatically saved settings\n"), 1); /* config_upgrade() */ format_add("config_upgrade_begin", _("%) EKG2 upgrade detected. The following changes were made since your previous version:\n"), 1); format_add("config_upgrade_important", "%) %W%2) %y*%n %1\n", 1); format_add("config_upgrade_major", "%) %W%2) %Y*%n %1\n", 1); format_add("config_upgrade_minor", "%) %W%2) %c*%n %1\n", 1); format_add("config_upgrade_end", _("%) To make configuration upgrade permament, please save your configuration: %c/save%n\n"), 1); /* rejestracja nowego numeru */ format_add("register", _("%> Registration successful. Your number: %T%1%n\n"), 1); format_add("register_change_passwd", _("%> Your password for account %T%1%n is '%T%2%n'. Change it as soon as possible, using command /passwd "), 1); format_add("register_failed", _("%! Error during registration: %1\n"), 1); format_add("register_pending", _("%! Registration in progress\n"), 1); format_add("register_timeout", _("%! Registration timed out\n"), 1); format_add("registered_today", _("%! Already registered. Do not abuse\n"), 1); /* deleting user's account from public catalog */ format_add("unregister", _("%> Account removed\n"), 1); format_add("unregister_timeout", _("%! Account removal timed out\n"), 1); format_add("unregister_bad_uin", _("%! Unknown uin: %T%1%n\n"), 1); format_add("unregister_failed", _("%! Error while deleting account: %1\n"), 1); /* password remind */ format_add("remind", _("%> Password sent\n"), 1); format_add("remind_failed", _("%! Error while sending password: %1\n"), 1); format_add("remind_timeout", _("%! Password sending timed out\n"), 1); /* password change */ format_add("passwd", _("%> Password changed\n"), 1); format_add("passwd_failed", _("%! Error while changing password: %1\n"), 1); format_add("passwd_timeout", _("%! Password changing timed out\n"), 1); format_add("passwd_possible_abuse", "%> (%1) Password reply send by wrong uid: %2, if this is good server uid please report this to developers and manually change your session password using /session password", 1); format_add("passwd_abuse", "%! (%1) Somebody want to clear our password (%2)", 1); /* changing information in public catalog */ format_add("change", _("%> Information in public directory has been changed\n"), 1); format_add("change_failed", _("%! Error while changing information in public directory\n"), 1); /* users finding */ format_add("search_failed", _("%! Error while search: %1\n"), 1); format_add("search_not_found", _("%! Not found\n"), 1); format_add("search_no_last", _("%! Last search returned no result\n"), 1); format_add("search_no_last_nickname", _("%! No nickname in last search\n"), 1); format_add("search_stopped", _("%> Search stopped\n"), 1); /* 1 uin, 2 name, 3 nick, 4 city, 5 born, 6 gender, 7 active */ format_add("search_results_multi_avail", "%Y<>%n", 1); format_add("search_results_multi_away", "%G<>%n", 1); format_add("search_results_multi_invisible", "%c<>%n", 1); format_add("search_results_multi_notavail", " ", 1); format_add("search_results_multi_unknown", " -", 1); /* format_add("search_results_multi_female", "k", 1); */ /* format_add("search_results_multi_male", "m", 1); */ format_add("search_results_multi", "%7 %[-8]1 %K|%n %[12]3 %K|%n %[12]2 %K|%n %[4]5 %K|%n %[12]4\n", 1); format_add("search_results_single_avail", _("%Y(available)%N"), 1); format_add("search_results_single_away", _("%G(away)%n"), 1); format_add("search_results_single_notavail", _("%r(offline)%n"), 1); format_add("search_results_single_invisible", _("%c(invisible)%n"), 1); format_add("search_results_single_unknown", "%T-%n", 1); format_add("search_results_single", _("%) Nickname: %T%3%n\n%) Number: %T%1%n %7\n%) Name: %T%2%n\n%) City: %T%4%n\n%) Birth year: %T%5%n\n"), 1); /* exec */ format_add("process", "%> %(-5)1 %2\n", 1); format_add("no_processes", _("%! There are no running procesees\n"), 1); format_add("process_exit", _("%> Process %1 (%2) exited with status %3\n"), 1); format_add("exec", "%1\n",1); /* lines are ended by \n */ format_add("exec_error", _("%! Error running process : %1\n"), 1); format_add("exec_prompt", "$ %1\n", 1); /* detailed info about user */ format_add("user_info_header", "%K.--%n %T%1%n/%2 %K--- -- -%n\n", 1); format_add("user_info_nickname", _("%K| %nNickname: %T%1%n\n"), 1); format_add("user_info_status", _("%K| %nStatus: %T%1%n\n"), 1); format_add("user_info_status_time_format", "%Y-%m-%d %H:%M", 1); format_add("user_info_status_time", _("%K| %nCurrent status since: %T%1%n\n"), 1); format_add("user_info_block", _("%K| %nBlocked\n"), 1); format_add("user_info_online", _("%K| %nCan always see our status\n"), 1); format_add("user_info_offline", _("%K| %nCan't see our status\n"), 1); format_add("user_info_name", _("%K| %nName: %T%1 %2%n\n"), 1); format_add("user_info_mobile", _("%K| %nTelephone: %T%1%n\n"), 1); format_add("user_info_ip", _("%K| %nAddress: %T%1%n\n"), 1); format_add("user_info_last_ip", _("%K| %nLast address: %T%1%n\n"), 1); format_add("user_info_groups", _("%K| %nGroups: %T%1%n\n"), 1); format_add("user_info_never_seen", _("%K| %nNever seen\n"), 1); format_add("user_info_last_seen", _("%K| %nLast seen: %T%1%n\n"), 1); format_add("user_info_last_seen_time", "%Y-%m-%d %H:%M", 1); format_add("user_info_last_status", _("%K| %nLast status: %T%1%n\n"), 1); format_add("user_info_footer", "%K`----- ---- --- -- -%n\n", 1); format_add("user_info_avail", _("%Yavailable%n"), 1); format_add("user_info_avail_descr", _("%Yavailable%n %K(%n%2%K)%n"), 1); format_add("user_info_away", _("%Gaway%n"), 1); format_add("user_info_away_descr", _("%Gaway%n %K(%n%2%K)%n"), 1); format_add("user_info_notavail", _("%roffline%n"), 1); format_add("user_info_notavail_descr", _("%roffline%n %K(%n%2%K)%n"), 1); format_add("user_info_invisible", _("%cinvisible%n"), 1); format_add("user_info_invisible_descr", _("%cinvisible%n %K(%n%2%K)%n"), 1); format_add("user_info_dnd", _("%Bdo not disturb%n"), 1); format_add("user_info_dnd_descr", _("%Bdo not disturb%n %K(%n%2%K)%n"), 1); format_add("user_info_chat", _("%Wfree for chat%n"), 1); format_add("user_info_chat_descr", _("%Wfree for chat%n %K(%n%2%K)%n"), 1); format_add("user_info_error", _("%m error%n"), 1); format_add("user_info_error_descr", _("%merror%n %K(%n%2%K)%n"), 1); format_add("user_info_xa", _("%gextended away%n"), 1); format_add("user_info_xa_descr", _("%gextended away%n %K(%n%2%K)%n"), 1); format_add("user_info_gone", _("%Rgone%n"), 1); format_add("user_info_gone_descr", _("%Rgone%n %K(%n%2%K)%n"), 1); format_add("user_info_blocking", _("%mblocking%n"), 1); format_add("user_info_blocking_descr", _("%mblocking%n %K(%n%2%K)%n"), 1); format_add("user_info_unknown", _("%Munknown%n"), 1); format_add("resource_info_status", _("%K| %nResource: %W%1%n Status: %T%2 Prio: %g%3%n"), 1); /* grupy */ format_add("group_members", _("%> %|Group %T%1%n: %2\n"), 1); format_add("group_member_already", _("%! %1 already in group %T%2%n\n"), 1); format_add("group_member_not_yet", _("%! %1 not in group %T%2%n\n"), 1); format_add("group_empty", _("%! Group %T%1%n is empty\n"), 1); /* status */ format_add("show_status_profile", _("%) Profile: %T%1%n\n"), 1); format_add("show_status_uid", "%) UID: %T%1%n\n", 1); format_add("show_status_uid_nick", "%) UID: %T%1%n (%T%2%n)\n", 1); format_add("show_status_status", _("%) Current status: %T%1%2%n\n"), 1); format_add("show_status_status_simple", _("%) Current status: %T%1%n\n"), 1); format_add("show_status_server", _("%) Current server: %T%1%n:%T%2%n\n"), 1); format_add("show_status_server_tls", _("%) Current server: %T%1%n:%T%2%Y (connection encrypted)%n\n"), 1); format_add("show_status_connecting", _("%) Connecting ..."), 1); format_add("show_status_avail", _("%Yavailable%n"), 1); format_add("show_status_avail_descr", _("%Yavailable%n (%T%1%n%2)"), 1); format_add("show_status_away", _("%Gaway%n"), 1); format_add("show_status_away_descr", _("%Gaway%n (%T%1%n%2)"), 1); format_add("show_status_invisible", _("%cinvisible%n"), 1); format_add("show_status_invisible_descr", _("%cinvisible%n (%T%1%n%2)"), 1); format_add("show_status_xa", _("%gextended away%n"), 1); format_add("show_status_xa_descr", _("%gextended away%n (%T%1%n%2)"), 1); format_add("show_status_gone", _("%Rgone%n"), 1); format_add("show_status_gone_descr", _("%Rgone%n (%T%1%n%2)"), 1); format_add("show_status_dnd", _("%cdo not disturb%n"), 1); format_add("show_status_dnd_descr", _("%cdo not disturb%n (%T%1%n%2)"), 1); format_add("show_status_chat", _("%Wfree for chat%n"), 1); format_add("show_status_chat_descr", _("%Wfree for chat%n (%T%1%n%2)"), 1); format_add("show_status_notavail", _("%roffline%n"), 1); format_add("show_status_private_on", _(", for friends only"), 1); format_add("show_status_private_off", "", 1); format_add("show_status_connected_since", _("%) Connected since: %T%1%n\n"), 1); format_add("show_status_disconnected_since", _("%) Disconnected since: %T%1%n\n"), 1); format_add("show_status_last_conn_event", "%Y-%m-%d %H:%M", 1); format_add("show_status_last_conn_event_today", "%H:%M", 1); format_add("show_status_ekg_started_since", _("%) Program started: %T%1%n\n"), 1); format_add("show_status_ekg_started", "%Y-%m-%d %H:%M", 1); format_add("show_status_ekg_started_today", "%H:%M", 1); format_add("show_status_msg_queue", _("%) Messages queued for delivery: %T%1%n\n"), 1); /* aliasy */ format_add("aliases_list_empty", _("%! No aliases\n"), 1); format_add("aliases_list", "%> %T%1%n: %2\n", 1); format_add("aliases_list_next", "%> %3 %2\n", 1); format_add("aliases_add", _("%> Created alias %T%1%n\n"), 1); format_add("aliases_append", _("%> Added to alias %T%1%n\n"), 1); format_add("aliases_del", _("%) Removed alias %T%1%n\n"), 1); format_add("aliases_del_all", _("%) Removed all aliases\n"), 1); format_add("aliases_exist", _("%! Alias %T%1%n already exists\n"), 1); format_add("aliases_noexist", _("%! Alias %T%1%n doesn't exist\n"), 1); format_add("aliases_command", _("%! %T%1%n is internal command\n"), 1); format_add("aliases_not_enough_params", _("%! Alias %T%1%n requires more parameters\n"), 1); /* dcc connections */ format_add("dcc_attack", _("%! To many direct connections, last from %1\n"), 1); format_add("dcc_limit", _("%! %|Direct connections count over limit, so they got disabled. To enable them use %Tset dcc 1% and reconnect. Limit is controlled by %Tdcc_limit%n variable.\n"), 1); format_add("dcc_create_error", _("%! Can't turn on direct connections: %1\n"), 1); format_add("dcc_error_network", _("%! Error transmitting with %1\n"), 1); format_add("dcc_error_refused", _("%! Connection to %1 refused\n"), 1); format_add("dcc_error_unknown", _("%! Uknown direct connection error\n"), 1); format_add("dcc_error_handshake", _("%! Can't connect with %1\n"), 1); format_add("dcc_user_aint_dcc", _("%! %1 doesn't have direct connections enabled\n"), 1); format_add("dcc_timeout", _("%! Direct connection to %1 timed out\n"), 1); format_add("dcc_not_supported", _("%! Operation %T%1%n isn't supported yet\n"), 1); format_add("dcc_open_error", _("%! Can't open %T%1%n: %2\n"), 1); format_add("dcc_show_pending_header", _("%> Pending connections:\n"), 1); format_add("dcc_show_pending_send", _("%) #%1, %2, sending %T%3%n\n"), 1); format_add("dcc_show_pending_get", _("%) #%1, %2, receiving %T%3%n\n"), 1); format_add("dcc_show_pending_voice", _("%) #%1, %2, chat\n"), 1); format_add("dcc_show_active_header", _("%> Active connections:\n"), 1); format_add("dcc_show_active_send", _("%) #%1, %2, sending %T%3%n, %T%4b%n z %T%5b%n (%6%%)\n"), 1); format_add("dcc_show_active_get", _("%) #%1, %2, receiving %T%3%n, %T%4b%n z %T%5b%n (%6%%)\n"), 1); format_add("dcc_show_active_voice", _("%) #%1, %2, chat\n"), 1); format_add("dcc_show_empty", _("%! No direct connections\n"), 1); format_add("dcc_receiving_already", _("%! File %T%1%n from %2 is being received\n"), 1); format_add("dcc_done_get", _("%> Finished receiving file %T%2%n from %1\n"), 1); format_add("dcc_done_send", _("%> Finished sending file %T%2%n to %1\n"), 1); format_add("dcc_close", _("%) Connection with %1 closed\n"), 1); format_add("dcc_voice_offer", _("%) %1 wants to chat\n%) Use %Tdcc voice #%2%n to start chat or %Tdcc close #%2%n to refuse\n"), 1); format_add("dcc_voice_running", _("%! Only one simultanous voice chat possible\n"), 1); format_add("dcc_voice_unsupported", _("%! Voice chat not compiled in. See %Tdocs/voip.txt%n\n"), 1); format_add("dcc_get_offer", _("%) %1 sends %T%2%n (size %T%3b%n)\n%) Use %Tdcc get #%4%n to receive or %Tdcc close #%4%n to refuse\n"), 1); format_add("dcc_get_offer_resume", _("%) File exist, you can resume with %Tdcc resume #%4%n\n"), 1); format_add("dcc_get_getting", _("%) Started receiving %T%2%n from %1\n"), 1); format_add("dcc_get_cant_create", _("%! Can't open file %T%1%n\n"), 1); format_add("dcc_not_found", _("%! Connection not found: %T%1%n\n"), 1); format_add("dcc_invalid_ip", _("%! Invalid IP address\n"), 1); format_add("dcc_user_notavail", _("%! %1 has to available to connect\n"), 1); /* query */ format_add("query_started", _("%) (%2) Query with %T%1%n started\n"), 1); format_add("query_started_window", _("%) Press %TAlt-G%n to ignore, %TAlt-K%n to close window\n"), 1); format_add("query_finished", _("%) (%2) Finished query with %T%1%n\n"), 1); format_add("query_exist", _("%! (%3) Query with %T%1%n already in window no %T%2%n\n"), 1); /* zdarzenia */ format_add("events_list_empty", _("%! No events\n"), 1); format_add("events_list_header", "", 1); format_add("events_list", "%> %5 on %1 %3 %4 - prio %2\n", 1); format_add("events_add", _("%> Added event %T%1%n\n"), 1); format_add("events_del", _("%) Removed event %T%1%n\n"), 1); format_add("events_del_all", _("%) Removed all events\n"), 1); format_add("events_exist", _("%! Event %T%1%n exist for %2\n"), 1); format_add("events_del_noexist", _("%! Event %T%1%n do not exist\n"), 1); /* contact list from the server */ format_add("userlist_put_ok", _("%> (%1) Roster exported\n"), 1); format_add("userlist_put_error", _("%! (%1) Error exporting roster\n"), 1); format_add("userlist_get_ok", _("%> (%1) Roster imported\n"), 1); format_add("userlist_get_error", _("%! (%1) Error importing roster\n"), 1); format_add("userlist_clear_ok", _("%) (%1) Removed roster from server\n"), 1); format_add("userlist_clear_error", _("%! (%1) Error removing roster from server\n"), 1); /* szybka lista kontaktw pod F2 */ format_add("quick_list", "%)%1\n", 1); format_add("quick_list,speech", _("roster:"), 1); format_add("quick_list_avail,speech", _("%1 is available"), 1); format_add("quick_list_away,speech", _("%1 is away"), 1); format_add("quick_list_chat,speech", _("%1 is free for chat"), 1); format_add("quick_list_xa,speech", _("%1 is extended away"), 1); format_add("quick_list_gone,speech", _("%1 is gone"), 1); format_add("quick_list_dnd,speech", _("%1 has 'do not disturb' status"), 1); /* window */ format_add("window_add", _("%) New window created\n"), 1); format_add("window_noexist", _("%! Chosen window does not exist\n"), 1); format_add("window_doesnt_exist", _("%! Window %T%1%n does not exist\n"), 1); format_add("window_no_windows", _("%! Can't close last window\n"), 1); format_add("window_del", _("%) Window closed\n"), 1); format_add("windows_max", _("%! Window limit exhausted\n"), 1); format_add("window_list_query", _("%) %1: query with %T%2%n\n"), 1); format_add("window_list_nothing", _("%) %1 no query\n"), 1); format_add("window_list_floating", _("%) %1: floating %4x%5 in %2,%3 %T%6%n\n"), 1); format_add("window_id_query_started", _("%) (%3) Query with %T%2%n started in %T%1%n\n"), 1); format_add("window_kill_status", _("%! Can't close status window!\n"), 1); format_add("window_cannot_move_status", _("%! Can't move status window!\n"), 1); format_add("window_invalid_move", _("%! Window %T%1%n can't be moved\n"), 1); format_add("cant_kill_irc_window", _("Can't kill window. Use /window kill"), 1); format_add("file_doesnt_exist", _("%! Can't open file %T%1%n\n"), 1); /* bind */ format_add("bind_seq_incorrect", _("%! Sequence %T%1%n is invalid\n"), 1); format_add("bind_seq_add", _("%> Sequence %T%1%n added\n"), 1); format_add("bind_seq_remove", _("%) Sequence %T%1%n removed\n"), 1); format_add("bind_seq_list", "%> %1: %T%2%n\n", 1); format_add("bind_seq_exist", _("%! Sequence %T%1%n is already bound\n"), 1); format_add("bind_seq_list_empty", _("%! No bound actions\n"), 1); format_add("bind_doesnt_exist", _("%! Can't find sequence %T%1%n\n"), 1); format_add("bind_press_key", _("%! Press key(s) which should be bound\n"), 1); format_add("bind_added", _("%> Binding added\n"), 1); /* at */ format_add("at_list", "%> %1, %2, %3 %K(%4)%n %5\n", 1); format_add("at_added", _("%> Created plan %T%1%n\n"), 1); format_add("at_deleted", _("%) Removed plan %T%1%n\n"), 1); format_add("at_deleted_all", _("%) Removed user's plans\n"), 1); format_add("at_exist", _("%! Plan %T%1%n already exists\n"), 1); format_add("at_noexist", _("%! Plan %T%1%n do not exists\n"), 1); format_add("at_empty", _("%! No plans\n"), 1); format_add("at_timestamp", "%d-%m-%Y %H:%M", 1); format_add("at_back_to_past", _("%! If time travels were possible...\n"), 1); /* timer */ format_add("timer_list", "%> %1, %2s, %3 %K(%4)%n %T%5%n\n", 1); format_add("timer_added", _("%> Created timer %T%1%n\n"), 1); format_add("timer_deleted", _("%) Removed timer %T%1%n\n"), 1); format_add("timer_deleted_all", _("%) Removed user's timers\n"), 1); format_add("timer_exist", _("%! Timer %T%1%n already exists\n"), 1); format_add("timer_noexist", _("%! Timer %T%1%n does not exists\n"), 1); format_add("timer_empty", _("%! No timers\n"), 1); /* last */ format_add("last_list_in", "%) %Y <<%n [%1] %2 %3\n", 1); format_add("last_list_out", "%) %G >>%n [%1] %2 %3\n", 1); format_add("last_list_status", "%) %G *%n [%1] %2 is %3\n", 1); format_add("last_list_status_descr", "%) %G *%n [%1] %2 is %3: %4\n", 1); format_add("last_list_empty", _("%! No messages logged\n"), 1); format_add("last_list_empty_status", _("%! No change status logged\n"), 1); format_add("last_list_empty_nick", _("%! No messages from %T%1%n logged\n"), 1); format_add("last_list_empty_nick_status", _("%! No change status by %T%1%n logged\n"), 1); format_add("last_list_timestamp", "%d-%m-%Y %H:%M", 1); format_add("last_list_timestamp_today", "%H:%M", 1); format_add("last_clear_uin", _("%) Messages from %T%1%n cleared\n"), 1); format_add("last_clear_uin_status", _("%) Statuses from %T%1%n cleared\n"), 1); format_add("last_clear", _("%) All messages cleared\n"), 1); format_add("last_clear_status", _("%) All statuses cleared\n"), 1); format_add("last_begin_uin", _("%) Lastlog from %T%1%n begins\n"), 1); format_add("last_begin_uin_status", _("%) Lastlog status from %T%1%n begins\n"), 1); format_add("last_begin", _("%) Lastlog begin\n"), 1); format_add("last_begin_status", _("%) Lastlog status begin\n"), 1); format_add("last_end", _("%) Lastlog end\n"), 1); format_add("last_end_status", _("%) Lastlog status end\n"), 1); /* queue */ format_add("queue_list_timestamp", "%d-%m-%Y %H:%M", 1); format_add("queue_list_message", "%) %G >>%n [%1] %2 %3\n", 1); format_add("queue_clear", _("%) Message queue cleared\n"), 1); format_add("queue_clear_uid", _("%) Message queue for %T%1%n cleared\n"), 1); format_add("queue_wrong_use", _("%! Command works only when disconected\n"), 1); format_add("queue_empty", _("%! Messaged queue is empty\n"), 1); format_add("queue_empty_uid", _("%! No messages to %T%1%n in queue\n"), 1); format_add("queue_flush", _("%> (%1) Sent messages from queue\n"), 1); /* conference */ format_add("conferences_list_empty", _("%! No conference\n"), 1); format_add("conferences_list", "%> %T%1%n: %2\n", 1); format_add("conferences_list_ignored", _("%> %T%1%n: %2 (%yignored%n)\n"), 1); format_add("conferences_add", _("%> Created conference %T%1%n\n"), 1); format_add("conferences_not_added", _("%! Conference not created %T%1%n\n"), 1); format_add("conferences_del", _("%) Removed conference %T%1%n\n"), 1); format_add("conferences_del_all", _("%) Removed all conferences\n"), 1); format_add("conferences_exist", _("%! Conference %T%1%n already exists\n"), 1); format_add("conferences_noexist", _("%! Conference %T%1%n do not exists\n"), 1); format_add("conferences_name_error", _("%! Conference name should start with %T#%n\n"), 1); format_add("conferences_rename", _("%> Conference renamed: %T%1%n --> %T%2%n\n"), 1); format_add("conferences_ignore", _("%> Konference %T%1%n will be ignored\n"), 1); format_add("conferences_unignore", _("%> Conference %T%1%n won't be ignored\n"), 1); format_add("conferences_joined", _("%> Joined %1 to conference %T%2%n\n"), 1); format_add("conferences_already_joined", _("%> %1 already in conference %T%2%n\n"), 1); /* shared by http */ format_add("http_failed_resolving", _("Server not found"), 1); format_add("http_failed_connecting", _("Can not connect ro server"), 1); format_add("http_failed_reading", _("Server disconnected"), 1); format_add("http_failed_writing", _("Server disconnected"), 1); format_add("http_failed_memory", _("No memory"), 1); format_add("session_name", "%B%1%n", 1); format_add("session_variable", "%> %T%1->%2 = %R%3%n\n", 1); /* uid, var, new_value*/ format_add("session_variable_removed", _("%> Removed %T%1->%2%n\n"), 1); /* uid, var */ format_add("session_variable_doesnt_exist", _("%! Unknown variable: %T%1->%2%n\n"), 1); /* uid, var */ format_add("session_list", "%> %T%1%n %3\n", 1); /* uid, uid, %{user_info_*} */ format_add("session_list_alias", "%> %T%2%n/%1 %3\n", 1); /* uid, alias, %{user_info_*} */ format_add("session_list_empty", _("%! Session list is empty\n"), 1); format_add("session_info_header", "%) %T%1%n %3\n", 1); /* uid, uid, %{user_info_*} */ format_add("session_info_header_alias", "%) %T%2%n/%1 %3\n", 1); /* uid, alias, %{user_info_*} */ format_add("session_info_param", "%) %1 = %T%2%n\n", 1); /* key, value */ format_add("session_info_footer", "", 1); /* uid */ format_add("session_exists", _("%! Session %T%1%n already exists\n"), 1); /* uid */ format_add("session_doesnt_exist", _("%! Session %T%1%n does not exist\n"), 1); /* uid */ format_add("session_added", _("%> Created session %T%1%n\n"), 1); /* uid */ format_add("session_removed", _("%> Removed session %T%1%n\n"), 1); /* uid */ format_add("session_format", "%T%1%n", 1); format_add("session_format_alias", "%T%1%n/%2", 1); format_add("session_cannot_change", _("%! Can't change session in query window%n\n"), 1); format_add("session_password_changed", _("%> %|(%1) Looks like you're changing password in connected session. This does only set password on the client-side. If you want you change your account password, please use dedicated function (e.g. /passwd)."), 1); format_add("session_locked", _("%! %|Session %T%1%n is currently locked. If there aren't any other copy of EKG2 using it, please call: %c/session --unlock%n to unlock it.\n"), 1); format_add("session_not_locked", _("%! Session %T%1%n is not locked"), 1); format_add("metacontact_list", "%> %T%1%n", 1); format_add("metacontact_list_empty", "%! Metacontact list is empty\n", 1); format_add("metacontact_exists", "%! Metacontact %T%1%n already exists\n", 1); format_add("metacontact_added", "%> Metacontact %T%1%n added\n", 1); format_add("metacontact_removed", "%> Metacontact %T%1%n removed\n", 1); format_add("metacontact_doesnt_exist", "%! Metacontact %T%1%n doesn't exist\n", 1); format_add("metacontact_added_item", "%> Added %T%1/%2%n to metacontact %T%3%n\n", 1); format_add("metacontact_removed_item", "%> Removed %T%1/%2%n from metacontact %T%3%n\n", 1); format_add("metacontact_item_list_header", "", 1); format_add("metacontact_item_list", "%> %T%1/%2 (%3)%n - prio %T%4%n\n", 1); format_add("metacontact_item_list_empty", "%! Metacontact is empty\n", 1); format_add("metacontact_item_list_footer", "", 1); format_add("metacontact_item_doesnt_exist", "%! Contact %T%1/%2%n doesn't exiet\n", 1); format_add("metacontact_info_header", "%K.--%n Metacontact %T%1%n %K--- -- -%n\n", 1); format_add("metacontact_info_status", "%K| %nStatus: %T%1%n\n", 1); format_add("metacontact_info_footer", "%K`----- ---- --- -- -%n\n", 1); format_add("metacontact_info_avail", _("%Yavailable%n"), 1); format_add("metacontact_info_avail_descr", _("%Yavailable%n %K(%n%2%K)%n"), 1); format_add("metacontact_info_away", _("%Gaway%n"), 1); format_add("metacontact_info_away_descr", _("%Gaway%n %K(%n%2%K)%n"), 1); format_add("metacontact_info_notavail", _("%roffline%n"), 1); format_add("metacontact_info_notavail_descr", _("%roffline%n %K(%n%2%K)%n"), 1); format_add("metacontact_info_invisible", _("%cinvisible%n"), 1); format_add("metacontact_info_invisible_descr", _("%cinvisible%n %K(%n%2%K)%n"), 1); format_add("metacontact_info_dnd", _("%Bdo not disturb%n"), 1); format_add("metacontact_info_dnd_descr", _("%Bdo not disturb%n %K(%n%2%K)%n"), 1); format_add("metacontact_info_chat", _("%Wfree for chat%n"), 1); format_add("metacontact_info_chat_descr", _("%Wfree for chat%n %K(%n%2%K)%n"), 1); format_add("metacontact_info_error", _("%merror%n"), 1); format_add("metacontact_info_error_descr", _("%merror%n %K(%n%2%K)%n"), 1); format_add("metacontact_info_xa", _("%gextended away%n"), 1); format_add("metacontact_info_xa_descr", _("%gextended away%n %K(%n%2%K)%n"), 1); format_add("metacontact_info_gone", _("%Rgone%n"), 1); format_add("metacontact_info_gone_descr", _("%Rgone%n %K(%n%2%K)%n"), 1); format_add("metacontact_info_blocking", _("%mblocking%n"), 1); format_add("metacontact_info_blocking_descr", _("%mblocking%n %K(%n%2%K)%n"), 1); format_add("metacontact_info_unknown", _("%Munknown%n"), 1); format_add("plugin_already_loaded", _("%! Plugin %T%1%n already loaded%n.\n"), 1); format_add("plugin_doesnt_exist", _("%! Can't load plugin %T%1%n. See debug window.%n\n"), 1); format_add("plugin_incorrect", _("%! Plugin %T%1%n is not correct EKG2 plugin%n\n"), 1); format_add("plugin_not_initialized", _("%! Plugin %T%1%n not initialized correctly, check debug window%n\n"), 1); format_add("plugin_unload_ui", _("%! Plugin %T%1%n is an UI plugin and can't be unloaded%n\n"), 1); format_add("plugin_loaded", _("%> Plugin %T%1%n loaded%n\n"), 1); format_add("plugin_unloaded", _("%> Plugin %T%1%n unloaded%n\n"), 1); format_add("plugin_list", _("%> %T%1%n - %2%n\n"), 1); format_add("plugin_prio_set", _("%> Plugin %T%1%n prio has been changed to %T%2%n\n"), 1); format_add("plugin_default", _("%> Plugins prio setted to default\n"), 1); format_add("script_autorun_succ", _("%> Script %W%1%n successful %G%2%n autorun dir"), 1); /* XXX sciezka by sie przydala */ format_add("script_autorun_fail", _("%! Script %W%1%n failed %R%2%n autorun dir %r(%3)"), 1); format_add("script_autorun_unkn", _("%! Error adding/removing script %W%1%n from autorundir %r(%3)"), 1); format_add("script_loaded", _("%) Script %W%1%n %g(%2)%n %Gloaded %b(%3)"), 1); format_add("script_incorrect", _("%! Script %W%1%n %g(%2)%n %rNOT LOADED%n %R[incorrect %3 script or you've got syntax errors]"), 1); format_add("script_incorrect2", _("%! Script %W%1%n %g(%2)%n %rNOT LOADED%n %R[script has no handler or error in getting handlers]"), 1); format_add("script_removed", _("%) Script %W%1%n %g(%2)%n %Rremoved %b(%3)"), 1); format_add("script_need_name", _("%! No filename given\n"), 1); format_add("script_not_found", _("%! Can't find script %W%1"), 1); format_add("script_wrong_location", _("%! Script have to be in %g%1%n (don't add path)"), 1); format_add("script_error", _("%! %rScript error: %|%1"), 1); format_add("script_autorun_list", "%) Script %1 -> %2\n", 1); format_add("script_eval_error", _("%! Error running code\n"), 1); format_add("script_list", _("%> %1 (%2, %3)\n"), 1); format_add("script_list_empty", _("%! No scripts loaded\n"), 1); format_add("script_generic", "%> [script,%2] (%1) %3\n", 1); format_add("script_varlist", _("%> %1 = %2 (%3)\n"), 1); format_add("script_varlist_empty", _("%! No script vars!\n"), 1); format_add("directory_cant_create", _("%! Can't create directory: %1 (%2)"), 1); /* jogger-like I/O */ format_add("io_cantopen", _("%! %|Unable to open file: %T%1%n (%c%2%n)!"), 1); format_add("io_nonfile", _("%! %|Given path doesn't appear to be regular file: %T%1%n!"), 1); format_add("io_cantread", _("%! %|Unable to read file: %T%1%n (%c%2%n)!"), 1); format_add("io_truncated", _("%! %|WARNING: EOF before reaching filesize (%c%2%n vs. %c%3%n). File %T%1%n probably truncated (somehow)!"), 1); format_add("io_expanded", _("%! %|WARNING: EOF after reaching filesize (%c%2%n vs. %c%3%n). File %T%1%n probably got expanded!"), 1); format_add("io_emptyfile", _("%! File %T%1%n is empty!"), 1); format_add("io_toobig", _("%! Size of file %T%1%n exceeds maximum allowed length (at least %c%2%n vs. %c%3%n)!"), 1); format_add("io_binaryfile", _("%! %|WARNING: The file %T%1%n probably contains NULs (is binary), so it can't be properly handled. It will be read until first encountered NUL, i.e. to offset %c%2%n (vs. filesize of %c%3%n)!"), 1); /* dns stuff */ format_add("dns2_resolved", _("%) DNS2 RESOLVED %G%1: %W%2%n"), 1); theme_plugins_init(); #endif /* !NO_DEFAULT_THEME */ } /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/ekg/themes.h000066400000000000000000000072651175142753400163730ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2001-2003 Wojtek Kaniewski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __EKG_THEMES_H #define __EKG_THEMES_H #include "gettext.h" #define _(a) gettext(a) #define N_(a) gettext_noop(a) #include "dynstuff.h" #include "sessions.h" #ifdef __cplusplus extern "C" { #endif typedef guint16 fstr_attr_t; typedef struct { gchar *str; /* possibly multibyte string */ fstr_attr_t *attr; /* atrybuty, cig o dugoci strlen(str) */ time_t ts; /* timestamp */ int prompt_len; /* dugo promptu, ktry bdzie powtarzany przy przejciu do kolejnej linii. */ unsigned int prompt_empty : 1; /* prompt przy przenoszeniu bdzie pusty */ int margin_left; /* where the margin is set (on what char) */ gchar *priv_data; /* can be helpfull */ } fstring_t; #define print(x...) print_window_w(NULL, EKG_WINACT_JUNK, x) #define print_status(x...) print_window_w(window_status, EKG_WINACT_JUNK, x) #ifndef EKG2_WIN32_NOFUNCTION void print_window(const char *target, session_t *session, int activity, int separate, const char *theme, ...); void print_info(const char *target, session_t *session, const char *theme, ...); void print_warning(const char *target, session_t *session, const char *theme, ...); void format_add(const char *name, const char *value, int replace); const char *format_find(const char *name); #define format_ok(format_find_result) (format_find_result[0]) #define format_exists(format) (format_ok(format_find(format))) char *format_string(const char *format, ...); void theme_init(); void theme_plugins_init(); void theme_enumerate(int (*enumerator)(const char *theme, const char *value)); int theme_read(const char *filename, int replace); int theme_write(const char *filename); void theme_cache_reset(); void theme_free(); fstring_t *fstring_dup(const fstring_t *str); fstring_t *fstring_new(const char *str); fstring_t *fstring_new_format(const char *format, ...); void fstring_free(fstring_t *str); void fstring_iter(const fstring_t *s, gchar **text, fstr_attr_t **attr, gssize *len); gboolean fstring_next(gchar **text, fstr_attr_t **attr, gssize *len, fstr_attr_t *change); #endif /* * makro udajce isalpha() z LC_CTYPE="pl_PL". niestety ncurses co psuje * i le wykrywa pe. */ #define isalpha_pl_PL(x) ((x >= 'a' && x <= 'z') || (x >= 'A' && x <= 'Z') || x == '' || x == '' || x == '' || x == '' || x == '' || x == '' || x == '' || x == '' || x == '' || x == '' || x == '' || x == '' || x == '' || x == '' || x == '' || x == '' || x == '' || x == '') typedef enum { FSTR_FOREA = 1, FSTR_FOREB = 2, FSTR_FOREC = 4, FSTR_FOREMASK = (FSTR_FOREA|FSTR_FOREB|FSTR_FOREC), FSTR_BACKA = 8, FSTR_BACKB = 16, FSTR_BACKC = 32, FSTR_BACKMASK = (FSTR_BACKA|FSTR_BACKB|FSTR_BACKC), FSTR_BOLD = 64, FSTR_NORMAL = 128, FSTR_BLINK = 256, FSTR_UNDERLINE = 512, FSTR_REVERSE = 1024, FSTR_ALTCHARSET = 2048, FSTR_LINEBREAK = 4096, } fstr_t; #ifdef __cplusplus } #endif #endif /* __EKG_THEMES_H */ /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/ekg/userlist.c000066400000000000000000000606701175142753400167520ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2001-2003 Wojtek Kaniewski * Robert J. Wony * Piotr Domagalski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #include #ifndef NO_POSIX_SYSTEM #include #include #include #endif #include #include #include #include #include #include #include static void userlist_private_free(userlist_t *u); struct ignore_label ignore_labels[IGNORE_LABELS_MAX] = { { IGNORE_STATUS, "status" }, { IGNORE_STATUS_DESCR, "descr" }, { IGNORE_NOTIFY, "notify" }, { IGNORE_MSG, "msg" }, { IGNORE_DCC, "dcc" }, { IGNORE_EVENTS, "events" }, { IGNORE_XOSD, "xosd" }, { IGNORE_LOG, "log" }, { 0, NULL } }; /* groups: */ static LIST_ADD_COMPARE(group_compare, struct ekg_group *) { return xstrcasecmp(data1->name, data2->name); } static LIST_FREE_ITEM(group_item_free, struct ekg_group *) { xfree(data->name); } DYNSTUFF_LIST_DECLARE_SORTED(ekg_groups, struct ekg_group, group_compare, group_item_free, static __DYNSTUFF_ADD_SORTED, /* ekg_groups_add() */ static __DYNSTUFF_REMOVE_ITER, /* ekg_groups_removei() */ static __DYNSTUFF_DESTROY) /* ekg_groups_destroy() */ /* resources: */ static LIST_ADD_COMPARE(userlist_resource_compare, ekg_resource_t *) { if (data1->prio != data2->prio) return (data2->prio - data1->prio); /* sort by prio */ return xstrcasecmp(data1->name, data2->name); /* sort by name */ } static LIST_FREE_ITEM(list_userlist_resource_free, ekg_resource_t *) { xfree(data->name); xfree(data->descr); } DYNSTUFF_LIST_DECLARE_SORTED(ekg_resources, ekg_resource_t, userlist_resource_compare, list_userlist_resource_free, static __DYNSTUFF_ADD_SORTED, /* ekg_resources_add() */ static __DYNSTUFF_REMOVE_SAFE, /* ekg_resources_remove() */ static __DYNSTUFF_DESTROY) /* ekg_resources_destroy() */ /* userlist: */ static LIST_ADD_COMPARE(userlist_compare, userlist_t *) { return xstrcoll(data1->nickname, data2->nickname); } static LIST_FREE_ITEM(userlist_free_item, userlist_t *) { userlist_private_free(data); private_items_destroy(&data->priv_list); xfree((void *) data->uid); xfree(data->nickname); xfree(data->descr); xfree(data->foreign); xfree(data->last_descr); xfree(data->descr1line); ekg_groups_destroy(&(data->groups)); ekg_resources_destroy(&(data->resources)); } DYNSTUFF_LIST_DECLARE_SORTED(userlists, userlist_t, userlist_compare, userlist_free_item, static __DYNSTUFF_ADD_SORTED, /* userlists_add() */ __DYNSTUFF_REMOVE_SAFE, /* userlists_remove() */ __DYNSTUFF_DESTROY) /* userlists_destroy() */ /* * userlist_add_entry() * * dodaje do listy kontaktw pojedyncz lini z pliku lub z serwera. */ void userlist_add_entry(session_t *session, const char *line) { char **entry = array_make(line, ";", 8, 0, 0); userlist_t *u; int count, i; if ((count = g_strv_length(entry)) < 7) { g_strfreev(entry); return; } u = xmalloc(sizeof(userlist_t)); /* we'd need this here */ u->uid = entry[6]; entry[6] = NULL; { int function = EKG_USERLIST_PRIVHANDLER_READING; query_emit(NULL, "userlist-privhandle", &u, &function, &entry, &count); } if (valid_plugin_uid(session->plugin, u->uid) != 1) { debug_error("userlist_add_entry() wrong uid: %s for session: %s [plugin: 0x%x]\n", u->uid, session->uid, session->plugin); array_free_count(entry, count); xfree((void *) u->uid); xfree(u); return; } u->status = EKG_STATUS_NA; for (i = 0; i < 6; i++) { if (!xstrcmp(entry[i], "(null)") || !xstrcmp(entry[i], "")) { xfree(entry[i]); entry[i] = NULL; } } u->groups = group_init(entry[5]); if (entry[3]) { u->nickname = !valid_nick(entry[3]) ? saprintf("_%s", entry[3]) : xstrdup(entry[3]); } u->foreign = entry[7] ? saprintf(";%s", entry[7]) : NULL; array_free_count(entry, count); userlists_add(&(session->userlist), u); } /** * userlist_read() * * wczytuje list kontaktw z pliku uid_sesji-userlist w postaci eksportu * tekstowego listy kontaktw windzianego klienta. * * @param session * @return 0 on success, -1 file not found */ int userlist_read(session_t *session) { char *buf; GDataInputStream *f; if (!(f = G_DATA_INPUT_STREAM(config_open("%s-userlist", "r", session->uid)))) return -1; while ((buf = read_line(f))) { if (buf[0] == '#' || (buf[0] == '/' && buf[1] == '/')) continue; userlist_add_entry(session, buf); } query_emit(NULL, "userlist-refresh"); /* XXX, wywolywac tylko kiedy dodalismy przynajmniej 1 */ g_object_unref(f); return 0; } /** * userlist_write() * * It writes @a session userlist to file: session->uid-userlist in ekg2 config directory * * @todo Each plugin should've own userlist_write()/ userlist_read() * This format is obsolete. * * @param session * * @return 0 on succees
* -1 if smth went wrong
* -2 if we fail to create/open userlist file in rw mode */ void userlist_write(session_t *session) { GOutputStream *f; userlist_t *ul; if (!(f = G_OUTPUT_STREAM(config_open("%s-userlist", "w", session->uid)))) return; /* userlist_dump() */ for (ul = session->userlist; ul; ul = ul->next) { userlist_t *u = ul; char **entry = xcalloc(7, sizeof(char *)); char *line; entry[0] = NULL; /* first name [gg] */ entry[1] = NULL; /* last name [gg] */ entry[2] = xstrdup(u->nickname); /* display? backwards compatibility? */ entry[3] = xstrdup(u->nickname); /* nickname */ entry[4] = NULL; /* mobile [gg] */ entry[5] = group_to_string(u->groups, 1, 0); /* groups (alloced itself) */ entry[6] = strdup(u->uid); /* uid */ { int function = EKG_USERLIST_PRIVHANDLER_WRITING; query_emit(NULL, "userlist-privhandle", &u, &function, &entry); } line = array_join_count(entry, ";", 7); ekg_fprintf(f, "%s%s\n", line, /* look upper */ u->foreign ? u->foreign : ""); /* backwards compatibility */ xfree(line); array_free_count(entry, 7); } } static void userlist_private_free(userlist_t *u) { if (u->priv) { int func = EKG_USERLIST_PRIVHANDLER_FREE; query_emit(NULL, "userlist-privhandle", &u, &func); } } void *userlist_private_get(plugin_t *plugin, userlist_t *u) { int func = EKG_USERLIST_PRIVHANDLER_GET; void *up = NULL; query_emit(plugin, "userlist-privhandle", &u, &func, &up); return up; } /** * userlist_clear_status() * * If @a uin == NULL then it clears all users presence information in the @a session userlist * otherwise it clears only specified user * It's useful if user goes notavail, or we goes disconnected..
* However if that happen you shouldn't use this function but emit query PROTOCOL_STATUS or PROTOCOL_DISCONNECTED * * @note By presence information I mean:
* -> status - user's status [avail, away, ffc, dnd], it'll be: @a EKG_STATUS_NA ("notavail")
* -> descr - user's description, it'll be: NULL
* -> ip - user's ip, il'll be: 0.0.0.0
* -> port - user's port, it'll be: 0
* -> resources - user's resource, list will be destroyed. * * @param session * @param uid */ void userlist_clear_status(session_t *session, const char *uid) { userlist_t *ul; if (!session) return; for (ul = session->userlist; ul; ul = ul->next) { userlist_t *u = ul; if (!uid || !xstrcasecmp(uid, u->uid)) { u->status = EKG_STATUS_NA; xfree(u->descr); u->descr = NULL; ekg_resources_destroy(&(u->resources)); } } } /* * userlist_free() * * czyci list uytkownikw i zwalnia pami. */ void userlist_free(session_t *session) { if (!session) return; userlists_destroy(&(session->userlist)); } /** * userlist_resource_add() * * It adds new user resource to resources list, with given data. * * @note It doesn't check if prio already exists.. So you must remember about * calling userlist_resource_find() if you don't want two (or more) same resources... * * @param u - user * @param name - name of resource * @param prio - prio of resource * * @return It returns inited ekg_resource_t pointer of given data, or NULL if @a u was NULL */ ekg_resource_t *userlist_resource_add(userlist_t *u, const char *name, int prio) { ekg_resource_t *r; if (!u) return NULL; r = xmalloc(sizeof(ekg_resource_t)); r->name = xstrdup(name); r->prio = prio; r->status = EKG_STATUS_NA; ekg_resources_add(&(u->resources), r); return r; } /** * userlist_resource_find() * * It search for given resource @a name in user resource list @a u * * @param u - user * @param name - name of resource * * @return It returns resource with given name if founded, otherwise NULL */ ekg_resource_t *userlist_resource_find(userlist_t *u, const char *name) { ekg_resource_t *rl; if (!u) return NULL; for (rl = u->resources; rl; rl = rl->next) { ekg_resource_t *r = rl; if (!xstrcmp(r->name, name)) return r; } return NULL; } /** * userlist_resource_remove() * * Remove given resource @a r from user resource list @a u
* Free allocated memory. * * @param u - user * @param r - resource */ void userlist_resource_remove(userlist_t *u, ekg_resource_t *r) { /* XXX, remove */ if (!u || !r) return; ekg_resources_remove(&(u->resources), r); } /* * userlist_add() * * dodaje uytkownika do listy. * * - uin, * - display. */ userlist_t *userlist_add(session_t *session, const char *uid, const char *nickname) { if (!session) return NULL; if (valid_plugin_uid(session->plugin, uid) != 1) { debug_error("userlist_add() wrong uid: %s for session: %s [plugin: 0x%x]\n", uid, session->uid, session->plugin); return NULL; } return userlist_add_u(&(session->userlist), uid, nickname); } /* * userlist_add_u() * * adds user to window's userlist * uid - uid, * nickname - display. */ userlist_t *userlist_add_u(userlist_t **userlist, const char *uid, const char *nickname) { userlist_t *u = xmalloc(sizeof(userlist_t)); u->uid = xstrdup(uid); u->nickname = xstrdup(nickname); u->status = EKG_STATUS_NA; userlists_add(userlist, u); return u; } /* * userlist_remove() * * usuwa danego uytkownika z listy kontaktw. * * - u. */ int userlist_remove(session_t *session, userlist_t *u) { return userlist_remove_u(&(session->userlist), u); } /* * userlist_remove_u() * * removes given user from window's userlist * * - u. */ int userlist_remove_u(userlist_t **userlist, userlist_t *u) { if (!u) return -1; userlists_remove(userlist, u); return 0; } /* * userlist_replace() * * usuwa i dodaje na nowo uytkownika, eby zosta umieszczony na odpowiednim * (pod wzgldem kolejnoci alfabetycznej) miejscu. gupie to troch, ale * przy listach jednokierunkowych nie za bardzo jest sens komplikowa spraw * z przesuwaniem elementw listy. * * - u. * * 0/-1 */ int userlist_replace(session_t *session, userlist_t *u) { if (!u) return -1; if (!LIST_UNLINK2(&(session->userlist), u) && (errno == ENOENT)) return -1; userlists_add(&(session->userlist), u); return 0; } /* * userlist_find() * * znajduje odpowiedni struktur `userlist' odpowiadajc danemu * identyfikatorowi lub jego opisowi. * * - uid, */ userlist_t *userlist_find(session_t *session, const char *uid) { if (!uid || !session) return NULL; return userlist_find_u(&(session->userlist), uid); } /* * userlist_find_u() * * finds and returns pointer to userlist_t which includes given * uid */ userlist_t *userlist_find_u(userlist_t **userlist, const char *uid) { userlist_t *ul; if (!uid || !userlist) return NULL; for (ul = *userlist; ul; ul = ul->next) { userlist_t *u = ul; const char *tmp; int len; if (!xstrcasecmp(u->uid, uid)) return u; if (u->nickname && !xstrcasecmp(u->nickname, uid)) return u; /* porwnujemy resource; if (len > 0) */ if (!(tmp = xstrchr(uid, '/')) || (xstrncmp(uid, "tlen:", 5) && xstrncmp(uid, "xmpp:", 5)) ) continue; len = (int)(tmp - uid); if (len > 0 && !xstrncasecmp(uid, u->uid, len)) return u; } return NULL; } /* * valid_nick() * * sprawdza, czy nick nie zawiera znakw specjalnych, * ktre mogyby powodowa problemy. * * zwraca 1 jeli nick jest w porzdku, w przeciwnym razie 0. */ int valid_nick(const char *nick) { const char *wrong[] = { "(null)", "__debug", "__status", "__current", "__contacts", "*", "$", NULL }; int i; if (!nick) return 0; for (i = 0; wrong[i]; i++) { if (!xstrcmp(nick, wrong[i])) return 0; } if (nick[0] == '@' || nick[0] == '#' || xstrchr(nick, ',')) return 0; return 1; } /** * valid_uid() * * Check if @a uid can be handled by any plugin * * @param uid - uid to check for * * @sa valid_plugin_uid() - You can specify plugin * * @return 1 - if uid can be handled by ekg2
* 0 - if not */ int valid_uid(const char *uid) { int valid = 0; char *tmp; tmp = xstrdup(uid); query_emit(NULL, "protocol-validate-uid", &tmp, &valid); xfree(tmp); return (valid > 0); } /** * valid_plugin_uid() * * Check if @a uid can be handled by given @a plugin * * @param plugin - plugin to check for * @param uid - uid to check for * * @sa valid_uid() - if we want to know if this @a uid can be handled by ekg2 [when it doesn't matter which plugin] * * @return 1 - if @a uid can be handled by @a plugin
* 0 - if not
* -1 - if @a plugin == NULL */ int valid_plugin_uid(plugin_t *plugin, const char *uid) { int valid = 0; char *tmp; if (!plugin) { debug_error("valid_plugin_uid() no plugin passed. In current api, it means than something really bad happen.\n"); return -1; } tmp = xstrdup(uid); query_emit(plugin, "protocol-validate-uid", &tmp, &valid); xfree(tmp); return (valid > 0); } /** * get_uid_any() * * Return and checks if uid passed @a text is proper for at least one session, or it's nickname of smb on @a session userlist * * @param session - session to search for item on userlist * @param text - uid to check for, if '$' then check current window. * * @sa get_uid() - to search only specified session. * * @return If we found proper uid for @a text, than return it. Otherwise NULL */ const char *get_uid_any(session_t *session, const char *text) { const char *uid = get_uid(session, text); if (!session) return uid; if (!uid && !xstrcmp(text, "$")) text = window_current->target; return uid ? uid : valid_uid(text) ? text : NULL; } /** * get_uid() * * Return and checks if uid passed @a text is proper for @a session or it's nickname of smb on @a session userlist. * * @note It also work with userlist_find() and if @a text is nickname of smb in session userlist.. * Than it return uid of this user. * So you shouldn't call userlist_find() with get_uid() as param, cause it's senseless * userlist_find() don't check for "$" target, so you must do it by hand. Rest is the same. * If there are such user: * userlist_find(s, get_uid(s, target)) return the same as userlist_find(s, target)
* If not, even userlist_find(s, get_uid(s, get_uid(s, get_uid(s, target)))) won't help * * @param session - session to check for, if NULL check all sessions (it doesn't look at userlists, in this mode) * @param text - uid to check for, if '$' than check current window. * * @sa userlist_find() * @sa get_nickname() - to look for nickname.. * @sa get_uid_any() - to do all session searching+specified session userlist search.. * This function does only all session searching if @a session is NULL... and than * it doesn't look at userlist. Do you feel difference? * * @return If we found proper uid for @a text, than return it. Otherwise NULL */ const char *get_uid(session_t *session, const char *text) { userlist_t *u; if (text && !xstrcmp(text, "$")) text = window_current->target; if (!session) return valid_uid(text) ? text : NULL; u = userlist_find(session, text); if (u && u->uid) return u->uid; if (valid_plugin_uid(session->plugin, text) == 1) return text; return NULL; } /* * get_nickname() * * if given text is nickname it returns the same, if it is * an uid it returns its nickname (if exists), if there is * no nickname it returns uid, else if contacts doesnt exist * it returns text if it is a correct uid, else NULL */ const char *get_nickname(session_t *session, const char *text) { userlist_t *u; if (text && !xstrcmp(text, "$")) text = window_current->target; if (!session) return valid_uid(text) ? text : NULL; u = userlist_find(session, text); if (u && u->nickname) return u->nickname; if (u && u->uid) return u->uid; if (valid_plugin_uid(session->plugin, text) == 1) return text; return NULL; } /* * get_user_name() * * returns users first name or nick name or NULL * */ char *get_user_name(userlist_t *u) { char *name; if (!u) return NULL; name = (char *)user_private_item_get(u, "first_name"); return (name && *name) ? name : u->nickname; } /* * format_user() * * zwraca adny (ew. kolorowy) tekst opisujcy dany numerek. jeli jest * w naszej licie kontaktw, formatuje uywajc `known_user', w przeciwnym * wypadku uywa `unknown_user'. wynik jest w statycznym buforze. * * - uin - numerek danej osoby. */ const char *format_user(session_t *session, const char *uid) { userlist_t *u = userlist_find(session, uid); static char buf[256], *tmp; if (!u || !u->nickname) tmp = format_string(format_find("unknown_user"), uid, uid); else tmp = format_string(format_find("known_user"), u->nickname, uid); g_strlcpy(buf, tmp, sizeof(buf)); xfree(tmp); return buf; } /* * ignored_remove() * * usuwa z listy ignorowanych numerkw. * * - uin. */ int ignored_remove(session_t *session, const char *uid) { userlist_t *u = userlist_find(session, uid); char *tmps, *tmp; struct ekg_group *gl; int level, tmp2 = 0; if (!u) return -1; if (!(level = ignored_check(session,uid))) return -1; for (gl = u->groups; gl; gl = gl->next) { struct ekg_group *g = gl; if (xstrncasecmp(g->name, "__ignored", 9)) continue; gl = ekg_groups_removei(&u->groups, g); } if (!u->nickname && !u->groups) { userlist_remove(session, u); return 0; } tmps = xstrdup(session->uid); tmp = xstrdup(u->uid); query_emit(NULL, "protocol-ignore", &tmps, &tmp, &level, &tmp2); xfree(tmps); xfree(tmp); if ((level & IGNORE_STATUS || level & IGNORE_STATUS_DESCR)) { query_emit(NULL, "protocol-unignore", &u, &session); } return 0; } /* * ignored_add() * * dopisuje do listy ignorowanych numerkw. * * - uin. * - level. */ int ignored_add(session_t *session, const char *uid, ignore_t level) { userlist_t *u; char *tmps, *tmp; int oldlevel = 0; if (ignored_check(session, uid)) return -1; if (!(u = userlist_find(session, uid))) u = userlist_add(session, uid, NULL); tmp = saprintf("__ignored_%d", level); ekg_group_add(u, tmp); xfree(tmp); if (level & IGNORE_STATUS) u->status = EKG_STATUS_NA; /* maybe EKG_STATUS_UNKNOWN would be better? */ if (level & IGNORE_STATUS_DESCR) { xfree(u->descr); u->descr = NULL; } tmps = xstrdup(session->uid); tmp = xstrdup(u->uid); query_emit(NULL, "protocol-ignore", &tmps, &tmp, &oldlevel, &level); xfree(tmps); xfree(tmp); return 0; } /** * ignored_check() * * czy dany numerek znajduje si na licie ignorowanych. * * @param session - sesja w ktorej mamy szukac uzytkownika * @param uid - uid uzytkownika * */ int ignored_check(session_t *session, const char *uid) { userlist_t *u = userlist_find(session, uid); struct ekg_group *gl; if (!u) return 0; for (gl = u->groups; gl; gl = gl->next) { struct ekg_group *g = gl; if (!xstrcasecmp(g->name, "__ignored")) return IGNORE_ALL; if (!xstrncasecmp(g->name, "__ignored_", 10)) return atoi(g->name + 10); } return 0; } /** * ignore_flags() * * zamienia acuch znakw na odpowiedni * poziom ignorowania w postaci liczby. * * @param str * @sa ignore_format * @sa ignore_t * @sa ignore_labels * * @return zwraca bitmaske opisana przez str */ int ignore_flags(const char *str) { int x, y, ret = 0; char **arr; if (!str) return ret; arr = array_make(str, "|,:", 0, 1, 0); for (x = 0; arr[x]; x++) { if (!xstrcmp(arr[x], "*")) { ret = IGNORE_ALL; break; } for (y = 0; ignore_labels[y].name; y++) if (!xstrcasecmp(arr[x], ignore_labels[y].name)) ret |= ignore_labels[y].level; } g_strfreev(arr); return ret; } /** * ignore_format() * * zwraca statyczny acuch znakw reprezentujcy * dany poziom ignorowania. * * @param level - poziom ignorowania, bitmaska z `enum ignore_t' * @sa ignore_flags * @sa ignore_t * @sa ignore_labels * * @return zwraca statyczny bufor opisujacy bitmaske za pomoca `ignore_labels` */ const char *ignore_format(int level) { static char buf[200]; int i, comma = 0; buf[0] = 0; if (level == IGNORE_ALL) return "*"; for (i = 0; ignore_labels[i].name; i++) { if (level & ignore_labels[i].level) { if (comma++) g_strlcat(buf, ",", sizeof(buf)); g_strlcat(buf, ignore_labels[i].name, sizeof(buf)); } } return buf; } /** * ekg_group_add() * * dodaje uytkownika do podanej grupy. * * @param u - wpis usera, * @param group - nazwa grupy. * * @return -1 jesli juz user jest w tej grupie, lub zle parametry. 0 gdy dodano. */ int ekg_group_add(userlist_t *u, const char *group) { struct ekg_group *g, *gl; if (!u || !group) return -1; for (gl = u->groups; gl; gl = gl->next) { g = gl; if (!xstrcasecmp(g->name, group)) return -1; } g = xmalloc(sizeof(struct ekg_group)); g->name = xstrdup(group); ekg_groups_add(&u->groups, g); return 0; } /** * ekg_group_remove() * * usuwa uytkownika z podanej grupy. * * @param u - wpis usera, * @param group - nazwa grupy. * * @return 0 jeli si udao, inaczej -1. */ int ekg_group_remove(userlist_t *u, const char *group) { struct ekg_group *gl; if (!u || !group) return -1; for (gl = u->groups; gl; gl = gl->next) { struct ekg_group *g = gl; if (!xstrcasecmp(g->name, group)) { (void) ekg_groups_removei(&u->groups, g); return 0; } } return -1; } /** * ekg_group_member() * * sprawdza czy uytkownik jest czonkiem danej grupy. * * @param u - uzytkownik, ktorego chcemy sprawdzic * @param group - grupa ktora chcemy sprawdzic * * @return 1 jeli tak, 0 jeli nie. */ int ekg_group_member(userlist_t *u, const char *group) { struct ekg_group *gl; if (!u || !group) return 0; for (gl = u->groups; gl; gl = gl->next) { struct ekg_group *g = gl; if (!xstrcasecmp(g->name, group)) return 1; } return 0; } /** * group_init() * * inicjuje list grup uytkownika na podstawie danego cigu znakw, * w ktrym kolejne nazwy grup s rozdzielone przecinkiem. * * @param names - nazwy grup. * * @return zwraca list `struct group' jeli si udao, inaczej NULL. */ struct ekg_group *group_init(const char *names) { struct ekg_group *gl = NULL; char **groups; int i; if (!names) return NULL; groups = array_make(names, ",", 0, 1, 0); for (i = 0; groups[i]; i++) { struct ekg_group *g = xmalloc(sizeof(struct ekg_group)); g->name = groups[i]; ekg_groups_add(&gl, g); } /* NOTE: we don't call here g_strfreev() cause we use items of this * array @ initing groups. We don't use strdup() */ xfree(groups); return gl; } /** * group_to_string() * * zmienia list grup na cig znakw rodzielony przecinkami. * * @param groups - lista grup. * @param meta - czy doczy ,,meta-grupy''? * @param sep - czy oddziela przecinkiem _i_ spacj? * * @return zwraca zaalokowany cig znakw lub NULL w przypadku bdu. */ char *group_to_string(struct ekg_group *groups, int meta, int sep) { string_t foo = string_init(NULL); struct ekg_group *gl; int comma = 0; for (gl = groups; gl; gl = gl->next) { struct ekg_group *g = gl; if (!meta && !xstrncmp(g->name, "__", 2)) { comma = 0; /* mg: why? */ continue; } if (comma) string_append(foo, (sep) ? ", " : ","); comma = 1; string_append(foo, g->name); } return string_free(foo, 0); } /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/ekg/userlist.h000066400000000000000000000174071175142753400167570ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2001-2003 Wojtek Kaniewski * Robert J. Wony * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __EKG_USERLIST_H #define __EKG_USERLIST_H #include "ekg2-config.h" #include "win32.h" #include #ifndef NO_POSIX_SYSTEM #include #include #endif #include #include #include #include "dynstuff.h" #include "sessions.h" #include "plugins.h" #include "windows.h" #ifdef __cplusplus extern "C" { #endif /** * userlist_t is used to manage all info about user.
* It's used not only to manage contacts in roster, but also to manage people in chat or conference * * @bug There are two private fields [u->priv_data and u->priv] one needs to be removed. */ typedef struct userlist { struct userlist *next; const char *uid; /**< uin in form protocol:id */ char *nickname; /**< nickname */ struct ekg_group *groups; /**< list_t with ekg_group
* Groups to which this user belongs like: work, friends, family..
* It's also used internally by ekg2, for example when user is ignore he has group with name: __ignore */ status_t status; /**< current status */ char *descr; /**< description of status. */ char *descr1line; /**< description of status without \r\n. */ struct ekg_resource *resources; /**< list_t with ekg_resource_t
It's used to handle Jabber resources, and also by irc friendlist. */ time_t last_seen; /**< Last time when user was available [when u->status was > notavail] */ char *foreign; /**< For compatilibity with ekg1 userlist. */ void *priv; /**< Private data for protocol plugin. */ unsigned int blink : 1; /**< Blink userlist entry (message) */ unsigned int typing : 1; /**< User is composing */ status_t last_status; /**< Lastseen status */ char *last_descr; /**< Lastseen description */ time_t status_time; /**< From when we have this status, description */ void *priv_data; /**< Alternate private data, used by ncurses plugin */ private_data_t *priv_list; /* New user private data */ } userlist_t; typedef enum { EKG_XSTATE_BLINK = 1, EKG_XSTATE_TYPING = 2 } xstate_t; /** * userlist_privhandler_func_t - here we declare possible options for 'function' arg in USERLIST_PRIVHANDLE * * All of them, excluding EKG_USERLIST_PRIVHANDLER_FREE, should alloc&init priv if needed */ typedef enum { EKG_USERLIST_PRIVHANDLER_FREE = 0, /**< Free private data (called when freeing userlist_t) */ EKG_USERLIST_PRIVHANDLER_GET, /**< Return private data ptr, arg is void** for ptr */ EKG_USERLIST_PRIVHANDLER_READING, /**< Called when reading userlist file,
* 1st arg is char*** with data array,
* 2nd arg is int* with array element count * you can assume it's always at least 7
* Please bear in mind that this query is called * at the very beginning of userlist_add_entry() */ EKG_USERLIST_PRIVHANDLER_WRITING, /**< Called when writing userlist file, arg is char*** with data array */ EKG_USERLIST_PRIVHANDLER_SETVAR_BYNAME = 0xC0, /**< Set private 'variable' by name, args care char** with var name * and char** with value (will be duplicated) */ } userlist_privhandler_func_t; /** * ekg_resource_t is used to manage userlist_t resources.
* For example jabber resources, or irc friendlist */ typedef struct ekg_resource { struct ekg_resource *next; char *name; /**< name of resource */ status_t status; /**< status, like u->status [status of resource] */ char *descr; /**< descr, like u->descr [description of resource] */ int prio; /**< prio of resource [priority of this resource] */ void *priv_data; /**< priv, like u->priv_data [private data info/struct] */ } ekg_resource_t; /** * struct ekg_group is used to manage userlist_t groups. */ struct ekg_group { struct ekg_group *next; char *name; /**< name of group */ }; typedef enum { IGNORE_STATUS = 0x01, IGNORE_STATUS_DESCR = 0x02, IGNORE_MSG = 0x04, IGNORE_DCC = 0x08, IGNORE_EVENTS = 0x10, IGNORE_NOTIFY = 0x20, IGNORE_XOSD = 0x40, IGNORE_LOG = 0x80, IGNORE_ALL = 0xFF } ignore_t; struct ignore_label { ignore_t level; char *name; }; #define IGNORE_LABELS_MAX 9 extern struct ignore_label ignore_labels[IGNORE_LABELS_MAX]; #ifndef EKG2_WIN32_NOFUNCTION int userlist_read(session_t* session); void userlist_write(session_t *session); void userlist_clear_status(session_t *session, const char *uid); userlist_t *userlist_add(session_t *session, const char *uid, const char *nickname); userlist_t *userlist_add_u(userlist_t **userlist, const char *uid, const char *nickname); void userlist_add_entry(session_t *session,const char *line); int userlist_remove(session_t *session, userlist_t *u); int userlist_remove_u(userlist_t **userlist, userlist_t *u); int userlist_replace(session_t *session, userlist_t *u); userlist_t *userlist_find(session_t *session, const char *uid); userlist_t *userlist_find_u(userlist_t **userlist, const char *uid); #define userlist_find_n(a, b) userlist_find(session_find(a), b) void userlist_free(session_t *session); void userlists_destroy(userlist_t **userlist); void *userlist_private_get(plugin_t *plugin, userlist_t *u); /* u->resource */ ekg_resource_t *userlist_resource_add(userlist_t *u, const char *name, int prio); ekg_resource_t *userlist_resource_find(userlist_t *u, const char *name); void userlist_resource_remove(userlist_t *u, ekg_resource_t *r); int ignored_add(session_t *session, const char *uid, ignore_t level); int ignored_remove(session_t *session, const char *uid); int ignored_check(session_t *session, const char *uid); int ignore_flags(const char *str); const char *ignore_format(int level); int ekg_group_add(userlist_t *u, const char *group); int ekg_group_remove(userlist_t *u, const char *group); int ekg_group_member(userlist_t *u, const char *group); char *group_to_string(struct ekg_group *l, int meta, int sep); struct ekg_group *group_init(const char *groups); int valid_nick(const char *nick); int valid_plugin_uid(plugin_t *plugin, const char *uid); const char *format_user(session_t *session, const char *uid); const char *get_uid(session_t *session, const char *text); const char *get_uid_any(session_t *session, const char *text); const char *get_nickname(session_t *session, const char *text); char *get_user_name(userlist_t *u); #endif #define user_private_item_get_safe(user, name, result) \ private_item_get_safe(&(user)->priv_list, name, result) #define user_private_item_get(user, name) \ private_item_get(&(user)->priv_list, name) #define user_private_item_get_int_safe(user, name, result) \ private_item_get_int_safe(&(user)->priv_list), name, int *result) #define user_private_item_get_int(user, name) \ private_item_get_int(&(user)->priv_list, name) #define user_private_item_set(user, name, value) \ private_item_set(&(user)->priv_list, name, value) #define user_private_item_set_int(user, name, value) \ private_item_set_int(&(user)->priv_list, name, value) #define user_private_items_destroy(user) \ private_items_destroy(&(user)->priv_list); #ifdef __cplusplus } #endif #endif /* __EKG_USERLIST_H */ /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/ekg/vars.c000066400000000000000000000436431175142753400160540ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2001-2004 Wojtek Kaniewski * Robert J. Wony * Leszek Krupiski * Adam Mikuta * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #include #include #include #include void changed_session_locks(const char *varname); /* sessions.c */ gboolean console_charset_is_utf8; const char *console_charset; GSList *variables = NULL; static gint variable_compare(gconstpointer a, gconstpointer b) { variable_t *data1 = (variable_t *) a; variable_t *data2 = (variable_t *) b; return xstrcasecmp(data1->name, data2->name); } static void variables_add(variable_t *v) { variables = g_slist_insert_sorted(variables, v, variable_compare); } /* * dd_*() * * funkcje informujce, czy dana grupa zmiennych ma zosta wywietlona. * rwnie dobrze mona byo przekaza wskanik do zmiennej, ktra musi * by rna od zera, ale dziki funkcjom nie trzeba bdzie miesza w * przyszoci. */ static int dd_sound(const char *name) { return (config_sound_app != NULL); } static int dd_color(const char *name) { return (config_display_color); } static int dd_beep(const char *name) { return (config_beep); } /* * variable_init() * * inicjuje list zmiennych. */ void variable_init() { variable_add(NULL, ("auto_save"), VAR_INT, 1, &config_auto_save, changed_auto_save, NULL, NULL); variable_add(NULL, ("auto_user_add"), VAR_BOOL, 1, &config_auto_user_add, NULL, NULL, NULL); variable_add(NULL, ("away_reason"), VAR_STR, 1, &config_away_reason, NULL, NULL, NULL); variable_add(NULL, ("back_reason"), VAR_STR, 1, &config_back_reason, NULL, NULL, NULL); variable_add(NULL, ("beep"), VAR_BOOL, 1, &config_beep, NULL, NULL, NULL); variable_add(NULL, ("beep_chat"), VAR_BOOL, 1, &config_beep_chat, NULL, NULL, dd_beep); variable_add(NULL, ("beep_msg"), VAR_BOOL, 1, &config_beep_msg, NULL, NULL, dd_beep); variable_add(NULL, ("beep_notify"), VAR_BOOL, 1, &config_beep_notify, NULL, NULL, dd_beep); variable_add(NULL, ("completion_char"), VAR_STR, 1, &config_completion_char, NULL, NULL, NULL); variable_add(NULL, ("completion_notify"), VAR_MAP, 1, &config_completion_notify, NULL, variable_map(4, 0, 0, "none", 1, 2, "add", 2, 1, "addremove", 4, 0, "away"), NULL); /* It's very, very special variable; shouldn't be used by user */ variable_add(NULL, ("config_version"), VAR_INT, 2, &config_version, NULL, NULL, NULL); variable_add(NULL, ("dcc_dir"), VAR_STR, 1, &config_dcc_dir, NULL, NULL, NULL); variable_add(NULL, ("debug"), VAR_BOOL, 1, &config_debug, NULL, NULL, NULL); /* variable_add(NULL, ("default_protocol"), VAR_STR, 1, &config_default_protocol, NULL, NULL, NULL); */ variable_add(NULL, ("default_status_window"), VAR_BOOL, 1, &config_default_status_window, NULL, NULL, NULL); variable_add(NULL, ("display_ack"), VAR_MAP, 1, &config_display_ack, NULL, variable_map(6, 0, 0, "none", 1, 0, "delivered", 2, 0, "queued", 4, 0, "dropped", 8, 0, "tempfail", 16, 0, "unknown"), NULL); variable_add(NULL, ("display_blinking"), VAR_BOOL, 1, &config_display_blinking, changed_display_blinking, NULL, NULL); variable_add(NULL, ("display_color"), VAR_INT, 1, &config_display_color, NULL, NULL, NULL); variable_add(NULL, ("display_color_map"), VAR_STR, 1, &config_display_color_map, NULL, NULL, dd_color); variable_add(NULL, ("display_crap"), VAR_BOOL, 1, &config_display_crap, NULL, NULL, NULL); variable_add(NULL, ("display_day_changed"), VAR_BOOL, 1, &config_display_day_changed, NULL, NULL , NULL); variable_add(NULL, ("display_notify"), VAR_MAP, 1, &config_display_notify, NULL, variable_map(4, 0, 0, "none", 1, 2, "all", 2, 1, "significant", 4, 0, "unknown_too"), NULL); variable_add(NULL, ("display_sent"), VAR_BOOL, 1, &config_display_sent, NULL, NULL, NULL); variable_add(NULL, ("display_welcome"), VAR_BOOL, 1, &config_display_welcome, NULL, NULL, NULL); variable_add(NULL, ("emoticons"), VAR_BOOL, 1, &config_emoticons, NULL, NULL, NULL); variable_add(NULL, ("events_delay"), VAR_INT, 1, &config_events_delay, NULL, NULL, NULL); variable_add(NULL, ("expert_mode"), VAR_INT, 1, &config_expert_mode, NULL, NULL, NULL); variable_add(NULL, ("exit_exec"), VAR_STR, 1, &config_exit_exec, NULL, NULL, NULL); variable_add(NULL, ("history_savedups"), VAR_BOOL, 1, &config_history_savedups, NULL, NULL, NULL); variable_add(NULL, ("keep_reason"), VAR_INT, 1, &config_keep_reason, NULL, NULL, NULL); variable_add(NULL, ("last"), VAR_MAP, 1, &config_last, NULL, variable_map(4, 0, 0, "none", 1, 2, "all", 2, 1, "separate", 4, 0, "sent"), NULL); variable_add(NULL, ("last_size"), VAR_INT, 1, &config_last_size, NULL, NULL, NULL); variable_add(NULL, ("nickname"), VAR_STR, 1, &config_nickname, NULL, NULL, NULL); variable_add(NULL, ("make_window"), VAR_MAP, 1, &config_make_window, changed_make_window, variable_map(4, 0, 0, "none", 1, 2, "usefree", 2, 1, "always", 4, 0, "chatonly"), NULL); variable_add(NULL, ("mesg"), VAR_INT, 1, &config_mesg, changed_mesg, variable_map(3, 0, 0, "no", 1, 2, "yes", 2, 1, "default"), NULL); variable_add(NULL, ("query_commands"), VAR_BOOL, 1, &config_query_commands, NULL, NULL, NULL); variable_add(NULL, ("quit_reason"), VAR_STR, 1, &config_quit_reason, NULL, NULL, NULL); variable_add(NULL, ("save_password"), VAR_BOOL, 1, &config_save_password, NULL, NULL, NULL); variable_add(NULL, ("save_quit"), VAR_INT, 1, &config_save_quit, NULL, NULL, NULL); variable_add(NULL, ("session_default"), VAR_STR, 1, &config_session_default, NULL, NULL, NULL); variable_add(NULL, ("send_white_lines"), VAR_BOOL, 1, &config_send_white_lines, NULL, NULL, NULL); variable_add(NULL, ("session_locks"), VAR_INT, 1, &config_session_locks, changed_session_locks, variable_map(3, 0, 0, "off", 1, 2, "flock", 2, 1, "file"), NULL); variable_add(NULL, ("sessions_save"), VAR_BOOL, 1, &config_sessions_save, NULL, NULL, NULL); variable_add(NULL, ("slash_messages"), VAR_INT, 1, &config_slash_messages, NULL, variable_map(3, 0, 0, "off", 1, 2, "moreslashes", 2, 1, "unknowncmd"), NULL); variable_add(NULL, ("sort_windows"), VAR_BOOL, 1, &config_sort_windows, NULL, NULL, NULL); variable_add(NULL, ("sound_app"), VAR_STR, 1, &config_sound_app, NULL, NULL, NULL); variable_add(NULL, ("sound_chat_file"), VAR_FILE, 1, &config_sound_chat_file, NULL, NULL, dd_sound); variable_add(NULL, ("sound_mail_file"), VAR_FILE, 1, &config_sound_mail_file, NULL, NULL, dd_sound); variable_add(NULL, ("sound_msg_file"), VAR_FILE, 1, &config_sound_msg_file, NULL, NULL, dd_sound); variable_add(NULL, ("sound_notify_file"), VAR_FILE, 1, &config_sound_notify_file, NULL, NULL, dd_sound); variable_add(NULL, ("sound_sysmsg_file"), VAR_FILE, 1, &config_sound_sysmsg_file, NULL, NULL, dd_sound); variable_add(NULL, ("speech_app"), VAR_STR, 1, &config_speech_app, NULL, NULL, NULL); variable_add(NULL, ("subject_prefix"), VAR_STR, 1, &config_subject_prefix, NULL, NULL, NULL); variable_add(NULL, ("subject_reply_prefix"), VAR_STR, 1, &config_subject_reply_prefix, NULL, NULL, NULL); variable_add(NULL, ("tab_command"), VAR_STR, 1, &config_tab_command, NULL, NULL, NULL); variable_add(NULL, ("theme"), VAR_THEME, 1, &config_theme, changed_theme, NULL, NULL); variable_add(NULL, ("time_deviation"), VAR_INT, 1, &config_time_deviation, NULL, NULL, NULL); variable_add(NULL, ("timestamp"), VAR_STR, 1, &config_timestamp, changed_config_timestamp, NULL, NULL); /* ? */ variable_add(NULL, ("timestamp_show"), VAR_BOOL, 1, &config_timestamp_show, NULL, NULL, NULL); variable_add(NULL, ("window_session_allow"), VAR_INT, 1, &config_window_session_allow, NULL, variable_map(4, 0, 0, "deny", 1, 6, "uid-capable", 2, 5, "any", 4, 3, "switch-to-status"), NULL); variable_add(NULL, ("windows_layout"), VAR_STR, 2, &config_windows_layout, NULL, NULL, NULL); variable_add(NULL, ("windows_save"), VAR_BOOL, 1, &config_windows_save, NULL, NULL, NULL); } /* * variable_set_default() * * ustawia pewne standardowe wartoci zmiennych * nieliczbowych. */ void variable_set_default() { xfree(config_timestamp); xfree(config_completion_char); xfree(config_display_color_map); xfree(config_subject_prefix); xfree(config_subject_reply_prefix); xfree(config_dcc_dir); config_slash_messages = 1; config_history_savedups = 1; /* save lines matching the previous history entry */ config_dcc_dir = NULL; config_timestamp = xstrdup("\\%H:\\%M:\\%S"); changed_config_timestamp("timestamp"); config_completion_char = xstrdup(":"); config_display_color_map = xstrdup("nTgGbBrR"); config_subject_prefix = xstrdup("## "); config_subject_reply_prefix = xstrdup("Re: "); console_charset_is_utf8 = g_get_charset(&console_charset); } /* * variable_find() * * znajduje struktur variable_t opisujc zmienn o podanej nazwie. * * - name. */ variable_t *variable_find(const char *name) { GSList *vl; int hash; if (!name) return NULL; hash = variable_hash(name); for (vl = variables; vl; vl = vl->next) { variable_t *v = vl->data; if (v->name_hash == hash && !xstrcasecmp(v->name, name)) return v; } return NULL; } /* * variable_map() * * tworzy now map wartoci. jeli ktra z wartoci powinna wyczy inne * (na przykad w ,,log'' 1 wycza 2, 2 wycza 1, ale nie maj wpywu na 4) * naley doda do ,,konflikt''. * * - count - ilo, * - ... - warto, konflikt, opis. * * zaalokowana tablica. */ variable_map_t *variable_map(int count, ...) { variable_map_t *res; va_list ap; int i; res = xcalloc(count + 1, sizeof(variable_map_t)); va_start(ap, count); for (i = 0; i < count; i++) { res[i].value = va_arg(ap, int); res[i].conflicts = va_arg(ap, int); res[i].label = xstrdup(va_arg(ap, char*)); } va_end(ap); return res; } /* * variable_add() * * dodaje zmienn do listy zmiennych. * * - plugin - opis wtyczki, ktra obsuguje zmienn, * - name - nazwa, * - type - typ zmiennej, * - display - czy i jak ma wywietla, * - ptr - wskanik do zmiennej, * - notify - funkcja powiadomienia, * - map - mapa wartoci, * - dyndisplay - funkcja sprawdzajca czy wywietli zmienn. * * zwraca 0 jeli si nie udao, w przeciwnym wypadku adres do strutury. */ variable_t *variable_add(plugin_t *plugin, const char *name, int type, int display, void *ptr, variable_notify_func_t *notify, variable_map_t *map, variable_display_func_t *dyndisplay) { variable_t *v; char *__name; if (!name) return NULL; if (plugin && !xstrchr(name, ':')) __name = saprintf("%s:%s", plugin->name, name); else __name = xstrdup(name); v = xmalloc(sizeof(variable_t)); v->name = __name; v->name_hash = variable_hash(__name); v->type = type; v->display = display; v->ptr = ptr; v->notify = notify; v->map = map; v->dyndisplay = dyndisplay; v->plugin = plugin; variables_add(v); return v; } /* * variable_remove() * * usuwa zmienn. */ int variable_remove(plugin_t *plugin, const char *name) { int hash; GSList *vl; if (!name) return -1; hash = ekg_hash(name); for (vl = variables; vl; vl = vl->next) { variable_t *v = vl->data; if (!v->name) continue; if (hash == v->name_hash && plugin == v->plugin && !xstrcasecmp(name, v->name)) { variables_remove(v); return 0; } } return -1; } /** * on_off() * * @return 1 - If @a value is one of: on, true, yes, tak, 1 [case-insensitive]
* 0 - If @a value is one of: off, false, no, nie, 0 [case-insensitive]
* else -1 */ static int on_off(const char *value) { if (!value) return -1; if (!xstrcasecmp(value, "on") || !xstrcasecmp(value, "true") || !xstrcasecmp(value, "yes") || !xstrcasecmp(value, "tak") || !xstrcmp(value, "1")) return 1; if (!xstrcasecmp(value, "off") || !xstrcasecmp(value, "false") || !xstrcasecmp(value, "no") || !xstrcasecmp(value, "nie") || !xstrcmp(value, "0")) return 0; return -1; } /* * variable_set() * * ustawia warto podanej zmiennej. jeli to zmienna liczbowa lub boolowska, * zmienia cig na liczb. w przypadku boolowskich, rozumie zwroty typu `on', * `off', `yes', `no' itp. jeli dana zmienna jest bitmap, akceptuje warto * w postaci listy flag oraz konstrukcje `+flaga' i `-flaga'. * * - name - nazwa zmiennej, * - value - nowa warto, */ int variable_set(const char *name, const char *value) { variable_t *v = variable_find(name); char *tmpname; int changed = 0; if (!v) return -1; switch (v->type) { case VAR_INT: case VAR_MAP: { const char *p = value; int hex, tmp; if (!value) return -2; if (v->map && v->type == VAR_INT && !xisdigit(*p)) { int i; for (i = 0; v->map[i].label; i++) if (!xstrcasecmp(v->map[i].label, value)) value = ekg_itoa(v->map[i].value); } if (v->map && v->type == VAR_MAP && !xisdigit(*p)) { int i, k = *(int*)(v->ptr); int mode = 0; /* 0 set, 1 add, 2 remove */ char **args; if (*p == '+') { mode = 1; p++; } else if (*p == '-') { mode = 2; p++; } if (!mode) k = 0; args = array_make(p, ",", 0, 1, 0); for (i = 0; args[i]; i++) { int j, found = 0; for (j = 0; v->map[j].label; j++) { if (!xstrcasecmp(args[i], v->map[j].label)) { found = 1; if (mode == 2) k &= ~(v->map[j].value); if (mode == 1) k &= ~(v->map[j].conflicts); if (mode == 1 || !mode) k |= v->map[j].value; } } if (!found) { g_strfreev(args); return -2; } } g_strfreev(args); value = ekg_itoa(k); } p = value; if ((hex = !xstrncasecmp(p, "0x", 2))) p += 2; while (*p && *p != ' ') { if (hex && !xisxdigit(*p)) return -2; if (!hex && !xisdigit(*p)) return -2; p++; } tmp = strtol(value, NULL, 0); if (v->map) { int i; for (i = 0; v->map[i].label; i++) { if ((tmp & v->map[i].value) && (tmp & v->map[i].conflicts)) return -2; } } changed = (*(int*)(v->ptr) != tmp); *(int*)(v->ptr) = tmp; break; } case VAR_BOOL: { int tmp; if (!value) return -2; if ((tmp = on_off(value)) == -1) return -2; changed = (*(int*)(v->ptr) != tmp); *(int*)(v->ptr) = tmp; break; } case VAR_THEME: case VAR_FILE: case VAR_DIR: case VAR_STR: { char **tmp = (char**)(v->ptr); char *oldval = *tmp; if (value) { if (*value == 1) *tmp = base64_decode(value + 1); else *tmp = xstrdup(value); } else *tmp = NULL; changed = xstrcmp(oldval, *tmp); xfree(oldval); break; } default: return -1; } if (v->notify) (v->notify)(v->name); if (!changed) return 1; tmpname = xstrdup(v->name); query_emit(NULL, "variable-changed", &tmpname); xfree(tmpname); return 0; } static void variable_free(void *_data) { variable_t *data = (variable_t *) _data; xfree(data->name); switch (data->type) { case VAR_STR: case VAR_FILE: case VAR_THEME: case VAR_DIR: xfree(*((char**) data->ptr)); *((char**) data->ptr) = NULL; break; default: break; } if (data->map) { int i; for (i = 0; data->map[i].label; i++) xfree(data->map[i].label); xfree(data->map); } xfree(data); } void variables_remove(variable_t *v) { variables = g_slist_remove(variables, v); variable_free(v); } void variables_destroy(void) { g_slist_free_full(variables, variable_free); } /* * variable_help() * * it shows help about variable from file ${datadir}/ekg/vars.txt * or ${datadir}/ekg/plugins/{plugin_name}/vars.txt * * name - name of the variable */ void variable_help(const char *name) { GDataInputStream *f; gchar *type = NULL, *def = NULL, *tmp; const gchar *line, *seeking_name; string_t s; int found = 0; variable_t *v = variable_find(name); if (!v) { print("variable_not_found", name); return; } if (v->plugin && v->plugin->name) { char *tmp2; if (!(f = help_open("vars", v->plugin->name))) { print("help_set_file_not_found_plugin", v->plugin->name); return; } tmp2 = xstrchr(name, ':'); if (tmp2) seeking_name = tmp2+1; else seeking_name = name; } else { if (!(f = help_open("vars", NULL))) { print("help_set_file_not_found"); return; } seeking_name = name; } while ((line = read_line(f))) { if (!xstrcasecmp(line, seeking_name)) { found = 1; break; } } if (!found) { g_object_unref(f); print("help_set_var_not_found", name); return; } line = read_line(f); if ((tmp = xstrstr(line, (": ")))) type = xstrdup(tmp + 2); else type = xstrdup(("?")); line = read_line(f); if ((tmp = xstrstr(line, (": ")))) def = xstrdup(tmp + 2); else def = xstrdup(("?")); print("help_set_header", name, type, def); xfree(type); xfree(def); if (tmp) /* jeli nie jest to ukryta zmienna... */ read_line(f); /* ... pomijamy lini */ s = string_init(NULL); while ((line = read_line(f))) { if (line[0] != '\t') break; if (!xstrncmp(line, ("\t- "), 3) && xstrcmp(s->str, (""))) { print("help_set_body", s->str); string_clear(s); } if (!xstrncmp(line, ("\t"), 1) && xstrlen(line) == 1) { string_append(s, ("\n\r")); continue; } string_append(s, line + 1); if (line[xstrlen(line) - 1] != ' ') string_append_c(s, ' '); } if (xstrcmp(s->str, (""))) print("help_set_body", s->str); string_free(s, 1); if (format_exists("help_set_footer")) print("help_set_footer", name); g_object_unref(f); } /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/ekg/vars.h000066400000000000000000000055631175142753400160600ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2001-2003 Wojtek Kaniewski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __EKG_VARS_H #define __EKG_VARS_H #include #include "dynstuff.h" #include "plugins.h" #ifdef __cplusplus extern "C" { #endif typedef enum { VAR_STR, /* cig znakw */ VAR_INT, /* liczba cakowita */ VAR_BOOL, /* 0/1, tak/nie, yes/no, on/off */ VAR_MAP, /* bitmapa */ VAR_FILE, /* plik */ VAR_DIR, /* katalog */ VAR_THEME /* theme */ } variable_class_t; typedef struct variable_map_t { char *label; /* nazwa wartoci */ int value; /* warto */ int conflicts; /* wartoci, z ktrymi koliduje */ } variable_map_t; typedef void (variable_notify_func_t)(const char *); typedef void (variable_check_func_t)(const char *, const char *); typedef int (variable_display_func_t)(const char *); typedef struct variable { char *name; /* nazwa zmiennej */ plugin_t *plugin; /* wstyczka obsugujca zmienn */ int name_hash; /* hash nazwy zmiennej */ int type; /* rodzaj */ int display; /* 0 bez wartoci, 1 pokazuje, 2 w ogle */ void *ptr; /* wskanik do zmiennej */ variable_check_func_t *check; /* funkcja sprawdzajca czy warto jest * prawidowa */ variable_notify_func_t *notify; /* funkcja wywoywana po zmianie wartoci */ variable_map_t *map; /* mapa wartoci i etykiet */ variable_display_func_t *dyndisplay; /* funkcja sprawdzajca, czy zmienn mona * wywietli na licie zmiennych */ } variable_t; #ifndef EKG2_WIN32_NOFUNCTION extern GSList *variables; void variable_init(); void variable_set_default(); variable_t *variable_find(const char *name); variable_map_t *variable_map(int count, ...); #define variable_hash ekg_hash variable_t *variable_add( plugin_t *plugin, const char *name, int type, int display, void *ptr, variable_notify_func_t *notify, variable_map_t *map, variable_display_func_t *dyndisplay); int variable_set(const char *name, const char *value); void variable_help(const char *name); int variable_remove(plugin_t *plugin, const char *name); void variables_remove(variable_t *v); void variables_destroy(); #endif #ifdef __cplusplus } #endif #endif /* __EKG_VARS_H */ /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/ekg/win32.c000066400000000000000000000052171175142753400160360ustar00rootroot00000000000000#include "win32.h" #ifdef NO_POSIX_SYSTEM #include #include #include #include #include #include #include "debug.h" #define ERRNO_NOT 0 /* not implemented */ HANDLE win32_fork(thread_func_t addr, void *data) { HANDLE ret = (HANDLE) -1; DWORD thread_id = 0; ret = CreateThread(NULL, 0, (void *) addr, data, 0, &thread_id); debug_function("NO_POSIX_SYSTEM: win32_fork() ADDR=0x%x data=0x%x; thread_id = %d result = %d\n", addr, data, thread_id, ret); if (!ret) { /* fails */ return NULL; /* -1 ? */ } return ret; } pid_t fork(void) { debug_function("NO_POSIX_SYSTEM: fork() ON NO POSIX SYSTEM DON'T USE FORK() REIMPLEMENT FUNCTION TO USE win32_fork()\n"); errno = ERRNO_NOT; return -1; } int fchmod(int fildes, mode_t mode) { errno = ERRNO_NOT; return -1; } int pipe(int filedes[2]) { HANDLE pread, pwrite; int res = CreatePipe(&pread, &pwrite, NULL, 0); debug_function("NO_POSIX_SYSTEM: pipe() read=%d write=%d result=%d\n", pread, pwrite, res); if (res == 0) { /* fails, XXX, errno && GetLastError? */ return -1; } filedes[0] = (int) pread; filedes[1] = (int) pwrite; return 0; } int fcntl(int fd, int cmd, long arg) { if (cmd == F_SETFL) { int no_block = -1; if (arg & O_NONBLOCK) { no_block = 1; arg -= O_NONBLOCK; } debug_function("NO_POSIX_SYSTEM: fcntl() fd: %d F_SETFL _NO_BLOCK: %d REST_args: %d\n", fd, no_block, arg); /* XXX */ if (no_block != -1) ioctlsocket(fd, FIONBIO, (u_long *) &no_block); if (arg) { errno = ERRNO_NOT; return -1; } return 0; } debug_function("NO_POSIX_SYSTEM: fcntl() fd: %d cmd:%d arg:%d\n", fd, cmd, arg); errno = ERRNO_NOT; return -1; } int ioctl(int fd, int request, void *flags) { if (request == FIONBIO) { return fcntl(fd, F_SETFL, O_NONBLOCK); } debug_function("NO_POSIX_SYSTEM: ioctl() fd: %d req: %d flags: 0x%x", fd, request, flags); errno = ERRNO_NOT; return -1; } /* code of (gettimeofday && friends) in windows ripped from http://www.tcpdump.org/lists/workers/2005/12/msg00003.html * (c) Gisle Vanem */ #if defined(_MSC_VER) || defined(_MSC_EXTENSIONS) || defined(__WATCOMC__) #define DELTA_EPOCH_IN_USEC 11644473600000000Ui64 #else #define DELTA_EPOCH_IN_USEC 11644473600000000ULL #endif static u_int64_t filetime_to_unix_epoch (const FILETIME *ft) { u_int64_t res = (u_int64_t) ft->dwHighDateTime << 32; res |= ft->dwLowDateTime; res /= 10; /* from 100 nano-sec periods to usec */ res -= DELTA_EPOCH_IN_USEC; /* from Win epoch to Unix epoch */ return res; } int uname(struct utsname *buf) { if (!buf) { errno = EFAULT; return -1; } /* XXX, NO_POSIX_SYSTEM */ return -1; } #endif ekg2-0.4~pre+20120506.1/ekg/win32.h000066400000000000000000000025161175142753400160420ustar00rootroot00000000000000#ifdef __MINGW32__ # define NO_POSIX_SYSTEM "mingw" #else # define EKG2_WIN32_H # undef NO_POSIX_SYSTEM #endif #ifndef EKG2_WIN32_H #define EKG2_WIN32_H #include "ekg2-config.h" #if 0 typedef unsigned long guint32; typedef unsigned short guint16; typedef unsigned char guint8; #endif #include #include #include #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif #undef pipe typedef unsigned __int64 u_int64_t; struct utsname { /* XXX, len */ char sysname[30]; char nodename[30]; char release[30]; char version[30]; char machine[30]; }; #define THREAD(x) DWORD x(void *data) typedef THREAD(thread_func_t); # ifndef EKG2_WIN32_NOFUNCTION pid_t fork(void); /* unimpl */ HANDLE win32_fork(thread_func_t *addr, void *data); /* fcntl.h */ #define F_SETFL 4 #define O_NONBLOCK 04000 int fcntl(int fd, int cmd, long arg); /* ... */ int fchmod(int fildes, mode_t mode); /* unimpl */ int pipe(int *filedes); int ioctl(int fd, int request, void *flags); /* BAD PROTOTYPE. I KNOW. XXX, emulate some things */ int uname(struct utsname *buf); /* emulated ? */ #endif #define EINPROGRESS WSAEINPROGRESS #define fileno(__F) ((__F)->_file) #define sleep(x) Sleep(x * 1000) #ifdef __cplusplus } #endif #endif ekg2-0.4~pre+20120506.1/ekg/windows.c000066400000000000000000000542741175142753400165750ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2002-2003 Wojtek Kaniewski * Pawe Maziarz * 2004 Piotr Kupisiewicz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #include #include #include #include int window_last_id = -1; /* ostatnio wywietlone okno */ window_t *windows = NULL; /* lista okien */ static LIST_ADD_COMPARE(window_new_compare, window_t *) { return data1->id - data2->id; } static LIST_FREE_ITEM(list_window_free, window_t *) { xfree(data->target); xfree(data->alias); userlists_destroy(&(data->userlist)); } static __DYNSTUFF_LIST_ADD_SORTED(windows, window_t, window_new_compare); /* windows_add() */ static __DYNSTUFF_LIST_UNLINK(windows, window_t); /* windows_unlink() */ static __DYNSTUFF_LIST_REMOVE_SAFE(windows, window_t, list_window_free); /* windows_remove() */ __DYNSTUFF_LIST_DESTROY(windows, window_t, list_window_free); /* windows_destroy() */ int config_display_crap = 1; /* czy wywietla mieci? */ window_t *window_current = NULL; /* okno aktualne, zawsze na co musi wskazywa! */ window_t *window_status = NULL; /* okno statusowe, zawsze musi miec dobry adres w pamieci [NULL jest ok] */ window_t *window_debug = NULL; /* okno debugowe, zawsze musi miec dobry adres w pamieci [NULL jest ok] */ /** * window_find_ptr() * * it's search over window list and checks if param @a w is still on that list. * * @note It's possible to find another window with the same address as old one.. it's rather not possible.. however, * It's better if you use other functions... * * @sa window_find() - If you want to search for window target, instead of window ptr. * @sa window_find_s() - If you want to search for window session && target, instead of window ptr. * @sa window_exist - If you want to search for window id, instead of window ptr. * * @param w - window to look for. * * @return It returns @a w if window was found, otherwise NULL. */ window_t *window_find_ptr(window_t *w) { window_t *v; for (v = windows; v; v = v->next) { if (w == v) return w; } return NULL; } /** * window_find_sa() * * Search for an window with given @a target and @a session
* * @param session - window session to search [See also @@ @a session_null_means_no_session] * @param target - window target to search * @param session_null_means_no_session - if you know that this window must belong to given session [NULL, or whatever] so this should be 1. * else NULL @@ @a session will mean that you don't know which session should it be. And it'll search/check * all sessions * * @sa window_find_ptr() - If you want to search for given window ptr. * @sa window_find_s() - macro to window_find_sa(session, target, 1) * @sa window_find() - wrapper to window_find_sa(NULL, target, 0) * * @return pointer to window_t struct if window was founded, else NULL */ window_t *window_find_sa(session_t *session, const char *target, int session_null_means_no_session) { userlist_t *u; window_t *w; if (!target || !xstrcasecmp(target, "__current")) return window_current->id ? window_current : window_status; if ((!xstrcasecmp(target, "__status"))) return window_status; if (!xstrcasecmp(target, "__debug")) return window_debug; for (w = windows; w; w = w->next) { /* if targets match, and (sessions match or [no session was specified, and it doesn't matter to which session window belongs to]) */ if (w->target && ((session == w->session) || (!session && !session_null_means_no_session)) && !xstrcasecmp(target, w->target)) return w; } /* if we don't want session window, code below is useless */ if (!session && session_null_means_no_session) return NULL; if (xstrncmp(target, "__", 2)) { session_t *s; for (s = sessions; s; s = s->next) { /* if sessions mishmash, and it wasn't NULL session, skip this session */ if (session != s && session) continue; /* get_uid() was bad here. Because if even it's uid of user but we don't have it in userlist it'll do nothing. */ if (!(u = userlist_find(s, target))) continue; for (w = windows; w; w = w->next) { /* if there's target, and sessions match [no session specified, or sessions equal, check if entry (from userlist) match */ if ((!session || session == w->session) && w->target) { if (u->nickname && !xstrcasecmp(u->nickname, w->target)) return w; /* XXX, userlist_find() search only for u->nickname or u->uid.. so code below is useless? we can always return w; ? * However userlist_find() also strip resources if preset.. here we don't have it. * maybe it's better, maybe not. Must think about it. * For now leave this code. */ if (!xstrcasecmp(u->uid, w->target)) return w; } } } } return NULL; } /** * window_find() * * Seeks for an window with given @a target
* Wrapper to: window_find_sa(NULL, target, 0); * * @note It's really slow, and you should avoid using it. You'll never know if you found good window... so use window_find_s() * * @param target - window target * * @sa window_find_s() - If you know session. * * @return pointer to window_t struct if window was founded, else NULL * */ window_t *window_find(const char *target) { return window_find_sa(NULL, target, 0); } /* * window_switch() * * przecza do podanego okna. * * - id - numer okna */ void window_switch(int id) { window_t *w; userlist_t *u; int ul_refresh = 0; for (w = windows; w; w = w->next) { if (id != w->id || w->floating) continue; if (id != window_current->id) window_last_id = window_current->id; if (w->id != 0 && w->session) session_current = w->session; window_current = w; query_emit(NULL, "ui-window-switch", &w); /* XXX */ w->act = EKG_WINACT_NONE; if (w->target && w->session && (u = userlist_find(w->session, w->target)) && u->blink) { u->blink = 0; ul_refresh = 1; } if (!(config_make_window & 3) && w->id == 1 && session_current) { userlist_t *ul; session_t *s = session_current; for (ul = s->userlist; ul; ul = ul->next) { userlist_t *u = ul; if (u->blink && !window_find_s(s, u->uid)) { u->blink = 0; ul_refresh = 1; } } } break; } if (ul_refresh) query_emit(NULL, "userlist-refresh"); } /** * window_new() * * Create new window_t, with given @a new_id (if @a new_id != 0) * * @note If target == "$" than it return current window. [POSSIBLE BUG] * If window with such target [it can also be u->uid/u->nickname combination] exists. * than it'll return it. * * @note You shouldn't pass @a new_id here. Because it can break UI stuff. don't ask. it's wrong. Just don't use it. * It'll be possible removed... In case you really need it, you * can talk to the devs, and ask for an id from class: 1000 to * 1999 * * @todo See XXX's * * @param target - name of window * @param session - session of this window * @param new_id - if different than 0, than window will take this id. * * @return window_t struct */ window_t *window_new(const char *target, session_t *session, int new_id) { window_t *w; if (target) { window_t *w; /* XXX, we don't check new_id here. stupido */ if (!xstrcmp(target, "$")) { /* XXX, what about sessions, check if match? */ return window_current; } w = window_find_s(session, target); if (w) return w; } /* XXX, check new_id ? */ /* if (new_id != 0 && (w = window_exist(new_id)) return w; */ /* if no new_id given, than let's search for window id.. */ if (new_id == 0) { window_t *v = windows; /* set to the beginning of the window list */ int id = 2; /* [XXX] set to first valid id? */ /* XXX, after it, we exactly know where to put new window to list, without list_add_sorted() we can do list_add_beggining() * but it'll ugly code. So not doing it :) */ /* we can do this stuff. because windows are sorted by id */ while (v) { window_t *w = v; v = v->next; /* goto next window */ if (w->id < 2) /* [RESERVED CLASS: 0-1] 0 for __debug, 1 for __status */ continue; /* if current window is larger than current id... than we found good id! */ if (w->id > id) break; if (w->id >= WINDOW_RESERVED_MIN_ID && w->id <= WINDOW_RESERVED_MAX_ID) { id = WINDOW_RESERVED_MAX_ID + 1; continue; } id = w->id+1; /* current window+1 */ } new_id = id; } /* debug window */ if (new_id == -1) new_id = 0; w = xmalloc(sizeof(window_t)); w->id = new_id; /* domylne rozmiary zostan dostosowane przez ui */ /* w->top = 0; w->left = 0; */ /* xmalloc memset() to 0 memory */ w->width = 1; w->height = 1; w->target = xstrdup(target); w->session = session; /* w->userlist = NULL; */ /* xmalloc memset() to 0 memory */ windows_add(w); query_emit(NULL, "ui-window-new", &w); /* XXX */ return w; } /** * window_print() * * Print fstring_t @a line to window * * @param w - window * @param line - line */ void window_print(window_t *w, fstring_t *line) { g_assert(w); g_assert(line); if (!line->ts) line->ts = time(NULL); query_emit(NULL, "ui-window-print", &w, &line); } /* * window_next() * * przechodzi do kolejnego okna. */ void window_next() { window_t *next = NULL, *w; int passed = 0; for (w = windows; w; w = w->next) { if (passed && !w->floating) { next = w; break; } if (w == window_current) passed = 1; } if (!next) next = window_status; window_switch(next->id); } /* * window_prev() * * przechodzi do poprzedniego okna. */ /* XXX, need check */ void window_prev() { window_t *prev = NULL, *w; for (w = windows; w; w = w->next) { if (w->floating) continue; if (w == window_current && w != windows) break; prev = w; } if (!prev) return; if (!prev->id) { for (w = windows; w; w = w->next) { if (!w->floating) prev = w; } } window_switch(prev->id); } /** * window_kill() * * Remove given window.
* If it's __status window, and w->target than display nice message about closing talk, else display message about no possibility to close status window
* * @note You cannot remove here __status and __debug windows.
* You must do it by hand like in ekg_exit() but if you want do it.
* Set @a window_debug and window_status for proper values.
* ekg2 core need them. * * @bug Possible bug with sort_windows. Can anyone who wrote it look at it? * * @param w - given window. */ void window_kill(window_t *w) { int id; if (!w) return; id = w->id; if (id == 1 && w->target) { print("query_finished", w->target, session_name(w->session)); list_window_free(w); w->target = NULL; /* w->session = NULL; */ query_emit(NULL, "ui-window-target-changed", &w); return; } if (w == window_status) { print("window_kill_status"); return; } if (w == window_debug) return; if (w == window_current) /* if it's current window. goto previous one. */ window_prev(); /* if config_sort_windows set, and it was not floating window... than resort stuff. */ if (config_sort_windows && !w->floating) { window_t *w; for (w = windows; w; w = w->next) { if (w->floating) continue; /* XXX, i'm leaving it. however if we set sort_windows for example when we have: windows: 1, 3, 5, 7 and we will remove 3.. We'll still have: 1, 4, 6 not: 1, 2, 3 bug? */ if (w->id > 1 && w->id > id) w->id--; } } query_emit(NULL, "ui-window-kill", &w); windows_remove(w); } /** * window_exist() * * check if window with @a id exist * * @param id - id of window. * * @sa window_find() - If you want to search for window target, instead of window id. * @sa window_find_s() - If you want to search for window session && target, instead of window id. * @sa window_find_ptr() - if you want to search for window pointer, instead of window id. * * @return window_t *, with id specified by @a id, or NULL if such window doesn't exists. */ window_t *window_exist(int id) { window_t *w; for (w = windows; w; w = w->next) { if (w->id == id) return w; } return NULL; } /** * window_move() * * swap windows (swap windows @a id -> change sequence of them in UI) * * @param first - 1st window id. * @param second - 2nd window id. * * @todo XXX: Rename to _swap, and make some real move. */ static void window_move(int first, int second) { window_t *w1, *w2; if (!(w1 = window_exist(first)) || !(w2 = window_exist(second))) return; windows_unlink(w1); windows_unlink(w2); w1->id = second; w2->id = first; windows_add(w1); windows_add(w2); } /* really move window, i.e. insert it at given id and move right all other windows */ static void window_real_move(int source, int dest) { window_t *ws, *wd; if (!(ws = window_exist(source))) return; if ((wd = window_exist(dest))) { /* need to move ids */ window_t *wl; if (dest < source) { for (wl = windows; wl; wl = wl->next) { window_t *w = wl; if (w->id >= dest && w->id < source) (w->id)++; /* XXX: move only when ids overlap? */ } } else { for (wl = windows; wl; wl = wl->next) { window_t *w = wl; if (w->id <= dest && w->id > source) (w->id)--; /* XXX: move only when ids overlap? */ } } } ws->id = dest; windows_unlink(ws); windows_add(ws); } /** * window_target() * * @param window - window * @todo Make it const? * * @return Never NULL pointer [to don't care about it] look below for more details: * if @a window->target is not NULL return it
* else:
* - __current if @a window is NULL
* - __status if @a window->id == 1
* - __debug if @a window->id == 0
* else return "" */ char *window_target(window_t *window) { if (!window) return "__current"; if (window->target) return window->target; else if (window->id == 1) return "__status"; else if (window->id == 0) return "__debug"; else return ""; } /* * * komenda ekg obsugujca okna */ COMMAND(cmd_window) { const int par0_len = xstrlen(params[0]); const int par0_matchlen = par0_len > 2 ? par0_len : 2; if (!xstrcmp(name, "clear") || (params[0] && !xstrncasecmp(params[0], "clear", par0_matchlen))) { window_t *w = g_memdup(window_current, sizeof(window_t)); query_emit(NULL, "ui-window-clear", &w); g_free(w); return 0; } if (!params[0] || !xstrncasecmp(params[0], "list", par0_matchlen)) { window_t *w; for (w = windows; w; w = w->next) { if (w->id) { if (w->target) { if (!w->floating) printq("window_list_query", ekg_itoa(w->id), w->alias ? w->alias : w->target); else printq("window_list_floating", ekg_itoa(w->id), ekg_itoa(w->left), ekg_itoa(w->top), ekg_itoa(w->width), ekg_itoa(w->height), w->target); } else printq("window_list_nothing", ekg_itoa(w->id)); } } return 0; } if (!xstrncasecmp(params[0], "active", par0_matchlen)) { window_t *w; int a, id = 0; for (a=EKG_WINACT_IMPORTANT; !id && a>EKG_WINACT_NONE; a--) for (w = windows; w; w = w->next) { if ((w->act==a) && !w->floating && w->id) { id = w->id; break; } } if (id) window_switch(id); return 0; } if (!xstrncasecmp(params[0], "new", par0_matchlen)) { window_t *w = window_new(params[1], session, 0); w->session = window_current->session; if (!w->session && sessions) w->session = sessions; if (!w->floating) window_switch(w->id); return 0; } if (atoi(params[0])) { window_switch(atoi(params[0])); return 0; } if (!xstrncasecmp(params[0], "switch", par0_matchlen)) { if (!params[1] || (!atoi(params[1]) && xstrcmp(params[1], ("0")))) printq("not_enough_params", name); else window_switch(atoi(params[1])); return 0; } if (!xstrncasecmp(params[0], "last", par0_matchlen)) { window_switch(window_last_id); return 0; } if (!xstrncasecmp(params[0], "kill", par0_matchlen)) { window_t *w = window_current; if (params[1]) { window_t *ww; for (w = NULL, ww = windows; ww; ww = ww->next) { if (ww->id == atoi(params[1])) { w = ww; break; } } if (!w) { printq("window_noexist"); return -1; } } window_kill(w); window_switch(window_current->id); return 0; } /* two first chars are same as with new, so it's like par0_matchlen was at least 3 */ if (!xstrncasecmp(params[0], "next", par0_matchlen)) { window_next(); return 0; } if (!xstrncasecmp(params[0], "prev", par0_matchlen)) { window_prev(); return 0; } if (!xstrncasecmp(params[0], "move", par0_matchlen) || !xstrncasecmp(params[0], "swap", par0_matchlen)) { int source, dest; if (!window_current) return -1; if (!params[1]) { printq("not_enough_params", name); return -1; } source = (params[2]) ? atoi(params[2]) : window_current->id; if (!source) { printq("window_invalid_move", ekg_itoa(source)); return -1; } if (!window_exist(source)) { printq("window_doesnt_exist", ekg_itoa(source)); return -1; } if (source == 1) { printq("window_cannot_move_status"); return -1; } /* source is okey, now we are checking destination window */ if (!xstrcasecmp(params[1], ("left"))) dest = source - 1; else if (!xstrcasecmp(params[1], ("right"))) dest = source + 1; else dest = atoi(params[1]); if (!dest) { printq("window_invalid_move", ekg_itoa(dest)); return -1; } if (dest == 1) { printq("window_cannot_move_status"); return -1; } if (dest == source) return 0; if (!xstrncasecmp(params[0], "swap", par0_matchlen)) { if (!window_exist(dest)) window_new(NULL, NULL, dest); window_move(source, dest); } else window_real_move(source, dest); window_switch(dest); return 0; } if (!xstrncasecmp(params[0], "refresh", par0_matchlen)) { query_emit(NULL, "ui-refresh"); return 0; } printq("invalid_params", name, params[0]); return 0; } /* NOTE: XXX, if you talk with someone on window_status/ window_debug we fail */ void window_session_set(window_t *w, session_t *new_session) { static int lock; if (!w || !new_session) /* XXX, new_session == NULL? */ return; if (w->session == new_session) return; w->session = new_session; if (w == window_current) { session_current = new_session; query_emit(NULL, "session-changed"); } query_emit(NULL, "ui-window-target-changed", &w); /* let's sync window_status->session with window_debug->session */ if (lock == 0) { lock = 1; if (w == window_debug) window_session_set(window_status, new_session); if (w == window_status) window_session_set(window_debug, new_session); lock = 0; } } /** * window_session_cycle() * * Change session of given window to next good one (based on @a config_window_session_allow value) * * @note behaviour of window_session_cycle() based on values of config_window_session_allow: * 0 - change session only if w->target == NULL * 1 - like 0 + if w->target is set than new session must accept that uid [default && other values] * 2 - change to any next session * 4 - jump to status window before cycling. * * @note If w->session was changed than UI_WINDOW_TARGET_CHANGED will be emited. * If w == window_current than SESSION_CHANGED will be emited also. * * @todo Gdy config_window_session_allow == 2, to najpierw sprobowac znalezc dobra sesje a potem jesli nie to * nastepna? * * @param w - window * * @return 0 - if session of window was changed * -1 - if not */ int window_session_cycle(window_t *w) { session_t *s; session_t *new_session = NULL; int once = 0; const char *uid; const char *nickname; if (!w || !sessions) { return -1; } /* @ab config_window_session_allow == 4: don't change session when we have open talk in __status window */ /* XXX, change to __status? */ if ((config_window_session_allow == 0 && w->target) || (config_window_session_allow == 4 && window_status->target)) { print("session_cannot_change"); return -1; } if (config_window_session_allow == 4) { /* higher level of magic */ /* switch to status window */ command_exec(NULL, NULL, "/window switch 1", 1); /* XXX, window_switch(window_status->id); */ /* and change switching order * we don't need to emit anything, because this function will (should?) change it again */ window_status->session = w->session; w = window_status; } /* find sessions->(...next..) == w->session */ for (s = sessions; s; s = s->next) { if (w->session == s) { s = s->next; break; } } if (!(uid = get_uid(w->session, w->target))) /* try to get old uid, because in w->target we can have nickname, and it could be other person */ uid = w->target; again: if (s) { session_t *k; for (k = s; k; k = k->next) { if (k == w->session) break; once = 1; if (config_window_session_allow == 2 || !w->target || (config_window_session_allow != 0 && get_uid(k, uid))) { new_session = k; break; } } } if (!new_session && s != sessions) { s = sessions; goto again; } if (!new_session) { /* not found */ if (once) { /* here config_window_session_allow don't allow to change session */ print("session_cannot_change"); } return -1; } if ((nickname = get_nickname(new_session, uid))) { /* if we've got nickname for old uid, than use it as w->target */ char *tmp = w->target; w->target = xstrdup(nickname); xfree(tmp); } else if (w->target != uid) { /* if not, than change w->target (possibility nickname) with uid value [XXX, untested behavior] */ char *tmp = w->target; w->target = xstrdup(uid); xfree(tmp); } window_session_set(w, new_session); return 0; } int window_lock_inc(window_t *w) { if (!w) return -1; w->lock++; return 0; } int window_lock_dec(window_t *w) { if (!w || w->lock < 1) return -1; w->lock--; return 0; } /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/ekg/windows.h000066400000000000000000000104151175142753400165670ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2003 Wojtek Kaniewski * 2004 Piotr Kupisiewicz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __EKG_WINDOWS_H #define __EKG_WINDOWS_H #include "ekg2-config.h" #include #include "commands.h" #include "dynstuff.h" #include "sessions.h" #include "themes.h" #ifdef __cplusplus extern "C" { #endif /* * Reserved window id: 1000-1999 * windows reverved for special use. * 1000 - __contacts, * 1001 - __lastlog */ typedef enum { WINDOW_DEBUG_ID = 0, WINDOW_RESERVED_MIN_ID = 1000, WINDOW_CONTACTS_ID = 1000, WINDOW_LASTLOG_ID = 1001, WINDOW_RESERVED_MAX_ID = 1999 } window_reserved_id_t; typedef struct { void *w; /* window, if NULL it means current */ int casense : 2; /* 0 - ignore case; 1 - don't ignore case, -1 - use global variable */ unsigned int lock : 1; /* if 0, don't update */ unsigned int isregex : 1; /* 1 - in target regexp */ GRegex *reg; /* regexp compilated expression */ char *expression; /* expression */ } window_lastlog_t; typedef enum { EKG_WINACT_NONE = 0, /* No activity in window */ EKG_WINACT_JUNK, /* Junks: status change, irc join/part, etc. */ EKG_WINACT_MSG, /* Message, but not to us */ EKG_WINACT_IMPORTANT /* important message */ } winact_t; typedef struct window { struct window *next; unsigned short id; /* numer okna */ char *target; /* nick query albo inna nazwa albo NULL */ char *alias; /* name for display */ session_t *session; /* ktrej sesji dotyczy okno */ unsigned short left, top; /* pozycja (x, y) wzgldem pocztku ekranu */ unsigned short width, height; /* wymiary okna */ unsigned int act : 2; /* activity: 1 - status/junk; 2 - msg ; 3 - msg to us */ unsigned int in_typing : 1; /* user is composing a message to us */ unsigned int more : 1; /* pojawio si co poza ekranem */ unsigned int floating : 1; /* czy pywajce? */ unsigned int doodle : 1; /* czy do gryzmolenia? [we don't set it anywhere] */ unsigned int frames : 4; /* informacje o ramkach */ unsigned int edge : 4; /* okienko brzegowe */ unsigned int nowrap : 1; /* nie zawijamy linii */ unsigned int hide : 1; /* ukrywamy, bo jest zbyt due */ unsigned int last_chatstate; /* last chat state */ time_t last_update; /* czas ostatniego uaktualnienia */ unsigned short lock; /* blokowanie zmian w obrbie komendy */ struct userlist *userlist; /* sometimes window may require separate userlist */ window_lastlog_t *lastlog; /* prywatne informacje lastloga */ void *priv_data; /* prywatne informacje ui */ } window_t; #ifndef EKG2_WIN32_NOFUNCTION extern window_t *windows; extern window_t *window_debug; extern window_t *window_status; extern window_t *window_current; window_t *window_find(const char *target); window_t *window_find_sa(session_t *session, const char *target, int session_null_means_no_session); #define window_find_s(s, target) window_find_sa(s, target, 1) /* XXX, need checking */ window_t *window_find_ptr(window_t *w); window_t *window_new(const char *target, session_t *session, int new_id); void window_kill(window_t *w); void window_switch(int id); window_t *window_exist(int id); void window_print(window_t *w, fstring_t *line); void print_window_w(window_t *w, int activity, const char *theme, ...); /* themes.c */ char *window_target(window_t *window); void window_session_set(window_t *w, session_t *newsession); int window_session_cycle(window_t *w); int window_lock_inc(window_t *w); int window_lock_dec(window_t *w); void windows_destroy(void); #endif COMMAND(cmd_window); #ifdef __cplusplus } #endif #endif /* __EKG_WINDOW_H */ /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/ekg/xmalloc.c000066400000000000000000000141211175142753400165250ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2001-2003 Wojtek Kaniewski 2004 Piotr Kupisiewicz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * Dodane s tutaj funkcje opakowujce, ktre maj zapobiec problemoom * z dostarczeniem do funkcji argumentu o wartoci NULL. W niektrych * systemach doprowadza to do segmeentation fault. * * Problem zostaje rozwizany poprzez zamienienie wartoci NULL * wartoci "", ktra jest ju prawidow dan wejciow. * Makro fix(s) zostao wanie stworzone w tym celu */ #include "ekg2.h" #include #include #include #include #include #include #include #include #ifdef NO_POSIX_SYSTEM #include #define strcasecmp(s1, s2) lstrcmpiA(s1, s2) #endif #define fix(s) ((s) ? (s) : "") #ifndef EKG_NO_DEPRECATED /** * xcalloc() * * @bug same as in xmalloc() * * @note Deprecated, please use g_new(), g_new0(), g_slice_new() * or g_slice_new0() instead. */ void *xcalloc(size_t nmemb, size_t size) { return g_malloc0(nmemb * size); } /** * xmalloc() * * Allocate memory for @a size bytes, clears it [set it with \\0], and returns pointer to allocated memory. * If malloc() fails with NULL, ekg_oom_handler() kills program.
* Wrapper to malloc()+memset()
* * @sa xcalloc() * @sa xfree() * * @param size - the same as in malloc() * * @return pointer to allocated memory * * @note Deprecated, please use g_malloc(), g_malloc0(), g_new(), * g_new0(), g_slice_new() or g_slice_new0() instead. */ void *xmalloc(size_t size) { return g_malloc0(size); } /** * xfree() * * Free memory pointed by @a ptr * if @a ptr == NULL do nothing.
* Equivalent to: if (ptr) free(ptr); * * @sa xrealloc() - If you want change size of allocated memory. * * @note Deprecated, please use g_free() (or g_slice_free()) instead. */ void xfree(void *ptr) { g_free(ptr); } /** * xrealloc() * * @bug same as in xmalloc() * * @note Deprecated, please use g_realloc() instead. */ void *xrealloc(void *ptr, size_t size) { return g_realloc(ptr, size); } /** * xstrdup() * * @note Deprecated, please use g_strdup() instead. */ char *xstrdup(const char *s) { return g_strdup((char *) s); } /** * xstrndup() * * @note Deprecated, please use g_strndup() and g_strdup() instead. */ char *xstrndup(const char *s, size_t n) { if (n == (size_t) -1) return g_strdup((char *) s); return g_strndup((char *) s, n); } #endif char *utf8ndup(const char *s, size_t n) { /* XXX any suggestions for function name? IDKHTNI_ndup()? (IDKHTNI - I Don't Know How To Name It) */ char *tmp = xstrndup(s, n); if (!tmp) return NULL; if (!is_utf8_string(s)) return tmp; if (n<0) n = xstrlen(tmp); while (!is_utf8_string(tmp) && n--) *(tmp + n) = 0; return tmp; } #ifndef EKG_NO_DEPRECATED /** * vsaprintf() * * @note Deprecated, please use g_strdup_vprintf() instead. */ char *vsaprintf(const char *format, va_list ap) { return g_strdup_vprintf(format, ap); } #endif /* XXX: most of the below funcs seem braindead, deprecate them as well? */ char *xstrstr(const char *haystack, const char *needle) { return strstr(fix(haystack), fix(needle)); } char *xstrcasestr(const char *haystack, const char *needle) { return strcasestr(fix(haystack), fix(needle)); } int xstrcasecmp(const char *s1, const char *s2) { return strcasecmp(fix(s1), fix(s2)); } char *xstrcat(char *dest, const char *src) { return strcat(dest, fix(src)); } char *xstrchr(const char *s, int c) { return strchr(fix(s), c); } int xstrcmp(const char *s1, const char *s2) { return strcmp(fix(s1), fix(s2)); } int xstrcoll(const char *s1, const char *s2) { return strcoll(fix(s1), fix(s2)); } char *xstrcpy(char *dest, const char *src) { return strcpy(dest, fix(src)); } size_t xstrcspn(const char *s, const char *reject) { return strcspn(fix(s), fix(reject)); } size_t xstrlen(const char *s) { return strlen(fix(s)); } int xstrncasecmp_pl(const char *s1, const char *s2, size_t n) { return strncasecmp_pl(fix(s1), fix(s2), n); } char *xstrncat(char *dest, const char *src, size_t n) { return strncat(dest, fix(src), n); } int xstrncmp(const char *s1, const char *s2, size_t n) { return strncmp(fix(s1), fix(s2), n); } char *xstrncpy(char *dest, const char *src, size_t n) { return strncpy(dest, fix(src), n); } int xstrncasecmp(const char *s1, const char *s2, size_t n) { return strncasecmp(fix(s1), fix(s2), n); } char *xstrpbrk(const char *s, const char *accept) { return strpbrk(fix(s), fix(accept)); } char *xstrrchr(const char *s, int c) { return strrchr(fix(s), c); } #if 0 nic tego nie uywa, a to nie jest zaimplementowane na wszystkich systemach i tylko powoduje problemy z kompilacj [Solaris again] char *xstrsep(char **stringp, const char *delim) { /* kiedy stringo -- NULL funkcja po prostu nic nie robi - man strsep */ return strsep(stringp, fix(delim)); } #endif size_t xstrspn(const char *s, const char *accept) { return strspn(fix(s), fix(accept)); } char *xstrtok(char *s, const char *delim) { /* pierwszy argument powinien by NULL */ return strtok(s, fix(delim)); } char *xindex(const char *s, int c) { #ifdef NO_POSIX_SYSTEM return xstrchr(s, c); #else return index(fix(s), c); #endif } char *xrindex(const char *s, int c) { #ifdef NO_POSIX_SYSTEM return xstrrchr(s, c); #else return rindex(fix(s), c); #endif } /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/ekg/xmalloc.h000066400000000000000000000063411175142753400165370ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2001-2002 Wojtek Kaniewski * 2004 Piotr Kupisiewicz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __EKG_XMALLOC_H #define __EKG_XMALLOC_H #include #include #include #include #define __(x) (x ? x : "(null)") /* stolen from: http://sourcefrog.net/weblog/software/languages/C/unused.html */ #ifdef UNUSED #elif defined(__GNUC__) # define UNUSED(x) UNUSED_ ## x __attribute__((unused)) #elif defined(__LCLINT__) # define UNUSED(x) /*@unused@*/ x #else # define UNUSED(x) x #endif /* /stolen */ #ifndef HAVE_SOCKLEN_T typedef unsigned int socklen_t; #endif /* buffer lengths in stuff.c */ #ifndef PATH_MAX # ifdef MAX_PATH # define PATH_MAX MAX_PATH # else # ifdef _POSIX_PATH_MAX # define PATH_MAX _POSIX_PATH_MAX # else # define PATH_MAX 4096 # endif # endif #endif #ifndef EKG2_WIN32_NOFUNCTION #ifndef EKG_NO_DEPRECATED void *xcalloc(size_t nmemb, size_t size); void *xmalloc(size_t size); void xfree(void *ptr); void *xrealloc(void *ptr, size_t size); char *xstrdup(const char *s); char *xstrndup(const char *s, size_t n); #endif char *utf8ndup(const char *s, size_t n); int xstrcasecmp(const char *s1, const char *s2); char *xstrcat(char *dest, const char *src); char *xstrchr(const char *s, int c); int xstrcmp(const char *s1, const char *s2); int xstrcoll(const char *s1, const char *s2); char *xstrcpy(char *dest, const char *src); size_t xstrcspn(const char *s, const char *reject); size_t xstrlen(const char *s); int xstrncasecmp_pl(const char *s1, const char *s2, size_t n); char *xstrncat(char *dest, const char *src, size_t n); int xstrncmp(const char *s1, const char *s2, size_t n); char *xstrncpy(char *dest, const char *src, size_t n); int xstrncasecmp(const char *s1, const char *s2, size_t n); char *xstrpbrk(const char *s, const char *accept); char *xstrrchr(const char *s, int c); /* char *xstrsep(char **stringp, const char *delim); */ size_t xstrspn(const char *s, const char *accept); char *xstrstr(const char *haystack, const char *needle); char *xstrcasestr(const char *haystack, const char *needle); char *xstrtok(char *s, const char *delim); char *xindex(const char *s, int c); char *xrindex(const char *s, int c); #ifndef EKG_NO_DEPRECATED char *vsaprintf(const char *format, va_list ap); /* stuff.h */ #ifdef __GNUC__ char *saprintf(const char *format, ...) __attribute__ ((format (printf, 1, 2))); #else char *saprintf(const char *format, ...); #endif #endif #endif #endif /* __EKG_XMALLOC_H */ /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/ekg2.h000066400000000000000000000012761175142753400151640ustar00rootroot00000000000000#include "ekg2-config.h" #include #include #include "ekg/xmalloc.h" #include "ekg/win32.h" #include "ekg/dynstuff.h" #include "ekg/sessions.h" #include "ekg/plugins.h" #include "ekg/protocol.h" #include "ekg/themes.h" #include "ekg/windows.h" #include "ekg/userlist.h" #include "ekg/stuff.h" #include "ekg/abort.h" #include "ekg/bindings.h" #include "ekg/commands.h" #include "ekg/configfile.h" #include "ekg/connections.h" #include "ekg/debug.h" #include "ekg/dynstuff_inline.h" #include "ekg/events.h" #include "ekg/log.h" #include "ekg/metacontacts.h" #include "ekg/msgqueue.h" #include "ekg/queries.h" #include "ekg/recode.h" #include "ekg/sources.h" #include "ekg/vars.h" ekg2-0.4~pre+20120506.1/gettext.h000066400000000000000000000061331175142753400160150ustar00rootroot00000000000000/* Convenience header for conditional use of GNU . Copyright (C) 1995-1998, 2000-2002 Free Software Foundation, Inc. This program 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, or (at your option) any later version. This program is distributed in the hope that 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 program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifndef _LIBGETTEXT_H #define _LIBGETTEXT_H 1 /* NLS can be disabled through the configure --disable-nls option. */ #if ENABLE_NLS /* Get declarations of GNU message catalog functions. */ # include #else /* Solaris /usr/include/locale.h includes /usr/include/libintl.h, which chokes if dcgettext is defined as a macro. So include it now, to make later inclusions of a NOP. We don't include as well because people using "gettext.h" will not include , and also including would fail on SunOS 4, whereas is OK. */ #if defined(__sun) # include #endif /* Disabled NLS. The casts to 'const char *' serve the purpose of producing warnings for invalid uses of the value returned from these functions. On pre-ANSI systems without 'const', the config.h file is supposed to contain "#define const". */ # define gettext(Msgid) ((const char *) (Msgid)) # define dgettext(Domainname, Msgid) ((const char *) (Msgid)) # define dcgettext(Domainname, Msgid, Category) ((const char *) (Msgid)) # define ngettext(Msgid1, Msgid2, N) \ ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2)) # define dngettext(Domainname, Msgid1, Msgid2, N) \ ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2)) # define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \ ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2)) # define textdomain(Domainname) ((const char *) (Domainname)) # define bindtextdomain(Domainname, Dirname) ((const char *) (Dirname)) # define bind_textdomain_codeset(Domainname, Codeset) ((const char *) (Codeset)) #endif /* A pseudo function call that serves as a marker for the automated extraction of messages, but does not call gettext(). The run-time translation is done at a different place in the code. The argument, String, should be a literal string. Concatenated strings and other string expressions won't work. The macro's expansion is not parenthesized, so that it is suitable as initializer for static 'char[]' or 'const char[]' variables. */ #define gettext_noop(String) String #endif /* _LIBGETTEXT_H */ /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/m4/000077500000000000000000000000001175142753400144755ustar00rootroot00000000000000ekg2-0.4~pre+20120506.1/m4/ekg2-checks.m4000066400000000000000000000045221175142753400170300ustar00rootroot00000000000000AC_DEFUN([AC_EKG2_FASTCONF], [ AC_ARG_ENABLE([fast-configure], AS_HELP_STRING([--enable-fast-configure], [enable non-thorough (fast) configure tests [default=off]])) ]) AC_DEFUN([AC_EKG2_CHECK_LIB], [ dnl AC_EKG2_CHECK_LIB(name, func, headers, [if-yes], [if-no]) dnl Perform AC_CHECK_HEADERS & AC_CHECK_LIB for a particular library. Run dnl 'if-yes' if both checks succeed (if not specified, defaults to AC_CHECK_LIB dnl default commands); otherwise run 'if-no'. AC_REQUIRE([AC_EKG2_FASTCONF]) found_any_header=no AC_CHECK_HEADERS([$3], [ AS_IF([test "x$enable_fast_configure" != "xyes"], [ AC_CHECK_LIB([$1], [$2], [$4], [$5]) ], [ LIBS="-l$1 $LIBS" AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_LIB$1), [1], [define if you have $1]) $4 ]) found_any_header=yes break ]) AS_IF([test $found_any_header != yes], [ $5 ]) AS_UNSET([found_any_header]) ]) AC_DEFUN([AC_EKG2_CHECK_FLAGEXPORTED_LIB], [ dnl AC_EKG2_CHECK_FLAGEXPORTED_LIB(variable-prefix, lib-name, func, header, [if-yes], [if-no]) AC_REQUIRE([AC_EKG2_FASTCONF]) pc_save_CPPFLAGS=$CPPFLAGS CPPFLAGS="$$1_CFLAGS $CPPFLAGS" AS_IF([test "x$enable_fast_configure" != "xyes"], [ AC_CHECK_HEADER([$4], [ pc_save_LIBS=$LIBS LIBS="$$1_LIBS $LIBS" AC_CHECK_FUNC([$3], [ m4_ifval([$2], [ AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_LIB$2), [1], [define if you have $1]) ]) $5 ], [ LIBS=$pc_save_LIBS CPPFLAGS=$pc_save_CPPFLAGS $6 ]) ], [ CPPFLAGS=$pc_save_CPPFLAGS $6 ]) ], [ LIBS="$$1_LIBS $LIBS" m4_ifval([$2], [ AC_DEFINE_UNQUOTED(AS_TR_CPP(HAVE_LIB$2), [1], [define if you have $1]) ]) $5 ]) ]) AC_DEFUN([AC_EKG2_CHECK_PKGCONFIG_LIB], [ dnl AC_EKG2_CHECK_PKGCONFIG_LIB(pkg-name, fallback-name, func, header, [if-yes], [if-no], [if-fallback-yes]) dnl Perform PKG_CHECK_MODULES for , grabbing LIBS & CFLAGS there; then dnl perform an AC_CHECK_HEADERS & AC_CHECK_FUNC to check if the obtained data is dnl useful. dnl dnl If the pkg-config call fails to grab needed data, fallback to dnl AC_EKG2_CHECK_LIB. If the fallback succeeds to find the library, run either dnl if specified or otherwise. PKG_CHECK_MODULES(AS_TR_SH([$1]), [$1], [ AC_EKG2_CHECK_FLAGEXPORTED_LIB(AS_TR_SH([$1]), [$2], [$3], [$4], [$5], [$6]) ], [ AC_EKG2_CHECK_LIB([$2], [$3], [$4], m4_default([$7], [$5]), [$6]) ]) ]) ekg2-0.4~pre+20120506.1/m4/ekg2-plugin.m4000066400000000000000000000060701175142753400170660ustar00rootroot00000000000000AC_DEFUN([AC_EKG2_PLUGIN_SETUP], [ AC_SUBST([EKG2_STATIC_PLUGIN_LIBS]) AS_IF([test "x$enable_static" = "xyes"], [ AC_DEFINE([STATIC_LIBS], [1], [define if you want static plugins]) EKG2_STATIC_PLUGIN_DECLS= EKG2_STATIC_PLUGIN_CALLS= AC_CONFIG_COMMANDS_PRE([ AC_DEFINE_UNQUOTED([STATIC_PLUGIN_DECLS], [$EKG2_STATIC_PLUGIN_DECLS], [static plugin init function declarations]) AC_DEFINE_UNQUOTED([STATIC_PLUGIN_CALLS], [$EKG2_STATIC_PLUGIN_CALLS], [static plugin init function calls]) ]) ]) AS_IF([test "x$enable_shared" = "xyes"], [ AC_DEFINE([SHARED_LIBS], [1], [define if you want shared plugins (in .so or .dll)]) ]) AM_CONDITIONAL([SHARED_LIBS], [test "x$enable_shared" = "xyes"]) AM_CONDITIONAL([STATIC_LIBS], [test "x$enable_static" = "xyes"]) ]) AC_DEFUN([AC_EKG2_PLUGIN], [ dnl AC_EKG2_PLUGIN(name, req-checks, opt-checks) dnl Create a '--enable-' option for a plugin. Unless disabled, run dnl . If a plugin is enabled and checks succeed, run dnl as well. dnl dnl The part is supposed to call EKG2_FAILED_TEST if the checks dnl fail. It can also call EKG2_DISABLED_TEST if the necessary checks dnl (dependencies) were explicitly disabled by user. dnl dnl CPPFLAGS & LIBS will be saved and restored on termination. If tests dnl succeed, they will be copied as well to $1_CPPFLAGS and $1_LIBS, dnl and AC_SUBSTituted with that names. AC_REQUIRE([AC_EKG2_PLUGIN_SETUP]) AC_ARG_ENABLE([$1], AS_HELP_STRING([--disable-$1], [disable building of $1 plugin [default=auto]]), , [ enable_$1=maybe ]) AS_IF([test $enable_$1 != no], [ ac_ekg2_plugin_save_CPPFLAGS=$CPPFLAGS ac_ekg2_plugin_save_LDFLAGS=$LDFLAGS ac_ekg2_plugin_save_LIBS=$LIBS m4_pushdef([EKG2_FAILED_PLUGIN], [ AS_IF([test $enable_$1 = yes], [ AC_MSG_ERROR([Requirements for plugin $1 not met, aborting build.]) ], [ AC_MSG_WARN([Requirements for plugin $1 not met, $1 plugin will not be built.]) enable_$1=no ]) ]) m4_pushdef([EKG2_DISABLED_TEST], [ dnl XXX: get somehow $1 inner expansion AS_IF([test $enable_$1 = yes], [ AC_MSG_ERROR([--without-* conflicts with --enable-$1]) ], [ AC_MSG_WARN([Requirements for plugin $1 disabled, $1 plugin will not be built.]) enable_$1=no ]) ]) $2 m4_popdef([EKG2_FAILED_PLUGIN]) m4_popdef([EKG2_DISABLED_TEST]) AS_IF([test $enable_$1 != no], [ $3 AC_SUBST([plugins_$1_$1_la_CPPFLAGS], ["\$(AM_CPPFLAGS) $CPPFLAGS"]) AC_SUBST([plugins_$1_$1_la_LDFLAGS], ["-module -avoid-version $LDFLAGS"]) AC_SUBST([plugins_$1_$1_la_LIBADD], [$LIBS]) AC_SUBST([$1dir], ['$(pkgdatadir)/plugins/$1']) AS_IF([test "x$enable_static" = "xyes"], [ EKG2_STATIC_PLUGIN_DECLS="$EKG2_STATIC_PLUGIN_DECLS G_MODULE_IMPORT int $1_plugin_init(int);" EKG2_STATIC_PLUGIN_CALLS="$EKG2_STATIC_PLUGIN_CALLS if (!xstrcmp(name, \"$1\")) plugin_init = &$1_plugin_init;" ]) ]) CPPFLAGS=$ac_ekg2_plugin_save_CPPFLAGS LDFLAGS=$ac_ekg2_plugin_save_LDFLAGS LIBS=$ac_ekg2_plugin_save_LIBS ]) AM_CONDITIONAL([ENABLE_]translit($1, [a-z], [A-Z]), [test $enable_$1 != no]) ]) ekg2-0.4~pre+20120506.1/m4/ekg2-with.m4000066400000000000000000000047051175142753400165460ustar00rootroot00000000000000dnl Stupid debian pkg-config-0.22 m4_pattern_allow([PKG_CONFIG_LIBDIR]) m4_pattern_allow([PKG_CONFIG_PATH]) AC_DEFUN([AC_EKG2_MULTILIB], [ dnl AC_EKG2_MULTILIB() dnl Initialize multilib support, i.e. find out what libdir to use. AS_CASE([$libdir], [*lib64], [ac_ekg2_multilib_libdir=lib64], [*lib32], [ac_ekg2_multilib_libdir=lib32], [ac_ekg2_multilib_libdir=lib]) ]) AC_DEFUN([EKG2_LIBDIRNAME], [AC_REQUIRE([AC_EKG2_MULTILIB])$ac_ekg2_multilib_libdir]) AC_DEFUN([AC_EKG2_WITH], [ dnl AC_EKG2_WITH(optname, if-yes, [if-no], [alt-setup], [alt-cleanup]) dnl Create an ekg2-style '--with-' option. Run if dnl '--without-' was used, otherwise. dnl dnl If '--with-=' was specified with a path, either run dnl before running if specified or set up -I, -L flags and pkg-config dnl vars. In the same case, is being run after if dnl was specified, or pkg-config vars are cleaned up. AC_ARG_WITH([$1], AS_HELP_STRING([translit([--with-$1[=]], [_], [-])], [build with $1 (in ) [default=auto]]), , [ with_$1=maybe ]) m4_pushdef([EKG2_FAILED_TEST], [ AS_CASE([$with_$1], [no|maybe], [ with_$1=no ], [ AC_MSG_ERROR([Test for --with-$1 failed, aborting build.]) ] ) m4_ifdef([EKG2_FAILED_PLUGIN], [ EKG2_FAILED_PLUGIN ]) ]) AS_CASE([$with_$1], [yes|maybe], [$2], [no], [ m4_default([$3], [ m4_ifdef([EKG2_DISABLED_TEST], [ EKG2_DISABLED_TEST([--without-$1]) ]) ]) ], [ m4_default([$4], [ CPPFLAGS="$CPPFLAGS -I$with_$1/include" LDFLAGS="$LDFLAGS -L$with_$1/EKG2_LIBDIRNAME" ekg_saved_PKG_CONFIG_LIBDIR=${PKG_CONFIG_LIBDIR+yes} ekg_save_PKG_CONFIG_PATH=$PKG_CONFIG_PATH ekg_save_PKG_CONFIG_LIBDIR=$PKG_CONFIG_LIBDIR ekg_save_PATH=$PATH AS_UNSET([PKG_CONFIG_PATH]) PKG_CONFIG_LIBDIR="$with_$1/EKG2_LIBDIRNAME/pkgconfig:$with_$1/share/pkgconfig" export PKG_CONFIG_LIBDIR PATH="$with_$1/bin:$PATH" export PATH ]) $2 m4_ifval([$4], [$5], [ # pkg-config differentiates between unset & empty PKG_CONFIG_LIBDIR AS_IF([test "$ekg_saved_PKG_CONFIG_LIBDIR" = "yes"], [ PKG_CONFIG_LIBDIR=$ekg_save_PKG_CONFIG_LIBDIR export PKG_CONFIG_LIBDIR ], [ AS_UNSET([PKG_CONFIG_LIBDIR]) ]) PKG_CONFIG_PATH=$ekg_save_PKG_CONFIG_PATH export PKG_CONFIG_PATH PATH=$ekg_save_PATH export PATH ]) ] ) m4_popdef([EKG2_FAILED_TEST]) ]) ekg2-0.4~pre+20120506.1/m4/gcc_strict_aliasing.m4000066400000000000000000000024751175142753400207420ustar00rootroot00000000000000dnl (C) Copyright 2011 Marcin Owsiany dnl dnl This program is free software; you can redistribute it and/or modify dnl it under the terms of the GNU General Public License Version 2 as dnl published by the Free Software Foundation. dnl dnl This program is distributed in the hope that it will be useful, dnl but WITHOUT ANY WARRANTY; without even the implied warranty of dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the dnl GNU General Public License for more details. dnl dnl You should have received a copy of the GNU General Public License dnl along with this program; if not, write to the Free Software dnl Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. AC_DEFUN([AC_PROG_C_NO_STRICT_ALIASING], [ AC_SUBST(C_STRICT_ALIASING) ac_save_CFLAGS=$CFLAGS CFLAGS="-fno-strict-aliasing" AC_CACHE_CHECK([whether $CC accepts ${CFLAGS}], [ekg2_cv_no_strict_aliasing_accepted], [AC_COMPILE_IFELSE([AC_LANG_PROGRAM()], [ekg2_cv_no_strict_aliasing_accepted=yes], [ekg2_cv_no_strict_aliasing_accepted=no] )] ) if test "$ekg2_cv_no_strict_aliasing_accepted" = yes; then C_STRICT_ALIASING="${CFLAGS}" else C_STRICT_ALIASING="" fi CFLAGS=$ac_save_CFLAGS ]) ekg2-0.4~pre+20120506.1/m4/gpgme.m4000066400000000000000000000240641175142753400160440ustar00rootroot00000000000000# gpgme.m4 - autoconf macro to detect GPGME. # Copyright (C) 2002, 2003, 2004 g10 Code GmbH # # This file is free software; as a special exception the author gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # # This file is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY, to the extent permitted by law; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. AC_DEFUN([_AM_PATH_GPGME_CONFIG], [ AC_ARG_WITH(gpgme-prefix, AC_HELP_STRING([--with-gpgme-prefix=PFX], [prefix where GPGME is installed (optional)]), gpgme_config_prefix="$withval", gpgme_config_prefix="") if test "x$gpgme_config_prefix" != x ; then GPGME_CONFIG="$gpgme_config_prefix/bin/gpgme-config" fi AC_PATH_PROG(GPGME_CONFIG, gpgme-config, no) if test "$GPGME_CONFIG" != "no" ; then gpgme_version=`$GPGME_CONFIG --version` fi gpgme_version_major=`echo $gpgme_version | \ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\1/'` gpgme_version_minor=`echo $gpgme_version | \ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\2/'` gpgme_version_micro=`echo $gpgme_version | \ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\).*/\3/'` ]) dnl AM_PATH_GPGME([MINIMUM-VERSION, dnl [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND ]]]) dnl Test for libgpgme and define GPGME_CFLAGS and GPGME_LIBS. dnl AC_DEFUN([AM_PATH_GPGME], [ AC_REQUIRE([_AM_PATH_GPGME_CONFIG])dnl tmp=ifelse([$1], ,1:0.4.2,$1) if echo "$tmp" | grep ':' >/dev/null 2>/dev/null ; then req_gpgme_api=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\1/'` min_gpgme_version=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\2/'` else req_gpgme_api=0 min_gpgme_version="$tmp" fi AC_MSG_CHECKING(for GPGME - version >= $min_gpgme_version) ok=no if test "$GPGME_CONFIG" != "no" ; then req_major=`echo $min_gpgme_version | \ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\1/'` req_minor=`echo $min_gpgme_version | \ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\2/'` req_micro=`echo $min_gpgme_version | \ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\3/'` if test "$gpgme_version_major" -gt "$req_major"; then ok=yes else if test "$gpgme_version_major" -eq "$req_major"; then if test "$gpgme_version_minor" -gt "$req_minor"; then ok=yes else if test "$gpgme_version_minor" -eq "$req_minor"; then if test "$gpgme_version_micro" -ge "$req_micro"; then ok=yes fi fi fi fi fi fi if test $ok = yes; then # If we have a recent GPGME, we should also check that the # API is compatible. if test "$req_gpgme_api" -gt 0 ; then tmp=`$GPGME_CONFIG --api-version 2>/dev/null || echo 0` if test "$tmp" -gt 0 ; then if test "$req_gpgme_api" -ne "$tmp" ; then ok=no fi fi fi fi if test $ok = yes; then GPGME_CFLAGS=`$GPGME_CONFIG --cflags` GPGME_LIBS=`$GPGME_CONFIG --libs` AC_MSG_RESULT(yes) ifelse([$2], , :, [$2]) else GPGME_CFLAGS="" GPGME_LIBS="" AC_MSG_RESULT(no) ifelse([$3], , :, [$3]) fi AC_SUBST(GPGME_CFLAGS) AC_SUBST(GPGME_LIBS) ]) dnl AM_PATH_GPGME_PTH([MINIMUM-VERSION, dnl [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND ]]]) dnl Test for libgpgme and define GPGME_PTH_CFLAGS and GPGME_PTH_LIBS. dnl AC_DEFUN([AM_PATH_GPGME_PTH], [ AC_REQUIRE([_AM_PATH_GPGME_CONFIG])dnl tmp=ifelse([$1], ,1:0.4.2,$1) if echo "$tmp" | grep ':' >/dev/null 2>/dev/null ; then req_gpgme_api=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\1/'` min_gpgme_version=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\2/'` else req_gpgme_api=0 min_gpgme_version="$tmp" fi AC_MSG_CHECKING(for GPGME Pth - version >= $min_gpgme_version) ok=no if test "$GPGME_CONFIG" != "no" ; then if `$GPGME_CONFIG --thread=pth 2> /dev/null` ; then req_major=`echo $min_gpgme_version | \ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\1/'` req_minor=`echo $min_gpgme_version | \ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\2/'` req_micro=`echo $min_gpgme_version | \ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\3/'` if test "$gpgme_version_major" -gt "$req_major"; then ok=yes else if test "$gpgme_version_major" -eq "$req_major"; then if test "$gpgme_version_minor" -gt "$req_minor"; then ok=yes else if test "$gpgme_version_minor" -eq "$req_minor"; then if test "$gpgme_version_micro" -ge "$req_micro"; then ok=yes fi fi fi fi fi fi fi if test $ok = yes; then # If we have a recent GPGME, we should also check that the # API is compatible. if test "$req_gpgme_api" -gt 0 ; then tmp=`$GPGME_CONFIG --api-version 2>/dev/null || echo 0` if test "$tmp" -gt 0 ; then if test "$req_gpgme_api" -ne "$tmp" ; then ok=no fi fi fi fi if test $ok = yes; then GPGME_PTH_CFLAGS=`$GPGME_CONFIG --thread=pth --cflags` GPGME_PTH_LIBS=`$GPGME_CONFIG --thread=pth --libs` AC_MSG_RESULT(yes) ifelse([$2], , :, [$2]) else GPGME_PTH_CFLAGS="" GPGME_PTH_LIBS="" AC_MSG_RESULT(no) ifelse([$3], , :, [$3]) fi AC_SUBST(GPGME_PTH_CFLAGS) AC_SUBST(GPGME_PTH_LIBS) ]) dnl AM_PATH_GPGME_PTHREAD([MINIMUM-VERSION, dnl [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND ]]]) dnl Test for libgpgme and define GPGME_PTHREAD_CFLAGS dnl and GPGME_PTHREAD_LIBS. dnl AC_DEFUN([AM_PATH_GPGME_PTHREAD], [ AC_REQUIRE([_AM_PATH_GPGME_CONFIG])dnl tmp=ifelse([$1], ,1:0.4.2,$1) if echo "$tmp" | grep ':' >/dev/null 2>/dev/null ; then req_gpgme_api=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\1/'` min_gpgme_version=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\2/'` else req_gpgme_api=0 min_gpgme_version="$tmp" fi AC_MSG_CHECKING(for GPGME pthread - version >= $min_gpgme_version) ok=no if test "$GPGME_CONFIG" != "no" ; then if `$GPGME_CONFIG --thread=pthread 2> /dev/null` ; then req_major=`echo $min_gpgme_version | \ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\1/'` req_minor=`echo $min_gpgme_version | \ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\2/'` req_micro=`echo $min_gpgme_version | \ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\3/'` if test "$gpgme_version_major" -gt "$req_major"; then ok=yes else if test "$gpgme_version_major" -eq "$req_major"; then if test "$gpgme_version_minor" -gt "$req_minor"; then ok=yes else if test "$gpgme_version_minor" -eq "$req_minor"; then if test "$gpgme_version_micro" -ge "$req_micro"; then ok=yes fi fi fi fi fi fi fi if test $ok = yes; then # If we have a recent GPGME, we should also check that the # API is compatible. if test "$req_gpgme_api" -gt 0 ; then tmp=`$GPGME_CONFIG --api-version 2>/dev/null || echo 0` if test "$tmp" -gt 0 ; then if test "$req_gpgme_api" -ne "$tmp" ; then ok=no fi fi fi fi if test $ok = yes; then GPGME_PTHREAD_CFLAGS=`$GPGME_CONFIG --thread=pthread --cflags` GPGME_PTHREAD_LIBS=`$GPGME_CONFIG --thread=pthread --libs` AC_MSG_RESULT(yes) ifelse([$2], , :, [$2]) else GPGME_PTHREAD_CFLAGS="" GPGME_PTHREAD_LIBS="" AC_MSG_RESULT(no) ifelse([$3], , :, [$3]) fi AC_SUBST(GPGME_PTHREAD_CFLAGS) AC_SUBST(GPGME_PTHREAD_LIBS) ]) dnl AM_PATH_GPGME_GLIB([MINIMUM-VERSION, dnl [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND ]]]) dnl Test for libgpgme-glib and define GPGME_GLIB_CFLAGS and GPGME_GLIB_LIBS. dnl AC_DEFUN([AM_PATH_GPGME_GLIB], [ AC_REQUIRE([_AM_PATH_GPGME_CONFIG])dnl tmp=ifelse([$1], ,1:0.4.2,$1) if echo "$tmp" | grep ':' >/dev/null 2>/dev/null ; then req_gpgme_api=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\1/'` min_gpgme_version=`echo "$tmp" | sed 's/\(.*\):\(.*\)/\2/'` else req_gpgme_api=0 min_gpgme_version="$tmp" fi AC_MSG_CHECKING(for GPGME - version >= $min_gpgme_version) ok=no if test "$GPGME_CONFIG" != "no" ; then req_major=`echo $min_gpgme_version | \ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\1/'` req_minor=`echo $min_gpgme_version | \ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\2/'` req_micro=`echo $min_gpgme_version | \ sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\.\([[0-9]]*\)/\3/'` if test "$gpgme_version_major" -gt "$req_major"; then ok=yes else if test "$gpgme_version_major" -eq "$req_major"; then if test "$gpgme_version_minor" -gt "$req_minor"; then ok=yes else if test "$gpgme_version_minor" -eq "$req_minor"; then if test "$gpgme_version_micro" -ge "$req_micro"; then ok=yes fi fi fi fi fi fi if test $ok = yes; then # If we have a recent GPGME, we should also check that the # API is compatible. if test "$req_gpgme_api" -gt 0 ; then tmp=`$GPGME_CONFIG --api-version 2>/dev/null || echo 0` if test "$tmp" -gt 0 ; then if test "$req_gpgme_api" -ne "$tmp" ; then ok=no fi fi fi fi if test $ok = yes; then GPGME_GLIB_CFLAGS=`$GPGME_CONFIG --glib --cflags` GPGME_GLIB_LIBS=`$GPGME_CONFIG --glib --libs` AC_MSG_RESULT(yes) ifelse([$2], , :, [$2]) else GPGME_GLIB_CFLAGS="" GPGME_GLIB_LIBS="" AC_MSG_RESULT(no) ifelse([$3], , :, [$3]) fi AC_SUBST(GPGME_GLIB_CFLAGS) AC_SUBST(GPGME_GLIB_LIBS) ]) ekg2-0.4~pre+20120506.1/m4/pkg.m4000066400000000000000000000125031175142753400155210ustar00rootroot00000000000000# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- # serial 1 (pkg-config-0.24) # # Copyright © 2004 Scott James Remnant . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # PKG_PROG_PKG_CONFIG([MIN-VERSION]) # ---------------------------------- AC_DEFUN([PKG_PROG_PKG_CONFIG], [m4_pattern_forbid([^_?PKG_[A-Z_]+$]) m4_pattern_allow([^PKG_CONFIG(_PATH)?$]) AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) fi if test -n "$PKG_CONFIG"; then _pkg_min_version=m4_default([$1], [0.9.0]) AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) PKG_CONFIG="" fi fi[]dnl ])# PKG_PROG_PKG_CONFIG # PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # # Check to see whether a particular set of modules exists. Similar # to PKG_CHECK_MODULES(), but does not set variables or print errors. # # Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) # only at the first occurence in configure.ac, so if the first place # it's called might be skipped (such as if it is within an "if", you # have to call PKG_CHECK_EXISTS manually # -------------------------------------------------------------- AC_DEFUN([PKG_CHECK_EXISTS], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl if test -n "$PKG_CONFIG" && \ AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then m4_default([$2], [:]) m4_ifvaln([$3], [else $3])dnl fi]) # _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) # --------------------------------------------- m4_define([_PKG_CONFIG], [if test -n "$$1"; then pkg_cv_[]$1="$$1" elif test -n "$PKG_CONFIG"; then PKG_CHECK_EXISTS([$3], [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`], [pkg_failed=yes]) else pkg_failed=untried fi[]dnl ])# _PKG_CONFIG # _PKG_SHORT_ERRORS_SUPPORTED # ----------------------------- AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], [AC_REQUIRE([PKG_PROG_PKG_CONFIG]) if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi[]dnl ])# _PKG_SHORT_ERRORS_SUPPORTED # PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], # [ACTION-IF-NOT-FOUND]) # # # Note that if there is a possibility the first call to # PKG_CHECK_MODULES might not happen, you should be sure to include an # explicit call to PKG_PROG_PKG_CONFIG in your configure.ac # # # -------------------------------------------------------------- AC_DEFUN([PKG_CHECK_MODULES], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl pkg_failed=no AC_MSG_CHECKING([for $1]) _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) _PKG_CONFIG([$1][_LIBS], [libs], [$2]) m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS and $1[]_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details.]) if test $pkg_failed = yes; then AC_MSG_RESULT([no]) _PKG_SHORT_ERRORS_SUPPORTED if test $_pkg_short_errors_supported = yes; then $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "$2" 2>&1` else $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors "$2" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD m4_default([$4], [AC_MSG_ERROR( [Package requirements ($2) were not met: $$1_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. _PKG_TEXT])[]dnl ]) elif test $pkg_failed = untried; then AC_MSG_RESULT([no]) m4_default([$4], [AC_MSG_FAILURE( [The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. _PKG_TEXT To get pkg-config, see .])dnl ]) else $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS $1[]_LIBS=$pkg_cv_[]$1[]_LIBS AC_MSG_RESULT([yes]) $3 fi[]dnl ])# PKG_CHECK_MODULES ekg2-0.4~pre+20120506.1/plugins/000077500000000000000000000000001175142753400156365ustar00rootroot00000000000000ekg2-0.4~pre+20120506.1/plugins/README000066400000000000000000000031651175142753400165230ustar00rootroot00000000000000plugin autor przeznaczenie zalenoci ----------------------------------------------------------------------------- autoreponder peres prosty autoresponder - dbus gim IPC per dbus dbus dummy * szkielet edukacyjny - feed darkjames obsuga feedw RSS i NNTP expat gg wojtekka obsuga protokou Gadu-Gadu libgadu gpg darkjames szyfrowanie wiadomoci GPG gpgme gsm wojtekka kodek GSM do rozmw gosowych libgsm gtk darkjames interfejs graficzny na GTK+ gtk+2 httprc_xajax * interfejs WWW (z AJAX) - icq dj/wiechu obsuga protokou icq - ioctld drg obsuga diod klawiatury - irc gim obsuga protokou irc - jabber wojtekka obsuga protokou XMPP/Jabber expat jogger peres komunikacja z botem Jogger.pl - logs zdzichu logowanie wiadomoci - logsoracle ecimon logowanie wiadomoci do oracle oracle logsqlite leafnode logowanie wiadomoci do sqlite sqlite mail szalik sprawdzanie poczty - ncurses * interfejs tekstowy ncurses oss * kodek OSS - pcm wojtekka kodek PCM - perl darkjames obsuga perla perl polchat darkjames obsuga chatw polchat - python leafnode obsuga pythona python rc wojtekka zdalne sterowanie ekg - readline * interfejs tekstowy readline remote darkjames zdalny interfejs uytkownika - rivchat darkjames rozmowy w sieci lan - rot13 darkjames proste szyfrowaie wiadomoci - ruby * obsuga skryptw Ruby ruby sim * szyfrowanie wiadomoci openssl sms wojtekka wysyanie smsw i takie tam /usr/bin/sms sniff darkjames sniffer GG pcap xmsg peres komunikacja via FS [inotify] xosd dredzik xosd ;] xosd --: vim:set ts=8 sts=8 sw=8 :-- ekg2-0.4~pre+20120506.1/plugins/autoresponder/000077500000000000000000000000001175142753400205305ustar00rootroot00000000000000ekg2-0.4~pre+20120506.1/plugins/autoresponder/autoresponder.c000066400000000000000000000116201175142753400235660ustar00rootroot00000000000000 /* Simple autoresponder * (C) 2006 Michał Górny */ #include "ekg2.h" #include static list_t list_find_str(const list_t first, const char *needle); static QUERY(autoresponder_message); static void autoresponder_varchange(const char *varname); int autoresponder_plugin_init(int prio); static int autoresponder_plugin_destroy(void); static char *config_autoresponder_question = NULL; static char *config_autoresponder_answer = NULL; static char *config_autoresponder_greeting = NULL; static char *config_autoresponder_allowed_sessions = NULL; static int config_autoresponder_match_mode = 1; static GRegex *autoresponder_answer_regex = NULL; static list_t autoresponder_allowed_uids; PLUGIN_DEFINE(autoresponder, PLUGIN_GENERIC, NULL); static list_t list_find_str(const list_t first, const char *needle) { list_t curr; for (curr = first; curr && xstrcmp(curr->data, needle); curr = curr->next); return curr; } static QUERY(autoresponder_message) { char *session = *(va_arg(ap, char**)); char *uid = *(va_arg(ap, char**)); char **UNUSED(rcpts) = *(va_arg(ap, char***)); char *text = *(va_arg(ap, char**)); guint32 *UNUSED(format)= *(va_arg(ap, guint32**)); time_t UNUSED(sent) = *(va_arg(ap, time_t*)); int class = *(va_arg(ap, int*)); char *UNUSED(seq) = *(va_arg(ap, char**)); int UNUSED(dobeep) = *(va_arg(ap, int*)); int UNUSED(secure) = *(va_arg(ap, int*)); session_t *s; /* if user didn't give us any answer to his question, we assume that sender should rewrite the question string */ const char *an = (config_autoresponder_answer && (*config_autoresponder_answer) ? config_autoresponder_answer : config_autoresponder_question); int matchoccured; if ((class >= EKG_MSGCLASS_SENT) /* at first, skip our messages */ || !config_autoresponder_question || !(*config_autoresponder_question) /* are we configured? */ || !(s = session_find(session)) /* session really exists? */ || !(cssfind(config_autoresponder_allowed_sessions, session, ',', 0) /* check, if session allows autoresponding */ || cssfind(config_autoresponder_allowed_sessions, session_alias_get(s), ',', 0)) /* and by alias */ || (userlist_find(s, uid)) /* check if it isn't currently on our roster */ || (window_find_s(s, uid)) /* or maybe we've already opened query? */ || (list_find_str(autoresponder_allowed_uids, uid))) /* search the allowed uids list */ /* TODO(porridge): || (gg session && uid == "gg:0") - he won't respond, and it may cause connection drops */ return 0; switch (config_autoresponder_match_mode) { case 0: /* exact match */ matchoccured = !xstrcmp(text, an); break; case 2: /* POSIX regex match */ matchoccured = g_regex_match(autoresponder_answer_regex, text, 0, NULL); break; case 1: /* substring match */ default: matchoccured = !!(xstrstr(text, an)); } if (matchoccured) { /* we've got the answer, hail the user! */ list_add(&autoresponder_allowed_uids, xstrdup(uid)); if (config_autoresponder_greeting && (*config_autoresponder_greeting)) command_exec_format(NULL, s, 1, "/msg %s %s", uid, config_autoresponder_greeting); } else { command_exec_format(NULL, s, 1, "/msg %s %s", uid, config_autoresponder_question); } return -1; /* ignore this message */ } static void autoresponder_varchange(const char *varname) { if (autoresponder_answer_regex) { g_regex_unref(autoresponder_answer_regex); autoresponder_answer_regex = NULL; } if (config_autoresponder_match_mode == 2 && config_autoresponder_answer && (*config_autoresponder_answer)) { GError *err = NULL; if (!((autoresponder_answer_regex = g_regex_new(config_autoresponder_answer, G_REGEX_RAW | G_REGEX_NO_AUTO_CAPTURE, 0, &err)))) { print("regex_error", err->message); g_error_free(err); config_autoresponder_match_mode = 1; } } } EXPORT int autoresponder_plugin_init(int prio) { PLUGIN_CHECK_VER("autoresponder"); plugin_register(&autoresponder_plugin, prio); query_connect(&autoresponder_plugin, "protocol-message", autoresponder_message, NULL); variable_add(&autoresponder_plugin, "allowed_sessions", VAR_STR, 1, &config_autoresponder_allowed_sessions, NULL, NULL, NULL); variable_add(&autoresponder_plugin, "answer", VAR_STR, 1, &config_autoresponder_answer, autoresponder_varchange, NULL, NULL); variable_add(&autoresponder_plugin, "greeting", VAR_STR, 1, &config_autoresponder_greeting, NULL, NULL, NULL); variable_add(&autoresponder_plugin, "match_mode", VAR_INT, 1, &config_autoresponder_match_mode, autoresponder_varchange, variable_map(3, 0, 0, "exact", 1, 2, "substring", 2, 1, "regex"), NULL); variable_add(&autoresponder_plugin, "question", VAR_STR, 1, &config_autoresponder_question, NULL, NULL, NULL); return 0; } static int autoresponder_plugin_destroy(void) { list_destroy(autoresponder_allowed_uids, 1); if (autoresponder_answer_regex) g_regex_unref(autoresponder_answer_regex); plugin_unregister(&autoresponder_plugin); return 0; } ekg2-0.4~pre+20120506.1/plugins/autoresponder/vars-en.txt000066400000000000000000000022441175142753400226460ustar00rootroot00000000000000allowed_sessions type: string default value: (none) A comma-separated list of sessions, for which autoresponder plugin will be used. Both UIDs and aliases can be used. Note that when using this plugin with a "gg" session, you need to ignore or add to your roster the special user "gg:0", who sends "system messages". Otherwise, the server may drop your connection right after establishing it. answer type: string default value: (none) Correct answer for your question - i.e. if user does reply with exact string, his further messages will be delivered to user. If null, then autoresponder will expect user to rewrite the 'question' string. greeting type: string default value: (none) If set, that message will be sent to user, when he sends correct answer. match_mode type: integer default value: 1 Defines match mode for comparing specified answer with incoming messages. Possible values: 0 - exact match 1 - substring match (answer must be somewhere in msg) 2 - POSIX regular expression match question type: string default value: (none) Question sent to unknown user on incoming message receival. If null, autoresponder will be disabled. ekg2-0.4~pre+20120506.1/plugins/autoresponder/vars-pl.txt000066400000000000000000000026271175142753400226640ustar00rootroot00000000000000allowed_sessions typ: tekst domyślna wartość: (brak) Oddzielona przecinkami lista sesji, dla których wtyczka autorespondera będzie używana. Może zawierać zarówno UID-y, jak i aliasy. Uwaga: korzystając z autorespondera w sesjach "gg", należy pamiętać aby dodać do listy kontaktów lub ignorowanych użytkownika "gg:0", który wysyła "wiadomości systemowe". W przeciwnym wypadku serwer może zrywać połączenie zaraz po jego nawiązaniu. answer typ: tekst domyślna wartość: (brak) Poprawna odpowiedź na twoje pytanie - tzn. jeśli użytkownik wyśle wiadomość o identycznej treści, jego dalsze wiadomości będą już dostarczane użytkownikowi. Jeśli nieustalone, autoreponder będzie oczekiwał od użytkownika dokładnego przepisania pytania. greeting typ: tekst domyślna wartość: (brak) Tekst wysyłany użytkownikowi w momencie otrzymania od niego poprawnej odpowiedzi. match_mode typ: liczba domyślna wartość: 1 Określa sposób porównywania wiadomości z odpowiedzią na pytanie. Dopuszczalne wartości: 0 - odpowiedź musi być identyczna 1 - odpowiedź musi zawierać się w wiadomości 2 - odpowiedź jest wyrażeniem regularnym (POSIX) question typ: tekst domyślna wartość: (brak) Pytanie, wysyłane do nieznanych użytkowników, w przypadku otrzymania od nich wiadomości. Jeśli nieustawione, autoresponder będzie wyłączony. ekg2-0.4~pre+20120506.1/plugins/check/000077500000000000000000000000001175142753400167135ustar00rootroot00000000000000ekg2-0.4~pre+20120506.1/plugins/check/check.c000066400000000000000000000012711175142753400201350ustar00rootroot00000000000000/* Integrity checks for ekg2 API * (c) 2011 Michał Górny */ #include "ekg2.h" #include void add_recode_tests(void); void add_static_aborts_tests(void); PLUGIN_DEFINE(check, PLUGIN_UI, NULL); static void simple_errprint(const gchar *out) { fputs(out, stderr); } EXPORT int check_plugin_init(int prio) { int argc = 0; char **argv = { NULL }; g_set_print_handler(simple_errprint); g_set_printerr_handler(simple_errprint); g_log_set_default_handler(g_log_default_handler, NULL); g_test_init(&argc, &argv, NULL); add_recode_tests(); add_static_aborts_tests(); g_test_run(); ekg_exit(); g_assert_not_reached(); } static int check_plugin_destroy(void) { return 0; } ekg2-0.4~pre+20120506.1/plugins/check/recode.c000066400000000000000000000035431175142753400203250ustar00rootroot00000000000000#include "ekg2.h" #include static const char input_iso[] = { 0x4c, 0x69, 0x74, 0x77, 0x6f, 0x21, 0x20, 0x4f, 0x6a, 0x63, 0x7a, 0x79, 0x7a, 0x6e, 0x6f, 0x20, 0x6d, 0x6f, 0x6a, 0x61, 0x21, 0x20, 0x74, 0x79, 0x20, 0x6a, 0x65, 0x73, 0x74, 0x65, 0xb6, 0x20, 0x6a, 0x61, 0x6b, 0x20, 0x7a, 0x64, 0x72, 0x6f, 0x77, 0x69, 0x65, 0x3b, 0x0a, 0x49, 0x6c, 0x65, 0x20, 0x63, 0x69, 0xea, 0x20, 0x74, 0x72, 0x7a, 0x65, 0x62, 0x61, 0x20, 0x63, 0x65, 0x6e, 0x69, 0xe6, 0x2c, 0x20, 0x74, 0x65, 0x6e, 0x20, 0x74, 0x79, 0x6c, 0x6b, 0x6f, 0x20, 0x73, 0x69, 0xea, 0x20, 0x64, 0x6f, 0x77, 0x69, 0x65, 0x2c, 0x0a, 0x4b, 0x74, 0x6f, 0x20, 0x63, 0x69, 0xea, 0x20, 0x73, 0x74, 0x72, 0x61, 0x63, 0x69, 0xb3, 0x2e, 0x20, 0x44, 0x7a, 0x69, 0xb6, 0x20, 0x70, 0x69, 0xea, 0x6b, 0x6e, 0x6f, 0xb6, 0xe6, 0x20, 0x74, 0x77, 0xb1, 0x20, 0x77, 0x20, 0x63, 0x61, 0xb3, 0x65, 0x6a, 0x20, 0x6f, 0x7a, 0x64, 0x6f, 0x62, 0x69, 0x65, 0x0a, 0x57, 0x69, 0x64, 0x7a, 0xea, 0x20, 0x69, 0x20, 0x6f, 0x70, 0x69, 0x73, 0x75, 0x6a, 0xea, 0x2c, 0x20, 0x62, 0x6f, 0x20, 0x74, 0xea, 0x73, 0x6b, 0x6e, 0x69, 0xea, 0x20, 0x70, 0x6f, 0x20, 0x74, 0x6f, 0x62, 0x69, 0x65, 0x2e, 0x0a, 0x00 }; static void check_fix_utf8(void) { gchar *cleaned; g_assert(!g_utf8_validate(input_iso, -1, NULL)); cleaned = g_strdup(input_iso); ekg_fix_utf8(cleaned); g_assert(g_utf8_validate(cleaned, -1, NULL)); g_free(cleaned); } static void check_recode_charp(void) { gchar *utf; char *iso; /* XXX: failable? */ utf = ekg_recode_from("iso-8859-2", input_iso); g_assert(strcmp(input_iso, utf)); g_assert(g_utf8_validate(utf, -1, NULL)); iso = ekg_recode_to("iso-8859-2", utf); g_assert(!strcmp(input_iso, iso)); g_free(utf); g_free(iso); } void add_recode_tests(void) { g_test_add_func("/recode/ekg_fix_utf8()", check_fix_utf8); g_test_add_func("/recode/ekg_recode_from() & ekg_recode_to()", check_recode_charp); } ekg2-0.4~pre+20120506.1/plugins/check/static-aborts.c000066400000000000000000000037111175142753400216400ustar00rootroot00000000000000#include "ekg/abort.h" #include static plugin_t p, p2; static int handler1_called = 0; static int handler2_called = 0; static void handler1(void) { handler1_called++; } static void handler2(void) { handler2_called++; } static void reset_counters(void) { handler1_called = 0; handler2_called = 0; } void basics(void) { g_assert(! handler1_called); g_assert(! handler2_called); ekg2_run_all_abort_handlers(); g_assert(! handler1_called); g_assert(! handler2_called); reset_counters(); g_assert(ekg2_register_abort_handler(handler1, &p)); ekg2_run_all_abort_handlers(); g_assert_cmpint(handler1_called, ==, 1); g_assert(! handler2_called); g_assert_cmpint(ekg2_unregister_abort_handlers_for_plugin(&p), ==, 1); } void interleaved(void) { reset_counters(); g_assert(ekg2_register_abort_handler(handler1, &p2)); g_assert(ekg2_register_abort_handler(handler1, &p)); g_assert(ekg2_register_abort_handler(handler2, &p)); g_assert_cmpint(ekg2_unregister_abort_handlers_for_plugin(&p2), ==, 1); g_assert(ekg2_register_abort_handler(handler2, &p)); ekg2_run_all_abort_handlers(); g_assert_cmpint(handler1_called, ==, 1); g_assert_cmpint(handler2_called, ==, 2); g_assert_cmpint(ekg2_unregister_abort_handlers_for_plugin(&p), ==, 3); } void capacity(void) { int number_exceeding_abort_handler_capacity = 500; int i, capacity = -1; reset_counters(); for (i = 1; i <= number_exceeding_abort_handler_capacity; i++) { if (! ekg2_register_abort_handler(handler1, &p)) { capacity = i - 1; break; } } g_assert_cmpint(capacity, >, 0); g_assert_cmpint(capacity, <, number_exceeding_abort_handler_capacity); ekg2_run_all_abort_handlers(); g_assert_cmpint(handler1_called, ==, capacity); g_assert_cmpint(ekg2_unregister_abort_handlers_for_plugin(&p), ==, capacity); } void add_static_aborts_tests(void) { g_test_add_func("/abort/basics", basics); g_test_add_func("/abort/capacity", capacity); g_test_add_func("/abort/interleaved", interleaved); } ekg2-0.4~pre+20120506.1/plugins/dbus/000077500000000000000000000000001175142753400165735ustar00rootroot00000000000000ekg2-0.4~pre+20120506.1/plugins/dbus/DEVELOPERS-BIG-FAT-WARNING000066400000000000000000000001601175142753400222750ustar00rootroot00000000000000Every string added to arguments with dbus_message_iter_append (..., DBUS_STRING, ...) MUST BE UTF-8 encoded! ekg2-0.4~pre+20120506.1/plugins/dbus/Interface-Readme000066400000000000000000000042141175142753400216120ustar00rootroot00000000000000TODO: Add support for telepathy and galago-project (system notifications) 1. I weren't sure which plugin_class to associate, so I've chosen PLUGIN_GENERIC... my initial idea of available interfaces: [JAVA-like NAMES!] the following interface proposed by Marcin Krzyzanowski in http://svn.hakore.com/ofi/ofi/README.OFI org.freedesktop.im m getProtocols() RETURNS: supported protocols lis as strings in format 'protocolname:' e.g.: DBUS_TYPE_STRING:'irc:' DBUS_TYPE_STRING:'gg:' DBUS_TYPE_STRING:'tlen:' DBUS_TYPE_STRING:'xmpp:' [or 'jid:' in ekg 0.1] m getPresence(DBUS_TYPE_STRING:contactUri) not yet available m openChat(DBUS_TYPE_STRING:contactUri) not yet available the following additional interfaces will be implemented: org.freedesktop.im m setStatus(DBUS_TYPE_STRING:presence, DBUS_TYPE_STRING:description) set given status in ALL sessions, that supports given presence (e.g: all protocols should support 'back' and 'away', but not every supports 'ffc' or 'xa') org.freedesktop.im.ekg2. m getProtcols not yet available m getSessions() RETURNS: following fours, sequentialy: (DBUS_TYPE_STRING:uid, DBUS_TYPE_BOOL:connected, DBUS_TYPE_STRING:status, DBUS_TYPE_STRING:description), [...] m getPlugins + .session m setStatus(DBUS_TYPE_STRING:sessionName, DBUS_TYPE_STRING:presence, DBUS_TYPE_STRING:description) set the given status in given session. Presence (e.g: back, away) must be supported by given session ... + .ui m getWindows() s switchWindow(window_id) s killWindow(window_id) ... [[ is protocol interface needed at all if we have session? ]] + .protocol + .protocol.irc ... + .protocol.gg ... + .protocol.jabber ... + .protocol.polchat ... + .crypto + .crypto.rot13 :D ... + .crytpo.gpg ... + .crypto.sim ... + .logging ... having something like org.ekg2.client.ui.windows.[window_id] [or org.ekg2.client.session.[session_id] would require to add_match/remove_match every window kill, window new [or session create, session destroy] this would be quite 'objective' approach, but seems quite senseless imho ekg2-0.4~pre+20120506.1/plugins/dbus/NOTES000066400000000000000000000001111175142753400173770ustar00rootroot00000000000000warning: in ubuntu gutsy dbus-launch has been moved to dbus-x11 package ekg2-0.4~pre+20120506.1/plugins/dbus/README000066400000000000000000000042141175142753400174540ustar00rootroot00000000000000TODO: Add support for telepathy and galago-project (system notifications) 1. I weren't sure which plugin_class to associate, so I've chosen PLUGIN_GENERIC... my initial idea of available interfaces: [JAVA-like NAMES!] the following interface proposed by Marcin Krzyzanowski in http://svn.hakore.com/ofi/ofi/README.OFI org.freedesktop.im m getProtocols() RETURNS: supported protocols lis as strings in format 'protocolname:' e.g.: DBUS_TYPE_STRING:'irc:' DBUS_TYPE_STRING:'gg:' DBUS_TYPE_STRING:'tlen:' DBUS_TYPE_STRING:'xmpp:' [or 'jid:' in ekg 0.1] m getPresence(DBUS_TYPE_STRING:contactUri) not yet available m openChat(DBUS_TYPE_STRING:contactUri) not yet available the following additional interfaces will be implemented: org.freedesktop.im m setStatus(DBUS_TYPE_STRING:presence, DBUS_TYPE_STRING:description) set given status in ALL sessions, that supports given presence (e.g: all protocols should support 'back' and 'away', but not every supports 'ffc' or 'xa') org.freedesktop.im.ekg2. m getProtcols not yet available m getSessions() RETURNS: following fours, sequentialy: (DBUS_TYPE_STRING:uid, DBUS_TYPE_BOOL:connected, DBUS_TYPE_STRING:status, DBUS_TYPE_STRING:description), [...] m getPlugins + .session m setStatus(DBUS_TYPE_STRING:sessionName, DBUS_TYPE_STRING:presence, DBUS_TYPE_STRING:description) set the given status in given session. Presence (e.g: back, away) must be supported by given session ... + .ui m getWindows() s switchWindow(window_id) s killWindow(window_id) ... [[ is protocol interface needed at all if we have session? ]] + .protocol + .protocol.irc ... + .protocol.gg ... + .protocol.jabber ... + .protocol.polchat ... + .crypto + .crypto.rot13 :D ... + .crytpo.gpg ... + .crypto.sim ... + .logging ... having something like org.ekg2.client.ui.windows.[window_id] [or org.ekg2.client.session.[session_id] would require to add_match/remove_match every window kill, window new [or session create, session destroy] this would be quite 'objective' approach, but seems quite senseless imho ekg2-0.4~pre+20120506.1/plugins/dbus/dbus.c000066400000000000000000000173051175142753400177020ustar00rootroot00000000000000/* * (C) Copyright 2006 Michal 'GiM' Spadlinski < gim913 a@t gmail d.o.t com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #include #define DBUS_API_SUBJECT_TO_CHANGE #include #include "dbus.h" #include "dbus_iface_im.h" #include "dbus_iface_im_ekg.h" #include "dbus_iface_im_ekg_ui.h" #include "dbus_iface_im_ekg_protocol.h" #include "dbus_iface_im_ekg_session.h" #include "dbus_iface_im_ekg_crypto.h" #include "dbus_iface_im_ekg_logging.h" PLUGIN_DEFINE(dbus, PLUGIN_GENERIC, NULL); static DBusConnection *conn; static DBusError err; static ekg2_dbus_iface_proto_t const ekg2_dbus_interfaces[] = { { "interface='" DBUS_ORG_FREEDESKTOP_IM_INTERFACE "'", DBUS_ORG_FREEDESKTOP_IM_INTERFACE, ekg2_dbus_iface_im }, { "interface='" DBUS_ORG_FREEDESKTOP_IM_INTERFACE ".ekg2'", DBUS_ORG_FREEDESKTOP_IM_INTERFACE ".ekg2", ekg2_dbus_iface_im_ekg2 }, { "interface='" DBUS_ORG_FREEDESKTOP_IM_INTERFACE ".ekg2.ui'", DBUS_ORG_FREEDESKTOP_IM_INTERFACE ".ekg2.ui", ekg2_dbus_iface_im_ekg2_ui }, { "interface='" DBUS_ORG_FREEDESKTOP_IM_INTERFACE ".ekg2.protocol'", DBUS_ORG_FREEDESKTOP_IM_INTERFACE ".ekg2.protocol", ekg2_dbus_iface_im_ekg2_protocol }, { "interface='" DBUS_ORG_FREEDESKTOP_IM_INTERFACE ".ekg2.session'", DBUS_ORG_FREEDESKTOP_IM_INTERFACE ".ekg2.session", ekg2_dbus_iface_im_ekg2_session }, { "interface='" DBUS_ORG_FREEDESKTOP_IM_INTERFACE ".ekg2.crypto'", DBUS_ORG_FREEDESKTOP_IM_INTERFACE ".ekg2.crypto", ekg2_dbus_iface_im_ekg2_crypto }, { "interface='" DBUS_ORG_FREEDESKTOP_IM_INTERFACE ".ekg2.logging'", DBUS_ORG_FREEDESKTOP_IM_INTERFACE ".ekg2.logging", ekg2_dbus_iface_im_ekg2_logging } }; #define EKG2_DBUS_MATCH(str) do { \ dbus_bus_add_match(conn, str, &err); \ if (dbus_error_is_set(&err)) { \ debug("match error at %s (probably resources exhausted): %s\n", str, err.message); \ dbus_connection_close(conn); conn = NULL; \ return -1; \ } \ } while(0) struct ekg2_dbus_watch_data { DBusConnection *con; DBusWatch *dbw; }; typedef struct ekg2_dbus_watch_data * ekg_dbus_watch_data_t; /* This is main message (from dbus) handler. * * This function iterates over ekg2_dbus_interfaces (you have read * README, don't you?), finds proper interface handler, and passes * control to it. Proper interface handlers are responsible for further * parsing. */ DBusHandlerResult ekg2_dbus_message_handler(DBusConnection *conn, DBusMessage *msg, void *empty) { int i; if (NULL == msg) { return DBUS_HANDLER_RESULT_HANDLED; } else { char const * const iface = dbus_message_get_interface(msg); debug("path: %s signal:%d\n", dbus_message_get_path(msg), dbus_message_get_type(msg) == DBUS_MESSAGE_TYPE_SIGNAL); debug("interface: %s\n", dbus_message_get_interface(msg)); for (i = 0; i < sizeof(ekg2_dbus_interfaces)/sizeof(ekg2_dbus_iface_proto_t); i++) if (!xstrcmp(iface, ekg2_dbus_interfaces[i].name) && ekg2_dbus_interfaces[i].handler) return ekg2_dbus_interfaces[i].handler(conn, msg, empty); } return DBUS_HANDLER_RESULT_HANDLED; } WATCHER(ekg2_dbus_read_watch) { ekg_dbus_watch_data_t p = (ekg_dbus_watch_data_t)data; debug("in read watch!\n"); if (type) { debug("fd lost in time o_0?\n"); return 0; } dbus_connection_ref(p->con); dbus_watch_handle(p->dbw, DBUS_WATCH_READABLE); /* run the dispatcher */ while (dbus_connection_dispatch(p->con) == DBUS_DISPATCH_DATA_REMAINS); dbus_connection_unref(p->con); return 0; } WATCHER(ekg2_dbus_write_watch) { /*ekg_dbus_watch_data_t p = (ekg_dbus_watch_data_t)data;*/ debug("in write watch!\n"); if (type) { debug("fd lost in time o_0?\n"); return 0; } return 0; } dbus_bool_t ekg2_dbus_add_watch(DBusWatch *w, void *data) { DBusConnection *con = (DBusConnection *)data; ekg_dbus_watch_data_t ekg_watcher_data; unsigned int flags; int fd; debug(" ekg2_dbus_add_watch step 1\n"); if (!dbus_watch_get_enabled(w)) return 1; debug(" ekg2_dbus_add_watch step 2\n"); ekg_watcher_data = xcalloc(1, sizeof(struct ekg2_dbus_watch_data)); ekg_watcher_data->con = con; ekg_watcher_data->dbw = w; fd = dbus_watch_get_fd(w); flags = dbus_watch_get_flags(w); if (flags & DBUS_WATCH_READABLE) { debug (" ekg2_dbus_add_watch readable bit set\n"); watch_add(&dbus_plugin, fd, WATCH_READ, ekg2_dbus_read_watch, ekg_watcher_data); } if (flags & DBUS_WATCH_WRITABLE) { debug (" ekg2_dbus_add_watch writeable bit set\n"); watch_add(&dbus_plugin, fd, WATCH_WRITE, ekg2_dbus_write_watch, ekg_watcher_data); } dbus_watch_set_data(w, ekg_watcher_data, NULL); return 1; } void ekg2_dbus_remove_watch(DBusWatch *w, void *data) { int fd = dbus_watch_get_fd(w), flags = dbus_watch_get_flags(w); debug(" ekg2_dbus_remove_watch step 1\n"); if (!dbus_watch_get_enabled(w)) return; debug(" ekg2_dbus_remove_watch step 2\n"); if (flags & DBUS_WATCH_WRITABLE) watch_remove(&dbus_plugin, fd, WATCH_WRITE); if (flags & DBUS_WATCH_READABLE) watch_remove(&dbus_plugin, fd, WATCH_READ); dbus_watch_set_data(w, NULL, NULL); xfree(data); return; } void ekg2_dbus_toggle_watch(DBusWatch *watch, void *data) { debug(" ekg2_dbus_toggle_watch\n"); if (dbus_watch_get_enabled(watch)) ekg2_dbus_add_watch(watch, data); else ekg2_dbus_remove_watch(watch, data); } /* This function connects plugin to dbus session in a system * it set up some dummy watch functions, finally, it informs * dbus about interfaces, we want to watch (defined in * ekg2_dbus_interfaces array) and associate main filter: * ekg2_dbus_message_handler() */ EXPORT int dbus_plugin_init(int prio) { int ret, i; PLUGIN_CHECK_VER("dbus"); dbus_error_init(&err); conn = dbus_bus_get(DBUS_BUS_SESSION, &err); if (dbus_error_is_set(&err)) { debug(" [-] dbus connection failed: %s\n", err.message); dbus_error_free(&err); return -1; } if (NULL == conn) { debug(" [-] dbus initialization failed\n"); return -1; } ret = dbus_bus_request_name(conn, DBUS_ORG_FREEDESKTOP_IM_INTERFACE, DBUS_NAME_FLAG_REPLACE_EXISTING , &err); if (dbus_error_is_set(&err)) { debug(" [-] dbus name setting error: %s\n", err.message); dbus_error_free(&err); dbus_connection_close(conn); conn = NULL; return -1; } if (DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER != ret) { debug(" [-] dbus, name setting error\n"); return -1; } if(!dbus_connection_set_watch_functions (conn, ekg2_dbus_add_watch, ekg2_dbus_remove_watch, ekg2_dbus_toggle_watch, conn, NULL)) { debug(" [-] dbus, couldn't set watches!\n"); dbus_connection_close(conn); conn = NULL; return -1; } for (i = 0; i < sizeof(ekg2_dbus_interfaces)/sizeof(ekg2_dbus_iface_proto_t); i++) EKG2_DBUS_MATCH(ekg2_dbus_interfaces[i].ifaceline); dbus_connection_add_filter(conn, ekg2_dbus_message_handler, NULL, NULL); dbus_connection_flush(conn); plugin_register(&dbus_plugin, prio); debug("plugin initialized!\n"); return 0; } static int dbus_plugin_destroy() { debug("destroying plugin\n"); if (conn) { debug("+calling set watch NULL\n"); dbus_connection_set_watch_functions(conn, NULL, NULL, NULL, /*data*/NULL, NULL); debug("+after call!\n"); dbus_connection_close(conn); } plugin_unregister(&dbus_plugin); return 0; } ekg2-0.4~pre+20120506.1/plugins/dbus/dbus.h000066400000000000000000000026551175142753400177110ustar00rootroot00000000000000#ifndef __EKG2_DBUS_H #define __EKG2_DBUS_H #define DBUS_API_SUBJECT_TO_CHANGE #include #define DBUS_ORG_FREEDESKTOP_IM_INTERFACE "org.freedesktop.im" #define EKG2_DBUS_IFACE_HANDLER(x) DBusHandlerResult x(DBusConnection *conn, DBusMessage *msg, void *data) struct ekg2_dbus_iface_proto { char *ifaceline; char *name; DBusHandleMessageFunction handler; }; typedef struct ekg2_dbus_iface_proto ekg2_dbus_iface_proto_t; struct ekg2_dbus_iface_function { char *name; int type; /* DBUS_MESSAGE_TYPE_METHOD_CALL or DBUS_MESSAGE_TYPE_SIGNAL */ DBusHandleMessageFunction handler; }; typedef struct ekg2_dbus_iface_function ekg2_dbus_iface_function_t; #define EKG2_DBUS_CALL_HANDLER_VARIABLES DBusMessage *reply; \ DBusMessageIter args; \ dbus_uint32_t serial = 0; #define EKG2_DBUS_INIT_REPLY reply = dbus_message_new_method_return(msg); \ dbus_message_iter_init_append(reply, &args) #define EKG2_DBUS_ADD(type, x) do { \ if (!dbus_message_iter_append_basic(&args, type, (x) )) { \ g_printerr("%s cannot allocate memory?\n", __FUNCTION__); \ abort(); \ } \ } while(0) #define EKG2_DBUS_ADD_STRING(x) EKG2_DBUS_ADD(DBUS_TYPE_STRING, x) #define EKG2_DBUS_SEND_REPLY do { \ if (!dbus_connection_send(conn, reply, &serial)) { \ debug("Cannot send reply!\n"); \ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; /* XXX */ \ } \ dbus_connection_flush(conn); \ } while(0) #endif /* __EKG2_DBUS_H */ ekg2-0.4~pre+20120506.1/plugins/dbus/dbus_iface_im.c000066400000000000000000000074331175142753400215170ustar00rootroot00000000000000#include "ekg2.h" #define DBUS_API_SUBJECT_TO_CHANGE #include #include "dbus.h" static EKG2_DBUS_IFACE_HANDLER(ekg2_dbus_iface_im_getProtocols); static EKG2_DBUS_IFACE_HANDLER(ekg2_dbus_iface_im_setStatus); static ekg2_dbus_iface_function_t const ekg2_dbus_iface_im_functions[] = { { "getProtocols", DBUS_MESSAGE_TYPE_METHOD_CALL, ekg2_dbus_iface_im_getProtocols }, { "setStatus", DBUS_MESSAGE_TYPE_METHOD_CALL, ekg2_dbus_iface_im_setStatus } }; static EKG2_DBUS_IFACE_HANDLER(ekg2_dbus_iface_im_getProtocols) { #define __FUNCTION__ "ekg2_dbus_iface_im_getProtocols" EKG2_DBUS_CALL_HANDLER_VARIABLES; const plugin_t *p; EKG2_DBUS_INIT_REPLY; for (p = plugins; p; p = p->next) { if (p->pclass == PLUGIN_PROTOCOL && p->priv) { struct protocol_plugin_priv *pp = p->priv; const char **a; for (a = pp->protocols; *a; a++) EKG2_DBUS_ADD_STRING(a); } } EKG2_DBUS_SEND_REPLY; return DBUS_HANDLER_RESULT_HANDLED; #undef __FUNCTION__ } /* m setStatus(DBUS_TYPE_STRING:presence, DBUS_TYPE_STRING:description) */ static EKG2_DBUS_IFACE_HANDLER(ekg2_dbus_iface_im_setStatus) { #define __FUNCTION__ "ekg2_dbus_iface_im_setStatus" EKG2_DBUS_CALL_HANDLER_VARIABLES; static char const status_errory[][20] = { "OK", "wrong argument", "session not found" }; char *error; DBusMessageIter iter; char const *param, *cmd; int current_type, st; session_t *s; list_t l; EKG2_DBUS_INIT_REPLY; dbus_message_iter_init (msg, &iter); if ((current_type = dbus_message_iter_get_arg_type (&iter)) != DBUS_TYPE_STRING) { error = status_errory[1]; EKG2_DBUS_ADD_STRING(&error); goto send_and_return; } dbus_message_iter_get_basic (&iter, ¶m); st = ekg_status_int(param); dbus_message_iter_next (&iter); if ((current_type = dbus_message_iter_get_arg_type (&iter)) != DBUS_TYPE_STRING) { error = status_errory[1]; EKG2_DBUS_ADD_STRING(&error); goto send_and_return; } dbus_message_iter_get_basic (&iter, ¶m); cmd = ekg_status_string(st, 1); for (l = sessions; l; l = l->next) { s = l->data; debug ("changing (%s) to: %s %s\n", s->uid, cmd, param); command_exec_format(NULL, s, 1, ("/%s %s"), cmd, param); } error = status_errory[0]; EKG2_DBUS_ADD_STRING(&error); send_and_return: EKG2_DBUS_SEND_REPLY; return DBUS_HANDLER_RESULT_HANDLED; #undef __FUNCTION__ } static EKG2_DBUS_IFACE_HANDLER(ekg2_dbus_iface_im_getPresence) { DBusMessage *reply; DBusMessageIter args; char **protos = NULL; dbus_uint32_t serial = 0; const plugin_t *p; reply = dbus_message_new_method_return(msg); dbus_message_iter_init_append(reply, &args); for (p = plugins; p; p = p->next) { if (p->pclass == PLUGIN_PROTOCOL && p->priv) { struct protocol_plugin_priv *pp = p->priv; const char **a; for (a = pp->protocols; *a; a++) { if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, a)) { g_printerr("ekg2_dbus_iface_im_getProtocols cannot allocate memory?\n"); abort(); } } } } if (!dbus_connection_send(conn, reply, &serial)) { debug("Cannot send reply!\n"); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; /* XXX */ } dbus_connection_flush(conn); return DBUS_HANDLER_RESULT_HANDLED; } EKG2_DBUS_IFACE_HANDLER(ekg2_dbus_iface_im) { int i, type; char const * const function_name = dbus_message_get_member(msg); type = dbus_message_get_type(msg); debug_error("zzzz> %s %d == %d\n", function_name, type,DBUS_MESSAGE_TYPE_METHOD_CALL ); for (i = 0; i < sizeof(ekg2_dbus_iface_im_functions) / sizeof(ekg2_dbus_iface_function_t); i++) { if (type == ekg2_dbus_iface_im_functions[i].type && ekg2_dbus_iface_im_functions[i].handler && !xstrcmp(function_name, ekg2_dbus_iface_im_functions[i].name)) { debug_function("calling handler\n"); return ekg2_dbus_iface_im_functions[i].handler(conn, msg, data); } } return DBUS_HANDLER_RESULT_HANDLED; } ekg2-0.4~pre+20120506.1/plugins/dbus/dbus_iface_im.h000066400000000000000000000003171175142753400215160ustar00rootroot00000000000000#ifndef __DBUS_IFACE_IM_H #define __DBUS_IFACE_IM_H #define DBUS_API_SUBJECT_TO_CHANGE #include #include "dbus.h" EKG2_DBUS_IFACE_HANDLER(ekg2_dbus_iface_im); #endif /* __DBUS_IFACE_IM_H */ ekg2-0.4~pre+20120506.1/plugins/dbus/dbus_iface_im_ekg.c000066400000000000000000000035061175142753400223420ustar00rootroot00000000000000#include "ekg2.h" #define DBUS_API_SUBJECT_TO_CHANGE #include #include "dbus.h" #include static EKG2_DBUS_IFACE_HANDLER(ekg2_dbus_iface_im_ekg2_getSessions); static ekg2_dbus_iface_function_t const ekg2_dbus_iface_im_functions[] = { { "getSessions", DBUS_MESSAGE_TYPE_METHOD_CALL, ekg2_dbus_iface_im_ekg2_getSessions } }; static EKG2_DBUS_IFACE_HANDLER(ekg2_dbus_iface_im_ekg2_getSessions) { #define __FUNCTION__ "ekg2_dbus_iface_im_ekg2_getSessions" EKG2_DBUS_CALL_HANDLER_VARIABLES; session_t *sl; char x[1] = "", *tmp_descr, *tmp; EKG2_DBUS_INIT_REPLY; for (sl = sessions; sl; sl = sl->next) { session_t *s = sl; EKG2_DBUS_ADD_STRING(&(s->uid)); #warning "XXX: Old API here, need updating." /* mg: updated? */ #if 0 EKG2_DBUS_ADD(DBUS_TYPE_BOOLEAN, &(s->connected)); #endif tmp = (char *)session_get(s, "status"); EKG2_DBUS_ADD_STRING(&tmp); /* XXX convert to utf before sending, d-bus sux? XXX */ tmp = (char *)session_descr_get(s); tmp_descr = ekg_locale_to_utf8_dup(tmp ? tmp : ""); EKG2_DBUS_ADD_STRING(&tmp_descr); xfree(tmp_descr); } EKG2_DBUS_SEND_REPLY; return DBUS_HANDLER_RESULT_HANDLED; #undef __FUNCTION__ } EKG2_DBUS_IFACE_HANDLER(ekg2_dbus_iface_im_ekg2) { int i, type; char const * const function_name = dbus_message_get_member(msg); type = dbus_message_get_type(msg); debug_error("zzzz> %s %d == %d\n", function_name, type, DBUS_MESSAGE_TYPE_METHOD_CALL ); for (i = 0; i < sizeof(ekg2_dbus_iface_im_functions) / sizeof(ekg2_dbus_iface_function_t); i++) { if (type == ekg2_dbus_iface_im_functions[i].type && ekg2_dbus_iface_im_functions[i].handler && !xstrcmp(function_name, ekg2_dbus_iface_im_functions[i].name)) { debug_function("calling handler\n"); return ekg2_dbus_iface_im_functions[i].handler(conn, msg, data); } } return DBUS_HANDLER_RESULT_HANDLED; } ekg2-0.4~pre+20120506.1/plugins/dbus/dbus_iface_im_ekg.h000066400000000000000000000003431175142753400223430ustar00rootroot00000000000000#ifndef __DBUS_IFACE_IM_EKG2_H #define __DBUS_IFACE_IM_EKG2_H #define DBUS_API_SUBJECT_TO_CHANGE #include #include "dbus.h" EKG2_DBUS_IFACE_HANDLER(ekg2_dbus_iface_im_ekg2); #endif /* __DBUS_IFACE_IM_EKG2_H */ ekg2-0.4~pre+20120506.1/plugins/dbus/dbus_iface_im_ekg_crypto.c000066400000000000000000000004231175142753400237350ustar00rootroot00000000000000#include "ekg2.h" #define DBUS_API_SUBJECT_TO_CHANGE #include #include "dbus.h" EKG2_DBUS_IFACE_HANDLER(ekg2_dbus_iface_im_ekg2_crypto) { debug_error("XXXXXwe're in handler dude: %s\n", dbus_message_get_interface(msg)); return DBUS_HANDLER_RESULT_HANDLED; } ekg2-0.4~pre+20120506.1/plugins/dbus/dbus_iface_im_ekg_crypto.h000066400000000000000000000003771175142753400237520ustar00rootroot00000000000000#ifndef __DBUS_IFACE_IM_EKG2_CRYPTO_H #define __DBUS_IFACE_IM_EKG2_CRYPTO_H #define DBUS_API_SUBJECT_TO_CHANGE #include #include "dbus.h" EKG2_DBUS_IFACE_HANDLER(ekg2_dbus_iface_im_ekg2_crypto); #endif /* __DBUS_IFACE_IM_EKG2_CRYPTO_H */ ekg2-0.4~pre+20120506.1/plugins/dbus/dbus_iface_im_ekg_logging.c000066400000000000000000000004241175142753400240440ustar00rootroot00000000000000#include "ekg2.h" #define DBUS_API_SUBJECT_TO_CHANGE #include #include "dbus.h" EKG2_DBUS_IFACE_HANDLER(ekg2_dbus_iface_im_ekg2_logging) { debug_error("XXXXXwe're in handler dude: %s\n", dbus_message_get_interface(msg)); return DBUS_HANDLER_RESULT_HANDLED; } ekg2-0.4~pre+20120506.1/plugins/dbus/dbus_iface_im_ekg_logging.h000066400000000000000000000004031175142753400240460ustar00rootroot00000000000000#ifndef __DBUS_IFACE_IM_EKG2_LOGGING_H #define __DBUS_IFACE_IM_EKG2_LOGGING_H #define DBUS_API_SUBJECT_TO_CHANGE #include #include "dbus.h" EKG2_DBUS_IFACE_HANDLER(ekg2_dbus_iface_im_ekg2_logging); #endif /* __DBUS_IFACE_IM_EKG2_LOGGING_H */ ekg2-0.4~pre+20120506.1/plugins/dbus/dbus_iface_im_ekg_protocol.c000066400000000000000000000004251175142753400242600ustar00rootroot00000000000000#include "ekg2.h" #define DBUS_API_SUBJECT_TO_CHANGE #include #include "dbus.h" EKG2_DBUS_IFACE_HANDLER(ekg2_dbus_iface_im_ekg2_protocol) { debug_error("XXXXXwe're in handler dude: %s\n", dbus_message_get_interface(msg)); return DBUS_HANDLER_RESULT_HANDLED; } ekg2-0.4~pre+20120506.1/plugins/dbus/dbus_iface_im_ekg_protocol.h000066400000000000000000000004071175142753400242650ustar00rootroot00000000000000#ifndef __DBUS_IFACE_IM_EKG2_PROTOCOL_H #define __DBUS_IFACE_IM_EKG2_PROTOCOL_H #define DBUS_API_SUBJECT_TO_CHANGE #include #include "dbus.h" EKG2_DBUS_IFACE_HANDLER(ekg2_dbus_iface_im_ekg2_protocol); #endif /* __DBUS_IFACE_IM_EKG2_PROTOCOL_H */ ekg2-0.4~pre+20120506.1/plugins/dbus/dbus_iface_im_ekg_session.c000066400000000000000000000053771175142753400241150ustar00rootroot00000000000000#include "ekg2.h" #include #include "dbus.h" static EKG2_DBUS_IFACE_HANDLER(ekg2_dbus_iface_im_ekg2_session_setStatus); static ekg2_dbus_iface_function_t const ekg2_dbus_iface_im_ekg2_session_functions[] = { { "setStatus", DBUS_MESSAGE_TYPE_METHOD_CALL, ekg2_dbus_iface_im_ekg2_session_setStatus } }; static EKG2_DBUS_IFACE_HANDLER(ekg2_dbus_iface_im_ekg2_session_setStatus) { static const char status_errory[][20] = { "OK", "wrong argument", "session not found" }; char *error; EKG2_DBUS_CALL_HANDLER_VARIABLES; DBusMessageIter iter; char const *param, *cmd; int current_type, st; session_t *s; EKG2_DBUS_INIT_REPLY; dbus_message_iter_init (msg, &iter); if ((current_type = dbus_message_iter_get_arg_type (&iter)) != DBUS_TYPE_STRING) { error = status_errory[1]; EKG2_DBUS_ADD_STRING(&error); goto send_and_return; } else { dbus_message_iter_get_basic (&iter, ¶m); if ((s = session_find(param)) == NULL) { error = status_errory[2]; EKG2_DBUS_ADD_STRING(&error); goto send_and_return; } } dbus_message_iter_next (&iter); if ((current_type = dbus_message_iter_get_arg_type (&iter)) != DBUS_TYPE_STRING) { error = status_errory[1]; EKG2_DBUS_ADD_STRING(&error); goto send_and_return; } dbus_message_iter_get_basic (&iter, ¶m); st = ekg_status_int(param); dbus_message_iter_next (&iter); if ((current_type = dbus_message_iter_get_arg_type (&iter)) != DBUS_TYPE_STRING) { error = status_errory[1]; EKG2_DBUS_ADD_STRING(&error); goto send_and_return; } dbus_message_iter_get_basic (&iter, ¶m); /* session_unidle(s); */ /* should we use status provided in dbus parameter * or keep current status */ cmd = ekg_status_string(st, 1); debug ("changing to: %s %s\n", cmd, param); command_exec_format(NULL, s, 1, ("/%s %s"), cmd, param); error = status_errory[0]; EKG2_DBUS_ADD_STRING(&error); send_and_return: EKG2_DBUS_SEND_REPLY; return DBUS_HANDLER_RESULT_HANDLED; } EKG2_DBUS_IFACE_HANDLER(ekg2_dbus_iface_im_ekg2_session) { int i, type; char const * const function_name = dbus_message_get_member(msg); debug_error("XXXXXwe're in handler dude: %s\n", dbus_message_get_interface(msg)); type = dbus_message_get_type(msg); debug_error("zzzz> %s %d == %d\n", function_name, type, DBUS_MESSAGE_TYPE_METHOD_CALL ); for (i = 0; i < sizeof(ekg2_dbus_iface_im_ekg2_session_functions) / sizeof(ekg2_dbus_iface_function_t); i++) { if (type == ekg2_dbus_iface_im_ekg2_session_functions[i].type && ekg2_dbus_iface_im_ekg2_session_functions[i].handler && !xstrcmp(function_name, ekg2_dbus_iface_im_ekg2_session_functions[i].name)) { debug_function("calling handler\n"); return ekg2_dbus_iface_im_ekg2_session_functions[i].handler(conn, msg, data); } } return DBUS_HANDLER_RESULT_HANDLED; } ekg2-0.4~pre+20120506.1/plugins/dbus/dbus_iface_im_ekg_session.h000066400000000000000000000004031175142753400241030ustar00rootroot00000000000000#ifndef __DBUS_IFACE_IM_EKG2_SESSION_H #define __DBUS_IFACE_IM_EKG2_SESSION_H #define DBUS_API_SUBJECT_TO_CHANGE #include #include "dbus.h" EKG2_DBUS_IFACE_HANDLER(ekg2_dbus_iface_im_ekg2_session); #endif /* __DBUS_IFACE_IM_EKG2_SESSION_H */ ekg2-0.4~pre+20120506.1/plugins/dbus/dbus_iface_im_ekg_ui.c000066400000000000000000000004171175142753400230350ustar00rootroot00000000000000#include "ekg2.h" #define DBUS_API_SUBJECT_TO_CHANGE #include #include "dbus.h" EKG2_DBUS_IFACE_HANDLER(ekg2_dbus_iface_im_ekg2_ui) { debug_error("XXXXXwe're in handler dude: %s\n", dbus_message_get_interface(msg)); return DBUS_HANDLER_RESULT_HANDLED; } ekg2-0.4~pre+20120506.1/plugins/dbus/dbus_iface_im_ekg_ui.h000066400000000000000000000003601175142753400230370ustar00rootroot00000000000000#ifndef __DBUS_IFACE_IM_EKG2_UI_H #define __DBUS_IFACE_IM_EKG2_UI_H #define DBUS_API_SUBJECT_TO_CHANGE #include #include "dbus.h" EKG2_DBUS_IFACE_HANDLER(ekg2_dbus_iface_im_ekg2_ui); #endif /* __DBUS_IFACE_IM_EKG2_UI_H */ ekg2-0.4~pre+20120506.1/plugins/dbus/testing/000077500000000000000000000000001175142753400202505ustar00rootroot00000000000000ekg2-0.4~pre+20120506.1/plugins/dbus/testing/sender_getProtocols.c000066400000000000000000000054131175142753400244430ustar00rootroot00000000000000#include //#define DBUS_API_SUBJECT_TO_CHANGE #include int main(int argc, char **argv) { DBusConnection *conn; DBusMessage *msg; DBusMessageIter args; DBusPendingCall* pending; DBusError err; dbus_uint32_t serial = 0; // unique number to associate replies with requests int current_type; char *buf, *retek; int val, type; dbus_uint32_t level; int ret; dbus_error_init(&err); // connect to the bus conn = dbus_bus_get(DBUS_BUS_SESSION, &err); if (dbus_error_is_set(&err)) { fprintf(stderr, " [+] Connection Error (%s)\n", err.message); dbus_error_free(&err); } if (NULL == conn) { return 1; } // create a signal and check for errors msg = dbus_message_new_method_call("org.freedesktop.im", // target for the method call "/dupa/dupa", // object to call on "org.freedesktop.im", // interface to call on "getProtocols"); // method name if (NULL == msg) { fprintf(stderr, "Message Null\n"); return 1; } // append arguments onto signal // dbus_message_iter_init_append(msg, &args); /* buf = strdup("super tajne dane!"); if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &buf)) { fprintf(stderr, "Out Of Memory!\n"); return 1; } free(buf); val = 666; if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_INT32, &val)) { fprintf(stderr, "Out Of Memory!\n"); return 1; }*/ // send message and get a handle for a reply if (!dbus_connection_send_with_reply (conn, msg, &pending, -1)) { // -1 is default timeout fprintf(stderr, "Out Of Memory!\n"); return (1); } if (NULL == pending) { fprintf(stderr, "Pending Call Null\n"); return (1); } dbus_connection_flush(conn); printf("Request Sent\n"); // free message dbus_message_unref(msg); // block until we recieve a reply dbus_pending_call_block(pending); // get the reply message msg = dbus_pending_call_steal_reply(pending); if (NULL == msg) { fprintf(stderr, "Reply Null\n"); return (1); } // free the pending message handle dbus_pending_call_unref(pending); dbus_message_iter_init (msg, &args); while ((type = dbus_message_iter_get_arg_type(&args)) != DBUS_TYPE_INVALID) { if (DBUS_TYPE_STRING == type) { dbus_message_iter_get_basic(&args, &retek); fprintf(stderr, "blink: %s\n", retek); } dbus_message_iter_next (&args); } // free reply and close connection dbus_message_unref(msg); dbus_connection_close(conn); /* // send the message and flush the connection if (!dbus_connection_send(conn, msg, &serial)) { fprintf(stderr, "Out Of Memory!\n"); return 1; } dbus_connection_flush(conn); // free the message dbus_message_unref(msg); dbus_connection_close(conn);*/ return 0; } ekg2-0.4~pre+20120506.1/plugins/dbus/testing/sender_getSessions.c000066400000000000000000000056031175142753400242660ustar00rootroot00000000000000#include #include int main(int argc, char **argv) { DBusConnection *conn; DBusMessage *msg; DBusMessageIter args; DBusPendingCall* pending; DBusError err; dbus_uint32_t serial = 0; // unique number to associate replies with requests int current_type; char *buf, *retek; int val, type, boolek; dbus_uint32_t level; int ret; dbus_error_init(&err); // connect to the bus conn = dbus_bus_get(DBUS_BUS_SESSION, &err); if (dbus_error_is_set(&err)) { fprintf(stderr, " [+] Connection Error (%s)\n", err.message); dbus_error_free(&err); } if (NULL == conn) { return 1; } // create a signal and check for errors msg = dbus_message_new_method_call("org.freedesktop.im", // target for the method call "/dupa/dupa", // object to call on "org.freedesktop.im.ekg2", // interface to call on "getSessions"); // method name if (NULL == msg) { fprintf(stderr, "Message Null\n"); return 1; } // append arguments onto signal // dbus_message_iter_init_append(msg, &args); /* buf = strdup("super tajne dane!"); if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &buf)) { fprintf(stderr, "Out Of Memory!\n"); return 1; } free(buf); val = 666; if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_INT32, &val)) { fprintf(stderr, "Out Of Memory!\n"); return 1; }*/ // send message and get a handle for a reply if (!dbus_connection_send_with_reply (conn, msg, &pending, -1)) { // -1 is default timeout fprintf(stderr, "Out Of Memory!\n"); return (1); } if (NULL == pending) { fprintf(stderr, "Pending Call Null\n"); return (1); } dbus_connection_flush(conn); printf("Request Sent\n"); // free message dbus_message_unref(msg); // block until we recieve a reply dbus_pending_call_block(pending); // get the reply message msg = dbus_pending_call_steal_reply(pending); if (NULL == msg) { fprintf(stderr, "Reply Null\n"); return (1); } // free the pending message handle dbus_pending_call_unref(pending); dbus_message_iter_init (msg, &args); while ((type = dbus_message_iter_get_arg_type(&args)) != DBUS_TYPE_INVALID) { if (DBUS_TYPE_STRING == type) { dbus_message_iter_get_basic(&args, &retek); fprintf(stderr, " str: %s\n", retek); } else if (DBUS_TYPE_BOOLEAN) { dbus_message_iter_get_basic(&args, &boolek); fprintf(stderr, "bool: %s\n", boolek?"yes":"no"); } dbus_message_iter_next (&args); } // free reply and close connection dbus_message_unref(msg); //dbus_connection_close(conn); /* // send the message and flush the connection if (!dbus_connection_send(conn, msg, &serial)) { fprintf(stderr, "Out Of Memory!\n"); return 1; } dbus_connection_flush(conn); // free the message dbus_message_unref(msg); dbus_connection_close(conn);*/ return 0; } ekg2-0.4~pre+20120506.1/plugins/dummy/000077500000000000000000000000001175142753400167715ustar00rootroot00000000000000ekg2-0.4~pre+20120506.1/plugins/dummy/main.c000066400000000000000000000022701175142753400200620ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2003 Jan Kowalski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License Version * 2.1 as published by the Free Software Foundation. * * This program is distributed in the hope that 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. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" PLUGIN_DEFINE(dummy, PLUGIN_GENERIC, NULL); int dummy_plugin_init(int prio) { PLUGIN_CHECK_VER("dummy"); plugin_register(&dummy_plugin, prio); debug("dummy plugin registered\n"); return 0; } static int dummy_plugin_destroy() { plugin_unregister(&dummy_plugin); debug("dummy plugin unregistered\n"); return 0; } /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/plugins/gg/000077500000000000000000000000001175142753400162335ustar00rootroot00000000000000ekg2-0.4~pre+20120506.1/plugins/gg/_unused.c000066400000000000000000000003621175142753400200420ustar00rootroot00000000000000#if 0 /* * Unused stuff. * * For archaeological and sentimental purposes only * */ /* never used? wtf? */ static void gg_dcc_close_handler(dcc_t *d) { struct gg_dcc *g; if (!d || !(g = d->priv)) return; gg_dcc_free(g); } #endif ekg2-0.4~pre+20120506.1/plugins/gg/commands-en.txt000066400000000000000000000134431175142753400212020ustar00rootroot00000000000000// descripton for gg-plugin proceedings // (c) copyright 2001-2003 wojtek kaniewski // (c) copyright 2004 piotr kupisiewicz // (c) copyright 2005 mateusz samonek _autoaway parameters: short description: automatically change status to away _autoback parameters: short description: automatically change status to available _descr parameters: [description/-] short description: change status description If description won't be defined then ekg2 will use random description. Giving ,,%T-%n'' instead of description will cause status without description. away parameters: [description/-] short description: change status to away. If description won't be defined then ekg2 will use random description. Giving ,,%T-%n'' instead of description will cause status without description. back parameters: [description/-] short description: change status to available. If description won't be defined then ekg2 will use random description. Giving ,,%T-%n'' instead of description will cause status without description. block parameters: [uid/alias] short description: add to blocked users list change parameters: short description: change information in public catalogue -f, --first -l, --last -n, --nick -b, --born -c, --city -N, --familyname maiden name -C, --familycity home town -F, --female sex: female -M, --male sex: male If some option won't be defined, that value will be erased in the public catalogue. Using option ,,%T-%n'' will erase %Tall%n values. chat parameters: short description: send message Command is similar to %Tmsg%n, but it send message like dialogue, not separately. check_conn parameters: short description: check if person is connected It checks if person is connected. That persons client must support images. Tested on GG 6.0 for Windows. In case of using clients: TLEN, kadu, ekg i ekg2 command don't work propertly (person will get empty message). With that command we can check if the 'non available' person is disconnected or only invisible. connect parameters: short description: connect with server dcc parameters: [options] short description: support for p2p connections [r]send sends specified file get [uin/alias/#id] get file. resume [uin/alias/#id] resume getting file [r]voice start voice dialogue close close connection list display connection list P2P connections need %Tdcc%n option. Commands %Trsend%n and %Trvoice%n sends request to connect other client with our and it is usefull, when we can't connect on our own. disconnect parameters: [description/-] short description: disconnect from server If option %Tauto_reconnect%n is on, after using this command, program will try to connect automatically after some time. dnd parameters: [description/-] short description: change status to do not disturb. If description won't be defined then ekg2 will use random description. Giving ,,%T-%n'' instead of description will cause status without description. ffc parameters: [description/-] short description: change status to free for chat. If description won't be defined then ekg2 will use random description. Giving ,,%T-%n'' instead of description will cause status without description. find parameters: [uin|options] short description: search users in public catalogue -u, --uin -f, --first -l, --last -n, --nick -c, --city -b, --born birth date range -a, --active only with available status. -F, --female sex: female -M, --male sex: male -s, --start display from n-number -A, --all display all -S, --stop stop all. image parameters: short description: send image to person Picture will be send as a separate message. Recipients' client must support images. invisible parameters: [description/-] short description: change status to invisible If description won't be defined then ekg2 will use random description. Giving ,,%T-%n'' instead of description will cause status without description. modify parameters: short description: change information in contact list You can use options like: -f, --first -l, --last -n, --nick nickname -u, --uin -g, --group [+/-]<@group> add or remove from group -p, --phone mobile number -o, --offline be offline for that person -O, --online be online for that person Two last options are working when 'only for friends' mode is on. msg parameters: short description: send message passwd parameters: short description: change user password reconnect parameters: short description: disconnect and connect to server. register parameters: short description: register new account Before registration use command 'token'. remind parameters: [uin] short description: send password on email token parameters: short description: download token from server unblock parameters: |* short description: remove from blocked users list unregister parameters: short description: remove account from server userlist parameters: [option] short description: contact list on server Contact list on server "list [-p|-g|-c]": -c, --clear remove list from server -g, --get get list from server -p, --put put list on server ekg2-0.4~pre+20120506.1/plugins/gg/commands-pl.txt000066400000000000000000000165751175142753400212240ustar00rootroot00000000000000// opis komend dla protokołu gg // (c) copyright 2001-2003 wojtek kaniewski // (c) copyright 2004 piotr kupisiewicz _autoaway parametry: krotki opis: automatycznie zmienia stan na zajęty _autoback parametry: krotki opis: automatycznie zmienia stan na dostępny _descr parametry: [opis/-] krotki opis: zmienia opis stanu Jeżeli nie podano opisu to będzie szukany losowy opis. Podanie ,,%T-%n'' zamiast powodu spowoduje wyczyszczenie opisu. away parametry: [opis/-] krotki opis: zmienia stan na zajęty Jeżeli nie podano opisu to będzie szukany losowy opis. Podanie ,,%T-%n'' zamiast powodu spowoduje wyczyszczenie opisu. back parametry: [opis/-] krotki opis: zmienia stan na dostępny Jeżeli nie podano opisu to będzie szukany losowy opis. Podanie ,,%T-%n'' zamiast powodu spowoduje wyczyszczenie opisu. block parametry: [numer/alias] krotki opis: dodaje do listy blokowanych change parametry: krotki opis: zmienia informacje w katalogu publicznym -f, --first -l, --last -n, --nick -b, --born -c, --city -N, --familyname nazwisko panieńskie -C, --familycity miasto rodzinne -F, --female kobieta -M, --male mężczyzna Jeśli któryś z parametrów nie zostanie podany, jego wartość zostanie wyczyszczona w katalogu publicznym. Podanie parametru ,,%T-%n'' wyczyści %Twszystkie%n pola. chat parametry: krotki opis: wysyła wiadomość Polecenie jest podobne do %Tmsg%n, ale wysyła wiadomość w ramach rozmowy, a nie jako pojedynczą. check_conn parametry: [alias] krotki opis: sprawdza czy podana osoba jest połączona Sprawdza czy podana osoba jest połączona. Klient tej osoby musi obsługiwać obrazki. Przetestowane na GG 6.x dla Windows. W przypadku niektórych klientów jak np. tlen komenda nie działa prawidłowo (osobie którą sprawdzamy pojawia się pusta wiadomość). Dzięki tej funkcji można sprawdzić czy osoba, którą widzimy jako niedostępna jest niewidoczna. Jeżeli brak aliasu jako parametr sprawdzana jest osoba, z którą rozmowa znajduję się w aktualnym okienku. Od wersji 7.0 nie jest możliwe sprawdzenie w ten sposób czy ktoś jest niewidoczny. Wszystkie obrazki, a nawet tekst, do którego dołoączony jest obrazek nie jest pokazywany użytkownikowi. Po 15 sekundach użytkownik uznawany jest za niepołączonego. W momencie gdy użytkownik zostanie połączony pojawi się również komunikat informujący o tym. connect parametry: krotki opis: łączy się z serwerem dcc parametry: [opcje] krotki opis: obsługa bezpośrednich połączeń [r]send <ścieżka> wysyła podany plik get [numer/alias/#id] akceptuje przysyłany plik resume [numer/alias/#id] wznawia pobieranie pliku [r]voice rozpoczyna rozmowę głosową close zamyka połączenie list wyświetla listę połączeń Połączenia bezpośrednie wymagają włączonej opcji %Tdcc%n. Komendy %Trsend%n i %Trvoice%n wysyłają żądanie połączenia się drugiego klienta z naszym i są przydatne, gdy nie jesteśmy w stanie się z nim sami połączyć. disconnect parametry: [powód/-] krotki opis: rozłącza się od serwera Jeśli włączona jest opcja %Tauto_reconnect%n, po wywołaniu tej komendy, program nadal będzie próbował się automatycznie łączyć po określonym czasie. dnd parametry: [opis/-] krotki opis: zmienia stan na nie przeszkadzać Jeżeli nie podano opisu to będzie szukany losowy opis. Podanie ,,%T-%n'' zamiast powodu spowoduje wyczyszczenie opisu. ffc parametry: [opis/-] krotki opis: zmienia stan na chętny do rozmowy Jeżeli nie podano opisu to będzie szukany losowy opis. Podanie ,,%T-%n'' zamiast powodu spowoduje wyczyszczenie opisu. find parametry: [numer|opcje] krotki opis: przeszukiwanie katalogu publicznego -u, --uin -f, --first -l, --last -n, --nick -c, --city -b, --born zakres roku urodzenia -a, --active tylko dostępni -F, --female kobiety -M, --male mężczyźni -s, --start wyświetla od n-tego numeru -A, --all wyświetla wszystkich -S, --stop zatrzymuje wszystkie poszukiwania image parametry: krotki opis: wysyła obrazek do podanej osoby Obrazek zostanie wysłany jako osobna wiadomość. Klient adresata musi obsługiwać obrazki. invisible parametry: [opis/-] krotki opis: zmienia stan na niewidoczny Jeżeli nie podano opisu to będzie szukany losowy opis. Podanie ,,%T-%n'' zamiast powodu spowoduje wyczyszczenie opisu. modify parametry: krotki opis: zmienia wpisy w liście kontaktów Opcje mogą być następujące: -f, --first -l, --last -n, --nick pseudonim (nie jest używany) -u, --uin -g, --group [+/-]<@grupa> dodaje lub usuwa z grupy można podać więcej grup po przecinku -p, --phone numer telefonu komórkowego -o, --offline bądź niedostępny dla danej osoby -O, --online bądź widoczny dla danej osoby Dwie ostatnie opcje działają tylko, gdy włączony jest tryb ,,tylko dla znajomych''. msg parametry: krotki opis: wysyła wiadomość Można podać większą ilość odbiorców oddzielając ich numery lub pseudonimy przecinkiem (ale bez odstępów). Jeśli zamiast odbiorcy podany zostanie znak ,,%T*%n'', to wiadomość będzie wysłana do wszystkich aktualnych rozmówców. passwd parametry: krotki opis: zmienia hasło użytkownika reconnect parametry: krotki opis: rozłącza i łączy się ponownie register parametry: krotki opis: rejestruje nowe konto Przed rejestracją należy pobrać token komendą token. remind parametry: [numer] krotki opis: wysyła hasło na skrzynkę pocztową token parametry: krotki opis: pobiera z serwera token Komenda ta jest niezbędna do rejestracji i zmiany hasła. Ma na celu zapewnienie serwera, że operację przeprowadza użytkownik, a nie automat. Jeśli system zawiera odpowiednie biblioteki, token zostanie wyświetlony na ekranie. W przeciwnym wypadku zostanie podana ścieżka, pod którą zapisano plik graficzny zawierający token. unblock parametry: |* krotki opis: usuwa z listy blokowanych unregister parametry: krotki opis: usuwa konto z serwera Podanie numeru i hasła jest niezbędne ze względów bezpieczeństwa. Nikt nie chciałby chyba usunąć konta przypadkowo, bez żadnego potwierdzenia. Przed rejestracją należy pobrać token komendą token. userlist parametry: [opcje] krotki opis: lista kontaktów na serwerze Lista kontaktów na serwerze "list [-p|-g|-c]": -c, --clear usuwa listę z serwera -g, --get pobiera listę z serwera -p, --put umieszcza listę na serwerze $Id$ ekg2-0.4~pre+20120506.1/plugins/gg/commands.c000066400000000000000000001350261175142753400202070ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2003 Wojtek Kaniewski * 2003 Adam Czerwiski * 2004 Piotr Kupisiewicz * 2006 Adam Mikuta * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #ifdef HAVE_LIBGIF # define GIF_OCR #endif #ifdef HAVE_LIBUNGIF # define GIF_OCR #endif #ifdef __APPLE__ #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_JPEGLIB_H # include #endif #ifdef HAVE_GIF_LIB_H # include /* open() */ # include #endif #include "dcc.h" #include "gg.h" #include "images.h" #include "misc.h" #include "pubdir.h" #include "pubdir50.h" #include "token.h" /* * session_descr_sync() * * For the given session, set (from new_reason) or delete (if "-" is passed or * NULL is passed while config_keep_reason is off) the description. * Return what is the session description from now on (NULL or newly allocated * string). * * - session - pointer to a session_t object * - new_reason - string containing the new description, or NULL, if the user * has not expressed a will to change the description */ static char* session_descr_sync(session_t* session, const char* new_reason) { char* myreason; if (new_reason) { if (!xstrcmp(new_reason, "-")) myreason = NULL; else myreason = xstrdup(new_reason); session_descr_set(session, myreason); } else { if (!config_keep_reason) session_descr_set(session, NULL); myreason = xstrdup(session_descr_get(session)); } return myreason; } static COMMAND(gg_command_connect) { int isreconnect = !xstrcmp(name, "reconnect"); gg_private_t *g = session_private_get(session); uin_t uin = (session) ? atoi(session->uid + 3) : 0; if (!xstrcmp(name, ("disconnect")) || isreconnect) { /* if ,,reconnect'' timer exists we should stop doing */ /* if ,,gg:reconnect'' command is executed, we should always try to do 'disconnect; connect;' */ if (timer_remove_session(session, "reconnect") == 0 && !isreconnect) { printq("auto_reconnect_removed", session_name(session)); return 0; } if (!g->sess) { /* don't print message when 'gg:reconnect' */ if (!isreconnect) printq("not_connected", session_name(session)); } else { char *myreason; char *tmp; if (session->autoaway) session_status_set(session, EKG_STATUS_AUTOBACK); myreason = session_descr_sync(session, params[0]); tmp = locale_to_gg_dup(session, myreason); if (tmp) gg_change_status_descr(g->sess, GG_STATUS_NOT_AVAIL_DESCR, tmp); else gg_change_status(g->sess, GG_STATUS_NOT_AVAIL); xfree(tmp); watch_remove(&gg_plugin, g->sess->fd, g->sess->check); gg_logoff(g->sess); gg_free_session(g->sess); g->sess = NULL; protocol_disconnected_emit(session, myreason, EKG_DISCONNECT_USER); xfree(myreason); } } if (!xstrcmp(name, ("connect")) || isreconnect) { struct gg_login_params p; const char *tmp, *local_ip = session_get(session, "local_ip"); int tmpi; int _status; const char *realserver = session_get(session, "server"); int port = session_int_get(session, "port"); char *password = (char *) session_get(session, "password"); if (g->sess) { printq((g->sess->state == GG_STATE_CONNECTED) ? "already_connected" : "during_connect", session_name(session)); return -1; } if (command_exec(NULL, session, "/session --lock", 0) == -1) return -1; if (local_ip == NULL) gg_local_ip = g_htonl(INADDR_ANY); else { #ifdef HAVE_INET_PTON int tmp = inet_pton(AF_INET, local_ip, &gg_local_ip); if (tmp == 0 || tmp == -1) { print("invalid_local_ip", session_name(session)); session_set(session, "local_ip", NULL); config_changed = 1; gg_local_ip = g_htonl(INADDR_ANY); } #else gg_local_ip = inet_addr(local_ip); #endif } if (!uin || !password) { printq("no_config"); return -1; } printq("connecting", session_name(session)); memset(&p, 0, sizeof(p)); if ((session_status_get(session) == EKG_STATUS_NA)) session_status_set(session, EKG_STATUS_AVAIL); { char *current_descr = session_descr_sync(session, params[0]); _status = gg_text_to_status(session_status_get(session), current_descr); xfree(current_descr); } /* dcc */ if (gg_config_dcc) { gg_dcc_socket_close(); if (!gg_config_dcc_ip || !xstrcasecmp(gg_config_dcc_ip, "auto")) { gg_dcc_ip = inet_addr("255.255.255.255"); } else { if (inet_addr(gg_config_dcc_ip) != INADDR_NONE) gg_dcc_ip = inet_addr(gg_config_dcc_ip); else { print("dcc_invalid_ip"); gg_config_dcc_ip = NULL; gg_dcc_ip = 0; } } if (gg_config_audio) p.has_audio = 1; gg_dcc_port = gg_config_dcc_port; gg_dcc_socket_open(gg_config_dcc_port); } p.uin = uin; p.password = (char*) password; p.image_size = gg_config_image_size; _status = GG_S(_status); if (session_int_get(session, "private")) _status |= GG_STATUS_FRIENDS_MASK; if ((tmpi = session_int_get(session, "protocol")) > 0) g->curr_prtcl_ver = p.protocol_version = tmpi; else g->curr_prtcl_ver = GG_DEFAULT_PROTOCOL_VERSION; if (g->curr_prtcl_ver >= 0x2d) { #ifdef GG_FEATURE_MSG80 p.encoding = GG_ENCODING_UTF8; #endif #ifdef GG_FEATURE_DND_FFC p.protocol_features = GG_FEATURE_STATUS80 | GG_FEATURE_DND_FFC; #endif #ifdef GG_FEATURE_TYPING_NOTIFICATION p.protocol_features |= GG_FEATURE_TYPING_NOTIFICATION; #endif } while (realserver) { in_addr_t tmp_in; #ifdef __GG_LIBGADU_HAVE_OPENSSL if (!xstrcasecmp(realserver, "tls")) { p.tls = 1; break; } #endif if (!xstrncasecmp(realserver, "tls:", 4)) { #ifdef __GG_LIBGADU_HAVE_OPENSSL p.tls = 1; #endif realserver += 4; } { char *myserver, *comma; if ((comma = xstrchr(realserver, ','))) myserver = xstrndup(realserver, comma-realserver); else /* IMO duplicating the string will be more readable then using (myserver ? myserver : realserver */ myserver = xstrdup(realserver); if ((tmp_in = inet_addr(myserver)) != INADDR_NONE) p.server_addr = inet_addr(myserver); else { print("inet_addr_failed", session_name(session)); xfree(myserver); return -1; } xfree(myserver); } break; } if ((port < 1) || (port > 65535)) { print("port_number_error", session_name(session)); return -1; } p.server_port = port; xfree(gg_proxy_host); xfree(gg_proxy_username); xfree(gg_proxy_password); gg_proxy_host = NULL; gg_proxy_username = NULL; gg_proxy_password = NULL; gg_proxy_port = 0; gg_proxy_enabled = 0; if ((tmp = session_get(session, "proxy"))) { char **auth, **userpass = NULL, **hostport = NULL; auth = array_make(tmp, "@", 0, 0, 0); if (!auth[0] || !xstrcmp(auth[0], "")) { g_strfreev(auth); goto noproxy; } gg_proxy_enabled = 1; if (auth[0] && auth[1]) { userpass = array_make(auth[0], ":", 0, 0, 0); hostport = array_make(auth[1], ":", 0, 0, 0); } else hostport = array_make(auth[0], ":", 0, 0, 0); if (userpass && userpass[0] && userpass[1]) { gg_proxy_username = xstrdup(userpass[0]); gg_proxy_password = xstrdup(userpass[1]); } gg_proxy_host = xstrdup(hostport[0]); gg_proxy_port = (hostport[1]) ? atoi(hostport[1]) : 8080; g_strfreev(hostport); g_strfreev(userpass); g_strfreev(auth); } noproxy: if ((tmp = session_get(session, "proxy_forwarding"))) { char *fwd = xstrdup(tmp), *colon = xstrchr(fwd, ':'); if (!colon) { p.external_addr = inet_addr(fwd); p.external_port = 1550; /* XXX */ } else { *colon = 0; p.external_addr = inet_addr(fwd); p.external_port = atoi(colon + 1); } xfree(fwd); } /* moved this further, because of locale_to_gg() allocation */ p.status = _status; p.status_descr = locale_to_gg_dup(session, session_descr_get(session)); p.async = 1; g->sess = gg_login(&p); xfree(p.status_descr); if (!g->sess) printq("conn_failed", format_find((errno == ENOMEM) ? "conn_failed_memory" : "conn_failed_connecting"), session_name(session)); else { session->connecting = 1; watch_t *w = watch_add_session(session, g->sess->fd, g->sess->check, gg_session_handler); watch_timeout_set(w, g->sess->timeout); } } return 0; } static COMMAND(gg_command_away) { gg_private_t *g = session_private_get(session); char *descr; char *cpdescr, *f = NULL, *fd = NULL, *params0 = xstrdup(params[0]); int df = 0; /* do we really need this? */ int status; int timeout = session_int_get(session, "scroll_long_desc"); int autoscroll = 0; int _status; if (xstrlen(params0)) g->scroll_pos = 0; if (!xstrcmp(name, ("_autoscroll"))) { autoscroll = 1; status = session_status_get(session); if ((status == EKG_STATUS_AWAY) /*|| (status == EKG_STATUS_AUTOAWAY) */) { fd = "away_descr"; } else if (status == EKG_STATUS_AVAIL) { fd = "back_descr"; } else if (status == EKG_STATUS_INVISIBLE) { fd = "invisible_descr"; } xfree(params0); params0 = xstrdup(session_descr_get(session)); g->scroll_last = time(NULL); if (!xstrlen(params0)) { xfree(params0); return -1; } /* debug("%s [%s] %d\n", session_name(session), fd, session->scroll_pos); */ if (xstrlen(params0) < GG_STATUS_DESCR_MAXSIZE) { xfree(params0); return -1; } } else if (!xstrcmp(name, ("away"))) { session_status_set(session, EKG_STATUS_AWAY); df = EKG_STATUS_AWAY; f = "away"; fd = "away_descr"; session_unidle(session); } else if (!xstrcmp(name, ("_autoaway"))) { session_status_set(session, EKG_STATUS_AUTOAWAY); df = EKG_STATUS_AWAY; f = "auto_away"; fd = "auto_away_descr"; } else if (!xstrcmp(name, ("back"))) { session_status_set(session, EKG_STATUS_AVAIL); df = EKG_STATUS_AVAIL; f = "back"; fd = "back_descr"; session_unidle(session); } else if (!xstrcmp(name, ("_autoback"))) { session_status_set(session, EKG_STATUS_AUTOBACK); df = EKG_STATUS_AVAIL; f = "auto_back"; fd = "auto_back_descr"; session_unidle(session); } else if (!xstrcmp(name, ("invisible"))) { session_status_set(session, EKG_STATUS_INVISIBLE); df = EKG_STATUS_NA; f = "invisible"; fd = "invisible_descr"; session_unidle(session); #ifdef GG_FEATURE_DND_FFC } else if (!xstrcmp(name, ("dnd"))) { session_status_set(session, EKG_STATUS_DND); df = EKG_STATUS_NA; f = "dnd"; fd = "dnd_descr"; session_unidle(session); } else if (!xstrcmp(name, ("ffc"))) { session_status_set(session, EKG_STATUS_FFC); df = EKG_STATUS_NA; f = "ffc"; fd = "ffc_descr"; session_unidle(session); #endif } else { xfree(params0); return -1; } if (params0) { char *tmp = locale_to_gg_dup(session, params0); if (xstrlen(tmp) > GG_STATUS_DESCR_MAXSIZE) { if (!timeout) { char *descr_poss = utf8ndup(params0, GG_STATUS_DESCR_MAXSIZE); char *descr_not_poss = xstrdup(params0 + xstrlen(descr_poss)); printq("descr_too_long", ekg_itoa(xstrlen(descr_not_poss)), descr_poss, descr_not_poss); /* XXX add new function utf8len() */ g->scroll_op = 0; xfree(tmp); xfree(descr_poss); xfree(descr_not_poss); xfree(params0); return -1; } } xfree(tmp); session_descr_set(session, (!xstrcmp(params0, "-")) ? NULL : params0); } else { char *tmp; if (!config_keep_reason) { session_descr_set(session, NULL); } else if ((tmp = ekg_draw_descr(df))) { session_descr_set(session, tmp); xfree(tmp); } } ekg2_reason_changed = 1; if (!session_descr_get(session)) autoscroll = timeout = 0; if (autoscroll || (timeout && xstrlen(params0) > GG_STATUS_DESCR_MAXSIZE)) { const char *mode = session_get(session, "scroll_mode"); char *desk; timeout = autoscroll; autoscroll = g->scroll_pos; desk = xstrndup(session_descr_get(session) + autoscroll, GG_STATUS_DESCR_MAXSIZE-1); /* this is made especially to make other people happy ;) * and make it easy to ignore states with '>' at beginning */ if (autoscroll) descr = saprintf(("<%s"), desk); else descr = saprintf(("%s>"), desk); xfree(desk); if (!xstrcmp(mode, "bounce")) { if (!g->scroll_op) { g->scroll_pos++; } else { g->scroll_pos--; } /* I've changed xor to simple setting to 0 and 1 because * it was possible to screw things up by playing with * scroll_mode session variable */ if (g->scroll_pos <= 0) g->scroll_op=0; else if (g->scroll_pos >= xstrlen(session_descr_get(session)) - GG_STATUS_DESCR_MAXSIZE+1) g->scroll_op=1; } else if (!xstrcmp(mode, "simple")) { g->scroll_pos++; if (g->scroll_pos > xstrlen(session_descr_get(session)) - GG_STATUS_DESCR_MAXSIZE+1) g->scroll_pos=0; } /* I wanted to add one more 'constant' to the left [or right] * but I'd have to change some things, and I'm soooo lazy */ autoscroll = timeout; } else { descr = xstrdup(session_descr_get(session)); } debug("%s - %s\n", name, descr); status = session_status_get(session); if (!autoscroll) { if (descr) printq(fd, descr, (""), session_name(session)); else printq(f, session_name(session)); } if (!g->sess || !session_connected_get(session)) { xfree(descr); xfree(params0); return 0; } ekg_update_status(session); cpdescr = locale_to_gg(session, descr); _status = GG_S(gg_text_to_status(status, cpdescr)); /* descr can be NULL it doesn't matter... */ if (session_int_get(session, "private")) _status |= GG_STATUS_FRIENDS_MASK; if (descr) gg_change_status_descr(g->sess, _status, cpdescr); else gg_change_status(g->sess, _status); xfree(params0); xfree(cpdescr); return 0; } static COMMAND(gg_command_msg) { int count, valid = 0, secure = 0, formatlen = 0; char **nicks = NULL, *nick = NULL, **p = NULL, *add_send = NULL; unsigned char *msg = NULL; char *raw_msg = NULL; unsigned char *format = NULL; char *cpmsg = NULL; const char *seq = NULL; guint32 *ekg_format = NULL; userlist_t *u; gg_private_t *g = session_private_get(session); const int chat = (xstrcmp(name, ("msg"))); const int class = (chat) ? EKG_MSGCLASS_SENT_CHAT : EKG_MSGCLASS_SENT; if (!quiet) session_unidle(session); if (!xstrcmp(params[0], ("*"))) { if (msg_all(session, name, params[1]) == -1) printq("list_empty"); return 0; } nick = xstrdup(target); if ((*nick == '@' || xstrchr(nick, ',')) && chat) { struct conference *c = conference_create(session, nick); list_t l; if (c) { xfree(nick); nick = xstrdup(c->name); for (l = c->recipients; l; l = l->next) array_add(&nicks, xstrdup((char *) (l->data))); add_send = xstrdup(c->name); } } else if (*nick == '#') { struct conference *c = conference_find(nick); list_t l; if (!c) { printq("conferences_noexist", nick); xfree(nick); return -1; } for (l = c->recipients; l; l = l->next) array_add(&nicks, xstrdup((char *) (l->data))); add_send = xstrdup(c->name); } else { char **tmp = array_make(nick, ",", 0, 0, 0); int i; for (i = 0; tmp[i]; i++) { int count = 0; userlist_t *ul; if (tmp[i][0] != '@') { if (!array_contains(nicks, tmp[i], 0)) array_add(&nicks, xstrdup(tmp[i])); continue; } for (ul = session->userlist; ul; ul = ul->next) { userlist_t *u = ul; struct ekg_group *gl; for (gl = u->groups; gl; gl = gl->next) { struct ekg_group *g = gl; if (!xstrcasecmp(g->name, tmp[i] + 1)) { if (u->nickname && !array_contains(nicks, u->nickname, 0)) array_add(&nicks, xstrdup(u->nickname)); count++; } } } if (!count) printq("group_empty", tmp[i] + 1); } g_strfreev(tmp); } if (!nicks) { xfree(nick); return 0; } if (gg_config_split_messages && xstrlen(params[1]) > GG_MSG_MAXSIZE) { int i, len = xstrlen(params[1]); for (i = 1; i * GG_MSG_MAXSIZE <= len; i++) { char *tmp = (i != len) ? xstrndup(params[1] + (i - 1) * GG_MSG_MAXSIZE, GG_MSG_MAXSIZE) : xstrdup(params[1] + (i - 1) * GG_MSG_MAXSIZE); command_exec_format(target, session, 0, ("/%s %s %s"), name, target, tmp); xfree(tmp); } return 0; } else if (xstrlen(params[1]) > GG_MSG_MAXSIZE) { printq("message_too_long"); } msg = (unsigned char *) xstrmid(params[1], 0, GG_MSG_MAXSIZE); ekg_format = ekg_sent_message_format((char *) msg); /* analize tekstu zrobimy w osobnym bloku dla porzdku */ { unsigned char attr = 0, last_attr = 0; const unsigned char *p = msg, *end = p + xstrlen((char *) p); int msglen = 0; unsigned char rgb[3], last_rgb[3]; for (p = msg; p < end; ) { if (*p == 18 || *p == 3) { /* Ctrl-R, Ctrl-C */ p++; if (xisdigit(*p)) { int num = atoi((char *) p); if (num < 0 || num > 15) num = 0; p++; if (xisdigit(*p)) p++; rgb[0] = color_map_default[num].r; rgb[1] = color_map_default[num].g; rgb[2] = color_map_default[num].b; attr |= GG_FONT_COLOR; } else attr &= ~GG_FONT_COLOR; continue; } if (*p == 2) { /* Ctrl-B */ attr ^= GG_FONT_BOLD; p++; continue; } if (*p == 20) { /* Ctrl-T */ attr ^= GG_FONT_ITALIC; p++; continue; } if (*p == 31) { /* Ctrl-_ */ attr ^= GG_FONT_UNDERLINE; p++; continue; } if (*p < 32 && *p != 13 && *p != 10 && *p != 9) { p++; continue; } if (attr != last_attr || ((attr & GG_FONT_COLOR) && memcmp(last_rgb, rgb, sizeof(rgb)))) { int color = 0; memcpy(last_rgb, rgb, sizeof(rgb)); if (!format) { format = xmalloc(3); format[0] = 2; formatlen = 3; } if ((attr & GG_FONT_COLOR)) color = 1; if ((last_attr & GG_FONT_COLOR) && !(attr & GG_FONT_COLOR)) { color = 1; memset(rgb, 0, 3); } format = xrealloc(format, formatlen + ((color) ? 6 : 3)); format[formatlen] = (msglen & 255); format[formatlen + 1] = ((msglen >> 8) & 255); format[formatlen + 2] = attr | ((color) ? GG_FONT_COLOR : 0); if (color) { memcpy(format + formatlen + 3, rgb, 3); formatlen += 6; } else formatlen += 3; last_attr = attr; } msg[msglen++] = *p; p++; } msg[msglen] = 0; if (format && formatlen) { format[1] = (formatlen - 3) & 255; format[2] = ((formatlen - 3) >> 8) & 255; } } raw_msg = xstrdup((char *) msg); cpmsg = locale_to_gg(session, (char *) msg); count = g_strv_length(nicks); for (p = nicks; *p; p++) { const char *uid; if (!xstrcmp(*p, "")) continue; if (!(uid = get_uid(session, *p))) { printq("user_not_found", *p); continue; } u = userlist_find(session, uid); if (config_last & 4) last_add(1, uid, time(NULL), 0, raw_msg); if (!chat || count == 1) { char *__msg = xstrdup(cpmsg); char *sid = xstrdup(session->uid); char *uid_tmp = xstrdup(uid); uin_t uin = atoi(uid + 3); secure = 0; query_emit(NULL, "message-encrypt", &sid, &uid_tmp, &__msg, &secure); xfree(sid); xfree(uid_tmp); if (g->sess) seq = ekg_itoa(gg_send_message_richtext(g->sess, (chat) ? GG_CLASS_CHAT : GG_CLASS_MSG, uin, (unsigned char *) __msg, (unsigned char *) format, formatlen)); else seq = "offline"; msg_queue_add(session_uid_get(session), target, params[1], seq, class); valid++; xfree(__msg); } } if (count > 1 && chat) { uin_t *uins = xmalloc(count * sizeof(uin_t)); int realcount = 0; for (p = nicks; *p; p++) { const char *uid; if (!(uid = get_uid(session, *p))) continue; uins[realcount++] = atoi(uid + 3); } if (g->sess) seq = ekg_itoa(gg_send_message_confer_richtext(g->sess, GG_CLASS_CHAT, realcount, uins, (unsigned char *) cpmsg, format, formatlen)); else seq = "offline"; msg_queue_add(session_uid_get(session), target, params[1], seq, class); valid++; xfree(uins); } if (!add_send) add_send = xstrdup(nick); if (valid) tabnick_add(add_send); xfree(add_send); if (valid && (!g->sess || g->sess->state != GG_STATE_CONNECTED)) printq("not_connected_msg_queued", session_name(session)); if (valid && !quiet) { char **rcpts = xmalloc(sizeof(char *) * 2); rcpts[0] = xstrdup(nick); rcpts[1] = NULL; protocol_message_emit(session, session->uid, rcpts, raw_msg, ekg_format, time(NULL), class, seq, EKG_TRY_BEEP, secure); xfree(rcpts[0]); xfree(rcpts); } xfree(cpmsg); xfree(raw_msg); xfree(format); xfree(nick); xfree(ekg_format); g_strfreev(nicks); return 0; } static COMMAND(gg_command_inline_msg) { const char *p[2] = { NULL, params[0] }; if (!target || !params[0]) return -1; return gg_command_msg(("chat"), p, session, target, quiet); } /** * gg_command_block() * * Block @a uid or printq() list of blocked uids.
* Handler for: /gg:block command * * @todo Think about config_changed ... maybe let's create userlist_changed for this or smth? * * @param params [0] (uid) - uid to block, or NULL if you want to display list of blocked uids. * * @sa gg_blocked_add() - for block function. * @sa gg_command_unblock() - for unblock command * * @return 0 - if @a uid == NULL, or @a uid was successfully blocked.
* -1 - if @a uid was neither valid gg uid, nor user nickname
* -2 - if @a uid is already blocked. */ static COMMAND(gg_command_block) { const char *uid; if (!params[0]) { userlist_t *ul; int i = 0; for (ul = session->userlist; ul; ul = ul->next) { userlist_t *u = ul; if (!ekg_group_member(u, "__blocked")) continue; i = 1; printq("blocked_list", format_user(session, u->uid)); } if (!i) printq("blocked_list_empty"); return 0; } if (!(uid = get_uid(session, params[0]))) { printq("user_not_found", params[0]); return -1; } if (gg_blocked_add(session, uid) == -1) { printq("blocked_exist", format_user(session, uid)); return -2; } printq("blocked_added", format_user(session, uid)); config_changed = 1; return 0; } /** * gg_command_unblock() * * Unblock @a uid. Or everybody if @a uid '*'
* Handler for: /gg:unlock command. * * @todo Think about config_changed ... maybe let's create userlist_changed for this or smth? * * @param params [0] (uid) - @a uid to unblock, or '*' to unblock everyone. * * @sa gg_blocked_remove() - for unblock function. * @sa gg_command_block() - for block command * * @return 0 - if somebody was unblocked.
* -1 - if smth went wrong. */ static COMMAND(gg_command_unblock) { const char *uid; int ret; if (!xstrcmp(params[0], "*")) { userlist_t *ul; int x = 0; for (ul = session->userlist; ul; ) { userlist_t *u = ul; userlist_t *next = ul->next; if (gg_blocked_remove(session, u->uid) != -1) x = 1; ul = next; } if (!x) { printq("blocked_list_empty"); return -1; } printq("blocked_deleted_all"); config_changed = 1; return 0; } if (!(uid = get_uid(session, params[0]))) { printq("user_not_found", params[0]); return -1; } { /* TODO: Explain why we need a copy. */ char *uid_copy = xstrdup(uid); if ( ( ret = gg_blocked_remove(session, uid_copy) ) == -1) printq("error_not_blocked", format_user(session, uid_copy)); else { printq("blocked_deleted", format_user(session, uid_copy)); config_changed = 1; } xfree(uid_copy); } return ret; } #ifdef GIF_OCR /* * token_gif_load() * * Reads token from a gif file. Returns -1 if error occurs (in token->data then * we will have error message) or 0. If token->pal_sz != 0 it means, that * token has some colours pallet, in which we have to check pixels (r.g.b) * order. Size of pallet in bites pal_sz * 3 * * - fname - name of the gif file to load * - token - pointer to structure token */ static int token_gif_load (char *fname, struct token_t *token) { char errbuf[512]; GifFileType *file; int fd; fd = open(fname, O_RDONLY); if (fd == -1) { snprintf(errbuf, sizeof(errbuf), "open(%s): %m", fname); goto err; } if (!(file = DGifOpenFileHandle(fd))) { snprintf(errbuf, sizeof(errbuf), "DGifOpenFileHandle(): %d", GifLastError()); goto err2; } if (file->SWidth <= 0 || file->SWidth > 1024 || file->SHeight <= 0 || file->SHeight > 1024) { snprintf(errbuf, sizeof(errbuf), "Invalid image size: %d,%d", file->SWidth, file->SHeight); goto err3; } if (DGifSlurp(file) != GIF_OK) { snprintf(errbuf, sizeof(errbuf), "DGifSlurp(): %d", GifLastError()); goto err3; } if (file->ImageCount != 1) { snprintf(errbuf, sizeof(errbuf), "ImageCount = %d", file->ImageCount); goto err3; } token->sx = file->SavedImages[0].ImageDesc.Width; token->sy = file->SavedImages[0].ImageDesc.Height; token->data = (unsigned char *) xmalloc(token->sx * token->sy); memcpy(token->data, file->SavedImages[0].RasterBits, token->sx * token->sy); DGifCloseFile(file); return 0; err3: DGifCloseFile(file); err2: close(fd); err: token->data = (unsigned char *) xstrdup(errbuf); return -1; } /* * token_gif_get_pixel() * * Gets pixel from given position. If the position is out of range it returns * given coulour of background. * * - token - pointer to structure, describing token * - x, y - pixel position * - backgr_color - number of background colour */ static char token_gif_get_pixel (struct token_t *token, size_t x, size_t y, unsigned char backgr_color) { return (x < 0 || y < 0 || x >= token->sx || y >= token->sy) ? backgr_color : token->data[y * token->sx + x]; } /* * token_gif_strip() * * It removes from the image everything that is not needed (lines, single * pixel and anyaliasing of the font). * * - token - pointer to structure, debribing token */ static void token_gif_strip (struct token_t *token) { unsigned char *new_data; size_t i; size_t x, y; unsigned char backgr_color = 0; size_t backgr_counts[256]; /* Usuwamy wszystkie samotne piksele. Piksel jest uznawany za samotny * wtedy, kiedy nie ma w jego najbliszym otoczeniu, obejmujcym 8 * pikseli dookola niego, przynajmniej trzech pikseli o tym samym * kolorze. To usuwa kropki i pojedyncze linie dodawane w celu * zaciemnienia obrazu tokena oraz anty-aliasing czcionek w znakach. * Otoczenie pikseli brzegowych jest uznawane za kolor ta tak, jakby * to zostao rozszerzone. */ /* Najpierw sprawdzamy kolor ta. To piksel, ktoego jest najwiecej. */ for (i = 0; i < 256; i++) backgr_counts[i] = 0; for (i = 0; i < token->sx * token->sy; i++) { unsigned char pixel = token->data[i]; if (++backgr_counts[pixel] > backgr_counts[backgr_color]) backgr_color = pixel; } new_data = (unsigned char *) xmalloc(token->sx * token->sy); for (y = 0; y < token->sy; y++) for (x = 0; x < token->sx; x++) { int dx, dy; char new_pixel = backgr_color; if (token->data[y * token->sx + x] != backgr_color) { int num_pixels = 0; /* num_pixels przechowuje liczbe pikseli w otoczeniu * badanego piksela (wliczajc sam badany piksel) * o tym samym kolorze, co badany piksel. */ for (dy = -1; dy <= 1; dy++) for (dx = -1; dx <= 1; dx++) if (token_gif_get_pixel(token, x + dx, y + dy, backgr_color) == token->data[y * token->sx + x]) num_pixels++; if (num_pixels >= 4) /* 4, bo razem z badanym */ new_pixel = token->data[y * token->sx + x]; } new_data[y * token->sx + x] = new_pixel; // ? 1 : 0; } xfree(token->data); token->data = new_data; } /* * token_gif_strip_txt * * It removes from given text buffer empty lines up and down. * Return newly allocated buffer. * * - buf - buffer to be stripped */ static char *token_gif_strip_txt (char *buf) { char *new_buf = NULL; size_t start, end, len; len = strlen(buf); for (start = 0; start < len; start++) if (buf[start] != 0x20 && buf[start] != '\n') break; if (!buf[start]) return NULL; while (start && buf[start] != '\n') start--; if (start) start++; for (end = 0; end < len; end++) if (buf[len - 1 - end] != 0x20 && buf[len - 1 - end] != '\n') break; end = len - 1 - end; end--; if (end < start) return NULL; new_buf = (char *) xmalloc(end - start + 2); memcpy(new_buf, buf + start, end - start); new_buf[end - start - 1] = '\n'; new_buf[end - start] = 0; return new_buf; } /* * token_gif_to_txt() * * Converts token to text. Returns text buffer with token stripped in that * way to match the screen. * * - token - pointer to token structure */ static char *token_gif_to_txt (struct token_t *token) { char *buf, *bptr; size_t x, y; static const char chars[] = " !@#$&*:;-=+?"; char mappings[256]; int cur_char = 0; /* Kolejny znaczek z chars[]. */ memset(mappings, 0, sizeof(mappings)); buf = bptr = (char *) xmalloc((token->sx * (token->sy + 1))+1); for (x = 0; x < token->sx; x++) { for (y = 0; y < token->sy; y++) { unsigned char reg; reg = token->data[y * token->sx + (token->sx - 1 - x)]; /* Mamy ju mapowanie dla tego koloru? */ if (reg && !mappings[reg]) { mappings[reg] = ++cur_char; /* Podzielenie przez drugi sizeof nie jest * potrzebne, ale gdyby kto kiedy chcia * wpa�na pomys zmiany typu draw_chars, * to dla bezpiecze�twa lepiej da� */ cur_char %= sizeof(chars) / sizeof(*chars) - 1; } *bptr++ = reg ? chars[(size_t) mappings[(size_t) reg]] : 0x20; } *bptr++ = '\n'; } *bptr = 0; bptr = token_gif_strip_txt(buf); if (bptr) { xfree(buf); return bptr; } return buf; } #endif #ifdef HAVE_LIBJPEG /* * token_check() * * function checks if in the given place exists proposed character * * - n - number from 0 to 15 (char from 0 to f) * - x, y - coordinates in ocr table */ static int token_check(int nr, int x, int y, const char *ocr, int maxx, int maxy) { int i; for (i = nr * token_char_height; i < (nr + 1) * token_char_height; i++, y++) { int j, xx = x; for (j = 0; token_id[i][j] && j + xx < maxx; j++, xx++) { if (token_id[i][j] != ocr[y * (maxx + 1) + xx]) return 0; } } debug("token_check(nr=%d,x=%d,y=%d,ocr=%p,maxx=%d,maxy=%d\n", nr, x, y, ocr, maxx, maxy); return 1; } /* * token_ocr() * * returns text of the token */ static char *token_ocr(const char *ocr, int width, int height, int length) { int x, y, count = 0; char *token; token = xmalloc(length + 1); memset(token, 0, length + 1); for (x = 0; x < width; x++) { for (y = 0; y < height - token_char_height; y++) { int result = 0, token_part = 0; do result = token_check(token_part++, x, y, ocr, width, height); while (!result && token_part < 16); if (result && count < length) token[count++] = token_id_char[token_part - 1]; } } if (count == length) return token; xfree(token); return NULL; } struct ekg_jpeg_error_mgr { struct jpeg_error_mgr pub; jmp_buf setjmp_buffer; }; static void ekg_jpeg_error_exit(j_common_ptr j) { struct ekg_jpeg_error_mgr *e = (struct ekg_jpeg_error_mgr *) j->err; /* Return control to the setjmp point */ longjmp(e->setjmp_buffer, 1); } #endif static WATCHER(gg_handle_token) { struct gg_http *h = data; struct gg_token *t = NULL; char *file = NULL; if (!h) return -1; if (type == 2) { debug("[gg] gg_handle_token() timeout\n"); print("register_timeout"); goto fail; } if (type != 0) return 0; if (gg_token_watch_fd(h) || h->state == GG_STATE_ERROR) { print("gg_token_failed", gg_http_error_string(h->error)); goto fail; } if (h->state != GG_STATE_DONE) { watch_t *w; if (fd == h->fd && watch == h->check) return 0; /* if this is the same watch... we leave it */ /* otherwise we delete old one (return -1) and create new one .... * XXX, should we copy data from gg_http *h ? and free them in type == 1 ? */ w = watch_add(&gg_plugin, h->fd, h->check, gg_handle_token, h); watch_timeout_set(w, h->timeout); return -1; } if (!(t = h->data) || !h->body) { print("gg_token_failed", gg_http_error_string(h->error)); goto fail; } xfree(last_tokenid); last_tokenid = xstrdup(t->tokenid); #ifdef HAVE_MKSTEMP file = saprintf("%s/token.XXXXXX", getenv("TMPDIR") ? getenv("TMPDIR") : "/tmp"); if ((fd = mkstemp(file)) == -1) { print("gg_token_failed", strerror(errno)); goto fail; } if ((write(fd, h->body, h->body_size) != h->body_size) || (close(fd) != 0)) { print("gg_token_failed", strerror(errno)); close(fd); unlink(file); goto fail; } if (query_emit(NULL, "gg-display-token", &file) == -1) goto fail; #ifdef GIF_OCR if (gg_config_display_token) { struct token_t token; char *buf; if (token_gif_load(file, &token) == -1) { print("gg_token_failed_saved", token.data, file); xfree (token.data); goto fail; } token_gif_strip (&token); buf = token_gif_to_txt(&token); print("gg_token_start"); print("gg_token_body", buf); print("gg_token_end"); xfree(buf); xfree(token.data); goto fail; } #endif #ifdef HAVE_LIBJPEG if (gg_config_display_token) { struct jpeg_decompress_struct j; struct ekg_jpeg_error_mgr e; JSAMPROW buf[1]; int size; char *token, *tmp; FILE *f; int h = 0; if (!(f = fopen(file, "rb"))) { print("gg_token_failed_saved", strerror(errno), file); goto fail; } j.err = jpeg_std_error(&e.pub); e.pub.error_exit = ekg_jpeg_error_exit; /* Establish the setjmp return context for ekg_jpeg_error_exit to use. */ if (setjmp(e.setjmp_buffer)) { char buf[JMSG_LENGTH_MAX]; /* If we ended up over here, then it means some call below called longjmp. */ (e.pub.format_message)((j_common_ptr)&j, buf); print("gg_token_failed_saved", buf, file); jpeg_destroy_decompress(&j); fclose(f); goto fail; } jpeg_create_decompress(&j); jpeg_stdio_src(&j, f); jpeg_read_header(&j, TRUE); jpeg_start_decompress(&j); size = j.output_width * j.output_components; buf[0] = xmalloc(size); token = xmalloc((j.output_width + 1) * j.output_height); while (j.output_scanline < j.output_height) { int i; jpeg_read_scanlines(&j, buf, 1); for (i = 0; i < j.output_width; i++, h++) token[h] = (buf[0][i*3] + buf[0][i*3+1] + buf[0][i*3+2] < 384) ? '#' : '.'; token[h++] = 0; } if (!(tmp = token_ocr(token, j.output_width, j.output_height, t->length))) { int i; for (i = 0; i < j.output_height; i++) print("gg_token_body", &token[i * (j.output_width + 1)]); } else { print("gg_token_ocr", tmp); xfree(tmp); } xfree(token); jpeg_finish_decompress(&j); jpeg_destroy_decompress(&j); xfree(buf[0]); fclose(f); unlink(file); } else #endif /* HAVE_LIBJPEG */ { char *file2 = saprintf("%s.gif", file); if (rename(file, file2) == -1) print("gg_token", file); else print("gg_token", file2); xfree(file2); } /* here success... let's create some struct with token and use if they needed? XXX */ #else /* HAVE_MKSTEMP */ print("gg_token_unsupported"); #endif /* HAVE_MKSTEMP */ #ifdef HAVE_MKSTEMP unlink(file); #endif fail: xfree(file); /* if we free token... we must search for it in all watches, and point data to NULL */ /* XXX, hack... let's copy token data to all watch ? */ list_t l; for (l = watches; l; l = l->next) { watch_t *w = l->data; if (w && w->data == h) { w->data = NULL; /* maybe we call remove here ? */ } } gg_token_free(h); return -1; /* watch_remove(&gg_plugin, h->fd, h->check); */ } static COMMAND(gg_command_token) { struct gg_http *h; watch_t *w; if (!(h = gg_token(1))) { printq("gg_token_failed", strerror(errno)); return -1; } w = watch_add(&gg_plugin, h->fd, h->check, gg_handle_token, h); watch_timeout_set(w, h->timeout); return 0; } static COMMAND(gg_command_modify) { userlist_t *u, *u1; gg_userlist_private_t *up; const char **par; char **argv = NULL; int i, res = 0, modified = 0; if (!xstrcmp(name, ("add"))) { int ret; /* we overlap /add command, so we need to execute it... maybe let's move it here? */ ret = cmd_add(name, params, session, target, quiet); /* if adding fails, quit */ if (ret != 0 || !params[1]) return ret; /* params[1] cause of: in commands.c, * query_emit(NULL, ("userlist-added"), &uid, ¶ms[1], &quiet); * and we emulate old behavior (via query handler executing command) with command handler... rewrite ? */ par = &(params[1]); } else par = params; if (!(u = userlist_find(session, par[0]))) { printq("user_not_found", par[0]); return -1; } up = gg_userlist_priv_get(u); if (par[1]) argv = array_make(par[1], " \t", 0, 1, 1); for (i = 0; argv && argv[i]; i++) { if (match_arg(argv[i], 'f', ("first"), 2) && argv[i + 1]) { user_private_item_set(u, "first_name", xstrdup(argv[++i])); modified = 1; continue; } if (match_arg(argv[i], 'l', ("last"), 2) && argv[i + 1]) { user_private_item_set(u, "last_name", xstrdup(argv[++i])); modified = 1; continue; } if (match_arg(argv[i], 'n', ("nickname"), 2) && argv[i + 1]) { char *tmp1, *tmp2; u1 = userlist_find(session, argv[i + 1]); if ( u1 && (u != u1) ) { printq("user_exists", argv[i + 1], session_name(session)); continue; } tmp1 = xstrdup(u->nickname); tmp2 = xstrdup(argv[++i]); query_emit(NULL, "userlist-renamed", &tmp1, &tmp2); xfree(tmp1); xfree(u->nickname); u->nickname = tmp2; userlist_replace(session, u); query_emit(NULL, "userlist-refresh"); modified = 1; continue; } if ((match_arg(argv[i], 'p', ("phone"), 2) || match_arg(argv[i], 'm', ("mobile"), 2)) && argv[i + 1]) { user_private_item_set(u, "mobile", argv[++i]); modified = 1; continue; } if (match_arg(argv[i], 'g', ("group"), 2) && argv[i + 1]) { char **tmp = array_make(argv[++i], ",", 0, 1, 1); int x, off; /* jeli zaczyna si�od '@', pomijamy pierwszy znak */ int chg = 0; for (x = 0; tmp[x]; x++) switch (*tmp[x]) { case '-': off = (tmp[x][1] == '@' && xstrlen(tmp[x]) > 1) ? 1 : 0; if (ekg_group_member(u, tmp[x] + 1 + off)) { ekg_group_remove(u, tmp[x] + 1 + off); chg = modified = 1; } else { printq("group_member_not_yet", format_user(session, u->uid), tmp[x] + 1); if (!modified) modified = -1; } break; case '+': off = (tmp[x][1] == '@' && xstrlen(tmp[x]) > 1) ? 1 : 0; if (!ekg_group_member(u, tmp[x] + 1 + off)) { ekg_group_add(u, tmp[x] + 1 + off); chg = modified = 1; } else { printq("group_member_already", format_user(session, u->uid), tmp[x] + 1); if (!modified) modified = -1; } break; default: off = (tmp[x][0] == '@' && xstrlen(tmp[x]) > 1) ? 1 : 0; if (!ekg_group_member(u, tmp[x] + off)) { ekg_group_add(u, tmp[x] + off); chg = modified = 1; } else { printq("group_member_already", format_user(session, u->uid), tmp[x]); if (!modified) modified = -1; } } if (chg) query_emit(NULL, "userlist-refresh"); g_strfreev(tmp); continue; } if (match_arg(argv[i], 'u', ("uid"), 2) && argv[i + 1]) { userlist_t *existing; char *tmp1, *tmp2; int q = 1; if (valid_plugin_uid(&gg_plugin, argv[i + 1]) != 1) { printq("invalid_uid"); g_strfreev(argv); return -1; } if ((existing = userlist_find(session, argv[i + 1]))) { if (existing->nickname) { printq("user_exists_other", argv[i + 1], format_user(session, existing->uid), session_name(session)); g_strfreev(argv); return -1; } else { char *egroups = group_to_string(existing->groups, 1, 0); if (egroups) { char **arr = array_make(egroups, ",", 0, 0, 0); int i; for (i = 0; arr[i]; i++) ekg_group_add(u, arr[i]); g_strfreev(arr); } userlist_remove(session, existing); } } tmp1 = xstrdup(u->uid); tmp2 = xstrdup(argv[i + 1]); query_emit(NULL, "userlist-removed", &tmp1, &tmp2, &q); xfree(tmp1); xfree(tmp2); userlist_clear_status(session, u->uid); tmp1 = xstrdup(argv[++i]); query_emit(NULL, "userlist-added", &tmp1, &tmp1, &q); xfree((void *) u->uid); u->uid = tmp1; modified = 1; continue; } if (match_arg(argv[i], 'o', ("offline"), 2)) { query_emit(NULL, "user-offline", &u, &session); modified = 2; continue; } if (match_arg(argv[i], 'O', ("online"), 2)) { query_emit(NULL, "user-online", &u, &session); modified = 2; continue; } printq("invalid_params", name, argv[i]); g_strfreev(argv); return -1; } if (xstrcmp(name, ("add"))) { switch (modified) { case 0: printq("not_enough_params", name); res = -1; break; case 1: printq("modify_done", par[0]); case 2: config_changed = 1; break; } } else config_changed = 1; g_strfreev(argv); return res; } static TIMER(gg_checked_timer_handler) { const gg_currently_checked_t *c = (gg_currently_checked_t *) data; list_t l; if (type == 1) { xfree(data); return -1; } for (l = gg_currently_checked; l; l = l->next) { gg_currently_checked_t *c2 = l->data; if (c2->session == c->session) { userlist_t *u = userlist_find(c->session, c->uid); if (u) { if (u->status == EKG_STATUS_INVISIBLE) { char *session = xstrdup(session_uid_get(c->session)); char *uid = xstrdup(c->uid); int status = EKG_STATUS_NA; char *descr = xstrdup(u->descr); char *host = NULL; int port = 0; time_t when = time(NULL); query_emit(NULL, "protocol-status", &session, &uid, &status, &descr, &host, &port, &when, NULL); xfree(session); xfree(uid); xfree(descr); } } else print("gg_user_is_not_connected", session_name(c->session), format_user(c->session, c->uid)); xfree(c2->uid); list_remove(&gg_currently_checked, c2, 1); return -1; } } return -1; /* timer tymczasowy */ } static COMMAND(gg_command_check_conn) { struct gg_msg_richtext_format_img { struct gg_msg_richtext rt; struct gg_msg_richtext_format f; struct gg_msg_richtext_image image; } msg; userlist_t *u; gg_private_t *g = session_private_get(session); gg_currently_checked_t c, *c_timer; list_t l; msg.rt.flag = 2; msg.rt.length = 13; msg.f.position = 0; msg.f.font = 0x80; msg.image.unknown1 = 0x0109; msg.image.size = 20; msg.image.crc32 = GG_CRC32_INVISIBLE; if (!(u = userlist_find(session, target))) { printq("user_not_found", target); return -1; } for (l = gg_currently_checked; l; l = l->next) { gg_currently_checked_t *c = l->data; if (!xstrcmp(c->uid, u->uid) && c->session == session) { debug("-- check_conn - we are already waiting for user to be connected\n"); return 0; } } if (gg_send_message_richtext(g->sess, GG_CLASS_MSG, atoi(u->uid + 3), (const unsigned char *) "", (const unsigned char *) &msg, sizeof(msg)) == -1) { debug("-- check_conn - shits happens\n"); return -1; } c_timer = xmalloc(sizeof(gg_currently_checked_t)); c_timer->uid = xstrdup(u->uid); /* if user gets deleted, we won't get undef value */ c_timer->session = session; c.uid = c_timer->uid; c.session = session; list_add(&gg_currently_checked, g_memdup(&c, sizeof(c))); /* if there is no reply after 15 secs user is not connected */ timer_add(&gg_plugin, NULL, 15, 0, gg_checked_timer_handler, c_timer); return 0; } void gg_register_commands() { #define GG_ONLY SESSION_MUSTBELONG | SESSION_MUSTHASPRIVATE #define GG_FLAGS GG_ONLY | SESSION_MUSTBECONNECTED #define GG_FLAGS_TARGET GG_FLAGS | COMMAND_ENABLEREQPARAMS | COMMAND_PARAMASTARGET command_add(&gg_plugin, ("gg:add"), "!U ? p", gg_command_modify, COMMAND_ENABLEREQPARAMS, "-f --find"); command_add(&gg_plugin, ("gg:connect"), "r", gg_command_connect, GG_ONLY, NULL); command_add(&gg_plugin, ("gg:disconnect"), "r", gg_command_connect, GG_ONLY, NULL); command_add(&gg_plugin, ("gg:reconnect"), "r", gg_command_connect, GG_ONLY, NULL); command_add(&gg_plugin, ("gg:msg"), "!uUC !", gg_command_msg, GG_ONLY | COMMAND_ENABLEREQPARAMS | COMMAND_PARAMASTARGET, NULL); command_add(&gg_plugin, ("gg:chat"), "!uUC !", gg_command_msg, GG_ONLY | COMMAND_ENABLEREQPARAMS | COMMAND_PARAMASTARGET, NULL); command_add(&gg_plugin, ("gg:"), "?", gg_command_inline_msg, GG_ONLY | COMMAND_PASS_UNCHANGED, NULL); command_add(&gg_plugin, ("gg:away"), "r", gg_command_away, GG_ONLY, NULL); command_add(&gg_plugin, ("gg:_autoaway"), "?", gg_command_away, GG_ONLY, NULL); command_add(&gg_plugin, ("gg:back"), "r", gg_command_away, GG_ONLY, NULL); #ifdef GG_FEATURE_DND_FFC command_add(&gg_plugin, ("gg:dnd"), "r", gg_command_away, GG_ONLY, NULL); command_add(&gg_plugin, ("gg:ffc"), "r", gg_command_away, GG_ONLY, NULL); #endif command_add(&gg_plugin, ("gg:_autoback"), "?", gg_command_away, GG_ONLY, NULL); command_add(&gg_plugin, ("gg:_autoscroll"), "?", gg_command_away, GG_ONLY, NULL); command_add(&gg_plugin, ("gg:check_conn"), "!uUC", gg_command_check_conn, GG_FLAGS_TARGET, NULL); command_add(&gg_plugin, ("gg:invisible"), "r", gg_command_away, GG_ONLY, NULL); command_add(&gg_plugin, ("gg:image"), "!u !f", gg_command_image, COMMAND_ENABLEREQPARAMS, NULL); command_add(&gg_plugin, ("gg:block"), "uUC", gg_command_block, GG_ONLY, NULL); command_add(&gg_plugin, ("gg:unblock"), "!b", gg_command_unblock, GG_ONLY | COMMAND_ENABLEREQPARAMS, NULL); command_add(&gg_plugin, ("gg:modify"), "!Uu ?", gg_command_modify, COMMAND_ENABLEREQPARAMS, NULL); command_add(&gg_plugin, ("gg:remind"), "? ?", gg_command_remind, 0, NULL); command_add(&gg_plugin, ("gg:register"), "? ? ?", gg_command_register, 0, NULL); command_add(&gg_plugin, ("gg:token"), NULL, gg_command_token, 0, NULL); command_add(&gg_plugin, ("gg:unregister"), "! ! !", gg_command_unregister, COMMAND_ENABLEREQPARAMS, NULL); command_add(&gg_plugin, ("gg:passwd"), "? ?", gg_command_passwd, GG_ONLY, NULL); command_add(&gg_plugin, ("gg:userlist"), "p ?", gg_command_list, GG_ONLY, "-c --clear -g --get -p --put"); command_add(&gg_plugin, ("gg:find"), "!puUC puUC puUC puUC puUC puUC puUC puUC puUC puUC puUC", gg_command_find, GG_FLAGS_TARGET, "-u --uin -f --first -l --last -n --nick -c --city -b --born -a --active -F --female -M --male -s --start -A --all -S --stop"); command_add(&gg_plugin, ("gg:change"), "p", gg_command_change, GG_ONLY, "-f --first -l --last -n --nick -b --born -c --city -N --familyname -C --familycity -F --female -M --male"); command_add(&gg_plugin, ("gg:dcc"), "p uU f ?", gg_command_dcc, GG_ONLY, "send rsend get resume rvoice voice close list"); } /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/plugins/gg/dcc.c000066400000000000000000000435541175142753400171430ustar00rootroot00000000000000/* $Id$ */ #include "ekg2.h" #include #include #include #include #include #include #include #include #include #include #include "dcc.h" #include "gg.h" struct gg_dcc *gg_dcc_socket = NULL; static int dcc_limit_time = 0; /* time from the first connection */ static int dcc_limit_count = 0; /* how many connections from the last time */ /* vars */ int gg_config_audio; int gg_config_dcc; int gg_config_dcc_port; char *gg_config_dcc_ip; char *gg_config_dcc_limit; /* * gg_changed_dcc() * * called when some dcc_* variables are changed */ /* khem, var jest gg:*.... byl kod ktory nie dzialal... tak? */ void gg_changed_dcc(const char *var) { if (!xstrcmp(var, ("gg:dcc"))) { if (!gg_config_dcc) { gg_dcc_socket_close(); gg_dcc_ip = 0; gg_dcc_port = 0; } if (gg_config_dcc) { if (gg_dcc_socket_open(gg_config_dcc_port) == -1) print("dcc_create_error", strerror(errno)); } } else if (!xstrcmp(var, ("gg:dcc_ip"))) { if (gg_config_dcc_ip) { if (!xstrcasecmp(gg_config_dcc_ip, "auto")) { gg_dcc_ip = inet_addr("255.255.255.255"); } else { if (inet_addr(gg_config_dcc_ip) != INADDR_NONE) gg_dcc_ip = inet_addr(gg_config_dcc_ip); else { print("dcc_invalid_ip"); gg_config_dcc_ip = NULL; gg_dcc_ip = 0; } } } else gg_dcc_ip = 0; } else if (!xstrcmp(var, ("gg:dcc_port"))) { if (gg_config_dcc && gg_config_dcc_port) { gg_dcc_socket_close(); gg_dcc_ip = 0; gg_dcc_port = 0; if (gg_dcc_socket_open(gg_config_dcc_port) == -1) print("dcc_create_error", strerror(errno)); } } else if (!xstrcmp(var, ("gg:audio"))) { gg_config_audio = 0; debug("[gg_config_audio] gg:audio not supported.\n"); } if (!in_autoexec) print("config_must_reconnect"); } #ifdef HAVE_GG_DCC7 static TIMER(gg_dcc_handler_timeout) { struct gg_common *gd = (struct gg_common *) data; if (type) { return 0; } debug("gg_dcc_handler_timeout() type: %d state: %d\n", gd->type, gd->state); if (gd->type == GG_SESSION_DCC7_GET) { if (gd->fd != -1) { debug("gg_dcc_handler_timeout() gd->fd: %d\n", gd->fd); /* great! */ watch_add(&gg_plugin, gd->fd, gd->check, gg_dcc7_handler, gd); /* add watch */ return -1; /* destroy timer */ } } else { if (gd->state == GG_STATE_REQUESTING_ID) { /* timeoutnelo na zadaniu id */ /* here destroy dcc */ return -1; } if (gd->state == GG_STATE_WAITING_FOR_ACCEPT) /* timeoutnelo na oczekiwnaiu na uzytkownika */ return 0; } return 0; } #endif COMMAND(gg_command_dcc) { uin_t uin = atoi(session->uid + 3); gg_private_t *g = session_private_get(session); /* send, rsend */ if (params[0] && (!xstrncasecmp(params[0], "se", 2) || !xstrncasecmp(params[0], "rse", 3))) { struct stat st; userlist_t *u; gg_userlist_private_t *up; dcc_t *d; int fd; int __ip, __port; const char *fn; void *dccdata = NULL; if (!params[1] || !params[2]) { printq("not_enough_params", name); return -1; } if (!(fn = prepare_path_user(params[2]))) { printq("generic_error", "path too long"); /* XXX? */ return -1; } if (!(u = userlist_find(session, get_uid(session, params[1])))) { printq("user_not_found", params[1]); return -1; } up = gg_userlist_priv_get(u); __ip = user_private_item_get_int(u, "ip"); __port = user_private_item_get_int(u, "port"); if (!session_connected_get(session)) { printq("not_connected", session_name(session)); return -1; } if (u->status == EKG_STATUS_NA) { printq("dcc_user_not_avail", format_user(session, u->uid)); return -1; } if (!__ip) { printq("dcc_user_aint_dcc", format_user(session, u->uid)); return -1; } if (!stat(fn, &st) && !S_ISREG(st.st_mode)) { printq("io_nonfile", params[2]); return -1; } if ((fd = open(fn, O_RDONLY|O_NONBLOCK)) == -1) { if (errno == ENXIO) printq("io_nonfile", params[2]); else printq("io_cantopen", params[2], strerror(errno)); return -1; } close(fd); #ifdef HAVE_GG_DCC7 if (up->protocol >= 0x2a) { struct gg_dcc7 *gd; if (!(gd = gg_dcc7_send_file(g->sess, atoi(u->uid + 3), fn, fn, NULL))) { printq("dcc_error", strerror(errno)); return -1; } dccdata = gd; } else #endif { if (__port < 10 || !xstrncasecmp(params[0], "rse", 3)) { /* nie moemy si z nim poczy, wic on sprbuje */ gg_dcc_request(g->sess, atoi(u->uid + 3)); } else { struct gg_dcc *gd; if (!(gd = gg_dcc_send_file(__ip, __port, uin, atoi(u->uid + 3)))) { printq("dcc_error", strerror(errno)); return -1; } if (gg_dcc_fill_file_info(gd, fn) == -1) { printq("dcc_open_error", params[2], strerror(errno)); gg_free_dcc(gd); return -1; } dccdata = gd; } } d = dcc_add(session, u->uid, DCC_SEND, dccdata); dcc_filename_set(d, fn); dcc_size_set(d, st.st_size); if (dccdata) { struct gg_common *gd = (struct gg_common *) dccdata; #ifdef HAVE_GG_DCC7 if (gd->fd != -1) watch_add(&gg_plugin, gd->fd, gd->check, gg_dcc_handler, gd); else { debug("[GG,DCC,SEND] Adding timer [timeout: %d] instead of watch (w->fd: %d) (w->check: %d) (gd: %p)\n", gd->timeout, gd->fd, gd->check, gd); timer_add(&gg_plugin, NULL, gd->timeout, 1, gg_dcc_handler_timeout, gd); } #endif watch_add(&gg_plugin, gd->fd, gd->check, gg_dcc_handler, gd); } return 0; } if (params[0] && (params[0][0] == 'v' || !xstrncasecmp(params[0], "rvo", 3))) { /* voice, rvoice */ if (!params[1]) { printq("not_enough_params", name); return -1; } printq("dcc_voice_unsupported"); return -1; } /* get, resume */ if (params[0] && (!xstrncasecmp(params[0], "g", 1) || !xstrncasecmp(params[0], "re", 2))) { dcc_t *d = NULL, *D; struct gg_common *g; char *path; int fd; unsigned int offset = 0; for (D = dccs; D; D = D->next) { userlist_t *u; if (!dcc_private_get(D) || !dcc_filename_get(D) || dcc_type_get(D) != DCC_GET) continue; if (!params[1]) { if (dcc_active_get(D)) continue; d = D; break; } if (params[1][0] == '#' && xstrlen(params[1]) > 1 && atoi(params[1] + 1) == dcc_id_get(D)) { d = D; break; } if ((u = userlist_find(session, dcc_uid_get(D)))) { if (!xstrcasecmp(params[1], u->uid) || (u->nickname && !xstrcasecmp(params[1], u->nickname))) { d = D; break; } } } if (!d || !(g = dcc_private_get(d))) { printq("dcc_not_found", (params[1]) ? params[1] : ""); return -1; } if (dcc_active_get(d)) { printq("dcc_receiving_already", dcc_filename_get(d), format_user(session, dcc_uid_get(d))); return -1; } if (xstrncmp(d->uid, "gg:", 3)) { debug_error("%s:%d /dcc command, incorrect `%s`!\n", __FILE__, __LINE__, __(d->uid)); printq("generic_error", "Use /dcc on correct session, sorry"); return -1; } if (config_dcc_dir) path = saprintf("%s/%s", config_dcc_dir, dcc_filename_get(d)); else path = xstrdup(dcc_filename_get(d)); if (params[0][0] == 'r') { fd = open(path, O_WRONLY); if (fd != -1) offset = lseek(fd, 0, SEEK_END); } else fd = open(path, O_WRONLY | O_CREAT, 0600); if (fd == -1) { printq("dcc_get_cant_create", path); #ifdef HAVE_GG_DCC7 /* XXX, use damn close_handler! and call only dcc_close() */ if (g->type == GG_SESSION_DCC7_GET) gg_dcc7_free((struct gg_dcc7 *) g); else #endif gg_free_dcc((struct gg_dcc *) g); dcc_close(d); xfree(path); return -1; } xfree(path); printq("dcc_get_getting", format_user(session, dcc_uid_get(d)), dcc_filename_get(d)); dcc_active_set(d, 1); #ifdef HAVE_GG_DCC7 if (g->type == GG_SESSION_DCC7_GET) { struct gg_dcc7 *gd = (struct gg_dcc7 *) g; gd->file_fd = fd; gd->offset = offset; /* hack, active waiting */ timer_add(&gg_plugin, NULL, 1 /* gd->timeout */, 1, gg_dcc_handler_timeout, gd); gg_dcc7_accept(gd, offset); /* accept */ } else #endif { struct gg_dcc *gd = (struct gg_dcc *) g; gd->file_fd = fd; gd->offset = offset; watch_add(&gg_plugin, gd->fd, gd->check, gg_dcc_handler, gd); } return 0; } return cmd_dcc(name, params, session, target, quiet); } #ifdef HAVE_GG_DCC7 static void gg_dcc7_close_handler(dcc_t *d) { struct gg_dcc7 *g; if (!d || !(g = d->priv)) return; gg_dcc7_free(g); } #endif /* * gg_dcc_find() * * szuka dcc_t zawierajcy dan struct gg_dcc. */ dcc_t *gg_dcc_find(void *d) { dcc_t *D; for (D = dccs; D; D = D->next) { if (D && D->priv == d) return D; } return NULL; } #ifdef HAVE_GG_DCC7 WATCHER(gg_dcc7_handler) { dcc_t *D; struct gg_event *e; struct gg_dcc7 *d = data; int again = 1; if (type) return 0; D = gg_dcc_find(d); if (!(e = gg_dcc7_watch_fd(d))) { print("dcc_error", strerror(errno)); if (d->type != GG_SESSION_DCC7_SOCKET) { if (!D) { gg_dcc7_free(d); } else { gg_dcc7_close_handler(D); dcc_close(D); } return -1; } } switch (e->type) { case GG_EVENT_DCC7_DONE: debug_function("## GG_EVENT_DCC7_DONE\n"); again = 0; if (!D) { gg_dcc7_free(d); break; } print((D->type == DCC_SEND) ? "dcc_done_send" : "dcc_done_get", format_user(D->session, D->uid), D->filename); gg_dcc7_close_handler(D); dcc_close(D); break; case GG_EVENT_DCC7_ERROR: { struct in_addr addr; unsigned short port = d->remote_port; char *tmp; again = 0; if (!D) { gg_dcc7_free(d); break; } addr.s_addr = d->remote_addr; #if 0 if (d->peer_uin) { struct userlist *u = userlist_find(D->session, D->uid); if (!addr.s_addr && u) { addr.s_addr = u->ip.s_addr; port = u->port; } tmp = saprintf("%s (%s:%d)", format_user(D->session, D->uid), inet_ntoa(addr), port); } else tmp = saprintf("%s:%d", inet_ntoa(addr), port); #endif tmp = saprintf("%s (%s:%d)", format_user(D->session, D->uid), inet_ntoa(addr), port); switch (e->event.dcc7_error) { case GG_ERROR_DCC7_HANDSHAKE: print("dcc_error_handshake", tmp); break; case GG_ERROR_DCC7_NET: print("dcc_error_network", tmp); break; case GG_ERROR_DCC7_REFUSED: print("dcc_error_refused", tmp); break; default: print("dcc_error_unknown", tmp); } xfree(tmp); gg_dcc7_close_handler(D); dcc_close(D); break; } } if (d /* && d->type != GG_SESSION_DCC_SOCKET */ && again) { if (d->fd == fd && d->check == watch) return 0; watch_add(&gg_plugin, d->fd, d->check, gg_dcc7_handler, d); /* XXX, timeouty */ } gg_event_free(e); return -1; } #endif /* * gg_dcc_handler() * * obsuga bezporednich pocze. w data jest przekazywana struct gg_dcc. */ WATCHER(gg_dcc_handler) /* tymczasowy */ { struct gg_event *e; struct gg_dcc *d = data; int again = 1; dcc_t *D; if (type != 0) return 0; if (!(e = gg_dcc_watch_fd(d))) { print("dcc_error", strerror(errno)); if (d->type == GG_SESSION_DCC_SOCKET) gg_dcc_socket_close(); return -1; } switch (e->type) { case GG_EVENT_DCC_NEW: { struct gg_dcc *d = e->event.dcc_new; int __port, __valid = 1; char *__host; debug("[gg] GG_EVENT_DCC_CLIENT_NEW\n"); if (gg_config_dcc_limit) { int c, t = 60; char *tmp; if ((tmp = xstrchr(gg_config_dcc_limit, '/'))) t = atoi(tmp + 1); c = atoi(gg_config_dcc_limit); if (time(NULL) - dcc_limit_time > t) { dcc_limit_time = time(NULL); dcc_limit_count = 0; } dcc_limit_count++; if (dcc_limit_count > c) { print("dcc_limit"); gg_config_dcc = 0; gg_changed_dcc(("dcc")); dcc_limit_time = 0; dcc_limit_count = 0; gg_dcc_free(e->event.dcc_new); e->event.dcc_new = NULL; break; } } __host = inet_ntoa(*((struct in_addr*) &d->remote_addr)); __port = d->remote_port; query_emit(NULL, "protocol-dcc-validate", &__host, &__port, &__valid, NULL); if (__valid) watch_add(&gg_plugin, d->fd, d->check, gg_dcc_handler, d); else gg_dcc_free(d); e->event.dcc_new = NULL; break; } case GG_EVENT_DCC_CLIENT_ACCEPT: { char peer[16], uin[16]; session_t *s; debug("[gg] GG_EVENT_DCC_CLIENT_ACCEPT\n"); snprintf(peer, sizeof(peer), "gg:%d", d->peer_uin); snprintf(uin, sizeof(uin), "gg:%d", d->uin); s = session_find(uin); if (!s || !userlist_find(s, peer) || (ignored_check(s, peer) & IGNORE_DCC)) { debug("[gg] unauthorized client (uin=%ld), closing connection\n", d->peer_uin); gg_free_dcc(d); gg_event_free(e); return -1; } break; } case GG_EVENT_DCC_CALLBACK: { int found = 0; char peer[16]; debug("[gg] GG_EVENT_DCC_CALLBACK\n"); snprintf(peer, sizeof(peer), "gg:%d", d->peer_uin); for (D = dccs; D; D = D->next) { debug("[gg] dcc id=%d, uid=%d, type=%d\n", dcc_id_get(D), dcc_uid_get(D), dcc_type_get(D)); if (!xstrcmp(dcc_uid_get(D), peer) && !dcc_private_get(D)) { debug("[gg] found transfer, uid=%d, type=%d\n", dcc_uid_get(D), dcc_type_get(D)); dcc_private_set(D, d); gg_dcc_set_type(d, (dcc_type_get(D) == DCC_SEND) ? GG_SESSION_DCC_SEND : GG_SESSION_DCC_VOICE); found = 1; break; } } if (!found) { debug("[gg] connection from %d not found\n", d->peer_uin); gg_dcc_free(d); gg_event_free(e); return -1; } break; /* w ekg jest break... byl bug? */ } case GG_EVENT_DCC_NEED_FILE_INFO: { debug("[gg] GG_EVENT_DCC_NEED_FILE_INFO\n"); for (D = dccs; D; D = D->next) { if (dcc_private_get(D) != d) continue; if (gg_dcc_fill_file_info(d, dcc_filename_get(D)) == -1) { debug("[gg] gg_dcc_fill_file_info() failed (%s)\n", strerror(errno)); print("dcc_open_error", dcc_filename_get(D)); dcc_close(D); gg_free_dcc(d); break; } break; } break; } case GG_EVENT_DCC_NEED_FILE_ACK: { char *path; unsigned char *p; char uin[16]; struct stat st; debug("[gg] GG_EVENT_DCC_NEED_FILE_ACK\n"); snprintf(uin, sizeof(uin), "gg:%d", d->uin); again = 0; if (!(D = gg_dcc_find(d))) { char peer[16]; snprintf(peer, sizeof(peer), "gg:%d", d->peer_uin); D = dcc_add(NULL, peer, DCC_GET, d); /* XXX, fix it. */ } for (p = d->file_info.filename; *p; p++) if (*p < 32 || *p == '\\' || *p == '/') *p = '_'; if (d->file_info.filename[0] == '.') d->file_info.filename[0] = '_'; dcc_filename_set(D, (const char *) d->file_info.filename); dcc_size_set(D, d->file_info.size); print("dcc_get_offer", format_user(session_find(uin), dcc_uid_get(D)), dcc_filename_get(D), ekg_itoa(d->file_info.size), ekg_itoa(dcc_id_get(D))); if (config_dcc_dir) path = saprintf("%s/%s", config_dcc_dir, dcc_filename_get(D)); else path = xstrdup(dcc_filename_get(D)); if (!stat(path, &st) && st.st_size < d->file_info.size) print("dcc_get_offer_resume", format_user(session_find(uin), dcc_uid_get(D)), dcc_filename_get(D), ekg_itoa(d->file_info.size), ekg_itoa(dcc_id_get(D))); xfree(path); break; } case GG_EVENT_DCC_DONE: { char uin[16]; debug("[gg] GG_EVENT_DCC_DONE\n"); snprintf(uin, sizeof(uin), "gg:%d", d->uin); if (!(D = gg_dcc_find(d))) { gg_free_dcc(d); d = NULL; break; } print((dcc_type_get(D) == DCC_SEND) ? "dcc_done_send" : "dcc_done_get", format_user(session_find(uin), dcc_uid_get(D)), dcc_filename_get(D)); dcc_close(D); gg_free_dcc(d); d = NULL; break; } case GG_EVENT_DCC_ERROR: { struct in_addr addr; unsigned short port = d->remote_port; char *tmp; char uin[16]; snprintf(uin, sizeof(uin), "gg:%d", d->uin); addr.s_addr = d->remote_addr; if (d->peer_uin) { char peer[16]; userlist_t *u; snprintf(peer, sizeof(peer), "gg:%d", d->peer_uin); u = userlist_find(session_find(uin), peer); if (!addr.s_addr && u) { addr.s_addr = user_private_item_get_int(u, "ip"); port = user_private_item_get_int(u, "port"); } tmp = saprintf("%s (%s:%d)", format_user(session_find(uin), peer), inet_ntoa(addr), port); } else tmp = saprintf("%s:%d", inet_ntoa(addr), port); switch (e->event.dcc_error) { case GG_ERROR_DCC_HANDSHAKE: print("dcc_error_handshake", tmp); break; case GG_ERROR_DCC_NET: print("dcc_error_network", tmp); break; case GG_ERROR_DCC_REFUSED: print("dcc_error_refused", tmp); break; default: print("dcc_error_unknown", tmp); } xfree(tmp); dcc_close(gg_dcc_find(d)); gg_free_dcc(d); d = NULL; break; } case GG_EVENT_DCC_NEED_VOICE_ACK: case GG_EVENT_DCC_VOICE_DATA: break; } /* uaktualnij statystyki */ for (D = dccs; D; D = D->next) { if (dcc_private_get(D) != d) continue; if (!d) continue; if (d->state == GG_STATE_SENDING_FILE_HEADER || d->state == GG_STATE_READING_FILE_HEADER) dcc_active_set(D, 1); if (d->state == GG_STATE_READING_VOICE_HEADER) dcc_active_set(D, 1); if (d->state == GG_STATE_SENDING_FILE || d->state == GG_STATE_GETTING_FILE) dcc_offset_set(D, d->offset); } if (d && d->type != GG_SESSION_DCC_SOCKET && again) { if (d->fd == fd && d->check == watch) return 0; watch_add(&gg_plugin, d->fd, d->check, gg_dcc_handler, d); } gg_event_free(e); return -1; } static WATCHER(gg_dcc_handler_open) { /* wrapper */ gg_dcc_handler(type, fd, watch, data); return 0; } /* * gg_dcc_socket_open() */ int gg_dcc_socket_open(int port) { if (gg_dcc_socket) return 0; if (!(gg_dcc_socket = gg_dcc_socket_create(1, port))) return -1; watch_add(&gg_plugin, gg_dcc_socket->fd, gg_dcc_socket->check, gg_dcc_handler_open, gg_dcc_socket); return 0; } /* * gg_dcc_socket_close() * * zamyka nasuchujcy port dcc. */ void gg_dcc_socket_close() { if (!gg_dcc_socket) return; watch_remove(&gg_plugin, gg_dcc_socket->fd, gg_dcc_socket->check); gg_free_dcc(gg_dcc_socket); gg_dcc_socket = NULL; } /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/plugins/gg/dcc.h000066400000000000000000000006401175142753400171350ustar00rootroot00000000000000/* $Id$ */ #ifndef __EKG_GG_DCC_H #define __EKG_GG_DCC_H COMMAND(gg_command_dcc); void gg_changed_dcc(const char *var); int gg_dcc_socket_open(int port); void gg_dcc_socket_close(); dcc_t *gg_dcc_find(void *D); WATCHER(gg_dcc_handler); #ifdef HAVE_GG_DCC7 WATCHER(gg_dcc7_handler); #endif #endif /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/plugins/gg/gg.c000066400000000000000000001370071175142753400170040ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2003 Wojtek Kaniewski * 2004 - 2006 Adam Mikuta * 2005 Leszek Krupiski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dcc.h" #include "gg.h" #include "misc.h" #include "pubdir.h" #include "images.h" #include "pubdir50.h" static int gg_theme_init(); static void gg_session_handler_msg(session_t *s, struct gg_event *e); PLUGIN_DEFINE(gg, PLUGIN_PROTOCOL, gg_theme_init); list_t gg_currently_checked = NULL; char *last_tokenid; int gg_config_display_token; int gg_config_skip_default_format; int gg_config_split_messages; int gg_config_enable_chatstates = 1; /** * gg_session_init() * * Handler for: SESSION_ADDED
* Init private session struct gg_private_t if @a session is gg one.
* Read saved userlist by userlist_read() * * @param ap 1st param: (char *) session - uid of session * @param data NULL * * @return 0 if @a session is gg one, and we init memory
* 1 if we don't found such session, or it wasn't gg session [most probable], or we already init memory. */ static QUERY(gg_session_init) { char *session = *(va_arg(ap, char**)); session_t *s = session_find(session); gg_private_t *g; if (!s || s->priv || s->plugin != &gg_plugin) return 1; g = xmalloc(sizeof(gg_private_t)); userlist_read(s); s->priv = g; return 0; } /** * gg_session_deinit() * * Handler for: SESSION_REMOVED
* Free memory allocated by gg_private_t if @a session is gg one. * * @param ap 1st param: (char *) session - uid of session * @param data NULL * * @todo Check if we really free all memory allocated by session. * * @return 0 if @a session is gg one, and memory allocated where xfree()'d.
* 1 if not such session, or it wasn't gg session [most probable], or we already free memory. */ static QUERY(gg_session_deinit) { char *session = *(va_arg(ap, char**)); session_t *s = session_find(session); gg_private_t *g; list_t l; if (!s || !(g = s->priv) || s->plugin != &gg_plugin) return 1; if (g->sess) gg_free_session(g->sess); for (l = g->searches; l; l = l->next) gg_pubdir50_free((gg_pubdir50_t) l->data); list_destroy(g->searches, 0); xfree(g); s->priv = NULL; return 0; } /** * gg_userlist_info_handle() * * Handler for: USERLIST_INFO
* (Emited by: /list command, when we want know more about given user)
* printq() all gg-protocol-only-data like: possible client version [read: which version of protocol he use], if he has voip, etc.. * * @param ap 1st param: (userlist_t *) u - item. * @param ap 2nd param: (int) quiet - If quiet for printq() * @param data NULL * * @return 1 - If no @a u passed, or it's invalid for gg plugin
* else printq() info and return 0 */ static QUERY(gg_userlist_info_handle) { userlist_t *u = *va_arg(ap, userlist_t **); int quiet = *va_arg(ap, int *); gg_userlist_private_t *up; int __port; if (!u || valid_plugin_uid(&gg_plugin, u->uid) != 1 || !(up = gg_userlist_priv_get(u))) return 1; __port = user_private_item_get_int(u, "port"); if (__port == 2) printq("gg_user_info_not_in_contacts"); if (__port == 1) printq("gg_user_info_firewalled"); if ((up->protocol & GG_HAS_AUDIO_MASK)) printq("gg_user_info_voip"); if ((up->protocol & 0x00ffffff)) { int v = up->protocol & 0x00ffffff; const char *ver = NULL; if (v < 0x0b) ver = ("<= 4.0.x"); if (v >= 0x0f && v <= 0x10) ver = ("4.5.x"); if (v == 0x11) ver = ("4.6.x"); if (v >= 0x14 && v <= 0x15) ver = ("4.8.x"); if (v >= 0x16 && v <= 0x17) ver = ("4.9.x"); if (v >= 0x18 && v <= 0x1b) ver = ("5.0.x"); if (v >= 0x1c && v <= 0x1e) ver = ("5.7"); if (v == 0x20) ver = ("6.0 (build >= 129)"); if (v == 0x21) ver = ("6.0 (build >= 133)"); if (v == 0x22) ver = ("6.0 (build >= 140)"); if (v == 0x24) ver = ("6.1 (build >= 155) || 7.6 (build >= 1359)"); if (v == 0x25) ver = ("7.0 (build >= 1)"); if (v == 0x26) ver = ("7.0 (build >= 20)"); if (v == 0x27) ver = ("7.0 (build >= 22)"); if (v == 0x28) ver = ("7.5.0 (build >= 2201)"); if (v == 0x29) ver = ("7.6 (build >= 1688)"); if (v == 0x2a) ver = ("7.7 (build >= 3315)"); if (v == 0x2d) ver = ("8.0 (build >= 4881)"); if (v == 0x2e) ver = ("8.0 (build >= 8283)"); if (ver) { printq("gg_user_info_version", ver); } else { char *tmp = saprintf(("unknown (%#.2x)"), v); printq("gg_user_info_version", tmp); xfree(tmp); } } return 0; } static QUERY(gg_user_offline_handle) { userlist_t *u = *(va_arg(ap, userlist_t **)); session_t *s = *(va_arg(ap, session_t **)); gg_private_t *g; int uin; if (!s || !(g = s->priv) || s->plugin != &gg_plugin) return 1; uin = atoi(u->uid + 3); gg_remove_notify_ex(g->sess, uin, gg_userlist_type(u)); ekg_group_add(u, "__offline"); print("modify_offline", format_user(s, u->uid)); gg_add_notify_ex(g->sess, uin, gg_userlist_type(u)); return 0; } static QUERY(gg_user_online_handle) { userlist_t *u = *(va_arg(ap, userlist_t **)); session_t *s = *(va_arg(ap, session_t **)); gg_private_t *g; int quiet = (data == NULL); int uin; if (!s || !(g = s->priv) || s->plugin != &gg_plugin) return 1; uin = atoi(u->uid + 3); gg_remove_notify_ex(g->sess, uin, gg_userlist_type(u)); ekg_group_remove(u, "__offline"); printq("modify_online", format_user(s, u->uid)); gg_add_notify_ex(g->sess, uin, gg_userlist_type(u)); return 0; } static QUERY(gg_status_show_handle) { char **uid = va_arg(ap, char**); session_t *s = session_find(*uid); userlist_t *u; struct in_addr i; int mqc; char *tmp, *priv, *r1, *r2; gg_private_t *g; if (!s) { debug("Function gg_status_show_handle() called with NULL data\n"); return -1; } if (!(g = session_private_get(s))) return -1; if ((u = userlist_find(s, s->uid)) && u->nickname) print("show_status_uid_nick", s->uid, u->nickname); else print("show_status_uid", s->uid); if (!g->sess || g->sess->state != GG_STATE_CONNECTED) { char *tmp = format_string(format_find("show_status_notavail"), ""); print("show_status_status_simple", tmp); xfree(tmp); if ((mqc = msg_queue_count_session(s->uid))) print("show_status_msg_queue", ekg_itoa(mqc)); return 0; } if (GG_S_F(g->sess->status)) priv = format_string(format_find("show_status_private_on")); else priv = format_string(format_find("show_status_private_off")); r1 = xstrmid(s->descr, 0, GG_STATUS_DESCR_MAXSIZE); r2 = xstrmid(s->descr, GG_STATUS_DESCR_MAXSIZE, -1); tmp = format_string(format_find(ekg_status_label(s->status, s->descr, "show_status_")), r1, r2); xfree(r1); xfree(r2); i.s_addr = g->sess->server_addr; print("show_status_status", tmp, priv); #ifdef __GG_LIBGADU_HAVE_OPENSSL if (g->sess->ssl) print("show_status_server_tls", inet_ntoa(i), ekg_itoa(g->sess->port)); else #endif print("show_status_server", inet_ntoa(i), ekg_itoa(g->sess->port)); xfree(tmp); xfree(priv); return 0; } /* * str_to_uin() * * funkcja, ktra zajmuje si zamian stringa na * liczb i sprawdzeniem, czy to prawidowy uin. * * zwraca uin lub 0 w przypadku bdu. */ /* XXX, pozamieniac atoi() na str_to_uin() */ uin_t str_to_uin(const char *text) { char *tmp; long num; if (!text) return 0; errno = 0; num = strtol(text, &tmp, 0); if (*text == '\0' || *tmp != '\0') return 0; if ((errno == ERANGE || (num == LONG_MAX || num == LONG_MIN)) || num > UINT_MAX || num < 0) return 0; return (uin_t) num; } /** * gg_add_notify_handle() * * Handler for: ADD_NOTIFY
* (Emited by: /add command, when you successfully add smb to the userlist)
* Notify gg server about it. * * @todo We ignore gg_add_notify_ex() result * * @param ap 1st param: (char *) session_uid - session uid * @param ap 2nd param: (char *) uid - user uid * @param data NULL * * @return 1 - If smth is wrong, @a session_uid or @a uid isn't valid gg number, or session is without private struct.
* else 0 * */ static QUERY(gg_add_notify_handle) { char *session_uid = *(va_arg(ap, char **)); char *uid = *(va_arg(ap, char **)); session_t *s = session_find(session_uid); gg_private_t *g; /* Check session */ if (!s) { debug("Function gg_add_notify_handle() called with NULL data\n"); return 1; } if (!(g = s->priv) || s->plugin != &gg_plugin) return 1; /* Check uid */ if (valid_plugin_uid(&gg_plugin, uid) != 1) return 1; gg_add_notify_ex(g->sess, str_to_uin(uid+3), gg_userlist_type(userlist_find(s, s->uid))); return 0; } /** * gg_remove_notify_handle() * * Handler for: REMOVE_NOTIFY
* (Emited by: /del command, when we sucessfully remove smb from userlist.
* Notify gg server about it. * * @todo We ignore gg_remove_notify() result * * @param ap 1st param: (char *) session_uid - session uid * @param ap 2nd param: (char *) uid - user uid * @param data NULL * * @return 1 - If smth is wrong, @a session_uid or @a uid isn't valid gg number, or session is without private struct.
* else 0 */ static QUERY(gg_remove_notify_handle) { char *session_uid = *(va_arg(ap, char **)); char *uid = *(va_arg(ap, char **)); session_t *s = session_find(session_uid); gg_private_t *g; /* Check session */ if (!s) { debug("Function gg_remove_notify_handle() called with NULL data\n"); return 1; } if (!(g = s->priv) || s->plugin != &gg_plugin) return 1; /* Check uid */ if (valid_plugin_uid(&gg_plugin, uid) != 1) return 1; gg_remove_notify(g->sess, str_to_uin(uid+3)); return 0; } /** * gg_print_version() * * Handler for: PLUGIN_PRINT_VERSION * print info about libgadu version. * * @return 0 */ static QUERY(gg_print_version) { char protov[3]; char clientv[sizeof(GG_DEFAULT_CLIENT_VERSION)]; { /* that IMO would be lighter than array_make+array_join */ char *p, *q; for (p = GG_DEFAULT_CLIENT_VERSION, q = clientv; *p; p++) { if (*p == ',') *(q++) = '.'; else if (*p != ' ') *(q++) = *p; } *q = '\0'; } snprintf(protov, 3, "%.2x", GG_DEFAULT_PROTOCOL_VERSION); print("gg_version", gg_libgadu_version(), GG_LIBGADU_VERSION, clientv, protov); return 0; } /** * gg_validate_uid() * * handler for PROTOCOL_VALIDATE_UID
* checks, if @a uid is proper for gg plugin. * * @note Proper for gg plugin means if @a uid starts with "gg:" and uid len > 3 * @todo Blah, irc does xstrncasecmp() here it's only xstrncmp() let's decide... GG: and gg: is proper, or only gg: * @todo Maybe let's check if after gg: we have max 32b number.. because libgadu and gg protocol only support 32bit uids... ;) * * @param ap 1st param: (char *) uid - of user/session/command/whatever * @param ap 2nd param: (int) valid - place to put 1 if uid is valid for gg plugin. * @param data NULL * * @return -1 if it's valid uid for gg plugin
* 0 if not */ static QUERY(gg_validate_uid) { char *uid = *(va_arg(ap, char **)); int *valid = va_arg(ap, int *); if (!uid) return 0; if (!xstrncmp(uid, "gg:", 3) && uid[3]) { uid+=3; /* now let's check if after gg: we have only digits */ for (; *uid; uid++) if (!isdigit(*uid)) return 0; (*valid)++; return -1; } return 0; } static QUERY(gg_userlist_priv_handler) { userlist_t *u = *va_arg(ap, userlist_t **); int function = *va_arg(ap, int *); gg_userlist_private_t *p; const char *tmp; if (!u || ((valid_plugin_uid(&gg_plugin, u->uid) != 1) && !(function == EKG_USERLIST_PRIVHANDLER_READING && atoi(u->uid)))) return 1; if (!(p = u->priv)) { if (function == EKG_USERLIST_PRIVHANDLER_FREE) return -1; p = xmalloc(sizeof(gg_userlist_private_t)); u->priv = p; } switch (function) { case EKG_USERLIST_PRIVHANDLER_FREE: xfree(u->priv); u->priv = NULL; user_private_items_destroy(u); u->priv_list = NULL; break; case EKG_USERLIST_PRIVHANDLER_GET: *va_arg(ap, void **) = p; break; case EKG_USERLIST_PRIVHANDLER_READING: { char **entry = *va_arg(ap, char ***); if (atoi(u->uid)) { /* backwards compatibility / userlist -g hack for GG */ /* * TODO: This is a hack, we should not be * freeing memory which has been passed to us * as const in the first place. But as things * are, we have no option but to cast here. */ char *tmp = (char *) u->uid; u->uid = saprintf("gg:%s", tmp); xfree(tmp); } user_private_item_set(u, "first_name", entry[0]); user_private_item_set(u, "last_name", entry[1]); user_private_item_set(u, "mobile", entry[4]); break; } case EKG_USERLIST_PRIVHANDLER_WRITING: { char **entry = *va_arg(ap, char ***); if ( (tmp = user_private_item_get(u, "first_name")) ) { xfree(entry[0]); entry[0] = xstrdup(tmp); } if ( (tmp = user_private_item_get(u, "last_name")) ) { xfree(entry[1]); entry[1] = xstrdup(tmp); } if ( (tmp = user_private_item_get(u, "mobile")) ) { xfree(entry[4]); entry[4] = xstrdup(tmp); } break; } case EKG_USERLIST_PRIVHANDLER_SETVAR_BYNAME: { const char *name = *va_arg(ap, const char **); const char *val = *va_arg(ap, const char **); user_private_item_set(u, name, val); // XXX ?wo? ??? break; } default: return 2; } return -1; } /* * gg_ping_timer_handler() * * pinguje serwer co jaki czas, jeli jest nadal poczony. */ static TIMER_SESSION(gg_ping_timer_handler) { gg_private_t *g; if (type == 1) return 0; if (!s || !session_connected_get(s)) { return -1; } if ((g = session_private_get(s))) { gg_ping(g->sess); } return 0; } /* * gg_inv_check_handler() * * checks if user marked as invisible, is still connected */ static TIMER(gg_inv_check_handler) { const gg_currently_checked_t *c = (gg_currently_checked_t *) data; userlist_t *u; if (type == 1) { xfree(c->uid); xfree(data); return -1; } if ((u = userlist_find(c->session, c->uid)) && (u->status == EKG_STATUS_INVISIBLE)) { command_exec_format(c->uid, c->session, 1, ("/gg:check_conn")); } return -1; } /* * gg_session_handler_success() * * obsuga udanego poczenia z serwerem. */ static void gg_session_handler_success(session_t *s) { gg_private_t *g = s->priv; int status; int _status; char *descr; char *cpdescr; if (!g || !g->sess) { debug("[gg] gg_session_handler_success() called with null gg_private_t\n"); return; } protocol_connected_emit(s); gg_userlist_send(g->sess, s->userlist); /* zapiszmy adres serwera */ if (session_int_get(s, "connection_save") == 1) { struct in_addr addr; addr.s_addr = g->sess->server_addr; session_set(s, "server", inet_ntoa(addr)); session_int_set(s, "port", g->sess->port); } /* pamitajmy, eby pingowa */ if (timer_find_session(s, "ping") == NULL) timer_add_session(s, "ping", 180, 1, gg_ping_timer_handler); descr = xstrdup(session_descr_get(s)); status = session_status_get(s); cpdescr = locale_to_gg(s, descr); /* ustawiamy swj status */ _status = GG_S(gg_text_to_status(status, s->descr ? cpdescr : NULL)); if (session_int_get(s, "private")) _status |= GG_STATUS_FRIENDS_MASK; if (s->descr) { gg_change_status_descr(g->sess, _status, cpdescr); } else { gg_change_status(g->sess, _status); } xfree(cpdescr); } /* * gg_session_handler_failure() * * obsuga nieudanego poczenia. */ static void gg_session_handler_failure(session_t *s, struct gg_event *e) { gg_private_t *g = s->priv; const char *reason; switch (e->event.failure) { case GG_FAILURE_CONNECTING: reason = "conn_failed_connecting"; break; case GG_FAILURE_INVALID: reason = "conn_failed_invalid"; break; case GG_FAILURE_READING: reason = "conn_failed_disconnected"; break; case GG_FAILURE_WRITING: reason = "conn_failed_disconnected"; break; case GG_FAILURE_PASSWORD: reason = "conn_failed_password"; break; case GG_FAILURE_404: reason = "conn_failed_404"; break; #ifdef __GG_LIBGADU_HAVE_OPENSSL case GG_FAILURE_TLS: reason = "conn_failed_tls"; break; #endif default: reason = "conn_failed_unknown"; break; } if (session_int_get(s, "connection_save") == 1) { session_set(s, "server", NULL); session_int_set(s, "port", GG_DEFAULT_PORT); } else { /* If we have some servers in 'server' variable and we're unable to connect to the first one, * then we should move it to the end and set the second one as default, * maybe that's kinda dirty way, but IMO most flexible [mg] */ const char *oldserver = session_get(s, "server"); const char *comma; if ((comma = xstrchr(oldserver, ','))) { char *newserver = xmalloc(xstrlen(oldserver)+1); xstrcpy(newserver, comma+1); xstrcat(newserver, ","); xstrncat(newserver, oldserver, comma-oldserver); session_set(s, "server", newserver); xfree(newserver); } } gg_free_session(g->sess); g->sess = NULL; protocol_disconnected_emit(s, format_find(reason), EKG_DISCONNECT_FAILURE); } /* * gg_session_handler_disconnect() * * obsuga rozczenia z powodu podczenia drugiej sesji. */ static void gg_session_handler_disconnect(session_t *s) { gg_private_t *g = s->priv; protocol_disconnected_emit(s, NULL, EKG_DISCONNECT_FORCED); gg_logoff(g->sess); /* zamknie gniazdo */ gg_free_session(g->sess); g->sess = NULL; } /* * gg_session_handler_status() * * obsuga zmiany stanu przez uytkownika. */ static void gg_session_handler_status(session_t *s, uin_t uin, int status, const char *descr, guint32 ip, guint16 port, int protocol) { char *__uid = saprintf(("gg:%d"), uin); char *__descr = gg_to_core(s, xstrdup(descr)); int i, j, dlen, state = 0, m = 0; { userlist_t *u; gg_userlist_private_t *up; if ( (u = userlist_find(s, __uid)) ) { if ((up = gg_userlist_priv_get(u))) up->protocol = protocol; /* zapisz adres IP i port */ user_private_item_set_int(u, "ip", ip); user_private_item_set_int(u, "port", port); if (ip) { user_private_item_set_int(u, "last_ip", ip); user_private_item_set_int(u, "last_port", port); } } } for (i = 0; i < xstrlen(__descr); i++) if (__descr[i] == 10 || __descr[i] == 13) m++; dlen = i; /* if it is not set it'll be -1 so, everythings ok */ if ( (i = session_int_get(s, "concat_multiline_status")) && m > i) { for (m = i = j = 0; i < dlen; i++) { if (__descr[i] != 10 && __descr[i] != 13) { __descr[j++] = __descr[i]; state = 0; } else { if (!state && __descr[i] == 10) __descr[j++] = ' '; else m++; if (__descr[i] == 10) state++; } } __descr[j] = '\0'; if (m > 3) { memmove (__descr+4, __descr, j + 1); /* multiline tag */ __descr[0] = '['; __descr[1] = 'm'; __descr[2] = ']'; __descr[3] = ' '; } } protocol_status_emit(s, __uid, gg_status_to_text(status), __descr, time(NULL)); xfree(__descr); xfree(__uid); } /* * gg_session_handler_msg() * * obsuga przychodzcych wiadomoci. */ static void gg_session_handler_msg(session_t *s, struct gg_event *e) { gg_private_t *g = s->priv; char *__sender, **__rcpts = NULL; char *__text; guint32 *__format = NULL; int image = 0, check_inv = 0; int i; if (e->event.msg.msgclass & GG_CLASS_CTCP) { struct gg_dcc *d; char *__host = NULL; char uid[16]; int __ip, __port = -1, __valid = 1; userlist_t *u; watch_t *w; if (!gg_config_dcc) return; snprintf(uid, sizeof(uid), "gg:%d", e->event.msg.sender); if (!(u = userlist_find(s, uid))) return; query_emit(NULL, "protocol-dcc-validate", &__host, &__port, &__valid, NULL); /* xfree(__host); */ if (!__valid) { print_status("dcc_attack", format_user(s, uid)); command_exec_format(NULL, s, 0, ("/ignore %s"), uid); return; } __ip = user_private_item_get_int(u, "ip"); __port = user_private_item_get_int(u, "port"); if (!(d = gg_dcc_get_file(__ip, __port, atoi(session_uid_get(s) + 3), e->event.msg.sender))) { print_status("dcc_error", strerror(errno)); return; } w = watch_add(&gg_plugin, d->fd, d->check, gg_dcc_handler, d); watch_timeout_set(w, d->timeout); return; } for (i = 0; i < e->event.msg.recipients_count; i++) array_add(&__rcpts, saprintf("gg:%d", e->event.msg.recipients[i])); __text = gg_to_core(s, xstrdup((const char *) e->event.msg.message)); if (e->event.msg.formats && e->event.msg.formats_length) { unsigned char *p = e->event.msg.formats; int i, len = xstrlen(__text), ii, skip = gg_config_skip_default_format; static char win_gg_default_format[6] = { 0x00, 0x00, 0x08, 0x00, 0x00, 0x00 }; debug_white("// formats:"); for (ii = 0; ii < e->event.msg.formats_length; ii++) { skip &= (p[ii] == win_gg_default_format[ii]); debug_white(" %.2x", (unsigned char) p[ii]); } if (skip) debug_white(" <- skipping"); else __format = xcalloc(len, sizeof(guint32)); debug_white("\n"); /* XXX, check it. especially this 'pos' */ if (!skip) for (i = 0; i < e->event.msg.formats_length; ) { int j, pos = p[i] + p[i + 1] * 256; guint32 val = 0; if ((p[i + 2] & GG_FONT_IMAGE)) { struct gg_msg_richtext_image *img = (void *) &p[i+3]; /* XXX, needed? */ if (i+3 + sizeof(struct gg_msg_richtext_image) > e->event.msg.formats_length) { debug_error("gg_session_handler_msg() wtf?\n"); break; } debug_function("gg_session_handler_msg() inline image: sender=%d, size=%d, crc32=0x%.8x\n", e->event.msg.sender, img->size, img->crc32); image=1; if (img->crc32 == GG_CRC32_INVISIBLE) check_inv = 1; if (gg_config_get_images) gg_image_request(g->sess, e->event.msg.sender, img->size, img->crc32); i+=sizeof(struct gg_msg_richtext_image); } else { if ((p[i + 2] & GG_FONT_BOLD)) val |= EKG_FORMAT_BOLD; if ((p[i + 2] & GG_FONT_ITALIC)) val |= EKG_FORMAT_ITALIC; if ((p[i + 2] & GG_FONT_UNDERLINE)) val |= EKG_FORMAT_UNDERLINE; if ((p[i + 2] & GG_FONT_COLOR)) { val |= EKG_FORMAT_COLOR | p[i + 3] | (p[i + 4] << 8) | (p[i + 5] << 16); i += 3; } } i += 3; //if (val!=0) // only image format for (j = pos; j < len; j++) __format[j] = val; } } __sender = saprintf("gg:%d", e->event.msg.sender); protocol_xstate_emit(s, __sender, 0, EKG_XSTATE_TYPING); /* stop typing */ if (image && check_inv) { print("gg_we_are_being_checked", session_name(s), format_user(s, __sender)); } else { int __class = e->event.msg.sender ? EKG_MSGCLASS_CHAT : EKG_MSGCLASS_SYSTEM; /* if (!check_inv || xstrcmp(__text, "")) printq("generic", "image in message.\n"); - or something */ protocol_message_emit(s, __sender, __rcpts, __text, __format, e->event.msg.time, __class, NULL, EKG_TRY_BEEP, 0); } xfree(__text); xfree(__sender); xfree(__format); g_strfreev(__rcpts); } /** * gg_session_handler_ack() * * Support for messages acknowledgement.
* Handler for libgadu: GG_EVENT_ACK events */ static void gg_session_handler_ack(session_t *s, struct gg_event *e) { char *__rcpt = saprintf("gg:%d", e->event.ack.recipient); char *__seq = xstrdup(ekg_itoa(e->event.ack.seq)); int __status; /* ifndef + defines for old libgadu */ #ifndef GG_ACK_BLOCKED #define GG_ACK_BLOCKED 0x0001 #endif #ifndef GG_ACK_MBOXFULL #define GG_ACK_MBOXFULL 0x0004 #endif switch (e->event.ack.status) { case GG_ACK_DELIVERED: /* from libgadu.h 1.1 (15-Oct-01) */ __status = EKG_ACK_DELIVERED; break; case GG_ACK_QUEUED: /* from libgadu.h 1.1 (15-Oct-01) */ __status = EKG_ACK_QUEUED; break; case GG_ACK_NOT_DELIVERED: /* from libgadu.h 1.50 (21-Dec-01) */ __status = EKG_ACK_DROPPED; break; case GG_ACK_BLOCKED: /* from libgadu.h 1.175 (21-Dec-04) */ __status = EKG_ACK_DROPPED; break; case GG_ACK_MBOXFULL: /* from libgadu.h 1.175 (21-Dec-04) */ __status = EKG_ACK_TEMPFAIL; break; default: /* unknown neither for ekg2 nor libgadu */ debug_error("gg_session_handler_ack() unknown message ack status. consider upgrade [0x%x]\n", e->event.ack.status); __status = EKG_ACK_UNKNOWN; break; } protocol_message_ack_emit(s, __rcpt, __seq, __status); xfree(__seq); xfree(__rcpt); } /* * gg_session_handler_image() * * support image request or reply * now it is used only by /check_inv * we don't use support images */ static void gg_session_handler_image(session_t *s, struct gg_event *e) { gg_private_t *g = s->priv; switch (e->type) { case GG_EVENT_IMAGE_REQUEST: { list_t l; debug("GG_EVENT_IMAGE_REQUEST (crc32 - %d)\n", e->event.image_request.crc32); if (e->event.image_request.crc32 == GG_CRC32_INVISIBLE) { char *tmp = saprintf("gg:%d", e->event.image_request.sender); list_t l; for (l = gg_currently_checked; l; ) { gg_currently_checked_t *c = l->data; l = l->next; if (c->session == s) { userlist_t *u = userlist_find(s, tmp); if (u) { const int interval = session_int_get(s, "invisible_check_interval"); gg_currently_checked_t *c_timer; if (interval > 0) { c_timer = xmalloc(sizeof(gg_currently_checked_t)); c_timer->uid = xstrdup(tmp); c_timer->session = s; timer_add(&gg_plugin, NULL, interval, 0, gg_inv_check_handler, c_timer); } if (u->status == EKG_STATUS_NA) protocol_status_emit(s, tmp, EKG_STATUS_INVISIBLE, u->descr, time(NULL)); } else print("gg_user_is_connected", session_name(s), format_user(s, tmp)); xfree(c->uid); list_remove(&gg_currently_checked, c, 1); break; } } xfree(tmp); break; } for (l = images; l;) { image_t *i = l->data; l = l->next; if (e->event.image_request.crc32 == i->crc32 && e->event.image_request.size == i->size) { gg_image_reply(g->sess, e->event.image_request.sender, i->filename, i->data, i->size); image_remove_queue(i); break; } } break; } case GG_EVENT_IMAGE_REPLY: if (e->event.image_reply.image) { const char *image_basedir; char *image_file; FILE *fp; int i; /* 0th, get basedir */ image_basedir = gg_config_images_dir ? gg_config_images_dir : /* dir specified by config */ prepare_pathf("images"); /* (ekg_config)/images */ /* 1st, create directories.. */ if (mkdir_recursive(image_basedir, 1)) { print("gg_image_cant_open_file", image_basedir, strerror(errno)); return; } /* XXX, recode from cp1250 to locales [e->event.image_reply.filename] */ /* XXX, sanity path */ image_file = saprintf("%s/gg_%d_%.4x_%s", image_basedir, e->event.image_reply.sender, e->event.image_reply.crc32, e->event.image_reply.filename); debug("image from %d called %s\n", e->event.image_reply.sender, image_file); if (!(fp = fopen(image_file, "w"))) { print("gg_image_cant_open_file", image_file, strerror(errno)); xfree(image_file); return; } for (i = 0; ievent.image_reply.size; i++) { fputc(e->event.image_reply.image[i],fp); } fclose(fp); { char *uid = saprintf("gg:%d", e->event.image_reply.sender); print_info(uid, s, "gg_image_ok_get", image_file, uid, e->event.image_reply.filename); xfree(uid); } xfree(image_file); break; } else { /* XXX, display no image, from libgadu: */ /* pusta odpowied - klient po drugiej stronie nie ma danego obrazka */ } } } /* * gg_session_handler_userlist() * * support for userlist's events * */ static void gg_session_handler_userlist(session_t *s, struct gg_event *e) { switch (e->event.userlist.type) { case GG_USERLIST_GET_REPLY: print("userlist_get_ok", session_name(s)); if (e->event.userlist.reply) { char *reply; userlist_t *ul; gg_private_t *g = session_private_get(s); /* remove all contacts from notification list on server */ for (ul = s->userlist; ul; ul = ul->next) { userlist_t *u = ul; char *parsed; if (!u || !(parsed = xstrchr(u->uid, ':'))) continue; gg_remove_notify_ex(g->sess, str_to_uin(parsed + 1), gg_userlist_type(u)); } reply = gg_to_core(s, xstrdup(e->event.userlist.reply)); gg_userlist_set(s, reply); xfree(reply); gg_userlist_send(g->sess, s->userlist); config_changed = 1; } session_int_set(s, "__userlist_get_config", -1); break; case GG_USERLIST_PUT_REPLY: switch (session_int_get(s, "__userlist_put_config")) { case 0: print("userlist_put_ok", session_name(s)); break; case 1: print("userlist_config_put_ok", session_name(s)); break; case 2: print("userlist_clear_ok", session_name(s)); break; case 3: print("userlist_config_clear_ok", session_name(s)); break; default: debug_error("gg_session_handler_userlist() occur, but __userlist_put_config: %d\n", session_int_get(s, "__userlist_put_config")); } session_int_set(s, "__userlist_put_config", -1); break; } } /* * gg_session_handler() * * obsuga zdarze przy poczeniu gg. */ WATCHER_SESSION(gg_session_handler) { /* tymczasowe */ gg_private_t *g; struct gg_event *e; int broken = 0; if (type == 1) { /* tutaj powinnimy usun dane watcha. nie, dzikuj. */ return 0; } if (!s || !(g = s->priv) || !g->sess) { debug_error("gg_session_handler() called with NULL gg_session\n"); return -1; } if (type == 2) { if (g->sess->state != GG_STATE_CONNECTING_GG) { protocol_disconnected_emit(s, NULL, EKG_DISCONNECT_FAILURE); gg_free_session(g->sess); g->sess = NULL; return -1; } /* jeli jest GG_STATE_CONNECTING_GG to kaemy stwierdzi * bd (EINPROGRESS) i czy si z kolejnym kandydatem. */ } if (!(e = gg_watch_fd(g->sess))) { protocol_disconnected_emit(s, NULL, EKG_DISCONNECT_NETWORK); gg_free_session(g->sess); g->sess = NULL; return -1; } switch (e->type) { case GG_EVENT_NONE: break; case GG_EVENT_CONN_SUCCESS: gg_session_handler_success(s); break; case GG_EVENT_CONN_FAILED: gg_session_handler_failure(s, e); broken = 1; break; case GG_EVENT_DISCONNECT: gg_session_handler_disconnect(s); broken = 1; break; case GG_EVENT_NOTIFY: case GG_EVENT_NOTIFY_DESCR: { struct gg_notify_reply *n; n = (e->type == GG_EVENT_NOTIFY) ? e->event.notify : e->event.notify_descr.notify; for (; n->uin; n++) { char *descr = (e->type == GG_EVENT_NOTIFY_DESCR) ? e->event.notify_descr.descr : NULL; gg_session_handler_status(s, n->uin, n->status, descr, n->remote_ip, n->remote_port, n->version); } break; } case GG_EVENT_STATUS: gg_session_handler_status(s, e->event.status.uin, e->event.status.status, e->event.status.descr, 0, 0, 0); break; #ifdef GG_STATUS60 case GG_EVENT_STATUS60: gg_session_handler_status(s, e->event.status60.uin, e->event.status60.status, e->event.status60.descr, e->event.status60.remote_ip, e->event.status60.remote_port, e->event.status60.version); break; #endif #ifdef GG_NOTIFY_REPLY60 case GG_EVENT_NOTIFY60: { int i; for (i = 0; e->event.notify60[i].uin; i++) gg_session_handler_status(s, e->event.notify60[i].uin, e->event.notify60[i].status, e->event.notify60[i].descr, e->event.notify60[i].remote_ip, e->event.notify60[i].remote_port, e->event.notify60[i].version); break; } #endif case GG_EVENT_MSG: gg_session_handler_msg(s, e); break; case GG_EVENT_ACK: gg_session_handler_ack(s, e); break; case GG_EVENT_PUBDIR50_SEARCH_REPLY: gg_session_handler_search50(s, e); break; case GG_EVENT_PUBDIR50_WRITE: gg_session_handler_change50(s, e); break; case GG_EVENT_USERLIST: gg_session_handler_userlist(s, e); break; case GG_EVENT_IMAGE_REQUEST: case GG_EVENT_IMAGE_REPLY: gg_session_handler_image(s, e); break; #ifdef HAVE_GG_DCC7 case GG_EVENT_DCC7_NEW: { struct gg_dcc7 *dccdata = e->event.dcc7_new; char *uid; debug("GG_EVENT_DCC7_NEW\n"); if (!gg_config_dcc) { gg_dcc7_reject(dccdata, GG_DCC7_REJECT_USER); gg_dcc7_free(dccdata); e->event.dcc7_new = NULL; break; } #if 0 if (check_dcc_limit(e) == -1) break; #endif uid = saprintf("gg:%d", dccdata->peer_uin); switch (dccdata->dcc_type) { case GG_DCC7_TYPE_FILE: { struct stat st; char *path; dcc_t *d; d = dcc_add(s, uid, DCC_GET, dccdata); dcc_filename_set(d, dccdata->filename); /* XXX< sanityzuj, cp -> iso */ dcc_size_set(d, dccdata->size); print("dcc_get_offer", format_user(s, uid), d->filename, ekg_itoa(d->size), ekg_itoa(d->id)); if (config_dcc_dir) path = saprintf("%s/%s", config_dcc_dir, d->filename); else path = xstrdup(d->filename); if (!stat(path, &st) && st.st_size < d->size) print("dcc_get_offer_resume", format_user(s, uid), d->filename, ekg_itoa(d->size), ekg_itoa(d->id)); xfree(path); break; } case GG_DCC7_TYPE_VOICE: { dcc_t *d = dcc_add(s, uid, DCC_VOICE, dccdata); print("dcc_voice_offer", format_user(s, uid), ekg_itoa(d->id)); break; } default: debug_error("[DCC7_NEW] unknown type %d\n", dccdata->type); } xfree(uid); /* XXX, add timeouter */ /* dane watcha dostajemy w _INFO [czekamy na libgadu] */ break; } case GG_EVENT_DCC7_REJECT: { struct gg_dcc7 *dccdata = e->event.dcc7_accept.dcc7; dcc_t *dcc; debug("GG_EVENT_DCC7_REJECT\n"); if (!(dcc = gg_dcc_find(dccdata))) { debug_error("GG_EVENT_DCC7_REJECT [DCC NOT FOUND: %p]\n", dcc); break; } print("dcc_error_refused", format_user(dcc->session, dcc->uid)); /* XXX, close handler powinien byc ustawiony! */ gg_dcc7_free(dccdata); dcc_close(dcc); break; } case GG_EVENT_DCC7_ACCEPT: { struct gg_dcc7 *dccdata = e->event.dcc7_accept.dcc7; dcc_t *dcc; debug("GG_EVENT_DCC7_ACCEPT [%p]\n", dccdata); timer_remove_data(&gg_plugin, dccdata); if (!(dcc = gg_dcc_find(dccdata))) { debug_error("GG_EVENT_DCC7_ACCEPT [DCC NOT FOUND: %p]\n", dcc); break; } watch_add(&gg_plugin, dccdata->fd, dccdata->check, gg_dcc7_handler, dccdata); break; } #endif #ifdef GG_FEATURE_TYPING_NOTIFICATION case GG_EVENT_TYPING_NOTIFICATION: { char *uid = saprintf("gg:%d", e->event.typing_notification.uin); if (e->event.typing_notification.length == 0) protocol_xstate_emit(s, uid, 0, EKG_XSTATE_TYPING); else protocol_xstate_emit(s, uid, EKG_XSTATE_TYPING, 0); xfree(uid); break; } #endif default: debug("[gg] unhandled event 0x%.4x, consider upgrade\n", e->type); } if (!broken && g->sess->state != GG_STATE_IDLE && g->sess->state != GG_STATE_ERROR) { watch_t *w; if ((watch == g->sess->check) && g->sess->fd == fd) { if ((w = watch_find(&gg_plugin, fd, watch))) watch_timeout_set(w, g->sess->timeout); else debug("[gg] watches managment went to hell?\n"); gg_event_free(e); return 0; } w = watch_add_session(s, g->sess->fd, g->sess->check, gg_session_handler); watch_timeout_set(w, g->sess->timeout); } gg_event_free(e); return -1; } /** * gg_changed_private() * * When connected, notify gg server about our privacy policy [Do we or don't we want to notify users not in our userlist about our status/description
* Handler executed when session variable: "private" change. * * @param s - session * @param var - session variable name */ static void gg_changed_private(session_t *s, const char *var) { gg_private_t *g; int status; /* status for libgadu */ char *cpdescr; /* description in cp1250 for libgadu */ if (!s || !s->connected || !(g = s->priv)) return; cpdescr = locale_to_gg(s, xstrdup(s->descr)); status = gg_text_to_status(s->status, cpdescr); /* XXX, check if gg_text_to_status() return smth correct */ if (session_int_get(s, "private") > 0) status |= GG_STATUS_FRIENDS_MASK; if (cpdescr) gg_change_status_descr(g->sess, status, cpdescr); else gg_change_status(g->sess, status); xfree(cpdescr); } /** * changed_proxy() * * Handler execute when session variable: "proxy" change * * @bug BIG XXX, Mistake at art, it should use global config variable, not session ones, because it's used to inform libgadu about proxy servers.
* And libgadu has got this variables global, not session private. Maybe we somehow can update these variables before gg_login() by callng gg_changed_proxy() * but now it's BAD, BAD, BAD. */ static void gg_changed_proxy(session_t *s, const char *var) { char **auth, **userpass = NULL, **hostport = NULL; const char *gg_config_proxy; gg_proxy_port = 0; xfree(gg_proxy_host); gg_proxy_host = NULL; xfree(gg_proxy_username); gg_proxy_username = NULL; xfree(gg_proxy_password); gg_proxy_password = NULL; gg_proxy_enabled = 0; if (!(gg_config_proxy = session_get(s, var))) return; auth = array_make(gg_config_proxy, "@", 0, 0, 0); if (!auth[0] || !xstrcmp(auth[0], "")) { g_strfreev(auth); return; } gg_proxy_enabled = 1; if (auth[0] && auth[1]) { userpass = array_make(auth[0], ":", 0, 0, 0); hostport = array_make(auth[1], ":", 0, 0, 0); } else hostport = array_make(auth[0], ":", 0, 0, 0); if (userpass && userpass[0] && userpass[1]) { gg_proxy_username = xstrdup(userpass[0]); gg_proxy_password = xstrdup(userpass[1]); } gg_proxy_host = xstrdup(hostport[0]); gg_proxy_port = (hostport[1]) ? atoi(hostport[1]) : 8080; g_strfreev(hostport); g_strfreev(userpass); g_strfreev(auth); } static void gg_statusdescr_handler(session_t *s, const char *varname) { gg_private_t *g = session_private_get(s); char *cpdescr = locale_to_gg(s, xstrdup(session_descr_get(s))); int _status = GG_S(gg_text_to_status(session_status_get(s), cpdescr)); if (session_int_get(s, "private")) _status |= GG_STATUS_FRIENDS_MASK; if (cpdescr) gg_change_status_descr(g->sess, _status, cpdescr); else gg_change_status(g->sess, _status); xfree(cpdescr); } static QUERY(gg_typing_out) { #ifdef GG_FEATURE_TYPING_NOTIFICATION const char *session = *va_arg(ap, const char **); const char *uid = *va_arg(ap, const char **); const int chatstate = *va_arg(ap, const int *); session_t *s = session_find(session); gg_private_t *g; if (!gg_config_enable_chatstates) return -1; if (!s || s->plugin != &gg_plugin || !(g = s->priv) || !s->connected) return 0; gg_typing_notification(g->sess, atoi(uid+3), chatstate==EKG_CHATSTATE_COMPOSING ? 1 : 0); #endif return 0; } static int gg_theme_init() { #ifndef NO_DEFAULT_THEME format_add("gg_version", _("%> %TGadu-Gadu%n: libgadu %g%1%n (headers %c%2%n), protocol %g%3%n (%c0x%4%n)"), 1); /* /list */ format_add("gg_user_info_not_in_contacts", _("%K| %nDoesn't have us in roster\n"), 1); format_add("gg_user_info_firewalled", _("%K| %nFirewalled/NATed\n"), 1); format_add("gg_user_info_voip", _("%K| %nVoIP-capable\n"), 1); format_add("gg_user_info_version", _("%K| %nVersion: %T%1%n\n"),1); /* token things */ format_add("gg_token", _("%> Token was written to the file %T%1%n\n"), 1); format_add("gg_token_ocr", _("%> Token: %T%1%n\n"), 1); format_add("gg_token_body", "%1\n", 1); format_add("gg_token_failed", _("%! Error getting token: %1\n"), 1); format_add("gg_token_failed_saved", _("%! Error reading token: %1 (saved@%2)\n"), 1); format_add("gg_token_timeout", _("%! Token getting timeout\n"), 1); format_add("gg_token_unsupported", _("%! Your operating system doesn't support tokens\n"), 1); format_add("gg_token_missing", _("%! First get token by function %Ttoken%n\n"), 1); /* check_conn */ format_add("gg_user_is_connected", _("%> (%1) User %T%2%n is connected\n"), 1); format_add("gg_user_is_not_connected", _("%> (%1) User %T%2%n is not connected\n"), 1); format_add("gg_we_are_being_checked", _("%> (%1) We are being checked by %T%2%n\n"), 1); /* images */ format_add("gg_image_cant_open_file", _("%! Can't open file for image %1 (%2)\n"), 1); format_add("gg_image_error_send", _("%! Error sending image\n"), 1); format_add("gg_image_ok_send", _("%> Image sent properly\n"), 1); format_add("gg_image_ok_get", _("%> Image <%3> saved in %1\n"), 1); /* %1 - path, %2 - uid, %3 - name of picture */ #endif return 0; } static QUERY(gg_setvar_default) { xfree(gg_config_dcc_ip); xfree(gg_config_images_dir); xfree(gg_config_dcc_limit); gg_config_display_token = 1; gg_config_get_images = 0; gg_config_images_dir = NULL; gg_config_image_size = 20; gg_config_dcc = 0; gg_config_dcc_ip = NULL; gg_config_dcc_limit = xstrdup("30/30"); gg_config_dcc_port = 1550; return 0; } /** * libgadu_debug_handler() * * Handler for libgadu: gg_debug_handler
* It's communcation channel between libgadu debug messages, and ekg2.
* Here we translate libgadu levels to ekg2 one, and than pass it to ekg_debug_handler() * * @param level - libgadu debug level */ static void libgadu_debug_handler(int level, const char *format, va_list ap) { int newlevel; if (!config_debug) return; switch (level) { /* stale z libgadu.h */ /* case GG_DEBUG_NET: 1: newlevel = 0; break; */ /* never used ? */ case /* GG_DEBUG_TRAFFIC */ 2: newlevel = DEBUG_IO; break; case /* GG_DEBUG_DUMP */ 4: newlevel = DEBUG_IO; break; case /* GG_DEBUG_FUNCTION */ 8: newlevel = DEBUG_FUNCTION; break; case /* GG_DEBUG_MISC */ 16: newlevel = DEBUG_GGMISC; break; default: newlevel = 0; break; } ekg_debug_handler(newlevel, format, ap); } static TIMER(gg_scroll_timer) { session_t *sl; time_t t; if (type) return 0; t = time(NULL); /* sprawd scroll timeouty */ /* XXX: nie tworzy variabla globalnego! */ for (sl = sessions; sl; sl = sl->next) { session_t *s = sl; gg_private_t *g = s->priv; int tmp; if (!s->connected || s->plugin != &gg_plugin || !g) continue; if (!(tmp = session_int_get(s, "scroll_long_desc")) || tmp == -1) continue; if (t - g->scroll_last > tmp) command_exec(NULL, s, ("/_autoscroll"), 0); } return 0; } static plugins_params_t gg_plugin_vars[] = { PLUGIN_VAR_ADD("alias", VAR_STR, 0, 0, NULL), PLUGIN_VAR_ADD("auto_away", VAR_INT, "600", 0, NULL), PLUGIN_VAR_ADD("auto_away_descr", VAR_STR, 0, 0, NULL), PLUGIN_VAR_ADD("auto_back", VAR_INT, "0", 0, NULL), PLUGIN_VAR_ADD("auto_connect", VAR_BOOL, "0", 0, NULL), PLUGIN_VAR_ADD("auto_find", VAR_INT, "0", 0, NULL), PLUGIN_VAR_ADD("auto_reconnect", VAR_INT, "10", 0, NULL), PLUGIN_VAR_ADD("concat_multiline_status",VAR_INT, "3", 0, NULL), PLUGIN_VAR_ADD("connection_save", VAR_INT, "0", 0, NULL), PLUGIN_VAR_ADD("display_notify", VAR_INT, "-1", 0, NULL), PLUGIN_VAR_ADD("email", VAR_STR, 0, 0, NULL), PLUGIN_VAR_ADD("invisible_check_interval",VAR_INT, 0, 0, NULL), PLUGIN_VAR_ADD("local_ip", VAR_STR, 0, 0, NULL), PLUGIN_VAR_ADD("log_formats", VAR_STR, "xml,simple,sqlite", 0, NULL), PLUGIN_VAR_ADD("password", VAR_STR, NULL, 1, NULL), PLUGIN_VAR_ADD("port", VAR_INT, "8074", 0, NULL), PLUGIN_VAR_ADD("private", VAR_BOOL, "0", 0, gg_changed_private), PLUGIN_VAR_ADD("protocol", VAR_INT, "0", 0, NULL), PLUGIN_VAR_ADD("proxy", VAR_STR, NULL, 0, gg_changed_proxy), PLUGIN_VAR_ADD("proxy_forwarding", VAR_STR, NULL, 0, NULL), PLUGIN_VAR_ADD("scroll_long_desc", VAR_INT, "0", 0, NULL), PLUGIN_VAR_ADD("scroll_mode", VAR_STR, "bounce", 0, NULL), PLUGIN_VAR_ADD("server", VAR_STR, NULL, 0, NULL), PLUGIN_VAR_ADD("statusdescr", VAR_STR, NULL, 0, gg_statusdescr_handler), PLUGIN_VAR_END() }; static const char *gg_protocols[] = { "gg:", NULL }; static const status_t gg_statuses[] = { EKG_STATUS_NA, #ifdef GG_FEATURE_DND_FFC EKG_STATUS_DND, #endif EKG_STATUS_AWAY, EKG_STATUS_AVAIL, #ifdef GG_FEATURE_DND_FFC EKG_STATUS_FFC, #endif EKG_STATUS_BLOCKED, EKG_STATUS_INVISIBLE, EKG_STATUS_NULL }; static const struct protocol_plugin_priv gg_priv = { .protocols = gg_protocols, .statuses = gg_statuses }; int EXPORT gg_plugin_init(int prio) { va_list dummy; PLUGIN_CHECK_VER("gg"); gg_plugin.params = gg_plugin_vars; gg_plugin.priv = &gg_priv; plugin_register(&gg_plugin, prio); ekg_recode_utf8_inc(); ekg_recode_cp_inc(); gg_setvar_default(NULL, dummy); query_connect(&gg_plugin, "set-vars-default", gg_setvar_default, NULL); query_connect(&gg_plugin, "protocol-validate-uid", gg_validate_uid, NULL); query_connect(&gg_plugin, "plugin-print-version", gg_print_version, NULL); query_connect(&gg_plugin, "session-added", gg_session_init, NULL); query_connect(&gg_plugin, "session-removed", gg_session_deinit, NULL); query_connect(&gg_plugin, "add-notify", gg_add_notify_handle, NULL); query_connect(&gg_plugin, "remove-notify", gg_remove_notify_handle, NULL); query_connect(&gg_plugin, "status-show", gg_status_show_handle, NULL); query_connect(&gg_plugin, "user-offline", gg_user_offline_handle, NULL); query_connect(&gg_plugin, "user-online", gg_user_online_handle, NULL); query_connect(&gg_plugin, "protocol-unignore", gg_user_online_handle, (void *)1); query_connect(&gg_plugin, "userlist-info", gg_userlist_info_handle, NULL); query_connect(&gg_plugin, "userlist-privhandle", gg_userlist_priv_handler, NULL); query_connect(&gg_plugin, "protocol-typing-out", gg_typing_out, NULL); gg_register_commands(); variable_add(&gg_plugin, ("audio"), VAR_BOOL, 1, &gg_config_audio, gg_changed_dcc, NULL, NULL); variable_add(&gg_plugin, ("display_token"), VAR_BOOL, 1, &gg_config_display_token, NULL, NULL, NULL); variable_add(&gg_plugin, ("dcc"), VAR_BOOL, 1, &gg_config_dcc, gg_changed_dcc, NULL, NULL); variable_add(&gg_plugin, ("dcc_ip"), VAR_STR, 1, &gg_config_dcc_ip, gg_changed_dcc, NULL, NULL); variable_add(&gg_plugin, ("dcc_limit"), VAR_STR, 1, &gg_config_dcc_limit, NULL, NULL, NULL); variable_add(&gg_plugin, ("dcc_port"), VAR_INT, 1, &gg_config_dcc_port, gg_changed_dcc, NULL, NULL); variable_add(&gg_plugin, ("get_images"), VAR_BOOL, 1, &gg_config_get_images, NULL, NULL, NULL); variable_add(&gg_plugin, ("images_dir"), VAR_STR, 1, &gg_config_images_dir, NULL, NULL, NULL); variable_add(&gg_plugin, ("image_size"), VAR_INT, 1, &gg_config_image_size, gg_changed_images, NULL, NULL); variable_add(&gg_plugin, ("skip_default_format"), VAR_BOOL, 1, &gg_config_skip_default_format, NULL, NULL, NULL); variable_add(&gg_plugin, ("split_messages"), VAR_BOOL, 1, &gg_config_split_messages, NULL, NULL, NULL); variable_add(&gg_plugin, ("enable_chatstates"), VAR_BOOL, 1, &gg_config_enable_chatstates, NULL, NULL, NULL); timer_add(&gg_plugin, "gg-scroller", 1, 1, gg_scroll_timer, NULL); gg_debug_handler = libgadu_debug_handler; gg_debug_level = 255; return 0; } static int gg_plugin_destroy() { list_t l; list_destroy(gg_currently_checked, 1); for (l = gg_reminds; l; l = l->next) { struct gg_http *h = l->data; watch_remove(&gg_plugin, h->fd, h->check); gg_pubdir_free(h); } for (l = gg_registers; l; l = l->next) { struct gg_http *h = l->data; watch_remove(&gg_plugin, h->fd, h->check); gg_pubdir_free(h); } for (l = gg_unregisters; l; l = l->next) { struct gg_http *h = l->data; watch_remove(&gg_plugin, h->fd, h->check); gg_pubdir_free(h); } xfree(gg_register_password); gg_register_password = NULL; xfree(gg_register_email); gg_register_email = NULL; image_flush_queue(); ekg_recode_utf8_dec(); ekg_recode_cp_dec(); plugin_unregister(&gg_plugin); return 0; } /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: * vim: noet */ ekg2-0.4~pre+20120506.1/plugins/gg/gg.h000066400000000000000000000045631175142753400170110ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2003 Wojtek Kaniewski * 2004 - 2006 Adam Mikuta * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __EKG_GG_GG_H #define __EKG_GG_GG_H #include COMMAND(gg_command_image); /* images.c */ extern plugin_t gg_plugin; extern char *last_tokenid; /* variables */ extern int gg_config_display_token; extern int gg_config_audio; extern int gg_config_dcc; extern char *gg_config_dcc_ip; extern char *gg_config_dcc_limit; extern int gg_config_dcc_port; extern int gg_config_get_images; extern char *gg_config_images_dir; extern int gg_config_image_size; extern int gg_config_split_messages; typedef enum { GG_QUIET_CHANGE = 0x0001 } gg_quiet_t; typedef struct { struct gg_session *sess; /* sesja */ list_t searches; /* operacje szukania */ list_t passwds; /* operacje zmiany hasa */ gg_quiet_t quiet; /* co ma by cicho */ int curr_prtcl_ver; /* current protocol version */ /* (annoying) description scrolling */ unsigned int scroll_op : 1; int scroll_pos; time_t scroll_last; } gg_private_t; void gg_register_commands(); WATCHER_SESSION(gg_session_handler); typedef struct { char *uid; session_t *session; } gg_currently_checked_t; extern list_t gg_currently_checked; /** * gg_userlist_private_t * * Here we keep all userlist things, which are private to GG protocol, and because of this were removed from core userlist_t. */ typedef struct { int protocol; /**< Protocol version */ } gg_userlist_private_t; #define gg_userlist_priv_get(u) ((gg_userlist_private_t *) userlist_private_get(&gg_plugin, u)) #endif /* __EKG_GG_GG_H */ /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/plugins/gg/images.c000066400000000000000000000067331175142753400176550ustar00rootroot00000000000000/* $id */ /* * (C) Copyright 2004 Piotr Kupisiewicz * 2006 Adam Mikuta * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #include #include #include #include #include #include #include #include #include #include "images.h" #include "gg.h" list_t images = NULL; int gg_config_image_size; int gg_config_get_images; char *gg_config_images_dir; static image_t *image_add_queue(const char *filename, char *data, guint32 size, guint32 crc32); /* * gg_changed_images() * * called when some images_* variables are changed */ void gg_changed_images(const char *var) { if (gg_config_image_size > 255) { gg_config_image_size = 255; } else if (gg_config_image_size < 20) gg_config_image_size = 20; if (!in_autoexec) print("config_must_reconnect"); } COMMAND(gg_command_image) { gg_private_t *g = session_private_get(session); FILE *f; guint32 size, crc32; int i; const char *filename = params[1]; char *data; const char *uid; struct gg_msg_richtext_format_img { struct gg_msg_richtext rt; struct gg_msg_richtext_format f; struct gg_msg_richtext_image image; } msg; if (!(uid = get_uid(session, params[0]))) { printq("user_not_found", params[0]); return -1; } if (!(f = fopen(filename, "r"))) { printq("file_doesnt_exist", filename); return -1; } /* finding size of file by seeking to the end and then checking where we are */ fseek(f, 0, SEEK_END); size = ftell(f); fseek(f, 0, SEEK_SET); data = xmalloc(size); for (i = 0; !feof(f); i++) { data[i] = fgetc(f); } fclose(f); crc32 = gg_crc32(0, (unsigned char *) data, size); msg.rt.flag=2; msg.rt.length=13; msg.f.position=0; msg.f.font=GG_FONT_IMAGE; msg.image.unknown1=0x0109; msg.image.size=size; msg.image.crc32=crc32; image_add_queue(filename, data, size, crc32); if (gg_send_message_richtext(g->sess, GG_CLASS_MSG, atoi(uid + 3), (const unsigned char *) "", (const unsigned char *) &msg, sizeof(msg)) == -1) { printq("gg_image_error_send"); return -1; } printq("gg_image_ok_send"); return 0; } /* * image_add_queue() * * data should be given as already allocated pointer */ static image_t *image_add_queue(const char *filename, char *data, guint32 size, guint32 crc32) { image_t *i = xmalloc(sizeof(image_t)); i->filename = xstrdup(filename); i->data = data; i->size = size; i->crc32 = crc32; return list_add(&images, i); } void image_remove_queue(image_t *i) { debug("image_remove_queue( %d)\n", i->size); xfree(i->filename); xfree(i->data); list_remove(&images, i, 1); } void image_flush_queue() { list_t l; if (!images) return; for (l = images; l; l = l->next) { image_t *i = l->data; xfree(i->filename); xfree(i->data); } list_destroy(images, 1); images = NULL; } ekg2-0.4~pre+20120506.1/plugins/gg/images.h000066400000000000000000000022611175142753400176520ustar00rootroot00000000000000/* * (C) Copyright 2004 Piotr Kupisiewicz * 2006 Adam Mikuta * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __EKG_GG_IMAGES_H #define __EKG_GG_IMAGES_H typedef struct { char *filename; char *data; guint32 size; guint32 crc32; } image_t; extern list_t images; #define GG_CRC32_INVISIBLE 99 void gg_changed_images(const char *var); void image_remove_queue(image_t *i); void image_flush_queue(); #endif /* __EKG_GG_IMAGES_H */ /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/plugins/gg/misc.c000066400000000000000000000146201175142753400173350ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2003 Wojtek Kaniewski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #include #include #include #include "gg.h" #include "misc.h" /* * gg_status_to_text() * * zamienia stan GG na enum ekg2 (dawniej: tekst, stad nazwa). * * - status */ int gg_status_to_text(int status) { switch (GG_S(status)) { case GG_STATUS_AVAIL: case GG_STATUS_AVAIL_DESCR: return EKG_STATUS_AVAIL; case GG_STATUS_NOT_AVAIL: case GG_STATUS_NOT_AVAIL_DESCR: return EKG_STATUS_NA; case GG_STATUS_BUSY: case GG_STATUS_BUSY_DESCR: return EKG_STATUS_AWAY; case GG_STATUS_INVISIBLE: case GG_STATUS_INVISIBLE_DESCR: return EKG_STATUS_INVISIBLE; #ifdef GG_FEATURE_DND_FFC case GG_STATUS_DND: case GG_STATUS_DND_DESCR: return EKG_STATUS_DND; case GG_STATUS_FFC: case GG_STATUS_FFC_DESCR: return EKG_STATUS_FFC; #endif case GG_STATUS_BLOCKED: return EKG_STATUS_BLOCKED; } return EKG_STATUS_UNKNOWN; } /* * gg_text_to_status() * * zamienia stan enumowy (dawniej: tekstowy, stad nazwa) ekg2 na stan liczbowy ekg. * * - status * - descr */ int gg_text_to_status(const int status, const char *descr) { #define GG_TTS(x, y) if (status == EKG_STATUS_##x) return (descr ? GG_STATUS_##y##_DESCR : GG_STATUS_##y); GG_TTS(NA, NOT_AVAIL) GG_TTS(AVAIL, AVAIL) GG_TTS(AWAY, BUSY) /* I think we don't need to care about Jabber aways */ GG_TTS(INVISIBLE, INVISIBLE) #ifdef GG_FEATURE_DND_FFC GG_TTS(DND, DND) GG_TTS(FFC, FFC) #endif #undef GG_TTS if (status == EKG_STATUS_BLOCKED) return GG_STATUS_BLOCKED; return GG_STATUS_NOT_AVAIL; } /* * gg_userlist_type() * * zwraca typ uytkownika. */ char gg_userlist_type(userlist_t *u) { if (u && ekg_group_member(u, "__blocked")) return GG_USER_BLOCKED; if (u && ekg_group_member(u, "__offline")) return GG_USER_OFFLINE; return GG_USER_NORMAL; } /* * gg_blocked_remove() * * usuwa z listy blokowanych numerkw. * * - uid */ int gg_blocked_remove(session_t *s, const char *uid) { userlist_t *u = userlist_find(s, uid); gg_private_t *g = session_private_get(s); if (!u || !s || !g) return -1; if (!ekg_group_member(u, "__blocked")) return -1; if (g->sess && g->sess->state == GG_STATE_CONNECTED) gg_remove_notify_ex(g->sess, atoi(u->uid + 3), gg_userlist_type(u)); ekg_group_remove(u, "__blocked"); if (!u->nickname && !u->groups) userlist_remove(s, u); else { if (g->sess && g->sess->state == GG_STATE_CONNECTED) gg_add_notify_ex(g->sess, atoi(u->uid + 3), gg_userlist_type(u)); } return 0; } /* * gg_blocked_add() * * dopisuje do listy blokowanych numerkw. * * - uid */ int gg_blocked_add(session_t *s, const char *uid) { userlist_t *u = userlist_find(s, uid); gg_private_t *g = session_private_get(s); if (!s || !g) return -1; if (u && ekg_group_member(u, "__blocked")) return -1; if (!u) u = userlist_add(s, uid, NULL); else { if (g->sess && g->sess->state == GG_STATE_CONNECTED) gg_remove_notify_ex(g->sess, atoi(u->uid + 3), gg_userlist_type(u)); } ekg_group_add(u, "__blocked"); if (g->sess && g->sess->state == GG_STATE_CONNECTED) gg_add_notify_ex(g->sess, atoi(u->uid + 3), gg_userlist_type(u)); return 0; } /* * gg_http_error_string() * * zwraca tekst opisujcy powd bdu usug po http. * * - h - warto gg_http->error lub 0, gdy funkcja zwrcia NULL. */ const char *gg_http_error_string(int h) { switch (h) { case 0: return format_find((errno == ENOMEM) ? "http_failed_memory" : "http_failed_connecting"); case GG_ERROR_RESOLVING: return format_find("http_failed_resolving"); case GG_ERROR_CONNECTING: return format_find("http_failed_connecting"); case GG_ERROR_READING: return format_find("http_failed_reading"); case GG_ERROR_WRITING: return format_find("http_failed_writing"); default: return "?"; } } /** * gg_userlist_send() * * Send to server our user list. * * @param s - gg_session [it's not session_t *] * @param userlist - user list. * * @todo XXX, think about args here, maybe let's pass only session_t ? * * @return result of gg_notify_ex() */ int gg_userlist_send(struct gg_session *s, userlist_t *userlist) { int i, res; int count = LIST_COUNT2(userlist); userlist_t *ul; char *types; uin_t *uins; if (!count) return gg_notify(s, NULL, 0); uins = xmalloc(count * sizeof(uin_t)); types = xmalloc(count * sizeof(char)); for (ul = userlist, i = 0; ul; ul = ul->next, i++) { userlist_t *u = ul; uins[i] = atoi(u->uid + 3); types[i] = gg_userlist_type(u); } res = gg_notify_ex(s, uins, types, count); xfree(uins); xfree(types); return res; } /* * * recode * * starting from protocol version 0x2e, GG handles unicode * */ char *gg_to_core(session_t *s, char *txt) { gg_private_t *g = session_private_get(s); return (g->curr_prtcl_ver >= 0x2e) ? ekg_utf8_to_core(txt) : ekg_cp_to_core(txt); } char *gg_to_core_dup(session_t *s, const char *txt) { gg_private_t *g = session_private_get(s); return (g->curr_prtcl_ver >= 0x2e) ? ekg_utf8_to_core_dup(txt) : ekg_cp_to_core_dup(txt); } char *locale_to_gg(session_t *s, char *txt) { gg_private_t *g = session_private_get(s); return (g->curr_prtcl_ver >= 0x2e) ? ekg_locale_to_utf8(txt) : ekg_locale_to_cp(txt); } char *locale_to_gg_dup(session_t *s, const char *txt) { gg_private_t *g = session_private_get(s); return (g->curr_prtcl_ver >= 0x2e) ? ekg_locale_to_utf8_dup(txt) : ekg_locale_to_cp_dup(txt); } const char *locale_to_gg_use(session_t *s, const char *txt) { gg_private_t *g = session_private_get(s); return (g->curr_prtcl_ver >= 0x2e) ? ekg_locale_to_utf8_use(txt) : ekg_locale_to_cp_use(txt); } /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/plugins/gg/misc.h000066400000000000000000000030161175142753400173370ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2003 Wojtek Kaniewski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __EKG_GG_MISC_H #define __EKG_GG_MISC_H int gg_status_to_text(const int status); int gg_text_to_status(const int status, const char *descr); char gg_userlist_type(userlist_t *u); int gg_blocked_add(session_t *s, const char *uid); int gg_blocked_remove(session_t *s, const char *uid); const char *gg_http_error_string(int h); int gg_userlist_send(struct gg_session *s, userlist_t *userlist); char *gg_to_core(session_t *s, char *txt); char *gg_to_core_dup(session_t *s, const char *txt); char *locale_to_gg(session_t *s, char *txt); char *locale_to_gg_dup(session_t *s, const char *txt); const char *locale_to_gg_use(session_t *s, const char *txt); #endif /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/plugins/gg/pubdir.c000066400000000000000000000336701175142753400176750ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2003 Wojtek Kaniewski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #include #include #include #include "gg.h" #include "misc.h" list_t gg_reminds = NULL; list_t gg_registers = NULL; list_t gg_unregisters = NULL; int gg_register_done = 0; char *gg_register_password = NULL; char *gg_register_email = NULL; static WATCHER(gg_handle_register) /* tymczasowy */ { struct gg_http *h = data; struct gg_pubdir *p; session_t *s; char *tmp; if (type == 2) { debug("[gg] gg_handle_register() timeout\n"); print("register_timeout"); goto fail; } if (type) return -1; if (!h) { debug("[gg] gg_handle_register() called with NULL data\n"); return -1; } if (gg_pubdir_watch_fd(h) || h->state == GG_STATE_ERROR) { print("register_failed", gg_http_error_string(h->error)); goto fail; } if (h->state != GG_STATE_DONE) { watch_t *w; if (watch == h->check && h->fd == fd) { if ((w = watch_find(&gg_plugin, fd, watch))) watch_timeout_set(w, h->timeout); else debug("[gg] watches managment went to hell?\n"); return 0; } w = watch_add(&gg_plugin, h->fd, h->check, gg_handle_register, h); watch_timeout_set(w, h->timeout); return -1; } if (!(p = h->data) || !p->success) { print("register_failed", gg_http_error_string(0)); goto fail; } print("register", ekg_itoa(p->uin)); gg_register_done = 1; tmp = saprintf("gg:%d", p->uin); s = session_add(tmp); xfree(tmp); session_set(s, "password", gg_register_password); xfree(gg_register_password); gg_register_password = NULL; session_set(s, "email", gg_register_email); xfree(gg_register_email); gg_register_email = NULL; fail: list_remove(&gg_registers, h, 0); gg_free_pubdir(h); return -1; } COMMAND(gg_command_register) { struct gg_http *h; char *passwd, *passwd_b; watch_t *w; if (gg_register_done) { printq("registered_today"); return -1; } if (!params[0] || !params[1]) { printq("not_enough_params", name); return -1; } if (gg_registers) { printq("register_pending"); return -1; } if (!last_tokenid) { printq("gg_token_missing"); return -1; } if (params[2]) { passwd_b = xstrdup(params[1]); params[1] = params[2]; params[2] = NULL; } else if (!(passwd_b = password_input(NULL, NULL, 0))) return -1; passwd = ekg_locale_to_cp_dup(passwd_b); if (!(h = gg_register3(params[0], passwd, last_tokenid, params[1], 1))) { xfree(passwd); xfree(passwd_b); printq("register_failed", strerror(errno)); return -1; } xfree(last_tokenid); last_tokenid = NULL; xfree(passwd); w = watch_add(&gg_plugin, h->fd, h->check, gg_handle_register, h); watch_timeout_set(w, h->timeout); list_add(&gg_registers, h); gg_register_email = xstrdup(params[0]); gg_register_password = passwd_b; return 0; } /* zwalnianie w type == 1 */ static WATCHER(gg_handle_unregister) /* tymczasowy */ { struct gg_http *h = data; struct gg_pubdir *s; if (type == 2) { debug("[gg] gg_handle_unregister() timeout\n"); print("unregister_timeout"); goto fail; } if (type) return 0; if (!h) { debug("[gg] gg_handle_unregister() called with NULL data\n"); return -1; } if (gg_pubdir_watch_fd(h) || h->state == GG_STATE_ERROR) { print("unregister_failed", gg_http_error_string(h->error)); goto fail; } if (h->state != GG_STATE_DONE) { watch_t *w = watch_add(&gg_plugin, h->fd, h->check, gg_handle_unregister, h); watch_timeout_set(w, h->timeout); return -1; } if (!(s = h->data) || !s->success) { print("unregister_failed", gg_http_error_string(0)); goto fail; } print("unregister", ekg_itoa(s->uin)); fail: list_remove(&gg_unregisters, h, 0); gg_free_pubdir(h); return -1; } COMMAND(gg_command_unregister) { struct gg_http *h; watch_t *w; uin_t uin; char *passwd; if (!last_tokenid) { printq("token_missing"); return -1; } if (!xstrncasecmp(params[0], "gg:", 3)) uin = atoi(params[0] + 3); else uin = atoi(params[0]); if (uin < 0) { printq("unregister_bad_uin", params[0]); return -1; } passwd = ekg_locale_to_cp_dup(params[1]); if (!(h = gg_unregister3(uin, passwd, last_tokenid, params[2], 1))) { printq("unregister_failed", strerror(errno)); xfree(passwd); return -1; } xfree(last_tokenid); last_tokenid = NULL; xfree(passwd); w = watch_add(&gg_plugin, h->fd, h->check, gg_handle_unregister, h); watch_timeout_set(w, h->timeout); list_add(&gg_unregisters, h); return 0; } /** * gg_handle_passwd() * * Watch used to change password
* We send asynchrous request for change password @@ gg_command_passwd() If it success, than this watch is created.
* Here we wait for server reply, and if it success we set new password in session variable.
* * @todo Need rewriting, it's buggy. We don't free memory @ type == 1, when watch wasn't executed... * We always create watch @@ not GG_STATE_DONE, even if there's no need, and maybe more. XXX * * @return -1 [TEMPORARY WATCH] */ static WATCHER(gg_handle_passwd) { struct gg_http *h = data; struct gg_pubdir *p = NULL; session_t *s; if (type == 2) { debug("[gg] gg_handle_passwd() timeout\n"); print("passwd_timeout"); goto fail; } if (type) return 0; if (!h) { debug("[gg] gg_handle_passwd() called with NULL data\n"); return -1; } if (gg_pubdir_watch_fd(h) || h->state == GG_STATE_ERROR) { print("passwd_failed", gg_http_error_string(h->error)); goto fail; } if (h->state != GG_STATE_DONE) { watch_t *w; w = watch_add(&gg_plugin, h->fd, h->check, gg_handle_passwd, h); watch_timeout_set(w, h->timeout); return -1; } if (!(p = h->data) || !p->success) { print("passwd_failed", gg_http_error_string(0)); goto fail; } print("passwd"); fail: for (s = sessions; s; s = s->next) { gg_private_t *g; list_t m; if (!s || !(g = s->priv) || s->plugin != &gg_plugin) continue; for (m = g->passwds; m; ) { struct gg_http *sh = m->data; m = m->next; if (sh != h) continue; if (p && p->success) { const char *new_passwd = session_get(s, "__new_password"); session_set(s, "password", new_passwd); } session_set(s, "__new_password", NULL); list_remove(&g->passwds, h, 0); gg_free_pubdir(h); } } return -1; } /** * gg_command_passwd() * * It's used to change password on gg server.
* Handler for: /gg:passwd command. * * @param params [0] - New password * @param params [1] - Text from token [only needed when HAVE_GG_CHANGE_PASSWD4 is defined] * * @todo Add support for really old libgadu functions? like: gg_change_passwd2() ? * * @return 0 on sucess, else -1 */ COMMAND(gg_command_passwd) { gg_private_t *g = session_private_get(session); struct gg_http *h; watch_t *w; char *oldpasswd, *newpasswd; #ifdef HAVE_GG_CHANGE_PASSWD4 /* gg_change_passwd4 since ~ LIBGADU 20030930 */ const char *config_email = session_get(session, "email"); if (!config_email) { printq("var_not_set", name, "/session email"); return -1; } if (!last_tokenid) { printq("gg_token_missing"); return -1; } if (!params[0]) { printq("not_enough_params", name); return -1; } if (!params[1]) { #else if (!params[0]) { #endif newpasswd = ekg_locale_to_cp(password_input(NULL, NULL, 0)); if (!newpasswd) return -1; } else newpasswd = ekg_locale_to_cp_dup(params[0]); oldpasswd = ekg_locale_to_cp_dup(session_get(session, "password")); #ifdef HAVE_GG_CHANGE_PASSWD4 if (!(h = gg_change_passwd4(atoi(session->uid + 3), config_email, (oldpasswd) ? oldpasswd : "", newpasswd, last_tokenid, params[1] ? params[1] : params[0], 1))) #else if (!(h = gg_change_passwd3(atoi(session->uid + 3), (oldpasswd) ? oldpasswd : "", newpasswd, "", 1))) #endif { xfree(newpasswd); xfree(oldpasswd); printq("passwd_failed", strerror(errno)); return -1; } #ifdef HAVE_GG_CHANGE_PASSWD4 xfree(last_tokenid); last_tokenid = NULL; #endif session_set(session, "__new_password", params[0]); w = watch_add(&gg_plugin, h->fd, h->check, gg_handle_passwd, h); watch_timeout_set(w, h->timeout); list_add(&g->passwds, h); xfree(newpasswd); xfree(oldpasswd); return 0; } static WATCHER(gg_handle_remind) /* tymczasowy */ { struct gg_http *h = data; struct gg_pubdir *s; if (type == 2) { debug("[gg] gg_handle_remind() timeout\n"); print("remind_timeout"); goto fail; } if (type) return 0; if (!h) { debug("[gg] gg_handle_remind() called with NULL data\n"); return -1; } if (gg_pubdir_watch_fd(h) || h->state == GG_STATE_ERROR) { print("remind_failed", gg_http_error_string(h->error)); goto fail; } if (h->state != GG_STATE_DONE) { watch_t *w = watch_add(&gg_plugin, h->fd, h->check, gg_handle_remind, h); watch_timeout_set(w, h->timeout); return -1; } if (!(s = h->data) || !s->success) { print("remind_failed", gg_http_error_string(0)); goto fail; } print("remind"); fail: list_remove(&gg_reminds, h, 0); gg_free_pubdir(h); return -1; } COMMAND(gg_command_remind) { gg_private_t *g = session_private_get(session); struct gg_http *h; watch_t *w; uin_t uin = 0; #ifdef HAVE_GG_REMIND_PASSWD3 const char *config_email; const char *token_eval; #endif #ifdef HAVE_GG_REMIND_PASSWD3 if (params[0] && params[1]) #else if (params[0]) #endif uin = atoi(params[0]); else { if (!uin && (!session || !g || xstrncasecmp(session_uid_get(session), "gg:", 3))) { if (!params[0]) printq("invalid_session"); return -1; } uin = atoi(session_uid_get(session) + 3); } if (!uin) { printq("invalid_uid"); return -1; } #ifdef HAVE_GG_REMIND_PASSWD3 /* since LIBGADU ~20050217 */ if (!(config_email = session_get(session, "email"))) { printq("var_not_set", name, "/session email"); return -1; } if (!last_tokenid) { printq("gg_token_missing"); return -1; } token_eval = (params[0] && params[1]) ? params[1] : params[0]; if (!token_eval) { printq("not_enough_params", name); return -1; } if (!(h = gg_remind_passwd3(uin, config_email, last_tokenid, token_eval, 1))) #else if (!(h = gg_remind_passwd(uin, 1))) #endif { printq("remind_failed", strerror(errno)); return -1; } #ifdef HAVE_GG_REMIND_PASSWD3 xfree(last_tokenid); last_tokenid = NULL; #endif w = watch_add(&gg_plugin, h->fd, h->check, gg_handle_remind, h); watch_timeout_set(w, h->timeout); list_add(&gg_reminds, h); return 0; } int gg_userlist_set(session_t *session, const char *contacts) { char **entries; int i; if (!session) return -1; entries = array_make(contacts, "\r\n", 0, 1, 0); userlist_free(session); for (i = 0; entries[i]; i++) userlist_add_entry(session, entries[i]); g_strfreev(entries); query_emit(NULL, "userlist-refresh"); return 0; } /* * gg_userlist_dump() * * zapisuje listę kontaktów w postaci tekstowej. * * zwraca zaalokowany bufor, który należy zwolnić. */ static char *gg_userlist_dump(session_t *session) { string_t s; userlist_t *ul; s = string_init(NULL); for (ul = session->userlist; ul; ul = ul->next) { userlist_t *u = ul; char *groups; const char *__first_name = user_private_item_get(u, "first_name"); const char *__last_name = user_private_item_get(u, "last_name"); const char *__mobile = user_private_item_get(u, "mobile"); groups = group_to_string(u->groups, 1, 0); string_append_format(s, "%s;%s;%s;%s;%s;%s;%s%s\r\n", __first_name ? __first_name : "", __last_name ? __last_name : "", (u->nickname) ? u->nickname : "", (u->nickname) ? u->nickname : "", __mobile ? __mobile : "", groups, u->uid + 3 /* skip gg: */, (u->foreign) ? u->foreign : ""); xfree(groups); } return string_free(s, 0); } COMMAND(gg_command_list) { gg_private_t *g = session_private_get(session); /* list --get */ if (params[0] && match_arg(params[0], 'g', ("get"), 2)) { #if 0 if (session_int_get(session, "__userlist_get_config") != -1) { printq("generic_error", "Another import userlist operation in progress... Please wait."); return -1; } #endif if (gg_userlist_request(g->sess, GG_USERLIST_GET, NULL) == -1) { printq("userlist_get_error", strerror(errno)); return -1; } session_int_set(session, "__userlist_get_config", 0); return 0; } /* list --clear */ if (params[0] && match_arg(params[0], 'c', ("clear"), 2)) { #if 0 if (session_int_get(session, "__userlist_put_config") != -1) { printq("generic_error", "Another export/clear userlist operation in progress... Please wait."); return -1; } #endif if (gg_userlist_request(g->sess, GG_USERLIST_PUT, NULL) == -1) { printq("userlist_clear_error", strerror(errno)); return -1; } session_int_set(session, "__userlist_put_config", 2); return 0; } /* list --put */ if (params[0] && (match_arg(params[0], 'p', ("put"), 2))) { char *contacts; char *cpcontacts; #if 0 if (session_int_get(session, "__userlist_put_config") != -1) { printq("generic_error", "Another export/clear userlist operation in progress... Please wait."); return -1; } #endif contacts = gg_userlist_dump(session); cpcontacts = ekg_locale_to_cp(contacts); if (gg_userlist_request(g->sess, GG_USERLIST_PUT, cpcontacts) == -1) { printq("userlist_put_error", strerror(errno)); xfree(cpcontacts); return -1; } session_int_set(session, "__userlist_put_config", 0); xfree(cpcontacts); return 0; } return cmd_list(name, params, session, target, quiet); } /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/plugins/gg/pubdir.h000066400000000000000000000024701175142753400176740ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2003 Wojtek Kaniewski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __EKG_GG_PUBDIR_H #define __EKG_GG_PUBDIR_H extern list_t gg_registers; extern list_t gg_unregisters; extern list_t gg_reminds; extern int gg_register_done; extern char *gg_register_password; extern char *gg_register_email; COMMAND(gg_command_register); COMMAND(gg_command_unregister); COMMAND(gg_command_passwd); COMMAND(gg_command_remind); COMMAND(gg_command_list); int gg_userlist_set(session_t *session, const char *contacts); #endif /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/plugins/gg/pubdir50.c000066400000000000000000000251001175142753400200270ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2003 Wojtek Kaniewski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #include #include "gg.h" #include "misc.h" COMMAND(gg_command_find) { gg_private_t *g = session_private_get(session); char **argv = NULL; char **uargv = NULL; gg_pubdir50_t req; int i, res = 0, all = 0; if (!g->sess || g->sess->state != GG_STATE_CONNECTED) { printq("not_connected", session_name(session)); return -1; } if (params[0] && match_arg(params[0], 'S', ("stop"), 3)) { list_t l; for (l = g->searches; l; ) { gg_pubdir50_t s = l->data; l = l->next; gg_pubdir50_free(s); list_remove(&g->searches, s, 0); } printq("search_stopped"); return 0; } argv = (char **) params; if (target[0] == '#' && (!argv[0] || !argv[1])) { return command_exec_format(target, session, quiet, ("/conference --find %s"), target); } if (!(req = gg_pubdir50_new(GG_PUBDIR50_SEARCH))) { return -1; } if (target[0] != '-' || !params[0]) { /* if window_current->target is even --blah use it. it's quite stupid hovewer. */ const char *uid = get_uid(session, target); if (!uid) { printq("user_not_found", target); return -1; } if (xstrncasecmp(uid, "gg:", 3)) { printq("generic_error", ("Tylko GG")); return -1; } gg_pubdir50_add(req, GG_PUBDIR50_UIN, uid + 3); if (!params[0]) goto no_argv; argv = &argv[1]; /* skip this param, and go to nextone */ } uargv = xcalloc(g_strv_length(argv)+1, sizeof(char **)); for (i = 0; argv[i]; i++) uargv[i] = (char*)locale_to_gg_use(session, argv[i]); for (i = 0; argv[i]; i++) { char *arg = argv[i]; if (match_arg(arg, 'f', ("first"), 2) && argv[i + 1]) { gg_pubdir50_add(req, GG_PUBDIR50_FIRSTNAME, uargv[++i]); continue; } if (match_arg(arg, 'l', ("last"), 2) && argv[i + 1]) { gg_pubdir50_add(req, GG_PUBDIR50_LASTNAME, uargv[++i]); continue; } if (match_arg(arg, 'n', ("nickname"), 2) && argv[i + 1]) { gg_pubdir50_add(req, GG_PUBDIR50_NICKNAME, uargv[++i]); continue; } if (match_arg(arg, 'c', ("city"), 2) && argv[i + 1]) { gg_pubdir50_add(req, GG_PUBDIR50_CITY, uargv[++i]); continue; } if (match_arg(arg, 'u', ("uin"), 2) && argv[i + 1]) { gg_pubdir50_add(req, GG_PUBDIR50_UIN, uargv[++i]); continue; } if (match_arg(arg, 's', ("start"), 3) && argv[i + 1]) { gg_pubdir50_add(req, GG_PUBDIR50_START, uargv[++i]); continue; } if (match_arg(arg, 'F', ("female"), 2)) { gg_pubdir50_add(req, GG_PUBDIR50_GENDER, GG_PUBDIR50_GENDER_FEMALE); continue; } if (match_arg(arg, 'M', ("male"), 2)) { gg_pubdir50_add(req, GG_PUBDIR50_GENDER, GG_PUBDIR50_GENDER_MALE); continue; } if (match_arg(arg, 'a', ("active"), 2)) { gg_pubdir50_add(req, GG_PUBDIR50_ACTIVE, GG_PUBDIR50_ACTIVE_TRUE); continue; } if (match_arg(arg, 'b', ("born"), 2) && argv[i + 1]) { char *foo = xstrchr(uargv[++i], ':'); if (foo) *foo = ' '; gg_pubdir50_add(req, GG_PUBDIR50_BIRTHYEAR, uargv[i]); continue; } if (match_arg(arg, 'A', ("all"), 3)) { if (!gg_pubdir50_get(req, 0, GG_PUBDIR50_START)) gg_pubdir50_add(req, GG_PUBDIR50_START, "0"); all = 1; continue; } printq("invalid_params", name, arg); gg_pubdir50_free(req); for (i = 0; argv[i]; i++) recode_xfree(argv[i], uargv[i]); xfree(uargv); return -1; } for (i = 0; argv[i]; i++) recode_xfree(argv[i], uargv[i]); xfree(uargv); no_argv: if (!gg_pubdir50(g->sess, req)) { printq("search_failed", ("Nie wiem o co chodzi")); res = -1; } if (all) list_add(&g->searches, req); else gg_pubdir50_free(req); return res; } COMMAND(gg_command_change) { gg_private_t *g = session_private_get(session); int i; gg_pubdir50_t req; if (!g->sess || g->sess->state != GG_STATE_CONNECTED) { printq("not_connected", session_name(session)); return -1; } if (!params[0]) { printq("not_enough_params", name); return -1; } if (!(req = gg_pubdir50_new(GG_PUBDIR50_WRITE))) return -1; if (xstrcmp(params[0], ("-"))) { char **argv = array_make(params[0], (" \t"), 0, 1, 1); for (i = 0; argv[i]; i++) argv[i] = locale_to_gg(session, argv[i]); for (i = 0; argv[i]; i++) { if (match_arg(argv[i], 'f', ("first"), 2) && argv[i + 1]) { gg_pubdir50_add(req, GG_PUBDIR50_FIRSTNAME, argv[++i]); continue; } if (match_arg(argv[i], 'N', ("familyname"), 7) && argv[i + 1]) { gg_pubdir50_add(req, GG_PUBDIR50_FAMILYNAME, argv[++i]); continue; } if (match_arg(argv[i], 'l', ("last"), 2) && argv[i + 1]) { gg_pubdir50_add(req, GG_PUBDIR50_LASTNAME, argv[++i]); continue; } if (match_arg(argv[i], 'n', ("nickname"), 2) && argv[i + 1]) { gg_pubdir50_add(req, GG_PUBDIR50_NICKNAME, argv[++i]); continue; } if (match_arg(argv[i], 'c', ("city"), 2) && argv[i + 1]) { gg_pubdir50_add(req, GG_PUBDIR50_CITY, argv[++i]); continue; } if (match_arg(argv[i], 'C', ("familycity"), 7) && argv[i + 1]) { gg_pubdir50_add(req, GG_PUBDIR50_FAMILYCITY, argv[++i]); continue; } if (match_arg(argv[i], 'b', ("born"), 2) && argv[i + 1]) { gg_pubdir50_add(req, GG_PUBDIR50_BIRTHYEAR, argv[++i]); continue; } if (match_arg(argv[i], 'F', ("female"), 2)) { gg_pubdir50_add(req, GG_PUBDIR50_GENDER, GG_PUBDIR50_GENDER_SET_FEMALE); continue; } if (match_arg(argv[i], 'M', ("male"), 2)) { gg_pubdir50_add(req, GG_PUBDIR50_GENDER, GG_PUBDIR50_GENDER_SET_MALE); continue; } printq("invalid_params", name, argv[i]); g_strfreev(argv); gg_pubdir50_free(req); return -1; } g_strfreev(argv); } if (!gg_pubdir50(g->sess, req)) { printq("change_failed", ("")); gg_pubdir50_free(req); return -1; } gg_pubdir50_free(req); g->quiet |= GG_QUIET_CHANGE; return 0; } /* * gg_session_handler_search50() * * zajmuje si obsug wyniku przeszukiwania katalogu publicznego. * * - s - sesja * - e - opis zdarzenia */ void gg_session_handler_search50(session_t *s, struct gg_event *e) { gg_private_t *g = session_private_get(s); gg_pubdir50_t res = e->event.pubdir50; int i, count, all = 0; list_t l; uin_t last_uin = 0; if (!g) return; if ((count = gg_pubdir50_count(res)) < 1) { print("search_not_found"); return; } debug_function("gg_session_handler_search50() handle_search50, count = %d\n", gg_pubdir50_count(res)); for (l = g->searches; l; l = l->next) { gg_pubdir50_t req = l->data; if (gg_pubdir50_seq(req) == gg_pubdir50_seq(res)) { all = 1; break; } } for (i = 0; i < count; i++) { const char *uin = gg_pubdir50_get(res, i, "fmnumber"); const char *__firstname = gg_pubdir50_get(res, i, "firstname"); const char *__lastname = gg_pubdir50_get(res, i, "lastname"); const char *__nickname = gg_pubdir50_get(res, i, "nickname"); const char *__fmstatus = gg_pubdir50_get(res, i, "fmstatus"); const char *__birthyear = gg_pubdir50_get(res, i, "birthyear"); const char *__city = gg_pubdir50_get(res, i, "city"); char *firstname = gg_to_core_dup(s, __firstname); char *lastname = gg_to_core_dup(s, __lastname); char *nickname = gg_to_core_dup(s, __nickname); char *city = gg_to_core_dup(s, __city); int status = (__fmstatus) ? atoi(__fmstatus) : GG_STATUS_NOT_AVAIL; const char *birthyear = (__birthyear && xstrcmp(__birthyear, "0")) ? __birthyear : NULL; char *name, *active, *gender; const char *target = NULL; if (count == 1 && !all) { xfree(last_search_first_name); xfree(last_search_last_name); xfree(last_search_nickname); xfree(last_search_uid); last_search_first_name = xstrdup(firstname); last_search_last_name = xstrdup(lastname); last_search_nickname = xstrdup(nickname); last_search_uid = saprintf("gg:%s", uin); } name = saprintf( ("%s %s"), firstname ? firstname : (""), lastname ? lastname : ("")); #define __format(x) ((count == 1 && !all) ? "search_results_single" x : "search_results_multi" x) { const char *fvalue; switch (status) { case GG_STATUS_AVAIL: case GG_STATUS_AVAIL_DESCR: fvalue = format_find(__format("_avail")); break; case GG_STATUS_BUSY: case GG_STATUS_BUSY_DESCR: fvalue = format_find(__format("_away")); break; default: fvalue = format_find(__format("_notavail")); } active = format_string(fvalue, (__firstname) ? __firstname : nickname); } gender = format_string(format_find(__format("_unknown")), ""); /* XXX: why do we _exactly_ use it here? can't we just always * define target and thus display result in right conversation window? */ for (l = autofinds; l; l = l->next) { char *d = (char *) l->data; if (!xstrcasecmp(d + 3, uin)) { target = d; break; } } print_info(target, s, __format(""), uin ? uin : ("?"), name, nickname ? nickname : (""), city ? city : (""), birthyear ? birthyear : ("-"), gender, active); #undef __format xfree(name); xfree(active); xfree(gender); xfree(firstname); xfree(lastname); xfree(nickname); xfree(city); last_uin = atoi(uin); } /* jeli mielimy ,,/find --all'', szukamy dalej */ for (l = g->searches; l; l = l->next) { gg_pubdir50_t req = l->data; uin_t next; if (gg_pubdir50_seq(req) != gg_pubdir50_seq(res)) continue; /* nie ma dalszych? to dzikujemy */ if (!(next = gg_pubdir50_next(res)) || !g->sess || next <= last_uin) { list_remove(&g->searches, req, 0); gg_pubdir50_free(req); break; } gg_pubdir50_add(req, GG_PUBDIR50_START, ekg_itoa(next)); gg_pubdir50(g->sess, req); break; } } /* * handle_change50() * * zajmuje si obsug zmiany danych w katalogu publicznym. * * - s - sesja * - e - opis zdarzenia */ void gg_session_handler_change50(session_t *s, struct gg_event *e) { gg_private_t *g = session_private_get(s); int quiet; if (!g) return; quiet = (g->quiet & GG_QUIET_CHANGE); printq("change"); } /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/plugins/gg/pubdir50.h000066400000000000000000000021641175142753400200410ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2003 Wojtek Kaniewski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __EKG_GG_PUBDIR50_H #define __EKG_GG_PUBDIR50_H COMMAND(gg_command_find); COMMAND(gg_command_change); void gg_session_handler_search50(session_t *s, struct gg_event *e); void gg_session_handler_change50(session_t *s, struct gg_event *e); #endif /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/plugins/gg/session-en.txt000066400000000000000000000065251175142753400210670ustar00rootroot00000000000000// Little hint of session variables for gg plugin. // (c) copyright 2005 sebastian szary auto_find type: bool default value: 0 Variable determines if people we haven't got on userlist, but send us a message, should be checked in users catalog. concat_multiline_status type: integer default value: 3 concatenates nasty-multiline status if number of lines is bigger, than value set. if equals 0 no concatenation will be performed. to distinguish multiline status will be prefixed with '[m]' (multiline) if there is enough place for that. connection_save type: integer default value: 0 Variable determines that server adres should be saved. default type: bool default value: 0 Variable determines that session should be default (first at program startup) display_notify type: integer default value: -1 Value -1 determines using global variables. Value 0 determines ignoring friends status change. Value 1 determines displaying all the changes. Value 2 determines displaying changes from inavailable to available and reversed. Higher priority have variable 'contacts', which hides status change if set 2. local_ip type: string default value: (none) Variable determines IP address, from where any connections are made (example: with GG server). If value is incorect, will be deleted. log_formats type: string default value: xml,simple Variable determines logging format (?!). password type: string default value: (none) Variable keeps users password. It's needed to connect to server. Automatically set after sucsesful registration, if where blank. port type: integer default value: 8074 Variable keeps port number, which program uses to connect to GaduGadu server. proxy type: string default value: (none) Variable available only when libjpeg is compiled, turning variable off causes program like the library doesn't exists, that means tokens are save to file. proxy_forwarding type: string default value: (none) Variable keeps address and port (with : beetween them) of forwarding server (router, proxy or sth :-)) which forwards on port 1550 on our computer. private type: bool default value: 0 Variable set 'only for friends' mode. scroll_long_desc type: integer default value: 0 Variable determines scrolling of too long descriptions in seconds. If 0, no scrolling. WARNING: using scrolling my cause removing gg user account. ATTENTION: when description is scrolling in information is only displayed 'cuted' description. Information about description change are NOT displayed. scroll_mode type: string [bounce|simple] default value: bounce Variable determines type of scrolling. bounce - bouncing from 'ends'. simple - scrolling to left, until ends displayed, after reaching end return to beginning server type: string default value: (none) Variable keeps IP addresses of servers, which program tries to connect, avoiding normal connection procedure. Can be useful when main server crashes. Addresses should be seperated by commas, semicolons or spaces. After colon we can write server port. If server and port are prefixed by 'tls:' string, program will try to negotiate secure TLS connection to server. If variable is set to 'tls', program asks main server about server address which provide TLS connections and will try to connect with that server. ekg2-0.4~pre+20120506.1/plugins/gg/session-pl.txt000066400000000000000000000074541175142753400211020ustar00rootroot00000000000000// mały opis dostępnych zmiennych sesyjnych pluginu gg // (c) copyright 2001-2003 wojtek kaniewski // (c) copyright 2004 piotr kupisiewicz auto_find typ: bool domyślna wartość: 0 określa, czy osoby, których nie mamy na liście kontaktów, a wysłały do nas wiadomość, mają być automatycznie wyszukane w katalogu. concat_multiline_status typ: liczba domyślna wartość: 3 powoduje łączenie wkur?ających wielolinijkowych statusów o ile tylko liczba linii przekracza wartość zmiennej. jeśli równa 0 nie będzie konkatenacji. dla odróżnienia wielolinijkowy status poprzedzany jest ciągiem '[m]' (od multiline :)) o ile pozwala na to miejsce. connection_save typ: liczba domyślna wartość: 0 określa czy adres serwera ma być zapisywany default typ: bool domyślna wartość: 0 określa czy dana sesja ma być sesją domyślną (ustawianą jako pierwsza podczas uruchomienia programu) display_notify typ: liczba domyślna wartość: -1 wartość -1 powoduje korzystanie z globalnej zmiennej. wartość 0 powoduje ignorowanie zmian stanu znajomych, wartość 1 powoduje wyświetlanie wszystkich zmian, wartość 2 wyświetla tylko zmiany z niedostępnego na dostępny i na odwrót. większy priorytet ma zmienna ,,contacts'', która przy wartości 2 ukrywa zmiany stanu. local_ip typ: tekst domyślna wartość: brak określa adres IP, z którego następują wszelkiego rodzaju połączenia, na przykład z serwerem GG. w przypadku błędnie wpisanej wartości, zostanie ona usunięta. log_formats typ: tekst domyślna wartość: xml,simple TODO password typ: tekst domyślna wartość: brak hasło użytkownika. niezbędne do połączenia z serwerem. automatycznie ustawiane po udanej rejestracji, gdy wcześniej było puste. port typ: liczba domyślna wartość: 8074 port jakiego mamy używać przy łączeniu proxy typ: tekst domyślna wartość: brak adres i port serwera proxy, oddzielone dwukropkiem. jeśli nie podano portu, domyślnie jest przyjmowany 8080. jeśli serwer proxy wymaga autoryzacji, należy poprzedzić go nazwą użytkownika, dwukropkiem, hasłem i małpą (np. ,,jan:tajnehasło@serwer'') proxy_forwarding typ: tekst domyślna wartość: brak adres i port (oddzielone dwukropkiem) serwera pośredniczącego (routera, proxy lub czegoś innego) przekierowany na port 1550 naszego komputera. private typ: bool domyślna wartość: 0 tryb tylko dla znajomych. scroll_long_desc typ: liczba domyślna wartość: 0 Określa co ile sekund ma nastąpić automagiczne scrollowanie zbyt długich statusów. Zero - brak scrollowania. OSTRZEŻENIE: używanie scrolla w statusach może doprowadzić do usunięcia konta użytkownika gg UWAGA: gdy status jest scrollowany w informacji wyświetlany jest tylko 'obcięty' status. infromacje o zmianach statusu na skutek scrollowania NIE są wyświetlane scroll_mode typ: tekst [bounce|simple] domyślna wartość: bounce określa rodzaj scrollowania bounce - odbijanie od 'końców' simple - przewija w lewo, aż widać do samego końca, po osiągnięciu końca wraca na początek server typ: tekst domyślna wartość: brak adresy IP serwerów, z którym klient powinien próbować się połączyć, pomijając właściwą procedurę łączenia się. przydane podczas awarii głównego serwera. adresy należy oddzielać przecinkami, średnikami lub spacjami. po dwukropku można podać port serwera. jeśli adres i port serwera zostaną poprzedzone tekstem ,,tls:'', klient spróbuje wynegocjować bezpieczne połączenie TLS z serwerem. jeśli zmiennej przypisze się wartość ,,tls'', spyta główny serwer o adres serwera obsługującego połączenia TLS i spróbuje się z nim połączyć. ekg2-0.4~pre+20120506.1/plugins/gg/token.h000066400000000000000000000060021175142753400175220ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2003 Adam Czerwiski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __TOKEN_H #define __TOKEN_H #ifdef HAVE_LIBJPEG const int token_char_height = 12; const char token_id_char[] = {"0123456789abcdef"}; const char token_id[][15] = { "..###..", ".#...#.", ".#...#.", "#.....#", "#.....#", "#.....#", "#.....#", "#.....#", "#.....#", ".#...#.", ".#...#.", "..###..", "..#", "###", "..#", "..#", "..#", "..#", "..#", "..#", "..#", "..#", "..#", "..#", "..###..", ".#...#.", "#.....#", "#.....#", "......#", ".....#.", "....#..", "...#...", "..#....", ".#.....", "#......", "#######", "..###..", ".#...#.", "#.....#", "#.....#", ".....#.", "..###..", ".....#.", "......#", "#.....#", "#.....#", ".#...#.", "..###..", ".....#.", "....##.", "....##.", "...#.#.", "..#..#.", "..#..#.", ".#...#.", "#....#.", "#######", ".....#.", ".....#.", ".....#.", "#######", "#......", "#......", "#......", "#.###..", "##...#.", "#.....#", "......#", "#.....#", "#.....#", ".#...#.", "..###..", "..###..", ".#...#.", "#.....#", "#......", "#.###..", "##...#.", "#.....#", "#.....#", "#.....#", "#.....#", ".#...#.", "..###..", "#######", "......#", "......#", ".....#.", ".....#.", "....#..", "....#..", "....#..", "...#...", "...#...", "...#...", "...#...", "..###..", ".#...#.", "#.....#", "#.....#", ".#...#.", "..###..", ".#...#.", "#.....#", "#.....#", "#.....#", ".#...#.", "..###..", "..###..", ".#...#.", "#.....#", "#.....#", "#.....#", "#.....#", ".#...##", "..###.#", "......#", "#.....#", ".#...#.", "..###..", "........", "........", "........", ".#####..", "#.....#.", "......#.", "......#.", ".######.", "#.....#.", "#.....#.", "#.....#.", ".#####.#", "#......", "#......", "#......", "#.###..", "##...#.", "#.....#", "#.....#", "#.....#", "#.....#", "#.....#", "##...#.", "#.###..", ".......", ".......", ".......", "..###..", ".#...#.", "#.....#", "#......", "#......", "#......", "#.....#", ".#...#.", "..###..", "......#", "......#", "......#", "..###.#", ".#...##", "#.....#", "#.....#", "#.....#", "#.....#", "#.....#", ".#...##", "..###.#", ".......", ".......", ".......", "..###..", ".#...#.", "#.....#", "#.....#", "#######", "#......", "#.....#", ".#...#.", "..###..", "...##", "..#..", "..#..", "#####", "..#..", "..#..", "..#..", "..#..", "..#..", "..#..", "..#..", "..#.."}; #endif #ifdef GIF_OCR struct token_t { size_t sx, sy; unsigned char *data; }; #endif #endif /* __TOKEN_H */ ekg2-0.4~pre+20120506.1/plugins/gg/vars-en.txt000066400000000000000000000042011175142753400203440ustar00rootroot00000000000000// mały opis dostępnych zmiennych pluginu gg // (c) copyright 2001-2003 Wojtek Waniewski // (c) copyright 2004 Piotr Kupisiewicz // (c) copyright 2004-2006 Adam Mikuta // (c) copyright 2005 Mateusz Samonek dcc type: bool default value: 0 Turn on or off p2p connections between clients. You need to reconnect after changing that option to send new IP adress. dcc_ip type: text default value: none. set IP adress that is send to server. You can set this variable as ,,auto'', IP then will be set automatically. dcc_limit type: text default value: 30/30 Set p2p connections limit in specified time. number before / set maximum, and number after / number of seconds. after going beyond this number p2p connections will be cuted down, to prevent client run-down. dcc_port type: number default value: 1550 port, which ekg uses for p2p conections. display_token type: bool default value: 1 variable which is available only when we've got special liblary that wi'll let us to decode JPEG. Turning it off will cause that ekg will save tokens to file. enable_chatstates type: bool default value: 1 enable or disable typing notification get_images type: bool default value: 0 if value = 1 , images from chat will be downloaded and saved in catalogue defined by images_dir images_dir type: text default value: none set catalogue, in which images will be saved, when get_images is set. images_size type: liczba default value: 20 varible which define size of images in kB skip_default_format type: bool default value: 0 if set, skip default (for windows client) format (black on white) split_messages type: bool default value: 0 variable which define what to do if message is longer that it is set. If value = 1 then message will be splited , if value = 0 then message will be send incomplete. ekg2-0.4~pre+20120506.1/plugins/gg/vars-pl.txt000066400000000000000000000053641175142753400203700ustar00rootroot00000000000000// mały opis dostępnych zmiennych pluginu gg // (c) copyright 2001-2003 Wojtek Kaniewski // (c) copyright 2004 Piotr Kupisiewicz // (c) copyright 2004-2006 Adam Mikuta dcc typ: bool domyślna wartość: 0 włącza lub wyłącza bezpośrednie połączenia między klientami. zmiana tej opcji wymaga ponownego połączenia z serwerem, by przesłać nowy adres IP lub jego brak. dcc_ip typ: tekst domyślna wartość: brak określa adres IP, który jest wysyłany serwerowi. jeśli przypisze się tej zmiennej wartość ,,auto'', adres będzie ustalany automatycznie. jeśli chcemy bezproblemowo łączyć się z klientami z tej samej sieci LAN, dobrze jest podać adres IP sieci LAN zamiast zewnętrznego. dcc_limit typ: tekst domyślna wartość: 30/30 określa limit bezpośrednich połączeń w danym przedziale czasu. liczba przed ukośnikiem określa maksymalną ilość połączeń, a liczba po ukośniku ilość sekund. po przekroczeniu tego progu bezpośrednie połączenia zostają wyłączone, by zapobiec atakom polegającym na wyczerpaniu zasobów klienta. zwykle po ponownym włączeniu bezpośrednich połączeń należy połączyć się ponownie z serwerem. dcc_port typ: liczba domyślna wartość: 1550 port, na którym ekg będzie oczekiwać na połączenia bezpośrednie. display_token typ: bool domyślna wartość: 1 zmienna dostępna tylko gdy w systemie jest biblioteka pozwalająca dekodować pliki JPEG. wyłączenie jej spowoduje działanie ekg tak, jakby biblioteki nie było, tj. zapisywanie tokenów do pliku. enable_chatstates typ: bool domyślna wartość: 1 Jeśli ustawione, wysyła rozmówcy informację o tym, że piszemy wiadomość get_images typ: bool domyślna wartość: 0 przy ustawionej wartości 1 jeśli w wiadomościach będą się znajdowały obrazki to będą one ściągane i zapisywane do katalogu określonego przez images_dir images_dir typ: tekst domyślna wartość: brak zmienna określa katalog, do którego mają być zapisywane obrazki przy ustawionej zmiennej get_images images_size typ: liczba domyślna wartość: 20 zmienna określa górną granicę wielkości rozmiaru przyjmowanych obrazków w kB skip_default_format typ: bool domyślna wartość: 0 jeśli ma wartość 1, to nie przetwarza informacji o formatowaniu tekstu wysyłanej przez windowsowe komunikatory od drugiej (a może losowej?) wiadomości (informacja: czarne na białym) split_messages typ: bool domyślna wartość: 0 zmienna określa co robić gdy wiadomość przekracza maksymalną długość wiadomości. jeżeli 1 to wiadomość dzielona jest na części, jeśli 0 to wiadomość jest wysyłana niekompletna $Id$ ekg2-0.4~pre+20120506.1/plugins/gpg/000077500000000000000000000000001175142753400164135ustar00rootroot00000000000000ekg2-0.4~pre+20120506.1/plugins/gpg/commands-en.txt000066400000000000000000000006161175142753400213600ustar00rootroot00000000000000key parameters: [options] [uid [key]] short description: manage encryption keys -l, --listkeys print all keys and their statuses (default option) -s, --setkey set a gpg key for a given user -f, --forcekey set a gpg key for a given user even if the key is not valid -d, --delkey remove a gpg key of a given user ekg2-0.4~pre+20120506.1/plugins/gpg/commands-pl.txt000066400000000000000000000011061175142753400213640ustar00rootroot00000000000000// opis komend dla pluginu gpg // 2009 created by Paweł Tomak key parametry: [opcje] [uid [klucz]] krotki opis: zarządza kluczami używanymi do szyfrowania wiadomości -l, --listkeys wypisuje wszystkie klucze oraz ich status (domyślna opcja) -s, --setkey dodaje/ustawia klucz użytkownika o podanym uid -f, --forcekey wymusza dodanie klucza użytkownika nawet jesli coś się nie zgadza z poprawnością klucza -d, --delkey usuwa klucz dla podanego użytkownika ekg2-0.4~pre+20120506.1/plugins/gpg/gpg.c000066400000000000000000000504731175142753400173450ustar00rootroot00000000000000/* * Code ported from mcabber 0.9.0 to ekg2 * Copyright (C) 2006 Jakub Zawadzki * * Orginal code: * Copyright (C) 2006 Mikael Berthe * Some parts inspired by centericq (impgp.cc) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #include #include #include #include #include #include static int gpg_theme_init(); PLUGIN_DEFINE(gpg, PLUGIN_CRYPT, gpg_theme_init); /* XXX, enums */ typedef struct { char *uid; char *keyid; char *password; int keysetup; /* 0 - autoadded; 1 - added by user; 2 - forced by user */ int keynotok; /* -1 - keystatus unknown. 0 - key ok; 1 - key ver failed; 2 - key mishmashed */ } egpg_key_t; static list_t gpg_keydb; /* XXX, multiresource. */ static egpg_key_t *gpg_keydb_add(const char *uid, const char *keyid, const char *fpr) { egpg_key_t *a = xmalloc(sizeof(egpg_key_t)); a->uid = xstrdup(uid); a->keyid = xstrdup(keyid); a->keynotok = -1; list_add(&gpg_keydb, a); return a; } static egpg_key_t *gpg_keydb_find_uid(const char *uid) { list_t l; for (l = gpg_keydb; l; l = l->next) { egpg_key_t *k = l->data; if (!xstrcmp(k->uid, uid)) return k; } return NULL; } static gpgme_error_t gpg_passphrase_cb(void *data, const char *uid_hint, const char *passphrase_info, int prev_was_bad, int fd) { size_t len; if (!data) { /* no password ! */ write(fd, "\n", 1); return gpg_error(GPG_ERR_CANCELED); } len = xstrlen((char *) data); if (write(fd, (char *) data, len) != len) return gpg_error(GPG_ERR_CANCELED); if (write(fd, "\n", 1) != 1) return gpg_error(GPG_ERR_CANCELED); return 0; /* success */ } static const char *gpg_find_keyid(const char *uid, const char **password, char **error) { const char *key = NULL; session_t *s; if (password) *password = NULL; if ((s = session_find(uid))) { /* if we have that session */ key = session_get(s, "gpg_key"); /* get value from session */ /* get password from session too */ if (password) *password = session_get(s, "gpg_password"); } if (!key) { /* if we still have no key... than try our keydatabase */ egpg_key_t *k = gpg_keydb_find_uid(uid); if (k) { key = k->uid; if (password) *password = k->password; } } if (!key) key = uid; /* otherwise use uid */ if (!key) { *error = saprintf("GPG INTERNAL ERROR: @ [%s:%d] key == NULL", __FILE__, __LINE__); return NULL; } /* XXX here, we need to search gpg key db for search of key = 'key' coz we can have here everything... uid, name, etc, etc... * and if we have multiiple choices we should allow user to select proper one. */ return key; } #define GPGME_GENERROR(x) saprintf(x": %s", gpgme_strerror(err)); static QUERY(gpg_message_encrypt) { char *uid = *(va_arg(ap, char **)); /* uid */ char **message = va_arg(ap, char **); /* message to encrypt */ char **error = va_arg(ap, char **); /* place to put errormsg */ char *gpg_data = *message; egpg_key_t *key; *error = NULL; if (!(key = gpg_keydb_find_uid(uid))) { *error = saprintf("GPG KEY FOR USER: %s NOT FOUND. TRY /gpg:key --setkey\n", uid); return 1; } if (key->keynotok) { if (key->keysetup != 2) { if (key->keynotok == -1)*error = xstrdup("Message not encrypted cause key verification status unknown"); if (key->keynotok == 1) *error = xstrdup("Message not encrypted cause key failed verification"); if (key->keynotok == 2) *error = xstrdup("Message not encrypted cause key mishmash, if you really want encrypt messages use: /gpg:key --forcekey"); return 1; } /* key forced */ debug_error("gpg_message_encrypt() USER FORCE KEY!!!!\n"); } if (key->keysetup == 0 && 1 /* XXX, zmienna */) { *error = xstrdup("Message not encrypted, key is ok, but it was set up automagicly... you must [turn on global encryption with /set gpg:smth 1 (XXX) or] use /gpg:key --setkey"); return 1; } do { gpgme_ctx_t ctx; gpgme_data_t in, out; gpgme_key_t gpg_key; gpgme_error_t err; if ((err = gpgme_new(&ctx))) { *error = GPGME_GENERROR("GPGME error"); break; } gpgme_set_protocol(ctx, GPGME_PROTOCOL_OpenPGP); gpgme_set_textmode(ctx, 0); gpgme_set_armor(ctx, 1); err = gpgme_get_key(ctx, key->keyid, &gpg_key, 0); if (!err && gpg_key) { gpgme_key_t keys[] = { gpg_key, 0 }; err = gpgme_data_new_from_mem(&in, gpg_data, xstrlen(gpg_data), 0); if (!err) { err = gpgme_data_new(&out); if (!err) { err = gpgme_op_encrypt(ctx, keys, GPGME_ENCRYPT_ALWAYS_TRUST, in, out); if (!err) { size_t nread; char *encrypted_data = gpgme_data_release_and_get_mem(out, &nread); xfree(*message); *message = xstrndup(encrypted_data, nread); xfree(encrypted_data); } else gpgme_data_release(out); } gpgme_data_release(in); } gpgme_key_release(gpg_key); } else { *error = saprintf("GPGME encryption error: key not found"); } if (!*error && err /* && err != GPG_ERR_CANCELED */) *error = GPGME_GENERROR("GPGME encryption error"); gpgme_release(ctx); } while(0); if (*error) return 1; return 0; } static QUERY(gpg_message_decrypt) { char *uid = *(va_arg(ap, char **)); /* uid */ char **message = va_arg(ap, char **); /* message to decrypt */ char **error = va_arg(ap, char **); /* place to put errormsg */ char *gpg_data = saprintf(data, *message); const char *key = NULL; const char *pass = NULL; *error = NULL; if (!(key = gpg_find_keyid(uid, &pass, error))) return 1; if (!pass) { *error = saprintf("GPG: NO PASSPHRASE FOR KEY: %s SET PASSWORD AND TRY AGAIN (/sesion -s gpg_password \"[PASSWORD]\")\n", key); /* XXX, here if we don't have password. Allow user to type it. XXX */ return 1; } do { gpgme_ctx_t ctx; gpgme_error_t err; gpgme_data_t in, out; char *p; if ((err = gpgme_new(&ctx))) { *error = GPGME_GENERROR("GPGME error"); break; } gpgme_set_protocol(ctx, GPGME_PROTOCOL_OpenPGP); p = getenv("GPG_AGENT_INFO"); if (!(p && xstrchr(p, ':'))) gpgme_set_passphrase_cb(ctx, gpg_passphrase_cb, (void *) pass); err = gpgme_data_new_from_mem(&in, gpg_data, xstrlen(gpg_data), 0); if (!err) { err = gpgme_data_new(&out); if (!err) { err = gpgme_op_decrypt(ctx, in, out); if (!err) { size_t nread; char *decrypted_data = gpgme_data_release_and_get_mem(out, &nread); xfree(*message); *message = xstrndup(decrypted_data, nread); xfree(decrypted_data); } else { gpgme_data_release(out); } } gpgme_data_release(in); } /* if (err && err != GPG_ERR_CANCELED) */ if (err) *error = GPGME_GENERROR("GPGME decryption error"); gpgme_release(ctx); } while (0); xfree(gpg_data); if (*error) return 1; return 0; } static QUERY(gpg_sign) { char *uid = *(va_arg(ap, char **)); /* uid */ char **message = va_arg(ap, char **); /* message to sign */ char **error = va_arg(ap, char **); /* place to put errormsg */ const char *key = NULL; const char *pass = NULL; char *gpg_data = *message; *error = NULL; if (!(key = gpg_find_keyid(uid, &pass, error))) return 1; if (!pass) { *error = saprintf("GPG: NO PASSPHRASE FOR KEY: %s SET PASSWORD AND TRY AGAIN (/sesion -s gpg_password \"[PASSWORD]\")\n", key); /* XXX, here if we don't have password. Allow user to type it. XXX */ return 1; } do { gpgme_error_t err; gpgme_ctx_t ctx; gpgme_key_t gpg_key; gpgme_data_t in, out; char *p; if ((err = gpgme_new(&ctx))) { *error = GPGME_GENERROR("GPGME error"); break; } gpgme_set_protocol(ctx, GPGME_PROTOCOL_OpenPGP); gpgme_set_textmode(ctx, 0); gpgme_set_armor(ctx, 1); p = getenv("GPG_AGENT_INFO"); if (!(p && xstrchr(p, ':'))) gpgme_set_passphrase_cb(ctx, gpg_passphrase_cb, (void *) pass); /* last param -> data, .. in callback 1st param */ if ((err = gpgme_get_key(ctx, key, &gpg_key, 1)) || !gpg_key) { *error = saprintf("GPGME error: private key not found"); gpgme_release(ctx); break; } gpgme_signers_clear(ctx); gpgme_signers_add(ctx, gpg_key); gpgme_key_release(gpg_key); err = gpgme_data_new_from_mem(&in, gpg_data, xstrlen(gpg_data), 0); if (!err) { err = gpgme_data_new(&out); if (!err) { err = gpgme_op_sign(ctx, in, out, GPGME_SIG_MODE_DETACH); if (!err) { char *signed_data; size_t nread; xfree(*message); signed_data = gpgme_data_release_and_get_mem(out, &nread); *message = xstrndup(signed_data, nread); xfree(signed_data); } else { gpgme_data_release(out); } } gpgme_data_release(in); } /* if (err && err != GPG_ERR_CANCELED) */ if (err) *error = GPGME_GENERROR("GPGME signature error"); gpgme_release(ctx); } while(0); if (*error) return 1; return 0; } static QUERY(gpg_verify) { char *uid = *(va_arg(ap, char **)); /* uid */ char *message = *(va_arg(ap, char **)); /* message to verify WITHOUT HEADER! */ char **keydata = va_arg(ap, char **); /* key data, after key-id */ char **error = va_arg(ap, char **); /* key verification status */ char *gpg_data = saprintf(data, *keydata); *error = NULL; do { gpgme_ctx_t ctx; gpgme_error_t err; gpgme_data_t data_sign, data_text; if ((err = gpgme_new(&ctx))) { *error = GPGME_GENERROR("GPGME error"); break; } gpgme_set_protocol(ctx, GPGME_PROTOCOL_OpenPGP); err = gpgme_data_new_from_mem(&data_sign, gpg_data, xstrlen(gpg_data), 0); /* too much if... too much... use goto? XXX */ if (!err) { err = gpgme_data_new_from_mem(&data_text, message, xstrlen(message), 0); if (!err) { err = gpgme_op_verify(ctx, data_sign, data_text, 0); if (!err) { gpgme_verify_result_t vr = gpgme_op_verify_result(ctx); if (vr && vr->signatures) { char *fpr = vr->signatures->fpr; char *keyid = NULL; int keynotok = -1; gpgme_key_t key; egpg_key_t *k; /* FINGERPRINT -> KEY_ID */ if (!gpgme_get_key(ctx, fpr, &key, 0) && key) { keyid = xstrdup(key->subkeys->keyid); gpgme_key_release(key); } if (!vr->signatures->summary && !vr->signatures->status) { /* summary = 0, status = 0 -> signature valid */ *error = xstrdup("Signature ok"); keynotok = 0; /* ok */ } else if (vr->signatures->summary & GPGME_SIGSUM_RED) { *error = xstrdup("Signature bad"); keynotok = 1; /* bad */ } else if (vr->signatures->summary & GPGME_SIGSUM_GREEN) { *error = xstrdup("Signature ok"); keynotok = 0; /* ok */ } else { *error = xstrdup("Signature ?!?!"); keynotok = -1; /* bad, unknown */ } if ((k = gpg_keydb_find_uid(uid))) { if (xstrcmp(k->keyid, keyid)) { if (k->keysetup == 0) { /* if we don't setup our key... than replace it. */ xfree(k->keyid); k->keyid = xstrdup(keyid); } else debug_error("[gpg] uid: %s is really using key: %s in our db: %s\n", uid, keyid, k->keyid); if (k->keysetup) k->keynotok = 2; /* key mishmash (if we set it up manually. */ else k->keynotok = keynotok; } else k->keynotok = keynotok; } else { k = gpg_keydb_add(uid, keyid, fpr); k->keynotok = keynotok; } xfree(*keydata); *keydata = keyid; } else { xfree(*keydata); *keydata = NULL; } } gpgme_data_release(data_text); } gpgme_data_release(data_sign); } if (err) *error = GPGME_GENERROR("GPGME verification error"); gpgme_release(ctx); } while(0); xfree(gpg_data); if (*error) return 1; return 0; } static char *gpg_key_status(egpg_key_t *k) { static char buf[123]; if (!k) return NULL; /* X */ buf[0] = 0; if (k->keynotok == -1) xstrcat(&buf[0], "Warning: Signature unknown status"); if (k->keynotok == 0) xstrcat(&buf[0], "Signature ok"); if (k->keynotok == 1) xstrcat(&buf[0], "Warning: Signature bad."); if (k->keynotok == 2) xstrcat(&buf[0], "Warning: The KeyId doesn't match the key you set up."); if (k->keysetup == 2) xstrcat(&buf[0], " [ENCRPYTION FORCED]"); if (k->keysetup == 1 && k->keynotok == 0) xstrcat(&buf[0], " [ENCRYPTED]"); if (k->keysetup == 1 && k->keynotok != 0) xstrcat(&buf[0], " [NOTENCRYPTED]"); if (k->keysetup == 0) xstrcat(&buf[0], " [NOTENCRYPTED, NOTTRUSTED]"); if (k->keysetup == 0 && k->keynotok == 0) xstrcat(&buf[0], " [If you trust that key use /gpg:key -s]"); return &buf[0]; } static QUERY(gpg_user_keyinfo) { /* HERE, we display info about gpg support for user 'u' * query emited by /list */ userlist_t *u = *va_arg(ap, userlist_t **); int quiet = *va_arg(ap, int *); egpg_key_t *k; if (!u) return 0; if (xstrncmp(u->uid, "xmpp:", 5)) return 0; /* only jabber for now... */ if ((k = gpg_keydb_find_uid(u->uid))) { printq("user_info_gpg_key", k->keyid, gpg_key_status(k)); } return 0; } static COMMAND(gpg_command_key) { int fkey = 0; if ((!params[0]) || match_arg(params[0], 'l', "listkeys", 2)) { /* DISPLAY SUMMARY OF ALL KEYS */ list_t l; for (l = gpg_keydb; l; l = l->next) { egpg_key_t *k = l->data; printq("gpg_keys_list", k->uid, k->keyid, gpg_key_status(k)); } return 0; } #if 0 if ((params[0] && !params[1]) || match_arg(params[0], 'i', "infokey", 2)) { /* DISPLAY KEY INFO */ return 0; } #endif if ((fkey = match_arg(params[0], 'f', "forcekey", 2)) || match_arg(params[0], 's', "setkey", 2)) { egpg_key_t *k; if (!params[1] || !params[2]) { printq("not_enough_params", name); return -1; } if ((k = gpg_keydb_find_uid(params[1]))) { /* szukaj klucza */ if (xstrcmp(k->keyid, params[2])) { /* jesli mamy usera w bazie i klucze mishmashuja */ /* XXX, keep user valid key? and check here if match? XXX */ if (k->keynotok != 2 && k->keynotok != -1) { /* XXX ? keynotok != 0 */ printq(fkey ? "gpg_key_set_okfbutmish" : "gpg_key_set_okbutmish", k->uid, params[2]); k->keynotok = 2; } else { printq(fkey ? "gpg_key_set_okfbutunk" : "gpg_key_set_okbutunk", k->uid, params[2]); k->keynotok = -1; /* unknown status of key */ } /* replace keyid */ xfree(k->keyid); k->keyid = xstrdup(params[2]); } else { if (fkey) /* forced */ printq( k->keynotok == 0 ? "gpg_key_set_okf" : k->keynotok == 1 ? "gpg_key_set_okfbutver" : k->keynotok == 2 ? "gpg_key_set_okfbutmish": "gpg_key_set_okfbutunk", k->uid, k->keyid); else printq( k->keynotok == 0 ? "gpg_key_set_ok" : k->keynotok == 1 ? "gpg_key_set_okbutver" : k->keynotok == 2 ? "gpg_key_set_okbutmish": "gpg_key_set_okbutunk", k->uid, k->keyid); } } else { k = gpg_keydb_add(params[1], params[2], NULL); printq(fkey ? "gpg_key_set_newf" : "gpg_key_set_new", params[1], params[2]); } if (fkey) k->keysetup = 2; /* forced */ else k->keysetup = 1; /* normal */ return 0; } if (match_arg(params[0], 'd', "delkey", 2)) { egpg_key_t *k; if (!params[1]) { printq("not_enough_params", name); return -1; } if (!(k = gpg_keydb_find_uid(params[1]))) { printq("gpg_key_not_found", params[1]); return -1; } k->keysetup = 0; k->keynotok = -1; printq("gpg_key_unset", params[1]); return 0; } printq("invalid_params", name, params[0]); return -1; } static int gpg_theme_init() { #ifndef NO_DEFAULT_THEME format_add("gpg_key_unset", _("%) GPGKEY for uid: %W%1%n UNSET!"), 1); format_add("gpg_key_not_found", _("%> GPGKEY for uid: %W%1%n NOT FOUND!"), 1); format_add("gpg_key_set_new", _("%) You've set up new key for uid: %W%1%n keyid: %W%2%n\n%) Encryption will be disabled until you force key (gpg:key --forcekey) NOT RECOMENDED or we verify key (signed presence is enough)"), 1); format_add("gpg_key_set_newf", _("%) You've forced setting new key for uid: %W%1%n keyid: %W%2%n\n%! Forcing key is not good idea... Please rather use /gpg:key --setkey coz key will be verified before encryption..."), 1); format_add("gpg_key_set_ok", _("%> Keys you've set up for uid: %W%1%n match with our internal DB. Happy encrypted talk. F**k echelon"), 1); format_add("gpg_key_set_okf", _("%> Keys you've set up for uid: %W%1%n match with our internal DB. Happy encrypted talk. F**k echelon (Forcing key is not nessesary here!)"), 1); format_add("gpg_key_set_okbutver", _("%! Keys matched, but lasttime we fail to verify key. Encryption won't work until forced."), 1); format_add("gpg_key_set_okfbutver", _("%! Keys matched, but lasttime we fail to verify key. Encryption forced."), 1); format_add("gpg_key_set_okbutmish", _("%! Keys mishmash. Encryption won't work until forced or user change his keyid."), 1); format_add("gpg_key_set_okfbutmish", _("%! Keys mishmash. Encryption forced."), 1); format_add("gpg_key_set_okbutunk", _("%! We didn't verify this key, if you're sure it's ok force key (gpg:key --forcekey) however it's NOT RECOMENDED.. or wait until we verify key"), 1); format_add("gpg_key_set_okfbutunk", _("%! We didn't verify this key, You've forced encryption. NOT RECOMENDED."), 1); format_add("gpg_keys_list", "%> %W%1%n/%W%2%n %3", 1); /* uid, keyid, key status */ format_add("user_info_gpg_key", _("%K| %nGPGKEY: %T%1%n (%2)%n"), 1); /* keyid, key status */ #endif return 0; } #define MIN_GPGME_VERSION "1.0.0" EXPORT int gpg_plugin_init(int prio) { FILE *f; gpgme_error_t err; const char *dbfile = prepare_pathf("keys/gpgkeydb.txt"); PLUGIN_CHECK_VER("gpg"); if (mkdir_recursive(dbfile, 0)) { debug_error("Creating of directory keys failed, gpg plugin needs it!\n"); /* it's not 100% true.. but... */ return -1; } if (!gpgme_check_version(MIN_GPGME_VERSION)) { debug_error("GPGME initialization error: Bad library version"); return -1; } if ((err = gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP))) { debug_error("GPGME initialization error: %s", gpgme_strerror(err)); return -1; } if ((f = fopen(dbfile, "r"))) { char *line; while ((line = read_file(f, 0))) { char **p = array_make(line, "\t", 3, 0, 0); if (p && p[0] && p[1] && p[2]) { egpg_key_t *k = gpg_keydb_add(p[0], p[1], NULL); k->keysetup = atoi(p[2]); } else debug_error("[GPG] INVALID LINE: %s\n", line); g_strfreev(p); } fclose(f); } else debug_error("[GPG] Opening of %s failed: %d %s.\n", dbfile, errno, strerror(errno)); #if 0 /* XXX Set the locale information. ? */ gpgme_set_locale(NULL, LC_CTYPE, setlocale(LC_CTYPE, NULL)); gpgme_set_locale(NULL, LC_MESSAGES, setlocale(LC_MESSAGES, NULL)); #endif plugin_register(&gpg_plugin, prio); command_add(&gpg_plugin, "gpg:key", "p u ?", gpg_command_key, 0, "-d --delkey -f --forcekey -i --infokey -l --listkeys -s --setkey"); query_connect(&gpg_plugin, "gpg-message-encrypt", gpg_message_encrypt, NULL); query_connect(&gpg_plugin, "gpg-message-decrypt", gpg_message_decrypt, "-----BEGIN PGP MESSAGE-----\n\n" "%s\n" "-----END PGP MESSAGE-----\n"); query_connect(&gpg_plugin, "gpg-sign", gpg_sign, NULL); query_connect(&gpg_plugin, "gpg-verify", gpg_verify, "-----BEGIN PGP SIGNATURE-----\n\n" "%s\n" "-----END PGP SIGNATURE-----\n"); query_connect(&gpg_plugin, "userlist-info", gpg_user_keyinfo, NULL); return 0; } static int gpg_plugin_destroy() { FILE *f = NULL; list_t l; const char *dbfile = prepare_pathf("keys/gpgkeydb.txt"); if (mkdir_recursive(dbfile, 0) || !(f = fopen(dbfile, "w"))) { debug_error("[GPG] gpg db failed to save (%s)\n", strerror(errno)); } /* save our db to file, and cleanup memory... */ for (l = gpg_keydb; l; l = l->next) { egpg_key_t *k = l->data; if (f) fprintf(f, "%s\t%s\t%d\n", k->uid, k->keyid, k->keysetup); xfree(k->uid); xfree(k->keyid); xfree(k->password); /* WE DON'T SAVE PASSWORD ? XXX */ } list_destroy(gpg_keydb, 1); gpg_keydb = NULL; if (f) fclose(f); plugin_unregister(&gpg_plugin); return 0; } /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/plugins/gsm/000077500000000000000000000000001175142753400164245ustar00rootroot00000000000000ekg2-0.4~pre+20120506.1/plugins/gsm/main.c000066400000000000000000000144401175142753400175170ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2002 Wojtek Kaniewski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License Version * 2.1 as published by the Free Software Foundation. * * This program is distributed in the hope that 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. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #include #ifdef HAVE_GSM_H # include #else # ifdef HAVE_LIBGSM_GSM_H # include # else # ifdef HAVE_GSM_GSM_H # include # endif # endif #endif PLUGIN_DEFINE(gsm, PLUGIN_CODEC, NULL); CODEC_DEFINE(gsm); /* prywatna strukturka audio_codec_t */ typedef struct { char *from, *to; int msgsm; /* > 0 jeli mamy do czynienia z msgsm */ gsm codec; /* waciwa struktura libgsm */ } gsm_private_t; CODEC_CONTROL(gsm_codec_control) { va_list ap; if (type == AUDIO_CONTROL_INIT && aco) { gsm_private_t *priv = aco->priv_data; char **inpque = NULL, **outque = NULL, **tmp; /* we create array with vals... (XXX, to query only once.) */ audio_io_t *inp, *out; codec_way_t cway = -1; int value = 1; gsm codec; va_start(ap, aco); inp = va_arg(ap, audio_io_t *); out = va_arg(ap, audio_io_t *); va_end(ap); /* ;) */ inp->a->control_handler(AUDIO_CONTROL_SET, AUDIO_READ, inp, "__codec", "gsm", NULL); out->a->control_handler(AUDIO_CONTROL_SET, AUDIO_WRITE, out, "__codec", "gsm", NULL); /* QUERY FOR I/O if we don't have.. */ /* CACHE QUERY */ #define QUERY_INPUT_ADD(attr, val) if (!val) { array_add(&inpque, attr); array_add(&inpque, (char *) &val); } #define QUERY_OUTPUT_ADD(attr, val) if (!val) { array_add(&outque, attr); array_add(&outque, (char *) &val); } QUERY_INPUT_ADD("format", priv->from); QUERY_OUTPUT_ADD("format", priv->to); /* EXECUTE QUERIES */ if ((tmp = inpque)) { while (*tmp) { inp->a->control_handler(AUDIO_CONTROL_GET, AUDIO_READ, inp, tmp[0], tmp[1]); tmp++; tmp++; } } if ((tmp = outque)) { while (*tmp) { out->a->control_handler(AUDIO_CONTROL_GET, AUDIO_WRITE, out, tmp[0], tmp[1]); tmp++; tmp++; } } xfree(inpque); xfree(outque); debug("[gsm_codec_control] INIT (INP: 0x%x, 0x%x OUT: 0x%x, 0x%x) \n", inp, inpque, out, outque, 0); /* CHECK ALL ATTS: */ if ((!xstrcmp(priv->from, "pcm") || !xstrcmp(priv->from, "raw")) && !xstrcmp(priv->to, "gsm")) cway = CODEC_CODE; if ((!xstrcmp(priv->from, "gsm")) && (!xstrcmp(priv->to, "pcm") || !xstrcmp(priv->to, "raw"))) cway = CODEC_DECODE; if (cway == -1) { debug("NEITHER CODEING, NEIHER DECODING ? WHOA THERE... (from: %s to:%s)\n", priv->from, priv->to); return NULL; } /* INIT CODEC */ if (!(codec = gsm_create())) { debug("gsm_create() fails\n"); return NULL; } gsm_option(codec, GSM_OPT_FAST, &value); if (way == CODEC_DECODE) gsm_option(codec, GSM_OPT_LTP_CUT, &value); if (priv->msgsm) gsm_option(codec, GSM_OPT_WAV49, &value); priv->codec = codec; aco->way = cway; /* return 1 - succ ; 0 - failed*/ return (void *) 1; } else if (type == AUDIO_CONTROL_SET && !aco) { /* gsm_codec_init() */ char *attr; const char *from = NULL, *to = NULL; int with_ms = 0; gsm_private_t *priv; va_start(ap, aco); while ((attr = va_arg(ap, char *))) { char *val = va_arg(ap, char *); debug("[gsm_codec_control] attr: %s value: %s\n", attr, val); if (!xstrcmp(attr, "from")) from = val; else if (!xstrcmp(attr, "to")) to = val; else if (!xstrcmp(attr, "with-ms") && atoi(val)) with_ms = 1; /* XXX birate, channels */ } va_end(ap); priv = xmalloc(sizeof(gsm_private_t)); priv->msgsm = with_ms; priv->from = xstrdup(from); priv->to = xstrdup(to); aco = xmalloc(sizeof(audio_codec_t)); aco->c = &gsm_codec; aco->priv_data = priv; } else if (type == AUDIO_CONTROL_DEINIT && aco) { /* gsm_codec_destroy() */ gsm_private_t *priv = priv = aco->priv_data; if (priv && priv->codec) gsm_destroy(priv->codec); xfree(priv); aco = NULL; } else if (type == AUDIO_CONTROL_HELP) { static char *arr[] = { "-gsm", "", "-gsm:with-ms", "0 1", "-gsm:birate", "8000", "-gsm:sample", "16", "-gsm:channels", "1", "gsm:from", "gsm", ">gsm:to", "pcm raw", NULL, }; return arr; } return aco; } /* way: 0 - code ; 1 - decode */ int gsm_codec_process(int type, codec_way_t way, string_t input, string_t output, void *data) { gsm_private_t *c = data; int inpos = 0; if (type) return 0; if (!c || !input || !output) return -1; if (!input->str || !input->len) return 0; /* we have nothing to code? */ for (;;) { int inchunklen, outchunklen; char *out; if (way == CODEC_CODE) { inchunklen = 320; outchunklen = (c->msgsm == 1) ? 32 : 33; } else if (way == CODEC_DECODE) { inchunklen = (c->msgsm == 2) ? 32 : 33; outchunklen = 320; } else return -1; /* neither code neither decode? wtf? */ if ((input->len - inpos) < inchunklen) break; out = xmalloc(outchunklen); if (way == CODEC_CODE) gsm_encode(c->codec, (gsm_signal *) (input->str + inpos), (unsigned char *) out); else if (way == CODEC_DECODE) gsm_decode(c->codec, (unsigned char *) input->str + inpos, (gsm_signal *) out); string_append_raw(output, out, outchunklen); xfree(out); if (c->msgsm == 1) c->msgsm = 2; else if (c->msgsm == 2) c->msgsm = 1; inpos += inchunklen; } return inpos; } CODEC_RECODE(gsm_codec_code) { return gsm_codec_process(type, CODEC_CODE, input, output, data); } CODEC_RECODE(gsm_codec_decode) { return gsm_codec_process(type, CODEC_DECODE, input, output, data); } EXPORT int gsm_plugin_init(int prio) { PLUGIN_CHECK_VER("gsm"); plugin_register(&gsm_plugin, prio); codec_register(&gsm_codec); return 0; } static int gsm_plugin_destroy() { codec_unregister(&gsm_codec); plugin_unregister(&gsm_plugin); return 0; } /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/plugins/gtk/000077500000000000000000000000001175142753400164235ustar00rootroot00000000000000ekg2-0.4~pre+20120506.1/plugins/gtk/.gitignore000066400000000000000000000001141175142753400204070ustar00rootroot00000000000000# generate from iconssets/ inline_pngs_gg.h inline_pngs.h inline_pngs_icq.h ekg2-0.4~pre+20120506.1/plugins/gtk/bindings.c000066400000000000000000000312601175142753400203660ustar00rootroot00000000000000/* X-Chat * Copyright (C) 1998 Peter Zelezny. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ /* * port to ekg2: * Copyright (C) 2007 Jakub Zawadzki * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "main.h" #include "bindings.h" #include "ekg/completion.h" char *gtk_history[HISTORY_MAX]; int gtk_history_index; #define GTK_BINDING_FUNCTION(x) int x(GtkWidget *wid, GdkEventKey *evt, char *d1, window_t *sess) #define COMPLETION_MAXLEN 2048 /* rozmiar linii */ /* These are cp'ed from history.c --AGL */ #define STATE_SHIFT GDK_SHIFT_MASK #define STATE_ALT GDK_MOD1_MASK #define STATE_CTRL GDK_CONTROL_MASK static GTK_BINDING_FUNCTION(key_action_scroll_page) { int value, end; GtkAdjustment *adj; enum scroll_type { PAGE_UP, PAGE_DOWN, LINE_UP, LINE_DOWN }; int type = PAGE_DOWN; if (d1) { if (!xstrcasecmp(d1, "up")) type = PAGE_UP; else if (!xstrcasecmp(d1, "+1")) type = LINE_DOWN; else if (!xstrcasecmp(d1, "-1")) type = LINE_UP; } if (!sess) return 0; adj = GTK_RANGE(gtk_private_ui(sess)->vscrollbar)->adjustment; end = adj->upper - adj->lower - adj->page_size; switch (type) { case LINE_UP: value = adj->value - 1.0; break; case LINE_DOWN: value = adj->value + 1.0; break; case PAGE_UP: value = adj->value - (adj->page_size - 1); break; default: /* PAGE_DOWN */ value = adj->value + (adj->page_size - 1); break; } if (value < 0) value = 0; if (value > end) value = end; gtk_adjustment_set_value(adj, value); return 0; } static GTK_BINDING_FUNCTION(key_action_history_up) { if (gtk_history_index < HISTORY_MAX && gtk_history[gtk_history_index + 1]) { /* for each line? */ if (gtk_history_index == 0) { xfree(gtk_history[0]); gtk_history[0] = xstrdup((GTK_ENTRY(wid)->text)); } gtk_history_index++; gtk_entry_set_text(GTK_ENTRY(wid), gtk_history[gtk_history_index]); gtk_editable_set_position(GTK_EDITABLE(wid), -1); } return 2; } static GTK_BINDING_FUNCTION(key_action_history_down) { if (gtk_history_index > 0) { gtk_history_index--; gtk_entry_set_text(GTK_ENTRY(wid), gtk_history[gtk_history_index]); gtk_editable_set_position(GTK_EDITABLE(wid), -1); } return 2; } static void show_completions() { int maxlen = 0, cols, rows, i; char *tmp; int complcount = g_strv_length(ekg2_completions); for (i = 0; ekg2_completions[i]; i++) { size_t compllen = xstrlen(ekg2_completions[i]); if (compllen + 2 > maxlen) maxlen = compllen + 2; } cols = (window_current->width - 6) / maxlen; if (cols == 0) cols = 1; rows = complcount / cols + 1; tmp = xmalloc((cols * maxlen + 2)*sizeof(char)); for (i = 0; i < rows; i++) { int j; tmp[0] = 0; for (j = 0; j < cols; j++) { int cell = j * rows + i; if (cell < complcount) { int k; xstrcat(tmp, ekg2_completions[cell]); for (k = xstrlen(ekg2_completions[cell]); k < maxlen; k++) xstrcat(tmp, (" ")); } } if (tmp[0]) print("none", tmp); } xfree(tmp); } static GTK_BINDING_FUNCTION(key_action_tab_comp) { char buf[COMPLETION_MAXLEN]; const char *text; int cursor_pos; /* in fjuczer, use g_completion_new() ? */ text = ((GTK_ENTRY(wid)->text)); if (text[0] == '\0') return 1; cursor_pos = gtk_editable_get_position(GTK_EDITABLE(wid)); if (g_strlcpy(buf, text, sizeof(buf)) >= sizeof(buf)) printf("key_action_tab_comp(), strlcpy() UUUUUUUCH!\n"); int junk = 0; if (ekg2_complete(&junk, &cursor_pos, buf, COMPLETION_MAXLEN)) show_completions(); gtk_entry_set_text(GTK_ENTRY(wid), buf); gtk_editable_set_position(GTK_EDITABLE(wid), cursor_pos); return 2; } static GTK_BINDING_FUNCTION(key_action_cycle_session) { if (window_session_cycle(sess) == 0) { /* ekg2 ncurses->gtk XXX */ /* ncurses_contacts_update(NULL); update_statusbar(1); */ } return 2; } gboolean key_handle_key_press(GtkWidget *wid, GdkEventKey * evt, window_t *sess) { int keyval = evt->keyval; int mod, n; int was_complete = 0; window_t *w; { sess = NULL; /* where did this event come from? */ for (w = windows; w; w = w->next) { if (gtk_private_ui(w)->input_box == wid) { sess = w; if (gtk_private_ui(w)->is_tab) sess = window_current; break; } } } if (!sess) { printf("key_handle_key_press() FAILED (sess == NULL)\n"); return FALSE; } /* printf("key_handle_key_press() %p [%d %d %d %s]\n", sess, evt->state, evt->keyval, evt->length, evt->string); */ /* XXX, EMIT: KEY_PRESSED */ mod = evt->state & (STATE_CTRL | STATE_ALT | STATE_SHIFT); n = -1; /* yeah, i know it's awful. */ if (keyval == GDK_Page_Up) n = key_action_scroll_page(wid, evt, "up", sess); else if (keyval == GDK_Page_Down) n = key_action_scroll_page(wid, evt, "down", sess); else if (keyval == GDK_Up) n = key_action_history_up(wid, evt, NULL, sess); else if (keyval == GDK_Down) n = key_action_history_down(wid, evt, NULL, sess); else if (keyval == GDK_Tab) { n = key_action_tab_comp(wid, evt, NULL, sess); was_complete = 1; } else if (keyval == GDK_F12) command_exec(sess->target, sess->session, "/window switch 0", 0); else if (keyval == GDK_F1) command_exec(sess->target, sess->session, "/help", 0); else if (keyval == GDK_0 && mod == STATE_ALT) command_exec(sess->target, sess->session, "/window switch 10", 0); else if (keyval == GDK_9 && mod == STATE_ALT) command_exec(sess->target, sess->session, "/window switch 9", 0); else if (keyval == GDK_8 && mod == STATE_ALT) command_exec(sess->target, sess->session, "/window switch 8", 0); else if (keyval == GDK_7 && mod == STATE_ALT) command_exec(sess->target, sess->session, "/window switch 7", 0); else if (keyval == GDK_6 && mod == STATE_ALT) command_exec(sess->target, sess->session, "/window switch 6", 0); else if (keyval == GDK_5 && mod == STATE_ALT) command_exec(sess->target, sess->session, "/window switch 5", 0); else if (keyval == GDK_4 && mod == STATE_ALT) command_exec(sess->target, sess->session, "/window switch 4", 0); else if (keyval == GDK_3 && mod == STATE_ALT) command_exec(sess->target, sess->session, "/window switch 3", 0); else if (keyval == GDK_2 && mod == STATE_ALT) command_exec(sess->target, sess->session, "/window switch 2", 0); else if (keyval == GDK_1 && mod == STATE_ALT) command_exec(sess->target, sess->session, "/window switch 1", 0); else if (keyval == '`' && mod == STATE_ALT) command_exec(sess->target, sess->session, "/window switch 0", 0); else if (((keyval == GDK_Q) || (keyval == GDK_q)) && mod == STATE_ALT) command_exec(sess->target, sess->session, "/window switch 11", 0); else if (((keyval == GDK_W) || (keyval == GDK_w)) && mod == STATE_ALT) command_exec(sess->target, sess->session, "/window switch 12", 0); else if (((keyval == GDK_E) || (keyval == GDK_e)) && mod == STATE_ALT) command_exec(sess->target, sess->session, "/window switch 13", 0); else if (((keyval == GDK_R) || (keyval == GDK_r)) && mod == STATE_ALT) command_exec(sess->target, sess->session, "/window switch 14", 0); else if (((keyval == GDK_T) || (keyval == GDK_t)) && mod == STATE_ALT) command_exec(sess->target, sess->session, "/window switch 15", 0); else if (((keyval == GDK_Y) || (keyval == GDK_y)) && mod == STATE_ALT) command_exec(sess->target, sess->session, "/window switch 16", 0); else if (((keyval == GDK_U) || (keyval == GDK_u)) && mod == STATE_ALT) command_exec(sess->target, sess->session, "/window switch 17", 0); else if (((keyval == GDK_I) || (keyval == GDK_i)) && mod == STATE_ALT) command_exec(sess->target, sess->session, "/window switch 18", 0); else if (((keyval == GDK_O) || (keyval == GDK_o)) && mod == STATE_ALT) command_exec(sess->target, sess->session, "/window switch 19", 0); else if (((keyval == GDK_P) || (keyval == GDK_p)) && mod == STATE_ALT) command_exec(sess->target, sess->session, "/window switch 20", 0); else if (((keyval == GDK_N) || (keyval == GDK_n)) && mod == STATE_ALT) command_exec(sess->target, sess->session, "/window new", 0); else if (((keyval == GDK_K) || (keyval == GDK_k)) && mod == STATE_ALT) command_exec(sess->target, sess->session, "/window kill", 0); else if (((keyval == GDK_A) || (keyval == GDK_a)) && mod == STATE_ALT) command_exec(sess->target, sess->session, "/window active", 0); else if (((keyval == GDK_N) || (keyval == GDK_n)) && mod == STATE_CTRL) command_exec(sess->target, sess->session, "/window next", 0); else if (((keyval == GDK_P) || (keyval == GDK_p)) && mod == STATE_CTRL) command_exec(sess->target, sess->session, "/window prev", 0); else if (((keyval == GDK_F) || (keyval == GDK_f)) && mod == STATE_CTRL) n = key_action_scroll_page(wid, evt, "up", sess); else if (((keyval == GDK_G) || (keyval == GDK_g)) && mod == STATE_CTRL) n = key_action_scroll_page(wid, evt, "down", sess); else if (((keyval == GDK_X) || (keyval == GDK_x)) && mod == STATE_CTRL) n = key_action_cycle_session(wid, evt, NULL, sess); #if 0 ncurses_binding_add("Alt-G", "ignore-query", 1, 1); ncurses_binding_add("Alt-B", "backward-word", 1, 1); ncurses_binding_add("Alt-F", "forward-word", 1, 1); ncurses_binding_add("Alt-D", "kill-word", 1, 1); ncurses_binding_add("Alt-Enter", "toggle-input", 1, 1); ncurses_binding_add("Escape", "cancel-input", 1, 1); ncurses_binding_add("Backspace", "backward-delete-char", 1, 1); ncurses_binding_add("Ctrl-H", "backward-delete-char", 1, 1); ncurses_binding_add("Ctrl-A", "beginning-of-line", 1, 1); ncurses_binding_add("Home", "beginning-of-line", 1, 1); ncurses_binding_add("Ctrl-D", "delete-char", 1, 1); ncurses_binding_add("Delete", "delete-char", 1, 1); ncurses_binding_add("Ctrl-E", "end-of-line", 1, 1); ncurses_binding_add("End", "end-of-line", 1, 1); ncurses_binding_add("Ctrl-K", "kill-line", 1, 1); ncurses_binding_add("Ctrl-Y", "yank", 1, 1); ncurses_binding_add("Enter", "accept-line", 1, 1); ncurses_binding_add("Ctrl-M", "accept-line", 1, 1); ncurses_binding_add("Ctrl-U", "line-discard", 1, 1); ncurses_binding_add("Ctrl-V", "quoted-insert", 1, 1); ncurses_binding_add("Ctrl-W", "word-rubout", 1, 1); ncurses_binding_add("Alt-Backspace", "word-rubout", 1, 1); ncurses_binding_add("Ctrl-L", "/window refresh", 1, 1); ncurses_binding_add("Right", "forward-char", 1, 1); ncurses_binding_add("Left", "backward-char", 1, 1); ncurses_binding_add("F2", "quick-list", 1, 1); ncurses_binding_add("F3", "toggle-contacts", 1, 1); ncurses_binding_add("F4", "next-contacts-group", 1, 1); ncurses_binding_add("F11", "ui-ncurses-debug-toggle", 1, 1); #endif #if 0 for (l = bindings; l; l = l->next) { if (kb->keyval == keyval && kb->mod == mod) { /* Run the function */ n = key_actions[kb->action].handler(wid, evt, kb->data1, kb->data2, sess); switch (n) { case 0: return 1; case 2: g_signal_stop_emission_by_name(G_OBJECT(wid), "key_press_event"); return 1; } } } #endif if (!was_complete) { /* jeśli się coś zmieniło, wygeneruj dopełnienia na nowo */ ekg2_complete_clear(); /* w xchacie bylo tylko na GDK_space */ } if (n == 2) { g_signal_stop_emission_by_name(G_OBJECT(wid), "key_press_event"); return 1; } return (n == 0); } void gtk_binding_init() { } ekg2-0.4~pre+20120506.1/plugins/gtk/bindings.h000066400000000000000000000003101175142753400203630ustar00rootroot00000000000000int key_handle_key_press (GtkWidget * wid, GdkEventKey * evt, window_t *sess); void gtk_binding_init(); #define HISTORY_MAX 1000 extern char *gtk_history[HISTORY_MAX]; extern int gtk_history_index; ekg2-0.4~pre+20120506.1/plugins/gtk/chanview-tabs.inc000066400000000000000000000364031175142753400216570ustar00rootroot00000000000000/* file included in chanview.c */ typedef struct { GtkWidget *outer; /* outer box */ GtkWidget *inner; /* inner box */ GtkWidget *b1; /* button1 */ GtkWidget *b2; /* button2 */ } tabview; static void chanview_populate(chanview * cv); /* ignore "toggled" signal? */ static int ignore_toggle = FALSE; static int tab_left_is_moving = 0; static int tab_right_is_moving = 0; /* userdata for gobjects used here: * * tab (togglebuttons inside boxes): * "u" userdata passed to tab-focus callback function (sess) * "c" the tab's (chan *) */ /* * GtkViewports request at least as much space as their children do. * If we don't intervene here, the GtkViewport will be granted its * request, even at the expense of resizing the top-level window. */ static void cv_tabs_sizerequest(GtkWidget *viewport, GtkRequisition * requisition, chanview * cv) { if (!cv->vertical) requisition->width = 1; else requisition->height = 1; } static void cv_tabs_sizealloc(GtkWidget *widget, GtkAllocation * allocation, chanview * cv) { GtkAdjustment *adj; GtkWidget *inner; gint viewport_size; inner = ((tabview *) cv)->inner; if (cv->vertical) { adj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(inner->parent)); gdk_window_get_geometry(inner->parent->window, 0, 0, 0, &viewport_size, 0); } else { adj = gtk_viewport_get_hadjustment(GTK_VIEWPORT(inner->parent)); gdk_window_get_geometry(inner->parent->window, 0, 0, &viewport_size, 0, 0); } if (adj->upper <= viewport_size) { gtk_widget_hide(((tabview *) cv)->b1); gtk_widget_hide(((tabview *) cv)->b2); } else { gtk_widget_show(((tabview *) cv)->b1); gtk_widget_show(((tabview *) cv)->b2); } } static gint tab_search_offset(GtkWidget *inner, gint start_offset, gboolean forward, gboolean vertical) { GList *boxes; GList *tabs; GtkWidget *box; GtkWidget *button; gint found; boxes = GTK_BOX(inner)->children; if (!forward && boxes) boxes = g_list_last(boxes); while (boxes) { box = ((GtkBoxChild *) boxes->data)->widget; boxes = (forward ? boxes->next : boxes->prev); tabs = GTK_BOX(box)->children; if (!forward && tabs) tabs = g_list_last(tabs); while (tabs) { button = ((GtkBoxChild *) tabs->data)->widget; tabs = (forward ? tabs->next : tabs->prev); if (!GTK_IS_TOGGLE_BUTTON(button)) continue; found = (vertical ? button->allocation.y : button->allocation.x); if ((forward && found > start_offset) || (!forward && found < start_offset)) return found; } } return 0; } static void tab_scroll_left_up_clicked(GtkWidget *widget, chanview * cv) { GtkAdjustment *adj; gint viewport_size; gfloat new_value; GtkWidget *inner; gfloat i; inner = ((tabview *) cv)->inner; if (cv->vertical) { adj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(inner->parent)); gdk_window_get_geometry(inner->parent->window, 0, 0, 0, &viewport_size, 0); } else { adj = gtk_viewport_get_hadjustment(GTK_VIEWPORT(inner->parent)); gdk_window_get_geometry(inner->parent->window, 0, 0, &viewport_size, 0, 0); } new_value = tab_search_offset(inner, adj->value, 0, cv->vertical); if (new_value + viewport_size > adj->upper) new_value = adj->upper - viewport_size; if (!tab_left_is_moving) { tab_left_is_moving = 1; for (i = adj->value; ((i > new_value) && (tab_left_is_moving)); i -= 0.1) { gtk_adjustment_set_value(adj, i); while (g_main_pending()) g_main_iteration(TRUE); } gtk_adjustment_set_value(adj, new_value); tab_left_is_moving = 0; /* hSP: set to false in case we didnt get stopped (the normal case) */ } else { tab_left_is_moving = 0; /* hSP: jump directly to next element if user is clicking faster than we can scroll.. */ } } static void tab_scroll_right_down_clicked(GtkWidget *widget, chanview * cv) { GtkAdjustment *adj; gint viewport_size; gfloat new_value; GtkWidget *inner; gfloat i; inner = ((tabview *) cv)->inner; if (cv->vertical) { adj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(inner->parent)); gdk_window_get_geometry(inner->parent->window, 0, 0, 0, &viewport_size, 0); } else { adj = gtk_viewport_get_hadjustment(GTK_VIEWPORT(inner->parent)); gdk_window_get_geometry(inner->parent->window, 0, 0, &viewport_size, 0, 0); } new_value = tab_search_offset(inner, adj->value, 1, cv->vertical); if (new_value == 0 || new_value + viewport_size > adj->upper) new_value = adj->upper - viewport_size; if (!tab_right_is_moving) { tab_right_is_moving = 1; for (i = adj->value; ((i < new_value) && (tab_right_is_moving)); i += 0.1) { gtk_adjustment_set_value(adj, i); while (g_main_pending()) g_main_iteration(TRUE); } gtk_adjustment_set_value(adj, new_value); tab_right_is_moving = 0; /* hSP: set to false in case we didnt get stopped (the normal case) */ } else { tab_right_is_moving = 0; /* hSP: jump directly to next element if user is clicking faster than we can scroll.. */ } } static gboolean tab_scroll_cb(GtkWidget *widget, GdkEventScroll * event, gpointer cv) { /* mouse wheel scrolling */ if (event->direction == GDK_SCROLL_UP) tab_scroll_left_up_clicked(widget, cv); else if (event->direction == GDK_SCROLL_DOWN) tab_scroll_right_down_clicked(widget, cv); return FALSE; } static void cv_tabs_xclick_cb(GtkWidget *button, chanview * cv) { cv->cb_xbutton(cv, cv->focused, cv->focused->tag, cv->focused->userdata); } /* make a Scroll (arrow) button */ static GtkWidget *make_sbutton(GtkArrowType type, void *click_cb, void *userdata) { GtkWidget *button, *arrow; button = gtk_button_new(); arrow = gtk_arrow_new(type, GTK_SHADOW_NONE); gtk_container_add(GTK_CONTAINER(button), arrow); gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(click_cb), userdata); g_signal_connect(G_OBJECT(button), "scroll_event", G_CALLBACK(tab_scroll_cb), userdata); gtk_widget_show(arrow); return button; } static void cv_tabs_init(chanview * cv) { GtkWidget *box, *hbox = NULL; GtkWidget *viewport; GtkWidget *outer; GtkWidget *button; if (cv->vertical) outer = gtk_vbox_new(0, 0); else outer = gtk_hbox_new(0, 0); ((tabview *) cv)->outer = outer; g_signal_connect(G_OBJECT(outer), "size_allocate", G_CALLBACK(cv_tabs_sizealloc), cv); /* gtk_container_set_border_width (GTK_CONTAINER (outer), 2);*/ gtk_widget_show(outer); viewport = gtk_viewport_new(0, 0); gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE); g_signal_connect(G_OBJECT(viewport), "size_request", G_CALLBACK(cv_tabs_sizerequest), cv); g_signal_connect(G_OBJECT(viewport), "scroll_event", G_CALLBACK(tab_scroll_cb), cv); gtk_box_pack_start(GTK_BOX(outer), viewport, 1, 1, 0); gtk_widget_show(viewport); if (cv->vertical) box = gtk_vbox_new(FALSE, 0); else box = gtk_hbox_new(FALSE, 0); ((tabview *) cv)->inner = box; gtk_container_add(GTK_CONTAINER(viewport), box); gtk_widget_show(box); /* if vertical, the buttons can be side by side */ if (cv->vertical) { hbox = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(outer), hbox, 0, 0, 0); gtk_widget_show(hbox); } /* make the Scroll buttons */ ((tabview *) cv)->b2 = make_sbutton(cv->vertical ? GTK_ARROW_UP : GTK_ARROW_LEFT, tab_scroll_left_up_clicked, cv); ((tabview *) cv)->b1 = make_sbutton(cv->vertical ? GTK_ARROW_DOWN : GTK_ARROW_RIGHT, tab_scroll_right_down_clicked, cv); if (hbox) { gtk_container_add(GTK_CONTAINER(hbox), ((tabview *) cv)->b2); gtk_container_add(GTK_CONTAINER(hbox), ((tabview *) cv)->b1); } else { gtk_box_pack_start(GTK_BOX(outer), ((tabview *) cv)->b2, 0, 0, 0); gtk_box_pack_start(GTK_BOX(outer), ((tabview *) cv)->b1, 0, 0, 0); } button = gtkutil_button(outer, GTK_STOCK_CLOSE, NULL, cv_tabs_xclick_cb, cv, 0); gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE); GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS); gtk_container_add(GTK_CONTAINER(cv->box), outer); } static void cv_tabs_postinit(chanview * cv) { } static void tab_add_sorted(chanview * cv, GtkWidget *box, GtkWidget *tab, chan * ch) { GList *list; GtkBoxChild *child; int i = 0; void *b; if (!cv->sorted) { gtk_box_pack_start(GTK_BOX(box), tab, 0, 0, 0); gtk_widget_show(tab); return; } /* sorting TODO: * - move tab if renamed (dialogs) */ /* userdata, passed to mg_tabs_compare() */ b = ch->userdata; list = GTK_BOX(box)->children; while (list) { child = list->data; if (!GTK_IS_SEPARATOR(child->widget)) { void *a = g_object_get_data(G_OBJECT(child->widget), "u"); if (ch->tag == 0 && cv->cb_compare(a, b) > 0) { gtk_box_pack_start(GTK_BOX(box), tab, 0, 0, 0); gtk_box_reorder_child(GTK_BOX(box), tab, i); gtk_widget_show(tab); return; } } i++; list = list->next; } /* append */ gtk_box_pack_start(GTK_BOX(box), tab, 0, 0, 0); gtk_box_reorder_child(GTK_BOX(box), tab, i); gtk_widget_show(tab); } /* remove empty boxes and separators */ static void cv_tabs_prune(chanview * cv) { GList *boxes, *children; GtkWidget *box, *inner; GtkBoxChild *child; int empty; inner = ((tabview *) cv)->inner; boxes = GTK_BOX(inner)->children; while (boxes) { child = boxes->data; box = child->widget; boxes = boxes->next; /* check if the box is empty (except a vseperator) */ empty = TRUE; children = GTK_BOX(box)->children; while (children) { if (!GTK_IS_SEPARATOR(((GtkBoxChild *) children->data)->widget)) { empty = FALSE; break; } children = children->next; } if (empty) gtk_widget_destroy(box); } } static void tab_add_real(chanview * cv, GtkWidget *tab, chan * ch) { GList *boxes; GtkWidget *sep, *box, *inner; GtkBoxChild *child; inner = ((tabview *) cv)->inner; /* see if a family for this tab already exists */ boxes = GTK_BOX(inner)->children; if (boxes) { child = boxes->data; box = child->widget; tab_add_sorted(cv, box, tab, ch); gtk_widget_queue_resize(inner->parent); return; } /* create a new family box */ if (cv->vertical) { /* vertical */ box = gtk_vbox_new(FALSE, 0); sep = gtk_hseparator_new(); } else { /* horiz */ box = gtk_hbox_new(FALSE, 0); sep = gtk_vseparator_new(); } gtk_box_pack_end(GTK_BOX(box), sep, 0, 0, 4); gtk_widget_show(sep); gtk_box_pack_start(GTK_BOX(inner), box, 0, 0, 0); gtk_box_pack_start(GTK_BOX(box), tab, 0, 0, 0); gtk_widget_show(tab); gtk_widget_show(box); gtk_widget_queue_resize(inner->parent); } static gboolean tab_ignore_cb(GtkWidget *widget, GdkEventCrossing * event, gpointer user_data) { return TRUE; } /* called when a tab is clicked (button down) */ static void tab_pressed_cb(GtkToggleButton * tab, chan * ch) { chan *old_tab; int is_switching = TRUE; chanview *cv = ch->cv; ignore_toggle = TRUE; /* de-activate the old tab */ old_tab = cv->focused; if (old_tab && old_tab->impl) { gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(old_tab->impl), FALSE); if (old_tab == ch) is_switching = FALSE; } gtk_toggle_button_set_active(tab, TRUE); ignore_toggle = FALSE; cv->focused = ch; if ( /*tab->active */ is_switching) /* call the focus callback */ cv->cb_focus(cv, ch, ch->tag, ch->userdata); } /* called for keyboard tab toggles only */ static void tab_toggled_cb(GtkToggleButton * tab, chan * ch) { if (ignore_toggle) return; /* activated a tab via keyboard */ tab_pressed_cb(tab, ch); } static gboolean tab_click_cb(GtkWidget *wid, GdkEventButton * event, chan * ch) { return ch->cv->cb_contextmenu(ch->cv, ch, ch->tag, ch->userdata, event); } static void *cv_tabs_add(chanview * cv, chan * ch, char *name, GtkTreeIter * parent) { GtkWidget *but; but = gtk_toggle_button_new_with_label(name); gtk_widget_set_name(but, "xchat-tab"); g_object_set_data(G_OBJECT(but), "c", ch); /* used to trap right-clicks */ g_signal_connect(G_OBJECT(but), "button_press_event", G_CALLBACK(tab_click_cb), ch); /* avoid prelights */ g_signal_connect(G_OBJECT(but), "enter_notify_event", G_CALLBACK(tab_ignore_cb), NULL); g_signal_connect(G_OBJECT(but), "leave_notify_event", G_CALLBACK(tab_ignore_cb), NULL); g_signal_connect(G_OBJECT(but), "pressed", G_CALLBACK(tab_pressed_cb), ch); /* for keyboard */ g_signal_connect(G_OBJECT(but), "toggled", G_CALLBACK(tab_toggled_cb), ch); g_object_set_data(G_OBJECT(but), "u", ch->userdata); tab_add_real(cv, but, ch); return but; } static int tab_group_for_each_tab(chanview * cv, int (*callback) (GtkWidget *tab, int num, int usernum), int usernum) { GList *tabs; GList *boxes; GtkBoxChild *child; GtkBox *innerbox; int i; innerbox = (GtkBox *) ((tabview *) cv)->inner; boxes = innerbox->children; i = 0; while (boxes) { child = boxes->data; tabs = GTK_BOX(child->widget)->children; while (tabs) { child = tabs->data; if (!GTK_IS_SEPARATOR(child->widget)) { if (callback(child->widget, i, usernum) != -1) return i; i++; } tabs = tabs->next; } boxes = boxes->next; } return i; } static int tab_check_focus_cb(GtkWidget *tab, int num, int unused) { if (GTK_TOGGLE_BUTTON(tab)->active) return num; return -1; } /* returns the currently focused tab number */ static int tab_group_get_cur_page(chanview * cv) { return tab_group_for_each_tab(cv, tab_check_focus_cb, 0); } static void cv_tabs_focus(chan * ch) { if (ch->impl) /* focus the new one (tab_pressed_cb defocuses the old one) */ tab_pressed_cb(GTK_TOGGLE_BUTTON(ch->impl), ch); } static int tab_focus_num_cb(GtkWidget *tab, int num, int want) { if (num == want) { cv_tabs_focus(g_object_get_data(G_OBJECT(tab), "c")); return 1; } return -1; } static void cv_tabs_change_orientation(chanview * cv) { /* cleanup the old one */ if (cv->func_cleanup) cv->func_cleanup(cv); /* now rebuild a new tabbar or tree */ cv->func_init(cv); chanview_populate(cv); } /* switch to the tab number specified */ static void cv_tabs_move_focus(chanview * cv, gboolean relative, int num) { int i, max; if (relative) { max = cv->size; i = tab_group_get_cur_page(cv) + num; /* make it wrap around at both ends */ if (i < 0) i = max - 1; if (i >= max) i = 0; tab_group_for_each_tab(cv, tab_focus_num_cb, i); return; } tab_group_for_each_tab(cv, tab_focus_num_cb, num); } static void cv_tabs_remove(chan * ch) { gtk_widget_destroy(ch->impl); ch->impl = NULL; cv_tabs_prune(ch->cv); } static void cv_tabs_move(chan * ch, int delta) { int i, pos = 0; GList *list; GtkWidget *parent = ((GtkWidget *)ch->impl)->parent; i = 0; for (list = GTK_BOX(parent)->children; list; list = list->next) { GtkBoxChild *child_entry; child_entry = list->data; if (child_entry->widget == ch->impl) pos = i; i++; } pos = (pos - delta) % i; gtk_box_reorder_child(GTK_BOX(parent), ch->impl, pos); } static void cv_tabs_cleanup(chanview * cv) { if (cv->box) gtk_widget_destroy(((tabview *) cv)->outer); } static void cv_tabs_set_color(chan * ch, PangoAttrList * list) { gtk_label_set_attributes(GTK_LABEL(GTK_BIN(ch->impl)->child), list); } static void cv_tabs_rename(chan * ch, char *name) { PangoAttrList *attr; GtkWidget *tab = ch->impl; attr = gtk_label_get_attributes(GTK_LABEL(GTK_BIN(tab)->child)); if (attr) pango_attr_list_ref(attr); gtk_button_set_label(GTK_BUTTON(tab), name); gtk_widget_queue_resize(tab->parent->parent->parent); if (attr) { gtk_label_set_attributes(GTK_LABEL(GTK_BIN(tab)->child), attr); pango_attr_list_unref(attr); } } static gboolean cv_tabs_is_collapsed(chan * ch) { return FALSE; } static chan *cv_tabs_get_parent(chan * ch) { return NULL; } // vim:syn=c ekg2-0.4~pre+20120506.1/plugins/gtk/chanview-tree.inc000066400000000000000000000216021175142753400216600ustar00rootroot00000000000000/* file included in chanview.c */ typedef struct { GtkTreeView *tree; GtkWidget *scrollw; /* scrolledWindow */ } treeview; #include "main.h" #if 0 #include "../common/xchat.h" #include "../common/xchatc.h" #include "fe-gtk.h" #include "maingui.h" #endif #include #include static void /* row-activated, when a row is double clicked */ cv_tree_activated_cb(GtkTreeView * view, GtkTreePath * path, GtkTreeViewColumn * column, gpointer data) { if (gtk_tree_view_row_expanded(view, path)) gtk_tree_view_collapse_row(view, path); else gtk_tree_view_expand_row(view, path, FALSE); } static void /* row selected callback */ cv_tree_sel_cb(GtkTreeSelection * sel, chanview * cv) { GtkTreeModel *model; GtkTreeIter iter; chan *ch; if (gtk_tree_selection_get_selected(sel, &model, &iter)) { gtk_tree_model_get(model, &iter, COL_CHAN, &ch, -1); cv->focused = ch; cv->cb_focus(cv, ch, ch->tag, ch->userdata); } } static gboolean cv_tree_click_cb(GtkTreeView * tree, GdkEventButton * event, chanview * cv) { chan *ch; GtkTreeSelection *sel; GtkTreePath *path; GtkTreeIter iter; int ret = FALSE; if (event->button != 3 && event->state == 0) return FALSE; sel = gtk_tree_view_get_selection(tree); if (gtk_tree_view_get_path_at_pos(tree, event->x, event->y, &path, 0, 0, 0)) { if (event->button == 2) { gtk_tree_selection_unselect_all(sel); gtk_tree_selection_select_path(sel, path); } if (gtk_tree_model_get_iter(GTK_TREE_MODEL(cv->store), &iter, path)) { gtk_tree_model_get(GTK_TREE_MODEL(cv->store), &iter, COL_CHAN, &ch, -1); ret = cv->cb_contextmenu(cv, ch, ch->tag, ch->userdata, event); } gtk_tree_path_free(path); } return ret; } static void cv_tree_init(chanview * cv) { GtkWidget *view, *win; GtkCellRenderer *renderer; static const GtkTargetEntry dnd_src_target[] = { {"XCHAT_CHANVIEW", GTK_TARGET_SAME_APP, 75} }; static const GtkTargetEntry dnd_dest_target[] = { {"XCHAT_USERLIST", GTK_TARGET_SAME_APP, 75} }; win = gtk_scrolled_window_new(0, 0); /*gtk_container_set_border_width (GTK_CONTAINER (win), 1); */ gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(win), GTK_SHADOW_IN); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(win), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); gtk_container_add(GTK_CONTAINER(cv->box), win); gtk_widget_show(win); view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(cv->store)); gtk_widget_set_name(view, "xchat-tree"); if (cv->style) gtk_widget_set_style(view, cv->style); /*gtk_widget_modify_base (view, GTK_STATE_NORMAL, &colors[COL_BG]); */ GTK_WIDGET_UNSET_FLAGS(view, GTK_CAN_FOCUS); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE); #if GTK_CHECK_VERSION(2,10,0) if (!(gui_tweaks_config & 8)) gtk_tree_view_set_enable_tree_lines(GTK_TREE_VIEW(view), TRUE); #endif gtk_container_add(GTK_CONTAINER(win), view); /* icon column */ if (cv->use_icons) { renderer = gtk_cell_renderer_pixbuf_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, NULL, renderer, "pixbuf", COL_PIXBUF, NULL); } /* main column */ renderer = gtk_cell_renderer_text_new(); gtk_cell_renderer_text_set_fixed_height_from_font(GTK_CELL_RENDERER_TEXT(renderer), 1); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), -1, NULL, renderer, "text", COL_NAME, "attributes", COL_ATTR, NULL); g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(view))), "changed", G_CALLBACK(cv_tree_sel_cb), cv); g_signal_connect(G_OBJECT(view), "button-press-event", G_CALLBACK(cv_tree_click_cb), cv); g_signal_connect(G_OBJECT(view), "row-activated", G_CALLBACK(cv_tree_activated_cb), NULL); gtk_drag_dest_set(view, GTK_DEST_DEFAULT_ALL, dnd_dest_target, 1, GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK); gtk_drag_source_set(view, GDK_BUTTON1_MASK, dnd_src_target, 1, GDK_ACTION_COPY); #ifdef DARK g_signal_connect(G_OBJECT(view), "drag_begin", G_CALLBACK(mg_drag_begin_cb), NULL); g_signal_connect(G_OBJECT(view), "drag_drop", G_CALLBACK(mg_drag_drop_cb), NULL); g_signal_connect(G_OBJECT(view), "drag_motion", G_CALLBACK(mg_drag_motion_cb), NULL); g_signal_connect(G_OBJECT(view), "drag_end", G_CALLBACK(mg_drag_end_cb), NULL); #endif ((treeview *) cv)->tree = GTK_TREE_VIEW(view); ((treeview *) cv)->scrollw = win; gtk_widget_show(view); } static void cv_tree_postinit(chanview * cv) { gtk_tree_view_expand_all(((treeview *) cv)->tree); } static void *cv_tree_add(chanview * cv, chan * ch, char *name, GtkTreeIter * parent) { GtkTreePath *path; if (parent) { /* expand the parent node */ path = gtk_tree_model_get_path(GTK_TREE_MODEL(cv->store), parent); if (path) { gtk_tree_view_expand_row(((treeview *) cv)->tree, path, FALSE); gtk_tree_path_free(path); } } return NULL; } static void cv_tree_change_orientation(chanview * cv) { } static void cv_tree_focus(chan * ch) { GtkTreeView *tree = ((treeview *) ch->cv)->tree; GtkTreeModel *model = gtk_tree_view_get_model(tree); GtkTreePath *path; GtkTreeIter parent; GdkRectangle cell_rect; GdkRectangle vis_rect; gint dest_y; /* expand the parent node */ if (gtk_tree_model_iter_parent(model, &parent, &ch->iter)) { path = gtk_tree_model_get_path(model, &parent); if (path) { /* This full section does what * gtk_tree_view_scroll_to_cell (tree, path, NULL, TRUE, 0.5, 0.5); * does, except it only scrolls the window if the provided cell is * not visible. Basic algorithm taken from gtktreeview.c */ /* obtain information to see if the cell is visible */ gtk_tree_view_get_background_area(tree, path, NULL, &cell_rect); gtk_tree_view_get_visible_rect(tree, &vis_rect); /* The cordinates aren't offset correctly */ gtk_tree_view_widget_to_tree_coords(tree, cell_rect.x, cell_rect.y, NULL, &cell_rect.y ); /* only need to scroll if out of bounds */ if (cell_rect.y < vis_rect.y || cell_rect.y + cell_rect.height > vis_rect.y + vis_rect.height) { dest_y = cell_rect.y - ((vis_rect.height - cell_rect.height) * 0.5); if (dest_y < 0) dest_y = 0; gtk_tree_view_scroll_to_point(tree, -1, dest_y); } /* theft done, now make it focused like */ gtk_tree_view_set_cursor(tree, path, NULL, FALSE); gtk_tree_path_free(path); } } path = gtk_tree_model_get_path(model, &ch->iter); if (path) { gtk_tree_view_scroll_to_cell(tree, path, NULL, TRUE, 0.5, 0.5); gtk_tree_view_set_cursor(tree, path, NULL, FALSE); gtk_tree_path_free(path); } } static void cv_tree_move_focus(chanview * cv, gboolean relative, int num) { chan *ch; if (relative) { num += cv_find_number_of_chan(cv, cv->focused); num %= cv->size; /* make it wrap around at both ends */ if (num < 0) num = cv->size - 1; } ch = cv_find_chan_by_number(cv, num); if (ch) cv_tree_focus(ch); } static void cv_tree_remove(chan * ch) { } static void move_row(chan * ch, int delta, GtkTreeIter * parent) { GtkTreeStore *store = ch->cv->store; GtkTreeIter *src = &ch->iter; GtkTreeIter dest = ch->iter; GtkTreePath *dest_path; if (delta < 0) { /* down */ if (gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &dest)) gtk_tree_store_swap(store, src, &dest); else /* move to top */ gtk_tree_store_move_after(store, src, NULL); } else { dest_path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &dest); if (gtk_tree_path_prev(dest_path)) { gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &dest, dest_path); gtk_tree_store_swap(store, src, &dest); } else { /* move to bottom */ gtk_tree_store_move_before(store, src, NULL); } gtk_tree_path_free(dest_path); } } static void cv_tree_move(chan * ch, int delta) { GtkTreeIter parent; /* do nothing if this is a server row */ if (gtk_tree_model_iter_parent(GTK_TREE_MODEL(ch->cv->store), &parent, &ch->iter)) move_row(ch, delta, &parent); } static void cv_tree_cleanup(chanview * cv) { if (cv->box) /* kill the scrolled window */ gtk_widget_destroy(((treeview *) cv)->scrollw); } static void cv_tree_set_color(chan * ch, PangoAttrList * list) { /* nothing to do, it's already set in the store */ } static void cv_tree_rename(chan * ch, char *name) { /* nothing to do, it's already renamed in the store */ } static chan *cv_tree_get_parent(chan * ch) { chan *parent_ch = NULL; GtkTreeIter parent; if (gtk_tree_model_iter_parent(GTK_TREE_MODEL(ch->cv->store), &parent, &ch->iter)) { gtk_tree_model_get(GTK_TREE_MODEL(ch->cv->store), &parent, COL_CHAN, &parent_ch, -1); } return parent_ch; } static gboolean cv_tree_is_collapsed(chan * ch) { chan *parent = cv_tree_get_parent(ch); GtkTreePath *path = NULL; gboolean ret; if (parent == NULL) return FALSE; path = gtk_tree_model_get_path(GTK_TREE_MODEL(parent->cv->store), &parent->iter); ret = !gtk_tree_view_row_expanded(((treeview *) parent->cv)->tree, path); gtk_tree_path_free(path); return ret; } // vim:syn=c ekg2-0.4~pre+20120506.1/plugins/gtk/chanview.c000066400000000000000000000307251175142753400204020ustar00rootroot00000000000000/* abstract channel view: tabs or tree or anything you like */ #include "ekg2.h" #include #include #include #include "chanview.h" #include "gtkutil.h" /* treeStore columns */ #define COL_NAME 0 /* (char *) */ #define COL_CHAN 1 /* (chan *) */ #define COL_ATTR 2 /* (PangoAttrList *) */ #define COL_PIXBUF 3 /* (GdkPixbuf *) */ struct _chanview { /* impl scratch area */ char implscratch[sizeof (void *) * 8]; GtkTreeStore *store; int size; /* number of channels in view */ GtkWidget *box; /* the box we destroy when changing implementations */ GtkStyle *style; /* style used for tree */ chan *focused; /* currently focused channel */ int trunc_len; /* callbacks */ void (*cb_focus) (chanview *, chan *, int tag, void *userdata); void (*cb_xbutton) (chanview *, chan *, int tag, void *userdata); gboolean (*cb_contextmenu) (chanview *, chan *, int tag, void *userdata, GdkEventButton *); int (*cb_compare) (void *a, void *b); /* impl */ void (*func_init) (chanview *); void (*func_postinit) (chanview *); void *(*func_add) (chanview *, chan *, char *, GtkTreeIter *); void (*func_move_focus) (chanview *, gboolean, int); void (*func_change_orientation) (chanview *); void (*func_remove) (chan *); void (*func_move) (chan *, int delta); void (*func_focus) (chan *); void (*func_set_color) (chan *, PangoAttrList *); void (*func_rename) (chan *, char *); gboolean (*func_is_collapsed) (chan *); chan *(*func_get_parent) (chan *); void (*func_cleanup) (chanview *); unsigned int sorted:1; unsigned int vertical:1; unsigned int use_icons:1; }; struct _chan { chanview *cv; /* our owner */ GtkTreeIter iter; void *userdata; /* session * */ void *impl; /* togglebutton or null */ GdkPixbuf *icon; short allow_closure; /* allow it to be closed when it still has children? */ short tag; }; static chan *cv_find_chan_by_number (chanview *cv, int num); static int cv_find_number_of_chan (chanview *cv, chan *find_ch); /* ======= TABS ======= */ #include "chanview-tabs.inc" /* ======= TREE ======= */ #include "chanview-tree.inc" /* ==== ABSTRACT CHANVIEW ==== */ static char * truncate_tab_name (char *name, int max) { char *buf; if (max > 2 && g_utf8_strlen (name, -1) > max) { /* truncate long channel names */ buf = malloc (strlen (name) + 4); strcpy (buf, name); g_utf8_offset_to_pointer (buf, max)[0] = 0; strcat (buf, ".."); return buf; } return name; } /* iterate through a model, into 1 depth of children */ static void model_foreach_1 (GtkTreeModel *model, void (*func)(void *, GtkTreeIter *), void *userdata) { GtkTreeIter iter, inner; if (gtk_tree_model_get_iter_first (model, &iter)) { do { func (userdata, &iter); if (gtk_tree_model_iter_children (model, &inner, &iter)) { do func (userdata, &inner); while (gtk_tree_model_iter_next (model, &inner)); } } while (gtk_tree_model_iter_next (model, &iter)); } } static void chanview_pop_cb (chanview *cv, GtkTreeIter *iter) { chan *ch; char *name; PangoAttrList *attr; gtk_tree_model_get (GTK_TREE_MODEL (cv->store), iter, COL_NAME, &name, COL_CHAN, &ch, COL_ATTR, &attr, -1); ch->impl = cv->func_add (cv, ch, name, NULL); if (attr) { cv->func_set_color (ch, attr); pango_attr_list_unref (attr); } g_free (name); } static void chanview_populate (chanview *cv) { model_foreach_1 (GTK_TREE_MODEL (cv->store), (void *)chanview_pop_cb, cv); } void chanview_set_impl (chanview *cv, int type) { /* cleanup the old one */ if (cv->func_cleanup) cv->func_cleanup (cv); switch (type) { case 0: cv->func_init = cv_tabs_init; cv->func_postinit = cv_tabs_postinit; cv->func_add = cv_tabs_add; cv->func_move_focus = cv_tabs_move_focus; cv->func_change_orientation = cv_tabs_change_orientation; cv->func_remove = cv_tabs_remove; cv->func_move = cv_tabs_move; cv->func_focus = cv_tabs_focus; cv->func_set_color = cv_tabs_set_color; cv->func_rename = cv_tabs_rename; cv->func_is_collapsed = cv_tabs_is_collapsed; cv->func_get_parent = cv_tabs_get_parent; cv->func_cleanup = cv_tabs_cleanup; break; default: cv->func_init = cv_tree_init; cv->func_postinit = cv_tree_postinit; cv->func_add = cv_tree_add; cv->func_move_focus = cv_tree_move_focus; cv->func_change_orientation = cv_tree_change_orientation; cv->func_remove = cv_tree_remove; cv->func_move = cv_tree_move; cv->func_focus = cv_tree_focus; cv->func_set_color = cv_tree_set_color; cv->func_rename = cv_tree_rename; cv->func_is_collapsed = cv_tree_is_collapsed; cv->func_get_parent = cv_tree_get_parent; cv->func_cleanup = cv_tree_cleanup; break; } /* now rebuild a new tabbar or tree */ cv->func_init (cv); chanview_populate (cv); cv->func_postinit (cv); /* force re-focus */ if (cv->focused) cv->func_focus (cv->focused); } static void chanview_free_ch (chanview *cv, GtkTreeIter *iter) { chan *ch; gtk_tree_model_get (GTK_TREE_MODEL (cv->store), iter, COL_CHAN, &ch, -1); free (ch); } static void chanview_destroy_store (chanview *cv) /* free every (chan *) in the store */ { model_foreach_1 (GTK_TREE_MODEL (cv->store), (void *)chanview_free_ch, cv); g_object_unref (cv->store); } static void chanview_destroy (chanview *cv) { if (cv->func_cleanup) cv->func_cleanup (cv); if (cv->box) gtk_widget_destroy (cv->box); chanview_destroy_store (cv); free (cv); } static void chanview_box_destroy_cb (GtkWidget *box, chanview *cv) { cv->box = NULL; chanview_destroy (cv); } chanview * chanview_new (int type, int trunc_len, gboolean sort, gboolean use_icons, GtkStyle *style) { chanview *cv; cv = calloc (1, sizeof (chanview)); cv->store = gtk_tree_store_new (4, G_TYPE_STRING, G_TYPE_POINTER, PANGO_TYPE_ATTR_LIST, GDK_TYPE_PIXBUF); cv->style = style; cv->box = gtk_hbox_new (0, 0); cv->trunc_len = trunc_len; cv->sorted = sort; cv->use_icons = use_icons; gtk_widget_show (cv->box); chanview_set_impl (cv, type); g_signal_connect (G_OBJECT (cv->box), "destroy", G_CALLBACK (chanview_box_destroy_cb), cv); return cv; } /* too lazy for signals */ void chanview_set_callbacks (chanview *cv, void (*cb_focus) (chanview *, chan *, int tag, void *userdata), void (*cb_xbutton) (chanview *, chan *, int tag, void *userdata), gboolean (*cb_contextmenu) (chanview *, chan *, int tag, void *userdata, GdkEventButton *), int (*cb_compare) (void *a, void *b)) { cv->cb_focus = cb_focus; cv->cb_xbutton = cb_xbutton; cv->cb_contextmenu = cb_contextmenu; cv->cb_compare = cb_compare; } static chan * chanview_add_real (chanview *cv, char *name, void *userdata, gboolean allow_closure, int tag, GdkPixbuf *icon, chan *ch, chan *avoid) { GtkTreeIter parent_iter; GtkTreeIter iter; gboolean has_parent = FALSE; gtk_tree_store_append (cv->store, &iter, NULL); if (!ch) { ch = calloc (1, sizeof (chan)); ch->userdata = userdata; ch->cv = cv; ch->allow_closure = allow_closure; ch->tag = tag; ch->icon = icon; } memcpy (&(ch->iter), &iter, sizeof (iter)); gtk_tree_store_set (cv->store, &iter, COL_NAME, name, COL_CHAN, ch, COL_PIXBUF, icon, -1); cv->size++; if (!has_parent) ch->impl = cv->func_add (cv, ch, name, NULL); else ch->impl = cv->func_add (cv, ch, name, &parent_iter); return ch; } chan * chanview_add (chanview *cv, char *name, void *userdata, gboolean allow_closure, int tag, GdkPixbuf *icon) { char *new_name; chan *ret; new_name = truncate_tab_name (name, cv->trunc_len); ret = chanview_add_real (cv, new_name, userdata, allow_closure, tag, icon, NULL, NULL); if (new_name != name) free (new_name); return ret; } int chanview_get_size (chanview *cv) { return cv->size; } GtkWidget * chanview_get_box (chanview *cv) { return cv->box; } void chanview_move_focus (chanview *cv, gboolean relative, int num) { cv->func_move_focus (cv, relative, num); } GtkOrientation chanview_get_orientation (chanview *cv) { return (cv->vertical ? GTK_ORIENTATION_VERTICAL : GTK_ORIENTATION_HORIZONTAL); } void chanview_set_orientation (chanview *cv, gboolean vertical) { if (vertical != cv->vertical) { cv->vertical = vertical; cv->func_change_orientation (cv); } } int chan_get_tag (chan *ch) { return ch->tag; } void * chan_get_userdata (chan *ch) { return ch->userdata; } void chan_focus (chan *ch) { if (ch->cv->focused == ch) return; ch->cv->func_focus (ch); } void chan_move (chan *ch, int delta) { ch->cv->func_move (ch, delta); } void chan_set_color (chan *ch, PangoAttrList *list) { gtk_tree_store_set (ch->cv->store, &ch->iter, COL_ATTR, list, -1); ch->cv->func_set_color (ch, list); } void chan_rename (chan *ch, char *name, int trunc_len) { char *new_name; new_name = truncate_tab_name (name, trunc_len); gtk_tree_store_set (ch->cv->store, &ch->iter, COL_NAME, new_name, -1); ch->cv->func_rename (ch, new_name); ch->cv->trunc_len = trunc_len; if (new_name != name) free (new_name); } /* this thing is overly complicated */ static int cv_find_number_of_chan (chanview *cv, chan *find_ch) { GtkTreeIter iter, inner; chan *ch; int i = 0; if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (cv->store), &iter)) { do { gtk_tree_model_get (GTK_TREE_MODEL (cv->store), &iter, COL_CHAN, &ch, -1); if (ch == find_ch) return i; i++; if (gtk_tree_model_iter_children (GTK_TREE_MODEL (cv->store), &inner, &iter)) { do { gtk_tree_model_get (GTK_TREE_MODEL (cv->store), &inner, COL_CHAN, &ch, -1); if (ch == find_ch) return i; i++; } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (cv->store), &inner)); } } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (cv->store), &iter)); } return 0; /* WARNING */ } /* this thing is overly complicated too */ static chan * cv_find_chan_by_number (chanview *cv, int num) { GtkTreeIter iter, inner; chan *ch; int i = 0; if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (cv->store), &iter)) { do { if (i == num) { gtk_tree_model_get (GTK_TREE_MODEL (cv->store), &iter, COL_CHAN, &ch, -1); return ch; } i++; if (gtk_tree_model_iter_children (GTK_TREE_MODEL (cv->store), &inner, &iter)) { do { if (i == num) { gtk_tree_model_get (GTK_TREE_MODEL (cv->store), &inner, COL_CHAN, &ch, -1); return ch; } i++; } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (cv->store), &inner)); } } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (cv->store), &iter)); } return NULL; } static void chan_emancipate_children (chan *ch) { char *name; chan *childch; GtkTreeIter childiter; PangoAttrList *attr; while (gtk_tree_model_iter_children (GTK_TREE_MODEL (ch->cv->store), &childiter, &ch->iter)) { /* remove and re-add all the children, but avoid using "ch" as parent */ gtk_tree_model_get (GTK_TREE_MODEL (ch->cv->store), &childiter, COL_NAME, &name, COL_CHAN, &childch, COL_ATTR, &attr, -1); ch->cv->func_remove (childch); gtk_tree_store_remove (ch->cv->store, &childiter); ch->cv->size--; chanview_add_real (childch->cv, name, childch->userdata, childch->allow_closure, childch->tag, childch->icon, childch, ch); if (attr) { childch->cv->func_set_color (childch, attr); pango_attr_list_unref (attr); } g_free (name); } } gboolean chan_remove (chan *ch, gboolean force) { chan *new_ch; int i, num; if (ui_quit) /* avoid lots of looping on exit */ return TRUE; /* is this ch allowed to be closed while still having children? */ if (!force && gtk_tree_model_iter_has_child (GTK_TREE_MODEL (ch->cv->store), &ch->iter) && !ch->allow_closure) return FALSE; chan_emancipate_children (ch); ch->cv->func_remove (ch); /* is it the focused one? */ if (ch->cv->focused == ch) { ch->cv->focused = NULL; /* try to move the focus to some other valid channel */ num = cv_find_number_of_chan (ch->cv, ch); /* move to the one left of the closing tab */ new_ch = cv_find_chan_by_number (ch->cv, num - 1); if (new_ch && new_ch != ch) { chan_focus (new_ch); /* this'll will set ch->cv->focused for us too */ } else { /* if it fails, try focus from tab 0 and up */ for (i = 0; i < ch->cv->size; i++) { new_ch = cv_find_chan_by_number (ch->cv, i); if (new_ch && new_ch != ch) { chan_focus (new_ch); /* this'll will set ch->cv->focused for us too */ break; } } } } ch->cv->size--; gtk_tree_store_remove (ch->cv->store, &ch->iter); free (ch); return TRUE; } gboolean chan_is_collapsed (chan *ch) { return ch->cv->func_is_collapsed (ch); } chan * chan_get_parent (chan *ch) { return ch->cv->func_get_parent (ch); } ekg2-0.4~pre+20120506.1/plugins/gtk/chanview.h000066400000000000000000000025161175142753400204040ustar00rootroot00000000000000 typedef struct _chanview chanview; typedef struct _chan chan; chanview *chanview_new (int type, int trunc_len, gboolean sort, gboolean use_icons, GtkStyle *style); void chanview_set_callbacks (chanview *cv, void (*cb_focus) (chanview *, chan *, int tag, void *userdata), void (*cb_xbutton) (chanview *, chan *, int tag, void *userdata), gboolean (*cb_contextmenu) (chanview *, chan *, int tag, void *userdata, GdkEventButton *), int (*cb_compare) (void *a, void *b)); void chanview_set_impl (chanview *cv, int type); chan *chanview_add (chanview *cv, char *name, void *userdata, gboolean allow_closure, int tag, GdkPixbuf *icon); int chanview_get_size (chanview *cv); GtkWidget *chanview_get_box (chanview *cv); void chanview_move_focus (chanview *cv, gboolean relative, int num); GtkOrientation chanview_get_orientation (chanview *cv); void chanview_set_orientation (chanview *cv, gboolean vertical); int chan_get_tag (chan *ch); void *chan_get_userdata (chan *ch); void chan_focus (chan *ch); void chan_move (chan *ch, int delta); void chan_set_color (chan *ch, PangoAttrList *list); void chan_rename (chan *ch, char *new_name, int trunc_len); gboolean chan_remove (chan *ch, gboolean force); gboolean chan_is_collapsed (chan *ch); chan * chan_get_parent (chan *ch); #define FOCUS_NEW_ALL 1 #define FOCUS_NEW_ONLY_ASKED 2 #define FOCUS_NEW_NONE 0 ekg2-0.4~pre+20120506.1/plugins/gtk/gtkutil.c000066400000000000000000000420541175142753400202570ustar00rootroot00000000000000/* X-Chat * Copyright (C) 1998 Peter Zelezny. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #include "ekg2.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* xstrcmp */ #include "main.h" #include "gtkutil.h" #if 0 #include "pixmaps.h" /* gtkutil.c, just some gtk wrappers */ extern void path_part(char *file, char *path, int pathlen); struct file_req { GtkWidget *dialog; void *userdata; filereqcallback callback; int flags; /* FRF_* flags */ }; static char last_dir[256] = ""; static void gtkutil_file_req_destroy(GtkWidget *wid, struct file_req *freq) { freq->callback(freq->userdata, NULL); free(freq); } static void gtkutil_check_file(char *file, struct file_req *freq) { struct stat st; int axs = FALSE; path_part(file, last_dir, sizeof(last_dir)); /* check if the file is readable or writable */ if (freq->flags & FRF_WRITE) { if (access(last_dir, W_OK) == 0) axs = TRUE; } else { if (stat(file, &st) != -1) { if (!S_ISDIR(st.st_mode) || (freq->flags & FRF_CHOOSEFOLDER)) axs = TRUE; } } if (axs) { char *utf8_file; /* convert to UTF8. It might be converted back to locale by server.c's g_convert */ utf8_file = xchat_filename_to_utf8(file, -1, NULL, NULL, NULL); if (utf8_file) { freq->callback(freq->userdata, utf8_file); g_free(utf8_file); } else { fe_message("Filename encoding is corrupt.", FE_MSG_ERROR); } } else { if (freq->flags & FRF_WRITE) fe_message(_("Cannot write to that file."), FE_MSG_ERROR); else fe_message(_("Cannot read that file."), FE_MSG_ERROR); } } static void gtkutil_file_req_done(GtkWidget *wid, struct file_req *freq) { GSList *files, *cur; GtkFileChooser *fs = GTK_FILE_CHOOSER(freq->dialog); if (freq->flags & FRF_MULTIPLE) { files = cur = gtk_file_chooser_get_filenames(fs); while (cur) { gtkutil_check_file(cur->data, freq); g_free(cur->data); cur = cur->next; } if (files) g_slist_free(files); } else { if (freq->flags & FRF_CHOOSEFOLDER) gtkutil_check_file(gtk_file_chooser_get_current_folder(fs), freq); else gtkutil_check_file(gtk_file_chooser_get_filename(fs), freq); } /* this should call the "destroy" cb, where we free(freq) */ gtk_widget_destroy(freq->dialog); } static void gtkutil_file_req_response(GtkWidget *dialog, gint res, struct file_req *freq) { switch (res) { case GTK_RESPONSE_ACCEPT: gtkutil_file_req_done(dialog, freq); break; case GTK_RESPONSE_CANCEL: /* this should call the "destroy" cb, where we free(freq) */ gtk_widget_destroy(freq->dialog); } } void gtkutil_file_req(const char *title, void *callback, void *userdata, char *filter, int flags) { struct file_req *freq; GtkWidget *dialog; extern char *get_xdir_fs(void); if (flags & FRF_WRITE) { dialog = gtk_file_chooser_dialog_new(title, NULL, GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL); if (filter && filter[0]) { /* filter becomes initial name when saving */ char temp[1024]; path_part(filter, temp, sizeof(temp)); gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), temp); gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), file_part(filter)); } #if GTK_CHECK_VERSION(2,8,0) if (!(flags & FRF_NOASKOVERWRITE)) gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE); #endif } else dialog = gtk_file_chooser_dialog_new(title, NULL, GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL); if (flags & FRF_MULTIPLE) gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE); if (last_dir[0]) gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), last_dir); if (flags & FRF_ADDFOLDER) gtk_file_chooser_add_shortcut_folder(GTK_FILE_CHOOSER(dialog), get_xdir_fs(), NULL); if (flags & FRF_CHOOSEFOLDER) { gtk_file_chooser_set_action(GTK_FILE_CHOOSER(dialog), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER); gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), filter); } else { if (filter && (flags & FRF_FILTERISINITIAL)) gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), filter); } freq = malloc(sizeof(struct file_req)); freq->dialog = dialog; freq->flags = flags; freq->callback = callback; freq->userdata = userdata; g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(gtkutil_file_req_response), freq); g_signal_connect(G_OBJECT(dialog), "destroy", G_CALLBACK(gtkutil_file_req_destroy), (gpointer) freq); gtk_widget_show(dialog); } void gtkutil_destroy(GtkWidget *igad, GtkWidget *dgad) { gtk_widget_destroy(dgad); } static void gtkutil_get_str_response(GtkDialog * dialog, gint arg1, gpointer entry) { void (*callback) (int cancel, char *text, void *user_data); char *text; void *user_data; text = (char *)gtk_entry_get_text(GTK_ENTRY(entry)); callback = g_object_get_data(G_OBJECT(dialog), "cb"); user_data = g_object_get_data(G_OBJECT(dialog), "ud"); switch (arg1) { case GTK_RESPONSE_REJECT: callback(TRUE, text, user_data); gtk_widget_destroy(GTK_WIDGET(dialog)); break; case GTK_RESPONSE_ACCEPT: callback(FALSE, text, user_data); gtk_widget_destroy(GTK_WIDGET(dialog)); break; } } static void gtkutil_str_enter(GtkWidget *entry, GtkWidget *dialog) { gtk_dialog_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT); } void fe_get_str(char *msg, char *def, void *callback, void *userdata) { GtkWidget *dialog; GtkWidget *entry; GtkWidget *hbox; GtkWidget *label; dialog = gtk_dialog_new_with_buttons(msg, NULL, 0, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL); gtk_box_set_homogeneous(GTK_BOX(GTK_DIALOG(dialog)->vbox), TRUE); gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE); hbox = gtk_hbox_new(TRUE, 0); g_object_set_data(G_OBJECT(dialog), "cb", callback); g_object_set_data(G_OBJECT(dialog), "ud", userdata); entry = gtk_entry_new(); g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(gtkutil_str_enter), dialog); gtk_entry_set_text(GTK_ENTRY(entry), def); gtk_box_pack_end(GTK_BOX(hbox), entry, 0, 0, 0); label = gtk_label_new(msg); gtk_box_pack_end(GTK_BOX(hbox), label, 0, 0, 0); g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(gtkutil_get_str_response), entry); gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), hbox); gtk_widget_show_all(dialog); } static void gtkutil_get_number_response(GtkDialog * dialog, gint arg1, gpointer spin) { void (*callback) (int cancel, int value, void *user_data); int num; void *user_data; num = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin)); callback = g_object_get_data(G_OBJECT(dialog), "cb"); user_data = g_object_get_data(G_OBJECT(dialog), "ud"); switch (arg1) { case GTK_RESPONSE_REJECT: callback(TRUE, num, user_data); gtk_widget_destroy(GTK_WIDGET(dialog)); break; case GTK_RESPONSE_ACCEPT: callback(FALSE, num, user_data); gtk_widget_destroy(GTK_WIDGET(dialog)); break; } } void fe_get_int(char *msg, int def, void *callback, void *userdata) { GtkWidget *dialog; GtkWidget *spin; GtkWidget *hbox; GtkWidget *label; GtkAdjustment *adj; dialog = gtk_dialog_new_with_buttons(msg, NULL, 0, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL); gtk_box_set_homogeneous(GTK_BOX(GTK_DIALOG(dialog)->vbox), TRUE); gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE); hbox = gtk_hbox_new(TRUE, 0); g_object_set_data(G_OBJECT(dialog), "cb", callback); g_object_set_data(G_OBJECT(dialog), "ud", userdata); spin = gtk_spin_button_new(NULL, 1, 0); adj = gtk_spin_button_get_adjustment((GtkSpinButton *) spin); adj->lower = 0; adj->upper = 1024; adj->step_increment = 1; gtk_adjustment_changed(adj); gtk_spin_button_set_value((GtkSpinButton *) spin, def); gtk_box_pack_end(GTK_BOX(hbox), spin, 0, 0, 0); label = gtk_label_new(msg); gtk_box_pack_end(GTK_BOX(hbox), label, 0, 0, 0); g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(gtkutil_get_number_response), spin); gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), hbox); gtk_widget_show_all(dialog); } #endif GtkWidget * gtkutil_button(GtkWidget *box, char *stock, char *tip, void *callback, void *userdata, char *labeltext) { GtkWidget *wid, *img, *bbox; wid = gtk_button_new(); if (labeltext) { gtk_button_set_label(GTK_BUTTON(wid), labeltext); gtk_button_set_image(GTK_BUTTON(wid), gtk_image_new_from_stock(stock, GTK_ICON_SIZE_MENU)); gtk_button_set_use_underline(GTK_BUTTON(wid), TRUE); if (box) gtk_container_add(GTK_CONTAINER(box), wid); } else { bbox = gtk_hbox_new(0, 0); gtk_container_add(GTK_CONTAINER(wid), bbox); gtk_widget_show(bbox); img = gtk_image_new_from_stock(stock, GTK_ICON_SIZE_MENU); if (!xstrcmp(stock, GTK_STOCK_GOTO_LAST)) gtk_widget_set_usize(img, 10, 6); gtk_container_add(GTK_CONTAINER(bbox), img); gtk_widget_show(img); gtk_box_pack_start(GTK_BOX(box), wid, 0, 0, 0); } g_signal_connect(G_OBJECT(wid), "clicked", G_CALLBACK(callback), userdata); gtk_widget_show(wid); if (tip) add_tip(wid, tip); return wid; } #if 0 void gtkutil_label_new(char *text, GtkWidget *box) { GtkWidget *label = gtk_label_new(text); gtk_container_add(GTK_CONTAINER(box), label); gtk_widget_show(label); } GtkWidget * gtkutil_entry_new(int max, GtkWidget *box, void *callback, gpointer userdata) { GtkWidget *entry = gtk_entry_new_with_max_length(max); gtk_container_add(GTK_CONTAINER(box), entry); if (callback) g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(callback), userdata); gtk_widget_show(entry); return entry; } GtkWidget * gtkutil_clist_new(int columns, char *titles[], GtkWidget *box, int policy, void *select_callback, gpointer select_userdata, void *unselect_callback, gpointer unselect_userdata, int selection_mode) { GtkWidget *clist, *win; win = gtk_scrolled_window_new(0, 0); gtk_container_add(GTK_CONTAINER(box), win); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(win), GTK_POLICY_AUTOMATIC, policy); gtk_widget_show(win); if (titles) clist = gtk_clist_new_with_titles(columns, titles); else clist = gtk_clist_new(columns); gtk_clist_set_selection_mode(GTK_CLIST(clist), selection_mode); gtk_clist_column_titles_passive(GTK_CLIST(clist)); gtk_container_add(GTK_CONTAINER(win), clist); if (select_callback) { g_signal_connect(G_OBJECT(clist), "select_row", G_CALLBACK(select_callback), select_userdata); } if (unselect_callback) { g_signal_connect(G_OBJECT(clist), "unselect_row", G_CALLBACK(unselect_callback), unselect_userdata); } gtk_widget_show(clist); return clist; } int gtkutil_clist_selection(GtkWidget *clist) { if (GTK_CLIST(clist)->selection) return GPOINTER_TO_INT(GTK_CLIST(clist)->selection->data); return -1; } static int int_compare(const int *elem1, const int *elem2) { return (*elem1) - (*elem2); } int gtkutil_clist_multiple_selection(GtkWidget *clist, int **rows, const int max_rows) { int i = 0; GList *tmp_clist; *rows = malloc(sizeof(int) * max_rows); memset(*rows, -1, max_rows * sizeof(int)); for (tmp_clist = GTK_CLIST(clist)->selection; tmp_clist && i < max_rows; tmp_clist = tmp_clist->next, i++) { (*rows)[i] = GPOINTER_TO_INT(tmp_clist->data); } qsort(*rows, i, sizeof(int), (void *)int_compare); return i; } #endif void add_tip(GtkWidget *wid, char *text) { static GtkTooltips *tip = NULL; if (!tip) tip = gtk_tooltips_new(); gtk_tooltips_set_tip(tip, wid, text, 0); } #if 0 void show_and_unfocus(GtkWidget *wid) { GTK_WIDGET_UNSET_FLAGS(wid, GTK_CAN_FOCUS); gtk_widget_show(wid); } #endif void gtkutil_set_icon(GtkWidget *win) { #if 0 gtk_window_set_icon(GTK_WINDOW(win), pix_xchat); #endif } extern GtkWidget *parent_window; /* maingui.c */ GtkWidget * gtkutil_window_new(char *title, char *role, int width, int height, int flags) { GtkWidget *win; win = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtkutil_set_icon(win); gtk_window_set_title(GTK_WINDOW(win), title); gtk_window_set_default_size(GTK_WINDOW(win), width, height); gtk_window_set_role(GTK_WINDOW(win), role); if (flags & 1) gtk_window_set_position(GTK_WINDOW(win), GTK_WIN_POS_MOUSE); if ((flags & 2) && parent_window) { gtk_window_set_type_hint(GTK_WINDOW(win), GDK_WINDOW_TYPE_HINT_DIALOG); gtk_window_set_transient_for(GTK_WINDOW(win), GTK_WINDOW(parent_window)); } return win; } #if 0 /* pass NULL as selection to paste to both clipboard & X11 text */ void gtkutil_copy_to_clipboard(GtkWidget *widget, GdkAtom selection, const gchar * str) { GtkWidget *win; GtkClipboard *clip, *clip2; win = gtk_widget_get_toplevel(GTK_WIDGET(widget)); if (GTK_WIDGET_TOPLEVEL(win)) { int len = strlen(str); if (selection) { clip = gtk_widget_get_clipboard(win, selection); gtk_clipboard_set_text(clip, str, len); } else { /* copy to both primary X selection and clipboard */ clip = gtk_widget_get_clipboard(win, GDK_SELECTION_PRIMARY); clip2 = gtk_widget_get_clipboard(win, GDK_SELECTION_CLIPBOARD); gtk_clipboard_set_text(clip, str, len); gtk_clipboard_set_text(clip2, str, len); } } } /* Treeview util functions */ GtkWidget * gtkutil_treeview_new(GtkWidget *box, GtkTreeModel * model, GtkTreeCellDataFunc mapper, ...) { GtkWidget *win, *view; GtkCellRenderer *renderer = NULL; GtkTreeViewColumn *col; va_list args; int col_id = 0; GType type; char *title, *attr; win = gtk_scrolled_window_new(0, 0); gtk_container_add(GTK_CONTAINER(box), win); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(win), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_widget_show(win); view = gtk_tree_view_new_with_model(model); /* the view now has a ref on the model, we can unref it */ g_object_unref(G_OBJECT(model)); gtk_container_add(GTK_CONTAINER(win), view); va_start(args, mapper); for (col_id = va_arg(args, int); col_id != -1; col_id = va_arg(args, int)) { type = gtk_tree_model_get_column_type(model, col_id); switch (type) { case G_TYPE_BOOLEAN: renderer = gtk_cell_renderer_toggle_new(); attr = "active"; break; case G_TYPE_STRING: /* fall through */ default: renderer = gtk_cell_renderer_text_new(); attr = "text"; break; } title = va_arg(args, char *); if (mapper) { /* user-specified function to set renderer attributes */ col = gtk_tree_view_column_new_with_attributes(title, renderer, NULL); gtk_tree_view_column_set_cell_data_func(col, renderer, mapper, GINT_TO_POINTER(col_id), NULL); } else { /* just set the typical attribute for this type of renderer */ col = gtk_tree_view_column_new_with_attributes(title, renderer, attr, col_id, NULL); } gtk_tree_view_append_column(GTK_TREE_VIEW(view), col); } va_end(args); return view; } gboolean gtkutil_treemodel_string_to_iter(GtkTreeModel * model, gchar * pathstr, GtkTreeIter * iter_ret) { GtkTreePath *path = gtk_tree_path_new_from_string(pathstr); gboolean success; success = gtk_tree_model_get_iter(model, iter_ret, path); gtk_tree_path_free(path); return success; } /*gboolean gtkutil_treeview_get_selected_iter (GtkTreeView *view, GtkTreeIter *iter_ret) { GtkTreeModel *store; GtkTreeSelection *select; select = gtk_tree_view_get_selection (view); return gtk_tree_selection_get_selected (select, &store, iter_ret); }*/ gboolean gtkutil_treeview_get_selected(GtkTreeView * view, GtkTreeIter * iter_ret, ...) { GtkTreeModel *store; GtkTreeSelection *select; gboolean has_selected; va_list args; select = gtk_tree_view_get_selection(view); has_selected = gtk_tree_selection_get_selected(select, &store, iter_ret); if (has_selected) { va_start(args, iter_ret); gtk_tree_model_get_valist(store, iter_ret, args); va_end(args); } return has_selected; } #endif ekg2-0.4~pre+20120506.1/plugins/gtk/gtkutil.h000066400000000000000000000033631175142753400202640ustar00rootroot00000000000000#include #include typedef void (*filereqcallback) (void *, char *file); #define FRF_WRITE 1 #define FRF_MULTIPLE 2 #define FRF_ADDFOLDER 4 #define FRF_CHOOSEFOLDER 8 #define FRF_FILTERISINITIAL 16 #define FRF_NOASKOVERWRITE 32 #if 0 void gtkutil_file_req(const char *title, void *callback, void *userdata, char *filter, int flags); void gtkutil_destroy(GtkWidget *igad, GtkWidget *dgad); void gtkutil_label_new(char *text, GtkWidget *box); GtkWidget *gtkutil_entry_new(int max, GtkWidget *box, void *callback, gpointer userdata); GtkWidget *gtkutil_clist_new(int columns, char *titles[], GtkWidget *box, int policy, void *select_callback, gpointer select_userdata, void *unselect_callback, gpointer unselect_userdata, int selection_mode); int gtkutil_clist_selection(GtkWidget *clist); int gtkutil_clist_multiple_selection(GtkWidget *clist, int **rows, const int max_rows); void show_and_unfocus(GtkWidget *wid); void gtkutil_set_icon(GtkWidget *win); void gtkutil_copy_to_clipboard(GtkWidget *widget, GdkAtom selection, const gchar * str); GtkWidget *gtkutil_treeview_new(GtkWidget *box, GtkTreeModel * model, GtkTreeCellDataFunc mapper, ...); gboolean gtkutil_treemodel_string_to_iter(GtkTreeModel * model, gchar * pathstr, GtkTreeIter * iter_ret); gboolean gtkutil_treeview_get_selected_iter(GtkTreeView * view, GtkTreeIter * iter_ret); gboolean gtkutil_treeview_get_selected(GtkTreeView * view, GtkTreeIter * iter_ret, ...); #endif GtkWidget *gtkutil_window_new(char *title, char *role, int width, int height, int flags); GtkWidget *gtkutil_button(GtkWidget *box, char *stock, char *tip, void *callback, void *userdata, char *labeltext); void add_tip(GtkWidget *wid, char *text); ekg2-0.4~pre+20120506.1/plugins/gtk/iconssets/000077500000000000000000000000001175142753400204355ustar00rootroot00000000000000ekg2-0.4~pre+20120506.1/plugins/gtk/iconssets/COPYING000066400000000000000000000012521175142753400214700ustar00rootroot00000000000000iconsets copied from psi-0.12 sources, from iconsets/roster/* avail.png, away.png, dnd.png, ffc.png xa.png, invisible.png, notavail.png, icon_error.png, icon_unknown.png from crystal-roster.jisp gg-avail.png, gg-away.png, gg-invisible.png, gg-notavail.png from crystal-gadu.jisp icq-avail.png icq-away.png icq-dnd.png icq-ffc.png icq-invisible.png icq-notavail.png icq-unknown.png icq-xa.png from crystal-icq.jisp Each jisp under licence: The icons in this iconset are copyright by Remko Tronçon, and are made available under the terms of Lesser General Public License. They are based on the 'Crystal project icons', copyright 2006-2007 Everaldo Coelho, under the same license. ekg2-0.4~pre+20120506.1/plugins/gtk/iconssets/avail.png000066400000000000000000000012751175142753400222440ustar00rootroot00000000000000PNG  IHDRabKGD pHYs  ~tIME#$釉JIDATxuOQPRZ" JQѨݹs!хl4cB/`E .nܘ+!1Q-?h X"P`0sQ`'9;{ )%daU6\z6GؾПyU/U|C 4!Xоc1{^ _yerPvJuoڷ@j=l:>͕΄8tbJ[q jZXXrs+9#dLRbLD讫QeEa &Cqų &:8EhIa&,J$$=P2}=I*WuzOu=Ơ3 ywu;[ ٽ_;7^&7#h5-=9PvZh4Eeh=vR'/467Իe%,}s5G##ʝ&On*/}ŀBx#Mar wjNRʋGnl>zXToP(ܣpK^} rO?[* 'V0tLᣏӑH*#*Tm3q}Z"! H%F>`"6",%p] C"(j?`j/a͹MWr4x' F.g!˦Q7{Sb_SyPxQy"*!έI9m-3}ɗ?R(ByiVKe .k j>#?PPm{(s9ɳ,,YXڅsC>h…rŌLZ>] MquS0BsLZ_F:9 Q@LW#&4fL6a}b_j}lSN`f<xeӘvtݩz8hJ`)Du=Q嗑0oX.m@ÙYKwh$FOhLO+ysXkҵL]c%'V -1} nN@l-b:gw:g9g?uʗM4]7IENDB`ekg2-0.4~pre+20120506.1/plugins/gtk/iconssets/gg-avail.png000066400000000000000000000016601175142753400226350ustar00rootroot00000000000000PNG  IHDRa pHYs  d_gAMA|Q cHRMz%u0`:o_F&IDATxb`_Y3jU  l;O(·[@,`V gG5 ?~927b_!~}ɠW 0c` lN ҡ Yz30|xv> 7߿ :#i3k 5?Ƈ@~9ïG~^{C>>5>ρW ?1pZ0|oG6bJ51@3g'y^`C>|0udgPaxa0G} ?~:C݁Mlo< ͵GE4sfW? 8@!?A Ãy 1)RF / tz `Çg$O>ex*x(7b?'3]w?/Xue/_7{Z 2OHƯ} ~2```a`~&?gpa×`@ S(@LX8DÇ; /R6pr ) PJa?:~y=Ph`A) '$zQ5Pg>0xHlP"RAĈ+ C@ 0\wqIENDB`ekg2-0.4~pre+20120506.1/plugins/gtk/iconssets/gg-away.png000066400000000000000000000016701175142753400225030ustar00rootroot00000000000000PNG  IHDRagAMA|Q cHRMz%u0`:o_FbKGD pHYs  d_IDATxu_lSu?mv+E 0B=1D4̇a,J2y$˦(/h#dMl.3-k!-]׻v?L19O|=ߓ 3{|B9CڳԉhݍQ5P5/9yI) ;V7/^߷(79XKO%:TJ:9-ڵm"݁W(v/4IfJ7Y4i%٨ xaxS~IVJEf/i2}D0~+EFwDDlmhfvlGRl&H^+G_Qyjo8⾄T:NDy9pI-в*_8d~opqI_܄aF~@p P|{F\_Q ˸V&s[E$.ͫ{[VHFh*ymyuԚc箆oex@wfG-qۣpJy޻ոanEv6)>yXI#B#.[{yxl0'EDX|q>a<'ĔKq)DyAr=?rI [N=˝-fpLL8䃇~Jgde+i$ x1B umyLYk22v[,I,6 e8N ّ87Nڃ-t8j *sD#JpLIENDB`ekg2-0.4~pre+20120506.1/plugins/gtk/iconssets/gg-invisible.png000066400000000000000000000016561175142753400235320ustar00rootroot00000000000000PNG  IHDRagAMA|Q cHRMz%u0`:o_FbKGD pHYs  d_IDATxu_HdwgqFD츊#!]Y-a^'zXz2#!--OnKάڨ8Cs{g9=,c;<||nv?lU-4M*,Y$;RѪXw_-5Coćm%̓x8+hX=ب4ն|3ֆx'Nsttm[\c{K(Y6vXyr~~^JJ"ڒp8,JcyM~;곳b۶R)I$+dR$K8͛>-w\3~gOfYNOOQU?v/c ʙYr6eh4g``=GEɦۋ.q0 :r4KKKP &E5J!"b18PEQX__@ ~2wOUAD'B!,a!wHrrtvvrrra466'Yv_4MRKK gggn|;]_ݾv&<Ѫʿx`0w/; p=1g{GIENDB`ekg2-0.4~pre+20120506.1/plugins/gtk/iconssets/gg-notavail.png000066400000000000000000000014641175142753400233600ustar00rootroot00000000000000PNG  IHDRagAMA|Q cHRMz%u0`:o_FbKGD pHYs  d_IDATxuJ#Q?@j,, HE',FZ*l(H,BT2sf$i].ߙ?q~~"Vwww^j...AD0탃k.//--"4EתjԿIܪjID\cLjIU-UEUKb@|||(,Ucj OR~jiyyr`0>yvP_ DrLO' Cʏ̸UI033Obee x||$ET c=eg1~8v,..l0 1Ɛ$dȈHUDv$}677#JF!IG~Lor_bq*tWnnn}7WU qU `wwwd2:QtPU &N۳Evd[K6]0EED8MckDqkG@uT@sbc7よTjٙ'iq\qր 0a0b{{{ ɪ8ߺEvZ9IENDB`ekg2-0.4~pre+20120506.1/plugins/gtk/iconssets/icon_error.png000066400000000000000000000015351175142753400233100ustar00rootroot00000000000000PNG  IHDRabKGDC pHYs  ~IDATxeKL\ey]2PcC D[BjJ]h2Ѩ .uIc n j3Z*RH uJ20O{wuA)=-T xw!G vrp׻|7QiZk03wah=gVX'u90:7 [wμ£w8^zi0t6OmRcמ\])W}'6hJu:Eo"G>cj u:f 'o,K:f X9/,Ia1`fkE[FSeϞpk: @uv'?:[-+) LNX`n|z%ϟw9{*JR~&C_ˣ\z$Κ5yS^iL<\))J!E<5tcMdU7'V  ;ûpI92u]f ixk|iiPdj 0Ul޹Ã.{{{}2o~ig^}Gv ydxY{Ӷq lۦT*|y\xdd*b8{&J 666m\]Ey!~H' 80@oveqC!jGl ^}g##hw" ܺ}.j'OHy{Nh"pYbKuV t:v,xZ B ibϣ&!YRVaֶvww-X1:sx \JA<ί0] ,鴤^ņZV)$I$@6(,0$"IU8w.VOu_^ _-j8BIENDB`ekg2-0.4~pre+20120506.1/plugins/gtk/iconssets/icq-avail.png000066400000000000000000000017551175142753400230210ustar00rootroot00000000000000PNG  IHDRa pHYs  ~gAMA|Q cHRMz%u0`:o_FcIDATxbd&2J` > +?@1yd?P_ Q Llyp @C&273@&1(ppO4exvag 2 *& 00>p;o]b!AS!|7{ t?~.&k cp;? @3|z1;oyKa@'/FW @$>20]+D#Ë[@b`ЅhO^*pd@1L|#p43 O/30:13\e0`a`81˗~ 0"@1) I5la`Ps``0#`oA{ QW!a=r_E#߯ζ tko2) kv7>oG;@x~vI=e7`}İCAA3ñk< _0ct`o@0fVgcw6~0x p=:0d>l`Fṕ@1M2Qx?>1[2,``d@Ldo L_y>>``PQ0,Di: >  X) wnCq^`e4?@ t90 r 1ra*CC?9(00| -%_`BkvdXp 6@:u0 ?2- *Aa `4IENDB`ekg2-0.4~pre+20120506.1/plugins/gtk/iconssets/icq-away.png000066400000000000000000000016351175142753400226630ustar00rootroot00000000000000PNG  IHDRagAMA|Q cHRMz%u0`:o_FbKGD pHYs  ~IDATxu_LSNʥN3 4Y51F͐̐lOh|1*{ڋ.0,1S?UcVZ \io=O|'Gx]d? jk_a#P]?}zJgK$i^YpQ=}xO7E8 Uk0 9f?̢cgV UPKMz\~\JQ rQ<ҺЉGG2ILD2¿T4Ky8gҺz&*X5@ hf% W% Qx0AKƝ֖8G 9{ |3 ` ,/ń LeM,nneVǼR ytXkia ćᘒDvNQ9z%n4 i\q}YM^T<7dR}',gw8B֤l IqM2rW"B1:ϯQ3I]rHf DZj /h=CƸ PI`҅0t5߮Tn`*MdF}4ts/f5^ I6= V@)Pj;PD5ֶٿՃ)̃rbr9 zqx w4|`B{LHbxox˜^f,`G{& 5}Sb8VM;w,a7sэh/ t-)IENDB`ekg2-0.4~pre+20120506.1/plugins/gtk/iconssets/icq-dnd.png000066400000000000000000000015301175142753400224610ustar00rootroot00000000000000PNG  IHDRagAMA|Q cHRMz%u0`:o_FbKGD pHYs  ~IDATxuKh>&6)dۤmFj4{1=I#(^*hPѣ zEA5xCz(JڦUؐwyϮ|73Nq̉RڒW!mӞG>N2\n”+"VS[[G9`:qݩ/'4!d RK;`}t- Ru̎}7a zy=Q l^uͩ(˗E,My{: 5 z#jʡ0Z8:wxI 3}cCꝊt~]O~h薄~iԦ!qI!ڑl~ oH&G숅:yYRk刯 _N:ss稫T*榎04˺>1Ա(ď%W~g}z.2.#%XEXnb;޽2Z^q7 [YZN9JN/˽'O HRD4&!C3l%bNmj%\4-bׂ|nigU**+I&5sg?q)ۍbڢ<+o32x6b]MtW '| -2nV:N߶l IENDB`ekg2-0.4~pre+20120506.1/plugins/gtk/iconssets/icq-ffc.png000066400000000000000000000016411175142753400224550ustar00rootroot00000000000000PNG  IHDRagAMA|Q cHRMz%u0`:o_FbKGD pHYs  ~IDATxu]lSuO{N9ڍvm`@€D8bd@cP Ą;مo5,&$`K6`uX;֭Z=_^T~W#rlXX INI4?"Oq9`k09oZ~O3G駇 Zn>un>#X  -EL\oﯠи,ձe1DlI]ׂ7"U"ԑP`x}0gpfSZ5cS%f@ҿVO5Nwy$AH5&\\i`.zDGM}ʻ_`k+dP\b2I.λa8t\Hem]$|Sr*ɹN{urzLLH8N+lZHm<% vص?P{{p 0cgQȠ aelnG)O1 nr@)jĂHXʙ6&fmFUi7F?@{73%CԷH?U00hT`3 AvG-DJ|{Taޏ/(p2}> Q2\,[*@5Y,yAA:z@!&geFxc#n%EI.yMG)j9ЯIENDB`ekg2-0.4~pre+20120506.1/plugins/gtk/iconssets/icq-invisible.png000066400000000000000000000016371175142753400237100ustar00rootroot00000000000000PNG  IHDRagAMA|Q cHRMz%u0`:o_FbKGD pHYs  ~IDATxuOu?3,vAj b!&])1$4l(Ń'K#4!5B`*삛PCeŘ;};jDeTT1@޴$yx/8F֥g8bƬr#F7eCWX~m}9m/mt]yAdq+NENwsluuXN@9a XhZihs XmۨJTb||`&F-\fL&R nROeYNEl4Mz""gggt:t4|vM:&$yJd2;x<FZ0N BX u"l~D"d2""B^Dz,.T+,CgggjLS& 籰m۸ixB<vd28///(X^^< 84 h4`0Va6a"I&Hb6 k7Zkh4:K>XEF#Ri/H ˡ)jA jZZ?* zw.뢵>T*֟R_jy#d5IENDB`ekg2-0.4~pre+20120506.1/plugins/gtk/iconssets/icq-unknown.png000066400000000000000000000017211175142753400234150ustar00rootroot00000000000000PNG  IHDRagAMA|Q cHRMz%u0`:o_FbKGDC pHYs  ~5IDATxu=O[gQBKX22YP6RXNJ ,R%tJ"S'$*%H͇ؾ;Pg:yrGe2nNa}}Fө7]x<~kzzt:뺹OgffAJ}hnno_p mɹfggj RJ(Xi !DUH&_.BBZH$rY8cccb1Rʄy?r]t]ǡh l"`xx!BlƲ$>88011q/T019MCW!RR)Tz.w;&ǹy-'xc8;;YT@*‡Av =dx?x-p0mEQi H !i~7ap91ׯRevJPMd7ޕ+K~6gy18ʨ|aE1{'i?tE 4 \xꢳ8o/Ɩi]auDO[ܘЩ ěhBe,p.DpR `H} 4yس@'ꡯ^ Q1M)%"$Iߑr{+AX}TzF]j:LBB  h:V)LO^h(D# NR!M!h6Ah| _XZ䀢(x8Xo/`~ L  k׊mkmoǮ'(JT=gj\opCk XaߙȚu{e;ֽF`ټE~b+)M hpБ"jnss_28N"2_>UT2IENDB`ekg2-0.4~pre+20120506.1/plugins/gtk/iconssets/invisible.png000066400000000000000000000015501175142753400231300ustar00rootroot00000000000000PNG  IHDRabKGDC pHYs  ~IDATxmO[uC-d$0IPscq\(DҘ%K\ h?Eehb0/d%0[ڞ+z'Os{$R^vw|v+7>qPxU|.>:ǻ.g e/yn8g6]wEx$ұ"1=:"(xq3TWO*+ܷ'{F`l6K&.{9>y !qf>ůheibY(aDVu$׋^;/LLLH۶armmM˭-Ne2HDy_>%d"B`^immc&bUU4 Mp\b*E-1V/I*"Hlll055E:4{Y,]_J Vr9<ӌ( q\B!6o*mvjaH)YYY!LB077GKK PMhll.7?&DJIOOhnFFFg``r6qGb88 vRFGG|\.G}}=[,@4˲V(i8˲j1fKgg'Blf{{rGeCD"N ~RĮ/VNO᦮.wg^{LJ}ꗟ㕔ՊSKP'pdIENDB`ekg2-0.4~pre+20120506.1/plugins/gtk/iconssets/notavail.png000066400000000000000000000011521175142753400227570ustar00rootroot00000000000000PNG  IHDRabKGDC pHYs  ~ IDATx}ѱKA.p+؈ȩUY$ IT6Imce"I#T) 6&eD"z{;3;)^λ3jy~7Z *@`R<}p7?߄ǿOewaa va|uppnAJVZRewa2==Ԕh4V~uת*aEQ2BǙzmƴ֜9~y6VzV]^^zEQyH@kMfZoqa/pssC(>i2'R(>5oRjX$!M=8tv#oOdWa0ƘeZP"RTk1k-ZR8u !N,{zqqA$dYRtfffp=`nn}߭뜜jg}}=>>{WBxGGGq{{!ePggg{EkdE_4m?C& IENDB`ekg2-0.4~pre+20120506.1/plugins/gtk/iconssets/xa.png000066400000000000000000000015351175142753400215570ustar00rootroot00000000000000PNG  IHDRabKGDC pHYs  ~IDATxeKL\ey]2PcC D[BjJ]h2Ѩ .uIc n j3Z*RH uJ20O{wuA)=-T xw!G vrp׻|7QiZk03wah=gVX'u90:7 [wμ£w8^zi0t6OmRcמ\])W}'6hJu:Eo"G>cj u:f 'o,K:f X9/,Ia1`fkE[FSeϞpk: @uv'?:[-+) LNX`n|z%ϟw9{*JR~&C_ˣ\z$Κ5yS^iL<\))J!E<5tcMdU * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #include #include #include #include #include #include #include "main.h" #include "palette.h" #include "xtext.h" #include "bindings.h" #include "maingui.h" PLUGIN_DEFINE(gtk, PLUGIN_UI, NULL); /* config vars */ int mainwindow_width_config; int mainwindow_height_config; int gui_pane_left_size_config; int gui_tweaks_config; int tab_small_config; int tab_pos_config; int max_auto_indent_config; int thin_separator_config; int backlog_size_config; int show_marker_config; int tint_red_config; int tint_green_config; int tint_blue_config; int transparent_config; int wordwrap_config; int indent_nicks_config; int show_separator_config; int tab_layout_config; int gui_ulist_pos_config; int tab_pos_config; int contacts_config; char *font_normal_config; int ui_quit = 0; int gui_pane_left_size_config; int gui_pane_right_size_config; int new_window_in_tab_config = 1; /* TODO: * - wrzucic zmienne do variable_add() przynajmniej te wazne.. * - zrobic menu * - zrobic traya * - zrobic userliste * - zaimplementowac w xtext kolorki ekg2 [done, nie wszystkie atrybuty zrobione, ale xtext.c juz wie co to jest fstring_t * i ze ma tam atrybuty, see gtk_xtext_render_str()] * - timestamp powinien byc renderowany z config_timestamp, i zamieniany na fstring_t !!! * - implementowac wiecej waznych dla UI QUERY() * - brakuje funkcjonalnosci detach-okienka z XChata, trzeba przejrzec ten kod i dokonczyc. * - w XChacie byl jeszcze drag-drop np. plikow... jak ktos potrzebuje to wie co zrobic. * * - zaimplementowac do konca ten dialog zamykania ekg2-gtk i dodac do niego opcje zapisu... * zeby ekg2 sie na konsoli nie pytalo czy zapisac. */ /* BUGS: * - /window move, /window kill, itd.. nie dziala * - zabijanie okienek zle zrobione [czyt. w ogole nic nie dziala] * * - sa lagi, nie wszystko w gtk sie dzieje jak user rusza myszka, klika klawiszami... * niektore operacje sa robione zeby byly ,,animacja'' a select() 1s powoduje calkiem duze latencje. * watch wykonywany co 0.03s bylby calkiem dobrym pomyslem... * * - duzo innych, roznych bledow.. * - jak zrobimy detach okienek, to prawdopodobnie operacje beda robione na zlych oknach, * xchat mial current_tab, ja myslalem ze to jest to samo co window_current, wiec do pupy. */ /* XXX, here, we update whole window, it's enough to update only statusbar && headerbar */ #define _ncurses_update_statusbar(commit) mg_populate(window_current); static QUERY(gtk_ui_is_initialized) { int *tmp = va_arg(ap, int *); *tmp = (ui_quit == 0); return 0; } static QUERY(ekg2_gtk_loop) { extern void ekg_loop(); do { int i = 10; ekg_loop(); while (gtk_events_pending() && --i) // stupid hack to give ekg_loop() some love. gtk_main_iteration(); } while (ui_quit == 0); return -1; } static WATCHER(ekg2_xorg_watcher) { if (type || ui_quit == 1) return -1; /* do nothing.. successfully. it's just like readline_watch_stdin() to don't matter about select() latency... default 1s. yeah I know it's only for * communication between x'org server and gtk... gtk maybe want to do somethink else.. but we can provide it only by decreasing latency from 1s to for instance * 0.1s in select() or by creating another thread.. */ return 0; } static TIMER(ekg2_xorg_idle) { if (type) return -1; // it's enough if we just run it. // no harm done. // while (gtk_events_pending()) { // if (gtk_events_pending()) // gtk_main_iteration(); // } return 0; } void ekg_gtk_window_new(window_t *w) { /* fe_new_window() */ mg_changui_new(w, NULL, new_window_in_tab_config, 0); } static QUERY(gtk_ui_window_new) { /* fe_new_window() */ window_t *w = *(va_arg(ap, window_t **)); ekg_gtk_window_new(w); return 0; } int gtk_ui_window_switch_lock = 0; static QUERY(gtk_ui_window_switch) { /* XXX, fast implementation */ window_t *w = *(va_arg(ap, window_t **)); if (gtk_ui_window_switch_lock) return 0; gtk_ui_window_switch_lock = 1; mg_switch_page(FALSE, w->id); gtk_ui_window_switch_lock = 0; fe_set_tab_color(w, w->act); return 0; } static QUERY(gtk_ui_window_kill) { /* fe_session_callback() || fe_close_window() */ window_t *w = *(va_arg(ap, window_t **)); fe_close_window(w); return 0; } static QUERY(gtk_ui_window_print) { /* fe_print_text() */ window_t *w = *(va_arg(ap, window_t **)); const fstring_t *line = *(va_arg(ap, const fstring_t **)); gtk_xtext_append_fstring(gtk_private(w)->buffer, line); return 0; } static QUERY(gtk_ui_window_target_changed) { window_t *w = *(va_arg(ap, window_t **)); _ncurses_update_statusbar(1); fe_set_channel(w); return 0; } static QUERY(gtk_beep) { /* fe_beep() */ gdk_beep(); return -1; } static QUERY(gtk_ui_window_act_changed) { /* fe_set_tab_color() */ window_t *w; for (w = windows; w; w = w->next) fe_set_tab_color(w, w->act); return 0; } static QUERY(gtk_print_version) { char *ver = saprintf("GTK2 plugin for ekg2 ported from XChat gtk frontend ((C) Peter Zelezny) using gtk: %d.%d.%d.%d", gtk_major_version, gtk_minor_version, gtk_micro_version, gtk_binary_age); /* GTK_MAJOR_VERSION, GTK_MINOR_VERSION, GTK_MICRO_VERSION GTK_BINARY_AGE );*/ print("generic", ver); xfree(ver); return 0; } static QUERY(gtk_postinit) { mg_apply_setup(); mg_switch_page(FALSE, window_current->id); return 0; } static QUERY(gtk_setvar_default) { mainwindow_width_config = 640; mainwindow_height_config = 400; gui_pane_left_size_config = 100; gui_tweaks_config = 0; tab_small_config = 0; tab_pos_config = 0; transparent_config = 0; wordwrap_config = 1; indent_nicks_config = 1; show_separator_config = 1; show_marker_config = 1; thin_separator_config = 1; max_auto_indent_config = 256; backlog_size_config = 1000; tint_red_config = tint_green_config = tint_blue_config = 195; gui_ulist_pos_config = 3; tab_pos_config = 6; tab_layout_config = 2; xfree(font_normal_config); font_normal_config = xstrdup("Monospace 9"); contacts_config = 2; gui_pane_left_size_config = 100; gui_pane_right_size_config = 100; return 0; } static void gtk_tab_layout_change(const char *var) { mg_change_layout(tab_layout_config); } static QUERY(gtk_variable_changed) { char *name = *(va_arg(ap, char**)); if (!xstrcasecmp(name, "timestamp_show")) { mg_apply_setup(); } return 0; } static QUERY(gtk_userlist_changed) { mg_populate_userlist(window_current); return 0; } static QUERY(gtk_session_changed) { mg_populate(window_current); return 0; } static QUERY(gtk_statusbar_query) { _ncurses_update_statusbar(1); return 0; } static QUERY(gtk_ui_window_clear) { window_t *w = *(va_arg(ap, window_t **)); /* This is real clear, not ncurses-like */ gtk_xtext_clear(gtk_private(w)->buffer); return 0; } EXPORT int gtk_plugin_init(int prio) { const char ekg2_another_ui[] = "Masz uruchomione inne ui, aktualnie nie mozesz miec uruchomionych obu na raz... Jesli chcesz zmienic ui uzyj ekg2 -F gtk\n"; int is_UI = 0; int xfd; window_t *w; PLUGIN_CHECK_VER("gtk"); query_emit(NULL, "ui-is-initialized", &is_UI); if (is_UI) { debug(ekg2_another_ui); return -1; } /* fe_args() */ if (!(gtk_init_check(0, NULL))) return -1; /* fe_init() */ gtk_binding_init(); pixmaps_init(); #if 0 channelwin_pix = pixmap_load_from_file(prefs.background); input_style = create_input_style(gtk_style_new()); #endif /* xchat_init() */ /* fe_main() */ plugin_register(>k_plugin, prio); query_connect(>k_plugin, "ui-is-initialized", gtk_ui_is_initialized, NULL); /* aby __debug sie wyswietlalo */ query_connect(>k_plugin, "set-vars-default", gtk_setvar_default, NULL); query_emit(>k_plugin, "set-vars-default"); query_connect(>k_plugin, "config-postinit", gtk_postinit, NULL); query_connect(>k_plugin, "ui-loop", ekg2_gtk_loop, NULL); query_connect(>k_plugin, "plugin-print-version", gtk_print_version, NULL); query_connect(>k_plugin, "ui-beep", gtk_beep, NULL); /* fe_beep() */ query_connect(>k_plugin, "ui-window-new", gtk_ui_window_new, NULL); /* fe_new_window() */ query_connect(>k_plugin, "ui-window-print", gtk_ui_window_print, NULL); /* fe_print_text() */ query_connect(>k_plugin, "ui-window-act-changed", gtk_ui_window_act_changed, NULL);/* fe_set_tab_color() */ query_connect(>k_plugin, "ui-window-kill", gtk_ui_window_kill, NULL); /* fe_session_callback() */ query_connect(>k_plugin, "ui-window-switch", gtk_ui_window_switch, NULL); query_connect(>k_plugin, "ui-window-target-changed", gtk_ui_window_target_changed, NULL); /* fe_set_channel() */ query_connect(>k_plugin, "ui-window-clear", gtk_ui_window_clear, NULL); query_connect(>k_plugin, "session-changed", gtk_session_changed, NULL); query_connect(>k_plugin, "session-event", gtk_statusbar_query, NULL); query_connect(>k_plugin, "session-renamed", gtk_statusbar_query, NULL); query_connect(>k_plugin, "variable-changed", gtk_variable_changed, NULL); query_connect(>k_plugin, "userlist-changed", gtk_userlist_changed, NULL); query_connect(>k_plugin, "userlist-added", gtk_userlist_changed, NULL); query_connect(>k_plugin, "userlist-removed", gtk_userlist_changed, NULL); query_connect(>k_plugin, "userlist-renamed", gtk_userlist_changed, NULL); query_connect(>k_plugin, "session-event", gtk_userlist_changed, NULL); query_connect(>k_plugin, "ui-window-refresh", gtk_userlist_changed, NULL); query_connect(>k_plugin, "userlist-refresh", gtk_userlist_changed, NULL); /* query_connect(&ncurses_plugin, "ui-window-refresh", ncurses_ui_window_refresh, NULL); query_connect(&ncurses_plugin, "ui-window-update-lastlog", ncurses_ui_window_lastlog, NULL); query_connect(&ncurses_plugin, "session-added", ncurses_statusbar_query, NULL); query_connect(&ncurses_plugin, "session-removed", ncurses_statusbar_query, NULL); query_connect(&ncurses_plugin, "binding-set", ncurses_binding_set_query, NULL); query_connect(&ncurses_plugin, "binding-command", ncurses_binding_adddelete_query, NULL); query_connect(&ncurses_plugin, "binding-default", ncurses_binding_default, NULL); query_connect(&ncurses_plugin, "variable-changed", ncurses_variable_changed, NULL); query_connect(&ncurses_plugin, "conference-renamed", ncurses_conference_renamed, NULL); */ query_connect(>k_plugin, "metacontact-added", gtk_userlist_changed, NULL); query_connect(>k_plugin, "metacontact-removed", gtk_userlist_changed, NULL); query_connect(>k_plugin, "metacontact-item-added", gtk_userlist_changed, NULL); query_connect(>k_plugin, "metacontact-item-removed", gtk_userlist_changed, NULL); #define gtk_backlog_change NULL /* gtk_backlog_change == NULL, need research */ variable_add(>k_plugin, ("backlog_size"), VAR_INT, 1, &backlog_size_config, gtk_backlog_change, NULL, NULL); variable_add(>k_plugin, ("tab_layout"), VAR_INT, 1, &tab_layout_config, gtk_tab_layout_change, NULL, NULL); /* XXX, variable_map() 0 -> 2-> */ xfd = XConnectionNumber(gdk_x11_get_default_xdisplay()); printf("[HELLO ekg2-GTK] XFD: %d\n", xfd); if (xfd != -1) watch_add(>k_plugin, xfd, WATCH_READ, ekg2_xorg_watcher, NULL); timer_add_ms(>k_plugin, "gtk-updater", 50, 1, ekg2_xorg_idle, NULL); for (w = windows; w; w = w->next) ekg_gtk_window_new(w); memset(gtk_history, 0, sizeof(gtk_history)); return 0; } static int gtk_plugin_destroy() { int i; for (i = 0; i < HISTORY_MAX; i++) { xfree(gtk_history[i]); gtk_history[i] = NULL; } plugin_unregister(>k_plugin); return 0; } ekg2-0.4~pre+20120506.1/plugins/gtk/main.h000066400000000000000000000053551175142753400175300ustar00rootroot00000000000000#include #include #include extern int ui_quit; extern plugin_t gtk_plugin; typedef struct { GtkWidget *xtext, *vscrollbar, *window, /* toplevel */ *topic_entry, *note_book, *main_table, *user_tree, /* GtkTreeView */ *user_box, /* userlist box */ *dialogbutton_box, *topicbutton_box, *topic_bar, *hpane_left, *hpane_right, *vpane_left, *vpane_right, *menu, *bar, /* connecting progress bar */ *nick_box, /* contains label to the left of input_box */ *nick_label, *op_xpm, /* icon to the left of nickname */ *namelistinfo, /* label above userlist */ *input_box; #define MENU_ID_NUM 12 GtkWidget *menu_item[MENU_ID_NUM + 1]; /* some items we may change state of */ void *chanview; /* chanview.h */ int pane_left_size; /*last position of the pane */ int pane_right_size; guint16 is_tab; /* is tab or toplevel? */ guint16 ul_hidden; /* userlist hidden? */ } gtk_window_ui_t; typedef struct { gtk_window_ui_t *gui; void *tab; /* (chan *) */ /* information stored when this tab isn't front-most */ void *user_model; /* for filling the GtkTreeView */ void *buffer; /* xtext_Buffer */ gfloat old_ul_value; /* old userlist value (for adj) */ } gtk_window_t; /* config */ extern int mainwindow_width_config; extern int mainwindow_height_config; extern int gui_pane_left_size_config; extern int gui_tweaks_config; extern int tab_small_config; extern int tab_pos_config; extern int max_auto_indent_config; extern int thin_separator_config; extern int show_marker_config; extern int tint_red_config; extern int tint_green_config; extern int tint_blue_config; extern int transparent_config; extern int wordwrap_config; extern int indent_nicks_config; extern int show_separator_config; extern char *font_normal_config; extern int transparent_config; extern int gui_ulist_pos_config; extern int tab_pos_config; extern int tab_layout_config; extern int contacts_config; extern int backlog_size_config; extern int gui_pane_left_size_config; extern int gui_pane_right_size_config; extern int new_window_in_tab_config; #define hidemenu_config 0 #define topicbar_config 1 #define mainwindow_left_config 0 #define mainwindow_top_config 0 #define newtabstofront_config 2 #define gtk_private_ui(w) (((gtk_window_t*) w->priv_data)->gui) #define gtk_private(w) ((gtk_window_t*) w->priv_data) #define gui_win_state_config 0 #define truncchans_config 20 #define tab_sort_config 1 #define style_namelistgad_config 0 #define chanmodebuttons_config -1 #define gui_quit_dialog_config -1 #define FOCUS_NEW_ALL 1 #define FOCUS_NEW_ONLY_ASKED 2 #define paned_userlist_config 0 /* XXX xchat def: 1 */ #define style_inputbox_config 0 /* XXX xchat commented def: 1 */ extern int gtk_ui_window_switch_lock; ekg2-0.4~pre+20120506.1/plugins/gtk/maingui.c000066400000000000000000001754341175142753400202360ustar00rootroot00000000000000/* X-Chat * Copyright (C) 1998-2005 Peter Zelezny. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ /* * port to ekg2: * Copyright (C) 2007 Jakub Zawadzki * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #define USE_XLIB #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "main.h" #include "xtext.h" #include "gtkutil.h" #include "palette.h" #include "menu.h" #include "chanview.h" #include "bindings.h" #include "userlistgui.h" #include "maingui.h" #include "userlistgui.h" #if 0 #include "../common/xchat.h" #include "../common/fe.h" #include "../common/server.h" #include "../common/xchatc.h" #include "../common/outbound.h" #include "../common/inbound.h" #include "../common/plugin.h" #include "../common/modes.h" #include "../common/url.h" #include "fe-gtk.h" #include "banlist.h" #include "joind.h" #include "maingui.h" #include "pixmaps.h" #include "plugin-tray.h" #endif #define GUI_SPACING (3) #define GUI_BORDER (0) #define SCROLLBAR_SPACING (2) enum { POS_INVALID = 0, POS_TOPLEFT = 1, POS_BOTTOMLEFT = 2, POS_TOPRIGHT = 3, POS_BOTTOMRIGHT = 4, POS_TOP = 5, /* for tabs only */ POS_BOTTOM = 6, POS_HIDDEN = 7 }; /* two different types of tabs */ #define TAG_IRC 0 /* server, channel, dialog */ #define TAG_UTIL 1 /* dcc, notify, chanlist */ static void mg_link_irctab(window_t *sess, int focus); static void mg_create_entry(window_t *sess, GtkWidget *box); static gtk_window_ui_t static_mg_gui; static gtk_window_ui_t *mg_gui = NULL; /* the shared irc tab */ GtkWidget *parent_window = NULL; /* the master window */ GtkStyle *input_style; static chan *active_tab = NULL; /* active tab */ static PangoAttrList *away_list; static PangoAttrList *newdata_list; static PangoAttrList *nickseen_list; static PangoAttrList *newmsg_list; static PangoAttrList *plain_list = NULL; #define NO_SESSION "no session" const char *gtk_session_target(session_t *sess) { if (!sess) return NO_SESSION; if (sess->alias) return sess->alias; return sess->uid; } const char *gtk_window_target(window_t *window) { if (!window) return ""; if (window->target) return window->target; else if (window->id == 1) return "__status"; else if (window->id == 0) return "__debug"; else return ""; } static PangoAttrList *mg_attr_list_create(GdkColor *col, int size) { PangoAttribute *attr; PangoAttrList *list; list = pango_attr_list_new(); if (col) { attr = pango_attr_foreground_new(col->red, col->green, col->blue); attr->start_index = 0; attr->end_index = 0xffff; pango_attr_list_insert(list, attr); } if (size > 0) { attr = pango_attr_scale_new(size == 1 ? PANGO_SCALE_SMALL : PANGO_SCALE_X_SMALL); attr->start_index = 0; attr->end_index = 0xffff; pango_attr_list_insert(list, attr); } return list; } static void mg_create_tab_colors(void) { if (plain_list) { pango_attr_list_unref(plain_list); pango_attr_list_unref(newmsg_list); pango_attr_list_unref(newdata_list); pango_attr_list_unref(nickseen_list); pango_attr_list_unref(away_list); } plain_list = mg_attr_list_create(NULL, tab_small_config); newdata_list = mg_attr_list_create(&colors[COL_NEW_DATA], tab_small_config); nickseen_list = mg_attr_list_create(&colors[COL_HILIGHT], tab_small_config); newmsg_list = mg_attr_list_create(&colors[COL_NEW_MSG], tab_small_config); away_list = mg_attr_list_create(&colors[COL_AWAY], FALSE); } #ifdef USE_XLIB #include static void set_window_urgency(GtkWidget *win, gboolean set) { XWMHints *hints; hints = XGetWMHints(GDK_WINDOW_XDISPLAY(win->window), GDK_WINDOW_XWINDOW(win->window)); if (set) hints->flags |= XUrgencyHint; else hints->flags &= ~XUrgencyHint; XSetWMHints(GDK_WINDOW_XDISPLAY(win->window), GDK_WINDOW_XWINDOW(win->window), hints); XFree(hints); } static void flash_window(GtkWidget *win) { set_window_urgency(win, TRUE); } static void unflash_window(GtkWidget *win) { set_window_urgency(win, FALSE); } #endif int fe_gui_info(window_t *sess, int info_type) { /* code from fe-gtk.c */ switch (info_type) { case 0: /* window status */ #if GTK_CHECK_VERSION(2,20,0) if (!gtk_widget_get_visible(GTK_WIDGET(gtk_private_ui(sess)->window))) #else if (!GTK_WIDGET_VISIBLE(GTK_WIDGET(gtk_private_ui(sess)->window))) #endif return 2; /* hidden (iconified or systray) */ #if GTK_CHECK_VERSION(2,4,0) if (gtk_window_is_active(GTK_WINDOW(gtk_private_ui(sess)->window))) #else #if GTK_CHECK_VERSION(2,2,0) if (GTK_WINDOW(gtk_private_ui(sess)->window)->is_active) #else if (0) /* ? */ #endif #endif return 1; /* active/focused */ return 0; /* normal (no keyboard focus or behind a window) */ } return -1; } /* flash the taskbar button */ void fe_flash_window(window_t *sess) { #if defined(WIN32) || defined(USE_XLIB) if (fe_gui_info(sess, 0) != 1) /* only do it if not focused */ flash_window(gtk_private_ui(sess)->window); #endif } /* set a tab plain, red, light-red, or blue */ void fe_set_tab_color(window_t *sess, int col) { if (!gtk_private_ui(sess)->is_tab) return; if (sess == window_current || sess->id == 0) col = 0; /* XXX */ // col value, what todo values comment. // 0: chan_set_color(sess->tab, plain_list); [new_data = NULL, msg_said = NULL, nick_said = NULL] /* no particular color (theme default) */ // 1: chan_set_color(sess->tab, newdata_list); [new_data = TRUE, msg_said = NULL, nick_said = NULL] /* new data has been displayed (dark red) */ // 2: chan_set_color(sess->tab, newmsg_list); [new_data = NULL, msg_said = TRUE, nick_said = NULL] /* new message arrived in channel (light red) */ // 3: chan_set_color(sess->tab, nickseen_list) ; [new_data = NULL, msg_said = NULL, nick_said = TRUE] /* your nick has been seen (blue) */ if (col == 0) chan_set_color(gtk_private(sess)->tab, plain_list); if (col == 1) chan_set_color(gtk_private(sess)->tab, newdata_list); if (col == 2) chan_set_color(gtk_private(sess)->tab, newmsg_list); } #if 0 static void mg_set_myself_away(gtk_window_ui_t *gui, gboolean away) { gtk_label_set_attributes(GTK_LABEL(GTK_BIN(gui->nick_label)->child), away ? away_list : NULL); } /* change the little icon to the left of your nickname */ void mg_set_access_icon(gtk_window_ui_t *gui, GdkPixbuf *pix, gboolean away) { if (gui->op_xpm) { if (pix == gtk_image_get_pixbuf (GTK_IMAGE (gui->op_xpm))) { /* no change? */ mg_set_myself_away (gui, away); return; } gtk_widget_destroy(gui->op_xpm); gui->op_xpm = NULL; } if (pix) { gui->op_xpm = gtk_image_new_from_pixbuf(pix); gtk_box_pack_start(GTK_BOX(gui->nick_box), gui->op_xpm, 0, 0, 0); gtk_widget_show(gui->op_xpm); } mg_set_myself_away(gui, away); } #endif static gboolean mg_inputbox_focus(GtkWidget *widget, GdkEventFocus *event, gtk_window_ui_t *gui) { window_t *w; if (gui->is_tab) return FALSE; for (w = windows; w; w = w->next) { if (gtk_private(w)->gui == gui) { /* window_switch() XXX */ window_switch(w->id); return FALSE; } } printf("mg_inputbox_focus() internal error!\n"); return FALSE; } void mg_inputbox_cb(GtkWidget *igad, gtk_window_ui_t *gui) { static int ignore = FALSE; window_t *sess = NULL; char *cmd, *p; if (ignore) return; cmd = GTK_ENTRY(igad)->text; if (cmd[0] == '\0') return; cmd = xstrdup(cmd); /* avoid recursive loop */ ignore = TRUE; gtk_entry_set_text(GTK_ENTRY(igad), ""); ignore = FALSE; /* where did this event come from? */ if (gui->is_tab) { sess = window_current; } else { window_t *w; for (w = windows; w; w = w->next) { if (gtk_private_ui(w) == gui) { sess = w; break; } } if (!sess) printf("FATAL, not found.\n"); } if (sess) { for (p=cmd; *p && isspace(*p); p++); if (*p || config_send_white_lines) command_exec(sess->target, sess->session, cmd, 0); if (config_history_savedups || xstrcmp(cmd, gtk_history[1])) { gtk_history[0] = cmd; xfree(gtk_history[HISTORY_MAX - 1]); memmove(>k_history[1], >k_history[0], sizeof(gtk_history) - sizeof(gtk_history[0])); gtk_history_index = 0; gtk_history[0] = NULL; } else xfree(cmd); return; } xfree(cmd); } void fe_set_title(window_t *sess) { gtk_window_ui_t *n = gtk_private_ui(sess); if (n->is_tab && sess != window_current) return; gtk_window_set_title(GTK_WINDOW(n->window), "ekg2"); } static gboolean mg_windowstate_cb(GtkWindow * wid, GdkEventWindowState * event, gpointer userdata) { #if 0 prefs.gui_win_state = 0; if (event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) prefs.gui_win_state = 1; if ((event->changed_mask & GDK_WINDOW_STATE_ICONIFIED) && (event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) && (prefs.gui_tray_flags & 4)) { tray_toggle_visibility(TRUE); gtk_window_deiconify(wid); } #endif return FALSE; } static gboolean mg_configure_cb(GtkWidget *wid, GdkEventConfigure * event, window_t *sess) { #if 0 if (sess == NULL) { /* for the main_window */ if (mg_gui) { if (prefs.mainwindow_save) { sess = current_sess; gtk_window_get_position(GTK_WINDOW(wid), &prefs.mainwindow_left, &prefs.mainwindow_top); gtk_window_get_size(GTK_WINDOW(wid), &prefs.mainwindow_width, &prefs.mainwindow_height); } } } if (sess) { if (sess->type == SESS_DIALOG && prefs.mainwindow_save) { gtk_window_get_position(GTK_WINDOW(wid), &prefs.dialog_left, &prefs.dialog_top); gtk_window_get_size(GTK_WINDOW(wid), &prefs.dialog_width, &prefs.dialog_height); } if (((GtkXText *) sess->gui->xtext)->transparent) gtk_widget_queue_draw(sess->gui->xtext); } #endif return FALSE; } #if 0 /* move to a non-irc tab */ static void mg_show_generic_tab(GtkWidget *box) { int num; GtkWidget *f = NULL; #if defined(GTK_WIDGET_HAS_FOCUS) if (current_sess && GTK_WIDGET_HAS_FOCUS(current_sess->gui->input_box)) #else if (current_sess && gtk_widget_has_focus(current_sess->gui->input_box)) #endif f = current_sess->gui->input_box; num = gtk_notebook_page_num(GTK_NOTEBOOK(mg_gui->note_book), box); gtk_notebook_set_current_page(GTK_NOTEBOOK(mg_gui->note_book), num); gtk_tree_view_set_model(GTK_TREE_VIEW(mg_gui->user_tree), NULL); gtk_window_set_title(GTK_WINDOW(mg_gui->window), g_object_get_data(G_OBJECT(box), "title")); gtk_widget_set_sensitive(mg_gui->menu, FALSE); if (f) gtk_widget_grab_focus(f); } #endif /* a channel has been focused */ static void mg_focus(window_t *sess) { #if 0 if (sess->gui->is_tab) current_tab = sess; current_sess = sess; /* dirty trick to avoid auto-selection */ SPELL_ENTRY_SET_EDITABLE(sess->gui->input_box, FALSE); gtk_widget_grab_focus(sess->gui->input_box); SPELL_ENTRY_SET_EDITABLE(sess->gui->input_box, TRUE); sess->server->front_session = sess; if (sess->server->server_session != NULL) { if (sess->server->server_session->type != SESS_SERVER) sess->server->server_session = sess; } else { sess->server->server_session = sess; } if (sess->new_data || sess->nick_said || sess->msg_said) { sess->nick_said = FALSE; sess->msg_said = FALSE; sess->new_data = FALSE; /* when called via mg_changui_new, is_tab might be true, but sess->res->tab is still NULL. */ if (sess->res->tab) fe_set_tab_color(sess, 0); } #endif } #if 0 void mg_set_topic_tip(session *sess) { char *text; switch (sess->type) { case SESS_CHANNEL: if (sess->topic) { text = g_strdup_printf(_("Topic for %s is: %s"), sess->channel, sess->topic); add_tip(sess->gui->topic_entry, text); g_free(text); } else add_tip(sess->gui->topic_entry, _("No topic is set")); break; default: if (GTK_ENTRY(sess->gui->topic_entry)->text && GTK_ENTRY(sess->gui->topic_entry)->text[0]) add_tip(sess->gui->topic_entry, GTK_ENTRY(sess->gui->topic_entry)->text); else add_tip(sess->gui->topic_entry, NULL); } } #endif static void mg_hide_empty_pane(GtkPaned * pane) { #if defined(GTK_WIDGET_VISIBLE) if ((pane->child1 == NULL || !GTK_WIDGET_VISIBLE(pane->child1)) && (pane->child2 == NULL || !GTK_WIDGET_VISIBLE(pane->child2))) #else if ((pane->child1 == NULL || !gtk_widget_get_visible(pane->child1)) && (pane->child2 == NULL || !gtk_widget_get_visible(pane->child2))) #endif { gtk_widget_hide(GTK_WIDGET(pane)); return; } gtk_widget_show(GTK_WIDGET(pane)); } static void mg_hide_empty_boxes(gtk_window_ui_t *gui) { /* hide empty vpanes - so the handle is not shown */ mg_hide_empty_pane((GtkPaned *) gui->vpane_right); mg_hide_empty_pane((GtkPaned *) gui->vpane_left); } static void mg_userlist_showhide(window_t *sess, int show) { gtk_window_ui_t *gui = gtk_private_ui(sess); int handle_size; if (show) { gtk_widget_show(gui->user_box); gui->ul_hidden = 0; gtk_widget_style_get(GTK_WIDGET(gui->hpane_right), "handle-size", &handle_size, NULL); gtk_paned_set_position(GTK_PANED(gui->hpane_right), GTK_WIDGET(gui->hpane_right)->allocation.width - (gui_pane_right_size_config + handle_size)); } else { gtk_widget_hide(gui->user_box); gui->ul_hidden = 1; } mg_hide_empty_boxes(gui); } /* decide if the userlist should be shown or hidden for this tab */ void mg_decide_userlist(window_t *sess, gboolean switch_to_current) { /* when called from menu.c we need this */ if (gtk_private_ui(sess) == mg_gui && switch_to_current) sess = window_current; if (!contacts_config) { mg_userlist_showhide(sess, FALSE); return; } /* xchat->ekg2 XXX, here: mg_is_userlist_and_tree_combined() stuff */ mg_userlist_showhide(sess, TRUE); /* show */ } static void mg_userlist_toggle_cb(GtkWidget *button, gpointer userdata) { contacts_config = !contacts_config; mg_decide_userlist(window_current, FALSE); gtk_widget_grab_focus(gtk_private_ui(window_current)->input_box); } static int ul_tag = 0; /* static */ gboolean mg_populate_userlist(window_t *sess) { gtk_window_ui_t *gui; GdkPixbuf **pxs; if (!sess) sess = window_current; /* mg_populate_userlist() hack, slowdown */ fe_userlist_clear(sess); /* xchat->ekg2, mg_populate_userlist() xchat here check if param is valid window_t, XXX */ if (sess->userlist) { userlist_t *ul; /* XXX, irc_pixs! */ pxs = pixs; for (ul = sess->userlist; ul; ul = ul->next) { userlist_t *u = ul; if (!u || !u->nickname || !u->status) continue; fe_userlist_insert(sess, u, pxs); } } else if (sess->session) { userlist_t *ul; /* check what network, and select pixs */ if (sess->session->plugin == plugin_find("gg")) pxs = gg_pixs; else if (sess->session->plugin == plugin_find("icq")) pxs = icq_pixs; else pxs = pixs; for (ul = sess->session->userlist; ul; ul = ul->next) { userlist_t *u = ul; if (!u || !u->nickname || !u->status) continue; fe_userlist_insert(sess, u, pxs); } } // if (is_session(sess)) -> if (window_find_ptr(sess) if (1) { gui = gtk_private_ui(sess); #if 0 if (sess->type == SESS_DIALOG) mg_set_access_icon(sess->gui, NULL, sess->server->is_away); else mg_set_access_icon(sess->gui, get_user_icon(sess->server, sess->me), sess->server->is_away); #endif userlist_show(sess); userlist_set_value(gtk_private_ui(sess)->user_tree, gtk_private(sess)->old_ul_value); } return 0; } static gboolean mg_populate_userlist_idle(window_t *wnd) { mg_populate_userlist(wnd); ul_tag = 0; return 0; } /* fill the irc tab with a new channel */ /* static */ void mg_populate(window_t *sess) { gtk_window_t *res = gtk_private(sess); gtk_window_ui_t *gui = res->gui; int render = TRUE; guint16 vis = gui->ul_hidden; #if 0 switch (sess->type) { case SESS_DIALOG: /* show the dialog buttons */ gtk_widget_show(gui->dialogbutton_box); /* hide the chan-mode buttons */ gtk_widget_hide(gui->topicbutton_box); /* hide the userlist */ mg_decide_userlist(sess, FALSE); /* shouldn't edit the topic */ gtk_editable_set_editable(GTK_EDITABLE(gui->topic_entry), FALSE); break; case SESS_SERVER: if (prefs.chanmodebuttons) gtk_widget_show(gui->topicbutton_box); /* hide the dialog buttons */ gtk_widget_hide(gui->dialogbutton_box); /* hide the userlist */ mg_decide_userlist(sess, FALSE); /* shouldn't edit the topic */ gtk_editable_set_editable(GTK_EDITABLE(gui->topic_entry), FALSE); break; default: /* hide the dialog buttons */ gtk_widget_hide(gui->dialogbutton_box); if (prefs.chanmodebuttons) gtk_widget_show(gui->topicbutton_box); /* show the userlist */ mg_decide_userlist(sess, FALSE); /* let the topic be editted */ gtk_editable_set_editable(GTK_EDITABLE(gui->topic_entry), TRUE); } #else mg_decide_userlist(sess, FALSE); #endif /* move to THE irc tab */ if (gui->is_tab) gtk_notebook_set_current_page(GTK_NOTEBOOK(gui->note_book), 0); /* xtext size change? Then don't render, wait for the expose caused by showing/hidding the userlist */ if (vis != gui->ul_hidden && gui->user_box->allocation.width > 1) render = FALSE; gtk_xtext_buffer_show(GTK_XTEXT(gui->xtext), res->buffer, render); if (gui->is_tab) gtk_widget_set_sensitive(gui->menu, TRUE); mg_focus(sess); fe_set_title(sess); /* this one flickers, so only change if necessary */ if (strcmp(gtk_session_target(sess->session), gtk_button_get_label(GTK_BUTTON(gui->nick_label)))) gtk_button_set_label(GTK_BUTTON(gui->nick_label), gtk_session_target(sess->session)); /* this is slow, so make it a timeout event */ if (!gui->is_tab) { mg_populate_userlist(sess); } else { if (ul_tag == 0) ul_tag = g_idle_add((GSourceFunc) mg_populate_userlist_idle, NULL); } fe_userlist_numbers(sess); #if 0 /* menu items */ GTK_CHECK_MENU_ITEM(gui->menu_item[MENU_ID_AWAY])->active = sess->server->is_away; gtk_widget_set_sensitive(gui->menu_item[MENU_ID_AWAY], sess->server->connected); gtk_widget_set_sensitive(gui->menu_item[MENU_ID_JOIN], sess->server->end_of_motd); gtk_widget_set_sensitive(gui->menu_item[MENU_ID_DISCONNECT], sess->server->connected || sess->server->recondelay_tag); mg_set_topic_tip(sess); plugin_emit_dummy_print(sess, "Focus Tab"); #endif } #if 0 void mg_bring_tofront_sess(session *sess) { /* IRC tab or window */ if (sess->gui->is_tab) chan_focus(sess->res->tab); else gtk_window_present(GTK_WINDOW(sess->gui->window)); } void mg_bring_tofront(GtkWidget *vbox) { /* non-IRC tab or window */ chan *ch; ch = g_object_get_data(G_OBJECT(vbox), "ch"); if (ch) chan_focus(ch); else gtk_window_present(GTK_WINDOW(gtk_widget_get_toplevel(vbox))); } #endif void mg_switch_page(int relative, int num) { if (mg_gui) chanview_move_focus(mg_gui->chanview, relative, num); } /* a toplevel IRC window was destroyed */ static void mg_topdestroy_cb(GtkWidget *win, window_t *sess) { /* xchat->ekg2: mg_topdestroy_cb() BIG XXX */ printf("mg_topdestroy_cb() XXX\n"); #if 0 /* printf("enter mg_topdestroy. sess %p was destroyed\n", sess);*/ /* kill the text buffer */ gtk_xtext_buffer_free(gtk_private(sess)->buffer); #if 0 /* kill the user list */ g_object_unref(G_OBJECT(sess->res->user_model)); #endif window_kill(sess); /* XXX, session_free(sess) */ #endif } #if 0 /* cleanup an IRC tab */ static void mg_ircdestroy(session *sess) { GSList *list; /* kill the text buffer */ gtk_xtext_buffer_free(sess->res->buffer); /* kill the user list */ g_object_unref(G_OBJECT(sess->res->user_model)); session_free(sess); /* tell xchat.c about it */ if (mg_gui == NULL) { /* puts("-> mg_gui is already NULL");*/ return; } list = sess_list; while (list) { sess = list->data; if (sess->gui->is_tab) { /* puts("-> some tabs still remain");*/ return; } list = list->next; } /* puts("-> no tabs left, killing main tabwindow");*/ gtk_widget_destroy(mg_gui->window); active_tab = NULL; mg_gui = NULL; parent_window = NULL; } #endif void mg_tab_close(window_t *sess) { /* xchat->ekg2: mg_tab_close() XXX */ if (chan_remove(gtk_private(sess)->tab, FALSE)) #if 0 mg_ircdestroy(sess); #else ; #endif } static void mg_menu_destroy(GtkWidget *menu, gpointer userdata) { gtk_widget_destroy(menu); g_object_unref(menu); } void mg_create_icon_item(char *label, char *stock, GtkWidget *menu, void *callback, void *userdata) { GtkWidget *item; item = create_icon_menu(label, stock, TRUE); gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(callback), userdata); gtk_widget_show(item); } void mg_open_quit_dialog(gboolean minimize_button) { static GtkWidget *dialog = NULL; GtkWidget *dialog_vbox1; GtkWidget *table1; GtkWidget *image; GtkWidget *checkbutton1; GtkWidget *label; GtkWidget *dialog_action_area1; GtkWidget *button; char *text; if (dialog) { gtk_window_present(GTK_WINDOW(dialog)); return; } if (!gui_quit_dialog_config) { ekg_exit(); return; } if (config_save_quit == 1) { /* Display question if user want to /save config */ /* if (config_changed) format_find("config_changed") else if (config_keep_reason && ekg2_reason_changed) format_find("quit_keep_reason"); */ config_save_quit = 0; } /* xchat->ekg2 XXX */ /* xchat count dcc's + connected network, and display warning about it. * * "Are you sure you want to quit?\n * "You are connected to %i IRC networks." * "Some file transfers are still active." */ dialog = gtk_dialog_new(); gtk_container_set_border_width(GTK_CONTAINER(dialog), 6); gtk_window_set_title(GTK_WINDOW(dialog), _("Quit ekg2?")); gtk_window_set_transient_for(GTK_WINDOW(dialog), GTK_WINDOW(parent_window)); gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE); gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE); dialog_vbox1 = GTK_DIALOG(dialog)->vbox; gtk_widget_show(dialog_vbox1); table1 = gtk_table_new(2, 2, FALSE); gtk_widget_show(table1); gtk_box_pack_start(GTK_BOX(dialog_vbox1), table1, TRUE, TRUE, 0); gtk_container_set_border_width(GTK_CONTAINER(table1), 6); gtk_table_set_row_spacings(GTK_TABLE(table1), 12); gtk_table_set_col_spacings(GTK_TABLE(table1), 12); image = gtk_image_new_from_stock("gtk-dialog-warning", GTK_ICON_SIZE_DIALOG); gtk_widget_show(image); gtk_table_attach(GTK_TABLE(table1), image, 0, 1, 0, 1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (GTK_FILL), 0, 0); checkbutton1 = gtk_check_button_new_with_mnemonic(_("Don't ask next time.")); gtk_widget_show(checkbutton1); gtk_table_attach(GTK_TABLE(table1), checkbutton1, 0, 2, 1, 2, (GtkAttachOptions) (GTK_EXPAND | GTK_FILL), (GtkAttachOptions) (0), 0, 4); text = saprintf("%s\n", _("Are you sure you want to quit?")); label = gtk_label_new(text); xfree(text); gtk_widget_show(label); gtk_table_attach(GTK_TABLE(table1), label, 1, 2, 0, 1, (GtkAttachOptions) (GTK_EXPAND | GTK_SHRINK | GTK_FILL), (GtkAttachOptions) (GTK_EXPAND | GTK_SHRINK), 0, 0); gtk_label_set_use_markup(GTK_LABEL(label), TRUE); gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); dialog_action_area1 = GTK_DIALOG(dialog)->action_area; gtk_widget_show(dialog_action_area1); gtk_button_box_set_layout(GTK_BUTTON_BOX(dialog_action_area1), GTK_BUTTONBOX_END); if (minimize_button) { button = gtk_button_new_with_mnemonic(_("_Minimize to Tray")); gtk_widget_show(button); gtk_dialog_add_action_widget(GTK_DIALOG(dialog), button, 1); } button = gtk_button_new_from_stock("gtk-cancel"); gtk_widget_show(button); gtk_dialog_add_action_widget(GTK_DIALOG(dialog), button, GTK_RESPONSE_CANCEL); gtk_widget_grab_focus(button); button = gtk_button_new_from_stock("gtk-quit"); gtk_widget_show(button); gtk_dialog_add_action_widget(GTK_DIALOG(dialog), button, 0); gtk_widget_show(dialog); switch (gtk_dialog_run(GTK_DIALOG(dialog))) { case 0: #if 0 if (GTK_TOGGLE_BUTTON(checkbutton1)->active) gui_quit_dialog_config = 0; xchat_exit(); #endif ekg_exit(); break; case 1: /* minimize to tray */ #if 0 if (GTK_TOGGLE_BUTTON(checkbutton1)->active) { gui_tray_flags_config |= 1; /*prefs.gui_quit_dialog = 0; */ } tray_toggle_visibility(TRUE); #endif break; } gtk_widget_destroy(dialog); dialog = NULL; } void mg_close_sess(window_t *sess) { if (sess == window_status) { /* status window? */ mg_open_quit_dialog(FALSE); return; } window_kill(sess); /* fe_close_window() */ } static int mg_chan_remove(chan * ch) { /* remove the tab from chanview */ chan_remove(ch, TRUE); /* any tabs left? */ if (chanview_get_size(mg_gui->chanview) < 1) { /* if not, destroy the main tab window */ gtk_widget_destroy(mg_gui->window); #if DARK current_tab = NULL; #endif active_tab = NULL; mg_gui = NULL; parent_window = NULL; return TRUE; } return FALSE; } /* the "X" close button has been pressed (tab-view) */ static void mg_xbutton_cb(chanview * cv, chan * ch, int tag, gpointer userdata) { printf("mg_xbutoon_cb(%p) [%d [TAG_IRC: %d]\n", userdata, tag, TAG_IRC); if (tag == TAG_IRC) /* irc tab */ mg_close_sess(userdata); /* xchat->ekg2, removed support for generic tabs */ } static void mg_detach_tab_cb(GtkWidget *item, chan * ch) { if (chan_get_tag(ch) == TAG_IRC) { /* IRC tab */ /* userdata is session * */ mg_link_irctab(chan_get_userdata(ch), 1); return; } /* xchat->ekg2, removed support for generic tabs */ } static void mg_destroy_tab_cb(GtkWidget *item, chan * ch) { /* treat it just like the X button press */ mg_xbutton_cb(mg_gui->chanview, ch, chan_get_tag(ch), chan_get_userdata(ch)); } static void mg_color_insert(GtkWidget *item, gpointer userdata) { char *text; int num = GPOINTER_TO_INT(userdata); if (num > 99) { switch (num) { case 100: text = "\002"; break; case 101: text = "\037"; break; case 102: text = "\035"; break; default: text = "\017"; break; } #if 0 key_action_insert(current_sess->gui->input_box, 0, text, 0, 0); #endif } else { #if 0 char buf[32]; sprintf(buf, "\003%02d", num); key_action_insert(current_sess->gui->input_box, 0, buf, 0, 0); #endif } } static void mg_markup_item(GtkWidget *menu, char *text, int arg) { GtkWidget *item; item = gtk_menu_item_new_with_label(""); gtk_label_set_markup(GTK_LABEL(GTK_BIN(item)->child), text); g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(mg_color_insert), GINT_TO_POINTER(arg)); gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); gtk_widget_show(item); } GtkWidget * mg_submenu(GtkWidget *menu, char *text) { GtkWidget *submenu, *item; item = gtk_menu_item_new_with_mnemonic(text); gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); gtk_widget_show(item); submenu = gtk_menu_new(); gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu); gtk_widget_show(submenu); return submenu; } static void mg_create_color_menu(GtkWidget *menu, window_t *sess) { GtkWidget *submenu; GtkWidget *subsubmenu; char buf[256]; int i; submenu = mg_submenu(menu, _("Insert Attribute or Color Code")); mg_markup_item(submenu, _("Bold"), 100); mg_markup_item(submenu, _("Underline"), 101); /*mg_markup_item (submenu, _("Italic"), 102); */ mg_markup_item(submenu, _("Normal"), 103); subsubmenu = mg_submenu(submenu, _("Colors 0-7")); for (i = 0; i < 8; i++) { sprintf(buf, "%02d " " ", i, colors[i].red >> 8, colors[i].green >> 8, colors[i].blue >> 8); mg_markup_item(subsubmenu, buf, i); } subsubmenu = mg_submenu(submenu, _("Colors 8-15")); for (i = 8; i < 16; i++) { sprintf(buf, "%02d " " ", i, colors[i].red >> 8, colors[i].green >> 8, colors[i].blue >> 8); mg_markup_item(subsubmenu, buf, i); } } static void mg_create_tabmenu(window_t *sess, GdkEventButton *event, chan *ch) { GtkWidget *menu, *item; char buf[256]; menu = gtk_menu_new(); if (sess) { const char *w_target = gtk_window_target(sess); char *target = g_markup_escape_text(w_target[0] ? w_target : "", -1); const char *w_session = (sess && sess->session) ? sess->session->uid : NULL; char *session = w_session ? g_markup_escape_text(w_session, -1) : NULL; snprintf(buf, sizeof(buf), "%s %s", target, session ? session : ""); g_free(target); g_free(session); item = gtk_menu_item_new_with_label(""); gtk_label_set_markup(GTK_LABEL(GTK_BIN(item)->child), buf); gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); gtk_widget_show(item); /* separator */ item = gtk_menu_item_new(); gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); gtk_widget_show(item); #if 0 /* per-channel alerts */ mg_create_alertmenu (sess, menu); /* per-channel settings */ mg_create_perchannelmenu (sess, menu); /* separator */ menu_quick_item (0, 0, menu, XCMENU_SHADED, 0, 0); if (sess->type == SESS_CHANNEL) menu_addfavoritemenu (sess->server, menu, sess->channel); #endif } mg_create_icon_item(_("_Close"), GTK_STOCK_CLOSE, menu, mg_destroy_tab_cb, ch); mg_create_icon_item(_("_Detach"), GTK_STOCK_REDO, menu, mg_detach_tab_cb, ch); #if 0 if (sess && tabmenu_list) menu_create(menu, tabmenu_list, sess->channel, FALSE); menu_add_plugin_items(menu, "\x4$TAB", sess->channel); #endif if (event->window) gtk_menu_set_screen(GTK_MENU(menu), gdk_drawable_get_screen(event->window)); g_object_ref(menu); g_object_ref_sink(menu); g_object_unref(menu); g_signal_connect(G_OBJECT(menu), "selection-done", G_CALLBACK(mg_menu_destroy), NULL); gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 0, event->time); } static gboolean mg_tab_contextmenu_cb (chanview *cv, chan *ch, int tag, gpointer ud, GdkEventButton *event) { /* shift-click to close a tab */ if ((event->state & GDK_SHIFT_MASK) && event->type == GDK_BUTTON_PRESS) { mg_xbutton_cb(cv, ch, tag, ud); return FALSE; } if (event->button != 3) return FALSE; if (tag == TAG_IRC) mg_create_tabmenu(ud, event, ch); else mg_create_tabmenu(NULL, event, ch); return TRUE; } /* add a tabbed channel */ static void mg_add_chan(window_t *sess) { GdkPixbuf *icon = NULL; /* pix_channel || pix_server || pix_dialog */ gtk_private(sess)->tab = chanview_add(gtk_private_ui(sess)->chanview, (char *) gtk_window_target(sess), /* sess->session, */ sess, FALSE, TAG_IRC, icon); if (plain_list == NULL) mg_create_tab_colors(); chan_set_color(gtk_private(sess)->tab, plain_list); if (gtk_private(sess)->buffer == NULL) { gtk_private(sess)->buffer = gtk_xtext_buffer_new(GTK_XTEXT(gtk_private_ui(sess)->xtext)); gtk_xtext_set_time_stamp(gtk_private(sess)->buffer, config_timestamp_show); gtk_private(sess)->user_model = userlist_create_model(); } } #if 0 /* mg_userlist_button() do przemyslenia */ /* mg_create_userlistbuttons() */ static void mg_topic_cb(GtkWidget *entry, gpointer userdata) { session *sess = current_sess; char *text; if (sess->channel[0] && sess->server->connected && sess->type == SESS_CHANNEL) { text = GTK_ENTRY(entry)->text; if (text[0] == 0) text = NULL; sess->server->p_topic(sess->server, sess->channel, text); } else gtk_entry_set_text(GTK_ENTRY(entry), ""); /* restore focus to the input widget, where the next input will most likely be */ gtk_widget_grab_focus(sess->gui->input_box); } #endif static void mg_tabwindow_kill_cb(GtkWidget *win, gpointer userdata) { #if 0 GSList *list, *next; session *sess; /* puts("enter mg_tabwindow_kill_cb");*/ xchat_is_quitting = TRUE; /* see if there's any non-tab windows left */ list = sess_list; while (list) { sess = list->data; next = list->next; if (!sess->gui->is_tab) { xchat_is_quitting = FALSE; /* puts("-> will not exit, some toplevel windows left");*/ } else { mg_ircdestroy(sess); } list = next; } current_tab = NULL; active_tab = NULL; mg_gui = NULL; parent_window = NULL; #endif } static GtkWidget *mg_changui_destroy(window_t *sess) { GtkWidget *ret = NULL; if (gtk_private_ui(sess)->is_tab) { /* avoid calling the "destroy" callback */ g_signal_handlers_disconnect_by_func(G_OBJECT(gtk_private_ui(sess)->window), mg_tabwindow_kill_cb, 0); /* remove the tab from the chanview */ if (!mg_chan_remove(gtk_private(sess)->tab)) /* if the window still exists, restore the signal handler */ g_signal_connect(G_OBJECT(gtk_private_ui(sess)->window), "destroy", G_CALLBACK(mg_tabwindow_kill_cb), 0); } else { /* avoid calling the "destroy" callback */ g_signal_handlers_disconnect_by_func(G_OBJECT(gtk_private_ui(sess)->window), mg_topdestroy_cb, sess); /*gtk_widget_destroy (sess->gui->window); */ /* don't destroy until the new one is created. Not sure why, but */ /* it fixes: Gdk-CRITICAL **: gdk_colormap_get_screen: */ /* assertion `GDK_IS_COLORMAP (cmap)' failed */ ret = gtk_private_ui(sess)->window; free(gtk_private_ui(sess)); gtk_private(sess)->gui = NULL; } return ret; } static void mg_link_irctab(window_t *sess, int focus) { GtkWidget *win; if (gtk_private_ui(sess)->is_tab) { win = mg_changui_destroy(sess); mg_changui_new(sess, gtk_private(sess), 0, focus); mg_populate(sess); #if 0 xchat_is_quitting = FALSE; #endif if (win) gtk_widget_destroy(win); return; } win = mg_changui_destroy(sess); mg_changui_new(sess, gtk_private(sess), 1, focus); /* the buffer is now attached to a different widget */ ((xtext_buffer *) gtk_private(sess)->buffer)->xtext = (GtkXText *) gtk_private_ui(sess)->xtext; if (win) gtk_widget_destroy(win); } void mg_detach(window_t *sess, int mode) { switch (mode) { /* detach only */ case 1: if (gtk_private_ui(sess)->is_tab) mg_link_irctab(sess, 1); break; /* attach only */ case 2: if (!gtk_private_ui(sess)->is_tab) mg_link_irctab(sess, 1); break; /* toggle */ default: mg_link_irctab(sess, 1); } } static void mg_apply_entry_style(GtkWidget *entry) { gtk_widget_modify_base(entry, GTK_STATE_NORMAL, &colors[COL_BG]); gtk_widget_modify_text(entry, GTK_STATE_NORMAL, &colors[COL_FG]); gtk_widget_modify_font(entry, input_style->font_desc); } #if 0 static void mg_dialog_button_cb(GtkWidget *wid, char *cmd) { /* the longest cmd is 12, and the longest nickname is 64 */ char buf[128]; char *host = ""; char *topic; topic = (char *)(GTK_ENTRY(gtk_private(window_current)->gui->topic_entry)->text); topic = strrchr(topic, '@'); if (topic) host = topic + 1; auto_insert(buf, sizeof(buf), cmd, 0, 0, "", "", "", server_get_network(current_sess->server, TRUE), host, "", current_sess->channel); handle_command(current_sess, buf, TRUE); /* dirty trick to avoid auto-selection */ SPELL_ENTRY_SET_EDITABLE(current_sess->gui->input_box, FALSE); gtk_widget_grab_focus(current_sess->gui->input_box); SPELL_ENTRY_SET_EDITABLE(current_sess->gui->input_box, TRUE); } static void mg_dialog_button(GtkWidget *box, char *name, char *cmd) { GtkWidget *wid; wid = gtk_button_new_with_label(name); gtk_box_pack_start(GTK_BOX(box), wid, FALSE, FALSE, 0); g_signal_connect(G_OBJECT(wid), "clicked", G_CALLBACK(mg_dialog_button_cb), cmd); gtk_widget_set_size_request(wid, -1, 0); } static void mg_create_dialogbuttons(GtkWidget *box) { struct popup *pop; GSList *list = dlgbutton_list; while (list) { pop = list->data; if (pop->cmd[0]) mg_dialog_button(box, pop->name, pop->cmd); list = list->next; } } #endif static void mg_create_topicbar(window_t *sess, GtkWidget *box) { GtkWidget *hbox, *topic, *bbox; gtk_window_ui_t *gui = gtk_private_ui(sess); gui->topic_bar = hbox = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(box), hbox, 0, 0, 0); if (!gui->is_tab) gtk_private(sess)->tab = NULL; gui->topic_entry = topic = gtk_entry_new(); gtk_widget_set_name(topic, "xchat-inputbox"); gtk_container_add(GTK_CONTAINER(hbox), topic); #if 0 g_signal_connect(G_OBJECT(topic), "activate", G_CALLBACK(mg_topic_cb), 0); #endif if (style_inputbox_config) mg_apply_entry_style(topic); gui->topicbutton_box = bbox = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(hbox), bbox, 0, 0, 0); gui->dialogbutton_box = bbox = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(hbox), bbox, 0, 0, 0); #if 0 mg_create_dialogbuttons(bbox); #endif if (!paned_userlist_config) gtkutil_button(hbox, GTK_STOCK_GOTO_LAST, _("Show/Hide userlist"), mg_userlist_toggle_cb, 0, 0); } /* check if a word is clickable */ static int mg_word_check(GtkWidget *xtext, char *word, int len) { /* xchat->ekg2: mg_word_check() nice functionality XXX */ return 0; } /* mouse click inside text area */ static void mg_word_clicked(GtkWidget *xtext, char *word, GdkEventButton * even) { /* xchat->ekg2: mg_word_clicked() nice functionality XXX */ } void mg_update_xtext(GtkWidget *wid) { GtkXText *xtext = GTK_XTEXT(wid); gtk_xtext_set_palette(xtext, colors); gtk_xtext_set_max_lines(xtext, backlog_size_config); gtk_xtext_set_tint(xtext, tint_red_config, tint_green_config, tint_blue_config); // gtk_xtext_set_background (xtext, channelwin_pix, transparent_config); gtk_xtext_set_wordwrap(xtext, wordwrap_config); gtk_xtext_set_show_marker(xtext, show_marker_config); gtk_xtext_set_show_separator(xtext, indent_nicks_config ? show_separator_config : 0); gtk_xtext_set_indent(xtext, indent_nicks_config); if (!gtk_xtext_set_font(xtext, font_normal_config)) { printf("Failed to open any font. I'm out of here!"); /* FE_MSG_WAIT | FE_MSG_ERROR */ exit(1); } gtk_xtext_refresh(xtext, FALSE); } /* handle errors reported by xtext */ static void mg_xtext_error(int type) { printf("mg_xtext_error() %d\n", type); /* @ type == 0 "Unable to set transparent background!\n\n" * "You may be using a non-compliant window\n" * "manager that is not currently supported.\n"), FE_MSG_WARN); * * config_transparent = 0; */ } static void mg_create_textarea(window_t *sess, GtkWidget *box) { GtkWidget *inbox, *vbox, *frame; GtkXText *xtext; gtk_window_ui_t *gui = gtk_private_ui(sess); vbox = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(box), vbox); inbox = gtk_hbox_new(FALSE, SCROLLBAR_SPACING); gtk_container_add(GTK_CONTAINER(vbox), inbox); frame = gtk_frame_new(NULL); gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN); gtk_container_add(GTK_CONTAINER(inbox), frame); gui->xtext = gtk_xtext_new(colors, TRUE); xtext = GTK_XTEXT(gui->xtext); gtk_xtext_set_max_indent(xtext, max_auto_indent_config); gtk_xtext_set_thin_separator(xtext, thin_separator_config); gtk_xtext_set_error_function(xtext, mg_xtext_error); gtk_xtext_set_urlcheck_function(xtext, mg_word_check); gtk_xtext_set_max_lines(xtext, backlog_size_config); gtk_container_add(GTK_CONTAINER(frame), GTK_WIDGET(xtext)); mg_update_xtext(GTK_WIDGET(xtext)); g_signal_connect(G_OBJECT(xtext), "word_click", G_CALLBACK(mg_word_clicked), NULL); gui->vscrollbar = gtk_vscrollbar_new(GTK_XTEXT(xtext)->adj); gtk_box_pack_start(GTK_BOX(inbox), gui->vscrollbar, FALSE, TRUE, 0); /* xchat->ekg2: g_signal_connect() \"drag_begin\", \"drag_drop\", \"drag_motion\", \"drag_end\", \"drag_data_received\" && gtk_drag_dest_set() do zaimplementowania */ } static void mg_create_userlist(gtk_window_ui_t *gui, GtkWidget *box) { GtkWidget *frame, *ulist, *vbox; vbox = gtk_vbox_new(0, 1); gtk_container_add(GTK_CONTAINER(box), vbox); frame = gtk_frame_new(NULL); if (!(gui_tweaks_config & 1)) gtk_box_pack_start(GTK_BOX(vbox), frame, 0, 0, GUI_SPACING); gui->namelistinfo = gtk_label_new(NULL); gtk_container_add(GTK_CONTAINER(frame), gui->namelistinfo); gui->user_tree = ulist = userlist_create(vbox); #if 0 if (prefs.style_namelistgad) { gtk_widget_set_style(ulist, input_style); gtk_widget_modify_base(ulist, GTK_STATE_NORMAL, &colors[COL_BG]); } #endif } static void mg_leftpane_cb(GtkPaned * pane, GParamSpec * param, gtk_window_ui_t* gui) { gui_pane_left_size_config = gtk_paned_get_position(pane); } static void mg_rightpane_cb(GtkPaned * pane, GParamSpec * param, gtk_window_ui_t* gui) { int handle_size; /* if (pane->child1 == NULL || (!GTK_WIDGET_VISIBLE(pane->child1))) return; if (pane->child2 == NULL || (!GTK_WIDGET_VISIBLE(pane->child2))) return;*/ gtk_widget_style_get(GTK_WIDGET(pane), "handle-size", &handle_size, NULL); /* record the position from the RIGHT side */ gui_pane_right_size_config = GTK_WIDGET(pane)->allocation.width - gtk_paned_get_position(pane) - handle_size; } static gboolean mg_add_pane_signals(gtk_window_ui_t *gui) { g_signal_connect(G_OBJECT(gui->hpane_right), "notify::position", G_CALLBACK(mg_rightpane_cb), gui); g_signal_connect(G_OBJECT(gui->hpane_left), "notify::position", G_CALLBACK(mg_leftpane_cb), gui); return FALSE; } static void mg_create_center(window_t *sess, gtk_window_ui_t *gui, GtkWidget *box) { GtkWidget *vbox, *hbox, *book; /* sep between top and bottom of left side */ gui->vpane_left = gtk_vpaned_new(); /* sep between top and bottom of right side */ gui->vpane_right = gtk_vpaned_new(); /* sep between left and xtext */ gui->hpane_left = gtk_hpaned_new(); gtk_paned_set_position(GTK_PANED(gui->hpane_left), gui_pane_left_size_config); /* sep between xtext and right side */ gui->hpane_right = gtk_hpaned_new(); if (gui_tweaks_config & 4) { gtk_paned_pack2(GTK_PANED(gui->hpane_left), gui->vpane_left, FALSE, TRUE); gtk_paned_pack1(GTK_PANED(gui->hpane_left), gui->hpane_right, TRUE, TRUE); } else { gtk_paned_pack1(GTK_PANED(gui->hpane_left), gui->vpane_left, FALSE, TRUE); gtk_paned_pack2(GTK_PANED(gui->hpane_left), gui->hpane_right, TRUE, TRUE); } gtk_paned_pack2(GTK_PANED(gui->hpane_right), gui->vpane_right, FALSE, TRUE); gtk_container_add(GTK_CONTAINER(box), gui->hpane_left); gui->note_book = book = gtk_notebook_new(); gtk_notebook_set_show_tabs(GTK_NOTEBOOK(book), FALSE); gtk_notebook_set_show_border(GTK_NOTEBOOK(book), FALSE); gtk_paned_pack1(GTK_PANED(gui->hpane_right), book, TRUE, TRUE); hbox = gtk_hbox_new(FALSE, 0); gtk_paned_pack1(GTK_PANED(gui->vpane_right), hbox, FALSE, TRUE); mg_create_userlist(gui, hbox); gui->user_box = hbox; vbox = gtk_vbox_new(FALSE, 3); gtk_notebook_append_page(GTK_NOTEBOOK(book), vbox, NULL); mg_create_topicbar(sess, vbox); mg_create_textarea(sess, vbox); mg_create_entry(sess, vbox); g_idle_add((GSourceFunc) mg_add_pane_signals, gui); } static void mg_sessionclick_cb(GtkWidget *button, gpointer userdata) { /* xchat->ekg2: mg_sessionclick_cb() XXX, change session using this [like ^X] implement */ /* xchat: * fe_get_str (_("Enter new nickname:"), current_sess->server->nick, mg_change_nick, NULL); */ } /* make sure chanview and userlist positions are sane */ static void mg_sanitize_positions(int *cv, int *ul) { if (tab_layout_config == 2) { /* treeview can't be on TOP or BOTTOM */ if (*cv == POS_TOP || *cv == POS_BOTTOM) *cv = POS_TOPLEFT; } /* userlist can't be on TOP or BOTTOM */ if (*ul == POS_TOP || *ul == POS_BOTTOM) *ul = POS_TOPRIGHT; /* can't have both in the same place */ if (*cv == *ul) { *cv = POS_TOPRIGHT; if (*ul == POS_TOPRIGHT) *cv = POS_BOTTOMRIGHT; } } static void mg_place_userlist_and_chanview_real(gtk_window_ui_t *gui, GtkWidget *userlist, GtkWidget *chanview) { int unref_userlist = FALSE; int unref_chanview = FALSE; /* first, remove userlist/treeview from their containers */ if (userlist && userlist->parent) { g_object_ref(userlist); gtk_container_remove(GTK_CONTAINER(userlist->parent), userlist); unref_userlist = TRUE; } if (chanview && chanview->parent) { g_object_ref(chanview); gtk_container_remove(GTK_CONTAINER(chanview->parent), chanview); unref_chanview = TRUE; } if (chanview) { /* incase the previous pos was POS_HIDDEN */ gtk_widget_show(chanview); gtk_table_set_row_spacing(GTK_TABLE(gui->main_table), 1, 0); gtk_table_set_row_spacing(GTK_TABLE(gui->main_table), 2, 2); /* then place them back in their new positions */ switch (tab_pos_config) { case POS_TOPLEFT: gtk_paned_pack1(GTK_PANED(gui->vpane_left), chanview, FALSE, TRUE); break; case POS_BOTTOMLEFT: gtk_paned_pack2(GTK_PANED(gui->vpane_left), chanview, FALSE, TRUE); break; case POS_TOPRIGHT: gtk_paned_pack1(GTK_PANED(gui->vpane_right), chanview, FALSE, TRUE); break; case POS_BOTTOMRIGHT: gtk_paned_pack2(GTK_PANED(gui->vpane_right), chanview, FALSE, TRUE); break; case POS_TOP: gtk_table_set_row_spacing(GTK_TABLE(gui->main_table), 1, GUI_SPACING - 1); gtk_table_attach(GTK_TABLE(gui->main_table), chanview, 1, 2, 1, 2, GTK_FILL, GTK_FILL, 0, 0); break; case POS_HIDDEN: gtk_widget_hide(chanview); /* always attach it to something to avoid ref_count=0 */ if (gui_ulist_pos_config == POS_TOP) gtk_table_attach(GTK_TABLE(gui->main_table), chanview, 1, 2, 3, 4, GTK_FILL, GTK_FILL, 0, 0); else gtk_table_attach(GTK_TABLE(gui->main_table), chanview, 1, 2, 1, 2, GTK_FILL, GTK_FILL, 0, 0); break; default: /* POS_BOTTOM */ gtk_table_set_row_spacing(GTK_TABLE(gui->main_table), 2, 3); gtk_table_attach(GTK_TABLE(gui->main_table), chanview, 1, 2, 3, 4, GTK_FILL, GTK_FILL, 0, 0); } } if (userlist) { switch (gui_ulist_pos_config) { case POS_TOPLEFT: gtk_paned_pack1(GTK_PANED(gui->vpane_left), userlist, FALSE, TRUE); break; case POS_BOTTOMLEFT: gtk_paned_pack2(GTK_PANED(gui->vpane_left), userlist, FALSE, TRUE); break; case POS_BOTTOMRIGHT: gtk_paned_pack2(GTK_PANED(gui->vpane_right), userlist, FALSE, TRUE); break; /* case POS_HIDDEN: break; */ /* Hide using the VIEW menu instead */ default: /* POS_TOPRIGHT */ gtk_paned_pack1(GTK_PANED(gui->vpane_right), userlist, FALSE, TRUE); } } if (unref_chanview) g_object_unref(chanview); if (unref_userlist) g_object_unref(userlist); mg_hide_empty_boxes(gui); } static void mg_place_userlist_and_chanview(gtk_window_ui_t *gui) { GtkOrientation orientation; GtkWidget *chanviewbox = NULL; int pos; mg_sanitize_positions(&tab_pos_config, &gui_ulist_pos_config); if (gui->chanview) { pos = tab_pos_config; orientation = chanview_get_orientation(gui->chanview); if ((pos == POS_BOTTOM || pos == POS_TOP) && orientation == GTK_ORIENTATION_VERTICAL) chanview_set_orientation(gui->chanview, FALSE); else if ((pos == POS_TOPLEFT || pos == POS_BOTTOMLEFT || pos == POS_TOPRIGHT || pos == POS_BOTTOMRIGHT) && orientation == GTK_ORIENTATION_HORIZONTAL) chanview_set_orientation(gui->chanview, TRUE); chanviewbox = chanview_get_box(gui->chanview); } mg_place_userlist_and_chanview_real(gui, gui->user_box, chanviewbox); } void mg_change_layout(int type) { if (mg_gui) { /* put tabs at the bottom */ if (type == 0 && tab_pos_config != POS_BOTTOM && tab_pos_config != POS_TOP) tab_pos_config = POS_BOTTOM; mg_place_userlist_and_chanview(mg_gui); chanview_set_impl(mg_gui->chanview, type); } } static void mg_inputbox_rightclick(GtkEntry * entry, GtkWidget *menu) { mg_create_color_menu(menu, NULL); } static void mg_create_entry(window_t *sess, GtkWidget *box) { GtkWidget *hbox, *but, *entry; gtk_window_ui_t *gui = gtk_private_ui(sess); hbox = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(box), hbox, 0, 0, 0); gui->nick_box = gtk_hbox_new(FALSE, 0); gtk_box_pack_start(GTK_BOX(hbox), gui->nick_box, 0, 0, 0); #if DARK # warning "XXX?" #endif gui->nick_label = but = gtk_button_new_with_label(gtk_session_target(sess->session)); gtk_button_set_relief(GTK_BUTTON(but), GTK_RELIEF_NONE); GTK_WIDGET_UNSET_FLAGS(but, GTK_CAN_FOCUS); gtk_box_pack_end(GTK_BOX(gui->nick_box), but, 0, 0, 0); g_signal_connect(G_OBJECT(but), "clicked", G_CALLBACK(mg_sessionclick_cb), NULL); gui->input_box = entry = gtk_entry_new(); gtk_entry_set_max_length(GTK_ENTRY(gui->input_box), 2048); g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(mg_inputbox_cb), gui); gtk_container_add(GTK_CONTAINER(hbox), entry); gtk_widget_set_name(entry, "xchat-inputbox"); g_signal_connect(G_OBJECT(entry), "key_press_event", G_CALLBACK(key_handle_key_press), NULL); g_signal_connect(G_OBJECT(entry), "focus_in_event", G_CALLBACK(mg_inputbox_focus), gui); g_signal_connect(G_OBJECT(entry), "populate_popup", G_CALLBACK(mg_inputbox_rightclick), NULL); gtk_widget_grab_focus(entry); if (style_inputbox_config) mg_apply_entry_style(entry); } static void mg_switch_tab_cb(chanview * cv, chan * ch, int tag, gpointer ud) { chan *old; window_t *sess = ud; old = active_tab; active_tab = ch; if (active_tab != old) { /* xchat->ekg2 mg_switch_tab_cb() mg_unpopulate() */ mg_populate(sess); /* it's switched by gui, let's inform ekg2 */ if (in_autoexec == 0 && gtk_ui_window_switch_lock == 0) { window_switch(sess->id); } } } /* compare two tabs (for tab sorting function) */ static int mg_tabs_compare(window_t *a, window_t *b) { /* it's lik: window_new_compare() */ return (a->id - b->id); } static void mg_create_tabs(gtk_window_ui_t *gui) { gboolean use_icons = FALSE; #if 0 /* if any one of these PNGs exist, the chanview will create * the extra column for icons. */ if (pix_channel || pix_dialog || pix_server || pix_util) use_icons = TRUE; #endif gui->chanview = chanview_new(tab_layout_config, truncchans_config, tab_sort_config, use_icons, style_namelistgad_config ? input_style : NULL); chanview_set_callbacks(gui->chanview, mg_switch_tab_cb, mg_xbutton_cb, mg_tab_contextmenu_cb, (void *) mg_tabs_compare); mg_place_userlist_and_chanview(gui); } static gboolean mg_tabwin_focus_cb(GtkWindow * win, GdkEventFocus * event, gpointer userdata) { #if 0 current_sess = current_tab; if (current_sess) { gtk_xtext_check_marker_visibility(GTK_XTEXT(current_sess->gui->xtext)); plugin_emit_dummy_print(current_sess, "Focus Window"); } #endif #ifdef USE_XLIB unflash_window(GTK_WIDGET(win)); #endif return FALSE; } static gboolean mg_topwin_focus_cb(GtkWindow * win, GdkEventFocus * event, window_t *sess) { #if 0 current_sess = sess; if (!sess->server->server_session) sess->server->server_session = sess; gtk_xtext_check_marker_visibility(GTK_XTEXT(current_sess->gui->xtext)); #ifdef USE_XLIB unflash_window(GTK_WIDGET(win)); #endif plugin_emit_dummy_print(sess, "Focus Window"); #endif return FALSE; } static void mg_create_menu(gtk_window_ui_t *gui, GtkWidget *table, int away_state) { GtkAccelGroup *accel_group; accel_group = gtk_accel_group_new(); gtk_window_add_accel_group(GTK_WINDOW(gtk_widget_get_toplevel(table)), accel_group); g_object_unref(accel_group); gui->menu = menu_create_main(accel_group, TRUE, away_state, !gui->is_tab, gui->menu_item); gtk_table_attach(GTK_TABLE(table), gui->menu, 0, 3, 0, 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0); } static void mg_create_irctab(window_t *sess, GtkWidget *table) { GtkWidget *vbox; gtk_window_ui_t *gui = gtk_private_ui(sess); vbox = gtk_vbox_new(FALSE, 0); gtk_table_attach(GTK_TABLE(table), vbox, 1, 2, 2, 3, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); mg_create_center(sess, gui, vbox); } static void mg_create_topwindow(window_t *sess) { GtkWidget *win; GtkWidget *table; win = gtkutil_window_new("ekg2", NULL, mainwindow_width_config, mainwindow_height_config, 0); gtk_private_ui(sess)->window = win; gtk_container_set_border_width(GTK_CONTAINER(win), GUI_BORDER); g_signal_connect(G_OBJECT(win), "focus_in_event", G_CALLBACK(mg_topwin_focus_cb), sess); g_signal_connect(G_OBJECT(win), "destroy", G_CALLBACK(mg_topdestroy_cb), sess); g_signal_connect(G_OBJECT(win), "configure_event", G_CALLBACK(mg_configure_cb), sess); palette_alloc(win); table = gtk_table_new(4, 3, FALSE); /* spacing under the menubar */ gtk_table_set_row_spacing(GTK_TABLE(table), 0, GUI_SPACING); /* left and right borders */ gtk_table_set_col_spacing(GTK_TABLE(table), 0, 1); gtk_table_set_col_spacing(GTK_TABLE(table), 1, 1); gtk_container_add(GTK_CONTAINER(win), table); mg_create_irctab(sess, table); /* vvvvv sess->server->is_away */ mg_create_menu(gtk_private_ui(sess), table, 0); if (gtk_private(sess)->buffer == NULL) { gtk_private(sess)->buffer = gtk_xtext_buffer_new(GTK_XTEXT(gtk_private_ui(sess)->xtext)); gtk_xtext_buffer_show(GTK_XTEXT(gtk_private_ui(sess)->xtext), gtk_private(sess)->buffer, TRUE); gtk_xtext_set_time_stamp(gtk_private(sess)->buffer, config_timestamp_show); gtk_private(sess)->user_model = userlist_create_model(); } userlist_show(sess); gtk_widget_show_all(table); if (hidemenu_config) gtk_widget_hide(gtk_private_ui(sess)->menu); if (!topicbar_config) gtk_widget_hide(gtk_private_ui(sess)->topic_bar); if (gui_tweaks_config & 2) gtk_widget_hide(gtk_private_ui(sess)->nick_box); mg_decide_userlist(sess, FALSE); #if DARK if (sess->type == SESS_DIALOG) { /* hide the chan-mode buttons */ gtk_widget_hide(sess->gui->topicbutton_box); } else { gtk_widget_hide(sess->gui->dialogbutton_box); if (!prefs.chanmodebuttons) gtk_widget_hide(sess->gui->topicbutton_box); } #endif mg_place_userlist_and_chanview(gtk_private_ui(sess)); gtk_widget_show(win); } static gboolean mg_tabwindow_de_cb(GtkWidget *widget, GdkEvent * event, gpointer user_data) { #if 0 if ((gui_tray_flags_config & 1) && tray_toggle_visibility(FALSE)) return TRUE; /* check for remaining toplevel windows */ list = sess_list; while (list) { sess = list->data; if (!sess->gui->is_tab) return FALSE; list = list->next; } #endif mg_open_quit_dialog(TRUE); return TRUE; } static void mg_create_tabwindow(window_t *sess) { GtkWidget *win; GtkWidget *table; win = gtkutil_window_new("ekg2", NULL, mainwindow_width_config, mainwindow_height_config, 0); gtk_private_ui(sess)->window = win; gtk_window_move(GTK_WINDOW(win), mainwindow_left_config, mainwindow_top_config); if (gui_win_state_config) gtk_window_maximize(GTK_WINDOW(win)); gtk_container_set_border_width(GTK_CONTAINER(win), GUI_BORDER); g_signal_connect(G_OBJECT(win), "delete_event", G_CALLBACK(mg_tabwindow_de_cb), 0); g_signal_connect(G_OBJECT(win), "destroy", G_CALLBACK(mg_tabwindow_kill_cb), 0); g_signal_connect(G_OBJECT(win), "focus_in_event", G_CALLBACK(mg_tabwin_focus_cb), NULL); g_signal_connect(G_OBJECT(win), "configure_event", G_CALLBACK(mg_configure_cb), NULL); g_signal_connect(G_OBJECT(win), "window_state_event", G_CALLBACK(mg_windowstate_cb), NULL); palette_alloc(win); gtk_private_ui(sess)->main_table = table = gtk_table_new(4, 3, FALSE); /* spacing under the menubar */ gtk_table_set_row_spacing(GTK_TABLE(table), 0, GUI_SPACING); /* left and right borders */ gtk_table_set_col_spacing(GTK_TABLE(table), 0, 1); gtk_table_set_col_spacing(GTK_TABLE(table), 1, 1); gtk_container_add(GTK_CONTAINER(win), table); mg_create_irctab(sess, table); mg_create_tabs(gtk_private_ui(sess)); /* vvvvvv sess->server->is_away */ mg_create_menu(gtk_private_ui(sess), table, 0); mg_focus(sess); gtk_widget_show_all(table); if (hidemenu_config) gtk_widget_hide(gtk_private_ui(sess)->menu); mg_decide_userlist(sess, FALSE); if (!topicbar_config) gtk_widget_hide(gtk_private_ui(sess)->topic_bar); if (!chanmodebuttons_config) gtk_widget_hide(gtk_private_ui(sess)->topicbutton_box); if (gui_tweaks_config & 2) gtk_widget_hide(gtk_private_ui(sess)->nick_box); mg_place_userlist_and_chanview(gtk_private_ui(sess)); gtk_widget_show(win); } void mg_apply_setup(void) { int done_main = FALSE; window_t *w; mg_create_tab_colors(); for (w = windows; w; w = w->next) { gtk_xtext_set_time_stamp(gtk_private(w)->buffer, config_timestamp_show); ((xtext_buffer *) gtk_private(w)->buffer)->needs_recalc = TRUE; if (!gtk_private_ui(w)->is_tab || !done_main) mg_place_userlist_and_chanview(gtk_private_ui(w)); if (gtk_private_ui(w)->is_tab) done_main = TRUE; } } #if 0 static chan * mg_add_generic_tab(char *name, char *title, void *family, GtkWidget *box) { chan *ch; gtk_notebook_append_page(GTK_NOTEBOOK(mg_gui->note_book), box, NULL); gtk_widget_show(box); ch = chanview_add(mg_gui->chanview, name, NULL, box, TRUE, TAG_UTIL, pix_util); chan_set_color(ch, plain_list); /* FIXME: memory leak */ g_object_set_data(G_OBJECT(box), "title", strdup(title)); g_object_set_data(G_OBJECT(box), "ch", ch); if (prefs.newtabstofront) chan_focus(ch); return ch; } #endif #if 0 void fe_clear_channel(window_t *sess) { char tbuf[CHANLEN + 6]; gtk_window_ui_t *gui = gtk_private(sess); if (gui->is_tab) { if (sess->waitchannel[0]) { if (prefs.truncchans > 2 && g_utf8_strlen(sess->waitchannel, -1) > prefs.truncchans) { /* truncate long channel names */ tbuf[0] = '('; strcpy(tbuf + 1, sess->waitchannel); g_utf8_offset_to_pointer(tbuf, prefs.truncchans)[0] = 0; strcat(tbuf, "..)"); } else { sprintf(tbuf, "(%s)", sess->waitchannel); } } else strcpy(tbuf, _("")); chan_rename(sess->res->tab, tbuf, prefs.truncchans); } if (!gui->is_tab || sess == current_tab) { gtk_entry_set_text(GTK_ENTRY(gui->topic_entry), ""); if (gui->op_xpm) { gtk_widget_destroy(gui->op_xpm); gui->op_xpm = NULL; } } else { } } void fe_dlgbuttons_update(window_t *sess) { GtkWidget *box; gtk_window_ui_t *gui = gtk_private(sess); gtk_widget_destroy(gui->dialogbutton_box); gui->dialogbutton_box = box = gtk_hbox_new(0, 0); gtk_box_pack_start(GTK_BOX(gui->topic_bar), box, 0, 0, 0); gtk_box_reorder_child(GTK_BOX(gui->topic_bar), box, 3); mg_create_dialogbuttons(box); gtk_widget_show_all(box); if (current_tab && current_tab->type != SESS_DIALOG) gtk_widget_hide(current_tab->gui->dialogbutton_box); } /* fe_set_nick() nieciekawe */ #endif void fe_set_away(session_t * serv) { window_t *w; for (w = windows; w; w = w->next) { if (w->session == serv) { #if DARK if (!sess->gui->is_tab || sess == current_tab) { GTK_CHECK_MENU_ITEM(sess->gui->menu_item[MENU_ID_AWAY])->active = serv->is_away; /* gray out my nickname */ mg_set_myself_away(sess->gui, serv->is_away); } #endif } } } void fe_set_channel(window_t *sess) { if (gtk_private(sess)->tab != NULL) chan_rename(gtk_private(sess)->tab, (char *) gtk_window_target(sess), truncchans_config); } void mg_changui_new(window_t *sess, gtk_window_t *res, int tab, int focus) { int first_run = FALSE; gtk_window_t *gtk_window; gtk_window_ui_t *gui; if (res) gtk_window = res; else gtk_window = xmalloc(sizeof(gtk_window_t)); #if DARK struct User *user = NULL; if (!sess->server->front_session) sess->server->front_session = sess; if (!is_channel(sess->server, sess->channel)) user = userlist_find_global(sess->server, sess->channel); #endif if (!tab) { gui = xmalloc(sizeof(gtk_window_ui_t)); gui->is_tab = FALSE; gtk_window->gui = gui; sess->priv_data = gtk_window; mg_create_topwindow(sess); fe_set_title(sess); #if DARK if (user && user->hostname) set_topic(sess, user->hostname); #endif return; } if (mg_gui == NULL) { first_run = TRUE; gui = &static_mg_gui; memset(gui, 0, sizeof(gtk_window_ui_t)); gui->is_tab = TRUE; gtk_window->gui = gui; sess->priv_data = gtk_window; mg_create_tabwindow(sess); mg_gui = gui; parent_window = gui->window; } else { gtk_window->gui = gui = mg_gui; sess->priv_data = gtk_window; gui->is_tab = TRUE; } #if 0 if (user && user->hostname) set_topic(sess, user->hostname); #endif mg_add_chan(sess); if (first_run || (newtabstofront_config == FOCUS_NEW_ONLY_ASKED && focus) || newtabstofront_config == FOCUS_NEW_ALL) chan_focus(gtk_private(sess)->tab); } #if 0 GtkWidget * mg_create_generic_tab(char *name, char *title, int force_toplevel, int link_buttons, void *close_callback, void *userdata, int width, int height, GtkWidget **vbox_ret, void *family) { GtkWidget *vbox, *win; if (tab_pos_config == POS_HIDDEN && prefs.windows_as_tabs) prefs.windows_as_tabs = 0; if (force_toplevel || !prefs.windows_as_tabs) { win = gtkutil_window_new(title, name, width, height, 3); vbox = gtk_vbox_new(0, 0); *vbox_ret = vbox; gtk_container_add(GTK_CONTAINER(win), vbox); gtk_widget_show(vbox); if (close_callback) g_signal_connect(G_OBJECT(win), "destroy", G_CALLBACK(close_callback), userdata); return win; } vbox = gtk_vbox_new(0, 2); g_object_set_data(G_OBJECT(vbox), "w", GINT_TO_POINTER(width)); g_object_set_data(G_OBJECT(vbox), "h", GINT_TO_POINTER(height)); gtk_container_set_border_width(GTK_CONTAINER(vbox), 3); *vbox_ret = vbox; if (close_callback) g_signal_connect(G_OBJECT(vbox), "destroy", G_CALLBACK(close_callback), userdata); mg_add_generic_tab(name, title, family, vbox); /* if (link_buttons) { hbox = gtk_hbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (vbox), hbox, 0, 0, 0); mg_create_link_buttons (hbox, ch); gtk_widget_show (hbox); }*/ return vbox; } void mg_move_tab(window_t *sess, int delta) { if (sess->gui->is_tab) chan_move(sess->res->tab, delta); } void mg_set_title(GtkWidget *vbox, char *title) { /* for non-irc tab/window only */ char *old; old = g_object_get_data(G_OBJECT(vbox), "title"); if (old) { g_object_set_data(G_OBJECT(vbox), "title", xstrdup(title)); free(old); } else { gtk_window_set_title(GTK_WINDOW(vbox), title); } } #endif /* called when a session is being killed */ void fe_close_window(window_t *sess) { printf("fe_close_window(%p)\n", sess); if (gtk_private_ui(sess)->is_tab) mg_tab_close(sess); else gtk_widget_destroy(gtk_private_ui(sess)->window); if (gtk_private_ui(sess) != &static_mg_gui) xfree(gtk_private_ui(sess)); /* free gui, if not static */ xfree(sess->priv_data); /* free window strukt */ sess->priv_data = NULL; } /* NOT COPIED: * * is_child_of() mg_handle_drop() mg_drag_begin_cb() mg_drag_end_cb() mg_drag_drop_cb() * mg_drag_motion_cb() * mg_dialog_dnd_drop() * mg_dnd_drop_file() */ /* mg_count_dccs() mg_count_networks() */ /* inne okienka, ,,generic'': * mg_link_gentab() wywolywany z mg_detach_tab_cb() * mg_close_gen() wywolywany z mg_xbutton_cb() */ ekg2-0.4~pre+20120506.1/plugins/gtk/maingui.h000066400000000000000000000012131175142753400202220ustar00rootroot00000000000000#ifndef __GTK_MAINGUI_H #define __GTK_MAINGUI_H gboolean mg_populate_userlist(window_t *sess); void mg_populate(window_t *sess); void fe_set_channel(window_t *sess); void mg_changui_new(window_t *sess, gtk_window_t *res, int tab, int focus); void fe_set_tab_color(window_t *sess, int col); void fe_close_window(window_t *sess); void mg_apply_setup(void); void mg_change_layout(int type); void mg_switch_page(int relative, int num); void mg_detach(window_t *sess, int mode); void mg_close_sess(window_t *sess); void mg_open_quit_dialog(gboolean minimize_button); void mg_changui_new(window_t *sess, gtk_window_t *res, int tab, int focus); #endif ekg2-0.4~pre+20120506.1/plugins/gtk/menu.c000066400000000000000000001313351175142753400175410ustar00rootroot00000000000000/* X-Chat * Copyright (C) 1998-2007 Peter Zelezny. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ /* * port to ekg2: * Copyright (C) 2007 Jakub Zawadzki * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "main.h" #include "maingui.h" #include "palette.h" #include "xtext.h" #if 0 #include "fe-gtk.h" #include "../common/xchat.h" #include "../common/xchatc.h" #include "../common/cfgfiles.h" #include "../common/outbound.h" #include "../common/ignore.h" #include "../common/fe.h" #include "../common/server.h" #include "../common/util.h" #include "about.h" #include "ascii.h" #include "banlist.h" #include "chanlist.h" #include "editlist.h" #include "fkeys.h" #include "gtkutil.h" #include "maingui.h" #include "notifygui.h" #include "pixmaps.h" #include "plugingui.h" #include "search.h" #include "textgui.h" #include "urlgrab.h" #include "userlistgui.h" #endif #include "menu.h" static GSList *submenu_list; enum { M_MENUITEM, M_NEWMENU, M_END, M_SEP, M_MENUTOG, M_MENURADIO, M_MENUSTOCK, M_MENUPIX, M_MENUSUB }; struct mymenu { char *text; void *callback; char *image; unsigned char type; /* M_XXX */ unsigned char id; /* MENU_ID_XXX (menu.h) */ unsigned char state; /* ticked or not? */ unsigned char sensitive; /* shaded out? */ guint key; /* GDK_x */ }; static void menu_about(GtkWidget *wid, gpointer sess) { GtkWidget *vbox, *label, *hbox; static GtkWidget *about = NULL; char buf[512]; if (about) { gtk_window_present(GTK_WINDOW(about)); return; } about = gtk_dialog_new(); gtk_window_set_position(GTK_WINDOW (about), GTK_WIN_POS_CENTER); gtk_window_set_resizable(GTK_WINDOW (about), FALSE); gtk_window_set_title(GTK_WINDOW(about), _("About ekg2")); vbox = GTK_DIALOG(about)->vbox; wid = gtk_image_new_from_pixbuf(pix_ekg2); gtk_container_add(GTK_CONTAINER(vbox), wid); label = gtk_label_new(NULL); gtk_label_set_selectable(GTK_LABEL (label), TRUE); gtk_container_add(GTK_CONTAINER(vbox), label); snprintf(buf, sizeof(buf), "ekg2-%s\n\n" "Compiled on: %s\n\n" "gtk frontend based on xchat: \302\251 1998-2010 Peter \305\275elezn\303\275 <zed@xchat.org>\n" "iconsets in userlist copied from psi (crystal-gadu.jisp and crystal-roster.jisp and crystal-icq.jisp) (c) Remko Tronçon", VERSION, compile_time()); gtk_label_set_markup(GTK_LABEL(label), buf); gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER); hbox = gtk_hbox_new(0, 2); gtk_container_add(GTK_CONTAINER(vbox), hbox); wid = gtk_button_new_from_stock(GTK_STOCK_CLOSE); GTK_WIDGET_SET_FLAGS(GTK_WIDGET(wid), GTK_CAN_DEFAULT); gtk_dialog_add_action_widget(GTK_DIALOG(about), wid, GTK_RESPONSE_OK); gtk_widget_grab_default(wid); gtk_widget_show_all(about); gtk_dialog_run(GTK_DIALOG(about)); gtk_widget_destroy(about); about = NULL; } #if 0 /* execute a userlistbutton/popupmenu command */ static void nick_command(session *sess, char *cmd) { if (*cmd == '!') xchat_exec(cmd + 1); else handle_command(sess, cmd, TRUE); } /* fill in the %a %s %n etc and execute the command */ void nick_command_parse(session *sess, char *cmd, char *nick, char *allnick) { char *buf; char *host = _("Host unknown"); struct User *user; int len; /* if (sess->type == SESS_DIALOG) { buf = (char *)(GTK_ENTRY (sess->gui->topic_entry)->text); buf = strrchr (buf, '@'); if (buf) host = buf + 1; } else*/ { user = userlist_find(sess, nick); if (user && user->hostname) host = strchr(user->hostname, '@') + 1; } /* this can't overflow, since popup->cmd is only 256 */ len = strlen(cmd) + strlen(nick) + strlen(allnick) + 512; buf = malloc(len); auto_insert(buf, len, cmd, 0, 0, allnick, sess->channel, "", server_get_network(sess->server, TRUE), host, sess->server->nick, nick); nick_command(sess, buf); free(buf); } /* userlist button has been clicked */ void userlist_button_cb(GtkWidget *button, char *cmd) { int i, num_sel, using_allnicks = FALSE; char **nicks, *allnicks; char *nick = NULL; session *sess; sess = current_sess; if (strstr(cmd, "%a")) using_allnicks = TRUE; if (sess->type == SESS_DIALOG) { /* fake a selection */ nicks = malloc(sizeof(char *) * 2); nicks[0] = g_strdup(sess->channel); nicks[1] = NULL; num_sel = 1; } else { /* find number of selected rows */ nicks = userlist_selection_list(sess->gui->user_tree, &num_sel); if (num_sel < 1) { nick_command_parse(sess, cmd, "", ""); return; } } /* create "allnicks" string */ allnicks = malloc(((NICKLEN + 1) * num_sel) + 1); *allnicks = 0; i = 0; while (nicks[i]) { if (i > 0) strcat(allnicks, " "); strcat(allnicks, nicks[i]); if (!nick) nick = nicks[0]; /* if not using "%a", execute the command once for each nickname */ if (!using_allnicks) nick_command_parse(sess, cmd, nicks[i], ""); i++; } if (using_allnicks) { if (!nick) nick = ""; nick_command_parse(sess, cmd, nick, allnicks); } while (num_sel) { num_sel--; g_free(nicks[num_sel]); } free(nicks); free(allnicks); } #endif /* a popup-menu-item has been selected */ static void popup_menu_cb(GtkWidget *item, char *cmd) { char *nick; /* the userdata is set in menu_quick_item() */ nick = g_object_get_data(G_OBJECT(item), "u"); #if 0 if (!nick) { /* userlist popup menu */ /* treat it just like a userlist button */ userlist_button_cb(NULL, cmd); return; } if (!current_sess) /* for url grabber window */ nick_command_parse(sess_list->data, cmd, nick, nick); else nick_command_parse(current_sess, cmd, nick, nick); #endif } #if 0 GtkWidget *menu_toggle_item(char *label, GtkWidget *menu, void *callback, void *userdata, int state) { GtkWidget *item; item = gtk_check_menu_item_new_with_mnemonic(label); gtk_check_menu_item_set_active((GtkCheckMenuItem *) item, state); gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(callback), userdata); gtk_widget_show(item); return item; } #endif GtkWidget *menu_quick_item(char *cmd, char *label, GtkWidget *menu, int flags, gpointer userdata, char *icon) { GtkWidget *img, *item; if (!label) item = gtk_menu_item_new(); else { if (icon) { /*if (flags & XCMENU_MARKUP) item = gtk_image_menu_item_new_with_markup (label); else */ item = gtk_image_menu_item_new_with_mnemonic(label); img = gtk_image_new_from_file(icon); if (img) gtk_image_menu_item_set_image((GtkImageMenuItem *) item, img); else { img = gtk_image_new_from_stock(icon, GTK_ICON_SIZE_MENU); if (img) gtk_image_menu_item_set_image((GtkImageMenuItem *) item, img); } } else { if (flags & XCMENU_MARKUP) { item = gtk_menu_item_new_with_label(""); if (flags & XCMENU_MNEMONIC) gtk_label_set_markup_with_mnemonic(GTK_LABEL (GTK_BIN(item)->child), label); else gtk_label_set_markup(GTK_LABEL(GTK_BIN(item)->child), label); } else { if (flags & XCMENU_MNEMONIC) item = gtk_menu_item_new_with_mnemonic(label); else item = gtk_menu_item_new_with_label(label); } } } gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); g_object_set_data(G_OBJECT(item), "u", userdata); if (cmd) g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(popup_menu_cb), cmd); if (flags & XCMENU_SHADED) gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE); gtk_widget_show_all(item); return item; } #if 0 static void menu_quick_item_with_callback(void *callback, char *label, GtkWidget *menu, void *arg) { GtkWidget *item; item = gtk_menu_item_new_with_label(label); gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(callback), arg); gtk_widget_show(item); } #endif GtkWidget *menu_quick_sub(char *name, GtkWidget *menu, GtkWidget **sub_item_ret, int flags, int pos) { GtkWidget *sub_menu; GtkWidget *sub_item; if (!name) return menu; /* Code to add a submenu */ sub_menu = gtk_menu_new(); if (flags & XCMENU_MARKUP) { sub_item = gtk_menu_item_new_with_label(""); gtk_label_set_markup(GTK_LABEL(GTK_BIN(sub_item)->child), name); } else { if (flags & XCMENU_MNEMONIC) sub_item = gtk_menu_item_new_with_mnemonic(name); else sub_item = gtk_menu_item_new_with_label(name); } gtk_menu_shell_insert(GTK_MENU_SHELL(menu), sub_item, pos); gtk_widget_show(sub_item); gtk_menu_item_set_submenu(GTK_MENU_ITEM(sub_item), sub_menu); if (sub_item_ret) *sub_item_ret = sub_item; if (flags & XCMENU_DOLIST) /* We create a new element in the list */ submenu_list = g_slist_prepend(submenu_list, sub_menu); return sub_menu; } static GtkWidget *menu_quick_endsub() { /* Just delete the first element in the linked list pointed to by first */ if (submenu_list) submenu_list = g_slist_remove(submenu_list, submenu_list->data); if (submenu_list) return (submenu_list->data); else return NULL; } #if 0 static void toggle_cb(GtkWidget *item, char *pref_name) { char buf[256]; if (GTK_CHECK_MENU_ITEM(item)->active) snprintf(buf, sizeof(buf), "set %s 1", pref_name); else snprintf(buf, sizeof(buf), "set %s 0", pref_name); handle_command(current_sess, buf, FALSE); } static int is_in_path(char *cmd) { char *prog = strdup(cmd + 1); /* 1st char is "!" */ char *space, *path, *orig; orig = prog; /* save for free()ing */ /* special-case these default entries. */ /* 123456789012345678 */ if (strncmp(prog, "gnome-terminal -x ", 18) == 0) /* don't check for gnome-terminal, but the thing it's executing! */ prog += 18; space = strchr(prog, ' '); /* this isn't 100% but good enuf */ if (space) *space = 0; path = g_find_program_in_path(prog); if (path) { g_free(path); g_free(orig); return 1; } g_free(orig); return 0; } /* append items to "menu" using the (struct popup*) list provided */ void menu_create(GtkWidget *menu, GSList * list, char *target, int check_path) { struct popup *pop; GtkWidget *tempmenu = menu, *subitem = NULL; int childcount = 0; submenu_list = g_slist_prepend(0, menu); while (list) { pop = (struct popup *)list->data; if (!strncasecmp(pop->name, "SUB", 3)) { childcount = 0; tempmenu = menu_quick_sub(pop->cmd, tempmenu, &subitem, XCMENU_DOLIST, -1); } else if (!strncasecmp(pop->name, "TOGGLE", 6)) { childcount++; menu_toggle_item(pop->name + 7, tempmenu, toggle_cb, pop->cmd, cfg_get_bool(pop->cmd)); } else if (!strncasecmp(pop->name, "ENDSUB", 6)) { /* empty sub menu due to no programs in PATH? */ if (check_path && childcount < 1) gtk_widget_destroy(subitem); subitem = NULL; if (tempmenu != menu) tempmenu = menu_quick_endsub(); /* If we get here and tempmenu equals menu that means we havent got any submenus to exit from */ } else if (!strncasecmp(pop->name, "SEP", 3)) { menu_quick_item(0, 0, tempmenu, XCMENU_SHADED, 0, 0); } else { if (!check_path || pop->cmd[0] != '!') { menu_quick_item(pop->cmd, pop->name, tempmenu, 0, target, 0); /* check if the program is in path, if not, leave it out! */ } else if (is_in_path(pop->cmd)) { childcount++; menu_quick_item(pop->cmd, pop->name, tempmenu, 0, target, 0); } } list = list->next; } /* Let's clean up the linked list from mem */ while (submenu_list) submenu_list = g_slist_remove(submenu_list, submenu_list->data); } #endif static char *str_copy = NULL; /* for all pop-up menus */ static GtkWidget *nick_submenu = NULL; /* user info submenu */ static void menu_destroy(GtkWidget *menu, gpointer objtounref) { gtk_widget_destroy(menu); g_object_unref(menu); if (objtounref) g_object_unref(G_OBJECT(objtounref)); nick_submenu = NULL; } static void menu_popup(GtkWidget *menu, GdkEventButton * event, gpointer objtounref) { #if (GTK_MAJOR_VERSION != 2) || (GTK_MINOR_VERSION != 0) if (event && event->window) gtk_menu_set_screen(GTK_MENU(menu), gdk_drawable_get_screen(event->window)); #endif g_object_ref(menu); g_object_ref_sink(menu); g_object_unref(menu); g_signal_connect(G_OBJECT(menu), "selection-done", G_CALLBACK(menu_destroy), objtounref); gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 0, event ? event->time : 0); } void menu_nickmenu(window_t *sess, GdkEventButton * event, char *nick, int num_sel) { char buf[512]; GtkWidget *menu = gtk_menu_new(); userlist_t *user; if (str_copy) free(str_copy); str_copy = xstrdup(nick); submenu_list = NULL; /* first time through, might not be 0 */ /* [XXX, how does this work? memleak? */ /* more than 1 nick selected? */ if (num_sel > 1) { snprintf(buf, sizeof(buf), "%d nicks selected.", num_sel); menu_quick_item(0, buf, menu, 0, 0, 0); menu_quick_item(0, 0, menu, XCMENU_SHADED, 0, 0); } else { user = userlist_find(sess->session, nick); /* XXX, * jesli nadal nie ma uzytkownika, to szukaj go w konferencjach */ if (user) { GtkWidget *submenu = menu_quick_sub(nick, menu, NULL, XCMENU_DOLIST, -1); char *fmt = "%-11s %s"; /* XXX, gettext? (let the translators tweak this if need be) */ char *real; /* UID */ real = g_markup_escape_text(user->uid, -1); snprintf(buf, sizeof(buf), fmt, "UID:", real); g_free(real); menu_quick_item(0, buf, submenu, XCMENU_MARKUP, 0, 0); /* ? */ /* XXX, get more data using USERLIST_PRIVHANDLE (?) */ /* the same like above */ menu_quick_endsub(); menu_quick_item(0, 0, menu, XCMENU_SHADED, 0, 0); } } #if 0 if (num_sel > 1) menu_create(menu, popup_list, NULL, FALSE); else menu_create(menu, popup_list, str_copy, FALSE); if (num_sel == 0) /* xtext click */ menu_add_plugin_items(menu, "\x5$NICK", str_copy); else /* userlist treeview click */ menu_add_plugin_items(menu, "\x5$NICK", NULL); #endif menu_popup(menu, event, NULL); } #if 0 /* stuff for the View menu */ static void menu_showhide_cb(session *sess) { if (prefs.hidemenu) gtk_widget_hide(sess->gui->menu); else gtk_widget_show(sess->gui->menu); } static void menu_topic_showhide_cb(session *sess) { if (prefs.topicbar) gtk_widget_show(sess->gui->topic_bar); else gtk_widget_hide(sess->gui->topic_bar); } static void menu_userlist_showhide_cb(session *sess) { mg_decide_userlist(sess, TRUE); } static void menu_ulbuttons_showhide_cb(session *sess) { if (prefs.userlistbuttons) gtk_widget_show(sess->gui->button_box); else gtk_widget_hide(sess->gui->button_box); } static void menu_cmbuttons_showhide_cb(session *sess) { switch (sess->type) { case SESS_CHANNEL: if (prefs.chanmodebuttons) gtk_widget_show(sess->gui->topicbutton_box); else gtk_widget_hide(sess->gui->topicbutton_box); break; default: gtk_widget_hide(sess->gui->topicbutton_box); } } #endif static void menu_setting_foreach(void (*callback) (window_t *), int id, guint state) { int maindone = FALSE; /* do it only once for EVERY tab */ window_t *w; for (w = windows; w; w = w->next) { gtk_window_ui_t *gui = gtk_private_ui(w); if (!gui->is_tab || !maindone) { if (gui->is_tab) maindone = TRUE; if (id != -1) GTK_CHECK_MENU_ITEM(gui->menu_item[id])->active = state; if (callback) callback(w); } } } #if 0 void menu_bar_toggle(void) { prefs.hidemenu = !prefs.hidemenu; menu_setting_foreach(menu_showhide_cb, MENU_ID_MENUBAR, !prefs.hidemenu); } static void menu_bar_toggle_cb(void) { menu_bar_toggle(); if (prefs.hidemenu) fe_message(_("The Menubar is now hidden. You can show it again" " by pressing F9 or right-clicking in a blank part of" " the main text area."), FE_MSG_INFO); } static void menu_topicbar_toggle(GtkWidget *wid, gpointer ud) { prefs.topicbar = !prefs.topicbar; menu_setting_foreach(menu_topic_showhide_cb, MENU_ID_TOPICBAR, prefs.topicbar); } static void menu_userlist_toggle(GtkWidget *wid, gpointer ud) { prefs.hideuserlist = !prefs.hideuserlist; menu_setting_foreach(menu_userlist_showhide_cb, MENU_ID_USERLIST, !prefs.hideuserlist); } static void menu_ulbuttons_toggle(GtkWidget *wid, gpointer ud) { prefs.userlistbuttons = !prefs.userlistbuttons; menu_setting_foreach(menu_ulbuttons_showhide_cb, MENU_ID_ULBUTTONS, prefs.userlistbuttons); } static void menu_cmbuttons_toggle(GtkWidget *wid, gpointer ud) { prefs.chanmodebuttons = !prefs.chanmodebuttons; menu_setting_foreach(menu_cmbuttons_showhide_cb, MENU_ID_MODEBUTTONS, prefs.chanmodebuttons); } void menu_middlemenu(session *sess, GdkEventButton * event) { GtkWidget *menu; GtkAccelGroup *accel_group; accel_group = gtk_accel_group_new(); menu = menu_create_main(accel_group, FALSE, sess->server->is_away, !sess->gui->is_tab, NULL); menu_popup(menu, event, accel_group); } static void open_url_cb(GtkWidget *item, char *url) { char buf[512]; /* pass this to /URL so it can handle irc:// */ snprintf(buf, sizeof(buf), "URL %s", url); handle_command(current_sess, buf, FALSE); } static void copy_to_clipboard_cb(GtkWidget *item, char *url) { gtkutil_copy_to_clipboard(item, NULL, url); } void menu_urlmenu(GdkEventButton * event, char *url) { GtkWidget *menu; char *tmp, *chop; if (str_copy) free(str_copy); str_copy = strdup(url); menu = gtk_menu_new(); /* more than 51 chars? Chop it */ if (g_utf8_strlen(str_copy, -1) >= 52) { tmp = strdup(str_copy); chop = g_utf8_offset_to_pointer(tmp, 48); chop[0] = chop[1] = chop[2] = '.'; chop[3] = 0; menu_quick_item(0, tmp, menu, XCMENU_SHADED, 0, 0); free(tmp); } else { menu_quick_item(0, str_copy, menu, XCMENU_SHADED, 0, 0); } menu_quick_item(0, 0, menu, XCMENU_SHADED, 0, 0); /* Two hardcoded entries */ if (strncmp(str_copy, "irc://", 6) == 0 || strncmp(str_copy, "ircs://", 7) == 0) menu_quick_item_with_callback(open_url_cb, _("Connect"), menu, str_copy); else menu_quick_item_with_callback(open_url_cb, _("Open Link in Browser"), menu, str_copy); menu_quick_item_with_callback(copy_to_clipboard_cb, _("Copy Selected Link"), menu, str_copy); /* custom ones from urlhandlers.conf */ menu_create(menu, urlhandler_list, str_copy, TRUE); menu_add_plugin_items(menu, "\x4$URL", str_copy); menu_popup(menu, event, NULL); } static void menu_chan_cycle(GtkWidget *menu, char *chan) { char tbuf[256]; if (current_sess) { snprintf(tbuf, sizeof tbuf, "CYCLE %s", chan); handle_command(current_sess, tbuf, FALSE); } } static void menu_chan_part(GtkWidget *menu, char *chan) { char tbuf[256]; if (current_sess) { snprintf(tbuf, sizeof tbuf, "part %s", chan); handle_command(current_sess, tbuf, FALSE); } } static void menu_chan_join(GtkWidget *menu, char *chan) { char tbuf[256]; if (current_sess) { snprintf(tbuf, sizeof tbuf, "join %s", chan); handle_command(current_sess, tbuf, FALSE); } } void menu_chanmenu(struct session *sess, GdkEventButton * event, char *chan) { GtkWidget *menu; int is_joined = FALSE; if (find_channel(sess->server, chan)) is_joined = TRUE; if (str_copy) free(str_copy); str_copy = strdup(chan); menu = gtk_menu_new(); menu_quick_item(0, chan, menu, XCMENU_SHADED, str_copy, 0); menu_quick_item(0, 0, menu, XCMENU_SHADED, str_copy, 0); if (!is_joined) menu_quick_item_with_callback(menu_chan_join, _("Join Channel"), menu, str_copy); else { menu_quick_item_with_callback(menu_chan_part, _("Part Channel"), menu, str_copy); menu_quick_item_with_callback(menu_chan_cycle, _("Cycle Channel"), menu, str_copy); } menu_add_plugin_items(menu, "\x5$CHAN", str_copy); menu_popup(menu, event, NULL); } static void menu_open_server_list(GtkWidget *wid, gpointer none) { fe_serverlist_open(current_sess); } static void menu_settings(GtkWidget *wid, gpointer none) { extern void setup_open(void); setup_open(); } static void menu_usermenu(void) { editlist_gui_open(NULL, NULL, usermenu_list, _("XChat: User menu"), "usermenu", "usermenu.conf", 0); } static void usermenu_create(GtkWidget *menu) { menu_create(menu, usermenu_list, "", FALSE); menu_quick_item(0, 0, menu, XCMENU_SHADED, 0, 0); /* sep */ menu_quick_item_with_callback(menu_usermenu, _("Edit This Menu..."), menu, 0); } static void usermenu_destroy(GtkWidget *menu) { GList *items = ((GtkMenuShell *) menu)->children; GList *next; while (items) { next = items->next; gtk_widget_destroy(items->data); items = next; } } void usermenu_update(void) { int done_main = FALSE; GSList *list = sess_list; session *sess; GtkWidget *menu; while (list) { sess = list->data; menu = sess->gui->menu_item[MENU_ID_USERMENU]; if (sess->gui->is_tab) { if (!done_main && menu) { usermenu_destroy(menu); usermenu_create(menu); done_main = TRUE; } } else if (menu) { usermenu_destroy(menu); usermenu_create(menu); } list = list->next; } } #endif /* XXX, window_current->session */ static void menu_newwindow_window(GtkWidget *wid, gpointer none) { int old = new_window_in_tab_config; new_window_in_tab_config = 0; window_new(NULL, window_current->session, 0); new_window_in_tab_config = old; } static void menu_newwindow_tab(GtkWidget *wid, gpointer none) { int old = new_window_in_tab_config; new_window_in_tab_config = 1; window_new(NULL, window_current->session, 0); new_window_in_tab_config = old; } static void menu_detach(GtkWidget *wid, gpointer none) { mg_detach(window_current, 0); } static void menu_close(GtkWidget *wid, gpointer none) { mg_close_sess(window_current); } static void menu_quit(GtkWidget *wid, gpointer none) { mg_open_quit_dialog(FALSE); } #if 0 static void menu_search() { search_open(current_sess); } #endif static void menu_resetmarker(GtkWidget *wid, gpointer none) { gtk_xtext_reset_marker_pos(GTK_XTEXT(gtk_private_ui(window_current)->xtext)); } static void menu_flushbuffer(GtkWidget *wid, gpointer none) { gtk_xtext_clear(gtk_private(window_current)->buffer); } #if 0 static void menu_disconnect(GtkWidget *wid, gpointer none) { handle_command(current_sess, "DISCON", FALSE); } static void menu_reconnect(GtkWidget *wid, gpointer none) { if (current_sess->server->hostname[0]) handle_command(current_sess, "RECONNECT", FALSE); else fe_serverlist_open(current_sess); } static void menu_join_cb(GtkWidget *dialog, gint response, GtkEntry * entry) { switch (response) { case GTK_RESPONSE_ACCEPT: menu_chan_join(NULL, entry->text); break; case GTK_RESPONSE_HELP: chanlist_opengui(current_sess->server, TRUE); break; } gtk_widget_destroy(dialog); } static void menu_join_entry_cb(GtkWidget *entry, GtkDialog * dialog) { gtk_dialog_response(dialog, GTK_RESPONSE_ACCEPT); } static void menu_join(GtkWidget *wid, gpointer none) { GtkWidget *hbox, *dialog, *entry, *label; dialog = gtk_dialog_new_with_buttons(_("Join Channel"), GTK_WINDOW(parent_window), 0, _("Retrieve channel list..."), GTK_RESPONSE_HELP, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL); gtk_box_set_homogeneous(GTK_BOX(GTK_DIALOG(dialog)->vbox), TRUE); gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE); hbox = gtk_hbox_new(TRUE, 0); entry = gtk_entry_new(); GTK_ENTRY(entry)->editable = 0; /* avoid auto-selection */ gtk_entry_set_text(GTK_ENTRY(entry), "#"); g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(menu_join_entry_cb), dialog); gtk_box_pack_end(GTK_BOX(hbox), entry, 0, 0, 0); label = gtk_label_new(_("Enter Channel to Join:")); gtk_box_pack_end(GTK_BOX(hbox), label, 0, 0, 0); g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(menu_join_cb), entry); gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), hbox); gtk_widget_show_all(dialog); gtk_editable_set_editable(GTK_EDITABLE(entry), TRUE); gtk_editable_set_position(GTK_EDITABLE(entry), 1); } static void menu_away(GtkCheckMenuItem * item, gpointer none) { handle_command(current_sess, item->active ? "away" : "back", FALSE); } static void menu_chanlist(GtkWidget *wid, gpointer none) { chanlist_opengui(current_sess->server, FALSE); } static void menu_banlist(GtkWidget *wid, gpointer none) { banlist_opengui(current_sess); } static void menu_rpopup(void) { editlist_gui_open(_("Text"), _("Replace with"), replace_list, _("XChat: Replace"), "replace", "replace.conf", 0); } static void menu_urlhandlers(void) { editlist_gui_open(NULL, NULL, urlhandler_list, _("XChat: URL Handlers"), "urlhandlers", "urlhandlers.conf", url_help); } static void menu_evtpopup(void) { pevent_dialog_show(); } static void menu_dcc_win(GtkWidget *wid, gpointer none) { fe_dcc_open_recv_win(FALSE); fe_dcc_open_send_win(FALSE); } static void menu_dcc_chat_win(GtkWidget *wid, gpointer none) { fe_dcc_open_chat_win(FALSE); } #endif void menu_change_layout(void) { if (tab_layout_config == 0) { menu_setting_foreach(NULL, MENU_ID_LAYOUT_TABS, 1); menu_setting_foreach(NULL, MENU_ID_LAYOUT_TREE, 0); mg_change_layout(0); } else { menu_setting_foreach(NULL, MENU_ID_LAYOUT_TABS, 0); menu_setting_foreach(NULL, MENU_ID_LAYOUT_TREE, 1); mg_change_layout(2); } } static void menu_layout_cb(GtkWidget *item, gpointer none) { tab_layout_config = 2; if (GTK_CHECK_MENU_ITEM(item)->active) tab_layout_config = 0; menu_change_layout(); } static GdkPixbuf *pix_book = NULL; /* XXX */ static struct mymenu mymenu[] = { {N_("_ekg2"), 0, 0, M_NEWMENU, 0, 0, 1}, #define menu_open_server_list NULL #define menu_loadplugin NULL {N_("Session Li_st..."), menu_open_server_list, (char *)&pix_book, M_MENUPIX, 0, 0, 1, GDK_s}, {0, 0, 0, M_SEP, 0, 0, 0}, {N_("_New"), 0, GTK_STOCK_NEW, M_MENUSUB, 0, 0, 1}, {N_("Window in Tab..."), menu_newwindow_tab, 0, M_MENUITEM, 0, 0, 1, GDK_t}, {N_("Window in new window..."), menu_newwindow_window, 0, M_MENUITEM, 0, 0, 1}, {0, 0, 0, M_END, 0, 0, 0}, {0, 0, 0, M_SEP, 0, 0, 0}, {N_("_Load Plugin or Script..."), menu_loadplugin, GTK_STOCK_REVERT_TO_SAVED, M_MENUSTOCK, 0, 0, 1}, {0, 0, 0, M_SEP, 0, 0, 0}, /* 9 */ #define DETACH_OFFSET (10) {0, menu_detach, GTK_STOCK_REDO, M_MENUSTOCK, 0, 0, 1, GDK_I}, /* 10 */ #define CLOSE_OFFSET (11) {0, menu_close, GTK_STOCK_CLOSE, M_MENUSTOCK, 0, 0, 1, GDK_w}, {0, 0, 0, M_SEP, 0, 0, 0}, {N_("_Quit"), menu_quit, GTK_STOCK_QUIT, M_MENUSTOCK, 0, 0, 1, GDK_q}, /* 13 */ {N_("_View"), 0, 0, M_NEWMENU, 0, 0, 1}, #if 0 #define MENUBAR_OFFSET (17) {N_("_Menu Bar"), menu_bar_toggle_cb, 0, M_MENUTOG, MENU_ID_MENUBAR, 0, 1, GDK_F9}, {N_("_Topic Bar"), menu_topicbar_toggle, 0, M_MENUTOG, MENU_ID_TOPICBAR, 0, 1}, {N_("_User List"), menu_userlist_toggle, 0, M_MENUTOG, MENU_ID_USERLIST, 0, 1, GDK_F7}, {N_("U_serlist Buttons"), menu_ulbuttons_toggle, 0, M_MENUTOG, MENU_ID_ULBUTTONS, 0, 1}, {N_("M_ode Buttons"), menu_cmbuttons_toggle, 0, M_MENUTOG, MENU_ID_MODEBUTTONS, 0, 1}, {0, 0, 0, M_SEP, 0, 0, 0}, #endif {N_("_Channel Switcher"), 0, 0, M_MENUSUB, 0, 0, 1}, /* 15 */ #define TABS_OFFSET (16) {N_("_Tabs"), menu_layout_cb, 0, M_MENURADIO, MENU_ID_LAYOUT_TABS, 0, 1}, {N_("T_ree"), 0, 0, M_MENURADIO, MENU_ID_LAYOUT_TREE, 0, 1}, {0, 0, 0, M_END, 0, 0, 0}, #if 0 {N_("_Server"), 0, 0, M_NEWMENU, 0, 0, 1}, {N_("_Disconnect"), menu_disconnect, GTK_STOCK_DISCONNECT, M_MENUSTOCK, MENU_ID_DISCONNECT, 0, 1}, {N_("_Reconnect"), menu_reconnect, GTK_STOCK_CONNECT, M_MENUSTOCK, MENU_ID_RECONNECT, 0, 1}, {N_("Join Channel..."), menu_join, GTK_STOCK_JUMP_TO, M_MENUSTOCK, MENU_ID_JOIN, 0, 1}, {0, 0, 0, M_SEP, 0, 0, 0}, #define AWAY_OFFSET (38) {N_("Marked Away"), menu_away, 0, M_MENUTOG, MENU_ID_AWAY, 0, 1, GDK_a}, {N_("_Usermenu"), 0, 0, M_NEWMENU, MENU_ID_USERMENU, 0, 1}, /* 39 */ {N_("S_ettings"), 0, 0, M_NEWMENU, 0, 0, 1}, {N_("_Preferences"), menu_settings, GTK_STOCK_PREFERENCES, M_MENUSTOCK, 0, 0, 1}, {N_("Advanced"), 0, GTK_STOCK_JUSTIFY_LEFT, M_MENUSUB, 0, 0, 1}, {N_("Auto Replace..."), menu_rpopup, 0, M_MENUITEM, 0, 0, 1}, {N_("CTCP Replies..."), menu_ctcpguiopen, 0, M_MENUITEM, 0, 0, 1}, {N_("Text Events..."), menu_evtpopup, 0, M_MENUITEM, 0, 0, 1}, {N_("URL Handlers..."), menu_urlhandlers, 0, M_MENUITEM, 0, 0, 1}, {N_("Userlist Buttons..."), menu_ulbuttons, 0, M_MENUITEM, 0, 0, 1}, {0, 0, 0, M_END, 0, 0, 0}, /* 52 */ #endif {N_("_Window"), 0, 0, M_NEWMENU, 0, 0, 1}, #if 0 {N_("Ban List..."), menu_banlist, 0, M_MENUITEM, 0, 0, 1}, {N_("Channel List..."), menu_chanlist, 0, M_MENUITEM, 0, 0, 1}, {N_("Character Chart..."), ascii_open, 0, M_MENUITEM, 0, 0, 1}, {N_("Direct Chat..."), menu_dcc_chat_win, 0, M_MENUITEM, 0, 0, 1}, {N_("File Transfers..."), menu_dcc_win, 0, M_MENUITEM, 0, 0, 1}, {N_("Friends List..."), notify_opengui, 0, M_MENUITEM, 0, 0, 1}, {N_("Ignore List..."), ignore_gui_open, 0, M_MENUITEM, 0, 0, 1}, {N_("Plugins and Scripts..."), menu_pluginlist, 0, M_MENUITEM, 0, 0, 1}, #endif {0, 0, 0, M_SEP, 0, 0, 0}, {N_("Reset Marker Line"), menu_resetmarker, 0, M_MENUITEM, 0, 0, 1, GDK_m}, {N_("C_lear Text"), menu_flushbuffer, GTK_STOCK_CLEAR, M_MENUSTOCK, 0, 0, 1, GDK_l}, #define menu_search NULL #define SEARCH_OFFSET 20 /* ? */ {N_("Search Text..."), menu_search, GTK_STOCK_FIND, M_MENUSTOCK, 0, 0, 1, GDK_f}, #if 0 #endif {N_("_Help"), 0, 0, M_NEWMENU, 0, 0, 1}, /* 69 */ {N_("_About"), menu_about, GTK_STOCK_ABOUT, M_MENUSTOCK, 0, 0, 1}, {0, 0, 0, M_END, 0, 0, 0}, }; GtkWidget *create_icon_menu(char *labeltext, void *stock_name, int is_stock) { GtkWidget *item, *img; if (is_stock) img = gtk_image_new_from_stock(stock_name, GTK_ICON_SIZE_MENU); else img = gtk_image_new_from_pixbuf(*((GdkPixbuf **)stock_name)); item = gtk_image_menu_item_new_with_mnemonic(labeltext); gtk_image_menu_item_set_image((GtkImageMenuItem *) item, img); gtk_widget_show(img); return item; } #if GTK_CHECK_VERSION(2,4,0) /* Override the default GTK2.4 handler, which would make menu bindings not work when the menu-bar is hidden. */ static gboolean menu_canacaccel(GtkWidget *widget, guint signal_id, gpointer user_data) { /* GTK2.2 behaviour */ #if GTK_CHECK_VERSION(2,20,0) return gtk_widget_is_sensitive(widget); #else return GTK_WIDGET_IS_SENSITIVE(widget); #endif } #endif #if 0 /* === STUFF FOR /MENU === */ static GtkMenuItem * menu_find_item(GtkWidget *menu, char *name) { GList *items = ((GtkMenuShell *) menu)->children; GtkMenuItem *item; GtkWidget *child; const char *labeltext; while (items) { item = items->data; child = GTK_BIN(item)->child; if (child) { /* separators arn't labels, skip them */ labeltext = g_object_get_data(G_OBJECT(item), "name"); if (!labeltext) labeltext = gtk_label_get_text(GTK_LABEL(child)); if (!menu_streq(labeltext, name, 1)) return item; } else if (name == NULL) { return item; } items = items->next; } return NULL; } static GtkWidget * menu_find_path(GtkWidget *menu, char *path) { GtkMenuItem *item; char *s; char name[128]; int len; /* grab the next part of the path */ s = strchr(path, '/'); len = s - path; if (!s) len = strlen(path); len = MIN(len, sizeof(name) - 1); memcpy(name, path, len); name[len] = 0; item = menu_find_item(menu, name); if (!item) return NULL; menu = gtk_menu_item_get_submenu(item); if (!menu) return NULL; path += len; if (*path == 0) return menu; return menu_find_path(menu, path + 1); } static GtkWidget * menu_find(GtkWidget *menu, char *path, char *label) { GtkWidget *item = NULL; if (path[0] != 0) menu = menu_find_path(menu, path); if (menu) item = (GtkWidget *)menu_find_item(menu, label); return item; } static void menu_foreach_gui(menu_entry * me, void (*callback) (GtkWidget *, menu_entry *, char *)) { GSList *list = sess_list; int tabdone = FALSE; session *sess; if (!me->is_main) return; /* not main menu */ while (list) { sess = list->data; /* do it only once for tab sessions, since they share a GUI */ if (!sess->gui->is_tab || !tabdone) { callback(sess->gui->menu, me, NULL); if (sess->gui->is_tab) tabdone = TRUE; } list = list->next; } } static void menu_update_cb(GtkWidget *menu, menu_entry * me, char *target) { GtkWidget *item; item = menu_find(menu, me->path, me->label); if (item) { gtk_widget_set_sensitive(item, me->enable); /* must do it without triggering the callback */ if (GTK_IS_CHECK_MENU_ITEM(item)) GTK_CHECK_MENU_ITEM(item)->active = me->state; } } /* radio state changed via mouse click */ static void menu_radio_cb(GtkCheckMenuItem * item, menu_entry * me) { me->state = 0; if (item->active) me->state = 1; /* update the state, incase this was changed via right-click. */ /* This will update all other windows and menu bars */ menu_foreach_gui(me, menu_update_cb); if (me->state && me->cmd) handle_command(current_sess, me->cmd, FALSE); } /* toggle state changed via mouse click */ static void menu_toggle_cb(GtkCheckMenuItem * item, menu_entry * me) { me->state = 0; if (item->active) me->state = 1; /* update the state, incase this was changed via right-click. */ /* This will update all other windows and menu bars */ menu_foreach_gui(me, menu_update_cb); if (me->state) handle_command(current_sess, me->cmd, FALSE); else handle_command(current_sess, me->ucmd, FALSE); } static GtkWidget * menu_radio_item(char *label, GtkWidget *menu, void *callback, void *userdata, int state, char *groupname) { GtkWidget *item; GtkMenuItem *parent; GSList *grouplist = NULL; parent = menu_find_item(menu, groupname); if (parent) grouplist = gtk_radio_menu_item_get_group((GtkRadioMenuItem *) parent); item = gtk_radio_menu_item_new_with_label(grouplist, label); gtk_check_menu_item_set_active((GtkCheckMenuItem *) item, state); gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(callback), userdata); gtk_widget_show(item); return item; } static void menu_reorder(GtkMenu * menu, GtkWidget *item, int pos) { if (pos == 0xffff) /* outbound.c uses this default */ return; if (pos < 0) /* position offset from end/bottom */ gtk_menu_reorder_child(menu, item, (g_list_length(GTK_MENU_SHELL(menu)->children) + pos) - 1); else gtk_menu_reorder_child(menu, item, pos); } static GtkWidget * menu_add_radio(GtkWidget *menu, menu_entry * me) { GtkWidget *item = NULL; char *path = me->path + me->root_offset; if (path[0] != 0) menu = menu_find_path(menu, path); if (menu) { item = menu_radio_item(me->label, menu, menu_radio_cb, me, me->state, me->group); menu_reorder(GTK_MENU(menu), item, me->pos); } return item; } static GtkWidget * menu_add_toggle(GtkWidget *menu, menu_entry * me) { GtkWidget *item = NULL; char *path = me->path + me->root_offset; if (path[0] != 0) menu = menu_find_path(menu, path); if (menu) { item = menu_toggle_item(me->label, menu, menu_toggle_cb, me, me->state); menu_reorder(GTK_MENU(menu), item, me->pos); } return item; } static GtkWidget * menu_add_item(GtkWidget *menu, menu_entry * me, char *target) { GtkWidget *item = NULL; char *path = me->path + me->root_offset; if (path[0] != 0) menu = menu_find_path(menu, path); if (menu) { item = menu_quick_item(me->cmd, me->label, menu, me-> markup ? XCMENU_MARKUP | XCMENU_MNEMONIC : XCMENU_MNEMONIC, target, me->icon); menu_reorder(GTK_MENU(menu), item, me->pos); } return item; } static GtkWidget *menu_add_sub(GtkWidget *menu, menu_entry *me) { GtkWidget *item = NULL; char *path = me->path + me->root_offset; int pos; if (path[0] != 0) menu = menu_find_path(menu, path); if (menu) { pos = me->pos; if (pos < 0) /* position offset from end/bottom */ pos = g_list_length(GTK_MENU_SHELL(menu)->children) + pos; menu_quick_sub(me->label, menu, &item, me->markup ? XCMENU_MARKUP | XCMENU_MNEMONIC : XCMENU_MNEMONIC, pos); } return item; } static void menu_del_cb(GtkWidget *menu, menu_entry * me, char *target) { GtkWidget *item = menu_find(menu, me->path + me->root_offset, me->label); if (item) gtk_widget_destroy(item); } static void menu_add_cb(GtkWidget *menu, menu_entry * me, char *target) { GtkWidget *item; GtkAccelGroup *accel_group; if (me->group) /* have a group name? Must be a radio item */ item = menu_add_radio(menu, me); else if (me->ucmd) /* have unselect-cmd? Must be a toggle item */ item = menu_add_toggle(menu, me); else if (me->cmd || !me->label) /* label=NULL for separators */ item = menu_add_item(menu, me, target); else item = menu_add_sub(menu, me); if (item) { gtk_widget_set_sensitive(item, me->enable); if (me->key) { accel_group = g_object_get_data(G_OBJECT(menu), "accel"); if (accel_group) /* popup menus don't have them */ gtk_widget_add_accelerator(item, "activate", accel_group, me->key, me->modifier, GTK_ACCEL_VISIBLE); } } } char *fe_menu_add(menu_entry *me) { char *text; menu_foreach_gui(me, menu_add_cb); if (!me->markup) return NULL; if (!pango_parse_markup(me->label, -1, 0, NULL, &text, NULL, NULL)) return NULL; /* return the label with markup stripped */ return text; } void fe_menu_del(menu_entry *me) { menu_foreach_gui(me, menu_del_cb); } void fe_menu_update(menu_entry * me) { menu_foreach_gui(me, menu_update_cb); } #endif /* used to add custom menus to the right-click menu */ static void menu_add_plugin_mainmenu_items(GtkWidget *menu) { #if 0 GSList *list; menu_entry *me; list = menu_list; /* outbound.c */ while (list) { me = list->data; if (me->is_main) menu_add_cb(menu, me, NULL); list = list->next; } #endif } #if 0 void menu_add_plugin_items(GtkWidget *menu, char *root, char *target) { GSList *list; menu_entry *me; list = menu_list; /* outbound.c */ while (list) { me = list->data; if (!me->is_main && !strncmp(me->path, root + 1, root[0])) menu_add_cb(menu, me, target); list = list->next; } } /* === END STUFF FOR /MENU === */ #endif GtkWidget *menu_create_main(void *accel_group, int bar, int away, int toplevel, GtkWidget **menu_widgets) { int i = 0; GtkWidget *item; GtkWidget *menu = 0; GtkWidget *menu_item = 0; GtkWidget *menu_bar; #if 0 GtkWidget *usermenu = 0; #endif GtkWidget *submenu = 0; int close_mask = GDK_CONTROL_MASK; int away_mask = GDK_MOD1_MASK; char *key_theme = NULL; GtkSettings *settings; GSList *group = NULL; if (bar) menu_bar = gtk_menu_bar_new(); else menu_bar = gtk_menu_new(); /* /MENU needs to know this later */ g_object_set_data(G_OBJECT(menu_bar), "accel", accel_group); #if GTK_CHECK_VERSION(2,4,0) g_signal_connect(G_OBJECT(menu_bar), "can-activate-accel", G_CALLBACK(menu_canacaccel), 0); #endif #if 0 /* set the initial state of toggles */ mymenu[MENUBAR_OFFSET].state = !prefs.hidemenu; mymenu[MENUBAR_OFFSET + 1].state = prefs.topicbar; mymenu[MENUBAR_OFFSET + 2].state = !prefs.hideuserlist; mymenu[MENUBAR_OFFSET + 3].state = prefs.userlistbuttons; mymenu[MENUBAR_OFFSET + 4].state = prefs.chanmodebuttons; mymenu[AWAY_OFFSET].state = away; #endif switch (tab_layout_config) { case 0: mymenu[TABS_OFFSET].state = 1; mymenu[TABS_OFFSET + 1].state = 0; break; default: mymenu[TABS_OFFSET].state = 0; mymenu[TABS_OFFSET + 1].state = 1; } /* change Close binding to ctrl-shift-w when using emacs keys */ settings = gtk_widget_get_settings(menu_bar); if (settings) { g_object_get(settings, "gtk-key-theme-name", &key_theme, NULL); if (key_theme) { if (!xstrcasecmp(key_theme, "Emacs")) { close_mask = GDK_SHIFT_MASK | GDK_CONTROL_MASK; mymenu[SEARCH_OFFSET].key = 0; } g_free(key_theme); } } /* Away binding to ctrl-alt-a if the _Help menu conflicts (FR/PT/IT) */ { char *help = _("_Help"); char *under = strchr(help, '_'); if (under && (under[1] == 'a' || under[1] == 'A')) away_mask = GDK_MOD1_MASK | GDK_CONTROL_MASK; } if (!toplevel) { mymenu[DETACH_OFFSET].text = N_("_Detach"); mymenu[CLOSE_OFFSET].text = N_("_Close"); } else { mymenu[DETACH_OFFSET].text = N_("_Attach"); mymenu[CLOSE_OFFSET].text = N_("_Close"); } while (1) { item = NULL; #if 0 if (mymenu[i].id == MENU_ID_USERMENU && !prefs.gui_usermenu) { i++; continue; } #endif switch (mymenu[i].type) { case M_NEWMENU: if (menu) gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), menu); item = menu = gtk_menu_new(); #if 0 if (mymenu[i].id == MENU_ID_USERMENU) usermenu = menu; #endif menu_item = gtk_menu_item_new_with_mnemonic(_(mymenu[i].text)); /* record the English name for /menu */ g_object_set_data(G_OBJECT(menu_item), "name", mymenu[i].text); gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar), menu_item); gtk_widget_show(menu_item); break; case M_MENUPIX: item = create_icon_menu(_(mymenu[i].text), mymenu[i].image, FALSE); goto normalitem; case M_MENUSTOCK: item = create_icon_menu(_(mymenu[i].text), mymenu[i].image, TRUE); goto normalitem; case M_MENUITEM: item = gtk_menu_item_new_with_mnemonic(_(mymenu[i].text)); normalitem: if (mymenu[i].key != 0) gtk_widget_add_accelerator(item, "activate", accel_group, mymenu[i].key, mymenu[i].key == GDK_F1 ? 0 : mymenu[i].key == GDK_w ? close_mask : GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE); if (mymenu[i].callback) g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(mymenu[i].callback), 0); if (submenu) gtk_menu_shell_append(GTK_MENU_SHELL(submenu), item); else gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); gtk_widget_show(item); break; case M_MENUTOG: item = gtk_check_menu_item_new_with_mnemonic(_(mymenu[i].text)); togitem: /* must avoid callback for Radio buttons */ GTK_CHECK_MENU_ITEM(item)->active = mymenu[i].state; /*gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), mymenu[i].state); */ #if 0 if (mymenu[i].key != 0) gtk_widget_add_accelerator(item, "activate", accel_group, mymenu[i].key, mymenu[i].id == MENU_ID_AWAY ? away_mask : GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE); #endif if (mymenu[i].callback) g_signal_connect(G_OBJECT(item), "toggled", G_CALLBACK(mymenu[i].callback), 0); if (submenu) gtk_menu_shell_append(GTK_MENU_SHELL(submenu), item); else gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); gtk_widget_show(item); gtk_widget_set_sensitive(item, mymenu[i].sensitive); break; case M_MENURADIO: item = gtk_radio_menu_item_new_with_mnemonic(group, _(mymenu[i].text)); group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(item)); goto togitem; case M_SEP: item = gtk_menu_item_new(); gtk_widget_set_sensitive(item, FALSE); gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); gtk_widget_show(item); break; case M_MENUSUB: group = NULL; submenu = gtk_menu_new(); item = create_icon_menu(_(mymenu[i].text), mymenu[i].image, TRUE); /* record the English name for /menu */ g_object_set_data(G_OBJECT(item), "name", mymenu[i].text); gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu); gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); gtk_widget_show(item); break; /*case M_END: */ default: if (!submenu) { if (menu) { gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), menu); menu_add_plugin_mainmenu_items(menu_bar); } #if 0 if (usermenu) usermenu_create(usermenu); #endif return (menu_bar); } submenu = NULL; } /* record this GtkWidget * so it's state might be changed later */ if (mymenu[i].id != 0 && menu_widgets) /* this ends up in sess->gui->menu_item[MENU_ID_XXX] */ menu_widgets[mymenu[i].id] = item; i++; } } /* usuniete itemy z menu: * rawlog [fajne, ale trudne do realizacji, my mamy osobne okienko debug * {N_("Save Text..."), menu_savebuffer, GTK_STOCK_SAVE, M_MENUSTOCK, 0, 0, 1}, */ ekg2-0.4~pre+20120506.1/plugins/gtk/menu.h000066400000000000000000000033631175142753400175450ustar00rootroot00000000000000GtkWidget *menu_create_main (void *accel_group, int bar, int away, int toplevel, GtkWidget **menu_widgets); GtkWidget *create_icon_menu (char *labeltext, void *stock_name, int is_stock); void menu_nickmenu(window_t *sess, GdkEventButton * event, char *nick, int num_sel); GtkWidget *menu_quick_item (char *cmd, char *label, GtkWidget * menu, int flags, gpointer userdata, char *icon); GtkWidget *menu_quick_sub (char *name, GtkWidget *menu, GtkWidget **sub_item_ret, int flags, int pos); #if 0 void menu_urlmenu (GdkEventButton * event, char *url); void menu_chanmenu (session *sess, GdkEventButton * event, char *chan); void menu_middlemenu (session *sess, GdkEventButton *event); void userlist_button_cb (GtkWidget * button, char *cmd); void nick_command_parse (session *sess, char *cmd, char *nick, char *allnick); void usermenu_update (void); GtkWidget *menu_toggle_item (char *label, GtkWidget *menu, void *callback, void *userdata, int state); void menu_create (GtkWidget *menu, GSList *list, char *target, int check_path); void menu_bar_toggle (void); void menu_add_plugin_items (GtkWidget *menu, char *root, char *target); void menu_change_layout (void); #endif /* for menu_quick functions */ #define XCMENU_DOLIST 1 #define XCMENU_SHADED 1 #define XCMENU_MARKUP 2 #define XCMENU_MNEMONIC 4 /* menu items we keep a GtkWidget* for (to change their state) */ #define MENU_ID_AWAY 1 #define MENU_ID_MENUBAR 2 #define MENU_ID_TOPICBAR 3 #define MENU_ID_USERLIST 4 #define MENU_ID_ULBUTTONS 5 #define MENU_ID_MODEBUTTONS 6 #define MENU_ID_LAYOUT_TABS 7 #define MENU_ID_LAYOUT_TREE 8 #define MENU_ID_DISCONNECT 9 #define MENU_ID_RECONNECT 10 #define MENU_ID_JOIN 11 #define MENU_ID_USERMENU 12 #if (MENU_ID_NUM < MENU_ID_USERMENU) #error MENU_ID_NUM is set wrong #endif ekg2-0.4~pre+20120506.1/plugins/gtk/palette.c000066400000000000000000000240021175142753400202230ustar00rootroot00000000000000/* X-Chat * Copyright (C) 1998 Peter Zelezny. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ /* * port to ekg2: * Copyright (C) 2007 Jakub Zawadzki * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* fix includes */ #include "ekg2.h" #include #include #include #include #include #include #include #include #include #include #include "main.h" #include "palette.h" #include "inline_pngs.h" #include "inline_pngs_gg.h" #include "inline_pngs_icq.h" /* XXX, todo: colors[0..15] are ok, than colors[16..31] are not ok [duplication of above], invistigate xchat sources if they needed, fix and remove. */ /* XXX, colors[16..31] remove! */ GdkColor colors[] = { #if XCHAT_COLORS /* colors for xtext */ {0, 0x0000, 0x0000, 0x0000}, /* 17 black [30 V] */ {0, 0xc3c3, 0x3b3b, 0x3b3b}, /* 20 red [31 V] */ {0, 0x2a3d, 0x8ccc, 0x2a3d}, /* 19 green [32 V] */ {0, 0xd999, 0xa6d3, 0x4147}, /* 24 yellow [33 V] */ {0, 0x35c2, 0x35c2, 0xb332}, /* 18 blue [34 V] */ {0, 0x8000, 0x2666, 0x7fff}, /* 22 purple [35] V */ {0, 0x199a, 0x5555, 0x5555}, /* 26 aqua [36] V */ {0, 0xcccc, 0xcccc, 0xcccc}, /* 16 white [37 V] */ {0, 0x4c4c, 0x4c4c, 0x4c4c}, /* 30 grey [30b V] */ {0, 0xc7c7, 0x3232, 0x3232}, /* 21 light red [31b V] */ {0, 0x3d70, 0xcccc, 0x3d70}, /* 25 green [32b V] */ {0, 0x6666, 0x3636, 0x1f1f}, /* 23 orange [33b V] */ {0, 0x451e, 0x451e, 0xe666}, /* 28 blue [34b V] */ {0, 0xb0b0, 0x3737, 0xb0b0}, /* 29 light purple [35b V] */ {0, 0x2eef, 0x8ccc, 0x74df}, /* 27 light aqua [36b V] */ {0, 0x9595, 0x9595, 0x9595}, /* 31 light grey [37b V] */ {0, 0xcccc, 0xcccc, 0xcccc}, /* 16 white */ {0, 0x0000, 0x0000, 0x0000}, /* 17 black */ {0, 0x35c2, 0x35c2, 0xb332}, /* 18 blue */ {0, 0x2a3d, 0x8ccc, 0x2a3d}, /* 19 green */ {0, 0xc3c3, 0x3b3b, 0x3b3b}, /* 20 red */ {0, 0xc7c7, 0x3232, 0x3232}, /* 21 light red */ {0, 0x8000, 0x2666, 0x7fff}, /* 22 purple */ {0, 0x6666, 0x3636, 0x1f1f}, /* 23 orange */ {0, 0xd999, 0xa6d3, 0x4147}, /* 24 yellow */ {0, 0x3d70, 0xcccc, 0x3d70}, /* 25 green */ {0, 0x199a, 0x5555, 0x5555}, /* 26 aqua */ {0, 0x2eef, 0x8ccc, 0x74df}, /* 27 light aqua */ {0, 0x451e, 0x451e, 0xe666}, /* 28 blue */ {0, 0xb0b0, 0x3737, 0xb0b0}, /* 29 light purple */ {0, 0x4c4c, 0x4c4c, 0x4c4c}, /* 30 grey */ {0, 0x9595, 0x9595, 0x9595}, /* 31 light grey */ {0, 0xffff, 0xffff, 0xffff}, /* 32 marktext Fore (white) */ {0, 0x3535, 0x6e6e, 0xc1c1}, /* 33 marktext Back (blue) */ {0, 0x0000, 0x0000, 0x0000}, /* 34 foreground (black) */ {0, 0xf0f0, 0xf0f0, 0xf0f0}, /* 35 background (white) */ {0, 0xcccc, 0x1010, 0x1010}, /* 36 marker line (red) */ /* colors for GUI */ {0, 0x9999, 0x0000, 0x0000}, /* 37 tab New Data (dark red) */ {0, 0x0000, 0x0000, 0xffff}, /* 38 tab Nick Mentioned (blue) */ {0, 0xffff, 0x0000, 0x0000}, /* 39 tab New Message (red) */ {0, 0x9595, 0x9595, 0x9595}, /* 40 away user (grey) */ #elif ETERM_COLORS /* colors for xtext */ {0, 0x0000, 0x0000, 0x0000}, /* 17 black [30 V] */ {0, 0xcccc, 0x0000, 0x0000}, /* 20 red [31 V] */ {0, 0x0000, 0xcccc, 0x0000}, /* 19 green [32 V] */ {0, 0xcccc, 0xcccc, 0x0000}, /* 24 yellow [33 V] */ {0, 0x0000, 0x0000, 0xcccc}, /* 18 blue [34 V] */ {0, 0xcccc, 0x0000, 0xcccc}, /* 22 purple [35] V */ {0, 0x0000, 0xcccc, 0xcccc}, /* 26 aqua [36] V */ {0, 0xaaaa, 0xaaaa, 0xaaaa}, /* 16 white [37 V] */ {0, 0x3333, 0x3333, 0x3333}, /* 30 grey [30b V] */ {0, 0xffff, 0x0000, 0x0000}, /* 21 light red [31b V] */ {0, 0x0000, 0xffff, 0x0000}, /* 25 green [32b V] */ {0, 0xffff, 0xffff, 0x0000}, /* 23 orange [33b V] */ {0, 0x0000, 0x0000, 0xffff}, /* 28 blue [34b V] */ {0, 0xffff, 0x0000, 0xffff}, /* 29 light purple [35b V] */ {0, 0x0000, 0xffff, 0xffff}, /* 27 light aqua [36b V] */ {0, 0xffff, 0xffff, 0xffff}, /* 31 light grey [37b V] */ /* ... */ {0, 0xaaaa, 0xaaaa, 0xaaaa}, /* 16 white [37 V] */ {0, 0x0000, 0x0000, 0x0000}, /* 17 black [30 V] */ {0, 0x0000, 0x0000, 0xcccc}, /* 18 blue [34 V] */ {0, 0x0000, 0xcccc, 0x0000}, /* 19 green [32 V] */ {0, 0xcccc, 0x0000, 0x0000}, /* 20 red [31 V] */ {0, 0xffff, 0x0000, 0x0000}, /* 21 light red [31b V] */ {0, 0xcccc, 0x0000, 0xcccc}, /* 22 purple [35] V */ {0, 0xffff, 0xffff, 0x0000}, /* 23 orange [33b V] */ {0, 0xcccc, 0xcccc, 0x0000}, /* 24 yellow [33 V] */ {0, 0x0000, 0xffff, 0x0000}, /* 25 green [32b V] */ {0, 0x0000, 0xcccc, 0xcccc}, /* 26 aqua [36] V */ {0, 0x0000, 0xffff, 0xffff}, /* 27 light aqua [36b V] */ {0, 0x0000, 0x0000, 0xffff}, /* 28 blue [34b V] */ {0, 0xffff, 0x0000, 0xffff}, /* 29 light purple [35b V] */ {0, 0x3333, 0x3333, 0x3333}, /* 30 grey [30b V] */ {0, 0xffff, 0xffff, 0xffff}, /* 31 light grey [37b V] */ {0, 0x0000, 0x0000, 0x0000}, {0, 0xa4a4, 0xdfdf, 0xffff}, {0, 0xaaaa, 0xaaaa, 0xaaaa}, /* fore XXX */ {0, 0x0000, 0x0000, 0x0000}, /* background */ {0, 0xcccc, 0x1010, 0x1010}, {0, 0x8c8c, 0x1010, 0x1010}, {0, 0x0000, 0x0000, 0xffff}, {0, 0xf5f5, 0x0000, 0x0000}, {0, 0x9999, 0x9999, 0x9999}, #else /* these are from http://xchat.org/files/themes/blacktheme.zip ;) */ {0, 0x0000, 0x0000, 0x0000}, /* 17 */ {0, 0xdddd, 0x0000, 0x0000}, /* 20 */ {0, 0x0000, 0xcccc, 0x0000}, /* 19 */ {0, 0xeeee, 0xdddd, 0x2222}, /* 24 */ {0, 0x0000, 0x0000, 0xcccc}, /* 18 */ {0, 0xbbbb, 0x0000, 0xbbbb}, /* 22 */ {0, 0x0000, 0xcccc, 0xcccc}, /* 26 */ {0, 0xcf3c, 0xcf3c, 0xcf3c}, /* 16 */ {0, 0x7777, 0x7777, 0x7777}, /* 30 */ {0, 0xaaaa, 0x0000, 0x0000}, /* 21 */ {0, 0x3333, 0xdede, 0x5555}, /* 25 */ {0, 0xffff, 0xaaaa, 0x0000}, /* 23 */ {0, 0x0000, 0x0000, 0xffff}, /* 28 */ {0, 0xeeee, 0x2222, 0xeeee}, /* 29 */ {0, 0x3333, 0xeeee, 0xffff}, /* 27 */ {0, 0x9999, 0x9999, 0x9999}, /* 31 */ /* duplicate */ {0, 0xcf3c, 0xcf3c, 0xcf3c}, {0, 0x0000, 0x0000, 0x0000}, {0, 0x0000, 0x0000, 0xcccc}, {0, 0x0000, 0xcccc, 0x0000}, {0, 0xdddd, 0x0000, 0x0000}, {0, 0xaaaa, 0x0000, 0x0000}, {0, 0xbbbb, 0x0000, 0xbbbb}, {0, 0xffff, 0xaaaa, 0x0000}, {0, 0xeeee, 0xdddd, 0x2222}, {0, 0x3333, 0xdede, 0x5555}, {0, 0x0000, 0xcccc, 0xcccc}, {0, 0x3333, 0xeeee, 0xffff}, {0, 0x0000, 0x0000, 0xffff}, {0, 0xeeee, 0x2222, 0xeeee}, {0, 0x7777, 0x7777, 0x7777}, {0, 0x9999, 0x9999, 0x9999}, {0, 0x0000, 0x0000, 0x0000}, {0, 0xa4a4, 0xdfdf, 0xffff}, {0, 0xcf3c, 0xcf3c, 0xcf3c}, {0, 0x0000, 0x0000, 0x0000}, {0, 0xcccc, 0x1010, 0x1010}, {0, 0x8c8c, 0x1010, 0x1010}, {0, 0x0000, 0x0000, 0xffff}, {0, 0xf5f5, 0x0000, 0x0000}, {0, 0x9999, 0x9999, 0x9999}, #endif }; #define MAX_COL 40 void palette_alloc(GtkWidget *widget) { int i; static int done_alloc = FALSE; GdkColormap *cmap; if (!done_alloc) { /* don't do it again */ done_alloc = TRUE; cmap = gtk_widget_get_colormap(widget); for (i = MAX_COL; i >= 0; i--) gdk_colormap_alloc_color(cmap, &colors[i], FALSE, TRUE); } } GdkPixbuf *pix_ekg2; GdkPixbuf *pixs[STATUS_PIXBUFS]; GdkPixbuf *gg_pixs[STATUS_PIXBUFS]; GdkPixbuf *icq_pixs[STATUS_PIXBUFS]; void pixmaps_init(void) { /* XXX, here load from file, or inline from .h */ /* gdk_pixbuf_new_from_file() */ /* gdk_pixbuf_new_from_inline() */ pix_ekg2 = NULL; memset(gg_pixs, 0, sizeof(gg_pixs)); gg_pixs[PIXBUF_AVAIL] = gdk_pixbuf_new_from_inline(-1, gg_avail, FALSE, 0); gg_pixs[PIXBUF_AWAY] = gdk_pixbuf_new_from_inline(-1, gg_away, FALSE, 0); gg_pixs[PIXBUF_INVISIBLE] = gdk_pixbuf_new_from_inline(-1, gg_invisible, FALSE, 0); gg_pixs[PIXBUF_NOTAVAIL] = gdk_pixbuf_new_from_inline(-1, gg_notavail, FALSE, 0); memset(icq_pixs, 0, sizeof(icq_pixs)); icq_pixs[PIXBUF_FFC] = gdk_pixbuf_new_from_inline(-1, icq_ffc, FALSE, 0); icq_pixs[PIXBUF_AVAIL] = gdk_pixbuf_new_from_inline(-1, icq_avail, FALSE, 0); icq_pixs[PIXBUF_AWAY] = gdk_pixbuf_new_from_inline(-1, icq_away, FALSE, 0); icq_pixs[PIXBUF_DND] = gdk_pixbuf_new_from_inline(-1, icq_dnd, FALSE, 0); icq_pixs[PIXBUF_XA] = gdk_pixbuf_new_from_inline(-1, icq_xa, FALSE, 0); icq_pixs[PIXBUF_INVISIBLE] = gdk_pixbuf_new_from_inline(-1, icq_invisible, FALSE, 0); icq_pixs[PIXBUF_NOTAVAIL] = gdk_pixbuf_new_from_inline(-1, icq_notavail, FALSE, 0); icq_pixs[PIXBUF_UNKNOWN] = gdk_pixbuf_new_from_inline(-1, icq_unknown, FALSE, 0); pixs[PIXBUF_FFC] = gdk_pixbuf_new_from_inline(-1, ffc, FALSE, 0); pixs[PIXBUF_AVAIL] = gdk_pixbuf_new_from_inline(-1, avail, FALSE, 0); pixs[PIXBUF_AWAY] = gdk_pixbuf_new_from_inline(-1, away, FALSE, 0); pixs[PIXBUF_DND] = gdk_pixbuf_new_from_inline(-1, dnd, FALSE, 0); pixs[PIXBUF_XA] = gdk_pixbuf_new_from_inline(-1, xa, FALSE, 0); pixs[PIXBUF_INVISIBLE] = gdk_pixbuf_new_from_inline(-1, invisible, FALSE, 0); pixs[PIXBUF_NOTAVAIL] = gdk_pixbuf_new_from_inline(-1, notavail, FALSE, 0); pixs[PIXBUF_ERROR] = gdk_pixbuf_new_from_inline(-1, icon_error, FALSE, 0); pixs[PIXBUF_UNKNOWN] = gdk_pixbuf_new_from_inline(-1, icon_unknown, FALSE, 0); } ekg2-0.4~pre+20120506.1/plugins/gtk/palette.h000066400000000000000000000013011175142753400202250ustar00rootroot00000000000000extern GdkColor colors[]; #define COL_MARK_FG 32 #define COL_MARK_BG 33 #define COL_FG 34 #define COL_BG 35 #define COL_MARKER 36 #define COL_NEW_DATA 37 #define COL_HILIGHT 38 #define COL_NEW_MSG 39 #define COL_AWAY 40 void palette_alloc(GtkWidget *widget); void pixmaps_init(void); extern GdkPixbuf *pix_ekg2; extern GdkPixbuf *pixs[]; extern GdkPixbuf *gg_pixs[]; extern GdkPixbuf *icq_pixs[]; #define PIXBUF_FFC 0 #define PIXBUF_AVAIL 1 #define PIXBUF_AWAY 2 #define PIXBUF_DND 3 #define PIXBUF_XA 4 #define PIXBUF_INVISIBLE 5 #define PIXBUF_NOTAVAIL 6 #define PIXBUF_ERROR 7 #define PIXBUF_UNKNOWN 8 #define STATUS_PIXBUFS 9 /* FFC, AVAIL, AWAY, DND, XA, INVISIBLE, NOTAVAIL, ERROR, UNKNOWN */ ekg2-0.4~pre+20120506.1/plugins/gtk/userlistgui.c000066400000000000000000000342051175142753400211520ustar00rootroot00000000000000/* X-Chat * Copyright (C) 1998 Peter Zelezny. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #include "ekg2.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "main.h" #include "menu.h" #include "palette.h" #include "userlistgui.h" enum { USERLIST_PIXMAP = 0, USERLIST_NICKNAME, USERLIST_DESCRIPTION, USERLIST_USER, USERLIST_COLOR, USERLIST_COLS }; #define show_descr_in_userlist_config 1 #if 0 #include "fe-gtk.h" #include "../common/xchat.h" #include "../common/util.h" #include "../common/userlist.h" #include "../common/modes.h" #include "../common/notify.h" #include "../common/xchatc.h" #include "gtkutil.h" #include "palette.h" #include "maingui.h" #include "menu.h" #include "userlistgui.h" #endif /* extern */ const char *gtk_session_target(session_t *sess); void fe_userlist_numbers(window_t *sess) { if (sess == window_current || !gtk_private_ui(sess)->is_tab) { #if 0 char tbuf[256]; if (sess->total) { snprintf(tbuf, sizeof(tbuf), _("%d ops, %d total"), sess->ops, sess->total); tbuf[sizeof(tbuf) - 1] = 0; gtk_label_set_text(GTK_LABEL(sess->gui->namelistinfo), tbuf); } else { gtk_label_set_text(GTK_LABEL(sess->gui->namelistinfo), NULL); } if (sess->type == SESS_CHANNEL && prefs.gui_tweaks & 1) fe_set_title(sess); #endif gtk_label_set_text(GTK_LABEL(gtk_private_ui(sess)->namelistinfo), gtk_session_target(sess->session)); } } #if 0 static void scroll_to_iter(GtkTreeIter * iter, GtkTreeView * treeview, GtkTreeModel * model) { GtkTreePath *path = gtk_tree_model_get_path(model, iter); if (path) { gtk_tree_view_scroll_to_cell(treeview, path, NULL, TRUE, 0.5, 0.5); gtk_tree_path_free(path); } } /* select a row in the userlist by nick-name */ void userlist_select(session *sess, char *name) { GtkTreeIter iter; GtkTreeView *treeview = GTK_TREE_VIEW(sess->gui->user_tree); GtkTreeModel *model = gtk_tree_view_get_model(treeview); GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview); struct User *row_user; if (gtk_tree_model_get_iter_first(model, &iter)) { do { gtk_tree_model_get(model, &iter, USERLIST_USER, &row_user, -1); if (sess->server->p_cmp(row_user->nick, name) == 0) { if (gtk_tree_selection_iter_is_selected(selection, &iter)) gtk_tree_selection_unselect_iter(selection, &iter); else gtk_tree_selection_select_iter(selection, &iter); /* and make sure it's visible */ scroll_to_iter(&iter, treeview, model); return; } } while (gtk_tree_model_iter_next(model, &iter)); } } #endif char **userlist_selection_list(GtkWidget *widget, int *num_ret) { GtkTreeIter iter; GtkTreeView *treeview = (GtkTreeView *) widget; GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview); GtkTreeModel *model = gtk_tree_view_get_model(treeview); int i, num_sel; char **nicks; *num_ret = 0; /* first, count the number of selections */ num_sel = 0; if (gtk_tree_model_get_iter_first(model, &iter)) { do { if (gtk_tree_selection_iter_is_selected(selection, &iter)) num_sel++; } while (gtk_tree_model_iter_next(model, &iter)); } if (num_sel < 1) return NULL; nicks = xmalloc(sizeof(char *) * (num_sel + 1)); i = 0; gtk_tree_model_get_iter_first(model, &iter); do { if (gtk_tree_selection_iter_is_selected(selection, &iter)) { userlist_t *user; gtk_tree_model_get(model, &iter, USERLIST_USER, &user, -1); nicks[i] = g_strdup(user->nickname); i++; nicks[i] = NULL; } } while (gtk_tree_model_iter_next(model, &iter)); *num_ret = i; return nicks; } #if 0 void fe_userlist_set_selected(struct session *sess) { GtkListStore *store = sess->res->user_model; GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(sess->gui->user_tree)); GtkTreeIter iter; struct User *user; /* if it's not front-most tab it doesn't own the GtkTreeView! */ if (store != (GtkListStore *) gtk_tree_view_get_model(GTK_TREE_VIEW(sess->gui->user_tree))) return; if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter)) { do { gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, USERLIST_USER, &user, -1); if (gtk_tree_selection_iter_is_selected(selection, &iter)) user->selected = 1; else user->selected = 0; } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter)); } } static GtkTreeIter *find_row(GtkTreeView * treeview, GtkTreeModel * model, struct User *user, int *selected) { static GtkTreeIter iter; struct User *row_user; *selected = FALSE; if (gtk_tree_model_get_iter_first(model, &iter)) { do { gtk_tree_model_get(model, &iter, USERLIST_USER, &row_user, -1); if (row_user == user) { if (gtk_tree_view_get_model(treeview) == model) { if (gtk_tree_selection_iter_is_selected (gtk_tree_view_get_selection(treeview), &iter)) *selected = TRUE; } return &iter; } } while (gtk_tree_model_iter_next(model, &iter)); } return NULL; } #endif void userlist_set_value(GtkWidget *treeview, gfloat val) { gtk_adjustment_set_value(gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(treeview)), val); } gfloat userlist_get_value(GtkWidget *treeview) { return gtk_tree_view_get_vadjustment(GTK_TREE_VIEW(treeview))->value; } static gint gtk_userlist_sort_func(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer userdata) { GdkPixbuf *a1, *b1; gint sortcol = GPOINTER_TO_INT(userdata); if (sortcol != USERLIST_PIXMAP) { printf("gtk_userlist_sort_func() IE\n"); return 0; } /* XXX, sequence should match sequence in contacts_options */ gtk_tree_model_get(model, a, USERLIST_PIXMAP, &a1, -1); gtk_tree_model_get(model, b, USERLIST_PIXMAP, &b1, -1); /* yeah, i know, i'm lazy */ if (a1 < b1) return -1; else if (a1 > b1) return 1; else return 0; } void fe_userlist_insert(window_t *sess, userlist_t *u, GdkPixbuf **pixmaps) { GtkTreeModel *model = gtk_private(sess)->user_model; GdkPixbuf *pix = NULL; /* get_user_icon (sess->server, newuser); */ GtkTreeIter iter; #if 0 int do_away = TRUE; #endif int sel = 0; if (pixmaps) { switch (u->status) { case EKG_STATUS_NA: pix = pixmaps[PIXBUF_NOTAVAIL]; break; case EKG_STATUS_INVISIBLE: pix = pixmaps[PIXBUF_INVISIBLE]; break; case EKG_STATUS_XA: pix = pixmaps[PIXBUF_XA]; break; case EKG_STATUS_DND: pix = pixmaps[PIXBUF_DND]; break; case EKG_STATUS_AWAY: pix = pixmaps[PIXBUF_AWAY]; break; case EKG_STATUS_AVAIL: pix = pixmaps[PIXBUF_AVAIL]; break; case EKG_STATUS_FFC: pix = pixmaps[PIXBUF_FFC]; break; case EKG_STATUS_ERROR: pix = pixmaps[PIXBUF_ERROR]; break; default: /* + EKG_STATUS_UNKNOWN */ pix = pixmaps[PIXBUF_UNKNOWN]; } } #if 0 if (prefs.away_size_max < 1 || !prefs.away_track) do_away = FALSE; #endif gtk_list_store_insert_with_values(GTK_LIST_STORE(model), &iter, -1, USERLIST_PIXMAP, pix, USERLIST_NICKNAME, u->nickname, /* XXX, u->uid */ USERLIST_DESCRIPTION, u->descr, USERLIST_USER, u, // USERLIST_COLOR, /* (do_away) */ FALSE, ? (newuser->away ? &colors[COL_AWAY] : NULL) : */ (NULL), -1); #if DARK /* is it me? */ if (newuser->me && sess->gui->nick_box) { if (!sess->gui->is_tab || sess == current_tab) mg_set_access_icon(sess->gui, pix, sess->server->is_away); } #if 0 /* not mine IF !! */ if (prefs.hilitenotify && notify_isnotify(sess, newuser->nick)) { gtk_clist_set_foreground((GtkCList *) sess->gui->user_clist, row, &colors[prefs.nu_color]); } #endif #endif /* is it the front-most tab? */ if (sel && gtk_tree_view_get_model(GTK_TREE_VIEW(gtk_private_ui(sess)->user_tree)) == model) { gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(gtk_private_ui(sess)->user_tree)), &iter); } } void fe_userlist_clear(window_t *sess) { gtk_list_store_clear(gtk_private(sess)->user_model); } void *userlist_create_model(void) { GtkTreeSortable *sortable; void *liststore; liststore = gtk_list_store_new(USERLIST_COLS, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, GDK_TYPE_COLOR); sortable = GTK_TREE_SORTABLE(liststore); gtk_tree_sortable_set_sort_func(sortable, USERLIST_PIXMAP, gtk_userlist_sort_func, GINT_TO_POINTER(USERLIST_PIXMAP), NULL); /* initial sort */ gtk_tree_sortable_set_sort_column_id(sortable, USERLIST_PIXMAP, GTK_SORT_ASCENDING); return liststore; } static void userlist_add_columns(GtkTreeView * treeview) { GtkCellRenderer *renderer; /* icon column */ renderer = gtk_cell_renderer_pixbuf_new(); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), -1, NULL, renderer, "pixbuf", USERLIST_PIXMAP, NULL); /* nick column */ renderer = gtk_cell_renderer_text_new(); gtk_cell_renderer_text_set_fixed_height_from_font(GTK_CELL_RENDERER_TEXT(renderer), 1); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), -1, NULL, renderer, "text", USERLIST_NICKNAME, "foreground-gdk", USERLIST_COLOR, NULL); /* description column (?) */ if (show_descr_in_userlist_config) { renderer = gtk_cell_renderer_text_new(); gtk_cell_renderer_text_set_fixed_height_from_font(GTK_CELL_RENDERER_TEXT(renderer), 1); gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(treeview), -1, NULL, renderer, "text", 2, NULL); } } static gint userlist_click_cb(GtkWidget *widget, GdkEventButton * event, gpointer userdata) { char **nicks; int i; GtkTreeSelection *sel; GtkTreePath *path; if (!event) return FALSE; if (!(event->state & GDK_CONTROL_MASK) && event->type == GDK_2BUTTON_PRESS /* && prefs.doubleclickuser[0] */) { nicks = userlist_selection_list(widget, &i); if (nicks) { /* nick_command_parse(current_sess, prefs.doubleclickuser, nicks[0], nicks[0]); */ command_exec_format(NULL, NULL, 0, ("/query \"%s\""), nicks[0]); while (i) { i--; g_free(nicks[i]); } free(nicks); } return TRUE; } if (event->button == 3) { /* do we have a multi-selection? */ nicks = userlist_selection_list(widget, &i); if (nicks && i > 1) { menu_nickmenu(window_current, event, nicks[0], i); while (i) { i--; g_free(nicks[i]); } free(nicks); return TRUE; } if (nicks) { g_free(nicks[0]); free(nicks); } sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget)); if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), event->x, event->y, &path, 0, 0, 0)) { gtk_tree_selection_unselect_all(sel); gtk_tree_selection_select_path(sel, path); gtk_tree_path_free(path); nicks = userlist_selection_list(widget, &i); if (nicks) { menu_nickmenu(window_current, event, nicks[0], i); while (i) { i--; g_free(nicks[i]); } free(nicks); } } else { gtk_tree_selection_unselect_all(sel); } return TRUE; } return FALSE; } static gboolean userlist_key_cb(GtkWidget *wid, GdkEventKey * evt, gpointer userdata) { #if 0 if (evt->keyval >= GDK_asterisk && evt->keyval <= GDK_z) { /* dirty trick to avoid auto-selection */ SPELL_ENTRY_SET_EDITABLE(current_sess->gui->input_box, FALSE); gtk_widget_grab_focus(current_sess->gui->input_box); SPELL_ENTRY_SET_EDITABLE(current_sess->gui->input_box, TRUE); gtk_widget_event(current_sess->gui->input_box, (GdkEvent *) evt); return TRUE; } #endif return FALSE; } GtkWidget *userlist_create(GtkWidget *box) { GtkWidget *sw, *treeview; sw = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_ETCHED_IN); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), show_descr_in_userlist_config ? GTK_POLICY_AUTOMATIC : GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); gtk_box_pack_start(GTK_BOX(box), sw, TRUE, TRUE, 0); gtk_widget_show(sw); treeview = gtk_tree_view_new(); gtk_widget_set_name(treeview, "xchat-userlist"); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), FALSE); gtk_tree_selection_set_mode(gtk_tree_view_get_selection (GTK_TREE_VIEW(treeview)), GTK_SELECTION_MULTIPLE); g_signal_connect(G_OBJECT(treeview), "button_press_event", G_CALLBACK(userlist_click_cb), 0); g_signal_connect(G_OBJECT(treeview), "key_press_event", G_CALLBACK(userlist_key_cb), 0); /* xchat->ekg2: drag & drop */ userlist_add_columns(GTK_TREE_VIEW(treeview)); gtk_container_add(GTK_CONTAINER(sw), treeview); gtk_widget_show(treeview); return treeview; } void userlist_show(window_t *sess) { gtk_tree_view_set_model(GTK_TREE_VIEW(gtk_private_ui(sess)->user_tree), gtk_private(sess)->user_model); } #if 0 void fe_uselect(session *sess, char *word[], int do_clear, int scroll_to) { int thisname; char *name; GtkTreeIter iter; GtkTreeView *treeview = GTK_TREE_VIEW(sess->gui->user_tree); GtkTreeModel *model = gtk_tree_view_get_model(treeview); GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview); struct User *row_user; if (gtk_tree_model_get_iter_first(model, &iter)) { if (do_clear) gtk_tree_selection_unselect_all(selection); do { if (*word[0]) { gtk_tree_model_get(model, &iter, USERLIST_USER, &row_user, -1); thisname = 0; while (*(name = word[thisname++])) { if (sess->server->p_cmp(row_user->nick, name) == 0) { gtk_tree_selection_select_iter(selection, &iter); if (scroll_to) scroll_to_iter(&iter, treeview, model); break; } } } } while (gtk_tree_model_iter_next(model, &iter)); } } #endif ekg2-0.4~pre+20120506.1/plugins/gtk/userlistgui.h000066400000000000000000000010771175142753400211600ustar00rootroot00000000000000#if 0 void userlist_select (session *sess, char *name); char **userlist_selection_list (GtkWidget *widget, int *num_ret); GdkPixbuf *get_user_icon (server *serv, struct User *user); #endif GtkWidget *userlist_create (GtkWidget *box); void *userlist_create_model(void); void userlist_show(window_t *sess); void userlist_set_value (GtkWidget *treeview, gfloat val); gfloat userlist_get_value (GtkWidget *treeview); void fe_userlist_numbers(window_t *sess); void fe_userlist_clear(window_t *sess); void fe_userlist_insert(window_t *sess, userlist_t *u, GdkPixbuf **pixmaps); ekg2-0.4~pre+20120506.1/plugins/gtk/xtext.c000066400000000000000000003257011175142753400177530ustar00rootroot00000000000000/* X-Chat * Copyright (C) 1998 Peter Zelezny. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * ========================================================================= * * xtext, the text widget used by X-Chat. * By Peter Zelezny . * * optimized, and rewriten to support fstring_t * By Jakub Zawadzki . */ #define TINT_VALUE 195 /* 195/255 of the brightness. */ #define MOTION_MONITOR /* URL hilights. */ #define SMOOTH_SCROLL /* line-by-line or pixel scroll? */ #define SCROLL_HACK /* use XCopyArea scroll, or full redraw? */ #undef COLOR_HILIGHT /* Color instead of underline? */ /* Italic is buggy because it assumes drawing an italic string will have identical extents to the normal font. This is only true some of the time, so we can't use this hack yet. */ #undef ITALIC /* support Italic? */ #define GDK_MULTIHEAD_SAFE #define USE_DB /* double buffer */ #define MARGIN 2 /* dont touch. */ #define REFRESH_TIMEOUT 20 #define WORDWRAP_LIMIT 24 #include "ekg2.h" #define USE_XLIB #include #include #include #include #include #include #include #include #include #include #include #ifdef USE_XLIB #include #include #include #endif #include "xtext.h" #define charlen(str) g_utf8_skip[*(guchar *)(str)] /* is delimiter */ #define is_del(c) (c == ' ' || c == '\n' || c == ')' || c == '(' || c == '>' || c == '<' || c == 0) /* XXX, ATTR_* stuff */ /* is_del includes ATTR_RESET, ATTR_BOLD */ #ifdef SCROLL_HACK /* force scrolling off */ #define dontscroll(buf) (buf)->last_pixel_pos = 0x7fffffff #else #define dontscroll(buf) #endif static GtkWidgetClass *parent_class = NULL; struct textentry { struct textentry *next; struct textentry *prev; fstring_t *fstr; const unsigned char *str; gint16 str_width; gint16 str_len; gint16 mark_start; gint16 mark_end; gint16 indent; gint16 left_len; gint16 lines_taken; #define RECORD_WRAPS 4 guint16 wrap_offset[RECORD_WRAPS]; guchar mb; /* boolean: is multibyte? */ #if 0 guchar tag; guchar pad1; guchar pad2; /* 32-bit align : 44 bytes total */ #endif }; enum { WORD_CLICK, LAST_SIGNAL }; /* values for selection info */ enum { TARGET_UTF8_STRING, TARGET_STRING, TARGET_TEXT, TARGET_COMPOUND_TEXT }; static guint xtext_signals[LAST_SIGNAL]; static void gtk_xtext_render_page(GtkXText * xtext); static void gtk_xtext_calc_lines(xtext_buffer * buf, int); #if defined(USE_XLIB) || defined(WIN32) static void gtk_xtext_load_trans(GtkXText * xtext); static void gtk_xtext_free_trans(GtkXText * xtext); #endif static char *gtk_xtext_selection_get_text(GtkXText * xtext, int *len_ret); static textentry *gtk_xtext_nth(GtkXText * xtext, int line, int *subline); static void gtk_xtext_adjustment_changed(GtkAdjustment * adj, GtkXText * xtext); static int gtk_xtext_render_ents(GtkXText * xtext, textentry *, textentry *); static void gtk_xtext_recalc_widths(xtext_buffer * buf, int); static void gtk_xtext_fix_indent(xtext_buffer * buf); static int gtk_xtext_find_subline(GtkXText * xtext, textentry * ent, int line); static unsigned char *gtk_xtext_strip_color(const unsigned char *text, int len, unsigned char *outbuf, int *mb_ret); /* some utility functions first */ /* gives width of a 8bit string */ static int gtk_xtext_text_width_8bit(GtkXText * xtext, const unsigned char *str, int len) { int width = 0; while (len) { width += xtext->fontwidth[*str]; str++; len--; } return width; } #define xtext_draw_bg(xt,x,y,w,h) gdk_draw_rectangle(xt->draw_buf, xt->bgc,1,x,y,w,h); /* ========================================= */ /* ========== XFT 1 and 2 BACKEND ========== */ /* ========================================= */ #ifdef USE_XFT static void backend_font_close(GtkXText * xtext) { XftFontClose(GDK_WINDOW_XDISPLAY(xtext->draw_buf), xtext->font); #ifdef ITALIC XftFontClose(GDK_WINDOW_XDISPLAY(xtext->draw_buf), xtext->ifont); #endif } static void backend_init(GtkXText * xtext) { if (xtext->xftdraw == NULL) { xtext->xftdraw = XftDrawCreate(GDK_WINDOW_XDISPLAY(xtext->draw_buf), GDK_WINDOW_XWINDOW(xtext->draw_buf), GDK_VISUAL_XVISUAL(gdk_drawable_get_visual (xtext->draw_buf)), GDK_COLORMAP_XCOLORMAP(gdk_drawable_get_colormap (xtext->draw_buf))); XftDrawSetSubwindowMode(xtext->xftdraw, IncludeInferiors); } } static void backend_deinit(GtkXText * xtext) { if (xtext->xftdraw) { XftDrawDestroy(xtext->xftdraw); xtext->xftdraw = NULL; } } static XftFont *backend_font_open_real(Display * xdisplay, char *name, gboolean italics) { XftFont *font = NULL; PangoFontDescription *fontd; int weight, slant, screen = DefaultScreen(xdisplay); fontd = pango_font_description_from_string(name); if (pango_font_description_get_size(fontd) != 0) { weight = pango_font_description_get_weight(fontd); /* from pangoft2-fontmap.c */ if (weight < (PANGO_WEIGHT_NORMAL + PANGO_WEIGHT_LIGHT) / 2) weight = XFT_WEIGHT_LIGHT; else if (weight < (PANGO_WEIGHT_NORMAL + 600) / 2) weight = XFT_WEIGHT_MEDIUM; else if (weight < (600 + PANGO_WEIGHT_BOLD) / 2) weight = XFT_WEIGHT_DEMIBOLD; else if (weight < (PANGO_WEIGHT_BOLD + PANGO_WEIGHT_ULTRABOLD) / 2) weight = XFT_WEIGHT_BOLD; else weight = XFT_WEIGHT_BLACK; slant = pango_font_description_get_style(fontd); if (slant == PANGO_STYLE_ITALIC) slant = XFT_SLANT_ITALIC; else if (slant == PANGO_STYLE_OBLIQUE) slant = XFT_SLANT_OBLIQUE; else slant = XFT_SLANT_ROMAN; font = XftFontOpen(xdisplay, screen, XFT_FAMILY, XftTypeString, pango_font_description_get_family(fontd), XFT_CORE, XftTypeBool, False, XFT_SIZE, XftTypeDouble, (double)pango_font_description_get_size(fontd) / PANGO_SCALE, XFT_WEIGHT, XftTypeInteger, weight, XFT_SLANT, XftTypeInteger, italics ? XFT_SLANT_ITALIC : slant, NULL); } pango_font_description_free(fontd); if (font == NULL) { font = XftFontOpenName(xdisplay, screen, name); if (font == NULL) font = XftFontOpenName(xdisplay, screen, "sans-11"); } return font; } static void backend_font_open(GtkXText * xtext, char *name) { Display *dis = GDK_WINDOW_XDISPLAY(xtext->draw_buf); xtext->font = backend_font_open_real(dis, name, FALSE); #ifdef ITALIC xtext->ifont = backend_font_open_real(dis, name, TRUE); #endif } inline static int backend_get_char_width(GtkXText * xtext, const unsigned char *str, int *mbl_ret) { XGlyphInfo ext; if (*str < 128) { *mbl_ret = 1; return xtext->fontwidth[*str]; } *mbl_ret = charlen(str); XftTextExtentsUtf8(GDK_WINDOW_XDISPLAY(xtext->draw_buf), xtext->font, str, *mbl_ret, &ext); return ext.xOff; } static int backend_get_text_width(GtkXText * xtext, const guchar * str, int len, int is_mb) { XGlyphInfo ext; if (!is_mb) return gtk_xtext_text_width_8bit(xtext, str, len); XftTextExtentsUtf8(GDK_WINDOW_XDISPLAY(xtext->draw_buf), xtext->font, str, len, &ext); return ext.xOff; } static void backend_draw_text(GtkXText * xtext, int dofill, GdkGC * gc, int x, int y, const char *str, int len, int str_width, int is_mb) { /*Display *xdisplay = GDK_WINDOW_XDISPLAY (xtext->draw_buf); */ void (*draw_func) (XftDraw *, XftColor *, XftFont *, int, int, XftChar8 *, int) = (void *)XftDrawString8; XftFont *font; /* if all ascii, use String8 to avoid the conversion penalty */ if (is_mb) draw_func = (void *)XftDrawStringUtf8; if (dofill) { /* register GC xgc = GDK_GC_XGC (gc); XSetForeground (xdisplay, xgc, xtext->xft_bg->pixel); XFillRectangle (xdisplay, GDK_WINDOW_XWINDOW (xtext->draw_buf), xgc, x, y - xtext->font->ascent, str_width, xtext->fontsize);*/ XftDrawRect(xtext->xftdraw, xtext->xft_bg, x, y - xtext->font->ascent, str_width, xtext->fontsize); } font = xtext->font; #ifdef ITALIC if (xtext->italics) font = xtext->ifont; #endif draw_func(xtext->xftdraw, xtext->xft_fg, font, x, y, str, len); if (xtext->overdraw) draw_func(xtext->xftdraw, xtext->xft_fg, font, x, y, str, len); if (xtext->bold) draw_func(xtext->xftdraw, xtext->xft_fg, font, x + 1, y, str, len); } /*static void backend_set_clip (GtkXText *xtext, GdkRectangle *area) { gdk_gc_set_clip_rectangle (xtext->fgc, area); gdk_gc_set_clip_rectangle (xtext->bgc, area); } static void backend_clear_clip (GtkXText *xtext) { gdk_gc_set_clip_rectangle (xtext->fgc, NULL); gdk_gc_set_clip_rectangle (xtext->bgc, NULL); }*/ /*static void backend_set_clip (GtkXText *xtext, GdkRectangle *area) { Region reg; XRectangle rect; rect.x = area->x; rect.y = area->y; rect.width = area->width; rect.height = area->height; reg = XCreateRegion (); XUnionRectWithRegion (&rect, reg, reg); XftDrawSetClip (xtext->xftdraw, reg); XDestroyRegion (reg); gdk_gc_set_clip_rectangle (xtext->fgc, area); } static void backend_clear_clip (GtkXText *xtext) { XftDrawSetClip (xtext->xftdraw, NULL); gdk_gc_set_clip_rectangle (xtext->fgc, NULL); } */ #else /* !USE_XFT */ /* ======================================= */ /* ============ PANGO BACKEND ============ */ /* ======================================= */ static void backend_font_close(GtkXText * xtext) { pango_font_description_free(xtext->font->font); #ifdef ITALIC pango_font_description_free(xtext->font->ifont); #endif } static void backend_init(GtkXText * xtext) { if (xtext->layout == NULL) { xtext->layout = gtk_widget_create_pango_layout(GTK_WIDGET(xtext), 0); if (xtext->font) pango_layout_set_font_description(xtext->layout, xtext->font->font); } } static void backend_deinit(GtkXText * xtext) { if (xtext->layout) { g_object_unref(xtext->layout); xtext->layout = NULL; } } static PangoFontDescription *backend_font_open_real(char *name) { PangoFontDescription *font; font = pango_font_description_from_string(name); if (font && pango_font_description_get_size(font) == 0) { pango_font_description_free(font); font = NULL; /* we'll try again, with sans 11 */ } if (!font) font = pango_font_description_from_string("sans 11"); return font; } static void backend_font_open(GtkXText * xtext, char *name) { PangoLanguage *lang; PangoContext *context; PangoFontMetrics *metrics; xtext->font = &xtext->pango_font; xtext->font->font = backend_font_open_real(name); if (!xtext->font->font) { xtext->font = NULL; return; } #ifdef ITALIC xtext->font->ifont = backend_font_open_real(name); pango_font_description_set_style(xtext->font->ifont, PANGO_STYLE_ITALIC); #endif backend_init(xtext); pango_layout_set_font_description(xtext->layout, xtext->font->font); /* vte does it this way */ context = gtk_widget_get_pango_context(GTK_WIDGET(xtext)); lang = pango_context_get_language(context); metrics = pango_context_get_metrics(context, xtext->font->font, lang); xtext->font->ascent = pango_font_metrics_get_ascent(metrics) / PANGO_SCALE; xtext->font->descent = pango_font_metrics_get_descent(metrics) / PANGO_SCALE; pango_font_metrics_unref(metrics); } static int backend_get_text_width(GtkXText * xtext, const guchar * str, int len, int is_mb) { int width; if (!is_mb) return gtk_xtext_text_width_8bit(xtext, str, len); if (*str == 0) return 0; pango_layout_set_text(xtext->layout, str, len); pango_layout_get_pixel_size(xtext->layout, &width, NULL); return width; } inline static int backend_get_char_width(GtkXText * xtext, const unsigned char *str, int *mbl_ret) { int width; if (*str < 128) { *mbl_ret = 1; return xtext->fontwidth[*str]; } *mbl_ret = charlen(str); pango_layout_set_text(xtext->layout, str, *mbl_ret); pango_layout_get_pixel_size(xtext->layout, &width, NULL); return width; } /* simplified version of gdk_draw_layout_line_with_colors() */ static void xtext_draw_layout_line(GdkDrawable * drawable, GdkGC * gc, gint x, gint y, PangoLayoutLine * line) { GSList *tmp_list; PangoRectangle logical_rect; gint x_off = 0; for (tmp_list = line->runs; tmp_list; tmp_list = tmp_list->next) { PangoLayoutRun *run = tmp_list->data; pango_glyph_string_extents(run->glyphs, run->item->analysis.font, NULL, &logical_rect); gdk_draw_glyphs(drawable, gc, run->item->analysis.font, x + x_off / PANGO_SCALE, y, run->glyphs); x_off += logical_rect.width; } } static void backend_draw_text(GtkXText * xtext, int dofill, GdkGC * gc, int x, int y, const char *str, int len, int str_width, int is_mb) { GdkGCValues val; GdkColor col; PangoLayoutLine *line; #ifdef ITALIC if (xtext->italics) pango_layout_set_font_description(xtext->layout, xtext->font->ifont); #endif pango_layout_set_text(xtext->layout, str, len); if (dofill) { gdk_gc_get_values(gc, &val); col.pixel = val.background.pixel; gdk_gc_set_foreground(gc, &col); gdk_draw_rectangle(xtext->draw_buf, gc, 1, x, y - xtext->font->ascent, str_width, xtext->fontsize); col.pixel = val.foreground.pixel; gdk_gc_set_foreground(gc, &col); } line = pango_layout_get_lines(xtext->layout)->data; xtext_draw_layout_line(xtext->draw_buf, gc, x, y, line); if (xtext->overdraw) xtext_draw_layout_line(xtext->draw_buf, gc, x, y, line); if (xtext->bold) xtext_draw_layout_line(xtext->draw_buf, gc, x + 1, y, line); #ifdef ITALIC if (xtext->italics) pango_layout_set_font_description(xtext->layout, xtext->font->font); #endif } /*static void backend_set_clip (GtkXText *xtext, GdkRectangle *area) { gdk_gc_set_clip_rectangle (xtext->fgc, area); gdk_gc_set_clip_rectangle (xtext->bgc, area); } static void backend_clear_clip (GtkXText *xtext) { gdk_gc_set_clip_rectangle (xtext->fgc, NULL); gdk_gc_set_clip_rectangle (xtext->bgc, NULL); }*/ #endif /* !USE_PANGO */ static void xtext_set_fg(GtkXText * xtext, GdkGC * gc, int index) { GdkColor col; col.pixel = xtext->palette[index]; gdk_gc_set_foreground(gc, &col); #ifdef USE_XFT if (gc == xtext->fgc) xtext->xft_fg = &xtext->color[index]; else xtext->xft_bg = &xtext->color[index]; #endif } #ifdef USE_XFT #define xtext_set_bg(xt,gc,index) xt->xft_bg = &xt->color[index] #else static void xtext_set_bg(GtkXText * xtext, GdkGC * gc, int index) { GdkColor col; col.pixel = xtext->palette[index]; gdk_gc_set_background(gc, &col); } #endif static void gtk_xtext_init(GtkXText * xtext) { xtext->pixmap = NULL; xtext->io_tag = 0; xtext->add_io_tag = 0; xtext->scroll_tag = 0; xtext->max_lines = 0; xtext->col_back = XTEXT_BG; xtext->col_fore = XTEXT_FG; xtext->pixel_offset = 0; xtext->bold = FALSE; xtext->underline = FALSE; xtext->italics = FALSE; xtext->font = NULL; #ifdef USE_XFT xtext->xftdraw = NULL; #else xtext->layout = NULL; #endif xtext->jump_out_offset = 0; xtext->jump_in_offset = 0; xtext->ts_x = 0; xtext->ts_y = 0; xtext->clip_x = 0; xtext->clip_x2 = 1000000; xtext->clip_y = 0; xtext->clip_y2 = 1000000; xtext->error_function = NULL; xtext->urlcheck_function = NULL; xtext->skip_border_fills = FALSE; xtext->skip_stamp = FALSE; xtext->render_hilights_only = FALSE; xtext->un_hilight = FALSE; xtext->recycle = FALSE; xtext->dont_render = FALSE; xtext->dont_render2 = FALSE; xtext->overdraw = FALSE; xtext->tint_red = xtext->tint_green = xtext->tint_blue = TINT_VALUE; xtext->adj = (GtkAdjustment *) gtk_adjustment_new(0, 0, 1, 1, 1, 1); g_object_ref(G_OBJECT(xtext->adj)); g_object_ref_sink(G_OBJECT(xtext->adj)); g_object_unref(G_OBJECT(xtext->adj)); xtext->vc_signal_tag = g_signal_connect(G_OBJECT(xtext->adj), "value_changed", G_CALLBACK(gtk_xtext_adjustment_changed), xtext); { static const GtkTargetEntry targets[] = { {"UTF8_STRING", 0, TARGET_UTF8_STRING}, {"STRING", 0, TARGET_STRING}, {"TEXT", 0, TARGET_TEXT}, {"COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT} }; static const gint n_targets = sizeof(targets) / sizeof(targets[0]); gtk_selection_add_targets(GTK_WIDGET(xtext), GDK_SELECTION_PRIMARY, targets, n_targets); } if (getenv("XCHAT_OVERDRAW")) xtext->overdraw = TRUE; } static void gtk_xtext_adjustment_set(xtext_buffer * buf, int fire_signal) { GtkAdjustment *adj = buf->xtext->adj; if (buf->xtext->buffer == buf) { adj->lower = 0; adj->upper = buf->num_lines; if (adj->upper == 0) adj->upper = 1; adj->page_size = (GTK_WIDGET(buf->xtext)->allocation.height - buf->xtext->font->descent) / buf->xtext->fontsize; adj->page_increment = adj->page_size; if (adj->value > adj->upper - adj->page_size) adj->value = adj->upper - adj->page_size; if (adj->value < 0) adj->value = 0; if (fire_signal) gtk_adjustment_changed(adj); } } static gint gtk_xtext_adjustment_timeout(GtkXText * xtext) { gtk_xtext_render_page(xtext); xtext->io_tag = 0; return 0; } static void gtk_xtext_adjustment_changed(GtkAdjustment * adj, GtkXText * xtext) { #ifdef SMOOTH_SCROLL if (xtext->buffer->old_value != xtext->adj->value) #else if ((int)xtext->buffer->old_value != (int)xtext->adj->value) #endif { if (xtext->adj->value >= xtext->adj->upper - xtext->adj->page_size) xtext->buffer->scrollbar_down = TRUE; else xtext->buffer->scrollbar_down = FALSE; if (xtext->adj->value + 1 == xtext->buffer->old_value || xtext->adj->value - 1 == xtext->buffer->old_value) { /* clicked an arrow? */ if (xtext->io_tag) { g_source_remove(xtext->io_tag); xtext->io_tag = 0; } gtk_xtext_render_page(xtext); } else { if (!xtext->io_tag) xtext->io_tag = g_timeout_add(REFRESH_TIMEOUT, (GSourceFunc) gtk_xtext_adjustment_timeout, xtext); } } xtext->buffer->old_value = adj->value; } GtkWidget *gtk_xtext_new(GdkColor palette[], int separator) { GtkXText *xtext = g_object_new(gtk_xtext_get_type(), NULL); xtext->separator = separator; xtext->wordwrap = TRUE; xtext->buffer = gtk_xtext_buffer_new(xtext); xtext->orig_buffer = xtext->buffer; gtk_widget_set_double_buffered(GTK_WIDGET(xtext), FALSE); gtk_xtext_set_palette(xtext, palette); return GTK_WIDGET(xtext); } static void gtk_xtext_destroy(GtkObject * object) { GtkXText *xtext = GTK_XTEXT(object); if (xtext->add_io_tag) { g_source_remove(xtext->add_io_tag); xtext->add_io_tag = 0; } if (xtext->scroll_tag) { g_source_remove(xtext->scroll_tag); xtext->scroll_tag = 0; } if (xtext->io_tag) { g_source_remove(xtext->io_tag); xtext->io_tag = 0; } if (xtext->pixmap) { #if defined(USE_XLIB) || defined(WIN32) if (xtext->transparent) gtk_xtext_free_trans(xtext); else #endif g_object_unref(xtext->pixmap); xtext->pixmap = NULL; } if (xtext->font) { backend_font_close(xtext); xtext->font = NULL; } if (xtext->adj) { g_signal_handlers_disconnect_matched(G_OBJECT(xtext->adj), G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, xtext); /* gtk_signal_disconnect_by_data (GTK_OBJECT (xtext->adj), xtext); */ g_object_unref(G_OBJECT(xtext->adj)); xtext->adj = NULL; } if (xtext->bgc) { g_object_unref(xtext->bgc); xtext->bgc = NULL; } if (xtext->fgc) { g_object_unref(xtext->fgc); xtext->fgc = NULL; } if (xtext->light_gc) { g_object_unref(xtext->light_gc); xtext->light_gc = NULL; } if (xtext->dark_gc) { g_object_unref(xtext->dark_gc); xtext->dark_gc = NULL; } if (xtext->thin_gc) { g_object_unref(xtext->thin_gc); xtext->thin_gc = NULL; } if (xtext->marker_gc) { g_object_unref(xtext->marker_gc); xtext->marker_gc = NULL; } if (xtext->hand_cursor) { gdk_cursor_unref(xtext->hand_cursor); xtext->hand_cursor = NULL; } if (xtext->resize_cursor) { gdk_cursor_unref(xtext->resize_cursor); xtext->resize_cursor = NULL; } if (xtext->orig_buffer) { gtk_xtext_buffer_free(xtext->orig_buffer); xtext->orig_buffer = NULL; } if (GTK_OBJECT_CLASS(parent_class)->destroy) (*GTK_OBJECT_CLASS(parent_class)->destroy) (object); } static void gtk_xtext_unrealize(GtkWidget *widget) { backend_deinit(GTK_XTEXT(widget)); /* if there are still events in the queue, this'll avoid segfault */ gdk_window_set_user_data(widget->window, NULL); if (parent_class->unrealize) (*GTK_WIDGET_CLASS(parent_class)->unrealize) (widget); } static void gtk_xtext_realize(GtkWidget *widget) { GtkXText *xtext; GdkWindowAttr attributes; GdkGCValues val; GdkColor col; GdkColormap *cmap; GTK_WIDGET_SET_FLAGS(widget, GTK_REALIZED); xtext = GTK_XTEXT(widget); attributes.x = widget->allocation.x; attributes.y = widget->allocation.y; attributes.width = widget->allocation.width; attributes.height = widget->allocation.height; attributes.wclass = GDK_INPUT_OUTPUT; attributes.window_type = GDK_WINDOW_CHILD; attributes.event_mask = gtk_widget_get_events(widget) | GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK #ifdef MOTION_MONITOR | GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK; #else | GDK_POINTER_MOTION_MASK; #endif cmap = gtk_widget_get_colormap(widget); attributes.colormap = cmap; attributes.visual = gtk_widget_get_visual(widget); widget->window = gdk_window_new(widget->parent->window, &attributes, GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP); gdk_window_set_user_data(widget->window, widget); xtext->depth = gdk_drawable_get_visual(widget->window)->depth; val.subwindow_mode = GDK_INCLUDE_INFERIORS; val.graphics_exposures = 0; xtext->bgc = gdk_gc_new_with_values(widget->window, &val, GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW); xtext->fgc = gdk_gc_new_with_values(widget->window, &val, GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW); xtext->light_gc = gdk_gc_new_with_values(widget->window, &val, GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW); xtext->dark_gc = gdk_gc_new_with_values(widget->window, &val, GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW); xtext->thin_gc = gdk_gc_new_with_values(widget->window, &val, GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW); xtext->marker_gc = gdk_gc_new_with_values(widget->window, &val, GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW); /* for the separator bar (light) */ col.red = 0xffff; col.green = 0xffff; col.blue = 0xffff; gdk_colormap_alloc_color(cmap, &col, FALSE, TRUE); gdk_gc_set_foreground(xtext->light_gc, &col); /* for the separator bar (dark) */ col.red = 0x1111; col.green = 0x1111; col.blue = 0x1111; gdk_colormap_alloc_color(cmap, &col, FALSE, TRUE); gdk_gc_set_foreground(xtext->dark_gc, &col); /* for the separator bar (thinline) */ col.red = 0x8e38; col.green = 0x8e38; col.blue = 0x9f38; gdk_colormap_alloc_color(cmap, &col, FALSE, TRUE); gdk_gc_set_foreground(xtext->thin_gc, &col); /* for the marker bar (marker) */ col.pixel = xtext->palette[XTEXT_MARKER]; gdk_gc_set_foreground(xtext->marker_gc, &col); xtext_set_fg(xtext, xtext->fgc, XTEXT_FG); xtext_set_bg(xtext, xtext->fgc, XTEXT_BG); xtext_set_fg(xtext, xtext->bgc, XTEXT_BG); /* draw directly to window */ xtext->draw_buf = widget->window; #if defined(USE_XLIB) || defined(WIN32) if (xtext->transparent) { gtk_xtext_load_trans(xtext); } else #endif if (xtext->pixmap) { gdk_gc_set_tile(xtext->bgc, xtext->pixmap); gdk_gc_set_ts_origin(xtext->bgc, 0, 0); xtext->ts_x = xtext->ts_y = 0; gdk_gc_set_fill(xtext->bgc, GDK_TILED); } #if (GTK_MAJOR_VERSION == 2) && (GTK_MINOR_VERSION == 0) xtext->hand_cursor = gdk_cursor_new(GDK_HAND1); xtext->resize_cursor = gdk_cursor_new(GDK_LEFT_SIDE); #else xtext->hand_cursor = gdk_cursor_new_for_display(gdk_drawable_get_display(widget->window), GDK_HAND1); xtext->resize_cursor = gdk_cursor_new_for_display(gdk_drawable_get_display(widget->window), GDK_LEFT_SIDE); #endif gdk_window_set_back_pixmap(widget->window, NULL, FALSE); widget->style = gtk_style_attach(widget->style, widget->window); backend_init(xtext); } static void gtk_xtext_size_request(GtkWidget *widget, GtkRequisition * requisition) { requisition->width = 200; requisition->height = 90; } static void gtk_xtext_size_allocate(GtkWidget *widget, GtkAllocation * allocation) { GtkXText *xtext = GTK_XTEXT(widget); int height_only = FALSE; int do_trans = TRUE; if (allocation->width == xtext->buffer->window_width) height_only = TRUE; if (allocation->x == widget->allocation.x && allocation->y == widget->allocation.y && xtext->avoid_trans) do_trans = FALSE; xtext->avoid_trans = FALSE; widget->allocation = *allocation; if (GTK_WIDGET_REALIZED(widget)) { xtext->buffer->window_width = allocation->width; xtext->buffer->window_height = allocation->height; gdk_window_move_resize(widget->window, allocation->x, allocation->y, allocation->width, allocation->height); dontscroll(xtext->buffer); /* force scrolling off */ if (!height_only) gtk_xtext_calc_lines(xtext->buffer, FALSE); else { xtext->buffer->pagetop_ent = NULL; gtk_xtext_adjustment_set(xtext->buffer, FALSE); } #if defined(USE_XLIB) || defined(WIN32) if (do_trans && xtext->transparent && xtext->shaded) { gtk_xtext_free_trans(xtext); gtk_xtext_load_trans(xtext); } #endif if (xtext->buffer->scrollbar_down) gtk_adjustment_set_value(xtext->adj, xtext->adj->upper - xtext->adj->page_size); } } static int gtk_xtext_selection_clear(xtext_buffer * buf) { textentry *ent; int ret = 0; for (ent = buf->last_ent_start; ent; ent = ent->next) { if (ent->mark_start != -1) ret = 1; ent->mark_start = -1; ent->mark_end = -1; if (ent == buf->last_ent_end) break; } return ret; } static int find_x(GtkXText * xtext, textentry * ent, const unsigned char *text, int x, int indent) { int xx = indent; int i = 0; const unsigned char *orig = text; int mbl; int char_width; while (*text) { mbl = 1; char_width = backend_get_char_width(xtext, text, &mbl); xx += char_width; text += mbl; if (xx >= x) return i + (orig - ent->str); i += mbl; if (text - orig >= ent->str_len) return ent->str_len; } return ent->str_len; } static int gtk_xtext_find_x(GtkXText * xtext, int x, textentry * ent, int subline, int line, int *out_of_bounds) { int indent; const unsigned char *str; if (subline < 1) indent = ent->indent; else indent = xtext->buffer->indent; if (line > xtext->adj->page_size || line < 0) return 0; if (xtext->buffer->grid_dirty || line > 255) { str = ent->str + gtk_xtext_find_subline(xtext, ent, subline); if (str >= ent->str + ent->str_len) return 0; } else { if (xtext->buffer->grid_offset[line] > ent->str_len) return 0; str = ent->str + xtext->buffer->grid_offset[line]; } if (x < indent) { *out_of_bounds = 1; return (str - ent->str); } *out_of_bounds = 0; return find_x(xtext, ent, str, x, indent); } static textentry *gtk_xtext_find_char(GtkXText * xtext, int x, int y, int *off, int *out_of_bounds) { textentry *ent; int line; int subline; line = (y + xtext->pixel_offset) / xtext->fontsize; if (!(ent = gtk_xtext_nth(xtext, line + (int)xtext->adj->value, &subline))) return NULL; if (off) *off = gtk_xtext_find_x(xtext, x, ent, subline, line, out_of_bounds); return ent; } static void gtk_xtext_draw_sep(GtkXText * xtext, int y) { int x, height; GdkGC *light, *dark; if (y == -1) { y = 0; height = GTK_WIDGET(xtext)->allocation.height; } else { height = xtext->fontsize; } /* draw the separator line */ if (xtext->separator && xtext->buffer->indent) { light = xtext->light_gc; dark = xtext->dark_gc; x = xtext->buffer->indent - ((xtext->space_width + 1) / 2); if (x < 1) return; if (xtext->thinline) { if (xtext->moving_separator) gdk_draw_line(xtext->draw_buf, light, x, y, x, y + height); else gdk_draw_line(xtext->draw_buf, xtext->thin_gc, x, y, x, y + height); } else { if (xtext->moving_separator) { gdk_draw_line(xtext->draw_buf, light, x - 1, y, x - 1, y + height); gdk_draw_line(xtext->draw_buf, dark, x, y, x, y + height); } else { gdk_draw_line(xtext->draw_buf, dark, x - 1, y, x - 1, y + height); gdk_draw_line(xtext->draw_buf, light, x, y, x, y + height); } } } } static void gtk_xtext_draw_marker(GtkXText * xtext, textentry * ent, int y) { int x, width, render_y; if (!xtext->marker) return; if (xtext->buffer->marker_pos == ent) { render_y = y + xtext->font->descent; } else if (xtext->buffer->marker_pos == ent->next && ent->next != NULL) { render_y = y + xtext->font->descent + xtext->fontsize * ent->lines_taken; } else return; x = 0; width = GTK_WIDGET(xtext)->allocation.width; gdk_draw_line(xtext->draw_buf, xtext->marker_gc, x, render_y, x + width, render_y); #if GTK_CHECK_VERSION(2,4,0) if (gtk_window_has_toplevel_focus(GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(xtext))))) #else if (GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(xtext)))->has_focus) #endif { xtext->buffer->marker_seen = TRUE; } } #ifdef USE_SHM static int have_shm_pixmaps(Display *dpy) { int major, minor; static int checked = 0; static int have = FALSE; if (!checked) { XShmQueryVersion (dpy, &major, &minor, &have); checked = 1; } return have; } #endif static void gtk_xtext_paint(GtkWidget *widget, GdkRectangle * area) { GtkXText *xtext = GTK_XTEXT(widget); textentry *ent_start, *ent_end; int x, y; #if defined(USE_XLIB) || defined(WIN32) if (xtext->transparent) { gdk_window_get_origin(widget->window, &x, &y); /* update transparency only if it moved */ if (xtext->last_win_x != x || xtext->last_win_y != y) { xtext->last_win_x = x; xtext->last_win_y = y; #ifndef WIN32 #ifdef USE_SHM if (xtext->shaded && !have_shm_pixmaps(GDK_WINDOW_XDISPLAY(xtext->draw_buf))) #else if (xtext->shaded) #endif { xtext->recycle = TRUE; gtk_xtext_load_trans(xtext); xtext->recycle = FALSE; } else #endif { gtk_xtext_free_trans(xtext); gtk_xtext_load_trans(xtext); } } } #endif if (area->x == 0 && area->y == 0 && area->height == widget->allocation.height && area->width == widget->allocation.width) { dontscroll(xtext->buffer); /* force scrolling off */ gtk_xtext_render_page(xtext); return; } ent_start = gtk_xtext_find_char(xtext, area->x, area->y, NULL, NULL); if (!ent_start) { xtext_draw_bg(xtext, area->x, area->y, area->width, area->height); goto xit; } ent_end = gtk_xtext_find_char(xtext, area->x + area->width, area->y + area->height, NULL, NULL); if (!ent_end) ent_end = xtext->buffer->text_last; /* can't set a clip here, because fgc/bgc are used to draw the DB too */ /* backend_set_clip (xtext, area);*/ xtext->clip_x = area->x; xtext->clip_x2 = area->x + area->width; xtext->clip_y = area->y; xtext->clip_y2 = area->y + area->height; /* y is the last pixel y location it rendered text at */ y = gtk_xtext_render_ents(xtext, ent_start, ent_end); if (y && y < widget->allocation.height && !ent_end->next) { GdkRectangle rect; rect.x = 0; rect.y = y; rect.width = widget->allocation.width; rect.height = widget->allocation.height - y; /* fill any space below the last line that also intersects with the exposure rectangle */ if (gdk_rectangle_intersect(area, &rect, &rect)) { xtext_draw_bg(xtext, rect.x, rect.y, rect.width, rect.height); } } /*backend_clear_clip (xtext); */ xtext->clip_x = 0; xtext->clip_x2 = 1000000; xtext->clip_y = 0; xtext->clip_y2 = 1000000; xit: x = xtext->buffer->indent - ((xtext->space_width + 1) / 2); if (area->x <= x) gtk_xtext_draw_sep(xtext, -1); } static gboolean gtk_xtext_expose(GtkWidget *widget, GdkEventExpose * event) { gtk_xtext_paint(widget, &event->area); return FALSE; } /* render a selection that has extended or contracted upward */ static void gtk_xtext_selection_up(GtkXText * xtext, textentry * start, textentry * end, int start_offset) { /* render all the complete lines */ if (start->next == end) gtk_xtext_render_ents(xtext, end, NULL); else gtk_xtext_render_ents(xtext, start->next, end); /* now the incomplete upper line */ if (start == xtext->buffer->last_ent_start) xtext->jump_in_offset = xtext->buffer->last_offset_start; else xtext->jump_in_offset = start_offset; gtk_xtext_render_ents(xtext, start, NULL); xtext->jump_in_offset = 0; } /* render a selection that has extended or contracted downward */ static void gtk_xtext_selection_down(GtkXText * xtext, textentry * start, textentry * end, int end_offset) { /* render all the complete lines */ if (end->prev == start) gtk_xtext_render_ents(xtext, start, NULL); else gtk_xtext_render_ents(xtext, start, end->prev); /* now the incomplete bottom line */ if (end == xtext->buffer->last_ent_end) xtext->jump_out_offset = xtext->buffer->last_offset_end; else xtext->jump_out_offset = end_offset; gtk_xtext_render_ents(xtext, end, NULL); xtext->jump_out_offset = 0; } static void gtk_xtext_selection_render(GtkXText * xtext, textentry * start_ent, int start_offset, textentry * end_ent, int end_offset) { textentry *ent; int start, end; xtext->skip_border_fills = TRUE; xtext->skip_stamp = TRUE; /* force an optimized render if there was no previous selection */ if (xtext->buffer->last_ent_start == NULL && start_ent == end_ent) { xtext->buffer->last_offset_start = start_offset; xtext->buffer->last_offset_end = end_offset; goto lamejump; } /* mark changed within 1 ent only? */ if (xtext->buffer->last_ent_start == start_ent && xtext->buffer->last_ent_end == end_ent) { /* when only 1 end of the selection is changed, we can really save on rendering */ if (xtext->buffer->last_offset_start == start_offset || xtext->buffer->last_offset_end == end_offset) { lamejump: ent = end_ent; /* figure out where to start and end the rendering */ if (end_offset > xtext->buffer->last_offset_end) { end = end_offset; start = xtext->buffer->last_offset_end; } else if (end_offset < xtext->buffer->last_offset_end) { end = xtext->buffer->last_offset_end; start = end_offset; } else if (start_offset < xtext->buffer->last_offset_start) { end = xtext->buffer->last_offset_start; start = start_offset; ent = start_ent; } else if (start_offset > xtext->buffer->last_offset_start) { end = start_offset; start = xtext->buffer->last_offset_start; ent = start_ent; } else { /* WORD selects end up here */ end = end_offset; start = start_offset; } } else { /* LINE selects end up here */ /* so which ent actually changed? */ ent = start_ent; if (xtext->buffer->last_offset_start == start_offset) ent = end_ent; end = MAX(xtext->buffer->last_offset_end, end_offset); start = MIN(xtext->buffer->last_offset_start, start_offset); } xtext->jump_out_offset = end; xtext->jump_in_offset = start; gtk_xtext_render_ents(xtext, ent, NULL); xtext->jump_out_offset = 0; xtext->jump_in_offset = 0; } /* marking downward? */ else if (xtext->buffer->last_ent_start == start_ent && xtext->buffer->last_offset_start == start_offset) { /* find the range that covers both old and new selection */ for (ent = start_ent; ent; ent = ent->next) { if (ent == xtext->buffer->last_ent_end) { gtk_xtext_selection_down(xtext, ent, end_ent, end_offset); /*gtk_xtext_render_ents (xtext, ent, end_ent); */ break; } if (ent == end_ent) { gtk_xtext_selection_down(xtext, ent, xtext->buffer->last_ent_end, end_offset); /*gtk_xtext_render_ents (xtext, ent, xtext->buffer->last_ent_end); */ break; } } } /* marking upward? */ else if (xtext->buffer->last_ent_end == end_ent && xtext->buffer->last_offset_end == end_offset) { for (ent = end_ent; ent; ent = ent->prev) { if (ent == start_ent) { gtk_xtext_selection_up(xtext, xtext->buffer->last_ent_start, ent, start_offset); /*gtk_xtext_render_ents (xtext, xtext->buffer->last_ent_start, ent); */ break; } if (ent == xtext->buffer->last_ent_start) { gtk_xtext_selection_up(xtext, start_ent, ent, start_offset); /*gtk_xtext_render_ents (xtext, start_ent, ent); */ break; } } } else { /* cross-over mark (stretched or shrunk at both ends) */ /* unrender the old mark */ gtk_xtext_render_ents(xtext, xtext->buffer->last_ent_start, xtext->buffer->last_ent_end); /* now render the new mark, but skip overlaps */ if (start_ent == xtext->buffer->last_ent_start) { /* if the new mark is a sub-set of the old, do nothing */ if (start_ent != end_ent) gtk_xtext_render_ents(xtext, start_ent->next, end_ent); } else if (end_ent == xtext->buffer->last_ent_end) { /* if the new mark is a sub-set of the old, do nothing */ if (start_ent != end_ent) gtk_xtext_render_ents(xtext, start_ent, end_ent->prev); } else gtk_xtext_render_ents(xtext, start_ent, end_ent); } xtext->buffer->last_ent_start = start_ent; xtext->buffer->last_ent_end = end_ent; xtext->buffer->last_offset_start = start_offset; xtext->buffer->last_offset_end = end_offset; xtext->skip_border_fills = FALSE; xtext->skip_stamp = FALSE; } static void gtk_xtext_selection_draw(GtkXText * xtext, GdkEventMotion * event, gboolean render) { textentry *ent; textentry *ent_end; textentry *ent_start; int offset_start; int offset_end; int low_x; int low_y; int high_x; int high_y; int tmp; if (xtext->select_start_y > xtext->select_end_y) { low_x = xtext->select_end_x; low_y = xtext->select_end_y; high_x = xtext->select_start_x; high_y = xtext->select_start_y; } else { low_x = xtext->select_start_x; low_y = xtext->select_start_y; high_x = xtext->select_end_x; high_y = xtext->select_end_y; } ent_start = gtk_xtext_find_char(xtext, low_x, low_y, &offset_start, &tmp); if (!ent_start) { if (xtext->adj->value != xtext->buffer->old_value) gtk_xtext_render_page(xtext); return; } ent_end = gtk_xtext_find_char(xtext, high_x, high_y, &offset_end, &tmp); if (!ent_end) { ent_end = xtext->buffer->text_last; if (!ent_end) { if (xtext->adj->value != xtext->buffer->old_value) gtk_xtext_render_page(xtext); return; } offset_end = ent_end->str_len; } /* marking less than a complete line? */ /* make sure "start" is smaller than "end" (swap them if need be) */ if (ent_start == ent_end && offset_start > offset_end) { tmp = offset_start; offset_start = offset_end; offset_end = tmp; } /* has the selection changed? Dont render unless necessary */ if (xtext->buffer->last_ent_start == ent_start && xtext->buffer->last_ent_end == ent_end && xtext->buffer->last_offset_start == offset_start && xtext->buffer->last_offset_end == offset_end) return; /* set all the old mark_ fields to -1 */ gtk_xtext_selection_clear(xtext->buffer); ent_start->mark_start = offset_start; ent_start->mark_end = offset_end; if (ent_start != ent_end) { ent_start->mark_end = ent_start->str_len; if (offset_end != 0) { ent_end->mark_start = 0; ent_end->mark_end = offset_end; } /* set all the mark_ fields of the ents within the selection */ for (ent = ent_start->next; (ent && ent != ent_end); ent = ent->next) { ent->mark_start = 0; ent->mark_end = ent->str_len; } } if (render) gtk_xtext_selection_render(xtext, ent_start, offset_start, ent_end, offset_end); } static gint gtk_xtext_scrolldown_timeout(GtkXText * xtext) { int p_y, win_height; gdk_window_get_pointer(GTK_WIDGET(xtext)->window, 0, &p_y, 0); gdk_drawable_get_size(GTK_WIDGET(xtext)->window, 0, &win_height); if (p_y > win_height && xtext->adj->value < (xtext->adj->upper - xtext->adj->page_size)) { xtext->adj->value++; gtk_adjustment_changed(xtext->adj); gtk_xtext_render_page(xtext); return 1; } xtext->scroll_tag = 0; return 0; } static gint gtk_xtext_scrollup_timeout(GtkXText * xtext) { int p_y; gdk_window_get_pointer(GTK_WIDGET(xtext)->window, 0, &p_y, 0); if (p_y < 0 && xtext->adj->value > 0.0) { xtext->adj->value--; gtk_adjustment_changed(xtext->adj); gtk_xtext_render_page(xtext); return 1; } xtext->scroll_tag = 0; return 0; } static void gtk_xtext_selection_update(GtkXText * xtext, GdkEventMotion * event, int p_y, gboolean render) { int win_height; int moved; gdk_drawable_get_size(GTK_WIDGET(xtext)->window, 0, &win_height); /* selecting past top of window, scroll up! */ if (p_y < 0 && xtext->adj->value >= 0) { if (!xtext->scroll_tag) xtext->scroll_tag = g_timeout_add(100, (GSourceFunc) gtk_xtext_scrollup_timeout, xtext); return; } /* selecting past bottom of window, scroll down! */ if (p_y > win_height && xtext->adj->value < (xtext->adj->upper - xtext->adj->page_size)) { if (!xtext->scroll_tag) xtext->scroll_tag = g_timeout_add(100, (GSourceFunc) gtk_xtext_scrolldown_timeout, xtext); return; } moved = (int)xtext->adj->value - xtext->select_start_adj; xtext->select_start_y -= (moved * xtext->fontsize); xtext->select_start_adj = xtext->adj->value; gtk_xtext_selection_draw(xtext, event, render); } static char *gtk_xtext_get_word(GtkXText * xtext, int x, int y, textentry ** ret_ent, int *ret_off, int *ret_len) { textentry *ent; int offset; const unsigned char *str; const unsigned char *word; int len; int out_of_bounds = 0; ent = gtk_xtext_find_char(xtext, x, y, &offset, &out_of_bounds); if (!ent) return NULL; if (out_of_bounds) return NULL; if (offset == ent->str_len) return NULL; if (offset < 1) return NULL; /*offset--; *//* FIXME: not all chars are 1 byte */ str = ent->str + offset; while (!is_del(*str) && str != ent->str) str--; word = str + 1; len = 0; str = word; while (!is_del(*str) && len != ent->str_len) { str++; len++; } if (len > 0 && word[len - 1] == '.') { len--; str--; } if (ret_ent) *ret_ent = ent; if (ret_off) *ret_off = word - ent->str; if (ret_len) *ret_len = str - word; return gtk_xtext_strip_color(word, len, xtext->scratch_buffer, NULL); } #ifdef MOTION_MONITOR static void gtk_xtext_unrender_hilight(GtkXText * xtext) { xtext->render_hilights_only = TRUE; xtext->skip_border_fills = TRUE; xtext->skip_stamp = TRUE; xtext->un_hilight = TRUE; gtk_xtext_render_ents(xtext, xtext->hilight_ent, NULL); xtext->render_hilights_only = FALSE; xtext->skip_border_fills = FALSE; xtext->skip_stamp = FALSE; xtext->un_hilight = FALSE; } static gboolean gtk_xtext_leave_notify(GtkWidget *widget, GdkEventCrossing * event) { GtkXText *xtext = GTK_XTEXT(widget); if (xtext->cursor_hand) { gtk_xtext_unrender_hilight(xtext); xtext->hilight_start = -1; xtext->hilight_end = -1; xtext->cursor_hand = FALSE; gdk_window_set_cursor(widget->window, 0); xtext->hilight_ent = NULL; } if (xtext->cursor_resize) { gtk_xtext_unrender_hilight(xtext); xtext->hilight_start = -1; xtext->hilight_end = -1; xtext->cursor_resize = FALSE; gdk_window_set_cursor(widget->window, 0); xtext->hilight_ent = NULL; } return FALSE; } #endif /* check if we should mark time stamps, and if a redraw is needed */ static gboolean gtk_xtext_check_mark_stamp(GtkXText * xtext, GdkModifierType mask) { gboolean redraw = FALSE; if ((mask & GDK_SHIFT_MASK)) { if (!xtext->mark_stamp) { redraw = TRUE; /* must redraw all */ xtext->mark_stamp = TRUE; } } else { if (xtext->mark_stamp) { redraw = TRUE; /* must redraw all */ xtext->mark_stamp = FALSE; } } return redraw; } static gboolean gtk_xtext_motion_notify(GtkWidget *widget, GdkEventMotion * event) { GtkXText *xtext = GTK_XTEXT(widget); GdkModifierType mask; int redraw, tmp, x, y, offset, len, line_x; char *word; textentry *word_ent; gdk_window_get_pointer(widget->window, &x, &y, &mask); if (xtext->moving_separator) { if (x < (3 * widget->allocation.width) / 5 && x > 15) { tmp = xtext->buffer->indent; xtext->buffer->indent = x; gtk_xtext_fix_indent(xtext->buffer); if (tmp != xtext->buffer->indent) { gtk_xtext_recalc_widths(xtext->buffer, FALSE); if (xtext->buffer->scrollbar_down) gtk_adjustment_set_value(xtext->adj, xtext->adj->upper - xtext->adj->page_size); if (!xtext->io_tag) xtext->io_tag = g_timeout_add(REFRESH_TIMEOUT, (GSourceFunc) gtk_xtext_adjustment_timeout, xtext); } } return FALSE; } if (xtext->button_down) { redraw = gtk_xtext_check_mark_stamp(xtext, mask); gtk_grab_add(widget); /*gdk_pointer_grab (widget->window, TRUE, GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_MOTION_MASK, NULL, NULL, 0); */ xtext->select_end_x = x; xtext->select_end_y = y; gtk_xtext_selection_update(xtext, event, y, !redraw); xtext->hilighting = TRUE; /* user has pressed or released SHIFT, must redraw entire selection */ if (redraw) { xtext->force_stamp = TRUE; gtk_xtext_render_ents(xtext, xtext->buffer->last_ent_start, xtext->buffer->last_ent_end); xtext->force_stamp = FALSE; } return FALSE; } #ifdef MOTION_MONITOR if (xtext->separator && xtext->buffer->indent) { line_x = xtext->buffer->indent - ((xtext->space_width + 1) / 2); if (line_x == x || line_x == x + 1 || line_x == x - 1) { if (!xtext->cursor_resize) { gdk_window_set_cursor(GTK_WIDGET(xtext)->window, xtext->resize_cursor); xtext->cursor_resize = TRUE; } return FALSE; } } if (xtext->urlcheck_function == NULL) return FALSE; word = gtk_xtext_get_word(xtext, x, y, &word_ent, &offset, &len); if (word) { if (xtext->urlcheck_function(GTK_WIDGET(xtext), word, len) > 0) { if (!xtext->cursor_hand || xtext->hilight_ent != word_ent || xtext->hilight_start != offset || xtext->hilight_end != offset + len) { if (!xtext->cursor_hand) { gdk_window_set_cursor(GTK_WIDGET(xtext)->window, xtext->hand_cursor); xtext->cursor_hand = TRUE; } /* un-render the old hilight */ if (xtext->hilight_ent) gtk_xtext_unrender_hilight(xtext); xtext->hilight_ent = word_ent; xtext->hilight_start = offset; xtext->hilight_end = offset + len; xtext->skip_border_fills = TRUE; xtext->render_hilights_only = TRUE; xtext->skip_stamp = TRUE; gtk_xtext_render_ents(xtext, word_ent, NULL); xtext->skip_border_fills = FALSE; xtext->render_hilights_only = FALSE; xtext->skip_stamp = FALSE; } return FALSE; } } gtk_xtext_leave_notify(widget, NULL); #endif return FALSE; } static void gtk_xtext_set_clip_owner(GtkWidget *xtext, GdkEventButton * event) { char *str; int len; if (GTK_XTEXT(xtext)->selection_buffer && GTK_XTEXT(xtext)->selection_buffer != GTK_XTEXT(xtext)->buffer) gtk_xtext_selection_clear(GTK_XTEXT(xtext)->selection_buffer); GTK_XTEXT(xtext)->selection_buffer = GTK_XTEXT(xtext)->buffer; if ((str = gtk_xtext_selection_get_text(GTK_XTEXT(xtext), &len))) { #if (GTK_MAJOR_VERSION == 2) && (GTK_MINOR_VERSION == 0) gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), str, len); #else gtk_clipboard_set_text(gtk_widget_get_clipboard(xtext, GDK_SELECTION_CLIPBOARD), str, len); #endif free(str); } gtk_selection_owner_set(xtext, GDK_SELECTION_PRIMARY, event->time); } static void gtk_xtext_unselect(GtkXText * xtext) { xtext_buffer *buf = xtext->buffer; xtext->skip_border_fills = TRUE; xtext->skip_stamp = TRUE; xtext->jump_in_offset = buf->last_ent_start->mark_start; /* just a single ent was marked? */ if (buf->last_ent_start == buf->last_ent_end) { xtext->jump_out_offset = buf->last_ent_start->mark_end; buf->last_ent_end = NULL; } gtk_xtext_selection_clear(xtext->buffer); /* FIXME: use jump_out on multi-line selects too! */ gtk_xtext_render_ents(xtext, buf->last_ent_start, buf->last_ent_end); xtext->jump_in_offset = 0; xtext->jump_out_offset = 0; xtext->skip_border_fills = FALSE; xtext->skip_stamp = FALSE; xtext->buffer->last_ent_start = NULL; xtext->buffer->last_ent_end = NULL; } static gboolean gtk_xtext_button_release(GtkWidget *widget, GdkEventButton * event) { GtkXText *xtext = GTK_XTEXT(widget); int old; if (xtext->moving_separator) { xtext->moving_separator = FALSE; old = xtext->buffer->indent; if (event->x < (4 * widget->allocation.width) / 5 && event->x > 15) xtext->buffer->indent = event->x; gtk_xtext_fix_indent(xtext->buffer); if (xtext->buffer->indent != old) { gtk_xtext_recalc_widths(xtext->buffer, FALSE); gtk_xtext_adjustment_set(xtext->buffer, TRUE); gtk_xtext_render_page(xtext); } else gtk_xtext_draw_sep(xtext, -1); return FALSE; } if (xtext->word_or_line_select) { xtext->word_or_line_select = FALSE; xtext->button_down = FALSE; return FALSE; } if (event->button == 1) { xtext->button_down = FALSE; gtk_grab_remove(widget); /*gdk_pointer_ungrab (0); */ /* got a new selection? */ if (xtext->buffer->last_ent_start) gtk_xtext_set_clip_owner(GTK_WIDGET(xtext), event); if (xtext->select_start_x == event->x && xtext->select_start_y == event->y && xtext->buffer->last_ent_start) { gtk_xtext_unselect(xtext); xtext->mark_stamp = FALSE; return FALSE; } if (!xtext->hilighting) { char *word = gtk_xtext_get_word(xtext, event->x, event->y, 0, 0, 0); g_signal_emit(G_OBJECT(xtext), xtext_signals[WORD_CLICK], 0, word ? word : NULL, event); } else { xtext->hilighting = FALSE; } } return FALSE; } static gboolean gtk_xtext_button_press(GtkWidget *widget, GdkEventButton * event) { GtkXText *xtext = GTK_XTEXT(widget); GdkModifierType mask; textentry *ent; int line_x, x, y, offset, len; gdk_window_get_pointer(widget->window, &x, &y, &mask); if (event->button == 3 || event->button == 2) { /* right/middle click */ char *word = gtk_xtext_get_word(xtext, x, y, 0, 0, 0); if (word) { g_signal_emit(G_OBJECT(xtext), xtext_signals[WORD_CLICK], 0, word, event); } else g_signal_emit(G_OBJECT(xtext), xtext_signals[WORD_CLICK], 0, "", event); return FALSE; } if (event->button != 1) /* we only want left button */ return FALSE; if (event->type == GDK_2BUTTON_PRESS) { /* WORD select */ gtk_xtext_check_mark_stamp(xtext, mask); if (gtk_xtext_get_word(xtext, x, y, &ent, &offset, &len)) { if (len == 0) return FALSE; gtk_xtext_selection_clear(xtext->buffer); ent->mark_start = offset; ent->mark_end = offset + len; gtk_xtext_selection_render(xtext, ent, offset, ent, offset + len); xtext->word_or_line_select = TRUE; gtk_xtext_set_clip_owner(GTK_WIDGET(xtext), event); } return FALSE; } if (event->type == GDK_3BUTTON_PRESS) { /* LINE select */ gtk_xtext_check_mark_stamp(xtext, mask); if (gtk_xtext_get_word(xtext, x, y, &ent, 0, 0)) { gtk_xtext_selection_clear(xtext->buffer); ent->mark_start = 0; ent->mark_end = ent->str_len; gtk_xtext_selection_render(xtext, ent, 0, ent, ent->str_len); xtext->word_or_line_select = TRUE; gtk_xtext_set_clip_owner(GTK_WIDGET(xtext), event); } return FALSE; } /* check if it was a separator-bar click */ if (xtext->separator && xtext->buffer->indent) { line_x = xtext->buffer->indent - ((xtext->space_width + 1) / 2); if (line_x == x || line_x == x + 1 || line_x == x - 1) { xtext->moving_separator = TRUE; /* draw the separator line */ gtk_xtext_draw_sep(xtext, -1); return FALSE; } } xtext->button_down = TRUE; xtext->select_start_x = x; xtext->select_start_y = y; xtext->select_start_adj = xtext->adj->value; return FALSE; } /* another program has claimed the selection */ static gboolean gtk_xtext_selection_kill(GtkXText * xtext, GdkEventSelection * event) { if (xtext->buffer->last_ent_start) gtk_xtext_unselect(xtext); return TRUE; } static char *gtk_xtext_selection_get_text(GtkXText * xtext, int *len_ret) { textentry *ent; char *txt; char *pos; int len; int first = TRUE; xtext_buffer *buf; buf = xtext->selection_buffer; if (!buf) return NULL; /* first find out how much we need to malloc ... */ len = 0; for (ent = buf->last_ent_start; ent; ent = ent->next) { if (ent->mark_start != -1) { /* include timestamp? */ if (ent->mark_start == 0 && xtext->mark_stamp) { int stamp_size = xstrlen(timestamp_time("%H:%M:%S", ent->fstr->ts)); len += stamp_size; } if (ent->mark_end - ent->mark_start > 0) len += (ent->mark_end - ent->mark_start) + 1; else len++; } if (ent == buf->last_ent_end) break; } if (len < 1) return NULL; /* now allocate mem and copy buffer */ pos = txt = xmalloc(len); for (ent = buf->last_ent_start; ent; ent = ent->next) { if (ent->mark_start != -1) { if (!first) { *pos = '\n'; pos++; } first = FALSE; if (ent->mark_end - ent->mark_start > 0) { /* include timestamp? */ if (ent->mark_start == 0 && xtext->mark_stamp) { const char *time_str = timestamp_time("%H:%M:%S", ent->fstr->ts); int stamp_size = xstrlen(time_str); memcpy(pos, time_str, stamp_size); pos += stamp_size; } memcpy(pos, ent->str + ent->mark_start, ent->mark_end - ent->mark_start); pos += ent->mark_end - ent->mark_start; } } if (ent == buf->last_ent_end) break; } *pos = 0; *len_ret = xstrlen(txt); return txt; } /* another program is asking for our selection */ static void gtk_xtext_selection_get(GtkWidget *widget, GtkSelectionData * selection_data_ptr, guint info, guint time) { GtkXText *xtext = GTK_XTEXT(widget); char *stripped; guchar *new_text; int len; gsize glen; if (!(stripped = gtk_xtext_selection_get_text(xtext, &len))) return; switch (info) { case TARGET_UTF8_STRING: /* it's already in utf8 */ gtk_selection_data_set_text(selection_data_ptr, stripped, len); break; case TARGET_TEXT: case TARGET_COMPOUND_TEXT: { GdkAtom encoding; gint format; gint new_length; #if (GTK_MAJOR_VERSION == 2) && (GTK_MINOR_VERSION == 0) gdk_string_to_compound_text( stripped, &encoding, &format, &new_text, &new_length); #else gdk_string_to_compound_text_for_display(gdk_drawable_get_display (widget->window), stripped, &encoding, &format, &new_text, &new_length); #endif gtk_selection_data_set(selection_data_ptr, encoding, format, new_text, new_length); gdk_free_compound_text(new_text); } break; /* case TARGET_STRING */ default: new_text = g_locale_from_utf8(stripped, len, NULL, &glen, NULL); gtk_selection_data_set(selection_data_ptr, GDK_SELECTION_TYPE_STRING, 8, new_text, glen); g_free(new_text); } free(stripped); } static gboolean gtk_xtext_scroll(GtkWidget *widget, GdkEventScroll * event) { GtkXText *xtext = GTK_XTEXT(widget); gfloat new_value; if (event->direction == GDK_SCROLL_UP) { /* mouse wheel pageUp */ new_value = xtext->adj->value - (xtext->adj->page_increment / 10); if (new_value < xtext->adj->lower) new_value = xtext->adj->lower; gtk_adjustment_set_value(xtext->adj, new_value); } else if (event->direction == GDK_SCROLL_DOWN) { /* mouse wheel pageDn */ new_value = xtext->adj->value + (xtext->adj->page_increment / 10); if (new_value > (xtext->adj->upper - xtext->adj->page_size)) new_value = xtext->adj->upper - xtext->adj->page_size; gtk_adjustment_set_value(xtext->adj, new_value); } return FALSE; } static void gtk_xtext_class_init(GtkXTextClass * class) { GtkObjectClass *object_class; GtkWidgetClass *widget_class; GtkXTextClass *xtext_class; object_class = (GtkObjectClass *) class; widget_class = (GtkWidgetClass *) class; xtext_class = (GtkXTextClass *) class; parent_class = gtk_type_class(gtk_widget_get_type()); xtext_signals[WORD_CLICK] = g_signal_new("word_click", G_TYPE_FROM_CLASS(object_class), G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, G_STRUCT_OFFSET(GtkXTextClass, word_click), NULL, NULL, gtk_marshal_VOID__POINTER_POINTER, G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER); object_class->destroy = gtk_xtext_destroy; widget_class->realize = gtk_xtext_realize; widget_class->unrealize = gtk_xtext_unrealize; widget_class->size_request = gtk_xtext_size_request; widget_class->size_allocate = gtk_xtext_size_allocate; widget_class->button_press_event = gtk_xtext_button_press; widget_class->button_release_event = gtk_xtext_button_release; widget_class->motion_notify_event = gtk_xtext_motion_notify; widget_class->selection_clear_event = (void *)gtk_xtext_selection_kill; widget_class->selection_get = gtk_xtext_selection_get; widget_class->expose_event = gtk_xtext_expose; widget_class->scroll_event = gtk_xtext_scroll; #ifdef MOTION_MONITOR widget_class->leave_notify_event = gtk_xtext_leave_notify; #endif xtext_class->word_click = NULL; } GType gtk_xtext_get_type(void) { static GType xtext_type = 0; if (!xtext_type) { static const GTypeInfo xtext_info = { sizeof(GtkXTextClass), NULL, /* base_init */ NULL, /* base_finalize */ (GClassInitFunc) gtk_xtext_class_init, NULL, /* class_finalize */ NULL, /* class_data */ sizeof(GtkXText), 0, /* n_preallocs */ (GInstanceInitFunc) gtk_xtext_init, }; xtext_type = g_type_register_static(GTK_TYPE_WIDGET, "GtkXText", &xtext_info, 0); } return xtext_type; } /* copy text to outbuf, [len bytes], terminate with \0 * set *mb_ret to TRUE, if there're chars with ANSI code >= 128 */ static unsigned char *gtk_xtext_strip_color(const unsigned char *text, int len, unsigned char *outbuf, int *mb_ret) { int i = 0; int mb = FALSE; for (i = 0; i < len; i++) { if (text[i] >= 128) mb = TRUE; outbuf[i] = *text; } outbuf[len] = 0; if (mb_ret != NULL) *mb_ret = mb; return outbuf; } /* gives width of a string */ static int gtk_xtext_text_width(GtkXText * xtext, const unsigned char *text, int len, int *mb_ret) { unsigned char *new_buf; int mb; new_buf = gtk_xtext_strip_color(text, len, xtext->scratch_buffer, &mb); if (mb_ret) *mb_ret = mb; return backend_get_text_width(xtext, new_buf, len, mb); } /* actually draw text to screen (one run with the same color/attribs) */ static int gtk_xtext_render_flush(GtkXText * xtext, int x, int y, const unsigned char *str, int len, GdkGC * gc, int is_mb) { int str_width, dofill = TRUE; #ifdef USE_DB GdkDrawable *pix; #endif if (xtext->dont_render || len < 1) return 0; str_width = backend_get_text_width(xtext, str, len, is_mb); if (xtext->dont_render2) return str_width; /* roll-your-own clipping (avoiding XftDrawString is always good!) */ if (x > xtext->clip_x2 || x + str_width < xtext->clip_x) return str_width; if (y - xtext->font->ascent > xtext->clip_y2 || (y - xtext->font->ascent) + xtext->fontsize < xtext->clip_y) return str_width; if (xtext->render_hilights_only) { if (!xtext->in_hilight) /* is it a hilight prefix? */ return str_width; #ifndef COLOR_HILIGHT if (!xtext->un_hilight) /* doing a hilight? no need to draw the text */ goto dounder; #endif } #ifdef USE_DB pix = gdk_pixmap_new(xtext->draw_buf, str_width, xtext->fontsize, xtext->depth); if (pix) { GdkRectangle clip; GdkRectangle dest; int dest_x, dest_y; #ifdef USE_XFT XftDrawChange(xtext->xftdraw, GDK_WINDOW_XWINDOW(pix)); #endif dest_x = x; dest_y = y - xtext->font->ascent; gdk_gc_set_ts_origin(xtext->bgc, xtext->ts_x - x, xtext->ts_y - dest_y); xtext->draw_buf = pix; /* backcolor is always handled by XDrawImageString */ if (!xtext->backcolor && xtext->pixmap) { /* draw the background pixmap behind the text - CAUSES FLICKER HERE!! */ xtext_draw_bg(xtext, 0, 0, str_width, xtext->fontsize); dofill = FALSE; /* already drawn the background */ } backend_draw_text(xtext, dofill, gc, 0, xtext->font->ascent, str, len, str_width, is_mb); gdk_gc_set_ts_origin(xtext->bgc, xtext->ts_x, xtext->ts_y); xtext->draw_buf = GTK_WIDGET(xtext)->window; #ifdef USE_XFT XftDrawChange(xtext->xftdraw, GDK_WINDOW_XWINDOW(xtext->draw_buf)); #endif clip.x = xtext->clip_x; clip.y = xtext->clip_y; clip.width = xtext->clip_x2 - xtext->clip_x; clip.height = xtext->clip_y2 - xtext->clip_y; dest.x = dest_x; dest.y = dest_y; dest.width = str_width; dest.height = xtext->fontsize; if (gdk_rectangle_intersect(&clip, &dest, &dest)) /* dump the DB to window, but only within the clip_x/x2/y/y2 */ gdk_draw_drawable(xtext->draw_buf, xtext->bgc, pix, dest.x - dest_x, dest.y - dest_y, dest.x, dest.y, dest.width, dest.height); g_object_unref(pix); } else #endif { /* backcolor is always handled by XDrawImageString */ if (!xtext->backcolor && xtext->pixmap) { /* draw the background pixmap behind the text - CAUSES FLICKER HERE!! */ xtext_draw_bg(xtext, x, y - xtext->font->ascent, str_width, xtext->fontsize); dofill = FALSE; /* already drawn the background */ } backend_draw_text(xtext, dofill, gc, x, y, str, len, str_width, is_mb); } if (xtext->underline) { #ifdef USE_XFT GdkColor col; #endif #ifndef COLOR_HILIGHT dounder: #endif #ifdef USE_XFT col.pixel = xtext->xft_fg->pixel; gdk_gc_set_foreground(gc, &col); #endif /* draw directly to window, it's out of the range of our DB */ gdk_draw_line(xtext->draw_buf, gc, x, y + 1, x + str_width - 1, y + 1); } return str_width; } static void gtk_xtext_reset(GtkXText * xtext, int mark, int attribs) { if (attribs) { xtext->underline = FALSE; xtext->bold = FALSE; xtext->italics = FALSE; } if (!mark) { xtext->backcolor = FALSE; if (xtext->col_fore != XTEXT_FG) xtext_set_fg(xtext, xtext->fgc, XTEXT_FG); if (xtext->col_back != XTEXT_BG) xtext_set_bg(xtext, xtext->fgc, XTEXT_BG); } xtext->col_fore = XTEXT_FG; xtext->col_back = XTEXT_BG; } /* render a single line, which WONT wrap, and parse fstring_t attr */ static int gtk_xtext_render_str(GtkXText * xtext, int y, textentry * ent, const unsigned char *str, fstr_attr_t *attr, int len, int win_width, int indent, int line, int left_only, int *x_size_ret) { GdkGC *gc; int i = 0, x = indent, j = 0; const unsigned char *pstr = str; int offset; int mark = FALSE; int ret = 1; fstr_attr_t last_attr = FSTR_NORMAL; xtext->in_hilight = FALSE; offset = str - ent->str; if (line < 255 && line >= 0) xtext->buffer->grid_offset[line] = offset; gc = xtext->fgc; /* our foreground GC */ if (ent->mark_start != -1 && ent->mark_start <= i + offset && ent->mark_end > i + offset) { xtext_set_bg(xtext, gc, XTEXT_MARK_BG); xtext_set_fg(xtext, gc, XTEXT_MARK_FG); xtext->backcolor = TRUE; mark = TRUE; } #ifdef MOTION_MONITOR if (xtext->hilight_ent == ent && xtext->hilight_start <= i + offset && xtext->hilight_end > i + offset) { if (!xtext->un_hilight) { #ifdef COLOR_HILIGHT xtext_set_bg(xtext, gc, 2); #else xtext->underline = TRUE; #endif } xtext->in_hilight = TRUE; } #endif if (!xtext->skip_border_fills && !xtext->dont_render) { /* draw background to the left of the text */ if (str == ent->str && indent > MARGIN && xtext->buffer->time_stamp) { /* don't overwrite the timestamp */ if (indent > xtext->stamp_width) { xtext_draw_bg(xtext, xtext->stamp_width, y - xtext->font->ascent, indent - xtext->stamp_width, xtext->fontsize); } } else { /* fill the indent area with background gc */ if (indent >= xtext->clip_x) { xtext_draw_bg(xtext, 0, y - xtext->font->ascent, MIN(indent, xtext->clip_x2), xtext->fontsize); } } } if (xtext->jump_in_offset > 0 && offset < xtext->jump_in_offset) xtext->dont_render2 = TRUE; while (i < len) { #ifdef MOTION_MONITOR if (xtext->hilight_ent == ent && xtext->hilight_start == (i + offset)) { x += gtk_xtext_render_flush(xtext, x, y, pstr, j, gc, ent->mb); pstr += j; j = 0; if (!xtext->un_hilight) { #ifdef COLOR_HILIGHT xtext_set_bg(xtext, gc, 2); #else xtext->underline = TRUE; #endif } xtext->in_hilight = TRUE; } #endif if (attr && last_attr != attr[i]) { int isbold; last_attr = attr[i]; if (i) { x += gtk_xtext_render_flush(xtext, x, y, pstr, j, gc, ent->mb); pstr += j; j = 0; } gtk_xtext_reset(xtext, mark, !xtext->in_hilight); isbold = ((last_attr & FSTR_BOLD) != 0); /* xtext->bold = (isbold); */ /* more follow */ if (!(last_attr & FSTR_NORMAL)) { if (!mark) { xtext_set_fg(xtext, gc, ((last_attr & FSTR_FOREMASK) + 8*(isbold))); /* xtext_set_bg(xtext, gc, ((last_attr >> 3) & 7)); */ } xtext->col_fore = ((last_attr & FSTR_FOREMASK) + 8*(isbold)); /* xtext->col_back = ((last_attr >> 3) & 7); */ } else { if (isbold) { if (!mark) xtext_set_fg(xtext, gc, 7+8); xtext->col_fore = 7+8; } } } if (str[i] != '\n') { int tmp = charlen(str + i); /* invalid utf8 safe guard */ if (tmp + i > len) tmp = len - i; j += tmp; /* move to the next utf8 char */ } i += charlen(str + i); /* move to the next utf8 char */ /* invalid utf8 safe guard */ if (i > len) i = len; /* Separate the left part, the space and the right part into separate runs, and reset bidi state inbetween. Perform this only on the first line of the message. */ if (offset == 0) { /* we've reached the end of the left part? */ if ((pstr - str) + j == ent->left_len) { x += gtk_xtext_render_flush(xtext, x, y, pstr, j, gc, ent->mb); pstr += j; j = 0; } else if ((pstr - str) + j == ent->left_len + 1) { x += gtk_xtext_render_flush(xtext, x, y, pstr, j, gc, ent->mb); pstr += j; j = 0; } } /* have we been told to stop rendering at this point? */ if (xtext->jump_out_offset > 0 && xtext->jump_out_offset <= (i + offset)) { gtk_xtext_render_flush(xtext, x, y, pstr, j, gc, ent->mb); ret = 0; /* skip the rest of the lines, we're done. */ j = 0; break; } if (xtext->jump_in_offset > 0 && xtext->jump_in_offset == (i + offset)) { x += gtk_xtext_render_flush(xtext, x, y, pstr, j, gc, ent->mb); pstr += j; j = 0; xtext->dont_render2 = FALSE; } #ifdef MOTION_MONITOR if (xtext->hilight_ent == ent && xtext->hilight_end == (i + offset)) { x += gtk_xtext_render_flush(xtext, x, y, pstr, j, gc, ent->mb); pstr += j; j = 0; #ifdef COLOR_HILIGHT if (mark) { xtext_set_bg(xtext, gc, XTEXT_MARK_BG); xtext->backcolor = TRUE; } else { xtext_set_bg(xtext, gc, xtext->col_back); if (xtext->col_back != XTEXT_BG) xtext->backcolor = TRUE; else xtext->backcolor = FALSE; } #else xtext->underline = FALSE; #endif xtext->in_hilight = FALSE; if (xtext->render_hilights_only) { /* stop drawing this ent */ ret = 0; break; } } #endif if (!mark && ent->mark_start == (i + offset)) { x += gtk_xtext_render_flush(xtext, x, y, pstr, j, gc, ent->mb); pstr += j; j = 0; xtext_set_bg(xtext, gc, XTEXT_MARK_BG); xtext_set_fg(xtext, gc, XTEXT_MARK_FG); xtext->backcolor = TRUE; mark = TRUE; } if (mark && ent->mark_end == (i + offset)) { x += gtk_xtext_render_flush(xtext, x, y, pstr, j, gc, ent->mb); pstr += j; j = 0; xtext_set_bg(xtext, gc, xtext->col_back); xtext_set_fg(xtext, gc, xtext->col_fore); if (xtext->col_back != XTEXT_BG) xtext->backcolor = TRUE; else xtext->backcolor = FALSE; mark = FALSE; } } if (j) x += gtk_xtext_render_flush(xtext, x, y, pstr, j, gc, ent->mb); if (mark) { xtext_set_bg(xtext, gc, xtext->col_back); xtext_set_fg(xtext, gc, xtext->col_fore); if (xtext->col_back != XTEXT_BG) xtext->backcolor = TRUE; else xtext->backcolor = FALSE; } /* draw background to the right of the text */ if (!left_only && !xtext->dont_render) { /* draw separator now so it doesn't appear to flicker */ gtk_xtext_draw_sep(xtext, y - xtext->font->ascent); if (!xtext->skip_border_fills && xtext->clip_x2 >= x) { int xx = MAX(x, xtext->clip_x); xtext_draw_bg(xtext, xx, /* x */ y - xtext->font->ascent, /* y */ MIN(xtext->clip_x2 - xx, (win_width + MARGIN) - xx), /* width */ xtext->fontsize); /* height */ } } xtext->dont_render2 = FALSE; /* return how much we drew in the x direction */ if (x_size_ret) *x_size_ret = x - indent; return ret; } #ifdef USE_XLIB /* get the desktop/root window */ static Window desktop_window = None; static Window get_desktop_window(Display * xdisplay, Window the_window) { Atom prop, type; int format; unsigned long length, after; unsigned char *data; unsigned int nchildren; Window w, root, *children, parent; prop = XInternAtom(xdisplay, "_XROOTPMAP_ID", True); if (prop == None) { prop = XInternAtom(xdisplay, "_XROOTCOLOR_PIXEL", True); if (prop == None) return None; } for (w = the_window; w; w = parent) { if ((XQueryTree(xdisplay, w, &root, &parent, &children, &nchildren)) == False) return None; if (nchildren) XFree(children); XGetWindowProperty(xdisplay, w, prop, 0L, 1L, False, AnyPropertyType, &type, &format, &length, &after, &data); if (data) XFree(data); if (type != None) return (desktop_window = w); } return (desktop_window = None); } /* find the root window (backdrop) Pixmap */ static Pixmap get_pixmap_prop(Display * xdisplay, Window the_window) { Atom type; int format; unsigned long length, after; unsigned char *data; Pixmap pix = None; static Atom prop = None; if (desktop_window == None) desktop_window = get_desktop_window(xdisplay, the_window); if (desktop_window == None) desktop_window = DefaultRootWindow(xdisplay); if (prop == None) prop = XInternAtom(xdisplay, "_XROOTPMAP_ID", True); if (prop == None) return None; XGetWindowProperty(xdisplay, desktop_window, prop, 0L, 1L, False, AnyPropertyType, &type, &format, &length, &after, &data); if (data) { if (type == XA_PIXMAP) pix = *((Pixmap *) data); XFree(data); } return pix; } /* slow generic routine, for the depths/bpp we don't know about */ static void shade_ximage_generic(GdkVisual * visual, XImage * ximg, int bpl, int w, int h, int rm, int gm, int bm, int bg) { int x, y; int bgr = (256 - rm) * (bg & visual->red_mask); int bgg = (256 - gm) * (bg & visual->green_mask); int bgb = (256 - bm) * (bg & visual->blue_mask); for (x = 0; x < w; x++) { for (y = 0; y < h; y++) { unsigned long pixel = XGetPixel(ximg, x, y); int r, g, b; r = rm * (pixel & visual->red_mask) + bgr; g = gm * (pixel & visual->green_mask) + bgg; b = bm * (pixel & visual->blue_mask) + bgb; XPutPixel(ximg, x, y, ((r >> 8) & visual->red_mask) | ((g >> 8) & visual->green_mask) | ((b >> 8) & visual->blue_mask)); } } } #endif /* Fast shading routine. Based on code by Willem Monsuwe */ #define SHADE_IMAGE(bytes, type, rmask, gmask, bmask) \ unsigned char *ptr; \ int x, y; \ int bgr = (256 - rm) * (bg & rmask); \ int bgg = (256 - gm) * (bg & gmask); \ int bgb = (256 - bm) * (bg & bmask); \ ptr = (unsigned char *) data + (w * bytes); \ for (y = h; --y >= 0;) \ { \ for (x = -w; x < 0; x++) \ { \ int r, g, b; \ b = ((type *) ptr)[x]; \ r = rm * (b & rmask) + bgr; \ g = gm * (b & gmask) + bgg; \ b = bm * (b & bmask) + bgb; \ ((type *) ptr)[x] = ((r >> 8) & rmask) \ | ((g >> 8) & gmask) \ | ((b >> 8) & bmask); \ } \ ptr += bpl; \ } /* RGB 15 */ static void shade_ximage_15(void *data, int bpl, int w, int h, int rm, int gm, int bm, int bg) { SHADE_IMAGE(2, guint16, 0x7c00, 0x3e0, 0x1f); } /* RGB 16 */ static void shade_ximage_16(void *data, int bpl, int w, int h, int rm, int gm, int bm, int bg) { SHADE_IMAGE(2, guint16, 0xf800, 0x7e0, 0x1f); } /* RGB 24 */ static void shade_ximage_24(void *data, int bpl, int w, int h, int rm, int gm, int bm, int bg) { /* 24 has to be a special case, there's no guint24, or 24bit MOV :) */ unsigned char *ptr; int x, y; int bgr = (256 - rm) * ((bg & 0xff0000) >> 16); int bgg = (256 - gm) * ((bg & 0xff00) >> 8); int bgb = (256 - bm) * (bg & 0xff); ptr = (unsigned char *)data + (w * 3); for (y = h; --y >= 0;) { for (x = -(w * 3); x < 0; x += 3) { int r, g, b; #if (G_BYTE_ORDER == G_BIG_ENDIAN) r = (ptr[x + 0] * rm + bgr) >> 8; g = (ptr[x + 1] * gm + bgg) >> 8; b = (ptr[x + 2] * bm + bgb) >> 8; ptr[x + 0] = r; ptr[x + 1] = g; ptr[x + 2] = b; #else r = (ptr[x + 2] * rm + bgr) >> 8; g = (ptr[x + 1] * gm + bgg) >> 8; b = (ptr[x + 0] * bm + bgb) >> 8; ptr[x + 2] = r; ptr[x + 1] = g; ptr[x + 0] = b; #endif } ptr += bpl; } } /* RGB 32 */ static void shade_ximage_32(void *data, int bpl, int w, int h, int rm, int gm, int bm, int bg) { SHADE_IMAGE(4, guint32, 0xff0000, 0xff00, 0xff); } static void shade_image(GdkVisual * visual, void *data, int bpl, int bpp, int w, int h, int rm, int gm, int bm, int bg, int depth) { int bg_r, bg_g, bg_b; bg_r = bg & visual->red_mask; bg_g = bg & visual->green_mask; bg_b = bg & visual->blue_mask; #ifdef USE_MMX /* the MMX routines are about 50% faster at 16-bit. */ /* only use MMX routines with a pure black background */ if (bg_r == 0 && bg_g == 0 && bg_b == 0 && have_mmx()) { /* do a runtime check too! */ switch (depth) { case 15: shade_ximage_15_mmx(data, bpl, w, h, rm, gm, bm); break; case 16: shade_ximage_16_mmx(data, bpl, w, h, rm, gm, bm); break; case 24: if (bpp != 32) goto generic; case 32: shade_ximage_32_mmx(data, bpl, w, h, rm, gm, bm); break; default: goto generic; } } else { generic: #endif switch (depth) { case 15: shade_ximage_15(data, bpl, w, h, rm, gm, bm, bg); break; case 16: shade_ximage_16(data, bpl, w, h, rm, gm, bm, bg); break; case 24: if (bpp != 32) { shade_ximage_24(data, bpl, w, h, rm, gm, bm, bg); break; } case 32: shade_ximage_32(data, bpl, w, h, rm, gm, bm, bg); } #ifdef USE_MMX } #endif } #ifdef USE_XLIB #ifdef USE_SHM static XImage *get_shm_image(Display * xdisplay, XShmSegmentInfo * shminfo, int x, int y, int w, int h, int depth, Pixmap pix) { XImage *ximg; shminfo->shmid = -1; shminfo->shmaddr = (char *)-1; ximg = XShmCreateImage(xdisplay, 0, depth, ZPixmap, 0, shminfo, w, h); if (!ximg) return NULL; shminfo->shmid = shmget(IPC_PRIVATE, ximg->bytes_per_line * ximg->height, IPC_CREAT | 0600); if (shminfo->shmid == -1) { XDestroyImage(ximg); return NULL; } shminfo->readOnly = False; ximg->data = shminfo->shmaddr = (char *)shmat(shminfo->shmid, 0, 0); if (shminfo->shmaddr == ((char *)-1)) { shmctl(shminfo->shmid, IPC_RMID, 0); XDestroyImage(ximg); return NULL; } XShmAttach(xdisplay, shminfo); XSync(xdisplay, False); shmctl(shminfo->shmid, IPC_RMID, 0); XShmGetImage(xdisplay, pix, ximg, x, y, AllPlanes); return ximg; } static XImage *get_image(GtkXText * xtext, Display * xdisplay, XShmSegmentInfo * shminfo, int x, int y, int w, int h, int depth, Pixmap pix) { XImage *ximg; xtext->shm = 1; ximg = get_shm_image(xdisplay, shminfo, x, y, w, h, depth, pix); if (!ximg) { xtext->shm = 0; ximg = XGetImage(xdisplay, pix, x, y, w, h, -1, ZPixmap); } return ximg; } #endif static GdkPixmap *shade_pixmap(GtkXText * xtext, Pixmap p, int x, int y, int w, int h) { unsigned int dummy, width, height, depth; int dummy_; GdkPixmap *shaded_pix; Window root; Pixmap tmp; XImage *ximg; XGCValues gcv; GC tgc; Display *xdisplay = GDK_WINDOW_XDISPLAY(xtext->draw_buf); #ifdef USE_SHM int shm_pixmaps = have_shm_pixmaps(xdisplay); #endif XGetGeometry(xdisplay, p, &root, &dummy_, &dummy_, &width, &height, &dummy, &depth); if (width < x + w || height < y + h || x < 0 || y < 0) { gcv.subwindow_mode = IncludeInferiors; gcv.graphics_exposures = False; tgc = XCreateGC(xdisplay, p, GCGraphicsExposures | GCSubwindowMode, &gcv); tmp = XCreatePixmap(xdisplay, p, w, h, depth); XSetTile(xdisplay, tgc, p); XSetFillStyle(xdisplay, tgc, FillTiled); XSetTSOrigin(xdisplay, tgc, -x, -y); XFillRectangle(xdisplay, tmp, tgc, 0, 0, w, h); XFreeGC(xdisplay, tgc); #ifdef USE_SHM if (shm_pixmaps) ximg = get_image(xtext, xdisplay, &xtext->shminfo, 0, 0, w, h, depth, tmp); else #endif ximg = XGetImage(xdisplay, tmp, 0, 0, w, h, -1, ZPixmap); XFreePixmap(xdisplay, tmp); } else { #ifdef USE_SHM if (shm_pixmaps) ximg = get_image(xtext, xdisplay, &xtext->shminfo, x, y, w, h, depth, p); else #endif ximg = XGetImage(xdisplay, p, x, y, w, h, -1, ZPixmap); } if (!ximg) return NULL; if (depth <= 14) { shade_ximage_generic(gdk_drawable_get_visual(GTK_WIDGET(xtext)->window), ximg, ximg->bytes_per_line, w, h, xtext->tint_red, xtext->tint_green, xtext->tint_blue, xtext->palette[XTEXT_BG]); } else { shade_image(gdk_drawable_get_visual(GTK_WIDGET(xtext)->window), ximg->data, ximg->bytes_per_line, ximg->bits_per_pixel, w, h, xtext->tint_red, xtext->tint_green, xtext->tint_blue, xtext->palette[XTEXT_BG], depth); } if (xtext->recycle) shaded_pix = xtext->pixmap; else { #ifdef USE_SHM if (xtext->shm && shm_pixmaps) { #if (GTK_MAJOR_VERSION == 2) && (GTK_MINOR_VERSION == 0) shaded_pix = gdk_pixmap_foreign_new(XShmCreatePixmap (xdisplay, p, ximg->data, &xtext->shminfo, w, h, depth)); #else shaded_pix = gdk_pixmap_foreign_new_for_display(gdk_drawable_get_display (xtext->draw_buf), XShmCreatePixmap(xdisplay, p, ximg->data, &xtext-> shminfo, w, h, depth)); #endif } else #endif { shaded_pix = gdk_pixmap_new(GTK_WIDGET(xtext)->window, w, h, depth); } } #ifdef USE_SHM if (!xtext->shm || !shm_pixmaps) #endif XPutImage(xdisplay, GDK_WINDOW_XWINDOW(shaded_pix), GDK_GC_XGC(xtext->fgc), ximg, 0, 0, 0, 0, w, h); XDestroyImage(ximg); return shaded_pix; } #endif /* !USE_XLIB */ /* free transparency xtext->pixmap */ #if defined(USE_XLIB) || defined(WIN32) static void gtk_xtext_free_trans(GtkXText * xtext) { if (xtext->pixmap) { #ifdef USE_SHM if (xtext->shm && have_shm_pixmaps(GDK_WINDOW_XDISPLAY(xtext->draw_buf))) { XFreePixmap(GDK_WINDOW_XDISPLAY(xtext->pixmap), GDK_WINDOW_XWINDOW(xtext->pixmap)); XShmDetach(GDK_WINDOW_XDISPLAY(xtext->draw_buf), &xtext->shminfo); shmdt(xtext->shminfo.shmaddr); } #endif g_object_unref(xtext->pixmap); xtext->pixmap = NULL; xtext->shm = 0; } } #endif /* grab pixmap from root window and set xtext->pixmap */ #if defined(USE_XLIB) || defined(WIN32) static void gtk_xtext_load_trans(GtkXText * xtext) { Pixmap rootpix; GtkWidget *widget = GTK_WIDGET(xtext); int x, y; rootpix = get_pixmap_prop(GDK_WINDOW_XDISPLAY(widget->window), GDK_WINDOW_XWINDOW(widget->window)); if (rootpix == None) { if (xtext->error_function) xtext->error_function(0); xtext->transparent = FALSE; return; } gdk_window_get_origin(widget->window, &x, &y); if (xtext->shaded) { int width, height; gdk_drawable_get_size(GTK_WIDGET(xtext)->window, &width, &height); xtext->pixmap = shade_pixmap(xtext, rootpix, x, y, width + 105, height); if (xtext->pixmap == NULL) { xtext->shaded = 0; goto noshade; } gdk_gc_set_tile(xtext->bgc, xtext->pixmap); gdk_gc_set_ts_origin(xtext->bgc, 0, 0); xtext->ts_x = xtext->ts_y = 0; } else { noshade: #if (GTK_MAJOR_VERSION == 2) && (GTK_MINOR_VERSION == 0) xtext->pixmap = gdk_pixmap_foreign_new(rootpix); #else xtext->pixmap = gdk_pixmap_foreign_new_for_display(gdk_drawable_get_display (GTK_WIDGET(xtext)->window), rootpix); #endif gdk_gc_set_tile(xtext->bgc, xtext->pixmap); gdk_gc_set_ts_origin(xtext->bgc, -x, -y); xtext->ts_x = -x; xtext->ts_y = -y; } gdk_gc_set_fill(xtext->bgc, GDK_TILED); } #endif /* ! XLIB || WIN32 */ /* walk through str until this line doesn't fit anymore */ static int find_next_wrap(GtkXText * xtext, textentry * ent, const unsigned char *str, int win_width, int indent) { const unsigned char *last_space = str; const unsigned char *orig_str = str; int str_width = indent; int mbl; int char_width; int ret; /* single liners */ if (win_width >= ent->str_width + ent->indent) return ent->str_len; /* it does happen! */ if (win_width < 1) { ret = ent->str_len - (str - ent->str); goto done; } while (1) { char_width = backend_get_char_width(xtext, str, &mbl); str_width += char_width; if (str_width > win_width) { if (xtext->wordwrap) { if (str - last_space > WORDWRAP_LIMIT) ret = str - orig_str; /* fall back to character wrap */ else { if (*last_space == ' ') last_space++; ret = last_space - orig_str; if (ret == 0) /* fall back to character wrap */ ret = str - orig_str; } goto done; } ret = str - orig_str; goto done; } /* keep a record of the last space, for wordwrapping */ if (is_del(*str)) last_space = str; /* progress to the next char */ str += mbl; if (str >= ent->str + ent->str_len) { ret = str - orig_str; goto done; } } done: /* must make progress */ if (ret < 1) ret = 1; return ret; } /* find the offset, in bytes, that wrap number 'line' starts at */ static int gtk_xtext_find_subline(GtkXText * xtext, textentry * ent, int line) { int win_width; const unsigned char *str; int indent, str_pos, line_pos, len; if (ent->lines_taken < 2 || line < 1) return 0; /* we record the first 4 lines' wraps, so take a shortcut */ if (line <= RECORD_WRAPS) return ent->wrap_offset[line - 1]; gdk_drawable_get_size(GTK_WIDGET(xtext)->window, &win_width, 0); win_width -= MARGIN; /* indent = ent->indent; str = ent->str; line_pos = str_pos = 0;*/ /* start from the last recorded wrap, and move forward */ indent = xtext->buffer->indent; str_pos = ent->wrap_offset[RECORD_WRAPS - 1]; str = str_pos + ent->str; line_pos = RECORD_WRAPS; do { len = find_next_wrap(xtext, ent, str, win_width, indent); indent = xtext->buffer->indent; str += len; str_pos += len; line_pos++; if (line_pos >= line) return str_pos; } while (str < ent->str + ent->str_len); return 0; } /* horrible hack for drawing time stamps */ static void gtk_xtext_render_stamp(GtkXText * xtext, textentry * ent, const unsigned char *text, int len, int line, int win_width) { textentry tmp_ent; int jo, ji, hs; int xsize, y; /* trashing ent here, so make a backup first */ memcpy(&tmp_ent, ent, sizeof(tmp_ent)); ent->mb = TRUE; /* make non-english days of the week work */ jo = xtext->jump_out_offset; /* back these up */ ji = xtext->jump_in_offset; hs = xtext->hilight_start; xtext->jump_out_offset = 0; xtext->jump_in_offset = 0; xtext->hilight_start = 0xffff; /* temp disable */ if (xtext->mark_stamp) { /* if this line is marked, mark this stamp too */ if (ent->mark_start == 0) { ent->mark_start = 0; ent->mark_end = len; } else { ent->mark_start = -1; ent->mark_end = -1; } ent->str = text; } y = (xtext->fontsize * line) + xtext->font->ascent - xtext->pixel_offset; gtk_xtext_render_str(xtext, y, ent, text, NULL, len, win_width, 2, line, TRUE, &xsize); /* restore everything back to how it was */ memcpy(ent, &tmp_ent, sizeof(tmp_ent)); xtext->jump_out_offset = jo; xtext->jump_in_offset = ji; xtext->hilight_start = hs; /* with a non-fixed-width font, sometimes we don't draw enough background i.e. when this stamp is shorter than xtext->stamp_width */ xsize += MARGIN; if (xsize < xtext->stamp_width) { y -= xtext->font->ascent; xtext_draw_bg(xtext, xsize, /* x */ y, /* y */ xtext->stamp_width - xsize, /* width */ xtext->fontsize /* height */ ); } } /* render a single line, which may wrap to more lines */ static int gtk_xtext_render_line(GtkXText * xtext, textentry * ent, int line, int lines_max, int subline, int win_width) { const unsigned char *str; fstr_attr_t *attr; int indent, taken, entline, len, y, start_subline; entline = taken = 0; str = ent->str; attr = ent->fstr->attr; indent = ent->indent; start_subline = subline; #if 1 /* draw the timestamp */ if (xtext->auto_indent && xtext->buffer->time_stamp && (!xtext->skip_stamp || xtext->mark_stamp || xtext->force_stamp)) { const char *ts = timestamp_time("%H:%M:%S", ent->fstr->ts); int len = xstrlen(ts); gtk_xtext_render_stamp(xtext, ent, ts, len, line, win_width); } #endif /* draw each line one by one */ do { /* if it's one of the first 4 wraps, we don't need to calculate it, it's recorded in ->wrap_offset. This saves us a loop. */ if (entline < RECORD_WRAPS) { if (ent->lines_taken < 2) len = ent->str_len; else { if (entline > 0) len = ent->wrap_offset[entline] - ent->wrap_offset[entline - 1]; else len = ent->wrap_offset[0]; } } else len = find_next_wrap(xtext, ent, str, win_width, indent); entline++; y = (xtext->fontsize * line) + xtext->font->ascent - xtext->pixel_offset; if (!subline) { if (!gtk_xtext_render_str(xtext, y, ent, str, attr, len, win_width, indent, line, FALSE, NULL)) { /* small optimization */ gtk_xtext_draw_marker(xtext, ent, y - xtext->fontsize * (taken + start_subline + 1)); return ent->lines_taken - subline; } } else { xtext->dont_render = TRUE; gtk_xtext_render_str(xtext, y, ent, str, attr, len, win_width, indent, line, FALSE, NULL); xtext->dont_render = FALSE; subline--; line--; taken--; } indent = xtext->buffer->indent; line++; taken++; str += len; attr += len; if (line >= lines_max) break; } while (str < ent->str + ent->str_len); gtk_xtext_draw_marker(xtext, ent, y - xtext->fontsize * (taken + start_subline)); return taken; } void gtk_xtext_set_palette(GtkXText * xtext, GdkColor palette[]) { int i; GdkColor col; for (i = (XTEXT_COLS - 1); i >= 0; i--) { #ifdef USE_XFT xtext->color[i].color.red = palette[i].red; xtext->color[i].color.green = palette[i].green; xtext->color[i].color.blue = palette[i].blue; xtext->color[i].color.alpha = 0xffff; xtext->color[i].pixel = palette[i].pixel; #endif xtext->palette[i] = palette[i].pixel; } if (GTK_WIDGET_REALIZED(xtext)) { xtext_set_fg(xtext, xtext->fgc, XTEXT_FG); xtext_set_bg(xtext, xtext->fgc, XTEXT_BG); xtext_set_fg(xtext, xtext->bgc, XTEXT_BG); col.pixel = xtext->palette[XTEXT_MARKER]; gdk_gc_set_foreground(xtext->marker_gc, &col); } xtext->col_fore = XTEXT_FG; xtext->col_back = XTEXT_BG; } static void gtk_xtext_fix_indent(xtext_buffer * buf) { int j; /* make indent a multiple of the space width */ if (buf->indent && buf->xtext->space_width) { j = 0; while (j < buf->indent) { j += buf->xtext->space_width; } buf->indent = j; } dontscroll(buf); /* force scrolling off */ } static void gtk_xtext_recalc_widths(xtext_buffer * buf, int do_str_width) { textentry *ent; /* since we have a new font, we have to recalc the text widths */ for (ent = buf->text_first; ent; ent = ent->next) { if (do_str_width) { ent->str_width = gtk_xtext_text_width(buf->xtext, ent->str, ent->str_len, NULL); } if (ent->left_len != -1) { ent->indent = (buf->indent - gtk_xtext_text_width(buf->xtext, ent->str, ent->left_len, NULL)) - buf->xtext->space_width; if (ent->indent < MARGIN) ent->indent = MARGIN; } } gtk_xtext_calc_lines(buf, FALSE); } int gtk_xtext_set_font(GtkXText * xtext, char *name) { int i; unsigned char c; if (xtext->font) backend_font_close(xtext); /* realize now, so that font_open has a XDisplay */ gtk_widget_realize(GTK_WIDGET(xtext)); backend_font_open(xtext, name); if (xtext->font == NULL) return FALSE; /* measure the width of every char; only the ASCII ones for XFT */ for (i = 0; i < sizeof(xtext->fontwidth) / sizeof(xtext->fontwidth[0]); i++) { c = i; xtext->fontwidth[i] = backend_get_text_width(xtext, &c, 1, TRUE); } xtext->space_width = xtext->fontwidth[' ']; xtext->fontsize = xtext->font->ascent + xtext->font->descent; #if 1 { const char *time_str = timestamp("%H:%M:%S"); int stamp_size = xstrlen(time_str); xtext->stamp_width = gtk_xtext_text_width(xtext, time_str, stamp_size, NULL) + MARGIN; } #endif gtk_xtext_fix_indent(xtext->buffer); if (GTK_WIDGET_REALIZED(xtext)) gtk_xtext_recalc_widths(xtext->buffer, TRUE); return TRUE; } void gtk_xtext_set_background(GtkXText * xtext, GdkPixmap * pixmap, gboolean trans) { GdkGCValues val; gboolean shaded = FALSE; if (trans && (xtext->tint_red != 255 || xtext->tint_green != 255 || xtext->tint_blue != 255)) shaded = TRUE; #if !defined(USE_XLIB) && !defined(WIN32) shaded = FALSE; trans = FALSE; #endif if (xtext->pixmap) { #if defined(USE_XLIB) || defined(WIN32) if (xtext->transparent) gtk_xtext_free_trans(xtext); else #endif g_object_unref(xtext->pixmap); xtext->pixmap = NULL; } xtext->transparent = trans; #if defined(USE_XLIB) || defined(WIN32) if (trans) { xtext->shaded = shaded; if (GTK_WIDGET_REALIZED(xtext)) gtk_xtext_load_trans(xtext); return; } #endif dontscroll(xtext->buffer); xtext->pixmap = pixmap; if (pixmap != 0) { g_object_ref(pixmap); if (GTK_WIDGET_REALIZED(xtext)) { gdk_gc_set_tile(xtext->bgc, pixmap); gdk_gc_set_ts_origin(xtext->bgc, 0, 0); xtext->ts_x = xtext->ts_y = 0; gdk_gc_set_fill(xtext->bgc, GDK_TILED); } } else if (GTK_WIDGET_REALIZED(xtext)) { g_object_unref(xtext->bgc); val.subwindow_mode = GDK_INCLUDE_INFERIORS; val.graphics_exposures = 0; xtext->bgc = gdk_gc_new_with_values(GTK_WIDGET(xtext)->window, &val, GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW); xtext_set_fg(xtext, xtext->bgc, XTEXT_BG); } } /* count how many lines 'ent' will take (with wraps) */ static int gtk_xtext_lines_taken(xtext_buffer * buf, textentry * ent) { const unsigned char *str; int indent, taken, len; int win_width; win_width = buf->window_width - MARGIN; if (ent->str_width + ent->indent < win_width) return 1; indent = ent->indent; str = ent->str; taken = 0; do { len = find_next_wrap(buf->xtext, ent, str, win_width, indent); if (taken < RECORD_WRAPS) ent->wrap_offset[taken] = (str + len) - ent->str; indent = buf->indent; taken++; str += len; } while (str < ent->str + ent->str_len); return taken; } /* Calculate number of actual lines (with wraps), to set adj->lower. * * This should only be called when the window resizes. */ static void gtk_xtext_calc_lines(xtext_buffer * buf, int fire_signal) { textentry *ent; int width; int height; int lines; gdk_drawable_get_size(GTK_WIDGET(buf->xtext)->window, &width, &height); width -= MARGIN; if (width < 30 || height < buf->xtext->fontsize || width < buf->indent + 30) return; lines = 0; for (ent = buf->text_first; ent; ent = ent->next) { ent->lines_taken = gtk_xtext_lines_taken(buf, ent); lines += ent->lines_taken; } buf->pagetop_ent = NULL; buf->num_lines = lines; gtk_xtext_adjustment_set(buf, fire_signal); } /* find the n-th line in the linked list, this includes wrap calculations */ static textentry *gtk_xtext_nth(GtkXText * xtext, int line, int *subline) { int lines = 0; textentry *ent; ent = xtext->buffer->text_first; /* -- optimization -- try to make a short-cut using the pagetop ent */ if (xtext->buffer->pagetop_ent) { if (line == xtext->buffer->pagetop_line) { *subline = xtext->buffer->pagetop_subline; return xtext->buffer->pagetop_ent; } if (line > xtext->buffer->pagetop_line) { /* lets start from the pagetop instead of the absolute beginning */ ent = xtext->buffer->pagetop_ent; lines = xtext->buffer->pagetop_line - xtext->buffer->pagetop_subline; } else if (line > xtext->buffer->pagetop_line - line) { /* move backwards from pagetop */ ent = xtext->buffer->pagetop_ent; lines = xtext->buffer->pagetop_line - xtext->buffer->pagetop_subline; while (1) { if (lines <= line) { *subline = line - lines; return ent; } ent = ent->prev; if (!ent) break; lines -= ent->lines_taken; } return NULL; } } /* -- end of optimization -- */ while (ent) { lines += ent->lines_taken; if (lines > line) { *subline = ent->lines_taken - (lines - line); return ent; } ent = ent->next; } return NULL; } /* render enta (or an inclusive range enta->entb) */ static int gtk_xtext_render_ents(GtkXText * xtext, textentry * enta, textentry * entb) { textentry *ent, *orig_ent, *tmp_ent; int line; int lines_max; int width; int height; int subline; int drawing = FALSE; if (xtext->buffer->indent < MARGIN) xtext->buffer->indent = MARGIN; /* 2 pixels is our left margin */ gdk_drawable_get_size(GTK_WIDGET(xtext)->window, &width, &height); width -= MARGIN; if (width < 32 || height < xtext->fontsize || width < xtext->buffer->indent + 30) return 0; lines_max = ((height + xtext->pixel_offset) / xtext->fontsize) + 1; line = 0; orig_ent = xtext->buffer->pagetop_ent; subline = xtext->buffer->pagetop_subline; /* used before a complete page is in buffer */ if (orig_ent == NULL) orig_ent = xtext->buffer->text_first; /* check if enta is before the start of this page */ if (entb) { for (tmp_ent = orig_ent; tmp_ent; tmp_ent = tmp_ent->next) { if (tmp_ent == enta) break; if (tmp_ent == entb) { drawing = TRUE; break; } } } for (ent = orig_ent; ent; ent = ent->next) { if (entb && ent == enta) drawing = TRUE; if (drawing || ent == entb || ent == enta) { gtk_xtext_reset(xtext, FALSE, TRUE); line += gtk_xtext_render_line(xtext, ent, line, lines_max, subline, width); subline = 0; xtext->jump_in_offset = 0; /* jump_in_offset only for the 1st */ } else { if (ent == orig_ent) { line -= subline; subline = 0; } line += ent->lines_taken; } if (ent == entb) break; if (line >= lines_max) break; } /* space below last line */ return (xtext->fontsize * line) - xtext->pixel_offset; } /* render a whole page/window, starting from 'startline' */ static void gtk_xtext_render_page(GtkXText * xtext) { textentry *ent; int line; int lines_max; int width; int height; int subline; int startline = xtext->adj->value; if (!GTK_WIDGET_REALIZED(xtext)) return; if (xtext->buffer->indent < MARGIN) xtext->buffer->indent = MARGIN; /* 2 pixels is our left margin */ gdk_drawable_get_size(GTK_WIDGET(xtext)->window, &width, &height); if (width < 34 || height < xtext->fontsize || width < xtext->buffer->indent + 32) return; #ifdef SMOOTH_SCROLL xtext->pixel_offset = (xtext->adj->value - startline) * xtext->fontsize; #else xtext->pixel_offset = 0; #endif subline = line = 0; ent = xtext->buffer->text_first; if (startline > 0) ent = gtk_xtext_nth(xtext, startline, &subline); xtext->buffer->pagetop_ent = ent; xtext->buffer->pagetop_subline = subline; xtext->buffer->pagetop_line = startline; #ifdef SCROLL_HACK { int pos, overlap; GdkRectangle area; if (xtext->buffer->num_lines <= xtext->adj->page_size) dontscroll(xtext->buffer); #ifdef SMOOTH_SCROLL pos = xtext->adj->value * xtext->fontsize; #else pos = startline * xtext->fontsize; #endif overlap = xtext->buffer->last_pixel_pos - pos; xtext->buffer->last_pixel_pos = pos; #ifdef USE_DB if (!xtext->pixmap && abs(overlap) < height) #else /* dont scroll PageUp/Down without a DB, it looks ugly */ if (!xtext->pixmap && abs(overlap) < height - (3 * xtext->fontsize)) #endif { /* so the obscured regions are exposed */ gdk_gc_set_exposures(xtext->fgc, TRUE); if (overlap < 1) { /* DOWN */ int remainder; gdk_draw_drawable(xtext->draw_buf, xtext->fgc, xtext->draw_buf, 0, -overlap, 0, 0, width, height + overlap); remainder = ((height - xtext->font->descent) % xtext->fontsize) + xtext->font->descent; area.y = (height + overlap) - remainder; area.height = remainder - overlap; } else { gdk_draw_drawable(xtext->draw_buf, xtext->fgc, xtext->draw_buf, 0, 0, 0, overlap, width, height - overlap); area.y = 0; area.height = overlap; } gdk_gc_set_exposures(xtext->fgc, FALSE); if (area.height > 0) { area.x = 0; area.width = width; gtk_xtext_paint(GTK_WIDGET(xtext), &area); } xtext->buffer->grid_dirty = TRUE; return; } } #endif xtext->buffer->grid_dirty = FALSE; width -= MARGIN; lines_max = ((height + xtext->pixel_offset) / xtext->fontsize) + 1; while (ent) { gtk_xtext_reset(xtext, FALSE, TRUE); line += gtk_xtext_render_line(xtext, ent, line, lines_max, subline, width); subline = 0; if (line >= lines_max) break; ent = ent->next; } line = (xtext->fontsize * line) - xtext->pixel_offset; /* fill any space below the last line with our background GC */ xtext_draw_bg(xtext, 0, line, width + MARGIN, height - line); /* draw the separator line */ gtk_xtext_draw_sep(xtext, -1); } void gtk_xtext_refresh(GtkXText * xtext, int do_trans) { if (GTK_WIDGET_REALIZED(GTK_WIDGET(xtext))) { #if defined(USE_XLIB) || defined(WIN32) if (xtext->transparent && do_trans) { gtk_xtext_free_trans(xtext); gtk_xtext_load_trans(xtext); } #endif gtk_xtext_render_page(xtext); } } static gboolean gtk_xtext_check_ent_visibility(GtkXText * xtext, textentry * find_ent, int add); static int gtk_xtext_render_page_timeout(GtkXText * xtext); /* remove the topline from the list */ static void gtk_xtext_remove_top(xtext_buffer * buffer) { int visible; textentry *ent; ent = buffer->text_first; if (!ent) return; buffer->num_lines -= ent->lines_taken; buffer->pagetop_line -= ent->lines_taken; buffer->last_pixel_pos -= (ent->lines_taken * buffer->xtext->fontsize); buffer->text_first = ent->next; buffer->text_first->prev = NULL; buffer->old_value -= ent->lines_taken; if (buffer->xtext->buffer == buffer) { /* is it the current buffer? */ buffer->xtext->adj->value -= ent->lines_taken; buffer->xtext->select_start_adj -= ent->lines_taken; } visible = buffer->xtext->buffer == buffer && ent == buffer->pagetop_ent; if (ent == buffer->pagetop_ent) buffer->pagetop_ent = NULL; if (ent == buffer->last_ent_start) buffer->last_ent_start = ent->next; if (ent == buffer->last_ent_end) { buffer->last_ent_start = NULL; buffer->last_ent_end = NULL; } if (buffer->marker_pos == ent) buffer->marker_pos = NULL; free(ent); if (visible) { if (!buffer->xtext->add_io_tag) { /* remove scrolling events */ if (buffer->xtext->io_tag) { g_source_remove(buffer->xtext->io_tag); buffer->xtext->io_tag = 0; } buffer->xtext->force_render = TRUE; buffer->xtext->add_io_tag = g_timeout_add(REFRESH_TIMEOUT * 2, (GSourceFunc) gtk_xtext_render_page_timeout, buffer->xtext); } } } void gtk_xtext_clear(xtext_buffer * buf) { textentry *next; if (buf->xtext->auto_indent) buf->indent = MARGIN; buf->scrollbar_down = TRUE; buf->last_ent_start = NULL; buf->last_ent_end = NULL; buf->marker_pos = NULL; dontscroll(buf); while (buf->text_first) { next = buf->text_first->next; free(buf->text_first); buf->text_first = next; } buf->text_last = NULL; if (buf->xtext->buffer == buf) { gtk_xtext_calc_lines(buf, TRUE); gtk_xtext_refresh(buf->xtext, 0); } else { gtk_xtext_calc_lines(buf, FALSE); } } static gboolean gtk_xtext_check_ent_visibility(GtkXText * xtext, textentry * find_ent, int add) { textentry *ent; int lines_max; int line = 0; int width; int height; gdk_drawable_get_size(GTK_WIDGET(xtext)->window, &width, &height); lines_max = ((height + xtext->pixel_offset) / xtext->fontsize) + add; ent = xtext->buffer->pagetop_ent; while (ent && line < lines_max) { if (find_ent == ent) return TRUE; line += ent->lines_taken; ent = ent->next; } return FALSE; } void gtk_xtext_check_marker_visibility(GtkXText * xtext) { if (gtk_xtext_check_ent_visibility(xtext, xtext->buffer->marker_pos, 1)) xtext->buffer->marker_seen = TRUE; } static int gtk_xtext_render_page_timeout(GtkXText * xtext) { GtkAdjustment *adj = xtext->adj; xtext->add_io_tag = 0; /* less than a complete page? */ if (xtext->buffer->num_lines <= adj->page_size) { xtext->buffer->old_value = 0; adj->value = 0; gtk_xtext_render_page(xtext); } else if (xtext->buffer->scrollbar_down) { g_signal_handler_block(xtext->adj, xtext->vc_signal_tag); gtk_xtext_adjustment_set(xtext->buffer, FALSE); gtk_adjustment_set_value(adj, adj->upper - adj->page_size); g_signal_handler_unblock(xtext->adj, xtext->vc_signal_tag); xtext->buffer->old_value = adj->value; gtk_xtext_render_page(xtext); } else { gtk_xtext_adjustment_set(xtext->buffer, TRUE); if (xtext->force_render) { xtext->force_render = FALSE; gtk_xtext_render_page(xtext); } } return 0; } /* append a textentry to our linked list */ static void gtk_xtext_append_entry(xtext_buffer * buf, textentry * ent) { int mb; /* xchat->ekg2, note: i removed here strtr(ent->str, '\t', ' ') * coz we should pass here only fstring_t, where fstring_t can't have \t */ ent->str_width = gtk_xtext_text_width(buf->xtext, ent->str, ent->str_len, &mb); ent->mb = FALSE; if (mb) ent->mb = TRUE; ent->mark_start = -1; ent->mark_end = -1; ent->next = NULL; if (ent->indent < MARGIN) ent->indent = MARGIN; /* 2 pixels is the left margin */ /* append to our linked list */ if (buf->text_last) buf->text_last->next = ent; else buf->text_first = ent; ent->prev = buf->text_last; buf->text_last = ent; ent->lines_taken = gtk_xtext_lines_taken(buf, ent); buf->num_lines += ent->lines_taken; if (buf->reset_marker_pos || ((buf->marker_pos == NULL || buf->marker_seen) && (buf->xtext->buffer != buf || #if GTK_CHECK_VERSION(2,4,0) !gtk_window_has_toplevel_focus (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET(buf->xtext)))) #else !(GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET(buf->xtext))))-> has_focus #endif ))) { buf->marker_pos = ent; dontscroll(buf); /* force scrolling off */ buf->marker_seen = FALSE; buf->reset_marker_pos = FALSE; } if (buf->xtext->max_lines > 2 && buf->xtext->max_lines < buf->num_lines) { gtk_xtext_remove_top(buf); } if (buf->xtext->buffer == buf) { #ifdef SCROLL_HACK /* this could be improved */ if ((buf->num_lines - 1) <= buf->xtext->adj->page_size) dontscroll(buf); #endif if (!buf->xtext->add_io_tag) { /* remove scrolling events */ if (buf->xtext->io_tag) { g_source_remove(buf->xtext->io_tag); buf->xtext->io_tag = 0; } buf->xtext->add_io_tag = g_timeout_add(REFRESH_TIMEOUT * 2, (GSourceFunc) gtk_xtext_render_page_timeout, buf->xtext); } } else if (buf->scrollbar_down) { buf->old_value = buf->num_lines - buf->xtext->adj->page_size; if (buf->old_value < 0) buf->old_value = 0; } } /* XXX: gtk_xtext_append(), gtk_xtext_append_indent() */ void gtk_xtext_append_fstring(xtext_buffer *buf, const fstring_t *fstr) { textentry *ent; int space; int tempindent; size_t len = xstrlen(fstr->str); if (len >= sizeof(buf->xtext->scratch_buffer)) len = sizeof(buf->xtext->scratch_buffer) - 1; ent = xmalloc(sizeof(textentry)); ent->fstr = fstring_dup(fstr); /* NOTE, xchat create new string with str[0] = ' ' str[1...] = str[0...] * i don't know why, but without it, ui looks ugly. * * slowdown, hack. */ ent->fstr->str = xrealloc(ent->fstr->str, sizeof(char) * (len+2)); memmove(ent->fstr->str+1, ent->fstr->str, len); ent->fstr->str[0] = ' '; ent->fstr->str[len+1] = '\0'; ent->fstr->attr = xrealloc(ent->fstr->attr, sizeof(fstr_attr_t) * (len+1)); memmove(ent->fstr->attr+1, ent->fstr->attr, len*sizeof(fstr_attr_t)); ent->fstr->attr[0] = FSTR_NORMAL; ent->left_len = 0; ent->str = ent->fstr->str; ent->str_len = len+1; ent->indent = (buf->indent) - buf->xtext->space_width; if (buf->time_stamp) space = buf->xtext->stamp_width; else space = 0; /* do we need to auto adjust the separator position? */ if (buf->xtext->auto_indent && ent->indent < MARGIN + space) { tempindent = MARGIN + space + buf->xtext->space_width; if (tempindent > buf->indent) buf->indent = tempindent; if (buf->indent > buf->xtext->max_auto_indent) buf->indent = buf->xtext->max_auto_indent; gtk_xtext_fix_indent(buf); gtk_xtext_recalc_widths(buf, FALSE); ent->indent = (buf->indent) - buf->xtext->space_width; buf->xtext->force_render = TRUE; } gtk_xtext_append_entry(buf, ent); } void gtk_xtext_set_error_function(GtkXText * xtext, void (*error_function) (int)) { xtext->error_function = error_function; } void gtk_xtext_set_indent(GtkXText * xtext, gboolean indent) { xtext->auto_indent = indent; } void gtk_xtext_set_max_indent(GtkXText * xtext, int max_auto_indent) { xtext->max_auto_indent = max_auto_indent; } void gtk_xtext_set_max_lines(GtkXText * xtext, int max_lines) { xtext->max_lines = max_lines; } void gtk_xtext_set_show_marker(GtkXText * xtext, gboolean show_marker) { xtext->marker = show_marker; } void gtk_xtext_set_show_separator(GtkXText * xtext, gboolean show_separator) { xtext->separator = show_separator; } void gtk_xtext_set_thin_separator(GtkXText * xtext, gboolean thin_separator) { xtext->thinline = thin_separator; } void gtk_xtext_set_time_stamp(xtext_buffer * buf, gboolean time_stamp) { buf->time_stamp = time_stamp; } void gtk_xtext_set_tint(GtkXText * xtext, int tint_red, int tint_green, int tint_blue) { xtext->tint_red = tint_red; xtext->tint_green = tint_green; xtext->tint_blue = tint_blue; /*if (xtext->tint_red != 255 || xtext->tint_green != 255 || xtext->tint_blue != 255) shaded = TRUE; */ } void gtk_xtext_set_urlcheck_function(GtkXText * xtext, int (*urlcheck_function) (GtkWidget *, char *, int)) { xtext->urlcheck_function = urlcheck_function; } void gtk_xtext_set_wordwrap(GtkXText * xtext, gboolean wordwrap) { xtext->wordwrap = wordwrap; } void gtk_xtext_reset_marker_pos(GtkXText * xtext) { xtext->buffer->marker_pos = NULL; dontscroll(xtext->buffer); /* force scrolling off */ gtk_xtext_render_page(xtext); xtext->buffer->reset_marker_pos = TRUE; } void gtk_xtext_buffer_show(GtkXText * xtext, xtext_buffer * buf, int render) { int w, h; buf->xtext = xtext; if (xtext->buffer == buf) return; /*printf("text_buffer_show: xtext=%p buffer=%p\n", xtext, buf);*/ if (xtext->add_io_tag) { g_source_remove(xtext->add_io_tag); xtext->add_io_tag = 0; } if (xtext->io_tag) { g_source_remove(xtext->io_tag); xtext->io_tag = 0; } if (!GTK_WIDGET_REALIZED(GTK_WIDGET(xtext))) gtk_widget_realize(GTK_WIDGET(xtext)); gdk_drawable_get_size(GTK_WIDGET(xtext)->window, &w, &h); /* after a font change */ if (buf->needs_recalc) { buf->needs_recalc = FALSE; gtk_xtext_recalc_widths(buf, TRUE); } /* now change to the new buffer */ xtext->buffer = buf; dontscroll(buf); /* force scrolling off */ xtext->adj->value = buf->old_value; xtext->adj->upper = buf->num_lines; if (xtext->adj->upper == 0) { xtext->adj->upper = 1; /* sanity check */ } else if (xtext->adj->value > xtext->adj->upper - xtext->adj->page_size) { /*buf->pagetop_ent = NULL; */ xtext->adj->value = xtext->adj->upper - xtext->adj->page_size; if (xtext->adj->value < 0) xtext->adj->value = 0; } if (render) { /* did the window change size since this buffer was last shown? */ if (buf->window_width != w) { buf->window_width = w; gtk_xtext_calc_lines(buf, FALSE); if (buf->scrollbar_down) gtk_adjustment_set_value(xtext->adj, xtext->adj->upper - xtext->adj->page_size); } else if (buf->window_height != h) { buf->window_height = h; buf->pagetop_ent = NULL; gtk_xtext_adjustment_set(buf, FALSE); } gtk_xtext_render_page(xtext); gtk_adjustment_changed(xtext->adj); } else { /* avoid redoing the transparency */ xtext->avoid_trans = TRUE; } } xtext_buffer *gtk_xtext_buffer_new(GtkXText * xtext) { xtext_buffer *buf = xmalloc(sizeof(xtext_buffer)); buf->old_value = -1; buf->xtext = xtext; buf->scrollbar_down = TRUE; buf->indent = xtext->space_width * 2; dontscroll(buf); return buf; } void gtk_xtext_buffer_free(xtext_buffer * buf) { textentry *ent; if (buf->xtext->buffer == buf) buf->xtext->buffer = buf->xtext->orig_buffer; if (buf->xtext->selection_buffer == buf) buf->xtext->selection_buffer = NULL; for (ent = buf->text_first; ent;) { textentry *next = ent->next; free(ent); /* XXX, fstring_t */ ent = next; } free(buf); } /* gtk_xtext_search() XXX, lastlog */ /* gtk_xtext_save() XXX? */ /* gtk_xtext_lastlog() XXX */ /* gtk_xtext_foreach() */ /* gtk_xtext_is_empty() */ ekg2-0.4~pre+20120506.1/plugins/gtk/xtext.h000066400000000000000000000157331175142753400177610ustar00rootroot00000000000000#ifndef __XTEXT_H__ #define __XTEXT_H__ #include #ifdef USE_XFT #include #endif #ifdef USE_SHM #include #include #include #include #endif #define GTK_TYPE_XTEXT (gtk_xtext_get_type ()) #define GTK_XTEXT(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GTK_TYPE_XTEXT, GtkXText)) #define GTK_XTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_XTEXT, GtkXTextClass)) #define GTK_IS_XTEXT(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GTK_TYPE_XTEXT)) #define GTK_IS_XTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_XTEXT)) #define GTK_XTEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_XTEXT, GtkXTextClass)) /* these match palette.h */ #define XTEXT_MIRC_COLS 32 #define XTEXT_COLS 37 /* 32 plus 5 for extra stuff below */ #define XTEXT_MARK_FG 32 /* for marking text */ #define XTEXT_MARK_BG 33 #define XTEXT_FG 34 #define XTEXT_BG 35 #define XTEXT_MARKER 36 /* for marker line */ typedef struct _GtkXText GtkXText; typedef struct _GtkXTextClass GtkXTextClass; typedef struct textentry textentry; typedef struct { GtkXText *xtext; /* attached to this widget */ gfloat old_value; /* last known adj->value */ textentry *text_first; textentry *text_last; guint16 grid_offset[256]; textentry *last_ent_start; /* this basically describes the last rendered */ textentry *last_ent_end; /* selection. */ int last_offset_start; int last_offset_end; int last_pixel_pos; int pagetop_line; int pagetop_subline; textentry *pagetop_ent; /* what's at xtext->adj->value */ int num_lines; int indent; /* position of separator (pixels) from left */ textentry *marker_pos; int window_width; /* window size when last rendered. */ int window_height; unsigned int time_stamp:1; unsigned int scrollbar_down:1; unsigned int needs_recalc:1; unsigned int grid_dirty:1; unsigned int marker_seen:1; unsigned int reset_marker_pos:1; } xtext_buffer; struct _GtkXText { GtkWidget widget; xtext_buffer *buffer; xtext_buffer *orig_buffer; xtext_buffer *selection_buffer; #ifdef USE_SHM XShmSegmentInfo shminfo; #endif GtkAdjustment *adj; GdkPixmap *pixmap; /* 0 = use palette[19] */ GdkDrawable *draw_buf; /* points to ->window */ GdkCursor *hand_cursor; GdkCursor *resize_cursor; int pixel_offset; /* amount of pixels the top line is chopped by */ int last_win_x; int last_win_y; int last_win_h; int last_win_w; int tint_red; int tint_green; int tint_blue; GdkGC *bgc; /* backing pixmap */ GdkGC *fgc; /* text foreground color */ GdkGC *light_gc; /* sep bar */ GdkGC *dark_gc; GdkGC *thin_gc; GdkGC *marker_gc; gulong palette[XTEXT_COLS]; gint io_tag; /* for delayed refresh events */ gint add_io_tag; /* "" when adding new text */ gint scroll_tag; /* marking-scroll timeout */ gulong vc_signal_tag; /* signal handler for "value_changed" adj */ int select_start_adj; /* the adj->value when the selection started */ int select_start_x; int select_start_y; int select_end_x; int select_end_y; int max_lines; int col_fore; int col_back; int depth; /* gdk window depth */ textentry *hilight_ent; int hilight_start; int hilight_end; guint16 fontwidth[128]; /* each char's width, only the ASCII ones */ #ifdef USE_XFT XftColor color[XTEXT_COLS]; XftColor *xft_fg; XftColor *xft_bg; /* both point into color[20] */ XftDraw *xftdraw; XftFont *font; XftFont *ifont; /* italics */ #else struct pangofont { PangoFontDescription *font; PangoFontDescription *ifont; /* italics */ int ascent; int descent; } *font, pango_font; PangoLayout *layout; #endif int fontsize; int space_width; /* width (pixels) of the space " " character */ int stamp_width; /* width of "[88:88:88]" */ int max_auto_indent; unsigned char scratch_buffer[4096]; void (*error_function) (int type); int (*urlcheck_function) (GtkWidget *xtext, char *word, int len); int jump_out_offset; /* point at which to stop rendering */ int jump_in_offset; /* "" start rendering */ int ts_x; /* ts origin for ->bgc GC */ int ts_y; int clip_x; /* clipping (x directions) */ int clip_x2; /* from x to x2 */ int clip_y; /* clipping (y directions) */ int clip_y2; /* from y to y2 */ /* current text states */ unsigned int bold:1; unsigned int underline:1; unsigned int italics:1; /* text parsing states */ unsigned int backcolor:1; /* various state information */ unsigned int moving_separator:1; unsigned int word_or_line_select:1; unsigned int button_down:1; unsigned int hilighting:1; unsigned int dont_render:1; unsigned int dont_render2:1; unsigned int cursor_hand:1; unsigned int cursor_resize:1; unsigned int skip_border_fills:1; unsigned int skip_stamp:1; unsigned int mark_stamp:1; /* Cut&Paste with stamps? */ unsigned int force_stamp:1; /* force redrawing it */ unsigned int render_hilights_only:1; unsigned int in_hilight:1; unsigned int un_hilight:1; unsigned int recycle:1; unsigned int avoid_trans:1; unsigned int force_render:1; unsigned int shm:1; /* settings/prefs */ unsigned int auto_indent:1; unsigned int thinline:1; unsigned int transparent:1; unsigned int shaded:1; unsigned int marker:1; unsigned int separator:1; unsigned int wordwrap:1; unsigned int overdraw:1; }; struct _GtkXTextClass { GtkWidgetClass parent_class; void (*word_click) (GtkXText * xtext, char *word, GdkEventButton * event); }; GtkWidget *gtk_xtext_new(GdkColor palette[], int separator); void gtk_xtext_append_fstring(xtext_buffer *buf, const fstring_t *fstr); int gtk_xtext_set_font(GtkXText * xtext, char *name); void gtk_xtext_set_background(GtkXText * xtext, GdkPixmap * pixmap, gboolean trans); void gtk_xtext_set_palette(GtkXText * xtext, GdkColor palette[]); void gtk_xtext_clear(xtext_buffer * buf); void gtk_xtext_refresh(GtkXText * xtext, int do_trans); void gtk_xtext_reset_marker_pos(GtkXText * xtext); typedef void (*GtkXTextForeach) (GtkXText * xtext, unsigned char *text, void *data); void gtk_xtext_set_error_function(GtkXText * xtext, void (*error_function) (int)); void gtk_xtext_set_indent(GtkXText * xtext, gboolean indent); void gtk_xtext_set_max_indent(GtkXText * xtext, int max_auto_indent); void gtk_xtext_set_max_lines(GtkXText * xtext, int max_lines); void gtk_xtext_set_show_marker(GtkXText * xtext, gboolean show_marker); void gtk_xtext_set_show_separator(GtkXText * xtext, gboolean show_separator); void gtk_xtext_set_thin_separator(GtkXText * xtext, gboolean thin_separator); void gtk_xtext_set_time_stamp(xtext_buffer * buf, gboolean timestamp); void gtk_xtext_set_tint(GtkXText * xtext, int tint_red, int tint_green, int tint_blue); void gtk_xtext_set_urlcheck_function(GtkXText * xtext, int (*urlcheck_function) (GtkWidget *, char *, int)); void gtk_xtext_set_wordwrap(GtkXText * xtext, gboolean word_wrap); xtext_buffer *gtk_xtext_buffer_new(GtkXText * xtext); void gtk_xtext_buffer_free(xtext_buffer * buf); void gtk_xtext_buffer_show(GtkXText * xtext, xtext_buffer * buf, int render); GType gtk_xtext_get_type(void); #endif ekg2-0.4~pre+20120506.1/plugins/httprc_xajax/000077500000000000000000000000001175142753400203355ustar00rootroot00000000000000ekg2-0.4~pre+20120506.1/plugins/httprc_xajax/ChangeLog000066400000000000000000000007051175142753400221110ustar00rootroot0000000000000019:35:24 CET 2oo6-o4-12 ======================= * some changes before commiting this stuff to cvs [on dj's request] * window kill and window new handling o1:o7:38 CEST 2oo6-23-o9 * nice chaning windows 19:45:22 CEST 2oo6-22-o9 ======================== After taking code from darkjames: * added xajax javascript engine * added some basic cookie support * connected different queries to default handler * I had to remove timestamp and colours ekg2-0.4~pre+20120506.1/plugins/httprc_xajax/README000066400000000000000000000040651175142753400212220ustar00rootroot00000000000000This is experimental http remote control plugin for modern browser with some basic ajax support There are some flaws in desing of EKG2 that makes makes some thing quite problematic, but I'm not going to hack them around. design note: + I'm using ajax.js from xajax, I've taken it from xajax 0.2.4, it's messy since it was compressed (?) + There is simple query parsing cookie setting + I'm not using http_fstring at all since it'd destroy xhtml dom structure of document. It would have to be rewritten using functions from xajax.js + If there is GET /ekg.css /ekg.js /xajax.js I'm sending back content of a proper file [CONSTANT paths used!] + currently there is one handler for different queries for debugging purposes. It saves all changes to buffer assigned to user. and then if ajax request occur [post], data is sent. [In case of window-print it adds line to webpage JS 'gwins' ] [buffer, and JS code to update current window or highlight ] [active one. ] [In case of window-new specific code is added to add new win] [to JS and to update window list ] [In case of all queries some stuff is written to ] [specially for this purpose created div 'LOG' ] + _debug window is empty by design, since it could do some nasty recurssion [if (w->id != 0) in ui-window-print in xajax_def_action] + gwins internal buffer - you can think of it as a array of structures: struct gwins { int magic [2-current win, 1-active, 0-normal] char *windowname char **lines }; so gwins[3][2][5] is simply line number 2 of window, that has id of '3' and gwins[3][1] is the name of that window. + window name cannot contain any html tags, since update_windows_list() in ekg2.js uses aa.innerHTML, to set link name.... + For debugging purposes COMMENT OUT the line responsible for auto-update "window.setTimeout('eventsinbackground()', 1000);\n" and click the ajax link on generated page, whenever you want... -- GiM ekg2-0.4~pre+20120506.1/plugins/httprc_xajax/ekg2.css000066400000000000000000000035321175142753400217020ustar00rootroot00000000000000 body { background: #000000; color: #e8e8e8; color: #aaaaaa; } a { color: #a0d0d0; } #left, #right, #input { border: 1px solid #808080; } #left, #right { float: left; padding: 0.25em; } #left { width: 80%; } #windows_list li { display: inline; list-style: none; padding: 0 .5em; } #windows_list li.cur { background: #808080; } #windows_list li.act { background: #404040; } #window_content { font-family: "Consolas", "Courier New", monospace; font-size: small; list-style: none; margin: 0; padding: 0.5em; } #window_content li.info1 { background: #404040; display: block; } #window_content li.info2 { background: #202020; display: block; } /* # color 5 rgb:cc/00/cc x # color 7 rgb:aa/aa/aa grey # color 8 rgb:33/33/33 black # color 11 rgb:ff/ff/00 yellow # color 14 rgb:00/ff/ff */ em { font-style:normal; } em.bold { font-weight: bold; } em.underline { text-decoration:underline; } em.blink { border: 1px solid white; } span.grey { color: #aaa; } span.red { color: #c00; } span.green { color: #0c0; } span.yellow { color: #cc0; } span.blue { color: #008; } span.purple { color: #c0c; } span.turquoise { color: #0ff; } span.white { color: #fff; } #left #window_content .nick:before { content: '<'; } #left #window_content .nick:after { content: '>'; } #right { margin-left: 1%; width: 17%; } #right dl,#right dd ul,#right dd,#left #windows_list { margin: 0; padding: 0; } #right dt { font-weight: bold; } #right dd li { list-style: none; margin-left: 18px; } #right dd li.avail { color: #e8e800; } #right dd li.xa { color: #00c000; } #right dd li.notavail { color: #f06060; } #right dd li.err { color: #c080c0; } #zwijarka { float: right; } #input input { width: 99%; margin: 0 0.5%; background: #000000; color: #e8e8e8; } ekg2-0.4~pre+20120506.1/plugins/httprc_xajax/ekg2.js000066400000000000000000000053361175142753400215320ustar00rootroot00000000000000/* * (C) Copyright 2006+ Michal 'peres' Gorny * Michal 'GiM' Spadlinski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ function zwinRoster() { var left = xajax.$('left'); var right = xajax.$('right'); var zwijarka = xajax.$('zwijarka'); if (right.style.display == 'block') { right.style.display = 'none'; zwijarka.childNodes[0].nodeValue = '\u00ab'; left.style.width = '99%'; } else { right.style.display = 'block'; zwijarka.childNodes[0].nodeValue = '\u00bb'; left.style.width = '80%'; } } window.onload = function() { /* zwijanie rostera */ var roster = xajax.$('left'); if (roster) { var zwijarka = document.createElement('a'); zwijarka.href = 'javascript:zwinRoster();'; zwijarka.id = 'zwijarka'; var tekst = document.createTextNode('\u00bb'); zwijarka.appendChild(tekst); roster.insertBefore(zwijarka, roster.firstChild); } } function update_windows_list() { var el = xajax.$('windows_list'); if (el) { el.innerHTML=''; for (i=0; i hahahaha hihihi dupa.8 i chcemy żeby w htmlu wyglądało to jakoś tak <@GiM > hahahaha hihihi dupa.8 najpierw załóżmy, że funkcję update_window_content podmieniamy na coś takiego: function update_window_content(win) { var el = xajax.$('window_content'); if (el) { el.innerHTML=''; for (i=0; i 0) { temp = document.createElement('span'); temp.setAttribute('class', 'purple'); temp.appendChild(document.createTextNode("<")); ch.appendChild(temp); temp = document.createElement('span'); temp.setAttribute('class', 'green'); temp.appendChild(document.createTextNode("@GiM")); ch.appendChild(temp); temp = document.createElement('span'); temp.setAttribute('class', 'purple'); temp.appendChild(document.createTextNode(">")); ch.appendChild(temp); ch.appendChild(document.createTextNode(" hahahaha "); temp = document.createElement('strong'); temp.appendChild(document.createTextNode("hihihi")); ch.appendChild(temp); ch.appendChild(document.createTextNode(" dupa.8"); } else ch.appendChild(document.createTextNode(" ")); if (i % 2) ch.className="info1"; else ch.className="info2"; // no i wreszcie: gwins[win][2][i] = ch UWAGA: na stringach które mają trafić do createTextNode trza wcześniej z poziomu C wywołać xml_excape !!! XXX dj: rozumiesz już, czemu mnie się nie chciało ;> to co w htmlu to linijka, to używając metod dom, widzisz ile... pi razy drzwi zrobiłem... zdałoby się jeszcze wypróbować, czy zamiast przesyłania całego kodu jsowego nie lepiej by było posyłać xajaxowe instrukcje postaci: ale na razie mi się nie chce :P ekg2-0.4~pre+20120506.1/plugins/httprc_xajax/httprc_xajax.c000066400000000000000000000712121175142753400232030ustar00rootroot00000000000000/* * (C) Copyright 2006+ Jakub 'darkjames' Zawadzki * Michal 'GiM' Spadlinski * Michal 'peres' Gorny * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #include #include #include #include #include #include #include #include #include /* string that you're typing in browser's window: * e.g: your.server.with.ekg2.com, localhost, 127.0.0.1 */ #define LOCALHOST "localhost" #define HTTPRCXAJAX_DEFPORT "8080" /* PLUGIN_LOG NOT PLUGIN_UI, since it depends on ncurses !!! */ PLUGIN_DEFINE(httprc_xajax, PLUGIN_UI, NULL); typedef struct { void *window; char *prompt; int prompt_len; int margin_left, margin_right, margin_top, margin_bottom; fstring_t **backlog; int backlog_size; int redraw; int start; int lines_count; void **lines; int overflow; int (*handle_redraw)(window_t *w); void (*handle_mouse)(int x, int y, int state); } ncurses_window_t; typedef struct { char *cookie; int collector_active; int fd, httpver; int waiting; string_t collected; } client_t; list_t clients = NULL; client_t *find_client_by_cookie(list_t clients, char *cookie) { list_t l; for (l=clients; l; l=l->next) { client_t *p = l->data; if (!xstrcmp(p->cookie, cookie)) return p; } return NULL; } char *generate_cookie(void) { return saprintf("%x%d%d", rand()*rand(), (int)time(NULL), rand()); } char *escape_single_quote(char *p) { string_t s = string_init(NULL); int l=xstrlen(p); while (l>0) { if (*p == '\'') string_append(s, "\\'"); else string_append_c(s, *p); l --; p ++; } return string_free(s, 0); } char *http_fstring(int winid, char *parent, const fstring_t *line) { const fstr_attr_t *attr = line->attr; const char *str = line->str; string_t asc = string_init(NULL); int i, last, lastbeg, len, att; char tempchar; char *normal; char *tmp; char *colortbl[10] = { "grey", "red", "green", "yellow", "blue", "purple", "turquoise", "white" }; #define ISBOLD (att & FSTR_BOLD) #define ISBLINK (att & FSTR_BLINK) #define ISUNDERLINE (att & FSTR_UNDERLINE) #define ISREVERSE (att & FSTR_REVERSE) #define ISNORMAL (att & FSTR_NORMAL) #define ISONLYNORMAL ((att & FSTR_NORMAL) && !(att & (FSTR_BOLD|FSTR_BLINK|FSTR_REVERSE|FSTR_UNDERLINE))) #define FORE (att & FSTR_FOREMASK) #define BACK ((att & FSTR_BACKMASK)>>3) #define ADDJS(x) string_append(asc, x) #define ADDJSf(x...) string_append_format(asc, x) lastbeg = 0; last = attr[0]; /* I do not create nested tags like: * ... ... ... * since this would be quite senseless */ len = strlen(str); for (i = 1; i <= len; i++) { if (attr[i] == last) continue; tempchar = str[i]; str[i] = 0; att = attr[lastbeg]; normal = str + lastbeg; if (ISONLYNORMAL) { ADDJSf("%s.appendChild(document.createTextNode('%s'));\n",parent,(tmp = escape_single_quote(normal))); } else { if (ISBOLD || ISUNDERLINE || ISBLINK) ADDJS("em = document.createElement('em'); em.setAttribute('class', '"); /* DO NOT REMOVE THOSE SPACES AT THE END!!! */ if (ISBOLD) ADDJS("bold "); if (ISUNDERLINE) ADDJS("underline "); if (ISBLINK) ADDJS("blink "); if (ISBOLD || ISUNDERLINE || ISBLINK) ADDJS("');"); ADDJS("sp = document.createElement('span');"); if (!ISNORMAL) ADDJSf("sp.setAttribute('class', '%s');", colortbl[FORE]); ADDJSf("sp.appendChild(document.createTextNode('%s'));\n",(tmp = escape_single_quote(normal))); if (ISBOLD) { ADDJS("em.appendChild(sp);"); ADDJSf("%s.appendChild(em);", parent); } else ADDJSf("%s.appendChild(sp);", parent); } xfree(tmp); ADDJS("\n"); str[i] = tempchar; lastbeg = i; last = attr[i]; } if (!len) { ADDJSf("%s.appendChild(document.createTextNode('\\u00a0'));\n", parent); } return string_free(asc, 0); } #define httprc_write(watch, args...) do { string_append_format(watch->buf, args); } while (0) #define httprc_write2(watch, str) do { string_append_n(watch->buf, str, -1); } while (0) #define httprc_write3(watch, str, len) do { string_append_raw(watch->buf, str, len); } while (0) #define HTTP_HEADER(ver, scode, eheaders) \ httprc_write(send_watch, \ "%s %d %s\r\n" /* statusline: $PROTOCOL $SCODE $RESPONSE */\ "Server: ekg2-GIT-httprc_xajax plugin\r\n" /* server info */ \ "%s\r\n", /* headers */ \ ver == 0 ? "HTTP/1.0" : ver == 1 ? "HTTP/1.1" : "", /* PROTOCOL */ \ scode, /* Status code */ \ /* some typical responses */ \ scode == 100 ? "Continue" : \ scode == 101 ? "Switching Protocols" : \ scode == 200 ? "OK" : \ scode == 302 ? "Found" : \ "", \ eheaders ? eheaders : "\r\n" \ ); clen = (send_watch->buf ? send_watch->buf->len : 0) - clen; #define WATCH_FIND(w, fd) do { \ w = watch_find(&httprc_xajax_plugin, fd, WATCH_WRITE);\ if (!w) { \ w = watch_add_line(&httprc_xajax_plugin, fd, WATCH_WRITE_LINE, http_watch_send, NULL); \ } \ } while(0) WATCHER_LINE(http_watch_send) { if (type) return 0; /* XXX, sprawdzic czy user chce gzipa. jesli tak wysylamy gzipniete. */ /* XXX, sprawdzic czy user leci po https */ /* XXX, sprawdzic, czy user leci w kulki! */ return write(fd, watch, xstrlen(watch)); } QUERY(httprc_xajax_def_action) { list_t a; client_t *p; static int xxxid=0; int nobr = 0; int gname=0, gw=0, gline=0; char *name = NULL, *tmp = NULL; window_t *w = NULL; const fstring_t *line = NULL; for (a=clients; a; a=a->next) { p = (client_t *)a->data; if (!(p->collector_active)) continue; if (!xstrcmp((char *)data, "ui-window-print")) { if (!gw) w = *(va_arg(ap, window_t **)),gw=1; if (w->id != 0) { if (!gline) { char *fstringed; /* ncurses_window_t *n = w->priv_data; */ line = *(va_arg(ap, const fstring_t **)); gline=1; fstringed = http_fstring(w->id, "ch", line); tmp = saprintf("glst=gwins[%d][2].length;\n" "ch = document.createElement('li');\n" "ch.setAttribute('id', 'lin'+glst);\n" "%s\n" "ch.className='info'+(glst%%2);\n" "gwins[%d][2][glst]=ch;\n" "if (current_window != %d) { xajax.$\('wi'+%d).className='act'; }\n" "else { window_content_add_line(%d); }\n", w->id, fstringed, w->id, w->id, w->id, w->id); xfree(fstringed); } /* it'd be easier if we'd got iface for creating xml files... */ string_append(p->collected, "collected, ekg_itoa(nobr)); string_append(p->collected, (char *)data); string_append(p->collected, " = "); string_append(p->collected, ekg_itoa(w->id)); string_append(p->collected, " = "); string_append(p->collected, line->str); string_append(p->collected, "]]>"); string_append(p->collected, "collected, tmp); string_append(p->collected, "eventsinbackground();\n"); string_append(p->collected, "]]>"); } else { nobr = 1; } } else if (!xstrcmp((char *)data, "ui-window-new")) { if (!gw) { w = *(va_arg(ap, window_t **)); gw=1; } /* create gwins structure */ if (w == window_current) tmp = saprintf("gwins[%d] = new Array(2, \"%s\", new Array());\n ", w->id, window_target(w)); else if (w->act) tmp = saprintf("gwins[%d] = new Array(1, \"%s\", new Array());\n ", w->id, window_target(w)); else tmp = saprintf("gwins[%d] = new Array(0, \"%s\", new Array());\n ", w->id, window_target(w)); string_append(p->collected, "collected, tmp); /* add call to update_windows_list [in ekg.js] */ string_append(p->collected, "update_windows_list();\n"); string_append(p->collected, "eventsinbackground();\n"); string_append(p->collected, "]]>"); string_append(p->collected, "collected, (char *)data); string_append(p->collected, " = ID: "); string_append(p->collected, ekg_itoa(w->id)); string_append(p->collected, " = targ:"); string_append(p->collected, w->target?w->target:"empty_target"); string_append(p->collected, " = sess:"); string_append(p->collected, w->session?(w->session->uid?w->session->uid:"empty_sessionuid"):"empty_session"); string_append(p->collected, "]]>"); string_append(p->collected, "collected, "eventsinbackground();\n"); string_append(p->collected, "]]>"); } else if (!xstrcmp((char *)data, "ui-window-kill")) { if (!gw) { w = *(va_arg(ap, window_t **)); gw=1; } /* kill gwins struct */ tmp = saprintf("gwins[%d] = void 0;\n", w->id); string_append(p->collected, "collected, tmp); /* add call to update_windows_list [in ekg.js] */ string_append(p->collected, "update_windows_list();\n"); xfree(tmp); tmp = saprintf("update_window_content(%d);\n",window_current->id); string_append(p->collected, tmp); string_append(p->collected, "]]>"); string_append(p->collected, "collected, (char *)data); string_append(p->collected, " = current: "); string_append(p->collected, ekg_itoa(window_current->id)); string_append(p->collected, " = ID: "); string_append(p->collected, ekg_itoa(w->id)); string_append(p->collected, " = targ:"); string_append(p->collected, window_target(w)); string_append(p->collected, " = sess:"); string_append(p->collected, w->session?(w->session->uid?w->session->uid:"empty_sessionuid"):"empty_session"); string_append(p->collected, "]]>"); string_append(p->collected, "collected, "eventsinbackground();\n"); string_append(p->collected, "]]>"); } else if (!xstrcmp((char *)data, "variable-changed")) { if (!gname) { name = *(va_arg(ap, char**)); gname=1; } string_append(p->collected, "collected, (char *)data); string_append(p->collected, " = "); string_append(p->collected, name); string_append(p->collected, "]]>"); string_append(p->collected, "collected, "eventsinbackground();\n"); string_append(p->collected, "]]>"); } else { debug("oth: %08X\n", data); string_append(p->collected, "collected, (char *)data); string_append(p->collected, "]]>"); string_append(p->collected, "collected, "eventsinbackground();\n"); string_append(p->collected, "]]>"); } if (!nobr) { string_append(p->collected, "collected, ekg_itoa(xxxid++)); string_append(p->collected, "\">"); } if (p->fd != -1 && p->collected->len && p->waiting) { watch_t *send_watch = NULL; int clen = 0; WATCH_FIND(send_watch, p->fd); /* VERY VERY BAAAAD */ if (!send_watch) { debug_error("[%s:%d] NOT SEND_WATCH @ fd: %d!\n", __FILE__, __LINE__, p->fd); return -1; } if (send_watch->buf) clen = send_watch->buf->len; p->waiting = 0; HTTP_HEADER(p->httpver, 200, "Content-Type: text/html\r\n"); httprc_write(send_watch, "\n" "%s", config_console_charset, p->collected->str); string_free(p->collected, 1); p->collected = string_init(""); if (send_watch->buf) { char *tmp = saprintf("Content-length: %d\r\n", send_watch->buf->len - clen); string_insert(send_watch->buf, clen - 2, tmp); xfree(tmp); } watch_handle_write(send_watch); } } xfree(tmp); return 0; } const char *http_timestamp(time_t t) { static char buf[2][100]; struct tm *tm = localtime(&t); static int i = 0; const char *format = format_find("timestamp"); if (!format) return ekg_itoa(t); i = i % 2; if (!strftime(buf[i], sizeof(buf[0]), format, tm) && xstrlen(format)>0) xstrcpy(buf[i], "TOOLONG"); return buf[i++]; } typedef enum { HTTP_METHOD_UNKNOWN = -1, HTTP_METHOD_OPTIONS = 0, HTTP_METHOD_GET, HTTP_METHOD_HEAD, HTTP_METHOD_POST, HTTP_METHOD_PUT, HTTP_METHOD_DELETE, HTTP_METHOD_TRACE, HTTP_METHOD_CONNECT, } http_method_t; WATCHER(http_watch_read) { char rbuf[4096]; char *buf; int len, send_cookie_again = 1; client_t *client = NULL; watch_t *send_watch = NULL; http_method_t method = HTTP_METHOD_UNKNOWN; int ver = -1; /* 0 - HTTP/1.0 1 - HTTP/1.1 */ char *req = NULL; char *line; int clen = 0; if (type) { list_t l; for (l = clients; l; l = l->next) { client_t *c = l->data; if (c->fd == fd) c->fd = -1; } debug(">>>>>>>>>>>>>>>>>>\n closing http fd\n"); close(fd); return 0; } if ((len = read(fd, &rbuf[0], sizeof(rbuf)-1)) < 1) { if (len == -1) { if (errno == EAGAIN || errno == EINTR) return 0; debug_error("HTTPRC: read() errno = %d %s\n", errno, strerror(errno)); } return -1; } rbuf[len] = 0; buf = &rbuf[0]; if ((line = split_line(&buf))) { /* Request-Line = Method SP Request-URI SP HTTP-Version CRLF */ char *httpver = NULL; char *hline = NULL; if (!xstrncmp(line, "OPTIONS ", 8)) method = HTTP_METHOD_OPTIONS; else if (!xstrncmp(line, "TRACE ", 6)) method = HTTP_METHOD_TRACE; else if (!xstrncmp(line, "CONNECT ", 8))method = HTTP_METHOD_CONNECT; else if (!xstrncmp(line, "GET ", 4)) method = HTTP_METHOD_GET; else if (!xstrncmp(line, "HEAD ", 5)) method = HTTP_METHOD_HEAD; else if (!xstrncmp(line, "POST ", 5)) method = HTTP_METHOD_POST; else if (!xstrncmp(line, "PUT ", 4)) method = HTTP_METHOD_PUT; else if (!xstrncmp(line, "DELETE ", 7)) method = HTTP_METHOD_DELETE; /* HTTP/1.0 definiuje jeszcze LINK i UNLINK */ else { /* pozostale traktujemy jako nie zaimplementowane... */ return 0; } /* przechodzimy do nastepnego pola */ if (!(line = xstrchr(line, ' '))) return 0; line++; req = line; /* request */ if ((line = xstrchr(line, ' '))) { *line = 0; line++; } else { return 0; } httpver = line; /* method, req, httpver w buf naglowki... */ if (!xstrcmp(httpver, "HTTP/1.1")) ver = 1; else if (!xstrcmp(httpver, "HTTP/1.0")) ver = 0; else { /* zly protokol, papa. */ return 0; } if (ver == 0 && (method == HTTP_METHOD_OPTIONS || method == HTTP_METHOD_TRACE || method == HTTP_METHOD_CONNECT)) { /* probowano zarzadzac czegos czego nie ma w aktualnym protokole, papa */ return 0; } debug(":: %d %s %d\n", method, req, ver); /* headers */ while ((hline = split_line(&buf))) { if (!xstrcmp(hline, "\r")) { if (xstrlen(buf)) buf++; break; /* \r\n headers ends */ } debug("XXX, %s\n", hline); if (!xstrncmp(hline, "Cookie: httprc=", 15) && xstrlen(hline)>17) { char *cookie=xstrdup(hline+15); debug("Cookie found: %s\n", cookie); if ((client = find_client_by_cookie(clients, cookie))) { send_cookie_again = 0; client->collector_active = 1; } xfree(cookie); } } if (send_cookie_again) { client = (client_t *)xcalloc(1, sizeof(client_t)); client->collector_active = 1; client->collected = string_init(""); client->cookie = generate_cookie(); client->httpver = ver; client->fd = fd; /* XXX -1 ? */ list_add(&clients, client); debug("Adding client %s!\n", client->cookie); } /* reszta, e.g POST */ while ((hline = split_line(&buf))) { debug("XXXX, %s\n", hline); } } else { /* failed ? */ return 0; } debug ("%d %08x\n", send_cookie_again, client); WATCH_FIND(send_watch, fd); if (!send_watch) { debug_error("[%s:%d] NOT SEND_WATCH @ fd: %d!\n", __FILE__, __LINE__, fd); return -1; } if (send_watch->buf) clen = send_watch->buf->len; if (method == HTTP_METHOD_GET) { string_t htheader = string_init(""); if (!xstrcmp(req, "/ekg2.js") || !xstrcmp(req, "/ekg2.css") || !xstrcmp(req, "/xajax.js")) { FILE *f = NULL; char *mime; if (!xstrcmp(req, "/xajax.js")) { f = fopen(DATADIR"/plugins/httprc_xajax/xajax_0.2.4.js", "r"); mime = "text/javascript"; } else if (!xstrcmp(req, "/ekg2.js")) { f = fopen(DATADIR"/plugins/httprc_xajax/ekg2.js", "r"); mime = "text/javascript"; } else { f = fopen(DATADIR"/plugins/httprc_xajax/ekg2.css", "r"); mime = "text/css"; } string_append(htheader, "Connection: Keep-Alive\r\n" "Keep-Alive: timeout=5, max=100\r\n" "Content-Type: "); string_append(htheader, mime); string_append(htheader, "\r\n"); HTTP_HEADER(ver, 200, htheader->str); string_free(htheader, 1); if (f) { char buf[4096]; int len; while ((!feof(f))) { len = fread(&buf[0], 1, sizeof(buf), f); httprc_write3(send_watch, buf, len); } fclose(f); } else { debug_error("[%s:%d] File couldn't be open req: %s\n", __FILE__, __LINE__, req); } } else { char *temp; int i, j; window_t *w = window_current, *w2; ncurses_window_t *n; /* if user is making a refresh, we must clear collected events * whether he has a cookie or not */ client->collected = string_init(""); if (send_cookie_again) { string_append(htheader, "Set-Cookie: httprc="); string_append(htheader, client->cookie); string_append(htheader, "; path=/\r\n"); } string_append(htheader, "Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0\r\n" "Pragma: no-cache\r\n" "Connection: Keep-Alive\r\n" "Keep-Alive: timeout=5, max=100\r\n" "Content-Type: text/html\r\n"); HTTP_HEADER(ver, 200, htheader->str); string_free(htheader, 1); /* naglowek HTML */ httprc_write(send_watch, "\n" "\n" "\n" "\t\n" "\t\t\n" "\t\tEKG2 :: Remote Control\n" "\t\t\n" "\t\t\n" "\t\t\n" "\t\t" "\t\t\n" "\t\t\n" "\t\n" "\t\n" "\t\t
\n" "\t\t\t
    \n"); httprc_write2(send_watch, "\t\t\t
\n" "\t\t\t
    \n"); httprc_write2(send_watch, "\t\t\t
\n" "\t\t
\n" "\t\t
\n" "\t\t\t
\n"); /* USERLISTA */ if (session_current) { userlist_t *ul; httprc_write(send_watch, "\t\t\t\t
Aktualna sesja: %s
\n", session_current->alias ? session_current->alias : session_current->uid); if (session_current->userlist) httprc_write2(send_watch, "\t\t\t\t
    \n"); for (ul = session_current->userlist; ul; ul = ul->next) { userlist_t *u = ul; httprc_write(send_watch, "\t\t\t\t\t
  • %s
  • \n", ekg_status_string(u->status, 0), u->nickname ? u->nickname : u->uid); } if (session_current->userlist) httprc_write2(send_watch, "\t\t\t\t
\n"); } /* KOMENDY */ httprc_write(send_watch, "\t\t\t
\n" "\t\t
\n\n" "\t\t
\n" "\t\t\t
\n" "\t\t\t\t\n" "\t\t\t
\n" "\t\t\tAJAX!\n" "\t\t
\n" "\t\t
\n" "\t\t
\n" "\t\t
\n" "\t\t
\n" "\t\t
\n" "\t\t
\n" "\t\n" "\t\n" "", w->id, w->id); } } else if (method == HTTP_METHOD_POST && !xstrcmp(req, "/xajax/")) { //if (xstrlen(client->collected->str)) //{ /* HTTP_HEADER(ver, 200, "Content-Type: text/html\r\n"); httprc_write(send_watch, "\n" "%s", config_console_charset, client->collected->str); string_free(client->collected, 1); client->collected = string_init(""); */ client->fd = fd; client->waiting = 1; debug_error("turning on waiting: on fd: %d by cookie: %s\n", fd, client->cookie); //} return 0; } else { HTTP_HEADER(ver, 200, "Content-Type: text/html\r\n"); } /* commit data */ if (send_watch->buf) { char *tmp = saprintf("Content-length: %d\r\n", send_watch->buf->len - clen); string_insert(send_watch->buf, clen - 2, tmp); xfree(tmp); } watch_handle_write(send_watch); return 0; } WATCHER(http_watch_accept) { struct sockaddr sa; int cfd; unsigned int salen = sizeof(sa); if (type) { close(fd); return 0; } if ((cfd = accept(fd, &sa, &salen)) == -1) { debug("[httprc-xajax] accept() failed: %s\n", strerror(errno)); return -1; } debug("[httprc-xajax] accept() succ: %d\n",cfd); watch_add_line(&httprc_xajax_plugin, cfd, WATCH_READ, http_watch_read, NULL); return 0; } int httprc_xajax_plugin_init(int prio) { int one = 1; int fd; struct sockaddr_in sin; PLUGIN_CHECK_VER("httprc_xajax"); sin.sin_family = AF_INET; sin.sin_port = g_htons(atoi(HTTPRCXAJAX_DEFPORT)); sin.sin_addr.s_addr = INADDR_ANY; if (!config_console_charset || (xstrcmp(config_console_charset, "ISO-8859-2") && xstrcmp(config_console_charset, "UTF-8"))) { debug("This plugin is under development and requires console_charset to be set to one of the following:\n"); debug("ISO-8859-2, UTF-8. Use /set console_charset to change current value: %s\n", config_console_charset); return -1; } if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { debug("[httprc-xajax] socket() failed: %s\n", strerror(errno)); return -1; } if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) == -1) debug("[httprc-xajax] setsockopt(SO_REUSEADDR) failed: %s\n", strerror(errno)); if (bind(fd, (struct sockaddr*) &sin, sizeof(sin))) { debug("[httprc-xajax] bind() failed: %s\n", strerror(errno)); return -1; } if (listen(fd, 10)) { debug("[httprc-xajax] listen() failed: %s\n", strerror(errno)); return -1; } plugin_register(&httprc_xajax_plugin, prio); /* variable_add(&rc_plugin, ("remote_control"), VAR_STR, 1, &rc_paths, rc_paths_changed, NULL, NULL); */ watch_add(&httprc_xajax_plugin, fd, WATCH_READ, http_watch_accept, NULL); // query_connect(&httprc_xajax_plugin, ("set-vars-default"), httprc_xajax_def_action, NULL); // query_connect(&httprc_xajax_plugin, ("ui-beep"), httprc_xajax_def_action, NULL); // query_connect(&httprc_xajax_plugin, ("ui-is-initialized"), httprc_xajax_def_action, NULL); query_connect(&httprc_xajax_plugin, "ui-window-switch", httprc_xajax_def_action, "ui-window-switch"); query_connect(&httprc_xajax_plugin, "ui-window-print", httprc_xajax_def_action, "ui-window-print"); query_connect(&httprc_xajax_plugin, "ui-window-new", httprc_xajax_def_action, "ui-window-new"); query_connect(&httprc_xajax_plugin, "ui-window-kill", httprc_xajax_def_action, "ui-window-kill"); query_connect(&httprc_xajax_plugin, "ui-window-target-changed", httprc_xajax_def_action, "ui-target-changed"); /* We're not touching this one, since this would cause * A LOT of unneeded traffic! query_connect(&httprc_xajax_plugin, "ui-window-act-changed", httprc_xajax_def_action, "ui-window-act-changed"); */ query_connect(&httprc_xajax_plugin, "ui-window-refresh", httprc_xajax_def_action, "ui-window-refresh"); query_connect(&httprc_xajax_plugin, "ui-window-clear", httprc_xajax_def_action, "ui-window-clear"); query_connect(&httprc_xajax_plugin, "session-added", httprc_xajax_def_action, "session-added"); query_connect(&httprc_xajax_plugin, "session-removed", httprc_xajax_def_action, "session-removed"); query_connect(&httprc_xajax_plugin, "session-changed", httprc_xajax_def_action, "session-changed"); query_connect(&httprc_xajax_plugin, "userlist-changed", httprc_xajax_def_action, "userlist-changed"); query_connect(&httprc_xajax_plugin, "userlist-added", httprc_xajax_def_action, "userlist-added"); query_connect(&httprc_xajax_plugin, "userlist-removed", httprc_xajax_def_action, "userlist-removed"); query_connect(&httprc_xajax_plugin, "userlist-renamed", httprc_xajax_def_action, "userlist-renamed"); query_connect(&httprc_xajax_plugin, "binding-set", httprc_xajax_def_action, "binding-set"); query_connect(&httprc_xajax_plugin, "binding-command", httprc_xajax_def_action, "binding-command"); query_connect(&httprc_xajax_plugin, "binding-default", httprc_xajax_def_action, "binding-default"); query_connect(&httprc_xajax_plugin, "variable-changed", httprc_xajax_def_action, "variable-changed"); query_connect(&httprc_xajax_plugin, "conference-renamed", httprc_xajax_def_action, "conference-renamed"); query_connect(&httprc_xajax_plugin, "metacontact-added", httprc_xajax_def_action, "metacontact-added"); query_connect(&httprc_xajax_plugin, "metacontact-removed", httprc_xajax_def_action, "metacontact-removed"); query_connect(&httprc_xajax_plugin, "metacontact-item-added", httprc_xajax_def_action, "metacontact-item-added"); query_connect(&httprc_xajax_plugin, "metacontact-item-removed", httprc_xajax_def_action, "metacontact-item-removed"); query_connect(&httprc_xajax_plugin, "config-postinit", httprc_xajax_def_action, "config-postinit"); return 0; } static int httprc_xajax_plugin_destroy() { plugin_unregister(&httprc_xajax_plugin); return 0; } ekg2-0.4~pre+20120506.1/plugins/httprc_xajax/xajax_0.2.4.js000066400000000000000000000402101175142753400225240ustar00rootroot00000000000000/* xajax Javascript library :: version 0.2.4 */ Array.prototype.containsValue=function(valueToCheck){for(var i=0;i 1000)text=text.substr(0,1000)+"...\n[long response]\n...";try{if(this.debugWindow==undefined||this.debugWindow.closed==true){this.debugWindow=window.open('about:blank','xajax-debug','width=800,height=600,scrollbars=1,resizable,status');this.debugWindow.document.write('Xajax debug output

Xajax debug output

');} text=text.replace(/&/g,"&") text=text.replace(//g,">") debugTag=this.debugWindow.document.getElementById('debugTag');debugTag.innerHTML=(''+(new Date()).toString()+': '+text+'
')+debugTag.innerHTML;}catch(e){alert("Xajax Debug:\n "+text);} };this.workId='xajaxWork'+new Date().getTime();this.depth=0;this.responseErrorsForAlert=["400","401","402","403","404","500","501","502","503"];this.getRequestObject=function(){if(xajaxDebug)this.DebugMessage("Initializing Request Object..");var req=null; if(typeof XMLHttpRequest!="undefined") req=new XMLHttpRequest(); if(!req&&typeof ActiveXObject!="undefined"){ try{ req=new ActiveXObject("Msxml2.XMLHTTP"); } catch(e){ try{ req=new ActiveXObject("Microsoft.XMLHTTP"); } catch(e2){ try{ req=new ActiveXObject("Msxml2.XMLHTTP.4.0");} catch(e3){req=null;} } } } req.overrideMimeType('text/xml'); if(!req&&window.createRequest) req=window.createRequest();if(!req)this.DebugMessage("Request Object Instantiation failed.");return req;} this.$=function(sId){if(!sId){return null;} var returnObj=document.getElementById(sId);if(!returnObj&&document.all){returnObj=document.all[sId];} if(xajaxDebug&&!returnObj&&sId!=this.workId){this.DebugMessage("Element with the id \""+sId+"\" not found.");} return returnObj;} this.include=function(sFileName){var objHead=document.getElementsByTagName('head');var objScript=document.createElement('script');objScript.type='text/javascript';objScript.src=sFileName;objHead[0].appendChild(objScript);} this.stripOnPrefix=function(sEventName){sEventName=sEventName.toLowerCase();if(sEventName.indexOf('on')==0){sEventName=sEventName.replace(/on/,'');} return sEventName;} this.addOnPrefix=function(sEventName){sEventName=sEventName.toLowerCase();if(sEventName.indexOf('on')!=0){sEventName='on'+sEventName;} return sEventName;} this.addHandler=function(sElementId,sEvent,sFunctionName){if(window.addEventListener){sEvent=this.stripOnPrefix(sEvent);eval("this.$('"+sElementId+"').addEventListener('"+sEvent+"',"+sFunctionName+",false);");} else{sAltEvent=this.addOnPrefix(sEvent);eval("this.$('"+sElementId+"').attachEvent('"+sAltEvent+"',"+sFunctionName+",false);");} } this.removeHandler=function(sElementId,sEvent,sFunctionName){if(window.addEventListener){sEvent=this.stripOnPrefix(sEvent);eval("this.$('"+sElementId+"').removeEventListener('"+sEvent+"',"+sFunctionName+",false);");} else{sAltEvent=this.addOnPrefix(sEvent);eval("this.$('"+sElementId+"').detachEvent('"+sAltEvent+"',"+sFunctionName+",false);");} } this.create=function(sParentId,sTag,sId){var objParent=this.$(sParentId);objElement=document.createElement(sTag);objElement.setAttribute('id',sId);if(objParent) objParent.appendChild(objElement);} this.insert=function(sBeforeId,sTag,sId){var objSibling=this.$(sBeforeId);objElement=document.createElement(sTag);objElement.setAttribute('id',sId);objSibling.parentNode.insertBefore(objElement,objSibling);} this.insertAfter=function(sAfterId,sTag,sId){var objSibling=this.$(sAfterId);objElement=document.createElement(sTag);objElement.setAttribute('id',sId);objSibling.parentNode.insertBefore(objElement,objSibling.nextSibling);} this.getInput=function(sType,sName,sId){var Obj;if(!window.addEventListener){Obj=document.createElement('');} else{Obj=document.createElement('input');Obj.setAttribute('type',sType);Obj.setAttribute('name',sName);Obj.setAttribute('id',sId);} return Obj;} this.createInput=function(sParentId,sType,sName,sId){var objParent=this.$(sParentId);var objElement=this.getInput(sType,sName,sId);if(objParent&&objElement) objParent.appendChild(objElement);} this.insertInput=function(sBeforeId,sType,sName,sId){var objSibling=this.$(sBeforeId);var objElement=this.getInput(sType,sName,sId);if(objElement&&objSibling&&objSibling.parentNode) objSibling.parentNode.insertBefore(objElement,objSibling);} this.insertInputAfter=function(sAfterId,sType,sName,sId){var objSibling=this.$(sAfterId);var objElement=this.getInput(sType,sName,sId);if(objElement&&objSibling&&objSibling.parentNode){objSibling.parentNode.insertBefore(objElement,objSibling.nextSibling);} } this.remove=function(sId){objElement=this.$(sId);if(objElement&&objElement.parentNode&&objElement.parentNode.removeChild){objElement.parentNode.removeChild(objElement);} } this.replace=function(sId,sAttribute,sSearch,sReplace){var bFunction=false;if(sAttribute=="innerHTML") sSearch=this.getBrowserHTML(sSearch);eval("var txt=this.$('"+sId+"')."+sAttribute);if(typeof txt=="function"){txt=txt.toString();bFunction=true;} if(txt.indexOf(sSearch)>-1){var newTxt='';while(txt.indexOf(sSearch)>-1){x=txt.indexOf(sSearch)+sSearch.length+1;newTxt+=txt.substr(0,x).replace(sSearch,sReplace);txt=txt.substr(x,txt.length-x);} newTxt+=txt;if(bFunction){eval('this.$("'+sId+'").'+sAttribute+'=newTxt;');} else if(this.willChange(sId,sAttribute,newTxt)){eval('this.$("'+sId+'").'+sAttribute+'=newTxt;');} } } this.getFormValues=function(frm){var objForm;var submitDisabledElements=false;if(arguments.length > 1&&arguments[1]==true) submitDisabledElements=true;var prefix="";if(arguments.length > 2) prefix=arguments[2];if(typeof(frm)=="string") objForm=this.$(frm);else objForm=frm;var sXml="";if(objForm&&objForm.tagName=='FORM'){var formElements=objForm.elements;for(var i=0;i < formElements.length;i++){if(!formElements[i].name) continue;if(formElements[i].name.substring(0,prefix.length)!=prefix) continue;if(formElements[i].type&&(formElements[i].type=='radio'||formElements[i].type=='checkbox')&&formElements[i].checked==false) continue;if(formElements[i].disabled&&formElements[i].disabled==true&&submitDisabledElements==false) continue;var name=formElements[i].name;if(name){if(sXml!='') sXml+='&';if(formElements[i].type=='select-multiple'){for(var j=0;j < formElements[i].length;j++){if(formElements[i].options[j].selected==true) sXml+=name+"="+encodeURIComponent(formElements[i].options[j].value)+"&";} } else{sXml+=name+"="+encodeURIComponent(formElements[i].value);} } } } sXml+="";return sXml;} this.objectToXML=function(obj){var sXml="";for(i in obj){try{if(i=='constructor') continue;if(obj[i]&&typeof(obj[i])=='function') continue;var key=i;var value=obj[i];if(value&&typeof(value)=="object"&&this.depth <=50){this.depth++;value=this.objectToXML(value);this.depth--;} sXml+=""+key+""+value+"";} catch(e){if(xajaxDebug)this.DebugMessage(e.name+": "+e.message);} } sXml+="";return sXml;} this._nodeToObject=function(node){if(node.nodeName=='#cdata-section'){var data="";for(var j=0;j"+document.getElementsByTagName("HTML")[0].innerHTML+"";} this.processResponse=function(xml){ clearTimeout(loadingTimeout); this.doneLoadingFunction(); if(xajaxStatusMessages==true) window.status='Processing...'; var tmpXajax=null; xml=xml.documentElement; if(xml==null) return; var skipCommands=0; var cmdline=""; var i; for(i=0;i 0){skipCommands--;continue;} if(xml.childNodes[i].nodeName=="cmd"){ var cmd;var id;var property;var data;var search;var type;var before;var objElement=null; for(var j=0;j 1&&xml.childNodes[i].firstChild.nodeName=="#cdata-section"){ data=""; for(var j=0;j 1){ for(var j=0;j 1&&xml.childNodes[i].childNodes[j].firstChild.nodeName=="#cdata-section"){ var internalData=""; for(var k=0;k #include typedef struct { guint32 state[4]; guint32 count[2]; unsigned char buffer[64]; } EKG2_MD5_CTX; static void Init(EKG2_MD5_CTX* context); static void Transform(guint32 state[4], unsigned char buffer[64]); static void Update(EKG2_MD5_CTX* context, unsigned char* data, unsigned int len); static void Final(unsigned char digest[20], EKG2_MD5_CTX* context); #define MD5Init(ctx) Init(ctx) #define MD5Transform(state, buffer) Transform(state, buffer) #define MD5Update(ctx, data, len) Update(ctx, (unsigned char *) data, len) #define MD5Final(digest, ctx) Final(digest, ctx) #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) #define F(x, y, z) (((x) & (y)) | ((~x) & (z))) #define G(x, y, z) (((x) & (z)) | ((y) & (~z))) #define H(x, y, z) ((x) ^ (y) ^ (z)) #define I(x, y, z) ((y) ^ ((x) | (~z))) #define FF(a, b, c, d, x, s, ac) { (a) += F ((b), (c), (d)) + (x) + (guint32)(ac); (a) = rol((a), (s)); (a) += (b); } #define GG(a, b, c, d, x, s, ac) { (a) += G ((b), (c), (d)) + (x) + (guint32)(ac); (a) = rol((a), (s)); (a) += (b); } #define HH(a, b, c, d, x, s, ac) { (a) += H ((b), (c), (d)) + (x) + (guint32)(ac); (a) = rol((a), (s)); (a) += (b); } #define II(a, b, c, d, x, s, ac) { (a) += I ((b), (c), (d)) + (x) + (guint32)(ac); (a) = rol((a), (s)); (a) += (b); } /* Hash a single 512-bit block. This is the core of the algorithm. */ static void Transform(guint32 state[4], unsigned char buffer[64]) { guint32 *block = (guint32 *) buffer; guint32 a, b, c, d; /* Copy context->state[] to working vars */ a = state[0]; b = state[1]; c = state[2]; d = state[3]; /* Round 1 */ #define S11 7 #define S12 12 #define S13 17 #define S14 22 FF (a, b, c, d, block[ 0], S11, 0xd76aa478); /* 1 */ FF (d, a, b, c, block[ 1], S12, 0xe8c7b756); /* 2 */ FF (c, d, a, b, block[ 2], S13, 0x242070db); /* 3 */ FF (b, c, d, a, block[ 3], S14, 0xc1bdceee); /* 4 */ FF (a, b, c, d, block[ 4], S11, 0xf57c0faf); /* 5 */ FF (d, a, b, c, block[ 5], S12, 0x4787c62a); /* 6 */ FF (c, d, a, b, block[ 6], S13, 0xa8304613); /* 7 */ FF (b, c, d, a, block[ 7], S14, 0xfd469501); /* 8 */ FF (a, b, c, d, block[ 8], S11, 0x698098d8); /* 9 */ FF (d, a, b, c, block[ 9], S12, 0x8b44f7af); /* 10 */ FF (c, d, a, b, block[10], S13, 0xffff5bb1); /* 11 */ FF (b, c, d, a, block[11], S14, 0x895cd7be); /* 12 */ FF (a, b, c, d, block[12], S11, 0x6b901122); /* 13 */ FF (d, a, b, c, block[13], S12, 0xfd987193); /* 14 */ FF (c, d, a, b, block[14], S13, 0xa679438e); /* 15 */ FF (b, c, d, a, block[15], S14, 0x49b40821); /* 16 */ /* Round 2 */ #define S21 5 #define S22 9 #define S23 14 #define S24 20 GG (a, b, c, d, block[ 1], S21, 0xf61e2562); /* 17 */ GG (d, a, b, c, block[ 6], S22, 0xc040b340); /* 18 */ GG (c, d, a, b, block[11], S23, 0x265e5a51); /* 19 */ GG (b, c, d, a, block[ 0], S24, 0xe9b6c7aa); /* 20 */ GG (a, b, c, d, block[ 5], S21, 0xd62f105d); /* 21 */ GG (d, a, b, c, block[10], S22, 0x2441453); /* 22 */ GG (c, d, a, b, block[15], S23, 0xd8a1e681); /* 23 */ GG (b, c, d, a, block[ 4], S24, 0xe7d3fbc8); /* 24 */ GG (a, b, c, d, block[ 9], S21, 0x21e1cde6); /* 25 */ GG (d, a, b, c, block[14], S22, 0xc33707d6); /* 26 */ GG (c, d, a, b, block[ 3], S23, 0xf4d50d87); /* 27 */ GG (b, c, d, a, block[ 8], S24, 0x455a14ed); /* 28 */ GG (a, b, c, d, block[13], S21, 0xa9e3e905); /* 29 */ GG (d, a, b, c, block[ 2], S22, 0xfcefa3f8); /* 30 */ GG (c, d, a, b, block[ 7], S23, 0x676f02d9); /* 31 */ GG (b, c, d, a, block[12], S24, 0x8d2a4c8a); /* 32 */ /* Round 3 */ #define S31 4 #define S32 11 #define S33 16 #define S34 23 HH (a, b, c, d, block[ 5], S31, 0xfffa3942); /* 33 */ HH (d, a, b, c, block[ 8], S32, 0x8771f681); /* 34 */ HH (c, d, a, b, block[11], S33, 0x6d9d6122); /* 35 */ HH (b, c, d, a, block[14], S34, 0xfde5380c); /* 36 */ HH (a, b, c, d, block[ 1], S31, 0xa4beea44); /* 37 */ HH (d, a, b, c, block[ 4], S32, 0x4bdecfa9); /* 38 */ HH (c, d, a, b, block[ 7], S33, 0xf6bb4b60); /* 39 */ HH (b, c, d, a, block[10], S34, 0xbebfbc70); /* 40 */ HH (a, b, c, d, block[13], S31, 0x289b7ec6); /* 41 */ HH (d, a, b, c, block[ 0], S32, 0xeaa127fa); /* 42 */ HH (c, d, a, b, block[ 3], S33, 0xd4ef3085); /* 43 */ HH (b, c, d, a, block[ 6], S34, 0x4881d05); /* 44 */ HH (a, b, c, d, block[ 9], S31, 0xd9d4d039); /* 45 */ HH (d, a, b, c, block[12], S32, 0xe6db99e5); /* 46 */ HH (c, d, a, b, block[15], S33, 0x1fa27cf8); /* 47 */ HH (b, c, d, a, block[ 2], S34, 0xc4ac5665); /* 48 */ /* Round 4 */ #define S41 6 #define S42 10 #define S43 15 #define S44 21 II (a, b, c, d, block[ 0], S41, 0xf4292244); /* 49 */ II (d, a, b, c, block[ 7], S42, 0x432aff97); /* 50 */ II (c, d, a, b, block[14], S43, 0xab9423a7); /* 51 */ II (b, c, d, a, block[ 5], S44, 0xfc93a039); /* 52 */ II (a, b, c, d, block[12], S41, 0x655b59c3); /* 53 */ II (d, a, b, c, block[ 3], S42, 0x8f0ccc92); /* 54 */ II (c, d, a, b, block[10], S43, 0xffeff47d); /* 55 */ II (b, c, d, a, block[ 1], S44, 0x85845dd1); /* 56 */ II (a, b, c, d, block[ 8], S41, 0x6fa87e4f); /* 57 */ II (d, a, b, c, block[15], S42, 0xfe2ce6e0); /* 58 */ II (c, d, a, b, block[ 6], S43, 0xa3014314); /* 59 */ II (b, c, d, a, block[13], S44, 0x4e0811a1); /* 60 */ II (a, b, c, d, block[ 4], S41, 0xf7537e82); /* 61 */ II (d, a, b, c, block[11], S42, 0xbd3af235); /* 62 */ II (c, d, a, b, block[ 2], S43, 0x2ad7d2bb); /* 63 */ II (b, c, d, a, block[ 9], S44, 0xeb86d391); /* 64 */ /* Add the working vars back into context.state[] */ state[0] += a; state[1] += b; state[2] += c; state[3] += d; /* Wipe variables */ a = b = c = d = 0; } /* MD5Init - Initialize new context */ static void Init(EKG2_MD5_CTX* context) { /* MD5 initialization constants */ context->state[0] = 0x67452301; context->state[1] = 0xEFCDAB89; context->state[2] = 0x98BADCFE; context->state[3] = 0x10325476; context->count[0] = context->count[1] = 0; } /* Run your data through this. */ static void Update(EKG2_MD5_CTX* context, unsigned char* data, unsigned int len) { unsigned int i, j; j = (context->count[0] >> 3) & 63; if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++; context->count[1] += (len >> 29); if ((j + len) > 63) { memcpy(&context->buffer[j], data, (i = 64-j)); Transform(context->state, context->buffer); for ( ; i + 63 < len; i += 64) { Transform(context->state, &data[i]); } j = 0; } else i = 0; memcpy(&context->buffer[j], &data[i], len - i); } static void Encode (unsigned char *output, guint32 *input, unsigned int len) { unsigned int i, j; for (i = 0, j = 0; j < len; i++, j += 4) { output[j] = (unsigned char) ((input[i] ) & 0xff); output[j+1] = (unsigned char) ((input[i] >> 8) & 0xff); output[j+2] = (unsigned char) ((input[i] >> 16) & 0xff); output[j+3] = (unsigned char) ((input[i] >> 24) & 0xff); } } /* Add padding and return the message digest. */ static void Final(unsigned char digest[16], EKG2_MD5_CTX* context) { unsigned char finalcount[8]; Encode(finalcount, context->count, 8); Update(context, (unsigned char *)"\200", 1); while ((context->count[0] & 504) != 448) { Update(context, (unsigned char *)"\0", 1); } Update(context, finalcount, 8); /* Should cause a Transform() */ Encode(digest, context->state, 16); /* Wipe variables */ memset(context->buffer, 0, 64); memset(context->state, 0, 16); memset(context->count, 0, 8); memset(&finalcount, 0, 8); } char *icq_md5_digest(const char *password, const unsigned char *key, int key_len) { static unsigned char digest[16]; EKG2_MD5_CTX ctx; MD5Init(&ctx); MD5Update(&ctx, password, xstrlen(password)); MD5Final(digest, &ctx); MD5Init(&ctx); MD5Update(&ctx, key, key_len); MD5Update(&ctx, digest, sizeof(digest)); MD5Update(&ctx, "AOL Instant Messenger (SM)", xstrlen("AOL Instant Messenger (SM)")); MD5Final(digest, &ctx); return (char *) digest; } ekg2-0.4~pre+20120506.1/plugins/icq/icq.c000066400000000000000000001442071175142753400173420ustar00rootroot00000000000000/* * (C) Copyright 2006-2008 Jakub Zawadzki * 2008 Wiesaw Ochmiski * * Protocol description with author's permission from: http://iserverd.khstu.ru/oscar/ * (C) Copyright 2000-2005 Alexander V. Shutko * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #include #include #include #include #include #include #include #include #include #include #include "icq.h" #include "misc.h" #include "icq_caps.h" #include "icq_const.h" #include "icq_flap_handlers.h" #include "icq_snac_handlers.h" #define ICQ_HUB_SERVER "login.icq.com" #define ICQ_HUB_PORT 5190 int icq_config_disable_chatstates = 0; static int icq_theme_init(); PLUGIN_DEFINE(icq, PLUGIN_PROTOCOL, icq_theme_init); int icq_send_pkt(session_t *s, GString *buf) { icq_private_t *j; if (!s || !(j = s->priv) || !buf) { g_string_free(buf, TRUE); return -1; } debug_io("icq_send_pkt(%s) len: %d\n", s->uid, buf->len); icq_hexdump(DEBUG_IO, (unsigned char *) buf->str, buf->len); if (j->migrate) debug_warn("Client migrate! Packet will not be send\n"); else ekg_connection_write_buf(j->send_stream, buf->str, buf->len); g_string_free(buf, TRUE); return 0; } static TIMER_SESSION(icq_ping) { icq_private_t *j; GString *pkt; if (type) return 0; if (!s || !(j = s->priv) || !s->connected) return -1; pkt = g_string_new(NULL); icq_makeflap(s, pkt, 0x05); icq_send_pkt(s, pkt); return 0; } int icq_write_status_msg(session_t *s) { icq_private_t *j; char *msg; if (!s || !(j = s->priv)) return -1; if (j->aim == 0) return -1; msg = ekg_locale_to_utf8(xstrndup(s->descr, 0x1000)); /* XXX, cookie */ /* SNAC(02,04) CLI_SET_LOCATION_INFO Set user information * Client use this snac to set its location information (like client * profile string, client directory profile string, client capabilities). */ icq_send_snac(s, 0x02, 0x04, 0, 0, "TT", icq_pack_tlv_str(0x03, "text/x-aolrtf; charset=\"utf-8\""), icq_pack_tlv_str(0x04, msg)); xfree(msg); return 0; } int icq_write_status(session_t *s) { icq_private_t *j = s->priv; guint32 status; if (!s || !s->connected) return 0; status = (j->status_flags << 16) | icq_status(s->status); icq_send_snac(s, 0x01, 0x001e, 0, 0, "tI", icq_pack_tlv_dword(0x06, status)); /* TLV 6: Status mode and security flags */ return 1; } int icq_write_info(session_t *s) { icq_private_t *j; if (!s || !(j = s->priv)) return -1; #define m_bAvatarsEnabled 0 #define m_bUtfEnabled 1 GString *pkt, *tlv_5; tlv_5 = g_string_new(NULL); #ifdef DBG_CAPMTN icq_pack_append_cap(tlv_5, CAP_TYPING); #endif icq_pack_append_cap(tlv_5, CAP_SRV_RELAY); /* Client supports channel 2 extended, TLV(0x2711) based messages. */ if (m_bUtfEnabled) icq_pack_append_cap(tlv_5, CAP_UTF); /* Broadcasts the capability to receive UTF8 encoded messages */ #ifdef DBG_NEWCAPS /* Tells server we understand to new format of caps */ icq_pack_append_cap(tlv_5, CAP_NEWCAPS); #endif #ifdef DBG_CAPXTRAZ icq_pack_append_cap(tlv_5, CAP_XTRAZ); /* Broadcasts the capability to handle Xtraz */ #endif if (m_bAvatarsEnabled) icq_pack_append_cap(tlv_5, CAP_DEVILS); #ifdef DBG_OSCARFT /* Broadcasts the capability to receive Oscar File Transfers */ icq_pack_append_cap(tlv_5, CAP_SENDFILE); #endif if (j->aim) icq_pack_append_cap(tlv_5, CAP_INTEROPERATE); /* Tells the server we can speak to AIM */ #ifdef DBG_AIMCONTACTSEND icq_pack_append_cap(tlv_5, CAP_CONTACTS); /* Client supports buddy lists transfer. */ #endif if (j->xstatus) icq_pack_append_xstatus(tlv_5, j->xstatus); icq_pack_append_cap(tlv_5, CAP_ICQDIRECT); /* Something called "route finder". */ /*packDWord(&packet, 0x178c2d9b); // Unknown cap packDWord(&packet, 0xdaa545bb); packDWord(&packet, 0x8ddbf3bd); packDWord(&packet, 0xbd53a10a);*/ #ifdef DBG_CAPHTML icq_pack_append_cap(tlv_5, CAP_HTML); /* Broadcasts the capability to receive HTML messages */ #endif pkt = icq_pack("T", icq_pack_tlv(0x05, tlv_5->str, tlv_5->len)); icq_makesnac(s, pkt, 0x02, 0x04, NULL, 0); icq_send_pkt(s, pkt); g_string_free(tlv_5, TRUE); return 0; } void icq_set_security(session_t *s) { icq_private_t *j; GString *pkt; guint8 webaware; if (!s || !(j = s->priv)) return; if ((webaware = atoi(session_get(s, "webaware")))) j->status_flags |= STATUS_WEBAWARE; else j->status_flags &= ~STATUS_WEBAWARE; if (!(s->connected)) return; /* SNAC(15,02)/07D0/0C3A CLI_SET_FULLINFO Save info tlv-based request * * This is client tlv-based set personal info request. This snac contain tlv chain and this * chain may contain several TLVs of same type (for example 3 tlv(0x186) - language codes). * Client can change all its information via single packet. * Server should respond with SNAC(15,03)/07DA/0C3F - which contain result flag. */ pkt = icq_pack("wwc wwc", icq_pack_tlv_char(0x30c, webaware), /* TLV(0x30c) (LE!) 'show web status' permissions */ icq_pack_tlv_char(0x2f8, !atoi(session_get(s, "require_auth"))) /* TLV(0x2f8) (LE!) authorization permissions */ ); icq_makemetasnac(s, pkt, CLI_META_INFO_REQ, CLI_SET_FULLINFO, NULL, NULL); icq_send_pkt(s, pkt); } void icq_session_connected(session_t *s) { icq_private_t *j = s->priv; GString *pkt; icq_write_info(s); /* SNAC 3,4: Tell server who's on our list */ /* XXX ?wo? SNAC 3,4 is: "Add buddy(s) to contact list" */ /* XXX ?wo? check & fix */ #if 0 if (s->userlist) { /* XXX, dla kazdego kontaktu... */ icq_send_snac(s, 0x03, 0x04, 0, 0, NULL); } #endif if (s->status == EKG_STATUS_INVISIBLE) { /* Tell server who's on our visible list */ #if MIRANDA if (!j->ssi) sendEntireListServ(ICQ_BOS_FAMILY, ICQ_CLI_ADDVISIBLE, BUL_VISIBLE); else updateServVisibilityCode(3); #endif } else { /* Tell server who's on our invisible list */ #if MIRANDA if (!j->ssi) sendEntireListServ(ICQ_BOS_FAMILY, ICQ_CLI_ADDINVISIBLE, BUL_INVISIBLE); else updateServVisibilityCode(4); #endif } /* SNAC 1,1E: Set status */ { GString *pkt, *tlv_c; guint32 cookie, status; cookie = rand() <<16 | rand(); status = (j->status_flags << 16) | icq_status(s->status); pkt = g_string_new(NULL); icq_pack_append(pkt, "tI", icq_pack_tlv_dword(0x06, status)); /* TLV 6: Status mode and security flags */ icq_pack_append(pkt, "tW", icq_pack_tlv_word(0x08, 0x00)); /* TLV 8: Error code */ /* TLV C: Direct connection info */ tlv_c = g_string_new(NULL); icq_pack_append(tlv_c, "I", (guint32) 0x00000000); /* XXX, getSettingDword(NULL, "RealIP", 0) */ icq_pack_append(tlv_c, "I", (guint32) 0x00000000); /* XXX, nPort */ icq_pack_append(tlv_c, "C", DC_NORMAL); /* Normal direct connection (without proxy/firewall) */ icq_pack_append(tlv_c, "W", ICQ_VERSION); /* Protocol version */ icq_pack_append(tlv_c, "I", cookie); /* DC Cookie */ icq_pack_append(tlv_c, "I", WEBFRONTPORT); /* Web front port */ icq_pack_append(tlv_c, "I", CLIENTFEATURES); /* Client features*/ icq_pack_append(tlv_c, "I", (guint32) 0xffffffff); /* gbUnicodeCore ? 0x7fffffff : 0xffffffff */ /* Abused timestamp */ icq_pack_append(tlv_c, "I", (guint32) 0x80050003); /* Abused timestamp */ icq_pack_append(tlv_c, "I", (guint32) 0x00000000); /* XXX, Timestamp */ icq_pack_append(tlv_c, "W", (guint32) 0x0000); /* Unknown */ icq_pack_append(pkt, "T", icq_pack_tlv(0x0C, tlv_c->str, tlv_c->len)); g_string_free(tlv_c, TRUE); /* TLV(0x1F) - unknown? */ icq_pack_append(pkt, "tW", icq_pack_tlv_word(0x1F, 0x00)); /* TLV(0x1D) -- xstatus (icqmood) */ if (j->xstatus && (j->xstatus - 1 <= MAX_ICQMOOD)) { char *mood = saprintf("icqmood%d", j->xstatus - 1); GString *tlv_1d = icq_pack("WCC", (guint32) 0x0e, // item type (guint32) 0, // item flags (guint32) xstrlen(mood)); g_string_append(tlv_1d, mood); icq_pack_append(pkt, "T", icq_pack_tlv(0x1d, tlv_1d->str, tlv_1d->len)); g_string_free(tlv_1d, TRUE); xfree(mood); } icq_makesnac(s, pkt, 0x01, 0x1e, NULL, NULL); /* Set status (set location info) */ icq_send_pkt(s, pkt); } /* SNAC(1,0x11) - Set idle time */ icq_send_snac(s, 0x01, 0x11, NULL, NULL, "I", (guint32) 0); /* j->idleAllow = 0; */ /* Finish Login sequence */ /* SNAC(1,2) - Client is now online and ready for normal function */ /* imitate ICQ 6 behaviour */ icq_send_snac(s, 0x01, 0x02, NULL, NULL, "WWWW WWWW WWWW WWWW WWWW WWWW WWWW WWWW WWWW WWWW WWWW", /* family number, family version, family tool id, family tool version */ (guint32) 0x01, (guint32) 0x04, (guint32) 0x0110, (guint32) 0x161b, (guint32) 0x02, (guint32) 0x01, (guint32) 0x0110, (guint32) 0x161b, (guint32) 0x03, (guint32) 0x01, (guint32) 0x0110, (guint32) 0x161b, (guint32) 0x04, (guint32) 0x01, (guint32) 0x0110, (guint32) 0x161b, (guint32) 0x06, (guint32) 0x01, (guint32) 0x0110, (guint32) 0x161b, (guint32) 0x09, (guint32) 0x01, (guint32) 0x0110, (guint32) 0x161b, (guint32) 0x0a, (guint32) 0x01, (guint32) 0x0110, (guint32) 0x161b, (guint32) 0x0b, (guint32) 0x01, (guint32) 0x0110, (guint32) 0x161b, (guint32) 0x13, (guint32) 0x04, (guint32) 0x0110, (guint32) 0x161b, (guint32) 0x15, (guint32) 0x01, (guint32) 0x0110, (guint32) 0x161b, (guint32) 0x22, (guint32) 0x01, (guint32) 0x0110, (guint32) 0x161b ); debug_ok(" *** Yeehah, login sequence complete\n"); /* login sequence is complete enter logged-in mode */ if (!s->connected) { /* SNAC(15,02)/003C CLI_OFFLINE_MESSAGE_REQ Offline messages request * * Client sends this SNAC when wants to retrieve messages that was sent by * another user and buffered by server during client was offline. Server * should respond with 0 or more SNAC(15,03)/0041 packets with SNAC bit1=1 * and after that it should send last SNAC(15,03)/0042. * * Warn: Server doesn't delete messages from database and will send them * again. You should ask it to delete them by SNAC(15,02)/003E */ /* XXX, cookie */ pkt = g_string_new(NULL); icq_makemetasnac(s, pkt, CLI_OFFLINE_MESSAGE_REQ, 0, NULL, NULL); icq_send_pkt(s, pkt); { /* Update our information from the server */ /* SNAC(15,02)/07D0/04D0 CLI_FULLINFO_REQUEST2 Request full user info #2 * * Client full userinfo request. Last reply snac flag bit1=0, other reply * packets have flags bit1=1 to inform client that more data follows. * Server should respond with following SNACs: * SNAC(15,03)/07DA/00C8, SNAC(15,03)/07DA/00DC, SNAC(15,03)/07DA/00EB, SNAC(15,03)/07DA/010E, * SNAC(15,03)/07DA/00D2, SNAC(15,03)/07DA/00E6, SNAC(15,03)/07DA/00F0, SNAC(15,03)/07DA/00FA */ private_data_t *data = NULL; private_item_set_int(&data, "uid", atoi(s->uid+4)); pkt = icq_pack("i", atoi(s->uid+4)); /* XXX, cookie, in-quiet-mode */ icq_makemetasnac(s, pkt, CLI_META_INFO_REQ, CLI_FULLINFO_REQUEST2, data, icq_my_meta_information_response); icq_send_pkt(s, pkt); } /* Start sending Keep-Alive packets */ timer_remove_session(s, "ping"); timer_add_session(s, "ping", 60, 1, icq_ping); timer_remove_session(s, "snac_timeout"); timer_add_session(s, "snac_timeout", 10, 1, icq_snac_ref_list_cleanup); #if 0 if (m_bAvatarsEnabled) { // Send SNAC 1,4 - request avatar family 0x10 connection icq_requestnewfamily(ICQ_AVATAR_FAMILY, &CIcqProto::StartAvatarThread); m_pendingAvatarsStart = 1; NetLog_Server("Requesting Avatar family entry point."); } #endif } protocol_connected_emit(s); icq_set_security(s); icq_write_status_msg(s); { /* XXX ?wo? find better place for it */ if (!j->default_group_id) { /* No groups on server!!! */ /* Once executed ugly code: */ icq_send_snac(s, 0x13, 0x11, 0, 0, ""); // Contacts edit start (begin transaction) j->default_group_id = 69; j->default_group_name = xstrdup("ekg2"); icq_send_snac(s, 0x13, 0x08, 0, 0, // SSI edit: add item(s) "U WW W W WWW", j->default_group_name, // default group name (guint16) j->default_group_id, // Group# (guint16) 0, // Item# (guint16) 1, // Group record (guint16) 6, // Length of the additional data (guint16) 0xc8, (guint16) 2, (guint16) 0 // tlv(0xc8) (len=2, val=0) ); icq_send_snac(s, 0x13, 0x12, 0, 0, ""); // Contacts edit end (finish transaction) } } } static guint32 icq_get_uid(session_t *s, const char *target) { const char *uid; char *first_invalid = NULL; long int uin; if (!target) return 0; if (!(uid = get_uid(s, target))) uid = target; if (!xstrncmp(uid, "icq:", 4)) uid += 4; if (!uid[0]) return 0; uin = strtol(uid, &first_invalid, 10); /* XXX, strtoll() */ if (*first_invalid != '\0') return 0; if (uin <= 0) return 0; return uin; } static QUERY(icq_session_init) { char *session = *(va_arg(ap, char**)); session_t *s = session_find(session); icq_private_t *j; if (!s || s->priv || s->plugin != &icq_plugin) return 1; #if OLD_ICQ userlist_free(s); userlist_read(s); #endif j = xmalloc(sizeof(icq_private_t)); j->stream_buf = g_string_new(NULL); s->priv = j; return 0; } static QUERY(icq_session_deinit) { char *session = *(va_arg(ap, char**)); session_t *s = session_find(session); icq_private_t *j; if (!s || !(j = s->priv) || s->plugin != &icq_plugin) return 1; #if OLD_ICQ userlist_write(s); config_commit(); #endif s->priv = NULL; private_items_destroy(&j->whoami); xfree(j->default_group_name); g_string_free(j->cookie, TRUE); g_string_free(j->stream_buf, TRUE); icq_snac_references_list_destroy(&j->snac_ref_list); icq_rates_destroy(s); xfree(j); return 0; } static QUERY(icq_validate_uid) { char *uid = *(va_arg(ap, char **)); int *valid = va_arg(ap, int *); if (!uid) return 0; if (!xstrncmp(uid, "icq:", 4) && uid[4]) { uid+=4; /* now let's check if after icq: we have only digits */ for (; *uid; uid++) if (!isdigit(*uid)) return 0; (*valid)++; return -1; } return 0; } static QUERY(icq_print_version) { return 0; } static QUERY(icq_typing_out) { const char *session = *va_arg(ap, const char **); const char *uid = *va_arg(ap, const char **); const int chstate = *va_arg(ap, const int *); guint32 q1 = rand(), q2 = rand(); guint16 typing; session_t *s = session_find(session); if (icq_config_disable_chatstates) return -1; if (!s || s->plugin != &icq_plugin) return 0; typing = (chstate==EKG_CHATSTATE_COMPOSING); icq_send_snac(s, 0x04, 0x14, NULL, NULL, "iiWsW", q1, q2, (guint16) 1, uid + 4, typing); return 0; } void icq_handle_disconnect(session_t *s, const char *reason, int type) { icq_private_t *j; GString *str; const char *__reason = reason ? reason : ""; if (!s || !(j = s->priv)) return; if (!s->connected && !s->connecting) return; if (s->connected) { /* XXX ?wo? recode & length check */ str = icq_pack("WC C U", (guint32) 2, (guint32) 4, /* avatar type & flags -- offline message */ (guint32) xstrlen(__reason) + 2, /* length of message part */ __reason /* message */ ); icq_send_snac(s, 0x01, 0x1e, 0, 0, /* Set status (set location info) */ "T", icq_pack_tlv(0x1d, str->str, str->len)); g_string_free(str, TRUE); } timer_remove_session(s, "ping"); timer_remove_session(s, "snac_timeout"); protocol_disconnected_emit(s, reason, type); g_string_set_size(j->stream_buf, 0); j->migrate = 0; } static void icq_handle_stream(GDataInputStream *input, gpointer data) { session_t *s = data; icq_private_t *j = NULL; gsize count; int left, ret, start_len; if (!s || !(j = s->priv)) { debug_error("icq_handle_stream() s: 0x%x j: 0x%x\n", s, j); return; } count = g_buffered_input_stream_get_available(G_BUFFERED_INPUT_STREAM(input)); if (count>0) { char *tmp = g_malloc(count); gssize result = g_input_stream_read(G_INPUT_STREAM(input), tmp, count, NULL, NULL); g_string_append_len(j->stream_buf, tmp, result); g_free(tmp); } debug_iorecv("icq_handle_stream(%d) rcv: %d, %d in buffer.\n", s->connecting, count, j->stream_buf->len); if (count < 1) { icq_handle_disconnect(s, strerror(errno), EKG_DISCONNECT_NETWORK); return; } icq_hexdump(DEBUG_IORECV, (unsigned char *) j->stream_buf->str, j->stream_buf->len); start_len = j->stream_buf->len; ret = icq_flap_handler(s, j->stream_buf); if ( (left = j->stream_buf->len) > 0 ) { j->stream_buf->len = start_len; g_string_erase(j->stream_buf, 0, start_len - left); } switch (ret) { /* XXX, magic values */ case 0: /* OK */ break; case -1: { debug_white("icq_handle_stream() NEED MORE DATA\n"); break; } case -2: debug_error("icq_handle_stream() DISCONNECT\n"); return; default: debug_error("icq_handle_stream() icq_flap_loop() returns %d ???\n", ret); break; } } static void icq_handle_failure(GDataInputStream *f, GError *err, gpointer data) { session_t *s = data; icq_handle_disconnect(s, err->message, EKG_DISCONNECT_NETWORK); } static void icq_handle_connect( GSocketConnection *conn, GInputStream *instream, GOutputStream *outstream, gpointer data) { session_t *s = data; icq_private_t *j = NULL; if (!s || !(j = s->priv)) { debug_error("icq_handle_connect() s: 0x%x j: 0x%x\n", s, j); return; } debug_function("[icq] handle_connect(%d)\n", s->connecting); g_string_set_size(j->stream_buf, 0); j->send_stream = ekg_connection_add( conn, instream, outstream, icq_handle_stream, icq_handle_failure, s); } static void icq_handle_connect_failure(GError *err, gpointer data) { session_t *s = data; icq_handle_disconnect(s, err->message, EKG_DISCONNECT_FAILURE); } void icq_connect(session_t *session, const char *server, int port) { GSocketClient *sock = g_socket_client_new(); ekg_connection_starter_t cs = ekg_connection_starter_new(port); ekg_connection_starter_set_servers(cs, server); ekg_connection_starter_run(cs, sock, icq_handle_connect, icq_handle_connect_failure, session); } static COMMAND(icq_command_addssi) { userlist_t *u; icq_private_t *j; private_data_t *refdata = NULL; guint32 uid; guint16 group, iid; int i, ok=0; char *nickname = NULL, *phone = NULL, *email = NULL, *comment = NULL; int cmd_add = !xstrcmp(name,"addssi"); if (cmd_add && get_uid(session, params[0])) { target = params[0]; params++; } if (!cmd_add && params[0] && !xstrcmp(params[0], target)) { // XXX ??? params++; } if ((u = userlist_find(session, target))) { if (cmd_add) { /* don't allow to add user again */ printq("user_exists_other", target, format_user(session, u->uid), session_name(session)); return -1; } } else if (!cmd_add) { printq("user_not_found", target); return -1; } if (!(uid = icq_get_uid(session, target))) { printq("invalid_uid", target); return -1; } if ( !session || !(j = session->priv) ) /* WTF? */ return -1; char **argv = array_make(params[0], " \t", 0, 1, 1); for (i = 0; argv[i]; i++) { if (match_arg(argv[i], 'g', "group", 2) && argv[i + 1] && ++i) { /* XXX */ ok = 1; continue; } if (match_arg(argv[i], 'p', "phone", 2) && argv[i + 1]) { phone = argv[++i]; ok = 1; continue; } if (match_arg(argv[i], 'c', "comment", 2) && argv[i + 1]) { comment = argv[++i]; ok = 1; continue; } if (match_arg(argv[i], 'e', "email", 2) && argv[i + 1]) { email = argv[++i]; ok = 1; continue; } /* if this is -n smth */ /* OR (/add only) if param doesn't looks like command treat as a nickname */ if ((match_arg(argv[i], 'n', ("nickname"), 2) && argv[i + 1] && ++i) || (cmd_add && argv[i][0] != '-')) { if (cmd_add && userlist_find(session, argv[i])) { printq("user_exists", argv[i], session_name(session)); } else { nickname = argv[i]; ok = 1; continue; } } printq("invalid_params", name, argv[i]); } if (nickname && (u = userlist_find(session, nickname))) { printq("user_exists_other", nickname, format_user(session, u->uid), session_name(session)); ok = 0; } if ((cmd_add && nickname) || (!cmd_add && ok)) { /* send packets */ GString *buddies, *data; guint16 min = 0xffff, max = 0, count = 0; buddies = g_string_new(NULL); for (u = session->userlist; u; u = u->next) { i = user_private_item_get_int(u, "iid"); icq_pack_append(buddies, "W", i); if (i>max) max = i; if (i1) iid = 1; // else if (max-min+1 != count) { // XXX ?wo? find iid (for new user) between min, max // } else iid = max + 1; } else iid = 1; icq_pack_append(buddies, "W", iid); group = j->default_group_id; // XXX } else { u = userlist_find(session, target); iid = user_private_item_get_int(u, "iid"); group = user_private_item_get_int(u, "gid"); } /* SNAC(13,11) CLI_SSI_EDIT_BEGIN Contacts edit start (begin transaction) * Use this before server side information (SSI) modification. */ icq_send_snac(session, 0x13, 0x11, NULL, NULL, ""); data = g_string_new(NULL); /* TLV(0x0066) - Signifies that you are awaiting authorization for this buddy. The client is in * charge of putting this TLV, but you will not receiving status updates for the contact until * they authorize you, regardless if this is here or not. Meaning, this is only here to tell * your client that you are waiting for authorization for the person. This TLV is always empty. */ icq_pack_append(data, "T", icq_pack_tlv(0x66, NULL, 0)); if (nickname) icq_pack_append(data, "T", icq_pack_tlv_str(0x131, nickname)); // contact's nick name if (email) icq_pack_append(data, "T", icq_pack_tlv_str(0x137, email)); // locally assigned mail address if (phone) icq_pack_append(data, "T", icq_pack_tlv_str(0x13a, phone)); // locally assigned SMS number if (comment) icq_pack_append(data, "T", icq_pack_tlv_str(0x13c, comment)); // buddy comment // data to server response handler private_item_set(&refdata, "cmd", cmd_add ? "add" : "modify"); private_item_set(&refdata, "nick", nickname); private_item_set_int(&refdata, "uid", uid); private_item_set_int(&refdata, "iid", iid); private_item_set_int(&refdata, "gid", group); private_item_set_int(&refdata, "quiet", quiet); if (phone) private_item_set(&refdata, "mobile", phone); if (email) private_item_set(&refdata, "email", email); if (comment) private_item_set(&refdata, "comment", comment); if (cmd_add) { /* SNAC(13,08) CLI_SSIxADD SSI edit: add item(s) * Client use this to add new items to server-side info. Server should reply via SNAC(13,0E) */ icq_send_snac(session, 0x13, 0x08, refdata, icq_cmd_addssi_ack, "U WWW WA", target+4, // item name (uin) group, // Group# (guint16) iid, // Item# (guint16) 0, // Type of item: 0 -- Buddy record (guint16) data->len, // Length of the additional data data ); } /* SNAC(13,09) CLI_SSIxUPDATE SSI edit: update group header * * This can be used to modify either the name or additional data for any items that are already * in your server-stored information. It is most commonly used after adding or removing a buddy: * you should either add or remove the buddy ID# from the type 0x00c8 TLV in the additional data of * the parent group, and then send this SNAC containing the modified data. * Server should reply via SNAC(13,0E). */ if (cmd_add) { icq_send_snac(session, 0x13, 0x09, NULL, NULL, "U WWWW T", j->default_group_name, // default group name group, // Group# (guint16) 0, // Item# (guint16) 1, // Group record (guint16) buddies->len + 4, // Length of the additional data icq_pack_tlv(0xc8, buddies->str, buddies->len) // TLV(0xC8) contains the buddy ID#s of all buddies in the group ); } else { icq_send_snac(session, 0x13, 0x09, refdata, icq_cmd_addssi_ack, "U WWW WA", /*XXX*/ ekg_itoa(uid), // item name (uin) group, // Group# (guint16) iid, // Item# (guint16) 0, // Type of item: 0 -- Buddy record (guint16) data->len, // Length of the additional data data ); } icq_send_snac(session, 0x13, 0x12, NULL, NULL, ""); // Contacts edit end (finish transaction) g_string_free(data, TRUE); g_string_free(buddies, TRUE); } g_strfreev(argv); return 0; } static COMMAND(icq_command_delssi) { userlist_t *u; icq_private_t *j; private_data_t *refdata = NULL; guint32 u_id; guint16 iid = 0; guint16 group; GString *buddies; int i; if (params[0]) target = params[0]; if (!(u = userlist_find(session, target))) { printq("user_not_found", target); return -1; } else { iid = user_private_item_get_int(u, "iid"); group = user_private_item_get_int(u, "gid"); } if (!(u_id = icq_get_uid(session, target))) { printq("invalid_uid", target); return -1; } if ( !session || !(j = session->priv) ) /* WTF? */ return -1; /* send packets */ icq_send_snac(session, 0x13, 0x11, NULL, NULL, ""); // Contacts edit start (begin transaction) // data to server response handler private_item_set(&refdata, "cmd", "del"); private_item_set_int(&refdata, "uid", u_id); private_item_set_int(&refdata, "quiet", quiet); /* SNAC(13,0A) CLI_SSIxDELETE SSI edit: remove item * Client use this to delete items from server-side info. Server should reply via SNAC(13,0E) */ icq_send_snac(session, 0x13, 0x0A, refdata, icq_cmd_addssi_ack, "U WWW W", /*XXX*/ ekg_itoa(u_id), // item name (uin) group, // Group# (guint16) iid, // Item# (guint16) 0, // Type of item: 0 -- Buddy record (guint16) 0 // Length of the additional data ); buddies = g_string_new(NULL); for (u = session->userlist; u; u = u->next) { if (group == user_private_item_get_int(u, "gid")) { i = user_private_item_get_int(u, "iid"); if (iid != i) icq_pack_append(buddies, "W", i); } } icq_send_snac(session, 0x13, 0x09, NULL, NULL, // SSI edit: update group header "U WWWW T", j->default_group_name, // default group name group, // Group# (guint16) 0, // Item# (guint16) 1, // Group record (guint16) buddies->len + 4, // Length of the additional data icq_pack_tlv(0xc8, buddies->str, buddies->len) // TLV(0xC8) contains the buddy ID#s of all buddies in the group ); g_string_free(buddies, TRUE); icq_send_snac(session, 0x13, 0x12, NULL, NULL, ""); // Contacts edit end (finish transaction) return 0; } static void icq_send_msg_ch1(session_t *session, const char *uid, const char *message) { GString *pkt, *tlv_2, *tlv_101; userlist_t *u = userlist_find(session, uid); guint16 enc = 0; /* ASCII */ const char *tmp = message; while (*tmp) { if ((*tmp++ & 0x80)) { enc = 2; /* not ascii char */ break; } } if ( enc && !(u && user_private_item_get_int(u, "utf")) ) enc = 3; /* ANSI -- XXX ?wo? what should we do now? */ /* TLV(101) */ tlv_101 = icq_pack("WW", enc, 0x00); // encoding, codepage if (enc == 2) { /* send unicode message */ GString *recode = icq_convert_to_ucs2be((char *) message); g_string_append_len(tlv_101, recode->str, recode->len); g_string_free(recode, TRUE); } else { /* send ASCII/ANSII message */ g_string_append(tlv_101, message); } /* TLV(2) */ tlv_2 = icq_pack("tcT", icq_pack_tlv_char(0x501, 0x1), /* TLV(501) features, meaning unknown, duplicated from ICQ Lite */ icq_pack_tlv(0x0101, tlv_101->str, tlv_101->len)/* TLV(101) text TLV. */ ); g_string_free(tlv_101, TRUE); /* main packet */ pkt = icq_pack("iiWs", (guint32) rand(), (guint32) rand(), 1, uid+4); // msgid1, msgid2, channel, recipient icq_pack_append(pkt, "TTT", icq_pack_tlv(0x02, tlv_2->str, tlv_2->len), /* TLV(2) message-block */ icq_pack_tlv(0x03, NULL, 0), /* TLV(3) server-ack */ icq_pack_tlv(0x06, NULL, 0) /* TLV(6) received-offline */ ); g_string_free(tlv_2, TRUE); /* message-header */ icq_makesnac(session, pkt, 0x04, 0x06, NULL, NULL); // CLI_SEND_ICBM -- Send message thru server icq_send_pkt(session, pkt); } static void icq_send_msg_ch2(session_t *session, const char *uid, const char *message) { GString *pkt, *t5, *t2711; guint32 msgid1 = rand(), msgid2 = rand(); int prio = 1; icq_private_t *j = session->priv; int cookie = j->snac_seq++; t5 = g_string_new(NULL); icq_pack_append(t5, "WII", 0, msgid1, msgid2); // icq_pack_append_cap(t5, CAP_SRV_RELAY); icq_pack_append(t5, "tW", icq_pack_tlv_word(0x0a, 1)); icq_pack_append(t5, "T", icq_pack_tlv(0x0f, NULL, 0)); // empty TLV(0x0f) t2711 = g_string_new(NULL); { icq_pack_append_rendezvous(t2711, ICQ_VERSION, cookie, MTYPE_PLAIN, 0, 1, prio); char *recode = ekg_locale_to_utf8_dup(message); icq_pack_append_nullterm_msg(t2711, recode); xfree(recode); icq_pack_append(t2711, "II", 0, 0xffffffff); // XXX text & background colors icq_pack_append(t2711, "i", xstrlen(CAP_UTF8_str)); g_string_append(t2711, CAP_UTF8_str); } icq_pack_append(t5, "T", icq_pack_tlv(0x2711, t2711->str, t2711->len)); g_string_free(t2711, TRUE); /* main packet */ pkt = icq_pack("iiWs", msgid1, msgid2, 2, uid+4); // msgid1, msgid2, channel, recipient icq_pack_append(pkt, "T", icq_pack_tlv(0x05, t5->str, t5->len)); /* message-header */ icq_makesnac(session, pkt, 0x04, 0x06, NULL, NULL); // CLI_SEND_ICBM -- Send message thru server icq_send_pkt(session, pkt); } static COMMAND(icq_command_msg) { guint32 uin; char *uid; userlist_t *u; if (!xstrcmp(target, "*")) { if (msg_all(session, name, params[1]) == -1) printq("list_empty"); return 0; } if (!(uin = icq_get_uid(session, target))) { printq("invalid_uid", target); return -1; } uid = saprintf("icq:%u", uin); if (!session->connected) goto msgdisplay; if (config_last & 4) last_add(1, uid, time(NULL), 0, params[1]); { // send "typing finished" snac const char *sid = session_uid_get(session); int first = 0, len = 0; query_emit(NULL, "protocol-typing-out", &sid, &uid, &len, &first); } u = userlist_find(session, uid); if (u && (u->status != EKG_STATUS_NA) && (user_private_item_get_int(u, "caps") & 1<uid, rcpts, params[1], NULL, time(NULL), class, NULL, EKG_NO_BEEP, 0); g_strfreev(rcpts); /* XXX, it's copied from jabber-plugin, however i think we should _always_ add message to queue (if we're offline), even if we want do it quiet */ if (!session->connected) return msg_queue_add(session_uid_get(session), uid, params[1], "offline", class); } if (!quiet) session_unidle(session); return 0; } static COMMAND(icq_command_inline_msg) { const char *p[2] = { NULL, params[0] }; if (!params[0] || !target) return -1; return icq_command_msg(("msg"), p, session, target, quiet); } static COMMAND(icq_command_away) { const char *format; char *new_descr = NULL; int chg = 0, new_status; if (!xstrcmp(name, ("_autoback"))) { format = "auto_back"; new_status = EKG_STATUS_AUTOBACK; } else if (!xstrcmp(name, ("back"))) { format = "back"; new_status = EKG_STATUS_AVAIL; } else if (!xstrcmp(name, ("_autoaway"))) { format = "auto_away"; new_status = EKG_STATUS_AUTOAWAY; } else if (!xstrcmp(name, ("_autoxa"))) { format = "auto_xa"; new_status = EKG_STATUS_AUTOXA; } else if (!xstrcmp(name, ("away"))) { format = "away"; new_status = EKG_STATUS_AWAY; } else if (!xstrcmp(name, ("dnd"))) { format = "dnd"; new_status = EKG_STATUS_DND; } else if (!xstrcmp(name, ("ffc"))) { format = "ffc"; new_status = EKG_STATUS_FFC; } else if (!xstrcmp(name, ("xa"))) { format = "xa"; new_status = EKG_STATUS_XA; } else if (!xstrcmp(name, ("gone"))) { format = "gone"; new_status = EKG_STATUS_GONE; } else if (!xstrcmp(name, ("invisible"))) { format = "invisible"; new_status = EKG_STATUS_INVISIBLE; // XXX invisible is flag, not status } else return -1; if (params[0]) { if (xstrcmp(params[0], "-")) new_descr = xstrdup(params[0]); } else if (config_keep_reason) new_descr = xstrdup(session_descr_get(session)); if (xstrcmp(new_descr, session->descr)) { ekg2_reason_changed = 1; chg = 1; session_descr_set(session, new_descr); } if (new_descr) { char *f = saprintf("%s_descr", format); printq(f, new_descr, "", session_name(session)); xfree(f); } else printq(format, session_name(session)); xfree(new_descr); if (session->connected && chg) icq_write_status_msg(session); if (new_status != session_status_get(session)) { session_status_set(session, new_status); if ((new_status != EKG_STATUS_AUTOAWAY) && (new_status != EKG_STATUS_AUTOXA)) session_unidle(session); if (session->connected) icq_write_status(session); } ekg_update_status(session); return 0; } static COMMAND(icq_command_connect) { icq_private_t *j = session->priv; const char *hubserver; if (session->connecting) { printq("during_connect", session_name(session)); return -1; } if (session->connected) { printq("already_connected", session_name(session)); return -1; } /* proxy */ if (session_int_get(session, "proxy") == 1) { /* XXX, implement proxy connection */ debug_error("icq_command_connect() proxy?\n"); return -1; } /* hubserver */ if (!(hubserver = session_get(session, "server"))) hubserver = ICQ_HUB_SERVER; session->connecting = 1; j->ssi = 1; /* XXX */ j->aim = 1; /* XXX */ printq("connecting", session_name(session)); icq_connect(session, hubserver, ICQ_HUB_PORT); if ((session_status_get(session) == EKG_STATUS_NA)) session_status_set(session, EKG_STATUS_AVAIL); return 0; } static COMMAND(icq_command_disconnect) { if (timer_remove_session(session, "reconnect") == 0) { printq("auto_reconnect_removed", session_name(session)); return 0; } if (!session->connecting && !session->connected) { printq("not_connected", session_name(session)); return -1; } if (session->connecting) icq_handle_disconnect(session, NULL, EKG_DISCONNECT_STOPPED); else icq_handle_disconnect(session, params[0], EKG_DISCONNECT_USER); return 0; } static COMMAND(icq_command_reconnect) { if (session->connecting || session->connected) icq_command_disconnect(name, params, session, target, quiet); return icq_command_connect(name, params, session, target, quiet); } static COMMAND(icq_command_userinfo) { GString *pkt; guint32 number; int minimal_req = 0; /* XXX */ private_data_t *ref_data = NULL; if (!(number = icq_get_uid(session, target))) { printq("invalid_uid", target); return -1; } /* SNAC(15,02)/07D0/04B2 CLI_FULLINFO_REQUEST Request full user info * Client full userinfo request. Last reply snac flag bit1=0, other reply * packets have flags bit1=1 to inform client that more data follows. * Server should respond with following SNAC(15,03)/07DA subtype: * 00C8, 00DC, 00EB, 010E, 00D2, 00E6, 00F0, 00FA. */ /* SNAC(15,02)/07D0/04BA CLI_SHORTINFO_REQUEST Request short user info * * Client short userinfo request. ICQ2k use this request when message from * unknown user arrived. Server should respond with SNAC(15,03)/07DA/0104 */ /* XXX xookie */ private_item_set_int(&ref_data, "uid", number); pkt = icq_pack("i", number); icq_makemetasnac(session, pkt, CLI_META_INFO_REQ, (minimal_req == 0) ? CLI_FULLINFO_REQUEST : CLI_SHORTINFO_REQUEST, ref_data, NULL); icq_send_pkt(session, pkt); return 0; } static COMMAND(icq_command_searchuin) { GString *pkt; guint32 uin; debug_function("icq_command_searchuin() %s\n", params[0]); if (!(uin = icq_get_uid(session, target))) { printq("invalid_uid", target); return -1; } /* XXX, cookie */ /* SNAC(15,02)/07D0/0569 CLI_FIND_BY_UIN2 Search by uin request (tlv) * * This is client search by uin tlv-based request. Server should respond * with last search found record SNAC(15,03)/07DA/01AE because uin number * is unique. UIN tlv number is 0x0136. */ pkt = icq_pack("wwi", icq_pack_tlv_dword(0x0136, uin)); /* TLV_UID */ icq_makemetasnac(session, pkt, CLI_META_INFO_REQ, CLI_FIND_BY_UIN2, NULL, 0); icq_send_pkt(session, pkt); return 0; } static COMMAND(icq_command_search) { const char *city = NULL; const char *email = NULL; const char *nickname = NULL; const char *last_name = NULL; const char *first_name = NULL; int only_online = 0; int gender = 0; char **argv; int i; GString *pkt; argv = array_make(params[0], " \t", 0, 1, 1); for (i = 0; argv[i]; i++) { if (match_arg(argv[i], 'c', "city", 2) && argv[i+1]) { city = argv[++i]; continue; } if (match_arg(argv[i], 'e', "email", 2) && argv[i+1]) { email = argv[++i]; continue; } if (match_arg(argv[i], 'f', "firstname", 2) && argv[i+1]) { first_name = argv[++i]; continue; } if (match_arg(argv[i], 'n', "nickname", 2) && argv[i+1]) { nickname = argv[++i]; continue; } if (match_arg(argv[i], 'l', "lastname", 2) && argv[i+1]) { last_name = argv[++i]; continue; } if (!xstrcasecmp(argv[i], "--female")) { gender = 1; continue; } if (!xstrcasecmp(argv[i], "--male")) { gender = 2; continue; } if (!xstrcasecmp(argv[i], "--online")) { only_online = 1; continue; } /* XXX, madrzej? zgadywanie? */ printq("invalid_params", name, argv[i]); g_strfreev(argv); return -1; } /* XXX, cookie */ /* Pack the search details */ pkt = g_string_new(NULL); #define wo_idnhtni(type, str) \ { \ guint32 len = xstrlen(str); \ icq_pack_append(pkt, "www", (guint32) type, len+3, len+1); \ g_string_append_len(pkt, (char *) str, len+1); \ } if (first_name) wo_idnhtni(0x0140, first_name); /* TLV_FIRSTNAME */ if (last_name) wo_idnhtni(0x014A, last_name); /* TLV_LASTNAME */ if (nickname) wo_idnhtni(0x0154, nickname); /* TLV_NICKNAME */ if (email) wo_idnhtni(0x015E, email); /* TLV_EMAIL */ if (city) wo_idnhtni(0x0190, city); /* TLV_CITY */ #undef wo_idnhtni /* more options: searchPackTLVLNTS(&buf, &buflen, hwndDlg, IDC_STATE, TLV_STATE); searchPackTLVLNTS(&buf, &buflen, hwndDlg, IDC_COMPANY, TLV_COMPANY); searchPackTLVLNTS(&buf, &buflen, hwndDlg, IDC_DEPARTMENT, TLV_DEPARTMENT); searchPackTLVLNTS(&buf, &buflen, hwndDlg, IDC_POSITION, TLV_POSITION); searchPackTLVLNTS(&buf, &buflen, hwndDlg, IDC_KEYWORDS, TLV_KEYWORDS); ppackTLVDWord(&buf, &buflen, (DWORD)getCurItemData(hwndDlg, IDC_AGERANGE), TLV_AGERANGE, 0); */ if (gender) icq_pack_append(pkt, "wwc", icq_pack_tlv_char(0x017C, gender)); /* ppackTLVByte(&buf, &buflen, (BYTE)getCurItemData(hwndDlg, IDC_MARITALSTATUS), TLV_MARITAL, 0); ppackTLVWord(&buf, &buflen, (WORD)getCurItemData(hwndDlg, IDC_LANGUAGE), TLV_LANGUAGE, 0); ppackTLVWord(&buf, &buflen, (WORD)getCurItemData(hwndDlg, IDC_COUNTRY), TLV_COUNTRY, 0); ppackTLVWord(&buf, &buflen, (WORD)getCurItemData(hwndDlg, IDC_WORKFIELD), TLV_OCUPATION, 0); searchPackTLVWordLNTS(&buf, &buflen, hwndDlg, IDC_PASTKEY, (WORD)getCurItemData(hwndDlg, IDC_PASTCAT), TLV_PASTINFO); searchPackTLVWordLNTS(&buf, &buflen, hwndDlg, IDC_INTERESTSKEY, (WORD)getCurItemData(hwndDlg, IDC_INTERESTSCAT), TLV_INTERESTS); searchPackTLVWordLNTS(&buf, &buflen, hwndDlg, IDC_ORGKEYWORDS, (WORD)getCurItemData(hwndDlg, IDC_ORGANISATION), TLV_AFFILATIONS); searchPackTLVWordLNTS(&buf, &buflen, hwndDlg, IDC_HOMEPAGEKEY, (WORD)getCurItemData(hwndDlg, IDC_HOMEPAGECAT), TLV_HOMEPAGE); */ icq_pack_append(pkt, "wwc", icq_pack_tlv_char(0x0230, only_online)); /* SNAC(15,02)/07D0/055F CLI_WHITE_PAGES_SEARCH2 Whitepages search request (tlv) * * This is client tlv-based white pages search request used by ICQ2001+. * Server should respond with 1 or more packets. Last reply packet allways * SNAC(15,03)/07DA/01AE, other reply packets SNAC(15,03)/07DA/01A4. */ icq_makemetasnac(session, pkt, CLI_META_INFO_REQ, CLI_WHITE_PAGES_SEARCH2, NULL, 0); icq_send_pkt(session, pkt); g_strfreev(argv); return 0; } static COMMAND(icq_command_auth) { guint32 number; const char *reason = NULL; if (match_arg(params[0], 'l', "list", 2)) { userlist_t *u; for (u = session->userlist; u; u = u->next) { if (user_private_item_get_int(u, "auth") == 1) { printq("icq_user_info_generic", _("Waiting for authorization"), format_user(session, u->uid)); } } return 0; } if (params[1]) { target = params[1]; reason = params[2]; } else if (!target) { printq("not_enough_params", name); return -1; } if (!(number = icq_get_uid(session, target))) { printq("invalid_uid", target); return -1; } /* XXX, pending auth!!! like /auth -l in jabber */ /* XXX, reasons!! */ /* XXX, messages */ if (match_arg(params[0], 'r', "request", 2)) { if (!reason) reason = "Please add me."; icq_send_snac(session, 0x13, 0x18, 0, 0, "uUW", number, reason, (guint32) 0x00); return 0; } if (match_arg(params[0], 'c', "cancel", 2)) { icq_send_snac(session, 0x13, 0x16, 0, 0, "u", number); return 0; } if (match_arg(params[0], 'a', "accept", 2) || match_arg(params[0], 'd', "deny", 2)) { /* accept / deny */ int auth = (match_arg(params[0], 'a', "accept", 2) != 0); icq_send_snac(session, 0x13, 0x1a, 0, 0, "ucUW", number, (guint32) auth, reason ? reason : "", (guint32) 0x00); return 0; } printq("invalid_params", name, params[0]); return -1; } static COMMAND(icq_command_rates) { icq_private_t *j = session->priv; int i; for (i=0; i < j->n_rates; i++) { if (!i) print("icq_rates_header"); printq("icq_rates", ekg_itoa(i+1), ekg_itoa(j->rates[i]->win_size), ekg_itoa(j->rates[i]->clear_lvl), ekg_itoa(j->rates[i]->alert_lvl), ekg_itoa(j->rates[i]->limit_lvl), ekg_itoa(j->rates[i]->discn_lvl), ekg_itoa(j->rates[i]->curr_lvl), ekg_itoa(j->rates[i]->max_lvl)); } return 0; } static COMMAND(icq_command_whoami) { display_whoami(session); return 0; } static COMMAND(icq_command_register) { printq("generic_error", "Create a new ICQ account on http://lite.icq.com/register"); return 0; } static QUERY(icq_userlist_info_handle) { userlist_t *u = *va_arg(ap, userlist_t **); int quiet = *va_arg(ap, int *); const char *tmp; int i; if (!u || valid_plugin_uid(&icq_plugin, u->uid) != 1) return 1; if ( (i = user_private_item_get_int(u, "xstatus")) ) printq("icq_user_info_generic", _("xStatus"), icq_xstatus_name(i)); if ( (i = user_private_item_get_int(u, "online")) && (tmp = timestamp_time("%Y-%m-%d %H:%M", i)) ) printq("icq_user_info_generic", _("Online since"), tmp); if ( (i = user_private_item_get_int(u, "member")) && (tmp = timestamp_time("%Y-%m-%d %H:%M", i)) ) printq("icq_user_info_generic", _("ICQ Member since"), tmp); if ( (tmp = user_private_item_get(u, "comment")) ) printq("icq_user_info_generic", _("Comment"), tmp); if ( (tmp = user_private_item_get(u, "email")) ) printq("icq_user_info_generic", _("e-mail"), tmp); if ( user_private_item_get_int(u, "auth")) printq("icq_user_info_generic", _("Waiting for authorization"), "yes"); return 0; } static int icq_theme_init() { #ifndef NO_DEFAULT_THEME format_add("icq_auth_subscribe", _("%> (%1) %T%2%n asks for authorisation. Use \"/auth -a %2\" to accept, \"/auth -d %2 [reason]\" to refuse. Reason: %T%3%n"), 1); format_add("icq_auth_decline", _("%> (%1) %2 decline your authorization: %3\n"), 1); format_add("icq_auth_grant", _("%> (%1) %2 grant your authorization: %3\n"), 1); format_add("icq_userinfo_start", "%g,+=%G----- %g%3%G info for: %Ticq:%2%n", 1); format_add("icq_userinfo_affilations", "%g|| %n %T%3:%n %4", 1); format_add("icq_userinfo_basic", "%g|| %n %T%3:%n %4", 1); format_add("icq_userinfo_email", "%g|| %n %T%3:%n %4", 1); format_add("icq_userinfo_short", "%g|| %n %T%3:%n %4", 1); format_add("icq_userinfo_more", "%g|| %n %T%3:%n %4", 1); format_add("icq_userinfo_work", "%g|| %n %T%3:%n %4", 1); format_add("icq_userinfo_interests", "%g|| %n %T%3:%n %4", 1); format_add("icq_userinfo_notes", "%g|| %n %T%3:%n %4", 1); format_add("icq_userinfo_hpagecat", "%g|| %n %T%3:%n %4", 1); format_add("icq_userinfo_end", "%g`+=%G----- End%n", 1); format_add("icq_user_info_generic", "%K| %n%1: %T%2%n\n", 1); format_add("icq_rates_header", "%>%n # %K|%n Curr %K|%n Alrt %K|%n Limt %K|%n Clear %K|%n Dscn %K|%n Max %K|%nwin %K|%n\n", 1); format_add("icq_rates", "%>%n%[-2]1 %K|%n%[-5]7 %K|%n%[-5]4 %K|%n%[-5]5 %K|%n%[-6]3 %K|%n%[-5]6 %K|%n%[-5]8 %K|%n%[-3]2 %K|%n\n", 1); format_add("icq_you_were_added", "%> (%1) %2 adds you to contact list\n", 1); format_add("icq_window_closed", "%> %1 has closed the message window.\n", 1); #endif return 0; } static void icq_changed_our_security(session_t *s, const char *var) { const char *val; icq_private_t *j; int webaware; if (!s || !(j = s->priv)) return; if (!(val = session_get(s, var)) || !*val) return; if ((webaware = !xstrcasecmp(var, "webaware")) || !xstrcasecmp(var, "require_auth")) { icq_set_security(s); if (webaware) icq_write_status(s); } else if (!xstrcasecmp(var, "hide_ip")) { if (*val & 1) { j->status_flags |= STATUS_DCAUTH; j->status_flags &= ~STATUS_SHOWIP; } else { j->status_flags |= STATUS_SHOWIP; j->status_flags &= ~STATUS_DCAUTH; } icq_write_status(s); } } static plugins_params_t icq_plugin_vars[] = { PLUGIN_VAR_ADD("alias", VAR_STR, NULL, 0, NULL), PLUGIN_VAR_ADD("auto_away", VAR_INT, "600", 0, NULL), PLUGIN_VAR_ADD("auto_away_descr", VAR_STR, 0, 0, NULL), PLUGIN_VAR_ADD("auto_back", VAR_INT, "0", 0, NULL), PLUGIN_VAR_ADD("auto_connect", VAR_BOOL, "0", 0, NULL), PLUGIN_VAR_ADD("auto_reconnect", VAR_INT, "0", 0, NULL), PLUGIN_VAR_ADD("auto_xa", VAR_INT, "0", 0, NULL), PLUGIN_VAR_ADD("auto_xa_descr", VAR_STR, 0, 0, NULL), PLUGIN_VAR_ADD("log_formats", VAR_STR, "xml,simple,sqlite", 0, NULL), PLUGIN_VAR_ADD("password", VAR_STR, NULL, 1, NULL), PLUGIN_VAR_ADD("plaintext_passwd", VAR_BOOL, "0", 0, NULL), PLUGIN_VAR_ADD("server", VAR_STR, NULL, 0, NULL), PLUGIN_VAR_ADD("hide_ip", VAR_BOOL, "1", 0, icq_changed_our_security), PLUGIN_VAR_ADD("require_auth", VAR_BOOL, "1", 0, icq_changed_our_security), PLUGIN_VAR_ADD("webaware", VAR_BOOL, "0", 0, icq_changed_our_security), PLUGIN_VAR_END() }; static const char *icq_protocols[] = { "icq:", NULL }; static const status_t icq_statuses[] = { EKG_STATUS_NA, EKG_STATUS_GONE, EKG_STATUS_DND, EKG_STATUS_XA, EKG_STATUS_AWAY, EKG_STATUS_AVAIL, EKG_STATUS_FFC, EKG_STATUS_INVISIBLE, EKG_STATUS_NULL }; static const struct protocol_plugin_priv icq_priv = { .protocols = icq_protocols, .statuses = icq_statuses }; EXPORT int icq_plugin_init(int prio) { #define ICQ_ONLY SESSION_MUSTBELONG | SESSION_MUSTHASPRIVATE #define ICQ_FLAGS ICQ_ONLY | SESSION_MUSTBECONNECTED #define ICQ_FLAGS_TARGET ICQ_FLAGS | COMMAND_ENABLEREQPARAMS | COMMAND_PARAMASTARGET #define ICQ_FLAGS_MSG ICQ_ONLY | COMMAND_ENABLEREQPARAMS | COMMAND_PARAMASTARGET PLUGIN_CHECK_VER("icq"); icq_convert_string_init(); ekg_recode_utf8_inc(); icq_plugin.params = icq_plugin_vars; icq_plugin.priv = &icq_priv; plugin_register(&icq_plugin, prio); query_connect(&icq_plugin, "protocol-validate-uid", icq_validate_uid, NULL); query_connect(&icq_plugin, "plugin-print-version", icq_print_version, NULL); query_connect(&icq_plugin, "session-added", icq_session_init, NULL); query_connect(&icq_plugin, "session-removed", icq_session_deinit, NULL); query_connect(&icq_plugin, "userlist-info", icq_userlist_info_handle, NULL); query_connect(&icq_plugin, "protocol-typing-out", icq_typing_out, NULL); variable_add(&icq_plugin, ("disable_chatstates"), VAR_BOOL, 1, &icq_config_disable_chatstates, NULL, NULL, NULL); command_add(&icq_plugin, "icq:", "?", icq_command_inline_msg, ICQ_ONLY | COMMAND_PASS_UNCHANGED, NULL); command_add(&icq_plugin, "icq:msg", "!uU !", icq_command_msg, ICQ_FLAGS_MSG, NULL); command_add(&icq_plugin, "icq:chat", "!uU !", icq_command_msg, ICQ_FLAGS_MSG, NULL); command_add(&icq_plugin, "icq:addssi", "!p ?", icq_command_addssi, ICQ_FLAGS, "-p --phone -c --comment -e --email -n --nick"); command_add(&icq_plugin, "icq:delssi", "!u ?", icq_command_delssi, ICQ_FLAGS_TARGET, NULL); command_add(&icq_plugin, "icq:modify", "!u ?", icq_command_addssi, ICQ_FLAGS_TARGET, "-p --phone -c --comment -e --email -n --nick"); command_add(&icq_plugin, "icq:auth", "!p uU ?", icq_command_auth, ICQ_FLAGS | COMMAND_ENABLEREQPARAMS, "-a --accept -d --deny -l --list -r --request -c --cancel"); command_add(&icq_plugin, "icq:away", "r", icq_command_away, ICQ_ONLY, NULL); command_add(&icq_plugin, "icq:back", "r", icq_command_away, ICQ_ONLY, NULL); command_add(&icq_plugin, "icq:dnd", "r", icq_command_away, ICQ_ONLY, NULL); command_add(&icq_plugin, "icq:ffc", "r", icq_command_away, ICQ_ONLY, NULL); command_add(&icq_plugin, "icq:gone", "r", icq_command_away, ICQ_ONLY, NULL); command_add(&icq_plugin, "icq:invisible", NULL, icq_command_away, ICQ_ONLY, NULL); command_add(&icq_plugin, "icq:xa", "r", icq_command_away, ICQ_ONLY, NULL); command_add(&icq_plugin, "icq:_autoaway", "?", icq_command_away, ICQ_ONLY, NULL); command_add(&icq_plugin, "icq:_autoback", "?", icq_command_away, ICQ_ONLY, NULL); command_add(&icq_plugin, "icq:_autoxa", "?", icq_command_away, ICQ_ONLY, NULL); command_add(&icq_plugin, "icq:userinfo", "!u", icq_command_userinfo, ICQ_FLAGS_TARGET, NULL); command_add(&icq_plugin, "icq:register", NULL, icq_command_register, 0, NULL); /* XXX, makes generic icq:search */ command_add(&icq_plugin, "icq:searchuin", "!u", icq_command_searchuin, ICQ_FLAGS_TARGET, NULL); command_add(&icq_plugin, "icq:search", "!p", icq_command_search, ICQ_FLAGS | COMMAND_ENABLEREQPARAMS, NULL); command_add(&icq_plugin, "icq:connect", NULL, icq_command_connect, ICQ_ONLY, NULL); command_add(&icq_plugin, "icq:disconnect", "r", icq_command_disconnect, ICQ_ONLY, NULL); command_add(&icq_plugin, "icq:reconnect", NULL, icq_command_reconnect, ICQ_ONLY, NULL); command_add(&icq_plugin, "icq:whoami", NULL, icq_command_whoami, ICQ_ONLY, NULL); command_add(&icq_plugin, "icq:_rates", NULL, icq_command_rates, ICQ_ONLY, NULL); return 0; } static int icq_plugin_destroy() { icq_convert_string_destroy(); plugin_unregister(&icq_plugin); ekg_recode_utf8_dec(); return 0; } ekg2-0.4~pre+20120506.1/plugins/icq/icq.h000066400000000000000000000040431175142753400173400ustar00rootroot00000000000000#ifndef __ICQ_ICQ_H #define __ICQ_ICQ_H #define SNAC_HANDLER(x) int x(session_t *s, guint16 cmd, unsigned char *buf, int len, private_data_t *data) typedef int (*snac_handler_t) (session_t *, guint16 cmd, unsigned char *, int, private_data_t * ); #define SNAC_SUBHANDLER(x) int x(session_t *s, unsigned char *buf, int len, private_data_t *data) typedef int (*snac_subhandler_t) (session_t *s, unsigned char *, int, private_data_t * ); typedef struct { int win_size; // Window size int clear_lvl; // Clear level int alert_lvl; // Alert level int limit_lvl; // Limit level int discn_lvl; // Disconnect level int curr_lvl; // Current level int max_lvl; // Max level time_t last_time; // Last time int n_groups; guint32 *groups; } icq_rate_t; typedef struct icq_snac_reference_list_s { struct icq_snac_reference_list_s *next; int ref; time_t timestamp; snac_subhandler_t subhandler; private_data_t *list; } icq_snac_reference_list_t; typedef struct { GCancellable *connect_cancellable; GDataOutputStream *send_stream; int flap_seq; /* FLAP seq id */ guint16 snac_seq; /* SNAC seq id */ int snacmeta_seq; /* META SNAC seq id */ int cookie_seq; /* Cookie seq id */ int ssi; /* server-side-userlist? */ int migrate; /* client migration sequence */ int aim; /* aim-ok? */ int default_group_id; /* XXX ?wo? TEMP! We should support list of groups */ int status_flags; int xstatus; /* XXX ?wo? set it! */ private_data_t *whoami; char *default_group_name; GString *cookie; /* connection login cookie */ GString *stream_buf; icq_snac_reference_list_t *snac_ref_list; int n_rates; icq_rate_t **rates; } icq_private_t; int icq_send_pkt(session_t *s, GString *buf); void icq_session_connected(session_t *s); int icq_write_status(session_t *s); void icq_handle_disconnect(session_t *s, const char *reason, int type); void icq_connect(session_t *session, const char *server, int port); #define icq_uid(target) protocol_uid("icq", target) #define MIRANDAOK 1 #define MIRANDA_COMPILANT_CLIENT 1 #define ICQ_DEBUG_UNUSED_INFORMATIONS 1 #endif ekg2-0.4~pre+20120506.1/plugins/icq/icq_caps.c000066400000000000000000000300201175142753400203330ustar00rootroot00000000000000/* * (C) Copyright 2006-2008 Jakub Zawadzki * 2008 Wiesaw Ochmiski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #include "icq_caps.h" typedef unsigned char _capability_t[16]; static const _capability_t _caps[CAP_UNKNOWN+1] = { {0x01, 0x38, 0xca, 0x7b, 0x76, 0x9a, 0x49, 0x15, 0x88, 0xf2, 0x13, 0xfc, 0x00, 0x97, 0x9e, 0xa8}, // CAP_HTML {0x09, 0x46, 0x00, 0x00, 0x4c, 0x7f, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, // CAP_NEWCAPS {0x09, 0x46, 0x13, 0x41, 0x4c, 0x7f, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, // CAP_VOICE {0x09, 0x46, 0x13, 0x42, 0x4c, 0x7f, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, // CAP_AIMDIRPLAY {0x09, 0x46, 0x13, 0x43, 0x4c, 0x7f, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, // CAP_SENDFILE {0x09, 0x46, 0x13, 0x44, 0x4c, 0x7f, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, // CAP_ICQDIRECT {0x09, 0x46, 0x13, 0x45, 0x4c, 0x7f, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, // CAP_IMIMAGE {0x09, 0x46, 0x13, 0x46, 0x4c, 0x7f, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, // CAP_BUDDYICON {0x09, 0x46, 0x13, 0x47, 0x4c, 0x7f, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, // CAP_SAVESTOCKS {0x09, 0x46, 0x13, 0x48, 0x4c, 0x7f, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, // CAP_GETFILE {0x09, 0x46, 0x13, 0x49, 0x4c, 0x7f, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, // CAP_SRV_RELAY {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1, 0x22, 0x82, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, // CAP_GAMES2 {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, // CAP_GAMES {0x09, 0x46, 0x13, 0x4b, 0x4c, 0x7f, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, // CAP_CONTACTS {0x09, 0x46, 0x13, 0x4c, 0x4c, 0x7f, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, // CAP_DEVILS {0x09, 0x46, 0x13, 0x4d, 0x4d, 0x7f, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, // CAP_INTEROPERATE {0x09, 0x46, 0x13, 0x4e, 0x4c, 0x7f, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, // CAP_UTF {0x1a, 0x09, 0x3c, 0x6c, 0xd7, 0xfd, 0x4e, 0xc5, 0x9d, 0x51, 0xa6, 0x47, 0x4e, 0x34, 0xf5, 0xa0}, // CAP_XTRAZ {0x56, 0x3f, 0xc8, 0x09, 0x0b, 0x6f, 0x41, 0xbd, 0x9f, 0x79, 0x42, 0x26, 0x09, 0xdf, 0xa2, 0xf3}, // CAP_TYPING {0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}, // CAP_CHAT {0x97, 0xb1, 0x27, 0x51, 0x24, 0x3c, 0x43, 0x34, 0xad, 0x22, 0xd6, 0xab, 0xf7, 0x3f, 0x14, 0x92}, // CAP_RTF {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} // CAP_UNKNOWN }; int icq_cap_id(unsigned char *buf) { int i; if (!buf) return CAP_UNKNOWN; for (i=0; i0) && (id0) && (id=CAP_UNKNOWN) || (cap_id < 0)) { debug_error("icq_pack_append_cap() - unknown cap id: 0x%x\n", cap_id); return; } g_string_append_len(pkt, (char *) _caps[cap_id], 0x10); } /* * xStatuses * */ static const _capability_t _capXStatus[XSTATUS_COUNT] = { {0x63, 0x62, 0x73, 0x37, 0xa0, 0x3f, 0x49, 0xff, 0x80, 0xe5, 0xf7, 0x09, 0xcd, 0xe0, 0xa4, 0xee}, {0x5a, 0x58, 0x1e, 0xa1, 0xe5, 0x80, 0x43, 0x0c, 0xa0, 0x6f, 0x61, 0x22, 0x98, 0xb7, 0xe4, 0xc7}, {0x83, 0xc9, 0xb7, 0x8e, 0x77, 0xe7, 0x43, 0x78, 0xb2, 0xc5, 0xfb, 0x6c, 0xfc, 0xc3, 0x5b, 0xec}, {0xe6, 0x01, 0xe4, 0x1c, 0x33, 0x73, 0x4b, 0xd1, 0xbc, 0x06, 0x81, 0x1d, 0x6c, 0x32, 0x3d, 0x81}, {0x8c, 0x50, 0xdb, 0xae, 0x81, 0xed, 0x47, 0x86, 0xac, 0xca, 0x16, 0xcc, 0x32, 0x13, 0xc7, 0xb7}, {0x3f, 0xb0, 0xbd, 0x36, 0xaf, 0x3b, 0x4a, 0x60, 0x9e, 0xef, 0xcf, 0x19, 0x0f, 0x6a, 0x5a, 0x7f}, {0xf8, 0xe8, 0xd7, 0xb2, 0x82, 0xc4, 0x41, 0x42, 0x90, 0xf8, 0x10, 0xc6, 0xce, 0x0a, 0x89, 0xa6}, {0x80, 0x53, 0x7d, 0xe2, 0xa4, 0x67, 0x4a, 0x76, 0xb3, 0x54, 0x6d, 0xfd, 0x07, 0x5f, 0x5e, 0xc6}, {0xf1, 0x8a, 0xb5, 0x2e, 0xdc, 0x57, 0x49, 0x1d, 0x99, 0xdc, 0x64, 0x44, 0x50, 0x24, 0x57, 0xaf}, {0x1b, 0x78, 0xae, 0x31, 0xfa, 0x0b, 0x4d, 0x38, 0x93, 0xd1, 0x99, 0x7e, 0xee, 0xaf, 0xb2, 0x18}, {0x61, 0xbe, 0xe0, 0xdd, 0x8b, 0xdd, 0x47, 0x5d, 0x8d, 0xee, 0x5f, 0x4b, 0xaa, 0xcf, 0x19, 0xa7}, {0x48, 0x8e, 0x14, 0x89, 0x8a, 0xca, 0x4a, 0x08, 0x82, 0xaa, 0x77, 0xce, 0x7a, 0x16, 0x52, 0x08}, {0x10, 0x7a, 0x9a, 0x18, 0x12, 0x32, 0x4d, 0xa4, 0xb6, 0xcd, 0x08, 0x79, 0xdb, 0x78, 0x0f, 0x09}, {0x6f, 0x49, 0x30, 0x98, 0x4f, 0x7c, 0x4a, 0xff, 0xa2, 0x76, 0x34, 0xa0, 0x3b, 0xce, 0xae, 0xa7}, {0x12, 0x92, 0xe5, 0x50, 0x1b, 0x64, 0x4f, 0x66, 0xb2, 0x06, 0xb2, 0x9a, 0xf3, 0x78, 0xe4, 0x8d}, {0xd4, 0xa6, 0x11, 0xd0, 0x8f, 0x01, 0x4e, 0xc0, 0x92, 0x23, 0xc5, 0xb6, 0xbe, 0xc6, 0xcc, 0xf0}, {0x60, 0x9d, 0x52, 0xf8, 0xa2, 0x9a, 0x49, 0xa6, 0xb2, 0xa0, 0x25, 0x24, 0xc5, 0xe9, 0xd2, 0x60}, {0x1f, 0x7a, 0x40, 0x71, 0xbf, 0x3b, 0x4e, 0x60, 0xbc, 0x32, 0x4c, 0x57, 0x87, 0xb0, 0x4c, 0xf1}, {0x78, 0x5e, 0x8c, 0x48, 0x40, 0xd3, 0x4c, 0x65, 0x88, 0x6f, 0x04, 0xcf, 0x3f, 0x3f, 0x43, 0xdf}, {0xa6, 0xed, 0x55, 0x7e, 0x6b, 0xf7, 0x44, 0xd4, 0xa5, 0xd4, 0xd2, 0xe7, 0xd9, 0x5c, 0xe8, 0x1f}, {0x12, 0xd0, 0x7e, 0x3e, 0xf8, 0x85, 0x48, 0x9e, 0x8e, 0x97, 0xa7, 0x2a, 0x65, 0x51, 0xe5, 0x8d}, {0xba, 0x74, 0xdb, 0x3e, 0x9e, 0x24, 0x43, 0x4b, 0x87, 0xb6, 0x2f, 0x6b, 0x8d, 0xfe, 0xe5, 0x0f}, {0x63, 0x4f, 0x6b, 0xd8, 0xad, 0xd2, 0x4a, 0xa1, 0xaa, 0xb9, 0x11, 0x5b, 0xc2, 0x6d, 0x05, 0xa1}, {0x01, 0xd8, 0xd7, 0xee, 0xac, 0x3b, 0x49, 0x2a, 0xa5, 0x8d, 0xd3, 0xd8, 0x77, 0xe6, 0x6b, 0x92}, {0x2c, 0xe0, 0xe4, 0xe5, 0x7c, 0x64, 0x43, 0x70, 0x9c, 0x3a, 0x7a, 0x1c, 0xe8, 0x78, 0xa7, 0xdc}, {0x10, 0x11, 0x17, 0xc9, 0xa3, 0xb0, 0x40, 0xf9, 0x81, 0xac, 0x49, 0xe1, 0x59, 0xfb, 0xd5, 0xd4}, {0x16, 0x0c, 0x60, 0xbb, 0xdd, 0x44, 0x43, 0xf3, 0x91, 0x40, 0x05, 0x0f, 0x00, 0xe6, 0xc0, 0x09}, {0x64, 0x43, 0xc6, 0xaf, 0x22, 0x60, 0x45, 0x17, 0xb5, 0x8c, 0xd7, 0xdf, 0x8e, 0x29, 0x03, 0x52}, {0x16, 0xf5, 0xb7, 0x6f, 0xa9, 0xd2, 0x40, 0x35, 0x8c, 0xc5, 0xc0, 0x84, 0x70, 0x3c, 0x98, 0xfa}, {0x63, 0x14, 0x36, 0xff, 0x3f, 0x8a, 0x40, 0xd0, 0xa5, 0xcb, 0x7b, 0x66, 0xe0, 0x51, 0xb3, 0x64}, {0xb7, 0x08, 0x67, 0xf5, 0x38, 0x25, 0x43, 0x27, 0xa1, 0xff, 0xcf, 0x4c, 0xc1, 0x93, 0x97, 0x97}, {0xdd, 0xcf, 0x0e, 0xa9, 0x71, 0x95, 0x40, 0x48, 0xa9, 0xc6, 0x41, 0x32, 0x06, 0xd6, 0xf2, 0x80} }; const char* _nameXStatus[XSTATUS_COUNT] = { ("Shopping"), "Taking a bath", "Tired", "Party", "Drinking beer", "Thinking", "Eating", "Watching TV", "Meeting", "Coffee", "Listening to music", "Business", "Shooting", "Having fun", "On the phone", "Gaming", "Studying", "Feeling sick", "Sleeping", "Surfing", "Browsing", "Working", "Typing", "Angry", "Picnic", "Cooking", "Smoking", "I'm high", "On WC", "To be or not to be", "Watching pro7 on TV", "Love"}; const char *icq_xstatus_name(int id) { if ((id <=0) || (id > XSTATUS_COUNT)) return "unknown"; return _nameXStatus[id-1]; } int icq_xstatus_id(unsigned char *buf) { int i; if (!buf) return 0; for (i=0; i XSTATUS_COUNT)) return; g_string_append_len(pkt, (char *) _capXStatus[x_id-1], 0x10); } static const _capability_t _plugins[PLUGIN_UNKNOWN] = { {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // PSIG_MESSAGE {0xd1, 0x40, 0xcf, 0x10, 0xe9, 0x4f, 0x11, 0xd3, 0xbc, 0xd2, 0x00, 0x04, 0xac, 0x96, 0xdd, 0x96}, // PSIG_STATUS_PLUGIN - Status manager plugin {0x37, 0x3f, 0xe9, 0xa0, 0xe9, 0x4f, 0x11, 0xd3, 0xbc, 0xd2, 0x00, 0x04, 0xac, 0x96, 0xdd, 0x96}, // PSIG_INFO_PLUGIN - Info manager plugin {0x05, 0x73, 0x6b, 0xbe, 0xc2, 0x0f, 0x4f, 0x10, 0xa6, 0xde, 0x4d, 0xb1, 0xe3, 0x56, 0x4b, 0x0e}, // MGTYPE_MESSAGE - Message plugin {0xd9, 0x12, 0x2d, 0xf0, 0x91, 0x30, 0x11, 0xd3, 0x8d, 0xd7, 0x00, 0x10, 0x4b, 0x06, 0x46, 0x2e}, // MGTYPE_FILE - File transfer plugin {0x72, 0x58, 0x1c, 0x37, 0x87, 0xe9, 0x11, 0xd4, 0xa4, 0xc1, 0x00, 0xd0, 0xb7, 0x59, 0xb1, 0xd9}, // MGTYPE_WEBURL - URL plugin {0xb2, 0x20, 0xf7, 0xbf, 0x8e, 0x37, 0x11, 0xd4, 0xbd, 0x28, 0x00, 0x04, 0xac, 0x96, 0xd9, 0x05}, // MGTYPE_CHAT - Chat plugin {0x46, 0x7d, 0x0e, 0x2a, 0x76, 0x76, 0x11, 0xd4, 0xbc, 0xe6, 0x00, 0x04, 0xac, 0x96, 0x1e, 0xa6}, // MGTYPE_CONTACTS - Send contact list plugin {0x00, 0xf6, 0x28, 0x0e, 0xe7, 0x11, 0x11, 0xd3, 0xbc, 0xf3, 0x00, 0x04, 0xac, 0x96, 0x9d, 0xc2}, // MGTYPE_SMS_MESSAGE - SMS plugin {0x48, 0x3b, 0xe5, 0x01, 0x11, 0xd1, 0xe4, 0x2a, 0x60, 0x00, 0x79, 0xb6, 0x94, 0xe2, 0xe1, 0x97}, // MGTYPE_GREETING_CARD {0xed, 0x2d, 0xed, 0x47, 0x1f, 0xf2, 0x11, 0xd4, 0xbc, 0xfd, 0x00, 0x06, 0x29, 0xee, 0x4d, 0xa1}, // User info plugin {0x2c, 0x21, 0x7c, 0x90, 0x4d, 0x91, 0x11, 0xd3, 0xad, 0xeb, 0x00, 0x04, 0xac, 0x96, 0xaa, 0xb2}, // Phone info plugin {0x5c, 0x1e, 0x1e, 0x50, 0xdd, 0x9e, 0x11, 0xd3, 0xab, 0x1f, 0x00, 0x50, 0x04, 0x8e, 0xbc, 0x8d}, // White search plugin {0x2c, 0xf8, 0x6d, 0x50, 0xde, 0x72, 0x11, 0xd3, 0xab, 0x21, 0x00, 0x50, 0x04, 0x8e, 0xbc, 0x8d}, // Search plugin {0xfd, 0xf5, 0xa1, 0x3b, 0x6e, 0xd2, 0x40, 0x3f, 0x86, 0xe0, 0xb4, 0x84, 0x6b, 0x77, 0xdf, 0xa7}, // Features list plugin {0xd6, 0x8e, 0x4b, 0x1c, 0x02, 0xe7, 0x11, 0xd4, 0xbc, 0xe8, 0x00, 0x04, 0xac, 0x96, 0xd9, 0x05}, // Ext contacts plugin {0x3d, 0xa8, 0xf1, 0x60, 0x49, 0x91, 0x11, 0xd3, 0x8d, 0xbe, 0x00, 0x10, 0x4b, 0x06, 0x46, 0x2e}, // Random users service {0xfb, 0x9f, 0x72, 0xc0, 0x56, 0x78, 0x11, 0xd3, 0x8d, 0xc2, 0x00, 0x10, 0x4b, 0x06, 0x46, 0x2e}, // Random plugin {0x5a, 0x88, 0x1d, 0x65, 0x2a, 0x73, 0x11, 0xd4, 0xbd, 0x0a, 0x00, 0x06, 0x29, 0xee, 0x4d, 0xa1}, // Wireless pager plugin {0x39, 0x34, 0x36, 0xbc, 0x07, 0xa4, 0x40, 0xa2, 0x90, 0x0c, 0x35, 0xa9, 0xf0, 0x03, 0xbe, 0x09}, // External plugin {0xfa, 0xda, 0x17, 0x86, 0x7e, 0x36, 0x4e, 0xa0, 0x8e, 0x1b, 0xc6, 0xb3, 0xbd, 0x0e, 0x51, 0x5c}, // Add user wizard plugin {0xc0, 0x31, 0xd0, 0xd1, 0x31, 0x2c, 0x11, 0xd2, 0x8a, 0x09, 0x00, 0x10, 0x4b, 0x9b, 0x48, 0xab}, // Voice message plugin {0x1c, 0xc9, 0x13, 0xa1, 0x7e, 0x1e, 0x11, 0xd2, 0xac, 0x9f, 0x00, 0x10, 0x4b, 0xbc, 0x2b, 0x53}, // IRCQ plugin }; int icq_plugin_id(unsigned char *buf) { int i; if (!buf) return PLUGIN_UNKNOWN; for (i=0; icode != -1 && f->table; f++) { if (f->code != family) continue; cmsg = f->table; } if (cmsg) { for ( ; (cmsg->code != -1) && cmsg->text; cmsg++) { if (cmsg->code == cmd) { return cmsg->text; } } } debug_error("icq_snac_name() Unknown SNAC(0x%x, 0x%x) name.\n", family, cmd); return NULL; } #endif // vim:syn=c ekg2-0.4~pre+20120506.1/plugins/icq/icq_fieldnames.inc000066400000000000000000000310701175142753400220510ustar00rootroot00000000000000// ---------------------------------------------------------------------------80 // ICQ plugin for Miranda Instant Messenger // ________________________________________ // // Copyright Š 2000-2001 Richard Hughes, Roland Rabien, Tristan Van de Vreede // Copyright Š 2001-2002 Jon Keating, Richard Hughes // Copyright Š 2002-2004 Martin berg, Sam Kothari, Robert Rainwater // Copyright Š 2004-2008 Joe Kucera // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. // // File name : $URL: http://miranda.svn.sourceforge.net/svnroot/miranda/trunk/miranda/protocols/IcqOscarJ/icq_fieldnames.cpp $ // Revision : $Revision: 7484 $ // Last change by : $Author: ghazan $ #define LPGEN(x) x struct fieldnames_t interestsField[]={ {100, LPGEN("Art")}, {101, LPGEN("Cars")}, {102, LPGEN("Celebrity Fans")}, {103, LPGEN("Collections")}, {104, LPGEN("Computers")}, {105, LPGEN("Culture & Literature")}, {106, LPGEN("Fitness")}, {107, LPGEN("Games")}, {108, LPGEN("Hobbies")}, {109, LPGEN("ICQ - Providing Help")}, {110, LPGEN("Internet")}, {111, LPGEN("Lifestyle")}, {112, LPGEN("Movies/TV")}, {113, LPGEN("Music")}, {114, LPGEN("Outdoor Activities")}, {115, LPGEN("Parenting")}, {116, LPGEN("Pets/Animals")}, {117, LPGEN("Religion")}, {118, LPGEN("Science/Technology")}, {119, LPGEN("Skills")}, {120, LPGEN("Sports")}, {121, LPGEN("Web Design")}, {122, LPGEN("Nature and Environment")}, {123, LPGEN("News & Media")}, {124, LPGEN("Government")}, {125, LPGEN("Business & Economy")}, {126, LPGEN("Mystics")}, {127, LPGEN("Travel")}, {128, LPGEN("Astronomy")}, {129, LPGEN("Space")}, {130, LPGEN("Clothing")}, {131, LPGEN("Parties")}, {132, LPGEN("Women")}, {133, LPGEN("Social science")}, {134, LPGEN("60's")}, {135, LPGEN("70's")}, {136, LPGEN("80's")}, {137, LPGEN("50's")}, {138, LPGEN("Finance and corporate")}, {139, LPGEN("Entertainment")}, {140, LPGEN("Consumer electronics")}, {141, LPGEN("Retail stores")}, {142, LPGEN("Health and beauty")}, {143, LPGEN("Media")}, {144, LPGEN("Household products")}, {145, LPGEN("Mail order catalog")}, {146, LPGEN("Business services")}, {147, LPGEN("Audio and visual")}, {148, LPGEN("Sporting and athletic")}, {149, LPGEN("Publishing")}, {150, LPGEN("Home automation")}, {-1, NULL}}; struct fieldnames_t languageField[]={ {1, LPGEN("Arabic")}, {2, LPGEN("Bhojpuri")}, {3, LPGEN("Bulgarian")}, {4, LPGEN("Burmese")}, {5, LPGEN("Cantonese")}, {6, LPGEN("Catalan")}, {7, LPGEN("Chinese")}, {8, LPGEN("Croatian")}, {9, LPGEN("Czech")}, {10, LPGEN("Danish")}, {11, LPGEN("Dutch")}, {12, LPGEN("English")}, {13, LPGEN("Esperanto")}, {14, LPGEN("Estonian")}, {15, LPGEN("Farci")}, {16, LPGEN("Finnish")}, {17, LPGEN("French")}, {18, LPGEN("Gaelic")}, {19, LPGEN("German")}, {20, LPGEN("Greek")}, {21, LPGEN("Hebrew")}, {22, LPGEN("Hindi")}, {23, LPGEN("Hungarian")}, {24, LPGEN("Icelandic")}, {25, LPGEN("Indonesian")}, {26, LPGEN("Italian")}, {27, LPGEN("Japanese")}, {28, LPGEN("Khmer")}, {29, LPGEN("Korean")}, {30, LPGEN("Lao")}, {31, LPGEN("Latvian")}, {32, LPGEN("Lithuanian")}, {33, LPGEN("Malay")}, {34, LPGEN("Norwegian")}, {35, LPGEN("Polish")}, {36, LPGEN("Portuguese")}, {37, LPGEN("Romanian")}, {38, LPGEN("Russian")}, {39, LPGEN("Serbo-Croatian")}, {40, LPGEN("Slovak")}, {41, LPGEN("Slovenian")}, {42, LPGEN("Somali")}, {43, LPGEN("Spanish")}, {44, LPGEN("Swahili")}, {45, LPGEN("Swedish")}, {46, LPGEN("Tagalog")}, {47, LPGEN("Tatar")}, {48, LPGEN("Thai")}, {49, LPGEN("Turkish")}, {50, LPGEN("Ukrainian")}, {51, LPGEN("Urdu")}, {52, LPGEN("Vietnamese")}, {53, LPGEN("Yiddish")}, {54, LPGEN("Yoruba")}, {55, LPGEN("Afrikaans")}, {56, LPGEN("Bosnian")}, {57, LPGEN("Persian")}, {58, LPGEN("Albanian")}, {59, LPGEN("Armenian")}, {60, LPGEN("Punjabi")}, {61, LPGEN("Chamorro")}, {62, LPGEN("Mongolian")}, {63, LPGEN("Mandarin")}, {64, LPGEN("Taiwaness")}, {65, LPGEN("Macedonian")}, {66, LPGEN("Sindhi")}, {67, LPGEN("Welsh")}, {68, LPGEN("Azerbaijani")}, {69, LPGEN("Kurdish")}, {70, LPGEN("Gujarati")}, {71, LPGEN("Tamil")}, {72, LPGEN("Belorussian")}, {-1, NULL}}; struct fieldnames_t countryField[]={ {1, LPGEN("USA")}, {7, LPGEN("Russia")}, {20, LPGEN("Egypt")}, {27, LPGEN("South Africa")}, {30, LPGEN("Greece")}, {31, LPGEN("Netherlands")}, {32, LPGEN("Belgium")}, {33, LPGEN("Monaco")}, {34, LPGEN("Spain")}, {36, LPGEN("Hungary")}, {39, LPGEN("San Marino")}, {40, LPGEN("Romania")}, {41, LPGEN("Switzerland")}, {42, LPGEN("Slovakia")}, {43, LPGEN("Austria")}, {44, LPGEN("UK")}, {45, LPGEN("Denmark")}, {46, LPGEN("Sweden")}, {47, LPGEN("Norway")}, {48, LPGEN("Poland")}, {49, LPGEN("Germany")}, {51, LPGEN("Peru")}, {52, LPGEN("Mexico")}, {53, LPGEN("Cuba")}, {54, LPGEN("Argentina")}, {55, LPGEN("Brazil")}, {56, LPGEN("Chile")}, {57, LPGEN("Columbia")}, {58, LPGEN("Wallis and Futuna Islands")}, {60, LPGEN("Malaysia")}, {61, LPGEN("Australia")}, {62, LPGEN("Indonesia")}, {63, LPGEN("Philippines")}, {64, LPGEN("New Zealand")}, {65, LPGEN("Singapore")}, {66, LPGEN("Thailand")}, {81, LPGEN("Japan")}, {82, LPGEN("South Korea")}, {84, LPGEN("Western Samoa")}, {86, LPGEN("China")}, {90, LPGEN("Turkey")}, {91, LPGEN("India")}, {92, LPGEN("Pakistan")}, {93, LPGEN("Afghanistan")}, {94, LPGEN("Sri Lanka")}, {98, LPGEN("Iran")}, {101, LPGEN("United States Virgin Islands")}, {102, LPGEN("Antigua")}, {103, LPGEN("Bahamas")}, {104, LPGEN("Barbados")}, {105, LPGEN("Bermuda")}, {106, LPGEN("British Virgin Islands")}, {107, LPGEN("Canada")}, {108, LPGEN("Cayman Islands")}, {109, LPGEN("Dominica")}, {110, LPGEN("Dominican Republic")}, {111, LPGEN("Grenada")}, {117, LPGEN("Trinadad and Tabago")}, {118, LPGEN("Turks and Caicos Islands")}, {121, LPGEN("Puerto Rico")}, {123, LPGEN("Uruguay")}, {212, LPGEN("Morocco")}, {213, LPGEN("Algeria")}, {216, LPGEN("Tunisia")}, {218, LPGEN("Libya")}, {220, LPGEN("Gambia")}, {221, LPGEN("Senegal")}, {223, LPGEN("Mali")}, {224, LPGEN("Guinea")}, {225, LPGEN("Ivory Coast")}, {226, LPGEN("Burkina Faso")}, {228, LPGEN("Togo")}, {229, LPGEN("Benin")}, {231, LPGEN("Liberia")}, {233, LPGEN("Ghana")}, {234, LPGEN("Nigeria")}, {235, LPGEN("Chad")}, {236, LPGEN("Central African Republic")}, {237, LPGEN("Cameroon")}, {238, LPGEN("Cape Verde Islands")}, {240, LPGEN("Equatorial Guinea")}, {241, LPGEN("Gabon")}, {242, LPGEN("Congo")}, {243, LPGEN("Zimbabwe")}, {244, LPGEN("Angola")}, {245, LPGEN("Guinea-Bissau")}, {246, LPGEN("Diego Garcia")}, {251, LPGEN("Ethiopia")}, {253, LPGEN("Djibouti")}, {254, LPGEN("Kenya")}, {255, LPGEN("Tanzania")}, {256, LPGEN("Uganda")}, {257, LPGEN("Burundi")}, {264, LPGEN("Namibia")}, {265, LPGEN("Malawi")}, {267, LPGEN("Botswana")}, {274, LPGEN("Ascention Island")}, {291, LPGEN("Eritrea")}, {297, LPGEN("Aruba")}, {298, LPGEN("Faeroe Islands")}, {299, LPGEN("Greenland")}, {350, LPGEN("Gibraltar")}, {351, LPGEN("Portugal")}, {352, LPGEN("Luxembourg")}, {353, LPGEN("Ireland")}, {354, LPGEN("Iceland")}, {355, LPGEN("Albania")}, {356, LPGEN("Malta")}, {357, LPGEN("Cyprus")}, {358, LPGEN("Finland")}, {359, LPGEN("Bulgaria")}, {372, LPGEN("Estonia")}, {373, LPGEN("Moldova")}, {374, LPGEN("Armenia")}, {375, LPGEN("Belarus")}, {376, LPGEN("Andorra")}, {378, LPGEN("Venezuela")}, {379, LPGEN("Vietnam")}, {380, LPGEN("Ukraine")}, {381, LPGEN("Zambia")}, {385, LPGEN("Croatia")}, {387, LPGEN("Bosnia & Herzegovina")}, {389, LPGEN("F.Y.R.O.M. (Former Yugoslavia)")}, {500, LPGEN("Falkland Islands")}, {501, LPGEN("Belize")}, {502, LPGEN("Guatemala")}, {503, LPGEN("El Salvador")}, {504, LPGEN("Honduras")}, {505, LPGEN("Nicaragua")}, {506, LPGEN("Costa Rice")}, {507, LPGEN("Panama")}, {509, LPGEN("Haiti")}, {590, LPGEN("Guadeloupe")}, {591, LPGEN("Bolivia")}, {592, LPGEN("Guyana")}, {593, LPGEN("Ecuador")}, {594, LPGEN("French Guiana")}, {595, LPGEN("Paraguay")}, {596, LPGEN("French Antilles")}, {597, LPGEN("Suriname")}, {598, LPGEN("Uzbekistan")}, {599, LPGEN("Netherlands Antilles")}, {670, LPGEN("Saipan")}, {671, LPGEN("Guam")}, {672, LPGEN("Christmas Island")}, {673, LPGEN("Brunei")}, {675, LPGEN("Papua New Guinea")}, {676, LPGEN("Tonga")}, {678, LPGEN("Vatican City")}, {679, LPGEN("Fiji")}, {681, LPGEN("Yemen")}, {682, LPGEN("Cook Islands")}, {684, LPGEN("American Samoa")}, {685, LPGEN("Yugoslavia")}, {687, LPGEN("New Caledonia")}, {688, LPGEN("Tuvalu")}, {689, LPGEN("French Polynesia")}, {690, LPGEN("Tokelau")}, {691, LPGEN("Federated States of Micronesia")}, {705, LPGEN("Kazakhstan")}, {709, LPGEN("Turkmenistan")}, {711, LPGEN("Vanuatu")}, {852, LPGEN("Hong Kong")}, {855, LPGEN("Cambodia")}, {870, LPGEN("INMARSAT Atlantic-East")}, {880, LPGEN("Bangladesh")}, {886, LPGEN("Taiwan")}, {934, LPGEN("Azerbaijan")}, {962, LPGEN("Jordan")}, {964, LPGEN("Iraq")}, {965, LPGEN("Kuwait")}, {966, LPGEN("Saudia Arabia")}, {967, LPGEN("Zaire")}, {968, LPGEN("Oman")}, {971, LPGEN("United Arab Emirates")}, {972, LPGEN("Israel")}, {973, LPGEN("Bahrain")}, {974, LPGEN("Qatar")}, {975, LPGEN("Bhutan")}, {977, LPGEN("Nepal")}, {995, LPGEN("Georgia")}, {2691, LPGEN("Comoros")}, {4101, LPGEN("Liechtenstein")}, {5399, LPGEN("Guantanomo Bay")}, {5901, LPGEN("French Antilles")}, {6101, LPGEN("Cocos-Keeling Islands")}, {6702, LPGEN("Tinian Island")}, {6721, LPGEN("Australian Antartic Territory")}, {-1, NULL}}; struct fieldnames_t pastField[]={ {300, LPGEN("Elementary School")}, {301, LPGEN("High School")}, {302, LPGEN("College")}, {303, LPGEN("University")}, {304, LPGEN("Military")}, {305, LPGEN("Past Work Place")}, {306, LPGEN("Past Organization")}, {399, LPGEN("Other")}, {-1, NULL}}; struct fieldnames_t genderField[]={ {1, LPGEN("Female")}, {2, LPGEN("Male")}, {-1, NULL}}; struct fieldnames_t workField[]={ {1, LPGEN("Academic")}, {2, LPGEN("Administrative")}, {3, LPGEN("Art/Entertainment")}, {4, LPGEN("College Student")}, {5, LPGEN("Computers")}, {6, LPGEN("Community & Social")}, {7, LPGEN("Education")}, {8, LPGEN("Engineering")}, {9, LPGEN("Financial Services")}, {10, LPGEN("Government")}, {11, LPGEN("High School Student")}, {12, LPGEN("Home")}, {13, LPGEN("ICQ - Providing Help")}, {14, LPGEN("Law")}, {15, LPGEN("Managerial")}, {16, LPGEN("Manufacturing")}, {17, LPGEN("Medical/Health")}, {18, LPGEN("Military")}, {19, LPGEN("Non-Government Organization")}, {20, LPGEN("Professional")}, {21, LPGEN("Retail")}, {22, LPGEN("Retired")}, {23, LPGEN("Science & Research")}, {24, LPGEN("Sports")}, {25, LPGEN("Technical")}, {26, LPGEN("University Student")}, {27, LPGEN("Web building")}, {99, LPGEN("Other services")}, {-1, NULL}}; struct fieldnames_t affiliationField[]={ {200, LPGEN("Alumni Org.")}, {201, LPGEN("Charity Org.")}, {202, LPGEN("Club/Social Org.")}, {203, LPGEN("Community Org.")}, {204, LPGEN("Cultural Org.")}, {205, LPGEN("Fan Clubs")}, {206, LPGEN("Fraternity/Sorority")}, {207, LPGEN("Hobbyists Org.")}, {208, LPGEN("International Org.")}, {209, LPGEN("Nature and Environment Org.")}, {210, LPGEN("Professional Org.")}, {211, LPGEN("Scientific/Technical Org.")}, {212, LPGEN("Self Improvement Group")}, {213, LPGEN("Spiritual/Religious Org.")}, {214, LPGEN("Sports Org.")}, {215, LPGEN("Support Org.")}, {216, LPGEN("Trade and Business Org.")}, {217, LPGEN("Union")}, {218, LPGEN("Volunteer Org.")}, {299, LPGEN("Other")}, {-1, NULL}}; struct fieldnames_t agesField[]={ {0x0011000D, LPGEN("13-17")}, {0x00160012, LPGEN("18-22")}, {0x001D0017, LPGEN("23-29")}, {0x0027001E, LPGEN("30-39")}, {0x00310028, LPGEN("40-49")}, {0x003B0032, LPGEN("50-59")}, {0x2710003C, LPGEN("60-above")}, {-1, NULL}}; struct fieldnames_t maritalField[]={ {10, LPGEN("Single")}, {11, LPGEN("Close relationships")}, {12, LPGEN("Engaged")}, {20, LPGEN("Married")}, {30, LPGEN("Divorced")}, {31, LPGEN("Separated")}, {40, LPGEN("Widowed")}, {-1, NULL}}; struct fieldnames_t webawareField[]={ {1, LPGEN("Offline")}, {2, LPGEN("Online")}, {3, LPGEN("Non webaware")}, {-1, NULL}}; // vim:syn=c ekg2-0.4~pre+20120506.1/plugins/icq/icq_flap_handlers.c000066400000000000000000000263671175142753400222320ustar00rootroot00000000000000/* * (C) Copyright 2006-2008 Jakub Zawadzki * 2008 Wiesaw Ochmiski * * Protocol description with author's permission from: http://iserverd.khstu.ru/oscar/ * (C) Copyright 2000-2005 Alexander V. Shutko * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #include #include #include #include #include #include #include #include "icq.h" #include "misc.h" #include "miscicq.h" #include "icq_flap_handlers.h" #include "icq_snac_handlers.h" static inline char *_icq_makeflap(guint8 cmd, guint16 id, guint16 len) { static char buf[FLAP_PACKET_LEN]; GString *tempstr; tempstr = icq_pack("CCWW", (guint32) 0x2a, (guint32) cmd, (guint32) id, (guint32) len); if (tempstr->len != FLAP_PACKET_LEN) { debug_error("_icq_makeflap() critical error\n"); return NULL; } memcpy(buf, tempstr->str, FLAP_PACKET_LEN); g_string_free(tempstr, TRUE); return buf; } // static inline void _icq_decodeflap(); void icq_makeflap(session_t *s, GString *pkt, guint8 cmd) { icq_private_t *j; if (!s || !(j = s->priv) || !pkt) return; if (!j->flap_seq) j->flap_seq = (rand() & 0x7fff); /* XXX */ j->flap_seq++; j->flap_seq &= 0x7fff; debug_function("icq_makeflap() 0x%x\n", cmd); g_string_prepend_len(pkt, _icq_makeflap(cmd, j->flap_seq, pkt->len), FLAP_PACKET_LEN); } #define ICQ_FLAP_HANDLER(x) int x(session_t *s, unsigned char *buf, int len) typedef int (*flap_handler_t) (session_t * , unsigned char * , int ); #define ICQ_FLAP_LOGIN 0x01 static ICQ_FLAP_HANDLER(icq_flap_login) { struct { guint32 id; /* LOGIN PACKET: 0x00 00 00 01 */ unsigned char *buf; /* extra data ? */ } login; icq_private_t *j = s->priv; debug_function("icq_flap_login()\n"); if (!ICQ_UNPACK(&(login.buf), "I", &login.id)) return -1; debug("icq_flap_login() id=%.8x extralen=%d\n", login.id, len); if (len != 0) { debug_error("icq_flap_login() len\n"); return -2; } if (login.id != 1) { debug_error("icq_flap_login() login.id\n"); return -2; } if (s->connecting == 1) { /* hub connecting */ /* * Currently there is two ways to pass authentification in OSCAR protocol. * First is FLAP channel 0x01 authorization (password not crypted but roasted), * second is MD5 based where password is MD5 crypted. In both * ways server could return error or authorization cookie + BOS address. */ GString *str = icq_pack("I", (guint32) 1); /* protocol version number */ if (session_int_get(s, "plaintext_passwd") == 1) { /* * Client use this packet in FLAP channel 0x01 based authorization sequence. * So client should send it on FLAP channel 0x01. Server should reply via * srv_cookie packet, containing BOS address/cookie or via auth_failed * packet, containing error code. */ char *password; debug("icq_flap_login(1) PLAINTEXT\n"); // Start channel 0x01 authorization icq_pack_append(str, "T", icq_pack_tlv_str(1, s->uid + 4)); // TLV(0x01) - uin password = icq_encryptpw(session_get(s, "password")); icq_pack_append(str, "T", icq_pack_tlv_str(2, password)); // TLV(0x02) - roasted password xfree(password); icq_pack_append_client_identification(str); // Pack client identification details. icq_makeflap(s, str, ICQ_FLAP_LOGIN); icq_send_pkt(s, str); str = NULL; // Send CLI_IDENT packet } else { /* Second way is MD5 based where password is MD5 crypted */ debug("icq_flap_login(1) MD5\n"); icq_pack_append(str, "tI", icq_pack_tlv_dword(0x8003, 0x00100000)); /* unknown */ icq_makeflap(s, str, ICQ_FLAP_LOGIN); icq_send_pkt(s, str); str = NULL; /* SNAC(17,06) CLI_AUTH_REQUEST Request md5 authkey * * This is the first snac in md5 crypted login sequence. Client use this * snac to request login random key from server. Server could return SNAC(17,07) * containing auth key string. */ icq_send_snac(s, 0x17, 6, 0, 0, "T", icq_pack_tlv_str(0x01, s->uid + 4)); /* Send CLI_AUTH_REQUEST */ } } else if (s->connecting == 2) { /* server connecting */ /* * After authorization client should send cookie to BOS via special FLAP channel 0x01 * packet named CLI_COOKIE. In reply server will return list of supported services - SNAC(01,03). */ GString *str; debug("icq_flap_login(2) s=0x%x cookie=0x%x cookielen=%d\n", s, j->cookie, j->cookie ? j->cookie->len : -1); if (!j->cookie) { debug_error("j->cookie == NULL???\n"); return -2; } str = icq_pack("I", (guint32) 1); /* spkt.flap.pkt.login.id CLI_HELLO */ icq_pack_append(str, "T", icq_pack_tlv(0x06, j->cookie->str, j->cookie->len)); icq_makeflap(s, str, ICQ_FLAP_LOGIN); icq_send_pkt(s, str); str = NULL; // Send CLI_COOKIE g_string_free(j->cookie, TRUE); j->cookie = NULL; } else { debug_error("icq_flap_login(%d) XXX?\n", s->connecting); return -2; } return 0; } #define ICQ_FLAP_DATA 0x02 static ICQ_FLAP_HANDLER(icq_flap_data) { snac_packet_t snac; unsigned char *data; debug_function("icq_flap_data()\n"); if (!ICQ_UNPACK(&(snac.data), "WWWI", &(snac.family), &(snac.cmd), &(snac.flags), &(snac.ref))) return -1; #if ICQ_SNAC_NAMES_DEBUG { const char *tmp = icq_snac_name(snac.family, snac.cmd); debug_white("icq_flap_data() SNAC(0x%x,0x%x) (flags=0x%x ref=0x%x len=%d) // %s\n", snac.family, snac.cmd, snac.flags, snac.ref, len, tmp?tmp:""); } #else debug_white("icq_flap_data() SNAC(0x%x,0x%x) (flags=0x%x ref=0x%x len=%d)\n", snac.family, snac.cmd, snac.flags, snac.ref, len); #endif data = snac.data; if (snac.flags & 0x8000) { guint16 skip_len; if (!icq_unpack(data, &data, &len, "W", &skip_len)) return -1; if (len < skip_len) return -1; data += skip_len; len -= skip_len; debug_white("icq_flap_data() len left: %d\n", len); } icq_snac_handler(s, snac.family, snac.cmd, data, len, snac.flags, snac.ref); return 0; } #define ICQ_FLAP_ERROR 0x03 static ICQ_FLAP_HANDLER(icq_flap_error) { debug_function("icq_flap_error()\n"); return 0; } #define ICQ_FLAP_CLOSE 0x04 int icq_flap_close_helper(session_t *s, unsigned char *buf, int len) { /* * SRV_COOKIE * This is the server reply for for CLI_IDENT packet. It contain BOS * address / authorization cookie. It always come from FLAP channel 0x04. */ icq_private_t *j = s->priv; struct icq_tlv_list *tlvs; icq_tlv_t *login_tlv; if (!(tlvs = icq_unpack_tlvs(&buf, &len, 0))) return -1; if ((login_tlv = icq_tlv_get(tlvs, 5)) && login_tlv->len) { icq_tlv_t *cookie_tlv = icq_tlv_get(tlvs, 0x06); char *login_str = xstrndup((char *) login_tlv->buf, login_tlv->len); GString *pkt; char *tmp; int port; if (!cookie_tlv) { debug_error("icq_flap_close() loginTLV, but no cookieTLV?\n"); icq_tlvs_destroy(&tlvs); return -2; } if (!(tmp = xstrchr(login_str, ':'))) { debug(".... TLV[5] == %s not in format IP:PORT ?\n", login_str); xfree(login_str); icq_tlvs_destroy(&tlvs); return -2; } port = atoi(tmp + 1); *tmp = '\0'; /* ip: login_str...tmp */ debug("icq_flap_close() Redirect to server %s:%d\n", login_str, port); j->cookie = g_string_new(NULL); g_string_append_len(j->cookie, (char *) cookie_tlv->buf, cookie_tlv->len); if (!j->migrate) { /* FlapCliGoodbye() */ pkt = g_string_new(NULL); icq_makeflap(s, pkt, ICQ_FLAP_CLOSE); icq_send_pkt(s, pkt); pkt = NULL; } // Client disconnects from authorizer ekg_disconnect_by_outstream(j->send_stream); s->connecting = 2; j->migrate = 0; icq_connect(s, login_str, port); } else { icq_tlv_t *t_uid = icq_tlv_get(tlvs, 0x01); // uin icq_tlv_t *t_url = icq_tlv_get(tlvs, 0x04); // [NOT ALWAYS] error description url string icq_tlv_t *t_err1= icq_tlv_get(tlvs, 0x08); // error subcode (family specific) icq_tlv_t *t_err2= icq_tlv_get(tlvs, 0x09); // disconnect reason // icq_tlv_t *unk = icq_tlv_get(l, 0x0C); /* unk, seen 0x5F 0x65 */ char *reason = NULL; if (t_uid && t_uid->len) { char *uid = xstrndup((char *) t_uid->buf, t_uid->len); if (xstrcmp(uid, s->uid+4)) debug("icq_ UID: %s\n", uid); xfree(uid); } if (t_url && t_url->len) { char *url = xstrndup((char *) t_url->buf, t_url->len); debug("icq_ URL: %s\n", url); xfree(url); } /* XXX, reason */ /* XXX: t_err1->nr == 0x05 -> mIcq unset password */ if (t_err1 && t_err1->nr == 24) { reason = "You logged in too frequently, please wait 30 minutes before trying again."; } else { debug("FLAP_CHANNEL4 1048 Error code: %ld\n", t_err2 ? t_err2->nr : t_err1 ? t_err1->nr : -1); } icq_handle_disconnect(s, reason, EKG_DISCONNECT_FAILURE); /* XXX */ } icq_tlvs_destroy(&tlvs); return 0; } static ICQ_FLAP_HANDLER(icq_flap_close) { debug_function("icq_flap_close()\n"); return icq_flap_close_helper(s, buf, len); } #define ICQ_FLAP_PING 0x05 static ICQ_FLAP_HANDLER(icq_flap_ping) { struct { guint16 seq; /* XXX, BE/LE? */ guint16 len; /* XXX, BE/LE? */ unsigned char *data; } ping; debug_function("icq_flap_ping()\n"); if (!ICQ_UNPACK(&(ping.data), "WW", &(ping.seq), (ping.len))) return -1; if (len) { debug("icq_flap_ping() dump"); icq_hexdump(DEBUG_WHITE, ping.data, len); } return 0; } int icq_flap_handler(session_t *s, GString *buffer) { unsigned char *buf = (unsigned char *) buffer->str; int next_flap = 0; int len = buffer->len; debug_iorecv("icq_flap_loop(%s) len: %d\n", s->uid, len); while (len >= FLAP_PACKET_LEN) { flap_packet_t flap; flap_handler_t handler; if (next_flap) debug("icq_flap_loop() nextflap restlen: %d\n", len); if (buf[0] != 0x2A) { debug_error("icq_flap_loop() Incoming packet is not a FLAP: id is %d.\n", buf[0]); icq_hexdump(DEBUG_ERROR, buf, len); return -2; } if (!ICQ_UNPACK(&(flap.data), "CCWW", &flap.unique, &flap.cmd, &flap.id, &flap.len)) return -1; buf = flap.data; debug_white("icq_flap_loop() FLAP PKT cmd=0x%x id=0x%x len: %d bytes (rlen: %d)\n", flap.cmd, flap.id, flap.len, len); if (flap.len > len) return -1; /* micq: conn->stat_pak_rcvd++; conn->stat_real_pak_rcvd++; */ switch (flap.cmd) { case ICQ_FLAP_LOGIN: handler = icq_flap_login; break; case ICQ_FLAP_DATA: handler = icq_flap_data; break; case ICQ_FLAP_ERROR: handler = icq_flap_error; break; case ICQ_FLAP_CLOSE: handler = icq_flap_close; break; case ICQ_FLAP_PING: handler = icq_flap_ping; break; default: handler = NULL; break; } if (!handler) { debug("icq_flap_loop() 1884 FLAP with unknown channel %x received.\n", flap.cmd); return -2; } handler(s, flap.data, flap.len); /* next flap? */ buf += (flap.len); len -= (flap.len); buffer->len = len; next_flap = 1; } return len ? -1 : 0; } ekg2-0.4~pre+20120506.1/plugins/icq/icq_flap_handlers.h000066400000000000000000000006001175142753400222150ustar00rootroot00000000000000#ifndef __ICQ_FLAP_H #define __ICQ_FLAP_H void icq_makeflap(session_t *s, GString *pkt, guint8 cmd); int icq_flap_handler(session_t *s, GString *buffer); int icq_flap_close_helper(session_t *s, unsigned char *buf, int len); typedef struct { guint8 unique; /* 0x2A */ guint8 cmd; guint16 id; guint16 len; unsigned char *data; } flap_packet_t; #define FLAP_PACKET_LEN 6 #endif ekg2-0.4~pre+20120506.1/plugins/icq/icq_snac_handlers.c000066400000000000000000000205401175142753400222170ustar00rootroot00000000000000/* * (C) Copyright 2006-2008 Jakub Zawadzki * 2008 Wiesaw Ochmiski * * Protocol description with author's permission from: http://iserverd.khstu.ru/oscar/ * (C) Copyright 2000-2005 Alexander V. Shutko * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #include #include #include "icq.h" #include "misc.h" #include "icq_caps.h" #include "icq_const.h" #include "icq_flap_handlers.h" #include "icq_snac_handlers.h" static LIST_FREE_ITEM(icq_snac_references_list_free, icq_snac_reference_list_t *) { if (data && data->list) private_items_destroy(&data->list); } static __DYNSTUFF_ADD(icq_snac_references_list, icq_snac_reference_list_t, __not_used) static __DYNSTUFF_REMOVE_SAFE(icq_snac_references_list, icq_snac_reference_list_t, icq_snac_references_list_free) static __DYNSTUFF_REMOVE_ITER(icq_snac_references_list, icq_snac_reference_list_t, icq_snac_references_list_free) __DYNSTUFF_DESTROY(icq_snac_references_list, icq_snac_reference_list_t, icq_snac_references_list_free) static void icq_snac_ref_add(session_t *s, icq_snac_reference_list_t *elem) { icq_private_t *j; if (!s || !s->priv) return; j = (icq_private_t *) s->priv; icq_snac_references_list_add(&(j->snac_ref_list), elem); } static icq_snac_reference_list_t *icq_snac_ref_find(session_t *s, guint32 ref) { icq_private_t *j; icq_snac_reference_list_t *l; if (!s || !(j = s->priv) || (!j->snac_ref_list) || (ref > 0xffff)) return NULL; for (l = j->snac_ref_list; l; l = l->next) { if (l->ref == ref) return l; } return NULL; } static void icq_snac_ref_remove(session_t *s, icq_snac_reference_list_t *elem) { icq_private_t *j; if (!s || !(j = (icq_private_t *) s->priv)) return; icq_snac_references_list_remove(&(j->snac_ref_list), elem); } TIMER_SESSION(icq_snac_ref_list_cleanup) { icq_private_t *j; icq_snac_reference_list_t *l; time_t t = time(NULL) - 100; /* XXX add to session configuration? */ if (!s || !(j = (icq_private_t *) s->priv)) return 0; /* XXX ?wo? inform about removed refs??? */ for (l = j->snac_ref_list; l ; l = l->next) { if (t > l->timestamp) l = icq_snac_references_list_removei(&j->snac_ref_list, l); } return 0; } static inline char *_icq_makesnac(guint8 family, guint16 cmd, guint16 flags, guint32 ref) { static char buf[SNAC_PACKET_LEN]; GString *tempstr; tempstr = icq_pack("WWWI", (guint32) family, (guint32) cmd, (guint32) flags, (guint32) ref); if (tempstr->len != SNAC_PACKET_LEN) { debug_error("_icq_makesnac() critical error\n"); return NULL; } memcpy(buf, tempstr->str, SNAC_PACKET_LEN); g_string_free(tempstr, TRUE); return buf; } void icq_makesnac(session_t *s, GString *pkt, guint16 fam, guint16 cmd, private_data_t *data, snac_subhandler_t subhandler) { icq_private_t *j; icq_snac_reference_list_t *snac_data = NULL; if (!s || !(j = s->priv) || !pkt) return; if (data || subhandler) { snac_data = xmalloc(sizeof(icq_snac_reference_list_t)); snac_data->ref = j->snac_seq; snac_data->timestamp = time(NULL); snac_data->subhandler = subhandler; snac_data->list = data; icq_snac_ref_add(s, snac_data); } g_string_prepend_len(pkt, _icq_makesnac(fam, cmd, 0x0000, j->snac_seq), SNAC_PACKET_LEN); #if ICQ_SNAC_NAMES_DEBUG { const char *tmp = icq_snac_name(fam, cmd); debug_function("icq_makesnac(0x%x) SNAC(0x%x,0x%x) // %s\n", j->snac_seq, fam, cmd, tmp?tmp:""); } #else debug_function("icq_makesnac(0x%x) SNAC(0x%x,0x%x)\n", j->snac_seq, fam, cmd); #endif icq_makeflap(s, pkt, 0x02); j->snac_seq++; } void icq_makemetasnac(session_t *s, GString *pkt, guint16 type, guint16 subtype, private_data_t *data, snac_subhandler_t subhandler) { icq_private_t *j; GString *newbuf; int t_len; if (!s || !(j = s->priv) || !pkt) return; j->snacmeta_seq++; if (j->snacmeta_seq & ~0x7fff) j->snacmeta_seq = 1; t_len = pkt->len + (2+4+2+2) + (subtype ? 2 : 0); newbuf = icq_pack("t", (guint32) 0x01, (guint32) t_len); icq_pack_append(newbuf, "wiww", (guint32) t_len - 2, // data chunk size (TLV.Length-2) (guint32) atoi(s->uid+4), // request owner uin (guint32) type, // request cmd type (guint32) j->snacmeta_seq); // request sequence number if (subtype) icq_pack_append(newbuf, "w", (guint32) subtype); g_string_prepend_len(pkt, newbuf->str, newbuf->len); g_string_free(newbuf, TRUE); debug_function("icq_makemetasnac() 0x%x 0x0%x\n", type, subtype); icq_makesnac(s, pkt, 0x15, 0x2, data, subhandler); } /* stolen from Miranda ICQ plugin CIcqProto::LogFamilyError() chan_02data.cpp under GPL-2 or later */ void icq_snac_error_handler(session_t *s, const char *from, guint16 error) { const char *msg; switch (error) { case 0x01: msg = "Invalid SNAC header"; break; case 0x02: msg = "Server rate limit exceeded"; break; case 0x03: msg = "Client rate limit exceeded"; break; case 0x04: msg = "Recipient is not logged in"; break; case 0x05: msg = "Requested service unavailable"; break; case 0x06: msg = "Requested service not defined"; break; case 0x07: msg = "You sent obsolete SNAC"; break; case 0x08: msg = "Not supported by server"; break; case 0x09: msg = "Not supported by client"; break; case 0x0A: msg = "Refused by client"; break; case 0x0B: msg = "Reply too big"; break; case 0x0C: msg = "Responses lost"; break; case 0x0D: msg = "Request denied"; break; case 0x0E: msg = "Incorrect SNAC format"; break; case 0x0F: msg = "Insufficient rights"; break; case 0x10: msg = "In local permit/deny (recipient blocked)"; break; case 0x11: msg = "Sender is too evil"; break; case 0x12: msg = "Receiver is too evil"; break; case 0x13: msg = "User temporarily unavailable"; break; case 0x14: msg = "No match"; break; case 0x15: msg = "List overflow"; break; case 0x16: msg = "Request ambiguous"; break; case 0x17: msg = "Server queue full"; break; case 0x18: msg = "Not while on AOL"; break; case 0x19: msg = "Query failed"; break; case 0x1A: msg = "Timeout"; break; case 0x1C: msg = "General failure"; break; case 0x1D: msg = "Progress"; break; case 0x1E: msg = "In free area"; break; case 0x1F: msg = "Restricted by parental controls"; break; case 0x20: msg = "Remote restricted by parental controls"; break; default: msg = ""; break; } debug_error("icq_snac_error_handler(%s) %s: %s (%.4x)\n", s->uid, from, msg, error); } int icq_snac_handler(session_t *s, guint16 family, guint16 cmd, unsigned char *buf, int len, guint16 flags, guint32 ref_no) { snac_handler_t handler; icq_snac_reference_list_t *ref_data = icq_snac_ref_find(s, ref_no);; private_data_t *h_data = ref_data ? ref_data->list : NULL; debug_white("icq_snac_handler() family=%.4x cmd=%.4x (len=%d)\n", family, cmd, len); /* XXX, queue */ // debug_error("icq_flap_data() XXX\n"); if (ref_data && ref_data->subhandler) { ref_data->subhandler(s, buf, len, h_data); if (!(flags & 0x0001)) icq_snac_ref_remove(s, ref_data); return 0; } switch (family) { case 0x01: handler = icq_snac_service_handler; break; case 0x02: handler = icq_snac_location_handler; break; case 0x03: handler = icq_snac_buddy_handler; break; case 0x04: handler = icq_snac_message_handler; break; case 0x09: handler = icq_snac_bos_handler; break; case 0x0a: handler = icq_snac_lookup_handler; break; case 0x0b: handler = icq_snac_status_handler; break; case 0x13: handler = icq_snac_userlist_handler; break; case 0x15: handler = icq_snac_extension_handler;break; case 0x17: handler = icq_snac_sigon_handler; break; default: handler = NULL; break; } if (!handler) { debug_error("snac_handler() SNAC with unknown family: %.4x cmd: %.4x received.\n", family, cmd); icq_hexdump(DEBUG_ERROR, buf, len); return 0; } handler(s, cmd, buf, len, h_data); return 0; } ekg2-0.4~pre+20120506.1/plugins/icq/icq_snac_handlers.h000066400000000000000000000030261175142753400222240ustar00rootroot00000000000000#ifndef __ICQ_SNAC_H #define __ICQ_SNAC_H #include "icq.h" typedef struct { /* flap_packet_t->data ** if flap_packet_t->cmd == 0x02 */ guint16 family; guint16 cmd; guint16 flags; guint32 ref; unsigned char *data; } snac_packet_t; #define SNAC_PACKET_LEN 10 void icq_makesnac(session_t *s, GString *pkt, guint16 fam, guint16 cmd, private_data_t *data, snac_subhandler_t subhandler); void icq_makemetasnac(session_t *s, GString *pkt, guint16 type, guint16 subtype, private_data_t *data, snac_subhandler_t subhandler); int icq_snac_handler(session_t *s, guint16 family, guint16 cmd, unsigned char *buf, int len, guint16 flags, guint32 ref_no); void icq_snac_error_handler(session_t *s, const char *from, guint16 error); void icq_snac_references_list_destroy(icq_snac_reference_list_t **lista); TIMER_SESSION(icq_snac_ref_list_cleanup); SNAC_SUBHANDLER(icq_my_meta_information_response); SNAC_SUBHANDLER(icq_cmd_addssi_ack); void display_whoami(session_t *s); void icq_pack_append_nullterm_msg(GString *pkt, const char *msg); void icq_pack_append_rendezvous(GString *pkt, int version, int cookie, int mtype, int mflags, int accept, int priority); SNAC_HANDLER(icq_snac_service_handler); SNAC_HANDLER(icq_snac_location_handler); SNAC_HANDLER(icq_snac_buddy_handler); SNAC_HANDLER(icq_snac_message_handler); SNAC_HANDLER(icq_snac_bos_handler); SNAC_HANDLER(icq_snac_lookup_handler); SNAC_HANDLER(icq_snac_status_handler); SNAC_HANDLER(icq_snac_userlist_handler); SNAC_HANDLER(icq_snac_extension_handler); SNAC_HANDLER(icq_snac_sigon_handler); #endif ekg2-0.4~pre+20120506.1/plugins/icq/icq_snac_handlers_01service.c000066400000000000000000000541471175142753400241120ustar00rootroot00000000000000/* * (C) Copyright 2000-2001 Richard Hughes, Roland Rabien, Tristan Van de Vreede * (C) Copyright 2001-2002 Jon Keating, Richard Hughes * (C) Copyright 2002-2004 Martin Öberg, Sam Kothari, Robert Rainwater * (C) Copyright 2004-2008 Joe Kucera * * ekg2 port: * (C) Copyright 2006-2008 Jakub Zawadzki * 2008 Wiesław Ochmiński * * Protocol description with author's permission from: http://iserverd.khstu.ru/oscar/ * (C) Copyright 2000-2005 Alexander V. Shutko * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include "icq.h" #include "misc.h" #include "icq_caps.h" #include "icq_const.h" #include "icq_flap_handlers.h" #include "icq_snac_handlers.h" SNAC_SUBHANDLER(icq_snac_service_error) { struct { guint16 error; unsigned char *data; } pkt; guint16 error; debug_function("icq_snac_service_error()\n"); if (ICQ_UNPACK(&pkt.data, "W", &pkt.error)) error = pkt.error; else error = 0; /* Something went wrong, probably the request for avatar family failed */ icq_snac_error_handler(s, "service", error); return 0; } SNAC_SUBHANDLER(icq_snac_service_families) { /* * SNAC(01,03) SRV_FAMILIES -- Server supported snac families list * * This is the first snac in protocol negotiation sequence. Client shouldn't use * families not listed in this SNAC. So if your client use SNAC(13) family and * server SNAC(01,03) doesn't contain it - your client should popup "server error" * message when user want's to change server-stored information (SSI). */ GString *pkt; debug_function("icq_snac_service_families()\n"); // Handle incoming packet #if ICQ_DEBUG_UNUSED_INFORMATIONS debug_white("icq_snac_service_families() Server knows families:"); while (len>=2) { guint16 family; ICQ_UNPACK(&buf, "W", &family); debug_white(" 0x%x", family); } debug_white("\n"); #endif /* This packet is a response to SRV_FAMILIES SNAC(1,3). * This tells the server which SNAC families and their corresponding * versions which the client understands. This also seems to identify * the client as an ICQ vice AIM client to the server. */ pkt = g_string_new(NULL); icq_pack_append(pkt, "WW", (guint32) 0x01, (guint32) 0x04); // Generic service controls icq_pack_append(pkt, "WW", (guint32) 0x02, (guint32) 0x01); // Location services icq_pack_append(pkt, "WW", (guint32) 0x03, (guint32) 0x01); // Buddy List management service icq_pack_append(pkt, "WW", (guint32) 0x04, (guint32) 0x01); // ICBM (messages) service icq_pack_append(pkt, "WW", (guint32) 0x09, (guint32) 0x01); // Privacy management service icq_pack_append(pkt, "WW", (guint32) 0x0a, (guint32) 0x01); // User lookup service icq_pack_append(pkt, "WW", (guint32) 0x0b, (guint32) 0x01); // Usage stats service icq_pack_append(pkt, "WW", (guint32) 0x13, (guint32) 0x05); // Server Side Information (SSI) service icq_pack_append(pkt, "WW", (guint32) 0x15, (guint32) 0x02); // ICQ specific extensions service icq_pack_append(pkt, "WW", (guint32) 0x17, (guint32) 0x01); // Authorization/registration service #if 0 icq_pack_append(pkt, "WW", (guint32) 0x05, (guint32) 0x01); // Advertisements service icq_pack_append(pkt, "WW", (guint32) 0x06, (guint32) 0x01); // Invitation service icq_pack_append(pkt, "WW", (guint32) 0x08, (guint32) 0x01); // Popup notices service icq_pack_append(pkt, "WW", (guint32) 0x07, (guint32) 0x01); // Administrative service icq_pack_append(pkt, "WW", (guint32) 0x0c, (guint32) 0x01); // Translation service icq_pack_append(pkt, "WW", (guint32) 0x0d, (guint32) 0x01); // Chat navigation service icq_pack_append(pkt, "WW", (guint32) 0x0e, (guint32) 0x01); // Chat service icq_pack_append(pkt, "WW", (guint32) 0x0f, (guint32) 0x01); // Directory user search icq_pack_append(pkt, "WW", (guint32) 0x10, (guint32) 0x01); // Server-stored buddy icons (SSBI) service icq_pack_append(pkt, "WW", (guint32) 0x15, (guint32) 0x01); // ICQ specific extensions service icq_pack_append(pkt, "WW", (guint32) 0x17, (guint32) 0x01); // Authorization/registration service icq_pack_append(pkt, "WW", (guint32) 0x22, (guint32) 0x01); icq_pack_append(pkt, "WW", (guint32) 0x24, (guint32) 0x01); icq_pack_append(pkt, "WW", (guint32) 0x25, (guint32) 0x01); icq_pack_append(pkt, "WW", (guint32) 0x85, (guint32) 0x01); // Broadcast service - IServerd extension #endif icq_makesnac(s, pkt, 0x01, 0x17, 0, 0); icq_send_pkt(s, pkt); // Send CLI_FAMILIES_VERSIONS return 0; } SNAC_SUBHANDLER(icq_snac_service_redirect) { /* * SNAC(01,05) SRV_REDIRECTxSERVICE -- Redirect (for 0x0004 subtype) * * Server replies with this to SNAC(01,04) CLI_SERVICE_REQ -- Client service request. * After receiving this snac client should connect to specified server to use requested service. */ debug_error("icq_snac_service_redirect() XXX\n"); return 0; } SNAC_SUBHANDLER(icq_snac_service_rateinfo) { /* SNAC(01,07) SRV_RATE_LIMIT_INFO -- Rate limits information response * This snac contain server information about its snac-rate limitations. */ icq_private_t *j = s->priv; struct { guint16 no; // Number of rate classes } pkt; struct { guint16 cl; // Class guint16 no; // Number of rate groups } pkt2; int i; if (!ICQ_UNPACK(&buf, "W", &pkt.no)) goto wrong; if (pkt.no*(2+8*4+1) > len) goto wrong; /* init rates management */ icq_rates_init(s, pkt.no); for (i=0; in_rates)) { r = j->rates[id - 1]; r->last_time = time(NULL); ICQ_UNPACK(&buf, "IIII III 5", &r->win_size, // Window size &r->clear_lvl, // Clear level &r->alert_lvl, // Alert level &r->limit_lvl, // Limit level &r->discn_lvl, // Disconnect level &r->curr_lvl, // Current level &r->max_lvl // Max level ); } else { buf += 7*4 + 5; len -= 7*4 + 5; } } // store rate groups while (len >= 4) { (void) ICQ_UNPACK(&buf, "WW", &pkt2.cl, &pkt2.no); if (pkt2.cl > j->n_rates) goto wrong; if (len < pkt2.no*4) goto wrong; pkt2.cl--; j->rates[pkt2.cl]->groups = xcalloc(pkt2.no, sizeof(guint32)); j->rates[pkt2.cl]->n_groups = pkt2.no; for (i=0; irates[pkt2.cl]->groups[i]); } } wrong: /* ack rate levels */ icq_send_snac(s, 0x01, 0x08, 0, 0, "WWWWW", (guint32) 0x01, (guint32) 0x02, (guint32) 0x03, (guint32) 0x04, (guint32) 0x05); /* CLI_REQINFO - This command requests from the server certain information about the client that is stored on the server. */ icq_send_snac(s, 0x01, 0x0e, 0, 0, NULL); if (j->ssi) { #if 0 DWORD dwLastUpdate = getSettingDword(NULL, "SrvLastUpdate", 0); WORD wRecordCount = getSettingWord(NULL, "SrvRecordCount", 0); servlistcookie* ack; DWORD dwCookie; #endif /* CLI_REQLISTS - we want to use SSI */ icq_send_snac(s, 0x13, 0x02, 0, 0, NULL); #if 0 if (!wRecordCount) { /* CLI_REQROSTER */ /* we do not have any data - request full list */ serverPacketInit(&packet, 10); ack = (servlistcookie*)SAFE_MALLOC(sizeof(servlistcookie)); if (ack) { // we try to use standalone cookie if available ack->dwAction = SSA_CHECK_ROSTER; // loading list dwCookie = AllocateCookie(CKT_SERVERLIST, ICQ_LISTS_CLI_REQUEST, 0, ack); } else // if not use that old fake dwCookie = ICQ_LISTS_CLI_REQUEST<<0x10; packFNACHeaderFull(&packet, ICQ_LISTS_FAMILY, ICQ_LISTS_CLI_REQUEST, 0, dwCookie); sendServPacket(&packet); } else { /* CLI_CHECKROSTER */ serverPacketInit(&packet, 16); ack = (servlistcookie*)SAFE_MALLOC(sizeof(servlistcookie)); if (ack) // TODO: rewrite - use get list service for empty list { // we try to use standalone cookie if available ack->dwAction = SSA_CHECK_ROSTER; // loading list dwCookie = AllocateCookie(CKT_SERVERLIST, ICQ_LISTS_CLI_CHECK, 0, ack); } else // if not use that old fake dwCookie = ICQ_LISTS_CLI_CHECK<<0x10; packFNACHeaderFull(&packet, ICQ_LISTS_FAMILY, ICQ_LISTS_CLI_CHECK, 0, dwCookie); // check if it was not changed elsewhere (force reload, set that setting to zero) if (IsServerGroupsDefined()) { packDWord(&packet, dwLastUpdate); // last saved time packWord(&packet, wRecordCount); // number of records saved } else { // we need to get groups info into DB, force receive list packDWord(&packet, 0); // last saved time packWord(&packet, 0); // number of records saved } sendServPacket(&packet); } #else // XXX ?wo? number of items should be "W" ? icq_send_snac(s, 0x13, 0x05, 0, 0, "II", (guint32) 0x0000, // modification date/time of client local SSI copy (guint32) 0x0000); // number of items in client local SSI copy #endif } /* SNAC(02,02) CLI_LOCATION_RIGHTS_REQ Request limitations/params * Client use this SNAC to request location service parameters and limitations. * Server should reply via SNAC(02,03). */ icq_send_snac(s, 0x02, 0x02, 0, 0, NULL); /* SNAC(03,02) CLI_BUDDYLIST_RIGHTS_REQ Request limitations/params * Client use this SNAC to request buddylist service parameters and * limitations. Server should reply via SNAC(03,03) */ icq_send_snac(s, 0x03, 0x02, 0, 0, ""); // example for empty snac: "" or NULL works /* SNAC(04,04) CLI_ICBM_PARAM_REQ Request parameters info * Use this snac to request your icbm parameters from server. * Server should reply via SNAC(04,05). You can change them using SNAC(04,02). */ icq_send_snac(s, 0x04, 0x04, 0, 0, ""); /* SNAC(09,02) CLI_PRIVACY_RIGHTS_REQ Request service parameters * Client use this SNAC to request buddylist service parameters and limitations. * Server should reply via SNAC(09,03). */ icq_send_snac(s, 0x09, 0x02, 0, 0, ""); return 0; } SNAC_SUBHANDLER(icq_snac_service_ratechange) { /* * SNAC(01,0A) SRV_RATE_LIMIT_WARN Rate information changed / rate limit warning * * Server send this snac when you goes over rate limit or when rate parameters changing. * Snac content is described by "message code". Here is the known code list: * 0x0001 Rate limits parameters changed * 0x0002 Rate limits warning (current level < alert level) * 0x0003 Rate limit hit (current level < limit level) * 0x0004 Rate limit clear (current level become > clear level) */ struct { guint16 status; } pkt; icq_private_t *j = s->priv; if (!ICQ_UNPACK(&buf, "W", &pkt.status)) // Message code (see above) return -1; /* TODO ?wo? -- print warning here ? */ while (len >= (2 + 8*4 +1)) { guint16 id; guint32 x0, x1, x2, x3, x4, x5, x6, x7; (void) ICQ_UNPACK(&buf, "W", &id); // Rate class ID (void) ICQ_UNPACK(&buf, "IIII IIII x", &x0, &x1, &x2, &x3, &x4, &x5, &x6, &x7); if (id && (id <= j->n_rates)) { id--; j->rates[id]->win_size = x0; // Window size j->rates[id]->clear_lvl = x1; // Clear level j->rates[id]->alert_lvl = x2; // Alert level j->rates[id]->limit_lvl = x3; // Limit level j->rates[id]->discn_lvl = x4; // Disconnect level j->rates[id]->curr_lvl = x5; // Current level j->rates[id]->max_lvl = x6; // Max level } } debug_function("icq_snac_service_ratechange() status:%u\n", pkt.status); return 0; } SNAC_SUBHANDLER(icq_snac_service_pause) { /* * SNAC(01,0B) SRV_PAUSE -- Server pause command * * This is the first SNAC in client migration sequence. * * Client should ack pause command with SNAC(01,0C) and stop send packets until it receive server * resume SNAC(01,0D), or migration notice SNAC(01,12). * * Migration sequence used to redirect client to new BOS server during current BOS shutdown. */ icq_private_t *j = s->priv; debug("icq_snac_service_pause() Server is going down in a few seconds...\n"); /* SNAC(01,0C) CLI_PAUSE_ACK Client pause ack * * The server sends a Server Pause message SNAC(01,0B), which the client * should respond to with a this server_pause_ack snac, which contains the * families it needs on this connection */ icq_send_snac(s, 0x01, 0x0c, NULL, NULL, "WWWW WWWW WW", /* This is the list of families that we want to have on the next server. Families array is optional. */ (guint32) 0x01, /* Generic service controls */ (guint32) 0x02, /* Location services */ (guint32) 0x03, /* Buddy List management service */ (guint32) 0x04, /* ICBM (messages) service */ (guint32) 0x09, /* Privacy management service */ (guint32) 0x0a, /* User lookup service (not used any more) (XXX ??? - This service used by old AIM clients to search users by email) */ (guint32) 0x0b, /* Usage stats service */ (guint32) 0x13, /* Server Side Information (SSI) service */ (guint32) 0x15, /* ICQ specific extensions service */ (guint32) 0x17 /* Authorization/registration service */ ); j->migrate = 1; return 0; } SNAC_SUBHANDLER(icq_snac_service_resume) { /* * SNAC(01,0D) SRV_RESUME -- Server resume command * * After this SNAC client, paused by SNAC(01,0B) may continue send packets to BOS. * Migration sequence used to redirect client to new BOS server during current BOS shutdown. */ icq_private_t *j = s->priv; debug_ok("Server resume command\n"); j->migrate = 0; return 0; } SNAC_SUBHANDLER(icq_snac_service_reqinfo) { icq_private_t *j = s->priv; unsigned char *databuf; char *uin; guint16 warning, count; struct icq_tlv_list *tlvs; icq_tlv_t *t; if (!icq_unpack(buf, &databuf, &len, "uWW", &uin, &warning, &count)) return -1; if (xstrcmp(s->uid+4, uin)) debug_error("icq_snac_service_reqinfo() Warning: Server thinks our UIN is %s, when it is %s\n", uin, s->uid+4); tlvs = icq_unpack_tlvs(&databuf, &len, count); /* XXX, miranda check dwRef. */ /* XXX, miranda saves */ for (t = tlvs; t; t = t->next) { switch (t->type) { case 0x01: /* User type */ if (tlv_length_check("icq_snac_service_reqinfo()", t, 2)) goto def; debug_white("icq_snac_service_reqinfo() User type: 0x%x\n", t->nr); break; case 0x03: /* The online since time */ if (tlv_length_check("icq_snac_service_reqinfo()", t, 4)) goto def; debug_white("icq_snac_service_reqinfo() Logon time: %s\n", timestamp_time("%Y-%m-%d %H:%M:%S", t->nr)); break; case 0x05: /* Member of ICQ since */ if (tlv_length_check("icq_snac_service_reqinfo()", t, 4)) goto def; debug_white("icq_snac_service_reqinfo() ICQ Member since: %s\n", timestamp_time("%Y-%m-%d %H:%M:%S", t->nr)); break; case 0x06: /* Status */ if (tlv_length_check("icq_snac_service_reqinfo()", t, 4)) goto def; debug_white("icq_snac_service_reqinfo() Status: 0x%.x\n", t->nr); break; case 0x0a: /* External IP */ if (tlv_length_check("icq_snac_service_reqinfo()", t, 4)) goto def; debug_white("icq_snac_service_reqinfo() External IP: %u.%u.%u.%u\n", t->buf[0], t->buf[1], t->buf[2], t->buf[3]); break; case 0x0c: /* Empty CLI2CLI Direct connection info */ break; case 0x0d: /* Our capabilities */ debug_white("icq_snac_service_reqinfo() Server knows %d our caps\n", t->len >> 4); break; case 0x0f: /* Number of seconds that user has been online */ if (tlv_length_check("icq_snac_service_reqinfo()", t, 4)) goto def; debug_white("icq_snac_service_reqinfo() Online: %u seconds.\n", t->nr); break; case 0x18: { char *nick = xstrndup((char *)t->buf, t->len); debug_white("icq_snac_service_reqinfo() nick:'%s'\n", nick); xfree(nick); break; } case 0x27: case 0x29: case 0x2d: case 0x30: debug("icq_snac_service_reqinfo() unknown tlv(0x%x), time???: %s\n",t->type,timestamp_time("%Y-%m-%d %H:%M:%S", t->nr)); break; default: if (t->len==1 || t->len==2 || t->len==4) { debug_warn("icq_snac_service_reqinfo() unknown tlv(0x%x), datalen=%d, value=0x%x\n", t->type, t->len, t->nr); break; } def: debug_error("icq_snac_service_reqinfo() TLV[0x%x] datalen: %u\n", t->type, t->len); icq_hexdump(DEBUG_WHITE, t->buf, t->len); } } /* If we are in SSI mode, this is sent after the list is acked instead to make sure that we don't set status before seing the visibility code */ if (!j->ssi) icq_session_connected(s); icq_tlvs_destroy(&tlvs); return 0; } SNAC_SUBHANDLER(icq_snac_service_evil) { /* * SNAC(01,10) SRV_EVIL_NOTICE Evil notification * * You'll receive this snac when somebody complain about you to an OSCAR server. * This snac contain your new warning_level and (optionaly) info about complaining user. * Anonymous evil notification doesn't contain user info. */ char *uin; guint16 warning, count; struct icq_tlv_list *tlvs; unsigned char *databuf; while (len>4) { if (!icq_unpack(buf, &databuf, &len, "uWW", &uin, &warning, &count)) return -1; debug_function("icq_snac_service_evil() %s\n", uin); tlvs = icq_unpack_tlvs(&databuf, &len, count); /* XXX - parse tlvs... */ icq_tlvs_destroy(&tlvs); } return 0; } SNAC_SUBHANDLER(icq_snac_service_migrate) { /* * SNAC(01,12) SRV_MIGRATION -- Migration notice and info * * This is the last SNAC in client migration sequence. * Client should open new connection to specified server and use this connection for specified families. * If there is no family list (empty) or it contain all connection families - client should close current * connection and use new one. */ int i; icq_private_t *j =s->priv; guint16 fam_count, fam; unsigned char *databuf; if (!icq_unpack(buf, &databuf, &len, "W", &fam_count)) return -1; debug_function("icq_snac_service_migrate() Migrate %d families\n", fam_count); for (i=0; imigrate = 1; icq_flap_close_helper(s, buf, len); return 0; } SNAC_SUBHANDLER(icq_snac_service_motd) { /* SNAC(01,0x13) SRV_MOTD -- Message of the day (MOTD) * * Server send this during protocol negotiation sequence. Various docs call this SNAC as * "message of the day" but it looks like that ICQ2K+ ignores this SNAC completely. */ struct icq_tlv_list *tlvs; icq_tlv_t *t; guint16 type; ICQ_UNPACK(&buf, "W", &type); /* MOTD types: * 0x0001 MTD_MDT_UPGRAGE Mandatory upgrade needed notice * 0x0002 MTD_ADV_UPGRAGE Advisable upgrade notice * 0x0003 MTD_SYS_BULLETIN AIM/ICQ service system announcements * 0x0004 MTD_NORMAL Standart notice * 0x0006 MTD_NEWS Some news from AOL service */ tlvs = icq_unpack_tlvs(&buf, &len, 0); if ( (t = icq_tlv_get(tlvs, 0x0b)) ) debug_white("icq_snac_service_motd() type:%d, MOTD: %s\n", type, t->buf); else debug_white("icq_snac_service_motd() type:%d\n", type); icq_tlvs_destroy(&tlvs); return 0; } SNAC_SUBHANDLER(icq_snac_service_urls) { /* SNAC(01,0x15) Well known urls */ debug_function("icq_snac_service_urls()\n"); #if ICQ_DEBUG_UNUSED_INFORMATIONS guint16 x, ulen; char *url; while (len>0) { ICQ_UNPACK(&buf, "WW", &x, &ulen); url = xstrndup((const char*)buf, ulen); debug_white("ICQ - well known url %d: %s\n", x, url); buf += ulen; len -= ulen; xfree(url); } #endif return 0; } SNAC_SUBHANDLER(icq_snac_service_nop) { /* SNAC(01,16) CLI_KEEPALIVE No operation (NOP) * * WinAIM (AIM service) use this snac to keep connection alive. This is usefull * when it use proxy server to connect to BOS. Proxy servers can disconnect client * if there is no activity for some time. */ debug_warn("icq_snac_service_nop() is deprecated!\n"); return 0; } SNAC_SUBHANDLER(icq_snac_service_families2) { /* SNAC(01,18) SRV_FAMILIES_VERSIONS -- Server services versions * * This is a reply to CLI_FAMILIES and it tells the client which families * and their versions that this server understands. */ debug_function("icq_snac_service_families2()\n"); // Handle incoming packet #if ICQ_DEBUG_UNUSED_INFORMATIONS while (len>=2) { guint16 fam, ver; ICQ_UNPACK(&buf, "WW", &fam, &ver); debug_white("icq_snac_service_families2() fam=0x%x ver=0x%x // %s\n", fam, ver, icq_lookuptable(snac_families, fam)); } #endif /* SNAC(01,06) CLI_RATES_REQUEST * Client use this SNAC to request server rate-limits. This happens during * protocol negotiation sequence. Server should reply via SNAC(01,07) */ icq_send_snac(s, 0x01, 0x06, NULL, NULL, ""); // Request rate limits information return 0; } SNAC_SUBHANDLER(icq_snac_service_extstatus) { debug_function("icq_snac_service_extstatus() Received our avatar hash & status. XXX\n"); icq_hexdump(DEBUG_ERROR, buf, len); return 0; } SNAC_HANDLER(icq_snac_service_handler) { snac_subhandler_t handler; switch (cmd) { case 0x01: handler = icq_snac_service_error; break; /* Miranda: OK */ case 0x03: handler = icq_snac_service_families; break; /* Miranda: OK */ case 0x05: handler = icq_snac_service_redirect; break; case 0x07: handler = icq_snac_service_rateinfo; break; /* Miranda: OK */ case 0x0A: handler = icq_snac_service_ratechange;break; /* Miranda: 2/3 OK */ case 0x0B: handler = icq_snac_service_pause; break; /* Miranda: OK */ case 0x0D: handler = icq_snac_service_resume; break; case 0x0F: handler = icq_snac_service_reqinfo; break; /* Miranda: OK */ case 0x10: handler = icq_snac_service_evil; break; /* Evil notification */ case 0x12: handler = icq_snac_service_migrate; break; case 0x13: handler = icq_snac_service_motd; break; /* Miranda: OK */ case 0x15: handler = icq_snac_service_urls; break; case 0x16: handler = icq_snac_service_nop; break; /* No operation (NOP) */ case 0x18: handler = icq_snac_service_families2;break; /* Miranda: OK */ case 0x1F: handler = NULL; break; /* XXX Client verification request */ case 0x21: handler = icq_snac_service_extstatus;break; default: handler = NULL; break; } if (!handler) { debug_error("icq_snac_service_handler() SNAC with unknown cmd: %.4x received\n", cmd); icq_hexdump(DEBUG_ERROR, buf, len); } else handler(s, buf, len, data); return 0; } // vim:syn=c ekg2-0.4~pre+20120506.1/plugins/icq/icq_snac_handlers_02location.c000066400000000000000000000117411175142753400242540ustar00rootroot00000000000000/* * (C) Copyright 2000-2001 Richard Hughes, Roland Rabien, Tristan Van de Vreede * (C) Copyright 2001-2002 Jon Keating, Richard Hughes * (C) Copyright 2002-2004 Martin Öberg, Sam Kothari, Robert Rainwater * (C) Copyright 2004-2008 Joe Kucera * * ekg2 port: * (C) Copyright 2006-2008 Jakub Zawadzki * 2008 Wiesław Ochmiński * * Protocol description with author's permission from: http://iserverd.khstu.ru/oscar/ * (C) Copyright 2000-2005 Alexander V. Shutko * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include "icq.h" #include "misc.h" #include "icq_caps.h" #include "icq_const.h" #include "icq_flap_handlers.h" #include "icq_snac_handlers.h" SNAC_SUBHANDLER(icq_snac_location_error) { struct { guint16 error; } pkt; guint16 error; if (ICQ_UNPACK(&buf, "W", &pkt.error)) error = pkt.error; else error = 0; icq_snac_error_handler(s, "location", error); return 0; } SNAC_SUBHANDLER(icq_snac_location_replyreq) { /* * Handle SNAC(0x2,0x3) -- Limitations/params response * */ debug_function("icq_snac_location_replyreq()\n"); #ifdef ICQ_DEBUG_UNUSED_INFORMATIONS struct icq_tlv_list *tlvs; icq_tlv_t *t; tlvs = icq_unpack_tlvs(&buf, &len, 0); for (t = tlvs; t; t = t->next) { if (tlv_length_check("icq_snac_location_replyreq()", t, 2)) continue; switch (t->type) { case 0x01: debug_white("Maximum signature length for this user: %d\n", t->nr); break; case 0x02: debug_white("Number of full UUID capabilities allowed: %d\n", t->nr); break; case 0x03: debug_white("Maximum number of email addresses to look up at once: %d\n", t->nr); break; case 0x04: debug_white("Largest CERT length for end to end encryption: %d\n", t->nr); break; case 0x05: debug_white("Number of short UUID capabilities allowed: %d\n", t->nr); break; default: debug_error("icq_snac_location_replyreq() Unknown type=0x%x\n", t->type); } } icq_tlvs_destroy(&tlvs); #endif return 0; } SNAC_SUBHANDLER(icq_user_online_info) { /* * Handle SNAC(0x2,0x6) -- User information response * */ struct { char *uid; guint16 warning; /* warning level (unused here) */ guint16 tlv_count; /* Number of TLV in fixed part (user online info) */ } pkt; char *uid, *descr = NULL; userlist_t *u; struct icq_tlv_list *tlvs; icq_tlv_t *t; if (!ICQ_UNPACK(&buf, "uWW", &pkt.uid, &pkt.warning, &pkt.tlv_count)) { debug_error("icq_user_online_info() Malformed SNAC(2,6)\n"); return -1; } uid = icq_uid(pkt.uid); u = userlist_find(s, uid); if (!u) { debug_warn("icq_user_online_info() Ignore unknown user: %s\n", uid); xfree(uid); return 0; } debug_function("icq_user_online_info() %s\n", uid); tlvs = icq_unpack_tlvs(&buf, &len, pkt.tlv_count); if ( !(t = icq_tlv_get(tlvs, 0x06)) ) { // we need only offline message if ( (t = icq_tlv_get(tlvs, 0x1d)) ) { guint16 a_type; guint8 a_flags, a_len; unsigned char *t_data = t->buf; int t_len = t->len; while (t_len > 0) { if (icq_unpack(t_data, &t_data, &t_len, "WCC", &a_type, &a_flags, &a_len)) { if ((a_type == 2) || (a_flags == 4)) icq_unpack_nc(t_data, a_len, "U", &descr); } t_data += a_len; t_len -= a_len; } if (descr) protocol_status_emit(s, uid, EKG_STATUS_NA, descr, time(NULL)); } } icq_tlvs_destroy(&tlvs); xfree(uid); return 0; } SNAC_SUBHANDLER(icq_watcher_notification) { /* * Handle SNAC(0x2,0x8) -- Watcher notification * */ debug_error("icq_watcher_notification() XXX\n"); return -3; } SNAC_SUBHANDLER(icq__update_dir_info_result) { /* * Handle SNAC(0x2,0xa) -- Update directory info reply * */ debug_error("icq__update_dir_info_result() XXX\n"); return -3; } SNAC_HANDLER(icq_snac_location_handler) { snac_subhandler_t handler; switch (cmd) { case 0x01: handler = icq_snac_location_error; break; case 0x03: handler = icq_snac_location_replyreq; break; /* Miranda: OK */ case 0x06: handler = icq_user_online_info; break; case 0x08: handler = icq_watcher_notification; break; case 0x0A: handler = icq__update_dir_info_result; break; default: handler = NULL; break; } if (!handler) { debug_error("icq_snac_location_handler() SNAC with unknown cmd: %.4x received\n", cmd); icq_hexdump(DEBUG_ERROR, buf, len); } else handler(s, buf, len, data); return 0; } // vim:syn=c ekg2-0.4~pre+20120506.1/plugins/icq/icq_snac_handlers_03buddy.c000066400000000000000000000363261175142753400235620ustar00rootroot00000000000000/* * (C) Copyright 2000-2001 Richard Hughes, Roland Rabien, Tristan Van de Vreede * (C) Copyright 2001-2002 Jon Keating, Richard Hughes * (C) Copyright 2002-2004 Martin Öberg, Sam Kothari, Robert Rainwater * (C) Copyright 2004-2008 Joe Kucera * * ekg2 port: * (C) Copyright 2006-2008 Jakub Zawadzki * 2008 Wiesław Ochmiński * * Protocol description with author's permission from: http://iserverd.khstu.ru/oscar/ * (C) Copyright 2000-2005 Alexander V. Shutko * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #include #include #include "icq.h" #include "misc.h" #include "icq_caps.h" #include "icq_const.h" #include "icq_flap_handlers.h" #include "icq_snac_handlers.h" SNAC_SUBHANDLER(icq_snac_buddy_error) { struct { guint16 error; } pkt; guint16 error; // XXX ?wo? TLV(8) - error subcode if (ICQ_UNPACK(&buf, "W", &pkt.error)) error = pkt.error; else error = 0; icq_snac_error_handler(s, "buddy", error); return 0; } SNAC_SUBHANDLER(icq_snac_buddy_reply) { struct icq_tlv_list *tlvs; if ((tlvs = icq_unpack_tlvs(&buf, &len, 0))) { icq_tlv_t *t_max_uins = icq_tlv_get(tlvs, 1); // Max number of contact list entries icq_tlv_t *t_max_watchers = icq_tlv_get(tlvs, 2); // Max number of watcher list entries guint16 max_uins = 0, max_watchers = 0; icq_unpack_tlv_word(t_max_uins, max_uins); icq_unpack_tlv_word(t_max_watchers, max_watchers); debug_white("icq_snac_buddy_reply() maxUins = %u maxWatchers = %u\n", max_uins, max_watchers); icq_tlvs_destroy(&tlvs); } else debug_error("icq_snac_buddy_reply() tlvs == NULL\n"); return 0; } void icq_pack_append_nullterm_msg(GString *pkt, const char *msg) { icq_pack_append(pkt, "w", (guint32) xstrlen(msg)+1); // null-terminated msg length if (xstrlen(msg)) g_string_append(pkt, msg); icq_pack_append(pkt, "c", (guint32) 0); // msg terminate } void icq_pack_append_rendezvous(GString *pkt, int version, int cookie, int mtype, int mflags, int accept, int priority) { icq_pack_append(pkt, "wwiiiiwicw wwiiiccww", (guint32) 27, // length of this data segment, always 27 (guint32) version, // protocol version (guint32) 0, (guint32) 0, (guint32) 0, (guint32) 0, // PSIG_MESSAGE (guint32) 0, // unknown (guint32) 3, // client capabilities flag (guint32) 0, // unknown byte cookie, // cookie (guint32) 14, // length of this data segment, always 14 cookie, // cookie (guint32) 0, (guint32) 0, (guint32) 0, // unknown, usually all zeros mtype, // msg type mflags, // msg flags (guint32) accept, // status code - accepted (guint32) priority // priority ??? XXX ); } static void icq_get_description(session_t *s, const char *uin, int status) { icq_private_t *j = s->priv; GString *pkt, *tlv5, *rdv; guint32 cookie1=rand(), cookie2=rand(); guint32 mtype, cookie = (j->cookie_seq-- && 0x7fff); debug_function("icq_get_description() for: %s\n", uin); switch (status) { case EKG_STATUS_AWAY: mtype = MTYPE_AUTOAWAY; break; case EKG_STATUS_GONE: mtype = MTYPE_AUTONA; break; case EKG_STATUS_XA: mtype = MTYPE_AUTOBUSY; break; case EKG_STATUS_DND: mtype = MTYPE_AUTODND; break; case EKG_STATUS_FFC: mtype = MTYPE_AUTOFFC; break; default: return; } pkt = g_string_new(NULL); icq_pack_append(pkt, "II", cookie1, cookie2); // cookie icq_pack_append(pkt, "W", (guint32) 2); // message type icq_pack_append(pkt, "s", uin); tlv5 = g_string_new(NULL); icq_pack_append(tlv5, "W", (guint32) 0); icq_pack_append(tlv5, "II", cookie1, cookie2); // cookie icq_pack_append_cap(tlv5, CAP_SRV_RELAY); // AIM_CAPS_ICQSERVERRELAY - Client supports channel 2 extended, TLV(0x2711) based messages. icq_pack_append(tlv5, "tW", icq_pack_tlv_word(0xA, 1)); // TLV 0x0A: acktype (1 = normal message) icq_pack_append(tlv5, "T", icq_pack_tlv(0x0F, NULL, 0)); // TLV 0x0F: unknown // RendezvousMessageData rdv = g_string_new(NULL); icq_pack_append_rendezvous(rdv, 9, cookie, mtype, MFLAG_AUTO, 1, 1); icq_pack_append_nullterm_msg(rdv, ""); icq_pack_append(tlv5, "T", icq_pack_tlv(0x2711, rdv->str, rdv->len)); g_string_free(rdv, TRUE); icq_pack_append(pkt, "T", icq_pack_tlv(0x05, tlv5->str, tlv5->len)); g_string_free(tlv5, TRUE); icq_pack_append(pkt, "T", icq_pack_tlv(0x03, NULL, 0)); // empty TLV 3 to get an ack from the server icq_makesnac(s, pkt, 0x04, 0x06, 0, 0); icq_send_pkt(s, pkt); } static void icq_get_user_info(session_t *s, userlist_t *u, struct icq_tlv_list *tlvs, int newstatus) { int caps = 0, desc_chg = 0; char *descr = NULL; icq_tlv_t *t; if (!u) return; debug_function("icq_get_user_info() %s\n", u->uid); descr = u->descr ? xstrdup(u->descr) : NULL; user_private_item_set_int(u, "idle", 0); user_private_item_set_int(u, "status_f", 0); user_private_item_set_int(u, "xstatus", 0); for (t = tlvs; t; t = t->next) { /* Check t->len */ switch (t->type) { case 0x01: if (tlv_length_check("icq_get_user_info()", t, 2)) continue; break; case 0x03: case 0x04: case 0x05: case 0x06: case 0x0a: case 0x0f: if (tlv_length_check("icq_get_user_info()", t, 4)) continue; break; } /* now we've got trusted length */ switch (t->type) { case 0x01: /* User class */ user_private_item_set_int(u, "class", t->nr); break; case 0x03: /* Time when client gone online (unix time_t) */ user_private_item_set_int(u, "online", t->nr); break; case 0x04: /* idle timer */ { guint16 idle; if ( (idle = t->nr) ) user_private_item_set_int(u, "idle", time(NULL) - 60*idle); break; } case 0x05: /* Time when this account was registered (unix time_t) */ user_private_item_set_int(u, "member", t->nr); break; case 0x06: { /* User status * * ICQ service presence notifications use user status field which consist * of two parts. First is a various flags (birthday flag, webaware flag, * etc). Second is a user status (online, away, busy, etc) flags. */ guint16 status = t->nr & 0xffff; guint16 flags = t->nr >> 16; user_private_item_set_int(u, "status_f", flags); debug_white(" %s status flags=0x%04x status=0x%04x\n", u->uid, flags, status); newstatus = icq2ekg_status(status); break; } case 0x0a: /* IP address */ { guint32 ip; if (icq_unpack_nc(t->buf, t->len, "i", &ip)) { if (ip) user_private_item_set_int(u, "ip", ip); } break; } case 0x0c: /* DC info */ { struct { guint32 ip; guint32 port; guint8 tcp_flag; guint16 version; guint32 conn_cookie; guint32 web_port; guint32 client_features; /* faked time signatures, used to identify clients */ guint32 ts1; guint32 ts2; guint32 ts3; guint16 junk; } tlv_c; if (!icq_unpack_nc(t->buf, t->len, "IICWIII", &tlv_c.ip, &tlv_c.port, &tlv_c.tcp_flag, &tlv_c.version, &tlv_c.conn_cookie, &tlv_c.web_port, &tlv_c.client_features)) { debug_error(" %s TLV(C) corrupted?\n", u->uid); continue; } else { user_private_item_set_int(u, "dcc.ip", tlv_c.ip); user_private_item_set_int(u, "dcc.port", tlv_c.port); user_private_item_set_int(u, "dcc.flag", tlv_c.tcp_flag); user_private_item_set_int(u, "version", tlv_c.version); user_private_item_set_int(u, "dcc.cookie", tlv_c.conn_cookie); user_private_item_set_int(u, "dcc.web_port", tlv_c.web_port); // ?wo? do we need it? if (t->len >= 12 && icq_unpack_nc(t->buf, t->len, "23 III", &tlv_c.ts1, &tlv_c.ts3, &tlv_c.ts3)) ; } break; } case 0x0d: /* Client capabilities list */ { unsigned char *data = t->buf; int d_len = t->len; if (tlv_length_check("icq_get_user_info()", t, t->len & ~0xF)) break; while (d_len > 0) { int cid = icq_cap_id(data); if (cid != CAP_UNKNOWN) { caps |= 1 << cid; } else if ((cid = icq_xstatus_id(data))) { user_private_item_set_int(u, "xstatus", cid); } else { /* ?wo? client id???*/ debug_error("Unknown cap\n"); icq_hexdump(DEBUG_ERROR, data, 0x10); } data += 0x10; // capability length d_len -= 0x10; } break; } case 0x0e: /* AOL users. No values */ break; case 0x0f: case 0x10: /* Online time in seconds */ break; case 0x19: /* short caps */ { unsigned char *data = t->buf; int d_len = t->len; while (d_len > 0) { int cid = icq_short_cap_id(data); if (cid != CAP_UNKNOWN) { caps |= 1 << cid; } else { /* WTF? */ } data += 2; // short capability length d_len -= 2; } break; } case 0x1d: /* user icon id & hash */ { static char empty_item[0x10] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; unsigned char *t_data = t->buf; int t_len = t->len; while (t_len > 0) { guint16 item_type; guint8 item_flags; guint8 item_len; if (!icq_unpack(t_data, &t_data, &t_len, "WCC", &item_type, &item_flags, &item_len)) { debug_error(" %s TLV(1D) corrupted?\n", u->uid); break; } /* just some validity check */ if (item_len > t_len) item_len = t_len; if ((item_type == 0x02) && (item_flags == 4)) { /* iChat online message */ if (item_len>4) { char *tmp; guint16 enc; icq_unpack_nc(t_data, item_len, "Uw", &tmp, &enc); descr = !enc ? ekg_utf8_to_core_dup(tmp) : xstrdup(tmp); } desc_chg = 1; } else if ((item_type == 0x0e) && (item_len>7)) { /* 000E: Custom Status (ICQ6) */ char *tmp = xstrndup((char *)t_data, item_len); if ( !xstrncmp(tmp, "icqmood", 7) && xisdigit(*(tmp+7)) ) { int xstatus = atoi(tmp+7) + 1; if (xstatus<=XSTATUS_COUNT) user_private_item_set_int(u, "xstatus", xstatus); } xfree(tmp); } else if (memcmp(t_data, empty_item, (item_len < 0x10) ? item_len : 0x10)) { /* Item types * 0000: AIM mini avatar * 0001: AIM/ICQ avatar ID/hash (len 5 or 16 bytes) * 0002: iChat online message * 0008: ICQ Flash avatar hash (16 bytes) * 0009: iTunes music store link * 000C: ICQ contact photo (16 bytes) * 000D: ? */ debug_white(" %s has got avatar: type: %d flags: %d\n", u->uid, item_type, item_flags); icq_hexdump(DEBUG_WHITE, t_data, item_len); /* XXX, display message, get? do something? */ } t_data += item_len; t_len -= item_len; } break; } default: if (t->len==4) debug_warn(" %s Unknown TLV(0x%x) len=4 v=%d (0x%x) (%s)\n", u->uid, t->type, t->nr, t->nr, t->nr?timestamp_time("%Y-%m-%d %H:%M:%S", t->nr):""); else debug_error(" %s Unknown TLV(0x%x) len=%d\n", u->uid, t->type, t->len); } } if (desc_chg || (newstatus != u->status)) { if (!descr && !desc_chg && u->descr) descr = xstrdup(u->descr); protocol_status_emit(s, u->uid, newstatus, descr, time(NULL)); } if (!desc_chg) { if (u->status == EKG_STATUS_NA) { icq_send_snac(s, 0x02, 0x05, NULL, NULL, /* Request user info */ "Ws", (guint32) 1, /* request type (1 - general info, 2 - short user info, 3 - away message, 4 - client capabilities) */ u->uid+4); } else { icq_get_description(s, u->uid+4, u->status); } } if (u->status == EKG_STATUS_NA) { user_private_item_set_int(u, "online", 0); user_private_item_set_int(u, "last_ip", user_private_item_get_int(u, "ip")); user_private_item_set_int(u, "ip", 0); if (user_private_item_get_int(u, "version") < 8) { caps &= ~(1<uid); } } user_private_item_set_int(u, "caps", caps); user_private_item_set_int(u, "utf", (caps && (1< 0); return 0; } SNAC_SUBHANDLER(icq_snac_buddy_offline) { /* SNAC(03,0C) SRV_USER_OFFLINE User offline notification * * Server send this when user from your contact list goes offline. */ struct { char *uid; guint16 warning; guint16 count; } pkt; char *uid; userlist_t *u; struct icq_tlv_list *tlvs; debug_function("icq_snac_buddy_offline()\n"); do { if (!ICQ_UNPACK(&buf, "uWW", &pkt.uid, &pkt.warning, &pkt.count)) return -1; uid = icq_uid(pkt.uid); u = userlist_find(s, uid); tlvs = icq_unpack_tlvs(&buf, &len, pkt.count); icq_get_user_info(s, u, tlvs, EKG_STATUS_NA); icq_tlvs_destroy(&tlvs); xfree(uid); } while (len >= 1); return 0; } SNAC_SUBHANDLER(icq_snac_buddy_notify_rejected) { char *uid; if (!ICQ_UNPACK(&buf, "u", &uid)) // XXX ?wo? may be repeated more then once? return -1; debug_function("icq_snac_buddy_notify_rejected() for: %s\n", uid); return 0; } SNAC_HANDLER(icq_snac_buddy_handler) { snac_subhandler_t handler; switch (cmd) { case 0x01: handler = icq_snac_buddy_error; break; /* Miranda: OK */ case 0x03: handler = icq_snac_buddy_reply; break; /* Miranda: OK */ case 0x0a: handler = icq_snac_buddy_notify_rejected; /* Miranda: OK */ break; /* .... */ case 0x0b: handler = icq_snac_buddy_online; break; /* Miranda: handleUserOnline() */ case 0x0c: handler = icq_snac_buddy_offline; break; /* Miranda: OK */ default: handler = NULL; break; } if (!handler) { debug_error("icq_snac_buddy_handler() SNAC with unknown cmd: %.4x received\n", cmd); icq_hexdump(DEBUG_ERROR, buf, len); } else handler(s, buf, len, data); return 0; } // vim:syn=c ekg2-0.4~pre+20120506.1/plugins/icq/icq_snac_handlers_04message.c000066400000000000000000000475531175142753400241040ustar00rootroot00000000000000/* * (C) Copyright 2000-2001 Richard Hughes, Roland Rabien, Tristan Van de Vreede * (C) Copyright 2001-2002 Jon Keating, Richard Hughes * (C) Copyright 2002-2004 Martin Öberg, Sam Kothari, Robert Rainwater * (C) Copyright 2004-2008 Joe Kucera * * ekg2 port: * (C) Copyright 2006-2008 Jakub Zawadzki * 2008 Wiesław Ochmiński * * Protocol description with author's permission from: http://iserverd.khstu.ru/oscar/ * (C) Copyright 2000-2005 Alexander V. Shutko * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #include #include #include "icq.h" #include "misc.h" #include "icq_caps.h" #include "icq_const.h" #include "icq_flap_handlers.h" #include "icq_snac_handlers.h" typedef struct { guint32 id1; guint32 id2; guint16 channel; /* Channel 1 message format (plain-text messages) Channel 2 message format (rtf messages, rendezvous) Channel 4 message format (typed old-style messages) */ char *sender; int ack_type; int msg_type; /* message type */ int msg_flags; /* message flags */ int version; int cookie; char uid[50]; session_t *s; userlist_t *u; } msg_params_t; SNAC_SUBHANDLER(icq_snac_message_error) { struct { guint16 error; } pkt; if (!ICQ_UNPACK(&buf, "W", &pkt.error)) return -1; debug_error("icq_snac_message_error() XXX\n"); icq_snac_error_handler(s, "message", pkt.error); return 0; } static void icq_snac_message_set_msg_channel(session_t *s, guint16 chan, guint32 flags) { icq_send_snac(s, 0x04, 0x02, 0, 0, "WIWWWWW", (guint32) chan, (guint32) flags, /* channel, flags */ (guint16) 8000, (guint32) 999, /* max-message-snac-size, max-sender-warning-level */ (guint32) 999, (guint32) 0, /* max-rcv-warning-level, minimum message-interval-in-secons */ (guint32) 0); /* unknown */ } SNAC_SUBHANDLER(icq_snac_message_replyicbm) { #if 1 icq_snac_message_set_msg_channel(s, 0x01, 0x0b); icq_snac_message_set_msg_channel(s, 0x02, 0x03); icq_snac_message_set_msg_channel(s, 0x04, 0x03); #else /* Miranda-like */ guint32 flags; /* Set message parameters for all channels (imitate ICQ 6) */ flags = 0x00000303; #ifdef DBG_CAPHTML flags |= 0x00000400; #endif #ifdef DBG_CAPMTN flags |= 0x00000008; #endif icq_snac_message_set_msg_channel(s, 0x00, flags); #endif return 0; } static void icq_pack_append_msg_header(GString *pkt, msg_params_t *msg_param) { icq_pack_append(pkt, "IIWsW", msg_param->id1, // message id part 1 msg_param->id2, // message id part 2 (guint32) 2, // channel msg_param->sender, (guint32) 3 // message formating ); } static void icq_send_adv_msg_ack(session_t *s, msg_params_t *msg_param) { GString *pkt = g_string_new(NULL); icq_pack_append_msg_header(pkt, msg_param); icq_pack_append_rendezvous(pkt, ICQ_VERSION, msg_param->cookie, msg_param->msg_type, msg_param->msg_flags, 0, 0); icq_pack_append_nullterm_msg(pkt, ""); icq_makesnac(s, pkt, 0x04, 0x0b, NULL, NULL); /* */ icq_send_pkt(s, pkt); } static int icq_snac_message_recv_icbm_ch1(session_t *s, unsigned char *buf, int len, msg_params_t *msg_param) { struct icq_tlv_list *tlvs; struct icq_tlv_list *tlvs_msg; icq_tlv_t *t; debug_function("icq_snac_message_recv_icbm_ch1() from: %s leftlen: %d\n", msg_param->sender, len); if (!(tlvs = icq_unpack_tlvs(&buf, &len, 0))) { debug("icq_snac_message_recv_icbm_ch1() ignoring empty message.\n"); return 0; } if (!(t = icq_tlv_get(tlvs, 0x02)) || t->type != 0x02) { debug_error("icq_snac_message_recv_icbm_ch1() TLV(0x02) not found?\n"); icq_tlvs_destroy(&tlvs); return 1; } // TLV(2) contains yet another TLV chain with the following TLVs: // TLV(0x501): Capability // TLV(0x101): this TLV contains the actual message (can be fragmented) if (!(tlvs_msg = icq_unpack_tlvs_nc(t->buf, t->len, 0))) { debug_error("icq_snac_message_recv_icbm_ch1() failed to read tlv chain in message\n"); icq_tlvs_destroy(&tlvs); return 1; } if ((t = icq_tlv_get(tlvs_msg, 0x501))) debug("icq_snac_message_recv_icbm_ch1() message has: %d caps\n", t->len); else debug("icq_snac_message_recv_icbm_ch1() no message cap\n"); { /* Parse the message parts, usually only one 0x0101 TLV containing the message, * but in some cases there can be more 0x0101 TLVs containing message parts in * different encoding (just like the new format of Offline Messages) */ struct icq_tlv_list *t; GString *msg = g_string_new(NULL); time_t sent; for (t = tlvs_msg; t; t = t->next) { struct { guint16 encoding; guint16 codepage; unsigned char *message; } t_msg; int t_len = t->len; char *recode = NULL; if (t->type != 0x0101) continue; if (!icq_unpack(t->buf, &t_msg.message, &t_len, "WW", &t_msg.encoding, &t_msg.codepage)) continue; debug_function("icq_snac_message_recv_icbm_ch1() enc: %.4x cp: %.4x\n", t_msg.encoding, t_msg.codepage); switch (t_msg.encoding) { case 0x02: /* UCS-2BE */ recode = icq_convert_from_ucs2be((char *)t_msg.message, t_len); break; case 0x00: /* US-ASCII */ case 0x03: /* ANSI */ recode = xstrndup((char *) t_msg.message, t_len); break; default: debug_error("icq_snac_message_recv_icbm_ch1() Unsupported encoding 0x%x\n", t_msg.encoding); } g_string_append(msg, recode); xfree(recode); } /* XXX, check if message was recv when we was offline */ sent = time(NULL); if (msg->len) { int msgclass = xisdigit(*(msg_param->sender)) ? EKG_MSGCLASS_CHAT : EKG_MSGCLASS_SYSTEM; protocol_message_emit(s, msg_param->uid, NULL, msg->str, NULL, sent, msgclass, NULL, EKG_TRY_BEEP, 0); } g_string_free(msg, TRUE); } icq_tlvs_destroy(&tlvs); icq_tlvs_destroy(&tlvs_msg); return 0; } static void icq_send_status_descr(session_t *s, int msg_type, msg_params_t *msg_param) { GString *pkt; char *desc; if (!msg_param->u) { debug_warn("icq_send_status_descr(). Ignore request from %s\n", msg_param->uid); return; } debug_function("icq_send_status_descr() to %s\n", msg_param->uid); pkt = g_string_new(NULL); desc = xstrdup(s->descr); icq_pack_append_msg_header(pkt, msg_param); icq_pack_append_rendezvous(pkt, msg_param->version, msg_param->cookie, msg_type, MFLAG_AUTO, 0, 0); if (msg_param->version == 9) /* unicode */ desc = ekg_locale_to_utf8(desc); else { /* XXX recode? */ } icq_pack_append_nullterm_msg(pkt, desc); xfree(desc); icq_makesnac(s, pkt, 0x04, 0x0b, NULL, NULL); icq_send_pkt(s, pkt); } static int icq_snac_message_recv_rtf2711(session_t *s, unsigned char *buf, int len, msg_params_t *msg_param) { struct { guint16 len; /* length of following data (LE) */ guint16 ver; /* protocol version (LE) */ unsigned char *plug; /* plugin or zero bytes (len=0x16) */ guint16 _unkn1; /* unknown */ guint32 _capflg; /* client capabilities flags */ guint8 _unkn2; /* unknown */ guint16 _cookie; /* cookie (LE) */ } pkt1; struct { guint16 len; /* length of following data (LE) */ guint16 cookie; /* cookie as in first chunk above (LE) */ char *_unkn; /* unknown, usually zeros */ } pkt2; /* if plugin field in first chunk above is zero, here is message, overwise here is plugin-specific data. */ struct { guint8 type; /* message type */ guint8 flags; /* message flags */ guint16 status; /* status code (LE) */ guint16 prio; /* priority code (LE) */ guint16 len; /* message string length (LE) */ char *str; /* message string (null-terminated) */ } msg; debug_function("icq_snac_message_recv_rtf2711()\n"); if ( len<27 ) { debug_error("icq_snac_message_recv_rtf2711() packet too short\n"); return 1; } icq_unpack_nc(buf, len, "ww", &pkt1.len, &pkt1.ver); pkt1.plug = buf + 4; msg_param->version = pkt1.ver; if (msg_param->u) user_private_item_set_int(msg_param->u, "version", pkt1.ver); /* next chunk */ buf += pkt1.len + 2; len -= pkt1.len + 2; icq_unpack_nc(buf, len, "ww", &pkt2.len, &pkt2.cookie); msg_param->cookie = pkt2.cookie; /* next chunk */ buf += pkt2.len + 2; len -= pkt2.len + 2; switch (icq_plugin_id(pkt1.plug)) { case PSIG_MESSAGE: { /* message */ ICQ_UNPACK(&buf, "ccwww", &msg.type, &msg.flags, &msg.status, &msg.prio, &msg.len); msg.str = (char *)buf; debug_white("icq_snac_message_recv_rtf2711() PSIG_MESSAGE, msg type:0x%x, flags:0x%x, ack:0x%x\n", msg.type, msg.flags, msg_param->ack_type); msg_param->msg_type = msg.type; msg_param->msg_flags = msg.flags; switch (msg.type) { /* strange types - XXX ?wo? handle? */ case MTYPE_FILEREQ: case MTYPE_CHAT: case MTYPE_PLUGIN: debug_warn("icq_snac_message_recv_rtf2711() message type 0x%x not handled yet\n", msg.type); break; case MTYPE_AUTOAWAY: case MTYPE_AUTOBUSY: case MTYPE_AUTONA: case MTYPE_AUTODND: case MTYPE_AUTOFFC: /* Somebody ask for our status */ icq_send_status_descr(s, msg.type, msg_param); break; case MTYPE_PLAIN: /* plain message */ { buf += msg.len; len -= msg.len; if (msg.len > 0) { time_t sent = time(NULL); char *tmp = ekg_utf8_to_core_dup(msg.str); protocol_message_emit(s, msg_param->uid, NULL, tmp, NULL, sent, EKG_MSGCLASS_CHAT, NULL, EKG_TRY_BEEP, 0); xfree(tmp); } icq_send_adv_msg_ack(s, msg_param); break; } default: debug_error("icq_snac_message_recv_rtf2711() PSIG_MESSAGE, Not supported message type:0x%x\n", msg.type); break; } break; } default: debug_error("icq_snac_message_recv_rtf2711() we've got data for unknown plugin\n"); icq_hexdump(DEBUG_ERROR, buf, len); } return 0; } static int icq_snac_message_recv_icbm_ch2(session_t *s, unsigned char *buf, int len, msg_params_t *msg_param) { struct { guint16 type; // message type (0 - normal, 1 - cancel, 2 - ack) guint32 _id1, _id2; // msg-id cookie (unused here) unsigned char *cap; // capability (determines format of message data) } pkt; struct icq_tlv_list *tlvs, *tlvs5 = NULL; icq_tlv_t *t, *t5, *t2711; debug_function("icq_snac_message_recv_icbm_ch2() from: %s leftlen: %d\n", msg_param->sender, len); if (!(tlvs = icq_unpack_tlvs(&buf, &len, 0))) { debug("icq_snac_message_recv_icbm_ch2() ignoring empty message.\n"); return 0; } if (!(t5 = icq_tlv_get(tlvs, 0x05))) { debug_error("icq_snac_message_recv_icbm_ch2() TLV(0x05) not found?\n"); icq_tlvs_destroy(&tlvs); return 1; } if (t5->len < 2 + 8 + 0x10) { debug_error("icq_snac_message_recv_icbm_ch2() TLV(0x05) too short\n"); icq_tlvs_destroy(&tlvs); return 1; } icq_unpack_nc(t5->buf, t5->len, "WII", &pkt.type, &pkt._id1, &pkt._id2); pkt.cap = t5->buf + (2 + 4 + 4); /* msg capability */ t5->buf += (2 + 4 + 4 + 0x10); t5->len -= (2 + 4 + 4 + 0x10); /* contents of TLV(0x05) is capability-specific */ switch (icq_cap_id(pkt.cap)) { case CAP_SRV_RELAY: { if (pkt.type == 1) { debug_warn("icq_snac_message_recv_icbm_ch2() Can't handle abort message yet\n"); icq_tlvs_destroy(&tlvs); return 0; } if ( !(tlvs5 = icq_unpack_tlvs_nc(t5->buf, t5->len, 0)) ) { debug("icq_snac_message_recv_icbm_ch2() ignoring empty TLV(0x05).\n"); icq_tlvs_destroy(&tlvs); return 0; } /* tlvs5 may contain the following TVLs: * * TLV.Type(0x0A) - Acktype: * 0x0000 - normal message * 0x0001 - file request / abort request * 0x0002 - file ack * * TLV.Type(0x03) - external ip * TLV.Type(0x04) - internal ip * TLV.Type(0x05) - listening port * TLV.Type(0x0F) - unknown (empty) * TLV.Type(0x2711) - extention data */ if ( (t = icq_tlv_get(tlvs5, 0x0a) ) ) msg_param->ack_type = t->nr; else msg_param->ack_type = 1; if ( msg_param->u && (t = icq_tlv_get(tlvs5, 0x03)) ) // External IP user_private_item_set_int(msg_param->u, "IP", t->nr); if (!(t2711 = icq_tlv_get(tlvs5, 0x2711))) { debug_error("icq_snac_message_recv_icbm_ch2() TLV(0x2711) not found?\n"); icq_tlvs_destroy(&tlvs5); icq_tlvs_destroy(&tlvs); return 1; } icq_snac_message_recv_rtf2711(s, t2711->buf, t2711->len, msg_param); break; } /* TO DO! ?wo? XXX */ case CAP_ICQDIRECT: // handle reverse DC request case CAP_SENDFILE: // handle OFT packet case CAP_CONTACTS: // handle contacts transfer debug_error("XXX icq_snac_message_recv_icbm_ch2() don't handle this yet\n"); icq_hexdump(DEBUG_ERROR, t5->buf, t5->len); break; default: debug_error("icq_snac_message_recv_icbm_ch2() Unknow 0x2711 message for capability id=%d\n", icq_cap_id(pkt.cap)); icq_hexdump(DEBUG_ERROR, t5->buf, t5->len); break; } icq_tlvs_destroy(&tlvs5); icq_tlvs_destroy(&tlvs); return 0; } static int icq_snac_message_recv_icbm_ch4(session_t *s, unsigned char *buf, int len, msg_params_t *msg_param) { debug_error("XXX icq_snac_message_recv_icbm_ch4()\n"); icq_hexdump(DEBUG_ERROR, buf, len); return 0; } static int icq_snac_unpack_message_params(session_t *s, unsigned char **buf, int *len, msg_params_t *msg_param) { /* init msg_params */ memset(msg_param, 0, sizeof(msg_params_t)); if (!icq_unpack(*buf, buf, len, "IIWu", &msg_param->id1, &msg_param->id2, &msg_param->channel, &msg_param->sender)) return 0; char *uid = icq_uid(msg_param->sender); msg_param->s = s; memcpy(msg_param->uid, uid, xstrlen(uid)+1); msg_param->u = userlist_find(s, uid); xfree(uid); return 1; /* OK */ } SNAC_SUBHANDLER(icq_snac_message_recv) { msg_params_t msg_param; struct { guint16 warning_level; /* not used */ guint16 tlv_count; } pkt; struct icq_tlv_list *tlvs; if (!icq_snac_unpack_message_params(s, &buf, &len, &msg_param) || !ICQ_UNPACK(&buf, "WW", &pkt.warning_level, &pkt.tlv_count) ) { debug_error("icq_snac_message_recv() Malformed message thru server\n"); return -1; } debug_function("icq_snac_message_recv() from: %s id1: %.8x id2: %.8x channel: %.4x warning: %.4x tlvs: %.4x\n", msg_param.sender, msg_param.id1, msg_param.id2, msg_param.channel, pkt.warning_level, pkt.tlv_count); /* XXX, spamer? */ tlvs = icq_unpack_tlvs(&buf, &len, pkt.tlv_count); /* * TLV.Type(0x01) - user class * TLV.Type(0x03) - account creation time * TLV.Type(0x06) - user status * TLV.Type(0x0F) - online time */ icq_tlvs_destroy(&tlvs); switch (msg_param.channel) { case 0x01: /* plain-text messages */ icq_snac_message_recv_icbm_ch1(s, buf, len, &msg_param); break; case 0x02: /* rtf messages, rendezvous */ icq_snac_message_recv_icbm_ch2(s, buf, len, &msg_param); break; case 0x04: /* yped old-style messages */ icq_snac_message_recv_icbm_ch4(s, buf, len, &msg_param); break; default: debug_error("icq_snac_message_recv() unknown format message from server. Channel:%d Sender: %s\n", msg_param.channel, msg_param.sender); /* dump message */ icq_hexdump(DEBUG_ERROR, buf, len); break; } return 0; } SNAC_SUBHANDLER(icq_snac_message_server_ack) { msg_params_t msg_param; if (!icq_snac_unpack_message_params(s, &buf, &len, &msg_param) ) { debug_error("icq_snac_message_server_ack() packet to short!\n"); return -1; } debug_error("XXX icq_snac_message_server_ack() chan=%.4x uid=%s\n", msg_param.channel, msg_param.sender); /* XXX, cookie, etc.. */ return 0; } static void icq_snac_message_status_reply(msg_params_t *msg_param, char *msg) { char *descr; if (!msg_param->u) { debug_warn("icq_snac_message_status_reply() Ignoring status description from unknown %s msg: %s\n", msg_param->uid, msg); return; } debug_function("icq_snac_message_status_reply() status from %s msg: %s\n", msg_param->uid, msg); if (msg_param->version == 9) /* utf-8 message */ descr = ekg_utf8_to_core_dup(msg); else descr = xstrdup(msg); /* We change only description, not status */ protocol_status_emit(msg_param->s, msg_param->uid, msg_param->u->status, descr, time(NULL)); xfree(descr); } SNAC_SUBHANDLER(icq_snac_message_response) { msg_params_t msg_param; struct { guint16 reason; /* reason code (1 - unsupported channel, 2 - busted payload, 3 - channel specific) */ guint16 len; guint16 version; /* this can be v8 greeting message reply */ /* 27b unknowns from the msg we sent */ guint16 cookie; /* Message sequence (cookie) */ /* 12b Unknown */ guint8 msg_type; /* Message type: MTYPE_AUTOAWAY, MTYPE_AUTOBUSY, etc. */ guint8 flags; /* Message flags: MFLAG_NORMAL, MFLAG_AUTO, etc. */ guint16 status; /* Status */ /* 2b Priority? */ guint16 msg_len; } pkt; if (!icq_snac_unpack_message_params(s, &buf, &len, &msg_param)) return -1; debug_function("icq_snac_message_response() uid: %s\n", msg_param.sender); if (msg_param.channel != 0x02) { debug_error("icq_snac_message_response() unknown type: %.4x\n", msg_param.channel); return 0; } /* XXX, cookie, check cookie uid */ if (!ICQ_UNPACK(&buf, "ww", &pkt.reason, &pkt.len)) pkt.len = 0; if (pkt.len == 0x1b && 1 /* XXX */) { if (!ICQ_UNPACK(&buf, "w27w12ccw2", &pkt.version, &pkt.cookie, &pkt.msg_type, &pkt.flags, &pkt.status)) return -1; msg_param.version = pkt.version; /* XXX, more cookies... */ } else { /* XXX */ pkt.flags = 0; } if (pkt.flags == MFLAG_AUTO) { /* A status message reply */ char *reason; if (len < 2 || !ICQ_UNPACK(&buf, "w", &pkt.msg_len)) return -1; reason = xstrndup((char *) buf, pkt.msg_len); icq_snac_message_status_reply(&msg_param, reason); xfree(reason); } else { /* XXX */ debug_error("icq_snac_message_response() Sorry, we dont't handle it yet\n"); icq_hexdump(DEBUG_ERROR, buf, len); } return 0; } SNAC_SUBHANDLER(icq_snac_message_mini_typing_notification) { #ifndef DBG_CAPMTN debug_error("icq_snac_message_mini_typing_notification() Ignoring unexpected typing notification"); #else msg_params_t msg_param; struct { guint16 typing; } pkt; if (!icq_snac_unpack_message_params(s, &buf, &len, &msg_param) || !ICQ_UNPACK(&buf, "W", &pkt.typing)) return -1; /* SetContactCapabilities(hContact, CAPF_TYPING); */ switch (pkt.typing) { case 0x0000: /* MTN_FINISHED */ protocol_xstate_emit(s, msg_param.uid, 0, EKG_XSTATE_TYPING); break; case 0x0001: /* MTN_TYPED */ /* XXX, accroding to Miranda code: user stopped typing... (like MTN_FINISHED) */ case 0x0002: /* MTN_BEGUN */ protocol_xstate_emit(s, msg_param.uid, EKG_XSTATE_TYPING, 0); break; case 0x000F: /* MTN_WINDOW_CLOSED */ print_info(msg_param.uid, s, "icq_window_closed", format_user(s, msg_param.uid)); break; default: debug_warn("icq_snac_message_mini_typing_notification() uid: %s, UNKNOWN typing!!! (0x%x)\n", msg_param.sender, pkt.typing); } #endif return 0; } SNAC_SUBHANDLER(icq_snac_message_queue) { /* SNAC(4, 0x17) Offline Messages response */ debug_error("icq_snac_message_queue() XXX\n"); return -3; } SNAC_HANDLER(icq_snac_message_handler) { snac_subhandler_t handler; switch (cmd) { case 0x01: handler = icq_snac_message_error; break; case 0x05: handler = icq_snac_message_replyicbm; break; /* Miranda: OK */ case 0x07: handler = icq_snac_message_recv; break; case 0x0B: handler = icq_snac_message_response; break; case 0x0C: handler = icq_snac_message_server_ack; break; case 0x14: handler = icq_snac_message_mini_typing_notification; break; case 0x17: handler = icq_snac_message_queue; break; default: handler = NULL; break; } if (!handler) { debug_error("icq_snac_message_handler() SNAC with unknown cmd: %.4x received\n", cmd); icq_hexdump(DEBUG_ERROR, buf, len); } else handler(s, buf, len, data); return 0; } // vim:syn=c ekg2-0.4~pre+20120506.1/plugins/icq/icq_snac_handlers_09bos.c000066400000000000000000000074061175142753400232410ustar00rootroot00000000000000/* * (C) Copyright 2000-2001 Richard Hughes, Roland Rabien, Tristan Van de Vreede * (C) Copyright 2001-2002 Jon Keating, Richard Hughes * (C) Copyright 2002-2004 Martin Öberg, Sam Kothari, Robert Rainwater * (C) Copyright 2004-2008 Joe Kucera * * ekg2 port: * (C) Copyright 2006-2008 Jakub Zawadzki * 2008 Wiesław Ochmiński * * Protocol description with author's permission from: http://iserverd.khstu.ru/oscar/ * (C) Copyright 2000-2005 Alexander V. Shutko * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include "icq.h" #include "misc.h" #include "icq_caps.h" #include "icq_const.h" #include "icq_flap_handlers.h" #include "icq_snac_handlers.h" SNAC_SUBHANDLER(icq_snac_bos_error) { /* SNAC(09,01) SRV_PRIVACY_ERROR Client/server error * * This is an error notification snac. */ struct { guint16 error; } pkt; if (!ICQ_UNPACK(&buf, "W", &pkt.error)) pkt.error = 0; /* XXX TLV.Type(0x08) - error subcode */ icq_snac_error_handler(s, "bos", pkt.error); return 0; } SNAC_SUBHANDLER(icq_snac_bos_replyreq) { /* SNAC(09,03) SRV_PRIVACY_RIGHTS_REPLY Requested service parameters * * Server replies with this SNAC to SNAC(09,02) - client service parameters request. */ if (len < 12) { // Failure debug_error("icq_snac_bos_replyreq() Malformed\n"); return 0; } #if ICQ_DEBUG_UNUSED_INFORMATIONS struct icq_tlv_list *tlvs; icq_tlv_t *t_max_visible_contacts, *t_max_invisible_contacts, *t_max_temp_visible_contacts; guint16 max_visible_contacts, max_invisible_contacts, max_temp_visible_contacts; if (!(tlvs = icq_unpack_tlvs(&buf, &len, 0))) return 0; t_max_visible_contacts = icq_tlv_get(tlvs, 1); t_max_invisible_contacts = icq_tlv_get(tlvs, 2); t_max_temp_visible_contacts = icq_tlv_get(tlvs, 3); icq_unpack_tlv_word(t_max_visible_contacts, max_visible_contacts); icq_unpack_tlv_word(t_max_invisible_contacts, max_invisible_contacts); icq_unpack_tlv_word(t_max_temp_visible_contacts, max_temp_visible_contacts); debug_white("icq_snac_bos_replyreq() Max visible %u, max invisible %u, max temporary visible %u items.\n", max_visible_contacts, max_invisible_contacts, max_temp_visible_contacts); icq_tlvs_destroy(&tlvs); #endif return 0; } SNAC_SUBHANDLER(icq_snac_bos_service_error) { /* SNAC(09,09) SRV_BOS_ERROR Service error * * This is an error notification snac. Known error codes: 0x01 - wrong mode */ struct { guint16 error; } pkt; if (!ICQ_UNPACK(&buf, "W", &pkt.error)) pkt.error = 0; /* XXX TLV.Type(0x08) - error subcode */ /* XXX TLV.Type(0x04) - error description url */ icq_snac_error_handler(s, "bos", pkt.error); return 0; } SNAC_HANDLER(icq_snac_bos_handler) { snac_subhandler_t handler; switch (cmd) { case 0x01: handler = icq_snac_bos_error; break; case 0x03: handler = icq_snac_bos_replyreq; break; case 0x09: handler = icq_snac_bos_service_error; break; default: handler = NULL; break; } if (!handler) { debug_error("icq_snac_bos_handler() SNAC with unknown cmd: %.4x received\n", cmd); icq_hexdump(DEBUG_ERROR, buf, len); } else handler(s, buf, len, data); return 0; } // vim:syn=c ekg2-0.4~pre+20120506.1/plugins/icq/icq_snac_handlers_0Alookup.c000066400000000000000000000050221175142753400237670ustar00rootroot00000000000000/* * (C) Copyright 2000,2001 Richard Hughes, Roland Rabien, Tristan Van de Vreede * (C) Copyright 2001,2002 Jon Keating, Richard Hughes * (C) Copyright 2002,2003,2004 Martin Öberg, Sam Kothari, Robert Rainwater * (C) Copyright 2004,2005,2006 Joe Kucera * * ekg2 port: * (C) Copyright 2006-2008 Jakub Zawadzki * 2008 Wiesław Ochmiński * * Protocol description with author's permission from: http://iserverd.khstu.ru/oscar/ * (C) Copyright 2000-2005 Alexander V. Shutko * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include "icq.h" #include "misc.h" #include "icq_caps.h" #include "icq_const.h" #include "icq_flap_handlers.h" #include "icq_snac_handlers.h" SNAC_SUBHANDLER(icq_snac_lookup_error) { /* SNAC(0A,01) SRV_EMAILxSEARCH_ERROR Client/server error * * This is an error notification snac. You'll receive it when search fails. */ struct { guint16 error; } pkt; if (!ICQ_UNPACK(&buf, "W", &pkt.error)) pkt.error = 0; /* XXX TLV.Type(0x08) - error subcode */ icq_snac_error_handler(s, "lookup", pkt.error); return 0; } SNAC_SUBHANDLER(icq_snac_lookup_replyreq) { /* SNAC(0A,03) SRV_SEARCHxEMAIL_REPLY Search response * * This is the server reply to client search by email request. It contain * found screennames associated with requested email. Server replies with * this SNAC to SNAC(0A,02). */ /* XXX list of tlv(1) */ return -3; /* not handled yet */ } SNAC_HANDLER(icq_snac_lookup_handler) { snac_subhandler_t handler; switch (cmd) { case 0x01: handler = icq_snac_lookup_error; break; case 0x03: handler = icq_snac_lookup_replyreq; break; default: handler = NULL; break; } if (!handler) { debug_error("icq_snac_lookup_handler() SNAC with unknown cmd: %.4x received\n", cmd); icq_hexdump(DEBUG_ERROR, buf, len); } else handler(s, buf, len, data); return 0; } // vim:syn=c ekg2-0.4~pre+20120506.1/plugins/icq/icq_snac_handlers_0Bstatus.c000066400000000000000000000061021175142753400240020ustar00rootroot00000000000000/* * (C) Copyright 2000,2001 Richard Hughes, Roland Rabien, Tristan Van de Vreede * (C) Copyright 2001,2002 Jon Keating, Richard Hughes * (C) Copyright 2002,2003,2004 Martin Öberg, Sam Kothari, Robert Rainwater * (C) Copyright 2004,2005,2006 Joe Kucera * * ekg2 port: * (C) Copyright 2006-2008 Jakub Zawadzki * 2008 Wiesław Ochmiński * * Protocol description with author's permission from: http://iserverd.khstu.ru/oscar/ * (C) Copyright 2000-2005 Alexander V. Shutko * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include "icq.h" #include "misc.h" #include "icq_caps.h" #include "icq_const.h" #include "icq_flap_handlers.h" #include "icq_snac_handlers.h" SNAC_SUBHANDLER(icq_snac_status_error) { /* SNAC(0B,01) SRV_STATS_ERROR Client / server error * * This is an error notification snac. */ struct { guint16 error; } pkt; if (!ICQ_UNPACK(&buf, "W", &pkt.error)) pkt.error = 0; /* XXX TLV.Type(0x08) - error subcode? */ icq_snac_error_handler(s, "status", pkt.error); return 0; } SNAC_SUBHANDLER(icq_snac_status_minreport) { /* SNAC(0B,02) SRV_SET_MINxREPORTxINTERVAL Set minimum report interval * * Server send this to client after login. This snac contain minimum stats report * interval value. Client should send stats report every 1200 hours (default value). */ struct { guint16 interval; // min interval between stats reports (hours) } pkt; if (!ICQ_UNPACK(&buf, "W", &pkt.interval)) return -1; #if ICQ_DEBUG_UNUSED_INFORMATIONS debug_white("icq_snac_status_minreport() minimum interval between stats reports: %u [hours]\n", pkt.interval); #endif return 0; } SNAC_SUBHANDLER(icq_snac_status_report_ack) { /* SNAC(0B,04) SRV_REPORT_ACK Usage stats report ack * * Server send this as an ack for client stats report. Currently used only * by AIM service. ICQ clients never send stat reports. * Empty snac (no snac data) */ return 0; } SNAC_HANDLER(icq_snac_status_handler) { snac_subhandler_t handler; switch (cmd) { case 0x01: handler = icq_snac_status_error; break; case 0x02: handler = icq_snac_status_minreport; break; case 0x04: handler = icq_snac_status_report_ack; break; default: handler = NULL; break; } if (!handler) { debug_error("icq_snac_status_handler() SNAC with unknown cmd: %.4x received\n", cmd); icq_hexdump(DEBUG_ERROR, buf, len); } else handler(s, buf, len, data); return 0; } // vim:syn=c ekg2-0.4~pre+20120506.1/plugins/icq/icq_snac_handlers_13userlist.c000066400000000000000000000500761175142753400243240ustar00rootroot00000000000000/* * (C) Copyright 2000-2001 Richard Hughes, Roland Rabien, Tristan Van de Vreede * (C) Copyright 2001-2002 Jon Keating, Richard Hughes * (C) Copyright 2002-2004 Martin Öberg, Sam Kothari, Robert Rainwater * (C) Copyright 2004-2008 Joe Kucera * * ekg2 port: * (C) Copyright 2006-2008 Jakub Zawadzki * 2008 Wiesław Ochmiński * * Protocol description with author's permission from: http://iserverd.khstu.ru/oscar/ * (C) Copyright 2000-2005 Alexander V. Shutko * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include "icq.h" #include "misc.h" #include "icq_caps.h" #include "icq_const.h" #include "icq_flap_handlers.h" #include "icq_snac_handlers.h" static SNAC_SUBHANDLER(icq_snac_userlist_error) { struct { guint16 error; } pkt; if (!ICQ_UNPACK(&buf, "W", &pkt.error)) return -1; if (s->connected == 0) icq_session_connected(s); icq_snac_error_handler(s, "userlist", pkt.error); return 0; } static SNAC_SUBHANDLER(icq_snac_userlist_reply) { /* SNAC(13,03) SRV_SSI_RIGHTS_REPLY Service parameters reply * * Server replies with this SNAC to SNAC(13,02) - client SSI service parameters request. */ #if ICQ_DEBUG_UNUSED_INFORMATIONS struct icq_tlv_list *tlvs; icq_tlv_t *t; debug_function("icq_snac_userlist_reply()\n"); tlvs = icq_unpack_tlvs(&buf, &len, 0); if ((t = icq_tlv_get(tlvs, 0x06)) && (t->len == 2)) debug_white("icq_snac_userlist_reply() length limit for server-list item's name = %d\n", t->nr); if ((t = icq_tlv_get(tlvs, 0x0C)) && (t->len == 2)) debug_white("icq_snac_userlist_reply() max number of contacts in a group = %d\n", t->nr); if ((t = icq_tlv_get(tlvs, 0x04))) { guint16 a, b, c, d, e; if (icq_unpack_nc(t->buf, t->len, "WWWW 20 W", &a, &b, &c, &d, &e)) debug_white("icq_snac_userlist_reply() Max %d contacts, %d groups; %d visible contacts, %d invisible contacts, %d ignore items.\n", a, b, c, d, e); } icq_tlvs_destroy(&tlvs); #endif return 0; } static void set_userinfo_from_tlv(userlist_t *u, const char *var, icq_tlv_t *t) { char *tmp; if (!u) return; tmp = (t && t->len) ? ekg_utf8_to_core(xstrndup((char *) t->buf, t->len)) : NULL; user_private_item_set(u, var, tmp); } static int icq_userlist_parse_entry(session_t *s, struct icq_tlv_list *tlvs, const char *name, guint16 type, guint16 item_id, guint16 group_id) { switch (type) { case 0x0000: /* Buddy record (name: uin for ICQ and screenname for AIM) */ { icq_tlv_t *t_nick = icq_tlv_get(tlvs, 0x131); icq_tlv_t *t_auth = icq_tlv_get(tlvs, 0x66); char *nick, *tmp; char *uid = icq_uid(name); userlist_t *u; tmp = (t_nick && t_nick->len) ? xstrndup((char *) t_nick->buf, t_nick->len) : xstrdup(uid); nick = ekg_utf8_to_core(tmp); if (!(u = userlist_find(s, uid)) && !(u = userlist_add(s, uid, nick)) ) { debug_error("icq_userlist_parse_entry() userlist_add(%s, %s) failed!\n", uid, nick); goto cleanup_user; } if (!u->nickname) u->nickname = xstrdup(nick); set_userinfo_from_tlv(u, "email", icq_tlv_get(tlvs, 0x0137)); set_userinfo_from_tlv(u, "phone", icq_tlv_get(tlvs, 0x0138)); // phone number set_userinfo_from_tlv(u, "cellphone", icq_tlv_get(tlvs, 0x0139)); // cellphone number set_userinfo_from_tlv(u, "mobile", icq_tlv_get(tlvs, 0x013A)); // SMS number set_userinfo_from_tlv(u, "comment", icq_tlv_get(tlvs, 0x013C)); if (group_id) { user_private_item_set_int(u, "iid", item_id); user_private_item_set_int(u, "gid", group_id); } if (t_auth) { user_private_item_set_int(u, "auth", 1); u->status = EKG_STATUS_UNKNOWN; } else user_private_item_set_int(u, "auth", 0); cleanup_user: xfree(nick); xfree(uid); break; } case 0x0001: /* Group record */ { if (item_id == 0) { if ((group_id == 0)) { /* list of groups. wTlvType=1, data is TLV(C8) containing list of WORDs which */ /* is the group ids */ /* we don't need to use this. Our processing is on-the-fly */ /* this record is always sent first in the first packet only, */ } else { /* wGroupId != 0: a group record */ /* no item ID: this is a group */ /* XXX ?wo? more groups ??? */ icq_private_t *j; if (s && (j = s->priv) && (!j->default_group_id)) { j->default_group_id = group_id; j->default_group_name = xstrdup(name); } } } else { debug_error("icq_userlist_parse_entry() Unhandled ROSTER_TYPE_GROUP wItemID != 0\n"); } break; } case 0x0002: /* Permit record ("Allow" list in AIM, and "Visible" list in ICQ) */ case 0x0003: /* Deny record ("Block" list in AIM, and "Invisible" list in ICQ) */ { char *uid = icq_uid(name); userlist_t *u; if (!(u = userlist_find(s, uid))) u = userlist_add(s, uid, NULL); xfree(uid); if (!u) break; if (type == 2) { user_private_item_set_int(u, "visible", item_id); user_private_item_set_int(u, "invisible", 0); ekg_group_add(u, "__online"); ekg_group_remove(u, "__offline"); } else { user_private_item_set_int(u, "visible", 0); user_private_item_set_int(u, "invisible", item_id); ekg_group_add(u, "__offline"); ekg_group_remove(u, "__online"); } break; } case 0x0004: /* Permit/deny settings or/and bitmask of the AIM classes */ { /* XXX */ break; } case 0x0005: /* Presence info (if others can see your idle status, etc) */ { /* XXX */ break; } case 0x0009: /* ICQ2k shortcut bar items */ { if (group_id == 0) { /* data is TLV(CD) text */ } break; } case 0x000e: /* Ignore list record */ { char *uid = icq_uid(name); userlist_t *u; if (!(u = userlist_find(s, uid))) u = userlist_add(s, uid, NULL); if (u) { user_private_item_set_int(u, "block", item_id); ekg_group_add(u, "__blocked"); } xfree(uid); break; } case 0x000F: /* Last update date (name: "LastUpdateDate") */ case 0x0010: /* Non-ICQ contact (to send SMS). Name: 1#EXT, 2#EXT, etc */ { /* XXX */ break; } case 0x0013: /* Item that contain roster import time (name: "Import time") */ { if (group_id == 0) { /* time our list was first imported */ /* pszRecordName is "Import Time" */ /* data is TLV(13) {TLV(D4) {time_t importTime}} */ /* XXX */ } break; } case 0x0014: /* Own icon (avatar) info. Name is an avatar id number as text */ { if (group_id == 0) { /* our avatar MD5-hash */ /* pszRecordName is "1" */ /* data is TLV(D5) hash */ /* we ignore this, just save the id */ /* cause we get the hash again after login */ /* XXX */ } break; } case 0x0019: /* deleted */ { #ifdef ICQ_DEBUG_UNUSED_INFORMATIONS icq_tlv_t *t; if ((t = icq_tlv_get(tlvs, 0x6d)) && (t->len == 8)) { int t1,t2; if (icq_unpack_nc(t->buf, t->len, "II", &t1, &t2)) { debug_white("'%s' was deleted %s\n", name, timestamp_time("%Y-%m-%d %H:%M:%S", t1)); } } #endif break; } case 0x001d: case 0x0020: case 0x0028: { /* XXX unknown, but friendly types */ break; } default: debug_error("icq_userlist_parse_entry() unknown type: 0x%.4x\n", type); } return 0; } static SNAC_SUBHANDLER(icq_snac_userlist_roster) { /* * Handle SNAC(0x13, 0x6) -- Server contact list reply * * This is the server reply to client roster requests: SNAC(13,04), SNAC(13,05). * * Server can split up the roster in several parts. This is indicated with * SNAC flags bit 1 as usual, however the "SSI list last change time" only * exists in the last packet. And the "Number of items" field indicates the * number of items in the current packet, not the entire list. * */ struct { guint8 unk1; /* empty */ /* possible version */ guint16 count; /* COUNT */ unsigned char *data; } pkt; int i; /* XXX, here we have should check if we send roster request with that ref.... if not, "Unrequested roster packet received.\n" return; */ if (!ICQ_UNPACK(&pkt.data, "CW", &pkt.unk1, &pkt.count)) return -1; debug_function("icq_snac_userlist_roster() contacts count: %d\n", pkt.count); buf = pkt.data; for (i = 0; i < pkt.count; i++) { char *orgname; guint16 item_id, item_type; guint16 group_id; guint16 tlv_len; struct icq_tlv_list *tlvs; char *name; if (!ICQ_UNPACK(&buf, "UWWWW", &orgname, &group_id, &item_id, &item_type, &tlv_len)) return -1; if (len < tlv_len) { debug_error("smth bad!\n"); return -1; } name = ekg_utf8_to_core_dup(orgname); debug_white("%sName:'%s'\tgroup:%u item:%u type:0x%x tlvLEN:%u\n", item_type==1?"Group ":"", name, group_id, item_id, item_type, tlv_len); tlvs = icq_unpack_tlvs_nc(buf, tlv_len, 0); icq_userlist_parse_entry(s, tlvs, name, item_type, item_id, group_id); icq_tlvs_destroy(&tlvs); xfree(name); buf += tlv_len; len -= tlv_len; } if (len >= 4) { guint32 last_update; if (!ICQ_UNPACK(&buf, "I", &last_update)) return -1; debug("icq_snac_userlist_roster() Last update of server list was (%u) %s\n", last_update, timestamp_time("%d/%m/%y %H:%M:%S", last_update)); /* sendRosterAck() */ icq_send_snac(s, 0x13, 0x07, 0, 0, ""); icq_session_connected(s); } else { debug("icq_snac_userlist_roster() Waiting for more packets..."); } if (len>0) debug_error("icq_snac_userlist_roster() left: %u bytes\n", len); return 0; } static char *icq_snac_userlist_edit_ack_msg(int error) { char *msg; switch (error) { case 0x0000: msg = "OK!"; break; case 0x0002: msg = "Item you want to modify not found in list"; break; case 0x0003: msg = "Item you want to add allready exists"; break; case 0x000A: msg = "Error adding item (invalid id, allready in list, invalid data)"; break; case 0x000C: msg = "Can't add item. Limit for this type of items exceeded"; break; case 0x000D: msg = "Trying to add ICQ contact to an AIM list"; break; case 0x000E: msg = "Can't add this contact because it requires authorization"; break; default: msg = "Unknown error"; } return msg; } static SNAC_SUBHANDLER(icq_snac_userlist_edit_ack) { /* * SNAC(13,0E) SRV_SSIxMODxACK -- SSI edit server ack * * This SNAC is an ack sent by the server when adding a buddy, * deleting a buddy, or otherwise modifying a group. */ guint16 err; debug_function("icq_snac_userlist_edit_ack()\n"); while (len>=2) { if (!ICQ_UNPACK(&buf, "W", &err)) break; if (!err) debug_white("icq_snac_userlist_edit_ack() err:0 // OK!\n"); else debug_error("icq_snac_userlist_edit_ack() Error code:0x%x // %s\n", err, icq_snac_userlist_edit_ack_msg(err)); } return 0; } static SNAC_SUBHANDLER(icq_snac_userlist_up_to_date) { /* * SNAC(13,0F) SRV_SSI_UPxTOxDATE -- client local SSI is up-to-date * * Server send this snac as reply for SNAC(13,05) when server-stored * information has the same modification date and items number. */ /* XXX "I" - modification date/time of client local SSI copy, "W" - number of items in client local SSI copy */ debug_function("icq_snac_userlist_up_to_date()\n"); return 0; } static SNAC_SUBHANDLER(icq_snac_userlist_modifystart) { /* * SNAC(13,11) CLI_SSI_EDIT_BEGIN -- Contacts edit start (begin transaction) * * Use this before server side information (SSI) modification. Also * you should send SNAC(13,12) after SSI modification. You could also * use "import" transaction mode to add contacts requiring * authorization. Just add 0x00010000 to snac data to start import * transaction. */ /* XXX empty (or 0x00010000 for import transaction) */ debug_white("icq_snac_userlist_modifystart() Server is modifying contact list\n"); return 0; } static SNAC_SUBHANDLER(icq_snac_userlist_modifyentry) { /* * SNAC(13,09) CLI_SSIxUPDATE * * This can be used to modify either the name or additional data for * any items that are already in your server-stored information. It * is most commonly used after adding or removing a buddy: you should * either add or remove the buddy ID# from the type 0x00c9 TLV in the * additional data of the parent group, and then send this SNAC * containing the modified data. Server should reply via SNAC(13,0E). */ debug_function("icq_snac_userlist_modifyentry() Server updated our contact on list\n"); return 0; } static SNAC_SUBHANDLER(icq_snac_userlist_removeentry) { /* * SNAC(13,0A) CLI_SSIxDELETE -- SSI edit: remove item * * Client use this to delete items from server-side info. Server * should reply via SNAC(13,0E). */ debug_function("icq_snac_userlist_removeentry() Server updated our contact on list\n"); #if ICQ_DEBUG_UNUSED_INFORMATIONS { struct { char *uid; guint16 group, item; guint16 type; } pkt; if (ICQ_UNPACK(&buf, "UWWW", &pkt.uid, &pkt.group, &pkt.item, &pkt.type)) { debug("icq_snac_userlist_removeentry() Details: delete '%s' (item id:%d, type:0x%x) from group %d\n", pkt.uid, pkt.item, pkt.type, pkt.group); } } #endif return 0; } static SNAC_SUBHANDLER(icq_snac_userlist_modifyend) { /* * SNAC(13,12) CLI_SSI_EDIT_END -- Contacts edit end (finish transaction) * * This snac used after SSI modification to commit transaction started by SNAC(13,11). * See also snac list for SSI service here. */ debug_white("icq_snac_userlist_modifyend() End of server modification\n"); return 0; } static SNAC_SUBHANDLER(icq_snac_userlist_future_auth) { /* * SNAC(13,15) SRV_SSI_FUTURExAUTHxGRANTED -- Future authorization granted * * You'll receive this when somebody grants future authorization to * you. You can use SNAC(13,14) to send such authorization grant. */ struct { char *uid; char *reason; } pkt; if (!ICQ_UNPACK(&buf, "u", &pkt.uid)) return -1; if (!ICQ_UNPACK(&buf, "U", &pkt.reason)) pkt.reason = ""; /* XXX, pkt.reason recode */ debug_function("icq_snac_userlist_future_auth() %s reason: %s\n", pkt.uid, pkt.reason); return 0; } static SNAC_SUBHANDLER(icq_snac_userlist_auth_req) { /* * SNAC(13,19) SRV_SSI_AUTHxREQUEST -- Authorization request * * This is the authorization request from another client because it * wants to add you to its contact list. You may just ignore this * request or you could reply via SNAC(13,1A) - authorization reply. */ struct { char *uid; char *reason; } pkt; char *uid; if (!ICQ_UNPACK(&buf, "u", &pkt.uid)) return -1; if (!ICQ_UNPACK(&buf, "U", &pkt.reason)) pkt.reason = ""; /* XXX, pkt.reason recode */ uid = icq_uid(pkt.uid); debug_function("icq_snac_userlist_auth_req() %s reason: %s\n", uid, pkt.reason); print("icq_auth_subscribe", session_name(s), uid, pkt.reason); xfree(uid); return 0; } static SNAC_SUBHANDLER(icq_snac_userlist_auth_reply) { /* * SNAC(13,1B) SRV_SSI_AUTHxREPLY -- Authorization reply * * This snac received when someone grant or declines you * authorization, sent via SNAC(13,18). It contain screen name (uin) * of the user, result flag and reason string. */ struct { char *uid; guint8 flag; char *reason; } pkt; char *uid; if (!ICQ_UNPACK(&buf, "u", &pkt.uid)) return -1; uid = icq_uid(pkt.uid); if (ICQ_UNPACK(&buf, "c", &pkt.flag)) { if (!ICQ_UNPACK(&buf, "U", &pkt.reason)) pkt.reason = ""; /* XXX, pkt.reason recode */ /* XXX, spammer */ switch (pkt.flag) { case 0: case 1: { userlist_t *u = userlist_find(s, uid); if (u && pkt.flag) user_private_item_set_int(u, "auth", 0); print_info(uid, s, pkt.flag ? "icq_auth_grant" : "icq_auth_decline", session_name(s), format_user(s, uid), pkt.reason); break; } default: debug_error("icq_snac_userlist_auth_reply() unknown response: %u from %s. (Reason: %s)\n", pkt.flag, uid, pkt.reason); break; } } else debug_error("icq_snac_userlist_auth_reply() (%s) Corrupt answer from %s\n", session_name(s), uid); xfree(uid); return 0; } static SNAC_SUBHANDLER(icq_snac_userlist_you_were_added) { /* * SNAC(13,1C) SRV_SSI_YOUxWERExADDED -- "You were added" message * * Server send this snac to clients, that announced the use of family * 0x13 in SNAC(01,17). This is the "you-were-added" message meaning * that somebody (snac contain his/her screenname) added you to his/her roster. */ struct { char *uid; } pkt; char *uid; userlist_t *u; if (!ICQ_UNPACK(&buf, "u", &pkt.uid)) return -1; uid = icq_uid(pkt.uid); print_info(uid, s, "icq_you_were_added", session_name(s), format_user(s, uid)); if ( config_auto_user_add && !(u = userlist_find(s, uid)) ) u = userlist_add(s, uid, uid); /* XXX "/addssi uid" */ xfree(uid); return 0; } SNAC_HANDLER(icq_snac_userlist_handler) { snac_subhandler_t handler; switch (cmd) { case 0x01: handler = icq_snac_userlist_error; break; case 0x03: handler = icq_snac_userlist_reply; break; /* Miranda: OK */ case 0x06: handler = icq_snac_userlist_roster; break; /* Miranda: 1/3 OK */ /* XXX, handleServerCList() */ case 0x09: handler = icq_snac_userlist_modifyentry; break; case 0x0a: handler = icq_snac_userlist_removeentry; break; case 0x0E: handler = icq_snac_userlist_edit_ack; break; case 0x0F: handler = icq_snac_userlist_up_to_date; break; case 0x11: handler = icq_snac_userlist_modifystart; break; /* Miranda: OK */ case 0x12: handler = icq_snac_userlist_modifyend; break; /* Miranda: OK */ case 0x15: handler = icq_snac_userlist_future_auth; break; case 0x19: handler = icq_snac_userlist_auth_req; break; case 0x1B: handler = icq_snac_userlist_auth_reply; break; case 0x1C: handler = icq_snac_userlist_you_were_added; break; default: handler = NULL; break; } if (!handler) { debug_error("icq_snac_userlist_handler() SNAC with unknown cmd: %.4x received\n", cmd); icq_hexdump(DEBUG_ERROR, buf, len); } else handler(s, buf, len, data); return 0; } SNAC_SUBHANDLER(icq_cmd_addssi_ack) { userlist_t *u; const char *nick = private_item_get(&data, "nick"); const char *cmd = private_item_get(&data, "cmd"); int quiet = private_item_get_int(&data, "quiet"); guint16 error; char *uid; if (!ICQ_UNPACK(&buf, "W", &error)) return -1; uid = icq_uid(private_item_get(&data, "uid")); if (error) { /* XXX ?wo? format for it */ char *tmp = saprintf("Can't add %s/%s", nick, uid); printq("icq_user_info_generic", tmp, icq_snac_userlist_edit_ack_msg(error)); xfree(tmp); xfree(uid); return -1; } if (!xstrcmp(cmd, "del")) { if ( (u = userlist_find(s, uid)) ) { char *tmp = xstrdup(u->nickname); printq("user_deleted", u->nickname, session_name(s)); tabnick_remove(u->uid); tabnick_remove(u->nickname); userlist_remove(s, u); query_emit(NULL, "userlist-removed", &tmp, &uid); query_emit(NULL, "remove-notify", &s->uid, &uid); xfree(tmp); } } else { // add or modify if (!xstrcmp(cmd, "add")) { if ( (u = userlist_add(s, uid, nick)) ) { printq("user_added", u->nickname, session_name(s)); query_emit(NULL, "userlist-added", &u->uid, &u->nickname, &quiet); query_emit(NULL, "add-notify", &s->uid, &u->uid); } } else { // modify if ( (u = userlist_find(s, uid)) ) { const char *nick = private_item_get(&data, "nick"); if (nick) { query_emit(NULL, "userlist-renamed", &u->nickname, &nick); xfree(u->nickname); u->nickname = xstrdup(nick); userlist_replace(s, u); query_emit(NULL, "userlist-refresh"); } } } if (u) { user_private_item_set_int(u, "iid", private_item_get_int(&data, "iid")); user_private_item_set_int(u, "gid", private_item_get_int(&data, "gid")); const char *p; p = private_item_get(&data, "mobile"); if (p) user_private_item_set(u, "mobile", p); p = private_item_get(&data, "email"); if (p) user_private_item_set(u, "email", p); p = private_item_get(&data, "comment"); if (p) user_private_item_set(u, "comment", p); } } xfree(uid); return 0; } // vim:syn=c ekg2-0.4~pre+20120506.1/plugins/icq/icq_snac_handlers_15extension.c000066400000000000000000000672231175142753400244720ustar00rootroot00000000000000/* * (C) Copyright 2000,2001 Richard Hughes, Roland Rabien, Tristan Van de Vreede * (C) Copyright 2001,2002 Jon Keating, Richard Hughes * (C) Copyright 2002,2003,2004 Martin Öberg, Sam Kothari, Robert Rainwater * (C) Copyright 2004,2005,2006,2007 Joe Kucera * * ekg2 port: * (C) Copyright 2006-2008 Jakub Zawadzki * 2008 Wiesaw Ochmiski * * Protocol description with author's permission from: http://iserverd.khstu.ru/oscar/ * (C) Copyright 2000-2005 Alexander V. Shutko * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include "icq.h" #include "misc.h" #include "icq_caps.h" #include "icq_const.h" #include "icq_flap_handlers.h" #include "icq_snac_handlers.h" SNAC_SUBHANDLER(icq_snac_extension_error) { /* SNAC(15,01) SRV_ICQEXT_ERROR Client/server error * * This is an error notification snac */ struct { guint16 error; } pkt; if (!ICQ_UNPACK(&buf, "W", &pkt.error)) pkt.error = 0; /* XXX TLV.Type(0x08) - error subcode */ /* XXX TLV.Type(0x21) - service specific data from request */ icq_snac_error_handler(s, "extension", pkt.error); return 0; } #define METASNAC_SUBHANDLER(x) static int x(session_t *s, unsigned char *buf, int len, private_data_t **info) typedef int (*metasnac_subhandler_t) (session_t *s, unsigned char *, int, private_data_t **info); #include "icq_fieldnames.inc" typedef struct { int type; int item; // 'S' - str; 'w' - word; 'c' - byte; 'L' bool const char *display; // display name const char *name; // private item name void *ltab; } _userinfo_t; static const _userinfo_t userinfo[] = { /* User basic info reply */ {META_BASIC_USERINFO, 'S', N_("Nickname"), "nick", NULL}, {META_BASIC_USERINFO, 'S', N_("Firstname"), "first_name", NULL}, {META_BASIC_USERINFO, 'S', N_("Lastname"), "last_name", NULL}, {META_BASIC_USERINFO, 'S', N_("Email"), "email", NULL}, {META_BASIC_USERINFO, 'S', N_("City"), "city", NULL}, {META_BASIC_USERINFO, 'S', N_("State"), "state", NULL}, {META_BASIC_USERINFO, 'S', N_("Phone"), "phone", NULL}, {META_BASIC_USERINFO, 'S', N_("Fax"), "fax", NULL}, {META_BASIC_USERINFO, 'S', N_("Street"), "street", NULL}, {META_BASIC_USERINFO, 'S', N_("Cellular"), "mobile", NULL}, {META_BASIC_USERINFO, 'S', N_("Zip"), "zip", NULL}, {META_BASIC_USERINFO, 'w', N_("Country"), "country", countryField}, {META_BASIC_USERINFO, 'c', N_("Timezone"), "tzone", NULL}, {META_BASIC_USERINFO, 'L', N_("Authorization"), "auth", NULL}, {META_BASIC_USERINFO, 'c', N_("Webaware"), "webaware", webawareField}, {META_BASIC_USERINFO, 'L', N_("Publish primary email"),"pub_email", NULL}, {META_BASIC_USERINFO, 'L', N_("Direct connection"), "dc_perm", NULL}, {META_BASIC_USERINFO, 'S', NULL, NULL, NULL}, // Is here 'zip code' again? /* User work info reply */ {META_WORK_USERINFO, 'S', N_("CompanyCity"), "c_city", NULL}, {META_WORK_USERINFO, 'S', N_("CompanyState"), "c_state", NULL}, {META_WORK_USERINFO, 'S', N_("CompanyPhone"), "c_phone", NULL}, {META_WORK_USERINFO, 'S', N_("CompanyFax"), "c_fax", NULL}, {META_WORK_USERINFO, 'S', N_("CompanyStreet"), "c_street", NULL}, {META_WORK_USERINFO, 'S', N_("CompanyZIP"), "c_zip", NULL}, {META_WORK_USERINFO, 'w', N_("CompanyCountry"), "c_country", countryField}, {META_WORK_USERINFO, 'S', N_("Company"), "c_name", NULL}, {META_WORK_USERINFO, 'S', N_("CompanyDepartment"), "c_depart", NULL}, {META_WORK_USERINFO, 'S', N_("CompanyPosition"), "c_pos", NULL}, {META_WORK_USERINFO, 'w', N_("CompanyOccupation"), "c_occup", workField}, {META_WORK_USERINFO, 'S', N_("CompanyHomepage"), "c_www", NULL}, {META_WORK_USERINFO, 'S', NULL, NULL, NULL}, // Is here 'zip code' again? /* User more info reply */ {META_MORE_USERINFO, 'w', N_("Age"), "age", NULL}, {META_MORE_USERINFO, 'c', N_("Gender"), "gender", genderField}, {META_MORE_USERINFO, 'S', N_("Homepage"), "www", NULL}, {META_MORE_USERINFO, 'w', N_("Birth date"), "birth", NULL}, {META_MORE_USERINFO, 'c', NULL, ".month", NULL}, {META_MORE_USERINFO, 'c', NULL, ".day", NULL}, {META_MORE_USERINFO, 'c', N_("Language1"), "lang1", languageField}, {META_MORE_USERINFO, 'c', N_("Language2"), "lang2", languageField}, {META_MORE_USERINFO, 'c', N_("Language3"), "lang3", languageField}, {META_MORE_USERINFO, 'w', NULL, NULL, NULL}, // skip 2 unknown bytes {META_MORE_USERINFO, 'S', N_("Original from City"), "o_city", NULL}, {META_MORE_USERINFO, 'S', N_("Original from State"), "o_state", NULL}, {META_MORE_USERINFO, 'w', N_("Original from Country"),"o_country", countryField}, {META_MORE_USERINFO, 'c', N_("Marital status"), "marital", maritalField}, {META_MORE_USERINFO, 'L', N_("AllowSpam"), "AllowSpam", NULL}, {META_MORE_USERINFO, 'w', N_("InfoCP"), "InfoCP", NULL}, {META_MORE_USERINFO, 'c', NULL, NULL, NULL}, // skip 1 unknown byte {META_MORE_USERINFO, 'S', NULL, NULL, NULL}, // skip unknown string {META_MORE_USERINFO, 'w', NULL, NULL, NULL}, // skip 2 unknown bytes {META_MORE_USERINFO, 'w', NULL, NULL, NULL}, // skip 2 unknown bytes {META_MORE_USERINFO, 'w', NULL, NULL, NULL}, // skip 2 unknown bytes {META_MORE_USERINFO, 'w', NULL, NULL, NULL}, // skip 2 unknown bytes {META_MORE_USERINFO, 'S', NULL, NULL, NULL}, // skip unknown string {META_MORE_USERINFO, 'w', NULL, NULL, NULL}, // skip 2 unknown bytes {META_MORE_USERINFO, 'w', NULL, NULL, NULL}, // skip 2 unknown bytes {META_MORE_USERINFO, 'c', NULL, NULL, NULL}, // skip 1 unknown byte {META_MORE_USERINFO, 'S', NULL, NULL, NULL}, // skip unknown string /* User notes (about) info reply */ {META_NOTES_USERINFO, 'S', N_("About"), "about", NULL}, /* User extended email info reply */ {META_EMAIL_USERINFO, 'S', N_("Email"), "email1", NULL}, {META_EMAIL_USERINFO, 'S', N_("Email"), "email2", NULL}, {META_EMAIL_USERINFO, 'S', N_("Email"), "email3", NULL}, {META_EMAIL_USERINFO, 'S', N_("Email"), "email4", NULL}, // more emails? ?wo? /* User interests info reply */ {META_INTERESTS_USERINFO, 'w', N_("Interests"), "interests1", interestsField}, {META_INTERESTS_USERINFO, 'S', N_("InterestsStr"), "interestsStr1", NULL}, {META_INTERESTS_USERINFO, 'w', N_("Interests"), "interests2", interestsField}, {META_INTERESTS_USERINFO, 'S', N_("InterestsStr"), "interestsStr2", NULL}, {META_INTERESTS_USERINFO, 'w', N_("Interests"), "interests3", interestsField}, {META_INTERESTS_USERINFO, 'S', N_("InterestsStr"), "interestsStr3", NULL}, {META_INTERESTS_USERINFO, 'w', N_("Interests"), "interests4", interestsField}, {META_INTERESTS_USERINFO, 'S', N_("InterestsStr"), "interestsStr4", NULL}, /* User past/affilations info reply */ {META_AFFILATIONS_USERINFO, 'w', N_("PastAff"), "pastaff1", pastField}, {META_AFFILATIONS_USERINFO, 'S', N_("PastAffStr"), "pastaffStr1", NULL}, {META_AFFILATIONS_USERINFO, 'w', N_("PastAff"), "pastaff2", pastField}, {META_AFFILATIONS_USERINFO, 'S', N_("PastAffStr"), "pastaffStr2", NULL}, {META_AFFILATIONS_USERINFO, 'w', N_("PastAff"), "pastaff3", pastField}, {META_AFFILATIONS_USERINFO, 'S', N_("PastAffStr"), "pastaffStr3", NULL}, {META_AFFILATIONS_USERINFO, 'w', N_("Aff"), "aff1", pastField}, {META_AFFILATIONS_USERINFO, 'S', N_("AffStr"), "affStr1", NULL}, {META_AFFILATIONS_USERINFO, 'w', N_("Aff"), "aff2", pastField}, {META_AFFILATIONS_USERINFO, 'S', N_("AffStr"), "affStr2", NULL}, {META_AFFILATIONS_USERINFO, 'w', N_("Aff"), "aff3", pastField}, {META_AFFILATIONS_USERINFO, 'S', N_("AffStr"), "affStr3", NULL}, /* Short user information reply */ {META_SHORT_USERINFO, 'S', N_("Nickname"), "nick", NULL}, {META_SHORT_USERINFO, 'S', N_("Firstname"), "first_name", NULL}, {META_SHORT_USERINFO, 'S', N_("Lastname"), "last_name", NULL}, {META_SHORT_USERINFO, 'S', N_("Email"), "email", NULL}, /* User homepage category information reply */ {META_HPAGECAT_USERINFO, 'w', N_("Homepage category"), "wwwcat", NULL}, // ?WO? lookup??? {META_HPAGECAT_USERINFO, 'S', N_("Homepage keywords"), "wwwkeys", NULL}, {0, 0, NULL, NULL, NULL} }; struct fieldnames_t meta_name[]={ {META_BASIC_USERINFO, "basic"}, {META_WORK_USERINFO, "work"}, {META_MORE_USERINFO, "more"}, {META_NOTES_USERINFO, "notes"}, {META_EMAIL_USERINFO, "email"}, {META_INTERESTS_USERINFO, "interests"}, {META_AFFILATIONS_USERINFO, "affilations"}, {META_SHORT_USERINFO, "short"}, {META_HPAGECAT_USERINFO, "hpagecat"}, {META_SET_FULLINFO_ACK, "fullinfo_ack"}, {SRV_USER_FOUND, "userfound"}, {SRV_LAST_USER_FOUND, "userfound_last"}, {SRV_RANDOM_FOUND, ""}, {-1, NULL}}; static int __get_userinfo_data(unsigned char *buf, int len, int type, private_data_t **info) { int i, ret = 0; for (i=0; userinfo[i].type; i++) { if (userinfo[i].type != type) continue; switch (userinfo[i].item) { case 'S': { char *str; if (!ICQ_UNPACK(&buf, "S", &str)) ret = 1; else private_item_set(info, userinfo[i].name, str); break; } case 'w': { guint16 w = 0; if (!ICQ_UNPACK(&buf, "w", &w)) ret = 1; else private_item_set_int(info, userinfo[i].name, w); break; } case 'b': case 'c': case 'L': { guint8 b = 0; if (!ICQ_UNPACK(&buf, "c", &b)) ret = 1; else private_item_set_int(info, userinfo[i].name, b); break; } default: debug_error("__get_userinfo_data() unknown item type %d\n", userinfo[i].item); ret = 1; break; } if (ret) private_item_set(info, userinfo[i].name, ""); } if (len) debug_error("__get_userinfo_data() more data follow: %u\n", len); if (ret) debug_error("__get_userinfo_data() type:0x%x error: %u\n", type, len); return ret; } static int __displayed = 0; /* Luckily we're not multithreaded */ static void __display_info(session_t *s, int type, private_data_t *data) { int i, uid = private_item_get_int(&data, "uid"); const char *str; char *theme = saprintf("icq_userinfo_%s", icq_lookuptable(meta_name, type)); for (i=0; userinfo[i].type; i++) { if ( (userinfo[i].type != type) || (!userinfo[i].name) ) continue; if (userinfo[i].ltab) str = icq_lookuptable(userinfo[i].ltab, private_item_get_int(&data, userinfo[i].name)); else if (userinfo[i].item == 'L') str = private_item_get_int(&data, userinfo[i].name) ? _("Yes") : _("No"); else str = private_item_get(&data, userinfo[i].name); if ( str && *str) { char *___str = xstrdup(str); /* XXX, guess recode */ if (!__displayed) print("icq_userinfo_start", session_name(s), ekg_itoa(uid), theme); print(theme, session_name(s), ekg_itoa(uid), userinfo[i].display, ___str); __displayed = 1; xfree(___str); } } xfree(theme); } /* * Userinfo handlers * */ METASNAC_SUBHANDLER(icq_snac_extensions_interests) { guint8 count; int i; if (!ICQ_UNPACK(&buf, "C", &count)) return -1; /* 4 is the maximum allowed personal interests, if count is higher it's likely a parsing error */ if (count > 4) count = 4; for (i = 0; i < count; i++) { char *tmp; const char *str; guint16 w; if (ICQ_UNPACK(&buf, "wS", &w, &str)) { tmp = saprintf("interests%d", i+1); private_item_set_int(info, tmp, w); xfree(tmp); tmp = saprintf("interestsStr%d", i+1); private_item_set(info, tmp, str); xfree(tmp); } } return 0; } METASNAC_SUBHANDLER(icq_snac_extensions_affilations) { static const char *names[] = {"pastaff", "aff"}; guint8 count; int i, k; for (k=0; k<2; k++) { if (!ICQ_UNPACK(&buf, "C", &count)) return -1; /* 3 is the maximum allowed backgrounds, if count is higher it's likely a parsing error */ if (count > 3) count = 3; for (i = 0; i < count; i++) { char *name1, *name2; const char *str; guint16 w; name1 = saprintf("%s%d", names[k], i+1); name2 = saprintf("%sStr%d", names[k], i+1); if (!ICQ_UNPACK(&buf, "wS", &w, &str)) { w = 0; str = ""; } private_item_set_int(info, name1, w); private_item_set(info, name2, str); xfree(name1); xfree(name2); } } return 0; } METASNAC_SUBHANDLER(icq_snac_extensions_basicinfo) { __get_userinfo_data(buf, len, META_BASIC_USERINFO, info); { /* correct results */ char *tmp; int tz = private_item_get_int(info, "tzone"); if (tz & 0x80) tz -= 256; tmp = saprintf("GMT%+d", tz/2); private_item_set(info, "tzone", tmp); xfree(tmp); private_item_set_int(info, "auth", !private_item_get_int(info, "auth")); private_item_set_int(info, "webaware", private_item_get_int(info, "webaware") + 1); } { userlist_t *u; char *uid = icq_uid(private_item_get(info, "uid")); if ( (u = userlist_find(s, uid)) ) { user_private_item_set(u, "first_name", private_item_get(info, "first_name")); user_private_item_set(u, "last_name", private_item_get(info, "last_name")); } xfree(uid); } return 0; } METASNAC_SUBHANDLER(icq_snac_extensions_notes) { __get_userinfo_data(buf, len, META_NOTES_USERINFO, info); return 0; } METASNAC_SUBHANDLER(icq_snac_extensions_workinfo) { __get_userinfo_data(buf, len, META_WORK_USERINFO, info); return 0; } METASNAC_SUBHANDLER(icq_snac_extensions_shortinfo) { __get_userinfo_data(buf, len, META_SHORT_USERINFO, info); return 0; } METASNAC_SUBHANDLER(icq_snac_extensions_email) { guint8 count_discard; int i; /* This value used to be a e-mail counter. Either that was wrong or * Mirabilis changed the behaviour again. It usually says NULL now so * I use the packet byte count to extract the e-mails instead. */ if (!ICQ_UNPACK(&buf, "C", &count_discard)) return -1; for (i = 0; (len > 4); i++) { char *tmp; const char *str; guint8 publish_flag; /* Don't publish flag */ if (!ICQ_UNPACK(&buf, "C", &publish_flag)) return -1; if (!ICQ_UNPACK(&buf, "S", &str)) { tmp = saprintf("email%d", i+1); private_item_set(info, tmp, str); xfree(tmp); } } return 0; } METASNAC_SUBHANDLER(icq_snac_extensions_moreinfo) { __get_userinfo_data(buf, len, META_MORE_USERINFO, info); { int year = private_item_get_int(info, "birth"); int month = private_item_get_int(info, ".month"); int day = private_item_get_int(info, ".day"); if (year && month && day) { char *bdate = saprintf("%04d-%02d-%02d", year, month, day); private_item_set(info, "birth", bdate); xfree(bdate); } else private_item_set(info, "birth", ""); private_item_set(info, ".month", ""); private_item_set(info, ".day", ""); if (!private_item_get_int(info, "age")) private_item_set(info, "age", ""); } return 0; } METASNAC_SUBHANDLER(icq_snac_extensions_hpagecat) { struct { guint8 enabled; guint16 cat; char *str; } pkt; private_item_set(info, "wwwcat", NULL); private_item_set(info, "wwwkeys", NULL); if (!ICQ_UNPACK(&buf, "c", &pkt.enabled)) return -1; if (!pkt.enabled) return 0; if (!ICQ_UNPACK(&buf, "wS", &pkt.cat, &pkt.str)) return -1; private_item_set_int(info, "wwwcat", pkt.cat); private_item_set(info, "wwwkeys", pkt.str); return 0; } /* * search handlers * */ static int icq_snac_extension_userfound_common(session_t *s, unsigned char *buf, int len, int islast) { char *nickname = NULL; char *first_name = NULL; char *last_name = NULL; char *email = NULL; char *full_name; char *temp; const char *__age = NULL; const char *__gender = ""; char *__active; guint32 uin; guint16 len2; guint16 status, age; guint8 auth, gender; /* XXX, sprawdzic czy mamy cookie. */ if (!ICQ_UNPACK(&buf, "w", &len2)) return -1; if (len < len2) return -1; if (!ICQ_UNPACK(&buf, "i", &uin)) return -1; if (!ICQ_UNPACK(&buf, "S", &temp)) goto cleanup; nickname = xstrdup(temp); if (!ICQ_UNPACK(&buf, "S", &temp)) goto cleanup; first_name = xstrdup(temp); if (!ICQ_UNPACK(&buf, "S", &temp)) goto cleanup; last_name = xstrdup(temp); if (!ICQ_UNPACK(&buf, "S", &temp)) goto cleanup; email = xstrdup(temp); if (first_name[0] && last_name[0]) full_name = saprintf("%s %s", first_name, last_name); else full_name = xstrdup(first_name[0] ? first_name : last_name); if (ICQ_UNPACK(&buf, "cwcw", &auth, &status, &gender, &age)) { if (age) __age = ekg_itoa(age); // XXX calculate birthyear? if (gender) __gender = (gender==2) ? "m" : "f"; } else { debug_error("icq_snac_extension_userfound_common() broken\n"); auth = status = gender = age = 0; } /* XXX, "search_results_multi", "search_results_single" */ /* XXX, instead of email we had city */ /* XXX, some time ago i was thinking to of function which * if data was truncated [because of width in format] * it'd take another line to complete.. * * i don't like truncation of data for instance: * 08:17:12 97320776 | darkjames | Jakub Zawadz | - | darkjames@po * * i was thinking about: * 97320776 | darkjames | Jakub Zawwdz | - | darkjames@po * ki czta.onet.pl * * of course we can do more magic, and wrap... * Jakub * Zawadzki * * or maybe let's align to center? :) * Jakub * Zawadzki */ { const char *fvalue; /* XXX ?wo? new formats for icq status * status (0 - offline, 1 - online, 2 - non_webaware) */ switch (status) { case 0: fvalue = format_find("search_results_multi_notavail"); break; case 1: fvalue = format_find("search_results_multi_avail"); break; default: fvalue = format_find("search_results_multi_unknown"); break; } temp = format_string(fvalue); /* XXX ?wo? add format for "auth" */ __active = saprintf("%s %s", temp, auth ? " " : "A"); xfree(temp); } print_info(NULL, s, "search_results_multi", ekg_itoa(uin), full_name, nickname, email, __age ? __age : ("-"), __gender, __active); xfree(__active); xfree(full_name); if (islast && len>=4) { guint32 omit; ICQ_UNPACK(&buf, "I", &omit); debug_warn("icq_snac_extension_userfound_last() Bulshit warning!\n"); debug_white("icq_snac_extension_userfound_last() %d search results omitted\n", omit); } icq_hexdump(DEBUG_WHITE, buf, len); xfree(nickname); xfree(first_name); xfree(last_name); xfree(email); return 0; cleanup: xfree(nickname); xfree(first_name); xfree(last_name); xfree(email); return -1; } METASNAC_SUBHANDLER(icq_snac_extension_userfound) { return icq_snac_extension_userfound_common(s, buf, len, 0); } METASNAC_SUBHANDLER(icq_snac_extension_userfound_last) { return icq_snac_extension_userfound_common(s, buf, len, 1); } METASNAC_SUBHANDLER(icq_snac_extension_fullinfo_ack) { return 0; } static metasnac_subhandler_t get_userinfo_extension_handler(guint16 subtype) { switch (subtype) { /* userinfo */ case META_BASIC_USERINFO: return icq_snac_extensions_basicinfo; case META_INTERESTS_USERINFO: return icq_snac_extensions_interests; case META_NOTES_USERINFO: return icq_snac_extensions_notes; case META_HPAGECAT_USERINFO: return icq_snac_extensions_hpagecat; case META_WORK_USERINFO: return icq_snac_extensions_workinfo; case META_MORE_USERINFO: return icq_snac_extensions_moreinfo; case META_AFFILATIONS_USERINFO: return icq_snac_extensions_affilations; case META_EMAIL_USERINFO: return icq_snac_extensions_email; case META_SHORT_USERINFO: return icq_snac_extensions_shortinfo; } return NULL; } static int icq_meta_info_reply(session_t *s, unsigned char *buf, int len, private_data_t **info, int show) { /* SNAC(15,03)/07DA SRV_META_INFO_REPLY Meta information response * * This is the server response to client meta info request SNAC(15,02)/07D0. */ struct { guint16 subtype; guint8 result; unsigned char *data; } pkt; int userinfo = 0; metasnac_subhandler_t handler; if (!ICQ_UNPACK(&pkt.data, "wc", &pkt.subtype, &pkt.result)) { debug_error("icq_meta_info_reply() broken\n"); return -1; } debug_white("icq_meta_info_reply() subtype=%.4x result=%.2x (len=%d)\n", pkt.subtype, pkt.result, len); if ( (handler = get_userinfo_extension_handler(pkt.subtype)) ) { userinfo = 1; } else { switch (pkt.subtype) { /* search */ case SRV_LAST_USER_FOUND: handler = icq_snac_extension_userfound_last; break; case SRV_USER_FOUND: handler = icq_snac_extension_userfound; break; case META_SET_FULLINFO_ACK: handler = icq_snac_extension_fullinfo_ack; break; case SRV_RANDOM_FOUND: handler = NULL; break; /* XXX, SRV_RANDOM_FOUND */ default: handler = NULL; } } __displayed = 0; if (!handler) { debug_error("icq_meta_info_reply() ignored: %.4x\n", pkt.subtype); icq_hexdump(DEBUG_ERROR, pkt.data, len); return 0; } else { int uid = info ? private_item_get_int(info, "uid") : -1; debug_function("icq_snac_extensions_%s()", icq_lookuptable(meta_name, pkt.subtype)); if (userinfo) debug_function(" uid: %u", uid); debug_function("\n"); if (pkt.result == 0x0A) { handler(s, pkt.data, len, info); } else if (!userinfo){ /* Failed search */ debug_error("icq_snac_extension_userfound() search error: %u\n", pkt.result); } if (show) { __display_info(s, pkt.subtype, *info); if (__displayed) print("icq_userinfo_end", session_name(s), ekg_itoa(uid)); } } return 0; } static int check_replyreq(session_t *s, unsigned char **buf, int *len, int *type) { struct { guint16 type; guint16 len; } tlv; struct { guint16 len; guint32 uid; guint16 type; guint16 id; } pkt; if (!icq_unpack(*buf, buf, len, "WW", &tlv.type, &tlv.len) || (tlv.type != 0x0001) || (tlv.len < 10)) { debug_error("check_replyreq() broken(1)\n"); return 0; } if (*len!=tlv.len) { debug_error("icq_snac_extension_replyreq() broken(1,5)\n"); return 0; } if (!icq_unpack(*buf, buf, len, "wiwW", &pkt.len, &pkt.uid, &pkt.type, &pkt.id)) { debug_error("icq_snac_extension_replyreq() broken(2)\n"); return 0; } debug_white("icq_snac_extension_replyreq() uid=%d type=%.4x (len=%d, len2=%d)\n", pkt.uid, pkt.type, *len, pkt.len); if (xstrcmp(s->uid+4, ekg_itoa(pkt.uid))) { debug_error("icq_snac_extension_replyreq() 1919 UIN mismatch: %s vs %ld.\n", s->uid+4, pkt.uid); return 0; } if (tlv.len - 2 != pkt.len) { debug("icq_snac_extension_replyreq() 1743 Size mismatch in packet lengths.\n"); return 0; } *type = pkt.type; return 1; } static int icq_offline_message(session_t *s, unsigned char *buf, int len, private_data_t **info) { /* * SNAC(15,03)/0041 SRV_OFFLINE_MESSAGE Offline message response * * This is the server response to CLI_OFFLINE_MSGS_REQ SNAC(15,02)/003C. * This snac contain single offline message that was sent by another user * and buffered by server when client was offline. */ struct { guint32 uin; /* message sender uin */ guint16 y; /* year when message was sent (LE) */ guint8 M; /* month when message was sent */ guint8 d; /* day when message was sent */ guint8 h; /* hour (GMT) when message was sent */ guint8 m; /* minute when message was sent */ guint8 type; /* message type */ guint8 flags; /* message flags */ guint16 len; /* message string length (LE) */ char *msg; /* message string (null-terminated) */ } pkt; char *recode = NULL; char *uid; debug_function("icq_offline_message()\n"); if (ICQ_UNPACK(&buf, "i wcccc cc w", &pkt.uin, &pkt.y, &pkt.M, &pkt.d, &pkt.h, &pkt.m, &pkt.type, &pkt.flags, &pkt.len)) { struct tm lt; lt.tm_sec = 0; lt.tm_min = pkt.m; lt.tm_hour = pkt.h; lt.tm_mday = pkt.d; lt.tm_mon = pkt.M - 1; lt.tm_year = pkt.y - 1900; lt.tm_isdst = -1; recode = icq_convert_from_ucs2be((char *) buf, pkt.len - 1); if (!recode) recode = xstrdup((const char*)buf); uid = saprintf("icq:%u", pkt.uin); if (recode && *recode) protocol_message_emit(s, uid, NULL, recode, NULL, mktime(<), EKG_MSGCLASS_CHAT, NULL, EKG_TRY_BEEP, 0); xfree(uid); xfree(recode); } return 0; } static int icq_offline_message_end(session_t *s, unsigned char *buf, int len, private_data_t **info) { /* * SNAC(15,03)/0042 SRV_END_OF_OFFLINE_MSGS End-of-offline messages reply * * This is the last SNAC in server response to CLI_OFFLINE_MSGS_REQ SNAC(15,02)/003C. * It doesn't contain message - it is only end_of_sequence marker. */ debug_function("icq_offline_message_end()\n"); /* SNAC(15,02)/003E CLI_DELETE_OFFLINE_MSGS_REQ Delete offline messages request * * Client sends this SNAC when wants to delete offline messages from * server. But first you should request them from server using * SNAC(15,02)/003C. If you doesn't delete messages server will send them * again after client request. */ GString *pkt = g_string_new(NULL); icq_makemetasnac(s, pkt, CLI_DELETE_OFFLINE_MSGS_REQ, 0, NULL, NULL); icq_send_pkt(s, pkt); return 0; } SNAC_SUBHANDLER(icq_snac_extension_replyreq) { /* SNAC(15,03) SRV_META_REPLY Meta information response * * This is the server response to client meta request SNAC(15,02) */ int type = 0; private_data_t *info = NULL; debug_function("icq_snac_extension_replyreq()\n"); if (!check_replyreq(s, &buf, &len, &type)) return -1; private_item_set_int(&info, "uid", private_item_get_int(&data, "uid")); switch (type) { case SRV_OFFLINE_MESSAGE: /* Offline message response */ icq_offline_message(s, buf, len, &info); break; case SRV_END_OF_OFFLINE_MSGS: /* End-of-offline messages reply */ icq_offline_message_end(s, buf, len, &info); break; case SRV_META_INFO_REPLY: /* Meta information response */ icq_meta_info_reply(s, buf, len, &info, 1); break; default: debug_error("icq_snac_extension_replyreq() METASNAC with unknown code: %x received.\n", type); break; } private_items_destroy(&info); return 0; } SNAC_SUBHANDLER(icq_my_meta_information_response) { int type; icq_private_t *j = s->priv; debug_function("icq_my_meta_information_response()\n"); if (!check_replyreq(s, &buf, &len, &type)) return -1; private_item_set(&j->whoami, "uid", s->uid+4); switch (type) { case 0x7da: icq_meta_info_reply(s, buf, len, &j->whoami, 0); break; default: debug_error("icq_my_meta_information_response() METASNAC with unknown code: %x received.\n", type); break; } return 0; } void display_whoami(session_t *s) { icq_private_t *j = s->priv; int uid = private_item_get_int(&j->whoami, "uid"); int end = 0; __displayed = 0; __display_info(s, META_BASIC_USERINFO, j->whoami); end |= __displayed; __displayed = 0; __display_info(s, META_MORE_USERINFO, j->whoami); end |= __displayed; __displayed = 0; __display_info(s, META_EMAIL_USERINFO, j->whoami); end |= __displayed; __displayed = 0; __display_info(s, META_NOTES_USERINFO, j->whoami); end |= __displayed; __displayed = 0; __display_info(s, META_HPAGECAT_USERINFO, j->whoami); end |= __displayed; __displayed = 0; __display_info(s, META_WORK_USERINFO, j->whoami); end |= __displayed; __displayed = 0; __display_info(s, META_INTERESTS_USERINFO, j->whoami); end |= __displayed; __displayed = 0; __display_info(s, META_AFFILATIONS_USERINFO, j->whoami); if (end) print("icq_userinfo_end", session_name(s), ekg_itoa(uid)); } SNAC_HANDLER(icq_snac_extension_handler) { snac_subhandler_t handler; switch (cmd) { case 0x01: handler = icq_snac_extension_error; break; case 0x03: handler = icq_snac_extension_replyreq; break; default: handler = NULL; break; } if (!handler) { debug_error("icq_snac_extension_handler() SNAC with unknown cmd: %.4x received\n", cmd); icq_hexdump(DEBUG_ERROR, buf, len); return 0; } else handler(s, buf, len, data); return 0; } // vim:syn=c ekg2-0.4~pre+20120506.1/plugins/icq/icq_snac_handlers_17sigon.c000066400000000000000000000130261175142753400235670ustar00rootroot00000000000000/* * (C) Copyright 2000-2001 Richard Hughes, Roland Rabien, Tristan Van de Vreede * (C) Copyright 2001-2002 Jon Keating, Richard Hughes * (C) Copyright 2002-2004 Martin Öberg, Sam Kothari, Robert Rainwater * (C) Copyright 2004-2008 Joe Kucera * * ekg2 port: * (C) Copyright 2006-2008 Jakub Zawadzki * 2008 Wiesław Ochmiński * * Protocol description with author's permission from: http://iserverd.khstu.ru/oscar/ * (C) Copyright 2000-2005 Alexander V. Shutko * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include "icq.h" #include "misc.h" #include "icq_caps.h" #include "icq_const.h" #include "icq_flap_handlers.h" #include "icq_snac_handlers.h" SNAC_SUBHANDLER(icq_snac_sigon_error) { /* SNAC(17,01) SRV_REGISTRATION_ERROR Server error (registration refused) * * Server replies with this SNAC to SNAC(17,04) - client registration * request. This snac mean that registration failed for some reason. */ // XXX we don't support registration yet struct { guint16 error; } pkt; if (!ICQ_UNPACK(&buf, "W", &pkt.error)) pkt.error = 0; icq_snac_error_handler(s, "sigon", pkt.error); return 0; } SNAC_SUBHANDLER(icq_snac_sigon_reply) { /* SNAC(17,03) SRV_LOGIN_REPLY Server login reply / error reply * * This is the server reply for for SNAC(17,02) and SNAC(17,06) client requests. * It may contain error information or BOS address/cookie. * * XXX There are two modifications of this snac. First (error notification) * contain TLV(0x01), TLV(0x08) and TLV(0x04). The second contain * TLV(0x01), TLV(0x05), TLV(0x06), TLV(0x11), latest client info * TLV(0x40)-TLV(0x48) and TLV(0x54). */ debug_function("icq_snac_sigon_reply()\n"); // XXX ?wo? Is icq_flap_close_helper() adequate to handle it? return icq_flap_close_helper(s, buf, len); } SNAC_SUBHANDLER(icq_snac_new_uin) { /* SNAC(17,05) SRV_NEW_UIN New uin response * * This is the server reply for SNAC(17,04). It contain new uin. This snac * mean that registration finished succesfully. Server also can reply with * SNAC(17,01) if it can't create new user account. */ // XXX we don't support registration yet return 0; } extern char *icq_md5_digest(const char *password, const unsigned char *key, int key_len); /* digest.c */ SNAC_SUBHANDLER(icq_snac_sigon_authkey) { /* SNAC(17,07) SRV_AUTH_KEY_RESPONSE Server md5 authkey response * * This is the second snac in md5 crypted login sequence. Server send this for SNAC(17,06) request. * This snac contain server generated auth key. Client should use it to crypt password. */ struct { guint16 key_len; } pkt; GString *str; char *digest; if (!ICQ_UNPACK(&buf, "W", &pkt.key_len)) { icq_handle_disconnect(s, "Secure login failed. Invalid server response.", 0); /* XXX */ return -1; } if (!pkt.key_len || len < pkt.key_len) { icq_handle_disconnect(s, "Secure login failed. Invalid key length.", 0); /* XXX */ return -1; } /* SNAC(17,02) CLI_MD5_LOGIN Client login request (md5 login sequence) * * This is the second snac in md5 crypted login sequence. * You'll need password, authkey from SNAC(17,07) and RFC 1321 md5 routines. * Server should reply via SNAC(17,03) */ /* XXX, miranda limit key to 64B */ digest = icq_md5_digest(session_password_get(s), buf, pkt.key_len); str = g_string_new(NULL); icq_pack_append(str, "T", icq_pack_tlv_str(1, s->uid + 4)); // TLV(0x01) - uin icq_pack_append(str, "T", icq_pack_tlv(0x25, digest, 16)); // TLV(0x25) - password md5 hash icq_pack_append(str, "T", icq_pack_tlv(0x4C, NULL, 0)); // empty TLV(0x4C): unknown XXX ?wo? ssi flag? icq_pack_append_client_identification(str); // Pack client identification details. icq_makesnac(s, str, 0x17, 2, 0, 0); icq_send_pkt(s, str); // Send CLI_MD5_LOGIN return 0; } SNAC_SUBHANDLER(icq_snac_secure_id_request) { /* SNAC(17,0A) SRV_SECURID_REQUEST Server SecureID request * * This snac used by server to request SecurID number from client. This * happens only if you are trying to login as AIM administrator. AIM * administrators have a little pager-like thing that display a SecurID * number wich changes every 60 seconds. Client must respond with * SNAC(17,0B) containing correct SecureID number (or server will send you * an auth error). */ /* ?wo? I'm sure, we don't need it */ return 0; } SNAC_HANDLER(icq_snac_sigon_handler) { snac_subhandler_t handler; switch (cmd) { case 0x01: handler = icq_snac_sigon_error; break; case 0x03: handler = icq_snac_sigon_reply; break; case 0x05: handler = icq_snac_new_uin; break; case 0x07: handler = icq_snac_sigon_authkey; break; case 0x0a: handler = icq_snac_secure_id_request; break; default: handler = NULL; break; } if (!handler) { debug_error("icq_snac_sigon_handler() SNAC with unknown cmd: %.4x received\n", cmd); icq_hexdump(DEBUG_ERROR, buf, len); } else handler(s, buf, len, data); return 0; } // vim:syn=c ekg2-0.4~pre+20120506.1/plugins/icq/misc.c000066400000000000000000000371631175142753400175230ustar00rootroot00000000000000/* * (C) Copyright 2006-2008 Jakub Zawadzki * 2008 Wiesaw Ochmiski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #include #include #include #include "icq.h" #include "icq_snac_handlers.h" #include "misc.h" void *ucs2be_conv_in = (void*) -1; void *ucs2be_conv_out = (void*) -1; void icq_hexdump(int level, unsigned char *p, size_t len) { #define MAX_BYTES_PER_LINE 16 unsigned char *payload = (unsigned char *) p; int offset = 0; while (len) { int display_len; int i; if (len > MAX_BYTES_PER_LINE) display_len = MAX_BYTES_PER_LINE; else display_len = len; /* offset */ debug_ext(level, "\t0x%.4x ", offset); /* hexdump */ for(i = 0; i < MAX_BYTES_PER_LINE; i++) { if (i < display_len) debug_ext(level, "%.2x ", payload[i]); else debug_ext(level, " "); } /* seperate */ debug_ext(level, " "); /* asciidump if printable, else '.' */ for(i = 0; i < display_len; i++) debug_ext(level, "%c", isprint(payload[i]) ? payload[i] : '.'); debug_ext(level, "\n"); payload += display_len; offset += display_len; len -= display_len; } } static void icq_pack_common(GString *str, char *format, va_list ap) { if (!format) return; while (*format) { switch (*format) { case 'c': /* guint8 */ case 'C': { guint32 src = va_arg(ap, guint32); unsigned char buf[1]; buf[0] = src; g_string_append_len(str, (char *) buf, 1); break; } case 'W': /* guint16 BE */ { guint32 src = va_arg(ap, guint32); unsigned char buf[2]; buf[0] = (src & 0xff00) >> 8; buf[1] = (src & 0x00ff); g_string_append_len(str, (char *) buf, 2); break; } case 'w': /* guint16 LE */ { guint32 src = va_arg(ap, guint32); unsigned char buf[2]; buf[0] = (src & 0x00ff); buf[1] = (src & 0xff00) >> 8; g_string_append_len(str, (char *) buf, 2); break; } case 'I': /* guint32 BE */ { guint32 src = va_arg(ap, guint32); unsigned char buf[4]; buf[0] = (src & 0xff000000) >> 24; buf[1] = (src & 0x00ff0000) >> 16; buf[2] = (src & 0x0000ff00) >> 8; buf[3] = (src & 0x000000ff); g_string_append_len(str, (char *) buf, 4); break; } case 'i': /* guint32 LE */ { guint32 src = va_arg(ap, guint32); unsigned char buf[4]; buf[3] = (src & 0xff000000) >> 24; buf[2] = (src & 0x00ff0000) >> 16; buf[1] = (src & 0x0000ff00) >> 8; buf[0] = (src & 0x000000ff); g_string_append_len(str, (char *) buf, 4); break; } case 'T': /* TLV */ /* guint32 type, guint32 len, guint8 *buf (buflen = len) */ { guint32 t_type = va_arg(ap, guint32); guint32 t_len = va_arg(ap, guint32); guint8 *t_buf = va_arg(ap, guint8 *); icq_pack_append(str, "WW", t_type, t_len); g_string_append_len(str, (char *) t_buf, t_len); break; } case 't': /* tlv */ /* guint32 type, guint32 len */ { guint32 t_type = va_arg(ap, guint32); guint32 t_len = va_arg(ap, guint32); icq_pack_append(str, "WW", t_type, t_len); break; } case 'U': { char *buf = va_arg(ap, char *); icq_pack_append(str, "W", (guint32) xstrlen(buf)); g_string_append(str, buf); break; } case 'u': /* uid */ { guint32 uin = va_arg(ap, guint32); const char *buf = ekg_itoa(uin); /* XXX, enough? */ icq_pack_append(str, "C", (guint32) xstrlen(buf)); g_string_append(str, buf); break; } case 's': { char *buf = va_arg(ap, char *); icq_pack_append(str, "C", (guint32) xstrlen(buf)); g_string_append(str, buf); break; } case 'P': /* caps */ { guint32 t_new = 0x09460000 | va_arg(ap, guint32); icq_pack_append(str, "IIII", (guint32) t_new, (guint32) 0x4c7f11d1, (guint32) 0x82224445, (guint32) 0x53540000); break; } case 'A': /* append GString* */ { GString *buf = va_arg(ap, GString *); g_string_append_len(str, buf->str, buf->len); break; } case ' ': /* skip whitespaces */ case 0x09: case 0x0A: case 0x0D: break; default: debug_error("icq_pack() unknown format: %c\n", *format); break; } format++; } } GString *icq_pack_append(GString *str, char *format, ...) { va_list ap; va_start(ap, format); icq_pack_common(str, format, ap); va_end(ap); return str; } GString *icq_pack(char *format, ...) { GString *str = g_string_new(NULL); va_list ap; va_start(ap, format); icq_pack_common(str, format, ap); va_end(ap); return str; } guint32 icq_string_to_BE(unsigned char *buf, int len) { switch (len) { case 1: return buf[0]; case 2: return buf[0] << 8 | buf[1]; case 4: return buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]; } /* debug("icq_string_to_BE() len: %d\n", len); */ return 0; } int icq_unpack_common(unsigned char *buf, unsigned char **endbuf, int *l, char *format, va_list ap) { static char ubuf[0xFF+1]; int used_ubuf = 0; static char Ubuf[0xFFFF+1]; int used_Ubuf = 0; int len = *l; if (!len || !format) return 0; while (*format) { if (*format >= '0' && *format <= '9') { long int skip = strtol(format, &format, 10); if (len < skip) { debug_error("icq_unpack() len: %d skiplen: %ld\n", len, skip); goto err2; } buf += skip; len -= skip; continue; } switch (*format) { case 'c': /* guint8 */ case 'C': { guint8 *dest = va_arg(ap, guint8 *); if (len < 1) goto err; *dest = *buf; buf++; len--; break; } case 'W': /* guint16 BE */ { guint16 *dest = va_arg(ap, guint16 *); if (len < 2) goto err; *dest = buf[0] << 8 | buf[1]; buf += 2; len -= 2; break; } case 'w': /* guint16 LE */ { guint16 *dest = va_arg(ap, guint16 *); if (len < 2) goto err; *dest = buf[1] << 8 | buf[0]; buf += 2; len -= 2; break; } case 'I': /* guint32 BE */ { guint32 *dest = va_arg(ap, guint32 *); if (len < 4) goto err; *dest = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]; buf += 4; len -= 4; break; } case 'i': /* guint32 LE */ { guint32 *dest = va_arg(ap, guint32 *); if (len < 4) goto err; *dest = buf[3] << 24 | buf[2] << 16 | buf[1] << 8 | buf[0]; buf += 4; len -= 4; break; } case 'u': { guint8 ulen; char **dest = va_arg(ap, char **); if (used_ubuf) { debug_error("icq_unpack() ubuf used!\n"); goto err2; } if (len < 1) goto err; ulen = buf[0]; buf += 1; len -= 1; if (len < ulen) goto err; *dest = memcpy(ubuf, buf, ulen); ubuf[ulen] = '\0'; buf += ulen; len -= ulen; used_ubuf = 1; break; } case 'U': case 'S': { guint16 Ulen; char **dest = va_arg(ap, char **); if (used_Ubuf) { debug_error("icq_unpack() Ubuf used!\n"); goto err2; } if (len < 2) goto err; if (*format == 'S') Ulen = buf[1] << 8 | buf[0]; else Ulen = buf[0] << 8 | buf[1]; buf += 2; len -= 2; if (len < Ulen) goto err; *dest = memcpy(Ubuf, buf, Ulen); Ubuf[Ulen] = '\0'; buf += Ulen; len -= Ulen; used_Ubuf = 1; break; } case 'x': /* skip this byte */ buf += 1; len -= 1; break; case 'X': /* skip this byte */ buf += 2; len -= 2; break; case ' ': /* skip whitespaces */ case 0x09: case 0x0A: case 0x0D: break; default: debug_error("icq_unpack() unknown format: %c\n", *format); goto err2; } format++; } *endbuf = buf; *l = len; return 1; err: debug_error("icq_unpack() len: %d format: %c\n", len, *format); err2: return 0; } int icq_unpack(unsigned char *buf, unsigned char **endbuf, int *l, char *format, ...) { va_list ap; int ret; va_start(ap, format); ret = icq_unpack_common(buf, endbuf, l, format, ap); va_end(ap); return ret; } int icq_unpack_nc(unsigned char *buf, int len, char *format, ...) { va_list ap; int ret; va_start(ap, format); ret = icq_unpack_common(buf, &buf, &len, format, ap); va_end(ap); return ret; } static LIST_FREE_ITEM(tlv_free_do_nothing, icq_tlv_t *) { } DYNSTUFF_LIST_DECLARE(icq_tlvs, icq_tlv_t, tlv_free_do_nothing, static __DYNSTUFF_ADD, /* icq_tlvs_add() */ __DYNSTUFF_NOREMOVE, __DYNSTUFF_DESTROY) /* icq_tlvs_destroy() */ icq_tlv_t *icq_tlv_get(struct icq_tlv_list *l, guint16 type) { for (; l; l = l->next) { if (l->type == type) return l; } return NULL; } struct icq_tlv_list *icq_unpack_tlvs(unsigned char **str, int *maxlen, unsigned int maxcount) { struct icq_tlv_list *ret = NULL; int count = 0; while (*maxlen >= 4) { guint16 type, len; icq_tlv_t *ptlv; if (!icq_unpack(*str, str, maxlen, "WW", &type, &len)) return ret; debug("str_readtlvs(%d) NEXTTLV type: 0x%x len: %d (maxlen: %d maxcount: %d)\n", count, type, len, *maxlen, maxcount ? maxcount-count : 0); if (*maxlen < len) { debug("str_readtlvs() 1897 Incomplete TLV %d, len %ld of %ld - ignoring.\n", type, len, *maxlen); return ret; } ptlv = xmalloc(sizeof(icq_tlv_t)); ptlv->type = type; ptlv->len = len; ptlv->buf = *str; ptlv->nr = icq_string_to_BE(ptlv->buf, ptlv->len); *maxlen -= len; *str += (len); /* go to next TLV */ icq_tlvs_add(&ret, ptlv); count++; if (maxcount && maxcount == count) break; } return ret; } struct icq_tlv_list *icq_unpack_tlvs_nc(unsigned char *str, int maxlen, unsigned int maxcount) { return icq_unpack_tlvs(&str, &maxlen, maxcount); } #include "miscicq.h" guint16 icq_status(int status) { switch (status) { case EKG_STATUS_NA: debug_error("icq_status(EKG_STATUS_NA)\n"); return 0; case EKG_STATUS_AVAIL: return ICQ_STATUS_ONLINE; case EKG_STATUS_AWAY: return ICQ_STATUS_AWAY; case EKG_STATUS_DND: return ICQ_STATUS_DND; case EKG_STATUS_FFC: return ICQ_STATUS_FFC; case EKG_STATUS_INVISIBLE: return ICQ_STATUS_INVISIBLE; case EKG_STATUS_XA: return ICQ_STATUS_OCCUPIED; /* XXX good choice? */ case EKG_STATUS_GONE: return ICQ_STATUS_NA; default: return STATUS_ICQONLINE; } } status_t icq2ekg_status2(int nMsgType) { switch (nMsgType) { case MTYPE_AUTOAWAY: return EKG_STATUS_AWAY; case MTYPE_AUTOBUSY: return EKG_STATUS_XA; /* XXX good choice? */ case MTYPE_AUTONA: return EKG_STATUS_GONE; case MTYPE_AUTODND: return EKG_STATUS_DND; case MTYPE_AUTOFFC: return EKG_STATUS_FFC; default: return EKG_STATUS_UNKNOWN; } } status_t icq2ekg_status(int icq_status) { /* * idea from IcqStatusToMiranda() * * Maps the ICQ status flag (as seen in the status change SNACS) and returns a EKG2 style status. * * NOTE: The order in which the flags are compared are important! */ if (icq_status & ICQ_STATUS_FLAG_INVISIBLE) return EKG_STATUS_INVISIBLE; if (icq_status & ICQ_STATUS_FLAG_DND) return EKG_STATUS_DND; if (icq_status & ICQ_STATUS_FLAG_OCCUPIED) return EKG_STATUS_XA; /* XXX good choice? */ if (icq_status & ICQ_STATUS_FLAG_NA) return EKG_STATUS_GONE; if (icq_status & ICQ_STATUS_FLAG_AWAY) return EKG_STATUS_AWAY; if (icq_status & ICQ_STATUS_FLAG_FFC) return EKG_STATUS_FFC; return EKG_STATUS_AVAIL; } int tlv_length_check(char *name, icq_tlv_t *t, int length) { if (t->len == length) return 0; debug_error("%s Incorrect TVL type=0x%02x. Length=%d, should be %d.\n", name, t->type, t->len, length); return 1; } /* hash password, ripped from micq */ char *icq_encryptpw(const char *pw) { /* Passwords are roasted when sent to the host. This is done so they aren't * sent in "clear text" over the wire, although they are still trivial to * decode. Roasting is performed by first xoring each byte in the password * with the equivalent modulo byte in the roasting array. */ guint8 tb[] = { 0xf3, 0x26, 0x81, 0xc4, 0x39, 0x86, 0xdb, 0x92, 0x71, 0xa3, 0xb9, 0xe6, 0x53, 0x7a, 0x95, 0x7c }; char *cpw = xstrdup(pw), *p; int i = 0; for (p = cpw; *p; p++, i++) *p ^= tb[i % 16]; return cpw; } #include "icq_debug.inc" const char *icq_lookuptable(struct fieldnames_t *table, int code) { int i; if (code == 0) return NULL; for(i = 0; table[i].code != -1 && table[i].text; i++) { if (table[i].code == code) return table[i].text; } debug_error("icq_lookuptable() invalid lookup: %x\n", code); return NULL; } void icq_pack_append_client_identification(GString *pkt) { /* * Pack client identification details. */ icq_pack_append(pkt, "T", icq_pack_tlv_str(0x03, CLIENT_ID_STRING)); // TLV(0x03) - client id string (name, version) icq_pack_append(pkt, "tW", icq_pack_tlv_word(0x16, CLIENT_ID_CODE)); // TLV(0x16) - client id number icq_pack_append(pkt, "tW", icq_pack_tlv_word(0x17, CLIENT_VERSION_MAJOR)); // TLV(0x17) - client major version icq_pack_append(pkt, "tW", icq_pack_tlv_word(0x18, CLIENT_VERSION_MINOR)); // TLV(0x18) - client minor version icq_pack_append(pkt, "tW", icq_pack_tlv_word(0x19, CLIENT_VERSION_LESSER)); // TLV(0x19) - client lesser version icq_pack_append(pkt, "tW", icq_pack_tlv_word(0x1a, CLIENT_VERSION_BUILD)); // TLV(0x1A) - client build number icq_pack_append(pkt, "tI", icq_pack_tlv_dword(0x14, CLIENT_DISTRIBUTION)); // TLV(0x14) - distribution number icq_pack_append(pkt, "T", icq_pack_tlv_str(0x0f, CLIENT_LANGUAGE)); // TLV(0x0F) - client language (2 symbols) icq_pack_append(pkt, "T", icq_pack_tlv_str(0x0e, CLIENT_COUNTRY)); // TLV(0x0E) - client country (2 symbols) } void icq_convert_string_init() { ucs2be_conv_in = ekg_convert_string_init("UCS-2BE", NULL, &ucs2be_conv_out); } void icq_convert_string_destroy() { if (ucs2be_conv_in != (void*) -1) { ekg_convert_string_destroy(ucs2be_conv_in); ekg_convert_string_destroy(ucs2be_conv_out); } } char *icq_convert_from_ucs2be(char *buf, int len) { GString *text, *ret; if (!buf || !len) return NULL; text = g_string_new(NULL); g_string_append_len(text, buf, len); ret = ekg_convert_string_t_p(text, ucs2be_conv_in); g_string_free(text, TRUE); if (ret) return g_string_free(ret, FALSE); return NULL; } GString *icq_convert_to_ucs2be(char *text) { GString *ret, *s; if (!text || !*text) return NULL; s = g_string_new(text); ret = ekg_convert_string_t_p(s, ucs2be_conv_out); /* XXX, ret == NULL */ g_string_free(s, TRUE); return ret; } void icq_send_snac(session_t *s, guint16 family, guint16 cmd, private_data_t *data, snac_subhandler_t subhandler, char *format, ...) { va_list ap; GString *pkt = g_string_new(NULL); if (format && *format) { va_start(ap, format); icq_pack_common(pkt, format, ap); va_end(ap); } icq_makesnac(s, pkt, family, cmd, data, subhandler); icq_send_pkt(s, pkt); } /* * rate limit handle */ void icq_rates_destroy(session_t *s) { icq_private_t *j; int i; if (!s || !(j = s->priv)) return; for (i=0; in_rates; i++) { xfree(j->rates[i]->groups); xfree(j->rates[i]); } xfree(j->rates); j->rates = NULL; j->n_rates = 0; } void icq_rates_init(session_t *s, int n_rates) { icq_private_t *j; int i; if (!s || !(j = s->priv)) return; if (j->rates) icq_rates_destroy(s); if (n_rates<=0) return; j->n_rates = n_rates; j->rates = xmalloc(sizeof(icq_rate_t *) * n_rates); for (i=0; in_rates; i++) j->rates[i] = xmalloc(sizeof(icq_rate_t)); } ekg2-0.4~pre+20120506.1/plugins/icq/misc.h000066400000000000000000000046431175142753400175250ustar00rootroot00000000000000#ifndef __ICQ_MISC_H #define __ICQ_MISC_H typedef struct icq_tlv_list { struct icq_tlv_list *next; guint16 type; guint16 len; guint32 nr; unsigned char *buf; } icq_tlv_t; struct fieldnames_t { int code; char *text; }; extern struct fieldnames_t snac_families[]; /* pack, unpack */ int icq_unpack(unsigned char *buf, unsigned char **endbuf, int *l, char *format, ...); int icq_unpack_nc(unsigned char *buf, int len, char *format, ...); #define icq_unpack_tlv_word(tlv, val) \ do { \ val = 0; \ icq_unpack_nc(tlv ? tlv->buf : NULL, tlv ? tlv->len : 0, "W", &val); \ } while(0); GString *icq_pack(char *format, ...); GString *icq_pack_append(GString *str, char *format, ...); #define icq_pack_tlv(type, data, datalen) (guint32) type, (guint32) datalen, (guint8 *) data #define icq_pack_tlv_char(type, data) (guint32) type, (guint32) 1, (guint32) data #define icq_pack_tlv_word(type, data) (guint32) type, (guint32) 2, (guint32) data #define icq_pack_tlv_dword(type, data) (guint32) type, (guint32) 4, (guint32) data #define icq_pack_tlv_str(type, str) icq_pack_tlv(type, str, xstrlen(str)) struct icq_tlv_list *icq_unpack_tlvs(unsigned char **str, int *maxlen, unsigned int maxcount); struct icq_tlv_list *icq_unpack_tlvs_nc(unsigned char *str, int maxlen, unsigned int maxcount); icq_tlv_t *icq_tlv_get(struct icq_tlv_list *l, guint16 type); void icq_tlvs_destroy(struct icq_tlv_list **list); void icq_hexdump(int level, unsigned char *p, size_t len); char *icq_encryptpw(const char *pw); guint16 icq_status(int status); #define ICQ_UNPACK(endbuf, args...) (icq_unpack(buf, endbuf, &len, args)) status_t icq2ekg_status(int icq_status); status_t icq2ekg_status2(int nMsgType); /* misc */ int tlv_length_check(char *name, icq_tlv_t *t, int length); #define ICQ_SNAC_NAMES_DEBUG 1 #if ICQ_SNAC_NAMES_DEBUG const char *icq_snac_name(int family, int cmd); #endif const char *icq_lookuptable(struct fieldnames_t *table, int code); void icq_pack_append_client_identification(GString *pkt); void icq_convert_string_init(); void icq_convert_string_destroy(); char *icq_convert_from_ucs2be(char *buf, int len); GString *icq_convert_to_ucs2be(char *text); char *icq_convert_from_utf8(char *text); void icq_send_snac(session_t *s, guint16 family, guint16 cmd, private_data_t *data, snac_subhandler_t subhandler, char *format, ...); void icq_rates_destroy(session_t *s); void icq_rates_init(session_t *s, int n_rates); #endif ekg2-0.4~pre+20120506.1/plugins/icq/miscicq.h000066400000000000000000000035571175142753400202250ustar00rootroot00000000000000#ifndef __ICQ_MISCICQ_H #define __ICQ_MISCICQ_H #define ICQ_STATUS_ONLINE 0x0000 #define ICQ_STATUS_AWAY 0x0001 #define ICQ_STATUS_NA 0x0005 #define ICQ_STATUS_OCCUPIED 0x0011 #define ICQ_STATUS_DND 0x0013 #define ICQ_STATUS_FFC 0x0020 #define ICQ_STATUS_INVISIBLE 0x0100 // Status FLAGS (used to determine status of other users) #define ICQ_STATUS_FLAG_ONLINE 0x0000 #define ICQ_STATUS_FLAG_AWAY 0x0001 #define ICQ_STATUS_FLAG_DND 0x0002 #define ICQ_STATUS_FLAG_NA 0x0004 #define ICQ_STATUS_FLAG_OCCUPIED 0x0010 #define ICQ_STATUS_FLAG_FFC 0x0020 #define ICQ_STATUS_FLAG_INVISIBLE 0x0100 #define MTYPE_AUTOAWAY 0xE8 // Auto away message #define MTYPE_AUTOBUSY 0xE9 // Auto occupied message #define MTYPE_AUTONA 0xEA // Auto not available message #define MTYPE_AUTODND 0xEB // Auto do not disturb message #define MTYPE_AUTOFFC 0xEC // Auto free for chat message #define STATUSF_WEBAWARE 0x00010000 /* The user is web-aware. */ #define STATUSF_DCAUTH 0x10000000 /* The user allows direct connections only upon authorization. */ #define STATUSF_DCCONTACT 0x20000000 /* The user allows direct connections only with contacts. */ #define STATUS_ICQONLINE 0x00000000 #define STATUS_ICQFFC 0x00000020 #define STATUS_ICQAWAY 0x00000001 #define STATUS_ICQDND 0x00000013 #define STATUS_INVISIBLE 0x00000100 #define STATUS_ICQOFFLINE 0xffffffff #define STATUSF_ICQOCC 0x00000010 #define STATUSF_ICQDND 0x00000002 #define STATUSF_ICQNA 0x00000004 // Client identification #define CLIENT_ID_STRING "EKG2" #define CLIENT_ID_CODE 0x0001 #define CLIENT_VERSION_MAJOR 0x0000 #define CLIENT_VERSION_MINOR 0x0002 #define CLIENT_VERSION_LESSER 0x0000 #define CLIENT_VERSION_BUILD 0x17AB #define CLIENT_DISTRIBUTION 0x00007535 #define CLIENT_LANGUAGE "en" #define CLIENT_COUNTRY "us" #endif ekg2-0.4~pre+20120506.1/plugins/ioctld/000077500000000000000000000000001175142753400171145ustar00rootroot00000000000000ekg2-0.4~pre+20120506.1/plugins/ioctld/commands-pl.txt000066400000000000000000000020571175142753400220730ustar00rootroot00000000000000// opis komend dla pluginu ioctld // (c) copyright 2001-2003 wojtek kaniewski // (c) copyright 2004 piotr kupisiewicz // $Id$ beeps_spk parametry: krotki opis: wydaje dźwięki zgodnie z sekwencją Kolejne dźwięki oddzielone są przecinkami. Dźwięk składa się z tonu w hercach i długości trwania w mikrosekundach oddzielonej ukośnikiem (,,%T/%n''). Jeśli nie podano czasu trwania, domyślna wartość to 0,1s. Zamiast sekwencji można podać nazwę formatu z themu. blink_leds parametry: krotki opis: odtwarza sekwencję na diodach LED Kombinacje diod oddzielone są przecinkami. Jeśli po kombinacji występuje znak ukośnika (,,%T/%n''), po nim podany jest czas trwania w mikrosekundach. Domyślny czas trwania to 0,1s. Kombinacja jest mapą bitową o następujących wartościach: 1 - NumLock, 2 - ScrollLock, 4 - CapsLock. Na przykład włączenie NumLock i CapsLock jednocześnie to 1+4 czyli 5. Zamiast sekwencji można podać nazwę formatu z themu. ekg2-0.4~pre+20120506.1/plugins/ioctld/ioctld.c000066400000000000000000000106251175142753400205420ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2002 Pawe Maziarz * Wojtek Kaniewski * Robert J. Wony * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifdef IOCTLD_BUILD /* to avoid miscompiling into plugin */ #include #include #include #include #include #ifdef __FreeBSD__ # include #endif #ifdef __sun /* Solaris */ # include # include #endif #ifdef __linux__ #if 0 /* This should fix compilation with newer kernel headers. I don't know why this * file was included here, so I'm not sure I don't break something * Maybe it was needed with older kernels? Anyone has got an idea? * As we still use -k in make, disabling it. If it fails, let me know. [peres] */ # include #endif # include #endif #include #include #include #include #include #include #include #include "ioctld.h" #ifndef PATH_MAX # ifdef MAX_PATH # define PATH_MAX MAX_PATH # else # define PATH_MAX _POSIX_PATH_MAX # endif #endif #include "ekg2-config.h" /* because of socklen_t */ #ifndef HAVE_SOCKLEN_T typedef unsigned int socklen_t; #endif char sock_path[PATH_MAX] = ""; int blink_leds(int *flag, int *delay) { int s, fd; #ifdef __sun int restore_data; if ((fd = open("/dev/kbd", O_RDONLY)) == -1) return -1; ioctl(fd, KIOCGLED, &restore_data); #else if ((fd = open("/dev/console", O_WRONLY)) == -1) fd = STDOUT_FILENO; #endif for (s = 0; flag[s] >= 0 && s <= IOCTLD_MAX_ITEMS; s++) { #ifdef __sun int leds = 0; /* tak.. na sunach jest to troszk inaczej */ if (flag[s] & 1) leds |= LED_NUM_LOCK; if (flag[s] & 2) leds |= LED_SCROLL_LOCK; if (flag[s] & 4) leds |= LED_CAPS_LOCK; ioctl(fd, KIOCSLED, &leds); #else ioctl(fd, KDSETLED, flag[s]); #endif if (delay[s] && delay[s] <= IOCTLD_MAX_DELAY) usleep(delay[s]); } #ifdef __sun ioctl(fd, KIOCSLED, &restore_data); #else ioctl(fd, KDSETLED, 8); #endif if (fd != STDOUT_FILENO) close(fd); return 0; } int beeps_spk(int *tone, int *delay) { int s; #ifndef __sun int fd; if ((fd = open("/dev/console", O_WRONLY)) == -1) fd = STDOUT_FILENO; #endif for (s = 0; tone[s] >= 0 && s <= IOCTLD_MAX_ITEMS; s++) { #ifdef __FreeBSD__ ioctl(fd, KIOCSOUND, 0); #endif #ifndef __sun ioctl(fd, KIOCSOUND, tone[s]); #else /* aosna namiastka... */ putchar('\a'); fflush(stdout); #endif if (delay[s] && delay[s] <= IOCTLD_MAX_DELAY) usleep(delay[s]); } #ifndef __sun ioctl(fd, KIOCSOUND, 0); if (fd != STDOUT_FILENO) close(fd); #endif return 0; } void quit() { unlink(sock_path); exit(0); } int main(int argc, char **argv) { int sock; socklen_t length; struct sockaddr_un addr; struct action_data data; if (argc != 2) { printf("program ten nie jest przeznaczony do samodzielnego wykonywania!\n"); exit(1); } if (strlen(argv[1]) >= PATH_MAX) exit(2); signal(SIGQUIT, quit); signal(SIGTERM, quit); signal(SIGINT, quit); umask(0177); close(STDERR_FILENO); strcpy(sock_path, argv[1]); unlink(sock_path); if ((sock = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) exit(1); addr.sun_family = AF_UNIX; strcpy(addr.sun_path, sock_path); length = sizeof(addr); if (bind(sock, (struct sockaddr *)&addr, length) == -1) exit(2); chown(sock_path, getuid(), -1); while (1) { if (recvfrom(sock, &data, sizeof(data), 0, (struct sockaddr *) &addr, &length) == -1) continue; if (data.act == ACT_BLINK_LEDS) blink_leds(data.value, data.delay); else if (data.act == ACT_BEEPS_SPK) beeps_spk(data.value, data.delay); } exit(0); } #endif /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/plugins/ioctld/ioctld.h000066400000000000000000000024721175142753400205500ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2002 Pawel Maziarz * Wojtek Kaniewski * Robert J. Wozny * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __EKG_IOCTLD_IOCTLD_H #define __EKG_IOCTLD_IOCTLD_H #define IOCTLD_MAX_ITEMS 50 #define IOCTLD_MAX_DELAY 2000000 #define IOCTLD_DEFAULT_DELAY 100000 struct action_data { int act; int value[IOCTLD_MAX_ITEMS]; int delay[IOCTLD_MAX_ITEMS]; }; enum action_type { ACT_BLINK_LEDS = 1, ACT_BEEPS_SPK = 2 }; int blink_leds(int *flag, int *delay); int beeps_spk(int *tone, int *delay); #endif /* __EKG_IOCTLD_IOCTLD_H */ /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/plugins/ioctld/main.c000066400000000000000000000112571175142753400202120ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2001-2003 Wojtek Kaniewski * Robert J. Wony * Pawe Maziarz * Wojciech Bojdo * Piotr Wysocki * Dawid Jarosz * Piotr Domagalski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #include #include #include #include #include #include #include #include #include #include #include "ioctld.h" static int ioctld_sock = -1; /* XXX, if this fd is -1, don't try to send data? don't register plugin? what? */ static int ioctld_pid = -1; PLUGIN_DEFINE(ioctld, PLUGIN_GENERIC, NULL); /* * ioctld_parse_seq() * * zamie string na odpowiedni struktur dla ioctld. * * - seq, * - data. * * 0/-1. */ static int ioctld_parse_seq(const char *seq, struct action_data *data) { char **entries; int i; if (!data || !seq) return -1; memset(data, 0, sizeof(struct action_data)); entries = array_make(seq, ",", 0, 0, 1); for (i = 0; entries[i] && i < IOCTLD_MAX_ITEMS; i++) { int delay; char *tmp; if ((tmp = xstrchr(entries[i], '/'))) { *tmp = 0; delay = atoi(tmp + 1); } else delay = IOCTLD_DEFAULT_DELAY; data->value[i] = atoi(entries[i]); data->delay[i] = delay; } g_strfreev(entries); return 0; } /* * ioctld_socket() * * inicjuje gniazdo dla ioctld. * * - path - cieka do gniazda. * * 0/-1. */ static int ioctld_socket(const char *path) { struct sockaddr_un sockun; int i, usecs = 50000; if (ioctld_sock != -1) close(ioctld_sock); if ((ioctld_sock = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) return -1; sockun.sun_family = AF_UNIX; g_strlcpy(sockun.sun_path, path, sizeof(sockun.sun_path)); for (i = 5; i; i--) { /* XXX, make it non-blocking.. use watches */ if (connect(ioctld_sock, (struct sockaddr*) &sockun, sizeof(sockun)) != -1) return 0; usleep(usecs); } close(ioctld_sock); ioctld_sock = -1; return -1; } /* * ioctld_send() * * wysya do ioctld polecenie uruchomienia danej akcji. * * - seq - sekwencja danych, * - act - rodzaj akcji. * * 0/-1. */ static int ioctld_send(const char *seq, int act, int quiet) { struct action_data data; if (*seq == '$') /* dla kompatybilnoci ze starym zachowaniem */ seq++; if (!xisdigit(*seq)) { const char *tmp = format_find(seq); if (!format_ok(tmp)) { printq("events_seq_not_found", seq); return -1; } seq = tmp; } if (ioctld_parse_seq(seq, &data)) { printq("events_seq_incorrect", seq); return -1; } data.act = act; if (ioctld_sock == -1) { printq("generic_error", "ioctld internal error, try /plugin -ioctl; /plugin +ioctl if it won't help report bugreport"); return -1; } return send(ioctld_sock, &data, sizeof(data), 0); } static COMMAND(command_beeps_spk) { if (!params[0]) { printq("not_enough_params", name); return -1; } return ((ioctld_send(params[0], ACT_BEEPS_SPK, quiet) == -1) ? -1 : 0); } static COMMAND(command_blink_leds) { if (!params[0]) { printq("not_enough_params", name); return -1; } return ((ioctld_send(params[0], ACT_BLINK_LEDS, quiet) == -1) ? -1 : 0); } EXPORT int ioctld_plugin_init(int prio) { const char *ioctld_sock_path; PLUGIN_CHECK_VER("ioctld"); plugin_register(&ioctld_plugin, prio); ioctld_sock_path = prepare_path(".socket", 1); if (!(ioctld_pid = fork())) { execl(IOCTLD_PATH, "ioctld", ioctld_sock_path, (void *) NULL); exit(0); } ioctld_socket(ioctld_sock_path); command_add(&ioctld_plugin, ("ioctld:beeps_spk"), ("?"), command_beeps_spk, 0, NULL); command_add(&ioctld_plugin, ("ioctld:blink_leds"), ("?"), command_blink_leds, 0, NULL); return 0; } static int ioctld_plugin_destroy() { plugin_unregister(&ioctld_plugin); if (ioctld_pid > 0) kill(ioctld_pid, SIGINT); if (ioctld_sock != -1) close(ioctld_sock); return 0; } /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/plugins/irc/000077500000000000000000000000001175142753400164135ustar00rootroot00000000000000ekg2-0.4~pre+20120506.1/plugins/irc/Changelog000066400000000000000000000126261175142753400202340ustar00rootroot00000000000000startdate: 15:26:o3 CET 2oo8-16-o2 * from now on all channel names are kept in lower case, mind that, when dealing with channel names, before operating on channel name, you should convert it's name to lower case BUT using: irc_tolower_int or IRC_TOLOWER macro stardate: 12:4o:o9 CET 2oo6-19-o2 version 0.9 (GiM) * this changelog is obsolete, please read commit-messages for more infor :) stardate: 23:16:28 CEST 2oo5-o1-o9 version 0.8 (dj,GiM) * new resolver [solid testing needed] * ignore * big code cleanup * features in /names * perl plugin support stardate: 15:44:24 CEST 2oo5-27-o3 version 0.7.65 (GiM) * new nice irc_getchan * applied dj's patches stardate: xx:xx:xx WEST 24.o3.2oo5 version 0.7.? (darkjames) * invite command && lazy handling (displaying && AUTO_JOIN_CHANS_ON_INVITE) stardate: xx:xx:xx WEST 21.o3.2oo5 version 0.7.? (darkjames) * fixed large 4K memleak @ irc_handle_stream when len < 1 * probably fixed sigsegv @ irc_handle_disconnect [CHECK] * nice channel and nick syncing. * prefer_family * some new features connected with nickchanging. * /ban /kick /kickban /ban implementation stardate: xx:xx:xx WEST 20.o3.2oo5 version 0.7.? (darkjames) * fixed iwil bug. Changed window_find() to window_find_s() * (buggy (?)) reconnecting support [commented out, because of faulty timer handling, [ekg2's fault not yours :)]] stardate: xx:xx:xx WEST 01.o3.2oo5 version 0.7.? (darkjames) * changing variable name from local_ip to hostname. (irssi like) stardate: xx:xx:xx WEST 27.o2.2oo5 version 0.7.? (darkjames) * fixed displaying "* no such nick / server" [rejected] * fixed stupid memleaks in irc_c_whois stardate: 12:1o:5o WEST 2oo4-17-1o version 0.7.26 (GiM) * irc.c: - password on servers * input.c: - colours in ctcps * misc.c: - DISPLAY_NICKCHANGE stardate: 19:54:13 CEST 2oo4-13-o9 version 0.7.14 (GiM) - ident@host instead of nick!ident@host in formats (dre/GiM) - yes, I'm forgetting about changing version no. * irc.c: - Solaris compatibility (Beeth) * misc.c: - fixed autojoin (viurs^/GiM) - fix for FreeBSD in *_msg (Greyer/GiM) - SHOW_NICKMODE_EMPTY (dre/GiM) * people.c: - fix in deleting person's channels (Greyer/GiM) * autoacts.c: - fixed autojoining on [dis]connect stardate: o9:34:41 CEST 2oo4-o7-o9 version 0.7.13 (GiM) - SKIP_MOTD --> SHOW_MOTD - AUTO_JOIN [/session AUTO_JOIN for help] * misc.c: - simple highlight when our nick is in message on channel - improved beeping ;) stardate: 11:57:26 CEST 2oo4-o4-o9 version 0.7.09 (GiM) - fixes in quit and whois - DISPLAY_QUIT - changed nick displaying (@%+ ) - added nick colouring depending on nick prefix - added /[de]halfop - changed beep bahaviour stardate: o9:o9:38 CEST 2oo4-31-o8 version 0.6.98 (GiM) * added session.txt * autoacts.[ch]: - responsible for autorejoin * irc.c: - changed /part (del/GiM) - deleted irc_getarg - changed most of function - added /me - changed /part,/join, added /cycle * misc.c: - fixed silly, but hard to find segv in irc_free_people stardate 18:37:53 CEST 2oo4-27-o8 version 0.6.68 (GiM) * irc.c: - irc_getchan() renamed to irc_getchan_int() - changed /mode behaviour, added /umode - /whois - changed /mode * misc.c: - WHOIS reply handling stardate: 22:51:3o CEST 2oo4-26-o8 version 0.6.48 (GiM) * input.c: - added simple ctcp handling routines - fixed window printing * irc.c: - added /ctcp command - added VERSION_* variables stardate: oo:o6:57 CEST 2oo4-25-o8 version 0.5.95 (GiM) * misc.c: - KICK and QUIT handling, - irc_del_person() rewritten * irc.c: - fixes in nearly every function - additional MODE commands: /op; /deop; /voice; /unvoice stardate: 22:11:53 CEST 2oo4-22-o8 version 0.5.62 (GiM) * input.[ch] [NEW file]: - colours support ;> stardate: 21:55:35 CEST 2oo4-2o-o8 version 0.5.20 (GiM) * irc.c: - MODE command * misc.c: - improvments in irc_parse_line() - Improvemnts in prefix-modes changing thanks to Piotr 'Beeth' Kucharski dor pointing me to: www.irc.org/tech_docs/005.html * people.c: - NEW irc_color_in_contacts() stardate: yesterday * irc.c: - More simplifications in /part; /join - TOPIC command: /topic [chan] [:|new topic] details in README * misc.c/irc.c: - NOTICE command stardate: o1:o6:28 CEST 2oo4-19-o8 version 0.4.57 (GiM) * I've started Changelog ;] * I've simplified today most of functions I've written to this moment, + different ways to join channel: /window new irc:#fsck + /join; /join #fsck; /join irc:#fsck; + different ways to part channel: /part; /part #fsck; /part irc:#fsck; Alt+k; /window kill * I've changed some things, that maybe aren't important now, but I hope they will make easier some stuff in the future... Yesterday iirc * changed lot of things, cause I've discoverer I was doing really baaad things ;) * dealing with RPLs 321, 322, 323, TOPIC, MODE server responses [right now I just care about +-[ov], to change colors in contactlist] Explanation: irc-plugin version changes quite quick, because it's rather built number than version number ;] The first number I've entered manually was 0.2.40. Maybe there were more builts, than 240, but ahh yawn, I'm going to bed client->server: irc.c server->client: misc.c channels stuff: people.c colouring+ctcp: input.c ekg2-0.4~pre+20120506.1/plugins/irc/IRCVERSION.h000066400000000000000000000003021175142753400202420ustar00rootroot00000000000000#define IRCVERSION "0.8.git" #ifndef IRCVERSION #include "ircversion.h" #endif /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/plugins/irc/README000066400000000000000000000001711175142753400172720ustar00rootroot00000000000000take a look into command.txt and maybe head Changelog, I'm too lazy to write readme file ;> - Michal 'GiM' Spadlinski ekg2-0.4~pre+20120506.1/plugins/irc/_unused.c000066400000000000000000000022731175142753400202250ustar00rootroot00000000000000#if 0 /* * Unused stuff. * * For archaeological and sentimental purposes only * */ /** * irc_toupper_int(char *buf, int casemapping) * * Converts buffer pointed at buf to upper case using one of casmapping's: * IRC_CASEMAPPING_ASCII, IRC_CASEMAPPING_RFC1459, IRC_CASEMAPPING_RFC1459_STRICT * * DO NOT pass strings that can be in unicode; * * @return pointer to beginning of a string */ static char *irc_toupper_int(char *buf, int casemapping) { char *p = buf; int upper_bound; /* please, do not change this code, to something like: * 122 + (!!casemapping * (5-casemapping)) */ switch (casemapping) { case IRC_CASEMAPPING_ASCII: upper_bound = 'z'; break; case IRC_CASEMAPPING_RFC1459_STRICT: upper_bound = '}'; break; case IRC_CASEMAPPING_RFC1459: upper_bound = '~'; break; default: debug_error ("bad value in call to irc_tolower_int: %d\n", casemapping); return 0; } while (*p) { if (*p >= 'a' && *p <= upper_bound) *p -= 32; /* substract 32, convesion 'a' -> 'A' */ p++; } return buf; } static void resolver_child_handler(child_t *c, int pid, const char *name, int status, void *priv) { debug("(%s) resolver [%d] exited with %d\n", name, pid, status); } #endif ekg2-0.4~pre+20120506.1/plugins/irc/autoacts.c000066400000000000000000000132641175142753400204100ustar00rootroot00000000000000/* * (C) Copyright 2004-2005 Michal 'GiM' Spadlinski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include "irc.h" #include "autoacts.h" static TIMER(irc_autorejoin_timer); /** * irc_onkick_handler() * * Handler for: IRC_KICK
* * Here we check if we were kicked (by checking @a nick) and if yes, than if we want to rejoin on kick (REJOIN && REJOIN_TIME)
* Than after time specified by REJOIN_TIME we try to rejoin * * @sa irc_autorejoin() - for rejoin function * @sa irc_autorejoin_timer() - for rejoin timer. * * @todo We don't check if @a nick and @a chan is full uid.. It's I think correct.. However can be faulty. * * @param ap 1st param: (char *) session - session uid * ap 2nd param: (char *) nick - full uid of kicked person (irc:nickname) * ap 3rd param: (char *) chan - full uid of channel where kick event happen. * ap 4th param: (char *) kickedby - full uid who kicked. * @param data NULL * * @return 1 - If no session, no irc session, or no private struct.
* 2 - If we are not interested in autorejoining [either smb else was kicked (not us) or REJOIN was not set]
* 3 - If we'll try to autorejoin */ QUERY(irc_onkick_handler) { char *session = *va_arg(ap, char **); char *nick = *va_arg(ap, char **); char *chan = *va_arg(ap, char **); char *kickedby = *va_arg(ap, char **); session_t *s = session_find(session); irc_private_t *j; if (!s || !(j = s->priv) || s->plugin != &irc_plugin) return 1; if (!xstrcmp(j->nick, nick+4)) { int rejoin = session_int_get(s, "REJOIN"); if (rejoin < 0) return 2; if (rejoin&(1<<(IRC_REJOIN_KICK))) { int rejoin_time = session_int_get(s, "REJOIN_TIME"); /* if it's negative value, fix it. cause we set REJOIN... */ if (rejoin_time < 0) rejoin_time = 0; /* if we don't want to wait after kick, we do it here. not in next ekg_loop() */ if (rejoin_time == 0) { irc_autorejoin(s, IRC_REJOIN_KICK, chan+4); return 3; } irc_onkick_handler_t *data = xmalloc(sizeof(irc_onkick_handler_t)); data->s = s; data->nick = xstrdup(nick); data->kickedby = xstrdup(kickedby); data->chan = xstrdup(chan); timer_add(&irc_plugin, NULL, rejoin_time, 0, irc_autorejoin_timer, data); return 3; } } return 2; } /** * irc_autorejoin_timer() * * Timer handler for auto-rejoining
* Added by irc_autorejoin() * It just execute irc_autorejoin() with params... * * @todo Remove some struct info from irc_onkick_handler_t? Here we use only channelname and session.. * @todo Check session with session_find_ptr() ? * * @param data - irc_onkick_handler_t * struct with data inited by irc_autorejoin() * * @return -1 [TEMPORARY HANDLER] */ static TIMER(irc_autorejoin_timer) { irc_onkick_handler_t *d = data; if (type == 1) { xfree(d->nick); xfree(d->kickedby); xfree(d->chan); xfree(d); return 0; } debug_white("irc_autorejoin_timer() rejoining to: %s\n", d->chan); irc_autorejoin(d->s, IRC_REJOIN_KICK, (d->chan)+4); return -1; } /** * irc_autorejoin() * * Try to rejoin. * * @todo We double check "REJOIN" if IRC_REJOIN_KICK * * @param s - session * @param when - type of rejoining:
* - IRC_REJOIN_CONNECT When we want to rejoin to all channels we had opened for example after /reconnect * - IRC_REJOIN_KICK When we want to rejoin to given channel (@a chan) * @param chan - if @a when == IRC_REJOIN_KICK than it specify to which channel we want to rejoin after kick. * * @return 0 - if we send JOIN commands to ircd...
* -1 - If smth went wrong. */ int irc_autorejoin(session_t *s, int when, char *chan) { irc_private_t *j; string_t st; window_t *w; char *chanprefix; int rejoin; #if 1 /* there's no need of doing it, already checked by irc_onkick_handler() or if it goes through irc_c_init() it's even better. */ if (!s || !(j = s->priv) || (s->plugin != &irc_plugin)) return -1; #endif chanprefix = SOP(_005_CHANTYPES); rejoin = session_int_get(s, "REJOIN"); if (!(rejoin&(1<<(when)))) return -1; switch (when) { case IRC_REJOIN_CONNECT: st = string_init(NULL); for (w = windows; w; w = w->next) { if (!w->target || w->session != s) /* check if sessions match and has w->target */ continue; if (valid_plugin_uid(s->plugin, w->target) != 1) /* check if window is correct for irc: */ continue; if (!xstrchr(chanprefix, (w->target)[4])) /* check if this is channel.. */ continue; if (st->len) string_append_c(st, ','); if ((w->target)[4] == '!') { string_append_c(st, '!'); string_append(st, w->target + 10); } else { string_append(st, w->target + 4); } } if (st->len) ekg_connection_write(j->send_stream, "JOIN %s\r\n", st->str); string_free(st, 1); break; case IRC_REJOIN_KICK: ekg_connection_write(j->send_stream, "JOIN %s\r\n", chan); break; default: return -1; } return 0; } /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/plugins/irc/autoacts.h000066400000000000000000000022101175142753400204020ustar00rootroot00000000000000/* * (C) Copyright 2004-2005 Michal 'GiM' Spadlinski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __EKG_PLUGINS_IRC_AUTOACTS_H #define __EKG_PLUGINS_IRC_AUTOACTS_H enum { IRC_REJOIN_KICK=0, IRC_REJOIN_CONNECT }; typedef struct { session_t *s; char *nick; char *kickedby; char *chan; } irc_onkick_handler_t; int irc_autorejoin(session_t *s, int when, char *chan); QUERY(irc_onkick_handler); #endif /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/plugins/irc/commands-pl.txt000066400000000000000000000125761175142753400214010ustar00rootroot00000000000000// IRC protocol commands description // (c) 2004 Michal 'GiM' Spadlinski // parametry opcjonalne są w <> wymagane w [], // + lub * oznacza, że można podać więcej parametrów // danego typu NIEZNANA_KOMENDA parametry: [tekst] krotki opis: /help irc: by dostać pomoc na temat plugina [tekst] oznacza, że parametr jest wymagany, , że parametr jest opcjonalny, + lub * po parametrze oznacza, że może występować więcej parametrów danego typu jeżeli w przykładach komend pojawia się średnik, oznacza on koniec komendy [należy wcisnąć enter ;>] connect parametry: krotki opis: podłącza sesję do sieci IRC Należy wcześniej ustawić zmienną sesyjną server [/session irc:nazwa_sesji server irc_serwer_name] oraz zmienną nickname. disconnect parametry: krotki opis: odłącza od servera sieci IRC wysyła jako QUIT, jeśli nie podano wysyła tekst: "EKG2 - It's better than sex!" reconnect parametry: krotki opis: rozłącza z serwerem i łączy ponownie jest przekazywany do polecenia disconnect join parametry: + krotki opis: dołącza użytkownika do kanału/ów W wypadku większej ilości parametrów, nazwy kanałów należy oddzielić przecinkami. np: /join !ekg2,#apcoh,!apcoh,#english jeżeli nie podano argumentu, próbuje się podłączyć do kanału, którego nazwą jest nazwa aktualnego okna [np można zrobić: /window new irc:!6E6WPekg2; /join] part parametry: krotki opis: odłącza użytkownika od aktualnego lub podanego kanału Jeśli podano powód i jeżeli serwer obsługuje powody, zostanie on wyświetlony przy wychodzeniu innym osobom na kanale. jeżeli pierwszy argument nie jest kanałem, brana jest nazwa aktualnego okna. cycle parametry: krotki opis: robi /part i /join jeżeli pierwszy argument nie jest kanałem, brana jest nazwa aktualnego okna query parametry: [nick] krotki opis: otwiera okno rozmowy z osobą nick parametry: [nowynick] krotki opis: zmienia nick na nowy. Komenda ta zmienia tylko nicka, na IRCu, nie zmienia zmiennej sesyjnej nickname, w związku z czym po ponownym połaczeniu będzie taki nick, jaką wartość ma owa zmienna sesyjna. topic parametry: <:>| krotki opis: wyświetla/zmienia/usuwa topic kanału -| bez parametrów - wyświetla topic aktualnego kanału -| [kanał] - wyświetla topic kanału -| [:] - usuwa topic aktualnego, bądź podanego kanału -| [nowy temat] - ustawia topic aktualnego, bądź podanego kanału na 'nowy temat' people parametry: add parametry: krotki opis: nie robi nic ;-) msg parametry: [nick|kanał] [wiadomość] krotki opis: no zgadnij ;-) me parametry: [tekst] krotki opis: robi /me ;-) ctcp parametry: <...> krotki opis: wysyła zapytanie ctcp do osoby, kanału. Rodzaj to: ACTION [/me], FINGER, VERSION, SOURCE, USERINFO, CLIENTINFO, PING, TIME, ERRMSG przed wysłaniem ctcp warto wysłać /ctcp nick|kanał clientinfo w celu sprawdzenia, jakie ctcp obsługuje inny klient. jeżeli nie zostanie podany rodzaj wysyłany jest VERSION jeżeli nie podamy, rodzaju, można nie podać pierwszego parametru, jeżeli tylko znajdujemy się w oknie [kanału|rozmowy] mode parametry: [tryby] krotki opis: zobacz też /help irc:umode ustawia MODE kanału [poczytaj jakiś help o MODEach kanałów] jeżeli nie podano parametru, brana jest nazwa aktualnego okna, [jeśli oczywiście jest to okno kanału!] umode parametry: [+|-][riws] krotki opis: zmienia %TTWÓJ%n mode r - ustawia restricted [serwer raczej nie pozwoli zdjąć ;>] i - invisible w - otrzymywanie wallops s - otrzymywanie notice'ów od serwera whois parametry: [nick] krotki opis: robi /whois na danej osobie op parametry: [nick]* krotki opis: tłumaczy się samo :) w wypadku większej ilości nicków, należy oddzielać spacjami deop parametry: [nick]* krotki opis: tłumaczy się samo :) w wypadku większej ilości nicków, należy oddzielać spacjami voice parametry: [nick]* krotki opis: tłumaczy się samo :-) w wypadku większej ilości nicków, należy oddzielać spacjami devoice parametry: [nick]* krotki opis: tłumaczy się samo :) w wypadku większej ilości nicków, należy oddzielać spacjami halfop parametry: [nick]* krotki opis: tłumaczy się samo :-) w wypadku większej ilości nicków, należy oddzielać spacjami dehalfop parametry: [nick]* krotki opis: tłumaczy się samo :) w wypadku większej ilości nicków, należy oddzielać spacjami away parametry: krotki opis: zmienia stan na zajęty _autoaway parametry: krotki opis: zmienia stan na zajęty back parametry: krotki opis: zmienia stan na dostępny _autoback parametry: krotki opis: zmienia stan na zajęty quote parametry: [komendy] krotki opis: wysyła bezpośrednio tekst do serwera IRC invite parametry: [nick] krotki opis: zaprasza użytkownika na kanał who parametry: krotki opis: wyświetla dane o użytkownikach z danego kanału/pasujących do maski Jeśli poleceniu nie podane zostaną żadne polecenia, to wyświetleni zostaną użytkownicy z bieżącego kanału. names parametry: krotki opis: wyświetla nazwy użytkowników z bieżącego kanału ekg2-0.4~pre+20120506.1/plugins/irc/input.c000066400000000000000000000265771175142753400177370ustar00rootroot00000000000000/* * (C) Copyright 2004-2005 Michal 'GiM' Spadlinski * Jakub 'darkjames' Zawadzki * Wiesaw Ochmiski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #include #include #ifndef NO_POSIX_SYSTEM #include #endif #include #include "input.h" #include "IRCVERSION.h" #define DEFAULT_COLOR 0 /* GiM: I've decided to make one big handler instead of many small ones */ CTCP_COMMAND(ctcp_main_priv); CTCP_COMMAND(ctcp_main_noti); static int irc_getircoldcol(char *org) { char *p = org; int ibg, ifg, isfg, isbg, isdel, i, ret; if (!p || !*p) return 0; isfg = isbg = isdel = 0; if (sscanf(p, "%02d", &ifg) == 1) { p++; if (isdigit(*p)) p++; isfg = 1; } if (*p == ',') { p++; isdel=1; if (sscanf(p, "%02d", &ibg) == 1) { p++; if(isdigit(*p)) p++; isbg = 1; } } i = ((p-org))&0xff; ret = i<<24; if (isfg) { ret |= (isfg<<17); ret |= (ifg<<8); } if (isdel && !isbg) { isbg = 1; ibg=DEFAULT_COLOR; } if (isbg) { ret |= (isbg<<16); ret |= ibg; } return ret; } char *irc_ircoldcolstr_juststrip(session_t *sess, char *inp) { int col; char *ret, *str, *back; if (!inp || !(*inp)) return xstrdup(""); ret = str = xstrdup(inp); back = str; for (;*str;) { if (*str == 3) /* ^c */ { col = irc_getircoldcol(str+1); str+=(col>>24)&0xff; } else if (*str == 2) /* ^b */ {} else if (*str == 15) /* ^o */ {} else if (*str == 18 || *str == 22) /* ^r */ {} else if (*str == 31) /* ^_ */ {} else *back++ = *str; str++; } *back = '\0'; return ret; } char *irc_ircoldcolstr_to_ekgcolstr_nf(session_t *sess, char *str, int strip) { int col, oldstrip = strip; char mirc_sux_so_much[16] = "WkbgrypRYGcCBPKw"; char mirc_sux_even_more[16] = "xlehszqszhddeqlx"; string_t s; if (!str || !(*str)) return xstrdup(""); s = string_init(""); if (strip) strip = session_int_get(sess, "STRIPMIRCCOL"); for (;*str;) { if (*str == 3) /* ^c */ { /* str++; */ col = irc_getircoldcol(str+1); if (strip) goto coloring_finito; if (!col) { string_append(s, "%n"); goto coloring_finito; } if (col&0x20000) { string_append_c(s, '%'); string_append_c(s, mirc_sux_so_much [(col>>8)&0xf]); } if (col&0x10000) { string_append_c(s, '%'); string_append_c(s, mirc_sux_even_more [col&0xf]); } coloring_finito: str+=(col>>24)&0xff; } else if (*str == 2) /* ^b */ string_append(s, "%T"); else if (*str == 15) /* ^o */ string_append(s, "%n"); else if (*str == 18 || *str == 22) /* ^r */ string_append(s, "%V"); else if (*str == 31) /* ^_ */ string_append(s, "%U"); else if (*str == '%') string_append(s, "\\%"); else if (*str == '\\') string_append(s, "\\\\"); else if ((*str == '/') && (str[1] == '|')) string_append(s, "//"); else string_append_c(s, *str); str++; } if (oldstrip) string_append(s, "%n"); return string_free(s, 0); } char *irc_ircoldcolstr_to_ekgcolstr(session_t *sess, char *str, int strip) { char *format; char *formatted; if (!str || !(*str)) return xstrdup(""); format = irc_ircoldcolstr_to_ekgcolstr_nf(sess, str, strip); formatted = format_string(format); xfree(format); return formatted; } /* * * http://www.irchelp.org/irchelp/rfc/ctcpspec.html * */ static int is_ctcp(char *mesg) { int i; char *p; const ctcp_t *ret; if ((p = xstrchr(mesg, ' '))) *p = '\0'; for (i = 0,ret = ctcps; (ret->name); i++, ret++) if (!xstrcmp(mesg, ret->name)) { if (p) *p = ' '; return i+1; } return 0; } char *ctcp_parser(session_t *sess, int ispriv, char *sender, char *recp, char *s, int to_us) { irc_private_t *j = session_private_get(sess); char *begin, *end, *winname, *p, *bang, *newsender, *coloured; string_t ret; int ctcp; if (!s || xstrlen(s) < 2) return s?xstrdup(s):NULL; winname = irc_uid(recp); ret = string_init(""); p = begin = s; while (1) { if (!(begin = xstrchr(begin, 1))) break; if (!(end = xstrchr(begin+1, 1))) break; *begin = '\0'; begin++; *end = '\0'; if ((ctcp = is_ctcp(begin))) { if ((bang = xstrchr(sender, '!'))) *bang = '\0'; newsender = irc_uid(sender); coloured = irc_ircoldcolstr_to_ekgcolstr(sess, begin,1); if (ispriv) { if (!ctcps[ctcp-1].handled) { irc_write(sess, "NOTICE %s :\01ERRMSG %s :not handled\01\r\n", sender, ctcps[ctcp-1].name); } else if ((ctcp_main_priv(sess, j, ctcp, coloured, newsender, bang?bang+1:"", winname, to_us))) { /* blah blah blah */ } } else { ctcp_main_noti(sess, j, ctcp, coloured, newsender, bang?bang+1:"", winname, to_us); } xfree(newsender); xfree(coloured); if (bang) *bang = '!'; string_append(ret, p); p = end+1; } else { /* print_info(winname, sess, "irc_unknown_ctcp", session_names(sess), sender, begin, spc+1); *spc = ' '; */ irc_write(sess, "NOTICE %s :\01ERRMSG %s :unknown ctcp\01\r\n", sender, begin); begin--; *begin = 1; *end = 1; } begin=end+1; } xfree(winname); string_append(ret, p); p = string_free(ret, 0); if (xstrlen(p)) return p; xfree(p); return NULL; } /* * This is used by losers on IRC to simulate "role playing" games. * ;-) */ CTCP_COMMAND(ctcp_main_priv) { char *ischn = xstrchr(SOP(_005_CHANTYPES), targ[4]); char *space = xstrchr(ctcp, ' '); int i, mw = session_int_get(s, "make_window"); char *ta, *tb, *tc; char *purename = sender+4, *win; char *cchname = clean_channel_names(s, targ+4); struct utsname un; time_t timek; window_t *w; if (space) while ((*space) && (*space == ' ')) space++; win = ischn?targ:sender; w = window_find_s(s, win); if (!(ischn || w || (mw&4))) win = window_current->target; switch (number) { case CTCP_ACTION: /* ===== ===== ===== ===== ===== ACTION */ /* skip spaces... */ if (ignored_check(s, sender) & IGNORE_MSG) break; if (space && xstrlen(space)) { char *format; int class = EKG_MSGCLASS_CHAT | EKG_NO_THEMEBIT; int beep = EKG_NO_BEEP; if (to_us) beep = EKG_TRY_BEEP; else class |= EKG_MSGCLASS_NOT2US; format = format_string(format_find(ischn?"irc_ctcp_action_pub":"irc_ctcp_action"), session_name(s), purename, idhost, cchname, space); protocol_message_emit(s, win, NULL, format, NULL, time(NULL), class, NULL, beep, 1); xfree(format); } break; case CTCP_DCC: /* ===== ===== ===== ===== ===== DCC */ break; case CTCP_SED: /* ===== ===== ===== ===== ===== SED */ break; case CTCP_FINGER: /* ===== ===== ===== ===== ===== FINGER */ ta = xstrdup(ctime(&(s->last_conn))); if (ta[xstrlen(ta)-1] == '\n') ta[xstrlen(ta)-1]='\0'; print_window(win, s, EKG_WINACT_MSG, ischn?(mw&1):!!(mw&4), ischn?"irc_ctcp_request_pub":"irc_ctcp_request", session_name(s), purename, idhost, cchname, ctcp); irc_write(s, "NOTICE %s :\01FINGER :%s connected since %s\01\r\n", purename, j->nick, ta); xfree(ta); break; case CTCP_VERSION: /* ===== ===== ===== ===== ===== VERSION */ print_window(win, s, EKG_WINACT_MSG, ischn?(mw&1):!!(mw&4), ischn?"irc_ctcp_request_pub":"irc_ctcp_request", session_name(s), purename, idhost, cchname, ctcp); ta = (char *)session_get(s, "VERSION_NAME"); tb = (char *)session_get(s, "VERSION_NO"); tc = (char *)session_get(s, "VERSION_SYS"); if (tc || uname(&un) == -1) { irc_write(s, "NOTICE %s :\01VERSION %s%s%s\01\r\n", purename, ta?ta:"IRC plugin under EKG2:", tb?tb:IRCVERSION":", tc?tc:"unknown OS"); break; } irc_write(s, "NOTICE %s :\01VERSION %s%s%s %s %s\01\r\n", purename, ta?ta:"IRC plugin under EKG2:", tb?tb:IRCVERSION":", un.sysname, un.release, un.machine); break; case CTCP_SOURCE: /* ===== ===== ===== ===== ===== SOURCE */ print_window(win, s, EKG_WINACT_MSG, ischn?(mw&1):!!(mw&4), ischn?"irc_ctcp_request_pub":"irc_ctcp_request", session_name(s), purename, idhost, cchname, ctcp); irc_write(s, "NOTICE %s :\01SOURCE \02\x1fhttp://ekg2.org/ekg2-current.tar.gz\x1f\02\01\r\n", purename); break; case CTCP_USERINFO: /* ===== ===== ===== ===== ===== USERINFO */ ta = (char *)session_get(s, "USERINFO"); print_window(win, s, EKG_WINACT_MSG, ischn?(mw&1):!!(mw&4), ischn?"irc_ctcp_request_pub":"irc_ctcp_request", session_name(s), purename, idhost, cchname, ctcp); irc_write(s, "NOTICE %s :\01USERINFO :%s\01\r\n", purename, ta?ta:"no userinfo set"); break; case CTCP_CLIENTINFO: /* ===== ===== ===== ===== ===== CLIENTINFO */ print_window(win, s, EKG_WINACT_MSG, ischn?(mw&1):!!(mw&4), ischn?"irc_ctcp_request_pub":"irc_ctcp_request", session_name(s), purename, idhost, cchname, ctcp); ta = xmalloc(sizeof(ctcps)); for (i=0; ctcps[i].name; i++) { if (ctcps[i].handled) { xstrcat(ta, ctcps[i].name); xstrcat(ta, " "); } } irc_write(s, "NOTICE %s :\01CLIENTINFO %s\01\r\n", purename, ta); xfree(ta); break; case CTCP_PING: /* ===== ===== ===== ===== ===== PING */ print_window(win, s, EKG_WINACT_MSG, ischn?(mw&1):!!(mw&4), ischn?"irc_ctcp_request_pub":"irc_ctcp_request", session_name(s), purename, idhost, cchname, ctcp); irc_write(s, "NOTICE %s :\01PING %s\01\r\n", purename, space?space:""); break; case CTCP_TIME: /* ===== ===== ===== ===== ===== TIME */ print_window(win, s, EKG_WINACT_MSG, ischn?(mw&1):!!(mw&4), ischn?"irc_ctcp_request_pub":"irc_ctcp_request", session_name(s), purename, idhost, cchname, ctcp); timek = time(NULL); ta = xstrdup(ctime(&timek)); if (ta[xstrlen(ta)-1] == '\n') ta[xstrlen(ta)-1]='\0'; irc_write(s, "NOTICE %s :\01TIME %s\01\r\n", purename, ta); xfree(ta); break; case CTCP_ERRMSG: /* ===== ===== ===== ===== ===== ERRMSG */ print_window(win, s, EKG_WINACT_MSG, ischn?(mw&1):!!(mw&4), ischn?"irc_ctcp_request_pub":"irc_ctcp_request", session_name(s), purename, idhost, cchname, ctcp); irc_write(s, "NOTICE %s :\01ERRMSG %s\01\r\n", purename, space?space:""); break; } /* switch(number) */ xfree(cchname); return (0); } CTCP_COMMAND(ctcp_main_noti) { char *ischn = xstrchr(SOP(_005_CHANTYPES), targ[4]); char *space = xstrchr(ctcp, ' '); int mw = session_int_get(s, "make_window"); char *t, *win; window_t *w; if (space) while ((*space) && (*space == ' ')) space++; win = ischn?targ:sender; w = window_find_s(s, win); if (!ischn && !w && !(mw&4)) win = window_current->target; t = irc_ircoldcolstr_to_ekgcolstr(s, space,1); /* if number == CTCP_PING, we could display * differences between current gettimeofday() && recv * like most irc clients do. */ print_window(win, s, EKG_WINACT_MSG, ischn?(mw&1):!!(mw&8), "irc_ctcp_reply", session_name(s), ctcps[number-1].name, sender+4, idhost, t); xfree (t); return (0); } /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/plugins/irc/input.h000066400000000000000000000036661175142753400177360ustar00rootroot00000000000000/* * (C) Copyright 2004-2005 Michal 'GiM' Spadlinski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __EKG_PLUGINS_IRC_INPUT_H #define __EKG_PLUGINS_IRC_INPUT_H #include "irc.h" char *irc_ircoldcolstr_juststrip(session_t *sess, char *inp); char *irc_ircoldcolstr_to_ekgcolstr(session_t *s, char *str, int strip); char *irc_ircoldcolstr_to_ekgcolstr_nf(session_t *sess, char *str, int strip); char *ctcp_parser(session_t *sess, int ispriv, char *sender, char *recp, char *s, int to_us); #define CTCP_COMMAND(x) static int x(session_t *s, irc_private_t *j, int number, \ char *ctcp, char *sender, char*idhost, char *targ, int to_us) typedef int (*CTCP_Cmd) (session_t *s, irc_private_t *j, int number, char *ctcp, char *sender, char *idhost, char *targ); typedef struct { char *name; int handled; } ctcp_t; enum { CTCP_ACTION=1, CTCP_DCC, CTCP_SED, CTCP_FINGER, CTCP_VERSION, CTCP_SOURCE, CTCP_USERINFO, CTCP_CLIENTINFO, CTCP_PING, CTCP_TIME, CTCP_ERRMSG }; static const ctcp_t ctcps[] = { { "ACTION", 1 }, { "DCC", 0 }, { "SED", 0 }, { "FINGER", 1 }, { "VERSION", 1 }, { "SOURCE", 1 }, { "USERINFO", 1 }, { "CLIENTINFO", 1 }, { "PING", 1 }, { "TIME", 1 }, { "ERRMSG", 1 }, { NULL, 0 } }; #endif /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/plugins/irc/irc.c000066400000000000000000002142221175142753400173370ustar00rootroot00000000000000/* * (C) Copyright 2004-2005 Michal 'GiM' Spadlinski * Jakub 'darkjames' Zawadzki * Wiesaw Ochmiski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #include #include #include #include #include #include #ifndef NO_POSIX_SYSTEM #include #include #include #endif #include #ifndef NO_POSIX_SYSTEM #include #endif #include #ifndef NO_POSIX_SYSTEM #include #endif #include #ifndef NO_POSIX_SYSTEM #include #include #endif #ifdef __sun /* Solaris, thanks to Beeth */ #include #endif #include #include "irc.h" #include "people.h" #include "input.h" #include "autoacts.h" #include "IRCVERSION.h" #define DEFPORT 6667 #define DEFPARTMSG "EKG2 bejbi! http://ekg2.org/" #define DEFQUITMSG "EKG2 - It's better than sex!" #define DEFKICKMSG "EKG2 - Y0U 57iNK2 50 MUCH!" #define SGPARTMSG(x) session_get(x, "PART_MSG") #define SGQUITMSG(x) session_get(x, "QUIT_MSG") #define SGKICKMSG(x) session_get(x, "KICK_MSG") #define PARTMSG(x,r) (r?r: SGPARTMSG(x)?SGPARTMSG(x):DEFPARTMSG) #define QUITMSG(x) (SGQUITMSG(x)?SGQUITMSG(x):DEFQUITMSG) #define KICKMSG(x,r) (r?r: SGKICKMSG(x)?SGKICKMSG(x):DEFKICKMSG) /* ************************ KNOWN BUGS *********************** * OTHER LESS IMPORTANT BUGS * -> somewhere with altnick sending * G->dj: still not as I would like it to be * !BUGS (?) TODO->check * -> buggy auto_find. if smb type smth on the channel. * * 10:58:27 ::: Nieprawidowe parametry. Sprobuj help find * ************************************************************* */ /* *************************** TODO ************************** * * -> split mode. * -> disconnection detection * why not just sending simple PING to SERVER and deal with it's * pong reply... * PING konstantynopolitanczykiewikowna lublin.irc.pl * :prefix PONG lublin.irc.pl konstantynopolitanczykiewikowna * ************************************************************* */ /* * IDEAS: * -> maybe some params in /connect command ? * for example if we have in /session server krakow.irc.pl,poznan.irc.pl * and we want connect to poznan.irc.pl (150.254.64.64) nor krakow... we type * /connect poznan.irc.pl * /connect #2 * /connect 150.254.64.64 * next, if we want to connect instead of default 6667 port to i.e 6665 we type * /connect :6665 * or we can use both. * /connect poznan.irc.pl:6665 || /connect 150.254.64.64 6665. */ /* * * ======================================== STARTUP AND STANDARD FUNCS - * * */ static int irc_theme_init(); static COMMAND(irc_command_disconnect); static int irc_really_connect(session_t *session, gboolean quiet); static char *irc_getchan_int(session_t *s, const char *name, int checkchan); static char *irc_getchan(session_t *s, const char **params, const char *name, char ***v, int pr, int checkchan); static char *irc_config_default_access_groups; int irc_config_allow_fake_contacts = 0; int irc_config_clean_channel_name; const gchar fillchars[] = "\xC2\xA0"; const gint fillchars_len = 2; PLUGIN_DEFINE(irc, PLUGIN_PROTOCOL, irc_theme_init); /** * irc_session_init() * * Handler for: SESSION_ADDED
* Init private session struct irc_private_t if @a session is irc one.
* Read saved userlist with userlist_read() * * @param ap 1st param: (char *) session - uid of session * @param data NULL * * @return 0 if @a session is irc one, and we init memory
* 1 if we don't found such session, or it wasn't irc session [most probable], or we already init memory. */ static QUERY(irc_session_init) { char *session = *(va_arg(ap, char**)); session_t *s = session_find(session); irc_private_t *j; if (!s || s->priv || (s->plugin != &irc_plugin)) return 1; userlist_read(s); ekg_recode_utf8_inc(); j = xmalloc(sizeof(irc_private_t)); j->nick_modes = j->nick_signs = NULL; j->conv = NULL; s->priv = j; return 0; } static LIST_FREE_ITEM(list_irc_awaylog_free, irc_awaylog_t *) { xfree(data->channame); xfree(data->uid); xfree(data->msg); xfree(data); } /** * irc_session_deinit() * * Handler for: SESSION_REMOVED
* Free memory allocated by irc_private_t if @a session is irc one.
* Save userlist with userlist_write() * * @param ap 1st param: (char *) session - uid of session * @param data NULL * * @todo Check if userlist_write() here is good. * @todo * * @return 0 if @a session is irc one, and memory allocated where xfree()'d.
* 1 if not such session, or it wasn't irc session [most probable], or we already free memory. */ static QUERY(irc_session_deinit) { char *session = *(va_arg(ap, char**)); session_t *s = session_find(session); irc_private_t *j; int i; if (!s || !(j = s->priv) || (s->plugin != &irc_plugin)) return 1; userlist_write(s); config_commit(); s->priv = NULL; xfree(j->host_ident); xfree(j->nick); g_free(j->conv); g_strfreev(j->auto_guess_encoding); /* XXX, hilights list_t */ /* NOTE: j->awaylog shouldn't exists here */ LIST_DESTROY(j->awaylog, list_irc_awaylog_free); irc_free_people(s, j); for (i = 0; isopt[i]); xfree(j->nick_modes); xfree(j); return 0; } static char *irc_make_banmask(session_t *session, const char *nick, const char *ident, const char *hostname) { /* * 1 (Nick) - nick!*@* * 2 (User) - *!*ident@* * 4 (Host) - *!*@host.* * 4 (IP) - *!*@*.168.11.11 - buggy, it bans @*.11 * 8 (Domain) - *!*@*.domain.net * 8 (IP) - *!*@192.168.11.* */ char *host = xstrdup(hostname); const char *tmp[4]; char *temp = NULL; int family = 0; char ind = '.'; int bantype = session_int_get(session, "ban_type"); #ifdef HAVE_INET_PTON char buf[33]; if (xstrchr(host, ':')) { /* to protect againt iwil var in ircd.conf (ircd-hybrid) * dot_in_ip6_addr = yes; */ if (host[xstrlen(host)-1] == '.') host[xstrlen(host)-1] = 0; if (inet_pton(AF_INET6, host, &buf) > 0) { family = AF_INET6; ind = ':'; } } else if (inet_pton(AF_INET, host, &buf) > 0) family = AF_INET; #else /* TODO */ print("generic_error", "It seems you don't have inet_pton(). The current version of irc_make_banmask won't work without this function. If you want to get it work faster, contact developers ;>"); #endif if (host && !family && (temp=xstrchr(host, ind))) *temp = '\0'; if (host && family && (temp=xstrrchr(host, ind))) *temp = '\0'; if (bantype > 15) bantype = 10; memset(tmp, 0, sizeof(tmp)); #define getit(x) tmp[x]?tmp[x]:"*" if (bantype & 1) tmp[0] = nick; if (bantype & 2 && (ident[0] != '~' || session_int_get(session, "dont_ban_user_on_noident") == 0 )) tmp[1] = ident; if (family) { if (bantype & 8) tmp[2] = host; if (bantype & 4) tmp[3] = hostname ? temp?temp+1:NULL : NULL; } else { if (bantype & 4) tmp[2] = host; if (bantype & 8) tmp[3] = hostname ? temp?temp+1:NULL : NULL; } /* temp = saprintf("%s!*%s@%s%c%s", getit(0), getit(1), getit(2), ind, getit(3)); */ temp = saprintf("%s!%s@%s%c%s", getit(0), getit(1), getit(2), ind, getit(3)); xfree(host); return temp; #undef getit } /** * irc_print_version() * * handler for PLUGIN_PRINT_VERSION query requests
* print info about this plugin [Copyright note+version] * * @note what the heck this can be ? ;) (c GiM) * * @return 0 */ static QUERY(irc_print_version) { print("generic", "IRC plugin by Michal 'GiM' Spadlinski, Jakub 'darkjames' Zawadzki v. "IRCVERSION); return 0; } static QUERY(irc_setvar_default) { xfree(irc_config_default_access_groups); irc_config_default_access_groups = xstrdup("__ison"); return 0; } /** * irc_validate_uid() * * handler for PROTOCOL_VALIDATE_UID
* checks, if @a uid is proper for irc plugin. * * @note Proper for irc plugin means if @a uid starts with "irc:" and uid len > 4 * * @param ap 1st param: (char *) uid - of user/session/command/whatever * @param ap 2nd param: (int) valid - place to put 1 if uid is valid for irc plugin. * @param data NULL * * @return -1 if it's valid uid for irc plugin
* 0 if not */ static QUERY(irc_validate_uid) { char *uid = *(va_arg(ap, char **)); int *valid = va_arg(ap, int *); if (!uid) return 0; if (!xstrncasecmp(uid, IRC4, 4) && uid[4]) { (*valid)++; return -1; /* if it's correct uid for irc we don't need to send to others... */ } return 0; } out_recodes_t *irc_find_out_recode(list_t rl, char *encname) { out_recodes_t *recode; if (!(encname && rl)) return NULL; for ( ; rl; rl = rl->next) { recode = (out_recodes_t *)(rl->data); if ( recode->name && !xstrcasecmp(recode->name, encname) ) return recode; } return NULL; } static char *irc_convert_out(irc_private_t *j, char *recipient, const char *line) { if ((j->recoded_channels)) { /* channel/nick recode */ char *channame = (!xstrncasecmp(recipient, IRC4, 4)) ? recipient+4 : recipient; gchar *enc = g_datalist_get_data(&j->recoded_channels, channame); if (enc) return ekg_recode_to(enc, line); } /* default recode */ return j->conv ? ekg_recode_to(j->conv, line) : xstrdup(line); } static void irc_changed_recode_list(session_t *s, const char *var) { const char *val; irc_private_t *j; char **list1, **list2, *nicks, *encoding; int i,i2; g_assert(s); if (!(j = s->priv)) return; g_datalist_clear(&j->recoded_channels); if (!(val = session_get(s, var)) || !*val) return; /* Parse list */ // Syntax: encoding1:nick1,nick2,#chan1,nick3;encoding2:nick4,#chan5,chan6 list1 = array_make(val, ";", 0, 1, 0); for (i=0; list1[i]; i++) { if (!(nicks = xstrchr(list1[i], ':'))) { debug_error("[irc] recode_list parse error: no colon. Skipped. '%s'\n", list1[i]); continue; } *nicks++ = '\0'; encoding = list1[i]; if (!*nicks) { debug_error("[irc] recode_list parse error: no nick or channel. Skipped. '%s:'\n", encoding); continue; } if (!*encoding) { debug_error("[irc] recode_list parse error: no encoding name. Skipped. ':%s'\n", nicks); continue; } list2 = array_make(nicks, ",", 0, 1, 0); for(i2=0; list2[i2]; i2++) { if (g_datalist_get_data(&j->recoded_channels, list2[i2])) { debug_error("[irc] recode_list. Duplicated channel/nick '%s'. Skipped.'\n", list2[i2]); continue; } g_datalist_set_data_full(&j->recoded_channels, list2[i2], g_strdup(encoding), g_free); } g_strfreev(list2); } g_strfreev(list1); } static void irc_changed_recode(session_t *s, const char *var) { const char *val; irc_private_t *j; if (!s || !(j = s->priv)) return; g_free(j->conv); if (!(val = session_get(s, var)) || !*val) { j->conv = NULL; return; } j->conv = g_strdup(val); } static void irc_changed_auto_guess_encoding(session_t *s, const char *var) { const char *val; irc_private_t *j; g_assert(s); j = s->priv; if (!j) return; if (j->auto_guess_encoding) { g_strfreev(j->auto_guess_encoding); j->auto_guess_encoding = NULL; } if (!(val = session_get(s, var)) || !*val) return; j->auto_guess_encoding = array_make(val, ",", 0, 1, 0); } /* * * ======================================== HANDLERS ------------------- * * */ void irc_handle_disconnect(session_t *s, const char *reason, int type) { /* * EKG_DISCONNECT_NETWORK @ irc_handle_stream type == 1 * EKG_DISCONNECT_FAILURE @ irc_handle_connect when we got timeouted or connection refused or other.. * EKG_DISCONNECT_FAILURE @ irc_c_error(misc.c) when we recv ERROR message @ connecting * EKG_DISCONNECT_FAILURE @ irc_command_connect when smth goes wrong. * EKG_DISCONNECT_STOPPED @ irc_command_disconnect when we do /disconnect when we are not connected and we try to connect. * EKG_DISCONNECT_USER @ irc_command_disconnect when we do /disconnect when we are connected. */ irc_private_t *j = irc_private(s); char *__reason; g_assert(j); j->disconnecting = FALSE; irc_free_people(s, j); switch (type) { case EKG_DISCONNECT_FAILURE: break; case EKG_DISCONNECT_STOPPED: /* this is just in case somebody would add some servers that have given * port disabled so answer comes very fast and plug would think * he is 'disconnected' while in fact he would try many times to * reconnect to given host */ j->autoreconnecting = 0; /* if ,,reconnect'' timer exists we should stop doing */ if (timer_remove_session(s, "reconnect") == 0) print("auto_reconnect_removed", session_name(s)); break; /* * default: * debug("[irc_handle_disconnect] unknow || !handled type = %d %s", type, reason); */ } __reason = xstrdup(format_find(reason)); if (!xstrcmp(__reason, "")) { xfree(__reason); __reason = xstrdup(reason); } protocol_disconnected_emit(s, __reason, type); xfree(__reason); } static void irc_handle_line(GDataInputStream *input, gpointer data) { session_t *s = data; gchar *l; const char *buf; const char *le = "\n"; gsize count; gboolean found; do { /* repeat till user grabs all lines */ buf = g_buffered_input_stream_peek_buffer(G_BUFFERED_INPUT_STREAM(input), &count); found = !!g_strstr_len(buf, count, le); if (found) { l = g_data_input_stream_read_line(input, NULL, NULL, NULL); if (l) { irc_parse_line(s, l, -1); g_free(l); } } } while (found); } static void irc_handle_failure(GDataInputStream *f, GError *err, gpointer data) { session_t *s = data; irc_private_t *j = irc_private(s); j->send_stream = NULL; /* XXX: needed? */ if (j->disconnecting && g_error_matches(err, EKG_CONNECTION_ERROR, EKG_CONNECTION_ERROR_EOF)) irc_handle_disconnect(s, NULL, EKG_DISCONNECT_USER); else irc_handle_disconnect(s, err->message, EKG_DISCONNECT_NETWORK); } static void irc_handle_connect( GSocketConnection *conn, GInputStream *instream, GOutputStream *outstream, gpointer data) { session_t *s = data; irc_private_t *j = irc_private(s); j->send_stream = ekg_connection_add( conn, instream, outstream, irc_handle_line, irc_handle_failure, s); { const gchar *real = session_get(s, "realname"); const gchar *mode = session_get(s, "usermode"); const gchar *pass = session_password_get(s); /* XXX: we used to strip_spaces() here?! */ /* XXX: check space in j->nick and mode */ if (pass && *pass) ekg_fprintf(G_OUTPUT_STREAM(j->send_stream), "PASS %s\r\n", pass); ekg_connection_write(j->send_stream, "USER %s %s unused_field :%s\r\n" "NICK %s\r\n", j->nick, (mode && *mode) ? mode : EKG_IRC_DEFAULT_USERMODE, (real && *real) ? real : j->nick, j->nick); } } static void irc_handle_connect_failure(GError *err, gpointer data) { session_t *s = data; irc_handle_disconnect(s, err->message, EKG_DISCONNECT_FAILURE); } /* * * ======================================== COMMANDS ------------------- * * */ static int irc_really_connect(session_t *session, gboolean quiet) { irc_private_t *j = irc_private(session); GSocketClient *s; const int defport = session_int_get(session, "port"); const gchar *bindhost = session_get(session, "hostname"); ekg_connection_starter_t cs; session->connecting = 1; j->autoreconnecting = 1; /* XXX? */ printq("connecting", session_name(session)); cs = ekg_connection_starter_new(defport > 0 ? defport : DEFPORT); ekg_connection_starter_set_servers(cs, session_get(session, "server")); ekg_connection_starter_set_use_tls(cs, !!session_int_get(session, "use_tls")); if (bindhost) ekg_connection_starter_bind(cs, bindhost); s = g_socket_client_new(); ekg_connection_starter_run(cs, s, irc_handle_connect, irc_handle_connect_failure, session); if (session_status_get(session) == EKG_STATUS_NA) session_status_set(session, EKG_STATUS_AVAIL); /* XXX: timeout */ return -1; } static COMMAND(irc_command_connect) { irc_private_t *j = irc_private(session); const char *newnick; if (!session_get(session, "server")) { printq("generic_error", "gdzie lecimy ziom ?! [/session server]"); return -1; } if (session->connecting) { printq("during_connect", session_name(session)); return -1; } if (session_connected_get(session)) { printq("already_connected", session_name(session)); return -1; } if ((newnick = session_get(session, "nickname"))) { xfree(j->nick); j->nick = xstrdup(newnick); } else { printq("generic_error", "gdzie lecimy ziom ?! [/session nickname]"); return -1; } return irc_really_connect(session, quiet); } static COMMAND(irc_command_disconnect) { irc_private_t *j = irc_private(session); const char *reason = params[0]?params[0]:QUITMSG(session); debug_function("[irc] comm_disconnect() !!!\n"); if (!session->connecting && !session_connected_get(session) && !j->autoreconnecting) { printq("not_connected", session_name(session)); return -1; } j->disconnecting = TRUE; if (reason && session_connected_get(session)) ekg_connection_write(j->send_stream, "QUIT :%s\r\n", reason); if (session->connecting) { g_cancellable_cancel(j->connect_cancellable); /* XXX: how about the 'connection processing' part? */ } #if 0 if (session->connecting || j->autoreconnecting) irc_handle_disconnect(session, reason, EKG_DISCONNECT_STOPPED); else irc_handle_disconnect(session, reason, EKG_DISCONNECT_USER); #endif return 0; } static COMMAND(irc_command_reconnect) { if (session->connecting || session_connected_get(session)) irc_command_disconnect(name, params, session, target, quiet); return irc_command_connect(name, params, session, target, quiet); } /*****************************************************************************/ static COMMAND(irc_command_msg) { irc_private_t *j = irc_private(session); people_chan_t *perchn = NULL; people_t *person; window_t *w; int prv = 0; /* NOTICE | PRIVMSG */ char *mline[2]; char *tmpbuf; int ischn; /* IS CHANNEL ? */ char prefix[2] = {' ', '\0'}; char *head, *coloured; const char *frname; /* formatname */ int secure = 0; char **rcpts; char *uid; if (!xstrncmp(target, IRC4, 4)) { uid = xstrdup(target); } else { uid = irc_uid(target); } w = window_find_s(session, uid); prv = xstrcmp(name, ("notice")); ischn = !!xstrchr(SOP(_005_CHANTYPES), uid[4]); /* PREFIX */ /* ok new irc-find-person checked */ if ((ischn && (person = irc_find_person(j, j->people, j->nick)) && (perchn = irc_find_person_chan(person->channels, (char *)uid)))) prefix[0] = *(perchn->sign); if (!ischn || (!session_int_get(session, "SHOW_NICKMODE_EMPTY") && *prefix==' ')) *prefix='\0'; frname = format_find(prv? ischn?"irc_msg_sent_chan":w?"irc_msg_sent_n":"irc_msg_sent": ischn?"irc_not_sent_chan":w?"irc_not_sent_n":"irc_not_sent"); rcpts = xmalloc(sizeof(char *) * 2); rcpts[0] = xstrdup(!!w?w->target:uid); rcpts[1] = NULL; debug ("%s - %s\n", uid, rcpts[0]); tmpbuf = (mline[0] = xstrdup(params[1])); while ((mline[1] = split_line(&(mline[0])))) { char *line, *recoded; char *__mtmp, *padding = NULL; int len_limit, msg_len; line = xstrdup(mline[1]); { /* one-shot variables */ int __isour = 1; int __to_us = 0; int __priv = !ischn; query_emit(NULL, "irc-protocol-message", &(session->uid), &(j->nick), &line, &__isour, &__to_us, &__priv, &uid); } if (perchn) padding = nickpad_string_apply(perchn->chanp, j->nick); head = format_string(frname, session_name(session), prefix, j->nick, j->nick, uid+4, line, padding); if (perchn) nickpad_string_restore(perchn->chanp); recoded = irc_convert_out(j, uid, line); coloured = irc_ircoldcolstr_to_ekgcolstr(session, head, 1); query_emit(NULL, "message-encrypt", &(session->uid), &uid, &recoded, &secure); protocol_message_emit(session, session->uid, rcpts, coloured, NULL, time(NULL), (EKG_MSGCLASS_SENT | EKG_NO_THEMEBIT), NULL, EKG_NO_BEEP, secure); debug ("%s ! %s\n", j->nick, j->host_ident); /* "Thus, there are 510 characters maximum allowed for the command and its parameters." [rfc2812] * yes, I know it's a nasty variable reusing ;) */ len_limit = 510 - (prv?7:6) - 6 - xstrlen(uid+4) - xstrlen(j->host_ident) - xstrlen(j->nick); /* 6 = 3xspace + '!' + 2xsemicolon; -> [:nick!ident@hostident PRIVMSG dest :mesg] */ msg_len = xstrlen(recoded); __mtmp = recoded; while ( msg_len > len_limit ) { char saved = __mtmp[len_limit]; __mtmp[len_limit] = '\0'; /* XXX danger: cut unicode chars */ ekg_connection_write(j->send_stream, "%s %s :%s\r\n", (prv) ? "PRIVMSG" : "NOTICE", uid+4, __mtmp); __mtmp[len_limit] = saved; __mtmp += len_limit; msg_len -= len_limit; } ekg_connection_write(j->send_stream, "%s %s :%s\r\n", (prv) ? "PRIVMSG" : "NOTICE", uid+4, __mtmp); xfree(line); xfree(recoded); xfree(coloured); xfree(head); } xfree(rcpts[0]); xfree(rcpts); xfree(uid); xfree(tmpbuf); if (!quiet) session_unidle(session); return 0; } static COMMAND(irc_command_inline_msg) { const char *p[2] = { NULL, params[0] }; if (!target || !params[0]) return -1; return irc_command_msg(("msg"), p, session, target, quiet); } static COMMAND(irc_command_quote) { ekg_connection_write(irc_private(session)->send_stream, "%s\r\n", params[0]); return 0; } static COMMAND(irc_command_pipl) { irc_private_t *j = irc_private(session); list_t t1, t2; people_t *per; people_chan_t *chan; debug_white("[irc] this is a secret command ;-)\n"); for (t1 = j->people; t1; t1=t1->next) { per = (people_t *)t1->data; debug("(%s)![%s]@{%s} ", per->nick, per->ident, per->host); for (t2 = per->channels; t2; t2=t2->next) { chan = (people_chan_t *)t2->data; debug ("%s:%d, ", chan->chanp->name, chan->mode); } debug("\n"); } return 0; } static COMMAND(irc_command_alist) { irc_private_t *j = irc_private(session); int isshow = 0; #if 0 for (l = j->people; l; l = l->next) { people_t *per = l->data; printq("irc_access_known", session_name(session), per->nick+4, per->ident, per->host); } #endif if (!params[0] || match_arg(params[0], 'l', "list", 2) || (isshow = match_arg(params[0], 's', "show", 2))) { /* list, list, show */ #if 0 list_t l; for (l = session->userlist; l; l = l->next) { userlist_t *u = l->data; /* u->resources; */ } #endif return 0; } if (match_arg(params[0], 'a', "add", 2)) { /* params[1] - nick!ident@hostname or nickname (in form irc:nick) * params[2] - options + channels like: +ison +autoop:#linux,#linux.pl +autounban:* +autovoice:* * ZZZ params[2] - channels: #chan1,#chan2 or '*' * ZZZ params[3] - options. */ /* /irc:access -a irc:nickname #chan1,#chan2 +autoop +autounban +revenge +ison */ char *mask = NULL; const char *channel; char *groupstr; userlist_t *u; if (!params[1] || !(channel = params[2])) { printq("not_enough_params", name); return -1; } if (!xstrncmp(params[1], "irc:", 4)) { /* nickname */ list_t l; for (l = j->people; l; l = l->next) { people_t *per = l->data; if (!xstrcmp(per->nick+4, params[1]+4)) { /* XXX, here generate mask */ mask = saprintf("%s!%s@%s", per->nick+4, per->ident, per->host); break; } } if (!mask) { printq("user_not_found", params[1]); return -1; } } else { /* XXX verify if mask in params[1] is ok */ mask = xstrdup(params[1]); } { char *tmp = saprintf("irc:%s:%s", mask, channel); u = userlist_add(session, tmp, params[1]); if (params[3]) { char **arr = array_make(params[3], " ", 0, 1, 1); int i; for (i=0; arr[i]; i++) { const char *value = arr[i]; if (arr[i][0] == '+') value++; if (!xstrcmp(value, "autoop")) ekg_group_add(u, "__autoop"); /* +o */ else if (!xstrcmp(value, "autovoice")) ekg_group_add(u, "__autovoice"); /* +v */ else if (!xstrcmp(value, "autounban")) ekg_group_add(u, "__autounban"); /* -b */ else if (!xstrcmp(value, "autoban")) ekg_group_add(u, "__autoban"); /* +b */ else if (!xstrcmp(value, "autodevop")) ekg_group_add(u, "__autodevop"); /* -o, -h, -v */ else if (!xstrcmp(value, "revenge")) ekg_group_add(u, "__revenge"); /* + */ else if (!xstrcmp(value, "ison")) ekg_group_add(u, "__ison"); /* + */ else printq("irc_access_invalid_flag", value); } g_strfreev(arr); } else u->groups = group_init(irc_config_default_access_groups); xfree(tmp); } groupstr = group_to_string(u->groups, 1, 1); printq("irc_access_added", session_name(session), "0" /* XXX # */, mask, channel, groupstr); xfree(groupstr); xfree(mask); /* XXX sync if wanted */ return 0; } if (match_arg(params[0], 'd', "delete", 2)) { printq("generic_error", "stub function use /del"); return -1; } if (match_arg(params[0], 'e', "edit", 2)) { printq("generic_error", "stub function"); return -1; } if (match_arg(params[0], 'S', "sync", 2)) { printq("generic_error", "stub function"); return -1; } printq("invalid_params", name, params[0]); return -1; } static COMMAND(irc_command_add) { printq("generic_error", "/irc:add do nothing. if you want friendlists use /irc:access"); /* XXX, wrapper do /irc:access --add $UID +ison ? */ return -1; } static void irc_display_awaylog(session_t *session) { irc_private_t *j = irc_private(session); if (j->awaylog) { list_t l; const char *awaylog_timestampf = format_find("irc_awaylog_timestamp"); print_status("irc_awaylog_begin", session_name(session)); for (l = j->awaylog; l; l = l->next) { irc_awaylog_t *e = l->data; if (e->channame) print_status("irc_awaylog_msg_chan", session_name(session), timestamp_time(awaylog_timestampf, e->t), (e->channame)+4, (e->uid)+4, e->msg); else print_status("irc_awaylog_msg", session_name(session), timestamp_time(awaylog_timestampf, e->t), "", (e->uid)+4, e->msg); xfree(e->channame); xfree(e->uid); xfree(e->msg); } print_status("irc_awaylog_end", session_name(session)); list_destroy(j->awaylog, 1); j->awaylog = NULL; } } static COMMAND(irc_command_away) { irc_private_t *j = irc_private(session); int isaway = 0; if (!xstrcmp(name, ("back"))) { session_descr_set(session, NULL); session_status_set(session, EKG_STATUS_AVAIL); session_unidle(session); } else if (!xstrcmp(name, ("away"))) { session_descr_set(session, params[0]); session_status_set(session, EKG_STATUS_AWAY); session_unidle(session); isaway = 1; } else if (!xstrcmp(name, ("_autoaway"))) { session_status_set(session, EKG_STATUS_AUTOAWAY); isaway = 1; } else if (!xstrcmp(name, ("_autoback"))) { session_status_set(session, EKG_STATUS_AUTOBACK); session_unidle(session); } else { printq("generic_error", "Ale o so chozi?"); return -1; } if (isaway) { const char *status = ekg_status_string(session_status_get(session), 0); const char *descr = session_descr_get(session); if (descr) ekg_connection_write(j->send_stream, "AWAY :%s\r\n", descr); else ekg_connection_write(j->send_stream, "AWAY :%s\r\n", status); } else { ekg_connection_write(j->send_stream, "AWAY :\r\n"); /* @ back, display awaylog. */ irc_display_awaylog(session); } return 0; } static void irc_statusdescr_handler(session_t *s, const char *varname) { irc_private_t *j = irc_private(s); const status_t status = session_status_get(s); if (status == EKG_STATUS_AWAY) { const char *descr = session_descr_get(s); if (descr) ekg_connection_write(j->send_stream, "AWAY :%s\r\n", descr); else ekg_connection_write(j->send_stream, "AWAY :%s\r\n", ekg_status_string(status, 0)); } else { ekg_connection_write(j->send_stream, "AWAY :\r\n"); /* @ back, display awaylog. */ irc_display_awaylog(s); } } /*****************************************************************************/ /** * irc_window_kill() * * handler for UI_WINDOW_KILL query requests * It checks if window which will be destroyed is valid irc channel window, and we * are on it now. if yes (and of course if we are connected) it send PART message to irc * server, with reason got using @a PARTMSG macro * * @param ap 1st param: (window_t *) w - window which will be destroyed * @param data NULL * * @return 0 */ static QUERY(irc_window_kill) { window_t *w = *va_arg(ap, window_t **); irc_private_t *j; char *tmp; if (w && w->id && w->target && w->session && w->session->plugin == &irc_plugin && (j = irc_private(w->session)) && (tmp = SOP(_005_CHANTYPES)) && xstrchr(tmp, (w->target)[4]) && irc_find_channel((j->channels), (w->target)) && session_connected_get(w->session) ) { ekg_connection_write(j->send_stream, "PART %s :%s\r\n", (w->target)+4, PARTMSG(w->session, NULL)); } return 0; } /** * irc_topic_header() * * handler for IRC_TOPIC query requests * @param ap 1st param: (char *) top - place to put:
* -> topic of current irc channel (if current window is valid irc channel)
* -> ident\@host of current irc user (if current window is known user) * @param ap 2nd param: (char *) setby - place to put:
* -> topic owner of current irc channel
* -> realname of current irc user * @param ap 3rd param: (char *) modes - place to put:
* modes of current irc channel or undefined if not channel * @param data 0 * * @return 1 if it's known irc channel.
* 2 if it's known irc user.
* 0 if it's neither known user, nor channel.
* -3 if it's not valid irc window, or session is not connected */ static QUERY(irc_topic_header) { char **top = va_arg(ap, char **); char **setby = va_arg(ap, char **); char **modes = va_arg(ap, char **); session_t *sess = window_current->session; char *targ = window_current->target; if (targ && sess && sess->plugin == &irc_plugin && sess->priv && sess->connected) { irc_private_t *j = sess->priv; char *tmp; channel_t *chanp; people_t *per; /* channel */ if ((tmp = SOP(_005_CHANTYPES)) && xstrchr(tmp, targ[4]) && (chanp = irc_find_channel((j->channels), targ))) { *top = irc_ircoldcolstr_to_ekgcolstr_nf(sess, chanp->topic, 1); *setby = xstrdup(chanp->topicby); *modes = xstrdup(chanp->mode_str); return 1; } /* person */ /* ok new irc-find-person checked */ if ((per = irc_find_person(j, j->people, targ+4))) { *top = saprintf("%s@%s", per->ident, per->host); *setby = xstrdup(per->realname); *modes = NULL; return 2; } *top = *setby = *modes = NULL; return 0; } return -3; } static char *irc_getchan_int(session_t *s, const char *name, int checkchan) { char *ret, *tmp; irc_private_t *j = irc_private(s); if (!xstrlen(name)) return NULL; if (!xstrncasecmp(name, IRC4, 4)) ret = xstrdup(name); else ret = irc_uid(name); if (checkchan == 2) return ret; tmp = SOP(_005_CHANTYPES); if (tmp && ((!!xstrchr(tmp, ret[4]))-checkchan) ) return ret; else xfree(ret); return NULL; } /* pr - specifies priority of check * 0 - first param, than current window * 1 - reverse * as side effect it adjust table passed as v argument */ static char *irc_getchan(session_t *s, const char **params, const char *name, char ***v, int pr, int checkchan) { char *chan, *tmpname; const char *tf, *ts, *tp; /* first, second */ int i = 0, parnum = 0, argnum = 0, hasq = 0; GSList *cl; if (params) tf = params[0]; else tf = NULL; ts = window_current->target; if (pr) { tp=tf; tf=ts; ts=tp; } if (!(chan = irc_getchan_int(s, tf, checkchan))) { if (!(chan = irc_getchan_int(s, ts, checkchan))) { print("invalid_params", name, ts); return 0; } pr = !!pr; } else { pr = !!!pr; } tmpname = irc_uid(name); for (cl = commands; cl; cl = cl->next) { command_t *c = cl->data; if (&irc_plugin == c->plugin && !xstrcmp(tmpname, c->name)) { while (c->params[parnum]) { if (!hasq && !xstrcmp(c->params[parnum], ("?"))) hasq = 1; parnum++; } break; } } xfree(tmpname); do { if (params) while (params[argnum]) argnum++; (*v) = (char **)xcalloc(parnum+1, sizeof (char *)); debug("argnum %d parnum %d pr %d hasq %d\n", argnum, parnum, pr, hasq); if (!pr) { if (!hasq) { for (i=0; i %s\n", (*v)[i++]); debug("\n"); */ } while (0); return chan; } /*****************************************************************************/ static COMMAND(irc_command_names) { irc_private_t *j = irc_private(session); /* 0 - op 1 - halfop 2 - voice 3 - owner 4 - admin 5 - rest */ int sort_status[6] = {EKG_STATUS_AVAIL, EKG_STATUS_AWAY, EKG_STATUS_XA, EKG_STATUS_INVISIBLE, EKG_STATUS_FFC, EKG_STATUS_DND}; int sum[6] = {0, 0, 0, 0, 0, 0}; char **mp, *channame, *cchn; channel_t *chan; string_t buf; int lvl, count = 0, i; userlist_t *ul; if (!(channame = irc_getchan(session, params, name, &mp, 0, IRC_GC_CHAN))) return -1; if (!(chan = irc_find_channel(j->channels, channame))) { printq("generic", "irc_command_names: wtf?"); return -1; } if (chan->longest_nick > atoi(SOP(_005_NICKLEN))) debug_warn("[irc, names] funny %d vs %s\n", chan->longest_nick, SOP(_005_NICKLEN)); buf = string_init(NULL); for (i = 0; i <= xstrlen(j->nick_modes); i++) { static char mode_str[2] = { '?', '\0' }; const char *mode; /* set mode string passed to formatee to proper * sign from modes, or if there are no modes left * set it to formatee letter, * The use of 160 fillerchar will cause that "[ nickname ]" * won't be splitted like: "[ nickname * ]", and whole will be treated as long 'longest_nick+2' long string :) */ switch (j->nick_modes[i]) { case 'o': lvl = 0; break; case 'h': lvl = 1; break; case 'v': lvl = 2; break; case 'q': lvl = 3; break; case 'a': lvl = 4; break; default: lvl = 5; break; } mode = (mode_str[0] = j->nick_signs[i]) ? mode_str : fillchars; for (ul = chan->window->userlist; ul; ul = ul->next) { userlist_t *ulist = ul; char *tmp; if (ulist->status != sort_status[lvl]) continue; nickpad_string_apply(chan, ulist->uid+4); string_append(buf, (tmp = format_string(format_find("IRC_NAMES"), mode, (ulist->uid + 4), chan->nickpad_str))); xfree(tmp); nickpad_string_restore(chan); ++sum[lvl]; ++count; } } cchn = clean_channel_names(session, channame+4); print_info(channame, session, "IRC_NAMES_NAME", session_name(session), cchn); if (count) print_info(channame, session, "none", buf->str); print_info(channame, session, "none2", ""); #define plvl(x) ekg_itoa(sum[x]) if (sum[1]+sum[3]+sum[4] != 0) /* has halfops, admins or owners */ print_info(channame, session, "IRC_NAMES_TOTAL_H", session_name(session), cchn, ekg_itoa(count), plvl(0), plvl(1), plvl(2), plvl(5), plvl(3), plvl(4)); else print_info(channame, session, "IRC_NAMES_TOTAL", session_name(session), cchn, ekg_itoa(count), plvl(0), plvl(2), plvl(5)); #undef plvl xfree(cchn); debug_white("[IRC_NAMES] levelcounts = %d %d %d %d %d %d\n", sum[0], sum[1], sum[2], sum[3], sum[4], sum[5]); g_strfreev(mp); string_free (buf, 1); xfree(channame); return 0; } static COMMAND(irc_command_topic) { irc_private_t *j = irc_private(session); char **mp, *chan, *newtop; if (!(chan=irc_getchan(session, params, name, &mp, 0, IRC_GC_CHAN))) return -1; if (*mp) if (xstrlen(*mp)==1 && **mp==':') newtop = saprintf("TOPIC %s :\r\n", chan+4); else { char *recode = irc_convert_out(j, chan+4, *mp); newtop = saprintf("TOPIC %s :%s\r\n", chan+4, recode); xfree(recode); } else newtop = saprintf("TOPIC %s\r\n", chan+4); ekg_connection_write(j->send_stream, "%s", newtop); g_strfreev(mp); xfree (newtop); xfree (chan); return 0; } static COMMAND(irc_command_who) { irc_private_t *j = irc_private(session); char **mp, *chan; if (!(chan=irc_getchan(session, params, name, &mp, 0, IRC_GC_CHAN))) return -1; ekg_connection_write(j->send_stream, "WHO %s\r\n", chan+4); g_strfreev(mp); xfree(chan); return 0; } static COMMAND(irc_command_invite) { irc_private_t *j = irc_private(session); char **mp, *chan; if (!(chan=irc_getchan(session, params, name, &mp, 0, IRC_GC_CHAN))) return -1; if (!(*mp)) { printq("not_enough_params", name); xfree(chan); return -1; } ekg_connection_write(j->send_stream, "INVITE %s %s\r\n", *mp, chan+4); g_strfreev(mp); xfree(chan); return 0; } static COMMAND(irc_command_kick) { irc_private_t *j = irc_private(session); char **mp, *chan; if (!(chan=irc_getchan(session, params, name, &mp, 0, IRC_GC_CHAN))) return -1; if (!(*mp)) { printq("not_enough_params", name); xfree(chan); return -1; } ekg_connection_write(j->send_stream, "KICK %s %s :%s\r\n", chan+4, *mp, KICKMSG(session, mp[1])); g_strfreev(mp); xfree(chan); return 0; } static COMMAND(irc_command_unban) { irc_private_t *j = irc_private(session); char *channame, **mp; channel_t *chan = NULL; list_t banlist; int i, banid = 0; if (!(channame = irc_getchan(session, params, name, &mp, 0, IRC_GC_CHAN))) return -1; debug_function("[irc]_command_unban(): chan: %s mp[0]:%s mp[1]:%s\n", channame, mp[0], mp[1]); if (!(*mp)) { printq("not_enough_params", name); g_strfreev(mp); xfree(channame); return -1; } else { if ( (banid = atoi(*mp)) ) { chan = irc_find_channel(j->channels, channame+4); if (chan && (banlist = (chan->banlist)) ) { for (i=1; banlist && inext, ++i); if (banlist) /* fit or add i<=banid) ? */ ekg_connection_write(j->send_stream, "MODE %s -b %s\r\n", channame+4, (const gchar*) banlist->data); else debug_warn("%d %d out of range or no such ban %08x\n", i, banid, banlist); } else debug_error("Chanell || chan->banlist not found -> channel not synced ?!Try /mode +b \n"); } else { ekg_connection_write(j->send_stream, "MODE %s -b %s\r\n", channame+4, *mp); } } g_strfreev(mp); xfree(channame); return 0; } static COMMAND(irc_command_ban) { irc_private_t *j = irc_private(session); char *chan, **mp, *temp = NULL; people_t *person; if (!(chan=irc_getchan(session, params, name, &mp, 0, IRC_GC_CHAN))) return -1; debug_function("[irc]_command_ban(): chan: %s mp[0]:%s mp[1]:%s\n", chan, mp[0], mp[1]); if (!(*mp)) ekg_connection_write(j->send_stream, "MODE %s +b \r\n", chan+4); else { /* if parameter to /ban is prefixed with irc: like /ban irc:xxx * we don't care, since this is what user requested ban user with * nickname: 'irc:xxx' [this is quite senseless, since IRC servers * doesn't allow ':' in nickname (an they probably never will) * * calling /ban irc:xxx will cause: * programmer's mistake in call to irc_find_person!: irc:xxx * in debug window, but please do not report it, according to * what is written above this is normal, DELETE THIS NOTE L8R */ /* ok new irc-find-person checked */ person = irc_find_person(j, j->people, (char *) *mp); if (person) temp = irc_make_banmask(session, person->nick+4, person->ident, person->host); if (temp) { ekg_connection_write(j->send_stream, "MODE %s +b %s\r\n", chan+4, temp); xfree(temp); } else ekg_connection_write(j->send_stream, "MODE %s +b %s\r\n", chan+4, *mp); } g_strfreev(mp); xfree(chan); return 0; } static COMMAND(irc_command_kickban) { if (!xstrcmp(name, ("kickban"))) { irc_command_kick(("kick"), params, session, target, quiet); irc_command_ban(("ban"), params, session, target, quiet); } else { irc_command_ban(("ban"), params, session, target, quiet); irc_command_kick(("kick"), params, session, target, quiet); } return 0; } static COMMAND(irc_command_devop) { irc_private_t *j = irc_private(session); int modes, i; char **mp, *op, *nicks, *tmp, c, *chan, *p; string_t zzz; if (!(chan = irc_getchan(session, params, name, &mp, 0, IRC_GC_CHAN))) return -1; if (!(*mp)) { printq("not_enough_params", name); xfree(chan); return -1; } modes = atoi(j->sopt[_005_MODES]); op = xmalloc((modes+2) * sizeof(char)); /* H alfop */ /* o P */ /* voice */ c=xstrchr(name, 'h')?'h':xstrchr(name, 'p')?'o':'v'; /* Yes, I know there is such a function as memset() ;> */ for (i=0, tmp=op+1; isend_stream, "MODE %s %s %s\r\n", chan, op, p); if (!tmp) break; *tmp = ' '; tmp++; p = tmp; } chan-=4; xfree(chan); xfree(nicks); xfree(op); g_strfreev(mp); return 0; } static COMMAND(irc_command_ctcp) { int i; char *who; char **mp; /* GiM: XXX */ if (!(who=irc_getchan(session, params, name, &mp, 0, IRC_GC_ANY))) return -1; if (*mp) { for (i=0; ctcps[i].name; i++) if (!xstrcasecmp(ctcps[i].name, *mp)) break; } else i = CTCP_VERSION-1; /*if (!ctcps[i].name) { return -1; }*/ ekg_connection_write(irc_private(session)->send_stream, "PRIVMSG %s :\01%s\01\r\n", who+4, ctcps[i].name?ctcps[i].name:(*mp)); g_strfreev(mp); xfree(who); return 0; } static COMMAND(irc_command_ping) { char **mp, *who; GTimeVal tv; if (!(who=irc_getchan(session, params, name, &mp, 0, IRC_GC_ANY))) return -1; g_get_current_time(&tv); ekg_connection_write(irc_private(session)->send_stream, "PRIVMSG %s :\01PING %ld %ld\01\r\n", who+4 ,tv.tv_sec, tv.tv_usec); g_strfreev(mp); xfree(who); return 0; } static COMMAND(irc_command_me) { irc_private_t *j = irc_private(session); char **mp, *chan, *chantypes = SOP(_005_CHANTYPES), *col; int mw = session_int_get(session, "make_window"), ischn; char *str = NULL; if (!(chan=irc_getchan(session, params, name, &mp, 1, IRC_GC_ANY))) return -1; ischn = chantypes?!!xstrchr(chantypes, chan[4]):0; str = irc_convert_out(j, chan+4, *mp); ekg_connection_write(irc_private(session)->send_stream, "PRIVMSG %s :\01ACTION %s\01\r\n", chan+4, str?str:""); col = irc_ircoldcolstr_to_ekgcolstr(session, *mp, 1); print_window(chan, session, EKG_WINACT_MSG, ischn?(mw&1):!!(mw&2), ischn?"irc_ctcp_action_y_pub":"irc_ctcp_action_y", session_name(session), j->nick, chan, col); g_strfreev(mp); xfree(chan); xfree(col); xfree(str); return 0; } static COMMAND(irc_command_mode) { char **mp, *chan; if (!(chan=irc_getchan(session, params, name, &mp, 0, IRC_GC_CHAN))) return -1; /* G->dj: I'm still leaving this if (!(*mp)) { print("not_enough_params", name); g_strfreev(mp); xfree(chan); return -1; } */ debug_function("irc_command_mode %s %s \n", chan, mp[0]); if (!(*mp)) ekg_connection_write(irc_private(session)->send_stream, "MODE %s\r\n", chan+4); else ekg_connection_write(irc_private(session)->send_stream, "MODE %s %s\r\n", chan+4, *mp); g_strfreev(mp); xfree(chan); return 0; } static COMMAND(irc_command_umode) { irc_private_t *j = irc_private(session); if (!(*params)) { print("not_enough_params", name); return -1; } ekg_connection_write(j->send_stream, "MODE %s %s\r\n", j->nick, *params); return 0; } static COMMAND(irc_command_whois) { char **mp, *person; if (!(person = irc_getchan(session, params, name, &mp, 0, IRC_GC_NOT_CHAN))) return -1; debug_function("irc_command_whois(): %s\n", name); if (!xstrcmp(name, ("whowas"))) ekg_connection_write(irc_private(session)->send_stream, "WHOWAS %s\r\n", person+4); else if (!xstrcmp(name, ("wii"))) ekg_connection_write(irc_private(session)->send_stream, "WHOIS %s %s\r\n", person+4, person+4); else ekg_connection_write(irc_private(session)->send_stream, "WHOIS %s\r\n", person+4); g_strfreev(mp); xfree (person); return 0; } static QUERY(irc_status_show_handle) { char **uid = va_arg(ap, char**); session_t *s = session_find(*uid); irc_private_t *j; const char *p[2]; if (!s) return -1; if ((j = s->priv)) { if (j->conv) { debug("[%s] Uses recoding for: %s\n", s->uid, j->conv); } } p[0] = irc_private(s)->nick; p[1] = 0; return irc_command_whois(("wii"), p, s, NULL, 0); } static COMMAND(irc_command_query) { irc_private_t *j = irc_private(session); window_t *w; char **mp, *tar, **p = xcalloc(3, sizeof(char*)), *tmp; int i; for (i = 0; i<2 && params[i]; i++) p[i] = xstrdup(params[i]); p[i] = NULL;/* G->dj: I'm leaving this, I like things like * that to be clearly visible */ if (p[0] && (tmp = xstrrchr(p[0], '/'))) { session_t *s; *tmp++ = 0; if ((s = session_find(p[0])) && (s != session)) { command_exec_format(NULL, s, 0, ("/query \"%s\""), tmp); g_strfreev(p); return -1; } xfree(p[0]); p[0] = xstrdup(tmp); } if (!(tar = irc_getchan(session, (const char **) p, name, &mp, 0, IRC_GC_NOT_CHAN))) { g_strfreev(p); return -1; } w = window_find_s(session, tar); if (!w) { w = window_new(tar, session, 0); if (session_int_get(session, "auto_lusers_sync") > 0) ekg_connection_write(j->send_stream, "USERHOST %s\r\n", tar+4); } window_switch(w->id); g_strfreev(mp); g_strfreev(p); xfree(tar); return 0; } static COMMAND(irc_command_jopacy) { irc_private_t *j = irc_private(session); char **mp, *tar = NULL, *pass = NULL, *str, *tmp; channel_t *chan; if (!(tar = irc_getchan(session, params, name, &mp, 0, IRC_GC_CHAN))) return -1; if (!xstrcmp(name, ("cycle"))) { chan = irc_find_channel(j->channels, tar); if (chan && (pass = xstrchr(chan->mode_str, 'k'))) pass+=2; debug_function("[IRC_CYCLE] %s\n", pass); } tmp = saprintf("JOIN %s%s\r\n", tar+4, pass ? pass : ""); if (!xstrcmp(name, ("part")) || !xstrcmp(name, ("cycle"))) { str = saprintf("PART %s :%s\r\n%s", tar+4, PARTMSG(session,(*mp)), !xstrcmp(name, ("cycle"))?tmp:""); } else if (!xstrcmp(name, ("join"))) { str = tmp; tmp=NULL; } else return 0; ekg_connection_write(j->send_stream, "%s", str); g_strfreev(mp); xfree(tar); xfree(str); xfree(tmp); return 0; } static COMMAND(irc_command_nick) { irc_private_t *j = irc_private(session); /* GiM: XXX FIXME TODO think more about session->connecting... */ if (session->connecting || session_connected_get(session)) { ekg_connection_write(j->send_stream, "NICK %s\r\n", params[0]); /* this is needed, couse, when connecting and server will * respond, nickname is already in use, and user * will type /nick somethin', server doesn't send respond * about nickname.... */ if (session->connecting) { xfree(j->nick); j->nick = xstrdup(params[0]); } } return 0; } /* nickpad, these funcs should go to misc.c */ char *nickpad_string_create(channel_t *chan) { int i; chan->nickpad_len = (chan->longest_nick + 1) * fillchars_len; xfree (chan->nickpad_str); chan->nickpad_str = (char *)xmalloc(chan->nickpad_len); /* fill string */ for (i = 0; i < chan->nickpad_len; i++) chan->nickpad_str[i] = fillchars[ i % fillchars_len ]; debug("created NICKPAD with len: %d\n", chan->nickpad_len); return chan->nickpad_str; } char *nickpad_string_apply(channel_t *chan, const char *str) { chan->nickpad_pos = (chan->longest_nick - g_utf8_strlen(str, -1)) * 2; if (chan->nickpad_pos < chan->nickpad_len && chan->nickpad_pos >= 0) { chan->nickpad_str[chan->nickpad_pos] = '\0'; } else { debug_error ("[irc, misc, nickpad], problem with padding %x against %x\n", chan->nickpad_pos, chan->nickpad_len); } return chan->nickpad_str; } char *nickpad_string_restore(channel_t *chan) { if (chan->nickpad_pos < chan->nickpad_len && chan->nickpad_pos >= 0) { chan->nickpad_str[chan->nickpad_pos] = fillchars[ chan->nickpad_pos % fillchars_len ]; } return chan->nickpad_str; } /* * * ======================================== INIT/DESTROY --------------- * * */ #define VAR_MAP_ADD(v, c, l) { l, v, c } #define VAR_MAP_END() { NULL, 0, 0 } #define params(x) x #include static variable_map_t ban_type_map[] = { VAR_MAP_ADD( 1, 0, "nick"), VAR_MAP_ADD( 2, 0, "user"), VAR_MAP_ADD( 3, 0, "host"), VAR_MAP_ADD( 4, 0, "domain"), VAR_MAP_END() }; static plugins_params_t irc_plugin_vars[] = { /* lower case: names of variables that reffer to client itself */ PLUGIN_VAR_ADD("alt_nick", VAR_STR, NULL, 0, NULL), PLUGIN_VAR_ADD("alias", VAR_STR, NULL, 0, NULL), PLUGIN_VAR_ADD("auto_away", VAR_INT, "0", 0, NULL), PLUGIN_VAR_ADD("auto_back", VAR_INT, "0", 0, NULL), PLUGIN_VAR_ADD("auto_connect", VAR_BOOL, "0", 0, NULL), PLUGIN_VAR_ADD("auto_find", VAR_BOOL, "0", 0, NULL), /* it's really auto_whois */ PLUGIN_VAR_ADD("auto_guess_encoding", VAR_STR, NULL, 0, irc_changed_auto_guess_encoding), PLUGIN_VAR_ADD("auto_reconnect", VAR_INT, "10", 0, NULL), PLUGIN_VAR_ADD("auto_channel_sync", VAR_BOOL, "1", 0, NULL), /* like channel_sync in irssi; better DO NOT turn it off! */ PLUGIN_VAR_ADD("auto_lusers_sync", VAR_BOOL, "0", 0, NULL), /* sync lusers, stupid ;(, G->dj: well why ? */ PLUGIN_VAR_ADD("away_log", VAR_BOOL, "1", 0, NULL), PLUGIN_VAR_ADD_MAP("ban_type", VAR_MAP, "10", 0, NULL, ban_type_map ), PLUGIN_VAR_ADD("connect_timeout", VAR_INT, "0", 0, NULL), PLUGIN_VAR_ADD("close_windows", VAR_BOOL, "0", 0, NULL), PLUGIN_VAR_ADD("dcc_port", VAR_INT, "0", 0, NULL), PLUGIN_VAR_ADD("display_notify", VAR_INT, "0", 0, NULL), PLUGIN_VAR_ADD("dont_ban_user_on_noident", VAR_BOOL, "0", 0, NULL), PLUGIN_VAR_ADD("hostname", VAR_STR, 0, 0, NULL), PLUGIN_VAR_ADD("identify", VAR_STR, 0, 0, NULL), PLUGIN_VAR_ADD("log_formats", VAR_STR, "irssi", 0, NULL), PLUGIN_VAR_ADD("make_window", VAR_INT, "2", 0, NULL), #define IRC_PLUGIN_VAR_NICKNAME 21 PLUGIN_VAR_ADD("nickname", VAR_STR, NULL, 0, NULL), /* value will be inited @ irc_plugin_init() [pwd_entry->pw_name] */ PLUGIN_VAR_ADD("password", VAR_STR, 0, 1, NULL), PLUGIN_VAR_ADD("port", VAR_INT, "6667", 0, NULL), PLUGIN_VAR_ADD("prefer_family", VAR_INT, "0", 0, NULL), #define IRC_PLUGIN_VAR_REALNAME 25 PLUGIN_VAR_ADD("realname", VAR_STR, NULL, 0, NULL), /* value will be inited @ irc_plugin_init() [pwd_entry->pw_gecos] */ PLUGIN_VAR_ADD("recode_list", VAR_STR, NULL, 0, irc_changed_recode_list), PLUGIN_VAR_ADD("recode_out_default_charset", VAR_STR, NULL, 0, irc_changed_recode), /* irssi-like-variable */ PLUGIN_VAR_ADD("server", VAR_STR, 0, 0, NULL), PLUGIN_VAR_ADD("statusdescr", VAR_STR, 0, 0, irc_statusdescr_handler), PLUGIN_VAR_ADD("usermode", VAR_STR, "+iw", 0, NULL), PLUGIN_VAR_ADD("use_tls", VAR_BOOL, "0", 0, NULL), /* upper case: names of variables, that reffer to protocol stuff */ PLUGIN_VAR_ADD("AUTO_JOIN", VAR_STR, 0, 0, NULL), PLUGIN_VAR_ADD("AUTO_JOIN_CHANS_ON_INVITE", VAR_BOOL, "0", 0, NULL), PLUGIN_VAR_ADD("DEFAULT_COLOR", VAR_INT, "0", 0, NULL), /* TODO :> */ PLUGIN_VAR_ADD("DISPLAY_AWAY_NOTIFICATION", VAR_INT, "1", 0, NULL), PLUGIN_VAR_ADD("DISPLAY_IN_CURRENT", VAR_INT, "2", 0, NULL), PLUGIN_VAR_ADD("DISPLAY_NICKCHANGE", VAR_INT, "0", 0, NULL), PLUGIN_VAR_ADD("DISPLAY_PONG", VAR_BOOL, "0", 0, NULL), PLUGIN_VAR_ADD("DISPLAY_QUIT", VAR_INT, "0", 0, NULL), /* plugin_var_add(&irc_plugin, "HIGHLIGHTS", VAR_STR, 0, 0, NULL); */ PLUGIN_VAR_ADD("KICK_MSG", VAR_STR, DEFKICKMSG, 0, NULL), PLUGIN_VAR_ADD("PART_MSG", VAR_STR, DEFPARTMSG, 0, NULL), PLUGIN_VAR_ADD("QUIT_MSG", VAR_STR, DEFQUITMSG, 0, NULL), PLUGIN_VAR_ADD("REJOIN", VAR_INT, "0", 0, NULL), PLUGIN_VAR_ADD("REJOIN_TIME", VAR_INT, "2", 0, NULL), PLUGIN_VAR_ADD("SHOW_NICKMODE_EMPTY", VAR_INT, "1", 0, NULL), PLUGIN_VAR_ADD("SHOW_MOTD", VAR_BOOL, "1", 0, NULL), PLUGIN_VAR_ADD("STRIPMIRCCOL", VAR_BOOL, "0", 0, NULL), PLUGIN_VAR_ADD("USERINFO", VAR_STR, 0, 0, NULL), PLUGIN_VAR_ADD("VERSION_NAME", VAR_STR, 0, 0, NULL), PLUGIN_VAR_ADD("VERSION_NO", VAR_STR, 0, 0, NULL), PLUGIN_VAR_ADD("VERSION_SYS", VAR_STR, 0, 0, NULL), PLUGIN_VAR_END() }; static const char *irc_protocols[] = { "irc:", NULL }; static const status_t irc_statuses[] = { EKG_STATUS_NA, EKG_STATUS_AWAY, EKG_STATUS_AVAIL /* XXX */, EKG_STATUS_NULL }; static const struct protocol_plugin_priv irc_priv = { .protocols = irc_protocols, .statuses = irc_statuses }; EXPORT int irc_plugin_init(int prio) { #ifndef NO_POSIX_SYSTEM struct passwd *pwd_entry = getpwuid(getuid()); /* yeah, i know it's static. */ static char pwd_name[2000] = { '\0'}; static char pwd_realname[2000] = { '\0'}; PLUGIN_CHECK_VER("irc"); if (pwd_entry) { xstrncpy(pwd_name, pwd_entry->pw_name, sizeof(pwd_name)); xstrncpy(pwd_realname, pwd_entry->pw_gecos, sizeof(pwd_realname)); pwd_name[sizeof(pwd_name)-1] = '\0'; pwd_realname[sizeof(pwd_realname)-1] = '\0'; /* XXX, we need to free buffer allocated by getpwuid()? */ } #else const char *pwd_name = NULL; const char *pwd_realname = NULL; #endif /* magic stuff */ irc_plugin_vars[IRC_PLUGIN_VAR_NICKNAME].value = pwd_name; irc_plugin_vars[IRC_PLUGIN_VAR_REALNAME].value = pwd_realname; irc_plugin.params = irc_plugin_vars; irc_plugin.priv = &irc_priv; plugin_register(&irc_plugin, prio); #define IRC_ONLY SESSION_MUSTBELONG | SESSION_MUSTHASPRIVATE #define IRC_FLAGS IRC_ONLY | SESSION_MUSTBECONNECTED #define IRC_FLAGS_TARGET IRC_FLAGS | COMMAND_ENABLEREQPARAMS | COMMAND_PARAMASTARGET command_add(&irc_plugin, ("irc:"), "?", irc_command_inline_msg, IRC_FLAGS | COMMAND_PASS_UNCHANGED, NULL); command_add(&irc_plugin, ("irc:_autoaway"), NULL, irc_command_away, IRC_FLAGS, NULL); command_add(&irc_plugin, ("irc:_autoback"), NULL, irc_command_away, IRC_FLAGS, NULL); command_add(&irc_plugin, ("irc:access"), "p uUw ? ?", irc_command_alist, 0, "-a --add -d --delete -e --edit -s --show -l --list -S --sync"); command_add(&irc_plugin, ("irc:add"), NULL, irc_command_add, IRC_ONLY | COMMAND_PARAMASTARGET, NULL); command_add(&irc_plugin, ("irc:away"), "?", irc_command_away, IRC_FLAGS, NULL); command_add(&irc_plugin, ("irc:back"), NULL, irc_command_away, IRC_FLAGS, NULL); command_add(&irc_plugin, ("irc:ban"), "uUw uU", irc_command_ban, IRC_FLAGS, NULL); command_add(&irc_plugin, ("irc:bankick"), "uUw uU ?", irc_command_kickban,IRC_FLAGS, NULL); command_add(&irc_plugin, ("irc:connect"), NULL, irc_command_connect, IRC_ONLY, NULL); command_add(&irc_plugin, ("irc:ctcp"), "uUw ?", irc_command_ctcp, IRC_FLAGS, NULL); command_add(&irc_plugin, ("irc:cycle"), "w ?", irc_command_jopacy, IRC_FLAGS, NULL); command_add(&irc_plugin, ("irc:dehalfop"), "uUw uU uU uU uU uU uU ?",irc_command_devop, IRC_FLAGS, NULL); command_add(&irc_plugin, ("irc:deop"), "uUw uU uU uU uU uU uU ?", irc_command_devop, IRC_FLAGS, NULL); command_add(&irc_plugin, ("irc:devoice"), "uUw uU uU uU uU uU uU ?",irc_command_devop, IRC_FLAGS, NULL); command_add(&irc_plugin, ("irc:disconnect"), "r",irc_command_disconnect,IRC_ONLY, NULL); command_add(&irc_plugin, ("irc:find"), "uU", irc_command_whois, IRC_FLAGS, NULL); /* for auto_find */ command_add(&irc_plugin, ("irc:halfop"), "uUw uU uU uU uU uU uU ?",irc_command_devop, IRC_FLAGS, NULL); command_add(&irc_plugin, ("irc:invite"), "uUw uUw",irc_command_invite, IRC_FLAGS, NULL); command_add(&irc_plugin, ("irc:join"), "w", irc_command_jopacy, IRC_FLAGS, NULL); command_add(&irc_plugin, ("irc:kick"), "uUw uU ?",irc_command_kick, IRC_FLAGS, NULL); command_add(&irc_plugin, ("irc:kickban"), "uUw uU ?", irc_command_kickban,IRC_FLAGS, NULL); command_add(&irc_plugin, ("irc:me"), "uUw ?", irc_command_me, IRC_FLAGS, NULL); command_add(&irc_plugin, ("irc:mode"), "w ?", irc_command_mode, IRC_FLAGS, NULL); command_add(&irc_plugin, ("irc:msg"), "!uUw !", irc_command_msg, IRC_FLAGS_TARGET, NULL); command_add(&irc_plugin, ("irc:names"), "w?", irc_command_names, IRC_FLAGS, NULL); command_add(&irc_plugin, ("irc:nick"), "!", irc_command_nick, IRC_ONLY | COMMAND_ENABLEREQPARAMS, NULL); command_add(&irc_plugin, ("irc:notice"), "!uUw !",irc_command_msg, IRC_FLAGS_TARGET, NULL); command_add(&irc_plugin, ("irc:op"), "uUw uU uU uU uU uU uU ?", irc_command_devop, IRC_FLAGS, NULL); command_add(&irc_plugin, ("irc:part"), "w ?", irc_command_jopacy, IRC_FLAGS, NULL); command_add(&irc_plugin, ("irc:people"), NULL, irc_command_pipl, IRC_ONLY, NULL); command_add(&irc_plugin, ("irc:ping"), "uUw ?", irc_command_ping, IRC_FLAGS, NULL); command_add(&irc_plugin, ("irc:query"), "uUw", irc_command_query, IRC_FLAGS, NULL); command_add(&irc_plugin, ("irc:quote"), "!", irc_command_quote, IRC_FLAGS | COMMAND_ENABLEREQPARAMS, NULL); command_add(&irc_plugin, ("irc:reconnect"), "r ?",irc_command_reconnect, IRC_ONLY, NULL); command_add(&irc_plugin, ("irc:topic"), "w ?", irc_command_topic, IRC_FLAGS, NULL); command_add(&irc_plugin, ("irc:umode"), "?", irc_command_umode, IRC_ONLY /* _FLAGS ? */, NULL); command_add(&irc_plugin, ("irc:unban"), "uUw uU",irc_command_unban, IRC_FLAGS, NULL); command_add(&irc_plugin, ("irc:voice"), "uUw uU uU uU uU uU uU ?",irc_command_devop, IRC_FLAGS, NULL); command_add(&irc_plugin, ("irc:who"), "uUw", irc_command_who, IRC_FLAGS, NULL); command_add(&irc_plugin, ("irc:whois"), "uU", irc_command_whois, IRC_FLAGS, NULL); command_add(&irc_plugin, ("irc:whowas"), "uU", irc_command_whois, IRC_FLAGS, NULL); command_add(&irc_plugin, ("irc:wii"), "uU", irc_command_whois, IRC_FLAGS, NULL); /* command_add(&irc_plugin, ("irc:admin"), "", NULL, 0, NULL); q admin command_add(&irc_plugin, ("irc:map"), "", NULL, 0, NULL); q map command_add(&irc_plugin, ("irc:links"), "", NULL, 0, NULL); V q links command_add(&irc_plugin, ("irc:oper"), "", NULL, 0, NULL); q oper %nick %pass command_add(&irc_plugin, ("irc:trace"), "", NULL, 0, NULL); q trace %... command_add(&irc_plugin, ("irc:stats"), "\"STATS\" ?",irc_command_quote, 0, NULL); V q stats command:add(&irc_plugin, ("irc:list"), .....) V q list */ variable_add(&irc_plugin, "access_groups", VAR_STR, 1, &irc_config_default_access_groups, NULL, NULL, NULL); variable_add(&irc_plugin, "allow_fake_contacts", VAR_BOOL, 1, &irc_config_allow_fake_contacts, NULL, NULL, NULL); variable_add(&irc_plugin, "clean_channel_name", VAR_BOOL, 1, &irc_config_clean_channel_name, NULL, NULL, NULL); /* * first register queries */ query_register("irc-join", QUERY_ARG_CHARP, /* session */ QUERY_ARG_CHARP, /* channel */ QUERY_ARG_CHARP, /* nick */ QUERY_ARG_INT, /* isour */ QUERY_ARG_CHARP, /* ident@host */ QUERY_ARG_END); query_register("irc-kick", QUERY_ARG_CHARP, /* session uid */ QUERY_ARG_CHARP, /* nick */ QUERY_ARG_CHARP, /* channel */ QUERY_ARG_CHARP, /* kickedby */ QUERY_ARG_END); query_register("irc-mode", QUERY_ARG_CHARP, /* session */ QUERY_ARG_CHARP, /* nick!ident@host */ QUERY_ARG_CHARP, /* channel */ QUERY_ARG_INT, /* act */ QUERY_ARG_CHARP, /* mode */ QUERY_ARG_CHARP, /* param */ QUERY_ARG_END); query_register("irc-notice", QUERY_ARG_CHARP, /* session */ QUERY_ARG_CHARP, /* from */ QUERY_ARG_CHARP, /* destination (channel|nick) */ QUERY_ARG_CHARP, /* message */ QUERY_ARG_INT, /* is to us */ QUERY_ARG_END); query_register("irc-parse-line", QUERY_ARG_CHARP, /* session */ QUERY_ARG_CHARP, /* line */ QUERY_ARG_END); query_register("irc-part", QUERY_ARG_CHARP, /* session */ QUERY_ARG_CHARP, /* channel */ QUERY_ARG_CHARP, /* nick */ QUERY_ARG_INT, /* isour */ QUERY_ARG_CHARP, /* ident@host */ QUERY_ARG_CHARP, /* reason */ QUERY_ARG_END); query_register("irc-privmsg", QUERY_ARG_CHARP, /* session */ QUERY_ARG_CHARP, /* from */ QUERY_ARG_CHARP, /* destination (channel|nick) */ QUERY_ARG_CHARP, /* message */ QUERY_ARG_INT, /* is to us */ QUERY_ARG_END); query_register("irc-protocol-message", QUERY_ARG_CHARP, /* session uid */ QUERY_ARG_CHARP, /* uid */ QUERY_ARG_CHARP, /* text */ QUERY_ARG_INT, /* isour */ QUERY_ARG_INT, /* foryou */ QUERY_ARG_INT, /* private */ QUERY_ARG_CHARP, /* channame */ QUERY_ARG_END); query_register("irc-protocol-numeric", QUERY_ARG_CHARP, /* session */ QUERY_ARG_INT, /* number */ QUERY_ARG_CHARPP, /* params */ QUERY_ARG_END); query_register("irc-quit", QUERY_ARG_CHARP, /* session */ QUERY_ARG_CHARP, /* nick */ QUERY_ARG_INT, /* isour */ QUERY_ARG_CHARP, /* ident@host */ QUERY_ARG_CHARP, /* reason */ QUERY_ARG_END); query_register("irc-topic", QUERY_ARG_CHARP, /* if CHANNEL -> topic; if USER -> ident@host */ QUERY_ARG_CHARP, /* if CHANNEL -> topicby; if USER -> realname */ QUERY_ARG_CHARP, /* if CHANNEL -> chanmodes; if USER -> undefined */ QUERY_ARG_END); /* * then connect queries */ query_connect(&irc_plugin, "protocol-validate-uid", irc_validate_uid, NULL); query_connect(&irc_plugin, "plugin-print-version", irc_print_version, NULL); query_connect(&irc_plugin, "ui-window-kill", irc_window_kill, NULL); query_connect(&irc_plugin, "session-added", irc_session_init, NULL); query_connect(&irc_plugin, "session-removed", irc_session_deinit, NULL); query_connect(&irc_plugin, "status-show", irc_status_show_handle, NULL); query_connect(&irc_plugin, "irc-topic", irc_topic_header, (void*) 0); query_connect(&irc_plugin, "irc-kick", irc_onkick_handler, 0); query_connect(&irc_plugin, "set-vars-default", irc_setvar_default, NULL); return 0; } static int irc_plugin_destroy() { plugin_unregister(&irc_plugin); return 0; } static int irc_theme_init() { debug_white("I love you honey bunny\n"); /* %1 should be _always_ session name, if it's not so, * you should report this to me (GiM) */ #ifndef NO_DEFAULT_THEME /* %2 - prefix, %3 - nick, %4 - nick+ident+host, %5 - chan, %6 - msg*/ format_add("irc_msg_sent", "%P<%n%3/%5%P>%n %6", 1); format_add("irc_msg_sent_n", "%P<%n%3%P>%n %6", 1); format_add("irc_msg_sent_chan", "%P%7<%w%{2*!@%+yrgcp}X%2%3%P>%n %6", 1); format_add("irc_msg_sent_chanh","%P%7<%W%{2*!@%+YRGCP}X%2%3%P>%n %6", 1); format_add("irc_not_sent", "%P(%n%3/%5%P)%n %6", 1); format_add("irc_not_sent_n", "%P(%n%3%P)%n %6", 1); format_add("irc_not_sent_chan", "%P%7(%w%{2*!@%+yrgcp}X%2%3%P)%n %6", 1); format_add("irc_not_sent_chanh","%P%7(%W%{2*!@%+YRGCP}X%2%3%P)%n %6", 1); // format_add("irc_msg_f_chan", "%B<%w%{2@%+gcp}X%2%3/%5%B>%n %6", 1); /* NOT USED */ // format_add("irc_msg_f_chanh", "%B<%W%{2@%+GCP}X%2%3/%5%B>%n %6", 1); /* NOT USED */ format_add("irc_msg_f_chan_n", "%B%7<%w%{2*!@%+yrgcp}X%2%3%B>%n %6", 1); format_add("irc_msg_f_chan_nh", "%B%7<%W%{2*!@%+YRGCP}X%2%3%B>%n %6", 1); format_add("irc_msg_f_some", "%b<%n%3%b>%n %6", 1); // format_add("irc_not_f_chan", "%B(%w%{2@%+gcp}X%2%3/%5%B)%n %6", 1); /* NOT USED */ // format_add("irc_not_f_chanh", "%B(%W%{2@%+GCP}X%2%3/%5%B)%n %6", 1); /* NOT USED */ format_add("irc_not_f_chan_n", "%B(%w%{2*!@%+yrgcp}X%2%3%B)%n %6", 1); format_add("irc_not_f_chan_nh", "%B(%W%{2*!@%+YRGCP}X%2%3%B)%n %6", 1); format_add("irc_not_f_some", "%b(%n%3%b)%n %6", 1); format_add("irc_not_f_server", "%g!%3%n %6", 1); format_add("IRC_NAMES_NAME", _("[%gUsers %G%2%n]"), 1); format_add("IRC_NAMES", "%K[%W%1%w%2%3%K]%n ", 1); format_add("IRC_NAMES_TOTAL_H", _("%> %WEKG2: %2%n: Total of %W%3%n nicks [%W%8%n owners, %W%9%n admins, %W%4%n ops, %W%5%n halfops, %W%6%n voices, %W%7%n normal]\n"), 1); format_add("IRC_NAMES_TOTAL", "%> %WEKG2: %2%n: Total of %W%3%n nicks [%W%4%n ops, %W%5%n voices, %W%6%n normal]\n", 1); format_add("irc_joined", _("%> %Y%2%n has joined %4\n"), 1); format_add("irc_joined_you", _("%> %RYou%n have joined %4\n"), 1); format_add("irc_left", _("%> %g%2%n has left %4 (%5)\n"), 1); format_add("irc_left_you", _("%> %RYou%n have left %4 (%5)\n"), 1); format_add("irc_kicked", _("%> %Y%2%n has been kicked out by %R%3%n from %5 (%6)\n"), 1); format_add("irc_kicked_you", _("%> You have been kicked out by %R%3%n from %5 (%6)\n"), 1); format_add("irc_quit", _("%> %Y%2%n has quit irc (%4)\n"), 1); format_add("irc_split", "%> ", 1); format_add("irc_unknown_ctcp", _("%> %Y%2%n sent unknown CTCP %3: (%4)\n"), 1); format_add("irc_ctcp_action_y_pub", "%y* %2%n %4", 1); format_add("irc_ctcp_action_y", "%Y* %2%n %4", 1); format_add("irc_ctcp_action_pub", "%y* %2%n %5", 1); format_add("irc_ctcp_action", "%Y* %2%n %5", 1); format_add("irc_ctcp_request_pub", _("%> %Y%2%n requested ctcp %5 from %4\n"), 1); format_add("irc_ctcp_request", _("%> %Y%2%n requested ctcp %5\n"), 1); format_add("irc_ctcp_reply", _("%> %Y%2%n CTCP reply from %3: %5\n"), 1); format_add("IRC_ERR_CANNOTSENDTOCHAN", "%! %2: %1\n", 1); format_add("IRC_RPL_FIRSTSECOND", "%> (%1) %2 %3\n", 1); format_add("IRC_RPL_SECONDFIRST", "%> (%1) %3 %2\n", 1); format_add("IRC_RPL_JUSTONE", "%> (%1) %2\n", 1); format_add("IRC_RPL_NEWONE", "%> (%1,%2) 1:%3 2:%4 3:%5 4:%6\n", 1); format_add("IRC_ERR_FIRSTSECOND", "%! (%1) %2 %3\n", 1); format_add("IRC_ERR_SECONDFIRST", "%! (%1) %3 %2\n", 1); format_add("IRC_ERR_JUSTONE", "%! (%1) %2\n", 1); format_add("IRC_ERR_NEWONE", "%! (%1,%2) 1:%3 2:%4 3:%5 4:%6\n", 1); format_add("IRC_RPL_CANTSEND", _("%> Cannot send to channel %T%2%n\n"), 1); format_add("RPL_MOTDSTART", "%g,+=%G-----\n", 1); format_add("RPL_MOTD", "%g|| %n%2\n", 1); format_add("RPL_ENDOFMOTD", "%g`+=%G-----\n", 1); format_add("RPL_INVITE", _("%> Inviting %W%2%n to %W%3%n\n"), 1); /* Used in: /mode +b|e|I %2 - chan %3 - data from server */ /* THIS IS TEMPORARY AND WILL BE DONE OTHER WAY, DO NOT USE THIS STYLES */ format_add("RPL_LISTSTART", "%g,+=%G-----\n", 1); format_add("RPL_EXCEPTLIST", _("%g|| %n %5 - %W%2%n: except %c%3\n"), 1); format_add("RPL_BANLIST", _("%g|| %n %5 - %W%2%n: ban %c%3\n"), 1); format_add("RPL_INVITELIST", _("%g|| %n %5 - %W%2%n: invite %c%3\n"), 1);; format_add("RPL_EMPTYLIST" , _("%g|| %n Empty list \n"), 1); format_add("RPL_LINKS", "%g|| %n %5 - %2 %3 %4\n", 1); format_add("RPL_ENDOFLIST", "%g`+=%G----- %2%n\n", 1); /* %2 - number; 3 - type of stats (I, O, K, etc..) ....*/ format_add("RPL_STATS", "%g|| %3 %n %4 %5 %6 %7 %8\n", 1); format_add("RPL_STATS_EXT", "%g|| %3 %n %2 %4 %5 %6 %7 %8\n", 1); format_add("RPL_STATSEND", "%g`+=%G--%3--- %2\n", 1); /* format_add("RPL_CHLISTSTART", "%g,+=%G lp %2\t%3\t%4\n", 1); format_add("RPL_CHLIST", "%g|| %n %5 %2\t%3\t%4\n", 1); */ format_add("RPL_CHLISTSTART", "%g,+=%G lp %2\t%3\t%4\n", 1); format_add("RPL_LIST", "%g|| %n %5 %2\t%3\t%4\n", 1); /* 2 - number; 3 - chan; 4 - ident; 5 - host; 6 - server ; 7 - nick; 8 - mode; 9 -> realname * format_add("RPL_WHOREPLY", "%g|| %c%3 %W%7 %n%8 %6 %4@%5 %W%9\n", 1); */ format_add("RPL_WHOREPLY", "%g|| %c%3 %W%7 %n%8 %6 %4@%5 %W%9\n", 1); /* delete those irssi-like styles */ format_add("RPL_AWAY", _("%G||%n away : %2 - %3\n"), 1); /* in whois %2 is always nick */ format_add("RPL_WHOISUSER", _("%G.+===%g-----\n%G||%n (%T%2%n) (%3@%4)\n%G||%n realname : %6\n"), 1); format_add("RPL_WHOWASUSER", _("%G.+===%g-----\n%G||%n (%T%2%n) (%3@%4)\n%G||%n realname : %6\n"), 1); /* %2 - nick %3 - there is/ was no such nickname / channel, and so on... */ /* format_add("IRC_WHOERROR", _("%G.+===%g-----\n%G||%n %3 (%2)\n"), 1); format_add("IRC_ERR_NOSUCHNICK", _("%n %3 (%2)\n"), 1); */ format_add("RPL_WHOISCHANNELS", _("%G||%n %|channels : %3\n"), 1); format_add("RPL_WHOISSERVER", _("%G||%n %|server : %3 (%4)\n"), 1); format_add("RPL_WHOISOPERATOR", _("%G||%n %|ircOp : %3\n"), 1); format_add("RPL_WHOISIDLE", _("%G||%n %|idle : %3 (signon: %4)\n"), 1); format_add("RPL_WHOISMISC", _("%G||%n %| * %3\n"), 1); format_add("RPL_ENDOFWHOIS", _("%G`+===%g-----\n"), 1); format_add("RPL_ENDOFWHOWAS", _("%G`+===%g-----\n"), 1); format_add("RPL_TOPIC", _("%> Topic %2: %3\n"), 1); /* \n not needed if you're including date [%4] */ format_add("IRC_RPL_TOPICBY", _("%> set by %2 on %4"), 1); format_add("IRC_TOPIC_CHANGE", _("%> %T%2%n changed topic on %T%4%n: %5\n"), 1); format_add("IRC_TOPIC_UNSET", _("%> %T%2%n unset topic on %T%4%n\n"), 1); format_add("IRC_MODE_CHAN_NEW", _("%> %2/%4 sets mode [%5]\n"), 1); format_add("IRC_MODE_CHAN", _("%> %2 mode is [%3]\n"), 1); format_add("IRC_MODE", _("%> (%1) %2 set mode %3 on You\n"), 1); format_add("IRC_INVITE", _("%> %W%2%n invites you to %W%5%n\n"), 1); format_add("IRC_PINGPONG", _("%) (%1) ping/pong %c%2%n\n"), 1); format_add("IRC_YOUNEWNICK", _("%> You are now known as %G%3%n\n"), 1); format_add("IRC_NEWNICK", _("%> %g%2%n is now known as %G%4%n\n"), 1); format_add("IRC_TRYNICK", _("%> Will try to use %G%2%n instead\n"), 1); format_add("IRC_CHANNEL_SYNCED", "%> Join to %W%2%n was synced in %W%3.%4%n secs", 1); /* %1 - sesja ; %2 - Connect, BIND, whatever, %3 - hostname %4 - adres %5 - port * %6 - family (debug pursuit only) */ format_add("IRC_TEST", "%> (%1) %2 to %W%3%n [%4] port %W%5%n (%6)", 1); format_add("IRC_CONN_ESTAB", "%> (%1) Connection to %W%3%n estabilished", 1); /* j/w %7 - error */ format_add("IRC_TEST_FAIL", "%! (%1) Error: %2 to %W%3%n [%4] port %W%5%n (%7)", 1); format_add("irc_channel_secure", "%) (%1) Echelon can kiss our ass on %2 *g*", 1); format_add("irc_channel_unsecure", "%! (%1) warning no plugin protects us on %2 :( install sim plugin now or at least rot13..", 1); format_add("irc_access_added", _("%> (%1) %3 [#%2] was added to accesslist chan: %4 (flags: %5)"), 1); format_add("irc_access_known", "a-> %2!%3@%4", 1); /* %2 is nickname, not uid ! */ /* away log */ format_add("irc_awaylog_begin", _("%G.+===%g----- Awaylog for: (%n%1%g)%n\n"), 1); format_add("irc_awaylog_msg", _("%G|| %n[%Y%2%n] <%W%4%n> %5\n"), 1); format_add("irc_awaylog_msg_chan", _("%G|| %n[%Y%2%n] [%G%3%n] <%W%4%n> %5\n"), 1); format_add("irc_awaylog_end", _("%G`+===%g-----\n"), 1); format_add("irc_awaylog_timestamp", "%d-%m-%Y %H:%M:%S", 1); #endif /* !NO_DEFAULT_THEME */ return 0; } /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: * vim: noet */ ekg2-0.4~pre+20120506.1/plugins/irc/irc.h000066400000000000000000000101421175142753400173370ustar00rootroot00000000000000/* * (C) Copyright 2004-2005 Michal 'GiM' Spadlinski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __EKG_PLUGINS_IRC_IRC_H #define __EKG_PLUGINS_IRC_IRC_H #define EKG_IRC_DEFAULT_USERMODE "+iw" /* irc_private->sopt */ enum { USERMODES=0, CHANMODES, _005_PREFIX, _005_CHANTYPES, _005_CHANMODES, _005_MODES, _005_CHANLIMIT, _005_NICKLEN, _005_IDCHAN, SERVOPTS }; /* irc_private_t->casemapping values */ enum { IRC_CASEMAPPING_ASCII, IRC_CASEMAPPING_RFC1459, IRC_CASEMAPPING_RFC1459_STRICT, IRC_CASEMAPPING_COUNT }; typedef struct _irc_private_t { int autoreconnecting; /* are we in reconnecting mode now? */ gboolean disconnecting; GCancellable *connect_cancellable; GDataOutputStream *send_stream; char *nick; /* guess again ? ;> */ char *host_ident; /* ident+host */ list_t people; /* list of people_t */ list_t channels; /* list of people_chan_t */ list_t hilights; char *sopt[SERVOPTS]; /* just a few options from * www.irc.org/tech_docs/005.html * server's response */ char *nick_modes; /* should be freed */ char *nick_signs; int casemapping; list_t awaylog; gchar **auto_guess_encoding; /* encoding names */ GData *recoded_channels; gchar *conv; } irc_private_t; /* data for private->out_recodes */ typedef struct { char *name; /* encoding name */ void *conv_in; void *conv_out; } out_recodes_t; /* data for private->recoded_channels */ typedef struct { char *name; /* channel or nick */ out_recodes_t *recode; } recoded_channels_t; typedef struct _irc_awaylog_t { char *channame; /* channel name, (null if priv) */ char *uid; /* nickname who wrote to us */ char *msg; /* msg */ time_t t; /* time_t when we recv message */ } irc_awaylog_t; #define SOP(x) (j->sopt[x]) /* data for private->people */ typedef struct { char *nick; char *realname; char *host, *ident; list_t channels; } people_t; /* data for private->channels */ typedef struct { char *name; int syncmode; GTimeVal syncstart; int mode; char *topic, *topicby, *mode_str; window_t *window; list_t onchan; char *nickpad_str; int nickpad_len, nickpad_pos; int longest_nick; list_t banlist; /* needed ? list_t exclist; list_t invlist; */ list_t acclist; } channel_t; /* data for private->people->channels */ typedef struct { int mode; /* bitfield */ char sign[2]; channel_t *chanp; } people_chan_t; #define irc_private(s) ((irc_private_t*) session_private_get(s)) /* DO NOT TOUCH THIS! */ #define IRC4 "irc:" #define irc_uid(target) protocol_uid("irc", target) extern plugin_t irc_plugin; void irc_handle_disconnect(session_t *s, const char *reason, int type); /* checks if name is in format irc:something * checkcon is one of: * name is channel | nick * IRC_GC_CHAN - channame | NULL * IRC_GC_NOT_CHAN - NULL | nickname * IRC_GC_ANY - name if it's in proper format [irc:something] */ enum { IRC_GC_CHAN=0, IRC_GC_NOT_CHAN, IRC_GC_ANY }; #define irc_write(s, args...) ekg_connection_write(irc_private(s)->send_stream, args) int irc_parse_line(session_t *s, const char *l, int fd); /* misc.c */ extern int irc_config_allow_fake_contacts; extern int irc_config_clean_channel_name; char *nickpad_string_create(channel_t *chan); char *nickpad_string_apply(channel_t *chan, const char *str); char *nickpad_string_restore(channel_t *chan); char *clean_channel_names(session_t *session, char *channels); #endif /* __EKG_PLUGINS_IRC_IRC_H */ /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/plugins/irc/misc.c000066400000000000000000001346271175142753400175270ustar00rootroot00000000000000/* * (C) Copyright 2004-2005 Michal 'GiM' Spadlinski * Wiesaw Ochmiski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #include #include #include #include #include #ifndef NO_POSIX_SYSTEM #include #endif #define GDEBUG #define MARLENE #include "irc.h" #include "misc.h" #include "people.h" #include "input.h" #include "autoacts.h" char *sopt_keys[SERVOPTS] = { NULL, NULL, "PREFIX", "CHANTYPES", "CHANMODES", "MODES", "CHANLIMIT", "NICKLEN", "IDCHAN" }; char sopt_casemapping[] = "CASEMAPPING"; char *sopt_casemapping_values[IRC_CASEMAPPING_COUNT] = { "ascii", "rfc1459", "strict-rfc1459" }; #define OMITCOLON(x) ((*x)==':'?(x+1):(x)) static void irc_parse_nick_identhost(char *line, char **nick, char **identhost) { char *p; if (!line || !*line) { *nick = NULL; *identhost = NULL; return; } if ( (p = xstrchr(line, '!')) ) { *nick = xstrndup(line, p-line); *identhost = xstrdup(p+1); } else { *nick = xstrdup(line); *identhost = xstrdup(""); } } static void irc_parse_ident_host(char *identhost, char **ident, char **host) { char *tmp; xfree(*ident); xfree(*host); if ((tmp = xstrchr(identhost, '@'))) { *ident = xstrndup(identhost, tmp-identhost); *host = xstrdup(tmp+1); } else { *ident = xstrdup(identhost); *host = NULL; } } static void irc_convert_in(irc_private_t *j, GString *line) { gchar **ep; if (j->auto_guess_encoding) { for (ep = j->auto_guess_encoding; *ep; ep++) { if (ekg_try_recode_gstring_from(*ep, line)) return; } } if (j->conv) ekg_recode_gstring_from(j->conv, line); else ekg_fix_utf8(line->str); } /* cos co blabla, zwraca liczbe pochlonietych znakow przez '*' XXX */ static int do_sample_wildcard_match(const char *str, const char *matchstr, const char stopon) { int i; for (i = 0; (str[i] && str[i] != stopon); i++); if (i == 0) return xstrlen(matchstr); debug_error("\nXXX do_sample_wildcard_match() XXX\n"); return 0; } static void irc_access_parse(session_t *s, channel_t *chan, people_t *p, int flags) { userlist_t *ul; if (!s || !chan || !p) return; #define dchar(x) debug("%c", x); for (ul = s->userlist; ul; ul = ul->next) { userlist_t *u = ul; ekg_resource_t *r = NULL, *rl; int i, j; if (!p->ident || !p->host) continue; if (xstrncmp(u->uid, "irc:", 4)) continue; /* check for irc: */ for (rl = u->resources; rl; rl = rl->next) { r = rl; if (r->priv_data == p) { const char *tmp = &(u->uid[4]); /* fast forward move.. */ if (!(tmp = xstrchr(tmp, '!')) || !(tmp = xstrchr(tmp, '@')) || !(tmp = xstrchr(tmp, ':'))) { debug_error("%s:%d INTERNAL ERROR\n", __FILE__, __LINE__); goto next3; } tmp++; i = (tmp - u->uid); debug("irc, checkchan: %s\n", tmp); goto skip_identhost_check; break; /* never here */ } } r = NULL; /* parse nick! */ for (i = 4, j = 0; (u->uid[i] != '\0' && u->uid[i] != '!'); i++, j++) { dchar(u->uid[i]); if (u->uid[i] != p->ident[j]) { if (u->uid[i] == '*') j += do_sample_wildcard_match(&u->uid[i+1], &p->ident[j], '!'); else if (u->uid[i] == '?') continue; else goto next; } } if (!u->uid[i]) goto next; dchar('!'); i++; /* parse ident@ */ for (j = 4 /* skip irc: */; (u->uid[i] != '\0' && u->uid[i] != '@'); i++, j++) { dchar(u->uid[i]); if (u->uid[i] != p->nick[j]) { if (u->uid[i] == '*') j += do_sample_wildcard_match(&u->uid[i+1], &p->ident[j], '@'); else if (u->uid[i] == '?') continue; else goto next; } } if (!u->uid[i]) goto next; dchar('@'); i++; /* parse host: */ for (j = 0; (u->uid[i] != '\0' && u->uid[i] != ':'); i++, j++) { dchar(u->uid[i]); if (u->uid[i] != p->host[j]) { if (u->uid[i] == '*') j += do_sample_wildcard_match(&u->uid[i+1], &p->ident[j], ':'); else if (u->uid[i] == '?') continue; else goto next; } } if (!u->uid[i]) goto next; dchar('\n'); i++; debug_error("irc_access_parse() %s!%s@%s MATCH with %s\n", p->ident, p->nick+4, p->host, u->uid+4); skip_identhost_check: /* let's rock with channels */ { char **arr = array_make(&u->uid[i], ",", 0, 1, 0); int ismatch = 0; for (i=0; arr[i]; i++) { int k; debug_error("CHAN%d: %s: ", i, arr[i]); for (j = 0, k = 4 /* skip irc: */; arr[i][j]; j++, k++) { if (arr[i][j] != chan->name[k]) { if (arr[i][j] == '*') k += do_sample_wildcard_match(&arr[i][j+1], &chan->name[k], '\0'); else if (arr[i][j] == '?') continue; else goto next2; } } if (chan->name[k] != '\0') goto next2; ismatch = 1; debug_error("MATCH\n"); break; next2: debug_error("NOT MATCH\n"); continue; } g_strfreev(arr); if (!ismatch) continue; } if (!r) { r = userlist_resource_add(u, p->nick, 0); r->status = EKG_STATUS_AVAIL; r->descr = xstrdup(chan->name+4); r->priv_data = p; if (u->status != EKG_STATUS_AVAIL) { xfree(u->descr); u->status = EKG_STATUS_AVAIL; u->descr = xstrdup("description... ?"); query_emit(NULL, "userlist-changed", &(s->uid), &(u->uid)); } } else { string_t str = string_init(r->descr); string_append_c(str, ','); string_append(str, chan->name+4); xfree(r->descr); r->descr = string_free(str, 0); } /* tutaj ladnie by wygladalo jakbysmy wywolali protocol-status.. ale jednak to jest b. kiepski pomysl */ debug_error("USER: 0x%x PERMISION GRANTED ON CHAN: 0x%x\n", u, chan); continue; next: dchar('\n'); debug_error("irc_access_parse() %s!%s@%s NOT MATCH with %s\n", p->ident, p->nick+4, p->host, u->uid+4); next3: continue; } #undef dchar } /** * int gatoi(char *buf, int *returnvalue) * * Simple wrapper around strtol. * Convert string pointed by buf to base 10 number, and save it in int value pointed at * returnvalue. * * @return 0 - OK * 1 - conversion failed, memory content of *number is unknown */ static int gatoi(char *buf, int *a) { char *x; long t; if(!buf) return (1); t=strtol(buf, &x, 10); if (x == buf) return (1); *a=t; return (0); } /** * irc_tolower_int(char *buf, int casemapping) * * Converts buffer pointed at buf to lower case using one of casmapping's: * IRC_CASEMAPPING_ASCII, IRC_CASEMAPPING_RFC1459, IRC_CASEMAPPING_RFC1459_STRICT * * DO NOT pass strings that can be in unicode; * * @return pointer to beginning of a string */ static char *irc_tolower_int(char *buf, int casemapping) { char *p = buf; int upper_bound; /* please, do not change this code, to something like: * 90 + (!!casemapping * (5 - casemapping)) */ switch (casemapping) { case IRC_CASEMAPPING_ASCII: upper_bound = 'Z'; break; case IRC_CASEMAPPING_RFC1459_STRICT: upper_bound = ']'; break; case IRC_CASEMAPPING_RFC1459: upper_bound = '^'; break; default: debug_error ("bad value in call to irc_toupper_int: %d\n", casemapping); return 0; } while (*p) { /* 65 ascii for 'A' */ if (*p >= 'A' && *p <= upper_bound) *p += 32; /* add 32, conversion 'A' -> 'a' */ p++; } return buf; } /** * IRC_TO_UPPER - macro around irc_upper_int, that passes currently * casemapping used by server */ #define IRC_TO_UPPER(x) irc_toupper_int(x, j->casemapping) /** * IRC_TO_LOWER - macro around irc_upper_int, that passes currently * casemapping used by server */ #define IRC_TO_LOWER(x) irc_tolower_int(x, j->casemapping) /*****************************************************************************/ /* */ int irc_parse_line(session_t *s, const char *l, int fd) { static GString *strbuf = NULL; irc_private_t *j = s->priv; int i, c=0, ecode; char *p, *q[20]; gchar *buf; int len; if (G_UNLIKELY(!strbuf)) strbuf = g_string_new(l); else g_string_assign(strbuf, l); irc_convert_in(j, strbuf); buf = strbuf->str; len = strbuf->len; query_emit(NULL, "irc-parse-line", &s->uid, &buf); p=buf; if(!p) return -1; for (i=0; i<20; i++) q[i]=NULL; /* Each IRC message may consist of up to three main parts: the prefix (optional), the command, and the command parameters (of which there may be up to 15). The prefix, command, and all parameters are separated by one (or more) ASCII space character(s) (0x20). The presence of a prefix is indicated with a single leading ASCII colon character (':', 0x3b), which must be the first character of the message itself. There must be no gap (whitespace) between the colon and the prefix. */ /* GiM: nasty hack, because, prefix is optional... */ if (':' != *p) { q[0]=":_empty_"; c++; } /* dj: maybe instead of :_empty_ we give here sesesion_get(.., server) ? * i don't remember rfc now ;( * G->dj: truly we shouldn't change this to anything... * this _empty_ is for our convenience */ q[c]=p; i=0; c++; /* GiM: split message into q table */ while (i */ #ifdef GDEBUG /* mg: well, it's not the exact data sent, but color is needed indeed */ i=0; while (q[i] != NULL) debug_iorecv("[%s]",q[i++]); debug_iorecv("\n"); #endif if (xstrlen(q[1]) > 1) { if(!gatoi(q[1], &ecode)) { /* for scripts */ char *emitname = saprintf("irc-protocol-numeric %s", q[1]); char **pq = &(q[2]); if ((query_emit(NULL, "irc-protocol-numeric", &s->uid, &ecode, &pq) == -1) || (query_emit(NULL, emitname, &s->uid, &pq) == -1)) { xfree(emitname); return -1; } xfree(emitname); c=0; while(irccommands[c].type != -1) { if (irccommands[c].type == 1 && irccommands[c].num == ecode) { /* I'm sending c not ecode!!!! */ if ((*(irccommands[c].handler))(s, j, fd, c, q) == -1 ) { debug_error("[irc] parse_line() error while executing handler!\n"); } /* GiM: XXX I don't expect more, * then one handler on list... */ break; } c++; } #ifdef GDEBUG if (irccommands[c].type == -1) { debug("trying default handler\n"); if ((*(irccommands[0].handler))(s, j, fd, 0, q) == -1 ) { debug("[irc] parse_line() error while executing handler!\n"); } } #endif } else { c=0; while(irccommands[c].type != -1) { if (irccommands[c].type == 0 && !xstrcmp(irccommands[c].comm, q[1])) { /* dj: instead of ecode, c; */ if ((*(irccommands[c].handler))(s, j, fd, c, q) == -1 ) { debug_error("[irc] parse_line() error while executing handler!\n"); } break; } c++; } } } return 0; } IRC_COMMAND(irc_c_init) { int i, k; char *t; switch (irccommands[ecode].num) { case 1: protocol_connected_emit(s); t = xstrchr(param[3], '!'); xfree(j->host_ident); if (t) j->host_ident=xstrdup(++t); else j->host_ident=NULL; debug_ok("\nirc_c_init()/num=1/...[%s:%s] given: %s\n", j->nick, j->host_ident, param[2]); xfree(j->nick); j->nick = xstrdup(param[2]); j->autoreconnecting = 0; j->casemapping = IRC_CASEMAPPING_RFC1459; xfree(SOP(_005_PREFIX)); SOP(_005_PREFIX) = xstrdup("(ov)@+"); j->nick_signs = SOP(_005_PREFIX) + 4; xfree(j->nick_modes); j->nick_modes = xstrdup("ov"); xfree(SOP(_005_CHANTYPES)); SOP(_005_CHANTYPES) = xstrdup("#!"); xfree(SOP(_005_MODES)); SOP(_005_MODES) = xstrdup("3"); xfree(SOP(_005_NICKLEN)); SOP(_005_NICKLEN) = xstrdup("9"); /* ~~ kinda optimal: */ xfree(SOP(_005_CHANMODES)); SOP(_005_CHANMODES) = xstrdup("b,k,l,imnpsta"); /* http://www.irc.org/tech_docs/005.html CHANMODES= b, k,l, imnpstr (ircu) CHANMODES= b, k,l, iLmMnOprRst (Bahamut) CHANMODES= beI,k,l, imnpstaqr (IRCNet) CHANMODES= beI,k,l, imnpsta (Hybrid) */ break; case 2: case 3: break; case 4: xfree(SOP(USERMODES)); SOP(USERMODES) = xstrdup(param[5]); xfree(SOP(CHANMODES)); SOP(CHANMODES) = xstrdup(param[6]); break; case 5: /* rfc says there can be 15 params */ /* yes I know it should be i<15 */ for (i = 3; i < 16; i++) { if (!param[i]) break; for (k = 0; k < SERVOPTS; k++) { if (sopt_keys[k] == NULL) continue; if (xstrncmp(param[i], sopt_keys[k], xstrlen(sopt_keys[k])) || param[i][xstrlen(sopt_keys[k])] != '=' ) continue; xfree(SOP(k)); SOP(k) = xstrdup(xstrchr(param[i], '=')+1); if (xstrlen(SOP(k))==0) { xfree(SOP(k)); SOP(k) = NULL; } } if (!xstrncmp(param[i], sopt_casemapping, xstrlen(sopt_casemapping))) if ((t = xstrchr(param[i], '='))) { /* I know this could be 'for', but I'm leavin' it for readability */ if (!xstrcmp(t+1, sopt_casemapping_values[IRC_CASEMAPPING_ASCII])) { j->casemapping = IRC_CASEMAPPING_ASCII; } else if (!xstrcmp(t+1, sopt_casemapping_values[IRC_CASEMAPPING_RFC1459])) { j->casemapping = IRC_CASEMAPPING_RFC1459; } else if (!xstrcmp(t+1, sopt_casemapping_values[IRC_CASEMAPPING_RFC1459_STRICT])) { j->casemapping = IRC_CASEMAPPING_RFC1459_STRICT; /* this one is already set above, as default } else { j->casemapping = IRC_CASEMAPPING_RFC1459; */ } } } k = (xstrlen(SOP(_005_PREFIX))>>1) - 1; j->nick_signs = SOP(_005_PREFIX) + k + 2; xfree(j->nick_modes); j->nick_modes = xstrndup(SOP(_005_PREFIX)+1, k); irc_autorejoin(s, IRC_REJOIN_CONNECT, NULL); break; default: break; } return 0; } IRC_COMMAND(irc_c_error) { int i; char *t = NULL, *dest = NULL, *coloured = NULL, *bang; time_t try; window_t *w; char *altnick; channel_t *chanp; char *tmpchn = NULL; #define IOK2(x) param[x]?OMITCOLON(param[x]):"" if (irccommands[ecode].future == 0 && !xstrcmp("ERROR", irccommands[ecode].comm)) { /* here error @ CONNECT * 21:03:35 [:_empty_][ERROR][:Trying to reconnect too fast.] * no I:line's etc.. everything that disconnects fd */ /* print_info(NULL, s, "IRC_ERR_FIRSTSECOND", session_name(s), irccommands[ecode].comm, IOK2(2)); */ if (s->connecting) irc_handle_disconnect(s, IOK2(2), EKG_DISCONNECT_FAILURE); else debug_error("[irc] !s->connecting\n"); return -1; } i = irccommands[ecode].future&0x100; switch (irccommands[ecode].future&0xff) { case IRC_ERR_21: print_info(NULL, s, i?"IRC_RPL_SECONDFIRST":"IRC_ERR_SECONDFIRST", session_name(s), param[3], IOK2(4)); return (0); case IRC_ERR_12: print_info(NULL, s, i?"IRC_RPL_FIRSTSECOND":"IRC_ERR_FIRSTSECOND", session_name(s), param[3], IOK2(4)); return (0); case IRC_ERR_ONLY1: print_info(NULL, s, i?"IRC_RPL_JUSTONE":"IRC_ERR_JUSTONE", session_name(s), IOK2(3)); return (0); #define IOK(x) param[x]?param[x]:"" case IRC_ERR_NEW: print_info(NULL, s, i?"IRC_RPL_NEWONE":"IRC_ERR_NEWONE", session_name(s), param[1], IOK(3), IOK(4), IOK(5), IOK(6)); return (0); case IRC_ERR_IGNO: return(0); default: break; } i = irccommands[ecode].num; /* GiM : XXX FIX IT * */ if (param[3]) { char *temp2; t = irc_uid(param[3]); temp2 = irc_uid(param[3]); IRC_TO_LOWER(temp2); w = window_find_s(s, t); if (!w) w = window_find_s(s, temp2); xfree(temp2); dest = w?t:NULL; } switch (i) { case 433: print_info(NULL, s, "IRC_ERR_SECONDFIRST", session_name(s), param[3], IOK2(4)); if (s->connecting) { altnick = (char *) session_get(s, "alt_nick"); /* G->dj: why this !xstrcmp ? */ if (altnick && !xstrcmp(param[3], session_get(s, "nickname")) && xstrcmp(param[3], altnick)) { print_info(NULL, s, "IRC_TRYNICK", session_name(s), altnick); xfree(j->nick); j->nick = xstrdup(altnick); ekg_connection_write(j->send_stream, "NICK %s\r\n", j->nick); } } break; case 404: tmpchn = clean_channel_names(s, param[3]); print_info(dest, s, "IRC_RPL_CANTSEND", session_name(s), tmpchn); break; case 301: if (!session_int_get(s, "DISPLAY_AWAY_NOTIFICATION")) break; dest = t; // NO BREAK!; /* topic */ case 331: case 332: IRC_TO_LOWER(param[3]); if ((chanp = irc_find_channel(j->channels, param[3]))) { xfree(chanp->topic); chanp->topic = g_strdup(OMITCOLON(param[4])); coloured = irc_ircoldcolstr_to_ekgcolstr(s, chanp->topic, 1); tmpchn = clean_channel_names(s, param[3]); print_info(dest, s, irccommands[ecode].name, session_name(s), tmpchn, coloured); xfree(coloured); } break; case 333: IRC_TO_LOWER(param[3]); if ((chanp = irc_find_channel(j->channels, param[3]))) { xfree(chanp->topicby); try = param[5]?atol(OMITCOLON(param[5])):0; if ((bang = xstrchr(param[4], '!'))) *bang = '\0'; chanp->topicby = xstrdup(param[4]); print_info(dest, s, "IRC_RPL_TOPICBY", session_name(s), param[4], bang?bang+1:"", param[5]?ctime(&try):"unknown\n"); if (bang) *bang ='!'; } break; case 341: tmpchn = clean_channel_names(s, param[4]); print_info(dest, s, irccommands[ecode].name, session_name(s), param[3], tmpchn); break; case 376: /* zero, identify with nickserv */ if (xstrlen(session_get(s, "identify"))) { /* temporary */ ekg_connection_write(j->send_stream, "PRIVMSG nickserv :IDENTIFY %s\n", session_get(s, "identify")); /* XXX, bedzie: * session_get(s, "identify") * "" "" "[GLOWNE HASLO]" * * array_make() splitowane po spacjach. " " traktuj jako jeden parametr * p[2...x] jesli ma spacje sprawdz czy nicki do spacji sie zgadzaja... jesli tak uzyj haslo. * jesli nie, uzywaj haslo. * * i dopiero robmy IDENTIFY jesli dostaniem request od serwisow !!! */ } /* first we join */ if (xstrlen(session_get(s, "AUTO_JOIN"))) ekg_connection_write(j->send_stream, "JOIN %s\r\n", session_get(s, "AUTO_JOIN")); case 372: case 375: if (session_int_get(s, "SHOW_MOTD") != 0) { coloured = irc_ircoldcolstr_to_ekgcolstr(s, IOK2(3), 1); print_info("__status", s, irccommands[ecode].name, session_name(s), coloured); xfree(coloured); } break; default: return(-1); } xfree(tmpchn); xfree(t); return 0; } char *clean_channel_names(session_t *session, char *channels) { irc_private_t *j = session->priv; char *chmode; char *dest, *src, *next, *p; int len, skip; char *ret; if (!irc_config_clean_channel_name) return xstrdup(channels); if (!SOP(_005_IDCHAN)) return xstrdup(channels); chmode = j->nick_signs; dest = src = ret = xstrdup(channels); while ( src && *src ) { char *idchan; if ((*src == ' ') || (strchr(chmode, *src))) { *dest++ = *src++; continue; } p = src; next = strchr(src, ' '); if (next) *next = '\0'; idchan = SOP(_005_IDCHAN); while (*idchan) { char chpfx = *idchan; if (idchan[1] != ':') /* ?WO? Check it only when IDCHAN is set & dot't check here */ break; skip = strtoul(idchan+2, &idchan, 10); if (*idchan == ',') idchan++; else if (*idchan) /* ?WO? Again: Check it only when IDCHAN is set & dot't check here */ break; if (chpfx != p[0]) break; if (strlen(p)-2 < skip) /* we need prefix and one (or more) chars in channel name */ break; strcpy(p + 1, p + skip + 1); } len = strlen(p); strcpy(dest, p); dest += len; src = next; if (next) *next=' '; } *dest='\0'; return ret; } IRC_COMMAND(irc_c_whois) { char *t = irc_uid(param[3]), *dest = NULL; char *str, *tmp, *col[5]; int secs = 0, mins, hours, days, which, i; time_t timek; int timek_int = (int) timek; window_t *w = window_find_s(s, t); if (session_int_get(s, "DISPLAY_IN_CURRENT")&2) dest = w?t:NULL; if (irccommands[ecode].num != 317) { /* idle */ char *chlist = NULL; for (i=0; i<5; i++) col[i] = irc_ircoldcolstr_to_ekgcolstr(s, param[3+i]?OMITCOLON(param[3+i]):NULL,1); if (irccommands[ecode].num == 319) chlist = clean_channel_names(s, col[1]); /* if (irccommands[ecode].future & IRC_WHOERR) print_info(dest, s, "IRC_WHOERROR", session_name(s), col[0], col[1]); else */ print_info(dest, s, irccommands[ecode].name, session_name(s), col[0], chlist ? chlist : col[1], col[2], col[3], col[4]); for (i=0; i<5; i++) xfree(col[i]); xfree(chlist); xfree(t); return (0); } gatoi(IOK2(4), &secs); which = gatoi(IOK2(5), &timek_int); timek = (time_t)timek_int; /* GiM: Yes, I know what is modulo ;> */ mins = secs/60; secs -= (mins * 60); hours = mins/60; mins -= (hours * 60); days = hours/24; hours -= (days * 24); #define IOK3(x) (x)?(x):"" /* GiM: No, I'm not going to do the same in polish * it'd have to be more cases ;> */ str = days?saprintf("%d %s ", days, days==1?"day":"days"):NULL; tmp = hours?saprintf("%s %d %s ", IOK3(str), hours, hours==1?"hour":"hours"):str; if (hours) xfree(str); str=tmp; tmp = mins?saprintf("%s %d %s ", IOK3(str), mins, mins==1?"minute":"minutes"):str; if (mins) xfree(str); str=tmp; tmp = secs?saprintf("%s %d %s ", IOK3(str), secs, secs==1?"second":"seconds"):str; if (secs) xfree(str); str=tmp; if (!str) str = xstrdup("Incredible, no idle!"); tmp = xstrdup(ctime(&timek)); if (tmp && tmp[xstrlen(tmp)-1] == '\n') tmp[xstrlen(tmp)-1]='\0'; print_info(dest, s, irccommands[ecode].name, session_name(s), IOK(3), str, which?"N/A":tmp); xfree(t); xfree(str); xfree(tmp); return 0; } int mode_act = 0; /** * irc_c_list - this function is only for evil hackers, * do not touch this stuff unless you want yourself get burned :> * * handling some list stuff * STATS, WHO, LIST, LINKS, IVITELIST, EXCEPTLIST, BANLIST */ IRC_COMMAND(irc_c_list) { #define PRINT_INFO if (!chan || !chan->syncmode) print_info char *dest, *t = NULL; int ltype = irccommands[ecode].future; int endlist = ltype & IRC_LISTEND; char *realname; char *coloured = NULL; char *tmpchn = NULL; window_t *w = NULL; people_t *osoba = NULL; channel_t *chan = NULL; list_t *tlist = NULL; if (endlist) ltype -= IRC_LISTEND; if (ltype == IRC_LISTWHO || ltype == IRC_LISTCHA || ltype == IRC_LISTSTA) t = NULL; else { IRC_TO_LOWER(IOK(3)); t = irc_uid(IOK(3)); } w = window_find_s(s, t); dest = w?t:NULL; if (ltype == IRC_LISTWHO || ltype == IRC_LISTBAN) { IRC_TO_LOWER(IOK(3)); chan = irc_find_channel(j->channels, IOK(3)); /* debug("!!!> %s %08X %d %d\n", IOK(3), chan, chan?chan->syncmode:-1, ltype); */ } if (!mode_act && ltype != IRC_LISTCHA) PRINT_INFO(dest, s, "RPL_LISTSTART", session_name(s)); if (endlist) { if (!mode_act) PRINT_INFO(dest, s, "RPL_EMPTYLIST", session_name(s), IOK(3)); if (ltype == IRC_LISTSTA) { print_info(dest, s, "RPL_STATSEND", session_name(s), IOK2(4), IOK2(3)); } else if (ltype == IRC_LISTCHA) { print_info(dest, s, "RPL_ENDOFLIST", session_name(s), IOK2(3)); } else { PRINT_INFO(dest, s, "RPL_ENDOFLIST", session_name(s), IOK2(4)); } if (chan) { if (chan->syncmode > 0) { chan->syncmode--; if (chan->syncmode == 0) { tmpchn = clean_channel_names(s, chan->name+4); GTimeVal tv; g_get_current_time(&tv); tv.tv_usec+=(1000000-chan->syncstart.tv_usec); if (tv.tv_usec>1000000) tv.tv_sec++, tv.tv_usec-=1000000; tv.tv_sec-=chan->syncstart.tv_sec; print_info(dest, s, "IRC_CHANNEL_SYNCED", session_name(s), tmpchn, ekg_itoa(tv.tv_sec), ekg_itoa(tv.tv_usec)); } } } mode_act = 0; } else { if (irccommands[ecode].num != 321) mode_act++; switch (ltype) { /* TODO: poprawic te 2 pierwsze... */ case (IRC_LISTSTA): print_info(dest, s, irccommands[ecode].name, session_name(s), ekg_itoa(mode_act), IOK2(3), IOK2(4), IOK(5), IOK(6), IOK(7), IOK(8)); break; case (IRC_LISTWHO): /* ok new irc-find-person checked */ osoba = irc_find_person(j, j->people, IOK(7)); realname = xstrchr(IOK2(9), ' '); tmpchn = clean_channel_names(s, IOK2(3)); PRINT_INFO(dest, s, irccommands[ecode].name, session_name(s), ekg_itoa(mode_act), tmpchn, IOK2(4), IOK(5), IOK(6), IOK(7), IOK(8), realname); if (osoba) { xfree(osoba->host); osoba->host = xstrdup(IOK(5)); xfree(osoba->ident); osoba->ident= xstrdup(IOK(4)); xfree(osoba->realname); osoba->realname = xstrdup(realname); if (chan && chan->syncmode) irc_access_parse(s, chan, osoba, 0); } break; /* case (IRC_LISTCHA): // TODO: /join #number (?) tlist = ... case (IRC_LISTINV): tlist = ... case (IRC_LISTEXC): tlist = ... */ case (IRC_LISTBAN): if (!tlist) tlist = &(chan->banlist); if (chan) { if (mode_act == 1 && *tlist) { debug("[IRC_LIST] Delete list 0x%x\n", *tlist); list_destroy(*tlist, 0); *tlist = NULL; } /*debug("[IRC_LIST] Add to list (id=%d; co=%s) 0x%x\n", mode_act, IOK2(4), tlist);*/ list_add(tlist, xstrdup(IOK2(4))); } default: if (param[5] && *param[5] == ':') { coloured = irc_ircoldcolstr_to_ekgcolstr(s, param[5]+1, 1); PRINT_INFO(dest, s, irccommands[ecode].name, session_name(s), IOK(3), IOK2(4), coloured, ekg_itoa(mode_act)); } else { tmpchn = clean_channel_names(s, IOK2(3)); PRINT_INFO(dest, s, irccommands[ecode].name, session_name(s), tmpchn, IOK2(4), IOK2(5), ekg_itoa(mode_act)); } xfree(coloured); break; } } xfree(tmpchn); xfree(t); return 0; #undef PRINT_INFO } #undef IOK #undef IOK2 /* p[0] - PING * p[1] - (:server|:something) ;> */ IRC_COMMAND(irc_c_ping) { ekg_connection_write(j->send_stream, "PONG %s\r\n", param[2]); if (session_int_get(s, "DISPLAY_PONG")) print_info("__status", s, "IRC_PINGPONG", session_name(s), OMITCOLON(param[2])); return 0; } /* p[0] - :nick!ident@host * p[1] - NICK * p[2] - :newnick */ IRC_COMMAND(irc_c_nick) { char *t, *temp; char *nick, *ihost, *newnick; int nickdisp = session_int_get(s, "DISPLAY_NICKCHANGE"); people_t *per; people_chan_t *ch; list_t l; window_t *w; if ((t = xstrchr(param[0], '!'))) *t ='\0'; nick = OMITCOLON(param[0]); ihost = t?t+1:""; newnick = OMITCOLON(param[2]); /* debug("irc_nick> %s %s\n", j->nick, param[0]+1); */ irc_nick_change(s, j, nick, newnick); if (!xstrcmp(j->nick, nick)) { print_info(window_current->target, s, "IRC_YOUNEWNICK", session_name(s), ihost, newnick); xfree(j->nick); j->nick = xstrdup(newnick); } else { /* ok new irc-find-person checked */ per = irc_find_person(j, j->people, newnick); debug_function("[irc]_c_nick %08X %s\n", per, nick); if (nickdisp || !per) print_info(nickdisp==2?window_current->target:"__status", s, "IRC_NEWNICK", session_name(s), nick, ihost, newnick); else if (per) { for (l = per->channels; l; l=l->next) { ch = (people_chan_t *)l->data; print_info(ch->chanp->name, s, "IRC_NEWNICK", session_name(s), nick, ihost, newnick); } } temp = irc_uid(nick); if ((w = window_find_s(s, temp))) { xfree(w->target); w->target = irc_uid(newnick); query_emit(NULL, "ui-window-target-changed", &w); print_window_w(w, EKG_WINACT_JUNK, "IRC_NEWNICK", session_name(s), nick, ihost, newnick); } xfree(temp); } if (t) *t='!'; return 0; } /* p[0] - :nick!ident@host * p[1] - PRIVMSG | NOTICE * p[2] - destination (channel|nick) * p[3] - :message * b */ IRC_COMMAND(irc_c_msg) { char *dest, *identhost, *format; char *head, *sender; char *ctcpstripped, *coloured, prefix[2]; int class, ekgbeep= EKG_NO_BEEP; int mw, prv; window_t *w = NULL; people_t *person; people_chan_t *perchn = NULL; int secure = 0, xosd_to_us = 0, xosd_is_priv = 0; char *ignore_nick = NULL; char *recoded, *recipient, *clear_string; prv = !xstrcasecmp(param[1], "privmsg"); if (!prv && xstrcasecmp(param[1], "notice")) return 0; mw = session_int_get(s, "make_window"); irc_parse_nick_identhost(OMITCOLON(param[0]), &sender, &identhost); recipient = xstrdup(param[2]); /* destination (channel|nick) */ recoded = g_strdup(OMITCOLON(param[3])); /* probably message from server ... */ if (s->connecting && !prv) { /* (!xstrcmp(":_empty_", param[0]) || !xstrcmp("AUTH", param[2])) */ class = (mw&16)?EKG_MSGCLASS_CHAT:EKG_MSGCLASS_MESSAGE; dest = xstrdup(recipient); format = xstrdup("irc_not_f_server"); xosd_to_us = 1; /* priv_data message ... */ } else if (!xstrcmp(j->nick, recipient)) { /* dj: if he's not on the list we should add him */ /* G->dj: okey, but must be done in other way imho * this 'param[0]' as a channel doesn't like nice to me... person = irc_find_person(j->people, param[0]+1); if (!person) person = irc_add_person(s, j, param[0]+1, NULL); if (person && t) irc_parse_ident_host(t+1, &(person->ident), &(person->host)); */ class = (mw&2)?EKG_MSGCLASS_CHAT:EKG_MSGCLASS_MESSAGE; dest = irc_uid(sender); format = xstrdup(prv?"irc_msg_f_some":"irc_not_f_some"); ekgbeep = EKG_TRY_BEEP; xosd_to_us = xosd_is_priv = 1; /* message on channel ... */ } else { class = EKG_MSGCLASS_CHAT; // class = (mw&1)?EKG_MSGCLASS_CHAT:EKG_MSGCLASS_MESSAGE; dest = irc_uid(recipient); IRC_TO_LOWER(dest+4); w = window_find_s(s, dest); format = NULL; /* ok new irc-find-person checked */ if (irc_config_allow_fake_contacts && !(person = irc_find_person(j, j->people, sender))) { person = irc_add_person(s, j, sender, dest); } if ((person = irc_find_person(j, j->people, sender))) { /* G->dj: I'm not sure if this what I've added * will still do the same you wanted */ if (*identhost && !(person->ident) && !(person->host)) irc_parse_ident_host(identhost, &(person->ident), &(person->host)); perchn = irc_find_person_chan(person->channels, dest); debug("channels: %08X %s %08X>\n", person->channels, dest, perchn); } } query_emit(NULL, prv ? "irc-privmsg" : "irc-notice", &(s->uid), &sender, &recipient, &recoded, &xosd_to_us); if (!xosd_to_us) { /* find our nick */ char *p, *str, *str0; str0 = str = irc_ircoldcolstr_juststrip(s, recoded); while (!xosd_to_us && (p = xstrcasestr(str, j->nick))) { /* p - points to beginning of a nickname */ char end = p[xstrlen(j->nick)]; /* end - char after end of a nickname */ if (!isalnum(end) && !isalpha_pl(end)) { /* End of nick is OK */ if (p == str0 || (!isalnum(*(p-1)) && !isalpha_pl(*(p-1)) && *(p-1)!=1)) { /* nick found */ ekgbeep = EKG_TRY_BEEP; xosd_to_us = 1; } } str = p + 1; } xfree(str0); } if ((ctcpstripped = ctcp_parser(s, prv, sender, recipient, recoded, xosd_to_us))) { char *padding = NULL; int isour = 0; if (xosd_is_priv) /* @ wrong place */ query_emit(NULL, "message-decrypt", &(s->uid), &dest, &ctcpstripped, &secure , NULL); else query_emit(NULL, "message-decrypt", &dest, &(s->uid), &ctcpstripped, &secure , NULL); /* TODO 'secure' var checking, but still don't know how to react to it (GiM) */ coloured = irc_ircoldcolstr_to_ekgcolstr(s, ctcpstripped, 1); clear_string = irc_ircoldcolstr_juststrip(s, ctcpstripped); debug("<%c%s/%s> %s [%s]\n", perchn?*(perchn->sign):' ', sender, recipient, OMITCOLON(param[3]), clear_string); xfree(clear_string); prefix[1] = '\0'; prefix[0] = perchn?*(perchn->sign):' '; if (*prefix==' ' && !session_int_get(s, "SHOW_NICKMODE_EMPTY")) *prefix='\0'; if (perchn) padding = nickpad_string_apply (perchn->chanp, sender); /* privmsg on channel */ if (NULL == format) { if (xosd_to_us) ekgbeep = EKG_TRY_BEEP; /* rest */ /* privmsg <--> notice * w -> window not yet created (other format) * ekgbeep -> higlight format or normal */ format = saprintf("irc_%s_f_chan%s%s", prv?"msg":"not", (!w)?"":"_n", ekgbeep?"h":""); if (!xosd_to_us) class |= EKG_MSGCLASS_NOT2US; } head = format_string(format_find(format), session_name(s), prefix, sender, identhost, recipient, coloured, padding, "Y "); if (perchn) nickpad_string_restore (perchn->chanp); xfree(coloured); coloured = irc_ircoldcolstr_to_ekgcolstr(s, ctcpstripped, 0); /* 234707 <@dredzik> GiM, string nadawca, string wiadomo, bool 234707 wiadomo_do_ciebie, bool kana_czy_priv, string 234707 jeeli_kana_to_nazwa_kanau 010539 <@dredzik> GiM, hm... jeszcze by si przydaa jedna rzecz - tak eby 010539 pierwszym argumentem bya sesja isour - 0 tutaj czy wiadomosc jest od nas. irc-protocol-message uid, nick, isour, istous, ispriv, dest. */ query_emit(NULL, "irc-protocol-message", &(s->uid), &sender, &coloured, &isour, &xosd_to_us, &xosd_is_priv, &dest); ignore_nick = irc_uid(sender); if (xosd_to_us && s->status == EKG_STATUS_AWAY && session_int_get(s, "away_log") == 1 && !(ignored_check(s, ignore_nick) & IGNORE_MSG)) { irc_awaylog_t *e = xmalloc(sizeof(irc_awaylog_t)); if (xosd_is_priv) { e->channame = NULL; e->uid = xstrdup(dest); } else { e->uid = irc_uid(sender); e->channame = xstrdup(dest); } e->msg = xstrdup(coloured); e->t = time(NULL); list_add(&(j->awaylog), e); } xfree(ctcpstripped); xfree(coloured); class |= EKG_NO_THEMEBIT; if (xosd_is_priv || !(ignored_check(s, ignore_nick) & IGNORE_MSG)) protocol_message_emit(s, dest, NULL, head, NULL, time(NULL), class, NULL, ekgbeep, secure); xfree(ignore_nick); xfree(head); } xfree(recoded); xfree(sender); xfree(recipient); xfree(dest); xfree(identhost); xfree(format); return 0; } /* p[0] - :nick!ident@host * p[1] - JOIN * p[2] - :channel * */ IRC_COMMAND(irc_c_join) { char *ekg2_channel, *chname, *irc_nick; char *__channel, *__identhost, *__nick; window_t *newwin; people_t *person; int me = 0; irc_parse_nick_identhost(param[0]+1, &__nick, &__identhost); /* irc channels are said to be case insensitive, so I think * we can do it 'in place', without a copy */ __channel = xstrdup(IRC_TO_LOWER(OMITCOLON(param[2]))); /* istnieje jakatam szansa e kto zrobi nick i part i bdzie * but I have no head to this now... */ me = !xstrcmp(j->nick, __nick); /* We join ? */ if (query_emit(NULL, "irc-join", &s->uid, &__channel, &__nick, &me, &__identhost) == -1) { xfree(__channel); xfree(__identhost); xfree(__nick); return -1; } irc_nick = irc_uid(__nick); ekg2_channel = irc_uid(__channel); chname = clean_channel_names(s, __channel); if (me) { newwin = window_new(ekg2_channel, s, 0); if (xstrcmp(__channel, chname)) newwin->alias = xstrdup(chname); else newwin->alias = xstrdup(__channel); query_emit(NULL, "ui-window-target-changed", &newwin); /* let's emit UI_WINDOW_TARGET_CHANGED XXX, another/new query? */ window_switch(newwin->id); debug_function("[irc] c_join() %08X\n", newwin); irc_add_channel(s, j , __channel, newwin); /* someone joined */ } else { person = irc_add_person(s, j, __nick, __channel); if (person && __identhost && !(person->ident) && !(person->host)) irc_parse_ident_host(__identhost, &(person->ident), &(person->host)); irc_access_parse(s, irc_find_channel(j->channels, __channel), person, 0); } if (!(ignored_check(s, ekg2_channel) & IGNORE_NOTIFY) && !(ignored_check(s, irc_nick) & IGNORE_NOTIFY)) { print_info(ekg2_channel, s, me ? "irc_joined_you" : "irc_joined", session_name(s), __nick, __identhost, chname); if (me) { int __secure = 0; char *__sid = xstrdup(session_uid_get(s)); char *__uid_full = xstrdup(ekg2_channel); char *__msg = xstrdup("test"); if (query_emit(NULL, "message-encrypt", &__sid, &__uid_full, &__msg, &__secure) == 0 && __secure) print_info(ekg2_channel, s, "channel_secure", session_name(s), chname); else print_info(ekg2_channel, s, "channel_unsecure", session_name(s), chname); xfree(__msg); xfree(__uid_full); xfree(__sid); } } xfree(chname); xfree(irc_nick); xfree(ekg2_channel); xfree(__channel); xfree(__identhost); xfree(__nick); return 0; } /* p[0] - :nick!ident@host * p[1] - PART * p[2] - channel * (p[3] - :reason) - optional */ IRC_COMMAND(irc_c_part) { char *ekg2_channel, *coloured, *irc_nick; char *__channel, *__identhost, *__nick, *__reason; int me = 0; irc_parse_nick_identhost(param[0]+1, &__nick, &__identhost); debug_function("[irc]_c_part: %s %s\n", j->nick, __nick); __channel = xstrdup(IRC_TO_LOWER(OMITCOLON(param[2]))); __reason = param[3] ? xstrdup(OMITCOLON(param[3])) : NULL; me = !xstrcmp(j->nick, __nick); /* we part ? */ if (query_emit(NULL, "irc-part", &s->uid, &__channel, &__nick, &me, &__identhost, &__reason) == -1) { xfree(__channel); xfree(__identhost); xfree(__nick); xfree(__reason); return -1; } ekg2_channel = irc_uid(__channel); irc_nick = irc_uid(__nick); /* Servers MUST be able to parse arguments in the form of * a list of target, but SHOULD NOT use lists when sending * PART messages to clients. * * damn it I think rfc should rather say MUSTN'T instead of * SHOULD NOT ;/ */ if (me) irc_del_channel(s, j, __channel); else irc_del_person_channel(s, j, __nick, __channel); coloured = __reason ? *__reason ? irc_ircoldcolstr_to_ekgcolstr(s, __reason, 1) : xstrdup("no reason") : xstrdup("no reason"); /* TODO: if channel window exists do print_info, else do nothing (?) * now after alt+k if user was on that channel-window, we recved info * about parting on __status window, is it right ? * G->dj: yep, but we can make this behaviour dependent on something * e.g: on my fave: DISPLAY_IN_CURRENT :) */ if (!(ignored_check(s, ekg2_channel) & IGNORE_NOTIFY) && !(ignored_check(s, irc_nick) & IGNORE_NOTIFY)) { char *cchn = clean_channel_names(s, __channel); print_info(ekg2_channel, s, (me)?"irc_left_you":"irc_left", session_name(s), __nick, __identhost, cchn, coloured); xfree(cchn); } xfree(coloured); xfree(ekg2_channel); xfree(irc_nick); xfree(__channel); xfree(__identhost); xfree(__nick); xfree(__reason); return 0; } /* this is quite similiar to PART * p[0] - :nick!ident@host * p[1] - KICK * p[2] - channel * p[3] - nick * (p[4] - :reason) - optional */ IRC_COMMAND(irc_c_kick) { char *ekg2_channel, *irc_channel, *tmp, *uid, *coloured, *cchn; char *_session, *_nick; int me = !xstrcmp(j->nick, param[3]); if ((tmp = xstrchr(param[0], '!'))) *tmp = '\0'; irc_channel = IRC_TO_LOWER(OMITCOLON(param[2])); ekg2_channel = irc_uid(irc_channel); /* we were kicked out */ if (me) irc_del_channel(s, j, irc_channel); else irc_del_person_channel(s, j, OMITCOLON(param[3]), irc_channel); uid = irc_uid(param[0]+1); if (tmp) *tmp='!'; coloured = param[4]?xstrlen(OMITCOLON(param[4]))? irc_ircoldcolstr_to_ekgcolstr(s, OMITCOLON(param[4]), 1): xstrdup("no reason"):xstrdup("no reason"); cchn = clean_channel_names(s, irc_channel); /* session, kicked_nick, kicker_nick, kicker_ident+host, chan, reason */ print_info(ekg2_channel, s, me ? "irc_kicked_you" : "irc_kicked", session_name(s), OMITCOLON(param[3]), uid+4, tmp?tmp+1:"", cchn, coloured); xfree(coloured); /*sending irc-kick event*/ _session = xstrdup(session_uid_get(s)); _nick = irc_uid(OMITCOLON(param[3])); query_emit(NULL, "irc-kick", &_session, &_nick, &ekg2_channel, &uid); xfree(_nick); xfree(_session); xfree(cchn); xfree(ekg2_channel); xfree(uid); return 0; } /* p[0] - :nick!ident@ihost * p[1] - QUIT * (p[2]) - reason */ IRC_COMMAND(irc_c_quit) { char *__identhost, *__nick, *__reason; int display_quit, me; /* TODO: SPLIT MODE! */ int split = 0; irc_parse_nick_identhost(param[0]+1, &__nick, &__identhost); __reason = param[2]?xstrlen(OMITCOLON(param[2]))? irc_ircoldcolstr_to_ekgcolstr(s, OMITCOLON(param[2]), 1): xstrdup("no reason"):xstrdup("no reason"); me = !xstrcmp(j->nick, __nick); /* we quit? */ if (query_emit(NULL, "irc-quit", &s->uid, &__nick, &me, &__identhost, &__reason) == -1) { xfree(__identhost); xfree(__nick); xfree(__reason); return -1; } if (split) display_quit = 0; /* (?) */ else display_quit = session_int_get(s, "DISPLAY_QUIT"); irc_del_person(s, j, __nick, __identhost, __reason, !display_quit); if (display_quit) print_info(display_quit==2?window_current->target:"__status", s, (split)?"irc_split":"irc_quit", session_name(s), __nick, __identhost, __reason); xfree(__identhost); xfree(__nick); xfree(__reason); return 0; } /* * p[0] :server * p[1] 353 RPL_NAMREPLY * p[2] nick * p[3] ( '='/'*'/'@' ) '@' is used for secret channels, * '*' for private channels * '=' for others (public channels) * p[4] channel * p[5] :names */ IRC_COMMAND(irc_c_namerpl) { if (!param[3]) return -1; /* rfc2812 */ if (*param[3] != '*' && *param[3] != '=' && *param[3] != '@') { debug_error("[irc] c_namerpl() kindda shitty ;/\n"); return -1; } if (!param[5]) { debug_error("[irc] c_namerpl() even more shitty!\n"); return -1; } irc_add_people (s, j, OMITCOLON(param[5]), IRC_TO_LOWER(param[4])); return 0; } /* * p[2] - channel */ IRC_COMMAND(irc_c_topic) { window_t *w; char *t, *dest=NULL; char *coloured, *cchn, *ihost; char *__topic, *__topicby; channel_t *chanp = NULL; IRC_TO_LOWER(param[2]); t = irc_uid(param[2]); w = window_find_s(s, t); chanp = irc_find_channel(j->channels, param[2]); dest = w?w->target:NULL; xfree(t); xfree(chanp->topic); xfree(chanp->topicby); __topicby = OMITCOLON(param[0]); if ((t = xstrchr(__topicby, '!'))) *t = '\0'; ihost = t ? t+1 : ""; __topic = OMITCOLON(param[3]); cchn = clean_channel_names(s, param[2]); if (xstrlen(__topic)) { chanp->topic = g_strdup(__topic); chanp->topicby = xstrdup(__topicby); coloured = irc_ircoldcolstr_to_ekgcolstr(s, chanp->topic, 1); print_info(dest, s, "IRC_TOPIC_CHANGE", session_name(s), __topicby, ihost, cchn, coloured); xfree(coloured); } else { chanp->topic = xstrdup("No topic set!"); chanp->topicby = xstrdup(__topicby); print_info(dest, s, "IRC_TOPIC_UNSET", session_name(s), __topicby, ihost, cchn); } if (t) *t='!'; xfree(cchn); return 0; } #ifdef GDEBUG #ifndef MARLENE #error She's all I really care about. You shouldn't play with my GDEBUG! #endif #endif /* p[0] - :nick!ident@ihost * p[1] - INVITE * p[2] - our nickname * p[3] - :channel */ /* TODO: add the person, that invites us, to list ? */ IRC_COMMAND(irc_c_invite) { char *tmp, *cchn, *nick, *ihost, *channel; nick = OMITCOLON(param[0]); if ((tmp = xstrchr(nick, '!'))) *tmp = '\0'; ihost = tmp?tmp+1:""; channel = OMITCOLON(param[3]); IRC_TO_LOWER(channel); cchn = clean_channel_names(s, channel); print_info(window_current->target, s, "IRC_INVITE", session_name(s), nick, ihost, param[2], cchn); xfree(cchn); if (session_int_get(s, "AUTO_JOIN_CHANS_ON_INVITE") == 1) ekg_connection_write(j->send_stream, "JOIN %s\r\n", channel); if (tmp) *tmp = '!'; return 0; } IRC_COMMAND(irc_c_mode) { int k, act=1, is324=irccommands[ecode].num==324; char *t, *bang, **pars, *ekg2_channame, *irc_channame, *mode_abcd, *mode_c, *mode_d=NULL, *cchn; people_t *per; people_chan_t *ch; channel_t *chan; userlist_t *ul; window_t *w; string_t moderpl; /* MODE * */ /* GiM: FIXME TODO [this shouldn't be xstrcasecmp! user mode * [well now [o3:o5:o4 CET 2oo8-16-o2] I think it's ok] */ if (is324) { param = &(param[1]); } else if (!xstrcasecmp(param[2], j->nick)) { print_info(window_current->target, s, "IRC_MODE", session_name(s), param[0]+1, IRC_TO_LOWER(OMITCOLON(param[3])) ); return 0; } irc_channame = IRC_TO_LOWER(param[2]); ekg2_channame = irc_uid(irc_channame); cchn = clean_channel_names(s, irc_channame); /* chan modes */ mode_abcd = SOP(_005_CHANMODES); mode_c = mode_d = mode_abcd + xstrlen(mode_abcd); if ( (mode_c = xstrchr(mode_abcd, ',')) && ++mode_c) if ( (mode_c = xstrchr(mode_c, ',')) && ++mode_c) if ((mode_d=xstrchr(mode_c, ','))) mode_d++; for (t=param[3], k=4; *t && xstrlen(param[k]); t++) { char * __mode, *__param, *__p0 = param[0]+1; if (*t=='+' || *t=='-') { act = ('+' == *t); continue; } /* 23:26:o2 CET 2oo5-22-o1 yet another ivil hack */ if (xstrchr(param[k], ' ')) *xstrchr(param[k], ' ') = '\0'; __param = param[k]; if ((bang = xstrchr(mode_abcd, *t))) { if ( (bang >= mode_d) || /* mode D never has a parameter */ ((bang >= mode_c) && !act) ) /* mode C only has a parameter when set */ { __param = NULL; } else { /* modes A & B always has a parameter */ k++; } __mode = xstrndup(t, 1); query_emit(NULL, "irc-mode", &s->uid, &__p0, &irc_channame, &act, &__mode, &__param); xfree(__mode); continue; } /* Modes in PREFIX are not listed but could be considered type B. */ if (!(bang=xstrchr(j->nick_modes, *t))) { debug_error("irc_c_mode() - unknown mode '%c'\n", *t); continue; } __mode = xstrndup(t, 1); query_emit(NULL, "irc-mode", &s->uid, &__p0, &irc_channame, &act, &__mode, &__param); xfree(__mode); if ((per = irc_find_person(j, j->people, param[k])) && (ch = irc_find_person_chan(per->channels, irc_channame)) ) { int mask = 1 << (bang - j->nick_modes); if (act) ch->mode |= mask; else ch->mode &=~mask; if ((ul = userlist_find_u(&(ch->chanp->window->userlist), param[k]))) { irc_nick_prefix(j, ch, irc_color_in_contacts(j, ch->mode, ul)); query_emit(NULL, "userlist-refresh"); } } k++; } w = window_find_s(s, ekg2_channame); bang = xstrchr(param[0], '!'); if (bang) *bang='\0'; moderpl = string_init(""); pars=&(param[3]); while (*pars) { string_append(moderpl, *pars++); if (*pars) string_append_c(moderpl, ' '); } if (!is324) { print_info(w?w->target:NULL, s, "IRC_MODE_CHAN_NEW", session_name(s), param[0]+1, bang?bang+1:"", cchn, moderpl->str); /* if (moderpl->str[1] == 'b') * ekg_connection_write(j->send_stream, "MODE %s +%c\r\n", irc_channame, moderpl->str[1]); */ } else { print_info(w?w->target:NULL, s, "IRC_MODE_CHAN", session_name(s), cchn, moderpl->str); if ((chan = irc_find_channel(j->channels, irc_channame))) { xfree(chan->mode_str); chan->mode_str = xstrdup(moderpl->str); } } if (bang) *bang='!'; string_free(moderpl, 1); xfree(cchn); xfree(ekg2_channame); return 0; } /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/plugins/irc/misc.h000066400000000000000000000331031175142753400175170ustar00rootroot00000000000000/* * (C) Copyright 2004-2005 Michal 'GiM' Spadlinski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __EKG_PLUGINS_IRC_MISC_H #define __EKG_PLUGINS_IRC_MISC_H #include #include "irc.h" #define IRC_COMMAND(x) static int x(session_t *s, irc_private_t *j, int fd, int ecode, char **param) typedef int (*Irc_Cmd) (session_t * , irc_private_t * , int , int , char **); #define IRC_LISTBAN 0x001 #define IRC_LISTEXC 0x002 #define IRC_LISTINV 0x004 #define IRC_LISTLIN 0x008 #define IRC_LISTSTA 0x010 #define IRC_LISTWHO 0x020 #define IRC_LISTCHA 0x040 #define IRC_LISTEND 0x080 #define IRC_WHOIS 0x001 #define IRC_WHOWAS 0x002 #define IRC_WHOERR 0x004 enum { IRC_ERR_12=0, IRC_ERR_21, IRC_ERR_ONLY1, IRC_ERR_NEW, IRC_ERR_IGNO, IRC_ERR_OTHER, IRC_RPL_12=256, IRC_RPL_21, IRC_RPL_ONLY1, IRC_RPL_NEW, IRC_RPL_IGNO, IRC_RPL_OTHER }; typedef struct { int type; int num; const char *comm; const char *name; Irc_Cmd handler; int future; } IrcCommand; IRC_COMMAND(irc_c_init); IRC_COMMAND(irc_c_invite); IRC_COMMAND(irc_c_ping); IRC_COMMAND(irc_c_nick); IRC_COMMAND(irc_c_msg); IRC_COMMAND(irc_c_join); IRC_COMMAND(irc_c_part); IRC_COMMAND(irc_c_kick); IRC_COMMAND(irc_c_quit); IRC_COMMAND(irc_c_error); IRC_COMMAND(irc_c_list); IRC_COMMAND(irc_c_namerpl); IRC_COMMAND(irc_c_mode); IRC_COMMAND(irc_c_topic); IRC_COMMAND(irc_c_whois); /* 1st - 1 if reply in numeric form, 0 if as a string * 1st== 1 | 0 * 2nd - code | 0 * 3rd - NULL | command * 4th - name from rfc and stylename | command name * 5th - function handler | function handler * 6th - 0 or one of enum's above... * IRC_ERR if error, IRC_RPL if reply [used to determine style * of display] * * for simple numeric replies [and for ERROR] exsist one function * irc_c_error * * first it checks style and if =NOT= *_OTHER or *_IGNO [ignore] * it displays some information * if *_OTHER is used reply is treated according to its code * starting with line switch(i), where i is code * * if you add something that is not currently on the list * give somewhere [*] in comment */ static const IrcCommand irccommands[] = { { 1, -1, NULL, NULL, &irc_c_error, IRC_ERR_NEW }, { 1, 1, NULL, "RPL_WELCOME", &irc_c_init, 0 }, { 1, 2, NULL, "RPL_YOURHOST", &irc_c_init, 0 }, { 1, 3, NULL, "RPL_CREATED", &irc_c_init, 0 }, { 1, 4, NULL, "RPL_MYINFO", &irc_c_init, 0 }, { 1, 5, NULL, "RPL_BOUNCE", &irc_c_init, 0 }, /* { 1, 200, NULL, "RPL_TRACELINK", &irc_c_error, { 1, 201, NULL, "RPL_TRACECONNECTING", &irc_c_error, { 1, 202, NULL, "RPL_TRACEHANDSHAKE", &irc_c_error, { 1, 203, NULL, "RPL_TRACEUNKNOWN", &irc_c_error, { 1, 204, NULL, "RPL_TRACEOPERATOR", &irc_c_error, { 1, 205, NULL, "RPL_TRACEUSER", &irc_c_error, { 1, 206, NULL, "RPL_TRACESERVER", &irc_c_error, { 1, 207, NULL, "RPL_TRACESERVICE", &irc_c_error, { 1, 208, NULL, "RPL_TRACENEWTYPE", &irc_c_error, { 1, 209, NULL, "RPL_TRACECLASS", &irc_c_error, { 1, 210, NULL, "RPL_TRACERECONNECT", &irc_c_error, { 1, 261, NULL, "RPL_TRACELOG", &irc_c_error, { 1, 262, NULL, "RPL_TRACEEND", &irc_c_error, */ /* { 1, 211, NULL, "RPL_STATSLINKINFO", &irc_c_error, { 1, 212, NULL, "RPL_STATSCOMMANDS", &irc_c_error, { 1, 219, NULL, "RPL_ENDOFSTATS", &irc_c_error, { 1, 242, NULL, "RPL_STATSUPTIME", &irc_c_error, { 1, 243, NULL, "RPL_STATSOLINE", &irc_c_error, */ /*[ ] /stats M -> modules */ { 1, 212, NULL, "RPL_STATS", &irc_c_list, IRC_LISTSTA}, /*[*] /stats C -> connect() */ { 1, 213, NULL, "RPL_STATS_EXT", &irc_c_list, IRC_LISTSTA}, /*[*] /stats I -> I:lines */ { 1, 215, NULL, "RPL_STATS_EXT", &irc_c_list, IRC_LISTSTA}, /*[*] /stats K -> K:lines */ { 1, 216, NULL, "RPL_STATS_EXT", &irc_c_list, IRC_LISTSTA}, /*[*] /stats Y -> classes */ { 1, 218, NULL, "RPL_STATS", &irc_c_list, IRC_LISTSTA}, { 1, 219, NULL, "RPL_STATSEND", &irc_c_list, IRC_LISTSTA|IRC_LISTEND }, /*[*] /stats P -> ports */ { 1, 220, NULL, "RPL_STATS_EXT", &irc_c_list, IRC_LISTSTA}, /*[*] /stats A */ { 1, 226, NULL, "RPL_STATS", &irc_c_list, IRC_LISTSTA}, /*[ ] /stats u -> uptime */ { 1, 242, NULL, "RPL_STATS", &irc_c_list, IRC_LISTSTA}, /*[ ] /stats O -> O:lines ; P -> aktywni */ { 1, 243, NULL, "RPL_STATS_EXT", &irc_c_list, IRC_LISTSTA}, /*[*] /stats H -> */ { 1, 244, NULL, "RPL_STATS", &irc_c_list, IRC_LISTSTA}, /*[*] /stats F, R, T, Z, ? */ { 1, 249, NULL, "RPL_STATS", &irc_c_list, IRC_LISTSTA}, /* { 1, 221, NULL, "RPL_UMODEIS", &irc_c_error, { 1, 234, NULL, "RPL_SERVLIST", &irc_c_error, { 1, 235, NULL, "RPL_SERVLISTEND", &irc_c_error, { 1,250,NULL,"RPL_STATS",&irc_c_list,IRC_LISTSTA }, [*] { 1, 251, NULL, "RPL_LUSERCLIENT", &irc_c_error, { 1, 252, NULL, "RPL_LUSEROP", &irc_c_error, { 1, 253, NULL, "RPL_LUSERUNKNOWN", &irc_c_error, { 1, 254, NULL, "RPL_LUSERCHANNELS", &irc_c_error, { 1, 255, NULL, "RPL_LUSERME", &irc_c_error, */ /* { 1, 256, NULL, "RPL_ADMINME", &irc_c_error, { 1, 257, NULL, "RPL_ADMINLOC1", &irc_c_error, { 1, 258, NULL, "RPL_ADMINLOC2", &irc_c_error, { 1, 259, NULL, "RPL_ADMINEMAIL", &irc_c_error, */ { 1, 263, NULL, "RPL_TRYAGAIN", &irc_c_error, IRC_ERR_ONLY1}, /* { 1, 302, NULL, "RPL_USERHOST", &irc_c_error, { 1, 303, NULL, "RPL_ISON", &irc_c_error, */ { 1, 301, NULL, "RPL_AWAY", &irc_c_error, IRC_RPL_OTHER}, { 1, 305, NULL, "RPL_UNAWAY", &irc_c_error, IRC_RPL_ONLY1}, { 1, 306, NULL, "RPL_NOWAWAY", &irc_c_error, IRC_RPL_ONLY1}, { 1, 311, NULL, "RPL_WHOISUSER", &irc_c_whois,IRC_WHOIS}, { 1, 312, NULL, "RPL_WHOISSERVER", &irc_c_whois,IRC_WHOIS}, { 1, 313, NULL, "RPL_WHOISOPERATOR", &irc_c_whois,IRC_WHOIS}, { 1, 317, NULL, "RPL_WHOISIDLE", &irc_c_whois,IRC_WHOIS}, { 1, 318, NULL, "RPL_ENDOFWHOIS", &irc_c_whois,IRC_WHOIS}, { 1, 319, NULL, "RPL_WHOISCHANNELS", &irc_c_whois,IRC_WHOIS}, { 1, 320, NULL, "RPL_WHOISMISC", &irc_c_whois,IRC_WHOIS}, { 1, 314, NULL, "RPL_WHOWASUSER", &irc_c_whois,IRC_WHOWAS}, { 1, 369, NULL, "RPL_ENDOFWHOWAS", &irc_c_whois,IRC_WHOWAS}, /* G->dj I want to keep the names from rfc2812 */ { 1, 315, NULL, "RPL_ENDOFWHO", &irc_c_list, IRC_LISTWHO|IRC_LISTEND }, { 1, 352, NULL, "RPL_WHOREPLY", &irc_c_list, IRC_LISTWHO }, /* G->dj: with what it colides ?? */ /* { 1, 321, NULL, "RPL_LISTSTART", &irc_c_error, */ { 1, 321, NULL, "RPL_CHLISTSTART", &irc_c_list, IRC_LISTCHA }, { 1, 322, NULL, "RPL_LIST", &irc_c_list, IRC_LISTCHA }, { 1, 323, NULL, "RPL_LISTEND", &irc_c_list, IRC_LISTCHA|IRC_LISTEND }, /* { 1, 325, NULL, "RPL_UNIQOPIS", &irc_c_error, */ { 1, 324, NULL, "RPL_CHANNELMODEIS", &irc_c_mode, IRC_RPL_OTHER}, /* 331 is really RPL_NOTOPIC, but I don't want another format... */ { 1, 331, NULL, "RPL_TOPIC", &irc_c_error, IRC_RPL_OTHER}, { 1, 332, NULL, "RPL_TOPIC", &irc_c_error, IRC_RPL_OTHER}, /* [*] 333 not in rfc 2812 */ { 1, 333, NULL, "RPL_TOPICBY", &irc_c_error, IRC_RPL_OTHER}, { 1, 341, NULL, "RPL_INVITE", &irc_c_error, IRC_RPL_OTHER}, /* { 1, 443, G->dj: ??? */ /* { 1, 351, NULL, "RPL_VERSION", &irc_c_error, */ { 1, 353, NULL, "RPL_NAMREPLY", &irc_c_namerpl, 0 }, { 1, 364, NULL, "RPL_LINKS", &irc_c_list, IRC_LISTLIN }, { 1, 365, NULL, "RPL_ENDOFLINKS", &irc_c_list, IRC_LISTLIN|IRC_LISTEND }, { 1, 346, NULL, "RPL_INVITELIST", &irc_c_list, IRC_LISTINV }, { 1, 347, NULL, "RPL_ENDOFLIST", &irc_c_list, IRC_LISTINV|IRC_LISTEND }, { 1, 348, NULL, "RPL_EXCEPTLIST", &irc_c_list, IRC_LISTEXC }, { 1, 349, NULL, "RPL_ENDOFLIST", &irc_c_list, IRC_LISTEXC|IRC_LISTEND }, { 1, 366, NULL, "RPL_ENDOFNAMES", &irc_c_error, IRC_RPL_IGNO}, { 1, 367, NULL, "RPL_BANLIST", &irc_c_list, IRC_LISTBAN }, { 1, 368, NULL, "RPL_ENDOFBANLIST", &irc_c_list, IRC_LISTBAN|IRC_LISTEND }, /* { 1, 371, NULL, "RPL_INFO", &irc_c_error, */ { 1, 372, NULL, "RPL_MOTD", &irc_c_error, IRC_RPL_OTHER}, /* { 1, 374, NULL, "RPL_ENDOFINFO", &irc_c_error, */ { 1, 375, NULL, "RPL_MOTDSTART", &irc_c_error, IRC_RPL_OTHER}, { 1, 376, NULL, "RPL_ENDOFMOTD", &irc_c_error, IRC_RPL_OTHER}, /* { 1, 381, NULL, "RPL_YOUREOPER", &irc_c_error, { 1, 382, NULL, "RPL_REHASHING", &irc_c_error, { 1, 383, NULL, "RPL_YOURESERVICE", &irc_c_error, { 1, 391, NULL, "RPL_TIME", &irc_c_error, { 1, 392, NULL, "RPL_USERSSTART", &irc_c_error, { 1, 393, NULL, "RPL_USERS", &irc_c_error, { 1, 394, NULL, "RPL_ENDOFUSERS", &irc_c_error, { 1, 395, NULL, "RPL_NOUSERS", &irc_c_error, */ { 1, 401, NULL, "ERR_NOSUCHNICK", &irc_c_error, IRC_ERR_21 }, { 1, 402, NULL, "ERR_NOSUCHSERVER", &irc_c_error, IRC_ERR_21 }, { 1, 403, NULL, "ERR_NOSUCHCHANNEL", &irc_c_error, IRC_ERR_21 }, /* * G->dj: what ? why dya want to put this stuff there ? + { 1, 401, NULL, "ERR_NOSUCHNICK", &irc_c_whois, IRC_WHOIS | IRC_WHOERR }, + { 1, 402, NULL, "ERR_NOSUCHSERVER", &irc_c_whois, IRC_WHOIS | IRC_WHOERR }, + { 1, 403, NULL, "ERR_NOSUCHCHANNEL", &irc_c_whois, IRC_WHOIS | IRC_WHOERR }, + { 1, 406, NULL, "ERR_WASNOSUCHNICK", &irc_c_whois, IRC_WHOWAS | IRC_WHOERR }, + */ { 1, 404, NULL, "ERR_CANNOTSENDTOCHAN", &irc_c_error, IRC_ERR_OTHER }, { 1, 404, NULL, "ERR_CANNOTSENDTOCHAN", &irc_c_error, IRC_ERR_OTHER }, { 1, 405, NULL, "ERR_TOOMANYCHANNELS", &irc_c_error, IRC_ERR_12 }, { 1, 406, NULL, "ERR_WASNOSUCHNICK", &irc_c_error, IRC_ERR_21 }, { 1, 407, NULL, "ERR_TOOMANYTARGETS", &irc_c_error, IRC_ERR_12 }, { 1, 408, NULL, "ERR_NOSUCHSERVICE", &irc_c_error, IRC_ERR_21 }, { 1, 409, NULL, "ERR_NOORIGIN", &irc_c_error, IRC_ERR_ONLY1 }, { 1, 411, NULL, "ERR_NORECIPIENT", &irc_c_error, IRC_ERR_ONLY1 }, { 1, 412, NULL, "ERR_NOTEXTTOSEND", &irc_c_error, IRC_ERR_ONLY1 }, { 1, 413, NULL, "ERR_NOTOPLEVEL", &irc_c_error, IRC_ERR_21 }, { 1, 414, NULL, "ERR_WILDTOPLEVEL", &irc_c_error, IRC_ERR_21 }, { 1, 415, NULL, "ERR_BADMASK", &irc_c_error, IRC_ERR_21 }, { 1, 421, NULL, "ERR_UNKNOWNCOMMAND", &irc_c_error, IRC_ERR_21 }, { 1, 422, NULL, "ERR_NOMOTD", &irc_c_error, IRC_ERR_ONLY1 }, { 1, 423, NULL, "ERR_NOADMININFO", &irc_c_error, IRC_ERR_12 }, { 1, 424, NULL, "ERR_FILEERROR", &irc_c_error, IRC_ERR_ONLY1 }, { 1, 431, NULL, "ERR_NONICKNAMEGIVEN", &irc_c_error, IRC_ERR_ONLY1 }, { 1, 432, NULL, "ERR_ERRONEUSNICKNAME", &irc_c_error, IRC_ERR_21 }, { 1, 433, NULL, "ERR_NICKNAMEINUSE", &irc_c_error, IRC_ERR_OTHER }, { 1, 436, NULL, "ERR_NICKCOLLISION", &irc_c_error, IRC_ERR_12 }, { 1, 437, NULL, "ERR_UNAVAILRESOURCE", &irc_c_error, IRC_ERR_12 }, { 1, 441, NULL, "ERR_USERNOTINCHANNEL", &irc_c_error, IRC_ERR_NEW }, { 1, 442, NULL, "ERR_NOTONCHANNEL", &irc_c_error, IRC_ERR_21 }, { 1, 443, NULL, "ERR_USERONCHANNEL", &irc_c_error, IRC_ERR_NEW }, { 1, 444, NULL, "ERR_NOLOGIN", &irc_c_error, IRC_ERR_12 }, { 1, 445, NULL, "ERR_SUMMONDISABLED", &irc_c_error, IRC_ERR_ONLY1 }, { 1, 446, NULL, "ERR_USERSDISABLED", &irc_c_error, IRC_ERR_ONLY1 }, { 1, 451, NULL, "ERR_NOTREGISTERED", &irc_c_error, IRC_ERR_ONLY1 }, { 1, 461, NULL, "ERR_NEEDMOREPARAMS", &irc_c_error, IRC_ERR_12 }, { 1, 462, NULL, "ERR_ALREADYREGISTRED", &irc_c_error, IRC_ERR_ONLY1 }, { 1, 463, NULL, "ERR_NOPERMFORHOST", &irc_c_error, IRC_ERR_ONLY1 }, { 1, 464, NULL, "ERR_PASSWDMISMATCH", &irc_c_error, IRC_ERR_ONLY1 }, { 1, 465, NULL, "ERR_YOUREBANNEDCREEP", &irc_c_error, IRC_ERR_ONLY1 }, { 1, 466, NULL, "ERR_YOUWILLBEBANNED", &irc_c_error, IRC_ERR_NEW }, { 1, 467, NULL, "ERR_KEYSET", &irc_c_error, IRC_ERR_12 }, { 1, 471, NULL, "ERR_CHANNELISFULL", &irc_c_error, IRC_ERR_12 }, { 1, 472, NULL, "ERR_UNKNOWNMODE", &irc_c_error, IRC_ERR_12 }, { 1, 473, NULL, "ERR_INVITEONLYCHAN", &irc_c_error, IRC_ERR_12 }, { 1, 474, NULL, "ERR_BANNEDFROMCHAN", &irc_c_error, IRC_ERR_12 }, { 1, 475, NULL, "ERR_BADCHANNELKEY", &irc_c_error, IRC_ERR_12 }, { 1, 476, NULL, "ERR_BADCHANMASK", &irc_c_error, IRC_ERR_12 }, { 1, 477, NULL, "ERR_NOCHANMODES", &irc_c_error, IRC_ERR_12 }, { 1, 478, NULL, "ERR_BANLISTFULL", &irc_c_error, IRC_ERR_NEW }, { 1, 481, NULL, "ERR_NOPRIVILEGES", &irc_c_error, IRC_ERR_ONLY1 }, { 1, 482, NULL, "ERR_CHANOPRIVSNEEDED", &irc_c_error, IRC_ERR_12 }, { 1, 483, NULL, "ERR_CANTKILLSERVER", &irc_c_error, IRC_ERR_ONLY1 }, { 1, 484, NULL, "ERR_RESTRICTED", &irc_c_error, IRC_ERR_ONLY1 }, { 1, 485, NULL, "ERR_UNIQOPPRIVSNEEDED",&irc_c_error, IRC_ERR_ONLY1 }, { 1, 491, NULL, "ERR_NOOPERHOST", &irc_c_error, IRC_ERR_ONLY1 }, { 1, 501, NULL, "ERR_UMODEUNKNOWNFLAG", &irc_c_error, IRC_ERR_ONLY1 }, { 1, 502, NULL, "ERR_USERSDONTMATCH", &irc_c_error, IRC_ERR_ONLY1 }, { 0, 0, "PING", "PING", &irc_c_ping, 0 }, { 0, 0, "INVITE", "INVITE", &irc_c_invite, 0 }, { 0, 0, "NICK", "NICK", &irc_c_nick, 0 }, { 0, 0, "PRIVMSG", "PRIVMSG", &irc_c_msg, 0 }, { 0, 0, "NOTICE", "NOTICE", &irc_c_msg, 0 }, { 0, 0, "JOIN", "JOIN", &irc_c_join, 0 }, { 0, 0, "PART", "PART", &irc_c_part, 0 }, { 0, 0, "KICK", "KICK", &irc_c_kick, 0 }, { 0, 0, "QUIT", "QUIT", &irc_c_quit, 0 }, { 0, 0, "MODE", "MODE", &irc_c_mode, 0 }, { 0, 0, "TOPIC", "TOPIC", &irc_c_topic, 0 }, { 0, 0, "ERROR", "ERROR", &irc_c_error, 0 }, { -1, -1, NULL, NULL, NULL, 0 } }; /* { 1, 372, "RPL_MOTD", irc_c_motd, 1, NULL }, { 1, 376, "RPL_ENDOFMOTD", irc_c_motd, 1, NULL } */ #endif /* __EKG_PLUGINS_IRC_MISC_H */ /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/plugins/irc/people.c000066400000000000000000000434651175142753400200570ustar00rootroot00000000000000/* * (C) Copyright 2004-2005 Michal 'GiM' Spadlinski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #include #include #include "people.h" #include "irc.h" enum { OTHER_NETWORK }; static LIST_FREE_ITEM(list_irc_people_free, people_t *) { xfree(data->nick); xfree(data->realname); xfree(data->host); xfree(data->ident); xfree(data); } static LIST_FREE_ITEM(list_irc_channel_free, channel_t *) { xfree(data->nickpad_str); xfree(data->name); xfree(data->topic); xfree(data->topicby); xfree(data->mode_str); list_destroy(data->banlist, 1); xfree(data); } /* add others */ int irc_xstrcasecmp_default(char *str1, char *str2) { return xstrcasecmp(str1, str2); } /* this function searches for a given nickname on a given list * nick MUST BE without the 'irc:' prefix * nick can contain a mode prefix (one of): '@%+' * * list should be one of: * priv_data->channels * priv_data->people->channels->onchan */ people_t *irc_find_person(irc_private_t *j, list_t p, char *nick) { int (*comp_func)(char *,char*); people_t *person; if (!(nick && p)) return NULL; /* debug only, delete after proper testing */ if (!xstrncmp(nick, IRC4, 4)) debug_error("programmer's mistake in call to irc_find_person!: %s\n", nick); if (xstrchr(j->nick_signs, *nick)) nick++; comp_func = irc_xstrcasecmp_default; for (; p; p=p->next) { person = (people_t *)(p->data); if (person->nick && !comp_func(nick, person->nick+4)) return person; } return NULL; } /* p = priv_data->channel || */ channel_t *irc_find_channel(list_t p, char *channame) { channel_t *chan; if (!(channame && p)) return NULL; for (; p; p=p->next) { chan = (channel_t *)(p->data); if (chan->name && (!xstrcmp(chan->name, channame) || !xstrcmp((chan->name)+4, channame))) return chan; } return NULL; } /* p = priv_data->people->channels */ people_chan_t *irc_find_person_chan(list_t p, char *channame) { people_chan_t *ret; channel_t *chan; if (!(channame && p)) return NULL; for (; p; p=p->next) { ret = (people_chan_t *)(p->data); chan = (channel_t *)(ret->chanp); if (chan && chan->name && (!xstrcmp(chan->name, channame) || !xstrcmp(chan->name+4, channame))) return ret; } return NULL; } /* update_longest_nick() * * this helper function iterates over people present on a channel, * finds the one with longest.. nickname and changes value of * longest_nick priv_data variable * * this function is used by irc_del_person_channel (e.g person /parts * or is kicked from channel) and by irc_nick_change. * * @param chan - channel_t structure */ static void update_longest_nick(channel_t *chan) { list_t p; chan->longest_nick = 0; for (p=chan->onchan; p; p=p->next) { people_t *person = (people_t *)p->data; const gsize nicklen = g_utf8_strlen(person->nick+4, -1); if (person->nick && nicklen > chan->longest_nick) chan->longest_nick = nicklen; } nickpad_string_create(chan); } /* irc_add_person_int() * * this is internal function * * this function adds person given by nick to internal structures of * irc plugin * * @param s - current session structure * @param j - irc priv_data structure of current session * @param nick - nickname of user without 'irc:' * prefix, and possibly with '@%+' prefix * @param chan - channel structure, on which nick appeared, * unfortunatelly it can't be NULL in current implementation * * @return pointer to allocated people_t structure. */ static people_t *irc_add_person_int(session_t *s, irc_private_t *j, char *nick, channel_t *chan) { people_t *person, *peronchan; people_chan_t *pch_tmp; userlist_t *ulist; window_t *w; int mode = 0, irccol = 0; char *ircnick, *t; if ((t = xstrchr(j->nick_signs, *nick))) mode = 1 << (t - j->nick_signs); /* debug("irc_add_person_int: %s %d %d\n", modes, mode, k); */ if (mode) nick++; ircnick = irc_uid(nick); w = window_find_s(s, chan->name); /* add user to userlist of window (of a given channel) if not yet there */ if (w && !(ulist = userlist_find_u(&(w->userlist), ircnick))) { /* debug("+userlisty %d, ", mode); */ ulist = userlist_add_u(&(w->userlist), ircnick, nick); irccol = irc_color_in_contacts(j, mode, ulist); } /* add entry in priv_data->people if nick's not yet there */ /* ok new irc-find-person checked */ if (!(person = irc_find_person(j, j->people, nick))) { /* debug("+%s lista ludzi, ", nick); */ person = xmalloc(sizeof(people_t)); person->nick = xstrdup(ircnick); /* K&Rv2 5.4 */ list_add(&(j->people), person); } /* add entry in priv_data->channels->onchan if nick's not yet there */ if (!(peronchan = irc_find_person(j, chan->onchan, nick))) { /* debug("+do kanau, "); */ list_add(&(chan->onchan), person); } xfree(ircnick); /* if channel's not yet on given user channels, add it to his channels */ /* as I haven't looked here for a longer time I'm wondering is this check needed at all */ if (!(pch_tmp = irc_find_person_chan(person->channels, chan->name))) { /* debug("+lista kanaw usera %08X ", person->channels); */ pch_tmp = xmalloc(sizeof(people_chan_t)); pch_tmp->mode = mode; pch_tmp->chanp = chan; irc_nick_prefix(j, pch_tmp, irccol); list_add(&(person->channels), pch_tmp); /* debug(" %08X\n", person->channels); */ } //else { pch_tmp->mode = mode; } return person; } people_t *irc_add_person(session_t *s, irc_private_t *j, char *nick, char *channame) { channel_t *chan; people_t *ret; if (!nick) return NULL; if (!(chan = irc_find_channel(j->channels, channame))) /* GiM: if someone typed /quote names * * and he's not on that channel... */ return NULL; ret = irc_add_person_int(s, j, nick, chan); /* instead of putting this code to irc_add_person_int, * I'm placing it here and in irc_add_people, * to lower number of allocations (made by nickpad_string_create) */ if (xstrlen(nick) > chan->longest_nick) { chan->longest_nick = xstrlen(nick); nickpad_string_create(chan); } query_emit(NULL, "userlist-refresh"); return ret; } int irc_add_people(session_t *s, irc_private_t *j, char *names, char *channame) { channel_t *chan; char **nick=NULL, **save, *tmp; if (!(channame && names)) return -1; /* I'm not sure if this is working on IRCNet, but on freenode * you can do: /quote NAMES #channelname * (if you're not on given channel, irc plugin doesn't allow * you to do /names #channel if you're not on that channel) * * this if-case is responsible for handling the response */ if (!(chan = irc_find_channel(j->channels, channame))) { tmp = saprintf("People on %s: %s", channame, names); if (session_int_get(s, "DISPLAY_IN_CURRENT")&1) print_info(window_current->target, s, "generic", tmp); else print_info("__status", s, "generic", tmp); return 0; } debug_function("[irc] add_people() %08X\n", j); save = nick = array_make(names, " ", 0, 1, 0); while (*nick) { irc_add_person_int(s, j, *nick, chan); /* instead of putting this code to irc_add_person_int, * I'm placing it here and in irc_add_people, * to lower number of allocations (made by nickpad_string_create) */ if (xstrlen(*nick) > chan->longest_nick) chan->longest_nick = xstrlen(*nick); nick++; } nickpad_string_create(chan); query_emit(NULL, "userlist-refresh"); g_strfreev(save); return 0; } static int irc_del_person_channel_int(session_t *s, irc_private_t *j, people_t *nick, channel_t *chan) { userlist_t *ulist = NULL; people_chan_t *tmp; window_t *w; if (!nick || !chan) { debug_error("programmer's mistake in call to irc_del_channel_int: nick: %s chan: %s\n", nick ? "OK" : "NULL", chan ? "OK" : "NULL"); return -1; } /* GiM: We can't use chan->window->userlist, * cause, window could be already destroyed. ;/ */ if ((w = window_find_s(s, chan->name))) ulist = userlist_find_u(&(w->userlist), nick->nick); if (ulist) { /* delete from userlist debug("-userlisty, "); */ userlist_remove_u(&(w->userlist), ulist); } if ((tmp = irc_find_person_chan(nick->channels, chan->name))) { /* delete entry in priv_data->people->channels debug("-lista kanaw usera, "); */ list_remove(&(nick->channels), tmp, 1); } if (!(nick->channels)) { /* delete entry in priv_data->people debug("-%s lista ludzi, ", nick->nick); */ LIST_REMOVE(&(j->people), nick, list_irc_people_free); list_remove(&(chan->onchan), nick, 0); return 1; } /* delete entry in priv_data->channels->onchan debug("-z kanau\n"); */ list_remove(&(chan->onchan), nick, 0); return 0; } /* irc_del_person_channel() * * deletes data from internal structures, when user has been kicked of or parts from a given channel * * @param s - current session structure * @param j - irc priv_data structure of current session * @param nick - nickname of user without 'irc:' * prefix, can contain '@%+' prefix * @param chan - channel structure, where part/kick occured * * @return -1 - no such channel, no such user
* 0 - user removed from given channel
* 1 - user removed from given channel and that was the last channel shared with that user */ int irc_del_person_channel(session_t *s, irc_private_t *j, char *nick, char *channame) { int ret; people_t *person; channel_t *chan; if (!(chan = irc_find_channel(j->channels, channame))) return -1; if (!(person = irc_find_person(j, j->people, nick))) return -1; ret = irc_del_person_channel_int(s, j, person, chan); if (xstrlen(nick) == chan->longest_nick) update_longest_nick(chan); query_emit(NULL, "userlist-refresh"); return ret; } /* irc_del_person() * * delete structures associated with given user, e.g. when he * /quits from IRC * * @param s - current session structure * @param j - irc priv_data structure of current session * @param nick - nickname of user without 'irc:' * prefix, can contain '@%+' prefix * @param chan - channel structure, where part/kick occured * * @return -1 - no such nickname * 1 - user entry deleted from internal structures */ int irc_del_person(session_t *s, irc_private_t *j, char *nick, char *wholenick, char *reason, int doprint) { people_t *person; channel_t *chan; people_chan_t *pech; window_t *w; list_t tmp; int ret; char *longnick; if (!(person = irc_find_person(j, j->people, nick))) return -1; /* if person doesn't have any channels, we shouldn't get here */ if (! (tmp = person->channels) ) { debug_error("logic error in call to irc_del_person!, %s doesn't have any channels\n", nick); /* I'm not adding memory freeing here, since we shouldn't get here by any chance, */ return -1; } /* * GiM: removing from priv_data->people is in * irc_del_person_channel_int * * tmp is set, we can run the loop */ while (1) { if (!(tmp && (pech = tmp->data))) break; if (doprint) print_info(pech->chanp->name, s, "irc_quit", session_name(s), nick, wholenick, reason); /* if this call returns !0 it means * person has been deleted, so let's break * the loop */ chan = pech->chanp; ret = irc_del_person_channel_int(s, j, person, pech->chanp); if (xstrlen(nick) == chan->longest_nick) update_longest_nick(chan); if (ret) break; tmp = person->channels; } longnick = irc_uid(nick); w = window_find_s(s, longnick); if (w) { if (session_int_get(s, "close_windows") > 0) { debug("[irc] del_person() window_kill(w, 1); %s\n", w->target); window_kill(w); } if (doprint) print_info(longnick,s, "irc_quit", session_name(s), nick, wholenick, reason); } xfree(longnick); query_emit(NULL, "userlist-refresh"); return 0; } int irc_del_channel(session_t *s, irc_private_t *j, char *name) { list_t p; channel_t *chan; char *tmp; window_t *w; if (!(chan = irc_find_channel((j->channels), name))) return -1; debug_function("[irc]_del_channel() %s\n", name); while ((p = (chan->onchan))) if (!(p->data)) break; else irc_del_person_channel_int(s, j, (people_t *)p->data, chan); tmp = chan->name; chan->name = NULL; xfree(chan->topic); xfree(chan->topicby); xfree(chan->mode_str); list_destroy(chan->banlist, 1); /* GiM: because we check j->channels in our kill-window handler * this must be done, before, we'll try to kill_window.... */ list_remove(&(j->channels), chan, 1); w = window_find_s(s, tmp); if (w && (session_int_get(s, "close_windows") > 0)) { debug("[irc]_del_channel() window_kill(w); %s\n", w->target); window_kill(w); } xfree(tmp); query_emit(NULL, "userlist-refresh"); return 0; } static int irc_sync_channel(session_t *s, irc_private_t *j, channel_t *p) { p->syncmode = 2; /* to ma sie rownac ile ma byc roznych syncow narazie tylko WHO * ale moze bedziemy syncowac /mode +b, +e, +I) */ g_get_current_time(&(p->syncstart)); ekg_connection_write(j->send_stream, "WHO %s\r\n", p->name+4); ekg_connection_write(j->send_stream, "MODE %s +b\r\n", p->name+4); ekg_connection_write(j->send_stream, "MODE %s\r\n", p->name+4); return 0; } channel_t *irc_add_channel(session_t *s, irc_private_t *j, char *name, window_t *win) { channel_t *p; p = irc_find_channel(j->channels, name); if (!p) { p = xmalloc(sizeof(channel_t)); p->name = irc_uid(name); p->window = win; debug("[irc] add_channel() WINDOW %08X\n", win); if (session_int_get(s, "auto_channel_sync") != 0) irc_sync_channel(s, j, p); list_add(&(j->channels), p); return p; } return NULL; } int irc_color_in_contacts(irc_private_t *j, int mode, userlist_t *ul) { int i, len; len = (xstrlen(SOP(_005_PREFIX))>>1) - 1; /* GiM: this could be done much easier on intel ;/ */ for (i=0; inick_modes[i]) { case 'o': ul->status = EKG_STATUS_AVAIL; break; /* op */ case 'h': ul->status = EKG_STATUS_AWAY; break; /* half-op */ case 'v': ul->status = EKG_STATUS_XA; break; /* voice */ case 'q': ul->status = EKG_STATUS_INVISIBLE; break; /* owner */ case 'a': ul->status = EKG_STATUS_FFC; break; /* admin */ default: ul->status = EKG_STATUS_DND; break; /* rest */ } return i; } int irc_nick_prefix(irc_private_t *j, people_chan_t *ch, int irc_color) { ch->sign[0] = ' '; ch->sign[1] = '\0'; if (irc_color < xstrlen(j->nick_signs)) *(ch->sign) = j->nick_signs[irc_color]; return 0; } /* irc_nick_change() * * this is internal function called when give person changes nick * * @param s - current session structure * @param j - irc priv_data structure of current session * @param old_nick - old nickname of user without 'irc:' * prefix, and WITHOUT '@%+' prefix * @param new_nick - new nickname of user without 'irc:' * prefix, and WITHOUT '@%+' prefix * * @return 0 */ int irc_nick_change(session_t *s, irc_private_t *j, char *old_nick, char *new_nick) { userlist_t *ulist, *newul; list_t i; userlist_t *ul; people_t *per; people_chan_t *pch; window_t *w; char *t1, *t2 = irc_uid(new_nick); if (!(per = irc_find_person(j, j->people, old_nick))) { debug_error("irc_nick_change() person not found?\n"); xfree(t2); return 0; } for (ul=s->userlist; ul; ul = ul->next) { userlist_t *u = ul; ekg_resource_t *rl; for (rl = u->resources; rl; rl = rl->next) { ekg_resource_t *r = rl; if (r->priv_data != per) continue; xfree(r->name); r->name = xstrdup(t2); /* XXX, here. readd to list, coz it'll be bad sorted. :( */ break; } } /* update userlists of proper windows */ for (i=per->channels; i; i=i->next) { pch = (people_chan_t *)i->data; w = window_find_s(s, pch->chanp->name); if (w && (ulist = userlist_find_u(&(w->userlist), old_nick))) { newul = userlist_add_u(&(w->userlist), t2, new_nick); newul->status = ulist->status; userlist_remove_u(&(w->userlist), ulist); /* XXX dj, userlist_replace() */ /* GiM: Yes, I thought about doin' this 'in place' * but we would have to change position in userlist * to still keep it sorted, so I've chosen to do this * this way */ } } query_emit(NULL, "userlist-refresh"); /* update nickname in internal structures */ t1 = per->nick; per->nick = t2; for (i=per->channels; i; i=i->next) { pch = (people_chan_t *)i->data; /* if person who changed nick had longest nick, * update longest_nick variable */ if (xstrlen(new_nick) > pch->chanp->longest_nick) { pch->chanp->longest_nick = xstrlen(new_nick); nickpad_string_create(pch->chanp); } else if (xstrlen(old_nick) == pch->chanp->longest_nick) update_longest_nick (pch->chanp); } xfree(t1); return 0; } /* GiM: nope, people will never be free ;/ */ int irc_free_people(session_t *s, irc_private_t *j) { list_t t1; people_t *per; channel_t *chan; window_t *w; debug_function("[irc] free_people() %08X %s\n", s, s->uid); for (t1=j->people; t1; t1=t1->next) { per = (people_t *)t1->data; list_destroy(per->channels, 1); per->channels=NULL; } for (t1=j->channels; t1; t1=t1->next) { chan = (channel_t *)t1->data; list_destroy(chan->onchan, 0); chan->onchan = NULL; /* GiM: check if window isn't allready destroyed */ w = window_find_s(s, chan->name); if (w && w->userlist) userlists_destroy(&(w->userlist)); /* * window_kill(chan->window, 1); */ } LIST_DESTROY(j->people, list_irc_people_free); j->people = NULL; LIST_DESTROY(j->channels, list_irc_channel_free); j->channels = NULL; return 0; } /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/plugins/irc/people.h000066400000000000000000000040371175142753400200540ustar00rootroot00000000000000/* * (C) Copyright 2004-2005 Michal 'GiM' Spadlinski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __IRC_PIPL_H #define __IRC_PIPL_H #include "irc.h" people_t *irc_find_person(irc_private_t *j, list_t p, char *nick); channel_t *irc_find_channel(list_t p, char *channame); people_chan_t *irc_find_person_chan(list_t p, char *channame); /* person joins channel */ people_t *irc_add_person(session_t *s, irc_private_t *j, char *nick, char *channame); /* we join channel */ int irc_add_people(session_t *s, irc_private_t *j, char *names, char *channame); /* someone made /part */ int irc_del_person_channel(session_t *s, irc_private_t *j, char *nick, char *chan); /* someone made /quit */ int irc_del_person(session_t *s, irc_private_t *j, char *nick, char *wholenick, char *reason, int doprint); /* we've made /part */ int irc_del_channel(session_t *s, irc_private_t *j, char *name); /* add channel to our list of channels */ channel_t *irc_add_channel(session_t *s, irc_private_t *j, char *name, window_t *win); int irc_nick_change(session_t *s, irc_private_t *j, char *old_nick, char *new_nick); int irc_nick_prefix(irc_private_t *j, people_chan_t *ch, int irc_color); int irc_color_in_contacts(irc_private_t *j, int mode, userlist_t *ul); /* clean up */ int irc_free_people(session_t *s, irc_private_t *j); #endif /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/plugins/irc/session-pl.txt000066400000000000000000000146431175142753400212600ustar00rootroot00000000000000// IRC protocol session variables description // (c) 2004-2005 Michal 'GiM' Spadlinski // Jakub 'darkjames' Zawadzki auto_guess_encoding typ: tekst domyślna wartość: brak lista kodowań, według których będziemy próbować przekodować przychodzący tekst ban_type typ: liczba domyślna wartość: 10 Typy banów: (irssi-like) tylko ze zamiast tekstu mamy numerki: - 1 (Nick) - nick!*@* - 2 (User) - *!*user@* - 4 (Host) - *!*@host.* (to jest troche inne niz w irssi jak chcesz irssi-like to ustaw 12) - 8 (Domain) - *!*@*.domain.net - 8 (IP) - *!*@192.168.11.* i po dodaniu mamy np: - 3 (Nick|User) - nick!user@* - 10 (User|Domain) - *!*user@*.domain.net Zmienna uzywana przy /ban oraz /kickban Wiecej /help ban (@irssi) close_windows typ: bool domyślna wartość: 0 plugin sam zamyka `niepotrzebne` okna. przy /quit usera z ktorym mamy otwarte query, kiedy zostalismy wykopaniu przez kogos z kanalu imho zbedny fjuczer. Zachowanie kompatylibnosci z GiMem ;> dcc_port typ: liczba domyślna wartość: 0 póki co jeszcze nie używane... display_notify typ: liczba domyślna wartość: 0 wartość -1 powoduje korzystanie z globalnej zmiennej. wartość 0 powoduje ignorowanie zmian stanu znajomych, wartość 1 powoduje wyświetlanie wszystkich zmian, wartość 2 wyświetla tylko zmiany z niedostępnego na dostępny i na odwrót. większy priorytet ma zmienna ,,contacts'', która przy wartości 2 ukrywa zmiany stanu. hostname typ: tekst domyślna wartość: brak pozwala na korzystanie z vhostów [opcja -h w irssi] log_formats typ: tekst domyślna wartość: xml,simple format logów make_window typ: liczba domyślna wartość: 2 suma bitowa, określająca czy należy tworzyć okna w danej sytuacji: 1 - nie używane 2 - tworzenie okna, gdy przychodzi do nas wiadomość od innego użytkownika 4 - tworzenie okna rozmowy z użytkownikiem, jeśli ten wysłał do nas zapytanie ctcp 8 - tworzenie okna rozmowy z użytkownikiem, jeśli przyszła od niego odpowiedź, na wysłane przez nas zapytanie ctcp 16 - tworzenie okienka, jeżeli dostajemy dostajemy NOTICE od serwera przy łaczeniu... [AUTH mesejgi itp] Czyli np: ustawienie na 10 spowoduje otwieranie okienka rozmowy przy przychodzeniu wiadomości i przy odpowiedzi na /ctcp [jeżeli tylko okienko jeszcze nie istnieje] nickname typ: tekst domyślna wartość: twoj login określa domyślny nick, pod jakim będziemy starali się połączyć z serwerem IRC zmienna musi być ustawiona, żeby móc się połaczyć password typ: tekst domyślna wartość: brak hasło do serwera port typ: liczba domyślna wartość: 6667 port serwera, do którego będziemy się podłączać prefer_family typ: liczba domyślna wartość: 0 Jesli serwer ma rekordy A i AAAA to zostanie wybrany rekord AAAA gdy prefer_family = 10 (AF_INET6) rekord A gdy prefer_family != 10 realname typ: tekst domyślna wartość: realname użyszkodnika dowolny tekst, który zostanie ustawiony jako nasz realname [m.in. w odpowiedzi na /whois], by zmiany realname odniosły skutek należy się ponownie połączyć recode_list typ: text domyślna wartość: brak Lista kodowań dla poszczególnych nicków lub/i kanałów Syntax: encoding1:nick1,nick2,#chan1,nick3;encoding2:nick4,#chan5,chan6 server typ: tekst domyślna wartość: brak adres serwera irc, np: warszawa.irc.pl zmienna musi być ustawiona, żeby móc się połączyć AUTO_JOIN typ: tekst domyślna wartość: brak kanały, do których klient ma się podłączyć po połaczeniu, podane w formie: kanał1,kanał2,kanał3,kanał4, klucznakanał1,klucznakanał2 DISPLAY_PONG typ: liczba domyślna wartość: 1 określa czy wyświetlać komunikat o odebraniu ping i wysłaniu pong do serwera IRC. 1 - wyświetlać, 0 - nie. DISPLAY_AWAY_NOTIFICATION typ: liczba domyślna wartość: 1 określa czy wyświetlać czyjeś away [np: jeśli ktoś ma ustawione away i zrobimy /msg ktoś cośtam] DISPLAY_IN_CURRENT typ: liczba domyślna wartość: 0 suma bitów, określająca, że pewne rzeczy będą wyświetlane w aktualnym oknie: 1 - wynik komendy /names [RACZEJ GŁUPOTA!] 2 - wynik /whois jeżeli nie ma otwartego okna rozmowy z danym uzytkownikiem będzie wyświetlony w aktualnym oknie zamiast w oknie statusu DISPLAY_NICKCHANGE typ: liczba domyślna wartość: 0 gdzie wyświetlać zmiany nicków [zobacz opis DISPLAY_QUIT DISPLAY_QUIT typ: liczba domyślna wartość: 0 0 - we wszystkich kanałach, na których był użytkownik 1 - tylko w oknie statusu 2 - tylko w aktualnym oknie REJOIN typ: liczba domyślna wartość: 2 suma bitowa, określająca kiedy robić autorejoin 1 - przy dostaniu kicka z kanału 2 - przy [re]connec'cie, jeśli są jakieś otwarte okienka z kanałami REJOIN_TIME typ: liczba domyślna wartość: 2 ilość sekund jaką plugin ma odczekać, przed próbą ponownego dołączeniu do kanału, jeśli zostaliśmy wykopani zmienna ta, nie ma żadnego znaczenia, jeśli zmienna %TREJOIN%n nie jest ustawiona tak, by klient robił autorejoin po kicku SHOW_NICKMODE_EMPTY typ: liczba domyślna wartość: 1 jeśli 0 spacja NIE jest wyświetlana przed nickiem jeżeli ircownik nie ma +, @ ani %, jeśli 1 spacja będzie wyświetlana SHOW_MOTD typ: liczba domyślna wartość: 1 czy wyświetlać MOTD. 1 - tak, 0 - nie. STRIPMIRCCOL typ: liczba domyślna wartość: 0 czy stripować wyświetlanie durnych kolorków na IRCu. 0 - kolorki będą wyświetlane 1 - nie będą. zmienna ta nie ma wpływu na wyświetlanie atryburów takich jak %Tbold%T, %Uunderline%U i %Vreverse%V... VERSION_NAME typ: tekst domyślna wartość: brak określa pierwszy z ciągów, którymi IRC plugin odpowiada na zapytanie %Tctcp VERSION%n, jeżeli nie ustawione używany jest ciąg: "IRC plugin for EKG2:" VERSION_NO typ: tekst domyślna wartość: brak określa drugi z ciągów, którymi IRC plugin odpowiada na zapytanie %Tctcp VERSION%n, jeżeli nie ustawione używany jest ciąg: "numer_wersji_plugina:", można ustawić na pusty: /session -s irc:nazwasesji VERSION_NO "" VERSION_SYS typ: tekst domyślna wartość: brak określa trzeci z ciągów, którymi IRC plugin odpowiada na zapytanie %Tctcp VERSION%n, jeżeli nie ustawione używany jest ciąg: "System wersja_jądra architektura", można ustawić na pusty: /session -s irc:nazwasesji VERSION_SYS "" ekg2-0.4~pre+20120506.1/plugins/jabber/000077500000000000000000000000001175142753400170635ustar00rootroot00000000000000ekg2-0.4~pre+20120506.1/plugins/jabber/commands-en.txt000066400000000000000000000200611175142753400220240ustar00rootroot00000000000000// descripton for jabber-plugin proceedings // (c) copyright 2001-2003 wojtek kaniewski // (c) copyright 2004 piotr kupisiewicz // (c) copyright 2004 tomasz torcz _autoaway parameters: short description: changes status to away _autoback parameters: short description: changes status to available add parameters: [name] short description: adds user to our roster while asking for authorization admin parameters: [conference field value] short description: alter chatroom configuration Alter MUC chatroom configuration: Set `field` to `value` for `conference` chatroom. Invoke it with no argmunets to see all fields and their values for the current chatroom. Example (make room persistent): /admin xmpp:test@conference.example.com --muc#roomconfig_persistentroom 1 auth parameters: short description: authorization support -a, --accept authorize JID -d, --deny deny JID authorization or revokes it -r, --request send autorization request -c, --cancel cancel authorization -j, --reject reject authorization request -l, --list display pending requests -p, --probe request presence information away parameters: [description/-] short description: changes status to away If description wasn't given, random description will be looked for. Giving ,,%T-%n'' instead of description will clear description. back parameters: [description/-] short description: changes status to available If description wasn't given, random description will be looked for. Giving ,,%T-%n'' instead of description will clear description. ban parameters: [reason] short description: Bans the specified JID from the given conference If no JID is specified, command lists all jids banned from given conference. Otherwise it bans specified JID from the given conference. bookmark parameters: -- short description: edit bookmarks on server --add add bookmark --remove remove bookmark --clear remove all bookmarks --modify modify selected bookmark --display display all bookmarks --conf --url --autojoin --nick --pass example: /bookmark --add --conf my@conference.server.domain --autojoin 1 --nick nick --pass pass -- #conference change parameters: short description: changes data in vcard -f, --fullname -n, --nick -b, --born (ISO 8601-formatted, e.g. yyyy-mm-dd) -c, --city -d, --description -C, --country -p, --photo If any of these parameteres is not given, it's value will be cleared on vCard. Giving ,,%T-%n'' will clear %Tall%n fields. chat parameters: short description: sends chat message using * instead of user's name will create queries with all your contacts config parameters: short description: connect parameters: short description: connects with server del parameters: [name] short description: removes user from roster deop parameters: short description: take all privileges off from the nick Take voice and moderator privilages off from the given nick on the given conference. devoice parameters: [reason] short description: take all privileges off from the nick Take voice and moderator privilages off from the given nick on the given conference. disconnect parameters: [description/-] short description: rozłącza się od serwera If description wasn't given, random description will be looked for. Giving ,,%T-%n'' instead of description will clear description. dnd parameters: [description/-] short description: changes status to ,,do not disturb'' If description wasn't given, random description will be looked for. Giving ,,%T-%n'' instead of description will clear description. ffc parameters: [description/-] short description: changes status to ,,free for chat'' If description wasn't given, random description will be looked for. Giving ,,%T-%n'' instead of description will clear description. find parameters: short description: finds a buddy and shows his vcard invisible parameters: [description/-] short description: changes status to invisible If description wasn't given, random description will be looked for. Giving ,,%T-%n'' instead of description will clear description. join parameters: [nick] short description: join conference Joins conference as nick. If conference does not exists, creates it. kick parameters: [reason] short description: kick nick off the conference lastseen parameters: short description: retrieves information on last logout time for given jid msg parameters: short description: sends message All receivers are marked by *. Preceding multiline message with string set in subject_prefix variable will make this line subject of this message. modify parameters: short description: changes roster entries Possible options: -n, --nickname contact nickname nick parameters: [conference] short description: change nick Change nick on a given conference. If no conference provided, use conference in current window. op parameters: short description: Gives moderator privileges to the specified nick passwd parameters: short description: changes password part parameters: short description: leaves conference reconnect parameters: short description: disconnects and connects again register parameters: short description: registers on server\transport --username --password reply parameters: #id ... short description: replies on given conversation Replies with given message to conversation connected with given Reply-ID. Message can contain subject like in msg, but if not specified, old one (prefixed by subject_reply_prefix) will be used. tmsg parameters: uid/nickname thread-id ... short description: sends message with thread Like msg, but additional, second arg specifies thread-ID to use. topic parameters: [uid] short description: set MUC topic transpinfo parameters: short description: shows you information about given server\transport transports parameters: short description: shows list of transports at given server unban parameters: short description: unban given JID from the muc conference unregister parameters: short description: deletes your account from server userinfo parameters: short description: retrieves Jabber Directory info about given jid userlist parameters: short description: userlist import/export (JRU-like) Depending on arg: -c, --clear clears current userlist (equal to 'del *') -g, --get [filename] imports userlist from file -p, --put [filename] exports userlist to file -G, --replace [filename] clear userlist & import new from file Userlist export file format is same as with JRU (http://jru.jabberstudio.org). vacation parameters: short description: function is not implemented yet ver parameters: short description: retrieves information about OS and client of given jid voice parameters: short description: give the voice to the nick Voice allows to speak on moderated conference. Note that if you give voice to operator, you will take off moderator privileges from him. xa parameters: [description/-] short description: changes to status ,,extended away'' If description wasn't given, random description will be looked for. Giving ,,%T-%n'' instead of description will clear description. xml parameters: short description: sends xml stream Command must be UTF-8 encoded, all XML special characters have to be changed to XML entities (< > &). // $Id$ ekg2-0.4~pre+20120506.1/plugins/jabber/commands-pl.txt000066400000000000000000000234551175142753400220470ustar00rootroot00000000000000// opis komend dla protokołu jabber // (c) copyright 2001-2003 wojtek kaniewski // (c) copyright 2004 piotr kupisiewicz // (c) copyright 2004 tomasz torcz _autoaway parametry: krotki opis: zmienia stan na zajęty _autoback parametry: krotki opis: zmienia stan na dostępny add parametry: [nazwa] krotki opis: dodaje użytkownika do naszego rostera, jednocześnie prosząc o autoryzację admin parametry: [konferencja pole wartość] krotki opis: zmienia konfigurację pokoju Zmienia konfigurację pokoju MUC: ustawia `pole` na `wartość` dla pokoju `konferencja`. Jeżeli nie podano żadnych argumentów, wyświetla wszystkie pola oraz ich wartości dla aktualnego pokoju. Przykład (zmień typ pokoju na `persistent`): /admin xmpp:test@conference.example.com --muc#roomconfig_persistentroom 1 auth parametry: krotki opis: obsługa autoryzacji -a, --accept autoryzuje JID -d, --deny odmawia udzielenia autoryzacji lub ją odbiera -r, --request wysyła żądanie autoryzacji -c, --cancel wysyła żądanie cofnięcia autoryzacji -j, --reject odrzuca żądanie autoryzacji -l, --list wyświetla oczekujące żądania -p, --probe wysyła pytanie o obecność użytkownika away parametry: [opis/-] krotki opis: zmienia stan na zajęty Jeżeli nie podano opisu to będzie szukany losowy opis. Podanie ,,%T-%n'' zamiast powodu spowoduje wyczyszczenie opisu. back parametry: [opis/-] krotki opis: zmienia stan na dostępny Jeżeli nie podano opisu to będzie szukany losowy opis. Podanie ,,%T-%n'' zamiast powodu spowoduje wyczyszczenie opisu. ban parametry: [powód] krotki opis: banuje JID z kanału lub wyświetla listę banów Jeżeli nie podano JID wyświetla listę zbanowanych uzytkowników. W przeciwnym wypadku banuje dany JID z określonego kanału. bookmark parametry: krotki opis: zarzadzanie zakladkami serwer-side (jabber:iq:private) Zarzadza zakladkami (dodaje/modyfikuje/usuwa/wyswietla). Ustawienie zmiennej sesyjnej auto_bookmark_sync zalecane. -d, --display wyswietla. -g, --get pobiera, bez wyswietlania -c, --clear czysci -p, --put wysyla liste lokalna na serwer (automatycznie robione po dodaniu/modyfikacji/usunieciu) -a, --add --url [-- nazwa] dodaje nowy wpis do zakladek ze stronami WWW -a, --add --conf dodaje nowy wpis do zakladek z konferencjami [--autojoin 1] czy automatycznie sie dolaczac po polaczeniu do serwera [niezaimplentowane w ekg2, ale wiele klientow umie :)] [--nick nasznick] [--pass haslo] [-- nazwa] -m, --modify NIE ZAIMPLEMENTOWANE, modyfikuje :) -r, --remove NIE ZAIMPLEMENTOWANE, usuwa :) change parametry: krotki opis: zmienia informacje w katalogu publicznym -f, --fullname -n, --nick -b, --born (zapisana wg. ISO 8601, tj. rrrr-mm-dd) -c, --city -d, --description -C, --country -p, --photo <ścieżka do zdjecia> Jeśli któryś z parametrów nie zostanie podany, jego wartość zostanie wyczyszczona w katalogu publicznym. Podanie parametru ,,%T-%n'' wyczyści %Twszystkie%n pola. chat parametry: krotki opis: wysyła wiadomość w ramach rozmowy connect parametry: krotki opis: łączy się z serwerem del parametry: [nazwa] krotki opis: usuwa z naszego rostera deop parametry: [kanał] [nick] krotki opis: degraduje nick do roli gościa Odbiera użytkownikowi prawa moderatora kanału oraz prawo wysyłania wiadomości jeżeli kanał jest moderowany. devoice parametry: [kanał] [nick] krotki opis: degraduje nick do roli gościa Odbiera użytkownikowi prawa moderatora kanału oraz prawo wysyłania wiadomości jeżeli kanał jest moderowany. disconnect parametry: [opis/-] krotki opis: rozłącza się od serwera Jeżeli nie podano opisu to będzie szukany losowy opis. Podanie ,,%T-%n'' zamiast powodu spowoduje wyczyszczenie opisu. dnd parametry: [opis/-] krotki opis: zmienia stan na nie przeszkadzać Jeżeli nie podano opisu to będzie szukany losowy opis. Podanie ,,%T-%n'' zamiast powodu spowoduje wyczyszczenie opisu. ffc parametry: [opis/-] krotki opis: zmienia stan na chętny do rozmowy Jeżeli nie podano opisu to będzie szukany losowy opis. Podanie ,,%T-%n'' zamiast powodu spowoduje wyczyszczenie opisu. kick parametry: [kanał] [nick] krotki opis: wyrzuca nick z kanału invisible parametry: [opis/-] krotki opis: zmienia stan na zajęty Jeżeli nie podano opisu to będzie szukany losowy opis. Podanie ,,%T-%n'' zamiast powodu spowoduje wyczyszczenie opisu. join parametry: [nick] krotki opis: przyłącza do konferencji Przyłącza do konferencji. Jeżeli podana konferencja nie istnieje, zakłada nową konferencję. Opcjonalnie ustawia nick użytkownika w konferencji. lastseen parametry: krotki opis: pobiera informację o czasie wylogowania się danego jid msg parametry: krotki opis: wysyła pojedynczą wiadomość Wszyscy odbiorcy to * zamiast nadawcy. Poprzedzenie wiadomości wielolinijkowej ciągiem zdefiniowanym w zmiennej subject_prefix spowoduje potraktowanie pierwszej linijki jako tematu. modify parametry: krotki opis: zmienia wpisy w liście kontaktów Opcje mogą być następujące: -n, --nickname wpis w liście kontaktów nick parametry: [konferencja] krotki opis: zmienia nick użytkownika Zmienia nick użytkownika w danej konferencji. Jeżeli nie podano konferencji, zmienia nick na konferencji w aktualnym oknie. op parametry: krotki opis: nadaje nickowi uprawnienia moderatora part parametry: krotki opis: opuszcza konferencję passwd parametry: krotki opis: zmienia hasło privacy parametry: Zarzadza lista ignorowanych serwer-side (%gjabber:iq:privacy%n). Ustawienie zmiennej sesyjnej %Tauto_privacylist_sync%n wymagane. Ustawienie zmiennej sesyjnej %Tprivacy_list%n zalecane (jesli nie, zakladamy ze korzystasz z listy %Tekg2%n). Jesli w parametrach nie ma podanej listy, to operujemy na liscie zdefiniowanej w %Tprivacy_list%n. %RKOMENDA EKSPERYMENTALNA, PROSZE INFORMOWAC O WSZYSTKICH BUGACH, Z DOKLADNYM OPISEM.%n [--lists] wyswietla listy dostepne na serwerze [--get] lista wyswietla liste. --session lista ustawia lista jako aktywna, jesli podano %T-%n deaktywuje aktualna liste. --default lista ustawia liste jako domyslna, jesli podano %T-%n kasuje ustawienia. --unset lista kasuje liste %r(NIEODWRACALNE!!)%n --sync %g[WEWNETRZNA KOMENDA]%n Wysyla liste lokalna do serwera (automatycznie robione po dodaniu/zmianie/usunieciu) --set [--order numerek] [-zabron] [+dozwol] dodaje wpis do listy albo xmpp:osoba albo @grupa albo %Tnone%n, %Tfrom%n, %Tto%n, %Tboth%n gdy definiujemy poziom autoryzacji [--order numerek] ustawia ktory to ma byc element na liscie. [-zabron] Parametr moze byc powtorzony wielokrotnie, znak minusa, a nastepnie jedna z opcji: iq, msg, pin, pout lub gwiazdka jesli wszystkie. [+pozwol] Parametr moze byc powtorzony wielokrotnie, znak plusa, a nastepnie jedna z opcji: iq, msg, pin, pout lub gwiazdka dla okreslenia ze wszystkie. %r!!! UWAGA !!! Po dodaniu pierwszego elementu do listy, jest wymagane aktywowanie listy przez %g/privacy --session lista%n --remove #id usuwa wpis z numerem id z listy. --modify zmienia wpis, NIEZAIMPLEMENTOWANE. reconnect parametry: krotki opis: rozłącza i łączy się ponownie reply parametry: <#reply-id> ... krotki opis: odpisuje na wątek Wysyła wiadomość jako kontynuację wskazanego przez reply-id wątku. Możliwe jest podanie tematu jak w msg, w przeciwnym razie temat zostanie utworzone w oparciu o temat wątku (poprzedzony subject_reply_prefix). tmsg parametry: ... krotki opis: wysyła wiadomość wątkowaną Działa podobnie jak msg, z tym, że dodatkowy, drugi parametr zawiera identyfikator wątku dla wiadomości. topic parametry: [konferencja] krotki opis: ustawia topic MUC unban parametry: krotki opis: zdejmuje ban na dany JID z kanału Jeżeli nie podano JID wyświetla listę zbanowanych uzytkowników. W przeciwnym wypadku usuwa ban danego JID z określonego kanału. userinfo parametry: krotki opis: pobiera informacje z katalogu Jabbera o danym jid ver parametry: krotki opis: pobiera informację o sytemie operacyjnym i wersji klienta Jabbera danego jid voice parametry: krotki opis: daje "głos" nickowi Nadaje użytkownikowi uprawnienia do wysyłania wiadomości w konferencji, nawet jeśli konferencja jest moderowana. Uwaga: ustawienie voice na uzytkowniku będącym moderatorem spowoduje odebraniu mu praw administratora. xa parametry: [opis/-] krotki opis: zmienia stan na bardzo zajęty Jeżeli nie podano opisu to będzie szukany losowy opis. Podanie ,,%T-%n'' zamiast powodu spowoduje wyczyszczenie opisu. xml parametry: krotki opis: wysyła polecenie xml Polecenie musi być zakodowanie w UTF-8, a wszystkie znaki specjalne używane w XML (& < >) muszą być zamienione na odpowiadające im sekwencje. // $Id$ ekg2-0.4~pre+20120506.1/plugins/jabber/commands.c000066400000000000000000002336251175142753400210430ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2003 Wojtek Kaniewski * Tomasz Torcz * Libtlen developers (http://libtlen.sourceforge.net/index.php?theme=teary&page=authors) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #include #include #ifndef NO_POSIX_SYSTEM #include #include #include #include #endif #include #include #include #include #include #include #ifndef NO_POSIX_SYSTEM #include #endif #ifdef HAVE_EXPAT_H /* expat is used for XEP-0071 syntax checking */ # include #endif #include #include "jabber.h" #include "jabber_dcc.h" const char *jabber_prefixes[2] = { "xmpp:", "tlen:" }; extern int config_jabber_disable_chatstates; /* in jabber.c */ WATCHER(jabber_handle_connect2); static COMMAND(jabber_command_connect) { const char *realserver = session_get(session, "server"); const char *resource = session_get(session, "resource"); const char *server; jabber_private_t *j = session_private_get(session); if (session->connecting) { printq("during_connect", session_name(session)); return -1; } if (session_connected_get(session)) { printq("already_connected", session_name(session)); return -1; } if (!session_get(session, "__new_account") && !(session_get(session, "password"))) { printq("no_config"); return -1; } if (command_exec(NULL, session, "/session --lock", 0) == -1) return -1; debug("session->uid = %s\n", session->uid); /* XXX, nie wymagac od usera podania calego uida w postaci: tlen:ktostam@tlen.pl tylko samo tlen:ktostam? */ if (!(server = xstrchr(session->uid, '@'))) { printq("wrong_id", session->uid); return -1; } xfree(j->server); j->server = xstrdup(++server); if (!realserver) { if (j->istlen) { j->istlen++; realserver = TLEN_HUB; } else realserver = server; } { int port = session_int_get(session, "port"); #ifdef JABBER_HAVE_SSL int ssl_port = session_int_get(session, "ssl_port"); int use_ssl = session_int_get(session, "use_ssl"); j->using_ssl = 0; #endif if (j->istlen && !xstrcmp(realserver, TLEN_HUB)) j->port = 80; else #ifdef JABBER_HAVE_SSL if (use_ssl) j->port = ssl_port < 1 ? 5223 : ssl_port; else #endif j->port = port < 1 ? 5222 : port; if (!(( j->connect_watch = ekg_connect(session, realserver, 5222, j->port, jabber_handle_connect2)))) { printq("generic_error", strerror(errno)); return -1; } } if (!resource) resource = JABBER_DEFAULT_RESOURCE; xfree(j->resource); j->resource = xstrdup(resource); session->connecting = 1; j->sasl_connecting = 0; printq("connecting", session_name(session)); if (session_status_get(session) == EKG_STATUS_NA) session_status_set(session, EKG_STATUS_AVAIL); return 0; } static COMMAND(jabber_command_disconnect) { jabber_private_t *j = session_private_get(session); char *descr = NULL; /* jesli istnieje timer reconnecta, to znaczy, ze przerywamy laczenie */ if (timer_remove_session(session, "reconnect") == 0) { printq("auto_reconnect_removed", session_name(session)); return 0; } if (!session->connecting && !session_connected_get(session)) { printq("not_connected", session_name(session)); return -1; } if (session->autoaway) session_status_set(session, EKG_STATUS_AUTOBACK); /* jeli jest /reconnect, nie mieszamy z opisami */ if (xstrcmp(name, ("reconnect"))) { if (params[0]) { if (!xstrcmp(params[0], "-")) descr = NULL; else descr = xstrdup(params[0]); } else if (config_keep_reason && !(descr = ekg_draw_descr(EKG_STATUS_NA))) descr = xstrdup(session_descr_get(session)); session_descr_set(session, descr); } else descr = xstrdup(session_descr_get(session)); /* w libtlenie jest unavailable + eskejpiete tlen_encode() */ if (session->connected) { #if 0 char *lt = session_get(session, "__last_typing"); if (lt) watch_write(j->send_watch, "" "" "" "\n", lt); #endif { char *__session = xstrdup(session_uid_get(session)); query_emit(NULL, "protocol-disconnecting", &__session); xfree(__session); } if (descr) { char *tmp = jabber_escape(descr); watch_write(j->send_watch, "%s", tmp ? tmp : ""); xfree(tmp); } else watch_write(j->send_watch, ""); } if (!j->istlen) watch_write(j->send_watch, ""); else watch_write(j->send_watch, ""); if (session->connecting) jabber_handle_disconnect(session, descr, EKG_DISCONNECT_STOPPED); else jabber_handle_disconnect(session, descr, EKG_DISCONNECT_USER); xfree(descr); return 0; } static COMMAND(jabber_command_reconnect) { if (session->connecting || session_connected_get(session)) { jabber_command_disconnect(name, params, session, target, quiet); } return jabber_command_connect(name, params, session, target, quiet); } static const char *jid_target2uid(session_t *s, const char *target, int quiet) { const char *uid; int istlen = jabber_private(s)->istlen; if (!(uid = get_uid(s, target))) uid = target; /* XXX, get_uid() checks if this plugin is ok to handle it. so we have here tlen: or xmpp: but, we must check * if this uid match istlen.. However doesn't matter which protocol is, function should work... */ if (xstrncasecmp(uid, jabber_prefixes[istlen], 5)) { printq("invalid_session"); return NULL; } return uid; } static COMMAND(jabber_command_msg) { jabber_private_t *j = session_private_get(session); int chat = !xstrcmp(name, ("chat")); int subjectlen = xstrlen(config_subject_prefix); char *msg; char *htmlmsg = NULL; char *subject = NULL; char *thread = NULL; const char *uid; newconference_t *c; int ismuc = 0; int secure = 0; const char *msg2; /* used for transcoding */ if (!xstrcmp(target, "*")) { if (msg_all(session, name, params[1]) == -1) printq("list_empty"); return 0; } if (!(uid = jid_target2uid(session, target, quiet))) return -1; /* threaded messages */ if (!xstrcmp(name, "tmsg")) { /* just make it compatible with /msg */ const char *tmp = params[1]; params[1] = params[2]; params[2] = tmp; /* and now we can set real thread */ thread = jabber_escape(params[2]); } else if (!xstrcmp(name, "msg") && (session_int_get(session, "msg_gen_thread"))) thread = jabber_thread_gen(j, uid); /* we don't return any chars to escape */ if (!session_connected_get(session)) { xfree(thread); goto msgdisplay; } /* message subject */ if (!j->istlen && config_subject_prefix && !xstrncmp(params[1], config_subject_prefix, subjectlen)) { char *last = xstrchr(params[1]+subjectlen, 10); if (last) { *last = 0; subject = jabber_escape(params[1]+subjectlen); *last = 10; msg = last+1; } else { subject = jabber_escape(params[1]+subjectlen); msg = NULL; } } else msg = (char*) params[1]; /* bez tematu */ if ((c = newconference_find(session, target))) ismuc = 1; if (!j->istlen) { /* Very, very simple XEP-0071 support + 'modified' jabber_encode() */ msg2 = ekg_locale_to_utf8_use(msg); if ((htmlmsg = xstrchr(msg2, 18))) { /* ^R */ int omitsyntaxcheck; *(htmlmsg++) = 0; if ((omitsyntaxcheck = (*htmlmsg == 18))) htmlmsg++; htmlmsg = saprintf("" "" "%s", htmlmsg); if (!omitsyntaxcheck) { XML_Parser p = XML_ParserCreate("utf-8"); /* expat syntax-checking needs the code to be embedded in some parent element * so we create the whole block here, instead of giving %s to watch_write() */ int r; if (!(r = XML_Parse(p, htmlmsg, xstrlen(htmlmsg), 1))) { enum XML_Error errc = XML_GetErrorCode(p); const char *errs; if (errc && (errs = XML_ErrorString(errc))) print_warning(target, session, "jabber_msg_xmlsyntaxerr", errs); else print_warning(target, session, "jabber_msg_xmlsyntaxerr", "unknown"); xfree(htmlmsg); xfree(subject); xfree(thread); } XML_ParserFree(p); if (!r) return -1; } } } else msg2 = msg; /* writing: */ if (j->send_watch) j->send_watch->transfer_limit = -1; if (ismuc) watch_write(j->send_watch, "", uid+5, time(NULL)); else watch_write(j->send_watch, "", chat ? "type=\"chat\" " : "", /* j->istlen ? "type=\"normal\" " : "", */ uid+5, time(NULL)); if (subject) { watch_write(j->send_watch, "%s", subject); xfree(subject); } if (thread) { watch_write(j->send_watch, "%s", thread); xfree(thread); } if (!msg2) goto nomsg; if (session_int_get(session, "__gpg_enabled") == 1) { char *e_msg = xstrdup(msg2); if ((e_msg = jabber_openpgp(session, uid, JABBER_OPENGPG_ENCRYPT, e_msg, NULL, NULL))) { watch_write(j->send_watch, "%s" "This message was encrypted by ekg2! (EKG2 BABY) Sorry if you cannot decode it ;)", e_msg); secure = 1; xfree(e_msg); } } if (!secure /* || j->istlen */) { char *tmp = (j->istlen ? tlen_encode(msg2) : xml_escape(msg2)); watch_write(j->send_watch, "%s", tmp); xfree(tmp); } if (!j->istlen) recode_xfree(msg, msg2); /* recoded string */ if (config_last & 4) last_add(1, uid, time(NULL), 0, params[1]); nomsg: if (htmlmsg) { watch_write(j->send_watch, "%s", htmlmsg); xfree(htmlmsg); } if (!j->istlen) watch_write(j->send_watch, "%s%s", ( config_display_ack & 1 ? "" : ""), /* ? */ ( config_display_ack & 2 ? "" : ""), /* ? */ ( chat && ((config_jabber_disable_chatstates & 7) != 7) ? "" : "")); watch_write(j->send_watch, ""); JABBER_COMMIT_DATA(j->send_watch); msgdisplay: if (!quiet && !ismuc) { /* if (1) ? */ char **rcpts = xcalloc(2, sizeof(char *)); char *msg = xstrdup(params[1]); int class = (chat) ? EKG_MSGCLASS_SENT_CHAT : EKG_MSGCLASS_SENT; guint32 *format= jabber_msg_format(msg, NULL /*XXX: pass htmlmsg as xmlnode_t ...*/); rcpts[0] = xstrdup(uid); rcpts[1] = NULL; if (ismuc) class |= EKG_NO_THEMEBIT; protocol_message_emit(session, session->uid, rcpts, msg, format, time(NULL), class, NULL, EKG_NO_BEEP, secure); xfree(msg); g_strfreev(rcpts); if (!session_connected_get(session)) return msg_queue_add(session_uid_get(session), uid, params[1], "offline", class); } if (!quiet) session_unidle(session); return 0; } static COMMAND(jabber_command_inline_msg) { const char *p[2] = { NULL, params[0] }; if (!params[0] || !target) return -1; return jabber_command_msg(("chat"), p, session, target, quiet); } static COMMAND(jabber_command_xml) { jabber_private_t *j = session_private_get(session); if (!(j->send_watch)) { printq("not_connected", session_name(session)); return -1; } watch_write(j->send_watch, "%s", params[0]); return 0; } static COMMAND(jabber_command_away) { const char *descr, *format; if (params[0]) { session_descr_set(session, (!xstrcmp(params[0], "-")) ? NULL : params[0]); ekg2_reason_changed = 1; } if (!xstrcmp(name, ("_autoback"))) { format = "auto_back"; session_status_set(session, EKG_STATUS_AUTOBACK); session_unidle(session); } else if (!xstrcmp(name, ("back"))) { format = "back"; session_status_set(session, EKG_STATUS_AVAIL); session_unidle(session); } else if (!xstrcmp(name, ("_autoaway"))) { format = "auto_away"; session_status_set(session, EKG_STATUS_AUTOAWAY); } else if (!xstrcmp(name, ("_autoxa"))) { format = "auto_xa"; session_status_set(session, EKG_STATUS_AUTOXA); } else if (!xstrcmp(name, ("away"))) { format = "away"; session_status_set(session, EKG_STATUS_AWAY); session_unidle(session); } else if (!xstrcmp(name, ("dnd"))) { format = "dnd"; session_status_set(session, EKG_STATUS_DND); session_unidle(session); } else if (!xstrcmp(name, ("ffc"))) { format = "ffc"; session_status_set(session, EKG_STATUS_FFC); session_unidle(session); } else if (!xstrcmp(name, ("xa"))) { format = "xa"; session_status_set(session, EKG_STATUS_XA); session_unidle(session); } else if (!xstrcmp(name, ("invisible"))) { format = "invisible"; session_status_set(session, EKG_STATUS_INVISIBLE); session_unidle(session); } else return -1; if (!params[0]) { char *tmp; if (!config_keep_reason) { session_descr_set(session, NULL); } else if ((tmp = ekg_draw_descr(session_status_get(session)))) { session_descr_set(session, tmp); xfree(tmp); } } descr = (char *) session_descr_get(session); ekg_update_status(session); if (descr) { char *f = saprintf("%s_descr", format); printq(f, descr, "", session_name(session)); xfree(f); } else printq(format, session_name(session)); if (session_connected_get(session)) jabber_write_status(session); return 0; } static COMMAND(jabber_command_passwd) { jabber_private_t *j = session_private_get(session); char *username; char *passwd; username = xstrdup(session->uid + 5); *(xstrchr(username, '@')) = 0; if (!params[0]) { char *tmp = password_input(NULL, NULL, 0); if (!tmp) return -1; passwd = jabber_escape(tmp); session_set(session, "__new_password", tmp); xfree(tmp); } else { passwd = jabber_escape(params[0]); session_set(session, "__new_password", params[0]); } watch_write(j->send_watch, "%s%s", j->server, j->id++, username, passwd); xfree(username); xfree(passwd); return 0; } static COMMAND(jabber_command_auth) { jabber_private_t *j = session->priv; const char *action, *__uid; char *uid; userlist_t *ul, *u, *next; jabber_userlist_private_t *up; int multi = 0, reject, result = 0; if (match_arg(params[0], 'l', "list", 2)) { const char *formats[2] = { "jabber_auth_list_req", "jabber_auth_list_unreq" }; const int masks[2] = { EKG_JABBER_AUTH_REQ, EKG_JABBER_AUTH_UNREQ }; int i, ph = -1; for (i = 0; i < 2; i++) { for (ul = session->userlist; ul; ul = ul->next) { u = ul; up = jabber_userlist_priv_get(u); if ((up->authtype & masks[i])) { if (ph < i) { print(formats[i], session_name(session)); ph = i; } print("jabber_auth_list", u->uid+5, session_name(session)); } } } if (ph == -1) print("jabber_auth_list_empty", session_name(session)); return 0; } if (params[1]) target = params[1]; else if (!target) { printq("not_enough_params", name); return -1; } if (!xstrcmp(target, "*")) { if (!(ul = session->userlist)) return -1; j->send_watch->transfer_limit = -1; u = ul; multi = 1; } else if ((__uid = jid_target2uid(session, target, quiet))) { tabnick_add(__uid); /* user jest OK, wic lepiej mie go pod rk */ if (!(u = userlist_find(session, __uid))) u = userlist_add(session, __uid, NULL); } else return -1; do { uid = xstrdup(u->uid); /* XXX: shall we check uid ? */ up = jabber_userlist_priv_get(u); next = u->next; /* u may be removed */ if (match_arg(params[0], 'r', ("request"), 2)) { if (multi && (up->authtype & EKG_JABBER_AUTH_TO)) /* already authorized */ continue; action = "subscribe"; printq("jabber_auth_request", uid+5, session_name(session)); } else if (match_arg(params[0], 'a', ("accept"), 2)) { if (multi && !(up->authtype & EKG_JABBER_AUTH_REQ)) /* already authorized */ continue; action = "subscribed"; printq((up->authtype & EKG_JABBER_AUTH_REQ) ? "jabber_auth_accept" : "jabber_auth_acceptnoreq", uid+5, session_name(session)); if (ekg_group_member(u, "__authreq")) /* (s)he would be readded in a moment */ userlist_remove(session, u); } else if (match_arg(params[0], 'c', ("cancel"), 2)) { if (multi && !(up->authtype & EKG_JABBER_AUTH_TO)) /* not yet authorized */ continue; action = "unsubscribe"; printq("jabber_auth_unsubscribed", uid+5, session_name(session)); } else if (((reject = match_arg(params[0], 'j', "reject", 3))) || match_arg(params[0], 'd', ("deny"), 2)) { if (multi) { if (!(up->authtype & (( reject ? 0 : EKG_JABBER_AUTH_FROM ) | EKG_JABBER_AUTH_REQ | EKG_JABBER_AUTH_UNREQ))) continue; } else if (reject && !(up->authtype & (EKG_JABBER_AUTH_REQ | EKG_JABBER_AUTH_UNREQ))) { printq("jabber_auth_noreq", uid+5, session_name(session)); result = -1; xfree(uid); break; } action = "unsubscribed"; printq( (up->authtype & EKG_JABBER_AUTH_FROM) ? "jabber_auth_cancel" :"jabber_auth_denied", uid+5, session_name(session)); if (ekg_group_member(u, "__authreq")) /* we don't want you! */ userlist_remove(session, u); } else if (match_arg(params[0], 'p', ("probe"), 2)) { /* TLEN ? */ /* ha! undocumented :-); [Used on server only. Client authors need not worry about this.] */ action = "probe"; printq("jabber_auth_probe", uid+5, session_name(session)); } else { printq("invalid_params", name, params[0]); result = -1; xfree(uid); break; } /* NOTE: libtlen send this without id */ watch_write(j->send_watch, "", uid+5, action); xfree(uid); } while ( multi && (u = next) ); if (multi) JABBER_COMMIT_DATA(j->send_watch); return result; } static COMMAND(jabber_command_modify) { jabber_private_t *j = session->priv; int addcom = !xstrcmp(name, ("add")); const char *uid = NULL; char *nickname = NULL; struct ekg_group *gl; userlist_t *u, *u_tmp; /* instead of PARAMASTARGET, 'cause that one fails with /add username in query */ if (get_uid(session, params[0])) { /* XXX: create&use shift()? */ target = params[0]; params++; } u = userlist_find(session, target); if (u && addcom) { /* don't allow to add user again */ printq("user_exists_other", (params[0] ? params[0] : target), format_user(session, u->uid), session_name(session)); return -1; } if (!u && !addcom) { printq("user_not_found", target); return -1; } /* XXX: jid_target2uid() ? */ if (!(uid = jid_target2uid(session, target, quiet))) return -1; if (!u) u = xmalloc(sizeof(userlist_t)); /* alloc temporary memory for /xmpp:add */ if (addcom) { nickname = tlenjabber_escape(params[0]); u->uid = uid; u->nickname = nickname; } else if (params[0]) { char **argv = array_make(params[0], " \t", 0, 1, 1); int i; for (i = 0; argv[i]; i++) { if (match_arg(argv[i], 'g', ("group"), 2) && argv[i + 1]) { char **tmp = array_make(argv[++i], ",", 0, 1, 1); int x, off; /* jeli zaczyna si od '@', pomijamy pierwszy znak */ for (x = 0; tmp[x]; x++) switch (*tmp[x]) { case '-': off = (tmp[x][1] == '@' && xstrlen(tmp[x]) > 1) ? 1 : 0; if (ekg_group_member(u, tmp[x] + 1 + off)) { ekg_group_remove(u, tmp[x] + 1 + off); } else { printq("group_member_not_yet", format_user(session, uid), tmp[x] + 1); } break; case '+': off = (tmp[x][1] == '@' && xstrlen(tmp[x]) > 1) ? 1 : 0; if (!ekg_group_member(u, tmp[x] + 1 + off)) { ekg_group_add(u, tmp[x] + 1 + off); } else { printq("group_member_already", format_user(session, uid), tmp[x] + 1); } break; default: off = (tmp[x][0] == '@' && xstrlen(tmp[x]) > 1) ? 1 : 0; if (!ekg_group_member(u, tmp[x] + off)) { ekg_group_add(u, tmp[x] + off); } else { printq("group_member_already", format_user(session, uid), tmp[x]); } } g_strfreev(tmp); continue; } /* emulate gg:modify behavior */ if (!j->istlen && match_arg(argv[i], 'o', ("online"), 2)) { /* only jabber:iq:privacy */ command_exec_format(target, session, 0, ("/xmpp:privacy --set %s +pin"), uid); continue; } if (!j->istlen && match_arg(argv[i], 'O', ("offline"), 2)) { /* only jabber:iq:privacy */ command_exec_format(target, session, 0, ("/xmpp:privacy --set %s -pin"), uid); continue; } /* if this is -n smth */ /* OR if param doesn't looks like command treat as a nickname */ if ((match_arg(argv[i], 'n', ("nickname"), 2) && argv[i + 1] && i++) || argv[i][0] != '-') { if ((u_tmp=userlist_find(session, argv[i])) && (u_tmp!=u) ) { printq("user_exists", argv[i], session_name(session)); continue; } xfree(nickname); nickname = tlenjabber_escape(argv[i]); continue; } } g_strfreev(argv); } if (!nickname && !addcom) nickname = tlenjabber_escape(u->nickname); /* use current nickname */ if (j->send_watch) j->send_watch->transfer_limit = -1; /* let's send this in one/two packets not in 7 or more. */ watch_write(j->send_watch, ""); /* nickname always should be set */ if (nickname) watch_write(j->send_watch, "", uid+5, nickname, (u->groups ? "" : "/")); else watch_write(j->send_watch, "", uid+5, (u->groups ? "" : "/")); for (gl = u->groups; gl; gl = gl->next) { struct ekg_group *g = gl; char *gname = tlenjabber_escape(g->name); watch_write(j->send_watch, "%s", gname); xfree(gname); } if (u->groups) watch_write(j->send_watch, ""); watch_write(j->send_watch, ""); JABBER_COMMIT_DATA(j->send_watch); xfree(nickname); if (addcom) { xfree(u); return (session_int_get(session, "auto_auth") & 16 ? 0 : command_exec_format(target, session, quiet, ("/auth --request %s"), uid)); } return 0; } static COMMAND(jabber_command_del) { jabber_private_t *j = session->priv; int del_all = !xstrcmp(params[0], "*"); userlist_t *u; if (del_all) { userlist_t *ul; if (!session->userlist) { printq("list_empty", session_name(session)); return 1; } if (j->send_watch) j->send_watch->transfer_limit = -1; watch_write(j->send_watch, ""); for (ul = session->userlist; ul; ul = ul->next) { userlist_t *u = ul; watch_write(j->send_watch, "", u->uid+5); } watch_write(j->send_watch, ""); JABBER_COMMIT_DATA(j->send_watch); printq("user_cleared_list", session_name(session)); return 0; } if (!(u = userlist_find(session, target))) { printq("user_not_found", target); return 1; } watch_write(j->send_watch, "", u->uid + 5); printq("user_deleted", target, session_name(session)); return 0; } /* * Warning! This command is kinda special: * it needs destination uid, so destination must be in our userlist. * It won't work for unknown JIDs. * When implementing new command don't use jabber_command_ver as template. */ static COMMAND(jabber_command_ver) { const char *uid; userlist_t *ut; ekg_resource_t *rl; int once = 0; if (!(uid = jid_target2uid(session, target, quiet))) return -1; if (!(ut = userlist_find(session, uid))) { print("user_not_found", target); return -1; } if (ut->status <= EKG_STATUS_NA) { print("jabber_status_notavail", session_name(session), ut->uid); return -1; } if (!ut->resources) { print("jabber_unknown_resource", session_name(session), target); return -1; } for (rl = ut->resources; rl; rl = rl->next) { /* send query to each resource */ ekg_resource_t *r = rl; char *to = saprintf("%s/%s", uid + 5, r->name); if (!jabber_iq_send(session, "versionreq_", JABBER_IQ_TYPE_GET, to, "query", "jabber:iq:version") && !once) { printq("generic_error", "Error while sending jabber:iq:version request, check debug window"); once = 1; } } return 0; } static COMMAND(jabber_command_userinfo) { const char *uid; /* jabber id: [user@]host[/resource] */ if (!(uid = jid_target2uid(session, target, quiet))) return -1; if (!jabber_iq_send(session, "vcardreq_", JABBER_IQ_TYPE_GET, uid + 5, "vCard", "vcard-temp")) { printq("generic_error", "Error while sending vCard request, check debug window"); return 1; } return 0; } static char *jabber_avatar_load(session_t *s, const char *path, const int quiet) { const char *fn = prepare_path_user(path); FILE *fd; struct stat st; char buf[16385]; /* XEP-0153 says we should limit avatar size to 8k, but I like to be honest and give 16k */ int len; /* code from dcc */ if (!fn) { printq("generic_error", "path too long"); /* XXX? */ return NULL; } if (!stat(fn, &st) && !S_ISREG(st.st_mode)) { printq("io_nonfile", path); return NULL; } if ((fd = fopen(fn, "r")) == NULL) { printq("io_cantopen", path, strerror(errno)); return NULL; } if (!(len = fread(buf, 1, sizeof(buf), fd))) { if (ferror(fd)) printq("io_cantread", path, strerror(errno)); /* can we use errno here? */ else printq("io_emptyfile", path); } else if (len >= sizeof(buf)) printq("io_toobig", path, ekg_itoa(len), sizeof(buf)-1); else { char *enc = base64_encode(buf, len); /* XXX base64_encode() CHANGED!! len+1 ? */ char *out; const char *type = "application/octet-stream"; string_t str = string_init(NULL); int enclen = xstrlen(enc); char *p = enc; /* those are from 'magic.mime', from 'file' utility */ if (len > 4 && !xstrncmp(buf, "\x89PNG", 4)) type = "image/png"; else if (len > 3 && !xstrncmp(buf, "GIF", 3)) type = "image/gif"; else if (len > 2 && !xstrncmp(buf, "\xFF\xD8", 2)) type = "image/jpeg"; fclose(fd); session_set(s, "photo_hash", jabber_sha1_generic(buf, len)); while (enclen > 72) { string_append_n(str, p, 72); string_append_c(str, '\n'); p += 72; enclen -= 72; } string_append(str, p); xfree(enc); out = saprintf("%s\n%s\n", type, str->str); string_free(str, 1); return out; } fclose(fd); return NULL; } /** * jabber_command_change() * * Changes data in vcard. * * @todo - reimplement it using jabber_params_split() * - and implement more stuff. See http://www.xmpp.org/extensions/xep-0054.html */ static COMMAND(jabber_command_change) { #define pub_sz 6 #define strfix(s) (s ? s : "") jabber_private_t *j = session_private_get(session); char *pub[pub_sz] = { NULL, NULL, NULL, NULL, NULL, NULL }; char *photo = NULL; const int hadphoto = !!session_get(session, "photo_hash"); int i; for (i = 0; params[i]; i++) { if (match_arg(params[i], 'f', ("fullname"), 2) && params[i + 1]) { pub[0] = (char *) params[++i]; } else if (match_arg(params[i], 'n', ("nickname"), 2) && params[i + 1]) { pub[1] = (char *) params[++i]; } else if (match_arg(params[i], 'c', ("city"), 2) && params[i + 1]) { pub[2] = (char *) params[++i]; } else if (match_arg(params[i], 'b', ("born"), 2) && params[i + 1]) { pub[3] = (char *) params[++i]; } else if (match_arg(params[i], 'd', ("description"), 2) && params[i + 1]) { pub[4] = (char *) params[++i]; } else if (match_arg(params[i], 'C', ("country"), 2) && params[i + 1]) { pub[5] = (char *) params[++i]; } else if (match_arg(params[i], 'p', ("photo"), 2) && params[i + 1]) { photo = (char *) params[++i]; } } for (i=0; isend_watch, "" "%s" "%s" "%s%s" "%s%s%s\n", strfix(pub[0]), strfix(pub[1]), strfix(pub[2]), strfix(pub[5]), strfix(pub[3]), strfix(pub[4]), strfix(photo)); if (photo || hadphoto) jabber_write_status(session); xfree(photo); for (i=0; i 2) ret[num++] = xstrdup (arr[i]+2); else if (allow_empty) { ret[num++] = xstrdup(""); } else { g_strfreev(arr); ret[num] = NULL; g_strfreev(ret); return NULL; //ret[num++] = xstrdup (""); } i++; } else { // this is the name of next param, so use "" as value and // do not increment i, so we'll parse it in next loop if (arr[i][0] == '-' && arr[i][1] == '-' && xstrlen(arr[i]) > 2) ret[num++] = xstrdup(""); else { ret[num++] = xstrdup(arr[i]); i++; } } z^=1; } // if the last is --param if (z) { ret = (char **)xrealloc (ret, (num + 2)*sizeof (char *)); ret[num++] = xstrdup(""); } ret [num] = NULL; g_strfreev(arr); i = 0; while (ret[i]) { debug (" *[%d]* %s\n", i, ret[i]); i++; } return ret; } /** * jabber_command_search() * * @note implementation bug ? should server be last variable? * * @note any server support jabber:iq:search ? * */ static COMMAND(jabber_command_search) { jabber_private_t *j = session_private_get(session); const char *server = params[0] ? params[0] : jabber_default_search_server ? jabber_default_search_server : j->server; /* XXX j->server */ char **splitted = NULL; const char *id; if (g_strv_length((char **) params) > 1 && !(splitted = jabber_params_split(params[1], 0))) { printq("not_enough_params", name); return -1; } if (!(id = jabber_iq_reg(session, "search_", server, "query", "jabber:iq:search"))) { printq("generic_error", "Error in getting id for search request, check debug window"); g_strfreev(splitted); return 1; } if (j->send_watch) j->send_watch->transfer_limit = -1; watch_write(j->send_watch, "", params[1] ? "set" : "get", server, id); if (splitted) { int i = 0; int use_x_data = 0; if (!xstrcmp(splitted[0], "jabber_x_data")) { use_x_data = 1; i = 2; watch_write(j->send_watch, ""); } for (; (splitted[i] && splitted[i+1]); i+=2) { char *value = jabber_escape(splitted[i+1]); if (use_x_data) watch_write(j->send_watch, "%s", splitted[i], value); else watch_write(j->send_watch, "<%s>%s", splitted[i], value, splitted[i]); xfree(value); } if (use_x_data) watch_write(j->send_watch, ""); } watch_write(j->send_watch, ""); g_strfreev(splitted); JABBER_COMMIT_DATA(j->send_watch); return 0; } static COMMAND(jabber_command_privacy) { /* jabber:iq:privacy in ekg2 (RFC 3921) by my watch */ jabber_private_t *j = jabber_private(session); int needsync = 0; if (!params[0] || !xstrcmp(params[0], "--lists")) { /* Usage: --lists -- request for lists */ const char *id; if (!(id = jabber_iq_reg(session, "privacy_", NULL, "query", "jabber:iq:privacy"))) { printq("generic_error", "Error in getting id for jabber:iq:privacy request, check debug window"); return 1; } watch_write(j->send_watch, "", id); return 0; } if (!xstrcmp(params[0], "--session") || !xstrcmp(params[0], "--default")) { /* Usage: --session -- set session's list */ /* Usage: --default -- set default list */ char *val = jabber_escape(params[1]); int unset = !xstrcmp(params[1], "-"); char *tag = !xstrcmp(params[0], "--session") ? "active" : "default"; if (!val) { /* XXX, display current default/session list? */ printq("not_enough_params", name); return -1; } if (unset) watch_write(j->send_watch, "<%s/>", j->id++, tag); else watch_write(j->send_watch, "<%s name=\"%s\"/>", j->id++, tag, val); xfree(val); return 0; } if (!xstrcmp(params[0], "--get")) { /* display list */ /* Usage: --get [list] -- display list */ char *val = jabber_escape(params[1] ? params[1] : session_get(session, "privacy_list")); watch_write(j->send_watch, "", j->id++, val ? val : "ekg2"); xfree(val); return 0; } if (!xstrcmp(params[0], "--modify")) { /* modify list's entry */ printq("not_implemented"); return -1; needsync = 1; } if (!xstrcmp(params[0], "--remove")) { /* delete list's entry */ if (!params[1]) { printq("not_enough_params", name); return -1; } if (params[1][0] == '#' && params[1][1]) { int liczba = atoi(&(params[1][1])); jabber_iq_privacy_t *p; if (!liczba) { printq("invalid_params", name, params[1]); return -1; } if ((p = list_get_nth(j->privacy, liczba))) { jabber_privacy_freeone(j, p); goto privacy_delete_ok; } printq("invalid_params", name, params[1]); /* invalid_id ? */ return -1; } printq("not_implemented"); return -1; privacy_delete_ok: needsync = 1; } if (!xstrcmp(params[0], "--set")) { /* Usage: --set xmpp:/@grupa/typ [opcje] * --order xyz : only with new lists... if you want to modify, please use --modify * -* : set order to 1, enable blist[PRIVACY_LIST_ALL] * +* : set order to 0, enable alist[PRIVACY_LIST_ALL] * -* +pin +pout +msg: set order to 1, enable alist[PRIVACY_LIST_PRESENCE_IN, PRIVACY_LIST_PRESENCE_OUT, PRIVACY_LIST_MESSAGE] && blist[PRIVACY_LIST_ALL] * -pout -pin : (order doesn't matter) enable blist[PRIVACY_LIST_PRESENCE_IN, PRIVACY_LIST_PRESENCE_OUT] */ const char *type; /* items & PRIVACY_LIST_ALL) && allowlist->order > denylist->order) { /* swap order if -* is passed (firstlist should be allowlist) */ int tmp = denylist->order; denylist->order = allowlist->order; allowlist->order = tmp; } #endif if (params[2]) { /* parsing params[2] */ char **p = array_make(params[2], " ", 0, 1, 0); jabber_iq_privacy_t *allowlist= NULL; jabber_iq_privacy_t *denylist = NULL; unsigned int order = 0; int opass = 0; int i; list_t l; for (l = j->privacy; l; l = l->next) { jabber_iq_privacy_t *p = l->data; if (!xstrcmp(p->value, value) && !xstrcmp(p->type, type)) { if (p->allow) allowlist = p; else denylist = p; } } for (i=0; p[i]; i++) { int flag = -1; /* bitmask PRIVACY_LIST_MESSAGE...PRIVACY_LIST_ALL */ char *cur = p[i]; /* current */ jabber_iq_privacy_t *lista = NULL; jabber_iq_privacy_t *lista2= NULL; if (!xstrcmp(p[i], "--order") && p[i + 1]) { order = atoi(p[++i]); opass = 1; continue; } if (cur[0] == '-') { if (!denylist) denylist = xmalloc(sizeof(jabber_iq_privacy_t)); lista = denylist; cur++; lista2= allowlist; } else if (cur[0] == '+') { if (!allowlist) allowlist = xmalloc(sizeof(jabber_iq_privacy_t)); lista = allowlist; cur++; lista2 = denylist; } if (!xstrcmp(cur, "iq")) flag = PRIVACY_LIST_IQ; else if (!xstrcmp(cur, "msg")) flag = PRIVACY_LIST_MESSAGE; else if (!xstrcmp(cur, "pin")) flag = PRIVACY_LIST_PRESENCE_IN; else if (!xstrcmp(cur, "pout")) flag = PRIVACY_LIST_PRESENCE_OUT; else if (!xstrcmp(cur, "*")) flag = PRIVACY_LIST_ALL; if (flag == -1 || !lista) { debug("[JABBER, PRIVACY] INVALID PARAM @ p[%d] = %s... [%d, 0x%x] \n", i, cur, flag, lista); printq("invalid_params", name, cur); /* XXX cur? */ if (allowlist && !allowlist->value) xfree(allowlist); if (denylist && !denylist->value) xfree(denylist); g_strfreev(p); return -1; } lista->items |= flag; if (flag != PRIVACY_LIST_ALL && lista2 && (lista2->items & flag)) lista2->items &= ~flag; /* uncheck it on 2nd list */ } g_strfreev(p); /* jesli nie podano order, to bierzemy ostatnia wartosc */ if (!opass && !order && ( (denylist && !denylist->value) || (allowlist && !allowlist->value) )) for (l = j->privacy; l; l = l->next) { jabber_iq_privacy_t *p = l->data; if (!l->next) order = p->order+1; } /* podano order, musimy przesunac wszystkie elementy ktora maja ten order o 1 lub 2 (jesli jest tez denylist) w przod) */ else if (opass && order) for (l = j->privacy; l; l = l->next) { jabber_iq_privacy_t *p = l->data; int nextorder = (allowlist && denylist && !allowlist->value && !denylist->value); if (p == allowlist || p == denylist) continue; if (p->order == order || p->order == order+nextorder) { jabber_iq_privacy_t *m = l->data; list_t j = l; if (p->order == order+nextorder) nextorder--; do { m = j->data; m->order += (1 + nextorder); j = j->next; } while (j && ((jabber_iq_privacy_t *) j->data)->order <= m->order); break; } } /* jesli jest -* to wtedy allowlista powinna byc przed denylista */ if (denylist && !denylist->value && denylist->items & PRIVACY_LIST_ALL && allowlist && !allowlist->value) denylist->order = 1; /* w przeciwnym wypadku najpierw deynlista */ else if (denylist && allowlist && !denylist->value && !allowlist->value) allowlist->order = 1; if (allowlist && !allowlist->value) { allowlist->value = xstrdup(value); allowlist->type = xstrdup(type); allowlist->allow = 1; allowlist->order += order; LIST_ADD_SORTED(&j->privacy, allowlist, jabber_privacy_add_compare); } if (denylist && !denylist->value) { denylist->value = xstrdup(value); denylist->type = xstrdup(type); /* denylist->allow = 0; */ denylist->order += order; LIST_ADD_SORTED(&j->privacy, denylist, jabber_privacy_add_compare); } } needsync = 1; } if (((needsync && !j->privacy) || !xstrcmp(params[0], "--unset"))) { /* usage: --unset [lista] Unset / remove list */ char *val = jabber_escape(!needsync && params[1] ? params[1] : session_get(session, "privacy_list")); watch_write(j->send_watch, "", j->id++, val ? val : "ekg2"); xfree(val); return 0; } if ((needsync || !xstrcmp(params[0], "--sync")) && j->privacy) { /* Usage: --sync -- sync default list [internal use] */ static unsigned int last_order; char *val = jabber_escape(!needsync && params[1] ? params[1] : session_get(session, "privacy_list")); list_t l; last_order = 0; if (j->send_watch) j->send_watch->transfer_limit = -1; watch_write(j->send_watch, "", j->id++, val ? val : "ekg2"); for (l = j->privacy; l; l = l->next) { jabber_iq_privacy_t *p = l->data; char *eval; if (!p->items) continue; /* XXX, remove? */ eval = jabber_escape(p->value); /* XXX XXX */ if (last_order == p->order) p->order++; last_order = p->order; watch_write(j->send_watch, "", p->type, /* type (jid/group/subscription)*/ eval, /* value */ p->allow ? "allow" : "deny", /* action */ p->order); /* order */ if (p->items & PRIVACY_LIST_MESSAGE) watch_write(j->send_watch, ""); if (p->items & PRIVACY_LIST_IQ) watch_write(j->send_watch, ""); if (p->items & PRIVACY_LIST_PRESENCE_IN) watch_write(j->send_watch, ""); if (p->items & PRIVACY_LIST_PRESENCE_OUT) watch_write(j->send_watch, ""); watch_write(j->send_watch, ""); } watch_write(j->send_watch, ""); xfree(val); JABBER_COMMIT_DATA(j->send_watch); return 0; } if (params[0] && params[0][0] != '-') /* jesli nie opcja, to pewnie jest to lista, wyswietlamy liste */ return command_exec_format(target, session, 0, "/xmpp:privacy --get %s", params[0]); print("invalid_params", name, params[0]); return 1; } /** * jabber_command_private() * * @todo Read f**cking XEP-0048,--0049, and remove security hole in jabber_handle_iq() * I suspect there should be somewhere info, about to= * (We recv data from our JID (or requested), not server one.) */ static COMMAND(jabber_command_private) { jabber_private_t *j = jabber_private(session); char *namespace; /* */ int config = 0; /* 1 if name == jid:config */ int bookmark = 0; /* 1 if name == jid:bookmark */ if (!xstrcmp(name, ("config"))) config = 1; if (!xstrcmp(name, ("bookmark"))) bookmark = 1; if (config) namespace = ("ekg2 xmlns=\"ekg2:prefs\""); else if (bookmark) namespace = ("storage xmlns=\"storage:bookmarks\""); else namespace = (char *) params[1]; if (bookmark) { /* bookmark-only-commands */ int bookmark_sync = 0; /* 0 - no sync; 1 - sync (item added); 2 - sync (item modified) 3 - sync (item removed) */ if (match_arg(params[0], 'a', ("add"), 2)) bookmark_sync = 1; /* add item */ if (match_arg(params[0], 'm', ("modify"), 2)) bookmark_sync = 2; /* modify item */ if (match_arg(params[0], 'r', ("remove"), 2)) bookmark_sync = 3; /* remove item */ if (bookmark_sync) { const char *p[2] = {("-p"), NULL}; /* --put */ char **splitted = NULL; splitted = jabber_params_split(params[1], 1); if (!splitted) { printq("not_enough_params", name); return -1; } switch (bookmark_sync) { case (1): { /* add item */ /* Usage: * /jid:bookmark --add --url url [-- name] * /jid:bookmark --add --conf jid [--autojoin 1] [--nick cos] [--pass cos] [-- name] */ jabber_bookmark_t *book = NULL; if (!xstrcmp(splitted[0], "url")) { book = xmalloc(sizeof(jabber_bookmark_t)); book->type = JABBER_BOOKMARK_URL; book->priv_data.url = xmalloc(sizeof(jabber_bookmark_url_t)); book->priv_data.url->name = xstrdup(jabber_attr(splitted, "")); book->priv_data.url->url = xstrdup(splitted[1]); } else if (!xstrcmp(splitted[0], "conf")) { book = xmalloc(sizeof(jabber_bookmark_t)); book->type = JABBER_BOOKMARK_CONFERENCE; book->priv_data.conf = xmalloc(sizeof(jabber_bookmark_conference_t)); book->priv_data.conf->name = xstrdup(jabber_attr(splitted, "")); book->priv_data.conf->jid = xstrdup(splitted[1]); book->priv_data.conf->nick= xstrdup(jabber_attr(splitted, "nick")); book->priv_data.conf->pass= xstrdup(jabber_attr(splitted, "pass")); if (jabber_attr(splitted, "autojoin") && atoi(jabber_attr(splitted, "autojoin"))) book->priv_data.conf->autojoin = 1; /* else book->priv_data.conf->autojoin = 0; */ } else bookmark_sync = -1; if (book) list_add(&(j->bookmarks), book); } break; case (2): /* modify item XXX */ case (3): /* remove item XXX */ default: /* error */ bookmark_sync = -bookmark_sync; /* make it negative -- error */ debug("[JABBER, BOOKMARKS] switch(bookmark_sync) sync=%d ?!\n", bookmark_sync); } if (bookmark_sync <0) printq("invalid_params", name, splitted[0]); g_strfreev(splitted); if (bookmark_sync > 0) { return jabber_command_private(name, (const char **) p, session, target, quiet); /* synchronize db */ } else if (bookmark_sync < 0) { debug("[JABBER, BOOKMARKS] sync=%d\n", bookmark_sync); return -1; } } } if (match_arg(params[0], 'g', ("get"), 2) || match_arg(params[0], 'd', ("display"), 2)) { /* get/display */ const char *id; if (!(id = jabber_iq_reg(session, (match_arg(params[0], 'g', ("get"), 2) && (config || bookmark) ) ? "config_" : "private_", NULL, "query", "jabber:iq:private"))) { printq("generic_error", "Error in getting id for jabber:iq:private GET/DISPLAY request, check debug window"); return 1; } watch_write(j->send_watch, "<%s/>", id, namespace); return 0; } if (match_arg(params[0], 'p', ("put"), 2)) { /* put */ const char *id; if (!(id = jabber_iq_reg(session, "private_", NULL, "query", "jabber:iq:private"))) { printq("generic_error", "Error in getting id for jabber:iq:private PUT request, check debug window"); return 1; } if (j->send_watch) j->send_watch->transfer_limit = -1; watch_write(j->send_watch, "<%s>", id, namespace); /* Synchronize config (?) */ if (config) { GSList *pl; session_t *s; for (pl = plugins; pl; pl = pl->next) { plugin_t *p = pl->data; GSList *vl; watch_write(j->send_watch, "", p->name, p->prio); back: for (vl = variables; vl; vl = vl->next) { variable_t *v = vl->data; char *vname, *tname; if (v->plugin != p) continue; tname = vname = jabber_escape(v->name); if (p && !xstrncmp(tname, p->name, xstrlen(p->name))) tname += xstrlen(p->name); if (tname[0] == ':') tname++; switch (v->type) { case(VAR_STR): case(VAR_FILE): case(VAR_DIR): case(VAR_THEME): if (*(char **) v->ptr) watch_write(j->send_watch, "<%s>%s", tname, *(char **) v->ptr, tname); else watch_write(j->send_watch, "<%s/>", tname); break; case(VAR_INT): case(VAR_BOOL): watch_write(j->send_watch, "<%s>%d", tname, *(int *) v->ptr, tname); break; case(VAR_MAP): /* XXX TODO */ default: break; } xfree(vname); } if (p) { watch_write(j->send_watch, ""); if (!pl->next) { /* if last plugin, then jump back and write core vars */ p = NULL; goto back; } } } for (s = sessions; s; s = s->next) { plugin_t *pl = s->plugin; int i; if (!pl) { printq("generic_error", "Internal fatal error, plugin somewhere disappear. Report this bug"); continue; } watch_write(j->send_watch, "", s->uid, s->password); /* XXX, alias, descr, status */ for (i = 0; (pl->params[i].key /* && p->params[i].id != -1 */); i++) { if (s->values[i]) { char *esc = jabber_escape(s->values[i]); watch_write(j->send_watch, "<%s>%s", pl->params[i].key, esc, pl->params[i].key); xfree(esc); } else watch_write(j->send_watch, "<%s/>", pl->params[i].key); } watch_write(j->send_watch, ""); } goto put_finish; } /* Synchronize bookmarks (?) */ if (bookmark) { list_t l; for (l = j->bookmarks; l; l = l->next) { jabber_bookmark_t *book = l->data; switch (book->type) { case (JABBER_BOOKMARK_URL): { char *esc_name = jabber_escape(book->priv_data.url->name); watch_write(j->send_watch, "", esc_name, book->priv_data.url->url); xfree(esc_name); break; } case (JABBER_BOOKMARK_CONFERENCE): { char *esc_name = jabber_escape(book->priv_data.conf->name); char *esc_nick = jabber_escape(book->priv_data.conf->nick); watch_write(j->send_watch, "", esc_name, book->priv_data.conf->autojoin ? "true" : "false", book->priv_data.conf->jid); if (book->priv_data.conf->nick) watch_write(j->send_watch, "%s", esc_nick); if (book->priv_data.conf->pass) watch_write(j->send_watch, "%s", book->priv_data.conf->pass); watch_write(j->send_watch, ""); xfree(esc_nick); xfree(esc_name); break; } default: debug("[JABBER, BOOKMARK] while syncing j->bookmarks... book->type = %d wtf?\n", book->type); } } goto put_finish; } /* Do what user want */ if (params[0] && params[1] && params[2]) /* XXX check */ watch_write(j->send_watch, "%s", params[2]); put_finish: { char *beg = namespace; char *end = xstrstr(namespace, (" ")); /* if (end) *end = '\0'; */ /* SEGV? where? why? :( */ if (end) beg = xstrndup(namespace, end-namespace); else beg = xstrdup(namespace); watch_write(j->send_watch, "", beg); xfree(beg); } JABBER_COMMIT_DATA(j->send_watch); return 0; } if (match_arg(params[0], 'c', ("clear"), 2)) { /* clear */ const char *id; if (!(id = jabber_iq_reg(session, "private_", NULL, "query", "jabber:iq:private"))) { printq("generic_error", "Error in getting id for jabber:iq:private CLEAR request, check debug window"); return 1; } if (bookmark) jabber_bookmarks_free(j); /* let's destroy previously saved bookmarks */ watch_write(j->send_watch, "<%s/>", id, namespace); return 0; } printq("invalid_params", name, params[0]); return -1; } static COMMAND(jabber_command_register) { jabber_private_t *j = session_private_get(session); const char *server = params[0] ? params[0] : j->server; const char *passwd = session_get(session, "password"); const int unregister = !xstrcmp(name, "unregister"); char **splitted = NULL; if (!session_connected_get(session)) { if ((!passwd || !*passwd || !xstrcmp(passwd, "foo"))) { session_set(session, "__new_account", "1"); if (params[0]) session_set(session, "password", params[0]); jabber_command_connect(("connect"), NULL, session, target, quiet); return 0; } printq("not_connected", session_name(session)); return -1; } if (!j->send_watch) return -1; j->send_watch->transfer_limit = -1; if (g_strv_length((char **) params) > 1 && !(splitted = jabber_params_split(params[1], 0))) { printq("not_enough_params", name); return -1; } watch_write(j->send_watch, "", params[1] || unregister ? "set" : "get", server, j->id++); if (unregister) watch_write(j->send_watch, ""); if (splitted) { int i = 0; int use_x_data = 0; if (!xstrcmp(splitted[0], "jabber_x_data")) { use_x_data = 1; i = 2; watch_write(j->send_watch, ""); } for (; (splitted[i] && splitted[i+1]); i+=2) { if (use_x_data) watch_write(j->send_watch, "%s", splitted[i], splitted[i+1]); else watch_write(j->send_watch, "<%s>%s", splitted[i], splitted[i+1], splitted[i]); } if (use_x_data) watch_write(j->send_watch, ""); } watch_write(j->send_watch, ""); g_strfreev(splitted); JABBER_COMMIT_DATA(j->send_watch); return 0; } static COMMAND(jabber_command_transpinfo) { jabber_private_t *j = session_private_get(session); const char *server = params[0] ? params[0] : j->server; const char *node = (params[0] && params[1]) ? params[1] : NULL; const char *id; if (!(id = jabber_iq_reg(session, "transpinfo_", server, "query", "http://jabber.org/protocol/disco#info"))) { printq("generic_error", "Error in getting id for transport info request, check debug window"); return 1; } if (node) { watch_write(j->send_watch, "", server, id, node); } else { watch_write(j->send_watch, "", server, id); } return 0; } static COMMAND(jabber_command_transports) { jabber_private_t *j = session_private_get(session); const char *server = params[0] ? params[0] : j->server; const char *node = (params[0] && params[1]) ? params[1] : NULL; const char *id; if (!(id = jabber_iq_reg(session, "transplist_", server, "query", "http://jabber.org/protocol/disco#items"))) { printq("generic_error", "Error in getting id for transport list request, check debug window"); return 1; } if (node) { watch_write(j->send_watch, "", server, id, node); } else { watch_write(j->send_watch, "", server, id); } return 0; } static COMMAND(jabber_command_vacation) { /* JEP-0109: Vacation Messages (DEFERRED) */ jabber_private_t *j = session_private_get(session); char *message; const char *id; if (!(id = jabber_iq_reg(session, "vacationreq_", NULL, "query", "http://jabber.org/protocol/vacation"))) { printq("generic_error", "Error in getting id for vacation request, check debug window"); return 1; } message = jabber_escape(params[0]); /* XXX, porobic potwierdzenia ustawiania/ usuwania. oraz jesli nie ma statusu to wyswylic jakies 'no vacation status'... */ if (!params[0]) { watch_write(j->send_watch, "", id); } else if (xstrlen(params[0]) == 1 && params[0][0] == '-') { watch_write(j->send_watch, "", id); } else { watch_write(j->send_watch, "" "" /* XXX, startdate, enddate */ "%s" "", id, message); } xfree(message); return 0; } /** * jabber_muc_command_join() * * @param params [0] (full channel name) * @param params [1] (nickname) * @param params [2] (password) * * @todo make (session) variable jabber:default_muc && then if exists and params[0] has not specific server than append '@' jabber:default_muc and use it. * @todo make (session) variable jabber:default_nickname. * @todo history requesting, without history requesting.. etc */ static COMMAND(jabber_muc_command_join) { jabber_private_t *j = session_private_get(session); newconference_t *conf; char *tmp; char *username = (params[1]) ? xstrdup(params[1]) : (tmp = xstrchr(session->uid, '@')) ? xstrndup(session->uid+5, tmp-session->uid-5) : NULL; char *password = (params[1] && params[2]) ? saprintf("%s", params[2]) : NULL; char *mucuid; if (!username) { /* shouldn't happen */ printq("not_enough_params", name); return -1; } if (!xstrncmp(target, "xmpp:", 5)) target += 5; /* remove xmpp: */ mucuid = xmpp_uid(target); #if 0 if (newconference_find(session, mucuid)) { printq("conferences_already_joined", session_name(session), mucuid); xfree(mucuid); return 1; } #endif tmp = jabber_escape(username); watch_write(j->send_watch, "%s", target, tmp, password ? password : ""); xfree(tmp); conf = newconference_create(session, mucuid, 1); conf->priv_data = xstrdup(username); xfree(username); xfree(password); xfree(mucuid); return 0; } static COMMAND(jabber_muc_command_part) { jabber_private_t *j = session_private_get(session); newconference_t *c; char *status; if (!(c = newconference_find(session, target))) { printq("generic_error", "/xmpp:part only valid in MUC"); return -1; } status = (params[0] && params[1]) ? saprintf("%s", params[1]) : NULL; watch_write(j->send_watch, "%s", c->name+5, c->priv_data, status ? status : ""); xfree(status); newconference_destroy(c, 1 /* XXX, dorobic zmienna */); return 0; } /** * jabber_muc_command_nick() * * @param params [0] (full channel name or new nickname) * @param params [1] (new nickname) */ static COMMAND(jabber_muc_command_nick) { jabber_private_t *j = session_private_get(session); newconference_t *c; const char *nickname; const char *conference; char *conference_uid; if (params[1]) { /* there are two parameters, so param[0] is a conference name and * params[1] is a new nickname */ conference = params[0]; nickname = params[1]; } else { conference = window_current->target; nickname = params[0]; } if (!xstrncmp(conference, "xmpp:", 5)) { /* strip xmpp: prefix */ conference += 5; } conference_uid = xmpp_uid(conference); if (!(c = newconference_find(session, conference_uid))) { printq("generic_error", "/xmpp:nick only valid in MUC"); return -1; } char *tmp = jabber_escape(nickname); watch_write(j->send_watch, "", conference, tmp); xfree(tmp); xfree(conference_uid); return 0; } static COMMAND(jabber_muc_command_admin) { jabber_private_t *j = session_private_get(session); newconference_t *c; if (!(c = newconference_find(session, target))) { printq("generic_error", "/xmpp:admin only valid in MUC"); return -1; } if (!params[1]) { if (!jabber_iq_send(session, "mucowner_", JABBER_IQ_TYPE_GET, c->name+5, "query", "http://jabber.org/protocol/muc#owner")) { printq("generic_error", "Error while sending muc configuration request form, check debug window"); return 1; } } else { const char *id; char **splitted = NULL; int i; int isinstant = !xstrcmp(params[1], "--instant"); if (isinstant) { const char *id; if (!(id = jabber_iq_reg(session, "mucowner_", c->name+5, "query", "http://jabber.org/protocol/muc#owner"))) { printq("generic_error", "Error in getting id for instant room configuration, check debug window"); return 1; } watch_write(j->send_watch, "" "" "" "", c->name+5, id); return 0; } if (!(splitted = jabber_params_split(params[1], 0))) { printq("not_enough_params", name); return -1; } if (!(id = jabber_iq_reg(session, "mucowner_", c->name+5, "query", "http://jabber.org/protocol/muc#owner"))) { printq("generic_error", "Error in getting id for room configuration, check debug window"); g_strfreev(splitted); return 1; } if (j->send_watch) j->send_watch->transfer_limit = -1; watch_write(j->send_watch, "" "" "" /* "http://jabber.org/protocol/muc#roomconfig/value>" */ ,c->name+5, id); for (i=0; (splitted[i] && splitted[i+1]); i+=2) { char *name = jabber_escape(splitted[i]); char *value = jabber_escape(splitted[i+1]); watch_write(j->send_watch, "%s", name, value); xfree(value); xfree(name); } g_strfreev(splitted); watch_write(j->send_watch, ""); JABBER_COMMIT_DATA(j->send_watch); } return 0; } /* This function handles commands related to user roles: * /xmpp:kick - unset users role (remove him from channel) * /xmpp:voice - set Participant role (allow him to spead at moderated channels) * /xmpp:op - set Moderator role * /xmpp:deop - set Visitor role * /xmpp:devoice - alias for /xmpp:deop command * * TODO: * - allow to specify user by jid, not only by nick * - check if user is on the muc channel */ static COMMAND(jabber_muc_command_role) { /* %0 [target] %1 [jid] %2 [reason] */ jabber_private_t *j = session_private_get(session); newconference_t *c; const char *id, *nick; const char *role; char *reason; if (!(c = newconference_find(session, target))) { printq("generic_error", "/xmpp:kick && /xmpp:op && /xmpp:deop && /xmpp:voice && /xmpp:devoice only valid in MUC"); return -1; } nick = params[1]; if (!xstrcmp(name, "op")) role = "moderator"; else if (!xstrcmp(name, "voice")) role = "participant"; else if (!xstrcmp(name, "deop")) role = "visitor"; else if (!xstrcmp(name, "devoice")) role = "visitor"; else if (!xstrcmp(name, "kick")) role = "none"; else { printq("generic_error", "Unimplemented command"); return -1; } if (!(id = jabber_iq_reg(session, "mucadmin_", c->name+5, "query", "http://jabber.org/protocol/muc#admin"))) { printq("generic_error", "Error in getting id for ban, check debug window. Lucky guy."); return 1; } reason = jabber_escape(params[2]); watch_write(j->send_watch, "" "%s" "", id, c->name+5, role, nick, reason ? reason : ""); xfree(reason); return 0; } /* This function handles commands related to affiliation: * /xmpp:ban - set Outcast affiliation * /xmpp:unban - unset affiliation * * TODO: * - What commands should do following: * + set Owner affiliation * + set Admin affiliation * + set Member affiliation * - Implement these commands */ static COMMAND(jabber_muc_command_affiliation) { /* %0 [target] %1 [jid] %2 [reason] */ jabber_private_t *j = session_private_get(session); newconference_t *c; if (!(c = newconference_find(session, target))) { printq("generic_error", "/xmpp:ban && /xmpp:unban only valid in MUC"); return -1; } if (!params[1]) { const char *id; if (!(id = jabber_iq_reg(session, "mucadmin_", c->name+5, "query", "http://jabber.org/protocol/muc#admin"))) { printq("generic_error", "Error in getting id for banlist request, check debug window"); return 1; } watch_write(j->send_watch, "", id, c->name+5); } else { const char *affiliation; const char *id, *jid; char *reason; jid = params[1]; if (!xstrcmp(name, "ban")) affiliation = "outcast"; else if (!xstrcmp(name, "unban")) affiliation = "none"; else { printq("generic_error", "Unimplemented command"); return -1; } if (!(id = jabber_iq_reg(session, "mucadmin_", c->name+5, "query", "http://jabber.org/protocol/muc#admin"))) { printq("generic_error", "Error in getting id for ban, check debug window. Lucky guy."); return 1; } if (!xstrncmp(jid, "xmpp:", 5)) jid += 5; reason = jabber_escape(params[2]); watch_write(j->send_watch, "" "%s" "", id, c->name+5, affiliation, jid, reason ? reason : ""); xfree(reason); } return 0; } static COMMAND(jabber_muc_command_topic) { jabber_private_t *j = session_private_get(session); newconference_t *c; char *subject, *tmp=NULL; /* XXX da, /topic is possible in normal talk too... current limit only to muc. */ if (params[0] && (c = newconference_find(session, params[0]))) { subject = jabber_escape(params[1]); } else if ((c = newconference_find(session, target))) { if (params[0] && params[1]) { tmp = saprintf("%s %s", params[0], params[1]); subject = jabber_escape(tmp); xfree(tmp); } else subject = jabber_escape(params[0]); } else { printq("generic_error", "/xmpp:topic only valid in MUC"); return -1; } if (!subject) { /* XXX, display current topic */ debug_error("jabber_muc_command_topic: Current topic is ??? -- not implemented yet\n"); } else { watch_write(j->send_watch, "%s", c->name+5, subject); xfree(subject); } return 0; } /** * tlen_command_alert() * * XXX, info
* ONLY TLEN PROTOCOL * * @param params [0] - uid of target [target can be passed in params[0] COMMAND_PARAMASTARGET] [target is uid COMMAND_TARGET_VALID_UID] * * @return -1 if wrong uid/session
* 0 on success
*/ static COMMAND(tlen_command_alert) { jabber_private_t *j = jabber_private(session); if (!j->istlen) { /* check if this is tlen session */ printq("invalid_session"); return -1; } if (tolower(target[0] != 't')) { /* check if uid starts with 't' [tlen:] */ printq("invalid_uid"); return -1; } watch_write(j->send_watch, "", target+5); /* sound alert */ printq("tlen_alert_send", session_name(session), format_user(session, target)); return 0; } static COMMAND(jabber_command_reply) { jabber_private_t *j = session_private_get(session); int subjectlen = xstrlen(config_subject_prefix); int id, ret; char *tmp = NULL; jabber_conversation_t *thr = NULL; if (((params[0][0] == '#') && (id = atoi(params[0]+1)) > 0) /* #reply-id */ || ((id = atoi(params[0])) > 0)) { /* or without # */ debug("We have id = %d!\n", id); thr = jabber_conversation_get(j, id); } /* XXX: some UID/thread/whatever match? */ if (!thr) { printq("invalid_params", name, params[0]); return -1; } debug("[jabber]_reply(), thread %d, thread-id = %s, subject = %s, uid = %s...\n", id, thr->thread, thr->subject, thr->uid); /* subject here */ if (thr->subject && (!config_subject_prefix || xstrncmp(params[1], config_subject_prefix, subjectlen))) { tmp = saprintf("%s%s%s\n", config_subject_prefix, (xstrncmp(thr->subject, config_subject_reply_prefix, xstrlen(config_subject_reply_prefix)) ? config_subject_reply_prefix : ""), thr->subject); } ret = command_exec_format(target, session, 0, "/xmpp:%smsg %s %s %s%s", (thr->thread ? "t" : ""), thr->uid, (thr->thread ? thr->thread : ""), (tmp ? tmp : ""), params[1]); xfree(tmp); return ret; } static COMMAND(jabber_command_conversations) { jabber_private_t *j = session_private_get(session); int i; jabber_conversation_t *thr; if (!(thr = j->conversations)) return 0; print("jabber_conversations_begin", session_name(session)); for (i = 1; thr; i++, thr = thr->next) { print("jabber_conversations_item", ekg_itoa(i), get_nickname(session, thr->uid), (thr->subject ? thr->subject : format_find("jabber_conversations_nosubject")), (thr->thread ? thr->thread : format_find("jabber_conversations_nothread"))); } print("jabber_conversations_end"); return 0; } /* like gg:find, mix of xmpp:userinfo & xmpp:search */ static COMMAND(jabber_command_find) { if (get_uid(session, params[0])) { target = params[0]; params++; } if (params[0] || !target) /* shifted */ return jabber_command_search("search", params, session, NULL, quiet); else return jabber_command_userinfo("userinfo", params, session, target, quiet); } static COMMAND(jabber_command_userlist) { const char *listfile; FILE *f; const int replace = match_arg(params[0], 'G', "replace", 2); /* we must use other userlist path, so that ekg2 will not overwrite it */ if (params[1]) listfile = prepare_path_user(params[1]); else listfile = prepare_pathf("%s-userlist-backup", session->uid); if (match_arg(params[0], 'c', "clear", 2) || replace) { /* clear the userlist */ const char *args[] = { "*", NULL }; /* if using 'replace', we don't wan't any output from 'del *' */ jabber_command_del("del", args, session, NULL, replace); if (!replace) return 0; } if (match_arg(params[0], 'g', "get", 2) || replace) { /* fill userlist with data from file */ jabber_private_t *j = session->priv; char *line; if (!(f = fopen(listfile, "r"))) { printq("io_cantopen", listfile, strerror(errno)); return -1; } while ((line = read_file(f, 0))) { char *uid = &line[2]; char *nickname; if (xstrncmp(line, "+,", 2)) { /* XXX: '-'? */ debug_error("jabber_command_userlist(), unknown op on '%s'\n", line); continue; } if ((nickname = xstrchr(uid, ','))) { char *p; *(nickname++) = '\0'; if ((p = xstrchr(nickname, ','))) *p = '\0'; } uid = tlenjabber_uid(uid); if (userlist_find(session, uid)) { if (nickname) { command_exec_format(NULL, session, 1, "/modify %s -n \"%s\"", uid, nickname); } } else { command_exec_format(NULL, session, 1, "/add %s \"%s\"", uid, nickname); } xfree(uid); } fclose(f); printq("userlist_get_ok", session_name(session)); return 0; } if (match_arg(params[0], 'p', "put", 2)) { /* write userlist into file */ userlist_t *ul; if (!(f = fopen(listfile, "w"))) { printq("io_cantopen", listfile, strerror(errno)); return -1; } for (ul = session->userlist; ul; ul = ul->next) { userlist_t *u = ul; fprintf(f, "+,%s,%s,\n", u->uid+5, u->nickname /*, XXX? */); /* JRU syntax */ } fclose(f); printq("userlist_put_ok", session_name(session)); return 0; } printq("invalid_params", name, params[0]); return -1; } static COMMAND(jabber_command_stanzas) { jabber_private_t *j = session_private_get(session); list_t l; for (l = j->iq_stanzas; l; l = l->next) { jabber_stanza_t *st = l->data; printq("jabber_iq_stanza", session_name(session), st->type, st->xmlns, st->to, st->id); } return 0; } void jabber_register_commands() { #define JABBER_ONLY SESSION_MUSTBELONG | SESSION_MUSTHASPRIVATE #define JABBER_FLAGS JABBER_ONLY | SESSION_MUSTBECONNECTED #define JABBER_FLAGS_REQ JABBER_FLAGS | COMMAND_ENABLEREQPARAMS #define JABBER_FLAGS_TARGET JABBER_FLAGS_REQ | COMMAND_PARAMASTARGET #define JABBER_FLAGS_TARGET_VALID JABBER_FLAGS_TARGET | COMMAND_TARGET_VALID_UID #define JABBER_FLAGS_MSG JABBER_ONLY | COMMAND_ENABLEREQPARAMS | COMMAND_PARAMASTARGET command_add(&jabber_plugin, "xmpp:", "?", jabber_command_inline_msg, JABBER_ONLY | COMMAND_PASS_UNCHANGED, NULL); command_add(&jabber_plugin, "xmpp:_autoaway", "r", jabber_command_away, JABBER_ONLY, NULL); command_add(&jabber_plugin, "xmpp:_autoxa", "r", jabber_command_away, JABBER_ONLY, NULL); command_add(&jabber_plugin, "xmpp:_autoback", "r", jabber_command_away, JABBER_ONLY, NULL); command_add(&jabber_plugin, "xmpp:_stanzas", "?", jabber_command_stanzas, JABBER_ONLY, NULL); command_add(&jabber_plugin, "xmpp:add", "U ?", jabber_command_modify, JABBER_FLAGS, NULL); command_add(&jabber_plugin, "xmpp:admin", "! ?", jabber_muc_command_admin, JABBER_FLAGS_TARGET, NULL); command_add(&jabber_plugin, "xmpp:auth", "!p uU", jabber_command_auth, JABBER_FLAGS_REQ, "-a --accept -d --deny -r --request -c --cancel"); command_add(&jabber_plugin, "xmpp:away", "r", jabber_command_away, JABBER_ONLY, NULL); command_add(&jabber_plugin, "xmpp:back", "r", jabber_command_away, JABBER_ONLY, NULL); command_add(&jabber_plugin, "xmpp:ban", "! ? ?", jabber_muc_command_affiliation, JABBER_FLAGS_TARGET, NULL); command_add(&jabber_plugin, "xmpp:bookmark", "!p ?", jabber_command_private, JABBER_FLAGS_REQ, "-a --add -c --clear -d --display -m --modify -r --remove"); command_add(&jabber_plugin, "xmpp:config", "!p", jabber_command_private, JABBER_FLAGS_REQ, "-c --clear -d --display -g --get -p --put"); command_add(&jabber_plugin, "xmpp:change", "!p ? p ? p ? p ? p ? p ?", jabber_command_change, JABBER_FLAGS_REQ, "-f --fullname -c --city -b --born -d --description -n --nick -C --country"); command_add(&jabber_plugin, "xmpp:chat", "!uU !", jabber_command_msg, JABBER_FLAGS_MSG, NULL); command_add(&jabber_plugin, "xmpp:connect", NULL, jabber_command_connect, JABBER_ONLY, NULL); command_add(&jabber_plugin, "xmpp:conversations", NULL, jabber_command_conversations, JABBER_FLAGS, NULL); command_add(&jabber_plugin, "xmpp:del", "!u", jabber_command_del, JABBER_FLAGS_TARGET, NULL); command_add(&jabber_plugin, "xmpp:deop", "! !", jabber_muc_command_role, JABBER_FLAGS_TARGET, NULL); command_add(&jabber_plugin, "xmpp:devoice", "! !", jabber_muc_command_role, JABBER_FLAGS_TARGET, NULL); command_add(&jabber_plugin, "xmpp:disconnect", "r", jabber_command_disconnect, JABBER_ONLY, NULL); command_add(&jabber_plugin, "xmpp:dnd", "r", jabber_command_away, JABBER_ONLY, NULL); command_add(&jabber_plugin, "xmpp:ffc", "r", jabber_command_away, JABBER_ONLY, NULL); command_add(&jabber_plugin, "xmpp:find", "?", jabber_command_find, JABBER_FLAGS, NULL); command_add(&jabber_plugin, "xmpp:invisible", "r", jabber_command_away, JABBER_ONLY, NULL); command_add(&jabber_plugin, "xmpp:join", "! ? ?", jabber_muc_command_join, JABBER_FLAGS_TARGET, NULL); command_add(&jabber_plugin, "xmpp:kick", "! ! ?", jabber_muc_command_role, JABBER_FLAGS_TARGET, NULL); command_add(&jabber_plugin, "xmpp:lastseen", "!u", jabber_command_lastseen, JABBER_FLAGS_TARGET, NULL); command_add(&jabber_plugin, "xmpp:modify", "!Uu ?", jabber_command_modify,JABBER_FLAGS_REQ, "-n --nickname -g --group"); command_add(&jabber_plugin, "xmpp:msg", "!uU !", jabber_command_msg, JABBER_FLAGS_MSG, NULL); command_add(&jabber_plugin, "xmpp:nick", "!C ?", jabber_muc_command_nick, JABBER_FLAGS_REQ, NULL); command_add(&jabber_plugin, "xmpp:op", "! !", jabber_muc_command_role, JABBER_FLAGS_TARGET, NULL); command_add(&jabber_plugin, "xmpp:part", "! ?", jabber_muc_command_part, JABBER_FLAGS_TARGET, NULL); command_add(&jabber_plugin, "xmpp:passwd", "?", jabber_command_passwd, JABBER_FLAGS, NULL); command_add(&jabber_plugin, "xmpp:privacy", "? ? ?", jabber_command_privacy, JABBER_FLAGS, NULL); command_add(&jabber_plugin, "xmpp:private", "!p ! ?", jabber_command_private, JABBER_FLAGS_REQ, "-c --clear -d --display -p --put"); command_add(&jabber_plugin, "xmpp:reconnect", NULL, jabber_command_reconnect, JABBER_ONLY, NULL); command_add(&jabber_plugin, "xmpp:register", "? ?", jabber_command_register, JABBER_ONLY, NULL); command_add(&jabber_plugin, "xmpp:reply", "! !", jabber_command_reply, JABBER_FLAGS_TARGET, NULL); command_add(&jabber_plugin, "xmpp:search", "? ?", jabber_command_search, JABBER_FLAGS, NULL); command_add(&jabber_plugin, "xmpp:tmsg", "!uU ! !", jabber_command_msg, JABBER_FLAGS_TARGET, NULL); /* threaded msg */ command_add(&jabber_plugin, "xmpp:topic", "? ?", jabber_muc_command_topic, JABBER_FLAGS_REQ, NULL); command_add(&jabber_plugin, "xmpp:transpinfo", "? ?", jabber_command_transpinfo, JABBER_FLAGS, NULL); command_add(&jabber_plugin, "xmpp:transports", "? ?", jabber_command_transports, JABBER_FLAGS, NULL); command_add(&jabber_plugin, "xmpp:unban", "! ?", jabber_muc_command_affiliation, JABBER_FLAGS_TARGET, NULL); command_add(&jabber_plugin, "xmpp:unregister", "?", jabber_command_register, JABBER_FLAGS, NULL); command_add(&jabber_plugin, "xmpp:userinfo", "!u", jabber_command_userinfo, JABBER_FLAGS_TARGET, NULL); command_add(&jabber_plugin, "xmpp:userlist", "! ?", jabber_command_userlist, JABBER_FLAGS_REQ, "-g --get -p --put"); /* BFW: it is unlike GG, -g gets userlist from file, -p writes it into it */ command_add(&jabber_plugin, "xmpp:vacation", "?", jabber_command_vacation, JABBER_FLAGS, NULL); command_add(&jabber_plugin, "xmpp:ver", "!u", jabber_command_ver, JABBER_FLAGS_TARGET, NULL); /* ??? ?? ? ?@?!#??#!@? */ command_add(&jabber_plugin, "xmpp:voice", "! !", jabber_muc_command_role, JABBER_FLAGS_TARGET, NULL); command_add(&jabber_plugin, "xmpp:xa", "r", jabber_command_away, JABBER_ONLY, NULL); command_add(&jabber_plugin, "xmpp:xml", "!", jabber_command_xml, JABBER_ONLY, NULL); command_add(&jabber_plugin, "tlen:", "?", jabber_command_inline_msg, JABBER_ONLY | COMMAND_PASS_UNCHANGED, NULL); command_add(&jabber_plugin, "tlen:_autoaway", "r", jabber_command_away, JABBER_ONLY, NULL); command_add(&jabber_plugin, "tlen:_autoxa", "r", jabber_command_away, JABBER_ONLY, NULL); command_add(&jabber_plugin, "tlen:_autoback", "r", jabber_command_away, JABBER_ONLY, NULL); command_add(&jabber_plugin, "tlen:add", "U ?", jabber_command_modify, JABBER_FLAGS, NULL); command_add(&jabber_plugin, "tlen:alert", "!u", tlen_command_alert, JABBER_FLAGS_TARGET_VALID, NULL); command_add(&jabber_plugin, "tlen:auth", "!p uU", jabber_command_auth, JABBER_FLAGS_REQ, "-a --accept -d --deny -r --request -c --cancel"); command_add(&jabber_plugin, "tlen:away", "r", jabber_command_away, JABBER_ONLY, NULL); command_add(&jabber_plugin, "tlen:back", "r", jabber_command_away, JABBER_ONLY, NULL); command_add(&jabber_plugin, "tlen:connect", "r ?", jabber_command_connect, JABBER_ONLY, NULL); command_add(&jabber_plugin, "tlen:disconnect", "r ?", jabber_command_disconnect, JABBER_ONLY, NULL); command_add(&jabber_plugin, "tlen:del", "!u", jabber_command_del, JABBER_FLAGS_TARGET, NULL); command_add(&jabber_plugin, "tlen:dnd", "r", jabber_command_away, JABBER_ONLY, NULL); command_add(&jabber_plugin, "tlen:ffc", "r", jabber_command_away, JABBER_ONLY, NULL); command_add(&jabber_plugin, "tlen:invisible", "r", jabber_command_away, JABBER_ONLY, NULL); command_add(&jabber_plugin, "tlen:modify", "!Uu ?", jabber_command_modify, JABBER_FLAGS_REQ, "-n --nickname -g --group"); command_add(&jabber_plugin, "tlen:msg", "!uU !", jabber_command_msg, JABBER_FLAGS_MSG, NULL); command_add(&jabber_plugin, "tlen:reconnect", NULL, jabber_command_reconnect, JABBER_ONLY, NULL); command_add(&jabber_plugin, "tlen:xa", "r", jabber_command_away, JABBER_ONLY, NULL); }; /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/plugins/jabber/digest.c000066400000000000000000000432141175142753400205120ustar00rootroot00000000000000/* $Id$ */ /* * This is work is derived from material Copyright RSA Data Security, Inc. */ /* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm */ /* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All * rights reserved. * * License to copy and use this software is granted provided that it * is identified as the "RSA Data Security, Inc. MD5 Message-Digest * Algorithm" in all material mentioning or referencing this software * or this function. * * License is also granted to make and use derivative works provided * that such works are identified as "derived from the RSA Data * Security, Inc. MD5 Message-Digest Algorithm" in all material * mentioning or referencing the derived work. * * RSA Data Security, Inc. makes no representations concerning either * the merchantability of this software or the suitability of this * software for any particular purpose. It is provided "as is" * without express or implied warranty of any kind. * * These notices must be retained in any copies of any part of this * documentation and/or software. */ /* SHA-1 in C By Steve Reid 100% Public Domain Test Vectors (from FIPS PUB 180-1) "abc" A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 A million repetitions of "a" 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F */ #include "ekg2.h" #include "jabber.h" #if !defined(WORDS_BIGENDIAN) && !defined(LITTLE_ENDIAN) # define LITTLE_ENDIAN #endif #define SHA1HANDSOFF #include #include typedef struct { guint32 state[5]; guint32 count[2]; unsigned char buffer[64]; } EKG2_SHA1_CTX, EKG2_MD5_CTX; static void Init(EKG2_SHA1_CTX* context, int usesha); static void Transform(guint32 state[5], unsigned char buffer[64], int usesha); static void Update(EKG2_SHA1_CTX* context, unsigned char* data, unsigned int len, int usesha); static void Final(unsigned char digest[20], EKG2_SHA1_CTX* context, int usesha); #define SHA1Init(ctx) Init(ctx, 1) #define SHA1Transform(state, buffer) Transform(state, buffer, 1) #define SHA1Update(ctx, data, len) Update(ctx, (unsigned char *) data, len, 1) #define SHA1Final(digest, ctx) Final(digest, ctx, 1) #define MD5Init(ctx) Init(ctx, 0) #define MD5Transform(state, buffer) Transform(state, buffer, 0) #define MD5Update(ctx, data, len) Update(ctx, (unsigned char *) data, len, 0) #define MD5Final(digest, ctx) Final(digest, ctx, 0) #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) /* blk0() and blk() perform the initial expand. */ /* I got the idea of expanding during the round function from SSLeay */ #ifdef LITTLE_ENDIAN #define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ |(rol(block->l[i],8)&0x00FF00FF)) #else #define blk0(i) block->l[i] #endif #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ ^block->l[(i+2)&15]^block->l[i&15],1)) /* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ #define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); #define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); #define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); #define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); #define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); #define F(x, y, z) (((x) & (y)) | ((~x) & (z))) #define G(x, y, z) (((x) & (z)) | ((y) & (~z))) #define H(x, y, z) ((x) ^ (y) ^ (z)) #define I(x, y, z) ((y) ^ ((x) | (~z))) #define FF(a, b, c, d, x, s, ac) { (a) += F ((b), (c), (d)) + (x) + (guint32)(ac); (a) = rol((a), (s)); (a) += (b); } #define GG(a, b, c, d, x, s, ac) { (a) += G ((b), (c), (d)) + (x) + (guint32)(ac); (a) = rol((a), (s)); (a) += (b); } #define HH(a, b, c, d, x, s, ac) { (a) += H ((b), (c), (d)) + (x) + (guint32)(ac); (a) = rol((a), (s)); (a) += (b); } #define II(a, b, c, d, x, s, ac) { (a) += I ((b), (c), (d)) + (x) + (guint32)(ac); (a) = rol((a), (s)); (a) += (b); } /* Hash a single 512-bit block. This is the core of the algorithm. */ static void Transform(guint32 state[5], unsigned char buffer[64], int usesha) { guint32 a, b, c, d, e; typedef union { unsigned char c[64]; guint32 l[16]; } CHAR64LONG16; CHAR64LONG16* block; #ifdef SHA1HANDSOFF static unsigned char workspace[64]; block = (CHAR64LONG16*)workspace; memcpy(block, buffer, 64); #else block = (CHAR64LONG16*)buffer; #endif /* Copy context->state[] to working vars */ a = state[0]; b = state[1]; c = state[2]; d = state[3]; e = state[4]; if (usesha) { /* 4 rounds of 20 operations each. Loop unrolled. */ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); } else { guint32 *block = (guint32 *) &workspace[0]; /* Round 1 */ #define S11 7 #define S12 12 #define S13 17 #define S14 22 FF (a, b, c, d, block[ 0], S11, 0xd76aa478); /* 1 */ FF (d, a, b, c, block[ 1], S12, 0xe8c7b756); /* 2 */ FF (c, d, a, b, block[ 2], S13, 0x242070db); /* 3 */ FF (b, c, d, a, block[ 3], S14, 0xc1bdceee); /* 4 */ FF (a, b, c, d, block[ 4], S11, 0xf57c0faf); /* 5 */ FF (d, a, b, c, block[ 5], S12, 0x4787c62a); /* 6 */ FF (c, d, a, b, block[ 6], S13, 0xa8304613); /* 7 */ FF (b, c, d, a, block[ 7], S14, 0xfd469501); /* 8 */ FF (a, b, c, d, block[ 8], S11, 0x698098d8); /* 9 */ FF (d, a, b, c, block[ 9], S12, 0x8b44f7af); /* 10 */ FF (c, d, a, b, block[10], S13, 0xffff5bb1); /* 11 */ FF (b, c, d, a, block[11], S14, 0x895cd7be); /* 12 */ FF (a, b, c, d, block[12], S11, 0x6b901122); /* 13 */ FF (d, a, b, c, block[13], S12, 0xfd987193); /* 14 */ FF (c, d, a, b, block[14], S13, 0xa679438e); /* 15 */ FF (b, c, d, a, block[15], S14, 0x49b40821); /* 16 */ /* Round 2 */ #define S21 5 #define S22 9 #define S23 14 #define S24 20 GG (a, b, c, d, block[ 1], S21, 0xf61e2562); /* 17 */ GG (d, a, b, c, block[ 6], S22, 0xc040b340); /* 18 */ GG (c, d, a, b, block[11], S23, 0x265e5a51); /* 19 */ GG (b, c, d, a, block[ 0], S24, 0xe9b6c7aa); /* 20 */ GG (a, b, c, d, block[ 5], S21, 0xd62f105d); /* 21 */ GG (d, a, b, c, block[10], S22, 0x2441453); /* 22 */ GG (c, d, a, b, block[15], S23, 0xd8a1e681); /* 23 */ GG (b, c, d, a, block[ 4], S24, 0xe7d3fbc8); /* 24 */ GG (a, b, c, d, block[ 9], S21, 0x21e1cde6); /* 25 */ GG (d, a, b, c, block[14], S22, 0xc33707d6); /* 26 */ GG (c, d, a, b, block[ 3], S23, 0xf4d50d87); /* 27 */ GG (b, c, d, a, block[ 8], S24, 0x455a14ed); /* 28 */ GG (a, b, c, d, block[13], S21, 0xa9e3e905); /* 29 */ GG (d, a, b, c, block[ 2], S22, 0xfcefa3f8); /* 30 */ GG (c, d, a, b, block[ 7], S23, 0x676f02d9); /* 31 */ GG (b, c, d, a, block[12], S24, 0x8d2a4c8a); /* 32 */ /* Round 3 */ #define S31 4 #define S32 11 #define S33 16 #define S34 23 HH (a, b, c, d, block[ 5], S31, 0xfffa3942); /* 33 */ HH (d, a, b, c, block[ 8], S32, 0x8771f681); /* 34 */ HH (c, d, a, b, block[11], S33, 0x6d9d6122); /* 35 */ HH (b, c, d, a, block[14], S34, 0xfde5380c); /* 36 */ HH (a, b, c, d, block[ 1], S31, 0xa4beea44); /* 37 */ HH (d, a, b, c, block[ 4], S32, 0x4bdecfa9); /* 38 */ HH (c, d, a, b, block[ 7], S33, 0xf6bb4b60); /* 39 */ HH (b, c, d, a, block[10], S34, 0xbebfbc70); /* 40 */ HH (a, b, c, d, block[13], S31, 0x289b7ec6); /* 41 */ HH (d, a, b, c, block[ 0], S32, 0xeaa127fa); /* 42 */ HH (c, d, a, b, block[ 3], S33, 0xd4ef3085); /* 43 */ HH (b, c, d, a, block[ 6], S34, 0x4881d05); /* 44 */ HH (a, b, c, d, block[ 9], S31, 0xd9d4d039); /* 45 */ HH (d, a, b, c, block[12], S32, 0xe6db99e5); /* 46 */ HH (c, d, a, b, block[15], S33, 0x1fa27cf8); /* 47 */ HH (b, c, d, a, block[ 2], S34, 0xc4ac5665); /* 48 */ /* Round 4 */ #define S41 6 #define S42 10 #define S43 15 #define S44 21 II (a, b, c, d, block[ 0], S41, 0xf4292244); /* 49 */ II (d, a, b, c, block[ 7], S42, 0x432aff97); /* 50 */ II (c, d, a, b, block[14], S43, 0xab9423a7); /* 51 */ II (b, c, d, a, block[ 5], S44, 0xfc93a039); /* 52 */ II (a, b, c, d, block[12], S41, 0x655b59c3); /* 53 */ II (d, a, b, c, block[ 3], S42, 0x8f0ccc92); /* 54 */ II (c, d, a, b, block[10], S43, 0xffeff47d); /* 55 */ II (b, c, d, a, block[ 1], S44, 0x85845dd1); /* 56 */ II (a, b, c, d, block[ 8], S41, 0x6fa87e4f); /* 57 */ II (d, a, b, c, block[15], S42, 0xfe2ce6e0); /* 58 */ II (c, d, a, b, block[ 6], S43, 0xa3014314); /* 59 */ II (b, c, d, a, block[13], S44, 0x4e0811a1); /* 60 */ II (a, b, c, d, block[ 4], S41, 0xf7537e82); /* 61 */ II (d, a, b, c, block[11], S42, 0xbd3af235); /* 62 */ II (c, d, a, b, block[ 2], S43, 0x2ad7d2bb); /* 63 */ II (b, c, d, a, block[ 9], S44, 0xeb86d391); /* 64 */ } /* Add the working vars back into context.state[] */ state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e; /* Wipe variables */ a = b = c = d = e = 0; } /* SHA1Init - Initialize new context */ static void Init(EKG2_SHA1_CTX* context, int usesha) { /* SHA1 initialization constants */ context->state[0] = 0x67452301; context->state[1] = 0xEFCDAB89; context->state[2] = 0x98BADCFE; context->state[3] = 0x10325476; context->state[4] = usesha ? 0xC3D2E1F0 : 0x00000000; context->count[0] = context->count[1] = 0; } /* Run your data through this. */ static void Update(EKG2_SHA1_CTX* context, unsigned char* data, unsigned int len, int usesha) { unsigned int i, j; j = (context->count[0] >> 3) & 63; if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++; context->count[1] += (len >> 29); if ((j + len) > 63) { memcpy(&context->buffer[j], data, (i = 64-j)); Transform(context->state, context->buffer, usesha); for ( ; i + 63 < len; i += 64) { Transform(context->state, &data[i], usesha); } j = 0; } else i = 0; memcpy(&context->buffer[j], &data[i], len - i); } static void Encode (unsigned char *output, guint32 *input, unsigned int len, int usesha) { unsigned int i, j; if (usesha) { if (len == 8) for (i = 0; i < 8; i++) output[i] = (unsigned char) ((input[(i >= 4 ? 0 : 1)] >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ if (len == 20) for (i = 0; i < 20; i++) output[i] = (unsigned char) ((input[i>>2] >> ((3-(i & 3)) * 8) ) & 255); } else { for (i = 0, j = 0; j < len; i++, j += 4) { output[j] = (unsigned char) ((input[i] ) & 0xff); output[j+1] = (unsigned char) ((input[i] >> 8) & 0xff); output[j+2] = (unsigned char) ((input[i] >> 16) & 0xff); output[j+3] = (unsigned char) ((input[i] >> 24) & 0xff); } } } /* Add padding and return the message digest. */ static void Final(unsigned char digest[20], EKG2_SHA1_CTX* context, int usesha) { unsigned char finalcount[8]; Encode(finalcount, context->count, 8, usesha); Update(context, (unsigned char *)"\200", 1, usesha); #if 0 else { /* ORGINAL MD5Final() code from rfc1321 (http://tools.ietf.org/html/rfc1321) XXX, do we need it? \200 == 0x80 */ static unsigned char PADDING[64] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; unsigned int index, padLen; index = (unsigned int)((context->count[0] >> 3) & 0x3f); padLen = (index < 56) ? (56 - index) : (120 - index); MD5Update (context, PADDING, padLen); } #endif while ((context->count[0] & 504) != 448) { Update(context, (unsigned char *)"\0", 1, usesha); } Update(context, finalcount, 8, usesha); /* Should cause a SHA1Transform() */ Encode(digest, context->state, usesha ? 20 : 16, usesha); /* Wipe variables */ memset(context->buffer, 0, 64); memset(context->state, 0, 20); memset(context->count, 0, 8); memset(&finalcount, 0, 8); #ifdef SHA1HANDSOFF /* make SHA1Transform overwrite it's own static vars */ Transform(context->state, context->buffer, usesha); #endif } /* EKG2 STUFF */ extern char *config_console_charset; /* ekg/stuff.h */ /** * base16_encode() * * Return base16 hash of @a data * * @return static with 32 digit BASE16 HASH + NUL char. */ static char *base16_encode(const unsigned char *data) { static char result[33]; int i; if (!data) return NULL; for (i = 0; i < 16; i++) snprintf(&result[i * 2], 3, "%02hhx", data[i]); result[32] = 0; return result; } /** * jabber_challenge_digest() * * Return base16 encoded hash for SASL MD5 CHALLENGE * * @todo MD5Update() on NULL params will fail. XXX, no idea what to do. * * @return static buffer with 32 digit BASE16 HASH + NUL char */ char *jabber_challenge_digest(const char *sid, const char *password, const char *nonce, const char *cnonce, const char *xmpp_temp, const char *realm) { EKG2_MD5_CTX ctx; unsigned char digest[20]; const char *convnode, *convpasswd; /* sid && password encoded in UTF-8 */ char *ha1, *ha2; char *kd; /* ZERO STEP -> recode */ convnode = ekg_locale_to_utf8_use(sid); convpasswd = ekg_locale_to_utf8_use(password); /* FIRST STEP */ kd = saprintf("%s:%s:%s", convnode, realm, convpasswd); recode_xfree(sid, convnode); recode_xfree(password, convpasswd); MD5Init(&ctx); MD5Update(&ctx, kd, xstrlen(kd)); MD5Final(digest, &ctx); xfree(kd); /* SECOND STEP */ kd = saprintf("xxxxxxxxxxxxxxxx:%s:%s", nonce, cnonce); memcpy(kd, digest, 16); MD5Init(&ctx); MD5Update(&ctx, kd, 16 + 1 + xstrlen(nonce) + 1 + xstrlen(cnonce)); MD5Final(digest, &ctx); xfree(kd); /* 3a) DATA */ ha1 = xstrdup(base16_encode(digest)); MD5Init(&ctx); MD5Update(&ctx, xmpp_temp, xstrlen(xmpp_temp)); MD5Final(digest, &ctx); /* 3b) DATA */ ha2 = xstrdup(base16_encode(digest)); /* THIRD STEP */ kd = saprintf("%s:%s:00000001:%s:auth:%s", ha1, nonce, cnonce, ha2); xfree(ha1); xfree(ha2); MD5Init(&ctx); MD5Update(&ctx, kd, xstrlen(kd)); MD5Final(digest, &ctx); xfree(kd); /* FINAL */ return base16_encode(digest); } /** [XXX] SOME TIME AGO, I had idea to connect jabber_dcc_digest() and jabber_digest() * with one function, and use va_list for it... i don't know. */ /** * jabber_dcc_digest() * * Return SHA1 hash for SOCKS5 Bytestreams connections [DCC]
* Make SHA1Update()'s on (@a uid, @a initiator and @a target) * * @todo SHA1Update() on NULL params will fail. XXX, no idea what to do. * * @todo We don't reencode params here to utf-8. * * @return static buffer, with 40 digit SHA1 hash + NUL char */ char *jabber_dcc_digest(char *sid, char *initiator, char *target) { EKG2_SHA1_CTX ctx; unsigned char digest[20]; static char result[41]; int i; SHA1Init(&ctx); SHA1Update(&ctx, sid, xstrlen(sid)); SHA1Update(&ctx, initiator, xstrlen(initiator)); SHA1Update(&ctx, target, xstrlen(target)); SHA1Final(digest, &ctx); for (i = 0; i < 20; i++) sprintf(result + i * 2, "%.2x", digest[i]); return result; } /** * jabber_digest() * * Return SHA1 hash for jabber:iq:auth
* Make SHA1Update()'s on recoded to utf-8 (@a sid and @a password) * * @todo SHA1Update() on NULL params will fail. XXX, no idea what to do. * * @return static buffer, with 40 digit SHA1 hash + NUL char */ char *jabber_digest(const char *sid, const char *password, int istlen) { EKG2_SHA1_CTX ctx; unsigned char digest[20]; static char result[41]; const char *tmp; int i; SHA1Init(&ctx); tmp = (istlen) ? ekg_locale_to_iso2_use(sid) : ekg_locale_to_utf8_use(sid); SHA1Update(&ctx, tmp, xstrlen(tmp)); recode_xfree(sid, tmp); tmp = (istlen) ? ekg_locale_to_iso2_use(password) : ekg_locale_to_utf8_use(password); SHA1Update(&ctx, tmp, xstrlen(tmp)); recode_xfree(password, tmp); SHA1Final(digest, &ctx); for (i = 0; i < 20; i++) sprintf(result + i * 2, "%.2x", digest[i]); return result; } char *jabber_sha1_generic(char *buf, int len) { EKG2_SHA1_CTX ctx; unsigned char digest[20]; static char result[41]; int i; SHA1Init(&ctx); SHA1Update(&ctx, buf, len); SHA1Final(digest, &ctx); for (i = 0; i < 20; i++) sprintf(result + i * 2, "%.2x", digest[i]); return result; } /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/plugins/jabber/jabber-ssl.h000066400000000000000000000050441175142753400212630ustar00rootroot00000000000000#ifndef __EKG_JABBER_SSL_H #define __EKG_JABBER_SSL_H #ifdef HAVE_LIBGNUTLS # define JABBER_HAVE_SSL 1 # undef HAVE_LIBSSL /* na wszelki wypadek */ #else #ifdef HAVE_LIBSSL # define JABBER_HAVE_SSL 1 # warning "You want to use OpenSSL library as ssl transport layer for jabber, it have bugs and is not well tested.. It's better if you use gnutls" #endif #endif #ifdef JABBER_HAVE_SSL #ifdef HAVE_LIBGNUTLS /* HAVE_GNUTLS */ # include # define SSL_SESSION gnutls_session static int __attribute__((unused)) SSL_SET_FD(SSL_SESSION session, long int fd) { gnutls_transport_set_ptr(session, (gnutls_transport_ptr)(fd)); return 1; /* always success */ } # define SSL_INIT(session) gnutls_init((&session), GNUTLS_CLIENT) # define SSL_DEINIT(session) gnutls_deinit(session) # define SSL_HELLO(session) gnutls_handshake(session) # define SSL_BYE(session) gnutls_bye(session, GNUTLS_SHUT_RDWR) # define SSL_GLOBAL_INIT() gnutls_global_init() # define SSL_GLOBAL_DEINIT() gnutls_global_deinit() # define SSL_ERROR(retcode) gnutls_strerror(retcode) # define SSL_E_AGAIN(ret) ((ret == GNUTLS_E_INTERRUPTED) || (ret == GNUTLS_E_AGAIN)) # define SSL_SEND(session, str, len) gnutls_record_send(session, str, len) # define SSL_RECV(session, buf, size) gnutls_record_recv(session, buf, size) # define SSL_GET_FD(session, fd) (long int) gnutls_transport_get_ptr(session) # define SSL_WRITE_DIRECTION(session, ret) gnutls_record_get_direction(session) #else /* HAVE_OPENSSL */ # include # include extern SSL_CTX *jabberSslCtx; /* jabber.c */ # define SSL_SESSION SSL * # define SSL_INIT(session) !(session = SSL_new(jabberSslCtx)) # define SSL_HELLO(session) SSL_connect(session) # define SSL_BYE(session) SSL_shutdown(session) # define SSL_DEINIT(session) SSL_free(session) # define SSL_GLOBAL_INIT() SSL_library_init(); jabberSslCtx = SSL_CTX_new(SSLv23_client_method()) # define SSL_GLOBAL_DEINIT() SSL_CTX_free(jabberSslCtx) # define SSL_ERROR(retcode) ERR_error_string(retcode, NULL) /* retcode need be value from SSL_get_error(session, res) */ # define SSL_E_AGAIN(ret) ((ret == SSL_ERROR_WANT_READ || ret == SSL_ERROR_WANT_WRITE)) # define SSL_SEND(session, str, len) SSL_write(session, str, len) # define SSL_RECV(session, buf, size) SSL_read(session, buf, size) # define SSL_SET_FD(session, fd) SSL_set_fd(session, fd) # define SSL_GET_FD(session, fd) fd # define SSL_WRITE_DIRECTION(session, ret) (ret != SSL_ERROR_WANT_READ) #endif /* ... */ #endif /* JABBER_HAVE_SSL */ #endif /* __EKG_JABBER_SSL_H */ ekg2-0.4~pre+20120506.1/plugins/jabber/jabber.c000066400000000000000000001663541175142753400204730ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2003-2005 Wojtek Kaniewski * Tomasz Torcz * Leszek Krupiski * Piotr Pawow and other libtlen developers (http://libtlen.sourceforge.net/index.php?theme=teary&page=authors) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #ifndef NO_POSIX_SYSTEM #include #include #include #include #endif #include #include #include #include #include #include #ifndef NO_POSIX_SYSTEM #include #endif #ifdef __sun /* Solaris, thanks to Beeth */ #include #endif #ifdef HAVE_LIBZ # include "zlib.h" #endif #include #include "jabber.h" #include "jabber-ssl.h" #include "jabber_dcc.h" #ifdef HAVE_LIBSSL SSL_CTX *jabberSslCtx; #endif char *jabber_default_search_server = NULL; char *jabber_default_pubsub_server = NULL; int config_jabber_beep_mail = 0; int config_jabber_disable_chatstates = EKG_CHATSTATE_ACTIVE | EKG_CHATSTATE_GONE; const char *jabber_authtypes[] = { "none", "from", "to", "both" }; static int session_postinit; static int jabber_theme_init(); WATCHER_SESSION(jabber_handle_connect_ssl); PLUGIN_DEFINE(jabber, PLUGIN_PROTOCOL, jabber_theme_init); /** * jabber_session_init() * * Handler for: SESSION_ADDED
* Init priv_data session struct jabber_private_t if @a session is jabber one. * * @param ap 1st param: (char *) session - uid of session * @param data NULL * * @return 0 if @a session is jabber one, and we init memory
* 1 if we don't found such session, or it wasn't jabber session [most probable], or we already init memory. */ static QUERY(jabber_session_init) { char *session = *(va_arg(ap, char**)); session_t *s = session_find(session); jabber_private_t *j; if (!s || s->plugin != &jabber_plugin || s->priv) return 1; j = xmalloc(sizeof(jabber_private_t)); j->fd = -1; j->istlen = (tolower(s->uid[0]) == 't'); /* mark if this is tlen protocol */ if (!j->istlen) ekg_recode_utf8_inc(); else ekg_recode_iso2_inc(); #ifdef HAVE_LIBGNUTLS gnutls_certificate_allocate_credentials(&(j->xcred)); /* XXX - ~/.ekg/certs/server.pem */ gnutls_certificate_set_x509_trust_file(j->xcred, "brak", GNUTLS_X509_FMT_PEM); #endif s->priv = j; return 0; } /** * jabber_session_deinit() * * Handler for: SESSION_REMOVED
* Free memory allocated by jabber_private_t if @a session is jabber one. * * @param ap 1st param: (char *) session - uid of session * @param data NULL * * @return 0 if @a session is jabber one, and memory allocated where xfree()'d.
* 1 if not such session, or it wasn't jabber session [most probable], or we already free memory. */ static QUERY(jabber_session_deinit) { char *session = *(va_arg(ap, char**)); session_t *s = session_find(session); jabber_private_t *j; jabber_conversation_t *thr, *next; if (!s || s->plugin != &jabber_plugin || !(j = s->priv)) return 1; s->priv = NULL; #ifdef HAVE_LIBGNUTLS gnutls_certificate_free_credentials(j->xcred); #endif if (!j->istlen) ekg_recode_utf8_dec(); else ekg_recode_iso2_dec(); xfree(j->server); xfree(j->resource); xfree(j->last_gmail_result_time); xfree(j->last_gmail_tid); if (j->parser) XML_ParserFree(j->parser); jabber_bookmarks_free(j); jabber_privacy_free(j); jabber_iq_stanza_free(j); /* conversations */ for (thr = j->conversations; thr; thr = next) { next = thr->next; /* we shouldn't rely on freed thr->next */ xfree(thr->thread); xfree(thr->subject); xfree(thr->uid); xfree(thr); } xfree(j); return 0; } static LIST_FREE_ITEM(list_jabber_stanza_free, jabber_stanza_t *) { xfree(data->id); xfree(data->to); xfree(data->type); xfree(data->xmlns); xfree(data); } int jabber_iq_stanza_free(jabber_private_t *j) { if (!j || !j->iq_stanzas) return -1; LIST_DESTROY(j->iq_stanzas, list_jabber_stanza_free); j->iq_stanzas = NULL; return 0; } int jabber_stanza_freeone(jabber_private_t *j, jabber_stanza_t *stanza) { if (!j || !stanza) return -1; LIST_REMOVE(&(j->iq_stanzas), stanza, list_jabber_stanza_free); return 0; } LIST_ADD_COMPARE(jabber_privacy_add_compare, jabber_iq_privacy_t *) { return (data1->order - data2->order); } static LIST_FREE_ITEM(list_jabber_privacy_free, jabber_iq_privacy_t *) { xfree(data->type); xfree(data->value); xfree(data); } /* destroy all previously saved jabber:iq:privacy list... we DON'T DELETE LIST on jabberd server... only list saved @ j->privacy */ int jabber_privacy_free(jabber_private_t *j) { if (!j || !j->privacy) return -1; LIST_DESTROY(j->privacy, list_jabber_privacy_free); j->privacy = NULL; return 0; } int jabber_privacy_freeone(jabber_private_t *j, jabber_iq_privacy_t *item) { if (!j || !item) return -1; LIST_REMOVE(&(j->privacy), item, list_jabber_privacy_free); return 0; } static LIST_FREE_ITEM(list_jabber_bookmarks_free, jabber_bookmark_t *) { if (data->type == JABBER_BOOKMARK_URL) { xfree(data->priv_data.url->name); xfree(data->priv_data.url->url); } else if (data->type == JABBER_BOOKMARK_CONFERENCE) { xfree(data->priv_data.conf->name); xfree(data->priv_data.conf->jid); xfree(data->priv_data.conf->nick); xfree(data->priv_data.conf->pass); } xfree(data->priv_data.other); xfree(data); } /* destroy all previously saved bookmarks... we DON'T DELETE LIST on jabberd server... only list saved @ j->bookamarks */ int jabber_bookmarks_free(jabber_private_t *j) { if (!j || !j->bookmarks) return -1; LIST_DESTROY(j->bookmarks, list_jabber_bookmarks_free); j->bookmarks = NULL; return 0; } /** * jabber_print_version() * * handler for: PLUGIN_PRINT_VERSION
* Print expat version * * @return 0 */ static QUERY(jabber_print_version) { print("generic", XML_ExpatVersion()); return 0; } /** * jabber_validate_uid() * * handler for: PROTOCOL_VALIDATE_UID
* checks, if @a uid is proper for jabber plugin. * * @note Proper for jabber plugin means either: * - If @a uid starts with xmpp: have got '@' (but xmpp:@ is wrong) and after '@' there is at least one char [xmpp protocol]
* - If @a uid starts with tlen: (and len > 5) [tlen protocol] * * @param ap 1st param: (char *) uid - of user/session/command/whatever * @param ap 2nd param: (int) valid - place to put 1 if uid is valid for jabber plugin. * @param data NULL * * @return -1 if it's valid uid for jabber plugin
* 0 if not */ static QUERY(jabber_validate_uid) { char *uid = *(va_arg(ap, char **)); int *valid = va_arg(ap, int *); if (!uid) return 0; /* XXX: think about 'at' in jabber UIDs */ if (!xstrncasecmp(uid, "xmpp:", 5) || !xstrncasecmp(uid, "tlen:", 5)) { (*valid)++; return -1; } return 0; } static QUERY(jabber_window_kill) { window_t *w = *va_arg(ap, window_t **); jabber_private_t *j; newconference_t *c; char *status = NULL; if (w && w->id && w->target && session_check(w->session, 1, "xmpp") && (c = newconference_find(w->session, w->target)) && (j = jabber_private(w->session)) && session_connected_get(w->session)) { /* XXX: check really needed? vv */ watch_write(j->send_watch, "%s", w->target + 5, c->priv_data, status ? status : ""); newconference_destroy(c, 0); } return 0; } int jabber_write_status(session_t *s) { #define JABBER_EKG_CAPS "" jabber_private_t *j = session_private_get(s); int prio = session_int_get(s, "priority"); int status; char *descr; char *real = NULL; char *priority = NULL; char *x_signed = NULL; char *x_vcard = NULL; if (!s || !j) return -1; if (!session_connected_get(s)) return 0; status = session_status_get(s); /*if (!xstrcmp(status, EKG_STATUS_AUTOAWAY)) status = "away"; (that shouldn't take place...)*/ if ((descr = tlenjabber_escape(session_descr_get(s)))) { real = saprintf("%s", descr); xfree(descr); } if (!j->istlen) { const char *tmp; priority = saprintf("%d", prio); /* priority only in real jabber session */ if (session_int_get(s, "__gpg_enabled") == 1) { char *signpresence; signpresence = xstrdup(session_descr_get(s)); /* XXX, data in unicode required (?) */ if (!signpresence) signpresence = xstrdup(""); signpresence = jabber_openpgp(s, NULL, JABBER_OPENGPG_SIGN, signpresence, NULL, NULL); if (signpresence) { x_signed = saprintf("%s", signpresence); xfree(signpresence); } } if ((tmp = session_get(s, "photo_hash"))) x_vcard = saprintf("%s", tmp); } #define P(x) (x ? x : "") if (!j->istlen && (status == EKG_STATUS_AVAIL)) watch_write(j->send_watch, "%s%s%s%s%s", P(real), P(priority), P(x_signed), P(x_vcard), JABBER_EKG_CAPS); else if (status == EKG_STATUS_INVISIBLE) watch_write(j->send_watch, "%s%s", P(real), P(priority)); else { const char *status_s; if (j->istlen && (status == EKG_STATUS_AVAIL)) status_s = "available"; else status_s = ekg_status_string(status, 0); watch_write(j->send_watch, "%s%s%s%s%s%s", status_s, P(real), P(priority), P(x_signed), P(x_vcard), JABBER_EKG_CAPS); } #undef P xfree(priority); xfree(real); xfree(x_signed); xfree(x_vcard); return 0; } void jabber_handle_disconnect(session_t *s, const char *reason, int type) { jabber_private_t *j; if (!s || !(j = s->priv)) return; if (!s->connected && !s->connecting) return; protocol_disconnected_emit(s, reason, type); if (j->connect_watch) { watch_free(j->connect_watch); j->connect_watch = NULL; } if (j->send_watch) { j->send_watch->type = WATCH_NONE; watch_free(j->send_watch); j->send_watch = NULL; } watch_remove(&jabber_plugin, j->fd, WATCH_WRITE); watch_remove(&jabber_plugin, j->fd, WATCH_READ); j->using_compress = JABBER_COMPRESSION_NONE; #ifdef JABBER_HAVE_SSL if (j->using_ssl && j->ssl_session) SSL_BYE(j->ssl_session); #endif if (j->fd != -1) { close(j->fd); j->fd = -1; } #ifdef JABBER_HAVE_SSL if (j->using_ssl && j->ssl_session) SSL_DEINIT(j->ssl_session); j->using_ssl = 0; j->ssl_session = NULL; #endif jabber_iq_stanza_free(j); if (j->parser) XML_ParserFree(j->parser); j->parser = NULL; { window_t *wl; for (wl = windows; wl; wl = wl->next) { window_t *w = wl; if (w->session == s) { const char *tmp = get_uid(s, w->target); if (tmp != w->target) { xfree(w->target); w->target = xstrdup(tmp); } } } userlist_free(s); query_emit(NULL, "userlist-refresh"); } session_set(s, "__sasl_excepted", NULL); session_int_set(s, "__roster_retrieved", 0); session_int_set(s, "__session_need_start", 0); } static void xmlnode_handle_start(void *data, const char *name, const char **atts) { session_t *s = (session_t *) data; jabber_private_t *j; if (!s || !(j = s->priv) || !name) { debug_error("[jabber] xmlnode_handle_start() invalid parameters\n"); return; } /* XXX, czy tego nie mozna parsowac tak jak wszystko inne w jabber_handle() ? * A tutaj tylko tworzyc drzewo xmlowe? * XXX, rtfm expat */ /* (WO) Nie mona, bo przetwarzanie rozpoczoby si dopiero po skompletowaniu, czyli po otrzymaniu * Wtpi, by kto chcia czeka a to nastpi, ale adniej byoby gdyby caa ta cz po if wyldowaa * w jakiej jabber_just_like_starting_over() * -- Wiesaw Ochmiski */ if (!(s->connected) && (j->istlen ? !xstrcmp(name, "s") : !xstrcmp(name, "http://etherx.jabber.org/streams\033stream"))) { const char *passwd = session_get(s, "password"); char *username, *tmp; if ((tmp = xstrchr(s->uid + 5, '@'))) username = xstrndup(s->uid + 5, tmp - s->uid - 5); else username = xstrdup(s->uid + 5); /* XXX, * Here if we've got SASL-connection we should do jabber:iq:register only when * j->connecting == 1, * * but i'm not quite sure if s->connected, and j->connecting can be 0 [yeap, i know it would be stupid] * So, to avoid regression, we use here j->connecting != 2 */ if (!j->istlen && !j->sasl_connecting && session_get(s, "__new_account")) { char *epasswd = jabber_escape(passwd); watch_write(j->send_watch, "" "%s%s", j->server, j->id++, username, epasswd ? epasswd : ("foo")); xfree(epasswd); } if (!j->istlen && session_int_get(s, "disable_sasl") != 2) { if (session_int_get(s, "disable_sasl") == 1) watch_write(j->send_watch, /* let's rock with XEP-0078: Non-SASL Authentication */ "" "" ""); xfree(username); /* waste */ return; } /* here forced old jabber only, no XMPP 1.0, NON-SASL AUTH */ jabber_iq_auth_send(s, username, passwd, jabber_attr((char **) atts, j->istlen ? "i" : "id")); xfree(username); } else { xmlnode_t *n, *newnode; int arrcount, i; newnode = xmalloc(sizeof(xmlnode_t)); { /* get the namespace */ char *x = NULL; char *tmp = xstrdup(name); char *sep = xstrchr(tmp, '\033'); if (sep) { *sep = '\0'; name = ++sep; x = tmp; } newnode->name = xstrdup(name); newnode->xmlns = xstrdup(x); xfree(tmp); } if ((n = j->node)) { newnode->parent = n; if (!n->children) n->children = newnode; else { xmlnode_t *m = n->children; while (m->next) m = m->next; m->next = newnode; } } arrcount = g_strv_length((char **) atts); if (arrcount > 0) { /* we don't need to allocate table if arrcount = 0 */ newnode->atts = xmalloc((arrcount + 1) * sizeof(char *)); for (i = 0; i < arrcount; i++) newnode->atts[i] = xstrdup(atts[i]); } j->node = newnode; } } static WATCHER_SESSION(jabber_handle_stream) { #define BUFFER_LEN 4096 jabber_private_t *j; XML_Parser parser; /* j->parser */ char *uncompressed = NULL; char *buf; int len; int rlen; /* session dissapear, shouldn't happen */ if (!s || !(j = s->priv)) return -1; if (!(j->send_watch) || (j->send_watch->type == WATCH_NONE)) /* TLS in progress; XXX: check if it doesn't collide with sth */ return 0; /* s->activity = time(NULL); */ /* we got disconnected? */ if (type == 1) { debug("[jabber] jabber_handle_stream() type == 1, exitting\n"); jabber_handle_disconnect(s, NULL, EKG_DISCONNECT_NETWORK); return 0; } #ifdef JABBER_HAVE_SSL /* we need to loop ssl-reading, 'cause gnutls may be using quite large buffer * and if it reads all data from fd, our handler won't be called again until * more data arrives, and some current data will be left in gnutls buffer. * * This feature really needs testing, it might cause real problems.*/ do { #endif debug_function("[jabber] jabber_handle_stream()\n"); parser = j->parser; if (!(buf = XML_GetBuffer(parser, BUFFER_LEN))) { jabber_handle_disconnect(s, "XML_GetBuffer failed", EKG_DISCONNECT_NETWORK); return -1; } #ifdef JABBER_HAVE_SSL if (j->using_ssl && j->ssl_session) { len = SSL_RECV(j->ssl_session, buf, BUFFER_LEN-1); #ifdef HAVE_LIBSSL if ((len == 0 && SSL_get_error(j->ssl_session, len) == SSL_ERROR_ZERO_RETURN)); /* connection shut down cleanly */ else if (len < 0) len = SSL_get_error(j->ssl_session, len); /* XXX, When an SSL_read() operation has to be repeated because of SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE, it must be repeated with the same arguments. */ #endif if (SSL_E_AGAIN(len)) { // will be called again ekg_yield_cpu(); return 0; } if (len < 0) { jabber_handle_disconnect(s, SSL_ERROR(len), EKG_DISCONNECT_NETWORK); return -1; } } else #endif if ((len = read(fd, buf, BUFFER_LEN-1)) < 1) { if (len == -1 && (errno == EINPROGRESS || errno == EAGAIN)) return 0; jabber_handle_disconnect(s, len == -1 ? strerror(errno) : "got disconnected", EKG_DISCONNECT_NETWORK); return -1; } buf[len] = 0; rlen = len; switch (j->using_compress) { case JABBER_COMPRESSION_ZLIB: #ifdef HAVE_LIBZ uncompressed = jabber_zlib_decompress(buf, &rlen); #else debug_error("[jabber] jabber_handle_stream() compression zlib, but no zlib support.. you're joking, right?\n"); #endif break; case JABBER_COMPRESSION_LZW: debug_error("[jabber] jabber_handle_stream() j->using_compress XXX implement LZW!\n"); break; case JABBER_COMPRESSION_NONE: case JABBER_COMPRESSION_LZW_INIT: case JABBER_COMPRESSION_ZLIB_INIT: break; default: debug_error("[jabber] jabber_handle_stream() j->using_compress wtf? unknown! %d\n", j->using_compress); } debug_iorecv("[jabber] (%db/%db) recv: %s\n", rlen, len, uncompressed ? uncompressed : buf); /* if (uncompressed) { memcpy(buf, uncompressed, rlen); } */ if (!XML_ParseBuffer(parser, rlen, (rlen == 0))) // if (!XML_Parse(parser, uncompressed ? uncompressed : buf, rlen, (rlen == 0))) { char *tmp; tmp = format_string(format_find("jabber_xmlerror_disconnect"), XML_ErrorString(XML_GetErrorCode(parser))); if ((!j->parser && parser) || (parser != j->parser)) XML_ParserFree(parser); jabber_handle_disconnect(s, tmp, EKG_DISCONNECT_NETWORK); xfree(tmp); xfree(uncompressed); return -1; } if ((!j->parser && parser) || (parser != j->parser)) XML_ParserFree(parser); xfree(uncompressed); #ifdef JABBER_HAVE_SSL } while (j->using_ssl && j->ssl_session); #endif return 0; } static TIMER_SESSION(jabber_ping_timer_handler) { jabber_private_t *j; if (type == 1) return 0; if (!s || !s->priv || !s->connected) { return -1; } j = jabber_private(s); if (j->istlen) { watch_write(j->send_watch, " \t "); /* ping according to libtlen */ return 0; } if (session_int_get(s, "ping_server") == 0) return -1; /* XEP-0199 */ watch_write(j->send_watch, "\n", j->server, j->id++); return 0; } static WATCHER(jabber_handle_connect_tlen_hub); WATCHER(jabber_handle_connect) { session_t *s = (session_t *) data; jabber_private_t *j = jabber_private(s); int tlenishub; if (type) return -1; debug_function("[jabber] socket() = %d\n", fd); tlenishub = (j->istlen > 1); j->fd = fd; if (tlenishub) { char *req, *esc; j->istlen = 1; /* reset */ esc = tlen_encode(s->uid+5); req = saprintf("GET /4starters.php?u=%s&v=10 HTTP/1.0\r\nHost: %s\r\n\r\n", esc, TLEN_HUB); /* libtlen */ write(fd, req, xstrlen(req)); xfree(req); xfree(esc); /* XXX, timeout? */ watch_add(&jabber_plugin, fd, WATCH_READ, jabber_handle_connect_tlen_hub, data); /* WATCH_READ_LINE? */ return -1; } else { session_t *s = (session_t *) data; jabber_private_t *j = session_private_get(s); session_int_set(s, "__roster_retrieved", 0); watch_add_session(s, fd, WATCH_READ, jabber_handle_stream); j->using_compress = JABBER_COMPRESSION_NONE; #ifdef JABBER_HAVE_SSL j->send_watch = watch_add_line(&jabber_plugin, fd, WATCH_WRITE_LINE, j->using_ssl ? jabber_handle_write : NULL, j); #else j->send_watch = watch_add_line(&jabber_plugin, fd, WATCH_WRITE_LINE, NULL, NULL); #endif if (!(j->istlen)) { watch_write(j->send_watch, "", j->server, (session_int_get(s, "disable_sasl") != 2) ? " version=\"1.0\"" : ""); } else { watch_write(j->send_watch, ""); } j->id = 1; j->parser = jabber_parser_recreate(NULL, s); if (j->istlen || (session_int_get(s, "ping_server") != 0)) { if (timer_find_session(s, "ping") == NULL) { /* w/g dokumentacji do libtlen powinnismy wysylac pinga co 60 sekund */ timer_add_session(s, "ping", j->istlen ? 60 : 180, 1, jabber_ping_timer_handler); } } } return -1; } WATCHER(jabber_handle_connect2) { session_t *s = (session_t *) data; jabber_private_t *j = jabber_private(s); j->connect_watch = NULL; if (type == -1) { /* special ekg_connect() state */ jabber_handle_disconnect(s, _("No server could be reached"), EKG_DISCONNECT_FAILURE); /* fd == -1 */ return 0; } if (type == 2) { jabber_handle_disconnect(s, _("No server could be reached"), EKG_DISCONNECT_FAILURE); /* XXX, session timeouted */ return 0; } #ifdef JABBER_HAVE_SSL if (session_int_get(s, "use_ssl")) { jabber_handle_connect_ssl(-1, fd, 0, s); return -1; } #endif return jabber_handle_connect(type, fd, watch, data); } static WATCHER(jabber_handle_connect_tlen_hub) { /* tymczasowy */ session_t *s = (session_t *) data; jabber_private_t *j = jabber_private(s); char *header, *body; char buf[1024]; int len; if (type) { close(fd); return 0; } /* libtlen */ len = read(fd, buf, sizeof(buf)); buf[len] = 0; header = xstrstr(buf, "\r\n"); body = xstrstr(buf, "\r\n\r\n"); if (header && body) { *header = '\0'; body += 4; debug_function("[TLEN, HUB]: %s / %s\n", buf, body); if (!xstrstr(buf, " 200 ")) return -1; /* XXX: use XML parser instead of hardcoded lengths */ /* 91 */ { char *end, *endb; body += 6; if ((end = xstrchr(body, '\''))) { *end = 0; end += 5; if ((endb = xstrchr(end, '\''))) *endb = 0; const int newport = atoi(end); if (newport != 0) j->port = newport; } } debug_function("[TLEN, HUB]: host = %s, port = %d\n", body, j->port); if (!ekg_connect(s, body, 5222, j->port, jabber_handle_connect2)) { /* XXX, we should have disconnect here.. */ print("generic_error", strerror(errno)); return -1; } return -1; } /* XXX: hm? */ if (len == 0) return -1; else return 0; } XML_Parser jabber_parser_recreate(XML_Parser parser, void *data) { /* debug_function("jabber_parser_recreate() 0x%x 0x%x\n", parser, data); */ if (!parser) parser = XML_ParserCreateNS("UTF-8", '\033'); /* new parser */ else XML_ParserReset(parser, "UTF-8"); /* reset parser */ XML_SetUserData(parser, (void*) data); XML_SetElementHandler(parser, (XML_StartElementHandler) xmlnode_handle_start, (XML_EndElementHandler) xmlnode_handle_end); XML_SetCharacterDataHandler(parser, (XML_CharacterDataHandler) xmlnode_handle_cdata); return parser; } #ifdef JABBER_HAVE_SSL /** * jabber_ssl_cert_verify() * * Initial version of routine to test if certificate used by SSL_SESSION is 100% correct. * If not, return error why isn't ok. * * @note Code to handle SSL_get_verify_result() result copied from qssl.cpp
* qssl.cpp - Qt OpenSSL plugin Copyright (C) 2001, 2002 Justin Karneges under LGPL 2.1 * * @todo It's testing function, so it don't catch all not 100% valid certificates. * If you can and you know smth/ a lot about either OpenSSL or GnuTLS. Could you look at it? * * @param ssl - SSL_SESSION * * @return NULL if certificate is correct.
* else NUL terminated string with error description. */ static const char *jabber_ssl_cert_verify(const SSL_SESSION ssl) { #ifdef HAVE_LIBSSL X509 *peer_cert = SSL_get_peer_certificate(ssl); long ret; if (!peer_cert) return _("No peer certificate"); switch ((ret = SSL_get_verify_result(ssl))) { /* copied from qssl.cpp - Qt OpenSSL plugin Copyright (C) 2001, 2002 Justin Karneges under LGPL 2.1 */ case X509_V_OK: return NULL; case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: return _("Unable to get issuer certificate"); case X509_V_ERR_UNABLE_TO_GET_CRL: return _("Unable to get certificate CRL"); case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: return _("Unable to decrypt certificate's signature"); case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE: return _("Unable to decrypt CRL's signature"); case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: return _("Unable to decode issuer public key"); case X509_V_ERR_CERT_SIGNATURE_FAILURE: return _("Invalid certificate signature"); case X509_V_ERR_CRL_SIGNATURE_FAILURE: return _("Invalid CRL signature"); case X509_V_ERR_CERT_NOT_YET_VALID: return _("Certificate not yet valid"); case X509_V_ERR_CERT_HAS_EXPIRED: return _("Certificate has expired"); case X509_V_ERR_CRL_NOT_YET_VALID: return _("CRL not yet valid"); case X509_V_ERR_CRL_HAS_EXPIRED: return _("CRL has expired"); case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: return _("Invalid time in certifiate's notBefore field"); case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: return _("Invalid time in certificate's notAfter field"); case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD: return _("Invalid time in CRL's lastUpdate field"); case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD: return _("Invalid time in CRL's nextUpdate field"); case X509_V_ERR_OUT_OF_MEM: return _("Out of memory while checking the certificate chain"); case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: return _("Certificate is self-signed but isn't found in the list of trusted certificates"); case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: return _("Certificate chain ends in a self-signed cert that isn't found in the list of trusted certificates"); case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: return _("Unable to get issuer certificate locally"); case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: return _("Certificate chain contains only one certificate and it's not self-signed"); case X509_V_ERR_CERT_CHAIN_TOO_LONG: return _("Certificate chain too long"); case X509_V_ERR_CERT_REVOKED: return _("Certificate is revoked"); case X509_V_ERR_INVALID_CA: return _("Invalid CA certificate"); case X509_V_ERR_PATH_LENGTH_EXCEEDED: return _("Maximum certificate chain length exceeded"); case X509_V_ERR_INVALID_PURPOSE: return _("Invalid certificate purpose"); case X509_V_ERR_CERT_UNTRUSTED: return _("Certificate not trusted for the required purpose"); case X509_V_ERR_CERT_REJECTED: return _("Root CA is marked to reject the specified purpose"); case X509_V_ERR_SUBJECT_ISSUER_MISMATCH: return _("Subject issuer mismatch"); case X509_V_ERR_AKID_SKID_MISMATCH: return _("Subject Key Identifier doesn't match the Authority Key Identifier"); case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH: return _("Subject Key Identifier serial number doesn't match the Authority's"); case X509_V_ERR_KEYUSAGE_NO_CERTSIGN: return _("Key Usage doesn't include certificate signing"); default: debug_error("[jabber] SSL_get_verify_result() unknown retcode: %d\n", ret); return _("Unknown/Generic SSL_get_verify_result() result"); } return NULL; /* never here */ #else static char buf[100]; int res; unsigned int ret; if ((res = gnutls_certificate_verify_peers2(ssl, &ret)) != 0) return gnutls_strerror(res); buf[0] = '\0'; /* ret is bitmask of gnutls_certificate_status_t */ if (ret & GNUTLS_CERT_INVALID) xstrcat(buf, "Certificate is invalid:"); /* 23b */ if (ret & GNUTLS_CERT_REVOKED) xstrcat(buf, " revoked"); /* 08b */ if (ret & GNUTLS_CERT_SIGNER_NOT_FOUND) xstrcat(buf, " signer not found"); /* 17b */ if (ret & GNUTLS_CERT_SIGNER_NOT_CA) xstrcat(buf, " signer not a CA"); /* 16b */ /* if (ret & GNUTLS_CERT_INSECURE_ALGORITHM) xstrcat(buf, " INSECURE ALGO?"); */ return (buf[0] != '\0') ? buf : NULL; #endif /* XXX czy sie dane zgadzaja z j->server */ } /* * jabber_handle_connect_ssl() * * Asynchronic connection to jabberd server by SSL or TLS [XXX]. * TEMPORARY, Session watch.
* If @a type is -1 than it try to create ssl structs. * * @todo Some xxx's to fix. * */ WATCHER_SESSION(jabber_handle_connect_ssl) { jabber_private_t *j; int ret; const char *certret; if (!s || !(j = s->priv)) return -1; if (type == -1) { /* XXX here. old tls code do: j->parser = NULL. check if needed */ #ifdef HAVE_LIBGNUTLS /* Allow connections to servers that have OpenPGP keys as well. */ const int cert_type_priority[3] = {GNUTLS_CRT_X509, GNUTLS_CRT_OPENPGP, 0}; const int comp_type_priority[3] = {GNUTLS_COMP_ZLIB, GNUTLS_COMP_NULL, 0}; #endif if ((ret = SSL_INIT(j->ssl_session))) { /* XXX, OpenSSL error value XXX */ print("conn_failed_tls"); jabber_handle_disconnect(s, SSL_ERROR(ret), EKG_DISCONNECT_FAILURE); return -1; } #ifdef HAVE_LIBGNUTLS gnutls_set_default_priority(j->ssl_session); gnutls_certificate_type_set_priority(j->ssl_session, cert_type_priority); gnutls_credentials_set(j->ssl_session, GNUTLS_CRD_CERTIFICATE, j->xcred); gnutls_compression_set_priority(j->ssl_session, comp_type_priority); /* we use read/write instead of recv/send */ gnutls_transport_set_pull_function(j->ssl_session, (gnutls_pull_func)read); gnutls_transport_set_push_function(j->ssl_session, (gnutls_push_func)write); #endif if (SSL_SET_FD(j->ssl_session, fd) == 0) { /* gnutls never fail */ print("conn_failed_tls"); SSL_DEINIT(j->ssl_session); j->ssl_session = NULL; jabber_handle_disconnect(s, SSL_ERROR(ret), EKG_DISCONNECT_FAILURE); return -1; } watch_add_session(s, fd, WATCH_WRITE, jabber_handle_connect_ssl); /* XXX, continue type = 0; and let's rock */ } if (type) return 0; ret = SSL_HELLO(j->ssl_session); #ifdef HAVE_LIBSSL if (ret != -1) goto handshake_ok; /* ssl was ok */ ret = SSL_get_error(j->ssl_session, ret); #endif if (SSL_E_AGAIN(ret)) { int direc = SSL_WRITE_DIRECTION(j->ssl_session, ret) ? WATCH_WRITE : WATCH_READ; int newfd = SSL_GET_FD(j->ssl_session, fd); /* don't create && destroy watch if data is the same... */ if (newfd == fd && direc == watch) { ekg_yield_cpu(); return 0; } watch_add_session(s, fd, direc, jabber_handle_connect_ssl); ekg_yield_cpu(); return -1; } else { #ifdef HAVE_LIBGNUTLS if (ret >= 0) goto handshake_ok; /* gnutls was ok */ /* XXX, move it to jabber_handle_disconnect() */ SSL_DEINIT(j->ssl_session); j->using_ssl = 0; /* XXX, hack, peres has reported that here j->using_ssl can be 1 (how possible?) hack to avoid double free */ #endif jabber_handle_disconnect(s, SSL_ERROR(ret), EKG_DISCONNECT_FAILURE); return -1; } handshake_ok: if ((certret = jabber_ssl_cert_verify(j->ssl_session))) { debug_error("[jabber] jabber_ssl_cert_verify() %s retcode = %s\n", s->uid, certret); print("generic2", certret); } /* if we already got send_watch, then it's TLS connection HACK XXX */ if (j->send_watch) { j->using_ssl = 2; /* XXX, recv watch */ /* send watch. */ j->send_watch->type = WATCH_WRITE; j->send_watch->handler = jabber_handle_write; /* reset parser */ j->parser = jabber_parser_recreate(NULL, XML_GetUserData(j->parser)); /* reinitialize stream */ watch_write(j->send_watch, "", j->server); } else { // handshake successful j->using_ssl = 1; watch_add(&jabber_plugin, fd, WATCH_WRITE, jabber_handle_connect, s); } return -1; } #endif static QUERY(jabber_protocol_ignore) { char *sesion = *(va_arg(ap, char **)); char *uid = *(va_arg(ap, char **)); /* int oldlvl = *(va_arg(ap, int *)); int newlvl = *(va_arg(ap, int *)); */ session_t *s = session_find(sesion); /* check this just to be sure... */ if (session_check(s, 1, "xmpp")) /* SPOT rule, first of all here was code to check sesion, valid user, etc... * then send jabber:iq:roster request... with all new & old group... * but it was code copied from modify command handler... so here it is. */ command_exec_format(NULL, s, 0, ("/xmpp:modify %s -x"), uid); return 0; } static QUERY(jabber_status_show_handle) { char *uid = *(va_arg(ap, char**)); session_t *s = session_find(uid); jabber_private_t *j = session_private_get(s); userlist_t *u; char *fulluid; char *tmp; if (!s || !j) return -1; fulluid = saprintf("%s/%s", uid, j->resource); // nasz stan if ((u = userlist_find(s, uid)) && u->nickname) print("show_status_uid_nick", fulluid, u->nickname); else print("show_status_uid", fulluid); xfree(fulluid); // nasz status tmp = (s->connected) ? format_string(format_find(ekg_status_label(s->status, s->descr, "show_status_")),s->descr, "") : format_string(format_find("show_status_notavail"), ""); print("show_status_status_simple", tmp); xfree(tmp); // serwer #ifdef JABBER_HAVE_SSL print(j->using_ssl ? "show_status_server_tls" : "show_status_server", j->server, ekg_itoa(j->port)); #else print("show_status_server", j->server, ekg_itoa(j->port)); #endif if (session_int_get(s, "__gpg_enabled") == 1) print("jabber_gpg_sok", session_name(s), session_get(s, "gpg_key")); if (s->connecting) print("show_status_connecting"); return 0; } static int jabber_theme_init() { #ifndef NO_DEFAULT_THEME /* USERLIST_INFO */ format_add("user_info_auth_type", _("%K| %nSubscription type: %T%1%n\n"), 1); format_add("jabber_xmlerror_disconnect", _("Error parsing XML: %R%1%n"), 1); format_add("jabber_msg_failed", _("%! Message to %T%1%n can't be delivered: %R(%2) %r%3%n\n"),1); format_add("jabber_msg_failed_long", _("%! Message to %T%1%n %y(%n%K%4(...)%y)%n can't be delivered: %R(%2) %r%3%n\n"),1); format_add("jabber_unknown_resource", _("%! (%1) User's resource unknown%n\n\n"), 1); format_add("jabber_status_notavail", _("%! (%1) Unable to check version, because %2 is unavailable%n\n"), 1); format_add("jabber_remotecontrols_preparing", _("%> (%1) Remote client: %W%2%n is preparing to execute command @node: %W%3"), 1); /* %2 - uid %3 - node */ format_add("jabber_remotecontrols_commited", _("%> (%1) Remote client: %W%2%n executed command @node: %W%3"), 1); /* %2 - uid %3 - node */ format_add("jabber_remotecontrols_commited_status", _("%> (%1) RC %W%2%n: requested changing status to: %3 %4 with priority: %5"), 1); /* %3 - status %4 - descr %5 - prio */ /* %3 - command+params %4 - sessionname %5 - target %6 - quiet */ format_add("jabber_remotecontrols_commited_command",_("%> (%1) RC %W%2%n: requested command: %W%3%n @ session: %4 window: %5 quiet: %6"), 1); format_add("jabber_form_title", "%g,+=%G----- %3 %n(%T%2%n)", 1); format_add("jabber_form_item", "%g|| %n%(21)3 (%6) %K|%n --%4 %(20)5", 1); /* %3 - label %4 - keyname %5 - value %6 - req; optional */ format_add("jabber_form_item_beg", "%g|| ,+=%G-----%n", 1); format_add("jabber_form_item_plain", "%g|| | %n %3: %5", 1); /* %3 - label %4 - keyname %5 - value */ format_add("jabber_form_item_end", "%g|| `+=%G-----%n", 1); format_add("jabber_form_item_val", "%K[%b%3%n %g%4%K]%n", 1); /* %3 - value %4 - label */ format_add("jabber_form_item_sub", "%g|| %|%n\t%3", 1); /* %3 formated jabber_form_item_val */ format_add("jabber_form_command", _("%g|| %nType %W/%3 %g%2 %W%4%n"), 1); format_add("jabber_form_instructions", "%g|| %n%|%3", 1); format_add("jabber_form_description", "%g|| %n%|%3", 1); format_add("jabber_form_end", _("%g`+=%G----- End of this %3 form ;)%n"), 1); format_add("jabber_registration_item", "%g|| %n --%3 %4%n", 1); /* %3 - keyname %4 - value */ /* XXX, merge */ /* simple XEP-0071 - XML parsing error */ format_add("jabber_msg_xmlsyntaxerr", _("%! Expat syntax-checking failed on your message: %T%1%n. Please correct your code or use double ^R to disable syntax-checking."), 1); /* %1 - session %2 - message %3 - start %4 - end */ format_add("jabber_vacation", _("%> You'd set up your vacation status: %g%2%n (since: %3 expires@%4)"), 1); /* %1 - sessionname %2 - mucjid %3 - nickname %4 - text %5 - atr */ format_add("jabber_muc_recv", "%B<%w%X%5%3%B>%n %4", 1); format_add("jabber_muc_send", "%B<%n%X%5%W%3%B>%n %4", 1); format_add("jabber_muc_me", "%y*%X%5%3%B%n %4", 1); format_add("jabber_muc_me_sent","%Y*%X%5%3%B%n %4", 1); /* %1 - sessionname, %2 - mucjid %3 - text */ format_add("jabber_muc_notice", "%n-%P%2%n- %3", 1); format_add("jabber_muc_room_created", _("%> Room %W%2%n created, now to configure it: type %W/admin %g%2%n to get configuration form, or type %W/admin %g%2%n --instant to create instant one"), 1); format_add("jabber_muc_banlist", _("%g|| %n %5 - %W%2%n: ban %c%3%n [%4]"), 1); /* %1 sesja %2 kanal %3 kto %4 reason %5 numerek */ #if 0 format_add("jabber_send_chan", _("%B<%W%2%B>%n %5"), 1); format_add("jabber_send_chan_n", _("%B<%W%2%B>%n %5"), 1); format_add("jabber_recv_chan", _("%b<%w%2%b>%n %5"), 1); format_add("jabber_recv_chan_n", _("%b<%w%2%b>%n %5"), 1); #endif /* %1 sesja %2 nick %3 - jid %4 - kanal %6 - role %7 affiliation*/ format_add("muc_joined", _("%> %C%2%n %B[%c%3%B]%n has joined %W%4%n as a %g%6%n and a %g%7%n"), 1); /* %1 sesja %2 nick %3 - jid %4 - kanal %5 - reason */ format_add("muc_left", _("%> %c%2%n [%c%3%n] has left %W%4 %n[%5]\n"), 1); format_add("gmail_new_mail", _("%> (%1) Content of your mailbox have changed or new mail arrived."), 1); /* sesja */ format_add("gmail_count", _("%> (%1) You have %T%2%n new thread(s) on your gmail account."), 1); /* sesja, mail count */ format_add("gmail_mail", "%> %|%T%2%n - %g%3%n - %c%5%\n", 1); /* sesja, from, topic, [UNUSED messages count in thread (?1)], snippet */ format_add("gmail_thread", "%> %|%T%2 [%4]%n - %g%3%n\n", 1); /* sesja, from, topic, messages count in thread */ format_add("tlen_mail", _("%> (%1) New mail from %T%2%n, with subject: %G%3%n"), 1); /* sesja, from, topic */ format_add("tlen_alert", _("%> (%1) %T%2%n sent us an alert ...%n"), 1); /* sesja, from */ format_add("tlen_alert_send", _("%> (%1) We send alert to %T%2%n"), 1); /* sesja, to */ format_add("jabber_remotecontrols_executing", _("%> (%1) Executing command: %W%3%n @ %W%2%n (%4)"), 1); format_add("jabber_remotecontrols_completed", _("%> (%1) Command: %W%3%n @ %W%2 %gcompleted"), 1); format_add("jabber_iq_stanza", _("%> (%1) %gIQ: <%W%2 %gxmlns='%W%3%g' to='%W%4%g' id='%W%5%g'>"), 1); /* auth */ format_add("jabber_auth_subscribe", _("%> (%2) %T%1%n asks for authorisation. Use \"/auth -a %1\" to accept, \"/auth -d %1\" to refuse.%n\n"), 1); format_add("jabber_auth_unsubscribe", _("%> (%2) %T%1%n asks for removal. Use \"/auth -d %1\" to delete.%n\n"), 1); format_add("jabber_auth_request", _("%> (%2) Sent authorisation request to %T%1%n.\n"), 1); format_add("jabber_auth_accept", _("%> (%2) Authorised %T%1%n.\n"), 1); format_add("jabber_auth_unsubscribed", _("%> (%2) Asked %T%1%n to remove authorisation.\n"), 1); format_add("jabber_auth_cancel", _("%> (%2) Authorisation for %T%1%n revoked.\n"), 1); format_add("jabber_auth_denied", _("%> (%2) Authorisation for %T%1%n denied.\n"), 1); format_add("jabber_auth_probe", _("%> (%2) Sent presence probe to %T%1%n.\n"), 1); format_add("jabber_auth_rejectnoreq", _("%! (%2) No pending authorization request from %T%1%n. Use \"/auth -d %1\" to force unauth.\n"), 1); format_add("jabber_auth_acceptnoreq", _("%> %|(%2) No pending authorization request from %T%1%n. Permission has been sent, but the user would probably need to request one first.\n"), 1); /* XXX: some table? different coloring of different request types? */ format_add("jabber_auth_list_req", _("%> (%1) Pending authorization requests:\n"), 1); format_add("jabber_auth_list_unreq", _("%> (%1) Pending removal requests:\n"), 1); format_add("jabber_auth_list", _("%) - %G%1%n\n"), 1); format_add("jabber_auth_list_empty", _("%> (%1) No pending requests."), 1); /* conversations */ format_add("jabber_conversations_begin", _("%g,+=%G--%n (%1) %GAvailable Reply-IDs:%n"), 1); format_add("jabber_conversations_item", _("%g|| %n %1 - %W%2%n (%g%3%n [%c%4%n])"), 1); /* %1 - n, %2 - user, %3 - subject, %4 - thread */ format_add("jabber_conversations_end", _("%g`+=%G-- End of the available Reply-ID list%n"), 1); format_add("jabber_conversations_nothread", _("non-threaded"), 1); format_add("jabber_conversations_nosubject", _("[no subject]"), 1); format_add("jabber_gone", _("%> (%1) User %G%2%n has left the conversation."), 1); /* gpg */ format_add("jabber_gpg_plugin", _("%> (%1) To use OpenGPG support in jabber, first load gpg plugin!"), 1); /* sesja */ format_add("jabber_gpg_config", _("%> (%1) First set gpg_key and gpg_password before turning on gpg_active!"), 1); /* sesja */ format_add("jabber_gpg_ok", _("%) (%1) GPG support: %gENABLED%n using key: %W%2%n"), 1); /* sesja, klucz */ format_add("jabber_gpg_sok", _("%) GPG key: %W%2%n"), 1); /* sesja, klucz for /status */ format_add("jabber_gpg_fail", _("%> (%1) We didn't manage to sign testdata using key: %W%2%n (%R%3%n)\nOpenGPG support for this session disabled."), 1); /* sesja, klucz, error */ /* stream:features */ /* %1 - sesja, %2 - serwer, %3 - nazwa, %4 - XMLNS, %5 - z czym sie je */ format_add("xmpp_feature_header", _("%g,+=%G----- XMPP features %n(%T%2%n%3%n)"), 1); /* %3 - todo */ format_add("xmpp_feature", _("%g|| %n %W%2%n can: %5 [%G%3%g,%4%n]"), 1); format_add("xmpp_feature_sub", _("%g|| %n %W%3%n: %5 [%G%4%n]"), 1); format_add("xmpp_feature_sub_unknown", _("%g|| %n %W%3%n: Unknown, report to devs [%G%4%n]"), 1); format_add("xmpp_feature_unknown", _("%g|| %n %W%2%n feature: %r%3 %n[%G%3%g,%4%n]"), 1); format_add("xmpp_feature_footer", _("%g`+=%G----- %n Turn it off using: /session display_server_features 0\n"), 1); /* http://jabber.org/protocol/disco#items */ /* %1 - session_name, %2 - uid (*_item: %3 - agent uid %4 - description %5 - seq id) */ format_add("jabber_transport_list_begin", _("%g,+=%G----- Available agents on: %T%2%n"), 1); format_add("jabber_transport_list_item", ("%g|| %n %6 - %W%3%n (%5)"), 1); format_add("jabber_transport_list_item_node", _("%g|| %n %6 - %W%3%n node: %g%4%n (%5)"), 1); format_add("jabber_transport_list_end", _("%g`+=%G----- End of the agents list%n\n"), 1); format_add("jabber_transport_list_nolist", _("%! No agents @ %T%2%n"), 1); format_add("jabber_transport_error", _("%! (%1) Error in getting %gavailable agents%n from %W%2%n: %r%3"), 1); /* http://jabber.org/protocol/disco#items ## remotecontrol */ format_add("jabber_remotecontrols_list_begin", _("%g,+=%G----- Available remote controls on: %T%2%n"), 1); format_add("jabber_remotecontrols_list_item", ("%g|| %n %6 - %W%4%n (%5)"), 1); /* %3 - jid %4 - node %5 - descr %6 - seqid */ format_add("jabber_remotecontrols_list_end", _("%g`+=%G----- End of the remote controls list%n\n"), 1); format_add("jabber_remotecontrols_list_nolist", _("%! No remote controls @ %T%2%n"), 1); format_add("jabber_remotecontrols_error", _("%! (%1) Error in getting %gavailable commands%n from %W%2%n: %r%3"), 1); /* http://jabber.org/protocol/disco#info */ format_add("jabber_transinfo_begin", _("%g,+=%G----- Information about: %T%2%n"), 1); format_add("jabber_transinfo_begin_node", _("%g,+=%G----- Information about: %T%2%n (%3)"), 1); format_add("jabber_transinfo_identify", _("%g|| %G --== %g%3 %G==--%n"), 1); /* %4 - real fjuczer name %3 - translated fjuczer name. */ format_add("jabber_transinfo_feature", _("%g|| %n %W%2%n feature: %n%3"), 1); format_add("jabber_transinfo_comm_ser", _("%g|| %n %W%2%n can: %n%3 %2 (%4)"), 1); format_add("jabber_transinfo_comm_use", _("%g|| %n %W%2%n can: %n%3 $uid (%4)"), 1); format_add("jabber_transinfo_comm_not", _("%g|| %n %W%2%n can: %n%3 (%4)"), 1); format_add("jabber_transinfo_end", _("%g`+=%G----- End of the infomations%n\n"), 1); format_add("jabber_transinfo_error", _("%! (%1) Error in getting %ghttp://jabber.org/protocol/disco#info%n from %W%2%n: %r%3"), 1); /* vCard xmlns='vcard-temp' */ format_add("jabber_userinfo_response", _("%> Jabber ID: %T%1%n\n%> Full Name: %T%2%n\n%> Nickname: %T%3%n\n%> Birthday: %T%4%n\n%> City: %T%5%n\n%> Desc: %T%6%n\n"), 1); format_add("jabber_userinfo_response2", _("%g,+=%G----- vCard for:%n %T%2"), 1); format_add("jabber_userinfo_fullname", _("%g|| %n Full Name: %T%2"), 1); format_add("jabber_userinfo_nickname", _("%g|| %n Nickame: %T%2"), 1); format_add("jabber_userinfo_birthday", _("%g|| %n Birthday: %T%2"), 1); format_add("jabber_userinfo_email", _("%g|| %n Email: %T%2"), 1); format_add("jabber_userinfo_url", _("%g|| %n Webpage: %T%2"), 1); format_add("jabber_userinfo_desc", _("%g|| %n Description: %T%2"), 1); format_add("jabber_userinfo_telephone", _("%g|| %n Telephone: %T%2"), 1); format_add("jabber_userinfo_title", _("%g|| %n Title: %T%2"), 1); format_add("jabber_userinfo_organization", _("%g|| %nOrganization: %T%2"), 1); format_add("jabber_userinfo_adr", _("%g|| ,+=%G----- (Next) %2 address"), 1); format_add("jabber_userinfo_adr_street", _("%g|| || %n Street: %T%2"), 1); format_add("jabber_userinfo_adr_postalcode", _("%g|| || %nPostal code: %T%2"), 1); format_add("jabber_userinfo_adr_city", _("%g|| || %n City: %T%2"), 1); format_add("jabber_userinfo_adr_country", _("%g|| || %n Country: %T%2"), 1); format_add("jabber_userinfo_adr_end", _("%g|| %g`+=%G-----"), 1); format_add("jabber_userinfo_photourl", _("%g||\n%g|| %nYou can view attached photo at: %T%1"), 1); format_add("jabber_userinfo_end", _("%g`+=%G-----"), 1); format_add("jabber_userinfo_error", _("%! (%1) Error in getting %gvCard%n from %W%2%n: %r%3"), 1); /* jabber:iq:privacy */ /* %1 - session_name, %2 - server/ uid */ format_add("jabber_privacy_list_begin", _("%g,+=%G----- Privacy lists on %T%2%n"), 1); format_add("jabber_privacy_list_item", _("%g|| %n %3 - %W%4%n"), 1); /* %3 - lp %4 - itemname */ format_add("jabber_privacy_list_item_def", _("%g|| %g Default:%n %W%4%n"), 1); format_add("jabber_privacy_list_item_act", _("%g|| %r Active:%n %W%4%n"), 1); format_add("jabber_privacy_list_end", _("%g`+=%G----- End of the privacy list%n"), 1); format_add("jabber_privacy_list_noitem", _("%! No privacy lists in %T%2%n"), 1); format_add("jabber_privacy_item_header", _("%g,+=%G----- Details for: %T%3%n\n%g||%n JID\t\t\t\t\t MSG PIN POUT IQ%n"), 1); format_add("jabber_privacy_item", ("%g||%n %[-44]4 \t%K|%n %[2]5 %K|%n %[2]6 %K|%n %[2]7 %K|%n %[2]8\n"), 1); format_add("jabber_privacy_item_footer", _("%g`+=%G----- Legend: %n[%3] [%4]%n"), 1); /* %1 - item [group, jid, subscri*] */ format_add("jabber_privacy_item_allow", "%G%1%n", 1); format_add("jabber_privacy_item_deny", "%R%1%n", 1); format_add("jabber_privacy_error", _("%! (%1) Error in getting/setting %gprivacy list%n from %W%2%n: %r%3"), 1); /* jabber:iq:private */ /* %1 - session_name %2 - list_name %3 xmlns */ format_add("jabber_private_list_header", _("%g,+=%G----- Private list: %T%2/%3%n"), 1); /* jabber:iq:private ## bookmarks */ format_add("jabber_bookmark_url", _("%g|| %n URL: %W%3%n (%2)"), 1);/* %1 - session_name, bookmark url item: %2 - name %3 - url */ format_add("jabber_bookmark_conf", _("%g|| %n MUC: %W%3%n (%2)"), 1);/* %1 - session_name, bookmark conf item: %2 - name %3 - jid %4 - autojoin %5 - nick %6 - password */ /* jabber:iq:private ## config */ format_add("jabber_private_list_item", "%g|| %n %4: %W%5%n", 1); /* %4 - item %5 - value */ format_add("jabber_private_list_session", "%g|| + %n Session: %W%4%n", 1); /* %4 - uid */ format_add("jabber_private_list_plugin", "%g|| + %n Plugin: %W%4 (%5)%n", 1); /* %4 - name %5 - prio*/ format_add("jabber_private_list_subitem", "%g|| - %n %4: %W%5%n", 1); /* %4 - item %5 - value */ format_add("jabber_private_list_footer", _("%g`+=%G----- End of the priv_data list%n"), 1); format_add("jabber_private_list_empty", _("%! No list: %T%2/%3%n"), 1); format_add("jabber_private_list_error", _("%! (%1) Error in request %gjabber:iq:private%n from %W%2%n: %r%3"), 1); /* jabber:iq:search */ format_add("jabber_search_item", _("%) JID: %T%3%n\n%) Nickname: %T%4%n\n%) Name: %T%5 %6%n\n%) Email: %T%7%n\n"), 1); /* like gg-search_results_single */ /* %3 - jid %4 - nickname %5 - firstname %6 - surname %7 - email */ format_add("jabber_search_begin", _("%g,+=%G----- Search on %T%2%n"), 1); // format_add("jabber_search_items", ("%g||%n %[-24]3 %K|%n %[10]5 %K|%n %[10]6 %K|%n %[12]4 %K|%n %[16]7"), 1); /* like gg-search_results_multi. TODO */ format_add("jabber_search_items", ("%g||%n %3 - %5 '%4' %6 <%7>"), 1); format_add("jabber_search_end", _("%g`+=%G-----"), 1); format_add("jabber_search_error", _("%! (%1) Error in %gjabber:iq:search%n from %W%2%n: %r%3"), 1); /* jabber:iq:last */ format_add("jabber_lastseen_response", _("%> Jabber ID: %T%1%n\n%> Logged out: %T%2 ago%n\n"), 1); format_add("jabber_lastseen_uptime", _("%> Jabber ID: %T%1%n\n%> Server up: %T%2 ago%n\n"), 1); format_add("jabber_lastseen_idle", _("%> Jabber ID: %T%1%n\n%> Idle for: %T%2%n\n"), 1); format_add("jabber_lastseen_error", _("%! (%1) Error in getting %gjabber:iq:last%n from %W%2%n: %r%3"), 1); /* jabber:iq:version */ format_add("jabber_version_response", _("%> Jabber ID: %T%1%n\n%> Client name: %T%2%n\n%> Client version: %T%3%n\n%> Operating system: %T%4%n\n"), 1); format_add("jabber_version_error", _("%! (%1) Error in getting %gjabber:iq:version%n from %W%2%n: %r%3"), 1); format_add("jabber_ctcp_request", _("%> (%1) %T%2%n requested IQ %g%4%n"), 1); #endif /* !NO_DEFAULT_THEME */ return 0; } void jabber_gpg_changed(session_t *s, const char *name) { plugin_t *gpg_plug; const char *key; const char *passhrase; char *error; char *msg; if (!session_postinit) return; /* SLOWDOWN! */ session_int_set(s, "__gpg_enabled", 0); if (session_int_get(s, "gpg_active") != 1) return; if (!(key = session_get(s, "gpg_key")) || !(passhrase = session_get(s, "gpg_password"))) { print("jabber_gpg_config", session_name(s)); return; } if (!(gpg_plug = plugin_find("gpg"))) { print("jabber_gpg_plugin", session_name(s)); return; /* don't remove prev set password... */ } msg = xstrdup("test"); msg = jabber_openpgp(s, NULL, JABBER_OPENGPG_SIGN, msg, NULL, &error); if (error) { session_set(s, "gpg_active", "0"); session_set(s, "gpg_password", NULL); print("jabber_gpg_fail", session_name(s), key, error); xfree(error); } else { session_int_set(s, "__gpg_enabled", 1); print("jabber_gpg_ok", session_name(s), key); } jabber_write_status(s); xfree(msg); } static void jabber_statusdescr_handler(session_t *s, const char *name) { jabber_write_status(s); } /** * jabber_pgp_postinit() * * Handler for: CONFIG_POSTINIT
* Executed after ekg2 read sessions configuration.
* Here we try to init gpg for all jabber sessions by calling jabber_gpg_changed()
* * @return 0 */ static QUERY(jabber_pgp_postinit) { session_t *s; session_postinit = 1; for (s = sessions; s; s = s->next) { /* check if it's jabber_plugin session [DON'T DO IT ON TLEN SESSIONS] */ if (s && s->plugin == &jabber_plugin && !jabber_private(s)->istlen) jabber_gpg_changed(s, NULL); } return 0; } static QUERY(jabber_userlist_info) { userlist_t *u = *va_arg(ap, userlist_t **); int quiet = *va_arg(ap, int *); jabber_userlist_private_t *up; if (!u || valid_plugin_uid(&jabber_plugin, u->uid) != 1 || !(up = jabber_userlist_priv_get(u))) return 1; printq("user_info_auth_type", jabber_authtypes[up->authtype & EKG_JABBER_AUTH_BOTH]); return 0; } static QUERY(jabber_userlist_priv_handler) { userlist_t *u = *va_arg(ap, userlist_t **); int function = *va_arg(ap, int *); jabber_userlist_private_t *j; if (!u || (valid_plugin_uid(&jabber_plugin, u->uid) != 1)) return 1; if (!(j = u->priv)) { if (function == EKG_USERLIST_PRIVHANDLER_FREE) return -1; j = xmalloc(sizeof(jabber_userlist_private_t)); u->priv = j; } switch (function) { case EKG_USERLIST_PRIVHANDLER_FREE: xfree(j->role); xfree(j->aff); xfree(u->priv); u->priv = NULL; break; case EKG_USERLIST_PRIVHANDLER_GET: *va_arg(ap, void **) = j; break; default: return 2; } return -1; } static QUERY(jabber_typing_out) { const char *session = *va_arg(ap, const char **); const char *uid = *va_arg(ap, const char **); int chatstate = *va_arg(ap, const int *); const char *jid = uid + 5; session_t *s = session_find(session); jabber_private_t *j; if (!s || s->plugin != &jabber_plugin) return 0; /* if user closes window while typing, * and we are prohibited to send , * we just send standard */ if ((EKG_CHATSTATE_GONE==chatstate) && (config_jabber_disable_chatstates & EKG_CHATSTATE_GONE)) chatstate = EKG_CHATSTATE_ACTIVE; else if (config_jabber_disable_chatstates & chatstate) return -1; j = jabber_private(s); if (j->istlen) { if (!(chatstate & EKG_CHATSTATE_COMPOSING)) return -1; watch_write(j->send_watch, "", jid, (chatstate==EKG_CHATSTATE_COMPOSING ? 't' : 'u')); return 0; } if (!newconference_find(s, uid) /* DON'T SEND CHATSTATES TO MUCS! */) { int len = 0; char *csname; switch (chatstate) { case EKG_CHATSTATE_COMPOSING: csname = "composing"; len = 1; break; case EKG_CHATSTATE_ACTIVE: csname = "active"; break; case EKG_CHATSTATE_GONE: csname = "gone"; break; case EKG_CHATSTATE_PAUSED: csname = "paused"; break; case EKG_CHATSTATE_INACTIVE: csname = "inactive"; break; default: return -1; } watch_write(j->send_watch, "" "" "<%s xmlns=\"http://jabber.org/protocol/chatstates\"/>" "\n", jid, (len ? "> * register global jabber variables, register commands with call to jabber_register_commands()
* And call SSL_GLOBAL_INIT() if jabber is built with ssl support
* * @todo We should set default global jabber variables with set-vars-default * * @sa jabber_plugin_destroy() * * @return 0 [successfully loaded plugin] */ static const char *jabber_protocols[] = { "xmpp:", "tlen:", NULL }; static const status_t jabber_statuses[] = { EKG_STATUS_NA, EKG_STATUS_DND, EKG_STATUS_XA, EKG_STATUS_AWAY, EKG_STATUS_AVAIL, EKG_STATUS_FFC, EKG_STATUS_INVISIBLE, EKG_STATUS_ERROR, EKG_STATUS_UNKNOWN, EKG_STATUS_NULL }; static const struct protocol_plugin_priv jabber_priv = { .protocols = jabber_protocols, .statuses = jabber_statuses }; EXPORT int jabber_plugin_init(int prio) { PLUGIN_CHECK_VER("jabber"); jabber_plugin.params = jabber_plugin_vars; jabber_plugin.priv = &jabber_priv; plugin_register(&jabber_plugin, prio); session_postinit = 0; query_connect(&jabber_plugin, "protocol-validate-uid", jabber_validate_uid, NULL); query_connect(&jabber_plugin, "plugin-print-version", jabber_print_version, NULL); query_connect(&jabber_plugin, "session-added", jabber_session_init, NULL); query_connect(&jabber_plugin, "session-removed", jabber_session_deinit, NULL); query_connect(&jabber_plugin, "status-show", jabber_status_show_handle, NULL); query_connect(&jabber_plugin, "ui-window-kill", jabber_window_kill, NULL); query_connect(&jabber_plugin, "protocol-ignore", jabber_protocol_ignore, NULL); query_connect(&jabber_plugin, "config-postinit", jabber_dcc_postinit, NULL); query_connect(&jabber_plugin, "config-postinit", jabber_pgp_postinit, NULL); query_connect(&jabber_plugin, "userlist-info", jabber_userlist_info, NULL); query_connect(&jabber_plugin, "userlist-privhandle", jabber_userlist_priv_handler, NULL); query_connect(&jabber_plugin, "protocol-typing-out", jabber_typing_out, NULL); variable_add(&jabber_plugin, ("xmpp:beep_mail"), VAR_BOOL, 1, &config_jabber_beep_mail, NULL, NULL, NULL); variable_add(&jabber_plugin, ("xmpp:dcc"), VAR_BOOL, 1, &jabber_dcc, (void*) jabber_dcc_postinit, NULL, NULL); variable_add(&jabber_plugin, ("xmpp:dcc_ip"), VAR_STR, 1, &jabber_dcc_ip, NULL, NULL, NULL); variable_add(&jabber_plugin, ("xmpp:default_pubsub_server"), VAR_STR, 1, &jabber_default_pubsub_server, NULL, NULL, NULL); variable_add(&jabber_plugin, ("xmpp:default_search_server"), VAR_STR, 1, &jabber_default_search_server, NULL, NULL, NULL); variable_add(&jabber_plugin, ("xmpp:disable_chatstates"), VAR_MAP, 1, &config_jabber_disable_chatstates, NULL, variable_map(4, 0, 0, "none", EKG_CHATSTATE_COMPOSING, 0, "composing", EKG_CHATSTATE_ACTIVE, 0, "active", EKG_CHATSTATE_GONE, 0, "gone"), NULL); jabber_register_commands(); #ifdef JABBER_HAVE_SSL SSL_GLOBAL_INIT(); #endif return 0; } /** * jabber_plugin_destroy() * * Call SSL_GLOBAL_DEINIT() if jabber is built with ssl support
* and unregister jabber plugin. * * @sa jabber_plugin_init() * * @return 0 [successfully unloaded plugin] */ static int jabber_plugin_destroy() { #ifdef JABBER_HAVE_SSL SSL_GLOBAL_DEINIT(); #endif plugin_unregister(&jabber_plugin); return 0; } /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: * vim: noet */ ekg2-0.4~pre+20120506.1/plugins/jabber/jabber.h000066400000000000000000000175551175142753400204760ustar00rootroot00000000000000/* $Id$ */ #ifndef __EKG_JABBER_JABBER_H #define __EKG_JABBER_JABBER_H #ifdef HAVE_EXPAT_H #include #endif #include "jabber-ssl.h" #define DEFAULT_CLIENT_NAME "EKG2 -- http://www.ekg2.org" #define JABBER_DEFAULT_RESOURCE "ekg2" /* some tlen constants */ #define TLEN_HUB "idi.tlen.pl" /* hub */ #define TLEN_FALLBACK_SERVER "s1.tlen.pl" /* fallback server */ #define TLEN_FALLBACK_PORT 443 /* fallback port */ #define tlenjabber_escape(str) (j->istlen ? tlen_encode(str) : jabber_escape(str)) #define tlenjabber_unescape(str) (j->istlen ? tlen_decode(str) : jabber_unescape(str)) #define tlenjabber_uid(target) protocol_uid(j->istlen ? "tlen" : "xmpp", target) #define tlen_uid(target) protocol_uid("tlen", target) #define xmpp_uid(target) protocol_uid("xmpp", target) struct xmlnode_s { char *name; char *data; char **atts; char *xmlns; struct xmlnode_s *parent; struct xmlnode_s *children; struct xmlnode_s *next; /* struct xmlnode_s *prev; */ }; typedef struct xmlnode_s xmlnode_t; enum jabber_opengpg_type_t { JABBER_OPENGPG_ENCRYPT = 0, JABBER_OPENGPG_DECRYPT, JABBER_OPENGPG_SIGN, JABBER_OPENGPG_VERIFY }; enum jabber_bookmark_type_t { /* see JEP-0048 for details */ JABBER_BOOKMARK_UNKNOWN = 0, JABBER_BOOKMARK_URL, JABBER_BOOKMARK_CONFERENCE, }; typedef enum { JABBER_IQ_TYPE_NONE, JABBER_IQ_TYPE_GET, JABBER_IQ_TYPE_SET, JABBER_IQ_TYPE_RESULT, JABBER_IQ_TYPE_ERROR, } jabber_iq_type_t; typedef struct { char *name; char *url; } jabber_bookmark_url_t; typedef struct { char *name; char *jid; unsigned int autojoin : 1; char *nick; char *pass; } jabber_bookmark_conference_t; typedef struct { enum jabber_bookmark_type_t type; union { /* priv_data data based on bookmark type */ jabber_bookmark_url_t *url; /* for JABBER_BOOKMARK_URL */ jabber_bookmark_conference_t *conf; /* for JABBER_BOOKMARK_CONFERENCE */ void *other; /* ? ;p */ } priv_data; } jabber_bookmark_t; enum jabber_compression_method { JABBER_COMPRESSION_NONE = 0, JABBER_COMPRESSION_ZLIB_INIT, JABBER_COMPRESSION_LZW_INIT, JABBER_COMPRESSION_ZLIB, JABBER_COMPRESSION_LZW, }; /* name bit allow/block: */ typedef enum { PRIVACY_LIST_MESSAGE = 1, /* incoming messages */ PRIVACY_LIST_IQ = 2, /* incoming iq packets */ PRIVACY_LIST_PRESENCE_IN = 4, /* incoming presence packets */ PRIVACY_LIST_PRESENCE_OUT = 8, /* outgoint presence packets */ PRIVACY_LIST_ALL = (PRIVACY_LIST_MESSAGE | PRIVACY_LIST_IQ | PRIVACY_LIST_PRESENCE_IN | PRIVACY_LIST_PRESENCE_OUT) } jabber_iq_privacy_flags_t; typedef struct { char *type; /* jid/group/subscription/ */ char *value; /* jid:.../@group/subscription ---- value */ unsigned int allow : 1; /* 1 - allow 0 - deny */ jabber_iq_privacy_flags_t items; /* lista bitmaski j/w */ unsigned int order; /* order */ } jabber_iq_privacy_t; typedef struct { char *thread; char *uid; char *subject; void *next; } jabber_conversation_t; typedef struct { char *id; char *to; char *type; char *xmlns; void (*handler)(session_t *s, xmlnode_t *n, const char *from, const char *id); void (*error)(session_t *s, xmlnode_t *n, const char *from, const char *id); } jabber_stanza_t; /** * jabber_private_t contains priv_data data of jabber/tlen session. */ typedef struct { int fd; /**< connection's fd */ unsigned int istlen : 2; /**< whether this is a tlen session, 2 if connecting to tlen hub (XXX: ugly hack) */ enum jabber_compression_method using_compress; /**< whether we're using compressed connection, and what method */ #ifdef JABBER_HAVE_SSL unsigned char using_ssl : 2; /**< 1 if we're using SSL, 2 if we're using TLS, else 0 */ SSL_SESSION ssl_session; /**< SSL session */ #ifdef HAVE_LIBGNUTLS gnutls_certificate_credentials xcred; /**< gnutls credentials (?) */ #endif #endif int id; /**< queries ID */ XML_Parser parser; /**< expat instance */ char *server; /**< server name */ guint16 port; /**< server's port number */ unsigned int sasl_connecting :1;/**< whether we're connecting over SASL */ char *resource; /**< resource used when connecting to daemon */ char *last_gmail_result_time; /**< last time we're checking mail (this seems not to work correctly ;/) */ char *last_gmail_tid; /**< lastseen mail thread-id */ list_t privacy; /**< for jabber:iq:privacy */ list_t bookmarks; /**< for jabber:iq:private */ list_t iq_stanzas; watch_t *send_watch; watch_t *connect_watch; xmlnode_t *node; /**< current XML branch */ jabber_conversation_t *conversations; /**< known conversations */ } jabber_private_t; typedef struct { int authtype; /* from muc_userlist_t */ char *role; /* role: */ char *aff; /* affiliation: */ } jabber_userlist_private_t; enum jabber_auth_t { EKG_JABBER_AUTH_NONE = 0, EKG_JABBER_AUTH_FROM = 1, EKG_JABBER_AUTH_TO = 2, EKG_JABBER_AUTH_BOTH = 3, EKG_JABBER_AUTH_REQ = 4, EKG_JABBER_AUTH_UNREQ = 8 }; extern plugin_t jabber_plugin; extern char *jabber_default_pubsub_server; extern char *jabber_default_search_server; extern int config_jabber_beep_mail; extern const char *jabber_authtypes[]; #define jabber_private(s) ((jabber_private_t*) session_private_get(s)) #define jabber_userlist_priv_get(u) ((jabber_userlist_private_t *) userlist_private_get(&jabber_plugin, u)) void jabber_register_commands(void); XML_Parser jabber_parser_recreate(XML_Parser parser, void *data); int JABBER_COMMIT_DATA(watch_t *w); void jabber_handle(void *data, xmlnode_t *n); int jabber_privacy_freeone(jabber_private_t *j, jabber_iq_privacy_t *item); int jabber_stanza_freeone(jabber_private_t *j, jabber_stanza_t *stanza); const char *jabber_iq_reg(session_t *s, const char *prefix, const char *to, const char *type, const char *xmlns); const char *jabber_iq_send(session_t *s, const char *prefix, jabber_iq_type_t iqtype, const char *to, const char *type, const char *xmlns); /* digest.c hashowanie.. */ char *jabber_digest(const char *sid, const char *password, int istlen); char *jabber_sha1_generic(char *buf, int len); char *jabber_dcc_digest(char *sid, char *initiator, char *target); char *jabber_challenge_digest(const char *sid, const char *password, const char *nonce, const char *cnonce, const char *xmpp_temp, const char *realm); void jabber_iq_auth_send(session_t *s, const char *username, const char *passwd, const char *stream_id); char *jabber_attr(char **atts, const char *att); char *jabber_escape(const char *text); char *jabber_unescape(const char *text); char *tlen_encode(const char *what); char *tlen_decode(const char *what); int jabber_write_status(session_t *s); void jabber_reconnect_handler(int type, void *data); LIST_ADD_COMPARE(jabber_privacy_add_compare, jabber_iq_privacy_t *); int jabber_privacy_free(jabber_private_t *j); int jabber_bookmarks_free(jabber_private_t *j); int jabber_iq_stanza_free(jabber_private_t *j); #define jabber_write(s, args...) watch_write((s && s->priv) ? jabber_private(s)->send_watch : NULL, args); WATCHER_LINE(jabber_handle_write); void xmlnode_handle_end(void *data, const char *name); void xmlnode_handle_cdata(void *data, const char *text, int len); void jabber_handle_disconnect(session_t *s, const char *reason, int type); char *jabber_openpgp(session_t *s, const char *fromto, enum jabber_opengpg_type_t way, char *message, char *key, char **error); #ifdef HAVE_LIBZ char *jabber_zlib_decompress(const char *buf, int *len); char *jabber_zlib_compress(const char *buf, int *len); #endif int jabber_conversation_find(jabber_private_t *j, const char *uid, const char *subject, const char *thread, jabber_conversation_t **result, const int can_add); jabber_conversation_t *jabber_conversation_get(jabber_private_t *j, const int n); char *jabber_thread_gen(jabber_private_t *j, const char *uid); guint32 *jabber_msg_format(const char *plaintext, const xmlnode_t *html); #endif /* __EKG_JABBER_JABBER_H */ /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/plugins/jabber/jabber_dcc.c000066400000000000000000000242471175142753400212760ustar00rootroot00000000000000#include "ekg2.h" int jabber_dcc = 0; int jabber_dcc_port = 0; char *jabber_dcc_ip = NULL; static int jabber_dcc_fd = -1; #include #include #include #include #include #include #include "jabber.h" #include "jabber_dcc.h" WATCHER(jabber_dcc_handle_recv) { dcc_t *d = data; jabber_dcc_t *p; session_t *s; jabber_private_t *j; /* debug("jabber_dcc_handle_recv() data = %x type = %d\n", data, type); */ if (type) { if (d && d->priv) dcc_close(d); return 0; } if (!d || !(p = d->priv)) return -1; if (!(s = p->session) || !(j = jabber_private(s))) return -1; switch (p->protocol) { case (JABBER_DCC_PROTOCOL_BYTESTREAMS): { jabber_dcc_bytestream_t *b = p->priv_data.bytestream; char buf[16384]; /* dla data transfer */ int len; if (b->validate != JABBER_DCC_PROTOCOL_BYTESTREAMS) return -1; /* someone is doing mess with memory ? */ if (b->step != SOCKS5_DATA) { char buf[200]; /* dla SOCKS5 */ len = read(fd, &buf, sizeof(buf)-1); if (len == 0) { close(fd); return -1; } buf[len] = 0; if (buf[0] != 5) { debug_error("SOCKS5: protocol mishmash\n"); return -1; } if (buf[1] != 0) { debug_error("SOCKS5: reply error: %x\n", buf[1]); return -1; } switch (b->step) { case (SOCKS5_CONNECT): { char *ouruid; char req[47]; char *digest; int i; req[0] = 0x05; /* version */ req[1] = 0x01; /* req connect */ req[2] = 0x00; /* RSV */ req[3] = 0x03; /* ATYPE: 0x01-ipv4 0x03-DOMAINNAME 0x04-ipv6 */ req[4] = 40; /* length of hash. */ /* generate SHA1 hash */ ouruid = saprintf("%s/%s", s->uid+5, j->resource); digest = jabber_dcc_digest(p->sid, d->uid+5, ouruid); for (i=0; i < 40; i++) req[5+i] = digest[i]; xfree(ouruid); req[45] = 0x00; req[46] = 0x00; /* port = 0 */ write(fd, &req, sizeof(req)); b->step = SOCKS5_AUTH; return 0; } case (SOCKS5_AUTH): { jabber_write(p->session, "" "" "" "", d->uid+5, p->req, b->streamhost->jid); b->step = SOCKS5_DATA; d->active = 1; return 0; } default: debug_error("SOCKS5: UNKNOWN STATE: %x\n", b->step); close(fd); return -1; } } len = read(fd, &buf, sizeof(buf)-1); if (len == 0) { close(fd); return -1; } buf[len] = 0; fwrite(&buf, len, 1, p->fd); d->offset += len; if (d->offset == d->size) { /* moze sie zdarzyc ze klient chce nam wyslac wiecej danych niz na poczatku zadeklarowal... * ale takie psi tego nie umie... my tez nie. */ print("dcc_done_get", format_user(p->session, d->uid), d->filename); dcc_close(d); close(fd); return -1; } break; } default: debug_error("jabber_dcc_handle_recv() UNIMPLEMENTED PROTOTYPE: %x\n", p->protocol); } return 0; } WATCHER(jabber_dcc_handle_send) { /* XXX, try merge with jabber_dcc_handle_recv() */ dcc_t *d = data; jabber_dcc_t *p = d->priv; char buf[16384]; int flen, len; if (!d || !(p = d->priv)) { debug_error("jabber_dcc_handle_send() d == NULL 0x%x || d->priv == NULL 0x%x\n", d, d ? d->priv : NULL); return -1; } if (type) { p->sfd = -1; dcc_close(d); return -1; } if (!d->active) { debug_error("jabber_dcc_handle_send() d->active = 0\n"); return 0; /* we must wait untill stream will be accepted... BLAH! awful */ } if (!p->fd) { debug_error("jabber_dcc_handle_send() p->fd == NULL\n"); return -1; } if (p->sfd != fd) { debug_error("jabber_dcc_handle_send() p->sfd != fd\n"); return -1; } flen = sizeof(buf); if (d->offset + flen > d->size) flen = d->size - d->offset; flen = fread(&buf[0], 1, flen, p->fd); len = write(fd, &buf[0], flen); if (len < 1 && len != flen) { debug_error("jabber_dcc_handle_send() len: %d\n", len); close(fd); return -1; } d->offset += len; if (d->offset == d->size) { if (!feof(p->fd)) debug_error("d->offset > d->size... file changes size?\n"); print("dcc_done_send", format_user(p->session, d->uid), d->filename); /* Zamykamy to polaczenie... i tak takie psi nie przyjmie wiecej danych niz wyslalismy mu ... */ close(fd); return -1; } return 0; } WATCHER(jabber_dcc_handle_accepted) { /* XXX, try merge with jabber_dcc_handle_recv() */ char buf[200]; int len; if (type) return -1; len = read(fd, &buf, sizeof(buf)-1); if (len < 1) return -1; buf[len] = 0; debug_function("jabber_dcc_handle_accepted() read: %d bytes data: %s\n", len, buf); if (buf[0] != 0x05) { debug_error("SOCKS5: protocol mishmash\n"); return -1; } if (buf[1] == 0x02 /* SOCKS5 AUTH */) { char req[2]; req[0] = 0x05; req[1] = 0x00; write(fd, (char *) &req, sizeof(req)); } if (buf[1] == 0x01 /* REQ CONNECT */ && buf[2] == 0x00 /* RSVD */ && buf[3] == 0x03 /* DOMAINNAME */ && len == 47 /* plen == 47 */ ) { char *sha1 = &buf[5]; char req[47]; dcc_t *d = NULL; dcc_t *D; int i; for (D=dccs; D; D = D->next) { jabber_dcc_t *p = D->priv; char *this_sha1; session_t *s; if (xstrncmp(D->uid, "xmpp:", 5)) continue; /* we skip not jabber dccs */ if (!p) { debug_error("[%s:%d] D->priv == NULL ?\n", __FILE__, __LINE__); continue; } /* we skip invalid dccs */ if (p->sfd != -1) { debug_error("[%s:%d] p->sfd != -1, already associated ?\n", __FILE__, __LINE__); continue; } /* we skip associated dccs */ if (p->protocol != JABBER_DCC_PROTOCOL_BYTESTREAMS) continue; /* we skip not BYTESTREAMS dccs */ for (s = sessions; s; s = s->next) { jabber_private_t *j = s->priv; char *fulluid; if (!s->connected) continue; if (!(session_check(s, 1, "jid"))) continue; fulluid = saprintf("%s/%s", s->uid+5, j->resource); /* XXX, take care about initiator && we */ /* D->type == DCC_SEND initiator -- we */ /* DCC_GET initiator -- D->uid+5 */ this_sha1 = jabber_dcc_digest(p->sid, fulluid, D->uid+5); debug_function("[JABBER_DCC_ACCEPTED] SHA1: %s THIS: %s (session: %s)\n", sha1, this_sha1, fulluid); if (!xstrcmp(sha1, this_sha1)) { d = D; p->sfd = fd; /* associate client FD... */ break; } xfree(fulluid); } } if (!d) { debug_error("[JABBER_DCC_ACCEPTED] SHA1 HASH NOT FOUND: %s\n", sha1); close(fd); return -1; } /* HEADER: */ req[0] = 0x05; /* SOCKS5 */ req[1] = 0x00; /* SUCC */ req[2] = 0x00; /* RSVD */ req[3] = 0x03; /* DOMAINNAME */ req[4] = 40; /* length of hash */ /* SHA1 HASH: */ for (i=0; i < 40; i++) req[5+i] = sha1[i]; /* PORT: */ req[45] = 0x00; req[46] = 0x00; /* LET'S SEND IWIL (OK, AUTH NOT IWIL) PACKET: */ write(fd, (char *) &req, sizeof(req)); watch_add(&jabber_plugin, fd, WATCH_NONE, jabber_dcc_handle_send, d); return -1; } return 0; } WATCHER(jabber_dcc_handle_accept) { struct sockaddr_in sin; int newfd; socklen_t sin_len = sizeof(sin); if (type) { close(fd); jabber_dcc_fd = -1; jabber_dcc_port = 0; return -1; } if ((newfd = accept(fd, (struct sockaddr *) &sin, &sin_len)) == -1) { debug_error("jabber_dcc_handle_accept() accept() FAILED (%s)\n", strerror(errno)); return -1; } debug_function("jabber_dcc_handle_accept() accept() fd: %d\n", newfd); watch_add(&jabber_plugin, newfd, WATCH_READ, jabber_dcc_handle_accepted, NULL); return 0; } /* zwraca watcha */ static watch_t *jabber_dcc_init(int port) { struct sockaddr_in sin; int fd; if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { debug_error("jabber_dcc_init() socket() FAILED (%s)\n", strerror(errno)); return NULL; } sin.sin_family = AF_INET; sin.sin_addr.s_addr = INADDR_ANY; sin.sin_port = g_htons(port); while (bind(fd, (struct sockaddr *) &sin, sizeof(struct sockaddr_in))) { debug_error("jabber_dcc_init() bind() port: %d FAILED (%s)\n", port, strerror(errno)); port++; if (port > 65535) { close(fd); return NULL; } sin.sin_port = g_htons(port); } if (listen(fd, 10)) { debug_error("jabber_dcc_init() listen() FAILED (%s)\n", strerror(errno)); close(fd); return NULL; } debug_function("jabber_dcc_init() SUCCESSED fd:%d port:%d\n", fd, port); jabber_dcc_port = port; jabber_dcc_fd = fd; return watch_add(&jabber_plugin, fd, WATCH_READ, jabber_dcc_handle_accept, NULL); } void jabber_dcc_close_handler(struct dcc_s *d) { jabber_dcc_t *p = d->priv; debug_error("jabber_dcc_close_handler() d->priv: 0x%x\n", d->priv); if (!p) return; if (!d->active && d->type == DCC_GET) { session_t *s = p->session; jabber_private_t *j; if (!s || !(j= session_private_get(s))) return; watch_write(j->send_watch, "Declined", d->uid+5, p->req); } d->priv = NULL; if (p) { if (p->protocol == JABBER_DCC_PROTOCOL_BYTESTREAMS) { /* XXX, free protocol-specified data */ } if (p->sfd != -1) close(p->sfd); if (p->fd) fclose(p->fd); xfree(p->req); xfree(p->sid); xfree(p); } else { debug_error("[jabber] jabber_dcc_close_handler() d->priv == NULL ?! wtf?\n"); } } dcc_t *jabber_dcc_find(const char *uin, /* without xmpp: */ const char *id, const char *sid) { #define DCC_RULE(x) (!xstrncmp(x->uid, "xmpp:", 5) && !xstrcmp(x->uid+5, uin)) dcc_t *d; if (!id && !sid) { debug_error("jabber_dcc_find() neither id nor sid passed.. Returning NULL\n"); return NULL; } for (d = dccs; d; d = d->next) { jabber_dcc_t *p = d->priv; if (DCC_RULE(d) && (!sid || !xstrcmp(p->sid, sid)) && (!id || !xstrcmp(p->req, id))) { debug_function("jabber_dcc_find() %s sid: %s id: %s founded: 0x%x\n", __(uin), __(sid), __(id), d); return d; } } debug_error("jabber_dcc_find() %s %s not founded. Possible abuse attempt?!\n", __(uin), __(sid)); return NULL; } QUERY(jabber_dcc_postinit) { static watch_t *dcc_watch = NULL; debug("jabber_dcc_postinit() dcc: %d fd: %d dcc_watch: 0x%x\n", jabber_dcc, jabber_dcc_fd, dcc_watch); if (jabber_dcc_fd == -1) dcc_watch = NULL; if (jabber_dcc && !dcc_watch) dcc_watch = jabber_dcc_init(JABBER_DEFAULT_DCC_PORT); else if (!jabber_dcc) { watch_free(dcc_watch); dcc_watch = NULL; } if (!dcc_watch) { jabber_dcc = 0; jabber_dcc_fd = -1; } return 0; } ekg2-0.4~pre+20120506.1/plugins/jabber/jabber_dcc.h000066400000000000000000000030171175142753400212730ustar00rootroot00000000000000#ifndef __JABBER_DCC_H #define __JABBER_DCC_H #define JABBER_DEFAULT_DCC_PORT 6000 /* XXX */ #include enum jabber_dcc_protocol_type_t { JABBER_DCC_PROTOCOL_UNKNOWN = 0, JABBER_DCC_PROTOCOL_BYTESTREAMS, /* http://www.jabber.org/jeps/jep-0065.html */ JABBER_DCC_PROTOCOL_IBB, /* http://www.jabber.org/jeps/jep-0047.html */ JABBER_DCC_PROTOCOL_WEBDAV, /* http://www.jabber.org/jeps/jep-0129.html */ /* DON'T IMPLEMENT IT UNTILL IT WILL BE STARNDARD DRAFT */ }; enum jabber_socks5_step_t { SOCKS5_UNKNOWN = 0, SOCKS5_CONNECT, SOCKS5_AUTH, SOCKS5_DATA, }; /* */ struct jabber_streamhost_item { char *jid; char *ip; int port; }; typedef struct { int validate; /* should be: JABBER_DCC_PROTOCOL_BYTESTREAMS */ enum jabber_socks5_step_t step; struct jabber_streamhost_item *streamhost; list_t streamlist; } jabber_dcc_bytestream_t; /* */ typedef struct { FILE *fd; int sfd; session_t *session; char *req; char *sid; enum jabber_dcc_protocol_type_t protocol; union { /* priv_data data based on protocol */ jabber_dcc_bytestream_t *bytestream; /* for JABBER_DCC_PROTOCOL_BYTESTREAMS */ void *other; /* XXX */ } priv_data; } jabber_dcc_t; dcc_t *jabber_dcc_find(const char *uin, const char *id, const char *sid); void jabber_dcc_close_handler(struct dcc_s *d); WATCHER(jabber_dcc_handle_recv); QUERY(jabber_dcc_postinit); extern int jabber_dcc; extern int jabber_dcc_port; extern char *jabber_dcc_ip; extern int jabber_dcc; #endif ekg2-0.4~pre+20120506.1/plugins/jabber/jabber_handlers.c000066400000000000000000001671031175142753400223440ustar00rootroot00000000000000/* * (C) Copyright 2003-2006 Wojtek Kaniewski * Tomasz Torcz * Leszek Krupiski * Piotr Pawow and other libtlen developers (http://libtlen.sourceforge.net/index.php?theme=teary&page=authors) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #ifndef NO_POSIX_SYSTEM #include #include #include #include #include /* dla jabber:iq:version */ #endif #include #include #include #include #include #include #ifndef NO_POSIX_SYSTEM #include #endif #ifdef HAVE_EXPAT_H # include #endif #ifdef __sun /* Solaris, thanks to Beeth */ #include #endif #include #include "jabber.h" #include "jabber_dcc.h" WATCHER_SESSION(jabber_handle_connect_ssl); /* jabber.c */ #define jabberfix(x,a) ((x) ? x : a) #define JABBER_HANDLER(x) static void x(session_t *s, xmlnode_t *n) JABBER_HANDLER(jabber_handle_message); JABBER_HANDLER(jabber_handle_iq); JABBER_HANDLER(jabber_handle_presence); static time_t jabber_try_xdelay(const char *stamp); static void jabber_session_connected(session_t *s); static void newmail_common(session_t *s); /** * xmlnode_find_child() * * Find child of @a node, with @a name * * @param n - node * @param name - name * * @return Pointer to node if such child was found, else NULL */ static xmlnode_t *xmlnode_find_child(xmlnode_t *n, const char *name) { if (!n || !n->children) return NULL; for (n = n->children; n; n = n->next) if (!xstrcmp(n->name, name)) return n; return NULL; } /** * xmlnode_find_child_xmlns() * * Find child of @a node, with @a name, which has 'xmlns' atts equal @a xmlns * * @param n - node * @param name - name * @param xmlns - xmlns * * @return Pointer to node if such child was found, else NULL */ static xmlnode_t *xmlnode_find_child_xmlns(xmlnode_t *n, const char *name, const char *xmlns) { if (!n || !n->children) return NULL; for (n = n->children; n; n = n->next) if (!xstrcmp(n->name, name) && !xstrcmp(n->xmlns, xmlns)) return n; return NULL; } /** * jabber_iq_auth_send() * * Send jabber:iq:auth with DIGEST or PLAINTEXT_PASSWORD
* It support both tlen auth, and jabber NON-SASL Authentication [XEP-0078]
* * @note XEP-0078: Non-SASL Authentication: http://www.xmpp.org/extensions/xep-0078.html * * @todo It's not really XEP-0078 cause ekg2 don't support it. But it this done that way.. I don't know any server with XEP-0078 functonality..
* I still rcv 'service-unavailable' or 'bad-request' ;(
* But it MUST be implemented for /session disable_sasl 1
* So it's just jabber:iq:auth for disable_sasl 2. * * @note Tlen Authentication was stolen from libtlen calc_passcode() with magic stuff (C) libtlen's developer and Piotr Pawow
* see: http://libtlen.sourceforge.net/ * * @param s - session to authenticate CANNOT BE NULL * @param username - username * @param passwd - password to hash or to escape * @param stream_id - id of stream. */ void jabber_iq_auth_send(session_t *s, const char *username, const char *passwd, const char *stream_id) { jabber_private_t *j = s->priv; const char *passwd2 = NULL; /* if set than jabber_digest() should be done on it. else plaintext_passwd Variable to keep password from @a password, or generated hash of password with magic constant [tlen] */ char *resource = tlenjabber_escape(j->resource);/* escaped resource name */ char *epasswd = NULL; /* temporary password [escaped, or hash], if needed for xfree() */ char *authpass; /* digest or plaintext_password */ const char *host = ""; /* stolen from libtlen function calc_passcode() Copyrighted by libtlen's developer and Piotr Pawow */ if (j->istlen) { int magic1 = 0x50305735, magic2 = 0x12345671, sum = 7; char z; while ((z = *passwd++) != 0) { if (z == ' ' || z == '\t') continue; magic1 ^= (((magic1 & 0x3f) + sum) * z) + (magic1 << 8); magic2 += (magic2 << 8) ^ magic1; sum += z; } magic1 &= 0x7fffffff; magic2 &= 0x7fffffff; passwd2 = epasswd = saprintf("%08x%08x", magic1, magic2); host = "tlen.pl"; } else if (session_int_get(s, "plaintext_passwd")) { epasswd = jabber_escape(passwd); } else passwd2 = passwd; authpass = (passwd2) ? saprintf("%s", jabber_digest(stream_id, passwd2, j->istlen)) : /* hash */ saprintf("%s", epasswd); /* plaintext */ watch_write(j->send_watch, "%s%s%s%s", j->server, host, username, authpass, resource); xfree(authpass); xfree(epasswd); xfree(resource); } /* XXX: temporary solution */ #define CHECK_CONNECT(connecting_, connected_, func) if ((j->sasl_connecting && s->connecting ? 2 : s->connecting) != connecting_ || s->connected != connected_) { \ debug_error("[jabber] %s:%d ASSERT_CONNECT connecting: %d+%d (shouldbe: %d) s->connected: %d (shouldbe: %d)\n", \ __FILE__, __LINE__, s->connecting, j->sasl_connecting, connecting_, s->connected, connected_); func; } #define CHECK_XMLNS(n, _xmlns, func) if (xstrcmp(n->xmlns, _xmlns)) { \ debug_error("[jabber] %s:%d ASSERT_XMLNS BAD XMLNS, IS: %s SHOULDBE: %s\n", __FILE__, __LINE__, n->xmlns, _xmlns); func; } JABBER_HANDLER(jabber_handle_stream_features) { jabber_private_t *j = s->priv; int use_sasl = !j->sasl_connecting && (session_int_get(s, "disable_sasl") != 2); int use_fjuczers = 0; /* bitmaska (& 1 -> session) (& 2 -> bind) */ int display_fjuczers = session_int_get(s, "display_server_features"); xmlnode_t *mech_node = NULL; if (display_fjuczers < 0) display_fjuczers = 0; if (display_fjuczers == 1 && session_int_get(s, "__features_displayed") == 1) display_fjuczers = 0; if (display_fjuczers) { xmlnode_t *ch; print("xmpp_feature_header", session_name(s), j->server, ""); for (ch = n->children; ch; ch = ch->next) { xmlnode_t *another; if (!xstrcmp(ch->name, "starttls")) { print("xmpp_feature", session_name(s), j->server, ch->name, ch->xmlns, "/session use_tls"); continue; } if (!xstrcmp(ch->name, "mechanisms")) { print("xmpp_feature", session_name(s), j->server, ch->name, ch->xmlns, "/session disable_sasl"); for (another = ch->children; another; another = another->next) { if (!xstrcmp(another->name, "mechanism")) { if (!xstrcmp(another->data, "DIGEST-MD5")) print("xmpp_feature_sub", session_name(s), j->server, "SASL", another->data, "Default"); else if (!xstrcmp(another->data, "PLAIN")) print("xmpp_feature_sub", session_name(s), j->server, "SASL", another->data, "/session plaintext_passwd"); else print("xmpp_feature_sub_unknown", session_name(s), j->server, "SASL", __(another->data), ""); } } continue; } #if 0 if (!xstrcmp(ch->name, "compression")) { print("xmpp_feature", session_name(s), j->server, ch->name, ch->xmlns, "/session use_compression method1,method2,.."); for (another = ch->children; another; another = another->next) { if (!xstrcmp(another->name, "method")) { if (!xstrcmp(another->data, "zlib")) print("xmpp_feature_sub", session_name(s), j->server, "COMPRESSION", another->data, "/session use_compression zlib"); else if (!xstrcmp(another->data, "lzw")) print("xmpp_feature_sub", session_name(s), j->server, "COMPRESSION", another->data, "/session use_compression lzw"); else print("xmpp_feature_sub_unknown", session_name(s), j->server, "COMPRESSION", __(another->data), ""); } } continue; } #endif if (!xstrcmp(ch->name, "session")) print("xmpp_feature", session_name(s), j->server, ch->name, ch->xmlns, "Manage session"); else if (!xstrcmp(ch->name, "bind")) print("xmpp_feature", session_name(s), j->server, ch->name, ch->xmlns, "Bind resource"); else if (!xstrcmp(ch->name, "register")) print("xmpp_feature", session_name(s), j->server, ch->name, ch->xmlns, "Register account using /register"); else print("xmpp_feature_unknown", session_name(s), j->server, ch->name, ch->xmlns); } print("xmpp_feature_footer", session_name(s), j->server); } for (n = n->children; n; n = n->next) { if (!xstrcmp(n->name, "starttls")) { #ifdef JABBER_HAVE_SSL CHECK_CONNECT(1, 0, continue) CHECK_XMLNS(n, "urn:ietf:params:xml:ns:xmpp-tls", continue) if (!j->using_ssl && session_int_get(s, "use_tls") == 1 && session_int_get(s, "use_ssl") == 0) { debug_function("[jabber] stream:features && TLS! let's rock.\n"); watch_write(j->send_watch, ""); return; } #endif } else if (!xstrcmp(n->name, "mechanisms") && !mech_node) { /* faster than xmlnode_find_child */ CHECK_XMLNS(n, "urn:ietf:params:xml:ns:xmpp-sasl", continue) mech_node = n->children; } else if (!xstrcmp(n->name, "session")) { CHECK_CONNECT(2, 0, continue) CHECK_XMLNS(n, "urn:ietf:params:xml:ns:xmpp-session", continue) use_fjuczers |= 1; } else if (!xstrcmp(n->name, "bind")) { CHECK_CONNECT(2, 0, continue) CHECK_XMLNS(n, "urn:ietf:params:xml:ns:xmpp-bind", continue) use_fjuczers |= 2; } else if (!xstrcmp(n->name, "compression")) { const char *tmp = session_get(s, "use_compression"); const char *method_comp = NULL; char *tmp2; xmlnode_t *method; if (!tmp) continue; CHECK_CONNECT(1, 0, continue) CHECK_XMLNS(n, "http://jabber.org/features/compress", continue) j->using_compress = JABBER_COMPRESSION_NONE; for (method = n->children; method; method = method->next) { if (!xstrcmp(method->name, "method")) { if (!xstrcmp(method->data, "zlib")) { #ifdef HAVE_LIBZ if ((tmp2 = xstrstr(tmp, "zlib")) && ((tmp2 < method_comp) || (!method_comp)) && (tmp2[4] == ',' || tmp2[4] == '\0')) { method_comp = tmp2; /* found more preferable method */ j->using_compress = JABBER_COMPRESSION_ZLIB_INIT; } #else debug_error("[jabber] compression... NO ZLIB support\n"); #endif } else if (!xstrcmp(method->data, "lzw")) { if ((tmp2 = xstrstr(tmp, "zlib")) && ((tmp2 < method_comp) || (!method_comp)) && (tmp2[3] == ',' || tmp2[3] == '\0')) { /* method_comp = tmp2 */ /* nieczynne */ /* j->using_compress = JABBER_COMPRESSION_LZW_INIT; */ } debug_error("[jabber] compression... sorry NO LZW support\n"); } else debug_error("[jabber] compression %s\n", __(method->data)); } else debug_error("[jabber] stream:features/compression %s\n", __(method->name)); } debug_function("[jabber] compression, method: %d\n", j->using_compress); if (!j->using_compress) continue; if (j->using_compress == JABBER_COMPRESSION_ZLIB_INIT) method_comp = "zlib"; else if (j->using_compress == JABBER_COMPRESSION_LZW_INIT) method_comp = "lzw"; else { debug_error("[jabber] BLAH [%s:%d] %s; %d\n", __FILE__, __LINE__, method_comp, j->using_compress); continue; } watch_write(j->send_watch, "%s", method_comp); return; } else { debug_error("[jabber] stream:features %s\n", __(n->name)); } } if (j->send_watch) j->send_watch->transfer_limit = -1; if (use_fjuczers & 2) { /* bind */ char *resource = jabber_escape(j->resource); watch_write(j->send_watch, "%s", j->id++, resource); xfree(resource); session_int_set(s, "__session_need_start", (use_fjuczers & 1)); } else /* else here, to avoid : 'stanza sent before session start' */ if (use_fjuczers & 1) { /* session */ watch_write(j->send_watch, "", j->id++); } if (j->sasl_connecting && !(use_fjuczers & 2)) { /* STRANGE, BUT MAYBE POSSIBLE? */ jabber_session_connected(s); } /* i think it's done here.. */ if (j->sasl_connecting && display_fjuczers) session_int_set(s, "__features_displayed", 1); JABBER_COMMIT_DATA(j->send_watch); if (use_sasl && mech_node) { enum { JABBER_SASL_AUTH_UNKNOWN, /* UNKNOWN */ JABBER_SASL_AUTH_PLAIN, /* PLAIN */ JABBER_SASL_AUTH_DIGEST_MD5, /* DIGEST-MD5 */ } auth_type = JABBER_SASL_AUTH_UNKNOWN; for (; mech_node; mech_node = mech_node->next) { if (!xstrcmp(mech_node->name, "mechanism")) { if (!xstrcmp(mech_node->data, "DIGEST-MD5")) auth_type = JABBER_SASL_AUTH_DIGEST_MD5; else if (!xstrcmp(mech_node->data, "PLAIN")) { if ((session_int_get(s, "plaintext_passwd"))) { auth_type = JABBER_SASL_AUTH_PLAIN; break; /* jesli plaintext jest preferred - wychodzimy */ } /* ustaw tylko wtedy gdy nie ma ustawionego, wolimy MD5 */ if (auth_type == JABBER_SASL_AUTH_UNKNOWN) auth_type = JABBER_SASL_AUTH_PLAIN; } else debug_error("[jabber] SASL, unk mechanism: %s\n", __(mech_node->data)); } else debug_error("[jabber] SASL mechanisms: %s\n", mech_node->name); } if (auth_type != JABBER_SASL_AUTH_UNKNOWN) j->sasl_connecting = 1; switch (auth_type) { string_t str; char *encoded; case JABBER_SASL_AUTH_DIGEST_MD5: debug_function("[jabber] SASL chosen: JABBER_SASL_AUTH_DIGEST_MD5\n"); watch_write(j->send_watch, ""); break; case JABBER_SASL_AUTH_PLAIN: debug_function("[jabber] SASL chosen: JABBER_SASL_AUTH_PLAIN\n"); str = string_init(NULL); string_append_raw(str, "\0", 1); string_append_n(str, s->uid+5, xstrchr(s->uid+5, '@')-s->uid-5); string_append_raw(str, "\0", 1); string_append(str, session_get(s, "password")); /* XXX, haslo rekodowac na UTF-8 */ encoded = base64_encode(str->str, str->len); watch_write(j->send_watch, "%s", encoded); xfree(encoded); string_free(str, 1); break; default: debug_error("[jabber] SASL auth_type: UNKNOWN!!!, disconnecting from host.\n"); j->parser = NULL; jabber_handle_disconnect(s, "We tried to auth using SASL but none of method supported by server we know. " "Check __debug window and supported SASL server auth methods and sent them to ekg2 devs. " "Temporary you can turn off SASL auth using by setting disable_sasl to 1 or 2. " "/session disable_sasl 2", EKG_DISCONNECT_FAILURE); break; } } } JABBER_HANDLER(jabber_handle_compressed) { jabber_private_t *j = s->priv; CHECK_CONNECT(1, 0, return) CHECK_XMLNS(n, "http://jabber.org/protocol/compress", return) /* REINITIALIZE STREAM WITH COMPRESSION TURNED ON */ switch (j->using_compress) { case JABBER_COMPRESSION_NONE: break; case JABBER_COMPRESSION_ZLIB_INIT: j->using_compress = JABBER_COMPRESSION_ZLIB; break; case JABBER_COMPRESSION_LZW_INIT: j->using_compress = JABBER_COMPRESSION_LZW; break; default: debug_error("[jabber] invalid j->use_compression (%d) state..\n", j->using_compress); j->using_compress = JABBER_COMPRESSION_NONE; } if (j->using_compress == JABBER_COMPRESSION_NONE) { debug_error("[jabber] j->using_compress == JABBER_COMPRESSION_NONE but, compressed stanza?\n"); return; } j->parser = jabber_parser_recreate(NULL, XML_GetUserData(j->parser)); j->send_watch->handler = jabber_handle_write; watch_write(j->send_watch, "", j->server); } JABBER_HANDLER(jabber_handle_challenge) { jabber_private_t *j = s->priv; char *data; char **arr; int i; char *realm = NULL; char *rspauth = NULL; char *nonce = NULL; CHECK_CONNECT(2, 0, return) CHECK_XMLNS(n, "urn:ietf:params:xml:ns:xmpp-sasl", return) if (!n->data) { debug_error("[jabber] challenge, no data.. (XXX?) disconnecting from host.\n"); return; } /* decode && parse challenge data */ data = base64_decode(n->data); debug_error("[jabber] PARSING challenge (%s): \n", data); arr = array_make(data, "=,", 0, 1, 1); /* maybe we need to change/create another one parser... i'm not sure. please notify me, I'm lazy, sorry */ /* for chrome.pl and jabber.autocom.pl it works */ xfree(data); /* check parsed data... */ i = 0; while (arr[i]) { debug_error("[%d] %s: %s\n", i / 2, arr[i], __(arr[i+1])); if (!arr[i+1]) { debug_error("Parsing var<=>value failed, NULL....\n"); g_strfreev(arr); j->parser = NULL; jabber_handle_disconnect(s, "IE, Current SASL support for ekg2 cannot handle with this data, sorry.", EKG_DISCONNECT_FAILURE); return; } { char *tmp = strip_spaces(arr[i]); if (!xstrcmp(tmp, "realm")) realm = arr[i+1]; else if (!xstrcmp(tmp, "rspauth")) rspauth = arr[i+1]; else if (!xstrcmp(tmp, "nonce")) nonce = arr[i+1]; } i++; if (arr[i]) i++; } if (rspauth) { const char *tmp = session_get(s, "__sasl_excepted"); if (!xstrcmp(tmp, rspauth)) { debug_function("[jabber] KEYS MATCHED, THX FOR USING SASL SUPPORT IN EKG2.\n"); watch_write(j->send_watch, ""); } else { debug_error("[jabber] RSPAUTH BUT KEYS DON'T MATCH!!! IS: %s EXCEPT: %s, DISCONNECTING\n", __(rspauth), __(tmp)); j->parser = NULL; jabber_handle_disconnect(s, "IE, SASL RSPAUTH DOESN'T MATCH!!", EKG_DISCONNECT_FAILURE); } session_set(s, "__sasl_excepted", NULL); } else { char *username = xstrndup(s->uid+5, xstrchr(s->uid+5, '@')-s->uid-5); const char *password = session_get(s, "password"); string_t str = string_init(NULL); /* text to encode && sent */ char *encoded; /* BASE64 response text */ char tmp_cnonce[32]; char *cnonce; char *xmpp_temp; char *auth_resp; if (!realm) realm = j->server; for (i=0; i < sizeof(tmp_cnonce); i++) tmp_cnonce[i] = (char) (256.0*rand()/(RAND_MAX+1.0)); /* generate random number using high-order bytes man 3 rand() */ cnonce = base64_encode(tmp_cnonce, sizeof(tmp_cnonce)); xmpp_temp = saprintf(":xmpp/%s", realm); auth_resp = jabber_challenge_digest(username, password, nonce, cnonce, xmpp_temp, realm); session_set(s, "__sasl_excepted", auth_resp); xfree(xmpp_temp); xmpp_temp = saprintf("AUTHENTICATE:xmpp/%s", realm); auth_resp = jabber_challenge_digest(username, password, nonce, cnonce, xmpp_temp, realm); xfree(xmpp_temp); string_append(str, "username=\""); string_append(str, username); string_append_c(str, '\"'); string_append(str, ",realm=\""); string_append(str, realm); string_append_c(str, '\"'); string_append(str, ",nonce=\""); string_append(str, nonce); string_append_c(str, '\"'); string_append(str, ",cnonce=\""); string_append(str, cnonce); string_append_c(str, '\"'); string_append(str, ",nc=00000001"); string_append(str, ",digest-uri=\"xmpp/"); string_append(str, realm); string_append_c(str, '\"'); string_append(str, ",qop=auth"); string_append(str, ",response="); string_append(str, auth_resp); string_append(str, ",charset=utf-8"); encoded = base64_encode(str->str, str->len); /* XXX base64_encoded() CHANGED!! str->len+1 ? */ watch_write(j->send_watch, "%s", encoded); xfree(encoded); string_free(str, 1); xfree(username); xfree(cnonce); } g_strfreev(arr); } JABBER_HANDLER(jabber_handle_proceed) { jabber_private_t *j = s->priv; CHECK_CONNECT(1, 0, return) if (!xstrcmp(n->xmlns, "urn:ietf:params:xml:ns:xmpp-tls")) { #ifdef JABBER_HAVE_SSL debug_function("[jabber] proceed urn:ietf:params:xml:ns:xmpp-tls TLS let's rock\n"); /* XXX HERE WE SHOULD DISABLE RECV_WATCH && (SEND WATCH TOO?) */ /* mg: using disabled send watch in jabber_handle_stream() to mark that TLS is in progress */ j->send_watch->type = WATCH_NONE; jabber_handle_connect_ssl(-1, j->fd, WATCH_NONE, s); #else debug_error("[jabber] proceed + urn:ietf:params:xml:ns:xmpp-tls but jabber compilated without ssl support?\n"); #endif } else debug_error("[jabber] proceed what's that xmlns: %s ?\n", n->xmlns); } JABBER_HANDLER(jabber_handle_stream_error) { jabber_private_t *j = s->priv; xmlnode_t *text = xmlnode_find_child(n, "text"); char *text2 = NULL; if (text && text->data) text2 = jabber_unescape(text->data); /* here we should use: EKG_DISCONNECT_FORCED, but because of noautoreconnection and noreason, we'll use: EKG_DISCONNECT_NETWORK */ j->parser = NULL; jabber_handle_disconnect(s, text2 ? text2 : n->children ? n->children->name : "stream:error XXX", EKG_DISCONNECT_NETWORK); xfree(text2); } JABBER_HANDLER(jabber_handle_success) { jabber_private_t *j = s->priv; CHECK_CONNECT(2, 0, return) CHECK_XMLNS(n, "urn:ietf:params:xml:ns:xmpp-sasl", return) j->parser = jabber_parser_recreate(NULL, XML_GetUserData(j->parser)); /* here could be passed j->parser to jabber_parser_recreate() but unfortunetly expat makes SIGSEGV */ watch_write(j->send_watch, "", j->server); } JABBER_HANDLER(jabber_handle_failure) { jabber_private_t *j = s->priv; char *reason; CHECK_CONNECT(2, 0, return) CHECK_XMLNS(n, "urn:ietf:params:xml:ns:xmpp-sasl", return) reason = n->children ? n->children->name : NULL; debug_function("[jabber] failure n->child: 0x%x n->child->name: %s\n", n->children, __(reason)); /* XXX here, think about nice reasons */ if (!reason) reason = "(SASL) GENERIC FAILURE"; else if (!xstrcmp(reason, "temporary-auth-failure")) reason = "(SASL) TEMPORARY AUTH FAILURE"; else debug_error("[jabber] UNKNOWN reason: %s\n", reason); j->parser = NULL; jabber_handle_disconnect(s, reason, EKG_DISCONNECT_FAILURE); } struct jabber_generic_handler { const char *name; void (*handler)(session_t *s, xmlnode_t *n); }; static const struct jabber_generic_handler jabber_handlers[] = { { "message", jabber_handle_message }, { "presence", jabber_handle_presence }, { "iq", jabber_handle_iq }, /* XXX: shall below two have some kind of namespace? (previously they were 'stream:'-prefixed) */ { "features", jabber_handle_stream_features }, { "error", jabber_handle_stream_error }, { "challenge", jabber_handle_challenge }, { "compressed", jabber_handle_compressed }, { "proceed", jabber_handle_proceed }, { "success", jabber_handle_success }, { "failure", jabber_handle_failure }, { NULL, NULL } }; #include "jabber_handlers_tlen.inc" void jabber_handle(void *data, xmlnode_t *n) { session_t *s = (session_t *) data; jabber_private_t *j; const struct jabber_generic_handler *tmp; if (!s || !(j = s->priv) || !n) { debug_error("jabber_handle() invalid parameters\n"); return; } /* jabber handlers */ for (tmp = jabber_handlers; tmp->name; tmp++) { if (!xstrcmp(n->name, tmp->name)) { tmp->handler(s, n); return; } } if (!j->istlen) { debug_error("[jabber] what's that: %s ?\n", n->name); return; } /* tlen handlers */ for (tmp = tlen_handlers; tmp->name; tmp++) { if (!xstrcmp(n->name, tmp->name)) { tmp->handler(s, n); return; } } debug_error("[tlen] what's that: %s ?\n", n->name); } JABBER_HANDLER(jabber_handle_message) { jabber_private_t *j = s->priv; xmlnode_t *nerr = xmlnode_find_child(n, "error"); xmlnode_t *nbody = xmlnode_find_child(n, "body"); xmlnode_t *nsubject = NULL; xmlnode_t *nthread = NULL; xmlnode_t *nhtml = NULL; xmlnode_t *xitem; const char *from = jabber_attr(n->atts, "from"); char *x_encrypted = NULL; char *juid = tlenjabber_unescape(from); /* was tmp */ char *uid; time_t bsent = 0; string_t body; int new_line = 0; /* if there was headlines do we need to display seperator between them and body? */ int class = -1; int composing = 0; uid = tlenjabber_uid(juid); xfree(juid); if (nerr) { char *ecode = jabber_attr(nerr->atts, "code"); char *etext = jabber_unescape(nerr->data); const char *recipient = get_nickname(s, uid); if (nbody && nbody->data) { char *tmp2 = jabber_unescape(nbody->data); char *mbody = xstrndup(tmp2, 15); xstrtr(mbody, '\n', ' '); print_warning(uid, s, "jabber_msg_failed_long", recipient, ecode, etext, mbody); xfree(mbody); xfree(tmp2); } else print_warning(uid, s, "jabber_msg_failed", recipient, ecode, etext); xfree(etext); xfree(uid); return; } /* */ if (j->istlen) /* disable typing notify, tlen protocol doesn't send info about it (?) XXX */ protocol_xstate_emit(s, uid, 0, EKG_XSTATE_TYPING); body = string_init(""); for (xitem = n->children; xitem; xitem = xitem->next) { if (!xstrcmp(xitem->name, "x")) { const char *ns = xitem->xmlns; if (!xstrcmp(ns, "jabber:x:encrypted")) { /* JEP-0027 */ x_encrypted = xstrdup(xitem->data); char *error = NULL; if (!(x_encrypted = jabber_openpgp(s, uid, JABBER_OPENGPG_DECRYPT, x_encrypted, NULL, &error))) { string_append(body, "Encrypted message but error: "); string_append(body, error); string_append_c(body, '\n'); new_line = 1; } xfree(error); } else if (!xstrncmp(ns, "jabber:x:event", 14)) { int acktype = 0; /* bitmask: 2 - queued ; 1 - delivered */ int isack; if (xmlnode_find_child(xitem, "delivered")) acktype |= 1; /* delivered */ if (xmlnode_find_child(xitem, "offline")) acktype |= 2; /* queued */ if (xmlnode_find_child(xitem, "composing")) acktype |= 4; /* composing */ isack = (acktype & 3); /* jesli jest body, to mamy do czynienia z prosba o potwierdzenie */ if (nbody && isack) { char *id = jabber_attr(n->atts, "id"); const int our_status = session_status_get(s); if (j->send_watch) j->send_watch->transfer_limit = -1; watch_write(j->send_watch, "", from); if (our_status == EKG_STATUS_INVISIBLE) { watch_write(j->send_watch, ""); } else { if (acktype & 1) watch_write(j->send_watch, ""); if (acktype & 2) watch_write(j->send_watch, ""); }; watch_write(j->send_watch, "%s", id); JABBER_COMMIT_DATA(j->send_watch); } /* jeli body nie ma, to odpowiedz na nasza prosbe */ if (!nbody && isack) { int __status = ((acktype & 1) ? EKG_ACK_DELIVERED : (acktype & 2) ? EKG_ACK_QUEUED : EKG_ACK_UNKNOWN); protocol_message_ack_emit(s, uid, NULL, __status); } if (!(composing & 2)) /* '& 2' means we've already got chatstate (higher prio) */ composing = 1 + (acktype & 4); /* jabber:x:event */ } else if (!xstrncmp(ns, "jabber:x:oob", 12)) { xmlnode_t *xurl; xmlnode_t *xdesc; if ((xurl = xmlnode_find_child(xitem, "url"))) { string_append(body, "URL: "); string_append(body, xurl->data); if ((xdesc = xmlnode_find_child(xitem, "desc"))) { string_append(body, " ("); string_append(body, xdesc->data); string_append(body, ")"); } string_append(body, "\n"); new_line = 1; } /* jabber:x:oob */ } else if (!xstrncmp(ns, "jabber:x:delay", 14)) { bsent = jabber_try_xdelay(jabber_attr(xitem->atts, "stamp")); #if 0 /* XXX, fjuczer? */ if (nazwa_zmiennej_do_formatowania_czasu) { /* some people don't have time in formats... and if we really do like in emails (first headlines than body) so display it too.. */ stuct tm *tm = localtime(&bsent); char buf[100]; string_append(body, "Sent: "); if (!strftime(buf, sizeof(buf), nazwa_zmiennej_do_formatowania_czasu, tm) string_append(body, ekg_itoa(bsent); /* if too long display seconds since the Epoch */ else string_append(body, buf); /* otherwise display formatted time */ new_line = 1; } #endif } else debug_error("[JABBER, MESSAGE]: \n", __(ns)); /* x */ } else if (!xstrcmp(xitem->xmlns, "http://jabber.org/protocol/chatstates")) { composing = 3; /* disable + higher prio */ if (!xstrcmp(xitem->name, "active")) { } else if (!xstrcmp(xitem->name, "composing")) { composing = 7; /* enable + higher prio */ } else if (!xstrcmp(xitem->name, "paused")) { } else if (!xstrcmp(xitem->name, "inactive")) { } else if (!xstrcmp(xitem->name, "gone")) { print_info(uid, s, "jabber_gone", session_name(s), get_nickname(s, uid)); } else debug_error("[JABBER, MESSAGE]: INVALID CHATSTATE: %s\n", xitem->name); /* chatstate */ } else if (!xstrcmp(xitem->name, "subject")) { nsubject = xitem; } else if (!xstrcmp(xitem->name, "thread")) { nthread = xitem; /* subject */ } else if (!xstrcmp(xitem->name, "body")) { } else if (!xstrcmp(xitem->name, "html") && !xstrcmp(xitem->xmlns, "http://jabber.org/protocol/xhtml-im")) { /* xep-0071 */ xmlnode_t *tmp; if (!((tmp = xmlnode_find_child(xitem, "body")))) debug_error("[JABBER, MESSAGE, XEP0071]: w/o !\n"); else { if (xstrcmp(tmp->xmlns, "http://www.w3.org/1999/xhtml")) debug_error("[JABBER, MESSAGE, XEP0071]: ?\n", tmp->xmlns); nhtml = xitem; } } else if (j->istlen && !xstrcmp(xitem->name, "no")) class = EKG_MSGCLASS_SYSTEM; else debug_error("[JABBER, MESSAGE]: <%s\n", xitem->name); } const char *type = jabber_attr(n->atts, "type"); if (class == -1) class = (!xstrcmp(type, "chat") || !xstrcmp(type, "groupchat") ? EKG_MSGCLASS_CHAT : EKG_MSGCLASS_MESSAGE); const int nonthreaded = (!nthread || !nthread->data); const int hassubject = (nsubject && nsubject->data); if (hassubject) { /* we need to linearize this earlier */ char *tmp = nsubject->data; while ((tmp = xstrchr(tmp, '\n'))) { if (*(tmp+1) == '\0') { *tmp = '\0'; break; /* we really don't need to search again */ } else *tmp = ' '; } } if ((class == EKG_MSGCLASS_MESSAGE) /* conversations only with messages */ && (!nonthreaded /* either if we've got thread */ || ((nbody || nsubject) && (session_int_get(s, "allow_add_reply_id") > 1)) /* or we're allowing to use conversations for non-threaded messages */ )) { jabber_conversation_t *thr; int i = jabber_conversation_find(j, uid, (nonthreaded && hassubject ? nsubject->data : NULL), (nonthreaded ? NULL : nthread->data), &thr, (session_int_get(s, "allow_add_reply_id") > 0)); if (nonthreaded) nthread = xmalloc(sizeof(xmlnode_t)); /* ugly hack, to make non-threaded reply work */ if (thr) { /* XXX, do it nicer */ xfree(nthread->data); nthread->data = saprintf("#%d", i); debug("[jabber, message] thread: %s -> #%d\n", thr->thread, i); } if (!(nsubject && nsubject->data)) { string_append(body, (thr ? "Reply-ID: " : "Thread: ")); string_append(body, nthread->data); string_append(body, "\n"); if (nonthreaded) xfree(nthread); new_line = 1; } else if (thr) { xfree(thr->subject); thr->subject = xstrdup(nsubject->data); /* we should store newest message subject, not first */ } } if (hassubject) { string_append(body, "Subject: "); string_append(body, nsubject->data); if (nthread && nthread->data) { string_append(body, " ["); string_append(body, nthread->data); string_append(body, "]"); if (!nthread->name) /* this means we're using above hack */ xfree(nthread); } string_append(body, "\n"); new_line = 1; } if (new_line) string_append(body, "\n"); /* let's seperate headlines from message */ if (x_encrypted) string_append(body, x_encrypted); /* encrypted message */ else if (nbody) string_append(body, nbody->data); /* unecrypted message */ /* 'composing' is quite special variable: * 1st bit determines whether we should send any update, * if its' off, then other should be too; * 2nd bit determines whether we've got chatstate-based update, * if its' on, then jabber:x:event can't replace the state; * 3rd bit determines whether the is on */ if (composing) { if (!nbody && (composing & 4)) protocol_xstate_emit(s, uid, EKG_XSTATE_TYPING, 0); else protocol_xstate_emit(s, uid, 0, EKG_XSTATE_TYPING); } if (nbody || nsubject) { int secure = (x_encrypted != NULL); time_t sent = bsent; char *text = tlenjabber_unescape(body->str); guint32 *format= jabber_msg_format(text, nhtml); if (!sent) sent = time(NULL); debug_function("[jabber,message] type = %s\n", __(type)); if (!xstrcmp(type, "groupchat")) { char *tuid = xstrchr(uid, '/'); /* temporary */ char *uid2 = (tuid) ? xstrndup(uid, tuid-uid) : xstrdup(uid); /* muc room */ char *nick = (tuid) ? xstrdup(tuid+1) : NULL; /* nickname */ newconference_t *c = newconference_find(s, uid2); int isour = (c && !xstrcmp(c->priv_data, nick)) ? 1 : 0; /* is our message? */ char *formatted; userlist_t *u; /* jesli (bsent != 0) wtedy mamy do czynienia z backlogiem */ class |= EKG_NO_THEMEBIT; /* XXX: maybe some core-side support instead of forcing our themes? */ if (nick) { /* XXX !!! */ char attr[2] = { ' ', 0 }; const int is_me = !xstrncmp(text, "/me ", 4); if ((u = userlist_find_u(&(c->participants), nick))) { jabber_userlist_private_t *up = jabber_userlist_priv_get(u); /* glupie, trzeba doszlifowac */ if (!xstrcmp(up->aff, "owner")) attr[0] = '@'; else if (!xstrcmp(up->aff, "admin")) attr[0] = '@'; else if (!xstrcmp(up->role, "moderator")) attr[0] = '%'; else attr[0] = ' '; } else { debug_error("[MUC, MESSAGE] userlist_find_u(%s) failed\n", nick); return; } formatted = format_string(format_find( is_me ? ( isour ? "jabber_muc_me_sent" : "jabber_muc_me" ) : ( isour ? "jabber_muc_send" : "jabber_muc_recv")), session_name(s), uid2, nick, is_me ? text+4 : text, attr); } else { formatted = format_string(format_find("jabber_muc_notice"), session_name(s), uid+5, text); } protocol_message_emit(s, uid, NULL, formatted, format, sent, class, NULL, EKG_NO_BEEP, secure); xfree(uid2); xfree(nick); xfree(formatted); xfree(format); } else { protocol_message_emit(s, uid, NULL, text, format, sent, class, NULL, EKG_TRY_BEEP, secure); } xfree(text); } xfree(x_encrypted); string_free(body, 1); xfree(uid); } /* */ /* idea and some code copyrighted by Marek Marczykowski xmpp:marmarek@jabberpl.org */ /* handlue next) { if (!xstrcmp(node->name, "title")) { char *title = jabber_unescape(node->data); print("jabber_form_title", session_name(s), uid, title); xfree(title); } else if (!xstrcmp(node->name, "instructions")) { char *inst = jabber_unescape(node->data); print("jabber_form_instructions", session_name(s), uid, inst); xfree(inst); } else if (!xstrcmp(node->name, "field")) { xmlnode_t *child; char *label = jabber_unescape(jabber_attr(node->atts, "label")); char *type = jabber_unescape(jabber_attr(node->atts, "type")); char *var = jabber_unescape(jabber_attr(node->atts, "var")); char *def_option = NULL; string_t sub = NULL; int subcount = 0; int isreq = 0; /* -1 - optional; 1 - required */ /* ?WO? XEP-0004 tells nothing about optional values ??? */ if (!fieldcount) print("jabber_form_command", session_name(s), uid, command, param); for (child = node->children; child; child = child->next) { if (!xstrcmp(child->name, "required")) isreq = 1; else if (!xstrcmp(child->name, "value")) { xfree(def_option); def_option = jabber_unescape(child->data); } else if (!xstrcmp(child->name, "option")) { xmlnode_t *tmp; char *opt_value = jabber_unescape( (tmp = xmlnode_find_child(child, "value")) ? tmp->data : NULL); char *opt_label = jabber_unescape(jabber_attr(child->atts, "label")); char *fritem; fritem = format_string(format_find("jabber_form_item_val"), session_name(s), uid, opt_value, opt_label); if (!sub) sub = string_init(fritem); else string_append(sub, fritem); xfree(fritem); /* print("jabber_form_item_sub", session_name(s), uid, opt_label, opt_value); */ /* debug("[[option]] [value] %s [label] %s\n", opt_value, opt_label); */ xfree(opt_value); xfree(opt_label); subcount++; if (!(subcount % 4)) string_append(sub, "\n\t"); } else debug_error("[jabber] wtf? FIELD->CHILD: %s\n", child->name); } if (!(xstrcmp(type, "fixed"))) print("jabber_form_description", session_name(s), uid, def_option); else if (!(xstrcmp(type, "hidden"))) print("jabber_form_hidden", session_name(s), uid, label, var, def_option); else print("jabber_form_item", session_name(s), uid, label, var, def_option, isreq == -1 ? "X" : isreq == 1 ? "V" : " ", type); if (sub && sub->len > 1) { int len = sub->len; if (sub->str[len-1] == '\t' && sub->str[len-2] == '\n') sub->str[len-2] = 0; print("jabber_form_item_sub", session_name(s), uid, sub->str); string_free(sub, 1); } fieldcount++; xfree(var); xfree(type); xfree(label); } } if (!fieldcount) print("jabber_form_command", session_name(s), uid, command); print("jabber_form_end", session_name(s), uid, command, param); } /* handluje next) { if (!xstrcmp(form->name, "field")) { int quiet = 0; const char *vartype = jabber_attr(form->atts, "type"); const char *varname = jabber_attr(form->atts, "var"); char *value = jabber_unescape(form->children ? form->children->data : NULL); char **tmp; if (FORM_TYPE && (!xstrcmp(varname, "FORM_TYPE") && !xstrcmp(vartype, "hidden") && !xstrcmp(value, FORM_TYPE))) { valid = 1; quiet = 1; } if ((tmp = (char **) jabber_attr(atts, varname))) { if (!alloc) { xfree(*tmp); *tmp = value; } /* here is char ** */ else { xfree((char *) tmp); tmp = (char **) value; } /* here is char * */ value = NULL; } else if (alloc) { atts = (char **) xrealloc(atts, sizeof(char *) * (count + 3)); atts[count] = xstrdup(varname); atts[count+1] = value; /* here is char * */ atts[count+2] = NULL; count += 2; value = NULL; } else if (!quiet) debug_error("JABBER, RC, FORM_TYPE: %s ATTR NOT IN ATTS: %s (SOMEONE IS DOING MESS WITH FORM_TYPE?)\n", FORM_TYPE, varname); xfree(value); } } if (alloc) (*(va_arg(ap, char ***))) = atts; va_end(ap); return valid; } /* handlue next) { if (!xstrcmp(form->name, "title")) { char *title = jabber_unescape(form->data); print("jabber_form_title", session_name(s), uid, title); print_end = 1; xfree(title); } else if (!xstrcmp(form->name, "item")) { xmlnode_t *q; print("jabber_form_item_beg", session_name(s), uid); for (q = form->children; q; q = q->next) { if (!xstrcmp(q->name, "field")) { xmlnode_t *temp; char *var = jabber_attr(q->atts, "var"); char *tmp = jabber_attr(labels, var); char *val = ((temp = xmlnode_find_child(q, "value"))) ? jabber_unescape(temp->data) : NULL; print("jabber_form_item_plain", session_name(s), uid, tmp ? tmp : var, var, val); xfree(val); } } print("jabber_form_item_end", session_name(s), uid); } else if (!xstrcmp(form->name, "reported")) { xmlnode_t *q; for (q = form->children; q; q = q->next) { if (!xstrcmp(q->name, "field")) { labels = (char **) xrealloc(labels, (sizeof(char *) * ((labels_count+1) * 2)) + 1); labels[labels_count*2] = xstrdup(jabber_attr(q->atts, "var")); labels[labels_count*2+1] = jabber_unescape(jabber_attr(q->atts,"label")); labels[labels_count*2+2] = NULL; labels_count++; } } } else if (!xstrcmp(form->name, "field")) { xmlnode_t *temp; char *var = jabber_attr(form->atts, "var"); char *label = jabber_unescape(jabber_attr(form->atts, "label")); char *val = jabber_unescape(((temp = xmlnode_find_child(form, "value"))) ? temp->data : NULL); print("jabber_privacy_list_item" /* XXX */, session_name(s), uid, label ? label : var, val); xfree(label); xfree(val); } else debug_error("jabber_handle_xmldata_result() name: %s\n", form->name); } if (print_end) print("jabber_form_end", session_name(s), uid, ""); g_strfreev(labels); } struct jabber_iq_generic_handler { const char *name; const char *xmlns; void (*handler)(session_t *s, xmlnode_t *n, const char *from, const char *id); }; static const struct jabber_iq_generic_handler *jabber_iq_find_handler(const struct jabber_iq_generic_handler *items, const char *type, const char *xmlns) { const struct jabber_iq_generic_handler *tmp = items; while (tmp->handler) { int matched = !xstrcmp(type, tmp->name); do { if (matched && !xstrcmp(tmp->xmlns, xmlns)) return tmp; tmp++; } while (!tmp->name); if (matched) break; } return NULL; } #include "jabber_handlers_iq_error.inc" #include "jabber_handlers_iq_get.inc" #include "jabber_handlers_iq_result.inc" JABBER_HANDLER(jabber_handle_iq) { jabber_private_t *j = s->priv; const char *atype= jabber_attr(n->atts, "type"); const char *id = jabber_attr(n->atts, "id"); const char *from = jabber_attr(n->atts, "from"); jabber_iq_type_t type = JABBER_IQ_TYPE_NONE; const struct jabber_iq_generic_handler *callbacks; xmlnode_t *q; if (!xstrcmp(atype, "get")) type = JABBER_IQ_TYPE_GET; else if (!xstrcmp(atype, "set")) type = JABBER_IQ_TYPE_SET; else if (!xstrcmp(atype, "result")) type = JABBER_IQ_TYPE_RESULT; else if (!xstrcmp(atype, "error")) type = JABBER_IQ_TYPE_ERROR; else if (!atype) { debug_error("[jabber] without type!\n"); return; } if (type == JABBER_IQ_TYPE_RESULT || type == JABBER_IQ_TYPE_ERROR) { list_t l; char *uid = tlenjabber_unescape(from); /* XXX: really worth unescaping? */ /* XXX, do sprawdzenia w RFC/ napisania maila do gosci od XMPP. * czy jesli nie mamy nic w from, to mamy zakladac ze w from jest 'nasz.jabber.server' (j->server) */ /* XXX, note: we temporary pass here: 'from' instead of unescaped 'uid'. */ for (l = j->iq_stanzas; l; l = l->next) { jabber_stanza_t *st = l->data; if (!xstrcmp(st->id, id)) { /* SECURITY NOTE: for instance, mcabber in version 0.9.5 doesn't check from and id of iq is always increment by one ^^ */ if ( (!xstrcmp(st->to, uid) /* || jakas_iwil_zmienna [np: bypass_FROM_checkin_from_iq] */) || !xstrcmp(st->xmlns, "jabber:iq:private") /* makeing security HOLE for jabber:iq:private */ || !xstrcmp(st->xmlns, "jabber:iq:privacy") /* makeing security HOLE for jabber:iq:privacy */ ) { if (type == JABBER_IQ_TYPE_RESULT) { if ((q = xmlnode_find_child_xmlns(n, st->type, st->xmlns))) { debug("[jabber] Executing handler id: %s <%s xmlns='%s' 0x%x\n", st->id, st->type, st->xmlns, st->handler); st->handler(s, q, from, id); } else { debug_error("[jabber] Warning, [<%s xmlns='%s'] Not found, calling st->error: %x\n", st->type, st->xmlns, st->error); st->error(s, NULL, from, id); } } else { q = xmlnode_find_child(n, "error"); /* WARN: IT CAN BE NULL, jabber_iq_error_string() handles it. */ debug("[jabber] Executing error handler id: %s q: %x <%s xmlns='%s' 0x%x%x\n", st->id, q, st->type, st->xmlns, st->error); st->error(s, q, from, id); } jabber_stanza_freeone(j, st); xfree(uid); return; } debug_error("[jabber] Security warning: recved iq from invalid source %s vs %s\n", __(st->to), __(uid)); break; } } xfree(uid); } switch (type) { case JABBER_IQ_TYPE_RESULT: callbacks = jabber_iq_result_handlers; break; case JABBER_IQ_TYPE_SET: callbacks = jabber_iq_set_handlers; break; case JABBER_IQ_TYPE_GET: callbacks = jabber_iq_get_handlers; break; case JABBER_IQ_TYPE_ERROR: jabber_handle_iq_error_generic_old(s, n, from, id); return; case JABBER_IQ_TYPE_NONE: debug_error("[jabber] wtf iq type: %s\n", atype); return; default: /* never here */ /* protect from gcc warning: * jabber_handlers.c:1271: warning: 'callbacks' may be used uninitialized in this function */ return; } if (!xstrcmp(id, "auth")) { if (type == JABBER_IQ_TYPE_RESULT) { jabber_session_connected(s); } else { /* Can someone look at that, i don't undestand for what is it here... */ s->last_conn = time(NULL); s->connecting = 0; } } if (!xstrncmp(id, "passwd", 6)) { if (type == JABBER_IQ_TYPE_RESULT) { const char *new_passwd = session_get(s, "__new_password"); if (new_passwd && (!from || !xstrcmp(from, j->server))) { session_set(s, "password", new_passwd); session_set(s, "__new_password", NULL); print("passwd"); } else { print(new_passwd ? "passwd_possible_abuse" : "passwd_abuse", session_name(s), from); } } session_set(s, "__new_password", NULL); return; } for (q = n->children; q; q = q->next) { const char *ns = q->xmlns; const struct jabber_iq_generic_handler *tmp; if (type == JABBER_IQ_TYPE_GET && session_int_get(s, "display_ctcp") == 1) print("jabber_ctcp_request", session_name(s), from, __(q->name), __(ns)); if (!(tmp = jabber_iq_find_handler(callbacks, q->name, ns))) { debug_error("[jabber] unknown name: <%s xmlns='%s'\n", atype, __(q->name), __(ns)); continue; } debug_function("[jabber] <%s xmlns=%s\n", atype, q->name, ns); tmp->handler(s, q, from, id); /* XXX, read RFC if we can have more get stanzas */ /* * * ... * ... */ } } static inline int jabber_status_int(int tlen, const char *text) { if (!xstrcasecmp(text, "online") || !xstrcasecmp(text, "available")) return EKG_STATUS_AVAIL; return ekg_status_int(text); } static status_t role_and_affiliation_to_ekg2_status(char *role, char * affiliation) { if(!xstrcmp(role, "moderator")) return xstrcmp(affiliation, "owner") ? EKG_STATUS_AVAIL : EKG_STATUS_FFC; else if (!xstrcmp(role,"participant")) return EKG_STATUS_AWAY; else if (!xstrcmp(role, "visitor")) return EKG_STATUS_XA; else /* none */ return EKG_STATUS_NA; } JABBER_HANDLER(jabber_handle_presence) { jabber_private_t *j = s->priv; const char *from = jabber_attr(n->atts, "from"); const char *type = jabber_attr(n->atts, "type"); char *jid, *uid; time_t when = 0; xmlnode_t *q; int ismuc = 0; int istlen = j ? j->istlen : 0; int na = !xstrcmp(type, "unavailable"); jid = tlenjabber_unescape(from); uid = tlenjabber_uid(jid); xfree(jid); if (from && !xstrcmp(type, "subscribe")) { userlist_t *u; jabber_userlist_private_t *up; int auto_auth; if ((auto_auth = session_int_get(s, "auto_auth")) == -1) auto_auth = 0; if (!(u = userlist_find(s, uid))) { u = userlist_add(s, uid, NULL); ekg_group_add(u, "__authreq"); } up = jabber_userlist_priv_get(u); if ((up->authtype & EKG_JABBER_AUTH_FROM)) { /* XXX: maybe just auto-auth? */ debug_error("[jabber] subscribe req for already subscribed uid (%s), authtype=%d, wtf?\n", uid, up->authtype); up->authtype &= ~EKG_JABBER_AUTH_FROM; } up->authtype |= EKG_JABBER_AUTH_REQ; if ((auto_auth & 1)) { if (!(auto_auth & 4)) /* auto-accept */ command_exec_format(NULL, s, 2, "/auth --accept %s", uid); /* else ignore */ } else if ((auto_auth & 4)) /* auto-deny */ command_exec_format(NULL, s, 2, "/auth --deny %s", uid); else { /* ask */ print("jabber_auth_subscribe", uid, session_name(s)); } xfree(uid); return; } if (from && !xstrcmp(type, "unsubscribe")) { int auto_auth = session_int_get(s, "auto_auth"); if (auto_auth == -1) auto_auth = 0; if ((auto_auth & 2)) { if (!(auto_auth & 8)) /* auto-accept */ command_exec_format(NULL, s, 2, "/auth --deny %s", uid); /* else ignore */ } else if ((auto_auth & 8)) /* auto-deny, czyli robienie na opak? */ command_exec_format(NULL, s, 2, "/auth --accept %s", uid); else { /* ask */ userlist_t *u = userlist_find(s, uid); if (!u) { u = userlist_add(s, uid, NULL); ekg_group_add(u, "__authreq"); } jabber_userlist_private_t *up = jabber_userlist_priv_get(u); if (!(up->authtype & EKG_JABBER_AUTH_FROM)) { /* XXX: maybe just auto-auth? or some other wtf? */ debug_error("[jabber] unsubscribe req for not subscribed uid (%s), authtype=%d, wtf?\n", uid, up->authtype); up->authtype |= EKG_JABBER_AUTH_FROM; } up->authtype |= EKG_JABBER_AUTH_UNREQ; print("jabber_auth_unsubscribe", uid, session_name(s)); } xfree(uid); return; } for (q = n->children; q; q = q->next) { char *tmp = xstrchr(uid, '/'); char *mucuid = xstrndup(uid, tmp ? tmp - uid : -1); const char *ns = q->xmlns; if (!xstrcmp(q->name, "x")) { if (!xstrcmp(ns, "http://jabber.org/protocol/muc#user")) { xmlnode_t *child; for (child = q->children; child; child = child->next) { if (!xstrcmp(child->name, "status")) { /* status codes, --- http://www.jabber.org/jeps/jep-0045.html#registrar-statuscodes */ char *code = jabber_attr(child->atts, "code"); int codenr = code ? atoi(code) : -1; switch (codenr) { case 201: print_info(mucuid, s, "jabber_muc_room_created", session_name(s), mucuid); break; case -1: debug_error("[jabber, iq, muc#user] codenr: -1 code: %s\n", __(code)); break; default : debug_error("[jabber, iq, muc#user] XXX codenr: %d code: %s\n", codenr, code); } } else if (!xstrcmp(child->name, "item")) { /* lista userow */ char *jid = jabber_unescape(jabber_attr(child->atts, "jid")); /* jid */ char *role = jabber_unescape(jabber_attr(child->atts, "role")); /* ? */ char *affiliation = jabber_unescape(jabber_attr(child->atts, "affiliation")); /* ? */ char *nickjid = NULL; newconference_t *c; userlist_t *ulist; if (!(c = newconference_find(s, mucuid))) { debug("[jabber,muc] recved muc#user but conference: %s not found ?\n", mucuid); xfree(jid); xfree(role); xfree(affiliation); break; } if (tmp) nickjid = xmpp_uid(tmp + 1); else nickjid = xstrdup(uid); if (na) print_info(mucuid, s, "muc_left", session_name(s), nickjid + 5, jid, mucuid+5, ""); ulist = newconference_member_find(c, nickjid); if (ulist && na) { newconference_member_remove(c, ulist); ulist = NULL; } else if (!ulist) { ulist = newconference_member_add(c, nickjid, nickjid + 5); print_info(mucuid, s, "muc_joined", session_name(s), nickjid + 5, jid, mucuid+5, "", role, affiliation); } if (ulist) { jabber_userlist_private_t *up = jabber_userlist_priv_get(ulist); ulist->status = role_and_affiliation_to_ekg2_status(role, affiliation); if (up) { up->role = xstrdup(role); up->aff = xstrdup(affiliation); } } query_emit(NULL, "userlist-refresh"); debug("[MUC, PRESENCE] NEWITEM: %s (%s) ROLE:%s AFF:%s\n", nickjid, __(jid), role, affiliation); xfree(nickjid); xfree(jid); xfree(role); xfree(affiliation); } else { debug_error("[MUC, PRESENCE] wtf? child->name: %s\n", child->name); } } ismuc = 1; } else if (!xstrcmp(ns, "jabber:x:signed")) { /* JEP-0027 */ char *x_signed = xstrdup(q->data); char *x_status = NULL; char *x_key; xmlnode_t *nstatus = xmlnode_find_child(n, "status"); x_key = jabber_openpgp(s, mucuid, JABBER_OPENGPG_VERIFY, nstatus ? nstatus->data ? nstatus->data : "" : "", x_signed, &x_status); /* @ x_key KEY, x_status STATUS of verification */ debug("jabber_openpgp() %s %s\n", __(x_key), __(x_status)); xfree(x_key); xfree(x_status); } else if (!xstrncmp(ns, "jabber:x:delay", 14)) { when = jabber_try_xdelay(jabber_attr(q->atts, "stamp")); } else debug("[JABBER, PRESENCE]: */ xfree(mucuid); } if (!ismuc && (!type || ( na || !xstrcmp(type, "error") || !xstrcmp(type, "available")))) { xmlnode_t *nshow, *nstatus, *nerr, *temp; userlist_t *u = userlist_find(s, uid); char *descr = /*u ? xstrdup(u->descr) :*/ NULL; int status = /*u ? u->status :*/ 0; /* it'll probably always get replaced */ char *tmp2; int prio = (temp = xmlnode_find_child(n, "priority")) && temp->data ? atoi(temp->data) : 10; { /* first set unknown if we have no auth */ jabber_userlist_private_t *up = jabber_userlist_priv_get(u); if ((!up || !(up->authtype & EKG_JABBER_AUTH_TO))) status = EKG_STATUS_UNKNOWN; } /* then check if we've got another status */ if ((nshow = xmlnode_find_child(n, "show"))) { /* typ */ char *jstatus; if (!(jstatus = tlenjabber_unescape(nshow->data))) jstatus = xstrdup("unknown"); if (!xstrcmp(jstatus, "na")) na = 1; else if ((status = jabber_status_int(j->istlen, jstatus)) == EKG_STATUS_UNKNOWN) debug_error("[jabber] Unknown presence: %s from %s. Please report!\n", jstatus, uid); xfree(jstatus); } else if (!na) /* XXX: check auth? */ status = EKG_STATUS_AVAIL; /* set NA only if we have auth, else leave UNKNOWN */ if (na && (status != EKG_STATUS_UNKNOWN)) status = EKG_STATUS_NA; /* replace any status with error, if we've got one, XXX: or maybe not? */ if ((nerr = xmlnode_find_child(n, "error"))) { /* bledny */ char *ecode = jabber_attr(nerr->atts, "code"); char *etext = jabber_unescape(nerr->data); xfree(descr); descr = saprintf("(%s) %s", ecode, __(etext)); xfree(etext); if (!istlen && ecode && (atoi(ecode) == 403 || atoi(ecode) == 401)) /* we lack auth */ status = EKG_STATUS_UNKNOWN; /* shall we remove the error description? */ else status = EKG_STATUS_ERROR; na = 1; if (istlen) { /* we need to get&fix the UID - userlist entry is sent with @tlen.pl, but error with user-given host */ char *tmp = tlenjabber_unescape(jabber_attr(n->atts, "to")); char *atsign = xstrchr(tmp, '@'); if (atsign) *atsign = 0; uid = saprintf("tlen:%s@tlen.pl", tmp); xfree(tmp); u = userlist_find(s, uid); } } else { if ((nstatus = xmlnode_find_child(n, "status"))) { /* opisowy */ xfree(descr); descr = tlenjabber_unescape(nstatus->data); } } if ((tmp2 = xstrchr(uid, '/'))) { userlist_t *ut; if ((ut = userlist_find(s, uid))) { ekg_resource_t *r; if ((r = userlist_resource_find(ut, tmp2+1))) { if (na) { /* if resource went offline remove... */ userlist_resource_remove(ut, r); r = NULL; } } else r = userlist_resource_add(ut, tmp2+1, prio); if (r && r->prio != prio) { /* XXX, here resort, stupido */ r->prio = prio; } } } if (!when) when = time(NULL); protocol_status_emit(s, uid, status, descr, when); xfree(descr); } xfree(uid); } /* */ static void jabber_session_connected(session_t *s) { jabber_private_t *j = jabber_private(s); s->connecting = 0; protocol_connected_emit(s); if (session_get(s, "__new_account")) { print("register", s->uid); if (!xstrcmp(session_get(s, "password"), "foo")) print("register_change_passwd", s->uid, "foo"); session_set(s, "__new_account", NULL); } session_int_set(s, "__roster_retrieved", 0); userlist_free(s); /* Send it before roster query, so that we can use __roster_retrieved */ if (!j->istlen) watch_write(j->send_watch, "", j->server); watch_write(j->send_watch, ""); if (session_int_get(s, "auto_bookmark_sync") != 0) command_exec(NULL, s, ("/xmpp:bookmark --get"), 1); if (session_int_get(s, "auto_privacylist_sync") != 0) { const char *list = session_get(s, "privacy_list"); if (!list) list = "ekg2"; command_exec_format(NULL, s, 1, ("/xmpp:privacy --get %s"), list); /* synchronize list */ command_exec_format(NULL, s, 1, ("/xmpp:privacy --session %s"), list); /* set as active */ } } static void newmail_common(session_t *s) { /* maybe inline? */ if (config_sound_mail_file) play_sound(config_sound_mail_file); else if (config_jabber_beep_mail) query_emit(NULL, "ui-beep", NULL); /* XXX, we NEED to connect to MAIL_COUNT && display info about mail like mail plugin do. */ /* XXX, emit events */ } static time_t jabber_try_xdelay(const char *stamp) { /* try to parse timestamp */ if (stamp) { struct tm tm; char *tmp = xstrdup(getenv("TZ")); time_t out; memset(&tm, 0, sizeof(tm)); sscanf(stamp, "%4d%2d%2dT%2d:%2d:%2d", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec); tm.tm_year -= 1900; tm.tm_mon -= 1; setenv("TZ", "UTC", 1); out = mktime(&tm); if (tmp) setenv("TZ", tmp, 1); else unsetenv("TZ"); xfree(tmp); return out; } return time(NULL); } const char *jabber_iq_reg(session_t *s, const char *prefix, const char *to, const char *type, const char *xmlns) { jabber_private_t *j = jabber_private(s); int loop = 10; jabber_stanza_t *st; list_t l; const struct jabber_iq_generic_handler *tmp; char *id; id = saprintf("%s%x", prefix ? prefix : "", j->id++); again: for (l = j->iq_stanzas; l; l = l->next) { jabber_stanza_t *i = l->data; if (!xstrcmp(id, i->id)) { xfree(id); if (--loop) { debug_error("jabber_iq_reg() avoiding deadlock\n"); return NULL; } id = saprintf("%s%x_%d", prefix ? prefix : "", j->id++, rand()); debug_white("jabber_iq_reg() found id: %s, new id: %s\n", i->id, id); goto again; } } st = xmalloc(sizeof(jabber_stanza_t)); st->id = id; st->to = xstrdup(to); st->type = xstrdup(type); st->xmlns = xstrdup(xmlns); tmp = jabber_iq_find_handler(jabber_iq_result_handlers, type, xmlns); st->handler = tmp ? tmp->handler : jabber_handle_iq_result_generic; tmp = jabber_iq_find_handler(jabber_iq_error_handlers, type, xmlns); st->error = tmp ? tmp->handler : jabber_handle_iq_error_generic; list_add_beginning(&(j->iq_stanzas), st); return id; } const char *jabber_iq_send(session_t *s, const char *prefix, jabber_iq_type_t iqtype, const char *to, const char *type, const char *xmlns) { jabber_private_t *j = jabber_private(s); const char *id; char *tmp; char *aiqtype; if (iqtype == JABBER_IQ_TYPE_GET) aiqtype = "get"; else if (iqtype == JABBER_IQ_TYPE_SET) aiqtype = "set"; else { debug_error("jabber_iq_send() wrong iqtype passed\n"); return NULL; } if (!(id = jabber_iq_reg(s, prefix, to, type, xmlns))) return NULL; tmp = jabber_escape(to); /* XXX: really worth escaping? */ watch_write(j->send_watch, "<%s xmlns='%s'/>", id, tmp, aiqtype, type, xmlns); xfree(tmp); return id; } ekg2-0.4~pre+20120506.1/plugins/jabber/jabber_handlers_iq_error.inc000066400000000000000000000103721175142753400245700ustar00rootroot00000000000000#define JABBER_HANDLER_ERROR(x) static void x(session_t *s, xmlnode_t *n, const char *from, const char *id) static char *jabber_iq_error_string(xmlnode_t *n) { /* in n we have atts, "code"); if (n->data) { reason = jabber_unescape(n->data); } else { for (n = n->children; n; n = n->next) { if (n->name) { reason = jabber_unescape(n->name); break; } } } debug_error("[JABBER] jabber_iq_error_string: code=%s, [%s]\n", __(ecode), __(reason)); return reason ? reason : xstrdup("ekg2 sux in parsing errors, for more info check debug"); } /* this sux, no idea howto pass formatname to jabber_handle_iq_error_generic() */ JABBER_HANDLER_ERROR(jabber_handle_iq_error_last) { char *error = jabber_iq_error_string(n); print("jabber_lastseen_error", session_name(s), from, error); xfree(error); } JABBER_HANDLER_ERROR(jabber_handle_iq_error_version) { char *error = jabber_iq_error_string(n); print("jabber_version_error", session_name(s), from, error); xfree(error); } JABBER_HANDLER_ERROR(jabber_handle_iq_error_disco_info) { char *error = jabber_iq_error_string(n); print("jabber_transinfo_error", session_name(s), from, error); xfree(error); } JABBER_HANDLER_ERROR(jabber_handle_iq_error_disco) { int iscontrol = !xstrncmp(id, "control", 7); char *error = jabber_iq_error_string(n); print(iscontrol ? "jabber_remotecontrols_error" : "jabber_transport_error", session_name(s), from, error); xfree(error); } JABBER_HANDLER_ERROR(jabber_handle_iq_error_privacy) { char *error = jabber_iq_error_string(n); print("jabber_privacy_error", session_name(s), from, error); xfree(error); } JABBER_HANDLER_ERROR(jabber_handle_iq_error_private) { char *error = jabber_iq_error_string(n); print("jabber_private_list_error", session_name(s), from, error); xfree(error); } JABBER_HANDLER_ERROR(jabber_handle_iq_error_vcard) { char *error = jabber_iq_error_string(n); print("jabber_userinfo_error", session_name(s), from, error); xfree(error); } JABBER_HANDLER_ERROR(jabber_handle_iq_error_search) { char *error = jabber_iq_error_string(n); print("jabber_search_error", session_name(s), from, error); xfree(error); } JABBER_HANDLER_ERROR(jabber_handle_iq_error_generic) { char *error = jabber_iq_error_string(n); debug_error("jabber_handle_iq_error_generic() %s\n", __(error)); xfree(error); } static const struct jabber_iq_generic_handler jabber_iq_error_handlers[] = { { "vCard", "vcard-temp", jabber_handle_iq_error_vcard }, { "query", "jabber:iq:last", jabber_handle_iq_error_last }, { NULL, "jabber:iq:privacy", jabber_handle_iq_error_privacy }, { NULL, "jabber:iq:private", jabber_handle_iq_error_private }, { NULL, "jabber:iq:version", jabber_handle_iq_error_version }, { NULL, "jabber:iq:search", jabber_handle_iq_error_search }, { NULL, "http://jabber.org/protocol/disco#info", jabber_handle_iq_error_disco_info }, { NULL, "http://jabber.org/protocol/disco#items", jabber_handle_iq_error_disco }, { "", NULL, NULL } }; #if 0 /* ERROR handler for id: offer */ char *uin = jabber_unescape(from); dcc_t *p = jabber_dcc_find(uin, id, NULL); if (p) { /* XXX, new theme it's for ip:port */ print("dcc_error_refused", format_user(s, p->uid)); dcc_close(p); } else { /* XXX, possible abuse attempt */ } xfree(uin); #endif JABBER_HANDLER_ERROR(jabber_handle_iq_error_generic_old) { jabber_private_t *j = s->priv; xmlnode_t *e = xmlnode_find_child(n, "error"); char *reason = (e) ? jabber_unescape(e->data) : NULL; if (!xstrncmp(id, "register", 8)) { print("register_failed", jabberfix(reason, "?")); } else if (!xstrcmp(id, "auth")) { j->parser = NULL; jabber_handle_disconnect(s, reason ? reason : _("Error connecting to Jabber server"), EKG_DISCONNECT_FAILURE); } else if (!xstrncmp(id, "passwd", 6)) { print("passwd_failed", jabberfix(reason, "?")); session_set(s, "__new_password", NULL); } else debug_error("[JABBER] GENERIC IQ ERROR: %s\n", __(reason)); xfree(reason); } // vim:syn=c ekg2-0.4~pre+20120506.1/plugins/jabber/jabber_handlers_iq_get.inc000066400000000000000000000155071175142753400242230ustar00rootroot00000000000000#define JABBER_HANDLER_GET_REPLY(x) static void x(session_t *s, xmlnode_t *n, const char *from, const char *id) /** * jabber_handle_iq_get_disco() * * Handler for IQ GET QUERY xmlns="http://jabber.org/protocol/disco#items"
* Send some info about what ekg2 can do/know with given node [node= in n->atts]
* XXX info about it in XEP/RFC * * @todo We send here only info about node: http://jabber.org/protocol/commands * Be more XEP/RFC compilant... return error if node not known, return smth * what we can do at all. etc. etc. */ JABBER_HANDLER_GET_REPLY(jabber_handle_iq_get_disco) { jabber_private_t *j = s->priv; if (!xstrcmp(jabber_attr(n->atts, "node"), "http://jabber.org/protocol/commands")) { /* jesli node commandowe */ /* XXX, check if $uid can do it */ watch_write(j->send_watch, "" "" "" "" "" "" "" "" "" "", from, id, s->uid+5, j->resource, s->uid+5, j->resource, s->uid+5, j->resource, s->uid+5, j->resource, s->uid+5, j->resource, s->uid+5, j->resource, s->uid+5, j->resource); return; } /* XXX, tutaj jakies ogolne informacje co umie ekg2 */ } /** * jabber_handle_iq_get_disco_info() * * Handler for IQ GET QUERY xmlns="http://jabber.org/protocol/disco#info"
* XXX * */ JABBER_HANDLER_GET_REPLY(jabber_handle_iq_get_disco_info) { jabber_private_t *j = s->priv; watch_write(j->send_watch, "" "" "" /* jabber_handle_iq_get_last() */ "" /* jabber_handle_iq_get_version() */ "" /* jabber_handle_iq_ping() */ "" "", from, id); #if 0 "" "" "" "" #endif } /** * jabber_handle_iq_get_last() * * XEP-0012: Last Activity (http://www.xmpp.org/extensions/xep-0012.html) [1.2 2007-02-15] (iq:type='get' iq::query:xmlns='jabber:iq:last')
* * Send reply about our last activity.
* * @todo From XEP-0012: * - 8. A client MUST provide a way for a human user to disable sending of Last Activity responses from the client's full JID (). */ JABBER_HANDLER_GET_REPLY(jabber_handle_iq_get_last) { jabber_private_t *j = s->priv; watch_write(j->send_watch, "" "" "", from, id, (time(NULL)-s->activity)); } /** * jabber_handle_iq_get_version() * * XEP-0092: Software Version (http://www.xmpp.org/extensions/xep-0092.html) [1.1 2007-02-15] (iq:type='get' iq::query:xmlns='jabber:iq:version')
* * Send info about our program and system
* * @note * PRIVACY WARNING: It'll send potential useful information like: what version of kernel you use.
* If you don't want to send this information set session variables:
* - ver_client_name - If you want spoof program name. [Although I think it's good to send info about ekg2. Because it's good program.]
* - ver_client_version - If you want to spoof program version.
* - ver_os - The most useful, to spoof OS name, version and stuff.
* * @todo From XEP-0092: * - 5. an application MUST provide a way for a human user or administrator to disable sharing of information about the operating system. */ JABBER_HANDLER_GET_REPLY(jabber_handle_iq_get_version) { jabber_private_t *j = s->priv; const char *ver_os; const char *tmp; char *escaped_client_name = jabber_escape(jabberfix((tmp = session_get(s, "ver_client_name")), DEFAULT_CLIENT_NAME)); char *escaped_client_version = jabber_escape(jabberfix((tmp = session_get(s, "ver_client_version")), VERSION)); char *osversion; if (!(ver_os = session_get(s, "ver_os"))) { struct utsname buf; if (uname(&buf) != -1) { char *osver = saprintf("%s %s %s", buf.sysname, buf.release, buf.machine); osversion = jabber_escape(osver); xfree(osver); } else { osversion = xstrdup(("unknown")); /* uname failed and not ver_os session variable */ } } else { osversion = jabber_escape(ver_os); /* ver_os session variable */ } watch_write(j->send_watch, "" "" "%s" "%s" "%s", from, id, escaped_client_name, escaped_client_version, osversion); xfree(escaped_client_name); xfree(escaped_client_version); xfree(osversion); } /** * jabber_handle_iq_ping() * * XEP-0199: XMPP Ping (http://www.xmpp.org/extensions/xep-0199.html) [1.0 2007-06-12] (iq:type='get' iq::ping:xmlns='urn:xmpp:ping')
* * @note From XEP-0199: * - 6. If a connected resource receives a ping request but it does not want to reveal its network availability to the sender for any reason * (e.g., because the sender is not authorized to know the connected resource's availability), * then it too MUST reply with a error. */ JABBER_HANDLER_GET_REPLY(jabber_handle_iq_ping) { jabber_private_t *j = s->priv; watch_write(j->send_watch, "\n", from, id); } static const struct jabber_iq_generic_handler jabber_iq_get_handlers[] = { { "query", "jabber:iq:last", jabber_handle_iq_get_last }, { NULL, "jabber:iq:version", jabber_handle_iq_get_version }, { NULL, "http://jabber.org/protocol/disco#items", jabber_handle_iq_get_disco }, { NULL, "http://jabber.org/protocol/disco#info", jabber_handle_iq_get_disco_info }, { "ping", "urn:xmpp:ping", jabber_handle_iq_ping }, { "", NULL, NULL } }; // vim:syn=c ekg2-0.4~pre+20120506.1/plugins/jabber/jabber_handlers_iq_result.inc000066400000000000000000001234021175142753400247540ustar00rootroot00000000000000#define JABBER_HANDLER_RESULT(x) static void x(session_t *s, xmlnode_t *n, const char *from, const char *id) #define JABBER_HANDLER_SET(x) static void x(session_t *s, xmlnode_t *n, const char *from, const char *id) JABBER_HANDLER_RESULT(jabber_handle_iq_result_disco) { xmlnode_t *item = xmlnode_find_child(n, "item"); char *uid = jabber_unescape(from); int iscontrol = !xstrncmp(id, "control", 7); if (item) { int i = 1; print(iscontrol ? "jabber_remotecontrols_list_begin" : "jabber_transport_list_begin", session_name(s), uid); for (; item; item = item->next, i++) { char *sjid = jabber_unescape(jabber_attr(item->atts, "jid")); char *sdesc = jabber_unescape(jabber_attr(item->atts, "name")); char *snode = jabber_unescape(jabber_attr(item->atts, "node")); if (!iscontrol) print(snode ? "jabber_transport_list_item_node" : "jabber_transport_list_item", session_name(s), uid, sjid, snode, sdesc, ekg_itoa(i)); else print("jabber_remotecontrols_list_item", session_name(s), uid, sjid, snode, sdesc, ekg_itoa(i)); xfree(sdesc); xfree(sjid); xfree(snode); } print(iscontrol ? "jabber_remotecontrols_list_end" : "jabber_transport_list_end", session_name(s), uid); } else print(iscontrol ? "jabber_remotecontrols_list_nolist" : "jabber_transport_list_nolist", session_name(s), uid); xfree(uid); } JABBER_HANDLER_RESULT(jabber_handle_iq_result_new_mail) { jabber_private_t *j = s->priv; watch_write(j->send_watch, "", j->id++); } JABBER_HANDLER_RESULT(jabber_handle_iq_result_disco_info) { const char *wezel = jabber_attr(n->atts, "node"); xmlnode_t *node; char *uid; if (!session_int_get(s, "__roster_retrieved")) { for (node = n->children; node; node = node->next) { if (!xstrcmp(node->name, "feature") && !xstrcmp(jabber_attr(node->atts, "var"), "google:mail:notify")) { /* it's not nice to send such NULLs, but as long as that func doesn't use any other args... */ jabber_handle_iq_result_new_mail(s, NULL, NULL, NULL); } } return; } uid = jabber_unescape(from); print((wezel) ? "jabber_transinfo_begin_node" : "jabber_transinfo_begin", session_name(s), uid, wezel); for (node = n->children; node; node = node->next) { if (!xstrcmp(node->name, "identity")) { char *name = jabber_unescape(jabber_attr(node->atts, "name")); /* nazwa */ // char *cat = jabber_attr(node->atts, "category"); /* server, gateway, directory */ // char *type = jabber_attr(node->atts, "type"); /* typ: im */ if (name) /* jesli nie ma nazwy don't display it. */ print("jabber_transinfo_identify" /* _server, _gateway... ? */, session_name(s), uid, name); xfree(name); } else if (!xstrcmp(node->name, "feature")) { char *var = jabber_attr(node->atts, "var"); char *tvar = NULL; /* translated */ int user_command = 0; /* dj, jakas glupota... ale ma ktos pomysl zeby to inaczej zrobic?... jeszcze istnieje pytanie czy w ogole jest sens to robic.. */ if (!xstrcmp(var, "http://jabber.org/protocol/disco#info")) tvar = "/xmpp:transpinfo"; else if (!xstrcmp(var, "http://jabber.org/protocol/disco#items")) tvar = "/xmpp:transports"; else if (!xstrcmp(var, "http://jabber.org/protocol/disco")) tvar = "/xmpp:transpinfo && /xmpp:transports"; else if (!xstrcmp(var, "http://jabber.org/protocol/muc")) tvar = "/xmpp:mucpart && /xmpp:mucjoin"; else if (!xstrcmp(var, "http://jabber.org/protocol/stats")) tvar = "/xmpp:stats"; else if (!xstrcmp(var, "jabber:iq:register")) tvar = "/xmpp:register"; else if (!xstrcmp(var, "jabber:iq:search")) tvar = "/xmpp:search"; else if (!xstrcmp(var, "http://jabber.org/protocol/bytestreams")) { user_command = 1; tvar = "/xmpp:dcc (PROT: BYTESTREAMS)"; } else if (!xstrcmp(var, "http://jabber.org/protocol/si/profile/file-transfer")) { user_command = 1; tvar = "/xmpp:dcc"; } else if (!xstrcmp(var, "http://jabber.org/protocol/commands")) { user_command = 1; tvar = "/xmpp:control"; } else if (!xstrcmp(var, "jabber:iq:version")) { user_command = 1; tvar = "/xmpp:ver"; } else if (!xstrcmp(var, "jabber:iq:last")) { user_command = 1; tvar = "/xmpp:lastseen"; } else if (!xstrcmp(var, "vcard-temp")) { user_command = 1; tvar = "/xmpp:change && /xmpp:userinfo"; } else if (!xstrcmp(var, "message")) { user_command = 1; tvar = "/xmpp:msg"; } else if (!xstrcmp(var, "http://jabber.org/protocol/vacation")) { user_command = 2; tvar = "/xmpp:vacation"; } else if (!xstrcmp(var, "presence-invisible")) { user_command = 2; tvar = "/invisible"; } /* we ought use jabber:iq:privacy */ else if (!xstrcmp(var, "jabber:iq:privacy")) { user_command = 2; tvar = "/xmpp:privacy"; } else if (!xstrcmp(var, "jabber:iq:private")) { user_command = 2; tvar = "/xmpp:private && /xmpp:config && /xmpp:bookmark"; } if (tvar) print( user_command == 2 ? "jabber_transinfo_comm_not" : user_command == 1 ? "jabber_transinfo_comm_use" : "jabber_transinfo_comm_ser", session_name(s), uid, tvar, var); else print("jabber_transinfo_feature", session_name(s), uid, var, var); } else if (!xstrcmp(node->name, "x") && !xstrcmp(node->xmlns, "jabber:x:data") && !xstrcmp(jabber_attr(node->atts, "type"), "result")) { jabber_handle_xmldata_result(s, node->children, uid); } } print("jabber_transinfo_end", session_name(s), uid); xfree(uid); } /** * jabber_handle_iq_result_last() * * Handler for IQ RESULT QUERY xmlns="jabber:iq:last"
* Display info about server uptime/client idle/client logout time. * * @todo It's ugly written. */ JABBER_HANDLER_RESULT(jabber_handle_iq_result_last) { const char *last = jabber_attr(n->atts, "seconds"); char buff[21]; char *from_str; int seconds; seconds = atoi(last); if ((seconds>=0) && (seconds < 999*24*60*60-1)) /* days, hours, minutes, seconds... */ snprintf(buff, sizeof(buff), _("%03dd %02dh %02dm %02ds"),seconds / 86400, \ (seconds / 3600) % 24, (seconds / 60) % 60, seconds % 60); else strcpy(buff, _("very long")); from_str = (from) ? jabber_unescape(from) : NULL; print( (xstrchr(from_str, '/') ? "jabber_lastseen_idle" : /* if we have resource than display -> user online+idle */ xstrchr(from_str, '@') ? "jabber_lastseen_response" : /* if we have '@' in xmpp: than display -> user logged out */ "jabber_lastseen_uptime"), /* otherwise we have server -> server up for: */ jabberfix(from_str, "unknown"), buff); xfree(from_str); } /** * jabber_handle_iq_result_version() * * Handler for IQ RESULT QUERY xmlns="jabber:iq:version"
* Display info about smb program version and system
* */ JABBER_HANDLER_RESULT(jabber_handle_iq_result_version) { xmlnode_t *name = xmlnode_find_child(n, "name"); xmlnode_t *version = xmlnode_find_child(n, "version"); xmlnode_t *os = xmlnode_find_child(n, "os"); char *from_str = (from) ? jabber_unescape(from) : NULL; char *name_str = (name) ? jabber_unescape(name->data) : NULL; char *version_str = (version) ? jabber_unescape(version->data) : NULL; char *os_str = (os) ? jabber_unescape(os->data) : NULL; print("jabber_version_response", jabberfix(from_str, "unknown"), jabberfix(name_str, "unknown"), jabberfix(version_str, "unknown"), jabberfix(os_str, "unknown")); xfree(os_str); xfree(version_str); xfree(name_str); xfree(from_str); } JABBER_HANDLER_RESULT(jabber_handle_iq_result_privacy) { jabber_private_t *j = s->priv; xmlnode_t *active = xmlnode_find_child(n, "active"); xmlnode_t *def = xmlnode_find_child(n, "default"); char *activename = active ? jabber_attr(active->atts, "name") : NULL; char *defaultname = def ? jabber_attr(def->atts, "name") : NULL; xmlnode_t *node; int i = 0; for (node = n->children; node; node = node->next) { if (!xstrcmp(node->name, "list")) { if (node->children) { /* Display all details */ list_t *lista = NULL; xmlnode_t *temp; i = -1; if (session_int_get(s, "auto_privacylist_sync") != 0) { const char *list = session_get(s, "privacy_list"); if (!list) list = "ekg2"; if (!xstrcmp(list, jabber_attr(node->atts, "name"))) { jabber_privacy_free(j); lista = &(j->privacy); } } print("jabber_privacy_item_header", session_name(s), j->server, jabber_attr(node->atts, "name")); for (temp=node->children; temp; temp = temp->next) { if (!xstrcmp(temp->name, "item")) { /* (we should sort it by order! XXX) (server send like it was set) */ /* a lot TODO */ xmlnode_t *iq = xmlnode_find_child(temp, "iq"); xmlnode_t *message = xmlnode_find_child(temp, "message"); xmlnode_t *presencein = xmlnode_find_child(temp, "presence-in"); xmlnode_t *presenceout = xmlnode_find_child(temp, "presence-out"); char *value = jabber_attr(temp->atts, "value"); char *type = jabber_attr(temp->atts, "type"); char *action = jabber_attr(temp->atts, "action"); char *jid, *formated; char *formatedx; if (!xstrcmp(type, "jid")) jid = xmpp_uid(value); else if (!xstrcmp(type, "group")) jid = saprintf("@%s", value); else jid = xstrdup(value); formated = format_string( format_find(!xstrcmp(action,"allow") ? "jabber_privacy_item_allow" : "jabber_privacy_item_deny"), jid); formatedx = format_string( format_find(!xstrcmp(action,"allow") ? "jabber_privacy_item_allow" : "jabber_privacy_item_deny"), "*"); xfree(formatedx); formatedx = xstrdup("x"); if (lista) { jabber_iq_privacy_t *item = xmalloc(sizeof(jabber_iq_privacy_t)); item->type = xstrdup(type); item->value = xstrdup(value); item->allow = !xstrcmp(action, "allow"); if (iq) item->items |= PRIVACY_LIST_IQ; if (message) item->items |= PRIVACY_LIST_MESSAGE; if (presencein) item->items |= PRIVACY_LIST_PRESENCE_IN; if (presenceout)item->items |= PRIVACY_LIST_PRESENCE_OUT; item->order = atoi(jabber_attr(temp->atts, "order")); LIST_ADD_SORTED(lista, item, jabber_privacy_add_compare); } print("jabber_privacy_item", session_name(s), j->server, jabber_attr(temp->atts, "order"), formated, message ? formatedx : " ", presencein ? formatedx : " ", presenceout ? formatedx : " ", iq ? formatedx : " "); xfree(formated); xfree(formatedx); xfree(jid); } } { /* just help... */ char *allowed = format_string(format_find("jabber_privacy_item_allow"), "xmpp:allowed - JID ALLOWED"); char *denied = format_string(format_find("jabber_privacy_item_deny"), "@denied - GROUP DENIED"); print("jabber_privacy_item_footer", session_name(s), j->server, allowed, denied); xfree(allowed); xfree(denied); } } else { /* Display only name */ int dn = 0; /* rekurencja, ask for this item (?) */ if (!i) print("jabber_privacy_list_begin", session_name(s), j->server); if (i != -1) i++; if (active && !xstrcmp(jabber_attr(node->atts, "name"), activename)) { print("jabber_privacy_list_item_act", session_name(s), j->server, ekg_itoa(i), activename); dn++; } if (def && !xstrcmp(jabber_attr(node->atts, "name"), defaultname)) { print("jabber_privacy_list_item_def", session_name(s), j->server, ekg_itoa(i), defaultname); dn++; } if (!dn) print("jabber_privacy_list_item", session_name(s), j->server, ekg_itoa(i), jabber_attr(node->atts, "name")); } } } if (i > 0) print("jabber_privacy_list_end", session_name(s), j->server); if (i == 0) print("jabber_privacy_list_noitem", session_name(s), j->server); } JABBER_HANDLER_RESULT(jabber_handle_iq_result_private) { jabber_private_t *j = s->priv; xmlnode_t *node; for (node = n->children; node; node = node->next) { char *lname = jabber_unescape(node->name); const char *ns = node->xmlns; xmlnode_t *child; int config_display = 1; int bookmark_display = 1; int quiet = 0; if (!xstrcmp(node->name, "ekg2")) { if (!xstrcmp(ns, "ekg2:prefs") && !xstrncmp(id, "config", 6)) config_display = 0; /* if it's /xmpp:config --get (not --display) we don't want to display it */ /* XXX, other */ } if (!xstrcmp(node->name, "storage")) { if (!xstrcmp(ns, "storage:bookmarks") && !xstrncmp(id, "config", 6)) bookmark_display = 0; /* if it's /xmpp:bookmark --get (not --display) we don't want to display it (/xmpp:bookmark --get performed @ connect) */ } if (!config_display || !bookmark_display) quiet = 1; if (node->children) printq("jabber_private_list_header", session_name(s), lname, ns); if (!xstrcmp(node->name, "ekg2") && !xstrcmp(ns, "ekg2:prefs")) { /* our priv_data struct, containing `full` configuration of ekg2 */ for (child = node->children; child; child = child->next) { char *cname = jabber_unescape(child->name); char *cvalue = jabber_unescape(child->data); if (!xstrcmp(child->name, "plugin") && !xstrcmp(child->xmlns, "ekg2:plugin")) { xmlnode_t *temp; printq("jabber_private_list_plugin", session_name(s), lname, ns, jabber_attr(child->atts, "name"), jabber_attr(child->atts, "prio")); for (temp = child->children; temp; temp = temp->next) { char *snname = jabber_unescape(temp->name); char *svalue = jabber_unescape(temp->data); printq("jabber_private_list_subitem", session_name(s), lname, ns, snname, svalue); xfree(snname); xfree(svalue); } } else if (!xstrcmp(child->name, "session") && !xstrcmp(child->xmlns, "ekg2:session")) { xmlnode_t *temp; printq("jabber_private_list_session", session_name(s), lname, ns, jabber_attr(child->atts, "uid")); for (temp = child->children; temp; temp = temp->next) { char *snname = jabber_unescape(temp->name); char *svalue = jabber_unescape(temp->data); printq("jabber_private_list_subitem", session_name(s), lname, ns, snname, svalue); xfree(snname); xfree(svalue); } } else printq("jabber_private_list_item", session_name(s), lname, ns, cname, cvalue); xfree(cname); xfree(cvalue); } } else if (!xstrcmp(node->name, "storage") && !xstrcmp(ns, "storage:bookmarks")) { /* JEP-0048: Bookmark Storage */ /* destroy previously-saved list */ jabber_bookmarks_free(j); /* create new-one */ for (child = node->children; child; child = child->next) { jabber_bookmark_t *book = xmalloc(sizeof(jabber_bookmark_t)); debug_function("[JABBER:IQ:PRIVATE BOOKMARK item=%s\n", child->name); if (!xstrcmp(child->name, "conference")) { xmlnode_t *temp; book->type = JABBER_BOOKMARK_CONFERENCE; book->priv_data.conf = xmalloc(sizeof(jabber_bookmark_conference_t)); book->priv_data.conf->name = jabber_unescape(jabber_attr(child->atts, "name")); book->priv_data.conf->jid = jabber_unescape(jabber_attr(child->atts, "jid")); book->priv_data.conf->autojoin = !xstrcmp(jabber_attr(child->atts, "autojoin"), "true"); book->priv_data.conf->nick = jabber_unescape( (temp = xmlnode_find_child(child, "nick")) ? temp->data : NULL); book->priv_data.conf->pass = jabber_unescape( (temp = xmlnode_find_child(child, "password")) ? temp->data : NULL); printq("jabber_bookmark_conf", session_name(s), book->priv_data.conf->name, book->priv_data.conf->jid, book->priv_data.conf->autojoin ? "X" : " ", book->priv_data.conf->nick, book->priv_data.conf->pass); } else if (!xstrcmp(child->name, "url")) { book->type = JABBER_BOOKMARK_URL; book->priv_data.url = xmalloc(sizeof(jabber_bookmark_url_t)); book->priv_data.url->name = jabber_unescape(jabber_attr(child->atts, "name")); book->priv_data.url->url = jabber_unescape(jabber_attr(child->atts, "url")); printq("jabber_bookmark_url", session_name(s), book->priv_data.url->name, book->priv_data.url->url); } else { debug_error("[JABBER:IQ:PRIVATE:BOOKMARK UNKNOWNITEM=%s\n", child->name); xfree(book); book = NULL; } if (book) list_add(&j->bookmarks, book); } } else { /* DISPLAY IT ? w jakim formacie? * + CHILD item=value item2=value2 * - SUBITEM ..... * - SUBITEM........ * + SUBCHILD ...... * - SUBITEM * ? XXX */ } if (node->children) printq("jabber_private_list_footer", session_name(s), lname, ns); else printq("jabber_private_list_empty", session_name(s), lname, ns); xfree(lname); } } JABBER_HANDLER_RESULT(jabber_handle_gmail_result_mailbox) { jabber_private_t *j = s->priv; char *mailcount = jabber_attr(n->atts, "total-matched"); int tid_set = 0; xmlnode_t *child; xfree(j->last_gmail_result_time); j->last_gmail_result_time = xstrdup(jabber_attr(n->atts, "result-time")); print("gmail_count", session_name(s), mailcount); /* http://code.google.com/apis/talk/jep_extensions/gmail.html */ for (child = n->children; child; child = child->next) { if (!xstrcmp(child->name, "mail-thread-info")) { if (!tid_set) { xfree(j->last_gmail_tid); j->last_gmail_tid = xstrdup(jabber_attr(child->atts, "tid")); } tid_set = 1; xmlnode_t *subchild; string_t from = string_init(NULL); char *amessages = jabber_attr(child->atts, "messages"); /* messages count in thread */ char *subject = NULL; char *snippet = NULL; int firstsender = 1; for (subchild = child->children; subchild; subchild = subchild->next) { if (0) { } else if (!xstrcmp(subchild->name, "subject")) { if (xstrcmp(subchild->data, "")) { xfree(subject); subject = jabber_unescape(subchild->data); } } else if (!xstrcmp(subchild->name, "senders")) { xmlnode_t *senders; for (senders = subchild->children; senders; senders = senders->next) { char *aname = jabber_unescape(jabber_attr(senders->atts, "name")); char *amail = jabber_attr(senders->atts, "address"); if (!firstsender) string_append(from, ", "); if (aname) { char *tmp = saprintf("%s <%s>", aname, amail); string_append(from, tmp); xfree(tmp); } else { string_append(from, amail); } firstsender = 0; xfree(aname); } } else if (!xstrcmp(subchild->name, "labels")) { /* | A tag that contains a pipe ('|') delimited list of labels applied to this thread. */ } else if (!xstrcmp(subchild->name, "snippet")) { /* A snippet from the body of the email. This must be HTML-encoded. */ snippet = jabber_unescape(subchild->data); } else debug_error("[jabber] google:mail:notify/mail-thread-info wtf: %s\n", __(subchild->name)); } print((amessages && atoi(amessages) > 1) ? "gmail_thread" : "gmail_mail", session_name(s), from->str, jabberfix(subject, "(no subject)"), amessages, jabberfix(snippet, "")); string_free(from, 1); xfree(subject); xfree(snippet); } else debug_error("[jabber, iq] google:mail:notify wtf: %s\n", __(child->name)); } if (mailcount && atoi(mailcount)) /* we don't want to beep or send events if no new mail is available */ newmail_common(s); } JABBER_HANDLER_RESULT(jabber_handle_iq_result_search) { jabber_private_t *j = s->priv; xmlnode_t *node; int rescount = 0; char *uid = jabber_unescape(from); int formdone = 0; for (node = n->children; node; node = node->next) { if (!xstrcmp(node->name, "item")) rescount++; } if (rescount > 1) print("jabber_search_begin", session_name(s), uid); for (node = n->children; node; node = node->next) { if (!xstrcmp(node->name, "item")) { xmlnode_t *tmp; char *jid = jabber_attr(node->atts, "jid"); char *nickname = tlenjabber_unescape( (tmp = xmlnode_find_child(node, "nick")) ? tmp->data : NULL); char *fn = tlenjabber_unescape( (tmp = xmlnode_find_child(node, "first")) ? tmp->data : NULL); char *lastname = tlenjabber_unescape( (tmp = xmlnode_find_child(node, "last")) ? tmp->data : NULL); char *email = tlenjabber_unescape( (tmp = xmlnode_find_child(node, "email")) ? tmp->data : NULL); /* idea about displaink user in depend of number of users founded gathered from gg plugin */ print(rescount > 1 ? "jabber_search_items" : "jabber_search_item", session_name(s), uid, jid, nickname, fn, lastname, email); xfree(nickname); xfree(fn); xfree(lastname); xfree(email); } else { xmlnode_t *reg; if (rescount == 0) rescount = -1; if (formdone) continue; for (reg = n->children; reg; reg = reg->next) { if (!xstrcmp(reg->name, "x") && !xstrcmp("jabber:x:data", reg->xmlns)) { if (!xstrcmp(jabber_attr(reg->atts, "type"), "form")) { formdone = 1; jabber_handle_xmldata_form(s, uid, "search", reg->children, "--jabber_x_data"); break; } else if (!xstrcmp(jabber_attr(reg->atts, "type"), "result")) { formdone = 1; jabber_handle_xmldata_result(s, reg->children, uid); break; } } } if (!formdone) { /* XXX */ } } } if (rescount > 1) print("jabber_search_end", session_name(s), uid); if (rescount == 0) print("search_not_found"); /* not found */ xfree(uid); } JABBER_HANDLER_RESULT(jabber_handle_result_pubsub) { xmlnode_t *p; for (p = n->children; p; p = p->next) { if (!xstrcmp(p->name, "items")) { const char *nodename = jabber_attr(p->atts, "node"); xmlnode_t *node; debug_error("XXX %s\n", __(nodename)); for (node = p->children; node; node = node->next) { if (!xstrcmp(node->name, "item")) { const char *itemid = jabber_attr(node->atts, "id"); debug_error("XXX XXX %s\n", __(itemid)); /* XXX HERE, node->children... is entry. */ } else debug_error("[%s:%d] wtf? %s\n", __FILE__, __LINE__, __(node->name)); } } else debug_error("[%s:%d] wtf? %s\n", __FILE__, __LINE__, __(p->name)); } } /*******************************************************************************************************/ /* these need cleanup SET/ RESULT */ JABBER_HANDLER_RESULT(jabber_handle_iq_roster) { int roster_retrieved = (session_int_get(s, "__roster_retrieved") == 1); jabber_private_t *j = s->priv; xmlnode_t *item = xmlnode_find_child(n, "item"); for (; item ; item = item->next) { const char *jid = jabber_attr(item->atts, "jid"); userlist_t *u; char *uid; if (j->istlen) uid = tlen_uid(jid); else uid = xmpp_uid(jid); /* jeśli element rostera ma subscription = remove to tak naprawde użytkownik jest usuwany; w przeciwnym wypadku - nalezy go dopisać do userlisty; dodatkowo, jesli uzytkownika mamy już w liscie, to przyszla do nas zmiana rostera; usunmy wiec najpierw, a potem sprawdzmy, czy warto dodawac :) */ if (roster_retrieved && (u = userlist_find(s, uid))) userlist_remove(s, u); if (!xstrncmp(jabber_attr(item->atts, "subscription"), "remove", 6)) { /* nic nie robimy, bo juz usuniete */ } else { char *nickname = tlenjabber_unescape(jabber_attr(item->atts, "name")); const char *authval; xmlnode_t *group; u = userlist_find(s, uid); if (u && xstrcmp(u->nickname, nickname)) query_emit(NULL, "userlist-renamed", &nickname, &(u->nickname)); else u = userlist_add(s, uid, nickname); if ((authval = jabber_attr(item->atts, "subscription"))) { jabber_userlist_private_t *up = jabber_userlist_priv_get(u); if (up) { int tmp; for (tmp = EKG_JABBER_AUTH_BOTH; (tmp > EKG_JABBER_AUTH_NONE) && xstrcasecmp(authval, jabber_authtypes[tmp]); tmp--); /* clear appropriate request bit and old authtype */ up->authtype &= ~(EKG_JABBER_AUTH_BOTH | ((tmp & EKG_JABBER_AUTH_FROM) ? EKG_JABBER_AUTH_REQ : EKG_JABBER_AUTH_UNREQ)); /* and set new one */ up->authtype |= tmp; } if (!up || !(up->authtype & EKG_JABBER_AUTH_TO)) { if (u && u->status == EKG_STATUS_NA) u->status = EKG_STATUS_UNKNOWN; } else { if (u && u->status == EKG_STATUS_UNKNOWN) u->status = EKG_STATUS_NA; } } for (group = xmlnode_find_child(item, "group"); group ; group = group->next ) { char *gname = jabber_unescape(group->data); ekg_group_add(u, gname); xfree(gname); } if (roster_retrieved) { command_exec_format(NULL, s, 1, ("/auth --probe %s"), uid); } xfree(nickname); } xfree(uid); }; /* for */ { /* nickname generator */ userlist_t *ul; for (ul = s->userlist; ul;) { userlist_t *u = ul; if (!u->nickname && !ekg_group_member(u, "__authreq")) { char *myuid = xstrdup(u->uid); char *userpart = xstrdup(u->uid); char *tmp; const char **cp; const char *possibilities[] = { userpart+5, /* user-part of UID */ myuid+5, /* JID without resource */ u->uid+5, /* JID with resource */ myuid, /* UID without resource */ u->uid, /* UID with resource */ NULL }; if ((tmp = xstrchr(userpart, '@'))) *tmp = 0; if ((tmp = xstrchr(myuid, '/'))) *tmp = 0; for (cp = possibilities; *cp; cp++) { userlist_t *m; for (m = s->userlist; m; m = m->next) { userlist_t *w = m; if (w && w->nickname && !xstrcasecmp(w->nickname, *cp)) break; } if (!m) break; } if (*cp) { u->nickname = xstrdup(*cp); userlist_replace(s, u); /* resort */ /* sorting changes order, * so we need to start from beginning * sorry */ ul = s->userlist; xfree(userpart); xfree(myuid); continue; } else debug_error("[jabber] can't find any free nickname for UID %s.. that's kinda bitch!\n", u->uid); xfree(userpart); xfree(myuid); } ul = ul->next; } } if (!roster_retrieved) { session_int_set(s, "__roster_retrieved", 1); jabber_write_status(s); } query_emit(NULL, "userlist-refresh"); } JABBER_HANDLER_RESULT(jabber_handle_iq_result_register) { xmlnode_t *reg; char *from_str = (from) ? jabber_unescape(from) : xstrdup(_("unknown")); int done = 0; for (reg = n->children; reg; reg = reg->next) { if (!xstrcmp(reg->name, "x") && !xstrcmp("jabber:x:data", reg->xmlns) && ( !xstrcmp("form", jabber_attr(reg->atts, "type")) || !jabber_attr(reg->atts, "type"))) { done = 1; jabber_handle_xmldata_form(s, from_str, "register", reg->children, "--jabber_x_data"); break; } } if (!done && !n->children) { /* XXX */ done = 1; } if (!done) { xmlnode_t *instr = xmlnode_find_child(n, "instructions"); print("jabber_form_title", session_name(s), from_str, from_str); if (instr && instr->data) { char *instr_str = jabber_unescape(instr->data); print("jabber_form_instructions", session_name(s), from_str, instr_str); xfree(instr_str); } print("jabber_form_command", session_name(s), from_str, "register", ""); for (reg = n->children; reg; reg = reg->next) { char *jname, *jdata; if (!xstrcmp(reg->name, "instructions")) continue; if (!xstrcmp(reg->name, "registered")) continue; /* XXX the entity is already registered! */ jname = jabber_unescape(reg->name); if (!xstrcmp(jname, "password")) jdata = xstrdup("(...)"); else jdata = jabber_unescape(reg->data); print("jabber_registration_item", session_name(s), from_str, jname, jdata); xfree(jname); xfree(jdata); } print("jabber_form_end", session_name(s), from_str, "register"); } xfree(from_str); } static void jabber_handle_vcard_helper(session_t *s, const char *formatka, const char *data) { char *tmp; tmp = jabber_unescape(data); print(formatka, session_name(s), jabberfix(tmp, _("unknown"))); xfree(tmp); } /** * jabber_handle_vcard() * * XEP-0054: vcard-temp (http://www.xmpp.org/extensions/xep-0054.html) [1.1 2003-03-26] (iq:type='result' iq::vCard:xmlns='vcard-temp')
* * @todo Till 20 jan 2008 ekg2 display vCard in other format, (Check: jabber_userinfo_response format), and jabber_handle_vcard_old() * I think old version was extremly poor in support of DTD, so everyone will be happy to switch to new one, but SHOULD we allow user to switch between implementations? * For instance: when format "jabber_userinfo_response2" is not found? * * @todo Implement rest od DTD. * * @note From XEP-0054: * - The country abbreviation is contained in a element, not a element (even though this is at odds with draft-dawson-vcard-xml-dtd-01). * (ekg2 till (19 jan 2008) send country in , so I think we should use also old elemenet (even if we are broking DTD)) */ JABBER_HANDLER_RESULT(jabber_handle_vcard) { static const char vcardbrowser[] = "http://vcard.ekg2.org/"; char *from_str = jabber_unescape(from); char *tmp; char *photo_url = NULL; int ismuc = 0; /* Normally vCard is resource-independent, so we can strip it. * We also strip it to check whether it ain't conference UID. * If it is, then we shall leave the resource (as it points to user), * and not display vCard-daemon link (as it wouldn't work). */ if ((tmp = xstrchr(from_str, '/'))) { char *mucid; *tmp = 0; mucid = xmpp_uid(from_str); if ((ismuc = !!newconference_find(s, mucid))) *tmp = '/'; xfree(mucid); } print("jabber_userinfo_response2", session_name(s), jabberfix(from_str, _("unknown"))); for (n = n->children; n; n = n->next) { if (!xstrcmp(n->name, "FN")) jabber_handle_vcard_helper(s, "jabber_userinfo_fullname", n->data); else if (!xstrcmp(n->name, "NICKNAME")) jabber_handle_vcard_helper(s, "jabber_userinfo_nickname", n->data); else if (!xstrcmp(n->name, "BDAY")) jabber_handle_vcard_helper(s, "jabber_userinfo_birthday", n->data); else if (!xstrcmp(n->name, "URL")) jabber_handle_vcard_helper(s, "jabber_userinfo_url", n->data); else if (!xstrcmp(n->name, "DESC")) jabber_handle_vcard_helper(s, "jabber_userinfo_desc", n->data); else if (!xstrcmp(n->name, "TITLE")) jabber_handle_vcard_helper(s, "jabber_userinfo_title", n->data); else if (!xstrcmp(n->name, "PHOTO")) { xmlnode_t *q; for (q = n->children; q; q = q->next) { if (!xstrcmp(q->name, "EXTVAL") && q->data && *(q->data)) { xfree(photo_url); photo_url = xstrdup(q->data); break; } else if (!xstrcmp(q->name, "BINVAL") && q->data && *(q->data) && !ismuc) { xfree(photo_url); photo_url = saprintf("%s%s", vcardbrowser, from_str); break; } } } else if (!xstrcmp(n->name, "EMAIL")) { const char *userid = NULL; xmlnode_t *q; for (q = n->children; q; q = q->next) { if (!xstrcmp(q->name, "USERID")) userid = q->data; /* XXX: HOME, WORK, INTERNET [*] PREF, X400 */ else debug_error("vCard EMAIL/%s data: %s\n", __(q->name), __(q->data)); } jabber_handle_vcard_helper(s, "jabber_userinfo_email", userid); } else if (!xstrcmp(n->name, "ADR")) { const char *type = NULL; const char *street = NULL; const char *pcode = NULL; const char *city = NULL; const char *country = NULL; xmlnode_t *q; for (q = n->children; q; q = q->next) { if (!xstrcmp(q->name, "LOCALITY")) city = q->data; else if (!xstrcmp(q->name, "STREET")) street = q->data; else if (!xstrcmp(q->name, "PCODE")) pcode = q->data; else if (!xstrcmp(q->name, "CTRY") || !xstrcmp(q->name, "COUNTRY")) /* XXX, what about COUNTRY? [See NOTE] */ country = q->data; else if (!xstrcmp(q->name, "HOME")) type = _("Home"); else if (!xstrcmp(q->name, "WORK")) type = _("Work"); else debug_error("vCard ADR/%s data: %s\n", __(q->name), __(q->data)); } jabber_handle_vcard_helper(s, "jabber_userinfo_adr", type); if (street) jabber_handle_vcard_helper(s, "jabber_userinfo_adr_street", street); if (city) jabber_handle_vcard_helper(s, "jabber_userinfo_adr_city", city); if (pcode) jabber_handle_vcard_helper(s, "jabber_userinfo_adr_postalcode", pcode); if (country) jabber_handle_vcard_helper(s, "jabber_userinfo_adr_country", country); jabber_handle_vcard_helper(s, "jabber_userinfo_adr_end", ""); } else if (!xstrcmp(n->name, "TEL")) { const char *type = NULL; const char *number = NULL; xmlnode_t *q; for (q = n->children; q; q = q->next) { if (!xstrcmp(q->name, "NUMBER")) number = q->data; else if (!xstrcmp(q->name, "HOME")) type = _("Home"); else if (!xstrcmp(q->name, "WORK")) type = _("Work"); else debug_error("vCard TEL/%s data: %s\n", __(q->name), __(q->data)); } if (type) debug_error("XXX: vCard TEL type: %s\n", type); /* XXX */ jabber_handle_vcard_helper(s, "jabber_userinfo_telephone", number); } else if (!xstrcmp(n->name, "ORG")) { xmlnode_t *q; for (q = n->children; q; q = q->next) { if (!xstrcmp(q->name, "ORGNAME")) jabber_handle_vcard_helper(s, "jabber_userinfo_organization", q->data); else debug_error("vCard ORG/%s data: %s\n", __(q->name), __(q->data)); } } else debug_error("vCard n->name: %s data: %s\n", __(n->name), __(n->data)); } if (photo_url) { print("jabber_userinfo_photourl", photo_url); xfree(photo_url); } print("jabber_userinfo_end", session_name(s), jabberfix(from_str, _("unknown"))); xfree(from_str); } JABBER_HANDLER_RESULT(jabber_handle_vcard_old) { xmlnode_t *fullname = xmlnode_find_child(n, "FN"); xmlnode_t *nickname = xmlnode_find_child(n, "NICKNAME"); xmlnode_t *birthday = xmlnode_find_child(n, "BDAY"); xmlnode_t *adr = xmlnode_find_child(n, "ADR"); xmlnode_t *city = xmlnode_find_child(adr, "LOCALITY"); xmlnode_t *desc = xmlnode_find_child(n, "DESC"); char *from_str = (from) ? jabber_unescape(from) : NULL; char *fullname_str = (fullname) ? jabber_unescape(fullname->data) : NULL; char *nickname_str = (nickname) ? jabber_unescape(nickname->data) : NULL; char *bday_str = (birthday) ? jabber_unescape(birthday->data) : NULL; char *city_str = (city) ? jabber_unescape(city->data) : NULL; char *desc_str = (desc) ? jabber_unescape(desc->data) : NULL; print("jabber_userinfo_response", jabberfix(from_str, _("unknown")), jabberfix(fullname_str, _("unknown")), jabberfix(nickname_str, _("unknown")), jabberfix(bday_str, _("unknown")), jabberfix(city_str, _("unknown")), jabberfix(desc_str, _("unknown"))); xfree(desc_str); xfree(city_str); xfree(bday_str); xfree(nickname_str); xfree(fullname_str); xfree(from_str); } JABBER_HANDLER_RESULT(jabber_handle_iq_result_vacation) { xmlnode_t *temp; char *message = jabber_unescape( (temp = xmlnode_find_child(n, "message")) ? temp->data : NULL); char *begin = (temp = xmlnode_find_child(n, "start")) && temp->data ? temp->data : _("begin"); char *end = (temp = xmlnode_find_child(n, "end")) && temp->data ? temp->data : _("never"); print("jabber_vacation", session_name(s), message, begin, end); xfree(message); } JABBER_HANDLER_RESULT(jabber_handle_iq_muc_owner) { xmlnode_t *node; int formdone = 0; char *uid = jabber_unescape(from); for (node = n->children; node; node = node->next) { if (!xstrcmp(node->name, "x") && !xstrcmp("jabber:x:data", node->xmlns)) { if (!xstrcmp(jabber_attr(node->atts, "type"), "form")) { formdone = 1; jabber_handle_xmldata_form(s, uid, "admin", node->children, NULL); break; } } } // if (!formdone) ; // XXX xfree(uid); } JABBER_HANDLER_RESULT(jabber_handle_iq_muc_admin) { xmlnode_t *node; int count = 0; for (node = n->children; node; node = node->next) { if (!xstrcmp(node->name, "item")) { char *jid = jabber_attr(node->atts, "jid"); // char *aff = jabber_attr(node->atts, "affiliation"); xmlnode_t *reason = xmlnode_find_child(node, "reason"); char *rsn = reason ? jabber_unescape(reason->data) : NULL; print("jabber_muc_banlist", session_name(s), from, jid, rsn ? rsn : "", ekg_itoa(++count)); xfree(rsn); } } } JABBER_HANDLER_SET(jabber_handle_iq_set_new_mail) { jabber_private_t *j = s->priv; print("gmail_new_mail", session_name(s)); watch_write(j->send_watch, "", jabber_attr(n->atts, "id")); if (j->last_gmail_result_time && j->last_gmail_tid) watch_write(j->send_watch, "", j->id++, j->last_gmail_result_time, j->last_gmail_tid); else watch_write(j->send_watch, "", j->id++); } JABBER_HANDLER_SET(jabber_handle_si_set) { xmlnode_t *p; if (((p = xmlnode_find_child(n, "file")))) { /* JEP-0096: File Transfer */ dcc_t *D; char *uin = jabber_unescape(from); char *uid; char *filename = jabber_unescape(jabber_attr(p->atts, "name")); char *size = jabber_attr(p->atts, "size"); #if 0 xmlnode_t *range; /* unused? */ #endif jabber_dcc_t *jdcc; uid = xmpp_uid(uin); jdcc = xmalloc(sizeof(jabber_dcc_t)); jdcc->session = s; jdcc->req = xstrdup(id); jdcc->sid = jabber_unescape(jabber_attr(n->atts, "id")); jdcc->sfd = -1; D = dcc_add(s, uid, DCC_GET, NULL); dcc_filename_set(D, filename); dcc_size_set(D, atoi(size)); dcc_private_set(D, jdcc); dcc_close_handler_set(D, jabber_dcc_close_handler); /* XXX, result if ((range = xmlnode_find_child(p, "range"))) { char *off = jabber_attr(range->atts, "offset"); char *len = jabber_attr(range->atts, "length"); if (off) dcc_offset_set(D, atoi(off)); if (len) dcc_size_set(D, atoi(len)); } */ print("dcc_get_offer", format_user(s, uid), filename, size, ekg_itoa(dcc_id_get(D))); xfree(uin); xfree(uid); xfree(filename); } } JABBER_HANDLER_RESULT(jabber_handle_si_result) { jabber_private_t *j = s->priv; char *uin = jabber_unescape(from); dcc_t *d; if ((d = jabber_dcc_find(uin, id, NULL))) { xmlnode_t *node; jabber_dcc_t *p = d->priv; char *stream_method = NULL; for (node = n->children; node; node = node->next) { if (!xstrcmp(node->name, "feature") && !xstrcmp(node->xmlns, "http://jabber.org/protocol/feature-neg")) { xmlnode_t *subnode; for (subnode = node->children; subnode; subnode = subnode->next) { if (!xstrcmp(subnode->name, "x") && !xstrcmp(subnode->xmlns, "jabber:x:data") && !xstrcmp(jabber_attr(subnode->atts, "type"), "submit")) { /* var stream-method == http://jabber.org/protocol/bytestreams */ jabber_handle_xmldata_submit(s, subnode->children, NULL, 0, "stream-method", &stream_method, NULL); } } } } if (!xstrcmp(stream_method, "http://jabber.org/protocol/bytestreams")) p->protocol = JABBER_DCC_PROTOCOL_BYTESTREAMS; else debug_error("[JABBER] JEP-0095: ERROR, stream_method XYZ error: %s\n", stream_method); xfree(stream_method); if (p->protocol == JABBER_DCC_PROTOCOL_BYTESTREAMS) { struct jabber_streamhost_item streamhost; jabber_dcc_bytestream_t *b; list_t l; b = p->priv_data.bytestream = xmalloc(sizeof(jabber_dcc_bytestream_t)); b->validate = JABBER_DCC_PROTOCOL_BYTESTREAMS; if (jabber_dcc_ip && jabber_dcc) { /* basic streamhost, our ip, default port, our jid. check if we enable it. XXX*/ streamhost.jid = saprintf("%s/%s", s->uid+5, j->resource); streamhost.ip = xstrdup(jabber_dcc_ip); streamhost.port = jabber_dcc_port; list_add(&(b->streamlist), g_memdup(&streamhost, sizeof(struct jabber_streamhost_item))); } /* ... other, proxy, etc, etc.. streamhost.ip = .... streamhost.port = .... list_add(...); */ xfree(p->req); p->req = xstrdup(ekg_itoa(j->id++)); watch_write(j->send_watch, "" "", d->uid+5, p->req, p->sid); for (l = b->streamlist; l; l = l->next) { struct jabber_streamhost_item *item = l->data; watch_write(j->send_watch, "", item->port, item->ip, item->jid); } watch_write(j->send_watch, ""); } } else /* XXX */; } JABBER_HANDLER_RESULT(jabber_handle_bind) { jabber_private_t *j = s->priv; if (session_int_get(s, "__session_need_start") == 1) { watch_write(j->send_watch, "", j->id++); session_int_set(s, "__session_need_start", 0); } else debug_error("jabber_handle_bind() but not __session_need_start\n"); } JABBER_HANDLER_RESULT(jabber_handle_iq_result_generic) { debug_error("jabber_handle_iq_result_generic()\n"); } static const struct jabber_iq_generic_handler jabber_iq_result_handlers[] = { { "vCard", "vcard-temp", jabber_handle_vcard }, { "pubsub", "http://jabber.org/protocol/pubsub#event", jabber_handle_result_pubsub }, /* not done */ { "mailbox", "google:mail:notify", jabber_handle_gmail_result_mailbox }, /* not done */ { "new-mail", "google:mail:notify", jabber_handle_iq_result_new_mail }, /* not done */ { "si", NULL, jabber_handle_si_result }, /* not done */ { "query", "jabber:iq:last", jabber_handle_iq_result_last }, { NULL, "jabber:iq:privacy", jabber_handle_iq_result_privacy }, /* zaczete */ { NULL, "jabber:iq:private", jabber_handle_iq_result_private }, { NULL, "jabber:iq:register", jabber_handle_iq_result_register }, /* not done */ { NULL, "jabber:iq:roster", jabber_handle_iq_roster }, /* not done */ { NULL, "jabber:iq:search", jabber_handle_iq_result_search }, { NULL, "jabber:iq:version", jabber_handle_iq_result_version }, { NULL, "http://jabber.org/protocol/disco#info", jabber_handle_iq_result_disco_info }, { NULL, "http://jabber.org/protocol/disco#items", jabber_handle_iq_result_disco }, { NULL, "http://jabber.org/protocol/muc#admin", jabber_handle_iq_muc_admin }, /* bez ERROR */ { NULL, "http://jabber.org/protocol/muc#owner", jabber_handle_iq_muc_owner }, /* bez ERROR */ { NULL, "http://jabber.org/protocol/vacation", jabber_handle_iq_result_vacation }, /* done, but not checked, without ERROR */ { "bind", "urn:ietf:params:xml:ns:xmpp-bind", jabber_handle_bind }, /* not done */ { "", NULL, NULL } }; /* XXX: temporary hack: roster przychodzi jako typ 'set' (przy dodawaniu), jak * i typ "result" (przy zażądaniu rostera od serwera) */ /* niektore hacki zdecydowanie za dlugo... */ static const struct jabber_iq_generic_handler jabber_iq_set_handlers[] = { { "vCard", "vcard-temp", jabber_handle_vcard_old }, /* ??????? */ { "new-mail", "google:mail:notify", jabber_handle_iq_set_new_mail }, { "si", NULL, jabber_handle_si_set }, { "query", "jabber:iq:privacy", jabber_handle_iq_result_privacy }, /* XXX: przeniesc kod ktory przychodzi jako set do innej funkcji */ { NULL, "jabber:iq:roster", jabber_handle_iq_roster }, { "", NULL, NULL } }; // vim:syn=c ekg2-0.4~pre+20120506.1/plugins/jabber/jabber_handlers_tlen.inc000066400000000000000000000044571175142753400237170ustar00rootroot00000000000000/* * tlen_handle_notification() * * */ JABBER_HANDLER(tlen_handle_notification) { /* n->name: "m" TLEN only: typing, nottyping, and alert notifications */ char *type = jabber_attr(n->atts, "tp"); char *from = jabber_attr(n->atts, "f"); char *typeadd = jabber_attr(n->atts, "type"); if (!type || !from || (typeadd && !xstrcmp(typeadd, "error"))) { debug_error("tlen_handle() %d %s/%s/%s", __LINE__, type, from, typeadd); return; } if (!xstrcmp(type, "t") || !xstrcmp(type, "u")) { char *uid = tlen_uid(from); /* typing notification */ if (!xstrcmp(type, "u")) protocol_xstate_emit(s, uid, 0, EKG_XSTATE_TYPING); else protocol_xstate_emit(s, uid, EKG_XSTATE_TYPING, 0); xfree(uid); return; } if (!xstrcmp(type, "a")) { /* funny thing called alert */ char *uid = tlen_uid(from); print_info(uid, s, "tlen_alert", session_name(s), format_user(s, uid)); if (config_sound_notify_file) play_sound(config_sound_notify_file); else if (config_beep && config_beep_notify) query_emit(NULL, "ui-beep", NULL); xfree(uid); return; } } /* * tlen_handle_newmail() * */ JABBER_HANDLER(tlen_handle_newmail) { char *from = tlen_decode(jabber_attr(n->atts, "f")); char *subj = tlen_decode(jabber_attr(n->atts, "s")); print("tlen_mail", session_name(s), from, subj); newmail_common(s); xfree(from); xfree(subj); } /* * tlen_handle_webmessage() * * */ JABBER_HANDLER(tlen_handle_webmessage) { char *from = jabber_attr(n->atts, "f"); char *mail = jabber_attr(n->atts, "e"); char *content = n->data; string_t body = string_init(""); char *text; if (from || mail) { string_append(body, "From:"); if (from) { string_append_c(body, ' '); string_append(body, from); } if (mail) { string_append(body, " <"); string_append(body, mail); string_append_c(body, '>'); } string_append_c(body, '\n'); } if (body->len) string_append_c(body, '\n'); string_append(body, content); text = tlen_decode(body->str); string_free(body, 1); protocol_message_emit(s, "ludzie.tlen.pl", NULL, text, NULL, time(NULL), EKG_MSGCLASS_MESSAGE, NULL, EKG_TRY_BEEP, 0); xfree(text); } static const struct jabber_generic_handler tlen_handlers[] = { { "m", tlen_handle_notification }, { "n", tlen_handle_newmail }, { "w", tlen_handle_webmessage }, { NULL, NULL } }; // vim:syn=c ekg2-0.4~pre+20120506.1/plugins/jabber/misc.c000066400000000000000000000354411175142753400201710ustar00rootroot00000000000000/* $Id$ */ #include "ekg2.h" #include #include #include #ifdef HAVE_LIBZ # include "zlib.h" #endif #include "jabber.h" #include "jabber-ssl.h" /* XXX, It's the same function from mcjabber, but uses one buffor. */ static char *jabber_gpg_strip_header_footer(char *data) { char *p, *q; if (!data) return NULL; if (!(p = xstrstr(data, "\n\n"))) return data; p += 2; for (q = p ; *q; q++); for (q--; q > p && (*q != '\n' || *(q+1) != '-'); q--) ; if (q <= p) { debug_error("jabber_gpg_strip_header_footer() assert. shouldn't happen, happen!\n"); xfree(data); return NULL; } xstrncpy(data, p, q-p); data[q-p] = 0; return data; } char *jabber_openpgp(session_t *s, const char *fromto, enum jabber_opengpg_type_t way, char *message, char *key, char **error) { char *err = NULL; int ret = -2; char *oldkey = key; if (!message) return NULL; if (!s) return NULL; switch (way) { case JABBER_OPENGPG_ENCRYPT: ret = query_emit(NULL, "gpg-message-encrypt", &fromto, &message, &err); break; case JABBER_OPENGPG_DECRYPT: ret = query_emit(NULL, "gpg-message-decrypt", &s->uid, &message, &err); break; case JABBER_OPENGPG_SIGN: ret = query_emit(NULL, "gpg-sign", &s->uid, &message, &err); break; case JABBER_OPENGPG_VERIFY: ret = query_emit(NULL, "gpg-verify", &fromto, &message, &key, &err); break; /* @ KEY retval key-id */ } if (ret == -2) err = xstrdup("Load GPG plugin you moron."); /* if way == JABBER_OPENGPG_VERIFY than message is never NULL */ if (!message && !err) err = xstrdup("Bad password?"); if (way == JABBER_OPENGPG_VERIFY && !key && !err) err = xstrdup("wtf?"); if (err) debug_error("jabber_openpgp(): %s\n", err); if (error) *error = err; else xfree(err); if (err && way == JABBER_OPENGPG_VERIFY) { if (oldkey == key) { xfree(key); return NULL; } } else if (err) { xfree(message); return NULL; } if (way == JABBER_OPENGPG_SIGN || way == JABBER_OPENGPG_ENCRYPT) { message = jabber_gpg_strip_header_footer(message); } return way != JABBER_OPENGPG_VERIFY ? message : key; } #ifdef HAVE_LIBZ char *jabber_zlib_compress(const char *buf, int *len) { size_t destlen = (*len) * 1.01 + 12; char *compressed = xmalloc(destlen); if (compress((unsigned char *) compressed, &destlen, (unsigned char *) buf, *len) != Z_OK) { debug_error("jabber_zlib_compress() zlib compress() != Z_OK\n"); xfree(compressed); return NULL; } debug_function("jabber_handle_write() compress ok, retlen: %d orglen: %d\n", destlen, *len); *len = destlen; return compressed; } char *jabber_zlib_decompress(const char *buf, int *len) { #define ZLIB_BUF_SIZE 1024 z_stream zlib_stream; int err; size_t size = ZLIB_BUF_SIZE+1; int rlen = 0; unsigned char *uncompressed = NULL; zlib_stream.zalloc = Z_NULL; zlib_stream.zfree = Z_NULL; zlib_stream.opaque = Z_NULL; if ((err = inflateInit(&zlib_stream)) != Z_OK) { debug_error("[jabber] jabber_handle_stream() inflateInit() %d != Z_OK\n", err); return NULL; } zlib_stream.next_in = (unsigned char *) buf; zlib_stream.avail_in = *len; do { uncompressed = xrealloc(uncompressed, size); zlib_stream.next_out = uncompressed + rlen; zlib_stream.avail_out= ZLIB_BUF_SIZE; err = inflate(&zlib_stream, Z_NO_FLUSH); if (err != Z_OK && err != Z_STREAM_END) { debug_error("[jabber] jabber_handle_stream() inflate() %d != Z_OK && %d != Z_STREAM_END %s\n", err, err, zlib_stream.msg); break; } rlen += (ZLIB_BUF_SIZE - zlib_stream.avail_out); size += (ZLIB_BUF_SIZE - zlib_stream.avail_out); } while (err == Z_OK && zlib_stream.avail_out == 0); inflateEnd(&zlib_stream); uncompressed[rlen] = 0; *len = rlen; return (char *) uncompressed; } #endif int JABBER_COMMIT_DATA(watch_t *w) { #ifdef FIXME_WATCHES_TRANSFER_LIMITS if (w) { w->transfer_limit = 0; return watch_handle_write(w); } return -1; #else return 0; #endif } char *jabber_attr(char **atts, const char *att) { int i; if (!atts) return NULL; for (i = 0; atts[i]; i += 2) if (!xstrcmp(atts[i], att)) return atts[i + 1]; return NULL; } /** * jabber_escape() * * Convert charset from config_console_charset to "utf-8"
* Escape xml chars using xml_escape() * * @note If config_use_unicode is set, this function return only xml_escape(@a text) * * @param text - text to reencode+escape * * @sa jabber_unescape() - For function reconverting charset back to config_console_charset * * @return Dynamic allocated string, which should be xfree()'d */ char *jabber_escape(const char *text) { const char *utftext; char *res; if (!text) return NULL; utftext = ekg_locale_to_utf8_use(text); res = xml_escape(utftext); recode_xfree(text, utftext); return res; } /** * jabber_unescape() * * Convert charset from "utf-8" to config_console_charset.
* xml escaped chars are already changed by expat. so we don't care about them. * * @note If config_use_unicode is set, this function only xstrdup(@a text) * * @param text - text to reencode. * * @sa jabber_escape() - for function escaping xml chars + reencoding string to utf-8 * * @return Dynamic allocated string, which should be xfree()'d */ char *jabber_unescape(const char *text) { if (!text) return NULL; return ekg_utf8_to_core_dup(text); } /** * tlen_encode() * * Convert charset from config_console_charset to ISO-8859-2
* ,,encode'' string with urlencode * * @note It was ripped from libtlen. (c) Libtlen developers see: http://libtlen.sourceforge.net/ * * @todo Try to rewrite. * * @param what - string to encode. * * @sa tlen_decode() - for urldecode. * * @return Dynamic allocated string, which should be xfree()'d */ char *tlen_encode(const char *what) { unsigned char *s; unsigned char *ptr, *str; const char *text = NULL; if (!what) return NULL; text = ekg_locale_to_iso2_use(what); s = (unsigned char *) text; str = ptr = (unsigned char *) xcalloc(3 * xstrlen((char*) s) + 1, 1); while (*s) { if (*s == ' ') *ptr++ = '+'; else if ((*s < '0' && *s != '-' && *s != '.') || (*s < 'A' && *s > '9') || (*s > 'Z' && *s < 'a' && *s != '_') || (*s > 'z')) { sprintf((char*) ptr, "%%%02X", *s); ptr += 3; } else *ptr++ = *s; s++; } recode_xfree(what, text); return (char*) str; } /** * tlen_decode() * * Decode string ,,encoded'' with urldecode [in ISO-8859-2] and convert charset to config_console_charset
* * @note It was ripped from libtlen. (c) Libtlen developers see: http://libtlen.sourceforge.net/ * * @todo Try to rewrite * * @param what - string to decode. * * @sa tlen_encode() - for urlencode. * * @return Dynamic allocated string, which should be xfree()'d */ char *tlen_decode(const char *what) { unsigned char *dest, *data, *retval; if (!what) return NULL; dest = data = retval = (unsigned char *) xstrdup(what); while (*data) { if (*data == '+') *dest++ = ' '; else if ((*data == '%') && xisxdigit(data[1]) && xisxdigit(data[2])) { int code; sscanf((char*) (data + 1), "%2x", &code); if (code != '\r') *dest++ = (unsigned char) code; data += 2; } else *dest++ = *data; data++; } *dest = '\0'; return ekg_iso2_to_core((char *) retval); } /* * jabber_handle_write() * * obsuga moliwoci zapisu do socketa. wypluwa z bufora ile si da * i jeli co jeszcze zostanie, ponawia prb. */ WATCHER_LINE(jabber_handle_write) /* tylko gdy jest wlaczona kompresja lub TLS/SSL. dla zwyklych polaczen jest watch_handle_write() */ { jabber_private_t *j = data; char *compressed = NULL; int res = 0, len; if (type) { /* XXX, do we need to make jabber_handle_disconnect() or smth simillar? */ j->send_watch = NULL; return 0; } if ( #ifdef JABBER_HAVE_SSL !j->using_ssl && #endif !j->using_compress) { /* XXX ? */ debug_error("[jabber] jabber_handle_write() nor j->using_ssl nor j->using_compression.... wtf?!\n"); return 0; } len = xstrlen(watch); switch (j->using_compress) { case JABBER_COMPRESSION_NONE: case JABBER_COMPRESSION_LZW_INIT: case JABBER_COMPRESSION_ZLIB_INIT: break; case JABBER_COMPRESSION_ZLIB: #ifdef HAVE_LIBZ res = len; if (!(compressed = jabber_zlib_compress(watch, &len))) return 0; #else debug_error("[jabber] jabber_handle_write() compression zlib, but no zlib support.. you're joking, right?\n"); #endif break; case JABBER_COMPRESSION_LZW: /* XXX */ default: debug_error("[jabber] jabber_handle_write() unknown compression: %d\n", j->using_compress); } if (compressed) watch = (const char *) compressed; #ifdef JABBER_HAVE_SSL if (j->using_ssl) { res = SSL_SEND(j->ssl_session, watch, (size_t) len); #ifdef HAVE_LIBSSL /* OpenSSL */ if ((res == 0 && SSL_get_error(j->ssl_session, res) == SSL_ERROR_ZERO_RETURN)); /* connection shut down cleanly */ else if (res < 0) res = SSL_get_error(j->ssl_session, res); /* XXX, When an SSL_write() operation has to be repeated because of SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE, it must be repeated with the same arguments. */ #endif if (SSL_E_AGAIN(res)) { ekg_yield_cpu(); return 0; } if (res < 0) { print("generic_error", SSL_ERROR(res)); } xfree(compressed); return res; } #endif /* here we call write() */ write(fd, watch, len); xfree(compressed); return res; } /* conversations */ /** * jabber_conversation_find() searches session's conversation list for matching one. * * @param j - private data of session. * @param uid - UID of recipient. * @param subject - message subject (for non-threaded conversations). * @param thread - jabber thread ID, if threaded. * @param result - place to write address of jabber_conversation_t or NULL, if not needed. * @param can_add - if nonzero, we can create new conversation, if none match. * * @return Reply-ID of conversation. */ int jabber_conversation_find(jabber_private_t *j, const char *uid, const char *subject, const char *thread, jabber_conversation_t **result, const int can_add) { jabber_conversation_t *thr, *prev; const char *resubject = NULL; int i, l = 0; if (!thread && subject && !xstrncmp(subject, config_subject_reply_prefix, (l = xstrlen(config_subject_reply_prefix)))) resubject = subject + l; for (thr = j->conversations, prev = NULL, i = 1; thr && ((thread ? xstrcmp(thr->thread, thread) /* try to match the thread, if avail */ : (subject ? xstrcmp(thr->subject, subject) /* else try to match the subject... */ && xstrcmp(thr->subject, resubject) /* ...also with Re: prefix... */ && (xstrncmp(thr->subject, config_subject_reply_prefix, l) || xstrcmp(thr->subject+l, subject)) /* ...on both sides... */ : !!thr->subject)) || !uid || xstrcmp(thr->uid, uid)); /* ...and UID */ prev = thr, thr = thr->next, i++); /* <- that's third param for 'for' */ if (!thr && can_add) { /* haven't found anything, but can create something */ thr = xmalloc(sizeof(jabber_conversation_t)); thr->thread = xstrdup(thread); thr->uid = xstrdup(uid); /* IMPORTANT: thr->subject is maintained by message handler * Now I know why I haven't added it earlier here */ if (prev) prev->next = thr; else j->conversations = thr; } if (result) *result = thr; return i; } /** * jabber_conversation_get() is used to get conversation by its Reply-ID. * * @param j - private data of session. * @param n - Reply-ID. * * @return Pointer to jabber_conversation_t or NULL, when no conversation found. */ jabber_conversation_t *jabber_conversation_get(jabber_private_t *j, const int n) { jabber_conversation_t *thr; int i; for (thr = j->conversations, i = 1; thr && (i < n); thr = thr->next, i++); return thr; } /** * jabber_thread_gen() generates new thread-ID for outgoing messages. * * @param j - private data of session. * @param uid - recipient UID. * * @return New, session-unique thread-ID. */ char *jabber_thread_gen(jabber_private_t *j, const char *uid) { int i, k, n = 0; char *thread = NULL; /* just trying to find first free one */ for (i = jabber_conversation_find(j, NULL, NULL, NULL, NULL, 0), k = i; n != k; i++) { xfree(thread); thread = saprintf("thr%d-%8x-%8x", i, rand(), (unsigned int) time(NULL)); /* that should look gorgeous */ n = jabber_conversation_find(j, thread, NULL, uid, NULL, 0); debug("[jabber,thread_gen] i = %d, k = %d, n = %d, t = %s\n", i, n, k, thread); } return thread; } static inline guint32 jabber_formatchar(const char c) { if (c == '*') return EKG_FORMAT_BOLD; if (c == '_') return EKG_FORMAT_UNDERLINE; if (c == '/') return EKG_FORMAT_ITALIC; return 0; } /* detect whether formatchar is surrounded by space, * when beginning == NULL, check following chars, * else check preceding chars * mode: 0 - check for spaces * 1 - check for previous/following occurence */ static inline int jabber_fc_check(const char *curr, const char *beginning, const int mode) { const char c = *curr; while (beginning ? --curr >= beginning : *(++curr)) { if (mode ? *curr == c : isspace(*curr)) return 1; else if (!jabber_formatchar(*curr)) return 0; } return !mode; } /* currently parses message text and tries to add some formatting, * e.g. *bold* /italic/ * * some time may also parse XHTML */ guint32 *jabber_msg_format(const char *plaintext, const xmlnode_t *html) { guint32 *fmtstring = NULL, *p = NULL, *pf = NULL; const char *c; return NULL; /* disable, because of XXX: 1) slash-asterisk-slash test slash-asterisk-slash - first slash disabler is still italic 2) it collides with popular emoticons 3) and needs to be also updated to match more special chars than spaces - for example, question marks 4) and after that modification, it would match more popular emoticons 5) thou it sucks */ for (c = plaintext; *c; c++) { int enabling; int flag = jabber_formatchar(*c); if (p) { *p |= *pf; /* 'or' to not lose just-enabled formatting */ pf = p++; } if (flag) { enabling = (!pf || !(*pf & flag)); if (enabling) { const char *tmp = c+1; if (!jabber_fc_check(c, plaintext, 0)) /* ignore middle-of-a-word formatstrings */ continue; if (jabber_fc_check(c, NULL, 1)) /* use last formatchar in a row */ continue; do { /* check if formatstring is finished */ tmp = xstrchr(tmp, *c); if (tmp && !jabber_fc_check(tmp, NULL, 0)) tmp += 1; else break; } while (1); if (!tmp) continue; } else if (!jabber_fc_check(c, NULL, 0)) /* like above */ continue; else if (jabber_fc_check(c, plaintext, 1)) /* use first disabling formatchar in a row */ continue; if (!p) { fmtstring = xcalloc(xstrlen(plaintext), sizeof(guint32)); pf = &fmtstring[c - plaintext]; p = pf+1; } /* p is always +1 here */ if (enabling) *p |= flag; /* don't need to check p+1 - unfinished formatting check does it for us */ else *pf &= ~flag; } } return fmtstring; } /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/plugins/jabber/session-en.txt000066400000000000000000000105111175142753400217050ustar00rootroot00000000000000// Little hint of session variables for jabber plugin. // (c) copyright 2001-2003 wojtek kaniewski // (c) copyright 2004 piotr kupisiewicz // (c) copyright 2004 tomasz torcz allow_add_reply_id type: integer default value: 1 Whether ekg2 should add Reply-IDs for incoming messages: 0 - disabled 1 - add Reply-IDs for messages with threads 2 - add Reply-IDs for any message (excl. chats and likes) allow_remote_control type: integer default value: 0 Allow remote control. Possible values: 0 - disabled 1 - enabled for same jid(allow from different resources) 2 - enabled for allow_remote_control_jid 666 - enabled for all(DO NOT USE IT!!!) auto_auth type: integer default value: 0 Whether ekg2 should handle authorization request automagically. Variable bitmapped (i.e. you should sum up following values): 1 - accept subscribe requests 2 - accept unsubscribe requests 4 - deny subscribe requests 8 - deny unsubscribe requests 5 - ignore subscribe requests 10 - ignore unsubscribe requests 16 - when adding contacts, don't request auth auto_bookmark_sync type: bool default value: 0 Possible values: 0 - bookmarks will not sync 1 - bookmarks will sync every time, ekg2 connects to server auto_reconnect type: integer default value: 0 Possible values: 0 - no reconnect at all >0 - amount of seconds, how long ekg2 will wait to reconnect auto_find type: integer default value: 0 Possible values: 0 - auto_find disables >0 - finds out every vcard of every person, who sends us a message display_ctcp type: bool default value: 0 Possible values: 0 - if anyone sends a request(for example, to determine your version) it won't be displayed 1 - any requests will be displayed to you disable_sasl type: integer default value: 0 Possible values: 0 - sasl enabled 1 - use XEP-0078(Non-SASL Authentication) 2 - disable sasl at all display_notify type: integer default value: -1 -1 - use global display_notify variable. 0 - ignore status changes 1 - show all changes 2 - show only changes from unavailable to available and vice versa Setting ,,contacts'' variable to 2 takes precendence (status changes are hidden). display_server_features type: integer default value: 1 Set when server's features should be shown: 0 - never 1 - only when first connected 2 - always log_formats type: string default value: xml,simple Defines file formats to use when logging to file. See also: log_path variable msg_gen_thread type: bool default value: 0 Whether to automagically generate thread-IDs for outgoing messages without given thread. password type: string default value: none User password. Necessary while connecting. photo_hash type: string default value: none Sha-1 hash of your avatar. plaintext_passwd type: bool default value: 0 Determines whether password can be sent using plaintext (1) or digest (0) method. ping_server type: integer default value: 0 Possible values: 0 - ekg2 will not ping server >0 - amount of seconds between pings port type: integer default value: 5222 Server port. prefer_family type: integer default value: 0 Prefered address family(ipv4 of ipv6) Possible values: 0 - default(ipv4) AF_INET - ipv4 AF_INET6 - ipv6 priority type: integer default value: 5 Jabber server connection priority. resource type: string default value: ekg2 Jabber resource. server type: string default value: none Server address. Set only if it's other than server part of JID. ssl_port type: integer default value: 5223 Ciphered connection port. use_compression type: integer default value: 0 Possible values: 0 - data compression disabled 1 - data compression enabled use_ssl type: bool default value: 1 If set to 1 ekg2 will use ciphered connection. use_tls type: bool default value: 1 If set to 1 ekg2 will use ciphered connection. ver_client_name type: string default value: none Client name returned when being queried. ver_client_version type: string default value: none Client version returned when being queried. ver_os type: string default value: none OS information returned when being queried. $Id$ ekg2-0.4~pre+20120506.1/plugins/jabber/session-pl.txt000066400000000000000000000063251175142753400217260ustar00rootroot00000000000000// mały opis dostępnych zmiennych sesyjnych pluginu jabber // (c) copyright 2001-2003 wojtek kaniewski // (c) copyright 2004 piotr kupisiewicz // (c) copyright 2004 tomasz torcz allow_add_reply_id typ: liczba domyślna wartość: 1 Określa, czy ekg2 powinno dodawać tzw. Reply-ID dla wiadomości przychodzących: 0 - wyłączone 1 - tylko dla wiadomości wątkowanych (thread) 2 - dla wszystkich wiadomości (wyłączając chat, itp.) auto_auth typ: liczba domyślna wartość: 0 Określa, czy ekg2 ma automagicznie odpowiadać na prośby o autoryzację. Zmienna ta stanowi bitmapę (sumę następujących wartości): 1 - odpowiada twierdząco na żądania autoryzacji 2 - odpowiada twierdząco na żądania cofnięcia autoryzacji 4 - odpowiada przecząco na żądania autoryzacji 8 - odpowiada przecząco na żądania cofnięcia autoryzacji 5 - (1+4) ignoruje żądania autoryzacji 10 - (8+2) ignoruje żądania cofnięcia autoryzacji 16 - przy dodawaniu kontaktów, nie wysyła prośby o autoryzację display_notify typ: liczba domyślna wartość: -1 wartość -1 powoduje korzystanie z globalnej zmiennej. wartość 0 powoduje ignorowanie zmian stanu znajomych, wartość 1 powoduje wyświetlanie wszystkich zmian, wartość 2 wyświetla tylko zmiany z niedostępnego na dostępny i na odwrót. większy priorytet ma zmienna ,,contacts'', która przy wartości 2 ukrywa zmiany stanu. display_server_features typ: liczba domyślna wartość: 1 Określa kiedy powinny zostać pokazane obsługiwane przez serwer ficzery: 0 - nigdy 1 - tylko przy pierwszym połączeniu 2 - zawsze log_formats typ: tekst domyślna wartość: xml,simple Określa formaty, w jakich zapisywane są logi z rozmów. Patrz tez: zmienna "log_path" msg_gen_thread typ: bool domyślna wartość: 0 Określa, czy ekg2 będzie automagicznie generować identyfikatory wątku dla wiadomości bez podanego owego. password typ: tekst domyślna wartość: brak hasło użytkownika. niezbędne do połączenia z serwerem. plaintext_passwd typ: bool domyślna wartość: 0 określa, czy hasło ma być przesyłane do serwera jawnym tekstem (1), czy za pomocą skrótu kryptograficznego (0). port typ: liczba domyślna wartość: 5222 port jakiego mamy używać przy łączeniu priority typ: liczba domyślna wartość: 5 priorytet połączenia z serwerem jabbera. resource typ: tekst domyślna wartość: ekg2 zasób jabberowy. server typ: tekst domyślna wartość: brak adres serwera, z którym należy sie połączyć, jeśli jest on inny niż to wynika z Jabber ID użytkownika. ssl_port typ: liczba domyślna wartość: 5223 port używany przy połączeniu szyfrowanym. use_ssl typ: bool domyślna wartość: 1 określa, czy nawiązywać z serwerem połączenie szyfrowane. ver_client_name typ: tekst domyślna wartość: brak nazwa klienta zwracana przy zapytaniach o wersję ver_client_version typ: tekst domyślna wartość: brak wersja klienta zwracana przy zapytaniach o wersję ver_os typ: tekst domyślna wartość: brak system operacyjny zwracany przy zapytaniach o wersję $Id$ ekg2-0.4~pre+20120506.1/plugins/jabber/xmlnode.c000066400000000000000000000027701175142753400207030ustar00rootroot00000000000000/* $Id$ */ #include "ekg2.h" #include #include "jabber.h" static void xmlnode_free(xmlnode_t *n) { xmlnode_t *m; if (!n) return; for (m = n->children; m;) { xmlnode_t *cur = m; m = m->next; xmlnode_free(cur); } xfree(n->name); xfree(n->data); xfree(n->xmlns); g_strfreev(n->atts); xfree(n); } void xmlnode_handle_end(void *data, const char *name) { session_t *s = (session_t *) data; xmlnode_t *n; jabber_private_t *j; if (!s || !(j = s->priv) || !name) { debug_error("[jabber] xmlnode_handle_end() invalid parameters\n"); return; } if (!(n = j->node)) { /* XXX: dj, maybe we set some sessionvar here, * and then take a look at it before submitting PROTOCOL_DISCONNECTED * with some weird error? */ debug("[jabber] end tag within , ignoring\n"); return; } if (!n->parent) { jabber_handle(data, n); xmlnode_free(n); j->node = NULL; return; } else { j->node = n->parent; } } void xmlnode_handle_cdata(void *data, const char *text, int len) { session_t *s = (session_t *) data; jabber_private_t *j; xmlnode_t *n; int oldlen; if (!s || !(j = s->priv) || !text) { debug_error("[jabber] xmlnode_handle_cdata() invalid parameters\n"); return; } if (!(n = j->node)) return; oldlen = xstrlen(n->data); n->data = xrealloc(n->data, oldlen + len + 1); memcpy(n->data + oldlen, text, len); n->data[oldlen + len] = 0; } /* * Local Variables: * mode: c * c-file-style: "k&r" * c-basic-offset: 8 * indent-tabs-mode: t * End: */ ekg2-0.4~pre+20120506.1/plugins/jogger/000077500000000000000000000000001175142753400171135ustar00rootroot00000000000000ekg2-0.4~pre+20120506.1/plugins/jogger/drafts.c000066400000000000000000000244341175142753400205510ustar00rootroot00000000000000/* * (C) Copyright 2007 Michał Górny & EKG2 authors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #include #include #include #include #include #include #include #include #define JOGGER_KEYS_MAX 25 #define JOGGER_VALUES_MAX 14 /* 10 char-long don't use ':', because they're already on limit (longer ones are discarded) */ const char *utf_jogger_header_keys[JOGGER_KEYS_MAX] = { "tytul:", "temat:", "subject:", "tytuł:", NULL, "poziom:", "level:", NULL, /* 2 */ "tag:", NULL, /* 3 */ "kategoria:", "category:", "kategorie:", "categories", NULL, /* 4 */ "trackback:", NULL, /* 5 */ "tidy", NULL, /* 6 */ "komentarze", "comments:", NULL, /* 7 */ "miniblog:", NULL, /* 8 - deprecated */ NULL }; const char *utf_jogger_header_values[JOGGER_VALUES_MAX] = { "off", "no", "nie", "wylacz", "wyłącz", "on", "yes", "tak", "wlacz", "włącz", NULL, "jogger", NULL, NULL }; char *jogger_header_keys[JOGGER_KEYS_MAX]; char *jogger_header_values[JOGGER_VALUES_MAX]; void jogger_free_headers(int real_free) { int i; for (i = 0; i < JOGGER_KEYS_MAX; i++) { if (real_free) xfree(jogger_header_keys[i]); jogger_header_keys[i] = NULL; } for (i = 0; i < JOGGER_VALUES_MAX; i++) { if (real_free) xfree(jogger_header_values[i]); jogger_header_values[i] = NULL; } } void jogger_localize_headers() { int i; jogger_free_headers(1); for (i = 0; i < JOGGER_KEYS_MAX; i++) jogger_header_keys[i] = ekg_utf8_to_core_dup(utf_jogger_header_keys[i]); for (i = 0; i < JOGGER_VALUES_MAX; i++) jogger_header_values[i] = ekg_utf8_to_core_dup(utf_jogger_header_values[i]); } /** * jogger_checkoutfile() * * Tries to open given file (check), and reads it, if expected (checkout). * It is designed to be proof to special file problems (especially named pipe ones). * * @param file - filename to open. * @param data - pointer to store file contents or NULL, if don't want to read it. * @param len - pointer to store filelength or NULL, if not needed. * @param hash - pointer to store filehash or NULL, if not needed. * @param maxlen - maximum filesize to accept (not counting additional NUL) or 0, if n/a. * @param quiet - if set, don't output anything to __status. * * @return 0 on success, errno on failure. */ static int jogger_checkoutfile(const char *file, char **data, int *len, char **hash, const int maxlen, const int quiet) { static char jogger_hash[sizeof(int)*2+3]; int mylen, fs, fd; const char *fn = prepare_path_user(file); if (!fn) return EINVAL; if ((fd = open(fn, O_RDONLY|O_NONBLOCK)) == -1) { /* we use O_NONBLOCK to get rid of FIFO problems */ const int err = errno; if (err == ENXIO) printq("io_nonfile", file); else printq("io_cantopen", file, strerror(err)); return err; } { struct stat st; if ((fstat(fd, &st) == -1) || !S_ISREG(st.st_mode)) { close(fd); printq("io_nonfile", file); return EISDIR; /* nearest, I think */ } fs = st.st_size; } int bufsize = (fs ? (maxlen && fs > maxlen ? maxlen+1 : fs+1) : 0x4000); /* we leave 1 byte for additional NUL */ char *out = xmalloc(bufsize); void *p = out; int _read = 0, res; { int cf = fcntl(fd, F_GETFL); if (cf == -1) /* evil thing */ cf = 0; else cf &= ~O_NONBLOCK; fcntl(fd, F_SETFL, cf); } while ((res = read(fd, p, bufsize-_read))) { if (res == -1) { const int err = errno; if (err != EINTR && err != EAGAIN) { close(fd); printq("io_cantread", file, strerror(errno)); return err; } } else { _read += res; if (maxlen && _read > maxlen) { xfree(out); printq("io_toobig", file, ekg_itoa(_read > fs ? _read : fs), ekg_itoa(maxlen)); return EFBIG; } else if (_read == bufsize) { /* fs sucks? */ bufsize += 0x4000; out = xrealloc(out, bufsize); p = out+_read; } else p += res; } } close(fd); if (_read == 0) { xfree(out); printq("io_emptyfile", file); return EINVAL; /* like mmap() */ } else if (_read+1 != bufsize) { out = xrealloc(out, _read+1); out[_read] = 0; /* add NUL */ } mylen = xstrlen(out); if (fs && _read > fs) printq("io_expanded", file, ekg_itoa(_read), ekg_itoa(fs)); else if (_read < fs) printq("io_truncated", file, ekg_itoa(_read), ekg_itoa(fs)); if (_read > mylen) printq("io_binaryfile", file, ekg_itoa(mylen), ekg_itoa(_read)); if (len) *len = _read; /* I don't want to write my own hashing function, so using EKG2 one * it will fail to hash data after any \0 in file, if there're any * but we also aren't prepared to handle them */ if (hash) { char sizecont[8]; snprintf(sizecont, 8, "0x%%0%lux", sizeof(int)*2); snprintf(jogger_hash, sizeof(int)*2+3, sizecont, ekg_hash(out)); *hash = jogger_hash; } if (data) *data = out; else xfree(out); return 0; } #define WARN_PRINT(x) do { if (!outstarted) { outstarted++; print("jogger_warning"); } print(x, tmp); } while (0) COMMAND(jogger_prepare) { const char *fn = (params[0] ? params[0] : session_get(session, "entry_file")); int len; char *entry, *s, *hash; int seen = 0; int outstarted = 0; if (!fn) { printq("invalid_params", name, params[0]); return -1; } if (jogger_checkoutfile(fn, &entry, NULL, &hash, 0, quiet)) return -1; len = xstrlen(entry); s = entry; s += xstrspn(s, " \n\r"); /* get on to first real char */ while (*s == '(') { /* parse headers */ const char *sep = xstrchr(s, ':'); char *next = xstrchr(s, '\n'); if (next) *next = 0; const char *end = xstrrchr(s, ')'); if (next) *next = '\n'; char tmp[24]; /* longest correct key has 10 chars + '(' + \0 */ g_strlcpy(tmp, s, 20); xstrcpy(tmp+20, "..."); /* add ellipsis and \0 */ xstrtr(tmp, '\n', 0); if (!sep || !end || !next || (sep > end) || (end+1+xstrspn(end+1, " ") != next)) { WARN_PRINT("jogger_warning_brokenheader"); if (!next) s = entry+len; continue; } else if ((*(s+1) == ' ') || (*(sep-1) == ' ')) WARN_PRINT("jogger_warning_wrong_key_spaces"); else if (end-sep-1 <= xstrspn(sep+1, " ")) WARN_PRINT("jogger_warning_wrong_value_empty"); else { int i = 1; const char **p = (const char **) (sep-s < 12 ? jogger_header_keys : NULL); for (; *p; i++, p++) { /* awaiting second NULL here */ for (; *p; p++) { /* awaiting single NULL here */ if (!xstrncasecmp(tmp+1, *p, xstrlen(*p))) { if (seen & (1< end)) && firstspace && (firstspace < end)) WARN_PRINT("jogger_warning_spacesep"); else if (i == 4) { char *values = xstrndup(sep+1, end-sep-1); if (cssfind(values, "techblog", ',', 1) && cssfind(values, "miniblog", ',', 1)) WARN_PRINT("jogger_warning_miniblog_techblog"); else if (cssfind(values, "techblog", 0, 1)) WARN_PRINT("jogger_warning_techblog_only"); xfree(values); } } else if (i == 5) { const char *first = sep+1+xstrspn(sep+1, " "); if (xstrncmp(first, "http://", 7) && xstrncmp(first, "https://", 8)) /* XXX: https trackbacks? */ WARN_PRINT("jogger_warning_malformed_url"); } else if (i == 6 || i == 7) { const int jmax = i-5; int j = 1; char *myval = xstrndup(sep+1, end-sep-1); const char **q = (const char **) jogger_header_values; for (; *q && j <= jmax; j++, q++) { /* second NULL or jmax */ for (; *q; q++) { /* first NULL */ if (!xstrcasecmp(myval, *q)) break; } if (*q) break; } if (!*q || (j > jmax)) { char *endval; int n = strtol(myval, &endval, 10); if (n == 0 && endval == myval) { if (*myval == ' ' || *(myval+xstrlen(myval)-1) == ' ') WARN_PRINT("jogger_warning_wrong_value_spaces"); else WARN_PRINT("jogger_warning_wrong_value"); } if (n > jmax) WARN_PRINT("jogger_warning_wrong_value"); } xfree(myval); } else if (i == 8) WARN_PRINT("jogger_warning_deprecated_miniblog"); } s = next+1; } s += xstrspn(s, " \n\r"); /* get on to first real char (again) */ if (*s == '(') { char tmp[14]; g_strlcpy(tmp, s, 10); xstrcpy(tmp+10, "..."); xstrtr(tmp, '\n', 0); WARN_PRINT("jogger_warning_mislocated_header"); } if (!xstrstr(s, "") && (len - (s-entry) > 4096)) { char tmp[21]; g_strlcpy(tmp, s+4086, 20); tmp[20] = 0; xstrtr(tmp, '\n', ' '); /* sanitize */ WARN_PRINT("jogger_warning_noexcerpt"); } xfree(entry); if (params[0]) session_set(session, "entry_file", params[0]); session_set(session, "entry_hash", hash); printq("jogger_prepared", fn); return 0; } COMMAND(jogger_publish) { const char *fn = (params[0] ? params[0] : session_get(session, "entry_file")); const char *oldhash = (!xstrcmp(session_get(session, "entry_file"), fn) ? session_get(session, "entry_hash") : NULL); char *entry, *hash; if (!fn) { printq("jogger_notprepared"); return -1; } if (jogger_checkoutfile(fn, &entry, NULL, &hash, 0, quiet)) return -1; if (oldhash && xstrcmp(oldhash, hash)) { print("jogger_hashdiffers"); xfree(entry); session_set(session, "entry_hash", hash); return -1; } command_exec("jogger:", session, entry, 0); xfree(entry); if (!oldhash) { session_set(session, "entry_hash", hash); session_set(session, "entry_file", fn); } return 0; } ekg2-0.4~pre+20120506.1/plugins/jogger/jogger.c000066400000000000000000000262651175142753400205470ustar00rootroot00000000000000 /* * (C) Copyright 2007 Michał Górny & EKG2 authors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #define JOGGER_DATE "2007-05-04" static int jogger_theme_init(void); int jogger_plugin_init(int prio); static int jogger_plugin_destroy(void); /* messages.c */ void jogger_localize_texts(); void jogger_free_texts(int real_free); QUERY(jogger_msghandler); COMMAND(jogger_msg); COMMAND(jogger_subscribe); /* drafts.c */ void jogger_localize_headers(); void jogger_free_headers(int real_free); COMMAND(jogger_prepare); COMMAND(jogger_publish); /* we need to be 'protocol' to establish sessions */ PLUGIN_DEFINE(jogger, PLUGIN_PROTOCOL, jogger_theme_init); /** * jogger_session_find_uid() tries to find Jogger session connected with given session (@a s) and @a uid. * * @return Session pointer or NULL if none match. */ session_t *jogger_session_find_uid(session_t *s, const char *uid) { session_t *js; for (js = sessions; js; js = js->next) { if (js->plugin == &jogger_plugin) { const char *jsw = session_get(js, "used_session"); if (jsw && (!xstrcasecmp(jsw, session_uid_get(s)) || !xstrcasecmp(jsw, session_alias_get(s)))) { const char *jsu = session_get(js, "used_uid"); const char *nickname = get_nickname(s, uid); if (!xstrcasecmp(uid, jsu) || (nickname && !xstrcasecmp(nickname, jsu))) /* yeah, that's it! */ return js; } } } return NULL; } static QUERY(jogger_validate_uid) { const char *uid = *(va_arg(ap, const char **)); int *valid = va_arg(ap, int *); if (uid && !xstrncmp(uid, "jogger:", 7)) { (*valid)++; return -1; } return 0; } static QUERY(jogger_statuschanged) { const char *suid = *(va_arg(ap, const char **)); const char *uid = *(va_arg(ap, const char **)); const int status = *(va_arg(ap, const int *)); session_t *s = session_find(suid); session_t *js; if (!s || !uid || !status) return 0; if ((js = jogger_session_find_uid(s, uid))) { session_connected_set(js, (status > EKG_STATUS_NA)); session_status_set(js, status); } return 0; } static QUERY(jogger_statuscleanup) { const char *suid = *(va_arg(ap, const char **)); session_t *s = session_find(suid); session_t *js; if (!s) return 0; for (js = sessions; js; js = js->next) { if (js->plugin == &jogger_plugin) { const char *jsw = session_get(js, "used_session"); if (session_connected_get(js) && jsw && (!xstrcasecmp(jsw, session_uid_get(s)) || !xstrcasecmp(jsw, session_alias_get(s)))) { session_connected_set(js, 0); session_status_set(js, EKG_STATUS_NA); } } } return 0; } static void jogger_usedchanged(session_t *s, const char *varname) { const char *tmp, *tmpb; /* first check if session is correct */ session_t *js = session_find((tmp = session_get(s, "used_session"))); if (!js) { #if 0 print("jogger_wrong_session", tmp); session_connected_set(s, 0); session_status_set(s, EKG_STATUS_ERROR); #endif return; } if (xstrcmp((tmpb = session_uid_get(js)), tmp)) /* replace alias with UID */ session_set(s, "used_session", tmpb); /* then check the UID */ if (!(tmpb = get_uid(js, (tmp = session_get(s, "used_uid"))))) { #if 0 print("jogger_wrong_uid", tmp); session_connected_set(s, 0); session_status_set(s, EKG_STATUS_ERROR); #endif return; } if (xstrcmp(tmpb, tmp)) /* replace nickname with UID */ session_set(s, "used_uid", tmpb); { /* update status */ userlist_t *u = userlist_find(js, tmpb); if (session_connected_get(s) != (u && (u->status > EKG_STATUS_NA))) { session_connected_set(s, (u && (u->status > EKG_STATUS_NA))); session_status_set(s, (u ? u->status : EKG_STATUS_NA)); } } } /* we need some dummy commands, e.g. /disconnect */ static COMMAND(jogger_null) { return 0; } static QUERY(jogger_print_version) { print("jogger_version", JOGGER_DATE); return 0; } static QUERY(jogger_newsession) { char *suid = *(va_arg(ap, char **)); session_t *js = session_find(suid); if (!js || (js->plugin != &jogger_plugin)) return 1; userlist_read(js); return 0; } static QUERY(jogger_postconfig) { session_t *js; /* note: converters init only one-side converting (utf-8 ==> locale) recoders can't do it ;/ */ ekg_recode_utf8_inc(); jogger_localize_texts(); jogger_localize_headers(); ekg_recode_utf8_dec(); for (js = sessions; js; js = js->next) { if ((js->plugin == &jogger_plugin) && !session_int_get(js, "userlist_keep")) userlist_free(js); } return 0; } static int jogger_theme_init(void) { #ifndef NO_DEFAULT_THEME format_add("jogger_noentry", _("%> (%1) No thread with id %c%2%n found."), 1); format_add("jogger_subscribed", _("%> %|(%1) The thread %T%2%n has been subscribed."), 1); format_add("jogger_unsubscribed", _("%> %|(%1) The thread %T%2%n has been unsubscribed."), 1); format_add("jogger_subscription_denied", _("%! (%1) Subscription denied because of no permission."), 1); format_add("jogger_unsubscribed_earlier", _("%> (%1) The thread weren't subscribed."), 1); format_add("jogger_comment_added", _("%) %|(%1) Your comment was added to entry %c%2%n."), 1); format_add("jogger_modified", _("%> %|(%1) Subscribed entry has been modified: %c%2%n."), 1); format_add("jogger_published", _("%) %|(%1) Your new entry has been published as: %c%2%n."), 1); format_add("jogger_posting_denied", _("%! (%1) Comment posting denied because of no permission."), 1); format_add("jogger_version", _("%> %TJogger:%n match data %g%1%n."), 1); format_add("jogger_prepared", _("%) File %T%1%n is ready for submission."), 1); format_add("jogger_notprepared", _("%! No filename given and no entry prepared!"), 1); format_add("jogger_hashdiffers", _("%! %|File contents (checksum) differs from the time it was prepared. If you changed anything in the entry file, please run %Tprepare%n again. If you want to force submission, please use %Tpublish%n again."), 1); format_add("jogger_warning", _("%) %|During QA check of the entry, following warnings have been issued:"), 1); format_add("jogger_warning_brokenheader", _("%> %|* Header with broken syntax found at: %c%1%n"), 1); format_add("jogger_warning_wrong_key", _("%> %|* Header contains unknown/wrong key at: %c%1%n"), 1); format_add("jogger_warning_wrong_key_spaces", _("%> %|* Key in header mustn't be followed or preceded by spaces at: %c%1%n"), 1); format_add("jogger_warning_deprecated_miniblog", _("%> %|* Key %Tminiblog%n is deprecated in favor of such category at: %c%1%n"), 1); format_add("jogger_warning_miniblog_techblog", _("%> %|* Miniblog entry will be posted to Techblog at: %c%1%n"), 1); format_add("jogger_warning_techblog_only", _("%> * Entries posted to Techblog should have also some normal category: %c%1%n"), 1); format_add("jogger_warning_malformed_url", _("%> %|* Malformed URL found at: %c%1%n"), 1); format_add("jogger_warning_spacesep", _("%> %|* Possibility of accidentially using space as a separator instead of commas: %c%1%n"), 1); format_add("jogger_warning_wrong_value", _("%> %|* Incorrect value found at: %c%1%n"), 1); format_add("jogger_warning_wrong_value_level", _("%> %|* Wrong %Tlevel%n found (level %Tnumber%n should be used), entry would be published on %Tzeroth%n level (not default) at: %c%1%n"), 1); format_add("jogger_warning_wrong_value_spaces", _("%> %|* Incorrent value found (try to remove leading&trailing spaces) at: %c%1%n"), 1); format_add("jogger_warning_wrong_value_empty", _("%> %|* Empty value found in header at: %c%1%n"), 1); format_add("jogger_warning_duplicated_header", _("%> %|* Duplicated header found at: %c%1%n"), 1); format_add("jogger_warning_mislocated_header", _("%> %|* Mislocated header (?) at: %c%1%n"), 1); format_add("jogger_warning_noexcerpt", _("%> %|* Entry text size exceeds 4096 bytes, but no tag has been found. It will be probably cut by Jogger near: ...%c%1%n..."), 1); #endif return 0; } static plugins_params_t jogger_plugin_vars[] = { PLUGIN_VAR_ADD("entry_file", VAR_STR, NULL, 0, NULL), PLUGIN_VAR_ADD("entry_hash", VAR_STR, NULL, 0, NULL), PLUGIN_VAR_ADD("ignore_outgoing_entries",VAR_BOOL, "0", 0, NULL), PLUGIN_VAR_ADD("log_formats", VAR_STR, "simple,sqlite", 0, NULL), PLUGIN_VAR_ADD("newentry_open_query", VAR_BOOL, "1", 0, NULL), PLUGIN_VAR_ADD("own_commentformat", VAR_STR, NULL, 0, NULL), PLUGIN_VAR_ADD("own_commentformat_autodetect", VAR_BOOL, "1", 0, NULL), PLUGIN_VAR_ADD("used_session", VAR_STR, NULL, 0, jogger_usedchanged), PLUGIN_VAR_ADD("used_uid", VAR_STR, NULL, 0, jogger_usedchanged), PLUGIN_VAR_ADD("userlist_keep", VAR_BOOL, "0", 0, NULL), PLUGIN_VAR_END() }; static const char *jogger_protocols[] = { "jogger:", NULL }; static const struct protocol_plugin_priv jogger_priv = { .protocols = jogger_protocols }; int jogger_plugin_init(int prio) { PLUGIN_CHECK_VER("jogger"); jogger_plugin.params = jogger_plugin_vars; jogger_plugin.priv = &jogger_priv; query_connect(&jogger_plugin, "plugin-print-version", jogger_print_version, NULL); query_connect(&jogger_plugin, "protocol-validate-uid", jogger_validate_uid, NULL); query_connect(&jogger_plugin, "protocol-status", jogger_statuschanged, NULL); query_connect(&jogger_plugin, "protocol-disconnected", jogger_statuscleanup, NULL); query_connect(&jogger_plugin, "protocol-message", jogger_msghandler, NULL); query_connect(&jogger_plugin, "session-added", jogger_newsession, NULL); query_connect(&jogger_plugin, "config-postinit", jogger_postconfig, NULL); #define JOGGER_CMDFLAGS SESSION_MUSTBELONG #define JOGGER_CMDFLAGS_TARGET SESSION_MUSTBELONG|COMMAND_ENABLEREQPARAMS|COMMAND_PARAMASTARGET command_add(&jogger_plugin, "jogger:", "?", jogger_msg, JOGGER_CMDFLAGS, NULL); command_add(&jogger_plugin, "jogger:chat", "!uU !", jogger_msg, JOGGER_CMDFLAGS_TARGET, NULL); command_add(&jogger_plugin, "jogger:connect", NULL, jogger_null, JOGGER_CMDFLAGS, NULL); command_add(&jogger_plugin, "jogger:disconnect", NULL, jogger_null, JOGGER_CMDFLAGS, NULL); command_add(&jogger_plugin, "jogger:msg", "!uU !", jogger_msg, JOGGER_CMDFLAGS_TARGET, NULL); command_add(&jogger_plugin, "jogger:prepare", "?f", jogger_prepare, JOGGER_CMDFLAGS, NULL); command_add(&jogger_plugin, "jogger:publish", "?f", jogger_publish, JOGGER_CMDFLAGS, NULL); command_add(&jogger_plugin, "jogger:reconnect", NULL, jogger_null, JOGGER_CMDFLAGS, NULL); command_add(&jogger_plugin, "jogger:subscribe", "!uU", jogger_subscribe, JOGGER_CMDFLAGS_TARGET, NULL); command_add(&jogger_plugin, "jogger:unsubscribe", "!uU", jogger_subscribe, JOGGER_CMDFLAGS_TARGET, NULL); #undef JOGGER_CMDFLAGS_TARGET #undef JOGGER_CMDFLAGS jogger_free_texts(0); /* set NULLs */ plugin_register(&jogger_plugin, prio); return 0; } static int jogger_plugin_destroy(void) { plugin_unregister(&jogger_plugin); jogger_free_texts(1); jogger_free_headers(1); return 0; } ekg2-0.4~pre+20120506.1/plugins/jogger/messages.c000066400000000000000000000223301175142753400210660ustar00rootroot00000000000000/* * (C) Copyright 2007 Michał Górny & EKG2 authors * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #include #include #define JOGGER_TEXT_MAX 14 /* jogger.c */ session_t *jogger_session_find_uid(session_t *s, const char *uid); const char *utf_jogger_text[JOGGER_TEXT_MAX] = { "Do Twojego joggera został dodany komentarz", /* [0] url (#eid[ / Texti*])\n----------------\n */ "Pojawił się nowy komentarz do wpisu", /* [1] as above */ "Wpis", /* [2] */ "został zmodyfikowany", /* url [3] */ "nie istnieje", /* n [4] */ "Dodano wpis:", /* [5] url */ "Dodałeś komentarz do wpisu", /* [6] url */ "Śledzenie wpisu", /* [7] url + below */ "zostało wyłączone", /* [8] */ "zostało włączone", /* [9] */ "Brak uprawnień do śledzenia tego wpisu", /* [10] */ "Wpis nie był śledzony", /* [11] */ "Do śledzonego joggera został dodany nowy wpis:", /* [12] url (#eid[ / Texti*]) */ "Brak uprawnień do komentowania tego wpisu!" /* [13] */ }; char *jogger_text[JOGGER_TEXT_MAX]; void jogger_free_texts(int real_free) { int i; for (i = 0; i < JOGGER_TEXT_MAX; i++) { if (real_free) xfree(jogger_text[i]); jogger_text[i] = NULL; } } void jogger_localize_texts() { int i; jogger_free_texts(1); for (i = 0; i < JOGGER_TEXT_MAX; i++) jogger_text[i] = ekg_utf8_to_core_dup(utf_jogger_text[i]); } QUERY(jogger_msghandler) { const char *suid = *(va_arg(ap, const char **)); const char *uid = *(va_arg(ap, char **)); char **rcpts = *(va_arg(ap, char ***)); const char *msg = *(va_arg(ap, char **)); guint32 **UNUSED(format) = va_arg(ap, guint32 **); time_t sent = *(va_arg(ap, const time_t *)); const int class = *(va_arg(ap, const int *)); const char *seq = *(va_arg(ap, char**)); const int dobeep = *(va_arg(ap, int*)); const int secure = *(va_arg(ap, int*)); session_t *s = session_find(suid); session_t *js; if (class == EKG_MSGCLASS_MESSAGE || class == EKG_MSGCLASS_CHAT) { /* incoming */ if (!s || !(js = jogger_session_find_uid(s, uid))) return 0; const char *owncf = session_get(js, "own_commentformat"); const int owncfau = atoi(session_get(js, "own_commentformat_autodetect")); int found = 0; char *tmp; if (!xstrncmp(msg, jogger_text[0], xstrlen(jogger_text[0]))) found = 1; /* own jogger comment */ else if (!xstrncmp(msg, jogger_text[1], xstrlen(jogger_text[1]))) found = 2; /* other jogger comment */ else if (owncf && !xstrncmp(msg, owncf, xstrlen(owncf))) found = 3; /* own jogger comment with custom text */ else if (!xstrncmp(msg, jogger_text[12], xstrlen(jogger_text[12]))) found = 4; /* new jogger entry */ else if (!xstrncmp(msg, jogger_text[2], xstrlen(jogger_text[2]))) { if ((tmp = xstrstr(msg, jogger_text[3]))) found = 5; else if ((tmp = xstrstr(msg, jogger_text[4]))) found = 6; } else if (!xstrncmp(msg, jogger_text[7], xstrlen(jogger_text[7]))) { if ((tmp = xstrstr(msg, jogger_text[8]))) found = 7; else if ((tmp = xstrstr(msg, jogger_text[9]))) found = 8; } else if (!xstrncmp(msg, jogger_text[5], xstrlen(jogger_text[5]))) found = 9; else if (!xstrncmp(msg, jogger_text[6], xstrlen(jogger_text[6]))) found = 10; else if (!xstrncmp(msg, jogger_text[10], xstrlen(jogger_text[10]))) found = 11; else if (!xstrncmp(msg, jogger_text[11], xstrlen(jogger_text[11]))) found = 12; else if (!xstrncmp(msg, jogger_text[13], xstrlen(jogger_text[13]))) found = 13; if (found <= 4) { /* we get id here */ const char *tmp = xstrstr(msg, " (#"); au_retry: if (!tmp) found = 0; else if (owncfau && !found) { /* own commentformat autodetect & bug workaround */ char *urltmp = xstrstr(msg, " http://"); if (urltmp && urltmp < tmp) { *urltmp = 0; owncf = msg; found = 3; } } const int oq = ((found > 0) && (session_int_get(js, "newentry_open_query") || (found < 4))); char *suid, *uid, *lmsg, *url; char **rcpts = NULL; guint32 *fmt = NULL; if (found == 4) { lmsg = xstrdup(msg+xstrlen(jogger_text[12])+1); url = xstrndup(lmsg, tmp-msg-xstrlen(jogger_text[12])-1); } else if (found == 0) { lmsg = xstrdup(msg); url = NULL; } else { url = (char *) msg+xstrlen(found == 3 ? owncf : jogger_text[found-1])+1; if (!(lmsg = xstrchr(tmp, '\n')) || !(lmsg = xstrchr(lmsg+1, '\n'))) return 0; lmsg = xstrdup(lmsg+1); url = xstrndup(url, tmp-url); } if (url && xstrncmp(url, "http://", 7)) { /* if URL is invalid, we probably have partial match */ xfree(url); xfree(lmsg); if (owncfau) { found = 0; goto au_retry; } lmsg = xstrdup(msg); url = NULL; } if (oq && url) uid = saprintf("jogger:%d", atoi(tmp+3)); else uid = xstrdup("jogger:"); suid = xstrdup(session_uid_get(js)); if (url) { userlist_t *u = userlist_find(js, uid); if (!u) userlist_add(js, uid, url); else if (xstrcmp(u->nickname, url)) { xfree(u->nickname); u->nickname = url; } else xfree(url); } query_emit(NULL, "protocol-message", &suid, &uid, &rcpts, &lmsg, &fmt, &sent, &class, &seq, &dobeep, &secure); xfree(suid); xfree(uid); xfree(lmsg); } else if (found <= 8) { const char *formats[] = { "jogger_modified", "jogger_noentry", "jogger_unsubscribed", "jogger_subscribed" }; const char *url = msg+xstrlen(found > 6 ? jogger_text[7] : jogger_text[2])+1; char *id = (found == 6 ? saprintf("jogger:%d", atoi(url+1)) : NULL); *(tmp-1) = '\0'; print_info((id ? id : url), js, formats[found-5], session_name(js), url); *(tmp-1) = ' '; xfree(id); } else if (found <= 10) { const char *formats[] = { "jogger_published", "jogger_comment_added" }; const char *url = msg+xstrlen(jogger_text[found-4])+1; print_info(url, js, formats[found-9], session_name(js), url); } else if (found <= 13) { const char *formats[] = { "jogger_subscription_denied", "jogger_unsubscribed_earlier", "jogger_posting_denied" }; print(formats[found-11], session_name(js)); } return -1; } else if (class == EKG_MSGCLASS_SENT || class == EKG_MSGCLASS_SENT_CHAT) { /* outgoing */ if (!s || !rcpts || !rcpts[0] || !(js = jogger_session_find_uid(s, rcpts[0]))) return 0; char *suid, *uid; char *lmsg = (char*) msg; char *rcpts[2] = { NULL, NULL }; char **rcptsb = rcpts; /* this is REALLY necessary, please do not remove or else we get segv */ guint32 *fmt = NULL; if (*lmsg == '#') { int n; char *tmp; if ((*(++lmsg) == '-') || (*lmsg == '+')) /* throw away subscriptions */ return -1; if ((n = atoi(lmsg)) && (tmp = xstrchr(lmsg, ' '))) { /* comment */ lmsg = tmp+1; rcpts[0] = saprintf("jogger:%d", n); } } else if (session_int_get(js, "ignore_outgoing_entries")) return -1; lmsg = xstrdup(lmsg); suid = xstrdup(session_uid_get(js)); uid = xstrdup(suid); if (!rcpts[0]) rcpts[0] = xstrdup("jogger:"); query_emit(NULL, "protocol-message", &suid, &uid, &rcptsb, &lmsg, &fmt, &sent, &class, &seq, &dobeep, &secure); xfree(rcpts[0]); xfree(uid); xfree(suid); xfree(lmsg); return -1; } return 0; } COMMAND(jogger_msg) { const int is_inline = (*name == '\0'); const char *uid = get_uid(session, target); session_t *js = session_find(session_get(session, "used_session")); const char *juid = session_get(session, "used_uid"); const char *msg = (is_inline ? params[0] : params[1]); int n; if (!params[0]) /* we don't print anything, because it is only possible with inline_msg */ return 0; if (!uid || !js || !juid) { printq("invalid_session"); /* XXX, unprepared session? */ return -1; } uid += 7; /* skip jogger: */ if (*uid == '\0') { /* redirect message to jogger uid */ if (is_inline) return command_exec(juid, js, msg, 0); else return command_exec_format(NULL, js, 0, "/%s \"%s\" %s", name, juid, msg); } if (*uid == '#') uid++; if (!(n = atoi(uid))) { printq("invalid_uid"); return -1; } { /* skip #eid if (unnecessarily) added */ char *tmp = saprintf("#%d ", n); if (!xstrncmp(msg, tmp, xstrlen(tmp))) msg += xstrlen(tmp); xfree(tmp); } /* post as comment-reply */ if (is_inline) return command_exec_format(juid, js, 0, "#%d %s", n, msg); else return command_exec_format(NULL, js, 0, "/%s \"%s\" #%d %s", name, juid, n, msg); } COMMAND(jogger_subscribe) { const char *uid = get_uid(session, target); int n; if (!uid) uid = target; /* try also without jogger: prefix */ else uid += 7; if (*uid == '#') uid++; if (!(n = atoi(uid))) { printq("invalid_uid"); return -1; } return command_exec_format("jogger:", session, 0, "#%c%d", (!xstrcmp(name, "subscribe") ? '+' : '-'), n); } ekg2-0.4~pre+20120506.1/plugins/logs/000077500000000000000000000000001175142753400166025ustar00rootroot00000000000000ekg2-0.4~pre+20120506.1/plugins/logs/main.c000066400000000000000000001077301175142753400177020ustar00rootroot00000000000000/* $Id$ */ /* * (C) Copyright 2003-2005 Tomasz Torcz * Leszek Krupiski * Adam Kuczyski * Adam Mikuta * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License Version * 2.1 as published by the Free Software Foundation. * * This program is distributed in the hope that 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. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "ekg2.h" #ifdef __APPLE__ #define _DARWIN_C_SOURCE #include #endif #if defined(__MINGW32__) || defined(__FreeBSD__) || defined(__sun) #include #endif #include #include #ifndef NO_POSIX_SYSTEM #include #include #endif #include #include #include #ifdef HAVE_LIBZ #include #endif #include "main.h" #undef HAVE_LIBZ /* disable zlib fjuczer */ PLUGIN_DEFINE(logs, PLUGIN_LOG, NULL); static struct buffer_info buffer_lograw = { NULL, 0, 0 }; static logs_log_t *log_curlog = NULL; /* * log_escape() * * jeli trzeba, eskejpuje tekst do logw. * * - str - tekst. * * zaalokowany bufor. */ static char *log_escape(const char *str) { const char *p; char *res, *q; int size, needto = 0; if (!str) return NULL; for (p = str; *p; p++) { if (*p == '"' || *p == '\'' || *p == '\r' || *p == '\n' || *p == ',') needto = 1; } if (!needto) return xstrdup(str); for (p = str, size = 0; *p; p++) { if (*p == '"' || *p == '\'' || *p == '\r' || *p == '\n' || *p == '\\') size += 2; else size++; } q = res = xmalloc(size + 3); *q++ = '"'; for (p = str; *p; p++, q++) { if (*p == '\\' || *p == '"' || *p == '\'') { *q++ = '\\'; *q = *p; } else if (*p == '\n') { *q++ = '\\'; *q = 'n'; } else if (*p == '\r') { *q++ = '\\'; *q = 'r'; } else *q = *p; } *q++ = '"'; *q = 0; return res; } static char *fstring_reverse(fstring_t *fstr) { const char *str; const fstr_attr_t *attr; string_t asc; int i; if (!fstr) return NULL; attr = fstr->attr; str = fstr->str; if (!attr || !str) return NULL; asc = string_init(NULL); for (i = 0; str[i]; i++) { #define prev attr[i-1] #define cur attr[i] int reset = 0; if (i) { if (!(cur & FSTR_BOLD) && (prev & FSTR_BOLD)) reset = 1; if (!(cur & FSTR_BLINK) && (prev & FSTR_BLINK)) reset = 1; if (!(cur & FSTR_UNDERLINE) && (prev & FSTR_UNDERLINE)) reset = 1; if (!(cur & FSTR_REVERSE) && (prev & FSTR_REVERSE)) reset = 1; if ((cur & FSTR_NORMAL) && !(prev & FSTR_NORMAL)) reset = 1; /* colors disappear */ if (reset) string_append(asc, "%n"); } else reset = 1; /* attr */ if ((cur & FSTR_BLINK) && (reset || !(prev & FSTR_BLINK))) string_append(asc, "%i"); // if ((cur & FSTR_UNDERLINE) && (reset || !(prev & FSTR_UNDERLINE))) string_append(asc, "%"); // if ((cur & FSTR_REVERSE) && (reset || !(prev & FSTR_REVERSE))) string_append(asc, "%"); if (!(cur & FSTR_NORMAL)) { /* background color XXX */ #define BGCOLOR(x) -1 if (0 && ((reset || BGCOLOR(cur) != BGCOLOR(prev)))) { string_append_c(asc, '%'); switch (BGCOLOR(cur)) { case (0): string_append_c(asc, 'l'); break; case (1): string_append_c(asc, 's'); break; case (2): string_append_c(asc, 'h'); break; case (3): string_append_c(asc, 'z'); break; case (4): string_append_c(asc, 'e'); break; case (5): string_append_c(asc, 'q'); break; case (6): string_append_c(asc, 'd'); break; case (7): string_append_c(asc, 'x'); break; } } #undef BGCOLOR /* foreground color */ #define FGCOLOR(x) ((!(x & FSTR_NORMAL)) ? (x & FSTR_FOREMASK) : -1) if (((reset || FGCOLOR(cur) != FGCOLOR(prev)) || (i && (prev & FSTR_BOLD) != (cur & FSTR_BOLD)))) { string_append_c(asc, '%'); switch ((cur & FSTR_FOREMASK)) { case (0): string_append_c(asc, (cur & FSTR_BOLD) ? 'K' : 'k'); break; case (1): string_append_c(asc, (cur & FSTR_BOLD) ? 'R' : 'r'); break; case (2): string_append_c(asc, (cur & FSTR_BOLD) ? 'G' : 'g'); break; case (3): string_append_c(asc, (cur & FSTR_BOLD) ? 'Y' : 'y'); break; case (4): string_append_c(asc, (cur & FSTR_BOLD) ? 'B' : 'b'); break; case (5): string_append_c(asc, (cur & FSTR_BOLD) ? 'M' : 'm'); break; /* | fioletowy | %m/%p | %M/%P | %q | */ case (6): string_append_c(asc, (cur & FSTR_BOLD) ? 'C' : 'c'); break; case (7): string_append_c(asc, (cur & FSTR_BOLD) ? 'W' : 'w'); break; } } #undef FGCOLOR } else { /* no color */ if ((cur & FSTR_BOLD) && (reset || !(prev & FSTR_BOLD))) string_append(asc, "%T"); } /* str */ if (str[i] == '%' || str[i] == '\\') string_append_c(asc, '\\'); string_append_c(asc, str[i]); } /* reset, and return. */ string_append(asc, "%n"); return string_free(asc, 0); #undef prev #undef cur } /* * zwraca irssi lub simple lub xml lub NULL * w zaleznosci od ustawien log_format w sesji i log:logs */ static int logs_log_format(session_t *s) { const char *log_formats; if (config_logs_log == LOG_FORMAT_NONE) return LOG_FORMAT_NONE; if (!s || !(log_formats = session_get(s, "log_formats"))) return LOG_FORMAT_NONE; if (xstrstr(log_formats, "irssi")) return LOG_FORMAT_IRSSI; if (config_logs_log == LOG_FORMAT_SIMPLE && xstrstr(log_formats, "simple")) return LOG_FORMAT_SIMPLE; if (config_logs_log == LOG_FORMAT_XML && xstrstr(log_formats, "xml")) return LOG_FORMAT_XML; return LOG_FORMAT_NONE; } /* zwraca 1 lub 2 lub 3 jesli cos sie zmienilo. (log_format, sciezka, t) i zmienia path w log_window_t i jak cos zamyka plik / otwiera na nowo. * 0 jesli nie. * -1 jesli cos sie zjebalo. */ static int logs_window_check(logs_log_t *ll, time_t t) { session_t *s; log_window_t *l = ll->lw; int chan = 0; int tmp; if (!l || !(s = session_find(ll->session))) return -1; if (l->logformat != (tmp = logs_log_format(s))) { l->logformat = tmp; chan = 1; } if (!(l->path)) { chan = 2; } else { int datechanged = 0; /* bitmaska 0x01 (dzien) 0x02 (miesiac) 0x04 (rok) */ struct tm *tm = g_memdup(localtime(&(ll->t)), sizeof(struct tm)); struct tm *tm2 = localtime(&t); /* sprawdzic czy dane z (tm == tm2) */ if (tm->tm_mday != tm2->tm_mday) datechanged |= 0x01; if (tm->tm_mon != tm2->tm_mon) datechanged |= 0x02; if (tm->tm_year != tm2->tm_year) datechanged |= 0x04; if ( ((datechanged & 0x04) && xstrstr(config_logs_path, "%Y")) || ((datechanged & 0x02) && xstrstr(config_logs_path, "%M")) || ((datechanged & 0x01) && xstrstr(config_logs_path, "%D")) ) chan = 3; /* zalogowac jak sie zmienila data */ if (datechanged && l->logformat == LOG_FORMAT_IRSSI) { /* yes i know it's wrong place for doing this but .... */ if (!(l->file)) l->file = logs_open_file(l->path, LOG_FORMAT_IRSSI); logs_irssi(l->file, ll->session, NULL, prepare_timestamp_format(IRSSI_LOG_DAY_CHANGED, time(NULL)), 0, EKG_MSGCLASS_SYSTEM); } xfree(tm); } ll->t = t; if (chan > 1) { char *tmp = l->path; l->path = logs_prepare_path(s, config_logs_path, ll->uid, t); debug("[logs] logs_window_check chan = %d oldpath = %s newpath = %s\n", chan, __(tmp), __(l->path)); #if 0 /* TODO: nie moze byc - bo mogl logformat sie zmienic... */ if (chan != 3 && !xstrcmp(tmp, l->path)) /* jesli sciezka sie nie zmienila to nie otwieraj na nowo pliku */ chan = -chan; #endif xfree(tmp); } if (chan > 0) { if (l->file) { /* jesli plik byl otwarty otwieramy go z nowa sciezka */ fclose(l->file); l->file = NULL; l->file = logs_open_file(l->path, l->logformat); } } return chan; } static FILE *logs_window_close(logs_log_t *l, int close); /* forward */ static logs_log_t *logs_log_find(const char *session, const char *uid, int create) { list_t l; logs_log_t *temp = NULL; if (log_curlog && !xstrcmp(log_curlog->session, session) && !xstrcmp(log_curlog->uid, uid)) { if (log_curlog->lw) logs_window_check(log_curlog, time(NULL)); /* tutaj ? */ return log_curlog->lw ? log_curlog : logs_log_new(log_curlog, session, uid); } for (l=log_logs; l; l = l->next) { logs_log_t *ll = l->data; if ( (!ll->session || !xstrcmp(ll->session, session)) && !xstrcmp(ll->uid, uid)) { log_window_t *lw = ll->lw; if (lw || !create) { if (lw) logs_window_check(ll, time(NULL)); /* tutaj ? */ return ll; } else { temp = ll; break; } } } logs_window_close(log_curlog, 1); if (!create) return NULL; return (log_curlog = logs_log_new(temp, session, uid)); } static logs_log_t *logs_log_new(logs_log_t *l, const char *session, const char *uid) { logs_log_t *ll; int created = 0; debug("[logs] log_new uid = %s session %s", __(uid), __(session)); ll = l ? l : logs_log_find(session, uid, 0); debug(" logs_log_t %x\n", ll); if (!ll) { ll = xmalloc(sizeof(logs_log_t)); ll->session = xstrdup(session); ll->uid = xstrdup(uid); created = 1; } if (!(ll->lw)) { ll->lw = xmalloc(sizeof(log_window_t)); logs_window_check(ll, time(NULL)); /* l->log_format i l->path, l->t */ ll->lw->file = logs_open_file(ll->lw->path, ll->lw->logformat); } if (created) { if (ll->lw->logformat == LOG_FORMAT_IRSSI && xstrlen(IRSSI_LOG_EKG2_OPENED)) { logs_irssi(ll->lw->file, session, NULL, prepare_timestamp_format(IRSSI_LOG_EKG2_OPENED, time(NULL)), 0, EKG_MSGCLASS_SYSTEM); } list_add(&log_logs, ll); } return ll; } static void logs_window_new(window_t *w) { const char *uid; if (!w->target || !w->session || w->id == WINDOW_CONTACTS_ID) /* XXX w->id in WINDOW_RESERVED_ID ??? */ return; uid = get_uid_any(w->session, w->target); /* XXX, do we really want/need to create log struct with invalid uid? XXX */ logs_log_new(NULL, session_uid_get(w->session), uid ? uid : w->target); } static FILE *logs_window_close(logs_log_t *l, int close) { log_window_t *lw; FILE *f; if (!l || !(lw = l->lw)) return NULL; f = lw->file; xfree(lw->path); xfree(lw); l->lw = NULL; if (close && f) { fclose(f); return NULL; } return f; } static void logs_changed_path(const char *var) { list_t l; if (in_autoexec || !log_logs) return; for (l = log_logs; l; l = l->next) { logs_log_t *ll = l->data; if (ll->lw) { /* We don't need reopening file../ recreate magic struct.. because it'd be done when we try log smth into it. */ if (ll->lw->file) { fclose(ll->lw->file); ll->lw->file = NULL; } if (ll->lw->path) { xfree(ll->lw->path); ll->lw->path = NULL; } } } } static void logs_changed_raw(const char *var) { /* if logs:log_raw == 0, clean LOGRAW buffer */ if (!config_logs_log_raw) buffer_free(&buffer_lograw); } static int logs_print_window(session_t *s, window_t *w, const char *line, time_t ts) { static plugin_t *ui_plugin = NULL; fstring_t *fstr; /* it's enough to look for ui_plugin once */ if (!ui_plugin) ui_plugin = plugin_find(("ncurses")); if (!ui_plugin) ui_plugin = plugin_find(("gtk")); if (!ui_plugin) { debug_error("WARN logs_print_window() called but neither ncurses plugin nor gtk found\n"); return -1; } fstr = fstring_new_format(line); fstr->ts = ts; query_emit(ui_plugin, "ui-window-print", &w, &fstr); fstring_free(fstr); return 0; } /* items == -1 display all */ static int logs_buffer_raw_display(const char *file, int items) { struct buffer **bs = NULL; struct buffer *b; char *beg = NULL, *profile = NULL, *sesja = NULL, *target = NULL; int item = 0; int i; session_t *s; window_t *w; if (!file) return -1; if (!items) return 0; /* i'm weird, let's search for logs/__internal__ than check if there are smth after it... & check if there are two '/' */ if ((beg = xstrstr(file, "logs/__internal__")) && xstrlen(beg) > 19 && xstrchr(beg+18, '/') && xstrchr(beg+18, '/') != xstrrchr(beg+18, '/')) { profile = beg + 18; sesja = xstrchr(profile, '/')+1; target = xstrchr(sesja, '/')+1; } debug("[logs_buffer_raw_display()] profile: 0x%x sesja: 0x%x target: 0x%x\n", profile, sesja, target); if (!profile || !sesja || !target) return -1; profile = (xstrcmp(target, "_default_")) ? xstrndup(profile, sesja-profile-1) : NULL; sesja = (xstrcmp(target, "_null_")) ? xstrndup(sesja, target-sesja-1) : NULL; target = xstrdup(target); debug("[logs_buffer_raw_display()] profile: %s sesja: %s target: %s\n", __(profile), __(sesja), __(target)); /* search for session+window */ s = session_find(sesja); w = window_find_s(s, target); debug("[logs_buffer_raw_display()] s:0x%x; w:0x%x;\n", s, w); if (!w) w = window_current; if (w) w->lock++; for (b = buffer_lograw.data; b; b = b->next) { if (!xstrcmp(b->target, file)) { /* we asume that (b->ts < (b->next)->ts, it's quite correct unless other plugin do this trick... */ if (items == -1) { logs_print_window(s, w, b->line, b->ts); } else { bs = (struct buffer **) xrealloc(bs, (item+2) * sizeof(struct buffer *)); bs[item + 1] = NULL; bs[item] = b; } item++; } } if (bs) for (i = item < items ? 0 : item-items; i < item; i++) logs_print_window(s, w, bs[i]->line, bs[i]->ts); if (w) { w->lock--; query_emit(NULL, "ui-window-refresh"); } xfree(bs); xfree(profile); xfree(sesja); xfree(target); return item; } /* * przygotowanie nazwy pliku bez rozszerzenia * %S - sesja nasza * %u - uytkownik (uid), z ktrym piszemy * %U - uytkownik (nick) -||- * %Y, %M, %D - rok, miesic, dzie * zwraca ciek, ktra naley rcznie zwolni przez xfree() */ static char *logs_prepare_path(session_t *session, const char *logs_path, const char *uid, time_t sent) { char *uidtmp, datetime[5]; struct tm *tm = NULL; string_t buf; if (!logs_path) return NULL; buf = string_init(NULL); while (*logs_path) { if ((char)*logs_path == '%' && (logs_path+1) != NULL) { switch (*(logs_path+1)) { case 'S': string_append_n(buf, session ? session->uid : "_null_", -1); break; case 'P': string_append_n(buf, config_profile ? config_profile : "_default_", -1); break; case 'u': uidtmp = xstrdup(get_uid(session, uid)); goto attach; /* avoid code duplication */ case 'U': uidtmp = xstrdup(get_nickname(session, uid)); attach: if (!uidtmp) uidtmp = xstrdup(uid); if (xstrchr(uidtmp, '/')) *(xstrchr(uidtmp, '/')) = 0; // strip resource string_append_n(buf, uidtmp ? uidtmp : uid, -1); xfree(uidtmp); break; case 'Y': if (!tm) tm = localtime(&sent); snprintf(datetime, 5, "%4d", tm->tm_year+1900); string_append_n(buf, datetime, 4); break; case 'M': if (!tm) tm = localtime(&sent); snprintf(datetime, 3, "%02d", tm->tm_mon+1); string_append_n(buf, datetime, 2); break; case 'D': if (!tm) tm = localtime(&sent); snprintf(datetime, 3, "%02d", tm->tm_mday); string_append_n(buf, datetime, 2); break; default: string_append_c(buf, *(logs_path+1)); }; logs_path++; } else if (*logs_path == '~' && (*(logs_path+1) == '/' || *(logs_path+1) == '\0')) { string_append_n(buf, home_dir, -1); //string_append_c(buf, '/'); } else string_append_c(buf, *logs_path); logs_path++; }; // sanityzacja sciezki - wywalic "../", zamienic znaki spec. na inne // zamieniamy szkodliwe znaki na minusy, spacje na podkreslenia // TODO xstrtr(buf->str, ' ', '_'); return string_free(buf, 0); } /* * otwarcie pliku do zapisu/odczytu * tworzy wszystkie katalogi po drodze, jeli nie istniej i mkdir =1 * ff - xml 2 || irssi 3 || simple 1 * zwraca numer deskryptora bd NULL */ static FILE* logs_open_file(char *path, int ff) { char fullname[PATH_MAX]; #ifdef HAVE_LIBZ int zlibmode = 0; #endif if (ff != LOG_FORMAT_IRSSI && ff != LOG_FORMAT_SIMPLE && ff != LOG_FORMAT_XML && ff != LOG_FORMAT_RAW) { if (ff == LOG_FORMAT_NONE) debug("[logs] opening log file %s with ff == LOG_FORMAT_NONE CANCELLED\n", __(path), ff); else debug("[logs] opening log file %s with ff == %d CANCELED\n", __(path), ff); return NULL; } debug("[logs] opening log file %s ff:%d\n", __(path), ff); if (!path) { errno = EACCES; /* = 0 ? */ return NULL; } { /* check if such file was already open SLOW :( */ list_t l; for (l=log_logs; l; l = l->next) { logs_log_t *ll = l->data; log_window_t *lw; if (!ll || !(lw = ll->lw)) continue; /* debug_error("here: %x [%s, %s] [%d %d]\n", lw->file, lw->path, path, lw->logformat, ff); */ if (lw->file && lw->logformat == ff && !xstrcmp(lw->path, path)) { FILE *f = lw->file; lw->file = NULL; /* simulate fclose() on this */ return f; /* simulate fopen() here */ } } } if (mkdir_recursive(path, 0)) { print("directory_cant_create", path, strerror(errno)); return NULL; } g_strlcpy(fullname, path, PATH_MAX); if (ff == LOG_FORMAT_IRSSI) g_strlcat(fullname, ".log", PATH_MAX); else if (ff == LOG_FORMAT_SIMPLE) g_strlcat(fullname, ".txt", PATH_MAX); else if (ff == LOG_FORMAT_XML) g_strlcat(fullname, ".xml", PATH_MAX); else if (ff == LOG_FORMAT_RAW) g_strlcat(fullname, ".raw", PATH_MAX); #ifdef HAVE_LIBZ /* z log.c i starego ekg1. Wypadaloby zaimplementowac... */ /* nawet jeli chcemy gzipowane logi, a istnieje nieskompresowany log, * olewamy kompresj. jeli loga nieskompresowanego nie ma, dodajemy * rozszerzenie .gz i balujemy. */ if (config_log & 4) { struct stat st; if (stat(fullname, &st) == -1) { gzFile f; if (!(f = gzopen(path, "a"))) return NULL; gzputs(f, buf); gzclose(f); zlibmode = 1; } } if (zlibmode) { /* XXX, ustawic jakas flage... */ g_strlcat(fullname, ".gz", PATH_MAX); } #endif /* if xml, prepare xml file */ if (ff == LOG_FORMAT_XML) { FILE *fdesc = fopen(fullname, "r+"); if (!fdesc) { if (!(fdesc = fopen(fullname, "w+"))) return NULL; /* XXX: what about old, locale-encoded logs? */ fputs("\n", fdesc); fputs("\n", fdesc); fputs("\n", fdesc); fputs("\n", fdesc); } return fdesc; } return fopen(fullname, "a+"); } /* * zapis w formacie znanym z ekg1 * typ,uid,nickname,timestamp,{timestamp wyslania dla odleglych}, text */ static void logs_simple(FILE *file, const char *session, const char *uid, const char *text, time_t sent, msgclass_t class, const char *status) { char *textcopy; const char *timestamp = prepare_timestamp_format(config_logs_timestamp, time(0)); session_t *s = session_find((const char*)session); const char *gotten_uid = get_uid(s, uid); const char *gotten_nickname = get_nickname(s, uid); const gchar *logsenc = config_logs_encoding ? config_logs_encoding : console_charset; GString *tmp; if (!file) return; textcopy = log_escape(text); if (!gotten_uid) gotten_uid = uid; if (!gotten_nickname) gotten_nickname = uid; switch (class) { case EKG_MSGCLASS_MESSAGE : fputs("msgrecv,", file); break; case EKG_MSGCLASS_CHAT : fputs("chatrecv,", file); break; case EKG_MSGCLASS_SENT : fputs("msgsend,", file); break; case EKG_MSGCLASS_SENT_CHAT : fputs("chatsend,", file); break; case EKG_MSGCLASS_SYSTEM : fputs("msgsystem,", file); break; case EKG_MSGCLASS_PRIV_STATUS : fputs("status,", file); break; default : fputs("chatrecv,", file); break; }; /* * chatsend,,,, * chatrecv,,,,, * status,,,[],