diff options
211 files changed, 66522 insertions, 0 deletions
diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..d60c31a9 --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + 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. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can 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 + + +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. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..86302ecc --- /dev/null +++ b/Makefile @@ -0,0 +1,98 @@ +########################### +## Makefile for BitlBee ## +## ## +## Copyright 2002 Lintux ## +########################### + +### DEFINITIONS + +-include Makefile.settings + +# Program variables +objects = account.o bitlbee.o commands.o conf.o crypting.o help.o ini.o irc.o log.o nick.o query.o set.o unix.o url.o user.o debug.o +subdirs = protocols + +# Expansion of variables +subdirobjs = $(foreach dir,$(subdirs),$(dir)/$(dir).o) +CFLAGS += -Wall + +all: $(OUTFILE) + +uninstall: uninstall-bin uninstall-doc + @echo -e '\nmake uninstall does not remove files in '$(DESTDIR)$(ETCDIR)', you can use make uninstall-etc to do that.\n' + +install: install-bin install-doc + @if ! [ -d $(DESTDIR)$(CONFIG) ]; then echo -e '\nThe configuration directory $(DESTDIR)$(CONFIG) does not exist yet, don'\''t forget to create it!'; fi + @if ! [ -e $(DESTDIR)$(ETCDIR)/bitlbee.conf ]; then echo -e '\nNo files are installed in '$(DESTDIR)$(ETCDIR)' by make install. Run make install-etc to do that.'; fi + @echo + +.PHONY: install install-bin install-etc install-doc \ + uninstall uninstall-bin uninstall-etc uninstall-doc \ + all clean distclean tar $(subdirs) + +Makefile.settings: + @echo + @echo Run ./configure to create Makefile.settings, then rerun make + @echo + +clean: $(subdirs) + rm -f *.o $(OUTFILE) core utils/bitlbeed encode decode + +distclean: clean $(subdirs) + rm -f Makefile.settings config.h + find . -name 'DEADJOE' -o -name '*.orig' -o -name '*.rej' -o -name '*~' | xargs rm -f + +install-doc: + $(MAKE) -C doc install + +uninstall-doc: + $(MAKE) -C doc uninstall + +install-bin: + mkdir -p $(DESTDIR)$(BINDIR) + install -m 0755 $(OUTFILE) $(DESTDIR)$(BINDIR)/$(OUTFILE) + +uninstall-bin: + rm -f $(DESTDIR)$(BINDIR)/$(OUTFILE) + +install-etc: + mkdir -p $(DESTDIR)$(ETCDIR) + install -m 0644 motd.txt $(DESTDIR)$(ETCDIR)/motd.txt + install -m 0644 bitlbee.conf $(DESTDIR)$(ETCDIR)/bitlbee.conf + +uninstall-etc: + rm -f $(DESTDIR)$(ETCDIR)/motd.txt + rm -f $(DESTDIR)$(ETCDIR)/bitlbee.conf + -rmdir $(DESTDIR)$(ETCDIR) + +tar: + fakeroot debian/rules clean || make distclean + x=$$(basename $$(pwd)); \ + cd ..; \ + tar czf $$x.tar.gz --exclude=debian $$x + +$(subdirs): + @$(MAKE) -C $@ $(MAKECMDGOALS) + +$(objects): %.o: %.c + @echo '*' Compiling $< + @$(CC) -c $(CFLAGS) $< -o $@ + +$(objects): Makefile Makefile.settings config.h + +$(OUTFILE): $(objects) $(subdirs) + @echo '*' Linking $(OUTFILE) + @$(CC) $(objects) $(subdirobjs) -o $(OUTFILE) $(LFLAGS) $(EFLAGS) +ifndef DEBUG + @echo '*' Stripping $(OUTFILE) + @-$(STRIP) $(OUTFILE) +endif + +encode: crypting.c + $(CC) crypting.c protocols/md5.c $(CFLAGS) -o encode -DCRYPTING_MAIN $(CFLAGS) $(EFLAGS) $(LFLAGS) + +decode: encode + cp encode decode + +ctags: + ctags `find . -name "*.c"` `find . -name "*.h"` diff --git a/account.c b/account.c new file mode 100644 index 00000000..d5bd24c4 --- /dev/null +++ b/account.c @@ -0,0 +1,156 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Account management functions */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#define BITLBEE_CORE +#include "bitlbee.h" +#include "account.h" + +account_t *account_add( irc_t *irc, int protocol, char *user, char *pass ) +{ + account_t *a; + + if( irc->accounts ) + { + for( a = irc->accounts; a->next; a = a->next ); + a = a->next = g_new0 ( account_t, 1 ); + } + else + { + irc->accounts = a = g_new0 ( account_t, 1 ); + } + + a->protocol = protocol; + a->user = g_strdup( user ); + a->pass = g_strdup( pass ); + a->irc = irc; + + return( a ); +} + +account_t *account_get( irc_t *irc, char *id ) +{ + account_t *a, *ret = NULL; + int nr; + + if( sscanf( id, "%d", &nr ) == 1 && nr < 1000 ) + { + for( a = irc->accounts; a; a = a->next ) + if( ( nr-- ) == 0 ) + return( a ); + + return( NULL ); + } + + for( a = irc->accounts; a; a = a->next ) + { + if( g_strcasecmp( id, proto_name[a->protocol] ) == 0 ) + { + if( !ret ) + ret = a; + else + return( NULL ); /* We don't want to match more than one... */ + } + else if( strstr( a->user, id ) ) + { + if( !ret ) + ret = a; + else + return( NULL ); + } + } + + return( ret ); +} + +void account_del( irc_t *irc, account_t *acc ) +{ + account_t *a, *l = NULL; + + for( a = irc->accounts; a; a = (l=a)->next ) + if( a == acc ) + { + if( a->gc ) return; /* Caller should have checked, accounts still in use can't be deleted. */ + + if( l ) + { + l->next = a->next; + } + else + { + irc->accounts = a->next; + } + + g_free( a->user ); + g_free( a->pass ); + if( a->server ) g_free( a->server ); + if( a->reconnect ) /* This prevents any reconnect still queued to happen */ + cancel_auto_reconnect( a ); + g_free( a ); + + break; + } +} + +void account_on( irc_t *irc, account_t *a ) +{ + struct aim_user *u; + + if( a->gc ) + { + /* Trying to enable an already-enabled account */ + return; + } + + if( proto_prpl[a->protocol]->login == NULL ) + { + irc_usermsg( irc, "Support for protocol %s is not included in this BitlBee", proto_name[a->protocol] ); + return; + } + + cancel_auto_reconnect( a ); + + u = g_new0 ( struct aim_user, 1 ); + u->irc = irc; + u->protocol = a->protocol; + strncpy( u->username, a->user, sizeof( u->username ) - 1 ); + strncpy( u->password, a->pass, sizeof( u->password ) - 1 ); + if( a->server) strncpy( u->proto_opt[0], a->server, sizeof( u->proto_opt[0] ) - 1 ); + + a->gc = (struct gaim_connection *) u; /* Bit hackish :-/ */ + a->reconnect = 0; + + proto_prpl[a->protocol]->login( u ); +} + +void account_off( irc_t *irc, account_t *a ) +{ + account_offline( a->gc ); + a->gc = NULL; + if( a->reconnect ) + { + /* Shouldn't happen */ + cancel_auto_reconnect( a ); + } +} diff --git a/account.h b/account.h new file mode 100644 index 00000000..a8653411 --- /dev/null +++ b/account.h @@ -0,0 +1,49 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Account management functions */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef _ACCOUNT_H +#define _ACCOUNT_H + +typedef struct account +{ + int protocol; + char *user; + char *pass; + char *server; + + int reconnect; + + struct irc *irc; + struct gaim_connection *gc; + struct account *next; +} account_t; + +account_t *account_add( irc_t *irc, int protocol, char *user, char *pass ); +account_t *account_get( irc_t *irc, char *id ); +void account_del( irc_t *irc, account_t *acc ); +void account_on( irc_t *irc, account_t *a ); +void account_off( irc_t *irc, account_t *a ); + +#endif diff --git a/bitlbee.c b/bitlbee.c new file mode 100644 index 00000000..66552130 --- /dev/null +++ b/bitlbee.c @@ -0,0 +1,601 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Main file */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#define BITLBEE_CORE +#include "bitlbee.h" +#include "commands.h" +#include "crypting.h" +#include "protocols/nogaim.h" +#include "help.h" +#include <signal.h> +#include <stdio.h> +#include <errno.h> + +gboolean bitlbee_io_new_client( GIOChannel *source, GIOCondition condition, gpointer data ) +{ + size_t size = sizeof( struct sockaddr_in ); + struct sockaddr_in conn_info; + int new_socket = accept( global.listen_socket, (struct sockaddr *) &conn_info, + &size ); + + count_io_event(source, "main"); + + log_message( LOGLVL_INFO, "Creating new connection with fd %d.", new_socket ); + irc_new( new_socket ); + + return TRUE; +} + + + +int bitlbee_daemon_init() +{ + struct sockaddr_in listen_addr; + int i; + GIOChannel *ch; + + log_link( LOGLVL_ERROR, LOGOUTPUT_SYSLOG ); + log_link( LOGLVL_WARNING, LOGOUTPUT_SYSLOG ); + + global.listen_socket = socket( AF_INET, SOCK_STREAM, 0 ); + if( global.listen_socket == -1 ) + { + log_error( "socket" ); + return( -1 ); + } + listen_addr.sin_family = AF_INET; + listen_addr.sin_port = htons( global.conf->port ); + listen_addr.sin_addr.s_addr = inet_addr( global.conf->iface ); + + i = bind( global.listen_socket, (struct sockaddr *) &listen_addr, sizeof( struct sockaddr ) ); + if( i == -1 ) + { + log_error( "bind" ); + return( -1 ); + } + + i = listen( global.listen_socket, 10 ); + if( i == -1 ) + { + log_error( "listen" ); + return( -1 ); + } + + ch = g_io_channel_unix_new( global.listen_socket ); + g_io_add_watch( ch, G_IO_IN, bitlbee_io_new_client, NULL ); + +#ifndef _WIN32 + if( !global.conf->nofork ) + { + i = fork(); + if( i == -1 ) + { + log_error( "fork" ); + return( -1 ); + } + else if( i != 0 ) + exit( 0 ); + close( 0 ); + close( 1 ); + close( 2 ); + chdir( "/" ); + } +#endif + + return( 0 ); +} + +int bitlbee_inetd_init() +{ + if( !irc_new( 0 ) ) + return( 1 ); + + log_link( LOGLVL_ERROR, LOGOUTPUT_IRC ); + log_link( LOGLVL_WARNING, LOGOUTPUT_IRC ); + + return( 0 ); +} + +gboolean bitlbee_io_current_client_read( GIOChannel *source, GIOCondition condition, gpointer data ) +{ + irc_t *irc = data; + char line[513]; + int st; + + count_io_event(source, "main"); + + if( condition & G_IO_ERR || condition & G_IO_HUP ) + { + irc_free( irc ); + return FALSE; + } + + st = read( irc->fd, line, sizeof( line ) - 1 ); + if( st == 0 ) + { + irc_free( irc ); + return FALSE; + } + else if( st < 0 ) + { + if( sockerr_again() ) + { + return TRUE; + } + else + { + irc_free( irc ); + return FALSE; + } + } + + line[st] = '\0'; + if( irc->readbuffer == NULL ) + { + irc->readbuffer = g_strdup( line ); + } + else + { + irc->readbuffer = g_renew( char, irc->readbuffer, strlen( irc->readbuffer ) + strlen ( line ) + 1 ); + strcpy( ( irc->readbuffer + strlen( irc->readbuffer ) ), line ); + } + + if( !irc_process( irc ) ) + { + log_message( LOGLVL_INFO, "Destroying connection with fd %d.", irc->fd ); + irc_free( irc ); + return FALSE; + } + + return TRUE; +} + +gboolean bitlbee_io_current_client_write( GIOChannel *source, GIOCondition condition, gpointer data ) +{ + irc_t *irc = data; + int st, size; + char *temp; +#ifdef FLOOD_SEND + time_t newtime; +#endif + + count_io_event(source, "main"); + +#ifdef FLOOD_SEND + newtime = time( NULL ); + if( ( newtime - irc->oldtime ) > FLOOD_SEND_INTERVAL ) + { + irc->sentbytes = 0; + irc->oldtime = newtime; + } +#endif + + if( irc->sendbuffer == NULL ) + return( FALSE ); + + size = strlen( irc->sendbuffer ); + +#ifdef FLOOD_SEND + if( ( FLOOD_SEND_BYTES - irc->sentbytes ) > size ) + st = write( irc->fd, irc->sendbuffer, size ); + else + st = write( irc->fd, irc->sendbuffer, ( FLOOD_SEND_BYTES - irc->sentbytes ) ); +#else + st = write( irc->fd, irc->sendbuffer, size ); +#endif + + if( st <= 0 ) + { + if( sockerr_again() ) + { + return TRUE; + } + else + { + irc_free( irc ); + return FALSE; + } + } + +#ifdef FLOOD_SEND + irc->sentbytes += st; +#endif + + if( st == size ) + { + g_free( irc->sendbuffer ); + irc->sendbuffer = NULL; + + irc->w_watch_source_id = 0; + return( FALSE ); + } + else + { + temp = g_strdup( irc->sendbuffer + st ); + g_free( irc->sendbuffer ); + irc->sendbuffer = temp; + + return( TRUE ); + } +} + +int bitlbee_load( irc_t *irc, char* password ) +{ + char s[512]; + char *line; + int proto; + char nick[MAX_NICK_LENGTH+1]; + FILE *fp; + user_t *ru = user_find( irc, ROOT_NICK ); + + if( irc->status == USTATUS_IDENTIFIED ) + return( 1 ); + + g_snprintf( s, 511, "%s%s%s", global.conf->configdir, irc->nick, ".accounts" ); + fp = fopen( s, "r" ); + if( !fp ) return( 0 ); + + fscanf( fp, "%32[^\n]s", s ); + if( setpass( irc, password, s ) < 0 ) + { + fclose( fp ); + return( -1 ); + } + + /* Do this now. If the user runs with AuthMode = Registered, the + account command will not work otherwise. */ + irc->status = USTATUS_IDENTIFIED; + + while( fscanf( fp, "%511[^\n]s", s ) > 0 ) + { + fgetc( fp ); + line = deobfucrypt( irc, s ); + root_command_string( irc, ru, line, 0 ); + g_free( line ); + } + fclose( fp ); + + g_snprintf( s, 511, "%s%s%s", global.conf->configdir, irc->nick, ".nicks" ); + fp = fopen( s, "r" ); + if( !fp ) return( 0 ); + while( fscanf( fp, "%s %d %s", s, &proto, nick ) > 0 ) + { + http_decode( s ); + nick_set( irc, s, proto, nick ); + } + fclose( fp ); + + if( set_getint( irc, "auto_connect" ) ) + { + strcpy( s, "account on" ); /* Can't do this directly because r_c_s alters the string */ + root_command_string( irc, ru, s, 0 ); + } + + return( 1 ); +} + +int bitlbee_save( irc_t *irc ) +{ + char s[512]; + char path[512], new_path[512]; + char *line; + nick_t *n; + set_t *set; + mode_t ou = umask( 0077 ); + account_t *a; + FILE *fp; + char *hash; + + /*\ + * [SH] Nothing should be saved if no password is set, because the + * password is not set if it was wrong, or if one is not identified + * yet. This means that a malicious user could easily overwrite + * files owned by someone else: + * a Bad Thing, methinks + \*/ + + /* [WVG] No? Really? */ + + /*\ + * [SH] Okay, okay, it wasn't really Wilmer who said that, it was + * me. I just thought it was funny. + \*/ + + hash = hashpass( irc ); + if( hash == NULL ) + { + irc_usermsg( irc, "Please register yourself if you want to save your settings." ); + return( 0 ); + } + + g_snprintf( path, 511, "%s%s%s", global.conf->configdir, irc->nick, ".nicks~" ); + fp = fopen( path, "w" ); + if( !fp ) return( 0 ); + for( n = irc->nicks; n; n = n->next ) + { + strcpy( s, n->handle ); + s[169] = 0; /* Prevent any overflow (169 ~ 512 / 3) */ + http_encode( s ); + g_snprintf( s + strlen( s ), 510 - strlen( s ), " %d %s", n->proto, n->nick ); + if( fprintf( fp, "%s\n", s ) != strlen( s ) + 1 ) + { + irc_usermsg( irc, "fprintf() wrote too little. Disk full?" ); + fclose( fp ); + return( 0 ); + } + } + if( fclose( fp ) != 0 ) + { + irc_usermsg( irc, "fclose() reported an error. Disk full?" ); + return( 0 ); + } + + g_snprintf( new_path, 512, "%s%s%s", global.conf->configdir, irc->nick, ".nicks" ); + if( unlink( new_path ) != 0 ) + { + if( errno != ENOENT ) + { + irc_usermsg( irc, "Error while removing old .nicks file" ); + return( 0 ); + } + } + if( rename( path, new_path ) != 0 ) + { + irc_usermsg( irc, "Error while renaming new .nicks file" ); + return( 0 ); + } + + g_snprintf( path, 511, "%s%s%s", global.conf->configdir, irc->nick, ".accounts~" ); + fp = fopen( path, "w" ); + if( !fp ) return( 0 ); + if( fprintf( fp, "%s", hash ) != strlen( hash ) ) + { + irc_usermsg( irc, "fprintf() wrote too little. Disk full?" ); + fclose( fp ); + return( 0 ); + } + g_free( hash ); + + for( a = irc->accounts; a; a = a->next ) + { + if( a->protocol == PROTO_OSCAR || a->protocol == PROTO_ICQ || a->protocol == PROTO_TOC ) + g_snprintf( s, sizeof( s ), "account add oscar \"%s\" \"%s\" %s", a->user, a->pass, a->server ); + else + g_snprintf( s, sizeof( s ), "account add %s \"%s\" \"%s\" \"%s\"", + proto_name[a->protocol], a->user, a->pass, a->server ? a->server : "" ); + + line = obfucrypt( irc, s ); + if( *line ) + { + if( fprintf( fp, "%s\n", line ) != strlen( line ) + 1 ) + { + irc_usermsg( irc, "fprintf() wrote too little. Disk full?" ); + fclose( fp ); + return( 0 ); + } + } + g_free( line ); + } + + for( set = irc->set; set; set = set->next ) + { + if( set->value && set->def ) + { + g_snprintf( s, sizeof( s ), "set %s \"%s\"", set->key, set->value ); + line = obfucrypt( irc, s ); + if( *line ) + { + if( fprintf( fp, "%s\n", line ) != strlen( line ) + 1 ) + { + irc_usermsg( irc, "fprintf() wrote too little. Disk full?" ); + fclose( fp ); + return( 0 ); + } + } + g_free( line ); + } + } + + if( strcmp( irc->mynick, ROOT_NICK ) != 0 ) + { + g_snprintf( s, sizeof( s ), "rename %s %s", ROOT_NICK, irc->mynick ); + line = obfucrypt( irc, s ); + if( *line ) + { + if( fprintf( fp, "%s\n", line ) != strlen( line ) + 1 ) + { + irc_usermsg( irc, "fprintf() wrote too little. Disk full?" ); + fclose( fp ); + return( 0 ); + } + } + g_free( line ); + } + if( fclose( fp ) != 0 ) + { + irc_usermsg( irc, "fclose() reported an error. Disk full?" ); + return( 0 ); + } + + g_snprintf( new_path, 512, "%s%s%s", global.conf->configdir, irc->nick, ".accounts" ); + if( unlink( new_path ) != 0 ) + { + if( errno != ENOENT ) + { + irc_usermsg( irc, "Error while removing old .accounts file" ); + return( 0 ); + } + } + if( rename( path, new_path ) != 0 ) + { + irc_usermsg( irc, "Error while renaming new .accounts file" ); + return( 0 ); + } + + umask( ou ); + + return( 1 ); +} + +void bitlbee_shutdown( gpointer data ) +{ + /* Try to save data for all active connections (if desired). */ + while( irc_connection_list != NULL ) + irc_free( irc_connection_list->data ); + + /* We'll only reach this point when not running in inetd mode: */ + g_main_quit( global.loop ); +} + +int root_command_string( irc_t *irc, user_t *u, char *command, int flags ) +{ + char *cmd[IRC_MAX_ARGS]; + char *s; + int k; + char q = 0; + + memset( cmd, 0, sizeof( cmd ) ); + cmd[0] = command; + k = 1; + for( s = command; *s && k < ( IRC_MAX_ARGS - 1 ); s ++ ) + if( *s == ' ' && !q ) + { + *s = 0; + while( *++s == ' ' ); + if( *s == '"' || *s == '\'' ) + { + q = *s; + s ++; + } + if( *s ) + { + cmd[k++] = s; + s --; + } + } + else if( *s == q ) + { + q = *s = 0; + } + cmd[k] = NULL; + + return( root_command( irc, cmd ) ); +} + +int root_command( irc_t *irc, char *cmd[] ) +{ + int i; + + if( !cmd[0] ) + return( 0 ); + + for( i = 0; commands[i].command; i++ ) + if( g_strcasecmp( commands[i].command, cmd[0] ) == 0 ) + { + if( !cmd[commands[i].required_parameters] ) + { + irc_usermsg( irc, "Not enough parameters given (need %d)", commands[i].required_parameters ); + return( 0 ); + } + commands[i].execute( irc, cmd ); + return( 1 ); + } + + irc_usermsg( irc, "Unknown command: %s. Please use help commands to get a list of available commands.", cmd[0] ); + + return( 1 ); +} + +/* Decode%20a%20file%20name */ +void http_decode( char *s ) +{ + char *t; + int i, j, k; + + t = g_new( char, strlen( s ) + 1 ); + + for( i = j = 0; s[i]; i ++, j ++ ) + { + if( s[i] == '%' ) + { + if( sscanf( s + i + 1, "%2x", &k ) ) + { + t[j] = k; + i += 2; + } + else + { + *t = 0; + break; + } + } + else + { + t[j] = s[i]; + } + } + t[j] = 0; + + strcpy( s, t ); + g_free( t ); +} + +/* Warning: This one explodes the string. Worst-cases can make the string 3x its original size! */ +/* This fuction is safe, but make sure you call it safely as well! */ +void http_encode( char *s ) +{ + char *t; + int i, j; + + t = g_strdup( s ); + + for( i = j = 0; t[i]; i ++, j ++ ) + { + if( t[i] <= ' ' || ((unsigned char *)t)[i] >= 128 || t[i] == '%' ) + { + sprintf( s + j, "%%%02X", ((unsigned char*)t)[i] ); + j += 2; + } + else + { + s[j] = t[i]; + } + } + s[j] = 0; + + g_free( t ); +} + +/* Strip newlines from a string. Modifies the string passed to it. */ +char *strip_newlines( char *source ) +{ + int i; + + for( i = 0; source[i] != '\0'; i ++ ) + if( source[i] == '\n' || source[i] == '\r' ) + source[i] = 32; + + return source; +} diff --git a/bitlbee.conf b/bitlbee.conf new file mode 100644 index 00000000..e0f592ae --- /dev/null +++ b/bitlbee.conf @@ -0,0 +1,104 @@ +## BitlBee default configuration file +## +## Comments are marked like this. The rest of the file is INI-style. The +## comments should tell you enough about what all settings mean. +## + +[settings] + +## RunMode: +## +## Inetd -- Run from inetd (default) +## Daemon -- Run as a stand-alone daemon -- EXPERIMENTAL! BitlBee is not yet +## stable enough to serve lots of users from one process. Because of this +## and other reasons, the use of daemon-mode is *STRONGLY* discouraged, +## don't think about reporting bugs when you use this! +## +# RunMode = Inetd + +## DaemonPort/DaemonInterface: +## +## For RunMode=Daemon, here you can specify on what interface and port the +## daemon should be listening for connections. +## +# DaemonInterface = 0.0.0.0 +# DaemonPort = 6667 + +## AuthMode +## +## Open -- Accept connections from anyone, use NickServ for user authentication. +## (default) +## Closed -- Require authorization (using the PASS command during login) before +## allowing the user to connect at all. +## Registered -- Only allow registered users to use this server; this disables +## the register- and the account command until the user identifies himself. +## +# AuthMode = Open + +## AuthPassword +## +## Password the user should enter when logging into a closed BitlBee server. +## +# AuthPassword = ItllBeBitlBee ## Heh.. Our slogan. ;-) + +## HostName +## +## Normally, BitlBee gets a hostname using getsockname(). If you have a nicer +## alias for your BitlBee daemon, you can set it here and BitlBee will identify +## itself with that name instead. Leave it commented out if you want BitlBee to +## use getsockname() to get a hostname. +## +# HostName = localhost + +## MotdFile +## +## Specify an alternative MOTD (Message Of The Day) file. Default value depends +## on the --etcdir argument to configure. +## +# MotdFile = /etc/bitlbee/motd.txt + +## ConfigDir +## +## Specify an alternative directory to store all the per-user configuration +## files. (.nicks/.accounts) +## +# ConfigDir = /var/lib/bitlbee + +## Ping settings +## +## BitlBee can send PING requests to the client to check whether it's still +## alive. This is not very useful on local servers, but it does make sense +## when most clients connect to the server over a real network interface. +## (Public servers) Pinging the client will make sure lost clients are +## detected and cleaned up sooner. +## +## PING requests are sent every PingInterval seconds. If no PONG reply has +## been received for PingTimeOut seconds, BitlBee aborts the connection. +## +## To disable the pinging, set at least one of these to 0. +## +# PingInterval = 180 +# PingTimeOut = 300 + +## Using proxy servers for outgoing connections +## +## If you're running BitlBee on a host which is behind a restrictive firewall +## and a proxy server, you can tell BitlBee to use that proxy server here. +## The setting has to be a URL, formatted like one of these examples: +## +## (Obviously, the username and password are optional) +## +## Proxy = http://john:doe@proxy.localnet.com:8080 +## Proxy = socks4://socksproxy.localnet.com +## Proxy = socks5://socksproxy.localnet.com + + +[defaults] + +## Here you can override the defaults for some per-user settings. Users are +## still able to override your defaults, so this is not a way to restrict +## your users... + +## To enable private mode by default, for example: + +## private = 1 diff --git a/bitlbee.h b/bitlbee.h new file mode 100644 index 00000000..67434568 --- /dev/null +++ b/bitlbee.h @@ -0,0 +1,138 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Main file */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef _BITLBEE_H +#define _BITLBEE_H + +#define _GNU_SOURCE /* Stupid GNU :-P */ + +#define PACKAGE "BitlBee" +#define BITLBEE_VERSION "0.99" +#define VERSION BITLBEE_VERSION + +#define MAX_STRING 128 + +#include "config.h" + +#include <fcntl.h> +#include <time.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <stdio.h> +#include <ctype.h> +#ifndef _WIN32 +#include <syslog.h> +#include <errno.h> +#endif + +#include <glib.h> +#include <gmodule.h> + +/* The following functions should not be used if we want to maintain Windows compatibility... */ +#undef free +#define free __PLEASE_USE_THE_GLIB_MEMORY_ALLOCATION_SYSTEM_INSTEAD__ +#undef malloc +#define malloc __PLEASE_USE_THE_GLIB_MEMORY_ALLOCATION_SYSTEM_INSTEAD__ +#undef calloc +#define calloc __PLEASE_USE_THE_GLIB_MEMORY_ALLOCATION_SYSTEM_INSTEAD__ +#undef realloc +#define realloc __PLEASE_USE_THE_GLIB_MEMORY_ALLOCATION_SYSTEM_INSTEAD__ +#undef strdup +#define strdup __PLEASE_USE_THE_GLIB_STRDUP_FUNCTIONS_SYSTEM_INSTEAD__ +#undef strndup +#define strndup __PLEASE_USE_THE_GLIB_STRDUP_FUNCTIONS_SYSTEM_INSTEAD__ +#undef snprintf +#define snprintf __PLEASE_USE_G_SNPRINTF_INSTEAD__ +#undef strcasecmp +#define strcasecmp __PLEASE_USE_G_STRCASECMP_INSTEAD__ +#undef strncasecmp +#define strncasecmp __PLEASE_USE_G_STRNCASECMP_INSTEAD__ + +#ifndef F_OK +#define F_OK 0 +#endif + +#define _( x ) x + +#define ROOT_NICK "root" +#define ROOT_CHAN "#bitlbee" +#define ROOT_FN "User manager" + +#define NS_NICK "NickServ" + +#define DEFAULT_AWAY "Away from computer" +#define CONTROL_TOPIC "Welcome to the control channel. Type help for help information." +#define IRCD_INFO "BitlBee <http://www.bitlbee.org/>" + +#define MAX_NICK_LENGTH 24 + +#define HELP_FILE VARDIR "help.txt" +#define CONF_FILE_DEF ETCDIR "bitlbee.conf" + +extern char *CONF_FILE; + +#include "irc.h" +#include "set.h" +#include "protocols/nogaim.h" +#include "commands.h" +#include "account.h" +#include "conf.h" +#include "log.h" +#include "ini.h" +#include "help.h" +#include "query.h" +#include "debug.h" +#include "sock.h" + +typedef struct global_t { + int listen_socket; + help_t *help; + conf_t *conf; + char *helpfile; + GMainLoop *loop; +} global_t; + +int bitlbee_daemon_init( void ); +int bitlbee_inetd_init( void ); + +gboolean bitlbee_io_current_client_read( GIOChannel *source, GIOCondition condition, gpointer data ); +gboolean bitlbee_io_current_client_write( GIOChannel *source, GIOCondition condition, gpointer data ); + +int root_command_string( irc_t *irc, user_t *u, char *command, int flags ); +int root_command( irc_t *irc, char *command[] ); +int bitlbee_load( irc_t *irc, char *password ); +int bitlbee_save( irc_t *irc ); +void bitlbee_shutdown( gpointer data ); +double gettime( void ); +G_MODULE_EXPORT void http_encode( char *s ); +G_MODULE_EXPORT void http_decode( char *s ); +G_MODULE_EXPORT char *strip_newlines(char *source); + +extern global_t global; + +#endif diff --git a/commands.c b/commands.c new file mode 100644 index 00000000..762cf510 --- /dev/null +++ b/commands.c @@ -0,0 +1,804 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* User manager (root) commands */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#define BITLBEE_CORE +#include "commands.h" +#include "crypting.h" +#include "bitlbee.h" +#include "help.h" + +#include <string.h> + +command_t commands[] = { + { "help", 0, cmd_help }, + { "identify", 1, cmd_identify }, + { "register", 1, cmd_register }, + { "drop", 1, cmd_drop }, + { "account", 1, cmd_account }, + { "add", 2, cmd_add }, + { "info", 1, cmd_info }, + { "rename", 2, cmd_rename }, + { "remove", 1, cmd_remove }, + { "block", 1, cmd_block }, + { "allow", 1, cmd_allow }, + { "save", 0, cmd_save }, + { "set", 0, cmd_set }, + { "yes", 0, cmd_yesno }, + { "no", 0, cmd_yesno }, + { "blist", 0, cmd_blist }, + { "nick", 1, cmd_nick }, + { "import_buddies", 1, cmd_import_buddies }, + { "qlist", 0, cmd_qlist }, + { "dump", 0, cmd_dump }, + { NULL } +}; + +int cmd_help( irc_t *irc, char **cmd ) +{ + char param[80]; + int i; + char *s; + + memset( param, 0, sizeof(param) ); + for ( i = 1; (cmd[i] != NULL && ( strlen(param) < (sizeof(param)-1) ) ); i++ ) { + if ( i != 1 ) // prepend space except for the first parameter + strcat(param, " "); + strncat( param, cmd[i], sizeof(param) - strlen(param) - 1 ); + } + + s = help_get( &(global.help), param ); + if( !s ) s = help_get( &(global.help), "" ); + + if( s ) + { + irc_usermsg( irc, "%s", s ); + g_free( s ); + return( 1 ); + } + else + { + irc_usermsg( irc, "Error opening helpfile." ); + return( 0 ); + } +} + +int cmd_identify( irc_t *irc, char **cmd ) +{ + int checkie = bitlbee_load( irc, cmd[1] ); + + if( checkie == -1 ) + { + irc_usermsg( irc, "Incorrect password" ); + } + else if( checkie == 0 ) + { + irc_usermsg( irc, "The nick is (probably) not registered" ); + } + else if( checkie == 1 ) + { + irc_usermsg( irc, "Password accepted" ); + } + else + { + irc_usermsg( irc, "Something very weird happened" ); + } + + return( 0 ); +} + +int cmd_register( irc_t *irc, char **cmd ) +{ + int checkie; + char path[512]; + + if( global.conf->authmode == AUTHMODE_REGISTERED ) + { + irc_usermsg( irc, "This server does not allow registering new accounts" ); + return( 0 ); + } + + g_snprintf( path, 511, "%s%s%s", global.conf->configdir, irc->nick, ".accounts" ); + checkie = access( path, F_OK ); + + g_snprintf( path, 511, "%s%s%s", global.conf->configdir, irc->nick, ".nicks" ); + checkie += access( path, F_OK ); + + if( checkie == -2 ) + { + setpassnc( irc, cmd[1] ); + root_command_string( irc, user_find( irc, irc->mynick ), "save", 0 ); + irc->status = USTATUS_IDENTIFIED; + } + else + { + irc_usermsg( irc, "Nick is already registered" ); + } + + return( 0 ); +} + +int cmd_drop( irc_t *irc, char **cmd ) +{ + char s[512]; + FILE *fp; + + g_snprintf( s, 511, "%s%s%s", global.conf->configdir, irc->nick, ".accounts" ); + fp = fopen( s, "r" ); + if( !fp ) + { + irc_usermsg( irc, "That account does not exist" ); + return( 0 ); + } + + fscanf( fp, "%32[^\n]s", s ); + fclose( fp ); + if( setpass( irc, cmd[1], s ) < 0 ) + { + irc_usermsg( irc, "Incorrect password" ); + return( 0 ); + } + + g_snprintf( s, 511, "%s%s%s", global.conf->configdir, irc->nick, ".accounts" ); + unlink( s ); + + g_snprintf( s, 511, "%s%s%s", global.conf->configdir, irc->nick, ".nicks" ); + unlink( s ); + + setpassnc( irc, NULL ); + irc_usermsg( irc, "Files belonging to account `%s' removed", irc->nick ); + + return( 0 ); +} + +int cmd_account( irc_t *irc, char **cmd ) +{ + account_t *a; + + if( global.conf->authmode == AUTHMODE_REGISTERED && irc->status < USTATUS_IDENTIFIED ) + { + irc_usermsg( irc, "This server only accepts registered users" ); + return( 0 ); + } + + if( g_strcasecmp( cmd[1], "add" ) == 0 ) + { + int prot; + + if( cmd[2] == NULL || cmd[3] == NULL || cmd[4] == NULL ) + { + irc_usermsg( irc, "Not enough parameters" ); + return( 0 ); + } + + for( prot = 0; prot < PROTO_MAX; prot ++ ) + if( proto_name[prot] && *proto_name[prot] && g_strcasecmp( proto_name[prot], cmd[2] ) == 0 ) + break; + + if( ( prot == PROTO_MAX ) || ( proto_prpl[prot] == NULL ) ) + { + irc_usermsg( irc, "Unknown protocol" ); + return( 0 ); + } + + if( prot == PROTO_OSCAR && cmd[5] == NULL ) + { + irc_usermsg( irc, "Not enough parameters" ); + return( 0 ); + } + + a = account_add( irc, prot, cmd[3], cmd[4] ); + + if( cmd[5] ) + a->server = g_strdup( cmd[5] ); + + irc_usermsg( irc, "Account successfully added" ); + } + else if( g_strcasecmp( cmd[1], "del" ) == 0 ) + { + if( !cmd[2] ) + { + irc_usermsg( irc, "Not enough parameters given (need %d)", 2 ); + } + else if( !( a = account_get( irc, cmd[2] ) ) ) + { + irc_usermsg( irc, "Invalid account" ); + } + else if( a->gc ) + { + irc_usermsg( irc, "Account is still logged in, can't delete" ); + } + else + { + account_del( irc, a ); + irc_usermsg( irc, "Account deleted" ); + } + } + else if( g_strcasecmp( cmd[1], "list" ) == 0 ) + { + int i = 0; + + for( a = irc->accounts; a; a = a->next ) + { + char *con; + + if( a->gc && ( a->gc->flags & OPT_LOGGED_IN ) ) + con = " (connected)"; + else if( a->gc ) + con = " (connecting)"; + else if( a->reconnect ) + con = " (awaiting reconnect)"; + else + con = ""; + + if( a->protocol == PROTO_OSCAR || a->protocol == PROTO_ICQ || a->protocol == PROTO_TOC ) + irc_usermsg( irc, "%2d. OSCAR, %s on %s%s", i, a->user, a->server, con ); + else + irc_usermsg( irc, "%2d. %s, %s%s", i, proto_name[a->protocol], a->user, con ); + + i ++; + } + irc_usermsg( irc, "End of account list" ); + } + else if( g_strcasecmp( cmd[1], "on" ) == 0 ) + { + if( cmd[2] ) + { + if( ( a = account_get( irc, cmd[2] ) ) ) + { + if( a->gc ) + { + irc_usermsg( irc, "Account already online" ); + return( 0 ); + } + else + { + account_on( irc, a ); + } + } + else + { + irc_usermsg( irc, "Invalid account" ); + return( 0 ); + } + } + else + { + if ( irc->accounts ) { + irc_usermsg( irc, "Trying to get all accounts connected..." ); + + for( a = irc->accounts; a; a = a->next ) + if( !a->gc ) + account_on( irc, a ); + } + else + { + irc_usermsg( irc, "No accounts known. Use 'account add' to add one." ); + } + } + } + else if( g_strcasecmp( cmd[1], "off" ) == 0 ) + { + if( !cmd[2] ) + { + irc_usermsg( irc, "Deactivating all active (re)connections..." ); + + for( a = irc->accounts; a; a = a->next ) + { + if( a->gc ) + account_off( irc, a ); + else if( a->reconnect ) + cancel_auto_reconnect( a ); + } + } + else if( ( a = account_get( irc, cmd[2] ) ) ) + { + if( a->gc ) + { + account_off( irc, a ); + } + else if( a->reconnect ) + { + cancel_auto_reconnect( a ); + irc_usermsg( irc, "Reconnect cancelled" ); + } + else + { + irc_usermsg( irc, "Account already offline" ); + return( 0 ); + } + } + else + { + irc_usermsg( irc, "Invalid account" ); + return( 0 ); + } + } + else + { + irc_usermsg( irc, "Unknown command: account %s. Please use help commands to get a list of available commands.", cmd[1] ); + } + + return( 1 ); +} + +int cmd_add( irc_t *irc, char **cmd ) +{ + account_t *a; + + if( !( a = account_get( irc, cmd[1] ) ) ) + { + irc_usermsg( irc, "Invalid account" ); + return( 1 ); + } + else if( !( a->gc && ( a->gc->flags & OPT_LOGGED_IN ) ) ) + { + irc_usermsg( irc, "That account is not on-line" ); + return( 1 ); + } + + if( cmd[3] ) + { + if( !nick_ok( cmd[3] ) ) + { + irc_usermsg( irc, "The requested nick `%s' is invalid", cmd[3] ); + return( 0 ); + } + else if( user_find( irc, cmd[3] ) ) + { + irc_usermsg( irc, "The requested nick `%s' already exists", cmd[3] ); + return( 0 ); + } + else + { + nick_set( irc, cmd[2], a->gc->protocol, cmd[3] ); + } + } + a->gc->prpl->add_buddy( a->gc, cmd[2] ); + add_buddy( a->gc, NULL, cmd[2], cmd[2] ); + + irc_usermsg( irc, "User `%s' added to your contact list as `%s'", cmd[2], user_findhandle( a->gc, cmd[2] )->nick ); + + return( 0 ); +} + +int cmd_info( irc_t *irc, char **cmd ) +{ + struct gaim_connection *gc; + account_t *a; + + if( !cmd[2] ) + { + user_t *u = user_find( irc, cmd[1] ); + if( !u || !u->gc ) + { + irc_usermsg( irc, "Nick `%s' does not exist", cmd[1] ); + return( 1 ); + } + gc = u->gc; + cmd[2] = u->handle; + } + else if( !( a = account_get( irc, cmd[1] ) ) ) + { + irc_usermsg( irc, "Invalid account" ); + return( 1 ); + } + else if( !( ( gc = a->gc ) && ( a->gc->flags & OPT_LOGGED_IN ) ) ) + { + irc_usermsg( irc, "That account is not on-line" ); + return( 1 ); + } + + if( !gc->prpl->get_info ) + { + irc_usermsg( irc, "Command `%s' not supported by this protocol", cmd[0] ); + return( 1 ); + } + gc->prpl->get_info( gc, cmd[2] ); + + return( 0 ); +} + +int cmd_rename( irc_t *irc, char **cmd ) +{ + user_t *u; + + if( g_strcasecmp( cmd[1], irc->nick ) == 0 ) + { + irc_usermsg( irc, "Nick `%s' can't be changed", cmd[1] ); + return( 1 ); + } + if( user_find( irc, cmd[2] ) && ( nick_cmp( cmd[1], cmd[2] ) != 0 ) ) + { + irc_usermsg( irc, "Nick `%s' already exists", cmd[2] ); + return( 1 ); + } + if( !nick_ok( cmd[2] ) ) + { + irc_usermsg( irc, "Nick `%s' is invalid", cmd[2] ); + return( 1 ); + } + if( !( u = user_find( irc, cmd[1] ) ) ) + { + irc_usermsg( irc, "Nick `%s' does not exist", cmd[1] ); + return( 1 ); + } + user_rename( irc, cmd[1], cmd[2] ); + irc_write( irc, ":%s!%s@%s NICK %s", cmd[1], u->user, u->host, cmd[2] ); + if( g_strcasecmp( cmd[1], irc->mynick ) == 0 ) + { + g_free( irc->mynick ); + irc->mynick = g_strdup( cmd[2] ); + } + else if( u->send_handler == buddy_send_handler ) + { + nick_set( irc, u->handle, u->gc->protocol, cmd[2] ); + } + + irc_usermsg( irc, "Nick successfully changed" ); + + return( 0 ); +} + +int cmd_remove( irc_t *irc, char **cmd ) +{ + user_t *u; + char *s; + + if( !( u = user_find( irc, cmd[1] ) ) || !u->gc ) + { + irc_usermsg( irc, "Buddy `%s' not found", cmd[1] ); + return( 1 ); + } + s = g_strdup( u->handle ); + + u->gc->prpl->remove_buddy( u->gc, u->handle, NULL ); + user_del( irc, cmd[1] ); + nick_del( irc, cmd[1] ); + + irc_usermsg( irc, "Buddy `%s' (nick %s) removed from contact list", s, cmd[1] ); + g_free( s ); + + return( 0 ); +} + +int cmd_block( irc_t *irc, char **cmd ) +{ + struct gaim_connection *gc; + account_t *a; + + if( !cmd[2] ) + { + user_t *u = user_find( irc, cmd[1] ); + if( !u || !u->gc ) + { + irc_usermsg( irc, "Nick `%s' does not exist", cmd[1] ); + return( 1 ); + } + gc = u->gc; + cmd[2] = u->handle; + } + else if( !( a = account_get( irc, cmd[1] ) ) ) + { + irc_usermsg( irc, "Invalid account" ); + return( 1 ); + } + else if( !( ( gc = a->gc ) && ( a->gc->flags & OPT_LOGGED_IN ) ) ) + { + irc_usermsg( irc, "That account is not on-line" ); + return( 1 ); + } + + if( !gc->prpl->add_deny || !gc->prpl->rem_permit ) + { + irc_usermsg( irc, "Command `%s' not supported by this protocol", cmd[0] ); + } + else + { + gc->prpl->rem_permit( gc, cmd[2] ); + gc->prpl->add_deny( gc, cmd[2] ); + irc_usermsg( irc, "Buddy `%s' moved from your permit- to your deny-list", cmd[2] ); + } + + return( 0 ); +} + +int cmd_allow( irc_t *irc, char **cmd ) +{ + struct gaim_connection *gc; + account_t *a; + + if( !cmd[2] ) + { + user_t *u = user_find( irc, cmd[1] ); + if( !u || !u->gc ) + { + irc_usermsg( irc, "Nick `%s' does not exist", cmd[1] ); + return( 1 ); + } + gc = u->gc; + cmd[2] = u->handle; + } + else if( !( a = account_get( irc, cmd[1] ) ) ) + { + irc_usermsg( irc, "Invalid account" ); + return( 1 ); + } + else if( !( ( gc = a->gc ) && ( a->gc->flags & OPT_LOGGED_IN ) ) ) + { + irc_usermsg( irc, "That account is not on-line" ); + return( 1 ); + } + + if( !gc->prpl->rem_deny || !gc->prpl->add_permit ) + { + irc_usermsg( irc, "Command `%s' not supported by this protocol", cmd[0] ); + } + else + { + gc->prpl->rem_deny( gc, cmd[2] ); + gc->prpl->add_permit( gc, cmd[2] ); + + irc_usermsg( irc, "Buddy `%s' moved from your deny- to your permit-list", cmd[2] ); + } + + return( 0 ); +} + +int cmd_yesno( irc_t *irc, char **cmd ) +{ + query_t *q = NULL; + int numq = 0; + + if( irc->queries == NULL ) + { + irc_usermsg( irc, "Did I ask you something?" ); + return( 0 ); + } + + /* If there's an argument, the user seems to want to answer another question than the + first/last (depending on the query_order setting) one. */ + if( cmd[1] ) + { + if( sscanf( cmd[1], "%d", &numq ) != 1 ) + { + irc_usermsg( irc, "Invalid query number" ); + return( 0 ); + } + + for( q = irc->queries; q; q = q->next, numq -- ) + if( numq == 0 ) + break; + + if( !q ) + { + irc_usermsg( irc, "Uhm, I never asked you something like that..." ); + return( 0 ); + } + } + + if( g_strcasecmp( cmd[0], "yes" ) == 0 ) + query_answer( irc, q, 1 ); + else if( g_strcasecmp( cmd[0], "no" ) == 0 ) + query_answer( irc, q, 0 ); + + return( 1 ); +} + +int cmd_set( irc_t *irc, char **cmd ) +{ + if( cmd[1] && cmd[2] ) + { + set_setstr( irc, cmd[1], cmd[2] ); + } + if( cmd[1] ) /* else 'forgotten' on purpose.. Must show new value after changing */ + { + char *s = set_getstr( irc, cmd[1] ); + if( s ) + irc_usermsg( irc, "%s = `%s'", cmd[1], s ); + } + else + { + set_t *s = irc->set; + while( s ) + { + if( s->value || s->def ) + irc_usermsg( irc, "%s = `%s'", s->key, s->value?s->value:s->def ); + s = s->next; + } + } + + return( 0 ); +} + +int cmd_save( irc_t *irc, char **cmd ) +{ + if( bitlbee_save( irc ) ) + irc_usermsg( irc, "Configuration saved" ); + else + irc_usermsg( irc, "Configuration could not be saved!" ); + + return( 0 ); +} + +int cmd_blist( irc_t *irc, char **cmd ) +{ + int online = 0, away = 0, offline = 0; + user_t *u; + char s[64]; + int n_online = 0, n_away = 0, n_offline = 0; + + if( cmd[1] && g_strcasecmp( cmd[1], "all" ) == 0 ) + online = offline = away = 1; + else if( cmd[1] && g_strcasecmp( cmd[1], "offline" ) == 0 ) + offline = 1; + else if( cmd[1] && g_strcasecmp( cmd[1], "away" ) == 0 ) + away = 1; + else if( cmd[1] && g_strcasecmp( cmd[1], "online" ) == 0 ) + online = 1; + else + online = away = 1; + + irc_usermsg( irc, "%-16.16s %-40.40s %s", "Nick", "User/Host/Network", "Status" ); + + if( online == 1 ) for( u = irc->users; u; u = u->next ) if( u->gc && u->online && !u->away ) + { + g_snprintf( s, 63, "%s@%s (%s)", u->user, u->host, proto_name[u->gc->user->protocol] ); + irc_usermsg( irc, "%-16.16s %-40.40s %s", u->nick, s, "Online" ); + n_online ++; + } + + if( away == 1 ) for( u = irc->users; u; u = u->next ) if( u->gc && u->online && u->away ) + { + g_snprintf( s, 63, "%s@%s (%s)", u->user, u->host, proto_name[u->gc->user->protocol] ); + irc_usermsg( irc, "%-16.16s %-40.40s %s", u->nick, s, u->away ); + n_away ++; + } + + if( offline == 1 ) for( u = irc->users; u; u = u->next ) if( u->gc && !u->online ) + { + g_snprintf( s, 63, "%s@%s (%s)", u->user, u->host, proto_name[u->gc->user->protocol] ); + irc_usermsg( irc, "%-16.16s %-40.40s %s", u->nick, s, "Offline" ); + n_offline ++; + } + + irc_usermsg( irc, "%d buddies (%d available, %d away, %d offline)", n_online + n_away + n_offline, n_online, n_away, n_offline ); + + return( 0 ); +} + +int cmd_nick( irc_t *irc, char **cmd ) +{ + account_t *a; + + if( !cmd[1] || !( a = account_get( irc, cmd[1] ) ) ) + { + irc_usermsg( irc, "Invalid account"); + } + else if( !( a->gc && ( a->gc->flags & OPT_LOGGED_IN ) ) ) + { + irc_usermsg( irc, "That account is not on-line" ); + } + else if ( !cmd[2] ) + { + irc_usermsg( irc, "Your name is `%s'" , a->gc->displayname ? a->gc->displayname : "NULL" ); + } + else if ( !a->gc->prpl->set_info ) + { + irc_usermsg( irc, "Command `%s' not supported by this protocol", cmd[0] ); + } + else + { + char utf8[1024]; + + irc_usermsg( irc, "Setting your name to `%s'", cmd[2] ); + + if( g_strncasecmp( set_getstr( irc, "charset" ), "none", 4 ) != 0 && + do_iconv( set_getstr( irc, "charset" ), "UTF-8", cmd[2], utf8, 0, 1024 ) != -1 ) + a->gc->prpl->set_info( a->gc, utf8 ); + else + a->gc->prpl->set_info( a->gc, cmd[2] ); + } + + return( 1 ); +} + +int cmd_qlist( irc_t *irc, char **cmd ) +{ + query_t *q = irc->queries; + int num; + + if( !q ) + { + irc_usermsg( irc, "There are no pending questions." ); + return( 0 ); + } + + irc_usermsg( irc, "Pending queries:" ); + + for( num = 0; q; q = q->next, num ++ ) + irc_usermsg( irc, "%d, %s", num, q->question ); + + return( 0 ); +} + +int cmd_import_buddies( irc_t *irc, char **cmd ) +{ + struct gaim_connection *gc; + account_t *a; + nick_t *n; + + if( !( a = account_get( irc, cmd[1] ) ) ) + { + irc_usermsg( irc, "Invalid account" ); + return( 0 ); + } + else if( !( ( gc = a->gc ) && ( a->gc->flags & OPT_LOGGED_IN ) ) ) + { + irc_usermsg( irc, "That account is not on-line" ); + return( 0 ); + } + + if( cmd[2] ) + { + if( g_strcasecmp( cmd[2], "clear" ) == 0 ) + { + user_t *u; + + for( u = irc->users; u; u = u->next ) + if( u->gc == gc ) + { + u->gc->prpl->remove_buddy( u->gc, u->handle, NULL ); + user_del( irc, u->nick ); + } + + irc_usermsg( irc, "Old buddy list cleared." ); + } + else + { + irc_usermsg( irc, "Invalid argument: %s", cmd[2] ); + return( 0 ); + } + } + + for( n = gc->irc->nicks; n; n = n->next ) + { + if( n->proto == gc->protocol && !user_findhandle( gc, n->handle ) ) + { + gc->prpl->add_buddy( gc, n->handle ); + add_buddy( gc, NULL, n->handle, NULL ); + } + } + + irc_usermsg( irc, "Sent all add requests. Please wait for a while, the server needs some time to handle all the adds." ); + + return( 0 ); +} + +int cmd_dump( irc_t *irc, char **cmd ) { + write_io_activity(); + irc_usermsg(irc, "Wrote GIO activity log to disk."); + + return( 0 ); +} diff --git a/commands.h b/commands.h new file mode 100644 index 00000000..b878f211 --- /dev/null +++ b/commands.h @@ -0,0 +1,64 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* User manager (root) commands */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef _COMMANDS_H +#define _COMMANDS_H + +#include "bitlbee.h" + +/* Hmm... Linked list? Plleeeeaaase?? ;-) */ + +typedef struct command_t +{ + char *command; + int required_parameters; + int (*execute)(irc_t *, char **args); +} command_t; + +int cmd_account( irc_t *irc, char **cmd ); +int cmd_help( irc_t *irc, char **args); +int cmd_info( irc_t *irc, char **args); +int cmd_add( irc_t *irc, char **args) ; +int cmd_rename( irc_t *irc, char **args ); +int cmd_remove( irc_t *irc, char **args ); +int cmd_block( irc_t *irc, char **args ); +int cmd_allow( irc_t *irc, char **args ); +int cmd_save( irc_t *irc, char **args ); +int cmd_set( irc_t *irc, char **args ); +int cmd_yesno( irc_t *irc, char **args ); +int cmd_identify( irc_t *irc, char **args ); +int cmd_register( irc_t *irc, char **args ); +int cmd_drop( irc_t *irc, char **args ); +int cmd_blist( irc_t *irc, char **cmd ); +int cmd_nick( irc_t *irc, char **cmd ); +int cmd_qlist( irc_t *irc, char **cmd ); +int cmd_import_buddies( irc_t *irc, char **cmd ); +int cmd_dump( irc_t *irc, char **cmd ); + + + +extern command_t commands[]; + +#endif @@ -0,0 +1,281 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Configuration reading code */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "bitlbee.h" + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include "conf.h" +#include "ini.h" +#include "url.h" + +#include "protocols/proxy.h" + +char *CONF_FILE; + +static int conf_loadini( conf_t *conf, char *file ); + +conf_t *conf_load( int argc, char *argv[] ) +{ + conf_t *conf; + int opt, i; + + conf = g_new0( conf_t, 1 ); + + conf->iface = "0.0.0.0"; + conf->port = 6667; + conf->nofork = 0; + conf->verbose = 0; + conf->runmode = RUNMODE_INETD; + conf->authmode = AUTHMODE_OPEN; + conf->password = NULL; + conf->configdir = g_strdup( CONFIG ); + conf->motdfile = g_strdup( ETCDIR "/motd.txt" ); + conf->ping_interval = 180; + conf->ping_timeout = 300; + + i = conf_loadini( conf, CONF_FILE ); + if( i == 0 ) + { + fprintf( stderr, "Error: Syntax error in configuration file `%s'.\n", CONF_FILE ); + return( NULL ); + } + else if( i == -1 ) + { + fprintf( stderr, "Warning: Unable to read configuration file `%s'.\n", CONF_FILE ); + } + + while( ( opt = getopt( argc, argv, "i:p:nvIDc:d:h" ) ) >= 0 ) + { + if( opt == 'i' ) + { + conf->iface = g_strdup( optarg ); + } + else if( opt == 'p' ) + { + if( ( sscanf( optarg, "%d", &i ) != 1 ) || ( i <= 0 ) || ( i > 65535 ) ) + { + fprintf( stderr, "Invalid port number: %s\n", optarg ); + return( NULL ); + } + conf->port = i; + } + else if( opt == 'n' ) + conf->nofork=1; + else if( opt == 'v' ) + conf->verbose=1; + else if( opt == 'I' ) + conf->runmode=RUNMODE_INETD; + else if( opt == 'D' ) + conf->runmode=RUNMODE_DAEMON; + else if( opt == 'c' ) + { + if( strcmp( CONF_FILE, optarg ) != 0 ) + { + g_free( CONF_FILE ); + CONF_FILE = g_strdup( optarg ); + g_free( conf ); + return( conf_load( argc, argv ) ); + } + } + else if( opt == 'd' ) + { + g_free( conf->configdir ); + conf->configdir = g_strdup( optarg ); + } + else if( opt == 'h' ) + { + printf( "Usage: bitlbee [-D [-i <interface>] [-p <port>] [-n] [-v]] [-I]\n" + " [-c <file>] [-d <dir>] [-h]\n" + "\n" + "An IRC-to-other-chat-networks gateway\n" + "\n" + " -I Classic/InetD mode. (Default)\n" + " -D Daemon mode. (Still EXPERIMENTAL!)\n" + " -i Specify the interface (by IP address) to listen on.\n" + " (Default: 0.0.0.0 (any interface))\n" + " -p Port number to listen on. (Default: 6667)\n" + " -n Don't fork.\n" + " -v Be verbose (only works in combination with -n)\n" + " -c Load alternative configuration file\n" + " -d Specify alternative user configuration directory\n" + " -h Show this help page.\n" ); + return( NULL ); + } + } + + if( conf->configdir[strlen(conf->configdir)-1] != '/' ) + { + char *s = g_new( char, strlen( conf->configdir ) + 2 ); + + sprintf( s, "%s/", conf->configdir ); + g_free( conf->configdir ); + conf->configdir = s; + } + + return( conf ); +} + +static int conf_loadini( conf_t *conf, char *file ) +{ + ini_t *ini; + int i; + + ini = ini_open( file ); + if( ini == NULL ) return( -1 ); + while( ini_read( ini ) ) + { + if( g_strcasecmp( ini->section, "settings" ) == 0 ) + { + if( g_strcasecmp( ini->key, "runmode" ) == 0 ) + { + if( g_strcasecmp( ini->value, "daemon" ) == 0 ) + conf->runmode = RUNMODE_DAEMON; + else + conf->runmode = RUNMODE_INETD; + } + else if( g_strcasecmp( ini->key, "daemoninterface" ) == 0 ) + { + conf->iface = g_strdup( ini->value ); + } + else if( g_strcasecmp( ini->key, "daemonport" ) == 0 ) + { + if( ( sscanf( ini->value, "%d", &i ) != 1 ) || ( i <= 0 ) || ( i > 65535 ) ) + { + fprintf( stderr, "Invalid port number: %s\n", ini->value ); + return( 0 ); + } + conf->port = i; + } + else if( g_strcasecmp( ini->key, "authmode" ) == 0 ) + { + if( g_strcasecmp( ini->value, "registered" ) == 0 ) + conf->authmode = AUTHMODE_REGISTERED; + else if( g_strcasecmp( ini->value, "closed" ) == 0 ) + conf->authmode = AUTHMODE_CLOSED; + else + conf->authmode = AUTHMODE_OPEN; + } + else if( g_strcasecmp( ini->key, "authpassword" ) == 0 ) + { + conf->password = g_strdup( ini->value ); + } + else if( g_strcasecmp( ini->key, "hostname" ) == 0 ) + { + conf->hostname = g_strdup( ini->value ); + } + else if( g_strcasecmp( ini->key, "configdir" ) == 0 ) + { + g_free( conf->configdir ); + conf->configdir = g_strdup( ini->value ); + } + else if( g_strcasecmp( ini->key, "motdfile" ) == 0 ) + { + g_free( conf->motdfile ); + conf->motdfile = g_strdup( ini->value ); + } + else if( g_strcasecmp( ini->key, "pinginterval" ) == 0 ) + { + if( sscanf( ini->value, "%d", &i ) != 1 ) + { + fprintf( stderr, "Invalid %s value: %s\n", ini->key, ini->value ); + return( 0 ); + } + conf->ping_interval = i; + } + else if( g_strcasecmp( ini->key, "pingtimeout" ) == 0 ) + { + if( sscanf( ini->value, "%d", &i ) != 1 ) + { + fprintf( stderr, "Invalid %s value: %s\n", ini->key, ini->value ); + return( 0 ); + } + conf->ping_timeout = i; + } + else if( g_strcasecmp( ini->key, "proxy" ) == 0 ) + { + url_t *url = g_new0( url_t, 1 ); + + if( !url_set( url, ini->value ) ) + { + fprintf( stderr, "Invalid %s value: %s\n", ini->key, ini->value ); + g_free( url ); + return( 0 ); + } + + strncpy( proxyhost, url->host, sizeof( proxyhost ) ); + strncpy( proxyuser, url->user, sizeof( proxyuser ) ); + strncpy( proxypass, url->pass, sizeof( proxypass ) ); + proxyport = url->port; + if( url->proto == PROTO_HTTP ) + proxytype = PROXY_HTTP; + else if( url->proto == PROTO_SOCKS4 ) + proxytype = PROXY_SOCKS4; + else if( url->proto == PROTO_SOCKS5 ) + proxytype = PROXY_SOCKS5; + + g_free( url ); + } + else + { + fprintf( stderr, "Error: Unknown setting `%s` in configuration file.\n", ini->key ); + return( 0 ); + /* For now just ignore unknown keys... */ + } + } + else if( g_strcasecmp( ini->section, "defaults" ) != 0 ) + { + fprintf( stderr, "Error: Unknown section [%s] in configuration file. " + "BitlBee configuration must be put in a [settings] section!\n", ini->section ); + return( 0 ); + } + } + ini_close( ini ); + + return( 1 ); +} + +void conf_loaddefaults( irc_t *irc ) +{ + ini_t *ini; + + ini = ini_open( CONF_FILE ); + if( ini == NULL ) return; + while( ini_read( ini ) ) + { + if( g_strcasecmp( ini->section, "defaults" ) == 0 ) + { + set_t *s = set_find( irc, ini->key ); + + if( s ) + { + if( s->def ) g_free( s->def ); + s->def = g_strdup( ini->value ); + } + } + } + ini_close( ini ); +} @@ -0,0 +1,51 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Configuration reading code */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __CONF_H +#define __CONF_H + +typedef enum runmode { RUNMODE_DAEMON, RUNMODE_INETD } runmode_t; +typedef enum authmode { AUTHMODE_OPEN, AUTHMODE_CLOSED, AUTHMODE_REGISTERED } authmode_t; + +typedef struct conf +{ + char *iface; + signed int port; + int nofork; + int verbose; + runmode_t runmode; + authmode_t authmode; + char *password; + char *hostname; + char *configdir; + char *motdfile; + int ping_interval; + int ping_timeout; +} conf_t; + +conf_t *conf_load( int argc, char *argv[] ); +void conf_loaddefaults( irc_t *irc ); + +#endif diff --git a/configure b/configure new file mode 100755 index 00000000..985e8209 --- /dev/null +++ b/configure @@ -0,0 +1,399 @@ +#!/bin/sh + +############################## +## Configurer for BitlBee ## +## ## +## Copyright 2004 Lintux ## +## Copyright 2002 Lucumo ## +############################## + +prefix='/usr/local/' +bindir='$prefix/sbin/' +etcdir='$prefix/etc/bitlbee/' +mandir='$prefix/share/man/' +datadir='$prefix/share/bitlbee/' +config='/var/lib/bitlbee/' + +msn=1 +jabber=1 +oscar=1 +yahoo=1 + +debug=0 +strip=1 +flood=0 +ipv6=1 +ssl=auto + +arch=`uname -s` +cpu=`uname -m` + +echo BitlBee configure + +while [ -n "$1" ]; do + e="`expr "X$1" : 'X--\(.*=.*\)'`" + if [ -z "$e" ]; then + cat<<EOF + +Usage: $0 [OPTIONS] + +Option Description Default + +--prefix=... Directories to put files in $prefix +--bindir=... $bindir +--etcdir=... $etcdir +--mandir=... $mandir +--datadir=... $datadir +--config=... $config + +--msn=0/1 Disable/enable MSN part $msn +--jabber=0/1 Disable/enable Jabber part $jabber +--oscar=0/1 Disable/enable Oscar part (ICQ, AIM) $oscar +--yahoo=0/1 Disable/enable Yahoo part $yahoo + +--debug=0/1 Disable/enable debugging $debug +--strip=0/1 Disable/enable binary stripping $strip + +--flood=0/1 Flood protection $flood +--ipv6=0/1 IPv6 socket support $ipv6 + +--ssl=... SSL library to use (gnutls, nss, openssl, bogus, auto) + $ssl +EOF + exit; + fi + eval "$e" + shift; +done + +# Expand $prefix and get rid of double slashes +bindir=`eval echo "$bindir/" | sed 's/\/\{1,\}/\//g'` +etcdir=`eval echo "$etcdir/" | sed 's/\/\{1,\}/\//g'` +mandir=`eval echo "$mandir/" | sed 's/\/\{1,\}/\//g'` +datadir=`eval echo "$datadir/" | sed 's/\/\{1,\}/\//g'` +config=`eval echo "$config/" | sed 's/\/\{1,\}/\//g'` + +cat<<EOF>Makefile.settings +## BitlBee settings, generated by configure +PREFIX=$prefix +BINDIR=$bindir +ETCDIR=$etcdir +MANDIR=$mandir +DATADIR=$datadir +CONFIG=$config + +ARCH=$arch +CPU=$cpu +OUTFILE=bitlbee + +DESTDIR= +LFLAGS= +EFLAGS= +EOF + +cat<<EOF>config.h +/* BitlBee settings, generated by configure + + Do *NOT* use any of these defines in your code without thinking twice, most + of them can/will be overridden at run-time */ + +#define CONFIG "$config" +#define ETCDIR "$etcdir" +#define VARDIR "$datadir" +#define ARCH "$arch" +#define CPU "$cpu" +EOF + +if [ "$ipv6" = "1" ]; then + echo '#define IPV6' >> config.h +fi + +if [ "$debug" = "1" ]; then + echo 'CFLAGS=-g' >> Makefile.settings + echo 'DEBUG=1' >> Makefile.settings + echo '#define DEBUG' >> config.h +else + echo 'CFLAGS=-O3' >> Makefile.settings +fi + +echo CFLAGS+=-I`pwd` -I`pwd`/protocols -I. >> Makefile.settings + +if [ -n "$CC" ]; then + echo "CC=$CC" >> Makefile.settings; +elif type gcc > /dev/null 2> /dev/null; then + echo "CC=gcc" >> Makefile.settings; +elif type cc > /dev/null 2> /dev/null; then + echo "CC=cc" >> Makefile.settings; +else + echo 'Cannot find a C compiler, aborting.' + exit 1; +fi + +if [ -n "$LD" ]; then + echo "LD=$LD" >> Makefile.settings; +elif type ld > /dev/null 2> /dev/null; then + echo "LD=ld" >> Makefile.settings; +else + echo 'Cannot find ld, aborting.' + exit 1; +fi + +if type pkg-config > /dev/null 2>/dev/null && pkg-config glib-2.0; then + cat<<EOF>>Makefile.settings +EFLAGS+=`pkg-config --libs glib-2.0` +CFLAGS+=`pkg-config --cflags glib-2.0` +EOF + echo '#define GLIB2' >> config.h +elif type glib-config > /dev/null 2> /dev/null; then + cat<<EOF>>Makefile.settings +EFLAGS+=`glib-config --libs` +CFLAGS+=`glib-config --cflags` +EOF + echo '#define GLIB1' >> config.h +else + echo 'Cannot find glib development libraries, aborting. (Install libglib-dev?)' + exit 1; +fi + +if [ -r /usr/include/iconv.h ]; then + :; +elif [ -r /usr/local/include/iconv.h ]; then + echo CFLAGS+=-I/usr/local/include >> Makefile.settings; +else + echo + echo 'Warning: Could not find iconv.h, you might have to install it and/or modify' + echo 'Makefile.settings to tell where this file is.'; +fi + + +detect_gnutls() +{ + if libgnutls-config --version > /dev/null 2> /dev/null; then + cat <<EOF>>Makefile.settings +EFLAGS+=`libgnutls-config --libs` +CFLAGS+=`libgnutls-config --cflags` +EOF + + ssl=gnutls + ret=1; + else + ret=0; + fi; +} + +detect_nss() +{ + if type pkg-config > /dev/null 2>/dev/null && pkg-config mozilla-nss; then + cat<<EOF>>Makefile.settings +EFLAGS+=`pkg-config --libs mozilla-nss` +CFLAGS+=`pkg-config --cflags mozilla-nss` +EOF + + ssl=nss + ret=1; + else + ret=0; + fi; +} + +if [ "$msn" = 1 -o "$jabber" = 1 ]; then + if [ "$ssl" = "auto" ]; then + detect_gnutls + if [ "$ret" = "0" ]; then + detect_nss + fi; + elif [ "$ssl" = "gnutls" ]; then + detect_gnutls; + elif [ "$ssl" = "nss" ]; then + detect_nss; + elif [ "$ssl" = "openssl" ]; then + echo + echo 'No detection code exists for OpenSSL. Make sure that you have a complete' + echo 'install of OpenSSL (including devel/header files) before reporting' + echo 'compilation problems.' + echo + echo 'Also, keep in mind that the OpenSSL is, according to some people, not' + echo 'completely GPL-compatible. Using GnuTLS or NSS is recommended and better' + echo 'supported by us. However, on many BSD machines, OpenSSL can be considered' + echo 'part of the operating system, which makes it GPL-compatible.' + echo + echo 'For more info, see: http://www.openssl.org/support/faq.html#LEGAL2' + echo ' http://www.gnome.org/~markmc/openssl-and-the-gpl.html' + echo + echo 'Please note that distributing a BitlBee binary which links to OpenSSL is' + echo 'probably illegal. If you want to create and distribute a binary BitlBee' + echo 'package, you really should use GnuTLS or NSS instead.' + echo + echo 'Also, the OpenSSL license requires us to say this:' + echo ' * "This product includes software developed by the OpenSSL Project' + echo ' * for use in the OpenSSL Toolkit. (http://www.openssl.org/)"' + + echo 'EFLAGS+=-lssl -lcrypto' >> Makefile.settings + + ret=1; + elif [ "$ssl" = "bogus" ]; then + echo + echo 'Using bogus SSL code. This will not make the MSN module work, but it will' + echo 'allow you to use the Jabber module - although without working SSL support.' + + ret=1; + else + echo + echo 'ERROR: Unknown SSL library specified.' + exit 1; + fi + + if [ "$ret" = "0" ]; then + echo + echo 'WARNING: Could not find a suitable SSL library (GnuTLS, libnss or OpenSSL).' + echo ' This is necessary for MSN and full Jabber support. To continue,' + echo ' install a suitable SSL library or disable MSN support (--msn=0).' + echo ' If you want Jabber without SSL support you can try --ssl=bogus.' + + exit 1; + fi; + + echo 'SSL_CLIENT=ssl_'$ssl'.o' >> Makefile.settings +fi + +if [ "$strip" = 0 ]; then + echo "STRIP=\# skip strip" >> Makefile.settings; +else + if [ "$debug" = 1 ]; then + echo + echo 'Stripping binaries does not make sense when debugging. Stripping disabled.' + echo 'STRIP=\# skip strip' >> Makefile.settings + strip=0; + elif [ -n "$STRIP" ]; then + echo "STRIP=$STRIP" >> Makefile.settings; + elif type strip > /dev/null 2> /dev/null; then + echo "STRIP=strip" >> Makefile.settings; + elif /bin/test -x /usr/ccs/bin/strip; then + echo "STRIP=/usr/ccs/bin/strip" >> Makefile.settings; + else + echo + echo 'No strip utility found, cannot remove unnecessary parts from executable.' + echo 'STRIP=\# skip strip' >> Makefile.settings + strip=0; + fi; +fi + +if [ "$flood" = 1 ]; then + echo '#define FLOOD_SEND' >> config.h +fi + +if [ -n "$BITLBEE_VERSION" ]; then + echo + echo 'Spoofing version number: '$BITLBEE_VERSION + echo '#undef BITLBEE_VERSION' >> config.h + echo '#define BITLBEE_VERSION '$BITLBEE_VERSION >> config.h; +fi + +protocols='' +protoobjs='' + +if [ "$msn" = 0 ]; then + echo '#undef WITH_MSN' >> config.h +else + echo '#define WITH_MSN' >> config.h + protocols=$protocols'msn ' + protoobjs=$protoobjs'msnn.o ' +fi + +if [ "$jabber" = 0 ]; then + echo '#undef WITH_JABBER' >> config.h +else + echo '#define WITH_JABBER' >> config.h + protocols=$protocols'jabber ' + protoobjs=$protoobjs'jabberr.o ' +fi + +if [ "$oscar" = 0 ]; then + echo '#undef WITH_OSCAR' >> config.h +else + echo '#define WITH_OSCAR' >> config.h + protocols=$protocols'oscar ' + protoobjs=$protoobjs'oscarr.o ' +fi + +if [ "$yahoo" = 0 ]; then + echo '#undef WITH_YAHOO' >> config.h +else + echo '#define WITH_YAHOO' >> config.h + protocols=$protocols'yahoo ' + protoobjs=$protoobjs'yahooo.o ' +fi + +if [ "$protocols" = "PROTOCOLS = " ]; then + echo + echo "WARNING: You haven't selected any communication protocol to compile!" + echo " Bitlbee will run, but you will be unable to connect to IM servers!" +fi + +echo "PROTOCOLS = $protocols" >> Makefile.settings +echo "PROTOOBJS = $protoobjs" >> Makefile.settings + +echo +echo Architecture: $arch +case "$arch" in +Linux ) + echo 'Linux.' +;; +GNU/* ) + echo 'Debian with non-Linux kernel?' +;; +*BSD ) + echo '*BSD.' + echo 'EFLAGS+=-liconv' >> Makefile.settings; +;; +SunOS ) + echo 'Solaris.' + echo 'EFLAGS+=-lresolv -lnsl -lsocket' >> Makefile.settings + echo 'STRIP=\# skip strip' >> Makefile.settings + echo 'EFLAGS+=-liconv' >> Makefile.settings; +;; +Darwin ) + echo 'Darwin/Mac OS X.' + echo 'EFLAGS+=-liconv' >> Makefile.settings; +;; +IRIX ) + echo 'IRIX.' +;; +CYGWIN* ) + echo 'Cygwin is not officially supported.' +;; +* ) + echo 'We haven'\''t tested BitlBee on many platforms yet, yours is untested. YMMV. Please report any problems to <wilmer@gaast.net>.' +;; +esac + +echo +echo 'Configuration done:' + +if [ "$debug" = "1" ]; then + echo ' Debugging enabled.'; +else + echo ' Debugging disabled.'; +fi + +if [ "$strip" = "1" ]; then + echo ' Binary stripping enabled.'; +else + echo ' Binary stripping disabled.'; +fi + +if [ "$msn" = "1" ]; then + echo ' Using SSL library: '$ssl; +fi + +if [ "$flood" = "0" ]; then + echo ' Flood protection disabled.'; +else + echo ' Flood protection enabled.'; +fi + +if [ -n "$protocols" ]; then + echo ' Building with these protocols:' $protocols; +else + echo ' Building without IM-protocol support. We wish you a lot of fun...'; +fi diff --git a/crypting.c b/crypting.c new file mode 100644 index 00000000..b2e523ff --- /dev/null +++ b/crypting.c @@ -0,0 +1,244 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Sjoerd Hemminga and others * + \********************************************************************/ + +/* A little bit of encryption for the users' passwords */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +/* [WvG] This file can also be compiled into a stand-alone program + which can encode/decode BitlBee account files. The main() will be + included if CRYPTING_MAIN is defined. Or just do "make decode" and + the programs will be built. */ + +#ifndef CRYPTING_MAIN +#define BITLBEE_CORE +#include "bitlbee.h" +#include "irc.h" +#include "md5.h" +#include "crypting.h" +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +#else + +typedef struct irc +{ + char *password; +} irc_t; + +#define set_add( a, b, c, d ) +#define set_find( a, b ) NULL + +#include "md5.h" +#include "crypting.h" +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +#endif + +/*\ + * [SH] Do _not_ call this if it's not entirely sure that it will not cause + * harm to another users file, since this does not check the password for + * correctness. +\*/ + +/* USE WITH CAUTION! + Sets pass without checking */ +void setpassnc (irc_t *irc, char *pass) { + if (!set_find (irc, "password")) + set_add (irc, "password", NULL, passchange); + + if (irc->password) g_free (irc->password); + + if (pass) { + irc->password = g_strdup (pass); + irc_usermsg (irc, "Password successfully changed"); + } else { + irc->password = NULL; + } +} + +char *passchange (irc_t *irc, void *set, char *value) { + setpassnc (irc, value); + return (NULL); +} + +int setpass (irc_t *irc, char *pass, char* md5sum) { + md5_state_t md5state; + md5_byte_t digest[16]; + int i, j; + char digits[3]; + + md5_init (&md5state); + md5_append (&md5state, (unsigned char *)pass, strlen (pass)); + md5_finish (&md5state, digest); + + for (i = 0, j = 0; i < 16; i++, j += 2) { + /* Check password for correctness */ + g_snprintf (digits, sizeof (digits), "%02x\n", digest[i]); + + if (digits[0] != md5sum[j]) return (-1); + if (digits[1] != md5sum[j + 1]) return (-1); + } + + /* If pass is correct, we end up here and we set the pass */ + setpassnc (irc, pass); + + return (0); +} + +char *hashpass (irc_t *irc) { + md5_state_t md5state; + md5_byte_t digest[16]; + int i; + char digits[3]; + char *rv; + + if (irc->password == NULL) return (NULL); + + rv = (char *)g_malloc (33); + memset (rv, 0, 33); + + md5_init (&md5state); + md5_append (&md5state, (unsigned char *)irc->password, strlen (irc->password)); + md5_finish (&md5state, digest); + + for (i = 0; i < 16; i++) { + /* Build a hash of the pass */ + g_snprintf (digits, sizeof (digits), "%02x", digest[i]); + strcat (rv, digits); + } + + return (rv); +} + +char *obfucrypt (irc_t *irc, char *line) { + int i, j; + char *rv; + + if (irc->password == NULL) return (NULL); + + rv = (char *)g_malloc (strlen (line) + 1); + memset (rv, '\0', strlen (line) + 1); + + i = j = 0; + while (*line) { + /* Encrypt/obfuscate the line, using the password */ + if (*(signed char*)line < 0) *line = - (*line); + if (((signed char*)irc->password)[i] < 0) irc->password[i] = - irc->password[i]; + + rv[j] = *line + irc->password[i]; /* Overflow intended */ + + line++; + if (!irc->password[++i]) i = 0; + j++; + } + + return (rv); +} + +char *deobfucrypt (irc_t *irc, char *line) { + int i, j; + char *rv; + + if (irc->password == NULL) return (NULL); + + rv = (char *)g_malloc (strlen (line) + 1); + memset (rv, '\0', strlen (line) + 1); + + i = j = 0; + while (*line) { + /* Decrypt/deobfuscate the line, using the pass */ + rv[j] = *line - irc->password[i]; /* Overflow intended */ + + line++; + if (!irc->password[++i]) i = 0; + j++; + } + + return (rv); +} + +#ifdef CRYPTING_MAIN + +/* A little main() function for people who want a stand-alone program to + encode/decode BitlCrypted files. */ + +int main( int argc, char *argv[] ) +{ + irc_t *irc = g_malloc( sizeof( irc_t ) ); + char *hash, *action, line[256]; + char* (*func)( irc_t *, char * ); + + if( argc < 2 ) + { + fprintf( stderr, "Usage: %s <password>\n\n" + "Reads from stdin, writes to stdout.\n" + "Call as \"encode\" to encode, \"decode\" to decode.\n", argv[0] ); + return( 1 ); + } + + memset( irc, 0, sizeof( irc_t ) ); + irc->password = g_strdup( argv[1] ); + + hash = hashpass( irc ); + action = argv[0] + strlen( argv[0] ) - strlen( "encode" ); + + if( strcmp( action, "encode" ) == 0 ) + { + fwrite( hash, 32, 1, stdout ); + func = obfucrypt; + } + else if( strcmp( action, "decode" ) == 0 ) + { + char hash2[32]; + + fread( hash2, 32, 1, stdin ); + if( memcmp( hash, hash2, 32 ) != 0 ) + { + fprintf( stderr, "Passwords don't match. Can't decode.\n" ); + return( 1 ); + } + func = deobfucrypt; + } + else + { + return( main( 0, NULL ) ); + } + + while( fscanf( stdin, "%[^\n]255s", line ) > 0 ) + { + char *out; + + /* Flush the newline */ + fgetc( stdin ); + + out = func( irc, line ); + printf( "%s\n", out ); + g_free( out ); + } + + return( 0 ); +} + +#endif diff --git a/crypting.h b/crypting.h new file mode 100644 index 00000000..0d4f0873 --- /dev/null +++ b/crypting.h @@ -0,0 +1,31 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Sjoerd Hemminga and others * + \********************************************************************/ + +/* A little bit of encryption for the users' passwords */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +void setpassnc (irc_t *irc, char *pass); /* USE WITH CAUTION! */ +char *passchange (irc_t *irc, void *set, char *value); +int setpass (irc_t *irc, char *pass, char* md5sum); +char *hashpass (irc_t *irc); +char *obfucrypt (irc_t *irc, char *line); +char *deobfucrypt (irc_t *irc, char *line); diff --git a/debian/README.Debian b/debian/README.Debian new file mode 100644 index 00000000..02c5050a --- /dev/null +++ b/debian/README.Debian @@ -0,0 +1,12 @@ +Debconf should have asked you on what port you want BitlBee to run. If it +did not, the port number is 6667 or 6668. (6668 if you already got an ircd +running at 6667) + +Fire up your favourite IRC client and connect to localhost:6667 (or 6668), +and read the documentation (type help for a list of commands). + +Have fun! + +The /usr/share/doc/bitlbee/examples/ directory contains some programs and +scripts you might like or need. They're not really examples but it's quite +normal behaviour to put small contrib stuff like that in there. diff --git a/debian/bitlbee.conffiles b/debian/bitlbee.conffiles new file mode 100644 index 00000000..2ccc958d --- /dev/null +++ b/debian/bitlbee.conffiles @@ -0,0 +1,2 @@ +/etc/bitlbee/motd.txt +/etc/bitlbee/bitlbee.conf diff --git a/debian/bitlbee.config b/debian/bitlbee.config new file mode 100755 index 00000000..3a04813d --- /dev/null +++ b/debian/bitlbee.config @@ -0,0 +1,19 @@ +#!/bin/sh -e + +. /usr/share/debconf/confmodule + +db_title BitlBee + +db_get bitlbee/serveport +if [ "$RET" = "stillhavetoask" ]; then + if netstat -ltn | grep ':6667' 2> /dev/null > /dev/null; then + port=6668; + else + port=6667; + fi + db_set bitlbee/serveport $port; +fi + +if db_input medium bitlbee/serveport; then + db_go; +fi diff --git a/debian/bitlbee.postinst b/debian/bitlbee.postinst new file mode 100755 index 00000000..92f75246 --- /dev/null +++ b/debian/bitlbee.postinst @@ -0,0 +1,22 @@ +#!/bin/sh -e + +. /usr/share/debconf/confmodule + +db_get bitlbee/serveport +PORT="$RET" + +TCPD='/usr/sbin/tcpd' +CONFDIR=/var/lib/bitlbee/ + +update-inetd --remove '/usr/sbin/bitlbee.*$' +if [ -n "$PORT" ]; then + update-inetd --group OTHER --add "$PORT"'\tstream\ttcp\tnowait\tbitlbee\t'"$TCPD"'\t/usr/sbin/bitlbee' +fi + +if [ -d $CONFDIR ] && chown bitlbee $CONFDIR; then + echo 'BitlBee (probably) already installed, skipping user/configdir installation' + exit 0; +fi + +adduser --system --home /var/lib/bitlbee/ --disabled-login --disabled-password bitlbee +chmod 700 /var/lib/bitlbee/ diff --git a/debian/bitlbee.postrm b/debian/bitlbee.postrm new file mode 100755 index 00000000..14bee47e --- /dev/null +++ b/debian/bitlbee.postrm @@ -0,0 +1,12 @@ +#!/bin/sh -e + +[ "$1" = "purge" ] || exit 0 + +if [ -e /usr/share/debconf/confmodule ]; then + . /usr/share/debconf/confmodule; + db_purge; +fi + +update-inetd --remove '/usr/sbin/bitlbee.*$' + +deluser --remove-home bitlbee || true diff --git a/debian/bitlbee.preinst b/debian/bitlbee.preinst new file mode 100755 index 00000000..744d7520 --- /dev/null +++ b/debian/bitlbee.preinst @@ -0,0 +1,4 @@ +#!/bin/sh -e + +## To prevent the help function from breaking in currently running BitlBee processes +rm -f /usr/share/bitlbee/help.txt diff --git a/debian/bitlbee.templates b/debian/bitlbee.templates new file mode 100644 index 00000000..3bb7ad50 --- /dev/null +++ b/debian/bitlbee.templates @@ -0,0 +1,57 @@ +Template: bitlbee/serveport +Type: string +Default: stillhavetoask +Description: On what TCP port should BitlBee listen for connections? + BitlBee normally listens on the regular IRC port, 6667. This might not be + a very good idea when you're running a real IRC daemon as well. 6668 might + be a good alternative. Leaving this value blank means that BitlBee will not + be run automatically. +Description-cs: Na kterém TCP portu má BitlBee naslouchat pøíchozím spojením? + BitlBee normálnì naslouchá na bì¾ném IRC portu 6667. Pokud máte spu¹tìný i + reálný IRC daemon, tak to nemusí být nejlep¹í nápad. Vhodná alternativa + mù¾e být 6668. Ponecháte-li pole prázdné, znamená to, ¾e se BitlBee nebude + spou¹tìt automaticky. +Description-de: An welchem TCP-Port soll BitlBee auf Verbindungen warten? + BitlBee lauscht normalerweise an dem üblichen IRC-Port 6667. Dies ist aber + keine gute Idee, wenn Sie außerdem noch einen richtigen IRC-Dienst + betreiben. Das Port 6668 ist eine gute Alternative. Wenn Sie keinen Wert + eingeben, wird BitlBee nicht automatisch starten. +Description-es: ¿En qué puerto TCP quiere que BitlBee escuche conexiones? + BitlBee normalmente escucha en el puerto 6667, que se usa también para + IRC. Por esta razón no es muy buena idea poner a BitlBee a escuchar en ese + puerto si también se está ejecutando un demonio real de IRC, en este caso + el puerto 6668 puede ser una buena alternativa. Si deja este valor en + blanco BitlBee no se ejecutará automáticamente. +Description-fr: Sur quel port TCP BitlBee doit-il être à l'écoute ? + BitlBee est usuellement à l'écoute sur le port IRC standard : 6667. Cela + n'est pas forcément un choix adapté si vous utilisez en même temps un vrai + démon IRC. Dans ce cas, choisir 6668 est conseillé. Si vous ne souhaitez + pas lancer BitlBee automatiquement, veuillez laissez ce champs vide. +Description-ja: BitlBee ¤Ï¡¢Àܳ¤Î¤¿¤á¤Ë¤É¤Î TCP ¥Ý¡¼¥È¤ò listen ¤·¤Þ¤¹¤«? + BitlBee ¤ÏÄ̾ï¤Î¾ì¹ç¡¢É¸½à¤Î IRC ¥Ý¡¼¥ÈÈÖ¹æ¤Ç¤¢¤ë 6667 ¤ò listen + ¤·¤Þ¤¹¡£Æ±Íͤˤ·¤Æ¼ÂºÝ¤Î IRC + ¥Ç¡¼¥â¥ó¤òưºî¤µ¤»¤Æ¤¤¤ë¾ì¹ç¡¢¤³¤ì¤Ï¤¢¤Þ¤êÎɤ¤¹Í¤¨¤Ç¤Ï̵¤¤¤«¤â¤·¤ì¤Þ¤»¤ó¡£Âå¤ï¤ê¤Ë + 6668 ¤ò»È¤¦¤Î¤¬Îɤ¤¤«¤âÃΤì¤Þ¤»¤ó¡£¤³¤ì¤ò¶õ¤Î¤Þ¤Þ¤Ë¤·¤Æ¤ª¤±¤Ð¡¢BitlBee + ¤Ï¼«Æ°Åª¤Ë¤Ïµ¯Æ°¤Ê¤¯¤Ê¤ê¤Þ¤¹¡£ +Description-nl: Op welke TCP poort moet BitlBee draaien? + Normaal 'luistert' BitlBee op de gebruikelijke IRC poort, 6667. Als je al + een andere IRC daemon draait is dat onmogelijk. Kies dan bijvoorbeeld voor + poort 6668. Als je niet wil dat BitlBee automatisch gestart wordt, vul + hier dan niets in. +Description-pt_BR: Em qual porta TCP o BitlBee deverá ouvir por conexões ? + O BitlBee normalmente ouve na porta de IRC padrão, 6667. Porém, esta pode + não ser uma boa idéia quando você está rodando um daemon IRC real também. + 6689 pode ser uma boa alternativa. Deixar esse valor em branco significa + que o BitlBee não será executado automaticamente. +Description-sv: På vilken TCP-port ska BitlBee lyssna på efter anslutningar? + BitlBee lyssnar normalt på den standardporten för IRC, 6667. Detta kanske + inte är en bra ide om du kör en riktig IRC-daemon på samma system. 6668 + kan vara ett bra alternativ då. Lämnar du detta värde blankt betyder det + att BitlBee inte kommer att startas automatiskt. +Description-vi: Trình BitlBee nên lắng nghe sá»± kết nối trên cổng TCP nà o? + Trình BitlBee thưá»ng lắng nghe trên cổng IRC bình thưá»ng, + 6667. Có lẽ nó không phải là má»™t ý kiến tốt nếu bạn + cÅ©ng có chạy má»™t trình ná»n (dæmon) IRC tháºt. Như thế thì, + cổng 6668 có thể là má»™t Ä‘iá»u thay thế tốt. Nếu bạn bá» + giá trị nà y rá»—ng, thì trình BitlBee sẽ không tá»± động + chạy. diff --git a/debian/bitlbee.templates.master b/debian/bitlbee.templates.master new file mode 100644 index 00000000..0cd04426 --- /dev/null +++ b/debian/bitlbee.templates.master @@ -0,0 +1,8 @@ +Template: bitlbee/serveport +Type: string +Default: stillhavetoask +_Description: On what TCP port should BitlBee listen for connections? + BitlBee normally listens on the regular IRC port, 6667. This might not be + a very good idea when you're running a real IRC daemon as well. 6668 might + be a good alternative. Leaving this value blank means that BitlBee will not + be run automatically. diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 00000000..2ae9ccbb --- /dev/null +++ b/debian/changelog @@ -0,0 +1,235 @@ +bitlbee (0.99-1) unstable; urgency=low + + * Should build on Debian GNU/kFreeBSD now. (Closes: #336965) + * New upstream version. + + -- Wilmer van der Gaast <wilmer@gaast.net> Thu, 3 Nov 2005 21:06:53 +0100 + +bitlbee (0.93a-1) unstable; urgency=low + + * Added Swedish and Spanish translations. (Closes: #333881, #331302) + * Changed debconf dependency. (Closes: #331762) + * Changed libgnutls dependency. (Closes: #335751) + * Fixed one crash-on-disconnect bug in the OSCAR module. + + -- Wilmer van der Gaast <wilmer@gaast.net> Tue, 1 Nov 2005 18:25:56 +0100 + +bitlbee (0.92-2) unstable; urgency=low + + * Added the patch that allows to connect to alternate Jabber servers. + Necessary for connecting to Google Talk. (Closes: #324832) + * Also possibly fixes some more problems with losing data when disk is + full. + * Added Vietnamese and Brazilian DebConf translations. Sorry for being + so late. (Closes: #297058, #313158) + + -- Wilmer van der Gaast <lintux@debian.org> Thu, 8 Sep 2005 19:55:56 +0200 + +bitlbee (0.92-1) unstable; urgency=low + + * New upstream release. + * Implemented support for the IRC WATCH command and got rid of the + IRC_MAX_ARGS limit. (Closes: #283504) + * Added Czech translation. (Closes: #293615) + + -- Wilmer van der Gaast <lintux@debian.org> Thu, 24 Feb 2005 17:11:32 +0100 + +bitlbee (0.91-3) unstable; urgency=low + + * Fixed a small bug in postrm which caused problems when removing/upgrading. + + -- Wilmer van der Gaast <lintux@debian.org> Sun, 10 Oct 2004 08:59:52 +0200 + +bitlbee (0.91-2) unstable; urgency=low + + * Removed the part that messes with tcpd configuration files because it + causes troubles for some people and because it's no problem for users + to edit those files by hand. (Closes: #275418) + When upgrading from previous versions, the bitlbee line won't be removed + from your tcpd conffiles. (This is only done when purging a BitlBee + install) You don't have to worry about BitlBee suddenly opening for the + whole world because of the removal of this feature. + * Updated German translation. (Closes: #274655) + * Removed the unreliable check for an existing BitlBee installation (a + /etc/passwd grep) and replaced it with something more reliable. + + -- Wilmer van der Gaast <lintux@debian.org> Sat, 9 Oct 2004 19:06:33 +0200 + +bitlbee (0.91-1) unstable; urgency=low + + * info-command works for Jabber connections now. (Closes: #232712) + * Saner code for duplicate nickname prevention. (Closes: #234285) + * Support for Jabber connections over SSL. (Closes: #252458) + * If the user chooses for noinetd.conf installation, this setting is now + remembered during reinstalls. (Closes: #260533) + * An up-to-date Japanse DebConf template. (Closes: #271091) + + -- Wilmer van der Gaast <lintux@debian.org> Sat, 25 Sep 2004 18:18:17 +0200 + +bitlbee (0.90a-2) unstable; urgency=low + + * Using libgnutls11 now. (Closes: #264740) + * postinst no longer appends newlines to hosts.* because grep already + makes sure the last line is terminated with a newline. (Closes: #253278) + * Added Japanese DebConf templates. (Closes: #259801) + * Installing BitlBee in inetd.conf is now optional. (Closes: #260533) + + -- Wilmer van der Gaast <lintux@debian.org> Mon, 6 Sep 2004 20:04:22 +0200 + +bitlbee (0.90a-1) unstable; urgency=low + + * New upstream release. + + -- Wilmer van der Gaast <lintux@debian.org> Mon, 28 Jun 2004 20:30:26 +0200 + +bitlbee (0.90-1) unstable; urgency=low + + * New upstream release. + * Added German DebConf translation. (Closes: #250787) + + -- Wilmer van der Gaast <lintux@debian.org> Sat, 29 May 2004 11:51:56 +0200 + +bitlbee (0.85a-1) unstable; urgency=low + + * New upstream release. This one should fix build problems on arm. + + -- Wilmer van der Gaast <lintux@debian.org> Thu, 25 Mar 2004 00:12:33 +0100 + +bitlbee (0.85-1) unstable; urgency=low + + * New upstream release. + * This version has a command line switch to specify alternate configuration + files/settings directories. (Closes: #207060) + + -- Wilmer van der Gaast <lintux@debian.org> Sat, 13 Mar 2004 22:19:35 +0100 + +bitlbee (0.84-2) unstable; urgency=low + + * Converted debconf templates to po2debconf format, without breaking + building on older (non-po2debconf) systems. Thanks to Martin Quinson. + (Closes: #205816) + * Added French debconf templates. Thanks to Christian Perrier. + (Closes: #206593) + + -- Wilmer van der Gaast <lintux@debian.org> Wed, 3 Mar 2004 21:19:12 +0100 + +bitlbee (0.84-1) unstable; urgency=low + + * New upstream release. + + -- Wilmer van der Gaast <lintux@debian.org> Fri, 13 Feb 2004 20:13:53 +0100 + +bitlbee (0.83-2) unstable; urgency=low + + * Removed libsoup dependency, BitlBee now uses libgnutls directly. + (Closes: #208475, #230895) + * Now including preprocessed documentation files to save some time on + slow buildd's (and fix build problems on archs without a working + sgmltools package). + + -- Wilmer van der Gaast <lintux@debian.org> Fri, 6 Feb 2004 01:26:27 +0100 + +bitlbee (0.83-1) unstable; urgency=low + + * Added bitlbee.conf to conffiles. Should've done that before, sorry. + * Sorry, still with MSN support disabled, because Debian's default + libsoup package won't work with BitlBee-MSN. + + -- Wilmer van der Gaast <lintux@debian.org> Wed, 31 Dec 2003 00:56:57 +0100 + +bitlbee (0.82-1) unstable; urgency=low + + * New upstream release. + * Disabled MSN support in the Debian version for now, because it needs + a patched version of libsoup. If you want MSN support, you'll have to + create one yourself and install a patched version of libsoup. + + -- Wilmer van der Gaast <lintux@debian.org> Fri, 31 Oct 2003 21:51:01 +0100 + +bitlbee (0.81a-1) unstable; urgency=low + + * New upstream release. + + -- Wilmer van der Gaast <lintux@debian.org> Wed, 16 Oct 2003 16:21:31 +0200 + +bitlbee (0.81-1) unstable; urgency=low + + * New upstream release. + * Fixes Yahoo! problems. (Closes: #213876) + + -- Wilmer van der Gaast <lintux@debian.org> Wed, 15 Oct 2003 16:00:00 +0200 + +bitlbee (0.80-1) unstable; urgency=low + + * New upstream release. + * preinst now unlinks the old helpfile while upgrading, see README.Debian + for details. + * 'Upgraded' to standards 3.5.9. + * "jabber: Non-ascii away messages not supported" patch included. + (Closes: #195852) + + -- Wilmer van der Gaast <lintux@debian.org> Tue, 24 Jun 2003 20:00:00 +0200 + +bitlbee (0.74a-1) unstable; urgency=low + + * This one actually does contain the bugfix 0.74 should've had. + + -- Wilmer van der Gaast <lintux@debian.org> Wed, 11 Jun 2003 13:44:01 +0200 + +bitlbee (0.74-1) unstable; urgency=high + + * Security release, fixing a little not-too-dangerous security bug. + + -- Wilmer van der Gaast <lintux@debian.org> Tue, 10 Jun 2003 22:50:19 +0200 + +bitlbee (0.73-1) unstable; urgency=low + + * New upstream release. + + -- Wilmer van der Gaast <lintux@debian.org> Sun, 13 Apr 2003 01:20:49 +0200 + +bitlbee (0.72-2) unstable; urgency=low + + * Now uses '127.0.0.1' as default for hosts.allow instead of 'localhost'. + (Closes: #174219) + * Fixed some other portability issues. (Closes: #177394) + * Added w3m builddep, needed for .txt documentation generation. + * Removed jadetex builddep because it seems not to be necessary after all. + + -- Wilmer van der Gaast <lintux@debian.org> Tue, 21 Jan 2003 01:35:46 +0100 + +bitlbee (0.72-1) unstable; urgency=low + + * BitlBee doesn't have tcpd in it anymore; external tcpd is used now. + * Added an examples/ directory. + * Fixed arm/ppc/s390 portability issue on char signedness. (Closes: #161026) + + -- Wilmer van der Gaast <lintux@debian.org> Thu, 19 Dec 2002 00:24:29 +0100 + +bitlbee (0.71-1) unstable; urgency=low + + * New upstream release. + + -- Wilmer van der Gaast <lintux@debian.org> Mon, 16 Sep 2002 01:02:09 +0200 + +bitlbee (0.7-2) unstable; urgency=low + + * Second try at a good upload. + + -- Wilmer van der Gaast <lintux@debian.org> Thu, 15 Aug 2002 20:14:54 +0200 + +bitlbee (0.7-1) unstable; urgency=low + + * First public release. (Closes: #153190) + + -- Wilmer van der Gaast <lintux@debian.org> Sat, 10 Aug 2002 04:47:07 +0200 + +bitlbee (0.6-1) unstable; urgency=low + + * Initial Release. (Testing only, not for release.) + + -- Wilmer van der Gaast <lintux@debian.org> Wed, 10 Jul 2002 11:02:28 +0200 + +Local variables: +mode: debian-changelog +End: diff --git a/debian/control b/debian/control new file mode 100644 index 00000000..6db36986 --- /dev/null +++ b/debian/control @@ -0,0 +1,13 @@ +Source: bitlbee +Section: net +Priority: optional +Maintainer: Wilmer van der Gaast <wilmer@gaast.net> +Standards-Version: 3.5.9 +Build-Depends: libglib2.0-dev | libglib-dev, libgnutls-dev | libnss-dev (>= 1.6), debconf-2.0 + +Package: bitlbee +Architecture: any +Depends: ${shlibs:Depends}, adduser, netbase, netkit-inetd, net-tools, ${debconf-depends}, debianutils (>= 1.16), tcpd +Description: An IRC to other chat networks gateway + This program can be used as an IRC server which forwards everything you + say to people on other chat networks: Jabber, ICQ, AIM, MSN and Yahoo. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 00000000..53c04657 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,895 @@ +This package was debianized by Wilmer van der Gaast <lintux@debian.org> on +Mon, 8 Jul 2002 13:17:42 +0200. + +The source can be downloaded from http://www.bitlbee.org/ + +Authors: Wilmer van der Gaast, Sjoerd Hemminga, Jelmer Vernooij, + Maurits Dijkstra and others. + +Mainly Copyright 2002-2004 Wilmer van der Gaast. +Some parts are borrowed from Gaim (version 0.58) <http://gaim.sf.net/>. +For the copyrights on those parts, please read the Gaim source code. + +BitlBee License: + +============================================================================ + This program is free software; you can 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 with + the Debian GNU/Linux distribution in file /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +============================================================================ + + + +Parts of the program (parts of the Jabber module, the XML parser to be more +specific) are licensed under the Mozilla license (version 1.1): + +============================================================================ + MOZILLA PUBLIC LICENSE + Version 1.1 + + --------------- + +1. Definitions. + + 1.0.1. "Commercial Use" means distribution or otherwise making the + Covered Code available to a third party. + + 1.1. "Contributor" means each entity that creates or contributes to + the creation of Modifications. + + 1.2. "Contributor Version" means the combination of the Original + Code, prior Modifications used by a Contributor, and the Modifications + made by that particular Contributor. + + 1.3. "Covered Code" means the Original Code or Modifications or the + combination of the Original Code and Modifications, in each case + including portions thereof. + + 1.4. "Electronic Distribution Mechanism" means a mechanism generally + accepted in the software development community for the electronic + transfer of data. + + 1.5. "Executable" means Covered Code in any form other than Source + Code. + + 1.6. "Initial Developer" means the individual or entity identified + as the Initial Developer in the Source Code notice required by Exhibit + A. + + 1.7. "Larger Work" means a work which combines Covered Code or + portions thereof with code not governed by the terms of this License. + + 1.8. "License" means this document. + + 1.8.1. "Licensable" means having the right to grant, to the maximum + extent possible, whether at the time of the initial grant or + subsequently acquired, any and all of the rights conveyed herein. + + 1.9. "Modifications" means any addition to or deletion from the + substance or structure of either the Original Code or any previous + Modifications. When Covered Code is released as a series of files, a + Modification is: + A. Any addition to or deletion from the contents of a file + containing Original Code or previous Modifications. + + B. Any new file that contains any part of the Original Code or + previous Modifications. + + 1.10. "Original Code" means Source Code of computer software code + which is described in the Source Code notice required by Exhibit A as + Original Code, and which, at the time of its release under this + License is not already Covered Code governed by this License. + + 1.10.1. "Patent Claims" means any patent claim(s), now owned or + hereafter acquired, including without limitation, method, process, + and apparatus claims, in any patent Licensable by grantor. + + 1.11. "Source Code" means the preferred form of the Covered Code for + making modifications to it, including all modules it contains, plus + any associated interface definition files, scripts used to control + compilation and installation of an Executable, or source code + differential comparisons against either the Original Code or another + well known, available Covered Code of the Contributor's choice. The + Source Code can be in a compressed or archival form, provided the + appropriate decompression or de-archiving software is widely available + for no charge. + + 1.12. "You" (or "Your") means an individual or a legal entity + exercising rights under, and complying with all of the terms of, this + License or a future version of this License issued under Section 6.1. + For legal entities, "You" includes any entity which controls, is + controlled by, or is under common control with You. For purposes of + this definition, "control" means (a) the power, direct or indirect, + to cause the direction or management of such entity, whether by + contract or otherwise, or (b) ownership of more than fifty percent + (50%) of the outstanding shares or beneficial ownership of such + entity. + +2. Source Code License. + + 2.1. The Initial Developer Grant. + The Initial Developer hereby grants You a world-wide, royalty-free, + non-exclusive license, subject to third party intellectual property + claims: + (a) under intellectual property rights (other than patent or + trademark) Licensable by Initial Developer to use, reproduce, + modify, display, perform, sublicense and distribute the Original + Code (or portions thereof) with or without Modifications, and/or + as part of a Larger Work; and + + (b) under Patents Claims infringed by the making, using or + selling of Original Code, to make, have made, use, practice, + sell, and offer for sale, and/or otherwise dispose of the + Original Code (or portions thereof). + + (c) the licenses granted in this Section 2.1(a) and (b) are + effective on the date Initial Developer first distributes + Original Code under the terms of this License. + + (d) Notwithstanding Section 2.1(b) above, no patent license is + granted: 1) for code that You delete from the Original Code; 2) + separate from the Original Code; or 3) for infringements caused + by: i) the modification of the Original Code or ii) the + combination of the Original Code with other software or devices. + + 2.2. Contributor Grant. + Subject to third party intellectual property claims, each Contributor + hereby grants You a world-wide, royalty-free, non-exclusive license + + (a) under intellectual property rights (other than patent or + trademark) Licensable by Contributor, to use, reproduce, modify, + display, perform, sublicense and distribute the Modifications + created by such Contributor (or portions thereof) either on an + unmodified basis, with other Modifications, as Covered Code + and/or as part of a Larger Work; and + + (b) under Patent Claims infringed by the making, using, or + selling of Modifications made by that Contributor either alone + and/or in combination with its Contributor Version (or portions + of such combination), to make, use, sell, offer for sale, have + made, and/or otherwise dispose of: 1) Modifications made by that + Contributor (or portions thereof); and 2) the combination of + Modifications made by that Contributor with its Contributor + Version (or portions of such combination). + + (c) the licenses granted in Sections 2.2(a) and 2.2(b) are + effective on the date Contributor first makes Commercial Use of + the Covered Code. + + (d) Notwithstanding Section 2.2(b) above, no patent license is + granted: 1) for any code that Contributor has deleted from the + Contributor Version; 2) separate from the Contributor Version; + 3) for infringements caused by: i) third party modifications of + Contributor Version or ii) the combination of Modifications made + by that Contributor with other software (except as part of the + Contributor Version) or other devices; or 4) under Patent Claims + infringed by Covered Code in the absence of Modifications made by + that Contributor. + +3. Distribution Obligations. + + 3.1. Application of License. + The Modifications which You create or to which You contribute are + governed by the terms of this License, including without limitation + Section 2.2. The Source Code version of Covered Code may be + distributed only under the terms of this License or a future version + of this License released under Section 6.1, and You must include a + copy of this License with every copy of the Source Code You + distribute. You may not offer or impose any terms on any Source Code + version that alters or restricts the applicable version of this + License or the recipients' rights hereunder. However, You may include + an additional document offering the additional rights described in + Section 3.5. + + 3.2. Availability of Source Code. + Any Modification which You create or to which You contribute must be + made available in Source Code form under the terms of this License + either on the same media as an Executable version or via an accepted + Electronic Distribution Mechanism to anyone to whom you made an + Executable version available; and if made available via Electronic + Distribution Mechanism, must remain available for at least twelve (12) + months after the date it initially became available, or at least six + (6) months after a subsequent version of that particular Modification + has been made available to such recipients. You are responsible for + ensuring that the Source Code version remains available even if the + Electronic Distribution Mechanism is maintained by a third party. + + 3.3. Description of Modifications. + You must cause all Covered Code to which You contribute to contain a + file documenting the changes You made to create that Covered Code and + the date of any change. You must include a prominent statement that + the Modification is derived, directly or indirectly, from Original + Code provided by the Initial Developer and including the name of the + Initial Developer in (a) the Source Code, and (b) in any notice in an + Executable version or related documentation in which You describe the + origin or ownership of the Covered Code. + + 3.4. Intellectual Property Matters + (a) Third Party Claims. + If Contributor has knowledge that a license under a third party's + intellectual property rights is required to exercise the rights + granted by such Contributor under Sections 2.1 or 2.2, + Contributor must include a text file with the Source Code + distribution titled "LEGAL" which describes the claim and the + party making the claim in sufficient detail that a recipient will + know whom to contact. If Contributor obtains such knowledge after + the Modification is made available as described in Section 3.2, + Contributor shall promptly modify the LEGAL file in all copies + Contributor makes available thereafter and shall take other steps + (such as notifying appropriate mailing lists or newsgroups) + reasonably calculated to inform those who received the Covered + Code that new knowledge has been obtained. + + (b) Contributor APIs. + If Contributor's Modifications include an application programming + interface and Contributor has knowledge of patent licenses which + are reasonably necessary to implement that API, Contributor must + also include this information in the LEGAL file. + + (c) Representations. + Contributor represents that, except as disclosed pursuant to + Section 3.4(a) above, Contributor believes that Contributor's + Modifications are Contributor's original creation(s) and/or + Contributor has sufficient rights to grant the rights conveyed by + this License. + + 3.5. Required Notices. + You must duplicate the notice in Exhibit A in each file of the Source + Code. If it is not possible to put such notice in a particular Source + Code file due to its structure, then You must include such notice in a + location (such as a relevant directory) where a user would be likely + to look for such a notice. If You created one or more Modification(s) + You may add your name as a Contributor to the notice described in + Exhibit A. You must also duplicate this License in any documentation + for the Source Code where You describe recipients' rights or ownership + rights relating to Covered Code. You may choose to offer, and to + charge a fee for, warranty, support, indemnity or liability + obligations to one or more recipients of Covered Code. However, You + may do so only on Your own behalf, and not on behalf of the Initial + Developer or any Contributor. You must make it absolutely clear than + any such warranty, support, indemnity or liability obligation is + offered by You alone, and You hereby agree to indemnify the Initial + Developer and every Contributor for any liability incurred by the + Initial Developer or such Contributor as a result of warranty, + support, indemnity or liability terms You offer. + + 3.6. Distribution of Executable Versions. + You may distribute Covered Code in Executable form only if the + requirements of Section 3.1-3.5 have been met for that Covered Code, + and if You include a notice stating that the Source Code version of + the Covered Code is available under the terms of this License, + including a description of how and where You have fulfilled the + obligations of Section 3.2. The notice must be conspicuously included + in any notice in an Executable version, related documentation or + collateral in which You describe recipients' rights relating to the + Covered Code. You may distribute the Executable version of Covered + Code or ownership rights under a license of Your choice, which may + contain terms different from this License, provided that You are in + compliance with the terms of this License and that the license for the + Executable version does not attempt to limit or alter the recipient's + rights in the Source Code version from the rights set forth in this + License. If You distribute the Executable version under a different + license You must make it absolutely clear that any terms which differ + from this License are offered by You alone, not by the Initial + Developer or any Contributor. You hereby agree to indemnify the + Initial Developer and every Contributor for any liability incurred by + the Initial Developer or such Contributor as a result of any such + terms You offer. + + 3.7. Larger Works. + You may create a Larger Work by combining Covered Code with other code + not governed by the terms of this License and distribute the Larger + Work as a single product. In such a case, You must make sure the + requirements of this License are fulfilled for the Covered Code. + +4. Inability to Comply Due to Statute or Regulation. + + If it is impossible for You to comply with any of the terms of this + License with respect to some or all of the Covered Code due to + statute, judicial order, or regulation then You must: (a) comply with + the terms of this License to the maximum extent possible; and (b) + describe the limitations and the code they affect. Such description + must be included in the LEGAL file described in Section 3.4 and must + be included with all distributions of the Source Code. Except to the + extent prohibited by statute or regulation, such description must be + sufficiently detailed for a recipient of ordinary skill to be able to + understand it. + +5. Application of this License. + + This License applies to code to which the Initial Developer has + attached the notice in Exhibit A and to related Covered Code. + +6. Versions of the License. + + 6.1. New Versions. + Netscape Communications Corporation ("Netscape") may publish revised + and/or new versions of the License from time to time. Each version + will be given a distinguishing version number. + + 6.2. Effect of New Versions. + Once Covered Code has been published under a particular version of the + License, You may always continue to use it under the terms of that + version. You may also choose to use such Covered Code under the terms + of any subsequent version of the License published by Netscape. No one + other than Netscape has the right to modify the terms applicable to + Covered Code created under this License. + + 6.3. Derivative Works. + If You create or use a modified version of this License (which you may + only do in order to apply it to code which is not already Covered Code + governed by this License), You must (a) rename Your license so that + the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape", + "MPL", "NPL" or any confusingly similar phrase do not appear in your + license (except to note that your license differs from this License) + and (b) otherwise make it clear that Your version of the license + contains terms which differ from the Mozilla Public License and + Netscape Public License. (Filling in the name of the Initial + Developer, Original Code or Contributor in the notice described in + Exhibit A shall not of themselves be deemed to be modifications of + this License.) + +7. DISCLAIMER OF WARRANTY. + + COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, + WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF + DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. + THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE + IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, + YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE + COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER + OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF + ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. + +8. TERMINATION. + + 8.1. This License and the rights granted hereunder will terminate + automatically if You fail to comply with terms herein and fail to cure + such breach within 30 days of becoming aware of the breach. All + sublicenses to the Covered Code which are properly granted shall + survive any termination of this License. Provisions which, by their + nature, must remain in effect beyond the termination of this License + shall survive. + + 8.2. If You initiate litigation by asserting a patent infringement + claim (excluding declatory judgment actions) against Initial Developer + or a Contributor (the Initial Developer or Contributor against whom + You file such action is referred to as "Participant") alleging that: + + (a) such Participant's Contributor Version directly or indirectly + infringes any patent, then any and all rights granted by such + Participant to You under Sections 2.1 and/or 2.2 of this License + shall, upon 60 days notice from Participant terminate prospectively, + unless if within 60 days after receipt of notice You either: (i) + agree in writing to pay Participant a mutually agreeable reasonable + royalty for Your past and future use of Modifications made by such + Participant, or (ii) withdraw Your litigation claim with respect to + the Contributor Version against such Participant. If within 60 days + of notice, a reasonable royalty and payment arrangement are not + mutually agreed upon in writing by the parties or the litigation claim + is not withdrawn, the rights granted by Participant to You under + Sections 2.1 and/or 2.2 automatically terminate at the expiration of + the 60 day notice period specified above. + + (b) any software, hardware, or device, other than such Participant's + Contributor Version, directly or indirectly infringes any patent, then + any rights granted to You by such Participant under Sections 2.1(b) + and 2.2(b) are revoked effective as of the date You first made, used, + sold, distributed, or had made, Modifications made by that + Participant. + + 8.3. If You assert a patent infringement claim against Participant + alleging that such Participant's Contributor Version directly or + indirectly infringes any patent where such claim is resolved (such as + by license or settlement) prior to the initiation of patent + infringement litigation, then the reasonable value of the licenses + granted by such Participant under Sections 2.1 or 2.2 shall be taken + into account in determining the amount or value of any payment or + license. + + 8.4. In the event of termination under Sections 8.1 or 8.2 above, + all end user license agreements (excluding distributors and resellers) + which have been validly granted by You or any distributor hereunder + prior to termination shall survive termination. + +9. LIMITATION OF LIABILITY. + + UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT + (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL + DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, + OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR + ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY + CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, + WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER + COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN + INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF + LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY + RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW + PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE + EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO + THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. + +10. U.S. GOVERNMENT END USERS. + + The Covered Code is a "commercial item," as that term is defined in + 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer + software" and "commercial computer software documentation," as such + terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 + C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), + all U.S. Government End Users acquire Covered Code with only those + rights set forth herein. + +11. MISCELLANEOUS. + + This License represents the complete agreement concerning subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. This License shall be governed by + California law provisions (except to the extent applicable law, if + any, provides otherwise), excluding its conflict-of-law provisions. + With respect to disputes in which at least one party is a citizen of, + or an entity chartered or registered to do business in the United + States of America, any litigation relating to this License shall be + subject to the jurisdiction of the Federal Courts of the Northern + District of California, with venue lying in Santa Clara County, + California, with the losing party responsible for costs, including + without limitation, court costs and reasonable attorneys' fees and + expenses. The application of the United Nations Convention on + Contracts for the International Sale of Goods is expressly excluded. + Any law or regulation which provides that the language of a contract + shall be construed against the drafter shall not apply to this + License. + +12. RESPONSIBILITY FOR CLAIMS. + + As between Initial Developer and the Contributors, each party is + responsible for claims and damages arising, directly or indirectly, + out of its utilization of rights under this License and You agree to + work with Initial Developer and Contributors to distribute such + responsibility on an equitable basis. Nothing herein is intended or + shall be deemed to constitute any admission of liability. + +13. MULTIPLE-LICENSED CODE. + + Initial Developer may designate portions of the Covered Code as + "Multiple-Licensed". "Multiple-Licensed" means that the Initial + Developer permits you to utilize portions of the Covered Code under + Your choice of the NPL or the alternative licenses, if any, specified + by the Initial Developer in the file described in Exhibit A. + +EXHIBIT A -Mozilla Public License. + + ``The contents of this file are subject to the Mozilla Public License + Version 1.1 (the "License"); you may not use this file except in + compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/ + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the + License for the specific language governing rights and limitations + under the License. + + The Original Code is ______________________________________. + + The Initial Developer of the Original Code is ________________________. + Portions created by ______________________ are Copyright (C) ______ + _______________________. All Rights Reserved. + + Contributor(s): ______________________________________. + + Alternatively, the contents of this file may be used under the terms + of the _____ license (the "[___] License"), in which case the + provisions of [______] License are applicable instead of those + above. If you wish to allow use of your version of this file only + under the terms of the [____] License and not to allow others to use + your version of this file under the MPL, indicate your decision by + deleting the provisions above and replace them with the notice and + other provisions required by the [___] License. If you do not delete + the provisions above, a recipient may use your version of this file + under either the MPL or the [___] License." + + [NOTE: The text of this Exhibit A may differ slightly from the text of + the notices in the Source Code files of the Original Code. You should + use the text of this Exhibit A rather than the text found in the + Original Code Source Code for Your Modifications.] +============================================================================ + + + +The MD5 generator used for authentication in some modules is written by +Aladdin Enterprises: + +============================================================================ + Copyright (C) 1999 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +============================================================================ + + + +The SGML-formatted documentation is written by Jelmer Vernooij +<jelmer@nl.linux.org> under the GNU Free Documentation License: + +============================================================================ + GNU Free Documentation License + Version 1.1, March 2000 + + Copyright (C) 2000 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + +0. PREAMBLE + +The purpose of this License is to make a manual, textbook, or other +written document "free" in the sense of freedom: to assure everyone +the effective freedom to copy and redistribute it, with or without +modifying it, either commercially or noncommercially. Secondarily, +this License preserves for the author and publisher a way to get +credit for their work, while not being considered responsible for +modifications made by others. + +This License is a kind of "copyleft", which means that derivative +works of the document must themselves be free in the same sense. It +complements the GNU General Public License, which is a copyleft +license designed for free software. + +We have designed this License in order to use it for manuals for free +software, because free software needs free documentation: a free +program should come with manuals providing the same freedoms that the +software does. But this License is not limited to software manuals; +it can be used for any textual work, regardless of subject matter or +whether it is published as a printed book. We recommend this License +principally for works whose purpose is instruction or reference. + + +1. APPLICABILITY AND DEFINITIONS + +This License applies to any manual or other work that contains a +notice placed by the copyright holder saying it can be distributed +under the terms of this License. The "Document", below, refers to any +such manual or work. Any member of the public is a licensee, and is +addressed as "you". + +A "Modified Version" of the Document means any work containing the +Document or a portion of it, either copied verbatim, or with +modifications and/or translated into another language. + +A "Secondary Section" is a named appendix or a front-matter section of +the Document that deals exclusively with the relationship of the +publishers or authors of the Document to the Document's overall subject +(or to related matters) and contains nothing that could fall directly +within that overall subject. (For example, if the Document is in part a +textbook of mathematics, a Secondary Section may not explain any +mathematics.) The relationship could be a matter of historical +connection with the subject or with related matters, or of legal, +commercial, philosophical, ethical or political position regarding +them. + +The "Invariant Sections" are certain Secondary Sections whose titles +are designated, as being those of Invariant Sections, in the notice +that says that the Document is released under this License. + +The "Cover Texts" are certain short passages of text that are listed, +as Front-Cover Texts or Back-Cover Texts, in the notice that says that +the Document is released under this License. + +A "Transparent" copy of the Document means a machine-readable copy, +represented in a format whose specification is available to the +general public, whose contents can be viewed and edited directly and +straightforwardly with generic text editors or (for images composed of +pixels) generic paint programs or (for drawings) some widely available +drawing editor, and that is suitable for input to text formatters or +for automatic translation to a variety of formats suitable for input +to text formatters. A copy made in an otherwise Transparent file +format whose markup has been designed to thwart or discourage +subsequent modification by readers is not Transparent. A copy that is +not "Transparent" is called "Opaque". + +Examples of suitable formats for Transparent copies include plain +ASCII without markup, Texinfo input format, LaTeX input format, SGML +or XML using a publicly available DTD, and standard-conforming simple +HTML designed for human modification. Opaque formats include +PostScript, PDF, proprietary formats that can be read and edited only +by proprietary word processors, SGML or XML for which the DTD and/or +processing tools are not generally available, and the +machine-generated HTML produced by some word processors for output +purposes only. + +The "Title Page" means, for a printed book, the title page itself, +plus such following pages as are needed to hold, legibly, the material +this License requires to appear in the title page. For works in +formats which do not have any title page as such, "Title Page" means +the text near the most prominent appearance of the work's title, +preceding the beginning of the body of the text. + + +2. VERBATIM COPYING + +You may copy and distribute the Document in any medium, either +commercially or noncommercially, provided that this License, the +copyright notices, and the license notice saying this License applies +to the Document are reproduced in all copies, and that you add no other +conditions whatsoever to those of this License. You may not use +technical measures to obstruct or control the reading or further +copying of the copies you make or distribute. However, you may accept +compensation in exchange for copies. If you distribute a large enough +number of copies you must also follow the conditions in section 3. + +You may also lend copies, under the same conditions stated above, and +you may publicly display copies. + + +3. COPYING IN QUANTITY + +If you publish printed copies of the Document numbering more than 100, +and the Document's license notice requires Cover Texts, you must enclose +the copies in covers that carry, clearly and legibly, all these Cover +Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on +the back cover. Both covers must also clearly and legibly identify +you as the publisher of these copies. The front cover must present +the full title with all words of the title equally prominent and +visible. You may add other material on the covers in addition. +Copying with changes limited to the covers, as long as they preserve +the title of the Document and satisfy these conditions, can be treated +as verbatim copying in other respects. + +If the required texts for either cover are too voluminous to fit +legibly, you should put the first ones listed (as many as fit +reasonably) on the actual cover, and continue the rest onto adjacent +pages. + +If you publish or distribute Opaque copies of the Document numbering +more than 100, you must either include a machine-readable Transparent +copy along with each Opaque copy, or state in or with each Opaque copy +a publicly-accessible computer-network location containing a complete +Transparent copy of the Document, free of added material, which the +general network-using public has access to download anonymously at no +charge using public-standard network protocols. If you use the latter +option, you must take reasonably prudent steps, when you begin +distribution of Opaque copies in quantity, to ensure that this +Transparent copy will remain thus accessible at the stated location +until at least one year after the last time you distribute an Opaque +copy (directly or through your agents or retailers) of that edition to +the public. + +It is requested, but not required, that you contact the authors of the +Document well before redistributing any large number of copies, to give +them a chance to provide you with an updated version of the Document. + + +4. MODIFICATIONS + +You may copy and distribute a Modified Version of the Document under +the conditions of sections 2 and 3 above, provided that you release +the Modified Version under precisely this License, with the Modified +Version filling the role of the Document, thus licensing distribution +and modification of the Modified Version to whoever possesses a copy +of it. In addition, you must do these things in the Modified Version: + +A. Use in the Title Page (and on the covers, if any) a title distinct + from that of the Document, and from those of previous versions + (which should, if there were any, be listed in the History section + of the Document). You may use the same title as a previous version + if the original publisher of that version gives permission. +B. List on the Title Page, as authors, one or more persons or entities + responsible for authorship of the modifications in the Modified + Version, together with at least five of the principal authors of the + Document (all of its principal authors, if it has less than five). +C. State on the Title page the name of the publisher of the + Modified Version, as the publisher. +D. Preserve all the copyright notices of the Document. +E. Add an appropriate copyright notice for your modifications + adjacent to the other copyright notices. +F. Include, immediately after the copyright notices, a license notice + giving the public permission to use the Modified Version under the + terms of this License, in the form shown in the Addendum below. +G. Preserve in that license notice the full lists of Invariant Sections + and required Cover Texts given in the Document's license notice. +H. Include an unaltered copy of this License. +I. Preserve the section entitled "History", and its title, and add to + it an item stating at least the title, year, new authors, and + publisher of the Modified Version as given on the Title Page. If + there is no section entitled "History" in the Document, create one + stating the title, year, authors, and publisher of the Document as + given on its Title Page, then add an item describing the Modified + Version as stated in the previous sentence. +J. Preserve the network location, if any, given in the Document for + public access to a Transparent copy of the Document, and likewise + the network locations given in the Document for previous versions + it was based on. These may be placed in the "History" section. + You may omit a network location for a work that was published at + least four years before the Document itself, or if the original + publisher of the version it refers to gives permission. +K. In any section entitled "Acknowledgements" or "Dedications", + preserve the section's title, and preserve in the section all the + substance and tone of each of the contributor acknowledgements + and/or dedications given therein. +L. Preserve all the Invariant Sections of the Document, + unaltered in their text and in their titles. Section numbers + or the equivalent are not considered part of the section titles. +M. Delete any section entitled "Endorsements". Such a section + may not be included in the Modified Version. +N. Do not retitle any existing section as "Endorsements" + or to conflict in title with any Invariant Section. + +If the Modified Version includes new front-matter sections or +appendices that qualify as Secondary Sections and contain no material +copied from the Document, you may at your option designate some or all +of these sections as invariant. To do this, add their titles to the +list of Invariant Sections in the Modified Version's license notice. +These titles must be distinct from any other section titles. + +You may add a section entitled "Endorsements", provided it contains +nothing but endorsements of your Modified Version by various +parties--for example, statements of peer review or that the text has +been approved by an organization as the authoritative definition of a +standard. + +You may add a passage of up to five words as a Front-Cover Text, and a +passage of up to 25 words as a Back-Cover Text, to the end of the list +of Cover Texts in the Modified Version. Only one passage of +Front-Cover Text and one of Back-Cover Text may be added by (or +through arrangements made by) any one entity. If the Document already +includes a cover text for the same cover, previously added by you or +by arrangement made by the same entity you are acting on behalf of, +you may not add another; but you may replace the old one, on explicit +permission from the previous publisher that added the old one. + +The author(s) and publisher(s) of the Document do not by this License +give permission to use their names for publicity for or to assert or +imply endorsement of any Modified Version. + + +5. COMBINING DOCUMENTS + +You may combine the Document with other documents released under this +License, under the terms defined in section 4 above for modified +versions, provided that you include in the combination all of the +Invariant Sections of all of the original documents, unmodified, and +list them all as Invariant Sections of your combined work in its +license notice. + +The combined work need only contain one copy of this License, and +multiple identical Invariant Sections may be replaced with a single +copy. If there are multiple Invariant Sections with the same name but +different contents, make the title of each such section unique by +adding at the end of it, in parentheses, the name of the original +author or publisher of that section if known, or else a unique number. +Make the same adjustment to the section titles in the list of +Invariant Sections in the license notice of the combined work. + +In the combination, you must combine any sections entitled "History" +in the various original documents, forming one section entitled +"History"; likewise combine any sections entitled "Acknowledgements", +and any sections entitled "Dedications". You must delete all sections +entitled "Endorsements." + + +6. COLLECTIONS OF DOCUMENTS + +You may make a collection consisting of the Document and other documents +released under this License, and replace the individual copies of this +License in the various documents with a single copy that is included in +the collection, provided that you follow the rules of this License for +verbatim copying of each of the documents in all other respects. + +You may extract a single document from such a collection, and distribute +it individually under this License, provided you insert a copy of this +License into the extracted document, and follow this License in all +other respects regarding verbatim copying of that document. + + +7. AGGREGATION WITH INDEPENDENT WORKS + +A compilation of the Document or its derivatives with other separate +and independent documents or works, in or on a volume of a storage or +distribution medium, does not as a whole count as a Modified Version +of the Document, provided no compilation copyright is claimed for the +compilation. Such a compilation is called an "aggregate", and this +License does not apply to the other self-contained works thus compiled +with the Document, on account of their being thus compiled, if they +are not themselves derivative works of the Document. + +If the Cover Text requirement of section 3 is applicable to these +copies of the Document, then if the Document is less than one quarter +of the entire aggregate, the Document's Cover Texts may be placed on +covers that surround only the Document within the aggregate. +Otherwise they must appear on covers around the whole aggregate. + + +8. TRANSLATION + +Translation is considered a kind of modification, so you may +distribute translations of the Document under the terms of section 4. +Replacing Invariant Sections with translations requires special +permission from their copyright holders, but you may include +translations of some or all Invariant Sections in addition to the +original versions of these Invariant Sections. You may include a +translation of this License provided that you also include the +original English version of this License. In case of a disagreement +between the translation and the original English version of this +License, the original English version will prevail. + + +9. TERMINATION + +You may not copy, modify, sublicense, or distribute the Document except +as expressly provided for under this License. Any other attempt to +copy, modify, sublicense or distribute the Document 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. + + +10. FUTURE REVISIONS OF THIS LICENSE + +The Free Software Foundation may publish new, revised versions +of the GNU Free Documentation 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. See +http://www.gnu.org/copyleft/. + +Each version of the License is given a distinguishing version number. +If the Document specifies that a particular numbered version of this +License "or any later version" applies to it, you have the option of +following the terms and conditions either of that specified version or +of any later version that has been published (not as a draft) by the +Free Software Foundation. If the Document does not specify a version +number of this License, you may choose any version ever published (not +as a draft) by the Free Software Foundation. + + +ADDENDUM: How to use this License for your documents + +To use this License in a document you have written, include a copy of +the License in the document and put the following copyright and +license notices just after the title page: + + Copyright (c) YEAR YOUR NAME. + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.1 + or any later version published by the Free Software Foundation; + with the Invariant Sections being LIST THEIR TITLES, with the + Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. + A copy of the license is included in the section entitled "GNU + Free Documentation License". + +If you have no Invariant Sections, write "with no Invariant Sections" +instead of saying which ones are invariant. If you have no +Front-Cover Texts, write "no Front-Cover Texts" instead of +"Front-Cover Texts being LIST"; likewise for Back-Cover Texts. + +If your document contains nontrivial examples of program code, we +recommend releasing these examples in parallel under your choice of +free software license, such as the GNU General Public License, +to permit their use in free software. +============================================================================ diff --git a/debian/motd.txt b/debian/motd.txt new file mode 100644 index 00000000..7c868e19 --- /dev/null +++ b/debian/motd.txt @@ -0,0 +1,19 @@ +Welcome to the BitlBee server at %h. + +This server is running BitlBee version %v-Debian. +The newest version can be found on http://www.bitlbee.org/, or nicely +packaged on your local Debian mirror. + +You are getting this message because the server administrator has not +yet had the time (or need) to change it. + +For those who don't know it yet, this is not quite a regular Internet +Relay Chat server. Please see the site mentioned above for more +information. + + +The developers of the Bee hope you have a buzzing time. + +* BitlBee development team: wilmer, ctrlsoft, Maurits + +... Buzzing, haha, get it? diff --git a/debian/po/POTFILES.in b/debian/po/POTFILES.in new file mode 100644 index 00000000..f17ddcfe --- /dev/null +++ b/debian/po/POTFILES.in @@ -0,0 +1 @@ +[type: gettext/rfc822deb] bitlbee.templates.master diff --git a/debian/po/cs.po b/debian/po/cs.po new file mode 100644 index 00000000..558c8602 --- /dev/null +++ b/debian/po/cs.po @@ -0,0 +1,44 @@ +# +# Translators, if you are not familiar with the PO format, gettext +# documentation is worth reading, especially sections dedicated to +# this format, e.g. by running: +# info -n '(gettext)PO Files' +# info -n '(gettext)Header Entry' +# +# Some information specific to po-debconf are available at +# /usr/share/doc/po-debconf/README-trans +# or http://www.debian.org/intl/l10n/po-debconf/README-trans +# +# Developers do not need to manually edit POT or PO files. +# +msgid "" +msgstr "" +"Project-Id-Version: bitlbee\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2004-09-25 18:12+0200\n" +"PO-Revision-Date: 2005-02-04 12:31+0100\n" +"Last-Translator: Miroslav Kure <kurem@debian.cz>\n" +"Language-Team: Czech <debian-l10n-czech@debian.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ISO-8859-2\n" +"Content-Transfer-Encoding: 8bit\n" + +#. Type: string +#. Description +#: ../bitlbee.templates.master:4 +msgid "On what TCP port should BitlBee listen for connections?" +msgstr "Na kterém TCP portu má BitlBee naslouchat pøíchozím spojením?" + +#. Type: string +#. Description +#: ../bitlbee.templates.master:4 +msgid "" +"BitlBee normally listens on the regular IRC port, 6667. This might not be a " +"very good idea when you're running a real IRC daemon as well. 6668 might be " +"a good alternative. Leaving this value blank means that BitlBee will not be " +"run automatically." +msgstr "" +"BitlBee normálnì naslouchá na bì¾ném IRC portu 6667. Pokud máte spu¹tìný i " +"reálný IRC daemon, tak to nemusí být nejlep¹í nápad. Vhodná alternativa mù¾e " +"být 6668. Ponecháte-li pole prázdné, znamená to, ¾e se BitlBee nebude " +"spou¹tìt automaticky." diff --git a/debian/po/de.po b/debian/po/de.po new file mode 100644 index 00000000..b85f744b --- /dev/null +++ b/debian/po/de.po @@ -0,0 +1,45 @@ +# +# Translators, if you are not familiar with the PO format, gettext +# documentation is worth reading, especially sections dedicated to +# this format, e.g. by running: +# info -n '(gettext)PO Files' +# info -n '(gettext)Header Entry' +# Some information specific to po-debconf are available at +# /usr/share/doc/po-debconf/README-trans +# or http://www.debian.org/intl/l10n/po-debconf/README-trans# +# Developers do not need to manually edit POT or PO files. +# Erik Schanze <mail@erikschanze.de>, 2004. +# +msgid "" +msgstr "" +"Project-Id-Version: bitlbee_0.90a-2_de\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2004-09-06 20:16+0200\n" +"PO-Revision-Date: 2004-10-03 14:33+0200\n" +"Last-Translator: Erik Schanze <mail@erikschanze.de>\n" +"Language-Team: German <debian-l10n-german@lists.debian.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: KBabel 1.3.1\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. Type: string +#. Description +#: ../bitlbee.templates.master:3 +msgid "On what TCP port should BitlBee listen for connections?" +msgstr "An welchem TCP-Port soll BitlBee auf Verbindungen warten?" + +#. Type: string +#. Description +#: ../bitlbee.templates.master:3 +msgid "" +"BitlBee normally listens on the regular IRC port, 6667. This might not be a " +"very good idea when you're running a real IRC daemon as well. 6668 might be " +"a good alternative. Leaving this value blank means that BitlBee will not be " +"run automatically." +msgstr "" +"BitlBee lauscht normalerweise an dem üblichen IRC-Port 6667. Dies ist aber " +"keine gute Idee, wenn Sie außerdem noch einen richtigen IRC-Dienst " +"betreiben. Das Port 6668 ist eine gute Alternative. Wenn Sie keinen Wert " +"eingeben, wird BitlBee nicht automatisch starten." diff --git a/debian/po/es.po b/debian/po/es.po new file mode 100644 index 00000000..cfd708ea --- /dev/null +++ b/debian/po/es.po @@ -0,0 +1,51 @@ +# bitlbee po-debconf translation to Spanish +# Copyright (C) 2005 Software in the Public Interest +# This file is distributed under the same license as the bitlbee package. +# +# Changes: +# - Initial translation +# César Gómez MartÃn <cesar.gomez@gmail.com> +# +# +# Traductores, si no conoce el formato PO, merece la pena leer la +# documentación de gettext, especialmente las secciones dedicadas a este +# formato, por ejemplo ejecutando: +# info -n '(gettext)PO Files' +# info -n '(gettext)Header Entry' +# Equipo de traducción al español, por favor, lean antes de traducir +# los siguientes documentos: +# +# - El proyecto de traducción de Debian al español +# http://www.debian.org/intl/spanish/ +# especialmente las notas de traducción en +# http://www.debian.org/intl/spanish/notas +# +# - La guÃa de traducción de po's de debconf: +# /usr/share/doc/po-debconf/README-trans +# o http://www.debian.org/intl/l10n/po-debconf/README-trans +# +msgid "" +msgstr "" +"Project-Id-Version: bitlbee\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2004-09-25 18:12+0200\n" +"PO-Revision-Date: 2005-08-24 19:37+0100\n" +"Last-Translator: César Gómez MartÃn <cesar.gomez@gmail.com>\n" +"Language-Team: Debian l10n spanish <debian-l10n-spanish@lists.debian.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Poedit-Language: Spanish\n" +"X-Poedit-Country: SPAIN\n" +"X-Poedit-SourceCharset: utf-8\n" + +#. Type: string +#: ../bitlbee.templates.master:4 +msgid "On what TCP port should BitlBee listen for connections?" +msgstr "¿En qué puerto TCP quiere que BitlBee escuche conexiones?" + +#. Type: string +#: ../bitlbee.templates.master:4 +msgid "BitlBee normally listens on the regular IRC port, 6667. This might not be a very good idea when you're running a real IRC daemon as well. 6668 might be a good alternative. Leaving this value blank means that BitlBee will not be run automatically." +msgstr "BitlBee normalmente escucha en el puerto 6667, que se usa también para IRC. Por esta razón no es muy buena idea poner a BitlBee a escuchar en ese puerto si también se está ejecutando un demonio real de IRC, en este caso el puerto 6668 puede ser una buena alternativa. Si deja este valor en blanco BitlBee no se ejecutará automáticamente." + diff --git a/debian/po/fr.po b/debian/po/fr.po new file mode 100644 index 00000000..562a0229 --- /dev/null +++ b/debian/po/fr.po @@ -0,0 +1,44 @@ +# +# Translators, if you are not familiar with the PO format, gettext +# documentation is worth reading, especially sections dedicated to +# this format, e.g. by running: +# info -n '(gettext)PO Files' +# info -n '(gettext)Header Entry' +# +# Some information specific to po-debconf are available at +# /usr/share/doc/po-debconf/README-trans +# or http://www.debian.org/intl/l10n/po-debconf/README-trans +# +# Developers do not need to manually edit POT or PO files. +# +msgid "" +msgstr "" +"Project-Id-Version: bitlbee (0.80-1)\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2004-09-25 18:12+0200\n" +"PO-Revision-Date: 2003-08-07 08:45+0100\n" +"Last-Translator: Christian Perrier <bubulle@debian.org>\n" +"Language-Team: French <debian-l10n-french@lists.debian.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=iso-8859-15\n" +"Content-Transfer-Encoding: 8bit\n" + +#. Type: string +#. Description +#: ../bitlbee.templates.master:4 +msgid "On what TCP port should BitlBee listen for connections?" +msgstr "Sur quel port TCP BitlBee doit-il être à l'écoute ?" + +#. Type: string +#. Description +#: ../bitlbee.templates.master:4 +msgid "" +"BitlBee normally listens on the regular IRC port, 6667. This might not be a " +"very good idea when you're running a real IRC daemon as well. 6668 might be " +"a good alternative. Leaving this value blank means that BitlBee will not be " +"run automatically." +msgstr "" +"BitlBee est usuellement à l'écoute sur le port IRC standard : 6667. Cela " +"n'est pas forcément un choix adapté si vous utilisez en même temps un vrai " +"démon IRC. Dans ce cas, choisir 6668 est conseillé. Si vous ne souhaitez pas " +"lancer BitlBee automatiquement, veuillez laissez ce champs vide." diff --git a/debian/po/ja.po b/debian/po/ja.po new file mode 100644 index 00000000..c94bbbe4 --- /dev/null +++ b/debian/po/ja.po @@ -0,0 +1,45 @@ +# +# Translators, if you are not familiar with the PO format, gettext +# documentation is worth reading, especially sections dedicated to +# this format, e.g. by running: +# info -n '(gettext)PO Files' +# info -n '(gettext)Header Entry' +# +# Some information specific to po-debconf are available at +# /usr/share/doc/po-debconf/README-trans +# or http://www.debian.org/intl/l10n/po-debconf/README-trans +# +# Developers do not need to manually edit POT or PO files. +# +# +msgid "" +msgstr "" +"Project-Id-Version: bitlbee 0.90a-2\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2004-09-25 18:12+0200\n" +"PO-Revision-Date: 2004-09-11 13:30+0900\n" +"Last-Translator: Hideki Yamane <henrich@samba.gr.jp>\n" +"Language-Team: Japanese <debian-japanese@lists.debian.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=EUC-JP\n" +"Content-Transfer-Encoding: 8bit\n" + +#. Type: string +#. Description +#: ../bitlbee.templates.master:4 +msgid "On what TCP port should BitlBee listen for connections?" +msgstr "BitlBee ¤Ï¡¢Àܳ¤Î¤¿¤á¤Ë¤É¤Î TCP ¥Ý¡¼¥È¤ò listen ¤·¤Þ¤¹¤«?" + +#. Type: string +#. Description +#: ../bitlbee.templates.master:4 +msgid "" +"BitlBee normally listens on the regular IRC port, 6667. This might not be a " +"very good idea when you're running a real IRC daemon as well. 6668 might be " +"a good alternative. Leaving this value blank means that BitlBee will not be " +"run automatically." +msgstr "" +"BitlBee ¤ÏÄ̾ï¤Î¾ì¹ç¡¢É¸½à¤Î IRC ¥Ý¡¼¥ÈÈÖ¹æ¤Ç¤¢¤ë 6667 ¤ò listen ¤·¤Þ¤¹¡£Æ±ÍÍ" +"¤Ë¤·¤Æ¼ÂºÝ¤Î IRC ¥Ç¡¼¥â¥ó¤òưºî¤µ¤»¤Æ¤¤¤ë¾ì¹ç¡¢¤³¤ì¤Ï¤¢¤Þ¤êÎɤ¤¹Í¤¨¤Ç¤Ï̵¤¤¤«" +"¤â¤·¤ì¤Þ¤»¤ó¡£Âå¤ï¤ê¤Ë 6668 ¤ò»È¤¦¤Î¤¬Îɤ¤¤«¤âÃΤì¤Þ¤»¤ó¡£¤³¤ì¤ò¶õ¤Î¤Þ¤Þ¤Ë¤·" +"¤Æ¤ª¤±¤Ð¡¢BitlBee ¤Ï¼«Æ°Åª¤Ë¤Ïµ¯Æ°¤Ê¤¯¤Ê¤ê¤Þ¤¹¡£" diff --git a/debian/po/nl.po b/debian/po/nl.po new file mode 100644 index 00000000..f9a97d76 --- /dev/null +++ b/debian/po/nl.po @@ -0,0 +1,44 @@ +# +# Translators, if you are not familiar with the PO format, gettext +# documentation is worth reading, especially sections dedicated to +# this format, e.g. by running: +# info -n '(gettext)PO Files' +# info -n '(gettext)Header Entry' +# +# Some information specific to po-debconf are available at +# /usr/share/doc/po-debconf/README-trans +# or http://www.debian.org/intl/l10n/po-debconf/README-trans +# +# Developers do not need to manually edit POT or PO files. +# +msgid "" +msgstr "" +"Project-Id-Version: bitlbee (0.90a-2)\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2004-09-25 18:12+0200\n" +"PO-Revision-Date: 2004-09-06 20:16+0200\n" +"Last-Translator: Wilmer van der Gaast <wilmer@gaast.net>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ISO-8859-15\n" +"Content-Transfer-Encoding: 8bit\n" + +#. Type: string +#. Description +#: ../bitlbee.templates.master:4 +msgid "On what TCP port should BitlBee listen for connections?" +msgstr "Op welke TCP poort moet BitlBee draaien?" + +#. Type: string +#. Description +#: ../bitlbee.templates.master:4 +msgid "" +"BitlBee normally listens on the regular IRC port, 6667. This might not be a " +"very good idea when you're running a real IRC daemon as well. 6668 might be " +"a good alternative. Leaving this value blank means that BitlBee will not be " +"run automatically." +msgstr "" +"Normaal 'luistert' BitlBee op de gebruikelijke IRC poort, 6667. Als je al " +"een andere IRC daemon draait is dat onmogelijk. Kies dan bijvoorbeeld voor " +"poort 6668. Als je niet wil dat BitlBee automatisch gestart wordt, vul hier " +"dan niets in." diff --git a/debian/po/pt_BR.po b/debian/po/pt_BR.po new file mode 100644 index 00000000..6c591005 --- /dev/null +++ b/debian/po/pt_BR.po @@ -0,0 +1,44 @@ +# +# Translators, if you are not familiar with the PO format, gettext +# documentation is worth reading, especially sections dedicated to +# this format, e.g. by running: +# info -n '(gettext)PO Files' +# info -n '(gettext)Header Entry' +# +# Some information specific to po-debconf are available at +# /usr/share/doc/po-debconf/README-trans +# or http://www.debian.org/intl/l10n/po-debconf/README-trans +# +# Developers do not need to manually edit POT or PO files. +# +msgid "" +msgstr "" +"Project-Id-Version: bitlbee\n" +"Report-Msgid-Bugs-To: debian-l10n-portuguese@lists.debian.org\n" +"POT-Creation-Date: 2004-09-25 18:12+0200\n" +"PO-Revision-Date: 2005-02-26 16:14-0300\n" +"Last-Translator: André Luís Lopes <andrelop@debian.org>\n" +"Language-Team: Debian-BR Project <debian-l10n-portuguese@lists.debian.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=ISO-8859-1\n" +"Content-Transfer-Encoding: 8bit\n" + +#. Type: string +#. Description +#: ../bitlbee.templates.master:4 +msgid "On what TCP port should BitlBee listen for connections?" +msgstr "Em qual porta TCP o BitlBee deverá ouvir por conexões ?" + +#. Type: string +#. Description +#: ../bitlbee.templates.master:4 +msgid "" +"BitlBee normally listens on the regular IRC port, 6667. This might not be a " +"very good idea when you're running a real IRC daemon as well. 6668 might be " +"a good alternative. Leaving this value blank means that BitlBee will not be " +"run automatically." +msgstr "" +"O BitlBee normalmente ouve na porta de IRC padrão, 6667. Porém, esta pode " +"não ser uma boa idéia quando você está rodando um daemon IRC real também. " +"6689 pode ser uma boa alternativa. Deixar esse valor em branco significa " +"que o BitlBee não será executado automaticamente." diff --git a/debian/po/sv.po b/debian/po/sv.po new file mode 100644 index 00000000..1a83930d --- /dev/null +++ b/debian/po/sv.po @@ -0,0 +1,44 @@ +# Translators, if you are not familiar with the PO format, gettext +# documentation is worth reading, especially sections dedicated to +# this format, e.g. by running: +# info -n '(gettext)PO Files' +# info -n '(gettext)Header Entry' +# Some information specific to po-debconf are available at +# /usr/share/doc/po-debconf/README-trans +# or http://www.debian.org/intl/l10n/po-debconf/README-trans +# Developers do not need to manually edit POT or PO files. +# , fuzzy +# +# +msgid "" +msgstr "" +"Project-Id-Version: bitlbee 0.92-2\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2004-09-25 18:12+0200\n" +"PO-Revision-Date: 2005-10-03 23:18+0200\n" +"Last-Translator: Daniel Nylander <po@danielnylander.se>\n" +"Language-Team: Swedish <sv@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=iso-8859-1\n" +"Content-Transfer-Encoding: 8bit" + +#. Type: string +#. Description +#: ../bitlbee.templates.master:4 +msgid "On what TCP port should BitlBee listen for connections?" +msgstr "På vilken TCP-port ska BitlBee lyssna på efter anslutningar?" + +#. Type: string +#. Description +#: ../bitlbee.templates.master:4 +msgid "" +"BitlBee normally listens on the regular IRC port, 6667. This might not be a " +"very good idea when you're running a real IRC daemon as well. 6668 might be " +"a good alternative. Leaving this value blank means that BitlBee will not be " +"run automatically." +msgstr "" +"BitlBee lyssnar normalt på den standardporten för IRC, 6667. Detta kanske inte är " +"en bra ide om du kör en riktig IRC-daemon på samma system. 6668 kan vara " +"ett bra alternativ då. Lämnar du detta värde blankt betyder det att BitlBee inte " +"kommer att startas automatiskt." + diff --git a/debian/po/templates.pot b/debian/po/templates.pot new file mode 100644 index 00000000..1a3ab2b8 --- /dev/null +++ b/debian/po/templates.pot @@ -0,0 +1,41 @@ +# +# Translators, if you are not familiar with the PO format, gettext +# documentation is worth reading, especially sections dedicated to +# this format, e.g. by running: +# info -n '(gettext)PO Files' +# info -n '(gettext)Header Entry' +# +# Some information specific to po-debconf are available at +# /usr/share/doc/po-debconf/README-trans +# or http://www.debian.org/intl/l10n/po-debconf/README-trans +# +# Developers do not need to manually edit POT or PO files. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2004-09-25 18:12+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"Language-Team: LANGUAGE <LL@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#. Type: string +#. Description +#: ../bitlbee.templates.master:4 +msgid "On what TCP port should BitlBee listen for connections?" +msgstr "" + +#. Type: string +#. Description +#: ../bitlbee.templates.master:4 +msgid "" +"BitlBee normally listens on the regular IRC port, 6667. This might not be a " +"very good idea when you're running a real IRC daemon as well. 6668 might be " +"a good alternative. Leaving this value blank means that BitlBee will not be " +"run automatically." +msgstr "" diff --git a/debian/po/vi.po b/debian/po/vi.po new file mode 100644 index 00000000..2bcb5908 --- /dev/null +++ b/debian/po/vi.po @@ -0,0 +1,32 @@ +# Vietnamese translation for bitlbee. +# Copyright © 2005 Free Software Foundation, Inc. +# Clytie Siddall <clytie@riverland.net.au>, 2005. +# +msgid "" +msgstr "" +"Project-Id-Version: bitlbee 0.92-1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2004-09-25 18:12+0200\n" +"PO-Revision-Date: 2005-06-12 18:34+0930\n" +"Last-Translator: Clytie Siddall <clytie@riverland.net.au>\n" +"Language-Team: Vietnamese <gnomevi-list@lists.sourceforge.net>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0\n" + +#. Type: string +#. Description +#: ../bitlbee.templates.master:4 +msgid "On what TCP port should BitlBee listen for connections?" +msgstr "Trình BitlBee nên lắng nghe sá»± kết nối trên cổng TCP nà o?" + +#. Type: string +#. Description +#: ../bitlbee.templates.master:4 +msgid "" +"BitlBee normally listens on the regular IRC port, 6667. This might not be a " +"very good idea when you're running a real IRC daemon as well. 6668 might be " +"a good alternative. Leaving this value blank means that BitlBee will not be " +"run automatically." +msgstr "Trình BitlBee thưá»ng lắng nghe trên cổng IRC bình thưá»ng, 6667. Có lẽ nó không phải là má»™t ý kiến tốt nếu bạn cÅ©ng có chạy má»™t trình ná»n (dæmon) IRC tháºt. Như thế thì, cổng 6668 có thể là má»™t Ä‘iá»u thay thế tốt. Nếu bạn bá» giá trị nà y rá»—ng, thì trình BitlBee sẽ không tá»± động chạy." diff --git a/debian/rules b/debian/rules new file mode 100755 index 00000000..f0bb7d17 --- /dev/null +++ b/debian/rules @@ -0,0 +1,100 @@ +#!/usr/bin/make -f + +DEBUG ?= 0 + +ifeq (,$(wildcard /usr/bin/po2debconf)) +PO2DEBCONF := no +MINDEBCONFVER := 0.5 +else +PO2DEBCONF := yes +MINDEBCONFVER := 1.2.0 +endif + + +build-arch: build-arch-stamp +build-arch-stamp: + if [ ! -d debian ]; then exit 1; fi + ./configure --debug=$(DEBUG) --prefix=/usr --etcdir=/etc/bitlbee $(DEB_BUILD_OPTIONS) + $(MAKE) +# $(MAKE) -C doc/ all + touch build-arch-stamp + +clean: + if [ "`whoami`" != "root" -o ! -d debian ]; then exit 1; fi + rm -rf build-arch-stamp debian/bitlbee debian/*.substvars debian/files + -$(MAKE) distclean +# -$(MAKE) -C doc/ clean +ifeq ($(PO2DEBCONF),yes) + # Hack for woody compatibility. This makes sure that the + # debian/templates file shipped in the source package doesn't + # specify encodings, which woody's debconf can't handle. If building + # on a system with po-debconf installed (conveniently debhelper (>= + # 4.1.16) depends on it), the binary-arch target will generate a + # better version for sarge. + echo 1 > debian/po/output + po2debconf debian/bitlbee.templates.master > debian/bitlbee.templates + rm -f debian/po/output +endif + + +install-arch: build-arch + if [ "`whoami`" != "root" -o ! -d debian ]; then exit 1; fi + mkdir -p debian/bitlbee/DEBIAN/ + $(MAKE) install install-etc DESTDIR=`pwd`/debian/bitlbee + + mkdir -p debian/bitlbee/usr/share/doc/bitlbee/ + cp doc/user-guide/user-guide.txt debian/bitlbee/usr/share/doc/bitlbee/ + cp doc/user-guide/user-guide.html debian/bitlbee/usr/share/doc/bitlbee/ + +binary-arch: build-arch install-arch + if [ "`whoami`" != "root" -o ! -d debian ]; then exit 1; fi + + chmod 755 debian/bitlbee.p* debian/bitlbee.config + + mkdir -p debian/bitlbee/usr/share/doc/bitlbee/examples/ + -cp doc/RELEASE-SPEECH* debian/bitlbee/usr/share/doc/bitlbee/ && gzip -9 debian/bitlbee/usr/share/doc/bitlbee/RELEASE-SPEECH* + cp doc/CREDITS doc/AUTHORS doc/TODO doc/README doc/FAQ debian/README.Debian debian/bitlbee/usr/share/doc/bitlbee/ + cp debian/changelog debian/bitlbee/usr/share/doc/bitlbee/changelog.Debian + cp debian/copyright debian/bitlbee/usr/share/doc/bitlbee/copyright + cp doc/CHANGES debian/bitlbee/usr/share/doc/bitlbee/changelog + cp utils/* debian/bitlbee/usr/share/doc/bitlbee/examples/ + cp debian/motd.txt debian/bitlbee/etc/bitlbee/ + cd debian/bitlbee/usr/share/; \ + gzip -9 doc/bitlbee/changelog.Debian doc/bitlbee/changelog doc/bitlbee/user-guide.txt \ + doc/bitlbee/examples/* man/man8/bitlbee.8 man/man5/bitlbee.conf.5 + + chown -R root.root debian/bitlbee/ + find debian/bitlbee/usr/share/ -type d -exec chmod 755 {} \; + find debian/bitlbee/usr/share/ -type f -exec chmod 644 {} \; + + cp debian/bitlbee.preinst debian/bitlbee/DEBIAN/preinst + cp debian/bitlbee.postinst debian/bitlbee/DEBIAN/postinst + cp debian/bitlbee.postrm debian/bitlbee/DEBIAN/postrm + cp debian/bitlbee.config debian/bitlbee/DEBIAN/config +ifeq ($(PO2DEBCONF),yes) + po2debconf -e utf8 debian/bitlbee.templates.master > debian/bitlbee.templates +endif + cp debian/bitlbee.templates debian/bitlbee/DEBIAN/templates + cp debian/bitlbee.conffiles debian/bitlbee/DEBIAN/conffiles + + if [ "$(DEBUG)" = "0" ]; then strip -R .comment -R .note debian/bitlbee/usr/sbin/bitlbee; fi + + cd debian/bitlbee; \ + find usr -type f -exec md5sum {} \; > DEBIAN/md5sums + dpkg-shlibdeps -Tdebian/bitlbee.substvars -dDepends debian/bitlbee/usr/sbin/bitlbee +ifdef BITLBEE_VERSION + dpkg-gencontrol -ldebian/changelog -isp -pbitlbee -Tdebian/bitlbee.substvars -Pdebian/bitlbee -v$(BITLBEE_VERSION)-0 -V'debconf-depends=debconf (>= $(MINDEBCONFVER)) | debconf-2.0' +else + dpkg-gencontrol -ldebian/changelog -isp -pbitlbee -Tdebian/bitlbee.substvars -Pdebian/bitlbee -V'debconf-depends=debconf (>= $(MINDEBCONFVER)) | debconf-2.0' +endif + + dpkg --build debian/bitlbee .. + +debug-build: + BITLBEE_VERSION=\"`date +%Y%m%d`-`hostname`-debug\" debian/rules clean binary DEBUG=1 + +binary: binary-arch +build: build-arch +install: install-arch + +.PHONY: build-arch build clean binary-arch binary install-arch install diff --git a/debug.c b/debug.c new file mode 100644 index 00000000..12f1ea29 --- /dev/null +++ b/debug.c @@ -0,0 +1,60 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Random debug stuff */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#define BITLBEE_CORE +#include "bitlbee.h" + +GHashTable *iocounter=NULL; +FILE *activity_output; + +static void for_each_node(gpointer key, gpointer value, gpointer user_data); + +void count_io_event(GIOChannel *source, char *section) { + long int *newcounter; + + if(iocounter==NULL) { + iocounter=g_hash_table_new(NULL, NULL); + } + + if(g_hash_table_lookup(iocounter, section)==NULL) { + newcounter=g_new0(long int, 1); + g_hash_table_insert(iocounter, section, newcounter); + } else { + newcounter=g_hash_table_lookup(iocounter, section); + (*newcounter)++; + } +} + +void write_io_activity(void) { + activity_output=fopen("ioactivity.log", "a"); + fprintf(activity_output, "Amount of GIO events raised for each section of the code:\n"); + g_hash_table_foreach(iocounter, &for_each_node, NULL); + fprintf(activity_output, "End of list\n"); + fclose(activity_output); +} + +static void for_each_node(gpointer key, gpointer value, gpointer user_data) { + fprintf(activity_output, "%s %ld\n", (char *)key, (*(long int *)value)); +} diff --git a/debug.h b/debug.h new file mode 100644 index 00000000..e17b40cf --- /dev/null +++ b/debug.h @@ -0,0 +1,33 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Random debug stuff */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef _DEBUG_H + +void count_io_event(GIOChannel *source, char *section); +void write_io_activity(void); + +#define _DEBUG_H +#endif + diff --git a/doc/AUTHORS b/doc/AUTHORS new file mode 100644 index 00000000..82d81862 --- /dev/null +++ b/doc/AUTHORS @@ -0,0 +1,19 @@ +Current developers: + +Wilmer van der Gaast <wilmer@gaast.net> + Main developer + +Jelmer 'ctrlsoft' Vernooij <jelmer@nl.linux.org> + Documentation, general hacking, Win32 port + +Maurits Dijkstra <mauritsd@xs4all.nl> + Daemon dude, plus some other stuff + + +Retired developer: + +Sjoerd 'lucumo' Hemminga <sjoerd@hemminga-online.nl> + NickServ, documentation + +The development team wishes to thank Sjoerd for his contributions to the +Bee. diff --git a/doc/CHANGES b/doc/CHANGES new file mode 100644 index 00000000..c135535d --- /dev/null +++ b/doc/CHANGES @@ -0,0 +1,514 @@ +Version 0.99: +- Fixed memory initialization bug in OSCAR module that caused crashes on + closing the connection. +- Most likely fixed the bug that caused BitlBee to use 100% CPU time in + some situations. +- Outgoing MSN typing notifications are now understood correctly by the + orignal MS Mac/Windows clients (again). +- Added "account add $protocol" to the documentation, got rid of a lot + of over-markup (i.e. overuse of bold-tags), reviewed some other parts. +- Small changes to help.xsl to fix small flaws in the help.txt formatting. +- Messaging yourself now doesn't make irssi or mIRC crash anymore. + +Finished 3 Nov 2005 + +Version 0.93: +- " is now correctly converted to " instead of '. +- Code cleanup in OSCAR module. +- Some changes for better RFC-compliancy. +- It's now possible to specify an alternate Jabber server. +- bitlbee_save() now also checks the return value of fclose(), which might + solve more problems with lost account data. +- Fixed a file descriptor leak in bitlbee.c. +- Signedness fixes (mainly to keep GCC4 happy). +- Away messages are now sent correctly to most ICQ clients. +- BitlBee knows now which connection sends HTML, so it will strip HTML + automatically, "set html strip" is no longer necessary. Also, outgoing HTML + is escaped correctly. +- info-command works for AIM-connections too now. +- /notices to users will be sent as auto-away replies. +- Messages about a connection now also mention a handle, for people who have + multiple accounts in use of the same protocol. +- Examples are back in help.txt. + +Finished 31 Oct 2005 + +Version 0.92: +- Fixed some formatting issues with the help.txt XSL-sheet. +- Moved the polling of the IRC connections to glib instead of a separate + select(). +- Added ctags generation to the Makefiles. +- Sending a CTCP TYPING message to a user in your buddy list now sends a + typing notification to that person, if the protocol supports it. You + probably want to write/use a script for your IRC client to do this. +- A dash is no longer considered a lowercase underscore in nicknames. +- CTCP's to buddies no longer alter their private/non-private state. +- Clean shutdown (with saving settings) on SIGTERM. +- Fixed a crash on connecting to unreachable Jabber/SSL servers. +- On ICQ, offline messages are now requested after receiving the buddy + list. This should get rid of the "Message from unknown OSCAR handle" + messages on login. +- The linked list of buddies/nicks is now always sorted, which makes the + blist output a bit more useful. +- Fixed a crash on renaming NickServ. (There's no reason to do this, but + still crashing isn't really how BitlBee should tell you that. ;-) +- Now the message about the "new" ICQ server-side buddy lists is only + displayed when the server-side list is still empty. +- The Yahoo! markup language stripper is now less likely to strip things + that shouldn't be stripped. +- Working around a shortcoming in the Yahoo! cleanup code that didn't + cause any serious problems in previous versions, but got pretty nasty + (100% CPU time usage) now with everything in the glib main loop. +- Fixed a bug in http_encode that might have caused buffer overflows + (although not likely to be exploitable) when trying to encode strings + with non-ASCII characters. +- Fixed AIM screenname handling on ICQ connections. +- When blocking someone, the MSN module now closes the conversation you're + having with him/her (if any) so he/she can't continue talking to you + (for as long as the conversation lasts). +- Away messages are only sent when messaging someone outside the control + channel. (And now also when the user is offline instead of just away.) +- Moved charset conversion in serv_buddy_rename() to the right place so + bogus changes are always detected now. +- iso8859-1 is now the default charset because -15 caused some problems + and because -1 is enough for most people anyway. +- Fixed crashes on attempting to add a buddy (or do other things) on + connections that are still initializing. +- Added support for server-side notifies using the WATCH command. +- IRC_MAX_ARGS is dead, at least for IRC commands. +- Fixed a bug that made BitlBee crash on failing fresh MSN switchboard + connections. +- Fixed an invisibility bug in the MSN code that handled transfers to + other servers in the middle of a session. +- Newline stripping added to prevent newline-in-friendlyname attacks. + (Which allowed remote people to make BitlBee send raw custom IRC lines.) + +Finished 23 Feb 2005 + +Version 0.91: +- Full support for ICQ server-side contact lists! + NOTE: BitlBee now ignores your client-side contact list. If you want to + import your ICQ contact list, use the import_buddies command. +- Added the import_buddies command for upgrading purposes. +- Added support for OpenSSL. +- Fixed one major problem with the daemon mode by getting rid of the global + IRC structure. +- Documentation fixes. help.txt is now generated from XML. Also updated the + installation manual. +- Made the quickstart up-to-date. (By Elizabeth Krumbach) +- Some bitlbeed additions. (By Marcus Dennis) +- info-command support for Jabber, MSN, Yahoo! and a more verbose info-reply + for ICQ. (By Frank Thieme) +- Support for Jabber over SSL. +- nick_get() appends a _ on duplicates instead of chopping off the last + character. +- Got rid of an unused piece of code that caused crashes. + (oscar.c:gaim_parse_clientauto) +- When splitting long messages into 450-char chunks, BitlBee now tries not + to cut off in the middle of a line. +- Added a warning when the user specifies an unknown OSCAR server. +- Removed GLib 2.x dependency, BitlBee will work with GLib 1.x again. +- Improved strip_html(), now less likely to strip non-HTML things. +- An invalid account-command will now display an error message. +- Fixed a bug that caused crashes when /CTCP'ing a groupchat channel. +- Hopefully better Unicode/non-ASCII support for ICQ. +- Fixed MSN connection crash on non-ASCII characters in screenname. +- Added some missing charset conversion calls. (serv_got_crap, + serv_buddy_rename) +- "account off" without extra arguments now logs off all connections. +- Fixed a crash-bug on disconnecting Yahoo! connections before they're fully + connected. +- Fixed a bug in helpfile handling which sometimes caused crashes in daemon + mode. +- block and allow commands work with just a nick as an argument again. +- Working around a crash on some probably invalid Yahoo! packets. +- Fixed a possible double free() on shutdown in irc_free(). +- Talking to ICQ people on AIM and vice versa and talking to people with + @mac.com accounts now possible. +- Low ASCII chars are now stripped from away-messages so the Jabber module + won't barf on BitchX and lame-script away messages anymore. + +Finished 25 Sep 2004 + +Version 0.90a: +- Fixed the Yahoo! authentication code. + +Finished 28 Jun 2004 + +Version 0.90: +- A complete rewrite of the MSN module. This gives BitlBee the following + new features/improvements: + * You can now start groupchats with MSN people without having to send them + a bogus message first. + * People who are in your MSN block/allow list, but not in your contact + list, shouldn't show up in your BitlBee buddy lists anymore. + * Passport authentication shouldn't lock up the whole program for a couple + of seconds anymore. Additionally, it should also work behind proxies now. + * Better recognition of incoming file transfers; they're now recognized + when coming from non-English MS Messenger clients too. + * Fixed a problem with MSN passwords with non-alphanumeric characters. + * Mail notification support (also for Yahoo!)... + * Parsing of maintenance messages (ie "Server is going down in x minutes"). + * Hopefully more stability. +- Changes in the OSCAR module: + * Better reading of ICQ-away-states. + * Lots of cleanups. +- Yahoo! module: + * Fixed authentication on 64-bit machines. (Patch from Matt Rogers) + * Better stripping of markup tags. +- Lots of cleanup in all IM-modules. +- Added support for using libnss instead of libgnutls. +- Reverse hostname lookups work on IPv6 sockets too now. (And don't crash + the program anymore.) +- Fixed a little problem with identifying right after registering a nick. +- Restored complete proxy support and added a proxy setting to the conffile. +- BitlBee can now tell you when people in your buddy list change their + "friendly name". +- Instead of an account number, you can also specify the protocol name or + (part of) the username as an account identification with commands like + "account on", "add", etc. +- BitlBee remembers what connection a question (i.e. authorization request) + belongs to and cleans it up when the connection goes down; this fixes + (one of) the last known crash bugs. +- Plus some other changes in question management. (The query_order setting + is one of them. The default behaviour changed a bit, for more information + please read "help set query_order".) +- Also fixed a memory management bug in the question code which caused some + crashes. +- Optimized some nick handling functions and added a hash of all the users + to speed up user_find() a bit (especially good for people with large + buddy and notify lists). +- Lots of changes for the Win32 port (see http://jelmer.vernstok.nl/). +- Added the drop-command. +- Fixed small problem with versions of sed which don't support the + + "operator" (the BSD version, for example, even though the operator is + documented properly in the re_format manpage...). +- Added the default_target setting. +- Added a CenterICQ2BitlBee conversion script. +- Put back the evaluator for "set charset" (which got lost somewhere between + 0.84 and 0.85), so an incorrect charset will be rejected again. +- ISON now (A) gives one single reply and (B) also replies when none of the + persons asked for are on-line. +- Using GConv instead of iconv now. +- Incoming messages larger than 450 characters are now split into parts + before sending them to the user. +- Fixed a bug in irc_exec() which could crash the program when some commands + were called with too little arguments. +- Fixed a dumb NULL pointer dereference in the JOIN command. +- Added rate limiting to bitlbeed. (Against server hammering) +- Added handling of CTCP PINGs (yet another self-ping used by some IRC + clients...) +- Added bitlbee_tab_completion.pl. +- Removed the vCard handling code from Jabber because it's (A) not used and + (B) had a possible format string vulnerability. +- Got rid of strcpy() in account.c. (Thanks to NETRIC for reporting these two + problems.) +- ISO8859-15 is now the default charset. + +Finished 21 May 2004 + +Version 0.85a: +- Fixed an authentication problem with logging into some MSN accounts. +- Removed a non-critical warning message from the ICQ servers when logging + in with an empty contact list. +- Fixed reading the [defaults] section of bitlbee.conf. +- The last newline in outgoing messages when using the buddy_sendbuffer is + now removed correctly. +- Yahoo! colour/font tag stripping now actually works. +- Fixed compilation on *BSD and some Linux architectures. + +Finished 24 Mar 2004 + +Version 0.85: +- Users can specify alternate configuration files/directories at runtime + now. +- Rename now doesn't choke on name changes with only case changes anymore. +- Imported the daemon patch into the main source tree. The daemon mode is + still experimental, this just eases maintenance. This daemon patch brings + a lot of features, including (as the name says) a real daemon mode and + also buffering of the data sent to the user, and flood protection. +- Strips font and colour codes from Yahoo! messages. +- Support for groupchats on Yahoo! +- Fixed removing Yahoo! buddies from a different group than "Buddies". +- Jabber presence packets with error messages are interpreted correctly now. + (They used to be parsed as a signin.) +- bitlbee_save() checks return values from fprintf() and writes to tempfiles + first to make sure no old files get lost when there's a write error. +- ICQ buddies are added all at once instead of once at a time. This should + solve problems with huge buddy lists. +- Made the client pinging timings configurable. (Without requiring + recompilation) +- MSN and Yahoo flag the connection as "wants_to_die" when they're logged + off because of a concurrent login. This means reconnection will be disabled + on concurrent logins. +- BitlBee can now buffer the messages sent to someone before they're actually + sent, and wait for a specified number of seconds for more lines to be added + to the buffer before the message will really be sent. +- Renamed the reconnect_delay setting to auto_reconnect_delay. +- Unknown settings aren't saved anymore. + +Finished 13 Mar 2004 + +Version 0.84: +- Removed the libsoup dependency. +- Fixed AuthMode=Registered: It will now restore your accounts when + identifying. +- Fixed Yahoo! support. +- Fixed a little memory leak in user.c. +- Fixed a security bug in unused code in proxy.c, only people who use + the HTTP proxy support and an untrusted proxy might need this. We + haven't done an emergency release for this fix because proxy support + is disabled by default. +- Fixed some memory leaks in IM-code. + +Finished 13 Feb 2004 + +Version 0.83: +- Fixed a crash bug on connecting to unsupported accounts. +- Fixed a problem with connecting to MSN accounts with empty buddy + lists. +- Fixed another inifite-loop bug in nick_get() and added a piece + of code which detects the infinite loop and asks the user to send + a bug report. +- Fixed iconv-Solaris linking issues. +- Fixed all the problems with spaces in AIM screennames now, we hope. +- Fixed a buffer overflow in the nick handling code. Buffers are + overflowed with static data (nulls), so we don't think it's exploitable. +- Added server-client pinging, useful for remote servers. +- Added the hostname setting. +- Some bitlbeed changes. +- Added a little part to the on-line quickstart about the settings and + other help topics, this hopefully answers a lot of FAQ's. +- Fixed the signal handler to re-raise the signal after the handler quits. + This makes sure the default handler is called after our handler sends + the user a bye-message, so core dumps are created and the parent will + get more useful information back from wait(). +- Added support for ICQ URL messages. +- Fixed strip_html() behaviour on unknown &entities;. +- Fixed a possible problem with Yahoo! +- Fixed a problem with logging into more than one MSN account at once. + +Finished 31 Dec 2003 + +Version 0.82: +- Fixed a little bug in nick.c which could cause a complete hang for + some tricky buddylists. (Thanks to Geert Hauwaerts for helping with + fixing this bug) +- Fixed MSN support. (Lots of thanks to Wouter Paesen!) +- Removed the old login/logout commands. +- Added the qlist command. +- Fixed a buffer overflow in the nick checking code. (Thanks to Jon + Åslund for pointing us at this problem) +- Adds the add_private and add_channel options for set handle_unknown. +- Some documentation updates. +- Added two small utilities to encode/decode BitlCrypted files. + +Finished 31 Oct 2003 + +Version 0.81a: +- This version just fixes some small things we should've fixed before + releasing 0.81: +- Fixed a small bug in the auto-reconnect cleanup code. +- Fixed the Makefile: Now it doesn't just overwrite your etc files when + installing. +- Fixed the Makefile: $prefix/etc/bitlbee/ is the default etcdir now. +- Disabling MSN by default, now that it doesn't work. It'll be back on + as soon as we get the module working again. + +Finished 16 Oct 2003 + +Version 0.81: +- Added a configuration file. +- Added support for the PASS command to restrict access to the daemon to + only the people who know this password. +- Only allowing registered people to use the daemon is possible too. +- In case you, at some time, need to check the currently running BitlBee + version, just CTCP-VERSION someone in the channel. +- Added the auto_connect setting for people who don't want the program + to connect to IM-networks automatically. +- Extended the blist command. +- Applied the auto-reconnect patch from G-Lite. +- Applied the iconv patch from Yuri Pimenov. +- Imported the MSN-friendlyname patch from Wouter Paesen. +- Away-message aliasing code now just parses the beginning of the string, + not the whole string. This allows you to have a more descriptive away + message like "Busy - Fixing bugs in BitlBee" and all the IM connections + will have a busy-like away-state. +- Added some information about away states to the help system. +- MSN file transfers aren't silently ignored anymore. +- Integrated the Yahoo protocol fix from Cerulean Studios (Trillian). + (Thanks to Tony Perrie!) +- Made all protocol plugins optional. (Patch from Andrej Kacian/Ticho) + +Finished 15 Oct 2003 + +Version 0.80: +- Fixed a very stupid bug in the MSN away-state reading. +- nick_cmp() now actually works, RFC-compliant. +- Fixed and cleaned up the away-state handling, there were some very + weird things in the original Gaim code base which broke this completely + all the time. +- The daemon prevents you from using root/NickServ as your nick now, + previous versions crashed on that. +- At last ... GROUP CHAT SUPPORT! :-D +- People who are *not* away get mode +v in #bitlbee now, so you can see + in /names who's away and who's not. +- Crashing BitlBee by using the NICKSERV command without any arguments + is impossible now. +- Added some notes about Darwin/OSX portability. +- Separated connections from accounts. This means restoring a lost + connection can be done using a simple "account on <number>" command. + See "help account" for more information. + *** For now this won't cause problems when upgrading because the login + command still exists (as an alias for "account add"). This alias will + not stay forever, though. +- irc_process() now makes sure it reads the whole available buffer before + executing the commands. Previous versions were very bad at handling + long floods from non-floodprotected clients. The code is still not + perfect, but good enough for local links. +- Allow/Deny questions from msn.c now also mention your own screenname. + This is useful for people who run two (or even more) MSN accounts in + one BitlBee. +- Fixed a little bug in the helpfile-changed-check code. +- A little trick in "make install" makes sure the help function in running + sessions doesn't break while upgrading. +- Added a nifty (and editable) MOTD. +- Added IRIX to the compatibility list. +- Added support for Cygwin. +- Better HTML-stripping, it also handles &entities; correctly now. +- Fixed some problems to make it compile on Solaris. +- Added support for messages from Miranda/Mac ICQ. (Code port from Gaim 0.59) +- Fixed the crash problem when removing yahoo buddies. +- Added the handle_unknown setting. +- Did some editing on a few docs. +- Added a FAQ. +- Added the daemon-patch by Maurits Dijkstra which adds stand-alone daemon + support to BitlBee. +- Jabber now doesn't barf on high ASCII characters in away messages anymore. + (Thanks to Christian Häggström <chm@c00.info>) + +Finished 24 Jun 2003 + +Version 0.74a: +- The music-festivals-are-bad-for-your-mind release. +- This one actually contains the fix for the bug 0.74 claimed to have. + +Finished 11 Jun 2003 + +Version 0.74: +- Fixed a security leak, where using a / in the nickname causes the saved + settings and account information to be stored in undesirable places. + +Finished 10 Jun 2003 + +Version 0.73: +- Fixed the unable-to-remove-icq-users (actually users from any *local* + contact list) bug. +- Fixed away bug in aim protocol. +- Fixed the 'statistics' under the blist command output. +- Removed the commands from the XML documentation because they're 'on-line' + already. +- Added some signal handling; ignoring SIGPIPE should als get rid of some + crashes (for some weird reason this has to be done). Also, crashes because + of things like segfaults are a bit more verbose now. ;-) +- Changed the select() timeout in main(), this might improve some latencies. + (At leasts it speeds up signing on (especially for ICQ) a lot!) +- Made the own-QUIT messages more compliant, probably. +- Fixed some memory-bugs, thanks to valgrind. +- irc_write() now checks the write() return value, and tries to send the rest + of the string, if it could not write it completely the first time. +- Hostname lookups also work on NetBSD now. + (Thanks to David.Edmondson*sun*com (hi spambot)) +- At last, a new protocol. Welcome to ... YAHOO! +- Documentation and code cleanup. Somehow the helpfile documented register + and identify twice, now that's what I call over-documenting.. :-/ +- Added the rename command to the helpfile, somehow I forgot that one. +- Been a bit pedantic about compiler warnings. They're all dead now. +- Fixed a small Makefile problem which gave an error when a clean tree was + "made distclean" +- Fixed a (possible) memory leak in nogaim.c:proto_away() +- Fixed the way proto_away() calls proto_away_alias_find(), now it gives + the *whole* list of away states +- proto_away() doesn't give a NULL message anymore because AIM didn't like + that +- Got rid of the last goto in my code (without ruining the code) +- Created a more samba-like compiling process (without the complete command + lines, just a simple echo) +- "help set ops" works now too, without quoting the "set ops" +- Trying to log in with a protocol without a prpl (ICQ and TOC, for example) + made previous versions crash + +Finished 13 Apr 2003 + +Version 0.72: +- Updated the documentation. +- Improved the MSN friendlyname code. (Although it doesn't seem to be perfect + yet..) +- info-command added to get more information about ICQ users. +- blist-command added to get a complete buddy list. +- Fixed a bug which broke the AIM code when adding a screenname with a space + in it. +- Added the NS alias for the NICKSERV command (Tony Vroon). +- Fixed the USERHOST command implementation (Tony Vroon). +- /me from IM-networks is now converted to a CTCP ACTION correctly. +- Added an utils/ directory with some misc toys and handy things. +- Added a /notice to the on_typing event. Don't use it though, the /notice + flood will just be a big annoyance. ;-) +- Some people like root and themself to be ops, some don't. Now it's + configurable. (set ops) +- Now the umode stuff actually works. Next step: Use those modes... (How?) + +Finished 19 Dec 2002 + +Version 0.71: +- Fixed the help command output layout (alignment) +- Added a sample xinetd file +- Cleaned up, 0.70 released with a build-stamp and DEADJOE file (oops).. +- Messages can be sent like '<user>, <message>' in the control channel now, + instead of just '<user>: <message>' +- Added a debug setting boolean: Set it to true for some debugging crap + which used to be on by default.. +- Changed the /whois reply: In the server section you now see the + connection this user belongs to. +- Added some root/permission checks. +- configure script works correctly when the terminating / is forgotten for + a directory. +- Fixed format string bug in protocols/oscar/meta.c (Hmm, what's the use of + that file?) +- Added '#include "crypting.h"' to commands.c to get rid of stupid warnings +- Fixed crash-bug (SIGSEGV when adding an @-less contact to MSN/Jabber) +- Added to_char setting +- Fixed bug in set.c: It ignored the new value returned by the evaluator + :-( +- Removed protocol tag from 'hostname' in user hostmask because this info + is in /whois' server section now +- Added the GPL. Somehow 0.7 released without a COPYING file.. :-/ +- Enhanced the root_command() parser, you can 'quote' "arguments" now so + arguments can be strings with spaces +- Debugging versions have True as the default value for set debug +- NICKSERV is now an alternative for PRIVMSG root. This does not affect + functionality of current NICKSERV commands, but does allow people to just + do identify <pass> in channel. +- NICKSERV REGISTER now doesn't try to log you in (to check if the user + exists) but checks for the existence of the user-configuration files. +- NICKSERV SET password now works (as does set password in channel). This + makes changing your password possible. +- NICKSERV password now stored in irc_t. +- ./configure now only bugs you about possible problems with strip if it's + actually going to strip (wooohoooo! _sexy_ :) +- Fixed a load of warnings in oscar.c, irc.c, nick.c and set.c +- Split up root_command() into a version which eats raw strings and one + which eats split strings +- New help system: Help available for most (all?) commands, all read from + an external help-file. +- Changed the maximum message length in irc_usermsg() from IRC_MAX_LINE to + 1024 (for loooong help messages..). +- Only allow user to set supported umodes. +- Fixed a memory leak in crypting.c (Thanks to Erik Hensema.) +- Added a send_handler callback to user_t. Needed for the following entry: +- Added the NickServ user as a root-clone. +- Disabled tcpd by default because it's just a PITA for a lot of systems + and because you can use /usr/sbin/tcpd as well. +- The root user can be renamed now. + +Finished 16 Sep 2002 diff --git a/doc/CREDITS b/doc/CREDITS new file mode 100644 index 00000000..83a19f51 --- /dev/null +++ b/doc/CREDITS @@ -0,0 +1,61 @@ +The authors thank the following people: + +- The Gaim team, for letting us steal their code. +- Sander van Schouwenburg, for his testing. +- Marten Klencke, for his testing. +- Lennart Kats, for putting up with Sjoerd, who bothered him many times to + test things. +- Ralph Slooten, for creating the RPM packages and testing the program. +- Erik Hensema, for creating SuSE RPM packages and some patching. +- Tony Vroon, for being a happy user and patch-submitter. +- Lots of Twente University students (and of course all the other users, + it's just that BitlBee seems to be some sort of hype over there ;-), + for spreading the word of the Bee. +- Han Boetes, for testing on and porting to OpenBSD. +- Paul Foote for some hints on running BitlBee on FreeBSD. +- Floris Kruisselbrink, for submitting the "help set ..." patch. +- Jan-Willem Lenting, for putting up with Wilmer, who wanted to test the + MSN away messages in the middle of the night. +- Jan Sabbe, for the hints about running BitlBee on Mac OS X. +- Kenny Gryp, for thoroughly testing the groupchat code and submitting + bug reports. +- Bryan Williams, for the help in getting BitlBee to run on Cygwin. +- Peter van Dijk for discovering a security leak in BitlBee. +- Christian Häggeström, for the fix for the Jabber barf on high ASCII + characters in away messages. +- James Ray, for some testing, development and patching. +- Yuri Pimenov, for writing the charset/iconv code, requested by a + lot of people. +- Wouter Paesen, for the MSN friendlyname code and the MSNP8 fix. +- Tony Perrie, for the RPM's and the Yahoo! patch. +- Andrej Kacian/Ticho for some patches. +- Jochem Kossen, for giving an account on his OpenBSD box to do some + portability testing. +- Geert Hauwaerts, for maintaining quite a big public BitlBee server + (msn.irssi.org, down for now) and reporting some very nice bugs. +- Robert C Lorentz and other AIM users, for all the reports on bugs (and + providing test accounts) about the stupid AIM spaces-in-screenname + handling. +- Scott Cruzen, for patching up strip_html() and more. +- Samuel Tardieu, for random patches. +- Tibor Csoegoer, for adding support for receiving URL messages to the + ICQ module. +- Jonathan/rise, for reporting and fixing a problem with the Yahoo! servers + and supporting BitlBee in other ways. +- Philip S Tellis, for libyahoo2. +- Simon Schubert, for providing code to read the names of ICQ contacts. +- NETRIC (www.netric.org) for auditting the BitlBee code security (and + finding some small problems). +- Elizabeth Krumbach, for her help on the docs. +- Frank Thieme, for the info-command enhancements and other patches. +- Marcus Dennis, for some bitlbeed enhancements. +- 1nfamus, for security auditing BitlBee code. +- Tijmen Ruizendaal, for some useful BitlBee-related irssi scripts. +- Ed Schouten, for reporting bugs. + +- And all other users who help us by sending useful bug reports, positive + feedback, nice patches and cool addons. Mentioning you all would make + this list fill up the whole source tree, so please don't be offended + by not seeing your name here. + +- All the people who run public BitlBee servers. diff --git a/doc/FAQ b/doc/FAQ new file mode 100644 index 00000000..8b6108f9 --- /dev/null +++ b/doc/FAQ @@ -0,0 +1,103 @@ +Frequently Asked Questions about BitlBee +======================================== + +Well, maybe not exactly "Frequently", but definitely "Asked" ... mostly by +the developers :-) + +Q: WTH were you guys on when you thought of that _weird_ name? +A: Though we live in The Netherlands and one of us even lives in Amsterdam, + we're not on drugs ... most of the time. + +Q: Okay, so the cops are so evil there, you can't even admit the truth, but + WTH does BitlBee mean then? +A: There are a few explanations. But the most symbolical goes like: the two + colors of the bee symbolize the two worlds betwee which the Bee flies. On + the one hand there's the IM-networks, on the other is IRC. + + Truth be told, it's absolute nonsense. The biggest nutcase in the + development team just played around with words for half an hour or so. + BitlBee was the result. We liked it, we kept it. We lovingly shorten it + to "the Bee" or even "het Bijtje" (Dutch for "the little Bee") sometimes. + +Q: What is 'root' doing in my control channel? I didn't start the Bee as + root. +A: 'root' is just the name for the most powerful user in BitlBee. Just like + in the system, it is root who is the ... eh ... root of the + functionality. Luckily, in BitlBee, root follows your orders (mostly), so + no BOFHs there. + + We get some complaints from time to time that 'root' is a confusing name. + Because of that name, some package maintainers have renamed root to, for + example, BitlBee. We recognize that some people see that need. If the + package maintainer hasn't renamed root, you can do this yourself with the + 'rename' command. + + The name root is not likely to change in the 'official' releases, though. + We find the metaphor of root correct and feel that there is no important + (security threatening) reason to change this non-creative piece of + artistic creativity. + +Q: Why is there no mailing list/CVS/<insert your favourite development tool + here>? +A: Short answer: we don't need it. + + Longer answer: and we're not completely convinced of their merits. + + Long answer: we currently use some shell scripts which make sure all + developers' copies are in sync with Wilmer's master copy. These shell + scripts also produce development "releases" and packages, as well as + stick them on a http-server. Patches are sent to Wilmer who decides + whether or not a patch should be applied and if it may need some + additional changes. This has the consistency advantage of a one-person + project while having the capacity of more people available. The system + works and we are kind of attached to it. + +Q: When is $random_feature going to be implemented? +A: Please do consult doc/TODO (preferably in a development snapshot, which + is more up-to-date than a TODO in a release version) before asking. + Please also check the documentation. You'd not be the first one to request + a feature which already exists! + + If your fabulous feature seems not to be requested before, just join + #bitlbee on irc.oftc.net and tell us the news. + + If your feature request is already in the TODO list, of course you can + still request it again/make us know that you'd like to see the feature as + well. But when the feature is in the "post-1.0" list, it's probably not + going to help. Most of the features in this list are low-priority because + we (the developers) don't need (or even want) them. (File transfers are a + good example here.) + Hence, they'll only be implemented when we really got too much spare + time. Obviously, if you're willing to help (i.e. submit a patch), you're + always welcome. + +Q: The messages I send and/or receive look weird. I see weird characters and + annoying HTML codes. Or, BitlBee does evil things when I send messages with + non-ASCII characters! +A: You probably have to change some settings. To get rid of HTML in messages, + see "help set html". If you seem to have problems with your charset, see + "help set charset". + +Q: Is BitlBee forked from Gaim? +A: BitlBee 0.7 was, sort-of. It contained a lot of code from Gaim 0.58 + (mainly the IM-code), although heavily modified, to make it work better + with BitlBee. We were planning to keep BitlBee up-to-date with later Gaim + versions, but this turned out to be very time-consuming because the API + changed a lot, and we don't have the time to keep up with those changes + all the time. + + These days, we replaced the Yahoo! code with libyahoo2 (which is a + separate Yahoo! module. It's derived from Gaim, but separately + maintained) and wrote our own MSN module. More modules are probably going + to be changed, so in the near future, the API might be the only thing + left from Gaim. + +Q: What's that Gaim doing in BitlBee error messages and my Jabber resource? +A: Ah, well, as you probably know we use some of Gaim's IM-modules, and we + don't think it's worth our time to do a search-and-replace over the whole + source to get rid of every reference to Gaim. In fact, we don't want to, + since we don't want to pretend we wrote all that code. + + About Jabber: If you want a different resource string, you can set it + when logging in by appending it to your Jabber ID, like: + lintux@jabber.com/BitlBee diff --git a/doc/INSTALL b/doc/INSTALL new file mode 100644 index 00000000..2ce92e20 --- /dev/null +++ b/doc/INSTALL @@ -0,0 +1 @@ +See the README file for installation instructions. diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 00000000..f6a80b21 --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,14 @@ +-include ../Makefile.settings + +install: + mkdir -p $(DESTDIR)$(MANDIR)/man8/ $(DESTDIR)$(MANDIR)/man5/ + install -m 0644 bitlbee.8 $(DESTDIR)$(MANDIR)/man8/ + install -m 0644 bitlbee.conf.5 $(DESTDIR)$(MANDIR)/man5/ + make -C user-guide $@ + +uninstall: + rm -f $(DESTDIR)$(MANDIR)/man8/bitlbee.8* + rm -f $(DESTDIR)$(MANDIR)/man5/bitlbee.conf.5* + make -C user-guide $@ + +.PHONY: install uninstall diff --git a/doc/README b/doc/README new file mode 100644 index 00000000..7a1309fb --- /dev/null +++ b/doc/README @@ -0,0 +1,172 @@ +INSTALLATION +============ + +If you installed BitlBee from a .deb or .rpm you probably don't have to do +anything anymore for installation. Just skip this section. + +If you want to compile BitlBee yourself, that's fine. Just run ./configure +to set up the build system. If configure succeeds, run make to build BitlBee. +make install will move all the files to the right places. + +After installation you have to set up inetd (you got that one running, +right? If not, just take a look at utils/bitlbeed.c) to start BitlBee. You +need to add BitlBee to inetd.conf, like this: + +6667 stream tcp nowait nobody /usr/sbin/tcpd /usr/local/sbin/bitlbee + +Creating a special BitlBee user and running BitlBee with that UID (instead +of just 'nobody') might be a good idea. + +Also, don't forget to create the configuration directory (/var/lib/bitlbee/ +by default) and chown it to the UID BitlBee is running as. Make sure this +directory is read-/writable by this user only. + +(For xinetd users a xinetd configuration file is included: doc/bitlbee.xinetd) + + +DEPENDENCIES +============ + +BitlBee's only real dependency is GLib. This is available on virtually every +platform. Any recent version of GLib (including 1.x versions) will work. + +These days, MSN Messenger clients have to connect to the MS Passport servers +through HTTPS. BitlBee can use serveral SSL libraries for this: GnuTLS, NSS +(which comes with Mozilla) and OpenSSL. OpenSSL is not GPL-compatible in some +situations, so using GnuTLS or NSS is preferred. However, especially on *BSD, +OpenSSL can be considered part of the operating system, which eliminates the +GPL incompatibility. + +The incompatibility is also the reason why the SSL library detection code +doesn't attempt to use OpenSSL. If you want to use OpenSSL, you have to +force configure to use it using the --ssl=openssl parameter. For more +information about this problem, see the URL's configure will write to stdout +when you attempt to use OpenSSL. + + +PORTABILITY ISSUES +================== + +FreeBSD NOTE: It looks like FreeBSD does allow port numbers instead of service +names in inetd.conf, as long as the service for this port exists in +/etc/services. Linux isn't this strict. If you got problems in FreeBSD, just +add a line for your chosen port number to /etc/services. + +Darwin/Mac OS X is even more strict; you should add a bitlbee service to +/etc/services and use that name in inetd.conf. Not really a problem, since +that's the cleanest way of doing this on any operating system anyway. + +Cygwin NOTE: You'll need a glib installation to run BitlBee. However, Cygwin +doesn't provide a glib package. You can download a binary tar.gz from: +<http://my.dreamwiz.com/jbdoll/>. When you installed it, BitlBee should work +fine. You'll probably like bitlbeed or xinetd to get it running on the +network. + +On some non-Linux systems the program still suffers from some random bugs. +Please do report them, we might be able to fix them if they're not too +mysterious. + + +RUNNING ON SERVERS WITH MANY USERS +================================== + +BitlBee is not yet bug-free. Sometimes a bug causes the program to get into +an infinite loop. Something you really don't want on a public server, +especially when that machine is also used for other (mission-critical) things. +For now we can't do much about it. We haven't seen that happen for a long +time already on our own machines, but some people still manage to get +themselves in nasty situations we haven't seen before. + +For now the best we can offer against this problem is bitlbeed, which allows +you to setrlimit() the child processes to use no more than a specified +number of CPU seconds. Not the best solution (not really a solution anyway), +but certainly trashing one busy daemon process is better than trashing your +whole machine. + +We don't believe adding a limit for bitlbee to /etc/security/limits.conf will +work, because that file is only read by PAM (ie just for real login users, +not daemons). + +See utils/bitlbeed.c for more information about the program. + +Just a little note: We run our public server im.bitlbee.org for a couple of +months now, and so far we haven't experienced this problem yet. The only +BitlBee processes killed because of CPU-time overuse were running for a long +time already, they were usually killed during the MSN login process (which +is quite CPU-time consuming). + + +USAGE +===== + +Not much to say here, it's all documented elsewhere already. Just connect to +the new BitlBee IRC server and the bot (root) will tell you what to do. + + +BACKGROUNDS +=========== + +We are both console lovers. But it is annoying to have a few tty's open with +chat things in them. IRC, ICQ, MSN, AIM, Jabber... For X there is Gaim, which +supports many chatprotocols. Why wasn't there such a thing for the console? + +The idea to port Gaim was easily thought of, of course. But we liked our IRC +clients. And we used it the most, so we used it best. Importing it into the +IRC client was a nice idea. But what if someone liked a different client. +Then (s)he had to duplicate our work. + +That's a shame, we thought. Doing work twice is pointless. So when Wilmer +got the ingenious thought in his mind while farming, to create an IRC to +other chatnetworks gateway, we were both so excited, that we started working +on it almost immediately. And the result is BitlBee. + + +WEBSITE +======= + +You can find new releases of BitlBee at: +http://www.bitlbee.org/ + + +A NOTE ON ENCRYPTION +==================== + +BitlBee stores the accounts and settings (not your contact list though) in +some sort of encrypted/obfuscated format. + +*** THIS IS NOT A SAFE FORMAT! *** + +You should still make sure the rights to the configuration directory and +files are set so that only root and the BitlBee user can read/write them. + +This format is not to prevent malicicous users from running with your +passwords, but to prevent accidental glimpses of the administrators to cause +any harm. You have no choice but to trust root though. + + +LEGAL +===== + +BitlBee is distributed under the GPL (GNU General Public License). See the +file COPYING for this license. + +Unfortunately some parts of the Gaim Jabber plugin (most notably the XML +code) were licensed under the MPL (Mozilla Public License) version 1.1. We +could not relicense this code under the GPL. As such it is still licensed +under the MPL. The parts of the code to which this applies are marked as +such. + +The MPL is provided in the file MPL-1.1.txt. This license is not GPL +compatible. It is however a free software license. + +Another part (the md5 algorithm) is licensed under the Aladdin license. +This license can be found in the files, to which this applies. + +The Yahoo! library used by BitlBee is libyahoo2 <http://libyahoo2.sf.net/>, +also licensed under the GPL. + + + BitlBee - An IRC to other chat networks gateway + <http://www.bitlbee.org/> + Copyright (C) 2002-2004 Wilmer van der Gaast <wilmer@gaast.net> + and others diff --git a/doc/TODO b/doc/TODO new file mode 100644 index 00000000..3bd241ef --- /dev/null +++ b/doc/TODO @@ -0,0 +1,40 @@ +TODO for BitlBee: + +We're trying to keep a better TODO list now. The priorities here are somewhat +random, maybe. Or at least they change from time to time. The "After 1.0" +changes are very low-priority, the "Before 1.0" are "highest" priority, the +others are changes we'll do when we feel like doing them. They're usually not +as critical as the "Before 1.0" changes, but still a bit more important than +"After 1.0". + +KNOWN BUGS TO FIX BEFORE 1.0: +- 100% cpu usage bugs. + -> They're still there... :-( +- Check if the IRC send flood protection is reliable now. + -> Probably not, at least it still causes troubles on logging in for some + people. Maybe the thresholds aren't okay yet. Logins are just floody, + nothing we can do about that. + +Some time: (mainly features) +- Rewrite Jabber module - the current one sucks. +- Test Yahoo! groupchats a bit better, because they still seem to be a bit + flakey. + -> There are bug reports from time to time, but we can't do much about + them, possibly it's more a libyahoo2 problem. +- Groupchats + -> Make them work on other nets than MSN/Yahoo as well. +- Make usernames case-insensitive. (On case-insensitive filesystems this + change isn't necessary. This one is going to suck with backward-compati- + bilty...) + -> We'll probably combine this with the introduction of a better file + format for userdata. +- Remind the user of unanswered questions after some time. +- Maybe a way to send global messages. (for server shutdowns, for example) +- Away-auto-replies. +- Allow nick changes. + +After 1.0: (mainly toys) +- File transfers -> DCC +- SSL support? Persistent connections? Things you can do with a bouncer/proxy. +- Support for buddy groups. +- What else? diff --git a/doc/bitlbee.8 b/doc/bitlbee.8 new file mode 100644 index 00000000..ccce2135 --- /dev/null +++ b/doc/bitlbee.8 @@ -0,0 +1,123 @@ +.\" BitlBee is free software; you can 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; see the file COPYING. If not, write to +.\" the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +.\" +.TH bitlbee 8 "07 March 2004" +.SH NAME +BitlBee \- IRC gateway to IM chat networks +.SH SYNOPSIS +.PP +.B bitlbee +[-I] +[-c \fIconfiguration file\fP] +[-d \fIconfiguration directory\fP] +.PP +.B bitlbee +-D +[-i \fIaddress\fP] +[-p \fIport number\fP] +[-n] +[-v] +[-c \fIconfiguration file\fP] +[-d \fIconfiguration directory\fP] +.PP +.B bitlbee +-h +.RI +.SH DESCRIPTION +BitlBee is an IRC daemon that can talk to instant messaging +networks and acts as a gateway. Users can connect to the server +with any normal IRC client and see their 'buddy list' in +#bitlbee. BitlBee's protocol support is based on the gaim +protocol plugins. BitlBee currently supports Oscar (aim and icq), +MSN, Jabber and Yahoo. + +\fBbitlbee\fP should be called by +.BR inetd (8). +(Or \fBbitlbeed\fP, +if you can't run and/or configure \fBinetd\fP.) There is an experimental +daemon mode too, in which BitlBee will serve all clients in one process +(and does not require inetd), but this mode is still experimental. +There are still some bugs left in BitlBee, and if they cause a crash, +that would terminate the BitlBee connection for all clients. +.PP +.SH OPTIONS +.PP +.IP "-I" +Run in +.BR inetd (8) +mode. This is the default setting, you usually don't have to specify this +option. +.IP "-D" +Run in daemon mode. In this mode, BitlBee forks to the background and +waits for new connections. All clients will be served from one process. +This is still experimental. See the note above for more information. +.IP "-i \fIaddress\fP" +Only useful when running in daemon mode, to specify the network interface +(identified by IP address) to which the daemon should attach. Use this if +you don't want BitlBee to listen on every interface (which is the default +behaviour). +.IP "-p \fIport number\fP" +Only useful when running in daemon mode, to specify the port number on +which BitlBee should listen for connections. 6667 is the default value. +.IP "-n" +Only useful when running in daemon mode. This option prevents BitlBee from +forking into the background. +.IP "-v" +Be more verbose. This only works together with the \fB-n\fP flag. +.IP "-c \fIpath to other configuration file\fP" +Use a different configuration file. +.IP "-d \fIpath to user settings directory\fP" +BitlBee normally saves every user's settings in \fB/var/lib/bitlbee/\fP. If +you want the settings to be stored somewhere else (for example, if you don't +have write permissions in the default location), use this option. +.IP "-h" +Show help information. +.SH COMMANDS +To get a complete list of commands, please use the \fBhelp commands\fP +command in the #bitlbee channel. +.SH "SEE ALSO" +.BR ircd (8), +.BR inetd (8), +.BR inetd.conf (5), +.BR gaim (1). + +.BR http://www.bitlbee.org/ + +For more information on using BitlBee, once connected, you should use +the on-line help system. +.SH BUGS +Of course there are bugs. If you find some, please report them by e-mail +to \fBwilmer@gaast.net\fP, or join \fB#bitlbee\fP on the OFTC network. +(\fBirc.bitlbee.org\fP) +.SH LICENSE +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +.PP +This program is distributed in the hope that it will be useful, but +\fBWITHOUT ANY WARRANTY\fR; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. +.PP +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple PLace, Suite 330, Boston, MA 02111-1307 USA +.SH AUTHORS +.PP + Wilmer van der Gaast <lintux@lintux.cx> +.BR + Jelmer Vernooij <jelmer@vernstok.nl> +.BR + Maurits Dijkstra <mauritsd@xs4all.nl> diff --git a/doc/bitlbee.conf.5 b/doc/bitlbee.conf.5 new file mode 100644 index 00000000..04b11b9a --- /dev/null +++ b/doc/bitlbee.conf.5 @@ -0,0 +1,19 @@ +.\" Manual page for bitlbee.conf, derived from the modules.conf manpage. +.\" Writing a complete manpage from scratch is just too much work... +.\" +.\" This program is distributed according to the Gnu General Public License. +.\" See the file COPYING in the base distribution directory +.\" +.TH BITLBEE.CONF 5 "07 March 2004" +.UC 4 +.SH NAME +bitlbee.conf \- configuration file for +.BR bitlbee (8) +.SH DESCRIPTION +This file contains system-wide settings for the +.BR bitlbee (8) +program. For more information about the file syntax, please read the +example configuration file which comes with the program. The default +file contains lots of comments which explain all the available options. +.SH SEE ALSO +.BR bitlbee (8) diff --git a/doc/bitlbee.xinetd b/doc/bitlbee.xinetd new file mode 100644 index 00000000..5b2dec3f --- /dev/null +++ b/doc/bitlbee.xinetd @@ -0,0 +1,12 @@ +## xinetd file for BitlBee. Please check this file before using it, the +## user, port and/or binary location might be wrong. + +service 6667 +{ + socket_type = stream + protocol = tcp + wait = no + user = nobody + server = /usr/local/sbin/bitlbee + port = 6667 +} diff --git a/doc/user-guide/Installation.xml b/doc/user-guide/Installation.xml new file mode 100644 index 00000000..6980ba3c --- /dev/null +++ b/doc/user-guide/Installation.xml @@ -0,0 +1,114 @@ +<chapter id="Installation"> + +<title>Installation</title> + +<sect1> +<title>Downloading the package</title> + +<para> +The latest BitlBee release is always available from <ulink +url="http://www.bitlbee.org/">http://www.bitlbee.org/</ulink>. +Download the package with your favorite program and unpack it: <command>tar +xvfz bitlbee-<version>.tar.gz</command> where <version> is to be +replaced by the version number of the BitlBee you downloaded (e.g. 0.91). +</para> + +</sect1> + +<sect1> +<title>Compiling</title> +<para> +BitlBee's build system has to be configured before compiling. The +<filename>configure</filename> script will do this for you. Just run +it, it'll set up with nice and hopefully well-working defaults. If you +want to change some settings, just try +<command>./configure --help</command> and see what you can do. +</para> + +<para>Some variables that might be of interest to the normal user:</para> + +<itemizedlist> +<listitem><para>prefix, bindir, etcdir, mandir, datadir - The place where +all the BitlBee program files will be put. There's usually no reason to +specify them all separately, just specifying prefix (or keeping the default +<filename>/usr/local/</filename>) should be okay.</para></listitem> +<listitem><para>config - The place where BitlBee will save all the per-user +settings and buddy information. <filename>/var/lib/bitlbee/</filename> +is the default value.</para></listitem> +<listitem><para>msn, jabber, oscar, yahoo - By default, support for all +these IM-protocols (OSCAR is the protocol used by both ICQ and AIM) will +be compiled in. To make the binary a bit smaller, you can use these options +to leave out support for protocols you're not planning to use.</para></listitem> +<listitem><para>debug - Generate an unoptimized binary with debugging symbols, +mainly useful if you want to do some debugging or help us to track down a +problem.</para></listitem> +<listitem><para>strip - By default, unnecessary parts of the generated binary +will be stripped out to make it as small as possible. If you don't want this +(because it might cause problems on some platforms), set this to 0. +</para></listitem> +<listitem><para>flood - To secure your BitlBee server against flooding attacks, +you can use this option. It's not compiled in by default because it needs +more testing first.</para></listitem> +<listitem><para>ssl - The MSN and Jabber modules require an SSL library for +some of their tasks. BitlBee can use three different SSL libraries: GnuTLS, +mozilla-nss and OpenSSL. (OpenSSL is, however, a bit troublesome because of +licensing issues, so don't forget to read the information configure will +give you when you try to use OpenSSL!) By default, configure will try to +detect GnuTLS or mozilla-nss. If none of them can be found, it'll give up. +If you want BitlBee to use OpenSSL, you have to explicitly specify that. +</para></listitem> +</itemizedlist> + +<para> +After running <filename>configure</filename>, you should run +<command>make</command>. After that, run <command>make install</command> as +root. +</para> + +</sect1> + +<sect1> +<title>Configuration</title> + +<para> +By default, BitlBee runs as the user nobody. You might want +to run it as a seperate user (some computers run named or apache as nobody). +</para> + +<para> +Since BitlBee uses inetd, you should add the following line to <filename>/etc/inetd.conf</filename>: +</para> + +<para> +<programlisting> +6667 stream tcp nowait nobody /usr/local/sbin/bitlbee bitlbee +</programlisting> +</para> + +<para> +Inetd has to be restarted after changing the configuration. Either +<command>killall -HUP inetd</command> or +<command>/etc/init.d/inetd restart</command> should do the job on most systems. +</para> + +<para> +You might be one of the.. ehr, lucky people running an xinetd-powered distro. +<command>xinetd</command> is quite different and they seem to be proud of that.. ;-) +Anyway, if you want BitlBee to work with <command>xinetd</command>, just copy the +bitlbee.xinetd file to your /etc/xinetd.d/ directory (and probably edit it to suit +your needs). +</para> + +<para> +You should create a directory where BitlBee can store it's data files. This +should be the directory named after the value 'CONFIG' in Makefile.settings. +The default is <filename>/var/lib/bitlbee</filename>, which can be created +with the command <command>mkdir -p /var/lib/bitlbee</command>. This +directory has to be owned by the user that runs bitlbee. To make +'nobody' owner of this directory, run <command>chown nobody /var/lib/bitlbee</command>. +Because things like passwords are saved in this directory, it's probably +a good idea to make this directory owner-read-/writable only. +</para> +</sect1> + +</chapter> diff --git a/doc/user-guide/Makefile b/doc/user-guide/Makefile new file mode 100644 index 00000000..98c4e99f --- /dev/null +++ b/doc/user-guide/Makefile @@ -0,0 +1,46 @@ +-include ../../Makefile.settings +EXTRAPARANEWLINE = 1 +# EXTRAPARANEWLINE = 0 + +all: user-guide.txt user-guide.html help.txt # user-guide.pdf user-guide.ps user-guide.rtf + +%.tex: %.db.xml + xsltproc --stringparam l10n.gentext.default.language "en" --stringparam latex.documentclass.common "" --stringparam latex.babel.language "" --output $@ http://db2latex.sourceforge.net/xsl/docbook.xsl $< + +%.txt: %.db.xml + xmlto --skip-validation txt $< + mv $*.db.txt $@ + +%.html: %.db.xml + xsltproc --output $@ http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl $< + +%.pdf: %.db.xml + xmlto --skip-validation pdf $< + mv $*.db.pdf $@ + +%.ps: %.db.xml + xmlto --skip-validation ps $< + mv $*.db.ps $@ + +help.xml: commands.xml + +%.db.xml: %.xml docbook.xsl + xsltproc --xinclude --output $@ docbook.xsl $< + +help.txt: help.xml help.xsl + xsltproc --stringparam extraparanewline "$(EXTRAPARANEWLINE)" --xinclude help.xsl $< | perl -0077 -pe 's/\n\n%/\n%/s; s/_b_/\002/g;' > $@ + +clean: + rm -f *.html *.pdf *.ps *.rtf *.txt *.db.xml + +install: + mkdir -p $(DESTDIR)$(DATADIR) + chmod 0755 $(DESTDIR)$(DATADIR) + rm -f $(DESTDIR)$(DATADIR)/help.txt # Prevent help function from breaking in running sessions + install -m 0644 help.txt $(DESTDIR)$(DATADIR)/help.txt + +uninstall: + rm -f $(DESTDIR)$(DATADIR)/help.txt + -rmdir $(DESTDIR)$(DATADIR) + +.PHONY: clean install uninstall diff --git a/doc/user-guide/Support.xml b/doc/user-guide/Support.xml new file mode 100644 index 00000000..401a4295 --- /dev/null +++ b/doc/user-guide/Support.xml @@ -0,0 +1,48 @@ +<chapter id="Support"> + +<title>Support</title> + +<sect1> +<title>BitlBee is beta software</title> + +<para> +Although BitlBee has quite some functionality it is still beta. That means it +can crash at any time, corrupt your data or whatever. Don't use it in +any production environment and don't rely on it. +</para> + +</sect1> + +<sect1> +<title>Support channels</title> + +<sect2> +<title>The World Wide Web</title> + +<para> +<ulink url="http://www.bitlbee.org/">http://www.bitlbee.org/</ulink> +is the homepage of bitlbee and contains the most recent news on bitlbee and +the latest releases. +</para> + +</sect2> + +<sect2> +<title>IRC</title> +<para> +BitlBee is discussed on #bitlbee on the OFTC IRC network (server: irc.oftc.net). +</para> + +</sect2> + +<sect2> +<title>Mailinglists</title> +<para> +BitlBee doesn't have any mailinglists. +</para> + +</sect2> + +</sect1> + +</chapter> diff --git a/doc/user-guide/Usage.xml b/doc/user-guide/Usage.xml new file mode 100644 index 00000000..5d87e32b --- /dev/null +++ b/doc/user-guide/Usage.xml @@ -0,0 +1,47 @@ +<chapter id="Usage"> + +<title>Usage</title> + +<sect1> +<title>Connecting to the server</title> +<para> +Since BitlBee acts just like any other irc daemon, you can connect to +it with your favorite irc client. Launch it and connect to localhost port 6667 +(or whatever host/port you are running bitlbee on). +</para> + +</sect1> + +<sect1> +<title>The #bitlbee control channel</title> + +<para> +Once you are connected to the BitlBee server, you are automatically joined +to #bitlbee on that server. This channel acts like the 'buddy list' you have +on the various other chat networks. +</para> + +<para> +The user 'root' always hangs around in #bitlbee and acts as your interface +to bitlbee. All commands you give on #bitlbee are 'answered' by root. +</para> + +</sect1> + +<sect1> +<title>Talking to people</title> + +<para> +You can talk to by starting a query with them. In most irc clients, +this can be done with either <command>/msg <nick> <text></command> +or <command>/query <nick></command>. +</para> + +<para> +To keep the number of open query windows limited, you can also talk to people +in the control channel, like <command><nick>: <text></command>. +</para> + +</sect1> + +</chapter> diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml new file mode 100644 index 00000000..1fc30e6d --- /dev/null +++ b/doc/user-guide/commands.xml @@ -0,0 +1,690 @@ +<chapter id="commands"> + <title>Bitlbee commands</title> + + <command-list/> + + <bitlbee-command name="account"> + <short-description>IM-account list maintenance</short-description> + <syntax>account <action> [<arguments>]</syntax> + + <description> + + <para> + Available actions: add, del, list, on, off. See <emphasis>help account <action></emphasis> for more information. + </para> + + </description> + + <bitlbee-command name="add"> + <syntax>account add <protocol> <username> <password> [<server>]</syntax> + + <description> + <para> + Adds an account on the given server with the specified protocol, username and password to the account list. Supported protocols right now are: Jabber, MSN, OSCAR (AIM/ICQ) and Yahoo. For more information about adding an account, see <emphasis>help account add <protocol></emphasis>. + </para> + </description> + + <bitlbee-command name="jabber"> + <syntax>account add jabber <handle> <password> [<servertag>]</syntax> + + <description> + <para> + Note that the servertag argument is optional. You only have to use it if the part after the @ in your handle isn't the hostname of your Jabber server, or if you want to use SSL/connect to a non-standard port number. The format is simple: [<servername>[:<portnumber>][:ssl]]. For example, this is how you can connect to Google Talk: + </para> + </description> + + <ircexample> + <ircline nick="wilmer">account add jabber example@gmail.com hobbelmeeuw talk.google.com:5223:ssl</ircline> + <ircline nick="root">Account successfully added</ircline> + </ircexample> + + <description> + <para> + Note that Google talk is SSL-only, but officially reachable over both port 5222 and 5223. However, for some people only port 5222 works, for some people only 5223. This is something you'll have to try out. + </para> + </description> + </bitlbee-command> + + <bitlbee-command name="msn"> + <syntax>account add msn <handle> <password></syntax> + + <description> + <para> + For MSN connections there are no special arguments. + </para> + </description> + </bitlbee-command> + + <bitlbee-command name="oscar"> + <syntax>account add oscar <handle> <password> [<servername>]</syntax> + + <description> + <para> + Specifying a server is required for OSCAR, since OSCAR can be used for both ICQ- and AIM-connections. Although these days it's supposed to be possible to connect to ICQ via AIM-servers and vice versa, we like to stick with this separation for now. For ICQ connections, the servername is <emphasis>login.icq.com</emphasis>, for AIM connections it's <emphasis>login.oscar.aol.com</emphasis>. + </para> + </description> + + <ircexample> + <ircline nick="wilmer">account add oscar 72696705 hobbelmeeuw login.icq.com</ircline> + <ircline nick="root">Account successfully added</ircline> + </ircexample> + </bitlbee-command> + + <bitlbee-command name="yahoo"> + <syntax>account add yahoo <handle> <password></syntax> + + <description> + <para> + For Yahoo! connections there are no special arguments. + </para> + </description> + </bitlbee-command> + + </bitlbee-command> + + <bitlbee-command name="del"> + <syntax>account del <account id></syntax> + + <description> + <para> + This commands deletes an account from your account list. You should signoff the account before deleting it. + </para> + + + <para> + The account ID can be a number (see <emphasis>account list</emphasis>), the protocol name or (part of) the screenname, as long as it matches only one connection. + </para> + </description> + </bitlbee-command> + + <bitlbee-command name="on"> + <syntax>account on [<account id>]</syntax> + + <description> + <para> + This command will try to log into the specified account. If no account is specified, BitlBee will log into all the accounts. (Including accounts awaiting a reconnection) + </para> + + <para> + The account ID can be a number (see <emphasis>account list</emphasis>), the protocol name or (part of) the screenname, as long as it matches only one connection. + </para> + </description> + + </bitlbee-command> + + <bitlbee-command name="off"> + <syntax>account off [<account id>]</syntax> + + <description> + <para> + This command disconnects the connection for the specified account. If no account is specified, BitlBee will deactivate all active accounts. (Including accounts awaiting a reconnection) + </para> + + <para> + The account ID can be a number (see <emphasis>account list</emphasis>), the protocol name or (part of) the screenname, as long as it matches only one connection. + </para> + </description> + </bitlbee-command> + + <bitlbee-command name="list"> + <syntax>account list</syntax> + + <description> + <para> + This command gives you a list of all the accounts known by BitlBee, including the numbers you'll need for most account commands. + </para> + </description> + </bitlbee-command> + </bitlbee-command> + + <bitlbee-command name="add"> + <short-description>Add a buddy to your contact list</short-description> + <syntax>add <connection> <handle> [<nick>]</syntax> + + <description> + <para> + Adds the given buddy at the specified connection to your buddy list. The account ID can be a number (see <emphasis>account list</emphasis>), the protocol name or (part of) the screenname, as long as it matches only one connection. + </para> + + <para> + If you want, you can also tell BitlBee what nick to give the new contact. Of course you can also use the <emphasis>rename</emphasis> command for that, but sometimes this might be more convenient. + </para> + </description> + + <ircexample> + <ircline nick="ctrlsoft">add 3 gryp@jabber.org grijp</ircline> + <ircaction nick="grijp" hostmask="gryp@jabber.org">has joined <emphasis>#bitlbee</emphasis></ircaction> + </ircexample> + </bitlbee-command> + + <bitlbee-command name="info"> + <short-description>Request user information</short-description> + <syntax>info <connection> <handle></syntax> + <syntax>info <nick></syntax> + + <description> + <para> + Requests IM-network-specific information about the specified user. The amount of information you'll get differs per protocol. For some protocols (ATM Yahoo! and MSN) it'll give you an URL which you can visit with a normal web browser to get the information. + </para> + </description> + + <ircexample> + <ircline nick="ctrlsoft">info 0 72696705</ircline> + <ircline nick="root">User info - UIN: 72696705 Nick: Lintux First/Last name: Wilmer van der Gaast E-mail: lintux@lintux.cx</ircline> + </ircexample> + + </bitlbee-command> + + <bitlbee-command name="remove"> + <short-description>Remove a buddy from your contact list</short-description> + <syntax>remove <nick></syntax> + + <description> + <para> + Removes the specified nick from your buddy list. + </para> + </description> + + <ircexample> + <ircline nick="ctrlsoft">remove gryp</ircline> + <ircaction nick="gryp" hostmask="gryp@jabber.jabber.org">has quit <emphasis>[Leaving...]</emphasis></ircaction> + </ircexample> + + </bitlbee-command> + + <bitlbee-command name="block"> + <short-description>Block someone</short-description> + <syntax>block <nick></syntax> + <syntax>block <connection> <handle></syntax> + + <description> + <para> + Puts the specified user on your ignore list. Either specify the user's nick when you have him/her in your contact list or a connection number and a user handle. + </para> + </description> + </bitlbee-command> + + <bitlbee-command name="allow"> + <short-description>Unblock someone</short-description> + <syntax>allow <nick></syntax> + <syntax>allow <connection> <handle></syntax> + + <description> + <para> + Reverse of block. Unignores the specified user or user handle on specified connection. + </para> + </description> + </bitlbee-command> + + <bitlbee-command name="set"> + <short-description>Miscellaneous settings</short-description> + <syntax>set [<variable> [<value>]]</syntax> + + <description> + + <para> + Without any arguments, this command lists all the set variables. You can also specify a single argument, a variable name, to get that variable's value. To change this value, specify the new value as the second argument. + </para> + + <para> + To get more help information about a setting, try: + </para> + + </description> + + <ircexample> + <ircline nick="ctrlsoft">help set private</ircline> + </ircexample> + + </bitlbee-command> + + <bitlbee-command name="help"> + <short-description>BitlBee help system</short-description> + + <syntax>help [subject]</syntax> + + <description> + <para> + This command gives you the help information you're reading right now. If you don't give any arguments, it'll give a short help index. + </para> + </description> + </bitlbee-command> + + <bitlbee-command name="save"> + <short-description>Save your account data</short-description> + <syntax>save</syntax> + + <description> + <para> + This command saves all your nicks and accounts immediately. Handy if you have the autosave functionality disabled, or if you don't trust the program's stability... ;-) + </para> + </description> + </bitlbee-command> + + <bitlbee-setting name="charset" type="string"> + <default>iso8859-1</default> + <possible-values>you can get a list of all possible values by doing 'iconv -l' in a shell</possible-values> + + <description> + <para> + The charset setting enables you to use different character sets in BitlBee. These get converted to UTF-8 before sending and from UTF-8 when receiving. + </para> + + <para> + If you don't know what's the best value for this, at least iso8859-1 is the best choice for most Western countries. You can try to find what works best for you on http://czyborra.com/charsets/iso8859.html + </para> + </description> + + </bitlbee-setting> + + <bitlbee-setting name="private" type="boolean"> + <default>True</default> + + <description> + + <para> + If value is true, messages from users will appear in separate query windows. If false, messages from users will appear in the control channel. + </para> + + <para> + This setting is remembered (during one session) per-user, this setting only changes the default state. This option takes effect as soon as you reconnect. + </para> + </description> + </bitlbee-setting> + + <bitlbee-setting name="save_on_quit" type="boolean"> + <default>True</default> + + <description> + <para> + If enabled causes BitlBee to save all current settings and account details when user disconnects. This is enabled by default, and these days there's not really a reason to have it disabled anymore. + </para> + </description> + </bitlbee-setting> + + <bitlbee-setting name="html" type="string"> + <default>nostrip</default> + <possible-values>strip, nostrip</possible-values> + + <description> + <para> + Determines what BitlBee should do with HTML in messages. If set to nostrip, HTML in messages will not be touched. If set to strip, all HTML will be stripped from messages. Unfortunately this sometimes strips too much. + </para> + </description> + </bitlbee-setting> + + <bitlbee-setting name="debug" type="boolean"> + <default>False</default> + + <description> + <para> + Some debugging messages can be sent to the control channel if you wish. They're probably not really useful for you, unless you're doing some development on BitlBee. + </para> + </description> + </bitlbee-setting> + + <bitlbee-setting name="to_char" type="string"> + <default>": "</default> + + <description> + + <para> + It's customary that messages meant for one specific person on an IRC channel are prepended by his/her alias followed by a colon ':'. BitlBee does this by default. If you prefer a different character, you can set it using <emphasis>set to_char</emphasis>. + </para> + + <para> + Please note that this setting is only used for incoming messages. For outgoing messages you can use ':' (colon) or ',' to separate the destination nick from the message, and this is not configurable. + </para> + </description> + </bitlbee-setting> + + <bitlbee-setting name="typing_notice" type="boolean"> + <default>False</default> + + <description> + <para> + Sends you a /notice when a user starts typing a message (if the protocol supports it, MSN for example). This is a bug, not a feature. (But please don't report it.. ;-) You don't want to use it. Really. In fact the typing-notification is just one of the least useful 'innovations' ever. It's just there because some guy will probably ask me about it anyway. ;-) + </para> + </description> + </bitlbee-setting> + + <bitlbee-setting name="ops" type="string"> + <default>both</default> + <possible-values>both, root, user, none</possible-values> + + <description> + <para> + Some people prefer themself and root to have operator status in #bitlbee, other people don't. You can change these states using this setting. + </para> + + <para> + The value "both" means both user and root get ops. "root" means, well, just root. "user" means just the user. "none" means nobody will get operator status. + </para> + </description> + </bitlbee-setting> + + <bitlbee-setting name="away_devoice" type="boolean"> + <default>True</default> + + <description> + <para> + With this option enabled, the root user devoices people when they go away (just away, not offline) and gives the voice back when they come back. You might dislike the voice-floods you'll get if your contact list is huge, so this option can be disabled. + </para> + </description> + </bitlbee-setting> + + <bitlbee-setting name="handle_unknown" type="string"> + <default>root</default> + <possible-values>root, add, add_private, add_channel, ignore</possible-values> + + <description> + <para> + Messages from unknown users are echoed like this by default: + </para> + + <ircexample> + <ircline nick="root">Unknown message from handle 3137137:</ircline> + <ircline nick="root">j0000! 1 4m l33t h4x0r! kill me!</ircline> + </ircexample> + + <para> + If you want this lame user to be added automatically, you can set this setting to "add". If you prefer to ignore messages from people you don't know, you can set this one to "ignore". "add_private" and "add_channel" are like add, but you can use them to make messages from unknown buddies appear in the channel instead of a query window. + </para> + + <note> + <para> + Auto-added users aren't added to your real contact list. This is because you don't want the user to get authorization requests. So when you restart BitlBee, the auto-added user will be gone. If you want to keep the person in your buddy-list, you have to fixate the add using the <emphasis>add</emphasis> command. + </para> + </note> + </description> + + </bitlbee-setting> + + <bitlbee-setting name="auto_connect" type="boolean"> + <default>True</default> + + <description> + <para> + With this option enabled, when you identify BitlBee will automatically connect to your accounts, with this disabled it will not do this. + </para> + </description> + </bitlbee-setting> + + <bitlbee-setting name="auto_reconnect" type="boolean"> + <default>False</default> + + <description> + <para> + If an IM-connections breaks, you're supposed to bring it back up yourself. Having BitlBee do this automatically might not always be a good idea, for several reasons. If you want the connections to be restored automatically, you can enable this setting. + </para> + + <para> + See also the <emphasis>auto_reconnect_delay</emphasis> setting. + </para> + </description> + + </bitlbee-setting> + + <bitlbee-setting name="auto_reconnect_delay" type="integer"> + <default>300</default> + + <description> + + <para> + Tell BitlBee after how many seconds it should attempt to bring an IM-connection back up after a crash. It's not a good idea to set this value very low, it will cause too much useless traffic when an IM-server is down for a few hours. + </para> + + <para> + See also the <emphasis>auto_reconnect</emphasis> setting. + </para> + </description> + </bitlbee-setting> + + <bitlbee-setting name="buddy_sendbuffer" type="boolean"> + <default>False</default> + + <description> + + <para> + By default, when you send a message to someone, BitlBee forwards this message to the user immediately. When you paste a large number of lines, the lines will be sent in separate messages, which might not be very nice to read. If you enable this setting, BitlBee will buffer your messages and wait for more data. + </para> + + <para> + Using the <emphasis>buddy_sendbuffer_delay</emphasis> setting you can specify the number of seconds BitlBee should wait for more data before the complete message is sent. + </para> + + <para> + Please note that if you remove a buddy from your list (or if the connection to that user drops) and there's still data in the buffer, this data will be lost. BitlBee will not try to send the message to the user in those cases. + </para> + </description> + + </bitlbee-setting> + + <bitlbee-setting name="buddy_sendbuffer_delay" type="integer"> + <description> + + <para> + Tell BitlBee after how many seconds a buffered message should be sent. + </para> + + <para> + See also the <emphasis>buddy_sendbuffer</emphasis> setting. + </para> + </description> + + </bitlbee-setting> + + <bitlbee-setting name="default_target" type="string"> + <default>root</default> + <possible-values>root, last</possible-values> + + <description> + <para> + With this value set to <emphasis>root</emphasis>, lines written in the control channel without any nickname in front of them will be interpreted as commands. If you want BitlBee to send those lines to the last person you addressed in the control channel, set this to <emphasis>last</emphasis>. + </para> + </description> + + </bitlbee-setting> + + <bitlbee-setting name="display_namechanges" type="boolean"> + <default>False</default> + + <para> + With this option enabled, root will inform you when someone in your buddy list changes his/her "friendly name". + </para> + </bitlbee-setting> + + <bitlbee-setting name="password" type="string"> + <description> + <para> + Use this setting to change your "NickServ" password. + </para> + </description> + </bitlbee-setting> + + <bitlbee-setting name="query_order" type="string"> + <default>lifo</default> + <possible-values>lifo, fifo</possible-values> + + <description> + <para> + This changes the order in which the questions from root (usually authorization requests from buddies) should be answered. When set to <emphasis>lifo</emphasis>, BitlBee immediately displays all new questions and they should be answered in reverse order. When this is set to <emphasis>fifo</emphasis>, BitlBee displays the first question which comes in and caches all the others until you answer the first one. + </para> + + <para> + Although the <emphasis>fifo</emphasis> setting might sound more logical (and used to be the default behaviour in older BitlBee versions), it turned out not to be very convenient for many users when they missed the first question (and never received the next ones). + </para> + </description> + </bitlbee-setting> + + <bitlbee-setting name="lcnicks" type="boolean"> + <default>True</default> + + <description> + <para> + Hereby you can change whether you want all lower case nick names or leave the case as it intended by your peer. + </para> + </description> + + </bitlbee-setting> + + <bitlbee-command name="rename"> + <short-description>Rename (renick) a buddy</short-description> + <syntax>rename <oldnick> <newnick></syntax> + + <description> + <para> + Renick a user in your buddy list. Very useful, in fact just very important, if you got a lot of people with stupid account names (or hard ICQ numbers). + </para> + </description> + + <ircexample> + <ircline nick="itsme">rename itsme_ you</ircline> + <ircaction nick="itsme_">is now known as <emphasis>you</emphasis></ircaction> + </ircexample> + + </bitlbee-command> + + <bitlbee-command name="yes"> + <short-description>Accept a request</short-description> + <syntax>yes [<number>]</syntax> + + <description> + <para> + Sometimes an IM-module might want to ask you a question. (Accept this user as your buddy or not?) To accept a question, use the <emphasis>yes</emphasis> command. + </para> + + <para> + By default, this answers the first unanswered question. You can also specify a different question as an argument. You can use the <emphasis>qlist</emphasis> command for a list of questions. + </para> + </description> + + </bitlbee-command> + + <bitlbee-command name="no"> + <short-description>Deny a request</short-description> + <syntax>no [<number>]</syntax> + + <description> + <para> + Sometimes an IM-module might want to ask you a question. (Accept this user as your buddy or not?) To reject a question, use the <emphasis>no</emphasis> command. + </para> + + <para> + By default, this answers the first unanswered question. You can also specify a different question as an argument. You can use the <emphasis>qlist</emphasis> command for a list of questions. + </para> + </description> + </bitlbee-command> + + <bitlbee-command name="qlist"> + <short-description>List all the unanswered questions root asked</short-description> + <syntax>qlist</syntax> + + <description> + <para> + This gives you a list of all the unanswered questions from root. + </para> + </description> + + </bitlbee-command> + + <bitlbee-command name="register"> + <short-description>Register yourself</short-description> + <syntax>register <password></syntax> + + <description> + <para> + BitlBee can save your settings so you won't have to enter all your IM passwords every time you log in. If you want the Bee to save your settings, use the <emphasis>register</emphasis> command. + </para> + + <para> + Please do pick a secure password, don't just use your nick as your password. Please note that IRC is not an encrypted protocol, so the passwords still go over the network in plaintext. Evil people with evil sniffers will read it all. (So don't use your root password.. ;-) + </para> + + <para> + To identify yourself in later sessions, you can use the <emphasis>identify</emphasis> command. + </para> + </description> + + </bitlbee-command> + + <bitlbee-command name="identify"> + <syntax>identify <password></syntax> + <short-description>Identify yourself with your password</short-description> + + <description> + <para> + BitlBee saves all your settings (contacts, accounts, passwords) on-server. To prevent other users from just logging in as you and getting this information, you'll have to identify yourself with your password. You can register this password using the <emphasis>register</emphasis> command. + </para> + + <para> + Once you're registered, you can change your password using <emphasis>set password <password></emphasis>. + </para> + </description> + </bitlbee-command> + + <bitlbee-command name="drop"> + <syntax>drop <password></syntax> + <short-description>Drop your account</short-description> + + <description> + <para> + Drop your BitlBee registration. Your account files will be removed and your password will be forgotten. For obvious security reasons, you have to specify your NickServ password to make this command work. + </para> + </description> + </bitlbee-command> + + <bitlbee-command name="blist"> + <syntax>blist [all|online|offline|away]</syntax> + <short-description>List all the buddies in your contact list</short-description> + + <description> + <para> + You can get a better readable buddy list using the <emphasis>blist</emphasis> command. If you want a complete list (including the offline users) you can use the <emphasis>all</emphasis> argument. + </para> + </description> + + </bitlbee-command> + + <bitlbee-command name="nick"> + <short-description>Change friendly name, nick</short-description> + <syntax>nick <connection> [<new nick>]</syntax> + <syntax>nick</syntax> + + <description> + <para> + This command allows to set the friendly name of an im account. If no new name is specified the command will report the current name. When the name contains spaces, don't forget to quote the whole nick in double quotes. Currently this command is only supported by the MSN protocol. + </para> + </description> + + <ircexample> + <ircline nick="wouter">nick 1 "Wouter Paesen"</ircline> + <ircline nick="root">Setting your name on connection 1 to `Wouter Paesen'</ircline> + </ircexample> + + </bitlbee-command> + + <bitlbee-command name="import_buddies"> + <short-description>Copy local buddy list to server (normally only needed when upgrading)</short-description> + <syntax>import_buddies <connection> [clear]</syntax> + + <description> + <para> + This command copies the locally stored buddy list to the server. This command exists for upgrading purposes. Previous versions of BitlBee didn't support server-side buddy lists for ICQ, so the list was stored locally. + </para> + + <para> + Since version 0.91 however, server-side contact lists are supported for all protocols, so the local list is now ignored. When upgrading from an older BitlBee to version 0.91, you might need this command to get your buddy list back. + </para> + + <para> + The only argument this command needs is your ICQ account identification. If your serverside buddy list contains some old buddies you don't want anymore, you can pass <emphasis>clear</emphasis> as a second argument. + </para> + + <para> + After giving this command, you have to wait for a while before all the adds are handled, because of ICQ's rate limiting. If your buddy list is very large and the ICQ server starts complaining, you might have to reconnect and enter this command again. + </para> + </description> + + </bitlbee-command> +</chapter> diff --git a/doc/user-guide/docbook.xsl b/doc/user-guide/docbook.xsl new file mode 100644 index 00000000..cfb27702 --- /dev/null +++ b/doc/user-guide/docbook.xsl @@ -0,0 +1,136 @@ +<?xml version='1.0'?> +<!-- + Convert BitlBee XML documentation to DocBook + (C) 2004 Jelmer Vernooij +--> +<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" + xmlns:exsl="http://exslt.org/common" + version="1.1" + extension-element-prefixes="exsl"> + + <xsl:output method="xml" encoding="UTF-8" doctype-public="-//OASIS//DTD DocBook XML V4.2//EN" indent="yes" doctype-system="http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"/> + + <xsl:strip-space elements="*"/> + + <xsl:template match="ircline"> + <xsl:element name="prompt"><xsl:text>< </xsl:text><xsl:value-of select="@nick"/><xsl:text>> </xsl:text></xsl:element> + <xsl:element name="userinput"><xsl:value-of select="normalize-space(.)"/></xsl:element><xsl:text> </xsl:text> + </xsl:template> + + <xsl:template match="ircaction"> + <xsl:text> * </xsl:text><xsl:value-of select="@nick"/><xsl:value-of select="normalize-space(.)"/><xsl:text> </xsl:text> + </xsl:template> + + <xsl:template match="ircexample"> + <xsl:element name="screen"> + <xsl:for-each select="ircline|ircaction"> + <xsl:apply-templates select="."/> + </xsl:for-each> + </xsl:element> + </xsl:template> + + + <!-- This is needed to copy content unchanged --> + <xsl:template match="@*|node()"> + <xsl:copy> + <xsl:apply-templates select="@*|node()"/> + </xsl:copy> + </xsl:template> + + <xsl:template name="subcmd-list"> + <xsl:if test="bitlbee-command != ''"> + <xsl:element name="variablelist"> + <xsl:for-each select="bitlbee-command"> + <xsl:element name="varlistentry"> + <xsl:element name="term"> + <xsl:value-of select="@name"/> + </xsl:element> + <xsl:element name="listitem"> + <xsl:element name="para"> + <xsl:value-of select="short-description"/> + </xsl:element> + </xsl:element> + </xsl:element> + </xsl:for-each> + </xsl:element> + </xsl:if> + </xsl:template> + + <xsl:template match="command-list"> + <xsl:call-template name="subcmd-list"/> + </xsl:template> + + <xsl:template match="bitlbee-setting"> + <xsl:element name="sect1"> + <xsl:attribute name="id"> + <xsl:text>set_</xsl:text> + <xsl:value-of select="@name"/> + </xsl:attribute> + <xsl:element name="title"><xsl:value-of select="@name"/></xsl:element> + + <xsl:element name="simplelist"> + <xsl:element name="member"> + <xsl:text>Type: </xsl:text><xsl:value-of select="@type"/> + </xsl:element> + </xsl:element> + + <xsl:for-each select="description/para"> + <xsl:apply-templates select="."/> + </xsl:for-each> + + </xsl:element> + </xsl:template> + + <xsl:template name="cmd"> + <xsl:variable name="thiscmd"><xsl:value-of select="$prefix"/><xsl:value-of select="@name"/></xsl:variable> + <xsl:attribute name="id"> + <xsl:text>cmd_</xsl:text> + <xsl:value-of select="translate($thiscmd,' ','_')"/> + </xsl:attribute> + <xsl:element name="title"><xsl:value-of select="$thiscmd"/> + <xsl:if test="short-description"> + <xsl:text> - </xsl:text> + <xsl:value-of select="short-description"/> + </xsl:if> + </xsl:element> + + <xsl:element name="formalpara"> + <xsl:element name="title"><xsl:text>Syntax:</xsl:text></xsl:element> + <xsl:element name="para"> + <xsl:element name="programlisting"> + <xsl:for-each select="syntax"> + <xsl:value-of select="normalize-space(.)"/><xsl:text> </xsl:text> + </xsl:for-each> + </xsl:element> + </xsl:element> + </xsl:element> + + <xsl:for-each select="description/para"> + <xsl:apply-templates select="."/> + </xsl:for-each> + + <xsl:for-each select="ircexample"> + <xsl:apply-templates select="."/> + </xsl:for-each> + + <!--<xsl:call-template name="subcmd-list"/>--> + + <xsl:for-each select="bitlbee-command"> + <xsl:element name="sect2"> + <xsl:call-template name="cmd"> + <xsl:with-param name="prefix"><xsl:value-of select="$thiscmd"/><xsl:text> </xsl:text> + </xsl:with-param> + </xsl:call-template> + </xsl:element> + </xsl:for-each> + </xsl:template> + + <xsl:template match="bitlbee-command"> + <xsl:element name="sect1"> + <xsl:call-template name="cmd"> + <xsl:with-param name="prefix" select="''"/> + </xsl:call-template> + </xsl:element> + </xsl:template> + + </xsl:stylesheet> diff --git a/doc/user-guide/help.txt b/doc/user-guide/help.txt new file mode 100644 index 00000000..a14e3a99 --- /dev/null +++ b/doc/user-guide/help.txt @@ -0,0 +1,554 @@ +? +These are the available help subjects: + + quickstart - A short introduction into BitlBee + commands - All available commands and settings + away - About setting away states + smileys - A summary of some non-standard smileys you might find and fail to understand + groupchats - How to work with groupchats on BitlBee + +You can read more about them with help <subject> + +BitlBee is written by Wilmer van der Gaast together with Jelmer Vernooij, Maurits Dijkstra and others. Bug reports and other kinds of feedback can be sent by e-mail to wilmer@gaast.net. (There is no BitlBee mailing list) + +Or just join #BitlBee on OFTC (irc.oftc.net) (OFTC, *not* FreeNode! Some people accidentally joined #BitlBee on FreeNode already, which is just an empty channel) and flame us right in the face. :-) +% +?index +These are the available help subjects: + + quickstart - A short introduction into BitlBee + commands - All available commands and settings + away - About setting away states + smileys - A summary of some non-standard smileys you might find and fail to understand + groupchats - How to work with groupchats on BitlBee + +You can read more about them with help <subject> +% +?quickstart +Welcome to BitlBee, your IRC gateway to ICQ, MSN, AOL, Jabber and Yahoo Instant Messaging Systems. + +The center of BitlBee is the control channel, #bitlbee. Two users will always be there, you (where "you" is the nickname you are using) and the system user, root. + +You need register so that all your IM settings (passwords, contacts, etc) can be saved on the BitlBee server. It's important that you pick a good password so no one else can access your account. Register with this password using the register command: register <password> (without the brackets!). + +Be sure to remember your password. The next time you connect to the BitlBee server you will need to identify <password> so that you will be recognised and logged in to all the IM services automatically. + +When finished, type help quickstart2 to continue. +% +?quickstart2 +Step Two: Add and Connect To your IM Account(s). + +To add an account to the account list you will need to use the account add command: account add <protocol> <username> <password> [<server>]. + +For instance, suppose you have an ICQ account with UIN 72696705 with password QuickStart, you would: + +<you> account add oscar 72696705 QuickStart login.icq.com +<root> Account successfully added + +Other available IM protocols are jabber, msn, and yahoo. Oscar is the protocol used by ICQ and AOL. For oscar, you need to specify the IM-server as a fourth argument (for msn and yahoo there is no fourth argument). For AOL Instant Messenger, the server name is login.oscar.aol.com. For ICQ, the server name is login.icq.com. + +When you are finished adding your account(s) use the account on command to enable all your accounts, type help quickstart3 to continue. +% +?quickstart3 +Step Three: Managing Contact Lists: Rename + +For most protocols (currently MSN, Jabber, Yahoo and AOL) BitlBee can download the contact list automatically from the IM server and all the on-line users should appear in the control channel when you log in. + +BitlBee will convert names into irc-friendly form (for instance: tux@example.com will be given the nickname tux). If you have more than one person who would have the same name by this logic (for instance: tux@example.com and tux@bitlbee.org) the second one to log on will be tux_. The same is true if you have a tux log on to AOL and a tux log on from Yahoo. + +It would be easy to get these two mixed up, so BitlBee has a rename command to change the nickname into something more suitable: rename <oldnick> <newnick> + +<you> rename tux_ bitlbeetux +* tux_ is now known as bitlbeetux +<root> Nick successfully changed + +When finished, type help quickstart4 to continue. +% +?quickstart4 +Step Four: Managing Contact Lists: Add and Remove. + +Now you might want to add some contacts, to do this we will use the add command. It needs two arguments: a connection ID (which can be a number (try account list), protocol name or (part of) the screenname) and the user's handle. It is used in the following way: add <connection> <handle> + +<you> add 0 r2d2@example.com +* r2d2 has joined #bitlbee + +In this case r2d2 is online, since he/she joins the channel immediately. If the user is not online you will not see them join until they log on. + +Lets say you accidentally added r2d3@example.com rather than r2d2@example.com, or maybe you just want to remove a user from your list because you never talk to them. To remove a name you will want to use the remove command: remove <nick> + +When finished, type help quickstart5 to continue. +% +?quickstart5 +Step Five: Chatting. + +First of all, a person must be on your contact list for you to chat with them (unless it's a group chat, help groupchats for more). If someone not on your contact list sends you a message, simply add them to the proper account with the add command. Once they are on your list and online, you can chat with them in #bitlbee: + +<you> tux: hey, how's the weather down there? +<tux> you: a bit chilly! + +If you'd rather chat with them in a separate window use the /msg or /query command, just like you would for a private message in IRC. If you want to have messages automatically come up in private messages rather than in the #bitlbee channel, use the set private command: set private true (set private false to change back). + +You know the basics. If you want to get to know more about BitlBee, please type help quickstart6. +% +?quickstart6 +So you want more than just chatting? Or maybe you're just looking for a feature? + +You can type help set to learn more about the possible BitlBee user settings. Among these user settings you will find options for common issues, such as changing the charset, HTML stripping and automatic connecting (simply type set to see current user settings). + +For more subjects (like groupchats and away states), please type help index. + +If you're still looking for something, please visit us in #bitlbee on the OFTC network (you can connect via irc.bitlbee.org), or mail us your problem/suggestion. Good luck and enjoy the Bee! +% +?commands + * account - IM-account list maintenance + * add - Add a buddy to your contact list + * info - Request user information + * remove - Remove a buddy from your contact list + * block - Block someone + * allow - Unblock someone + * set - Miscellaneous settings + * help - BitlBee help system + * save - Save your account data + * rename - Rename (renick) a buddy + * yes - Accept a request + * no - Deny a request + * qlist - List all the unanswered questions root asked + * register - Register yourself + * identify - Identify yourself with your password + * drop - Drop your account + * blist - List all the buddies in your contact list + * nick - Change friendly name, nick + * import_buddies - Copy local buddy list to server (normally only needed when upgrading) +% +?account +Syntax: account <action> [<arguments>] + +Available actions: add, del, list, on, off. See help account <action> for more information. +% +?account add +Syntax: account add <protocol> <username> <password> [<server>] + +Adds an account on the given server with the specified protocol, username and password to the account list. Supported protocols right now are: Jabber, MSN, OSCAR (AIM/ICQ) and Yahoo. For more information about adding an account, see help account add <protocol>. +% +?account add jabber +Syntax: account add jabber <handle> <password> [<servertag>] + +Note that the servertag argument is optional. You only have to use it if the part after the @ in your handle isn't the hostname of your Jabber server, or if you want to use SSL/connect to a non-standard port number. The format is simple: [<servername>[:<portnumber>][:ssl]]. For example, this is how you can connect to Google Talk: + +Note that Google talk is SSL-only, but officially reachable over both port 5222 and 5223. However, for some people only port 5222 works, for some people only 5223. This is something you'll have to try out. + +Example: +<wilmer> account add jabber example@gmail.com hobbelmeeuw talk.google.com:5223:ssl +<root> Account successfully added +% +?account add msn +Syntax: account add msn <handle> <password> + +For MSN connections there are no special arguments. +% +?account add oscar +Syntax: account add oscar <handle> <password> [<servername>] + +Specifying a server is required for OSCAR, since OSCAR can be used for both ICQ- and AIM-connections. Although these days it's supposed to be possible to connect to ICQ via AIM-servers and vice versa, we like to stick with this separation for now. For ICQ connections, the servername is login.icq.com, for AIM connections it's login.oscar.aol.com. + +Example: +<wilmer> account add oscar 72696705 hobbelmeeuw login.icq.com +<root> Account successfully added +% +?account add yahoo +Syntax: account add yahoo <handle> <password> + +For Yahoo! connections there are no special arguments. +% +?account del +Syntax: account del <account id> + +This commands deletes an account from your account list. You should signoff the account before deleting it. + +The account ID can be a number (see account list), the protocol name or (part of) the screenname, as long as it matches only one connection. +% +?account on +Syntax: account on [<account id>] + +This command will try to log into the specified account. If no account is specified, BitlBee will log into all the accounts. (Including accounts awaiting a reconnection) + +The account ID can be a number (see account list), the protocol name or (part of) the screenname, as long as it matches only one connection. +% +?account off +Syntax: account off [<account id>] + +This command disconnects the connection for the specified account. If no account is specified, BitlBee will deactivate all active accounts. (Including accounts awaiting a reconnection) + +The account ID can be a number (see account list), the protocol name or (part of) the screenname, as long as it matches only one connection. +% +?account list +Syntax: account list + +This command gives you a list of all the accounts known by BitlBee, including the numbers you'll need for most account commands. +% +?add +Syntax: add <connection> <handle> [<nick>] + +Adds the given buddy at the specified connection to your buddy list. The account ID can be a number (see account list), the protocol name or (part of) the screenname, as long as it matches only one connection. + +If you want, you can also tell BitlBee what nick to give the new contact. Of course you can also use the rename command for that, but sometimes this might be more convenient. + +Example: +<ctrlsoft> add 3 gryp@jabber.org grijp +* grijp has joined #bitlbee +% +?info +Syntax: info <connection> <handle> +Syntax: info <nick> + +Requests IM-network-specific information about the specified user. The amount of information you'll get differs per protocol. For some protocols (ATM Yahoo! and MSN) it'll give you an URL which you can visit with a normal web browser to get the information. + +Example: +<ctrlsoft> info 0 72696705 +<root> User info - UIN: 72696705 Nick: Lintux First/Last name: Wilmer van der Gaast E-mail: lintux@lintux.cx +% +?remove +Syntax: remove <nick> + +Removes the specified nick from your buddy list. + +Example: +<ctrlsoft> remove gryp +* gryp has quit [Leaving...] +% +?block +Syntax: block <nick> +Syntax: block <connection> <handle> + +Puts the specified user on your ignore list. Either specify the user's nick when you have him/her in your contact list or a connection number and a user handle. +% +?allow +Syntax: allow <nick> +Syntax: allow <connection> <handle> + +Reverse of block. Unignores the specified user or user handle on specified connection. +% +?set +Syntax: set [<variable> [<value>]] + +Without any arguments, this command lists all the set variables. You can also specify a single argument, a variable name, to get that variable's value. To change this value, specify the new value as the second argument. + +To get more help information about a setting, try: + +Example: +<ctrlsoft> help set private +% +?help +Syntax: help [subject] + +This command gives you the help information you're reading right now. If you don't give any arguments, it'll give a short help index. +% +?save +Syntax: save + +This command saves all your nicks and accounts immediately. Handy if you have the autosave functionality disabled, or if you don't trust the program's stability... ;-) +% +?rename +Syntax: rename <oldnick> <newnick> + +Renick a user in your buddy list. Very useful, in fact just very important, if you got a lot of people with stupid account names (or hard ICQ numbers). + +Example: +<itsme> rename itsme_ you +* itsme_ is now known as you +% +?yes +Syntax: yes [<number>] + +Sometimes an IM-module might want to ask you a question. (Accept this user as your buddy or not?) To accept a question, use the yes command. + +By default, this answers the first unanswered question. You can also specify a different question as an argument. You can use the qlist command for a list of questions. +% +?no +Syntax: no [<number>] + +Sometimes an IM-module might want to ask you a question. (Accept this user as your buddy or not?) To reject a question, use the no command. + +By default, this answers the first unanswered question. You can also specify a different question as an argument. You can use the qlist command for a list of questions. +% +?qlist +Syntax: qlist + +This gives you a list of all the unanswered questions from root. +% +?register +Syntax: register <password> + +BitlBee can save your settings so you won't have to enter all your IM passwords every time you log in. If you want the Bee to save your settings, use the register command. + +Please do pick a secure password, don't just use your nick as your password. Please note that IRC is not an encrypted protocol, so the passwords still go over the network in plaintext. Evil people with evil sniffers will read it all. (So don't use your root password.. ;-) + +To identify yourself in later sessions, you can use the identify command. +% +?identify +Syntax: identify <password> + +BitlBee saves all your settings (contacts, accounts, passwords) on-server. To prevent other users from just logging in as you and getting this information, you'll have to identify yourself with your password. You can register this password using the register command. + +Once you're registered, you can change your password using set password <password>. +% +?drop +Syntax: drop <password> + +Drop your BitlBee registration. Your account files will be removed and your password will be forgotten. For obvious security reasons, you have to specify your NickServ password to make this command work. +% +?blist +Syntax: blist [all|online|offline|away] + +You can get a better readable buddy list using the blist command. If you want a complete list (including the offline users) you can use the all argument. +% +?nick +Syntax: nick <connection> [<new nick>] +Syntax: nick + +This command allows to set the friendly name of an im account. If no new name is specified the command will report the current name. When the name contains spaces, don't forget to quote the whole nick in double quotes. Currently this command is only supported by the MSN protocol. + +Example: +<wouter> nick 1 "Wouter Paesen" +<root> Setting your name on connection 1 to `Wouter Paesen' +% +?import_buddies +Syntax: import_buddies <connection> [clear] + +This command copies the locally stored buddy list to the server. This command exists for upgrading purposes. Previous versions of BitlBee didn't support server-side buddy lists for ICQ, so the list was stored locally. + +Since version 0.91 however, server-side contact lists are supported for all protocols, so the local list is now ignored. When upgrading from an older BitlBee to version 0.91, you might need this command to get your buddy list back. + +The only argument this command needs is your ICQ account identification. If your serverside buddy list contains some old buddies you don't want anymore, you can pass clear as a second argument. + +After giving this command, you have to wait for a while before all the adds are handled, because of ICQ's rate limiting. If your buddy list is very large and the ICQ server starts complaining, you might have to reconnect and enter this command again. +% +?set charset +Type: string +Default: iso8859-1 +Possible Values: you can get a list of all possible values by doing 'iconv -l' in a shell + +The charset setting enables you to use different character sets in BitlBee. These get converted to UTF-8 before sending and from UTF-8 when receiving. + +If you don't know what's the best value for this, at least iso8859-1 is the best choice for most Western countries. You can try to find what works best for you on http://czyborra.com/charsets/iso8859.html +% +?set private +Type: boolean +Default: True + +If value is true, messages from users will appear in separate query windows. If false, messages from users will appear in the control channel. + +This setting is remembered (during one session) per-user, this setting only changes the default state. This option takes effect as soon as you reconnect. +% +?set save_on_quit +Type: boolean +Default: True + +If enabled causes BitlBee to save all current settings and account details when user disconnects. This is enabled by default, and these days there's not really a reason to have it disabled anymore. +% +?set html +Type: string +Default: nostrip +Possible Values: strip, nostrip + +Determines what BitlBee should do with HTML in messages. If set to nostrip, HTML in messages will not be touched. If set to strip, all HTML will be stripped from messages. Unfortunately this sometimes strips too much. +% +?set debug +Type: boolean +Default: False + +Some debugging messages can be sent to the control channel if you wish. They're probably not really useful for you, unless you're doing some development on BitlBee. +% +?set to_char +Type: string +Default: ": " + +It's customary that messages meant for one specific person on an IRC channel are prepended by his/her alias followed by a colon ':'. BitlBee does this by default. If you prefer a different character, you can set it using set to_char. + +Please note that this setting is only used for incoming messages. For outgoing messages you can use ':' (colon) or ',' to separate the destination nick from the message, and this is not configurable. +% +?set typing_notice +Type: boolean +Default: False + +Sends you a /notice when a user starts typing a message (if the protocol supports it, MSN for example). This is a bug, not a feature. (But please don't report it.. ;-) You don't want to use it. Really. In fact the typing-notification is just one of the least useful 'innovations' ever. It's just there because some guy will probably ask me about it anyway. ;-) +% +?set ops +Type: string +Default: both +Possible Values: both, root, user, none + +Some people prefer themself and root to have operator status in #bitlbee, other people don't. You can change these states using this setting. + +The value "both" means both user and root get ops. "root" means, well, just root. "user" means just the user. "none" means nobody will get operator status. +% +?set away_devoice +Type: boolean +Default: True + +With this option enabled, the root user devoices people when they go away (just away, not offline) and gives the voice back when they come back. You might dislike the voice-floods you'll get if your contact list is huge, so this option can be disabled. +% +?set handle_unknown +Type: string +Default: root +Possible Values: root, add, add_private, add_channel, ignore + +Messages from unknown users are echoed like this by default: + +<root> Unknown message from handle 3137137: +<root> j0000! 1 4m l33t h4x0r! kill me! + +If you want this lame user to be added automatically, you can set this setting to "add". If you prefer to ignore messages from people you don't know, you can set this one to "ignore". "add_private" and "add_channel" are like add, but you can use them to make messages from unknown buddies appear in the channel instead of a query window. + +Auto-added users aren't added to your real contact list. This is because you don't want the user to get authorization requests. So when you restart BitlBee, the auto-added user will be gone. If you want to keep the person in your buddy-list, you have to fixate the add using the add command. +% +?set auto_connect +Type: boolean +Default: True + +With this option enabled, when you identify BitlBee will automatically connect to your accounts, with this disabled it will not do this. +% +?set auto_reconnect +Type: boolean +Default: False + +If an IM-connections breaks, you're supposed to bring it back up yourself. Having BitlBee do this automatically might not always be a good idea, for several reasons. If you want the connections to be restored automatically, you can enable this setting. + +See also the auto_reconnect_delay setting. +% +?set auto_reconnect_delay +Type: integer +Default: 300 + +Tell BitlBee after how many seconds it should attempt to bring an IM-connection back up after a crash. It's not a good idea to set this value very low, it will cause too much useless traffic when an IM-server is down for a few hours. + +See also the auto_reconnect setting. +% +?set buddy_sendbuffer +Type: boolean +Default: False + +By default, when you send a message to someone, BitlBee forwards this message to the user immediately. When you paste a large number of lines, the lines will be sent in separate messages, which might not be very nice to read. If you enable this setting, BitlBee will buffer your messages and wait for more data. + +Using the buddy_sendbuffer_delay setting you can specify the number of seconds BitlBee should wait for more data before the complete message is sent. + +Please note that if you remove a buddy from your list (or if the connection to that user drops) and there's still data in the buffer, this data will be lost. BitlBee will not try to send the message to the user in those cases. +% +?set buddy_sendbuffer_delay +Type: integer +Default: + +Tell BitlBee after how many seconds a buffered message should be sent. + +See also the buddy_sendbuffer setting. +% +?set default_target +Type: string +Default: root +Possible Values: root, last + +With this value set to root, lines written in the control channel without any nickname in front of them will be interpreted as commands. If you want BitlBee to send those lines to the last person you addressed in the control channel, set this to last. +% +?set display_namechanges +Type: boolean +Default: False +% +?set password +Type: string +Default: + +Use this setting to change your "NickServ" password. +% +?set query_order +Type: string +Default: lifo +Possible Values: lifo, fifo + +This changes the order in which the questions from root (usually authorization requests from buddies) should be answered. When set to lifo, BitlBee immediately displays all new questions and they should be answered in reverse order. When this is set to fifo, BitlBee displays the first question which comes in and caches all the others until you answer the first one. + +Although the fifo setting might sound more logical (and used to be the default behaviour in older BitlBee versions), it turned out not to be very convenient for many users when they missed the first question (and never received the next ones). +% +?set lcnicks +Type: boolean +Default: True + +Hereby you can change whether you want all lower case nick names or leave the case as it intended by your peer. +% +?misc +% +?smileys +All MSN smileys (except one) are case insensitive and work without the nose too. + + (Y) - Thumbs up + (N) - Thumbs down + (B) - Beer mug + (D) - Martini glass + (X) - Girl + (Z) - Boy + (6) - Devil smiley + :-[ - Vampire bat + (}) - Right hug + ({) - Left hug + (M) - MSN Messenger or Windows Messenger icon (think a BitlBee logo here ;) + :-S - Crooked smiley (Confused smiley) + :-$ - Embarrassed smiley + (H) - Smiley with sunglasses + :-@ - Angry smiley + (A) - Angel smiley + (L) - Red heart (Love) + (U) - Broken heart + (K) - Red lips (Kiss) + (G) - Gift with bow + (F) - Red rose + (W) - Wilted rose + (P) - Camera + (~) - Film strip + (T) - Telephone receiver + (@) - Cat face + (&) - Dog's head + (C) - Coffee cup + (I) - Light bulb + (S) - Half-moon (Case sensitive!) + (*) - Star + (8) - Musical eighth note + (E) - Envelope + (^) - Birthday cake + (O) - Clock + +This list was extracted from http://help.msn.com/!data/en_us/data/messengerv50.its51/$content$/EMOTICONS.HTM?H_APP=. +% +?groupchats +Since version 0.8x, BitlBee supports groupchats on the MSN and Yahoo! networks. This text will try to explain you how they work. + +As soon as someone invites you into a groupchat, you will be force-joined or invited (depending on the protocol) into a new virtual channel with all the people in there. You can leave the channel at any time, just like you would close the window in regular IM clients. Please note that root-commands don't work in groupchat channels, they only work in the control channel (or to root directly). + +Of course you can also create your own groupchats. Type help groupchats2 to see how. +% +?groupchats2 +If you want to start a groupchat with the person jim_msn in it, just join the channel #jim_msn. BitlBee will refuse to join you to the channel with that name, but it will create a new virtual channel with root, you and jim_msn in it. + +Of course a channel with only two people isn't really exciting yet. So the next step is to invite some other people to the channel. For this, you can use the /invite command of your IRC client. Please do keep in mind that all the people have to be on the same network and contact list! You can't invite Yahoo! buddies into an MSN groupchat. + +This is all you'll probably need to know. If you have any problems, please read help groupchats3. +% +?groupchats3 +Obviously the (numbered) channel names don't make a lot of sense. Problem is that groupchats usually don't have names at all in the IM-world, while IRC insists on a name. So BitlBee just generates something random, just don't pay attention to it. :-) + +Please also note that BitlBee doesn't support groupchats for all protocols yet. BitlBee will tell you so. Support for other protocols will hopefully come later. +% +?away +As you might've expected, you can just use the /away command in your IRC client to set an away-state. BitlBee supports most away-states supported by the protocols. + +Not all away states are supported by all protocols, and some protocols have different names for them. BitlBee will try to pick the best available alias from this list for every connection: + + - Away from computer, Away, Extended away + - NA, N/A, Not available + - Busy, Do not disturb, DND, Occupied + - Be right back, BRB + - On the phone, Phone, On phone + - Out to lunch, Lunch, Food + +So /away Food will set your state to "Out to lunch" on your MSN connection, and for most other connections the default, "Away" or "Away from computer" will be chosen. + +You can also add more information to your away message. Setting it to "Busy - Fixing BitlBee bugs" will set your IM-away-states to Busy, but your away message will be more descriptive for people on IRC. Protocols like Yahoo! and Jabber will also show this complete away message to your buddies. +% diff --git a/doc/user-guide/help.xml b/doc/user-guide/help.xml new file mode 100644 index 00000000..9de4fbad --- /dev/null +++ b/doc/user-guide/help.xml @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> + +<book id="BitlBee-Help" xmlns:xi="http://www.w3.org/2003/XInclude"> + +<preface id=""> +<title>BitlBee help system</title> + +<para> +These are the available help subjects: +</para> + +<variablelist> + <varlistentry><term>quickstart</term><listitem><para>A short introduction into BitlBee</para></listitem></varlistentry> + <varlistentry><term>commands</term><listitem><para>All available commands and settings</para></listitem></varlistentry> + <varlistentry><term>away</term><listitem><para>About setting away states</para></listitem></varlistentry> + <varlistentry><term>smileys</term><listitem><para>A summary of some non-standard smileys you might find and fail to understand</para></listitem></varlistentry> + <varlistentry><term>groupchats</term><listitem><para>How to work with groupchats on BitlBee</para></listitem></varlistentry> +</variablelist> + +<para> +You can read more about them with <emphasis>help <subject></emphasis> +</para> + +<para> +BitlBee is written by Wilmer van der Gaast together with Jelmer Vernooij, Maurits Dijkstra and others. Bug reports and other kinds of feedback can be sent by e-mail to <emphasis>wilmer@gaast.net</emphasis>. (There is no BitlBee mailing list) +</para> + +<para> +Or just join <emphasis>#BitlBee</emphasis> on OFTC (<emphasis>irc.oftc.net</emphasis>) (OFTC, *not* FreeNode! Some people accidentally joined #BitlBee on FreeNode already, which is just an empty channel) and flame us right in the face. :-) +</para> + +</preface> + +<chapter id="index"> +<title>Index</title> + +<para> +These are the available help subjects: +</para> + +<variablelist> + <varlistentry><term>quickstart</term><listitem><para>A short introduction into BitlBee</para></listitem></varlistentry> + <varlistentry><term>commands</term><listitem><para>All available commands and settings</para></listitem></varlistentry> + <varlistentry><term>away</term><listitem><para>About setting away states</para></listitem></varlistentry> + <varlistentry><term>smileys</term><listitem><para>A summary of some non-standard smileys you might find and fail to understand</para></listitem></varlistentry> + <varlistentry><term>groupchats</term><listitem><para>How to work with groupchats on BitlBee</para></listitem></varlistentry> +</variablelist> + +<para> +You can read more about them with <emphasis>help <subject></emphasis> +</para> + +</chapter> + +<xi:include href="quickstart.xml"/> +<xi:include href="commands.xml"/> +<xi:include href="misc.xml"/> + +</book> diff --git a/doc/user-guide/help.xsl b/doc/user-guide/help.xsl new file mode 100644 index 00000000..0eb1a88b --- /dev/null +++ b/doc/user-guide/help.xsl @@ -0,0 +1,156 @@ +<?xml version='1.0'?> +<!-- + Convert DocBook documentation to help.txt file used by bitlbee + (C) 2004 Jelmer Vernooij +--> +<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" + version="1.1"> + + <xsl:output method="text" encoding="iso-8859-1" standalone="yes"/> + <xsl:strip-space elements="*"/> + + <xsl:template match="text()"> + <xsl:if test="starts-with(.,' ') and preceding-sibling::* and + not(preceding-sibling::*[1]/node()[1][self::text() and contains(concat(.,'^$%'),' ^$%')])"> + <xsl:text> </xsl:text> + </xsl:if> + + <xsl:value-of select="normalize-space(.)"/> + <xsl:if test="contains(concat(.,'^$%'),' ^$%') and following-sibling::* and + not(following-sibling::*[1]/node()[1][self::text() and starts-with(.,' ')])"> + <xsl:text> </xsl:text> + </xsl:if> + </xsl:template> + + <xsl:template match="para"> + <xsl:apply-templates/><xsl:text> </xsl:text> + <xsl:if test="$extraparanewline = '1'"> + <xsl:text> </xsl:text> + </xsl:if> + </xsl:template> + + <xsl:template name="subject"> + <xsl:message><xsl:text>Processing: </xsl:text><xsl:value-of select="$id"/></xsl:message> + <xsl:text>?</xsl:text><xsl:value-of select="$id"/><xsl:text> </xsl:text> + + <xsl:for-each select="para|variablelist|simplelist|command-list|ircexample"> + <xsl:if test="title != ''"> + <xsl:value-of select="title"/><xsl:text> </xsl:text> + </xsl:if> + <xsl:apply-templates select="."/> + </xsl:for-each> + <xsl:text>% </xsl:text> + + <xsl:for-each select="sect1|sect2"> + <xsl:call-template name="subject"> + <xsl:with-param name="id" select="@id"/> + </xsl:call-template> + </xsl:for-each> + + <xsl:for-each select="bitlbee-command"> + <xsl:call-template name="cmd"> + <xsl:with-param name="prefix" select="''"/> + </xsl:call-template> + </xsl:for-each> + + <xsl:for-each select="bitlbee-setting"> + <xsl:message><xsl:text>Processing setting '</xsl:text><xsl:value-of select="@name"/><xsl:text>'</xsl:text></xsl:message> + <xsl:text>?set </xsl:text><xsl:value-of select="@name"/><xsl:text> </xsl:text> + <xsl:text>_b_Type:_b_ </xsl:text><xsl:value-of select="@type"/><xsl:text> </xsl:text> + <xsl:text>_b_Default:_b_ </xsl:text><xsl:value-of select="default"/><xsl:text> </xsl:text> + <xsl:if test="possible-values"> + <xsl:text>_b_Possible Values:_b_ </xsl:text><xsl:value-of select="possible-values"/><xsl:text> </xsl:text> + </xsl:if> + <xsl:text> </xsl:text> + <xsl:apply-templates select="description"/> + <xsl:text>% </xsl:text> + </xsl:for-each> + </xsl:template> + + <xsl:template match="command-list"> + <xsl:for-each select="../bitlbee-command"> + <xsl:text> * _b_</xsl:text><xsl:value-of select="@name"/><xsl:text>_b_ - </xsl:text><xsl:value-of select="short-description"/><xsl:text> </xsl:text> + </xsl:for-each> + <xsl:text> </xsl:text> + </xsl:template> + + <xsl:template match="preface|chapter|sect1|sect2"> + <xsl:call-template name="subject"> + <xsl:with-param name="id" select="@id"/> + </xsl:call-template> + </xsl:template> + + <xsl:template match="emphasis"> + <xsl:text>_b_</xsl:text> + <xsl:apply-templates/> + <xsl:text>_b_</xsl:text> + </xsl:template> + + <xsl:template match="book"> + <xsl:apply-templates/> + </xsl:template> + + <xsl:template match="variablelist"> + <xsl:for-each select="varlistentry"> + <xsl:text> _b_</xsl:text><xsl:value-of select="term"/><xsl:text>_b_ - </xsl:text><xsl:value-of select="listitem/para"/><xsl:text> </xsl:text> + </xsl:for-each> + <xsl:text> </xsl:text> + </xsl:template> + + <xsl:template match="simplelist"> + <xsl:for-each select="member"> + <xsl:text> - </xsl:text><xsl:apply-templates/><xsl:text> </xsl:text> + </xsl:for-each> + <xsl:text> </xsl:text> + </xsl:template> + + <xsl:template match="ircline"> + <xsl:text>_b_<</xsl:text><xsl:value-of select="@nick"/><xsl:text>>_b_ </xsl:text><xsl:value-of select="."/><xsl:text> </xsl:text> + </xsl:template> + + <xsl:template match="ircaction"> + <xsl:text>_b_* </xsl:text><xsl:value-of select="@nick"/><xsl:text>_b_ </xsl:text><xsl:value-of select="."/><xsl:text> </xsl:text> + </xsl:template> + + <xsl:template match="ircexample"> + <xsl:apply-templates/> + <xsl:text> </xsl:text> + </xsl:template> + + <xsl:template name="cmd"> + <xsl:variable name="thiscmd"><xsl:value-of select="$prefix"/><xsl:value-of select="@name"/></xsl:variable> + <xsl:message><xsl:text>Processing command '</xsl:text><xsl:value-of select="$thiscmd"/><xsl:text>'</xsl:text></xsl:message> + <xsl:text>?</xsl:text><xsl:value-of select="$thiscmd"/><xsl:text> </xsl:text> + <xsl:for-each select="syntax"> + <xsl:text>_b_Syntax:_b_ </xsl:text><xsl:value-of select="."/><xsl:text> </xsl:text> + </xsl:for-each> + <xsl:text> </xsl:text> + + <xsl:apply-templates select="description"/> + + <xsl:for-each select="ircexample"> + <xsl:text>_b_Example:_b_ </xsl:text> + <xsl:apply-templates select="."/> + </xsl:for-each> + + <!-- + <xsl:if test="bitlbee-command != ''"> + <xsl:text>Subcommands: </xsl:text> + <xsl:for-each select="bitlbee-command"> + <xsl:value-of select="@name"/><xsl:text>, </xsl:text> + </xsl:for-each> + <xsl:text> </xsl:text> + </xsl:if> + --> + + <xsl:text>% </xsl:text> + + <xsl:for-each select="bitlbee-command"> + <xsl:call-template name="cmd"> + <xsl:with-param name="prefix"><xsl:value-of select="$thiscmd"/><xsl:text> </xsl:text></xsl:with-param> + </xsl:call-template> + </xsl:for-each> + + </xsl:template> + +</xsl:stylesheet> diff --git a/doc/user-guide/misc.xml b/doc/user-guide/misc.xml new file mode 100644 index 00000000..f90ce538 --- /dev/null +++ b/doc/user-guide/misc.xml @@ -0,0 +1,131 @@ +<chapter id="misc"> +<title>Misc Stuff</title> + +<sect1 id="smileys"> +<title>Smileys</title> + +<para> +All MSN smileys (except one) are case insensitive and work without the nose too. +</para> + +<variablelist> + <varlistentry><term>(Y)</term><listitem><para>Thumbs up</para></listitem></varlistentry> + <varlistentry><term>(N)</term><listitem><para>Thumbs down</para></listitem></varlistentry> + <varlistentry><term>(B)</term><listitem><para>Beer mug</para></listitem></varlistentry> + <varlistentry><term>(D)</term><listitem><para>Martini glass</para></listitem></varlistentry> + <varlistentry><term>(X)</term><listitem><para>Girl</para></listitem></varlistentry> + <varlistentry><term>(Z)</term><listitem><para>Boy</para></listitem></varlistentry> + <varlistentry><term>(6)</term><listitem><para>Devil smiley</para></listitem></varlistentry> + <varlistentry><term>:-[</term><listitem><para>Vampire bat</para></listitem></varlistentry> + <varlistentry><term>(})</term><listitem><para>Right hug</para></listitem></varlistentry> + <varlistentry><term>({)</term><listitem><para>Left hug</para></listitem></varlistentry> + <varlistentry><term>(M)</term><listitem><para>MSN Messenger or Windows Messenger icon (think a BitlBee logo here ;)</para></listitem></varlistentry> + <varlistentry><term>:-S</term><listitem><para>Crooked smiley (Confused smiley)</para></listitem></varlistentry> + <varlistentry><term>:-$</term><listitem><para>Embarrassed smiley</para></listitem></varlistentry> + <varlistentry><term>(H)</term><listitem><para>Smiley with sunglasses</para></listitem></varlistentry> + <varlistentry><term>:-@</term><listitem><para>Angry smiley</para></listitem></varlistentry> + <varlistentry><term>(A)</term><listitem><para>Angel smiley</para></listitem></varlistentry> + <varlistentry><term>(L)</term><listitem><para>Red heart (Love)</para></listitem></varlistentry> + <varlistentry><term>(U)</term><listitem><para>Broken heart</para></listitem></varlistentry> + <varlistentry><term>(K)</term><listitem><para>Red lips (Kiss)</para></listitem></varlistentry> + <varlistentry><term>(G)</term><listitem><para>Gift with bow</para></listitem></varlistentry> + <varlistentry><term>(F)</term><listitem><para>Red rose</para></listitem></varlistentry> + <varlistentry><term>(W)</term><listitem><para>Wilted rose</para></listitem></varlistentry> + <varlistentry><term>(P)</term><listitem><para>Camera</para></listitem></varlistentry> + <varlistentry><term>(~)</term><listitem><para>Film strip</para></listitem></varlistentry> + <varlistentry><term>(T)</term><listitem><para>Telephone receiver</para></listitem></varlistentry> + <varlistentry><term>(@)</term><listitem><para>Cat face</para></listitem></varlistentry> + <varlistentry><term>(&)</term><listitem><para>Dog's head</para></listitem></varlistentry> + <varlistentry><term>(C)</term><listitem><para>Coffee cup</para></listitem></varlistentry> + <varlistentry><term>(I)</term><listitem><para>Light bulb</para></listitem></varlistentry> + <varlistentry><term>(S)</term><listitem><para>Half-moon (Case sensitive!)</para></listitem></varlistentry> + <varlistentry><term>(*)</term><listitem><para>Star</para></listitem></varlistentry> + <varlistentry><term>(8)</term><listitem><para>Musical eighth note</para></listitem></varlistentry> + <varlistentry><term>(E)</term><listitem><para>Envelope</para></listitem></varlistentry> + <varlistentry><term>(^)</term><listitem><para>Birthday cake</para></listitem></varlistentry> + <varlistentry><term>(O)</term><listitem><para>Clock</para></listitem></varlistentry> +</variablelist> + +<para> +This list was extracted from <ulink url="http://help.msn.com/!data/en_us/data/messengerv50.its51/$content$/EMOTICONS.HTM?H_APP=">http://help.msn.com/!data/en_us/data/messengerv50.its51/$content$/EMOTICONS.HTM?H_APP=</ulink>. +</para> + +</sect1> + +<sect1 id="groupchats"> +<title>Groupchats</title> +<para> +Since version 0.8x, BitlBee supports groupchats on the MSN and Yahoo! networks. This text will try to explain you how they work. +</para> + +<para> +As soon as someone invites you into a groupchat, you will be force-joined or invited (depending on the protocol) into a new virtual channel with all the people in there. You can leave the channel at any time, just like you would close the window in regular IM clients. Please note that root-commands don't work in groupchat channels, they only work in the control channel (or to root directly). +</para> + +<para> +Of course you can also create your own groupchats. Type <emphasis>help groupchats2</emphasis> to see how. +</para> + +</sect1> + +<sect1 id="groupchats2"> +<title>Creating groupchats</title> + +<para> +If you want to start a groupchat with the person <emphasis>jim_msn</emphasis> in it, just join the channel <emphasis>#jim_msn</emphasis>. BitlBee will refuse to join you to the channel with that name, but it will create a new virtual channel with root, you and jim_msn in it. +</para> + +<para> +Of course a channel with only two people isn't really exciting yet. So the next step is to invite some other people to the channel. For this, you can use the <emphasis>/invite</emphasis> command of your IRC client. Please do keep in mind that all the people have to be on the same network and contact list! You can't invite Yahoo! buddies into an MSN groupchat. +</para> + +<para> +This is all you'll probably need to know. If you have any problems, please read <emphasis>help groupchats3</emphasis>. +</para> + +</sect1> + +<sect1 id="groupchats3"> +<title>Groupchat channel names</title> + +<para> +Obviously the (numbered) channel names don't make a lot of sense. Problem is that groupchats usually don't have names at all in the IM-world, while IRC insists on a name. So BitlBee just generates something random, just don't pay attention to it. :-) +</para> + +<para> +Please also note that BitlBee doesn't support groupchats for all protocols yet. BitlBee will tell you so. Support for other protocols will hopefully come later. +</para> + +</sect1> + +<sect1 id="away"> +<title>Away states</title> + +<para> +As you might've expected, you can just use the <emphasis>/away</emphasis> command in your IRC client to set an away-state. BitlBee supports most away-states supported by the protocols. +</para> + +<para> +Not all away states are supported by all protocols, and some protocols have different names for them. BitlBee will try to pick the best available alias from this list for every connection: +</para> + +<simplelist> + <member>Away from computer, Away, Extended away</member> + <member>NA, N/A, Not available</member> + <member>Busy, Do not disturb, DND, Occupied</member> + <member>Be right back, BRB</member> + <member>On the phone, Phone, On phone</member> + <member>Out to lunch, Lunch, Food</member> +</simplelist> + +<para> +So <emphasis>/away Food</emphasis> will set your state to "Out to lunch" on your MSN connection, and for most other connections the default, "Away" or "Away from computer" will be chosen. +</para> + +<para> +You can also add more information to your away message. Setting it to "Busy - Fixing BitlBee bugs" will set your IM-away-states to Busy, but your away message will be more descriptive for people on IRC. Protocols like Yahoo! and Jabber will also show this complete away message to your buddies. +</para> + +</sect1> + +</chapter> diff --git a/doc/user-guide/quickstart.xml b/doc/user-guide/quickstart.xml new file mode 100644 index 00000000..9a8b8770 --- /dev/null +++ b/doc/user-guide/quickstart.xml @@ -0,0 +1,163 @@ +<chapter id="quickstart"> +<title>Quickstart</title> + +<para> +Welcome to BitlBee, your IRC gateway to ICQ, MSN, AOL, Jabber and Yahoo Instant Messaging Systems. +</para> + +<para> +The center of BitlBee is the control channel, <emphasis>#bitlbee</emphasis>. Two users will always be there, <emphasis>you</emphasis> (where "you" is the nickname you are using) and the system user, <emphasis>root</emphasis>. +</para> + +<para> +You need register so that all your IM settings (passwords, contacts, etc) can be saved on the BitlBee server. It's important that you pick a good password so no one else can access your account. Register with this password using the <emphasis>register</emphasis> command: <emphasis>register <password></emphasis> (without the brackets!). +</para> + +<para> +Be sure to remember your password. The next time you connect to the BitlBee server you will need to <emphasis>identify <password></emphasis> so that you will be recognised and logged in to all the IM services automatically. +</para> + +<para> +When finished, type <emphasis>help quickstart2</emphasis> to continue. +</para> + +<sect1 id="quickstart2"> +<title>Add and Connect To your IM Account(s)</title> +<!-- quickstart2 --> +<para> +<emphasis>Step Two: Add and Connect To your IM Account(s).</emphasis> +</para> + +<para> +To add an account to the account list you will need to use the <emphasis>account add</emphasis> command: <emphasis>account add <protocol> <username> <password> [<server>]</emphasis>. +</para> + +<para> +For instance, suppose you have an ICQ account with UIN <emphasis>72696705</emphasis> with password <emphasis>QuickStart</emphasis>, you would: +</para> + +<ircexample> + <ircline nick="you">account add oscar 72696705 QuickStart login.icq.com</ircline> + <ircline nick="root">Account successfully added</ircline> +</ircexample> + +<para> +Other available IM protocols are jabber, msn, and yahoo. Oscar is the protocol used by ICQ and AOL. For oscar, you need to specify the IM-server as a fourth argument (for msn and yahoo there is no fourth argument). For AOL Instant Messenger, the server name is <emphasis>login.oscar.aol.com</emphasis>. For ICQ, the server name is <emphasis>login.icq.com</emphasis>. +</para> + +<para> +When you are finished adding your account(s) use the <emphasis>account on</emphasis> command to enable all your accounts, type <emphasis>help quickstart3</emphasis> to continue. +</para> + +</sect1> + +<sect1 id="quickstart3"> +<title>Managing Contact Lists: Rename</title> + +<!--quickstart3--> +<para> +<emphasis>Step Three: Managing Contact Lists: Rename</emphasis> +</para> + +<para> +For most protocols (currently MSN, Jabber, Yahoo and AOL) BitlBee can download the contact list automatically from the IM server and all the on-line users should appear in the control channel when you log in. +</para> + +<para> +BitlBee will convert names into irc-friendly form (for instance: tux@example.com will be given the nickname tux). If you have more than one person who would have the same name by this logic (for instance: tux@example.com and tux@bitlbee.org) the second one to log on will be tux_. The same is true if you have a tux log on to AOL and a tux log on from Yahoo. +</para> + +<para> +It would be easy to get these two mixed up, so BitlBee has a <emphasis>rename</emphasis> command to change the nickname into something more suitable: <emphasis>rename <oldnick> <newnick></emphasis> +</para> + +<ircexample> + <ircline nick="you">rename tux_ bitlbeetux</ircline> + <ircaction nick="tux_">is now known as <emphasis>bitlbeetux</emphasis></ircaction> + <ircline nick="root">Nick successfully changed</ircline> +</ircexample> + +<para> +When finished, type <emphasis>help quickstart4</emphasis> to continue. +</para> + +</sect1> + +<sect1 id="quickstart4"> +<title>Step Four: Managing Contact Lists: Add and Remove.</title> + +<para> +<emphasis>Step Four: Managing Contact Lists: Add and Remove.</emphasis> +</para> + +<para> +Now you might want to add some contacts, to do this we will use the <emphasis>add</emphasis> command. It needs two arguments: a connection ID (which can be a number (try <emphasis>account list</emphasis>), protocol name or (part of) the screenname) and the user's handle. It is used in the following way: <emphasis>add <connection> <handle></emphasis> +</para> + +<ircexample> + <ircline nick="you">add 0 r2d2@example.com</ircline> + <ircaction nick="r2d2"> has joined <emphasis>#bitlbee</emphasis></ircaction> +</ircexample> + +<para> +In this case r2d2 is online, since he/she joins the channel immediately. If the user is not online you will not see them join until they log on. +</para> + +<para> +Lets say you accidentally added r2d3@example.com rather than r2d2@example.com, or maybe you just want to remove a user from your list because you never talk to them. To remove a name you will want to use the <emphasis>remove</emphasis> command: <emphasis>remove <nick></emphasis> +</para> + +<para> +When finished, type <emphasis>help quickstart5</emphasis> to continue. +</para> + +</sect1> + +<sect1 id="quickstart5"> +<title>Chatting</title> + +<para> +<emphasis>Step Five: Chatting.</emphasis> +</para> + +<para> +First of all, a person must be on your contact list for you to chat with them (unless it's a group chat, <emphasis>help groupchats</emphasis> for more). If someone not on your contact list sends you a message, simply add them to the proper account with the <emphasis>add</emphasis> command. Once they are on your list and online, you can chat with them in #bitlbee: +</para> + +<ircexample> + <ircline nick="you">tux: hey, how's the weather down there?</ircline> + <ircline nick="tux"> you: a bit chilly!</ircline> +</ircexample> + +<para> +If you'd rather chat with them in a separate window use the <emphasis>/msg</emphasis> or <emphasis>/query</emphasis> command, just like you would for a private message in IRC. If you want to have messages automatically come up in private messages rather than in the #bitlbee channel, use the <emphasis>set private</emphasis> command: <emphasis>set private true</emphasis> (<emphasis>set private false</emphasis> to change back). +</para> + +<para> +You know the basics. If you want to get to know more about BitlBee, please type <emphasis>help quickstart6</emphasis>. +</para> + +</sect1> + +<sect1 id="quickstart6"> +<title>Further Resources</title> + +<para> +<emphasis>So you want more than just chatting? Or maybe you're just looking for a feature?</emphasis> +</para> + +<para> +You can type <emphasis>help set</emphasis> to learn more about the possible BitlBee user settings. Among these user settings you will find options for common issues, such as changing the charset, HTML stripping and automatic connecting (simply type <emphasis>set</emphasis> to see current user settings). +</para> + +<para> +For more subjects (like groupchats and away states), please type <emphasis>help index</emphasis>. +</para> + +<para> +If you're still looking for something, please visit us in #bitlbee on the OFTC network (you can connect via irc.bitlbee.org), or mail us your problem/suggestion. Good luck and enjoy the Bee! +</para> + +</sect1> + +</chapter> diff --git a/doc/user-guide/user-guide.html b/doc/user-guide/user-guide.html new file mode 100644 index 00000000..5d1fb46a --- /dev/null +++ b/doc/user-guide/user-guide.html @@ -0,0 +1,446 @@ +<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>BitlBee User Guide</title><meta name="generator" content="DocBook XSL Stylesheets V1.68.1"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="book" lang="en"><div class="titlepage"><div><div><h1 class="title"><a name="BitlBee-User-Guide"></a>BitlBee User Guide</h1></div><div><div class="author"><h3 class="author"><span class="firstname">Jelmer</span> <span class="surname">Vernooij</span></h3></div></div><div><div class="author"><h3 class="author"><span class="firstname">Wilmer</span> <span class="surname">van der Gaast</span></h3></div></div><div><div class="author"><h3 class="author"><span class="firstname">Sjoerd</span> <span class="surname">Hemminga</span></h3></div></div><div><p class="releaseinfo"> + This is the initial release of the BitlBee User Guide. + </p></div><div><div class="legalnotice"><a name="legalnotice"></a><p> + Permission is granted to copy, distribute and/or modify this + document under the terms of the <a href="gnome-help:fdl" target="_top"><em class="citetitle">GNU Free Documentation + License</em></a>, Version 1.1 or any later version + published by the Free Software Foundation with no Invariant + Sections, no Front-Cover Texts, and no Back-Cover Texts. You + may obtain a copy of the <em class="citetitle">GNU Free Documentation + License</em> from the Free Software Foundation by + visiting <a href="http://www.fsf.org" target="_top">their + Web site</a> or by writing to: Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. + </p></div></div></div><hr></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="chapter"><a href="#Installation">1. Installation</a></span></dt><dd><dl><dt><span class="sect1"><a href="#id4683446">Downloading the package</a></span></dt><dt><span class="sect1"><a href="#id4683474">Compiling</a></span></dt><dt><span class="sect1"><a href="#id4683726">Configuration</a></span></dt></dl></dd><dt><span class="chapter"><a href="#Usage">2. Usage</a></span></dt><dd><dl><dt><span class="sect1"><a href="#id4683835">Connecting to the server</a></span></dt><dt><span class="sect1"><a href="#id4683848">The #bitlbee control channel</a></span></dt><dt><span class="sect1"><a href="#id4683867">Talking to people</a></span></dt></dl></dd><dt><span class="chapter"><a href="#Support">3. Support</a></span></dt><dd><dl><dt><span class="sect1"><a href="#id4683912">BitlBee is beta software</a></span></dt><dt><span class="sect1"><a href="#id4683926">Support channels</a></span></dt><dd><dl><dt><span class="sect2"><a href="#id4683931">The World Wide Web</a></span></dt><dt><span class="sect2"><a href="#id4683948">IRC</a></span></dt><dt><span class="sect2"><a href="#id4683960">Mailinglists</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#quickstart">4. Quickstart</a></span></dt><dd><dl><dt><span class="sect1"><a href="#quickstart2">Add and Connect To your IM Account(s)</a></span></dt><dt><span class="sect1"><a href="#quickstart3">Managing Contact Lists: Rename</a></span></dt><dt><span class="sect1"><a href="#quickstart4">Step Four: Managing Contact Lists: Add and Remove.</a></span></dt><dt><span class="sect1"><a href="#quickstart5">Chatting</a></span></dt><dt><span class="sect1"><a href="#quickstart6">Further Resources</a></span></dt></dl></dd><dt><span class="chapter"><a href="#commands">5. Bitlbee commands</a></span></dt><dd><dl><dt><span class="sect1"><a href="#cmd_account">account - IM-account list maintenance</a></span></dt><dd><dl><dt><span class="sect2"><a href="#cmd_account_add">account add</a></span></dt><dt><span class="sect2"><a href="#cmd_account_del">account del</a></span></dt><dt><span class="sect2"><a href="#cmd_account_on">account on</a></span></dt><dt><span class="sect2"><a href="#cmd_account_off">account off</a></span></dt><dt><span class="sect2"><a href="#cmd_account_list">account list</a></span></dt></dl></dd><dt><span class="sect1"><a href="#cmd_add">add - Add a buddy to your contact list</a></span></dt><dt><span class="sect1"><a href="#cmd_info">info - Request user information</a></span></dt><dt><span class="sect1"><a href="#cmd_remove">remove - Remove a buddy from your contact list</a></span></dt><dt><span class="sect1"><a href="#cmd_block">block - Block someone</a></span></dt><dt><span class="sect1"><a href="#cmd_allow">allow - Unblock someone</a></span></dt><dt><span class="sect1"><a href="#cmd_set">set - Miscellaneous settings</a></span></dt><dt><span class="sect1"><a href="#cmd_help">help - BitlBee help system</a></span></dt><dt><span class="sect1"><a href="#cmd_save">save - Save your account data</a></span></dt><dt><span class="sect1"><a href="#set_charset">charset</a></span></dt><dt><span class="sect1"><a href="#set_private">private</a></span></dt><dt><span class="sect1"><a href="#set_save_on_quit">save_on_quit</a></span></dt><dt><span class="sect1"><a href="#set_html">html</a></span></dt><dt><span class="sect1"><a href="#set_debug">debug</a></span></dt><dt><span class="sect1"><a href="#set_to_char">to_char</a></span></dt><dt><span class="sect1"><a href="#set_typing_notice">typing_notice</a></span></dt><dt><span class="sect1"><a href="#set_ops">ops</a></span></dt><dt><span class="sect1"><a href="#set_away_devoice">away_devoice</a></span></dt><dt><span class="sect1"><a href="#set_handle_unknown">handle_unknown</a></span></dt><dt><span class="sect1"><a href="#set_auto_connect">auto_connect</a></span></dt><dt><span class="sect1"><a href="#set_auto_reconnect">auto_reconnect</a></span></dt><dt><span class="sect1"><a href="#set_auto_reconnect_delay">auto_reconnect_delay</a></span></dt><dt><span class="sect1"><a href="#set_buddy_sendbuffer">buddy_sendbuffer</a></span></dt><dt><span class="sect1"><a href="#set_buddy_sendbuffer_delay">buddy_sendbuffer_delay</a></span></dt><dt><span class="sect1"><a href="#set_default_target">default_target</a></span></dt><dt><span class="sect1"><a href="#set_display_namechanges">display_namechanges</a></span></dt><dt><span class="sect1"><a href="#set_password">password</a></span></dt><dt><span class="sect1"><a href="#set_query_order">query_order</a></span></dt><dt><span class="sect1"><a href="#set_lcnicks">lcnicks</a></span></dt><dt><span class="sect1"><a href="#cmd_rename">rename - Rename (renick) a buddy</a></span></dt><dt><span class="sect1"><a href="#cmd_yes">yes - Accept a request</a></span></dt><dt><span class="sect1"><a href="#cmd_no">no - Deny a request</a></span></dt><dt><span class="sect1"><a href="#cmd_qlist">qlist - List all the unanswered questions root asked</a></span></dt><dt><span class="sect1"><a href="#cmd_register">register - Register yourself</a></span></dt><dt><span class="sect1"><a href="#cmd_identify">identify - Identify yourself with your password</a></span></dt><dt><span class="sect1"><a href="#cmd_drop">drop - Drop your account</a></span></dt><dt><span class="sect1"><a href="#cmd_blist">blist - List all the buddies in your contact list</a></span></dt><dt><span class="sect1"><a href="#cmd_nick">nick - Change friendly name, nick</a></span></dt><dt><span class="sect1"><a href="#cmd_import_buddies">import_buddies - Copy local buddy list to server (normally only needed when upgrading)</a></span></dt></dl></dd><dt><span class="chapter"><a href="#misc">6. Misc Stuff</a></span></dt><dd><dl><dt><span class="sect1"><a href="#smileys">Smileys</a></span></dt><dt><span class="sect1"><a href="#groupchats">Groupchats</a></span></dt><dt><span class="sect1"><a href="#groupchats2">Creating groupchats</a></span></dt><dt><span class="sect1"><a href="#groupchats3">Groupchat channel names</a></span></dt><dt><span class="sect1"><a href="#away">Away states</a></span></dt></dl></dd></dl></div><div class="chapter" lang="en"><div class="titlepage"><div><div><h2 class="title"><a name="Installation"></a>Chapter 1. Installation</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="sect1"><a href="#id4683446">Downloading the package</a></span></dt><dt><span class="sect1"><a href="#id4683474">Compiling</a></span></dt><dt><span class="sect1"><a href="#id4683726">Configuration</a></span></dt></dl></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id4683446"></a>Downloading the package</h2></div></div></div><p> +The latest BitlBee release is always available from <a href="http://www.bitlbee.org/" target="_top">http://www.bitlbee.org/</a>. +Download the package with your favorite program and unpack it: <span><strong class="command">tar +xvfz bitlbee-<version>.tar.gz</strong></span> where <version> is to be +replaced by the version number of the BitlBee you downloaded (e.g. 0.91). +</p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id4683474"></a>Compiling</h2></div></div></div><p> +BitlBee's build system has to be configured before compiling. The +<code class="filename">configure</code> script will do this for you. Just run +it, it'll set up with nice and hopefully well-working defaults. If you +want to change some settings, just try +<span><strong class="command">./configure --help</strong></span> and see what you can do. +</p><p>Some variables that might be of interest to the normal user:</p><div class="itemizedlist"><ul type="disc"><li><p>prefix, bindir, etcdir, mandir, datadir - The place where +all the BitlBee program files will be put. There's usually no reason to +specify them all separately, just specifying prefix (or keeping the default +<code class="filename">/usr/local/</code>) should be okay.</p></li><li><p>config - The place where BitlBee will save all the per-user +settings and buddy information. <code class="filename">/var/lib/bitlbee/</code> +is the default value.</p></li><li><p>msn, jabber, oscar, yahoo - By default, support for all +these IM-protocols (OSCAR is the protocol used by both ICQ and AIM) will +be compiled in. To make the binary a bit smaller, you can use these options +to leave out support for protocols you're not planning to use.</p></li><li><p>debug - Generate an unoptimized binary with debugging symbols, +mainly useful if you want to do some debugging or help us to track down a +problem.</p></li><li><p>strip - By default, unnecessary parts of the generated binary +will be stripped out to make it as small as possible. If you don't want this +(because it might cause problems on some platforms), set this to 0. +</p></li><li><p>flood - To secure your BitlBee server against flooding attacks, +you can use this option. It's not compiled in by default because it needs +more testing first.</p></li><li><p>ssl - The MSN and Jabber modules require an SSL library for +some of their tasks. BitlBee can use three different SSL libraries: GnuTLS, +mozilla-nss and OpenSSL. (OpenSSL is, however, a bit troublesome because of +licensing issues, so don't forget to read the information configure will +give you when you try to use OpenSSL!) By default, configure will try to +detect GnuTLS or mozilla-nss. If none of them can be found, it'll give up. +If you want BitlBee to use OpenSSL, you have to explicitly specify that. +</p></li></ul></div><p> +After running <code class="filename">configure</code>, you should run +<span><strong class="command">make</strong></span>. After that, run <span><strong class="command">make install</strong></span> as +root. +</p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id4683726"></a>Configuration</h2></div></div></div><p> +By default, BitlBee runs as the user nobody. You might want +to run it as a seperate user (some computers run named or apache as nobody). +</p><p> +Since BitlBee uses inetd, you should add the following line to <code class="filename">/etc/inetd.conf</code>: +</p><p> + </p><pre class="programlisting"> +6667 stream tcp nowait nobody /usr/local/sbin/bitlbee bitlbee +</pre><p> + </p><p> +Inetd has to be restarted after changing the configuration. Either +<span><strong class="command">killall -HUP inetd</strong></span> or +<span><strong class="command">/etc/init.d/inetd restart</strong></span> should do the job on most systems. +</p><p> +You might be one of the.. ehr, lucky people running an xinetd-powered distro. +<span><strong class="command">xinetd</strong></span> is quite different and they seem to be proud of that.. ;-) +Anyway, if you want BitlBee to work with <span><strong class="command">xinetd</strong></span>, just copy the +bitlbee.xinetd file to your /etc/xinetd.d/ directory (and probably edit it to suit +your needs). +</p><p> +You should create a directory where BitlBee can store it's data files. This +should be the directory named after the value 'CONFIG' in Makefile.settings. +The default is <code class="filename">/var/lib/bitlbee</code>, which can be created +with the command <span><strong class="command">mkdir -p /var/lib/bitlbee</strong></span>. This +directory has to be owned by the user that runs bitlbee. To make +'nobody' owner of this directory, run <span><strong class="command">chown nobody /var/lib/bitlbee</strong></span>. +Because things like passwords are saved in this directory, it's probably +a good idea to make this directory owner-read-/writable only. +</p></div></div><div class="chapter" lang="en"><div class="titlepage"><div><div><h2 class="title"><a name="Usage"></a>Chapter 2. Usage</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="sect1"><a href="#id4683835">Connecting to the server</a></span></dt><dt><span class="sect1"><a href="#id4683848">The #bitlbee control channel</a></span></dt><dt><span class="sect1"><a href="#id4683867">Talking to people</a></span></dt></dl></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id4683835"></a>Connecting to the server</h2></div></div></div><p> +Since BitlBee acts just like any other irc daemon, you can connect to +it with your favorite irc client. Launch it and connect to localhost port 6667 +(or whatever host/port you are running bitlbee on). +</p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id4683848"></a>The #bitlbee control channel</h2></div></div></div><p> +Once you are connected to the BitlBee server, you are automatically joined +to #bitlbee on that server. This channel acts like the 'buddy list' you have +on the various other chat networks. +</p><p> +The user 'root' always hangs around in #bitlbee and acts as your interface +to bitlbee. All commands you give on #bitlbee are 'answered' by root. +</p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id4683867"></a>Talking to people</h2></div></div></div><p> +You can talk to by starting a query with them. In most irc clients, +this can be done with either <span><strong class="command">/msg <nick> <text></strong></span> +or <span><strong class="command">/query <nick></strong></span>. +</p><p> +To keep the number of open query windows limited, you can also talk to people +in the control channel, like <span><strong class="command"><nick>: <text></strong></span>. +</p></div></div><div class="chapter" lang="en"><div class="titlepage"><div><div><h2 class="title"><a name="Support"></a>Chapter 3. Support</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="sect1"><a href="#id4683912">BitlBee is beta software</a></span></dt><dt><span class="sect1"><a href="#id4683926">Support channels</a></span></dt><dd><dl><dt><span class="sect2"><a href="#id4683931">The World Wide Web</a></span></dt><dt><span class="sect2"><a href="#id4683948">IRC</a></span></dt><dt><span class="sect2"><a href="#id4683960">Mailinglists</a></span></dt></dl></dd></dl></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id4683912"></a>BitlBee is beta software</h2></div></div></div><p> +Although BitlBee has quite some functionality it is still beta. That means it +can crash at any time, corrupt your data or whatever. Don't use it in +any production environment and don't rely on it. +</p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="id4683926"></a>Support channels</h2></div></div></div><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="id4683931"></a>The World Wide Web</h3></div></div></div><p><a href="http://www.bitlbee.org/" target="_top">http://www.bitlbee.org/</a> +is the homepage of bitlbee and contains the most recent news on bitlbee and +the latest releases. +</p></div><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="id4683948"></a>IRC</h3></div></div></div><p> +BitlBee is discussed on #bitlbee on the OFTC IRC network (server: irc.oftc.net). +</p></div><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="id4683960"></a>Mailinglists</h3></div></div></div><p> +BitlBee doesn't have any mailinglists. +</p></div></div></div><div class="chapter" lang="en"><div class="titlepage"><div><div><h2 class="title"><a name="quickstart"></a>Chapter 4. Quickstart</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="sect1"><a href="#quickstart2">Add and Connect To your IM Account(s)</a></span></dt><dt><span class="sect1"><a href="#quickstart3">Managing Contact Lists: Rename</a></span></dt><dt><span class="sect1"><a href="#quickstart4">Step Four: Managing Contact Lists: Add and Remove.</a></span></dt><dt><span class="sect1"><a href="#quickstart5">Chatting</a></span></dt><dt><span class="sect1"><a href="#quickstart6">Further Resources</a></span></dt></dl></div><p> +Welcome to BitlBee, your IRC gateway to ICQ, MSN, AOL, Jabber and Yahoo Instant Messaging Systems. +</p><p> +The center of BitlBee is the control channel, <span class="emphasis"><em>#bitlbee</em></span>. Two users will always be there, <span class="emphasis"><em>you</em></span> (where "you" is the nickname you are using) and the system user, <span class="emphasis"><em>root</em></span>. +</p><p> +You need register so that all your IM settings (passwords, contacts, etc) can be saved on the BitlBee server. It's important that you pick a good password so no one else can access your account. Register with this password using the <span class="emphasis"><em>register</em></span> command: <span class="emphasis"><em>register <password></em></span> (without the brackets!). +</p><p> +Be sure to remember your password. The next time you connect to the BitlBee server you will need to <span class="emphasis"><em>identify <password></em></span> so that you will be recognised and logged in to all the IM services automatically. +</p><p> +When finished, type <span class="emphasis"><em>help quickstart2</em></span> to continue. +</p><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="quickstart2"></a>Add and Connect To your IM Account(s)</h2></div></div></div><p> + <span class="emphasis"><em>Step Two: Add and Connect To your IM Account(s).</em></span> + </p><p> +To add an account to the account list you will need to use the <span class="emphasis"><em>account add</em></span> command: <span class="emphasis"><em>account add <protocol> <username> <password> [<server>]</em></span>. +</p><p> +For instance, suppose you have an ICQ account with UIN <span class="emphasis"><em>72696705</em></span> with password <span class="emphasis"><em>QuickStart</em></span>, you would: +</p><pre class="screen"><code class="prompt">< you> </code><strong class="userinput"><code>account add oscar 72696705 QuickStart login.icq.com</code></strong> +<code class="prompt">< root> </code><strong class="userinput"><code>Account successfully added</code></strong> +</pre><p> +Other available IM protocols are jabber, msn, and yahoo. Oscar is the protocol used by ICQ and AOL. For oscar, you need to specify the IM-server as a fourth argument (for msn and yahoo there is no fourth argument). For AOL Instant Messenger, the server name is <span class="emphasis"><em>login.oscar.aol.com</em></span>. For ICQ, the server name is <span class="emphasis"><em>login.icq.com</em></span>. +</p><p> +When you are finished adding your account(s) use the <span class="emphasis"><em>account on</em></span> command to enable all your accounts, type <span class="emphasis"><em>help quickstart3</em></span> to continue. +</p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="quickstart3"></a>Managing Contact Lists: Rename</h2></div></div></div><p> + <span class="emphasis"><em>Step Three: Managing Contact Lists: Rename</em></span> + </p><p> +For most protocols (currently MSN, Jabber, Yahoo and AOL) BitlBee can download the contact list automatically from the IM server and all the on-line users should appear in the control channel when you log in. +</p><p> +BitlBee will convert names into irc-friendly form (for instance: tux@example.com will be given the nickname tux). If you have more than one person who would have the same name by this logic (for instance: tux@example.com and tux@bitlbee.org) the second one to log on will be tux_. The same is true if you have a tux log on to AOL and a tux log on from Yahoo. +</p><p> +It would be easy to get these two mixed up, so BitlBee has a <span class="emphasis"><em>rename</em></span> command to change the nickname into something more suitable: <span class="emphasis"><em>rename <oldnick> <newnick></em></span></p><pre class="screen"><code class="prompt">< you> </code><strong class="userinput"><code>rename tux_ bitlbeetux</code></strong> + * tux_is now known as bitlbeetux +<code class="prompt">< root> </code><strong class="userinput"><code>Nick successfully changed</code></strong> +</pre><p> +When finished, type <span class="emphasis"><em>help quickstart4</em></span> to continue. +</p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="quickstart4"></a>Step Four: Managing Contact Lists: Add and Remove.</h2></div></div></div><p> + <span class="emphasis"><em>Step Four: Managing Contact Lists: Add and Remove.</em></span> + </p><p> +Now you might want to add some contacts, to do this we will use the <span class="emphasis"><em>add</em></span> command. It needs two arguments: a connection ID (which can be a number (try <span class="emphasis"><em>account list</em></span>), protocol name or (part of) the screenname) and the user's handle. It is used in the following way: <span class="emphasis"><em>add <connection> <handle></em></span></p><pre class="screen"><code class="prompt">< you> </code><strong class="userinput"><code>add 0 r2d2@example.com</code></strong> + * r2d2has joined #bitlbee +</pre><p> +In this case r2d2 is online, since he/she joins the channel immediately. If the user is not online you will not see them join until they log on. +</p><p> +Lets say you accidentally added r2d3@example.com rather than r2d2@example.com, or maybe you just want to remove a user from your list because you never talk to them. To remove a name you will want to use the <span class="emphasis"><em>remove</em></span> command: <span class="emphasis"><em>remove <nick></em></span></p><p> +When finished, type <span class="emphasis"><em>help quickstart5</em></span> to continue. +</p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="quickstart5"></a>Chatting</h2></div></div></div><p> + <span class="emphasis"><em>Step Five: Chatting.</em></span> + </p><p> +First of all, a person must be on your contact list for you to chat with them (unless it's a group chat, <span class="emphasis"><em>help groupchats</em></span> for more). If someone not on your contact list sends you a message, simply add them to the proper account with the <span class="emphasis"><em>add</em></span> command. Once they are on your list and online, you can chat with them in #bitlbee: +</p><pre class="screen"><code class="prompt">< you> </code><strong class="userinput"><code>tux: hey, how's the weather down there?</code></strong> +<code class="prompt">< tux> </code><strong class="userinput"><code>you: a bit chilly!</code></strong> +</pre><p> +If you'd rather chat with them in a separate window use the <span class="emphasis"><em>/msg</em></span> or <span class="emphasis"><em>/query</em></span> command, just like you would for a private message in IRC. If you want to have messages automatically come up in private messages rather than in the #bitlbee channel, use the <span class="emphasis"><em>set private</em></span> command: <span class="emphasis"><em>set private true</em></span> (<span class="emphasis"><em>set private false</em></span> to change back). +</p><p> +You know the basics. If you want to get to know more about BitlBee, please type <span class="emphasis"><em>help quickstart6</em></span>. +</p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="quickstart6"></a>Further Resources</h2></div></div></div><p> + <span class="emphasis"><em>So you want more than just chatting? Or maybe you're just looking for a feature?</em></span> + </p><p> +You can type <span class="emphasis"><em>help set</em></span> to learn more about the possible BitlBee user settings. Among these user settings you will find options for common issues, such as changing the charset, HTML stripping and automatic connecting (simply type <span class="emphasis"><em>set</em></span> to see current user settings). +</p><p> +For more subjects (like groupchats and away states), please type <span class="emphasis"><em>help index</em></span>. +</p><p> +If you're still looking for something, please visit us in #bitlbee on the OFTC network (you can connect via irc.bitlbee.org), or mail us your problem/suggestion. Good luck and enjoy the Bee! +</p></div></div><div class="chapter" lang="en"><div class="titlepage"><div><div><h2 class="title"><a name="commands"></a>Chapter 5. Bitlbee commands</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="sect1"><a href="#cmd_account">account - IM-account list maintenance</a></span></dt><dd><dl><dt><span class="sect2"><a href="#cmd_account_add">account add</a></span></dt><dt><span class="sect2"><a href="#cmd_account_del">account del</a></span></dt><dt><span class="sect2"><a href="#cmd_account_on">account on</a></span></dt><dt><span class="sect2"><a href="#cmd_account_off">account off</a></span></dt><dt><span class="sect2"><a href="#cmd_account_list">account list</a></span></dt></dl></dd><dt><span class="sect1"><a href="#cmd_add">add - Add a buddy to your contact list</a></span></dt><dt><span class="sect1"><a href="#cmd_info">info - Request user information</a></span></dt><dt><span class="sect1"><a href="#cmd_remove">remove - Remove a buddy from your contact list</a></span></dt><dt><span class="sect1"><a href="#cmd_block">block - Block someone</a></span></dt><dt><span class="sect1"><a href="#cmd_allow">allow - Unblock someone</a></span></dt><dt><span class="sect1"><a href="#cmd_set">set - Miscellaneous settings</a></span></dt><dt><span class="sect1"><a href="#cmd_help">help - BitlBee help system</a></span></dt><dt><span class="sect1"><a href="#cmd_save">save - Save your account data</a></span></dt><dt><span class="sect1"><a href="#set_charset">charset</a></span></dt><dt><span class="sect1"><a href="#set_private">private</a></span></dt><dt><span class="sect1"><a href="#set_save_on_quit">save_on_quit</a></span></dt><dt><span class="sect1"><a href="#set_html">html</a></span></dt><dt><span class="sect1"><a href="#set_debug">debug</a></span></dt><dt><span class="sect1"><a href="#set_to_char">to_char</a></span></dt><dt><span class="sect1"><a href="#set_typing_notice">typing_notice</a></span></dt><dt><span class="sect1"><a href="#set_ops">ops</a></span></dt><dt><span class="sect1"><a href="#set_away_devoice">away_devoice</a></span></dt><dt><span class="sect1"><a href="#set_handle_unknown">handle_unknown</a></span></dt><dt><span class="sect1"><a href="#set_auto_connect">auto_connect</a></span></dt><dt><span class="sect1"><a href="#set_auto_reconnect">auto_reconnect</a></span></dt><dt><span class="sect1"><a href="#set_auto_reconnect_delay">auto_reconnect_delay</a></span></dt><dt><span class="sect1"><a href="#set_buddy_sendbuffer">buddy_sendbuffer</a></span></dt><dt><span class="sect1"><a href="#set_buddy_sendbuffer_delay">buddy_sendbuffer_delay</a></span></dt><dt><span class="sect1"><a href="#set_default_target">default_target</a></span></dt><dt><span class="sect1"><a href="#set_display_namechanges">display_namechanges</a></span></dt><dt><span class="sect1"><a href="#set_password">password</a></span></dt><dt><span class="sect1"><a href="#set_query_order">query_order</a></span></dt><dt><span class="sect1"><a href="#set_lcnicks">lcnicks</a></span></dt><dt><span class="sect1"><a href="#cmd_rename">rename - Rename (renick) a buddy</a></span></dt><dt><span class="sect1"><a href="#cmd_yes">yes - Accept a request</a></span></dt><dt><span class="sect1"><a href="#cmd_no">no - Deny a request</a></span></dt><dt><span class="sect1"><a href="#cmd_qlist">qlist - List all the unanswered questions root asked</a></span></dt><dt><span class="sect1"><a href="#cmd_register">register - Register yourself</a></span></dt><dt><span class="sect1"><a href="#cmd_identify">identify - Identify yourself with your password</a></span></dt><dt><span class="sect1"><a href="#cmd_drop">drop - Drop your account</a></span></dt><dt><span class="sect1"><a href="#cmd_blist">blist - List all the buddies in your contact list</a></span></dt><dt><span class="sect1"><a href="#cmd_nick">nick - Change friendly name, nick</a></span></dt><dt><span class="sect1"><a href="#cmd_import_buddies">import_buddies - Copy local buddy list to server (normally only needed when upgrading)</a></span></dt></dl></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="cmd_account"></a>account - IM-account list maintenance</h2></div></div></div><p><b>Syntax: </b> + </p><pre class="programlisting">account <action> [<arguments>] +</pre><p> + </p><p> + Available actions: add, del, list, on, off. See <span class="emphasis"><em>help account <action></em></span> for more information. + </p><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="cmd_account_add"></a>account add</h3></div></div></div><p><b>Syntax: </b> + </p><pre class="programlisting">account add <protocol> <username> <password> [<server>] +</pre><p> + </p><p> + Adds an account on the given server with the specified protocol, username and password to the account list. Supported protocols right now are: Jabber, MSN, OSCAR (AIM/ICQ) and Yahoo. For more information about adding an account, see <span class="emphasis"><em>help account add <protocol></em></span>. + </p><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="cmd_account_add_jabber"></a>account add jabber</h3></div></div></div><p><b>Syntax: </b> + </p><pre class="programlisting">account add jabber <handle> <password> [<servertag>] +</pre><p> + </p><p> + Note that the servertag argument is optional. You only have to use it if the part after the @ in your handle isn't the hostname of your Jabber server, or if you want to use SSL/connect to a non-standard port number. The format is simple: [<servername>[:<portnumber>][:ssl]]. For example, this is how you can connect to Google Talk: + </p><p> + Note that Google talk is SSL-only, but officially reachable over both port 5222 and 5223. However, for some people only port 5222 works, for some people only 5223. This is something you'll have to try out. + </p><pre class="screen"><code class="prompt">< wilmer> </code><strong class="userinput"><code>account add jabber example@gmail.com hobbelmeeuw talk.google.com:5223:ssl</code></strong> +<code class="prompt">< root> </code><strong class="userinput"><code>Account successfully added</code></strong> +</pre></div><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="cmd_account_add_msn"></a>account add msn</h3></div></div></div><p><b>Syntax: </b> + </p><pre class="programlisting">account add msn <handle> <password> +</pre><p> + </p><p> + For MSN connections there are no special arguments. + </p></div><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="cmd_account_add_oscar"></a>account add oscar</h3></div></div></div><p><b>Syntax: </b> + </p><pre class="programlisting">account add oscar <handle> <password> [<servername>] +</pre><p> + </p><p> + Specifying a server is required for OSCAR, since OSCAR can be used for both ICQ- and AIM-connections. Although these days it's supposed to be possible to connect to ICQ via AIM-servers and vice versa, we like to stick with this separation for now. For ICQ connections, the servername is <span class="emphasis"><em>login.icq.com</em></span>, for AIM connections it's <span class="emphasis"><em>login.oscar.aol.com</em></span>. + </p><pre class="screen"><code class="prompt">< wilmer> </code><strong class="userinput"><code>account add oscar 72696705 hobbelmeeuw login.icq.com</code></strong> +<code class="prompt">< root> </code><strong class="userinput"><code>Account successfully added</code></strong> +</pre></div><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="cmd_account_add_yahoo"></a>account add yahoo</h3></div></div></div><p><b>Syntax: </b> + </p><pre class="programlisting">account add yahoo <handle> <password> +</pre><p> + </p><p> + For Yahoo! connections there are no special arguments. + </p></div></div><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="cmd_account_del"></a>account del</h3></div></div></div><p><b>Syntax: </b> + </p><pre class="programlisting">account del <account id> +</pre><p> + </p><p> + This commands deletes an account from your account list. You should signoff the account before deleting it. + </p><p> + The account ID can be a number (see <span class="emphasis"><em>account list</em></span>), the protocol name or (part of) the screenname, as long as it matches only one connection. + </p></div><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="cmd_account_on"></a>account on</h3></div></div></div><p><b>Syntax: </b> + </p><pre class="programlisting">account on [<account id>] +</pre><p> + </p><p> + This command will try to log into the specified account. If no account is specified, BitlBee will log into all the accounts. (Including accounts awaiting a reconnection) + </p><p> + The account ID can be a number (see <span class="emphasis"><em>account list</em></span>), the protocol name or (part of) the screenname, as long as it matches only one connection. + </p></div><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="cmd_account_off"></a>account off</h3></div></div></div><p><b>Syntax: </b> + </p><pre class="programlisting">account off [<account id>] +</pre><p> + </p><p> + This command disconnects the connection for the specified account. If no account is specified, BitlBee will deactivate all active accounts. (Including accounts awaiting a reconnection) + </p><p> + The account ID can be a number (see <span class="emphasis"><em>account list</em></span>), the protocol name or (part of) the screenname, as long as it matches only one connection. + </p></div><div class="sect2" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="cmd_account_list"></a>account list</h3></div></div></div><p><b>Syntax: </b> + </p><pre class="programlisting">account list +</pre><p> + </p><p> + This command gives you a list of all the accounts known by BitlBee, including the numbers you'll need for most account commands. + </p></div></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="cmd_add"></a>add - Add a buddy to your contact list</h2></div></div></div><p><b>Syntax: </b> + </p><pre class="programlisting">add <connection> <handle> [<nick>] +</pre><p> + </p><p> + Adds the given buddy at the specified connection to your buddy list. The account ID can be a number (see <span class="emphasis"><em>account list</em></span>), the protocol name or (part of) the screenname, as long as it matches only one connection. + </p><p> + If you want, you can also tell BitlBee what nick to give the new contact. Of course you can also use the <span class="emphasis"><em>rename</em></span> command for that, but sometimes this might be more convenient. + </p><pre class="screen"><code class="prompt">< ctrlsoft> </code><strong class="userinput"><code>add 3 gryp@jabber.org grijp</code></strong> + * grijphas joined #bitlbee +</pre></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="cmd_info"></a>info - Request user information</h2></div></div></div><p><b>Syntax: </b> + </p><pre class="programlisting">info <connection> <handle> +info <nick> +</pre><p> + </p><p> + Requests IM-network-specific information about the specified user. The amount of information you'll get differs per protocol. For some protocols (ATM Yahoo! and MSN) it'll give you an URL which you can visit with a normal web browser to get the information. + </p><pre class="screen"><code class="prompt">< ctrlsoft> </code><strong class="userinput"><code>info 0 72696705</code></strong> +<code class="prompt">< root> </code><strong class="userinput"><code>User info - UIN: 72696705 Nick: Lintux First/Last name: Wilmer van der Gaast E-mail: lintux@lintux.cx</code></strong> +</pre></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="cmd_remove"></a>remove - Remove a buddy from your contact list</h2></div></div></div><p><b>Syntax: </b> + </p><pre class="programlisting">remove <nick> +</pre><p> + </p><p> + Removes the specified nick from your buddy list. + </p><pre class="screen"><code class="prompt">< ctrlsoft> </code><strong class="userinput"><code>remove gryp</code></strong> + * gryphas quit [Leaving...] +</pre></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="cmd_block"></a>block - Block someone</h2></div></div></div><p><b>Syntax: </b> + </p><pre class="programlisting">block <nick> +block <connection> <handle> +</pre><p> + </p><p> + Puts the specified user on your ignore list. Either specify the user's nick when you have him/her in your contact list or a connection number and a user handle. + </p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="cmd_allow"></a>allow - Unblock someone</h2></div></div></div><p><b>Syntax: </b> + </p><pre class="programlisting">allow <nick> +allow <connection> <handle> +</pre><p> + </p><p> + Reverse of block. Unignores the specified user or user handle on specified connection. + </p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="cmd_set"></a>set - Miscellaneous settings</h2></div></div></div><p><b>Syntax: </b> + </p><pre class="programlisting">set [<variable> [<value>]] +</pre><p> + </p><p> + Without any arguments, this command lists all the set variables. You can also specify a single argument, a variable name, to get that variable's value. To change this value, specify the new value as the second argument. + </p><p> + To get more help information about a setting, try: + </p><pre class="screen"><code class="prompt">< ctrlsoft> </code><strong class="userinput"><code>help set private</code></strong> +</pre></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="cmd_help"></a>help - BitlBee help system</h2></div></div></div><p><b>Syntax: </b> + </p><pre class="programlisting">help [subject] +</pre><p> + </p><p> + This command gives you the help information you're reading right now. If you don't give any arguments, it'll give a short help index. + </p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="cmd_save"></a>save - Save your account data</h2></div></div></div><p><b>Syntax: </b> + </p><pre class="programlisting">save +</pre><p> + </p><p> + This command saves all your nicks and accounts immediately. Handy if you have the autosave functionality disabled, or if you don't trust the program's stability... ;-) + </p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="set_charset"></a>charset</h2></div></div></div><table class="simplelist" border="0" summary="Simple list"><tr><td>Type: string</td></tr></table><p> + The charset setting enables you to use different character sets in BitlBee. These get converted to UTF-8 before sending and from UTF-8 when receiving. + </p><p> + If you don't know what's the best value for this, at least iso8859-1 is the best choice for most Western countries. You can try to find what works best for you on http://czyborra.com/charsets/iso8859.html + </p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="set_private"></a>private</h2></div></div></div><table class="simplelist" border="0" summary="Simple list"><tr><td>Type: boolean</td></tr></table><p> + If value is true, messages from users will appear in separate query windows. If false, messages from users will appear in the control channel. + </p><p> + This setting is remembered (during one session) per-user, this setting only changes the default state. This option takes effect as soon as you reconnect. + </p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="set_save_on_quit"></a>save_on_quit</h2></div></div></div><table class="simplelist" border="0" summary="Simple list"><tr><td>Type: boolean</td></tr></table><p> + If enabled causes BitlBee to save all current settings and account details when user disconnects. This is enabled by default, and these days there's not really a reason to have it disabled anymore. + </p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="set_html"></a>html</h2></div></div></div><table class="simplelist" border="0" summary="Simple list"><tr><td>Type: string</td></tr></table><p> + Determines what BitlBee should do with HTML in messages. If set to nostrip, HTML in messages will not be touched. If set to strip, all HTML will be stripped from messages. Unfortunately this sometimes strips too much. + </p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="set_debug"></a>debug</h2></div></div></div><table class="simplelist" border="0" summary="Simple list"><tr><td>Type: boolean</td></tr></table><p> + Some debugging messages can be sent to the control channel if you wish. They're probably not really useful for you, unless you're doing some development on BitlBee. + </p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="set_to_char"></a>to_char</h2></div></div></div><table class="simplelist" border="0" summary="Simple list"><tr><td>Type: string</td></tr></table><p> + It's customary that messages meant for one specific person on an IRC channel are prepended by his/her alias followed by a colon ':'. BitlBee does this by default. If you prefer a different character, you can set it using <span class="emphasis"><em>set to_char</em></span>. + </p><p> + Please note that this setting is only used for incoming messages. For outgoing messages you can use ':' (colon) or ',' to separate the destination nick from the message, and this is not configurable. + </p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="set_typing_notice"></a>typing_notice</h2></div></div></div><table class="simplelist" border="0" summary="Simple list"><tr><td>Type: boolean</td></tr></table><p> + Sends you a /notice when a user starts typing a message (if the protocol supports it, MSN for example). This is a bug, not a feature. (But please don't report it.. ;-) You don't want to use it. Really. In fact the typing-notification is just one of the least useful 'innovations' ever. It's just there because some guy will probably ask me about it anyway. ;-) + </p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="set_ops"></a>ops</h2></div></div></div><table class="simplelist" border="0" summary="Simple list"><tr><td>Type: string</td></tr></table><p> + Some people prefer themself and root to have operator status in #bitlbee, other people don't. You can change these states using this setting. + </p><p> + The value "both" means both user and root get ops. "root" means, well, just root. "user" means just the user. "none" means nobody will get operator status. + </p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="set_away_devoice"></a>away_devoice</h2></div></div></div><table class="simplelist" border="0" summary="Simple list"><tr><td>Type: boolean</td></tr></table><p> + With this option enabled, the root user devoices people when they go away (just away, not offline) and gives the voice back when they come back. You might dislike the voice-floods you'll get if your contact list is huge, so this option can be disabled. + </p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="set_handle_unknown"></a>handle_unknown</h2></div></div></div><table class="simplelist" border="0" summary="Simple list"><tr><td>Type: string</td></tr></table><p> + Messages from unknown users are echoed like this by default: + </p><p> + If you want this lame user to be added automatically, you can set this setting to "add". If you prefer to ignore messages from people you don't know, you can set this one to "ignore". "add_private" and "add_channel" are like add, but you can use them to make messages from unknown buddies appear in the channel instead of a query window. + </p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="set_auto_connect"></a>auto_connect</h2></div></div></div><table class="simplelist" border="0" summary="Simple list"><tr><td>Type: boolean</td></tr></table><p> + With this option enabled, when you identify BitlBee will automatically connect to your accounts, with this disabled it will not do this. + </p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="set_auto_reconnect"></a>auto_reconnect</h2></div></div></div><table class="simplelist" border="0" summary="Simple list"><tr><td>Type: boolean</td></tr></table><p> + If an IM-connections breaks, you're supposed to bring it back up yourself. Having BitlBee do this automatically might not always be a good idea, for several reasons. If you want the connections to be restored automatically, you can enable this setting. + </p><p> + See also the <span class="emphasis"><em>auto_reconnect_delay</em></span> setting. + </p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="set_auto_reconnect_delay"></a>auto_reconnect_delay</h2></div></div></div><table class="simplelist" border="0" summary="Simple list"><tr><td>Type: integer</td></tr></table><p> + Tell BitlBee after how many seconds it should attempt to bring an IM-connection back up after a crash. It's not a good idea to set this value very low, it will cause too much useless traffic when an IM-server is down for a few hours. + </p><p> + See also the <span class="emphasis"><em>auto_reconnect</em></span> setting. + </p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="set_buddy_sendbuffer"></a>buddy_sendbuffer</h2></div></div></div><table class="simplelist" border="0" summary="Simple list"><tr><td>Type: boolean</td></tr></table><p> + By default, when you send a message to someone, BitlBee forwards this message to the user immediately. When you paste a large number of lines, the lines will be sent in separate messages, which might not be very nice to read. If you enable this setting, BitlBee will buffer your messages and wait for more data. + </p><p> + Using the <span class="emphasis"><em>buddy_sendbuffer_delay</em></span> setting you can specify the number of seconds BitlBee should wait for more data before the complete message is sent. + </p><p> + Please note that if you remove a buddy from your list (or if the connection to that user drops) and there's still data in the buffer, this data will be lost. BitlBee will not try to send the message to the user in those cases. + </p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="set_buddy_sendbuffer_delay"></a>buddy_sendbuffer_delay</h2></div></div></div><table class="simplelist" border="0" summary="Simple list"><tr><td>Type: integer</td></tr></table><p> + Tell BitlBee after how many seconds a buffered message should be sent. + </p><p> + See also the <span class="emphasis"><em>buddy_sendbuffer</em></span> setting. + </p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="set_default_target"></a>default_target</h2></div></div></div><table class="simplelist" border="0" summary="Simple list"><tr><td>Type: string</td></tr></table><p> + With this value set to <span class="emphasis"><em>root</em></span>, lines written in the control channel without any nickname in front of them will be interpreted as commands. If you want BitlBee to send those lines to the last person you addressed in the control channel, set this to <span class="emphasis"><em>last</em></span>. + </p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="set_display_namechanges"></a>display_namechanges</h2></div></div></div><table class="simplelist" border="0" summary="Simple list"><tr><td>Type: boolean</td></tr></table></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="set_password"></a>password</h2></div></div></div><table class="simplelist" border="0" summary="Simple list"><tr><td>Type: string</td></tr></table><p> + Use this setting to change your "NickServ" password. + </p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="set_query_order"></a>query_order</h2></div></div></div><table class="simplelist" border="0" summary="Simple list"><tr><td>Type: string</td></tr></table><p> + This changes the order in which the questions from root (usually authorization requests from buddies) should be answered. When set to <span class="emphasis"><em>lifo</em></span>, BitlBee immediately displays all new questions and they should be answered in reverse order. When this is set to <span class="emphasis"><em>fifo</em></span>, BitlBee displays the first question which comes in and caches all the others until you answer the first one. + </p><p> + Although the <span class="emphasis"><em>fifo</em></span> setting might sound more logical (and used to be the default behaviour in older BitlBee versions), it turned out not to be very convenient for many users when they missed the first question (and never received the next ones). + </p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="set_lcnicks"></a>lcnicks</h2></div></div></div><table class="simplelist" border="0" summary="Simple list"><tr><td>Type: boolean</td></tr></table><p> + Hereby you can change whether you want all lower case nick names or leave the case as it intended by your peer. + </p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="cmd_rename"></a>rename - Rename (renick) a buddy</h2></div></div></div><p><b>Syntax: </b> + </p><pre class="programlisting">rename <oldnick> <newnick> +</pre><p> + </p><p> + Renick a user in your buddy list. Very useful, in fact just very important, if you got a lot of people with stupid account names (or hard ICQ numbers). + </p><pre class="screen"><code class="prompt">< itsme> </code><strong class="userinput"><code>rename itsme_ you</code></strong> + * itsme_is now known as you +</pre></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="cmd_yes"></a>yes - Accept a request</h2></div></div></div><p><b>Syntax: </b> + </p><pre class="programlisting">yes [<number>] +</pre><p> + </p><p> + Sometimes an IM-module might want to ask you a question. (Accept this user as your buddy or not?) To accept a question, use the <span class="emphasis"><em>yes</em></span> command. + </p><p> + By default, this answers the first unanswered question. You can also specify a different question as an argument. You can use the <span class="emphasis"><em>qlist</em></span> command for a list of questions. + </p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="cmd_no"></a>no - Deny a request</h2></div></div></div><p><b>Syntax: </b> + </p><pre class="programlisting">no [<number>] +</pre><p> + </p><p> + Sometimes an IM-module might want to ask you a question. (Accept this user as your buddy or not?) To reject a question, use the <span class="emphasis"><em>no</em></span> command. + </p><p> + By default, this answers the first unanswered question. You can also specify a different question as an argument. You can use the <span class="emphasis"><em>qlist</em></span> command for a list of questions. + </p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="cmd_qlist"></a>qlist - List all the unanswered questions root asked</h2></div></div></div><p><b>Syntax: </b> + </p><pre class="programlisting">qlist +</pre><p> + </p><p> + This gives you a list of all the unanswered questions from root. + </p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="cmd_register"></a>register - Register yourself</h2></div></div></div><p><b>Syntax: </b> + </p><pre class="programlisting">register <password> +</pre><p> + </p><p> + BitlBee can save your settings so you won't have to enter all your IM passwords every time you log in. If you want the Bee to save your settings, use the <span class="emphasis"><em>register</em></span> command. + </p><p> + Please do pick a secure password, don't just use your nick as your password. Please note that IRC is not an encrypted protocol, so the passwords still go over the network in plaintext. Evil people with evil sniffers will read it all. (So don't use your root password.. ;-) + </p><p> + To identify yourself in later sessions, you can use the <span class="emphasis"><em>identify</em></span> command. + </p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="cmd_identify"></a>identify - Identify yourself with your password</h2></div></div></div><p><b>Syntax: </b> + </p><pre class="programlisting">identify <password> +</pre><p> + </p><p> + BitlBee saves all your settings (contacts, accounts, passwords) on-server. To prevent other users from just logging in as you and getting this information, you'll have to identify yourself with your password. You can register this password using the <span class="emphasis"><em>register</em></span> command. + </p><p> + Once you're registered, you can change your password using <span class="emphasis"><em>set password <password></em></span>. + </p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="cmd_drop"></a>drop - Drop your account</h2></div></div></div><p><b>Syntax: </b> + </p><pre class="programlisting">drop <password> +</pre><p> + </p><p> + Drop your BitlBee registration. Your account files will be removed and your password will be forgotten. For obvious security reasons, you have to specify your NickServ password to make this command work. + </p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="cmd_blist"></a>blist - List all the buddies in your contact list</h2></div></div></div><p><b>Syntax: </b> + </p><pre class="programlisting">blist [all|online|offline|away] +</pre><p> + </p><p> + You can get a better readable buddy list using the <span class="emphasis"><em>blist</em></span> command. If you want a complete list (including the offline users) you can use the <span class="emphasis"><em>all</em></span> argument. + </p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="cmd_nick"></a>nick - Change friendly name, nick</h2></div></div></div><p><b>Syntax: </b> + </p><pre class="programlisting">nick <connection> [<new nick>] +nick +</pre><p> + </p><p> + This command allows to set the friendly name of an im account. If no new name is specified the command will report the current name. When the name contains spaces, don't forget to quote the whole nick in double quotes. Currently this command is only supported by the MSN protocol. + </p><pre class="screen"><code class="prompt">< wouter> </code><strong class="userinput"><code>nick 1 "Wouter Paesen"</code></strong> +<code class="prompt">< root> </code><strong class="userinput"><code>Setting your name on connection 1 to `Wouter Paesen'</code></strong> +</pre></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="cmd_import_buddies"></a>import_buddies - Copy local buddy list to server (normally only needed when upgrading)</h2></div></div></div><p><b>Syntax: </b> + </p><pre class="programlisting">import_buddies <connection> [clear] +</pre><p> + </p><p> + This command copies the locally stored buddy list to the server. This command exists for upgrading purposes. Previous versions of BitlBee didn't support server-side buddy lists for ICQ, so the list was stored locally. + </p><p> + Since version 0.91 however, server-side contact lists are supported for all protocols, so the local list is now ignored. When upgrading from an older BitlBee to version 0.91, you might need this command to get your buddy list back. + </p><p> + The only argument this command needs is your ICQ account identification. If your serverside buddy list contains some old buddies you don't want anymore, you can pass <span class="emphasis"><em>clear</em></span> as a second argument. + </p><p> + After giving this command, you have to wait for a while before all the adds are handled, because of ICQ's rate limiting. If your buddy list is very large and the ICQ server starts complaining, you might have to reconnect and enter this command again. + </p></div></div><div class="chapter" lang="en"><div class="titlepage"><div><div><h2 class="title"><a name="misc"></a>Chapter 6. Misc Stuff</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="sect1"><a href="#smileys">Smileys</a></span></dt><dt><span class="sect1"><a href="#groupchats">Groupchats</a></span></dt><dt><span class="sect1"><a href="#groupchats2">Creating groupchats</a></span></dt><dt><span class="sect1"><a href="#groupchats3">Groupchat channel names</a></span></dt><dt><span class="sect1"><a href="#away">Away states</a></span></dt></dl></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="smileys"></a>Smileys</h2></div></div></div><p> +All MSN smileys (except one) are case insensitive and work without the nose too. +</p><div class="variablelist"><dl><dt><span class="term">(Y)</span></dt><dd><p>Thumbs up</p></dd><dt><span class="term">(N)</span></dt><dd><p>Thumbs down</p></dd><dt><span class="term">(B)</span></dt><dd><p>Beer mug</p></dd><dt><span class="term">(D)</span></dt><dd><p>Martini glass</p></dd><dt><span class="term">(X)</span></dt><dd><p>Girl</p></dd><dt><span class="term">(Z)</span></dt><dd><p>Boy</p></dd><dt><span class="term">(6)</span></dt><dd><p>Devil smiley</p></dd><dt><span class="term">:-[</span></dt><dd><p>Vampire bat</p></dd><dt><span class="term">(})</span></dt><dd><p>Right hug</p></dd><dt><span class="term">({)</span></dt><dd><p>Left hug</p></dd><dt><span class="term">(M)</span></dt><dd><p>MSN Messenger or Windows Messenger icon (think a BitlBee logo here ;)</p></dd><dt><span class="term">:-S</span></dt><dd><p>Crooked smiley (Confused smiley)</p></dd><dt><span class="term">:-$</span></dt><dd><p>Embarrassed smiley</p></dd><dt><span class="term">(H)</span></dt><dd><p>Smiley with sunglasses</p></dd><dt><span class="term">:-@</span></dt><dd><p>Angry smiley</p></dd><dt><span class="term">(A)</span></dt><dd><p>Angel smiley</p></dd><dt><span class="term">(L)</span></dt><dd><p>Red heart (Love)</p></dd><dt><span class="term">(U)</span></dt><dd><p>Broken heart</p></dd><dt><span class="term">(K)</span></dt><dd><p>Red lips (Kiss)</p></dd><dt><span class="term">(G)</span></dt><dd><p>Gift with bow</p></dd><dt><span class="term">(F)</span></dt><dd><p>Red rose</p></dd><dt><span class="term">(W)</span></dt><dd><p>Wilted rose</p></dd><dt><span class="term">(P)</span></dt><dd><p>Camera</p></dd><dt><span class="term">(~)</span></dt><dd><p>Film strip</p></dd><dt><span class="term">(T)</span></dt><dd><p>Telephone receiver</p></dd><dt><span class="term">(@)</span></dt><dd><p>Cat face</p></dd><dt><span class="term">(&)</span></dt><dd><p>Dog's head</p></dd><dt><span class="term">(C)</span></dt><dd><p>Coffee cup</p></dd><dt><span class="term">(I)</span></dt><dd><p>Light bulb</p></dd><dt><span class="term">(S)</span></dt><dd><p>Half-moon (Case sensitive!)</p></dd><dt><span class="term">(*)</span></dt><dd><p>Star</p></dd><dt><span class="term">(8)</span></dt><dd><p>Musical eighth note</p></dd><dt><span class="term">(E)</span></dt><dd><p>Envelope</p></dd><dt><span class="term">(^)</span></dt><dd><p>Birthday cake</p></dd><dt><span class="term">(O)</span></dt><dd><p>Clock</p></dd></dl></div><p> +This list was extracted from <a href="http://help.msn.com/!data/en_us/data/messengerv50.its51/%24content%24/EMOTICONS.HTM?H_APP=" target="_top">http://help.msn.com/!data/en_us/data/messengerv50.its51/$content$/EMOTICONS.HTM?H_APP=</a>. +</p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="groupchats"></a>Groupchats</h2></div></div></div><p> +Since version 0.8x, BitlBee supports groupchats on the MSN and Yahoo! networks. This text will try to explain you how they work. +</p><p> +As soon as someone invites you into a groupchat, you will be force-joined or invited (depending on the protocol) into a new virtual channel with all the people in there. You can leave the channel at any time, just like you would close the window in regular IM clients. Please note that root-commands don't work in groupchat channels, they only work in the control channel (or to root directly). +</p><p> +Of course you can also create your own groupchats. Type <span class="emphasis"><em>help groupchats2</em></span> to see how. +</p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="groupchats2"></a>Creating groupchats</h2></div></div></div><p> +If you want to start a groupchat with the person <span class="emphasis"><em>jim_msn</em></span> in it, just join the channel <span class="emphasis"><em>#jim_msn</em></span>. BitlBee will refuse to join you to the channel with that name, but it will create a new virtual channel with root, you and jim_msn in it. +</p><p> +Of course a channel with only two people isn't really exciting yet. So the next step is to invite some other people to the channel. For this, you can use the <span class="emphasis"><em>/invite</em></span> command of your IRC client. Please do keep in mind that all the people have to be on the same network and contact list! You can't invite Yahoo! buddies into an MSN groupchat. +</p><p> +This is all you'll probably need to know. If you have any problems, please read <span class="emphasis"><em>help groupchats3</em></span>. +</p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="groupchats3"></a>Groupchat channel names</h2></div></div></div><p> +Obviously the (numbered) channel names don't make a lot of sense. Problem is that groupchats usually don't have names at all in the IM-world, while IRC insists on a name. So BitlBee just generates something random, just don't pay attention to it. :-) +</p><p> +Please also note that BitlBee doesn't support groupchats for all protocols yet. BitlBee will tell you so. Support for other protocols will hopefully come later. +</p></div><div class="sect1" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="away"></a>Away states</h2></div></div></div><p> +As you might've expected, you can just use the <span class="emphasis"><em>/away</em></span> command in your IRC client to set an away-state. BitlBee supports most away-states supported by the protocols. +</p><p> +Not all away states are supported by all protocols, and some protocols have different names for them. BitlBee will try to pick the best available alias from this list for every connection: +</p><table class="simplelist" border="0" summary="Simple list"><tr><td>Away from computer, Away, Extended away</td></tr><tr><td>NA, N/A, Not available</td></tr><tr><td>Busy, Do not disturb, DND, Occupied</td></tr><tr><td>Be right back, BRB</td></tr><tr><td>On the phone, Phone, On phone</td></tr><tr><td>Out to lunch, Lunch, Food</td></tr></table><p> +So <span class="emphasis"><em>/away Food</em></span> will set your state to "Out to lunch" on your MSN connection, and for most other connections the default, "Away" or "Away from computer" will be chosen. +</p><p> +You can also add more information to your away message. Setting it to "Busy - Fixing BitlBee bugs" will set your IM-away-states to Busy, but your away message will be more descriptive for people on IRC. Protocols like Yahoo! and Jabber will also show this complete away message to your buddies. +</p></div></div></div></body></html> diff --git a/doc/user-guide/user-guide.txt b/doc/user-guide/user-guide.txt new file mode 100644 index 00000000..b8854f7a --- /dev/null +++ b/doc/user-guide/user-guide.txt @@ -0,0 +1,1209 @@ +BitlBee User Guide + +Jelmer Vernooij + +Wilmer van der Gaast + +Sjoerd Hemminga + +This is the initial release of the BitlBee User Guide. + +Permission is granted to copy, distribute and/or modify this document under the +terms of the GNU Free Documentation License, Version 1.1 or any later version +published by the Free Software Foundation with no Invariant Sections, no +Front-Cover Texts, and no Back-Cover Texts. You may obtain a copy of the GNU +Free Documentation License from the Free Software Foundation by visiting their +Web site or by writing to: Free Software Foundation, Inc., 59 Temple Place - +Suite 330, Boston, MA 02111-1307, USA. + +------------------------------------------------------------------------------- + +Table of Contents + +1. Installation + + Downloading the package + Compiling + Configuration + +2. Usage + + Connecting to the server + The #bitlbee control channel + Talking to people + +3. Support + + BitlBee is beta software + Support channels + + The World Wide Web + IRC + Mailinglists + +4. Quickstart + + Add and Connect To your IM Account(s) + Managing Contact Lists: Rename + Step Four: Managing Contact Lists: Add and Remove. + Chatting + Further Resources + +5. Bitlbee commands + + account - IM-account list maintenance + + account add + account del + account on + account off + account list + + add - Add a buddy to your contact list + info - Request user information + remove - Remove a buddy from your contact list + block - Block someone + allow - Unblock someone + set - Miscellaneous settings + help - BitlBee help system + save - Save your account data + charset + private + save_on_quit + html + debug + to_char + typing_notice + ops + away_devoice + handle_unknown + auto_connect + auto_reconnect + auto_reconnect_delay + buddy_sendbuffer + buddy_sendbuffer_delay + default_target + display_namechanges + password + query_order + lcnicks + rename - Rename (renick) a buddy + yes - Accept a request + no - Deny a request + qlist - List all the unanswered questions root asked + register - Register yourself + identify - Identify yourself with your password + drop - Drop your account + blist - List all the buddies in your contact list + nick - Change friendly name, nick + import_buddies - Copy local buddy list to server (normally only needed when + upgrading) + +6. Misc Stuff + + Smileys + Groupchats + Creating groupchats + Groupchat channel names + Away states + +Chapter 1. Installation + +Table of Contents + +Downloading the package +Compiling +Configuration + +Downloading the package + +The latest BitlBee release is always available from http://www.bitlbee.org/. +Download the package with your favorite program and unpack it: tar xvfz +bitlbee-<version>.tar.gz where <version> is to be replaced by the version +number of the BitlBee you downloaded (e.g. 0.91). + +Compiling + +BitlBee's build system has to be configured before compiling. The configure +script will do this for you. Just run it, it'll set up with nice and hopefully +well-working defaults. If you want to change some settings, just try ./ +configure --help and see what you can do. + +Some variables that might be of interest to the normal user: + + * prefix, bindir, etcdir, mandir, datadir - The place where all the BitlBee + program files will be put. There's usually no reason to specify them all + separately, just specifying prefix (or keeping the default /usr/local/) + should be okay. + + * config - The place where BitlBee will save all the per-user settings and + buddy information. /var/lib/bitlbee/ is the default value. + + * msn, jabber, oscar, yahoo - By default, support for all these IM-protocols + (OSCAR is the protocol used by both ICQ and AIM) will be compiled in. To + make the binary a bit smaller, you can use these options to leave out + support for protocols you're not planning to use. + + * debug - Generate an unoptimized binary with debugging symbols, mainly + useful if you want to do some debugging or help us to track down a problem. + + * strip - By default, unnecessary parts of the generated binary will be + stripped out to make it as small as possible. If you don't want this + (because it might cause problems on some platforms), set this to 0. + + * flood - To secure your BitlBee server against flooding attacks, you can use + this option. It's not compiled in by default because it needs more testing + first. + + * ssl - The MSN and Jabber modules require an SSL library for some of their + tasks. BitlBee can use three different SSL libraries: GnuTLS, mozilla-nss + and OpenSSL. (OpenSSL is, however, a bit troublesome because of licensing + issues, so don't forget to read the information configure will give you + when you try to use OpenSSL!) By default, configure will try to detect + GnuTLS or mozilla-nss. If none of them can be found, it'll give up. If you + want BitlBee to use OpenSSL, you have to explicitly specify that. + +After running configure, you should run make. After that, run make install as +root. + +Configuration + +By default, BitlBee runs as the user nobody. You might want to run it as a +seperate user (some computers run named or apache as nobody). + +Since BitlBee uses inetd, you should add the following line to /etc/inetd.conf: + +6667 stream tcp nowait nobody /usr/local/sbin/bitlbee bitlbee + +Inetd has to be restarted after changing the configuration. Either killall -HUP +inetd or /etc/init.d/inetd restart should do the job on most systems. + +You might be one of the.. ehr, lucky people running an xinetd-powered distro. +xinetd is quite different and they seem to be proud of that.. ;-) Anyway, if +you want BitlBee to work with xinetd, just copy the bitlbee.xinetd file to your +/etc/xinetd.d/ directory (and probably edit it to suit your needs). + +You should create a directory where BitlBee can store it's data files. This +should be the directory named after the value 'CONFIG' in Makefile.settings. +The default is /var/lib/bitlbee, which can be created with the command mkdir -p +/var/lib/bitlbee. This directory has to be owned by the user that runs bitlbee. +To make 'nobody' owner of this directory, run chown nobody /var/lib/bitlbee. +Because things like passwords are saved in this directory, it's probably a good +idea to make this directory owner-read-/writable only. + +Chapter 2. Usage + +Table of Contents + +Connecting to the server +The #bitlbee control channel +Talking to people + +Connecting to the server + +Since BitlBee acts just like any other irc daemon, you can connect to it with +your favorite irc client. Launch it and connect to localhost port 6667 (or +whatever host/port you are running bitlbee on). + +The #bitlbee control channel + +Once you are connected to the BitlBee server, you are automatically joined to # +bitlbee on that server. This channel acts like the 'buddy list' you have on the +various other chat networks. + +The user 'root' always hangs around in #bitlbee and acts as your interface to +bitlbee. All commands you give on #bitlbee are 'answered' by root. + +Talking to people + +You can talk to by starting a query with them. In most irc clients, this can be +done with either /msg <nick> <text> or /query <nick>. + +To keep the number of open query windows limited, you can also talk to people +in the control channel, like <nick>: <text>. + +Chapter 3. Support + +Table of Contents + +BitlBee is beta software +Support channels + + The World Wide Web + IRC + Mailinglists + +BitlBee is beta software + +Although BitlBee has quite some functionality it is still beta. That means it +can crash at any time, corrupt your data or whatever. Don't use it in any +production environment and don't rely on it. + +Support channels + +The World Wide Web + +http://www.bitlbee.org/ is the homepage of bitlbee and contains the most recent +news on bitlbee and the latest releases. + +IRC + +BitlBee is discussed on #bitlbee on the OFTC IRC network (server: +irc.oftc.net). + +Mailinglists + +BitlBee doesn't have any mailinglists. + +Chapter 4. Quickstart + +Table of Contents + +Add and Connect To your IM Account(s) +Managing Contact Lists: Rename +Step Four: Managing Contact Lists: Add and Remove. +Chatting +Further Resources + +Welcome to BitlBee, your IRC gateway to ICQ, MSN, AOL, Jabber and Yahoo Instant +Messaging Systems. + +The center of BitlBee is the control channel, #bitlbee. Two users will always +be there, you (where "you" is the nickname you are using) and the system user, +root. + +You need register so that all your IM settings (passwords, contacts, etc) can +be saved on the BitlBee server. It's important that you pick a good password so +no one else can access your account. Register with this password using the +register command: register <password> (without the brackets!). + +Be sure to remember your password. The next time you connect to the BitlBee +server you will need to identify <password> so that you will be recognised and +logged in to all the IM services automatically. + +When finished, type help quickstart2 to continue. + +Add and Connect To your IM Account(s) + +Step Two: Add and Connect To your IM Account(s). + +To add an account to the account list you will need to use the account add +command: account add <protocol> <username> <password> [<server>]. + +For instance, suppose you have an ICQ account with UIN 72696705 with password +QuickStart, you would: + +< you> account add oscar 72696705 QuickStart login.icq.com +< root> Account successfully added + +Other available IM protocols are jabber, msn, and yahoo. Oscar is the protocol +used by ICQ and AOL. For oscar, you need to specify the IM-server as a fourth +argument (for msn and yahoo there is no fourth argument). For AOL Instant +Messenger, the server name is login.oscar.aol.com. For ICQ, the server name is +login.icq.com. + +When you are finished adding your account(s) use the account on command to +enable all your accounts, type help quickstart3 to continue. + +Managing Contact Lists: Rename + +Step Three: Managing Contact Lists: Rename + +For most protocols (currently MSN, Jabber, Yahoo and AOL) BitlBee can download +the contact list automatically from the IM server and all the on-line users +should appear in the control channel when you log in. + +BitlBee will convert names into irc-friendly form (for instance: +tux@example.com will be given the nickname tux). If you have more than one +person who would have the same name by this logic (for instance: +tux@example.com and tux@bitlbee.org) the second one to log on will be tux_. The +same is true if you have a tux log on to AOL and a tux log on from Yahoo. + +It would be easy to get these two mixed up, so BitlBee has a rename command to +change the nickname into something more suitable: rename <oldnick> <newnick> + +< you> rename tux_ bitlbeetux + * tux_is now known as bitlbeetux +< root> Nick successfully changed + +When finished, type help quickstart4 to continue. + +Step Four: Managing Contact Lists: Add and Remove. + +Step Four: Managing Contact Lists: Add and Remove. + +Now you might want to add some contacts, to do this we will use the add +command. It needs two arguments: a connection ID (which can be a number (try +account list), protocol name or (part of) the screenname) and the user's +handle. It is used in the following way: add <connection> <handle> + +< you> add 0 r2d2@example.com + * r2d2has joined #bitlbee + +In this case r2d2 is online, since he/she joins the channel immediately. If the +user is not online you will not see them join until they log on. + +Lets say you accidentally added r2d3@example.com rather than r2d2@example.com, +or maybe you just want to remove a user from your list because you never talk +to them. To remove a name you will want to use the remove command: remove +<nick> + +When finished, type help quickstart5 to continue. + +Chatting + +Step Five: Chatting. + +First of all, a person must be on your contact list for you to chat with them +(unless it's a group chat, help groupchats for more). If someone not on your +contact list sends you a message, simply add them to the proper account with +the add command. Once they are on your list and online, you can chat with them +in #bitlbee: + +< you> tux: hey, how's the weather down there? +< tux> you: a bit chilly! + +If you'd rather chat with them in a separate window use the /msg or /query +command, just like you would for a private message in IRC. If you want to have +messages automatically come up in private messages rather than in the #bitlbee +channel, use the set private command: set private true (set private false to +change back). + +You know the basics. If you want to get to know more about BitlBee, please type +help quickstart6. + +Further Resources + +So you want more than just chatting? Or maybe you're just looking for a +feature? + +You can type help set to learn more about the possible BitlBee user settings. +Among these user settings you will find options for common issues, such as +changing the charset, HTML stripping and automatic connecting (simply type set +to see current user settings). + +For more subjects (like groupchats and away states), please type help index. + +If you're still looking for something, please visit us in #bitlbee on the OFTC +network (you can connect via irc.bitlbee.org), or mail us your problem/ +suggestion. Good luck and enjoy the Bee! + +Chapter 5. Bitlbee commands + +Table of Contents + +account - IM-account list maintenance + + account add + account del + account on + account off + account list + +add - Add a buddy to your contact list +info - Request user information +remove - Remove a buddy from your contact list +block - Block someone +allow - Unblock someone +set - Miscellaneous settings +help - BitlBee help system +save - Save your account data +charset +private +save_on_quit +html +debug +to_char +typing_notice +ops +away_devoice +handle_unknown +auto_connect +auto_reconnect +auto_reconnect_delay +buddy_sendbuffer +buddy_sendbuffer_delay +default_target +display_namechanges +password +query_order +lcnicks +rename - Rename (renick) a buddy +yes - Accept a request +no - Deny a request +qlist - List all the unanswered questions root asked +register - Register yourself +identify - Identify yourself with your password +drop - Drop your account +blist - List all the buddies in your contact list +nick - Change friendly name, nick +import_buddies - Copy local buddy list to server (normally only needed when + upgrading) + +account - IM-account list maintenance + +Syntax: + +account <action> [<arguments>] + +Available actions: add, del, list, on, off. See help account <action> for more +information. + +account add + +Syntax: + +account add <protocol> <username> <password> [<server>] + +Adds an account on the given server with the specified protocol, username and +password to the account list. Supported protocols right now are: Jabber, MSN, +OSCAR (AIM/ICQ) and Yahoo. For more information about adding an account, see +help account add <protocol>. + +account add jabber + +Syntax: + +account add jabber <handle> <password> [<servertag>] + +Note that the servertag argument is optional. You only have to use it if the +part after the @ in your handle isn't the hostname of your Jabber server, or if +you want to use SSL/connect to a non-standard port number. The format is +simple: [<servername>[:<portnumber>][:ssl]]. For example, this is how you can +connect to Google Talk: + +Note that Google talk is SSL-only, but officially reachable over both port 5222 +and 5223. However, for some people only port 5222 works, for some people only +5223. This is something you'll have to try out. + +< wilmer> account add jabber example@gmail.com hobbelmeeuw talk.google.com:5223:ssl +< root> Account successfully added + +account add msn + +Syntax: + +account add msn <handle> <password> + +For MSN connections there are no special arguments. + +account add oscar + +Syntax: + +account add oscar <handle> <password> [<servername>] + +Specifying a server is required for OSCAR, since OSCAR can be used for both +ICQ- and AIM-connections. Although these days it's supposed to be possible to +connect to ICQ via AIM-servers and vice versa, we like to stick with this +separation for now. For ICQ connections, the servername is login.icq.com, for +AIM connections it's login.oscar.aol.com. + +< wilmer> account add oscar 72696705 hobbelmeeuw login.icq.com +< root> Account successfully added + +account add yahoo + +Syntax: + +account add yahoo <handle> <password> + +For Yahoo! connections there are no special arguments. + +account del + +Syntax: + +account del <account id> + +This commands deletes an account from your account list. You should signoff the +account before deleting it. + +The account ID can be a number (see account list), the protocol name or (part +of) the screenname, as long as it matches only one connection. + +account on + +Syntax: + +account on [<account id>] + +This command will try to log into the specified account. If no account is +specified, BitlBee will log into all the accounts. (Including accounts awaiting +a reconnection) + +The account ID can be a number (see account list), the protocol name or (part +of) the screenname, as long as it matches only one connection. + +account off + +Syntax: + +account off [<account id>] + +This command disconnects the connection for the specified account. If no +account is specified, BitlBee will deactivate all active accounts. (Including +accounts awaiting a reconnection) + +The account ID can be a number (see account list), the protocol name or (part +of) the screenname, as long as it matches only one connection. + +account list + +Syntax: + +account list + +This command gives you a list of all the accounts known by BitlBee, including +the numbers you'll need for most account commands. + +add - Add a buddy to your contact list + +Syntax: + +add <connection> <handle> [<nick>] + +Adds the given buddy at the specified connection to your buddy list. The +account ID can be a number (see account list), the protocol name or (part of) +the screenname, as long as it matches only one connection. + +If you want, you can also tell BitlBee what nick to give the new contact. Of +course you can also use the rename command for that, but sometimes this might +be more convenient. + +< ctrlsoft> add 3 gryp@jabber.org grijp + * grijphas joined #bitlbee + +info - Request user information + +Syntax: + +info <connection> <handle> +info <nick> + +Requests IM-network-specific information about the specified user. The amount +of information you'll get differs per protocol. For some protocols (ATM Yahoo! +and MSN) it'll give you an URL which you can visit with a normal web browser to +get the information. + +< ctrlsoft> info 0 72696705 +< root> User info - UIN: 72696705 Nick: Lintux First/Last name: Wilmer van der Gaast E-mail: lintux@lintux.cx + +remove - Remove a buddy from your contact list + +Syntax: + +remove <nick> + +Removes the specified nick from your buddy list. + +< ctrlsoft> remove gryp + * gryphas quit [Leaving...] + +block - Block someone + +Syntax: + +block <nick> +block <connection> <handle> + +Puts the specified user on your ignore list. Either specify the user's nick +when you have him/her in your contact list or a connection number and a user +handle. + +allow - Unblock someone + +Syntax: + +allow <nick> +allow <connection> <handle> + +Reverse of block. Unignores the specified user or user handle on specified +connection. + +set - Miscellaneous settings + +Syntax: + +set [<variable> [<value>]] + +Without any arguments, this command lists all the set variables. You can also +specify a single argument, a variable name, to get that variable's value. To +change this value, specify the new value as the second argument. + +To get more help information about a setting, try: + +< ctrlsoft> help set private + +help - BitlBee help system + +Syntax: + +help [subject] + +This command gives you the help information you're reading right now. If you +don't give any arguments, it'll give a short help index. + +save - Save your account data + +Syntax: + +save + +This command saves all your nicks and accounts immediately. Handy if you have +the autosave functionality disabled, or if you don't trust the program's +stability... ;-) + +charset + +Type: string + +The charset setting enables you to use different character sets in BitlBee. +These get converted to UTF-8 before sending and from UTF-8 when receiving. + +If you don't know what's the best value for this, at least iso8859-1 is the +best choice for most Western countries. You can try to find what works best for +you on http://czyborra.com/charsets/iso8859.html + +private + +Type: boolean + +If value is true, messages from users will appear in separate query windows. If +false, messages from users will appear in the control channel. + +This setting is remembered (during one session) per-user, this setting only +changes the default state. This option takes effect as soon as you reconnect. + +save_on_quit + +Type: boolean + +If enabled causes BitlBee to save all current settings and account details when +user disconnects. This is enabled by default, and these days there's not really +a reason to have it disabled anymore. + +html + +Type: string + +Determines what BitlBee should do with HTML in messages. If set to nostrip, +HTML in messages will not be touched. If set to strip, all HTML will be +stripped from messages. Unfortunately this sometimes strips too much. + +debug + +Type: boolean + +Some debugging messages can be sent to the control channel if you wish. They're +probably not really useful for you, unless you're doing some development on +BitlBee. + +to_char + +Type: string + +It's customary that messages meant for one specific person on an IRC channel +are prepended by his/her alias followed by a colon ':'. BitlBee does this by +default. If you prefer a different character, you can set it using set to_char. + +Please note that this setting is only used for incoming messages. For outgoing +messages you can use ':' (colon) or ',' to separate the destination nick from +the message, and this is not configurable. + +typing_notice + +Type: boolean + +Sends you a /notice when a user starts typing a message (if the protocol +supports it, MSN for example). This is a bug, not a feature. (But please don't +report it.. ;-) You don't want to use it. Really. In fact the +typing-notification is just one of the least useful 'innovations' ever. It's +just there because some guy will probably ask me about it anyway. ;-) + +ops + +Type: string + +Some people prefer themself and root to have operator status in #bitlbee, other +people don't. You can change these states using this setting. + +The value "both" means both user and root get ops. "root" means, well, just +root. "user" means just the user. "none" means nobody will get operator status. + +away_devoice + +Type: boolean + +With this option enabled, the root user devoices people when they go away (just +away, not offline) and gives the voice back when they come back. You might +dislike the voice-floods you'll get if your contact list is huge, so this +option can be disabled. + +handle_unknown + +Type: string + +Messages from unknown users are echoed like this by default: + +If you want this lame user to be added automatically, you can set this setting +to "add". If you prefer to ignore messages from people you don't know, you can +set this one to "ignore". "add_private" and "add_channel" are like add, but you +can use them to make messages from unknown buddies appear in the channel +instead of a query window. + +auto_connect + +Type: boolean + +With this option enabled, when you identify BitlBee will automatically connect +to your accounts, with this disabled it will not do this. + +auto_reconnect + +Type: boolean + +If an IM-connections breaks, you're supposed to bring it back up yourself. +Having BitlBee do this automatically might not always be a good idea, for +several reasons. If you want the connections to be restored automatically, you +can enable this setting. + +See also the auto_reconnect_delay setting. + +auto_reconnect_delay + +Type: integer + +Tell BitlBee after how many seconds it should attempt to bring an IM-connection +back up after a crash. It's not a good idea to set this value very low, it will +cause too much useless traffic when an IM-server is down for a few hours. + +See also the auto_reconnect setting. + +buddy_sendbuffer + +Type: boolean + +By default, when you send a message to someone, BitlBee forwards this message +to the user immediately. When you paste a large number of lines, the lines will +be sent in separate messages, which might not be very nice to read. If you +enable this setting, BitlBee will buffer your messages and wait for more data. + +Using the buddy_sendbuffer_delay setting you can specify the number of seconds +BitlBee should wait for more data before the complete message is sent. + +Please note that if you remove a buddy from your list (or if the connection to +that user drops) and there's still data in the buffer, this data will be lost. +BitlBee will not try to send the message to the user in those cases. + +buddy_sendbuffer_delay + +Type: integer + +Tell BitlBee after how many seconds a buffered message should be sent. + +See also the buddy_sendbuffer setting. + +default_target + +Type: string + +With this value set to root, lines written in the control channel without any +nickname in front of them will be interpreted as commands. If you want BitlBee +to send those lines to the last person you addressed in the control channel, +set this to last. + +display_namechanges + +Type: boolean + +password + +Type: string + +Use this setting to change your "NickServ" password. + +query_order + +Type: string + +This changes the order in which the questions from root (usually authorization +requests from buddies) should be answered. When set to lifo, BitlBee +immediately displays all new questions and they should be answered in reverse +order. When this is set to fifo, BitlBee displays the first question which +comes in and caches all the others until you answer the first one. + +Although the fifo setting might sound more logical (and used to be the default +behaviour in older BitlBee versions), it turned out not to be very convenient +for many users when they missed the first question (and never received the next +ones). + +lcnicks + +Type: boolean + +Hereby you can change whether you want all lower case nick names or leave the +case as it intended by your peer. + +rename - Rename (renick) a buddy + +Syntax: + +rename <oldnick> <newnick> + +Renick a user in your buddy list. Very useful, in fact just very important, if +you got a lot of people with stupid account names (or hard ICQ numbers). + +< itsme> rename itsme_ you + * itsme_is now known as you + +yes - Accept a request + +Syntax: + +yes [<number>] + +Sometimes an IM-module might want to ask you a question. (Accept this user as +your buddy or not?) To accept a question, use the yes command. + +By default, this answers the first unanswered question. You can also specify a +different question as an argument. You can use the qlist command for a list of +questions. + +no - Deny a request + +Syntax: + +no [<number>] + +Sometimes an IM-module might want to ask you a question. (Accept this user as +your buddy or not?) To reject a question, use the no command. + +By default, this answers the first unanswered question. You can also specify a +different question as an argument. You can use the qlist command for a list of +questions. + +qlist - List all the unanswered questions root asked + +Syntax: + +qlist + +This gives you a list of all the unanswered questions from root. + +register - Register yourself + +Syntax: + +register <password> + +BitlBee can save your settings so you won't have to enter all your IM passwords +every time you log in. If you want the Bee to save your settings, use the +register command. + +Please do pick a secure password, don't just use your nick as your password. +Please note that IRC is not an encrypted protocol, so the passwords still go +over the network in plaintext. Evil people with evil sniffers will read it all. +(So don't use your root password.. ;-) + +To identify yourself in later sessions, you can use the identify command. + +identify - Identify yourself with your password + +Syntax: + +identify <password> + +BitlBee saves all your settings (contacts, accounts, passwords) on-server. To +prevent other users from just logging in as you and getting this information, +you'll have to identify yourself with your password. You can register this +password using the register command. + +Once you're registered, you can change your password using set password +<password>. + +drop - Drop your account + +Syntax: + +drop <password> + +Drop your BitlBee registration. Your account files will be removed and your +password will be forgotten. For obvious security reasons, you have to specify +your NickServ password to make this command work. + +blist - List all the buddies in your contact list + +Syntax: + +blist [all|online|offline|away] + +You can get a better readable buddy list using the blist command. If you want a +complete list (including the offline users) you can use the all argument. + +nick - Change friendly name, nick + +Syntax: + +nick <connection> [<new nick>] +nick + +This command allows to set the friendly name of an im account. If no new name +is specified the command will report the current name. When the name contains +spaces, don't forget to quote the whole nick in double quotes. Currently this +command is only supported by the MSN protocol. + +< wouter> nick 1 "Wouter Paesen" +< root> Setting your name on connection 1 to `Wouter Paesen' + +import_buddies - Copy local buddy list to server (normally only needed when +upgrading) + +Syntax: + +import_buddies <connection> [clear] + +This command copies the locally stored buddy list to the server. This command +exists for upgrading purposes. Previous versions of BitlBee didn't support +server-side buddy lists for ICQ, so the list was stored locally. + +Since version 0.91 however, server-side contact lists are supported for all +protocols, so the local list is now ignored. When upgrading from an older +BitlBee to version 0.91, you might need this command to get your buddy list +back. + +The only argument this command needs is your ICQ account identification. If +your serverside buddy list contains some old buddies you don't want anymore, +you can pass clear as a second argument. + +After giving this command, you have to wait for a while before all the adds are +handled, because of ICQ's rate limiting. If your buddy list is very large and +the ICQ server starts complaining, you might have to reconnect and enter this +command again. + +Chapter 6. Misc Stuff + +Table of Contents + +Smileys +Groupchats +Creating groupchats +Groupchat channel names +Away states + +Smileys + +All MSN smileys (except one) are case insensitive and work without the nose +too. + +(Y) + + Thumbs up + +(N) + + Thumbs down + +(B) + + Beer mug + +(D) + + Martini glass + +(X) + + Girl + +(Z) + + Boy + +(6) + + Devil smiley + +:-[ + + Vampire bat + +(}) + + Right hug + +({) + + Left hug + +(M) + + MSN Messenger or Windows Messenger icon (think a BitlBee logo here ;) + +:-S + + Crooked smiley (Confused smiley) + +:-$ + + Embarrassed smiley + +(H) + + Smiley with sunglasses + +:-@ + + Angry smiley + +(A) + + Angel smiley + +(L) + + Red heart (Love) + +(U) + + Broken heart + +(K) + + Red lips (Kiss) + +(G) + + Gift with bow + +(F) + + Red rose + +(W) + + Wilted rose + +(P) + + Camera + +(~) + + Film strip + +(T) + + Telephone receiver + +(@) + + Cat face + +(&) + + Dog's head + +(C) + + Coffee cup + +(I) + + Light bulb + +(S) + + Half-moon (Case sensitive!) + +(*) + + Star + +(8) + + Musical eighth note + +(E) + + Envelope + +(^) + + Birthday cake + +(O) + + Clock + +This list was extracted from http://help.msn.com/!data/en_us/data/ +messengerv50.its51/$content$/EMOTICONS.HTM?H_APP=. + +Groupchats + +Since version 0.8x, BitlBee supports groupchats on the MSN and Yahoo! networks. +This text will try to explain you how they work. + +As soon as someone invites you into a groupchat, you will be force-joined or +invited (depending on the protocol) into a new virtual channel with all the +people in there. You can leave the channel at any time, just like you would +close the window in regular IM clients. Please note that root-commands don't +work in groupchat channels, they only work in the control channel (or to root +directly). + +Of course you can also create your own groupchats. Type help groupchats2 to see +how. + +Creating groupchats + +If you want to start a groupchat with the person jim_msn in it, just join the +channel #jim_msn. BitlBee will refuse to join you to the channel with that +name, but it will create a new virtual channel with root, you and jim_msn in +it. + +Of course a channel with only two people isn't really exciting yet. So the next +step is to invite some other people to the channel. For this, you can use the / +invite command of your IRC client. Please do keep in mind that all the people +have to be on the same network and contact list! You can't invite Yahoo! +buddies into an MSN groupchat. + +This is all you'll probably need to know. If you have any problems, please read +help groupchats3. + +Groupchat channel names + +Obviously the (numbered) channel names don't make a lot of sense. Problem is +that groupchats usually don't have names at all in the IM-world, while IRC +insists on a name. So BitlBee just generates something random, just don't pay +attention to it. :-) + +Please also note that BitlBee doesn't support groupchats for all protocols yet. +BitlBee will tell you so. Support for other protocols will hopefully come +later. + +Away states + +As you might've expected, you can just use the /away command in your IRC client +to set an away-state. BitlBee supports most away-states supported by the +protocols. + +Not all away states are supported by all protocols, and some protocols have +different names for them. BitlBee will try to pick the best available alias +from this list for every connection: + +Away from computer, Away, Extended away +NA, N/A, Not available +Busy, Do not disturb, DND, Occupied +Be right back, BRB +On the phone, Phone, On phone +Out to lunch, Lunch, Food + +So /away Food will set your state to "Out to lunch" on your MSN connection, and +for most other connections the default, "Away" or "Away from computer" will be +chosen. + +You can also add more information to your away message. Setting it to "Busy - +Fixing BitlBee bugs" will set your IM-away-states to Busy, but your away +message will be more descriptive for people on IRC. Protocols like Yahoo! and +Jabber will also show this complete away message to your buddies. + diff --git a/doc/user-guide/user-guide.xml b/doc/user-guide/user-guide.xml new file mode 100644 index 00000000..eff5998e --- /dev/null +++ b/doc/user-guide/user-guide.xml @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"> + +<book id="BitlBee-User-Guide" xmlns:xi="http://www.w3.org/2003/XInclude"> +<bookinfo> + <title>BitlBee User Guide</title> + <author> + <firstname>Jelmer</firstname><surname>Vernooij</surname> + <address><email>jelmer@samba.org</email></address> + </author> + + <author> + <firstname>Wilmer</firstname><surname>van der Gaast</surname> + <address><email>wilmer@gaast.net</email></address> + </author> + + <author> + <firstname>Sjoerd</firstname><surname>Hemminga</surname> + <address><email>sjoerd@huiswerkservice.nl</email></address> + </author> + + <legalnotice id="legalnotice"> + <para> + Permission is granted to copy, distribute and/or modify this + document under the terms of the <ulink type="help" + url="gnome-help:fdl"><citetitle>GNU Free Documentation + License</citetitle></ulink>, Version 1.1 or any later version + published by the Free Software Foundation with no Invariant + Sections, no Front-Cover Texts, and no Back-Cover Texts. You + may obtain a copy of the <citetitle>GNU Free Documentation + License</citetitle> from the Free Software Foundation by + visiting <ulink type="http" url="http://www.fsf.org">their + Web site</ulink> or by writing to: Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. + </para> + </legalnotice> + + <releaseinfo> + This is the initial release of the BitlBee User Guide. + </releaseinfo> + + </bookinfo> + + <xi:include href="Installation.xml"/> + <xi:include href="Usage.xml"/> + <xi:include href="Support.xml"/> + <xi:include href="quickstart.xml"/> + <xi:include href="commands.xml"/> + <xi:include href="misc.xml"/> + +</book> @@ -0,0 +1,148 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Help file control */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#define BITLBEE_CORE +#include "bitlbee.h" +#undef read +#undef write + +#define BUFSIZE 1100 + +help_t *help_init( help_t **help ) +{ + int i, buflen = 0; + help_t *h; + char *s, *t; + time_t mtime; + struct stat stat[1]; + + *help = h = g_new0 ( help_t, 1 ); + + h->fd = open( global.helpfile, O_RDONLY +#ifdef _WIN32 + | O_BINARY +#endif + ); + + if( h->fd == -1 ) + { + g_free( h ); + return( *help = NULL ); + } + + if( fstat( h->fd, stat ) != 0 ) + { + g_free( h ); + return( *help = NULL ); + } + mtime = stat->st_mtime; + + s = g_new (char, BUFSIZE + 1 ); + s[BUFSIZE] = 0; + + while( ( ( i = read( h->fd, s + buflen, BUFSIZE - buflen ) ) > 0 ) || + ( i == 0 && strstr( s, "\n%\n" ) ) ) + { + buflen += i; + memset( s + buflen, 0, BUFSIZE - buflen ); + if( !( t = strstr( s, "\n%\n" ) ) || s[0] != '?' ) + { + /* FIXME: Clean up */ +// help_close( *help ); + *help = NULL; + g_free( s ); + return( NULL ); + } + i = strchr( s, '\n' ) - s; + + if( h->string ) + { + h = h->next = g_new0( help_t, 1 ); + } + h->string = g_new ( char, i ); + + strncpy( h->string, s + 1, i - 1 ); + h->string[i-1] = 0; + h->fd = (*help)->fd; + h->offset.file_offset = lseek( h->fd, 0, SEEK_CUR ) - buflen + i + 1; + h->length = t - s - i - 1; + h->mtime = mtime; + + buflen -= ( t + 3 - s ); + t = g_strdup( t + 3 ); + g_free( s ); + s = g_renew( char, t, BUFSIZE + 1 ); + s[BUFSIZE] = 0; + } + + g_free( s ); + + return( *help ); +} + +char *help_get( help_t **help, char *string ) +{ + time_t mtime; + struct stat stat[1]; + help_t *h; + + h=*help; + + while( h ) + { + if( g_strcasecmp( h->string, string ) == 0 ) break; + h = h->next; + } + if( h ) + { + char *s = g_new( char, h->length + 1 ); + + if( fstat( h->fd, stat ) != 0 ) + { + g_free( h ); + *help=NULL; + return( NULL ); + } + mtime = stat->st_mtime; + + if( mtime > h->mtime ) { + return( NULL ); + return( g_strdup( "Help file changed during this session. Please restart to get help back." ) ); + } + s[h->length] = 0; + if( h->fd >= 0 ) + { + lseek( h->fd, h->offset.file_offset, SEEK_SET ); + read( h->fd, s, h->length ); + } + else + { + strncpy( s, h->offset.mem_offset, h->length ); + } + return( s ); + } + + return( NULL ); +} @@ -0,0 +1,48 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Help file control */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef _HELP_H +#define _HELP_H + +typedef union +{ + off_t file_offset; + char *mem_offset; +} help_off_t; + +typedef struct help +{ + int fd; + time_t mtime; + char *string; + help_off_t offset; + int length; + struct help *next; +} help_t; + +help_t *help_init( help_t **help ); +char *help_get( help_t **help, char *string ); + +#endif @@ -0,0 +1,90 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* INI file reading code */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ +#define BITLBEE_CORE +#include "bitlbee.h" + +ini_t *ini_open( char *file ) +{ + ini_t *ini = g_new0( ini_t, 1 ); + + if( ( ini->fp = fopen( file, "r" ) ) == NULL ) + { + g_free( ini ); + return( NULL ); + } + + return( ini ); +} + +int ini_read( ini_t *file ) +{ + char key[MAX_STRING], s[MAX_STRING], *t; + int i; + + while( !feof( file->fp ) ) + { + *s = 0; + fscanf( file->fp, "%127[^\n#]s", s ); + fscanf( file->fp, "%*[^\n]s" ); + fgetc( file->fp ); /* Skip newline */ + file->line ++; + if( strchr( s, '=' ) ) + { + sscanf( s, "%[^ =]s", key ); + if( ( t = strchr( key, '.' ) ) ) + { + *t = 0; + strcpy( file->section, key ); + t ++; + } + else + { + strcpy( file->section, file->c_section ); + t = key; + } + sscanf( t, "%s", file->key ); + t = strchr( s, '=' ) + 1; + for( i = 0; t[i] == ' '; i ++ ); + strcpy( file->value, &t[i] ); + for( i = strlen( file->value ) - 1; file->value[i] == 32; i -- ) + file->value[i] = 0; + + return( 1 ); + } + else if( ( t = strchr( s, '[' ) ) ) + { + strcpy( file->c_section, t + 1 ); + t = strchr( file->c_section, ']' ); + *t = 0; + } + } + return( 0 ); +} + +void ini_close( ini_t *file ) +{ + fclose( file->fp ); + g_free( file ); +} @@ -0,0 +1,43 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* INI file reading code */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef _INI_H +#define _INI_H + +typedef struct +{ + FILE *fp; + int line; + char c_section[MAX_STRING]; + char section[MAX_STRING]; + char key[MAX_STRING]; + char value[MAX_STRING]; +} ini_t; + +ini_t *ini_open( char *file ); +int ini_read( ini_t *file ); +void ini_close( ini_t *file ); + +#endif @@ -0,0 +1,1573 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* The big hairy IRCd part of the project */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#define BITLBEE_CORE +#include "bitlbee.h" +#include "crypting.h" + +static gboolean irc_userping( gpointer _irc ); + +GSList *irc_connection_list = NULL; + +irc_t *irc_new( int fd ) +{ + irc_t *irc = g_new0( irc_t, 1 ); + + struct sockaddr_in sock[1]; +#ifdef IPV6 + struct sockaddr_in6 sock6[1]; +#endif + struct hostent *peer; + unsigned int i, j; + + irc->fd = fd; + irc->io_channel = g_io_channel_unix_new( fd ); +#ifdef GLIB2 + g_io_channel_set_encoding (irc->io_channel, NULL, NULL); + g_io_channel_set_buffered (irc->io_channel, FALSE); + g_io_channel_set_flags( irc->io_channel, G_IO_FLAG_NONBLOCK, NULL ); +#else + fcntl( irc->fd, F_SETFL, O_NONBLOCK); +#endif + irc->r_watch_source_id = g_io_add_watch( irc->io_channel, G_IO_IN | G_IO_ERR | G_IO_HUP, bitlbee_io_current_client_read, irc ); + + irc->status = USTATUS_OFFLINE; + irc->last_pong = gettime(); + + irc->userhash = g_hash_table_new( g_str_hash, g_str_equal ); + irc->watches = g_hash_table_new( g_str_hash, g_str_equal ); + + strcpy( irc->umode, UMODE ); + irc->mynick = g_strdup( ROOT_NICK ); + irc->channel = g_strdup( ROOT_CHAN ); + + i = sizeof( *sock ); +#ifdef IPV6 + j = sizeof( *sock6 ); +#endif + if( global.conf->hostname ) + irc->myhost = g_strdup( global.conf->hostname ); + else if( getsockname( irc->fd, (struct sockaddr*) sock, &i ) == 0 && sock->sin_family == AF_INET ) + { + if( ( peer = gethostbyaddr( (char*) &sock->sin_addr, sizeof( sock->sin_addr ), AF_INET ) ) ) + irc->myhost = g_strdup( peer->h_name ); + } +#ifdef IPV6 + else if( getsockname( irc->fd, (struct sockaddr*) sock6, &j ) == 0 && sock6->sin6_family == AF_INET6 ) + { + if( ( peer = gethostbyaddr( (char*) &sock6->sin6_addr, sizeof( sock6->sin6_addr ), AF_INET6 ) ) ) + irc->myhost = g_strdup( peer->h_name ); + } +#endif + + i = sizeof( *sock ); +#ifdef IPV6 + j = sizeof( *sock6 ); +#endif + if( getpeername( irc->fd, (struct sockaddr*) sock, &i ) == 0 && sock->sin_family == AF_INET ) + { + if( ( peer = gethostbyaddr( (char*) &sock->sin_addr, sizeof( sock->sin_addr ), AF_INET ) ) ) + irc->host = g_strdup( peer->h_name ); + } +#ifdef IPV6 + else if( getpeername( irc->fd, (struct sockaddr*) sock6, &j ) == 0 && sock6->sin6_family == AF_INET6 ) + { + if( ( peer = gethostbyaddr( (char*) &sock6->sin6_addr, sizeof( sock6->sin6_addr ), AF_INET6 ) ) ) + irc->host = g_strdup( peer->h_name ); + } +#endif + + if( !irc->host ) irc->host = g_strdup( "localhost." ); + if( !irc->myhost ) irc->myhost = g_strdup( "localhost." ); + + if( global.conf->ping_interval > 0 && global.conf->ping_timeout > 0 ) + irc->ping_source_id = g_timeout_add( global.conf->ping_interval * 1000, irc_userping, irc ); + + irc_write( irc, ":%s NOTICE AUTH :%s", irc->myhost, "BitlBee-IRCd initialized, please go on" ); + + irc_connection_list = g_slist_append( irc_connection_list, irc ); + + set_add( irc, "away_devoice", "true", set_eval_away_devoice ); + set_add( irc, "auto_connect", "true", set_eval_bool ); + set_add( irc, "auto_reconnect", "false", set_eval_bool ); + set_add( irc, "auto_reconnect_delay", "300", set_eval_int ); + set_add( irc, "buddy_sendbuffer", "false", set_eval_bool ); + set_add( irc, "buddy_sendbuffer_delay", "1", set_eval_int ); + set_add( irc, "charset", "iso8859-1", set_eval_charset ); + set_add( irc, "debug", "false", set_eval_bool ); + set_add( irc, "default_target", "root", NULL ); + set_add( irc, "display_namechanges", "false", set_eval_bool ); + set_add( irc, "handle_unknown", "root", NULL ); + /* set_add( irc, "html", "nostrip", NULL ); */ + set_add( irc, "lcnicks", "true", set_eval_bool ); + set_add( irc, "ops", "both", set_eval_ops ); + set_add( irc, "private", "true", set_eval_bool ); + set_add( irc, "query_order", "lifo", NULL ); + set_add( irc, "save_on_quit", "true", set_eval_bool ); + set_add( irc, "to_char", ": ", set_eval_to_char ); + set_add( irc, "typing_notice", "false", set_eval_bool ); + + conf_loaddefaults( irc ); + + return( irc ); +} + +static gboolean irc_free_userhash( gpointer key, gpointer value, gpointer data ) +{ + g_free( key ); + + return( TRUE ); +} + +/* Because we have no garbage collection, this is quite annoying */ +void irc_free(irc_t * irc) +{ + account_t *account, *accounttmp; + user_t *user, *usertmp; + nick_t *nick, *nicktmp; + help_t *helpnode, *helpnodetmp; + set_t *setnode, *setnodetmp; + + log_message( LOGLVL_INFO, "Destroying connection with fd %d", irc->fd ); + + if( irc->status >= USTATUS_IDENTIFIED && set_getint( irc, "save_on_quit" ) ) + if( !bitlbee_save( irc ) ) + irc_usermsg( irc, "Error while saving settings!" ); + + if( irc->ping_source_id > 0 ) + g_source_remove( irc->ping_source_id ); + g_source_remove( irc->r_watch_source_id ); + if( irc->w_watch_source_id > 0 ) + g_source_remove( irc->w_watch_source_id ); + g_io_channel_close( irc->io_channel ); + g_io_channel_unref( irc->io_channel ); + irc_connection_list = g_slist_remove( irc_connection_list, irc ); + + for (account = irc->accounts; account; account = account->next) + if (account->gc) + signoff(account->gc); + + g_free(irc->sendbuffer); + g_free(irc->readbuffer); + + g_free(irc->nick); + g_free(irc->user); + g_free(irc->host); + g_free(irc->realname); + g_free(irc->password); + + g_free(irc->myhost); + g_free(irc->mynick); + + g_free(irc->channel); + + while (irc->queries != NULL) + query_del(irc, irc->queries); + + if (irc->accounts != NULL) { + account = irc->accounts; + while (account != NULL) { + g_free(account->user); + g_free(account->pass); + g_free(account->server); + accounttmp = account; + account = account->next; + g_free(accounttmp); + } + } + + if (irc->users != NULL) { + user = irc->users; + while (user != NULL) { + g_free(user->nick); + g_free(user->away); + g_free(user->handle); + if(user->user!=user->nick) g_free(user->user); + if(user->host!=user->nick) g_free(user->host); + if(user->realname!=user->nick) g_free(user->realname); + gaim_input_remove(user->sendbuf_timer); + + usertmp = user; + user = user->next; + g_free(usertmp); + } + } + + g_hash_table_foreach_remove(irc->userhash, irc_free_userhash, NULL); + g_hash_table_destroy(irc->userhash); + + g_hash_table_foreach_remove(irc->watches, irc_free_userhash, NULL); + g_hash_table_destroy(irc->watches); + + if (irc->nicks != NULL) { + nick = irc->nicks; + while (nick != NULL) { + g_free(nick->nick); + g_free(nick->handle); + + nicktmp = nick; + nick = nick->next; + g_free(nicktmp); + } + } + if (irc->help != NULL) { + helpnode = irc->help; + while (helpnode != NULL) { + g_free(helpnode->string); + + helpnodetmp = helpnode; + helpnode = helpnode->next; + g_free(helpnodetmp); + } + } + if (irc->set != NULL) { + setnode = irc->set; + while (setnode != NULL) { + g_free(setnode->key); + g_free(setnode->def); + g_free(setnode->value); + + setnodetmp = setnode; + setnode = setnode->next; + g_free(setnodetmp); + } + } + g_free(irc); + + if( global.conf->runmode == RUNMODE_INETD ) + g_main_quit( global.loop ); +} + +int irc_process( irc_t *irc ) +{ + char **lines, *temp; + int i; + + if( irc->readbuffer != NULL ) { + lines = irc_tokenize(irc->readbuffer ); + for( i = 0; *lines[i] != '\0'; i++ ) { + if( lines[i+1] == NULL ) { + temp = g_strdup( lines[i] ); + g_free( irc->readbuffer ); + irc->readbuffer = temp; + i++; + break; + } + if (!irc_process_line(irc, lines[i])) { + g_free( lines ); + return 0; + } + } + if(lines[i]!=NULL) { + g_free(irc->readbuffer); + irc->readbuffer=NULL; + } + g_free( lines ); + } + return 1; +} + +char **irc_tokenize( char *buffer ) +{ + int i, j; + char **lines; + + /* Count the number of elements we're gonna need. */ + for(i=0, j=1; buffer[i]!='\0'; i++ ) { + if(buffer[i]=='\n' ) + if(buffer[i+1]!='\r' && buffer[i+1]!='\n') + j++; + } + + /* Allocate j+1 elements. */ + lines=g_new (char *, j+1); + + /* NULL terminate our list. */ + lines[j]=NULL; + + lines[0]=buffer; + + /* Split the buffer in several strings, using \r\n as our seperator, where \r is optional. + * Although this is not in the RFC, some braindead ircds (newnet's) use this, so some clients might too. + */ + for( i=0, j=0; buffer[i]!='\0'; i++) { + if(buffer[i]=='\n') { + buffer[i]='\0'; + + /* We dont want to read 1 byte before our buffer + * and (in rare cases) generate a SIGSEGV. + */ + if(i!=0) + if(buffer[i-1]=='\r') + buffer[i-1]='\0'; + if(buffer[i+1]!='\r'&&buffer[i+1]!='\n') + lines[++j]=buffer+i+1; + } + } + + return(lines); +} + +int irc_process_line( irc_t *irc, char *line ) +{ + int i, j; + char **cmd; + + /* Move the line pointer to the start of the command, skipping spaces and the optional prefix. */ + if(line[0]==':') { + for(i=0; line[i]!=32; i++); + line=line+i; + } + for(i=0; line[i]==32; i++); + line=line+i; + + /* If we're already at the end of the line, return. If not, we're going to need at least one element. */ + if(line[0]=='\0') + return 1; + else + j=1; + + /* Count the number of char **cmd elements we're going to need. */ + for(i=0; line[i]!='\0'; i++) { + if((line[i]==32) && (line[i+1]!=32) && (line[i+1]!='\0') && (line[i+1]!=':')) + j++; + else if((line[i]==':') && (line[i+1]!='\0') && (line[i-1]==32)) { + j++; + break; + } + + } + + /* Allocate the space we need. */ + cmd=g_new(char *, j+1); + cmd[j]=NULL; + + /* Do the actual line splitting, format is: + * Input: "PRIVMSG #bitlbee :foo bar" + * Output: cmd[0]=="PRIVMSG", cmd[1]=="#bitlbee", cmd[2]=="foo bar", cmd[3]==NULL + */ + + cmd[0]=line; + for(i=0, j=0; line[i]!='\0'; i++) { + if((line[i]==32)) { + line[i]='\0'; + if((line[i+1]!=32) && (line[i+1]!='\0') && (line[i+1]!=':')) + cmd[++j]=line+i+1; + } + else if((line[i]==':') && (line[i+1]!='\0') && (line[i-1]=='\0')) { + cmd[++j]=line+i+1; + break; + } + } + + i=irc_exec(irc, cmd); + g_free(cmd); + + return(i); +} + +int irc_exec( irc_t *irc, char **cmd ) +{ + int i; + + if( (global.conf)->authmode == AUTHMODE_CLOSED && irc->status < USTATUS_AUTHORIZED ) + { + if( g_strcasecmp( cmd[0], "PASS" ) == 0 ) + { + if( !cmd[1] ) + { + irc_reply( irc, 461, "%s :Need more parameters", cmd[0] ); + } + else if( strcmp( cmd[1], (global.conf)->password ) == 0 ) + { + irc->status = USTATUS_AUTHORIZED; + } + else + { + irc_reply( irc, 464, ":Nope, maybe you should try it again..." ); + } + } + else + { + irc_reply( irc, 464, ":Uhh, fine, but I want the password first." ); + } + + return( 1 ); + } + + if( g_strcasecmp( cmd[0], "USER" ) == 0 ) + { + if( !( cmd[1] && cmd[2] && cmd[3] && cmd[4] ) ) + { + irc_reply( irc, 461, "%s :Need more parameters", cmd[0] ); + } + else if( irc->user ) + { + irc_reply( irc, 462, ":You can't change your nick/userinfo" ); + } + else + { + irc->user = g_strdup( cmd[1] ); + irc->realname = g_strdup( cmd[4] ); + if( irc->nick ) irc_login( irc ); + } + return( 1 ); + } + else if( g_strcasecmp( cmd[0], "NICK" ) == 0 ) + { + if( !cmd[1] ) + { + irc_reply( irc, 461, "%s :Need more parameters", cmd[0] ); + } + else if( irc->nick ) + { + irc_reply( irc, 438, ":The hand of the deity is upon thee, thy nick may not change" ); + } + /* This is not clean, but for now it'll have to be like this... */ + else if( ( nick_cmp( cmd[1], irc->mynick ) == 0 ) || ( nick_cmp( cmd[1], NS_NICK ) == 0 ) ) + { + irc_reply( irc, 433, ":This nick is already in use" ); + } + else if( !nick_ok( cmd[1] ) ) + { + /* [SH] Invalid characters. */ + irc_reply( irc, 432, ":This nick contains invalid characters" ); + } + else + { + irc->nick = g_strdup( cmd[1] ); + if( irc->user ) irc_login( irc ); + } + return( 1 ); + } + + if( !irc->user || !irc->nick ) + { + irc_reply( irc, 451, ":Register first" ); + return( 1 ); + } + + if( g_strcasecmp( cmd[0], "PING" ) == 0 ) + { + irc_write( irc, ":%s PONG %s :%s", irc->myhost, irc->myhost, cmd[1]?cmd[1]:irc->myhost ); + } + else if( g_strcasecmp( cmd[0], "MODE" ) == 0 ) + { + if( !cmd[1] ) + { + irc_reply( irc, 461, "%s :Need more parameters", cmd[0] ); + } + else if( *cmd[1] == '#' ) + { + if( cmd[2] ) + { + if( *cmd[2] == '+' || *cmd[2] == '-' ) + irc_reply( irc, 477, "%s :Can't change channel modes", cmd[1] ); + else if( *cmd[2] == 'b' ) + irc_reply( irc, 368, "%s :No bans possible", cmd[1] ); + } + else + irc_reply( irc, 324, "%s +%s", cmd[1], CMODE ); + } + else + { + if( nick_cmp( cmd[1], irc->nick ) == 0 ) + { + if( cmd[2] ) + irc_umode_set( irc, irc->nick, cmd[2] ); + } + else + irc_reply( irc, 502, ":Don't touch their modes" ); + } + } + else if( g_strcasecmp( cmd[0], "NAMES" ) == 0 ) + { + irc_names( irc, cmd[1]?cmd[1]:irc->channel ); + } + else if( g_strcasecmp( cmd[0], "PART" ) == 0 ) + { + struct conversation *c; + + if( !cmd[1] ) + { + irc_reply( irc, 461, "%s :Need more parameters", cmd[0] ); + } + else if( g_strcasecmp( cmd[1], irc->channel ) == 0 ) + { + user_t *u = user_find( irc, irc->nick ); + + /* Not allowed to leave control channel */ + irc_part( irc, u, irc->channel ); + irc_join( irc, u, irc->channel ); + } + else if( ( c = conv_findchannel( cmd[1] ) ) ) + { + user_t *u = user_find( irc, irc->nick ); + + irc_part( irc, u, c->channel ); + + if( c->gc && c->gc->prpl ) + { + c->joined = 0; + c->gc->prpl->chat_leave( c->gc, c->id ); + } + } + else + { + irc_reply( irc, 403, "%s :No such channel", cmd[1] ); + } + } + else if( g_strcasecmp( cmd[0], "JOIN" ) == 0 ) + { + if( !cmd[1] ) + { + irc_reply( irc, 461, "%s :Need more parameters", cmd[0] ); + } + else if( g_strcasecmp( cmd[1], irc->channel ) == 0 ) + ; /* Dude, you're already there... + RFC doesn't have any reply for that though? */ + else if( cmd[1] ) + { + if( cmd[1][0] == '#' && cmd[1][1] ) + { + user_t *u = user_find( irc, cmd[1] + 1 ); + + if( u && u->gc && u->gc->prpl && u->gc->prpl->chat_open ) + { + irc_reply( irc, 403, "%s :Initializing groupchat in a different channel", cmd[1] ); + + if( !u->gc->prpl->chat_open( u->gc, u->handle ) ) + { + irc_usermsg( irc, "Could not open a groupchat with %s, maybe you don't have a connection to him/her yet?", u->nick ); + } + } + else + { + irc_reply( irc, 403, "%s :Groupchats are not possible with %s", cmd[1], cmd[1]+1 ); + } + } + else + { + irc_reply( irc, 403, "%s :No such channel", cmd[1] ); + } + } + } + else if( g_strcasecmp( cmd[0], "INVITE" ) == 0 ) + { + if( cmd[1] && cmd[2] ) + irc_invite( irc, cmd[1], cmd[2] ); + else + irc_reply( irc, 461, "%s :Need more parameters", cmd[0] ); + } + else if( g_strcasecmp( cmd[0], "PRIVMSG" ) == 0 || g_strcasecmp( cmd[0], "NOTICE" ) == 0 ) + { + if( !cmd[1] ) + { + irc_reply( irc, 461, "%s :Need more parameters", cmd[0] ); + } + else if ( !cmd[2] ) + { + irc_reply( irc, 412, ":No text to send" ); + } + else if ( irc->nick && g_strcasecmp( cmd[1], irc->nick ) == 0 ) + { + irc_write( irc, ":%s!%s@%s PRIVMSG %s :%s", irc->nick, irc->user, irc->host, cmd[1], cmd[2] ); + } + else + { + if( g_strcasecmp( cmd[1], irc->channel ) == 0 ) + { + unsigned int i; + char *t = set_getstr( irc, "default_target" ); + + if( g_strcasecmp( t, "last" ) == 0 && irc->last_target ) + cmd[1] = irc->last_target; + else if( g_strcasecmp( t, "root" ) == 0 ) + cmd[1] = irc->mynick; + + for( i = 0; i < strlen( cmd[2] ); i ++ ) + { + if( cmd[2][i] == ' ' ) break; + if( cmd[2][i] == ':' || cmd[2][i] == ',' ) + { + cmd[1] = cmd[2]; + cmd[2] += i; + *cmd[2] = 0; + while( *(++cmd[2]) == ' ' ); + break; + } + } + + irc->is_private = 0; + + if( cmd[1] != irc->last_target ) + { + if( irc->last_target ) + g_free( irc->last_target ); + irc->last_target = g_strdup( cmd[1] ); + } + } + else + { + irc->is_private = 1; + } + irc_send( irc, cmd[1], cmd[2], ( g_strcasecmp( cmd[0], "NOTICE" ) == 0 ) ? IM_FLAG_AWAY : 0 ); + } + } + else if( g_strcasecmp( cmd[0], "QUIT" ) == 0 ) + { + irc_write( irc, "ERROR :%s%s", cmd[1]?"Quit: ":"", cmd[1]?cmd[1]:"Client Quit" ); + g_io_channel_close( irc->io_channel ); + return( 0 ); + } + else if( g_strcasecmp( cmd[0], "WHO" ) == 0 ) + { + irc_who( irc, cmd[1] ); + } + else if( g_strcasecmp( cmd[0], "USERHOST" ) == 0 ) + { + user_t *u; + + if( !cmd[1] ) + { + irc_reply( irc, 461, "%s :Need more parameters", cmd[0] ); + } + /* [TV] Usable USERHOST-implementation according to + RFC1459. Without this, mIRC shows an error + while connecting, and the used way of rejecting + breaks standards. + */ + + for( i = 1; cmd[i]; i ++ ) + if( ( u = user_find( irc, cmd[i] ) ) ) + { + if( u->online && u->away ) + irc_reply( irc, 302, ":%s=-%s@%s", u->nick, u->user, u->host ); + else + irc_reply( irc, 302, ":%s=+%s@%s", u->nick, u->user, u->host ); + } + } + else if( g_strcasecmp( cmd[0], "ISON" ) == 0 ) + { + user_t *u; + char buff[IRC_MAX_LINE]; + int lenleft; + + buff[0] = '\0'; + + /* [SH] Leave room for : and \0 */ + lenleft = IRC_MAX_LINE - 2; + + for( i = 1; cmd[i]; i ++ ) + { + if( ( u = user_find( irc, cmd[i] ) ) && u->online ) + { + /* [SH] Make sure we don't use too much buffer space. */ + lenleft -= strlen( u->nick ) + 1; + + if( lenleft < 0 ) + { + break; + } + + /* [SH] Add the nick to the buffer. Note + * that an extra space is always added. Even + * if it's the last nick in the list. Who + * cares? + */ + + strcat( buff, u->nick ); + strcat( buff, " " ); + } + } + + /* [WvG] Well, maybe someone cares, so why not remove it? */ + if( strlen( buff ) > 0 ) + buff[strlen(buff)-1] = '\0'; + + /* [SH] By the way, that really *was* WvG talking. */ + /* [WvG] Really? */ + /* [SH] Yeah... But *this* is WvG talking too. ;-P */ + /* [WvG] *sigh* */ + + irc_reply( irc, 303, ":%s", buff ); + } + else if( g_strcasecmp( cmd[0], "WATCH" ) == 0 ) + { + /* Obviously we could also mark a user structure as being + watched, but what if the WATCH command is sent right + after connecting? The user won't exist yet then... */ + for( i = 1; cmd[i]; i ++ ) + { + char *nick; + user_t *u; + + if( !cmd[i][0] || !cmd[i][1] ) + break; + + nick = g_strdup( cmd[i] + 1 ); + nick_lc( nick ); + + u = user_find( irc, nick ); + + if( cmd[i][0] == '+' ) + { + if( !g_hash_table_lookup( irc->watches, nick ) ) + g_hash_table_insert( irc->watches, nick, nick ); + + if( u && u->online ) + irc_reply( irc, 604, "%s %s %s %d :%s", u->nick, u->user, u->host, time( NULL ), "is online" ); + else + irc_reply( irc, 605, "%s %s %s %d :%s", nick, "*", "*", time( NULL ), "is offline" ); + } + else if( cmd[i][0] == '-' ) + { + gpointer okey, ovalue; + + if( g_hash_table_lookup_extended( irc->watches, nick, &okey, &ovalue ) ) + { + g_free( okey ); + g_hash_table_remove( irc->watches, okey ); + + irc_reply( irc, 602, "%s %s %s %d :%s", nick, "*", "*", 0, "Stopped watching" ); + } + } + } + } + else if( g_strcasecmp( cmd[0], "TOPIC" ) == 0 ) + { + if( cmd[1] && cmd[2] ) + irc_reply( irc, 482, "%s :Cannot change topic", cmd[1] ); + else if( cmd[1] ) + irc_topic( irc, cmd[1] ); + else + irc_reply( irc, 461, "%s :Need more parameters", cmd[0] ); + } + else if( g_strcasecmp( cmd[0], "AWAY" ) == 0 ) + { + irc_away( irc, cmd[1] ); + } + else if( g_strcasecmp( cmd[0], "WHOIS" ) == 0 ) + { + if( cmd[1] ) + { + irc_whois( irc, cmd[1] ); + } + else + { + irc_reply( irc, 461, "%s :Need more parameters", cmd[0] ); + } + } + else if( g_strcasecmp( cmd[0], "WHOWAS" ) == 0 ) + { + /* For some reason irssi tries a whowas when whois fails. We can + ignore this, but then the user never gets a "user not found" + message from irssi which is a bit annoying. So just respond + with not-found and irssi users will get better error messages */ + + if( cmd[1] ) + { + irc_reply( irc, 406, "%s :Nick does not exist", cmd[1] ); + irc_reply( irc, 369, "%s :End of WHOWAS", cmd[1] ); + } + else + { + irc_reply( irc, 461, "%s :Need more parameters", cmd[0] ); + } + } + else if( ( g_strcasecmp( cmd[0], "NICKSERV" ) == 0 ) || ( g_strcasecmp( cmd[0], "NS" ) == 0 ) ) + { + /* [SH] This aliases the NickServ command to PRIVMSG root */ + /* [TV] This aliases the NS command to PRIVMSG root as well */ + root_command( irc, cmd + 1 ); + } + else if( g_strcasecmp( cmd[0], "MOTD" ) == 0 ) + { + irc_motd( irc ); + } + else if( g_strcasecmp( cmd[0], "PONG" ) == 0 ) + { + /* We could check the value we get back from the user, but in + fact we don't care, we're just happy he's still alive. */ + irc->last_pong = gettime(); + irc->pinging = 0; + } + else if( g_strcasecmp( cmd[0], "COMPLETIONS" ) == 0 ) + { + user_t *u = user_find( irc, irc->mynick ); + help_t *h; + set_t *s; + int i; + + irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS ", "OK" ); + + for( i = 0; commands[i].command; i ++ ) + irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS ", commands[i].command ); + + for( h = global.help; h; h = h->next ) + irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS help ", h->string ); + + for( s = irc->set; s; s = s->next ) + irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS set ", s->key ); + + irc_privmsg( irc, u, "NOTICE", irc->nick, "COMPLETIONS ", "END" ); + } + else if( set_getint( irc, "debug" ) ) + { + irc_usermsg( irc, "\002--- Unknown command:" ); + for( i = 0; cmd[i]; i ++ ) irc_usermsg( irc, "%s", cmd[i] ); + irc_usermsg( irc, "\002--------------------" ); + } + + return( 1 ); +} + +void irc_reply( irc_t *irc, int code, char *format, ... ) +{ + char text[IRC_MAX_LINE]; + va_list params; + + va_start( params, format ); + g_vsnprintf( text, IRC_MAX_LINE, format, params ); + va_end( params ); + irc_write( irc, ":%s %03d %s %s", irc->myhost, code, irc->nick?irc->nick:"*", text ); + + return; +} + +int irc_usermsg( irc_t *irc, char *format, ... ) +{ + char text[1024]; + va_list params; + char is_private = 0; + user_t *u; + + u = user_find( irc, irc->mynick ); + if( u ) is_private = u->is_private; + + va_start( params, format ); + g_vsnprintf( text, sizeof( text ), format, params ); + va_end( params ); + + return( irc_msgfrom( irc, u->nick, text ) ); +} + +void irc_write( irc_t *irc, char *format, ... ) +{ + va_list params; + + va_start( params, format ); + irc_vawrite( irc, format, params ); + va_end( params ); + + return; + +} + +void irc_vawrite( irc_t *irc, char *format, va_list params ) +{ + int size; + char line[IRC_MAX_LINE]; + + if( irc->quit ) + return; + + g_vsnprintf( line, IRC_MAX_LINE - 3, format, params ); + + strip_newlines( line ); + strcat( line, "\r\n" ); + + if( irc->sendbuffer != NULL ) { + size = strlen( irc->sendbuffer ) + strlen( line ); +#ifdef FLOOD_SEND + if( size > FLOOD_SEND_MAXBUFFER ) { + /* Die flooder, die! >:) */ + + g_free(irc->sendbuffer); + + /* We need the \r\n at the start because else we might append our string to a half + * sent line. A bit hackish, but it works. + */ + irc->sendbuffer = g_strdup( "\r\nERROR :Sendq Exceeded\r\n" ); + irc->quit = 1; + + return; + } +#endif + irc->sendbuffer = g_renew ( char, irc->sendbuffer, size + 1 ); + strcpy( ( irc->sendbuffer + strlen( irc->sendbuffer ) ), line ); + } + else + irc->sendbuffer = g_strdup(line); + + if( irc->w_watch_source_id == 0 ) + { + irc->w_watch_source_id = g_io_add_watch( irc->io_channel, G_IO_OUT, bitlbee_io_current_client_write, irc ); + } + + return; +} + +void irc_write_all( char *format, ... ) +{ + va_list params; + GSList *temp; + + va_start( params, format ); + + temp = irc_connection_list; + while( temp!=NULL ) { + irc_vawrite( temp->data, format, params ); + temp = temp->next; + } + + va_end( params ); + return; +} + +void irc_names( irc_t *irc, char *channel ) +{ + user_t *u = irc->users; + char *s; + int control = ( g_strcasecmp( channel, irc->channel ) == 0 ); + struct conversation *c = NULL; + + if( !control ) + c = conv_findchannel( channel ); + + /* RFC's say there is no error reply allowed on NAMES, so when the + channel is invalid, just give an empty reply. */ + + if( control || c ) while( u ) + { + if( u->online ) + { + if( u->gc && control ) + { + if( set_getint( irc, "away_devoice" ) && !u->away ) + s = "+"; + else + s = ""; + + irc_reply( irc, 353, "@ %s :%s%s", channel, s, u->nick ); + } + else if( !u->gc ) + { + if( strcmp( u->nick, irc->mynick ) == 0 && ( strcmp( set_getstr( irc, "ops" ), "root" ) == 0 || strcmp( set_getstr( irc, "ops" ), "both" ) == 0 ) ) + s = "@"; + else if( strcmp( u->nick, irc->nick ) == 0 && ( strcmp( set_getstr( irc, "ops" ), "user" ) == 0 || strcmp( set_getstr( irc, "ops" ), "both" ) == 0 ) ) + s = "@"; + else + s = ""; + + irc_reply( irc, 353, "@ %s :%s%s", channel, s, u->nick ); + } + } + + u = u->next; + } + + /* For non-controlchannel channels (group conversations) only root and + you are listed now. Time to show the channel people: */ + if( !control && c ) + { + GList *l; + + for( l = c->in_room; l; l = l->next ) + if( ( u = user_findhandle( c->gc, l->data ) ) ) + irc_reply( irc, 353, "@ %s :%s%s", channel, "", u->nick ); + } + + irc_reply( irc, 366, "%s :End of /NAMES list", channel ); +} + +void irc_who( irc_t *irc, char *channel ) +{ + user_t *u = irc->users; + struct conversation *c; + GList *l; + + if( !channel || *channel == '0' || *channel == '*' || !*channel ) + while( u ) + { + irc_reply( irc, 352, "%s %s %s %s %s %c :0 %s", u->online ? irc->channel : "*", u->user, u->host, irc->myhost, u->nick, u->online ? ( u->away ? 'G' : 'H' ) : 'G', u->realname ); + u = u->next; + } + else if( g_strcasecmp( channel, irc->channel ) == 0 ) + while( u ) + { + if( u->online ) + irc_reply( irc, 352, "%s %s %s %s %s %c :0 %s", channel, u->user, u->host, irc->myhost, u->nick, u->away ? 'G' : 'H', u->realname ); + u = u->next; + } + else if( ( c = conv_findchannel( channel ) ) ) + for( l = c->in_room; l; l = l->next ) + { + if( ( u = user_findhandle( c->gc, l->data ) ) ) + irc_reply( irc, 352, "%s %s %s %s %s %c :0 %s", channel, u->user, u->host, irc->myhost, u->nick, u->away ? 'G' : 'H', u->realname ); + } + else if( ( u = user_find( irc, channel ) ) ) + irc_reply( irc, 352, "%s %s %s %s %s %c :0 %s", channel, u->user, u->host, irc->myhost, u->nick, u->online ? ( u->away ? 'G' : 'H' ) : 'G', u->realname ); + + irc_reply( irc, 315, "%s :End of /WHO list.", channel?channel:"**" ); +} + +void irc_login( irc_t *irc ) +{ + user_t *u; + + irc_reply( irc, 1, ":Welcome to the BitlBee gateway, %s", irc->nick ); + irc_reply( irc, 2, ":Host %s is running BitlBee " BITLBEE_VERSION " " ARCH "/" CPU ".", irc->myhost ); + irc_reply( irc, 3, ":%s", IRCD_INFO ); + irc_reply( irc, 4, "%s %s %s %s", irc->myhost, BITLBEE_VERSION, UMODES, CMODES ); + irc_motd( irc ); + irc_umode_set( irc, irc->myhost, "+" UMODE ); + + u = user_add( irc, irc->mynick ); + u->host = g_strdup( irc->myhost ); + u->realname = g_strdup( ROOT_FN ); + u->online = 1; + u->send_handler = root_command_string; + u->is_private = 0; /* [SH] The channel is root's personal playground. */ + irc_spawn( irc, u ); + + u = user_add( irc, NS_NICK ); + u->host = g_strdup( irc->myhost ); + u->realname = g_strdup( ROOT_FN ); + u->online = 0; + u->send_handler = root_command_string; + u->is_private = 1; /* [SH] NickServ is not in the channel, so should always /query. */ + + u = user_add( irc, irc->nick ); + u->user = g_strdup( irc->user ); + u->host = g_strdup( irc->host ); + u->realname = g_strdup( irc->realname ); + u->online = 1; +// u->send_handler = msg_echo; + irc_spawn( irc, u ); + + irc_usermsg( irc, "Welcome to the BitlBee gateway!\n\nIf you've never used BitlBee before, please do read the help information using the help command. Lots of FAQ's are answered there." ); + + irc->status = USTATUS_LOGGED_IN; +} + +void irc_motd( irc_t *irc ) +{ + int fd; + + fd = open( global.conf->motdfile, O_RDONLY ); + if( fd == -1 ) + { + irc_reply( irc, 422, ":We don't need MOTDs." ); + } + else + { + char linebuf[80]; /* Max. line length for MOTD's is 79 chars. It's what most IRC networks seem to do. */ + char *add, max; + int len; + + linebuf[79] = len = 0; + max = sizeof( linebuf ) - 1; + + irc_reply( irc, 375, ":- %s Message Of The Day - ", irc->myhost ); + while( read( fd, linebuf + len, 1 ) == 1 ) + { + if( linebuf[len] == '\n' || len == max ) + { + linebuf[len] = 0; + irc_reply( irc, 372, ":- %s", linebuf ); + len = 0; + } + else if( linebuf[len] == '%' ) + { + read( fd, linebuf + len, 1 ); + if( linebuf[len] == 'h' ) + add = irc->myhost; + else if( linebuf[len] == 'v' ) + add = BITLBEE_VERSION; + else if( linebuf[len] == 'n' ) + add = irc->nick; + else + add = "%"; + + strncpy( linebuf + len, add, max - len ); + while( linebuf[++len] ); + } + else if( len < max ) + { + len ++; + } + } + irc_reply( irc, 376, ":End of MOTD" ); + closesocket( fd ); + } +} + +void irc_topic( irc_t *irc, char *channel ) +{ + if( g_strcasecmp( channel, irc->channel ) == 0 ) + { + irc_reply( irc, 332, "%s :%s", channel, CONTROL_TOPIC ); + } + else + { + struct conversation *c = conv_findchannel( channel ); + + if( c ) + irc_reply( irc, 332, "%s :BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!", channel, c->title ); + else + irc_reply( irc, 331, "%s :No topic for this channel" ); + } +} + +void irc_whois( irc_t *irc, char *nick ) +{ + user_t *u = user_find( irc, nick ); + + if( u ) + { + irc_reply( irc, 311, "%s %s %s * :%s", u->nick, u->user, u->host, u->realname ); + + if( u->gc ) + irc_reply( irc, 312, "%s %s.%s :%s network", u->nick, u->gc->user->username, + *u->gc->user->proto_opt[0] ? u->gc->user->proto_opt[0] : "", proto_name[u->gc->user->protocol] ); + else + irc_reply( irc, 312, "%s %s :%s", u->nick, irc->myhost, IRCD_INFO ); + + if( !u->online ) + irc_reply( irc, 301, "%s :%s", u->nick, "User is offline" ); + else if( u->away ) + irc_reply( irc, 301, "%s :%s", u->nick, u->away ); + + irc_reply( irc, 318, "%s :End of /WHOIS list", nick ); + } + else + { + irc_reply( irc, 401, "%s :Nick does not exist", nick ); + } +} + + +void irc_umode_set( irc_t *irc, char *who, char *s ) +{ + char m[256], st = 1, *t; + int i; + + memset( m, 0, sizeof( m ) ); + + for( t = irc->umode; *t; t ++ ) + m[(int)*t] = 1; + + for( t = s; *t; t ++ ) + { + if( *t == '+' || *t == '-' ) + st = *t == '+'; + else + m[(int)*t] = st; + } + + memset( irc->umode, 0, sizeof( irc->umode ) ); + + for( i = 0; i < 256 && strlen( irc->umode ) < ( sizeof( irc->umode ) - 1 ); i ++ ) + if( m[i] && strchr( UMODES, i ) ) + irc->umode[strlen(irc->umode)] = i; + + irc_reply( irc, 221, "+%s", irc->umode ); +} + +int irc_away( irc_t *irc, char *away ) +{ + user_t *u = user_find( irc, irc->nick ); + GSList *c = get_connections(); + + if( !u ) return( 0 ); + + if( away && *away ) + { + int i, j; + + /* Copy away string, but skip control chars. Mainly because + Jabber really doesn't like them. */ + u->away = g_malloc( strlen( away ) + 1 ); + for( i = j = 0; away[i]; i ++ ) + if( ( u->away[j] = away[i] ) >= ' ' ) + j ++; + u->away[j] = 0; + + irc_reply( irc, 306, ":You're now away: %s", u->away ); + /* irc_umode_set( irc, irc->myhost, "+a" ); */ + } + else + { + if( u->away ) g_free( u->away ); + u->away = NULL; + /* irc_umode_set( irc, irc->myhost, "-a" ); */ + irc_reply( irc, 305, ":Welcome back" ); + } + + while( c ) + { + if( ((struct gaim_connection *)c->data)->flags & OPT_LOGGED_IN ) + proto_away( c->data, u->away ); + + c = c->next; + } + + return( 1 ); +} + +void irc_spawn( irc_t *irc, user_t *u ) +{ + irc_join( irc, u, irc->channel ); +} + +void irc_join( irc_t *irc, user_t *u, char *channel ) +{ + char *nick; + + if( ( g_strcasecmp( channel, irc->channel ) != 0 ) || user_find( irc, irc->nick ) ) + irc_write( irc, ":%s!%s@%s JOIN :%s", u->nick, u->user, u->host, channel ); + + if( nick_cmp( u->nick, irc->nick ) == 0 ) + { + irc_write( irc, ":%s MODE %s +%s", irc->myhost, channel, CMODE ); + irc_names( irc, channel ); + irc_topic( irc, channel ); + } + + nick = g_strdup( u->nick ); + nick_lc( nick ); + if( g_hash_table_lookup( irc->watches, nick ) ) + { + irc_reply( irc, 600, "%s %s %s %d :%s", u->nick, u->user, u->host, time( NULL ), "logged online" ); + } + g_free( nick ); +} + +void irc_part( irc_t *irc, user_t *u, char *channel ) +{ + irc_write( irc, ":%s!%s@%s PART %s :%s", u->nick, u->user, u->host, channel, "" ); +} + +void irc_kick( irc_t *irc, user_t *u, char *channel, user_t *kicker ) +{ + irc_write( irc, ":%s!%s@%s KICK %s %s :%s", kicker->nick, kicker->user, kicker->host, channel, u->nick, "" ); +} + +void irc_kill( irc_t *irc, user_t *u ) +{ + char *nick; + + irc_write( irc, ":%s!%s@%s QUIT :%s", u->nick, u->user, u->host, "Leaving..." ); + + nick = g_strdup( u->nick ); + nick_lc( nick ); + if( g_hash_table_lookup( irc->watches, nick ) ) + { + irc_reply( irc, 601, "%s %s %s %d :%s", u->nick, u->user, u->host, time( NULL ), "logged offline" ); + } + g_free( nick ); +} + +void irc_invite( irc_t *irc, char *nick, char *channel ) +{ + struct conversation *c = conv_findchannel( channel ); + user_t *u = user_find( irc, nick ); + + if( u && c && ( u->gc == c->gc ) ) + if( c->gc && c->gc->prpl && c->gc->prpl->chat_invite ) + { + c->gc->prpl->chat_invite( c->gc, c->id, "", u->handle ); + irc_reply( irc, 341, "%s %s", nick, channel ); + return; + } + + irc_reply( irc, 482, "%s :Invite impossible; User/Channel non-existent or incompatible", channel ); +} + +int irc_send( irc_t *irc, char *nick, char *s, int flags ) +{ + struct conversation *c = NULL; + user_t *u = NULL; + + if( *nick == '#' ) + { + if( !( c = conv_findchannel( nick ) ) ) + { + irc_reply( irc, 403, "%s :Channel does not exist", nick ); + return( 0 ); + } + } + else + { + u = user_find( irc, nick ); + + if( !u ) + { + if( irc->is_private ) + irc_reply( irc, 401, "%s :Nick does not exist", nick ); + else + irc_usermsg( irc, "Nick `%s' does not exist!", nick ); + return( 0 ); + } + } + + if( *s == 1 && s[strlen(s)-1] == 1 ) + { + if( g_strncasecmp( s + 1, "ACTION", 6 ) == 0 ) + { + if( s[7] == ' ' ) s ++; + s += 3; + *(s++) = '/'; + *(s++) = 'm'; + *(s++) = 'e'; + *(s++) = ' '; + s -= 4; + s[strlen(s)-1] = 0; + } + else if( g_strncasecmp( s + 1, "VERSION", 7 ) == 0 ) + { + u = user_find( irc, irc->mynick ); + irc_privmsg( irc, u, "NOTICE", irc->nick, "", "\001VERSION BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\001" ); + return( 1 ); + } + else if( g_strncasecmp( s + 1, "PING", 4 ) == 0 ) + { + u = user_find( irc, irc->mynick ); + irc_privmsg( irc, u, "NOTICE", irc->nick, "", s ); + return( 1 ); + } + else if( g_strncasecmp( s + 1, "TYPING", 6 ) == 0 ) + { + if( u && u->gc && u->gc->prpl->send_typing && strlen( s ) >= 10 ) + { + time_t current_typing_notice = time( NULL ); + + if( current_typing_notice - u->last_typing_notice >= 5 ) + { + u->gc->prpl->send_typing( u->gc, u->handle, s[8] == '1' ); + u->last_typing_notice = current_typing_notice; + } + } + return( 1 ); + } + else + { + irc_usermsg( irc, "Non-ACTION CTCP's aren't supported" ); + return( 0 ); + } + } + + if( u ) + { + /* For the next message, we probably do have to send new notices... */ + u->last_typing_notice = 0; + u->is_private = irc->is_private; + + if( u->is_private ) + { + if( !u->online ) + irc_reply( irc, 301, "%s :%s", u->nick, "User is offline" ); + else if( u->away ) + irc_reply( irc, 301, "%s :%s", u->nick, u->away ); + } + + if( u->send_handler ) + return( u->send_handler( irc, u, s, flags ) ); + } + else if( c && c->gc && c->gc->prpl ) + { + return( serv_send_chat( irc, c->gc, c->id, s ) ); + } + + return( 0 ); +} + +gboolean buddy_send_handler_delayed( gpointer data ) +{ + user_t *u = data; + + u->sendbuf[u->sendbuf_len-2] = 0; /* Cut off the last newline */ + serv_send_im( u->gc->irc, u, u->sendbuf, u->sendbuf_flags ); + + g_free( u->sendbuf ); + u->sendbuf = NULL; + u->sendbuf_len = 0; + u->sendbuf_timer = 0; + u->sendbuf_flags = 0; + + return( FALSE ); +} + +int buddy_send_handler( irc_t *irc, user_t *u, char *msg, int flags ) +{ + if( !u || !u->gc ) return( 0 ); + + if( set_getint( irc, "buddy_sendbuffer" ) && set_getint( irc, "buddy_sendbuffer_delay" ) > 0 ) + { + if( u->sendbuf_len > 0 && u->sendbuf_flags != flags) + { + //Flush the buffer + g_source_remove( u->sendbuf_timer ); + buddy_send_handler_delayed( u ); + } + + if( u->sendbuf_len == 0 ) + { + u->sendbuf_len = strlen( msg ) + 2; + u->sendbuf = g_new (char, u->sendbuf_len ); + u->sendbuf[0] = 0; + u->sendbuf_flags = flags; + } + else + { + u->sendbuf_len += strlen( msg ) + 1; + u->sendbuf = g_renew ( char, u->sendbuf, u->sendbuf_len ); + } + + strcat( u->sendbuf, msg ); + strcat( u->sendbuf, "\n" ); + + if( u->sendbuf_timer > 0 ) + g_source_remove( u->sendbuf_timer ); + u->sendbuf_timer = g_timeout_add( set_getint( irc, "buddy_sendbuffer_delay" ) * 1000, + buddy_send_handler_delayed, u ); + + return( 1 ); + } + else + { + return( serv_send_im( irc, u, msg, flags ) ); + } +} + +int irc_privmsg( irc_t *irc, user_t *u, char *type, char *to, char *prefix, char *msg ) +{ + char last = 0; + char *s = msg, *line = msg; + + /* The almighty linesplitter .. woohoo!! */ + while( !last ) + { + if( *s == '\r' && *(s+1) == '\n' ) + *(s++) = 0; + if( *s == '\n' ) + { + last = s[1] == 0; + *s = 0; + } + else + { + last = s[0] == 0; + } + if( *s == 0 ) + { + if( g_strncasecmp( line, "/me ", 4 ) == 0 && ( !prefix || !*prefix ) && g_strcasecmp( type, "PRIVMSG" ) == 0 ) + { + irc_write( irc, ":%s!%s@%s %s %s :\001ACTION %s\001", u->nick, u->user, u->host, + type, to, line + 4 ); + } + else + { + irc_write( irc, ":%s!%s@%s %s %s :%s%s", u->nick, u->user, u->host, + type, to, prefix, line ); + } + line = s + 1; + } + s ++; + } + + return( 1 ); +} + +int irc_msgfrom( irc_t *irc, char *nick, char *msg ) +{ + user_t *u = user_find( irc, nick ); + static char *prefix = NULL; + + if( !u ) return( 0 ); + if( prefix && *prefix ) g_free( prefix ); + + if( !u->is_private && nick_cmp( u->nick, irc->mynick ) != 0 ) + { + int len = strlen( irc->nick) + 3; + prefix = g_new (char, len ); + g_snprintf( prefix, len, "%s%s", irc->nick, set_getstr( irc, "to_char" ) ); + prefix[len-1] = 0; + } + else + { + prefix = ""; + } + + return( irc_privmsg( irc, u, "PRIVMSG", u->is_private ? irc->nick : irc->channel, prefix, msg ) ); +} + +int irc_noticefrom( irc_t *irc, char *nick, char *msg ) +{ + user_t *u = user_find( irc, nick ); + + if( u ) + return( irc_privmsg( irc, u, "NOTICE", irc->nick, "", msg ) ); + else + return( 0 ); +} + +/* Returns 0 if everything seems to be okay, a number >0 when there was a + timeout. The number returned is the number of seconds we received no + pongs from the user. When not connected yet, we don't ping but drop the + connection when the user fails to connect in IRC_LOGIN_TIMEOUT secs. */ +static gboolean irc_userping( gpointer _irc ) +{ + irc_t *irc = _irc; + int rv = 0; + + if( irc->status < USTATUS_LOGGED_IN ) + { + if( gettime() > ( irc->last_pong + IRC_LOGIN_TIMEOUT ) ) + rv = gettime() - irc->last_pong; + } + else + { + if( ( gettime() > ( irc->last_pong + global.conf->ping_interval ) ) && !irc->pinging ) + { + irc_write( irc, "PING :%s", IRC_PING_STRING ); + irc->pinging = 1; + } + else if( gettime() > ( irc->last_pong + global.conf->ping_timeout ) ) + { + rv = gettime() - irc->last_pong; + } + } + + if( rv > 0 ) + { + irc_write( irc, "ERROR :Closing Link: Ping Timeout: %d seconds", rv ); + irc_free( irc ); + return FALSE; + } + + return TRUE; +} @@ -0,0 +1,147 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* The big hairy IRCd part of the project */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef _IRC_H +#define _IRC_H + +#define IRC_MAX_LINE 512 +#define IRC_MAX_ARGS 8 + +#define IRC_LOGIN_TIMEOUT 60 +#define IRC_PING_STRING "PinglBee" + +/* #define FLOOD_SEND + * Not yet enabled by default due to some problems. + */ +#define FLOOD_SEND_INTERVAL 30 +#define FLOOD_SEND_BYTES (1024*10) +#define FLOOD_SEND_MAXBUFFER (1024*20) + +#define UMODES "ais" +#define CMODES "nt" +#define CMODE "t" +#define UMODE "s" + +typedef enum +{ + USTATUS_OFFLINE, + USTATUS_AUTHORIZED, + USTATUS_LOGGED_IN, + USTATUS_IDENTIFIED +} irc_status_t; + +typedef struct channel +{ + char *name; +} channel_t; + +typedef struct irc +{ + int fd; + irc_status_t status; + double last_pong; + int pinging; + char *sendbuffer; + char *readbuffer; + int quit; + + int sentbytes; + time_t oldtime; + + char *nick; + char *user; + char *host; + char *realname; + char *password; + + char umode[8]; + + char *myhost; + char *mynick; + + char *channel; + int c_id; + + char is_private; /* Not too nice... */ + char *last_target; + + struct query *queries; + struct account *accounts; + + struct __USER *users; + GHashTable *userhash; + GHashTable *watches; + struct __NICK *nicks; + struct help *help; + struct set *set; + + GIOChannel *io_channel; + gint r_watch_source_id; + gint w_watch_source_id; + gint ping_source_id; +} irc_t; + +#include "user.h" +#include "nick.h" + +extern GSList *irc_connection_list; + +irc_t *irc_new( int fd ); +void irc_free( irc_t *irc ); + +int irc_exec( irc_t *irc, char **cmd ); +int irc_process( irc_t *irc ); +int irc_process_line( irc_t *irc, char *line ); + +void irc_vawrite( irc_t *irc, char *format, va_list params ); +void irc_write( irc_t *irc, char *format, ... ); +void irc_write_all( char *format, ... ); +void irc_reply( irc_t *irc, int code, char *format, ... ); +G_MODULE_EXPORT int irc_usermsg( irc_t *irc, char *format, ... ); +char **irc_tokenize( char *buffer ); + +void irc_login( irc_t *irc ); +void irc_motd( irc_t *irc ); +void irc_names( irc_t *irc, char *channel ); +void irc_topic( irc_t *irc, char *channel ); +void irc_umode_set( irc_t *irc, char *who, char *s ); +void irc_who( irc_t *irc, char *channel ); +void irc_spawn( irc_t *irc, user_t *u ); +void irc_join( irc_t *irc, user_t *u, char *channel ); +void irc_part( irc_t *irc, user_t *u, char *channel ); +void irc_kick( irc_t *irc, user_t *u, char *channel, user_t *kicker ); +void irc_kill( irc_t *irc, user_t *u ); +void irc_invite( irc_t *irc, char *nick, char *channel ); +void irc_whois( irc_t *irc, char *nick ); +int irc_away( irc_t *irc, char *away ); + +int irc_send( irc_t *irc, char *nick, char *s, int flags ); +int irc_privmsg( irc_t *irc, user_t *u, char *type, char *to, char *prefix, char *msg ); +int irc_msgfrom( irc_t *irc, char *nick, char *msg ); +int irc_noticefrom( irc_t *irc, char *nick, char *msg ); + +int buddy_send_handler( irc_t *irc, user_t *u, char *msg, int flags ); + +#endif @@ -0,0 +1,175 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Logging services for the bee */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#define BITLBEE_CORE +#include "bitlbee.h" +#include <syslog.h> + +static log_t logoutput; + +static void log_null(int level, char *logmessage); +static void log_irc(int level, char *logmessage); +static void log_syslog(int level, char *logmessage); +static void log_console(int level, char *logmessage); + +void log_init(void) { + openlog("bitlbee", LOG_PID, LOG_DAEMON); + + logoutput.informational=&log_null; + logoutput.warning=&log_null; + logoutput.error=&log_null; +#ifdef DEBUG + logoutput.debug=&log_null; +#endif + + return; +} + +void log_link(int level, int output) { + /* I know it's ugly, but it works and I didn't feel like messing with pointer to function pointers */ + + if(level==LOGLVL_INFO) { + if(output==LOGOUTPUT_NULL) + logoutput.informational=&log_null; + else if(output==LOGOUTPUT_IRC) + logoutput.informational=&log_irc; + else if(output==LOGOUTPUT_SYSLOG) + logoutput.informational=&log_syslog; + else if(output==LOGOUTPUT_CONSOLE) + logoutput.informational=&log_console; + } + else if(level==LOGLVL_WARNING) { + if(output==LOGOUTPUT_NULL) + logoutput.warning=&log_null; + else if(output==LOGOUTPUT_IRC) + logoutput.warning=&log_irc; + else if(output==LOGOUTPUT_SYSLOG) + logoutput.warning=&log_syslog; + else if(output==LOGOUTPUT_CONSOLE) + logoutput.warning=&log_console; + } + else if(level==LOGLVL_ERROR) { + if(output==LOGOUTPUT_NULL) + logoutput.error=&log_null; + else if(output==LOGOUTPUT_IRC) + logoutput.error=&log_irc; + else if(output==LOGOUTPUT_SYSLOG) + logoutput.error=&log_syslog; + else if(output==LOGOUTPUT_CONSOLE) + logoutput.error=&log_console; + } +#ifdef DEBUG + else if(level==LOGLVL_DEBUG) { + if(output==LOGOUTPUT_NULL) + logoutput.debug=&log_null; + else if(output==LOGOUTPUT_IRC) + logoutput.debug=&log_irc; + else if(output==LOGOUTPUT_SYSLOG) + logoutput.debug=&log_syslog; + else if(output==LOGOUTPUT_CONSOLE) + logoutput.debug=&log_console; + } +#endif + return; + +} + +void log_message(int level, char *message, ... ) { + + va_list ap; + char *msgstring; + + va_start(ap, message); + msgstring = g_strdup_vprintf(message, ap); + va_end(ap); + + if(level==LOGLVL_INFO) + (*(logoutput.informational))(level, msgstring); + if(level==LOGLVL_WARNING) + (*(logoutput.warning))(level, msgstring); + if(level==LOGLVL_ERROR) + (*(logoutput.error))(level, msgstring); +#ifdef DEBUG + if(level==LOGLVL_DEBUG) + (*(logoutput.debug))(level, msgstring); +#endif + + g_free(msgstring); + + return; +} + +void log_error(char *functionname) { + log_message(LOGLVL_ERROR, "%s: %s", functionname, strerror(errno)); + + return; +} + +static void log_null(int level, char *message) { + return; +} + +static void log_irc(int level, char *message) { + if(level==LOGLVL_ERROR) + irc_write_all("ERROR :Error: %s", message); + if(level==LOGLVL_WARNING) + irc_write_all("ERROR :Warning: %s", message); + if(level==LOGLVL_INFO) + irc_write_all("ERROR :Informational: %s", message); +#ifdef DEBUG + if(level==LOGLVL_DEBUG) + irc_write_all("ERROR :Debug: %s", message); +#endif + + return; +} + +static void log_syslog(int level, char *message) { + if(level==LOGLVL_ERROR) + syslog(LOG_ERR, "%s", message); + if(level==LOGLVL_WARNING) + syslog(LOG_WARNING, "%s", message); + if(level==LOGLVL_INFO) + syslog(LOG_INFO, "%s", message); +#ifdef DEBUG + if(level==LOGLVL_DEBUG) + syslog(LOG_DEBUG, "%s", message); +#endif + return; +} + +static void log_console(int level, char *message) { + if(level==LOGLVL_ERROR) + fprintf(stderr, "Error: %s\n", message); + if(level==LOGLVL_WARNING) + fprintf(stderr, "Warning: %s\n", message); + if(level==LOGLVL_INFO) + fprintf(stdout, "Informational: %s\n", message); +#ifdef DEBUG + if(level==LOGLVL_DEBUG) + fprintf(stdout, "Debug: %s\n", message); +#endif + return; +} @@ -0,0 +1,59 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Logging services for the bee */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef _LOG_H +#define _LOG_H + +typedef enum { + LOGLVL_INFO, + LOGLVL_WARNING, + LOGLVL_ERROR, +#ifdef DEBUG + LOGLVL_DEBUG, +#endif +} loglvl_t; + +typedef enum { + LOGOUTPUT_NULL, + LOGOUTPUT_IRC, + LOGOUTPUT_SYSLOG, + LOGOUTPUT_CONSOLE, +} logoutput_t; + +typedef struct log_t { + void (*error)(int level, char *logmessage); + void (*warning)(int level, char *logmessage); + void (*informational)(int level, char *logmessage); +#ifdef DEBUG + void (*debug)(int level, char *logmessage); +#endif +} log_t; + +void log_init(void); +void log_link(int level, int output); +void log_message(int level, char *message, ...); +void log_error(char *functionname); + +#endif diff --git a/motd.txt b/motd.txt new file mode 100644 index 00000000..cbf813f0 --- /dev/null +++ b/motd.txt @@ -0,0 +1,18 @@ +Welcome to the BitlBee server at %h. + +This server is running BitlBee version %v. +The newest version can be found on http://www.bitlbee.org/ + +You are getting this message because the server administrator has not +yet had the time (or need) to change it. + +For those who don't know it yet, this is not quite a regular Internet +Relay Chat server. Please see the site mentioned above for more +information. + + +The developers of the Bee hope you have a buzzing time. + +* BitlBee development team: wilmer, ctrlsoft, Maurits + +... Buzzing, haha, get it? @@ -0,0 +1,263 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Some stuff to fetch, save and handle nicknames for your buddies */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#define BITLBEE_CORE +#include "bitlbee.h" + +void nick_set( irc_t *irc, char *handle, int proto, char *nick ) +{ + nick_t *m = NULL, *n = irc->nicks; + + while( n ) + { + if( ( g_strcasecmp( n->handle, handle ) == 0 ) && n->proto == proto ) + { + g_free( n->nick ); + n->nick = nick_dup( nick ); + nick_strip( n->nick ); + + return; + } + n = ( m = n )->next; // :-P + } + + if( m ) + n = m->next = g_new0( nick_t, 1 ); + else + n = irc->nicks = g_new0( nick_t, 1 ); + + n->handle = g_strdup( handle ); + n->proto = proto; + n->nick = nick_dup( nick ); + + nick_strip( n->nick ); +} + +char *nick_get( irc_t *irc, char *handle, int proto, const char *realname ) +{ + static char nick[MAX_NICK_LENGTH+1]; + nick_t *n = irc->nicks; + int inf_protection = 256; + + memset( nick, 0, MAX_NICK_LENGTH + 1 ); + + while( n && !*nick ) + if( ( n->proto == proto ) && ( g_strcasecmp( n->handle, handle ) == 0 ) ) + strcpy( nick, n->nick ); + else + n = n->next; + + if( !n ) + { + char *s; + + g_snprintf( nick, MAX_NICK_LENGTH, "%s", handle ); + if( ( s = strchr( nick, '@' ) ) ) + while( *s ) + *(s++) = 0; + + /* All-digit handles (mainly ICQ UINs) aren't cool, try to + use the realname instead. */ + for( s = nick; *s && isdigit( *s ); s ++ ); + if( !*s && realname && *realname ) + g_snprintf( nick, MAX_NICK_LENGTH, "%s", realname ); + + nick_strip( nick ); + if (set_getint(irc, "lcnicks")) + nick_lc( nick ); + } + + while( !nick_ok( nick ) || user_find( irc, nick ) ) + { + if( strlen( nick ) < ( MAX_NICK_LENGTH - 1 ) ) + { + nick[strlen(nick)+1] = 0; + nick[strlen(nick)] = '_'; + } + else + { + nick[0] ++; + } + + if( inf_protection-- == 0 ) + { + int i; + + irc_usermsg( irc, "WARNING: Almost had an infinite loop in nick_get()! " + "This used to be a fatal BitlBee bug, but we tried to fix it. " + "This message should *never* appear anymore. " + "If it does, please *do* send us a bug report! " + "Please send all the following lines in your report:" ); + + irc_usermsg( irc, "Trying to get a sane nick for handle %s", handle ); + for( i = 0; i < MAX_NICK_LENGTH; i ++ ) + irc_usermsg( irc, "Char %d: %c/%d", i, nick[i], nick[i] ); + + irc_usermsg( irc, "FAILED. Returning an insane nick now. Things might break. " + "Good luck, and please don't forget to paste the lines up here " + "in #bitlbee on OFTC or in a mail to wilmer@gaast.net" ); + + g_snprintf( nick, MAX_NICK_LENGTH + 1, "xx%x", rand() ); + + break; + } + } + + return( nick ); +} + +void nick_del( irc_t *irc, char *nick ) +{ + nick_t *l = NULL, *n = irc->nicks; + + while( n ) + { + if( g_strcasecmp( n->nick, nick ) == 0 ) + { + if( l ) + l->next = n->next; + else + irc->nicks = n->next; + + g_free( n->handle ); + g_free( n->nick ); + g_free( n ); + + break; + } + n = (l=n)->next; + } +} + + +/* Character maps, _lc_[x] == _uc_[x] (but uppercase), according to the RFC's. + With one difference, we allow dashes. */ + +static char *nick_lc_chars = "0123456789abcdefghijklmnopqrstuvwxyz{}^-_|"; +static char *nick_uc_chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ[]~-_\\"; + +void nick_strip( char * nick ) +{ + int i, j; + + for( i = j = 0; nick[i] && i < MAX_NICK_LENGTH; i++ ) + { + if( strchr( nick_lc_chars, nick[i] ) || + strchr( nick_uc_chars, nick[i] ) ) + { + nick[j] = nick[i]; + j++; + } + } + while( j < MAX_NICK_LENGTH ) + nick[j++] = '\0'; +} + +int nick_ok( char *nick ) +{ + char *s; + + /* Empty/long nicks are not allowed */ + if( !*nick || strlen( nick ) > MAX_NICK_LENGTH ) + return( 0 ); + + for( s = nick; *s; s ++ ) + if( !strchr( nick_lc_chars, *s ) && !strchr( nick_uc_chars, *s ) ) + return( 0 ); + + return( 1 ); +} + +int nick_lc( char *nick ) +{ + static char tab[256] = { 0 }; + int i; + + if( tab['A'] == 0 ) + for( i = 0; nick_lc_chars[i]; i ++ ) + { + tab[(int)nick_uc_chars[i]] = nick_lc_chars[i]; + tab[(int)nick_lc_chars[i]] = nick_lc_chars[i]; + } + + for( i = 0; nick[i]; i ++ ) + { + if( !tab[(int)nick[i]] ) + return( 0 ); + + nick[i] = tab[(int)nick[i]]; + } + + return( 1 ); +} + +int nick_uc( char *nick ) +{ + static char tab[128] = { 0 }; + int i; + + if( tab['A'] == 0 ) + for( i = 0; nick_lc_chars[i]; i ++ ) + { + tab[(int)nick_uc_chars[i]] = nick_uc_chars[i]; + tab[(int)nick_lc_chars[i]] = nick_uc_chars[i]; + } + + for( i = 0; nick[i]; i ++ ) + { + if( !tab[(int)nick[i]] ) + return( 0 ); + + nick[i] = tab[(int)nick[i]]; + } + + return( 1 ); +} + +int nick_cmp( char *a, char *b ) +{ + char aa[1024] = "", bb[1024] = ""; + + strncpy( aa, a, sizeof( aa ) - 1 ); + strncpy( bb, b, sizeof( bb ) - 1 ); + if( nick_lc( aa ) && nick_lc( bb ) ) + { + return( strcmp( aa, bb ) ); + } + else + { + return( -1 ); /* Hmm... Not a clear answer.. :-/ */ + } +} + +char *nick_dup( char *nick ) +{ + char *cp; + + cp = g_new0 ( char, MAX_NICK_LENGTH + 1 ); + strncpy( cp, nick, MAX_NICK_LENGTH ); + + return( cp ); +} @@ -0,0 +1,43 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Some stuff to fetch, save and handle nicknames for your buddies */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +typedef struct __NICK +{ + char *handle; + int proto; + char *nick; + struct __NICK *next; +} nick_t; + +void nick_set( irc_t *irc, char *handle, int proto, char *nick ); +char *nick_get( irc_t *irc, char *handle, int proto, const char *realname ); +void nick_del( irc_t *irc, char *nick ); +void nick_strip( char *nick ); + +int nick_ok( char *nick ); +int nick_lc( char *nick ); +int nick_uc( char *nick ); +int nick_cmp( char *a, char *b ); +char *nick_dup( char *nick ); diff --git a/protocols/Makefile b/protocols/Makefile new file mode 100644 index 00000000..c5f938fd --- /dev/null +++ b/protocols/Makefile @@ -0,0 +1,49 @@ +########################### +## Makefile for BitlBee ## +## ## +## Copyright 2002 Lintux ## +########################### + +### DEFINITIONS + +-include ../Makefile.settings + +# [SH] Program variables +objects = md5.o nogaim.o proxy.o sha.o util.o $(SSL_CLIENT) + +# [SH] The next two lines should contain the directory name (in $(subdirs)) +# and the name of the object file, which should be linked into +# protocols.o (in $(subdirobjs)). These need to be in order, i.e. the +# first object file should be in the first directory. +subdirs = $(PROTOCOLS) +subdirobjs = $(PROTOOBJS) + +# Expansion of variables +subdirobjs := $(join $(subdirs),$(addprefix /,$(subdirobjs))) +CFLAGS += -Wall +LFLAGS += -r + +# [SH] Phony targets +all: protocols.o + +.PHONY: all clean distclean $(subdirs) + +clean: $(subdirs) + rm -f *.o $(OUTFILE) core + +distclean: clean $(subdirs) + +$(subdirs): + @$(MAKE) -C $@ $(MAKECMDGOALS) + +### MAIN PROGRAM + +protocols.o: $(objects) $(subdirs) + @echo '*' Linking protocols.o + @$(LD) $(LFLAGS) $(objects) $(subdirobjs) -o protocols.o + +$(objects): ../Makefile.settings Makefile + +$(objects): %.o: %.c + @echo '*' Compiling $< + @$(CC) -c $(CFLAGS) $< -o $@ diff --git a/protocols/jabber/Makefile b/protocols/jabber/Makefile new file mode 100644 index 00000000..df326fe6 --- /dev/null +++ b/protocols/jabber/Makefile @@ -0,0 +1,37 @@ +########################### +## Makefile for BitlBee ## +## ## +## Copyright 2002 Lintux ## +########################### + +### DEFINITIONS + +-include ../../Makefile.settings + +# [SH] Program variables +objects = expat.o hashtable.o jid.o jpacket.o jutil.o log.o pool.o str.o xmlnode.o xmlparse.o xmlrole.o xmltok.o jabber.o + +CFLAGS += -Wall +LFLAGS += -r + +# [SH] Phony targets +all: jabberr.o + +.PHONY: all clean distclean + +clean: + rm -f *.o core + +distclean: clean + +### MAIN PROGRAM + +$(objects): ../../Makefile.settings Makefile + +$(objects): %.o: %.c + @echo '*' Compiling $< + @$(CC) -c $(CFLAGS) $< -o $@ + +jabberr.o: $(objects) + @echo '*' Linking jabberr.o + @$(LD) $(LFLAGS) $(objects) -o jabberr.o diff --git a/protocols/jabber/asciitab.h b/protocols/jabber/asciitab.h new file mode 100644 index 00000000..8a8a2dd3 --- /dev/null +++ b/protocols/jabber/asciitab.h @@ -0,0 +1,62 @@ +/* +The contents of this file are subject to the Mozilla Public License +Version 1.1 (the "License"); you may not use this file except in +compliance with the License. You may obtain a copy of the License at +http://www.mozilla.org/MPL/ + +Software distributed under the License is distributed on an "AS IS" +basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +License for the specific language governing rights and limitations +under the License. + +The Original Code is expat. + +The Initial Developer of the Original Code is James Clark. +Portions created by James Clark are Copyright (C) 1998, 1999 +James Clark. All Rights Reserved. + +Contributor(s): + +Alternatively, the contents of this file may be used under the terms +of the GNU General Public License (the "GPL"), in which case the +provisions of the GPL are applicable instead of those above. If you +wish to allow use of your version of this file only under the terms of +the GPL and not to allow others to use your version of this file under +the MPL, indicate your decision by deleting the provisions above and +replace them with the notice and other provisions required by the +GPL. If you do not delete the provisions above, a recipient may use +your version of this file under either the MPL or the GPL. +*/ + +/* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML, +/* 0x0C */ BT_NONXML, BT_CR, BT_NONXML, BT_NONXML, +/* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM, +/* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS, +/* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS, +/* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL, +/* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, +/* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, +/* 0x38 */ BT_DIGIT, BT_DIGIT, BT_COLON, BT_SEMI, +/* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST, +/* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, +/* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, +/* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB, +/* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT, +/* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, +/* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, +/* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, +/* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER, diff --git a/protocols/jabber/expat.c b/protocols/jabber/expat.c new file mode 100644 index 00000000..0eedb321 --- /dev/null +++ b/protocols/jabber/expat.c @@ -0,0 +1,54 @@ +/* -------------------------------------------------------------------------- + * + * License + * + * The contents of this file are subject to the Jabber Open Source License + * Version 1.0 (the "JOSL"). You may not copy or use this file, in either + * source code or executable form, except in compliance with the JOSL. You + * may obtain a copy of the JOSL at http://www.jabber.org/ or at + * http://www.opensource.org/. + * + * Software distributed under the JOSL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the JOSL + * for the specific language governing rights and limitations under the + * JOSL. + * + * Copyrights + * + * Portions created by or assigned to Jabber.com, Inc. are + * Copyright (c) 1999-2002 Jabber.com, Inc. All Rights Reserved. Contact + * information for Jabber.com, Inc. is available at http://www.jabber.com/. + * + * Portions Copyright (c) 1998-1999 Jeremie Miller. + * + * Acknowledgements + * + * Special thanks to the Jabber Open Source Contributors for their + * suggestions and support of Jabber. + * + * Alternatively, the contents of this file may be used under the terms of the + * GNU General Public License Version 2 or later (the "GPL"), in which case + * the provisions of the GPL are applicable instead of those above. If you + * wish to allow use of your version of this file only under the terms of the + * GPL and not to allow others to use your version of this file under the JOSL, + * indicate your decision by deleting the provisions above and replace them + * with the notice and other provisions required by the GPL. If you do not + * delete the provisions above, a recipient may use your version of this file + * under either the JOSL or the GPL. + * + * + * --------------------------------------------------------------------------*/ + +#include "lib.h" +#include <glib.h> + +void xmlnode_put_expat_attribs(xmlnode owner, const char** atts) +{ + int i = 0; + if (atts == NULL) return; + while (atts[i] != '\0') + { + xmlnode_put_attrib(owner, atts[i], atts[i+1]); + i += 2; + } +} diff --git a/protocols/jabber/genhash.c b/protocols/jabber/genhash.c new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/protocols/jabber/genhash.c diff --git a/protocols/jabber/hashtable.c b/protocols/jabber/hashtable.c new file mode 100644 index 00000000..82c33bc3 --- /dev/null +++ b/protocols/jabber/hashtable.c @@ -0,0 +1,142 @@ +/* +The contents of this file are subject to the Mozilla Public License +Version 1.1 (the "License"); you may not use this file except in +csompliance with the License. You may obtain a copy of the License at +http://www.mozilla.org/MPL/ + +Software distributed under the License is distributed on an "AS IS" +basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +License for the specific language governing rights and limitations +under the License. + +The Original Code is expat. + +The Initial Developer of the Original Code is James Clark. +Portions created by James Clark are Copyright (C) 1998, 1999 +James Clark. All Rights Reserved. + +Contributor(s): + +*/ + +#include "xmldef.h" + +#ifdef XML_UNICODE_WCHAR_T +#ifndef XML_UNICODE +#define XML_UNICODE +#endif +#endif + +#include "hashtable.h" + +#define INIT_SIZE 64 + +static +int keyeq(KEY s1, KEY s2) +{ + for (; *s1 == *s2; s1++, s2++) + if (*s1 == 0) + return 1; + return 0; +} + +static +unsigned long hash(KEY s) +{ + unsigned long h = 0; + while (*s) + h = (h << 5) + h + (unsigned char)*s++; + return h; +} + +NAMED *lookup(HASH_TABLE *table, KEY name, size_t createSize) +{ + size_t i; + if (table->size == 0) { + if (!createSize) + return 0; + table->v = calloc(INIT_SIZE, sizeof(NAMED *)); + if (!table->v) + return 0; + table->size = INIT_SIZE; + table->usedLim = INIT_SIZE / 2; + i = hash(name) & (table->size - 1); + } + else { + unsigned long h = hash(name); + for (i = h & (table->size - 1); + table->v[i]; + i == 0 ? i = table->size - 1 : --i) { + if (keyeq(name, table->v[i]->name)) + return table->v[i]; + } + if (!createSize) + return 0; + if (table->used == table->usedLim) { + /* check for overflow */ + size_t newSize = table->size * 2; + NAMED **newV = calloc(newSize, sizeof(NAMED *)); + if (!newV) + return 0; + for (i = 0; i < table->size; i++) + if (table->v[i]) { + size_t j; + for (j = hash(table->v[i]->name) & (newSize - 1); + newV[j]; + j == 0 ? j = newSize - 1 : --j) + ; + newV[j] = table->v[i]; + } + g_free(table->v); + table->v = newV; + table->size = newSize; + table->usedLim = newSize/2; + for (i = h & (table->size - 1); + table->v[i]; + i == 0 ? i = table->size - 1 : --i) + ; + } + } + table->v[i] = calloc(1, createSize); + if (!table->v[i]) + return 0; + table->v[i]->name = name; + (table->used)++; + return table->v[i]; +} + +void hashTableDestroy(HASH_TABLE *table) +{ + size_t i; + for (i = 0; i < table->size; i++) { + NAMED *p = table->v[i]; + if (p) + g_free(p); + } + g_free(table->v); +} + +void hashTableInit(HASH_TABLE *p) +{ + p->size = 0; + p->usedLim = 0; + p->used = 0; + p->v = 0; +} + +void hashTableIterInit(HASH_TABLE_ITER *iter, const HASH_TABLE *table) +{ + iter->p = table->v; + iter->end = iter->p + table->size; +} + +NAMED *hashTableIterNext(HASH_TABLE_ITER *iter) +{ + while (iter->p != iter->end) { + NAMED *tem = *(iter->p)++; + if (tem) + return tem; + } + return 0; +} + diff --git a/protocols/jabber/hashtable.h b/protocols/jabber/hashtable.h new file mode 100644 index 00000000..df8ab8a4 --- /dev/null +++ b/protocols/jabber/hashtable.h @@ -0,0 +1,69 @@ +/* +The contents of this file are subject to the Mozilla Public License +Version 1.1 (the "License"); you may not use this file except in +compliance with the License. You may obtain a copy of the License at +http://www.mozilla.org/MPL/ + +Software distributed under the License is distributed on an "AS IS" +basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +License for the specific language governing rights and limitations +under the License. + +The Original Code is expat. + +The Initial Developer of the Original Code is James Clark. +Portions created by James Clark are Copyright (C) 1998, 1999 +James Clark. All Rights Reserved. + +Contributor(s): + +Alternatively, the contents of this file may be used under the terms +of the GNU General Public License (the "GPL"), in which case the +provisions of the GPL are applicable instead of those above. If you +wish to allow use of your version of this file only under the terms of +the GPL and not to allow others to use your version of this file under +the MPL, indicate your decision by deleting the provisions above and +replace them with the notice and other provisions required by the +GPL. If you do not delete the provisions above, a recipient may use +your version of this file under either the MPL or the GPL. +*/ + + +#include <stddef.h> + +#ifdef XML_UNICODE + +#ifdef XML_UNICODE_WCHAR_T +typedef const wchar_t *KEY; +#else /* not XML_UNICODE_WCHAR_T */ +typedef const unsigned short *KEY; +#endif /* not XML_UNICODE_WCHAR_T */ + +#else /* not XML_UNICODE */ + +typedef const char *KEY; + +#endif /* not XML_UNICODE */ + +typedef struct { + KEY name; +} NAMED; + +typedef struct { + NAMED **v; + size_t size; + size_t used; + size_t usedLim; +} HASH_TABLE; + +NAMED *lookup(HASH_TABLE *table, KEY name, size_t createSize); +void hashTableInit(HASH_TABLE *); +void hashTableDestroy(HASH_TABLE *); + +typedef struct { + NAMED **p; + NAMED **end; +} HASH_TABLE_ITER; + +void hashTableIterInit(HASH_TABLE_ITER *, const HASH_TABLE *); +NAMED *hashTableIterNext(HASH_TABLE_ITER *); diff --git a/protocols/jabber/iasciitab.h b/protocols/jabber/iasciitab.h new file mode 100644 index 00000000..333d6bb7 --- /dev/null +++ b/protocols/jabber/iasciitab.h @@ -0,0 +1,63 @@ +/* +The contents of this file are subject to the Mozilla Public License +Version 1.1 (the "License"); you may not use this file except in +compliance with the License. You may obtain a copy of the License at +http://www.mozilla.org/MPL/ + +Software distributed under the License is distributed on an "AS IS" +basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +License for the specific language governing rights and limitations +under the License. + +The Original Code is expat. + +The Initial Developer of the Original Code is James Clark. +Portions created by James Clark are Copyright (C) 1998, 1999 +James Clark. All Rights Reserved. + +Contributor(s): + +Alternatively, the contents of this file may be used under the terms +of the GNU General Public License (the "GPL"), in which case the +provisions of the GPL are applicable instead of those above. If you +wish to allow use of your version of this file only under the terms of +the GPL and not to allow others to use your version of this file under +the MPL, indicate your decision by deleting the provisions above and +replace them with the notice and other provisions required by the +GPL. If you do not delete the provisions above, a recipient may use +your version of this file under either the MPL or the GPL. +*/ + +/* Like asciitab.h, except that 0xD has code BT_S rather than BT_CR */ +/* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML, +/* 0x0C */ BT_NONXML, BT_S, BT_NONXML, BT_NONXML, +/* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM, +/* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS, +/* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS, +/* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL, +/* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, +/* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, +/* 0x38 */ BT_DIGIT, BT_DIGIT, BT_COLON, BT_SEMI, +/* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST, +/* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, +/* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, +/* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB, +/* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT, +/* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, +/* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, +/* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, +/* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER, diff --git a/protocols/jabber/jabber.c b/protocols/jabber/jabber.c new file mode 100644 index 00000000..931a2182 --- /dev/null +++ b/protocols/jabber/jabber.c @@ -0,0 +1,2445 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * gaim + * + * Some code copyright (C) 1998-1999, Mark Spencer <markster@marko.net> + * libfaim code copyright 1998, 1999 Adam Fritzler <afritz@auk.cx> + * + * This program is free software; you can 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 + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef _WIN32 +#include <sys/utsname.h> +#endif +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <time.h> +#include <sys/stat.h> +#include "jabber.h" +#include "nogaim.h" +#include "bitlbee.h" +#include "proxy.h" +#include "ssl_client.h" + +/* The priv member of gjconn's is a gaim_connection for now. */ +#define GJ_GC(x) ((struct gaim_connection *)(x)->priv) + +#define IQID_AUTH "__AUTH__" + +#define IQ_NONE -1 +#define IQ_AUTH 0 +#define IQ_ROSTER 1 + +#define UC_AWAY (0x02 | UC_UNAVAILABLE) +#define UC_CHAT 0x04 +#define UC_XA (0x08 | UC_UNAVAILABLE) +#define UC_DND (0x10 | UC_UNAVAILABLE) + +#define DEFAULT_SERVER "jabber.org" +#define DEFAULT_GROUPCHAT "conference.jabber.org" +#define DEFAULT_PORT 5222 +#define DEFAULT_PORT_SSL 5223 + +#define JABBER_GROUP "Friends" + +/* i18n disabled - Bitlbee */ +#define N_(String) String + +/* + * Note: "was_connected" may seem redundant, but it was needed and I + * didn't want to touch the Jabber state stuff not specific to Gaim. + */ +typedef struct gjconn_struct { + /* Core structure */ + pool p; /* Memory allocation pool */ + int state; /* Connection state flag */ + int was_connected; /* We were once connected */ + int fd; /* Connection file descriptor */ + void *ssl; /* SSL connection */ + jid user; /* User info */ + char *pass; /* User passwd */ + + /* Stream stuff */ + int id; /* id counter for jab_getid() function */ + char idbuf[9]; /* temporary storage for jab_getid() */ + char *sid; /* stream id from server, for digest auth */ + XML_Parser parser; /* Parser instance */ + xmlnode current; /* Current node in parsing instance.. */ + + /* Event callback ptrs */ + void (*on_state)(struct gjconn_struct *gjc, int state); + void (*on_packet)(struct gjconn_struct *gjc, jpacket p); + + GHashTable *queries; /* query tracker */ + + void *priv; +} *gjconn, gjconn_struct; + +typedef void (*gjconn_state_h)(gjconn gjc, int state); +typedef void (*gjconn_packet_h)(gjconn gjc, jpacket p); + +static gjconn gjab_new(char *user, char *pass, void *priv); +static void gjab_delete(gjconn gjc); +static void gjab_state_handler(gjconn gjc, gjconn_state_h h); +static void gjab_packet_handler(gjconn gjc, gjconn_packet_h h); +static void gjab_start(gjconn gjc); +static void gjab_stop(gjconn gjc); +/* +static int gjab_getfd(gjconn gjc); +static jid gjab_getjid(gjconn gjc); +static char *gjab_getsid(gjconn gjc); +*/ +static char *gjab_getid(gjconn gjc); +static void gjab_send(gjconn gjc, xmlnode x); +static void gjab_send_raw(gjconn gjc, const char *str); +static void gjab_recv(gjconn gjc); +static void gjab_auth(gjconn gjc); + +/* + * It is *this* to which we point the gaim_connection proto_data + */ +struct jabber_data { + gjconn gjc; + gboolean did_import; + GSList *chats; + GHashTable *hash; + time_t idle; + gboolean die; +}; + +/* + * Jabber "chat group" info. Pointers to these go in jabber_data + * pending and existing chats lists. + */ +struct jabber_chat { + jid Jid; + struct gaim_connection *gc; + struct conversation *b; + int id; + int state; +}; + +/* + * Jabber chat states... + * + * Note: due to a bug in one version of the Jabber server, subscriptions + * to chat groups aren't (always?) properly removed at the server. The + * result is clients receive Jabber "presence" notifications for JIDs + * they no longer care about. The problem with such vestigial notifies is + * that we really have no way of telling if it's vestigial or if it's a + * valid "buddy" presence notification. So we keep jabber_chat structs + * around after leaving a chat group and simply mark them "closed." That + * way we can test for such errant presence notifications. I.e.: if we + * get a presence notfication from a JID that matches a chat group JID, + * we disregard it. + */ +#define JCS_PENDING 1 /* pending */ +#define JCS_ACTIVE 2 /* active */ +#define JCS_CLOSED 3 /* closed */ + + +static char *jabber_name() +{ + return "Jabber"; +} + +#define STATE_EVT(arg) if(gjc->on_state) { (gjc->on_state)(gjc, (arg) ); } + +static void jabber_remove_buddy(struct gaim_connection *gc, char *name, char *group); +static void jabber_handlevcard(gjconn gjc, xmlnode querynode, char *from); + +static char *create_valid_jid(const char *given, char *server, char *resource) +{ + char *valid; + + if (!strchr(given, '@')) + valid = g_strdup_printf("%s@%s/%s", given, server, resource); + else if (!strchr(strchr(given, '@'), '/')) + valid = g_strdup_printf("%s/%s", given, resource); + else + valid = g_strdup(given); + + return valid; +} + +static gjconn gjab_new(char *user, char *pass, void *priv) +{ + pool p; + gjconn gjc; + + if (!user) + return (NULL); + + p = pool_new(); + if (!p) + return (NULL); + gjc = pmalloc_x(p, sizeof(gjconn_struct), 0); + if (!gjc) { + pool_free(p); /* no need for this anymore! */ + return (NULL); + } + gjc->p = p; + + if((gjc->user = jid_new(p, user)) == NULL) { + pool_free(p); /* no need for this anymore! */ + return (NULL); + } + gjc->pass = pstrdup(p, pass); + + gjc->state = JCONN_STATE_OFF; + gjc->was_connected = 0; + gjc->id = 1; + gjc->fd = -1; + + gjc->priv = priv; + + return gjc; +} + +static void gjab_delete(gjconn gjc) +{ + if (!gjc) + return; + + gjab_stop(gjc); + pool_free(gjc->p); +} + +static void gjab_state_handler(gjconn gjc, gjconn_state_h h) +{ + if (!gjc) + return; + + gjc->on_state = h; +} + +static void gjab_packet_handler(gjconn gjc, gjconn_packet_h h) +{ + if (!gjc) + return; + + gjc->on_packet = h; +} + +static void gjab_stop(gjconn gjc) +{ + if (!gjc || gjc->state == JCONN_STATE_OFF) + return; + + gjab_send_raw(gjc, "</stream:stream>"); + gjc->state = JCONN_STATE_OFF; + gjc->was_connected = 0; + if (gjc->ssl) { + ssl_disconnect(gjc->ssl); + gjc->ssl = NULL; + } else { + closesocket(gjc->fd); + } + gjc->fd = -1; + XML_ParserFree(gjc->parser); + gjc->parser = NULL; +} + +/* +static int gjab_getfd(gjconn gjc) +{ + if (gjc) + return gjc->fd; + else + return -1; +} + +static jid gjab_getjid(gjconn gjc) +{ + if (gjc) + return (gjc->user); + else + return NULL; +} + +static char *gjab_getsid(gjconn gjc) +{ + if (gjc) + return (gjc->sid); + else + return NULL; +} +*/ + +static char *gjab_getid(gjconn gjc) +{ + g_snprintf(gjc->idbuf, 8, "%d", gjc->id++); + return &gjc->idbuf[0]; +} + +static void gjab_send(gjconn gjc, xmlnode x) +{ + if (gjc && gjc->state != JCONN_STATE_OFF) { + char *buf = xmlnode2str(x); + if (!buf) + return; + else if (gjc->ssl) + ssl_write(gjc->ssl, buf, strlen(buf)); + else + write(gjc->fd, buf, strlen(buf)); + } +} + +static void gjab_send_raw(gjconn gjc, const char *str) +{ + if (gjc && gjc->state != JCONN_STATE_OFF) { + int len; + + /* + * JFIXME: No error detection?!?! + */ + if (gjc->ssl) + len = ssl_write(gjc->ssl, str, strlen(str)); + else + len = write(gjc->fd, str, strlen(str)); + + if(len < 0) { + /* Do NOT write to stdout/stderr directly, IRC clients + might get confused, and we don't want that... + fprintf(stderr, "DBG: Problem sending. Error: %d\n", errno); + fflush(stderr); */ + } + } +} + +static void gjab_reqroster(gjconn gjc) +{ + xmlnode x; + + x = jutil_iqnew(JPACKET__GET, NS_ROSTER); + xmlnode_put_attrib(x, "id", gjab_getid(gjc)); + + gjab_send(gjc, x); + xmlnode_free(x); +} + +static void gjab_reqauth(gjconn gjc) +{ + xmlnode x, y, z; + char *user; + + if (!gjc) + return; + + x = jutil_iqnew(JPACKET__GET, NS_AUTH); + xmlnode_put_attrib(x, "id", IQID_AUTH); + y = xmlnode_get_tag(x, "query"); + + user = gjc->user->user; + + if (user) { + z = xmlnode_insert_tag(y, "username"); + xmlnode_insert_cdata(z, user, -1); + } + + gjab_send(gjc, x); + xmlnode_free(x); +} + +static void gjab_auth(gjconn gjc) +{ + xmlnode x, y, z; + char *hash, *user; + + if (!gjc) + return; + + x = jutil_iqnew(JPACKET__SET, NS_AUTH); + xmlnode_put_attrib(x, "id", IQID_AUTH); + y = xmlnode_get_tag(x, "query"); + + user = gjc->user->user; + + if (user) { + z = xmlnode_insert_tag(y, "username"); + xmlnode_insert_cdata(z, user, -1); + } + + z = xmlnode_insert_tag(y, "resource"); + xmlnode_insert_cdata(z, gjc->user->resource, -1); + + if (gjc->sid) { + z = xmlnode_insert_tag(y, "digest"); + hash = pmalloc(x->p, strlen(gjc->sid) + strlen(gjc->pass) + 1); + strcpy(hash, gjc->sid); + strcat(hash, gjc->pass); + hash = shahash(hash); + xmlnode_insert_cdata(z, hash, 40); + } else { + z = xmlnode_insert_tag(y, "password"); + xmlnode_insert_cdata(z, gjc->pass, -1); + } + + gjab_send(gjc, x); + xmlnode_free(x); + + return; +} + +static void gjab_recv(gjconn gjc) +{ + static char buf[4096]; + int len; + + if (!gjc || gjc->state == JCONN_STATE_OFF) + return; + + if (gjc->ssl) + len = ssl_read(gjc->ssl, buf, sizeof(buf) - 1); + else + len = read(gjc->fd, buf, sizeof(buf) - 1); + + if (len > 0) { + struct jabber_data *jd = GJ_GC(gjc)->proto_data; + buf[len] = '\0'; + XML_Parse(gjc->parser, buf, len, 0); + if (jd->die) + signoff(GJ_GC(gjc)); + } else if (len < 0 || errno != EAGAIN) { + STATE_EVT(JCONN_STATE_OFF) + } +} + +static void startElement(void *userdata, const char *name, const char **attribs) +{ + xmlnode x; + gjconn gjc = (gjconn) userdata; + + if (gjc->current) { + /* Append the node to the current one */ + x = xmlnode_insert_tag(gjc->current, name); + xmlnode_put_expat_attribs(x, attribs); + + gjc->current = x; + } else { + x = xmlnode_new_tag(name); + xmlnode_put_expat_attribs(x, attribs); + if (strcmp(name, "stream:stream") == 0) { + /* special case: name == stream:stream */ + /* id attrib of stream is stored for digest auth */ + gjc->sid = g_strdup(xmlnode_get_attrib(x, "id")); + /* STATE_EVT(JCONN_STATE_AUTH) */ + xmlnode_free(x); + } else { + gjc->current = x; + } + } +} + +static void endElement(void *userdata, const char *name) +{ + gjconn gjc = (gjconn) userdata; + xmlnode x; + jpacket p; + + if (gjc->current == NULL) { + /* we got </stream:stream> */ + STATE_EVT(JCONN_STATE_OFF) + return; + } + + x = xmlnode_get_parent(gjc->current); + + if (!x) { + /* it is time to fire the event */ + p = jpacket_new(gjc->current); + + if (gjc->on_packet) + (gjc->on_packet) (gjc, p); + else + xmlnode_free(gjc->current); + } + + gjc->current = x; +} + +static void jabber_callback(gpointer data, gint source, GaimInputCondition condition) +{ + struct gaim_connection *gc = (struct gaim_connection *)data; + struct jabber_data *jd = (struct jabber_data *)gc->proto_data; + + gjab_recv(jd->gjc); +} + +static void charData(void *userdata, const char *s, int slen) +{ + gjconn gjc = (gjconn) userdata; + + if (gjc->current) + xmlnode_insert_cdata(gjc->current, s, slen); +} + +static void gjab_connected(gpointer data, gint source, GaimInputCondition cond) +{ + xmlnode x; + char *t, *t2; + struct gaim_connection *gc = data; + struct jabber_data *jd; + gjconn gjc; + + if (!g_slist_find(get_connections(), gc)) { + closesocket(source); + return; + } + + jd = gc->proto_data; + gjc = jd->gjc; + + if (gjc->fd != source) + gjc->fd = source; + + if (source == -1) { + STATE_EVT(JCONN_STATE_OFF) + return; + } + + gjc->state = JCONN_STATE_CONNECTED; + STATE_EVT(JCONN_STATE_CONNECTED) + + /* start stream */ + x = jutil_header(NS_CLIENT, gjc->user->server); + t = xmlnode2str(x); + /* this is ugly, we can create the string here instead of jutil_header */ + /* what do you think about it? -madcat */ + t2 = strstr(t, "/>"); + *t2++ = '>'; + *t2 = '\0'; + gjab_send_raw(gjc, "<?xml version='1.0'?>"); + gjab_send_raw(gjc, t); + xmlnode_free(x); + + gjc->state = JCONN_STATE_ON; + STATE_EVT(JCONN_STATE_ON); + + gc = GJ_GC(gjc); + gc->inpa = gaim_input_add(gjc->fd, GAIM_INPUT_READ, jabber_callback, gc); +} + +static void gjab_connected_ssl(gpointer data, void *source, GaimInputCondition cond) +{ + struct gaim_connection *gc = data; + struct jabber_data *jd; + gjconn gjc; + + if (!g_slist_find(get_connections(), gc)) { + ssl_disconnect(source); + return; + } + + jd = gc->proto_data; + gjc = jd->gjc; + + if (source == NULL) { + STATE_EVT(JCONN_STATE_OFF) + return; + } + + gjab_connected(data, gjc->fd, cond); +} + +static void gjab_start(gjconn gjc) +{ + struct aim_user *user; + int port = -1, ssl = 0; + char *server = NULL, *s; + + if (!gjc || gjc->state != JCONN_STATE_OFF) + return; + + user = GJ_GC(gjc)->user; + if (*user->proto_opt[0]) { + /* If there's a dot, assume there's a hostname in the beginning */ + if (strchr(user->proto_opt[0], '.')) { + server = g_strdup(user->proto_opt[0]); + if ((s = strchr(server, ':'))) + *s = 0; + } + + /* After the hostname, there can be a port number */ + s = strchr(user->proto_opt[0], ':'); + if (s && isdigit(s[1])) + sscanf(s + 1, "%d", &port); + + /* And if there's the string ssl, the user wants an SSL-connection */ + if (strstr(user->proto_opt[0], ":ssl") || g_strcasecmp(user->proto_opt[0], "ssl") == 0) + ssl = 1; + } + + if (port == -1 && !ssl) + port = DEFAULT_PORT; + else if (port == -1 && ssl) + port = DEFAULT_PORT_SSL; + + if (server == NULL) + server = g_strdup(gjc->user->server); + + gjc->parser = XML_ParserCreate(NULL); + XML_SetUserData(gjc->parser, (void *)gjc); + XML_SetElementHandler(gjc->parser, startElement, endElement); + XML_SetCharacterDataHandler(gjc->parser, charData); + + if (ssl) { + if ((gjc->ssl = ssl_connect(server, port, gjab_connected_ssl, GJ_GC(gjc)))) + gjc->fd = ssl_getfd(gjc->ssl); + else + gjc->fd = -1; + } else { + gjc->fd = proxy_connect(server, port, gjab_connected, GJ_GC(gjc)); + } + + g_free(server); + + if (!user->gc || (gjc->fd < 0)) { + STATE_EVT(JCONN_STATE_OFF) + return; + } +} + +/* + * Find existing/active Jabber chat + */ +static struct jabber_chat *find_existing_chat(struct gaim_connection *gc, jid chat) +{ + GSList *jcs = ((struct jabber_data *)gc->proto_data)->chats; + struct jabber_chat *jc = NULL; + + while (jcs) { + jc = jcs->data; + if (jc->state == JCS_ACTIVE && !jid_cmpx(chat, jc->Jid, JID_USER | JID_SERVER)) + break; + jc = NULL; + jcs = jcs->next; + } + + return jc; +} + +/* + * Find pending chat + */ +static struct jabber_chat *find_pending_chat(struct gaim_connection *gc, jid chat) +{ + GSList *jcs = ((struct jabber_data *)gc->proto_data)->chats; + struct jabber_chat *jc = NULL; + + while (jcs) { + jc = jcs->data; + if (jc->state == JCS_PENDING && !jid_cmpx(chat, jc->Jid, JID_USER | JID_SERVER)) + break; + jc = NULL; + jcs = jcs->next; + } + + return jc; +} + +static gboolean find_chat_buddy(struct conversation *b, char *name) +{ + GList *m = b->in_room; + + while (m) { + if (!strcmp(m->data, name)) + return TRUE; + m = m->next; + } + + return FALSE; +} + +/* + * Remove a buddy from the (gaim) buddylist (if he's on it) + */ +static void jabber_remove_gaim_buddy(struct gaim_connection *gc, char *buddyname) +{ + struct buddy *b; + + if ((b = find_buddy(gc, buddyname)) != NULL) { + /* struct group *group; + + group = find_group_by_buddy(gc, buddyname); + remove_buddy(gc, group, b); */ + jabber_remove_buddy(gc, b->name, JABBER_GROUP); + } +} + +/* + * keep track of away msg same as yahoo plugin + */ +static void jabber_track_away(gjconn gjc, jpacket p, char *name, char *type) +{ + struct jabber_data *jd = GJ_GC(gjc)->proto_data; + gpointer val = g_hash_table_lookup(jd->hash, name); + char *show; + char *vshow = NULL; + char *status = NULL; + char *msg = NULL; + + if (type && (g_strcasecmp(type, "unavailable") == 0)) { + vshow = _("Unavailable"); + } else { + if((show = xmlnode_get_tag_data(p->x, "show")) != NULL) { + if (!g_strcasecmp(show, "away")) { + vshow = _("Away"); + } else if (!g_strcasecmp(show, "chat")) { + vshow = _("Online"); + } else if (!g_strcasecmp(show, "xa")) { + vshow = _("Extended Away"); + } else if (!g_strcasecmp(show, "dnd")) { + vshow = _("Do Not Disturb"); + } + } + } + + status = xmlnode_get_tag_data(p->x, "status"); + + if(vshow != NULL || status != NULL ) { + /* kinda hokey, but it works :-) */ + msg = g_strdup_printf("%s%s%s", + (vshow == NULL? "" : vshow), + (vshow == NULL || status == NULL? "" : ": "), + (status == NULL? "" : status)); + } else { + msg = g_strdup(_("Online")); + } + + if (val) { + g_free(val); + g_hash_table_insert(jd->hash, name, msg); + } else { + g_hash_table_insert(jd->hash, g_strdup(name), msg); + } +} + +static time_t iso8601_to_time(char *timestamp) +{ + struct tm t; + time_t retval = 0; + + if(sscanf(timestamp,"%04d%02d%02dT%02d:%02d:%02d", + &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec)) + { + t.tm_year -= 1900; + t.tm_mon -= 1; + t.tm_isdst = 0; + retval = mktime(&t); +# ifdef HAVE_TM_GMTOFF + retval += t.tm_gmtoff; +# else +# ifdef HAVE_TIMEZONE + tzset(); /* making sure */ + retval -= timezone; +# endif +# endif + } + + return retval; +} + +static void jabber_handlemessage(gjconn gjc, jpacket p) +{ + xmlnode y, xmlns, subj, z; + time_t time_sent = time(NULL); + + char *from = NULL, *msg = NULL, *type = NULL, *topic = NULL; + char m[BUF_LONG * 2]; + + type = xmlnode_get_attrib(p->x, "type"); + + z = xmlnode_get_firstchild(p->x); + + while(z) + { + if(NSCHECK(z,NS_DELAY)) + { + char *timestamp = xmlnode_get_attrib(z,"stamp"); + time_sent = iso8601_to_time(timestamp); + } + z = xmlnode_get_nextsibling(z); + } + + if (!type || !g_strcasecmp(type, "normal") || !g_strcasecmp(type, "chat")) { + + /* XXX namespaces could be handled better. (mid) */ + if ((xmlns = xmlnode_get_tag(p->x, "x"))) + type = xmlnode_get_attrib(xmlns, "xmlns"); + + from = jid_full(p->from); + /* + if ((y = xmlnode_get_tag(p->x, "html"))) { + msg = xmlnode_get_data(y); + } else + */ + if ((y = xmlnode_get_tag(p->x, "body"))) { + msg = xmlnode_get_data(y); + } + + + if (!from) + return; + + if (type && !g_strcasecmp(type, "jabber:x:conference")) { + char *room; + GList *m = NULL; + char **data; + + room = xmlnode_get_attrib(xmlns, "jid"); + data = g_strsplit(room, "@", 2); + m = g_list_append(m, g_strdup(data[0])); + m = g_list_append(m, g_strdup(data[1])); + m = g_list_append(m, g_strdup(gjc->user->user)); + g_strfreev(data); + + /* ** Bitlbee ** serv_got_chat_invite(GJ_GC(gjc), room, from, msg, m); */ + } else if (msg) { /* whisper */ + struct jabber_chat *jc; + g_snprintf(m, sizeof(m), "%s", msg); + if (((jc = find_existing_chat(GJ_GC(gjc), p->from)) != NULL) && jc->b) + serv_got_chat_in(GJ_GC(gjc), jc->b->id, p->from->resource, 1, m, time_sent); + else { + int flags = 0; + /* ** Bitlbee ** + if (xmlnode_get_tag(p->x, "gaim")) + flags = IM_FLAG_GAIMUSER; + if (find_conversation(jid_full(p->from))) + serv_got_im(GJ_GC(gjc), jid_full(p->from), m, flags, time_sent, -1); + else { + ** End - Bitlbee ** */ + if(p->from->user) { + from = g_strdup_printf("%s@%s", p->from->user, p->from->server); + } else { + /* server message? */ + from = g_strdup(p->from->server); + } + serv_got_im(GJ_GC(gjc), from, m, flags, time_sent, -1); + g_free(from); + /* ** Bitlbee ** } ** End - Bitlbee ** */ + } + } + + } else if (!g_strcasecmp(type, "error")) { + if ((y = xmlnode_get_tag(p->x, "error"))) { + type = xmlnode_get_attrib(y, "code"); + msg = xmlnode_get_data(y); + } + + if (msg) { + from = g_strdup_printf("Error %s", type ? type : ""); + do_error_dialog(GJ_GC(gjc), msg, from); + g_free(from); + } + } else if (!g_strcasecmp(type, "groupchat")) { + struct jabber_chat *jc; + static int i = 0; + + /* + if ((y = xmlnode_get_tag(p->x, "html"))) { + msg = xmlnode_get_data(y); + } else + */ + if ((y = xmlnode_get_tag(p->x, "body"))) { + msg = xmlnode_get_data(y); + } + + msg = utf8_to_str(msg); + + if ((subj = xmlnode_get_tag(p->x, "subject"))) { + topic = xmlnode_get_data(subj); + } + topic = utf8_to_str(topic); + + jc = find_existing_chat(GJ_GC(gjc), p->from); + if (!jc) { + /* we're not in this chat. are we supposed to be? */ + if ((jc = find_pending_chat(GJ_GC(gjc), p->from)) != NULL) { + /* yes, we're supposed to be. so now we are. */ + jc->b = serv_got_joined_chat(GJ_GC(gjc), i++, p->from->user); + jc->id = jc->b->id; + jc->state = JCS_ACTIVE; + } else { + /* no, we're not supposed to be. */ + g_free(msg); + return; + } + } + if (p->from->resource) { + if (!y) { + if (!find_chat_buddy(jc->b, p->from->resource)) { + add_chat_buddy(jc->b, p->from->resource); + } else if ((y = xmlnode_get_tag(p->x, "status"))) { + char *buf; + + buf = g_strdup_printf("%s@%s/%s", + p->from->user, p->from->server, p->from->resource); + jabber_track_away(gjc, p, buf, NULL); + g_free(buf); + + } + } else if (jc->b && msg) { + char buf[8192]; + + if (topic) { + char tbuf[8192]; + g_snprintf(tbuf, sizeof(tbuf), "%s", topic); + } + + + g_snprintf(buf, sizeof(buf), "%s", msg); + serv_got_chat_in(GJ_GC(gjc), jc->b->id, p->from->resource, 0, buf, time_sent); + } + } else { /* message from the server */ + if(jc->b && topic) { + char tbuf[8192]; + g_snprintf(tbuf, sizeof(tbuf), "%s", topic); + } + } + + g_free(msg); + g_free(topic); + + } +} + +static void jabber_handlepresence(gjconn gjc, jpacket p) +{ + char *to, *from, *type; + struct buddy *b = NULL; + jid who; + char *buddy; + xmlnode y; + char *show; + int state = 0; + GSList *resources; + char *res; + struct conversation *cnv = NULL; + struct jabber_chat *jc = NULL; + + to = xmlnode_get_attrib(p->x, "to"); + from = xmlnode_get_attrib(p->x, "from"); + type = xmlnode_get_attrib(p->x, "type"); + + if (type && g_strcasecmp(type, "error") == 0) { + return; + } + else if ((y = xmlnode_get_tag(p->x, "show"))) { + show = xmlnode_get_data(y); + if (!show) { + state = 0; + } else if (!g_strcasecmp(show, "away")) { + state = UC_AWAY; + } else if (!g_strcasecmp(show, "chat")) { + state = UC_CHAT; + } else if (!g_strcasecmp(show, "xa")) { + state = UC_XA; + } else if (!g_strcasecmp(show, "dnd")) { + state = UC_DND; + } + } else { + state = 0; + } + + who = jid_new(gjc->p, from); + if (who->user == NULL) { + /* FIXME: transport */ + return; + } + + buddy = g_strdup_printf("%s@%s", who->user, who->server); + + /* um. we're going to check if it's a chat. if it isn't, and there are pending + * chats, create the chat. if there aren't pending chats and we don't have the + * buddy on our list, simply bail out. */ + if ((cnv = NULL) == NULL) { + static int i = 0x70; + if ((jc = find_pending_chat(GJ_GC(gjc), who)) != NULL) { + jc->b = cnv = serv_got_joined_chat(GJ_GC(gjc), i++, who->user); + jc->id = jc->b->id; + jc->state = JCS_ACTIVE; + } else if ((b = find_buddy(GJ_GC(gjc), buddy)) == NULL) { + g_free(buddy); + return; + } + } + + if (!cnv) { + resources = b->proto_data; + res = who->resource; + if (res) + while (resources) { + if (!strcmp(res, resources->data)) + break; + resources = resources->next; + } + + /* keep track of away msg same as yahoo plugin */ + jabber_track_away(gjc, p, normalize(b->name), type); + + if (type && (g_strcasecmp(type, "unavailable") == 0)) { + if (resources) { + g_free(resources->data); + b->proto_data = g_slist_remove(b->proto_data, resources->data); + } + if (!b->proto_data) { + serv_got_update(GJ_GC(gjc), buddy, 0, 0, 0, 0, 0, 0); + } + } else { + if (!resources) { + b->proto_data = g_slist_append(b->proto_data, g_strdup(res)); + } + + serv_got_update(GJ_GC(gjc), buddy, 1, 0, b->signon, b->idle, state, 0); + + } + } else { + if (who->resource) { + char *buf; + + buf = g_strdup_printf("%s@%s/%s", who->user, who->server, who->resource); + jabber_track_away(gjc, p, buf, type); + g_free(buf); + + if (type && !g_strcasecmp(type, "unavailable")) { + struct jabber_data *jd; + if (!jc && !(jc = find_existing_chat(GJ_GC(gjc), who))) { + g_free(buddy); + return; + } + jd = jc->gc->proto_data; + /* if it's not ourselves...*/ + if (strcmp(who->resource, jc->Jid->resource) && jc->b) { + remove_chat_buddy(jc->b, who->resource, NULL); + g_free(buddy); + return; + } + + jc->state = JCS_CLOSED; + serv_got_chat_left(GJ_GC(gjc), jc->id); + /* + * TBD: put back some day? + jd->chats = g_slist_remove(jd->chats, jc); + g_free(jc); + */ + } else { + if ((!jc && !(jc = find_existing_chat(GJ_GC(gjc), who))) || !jc->b) { + g_free(buddy); + return; + } + if (!find_chat_buddy(jc->b, who->resource)) { + add_chat_buddy(jc->b, who->resource); + } + } + } + } + + g_free(buddy); + + return; +} + +/* + * Used only by Jabber accept/deny add stuff just below + */ +struct jabber_add_permit { + gjconn gjc; + gchar *user; +}; + +/* + * Common part for Jabber accept/deny adds + * + * "type" says whether we'll permit/deny the subscribe request + */ +static void jabber_accept_deny_add(struct jabber_add_permit *jap, const char *type) +{ + xmlnode g = xmlnode_new_tag("presence"); + + xmlnode_put_attrib(g, "to", jap->user); + xmlnode_put_attrib(g, "type", type); + gjab_send(jap->gjc, g); + + xmlnode_free(g); +} + +/* + * Callback from "accept" in do_ask_dialog() invoked by jabber_handles10n() + */ +static void jabber_accept_add(gpointer w, struct jabber_add_permit *jap) +{ + jabber_accept_deny_add(jap, "subscribed"); + /* + * If we don't already have the buddy on *our* buddylist, + * ask if we want him or her added. + */ + if(find_buddy(GJ_GC(jap->gjc), jap->user) == NULL) { + show_got_added(GJ_GC(jap->gjc), NULL, jap->user, NULL, NULL); + } + g_free(jap->user); + g_free(jap); +} + +/* + * Callback from "deny/cancel" in do_ask_dialog() invoked by jabber_handles10n() + */ +static void jabber_deny_add(gpointer w, struct jabber_add_permit *jap) +{ + jabber_accept_deny_add(jap, "unsubscribed"); + g_free(jap->user); + g_free(jap); +} + +/* + * Handle subscription requests + */ +static void jabber_handles10n(gjconn gjc, jpacket p) +{ + xmlnode g; + char *Jid = xmlnode_get_attrib(p->x, "from"); + char *type = xmlnode_get_attrib(p->x, "type"); + + g = xmlnode_new_tag("presence"); + xmlnode_put_attrib(g, "to", Jid); + + if (!strcmp(type, "subscribe")) { + /* + * A "subscribe to us" request was received - put up the approval dialog + */ + struct jabber_add_permit *jap = g_new0(struct jabber_add_permit, 1); + gchar *msg = g_strdup_printf(_("The user %s wants to add you to their buddy list."), + Jid); + + jap->gjc = gjc; + jap->user = g_strdup(Jid); + do_ask_dialog(GJ_GC(gjc), msg, jap, jabber_accept_add, jabber_deny_add); + + g_free(msg); + xmlnode_free(g); /* Never needed it here anyway */ + return; + + } else if (!strcmp(type, "unsubscribe")) { + /* + * An "unsubscribe to us" was received - simply "approve" it + */ + xmlnode_put_attrib(g, "type", "unsubscribed"); + } else { + /* + * Did we attempt to subscribe to somebody and they do not exist? + */ + if (!strcmp(type, "unsubscribed")) { + xmlnode y; + char *status; + if((y = xmlnode_get_tag(p->x, "status")) && (status = xmlnode_get_data(y)) && + !strcmp(status, "Not Found")) { + char *msg = g_strdup_printf("%s: \"%s\"", _("No such user"), + xmlnode_get_attrib(p->x, "from")); + do_error_dialog(GJ_GC(gjc), msg, _("Jabber Error")); + g_free(msg); + } + } + + xmlnode_free(g); + return; + } + + gjab_send(gjc, g); + xmlnode_free(g); +} + +/* + * Pending subscription to a buddy? + */ +#define BUD_SUB_TO_PEND(sub, ask) ((!g_strcasecmp((sub), "none") || !g_strcasecmp((sub), "from")) && \ + (ask) != NULL && !g_strcasecmp((ask), "subscribe")) + +/* + * Subscribed to a buddy? + */ +#define BUD_SUBD_TO(sub, ask) ((!g_strcasecmp((sub), "to") || !g_strcasecmp((sub), "both")) && \ + ((ask) == NULL || !g_strcasecmp((ask), "subscribe"))) + +/* + * Pending unsubscription to a buddy? + */ +#define BUD_USUB_TO_PEND(sub, ask) ((!g_strcasecmp((sub), "to") || !g_strcasecmp((sub), "both")) && \ + (ask) != NULL && !g_strcasecmp((ask), "unsubscribe")) + +/* + * Unsubscribed to a buddy? + */ +#define BUD_USUBD_TO(sub, ask) ((!g_strcasecmp((sub), "none") || !g_strcasecmp((sub), "from")) && \ + ((ask) == NULL || !g_strcasecmp((ask), "unsubscribe"))) + +/* + * If a buddy is added or removed from the roster on another resource + * jabber_handlebuddy is called + * + * Called with roster item node. + */ +static void jabber_handlebuddy(gjconn gjc, xmlnode x) +{ + xmlnode g; + char *Jid, *name, *sub, *ask; + jid who; + struct buddy *b = NULL; + char *buddyname, *groupname = NULL; + + Jid = xmlnode_get_attrib(x, "jid"); + name = xmlnode_get_attrib(x, "name"); + sub = xmlnode_get_attrib(x, "subscription"); + ask = xmlnode_get_attrib(x, "ask"); + who = jid_new(gjc->p, Jid); + + /* JFIXME: jabber_handleroster() had a "FIXME: transport" at this + * equivilent point. So... + * + * We haven't allocated any memory or done anything interesting to + * this point, so we'll violate Good Coding Structure here by + * simply bailing out. + */ + if (!who || !who->user) { + return; + } + + buddyname = g_strdup_printf("%s@%s", who->user, who->server); + + if((g = xmlnode_get_tag(x, "group")) != NULL) { + groupname = xmlnode_get_data(g); + } + + /* + * Add or remove a buddy? Change buddy's alias or group? + */ + if (BUD_SUB_TO_PEND(sub, ask) || BUD_SUBD_TO(sub, ask)) { + if ((b = find_buddy(GJ_GC(gjc), buddyname)) == NULL) { + add_buddy(GJ_GC(gjc), groupname ? groupname : _("Buddies"), buddyname, + name ? name : buddyname); + } else { + /* struct group *c_grp = find_group_by_buddy(GJ_GC(gjc), buddyname); */ + + /* + * If the buddy's in a new group or his/her alias is changed... + */ + if(groupname) { + int present = b->present; /* save presence state */ + int uc = b->uc; /* and away state (?) */ + int idle = b->idle; + int signon = b->signon; + + /* + * seems rude, but it seems to be the only way... + */ + /* remove_buddy(GJ_GC(gjc), c_grp, b); */ + jabber_remove_buddy(GJ_GC(gjc), b->name, JABBER_GROUP); + + add_buddy(GJ_GC(gjc), groupname, buddyname, + name ? name : buddyname); + if(present) { + serv_got_update(GJ_GC(gjc), buddyname, 1, 0, signon, idle, uc, 0); + } + } else if(name != NULL && strcmp(b->show, name)) { + strncpy(b->show, name, BUDDY_ALIAS_MAXLEN); + b->show[BUDDY_ALIAS_MAXLEN - 1] = '\0'; /* cheap safety feature */ + serv_buddy_rename(GJ_GC(gjc), buddyname, b->show); + } + } + } else if (BUD_USUB_TO_PEND(sub, ask) || BUD_USUBD_TO(sub, ask) || !g_strcasecmp(sub, "remove")) { + jabber_remove_gaim_buddy(GJ_GC(gjc), buddyname); + } + g_free(buddyname); + +} + +static void jabber_handleroster(gjconn gjc, xmlnode querynode) +{ + xmlnode x; + + x = xmlnode_get_firstchild(querynode); + while (x) { + jabber_handlebuddy(gjc, x); + x = xmlnode_get_nextsibling(x); + } + + x = jutil_presnew(0, NULL, "Online"); + gjab_send(gjc, x); + xmlnode_free(x); +} + +static void jabber_handleauthresp(gjconn gjc, jpacket p) +{ + if (jpacket_subtype(p) == JPACKET__RESULT) { + if (xmlnode_has_children(p->x)) { + xmlnode query = xmlnode_get_tag(p->x, "query"); + set_login_progress(GJ_GC(gjc), 4, _("Authenticating")); + if (!xmlnode_get_tag(query, "digest")) { + g_free(gjc->sid); + gjc->sid = NULL; + } + gjab_auth(gjc); + } else { + account_online(GJ_GC(gjc)); + + if (bud_list_cache_exists(GJ_GC(gjc))) + do_import(GJ_GC(gjc), NULL); + + ((struct jabber_data *)GJ_GC(gjc)->proto_data)->did_import = TRUE; + + gjab_reqroster(gjc); + } + } else { + xmlnode xerr; + char *errmsg = NULL; + int errcode = 0; + struct jabber_data *jd = GJ_GC(gjc)->proto_data; + + xerr = xmlnode_get_tag(p->x, "error"); + if (xerr) { + char msg[BUF_LONG]; + errmsg = xmlnode_get_data(xerr); + if (xmlnode_get_attrib(xerr, "code")) { + errcode = atoi(xmlnode_get_attrib(xerr, "code")); + g_snprintf(msg, sizeof(msg), "Error %d: %s", errcode, errmsg ? errmsg : "Unknown error"); + } else + g_snprintf(msg, sizeof(msg), "%s", errmsg); + hide_login_progress(GJ_GC(gjc), msg); + } else { + hide_login_progress(GJ_GC(gjc), _("Unknown login error")); + } + + jd->die = TRUE; + } +} + +static void jabber_handleversion(gjconn gjc, xmlnode iqnode) { + xmlnode querynode, x; + char *id, *from; + char os[1024]; +#ifndef _WIN32 + struct utsname osinfo; + + uname(&osinfo); + g_snprintf(os, sizeof os, "%s %s %s", osinfo.sysname, osinfo.release, osinfo.machine); +#else + g_snprintf(os, sizeof os, "Windows %d %d", _winmajor, _winminor); +#endif + + + id = xmlnode_get_attrib(iqnode, "id"); + from = xmlnode_get_attrib(iqnode, "from"); + + x = jutil_iqnew(JPACKET__RESULT, NS_VERSION); + + xmlnode_put_attrib(x, "to", from); + xmlnode_put_attrib(x, "id", id); + querynode = xmlnode_get_tag(x, "query"); + xmlnode_insert_cdata(xmlnode_insert_tag(querynode, "name"), PACKAGE, -1); + xmlnode_insert_cdata(xmlnode_insert_tag(querynode, "version"), BITLBEE_VERSION, -1); + xmlnode_insert_cdata(xmlnode_insert_tag(querynode, "os"), os, -1); + + gjab_send(gjc, x); + + xmlnode_free(x); +} + +static void jabber_handletime(gjconn gjc, xmlnode iqnode) { + xmlnode querynode, x; + char *id, *from; + time_t now_t; + struct tm *now; + char buf[1024]; + + time(&now_t); + now = localtime(&now_t); + + id = xmlnode_get_attrib(iqnode, "id"); + from = xmlnode_get_attrib(iqnode, "from"); + + x = jutil_iqnew(JPACKET__RESULT, NS_TIME); + + xmlnode_put_attrib(x, "to", from); + xmlnode_put_attrib(x, "id", id); + querynode = xmlnode_get_tag(x, "query"); + + strftime(buf, 1024, "%Y%m%dT%T", now); + xmlnode_insert_cdata(xmlnode_insert_tag(querynode, "utc"), buf, -1); + strftime(buf, 1024, "%Z", now); + xmlnode_insert_cdata(xmlnode_insert_tag(querynode, "tz"), buf, -1); + strftime(buf, 1024, "%d %b %Y %T", now); + xmlnode_insert_cdata(xmlnode_insert_tag(querynode, "display"), buf, -1); + + gjab_send(gjc, x); + + xmlnode_free(x); +} + +static void jabber_handlelast(gjconn gjc, xmlnode iqnode) { + xmlnode x, querytag; + char *id, *from; + struct jabber_data *jd = GJ_GC(gjc)->proto_data; + char idle_time[32]; + + id = xmlnode_get_attrib(iqnode, "id"); + from = xmlnode_get_attrib(iqnode, "from"); + + x = jutil_iqnew(JPACKET__RESULT, "jabber:iq:last"); + + xmlnode_put_attrib(x, "to", from); + xmlnode_put_attrib(x, "id", id); + querytag = xmlnode_get_tag(x, "query"); + g_snprintf(idle_time, sizeof idle_time, "%ld", jd->idle ? time(NULL) - jd->idle : 0); + xmlnode_put_attrib(querytag, "seconds", idle_time); + + gjab_send(gjc, x); + xmlnode_free(x); +} + +/* + * delete == TRUE: delete found entry + * + * returns pointer to (local) copy of value if found, NULL otherwise + * + * Note: non-reentrant! Local static storage re-used on subsequent calls. + * If you're going to need to keep the returned value, make a copy! + */ +static gchar *jabber_track_queries(GHashTable *queries, gchar *key, gboolean delete) +{ + gpointer my_key, my_val; + static gchar *ret_val = NULL; + + if(ret_val != NULL) { + g_free(ret_val); + ret_val = NULL; + } + + /* self-protection */ + if(queries != NULL && key != NULL) { + if(g_hash_table_lookup_extended(queries, key, &my_key, &my_val)) { + ret_val = g_strdup((gchar *) my_val); + if(delete) { + g_hash_table_remove(queries, key); + g_free(my_key); + g_free(my_val); + } + } + } + + return(ret_val); +} + +static void jabber_handlepacket(gjconn gjc, jpacket p) +{ + char *id; + switch (p->type) { + case JPACKET_MESSAGE: + jabber_handlemessage(gjc, p); + break; + case JPACKET_PRESENCE: + jabber_handlepresence(gjc, p); + break; + case JPACKET_IQ: + id = xmlnode_get_attrib(p->x, "id"); + if (id != NULL && !strcmp(id, IQID_AUTH)) { + jabber_handleauthresp(gjc, p); + break; + } + + if (jpacket_subtype(p) == JPACKET__SET) { + xmlnode querynode; + querynode = xmlnode_get_tag(p->x, "query"); + if (NSCHECK(querynode, "jabber:iq:roster")) { + jabber_handlebuddy(gjc, xmlnode_get_firstchild(querynode)); + } + } else if (jpacket_subtype(p) == JPACKET__GET) { + xmlnode querynode; + querynode = xmlnode_get_tag(p->x, "query"); + if (NSCHECK(querynode, NS_VERSION)) { + jabber_handleversion(gjc, p->x); + } else if (NSCHECK(querynode, NS_TIME)) { + jabber_handletime(gjc, p->x); + } else if (NSCHECK(querynode, "jabber:iq:last")) { + jabber_handlelast(gjc, p->x); + } + } else if (jpacket_subtype(p) == JPACKET__RESULT) { + xmlnode querynode, vcard; + /* char *xmlns; */ + char *from; + + /* + * TBD: ISTM maybe this part could use a serious re-work? + */ + from = xmlnode_get_attrib(p->x, "from"); + querynode = xmlnode_get_tag(p->x, "query"); + vcard = xmlnode_get_tag(p->x, "vCard"); + if (!vcard) + vcard = xmlnode_get_tag(p->x, "VCARD"); + + if (NSCHECK(querynode, NS_ROSTER)) { + jabber_handleroster(gjc, querynode); + } else if (NSCHECK(querynode, NS_VCARD)) { + jabber_track_queries(gjc->queries, id, TRUE); /* delete query track */ + jabber_handlevcard(gjc, querynode, from); + } else if (vcard) { + jabber_track_queries(gjc->queries, id, TRUE); /* delete query track */ + jabber_handlevcard(gjc, vcard, from); + } else { + char *val; + + /* handle "null" query results */ + if((val = jabber_track_queries(gjc->queries, id, TRUE)) != NULL) { + if (!g_strncasecmp(val, "vcard", 5)) { + jabber_handlevcard(gjc, NULL, from); + } + + /* No-op */ + } + } + + } else if (jpacket_subtype(p) == JPACKET__ERROR) { + xmlnode xerr; + char *from, *errmsg = NULL; + int errcode = 0; + + from = xmlnode_get_attrib(p->x, "from"); + xerr = xmlnode_get_tag(p->x, "error"); + if (xerr) { + errmsg = xmlnode_get_data(xerr); + if (xmlnode_get_attrib(xerr, "code")) + errcode = atoi(xmlnode_get_attrib(xerr, "code")); + } + + from = g_strdup_printf("Error %d (%s)", errcode, from); + do_error_dialog(GJ_GC(gjc), errmsg, from); + g_free(from); + + } + + break; + case JPACKET_S10N: + jabber_handles10n(gjc, p); + break; + } + + xmlnode_free(p->x); + + return; +} + +static void jabber_handlestate(gjconn gjc, int state) +{ + switch (state) { + case JCONN_STATE_OFF: + if(gjc->was_connected) { + hide_login_progress_error(GJ_GC(gjc), _("Connection lost")); + } else { + hide_login_progress(GJ_GC(gjc), _("Unable to connect")); + } + signoff(GJ_GC(gjc)); + break; + case JCONN_STATE_CONNECTED: + gjc->was_connected = 1; + set_login_progress(GJ_GC(gjc), 2, _("Connected")); + break; + case JCONN_STATE_ON: + set_login_progress(GJ_GC(gjc), 3, _("Requesting Authentication Method")); + gjab_reqauth(gjc); + break; + } + return; +} + +static void jabber_login(struct aim_user *user) +{ + struct gaim_connection *gc = new_gaim_conn(user); + struct jabber_data *jd = gc->proto_data = g_new0(struct jabber_data, 1); + char *loginname = create_valid_jid(user->username, DEFAULT_SERVER, "BitlBee"); + + jd->hash = g_hash_table_new(g_str_hash, g_str_equal); + jd->chats = NULL; /* we have no chats yet */ + + set_login_progress(gc, 1, _("Connecting")); + + if (!(jd->gjc = gjab_new(loginname, user->password, gc))) { + g_free(loginname); + hide_login_progress(gc, _("Unable to connect")); + signoff(gc); + return; + } + + g_free(loginname); + gjab_state_handler(jd->gjc, jabber_handlestate); + gjab_packet_handler(jd->gjc, jabber_handlepacket); + jd->gjc->queries = g_hash_table_new(g_str_hash, g_str_equal); + gjab_start(jd->gjc); +} + +static gboolean jabber_destroy_hash(gpointer key, gpointer val, gpointer data) { + g_free(key); + g_free(val); + return TRUE; +} + +static gboolean jabber_free(gpointer data) +{ + struct jabber_data *jd = data; + + if(jd->gjc != NULL) { + gjab_delete(jd->gjc); + g_free(jd->gjc->sid); + jd->gjc = NULL; + } + g_free(jd); + + return FALSE; +} + +static void jabber_close(struct gaim_connection *gc) +{ + struct jabber_data *jd = gc->proto_data; + + if(jd) { + GSList *jcs = jd->chats; + + /* Free-up the jabber_chat struct allocs and the list */ + while (jcs) { + g_free(jcs->data); + jcs = jcs->next; + } + g_slist_free(jd->chats); + + /* Free-up the away status memories and the list */ + if(jd->hash != NULL) { + g_hash_table_foreach_remove(jd->hash, jabber_destroy_hash, NULL); + g_hash_table_destroy(jd->hash); + jd->hash = NULL; + } + + /* Free-up the pending queries memories and the list */ + if(jd->gjc != NULL && jd->gjc->queries != NULL) { + g_hash_table_foreach_remove(jd->gjc->queries, jabber_destroy_hash, NULL); + g_hash_table_destroy(jd->gjc->queries); + jd->gjc->queries = NULL; + } + } + if (gc->inpa) + gaim_input_remove(gc->inpa); + + if(jd) { + g_timeout_add(50, jabber_free, jd); + if(jd->gjc != NULL) + xmlnode_free(jd->gjc->current); + } + gc->proto_data = NULL; +} + +static int jabber_send_im(struct gaim_connection *gc, char *who, char *message, int len, int flags) +{ + xmlnode x, y; + char *realwho; + gjconn gjc = ((struct jabber_data *)gc->proto_data)->gjc; + + if (!who || !message) + return 0; + + x = xmlnode_new_tag("message"); + /* Bare username and "username" not the server itself? */ + if (!strchr(who, '@') && strcmp(who, gjc->user->server) != 0) + realwho = g_strdup_printf("%s@%s", who, gjc->user->server); + else + realwho = g_strdup(who); + xmlnode_put_attrib(x, "to", realwho); + g_free(realwho); + + xmlnode_insert_tag(x, "bitlbee"); + xmlnode_put_attrib(x, "type", "chat"); + + if (message && strlen(message)) { + y = xmlnode_insert_tag(x, "body"); + xmlnode_insert_cdata(y, message, -1); + } + + gjab_send(((struct jabber_data *)gc->proto_data)->gjc, x); + xmlnode_free(x); + return 1; +} + +/* + * Add/update buddy's roster entry on server + */ +static void jabber_roster_update(struct gaim_connection *gc, char *name) +{ + xmlnode x, y; + char *realwho; + gjconn gjc; + struct buddy *buddy = NULL; + /* struct group *buddy_group = NULL; */ + + if(gc && gc->proto_data && ((struct jabber_data *)gc->proto_data)->gjc && name) { + gjc = ((struct jabber_data *)gc->proto_data)->gjc; + + if (!strchr(name, '@')) + realwho = g_strdup_printf("%s@%s", name, gjc->user->server); + else { + jid who = jid_new(gjc->p, name); + if (who->user == NULL) { + /* FIXME: transport */ + return; + } + realwho = g_strdup_printf("%s@%s", who->user, who->server); + } + + + x = jutil_iqnew(JPACKET__SET, NS_ROSTER); + y = xmlnode_insert_tag(xmlnode_get_tag(x, "query"), "item"); + xmlnode_put_attrib(y, "jid", realwho); + + + /* If we can find the buddy, there's an alias for him, it's not 0-length + * and it doesn't match his JID, add the "name" attribute. + */ + if((buddy = find_buddy(gc, realwho)) != NULL && + buddy->show != NULL && buddy->show[0] != '\0' && strcmp(realwho, buddy->show)) { + + xmlnode_put_attrib(y, "name", buddy->show); + } + + /* + * Find out what group the buddy's in and send that along + * with the roster item. + */ + /* ** Bitlbee disabled ** + if((buddy_group = NULL) != NULL) { + xmlnode z; + z = xmlnode_insert_tag(y, "group"); + xmlnode_insert_cdata(z, buddy_group->name, -1); + } + ** End - Bitlbee ** */ + + gjab_send(((struct jabber_data *)gc->proto_data)->gjc, x); + + xmlnode_free(x); + g_free(realwho); + } +} + +/* + * Change buddy's group on server roster + */ +static void jabber_group_change(struct gaim_connection *gc, char *name, char *old_group, char *new_group) +{ + if(strcmp(old_group, new_group)) { + jabber_roster_update(gc, name); + } +} + +static void jabber_add_buddy(struct gaim_connection *gc, char *name) +{ + xmlnode x; + char *realwho; + gjconn gjc = ((struct jabber_data *)gc->proto_data)->gjc; + + if (!((struct jabber_data *)gc->proto_data)->did_import) + return; + + if (!name) + return; + + if (!strcmp(gc->username, name)) + return; + + if (!strchr(name, '@')) + realwho = g_strdup_printf("%s@%s", name, gjc->user->server); + else { + jid who; + + if((who = jid_new(gjc->p, name)) == NULL) { + char *msg = g_strdup_printf("%s: \"%s\"", _("Invalid Jabber I.D."), name); + do_error_dialog(GJ_GC(gjc), msg, _("Jabber Error")); + g_free(msg); + jabber_remove_gaim_buddy(gc, name); + return; + } + if (who->user == NULL) { + /* FIXME: transport */ + return; + } + realwho = g_strdup_printf("%s@%s", who->user, who->server); + } + + x = xmlnode_new_tag("presence"); + xmlnode_put_attrib(x, "to", realwho); + xmlnode_put_attrib(x, "type", "subscribe"); + gjab_send(((struct jabber_data *)gc->proto_data)->gjc, x); + xmlnode_free(x); + + jabber_roster_update(gc, realwho); + + g_free(realwho); +} + +static void jabber_remove_buddy(struct gaim_connection *gc, char *name, char *group) +{ + xmlnode x; + char *realwho; + gjconn gjc = ((struct jabber_data *)gc->proto_data)->gjc; + + if (!name) + return; + + if (!strchr(name, '@')) + realwho = g_strdup_printf("%s@%s", name, gjc->user->server); + else + realwho = g_strdup(name); + + x = xmlnode_new_tag("presence"); + xmlnode_put_attrib(x, "to", realwho); + xmlnode_put_attrib(x, "type", "unsubscribe"); + gjab_send(((struct jabber_data *)gc->proto_data)->gjc, x); + g_free(realwho); + xmlnode_free(x); +} + +static void jabber_get_info(struct gaim_connection *gc, char *who) { + xmlnode x; + char *id; + char *realwho; + struct jabber_data *jd = gc->proto_data; + gjconn gjc = jd->gjc; + + x = jutil_iqnew(JPACKET__GET, NS_VCARD); + /* Bare username? */ + if (!strchr(who, '@')) { + realwho = g_strdup_printf("%s@%s", who, gjc->user->server); + } else { + realwho = g_strdup(who); + } + xmlnode_put_attrib(x, "to", realwho); + g_free(realwho); + + id = gjab_getid(gjc); + xmlnode_put_attrib(x, "id", id); + + g_hash_table_insert(jd->gjc->queries, g_strdup(id), g_strdup("vCard")); + + gjab_send(gjc, x); + + xmlnode_free(x); + +} + +static void jabber_get_away_msg(struct gaim_connection *gc, char *who) { + struct jabber_data *jd = gc->proto_data; + gjconn gjc = jd->gjc; + char *status; + + /* space for all elements: Jabber I.D. + "status" + NULL (list terminator) */ + gchar **str_arr = (gchar **) g_new(gpointer, 3); + gchar **ap = str_arr; + gchar *realwho, *final; + + /* Bare username? */ + if (!strchr(who, '@')) { + realwho = g_strdup_printf("%s@%s", who, gjc->user->server); + } else { + realwho = g_strdup(who); + } + *ap++ = g_strdup_printf("<B>Jabber ID:</B> %s<BR>\n", realwho); + + if((status = g_hash_table_lookup(jd->hash, realwho)) == NULL) { + status = _("Unknown"); + } + *ap++ = g_strdup_printf("<B>Status:</B> %s<BR>\n", status); + + *ap = NULL; + + final= g_strjoinv(NULL, str_arr); + g_strfreev(str_arr); + + g_free(realwho); + g_free(final); + +} + +static GList *jabber_away_states(struct gaim_connection *gc) { + GList *m = NULL; + + m = g_list_append(m, "Online"); + m = g_list_append(m, "Chatty"); + m = g_list_append(m, "Away"); + m = g_list_append(m, "Extended Away"); + m = g_list_append(m, "Do Not Disturb"); + + return m; +} + +static void jabber_set_away(struct gaim_connection *gc, char *state, char *message) +{ + xmlnode x, y; + struct jabber_data *jd = gc->proto_data; + gjconn gjc = jd->gjc; + + gc->away = NULL; /* never send an auto-response */ + + x = xmlnode_new_tag("presence"); + + if (!strcmp(state, GAIM_AWAY_CUSTOM)) { + /* oh goody. Gaim is telling us what to do. */ + if (message) { + /* Gaim wants us to be away */ + y = xmlnode_insert_tag(x, "show"); + xmlnode_insert_cdata(y, "away", -1); + y = xmlnode_insert_tag(x, "status"); + { + char *utf8 = str_to_utf8(message); + xmlnode_insert_cdata(y, utf8, -1); + g_free(utf8); + } + gc->away = ""; + } else { + /* Gaim wants us to not be away */ + /* but for Jabber, we can just send presence with no other information. */ + } + } else { + /* state is one of our own strings. it won't be NULL. */ + if (!g_strcasecmp(state, "Online")) { + /* once again, we don't have to put anything here */ + } else if (!g_strcasecmp(state, "Chatty")) { + y = xmlnode_insert_tag(x, "show"); + xmlnode_insert_cdata(y, "chat", -1); + } else if (!g_strcasecmp(state, "Away")) { + y = xmlnode_insert_tag(x, "show"); + xmlnode_insert_cdata(y, "away", -1); + gc->away = ""; + } else if (!g_strcasecmp(state, "Extended Away")) { + y = xmlnode_insert_tag(x, "show"); + xmlnode_insert_cdata(y, "xa", -1); + gc->away = ""; + } else if (!g_strcasecmp(state, "Do Not Disturb")) { + y = xmlnode_insert_tag(x, "show"); + xmlnode_insert_cdata(y, "dnd", -1); + gc->away = ""; + } + } + + gjab_send(gjc, x); + xmlnode_free(x); +} + +static void jabber_set_idle(struct gaim_connection *gc, int idle) { + struct jabber_data *jd = (struct jabber_data *)gc->proto_data; + jd->idle = idle ? time(NULL) - idle : idle; +} + +static void jabber_keepalive(struct gaim_connection *gc) { + struct jabber_data *jd = (struct jabber_data *)gc->proto_data; + gjab_send_raw(jd->gjc, " \t "); +} + +static void jabber_buddy_free(struct buddy *b) +{ + while (b->proto_data) { + g_free(((GSList *)b->proto_data)->data); + b->proto_data = g_slist_remove(b->proto_data, ((GSList *)b->proto_data)->data); + } +} + +/*---------------------------------------*/ +/* Jabber "set info" (vCard) support */ +/*---------------------------------------*/ + +/* + * V-Card format: + * + * <vCard prodid='' version='' xmlns=''> + * <FN></FN> + * <N> + * <FAMILY/> + * <GIVEN/> + * </N> + * <NICKNAME/> + * <URL/> + * <ADR> + * <STREET/> + * <EXTADD/> + * <LOCALITY/> + * <REGION/> + * <PCODE/> + * <COUNTRY/> + * </ADR> + * <TEL/> + * <EMAIL/> + * <ORG> + * <ORGNAME/> + * <ORGUNIT/> + * </ORG> + * <TITLE/> + * <ROLE/> + * <DESC/> + * <BDAY/> + * </vCard> + * + * See also: + * + * http://docs.jabber.org/proto/html/vcard-temp.html + * http://www.vcard-xml.org/dtd/vCard-XML-v2-20010520.dtd + */ + +/* + * Cross-reference user-friendly V-Card entry labels to vCard XML tags + * and attributes. + * + * Order is (or should be) unimportant. For example: we have no way of + * knowing in what order real data will arrive. + * + * Format: Label, Pre-set text, "visible" flag, "editable" flag, XML tag + * name, XML tag's parent tag "path" (relative to vCard node). + * + * List is terminated by a NULL label pointer. + * + * Entries with no label text, but with XML tag and parent tag + * entries, are used by V-Card XML construction routines to + * "automagically" construct the appropriate XML node tree. + * + * Thoughts on future direction/expansion + * + * This is a "simple" vCard. + * + * It is possible for nodes other than the "vCard" node to have + * attributes. Should that prove necessary/desirable, add an + * "attributes" pointer to the vcard_template struct, create the + * necessary tag_attr structs, and add 'em to the vcard_dflt_data + * array. + * + * The above changes will (obviously) require changes to the vCard + * construction routines. + */ + +static struct vcard_template { + char *label; /* label text pointer */ + char *text; /* entry text pointer */ + int visible; /* should entry field be "visible?" */ + int editable; /* should entry field be editable? */ + char *tag; /* tag text */ + char *ptag; /* parent tag "path" text */ + char *url; /* vCard display format if URL */ +} vcard_template_data[] = { + {N_("Full Name"), NULL, TRUE, TRUE, "FN", NULL, NULL}, + {N_("Family Name"), NULL, TRUE, TRUE, "FAMILY", "N", NULL}, + {N_("Given Name"), NULL, TRUE, TRUE, "GIVEN", "N", NULL}, + {N_("Nickname"), NULL, TRUE, TRUE, "NICKNAME", NULL, NULL}, + {N_("URL"), NULL, TRUE, TRUE, "URL", NULL, "<A HREF=\"%s\">%s</A>"}, + {N_("Street Address"), NULL, TRUE, TRUE, "STREET", "ADR", NULL}, + {N_("Extended Address"), NULL, TRUE, TRUE, "EXTADD", "ADR", NULL}, + {N_("Locality"), NULL, TRUE, TRUE, "LOCALITY", "ADR", NULL}, + {N_("Region"), NULL, TRUE, TRUE, "REGION", "ADR", NULL}, + {N_("Postal Code"), NULL, TRUE, TRUE, "PCODE", "ADR", NULL}, + {N_("Country"), NULL, TRUE, TRUE, "COUNTRY", "ADR", NULL}, + {N_("Telephone"), NULL, TRUE, TRUE, "TELEPHONE", NULL, NULL}, + {N_("Email"), NULL, TRUE, TRUE, "EMAIL", NULL, "<A HREF=\"mailto:%s\">%s</A>"}, + {N_("Organization Name"), NULL, TRUE, TRUE, "ORGNAME", "ORG", NULL}, + {N_("Organization Unit"), NULL, TRUE, TRUE, "ORGUNIT", "ORG", NULL}, + {N_("Title"), NULL, TRUE, TRUE, "TITLE", NULL, NULL}, + {N_("Role"), NULL, TRUE, TRUE, "ROLE", NULL, NULL}, + {N_("Birthday"), NULL, TRUE, TRUE, "BDAY", NULL, NULL}, + {N_("Description"), NULL, TRUE, TRUE, "DESC", NULL, NULL}, + {"", NULL, TRUE, TRUE, "N", NULL, NULL}, + {"", NULL, TRUE, TRUE, "ADR", NULL, NULL}, + {"", NULL, TRUE, TRUE, "ORG", NULL, NULL}, + {NULL, NULL, 0, 0, NULL, NULL, NULL} +}; + +/* + * Used by routines to parse an XML-encoded string into an xmlnode tree + */ +typedef struct { + XML_Parser parser; + xmlnode current; +} *xmlstr2xmlnode_parser, xmlstr2xmlnode_parser_struct; + + +/* + * Used by XML_Parse on parsing CDATA + */ +static void xmlstr2xmlnode_charData(void *userdata, const char *s, int slen) +{ + xmlstr2xmlnode_parser xmlp = (xmlstr2xmlnode_parser) userdata; + + if (xmlp->current) + xmlnode_insert_cdata(xmlp->current, s, slen); +} + +/* + * Used by XML_Parse to start or append to an xmlnode + */ +static void xmlstr2xmlnode_startElement(void *userdata, const char *name, const char **attribs) +{ + xmlnode x; + xmlstr2xmlnode_parser xmlp = (xmlstr2xmlnode_parser) userdata; + + if (xmlp->current) { + /* Append the node to the current one */ + x = xmlnode_insert_tag(xmlp->current, name); + xmlnode_put_expat_attribs(x, attribs); + + xmlp->current = x; + } else { + x = xmlnode_new_tag(name); + xmlnode_put_expat_attribs(x, attribs); + xmlp->current = x; + } +} + +/* + * Used by XML_Parse to end an xmlnode + */ +static void xmlstr2xmlnode_endElement(void *userdata, const char *name) +{ + xmlstr2xmlnode_parser xmlp = (xmlstr2xmlnode_parser) userdata; + xmlnode x; + + if (xmlp->current != NULL && (x = xmlnode_get_parent(xmlp->current)) != NULL) { + xmlp->current = x; + } +} + +/* + * Parse an XML-encoded string into an xmlnode tree + * + * Caller is responsible for freeing the returned xmlnode + */ +static xmlnode xmlstr2xmlnode(char *xmlstring) +{ + xmlstr2xmlnode_parser my_parser = g_new(xmlstr2xmlnode_parser_struct, 1); + xmlnode x = NULL; + + my_parser->parser = XML_ParserCreate(NULL); + my_parser->current = NULL; + + XML_SetUserData(my_parser->parser, (void *)my_parser); + XML_SetElementHandler(my_parser->parser, xmlstr2xmlnode_startElement, xmlstr2xmlnode_endElement); + XML_SetCharacterDataHandler(my_parser->parser, xmlstr2xmlnode_charData); + XML_Parse(my_parser->parser, xmlstring, strlen(xmlstring), 0); + + x = my_parser->current; + + XML_ParserFree(my_parser->parser); + g_free(my_parser); + + return(x); +} + +/* + * Insert a tag node into an xmlnode tree, recursively inserting parent tag + * nodes as necessary + * + * Returns pointer to inserted node + * + * Note to hackers: this code is designed to be re-entrant (it's recursive--it + * calls itself), so don't put any "static"s in here! + */ +static xmlnode insert_tag_to_parent_tag(xmlnode start, const char *parent_tag, const char *new_tag) +{ + xmlnode x = NULL; + + /* + * If the parent tag wasn't specified, see if we can get it + * from the vCard template struct. + */ + if(parent_tag == NULL) { + struct vcard_template *vc_tp = vcard_template_data; + + while(vc_tp->label != NULL) { + if(strcmp(vc_tp->tag, new_tag) == 0) { + parent_tag = vc_tp->ptag; + break; + } + ++vc_tp; + } + } + + /* + * If we have a parent tag... + */ + if(parent_tag != NULL ) { + /* + * Try to get the parent node for a tag + */ + if((x = xmlnode_get_tag(start, parent_tag)) == NULL) { + /* + * Descend? + */ + char *grand_parent = strcpy(g_malloc(strlen(parent_tag) + 1), parent_tag); + char *parent; + + if((parent = strrchr(grand_parent, '/')) != NULL) { + *(parent++) = '\0'; + x = insert_tag_to_parent_tag(start, grand_parent, parent); + } else { + x = xmlnode_insert_tag(start, grand_parent); + } + g_free(grand_parent); + } else { + /* + * We found *something* to be the parent node. + * Note: may be the "root" node! + */ + xmlnode y; + if((y = xmlnode_get_tag(x, new_tag)) != NULL) { + return(y); + } + } + } + + /* + * insert the new tag into its parent node + */ + return(xmlnode_insert_tag((x == NULL? start : x), new_tag)); +} + +/* + * Send vCard info to Jabber server + */ +static void jabber_set_info(struct gaim_connection *gc, char *info) +{ + xmlnode x, vc_node; + char *id; + struct jabber_data *jd = gc->proto_data; + gjconn gjc = jd->gjc; + + x = xmlnode_new_tag("iq"); + xmlnode_put_attrib(x,"type","set"); + + id = gjab_getid(gjc); + + xmlnode_put_attrib(x, "id", id); + + /* + * Send only if there's actually any *information* to send + */ + if((vc_node = xmlstr2xmlnode(info)) != NULL && xmlnode_get_name(vc_node) != NULL && + g_strncasecmp(xmlnode_get_name(vc_node), "vcard", 5) == 0) { + xmlnode_insert_tag_node(x, vc_node); + gjab_send(gjc, x); + } + + xmlnode_free(x); +} + +/* + * displays a Jabber vCard + */ +static void jabber_handlevcard(gjconn gjc, xmlnode querynode, char *from) +{ + struct jabber_data *jd = GJ_GC(gjc)->proto_data; + jid who = jid_new(gjc->p, from); + char *status = NULL, *text = NULL; + GString *str = g_string_sized_new(100); + xmlnode child; + + gchar *buddy = NULL; + + if(querynode == NULL) { + serv_got_crap(GJ_GC(gjc), "%s - Received empty info reply from %s", _("User Info"), from); + return; + } + + if(who->resource != NULL && (who->resource)[0] != '\0') { + buddy = g_strdup_printf("%s@%s/%s", who->user, who->server, who->resource); + } else { + buddy = g_strdup_printf("%s@%s", who->user, who->server); + } + + if((status = g_hash_table_lookup(jd->hash, buddy)) == NULL) { + status = _("Unknown"); + } + + g_string_sprintfa(str, "%s: %s - %s: %s", _("Jabber ID"), buddy, _("Status"), + status); + + for(child = querynode->firstchild; child; child = child->next) + { + xmlnode child2; + + if(child->type != NTYPE_TAG) + continue; + + text = xmlnode_get_data(child); + if(text && !strcmp(child->name, "FN")) { + info_string_append(str, "\n", _("Full Name"), text); + } else if (!strcmp(child->name, "N")) { + for (child2 = child->firstchild; child2; child2 = child2->next) { + char *text2 = NULL; + + if (child2->type != NTYPE_TAG) + continue; + + text2 = xmlnode_get_data(child2); + if (text2 && !strcmp(child2->name, "FAMILY")) { + info_string_append(str, "\n", _("Family Name"), text2); + } else if (text2 && !strcmp(child2->name, "GIVEN")) { + info_string_append(str, "\n", _("Given Name"), text2); + } else if (text2 && !strcmp(child2->name, "MIDDLE")) { + info_string_append(str, "\n", _("Middle Name"), text2); + } + } + } else if (text && !strcmp(child->name, "NICKNAME")) { + info_string_append(str, "\n", _("Nickname"), text); + } else if (text && !strcmp(child->name, "BDAY")) { + info_string_append(str, "\n", _("Birthday"), text); + } else if (!strcmp(child->name, "ADR")) { + /* show wich address it is */ + /* Just for the beauty of bitlbee + if (child->firstchild) + g_string_sprintfa(str, "%s:\n", _("Address")); + */ + for(child2 = child->firstchild; child2; child2 = child2->next) { + char *text2 = NULL; + + if(child2->type != NTYPE_TAG) + continue; + + text2 = xmlnode_get_data(child2); + if(text2 && !strcmp(child2->name, "POBOX")) { + info_string_append(str, "\n", + _("P.O. Box"), text2); + } else if(text2 && !strcmp(child2->name, "EXTADR")) { + info_string_append(str, "\n", + _("Extended Address"), text2); + } else if(text2 && !strcmp(child2->name, "STREET")) { + info_string_append(str, "\n", + _("Street Address"), text2); + } else if(text2 && !strcmp(child2->name, "LOCALITY")) { + info_string_append(str, "\n", + _("Locality"), text2); + } else if(text2 && !strcmp(child2->name, "REGION")) { + info_string_append(str, "\n", + _("Region"), text2); + } else if(text2 && !strcmp(child2->name, "PCODE")) { + info_string_append(str, "\n", + _("Postal Code"), text2); + } else if(text2 && (!strcmp(child2->name, "CTRY") + || !strcmp(child2->name, "COUNTRY"))) { + info_string_append(str, "\n", _("Country"), text2); + } + } + } else if(!strcmp(child->name, "TEL")) { + char *number = NULL; + if ((child2 = xmlnode_get_tag(child, "NUMBER"))) { + /* show what kind of number it is */ + number = xmlnode_get_data(child2); + if(number) { + info_string_append(str, "\n", _("Telephone"), number); + } + } else if((number = xmlnode_get_data(child))) { + /* lots of clients (including gaim) do this, + * but it's out of spec */ + info_string_append(str, "\n", _("Telephone"), number); + } + } else if(!strcmp(child->name, "EMAIL")) { + char *userid = NULL; + if((child2 = xmlnode_get_tag(child, "USERID"))) { + /* show what kind of email it is */ + userid = xmlnode_get_data(child2); + if(userid) { + info_string_append(str, "\n", _("Email"), userid); + } + } else if((userid = xmlnode_get_data(child))) { + /* lots of clients (including gaim) do this, + * but it's out of spec */ + info_string_append(str, "\n", _("Email"), userid); + } + } else if(!strcmp(child->name, "ORG")) { + for(child2 = child->firstchild; child2; child2 = child2->next) { + char *text2 = NULL; + + if(child2->type != NTYPE_TAG) + continue; + + text2 = xmlnode_get_data(child2); + if(text2 && !strcmp(child2->name, "ORGNAME")) { + info_string_append(str, "\n", _("Organization Name"), text2); + } else if(text2 && !strcmp(child2->name, "ORGUNIT")) { + info_string_append(str, "\n", _("Organization Unit"), text2); + } + } + } else if(text && !strcmp(child->name, "TITLE")) { + info_string_append(str, "\n", _("Title"), text); + } else if(text && !strcmp(child->name, "ROLE")) { + info_string_append(str, "\n", _("Role"), text); + } else if(text && !strcmp(child->name, "DESC")) { + g_string_sprintfa(str, "\n%s:\n%s\n%s", _("Description"), + text, _("End of Description")); + } + } + + serv_got_crap(GJ_GC(gjc), "%s\n%s", _("User Info"), str->str); + + g_free(buddy); + g_string_free(str, TRUE); +} + + +static GList *jabber_actions() +{ + GList *m = NULL; + + m = g_list_append(m, _("Set User Info")); + /* + m = g_list_append(m, _("Set Dir Info")); + m = g_list_append(m, _("Change Password")); + */ + + return m; +} + +static struct prpl *my_protocol = NULL; + +void jabber_init(struct prpl *ret) +{ + /* the NULL's aren't required but they're nice to have */ + ret->protocol = PROTO_JABBER; + ret->name = jabber_name; + ret->away_states = jabber_away_states; + ret->actions = jabber_actions; + ret->login = jabber_login; + ret->close = jabber_close; + ret->send_im = jabber_send_im; + ret->set_info = jabber_set_info; + ret->get_info = jabber_get_info; + ret->set_away = jabber_set_away; + ret->get_away = jabber_get_away_msg; + ret->set_idle = jabber_set_idle; + ret->add_buddy = jabber_add_buddy; + ret->remove_buddy = jabber_remove_buddy; + ret->add_permit = NULL; + ret->add_deny = NULL; + ret->rem_permit = NULL; + ret->rem_deny = NULL; + ret->set_permit_deny = NULL; + ret->keepalive = jabber_keepalive; + ret->buddy_free = jabber_buddy_free; + ret->alias_buddy = jabber_roster_update; + ret->group_buddy = jabber_group_change; + + my_protocol = ret; +} diff --git a/protocols/jabber/jabber.h b/protocols/jabber/jabber.h new file mode 100644 index 00000000..0b907500 --- /dev/null +++ b/protocols/jabber/jabber.h @@ -0,0 +1,315 @@ +/* + * This program is free software; you can 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. + * + * Jabber + * Copyright (C) 1998-1999 The Jabber Team http://jabber.org/ + */ + +#include <string.h> +#include <stdlib.h> +#include <sys/types.h> +#include <stdio.h> +#include <setjmp.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <signal.h> +#include <stdarg.h> +#include <time.h> +#include <ctype.h> +#ifdef _WIN32 +#undef DATADIR +#include "sock.h" +#endif + +#include "lib.h" + + +#ifndef INCL_JABBER_H +#define INCL_JABBER_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* --------------------------------------------------------- */ +/* */ +/* JID structures & constants */ +/* */ +/* --------------------------------------------------------- */ +#define JID_RESOURCE 1 +#define JID_USER 2 +#define JID_SERVER 4 + +typedef struct jid_struct +{ + pool p; + char* resource; + char* user; + char* server; + char* full; + struct jid_struct *next; /* for lists of jids */ +} *jid; + +jid jid_new(pool p, char *idstr); /* Creates a jabber id from the idstr */ +void jid_set(jid id, char *str, int item); /* Individually sets jid components */ +char* jid_full(jid id); /* Builds a string type=user/resource@server from the jid data */ +int jid_cmp(jid a, jid b); /* Compares two jid's, returns 0 for perfect match */ +int jid_cmpx(jid a, jid b, int parts); /* Compares just the parts specified as JID_|JID_ */ +jid jid_append(jid a, jid b); /* Appending b to a (list), no dups */ +xmlnode jid_xres(jid id); /* Returns xmlnode representation of the resource?query=string */ +xmlnode jid_nodescan(jid id, xmlnode x); /* Scans the children of the node for a matching jid attribute */ +jid jid_user(jid a); /* returns the same jid but just of the user@host part */ + + +/* --------------------------------------------------------- */ +/* */ +/* JPacket structures & constants */ +/* */ +/* --------------------------------------------------------- */ +#define JPACKET_UNKNOWN 0x00 +#define JPACKET_MESSAGE 0x01 +#define JPACKET_PRESENCE 0x02 +#define JPACKET_IQ 0x04 +#define JPACKET_S10N 0x08 + +#define JPACKET__UNKNOWN 0 +#define JPACKET__NONE 1 +#define JPACKET__ERROR 2 +#define JPACKET__CHAT 3 +#define JPACKET__GROUPCHAT 4 +#define JPACKET__GET 5 +#define JPACKET__SET 6 +#define JPACKET__RESULT 7 +#define JPACKET__SUBSCRIBE 8 +#define JPACKET__SUBSCRIBED 9 +#define JPACKET__UNSUBSCRIBE 10 +#define JPACKET__UNSUBSCRIBED 11 +#define JPACKET__AVAILABLE 12 +#define JPACKET__UNAVAILABLE 13 +#define JPACKET__PROBE 14 +#define JPACKET__HEADLINE 15 +#define JPACKET__INVISIBLE 16 + +typedef struct jpacket_struct +{ + unsigned char type; + int subtype; + int flag; + void* aux1; + xmlnode x; + jid to; + jid from; + char* iqns; + xmlnode iq; + pool p; +} *jpacket, _jpacket; + +jpacket jpacket_new(xmlnode x); /* Creates a jabber packet from the xmlnode */ +int jpacket_subtype(jpacket p); /* Returns the subtype value (looks at xmlnode for it) */ + + +/* --------------------------------------------------------- */ +/* */ +/* Presence Proxy DB structures & constants */ +/* */ +/* --------------------------------------------------------- */ +typedef struct ppdb_struct +{ + jid id; /* entry data */ + int pri; + xmlnode x; + struct ppdb_struct* user; /* linked list for user@server */ + pool p; /* db-level data */ + struct ppdb_struct* next; +} _ppdb, *ppdb; + +ppdb ppdb_insert(ppdb db, jid id, xmlnode x); /* Inserts presence into the proxy */ +xmlnode ppdb_primary(ppdb db, jid id); /* Fetches the matching primary presence for the id */ +void ppdb_free(ppdb db); /* Frees the db and all entries */ +xmlnode ppdb_get(ppdb db, jid id); /* Called successively to return each presence xmlnode */ + /* for the id and children, returns NULL at the end */ + + +/* --------------------------------------------------------- */ +/* */ +/* Simple Jabber Rate limit functions */ +/* */ +/* --------------------------------------------------------- */ +typedef struct jlimit_struct +{ + char *key; + int start; + int points; + int maxt, maxp; + pool p; +} *jlimit, _jlimit; + +jlimit jlimit_new(int maxt, int maxp); +void jlimit_free(jlimit r); +int jlimit_check(jlimit r, char *key, int points); + + +/* --------------------------------------------------------- */ +/* */ +/* Error structures & constants */ +/* */ +/* --------------------------------------------------------- */ +typedef struct terror_struct +{ + int code; + char msg[64]; +} terror; + +#define TERROR_BAD (terror){400,"Bad Request"} +#define TERROR_AUTH (terror){401,"Unauthorized"} +#define TERROR_PAY (terror){402,"Payment Required"} +#define TERROR_FORBIDDEN (terror){403,"Forbidden"} +#define TERROR_NOTFOUND (terror){404,"Not Found"} +#define TERROR_NOTALLOWED (terror){405,"Not Allowed"} +#define TERROR_NOTACCEPTABLE (terror){406,"Not Acceptable"} +#define TERROR_REGISTER (terror){407,"Registration Required"} +#define TERROR_REQTIMEOUT (terror){408,"Request Timeout"} +#define TERROR_CONFLICT (terror){409,"Conflict"} + +#define TERROR_INTERNAL (terror){500,"Internal Server Error"} +#define TERROR_NOTIMPL (terror){501,"Not Implemented"} +#define TERROR_EXTERNAL (terror){502,"Remote Server Error"} +#define TERROR_UNAVAIL (terror){503,"Service Unavailable"} +#define TERROR_EXTTIMEOUT (terror){504,"Remote Server Timeout"} +#define TERROR_DISCONNECTED (terror){510,"Disconnected"} + +/* --------------------------------------------------------- */ +/* */ +/* Namespace constants */ +/* */ +/* --------------------------------------------------------- */ +#define NSCHECK(x,n) (j_strcmp(xmlnode_get_attrib(x,"xmlns"),n) == 0) + +#define NS_CLIENT "jabber:client" +#define NS_SERVER "jabber:server" +#define NS_AUTH "jabber:iq:auth" +#define NS_REGISTER "jabber:iq:register" +#define NS_ROSTER "jabber:iq:roster" +#define NS_OFFLINE "jabber:x:offline" +#define NS_AGENT "jabber:iq:agent" +#define NS_AGENTS "jabber:iq:agents" +#define NS_DELAY "jabber:x:delay" +#define NS_VERSION "jabber:iq:version" +#define NS_TIME "jabber:iq:time" +#define NS_VCARD "vcard-temp" +#define NS_PRIVATE "jabber:iq:private" +#define NS_SEARCH "jabber:iq:search" +#define NS_OOB "jabber:iq:oob" +#define NS_XOOB "jabber:x:oob" +#define NS_ADMIN "jabber:iq:admin" +#define NS_FILTER "jabber:iq:filter" +#define NS_AUTH_0K "jabber:iq:auth:0k" + + +/* --------------------------------------------------------- */ +/* */ +/* Message Types */ +/* */ +/* --------------------------------------------------------- */ +#define TMSG_NORMAL "normal" +#define TMSG_ERROR "error" +#define TMSG_CHAT "chat" +#define TMSG_GROUPCHAT "groupchat" +#define TMSG_HEADLINE "headline" + + +/* --------------------------------------------------------- */ +/* */ +/* JUtil functions */ +/* */ +/* --------------------------------------------------------- */ +xmlnode jutil_presnew(int type, char *to, char *status); /* Create a skeleton presence packet */ +xmlnode jutil_iqnew(int type, char *ns); /* Create a skeleton iq packet */ +xmlnode jutil_msgnew(char *type, char *to, char *subj, char *body); + /* Create a skeleton message packet */ +xmlnode jutil_header(char* xmlns, char* server); /* Create a skeleton stream packet */ +int jutil_priority(xmlnode x); /* Determine priority of this packet */ +void jutil_tofrom(xmlnode x); /* Swaps to/from fields on a packet */ +xmlnode jutil_iqresult(xmlnode x); /* Generate a skeleton iq/result, given a iq/query */ +char* jutil_timestamp(void); /* Get stringified timestamp */ +void jutil_error(xmlnode x, terror E); /* Append an <error> node to x */ +void jutil_delay(xmlnode msg, char *reason); /* Append a delay packet to msg */ +char* jutil_regkey(char *key, char *seed); /* pass a seed to generate a key, pass the key again to validate (returns it) */ + + +/* --------------------------------------------------------- */ +/* */ +/* JConn structures & functions */ +/* */ +/* --------------------------------------------------------- */ +#define JCONN_STATE_OFF 0 +#define JCONN_STATE_CONNECTED 1 +#define JCONN_STATE_ON 2 +#define JCONN_STATE_AUTH 3 + +typedef struct jconn_struct +{ + /* Core structure */ + pool p; /* Memory allocation pool */ + int state; /* Connection state flag */ + int fd; /* Connection file descriptor */ + jid user; /* User info */ + char *pass; /* User passwd */ + + /* Stream stuff */ + int id; /* id counter for jab_getid() function */ + char idbuf[9]; /* temporary storage for jab_getid() */ + char *sid; /* stream id from server, for digest auth */ + XML_Parser parser; /* Parser instance */ + xmlnode current; /* Current node in parsing instance.. */ + + /* Event callback ptrs */ + void (*on_state)(struct jconn_struct *j, int state); + void (*on_packet)(struct jconn_struct *j, jpacket p); + +} *jconn, jconn_struct; + +typedef void (*jconn_state_h)(jconn j, int state); +typedef void (*jconn_packet_h)(jconn j, jpacket p); + + +jconn jab_new(char *user, char *pass); +void jab_delete(jconn j); +void jab_state_handler(jconn j, jconn_state_h h); +void jab_packet_handler(jconn j, jconn_packet_h h); +void jab_start(jconn j); +void jab_stop(jconn j); + +int jab_getfd(jconn j); +jid jab_getjid(jconn j); +char *jab_getsid(jconn j); +char *jab_getid(jconn j); + +void jab_send(jconn j, xmlnode x); +void jab_send_raw(jconn j, const char *str); +void jab_recv(jconn j); +void jab_poll(jconn j, int timeout); + +char *jab_auth(jconn j); +char *jab_reg(jconn j); + + + +#ifdef __cplusplus +} +#endif + +#endif /* INCL_JABBER_H */ diff --git a/protocols/jabber/jconn.c b/protocols/jabber/jconn.c new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/protocols/jabber/jconn.c diff --git a/protocols/jabber/jid.c b/protocols/jabber/jid.c new file mode 100644 index 00000000..ed2b9ba1 --- /dev/null +++ b/protocols/jabber/jid.c @@ -0,0 +1,170 @@ +/* -------------------------------------------------------------------------- + * + * License + * + * The contents of this file are subject to the Jabber Open Source License + * Version 1.0 (the "JOSL"). You may not copy or use this file, in either + * source code or executable form, except in compliance with the JOSL. You + * may obtain a copy of the JOSL at http://www.jabber.org/ or at + * http://www.opensource.org/. + * + * Software distributed under the JOSL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the JOSL + * for the specific language governing rights and limitations under the + * JOSL. + * + * Copyrights + * + * Portions created by or assigned to Jabber.com, Inc. are + * Copyright (c) 1999-2002 Jabber.com, Inc. All Rights Reserved. Contact + * information for Jabber.com, Inc. is available at http://www.jabber.com/. + * + * Portions Copyright (c) 1998-1999 Jeremie Miller. + * + * Acknowledgements + * + * Special thanks to the Jabber Open Source Contributors for their + * suggestions and support of Jabber. + * + * Alternatively, the contents of this file may be used under the terms of the + * GNU General Public License Version 2 or later (the "GPL"), in which case + * the provisions of the GPL are applicable instead of those above. If you + * wish to allow use of your version of this file only under the terms of the + * GPL and not to allow others to use your version of this file under the JOSL, + * indicate your decision by deleting the provisions above and replace them + * with the notice and other provisions required by the GPL. If you do not + * delete the provisions above, a recipient may use your version of this file + * under either the JOSL or the GPL. + * + * + * --------------------------------------------------------------------------*/ + +#include "jabber.h" +#include <glib.h> + +static jid jid_safe(jid id) +{ + char *str; + + if(strlen(id->server) == 0 || strlen(id->server) > 255) + return NULL; + + /* lowercase the hostname, make sure it's valid characters */ + for(str = id->server; *str != '\0'; str++) + { + *str = tolower(*str); + if(!(isalnum(*str) || *str == '.' || *str == '-' || *str == '_')) return NULL; + } + + /* cut off the user */ + if(id->user != NULL && strlen(id->user) > 64) + id->user[64] = '\0'; + + /* check for low and invalid ascii characters in the username */ + if(id->user != NULL) + for(str = id->user; *str != '\0'; str++) + if(*str <= 32 || *str == ':' || *str == '@' || *str == '<' || *str == '>' || *str == '\'' || *str == '"' || *str == '&') return NULL; + + return id; +} + +jid jid_new(pool p, char *idstr) +{ + char *server, *resource, *type, *str; + jid id; + + if(p == NULL || idstr == NULL || strlen(idstr) == 0) + return NULL; + + /* user@server/resource */ + + str = pstrdup(p, idstr); + + id = pmalloco(p,sizeof(struct jid_struct)); + id->p = p; + + resource = strstr(str,"/"); + if(resource != NULL) + { + *resource = '\0'; + ++resource; + if(strlen(resource) > 0) + id->resource = resource; + }else{ + resource = str + strlen(str); /* point to end */ + } + + type = strstr(str,":"); + if(type != NULL && type < resource) + { + *type = '\0'; + ++type; + str = type; /* ignore the type: prefix */ + } + + server = strstr(str,"@"); + if(server == NULL || server > resource) + { /* if there's no @, it's just the server address */ + id->server = str; + }else{ + *server = '\0'; + ++server; + id->server = server; + if(strlen(str) > 0) + id->user = str; + } + + return jid_safe(id); +} + +char *jid_full(jid id) +{ + spool s; + + if(id == NULL) + return NULL; + + /* use cached copy */ + if(id->full != NULL) + return id->full; + + s = spool_new(id->p); + + if(id->user != NULL) + spooler(s, id->user,"@",s); + + spool_add(s, id->server); + + if(id->resource != NULL) + spooler(s, "/",id->resource,s); + + id->full = spool_print(s); + return id->full; +} + +/* local utils */ +static int _jid_nullstrcmp(char *a, char *b) +{ + if(a == NULL && b == NULL) return 0; + if(a == NULL || b == NULL) return -1; + return strcmp(a,b); +} +static int _jid_nullstrcasecmp(char *a, char *b) +{ + if(a == NULL && b == NULL) return 0; + if(a == NULL || b == NULL) return -1; + return g_strcasecmp(a,b); +} + +/* suggested by Anders Qvist <quest@valdez.netg.se> */ +int jid_cmpx(jid a, jid b, int parts) +{ + if(a == NULL || b == NULL) + return -1; + + if(parts & JID_RESOURCE && _jid_nullstrcmp(a->resource, b->resource) != 0) return -1; + if(parts & JID_USER && _jid_nullstrcasecmp(a->user, b->user) != 0) return -1; + if(parts & JID_SERVER && _jid_nullstrcmp(a->server, b->server) != 0) return -1; + + return 0; +} diff --git a/protocols/jabber/jpacket.c b/protocols/jabber/jpacket.c new file mode 100644 index 00000000..9c7ce00d --- /dev/null +++ b/protocols/jabber/jpacket.c @@ -0,0 +1,159 @@ +/* -------------------------------------------------------------------------- + * + * License + * + * The contents of this file are subject to the Jabber Open Source License + * Version 1.0 (the "JOSL"). You may not copy or use this file, in either + * source code or executable form, except in compliance with the JOSL. You + * may obtain a copy of the JOSL at http://www.jabber.org/ or at + * http://www.opensource.org/. + * + * Software distributed under the JOSL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the JOSL + * for the specific language governing rights and limitations under the + * JOSL. + * + * Copyrights + * + * Portions created by or assigned to Jabber.com, Inc. are + * Copyright (c) 1999-2002 Jabber.com, Inc. All Rights Reserved. Contact + * information for Jabber.com, Inc. is available at http://www.jabber.com/. + * + * Portions Copyright (c) 1998-1999 Jeremie Miller. + * + * Acknowledgements + * + * Special thanks to the Jabber Open Source Contributors for their + * suggestions and support of Jabber. + * + * Alternatively, the contents of this file may be used under the terms of the + * GNU General Public License Version 2 or later (the "GPL"), in which case + * the provisions of the GPL are applicable instead of those above. If you + * wish to allow use of your version of this file only under the terms of the + * GPL and not to allow others to use your version of this file under the JOSL, + * indicate your decision by deleting the provisions above and replace them + * with the notice and other provisions required by the GPL. If you do not + * delete the provisions above, a recipient may use your version of this file + * under either the JOSL or the GPL. + * + * + * --------------------------------------------------------------------------*/ + +#include "jabber.h" +static jpacket jpacket_reset(jpacket p); + +jpacket jpacket_new(xmlnode x) +{ + jpacket p; + + if(x == NULL) + return NULL; + + p = pmalloc(xmlnode_pool(x),sizeof(_jpacket)); + p->x = x; + + return jpacket_reset(p); +} + +static jpacket jpacket_reset(jpacket p) +{ + char *val; + xmlnode x; + + x = p->x; + memset(p,0,sizeof(_jpacket)); + p->x = x; + p->p = xmlnode_pool(x); + + if(strncmp(xmlnode_get_name(x),"message",7) == 0) + { + p->type = JPACKET_MESSAGE; + }else if(strncmp(xmlnode_get_name(x),"presence",8) == 0) + { + p->type = JPACKET_PRESENCE; + val = xmlnode_get_attrib(x, "type"); + if(val == NULL) + p->subtype = JPACKET__AVAILABLE; + else if(strcmp(val,"unavailable") == 0) + p->subtype = JPACKET__UNAVAILABLE; + else if(strcmp(val,"probe") == 0) + p->subtype = JPACKET__PROBE; + else if(strcmp(val,"error") == 0) + p->subtype = JPACKET__ERROR; + else if(strcmp(val,"invisible") == 0) + p->subtype = JPACKET__INVISIBLE; + else if(*val == 's' || *val == 'u') + p->type = JPACKET_S10N; + else if(strcmp(val,"available") == 0) + { /* someone is using type='available' which is frowned upon */ + xmlnode_hide_attrib(x,"type"); + p->subtype = JPACKET__AVAILABLE; + }else + p->type = JPACKET_UNKNOWN; + }else if(strncmp(xmlnode_get_name(x),"iq",2) == 0) + { + p->type = JPACKET_IQ; + p->iq = xmlnode_get_tag(x,"?xmlns"); + p->iqns = xmlnode_get_attrib(p->iq,"xmlns"); + } + + /* set up the jids if any, flag packet as unknown if they are unparseable */ + val = xmlnode_get_attrib(x,"to"); + if(val != NULL) + if((p->to = jid_new(p->p, val)) == NULL) + p->type = JPACKET_UNKNOWN; + val = xmlnode_get_attrib(x,"from"); + if(val != NULL) + if((p->from = jid_new(p->p, val)) == NULL) + p->type = JPACKET_UNKNOWN; + + return p; +} + + +int jpacket_subtype(jpacket p) +{ + char *type; + int ret = p->subtype; + + if(ret != JPACKET__UNKNOWN) + return ret; + + ret = JPACKET__NONE; /* default, when no type attrib is specified */ + type = xmlnode_get_attrib(p->x, "type"); + if(j_strcmp(type,"error") == 0) + ret = JPACKET__ERROR; + else + switch(p->type) + { + case JPACKET_MESSAGE: + if(j_strcmp(type,"chat") == 0) + ret = JPACKET__CHAT; + else if(j_strcmp(type,"groupchat") == 0) + ret = JPACKET__GROUPCHAT; + else if(j_strcmp(type,"headline") == 0) + ret = JPACKET__HEADLINE; + break; + case JPACKET_S10N: + if(j_strcmp(type,"subscribe") == 0) + ret = JPACKET__SUBSCRIBE; + else if(j_strcmp(type,"subscribed") == 0) + ret = JPACKET__SUBSCRIBED; + else if(j_strcmp(type,"unsubscribe") == 0) + ret = JPACKET__UNSUBSCRIBE; + else if(j_strcmp(type,"unsubscribed") == 0) + ret = JPACKET__UNSUBSCRIBED; + break; + case JPACKET_IQ: + if(j_strcmp(type,"get") == 0) + ret = JPACKET__GET; + else if(j_strcmp(type,"set") == 0) + ret = JPACKET__SET; + else if(j_strcmp(type,"result") == 0) + ret = JPACKET__RESULT; + break; + } + + p->subtype = ret; + return ret; +} diff --git a/protocols/jabber/jutil.c b/protocols/jabber/jutil.c new file mode 100644 index 00000000..dd367ac9 --- /dev/null +++ b/protocols/jabber/jutil.c @@ -0,0 +1,122 @@ +/* -------------------------------------------------------------------------- + * + * License + * + * The contents of this file are subject to the Jabber Open Source License + * Version 1.0 (the "JOSL"). You may not copy or use this file, in either + * source code or executable form, except in compliance with the JOSL. You + * may obtain a copy of the JOSL at http://www.jabber.org/ or at + * http://www.opensource.org/. + * + * Software distributed under the JOSL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the JOSL + * for the specific language governing rights and limitations under the + * JOSL. + * + * Copyrights + * + * Portions created by or assigned to Jabber.com, Inc. are + * Copyright (c) 1999-2002 Jabber.com, Inc. All Rights Reserved. Contact + * information for Jabber.com, Inc. is available at http://www.jabber.com/. + * + * Portions Copyright (c) 1998-1999 Jeremie Miller. + * + * Acknowledgements + * + * Special thanks to the Jabber Open Source Contributors for their + * suggestions and support of Jabber. + * + * Alternatively, the contents of this file may be used under the terms of the + * GNU General Public License Version 2 or later (the "GPL"), in which case + * the provisions of the GPL are applicable instead of those above. If you + * wish to allow use of your version of this file only under the terms of the + * GPL and not to allow others to use your version of this file under the JOSL, + * indicate your decision by deleting the provisions above and replace them + * with the notice and other provisions required by the GPL. If you do not + * delete the provisions above, a recipient may use your version of this file + * under either the JOSL or the GPL. + * + * + * --------------------------------------------------------------------------*/ + +#include "jabber.h" +#include <glib.h> +#include "nogaim.h" + +/* util for making presence packets */ +xmlnode jutil_presnew(int type, char *to, char *status) +{ + xmlnode pres; + + pres = xmlnode_new_tag("presence"); + switch(type) + { + case JPACKET__SUBSCRIBE: + xmlnode_put_attrib(pres,"type","subscribe"); + break; + case JPACKET__UNSUBSCRIBE: + xmlnode_put_attrib(pres,"type","unsubscribe"); + break; + case JPACKET__SUBSCRIBED: + xmlnode_put_attrib(pres,"type","subscribed"); + break; + case JPACKET__UNSUBSCRIBED: + xmlnode_put_attrib(pres,"type","unsubscribed"); + break; + case JPACKET__PROBE: + xmlnode_put_attrib(pres,"type","probe"); + break; + case JPACKET__UNAVAILABLE: + xmlnode_put_attrib(pres,"type","unavailable"); + break; + case JPACKET__INVISIBLE: + xmlnode_put_attrib(pres,"type","invisible"); + break; + } + if(to != NULL) + xmlnode_put_attrib(pres,"to",to); + if(status != NULL) + xmlnode_insert_cdata(xmlnode_insert_tag(pres,"status"),status,strlen(status)); + + return pres; +} + +/* util for making IQ packets */ +xmlnode jutil_iqnew(int type, char *ns) +{ + xmlnode iq; + + iq = xmlnode_new_tag("iq"); + switch(type) + { + case JPACKET__GET: + xmlnode_put_attrib(iq,"type","get"); + break; + case JPACKET__SET: + xmlnode_put_attrib(iq,"type","set"); + break; + case JPACKET__RESULT: + xmlnode_put_attrib(iq,"type","result"); + break; + case JPACKET__ERROR: + xmlnode_put_attrib(iq,"type","error"); + break; + } + xmlnode_put_attrib(xmlnode_insert_tag(iq,"query"),"xmlns",ns); + + return iq; +} + +/* util for making stream packets */ +xmlnode jutil_header(char* xmlns, char* server) +{ + xmlnode result; + if ((xmlns == NULL)||(server == NULL)) + return NULL; + result = xmlnode_new_tag("stream:stream"); + xmlnode_put_attrib(result, "xmlns:stream", "http://etherx.jabber.org/streams"); + xmlnode_put_attrib(result, "xmlns", xmlns); + xmlnode_put_attrib(result, "to", server); + + return result; +} diff --git a/protocols/jabber/karma.c b/protocols/jabber/karma.c new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/protocols/jabber/karma.c diff --git a/protocols/jabber/latin1tab.h b/protocols/jabber/latin1tab.h new file mode 100644 index 00000000..48609aa8 --- /dev/null +++ b/protocols/jabber/latin1tab.h @@ -0,0 +1,62 @@ +/* +The contents of this file are subject to the Mozilla Public License +Version 1.1 (the "License"); you may not use this file except in +compliance with the License. You may obtain a copy of the License at +http://www.mozilla.org/MPL/ + +Software distributed under the License is distributed on an "AS IS" +basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +License for the specific language governing rights and limitations +under the License. + +The Original Code is expat. + +The Initial Developer of the Original Code is James Clark. +Portions created by James Clark are Copyright (C) 1998, 1999 +James Clark. All Rights Reserved. + +Contributor(s): + +Alternatively, the contents of this file may be used under the terms +of the GNU General Public License (the "GPL"), in which case the +provisions of the GPL are applicable instead of those above. If you +wish to allow use of your version of this file only under the terms of +the GPL and not to allow others to use your version of this file under +the MPL, indicate your decision by deleting the provisions above and +replace them with the notice and other provisions required by the +GPL. If you do not delete the provisions above, a recipient may use +your version of this file under either the MPL or the GPL. +*/ + +/* 0x80 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x84 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x88 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x8C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x90 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x94 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x98 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x9C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xA0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xA4 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xA8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER, +/* 0xAC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xB0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xB4 */ BT_OTHER, BT_NMSTRT, BT_OTHER, BT_NAME, +/* 0xB8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER, +/* 0xBC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xC0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xC4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xC8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xCC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xD0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xD4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, +/* 0xD8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xDC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xE0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xE4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xE8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xEC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xF0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xF4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, +/* 0xF8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xFC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, diff --git a/protocols/jabber/lib.h b/protocols/jabber/lib.h new file mode 100644 index 00000000..ce0669e5 --- /dev/null +++ b/protocols/jabber/lib.h @@ -0,0 +1,343 @@ + +#include <string.h> +#include <stdlib.h> +#include <sys/types.h> +#include <stdio.h> +#include <setjmp.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <signal.h> +#include <stdarg.h> +#include <ctype.h> +#include <time.h> + +#include "xmlparse.h" + +int j_strcmp(const char *a, const char *b); + +/* +** Arrange to use either varargs or stdargs +*/ + +#define MAXSHORTSTR 203 /* max short string length */ +#define QUAD_T unsigned long long + +#if defined(__STDC__) || defined(_WIN32) + +#include <stdarg.h> + +# define VA_LOCAL_DECL va_list ap; +# define VA_START(f) va_start(ap, f) +# define VA_END va_end(ap) + +#else /* __STDC__ */ + +# include <varargs.h> + +# define VA_LOCAL_DECL va_list ap; +# define VA_START(f) va_start(ap) +# define VA_END va_end(ap) + +#endif /* __STDC__ */ + + +#ifndef INCL_LIB_H +#define INCL_LIB_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* --------------------------------------------------------- */ +/* */ +/* Pool-based memory management routines */ +/* */ +/* --------------------------------------------------------- */ + +#undef POOL_DEBUG +/* + flip these, this should be a prime number for top # of pools debugging +#define POOL_DEBUG 40009 +*/ + +/* pheap - singular allocation of memory */ +struct pheap +{ + void *block; + int size, used; +}; + +/* pool_cleaner - callback type which is associated + with a pool entry; invoked when the pool entry is + free'd */ +typedef void (*pool_cleaner)(void *arg); + +/* pfree - a linked list node which stores an + allocation chunk, plus a callback */ +struct pfree +{ + pool_cleaner f; + void *arg; + struct pheap *heap; + struct pfree *next; +}; + +/* pool - base node for a pool. Maintains a linked list + of pool entries (pfree) */ +typedef struct pool_struct +{ + int size; + struct pfree *cleanup; + struct pheap *heap; +#ifdef POOL_DEBUG + char name[8], zone[32]; + int lsize; +} _pool, *pool; +#define pool_new() _pool_new(ZONE) +#define pool_heap(i) _pool_new_heap(i,ZONE) +#else +} _pool, *pool; +#define pool_heap(i) _pool_new_heap(i,NULL) +#define pool_new() _pool_new(NULL) +#endif + +pool _pool_new(char *zone); /* new pool :) */ +pool _pool_new_heap(int size, char *zone); /* creates a new memory pool with an initial heap size */ +void *pmalloc(pool p, int size); /* wrapper around malloc, takes from the pool, cleaned up automatically */ +void *pmalloc_x(pool p, int size, char c); /* Wrapper around pmalloc which prefils buffer with c */ +void *pmalloco(pool p, int size); /* YAPW for zeroing the block */ +char *pstrdup(pool p, const char *src); /* wrapper around strdup, gains mem from pool */ +void pool_stat(int full); /* print to stderr the changed pools and reset */ +void pool_cleanup(pool p, pool_cleaner f, void *arg); /* calls f(arg) before the pool is freed during cleanup */ +void pool_free(pool p); /* calls the cleanup functions, frees all the data on the pool, and deletes the pool itself */ + + + + +/* --------------------------------------------------------- */ +/* */ +/* Socket helper stuff */ +/* */ +/* --------------------------------------------------------- */ +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 64 +#endif + +#define NETSOCKET_SERVER 0 +#define NETSOCKET_CLIENT 1 +#define NETSOCKET_UDP 2 + +#ifndef WIN32 +int make_netsocket(u_short port, char *host, int type); +struct in_addr *make_addr(char *host); +int set_fd_close_on_exec(int fd, int flag); +#endif + + +/* --------------------------------------------------------- */ +/* */ +/* Hashtable functions */ +/* */ +/* --------------------------------------------------------- */ +typedef struct xhn_struct +{ + struct xhn_struct *next; + const char *key; + void *val; +} *xhn, _xhn; + +char *strescape(pool p, char *); + + +/* --------------------------------------------------------- */ +/* */ +/* String pools (spool) functions */ +/* */ +/* --------------------------------------------------------- */ +struct spool_node +{ + char *c; + struct spool_node *next; +}; + +typedef struct spool_struct +{ + pool p; + int len; + struct spool_node *last; + struct spool_node *first; +} *spool; + +spool spool_new(pool p); /* create a string pool */ +void spooler(spool s, ...); /* append all the char * args to the pool, terminate args with s again */ +char *spool_print(spool s); /* return a big string */ +void spool_add(spool s, char *str); /* add a single char to the pool */ + + +/* --------------------------------------------------------- */ +/* */ +/* xmlnodes - Document Object Model */ +/* */ +/* --------------------------------------------------------- */ +#define NTYPE_TAG 0 +#define NTYPE_ATTRIB 1 +#define NTYPE_CDATA 2 + +#define NTYPE_LAST 2 +#define NTYPE_UNDEF -1 + +/* -------------------------------------------------------------------------- + Node structure. Do not use directly! Always use accessor macros + and methods! + -------------------------------------------------------------------------- */ +typedef struct xmlnode_t +{ + char* name; + unsigned short type; + char* data; + int data_sz; + int complete; + pool p; + struct xmlnode_t* parent; + struct xmlnode_t* firstchild; + struct xmlnode_t* lastchild; + struct xmlnode_t* prev; + struct xmlnode_t* next; + struct xmlnode_t* firstattrib; + struct xmlnode_t* lastattrib; +} _xmlnode, *xmlnode; + +/* Node creation routines */ +xmlnode xmlnode_wrap(xmlnode x,const char* wrapper); +xmlnode xmlnode_new_tag(const char* name); +xmlnode xmlnode_new_tag_pool(pool p, const char* name); +xmlnode xmlnode_insert_tag(xmlnode parent, const char* name); +xmlnode xmlnode_insert_cdata(xmlnode parent, const char* CDATA, unsigned int size); +xmlnode xmlnode_insert_tag_node(xmlnode parent, xmlnode node); +xmlnode xmlnode_str(char *str, int len); +xmlnode xmlnode_dup(xmlnode x); /* duplicate x */ +xmlnode xmlnode_dup_pool(pool p, xmlnode x); + +/* Node Memory Pool */ +pool xmlnode_pool(xmlnode node); + +/* Node editing */ +void xmlnode_hide(xmlnode child); +void xmlnode_hide_attrib(xmlnode parent, const char *name); + +/* Node deletion routine, also frees the node pool! */ +void xmlnode_free(xmlnode node); + +/* Locates a child tag by name and returns it */ +xmlnode xmlnode_get_tag(xmlnode parent, const char* name); +char* xmlnode_get_tag_data(xmlnode parent, const char* name); + +/* Attribute accessors */ +void xmlnode_put_attrib(xmlnode owner, const char* name, const char* value); +char* xmlnode_get_attrib(xmlnode owner, const char* name); +void xmlnode_put_expat_attribs(xmlnode owner, const char** atts); + +/* Bastard am I, but these are fun for internal use ;-) */ +void xmlnode_put_vattrib(xmlnode owner, const char* name, void *value); +void* xmlnode_get_vattrib(xmlnode owner, const char* name); + +/* Node traversal routines */ +xmlnode xmlnode_get_firstchild(xmlnode parent); +xmlnode xmlnode_get_lastchild(xmlnode parent); +xmlnode xmlnode_get_nextsibling(xmlnode sibling); +xmlnode xmlnode_get_prevsibling(xmlnode sibling); +xmlnode xmlnode_get_parent(xmlnode node); + +/* Node information routines */ +char* xmlnode_get_name(xmlnode node); +char* xmlnode_get_data(xmlnode node); + +int xmlnode_has_children(xmlnode node); + +/* Node-to-string translation */ +char* xmlnode2str(xmlnode node); + +/********** END OLD libxode.h BEGIN OLD jabber.h *************/ + + +// #define KARMA_DEBUG +// default to disable karma +#define KARMA_READ_MAX(k) (abs(k)*100) /* how much you are allowed to read off the sock */ +#define KARMA_INIT 5 /* internal "init" value */ +#define KARMA_HEARTBEAT 2 /* seconds to register for heartbeat */ +#define KARMA_MAX 10 /* total max karma you can have */ +#define KARMA_INC 1 /* how much to increment every KARMA_HEARTBEAT seconds */ +#define KARMA_DEC 0 /* how much to penalize for reading KARMA_READ_MAX in + KARMA_HEARTBEAT seconds */ +#define KARMA_PENALTY -5 /* where you go when you hit 0 karma */ +#define KARMA_RESTORE 5 /* where you go when you payed your penelty or INIT */ +#define KARMA_RESETMETER 0 /* Reset byte meter on restore default is falst */ + +struct karma +{ + int init; /* struct initialized */ + int reset_meter; /* reset the byte meter on restore */ + int val; /* current karma value */ + long bytes; /* total bytes read (in that time period) */ + int max; /* max karma you can have */ + int inc,dec; /* how much to increment/decrement */ + int penalty,restore; /* what penalty (<0) or restore (>0) */ + time_t last_update; /* time this was last incremented */ +}; + +struct karma *karma_new(pool p); /* creates a new karma object, with default values */ +void karma_copy(struct karma *new, struct karma *old); /* makes a copy of old in new */ +void karma_increment(struct karma *k); /* inteligently increments karma */ +void karma_decrement(struct karma *k, long bytes_read); /* inteligently decrements karma */ +int karma_check(struct karma *k,long bytes_read); /* checks to see if we have good karma */ + + + +/* --------------------------------------------------------- */ +/* */ +/* Namespace constants */ +/* */ +/* --------------------------------------------------------- */ +#define NSCHECK(x,n) (j_strcmp(xmlnode_get_attrib(x,"xmlns"),n) == 0) + +#define NS_CLIENT "jabber:client" +#define NS_SERVER "jabber:server" +#define NS_AUTH "jabber:iq:auth" +#define NS_REGISTER "jabber:iq:register" +#define NS_ROSTER "jabber:iq:roster" +#define NS_OFFLINE "jabber:x:offline" +#define NS_AGENT "jabber:iq:agent" +#define NS_AGENTS "jabber:iq:agents" +#define NS_DELAY "jabber:x:delay" +#define NS_VERSION "jabber:iq:version" +#define NS_TIME "jabber:iq:time" +#define NS_VCARD "vcard-temp" +#define NS_PRIVATE "jabber:iq:private" +#define NS_SEARCH "jabber:iq:search" +#define NS_OOB "jabber:iq:oob" +#define NS_XOOB "jabber:x:oob" +#define NS_ADMIN "jabber:iq:admin" +#define NS_FILTER "jabber:iq:filter" +#define NS_AUTH_0K "jabber:iq:auth:0k" +#define NS_BROWSE "jabber:iq:browse" +#define NS_EVENT "jabber:x:event" +#define NS_CONFERENCE "jabber:iq:conference" +#define NS_SIGNED "jabber:x:signed" +#define NS_ENCRYPTED "jabber:x:encrypted" +#define NS_GATEWAY "jabber:iq:gateway" +#define NS_LAST "jabber:iq:last" +#define NS_ENVELOPE "jabber:x:envelope" +#define NS_EXPIRE "jabber:x:expire" +#define NS_XHTML "http://www.w3.org/1999/xhtml" + +#define NS_XDBGINSERT "jabber:xdb:ginsert" +#define NS_XDBNSLIST "jabber:xdb:nslist" + + + +#ifdef __cplusplus +} +#endif + +#endif /* INCL_LIB_H */ diff --git a/protocols/jabber/libxode.h b/protocols/jabber/libxode.h new file mode 100644 index 00000000..2ed3fd83 --- /dev/null +++ b/protocols/jabber/libxode.h @@ -0,0 +1,398 @@ +#include <string.h> +#include <stdlib.h> +#include <sys/types.h> +#include <stdio.h> +#include <setjmp.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <signal.h> +#include <syslog.h> +#include <strings.h> +#include <unistd.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> +#include <arpa/inet.h> + +#ifndef __CYGWIN__ +#include <arpa/nameser.h> +#include <resolv.h> +#endif + +#include <sys/time.h> +#include <time.h> + +#include "xmlparse.h" +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif /* HAVE_CONFIG_H */ + +/* +** Arrange to use either varargs or stdargs +*/ + +#define MAXSHORTSTR 203 /* max short string length */ +#define QUAD_T unsigned long long + +#ifdef __STDC__ + +#include <stdarg.h> + +# define VA_LOCAL_DECL va_list ap; +# define VA_START(f) va_start(ap, f) +# define VA_END va_end(ap) + +#else /* __STDC__ */ + +# include <varargs.h> + +# define VA_LOCAL_DECL va_list ap; +# define VA_START(f) va_start(ap) +# define VA_END va_end(ap) + +#endif /* __STDC__ */ + + +#ifndef INCL_LIBXODE_H +#define INCL_LIBXODE_H + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifndef HAVE_SNPRINTF +extern int ap_snprintf(char *, size_t, const char *, ...); +#define snprintf ap_snprintf +#endif + +#ifndef HAVE_VSNPRINTF +extern int ap_vsnprintf(char *, size_t, const char *, va_list ap); +#define vsnprintf ap_vsnprintf +#endif + +#define ZONE zonestr(__FILE__,__LINE__) +char *zonestr(char *file, int line); + +/* --------------------------------------------------------- */ +/* */ +/* Pool-based memory management routines */ +/* */ +/* --------------------------------------------------------- */ + +#undef POOL_DEBUG +/* + flip these, this should be a prime number for top # of pools debugging +#define POOL_DEBUG 40009 +*/ + +/* pheap - singular allocation of memory */ +struct pheap +{ + void *block; + int size, used; +}; + +/* pool_cleaner - callback type which is associated + with a pool entry; invoked when the pool entry is + free'd */ +typedef void (*pool_cleaner)(void *arg); + +/* pfree - a linked list node which stores an + allocation chunk, plus a callback */ +struct pfree +{ + pool_cleaner f; + void *arg; + struct pheap *heap; + struct pfree *next; +}; + +/* pool - base node for a pool. Maintains a linked list + of pool entries (pfree) */ +typedef struct pool_struct +{ + int size; + struct pfree *cleanup; + struct pheap *heap; +#ifdef POOL_DEBUG + char name[8], zone[32]; + int lsize; +} _pool, *pool; +#define pool_new() _pool_new(ZONE) +#define pool_heap(i) _pool_new_heap(i,ZONE) +#else +} _pool, *pool; +#define pool_heap(i) _pool_new_heap(i,NULL) +#define pool_new() _pool_new(NULL) +#endif + +pool _pool_new(char *zone); /* new pool :) */ +pool _pool_new_heap(int size, char *zone); /* creates a new memory pool with an initial heap size */ +void *pmalloc(pool p, int size); /* wrapper around malloc, takes from the pool, cleaned up automatically */ +void *pmalloc_x(pool p, int size, char c); /* Wrapper around pmalloc which prefils buffer with c */ +void *pmalloco(pool p, int size); /* YAPW for zeroing the block */ +char *pstrdup(pool p, const char *src); /* wrapper around strdup, gains mem from pool */ +void pool_stat(int full); /* print to stderr the changed pools and reset */ +void pool_cleanup(pool p, pool_cleaner f, void *arg); /* calls f(arg) before the pool is freed during cleanup */ +void pool_free(pool p); /* calls the cleanup functions, frees all the data on the pool, and deletes the pool itself */ + + + + +/* --------------------------------------------------------- */ +/* */ +/* Socket helper stuff */ +/* */ +/* --------------------------------------------------------- */ +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 64 +#endif + +#define NETSOCKET_SERVER 0 +#define NETSOCKET_CLIENT 1 +#define NETSOCKET_UDP 2 + +#ifndef WIN32 +int make_netsocket(u_short port, char *host, int type); +struct in_addr *make_addr(char *host); +int set_fd_close_on_exec(int fd, int flag); +#endif + + +/* --------------------------------------------------------- */ +/* */ +/* SHA calculations */ +/* */ +/* --------------------------------------------------------- */ +#if (SIZEOF_INT == 4) +typedef unsigned int uint32; +#elif (SIZEOF_SHORT == 4) +typedef unsigned short uint32; +#else +typedef unsigned int uint32; +#endif /* HAVEUINT32 */ + +int sha_hash(int *data, int *hash); +int sha_init(int *hash); +char *shahash(char *str); /* NOT THREAD SAFE */ +void shahash_r(const char* str, char hashbuf[40]); /* USE ME */ + +int strprintsha(char *dest, int *hashval); + + +/* --------------------------------------------------------- */ +/* */ +/* Hashtable functions */ +/* */ +/* --------------------------------------------------------- */ +typedef int (*KEYHASHFUNC)(const void *key); +typedef int (*KEYCOMPAREFUNC)(const void *key1, const void *key2); +typedef int (*TABLEWALKFUNC)(void *user_data, const void *key, void *data); + +typedef void *HASHTABLE; + +HASHTABLE ghash_create(int buckets, KEYHASHFUNC hash, KEYCOMPAREFUNC cmp); +void ghash_destroy(HASHTABLE tbl); +void *ghash_get(HASHTABLE tbl, const void *key); +int ghash_put(HASHTABLE tbl, const void *key, void *value); +int ghash_remove(HASHTABLE tbl, const void *key); +int ghash_walk(HASHTABLE tbl, TABLEWALKFUNC func, void *user_data); +int str_hash_code(const char *s); + + +/* --------------------------------------------------------- */ +/* */ +/* XML escaping utils */ +/* */ +/* --------------------------------------------------------- */ +char *strescape(pool p, char *buf); /* Escape <>&'" chars */ + + +/* --------------------------------------------------------- */ +/* */ +/* String pools (spool) functions */ +/* */ +/* --------------------------------------------------------- */ +struct spool_node +{ + char *c; + struct spool_node *next; +}; + +typedef struct spool_struct +{ + pool p; + int len; + struct spool_node *last; + struct spool_node *first; +} *spool; + +spool spool_new(pool p); /* create a string pool */ +void spooler(spool s, ...); /* append all the char * args to the pool, terminate args with s again */ +char *spool_print(spool s); /* return a big string */ +void spool_add(spool s, char *str); /* add a single char to the pool */ +char *spools(pool p, ...); /* wrap all the spooler stuff in one function, the happy fun ball! */ + + +/* --------------------------------------------------------- */ +/* */ +/* xmlnodes - Document Object Model */ +/* */ +/* --------------------------------------------------------- */ +#define NTYPE_TAG 0 +#define NTYPE_ATTRIB 1 +#define NTYPE_CDATA 2 + +#define NTYPE_LAST 2 +#define NTYPE_UNDEF -1 + +/* -------------------------------------------------------------------------- + Node structure. Do not use directly! Always use accessor macros + and methods! + -------------------------------------------------------------------------- */ +typedef struct xmlnode_t +{ + char* name; + unsigned short type; + char* data; + int data_sz; + int complete; + pool p; + struct xmlnode_t* parent; + struct xmlnode_t* firstchild; + struct xmlnode_t* lastchild; + struct xmlnode_t* prev; + struct xmlnode_t* next; + struct xmlnode_t* firstattrib; + struct xmlnode_t* lastattrib; +} _xmlnode, *xmlnode; + +/* Node creation routines */ +xmlnode xmlnode_wrap(xmlnode x,const char* wrapper); +xmlnode xmlnode_new_tag(const char* name); +xmlnode xmlnode_new_tag_pool(pool p, const char* name); +xmlnode xmlnode_insert_tag(xmlnode parent, const char* name); +xmlnode xmlnode_insert_cdata(xmlnode parent, const char* CDATA, unsigned int size); +xmlnode xmlnode_insert_tag_node(xmlnode parent, xmlnode node); +void xmlnode_insert_node(xmlnode parent, xmlnode node); +xmlnode xmlnode_str(char *str, int len); +xmlnode xmlnode_file(char *file); +xmlnode xmlnode_dup(xmlnode x); /* duplicate x */ +xmlnode xmlnode_dup_pool(pool p, xmlnode x); + +/* Node Memory Pool */ +pool xmlnode_pool(xmlnode node); +xmlnode _xmlnode_new(pool p, const char *name, unsigned int type); + +/* Node editing */ +void xmlnode_hide(xmlnode child); +void xmlnode_hide_attrib(xmlnode parent, const char *name); + +/* Node deletion routine, also frees the node pool! */ +void xmlnode_free(xmlnode node); + +/* Locates a child tag by name and returns it */ +xmlnode xmlnode_get_tag(xmlnode parent, const char* name); +char* xmlnode_get_tag_data(xmlnode parent, const char* name); + +/* Attribute accessors */ +void xmlnode_put_attrib(xmlnode owner, const char* name, const char* value); +char* xmlnode_get_attrib(xmlnode owner, const char* name); +void xmlnode_put_expat_attribs(xmlnode owner, const char** atts); + +/* Bastard am I, but these are fun for internal use ;-) */ +void xmlnode_put_vattrib(xmlnode owner, const char* name, void *value); +void* xmlnode_get_vattrib(xmlnode owner, const char* name); + +/* Node traversal routines */ +xmlnode xmlnode_get_firstattrib(xmlnode parent); +xmlnode xmlnode_get_firstchild(xmlnode parent); +xmlnode xmlnode_get_lastchild(xmlnode parent); +xmlnode xmlnode_get_nextsibling(xmlnode sibling); +xmlnode xmlnode_get_prevsibling(xmlnode sibling); +xmlnode xmlnode_get_parent(xmlnode node); + +/* Node information routines */ +char* xmlnode_get_name(xmlnode node); +char* xmlnode_get_data(xmlnode node); +int xmlnode_get_datasz(xmlnode node); +int xmlnode_get_type(xmlnode node); + +int xmlnode_has_children(xmlnode node); +int xmlnode_has_attribs(xmlnode node); + +/* Node-to-string translation */ +char* xmlnode2str(xmlnode node); + +/* Node-to-terminated-string translation + -- useful for interfacing w/ scripting langs */ +char* xmlnode2tstr(xmlnode node); + +int xmlnode_cmp(xmlnode a, xmlnode b); /* compares a and b for equality */ + +int xmlnode2file(char *file, xmlnode node); /* writes node to file */ + +/* Expat callbacks */ +void expat_startElement(void* userdata, const char* name, const char** atts); +void expat_endElement(void* userdata, const char* name); +void expat_charData(void* userdata, const char* s, int len); + +/* SHA.H */ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is SHA 180-1 Header File + * + * The Initial Developer of the Original Code is Paul Kocher of + * Cryptography Research. Portions created by Paul Kocher are + * Copyright (C) 1995-9 by Cryptography Research, Inc. All + * Rights Reserved. + * + * Contributor(s): + * + * Paul Kocher + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License Version 2 or later (the + * "GPL"), in which case the provisions of the GPL are applicable + * instead of those above. If you wish to allow use of your + * version of this file only under the terms of the GPL and not to + * allow others to use your version of this file under the MPL, + * indicate your decision by deleting the provisions above and + * replace them with the notice and other provisions required by + * the GPL. If you do not delete the provisions above, a recipient + * may use your version of this file under either the MPL or the + * GPL. + */ + +typedef struct { + unsigned long H[5]; + unsigned long W[80]; + int lenW; + unsigned long sizeHi,sizeLo; +} SHA_CTX; + + +void shaInit(SHA_CTX *ctx); +void shaUpdate(SHA_CTX *ctx, unsigned char *dataIn, int len); +void shaFinal(SHA_CTX *ctx, unsigned char hashout[20]); +void shaBlock(unsigned char *dataIn, int len, unsigned char hashout[20]); + + +/* END SHA.H */ + +#ifdef __cplusplus +} +#endif + +#endif /* INCL_LIBXODE_H */ diff --git a/protocols/jabber/log.c b/protocols/jabber/log.c new file mode 100644 index 00000000..86d19e1d --- /dev/null +++ b/protocols/jabber/log.c @@ -0,0 +1,44 @@ +/* + * This program is free software; you can 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. + * + * Jabber + * Copyright (C) 1998-1999 The Jabber Team http://jabber.org/ + */ + +#include "jabber.h" +#include "log.h" + +#ifdef DEBUG + +void jdebug(char *zone, const char *msgfmt, ...) +{ + va_list ap; + static char loghdr[LOGSIZE_HDR]; + static char logmsg[LOGSIZE_TAIL]; + static int size; + + /* XXX: We may want to check the sizes eventually */ + size = g_snprintf(loghdr, LOGSIZE_HDR, "debug/%s %s\n", zone, msgfmt); + + va_start(ap, msgfmt); + size = vsnprintf(logmsg, LOGSIZE_TAIL, loghdr, ap); + + fprintf(stderr,"%s",logmsg); + + return; +} + + +#endif /* DEBUG */ diff --git a/protocols/jabber/log.h b/protocols/jabber/log.h new file mode 100644 index 00000000..9bce9e12 --- /dev/null +++ b/protocols/jabber/log.h @@ -0,0 +1,36 @@ +/* + * This program is free software; you can 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. + * + * Jabber + * Copyright (C) 1998-1999 The Jabber Team http://jabber.org/ + */ + +#ifndef INCL_LOG_H +#define INCL_LOG_H + +#define LOGSIZE_HDR 1024 +#define LOGSIZE_TAIL 2048 + + +#ifdef DEBUG + void jdebug(char *zone, const char *msgfmt, ...); +#else + #define jdebug if(0) warn +#endif + + + +#endif /* INCL_LOG_H */ + diff --git a/protocols/jabber/nametab.h b/protocols/jabber/nametab.h new file mode 100644 index 00000000..b05e62c7 --- /dev/null +++ b/protocols/jabber/nametab.h @@ -0,0 +1,150 @@ +static const unsigned namingBitmap[] = { +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0x00000000, 0x04000000, 0x87FFFFFE, 0x07FFFFFE, +0x00000000, 0x00000000, 0xFF7FFFFF, 0xFF7FFFFF, +0xFFFFFFFF, 0x7FF3FFFF, 0xFFFFFDFE, 0x7FFFFFFF, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFE00F, 0xFC31FFFF, +0x00FFFFFF, 0x00000000, 0xFFFF0000, 0xFFFFFFFF, +0xFFFFFFFF, 0xF80001FF, 0x00000003, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFD740, 0xFFFFFFFB, 0x547F7FFF, 0x000FFFFD, +0xFFFFDFFE, 0xFFFFFFFF, 0xDFFEFFFF, 0xFFFFFFFF, +0xFFFF0003, 0xFFFFFFFF, 0xFFFF199F, 0x033FCFFF, +0x00000000, 0xFFFE0000, 0x027FFFFF, 0xFFFFFFFE, +0x0000007F, 0x00000000, 0xFFFF0000, 0x000707FF, +0x00000000, 0x07FFFFFE, 0x000007FE, 0xFFFE0000, +0xFFFFFFFF, 0x7CFFFFFF, 0x002F7FFF, 0x00000060, +0xFFFFFFE0, 0x23FFFFFF, 0xFF000000, 0x00000003, +0xFFF99FE0, 0x03C5FDFF, 0xB0000000, 0x00030003, +0xFFF987E0, 0x036DFDFF, 0x5E000000, 0x001C0000, +0xFFFBAFE0, 0x23EDFDFF, 0x00000000, 0x00000001, +0xFFF99FE0, 0x23CDFDFF, 0xB0000000, 0x00000003, +0xD63DC7E0, 0x03BFC718, 0x00000000, 0x00000000, +0xFFFDDFE0, 0x03EFFDFF, 0x00000000, 0x00000003, +0xFFFDDFE0, 0x03EFFDFF, 0x40000000, 0x00000003, +0xFFFDDFE0, 0x03FFFDFF, 0x00000000, 0x00000003, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFFFFE, 0x000D7FFF, 0x0000003F, 0x00000000, +0xFEF02596, 0x200D6CAE, 0x0000001F, 0x00000000, +0x00000000, 0x00000000, 0xFFFFFEFF, 0x000003FF, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0xFFFFFFFF, 0xFFFF003F, 0x007FFFFF, +0x0007DAED, 0x50000000, 0x82315001, 0x002C62AB, +0x40000000, 0xF580C900, 0x00000007, 0x02010800, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0x0FFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x03FFFFFF, +0x3F3FFFFF, 0xFFFFFFFF, 0xAAFF3F3F, 0x3FFFFFFF, +0xFFFFFFFF, 0x5FDFFFFF, 0x0FCF1FDC, 0x1FDC1FFF, +0x00000000, 0x00004C40, 0x00000000, 0x00000000, +0x00000007, 0x00000000, 0x00000000, 0x00000000, +0x00000080, 0x000003FE, 0xFFFFFFFE, 0xFFFFFFFF, +0x001FFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0x07FFFFFF, +0xFFFFFFE0, 0x00001FFF, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0xFFFFFFFF, 0x0000003F, 0x00000000, 0x00000000, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0xFFFFFFFF, 0x0000000F, 0x00000000, 0x00000000, +0x00000000, 0x07FF6000, 0x87FFFFFE, 0x07FFFFFE, +0x00000000, 0x00800000, 0xFF7FFFFF, 0xFF7FFFFF, +0x00FFFFFF, 0x00000000, 0xFFFF0000, 0xFFFFFFFF, +0xFFFFFFFF, 0xF80001FF, 0x00030003, 0x00000000, +0xFFFFFFFF, 0xFFFFFFFF, 0x0000003F, 0x00000003, +0xFFFFD7C0, 0xFFFFFFFB, 0x547F7FFF, 0x000FFFFD, +0xFFFFDFFE, 0xFFFFFFFF, 0xDFFEFFFF, 0xFFFFFFFF, +0xFFFF007B, 0xFFFFFFFF, 0xFFFF199F, 0x033FCFFF, +0x00000000, 0xFFFE0000, 0x027FFFFF, 0xFFFFFFFE, +0xFFFE007F, 0xBBFFFFFB, 0xFFFF0016, 0x000707FF, +0x00000000, 0x07FFFFFE, 0x0007FFFF, 0xFFFF03FF, +0xFFFFFFFF, 0x7CFFFFFF, 0xFFEF7FFF, 0x03FF3DFF, +0xFFFFFFEE, 0xF3FFFFFF, 0xFF1E3FFF, 0x0000FFCF, +0xFFF99FEE, 0xD3C5FDFF, 0xB080399F, 0x0003FFCF, +0xFFF987E4, 0xD36DFDFF, 0x5E003987, 0x001FFFC0, +0xFFFBAFEE, 0xF3EDFDFF, 0x00003BBF, 0x0000FFC1, +0xFFF99FEE, 0xF3CDFDFF, 0xB0C0398F, 0x0000FFC3, +0xD63DC7EC, 0xC3BFC718, 0x00803DC7, 0x0000FF80, +0xFFFDDFEE, 0xC3EFFDFF, 0x00603DDF, 0x0000FFC3, +0xFFFDDFEC, 0xC3EFFDFF, 0x40603DDF, 0x0000FFC3, +0xFFFDDFEC, 0xC3FFFDFF, 0x00803DCF, 0x0000FFC3, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFFFFE, 0x07FF7FFF, 0x03FF7FFF, 0x00000000, +0xFEF02596, 0x3BFF6CAE, 0x03FF3F5F, 0x00000000, +0x03000000, 0xC2A003FF, 0xFFFFFEFF, 0xFFFE03FF, +0xFEBF0FDF, 0x02FE3FFF, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x1FFF0000, 0x00000002, +0x000000A0, 0x003EFFFE, 0xFFFFFFFE, 0xFFFFFFFF, +0x661FFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0x77FFFFFF, +}; +static const unsigned char nmstrtPages[] = { +0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x00, +0x00, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, +0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x13, +0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x15, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x17, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x18, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; +static const unsigned char namePages[] = { +0x19, 0x03, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x00, +0x00, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, +0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x13, +0x26, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x27, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x17, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x18, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; diff --git a/protocols/jabber/pool.c b/protocols/jabber/pool.c new file mode 100644 index 00000000..8b88d747 --- /dev/null +++ b/protocols/jabber/pool.c @@ -0,0 +1,247 @@ +/* -------------------------------------------------------------------------- + * + * License + * + * The contents of this file are subject to the Jabber Open Source License + * Version 1.0 (the "JOSL"). You may not copy or use this file, in either + * source code or executable form, except in compliance with the JOSL. You + * may obtain a copy of the JOSL at http://www.jabber.org/ or at + * http://www.opensource.org/. + * + * Software distributed under the JOSL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the JOSL + * for the specific language governing rights and limitations under the + * JOSL. + * + * Copyrights + * + * Portions created by or assigned to Jabber.com, Inc. are + * Copyright (c) 1999-2002 Jabber.com, Inc. All Rights Reserved. Contact + * information for Jabber.com, Inc. is available at http://www.jabber.com/. + * + * Portions Copyright (c) 1998-1999 Jeremie Miller. + * + * Acknowledgements + * + * Special thanks to the Jabber Open Source Contributors for their + * suggestions and support of Jabber. + * + * Alternatively, the contents of this file may be used under the terms of the + * GNU General Public License Version 2 or later (the "GPL"), in which case + * the provisions of the GPL are applicable instead of those above. If you + * wish to allow use of your version of this file only under the terms of the + * GPL and not to allow others to use your version of this file under the JOSL, + * indicate your decision by deleting the provisions above and replace them + * with the notice and other provisions required by the GPL. If you do not + * delete the provisions above, a recipient may use your version of this file + * under either the JOSL or the GPL. + * + * + * --------------------------------------------------------------------------*/ + +#include "jabber.h" +#include "bitlbee.h" +#include <glib.h> + + +#ifdef POOL_DEBUG +int pool__total = 0; +int pool__ltotal = 0; +HASHTABLE pool__disturbed = NULL; +void *_pool__malloc(size_t size) +{ + pool__total++; + return g_malloc(size); +} +void _pool__free(void *block) +{ + pool__total--; + g_free(block); +} +#else +#define _pool__malloc g_malloc +#define _pool__free g_free +#endif + + +/* make an empty pool */ +pool _pool_new(char *zone) +{ + pool p; + while((p = _pool__malloc(sizeof(_pool))) == NULL) sleep(1); + p->cleanup = NULL; + p->heap = NULL; + p->size = 0; + +#ifdef POOL_DEBUG + p->lsize = -1; + p->zone[0] = '\0'; + strcat(p->zone,zone); + sprintf(p->name,"%X",p); + + if(pool__disturbed == NULL) + { + pool__disturbed = 1; /* reentrancy flag! */ + pool__disturbed = ghash_create(POOL_DEBUG,(KEYHASHFUNC)str_hash_code,(KEYCOMPAREFUNC)j_strcmp); + } + if(pool__disturbed != 1) + ghash_put(pool__disturbed,p->name,p); +#endif + + return p; +} + +/* free a heap */ +static void _pool_heap_free(void *arg) +{ + struct pheap *h = (struct pheap *)arg; + + _pool__free(h->block); + _pool__free(h); +} + +/* mem should always be freed last */ +static void _pool_cleanup_append(pool p, struct pfree *pf) +{ + struct pfree *cur; + + if(p->cleanup == NULL) + { + p->cleanup = pf; + return; + } + + /* fast forward to end of list */ + for(cur = p->cleanup; cur->next != NULL; cur = cur->next); + + cur->next = pf; +} + +/* create a cleanup tracker */ +static struct pfree *_pool_free(pool p, pool_cleaner f, void *arg) +{ + struct pfree *ret; + + /* make the storage for the tracker */ + while((ret = _pool__malloc(sizeof(struct pfree))) == NULL) sleep(1); + ret->f = f; + ret->arg = arg; + ret->next = NULL; + + return ret; +} + +/* create a heap and make sure it get's cleaned up */ +static struct pheap *_pool_heap(pool p, int size) +{ + struct pheap *ret; + struct pfree *clean; + + /* make the return heap */ + while((ret = _pool__malloc(sizeof(struct pheap))) == NULL) sleep(1); + while((ret->block = _pool__malloc(size)) == NULL) sleep(1); + ret->size = size; + p->size += size; + ret->used = 0; + + /* append to the cleanup list */ + clean = _pool_free(p, _pool_heap_free, (void *)ret); + clean->heap = ret; /* for future use in finding used mem for pstrdup */ + _pool_cleanup_append(p, clean); + + return ret; +} + +pool _pool_new_heap(int size, char *zone) +{ + pool p; + p = _pool_new(zone); + p->heap = _pool_heap(p,size); + return p; +} + +void *pmalloc(pool p, int size) +{ + void *block; + + if(p == NULL) + { + fprintf(stderr,"Memory Leak! [pmalloc received NULL pool, unable to track allocation, exiting]\n"); + abort(); + } + + /* if there is no heap for this pool or it's a big request, just raw, I like how we clean this :) */ + if(p->heap == NULL || size > (p->heap->size / 2)) + { + while((block = _pool__malloc(size)) == NULL) sleep(1); + p->size += size; + _pool_cleanup_append(p, _pool_free(p, _pool__free, block)); + return block; + } + + /* we have to preserve boundaries, long story :) */ + if(size >= 4) + while(p->heap->used&7) p->heap->used++; + + /* if we don't fit in the old heap, replace it */ + if(size > (p->heap->size - p->heap->used)) + p->heap = _pool_heap(p, p->heap->size); + + /* the current heap has room */ + block = (char *)p->heap->block + p->heap->used; + p->heap->used += size; + return block; +} + +void *pmalloc_x(pool p, int size, char c) +{ + void* result = pmalloc(p, size); + if (result != NULL) + memset(result, c, size); + return result; +} + +/* easy safety utility (for creating blank mem for structs, etc) */ +void *pmalloco(pool p, int size) +{ + void *block = pmalloc(p, size); + memset(block, 0, size); + return block; +} + +/* XXX efficient: move this to const char * and then loop throug the existing heaps to see if src is within a block in this pool */ +char *pstrdup(pool p, const char *src) +{ + char *ret; + + if(src == NULL) + return NULL; + + ret = pmalloc(p,strlen(src) + 1); + strcpy(ret,src); + + return ret; +} + +void pool_free(pool p) +{ + struct pfree *cur, *stub; + + if(p == NULL) return; + + cur = p->cleanup; + while(cur != NULL) + { + (*cur->f)(cur->arg); + stub = cur->next; + _pool__free(cur); + cur = stub; + } + +#ifdef POOL_DEBUG + ghash_remove(pool__disturbed,p->name); +#endif + + _pool__free(p); + +} diff --git a/protocols/jabber/pproxy.c b/protocols/jabber/pproxy.c new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/protocols/jabber/pproxy.c diff --git a/protocols/jabber/rate.c b/protocols/jabber/rate.c new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/protocols/jabber/rate.c diff --git a/protocols/jabber/str.c b/protocols/jabber/str.c new file mode 100644 index 00000000..a8454b44 --- /dev/null +++ b/protocols/jabber/str.c @@ -0,0 +1,215 @@ +/* -------------------------------------------------------------------------- + * + * License + * + * The contents of this file are subject to the Jabber Open Source License + * Version 1.0 (the "JOSL"). You may not copy or use this file, in either + * source code or executable form, except in compliance with the JOSL. You + * may obtain a copy of the JOSL at http://www.jabber.org/ or at + * http://www.opensource.org/. + * + * Software distributed under the JOSL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the JOSL + * for the specific language governing rights and limitations under the + * JOSL. + * + * Copyrights + * + * Portions created by or assigned to Jabber.com, Inc. are + * Copyright (c) 1999-2002 Jabber.com, Inc. All Rights Reserved. Contact + * information for Jabber.com, Inc. is available at http://www.jabber.com/. + * + * Portions Copyright (c) 1998-1999 Jeremie Miller. + * + * Acknowledgements + * + * Special thanks to the Jabber Open Source Contributors for their + * suggestions and support of Jabber. + * + * Alternatively, the contents of this file may be used under the terms of the + * GNU General Public License Version 2 or later (the "GPL"), in which case + * the provisions of the GPL are applicable instead of those above. If you + * wish to allow use of your version of this file only under the terms of the + * GPL and not to allow others to use your version of this file under the JOSL, + * indicate your decision by deleting the provisions above and replace them + * with the notice and other provisions required by the GPL. If you do not + * delete the provisions above, a recipient may use your version of this file + * under either the JOSL or the GPL. + * + * + * --------------------------------------------------------------------------*/ + +#include "jabber.h" +#include <glib.h> + +static char *j_strcat(char *dest, char *txt) +{ + if(!txt) return(dest); + + while(*txt) + *dest++ = *txt++; + *dest = '\0'; + + return(dest); +} + +int j_strcmp(const char *a, const char *b) +{ + if(a == NULL || b == NULL) + return -1; + + while(*a == *b && *a != '\0' && *b != '\0'){ a++; b++; } + + if(*a == *b) return 0; + + return -1; +} + +spool spool_new(pool p) +{ + spool s; + + s = pmalloc(p, sizeof(struct spool_struct)); + s->p = p; + s->len = 0; + s->last = NULL; + s->first = NULL; + return s; +} + +void spool_add(spool s, char *str) +{ + struct spool_node *sn; + int len; + + if(str == NULL) + return; + + len = strlen(str); + if(len == 0) + return; + + sn = pmalloc(s->p, sizeof(struct spool_node)); + sn->c = pstrdup(s->p, str); + sn->next = NULL; + + s->len += len; + if(s->last != NULL) + s->last->next = sn; + s->last = sn; + if(s->first == NULL) + s->first = sn; +} + +void spooler(spool s, ...) +{ + va_list ap; + char *arg = NULL; + + if(s == NULL) + return; + + VA_START(s); + + /* loop till we hfit our end flag, the first arg */ + while(1) + { + arg = va_arg(ap,char *); + if((spool)arg == s) + break; + else + spool_add(s, arg); + } + + va_end(ap); +} + +char *spool_print(spool s) +{ + char *ret,*tmp; + struct spool_node *next; + + if(s == NULL || s->len == 0 || s->first == NULL) + return NULL; + + ret = pmalloc(s->p, s->len + 1); + *ret = '\0'; + + next = s->first; + tmp = ret; + while(next != NULL) + { + tmp = j_strcat(tmp,next->c); + next = next->next; + } + + return ret; +} + +char *strescape(pool p, char *buf) +{ + int i,j,oldlen,newlen; + char *temp; + + if (p == NULL || buf == NULL) return(NULL); + + oldlen = newlen = strlen(buf); + for(i=0;i<oldlen;i++) + { + switch(buf[i]) + { + case '&': + newlen+=5; + break; + case '\'': + newlen+=6; + break; + case '\"': + newlen+=6; + break; + case '<': + newlen+=4; + break; + case '>': + newlen+=4; + break; + } + } + + if(oldlen == newlen) return buf; + + temp = pmalloc(p,newlen+1); + + if (temp==NULL) return(NULL); + + for(i=j=0;i<oldlen;i++) + { + switch(buf[i]) + { + case '&': + memcpy(&temp[j],"&",5); + j += 5; + break; + case '\'': + memcpy(&temp[j],"'",6); + j += 6; + break; + case '\"': + memcpy(&temp[j],""",6); + j += 6; + break; + case '<': + memcpy(&temp[j],"<",4); + j += 4; + break; + case '>': + memcpy(&temp[j],">",4); + j += 4; + break; + default: + temp[j++] = buf[i]; + } + } + temp[j] = '\0'; + return temp; +} diff --git a/protocols/jabber/utf8tab.h b/protocols/jabber/utf8tab.h new file mode 100644 index 00000000..a38fe624 --- /dev/null +++ b/protocols/jabber/utf8tab.h @@ -0,0 +1,63 @@ +/* +The contents of this file are subject to the Mozilla Public License +Version 1.1 (the "License"); you may not use this file except in +compliance with the License. You may obtain a copy of the License at +http://www.mozilla.org/MPL/ + +Software distributed under the License is distributed on an "AS IS" +basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +License for the specific language governing rights and limitations +under the License. + +The Original Code is expat. + +The Initial Developer of the Original Code is James Clark. +Portions created by James Clark are Copyright (C) 1998, 1999 +James Clark. All Rights Reserved. + +Contributor(s): + +Alternatively, the contents of this file may be used under the terms +of the GNU General Public License (the "GPL"), in which case the +provisions of the GPL are applicable instead of those above. If you +wish to allow use of your version of this file only under the terms of +the GPL and not to allow others to use your version of this file under +the MPL, indicate your decision by deleting the provisions above and +replace them with the notice and other provisions required by the +GPL. If you do not delete the provisions above, a recipient may use +your version of this file under either the MPL or the GPL. +*/ + + +/* 0x80 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x84 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x88 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x8C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x90 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x94 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x98 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x9C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xA0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xA4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xA8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xAC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xB0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xB4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xB8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xBC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xC0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xC4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xC8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xCC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xD0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xD4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xD8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xDC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xE0 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, +/* 0xE4 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, +/* 0xE8 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, +/* 0xEC */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, +/* 0xF0 */ BT_LEAD4, BT_LEAD4, BT_LEAD4, BT_LEAD4, +/* 0xF4 */ BT_LEAD4, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0xF8 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0xFC */ BT_NONXML, BT_NONXML, BT_MALFORM, BT_MALFORM, diff --git a/protocols/jabber/xhash.c b/protocols/jabber/xhash.c new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/protocols/jabber/xhash.c diff --git a/protocols/jabber/xmldef.h b/protocols/jabber/xmldef.h new file mode 100644 index 00000000..8b2b2308 --- /dev/null +++ b/protocols/jabber/xmldef.h @@ -0,0 +1,34 @@ +/* +The contents of this file are subject to the Mozilla Public License +Version 1.1 (the "License"); you may not use this file except in +compliance with the License. You may obtain a copy of the License at +http://www.mozilla.org/MPL/ + +Software distributed under the License is distributed on an "AS IS" +basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +License for the specific language governing rights and limitations +under the License. + +The Original Code is expat. + +The Initial Developer of the Original Code is James Clark. +Portions created by James Clark are Copyright (C) 1998, 1999 +James Clark. All Rights Reserved. + +Contributor(s): + +Alternatively, the contents of this file may be used under the terms +of the GNU General Public License (the "GPL"), in which case the +provisions of the GPL are applicable instead of those above. If you +wish to allow use of your version of this file only under the terms of +the GPL and not to allow others to use your version of this file under +the MPL, indicate your decision by deleting the provisions above and +replace them with the notice and other provisions required by the +GPL. If you do not delete the provisions above, a recipient may use +your version of this file under either the MPL or the GPL. +*/ + +#include <glib.h> +#include <string.h> +#include <stdlib.h> + diff --git a/protocols/jabber/xmlnode.c b/protocols/jabber/xmlnode.c new file mode 100644 index 00000000..88dd4eef --- /dev/null +++ b/protocols/jabber/xmlnode.c @@ -0,0 +1,705 @@ +/* -------------------------------------------------------------------------- + * + * License + * + * The contents of this file are subject to the Jabber Open Source License + * Version 1.0 (the "JOSL"). You may not copy or use this file, in either + * source code or executable form, except in compliance with the JOSL. You + * may obtain a copy of the JOSL at http://www.jabber.org/ or at + * http://www.opensource.org/. + * + * Software distributed under the JOSL is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the JOSL + * for the specific language governing rights and limitations under the + * JOSL. + * + * Copyrights + * + * Portions created by or assigned to Jabber.com, Inc. are + * Copyright (c) 1999-2002 Jabber.com, Inc. All Rights Reserved. Contact + * information for Jabber.com, Inc. is available at http://www.jabber.com/. + * + * Portions Copyright (c) 1998-1999 Jeremie Miller. + * + * Acknowledgements + * + * Special thanks to the Jabber Open Source Contributors for their + * suggestions and support of Jabber. + * + * Alternatively, the contents of this file may be used under the terms of the + * GNU General Public License Version 2 or later (the "GPL"), in which case + * the provisions of the GPL are applicable instead of those above. If you + * wish to allow use of your version of this file only under the terms of the + * GPL and not to allow others to use your version of this file under the JOSL, + * indicate your decision by deleting the provisions above and replace them + * with the notice and other provisions required by the GPL. If you do not + * delete the provisions above, a recipient may use your version of this file + * under either the JOSL or the GPL. + * + * + * --------------------------------------------------------------------------*/ + +#include "jabber.h" +#include <glib.h> + +static xmlnode xmlnode_get_firstattrib(xmlnode parent); +static int xmlnode_get_type(xmlnode node); +static void xmlnode_insert_node(xmlnode parent, xmlnode node); + +/* Internal routines */ +static xmlnode _xmlnode_new(pool p, const char* name, unsigned int type) +{ + xmlnode result = NULL; + if (type > NTYPE_LAST) + return NULL; + + if (type != NTYPE_CDATA && name == NULL) + return NULL; + + if (p == NULL) + { + p = pool_heap(1*1024); + } + + /* Allocate & zero memory */ + result = (xmlnode)pmalloco(p, sizeof(_xmlnode)); + + /* Initialize fields */ + if (type != NTYPE_CDATA) + result->name = pstrdup(p,name); + result->type = type; + result->p = p; + return result; +} + +static xmlnode _xmlnode_append_sibling(xmlnode lastsibling, const char* name, unsigned int type) +{ + xmlnode result; + + result = _xmlnode_new(xmlnode_pool(lastsibling), name, type); + if (result != NULL) + { + /* Setup sibling pointers */ + result->prev = lastsibling; + lastsibling->next = result; + } + return result; +} + +static xmlnode _xmlnode_insert(xmlnode parent, const char* name, unsigned int type) +{ + xmlnode result; + + if(parent == NULL || (type != NTYPE_CDATA && name == NULL)) return NULL; + + /* If parent->firstchild is NULL, simply create a new node for the first child */ + if (parent->firstchild == NULL) + { + result = _xmlnode_new(parent->p, name, type); + parent->firstchild = result; + } + /* Otherwise, append this to the lastchild */ + else + { + result= _xmlnode_append_sibling(parent->lastchild, name, type); + } + result->parent = parent; + parent->lastchild = result; + return result; + +} + +static xmlnode _xmlnode_search(xmlnode firstsibling, const char* name, unsigned int type) +{ + xmlnode current; + + /* Walk the sibling list, looking for a NTYPE_TAG xmlnode with + the specified name */ + current = firstsibling; + while (current != NULL) + { + if ((current->type == type) && (j_strcmp(current->name, name) == 0)) + return current; + else + current = current->next; + } + return NULL; +} + +static void _xmlnode_merge(xmlnode data) +{ + xmlnode cur; + char *merge, *scur; + int imerge; + + /* get total size of all merged cdata */ + imerge = 0; + for(cur = data; cur != NULL && cur->type == NTYPE_CDATA; cur = cur->next) + imerge += cur->data_sz; + + /* copy in current data and then spin through all of them and merge */ + scur = merge = pmalloc(data->p,imerge + 1); + for(cur = data; cur != NULL && cur->type == NTYPE_CDATA; cur = cur->next) + { + memcpy(scur,cur->data,cur->data_sz); + scur += cur->data_sz; + } + *scur = '\0'; + + /* this effectively hides all of the merged-in chunks */ + data->next = cur; + if(cur == NULL) + data->parent->lastchild = data; + else + cur->prev = data; + + /* reset data */ + data->data = merge; + data->data_sz = imerge; + +} + +static void _xmlnode_hide_sibling(xmlnode child) +{ + if(child == NULL) + return; + + if(child->prev != NULL) + child->prev->next = child->next; + if(child->next != NULL) + child->next->prev = child->prev; +} + +static void _xmlnode_tag2str(spool s, xmlnode node, int flag) +{ + xmlnode tmp; + + if(flag==0 || flag==1) + { + spooler(s,"<",xmlnode_get_name(node),s); + tmp = xmlnode_get_firstattrib(node); + while(tmp) { + spooler(s," ",xmlnode_get_name(tmp),"='",strescape(xmlnode_pool(node),xmlnode_get_data(tmp)),"'",s); + tmp = xmlnode_get_nextsibling(tmp); + } + if(flag==0) + spool_add(s,"/>"); + else + spool_add(s,">"); + } + else + { + spooler(s,"</",xmlnode_get_name(node),">",s); + } +} + +static spool _xmlnode2spool(xmlnode node) +{ + spool s; + int level=0,dir=0; + xmlnode tmp; + + if(!node || xmlnode_get_type(node)!=NTYPE_TAG) + return NULL; + + s = spool_new(xmlnode_pool(node)); + if(!s) return(NULL); + + while(1) + { + if(dir==0) + { + if(xmlnode_get_type(node) == NTYPE_TAG) + { + if(xmlnode_has_children(node)) + { + _xmlnode_tag2str(s,node,1); + node = xmlnode_get_firstchild(node); + level++; + continue; + }else{ + _xmlnode_tag2str(s,node,0); + } + }else{ + spool_add(s,strescape(xmlnode_pool(node),xmlnode_get_data(node))); + } + } + + tmp = xmlnode_get_nextsibling(node); + if(!tmp) + { + node = xmlnode_get_parent(node); + level--; + if(level>=0) _xmlnode_tag2str(s,node,2); + if(level<1) break; + dir = 1; + }else{ + node = tmp; + dir = 0; + } + } + + return s; +} + + +/* External routines */ + + +/* + * xmlnode_new_tag -- create a tag node + * Automatically creates a memory pool for the node. + * + * parameters + * name -- name of the tag + * + * returns + * a pointer to the tag node + * or NULL if it was unsuccessfull + */ +xmlnode xmlnode_new_tag(const char* name) +{ + return _xmlnode_new(NULL, name, NTYPE_TAG); +} + + +/* + * xmlnode_insert_tag -- append a child tag to a tag + * + * parameters + * parent -- pointer to the parent tag + * name -- name of the child tag + * + * returns + * a pointer to the child tag node + * or NULL if it was unsuccessfull + */ +xmlnode xmlnode_insert_tag(xmlnode parent, const char* name) +{ + return _xmlnode_insert(parent, name, NTYPE_TAG); +} + + +/* + * xmlnode_insert_cdata -- append character data to a tag + * + * parameters + * parent -- parent tag + * CDATA -- character data + * size -- size of CDATA + * or -1 for null-terminated CDATA strings + * + * returns + * a pointer to the child CDATA node + * or NULL if it was unsuccessfull + */ +xmlnode xmlnode_insert_cdata(xmlnode parent, const char* CDATA, unsigned int size) +{ + xmlnode result; + + if(CDATA == NULL || parent == NULL) + return NULL; + + if(size == -1) + size = strlen(CDATA); + + result = _xmlnode_insert(parent, NULL, NTYPE_CDATA); + if (result != NULL) + { + result->data = (char*)pmalloc(result->p, size + 1); + memcpy(result->data, CDATA, size); + result->data[size] = '\0'; + result->data_sz = size; + } + + return result; +} + + +/* + * xmlnode_get_tag -- find given tag in an xmlnode tree + * + * parameters + * parent -- pointer to the parent tag + * name -- "name" for the child tag of that name + * "name/name" for a sub child (recurses) + * "?attrib" to match the first tag with that attrib defined + * "?attrib=value" to match the first tag with that attrib and value + * "=cdata" to match the cdata contents of the child + * or any combination: "name/name/?attrib", "name=cdata", etc + * + * results + * a pointer to the tag matching search criteria + * or NULL if search was unsuccessfull + */ +xmlnode xmlnode_get_tag(xmlnode parent, const char* name) +{ + char *str, *slash, *qmark, *equals; + xmlnode step, ret; + + + if(parent == NULL || parent->firstchild == NULL || name == NULL || name == '\0') return NULL; + + if(strstr(name, "/") == NULL && strstr(name,"?") == NULL && strstr(name, "=") == NULL) + return _xmlnode_search(parent->firstchild, name, NTYPE_TAG); + + str = g_strdup(name); + slash = strstr(str, "/"); + qmark = strstr(str, "?"); + equals = strstr(str, "="); + + if(equals != NULL && (slash == NULL || equals < slash) && (qmark == NULL || equals < qmark)) + { /* of type =cdata */ + + *equals = '\0'; + equals++; + + for(step = parent->firstchild; step != NULL; step = xmlnode_get_nextsibling(step)) + { + if(xmlnode_get_type(step) != NTYPE_TAG) + continue; + + if(*str != '\0') + if(j_strcmp(xmlnode_get_name(step),str) != 0) + continue; + + if(j_strcmp(xmlnode_get_data(step),equals) != 0) + continue; + + break; + } + + g_free(str); + return step; + } + + + if(qmark != NULL && (slash == NULL || qmark < slash)) + { /* of type ?attrib */ + + *qmark = '\0'; + qmark++; + if(equals != NULL) + { + *equals = '\0'; + equals++; + } + + for(step = parent->firstchild; step != NULL; step = xmlnode_get_nextsibling(step)) + { + if(xmlnode_get_type(step) != NTYPE_TAG) + continue; + + if(*str != '\0') + if(j_strcmp(xmlnode_get_name(step),str) != 0) + continue; + + if(xmlnode_get_attrib(step,qmark) == NULL) + continue; + + if(equals != NULL && j_strcmp(xmlnode_get_attrib(step,qmark),equals) != 0) + continue; + + break; + } + + g_free(str); + return step; + } + + + *slash = '\0'; + ++slash; + + for(step = parent->firstchild; step != NULL; step = xmlnode_get_nextsibling(step)) + { + if(xmlnode_get_type(step) != NTYPE_TAG) continue; + + if(j_strcmp(xmlnode_get_name(step),str) != 0) + continue; + + ret = xmlnode_get_tag(step, slash); + if(ret != NULL) + { + g_free(str); + return ret; + } + } + + g_free(str); + return NULL; +} + + +/* return the cdata from any tag */ +char *xmlnode_get_tag_data(xmlnode parent, const char *name) +{ + xmlnode tag; + + tag = xmlnode_get_tag(parent, name); + if(tag == NULL) return NULL; + + return xmlnode_get_data(tag); +} + + +void xmlnode_put_attrib(xmlnode owner, const char* name, const char* value) +{ + xmlnode attrib; + + if(owner == NULL || name == NULL || value == NULL) return; + + /* If there are no existing attributs, allocate a new one to start + the list */ + if (owner->firstattrib == NULL) + { + attrib = _xmlnode_new(owner->p, name, NTYPE_ATTRIB); + owner->firstattrib = attrib; + owner->lastattrib = attrib; + } + else + { + attrib = _xmlnode_search(owner->firstattrib, name, NTYPE_ATTRIB); + if(attrib == NULL) + { + attrib = _xmlnode_append_sibling(owner->lastattrib, name, NTYPE_ATTRIB); + owner->lastattrib = attrib; + } + } + /* Update the value of the attribute */ + attrib->data_sz = strlen(value); + attrib->data = pstrdup(owner->p, value); + +} + +char* xmlnode_get_attrib(xmlnode owner, const char* name) +{ + xmlnode attrib; + + if (owner != NULL && owner->firstattrib != NULL) + { + attrib = _xmlnode_search(owner->firstattrib, name, NTYPE_ATTRIB); + if (attrib != NULL) + return (char*)attrib->data; + } + return NULL; +} + +static xmlnode xmlnode_get_firstattrib(xmlnode parent) +{ + if (parent != NULL) + return parent->firstattrib; + return NULL; +} + +xmlnode xmlnode_get_firstchild(xmlnode parent) +{ + if (parent != NULL) + return parent->firstchild; + return NULL; +} + +xmlnode xmlnode_get_nextsibling(xmlnode sibling) +{ + if (sibling != NULL) + return sibling->next; + return NULL; +} + +xmlnode xmlnode_get_parent(xmlnode node) +{ + if (node != NULL) + return node->parent; + return NULL; +} + +char* xmlnode_get_name(xmlnode node) +{ + if (node != NULL) + return node->name; + return NULL; +} + +char* xmlnode_get_data(xmlnode node) +{ + if(xmlnode_get_type(node) == NTYPE_TAG) /* loop till we find a CDATA in the children */ + for(node = xmlnode_get_firstchild(node); node != NULL; node = xmlnode_get_nextsibling(node)) + if(xmlnode_get_type(node) == NTYPE_CDATA) break; + + if(node == NULL) return NULL; + + /* check for a dirty node w/ unassembled cdata chunks */ + if(xmlnode_get_type(node->next) == NTYPE_CDATA) + _xmlnode_merge(node); + + return node->data; +} + +static int xmlnode_get_datasz(xmlnode node) +{ + if(xmlnode_get_type(node) != NTYPE_CDATA) return 0; + + /* check for a dirty node w/ unassembled cdata chunks */ + if(xmlnode_get_type(node->next) == NTYPE_CDATA) + _xmlnode_merge(node); + return node->data_sz; +} + +static int xmlnode_get_type(xmlnode node) +{ + if (node != NULL) + return node->type; + return NTYPE_UNDEF; +} + +int xmlnode_has_children(xmlnode node) +{ + if ((node != NULL) && (node->firstchild != NULL)) + return 1; + return 0; +} + +static int xmlnode_has_attribs(xmlnode node) +{ + if ((node != NULL) && (node->firstattrib != NULL)) + return 1; + return 0; +} + +pool xmlnode_pool(xmlnode node) +{ + if (node != NULL) + return node->p; + return (pool)NULL; +} + +void xmlnode_hide_attrib(xmlnode parent, const char *name) +{ + xmlnode attrib; + + if(parent == NULL || parent->firstattrib == NULL || name == NULL) + return; + + attrib = _xmlnode_search(parent->firstattrib, name, NTYPE_ATTRIB); + if(attrib == NULL) + return; + + /* first fix up at the child level */ + _xmlnode_hide_sibling(attrib); + + /* next fix up at the parent level */ + if(parent->firstattrib == attrib) + parent->firstattrib = attrib->next; + if(parent->lastattrib == attrib) + parent->lastattrib = attrib->prev; +} + + + +/* + * xmlnode2str -- convert given xmlnode tree into a string + * + * parameters + * node -- pointer to the xmlnode structure + * + * results + * a pointer to the created string + * or NULL if it was unsuccessfull + */ +char *xmlnode2str(xmlnode node) +{ + return spool_print(_xmlnode2spool(node)); +} + +/* loop through both a and b comparing everything, attribs, cdata, children, etc */ +static int xmlnode_cmp(xmlnode a, xmlnode b) +{ + int ret = 0; + + while(1) + { + if(a == NULL && b == NULL) + return 0; + + if(a == NULL || b == NULL) + return -1; + + if(xmlnode_get_type(a) != xmlnode_get_type(b)) + return -1; + + switch(xmlnode_get_type(a)) + { + case NTYPE_ATTRIB: + ret = j_strcmp(xmlnode_get_name(a), xmlnode_get_name(b)); + if(ret != 0) + return -1; + ret = j_strcmp(xmlnode_get_data(a), xmlnode_get_data(b)); + if(ret != 0) + return -1; + break; + case NTYPE_TAG: + ret = j_strcmp(xmlnode_get_name(a), xmlnode_get_name(b)); + if(ret != 0) + return -1; + ret = xmlnode_cmp(xmlnode_get_firstattrib(a), xmlnode_get_firstattrib(b)); + if(ret != 0) + return -1; + ret = xmlnode_cmp(xmlnode_get_firstchild(a), xmlnode_get_firstchild(b)); + if(ret != 0) + return -1; + break; + case NTYPE_CDATA: + ret = j_strcmp(xmlnode_get_data(a), xmlnode_get_data(b)); + if(ret != 0) + return -1; + } + a = xmlnode_get_nextsibling(a); + b = xmlnode_get_nextsibling(b); + } +} + + +xmlnode xmlnode_insert_tag_node(xmlnode parent, xmlnode node) +{ + xmlnode child; + + child = xmlnode_insert_tag(parent, xmlnode_get_name(node)); + if (xmlnode_has_attribs(node)) + xmlnode_insert_node(child, xmlnode_get_firstattrib(node)); + if (xmlnode_has_children(node)) + xmlnode_insert_node(child, xmlnode_get_firstchild(node)); + + return child; +} + +/* places copy of node and node's siblings in parent */ +static void xmlnode_insert_node(xmlnode parent, xmlnode node) +{ + if(node == NULL || parent == NULL) + return; + + while(node != NULL) + { + switch(xmlnode_get_type(node)) + { + case NTYPE_ATTRIB: + xmlnode_put_attrib(parent, xmlnode_get_name(node), xmlnode_get_data(node)); + break; + case NTYPE_TAG: + xmlnode_insert_tag_node(parent, node); + break; + case NTYPE_CDATA: + xmlnode_insert_cdata(parent, xmlnode_get_data(node), xmlnode_get_datasz(node)); + } + node = xmlnode_get_nextsibling(node); + } +} + + +void xmlnode_free(xmlnode node) +{ + if(node == NULL) + return; + + pool_free(node->p); +} diff --git a/protocols/jabber/xmlparse.c b/protocols/jabber/xmlparse.c new file mode 100644 index 00000000..492da948 --- /dev/null +++ b/protocols/jabber/xmlparse.c @@ -0,0 +1,2629 @@ +/* +The contents of this file are subject to the Mozilla Public License +Version 1.1 (the "License"); you may not use this file except in +compliance with the License. You may obtain a copy of the License at +http://www.mozilla.org/MPL/ + +Software distributed under the License is distributed on an "AS IS" +basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +License for the specific language governing rights and limitations +under the License. + +The Original Code is expat. + +The Initial Developer of the Original Code is James Clark. +Portions created by James Clark are Copyright (C) 1998, 1999 +James Clark. All Rights Reserved. + +Contributor(s): + +*/ + +#include "xmldef.h" +#include "xmlparse.h" + +#ifdef XML_UNICODE +#define XML_ENCODE_MAX XML_UTF16_ENCODE_MAX +#define XmlConvert XmlUtf16Convert +#define XmlGetInternalEncoding XmlGetUtf16InternalEncoding +#define XmlGetInternalEncodingNS XmlGetUtf16InternalEncodingNS +#define XmlEncode XmlUtf16Encode +#define MUST_CONVERT(enc, s) (!(enc)->isUtf16 || (((unsigned long)s) & 1)) +typedef unsigned short ICHAR; +#else +#define XML_ENCODE_MAX XML_UTF8_ENCODE_MAX +#define XmlConvert XmlUtf8Convert +#define XmlGetInternalEncoding XmlGetUtf8InternalEncoding +#define XmlGetInternalEncodingNS XmlGetUtf8InternalEncodingNS +#define XmlEncode XmlUtf8Encode +#define MUST_CONVERT(enc, s) (!(enc)->isUtf8) +typedef char ICHAR; +#endif + + +#ifndef XML_NS + +#define XmlInitEncodingNS XmlInitEncoding +#define XmlInitUnknownEncodingNS XmlInitUnknownEncoding +#undef XmlGetInternalEncodingNS +#define XmlGetInternalEncodingNS XmlGetInternalEncoding +#define XmlParseXmlDeclNS XmlParseXmlDecl + +#endif + + +#ifdef XML_UNICODE_WCHAR_T +#define XML_T(x) L ## x +#else +#define XML_T(x) x +#endif + +/* Round up n to be a multiple of sz, where sz is a power of 2. */ +#define ROUND_UP(n, sz) (((n) + ((sz) - 1)) & ~((sz) - 1)) + +#include "xmltok.h" +#include "xmlrole.h" +#include "hashtable.h" + +#define INIT_TAG_BUF_SIZE 32 /* must be a multiple of sizeof(XML_Char) */ +#define INIT_DATA_BUF_SIZE 1024 +#define INIT_ATTS_SIZE 16 +#define INIT_BLOCK_SIZE 1024 +#define INIT_BUFFER_SIZE 1024 + +#define EXPAND_SPARE 24 + +typedef struct binding { + struct prefix *prefix; + struct binding *nextTagBinding; + struct binding *prevPrefixBinding; + const struct attribute_id *attId; + XML_Char *uri; + int uriLen; + int uriAlloc; +} BINDING; + +typedef struct prefix { + const XML_Char *name; + BINDING *binding; +} PREFIX; + +typedef struct { + const XML_Char *str; + const XML_Char *localPart; + int uriLen; +} TAG_NAME; + +typedef struct tag { + struct tag *parent; + const char *rawName; + int rawNameLength; + TAG_NAME name; + char *buf; + char *bufEnd; + BINDING *bindings; +} TAG; + +typedef struct { + const XML_Char *name; + const XML_Char *textPtr; + int textLen; + const XML_Char *systemId; + const XML_Char *base; + const XML_Char *publicId; + const XML_Char *notation; + char open; +} ENTITY; + +typedef struct block { + struct block *next; + int size; + XML_Char s[1]; +} BLOCK; + +typedef struct { + BLOCK *blocks; + BLOCK *freeBlocks; + const XML_Char *end; + XML_Char *ptr; + XML_Char *start; +} STRING_POOL; + +/* The XML_Char before the name is used to determine whether +an attribute has been specified. */ +typedef struct attribute_id { + XML_Char *name; + PREFIX *prefix; + char maybeTokenized; + char xmlns; +} ATTRIBUTE_ID; + +typedef struct { + const ATTRIBUTE_ID *id; + char isCdata; + const XML_Char *value; +} DEFAULT_ATTRIBUTE; + +typedef struct { + const XML_Char *name; + PREFIX *prefix; + int nDefaultAtts; + int allocDefaultAtts; + DEFAULT_ATTRIBUTE *defaultAtts; +} ELEMENT_TYPE; + +typedef struct { + HASH_TABLE generalEntities; + HASH_TABLE elementTypes; + HASH_TABLE attributeIds; + HASH_TABLE prefixes; + STRING_POOL pool; + int complete; + int standalone; + const XML_Char *base; + PREFIX defaultPrefix; +} DTD; + +typedef struct open_internal_entity { + const char *internalEventPtr; + const char *internalEventEndPtr; + struct open_internal_entity *next; + ENTITY *entity; +} OPEN_INTERNAL_ENTITY; + +typedef enum XML_Error Processor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr); + +static Processor prologProcessor; +static Processor prologInitProcessor; +static Processor contentProcessor; +static Processor cdataSectionProcessor; +static Processor epilogProcessor; + +static enum XML_Error +handleUnknownEncoding(XML_Parser parser, const XML_Char *encodingName); +static enum XML_Error +processXmlDecl(XML_Parser parser, int isGeneralTextEntity, const char *, const char *); +static enum XML_Error +initializeEncoding(XML_Parser parser); +static enum XML_Error +doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, + const char *start, const char *end, const char **endPtr); +static enum XML_Error +doCdataSection(XML_Parser parser, const ENCODING *, const char **startPtr, const char *end, const char **nextPtr); +static enum XML_Error storeAtts(XML_Parser parser, const ENCODING *, const char *s, + TAG_NAME *tagNamePtr, BINDING **bindingsPtr); +static +int addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, const XML_Char *uri, BINDING **bindingsPtr); +static int +defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *, int isCdata, const XML_Char *dfltValue); +static enum XML_Error +storeAttributeValue(XML_Parser parser, const ENCODING *, int isCdata, const char *, const char *, + STRING_POOL *); +static enum XML_Error +appendAttributeValue(XML_Parser parser, const ENCODING *, int isCdata, const char *, const char *, + STRING_POOL *); +static ATTRIBUTE_ID * +getAttributeId(XML_Parser parser, const ENCODING *enc, const char *start, const char *end); +static int setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *); +static enum XML_Error +storeEntityValue(XML_Parser parser, const char *start, const char *end); +static int +reportProcessingInstruction(XML_Parser parser, const ENCODING *enc, const char *start, const char *end); +static int +reportComment(XML_Parser parser, const ENCODING *enc, const char *start, const char *end); +static void +reportDefault(XML_Parser parser, const ENCODING *enc, const char *start, const char *end); + +static const XML_Char *getContext(XML_Parser parser); +static void normalizePublicId(XML_Char *s); +static int dtdInit(DTD *); +static void dtdDestroy(DTD *); +static void poolInit(STRING_POOL *); +static void poolClear(STRING_POOL *); +static void poolDestroy(STRING_POOL *); +static XML_Char *poolAppend(STRING_POOL *pool, const ENCODING *enc, + const char *ptr, const char *end); +static XML_Char *poolStoreString(STRING_POOL *pool, const ENCODING *enc, + const char *ptr, const char *end); +static int poolGrow(STRING_POOL *pool); +static const XML_Char *poolCopyString(STRING_POOL *pool, const XML_Char *s); +static void *XML_GetBuffer(XML_Parser parser, int len); +static int XML_ParseBuffer(XML_Parser parser, int len, int isFinal); + +#define poolStart(pool) ((pool)->start) +#define poolEnd(pool) ((pool)->ptr) +#define poolLength(pool) ((pool)->ptr - (pool)->start) +#define poolChop(pool) ((void)--(pool->ptr)) +#define poolLastChar(pool) (((pool)->ptr)[-1]) +#define poolDiscard(pool) ((pool)->ptr = (pool)->start) +#define poolFinish(pool) ((pool)->start = (pool)->ptr) +#define poolAppendChar(pool, c) \ + (((pool)->ptr == (pool)->end && !poolGrow(pool)) \ + ? 0 \ + : ((*((pool)->ptr)++ = c), 1)) + +typedef struct { + /* The first member must be userData so that the XML_GetUserData macro works. */ + void *m_userData; + void *m_handlerArg; + char *m_buffer; + /* first character to be parsed */ + const char *m_bufferPtr; + /* past last character to be parsed */ + char *m_bufferEnd; + /* allocated end of buffer */ + const char *m_bufferLim; + long m_parseEndByteIndex; + const char *m_parseEndPtr; + XML_Char *m_dataBuf; + XML_Char *m_dataBufEnd; + XML_StartElementHandler m_startElementHandler; + XML_EndElementHandler m_endElementHandler; + XML_CharacterDataHandler m_characterDataHandler; + XML_ProcessingInstructionHandler m_processingInstructionHandler; + XML_CommentHandler m_commentHandler; + XML_StartCdataSectionHandler m_startCdataSectionHandler; + XML_EndCdataSectionHandler m_endCdataSectionHandler; + XML_DefaultHandler m_defaultHandler; + XML_UnparsedEntityDeclHandler m_unparsedEntityDeclHandler; + XML_NotationDeclHandler m_notationDeclHandler; + XML_StartNamespaceDeclHandler m_startNamespaceDeclHandler; + XML_EndNamespaceDeclHandler m_endNamespaceDeclHandler; + XML_NotStandaloneHandler m_notStandaloneHandler; + XML_ExternalEntityRefHandler m_externalEntityRefHandler; + void *m_externalEntityRefHandlerArg; + XML_UnknownEncodingHandler m_unknownEncodingHandler; + const ENCODING *m_encoding; + INIT_ENCODING m_initEncoding; + const XML_Char *m_protocolEncodingName; + int m_ns; + void *m_unknownEncodingMem; + void *m_unknownEncodingData; + void *m_unknownEncodingHandlerData; + void (*m_unknownEncodingRelease)(void *); + PROLOG_STATE m_prologState; + Processor *m_processor; + enum XML_Error m_errorCode; + const char *m_eventPtr; + const char *m_eventEndPtr; + const char *m_positionPtr; + OPEN_INTERNAL_ENTITY *m_openInternalEntities; + int m_defaultExpandInternalEntities; + int m_tagLevel; + ENTITY *m_declEntity; + const XML_Char *m_declNotationName; + const XML_Char *m_declNotationPublicId; + ELEMENT_TYPE *m_declElementType; + ATTRIBUTE_ID *m_declAttributeId; + char m_declAttributeIsCdata; + DTD m_dtd; + TAG *m_tagStack; + TAG *m_freeTagList; + BINDING *m_inheritedBindings; + BINDING *m_freeBindingList; + int m_attsSize; + int m_nSpecifiedAtts; + ATTRIBUTE *m_atts; + POSITION m_position; + STRING_POOL m_tempPool; + STRING_POOL m_temp2Pool; + char *m_groupConnector; + unsigned m_groupSize; + int m_hadExternalDoctype; + XML_Char m_namespaceSeparator; +} Parser; + +#define userData (((Parser *)parser)->m_userData) +#define handlerArg (((Parser *)parser)->m_handlerArg) +#define startElementHandler (((Parser *)parser)->m_startElementHandler) +#define endElementHandler (((Parser *)parser)->m_endElementHandler) +#define characterDataHandler (((Parser *)parser)->m_characterDataHandler) +#define processingInstructionHandler (((Parser *)parser)->m_processingInstructionHandler) +#define commentHandler (((Parser *)parser)->m_commentHandler) +#define startCdataSectionHandler (((Parser *)parser)->m_startCdataSectionHandler) +#define endCdataSectionHandler (((Parser *)parser)->m_endCdataSectionHandler) +#define defaultHandler (((Parser *)parser)->m_defaultHandler) +#define unparsedEntityDeclHandler (((Parser *)parser)->m_unparsedEntityDeclHandler) +#define notationDeclHandler (((Parser *)parser)->m_notationDeclHandler) +#define startNamespaceDeclHandler (((Parser *)parser)->m_startNamespaceDeclHandler) +#define endNamespaceDeclHandler (((Parser *)parser)->m_endNamespaceDeclHandler) +#define notStandaloneHandler (((Parser *)parser)->m_notStandaloneHandler) +#define externalEntityRefHandler (((Parser *)parser)->m_externalEntityRefHandler) +#define externalEntityRefHandlerArg (((Parser *)parser)->m_externalEntityRefHandlerArg) +#define unknownEncodingHandler (((Parser *)parser)->m_unknownEncodingHandler) +#define encoding (((Parser *)parser)->m_encoding) +#define initEncoding (((Parser *)parser)->m_initEncoding) +#define unknownEncodingMem (((Parser *)parser)->m_unknownEncodingMem) +#define unknownEncodingData (((Parser *)parser)->m_unknownEncodingData) +#define unknownEncodingHandlerData \ + (((Parser *)parser)->m_unknownEncodingHandlerData) +#define unknownEncodingRelease (((Parser *)parser)->m_unknownEncodingRelease) +#define protocolEncodingName (((Parser *)parser)->m_protocolEncodingName) +#define ns (((Parser *)parser)->m_ns) +#define prologState (((Parser *)parser)->m_prologState) +#define processor (((Parser *)parser)->m_processor) +#define errorCode (((Parser *)parser)->m_errorCode) +#define eventPtr (((Parser *)parser)->m_eventPtr) +#define eventEndPtr (((Parser *)parser)->m_eventEndPtr) +#define positionPtr (((Parser *)parser)->m_positionPtr) +#define position (((Parser *)parser)->m_position) +#define openInternalEntities (((Parser *)parser)->m_openInternalEntities) +#define defaultExpandInternalEntities (((Parser *)parser)->m_defaultExpandInternalEntities) +#define tagLevel (((Parser *)parser)->m_tagLevel) +#define buffer (((Parser *)parser)->m_buffer) +#define bufferPtr (((Parser *)parser)->m_bufferPtr) +#define bufferEnd (((Parser *)parser)->m_bufferEnd) +#define parseEndByteIndex (((Parser *)parser)->m_parseEndByteIndex) +#define parseEndPtr (((Parser *)parser)->m_parseEndPtr) +#define bufferLim (((Parser *)parser)->m_bufferLim) +#define dataBuf (((Parser *)parser)->m_dataBuf) +#define dataBufEnd (((Parser *)parser)->m_dataBufEnd) +#define dtd (((Parser *)parser)->m_dtd) +#define declEntity (((Parser *)parser)->m_declEntity) +#define declNotationName (((Parser *)parser)->m_declNotationName) +#define declNotationPublicId (((Parser *)parser)->m_declNotationPublicId) +#define declElementType (((Parser *)parser)->m_declElementType) +#define declAttributeId (((Parser *)parser)->m_declAttributeId) +#define declAttributeIsCdata (((Parser *)parser)->m_declAttributeIsCdata) +#define freeTagList (((Parser *)parser)->m_freeTagList) +#define freeBindingList (((Parser *)parser)->m_freeBindingList) +#define inheritedBindings (((Parser *)parser)->m_inheritedBindings) +#define tagStack (((Parser *)parser)->m_tagStack) +#define atts (((Parser *)parser)->m_atts) +#define attsSize (((Parser *)parser)->m_attsSize) +#define nSpecifiedAtts (((Parser *)parser)->m_nSpecifiedAtts) +#define tempPool (((Parser *)parser)->m_tempPool) +#define temp2Pool (((Parser *)parser)->m_temp2Pool) +#define groupConnector (((Parser *)parser)->m_groupConnector) +#define groupSize (((Parser *)parser)->m_groupSize) +#define hadExternalDoctype (((Parser *)parser)->m_hadExternalDoctype) +#define namespaceSeparator (((Parser *)parser)->m_namespaceSeparator) + +#ifdef _MSC_VER +#ifdef _DEBUG +Parser *asParser(XML_Parser parser) +{ + return parser; +} +#endif +#endif + +XML_Parser XML_ParserCreate(const XML_Char *encodingName) +{ + XML_Parser parser = malloc(sizeof(Parser)); + if (!parser) + return parser; + processor = prologInitProcessor; + XmlPrologStateInit(&prologState); + userData = 0; + handlerArg = 0; + startElementHandler = 0; + endElementHandler = 0; + characterDataHandler = 0; + processingInstructionHandler = 0; + commentHandler = 0; + startCdataSectionHandler = 0; + endCdataSectionHandler = 0; + defaultHandler = 0; + unparsedEntityDeclHandler = 0; + notationDeclHandler = 0; + startNamespaceDeclHandler = 0; + endNamespaceDeclHandler = 0; + notStandaloneHandler = 0; + externalEntityRefHandler = 0; + externalEntityRefHandlerArg = parser; + unknownEncodingHandler = 0; + buffer = 0; + bufferPtr = 0; + bufferEnd = 0; + parseEndByteIndex = 0; + parseEndPtr = 0; + bufferLim = 0; + declElementType = 0; + declAttributeId = 0; + declEntity = 0; + declNotationName = 0; + declNotationPublicId = 0; + memset(&position, 0, sizeof(POSITION)); + errorCode = XML_ERROR_NONE; + eventPtr = 0; + eventEndPtr = 0; + positionPtr = 0; + openInternalEntities = 0; + tagLevel = 0; + tagStack = 0; + freeTagList = 0; + freeBindingList = 0; + inheritedBindings = 0; + attsSize = INIT_ATTS_SIZE; + atts = malloc(attsSize * sizeof(ATTRIBUTE)); + nSpecifiedAtts = 0; + dataBuf = malloc(INIT_DATA_BUF_SIZE * sizeof(XML_Char)); + groupSize = 0; + groupConnector = 0; + hadExternalDoctype = 0; + unknownEncodingMem = 0; + unknownEncodingRelease = 0; + unknownEncodingData = 0; + unknownEncodingHandlerData = 0; + namespaceSeparator = '!'; + ns = 0; + poolInit(&tempPool); + poolInit(&temp2Pool); + protocolEncodingName = encodingName ? poolCopyString(&tempPool, encodingName) : 0; + if (!dtdInit(&dtd) || !atts || !dataBuf + || (encodingName && !protocolEncodingName)) { + XML_ParserFree(parser); + return 0; + } + dataBufEnd = dataBuf + INIT_DATA_BUF_SIZE; + XmlInitEncoding(&initEncoding, &encoding, 0); + return parser; +} + +static +void destroyBindings(BINDING *bindings) +{ + for (;;) { + BINDING *b = bindings; + if (!b) + break; + bindings = b->nextTagBinding; + g_free(b->uri); + g_free(b); + } +} + +void XML_ParserFree(XML_Parser parser) +{ + for (;;) { + TAG *p; + if (tagStack == 0) { + if (freeTagList == 0) + break; + tagStack = freeTagList; + freeTagList = 0; + } + p = tagStack; + tagStack = tagStack->parent; + g_free(p->buf); + destroyBindings(p->bindings); + g_free(p); + } + destroyBindings(freeBindingList); + destroyBindings(inheritedBindings); + poolDestroy(&tempPool); + poolDestroy(&temp2Pool); + dtdDestroy(&dtd); + g_free((void *)atts); + g_free(groupConnector); + g_free(buffer); + g_free(dataBuf); + g_free(unknownEncodingMem); + if (unknownEncodingRelease) + unknownEncodingRelease(unknownEncodingData); + g_free(parser); +} + +void XML_SetUserData(XML_Parser parser, void *p) +{ + if (handlerArg == userData) + handlerArg = userData = p; + else + userData = p; +} + +void XML_SetElementHandler(XML_Parser parser, + XML_StartElementHandler start, + XML_EndElementHandler end) +{ + startElementHandler = start; + endElementHandler = end; +} + +void XML_SetCharacterDataHandler(XML_Parser parser, + XML_CharacterDataHandler handler) +{ + characterDataHandler = handler; +} + +int XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) +{ + if (len == 0) { + if (!isFinal) + return 1; + positionPtr = bufferPtr; + errorCode = processor(parser, bufferPtr, parseEndPtr = bufferEnd, 0); + if (errorCode == XML_ERROR_NONE) + return 1; + eventEndPtr = eventPtr; + return 0; + } + else if (bufferPtr == bufferEnd) { + const char *end; + int nLeftOver; + parseEndByteIndex += len; + positionPtr = s; + if (isFinal) { + errorCode = processor(parser, s, parseEndPtr = s + len, 0); + if (errorCode == XML_ERROR_NONE) + return 1; + eventEndPtr = eventPtr; + return 0; + } + errorCode = processor(parser, s, parseEndPtr = s + len, &end); + if (errorCode != XML_ERROR_NONE) { + eventEndPtr = eventPtr; + return 0; + } + XmlUpdatePosition(encoding, positionPtr, end, &position); + nLeftOver = s + len - end; + if (nLeftOver) { + if (buffer == 0 || nLeftOver > bufferLim - buffer) { + /* FIXME avoid integer overflow */ + buffer = buffer == 0 ? malloc(len * 2) : realloc(buffer, len * 2); + if (!buffer) { + errorCode = XML_ERROR_NO_MEMORY; + eventPtr = eventEndPtr = 0; + return 0; + } + bufferLim = buffer + len * 2; + } + memcpy(buffer, end, nLeftOver); + bufferPtr = buffer; + bufferEnd = buffer + nLeftOver; + } + return 1; + } + else { + memcpy(XML_GetBuffer(parser, len), s, len); + return XML_ParseBuffer(parser, len, isFinal); + } +} + +static int XML_ParseBuffer(XML_Parser parser, int len, int isFinal) +{ + const char *start = bufferPtr; + positionPtr = start; + bufferEnd += len; + parseEndByteIndex += len; + errorCode = processor(parser, start, parseEndPtr = bufferEnd, + isFinal ? (const char **)0 : &bufferPtr); + if (errorCode == XML_ERROR_NONE) { + if (!isFinal) + XmlUpdatePosition(encoding, positionPtr, bufferPtr, &position); + return 1; + } + else { + eventEndPtr = eventPtr; + return 0; + } +} + +static void *XML_GetBuffer(XML_Parser parser, int len) +{ + if (len > bufferLim - bufferEnd) { + /* FIXME avoid integer overflow */ + int neededSize = len + (bufferEnd - bufferPtr); + if (neededSize <= bufferLim - buffer) { + memmove(buffer, bufferPtr, bufferEnd - bufferPtr); + bufferEnd = buffer + (bufferEnd - bufferPtr); + bufferPtr = buffer; + } + else { + char *newBuf; + int bufferSize = bufferLim - bufferPtr; + if (bufferSize == 0) + bufferSize = INIT_BUFFER_SIZE; + do { + bufferSize *= 2; + } while (bufferSize < neededSize); + newBuf = malloc(bufferSize); + if (newBuf == 0) { + errorCode = XML_ERROR_NO_MEMORY; + return 0; + } + bufferLim = newBuf + bufferSize; + if (bufferPtr) { + memcpy(newBuf, bufferPtr, bufferEnd - bufferPtr); + g_free(buffer); + } + bufferEnd = newBuf + (bufferEnd - bufferPtr); + bufferPtr = buffer = newBuf; + } + } + return bufferEnd; +} + +static +enum XML_Error contentProcessor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + return doContent(parser, 0, encoding, start, end, endPtr); +} + +static enum XML_Error +doContent(XML_Parser parser, + int startTagLevel, + const ENCODING *enc, + const char *s, + const char *end, + const char **nextPtr) +{ + const ENCODING *internalEnc = ns ? XmlGetInternalEncodingNS() : XmlGetInternalEncoding(); + const char **eventPP; + const char **eventEndPP; + if (enc == encoding) { + eventPP = &eventPtr; + eventEndPP = &eventEndPtr; + } + else { + eventPP = &(openInternalEntities->internalEventPtr); + eventEndPP = &(openInternalEntities->internalEventEndPtr); + } + *eventPP = s; + for (;;) { + const char *next = s; /* XmlContentTok doesn't always set the last arg */ + int tok = XmlContentTok(enc, s, end, &next); + *eventEndPP = next; + switch (tok) { + case XML_TOK_TRAILING_CR: + if (nextPtr) { + *nextPtr = s; + return XML_ERROR_NONE; + } + *eventEndPP = end; + if (characterDataHandler) { + XML_Char c = 0xA; + characterDataHandler(handlerArg, &c, 1); + } + else if (defaultHandler) + reportDefault(parser, enc, s, end); + if (startTagLevel == 0) + return XML_ERROR_NO_ELEMENTS; + if (tagLevel != startTagLevel) + return XML_ERROR_ASYNC_ENTITY; + return XML_ERROR_NONE; + case XML_TOK_NONE: + if (nextPtr) { + *nextPtr = s; + return XML_ERROR_NONE; + } + if (startTagLevel > 0) { + if (tagLevel != startTagLevel) + return XML_ERROR_ASYNC_ENTITY; + return XML_ERROR_NONE; + } + return XML_ERROR_NO_ELEMENTS; + case XML_TOK_INVALID: + *eventPP = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + if (nextPtr) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (nextPtr) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_ENTITY_REF: + { + const XML_Char *name; + ENTITY *entity; + XML_Char ch = XmlPredefinedEntityName(enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (ch) { + if (characterDataHandler) + characterDataHandler(handlerArg, &ch, 1); + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + } + name = poolStoreString(&dtd.pool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!name) + return XML_ERROR_NO_MEMORY; + entity = (ENTITY *)lookup(&dtd.generalEntities, name, 0); + poolDiscard(&dtd.pool); + if (!entity) { + if (dtd.complete || dtd.standalone) + return XML_ERROR_UNDEFINED_ENTITY; + if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + } + if (entity->open) + return XML_ERROR_RECURSIVE_ENTITY_REF; + if (entity->notation) + return XML_ERROR_BINARY_ENTITY_REF; + if (entity) { + if (entity->textPtr) { + enum XML_Error result; + OPEN_INTERNAL_ENTITY openEntity; + if (defaultHandler && !defaultExpandInternalEntities) { + reportDefault(parser, enc, s, next); + break; + } + entity->open = 1; + openEntity.next = openInternalEntities; + openInternalEntities = &openEntity; + openEntity.entity = entity; + openEntity.internalEventPtr = 0; + openEntity.internalEventEndPtr = 0; + result = doContent(parser, + tagLevel, + internalEnc, + (char *)entity->textPtr, + (char *)(entity->textPtr + entity->textLen), + 0); + entity->open = 0; + openInternalEntities = openEntity.next; + if (result) + return result; + } + else if (externalEntityRefHandler) { + const XML_Char *context; + entity->open = 1; + context = getContext(parser); + entity->open = 0; + if (!context) + return XML_ERROR_NO_MEMORY; + if (!externalEntityRefHandler(externalEntityRefHandlerArg, + context, + dtd.base, + entity->systemId, + entity->publicId)) + return XML_ERROR_EXTERNAL_ENTITY_HANDLING; + poolDiscard(&tempPool); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + } + break; + } + case XML_TOK_START_TAG_WITH_ATTS: + if (!startElementHandler) { + enum XML_Error result = storeAtts(parser, enc, s, 0, 0); + if (result) + return result; + } + /* fall through */ + case XML_TOK_START_TAG_NO_ATTS: + { + TAG *tag; + if (freeTagList) { + tag = freeTagList; + freeTagList = freeTagList->parent; + } + else { + tag = malloc(sizeof(TAG)); + if (!tag) + return XML_ERROR_NO_MEMORY; + tag->buf = malloc(INIT_TAG_BUF_SIZE); + if (!tag->buf) + return XML_ERROR_NO_MEMORY; + tag->bufEnd = tag->buf + INIT_TAG_BUF_SIZE; + } + tag->bindings = 0; + tag->parent = tagStack; + tagStack = tag; + tag->name.localPart = 0; + tag->rawName = s + enc->minBytesPerChar; + tag->rawNameLength = XmlNameLength(enc, tag->rawName); + if (nextPtr) { + /* Need to guarantee that: + tag->buf + ROUND_UP(tag->rawNameLength, sizeof(XML_Char)) <= tag->bufEnd - sizeof(XML_Char) */ + if (tag->rawNameLength + (int)(sizeof(XML_Char) - 1) + (int)sizeof(XML_Char) > tag->bufEnd - tag->buf) { + int bufSize = tag->rawNameLength * 4; + bufSize = ROUND_UP(bufSize, sizeof(XML_Char)); + tag->buf = realloc(tag->buf, bufSize); + if (!tag->buf) + return XML_ERROR_NO_MEMORY; + tag->bufEnd = tag->buf + bufSize; + } + memcpy(tag->buf, tag->rawName, tag->rawNameLength); + tag->rawName = tag->buf; + } + ++tagLevel; + if (startElementHandler) { + enum XML_Error result; + XML_Char *toPtr; + for (;;) { + const char *rawNameEnd = tag->rawName + tag->rawNameLength; + const char *fromPtr = tag->rawName; + int bufSize; + if (nextPtr) + toPtr = (XML_Char *)(tag->buf + ROUND_UP(tag->rawNameLength, sizeof(XML_Char))); + else + toPtr = (XML_Char *)tag->buf; + tag->name.str = toPtr; + XmlConvert(enc, + &fromPtr, rawNameEnd, + (ICHAR **)&toPtr, (ICHAR *)tag->bufEnd - 1); + if (fromPtr == rawNameEnd) + break; + bufSize = (tag->bufEnd - tag->buf) << 1; + tag->buf = realloc(tag->buf, bufSize); + if (!tag->buf) + return XML_ERROR_NO_MEMORY; + tag->bufEnd = tag->buf + bufSize; + if (nextPtr) + tag->rawName = tag->buf; + } + *toPtr = XML_T('\0'); + result = storeAtts(parser, enc, s, &(tag->name), &(tag->bindings)); + if (result) + return result; + startElementHandler(handlerArg, tag->name.str, (const XML_Char **)atts); + poolClear(&tempPool); + } + else { + tag->name.str = 0; + if (defaultHandler) + reportDefault(parser, enc, s, next); + } + break; + } + case XML_TOK_EMPTY_ELEMENT_WITH_ATTS: + if (!startElementHandler) { + enum XML_Error result = storeAtts(parser, enc, s, 0, 0); + if (result) + return result; + } + /* fall through */ + case XML_TOK_EMPTY_ELEMENT_NO_ATTS: + if (startElementHandler || endElementHandler) { + const char *rawName = s + enc->minBytesPerChar; + enum XML_Error result; + BINDING *bindings = 0; + TAG_NAME name; + name.str = poolStoreString(&tempPool, enc, rawName, + rawName + XmlNameLength(enc, rawName)); + if (!name.str) + return XML_ERROR_NO_MEMORY; + poolFinish(&tempPool); + result = storeAtts(parser, enc, s, &name, &bindings); + if (result) + return result; + poolFinish(&tempPool); + if (startElementHandler) + startElementHandler(handlerArg, name.str, (const XML_Char **)atts); + if (endElementHandler) { + if (startElementHandler) + *eventPP = *eventEndPP; + endElementHandler(handlerArg, name.str); + } + poolClear(&tempPool); + while (bindings) { + BINDING *b = bindings; + if (endNamespaceDeclHandler) + endNamespaceDeclHandler(handlerArg, b->prefix->name); + bindings = bindings->nextTagBinding; + b->nextTagBinding = freeBindingList; + freeBindingList = b; + b->prefix->binding = b->prevPrefixBinding; + } + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + if (tagLevel == 0) + return epilogProcessor(parser, next, end, nextPtr); + break; + case XML_TOK_END_TAG: + if (tagLevel == startTagLevel) + return XML_ERROR_ASYNC_ENTITY; + else { + int len; + const char *rawName; + TAG *tag = tagStack; + tagStack = tag->parent; + tag->parent = freeTagList; + freeTagList = tag; + rawName = s + enc->minBytesPerChar*2; + len = XmlNameLength(enc, rawName); + if (len != tag->rawNameLength + || memcmp(tag->rawName, rawName, len) != 0) { + *eventPP = rawName; + return XML_ERROR_TAG_MISMATCH; + } + --tagLevel; + if (endElementHandler && tag->name.str) { + if (tag->name.localPart) { + XML_Char *to = (XML_Char *)tag->name.str + tag->name.uriLen; + const XML_Char *from = tag->name.localPart; + while ((*to++ = *from++) != 0) + ; + } + endElementHandler(handlerArg, tag->name.str); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + while (tag->bindings) { + BINDING *b = tag->bindings; + if (endNamespaceDeclHandler) + endNamespaceDeclHandler(handlerArg, b->prefix->name); + tag->bindings = tag->bindings->nextTagBinding; + b->nextTagBinding = freeBindingList; + freeBindingList = b; + b->prefix->binding = b->prevPrefixBinding; + } + if (tagLevel == 0) + return epilogProcessor(parser, next, end, nextPtr); + } + break; + case XML_TOK_CHAR_REF: + { + int n = XmlCharRefNumber(enc, s); + if (n < 0) + return XML_ERROR_BAD_CHAR_REF; + if (characterDataHandler) { + XML_Char buf[XML_ENCODE_MAX]; + characterDataHandler(handlerArg, buf, XmlEncode(n, (ICHAR *)buf)); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + } + break; + case XML_TOK_XML_DECL: + return XML_ERROR_MISPLACED_XML_PI; + case XML_TOK_DATA_NEWLINE: + if (characterDataHandler) { + XML_Char c = 0xA; + characterDataHandler(handlerArg, &c, 1); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + case XML_TOK_CDATA_SECT_OPEN: + { + enum XML_Error result; + if (startCdataSectionHandler) + startCdataSectionHandler(handlerArg); +#if 0 + /* Suppose you doing a transformation on a document that involves + changing only the character data. You set up a defaultHandler + and a characterDataHandler. The defaultHandler simply copies + characters through. The characterDataHandler does the transformation + and writes the characters out escaping them as necessary. This case + will fail to work if we leave out the following two lines (because & + and < inside CDATA sections will be incorrectly escaped). + + However, now we have a start/endCdataSectionHandler, so it seems + easier to let the user deal with this. */ + + else if (characterDataHandler) + characterDataHandler(handlerArg, dataBuf, 0); +#endif + else if (defaultHandler) + reportDefault(parser, enc, s, next); + result = doCdataSection(parser, enc, &next, end, nextPtr); + if (!next) { + processor = cdataSectionProcessor; + return result; + } + } + break; + case XML_TOK_TRAILING_RSQB: + if (nextPtr) { + *nextPtr = s; + return XML_ERROR_NONE; + } + if (characterDataHandler) { + if (MUST_CONVERT(enc, s)) { + ICHAR *dataPtr = (ICHAR *)dataBuf; + XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)dataBufEnd); + characterDataHandler(handlerArg, dataBuf, dataPtr - (ICHAR *)dataBuf); + } + else + characterDataHandler(handlerArg, + (XML_Char *)s, + (XML_Char *)end - (XML_Char *)s); + } + else if (defaultHandler) + reportDefault(parser, enc, s, end); + if (startTagLevel == 0) { + *eventPP = end; + return XML_ERROR_NO_ELEMENTS; + } + if (tagLevel != startTagLevel) { + *eventPP = end; + return XML_ERROR_ASYNC_ENTITY; + } + return XML_ERROR_NONE; + case XML_TOK_DATA_CHARS: + if (characterDataHandler) { + if (MUST_CONVERT(enc, s)) { + for (;;) { + ICHAR *dataPtr = (ICHAR *)dataBuf; + XmlConvert(enc, &s, next, &dataPtr, (ICHAR *)dataBufEnd); + *eventEndPP = s; + characterDataHandler(handlerArg, dataBuf, dataPtr - (ICHAR *)dataBuf); + if (s == next) + break; + *eventPP = s; + } + } + else + characterDataHandler(handlerArg, + (XML_Char *)s, + (XML_Char *)next - (XML_Char *)s); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + case XML_TOK_PI: + if (!reportProcessingInstruction(parser, enc, s, next)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_COMMENT: + if (!reportComment(parser, enc, s, next)) + return XML_ERROR_NO_MEMORY; + break; + default: + if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + } + *eventPP = s = next; + } + /* not reached */ +} + +/* If tagNamePtr is non-null, build a real list of attributes, +otherwise just check the attributes for well-formedness. */ + +static enum XML_Error storeAtts(XML_Parser parser, const ENCODING *enc, + const char *s, TAG_NAME *tagNamePtr, + BINDING **bindingsPtr) +{ + ELEMENT_TYPE *elementType = 0; + int nDefaultAtts = 0; + const XML_Char **appAtts; + int attIndex = 0; + int i; + int n; + int nPrefixes = 0; + BINDING *binding; + const XML_Char *localPart; + + if (tagNamePtr) { + elementType = (ELEMENT_TYPE *)lookup(&dtd.elementTypes, tagNamePtr->str, 0); + if (!elementType) { + tagNamePtr->str = poolCopyString(&dtd.pool, tagNamePtr->str); + if (!tagNamePtr->str) + return XML_ERROR_NO_MEMORY; + elementType = (ELEMENT_TYPE *)lookup(&dtd.elementTypes, tagNamePtr->str, sizeof(ELEMENT_TYPE)); + if (!elementType) + return XML_ERROR_NO_MEMORY; + if (ns && !setElementTypePrefix(parser, elementType)) + return XML_ERROR_NO_MEMORY; + } + nDefaultAtts = elementType->nDefaultAtts; + } + n = XmlGetAttributes(enc, s, attsSize, atts); + if (n + nDefaultAtts > attsSize) { + int oldAttsSize = attsSize; + attsSize = n + nDefaultAtts + INIT_ATTS_SIZE; + atts = realloc((void *)atts, attsSize * sizeof(ATTRIBUTE)); + if (!atts) + return XML_ERROR_NO_MEMORY; + if (n > oldAttsSize) + XmlGetAttributes(enc, s, n, atts); + } + appAtts = (const XML_Char **)atts; + for (i = 0; i < n; i++) { + ATTRIBUTE_ID *attId = getAttributeId(parser, enc, atts[i].name, + atts[i].name + + XmlNameLength(enc, atts[i].name)); + if (!attId) + return XML_ERROR_NO_MEMORY; + if ((attId->name)[-1]) { + if (enc == encoding) + eventPtr = atts[i].name; + return XML_ERROR_DUPLICATE_ATTRIBUTE; + } + (attId->name)[-1] = 1; + appAtts[attIndex++] = attId->name; + if (!atts[i].normalized) { + enum XML_Error result; + int isCdata = 1; + + if (attId->maybeTokenized) { + int j; + for (j = 0; j < nDefaultAtts; j++) { + if (attId == elementType->defaultAtts[j].id) { + isCdata = elementType->defaultAtts[j].isCdata; + break; + } + } + } + + result = storeAttributeValue(parser, enc, isCdata, + atts[i].valuePtr, atts[i].valueEnd, + &tempPool); + if (result) + return result; + if (tagNamePtr) { + appAtts[attIndex] = poolStart(&tempPool); + poolFinish(&tempPool); + } + else + poolDiscard(&tempPool); + } + else if (tagNamePtr) { + appAtts[attIndex] = poolStoreString(&tempPool, enc, atts[i].valuePtr, atts[i].valueEnd); + if (appAtts[attIndex] == 0) + return XML_ERROR_NO_MEMORY; + poolFinish(&tempPool); + } + if (attId->prefix && tagNamePtr) { + if (attId->xmlns) { + if (!addBinding(parser, attId->prefix, attId, appAtts[attIndex], bindingsPtr)) + return XML_ERROR_NO_MEMORY; + --attIndex; + } + else { + attIndex++; + nPrefixes++; + (attId->name)[-1] = 2; + } + } + else + attIndex++; + } + nSpecifiedAtts = attIndex; + if (tagNamePtr) { + int j; + for (j = 0; j < nDefaultAtts; j++) { + const DEFAULT_ATTRIBUTE *da = elementType->defaultAtts + j; + if (!(da->id->name)[-1] && da->value) { + if (da->id->prefix) { + if (da->id->xmlns) { + if (!addBinding(parser, da->id->prefix, da->id, da->value, bindingsPtr)) + return XML_ERROR_NO_MEMORY; + } + else { + (da->id->name)[-1] = 2; + nPrefixes++; + appAtts[attIndex++] = da->id->name; + appAtts[attIndex++] = da->value; + } + } + else { + (da->id->name)[-1] = 1; + appAtts[attIndex++] = da->id->name; + appAtts[attIndex++] = da->value; + } + } + } + appAtts[attIndex] = 0; + } + i = 0; + if (nPrefixes) { + for (; i < attIndex; i += 2) { + if (appAtts[i][-1] == 2) { + ATTRIBUTE_ID *id; + ((XML_Char *)(appAtts[i]))[-1] = 0; + id = (ATTRIBUTE_ID *)lookup(&dtd.attributeIds, appAtts[i], 0); + if (id->prefix->binding) { + int j; + const BINDING *b = id->prefix->binding; + const XML_Char *s = appAtts[i]; + for (j = 0; j < b->uriLen; j++) { + if (!poolAppendChar(&tempPool, b->uri[j])) + return XML_ERROR_NO_MEMORY; + } + while (*s++ != ':') + ; + do { + if (!poolAppendChar(&tempPool, *s)) + return XML_ERROR_NO_MEMORY; + } while (*s++); + appAtts[i] = poolStart(&tempPool); + poolFinish(&tempPool); + } + if (!--nPrefixes) + break; + } + else + ((XML_Char *)(appAtts[i]))[-1] = 0; + } + } + for (; i < attIndex; i += 2) + ((XML_Char *)(appAtts[i]))[-1] = 0; + if (!tagNamePtr) + return XML_ERROR_NONE; + for (binding = *bindingsPtr; binding; binding = binding->nextTagBinding) + binding->attId->name[-1] = 0; + if (elementType->prefix) { + binding = elementType->prefix->binding; + if (!binding) + return XML_ERROR_NONE; + localPart = tagNamePtr->str; + while (*localPart++ != XML_T(':')) + ; + } + else if (dtd.defaultPrefix.binding) { + binding = dtd.defaultPrefix.binding; + localPart = tagNamePtr->str; + } + else + return XML_ERROR_NONE; + tagNamePtr->localPart = localPart; + tagNamePtr->uriLen = binding->uriLen; + i = binding->uriLen; + do { + if (i == binding->uriAlloc) { + binding->uri = realloc(binding->uri, binding->uriAlloc *= 2); + if (!binding->uri) + return XML_ERROR_NO_MEMORY; + } + binding->uri[i++] = *localPart; + } while (*localPart++); + tagNamePtr->str = binding->uri; + return XML_ERROR_NONE; +} + +static +int addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, const XML_Char *uri, BINDING **bindingsPtr) +{ + BINDING *b; + int len; + for (len = 0; uri[len]; len++) + ; + if (namespaceSeparator) + len++; + if (freeBindingList) { + b = freeBindingList; + if (len > b->uriAlloc) { + b->uri = realloc(b->uri, len + EXPAND_SPARE); + if (!b->uri) + return 0; + b->uriAlloc = len + EXPAND_SPARE; + } + freeBindingList = b->nextTagBinding; + } + else { + b = malloc(sizeof(BINDING)); + if (!b) + return 0; + b->uri = malloc(sizeof(XML_Char) * len + EXPAND_SPARE); + if (!b->uri) { + g_free(b); + return 0; + } + b->uriAlloc = len; + } + b->uriLen = len; + memcpy(b->uri, uri, len * sizeof(XML_Char)); + if (namespaceSeparator) + b->uri[len - 1] = namespaceSeparator; + b->prefix = prefix; + b->attId = attId; + b->prevPrefixBinding = prefix->binding; + if (*uri == XML_T('\0') && prefix == &dtd.defaultPrefix) + prefix->binding = 0; + else + prefix->binding = b; + b->nextTagBinding = *bindingsPtr; + *bindingsPtr = b; + if (startNamespaceDeclHandler) + startNamespaceDeclHandler(handlerArg, prefix->name, + prefix->binding ? uri : 0); + return 1; +} + +/* The idea here is to avoid using stack for each CDATA section when +the whole file is parsed with one call. */ + +static +enum XML_Error cdataSectionProcessor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + enum XML_Error result = doCdataSection(parser, encoding, &start, end, endPtr); + if (start) { + processor = contentProcessor; + return contentProcessor(parser, start, end, endPtr); + } + return result; +} + +/* startPtr gets set to non-null is the section is closed, and to null if +the section is not yet closed. */ + +static +enum XML_Error doCdataSection(XML_Parser parser, + const ENCODING *enc, + const char **startPtr, + const char *end, + const char **nextPtr) +{ + const char *s = *startPtr; + const char **eventPP; + const char **eventEndPP; + if (enc == encoding) { + eventPP = &eventPtr; + *eventPP = s; + eventEndPP = &eventEndPtr; + } + else { + eventPP = &(openInternalEntities->internalEventPtr); + eventEndPP = &(openInternalEntities->internalEventEndPtr); + } + *eventPP = s; + *startPtr = 0; + for (;;) { + const char *next; + int tok = XmlCdataSectionTok(enc, s, end, &next); + *eventEndPP = next; + switch (tok) { + case XML_TOK_CDATA_SECT_CLOSE: + if (endCdataSectionHandler) + endCdataSectionHandler(handlerArg); +#if 0 + /* see comment under XML_TOK_CDATA_SECT_OPEN */ + else if (characterDataHandler) + characterDataHandler(handlerArg, dataBuf, 0); +#endif + else if (defaultHandler) + reportDefault(parser, enc, s, next); + *startPtr = next; + return XML_ERROR_NONE; + case XML_TOK_DATA_NEWLINE: + if (characterDataHandler) { + XML_Char c = 0xA; + characterDataHandler(handlerArg, &c, 1); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + case XML_TOK_DATA_CHARS: + if (characterDataHandler) { + if (MUST_CONVERT(enc, s)) { + for (;;) { + ICHAR *dataPtr = (ICHAR *)dataBuf; + XmlConvert(enc, &s, next, &dataPtr, (ICHAR *)dataBufEnd); + *eventEndPP = next; + characterDataHandler(handlerArg, dataBuf, dataPtr - (ICHAR *)dataBuf); + if (s == next) + break; + *eventPP = s; + } + } + else + characterDataHandler(handlerArg, + (XML_Char *)s, + (XML_Char *)next - (XML_Char *)s); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + case XML_TOK_INVALID: + *eventPP = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (nextPtr) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_PARTIAL: + case XML_TOK_NONE: + if (nextPtr) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_UNCLOSED_CDATA_SECTION; + default: + abort(); + } + *eventPP = s = next; + } + /* not reached */ +} + +static enum XML_Error +initializeEncoding(XML_Parser parser) +{ + const char *s; +#ifdef XML_UNICODE + char encodingBuf[128]; + if (!protocolEncodingName) + s = 0; + else { + int i; + for (i = 0; protocolEncodingName[i]; i++) { + if (i == sizeof(encodingBuf) - 1 + || protocolEncodingName[i] >= 0x80 + || protocolEncodingName[i] < 0) { + encodingBuf[0] = '\0'; + break; + } + encodingBuf[i] = (char)protocolEncodingName[i]; + } + encodingBuf[i] = '\0'; + s = encodingBuf; + } +#else +s = protocolEncodingName; +#endif + if ((ns ? XmlInitEncodingNS : XmlInitEncoding)(&initEncoding, &encoding, s)) + return XML_ERROR_NONE; + return handleUnknownEncoding(parser, protocolEncodingName); +} + +static enum XML_Error +processXmlDecl(XML_Parser parser, int isGeneralTextEntity, + const char *s, const char *next) +{ + const char *encodingName = 0; + const ENCODING *newEncoding = 0; + const char *version; + int standalone = -1; + if (!(ns + ? XmlParseXmlDeclNS + : XmlParseXmlDecl)(isGeneralTextEntity, + encoding, + s, + next, + &eventPtr, + &version, + &encodingName, + &newEncoding, + &standalone)) + return XML_ERROR_SYNTAX; + if (!isGeneralTextEntity && standalone == 1) + dtd.standalone = 1; + if (defaultHandler) + reportDefault(parser, encoding, s, next); + if (!protocolEncodingName) { + if (newEncoding) { + if (newEncoding->minBytesPerChar != encoding->minBytesPerChar) { + eventPtr = encodingName; + return XML_ERROR_INCORRECT_ENCODING; + } + encoding = newEncoding; + } + else if (encodingName) { + enum XML_Error result; + const XML_Char *s = poolStoreString(&tempPool, + encoding, + encodingName, + encodingName + + XmlNameLength(encoding, encodingName)); + if (!s) + return XML_ERROR_NO_MEMORY; + result = handleUnknownEncoding(parser, s); + poolDiscard(&tempPool); + if (result == XML_ERROR_UNKNOWN_ENCODING) + eventPtr = encodingName; + return result; + } + } + return XML_ERROR_NONE; +} + +static enum XML_Error +handleUnknownEncoding(XML_Parser parser, const XML_Char *encodingName) +{ + if (unknownEncodingHandler) { + XML_Encoding info; + int i; + for (i = 0; i < 256; i++) + info.map[i] = -1; + info.convert = 0; + info.data = 0; + info.release = 0; + if (unknownEncodingHandler(unknownEncodingHandlerData, encodingName, &info)) { + ENCODING *enc; + unknownEncodingMem = malloc(XmlSizeOfUnknownEncoding()); + if (!unknownEncodingMem) { + if (info.release) + info.release(info.data); + return XML_ERROR_NO_MEMORY; + } + enc = (ns + ? XmlInitUnknownEncodingNS + : XmlInitUnknownEncoding)(unknownEncodingMem, + info.map, + info.convert, + info.data); + if (enc) { + unknownEncodingData = info.data; + unknownEncodingRelease = info.release; + encoding = enc; + return XML_ERROR_NONE; + } + } + if (info.release) + info.release(info.data); + } + return XML_ERROR_UNKNOWN_ENCODING; +} + +static enum XML_Error +prologInitProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + enum XML_Error result = initializeEncoding(parser); + if (result != XML_ERROR_NONE) + return result; + processor = prologProcessor; + return prologProcessor(parser, s, end, nextPtr); +} + +static enum XML_Error +prologProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + for (;;) { + const char *next; + int tok = XmlPrologTok(encoding, s, end, &next); + if (tok <= 0) { + if (nextPtr != 0 && tok != XML_TOK_INVALID) { + *nextPtr = s; + return XML_ERROR_NONE; + } + switch (tok) { + case XML_TOK_INVALID: + eventPtr = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_NONE: + return XML_ERROR_NO_ELEMENTS; + case XML_TOK_PARTIAL: + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_TRAILING_CR: + eventPtr = s + encoding->minBytesPerChar; + return XML_ERROR_NO_ELEMENTS; + default: + abort(); + } + } + switch (XmlTokenRole(&prologState, tok, s, next, encoding)) { + case XML_ROLE_XML_DECL: + { + enum XML_Error result = processXmlDecl(parser, 0, s, next); + if (result != XML_ERROR_NONE) + return result; + } + break; + case XML_ROLE_DOCTYPE_SYSTEM_ID: + if (!dtd.standalone + && notStandaloneHandler + && !notStandaloneHandler(handlerArg)) + return XML_ERROR_NOT_STANDALONE; + hadExternalDoctype = 1; + break; + case XML_ROLE_DOCTYPE_PUBLIC_ID: + case XML_ROLE_ENTITY_PUBLIC_ID: + if (!XmlIsPublicId(encoding, s, next, &eventPtr)) + return XML_ERROR_SYNTAX; + if (declEntity) { + XML_Char *tem = poolStoreString(&dtd.pool, + encoding, + s + encoding->minBytesPerChar, + next - encoding->minBytesPerChar); + if (!tem) + return XML_ERROR_NO_MEMORY; + normalizePublicId(tem); + declEntity->publicId = tem; + poolFinish(&dtd.pool); + } + break; + case XML_ROLE_INSTANCE_START: + processor = contentProcessor; + if (hadExternalDoctype) + dtd.complete = 0; + return contentProcessor(parser, s, end, nextPtr); + case XML_ROLE_ATTLIST_ELEMENT_NAME: + { + const XML_Char *name = poolStoreString(&dtd.pool, encoding, s, next); + if (!name) + return XML_ERROR_NO_MEMORY; + declElementType = (ELEMENT_TYPE *)lookup(&dtd.elementTypes, name, sizeof(ELEMENT_TYPE)); + if (!declElementType) + return XML_ERROR_NO_MEMORY; + if (declElementType->name != name) + poolDiscard(&dtd.pool); + else { + poolFinish(&dtd.pool); + if (!setElementTypePrefix(parser, declElementType)) + return XML_ERROR_NO_MEMORY; + } + break; + } + case XML_ROLE_ATTRIBUTE_NAME: + declAttributeId = getAttributeId(parser, encoding, s, next); + if (!declAttributeId) + return XML_ERROR_NO_MEMORY; + declAttributeIsCdata = 0; + break; + case XML_ROLE_ATTRIBUTE_TYPE_CDATA: + declAttributeIsCdata = 1; + break; + case XML_ROLE_IMPLIED_ATTRIBUTE_VALUE: + case XML_ROLE_REQUIRED_ATTRIBUTE_VALUE: + if (dtd.complete + && !defineAttribute(declElementType, declAttributeId, declAttributeIsCdata, 0)) + return XML_ERROR_NO_MEMORY; + break; + case XML_ROLE_DEFAULT_ATTRIBUTE_VALUE: + case XML_ROLE_FIXED_ATTRIBUTE_VALUE: + { + const XML_Char *attVal; + enum XML_Error result + = storeAttributeValue(parser, encoding, declAttributeIsCdata, + s + encoding->minBytesPerChar, + next - encoding->minBytesPerChar, + &dtd.pool); + if (result) + return result; + attVal = poolStart(&dtd.pool); + poolFinish(&dtd.pool); + if (dtd.complete + && !defineAttribute(declElementType, declAttributeId, declAttributeIsCdata, attVal)) + return XML_ERROR_NO_MEMORY; + break; + } + case XML_ROLE_ENTITY_VALUE: + { + enum XML_Error result = storeEntityValue(parser, s, next); + if (result != XML_ERROR_NONE) + return result; + } + break; + case XML_ROLE_ENTITY_SYSTEM_ID: + if (declEntity) { + declEntity->systemId = poolStoreString(&dtd.pool, encoding, + s + encoding->minBytesPerChar, + next - encoding->minBytesPerChar); + if (!declEntity->systemId) + return XML_ERROR_NO_MEMORY; + declEntity->base = dtd.base; + poolFinish(&dtd.pool); + } + break; + case XML_ROLE_ENTITY_NOTATION_NAME: + if (declEntity) { + declEntity->notation = poolStoreString(&dtd.pool, encoding, s, next); + if (!declEntity->notation) + return XML_ERROR_NO_MEMORY; + poolFinish(&dtd.pool); + if (unparsedEntityDeclHandler) { + eventPtr = eventEndPtr = s; + unparsedEntityDeclHandler(handlerArg, + declEntity->name, + declEntity->base, + declEntity->systemId, + declEntity->publicId, + declEntity->notation); + } + + } + break; + case XML_ROLE_GENERAL_ENTITY_NAME: + { + const XML_Char *name; + if (XmlPredefinedEntityName(encoding, s, next)) { + declEntity = 0; + break; + } + name = poolStoreString(&dtd.pool, encoding, s, next); + if (!name) + return XML_ERROR_NO_MEMORY; + if (dtd.complete) { + declEntity = (ENTITY *)lookup(&dtd.generalEntities, name, sizeof(ENTITY)); + if (!declEntity) + return XML_ERROR_NO_MEMORY; + if (declEntity->name != name) { + poolDiscard(&dtd.pool); + declEntity = 0; + } + else + poolFinish(&dtd.pool); + } + else { + poolDiscard(&dtd.pool); + declEntity = 0; + } + } + break; + case XML_ROLE_PARAM_ENTITY_NAME: + declEntity = 0; + break; + case XML_ROLE_NOTATION_NAME: + declNotationPublicId = 0; + declNotationName = 0; + if (notationDeclHandler) { + declNotationName = poolStoreString(&tempPool, encoding, s, next); + if (!declNotationName) + return XML_ERROR_NO_MEMORY; + poolFinish(&tempPool); + } + break; + case XML_ROLE_NOTATION_PUBLIC_ID: + if (!XmlIsPublicId(encoding, s, next, &eventPtr)) + return XML_ERROR_SYNTAX; + if (declNotationName) { + XML_Char *tem = poolStoreString(&tempPool, + encoding, + s + encoding->minBytesPerChar, + next - encoding->minBytesPerChar); + if (!tem) + return XML_ERROR_NO_MEMORY; + normalizePublicId(tem); + declNotationPublicId = tem; + poolFinish(&tempPool); + } + break; + case XML_ROLE_NOTATION_SYSTEM_ID: + if (declNotationName && notationDeclHandler) { + const XML_Char *systemId + = poolStoreString(&tempPool, encoding, + s + encoding->minBytesPerChar, + next - encoding->minBytesPerChar); + if (!systemId) + return XML_ERROR_NO_MEMORY; + eventPtr = eventEndPtr = s; + notationDeclHandler(handlerArg, + declNotationName, + dtd.base, + systemId, + declNotationPublicId); + } + poolClear(&tempPool); + break; + case XML_ROLE_NOTATION_NO_SYSTEM_ID: + if (declNotationPublicId && notationDeclHandler) { + eventPtr = eventEndPtr = s; + notationDeclHandler(handlerArg, + declNotationName, + dtd.base, + 0, + declNotationPublicId); + } + poolClear(&tempPool); + break; + case XML_ROLE_ERROR: + eventPtr = s; + switch (tok) { + case XML_TOK_PARAM_ENTITY_REF: + return XML_ERROR_PARAM_ENTITY_REF; + case XML_TOK_XML_DECL: + return XML_ERROR_MISPLACED_XML_PI; + default: + return XML_ERROR_SYNTAX; + } + case XML_ROLE_GROUP_OPEN: + if (prologState.level >= groupSize) { + if (groupSize) + groupConnector = realloc(groupConnector, groupSize *= 2); + else + groupConnector = malloc(groupSize = 32); + if (!groupConnector) + return XML_ERROR_NO_MEMORY; + } + groupConnector[prologState.level] = 0; + break; + case XML_ROLE_GROUP_SEQUENCE: + if (groupConnector[prologState.level] == '|') { + eventPtr = s; + return XML_ERROR_SYNTAX; + } + groupConnector[prologState.level] = ','; + break; + case XML_ROLE_GROUP_CHOICE: + if (groupConnector[prologState.level] == ',') { + eventPtr = s; + return XML_ERROR_SYNTAX; + } + groupConnector[prologState.level] = '|'; + break; + case XML_ROLE_PARAM_ENTITY_REF: + if (!dtd.standalone + && notStandaloneHandler + && !notStandaloneHandler(handlerArg)) + return XML_ERROR_NOT_STANDALONE; + dtd.complete = 0; + break; + case XML_ROLE_NONE: + switch (tok) { + case XML_TOK_PI: + eventPtr = s; + eventEndPtr = next; + if (!reportProcessingInstruction(parser, encoding, s, next)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_COMMENT: + eventPtr = s; + eventEndPtr = next; + if (!reportComment(parser, encoding, s, next)) + return XML_ERROR_NO_MEMORY; + break; + } + break; + } + if (defaultHandler) { + switch (tok) { + case XML_TOK_PI: + case XML_TOK_COMMENT: + case XML_TOK_BOM: + case XML_TOK_XML_DECL: + break; + default: + eventPtr = s; + eventEndPtr = next; + reportDefault(parser, encoding, s, next); + } + } + s = next; + } + /* not reached */ +} + +static +enum XML_Error epilogProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + processor = epilogProcessor; + eventPtr = s; + for (;;) { + const char *next; + int tok = XmlPrologTok(encoding, s, end, &next); + eventEndPtr = next; + switch (tok) { + case XML_TOK_TRAILING_CR: + if (defaultHandler) { + eventEndPtr = end; + reportDefault(parser, encoding, s, end); + } + /* fall through */ + case XML_TOK_NONE: + if (nextPtr) + *nextPtr = end; + return XML_ERROR_NONE; + case XML_TOK_PROLOG_S: + if (defaultHandler) + reportDefault(parser, encoding, s, next); + break; + case XML_TOK_PI: + if (!reportProcessingInstruction(parser, encoding, s, next)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_COMMENT: + if (!reportComment(parser, encoding, s, next)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_INVALID: + eventPtr = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + if (nextPtr) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (nextPtr) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_PARTIAL_CHAR; + default: + return XML_ERROR_JUNK_AFTER_DOC_ELEMENT; + } + eventPtr = s = next; + } +} + +static enum XML_Error +storeAttributeValue(XML_Parser parser, const ENCODING *enc, int isCdata, + const char *ptr, const char *end, + STRING_POOL *pool) +{ + enum XML_Error result = appendAttributeValue(parser, enc, isCdata, ptr, end, pool); + if (result) + return result; + if (!isCdata && poolLength(pool) && poolLastChar(pool) == 0x20) + poolChop(pool); + if (!poolAppendChar(pool, XML_T('\0'))) + return XML_ERROR_NO_MEMORY; + return XML_ERROR_NONE; +} + +static enum XML_Error +appendAttributeValue(XML_Parser parser, const ENCODING *enc, int isCdata, + const char *ptr, const char *end, + STRING_POOL *pool) +{ + const ENCODING *internalEnc = ns ? XmlGetInternalEncodingNS() : XmlGetInternalEncoding(); + for (;;) { + const char *next; + int tok = XmlAttributeValueTok(enc, ptr, end, &next); + switch (tok) { + case XML_TOK_NONE: + return XML_ERROR_NONE; + case XML_TOK_INVALID: + if (enc == encoding) + eventPtr = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_CHAR_REF: + { + XML_Char buf[XML_ENCODE_MAX]; + int i; + int n = XmlCharRefNumber(enc, ptr); + if (n < 0) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_BAD_CHAR_REF; + } + if (!isCdata + && n == 0x20 /* space */ + && (poolLength(pool) == 0 || poolLastChar(pool) == 0x20)) + break; + n = XmlEncode(n, (ICHAR *)buf); + if (!n) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_BAD_CHAR_REF; + } + for (i = 0; i < n; i++) { + if (!poolAppendChar(pool, buf[i])) + return XML_ERROR_NO_MEMORY; + } + } + break; + case XML_TOK_DATA_CHARS: + if (!poolAppend(pool, enc, ptr, next)) + return XML_ERROR_NO_MEMORY; + break; + break; + case XML_TOK_TRAILING_CR: + next = ptr + enc->minBytesPerChar; + /* fall through */ + case XML_TOK_ATTRIBUTE_VALUE_S: + case XML_TOK_DATA_NEWLINE: + if (!isCdata && (poolLength(pool) == 0 || poolLastChar(pool) == 0x20)) + break; + if (!poolAppendChar(pool, 0x20)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_ENTITY_REF: + { + const XML_Char *name; + ENTITY *entity; + XML_Char ch = XmlPredefinedEntityName(enc, + ptr + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (ch) { + if (!poolAppendChar(pool, ch)) + return XML_ERROR_NO_MEMORY; + break; + } + name = poolStoreString(&temp2Pool, enc, + ptr + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!name) + return XML_ERROR_NO_MEMORY; + entity = (ENTITY *)lookup(&dtd.generalEntities, name, 0); + poolDiscard(&temp2Pool); + if (!entity) { + if (dtd.complete) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_UNDEFINED_ENTITY; + } + } + else if (entity->open) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_RECURSIVE_ENTITY_REF; + } + else if (entity->notation) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_BINARY_ENTITY_REF; + } + else if (!entity->textPtr) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF; + } + else { + enum XML_Error result; + const XML_Char *textEnd = entity->textPtr + entity->textLen; + entity->open = 1; + result = appendAttributeValue(parser, internalEnc, isCdata, (char *)entity->textPtr, (char *)textEnd, pool); + entity->open = 0; + if (result) + return result; + } + } + break; + default: + abort(); + } + ptr = next; + } + /* not reached */ +} + +static +enum XML_Error storeEntityValue(XML_Parser parser, + const char *entityTextPtr, + const char *entityTextEnd) +{ + const ENCODING *internalEnc; + STRING_POOL *pool = &(dtd.pool); + entityTextPtr += encoding->minBytesPerChar; + entityTextEnd -= encoding->minBytesPerChar; + internalEnc = ns ? XmlGetInternalEncodingNS() : XmlGetInternalEncoding(); + for (;;) { + const char *next; + int tok = XmlEntityValueTok(encoding, entityTextPtr, entityTextEnd, &next); + switch (tok) { + case XML_TOK_PARAM_ENTITY_REF: + eventPtr = entityTextPtr; + return XML_ERROR_SYNTAX; + case XML_TOK_NONE: + if (declEntity) { + declEntity->textPtr = pool->start; + declEntity->textLen = pool->ptr - pool->start; + poolFinish(pool); + } + else + poolDiscard(pool); + return XML_ERROR_NONE; + case XML_TOK_ENTITY_REF: + case XML_TOK_DATA_CHARS: + if (!poolAppend(pool, encoding, entityTextPtr, next)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_TRAILING_CR: + next = entityTextPtr + encoding->minBytesPerChar; + /* fall through */ + case XML_TOK_DATA_NEWLINE: + if (pool->end == pool->ptr && !poolGrow(pool)) + return XML_ERROR_NO_MEMORY; + *(pool->ptr)++ = 0xA; + break; + case XML_TOK_CHAR_REF: + { + XML_Char buf[XML_ENCODE_MAX]; + int i; + int n = XmlCharRefNumber(encoding, entityTextPtr); + if (n < 0) { + eventPtr = entityTextPtr; + return XML_ERROR_BAD_CHAR_REF; + } + n = XmlEncode(n, (ICHAR *)buf); + if (!n) { + eventPtr = entityTextPtr; + return XML_ERROR_BAD_CHAR_REF; + } + for (i = 0; i < n; i++) { + if (pool->end == pool->ptr && !poolGrow(pool)) + return XML_ERROR_NO_MEMORY; + *(pool->ptr)++ = buf[i]; + } + } + break; + case XML_TOK_PARTIAL: + eventPtr = entityTextPtr; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_INVALID: + eventPtr = next; + return XML_ERROR_INVALID_TOKEN; + default: + abort(); + } + entityTextPtr = next; + } + /* not reached */ +} + +static void +normalizeLines(XML_Char *s) +{ + XML_Char *p; + for (;; s++) { + if (*s == XML_T('\0')) + return; + if (*s == 0xD) + break; + } + p = s; + do { + if (*s == 0xD) { + *p++ = 0xA; + if (*++s == 0xA) + s++; + } + else + *p++ = *s++; + } while (*s); + *p = XML_T('\0'); +} + +static int +reportProcessingInstruction(XML_Parser parser, const ENCODING *enc, const char *start, const char *end) +{ + const XML_Char *target; + XML_Char *data; + const char *tem; + if (!processingInstructionHandler) { + if (defaultHandler) + reportDefault(parser, enc, start, end); + return 1; + } + start += enc->minBytesPerChar * 2; + tem = start + XmlNameLength(enc, start); + target = poolStoreString(&tempPool, enc, start, tem); + if (!target) + return 0; + poolFinish(&tempPool); + data = poolStoreString(&tempPool, enc, + XmlSkipS(enc, tem), + end - enc->minBytesPerChar*2); + if (!data) + return 0; + normalizeLines(data); + processingInstructionHandler(handlerArg, target, data); + poolClear(&tempPool); + return 1; +} + +static int +reportComment(XML_Parser parser, const ENCODING *enc, const char *start, const char *end) +{ + XML_Char *data; + if (!commentHandler) { + if (defaultHandler) + reportDefault(parser, enc, start, end); + return 1; + } + data = poolStoreString(&tempPool, + enc, + start + enc->minBytesPerChar * 4, + end - enc->minBytesPerChar * 3); + if (!data) + return 0; + normalizeLines(data); + commentHandler(handlerArg, data); + poolClear(&tempPool); + return 1; +} + +static void +reportDefault(XML_Parser parser, const ENCODING *enc, const char *s, const char *end) +{ + if (MUST_CONVERT(enc, s)) { + const char **eventPP; + const char **eventEndPP; + if (enc == encoding) { + eventPP = &eventPtr; + eventEndPP = &eventEndPtr; + } + else { + eventPP = &(openInternalEntities->internalEventPtr); + eventEndPP = &(openInternalEntities->internalEventEndPtr); + } + do { + ICHAR *dataPtr = (ICHAR *)dataBuf; + XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)dataBufEnd); + *eventEndPP = s; + defaultHandler(handlerArg, dataBuf, dataPtr - (ICHAR *)dataBuf); + *eventPP = s; + } while (s != end); + } + else + defaultHandler(handlerArg, (XML_Char *)s, (XML_Char *)end - (XML_Char *)s); +} + + +static int +defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId, int isCdata, const XML_Char *value) +{ + DEFAULT_ATTRIBUTE *att; + if (type->nDefaultAtts == type->allocDefaultAtts) { + if (type->allocDefaultAtts == 0) { + type->allocDefaultAtts = 8; + type->defaultAtts = malloc(type->allocDefaultAtts*sizeof(DEFAULT_ATTRIBUTE)); + } + else { + type->allocDefaultAtts *= 2; + type->defaultAtts = realloc(type->defaultAtts, + type->allocDefaultAtts*sizeof(DEFAULT_ATTRIBUTE)); + } + if (!type->defaultAtts) + return 0; + } + att = type->defaultAtts + type->nDefaultAtts; + att->id = attId; + att->value = value; + att->isCdata = isCdata; + if (!isCdata) + attId->maybeTokenized = 1; + type->nDefaultAtts += 1; + return 1; +} + +static int setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *elementType) +{ + const XML_Char *name; + for (name = elementType->name; *name; name++) { + if (*name == XML_T(':')) { + PREFIX *prefix; + const XML_Char *s; + for (s = elementType->name; s != name; s++) { + if (!poolAppendChar(&dtd.pool, *s)) + return 0; + } + if (!poolAppendChar(&dtd.pool, XML_T('\0'))) + return 0; + prefix = (PREFIX *)lookup(&dtd.prefixes, poolStart(&dtd.pool), sizeof(PREFIX)); + if (!prefix) + return 0; + if (prefix->name == poolStart(&dtd.pool)) + poolFinish(&dtd.pool); + else + poolDiscard(&dtd.pool); + elementType->prefix = prefix; + + } + } + return 1; +} + +static ATTRIBUTE_ID * +getAttributeId(XML_Parser parser, const ENCODING *enc, const char *start, const char *end) +{ + ATTRIBUTE_ID *id; + const XML_Char *name; + if (!poolAppendChar(&dtd.pool, XML_T('\0'))) + return 0; + name = poolStoreString(&dtd.pool, enc, start, end); + if (!name) + return 0; + ++name; + id = (ATTRIBUTE_ID *)lookup(&dtd.attributeIds, name, sizeof(ATTRIBUTE_ID)); + if (!id) + return 0; + if (id->name != name) + poolDiscard(&dtd.pool); + else { + poolFinish(&dtd.pool); + if (!ns) + ; + else if (name[0] == 'x' + && name[1] == 'm' + && name[2] == 'l' + && name[3] == 'n' + && name[4] == 's' + && (name[5] == XML_T('\0') || name[5] == XML_T(':'))) { + if (name[5] == '\0') + id->prefix = &dtd.defaultPrefix; + else + id->prefix = (PREFIX *)lookup(&dtd.prefixes, name + 6, sizeof(PREFIX)); + id->xmlns = 1; + } + else { + int i; + for (i = 0; name[i]; i++) { + if (name[i] == XML_T(':')) { + int j; + for (j = 0; j < i; j++) { + if (!poolAppendChar(&dtd.pool, name[j])) + return 0; + } + if (!poolAppendChar(&dtd.pool, XML_T('\0'))) + return 0; + id->prefix = (PREFIX *)lookup(&dtd.prefixes, poolStart(&dtd.pool), sizeof(PREFIX)); + if (id->prefix->name == poolStart(&dtd.pool)) + poolFinish(&dtd.pool); + else + poolDiscard(&dtd.pool); + break; + } + } + } + } + return id; +} + +#define CONTEXT_SEP XML_T('\f') + +static +const XML_Char *getContext(XML_Parser parser) +{ + HASH_TABLE_ITER iter; + int needSep = 0; + + if (dtd.defaultPrefix.binding) { + int i; + int len; + if (!poolAppendChar(&tempPool, XML_T('='))) + return 0; + len = dtd.defaultPrefix.binding->uriLen; + if (namespaceSeparator != XML_T('\0')) + len--; + for (i = 0; i < len; i++) + if (!poolAppendChar(&tempPool, dtd.defaultPrefix.binding->uri[i])) + return 0; + needSep = 1; + } + + hashTableIterInit(&iter, &(dtd.prefixes)); + for (;;) { + int i; + int len; + const XML_Char *s; + PREFIX *prefix = (PREFIX *)hashTableIterNext(&iter); + if (!prefix) + break; + if (!prefix->binding) + continue; + if (needSep && !poolAppendChar(&tempPool, CONTEXT_SEP)) + return 0; + for (s = prefix->name; *s; s++) + if (!poolAppendChar(&tempPool, *s)) + return 0; + if (!poolAppendChar(&tempPool, XML_T('='))) + return 0; + len = prefix->binding->uriLen; + if (namespaceSeparator != XML_T('\0')) + len--; + for (i = 0; i < len; i++) + if (!poolAppendChar(&tempPool, prefix->binding->uri[i])) + return 0; + needSep = 1; + } + + + hashTableIterInit(&iter, &(dtd.generalEntities)); + for (;;) { + const XML_Char *s; + ENTITY *e = (ENTITY *)hashTableIterNext(&iter); + if (!e) + break; + if (!e->open) + continue; + if (needSep && !poolAppendChar(&tempPool, CONTEXT_SEP)) + return 0; + for (s = e->name; *s; s++) + if (!poolAppendChar(&tempPool, *s)) + return 0; + needSep = 1; + } + + if (!poolAppendChar(&tempPool, XML_T('\0'))) + return 0; + return tempPool.start; +} + +static +void normalizePublicId(XML_Char *publicId) +{ + XML_Char *p = publicId; + XML_Char *s; + for (s = publicId; *s; s++) { + switch (*s) { + case 0x20: + case 0xD: + case 0xA: + if (p != publicId && p[-1] != 0x20) + *p++ = 0x20; + break; + default: + *p++ = *s; + } + } + if (p != publicId && p[-1] == 0x20) + --p; + *p = XML_T('\0'); +} + +static int dtdInit(DTD *p) +{ + poolInit(&(p->pool)); + hashTableInit(&(p->generalEntities)); + hashTableInit(&(p->elementTypes)); + hashTableInit(&(p->attributeIds)); + hashTableInit(&(p->prefixes)); + p->complete = 1; + p->standalone = 0; + p->base = 0; + p->defaultPrefix.name = 0; + p->defaultPrefix.binding = 0; + return 1; +} + +static void dtdDestroy(DTD *p) +{ + HASH_TABLE_ITER iter; + hashTableIterInit(&iter, &(p->elementTypes)); + for (;;) { + ELEMENT_TYPE *e = (ELEMENT_TYPE *)hashTableIterNext(&iter); + if (!e) + break; + if (e->allocDefaultAtts != 0) + g_free(e->defaultAtts); + } + hashTableDestroy(&(p->generalEntities)); + hashTableDestroy(&(p->elementTypes)); + hashTableDestroy(&(p->attributeIds)); + hashTableDestroy(&(p->prefixes)); + poolDestroy(&(p->pool)); +} + +static +void poolInit(STRING_POOL *pool) +{ + pool->blocks = 0; + pool->freeBlocks = 0; + pool->start = 0; + pool->ptr = 0; + pool->end = 0; +} + +static +void poolClear(STRING_POOL *pool) +{ + if (!pool->freeBlocks) + pool->freeBlocks = pool->blocks; + else { + BLOCK *p = pool->blocks; + while (p) { + BLOCK *tem = p->next; + p->next = pool->freeBlocks; + pool->freeBlocks = p; + p = tem; + } + } + pool->blocks = 0; + pool->start = 0; + pool->ptr = 0; + pool->end = 0; +} + +static +void poolDestroy(STRING_POOL *pool) +{ + BLOCK *p = pool->blocks; + while (p) { + BLOCK *tem = p->next; + g_free(p); + p = tem; + } + pool->blocks = 0; + p = pool->freeBlocks; + while (p) { + BLOCK *tem = p->next; + g_free(p); + p = tem; + } + pool->freeBlocks = 0; + pool->ptr = 0; + pool->start = 0; + pool->end = 0; +} + +static +XML_Char *poolAppend(STRING_POOL *pool, const ENCODING *enc, + const char *ptr, const char *end) +{ + if (!pool->ptr && !poolGrow(pool)) + return 0; + for (;;) { + XmlConvert(enc, &ptr, end, (ICHAR **)&(pool->ptr), (ICHAR *)pool->end); + if (ptr == end) + break; + if (!poolGrow(pool)) + return 0; + } + return pool->start; +} + +static const XML_Char *poolCopyString(STRING_POOL *pool, const XML_Char *s) +{ + do { + if (!poolAppendChar(pool, *s)) + return 0; + } while (*s++); + s = pool->start; + poolFinish(pool); + return s; +} + +static +XML_Char *poolStoreString(STRING_POOL *pool, const ENCODING *enc, + const char *ptr, const char *end) +{ + if (!poolAppend(pool, enc, ptr, end)) + return 0; + if (pool->ptr == pool->end && !poolGrow(pool)) + return 0; + *(pool->ptr)++ = 0; + return pool->start; +} + +static +int poolGrow(STRING_POOL *pool) +{ + if (pool->freeBlocks) { + if (pool->start == 0) { + pool->blocks = pool->freeBlocks; + pool->freeBlocks = pool->freeBlocks->next; + pool->blocks->next = 0; + pool->start = pool->blocks->s; + pool->end = pool->start + pool->blocks->size; + pool->ptr = pool->start; + return 1; + } + if (pool->end - pool->start < pool->freeBlocks->size) { + BLOCK *tem = pool->freeBlocks->next; + pool->freeBlocks->next = pool->blocks; + pool->blocks = pool->freeBlocks; + pool->freeBlocks = tem; + memcpy(pool->blocks->s, pool->start, (pool->end - pool->start) * sizeof(XML_Char)); + pool->ptr = pool->blocks->s + (pool->ptr - pool->start); + pool->start = pool->blocks->s; + pool->end = pool->start + pool->blocks->size; + return 1; + } + } + if (pool->blocks && pool->start == pool->blocks->s) { + int blockSize = (pool->end - pool->start)*2; + pool->blocks = realloc(pool->blocks, offsetof(BLOCK, s) + blockSize * sizeof(XML_Char)); + if (!pool->blocks) + return 0; + pool->blocks->size = blockSize; + pool->ptr = pool->blocks->s + (pool->ptr - pool->start); + pool->start = pool->blocks->s; + pool->end = pool->start + blockSize; + } + else { + BLOCK *tem; + int blockSize = pool->end - pool->start; + if (blockSize < INIT_BLOCK_SIZE) + blockSize = INIT_BLOCK_SIZE; + else + blockSize *= 2; + tem = malloc(offsetof(BLOCK, s) + blockSize * sizeof(XML_Char)); + if (!tem) + return 0; + tem->size = blockSize; + tem->next = pool->blocks; + pool->blocks = tem; + memcpy(tem->s, pool->start, (pool->ptr - pool->start) * sizeof(XML_Char)); + pool->ptr = tem->s + (pool->ptr - pool->start); + pool->start = tem->s; + pool->end = tem->s + blockSize; + } + return 1; +} diff --git a/protocols/jabber/xmlparse.h b/protocols/jabber/xmlparse.h new file mode 100644 index 00000000..f39edb8c --- /dev/null +++ b/protocols/jabber/xmlparse.h @@ -0,0 +1,476 @@ +/* +The contents of this file are subject to the Mozilla Public License +Version 1.1 (the "License"); you may not use this file except in +compliance with the License. You may obtain a copy of the License at +http://www.mozilla.org/MPL/ + +Software distributed under the License is distributed on an "AS IS" +basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +License for the specific language governing rights and limitations +under the License. + +The Original Code is expat. + +The Initial Developer of the Original Code is James Clark. +Portions created by James Clark are Copyright (C) 1998, 1999 +James Clark. All Rights Reserved. + +Contributor(s): + +Alternatively, the contents of this file may be used under the terms +of the GNU General Public License (the "GPL"), in which case the +provisions of the GPL are applicable instead of those above. If you +wish to allow use of your version of this file only under the terms of +the GPL and not to allow others to use your version of this file under +the MPL, indicate your decision by deleting the provisions above and +replace them with the notice and other provisions required by the +GPL. If you do not delete the provisions above, a recipient may use +your version of this file under either the MPL or the GPL. +*/ + +#ifndef XmlParse_INCLUDED +#define XmlParse_INCLUDED 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef XMLPARSEAPI +#define XMLPARSEAPI /* as nothing */ +#endif + +typedef void *XML_Parser; + +#ifdef XML_UNICODE_WCHAR_T + +/* XML_UNICODE_WCHAR_T will work only if sizeof(wchar_t) == 2 and wchar_t +uses Unicode. */ +/* Information is UTF-16 encoded as wchar_ts */ + +#ifndef XML_UNICODE +#define XML_UNICODE +#endif + +#include <stddef.h> +typedef wchar_t XML_Char; +typedef wchar_t XML_LChar; + +#else /* not XML_UNICODE_WCHAR_T */ + +#ifdef XML_UNICODE + +/* Information is UTF-16 encoded as unsigned shorts */ +typedef unsigned short XML_Char; +typedef char XML_LChar; + +#else /* not XML_UNICODE */ + +/* Information is UTF-8 encoded. */ +typedef char XML_Char; +typedef char XML_LChar; + +#endif /* not XML_UNICODE */ + +#endif /* not XML_UNICODE_WCHAR_T */ + + +/* Constructs a new parser; encoding is the encoding specified by the external +protocol or null if there is none specified. */ + +XML_Parser XMLPARSEAPI +XML_ParserCreate(const XML_Char *encoding); + +/* Constructs a new parser and namespace processor. Element type names +and attribute names that belong to a namespace will be expanded; +unprefixed attribute names are never expanded; unprefixed element type +names are expanded only if there is a default namespace. The expanded +name is the concatenation of the namespace URI, the namespace separator character, +and the local part of the name. If the namespace separator is '\0' then +the namespace URI and the local part will be concatenated without any +separator. When a namespace is not declared, the name and prefix will be +passed through without expansion. */ + +XML_Parser XMLPARSEAPI +XML_ParserCreateNS(const XML_Char *encoding, XML_Char namespaceSeparator); + + +/* atts is array of name/value pairs, terminated by 0; + names and values are 0 terminated. */ + +typedef void (*XML_StartElementHandler)(void *userData, + const XML_Char *name, + const XML_Char **atts); + +typedef void (*XML_EndElementHandler)(void *userData, + const XML_Char *name); + +/* s is not 0 terminated. */ +typedef void (*XML_CharacterDataHandler)(void *userData, + const XML_Char *s, + int len); + +/* target and data are 0 terminated */ +typedef void (*XML_ProcessingInstructionHandler)(void *userData, + const XML_Char *target, + const XML_Char *data); + +/* data is 0 terminated */ +typedef void (*XML_CommentHandler)(void *userData, const XML_Char *data); + +typedef void (*XML_StartCdataSectionHandler)(void *userData); +typedef void (*XML_EndCdataSectionHandler)(void *userData); + +/* This is called for any characters in the XML document for +which there is no applicable handler. This includes both +characters that are part of markup which is of a kind that is +not reported (comments, markup declarations), or characters +that are part of a construct which could be reported but +for which no handler has been supplied. The characters are passed +exactly as they were in the XML document except that +they will be encoded in UTF-8. Line boundaries are not normalized. +Note that a byte order mark character is not passed to the default handler. +There are no guarantees about how characters are divided between calls +to the default handler: for example, a comment might be split between +multiple calls. */ + +typedef void (*XML_DefaultHandler)(void *userData, + const XML_Char *s, + int len); + +/* This is called for a declaration of an unparsed (NDATA) +entity. The base argument is whatever was set by XML_SetBase. +The entityName, systemId and notationName arguments will never be null. +The other arguments may be. */ + +typedef void (*XML_UnparsedEntityDeclHandler)(void *userData, + const XML_Char *entityName, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId, + const XML_Char *notationName); + +/* This is called for a declaration of notation. +The base argument is whatever was set by XML_SetBase. +The notationName will never be null. The other arguments can be. */ + +typedef void (*XML_NotationDeclHandler)(void *userData, + const XML_Char *notationName, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId); + +/* When namespace processing is enabled, these are called once for +each namespace declaration. The call to the start and end element +handlers occur between the calls to the start and end namespace +declaration handlers. For an xmlns attribute, prefix will be null. +For an xmlns="" attribute, uri will be null. */ + +typedef void (*XML_StartNamespaceDeclHandler)(void *userData, + const XML_Char *prefix, + const XML_Char *uri); + +typedef void (*XML_EndNamespaceDeclHandler)(void *userData, + const XML_Char *prefix); + +/* This is called if the document is not standalone (it has an +external subset or a reference to a parameter entity, but does not +have standalone="yes"). If this handler returns 0, then processing +will not continue, and the parser will return a +XML_ERROR_NOT_STANDALONE error. */ + +typedef int (*XML_NotStandaloneHandler)(void *userData); + +/* This is called for a reference to an external parsed general entity. +The referenced entity is not automatically parsed. +The application can parse it immediately or later using +XML_ExternalEntityParserCreate. +The parser argument is the parser parsing the entity containing the reference; +it can be passed as the parser argument to XML_ExternalEntityParserCreate. +The systemId argument is the system identifier as specified in the entity declaration; +it will not be null. +The base argument is the system identifier that should be used as the base for +resolving systemId if systemId was relative; this is set by XML_SetBase; +it may be null. +The publicId argument is the public identifier as specified in the entity declaration, +or null if none was specified; the whitespace in the public identifier +will have been normalized as required by the XML spec. +The context argument specifies the parsing context in the format +expected by the context argument to +XML_ExternalEntityParserCreate; context is valid only until the handler +returns, so if the referenced entity is to be parsed later, it must be copied. +The handler should return 0 if processing should not continue because of +a fatal error in the handling of the external entity. +In this case the calling parser will return an XML_ERROR_EXTERNAL_ENTITY_HANDLING +error. +Note that unlike other handlers the first argument is the parser, not userData. */ + +typedef int (*XML_ExternalEntityRefHandler)(XML_Parser parser, + const XML_Char *context, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId); + +/* This structure is filled in by the XML_UnknownEncodingHandler +to provide information to the parser about encodings that are unknown +to the parser. +The map[b] member gives information about byte sequences +whose first byte is b. +If map[b] is c where c is >= 0, then b by itself encodes the Unicode scalar value c. +If map[b] is -1, then the byte sequence is malformed. +If map[b] is -n, where n >= 2, then b is the first byte of an n-byte +sequence that encodes a single Unicode scalar value. +The data member will be passed as the first argument to the convert function. +The convert function is used to convert multibyte sequences; +s will point to a n-byte sequence where map[(unsigned char)*s] == -n. +The convert function must return the Unicode scalar value +represented by this byte sequence or -1 if the byte sequence is malformed. +The convert function may be null if the encoding is a single-byte encoding, +that is if map[b] >= -1 for all bytes b. +When the parser is finished with the encoding, then if release is not null, +it will call release passing it the data member; +once release has been called, the convert function will not be called again. + +Expat places certain restrictions on the encodings that are supported +using this mechanism. + +1. Every ASCII character that can appear in a well-formed XML document, +other than the characters + + $@\^`{}~ + +must be represented by a single byte, and that byte must be the +same byte that represents that character in ASCII. + +2. No character may require more than 4 bytes to encode. + +3. All characters encoded must have Unicode scalar values <= 0xFFFF, +(ie characters that would be encoded by surrogates in UTF-16 +are not allowed). Note that this restriction doesn't apply to +the built-in support for UTF-8 and UTF-16. + +4. No Unicode character may be encoded by more than one distinct sequence +of bytes. */ + +typedef struct { + int map[256]; + void *data; + int (*convert)(void *data, const char *s); + void (*release)(void *data); +} XML_Encoding; + +/* This is called for an encoding that is unknown to the parser. +The encodingHandlerData argument is that which was passed as the +second argument to XML_SetUnknownEncodingHandler. +The name argument gives the name of the encoding as specified in +the encoding declaration. +If the callback can provide information about the encoding, +it must fill in the XML_Encoding structure, and return 1. +Otherwise it must return 0. +If info does not describe a suitable encoding, +then the parser will return an XML_UNKNOWN_ENCODING error. */ + +typedef int (*XML_UnknownEncodingHandler)(void *encodingHandlerData, + const XML_Char *name, + XML_Encoding *info); + +void XMLPARSEAPI +XML_SetElementHandler(XML_Parser parser, + XML_StartElementHandler start, + XML_EndElementHandler end); + +void XMLPARSEAPI +XML_SetCharacterDataHandler(XML_Parser parser, + XML_CharacterDataHandler handler); + +void XMLPARSEAPI +XML_SetProcessingInstructionHandler(XML_Parser parser, + XML_ProcessingInstructionHandler handler); +void XMLPARSEAPI +XML_SetCommentHandler(XML_Parser parser, + XML_CommentHandler handler); + +void XMLPARSEAPI +XML_SetCdataSectionHandler(XML_Parser parser, + XML_StartCdataSectionHandler start, + XML_EndCdataSectionHandler end); + +/* This sets the default handler and also inhibits expansion of internal entities. +The entity reference will be passed to the default handler. */ + +void XMLPARSEAPI +XML_SetDefaultHandler(XML_Parser parser, + XML_DefaultHandler handler); + +/* This sets the default handler but does not inhibit expansion of internal entities. +The entity reference will not be passed to the default handler. */ + +void XMLPARSEAPI +XML_SetDefaultHandlerExpand(XML_Parser parser, + XML_DefaultHandler handler); + +void XMLPARSEAPI +XML_SetUnparsedEntityDeclHandler(XML_Parser parser, + XML_UnparsedEntityDeclHandler handler); + +void XMLPARSEAPI +XML_SetNotationDeclHandler(XML_Parser parser, + XML_NotationDeclHandler handler); + +void XMLPARSEAPI +XML_SetNamespaceDeclHandler(XML_Parser parser, + XML_StartNamespaceDeclHandler start, + XML_EndNamespaceDeclHandler end); + +void XMLPARSEAPI +XML_SetNotStandaloneHandler(XML_Parser parser, + XML_NotStandaloneHandler handler); + +void XMLPARSEAPI +XML_SetExternalEntityRefHandler(XML_Parser parser, + XML_ExternalEntityRefHandler handler); + +/* If a non-null value for arg is specified here, then it will be passed +as the first argument to the external entity ref handler instead +of the parser object. */ +void XMLPARSEAPI +XML_SetExternalEntityRefHandlerArg(XML_Parser, void *arg); + +void XMLPARSEAPI +XML_SetUnknownEncodingHandler(XML_Parser parser, + XML_UnknownEncodingHandler handler, + void *encodingHandlerData); + +/* This can be called within a handler for a start element, end element, +processing instruction or character data. It causes the corresponding +markup to be passed to the default handler. */ +void XMLPARSEAPI XML_DefaultCurrent(XML_Parser parser); + +/* This value is passed as the userData argument to callbacks. */ +void XMLPARSEAPI +XML_SetUserData(XML_Parser parser, void *userData); + +/* Returns the last value set by XML_SetUserData or null. */ +#define XML_GetUserData(parser) (*(void **)(parser)) + +/* This is equivalent to supplying an encoding argument +to XML_CreateParser. It must not be called after XML_Parse +or XML_ParseBuffer. */ + +int XMLPARSEAPI +XML_SetEncoding(XML_Parser parser, const XML_Char *encoding); + +/* If this function is called, then the parser will be passed +as the first argument to callbacks instead of userData. +The userData will still be accessible using XML_GetUserData. */ + +void XMLPARSEAPI +XML_UseParserAsHandlerArg(XML_Parser parser); + +/* Sets the base to be used for resolving relative URIs in system identifiers in +declarations. Resolving relative identifiers is left to the application: +this value will be passed through as the base argument to the +XML_ExternalEntityRefHandler, XML_NotationDeclHandler +and XML_UnparsedEntityDeclHandler. The base argument will be copied. +Returns zero if out of memory, non-zero otherwise. */ + +int XMLPARSEAPI +XML_SetBase(XML_Parser parser, const XML_Char *base); + +const XML_Char XMLPARSEAPI * +XML_GetBase(XML_Parser parser); + +/* Returns the number of the attributes passed in last call to the +XML_StartElementHandler that were specified in the start-tag rather +than defaulted. */ + +int XMLPARSEAPI XML_GetSpecifiedAttributeCount(XML_Parser parser); + +/* Parses some input. Returns 0 if a fatal error is detected. +The last call to XML_Parse must have isFinal true; +len may be zero for this call (or any other). */ +int XMLPARSEAPI +XML_Parse(XML_Parser parser, const char *s, int len, int isFinal); + +/* Creates an XML_Parser object that can parse an external general entity; +context is a '\0'-terminated string specifying the parse context; +encoding is a '\0'-terminated string giving the name of the externally specified encoding, +or null if there is no externally specified encoding. +The context string consists of a sequence of tokens separated by formfeeds (\f); +a token consisting of a name specifies that the general entity of the name +is open; a token of the form prefix=uri specifies the namespace for a particular +prefix; a token of the form =uri specifies the default namespace. +This can be called at any point after the first call to an ExternalEntityRefHandler +so longer as the parser has not yet been freed. +The new parser is completely independent and may safely be used in a separate thread. +The handlers and userData are initialized from the parser argument. +Returns 0 if out of memory. Otherwise returns a new XML_Parser object. */ +XML_Parser XMLPARSEAPI +XML_ExternalEntityParserCreate(XML_Parser parser, + const XML_Char *context, + const XML_Char *encoding); + +enum XML_Error { + XML_ERROR_NONE, + XML_ERROR_NO_MEMORY, + XML_ERROR_SYNTAX, + XML_ERROR_NO_ELEMENTS, + XML_ERROR_INVALID_TOKEN, + XML_ERROR_UNCLOSED_TOKEN, + XML_ERROR_PARTIAL_CHAR, + XML_ERROR_TAG_MISMATCH, + XML_ERROR_DUPLICATE_ATTRIBUTE, + XML_ERROR_JUNK_AFTER_DOC_ELEMENT, + XML_ERROR_PARAM_ENTITY_REF, + XML_ERROR_UNDEFINED_ENTITY, + XML_ERROR_RECURSIVE_ENTITY_REF, + XML_ERROR_ASYNC_ENTITY, + XML_ERROR_BAD_CHAR_REF, + XML_ERROR_BINARY_ENTITY_REF, + XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF, + XML_ERROR_MISPLACED_XML_PI, + XML_ERROR_UNKNOWN_ENCODING, + XML_ERROR_INCORRECT_ENCODING, + XML_ERROR_UNCLOSED_CDATA_SECTION, + XML_ERROR_EXTERNAL_ENTITY_HANDLING, + XML_ERROR_NOT_STANDALONE +}; + +/* If XML_Parse or XML_ParseBuffer have returned 0, then XML_GetErrorCode +returns information about the error. */ + +enum XML_Error XMLPARSEAPI XML_GetErrorCode(XML_Parser parser); + +/* These functions return information about the current parse location. +They may be called when XML_Parse or XML_ParseBuffer return 0; +in this case the location is the location of the character at which +the error was detected. +They may also be called from any other callback called to report +some parse event; in this the location is the location of the first +of the sequence of characters that generated the event. */ + +int XMLPARSEAPI XML_GetCurrentLineNumber(XML_Parser parser); +int XMLPARSEAPI XML_GetCurrentColumnNumber(XML_Parser parser); +long XMLPARSEAPI XML_GetCurrentByteIndex(XML_Parser parser); + +/* Return the number of bytes in the current event. +Returns 0 if the event is in an internal entity. */ + +int XMLPARSEAPI XML_GetCurrentByteCount(XML_Parser parser); + +/* For backwards compatibility with previous versions. */ +#define XML_GetErrorLineNumber XML_GetCurrentLineNumber +#define XML_GetErrorColumnNumber XML_GetCurrentColumnNumber +#define XML_GetErrorByteIndex XML_GetCurrentByteIndex + +/* Frees memory used by the parser. */ +void XMLPARSEAPI +XML_ParserFree(XML_Parser parser); + +/* Returns a string describing the error. */ +const XML_LChar XMLPARSEAPI *XML_ErrorString(int code); + +#ifdef __cplusplus +} +#endif + +#endif /* not XmlParse_INCLUDED */ diff --git a/protocols/jabber/xmlrole.c b/protocols/jabber/xmlrole.c new file mode 100644 index 00000000..320749e8 --- /dev/null +++ b/protocols/jabber/xmlrole.c @@ -0,0 +1,1104 @@ +/* +The contents of this file are subject to the Mozilla Public License +Version 1.1 (the "License"); you may not use this file except in +compliance with the License. You may obtain a copy of the License at +http://www.mozilla.org/MPL/ + +Software distributed under the License is distributed on an "AS IS" +basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +License for the specific language governing rights and limitations +under the License. + +The Original Code is expat. + +The Initial Developer of the Original Code is James Clark. +Portions created by James Clark are Copyright (C) 1998, 1999 +James Clark. All Rights Reserved. + +Contributor(s): + +*/ + +#include "xmldef.h" +#include "xmlrole.h" + +/* Doesn't check: + + that ,| are not mixed in a model group + content of literals + +*/ + +#ifndef MIN_BYTES_PER_CHAR +#define MIN_BYTES_PER_CHAR(enc) ((enc)->minBytesPerChar) +#endif + +typedef int PROLOG_HANDLER(struct prolog_state *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc); + +static PROLOG_HANDLER +prolog0, prolog1, prolog2, +doctype0, doctype1, doctype2, doctype3, doctype4, doctype5, +internalSubset, +entity0, entity1, entity2, entity3, entity4, entity5, entity6, +entity7, entity8, entity9, +notation0, notation1, notation2, notation3, notation4, +attlist0, attlist1, attlist2, attlist3, attlist4, attlist5, attlist6, +attlist7, attlist8, attlist9, +element0, element1, element2, element3, element4, element5, element6, +element7, +declClose, +error; + +static +int syntaxError(PROLOG_STATE *); + +static +int prolog0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + state->handler = prolog1; + return XML_ROLE_NONE; + case XML_TOK_XML_DECL: + state->handler = prolog1; + return XML_ROLE_XML_DECL; + case XML_TOK_PI: + state->handler = prolog1; + return XML_ROLE_NONE; + case XML_TOK_COMMENT: + state->handler = prolog1; + case XML_TOK_BOM: + return XML_ROLE_NONE; + case XML_TOK_DECL_OPEN: + if (!XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + "DOCTYPE")) + break; + state->handler = doctype0; + return XML_ROLE_NONE; + case XML_TOK_INSTANCE_START: + state->handler = error; + return XML_ROLE_INSTANCE_START; + } + return syntaxError(state); +} + +static +int prolog1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_PI: + case XML_TOK_COMMENT: + case XML_TOK_BOM: + return XML_ROLE_NONE; + case XML_TOK_DECL_OPEN: + if (!XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + "DOCTYPE")) + break; + state->handler = doctype0; + return XML_ROLE_NONE; + case XML_TOK_INSTANCE_START: + state->handler = error; + return XML_ROLE_INSTANCE_START; + } + return syntaxError(state); +} + +static +int prolog2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_PI: + case XML_TOK_COMMENT: + return XML_ROLE_NONE; + case XML_TOK_INSTANCE_START: + state->handler = error; + return XML_ROLE_INSTANCE_START; + } + return syntaxError(state); +} + +static +int doctype0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = doctype1; + return XML_ROLE_DOCTYPE_NAME; + } + return syntaxError(state); +} + +static +int doctype1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_OPEN_BRACKET: + state->handler = internalSubset; + return XML_ROLE_NONE; + case XML_TOK_DECL_CLOSE: + state->handler = prolog2; + return XML_ROLE_DOCTYPE_CLOSE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, "SYSTEM")) { + state->handler = doctype3; + return XML_ROLE_NONE; + } + if (XmlNameMatchesAscii(enc, ptr, "PUBLIC")) { + state->handler = doctype2; + return XML_ROLE_NONE; + } + break; + } + return syntaxError(state); +} + +static +int doctype2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_LITERAL: + state->handler = doctype3; + return XML_ROLE_DOCTYPE_PUBLIC_ID; + } + return syntaxError(state); +} + +static +int doctype3(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_LITERAL: + state->handler = doctype4; + return XML_ROLE_DOCTYPE_SYSTEM_ID; + } + return syntaxError(state); +} + +static +int doctype4(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_OPEN_BRACKET: + state->handler = internalSubset; + return XML_ROLE_NONE; + case XML_TOK_DECL_CLOSE: + state->handler = prolog2; + return XML_ROLE_DOCTYPE_CLOSE; + } + return syntaxError(state); +} + +static +int doctype5(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_DECL_CLOSE: + state->handler = prolog2; + return XML_ROLE_DOCTYPE_CLOSE; + } + return syntaxError(state); +} + +static +int internalSubset(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_DECL_OPEN: + if (XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + "ENTITY")) { + state->handler = entity0; + return XML_ROLE_NONE; + } + if (XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + "ATTLIST")) { + state->handler = attlist0; + return XML_ROLE_NONE; + } + if (XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + "ELEMENT")) { + state->handler = element0; + return XML_ROLE_NONE; + } + if (XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + "NOTATION")) { + state->handler = notation0; + return XML_ROLE_NONE; + } + break; + case XML_TOK_PI: + case XML_TOK_COMMENT: + return XML_ROLE_NONE; + case XML_TOK_PARAM_ENTITY_REF: + return XML_ROLE_PARAM_ENTITY_REF; + case XML_TOK_CLOSE_BRACKET: + state->handler = doctype5; + return XML_ROLE_NONE; + } + return syntaxError(state); +} + +static +int entity0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_PERCENT: + state->handler = entity1; + return XML_ROLE_NONE; + case XML_TOK_NAME: + state->handler = entity2; + return XML_ROLE_GENERAL_ENTITY_NAME; + } + return syntaxError(state); +} + +static +int entity1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + state->handler = entity7; + return XML_ROLE_PARAM_ENTITY_NAME; + } + return syntaxError(state); +} + +static +int entity2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, "SYSTEM")) { + state->handler = entity4; + return XML_ROLE_NONE; + } + if (XmlNameMatchesAscii(enc, ptr, "PUBLIC")) { + state->handler = entity3; + return XML_ROLE_NONE; + } + break; + case XML_TOK_LITERAL: + state->handler = declClose; + return XML_ROLE_ENTITY_VALUE; + } + return syntaxError(state); +} + +static +int entity3(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_LITERAL: + state->handler = entity4; + return XML_ROLE_ENTITY_PUBLIC_ID; + } + return syntaxError(state); +} + + +static +int entity4(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_LITERAL: + state->handler = entity5; + return XML_ROLE_ENTITY_SYSTEM_ID; + } + return syntaxError(state); +} + +static +int entity5(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_DECL_CLOSE: + state->handler = internalSubset; + return XML_ROLE_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, "NDATA")) { + state->handler = entity6; + return XML_ROLE_NONE; + } + break; + } + return syntaxError(state); +} + +static +int entity6(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + state->handler = declClose; + return XML_ROLE_ENTITY_NOTATION_NAME; + } + return syntaxError(state); +} + +static +int entity7(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, "SYSTEM")) { + state->handler = entity9; + return XML_ROLE_NONE; + } + if (XmlNameMatchesAscii(enc, ptr, "PUBLIC")) { + state->handler = entity8; + return XML_ROLE_NONE; + } + break; + case XML_TOK_LITERAL: + state->handler = declClose; + return XML_ROLE_ENTITY_VALUE; + } + return syntaxError(state); +} + +static +int entity8(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_LITERAL: + state->handler = entity9; + return XML_ROLE_ENTITY_PUBLIC_ID; + } + return syntaxError(state); +} + +static +int entity9(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_LITERAL: + state->handler = declClose; + return XML_ROLE_ENTITY_SYSTEM_ID; + } + return syntaxError(state); +} + +static +int notation0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + state->handler = notation1; + return XML_ROLE_NOTATION_NAME; + } + return syntaxError(state); +} + +static +int notation1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, "SYSTEM")) { + state->handler = notation3; + return XML_ROLE_NONE; + } + if (XmlNameMatchesAscii(enc, ptr, "PUBLIC")) { + state->handler = notation2; + return XML_ROLE_NONE; + } + break; + } + return syntaxError(state); +} + +static +int notation2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_LITERAL: + state->handler = notation4; + return XML_ROLE_NOTATION_PUBLIC_ID; + } + return syntaxError(state); +} + +static +int notation3(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_LITERAL: + state->handler = declClose; + return XML_ROLE_NOTATION_SYSTEM_ID; + } + return syntaxError(state); +} + +static +int notation4(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_LITERAL: + state->handler = declClose; + return XML_ROLE_NOTATION_SYSTEM_ID; + case XML_TOK_DECL_CLOSE: + state->handler = internalSubset; + return XML_ROLE_NOTATION_NO_SYSTEM_ID; + } + return syntaxError(state); +} + +static +int attlist0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = attlist1; + return XML_ROLE_ATTLIST_ELEMENT_NAME; + } + return syntaxError(state); +} + +static +int attlist1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_DECL_CLOSE: + state->handler = internalSubset; + return XML_ROLE_NONE; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = attlist2; + return XML_ROLE_ATTRIBUTE_NAME; + } + return syntaxError(state); +} + +static +int attlist2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + { + static const char *types[] = { + "CDATA", + "ID", + "IDREF", + "IDREFS", + "ENTITY", + "ENTITIES", + "NMTOKEN", + "NMTOKENS", + }; + int i; + for (i = 0; i < (int)(sizeof(types)/sizeof(types[0])); i++) + if (XmlNameMatchesAscii(enc, ptr, types[i])) { + state->handler = attlist8; + return XML_ROLE_ATTRIBUTE_TYPE_CDATA + i; + } + } + if (XmlNameMatchesAscii(enc, ptr, "NOTATION")) { + state->handler = attlist5; + return XML_ROLE_NONE; + } + break; + case XML_TOK_OPEN_PAREN: + state->handler = attlist3; + return XML_ROLE_NONE; + } + return syntaxError(state); +} + +static +int attlist3(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NMTOKEN: + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = attlist4; + return XML_ROLE_ATTRIBUTE_ENUM_VALUE; + } + return syntaxError(state); +} + +static +int attlist4(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_CLOSE_PAREN: + state->handler = attlist8; + return XML_ROLE_NONE; + case XML_TOK_OR: + state->handler = attlist3; + return XML_ROLE_NONE; + } + return syntaxError(state); +} + +static +int attlist5(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_OPEN_PAREN: + state->handler = attlist6; + return XML_ROLE_NONE; + } + return syntaxError(state); +} + + +static +int attlist6(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + state->handler = attlist7; + return XML_ROLE_ATTRIBUTE_NOTATION_VALUE; + } + return syntaxError(state); +} + +static +int attlist7(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_CLOSE_PAREN: + state->handler = attlist8; + return XML_ROLE_NONE; + case XML_TOK_OR: + state->handler = attlist6; + return XML_ROLE_NONE; + } + return syntaxError(state); +} + +/* default value */ +static +int attlist8(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_POUND_NAME: + if (XmlNameMatchesAscii(enc, + ptr + MIN_BYTES_PER_CHAR(enc), + "IMPLIED")) { + state->handler = attlist1; + return XML_ROLE_IMPLIED_ATTRIBUTE_VALUE; + } + if (XmlNameMatchesAscii(enc, + ptr + MIN_BYTES_PER_CHAR(enc), + "REQUIRED")) { + state->handler = attlist1; + return XML_ROLE_REQUIRED_ATTRIBUTE_VALUE; + } + if (XmlNameMatchesAscii(enc, + ptr + MIN_BYTES_PER_CHAR(enc), + "FIXED")) { + state->handler = attlist9; + return XML_ROLE_NONE; + } + break; + case XML_TOK_LITERAL: + state->handler = attlist1; + return XML_ROLE_DEFAULT_ATTRIBUTE_VALUE; + } + return syntaxError(state); +} + +static +int attlist9(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_LITERAL: + state->handler = attlist1; + return XML_ROLE_FIXED_ATTRIBUTE_VALUE; + } + return syntaxError(state); +} + +static +int element0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = element1; + return XML_ROLE_ELEMENT_NAME; + } + return syntaxError(state); +} + +static +int element1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, "EMPTY")) { + state->handler = declClose; + return XML_ROLE_CONTENT_EMPTY; + } + if (XmlNameMatchesAscii(enc, ptr, "ANY")) { + state->handler = declClose; + return XML_ROLE_CONTENT_ANY; + } + break; + case XML_TOK_OPEN_PAREN: + state->handler = element2; + state->level = 1; + return XML_ROLE_GROUP_OPEN; + } + return syntaxError(state); +} + +static +int element2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_POUND_NAME: + if (XmlNameMatchesAscii(enc, + ptr + MIN_BYTES_PER_CHAR(enc), + "PCDATA")) { + state->handler = element3; + return XML_ROLE_CONTENT_PCDATA; + } + break; + case XML_TOK_OPEN_PAREN: + state->level = 2; + state->handler = element6; + return XML_ROLE_GROUP_OPEN; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT; + case XML_TOK_NAME_QUESTION: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_OPT; + case XML_TOK_NAME_ASTERISK: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_REP; + case XML_TOK_NAME_PLUS: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_PLUS; + } + return syntaxError(state); +} + +static +int element3(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_CLOSE_PAREN: + case XML_TOK_CLOSE_PAREN_ASTERISK: + state->handler = declClose; + return XML_ROLE_GROUP_CLOSE_REP; + case XML_TOK_OR: + state->handler = element4; + return XML_ROLE_NONE; + } + return syntaxError(state); +} + +static +int element4(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = element5; + return XML_ROLE_CONTENT_ELEMENT; + } + return syntaxError(state); +} + +static +int element5(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_CLOSE_PAREN_ASTERISK: + state->handler = declClose; + return XML_ROLE_GROUP_CLOSE_REP; + case XML_TOK_OR: + state->handler = element4; + return XML_ROLE_NONE; + } + return syntaxError(state); +} + +static +int element6(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_OPEN_PAREN: + state->level += 1; + return XML_ROLE_GROUP_OPEN; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT; + case XML_TOK_NAME_QUESTION: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_OPT; + case XML_TOK_NAME_ASTERISK: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_REP; + case XML_TOK_NAME_PLUS: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_PLUS; + } + return syntaxError(state); +} + +static +int element7(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_CLOSE_PAREN: + state->level -= 1; + if (state->level == 0) + state->handler = declClose; + return XML_ROLE_GROUP_CLOSE; + case XML_TOK_CLOSE_PAREN_ASTERISK: + state->level -= 1; + if (state->level == 0) + state->handler = declClose; + return XML_ROLE_GROUP_CLOSE_REP; + case XML_TOK_CLOSE_PAREN_QUESTION: + state->level -= 1; + if (state->level == 0) + state->handler = declClose; + return XML_ROLE_GROUP_CLOSE_OPT; + case XML_TOK_CLOSE_PAREN_PLUS: + state->level -= 1; + if (state->level == 0) + state->handler = declClose; + return XML_ROLE_GROUP_CLOSE_PLUS; + case XML_TOK_COMMA: + state->handler = element6; + return XML_ROLE_GROUP_SEQUENCE; + case XML_TOK_OR: + state->handler = element6; + return XML_ROLE_GROUP_CHOICE; + } + return syntaxError(state); +} + +static +int declClose(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_DECL_CLOSE: + state->handler = internalSubset; + return XML_ROLE_NONE; + } + return syntaxError(state); +} + +#if 0 + +static +int ignore(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_DECL_CLOSE: + state->handler = internalSubset; + return 0; + default: + return XML_ROLE_NONE; + } + return syntaxError(state); +} +#endif + +static +int error(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + return XML_ROLE_NONE; +} + +static +int syntaxError(PROLOG_STATE *state) +{ + state->handler = error; + return XML_ROLE_ERROR; +} + +void XmlPrologStateInit(PROLOG_STATE *state) +{ + state->handler = prolog0; +} diff --git a/protocols/jabber/xmlrole.h b/protocols/jabber/xmlrole.h new file mode 100644 index 00000000..877c40ba --- /dev/null +++ b/protocols/jabber/xmlrole.h @@ -0,0 +1,111 @@ +/* +The contents of this file are subject to the Mozilla Public License +Version 1.1 (the "License"); you may not use this file except in +compliance with the License. You may obtain a copy of the License at +http://www.mozilla.org/MPL/ + +Software distributed under the License is distributed on an "AS IS" +basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +License for the specific language governing rights and limitations +under the License. + +The Original Code is expat. + +The Initial Developer of the Original Code is James Clark. +Portions created by James Clark are Copyright (C) 1998, 1999 +James Clark. All Rights Reserved. + +Contributor(s): + +Alternatively, the contents of this file may be used under the terms +of the GNU General Public License (the "GPL"), in which case the +provisions of the GPL are applicable instead of those above. If you +wish to allow use of your version of this file only under the terms of +the GPL and not to allow others to use your version of this file under +the MPL, indicate your decision by deleting the provisions above and +replace them with the notice and other provisions required by the +GPL. If you do not delete the provisions above, a recipient may use +your version of this file under either the MPL or the GPL. +*/ + +#ifndef XmlRole_INCLUDED +#define XmlRole_INCLUDED 1 + +#include "xmltok.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + XML_ROLE_ERROR = -1, + XML_ROLE_NONE = 0, + XML_ROLE_XML_DECL, + XML_ROLE_INSTANCE_START, + XML_ROLE_DOCTYPE_NAME, + XML_ROLE_DOCTYPE_SYSTEM_ID, + XML_ROLE_DOCTYPE_PUBLIC_ID, + XML_ROLE_DOCTYPE_CLOSE, + XML_ROLE_GENERAL_ENTITY_NAME, + XML_ROLE_PARAM_ENTITY_NAME, + XML_ROLE_ENTITY_VALUE, + XML_ROLE_ENTITY_SYSTEM_ID, + XML_ROLE_ENTITY_PUBLIC_ID, + XML_ROLE_ENTITY_NOTATION_NAME, + XML_ROLE_NOTATION_NAME, + XML_ROLE_NOTATION_SYSTEM_ID, + XML_ROLE_NOTATION_NO_SYSTEM_ID, + XML_ROLE_NOTATION_PUBLIC_ID, + XML_ROLE_ATTRIBUTE_NAME, + XML_ROLE_ATTRIBUTE_TYPE_CDATA, + XML_ROLE_ATTRIBUTE_TYPE_ID, + XML_ROLE_ATTRIBUTE_TYPE_IDREF, + XML_ROLE_ATTRIBUTE_TYPE_IDREFS, + XML_ROLE_ATTRIBUTE_TYPE_ENTITY, + XML_ROLE_ATTRIBUTE_TYPE_ENTITIES, + XML_ROLE_ATTRIBUTE_TYPE_NMTOKEN, + XML_ROLE_ATTRIBUTE_TYPE_NMTOKENS, + XML_ROLE_ATTRIBUTE_ENUM_VALUE, + XML_ROLE_ATTRIBUTE_NOTATION_VALUE, + XML_ROLE_ATTLIST_ELEMENT_NAME, + XML_ROLE_IMPLIED_ATTRIBUTE_VALUE, + XML_ROLE_REQUIRED_ATTRIBUTE_VALUE, + XML_ROLE_DEFAULT_ATTRIBUTE_VALUE, + XML_ROLE_FIXED_ATTRIBUTE_VALUE, + XML_ROLE_ELEMENT_NAME, + XML_ROLE_CONTENT_ANY, + XML_ROLE_CONTENT_EMPTY, + XML_ROLE_CONTENT_PCDATA, + XML_ROLE_GROUP_OPEN, + XML_ROLE_GROUP_CLOSE, + XML_ROLE_GROUP_CLOSE_REP, + XML_ROLE_GROUP_CLOSE_OPT, + XML_ROLE_GROUP_CLOSE_PLUS, + XML_ROLE_GROUP_CHOICE, + XML_ROLE_GROUP_SEQUENCE, + XML_ROLE_CONTENT_ELEMENT, + XML_ROLE_CONTENT_ELEMENT_REP, + XML_ROLE_CONTENT_ELEMENT_OPT, + XML_ROLE_CONTENT_ELEMENT_PLUS, + XML_ROLE_PARAM_ENTITY_REF +}; + +typedef struct prolog_state { + int (*handler)(struct prolog_state *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc); + unsigned level; +} PROLOG_STATE; + +void XMLTOKAPI XmlPrologStateInit(PROLOG_STATE *); + +#define XmlTokenRole(state, tok, ptr, end, enc) \ + (((state)->handler)(state, tok, ptr, end, enc)) + +#ifdef __cplusplus +} +#endif + +#endif /* not XmlRole_INCLUDED */ diff --git a/protocols/jabber/xmltok.c b/protocols/jabber/xmltok.c new file mode 100644 index 00000000..8b7ae15e --- /dev/null +++ b/protocols/jabber/xmltok.c @@ -0,0 +1,1518 @@ +/* +The contents of this file are subject to the Mozilla Public License +Version 1.1 (the "License"); you may not use this file except in +compliance with the License. You may obtain a copy of the License at +http://www.mozilla.org/MPL/ + +Software distributed under the License is distributed on an "AS IS" +basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +License for the specific language governing rights and limitations +under the License. + +The Original Code is expat. + +The Initial Developer of the Original Code is James Clark. +Portions created by James Clark are Copyright (C) 1998, 1999 +James Clark. All Rights Reserved. + +Contributor(s): + +*/ + +#include "xmldef.h" +#include "xmltok.h" +#include "nametab.h" + +#define VTABLE1 \ + { PREFIX(prologTok), PREFIX(contentTok), PREFIX(cdataSectionTok) }, \ + { PREFIX(attributeValueTok), PREFIX(entityValueTok) }, \ + PREFIX(sameName), \ + PREFIX(nameMatchesAscii), \ + PREFIX(nameLength), \ + PREFIX(skipS), \ + PREFIX(getAtts), \ + PREFIX(charRefNumber), \ + PREFIX(predefinedEntityName), \ + PREFIX(updatePosition), \ + PREFIX(isPublicId) + +#define VTABLE VTABLE1, PREFIX(toUtf8), PREFIX(toUtf16) + +#define UCS2_GET_NAMING(pages, hi, lo) \ + (namingBitmap[(pages[hi] << 3) + ((lo) >> 5)] & (1 << ((lo) & 0x1F))) + +/* A 2 byte UTF-8 representation splits the characters 11 bits +between the bottom 5 and 6 bits of the bytes. +We need 8 bits to index into pages, 3 bits to add to that index and +5 bits to generate the mask. */ +#define UTF8_GET_NAMING2(pages, byte) \ + (namingBitmap[((pages)[(((byte)[0]) >> 2) & 7] << 3) \ + + ((((byte)[0]) & 3) << 1) \ + + ((((byte)[1]) >> 5) & 1)] \ + & (1 << (((byte)[1]) & 0x1F))) + +/* A 3 byte UTF-8 representation splits the characters 16 bits +between the bottom 4, 6 and 6 bits of the bytes. +We need 8 bits to index into pages, 3 bits to add to that index and +5 bits to generate the mask. */ +#define UTF8_GET_NAMING3(pages, byte) \ + (namingBitmap[((pages)[((((byte)[0]) & 0xF) << 4) \ + + ((((byte)[1]) >> 2) & 0xF)] \ + << 3) \ + + ((((byte)[1]) & 3) << 1) \ + + ((((byte)[2]) >> 5) & 1)] \ + & (1 << (((byte)[2]) & 0x1F))) + +#define UTF8_GET_NAMING(pages, p, n) \ + ((n) == 2 \ + ? UTF8_GET_NAMING2(pages, (const unsigned char *)(p)) \ + : ((n) == 3 \ + ? UTF8_GET_NAMING3(pages, (const unsigned char *)(p)) \ + : 0)) + +#define UTF8_INVALID3(p) \ + ((*p) == 0xED \ + ? (((p)[1] & 0x20) != 0) \ + : ((*p) == 0xEF \ + ? ((p)[1] == 0xBF && ((p)[2] == 0xBF || (p)[2] == 0xBE)) \ + : 0)) + +#define UTF8_INVALID4(p) ((*p) == 0xF4 && ((p)[1] & 0x30) != 0) + +static +int isNever(const ENCODING *enc, const char *p) +{ + return 0; +} + +static +int utf8_isName2(const ENCODING *enc, const char *p) +{ + return UTF8_GET_NAMING2(namePages, (const unsigned char *)p); +} + +static +int utf8_isName3(const ENCODING *enc, const char *p) +{ + return UTF8_GET_NAMING3(namePages, (const unsigned char *)p); +} + +#define utf8_isName4 isNever + +static +int utf8_isNmstrt2(const ENCODING *enc, const char *p) +{ + return UTF8_GET_NAMING2(nmstrtPages, (const unsigned char *)p); +} + +static +int utf8_isNmstrt3(const ENCODING *enc, const char *p) +{ + return UTF8_GET_NAMING3(nmstrtPages, (const unsigned char *)p); +} + +#define utf8_isNmstrt4 isNever + +#define utf8_isInvalid2 isNever + +static +int utf8_isInvalid3(const ENCODING *enc, const char *p) +{ + return UTF8_INVALID3((const unsigned char *)p); +} + +static +int utf8_isInvalid4(const ENCODING *enc, const char *p) +{ + return UTF8_INVALID4((const unsigned char *)p); +} + +struct normal_encoding { + ENCODING enc; + unsigned char type[256]; +#ifdef XML_MIN_SIZE + int (*byteType)(const ENCODING *, const char *); + int (*isNameMin)(const ENCODING *, const char *); + int (*isNmstrtMin)(const ENCODING *, const char *); + int (*byteToAscii)(const ENCODING *, const char *); + int (*charMatches)(const ENCODING *, const char *, int); +#endif /* XML_MIN_SIZE */ + int (*isName2)(const ENCODING *, const char *); + int (*isName3)(const ENCODING *, const char *); + int (*isName4)(const ENCODING *, const char *); + int (*isNmstrt2)(const ENCODING *, const char *); + int (*isNmstrt3)(const ENCODING *, const char *); + int (*isNmstrt4)(const ENCODING *, const char *); + int (*isInvalid2)(const ENCODING *, const char *); + int (*isInvalid3)(const ENCODING *, const char *); + int (*isInvalid4)(const ENCODING *, const char *); +}; + +#ifdef XML_MIN_SIZE + +#define STANDARD_VTABLE(E) \ + E ## byteType, \ + E ## isNameMin, \ + E ## isNmstrtMin, \ + E ## byteToAscii, \ + E ## charMatches, + +#else + +#define STANDARD_VTABLE(E) /* as nothing */ + +#endif + +#define NORMAL_VTABLE(E) \ + E ## isName2, \ + E ## isName3, \ + E ## isName4, \ + E ## isNmstrt2, \ + E ## isNmstrt3, \ + E ## isNmstrt4, \ + E ## isInvalid2, \ + E ## isInvalid3, \ + E ## isInvalid4 + +static int checkCharRefNumber(int); + +#include "xmltok_impl.h" + +#ifdef XML_MIN_SIZE +#define sb_isNameMin isNever +#define sb_isNmstrtMin isNever +#endif + +#ifdef XML_MIN_SIZE +#define MINBPC(enc) ((enc)->minBytesPerChar) +#else +/* minimum bytes per character */ +#define MINBPC(enc) 1 +#endif + +#define SB_BYTE_TYPE(enc, p) \ + (((struct normal_encoding *)(enc))->type[(unsigned char)*(p)]) + +#ifdef XML_MIN_SIZE +static +int sb_byteType(const ENCODING *enc, const char *p) +{ + return SB_BYTE_TYPE(enc, p); +} +#define BYTE_TYPE(enc, p) \ + (((const struct normal_encoding *)(enc))->byteType(enc, p)) +#else +#define BYTE_TYPE(enc, p) SB_BYTE_TYPE(enc, p) +#endif + +#ifdef XML_MIN_SIZE +#define BYTE_TO_ASCII(enc, p) \ + (((const struct normal_encoding *)(enc))->byteToAscii(enc, p)) +static +int sb_byteToAscii(const ENCODING *enc, const char *p) +{ + return *p; +} +#else +#define BYTE_TO_ASCII(enc, p) (*p) +#endif + +#define IS_NAME_CHAR(enc, p, n) \ + (((const struct normal_encoding *)(enc))->isName ## n(enc, p)) +#define IS_NMSTRT_CHAR(enc, p, n) \ + (((const struct normal_encoding *)(enc))->isNmstrt ## n(enc, p)) +#define IS_INVALID_CHAR(enc, p, n) \ + (((const struct normal_encoding *)(enc))->isInvalid ## n(enc, p)) + +#ifdef XML_MIN_SIZE +#define IS_NAME_CHAR_MINBPC(enc, p) \ + (((const struct normal_encoding *)(enc))->isNameMin(enc, p)) +#define IS_NMSTRT_CHAR_MINBPC(enc, p) \ + (((const struct normal_encoding *)(enc))->isNmstrtMin(enc, p)) +#else +#define IS_NAME_CHAR_MINBPC(enc, p) (0) +#define IS_NMSTRT_CHAR_MINBPC(enc, p) (0) +#endif + +#ifdef XML_MIN_SIZE +#define CHAR_MATCHES(enc, p, c) \ + (((const struct normal_encoding *)(enc))->charMatches(enc, p, c)) +static +int sb_charMatches(const ENCODING *enc, const char *p, int c) +{ + return *p == c; +} +#else +/* c is an ASCII character */ +#define CHAR_MATCHES(enc, p, c) (*(p) == c) +#endif + +#define PREFIX(ident) normal_ ## ident +#include "xmltok_impl.c" + +#undef MINBPC +#undef BYTE_TYPE +#undef BYTE_TO_ASCII +#undef CHAR_MATCHES +#undef IS_NAME_CHAR +#undef IS_NAME_CHAR_MINBPC +#undef IS_NMSTRT_CHAR +#undef IS_NMSTRT_CHAR_MINBPC +#undef IS_INVALID_CHAR + +enum { /* UTF8_cvalN is value of masked first byte of N byte sequence */ + UTF8_cval1 = 0x00, + UTF8_cval2 = 0xc0, + UTF8_cval3 = 0xe0, + UTF8_cval4 = 0xf0 +}; + +static +void utf8_toUtf8(const ENCODING *enc, + const char **fromP, const char *fromLim, + char **toP, const char *toLim) +{ + char *to; + const char *from; + if (fromLim - *fromP > toLim - *toP) { + /* Avoid copying partial characters. */ + for (fromLim = *fromP + (toLim - *toP); fromLim > *fromP; fromLim--) + if (((unsigned char)fromLim[-1] & 0xc0) != 0x80) + break; + } + for (to = *toP, from = *fromP; from != fromLim; from++, to++) + *to = *from; + *fromP = from; + *toP = to; +} + +static +void utf8_toUtf16(const ENCODING *enc, + const char **fromP, const char *fromLim, + unsigned short **toP, const unsigned short *toLim) +{ + unsigned short *to = *toP; + const char *from = *fromP; + while (from != fromLim && to != toLim) { + switch (((struct normal_encoding *)enc)->type[(unsigned char)*from]) { + case BT_LEAD2: + *to++ = ((from[0] & 0x1f) << 6) | (from[1] & 0x3f); + from += 2; + break; + case BT_LEAD3: + *to++ = ((from[0] & 0xf) << 12) | ((from[1] & 0x3f) << 6) | (from[2] & 0x3f); + from += 3; + break; + case BT_LEAD4: + { + unsigned long n; + if (to + 1 == toLim) + break; + n = ((from[0] & 0x7) << 18) | ((from[1] & 0x3f) << 12) | ((from[2] & 0x3f) << 6) | (from[3] & 0x3f); + n -= 0x10000; + to[0] = (unsigned short)((n >> 10) | 0xD800); + to[1] = (unsigned short)((n & 0x3FF) | 0xDC00); + to += 2; + from += 4; + } + break; + default: + *to++ = *from++; + break; + } + } + *fromP = from; + *toP = to; +} + +#ifdef XML_NS +static const struct normal_encoding utf8_encoding_ns = { + { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 }, + { +#include "asciitab.h" +#include "utf8tab.h" + }, + STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_) + }; +#endif + +static const struct normal_encoding utf8_encoding = { + { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 }, + { +#define BT_COLON BT_NMSTRT +#include "asciitab.h" +#undef BT_COLON +#include "utf8tab.h" + }, + STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_) + }; + +#ifdef XML_NS + +static const struct normal_encoding internal_utf8_encoding_ns = { + { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 }, + { +#include "iasciitab.h" +#include "utf8tab.h" + }, + STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_) + }; + +#endif + +static const struct normal_encoding internal_utf8_encoding = { + { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 }, + { +#define BT_COLON BT_NMSTRT +#include "iasciitab.h" +#undef BT_COLON +#include "utf8tab.h" + }, + STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_) + }; + +static +void latin1_toUtf8(const ENCODING *enc, + const char **fromP, const char *fromLim, + char **toP, const char *toLim) +{ + for (;;) { + unsigned char c; + if (*fromP == fromLim) + break; + c = (unsigned char)**fromP; + if (c & 0x80) { + if (toLim - *toP < 2) + break; + *(*toP)++ = ((c >> 6) | UTF8_cval2); + *(*toP)++ = ((c & 0x3f) | 0x80); + (*fromP)++; + } + else { + if (*toP == toLim) + break; + *(*toP)++ = *(*fromP)++; + } + } +} + +static +void latin1_toUtf16(const ENCODING *enc, + const char **fromP, const char *fromLim, + unsigned short **toP, const unsigned short *toLim) +{ + while (*fromP != fromLim && *toP != toLim) + *(*toP)++ = (unsigned char)*(*fromP)++; +} + +#ifdef XML_NS + +static const struct normal_encoding latin1_encoding_ns = { + { VTABLE1, latin1_toUtf8, latin1_toUtf16, 1, 0, 0 }, + { +#include "asciitab.h" +#include "latin1tab.h" + }, + STANDARD_VTABLE(sb_) + }; + +#endif + +static const struct normal_encoding latin1_encoding = { + { VTABLE1, latin1_toUtf8, latin1_toUtf16, 1, 0, 0 }, + { +#define BT_COLON BT_NMSTRT +#include "asciitab.h" +#undef BT_COLON +#include "latin1tab.h" + }, + STANDARD_VTABLE(sb_) + }; + +static +void ascii_toUtf8(const ENCODING *enc, + const char **fromP, const char *fromLim, + char **toP, const char *toLim) +{ + while (*fromP != fromLim && *toP != toLim) + *(*toP)++ = *(*fromP)++; +} + +#ifdef XML_NS + +static const struct normal_encoding ascii_encoding_ns = { + { VTABLE1, ascii_toUtf8, latin1_toUtf16, 1, 1, 0 }, + { +#include "asciitab.h" + /* BT_NONXML == 0 */ + }, + STANDARD_VTABLE(sb_) + }; + +#endif + +static const struct normal_encoding ascii_encoding = { + { VTABLE1, ascii_toUtf8, latin1_toUtf16, 1, 1, 0 }, + { +#define BT_COLON BT_NMSTRT +#include "asciitab.h" +#undef BT_COLON + /* BT_NONXML == 0 */ + }, + STANDARD_VTABLE(sb_) + }; + +static int unicode_byte_type(char hi, char lo) +{ + switch ((unsigned char)hi) { +case 0xD8: case 0xD9: case 0xDA: case 0xDB: + return BT_LEAD4; +case 0xDC: case 0xDD: case 0xDE: case 0xDF: + return BT_TRAIL; + case 0xFF: + switch ((unsigned char)lo) { + case 0xFF: + case 0xFE: + return BT_NONXML; + } + break; + } + return BT_NONASCII; +} + +#define DEFINE_UTF16_TO_UTF8(E) \ +static \ +void E ## toUtf8(const ENCODING *enc, \ + const char **fromP, const char *fromLim, \ + char **toP, const char *toLim) \ +{ \ + const char *from; \ + for (from = *fromP; from != fromLim; from += 2) { \ + int plane; \ + unsigned char lo2; \ + unsigned char lo = GET_LO(from); \ + unsigned char hi = GET_HI(from); \ + switch (hi) { \ + case 0: \ + if (lo < 0x80) { \ + if (*toP == toLim) { \ + *fromP = from; \ + return; \ + } \ + *(*toP)++ = lo; \ + break; \ + } \ + /* fall through */ \ + case 0x1: case 0x2: case 0x3: \ + case 0x4: case 0x5: case 0x6: case 0x7: \ + if (toLim - *toP < 2) { \ + *fromP = from; \ + return; \ + } \ + *(*toP)++ = ((lo >> 6) | (hi << 2) | UTF8_cval2); \ + *(*toP)++ = ((lo & 0x3f) | 0x80); \ + break; \ + default: \ + if (toLim - *toP < 3) { \ + *fromP = from; \ + return; \ + } \ + /* 16 bits divided 4, 6, 6 amongst 3 bytes */ \ + *(*toP)++ = ((hi >> 4) | UTF8_cval3); \ + *(*toP)++ = (((hi & 0xf) << 2) | (lo >> 6) | 0x80); \ + *(*toP)++ = ((lo & 0x3f) | 0x80); \ + break; \ + case 0xD8: case 0xD9: case 0xDA: case 0xDB: \ + if (toLim - *toP < 4) { \ + *fromP = from; \ + return; \ + } \ + plane = (((hi & 0x3) << 2) | ((lo >> 6) & 0x3)) + 1; \ + *(*toP)++ = ((plane >> 2) | UTF8_cval4); \ + *(*toP)++ = (((lo >> 2) & 0xF) | ((plane & 0x3) << 4) | 0x80); \ + from += 2; \ + lo2 = GET_LO(from); \ + *(*toP)++ = (((lo & 0x3) << 4) \ + | ((GET_HI(from) & 0x3) << 2) \ + | (lo2 >> 6) \ + | 0x80); \ + *(*toP)++ = ((lo2 & 0x3f) | 0x80); \ + break; \ + } \ + } \ + *fromP = from; \ +} + +#define DEFINE_UTF16_TO_UTF16(E) \ +static \ +void E ## toUtf16(const ENCODING *enc, \ + const char **fromP, const char *fromLim, \ + unsigned short **toP, const unsigned short *toLim) \ +{ \ + /* Avoid copying first half only of surrogate */ \ + if (fromLim - *fromP > ((toLim - *toP) << 1) \ + && (GET_HI(fromLim - 2) & 0xF8) == 0xD8) \ + fromLim -= 2; \ + for (; *fromP != fromLim && *toP != toLim; *fromP += 2) \ + *(*toP)++ = (GET_HI(*fromP) << 8) | GET_LO(*fromP); \ +} + +#define SET2(ptr, ch) \ + (((ptr)[0] = ((ch) & 0xff)), ((ptr)[1] = ((ch) >> 8))) +#define GET_LO(ptr) ((unsigned char)(ptr)[0]) +#define GET_HI(ptr) ((unsigned char)(ptr)[1]) + +DEFINE_UTF16_TO_UTF8(little2_) +DEFINE_UTF16_TO_UTF16(little2_) + +#undef SET2 +#undef GET_LO +#undef GET_HI + +#define SET2(ptr, ch) \ + (((ptr)[0] = ((ch) >> 8)), ((ptr)[1] = ((ch) & 0xFF))) +#define GET_LO(ptr) ((unsigned char)(ptr)[1]) +#define GET_HI(ptr) ((unsigned char)(ptr)[0]) + +DEFINE_UTF16_TO_UTF8(big2_) +DEFINE_UTF16_TO_UTF16(big2_) + +#undef SET2 +#undef GET_LO +#undef GET_HI + +#define LITTLE2_BYTE_TYPE(enc, p) \ + ((p)[1] == 0 \ + ? ((struct normal_encoding *)(enc))->type[(unsigned char)*(p)] \ + : unicode_byte_type((p)[1], (p)[0])) +#define LITTLE2_BYTE_TO_ASCII(enc, p) ((p)[1] == 0 ? (p)[0] : -1) +#define LITTLE2_CHAR_MATCHES(enc, p, c) ((p)[1] == 0 && (p)[0] == c) +#define LITTLE2_IS_NAME_CHAR_MINBPC(enc, p) \ + UCS2_GET_NAMING(namePages, (unsigned char)p[1], (unsigned char)p[0]) +#define LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p) \ + UCS2_GET_NAMING(nmstrtPages, (unsigned char)p[1], (unsigned char)p[0]) + +#ifdef XML_MIN_SIZE + +static +int little2_byteType(const ENCODING *enc, const char *p) +{ + return LITTLE2_BYTE_TYPE(enc, p); +} + +static +int little2_byteToAscii(const ENCODING *enc, const char *p) +{ + return LITTLE2_BYTE_TO_ASCII(enc, p); +} + +static +int little2_charMatches(const ENCODING *enc, const char *p, int c) +{ + return LITTLE2_CHAR_MATCHES(enc, p, c); +} + +static +int little2_isNameMin(const ENCODING *enc, const char *p) +{ + return LITTLE2_IS_NAME_CHAR_MINBPC(enc, p); +} + +static +int little2_isNmstrtMin(const ENCODING *enc, const char *p) +{ + return LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p); +} + +#undef VTABLE +#define VTABLE VTABLE1, little2_toUtf8, little2_toUtf16 + +#else /* not XML_MIN_SIZE */ + +#undef PREFIX +#define PREFIX(ident) little2_ ## ident +#define MINBPC(enc) 2 +/* CHAR_MATCHES is guaranteed to have MINBPC bytes available. */ +#define BYTE_TYPE(enc, p) LITTLE2_BYTE_TYPE(enc, p) +#define BYTE_TO_ASCII(enc, p) LITTLE2_BYTE_TO_ASCII(enc, p) +#define CHAR_MATCHES(enc, p, c) LITTLE2_CHAR_MATCHES(enc, p, c) +#define IS_NAME_CHAR(enc, p, n) 0 +#define IS_NAME_CHAR_MINBPC(enc, p) LITTLE2_IS_NAME_CHAR_MINBPC(enc, p) +#define IS_NMSTRT_CHAR(enc, p, n) (0) +#define IS_NMSTRT_CHAR_MINBPC(enc, p) LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p) + +#include "xmltok_impl.c" + +#undef MINBPC +#undef BYTE_TYPE +#undef BYTE_TO_ASCII +#undef CHAR_MATCHES +#undef IS_NAME_CHAR +#undef IS_NAME_CHAR_MINBPC +#undef IS_NMSTRT_CHAR +#undef IS_NMSTRT_CHAR_MINBPC +#undef IS_INVALID_CHAR + +#endif /* not XML_MIN_SIZE */ + +#ifdef XML_NS + +static const struct normal_encoding little2_encoding_ns = { + { VTABLE, 2, 0, +#if XML_BYTE_ORDER == 12 + 1 +#else +0 +#endif + }, + { +#include "asciitab.h" +#include "latin1tab.h" + }, + STANDARD_VTABLE(little2_) + }; + +#endif + +static const struct normal_encoding little2_encoding = { + { VTABLE, 2, 0, +#if XML_BYTE_ORDER == 12 + 1 +#else + 0 +#endif + }, + { +#define BT_COLON BT_NMSTRT +#include "asciitab.h" +#undef BT_COLON +#include "latin1tab.h" + }, + STANDARD_VTABLE(little2_) + }; + +#if XML_BYTE_ORDER != 21 + +#ifdef XML_NS + +static const struct normal_encoding internal_little2_encoding_ns = { + { VTABLE, 2, 0, 1 }, + { +#include "iasciitab.h" +#include "latin1tab.h" + }, + STANDARD_VTABLE(little2_) + }; + +#endif + +static const struct normal_encoding internal_little2_encoding = { + { VTABLE, 2, 0, 1 }, + { +#define BT_COLON BT_NMSTRT +#include "iasciitab.h" +#undef BT_COLON +#include "latin1tab.h" + }, + STANDARD_VTABLE(little2_) + }; + +#endif + + +#define BIG2_BYTE_TYPE(enc, p) \ + ((p)[0] == 0 \ + ? ((struct normal_encoding *)(enc))->type[(unsigned char)(p)[1]] \ + : unicode_byte_type((p)[0], (p)[1])) +#define BIG2_BYTE_TO_ASCII(enc, p) ((p)[0] == 0 ? (p)[1] : -1) +#define BIG2_CHAR_MATCHES(enc, p, c) ((p)[0] == 0 && (p)[1] == c) +#define BIG2_IS_NAME_CHAR_MINBPC(enc, p) \ + UCS2_GET_NAMING(namePages, (unsigned char)p[0], (unsigned char)p[1]) +#define BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p) \ + UCS2_GET_NAMING(nmstrtPages, (unsigned char)p[0], (unsigned char)p[1]) + +#ifdef XML_MIN_SIZE + +static +int big2_byteType(const ENCODING *enc, const char *p) +{ + return BIG2_BYTE_TYPE(enc, p); +} + +static +int big2_byteToAscii(const ENCODING *enc, const char *p) +{ + return BIG2_BYTE_TO_ASCII(enc, p); +} + +static +int big2_charMatches(const ENCODING *enc, const char *p, int c) +{ + return BIG2_CHAR_MATCHES(enc, p, c); +} + +static +int big2_isNameMin(const ENCODING *enc, const char *p) +{ + return BIG2_IS_NAME_CHAR_MINBPC(enc, p); +} + +static +int big2_isNmstrtMin(const ENCODING *enc, const char *p) +{ + return BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p); +} + +#undef VTABLE +#define VTABLE VTABLE1, big2_toUtf8, big2_toUtf16 + +#else /* not XML_MIN_SIZE */ + +#undef PREFIX +#define PREFIX(ident) big2_ ## ident +#define MINBPC(enc) 2 +/* CHAR_MATCHES is guaranteed to have MINBPC bytes available. */ +#define BYTE_TYPE(enc, p) BIG2_BYTE_TYPE(enc, p) +#define BYTE_TO_ASCII(enc, p) BIG2_BYTE_TO_ASCII(enc, p) +#define CHAR_MATCHES(enc, p, c) BIG2_CHAR_MATCHES(enc, p, c) +#define IS_NAME_CHAR(enc, p, n) 0 +#define IS_NAME_CHAR_MINBPC(enc, p) BIG2_IS_NAME_CHAR_MINBPC(enc, p) +#define IS_NMSTRT_CHAR(enc, p, n) (0) +#define IS_NMSTRT_CHAR_MINBPC(enc, p) BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p) + +#include "xmltok_impl.c" + +#undef MINBPC +#undef BYTE_TYPE +#undef BYTE_TO_ASCII +#undef CHAR_MATCHES +#undef IS_NAME_CHAR +#undef IS_NAME_CHAR_MINBPC +#undef IS_NMSTRT_CHAR +#undef IS_NMSTRT_CHAR_MINBPC +#undef IS_INVALID_CHAR + +#endif /* not XML_MIN_SIZE */ + +#ifdef XML_NS + +static const struct normal_encoding big2_encoding_ns = { + { VTABLE, 2, 0, +#if XML_BYTE_ORDER == 21 + 1 +#else +0 +#endif + }, + { +#include "asciitab.h" +#include "latin1tab.h" + }, + STANDARD_VTABLE(big2_) + }; + +#endif + +static const struct normal_encoding big2_encoding = { + { VTABLE, 2, 0, +#if XML_BYTE_ORDER == 21 + 1 +#else + 0 +#endif + }, + { +#define BT_COLON BT_NMSTRT +#include "asciitab.h" +#undef BT_COLON +#include "latin1tab.h" + }, + STANDARD_VTABLE(big2_) + }; + +#if XML_BYTE_ORDER != 12 + +#ifdef XML_NS + +static const struct normal_encoding internal_big2_encoding_ns = { + { VTABLE, 2, 0, 1 }, + { +#include "iasciitab.h" +#include "latin1tab.h" + }, + STANDARD_VTABLE(big2_) + }; + +#endif + +static const struct normal_encoding internal_big2_encoding = { + { VTABLE, 2, 0, 1 }, + { +#define BT_COLON BT_NMSTRT +#include "iasciitab.h" +#undef BT_COLON +#include "latin1tab.h" + }, + STANDARD_VTABLE(big2_) + }; + +#endif + +#undef PREFIX + +static +int streqci(const char *s1, const char *s2) +{ + for (;;) { + char c1 = *s1++; + char c2 = *s2++; + if ('a' <= c1 && c1 <= 'z') + c1 += 'A' - 'a'; + if ('a' <= c2 && c2 <= 'z') + c2 += 'A' - 'a'; + if (c1 != c2) + return 0; + if (!c1) + break; + } + return 1; +} + +static +void initUpdatePosition(const ENCODING *enc, const char *ptr, + const char *end, POSITION *pos) +{ + normal_updatePosition(&utf8_encoding.enc, ptr, end, pos); +} + +static +int toAscii(const ENCODING *enc, const char *ptr, const char *end) +{ + char buf[1]; + char *p = buf; + XmlUtf8Convert(enc, &ptr, end, &p, p + 1); + if (p == buf) + return -1; + else + return buf[0]; +} + +static +int isSpace(int c) +{ + switch (c) { + case 0x20: + case 0xD: + case 0xA: + case 0x9: + return 1; + } + return 0; +} + +/* Return 1 if there's just optional white space +or there's an S followed by name=val. */ +static +int parsePseudoAttribute(const ENCODING *enc, + const char *ptr, + const char *end, + const char **namePtr, + const char **valPtr, + const char **nextTokPtr) +{ + int c; + char open; + if (ptr == end) { + *namePtr = 0; + return 1; + } + if (!isSpace(toAscii(enc, ptr, end))) { + *nextTokPtr = ptr; + return 0; + } + do { + ptr += enc->minBytesPerChar; + } while (isSpace(toAscii(enc, ptr, end))); + if (ptr == end) { + *namePtr = 0; + return 1; + } + *namePtr = ptr; + for (;;) { + c = toAscii(enc, ptr, end); + if (c == -1) { + *nextTokPtr = ptr; + return 0; + } + if (c == '=') + break; + if (isSpace(c)) { + do { + ptr += enc->minBytesPerChar; + } while (isSpace(c = toAscii(enc, ptr, end))); + if (c != '=') { + *nextTokPtr = ptr; + return 0; + } + break; + } + ptr += enc->minBytesPerChar; + } + if (ptr == *namePtr) { + *nextTokPtr = ptr; + return 0; + } + ptr += enc->minBytesPerChar; + c = toAscii(enc, ptr, end); + while (isSpace(c)) { + ptr += enc->minBytesPerChar; + c = toAscii(enc, ptr, end); + } + if (c != '"' && c != '\'') { + *nextTokPtr = ptr; + return 0; + } + open = c; + ptr += enc->minBytesPerChar; + *valPtr = ptr; + for (;; ptr += enc->minBytesPerChar) { + c = toAscii(enc, ptr, end); + if (c == open) + break; + if (!('a' <= c && c <= 'z') + && !('A' <= c && c <= 'Z') + && !('0' <= c && c <= '9') + && c != '.' + && c != '-' + && c != '_') { + *nextTokPtr = ptr; + return 0; + } + } + *nextTokPtr = ptr + enc->minBytesPerChar; + return 1; +} + +static +int doParseXmlDecl(const ENCODING *(*encodingFinder)(const ENCODING *, + const char *, + const char *), + int isGeneralTextEntity, + const ENCODING *enc, + const char *ptr, + const char *end, + const char **badPtr, + const char **versionPtr, + const char **encodingName, + const ENCODING **encoding, + int *standalone) +{ + const char *val = 0; + const char *name = 0; + ptr += 5 * enc->minBytesPerChar; + end -= 2 * enc->minBytesPerChar; + if (!parsePseudoAttribute(enc, ptr, end, &name, &val, &ptr) || !name) { + *badPtr = ptr; + return 0; + } + if (!XmlNameMatchesAscii(enc, name, "version")) { + if (!isGeneralTextEntity) { + *badPtr = name; + return 0; + } + } + else { + if (versionPtr) + *versionPtr = val; + if (!parsePseudoAttribute(enc, ptr, end, &name, &val, &ptr)) { + *badPtr = ptr; + return 0; + } + if (!name) { + if (isGeneralTextEntity) { + /* a TextDecl must have an EncodingDecl */ + *badPtr = ptr; + return 0; + } + return 1; + } + } + if (XmlNameMatchesAscii(enc, name, "encoding")) { + int c = toAscii(enc, val, end); + if (!('a' <= c && c <= 'z') && !('A' <= c && c <= 'Z')) { + *badPtr = val; + return 0; + } + if (encodingName) + *encodingName = val; + if (encoding) + *encoding = encodingFinder(enc, val, ptr - enc->minBytesPerChar); + if (!parsePseudoAttribute(enc, ptr, end, &name, &val, &ptr)) { + *badPtr = ptr; + return 0; + } + if (!name) + return 1; + } + if (!XmlNameMatchesAscii(enc, name, "standalone") || isGeneralTextEntity) { + *badPtr = name; + return 0; + } + if (XmlNameMatchesAscii(enc, val, "yes")) { + if (standalone) + *standalone = 1; + } + else if (XmlNameMatchesAscii(enc, val, "no")) { + if (standalone) + *standalone = 0; + } + else { + *badPtr = val; + return 0; + } + while (isSpace(toAscii(enc, ptr, end))) + ptr += enc->minBytesPerChar; + if (ptr != end) { + *badPtr = ptr; + return 0; + } + return 1; +} + +static +int checkCharRefNumber(int result) +{ + switch (result >> 8) { +case 0xD8: case 0xD9: case 0xDA: case 0xDB: +case 0xDC: case 0xDD: case 0xDE: case 0xDF: + return -1; + case 0: + if (latin1_encoding.type[result] == BT_NONXML) + return -1; + break; + case 0xFF: + if (result == 0xFFFE || result == 0xFFFF) + return -1; + break; + } + return result; +} + +int XmlUtf8Encode(int c, char *buf) +{ + enum { + /* minN is minimum legal resulting value for N byte sequence */ + min2 = 0x80, + min3 = 0x800, + min4 = 0x10000 + }; + + if (c < 0) + return 0; + if (c < min2) { + buf[0] = (c | UTF8_cval1); + return 1; + } + if (c < min3) { + buf[0] = ((c >> 6) | UTF8_cval2); + buf[1] = ((c & 0x3f) | 0x80); + return 2; + } + if (c < min4) { + buf[0] = ((c >> 12) | UTF8_cval3); + buf[1] = (((c >> 6) & 0x3f) | 0x80); + buf[2] = ((c & 0x3f) | 0x80); + return 3; + } + if (c < 0x110000) { + buf[0] = ((c >> 18) | UTF8_cval4); + buf[1] = (((c >> 12) & 0x3f) | 0x80); + buf[2] = (((c >> 6) & 0x3f) | 0x80); + buf[3] = ((c & 0x3f) | 0x80); + return 4; + } + return 0; +} + +int XmlUtf16Encode(int charNum, unsigned short *buf) +{ + if (charNum < 0) + return 0; + if (charNum < 0x10000) { + buf[0] = charNum; + return 1; + } + if (charNum < 0x110000) { + charNum -= 0x10000; + buf[0] = (charNum >> 10) + 0xD800; + buf[1] = (charNum & 0x3FF) + 0xDC00; + return 2; + } + return 0; +} + +struct unknown_encoding { + struct normal_encoding normal; + int (*convert)(void *userData, const char *p); + void *userData; + unsigned short utf16[256]; + char utf8[256][4]; +}; + +int XmlSizeOfUnknownEncoding() +{ + return sizeof(struct unknown_encoding); +} + +static +int unknown_isName(const ENCODING *enc, const char *p) +{ + int c = ((const struct unknown_encoding *)enc) + ->convert(((const struct unknown_encoding *)enc)->userData, p); + if (c & ~0xFFFF) + return 0; + return UCS2_GET_NAMING(namePages, c >> 8, c & 0xFF); +} + +static +int unknown_isNmstrt(const ENCODING *enc, const char *p) +{ + int c = ((const struct unknown_encoding *)enc) + ->convert(((const struct unknown_encoding *)enc)->userData, p); + if (c & ~0xFFFF) + return 0; + return UCS2_GET_NAMING(nmstrtPages, c >> 8, c & 0xFF); +} + +static +int unknown_isInvalid(const ENCODING *enc, const char *p) +{ + int c = ((const struct unknown_encoding *)enc) + ->convert(((const struct unknown_encoding *)enc)->userData, p); + return (c & ~0xFFFF) || checkCharRefNumber(c) < 0; +} + +static +void unknown_toUtf8(const ENCODING *enc, + const char **fromP, const char *fromLim, + char **toP, const char *toLim) +{ + char buf[XML_UTF8_ENCODE_MAX]; + for (;;) { + const char *utf8; + int n; + if (*fromP == fromLim) + break; + utf8 = ((const struct unknown_encoding *)enc)->utf8[(unsigned char)**fromP]; + n = *utf8++; + if (n == 0) { + int c = ((const struct unknown_encoding *)enc) + ->convert(((const struct unknown_encoding *)enc)->userData, *fromP); + n = XmlUtf8Encode(c, buf); + if (n > toLim - *toP) + break; + utf8 = buf; + *fromP += ((const struct normal_encoding *)enc)->type[(unsigned char)**fromP] + - (BT_LEAD2 - 2); + } + else { + if (n > toLim - *toP) + break; + (*fromP)++; + } + do { + *(*toP)++ = *utf8++; + } while (--n != 0); + } +} + +static +void unknown_toUtf16(const ENCODING *enc, + const char **fromP, const char *fromLim, + unsigned short **toP, const unsigned short *toLim) +{ + while (*fromP != fromLim && *toP != toLim) { + unsigned short c + = ((const struct unknown_encoding *)enc)->utf16[(unsigned char)**fromP]; + if (c == 0) { + c = (unsigned short)((const struct unknown_encoding *)enc) + ->convert(((const struct unknown_encoding *)enc)->userData, *fromP); + *fromP += ((const struct normal_encoding *)enc)->type[(unsigned char)**fromP] + - (BT_LEAD2 - 2); + } + else + (*fromP)++; + *(*toP)++ = c; + } +} + +ENCODING * +XmlInitUnknownEncoding(void *mem, + int *table, + int (*convert)(void *userData, const char *p), + void *userData) +{ + int i; + struct unknown_encoding *e = mem; + for (i = 0; i < sizeof(struct normal_encoding); i++) + ((char *)mem)[i] = ((char *)&latin1_encoding)[i]; + for (i = 0; i < 128; i++) + if (latin1_encoding.type[i] != BT_OTHER + && latin1_encoding.type[i] != BT_NONXML + && table[i] != i) + return 0; + for (i = 0; i < 256; i++) { + int c = table[i]; + if (c == -1) { + e->normal.type[i] = BT_MALFORM; + /* This shouldn't really get used. */ + e->utf16[i] = 0xFFFF; + e->utf8[i][0] = 1; + e->utf8[i][1] = 0; + } + else if (c < 0) { + if (c < -4) + return 0; + e->normal.type[i] = BT_LEAD2 - (c + 2); + e->utf8[i][0] = 0; + e->utf16[i] = 0; + } + else if (c < 0x80) { + if (latin1_encoding.type[c] != BT_OTHER + && latin1_encoding.type[c] != BT_NONXML + && c != i) + return 0; + e->normal.type[i] = latin1_encoding.type[c]; + e->utf8[i][0] = 1; + e->utf8[i][1] = (char)c; + e->utf16[i] = c == 0 ? 0xFFFF : c; + } + else if (checkCharRefNumber(c) < 0) { + e->normal.type[i] = BT_NONXML; + /* This shouldn't really get used. */ + e->utf16[i] = 0xFFFF; + e->utf8[i][0] = 1; + e->utf8[i][1] = 0; + } + else { + if (c > 0xFFFF) + return 0; + if (UCS2_GET_NAMING(nmstrtPages, c >> 8, c & 0xff)) + e->normal.type[i] = BT_NMSTRT; + else if (UCS2_GET_NAMING(namePages, c >> 8, c & 0xff)) + e->normal.type[i] = BT_NAME; + else + e->normal.type[i] = BT_OTHER; + e->utf8[i][0] = (char)XmlUtf8Encode(c, e->utf8[i] + 1); + e->utf16[i] = c; + } + } + e->userData = userData; + e->convert = convert; + if (convert) { + e->normal.isName2 = unknown_isName; + e->normal.isName3 = unknown_isName; + e->normal.isName4 = unknown_isName; + e->normal.isNmstrt2 = unknown_isNmstrt; + e->normal.isNmstrt3 = unknown_isNmstrt; + e->normal.isNmstrt4 = unknown_isNmstrt; + e->normal.isInvalid2 = unknown_isInvalid; + e->normal.isInvalid3 = unknown_isInvalid; + e->normal.isInvalid4 = unknown_isInvalid; + } + e->normal.enc.utf8Convert = unknown_toUtf8; + e->normal.enc.utf16Convert = unknown_toUtf16; + return &(e->normal.enc); +} + +/* If this enumeration is changed, getEncodingIndex and encodings +must also be changed. */ +enum { + UNKNOWN_ENC = -1, + ISO_8859_1_ENC = 0, + US_ASCII_ENC, + UTF_8_ENC, + UTF_16_ENC, + UTF_16BE_ENC, + UTF_16LE_ENC, + /* must match encodingNames up to here */ + NO_ENC +}; + +static +int getEncodingIndex(const char *name) +{ + static const char *encodingNames[] = { + "ISO-8859-1", + "US-ASCII", + "UTF-8", + "UTF-16", + "UTF-16BE" + "UTF-16LE", + }; + int i; + if (name == 0) + return NO_ENC; + for (i = 0; i < sizeof(encodingNames)/sizeof(encodingNames[0]); i++) + if (streqci(name, encodingNames[i])) + return i; + return UNKNOWN_ENC; +} + +/* For binary compatibility, we store the index of the encoding specified +at initialization in the isUtf16 member. */ + +#define INIT_ENC_INDEX(enc) ((enc)->initEnc.isUtf16) + +/* This is what detects the encoding. +encodingTable maps from encoding indices to encodings; +INIT_ENC_INDEX(enc) is the index of the external (protocol) specified encoding; +state is XML_CONTENT_STATE if we're parsing an external text entity, +and XML_PROLOG_STATE otherwise. +*/ + + +static +int initScan(const ENCODING **encodingTable, + const INIT_ENCODING *enc, + int state, + const char *ptr, + const char *end, + const char **nextTokPtr) +{ + const ENCODING **encPtr; + + if (ptr == end) + return XML_TOK_NONE; + encPtr = enc->encPtr; + if (ptr + 1 == end) { + /* only a single byte available for auto-detection */ + /* a well-formed document entity must have more than one byte */ + if (state != XML_CONTENT_STATE) + return XML_TOK_PARTIAL; + /* so we're parsing an external text entity... */ + /* if UTF-16 was externally specified, then we need at least 2 bytes */ + switch (INIT_ENC_INDEX(enc)) { + case UTF_16_ENC: + case UTF_16LE_ENC: + case UTF_16BE_ENC: + return XML_TOK_PARTIAL; + } + switch ((unsigned char)*ptr) { + case 0xFE: + case 0xFF: + case 0xEF: /* possibly first byte of UTF-8 BOM */ + if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC + && state == XML_CONTENT_STATE) + break; + /* fall through */ + case 0x00: + case 0x3C: + return XML_TOK_PARTIAL; + } + } + else { + switch (((unsigned char)ptr[0] << 8) | (unsigned char)ptr[1]) { + case 0xFEFF: + if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC + && state == XML_CONTENT_STATE) + break; + *nextTokPtr = ptr + 2; + *encPtr = encodingTable[UTF_16BE_ENC]; + return XML_TOK_BOM; + /* 00 3C is handled in the default case */ + case 0x3C00: + if ((INIT_ENC_INDEX(enc) == UTF_16BE_ENC + || INIT_ENC_INDEX(enc) == UTF_16_ENC) + && state == XML_CONTENT_STATE) + break; + *encPtr = encodingTable[UTF_16LE_ENC]; + return XmlTok(*encPtr, state, ptr, end, nextTokPtr); + case 0xFFFE: + if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC + && state == XML_CONTENT_STATE) + break; + *nextTokPtr = ptr + 2; + *encPtr = encodingTable[UTF_16LE_ENC]; + return XML_TOK_BOM; + case 0xEFBB: + /* Maybe a UTF-8 BOM (EF BB BF) */ + /* If there's an explicitly specified (external) encoding + of ISO-8859-1 or some flavour of UTF-16 + and this is an external text entity, + don't look for the BOM, + because it might be a legal data. */ + if (state == XML_CONTENT_STATE) { + int e = INIT_ENC_INDEX(enc); + if (e == ISO_8859_1_ENC || e == UTF_16BE_ENC || e == UTF_16LE_ENC || e == UTF_16_ENC) + break; + } + if (ptr + 2 == end) + return XML_TOK_PARTIAL; + if ((unsigned char)ptr[2] == 0xBF) { + *encPtr = encodingTable[UTF_8_ENC]; + return XML_TOK_BOM; + } + break; + default: + if (ptr[0] == '\0') { + /* 0 isn't a legal data character. Furthermore a document entity can only + start with ASCII characters. So the only way this can fail to be big-endian + UTF-16 if it it's an external parsed general entity that's labelled as + UTF-16LE. */ + if (state == XML_CONTENT_STATE && INIT_ENC_INDEX(enc) == UTF_16LE_ENC) + break; + *encPtr = encodingTable[UTF_16BE_ENC]; + return XmlTok(*encPtr, state, ptr, end, nextTokPtr); + } + else if (ptr[1] == '\0') { + /* We could recover here in the case: + - parsing an external entity + - second byte is 0 + - no externally specified encoding + - no encoding declaration + by assuming UTF-16LE. But we don't, because this would mean when + presented just with a single byte, we couldn't reliably determine + whether we needed further bytes. */ + if (state == XML_CONTENT_STATE) + break; + *encPtr = encodingTable[UTF_16LE_ENC]; + return XmlTok(*encPtr, state, ptr, end, nextTokPtr); + } + break; + } + } + *encPtr = encodingTable[(int)INIT_ENC_INDEX(enc)]; + return XmlTok(*encPtr, state, ptr, end, nextTokPtr); +} + + +#define NS(x) x +#define ns(x) x +#include "xmltok_ns.c" +#undef NS +#undef ns + +#ifdef XML_NS + +#define NS(x) x ## NS +#define ns(x) x ## _ns + +#include "xmltok_ns.c" + +#undef NS +#undef ns + +ENCODING * +XmlInitUnknownEncodingNS(void *mem, + int *table, + int (*convert)(void *userData, const char *p), + void *userData) +{ + ENCODING *enc = XmlInitUnknownEncoding(mem, table, convert, userData); + if (enc) + ((struct normal_encoding *)enc)->type[':'] = BT_COLON; + return enc; +} + +#endif /* XML_NS */ diff --git a/protocols/jabber/xmltok.h b/protocols/jabber/xmltok.h new file mode 100644 index 00000000..06544d15 --- /dev/null +++ b/protocols/jabber/xmltok.h @@ -0,0 +1,307 @@ +/* +The contents of this file are subject to the Mozilla Public License +Version 1.1 (the "License"); you may not use this file except in +compliance with the License. You may obtain a copy of the License at +http://www.mozilla.org/MPL/ + +Software distributed under the License is distributed on an "AS IS" +basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +License for the specific language governing rights and limitations +under the License. + +The Original Code is expat. + +The Initial Developer of the Original Code is James Clark. +Portions created by James Clark are Copyright (C) 1998, 1999 +James Clark. All Rights Reserved. + +Contributor(s): + +Alternatively, the contents of this file may be used under the terms +of the GNU General Public License (the "GPL"), in which case the +provisions of the GPL are applicable instead of those above. If you +wish to allow use of your version of this file only under the terms of +the GPL and not to allow others to use your version of this file under +the MPL, indicate your decision by deleting the provisions above and +replace them with the notice and other provisions required by the +GPL. If you do not delete the provisions above, a recipient may use +your version of this file under either the MPL or the GPL. +*/ + +#ifndef XmlTok_INCLUDED +#define XmlTok_INCLUDED 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef XMLTOKAPI +#define XMLTOKAPI /* as nothing */ +#endif + +/* The following token may be returned by XmlContentTok */ +#define XML_TOK_TRAILING_RSQB -5 /* ] or ]] at the end of the scan; might be start of + illegal ]]> sequence */ +/* The following tokens may be returned by both XmlPrologTok and XmlContentTok */ +#define XML_TOK_NONE -4 /* The string to be scanned is empty */ +#define XML_TOK_TRAILING_CR -3 /* A CR at the end of the scan; + might be part of CRLF sequence */ +#define XML_TOK_PARTIAL_CHAR -2 /* only part of a multibyte sequence */ +#define XML_TOK_PARTIAL -1 /* only part of a token */ +#define XML_TOK_INVALID 0 + +/* The following tokens are returned by XmlContentTok; some are also + returned by XmlAttributeValueTok, XmlEntityTok, XmlCdataSectionTok */ + +#define XML_TOK_START_TAG_WITH_ATTS 1 +#define XML_TOK_START_TAG_NO_ATTS 2 +#define XML_TOK_EMPTY_ELEMENT_WITH_ATTS 3 /* empty element tag <e/> */ +#define XML_TOK_EMPTY_ELEMENT_NO_ATTS 4 +#define XML_TOK_END_TAG 5 +#define XML_TOK_DATA_CHARS 6 +#define XML_TOK_DATA_NEWLINE 7 +#define XML_TOK_CDATA_SECT_OPEN 8 +#define XML_TOK_ENTITY_REF 9 +#define XML_TOK_CHAR_REF 10 /* numeric character reference */ + +/* The following tokens may be returned by both XmlPrologTok and XmlContentTok */ +#define XML_TOK_PI 11 /* processing instruction */ +#define XML_TOK_XML_DECL 12 /* XML decl or text decl */ +#define XML_TOK_COMMENT 13 +#define XML_TOK_BOM 14 /* Byte order mark */ + +/* The following tokens are returned only by XmlPrologTok */ +#define XML_TOK_PROLOG_S 15 +#define XML_TOK_DECL_OPEN 16 /* <!foo */ +#define XML_TOK_DECL_CLOSE 17 /* > */ +#define XML_TOK_NAME 18 +#define XML_TOK_NMTOKEN 19 +#define XML_TOK_POUND_NAME 20 /* #name */ +#define XML_TOK_OR 21 /* | */ +#define XML_TOK_PERCENT 22 +#define XML_TOK_OPEN_PAREN 23 +#define XML_TOK_CLOSE_PAREN 24 +#define XML_TOK_OPEN_BRACKET 25 +#define XML_TOK_CLOSE_BRACKET 26 +#define XML_TOK_LITERAL 27 +#define XML_TOK_PARAM_ENTITY_REF 28 +#define XML_TOK_INSTANCE_START 29 + +/* The following occur only in element type declarations */ +#define XML_TOK_NAME_QUESTION 30 /* name? */ +#define XML_TOK_NAME_ASTERISK 31 /* name* */ +#define XML_TOK_NAME_PLUS 32 /* name+ */ +#define XML_TOK_COND_SECT_OPEN 33 /* <![ */ +#define XML_TOK_COND_SECT_CLOSE 34 /* ]]> */ +#define XML_TOK_CLOSE_PAREN_QUESTION 35 /* )? */ +#define XML_TOK_CLOSE_PAREN_ASTERISK 36 /* )* */ +#define XML_TOK_CLOSE_PAREN_PLUS 37 /* )+ */ +#define XML_TOK_COMMA 38 + +/* The following token is returned only by XmlAttributeValueTok */ +#define XML_TOK_ATTRIBUTE_VALUE_S 39 + +/* The following token is returned only by XmlCdataSectionTok */ +#define XML_TOK_CDATA_SECT_CLOSE 40 + +/* With namespace processing this is returned by XmlPrologTok + for a name with a colon. */ +#define XML_TOK_PREFIXED_NAME 41 + +#define XML_N_STATES 3 +#define XML_PROLOG_STATE 0 +#define XML_CONTENT_STATE 1 +#define XML_CDATA_SECTION_STATE 2 + +#define XML_N_LITERAL_TYPES 2 +#define XML_ATTRIBUTE_VALUE_LITERAL 0 +#define XML_ENTITY_VALUE_LITERAL 1 + +/* The size of the buffer passed to XmlUtf8Encode must be at least this. */ +#define XML_UTF8_ENCODE_MAX 4 +/* The size of the buffer passed to XmlUtf16Encode must be at least this. */ +#define XML_UTF16_ENCODE_MAX 2 + +typedef struct position { + /* first line and first column are 0 not 1 */ + unsigned long lineNumber; + unsigned long columnNumber; +} POSITION; + +typedef struct { + const char *name; + const char *valuePtr; + const char *valueEnd; + char normalized; +} ATTRIBUTE; + +struct encoding; +typedef struct encoding ENCODING; + +struct encoding { + int (*scanners[XML_N_STATES])(const ENCODING *, + const char *, + const char *, + const char **); + int (*literalScanners[XML_N_LITERAL_TYPES])(const ENCODING *, + const char *, + const char *, + const char **); + int (*sameName)(const ENCODING *, + const char *, const char *); + int (*nameMatchesAscii)(const ENCODING *, + const char *, const char *); + int (*nameLength)(const ENCODING *, const char *); + const char *(*skipS)(const ENCODING *, const char *); + int (*getAtts)(const ENCODING *enc, const char *ptr, + int attsMax, ATTRIBUTE *atts); + int (*charRefNumber)(const ENCODING *enc, const char *ptr); + int (*predefinedEntityName)(const ENCODING *, const char *, const char *); + void (*updatePosition)(const ENCODING *, + const char *ptr, + const char *end, + POSITION *); + int (*isPublicId)(const ENCODING *enc, const char *ptr, const char *end, + const char **badPtr); + void (*utf8Convert)(const ENCODING *enc, + const char **fromP, + const char *fromLim, + char **toP, + const char *toLim); + void (*utf16Convert)(const ENCODING *enc, + const char **fromP, + const char *fromLim, + unsigned short **toP, + const unsigned short *toLim); + int minBytesPerChar; + char isUtf8; + char isUtf16; +}; + +/* +Scan the string starting at ptr until the end of the next complete token, +but do not scan past eptr. Return an integer giving the type of token. + +Return XML_TOK_NONE when ptr == eptr; nextTokPtr will not be set. + +Return XML_TOK_PARTIAL when the string does not contain a complete token; +nextTokPtr will not be set. + +Return XML_TOK_INVALID when the string does not start a valid token; nextTokPtr +will be set to point to the character which made the token invalid. + +Otherwise the string starts with a valid token; nextTokPtr will be set to point +to the character following the end of that token. + +Each data character counts as a single token, but adjacent data characters +may be returned together. Similarly for characters in the prolog outside +literals, comments and processing instructions. +*/ + + +#define XmlTok(enc, state, ptr, end, nextTokPtr) \ + (((enc)->scanners[state])(enc, ptr, end, nextTokPtr)) + +#define XmlPrologTok(enc, ptr, end, nextTokPtr) \ + XmlTok(enc, XML_PROLOG_STATE, ptr, end, nextTokPtr) + +#define XmlContentTok(enc, ptr, end, nextTokPtr) \ + XmlTok(enc, XML_CONTENT_STATE, ptr, end, nextTokPtr) + +#define XmlCdataSectionTok(enc, ptr, end, nextTokPtr) \ + XmlTok(enc, XML_CDATA_SECTION_STATE, ptr, end, nextTokPtr) + +/* This is used for performing a 2nd-level tokenization on +the content of a literal that has already been returned by XmlTok. */ + +#define XmlLiteralTok(enc, literalType, ptr, end, nextTokPtr) \ + (((enc)->literalScanners[literalType])(enc, ptr, end, nextTokPtr)) + +#define XmlAttributeValueTok(enc, ptr, end, nextTokPtr) \ + XmlLiteralTok(enc, XML_ATTRIBUTE_VALUE_LITERAL, ptr, end, nextTokPtr) + +#define XmlEntityValueTok(enc, ptr, end, nextTokPtr) \ + XmlLiteralTok(enc, XML_ENTITY_VALUE_LITERAL, ptr, end, nextTokPtr) + +#define XmlSameName(enc, ptr1, ptr2) (((enc)->sameName)(enc, ptr1, ptr2)) + +#define XmlNameMatchesAscii(enc, ptr1, ptr2) \ + (((enc)->nameMatchesAscii)(enc, ptr1, ptr2)) + +#define XmlNameLength(enc, ptr) \ + (((enc)->nameLength)(enc, ptr)) + +#define XmlSkipS(enc, ptr) \ + (((enc)->skipS)(enc, ptr)) + +#define XmlGetAttributes(enc, ptr, attsMax, atts) \ + (((enc)->getAtts)(enc, ptr, attsMax, atts)) + +#define XmlCharRefNumber(enc, ptr) \ + (((enc)->charRefNumber)(enc, ptr)) + +#define XmlPredefinedEntityName(enc, ptr, end) \ + (((enc)->predefinedEntityName)(enc, ptr, end)) + +#define XmlUpdatePosition(enc, ptr, end, pos) \ + (((enc)->updatePosition)(enc, ptr, end, pos)) + +#define XmlIsPublicId(enc, ptr, end, badPtr) \ + (((enc)->isPublicId)(enc, ptr, end, badPtr)) + +#define XmlUtf8Convert(enc, fromP, fromLim, toP, toLim) \ + (((enc)->utf8Convert)(enc, fromP, fromLim, toP, toLim)) + +#define XmlUtf16Convert(enc, fromP, fromLim, toP, toLim) \ + (((enc)->utf16Convert)(enc, fromP, fromLim, toP, toLim)) + +typedef struct { + ENCODING initEnc; + const ENCODING **encPtr; +} INIT_ENCODING; + +int XMLTOKAPI XmlParseXmlDecl(int isGeneralTextEntity, + const ENCODING *enc, + const char *ptr, + const char *end, + const char **badPtr, + const char **versionPtr, + const char **encodingNamePtr, + const ENCODING **namedEncodingPtr, + int *standalonePtr); + +int XMLTOKAPI XmlInitEncoding(INIT_ENCODING *, const ENCODING **, const char *name); +const ENCODING XMLTOKAPI *XmlGetUtf8InternalEncoding(); +const ENCODING XMLTOKAPI *XmlGetUtf16InternalEncoding(); +int XMLTOKAPI XmlUtf8Encode(int charNumber, char *buf); +int XMLTOKAPI XmlUtf16Encode(int charNumber, unsigned short *buf); + +int XMLTOKAPI XmlSizeOfUnknownEncoding(); +ENCODING XMLTOKAPI * +XmlInitUnknownEncoding(void *mem, + int *table, + int (*conv)(void *userData, const char *p), + void *userData); + +int XMLTOKAPI XmlParseXmlDeclNS(int isGeneralTextEntity, + const ENCODING *enc, + const char *ptr, + const char *end, + const char **badPtr, + const char **versionPtr, + const char **encodingNamePtr, + const ENCODING **namedEncodingPtr, + int *standalonePtr); +int XMLTOKAPI XmlInitEncodingNS(INIT_ENCODING *, const ENCODING **, const char *name); +const ENCODING XMLTOKAPI *XmlGetUtf8InternalEncodingNS(); +const ENCODING XMLTOKAPI *XmlGetUtf16InternalEncodingNS(); +ENCODING XMLTOKAPI * +XmlInitUnknownEncodingNS(void *mem, + int *table, + int (*conv)(void *userData, const char *p), + void *userData); +#ifdef __cplusplus +} +#endif + +#endif /* not XmlTok_INCLUDED */ diff --git a/protocols/jabber/xmltok_impl.c b/protocols/jabber/xmltok_impl.c new file mode 100644 index 00000000..de11c2a8 --- /dev/null +++ b/protocols/jabber/xmltok_impl.c @@ -0,0 +1,1737 @@ +/* +The contents of this file are subject to the Mozilla Public License +Version 1.1 (the "License"); you may not use this file except in +compliance with the License. You may obtain a copy of the License at +http://www.mozilla.org/MPL/ + +Software distributed under the License is distributed on an "AS IS" +basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +License for the specific language governing rights and limitations +under the License. + +The Original Code is expat. + +The Initial Developer of the Original Code is James Clark. +Portions created by James Clark are Copyright (C) 1998, 1999 +James Clark. All Rights Reserved. + +Contributor(s): + +*/ + +#ifndef IS_INVALID_CHAR +#define IS_INVALID_CHAR(enc, ptr, n) (0) +#endif + +#define INVALID_LEAD_CASE(n, ptr, nextTokPtr) \ + case BT_LEAD ## n: \ + if (end - ptr < n) \ + return XML_TOK_PARTIAL_CHAR; \ + if (IS_INVALID_CHAR(enc, ptr, n)) { \ + *(nextTokPtr) = (ptr); \ + return XML_TOK_INVALID; \ + } \ + ptr += n; \ + break; + +#define INVALID_CASES(ptr, nextTokPtr) \ + INVALID_LEAD_CASE(2, ptr, nextTokPtr) \ + INVALID_LEAD_CASE(3, ptr, nextTokPtr) \ + INVALID_LEAD_CASE(4, ptr, nextTokPtr) \ + case BT_NONXML: \ + case BT_MALFORM: \ + case BT_TRAIL: \ + *(nextTokPtr) = (ptr); \ + return XML_TOK_INVALID; + +#define CHECK_NAME_CASE(n, enc, ptr, end, nextTokPtr) \ + case BT_LEAD ## n: \ + if (end - ptr < n) \ + return XML_TOK_PARTIAL_CHAR; \ + if (!IS_NAME_CHAR(enc, ptr, n)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; \ + } \ + ptr += n; \ + break; + +#define CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) \ + case BT_NONASCII: \ + if (!IS_NAME_CHAR_MINBPC(enc, ptr)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; \ + } \ + case BT_NMSTRT: \ + case BT_HEX: \ + case BT_DIGIT: \ + case BT_NAME: \ + case BT_MINUS: \ + ptr += MINBPC(enc); \ + break; \ + CHECK_NAME_CASE(2, enc, ptr, end, nextTokPtr) \ + CHECK_NAME_CASE(3, enc, ptr, end, nextTokPtr) \ + CHECK_NAME_CASE(4, enc, ptr, end, nextTokPtr) + +#define CHECK_NMSTRT_CASE(n, enc, ptr, end, nextTokPtr) \ + case BT_LEAD ## n: \ + if (end - ptr < n) \ + return XML_TOK_PARTIAL_CHAR; \ + if (!IS_NMSTRT_CHAR(enc, ptr, n)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; \ + } \ + ptr += n; \ + break; + +#define CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) \ + case BT_NONASCII: \ + if (!IS_NMSTRT_CHAR_MINBPC(enc, ptr)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; \ + } \ + case BT_NMSTRT: \ + case BT_HEX: \ + ptr += MINBPC(enc); \ + break; \ + CHECK_NMSTRT_CASE(2, enc, ptr, end, nextTokPtr) \ + CHECK_NMSTRT_CASE(3, enc, ptr, end, nextTokPtr) \ + CHECK_NMSTRT_CASE(4, enc, ptr, end, nextTokPtr) + +#ifndef PREFIX +#define PREFIX(ident) ident +#endif + +/* ptr points to character following "<!-" */ + +static +int PREFIX(scanComment)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + if (ptr != end) { + if (!CHAR_MATCHES(enc, ptr, '-')) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + ptr += MINBPC(enc); + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + INVALID_CASES(ptr, nextTokPtr) + case BT_MINUS: + if ((ptr += MINBPC(enc)) == end) + return XML_TOK_PARTIAL; + if (CHAR_MATCHES(enc, ptr, '-')) { + if ((ptr += MINBPC(enc)) == end) + return XML_TOK_PARTIAL; + if (!CHAR_MATCHES(enc, ptr, '>')) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_COMMENT; + } + break; + default: + ptr += MINBPC(enc); + break; + } + } + } + return XML_TOK_PARTIAL; +} + +/* ptr points to character following "<!" */ + +static +int PREFIX(scanDecl)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + if (ptr == end) + return XML_TOK_PARTIAL; + switch (BYTE_TYPE(enc, ptr)) { + case BT_MINUS: + return PREFIX(scanComment)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_LSQB: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_COND_SECT_OPEN; + case BT_NMSTRT: + case BT_HEX: + ptr += MINBPC(enc); + break; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + case BT_PERCNT: + if (ptr + MINBPC(enc) == end) + return XML_TOK_PARTIAL; + /* don't allow <!ENTITY% foo "whatever"> */ + switch (BYTE_TYPE(enc, ptr + MINBPC(enc))) { +case BT_S: case BT_CR: case BT_LF: case BT_PERCNT: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + /* fall through */ +case BT_S: case BT_CR: case BT_LF: + *nextTokPtr = ptr; + return XML_TOK_DECL_OPEN; + case BT_NMSTRT: + case BT_HEX: + ptr += MINBPC(enc); + break; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return XML_TOK_PARTIAL; +} + +static +int PREFIX(checkPiTarget)(const ENCODING *enc, const char *ptr, const char *end, int *tokPtr) +{ + int upper = 0; + *tokPtr = XML_TOK_PI; + if (end - ptr != MINBPC(enc)*3) + return 1; + switch (BYTE_TO_ASCII(enc, ptr)) { + case 'x': + break; + case 'X': + upper = 1; + break; + default: + return 1; + } + ptr += MINBPC(enc); + switch (BYTE_TO_ASCII(enc, ptr)) { + case 'm': + break; + case 'M': + upper = 1; + break; + default: + return 1; + } + ptr += MINBPC(enc); + switch (BYTE_TO_ASCII(enc, ptr)) { + case 'l': + break; + case 'L': + upper = 1; + break; + default: + return 1; + } + if (upper) + return 0; + *tokPtr = XML_TOK_XML_DECL; + return 1; +} + +/* ptr points to character following "<?" */ + +static +int PREFIX(scanPi)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + int tok; + const char *target = ptr; + if (ptr == end) + return XML_TOK_PARTIAL; + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) +case BT_S: case BT_CR: case BT_LF: + if (!PREFIX(checkPiTarget)(enc, target, ptr, &tok)) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + ptr += MINBPC(enc); + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + INVALID_CASES(ptr, nextTokPtr) + case BT_QUEST: + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + if (CHAR_MATCHES(enc, ptr, '>')) { + *nextTokPtr = ptr + MINBPC(enc); + return tok; + } + break; + default: + ptr += MINBPC(enc); + break; + } + } + return XML_TOK_PARTIAL; + case BT_QUEST: + if (!PREFIX(checkPiTarget)(enc, target, ptr, &tok)) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + if (CHAR_MATCHES(enc, ptr, '>')) { + *nextTokPtr = ptr + MINBPC(enc); + return tok; + } + /* fall through */ + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return XML_TOK_PARTIAL; +} + + +static +int PREFIX(scanCdataSection)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + int i; + /* CDATA[ */ + if (end - ptr < 6 * MINBPC(enc)) + return XML_TOK_PARTIAL; + for (i = 0; i < 6; i++, ptr += MINBPC(enc)) { + if (!CHAR_MATCHES(enc, ptr, "CDATA["[i])) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + *nextTokPtr = ptr; + return XML_TOK_CDATA_SECT_OPEN; +} + +static +int PREFIX(cdataSectionTok)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + if (ptr == end) + return XML_TOK_NONE; + if (MINBPC(enc) > 1) { + size_t n = end - ptr; + if (n & (MINBPC(enc) - 1)) { + n &= ~(MINBPC(enc) - 1); + if (n == 0) + return XML_TOK_PARTIAL; + end = ptr + n; + } + } + switch (BYTE_TYPE(enc, ptr)) { + case BT_RSQB: + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + if (!CHAR_MATCHES(enc, ptr, ']')) + break; + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + if (!CHAR_MATCHES(enc, ptr, '>')) { + ptr -= MINBPC(enc); + break; + } + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_CDATA_SECT_CLOSE; + case BT_CR: + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + if (BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC(enc); + *nextTokPtr = ptr; + return XML_TOK_DATA_NEWLINE; + case BT_LF: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_DATA_NEWLINE; + INVALID_CASES(ptr, nextTokPtr) + default: + ptr += MINBPC(enc); + break; + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + if (end - ptr < n || IS_INVALID_CHAR(enc, ptr, n)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_DATA_CHARS; \ + } \ + ptr += n; \ + break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_NONXML: + case BT_MALFORM: + case BT_TRAIL: + case BT_CR: + case BT_LF: + case BT_RSQB: + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + default: + ptr += MINBPC(enc); + break; + } + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; +} + +/* ptr points to character following "</" */ + +static +int PREFIX(scanEndTag)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + if (ptr == end) + return XML_TOK_PARTIAL; + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) +case BT_S: case BT_CR: case BT_LF: + for (ptr += MINBPC(enc); ptr != end; ptr += MINBPC(enc)) { + switch (BYTE_TYPE(enc, ptr)) { + case BT_S: case BT_CR: case BT_LF: + break; + case BT_GT: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_END_TAG; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return XML_TOK_PARTIAL; +#ifdef XML_NS + case BT_COLON: + /* no need to check qname syntax here, since end-tag must match exactly */ + ptr += MINBPC(enc); + break; +#endif + case BT_GT: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_END_TAG; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return XML_TOK_PARTIAL; +} + +/* ptr points to character following "&#X" */ + +static +int PREFIX(scanHexCharRef)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + if (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + case BT_DIGIT: + case BT_HEX: + break; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + for (ptr += MINBPC(enc); ptr != end; ptr += MINBPC(enc)) { + switch (BYTE_TYPE(enc, ptr)) { + case BT_DIGIT: + case BT_HEX: + break; + case BT_SEMI: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_CHAR_REF; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + } + return XML_TOK_PARTIAL; +} + +/* ptr points to character following "&#" */ + +static +int PREFIX(scanCharRef)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + if (ptr != end) { + if (CHAR_MATCHES(enc, ptr, 'x')) + return PREFIX(scanHexCharRef)(enc, ptr + MINBPC(enc), end, nextTokPtr); + switch (BYTE_TYPE(enc, ptr)) { + case BT_DIGIT: + break; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + for (ptr += MINBPC(enc); ptr != end; ptr += MINBPC(enc)) { + switch (BYTE_TYPE(enc, ptr)) { + case BT_DIGIT: + break; + case BT_SEMI: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_CHAR_REF; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + } + return XML_TOK_PARTIAL; +} + +/* ptr points to character following "&" */ + +static +int PREFIX(scanRef)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + if (ptr == end) + return XML_TOK_PARTIAL; + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) + case BT_NUM: + return PREFIX(scanCharRef)(enc, ptr + MINBPC(enc), end, nextTokPtr); + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) + case BT_SEMI: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_ENTITY_REF; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return XML_TOK_PARTIAL; +} + +/* ptr points to character following first character of attribute name */ + +static +int PREFIX(scanAtts)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ +#ifdef XML_NS + int hadColon = 0; +#endif + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) +#ifdef XML_NS + case BT_COLON: + if (hadColon) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + hadColon = 1; + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + break; +#endif +case BT_S: case BT_CR: case BT_LF: + for (;;) { + int t; + + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + t = BYTE_TYPE(enc, ptr); + if (t == BT_EQUALS) + break; + switch (t) { + case BT_S: + case BT_LF: + case BT_CR: + break; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + /* fall through */ + case BT_EQUALS: + { + int open; +#ifdef XML_NS + hadColon = 0; +#endif + for (;;) { + + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + open = BYTE_TYPE(enc, ptr); + if (open == BT_QUOT || open == BT_APOS) + break; + switch (open) { + case BT_S: + case BT_LF: + case BT_CR: + break; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + ptr += MINBPC(enc); + /* in attribute value */ + for (;;) { + int t; + if (ptr == end) + return XML_TOK_PARTIAL; + t = BYTE_TYPE(enc, ptr); + if (t == open) + break; + switch (t) { + INVALID_CASES(ptr, nextTokPtr) + case BT_AMP: + { + int tok = PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, &ptr); + if (tok <= 0) { + if (tok == XML_TOK_INVALID) + *nextTokPtr = ptr; + return tok; + } + break; + } + case BT_LT: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + default: + ptr += MINBPC(enc); + break; + } + } + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + switch (BYTE_TYPE(enc, ptr)) { + case BT_S: + case BT_CR: + case BT_LF: + break; + case BT_SOL: + goto sol; + case BT_GT: + goto gt; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + /* ptr points to closing quote */ + for (;;) { + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) + case BT_S: case BT_CR: case BT_LF: + continue; + case BT_GT: +gt: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_START_TAG_WITH_ATTS; + case BT_SOL: +sol: + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + if (!CHAR_MATCHES(enc, ptr, '>')) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_EMPTY_ELEMENT_WITH_ATTS; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + break; + } + break; + } + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return XML_TOK_PARTIAL; +} + +/* ptr points to character following "<" */ + +static +int PREFIX(scanLt)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ +#ifdef XML_NS + int hadColon; +#endif + if (ptr == end) + return XML_TOK_PARTIAL; + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) + case BT_EXCL: + if ((ptr += MINBPC(enc)) == end) + return XML_TOK_PARTIAL; + switch (BYTE_TYPE(enc, ptr)) { + case BT_MINUS: + return PREFIX(scanComment)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_LSQB: + return PREFIX(scanCdataSection)(enc, ptr + MINBPC(enc), end, nextTokPtr); + } + *nextTokPtr = ptr; + return XML_TOK_INVALID; + case BT_QUEST: + return PREFIX(scanPi)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_SOL: + return PREFIX(scanEndTag)(enc, ptr + MINBPC(enc), end, nextTokPtr); + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } +#ifdef XML_NS + hadColon = 0; +#endif + /* we have a start-tag */ + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) +#ifdef XML_NS + case BT_COLON: + if (hadColon) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + hadColon = 1; + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + break; +#endif +case BT_S: case BT_CR: case BT_LF: + { + ptr += MINBPC(enc); + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) + case BT_GT: + goto gt; + case BT_SOL: + goto sol; + case BT_S: case BT_CR: case BT_LF: + ptr += MINBPC(enc); + continue; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + return PREFIX(scanAtts)(enc, ptr, end, nextTokPtr); + } + return XML_TOK_PARTIAL; + } + case BT_GT: +gt: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_START_TAG_NO_ATTS; + case BT_SOL: +sol: + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + if (!CHAR_MATCHES(enc, ptr, '>')) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_EMPTY_ELEMENT_NO_ATTS; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return XML_TOK_PARTIAL; +} + +static +int PREFIX(contentTok)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + if (ptr == end) + return XML_TOK_NONE; + if (MINBPC(enc) > 1) { + size_t n = end - ptr; + if (n & (MINBPC(enc) - 1)) { + n &= ~(MINBPC(enc) - 1); + if (n == 0) + return XML_TOK_PARTIAL; + end = ptr + n; + } + } + switch (BYTE_TYPE(enc, ptr)) { + case BT_LT: + return PREFIX(scanLt)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_AMP: + return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_CR: + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_TRAILING_CR; + if (BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC(enc); + *nextTokPtr = ptr; + return XML_TOK_DATA_NEWLINE; + case BT_LF: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_DATA_NEWLINE; + case BT_RSQB: + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_TRAILING_RSQB; + if (!CHAR_MATCHES(enc, ptr, ']')) + break; + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_TRAILING_RSQB; + if (!CHAR_MATCHES(enc, ptr, '>')) { + ptr -= MINBPC(enc); + break; + } + *nextTokPtr = ptr; + return XML_TOK_INVALID; + INVALID_CASES(ptr, nextTokPtr) + default: + ptr += MINBPC(enc); + break; + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + if (end - ptr < n || IS_INVALID_CHAR(enc, ptr, n)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_DATA_CHARS; \ + } \ + ptr += n; \ + break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_RSQB: + if (ptr + MINBPC(enc) != end) { + if (!CHAR_MATCHES(enc, ptr + MINBPC(enc), ']')) { + ptr += MINBPC(enc); + break; + } + if (ptr + 2*MINBPC(enc) != end) { + if (!CHAR_MATCHES(enc, ptr + 2*MINBPC(enc), '>')) { + ptr += MINBPC(enc); + break; + } + *nextTokPtr = ptr + 2*MINBPC(enc); + return XML_TOK_INVALID; + } + } + /* fall through */ + case BT_AMP: + case BT_LT: + case BT_NONXML: + case BT_MALFORM: + case BT_TRAIL: + case BT_CR: + case BT_LF: + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + default: + ptr += MINBPC(enc); + break; + } + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; +} + +/* ptr points to character following "%" */ + +static +int PREFIX(scanPercent)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + if (ptr == end) + return XML_TOK_PARTIAL; + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) +case BT_S: case BT_LF: case BT_CR: case BT_PERCNT: + *nextTokPtr = ptr; + return XML_TOK_PERCENT; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) + case BT_SEMI: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_PARAM_ENTITY_REF; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return XML_TOK_PARTIAL; +} + +static +int PREFIX(scanPoundName)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + if (ptr == end) + return XML_TOK_PARTIAL; + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) +case BT_CR: case BT_LF: case BT_S: +case BT_RPAR: case BT_GT: case BT_PERCNT: case BT_VERBAR: + *nextTokPtr = ptr; + return XML_TOK_POUND_NAME; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return XML_TOK_PARTIAL; +} + +static +int PREFIX(scanLit)(int open, const ENCODING *enc, + const char *ptr, const char *end, + const char **nextTokPtr) +{ + while (ptr != end) { + int t = BYTE_TYPE(enc, ptr); + switch (t) { + INVALID_CASES(ptr, nextTokPtr) + case BT_QUOT: + case BT_APOS: + ptr += MINBPC(enc); + if (t != open) + break; + if (ptr == end) + return XML_TOK_PARTIAL; + *nextTokPtr = ptr; + switch (BYTE_TYPE(enc, ptr)) { + case BT_S: case BT_CR: case BT_LF: + case BT_GT: case BT_PERCNT: case BT_LSQB: + return XML_TOK_LITERAL; + default: + return XML_TOK_INVALID; + } + default: + ptr += MINBPC(enc); + break; + } + } + return XML_TOK_PARTIAL; +} + +static +int PREFIX(prologTok)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + int tok; + if (ptr == end) + return XML_TOK_NONE; + if (MINBPC(enc) > 1) { + size_t n = end - ptr; + if (n & (MINBPC(enc) - 1)) { + n &= ~(MINBPC(enc) - 1); + if (n == 0) + return XML_TOK_PARTIAL; + end = ptr + n; + } + } + switch (BYTE_TYPE(enc, ptr)) { + case BT_QUOT: + return PREFIX(scanLit)(BT_QUOT, enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_APOS: + return PREFIX(scanLit)(BT_APOS, enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_LT: + { + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + switch (BYTE_TYPE(enc, ptr)) { + case BT_EXCL: + return PREFIX(scanDecl)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_QUEST: + return PREFIX(scanPi)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_NMSTRT: + case BT_HEX: + case BT_NONASCII: + case BT_LEAD2: + case BT_LEAD3: + case BT_LEAD4: + *nextTokPtr = ptr - MINBPC(enc); + return XML_TOK_INSTANCE_START; + } + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + case BT_CR: + if (ptr + MINBPC(enc) == end) + return XML_TOK_TRAILING_CR; + /* fall through */ +case BT_S: case BT_LF: + for (;;) { + ptr += MINBPC(enc); + if (ptr == end) + break; + switch (BYTE_TYPE(enc, ptr)) { + case BT_S: case BT_LF: + break; + case BT_CR: + /* don't split CR/LF pair */ + if (ptr + MINBPC(enc) != end) + break; + /* fall through */ + default: + *nextTokPtr = ptr; + return XML_TOK_PROLOG_S; + } + } + *nextTokPtr = ptr; + return XML_TOK_PROLOG_S; + case BT_PERCNT: + return PREFIX(scanPercent)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_COMMA: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_COMMA; + case BT_LSQB: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_OPEN_BRACKET; + case BT_RSQB: + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + if (CHAR_MATCHES(enc, ptr, ']')) { + if (ptr + MINBPC(enc) == end) + return XML_TOK_PARTIAL; + if (CHAR_MATCHES(enc, ptr + MINBPC(enc), '>')) { + *nextTokPtr = ptr + 2*MINBPC(enc); + return XML_TOK_COND_SECT_CLOSE; + } + } + *nextTokPtr = ptr; + return XML_TOK_CLOSE_BRACKET; + case BT_LPAR: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_OPEN_PAREN; + case BT_RPAR: + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + switch (BYTE_TYPE(enc, ptr)) { + case BT_AST: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_CLOSE_PAREN_ASTERISK; + case BT_QUEST: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_CLOSE_PAREN_QUESTION; + case BT_PLUS: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_CLOSE_PAREN_PLUS; +case BT_CR: case BT_LF: case BT_S: +case BT_GT: case BT_COMMA: case BT_VERBAR: + case BT_RPAR: + *nextTokPtr = ptr; + return XML_TOK_CLOSE_PAREN; + } + *nextTokPtr = ptr; + return XML_TOK_INVALID; + case BT_VERBAR: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_OR; + case BT_GT: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_DECL_CLOSE; + case BT_NUM: + return PREFIX(scanPoundName)(enc, ptr + MINBPC(enc), end, nextTokPtr); +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + if (end - ptr < n) \ + return XML_TOK_PARTIAL_CHAR; \ + if (IS_NMSTRT_CHAR(enc, ptr, n)) { \ + ptr += n; \ + tok = XML_TOK_NAME; \ + break; \ + } \ + if (IS_NAME_CHAR(enc, ptr, n)) { \ + ptr += n; \ + tok = XML_TOK_NMTOKEN; \ + break; \ + } \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_NMSTRT: + case BT_HEX: + tok = XML_TOK_NAME; + ptr += MINBPC(enc); + break; + case BT_DIGIT: + case BT_NAME: + case BT_MINUS: +#ifdef XML_NS + case BT_COLON: +#endif + tok = XML_TOK_NMTOKEN; + ptr += MINBPC(enc); + break; + case BT_NONASCII: + if (IS_NMSTRT_CHAR_MINBPC(enc, ptr)) { + ptr += MINBPC(enc); + tok = XML_TOK_NAME; + break; + } + if (IS_NAME_CHAR_MINBPC(enc, ptr)) { + ptr += MINBPC(enc); + tok = XML_TOK_NMTOKEN; + break; + } + /* fall through */ + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) +case BT_GT: case BT_RPAR: case BT_COMMA: +case BT_VERBAR: case BT_LSQB: case BT_PERCNT: +case BT_S: case BT_CR: case BT_LF: + *nextTokPtr = ptr; + return tok; +#ifdef XML_NS + case BT_COLON: + ptr += MINBPC(enc); + switch (tok) { + case XML_TOK_NAME: + if (ptr == end) + return XML_TOK_PARTIAL; + tok = XML_TOK_PREFIXED_NAME; + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) + default: + tok = XML_TOK_NMTOKEN; + break; + } + break; + case XML_TOK_PREFIXED_NAME: + tok = XML_TOK_NMTOKEN; + break; + } + break; +#endif + case BT_PLUS: + if (tok == XML_TOK_NMTOKEN) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_NAME_PLUS; + case BT_AST: + if (tok == XML_TOK_NMTOKEN) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_NAME_ASTERISK; + case BT_QUEST: + if (tok == XML_TOK_NMTOKEN) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_NAME_QUESTION; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return XML_TOK_PARTIAL; +} + +static +int PREFIX(attributeValueTok)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + const char *start; + if (ptr == end) + return XML_TOK_NONE; + start = ptr; + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: ptr += n; break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_AMP: + if (ptr == start) + return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr); + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_LT: + /* this is for inside entity references */ + *nextTokPtr = ptr; + return XML_TOK_INVALID; + case BT_LF: + if (ptr == start) { + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_DATA_NEWLINE; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_CR: + if (ptr == start) { + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_TRAILING_CR; + if (BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC(enc); + *nextTokPtr = ptr; + return XML_TOK_DATA_NEWLINE; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_S: + if (ptr == start) { + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_ATTRIBUTE_VALUE_S; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + default: + ptr += MINBPC(enc); + break; + } + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; +} + +static +int PREFIX(entityValueTok)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + const char *start; + if (ptr == end) + return XML_TOK_NONE; + start = ptr; + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: ptr += n; break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_AMP: + if (ptr == start) + return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr); + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_PERCNT: + if (ptr == start) + return PREFIX(scanPercent)(enc, ptr + MINBPC(enc), end, nextTokPtr); + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_LF: + if (ptr == start) { + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_DATA_NEWLINE; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_CR: + if (ptr == start) { + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_TRAILING_CR; + if (BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC(enc); + *nextTokPtr = ptr; + return XML_TOK_DATA_NEWLINE; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + default: + ptr += MINBPC(enc); + break; + } + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; +} + +static +int PREFIX(isPublicId)(const ENCODING *enc, const char *ptr, const char *end, + const char **badPtr) +{ + ptr += MINBPC(enc); + end -= MINBPC(enc); + for (; ptr != end; ptr += MINBPC(enc)) { + switch (BYTE_TYPE(enc, ptr)) { + case BT_DIGIT: + case BT_HEX: + case BT_MINUS: + case BT_APOS: + case BT_LPAR: + case BT_RPAR: + case BT_PLUS: + case BT_COMMA: + case BT_SOL: + case BT_EQUALS: + case BT_QUEST: + case BT_CR: + case BT_LF: + case BT_SEMI: + case BT_EXCL: + case BT_AST: + case BT_PERCNT: + case BT_NUM: +#ifdef XML_NS + case BT_COLON: +#endif + break; + case BT_S: + if (CHAR_MATCHES(enc, ptr, '\t')) { + *badPtr = ptr; + return 0; + } + break; + case BT_NAME: + case BT_NMSTRT: + if (!(BYTE_TO_ASCII(enc, ptr) & ~0x7f)) + break; + default: + switch (BYTE_TO_ASCII(enc, ptr)) { + case 0x24: /* $ */ + case 0x40: /* @ */ + break; + default: + *badPtr = ptr; + return 0; + } + break; + } + } + return 1; +} + +/* This must only be called for a well-formed start-tag or empty element tag. +Returns the number of attributes. Pointers to the first attsMax attributes +are stored in atts. */ + +static +int PREFIX(getAtts)(const ENCODING *enc, const char *ptr, + int attsMax, ATTRIBUTE *atts) +{ + enum { other, inName, inValue } state = inName; + int nAtts = 0; + int open = 0; + + for (ptr += MINBPC(enc);; ptr += MINBPC(enc)) { + switch (BYTE_TYPE(enc, ptr)) { +#define START_NAME \ + if (state == other) { \ + if (nAtts < attsMax) { \ + atts[nAtts].name = ptr; \ + atts[nAtts].normalized = 1; \ + } \ + state = inName; \ + } +#define LEAD_CASE(n) \ + case BT_LEAD ## n: START_NAME ptr += (n - MINBPC(enc)); break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_NONASCII: + case BT_NMSTRT: + case BT_HEX: + START_NAME + break; +#undef START_NAME + case BT_QUOT: + if (state != inValue) { + if (nAtts < attsMax) + atts[nAtts].valuePtr = ptr + MINBPC(enc); + state = inValue; + open = BT_QUOT; + } + else if (open == BT_QUOT) { + state = other; + if (nAtts < attsMax) + atts[nAtts].valueEnd = ptr; + nAtts++; + } + break; + case BT_APOS: + if (state != inValue) { + if (nAtts < attsMax) + atts[nAtts].valuePtr = ptr + MINBPC(enc); + state = inValue; + open = BT_APOS; + } + else if (open == BT_APOS) { + state = other; + if (nAtts < attsMax) + atts[nAtts].valueEnd = ptr; + nAtts++; + } + break; + case BT_AMP: + if (nAtts < attsMax) + atts[nAtts].normalized = 0; + break; + case BT_S: + if (state == inName) + state = other; + else if (state == inValue + && nAtts < attsMax + && atts[nAtts].normalized + && (ptr == atts[nAtts].valuePtr + || BYTE_TO_ASCII(enc, ptr) != ' ' + || BYTE_TO_ASCII(enc, ptr + MINBPC(enc)) == ' ' + || BYTE_TYPE(enc, ptr + MINBPC(enc)) == open)) + atts[nAtts].normalized = 0; + break; + case BT_CR: case BT_LF: + /* This case ensures that the first attribute name is counted + Apart from that we could just change state on the quote. */ + if (state == inName) + state = other; + else if (state == inValue && nAtts < attsMax) + atts[nAtts].normalized = 0; + break; + case BT_GT: + case BT_SOL: + if (state != inValue) + return nAtts; + break; + default: + break; + } + } + /* not reached */ +} + +static +int PREFIX(charRefNumber)(const ENCODING *enc, const char *ptr) +{ + int result = 0; + /* skip &# */ + ptr += 2*MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, 'x')) { + for (ptr += MINBPC(enc); !CHAR_MATCHES(enc, ptr, ';'); ptr += MINBPC(enc)) { + int c = BYTE_TO_ASCII(enc, ptr); + switch (c) { +case '0': case '1': case '2': case '3': case '4': +case '5': case '6': case '7': case '8': case '9': + result <<= 4; + result |= (c - '0'); + break; +case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + result <<= 4; + result += 10 + (c - 'A'); + break; +case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + result <<= 4; + result += 10 + (c - 'a'); + break; + } + if (result >= 0x110000) + return -1; + } + } + else { + for (; !CHAR_MATCHES(enc, ptr, ';'); ptr += MINBPC(enc)) { + int c = BYTE_TO_ASCII(enc, ptr); + result *= 10; + result += (c - '0'); + if (result >= 0x110000) + return -1; + } + } + return checkCharRefNumber(result); +} + +static +int PREFIX(predefinedEntityName)(const ENCODING *enc, const char *ptr, const char *end) +{ + switch ((end - ptr)/MINBPC(enc)) { + case 2: + if (CHAR_MATCHES(enc, ptr + MINBPC(enc), 't')) { + switch (BYTE_TO_ASCII(enc, ptr)) { + case 'l': + return '<'; + case 'g': + return '>'; + } + } + break; + case 3: + if (CHAR_MATCHES(enc, ptr, 'a')) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, 'm')) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, 'p')) + return '&'; + } + } + break; + case 4: + switch (BYTE_TO_ASCII(enc, ptr)) { + case 'q': + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, 'u')) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, 'o')) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, 't')) + return '"'; + } + } + break; + case 'a': + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, 'p')) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, 'o')) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, 's')) + return '\''; + } + } + break; + } + } + return 0; +} + +static +int PREFIX(sameName)(const ENCODING *enc, const char *ptr1, const char *ptr2) +{ + for (;;) { + switch (BYTE_TYPE(enc, ptr1)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + if (*ptr1++ != *ptr2++) \ + return 0; + LEAD_CASE(4) LEAD_CASE(3) LEAD_CASE(2) +#undef LEAD_CASE + /* fall through */ + if (*ptr1++ != *ptr2++) + return 0; + break; + case BT_NONASCII: + case BT_NMSTRT: +#ifdef XML_NS + case BT_COLON: +#endif + case BT_HEX: + case BT_DIGIT: + case BT_NAME: + case BT_MINUS: + if (*ptr2++ != *ptr1++) + return 0; + if (MINBPC(enc) > 1) { + if (*ptr2++ != *ptr1++) + return 0; + if (MINBPC(enc) > 2) { + if (*ptr2++ != *ptr1++) + return 0; + if (MINBPC(enc) > 3) { + if (*ptr2++ != *ptr1++) + return 0; + } + } + } + break; + default: + if (MINBPC(enc) == 1 && *ptr1 == *ptr2) + return 1; + switch (BYTE_TYPE(enc, ptr2)) { + case BT_LEAD2: + case BT_LEAD3: + case BT_LEAD4: + case BT_NONASCII: + case BT_NMSTRT: +#ifdef XML_NS + case BT_COLON: +#endif + case BT_HEX: + case BT_DIGIT: + case BT_NAME: + case BT_MINUS: + return 0; + default: + return 1; + } + } + } + /* not reached */ +} + +static +int PREFIX(nameMatchesAscii)(const ENCODING *enc, const char *ptr1, const char *ptr2) +{ + for (; *ptr2; ptr1 += MINBPC(enc), ptr2++) { + if (!CHAR_MATCHES(enc, ptr1, *ptr2)) + return 0; + } + switch (BYTE_TYPE(enc, ptr1)) { + case BT_LEAD2: + case BT_LEAD3: + case BT_LEAD4: + case BT_NONASCII: + case BT_NMSTRT: +#ifdef XML_NS + case BT_COLON: +#endif + case BT_HEX: + case BT_DIGIT: + case BT_NAME: + case BT_MINUS: + return 0; + default: + return 1; + } +} + +static +int PREFIX(nameLength)(const ENCODING *enc, const char *ptr) +{ + const char *start = ptr; + for (;;) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: ptr += n; break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_NONASCII: + case BT_NMSTRT: +#ifdef XML_NS + case BT_COLON: +#endif + case BT_HEX: + case BT_DIGIT: + case BT_NAME: + case BT_MINUS: + ptr += MINBPC(enc); + break; + default: + return ptr - start; + } + } +} + +static +const char *PREFIX(skipS)(const ENCODING *enc, const char *ptr) +{ + for (;;) { + switch (BYTE_TYPE(enc, ptr)) { + case BT_LF: + case BT_CR: + case BT_S: + ptr += MINBPC(enc); + break; + default: + return ptr; + } + } +} + +static +void PREFIX(updatePosition)(const ENCODING *enc, + const char *ptr, + const char *end, + POSITION *pos) +{ + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + ptr += n; \ + break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_LF: + pos->columnNumber = (unsigned)-1; + pos->lineNumber++; + ptr += MINBPC(enc); + break; + case BT_CR: + pos->lineNumber++; + ptr += MINBPC(enc); + if (ptr != end && BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC(enc); + pos->columnNumber = (unsigned)-1; + break; + default: + ptr += MINBPC(enc); + break; + } + pos->columnNumber++; + } +} + +#undef DO_LEAD_CASE +#undef MULTIBYTE_CASES +#undef INVALID_CASES +#undef CHECK_NAME_CASE +#undef CHECK_NAME_CASES +#undef CHECK_NMSTRT_CASE +#undef CHECK_NMSTRT_CASES diff --git a/protocols/jabber/xmltok_impl.h b/protocols/jabber/xmltok_impl.h new file mode 100644 index 00000000..e72b225c --- /dev/null +++ b/protocols/jabber/xmltok_impl.h @@ -0,0 +1,71 @@ +/* +The contents of this file are subject to the Mozilla Public License +Version 1.1 (the "License"); you may not use this file except in +compliance with the License. You may obtain a copy of the License at +http://www.mozilla.org/MPL/ + +Software distributed under the License is distributed on an "AS IS" +basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +License for the specific language governing rights and limitations +under the License. + +The Original Code is expat. + +The Initial Developer of the Original Code is James Clark. +Portions created by James Clark are Copyright (C) 1998, 1999 +James Clark. All Rights Reserved. + +Contributor(s): + +Alternatively, the contents of this file may be used under the terms +of the GNU General Public License (the "GPL"), in which case the +provisions of the GPL are applicable instead of those above. If you +wish to allow use of your version of this file only under the terms of +the GPL and not to allow others to use your version of this file under +the MPL, indicate your decision by deleting the provisions above and +replace them with the notice and other provisions required by the +GPL. If you do not delete the provisions above, a recipient may use +your version of this file under either the MPL or the GPL. +*/ + +enum { + BT_NONXML, + BT_MALFORM, + BT_LT, + BT_AMP, + BT_RSQB, + BT_LEAD2, + BT_LEAD3, + BT_LEAD4, + BT_TRAIL, + BT_CR, + BT_LF, + BT_GT, + BT_QUOT, + BT_APOS, + BT_EQUALS, + BT_QUEST, + BT_EXCL, + BT_SOL, + BT_SEMI, + BT_NUM, + BT_LSQB, + BT_S, + BT_NMSTRT, + BT_COLON, + BT_HEX, + BT_DIGIT, + BT_NAME, + BT_MINUS, + BT_OTHER, /* known not to be a name or name start character */ + BT_NONASCII, /* might be a name or name start character */ + BT_PERCNT, + BT_LPAR, + BT_RPAR, + BT_AST, + BT_PLUS, + BT_COMMA, + BT_VERBAR +}; + +#include <stddef.h> diff --git a/protocols/jabber/xmltok_ns.c b/protocols/jabber/xmltok_ns.c new file mode 100644 index 00000000..ace3e5a4 --- /dev/null +++ b/protocols/jabber/xmltok_ns.c @@ -0,0 +1,117 @@ +/* +The contents of this file are subject to the Mozilla Public License +Version 1.1 (the "License"); you may not use this file except in +compliance with the License. You may obtain a copy of the License at +http://www.mozilla.org/MPL/ + +Software distributed under the License is distributed on an "AS IS" +basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +License for the specific language governing rights and limitations +under the License. + +The Original Code is expat. + +The Initial Developer of the Original Code is James Clark. +Portions created by James Clark are Copyright (C) 1998, 1999 +James Clark. All Rights Reserved. + +Contributor(s): + +*/ + +const ENCODING *NS(XmlGetUtf8InternalEncoding)() +{ + return &ns(internal_utf8_encoding).enc; +} + +const ENCODING *NS(XmlGetUtf16InternalEncoding)() +{ +#if XML_BYTE_ORDER == 12 + return &ns(internal_little2_encoding).enc; +#elif XML_BYTE_ORDER == 21 +return &ns(internal_big2_encoding).enc; +#else +const short n = 1; + return *(const char *)&n ? &ns(internal_little2_encoding).enc : &ns(internal_big2_encoding).enc; +#endif +} + +static +const ENCODING *NS(encodings)[] = { + &ns(latin1_encoding).enc, + &ns(ascii_encoding).enc, + &ns(utf8_encoding).enc, + &ns(big2_encoding).enc, + &ns(big2_encoding).enc, + &ns(little2_encoding).enc, + &ns(utf8_encoding).enc /* NO_ENC */ +}; + +static +int NS(initScanProlog)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + return initScan(NS(encodings), (const INIT_ENCODING *)enc, XML_PROLOG_STATE, ptr, end, nextTokPtr); +} + +static +int NS(initScanContent)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + return initScan(NS(encodings), (const INIT_ENCODING *)enc, XML_CONTENT_STATE, ptr, end, nextTokPtr); +} + +int NS(XmlInitEncoding)(INIT_ENCODING *p, const ENCODING **encPtr, const char *name) +{ + int i = getEncodingIndex(name); + if (i == UNKNOWN_ENC) + return 0; + INIT_ENC_INDEX(p) = (char)i; + p->initEnc.scanners[XML_PROLOG_STATE] = NS(initScanProlog); + p->initEnc.scanners[XML_CONTENT_STATE] = NS(initScanContent); + p->initEnc.updatePosition = initUpdatePosition; + p->encPtr = encPtr; + *encPtr = &(p->initEnc); + return 1; +} + +static +const ENCODING *NS(findEncoding)(const ENCODING *enc, const char *ptr, const char *end) +{ +#define ENCODING_MAX 128 + char buf[ENCODING_MAX]; + char *p = buf; + int i; + XmlUtf8Convert(enc, &ptr, end, &p, p + ENCODING_MAX - 1); + if (ptr != end) + return 0; + *p = 0; + if (streqci(buf, "UTF-16") && enc->minBytesPerChar == 2) + return enc; + i = getEncodingIndex(buf); + if (i == UNKNOWN_ENC) + return 0; + return NS(encodings)[i]; +} + +int NS(XmlParseXmlDecl)(int isGeneralTextEntity, + const ENCODING *enc, + const char *ptr, + const char *end, + const char **badPtr, + const char **versionPtr, + const char **encodingName, + const ENCODING **encoding, + int *standalone) +{ + return doParseXmlDecl(NS(findEncoding), + isGeneralTextEntity, + enc, + ptr, + end, + badPtr, + versionPtr, + encodingName, + encoding, + standalone); +} diff --git a/protocols/jabber/xstream.c b/protocols/jabber/xstream.c new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/protocols/jabber/xstream.c diff --git a/protocols/md5.c b/protocols/md5.c new file mode 100644 index 00000000..e6273585 --- /dev/null +++ b/protocols/md5.c @@ -0,0 +1,392 @@ +/* + Copyright (C) 1999 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321. + It is derived directly from the text of the RFC and not from the + reference implementation. + + The original and principal author of md5.c is L. Peter Deutsch + <ghost@aladdin.com>. Other authors are noted in the change history + that follows (in reverse chronological order): + + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). + 1999-05-03 lpd Original version. + */ + +#include "md5.h" +#include <string.h> + +#ifdef TEST +/* + * Compile with -DTEST to create a self-contained executable test program. + * The test program should print out the same values as given in section + * A.5 of RFC 1321, reproduced below. + */ +#include <string.h> +main() +{ + static const char *const test[7] = { + "", /*d41d8cd98f00b204e9800998ecf8427e*/ + "945399884.61923487334tuvga", /*0cc175b9c0f1b6a831c399e269772661*/ + "abc", /*900150983cd24fb0d6963f7d28e17f72*/ + "message digest", /*f96b697d7cb7938d525a2f31aaf161d0*/ + "abcdefghijklmnopqrstuvwxyz", /*c3fcd3d76192e4007dfb496cca67e13b*/ + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", + /*d174ab98d277d9f5a5611c2c9f419d9f*/ + "12345678901234567890123456789012345678901234567890123456789012345678901234567890" /*57edf4a22be3c955ac49da2e2107b67a*/ + }; + int i; + + for (i = 0; i < 7; ++i) { + md5_state_t state; + md5_byte_t digest[16]; + int di; + + md5_init(&state); + md5_append(&state, (const md5_byte_t *)test[i], strlen(test[i])); + md5_finish(&state, digest); + printf("MD5 (\"%s\") = ", test[i]); + for (di = 0; di < 16; ++di) + printf("%02x", digest[di]); + printf("\n"); + } + return 0; +} +#endif /* TEST */ + + +/* + * For reference, here is the program that computed the T values. + */ +#if 0 +#include <math.h> +main() +{ + int i; + for (i = 1; i <= 64; ++i) { + unsigned long v = (unsigned long)(4294967296.0 * fabs(sin((double)i))); + printf("#define T%d 0x%08lx\n", i, v); + } + return 0; +} +#endif +/* + * End of T computation program. + */ +#define T1 0xd76aa478 +#define T2 0xe8c7b756 +#define T3 0x242070db +#define T4 0xc1bdceee +#define T5 0xf57c0faf +#define T6 0x4787c62a +#define T7 0xa8304613 +#define T8 0xfd469501 +#define T9 0x698098d8 +#define T10 0x8b44f7af +#define T11 0xffff5bb1 +#define T12 0x895cd7be +#define T13 0x6b901122 +#define T14 0xfd987193 +#define T15 0xa679438e +#define T16 0x49b40821 +#define T17 0xf61e2562 +#define T18 0xc040b340 +#define T19 0x265e5a51 +#define T20 0xe9b6c7aa +#define T21 0xd62f105d +#define T22 0x02441453 +#define T23 0xd8a1e681 +#define T24 0xe7d3fbc8 +#define T25 0x21e1cde6 +#define T26 0xc33707d6 +#define T27 0xf4d50d87 +#define T28 0x455a14ed +#define T29 0xa9e3e905 +#define T30 0xfcefa3f8 +#define T31 0x676f02d9 +#define T32 0x8d2a4c8a +#define T33 0xfffa3942 +#define T34 0x8771f681 +#define T35 0x6d9d6122 +#define T36 0xfde5380c +#define T37 0xa4beea44 +#define T38 0x4bdecfa9 +#define T39 0xf6bb4b60 +#define T40 0xbebfbc70 +#define T41 0x289b7ec6 +#define T42 0xeaa127fa +#define T43 0xd4ef3085 +#define T44 0x04881d05 +#define T45 0xd9d4d039 +#define T46 0xe6db99e5 +#define T47 0x1fa27cf8 +#define T48 0xc4ac5665 +#define T49 0xf4292244 +#define T50 0x432aff97 +#define T51 0xab9423a7 +#define T52 0xfc93a039 +#define T53 0x655b59c3 +#define T54 0x8f0ccc92 +#define T55 0xffeff47d +#define T56 0x85845dd1 +#define T57 0x6fa87e4f +#define T58 0xfe2ce6e0 +#define T59 0xa3014314 +#define T60 0x4e0811a1 +#define T61 0xf7537e82 +#define T62 0xbd3af235 +#define T63 0x2ad7d2bb +#define T64 0xeb86d391 + +static void +md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) +{ + md5_word_t + a = pms->abcd[0], b = pms->abcd[1], + c = pms->abcd[2], d = pms->abcd[3]; + md5_word_t t; + +#ifndef ARCH_IS_BIG_ENDIAN +# define ARCH_IS_BIG_ENDIAN 1 /* slower, default implementation */ +#endif +#if ARCH_IS_BIG_ENDIAN + + /* + * On big-endian machines, we must arrange the bytes in the right + * order. (This also works on machines of unknown byte order.) + */ + md5_word_t X[16]; + const md5_byte_t *xp = data; + int i; + + for (i = 0; i < 16; ++i, xp += 4) + X[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); + +#else /* !ARCH_IS_BIG_ENDIAN */ + + /* + * On little-endian machines, we can process properly aligned data + * without copying it. + */ + md5_word_t xbuf[16]; + const md5_word_t *X; + + if (!((data - (const md5_byte_t *)0) & 3)) { + /* data are properly aligned */ + X = (const md5_word_t *)data; + } else { + /* not aligned */ + memcpy(xbuf, data, 64); + X = xbuf; + } +#endif + +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + + /* Round 1. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ +#define F(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + F(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 7, T1); + SET(d, a, b, c, 1, 12, T2); + SET(c, d, a, b, 2, 17, T3); + SET(b, c, d, a, 3, 22, T4); + SET(a, b, c, d, 4, 7, T5); + SET(d, a, b, c, 5, 12, T6); + SET(c, d, a, b, 6, 17, T7); + SET(b, c, d, a, 7, 22, T8); + SET(a, b, c, d, 8, 7, T9); + SET(d, a, b, c, 9, 12, T10); + SET(c, d, a, b, 10, 17, T11); + SET(b, c, d, a, 11, 22, T12); + SET(a, b, c, d, 12, 7, T13); + SET(d, a, b, c, 13, 12, T14); + SET(c, d, a, b, 14, 17, T15); + SET(b, c, d, a, 15, 22, T16); +#undef SET + + /* Round 2. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ +#define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + G(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 1, 5, T17); + SET(d, a, b, c, 6, 9, T18); + SET(c, d, a, b, 11, 14, T19); + SET(b, c, d, a, 0, 20, T20); + SET(a, b, c, d, 5, 5, T21); + SET(d, a, b, c, 10, 9, T22); + SET(c, d, a, b, 15, 14, T23); + SET(b, c, d, a, 4, 20, T24); + SET(a, b, c, d, 9, 5, T25); + SET(d, a, b, c, 14, 9, T26); + SET(c, d, a, b, 3, 14, T27); + SET(b, c, d, a, 8, 20, T28); + SET(a, b, c, d, 13, 5, T29); + SET(d, a, b, c, 2, 9, T30); + SET(c, d, a, b, 7, 14, T31); + SET(b, c, d, a, 12, 20, T32); +#undef SET + + /* Round 3. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + H(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 5, 4, T33); + SET(d, a, b, c, 8, 11, T34); + SET(c, d, a, b, 11, 16, T35); + SET(b, c, d, a, 14, 23, T36); + SET(a, b, c, d, 1, 4, T37); + SET(d, a, b, c, 4, 11, T38); + SET(c, d, a, b, 7, 16, T39); + SET(b, c, d, a, 10, 23, T40); + SET(a, b, c, d, 13, 4, T41); + SET(d, a, b, c, 0, 11, T42); + SET(c, d, a, b, 3, 16, T43); + SET(b, c, d, a, 6, 23, T44); + SET(a, b, c, d, 9, 4, T45); + SET(d, a, b, c, 12, 11, T46); + SET(c, d, a, b, 15, 16, T47); + SET(b, c, d, a, 2, 23, T48); +#undef SET + + /* Round 4. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ +#define I(x, y, z) ((y) ^ ((x) | ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + I(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 6, T49); + SET(d, a, b, c, 7, 10, T50); + SET(c, d, a, b, 14, 15, T51); + SET(b, c, d, a, 5, 21, T52); + SET(a, b, c, d, 12, 6, T53); + SET(d, a, b, c, 3, 10, T54); + SET(c, d, a, b, 10, 15, T55); + SET(b, c, d, a, 1, 21, T56); + SET(a, b, c, d, 8, 6, T57); + SET(d, a, b, c, 15, 10, T58); + SET(c, d, a, b, 6, 15, T59); + SET(b, c, d, a, 13, 21, T60); + SET(a, b, c, d, 4, 6, T61); + SET(d, a, b, c, 11, 10, T62); + SET(c, d, a, b, 2, 15, T63); + SET(b, c, d, a, 9, 21, T64); +#undef SET + + /* Then perform the following additions. (That is increment each + of the four registers by the value it had before this block + was started.) */ + pms->abcd[0] += a; + pms->abcd[1] += b; + pms->abcd[2] += c; + pms->abcd[3] += d; +} + +void +md5_init(md5_state_t *pms) +{ + pms->count[0] = pms->count[1] = 0; + pms->abcd[0] = 0x67452301; + pms->abcd[1] = 0xefcdab89; + pms->abcd[2] = 0x98badcfe; + pms->abcd[3] = 0x10325476; +} + +void +md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) +{ + const md5_byte_t *p = data; + int left = nbytes; + int offset = (pms->count[0] >> 3) & 63; + md5_word_t nbits = (md5_word_t)(nbytes << 3); + + if (nbytes <= 0) + return; + + /* Update the message length. */ + pms->count[1] += nbytes >> 29; + pms->count[0] += nbits; + if (pms->count[0] < nbits) + pms->count[1]++; + + /* Process an initial partial block. */ + if (offset) { + int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); + + memcpy(pms->buf + offset, p, copy); + if (offset + copy < 64) + return; + p += copy; + left -= copy; + md5_process(pms, pms->buf); + } + + /* Process full blocks. */ + for (; left >= 64; p += 64, left -= 64) + md5_process(pms, p); + + /* Process a final partial block. */ + if (left) + memcpy(pms->buf, p, left); +} + +void +md5_finish(md5_state_t *pms, md5_byte_t digest[16]) +{ + static const md5_byte_t pad[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 + }; + md5_byte_t data[8]; + int i; + + /* Save the length before padding. */ + for (i = 0; i < 8; ++i) + data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); + /* Pad to 56 bytes mod 64. */ + md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); + /* Append the length. */ + md5_append(pms, data, 8); + for (i = 0; i < 16; ++i) + digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); +} diff --git a/protocols/md5.h b/protocols/md5.h new file mode 100644 index 00000000..f24f2ff1 --- /dev/null +++ b/protocols/md5.h @@ -0,0 +1,85 @@ +/* + Copyright (C) 1999 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost@aladdin.com + + */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321. + It is derived directly from the text of the RFC and not from the + reference implementation. + + The original and principal author of md5.h is L. Peter Deutsch + <ghost@aladdin.com>. Other authors are noted in the change history + that follows (in reverse chronological order): + + 2004-03-09 Jelmer Vernooij add G_MODULE_EXPORT for Bitlbee + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); + added conditionalization for C++ compilation from Martin + Purschke <purschke@bnl.gov>. + 1999-05-03 lpd Original version. + */ + +#ifndef md5_INCLUDED +# define md5_INCLUDED + +#include <glib.h> +#include <gmodule.h> + +/* + * This code has some adaptations for the Ghostscript environment, but it + * will compile and run correctly in any environment with 8-bit chars and + * 32-bit ints. Specifically, it assumes that if the following are + * defined, they have the same meaning as in Ghostscript: P1, P2, P3, + * ARCH_IS_BIG_ENDIAN. + */ + +typedef unsigned char md5_byte_t; /* 8-bit byte */ +typedef unsigned int md5_word_t; /* 32-bit word */ + +/* Define the state of the MD5 Algorithm. */ +typedef struct md5_state_s { + md5_word_t count[2]; /* message length in bits, lsw first */ + md5_word_t abcd[4]; /* digest buffer */ + md5_byte_t buf[64]; /* accumulate block */ +} md5_state_t; + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Initialize the algorithm. */ +G_MODULE_EXPORT void md5_init(md5_state_t *pms); + +/* Append a string to the message. */ +G_MODULE_EXPORT void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); + +/* Finish the message and return the digest. */ +G_MODULE_EXPORT void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +#endif /* md5_INCLUDED */ diff --git a/protocols/msn/Makefile b/protocols/msn/Makefile new file mode 100644 index 00000000..e6620323 --- /dev/null +++ b/protocols/msn/Makefile @@ -0,0 +1,39 @@ +########################### +## Makefile for BitlBee ## +## ## +## Copyright 2002 Lintux ## +########################### + +### DEFINITIONS + +-include ../../Makefile.settings + +# [SH] Program variables +objects = msn.o msn_util.o ns.o passport.o sb.o tables.o + +CFLAGS += -Wall +LFLAGS += -r + +# [SH] Phony targets +all: msnn.o + +.PHONY: all clean distclean + +clean: + rm -f *.o core + +distclean: clean + +### MAIN PROGRAM + +$(objects): ../../Makefile.settings Makefile + +$(objects): %.o: %.c + @echo '*' Compiling $< + @$(CC) -c $(CFLAGS) $< -o $@ + +msnn.o: $(objects) + @echo '*' Linking msnn.o + @$(LD) $(LFLAGS) $(objects) -o msnn.o + + diff --git a/protocols/msn/msn.c b/protocols/msn/msn.c new file mode 100644 index 00000000..33e12af5 --- /dev/null +++ b/protocols/msn/msn.c @@ -0,0 +1,402 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* MSN module - Main file; functions to be called from BitlBee */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "nogaim.h" +#include "msn.h" + +static struct prpl *my_protocol = NULL; + +static void msn_login( struct aim_user *acct ) +{ + struct gaim_connection *gc = new_gaim_conn( acct ); + struct msn_data *md = g_new0( struct msn_data, 1 ); + + set_login_progress( gc, 1, "Connecting" ); + + gc->proto_data = md; + md->fd = -1; + + if( strchr( acct->username, '@' ) == NULL ) + { + hide_login_progress( gc, "Invalid account name" ); + signoff( gc ); + return; + } + + md->fd = proxy_connect( "messenger.hotmail.com", 1863, msn_ns_connected, gc ); + if( md->fd < 0 ) + { + hide_login_progress( gc, "Could not connect to server" ); + signoff( gc ); + } + else + { + md->gc = gc; + md->away_state = msn_away_state_list; + + msn_connections = g_slist_append( msn_connections, gc ); + } +} + +static void msn_close( struct gaim_connection *gc ) +{ + struct msn_data *md = gc->proto_data; + GSList *l; + + if( md->fd >= 0 ) + closesocket( md->fd ); + + if( md->handler ) + { + if( md->handler->rxq ) g_free( md->handler->rxq ); + if( md->handler->cmd_text ) g_free( md->handler->cmd_text ); + g_free( md->handler ); + } + + while( md->switchboards ) + msn_sb_destroy( md->switchboards->data ); + + if( md->msgq ) + { + struct msn_message *m; + + for( l = md->msgq; l; l = l->next ) + { + m = l->data; + g_free( m->who ); + g_free( m->text ); + g_free( m ); + } + g_slist_free( md->msgq ); + + serv_got_crap( gc, "Warning: Closing down MSN connection with unsent message(s), you'll have to resend them." ); + } + + for( l = gc->permit; l; l = l->next ) + g_free( l->data ); + g_slist_free( gc->permit ); + + for( l = gc->deny; l; l = l->next ) + g_free( l->data ); + g_slist_free( gc->deny ); + + g_free( md ); + + msn_connections = g_slist_remove( msn_connections, gc ); +} + +static int msn_send_im( struct gaim_connection *gc, char *who, char *message, int len, int away ) +{ + struct msn_switchboard *sb; + struct msn_data *md = gc->proto_data; + + if( ( sb = msn_sb_by_handle( gc, who ) ) ) + { + return( msn_sb_sendmessage( sb, message ) ); + } + else + { + struct msn_message *m; + char buf[1024]; + + /* Create a message. We have to arrange a usable switchboard, and send the message later. */ + m = g_new0( struct msn_message, 1 ); + m->who = g_strdup( who ); + m->text = g_strdup( message ); + + /* FIXME: *CHECK* the reliability of using spare sb's! */ + if( ( sb = msn_sb_spare( gc ) ) ) + { + debug( "Trying to use a spare switchboard to message %s", who ); + + sb->who = g_strdup( who ); + g_snprintf( buf, sizeof( buf ), "CAL %d %s\r\n", ++sb->trId, who ); + if( msn_sb_write( sb, buf, strlen( buf ) ) ) + { + /* He/She should join the switchboard soon, let's queue the message. */ + sb->msgq = g_slist_append( sb->msgq, m ); + return( 1 ); + } + } + + debug( "Creating a new switchboard to message %s", who ); + + /* If we reach this line, there was no spare switchboard, so let's make one. */ + g_snprintf( buf, sizeof( buf ), "XFR %d SB\r\n", ++md->trId ); + if( !msn_write( gc, buf, strlen( buf ) ) ) + { + g_free( m->who ); + g_free( m->text ); + g_free( m ); + + return( 0 ); + } + + /* And queue the message to md. We'll pick it up when the switchboard comes up. */ + md->msgq = g_slist_append( md->msgq, m ); + + /* FIXME: If the switchboard creation fails, the message will not be sent. */ + + return( 1 ); + } + + return( 0 ); +} + +static GList *msn_away_states( struct gaim_connection *gc ) +{ + GList *l = NULL; + int i; + + for( i = 0; msn_away_state_list[i].number > -1; i ++ ) + l = g_list_append( l, msn_away_state_list[i].name ); + + return( l ); +} + +static char *msn_get_status_string( struct gaim_connection *gc, int number ) +{ + struct msn_away_state *st = msn_away_state_by_number( number ); + + if( st ) + return( st->name ); + else + return( "" ); +} + +static void msn_set_away( struct gaim_connection *gc, char *state, char *message ) +{ + char buf[1024]; + struct msn_data *md = gc->proto_data; + struct msn_away_state *st; + + if( strcmp( state, GAIM_AWAY_CUSTOM ) == 0 ) + st = msn_away_state_by_name( "Away" ); + else + st = msn_away_state_by_name( state ); + + if( !st ) st = msn_away_state_list; + md->away_state = st; + + g_snprintf( buf, sizeof( buf ), "CHG %d %s\r\n", ++md->trId, st->code ); + msn_write( gc, buf, strlen( buf ) ); +} + +static void msn_set_info( struct gaim_connection *gc, char *info ) +{ + int i; + char buf[1024], *fn, *s; + struct msn_data *md = gc->proto_data; + + if( strlen( info ) > 129 ) + { + do_error_dialog( gc, "Maximum name length exceeded", "MSN" ); + return; + } + + /* Of course we could use http_encode() here, but when we encode + every character, the server is less likely to complain about the + chosen name. However, the MSN server doesn't seem to like escaped + non-ASCII chars, so we keep those unescaped. */ + s = fn = g_new0( char, strlen( info ) * 3 + 1 ); + for( i = 0; info[i]; i ++ ) + if( info[i] & 128 ) + { + *s = info[i]; + s ++; + } + else + { + g_snprintf( s, 4, "%%%02X", info[i] ); + s += 3; + } + + g_snprintf( buf, sizeof( buf ), "REA %d %s %s\r\n", ++md->trId, gc->username, fn ); + msn_write( gc, buf, strlen( buf ) ); + g_free( fn ); +} + +static void msn_get_info(struct gaim_connection *gc, char *who) +{ + /* Just make an URL and let the user fetch the info */ + serv_got_crap( gc, "%s\n%s: %s%s", _("User Info"), _("For now, fetch yourself"), PROFILE_URL, who ); +} + +static void msn_add_buddy( struct gaim_connection *gc, char *who ) +{ + msn_buddy_list_add( gc, "FL", who, who ); +} + +static void msn_remove_buddy( struct gaim_connection *gc, char *who, char *group ) +{ + msn_buddy_list_remove( gc, "FL", who ); +} + +static int msn_chat_send( struct gaim_connection *gc, int id, char *message ) +{ + struct msn_switchboard *sb = msn_sb_by_id( gc, id ); + + if( sb ) + return( msn_sb_sendmessage( sb, message ) ); + else + return( 0 ); +} + +static void msn_chat_invite( struct gaim_connection *gc, int id, char *msg, char *who ) +{ + struct msn_switchboard *sb = msn_sb_by_id( gc, id ); + char buf[1024]; + + if( sb ) + { + g_snprintf( buf, sizeof( buf ), "CAL %d %s\r\n", ++sb->trId, who ); + msn_sb_write( sb, buf, strlen( buf ) ); + } +} + +static void msn_chat_leave( struct gaim_connection *gc, int id ) +{ + struct msn_switchboard *sb = msn_sb_by_id( gc, id ); + + if( sb ) + msn_sb_write( sb, "OUT\r\n", 5 ); +} + +static int msn_chat_open( struct gaim_connection *gc, char *who ) +{ + struct msn_switchboard *sb; + struct msn_data *md = gc->proto_data; + char buf[1024]; + + if( ( sb = msn_sb_by_handle( gc, who ) ) ) + { + debug( "Converting existing switchboard to %s to a groupchat", who ); + msn_sb_to_chat( sb ); + return( 1 ); + } + else + { + struct msn_message *m; + + if( ( sb = msn_sb_spare( gc ) ) ) + { + debug( "Trying to reuse an existing switchboard as a groupchat with %s", who ); + g_snprintf( buf, sizeof( buf ), "CAL %d %s\r\n", ++sb->trId, who ); + if( msn_sb_write( sb, buf, strlen( buf ) ) ) + { + msn_sb_to_chat( sb ); + return( 1 ); + } + } + + /* If the stuff above failed for some reason: */ + debug( "Creating a new switchboard to groupchat with %s", who ); + + /* Request a new switchboard. */ + g_snprintf( buf, sizeof( buf ), "XFR %d SB\r\n", ++md->trId ); + if( !msn_write( gc, buf, strlen( buf ) ) ) + return( 0 ); + + /* Create a magic message. This is quite hackish, but who cares? :-P */ + m = g_new0( struct msn_message, 1 ); + m->who = g_strdup( who ); + m->text = g_strdup( GROUPCHAT_SWITCHBOARD_MESSAGE ); + + /* Queue the magic message and cross your fingers. */ + md->msgq = g_slist_append( md->msgq, m ); + + return( 1 ); + } + + return( 0 ); +} + +static void msn_keepalive( struct gaim_connection *gc ) +{ + msn_write( gc, "PNG\r\n", strlen( "PNG\r\n" ) ); +} + +static void msn_add_permit( struct gaim_connection *gc, char *who ) +{ + msn_buddy_list_add( gc, "AL", who, who ); +} + +static void msn_rem_permit( struct gaim_connection *gc, char *who ) +{ + msn_buddy_list_remove( gc, "AL", who ); +} + +static void msn_add_deny( struct gaim_connection *gc, char *who ) +{ + struct msn_switchboard *sb; + + msn_buddy_list_add( gc, "BL", who, who ); + + /* If there's still a conversation with this person, close it. */ + if( ( sb = msn_sb_by_handle( gc, who ) ) ) + { + msn_sb_destroy( sb ); + } +} + +static void msn_rem_deny( struct gaim_connection *gc, char *who ) +{ + msn_buddy_list_remove( gc, "BL", who ); +} + +static int msn_send_typing( struct gaim_connection *gc, char *who, int typing ) +{ + if( typing ) + return( msn_send_im( gc, who, TYPING_NOTIFICATION_MESSAGE, strlen( TYPING_NOTIFICATION_MESSAGE ), 0 ) ); + else + return( 1 ); +} + +void msn_init(struct prpl *ret) +{ + ret->protocol = PROTO_MSN; + ret->login = msn_login; + ret->close = msn_close; + ret->send_im = msn_send_im; + ret->away_states = msn_away_states; + ret->get_status_string = msn_get_status_string; + ret->set_away = msn_set_away; + ret->set_info = msn_set_info; + ret->get_info = msn_get_info; + ret->add_buddy = msn_add_buddy; + ret->remove_buddy = msn_remove_buddy; + ret->chat_send = msn_chat_send; + ret->chat_invite = msn_chat_invite; + ret->chat_leave = msn_chat_leave; + ret->chat_open = msn_chat_open; + ret->keepalive = msn_keepalive; + ret->add_permit = msn_add_permit; + ret->rem_permit = msn_rem_permit; + ret->add_deny = msn_add_deny; + ret->rem_deny = msn_rem_deny; + ret->send_typing = msn_send_typing; + + my_protocol = ret; +} diff --git a/protocols/msn/msn.h b/protocols/msn/msn.h new file mode 100644 index 00000000..61231d8a --- /dev/null +++ b/protocols/msn/msn.h @@ -0,0 +1,170 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* MSN module */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +/* Some hackish magicstrings to make special-purpose messages/switchboards. + */ +#define TYPING_NOTIFICATION_MESSAGE "\r\r\rBEWARE, ME R TYPINK MESSAGE!!!!\r\r\r" +#define GROUPCHAT_SWITCHBOARD_MESSAGE "\r\r\rME WANT TALK TO MANY PEOPLE\r\r\r" + +#ifdef _WIN32 +#define debug +#else +#define debug( text... ) irc_usermsg( IRC, text ); +#undef debug +#define debug( text... ) +#endif + +#define QRY_NAME "msmsgs@msnmsgr.com" +#define QRY_CODE "Q1P7W2E4J9R8U3S5" + +#define MSN_SB_NEW -24062002 + +#define MSN_MESSAGE_HEADERS "MIME-Version: 1.0\r\n" \ + "Content-Type: text/plain; charset=UTF-8\r\n" \ + "User-Agent: BitlBee " BITLBEE_VERSION "\r\n" \ + "X-MMS-IM-Format: FN=MS%20Shell%20Dlg; EF=; CO=0; CS=0; PF=0\r\n" \ + "\r\n" + +#define MSN_TYPING_HEADERS "MIME-Version: 1.0\r\n" \ + "Content-Type: text/x-msmsgscontrol\r\n" \ + "TypingUser: %s\r\n" \ + "\r\n\r\n" + +#define PROFILE_URL "http://members.msn.com/" + +struct msn_data +{ + struct gaim_connection *gc; + + int fd; + struct msn_handler_data *handler; + + int trId; + + GSList *msgq; + GSList *switchboards; + int buddycount; + struct msn_away_state *away_state; +}; + +struct msn_switchboard +{ + struct gaim_connection *gc; + + int fd; + gint inp; + struct msn_handler_data *handler; + + int trId; + int ready; + + int session; + char *key; + + GSList *msgq; + char *who; + struct conversation *chat; +}; + +struct msn_away_state +{ + int number; + char code[4]; + char name[16]; +}; + +struct msn_status_code +{ + int number; + char *text; + int flags; +}; + +struct msn_message +{ + char *who; + char *text; +}; + +struct msn_handler_data +{ + int fd; + int rxlen; + char *rxq; + + int msglen; + char *cmd_text; + + gpointer data; + + int (*exec_command) ( gpointer data, char **cmd, int count ); + int (*exec_message) ( gpointer data, char *msg, int msglen, char **cmd, int count ); +}; + +/* Bitfield values for msn_status_code.flags */ +#define STATUS_FATAL 1 +#define STATUS_SB_FATAL 2 + +int msn_chat_id; +extern struct msn_away_state msn_away_state_list[]; +extern struct msn_status_code msn_status_code_list[]; + +/* Keep a list of all the active connections. We need these lists because + "connected" callbacks might be called when the connection they belong too + is down already (for example, when an impatient user disabled the + connection), the callback should check whether it's still listed here + before doing *anything* else. */ +GSList *msn_connections; +GSList *msn_switchboards; + +/* ns.c */ +void msn_ns_connected( gpointer data, gint source, GaimInputCondition cond ); + +/* msn_util.c */ +int msn_write( struct gaim_connection *gc, char *s, int len ); +int msn_logged_in( struct gaim_connection *gc ); +int msn_buddy_list_add( struct gaim_connection *gc, char *list, char *who, char *realname ); +int msn_buddy_list_remove( struct gaim_connection *gc, char *list, char *who ); +void msn_buddy_ask( struct gaim_connection *gc, char *handle, char *realname ); +char *msn_findheader( char *text, char *header, int len ); +char **msn_linesplit( char *line ); +int msn_handler( struct msn_handler_data *h ); + +/* tables.c */ +struct msn_away_state *msn_away_state_by_number( int number ); +struct msn_away_state *msn_away_state_by_code( char *code ); +struct msn_away_state *msn_away_state_by_name( char *name ); +struct msn_status_code *msn_status_by_number( int number ); + +/* sb.c */ +int msn_sb_write( struct msn_switchboard *sb, char *s, int len ); +struct msn_switchboard *msn_sb_create( struct gaim_connection *gc, char *host, int port, char *key, int session ); +struct msn_switchboard *msn_sb_by_handle( struct gaim_connection *gc, char *handle ); +struct msn_switchboard *msn_sb_by_id( struct gaim_connection *gc, int id ); +struct msn_switchboard *msn_sb_spare( struct gaim_connection *gc ); +int msn_sb_sendmessage( struct msn_switchboard *sb, char *text ); +void msn_sb_to_chat( struct msn_switchboard *sb ); +void msn_sb_destroy( struct msn_switchboard *sb ); +void msn_sb_connected( gpointer data, gint source, GaimInputCondition cond ); diff --git a/protocols/msn/msn_util.c b/protocols/msn/msn_util.c new file mode 100644 index 00000000..2f22acfc --- /dev/null +++ b/protocols/msn/msn_util.c @@ -0,0 +1,359 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* MSN module - Miscellaneous utilities */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "nogaim.h" +#include "msn.h" +#include <ctype.h> + +int msn_write( struct gaim_connection *gc, char *s, int len ) +{ + struct msn_data *md = gc->proto_data; + int st; + + st = write( md->fd, s, len ); + if( st != len ) + { + hide_login_progress_error( gc, "Short write() to main server" ); + signoff( gc ); + return( 0 ); + } + + return( 1 ); +} + +int msn_logged_in( struct gaim_connection *gc ) +{ + struct msn_data *md = gc->proto_data; + char buf[1024]; + + account_online( gc ); + + /* account_online() sets an away state if there is any, so only + execute this code if we're not away. */ + if( md->away_state == msn_away_state_list ) + { + g_snprintf( buf, sizeof( buf ), "CHG %d %s %d\r\n", ++md->trId, md->away_state->code, 0 ); + return( msn_write( gc, buf, strlen( buf ) ) ); + } + + return( 0 ); +} + +int msn_buddy_list_add( struct gaim_connection *gc, char *list, char *who, char *realname_ ) +{ + struct msn_data *md = gc->proto_data; + GSList *l, **lp = NULL; + char buf[1024], *realname; + + if( strcmp( list, "AL" ) == 0 ) + lp = &gc->permit; + else if( strcmp( list, "BL" ) == 0 ) + lp = &gc->deny; + + if( lp ) + for( l = *lp; l; l = l->next ) + if( g_strcasecmp( l->data, who ) == 0 ) + return( 1 ); + + realname = g_new0( char, strlen( realname_ ) * 3 + 1 ); + strcpy( realname, realname_ ); + http_encode( realname ); + + g_snprintf( buf, sizeof( buf ), "ADD %d %s %s %s\r\n", ++md->trId, list, who, realname ); + if( msn_write( gc, buf, strlen( buf ) ) ) + { + g_free( realname ); + + if( lp ) + *lp = g_slist_append( *lp, g_strdup( who ) ); + + return( 1 ); + } + + g_free( realname ); + + return( 0 ); +} + +int msn_buddy_list_remove( struct gaim_connection *gc, char *list, char *who ) +{ + struct msn_data *md = gc->proto_data; + GSList *l = NULL, **lp = NULL; + char buf[1024]; + + if( strcmp( list, "AL" ) == 0 ) + lp = &gc->permit; + else if( strcmp( list, "BL" ) == 0 ) + lp = &gc->deny; + + if( lp ) + { + for( l = *lp; l; l = l->next ) + if( g_strcasecmp( l->data, who ) == 0 ) + break; + + if( !l ) + return( 1 ); + } + + g_snprintf( buf, sizeof( buf ), "REM %d %s %s\r\n", ++md->trId, list, who ); + if( msn_write( gc, buf, strlen( buf ) ) ) + { + if( lp ) + *lp = g_slist_remove( *lp, l->data ); + + return( 1 ); + } + + return( 0 ); +} + +struct msn_buddy_ask_data +{ + struct gaim_connection *gc; + char *handle; + char *realname; +}; + +static void msn_buddy_ask_yes( gpointer w, struct msn_buddy_ask_data *bla ) +{ + msn_buddy_list_add( bla->gc, "AL", bla->handle, bla->realname ); + + g_free( bla->handle ); + g_free( bla->realname ); + g_free( bla ); +} + +static void msn_buddy_ask_no( gpointer w, struct msn_buddy_ask_data *bla ) +{ + msn_buddy_list_add( bla->gc, "BL", bla->handle, bla->realname ); + + g_free( bla->handle ); + g_free( bla->realname ); + g_free( bla ); +} + +void msn_buddy_ask( struct gaim_connection *gc, char *handle, char *realname ) +{ + struct msn_buddy_ask_data *bla = g_new0( struct msn_buddy_ask_data, 1 ); + char buf[1024]; + + bla->gc = gc; + bla->handle = g_strdup( handle ); + bla->realname = g_strdup( realname ); + + g_snprintf( buf, sizeof( buf ), + "The user %s (%s) wants to add you to his/her buddy list. Do you want to allow this?", + handle, realname ); + do_ask_dialog( gc, buf, bla, msn_buddy_ask_yes, msn_buddy_ask_no ); +} + +char *msn_findheader( char *text, char *header, int len ) +{ + int hlen = strlen( header ), i; + char *ret; + + if( len == 0 ) + len = strlen( text ); + + i = 0; + while( ( i + hlen ) < len ) + { + /* Maybe this is a bit over-commented, but I just hate this part... */ + if( g_strncasecmp( text + i, header, hlen ) == 0 ) + { + /* Skip to the (probable) end of the header */ + i += hlen; + + /* Find the first non-[: \t] character */ + while( i < len && ( text[i] == ':' || text[i] == ' ' || text[i] == '\t' ) ) i ++; + + /* Make sure we're still inside the string */ + if( i >= len ) return( NULL ); + + /* Save the position */ + ret = text + i; + + /* Search for the end of this line */ + while( i < len && text[i] != '\r' && text[i] != '\n' ) i ++; + + /* Make sure we're still inside the string */ + if( i >= len ) return( NULL ); + + /* Copy the found data */ + return( g_strndup( ret, text + i - ret ) ); + } + + /* This wasn't the header we were looking for, skip to the next line. */ + while( i < len && ( text[i] != '\r' && text[i] != '\n' ) ) i ++; + while( i < len && ( text[i] == '\r' || text[i] == '\n' ) ) i ++; + + /* End of headers? */ + if( strncmp( text + i - 2, "\n\n", 2 ) == 0 || + strncmp( text + i - 4, "\r\n\r\n", 4 ) == 0 || + strncmp( text + i - 2, "\r\r", 2 ) == 0 ) + { + break; + } + } + + return( NULL ); +} + +/* *NOT* thread-safe, but that's not a problem for now... */ +char **msn_linesplit( char *line ) +{ + static char **ret = NULL; + static int size = 3; + int i, n = 0; + + if( ret == NULL ) + ret = g_new0( char*, size ); + + for( i = 0; line[i] && line[i] == ' '; i ++ ); + if( line[i] ) + { + ret[n++] = line + i; + for( i ++; line[i]; i ++ ) + { + if( line[i] == ' ' ) + line[i] = 0; + else if( line[i] != ' ' && !line[i-1] ) + ret[n++] = line + i; + + if( n >= size ) + ret = g_renew( char*, ret, size += 2 ); + } + } + ret[n] = NULL; + + return( ret ); +} + +/* This one handles input from a MSN Messenger server. Both the NS and SB servers usually give + commands, but sometimes they give additional data (payload). This function tries to handle + this all in a nice way and send all data to the right places. */ + +/* Return values: -1: Read error, abort connection. + 0: Command reported error; Abort *immediately*. (The connection does not exist anymore) + 1: OK */ + +int msn_handler( struct msn_handler_data *h ) +{ + int st; + + h->rxq = g_renew( char, h->rxq, h->rxlen + 1024 ); + st = read( h->fd, h->rxq + h->rxlen, 1024 ); + h->rxlen += st; + + if( st <= 0 ) + return( -1 ); + + while( st ) + { + int i; + + if( h->msglen == 0 ) + { + for( i = 0; i < h->rxlen; i ++ ) + { + if( h->rxq[i] == '\r' || h->rxq[i] == '\n' ) + { + char *cmd_text, **cmd; + int count; + + cmd_text = g_strndup( h->rxq, i ); + cmd = msn_linesplit( cmd_text ); + for( count = 0; cmd[count]; count ++ ); + st = h->exec_command( h->data, cmd, count ); + g_free( cmd_text ); + + /* If the connection broke, don't continue. We don't even exist anymore. */ + if( !st ) + return( 0 ); + + if( h->msglen ) + h->cmd_text = g_strndup( h->rxq, i ); + + /* Skip to the next non-emptyline */ + while( i < h->rxlen && ( h->rxq[i] == '\r' || h->rxq[i] == '\n' ) ) i ++; + + break; + } + } + + /* If we reached the end of the buffer, there's still an incomplete command there. + Return and wait for more data. */ + if( i == h->rxlen && h->rxq[i-1] != '\r' && h->rxq[i-1] != '\n' ) + break; + } + else + { + char *msg, **cmd; + int count; + + /* Do we have the complete message already? */ + if( h->msglen > h->rxlen ) + break; + + msg = g_strndup( h->rxq, h->msglen ); + cmd = msn_linesplit( h->cmd_text ); + for( count = 0; cmd[count]; count ++ ); + + st = h->exec_message( h->data, msg, h->msglen, cmd, count ); + g_free( msg ); + g_free( h->cmd_text ); + h->cmd_text = NULL; + + if( !st ) + return( 0 ); + + i = h->msglen; + h->msglen = 0; + } + + /* More data after this block? */ + if( i < h->rxlen ) + { + char *tmp; + + tmp = g_memdup( h->rxq + i, h->rxlen - i ); + g_free( h->rxq ); + h->rxq = tmp; + h->rxlen -= i; + i = 0; + } + else + /* If not, reset the rx queue and get lost. */ + { + g_free( h->rxq ); + h->rxq = g_new0( char, 1 ); + h->rxlen = 0; + return( 1 ); + } + } + + return( 1 ); +} diff --git a/protocols/msn/ns.c b/protocols/msn/ns.c new file mode 100644 index 00000000..f1bda1a4 --- /dev/null +++ b/protocols/msn/ns.c @@ -0,0 +1,661 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* MSN module - Notification server callbacks */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <ctype.h> +#include "nogaim.h" +#include "msn.h" +#include "passport.h" +#include "md5.h" + +static void msn_ns_callback( gpointer data, gint source, GaimInputCondition cond ); +static int msn_ns_command( gpointer data, char **cmd, int num_parts ); +static int msn_ns_message( gpointer data, char *msg, int msglen, char **cmd, int num_parts ); + +static void msn_auth_got_passport_id( struct passport_reply *rep ); + +void msn_ns_connected( gpointer data, gint source, GaimInputCondition cond ) +{ + struct gaim_connection *gc = data; + struct msn_data *md; + char s[1024]; + + if( !g_slist_find( msn_connections, gc ) ) + return; + + if( source == -1 ) + { + hide_login_progress( gc, "Could not connect to server" ); + signoff( gc ); + return; + } + + md = gc->proto_data; + + if( !md->handler ) + { + md->handler = g_new0( struct msn_handler_data, 1 ); + md->handler->data = gc; + md->handler->exec_command = msn_ns_command; + md->handler->exec_message = msn_ns_message; + } + else + { + if( md->handler->rxq ) + g_free( md->handler->rxq ); + + md->handler->rxlen = 0; + } + + md->handler->fd = md->fd; + md->handler->rxq = g_new0( char, 1 ); + + g_snprintf( s, sizeof( s ), "VER %d MSNP8 CVR0\r\n", ++md->trId ); + if( msn_write( gc, s, strlen( s ) ) ) + { + gc->inpa = gaim_input_add( md->fd, GAIM_INPUT_READ, msn_ns_callback, gc ); + set_login_progress( gc, 1, "Connected to server, waiting for reply" ); + } +} + +void msn_ns_callback( gpointer data, gint source, GaimInputCondition cond ) +{ + struct gaim_connection *gc = data; + struct msn_data *md = gc->proto_data; + + if( msn_handler( md->handler ) == -1 ) /* Don't do this on ret == 0, it's already done then. */ + { + hide_login_progress( gc, "Error while reading from server" ); + signoff( gc ); + } +} + +static int msn_ns_command( gpointer data, char **cmd, int num_parts ) +{ + struct gaim_connection *gc = data; + struct msn_data *md = gc->proto_data; + char buf[1024]; + + if( num_parts == 0 ) + { + /* Hrrm... Empty command...? Ignore? */ + return( 1 ); + } + + if( strcmp( cmd[0], "VER" ) == 0 ) + { + if( cmd[2] && strncmp( cmd[2], "MSNP8", 5 ) != 0 ) + { + hide_login_progress( gc, "Unsupported protocol" ); + signoff( gc ); + return( 0 ); + } + + g_snprintf( buf, sizeof( buf ), "CVR %d 0x0409 mac 10.2.0 ppc macmsgs 3.5.1 macmsgs %s\r\n", + ++md->trId, gc->username ); + return( msn_write( gc, buf, strlen( buf ) ) ); + } + else if( strcmp( cmd[0], "CVR" ) == 0 ) + { + /* We don't give a damn about the information we just received */ + g_snprintf( buf, sizeof( buf ), "USR %d TWN I %s\r\n", ++md->trId, gc->username ); + return( msn_write( gc, buf, strlen( buf ) ) ); + } + else if( strcmp( cmd[0], "XFR" ) == 0 ) + { + char *server; + int port; + + if( num_parts == 6 && strcmp( cmd[2], "NS" ) == 0 ) + { + gaim_input_remove( gc->inpa ); + gc->inpa = 0; + closesocket( md->fd ); + + server = strchr( cmd[3], ':' ); + if( !server ) + { + hide_login_progress_error( gc, "Syntax error" ); + signoff( gc ); + return( 0 ); + } + *server = 0; + port = atoi( server + 1 ); + server = cmd[3]; + + set_login_progress( gc, 1, "Transferring to other server" ); + + md->fd = proxy_connect( server, port, msn_ns_connected, gc ); + } + else if( num_parts == 6 && strcmp( cmd[2], "SB" ) == 0 ) + { + struct msn_switchboard *sb; + + server = strchr( cmd[3], ':' ); + if( !server ) + { + hide_login_progress_error( gc, "Syntax error" ); + signoff( gc ); + return( 0 ); + } + *server = 0; + port = atoi( server + 1 ); + server = cmd[3]; + + if( strcmp( cmd[4], "CKI" ) != 0 ) + { + hide_login_progress_error( gc, "Unknown authentication method for switchboard" ); + signoff( gc ); + return( 0 ); + } + + debug( "Connecting to a new switchboard with key %s", cmd[5] ); + sb = msn_sb_create( gc, server, port, cmd[5], MSN_SB_NEW ); + + if( md->msgq ) + { + struct msn_message *m = md->msgq->data; + GSList *l; + + sb->who = g_strdup( m->who ); + + /* Move all the messages to the first user in the message + queue to the switchboard message queue. */ + l = md->msgq; + while( l ) + { + m = l->data; + l = l->next; + if( strcmp( m->who, sb->who ) == 0 ) + { + sb->msgq = g_slist_append( sb->msgq, m ); + md->msgq = g_slist_remove( md->msgq, m ); + } + } + } + } + else + { + hide_login_progress_error( gc, "Syntax error" ); + signoff( gc ); + return( 0 ); + } + } + else if( strcmp( cmd[0], "USR" ) == 0 ) + { + if( num_parts == 5 && strcmp( cmd[2], "TWN" ) == 0 && strcmp( cmd[3], "S" ) == 0 ) + { + /* Time for some Passport black magic... */ + if( !passport_get_id( gc, gc->username, gc->password, cmd[4], msn_auth_got_passport_id ) ) + { + hide_login_progress_error( gc, "Error while contacting Passport server" ); + signoff( gc ); + return( 0 ); + } + } + else if( num_parts == 7 && strcmp( cmd[2], "OK" ) == 0 ) + { + http_decode( cmd[4] ); + + strncpy( gc->displayname, cmd[4], sizeof( gc->displayname ) ); + gc->displayname[sizeof(gc->displayname)-1] = 0; + + set_login_progress( gc, 1, "Authenticated, getting buddy list" ); + + g_snprintf( buf, sizeof( buf ), "SYN %d 0\r\n", ++md->trId ); + return( msn_write( gc, buf, strlen( buf ) ) ); + } + else + { + hide_login_progress( gc, "Unknown authentication type" ); + signoff( gc ); + return( 0 ); + } + } + else if( strcmp( cmd[0], "MSG" ) == 0 ) + { + if( num_parts != 4 ) + { + hide_login_progress_error( gc, "Syntax error" ); + signoff( gc ); + return( 0 ); + } + + md->handler->msglen = atoi( cmd[3] ); + + if( md->handler->msglen <= 0 ) + { + hide_login_progress_error( gc, "Syntax error" ); + signoff( gc ); + return( 0 ); + } + } + else if( strcmp( cmd[0], "SYN" ) == 0 ) + { + if( num_parts == 5 ) + { + md->buddycount = atoi( cmd[3] ); + + if( !*cmd[3] || md->buddycount == 0 ) + msn_logged_in( gc ); + } + else + { + /* Hrrm... This SYN reply doesn't really look like something we expected. + Let's assume everything is okay. */ + + msn_logged_in( gc ); + } + } + else if( strcmp( cmd[0], "GTC" ) == 0 ) + { + } + else if( strcmp( cmd[0], "BLP" ) == 0 ) + { + } + else if( strcmp( cmd[0], "PRP" ) == 0 ) + { + } + else if( strcmp( cmd[0], "LSG" ) == 0 ) + { + } + else if( strcmp( cmd[0], "LST" ) == 0 ) + { + int list; + + if( num_parts != 4 && num_parts != 5 ) + { + hide_login_progress( gc, "Syntax error" ); + signoff( gc ); + return( 0 ); + } + + http_decode( cmd[2] ); + list = atoi( cmd[3] ); + + if( list & 1 ) /* FL */ + { + add_buddy( gc, NULL, cmd[1], cmd[2] ); + } + if( list & 2 ) /* AL */ + { + gc->permit = g_slist_append( gc->permit, g_strdup( cmd[1] ) ); + } + if( list & 4 ) /* BL */ + { + gc->deny = g_slist_append( gc->deny, g_strdup( cmd[1] ) ); + } + if( list & 8 ) /* RL */ + { + if( ( list & 6 ) == 0 ) + msn_buddy_ask( gc, cmd[1], cmd[2] ); + } + + if( --md->buddycount == 0 ) + { + if( gc->flags & OPT_LOGGED_IN ) + { + serv_got_crap( gc, "Successfully transferred to different server" ); + g_snprintf( buf, sizeof( buf ), "CHG %d %s %d\r\n", ++md->trId, md->away_state->code, 0 ); + return( msn_write( gc, buf, strlen( buf ) ) ); + } + else + { + msn_logged_in( gc ); + } + } + } + else if( strcmp( cmd[0], "BPR" ) == 0 ) + { + } + else if( strcmp( cmd[0], "CHG" ) == 0 ) + { + } + else if( strcmp( cmd[0], "CHL" ) == 0 ) + { + md5_state_t state; + md5_byte_t digest[16]; + int i; + + if( num_parts != 3 ) + { + hide_login_progress_error( gc, "Syntax error" ); + signoff( gc ); + return( 0 ); + } + + md5_init( &state ); + md5_append( &state, (const md5_byte_t *) cmd[2], strlen( cmd[2] ) ); + md5_append( &state, (const md5_byte_t *) QRY_CODE, strlen( QRY_CODE ) ); + md5_finish( &state, digest ); + + g_snprintf( buf, sizeof( buf ), "QRY %d %s %d\r\n", ++md->trId, QRY_NAME, 32 ); + for( i = 0; i < 16; i ++ ) + g_snprintf( buf + strlen( buf ), 3, "%02x", digest[i] ); + + return( msn_write( gc, buf, strlen( buf ) ) ); + } + else if( strcmp( cmd[0], "QRY" ) == 0 ) + { + } + else if( strcmp( cmd[0], "QNG" ) == 0 ) + { + } + else if( strcmp( cmd[0], "ILN" ) == 0 ) + { + struct msn_away_state *st; + + if( num_parts != 6 ) + { + hide_login_progress_error( gc, "Syntax error" ); + signoff( gc ); + return( 0 ); + } + + http_decode( cmd[4] ); + serv_buddy_rename( gc, cmd[3], cmd[4] ); + + st = msn_away_state_by_code( cmd[2] ); + if( !st ) + { + /* FIXME: Warn/Bomb about unknown away state? */ + st = msn_away_state_list; + } + + serv_got_update( gc, cmd[3], 1, 0, 0, 0, st->number, 0 ); + } + else if( strcmp( cmd[0], "FLN" ) == 0 ) + { + if( cmd[1] ) + serv_got_update( gc, cmd[1], 0, 0, 0, 0, 0, 0 ); + } + else if( strcmp( cmd[0], "NLN" ) == 0 ) + { + struct msn_away_state *st; + + if( num_parts != 5 ) + { + hide_login_progress_error( gc, "Syntax error" ); + signoff( gc ); + return( 0 ); + } + + http_decode( cmd[3] ); + serv_buddy_rename( gc, cmd[2], cmd[3] ); + + st = msn_away_state_by_code( cmd[1] ); + if( !st ) + { + /* FIXME: Warn/Bomb about unknown away state? */ + st = msn_away_state_list; + } + + serv_got_update( gc, cmd[2], 1, 0, 0, 0, st->number, 0 ); + } + else if( strcmp( cmd[0], "RNG" ) == 0 ) + { + struct msn_switchboard *sb; + char *server; + int session, port; + + if( num_parts != 7 ) + { + hide_login_progress_error( gc, "Syntax error" ); + signoff( gc ); + return( 0 ); + } + + session = atoi( cmd[1] ); + + server = strchr( cmd[2], ':' ); + if( !server ) + { + hide_login_progress_error( gc, "Syntax error" ); + signoff( gc ); + return( 0 ); + } + *server = 0; + port = atoi( server + 1 ); + server = cmd[2]; + + if( strcmp( cmd[3], "CKI" ) != 0 ) + { + hide_login_progress_error( gc, "Unknown authentication method for switchboard" ); + signoff( gc ); + return( 0 ); + } + + debug( "Got a call from %s (session %d). Key = %s", cmd[5], session, cmd[4] ); + + sb = msn_sb_create( gc, server, port, cmd[4], session ); + sb->who = g_strdup( cmd[5] ); + } + else if( strcmp( cmd[0], "ADD" ) == 0 ) + { + if( num_parts == 6 && strcmp( cmd[2], "RL" ) == 0 ) + { + GSList *l; + + http_decode( cmd[5] ); + + if( strchr( cmd[4], '@' ) == NULL ) + { + hide_login_progress_error( gc, "Syntax error" ); + signoff( gc ); + return( 0 ); + } + + /* We got added by someone. If we don't have this person in permit/deny yet, inform the user. */ + for( l = gc->permit; l; l = l->next ) + if( g_strcasecmp( l->data, cmd[4] ) == 0 ) + return( 1 ); + + for( l = gc->deny; l; l = l->next ) + if( g_strcasecmp( l->data, cmd[4] ) == 0 ) + return( 1 ); + + msn_buddy_ask( gc, cmd[4], cmd[5] ); + } + } + else if( strcmp( cmd[0], "REM" ) == 0 ) + { + } + else if( strcmp( cmd[0], "OUT" ) == 0 ) + { + if( cmd[1] && strcmp( cmd[1], "OTH" ) == 0 ) + { + hide_login_progress_error( gc, "Someone else logged in with your account" ); + gc->wants_to_die = 1; + } + else if( cmd[1] && strcmp( cmd[1], "SSD" ) == 0 ) + { + hide_login_progress_error( gc, "Terminating session because of server shutdown" ); + } + else + { + hide_login_progress_error( gc, "Session terminated by remote server (reason unknown)" ); + } + + signoff( gc ); + return( 0 ); + } + else if( strcmp( cmd[0], "REA" ) == 0 ) + { + if( num_parts != 5 ) + { + hide_login_progress_error( gc, "Syntax error" ); + signoff( gc ); + return( 0 ); + } + + if( g_strcasecmp( cmd[3], gc->username ) == 0 ) + { + http_decode( cmd[4] ); + strncpy( gc->displayname, cmd[4], sizeof( gc->displayname ) ); + gc->displayname[sizeof(gc->displayname)-1] = 0; + } + else + { + /* This is not supposed to happen, but let's handle it anyway... */ + http_decode( cmd[4] ); + serv_buddy_rename( gc, cmd[3], cmd[4] ); + } + } + else if( strcmp( cmd[0], "IPG" ) == 0 ) + { + do_error_dialog( gc, "Received IPG command, we don't handle them yet.", "MSN" ); + + md->handler->msglen = atoi( cmd[1] ); + + if( md->handler->msglen <= 0 ) + { + hide_login_progress_error( gc, "Syntax error" ); + signoff( gc ); + return( 0 ); + } + } + else if( isdigit( cmd[0][0] ) ) + { + int num = atoi( cmd[0] ); + struct msn_status_code *err = msn_status_by_number( num ); + + g_snprintf( buf, sizeof( buf ), "Error reported by MSN server: %s", err->text ); + do_error_dialog( gc, buf, "MSN" ); + + if( err->flags & STATUS_FATAL ) + { + signoff( gc ); + return( 0 ); + } + } + else + { + debug( "Received unknown command from main server: %s", cmd[0] ); + } + + return( 1 ); +} + +static int msn_ns_message( gpointer data, char *msg, int msglen, char **cmd, int num_parts ) +{ + struct gaim_connection *gc = data; + char *body; + int blen = 0; + + if( !num_parts ) + return( 1 ); + + if( ( body = strstr( msg, "\r\n\r\n" ) ) ) + { + body += 4; + blen = msglen - ( body - msg ); + } + + if( strcmp( cmd[0], "MSG" ) == 0 ) + { + if( g_strcasecmp( cmd[1], "Hotmail" ) == 0 ) + { + char *ct = msn_findheader( msg, "Content-Type:", msglen ); + + if( !ct ) + return( 1 ); + + if( g_strncasecmp( ct, "application/x-msmsgssystemmessage", 33 ) == 0 ) + { + char *mtype; + char *arg1; + + if( !body ) + return( 1 ); + + mtype = msn_findheader( body, "Type:", blen ); + arg1 = msn_findheader( body, "Arg1:", blen ); + + if( mtype && strcmp( mtype, "1" ) == 0 ) + { + if( arg1 ) + serv_got_crap( gc, "The server is going down for maintenance in %s minutes.", arg1 ); + } + + if( arg1 ) g_free( arg1 ); + if( mtype ) g_free( mtype ); + } + else if( g_strncasecmp( ct, "text/x-msmsgsprofile", 20 ) == 0 ) + { + /* We don't care about this profile for now... */ + } + else if( g_strncasecmp( ct, "text/x-msmsgsinitialemailnotification", 37 ) == 0 ) + { + char *inbox = msn_findheader( body, "Inbox-Unread:", blen ); + char *folders = msn_findheader( body, "Folders-Unread:", blen ); + + if( inbox && folders ) + { + serv_got_crap( gc, "INBOX contains %s new messages, plus %s messages in other folders.", inbox, folders ); + } + } + else if( g_strncasecmp( ct, "text/x-msmsgsemailnotification", 30 ) == 0 ) + { + char *from = msn_findheader( body, "From-Addr:", blen ); + char *fromname = msn_findheader( body, "From:", blen ); + + if( from && fromname ) + { + serv_got_crap( gc, "Received an e-mail message from %s <%s>.", fromname, from ); + } + } + else if( g_strncasecmp( ct, "text/x-msmsgsactivemailnotification", 35 ) == 0 ) + { + /* Sorry, but this one really is *USELESS* */ + } + else + { + debug( "Can't handle %s packet from notification server", ct ); + } + + g_free( ct ); + } + } + + return( 1 ); +} + +static void msn_auth_got_passport_id( struct passport_reply *rep ) +{ + struct gaim_connection *gc = rep->data; + struct msn_data *md = gc->proto_data; + char *key = rep->result; + char buf[1024]; + + if( key == NULL ) + { + hide_login_progress( gc, "Error during Passport authentication" ); + signoff( gc ); + } + else + { + g_snprintf( buf, sizeof( buf ), "USR %d TWN S %s\r\n", ++md->trId, key ); + msn_write( gc, buf, strlen( buf ) ); + } +} diff --git a/protocols/msn/passport.c b/protocols/msn/passport.c new file mode 100644 index 00000000..640126a0 --- /dev/null +++ b/protocols/msn/passport.c @@ -0,0 +1,313 @@ +/* passport.c + * + * Functions to login to microsoft passport service for Messenger + * Copyright (C) 2004 Wouter Paesen <wouter@blue-gate.be> + * Copyright (C) 2004 Wilmer van der Gaast <wilmer@gaast.net> + * + * 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 is will be useful, + * bit WITHOU ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU 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 "ssl_client.h" +#include "passport.h" +#include "msn.h" +#include "bitlbee.h" +#include <ctype.h> +#include <errno.h> + +#define MSN_BUF_LEN 8192 + +static char *prd_cached = NULL; + +static char *passport_create_header( char *reply, char *email, char *pwd ); +static int passport_retrieve_dalogin( gpointer data, gpointer func, char *header ); +static void passport_retrieve_dalogin_connected( gpointer data, void *ssl, GaimInputCondition cond ); +static int passport_get_id_from( gpointer data, gpointer func, char *header_i, char *url ); +static void passport_get_id_connected( gpointer data, void *ssl, GaimInputCondition cond ); +static void destroy_reply( struct passport_reply *rep ); + + +int passport_get_id( gpointer data, char *username, char *password, char *cookie, gpointer func ) +{ + char *header = passport_create_header( cookie, username, password ); + + if( prd_cached ) + { + int st; + + st = passport_get_id_from( data, func, header, prd_cached ); + g_free( header ); + return( st ); + } + else + { + return( passport_retrieve_dalogin( data, func, header ) ); + } +} + + +static char *passport_create_header( char *reply, char *email, char *pwd ) +{ + char *buffer = g_new0( char, 2048 ); + char *currenttoken; + char *email_enc, *pwd_enc; + + email_enc = g_new0( char, strlen( email ) * 3 + 1 ); + strcpy( email_enc, email ); + http_encode( email_enc ); + + pwd_enc = g_new0( char, strlen( pwd ) * 3 + 1 ); + strcpy( pwd_enc, pwd ); + http_encode( pwd_enc ); + + currenttoken = strstr( reply, "lc=" ); + if( currenttoken == NULL ) + return( NULL ); + + g_snprintf( buffer, 2048, + "Authorization: Passport1.4 OrgVerb=GET," + "OrgURL=http%%3A%%2F%%2Fmessenger%%2Emsn%%2Ecom," + "sign-in=%s,pwd=%s,%s", email_enc, pwd_enc, + currenttoken ); + + g_free( email_enc ); + g_free( pwd_enc ); + + return( buffer ); +} + + +static int passport_retrieve_dalogin( gpointer data, gpointer func, char *header ) +{ + struct passport_reply *rep = g_new0( struct passport_reply, 1 ); + void *ssl; + + rep->data = data; + rep->func = func; + rep->header = header; + + ssl = ssl_connect( "nexus.passport.com", 443, passport_retrieve_dalogin_connected, rep ); + + if( !ssl ) + destroy_reply( rep ); + + return( ssl != NULL ); +} + +#define PPR_BUFFERSIZE 2048 +#define PPR_REQUEST "GET /rdr/pprdr.asp HTTP/1.0\r\n\r\n" +static void passport_retrieve_dalogin_connected( gpointer data, void *ssl, GaimInputCondition cond ) +{ + int ret; + char buffer[PPR_BUFFERSIZE+1]; + struct passport_reply *rep = data; + + if( !g_slist_find( msn_connections, rep->data ) ) + { + if( ssl ) ssl_disconnect( ssl ); + destroy_reply( rep ); + return; + } + + if( !ssl ) + { + rep->func( rep ); + destroy_reply( rep ); + return; + } + + ssl_write( ssl, PPR_REQUEST, strlen( PPR_REQUEST ) ); + + if( ( ret = ssl_read( ssl, buffer, PPR_BUFFERSIZE ) ) <= 0 ) + { + goto failure; + } + + { + char *dalogin = strstr( buffer, "DALogin=" ); + char *urlend; + + if( !dalogin ) + goto failure; + + dalogin += strlen( "DALogin=" ); + urlend = strchr( dalogin, ',' ); + if( urlend ) + *urlend = 0; + + /* strip the http(s):// part from the url */ + urlend = strstr( urlend, "://" ); + if( urlend ) + dalogin = urlend + strlen( "://" ); + + if( prd_cached == NULL ) + prd_cached = g_strdup( dalogin ); + } + + if( passport_get_id_from( rep->data, rep->func, rep->header, prd_cached ) ) + { + ssl_disconnect( ssl ); + destroy_reply( rep ); + return; + } + +failure: + ssl_disconnect( ssl ); + rep->func( rep ); + destroy_reply( rep ); +} + + +static int passport_get_id_from( gpointer data, gpointer func, char *header_i, char *url ) +{ + struct passport_reply *rep = g_new0( struct passport_reply, 1 ); + char server[512], *dummy; + void *ssl; + + rep->data = data; + rep->func = func; + rep->redirects = 4; + + strncpy( server, url, 512 ); + dummy = strchr( server, '/' ); + if( dummy ) + *dummy = 0; + + ssl = ssl_connect( server, 443, passport_get_id_connected, rep ); + + if( ssl ) + { + rep->header = g_strdup( header_i ); + rep->url = g_strdup( url ); + } + else + { + destroy_reply( rep ); + } + + return( ssl != NULL ); +} + +#define PPG_BUFFERSIZE 4096 +static void passport_get_id_connected( gpointer data, void *ssl, GaimInputCondition cond ) +{ + struct passport_reply *rep = data; + char server[512], buffer[PPG_BUFFERSIZE+1], *dummy; + int ret; + + if( !g_slist_find( msn_connections, rep->data ) ) + { + if( ssl ) ssl_disconnect( ssl ); + destroy_reply( rep ); + return; + } + + if( !ssl ) + { + rep->func( rep ); + destroy_reply( rep ); + return; + } + + memset( buffer, 0, PPG_BUFFERSIZE + 1 ); + + strncpy( server, rep->url, 512 ); + dummy = strchr( server, '/' ); + if( dummy == NULL ) + goto end; + + g_snprintf( buffer, PPG_BUFFERSIZE - 1, "GET %s HTTP/1.0\r\n" + "%s\r\n\r\n", dummy, rep->header ); + + ssl_write( ssl, buffer, strlen( buffer ) ); + memset( buffer, 0, PPG_BUFFERSIZE + 1 ); + + { + char *buffer2 = buffer; + + while( ( ( ret = ssl_read( ssl, buffer2, 512 ) ) > 0 ) && + ( buffer + PPG_BUFFERSIZE - buffer2 - ret - 512 >= 0 ) ) + { + buffer2 += ret; + } + } + + if( *buffer == 0 ) + goto end; + + if( ( dummy = strstr( buffer, "Location:" ) ) ) + { + char *urlend; + + rep->redirects --; + if( rep->redirects == 0 ) + goto end; + + dummy += strlen( "Location:" ); + while( isspace( *dummy ) ) dummy ++; + urlend = dummy; + while( !isspace( *urlend ) ) urlend ++; + *urlend = 0; + if( ( urlend = strstr( dummy, "://" ) ) ) + dummy = urlend + strlen( "://" ); + + g_free( rep->url ); + rep->url = g_strdup( dummy ); + + strncpy( server, dummy, sizeof( server ) - 1 ); + dummy = strchr( server, '/' ); + if( dummy ) *dummy = 0; + + ssl_disconnect( ssl ); + + if( ssl_connect( server, 443, passport_get_id_connected, rep ) ) + { + return; + } + else + { + rep->func( rep ); + destroy_reply( rep ); + return; + } + } + else if( strstr( buffer, "200 OK" ) ) + { + if( ( dummy = strstr( buffer, "from-PP='" ) ) ) + { + char *responseend; + + dummy += strlen( "from-PP='" ); + responseend = strchr( dummy, '\'' ); + if( responseend ) + *responseend = 0; + + rep->result = g_strdup( dummy ); + } + } + +end: + ssl_disconnect( ssl ); + rep->func( rep ); + destroy_reply( rep ); +} + + +static void destroy_reply( struct passport_reply *rep ) +{ + if( rep->result ) g_free( rep->result ); + if( rep->url ) g_free( rep->url ); + if( rep->header ) g_free( rep->header ); + if( rep ) g_free( rep ); +} diff --git a/protocols/msn/passport.h b/protocols/msn/passport.h new file mode 100644 index 00000000..63fef2e9 --- /dev/null +++ b/protocols/msn/passport.h @@ -0,0 +1,47 @@ +#ifndef __PASSPORT_H__ +#define __PASSPORT_H__ +/* passport.h + * + * Functions to login to Microsoft Passport Service for Messenger + * Copyright (C) 2004 Wouter Paesen <wouter@blue-gate.be>, + * Wilmer van der Gaast <wilmer@gaast.net> + * + * 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 is will be useful, + * bit WITHOU ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU 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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#ifndef _WIN32 +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <unistd.h> +#endif +#include "nogaim.h" + +struct passport_reply +{ + void *data; + char *result; + void (*func)( struct passport_reply * ); + char *url; + char *header; + int redirects; +}; + +int passport_get_id( gpointer data, char *username, char *password, char *cookie, gpointer func ); + +#endif /* __PASSPORT_H__ */ diff --git a/protocols/msn/sb.c b/protocols/msn/sb.c new file mode 100644 index 00000000..38ead0bd --- /dev/null +++ b/protocols/msn/sb.c @@ -0,0 +1,659 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* MSN module - Switchboard server callbacks and utilities */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <ctype.h> +#include "nogaim.h" +#include "msn.h" +#include "passport.h" +#include "md5.h" + +static void msn_sb_callback( gpointer data, gint source, GaimInputCondition cond ); +static int msn_sb_command( gpointer data, char **cmd, int num_parts ); +static int msn_sb_message( gpointer data, char *msg, int msglen, char **cmd, int num_parts ); + +int msn_sb_write( struct msn_switchboard *sb, char *s, int len ) +{ + int st; + + st = write( sb->fd, s, len ); + if( st != len ) + { + msn_sb_destroy( sb ); + return( 0 ); + } + + return( 1 ); +} + +struct msn_switchboard *msn_sb_create( struct gaim_connection *gc, char *host, int port, char *key, int session ) +{ + struct msn_data *md = gc->proto_data; + struct msn_switchboard *sb = g_new0( struct msn_switchboard, 1 ); + + sb->fd = proxy_connect( host, port, msn_sb_connected, sb ); + if( sb->fd < 0 ) + { + g_free( sb ); + return( NULL ); + } + + sb->gc = gc; + sb->key = g_strdup( key ); + sb->session = session; + + msn_switchboards = g_slist_append( msn_switchboards, sb ); + md->switchboards = g_slist_append( md->switchboards, sb ); + + return( sb ); +} + +struct msn_switchboard *msn_sb_by_handle( struct gaim_connection *gc, char *handle ) +{ + struct msn_data *md = gc->proto_data; + struct msn_switchboard *sb; + GSList *l; + + for( l = md->switchboards; l; l = l->next ) + { + sb = l->data; + if( sb->who && strcmp( sb->who, handle ) == 0 ) + return( sb ); + } + + return( NULL ); +} + +struct msn_switchboard *msn_sb_by_id( struct gaim_connection *gc, int id ) +{ + struct msn_data *md = gc->proto_data; + struct msn_switchboard *sb; + GSList *l; + + for( l = md->switchboards; l; l = l->next ) + { + sb = l->data; + if( sb->chat && sb->chat->id == id ) + return( sb ); + } + + return( NULL ); +} + +struct msn_switchboard *msn_sb_spare( struct gaim_connection *gc ) +{ + struct msn_data *md = gc->proto_data; + struct msn_switchboard *sb; + GSList *l; + + for( l = md->switchboards; l; l = l->next ) + { + sb = l->data; + if( !sb->who && !sb->chat ) + return( sb ); + } + + return( NULL ); +} + +int msn_sb_sendmessage( struct msn_switchboard *sb, char *text ) +{ + if( sb->ready ) + { + char cmd[1024], *buf; + int i, j; + + if( strcmp( text, TYPING_NOTIFICATION_MESSAGE ) != 0 ) + { + buf = g_new0( char, sizeof( MSN_MESSAGE_HEADERS ) + strlen( text ) * 2 ); + i = strlen( MSN_MESSAGE_HEADERS ); + + strcpy( buf, MSN_MESSAGE_HEADERS ); + for( j = 0; text[j]; j ++ ) + { + if( text[j] == '\n' ) + buf[i++] = '\r'; + + buf[i++] = text[j]; + } + } + else + { + i = strlen( MSN_TYPING_HEADERS ) + strlen( sb->gc->username ); + buf = g_new0( char, strlen( MSN_TYPING_HEADERS ) + strlen( sb->gc->username ) ); + i = g_snprintf( buf, i, MSN_TYPING_HEADERS, sb->gc->username ); + } + + g_snprintf( cmd, sizeof( cmd ), "MSG %d N %d\r\n", ++sb->trId, i ); + if( msn_sb_write( sb, cmd, strlen( cmd ) ) && msn_sb_write( sb, buf, i ) ) + { + g_free( buf ); + return( 1 ); + } + else + { + g_free( buf ); + return( 0 ); + } + } + else if( sb->who ) + { + struct msn_message *m = g_new0( struct msn_message, 1 ); + + m->who = g_strdup( "" ); + m->text = g_strdup( text ); + sb->msgq = g_slist_append( sb->msgq, m ); + + return( 1 ); + } + else + { + return( 0 ); + } +} + +void msn_sb_to_chat( struct msn_switchboard *sb ) +{ + struct gaim_connection *gc = sb->gc; + char buf[1024]; + + /* Create the groupchat structure. */ + g_snprintf( buf, sizeof( buf ), "MSN groupchat session %d", sb->session ); + sb->chat = serv_got_joined_chat( gc, ++msn_chat_id, buf ); + + /* Populate the channel. */ + if( sb->who ) add_chat_buddy( sb->chat, sb->who ); + add_chat_buddy( sb->chat, gc->username ); + + /* And make sure the switchboard doesn't look like a regular chat anymore. */ + if( sb->who ) + { + g_free( sb->who ); + sb->who = NULL; + } +} + +void msn_sb_destroy( struct msn_switchboard *sb ) +{ + struct gaim_connection *gc = sb->gc; + struct msn_data *md = gc->proto_data; + + debug( "Destroying switchboard: %s", sb->who ? sb->who : sb->key ? sb->key : "" ); + + if( sb->key ) g_free( sb->key ); + if( sb->who ) g_free( sb->who ); + + if( sb->msgq ) + { + struct msn_message *m; + GSList *l; + + for( l = sb->msgq; l; l = l->next ) + { + m = l->data; + g_free( m->who ); + g_free( m->text ); + g_free( m ); + } + g_slist_free( sb->msgq ); + + serv_got_crap( gc, "Warning: Closing down MSN switchboard connection with unsent message(s), you'll have to resend them." ); + } + + if( sb->chat ) + { + serv_got_chat_left( gc, sb->chat->id ); + } + + if( sb->handler ) + { + if( sb->handler->rxq ) g_free( sb->handler->rxq ); + if( sb->handler->cmd_text ) g_free( sb->handler->cmd_text ); + g_free( sb->handler ); + } + + if( sb->inp ) gaim_input_remove( sb->inp ); + closesocket( sb->fd ); + + msn_switchboards = g_slist_remove( msn_switchboards, sb ); + md->switchboards = g_slist_remove( md->switchboards, sb ); + g_free( sb ); +} + +void msn_sb_connected( gpointer data, gint source, GaimInputCondition cond ) +{ + struct msn_switchboard *sb = data; + struct gaim_connection *gc; + struct msn_data *md; + char buf[1024]; + + /* Are we still alive? */ + if( !g_slist_find( msn_switchboards, sb ) ) + return; + + gc = sb->gc; + md = gc->proto_data; + + if( source != sb->fd ) + { + debug( "ERROR %d while connecting to switchboard server", 1 ); + msn_sb_destroy( sb ); + return; + } + + /* Prepare the callback */ + sb->handler = g_new0( struct msn_handler_data, 1 ); + sb->handler->fd = sb->fd; + sb->handler->rxq = g_new0( char, 1 ); + sb->handler->data = sb; + sb->handler->exec_command = msn_sb_command; + sb->handler->exec_message = msn_sb_message; + + if( sb->session == MSN_SB_NEW ) + g_snprintf( buf, sizeof( buf ), "USR %d %s %s\r\n", ++sb->trId, gc->username, sb->key ); + else + g_snprintf( buf, sizeof( buf ), "ANS %d %s %s %d\r\n", ++sb->trId, gc->username, sb->key, sb->session ); + + if( msn_sb_write( sb, buf, strlen( buf ) ) ) + sb->inp = gaim_input_add( sb->fd, GAIM_INPUT_READ, msn_sb_callback, sb ); + else + debug( "ERROR %d while connecting to switchboard server", 2 ); +} + +static void msn_sb_callback( gpointer data, gint source, GaimInputCondition cond ) +{ + struct msn_switchboard *sb = data; + + if( msn_handler( sb->handler ) == -1 ) + { + debug( "ERROR: Switchboard died" ); + msn_sb_destroy( sb ); + } +} + +static int msn_sb_command( gpointer data, char **cmd, int num_parts ) +{ + struct msn_switchboard *sb = data; + struct gaim_connection *gc = sb->gc; + char buf[1024]; + + if( !num_parts ) + { + /* Hrrm... Empty command...? Ignore? */ + return( 1 ); + } + + if( strcmp( cmd[0], "XFR" ) == 0 ) + { + hide_login_progress_error( gc, "Received an XFR from a switchboard server, unable to comply! This is likely to be a bug, please report it!" ); + signoff( gc ); + return( 0 ); + } + else if( strcmp( cmd[0], "USR" ) == 0 ) + { + if( num_parts != 5 ) + { + msn_sb_destroy( sb ); + return( 0 ); + } + + if( strcmp( cmd[2], "OK" ) != 0 ) + { + msn_sb_destroy( sb ); + return( 0 ); + } + + if( sb->who ) + { + g_snprintf( buf, sizeof( buf ), "CAL %d %s\r\n", ++sb->trId, sb->who ); + return( msn_sb_write( sb, buf, strlen( buf ) ) ); + } + else + { + debug( "Just created a switchboard, but I don't know what to do with it." ); + } + } + else if( strcmp( cmd[0], "IRO" ) == 0 ) + { + int num, tot; + + if( num_parts != 6 ) + { + msn_sb_destroy( sb ); + return( 0 ); + } + + num = atoi( cmd[2] ); + tot = atoi( cmd[3] ); + + if( tot <= 0 ) + { + msn_sb_destroy( sb ); + return( 0 ); + } + else if( tot > 1 ) + { + char buf[1024]; + + if( num == 1 ) + { + g_snprintf( buf, sizeof( buf ), "MSN groupchat session %d", sb->session ); + sb->chat = serv_got_joined_chat( gc, ++msn_chat_id, buf ); + + g_free( sb->who ); + sb->who = NULL; + } + + add_chat_buddy( sb->chat, cmd[4] ); + + if( num == tot ) + { + add_chat_buddy( sb->chat, gc->username ); + } + } + } + else if( strcmp( cmd[0], "ANS" ) == 0 ) + { + if( num_parts != 3 ) + { + msn_sb_destroy( sb ); + return( 0 ); + } + + if( strcmp( cmd[2], "OK" ) != 0 ) + { + debug( "Switchboard server sent a negative ANS reply" ); + msn_sb_destroy( sb ); + return( 0 ); + } + + sb->ready = 1; + } + else if( strcmp( cmd[0], "CAL" ) == 0 ) + { + if( num_parts != 4 || !isdigit( cmd[3][0] ) ) + { + msn_sb_destroy( sb ); + return( 0 ); + } + + sb->session = atoi( cmd[3] ); + } + else if( strcmp( cmd[0], "JOI" ) == 0 ) + { + if( num_parts != 3 ) + { + msn_sb_destroy( sb ); + return( 0 ); + } + + if( sb->who && g_strcasecmp( cmd[1], sb->who ) == 0 ) + { + /* The user we wanted to talk to is finally there, let's send the queued messages then. */ + struct msn_message *m; + GSList *l; + int st = 1; + + debug( "%s arrived in the switchboard session, now sending queued message(s)", cmd[1] ); + + /* Without this, sendmessage() will put everything back on the queue... */ + sb->ready = 1; + + while( ( l = sb->msgq ) ) + { + m = l->data; + if( st ) + { + /* This hack is meant to convert a regular new chat into a groupchat */ + if( strcmp( m->text, GROUPCHAT_SWITCHBOARD_MESSAGE ) == 0 ) + msn_sb_to_chat( sb ); + else + st = msn_sb_sendmessage( sb, m->text ); + } + g_free( m->text ); + g_free( m->who ); + g_free( m ); + + sb->msgq = g_slist_remove( sb->msgq, m ); + } + + return( st ); + } + else if( sb->who ) + { + debug( "Converting chat with %s to a groupchat because %s joined the session.", sb->who, cmd[1] ); + + /* This SB is a one-to-one chat right now, but someone else is joining. */ + msn_sb_to_chat( sb ); + + add_chat_buddy( sb->chat, cmd[1] ); + } + else if( sb->chat ) + { + add_chat_buddy( sb->chat, cmd[1] ); + sb->ready = 1; + } + else + { + /* PANIC! */ + } + } + else if( strcmp( cmd[0], "MSG" ) == 0 ) + { + if( num_parts != 4 ) + { + msn_sb_destroy( sb ); + return( 0 ); + } + + sb->handler->msglen = atoi( cmd[3] ); + + if( sb->handler->msglen <= 0 ) + { + debug( "Received a corrupted message on the switchboard, the switchboard will be closed" ); + msn_sb_destroy( sb ); + return( 0 ); + } + } + else if( strcmp( cmd[0], "BYE" ) == 0 ) + { + if( num_parts < 2 ) + { + msn_sb_destroy( sb ); + return( 0 ); + } + + /* if( cmd[2] && *cmd[2] == '1' ) -=> Chat is being cleaned up because of idleness */ + + if( sb->who ) + { + /* This is a single-person chat, and the other person is leaving. */ + g_free( sb->who ); + sb->who = NULL; + sb->ready = 0; + + debug( "Person %s left the one-to-one switchboard connection. Keeping it around as a spare...", cmd[1] ); + + /* We could clean up the switchboard now, but keeping it around + as a spare for a next conversation sounds more sane to me. + The server will clean it up when it's idle for too long. */ + } + else if( sb->chat ) + { + remove_chat_buddy( sb->chat, cmd[1], "" ); + } + else + { + /* PANIC! */ + } + } + else if( isdigit( cmd[0][0] ) ) + { + int num = atoi( cmd[0] ); + struct msn_status_code *err = msn_status_by_number( num ); + + g_snprintf( buf, sizeof( buf ), "Error reported by switchboard server: %s", err->text ); + do_error_dialog( gc, buf, "MSN" ); + + if( err->flags & STATUS_SB_FATAL ) + { + msn_sb_destroy( sb ); + return( 0 ); + } + else if( err->flags & STATUS_FATAL ) + { + signoff( gc ); + return( 0 ); + } + } + else + { + debug( "Received unknown command from switchboard server: %s", cmd[0] ); + } + + return( 1 ); +} + +static int msn_sb_message( gpointer data, char *msg, int msglen, char **cmd, int num_parts ) +{ + struct msn_switchboard *sb = data; + struct gaim_connection *gc = sb->gc; + char *body; + int blen = 0; + + if( !num_parts ) + return( 1 ); + + if( ( body = strstr( msg, "\r\n\r\n" ) ) ) + { + body += 4; + blen = msglen - ( body - msg ); + } + + if( strcmp( cmd[0], "MSG" ) == 0 ) + { + char *ct = msn_findheader( msg, "Content-Type:", msglen ); + + if( !ct ) + return( 1 ); + + if( g_strncasecmp( ct, "text/plain", 10 ) == 0 ) + { + g_free( ct ); + + if( !body ) + return( 1 ); + + if( sb->who ) + { + serv_got_im( gc, cmd[1], body, 0, 0, blen ); + } + else if( sb->chat ) + { + serv_got_chat_in( gc, sb->chat->id, cmd[1], 0, body, 0 ); + } + else + { + /* PANIC! */ + } + } + else if( g_strncasecmp( ct, "text/x-msmsgsinvite", 19 ) == 0 ) + { + char *itype = msn_findheader( body, "Application-GUID:", blen ); + char buf[1024]; + + g_free( ct ); + + *buf = 0; + + if( !itype ) + return( 1 ); + + /* File transfer. */ + if( strcmp( itype, "{5D3E02AB-6190-11d3-BBBB-00C04F795683}" ) == 0 ) + { + char *name = msn_findheader( body, "Application-File:", blen ); + char *size = msn_findheader( body, "Application-FileSize:", blen ); + + if( name && size ) + { + g_snprintf( buf, sizeof( buf ), "<< \x02""BitlBee\x02"" - Filetransfer: `%s', %s bytes >>\n" + "Filetransfers are not supported by BitlBee for now...", name, size ); + } + else + { + strcpy( buf, "<< \x02""BitlBee\x02"" - Corrupted MSN filetransfer invitation message >>" ); + } + + if( name ) g_free( name ); + if( size ) g_free( size ); + } + else + { + char *iname = msn_findheader( body, "Application-Name:", blen ); + + g_snprintf( buf, sizeof( buf ), "<< \x02""BitlBee\x02"" - Unknown MSN invitation - %s (%s) >>", + itype, iname ? iname : "no name" ); + + if( iname ) g_free( iname ); + } + + g_free( itype ); + + if( !*buf ) + return( 1 ); + + if( sb->who ) + { + serv_got_im( gc, cmd[1], buf, 0, 0, strlen( buf ) ); + } + else if( sb->chat ) + { + serv_got_chat_in( gc, sb->chat->id, cmd[1], 0, buf, 0 ); + } + else + { + /* PANIC! */ + } + } + else if( g_strncasecmp( ct, "text/x-msmsgscontrol", 20 ) == 0 ) + { + char *who = msn_findheader( msg, "TypingUser:", msglen ); + + if( who ) + { + serv_got_typing( gc, who, 5 ); + g_free( who ); + } + + g_free( ct ); + } + else + { + g_free( ct ); + } + } + + return( 1 ); +} diff --git a/protocols/msn/tables.c b/protocols/msn/tables.c new file mode 100644 index 00000000..0cd80c01 --- /dev/null +++ b/protocols/msn/tables.c @@ -0,0 +1,166 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* MSN module - Some tables with useful data */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "nogaim.h" +#include "msn.h" + +struct msn_away_state msn_away_state_list[] = +{ + { 0, "NLN", "Available" }, + { 1, "BSY", "Busy" }, + { 3, "IDL", "Idle" }, + { 5, "BRB", "Be Right Back" }, + { 7, "AWY", "Away" }, + { 9, "PHN", "On the Phone" }, + { 11, "LUN", "Out to Lunch" }, + { 13, "HDN", "Hidden" }, + { -1, "", "" } +}; + +struct msn_away_state *msn_away_state_by_number( int number ) +{ + int i; + + for( i = 0; msn_away_state_list[i].number > -1; i ++ ) + if( msn_away_state_list[i].number == number ) + return( msn_away_state_list + i ); + + return( NULL ); +} + +struct msn_away_state *msn_away_state_by_code( char *code ) +{ + int i; + + for( i = 0; msn_away_state_list[i].number > -1; i ++ ) + if( g_strcasecmp( msn_away_state_list[i].code, code ) == 0 ) + return( msn_away_state_list + i ); + + return( NULL ); +} + +struct msn_away_state *msn_away_state_by_name( char *name ) +{ + int i; + + for( i = 0; msn_away_state_list[i].number > -1; i ++ ) + if( g_strcasecmp( msn_away_state_list[i].name, name ) == 0 ) + return( msn_away_state_list + i ); + + return( NULL ); +} + +struct msn_status_code msn_status_code_list[] = +{ + { 200, "Invalid syntax", 0 }, + { 201, "Invalid parameter", 0 }, + { 205, "Invalid (non-existent) handle", 0 }, + { 206, "Domain name missing", 0 }, + { 207, "Already logged in", 0 }, + { 208, "Invalid handle", 0 }, + { 209, "Forbidden nickname", 0 }, + { 210, "Buddy list too long", 0 }, + { 215, "Handle is already in list", 0 }, + { 216, "Handle is not in list", 0 }, + { 217, "Person is off-line or non-existent", 0 }, + { 218, "Already in that mode", 0 }, + { 219, "Handle is already in opposite list", 0 }, + { 223, "Too many groups", 0 }, + { 224, "Invalid group or already in list", 0 }, + { 225, "Handle is not in that group", 0 }, + { 229, "Group name too long", 0 }, + { 230, "Cannot remove that group", 0 }, + { 231, "Invalid group", 0 }, + { 280, "Switchboard failed", STATUS_SB_FATAL }, + { 281, "Transfer to switchboard failed", 0 }, + + { 300, "Required field missing", 0 }, + { 302, "Not logged in", 0 }, + + { 500, "Internal server error/Account banned", STATUS_FATAL }, + { 501, "Database server error", STATUS_FATAL }, + { 502, "Command disabled", 0 }, + { 510, "File operation failed", STATUS_FATAL }, + { 520, "Memory allocation failed", STATUS_FATAL }, + { 540, "Challenge response invalid", STATUS_FATAL }, + + { 600, "Server is busy", STATUS_FATAL }, + { 601, "Server is unavailable", STATUS_FATAL }, + { 602, "Peer nameserver is down", STATUS_FATAL }, + { 603, "Database connection failed", STATUS_FATAL }, + { 604, "Server is going down", STATUS_FATAL }, + { 605, "Server is unavailable", STATUS_FATAL }, + + { 700, "Could not create connection", STATUS_FATAL }, + { 710, "Invalid CVR parameters", STATUS_FATAL }, + { 711, "Write is blocking", STATUS_FATAL }, + { 712, "Session is overloaded", STATUS_FATAL }, + { 713, "Calling too rapidly", 0 }, + { 714, "Too many sessions", STATUS_FATAL }, + { 715, "Not expected/Invalid argument/action", 0 }, + { 717, "Bad friend file", STATUS_FATAL }, + { 731, "Not expected/Invalid argument", 0 }, + + { 800, "Changing too rapidly", 0 }, + + { 910, "Server is busy", STATUS_FATAL }, + { 911, "Authentication failed", STATUS_FATAL }, + { 912, "Server is busy", STATUS_FATAL }, + { 913, "Not allowed when hiding", 0 }, + { 914, "Server is unavailable", STATUS_FATAL }, + { 915, "Server is unavailable", STATUS_FATAL }, + { 916, "Server is unavailable", STATUS_FATAL }, + { 917, "Authentication failed", STATUS_FATAL }, + { 918, "Server is busy", STATUS_FATAL }, + { 919, "Server is busy", STATUS_FATAL }, + { 920, "Not accepting new principals", 0 }, /* When a sb is full? */ + { 922, "Server is busy", STATUS_FATAL }, + { 923, "Kids Passport without parental consent", STATUS_FATAL }, + { 924, "Passport account not yet verified", STATUS_FATAL }, + { 928, "Bad ticket", STATUS_FATAL }, + { -1, NULL, 0 } +}; + +struct msn_status_code *msn_status_by_number( int number ) +{ + static struct msn_status_code *unknown = NULL; + int i; + + for( i = 0; msn_status_code_list[i].number >= 0; i ++ ) + if( msn_status_code_list[i].number == number ) + return( msn_status_code_list + i ); + + if( unknown == NULL ) + { + unknown = g_new0( struct msn_status_code, 1 ); + unknown->text = g_new0( char, 128 ); + } + + unknown->number = number; + unknown->flags = 0; + g_snprintf( unknown->text, 128, "Unknown error (%d)", number ); + + return( unknown ); +} diff --git a/protocols/nogaim.c b/protocols/nogaim.c new file mode 100644 index 00000000..97aa30cf --- /dev/null +++ b/protocols/nogaim.c @@ -0,0 +1,1082 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* + * nogaim + * + * Gaim without gaim - for BitlBee + * + * This file contains functions called by the Gaim IM-modules. It's written + * from scratch for BitlBee and doesn't contain any code from Gaim anymore + * (except for the function names). + * + * Copyright 2002-2004 Wilmer van der Gaast <lintux@lintux.cx> + */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#define BITLBEE_CORE +#include "nogaim.h" +#include <ctype.h> +#include <iconv.h> + +struct prpl *proto_prpl[PROTO_MAX]; +char proto_name[PROTO_MAX][8] = { "TOC", "OSCAR", "YAHOO", "ICQ", "MSN", "", "", "", "JABBER", "", "", "", "", "", "", "" }; + +static char *proto_away_alias[7][5] = +{ + { "Away from computer", "Away", "Extended away", NULL }, + { "NA", "N/A", "Not available", NULL }, + { "Busy", "Do not disturb", "DND", "Occupied", NULL }, + { "Be right back", "BRB", NULL }, + { "On the phone", "Phone", "On phone", NULL }, + { "Out to lunch", "Lunch", "Food", NULL }, + { NULL } +}; +static char *proto_away_alias_find( GList *gcm, char *away ); + +static int remove_chat_buddy_silent( struct conversation *b, char *handle ); + +GSList *connections; + + +/* nogaim.c */ + +void nogaim_init() +{ + proto_prpl[PROTO_MSN] = g_new0 ( struct prpl, 1 ); +#ifdef WITH_MSN + msn_init( proto_prpl[PROTO_MSN] ); +#endif + + proto_prpl[PROTO_OSCAR] = g_new0( struct prpl, 1 ); +#ifdef WITH_OSCAR + oscar_init( proto_prpl[PROTO_OSCAR] ); +#endif + + proto_prpl[PROTO_YAHOO] = g_new0( struct prpl, 1 ); +#ifdef WITH_YAHOO + byahoo_init( proto_prpl[PROTO_YAHOO] ); +#endif + + proto_prpl[PROTO_JABBER] = g_new0( struct prpl, 1 ); +#ifdef WITH_JABBER + jabber_init( proto_prpl[PROTO_JABBER] ); +#endif +} + +GSList *get_connections() { return connections; } + +int proto_away( struct gaim_connection *gc, char *away ) +{ + GList *m, *ms; + char *s; + + if( !away ) away = ""; + ms = m = gc->prpl->away_states( gc ); + + while( m ) + { + if( *away ) + { + if( g_strncasecmp( m->data, away, strlen( m->data ) ) == 0 ) + break; + } + else + { + if( g_strcasecmp( m->data, "Available" ) == 0 ) + break; + if( g_strcasecmp( m->data, "Online" ) == 0 ) + break; + } + m = m->next; + } + + if( m ) + { + gc->prpl->set_away( gc, m->data, *away ? away : NULL ); + } + else + { + s = proto_away_alias_find( ms, away ); + if( s ) + { + gc->prpl->set_away( gc, s, away ); + if( set_getint( gc->irc, "debug" ) ) + irc_usermsg( gc->irc, "Setting away state for %s to %s", proto_name[gc->protocol], s ); + } + else + gc->prpl->set_away( gc, GAIM_AWAY_CUSTOM, away ); + } + + g_list_free( ms ); + + return( 1 ); +} + +static char *proto_away_alias_find( GList *gcm, char *away ) +{ + GList *m; + int i, j; + + for( i = 0; *proto_away_alias[i]; i ++ ) + { + for( j = 0; proto_away_alias[i][j]; j ++ ) + if( g_strncasecmp( away, proto_away_alias[i][j], strlen( proto_away_alias[i][j] ) ) == 0 ) + break; + + if( !proto_away_alias[i][j] ) /* If we reach the end, this row */ + continue; /* is not what we want. Next! */ + + /* Now find an entry in this row which exists in gcm */ + for( j = 0; proto_away_alias[i][j]; j ++ ) + { + m = gcm; + while( m ) + { + if( g_strcasecmp( proto_away_alias[i][j], m->data ) == 0 ) + return( proto_away_alias[i][j] ); + m = m->next; + } + } + } + + return( NULL ); +} + +/* Compare two handles for a specific protocol. For most protocols, + g_strcasecmp is okay, but for AIM, for example, it's not. This really + should be a compare function inside the PRPL module, but I do it this + way for now because I don't want to touch the Gaim code too much since + it's not going to be here for too long anymore. */ +int handle_cmp( char *a, char *b, int protocol ) +{ + if( protocol == PROTO_TOC || protocol == PROTO_ICQ ) + { + /* AIM, being teh evil, thinks it's cool that users can put + random spaces in screennames. But "A B" and "AB" are + equal. Hrmm, okay. */ + while( 1 ) + { + while( *a == ' ' ) a ++; + while( *b == ' ' ) b ++; + + if( *a && *b ) + { + if( tolower( *a ) != tolower( *b ) ) + return( 1 ); + } + else if( *a || *b ) + return( 1 ); + else + return( 0 ); + + a ++; + b ++; + } + } + else + { + return( g_strcasecmp( a, b ) ); + } +} + + +/* multi.c */ + +struct gaim_connection *new_gaim_conn( struct aim_user *user ) +{ + struct gaim_connection *gc; + account_t *a; + + gc = g_new0( struct gaim_connection, 1 ); + + gc->protocol = user->protocol; + gc->prpl = proto_prpl[gc->protocol]; + g_snprintf( gc->username, sizeof( gc->username ), "%s", user->username ); + g_snprintf( gc->password, sizeof( gc->password ), "%s", user->password ); + /* [MD] BUGFIX: don't set gc->irc to the global IRC, but use the one from the struct aim_user. + * This fixes daemon mode breakage where IRC doesn't point to the currently active connection. + */ + gc->irc=user->irc; + + connections = g_slist_append( connections, gc ); + + user->gc = gc; + gc->user = user; + + // Find the account_t so we can set its gc pointer + for( a = gc->irc->accounts; a; a = a->next ) + if( ( struct aim_user * ) a->gc == user ) + { + a->gc = gc; + break; + } + + return( gc ); +} + +void destroy_gaim_conn( struct gaim_connection *gc ) +{ + account_t *a; + + /* Destroy the pointer to this connection from the account list */ + for( a = gc->irc->accounts; a; a = a->next ) + if( a->gc == gc ) + { + a->gc = NULL; + break; + } + + connections = g_slist_remove( connections, gc ); + g_free( gc->user ); + g_free( gc ); +} + +void set_login_progress( struct gaim_connection *gc, int step, char *msg ) +{ + irc_usermsg( gc->irc, "%s(%s) - Logging in: %s", proto_name[gc->protocol], gc->username, msg ); +} + +/* Errors *while* logging in */ +void hide_login_progress( struct gaim_connection *gc, char *msg ) +{ + irc_usermsg( gc->irc, "%s(%s) - Login error: %s", proto_name[gc->protocol], gc->username, msg ); +} + +/* Errors *after* logging in */ +void hide_login_progress_error( struct gaim_connection *gc, char *msg ) +{ + irc_usermsg( gc->irc, "%s(%s) - Logged out: %s", proto_name[gc->protocol], gc->username, msg ); +} + +void serv_got_crap( struct gaim_connection *gc, char *format, ... ) +{ + va_list params; + char text[1024], buf[1024]; + char *msg; + + va_start( params, format ); + g_vsnprintf( text, sizeof( text ), format, params ); + va_end( params ); + + if( g_strncasecmp( set_getstr( gc->irc, "charset" ), "none", 4 ) != 0 && + do_iconv( "UTF8", set_getstr( gc->irc, "charset" ), text, buf, 0, 1024 ) != -1 ) + msg = buf; + else + msg = text; + + /* if( g_strcasecmp( set_getstr(gc->irc, "html" ), "strip" ) == 0 ) */ + if( gc->flags & OPT_CONN_HTML ) + strip_html( msg ); + + irc_usermsg( gc->irc, "%s(%s) - %s", proto_name[gc->protocol], gc->username, msg ); +} + +static gboolean send_keepalive( gpointer d ) +{ + struct gaim_connection *gc = d; + + if( gc->prpl && gc->prpl->keepalive ) + gc->prpl->keepalive( gc ); + + return TRUE; +} + +void account_online( struct gaim_connection *gc ) +{ + user_t *u; + + /* MSN servers sometimes redirect you to a different server and do + the whole login sequence again, so subsequent calls to this + function should be handled correctly. (IOW, ignored) */ + if( gc->flags & OPT_LOGGED_IN ) + return; + + u = user_find( gc->irc, gc->irc->nick ); + + irc_usermsg( gc->irc, "%s(%s) - Logged in", proto_name[gc->protocol], gc->username ); + + gc->keepalive = g_timeout_add( 60000, send_keepalive, gc ); + gc->flags |= OPT_LOGGED_IN; + + if( u && u->away ) proto_away( gc, u->away ); + + if( gc->protocol == PROTO_ICQ ) + { + for( u = gc->irc->users; u; u = u->next ) + if( u->gc == gc ) + break; + + if( u == NULL ) + irc_usermsg( gc->irc, "\x02""***\x02"" BitlBee now supports ICQ server-side contact lists. " + "See \x02""help import_buddies\x02"" for more information." ); + } +} + +gboolean auto_reconnect( gpointer data ) +{ + account_t *a = data; + + a->reconnect = 0; + account_on( a->irc, a ); + + return( FALSE ); /* Only have to run the timeout once */ +} + +void cancel_auto_reconnect( account_t *a ) +{ + while( g_source_remove_by_user_data( (gpointer) a ) ); + a->reconnect = 0; +} + +void account_offline( struct gaim_connection *gc ) +{ + gc->wants_to_die = TRUE; + signoff( gc ); +} + +void signoff( struct gaim_connection *gc ) +{ + irc_t *irc = gc->irc; + user_t *t, *u = irc->users; + account_t *a; + + irc_usermsg( gc->irc, "%s(%s) - Signing off..", proto_name[gc->protocol], gc->username ); + + gaim_input_remove( gc->keepalive ); + gc->keepalive = 0; + gc->prpl->close( gc ); + gaim_input_remove( gc->inpa ); + + while( u ) + { + if( u->gc == gc ) + { + t = u->next; + user_del( irc, u->nick ); + u = t; + } + else + u = u->next; + } + + query_del_by_gc( gc->irc, gc ); + + for( a = irc->accounts; a; a = a->next ) + if( a->gc == gc ) + break; + + if( !a ) + { + /* Uhm... This is very sick. */ + } + else if( !gc->wants_to_die && set_getint( irc, "auto_reconnect" ) ) + { + int delay = set_getint( irc, "auto_reconnect_delay" ); + irc_usermsg( gc->irc, "%s(%s) - Reconnecting in %d seconds..", proto_name[gc->protocol], gc->username, delay); + + a->reconnect = 1; + g_timeout_add( delay * 1000, auto_reconnect, a ); + } + + destroy_gaim_conn( gc ); +} + + +/* dialogs.c */ + +void do_error_dialog( struct gaim_connection *gc, char *msg, char *title ) +{ + irc_usermsg( gc->irc, "%s(%s) - Error: %s", gc->username, title, msg ); +} + +void do_ask_dialog( struct gaim_connection *gc, char *msg, void *data, void *doit, void *dont ) +{ + query_add( gc->irc, gc, msg, doit, dont, data ); +} + + +/* list.c */ + +int bud_list_cache_exists( struct gaim_connection *gc ) +{ + return( 0 ); +} + +void do_import( struct gaim_connection *gc, void *null ) +{ + return; +} + +void add_buddy( struct gaim_connection *gc, char *group, char *handle, char *realname ) +{ + user_t *u; + char nick[MAX_NICK_LENGTH+1]; + char *s; + irc_t *irc = gc->irc; + + if( set_getint( irc, "debug" ) && 0 ) /* This message is too useless */ + irc_usermsg( irc, "Receiving user add from protocol: %s", handle ); + + if( user_findhandle( gc, handle ) ) + { + if( set_getint( irc, "debug" ) ) + irc_usermsg( irc, "User already exists, ignoring add request: %s", handle ); + + return; + + /* Buddy seems to exist already. Let's ignore this request then... */ + } + + memset( nick, 0, MAX_NICK_LENGTH + 1 ); + strcpy( nick, nick_get( gc->irc, handle, gc->protocol, realname ) ); + + u = user_add( gc->irc, nick ); + + if( !realname || !*realname ) realname = nick; + u->realname = g_strdup( realname ); + + if( ( s = strchr( handle, '@' ) ) ) + { + u->host = g_strdup( s + 1 ); + u->user = g_strndup( handle, s - handle ); + } + else if( gc->user->proto_opt[0] && *gc->user->proto_opt[0] ) + { + u->host = g_strdup( gc->user->proto_opt[0] ); + u->user = g_strdup( handle ); + + /* s/ /_/ ... important for AOL screennames */ + for( s = u->user; *s; s ++ ) + if( *s == ' ' ) + *s = '_'; + } + else + { + u->host = g_strdup( proto_name[gc->user->protocol] ); + u->user = g_strdup( handle ); + } + + u->gc = gc; + u->handle = g_strdup( handle ); + u->send_handler = buddy_send_handler; + u->last_typing_notice = 0; +} + +struct buddy *find_buddy( struct gaim_connection *gc, char *handle ) +{ + static struct buddy b[1]; + user_t *u; + + u = user_findhandle( gc, handle ); + + if( !u ) + return( NULL ); + + memset( b, 0, sizeof( b ) ); + strncpy( b->name, handle, 80 ); + strncpy( b->show, u->realname, BUDDY_ALIAS_MAXLEN ); + b->present = u->online; + b->gc = u->gc; + + return( b ); +} + +void do_export( struct gaim_connection *gc ) +{ + return; +} + +void signoff_blocked( struct gaim_connection *gc ) +{ + return; /* Make all blocked users look invisible (TODO?) */ +} + + +void serv_buddy_rename( struct gaim_connection *gc, char *handle, char *realname ) +{ + user_t *u = user_findhandle( gc, handle ); + char *name, buf[1024]; + + if( !u ) return; + + /* Convert all UTF-8 */ + if( g_strncasecmp( set_getstr( gc->irc, "charset" ), "none", 4 ) != 0 && + do_iconv( "UTF-8", set_getstr( gc->irc, "charset" ), realname, buf, 0, sizeof( buf ) ) != -1 ) + name = buf; + else + name = realname; + + if( g_strcasecmp( u->realname, name ) != 0 ) + { + if( u->realname != u->nick ) g_free( u->realname ); + + u->realname = g_strdup( name ); + + if( ( gc->flags & OPT_LOGGED_IN ) && set_getint( gc->irc, "display_namechanges" ) ) + irc_usermsg( gc->irc, "User `%s' changed name to `%s'", u->nick, u->realname ); + } +} + + +/* prpl.c */ + +void show_got_added( struct gaim_connection *gc, char *id, char *handle, const char *realname, const char *msg ) +{ + return; +} + + +/* server.c */ + +void serv_got_update( struct gaim_connection *gc, char *handle, int loggedin, int evil, time_t signon, time_t idle, int type, guint caps ) +{ + user_t *u; + int oa, oo; + + u = user_findhandle( gc, handle ); + + if( !u ) + { + if( g_strcasecmp( set_getstr( gc->irc, "handle_unknown" ), "add" ) == 0 ) + { + add_buddy( gc, NULL, handle, NULL ); + u = user_findhandle( gc, handle ); + } + else + { + if( set_getint( gc->irc, "debug" ) || g_strcasecmp( set_getstr( gc->irc, "handle_unknown" ), "ignore" ) != 0 ) + { + irc_usermsg( gc->irc, "serv_got_update() for handle %s on connection %s(%s):", handle, proto_name[gc->protocol], gc->username ); + irc_usermsg( gc->irc, "loggedin = %d, type = %d", loggedin, type ); + } + + return; + } + return; + } + + oa = u->away != NULL; + oo = u->online; + + if( u->away ) + { + g_free( u->away ); + u->away = NULL; + } + + if( loggedin && !u->online ) + { + irc_spawn( gc->irc, u ); + u->online = 1; + } + else if( !loggedin && u->online ) + { + struct conversation *c; + + irc_kill( gc->irc, u ); + u->online = 0; + + /* Remove him/her from the conversations to prevent PART messages after he/she QUIT already */ + for( c = gc->conversations; c; c = c->next ) + remove_chat_buddy_silent( c, handle ); + } + + if( ( type & UC_UNAVAILABLE ) && ( gc->protocol == PROTO_OSCAR || gc->protocol == PROTO_TOC ) ) + { + u->away = g_strdup( "Away" ); + } + else if( ( type & UC_UNAVAILABLE ) && ( gc->protocol == PROTO_JABBER ) ) + { + if( type & UC_DND ) + u->away = g_strdup( "Do Not Disturb" ); + else if( type & UC_XA ) + u->away = g_strdup( "Extended Away" ); + else // if( type & UC_AWAY ) + u->away = g_strdup( "Away" ); + } + else if( ( type & UC_UNAVAILABLE ) && gc->prpl->get_status_string ) + { + u->away = g_strdup( gc->prpl->get_status_string( gc, type ) ); + } + else + u->away = NULL; + + /* LISPy... */ + if( ( set_getint( gc->irc, "away_devoice" ) ) && /* Don't do a thing when user doesn't want it */ + ( u->online ) && /* Don't touch offline people */ + ( ( ( u->online != oo ) && !u->away ) || /* Voice joining people */ + ( ( u->online == oo ) && ( oa == !u->away ) ) ) ) /* (De)voice people changing state */ + { + irc_write( gc->irc, ":%s!%s@%s MODE %s %cv %s", gc->irc->mynick, gc->irc->mynick, gc->irc->myhost, + gc->irc->channel, u->away?'-':'+', u->nick ); + } +} + +void serv_got_im( struct gaim_connection *gc, char *handle, char *msg, guint32 flags, time_t mtime, gint len ) +{ + irc_t *irc = gc->irc; + user_t *u; + char buf[8192]; + + u = user_findhandle( gc, handle ); + + if( !u ) + { + char *h = set_getstr( irc, "handle_unknown" ); + + if( g_strcasecmp( h, "ignore" ) == 0 ) + { + if( set_getint( irc, "debug" ) ) + irc_usermsg( irc, "Ignoring message from unknown handle %s on connection %s(%s)", handle, proto_name[gc->protocol], gc->username ); + + return; + } + else if( g_strncasecmp( h, "add", 3 ) == 0 ) + { + int private = set_getint( irc, "private" ); + + if( h[3] ) + { + if( g_strcasecmp( h + 3, "_private" ) == 0 ) + private = 1; + else if( g_strcasecmp( h + 3, "_channel" ) == 0 ) + private = 0; + } + + add_buddy( gc, NULL, handle, NULL ); + u = user_findhandle( gc, handle ); + u->is_private = private; + } + else + { + irc_usermsg( irc, "Message from unknown handle %s on connection %s(%s):", handle, proto_name[gc->protocol], gc->username ); + u = user_find( irc, irc->mynick ); + } + } + + /* if( g_strcasecmp( set_getstr( irc, "html" ), "strip" ) == 0 ) */ + if( gc->flags & OPT_CONN_HTML ) + strip_html( msg ); + + if( g_strncasecmp( set_getstr( irc, "charset" ), "none", 4 ) != 0 && + do_iconv( "UTF-8", set_getstr( irc, "charset" ), msg, buf, 0, 8192 ) != -1 ) + msg = buf; + + while( strlen( msg ) > 450 ) + { + char tmp, *nl; + + tmp = msg[450]; + msg[450] = 0; + + /* If there's a newline in this string, split up there so we're not + going to split up lines. If there isn't a newline, well, too bad. */ + if( ( nl = strrchr( msg, '\n' ) ) ) + *nl = 0; + + irc_msgfrom( irc, u->nick, msg ); + + msg[450] = tmp; + + /* Move on. */ + if( nl ) + { + *nl = '\n'; + msg = nl + 1; + } + else + { + msg += 450; + } + } + irc_msgfrom( irc, u->nick, msg ); +} + +void serv_got_typing( struct gaim_connection *gc, char *handle, int timeout ) +{ + user_t *u; + + if( !set_getint( gc->irc, "typing_notice" ) ) + return; + + if( ( u = user_findhandle( gc, handle ) ) ) + irc_noticefrom( gc->irc, u->nick, "* Typing a message *" ); +} + +void serv_got_chat_left( struct gaim_connection *gc, int id ) +{ + struct conversation *c, *l = NULL; + GList *ir; + + if( set_getint( gc->irc, "debug" ) ) + irc_usermsg( gc->irc, "You were removed from conversation %d", (int) id ); + + for( c = gc->conversations; c && c->id != id; c = (l=c)->next ); + + if( c ) + { + if( c->joined ) + { + user_t *u, *r; + + r = user_find( gc->irc, gc->irc->mynick ); + irc_privmsg( gc->irc, r, "PRIVMSG", c->channel, "", "Cleaning up channel, bye!" ); + + u = user_find( gc->irc, gc->irc->nick ); + irc_kick( gc->irc, u, c->channel, r ); + /* irc_part( gc->irc, u, c->channel ); */ + } + + if( l ) + l->next = c->next; + else + gc->conversations = c->next; + + for( ir = c->in_room; ir; ir = ir->next ) + g_free( ir->data ); + g_list_free( c->in_room ); + g_free( c->channel ); + g_free( c->title ); + g_free( c ); + } +} + +void serv_got_chat_in( struct gaim_connection *gc, int id, char *who, int whisper, char *msg, time_t mtime ) +{ + struct conversation *c; + user_t *u; + char buf[8192]; + + /* Gaim sends own messages through this too. IRC doesn't want this, so kill them */ + if( g_strcasecmp( who, gc->user->username ) == 0 ) + return; + + u = user_findhandle( gc, who ); + for( c = gc->conversations; c && c->id != id; c = c->next ); + + /* if( g_strcasecmp( set_getstr( gc->irc, "html" ), "strip" ) == 0 ) */ + if( gc->flags & OPT_CONN_HTML ) + strip_html( msg ); + + if( g_strncasecmp( set_getstr( gc->irc, "charset" ), "none", 4 ) != 0 && + do_iconv( "UTF-8", set_getstr( gc->irc, "charset" ), msg, buf, 0, 8192 ) != -1 ) + msg = buf; + + if( c && u ) + irc_privmsg( gc->irc, u, "PRIVMSG", c->channel, "", msg ); + else + irc_usermsg( gc->irc, "Message from/to conversation %s@%d (unknown conv/user): %s", who, id, msg ); +} + +struct conversation *serv_got_joined_chat( struct gaim_connection *gc, int id, char *handle ) +{ + struct conversation *c; + char *s; + + /* This one just creates the conversation structure, user won't see anything yet */ + + if( gc->conversations ) + { + for( c = gc->conversations; c->next; c = c->next ); + c = c->next = g_new0( struct conversation, 1 ); + } + else + gc->conversations = c = g_new0( struct conversation, 1); + + c->id = id; + c->gc = gc; + c->title = g_strdup( handle ); + + s = g_new( char, 16 ); + sprintf( s, "#chat_%03d", gc->irc->c_id++ ); + c->channel = g_strdup( s ); + g_free( s ); + + if( set_getint( gc->irc, "debug" ) ) + irc_usermsg( gc->irc, "Creating new conversation: (id=%d,handle=%s)", id, handle ); + + return( c ); +} + +void serv_finish_login( struct gaim_connection *gc ) +{ + return; +} + + +/* buddy_chat.c */ + +void add_chat_buddy( struct conversation *b, char *handle ) +{ + user_t *u = user_findhandle( b->gc, handle ); + int me = 0; + + if( set_getint( b->gc->irc, "debug" ) ) + irc_usermsg( b->gc->irc, "User %s added to conversation %d", handle, b->id ); + + /* It might be yourself! */ + if( handle_cmp ( handle, b->gc->user->username, b->gc->protocol ) == 0 ) + { + u = user_find( b->gc->irc, b->gc->irc->nick ); + if( !b->joined ) + irc_join( b->gc->irc, u, b->channel ); + b->joined = me = 1; + } + + /* Most protocols allow people to join, even when they're not in + your contact list. Try to handle that here */ + if( !u ) + { + add_buddy( b->gc, NULL, handle, NULL ); + u = user_findhandle( b->gc, handle ); + } + + /* Add the handle to the room userlist, if it's not 'me' */ + if( !me ) + { + if( b->joined ) + irc_join( b->gc->irc, u, b->channel ); + b->in_room = g_list_append( b->in_room, g_strdup( handle ) ); + } +} + +void remove_chat_buddy( struct conversation *b, char *handle, char *reason ) +{ + user_t *u; + int me = 0; + + if( set_getint( b->gc->irc, "debug" ) ) + irc_usermsg( b->gc->irc, "User %s removed from conversation %d (%s)", handle, b->id, reason ? reason : "" ); + + /* It might be yourself! */ + if( g_strcasecmp( handle, b->gc->user->username ) == 0 ) + { + u = user_find( b->gc->irc, b->gc->irc->nick ); + b->joined = 0; + me = 1; + } + else + { + u = user_findhandle( b->gc, handle ); + } + + if( remove_chat_buddy_silent( b, handle ) ) + if( ( b->joined || me ) && u ) + irc_part( b->gc->irc, u, b->channel ); +} + +static int remove_chat_buddy_silent( struct conversation *b, char *handle ) +{ + GList *i; + + /* Find the handle in the room userlist and shoot it */ + i = b->in_room; + while( i ) + { + if( g_strcasecmp( handle, i->data ) == 0 ) + { + g_free( i->data ); + b->in_room = g_list_remove( b->in_room, i->data ); + return( 1 ); + } + + i = i->next; + } + + return( 0 ); +} + + +/* prefs.c */ + +/* Necessary? */ +void build_block_list() +{ + return; +} + +void build_allow_list() +{ + return; +} + + +/* Misc. BitlBee stuff which shouldn't really be here */ + +struct conversation *conv_findchannel( char *channel ) +{ + struct gaim_connection *gc; + struct conversation *c; + GSList *l; + + /* This finds the connection which has a conversation which belongs to this channel */ + for( l = connections; l; l = l->next ) + { + gc = l->data; + for( c = gc->conversations; c && g_strcasecmp( c->channel, channel ) != 0; c = c->next ); + if( c ) + return( c ); + } + + return( NULL ); +} + +char *set_eval_away_devoice( irc_t *irc, set_t *set, char *value ) +{ + int st; + + if( ( g_strcasecmp( value, "true" ) == 0 ) || ( g_strcasecmp( value, "yes" ) == 0 ) || ( g_strcasecmp( value, "on" ) == 0 ) ) + st = 1; + else if( ( g_strcasecmp( value, "false" ) == 0 ) || ( g_strcasecmp( value, "no" ) == 0 ) || ( g_strcasecmp( value, "off" ) == 0 ) ) + st = 0; + else if( sscanf( value, "%d", &st ) != 1 ) + return( NULL ); + + st = st != 0; + + /* Horror.... */ + + if( st != set_getint( irc, "away_devoice" ) ) + { + char list[80] = ""; + user_t *u = irc->users; + int i = 0, count = 0; + char pm; + char v[80]; + + if( st ) + pm = '+'; + else + pm = '-'; + + while( u ) + { + if( u->gc && u->online && !u->away ) + { + if( ( strlen( list ) + strlen( u->nick ) ) >= 79 ) + { + for( i = 0; i < count; v[i++] = 'v' ); v[i] = 0; + irc_write( irc, ":%s!%s@%s MODE %s %c%s%s", + irc->mynick, irc->mynick, irc->myhost, + irc->channel, pm, v, list ); + + *list = 0; + count = 0; + } + + sprintf( list + strlen( list ), " %s", u->nick ); + count ++; + } + u = u->next; + } + + /* $v = 'v' x $i */ + for( i = 0; i < count; v[i++] = 'v' ); v[i] = 0; + irc_write( irc, ":%s!%s@%s MODE %s %c%s%s", irc->mynick, irc->mynick, irc->myhost, + irc->channel, pm, v, list ); + } + + return( set_eval_bool( irc, set, value ) ); +} + +int serv_send_im( irc_t *irc, user_t *u, char *msg, int flags ) +{ + char buf[8192]; + + if( g_strncasecmp( set_getstr( irc, "charset" ), "none", 4 ) != 0 && + do_iconv( set_getstr( irc, "charset" ), "UTF-8", msg, buf, 0, 8192 ) != -1 ) + msg = buf; + + if( u->gc->flags & OPT_CONN_HTML) { + char * html = escape_html(msg); + strncpy(buf, html, 8192); + g_free(html); + } + + return( ((struct gaim_connection *)u->gc)->prpl->send_im( u->gc, u->handle, msg, strlen( msg ), flags ) ); +} + +int serv_send_chat( irc_t *irc, struct gaim_connection *gc, int id, char *msg ) +{ + char buf[8192]; + + if( g_strncasecmp( set_getstr( irc, "charset" ), "none", 4 ) != 0 && + do_iconv( set_getstr( irc, "charset" ), "UTF-8", msg, buf, 0, 8192 ) != -1 ) + msg = buf; + + if( gc->flags & OPT_CONN_HTML) { + char * html = escape_html(msg); + strncpy(buf, html, 8192); + g_free(html); + } + + return( gc->prpl->chat_send( gc, id, msg ) ); +} + +/* Convert from one charset to another. + + from_cs, to_cs: Source and destination charsets + src, dst: Source and destination strings + size: Size if src. 0 == use strlen(). strlen() is not reliable for UNICODE/UTF16 strings though. + maxbuf: Maximum number of bytes to write to dst + + Returns the number of bytes written to maxbuf or -1 on an error. +*/ +signed int do_iconv( char *from_cs, char *to_cs, char *src, char *dst, size_t size, size_t maxbuf ) +{ + iconv_t cd; + size_t res; + size_t inbytesleft, outbytesleft; + char *inbuf = src; + char *outbuf = dst; + + cd = iconv_open( to_cs, from_cs ); + if( cd == (iconv_t) -1 ) + return( -1 ); + + inbytesleft = size ? size : strlen( src ); + outbytesleft = maxbuf - 1; + res = iconv( cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft ); + *outbuf = '\0'; + iconv_close( cd ); + + if( res == (size_t) -1 ) + return( -1 ); + else + return( outbuf - dst ); +} + +char *set_eval_charset( irc_t *irc, set_t *set, char *value ) +{ + iconv_t cd; + + if ( g_strncasecmp( value, "none", 4 ) == 0 ) + return( value ); + + cd = iconv_open( "UTF-8", value ); + if( cd == (iconv_t) -1 ) + return( NULL ); + + iconv_close( cd ); + return( value ); +} diff --git a/protocols/nogaim.h b/protocols/nogaim.h new file mode 100644 index 00000000..4e10330a --- /dev/null +++ b/protocols/nogaim.h @@ -0,0 +1,347 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* + * nogaim + * + * Gaim without gaim - for BitlBee + * + * This file contains functions called by the Gaim IM-modules. It contains + * some struct and type definitions from Gaim. + * + * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net> + * (and possibly other members of the Gaim team) + * Copyright 2002-2004 Wilmer van der Gaast <lintux@lintux.cx> + */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef _NOGAIM_H +#define _NOGAIM_H + +#include "bitlbee.h" +#include "proxy.h" +#include "md5.h" +#include "sha.h" + + +#define BUF_LEN MSG_LEN +#define BUF_LONG ( BUF_LEN * 2 ) +#define MSG_LEN 2048 +#define BUF_LEN MSG_LEN + +#define SELF_ALIAS_LEN 400 +#define BUDDY_ALIAS_MAXLEN 388 /* because MSN names can be 387 characters */ + +#define PERMIT_ALL 1 +#define PERMIT_NONE 2 +#define PERMIT_SOME 3 +#define DENY_SOME 4 + +#define WEBSITE "http://www.bitlee.org/" +#define IM_FLAG_AWAY 0x0020 +#define OPT_CONN_HTML 0x00000001 +#define OPT_LOGGED_IN 0x00010000 +#define GAIM_AWAY_CUSTOM "Custom" + +#define GAIM_LOGO 0 +#define GAIM_ERROR 1 +#define GAIM_WARNING 2 +#define GAIM_INFO 3 + +/* ok. now the fun begins. first we create a connection structure */ +struct gaim_connection { + /* we need to do either oscar or TOC */ + /* we make this as an int in case if we want to add more protocols later */ + int protocol; + struct prpl *prpl; + guint32 flags; + + /* all connections need an input watcher */ + int inpa; + + /* buddy list stuff. there is still a global groups for the buddy list, but + * we need to maintain our own set of buddies, and our own permit/deny lists */ + GSList *permit; + GSList *deny; + int permdeny; + + /* all connections need a list of chats, even if they don't have chat */ + GSList *buddy_chats; + + /* each connection then can have its own protocol-specific data */ + void *proto_data; + + struct aim_user *user; + + char username[64]; + char displayname[128]; + char password[32]; + guint keepalive; + /* stuff needed for per-connection idle times */ + guint idle_timer; + time_t login_time; + time_t lastsent; + int is_idle; + + char *away; + int is_auto_away; + + int evil; + gboolean wants_to_die; /* defaults to FALSE */ + + /* BitlBee */ + irc_t *irc; + int lstitems; /* added for msnP8 */ + + struct conversation *conversations; +}; + +/* struct buddy_chat went away and got merged with this. */ +struct conversation { + struct gaim_connection *gc; + + /* stuff used just for chat */ + GList *in_room; + GList *ignored; + int id; + + /* BitlBee */ + struct conversation *next; + char *channel; + char *title; + char joined; + void *data; +}; + +struct buddy { + char name[80]; + char show[BUDDY_ALIAS_MAXLEN]; + int present; + int evil; + time_t signon; + time_t idle; + int uc; + guint caps; /* woohoo! */ + void *proto_data; /* what a hack */ + struct gaim_connection *gc; /* the connection it belongs to */ +}; + +struct aim_user { + char username[64]; + char alias[SELF_ALIAS_LEN]; + char password[32]; + char user_info[2048]; + int options; + int protocol; + /* prpls can use this to save information about the user, + * like which server to connect to, etc */ + char proto_opt[7][256]; + + struct gaim_connection *gc; + irc_t *irc; +}; + +struct prpl { + int protocol; + int options; + char *(* name)(); + + /* for ICQ and Yahoo, who have off/on per-conversation options */ + /* char *checkbox; this should be per-connection */ + + GList *(* away_states)(struct gaim_connection *gc); + GList *(* actions)(); + void (* do_action)(struct gaim_connection *, char *); + /* user_opts returns a GList* of g_malloc'd struct proto_user_opts */ + GList *(* user_opts)(); + GList *(* chat_info)(struct gaim_connection *); + + /* all the server-related functions */ + + /* a lot of these (like get_dir) are protocol-dependent and should be removed. ones like + * set_dir (which is also protocol-dependent) can stay though because there's a dialog + * (i.e. the prpl says you can set your dir info, the ui shows a dialog and needs to call + * set_dir in order to set it) */ + + void (* login) (struct aim_user *); + void (* close) (struct gaim_connection *); + int (* send_im) (struct gaim_connection *, char *who, char *message, int len, int away); + int (* send_typing) (struct gaim_connection *, char *who, int typing); + void (* set_info) (struct gaim_connection *, char *info); + void (* get_info) (struct gaim_connection *, char *who); + void (* set_away) (struct gaim_connection *, char *state, char *message); + void (* get_away) (struct gaim_connection *, char *who); + void (* set_idle) (struct gaim_connection *, int idletime); + void (* add_buddy) (struct gaim_connection *, char *name); + void (* remove_buddy) (struct gaim_connection *, char *name, char *group); + void (* add_permit) (struct gaim_connection *, char *name); + void (* add_deny) (struct gaim_connection *, char *name); + void (* rem_permit) (struct gaim_connection *, char *name); + void (* rem_deny) (struct gaim_connection *, char *name); + void (* set_permit_deny)(struct gaim_connection *); + void (* join_chat) (struct gaim_connection *, GList *data); + void (* chat_invite) (struct gaim_connection *, int id, char *who, char *message); + void (* chat_leave) (struct gaim_connection *, int id); + void (* chat_whisper) (struct gaim_connection *, int id, char *who, char *message); + int (* chat_send) (struct gaim_connection *, int id, char *message); + int (* chat_open) (struct gaim_connection *, char *who); + void (* keepalive) (struct gaim_connection *); + + /* get "chat buddy" info and away message */ + void (* get_cb_info) (struct gaim_connection *, int, char *who); + void (* get_cb_away) (struct gaim_connection *, int, char *who); + + /* save/store buddy's alias on server list/roster */ + void (* alias_buddy) (struct gaim_connection *, char *who); + + /* change a buddy's group on a server list/roster */ + void (* group_buddy) (struct gaim_connection *, char *who, char *old_group, char *new_group); + + void (* buddy_free) (struct buddy *); + + char *(* get_status_string) (struct gaim_connection *gc, int stat); +}; + +#define PROTO_TOC 0 +#define PROTO_OSCAR 1 +#define PROTO_YAHOO 2 +#define PROTO_ICQ 3 +#define PROTO_MSN 4 +#define PROTO_IRC 5 +#define PROTO_FTP 6 +#define PROTO_VGATE 7 +#define PROTO_JABBER 8 +#define PROTO_NAPSTER 9 +#define PROTO_ZEPHYR 10 +#define PROTO_GADUGADU 11 +#define PROTO_MAX 16 + +extern char proto_name[PROTO_MAX][8]; + +#define UC_UNAVAILABLE 1 + +/* JABBER */ +#define UC_AWAY (0x02 | UC_UNAVAILABLE) +#define UC_CHAT 0x04 +#define UC_XA (0x08 | UC_UNAVAILABLE) +#define UC_DND (0x10 | UC_UNAVAILABLE) + +G_MODULE_EXPORT GSList *get_connections(); +extern struct prpl *proto_prpl[16]; + +/* nogaim.c */ +int serv_send_im(irc_t *irc, user_t *u, char *msg, int flags); +int serv_send_chat(irc_t *irc, struct gaim_connection *gc, int id, char *msg ); + +G_MODULE_EXPORT signed int do_iconv( char *from_cs, char *to_cs, char *src, char *dst, size_t size, size_t maxbuf ); +char *set_eval_charset( irc_t *irc, set_t *set, char *value ); + +void nogaim_init(); +int proto_away( struct gaim_connection *gc, char *away ); +char *set_eval_away_devoice( irc_t *irc, set_t *set, char *value ); +int handle_cmp( char *a, char *b, int protocol ); + +gboolean auto_reconnect( gpointer data ); +void cancel_auto_reconnect( struct account *a ); + +/* multi.c */ +G_MODULE_EXPORT struct gaim_connection *new_gaim_conn( struct aim_user *user ); +G_MODULE_EXPORT void destroy_gaim_conn( struct gaim_connection *gc ); +G_MODULE_EXPORT void set_login_progress( struct gaim_connection *gc, int step, char *msg ); +G_MODULE_EXPORT void hide_login_progress( struct gaim_connection *gc, char *msg ); +G_MODULE_EXPORT void hide_login_progress_error( struct gaim_connection *gc, char *msg ); +G_MODULE_EXPORT void serv_got_crap( struct gaim_connection *gc, char *format, ... ); +G_MODULE_EXPORT void account_online( struct gaim_connection *gc ); +G_MODULE_EXPORT void account_offline( struct gaim_connection *gc ); +G_MODULE_EXPORT void signoff( struct gaim_connection *gc ); + +/* dialogs.c */ +G_MODULE_EXPORT void do_error_dialog( struct gaim_connection *gc, char *msg, char *title ); +G_MODULE_EXPORT void do_ask_dialog( struct gaim_connection *gc, char *msg, void *data, void *doit, void *dont ); + +/* list.c */ +G_MODULE_EXPORT int bud_list_cache_exists( struct gaim_connection *gc ); +G_MODULE_EXPORT void do_import( struct gaim_connection *gc, void *null ); +G_MODULE_EXPORT void add_buddy( struct gaim_connection *gc, char *group, char *handle, char *realname ); +G_MODULE_EXPORT struct buddy *find_buddy( struct gaim_connection *gc, char *handle ); +G_MODULE_EXPORT void do_export( struct gaim_connection *gc ); +G_MODULE_EXPORT void signoff_blocked( struct gaim_connection *gc ); + +G_MODULE_EXPORT void serv_buddy_rename( struct gaim_connection *gc, char *handle, char *realname ); + +/* buddy_chat.c */ +G_MODULE_EXPORT void add_chat_buddy( struct conversation *b, char *handle ); +G_MODULE_EXPORT void remove_chat_buddy( struct conversation *b, char *handle, char *reason ); + +/* prpl.c */ +G_MODULE_EXPORT void show_got_added( struct gaim_connection *gc, char *id, char *handle, const char *realname, const char *msg ); + +/* server.c */ +G_MODULE_EXPORT void serv_got_update( struct gaim_connection *gc, char *handle, int loggedin, int evil, time_t signon, time_t idle, int type, guint caps ); +G_MODULE_EXPORT void serv_got_im( struct gaim_connection *gc, char *handle, char *msg, guint32 flags, time_t mtime, gint len ); +G_MODULE_EXPORT void serv_got_typing( struct gaim_connection *gc, char *handle, int timeout ); +G_MODULE_EXPORT void serv_got_chat_invite( struct gaim_connection *gc, char *handle, char *who, char *msg, GList *data ); +G_MODULE_EXPORT struct conversation *serv_got_joined_chat( struct gaim_connection *gc, int id, char *handle ); +G_MODULE_EXPORT void serv_got_chat_in( struct gaim_connection *gc, int id, char *who, int whisper, char *msg, time_t mtime ); +G_MODULE_EXPORT void serv_got_chat_left( struct gaim_connection *gc, int id ); +/* void serv_finish_login( struct gaim_connection *gc ); */ + +/* util.c */ +G_MODULE_EXPORT char *utf8_to_str( const char *in ); +G_MODULE_EXPORT char *str_to_utf8( const char *in ); +G_MODULE_EXPORT void strip_linefeed( gchar *text ); +G_MODULE_EXPORT char *add_cr( char *text ); +G_MODULE_EXPORT char *tobase64( const char *text ); +G_MODULE_EXPORT char *normalize( const char *s ); +G_MODULE_EXPORT time_t get_time( int year, int month, int day, int hour, int min, int sec ); +G_MODULE_EXPORT void strip_html( char *msg ); +G_MODULE_EXPORT char * escape_html(const char *html); +G_MODULE_EXPORT void info_string_append(GString *str, char *newline, char *name, char *value); + +#ifdef WITH_MSN +/* msn.c */ +G_MODULE_EXPORT void msn_init( struct prpl *ret ); +#endif + +#ifdef WITH_OSCAR +/* oscar.c */ +G_MODULE_EXPORT void oscar_init( struct prpl *ret ); +#endif + +#ifdef WITH_JABBER +/* jabber.c */ +G_MODULE_EXPORT void jabber_init( struct prpl *ret ); +#endif + +#ifdef WITH_YAHOO +/* yahoo.c */ +G_MODULE_EXPORT void byahoo_init( struct prpl *ret ); +#endif + +/* prefs.c */ +G_MODULE_EXPORT void build_block_list(); +G_MODULE_EXPORT void build_allow_list(); + +struct conversation *conv_findchannel( char *channel ); + + +#endif diff --git a/protocols/oscar/AUTHORS b/protocols/oscar/AUTHORS new file mode 100644 index 00000000..5ca13988 --- /dev/null +++ b/protocols/oscar/AUTHORS @@ -0,0 +1,31 @@ + +Anyone else want to be in here? + +--- + +N: Adam Fritzler +H: mid +E: mid@auk.cx +W: http://www.auk.cx/~mid,http://www.auk.cx/faim +D: Wrote most of the wap of crap that you see before you. + +N: Josh Myer +E: josh@joshisanerd.com +D: OFT/ODC (not quite finished yet..), random little things, Munger-At-Large, compile-time warnings. + +N: Daniel Reed +H: n, linuxkitty +E: n@ml.org +W: http://users.n.ml.org/n/ +D: Fixed aim_snac.c + +N: Eric Warmenhoven +E: warmenhoven@linux.com +D: Some OFT info, author of the faim interface for gaim + +N: Brock Wilcox +H: awwaiid +E: awwaiid@auk.cx +D: Figured out original password roasting + + diff --git a/protocols/oscar/COPYING b/protocols/oscar/COPYING new file mode 100644 index 00000000..223ede7d --- /dev/null +++ b/protocols/oscar/COPYING @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +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 and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + <one line to give the library's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/protocols/oscar/Makefile b/protocols/oscar/Makefile new file mode 100644 index 00000000..acf4794a --- /dev/null +++ b/protocols/oscar/Makefile @@ -0,0 +1,37 @@ +########################### +## Makefile for BitlBee ## +## ## +## Copyright 2002 Lintux ## +########################### + +### DEFINITIONS + +-include ../../Makefile.settings + +# [SH] Program variables +objects = admin.o auth.o bos.o buddylist.o chat.o chatnav.o conn.o ft.o icq.o im.o info.o misc.o msgcookie.o rxhandlers.o rxqueue.o search.o service.o snac.o ssi.o stats.o tlv.o txqueue.o oscar_util.o oscar.o + +CFLAGS += -Wall +LFLAGS += -r + +# [SH] Phony targets +all: oscarr.o + +.PHONY: all clean distclean + +clean: + rm -f *.o core + +distclean: clean + +### MAIN PROGRAM + +$(objects): ../../Makefile.settings Makefile + +$(objects): %.o: %.c + @echo '*' Compiling $< + @$(CC) -c $(CFLAGS) $< -o $@ + +oscarr.o: $(objects) + @echo '*' Linking oscarr.o + @$(LD) $(LFLAGS) $(objects) -o oscarr.o diff --git a/protocols/oscar/admin.c b/protocols/oscar/admin.c new file mode 100644 index 00000000..09082c5b --- /dev/null +++ b/protocols/oscar/admin.c @@ -0,0 +1,196 @@ +#include <aim.h> +#include "admin.h" + +/* called for both reply and change-reply */ +static int infochange(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + + /* + * struct { + * guint16 perms; + * guint16 tlvcount; + * aim_tlv_t tlvs[tlvcount]; + * } admin_info[n]; + */ + while (aim_bstream_empty(bs)) { + guint16 perms, tlvcount; + + perms = aimbs_get16(bs); + tlvcount = aimbs_get16(bs); + + while (tlvcount && aim_bstream_empty(bs)) { + aim_rxcallback_t userfunc; + guint16 type, len; + guint8 *val; + int str = 0; + + type = aimbs_get16(bs); + len = aimbs_get16(bs); + + if ((type == 0x0011) || (type == 0x0004)) + str = 1; + + if (str) + val = (guint8 *)aimbs_getstr(bs, len); + else + val = aimbs_getraw(bs, len); + + /* XXX fix so its only called once for the entire packet */ + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + userfunc(sess, rx, (snac->subtype == 0x0005) ? 1 : 0, perms, type, len, val, str); + + g_free(val); + + tlvcount--; + } + } + + return 1; +} + +static int accountconfirm(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_rxcallback_t userfunc; + guint16 status; + + status = aimbs_get16(bs); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + return userfunc(sess, rx, status); + + return 0; +} + +static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + + if ((snac->subtype == 0x0003) || (snac->subtype == 0x0005)) + return infochange(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0007) + return accountconfirm(sess, mod, rx, snac, bs); + + return 0; +} + +int admin_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = AIM_CB_FAM_ADM; + mod->version = 0x0001; + mod->toolid = AIM_TOOL_NEWWIN; + mod->toolversion = 0x0629; + mod->flags = 0; + strncpy(mod->name, "admin", sizeof(mod->name)); + mod->snachandler = snachandler; + + return 0; +} + +int aim_admin_changepasswd(aim_session_t *sess, aim_conn_t *conn, const char *newpw, const char *curpw) +{ + aim_frame_t *tx; + aim_tlvlist_t *tl = NULL; + aim_snacid_t snacid; + + if (!(tx = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+4+strlen(curpw)+4+strlen(newpw)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0007, 0x0004, 0x0000, NULL, 0); + aim_putsnac(&tx->data, 0x0007, 0x0004, 0x0000, snacid); + + /* new password TLV t(0002) */ + aim_addtlvtochain_raw(&tl, 0x0002, strlen(newpw), (guint8 *)newpw); + + /* current password TLV t(0012) */ + aim_addtlvtochain_raw(&tl, 0x0012, strlen(curpw), (guint8 *)curpw); + + aim_writetlvchain(&tx->data, &tl); + aim_freetlvchain(&tl); + + aim_tx_enqueue(sess, tx); + + return 0; +} + +/* + * Request account confirmation. + * + * This will cause an email to be sent to the address associated with + * the account. By following the instructions in the mail, you can + * get the TRIAL flag removed from your account. + * + */ +int aim_admin_reqconfirm(aim_session_t *sess, aim_conn_t *conn) +{ + return aim_genericreq_n(sess, conn, 0x0007, 0x0006); +} + +/* + * Request a bit of account info. + * + * The only known valid tag is 0x0011 (email address). + * + */ +int aim_admin_getinfo(aim_session_t *sess, aim_conn_t *conn, guint16 info) +{ + aim_frame_t *tx; + aim_snacid_t snacid; + + if (!(tx = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 14))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0002, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&tx->data, 0x0007, 0x0002, 0x0000, snacid); + + aimbs_put16(&tx->data, info); + aimbs_put16(&tx->data, 0x0000); + + aim_tx_enqueue(sess, tx); + + return 0; +} + +int aim_admin_setemail(aim_session_t *sess, aim_conn_t *conn, const char *newemail) +{ + aim_frame_t *tx; + aim_snacid_t snacid; + aim_tlvlist_t *tl = NULL; + + if (!(tx = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+2+strlen(newemail)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0007, 0x0004, 0x0000, NULL, 0); + aim_putsnac(&tx->data, 0x0007, 0x0004, 0x0000, snacid); + + aim_addtlvtochain_raw(&tl, 0x0011, strlen(newemail), (guint8 *)newemail); + + aim_writetlvchain(&tx->data, &tl); + aim_freetlvchain(&tl); + + aim_tx_enqueue(sess, tx); + + return 0; +} + +int aim_admin_setnick(aim_session_t *sess, aim_conn_t *conn, const char *newnick) +{ + aim_frame_t *tx; + aim_snacid_t snacid; + aim_tlvlist_t *tl = NULL; + + if (!(tx = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+2+strlen(newnick)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0007, 0x0004, 0x0000, NULL, 0); + aim_putsnac(&tx->data, 0x0007, 0x0004, 0x0000, snacid); + + aim_addtlvtochain_raw(&tl, 0x0001, strlen(newnick), (guint8 *)newnick); + + aim_writetlvchain(&tx->data, &tl); + aim_freetlvchain(&tl); + + aim_tx_enqueue(sess, tx); + + + return 0; +} diff --git a/protocols/oscar/admin.h b/protocols/oscar/admin.h new file mode 100644 index 00000000..f9d74cae --- /dev/null +++ b/protocols/oscar/admin.h @@ -0,0 +1,13 @@ +#ifndef __OSCAR_ADMIN_H__ +#define __OSCAR_ADMIN_H__ + +#define AIM_CB_FAM_ADM 0x0007 + +/* + * SNAC Family: Administrative Services. + */ +#define AIM_CB_ADM_ERROR 0x0001 +#define AIM_CB_ADM_INFOCHANGE_REPLY 0x0005 +#define AIM_CB_ADM_DEFAULT 0xffff + +#endif /* __OSCAR_ADMIN_H__ */ diff --git a/protocols/oscar/aim.h b/protocols/oscar/aim.h new file mode 100644 index 00000000..2ba29c8b --- /dev/null +++ b/protocols/oscar/aim.h @@ -0,0 +1,921 @@ +/* + * Main libfaim header. Must be included in client for prototypes/macros. + * + * "come on, i turned a chick lesbian; i think this is the hackish equivalent" + * -- Josh Meyer + * + */ + +#ifndef __AIM_H__ +#define __AIM_H__ + +#include <stdio.h> +#include <string.h> +#include <fcntl.h> +#include <sys/types.h> +#include <stdlib.h> +#include <stdarg.h> +#include <errno.h> +#include <time.h> +#include <gmodule.h> + +#include "bitlbee.h" + +/* XXX adjust these based on autoconf-detected platform */ +typedef guint32 aim_snacid_t; +typedef guint16 flap_seqnum_t; + +/* Portability stuff (DMP) */ + +#if defined(mach) && defined(__APPLE__) +#define gethostbyname(x) gethostbyname2(x, AF_INET) +#endif + +/* + * Current Maximum Length for Screen Names (not including NULL) + * + * Currently only names up to 16 characters can be registered + * however it is aparently legal for them to be larger. + */ +#define MAXSNLEN 32 + +/* + * Current Maximum Length for Instant Messages + * + * This was found basically by experiment, but not wholly + * accurate experiment. It should not be regarded + * as completely correct. But its a decent approximation. + * + * Note that although we can send this much, its impossible + * for WinAIM clients (up through the latest (4.0.1957)) to + * send any more than 1kb. Amaze all your windows friends + * with utterly oversized instant messages! + * + * XXX: the real limit is the total SNAC size at 8192. Fix this. + * + */ +#define MAXMSGLEN 7987 + +/* + * Maximum size of a Buddy Icon. + */ +#define MAXICONLEN 7168 +#define AIM_ICONIDENT "AVT1picture.id" + +/* + * Current Maximum Length for Chat Room Messages + * + * This is actually defined by the protocol to be + * dynamic, but I have yet to see due cause to + * define it dynamically here. Maybe later. + * + */ +#define MAXCHATMSGLEN 512 + +/* + * Standard size of an AIM authorization cookie + */ +#define AIM_COOKIELEN 0x100 + +#define AIM_MD5_STRING "AOL Instant Messenger (SM)" + +/* + * Default Authorizer server name and TCP port for the OSCAR farm. + * + * You shouldn't need to change this unless you're writing + * your own server. + * + * Note that only one server is needed to start the whole + * AIM process. The later server addresses come from + * the authorizer service. + * + * This is only here for convenience. Its still up to + * the client to connect to it. + * + */ +#define AIM_DEFAULT_LOGIN_SERVER "login.oscar.aol.com" +#define AIM_LOGIN_PORT 5190 + +/* + * Size of the SNAC caching hash. + * + * Default: 16 + * + */ +#define AIM_SNAC_HASH_SIZE 16 + +/* + * Client info. Filled in by the client and passed in to + * aim_send_login(). The information ends up getting passed to OSCAR + * through the initial login command. + * + */ +struct client_info_s { + const char *clientstring; + guint16 clientid; + int major; + int minor; + int point; + int build; + const char *country; /* two-letter abbrev */ + const char *lang; /* two-letter abbrev */ +}; + +#define AIM_CLIENTINFO_KNOWNGOOD_3_5_1670 { \ + "AOL Instant Messenger (SM), version 3.5.1670/WIN32", \ + 0x0004, \ + 0x0003, \ + 0x0005, \ + 0x0000, \ + 0x0686, \ + "us", \ + "en", \ +} + +#define AIM_CLIENTINFO_KNOWNGOOD_4_1_2010 { \ + "AOL Instant Messenger (SM), version 4.1.2010/WIN32", \ + 0x0004, \ + 0x0004, \ + 0x0001, \ + 0x0000, \ + 0x07da, \ + "us", \ + "en", \ +} + +/* + * I would make 4.1.2010 the default, but they seem to have found + * an alternate way of breaking that one. + * + * 3.5.1670 should work fine, however, you will be subjected to the + * memory test, which may require you to have a WinAIM binary laying + * around. (see login.c::memrequest()) + */ +#define AIM_CLIENTINFO_KNOWNGOOD AIM_CLIENTINFO_KNOWNGOOD_3_5_1670 + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +/* + * These could be arbitrary, but its easier to use the actual AIM values + */ +#define AIM_CONN_TYPE_AUTH 0x0007 +#define AIM_CONN_TYPE_ADS 0x0005 +#define AIM_CONN_TYPE_BOS 0x0002 +#define AIM_CONN_TYPE_CHAT 0x000e +#define AIM_CONN_TYPE_CHATNAV 0x000d + +/* they start getting arbitrary in rendezvous stuff =) */ +#define AIM_CONN_TYPE_RENDEZVOUS 0x0101 /* these do not speak FLAP! */ +#define AIM_CONN_TYPE_RENDEZVOUS_OUT 0x0102 /* socket waiting for accept() */ + +/* + * Subtypes, we need these for OFT stuff. + */ +#define AIM_CONN_SUBTYPE_OFT_DIRECTIM 0x0001 +#define AIM_CONN_SUBTYPE_OFT_GETFILE 0x0002 +#define AIM_CONN_SUBTYPE_OFT_SENDFILE 0x0003 +#define AIM_CONN_SUBTYPE_OFT_BUDDYICON 0x0004 +#define AIM_CONN_SUBTYPE_OFT_VOICE 0x0005 + +/* + * Status values returned from aim_conn_new(). ORed together. + */ +#define AIM_CONN_STATUS_READY 0x0001 +#define AIM_CONN_STATUS_INTERNALERR 0x0002 +#define AIM_CONN_STATUS_RESOLVERR 0x0040 +#define AIM_CONN_STATUS_CONNERR 0x0080 +#define AIM_CONN_STATUS_INPROGRESS 0x0100 + +#define AIM_FRAMETYPE_FLAP 0x0000 +#define AIM_FRAMETYPE_OFT 0x0001 + +/* + * message type flags + */ +#define AIM_MTYPE_PLAIN 0x01 +#define AIM_MTYPE_CHAT 0x02 +#define AIM_MTYPE_FILEREQ 0x03 +#define AIM_MTYPE_URL 0x04 +#define AIM_MTYPE_AUTHREQ 0x06 +#define AIM_MTYPE_AUTHDENY 0x07 +#define AIM_MTYPE_AUTHOK 0x08 +#define AIM_MTYPE_SERVER 0x09 +#define AIM_MTYPE_ADDED 0x0C +#define AIM_MTYPE_WWP 0x0D +#define AIM_MTYPE_EEXPRESS 0x0E +#define AIM_MTYPE_CONTACTS 0x13 +#define AIM_MTYPE_PLUGIN 0x1A +#define AIM_MTYPE_AUTOAWAY 0xE8 +#define AIM_MTYPE_AUTOBUSY 0xE9 +#define AIM_MTYPE_AUTONA 0xEA +#define AIM_MTYPE_AUTODND 0xEB +#define AIM_MTYPE_AUTOFFC 0xEC + +typedef struct aim_conn_s { + int fd; + guint16 type; + guint16 subtype; + flap_seqnum_t seqnum; + guint32 status; + void *priv; /* misc data the client may want to store */ + void *internal; /* internal conn-specific libfaim data */ + time_t lastactivity; /* time of last transmit */ + int forcedlatency; + void *handlerlist; + void *sessv; /* pointer to parent session */ + void *inside; /* only accessible from inside libfaim */ + struct aim_conn_s *next; +} aim_conn_t; + +/* + * Byte Stream type. Sort of. + * + * Use of this type serves a couple purposes: + * - Buffer/buflen pairs are passed all around everywhere. This turns + * that into one value, as well as abstracting it slightly. + * - Through the abstraction, it is possible to enable bounds checking + * for robustness at the cost of performance. But a clean failure on + * weird packets is much better than a segfault. + * - I like having variables named "bs". + * + * Don't touch the insides of this struct. Or I'll have to kill you. + * + */ +typedef struct aim_bstream_s { + guint8 *data; + guint32 len; + guint32 offset; +} aim_bstream_t; + +typedef struct aim_frame_s { + guint8 hdrtype; /* defines which piece of the union to use */ + union { + struct { + guint8 type; + flap_seqnum_t seqnum; + } flap; + struct { + guint16 type; + guint8 magic[4]; /* ODC2 OFT2 */ + guint16 hdr2len; + guint8 *hdr2; /* rest of bloated header */ + } oft; + } hdr; + aim_bstream_t data; /* payload stream */ + guint8 handled; /* 0 = new, !0 = been handled */ + guint8 nofree; /* 0 = free data on purge, 1 = only unlink */ + aim_conn_t *conn; /* the connection it came in on... */ + struct aim_frame_s *next; +} aim_frame_t; + +typedef struct aim_msgcookie_s { + unsigned char cookie[8]; + int type; + void *data; + time_t addtime; + struct aim_msgcookie_s *next; +} aim_msgcookie_t; + +/* + * AIM Session: The main client-data interface. + * + */ +typedef struct aim_session_s { + + /* ---- Client Accessible ------------------------ */ + + /* Our screen name. */ + char sn[MAXSNLEN+1]; + + /* + * Pointer to anything the client wants to + * explicitly associate with this session. + * + * This is for use in the callbacks mainly. In any + * callback, you can access this with sess->aux_data. + * + */ + void *aux_data; + + /* ---- Internal Use Only ------------------------ */ + + /* Server-stored information (ssi) */ + struct { + int received_data; + guint16 revision; + struct aim_ssi_item *items; + time_t timestamp; + int waiting_for_ack; + aim_frame_t *holding_queue; + } ssi; + + /* Connection information */ + aim_conn_t *connlist; + + /* + * Transmit/receive queues. + * + * These are only used when you don't use your own lowlevel + * I/O. I don't suggest that you use libfaim's internal I/O. + * Its really bad and the API/event model is quirky at best. + * + */ + aim_frame_t *queue_outgoing; + aim_frame_t *queue_incoming; + + /* + * Tx Enqueuing function. + * + * This is how you override the transmit direction of libfaim's + * internal I/O. This function will be called whenever it needs + * to send something. + * + */ + int (*tx_enqueue)(struct aim_session_s *, aim_frame_t *); + + /* + * Outstanding snac handling + * + * XXX: Should these be per-connection? -mid + */ + void *snac_hash[AIM_SNAC_HASH_SIZE]; + aim_snacid_t snacid_next; + + struct aim_icq_info *icq_info; + struct aim_oft_info *oft_info; + struct aim_authresp_info *authinfo; + struct aim_emailinfo *emailinfo; + + struct { + struct aim_userinfo_s *userinfo; + struct userinfo_node *torequest; + struct userinfo_node *requested; + int waiting_for_response; + } locate; + + guint32 flags; /* AIM_SESS_FLAGS_ */ + + aim_msgcookie_t *msgcookies; + + void *modlistv; + + guint8 aim_icq_state; /* ICQ representation of away state */ +} aim_session_t; + +/* Values for sess->flags */ +#define AIM_SESS_FLAGS_SNACLOGIN 0x00000001 +#define AIM_SESS_FLAGS_XORLOGIN 0x00000002 +#define AIM_SESS_FLAGS_NONBLOCKCONNECT 0x00000004 +#define AIM_SESS_FLAGS_DONTTIMEOUTONICBM 0x00000008 + +/* Valid for calling aim_icq_setstatus() and for aim_userinfo_t->icqinfo.status */ +#define AIM_ICQ_STATE_NORMAL 0x00000000 +#define AIM_ICQ_STATE_AWAY 0x00000001 +#define AIM_ICQ_STATE_DND 0x00000002 +#define AIM_ICQ_STATE_OUT 0x00000004 +#define AIM_ICQ_STATE_BUSY 0x00000010 +#define AIM_ICQ_STATE_CHAT 0x00000020 +#define AIM_ICQ_STATE_INVISIBLE 0x00000100 +#define AIM_ICQ_STATE_WEBAWARE 0x00010000 +#define AIM_ICQ_STATE_HIDEIP 0x00020000 +#define AIM_ICQ_STATE_BIRTHDAY 0x00080000 +#define AIM_ICQ_STATE_DIRECTDISABLED 0x00100000 +#define AIM_ICQ_STATE_ICQHOMEPAGE 0x00200000 +#define AIM_ICQ_STATE_DIRECTREQUIREAUTH 0x10000000 +#define AIM_ICQ_STATE_DIRECTCONTACTLIST 0x20000000 + +/* + * AIM User Info, Standard Form. + */ +typedef struct { + char sn[MAXSNLEN+1]; + guint16 warnlevel; + guint16 idletime; + guint16 flags; + guint32 membersince; + guint32 onlinesince; + guint32 sessionlen; + guint32 capabilities; + struct { + guint32 status; + guint32 ipaddr; + guint8 crap[0x25]; /* until we figure it out... */ + } icqinfo; + guint32 present; +} aim_userinfo_t; + +#define AIM_USERINFO_PRESENT_FLAGS 0x00000001 +#define AIM_USERINFO_PRESENT_MEMBERSINCE 0x00000002 +#define AIM_USERINFO_PRESENT_ONLINESINCE 0x00000004 +#define AIM_USERINFO_PRESENT_IDLE 0x00000008 +#define AIM_USERINFO_PRESENT_ICQEXTSTATUS 0x00000010 +#define AIM_USERINFO_PRESENT_ICQIPADDR 0x00000020 +#define AIM_USERINFO_PRESENT_ICQDATA 0x00000040 +#define AIM_USERINFO_PRESENT_CAPABILITIES 0x00000080 +#define AIM_USERINFO_PRESENT_SESSIONLEN 0x00000100 + +const char *aim_userinfo_sn(aim_userinfo_t *ui); +guint16 aim_userinfo_flags(aim_userinfo_t *ui); +guint16 aim_userinfo_idle(aim_userinfo_t *ui); +float aim_userinfo_warnlevel(aim_userinfo_t *ui); +time_t aim_userinfo_membersince(aim_userinfo_t *ui); +time_t aim_userinfo_onlinesince(aim_userinfo_t *ui); +guint32 aim_userinfo_sessionlen(aim_userinfo_t *ui); +int aim_userinfo_hascap(aim_userinfo_t *ui, guint32 cap); + +#define AIM_FLAG_UNCONFIRMED 0x0001 /* "damned transients" */ +#define AIM_FLAG_ADMINISTRATOR 0x0002 +#define AIM_FLAG_AOL 0x0004 +#define AIM_FLAG_OSCAR_PAY 0x0008 +#define AIM_FLAG_FREE 0x0010 +#define AIM_FLAG_AWAY 0x0020 +#define AIM_FLAG_ICQ 0x0040 +#define AIM_FLAG_WIRELESS 0x0080 +#define AIM_FLAG_UNKNOWN100 0x0100 +#define AIM_FLAG_UNKNOWN200 0x0200 +#define AIM_FLAG_ACTIVEBUDDY 0x0400 +#define AIM_FLAG_UNKNOWN800 0x0800 +#define AIM_FLAG_ABINTERNAL 0x1000 + +#define AIM_FLAG_ALLUSERS 0x001f + +/* + * TLV handling + */ + +/* Generic TLV structure. */ +typedef struct aim_tlv_s { + guint16 type; + guint16 length; + guint8 *value; +} aim_tlv_t; + +/* List of above. */ +typedef struct aim_tlvlist_s { + aim_tlv_t *tlv; + struct aim_tlvlist_s *next; +} aim_tlvlist_t; + +/* TLV-handling functions */ + +#if 0 +/* Very, very raw TLV handling. */ +int aim_puttlv_8(guint8 *buf, const guint16 t, const guint8 v); +int aim_puttlv_16(guint8 *buf, const guint16 t, const guint16 v); +int aim_puttlv_32(guint8 *buf, const guint16 t, const guint32 v); +int aim_puttlv_raw(guint8 *buf, const guint16 t, const guint16 l, const guint8 *v); +#endif + +/* TLV list handling. */ +aim_tlvlist_t *aim_readtlvchain(aim_bstream_t *bs); +void aim_freetlvchain(aim_tlvlist_t **list); +aim_tlv_t *aim_gettlv(aim_tlvlist_t *list, guint16 t, const int n); +char *aim_gettlv_str(aim_tlvlist_t *list, const guint16 t, const int n); +guint8 aim_gettlv8(aim_tlvlist_t *list, const guint16 type, const int num); +guint16 aim_gettlv16(aim_tlvlist_t *list, const guint16 t, const int n); +guint32 aim_gettlv32(aim_tlvlist_t *list, const guint16 t, const int n); +int aim_writetlvchain(aim_bstream_t *bs, aim_tlvlist_t **list); +int aim_addtlvtochain8(aim_tlvlist_t **list, const guint16 t, const guint8 v); +int aim_addtlvtochain16(aim_tlvlist_t **list, const guint16 t, const guint16 v); +int aim_addtlvtochain32(aim_tlvlist_t **list, const guint16 type, const guint32 v); +int aim_addtlvtochain_availmsg(aim_tlvlist_t **list, const guint16 type, const char *msg); +int aim_addtlvtochain_raw(aim_tlvlist_t **list, const guint16 t, const guint16 l, const guint8 *v); +int aim_addtlvtochain_caps(aim_tlvlist_t **list, const guint16 t, const guint32 caps); +int aim_addtlvtochain_noval(aim_tlvlist_t **list, const guint16 type); +int aim_addtlvtochain_userinfo(aim_tlvlist_t **list, guint16 type, aim_userinfo_t *ui); +int aim_addtlvtochain_frozentlvlist(aim_tlvlist_t **list, guint16 type, aim_tlvlist_t **tl); +int aim_counttlvchain(aim_tlvlist_t **list); +int aim_sizetlvchain(aim_tlvlist_t **list); + + +/* + * Get command from connections + * + * aim_get_commmand() is the libfaim lowlevel I/O in the receive direction. + * XXX Make this easily overridable. + * + */ +int aim_get_command(aim_session_t *, aim_conn_t *); + +/* + * Dispatch commands that are in the rx queue. + */ +void aim_rxdispatch(aim_session_t *); + +int aim_debugconn_sendconnect(aim_session_t *sess, aim_conn_t *conn); + +typedef int (*aim_rxcallback_t)(aim_session_t *, aim_frame_t *, ...); + +struct aim_clientrelease { + char *name; + guint32 build; + char *url; + char *info; +}; + +struct aim_authresp_info { + char *sn; + guint16 errorcode; + char *errorurl; + guint16 regstatus; + char *email; + char *bosip; + guint8 *cookie; + struct aim_clientrelease latestrelease; + struct aim_clientrelease latestbeta; +}; + +/* Callback data for redirect. */ +struct aim_redirect_data { + guint16 group; + const char *ip; + const guint8 *cookie; + struct { /* group == AIM_CONN_TYPE_CHAT */ + guint16 exchange; + const char *room; + guint16 instance; + } chat; +}; + +int aim_clientready(aim_session_t *sess, aim_conn_t *conn); +int aim_sendflapver(aim_session_t *sess, aim_conn_t *conn); +int aim_request_login(aim_session_t *sess, aim_conn_t *conn, const char *sn); +int aim_send_login(aim_session_t *, aim_conn_t *, const char *, const char *, struct client_info_s *, const char *key); +int aim_encode_password_md5(const char *password, const char *key, unsigned char *digest); +void aim_purge_rxqueue(aim_session_t *); + +#define AIM_TX_QUEUED 0 /* default */ +#define AIM_TX_IMMEDIATE 1 +#define AIM_TX_USER 2 +int aim_tx_setenqueue(aim_session_t *sess, int what, int (*func)(aim_session_t *, aim_frame_t *)); + +int aim_tx_flushqueue(aim_session_t *); +void aim_tx_purgequeue(aim_session_t *); + +int aim_conn_setlatency(aim_conn_t *conn, int newval); + +void aim_conn_kill(aim_session_t *sess, aim_conn_t **deadconn); + +int aim_conn_addhandler(aim_session_t *, aim_conn_t *conn, u_short family, u_short type, aim_rxcallback_t newhandler, u_short flags); +int aim_clearhandlers(aim_conn_t *conn); + +aim_conn_t *aim_conn_findbygroup(aim_session_t *sess, guint16 group); +aim_session_t *aim_conn_getsess(aim_conn_t *conn); +void aim_conn_close(aim_conn_t *deadconn); +aim_conn_t *aim_newconn(aim_session_t *, int type, const char *dest); +int aim_conngetmaxfd(aim_session_t *); +aim_conn_t *aim_select(aim_session_t *, struct timeval *, int *); +int aim_conn_isready(aim_conn_t *); +int aim_conn_setstatus(aim_conn_t *, int); +int aim_conn_completeconnect(aim_session_t *sess, aim_conn_t *conn); +int aim_conn_isconnecting(aim_conn_t *conn); + +typedef void (*faim_debugging_callback_t)(aim_session_t *sess, int level, const char *format, va_list va); +int aim_setdebuggingcb(aim_session_t *sess, faim_debugging_callback_t); +void aim_session_init(aim_session_t *, guint32 flags, int debuglevel); +void aim_session_kill(aim_session_t *); +void aim_setupproxy(aim_session_t *sess, const char *server, const char *username, const char *password); +aim_conn_t *aim_getconn_type(aim_session_t *, int type); +aim_conn_t *aim_getconn_type_all(aim_session_t *, int type); +aim_conn_t *aim_getconn_fd(aim_session_t *, int fd); + +/* aim_misc.c */ + + +struct aim_chat_roominfo { + unsigned short exchange; + char *name; + unsigned short instance; +}; + + +#define AIM_VISIBILITYCHANGE_PERMITADD 0x05 +#define AIM_VISIBILITYCHANGE_PERMITREMOVE 0x06 +#define AIM_VISIBILITYCHANGE_DENYADD 0x07 +#define AIM_VISIBILITYCHANGE_DENYREMOVE 0x08 + +#define AIM_PRIVFLAGS_ALLOWIDLE 0x01 +#define AIM_PRIVFLAGS_ALLOWMEMBERSINCE 0x02 + +#define AIM_WARN_ANON 0x01 + +int aim_sendpauseack(aim_session_t *sess, aim_conn_t *conn); +int aim_send_warning(aim_session_t *sess, aim_conn_t *conn, const char *destsn, guint32 flags); +int aim_nop(aim_session_t *, aim_conn_t *); +int aim_flap_nop(aim_session_t *sess, aim_conn_t *conn); +int aim_bos_setidle(aim_session_t *, aim_conn_t *, guint32); +int aim_bos_changevisibility(aim_session_t *, aim_conn_t *, int, const char *); +int aim_bos_setbuddylist(aim_session_t *, aim_conn_t *, const char *); +int aim_bos_setprofile(aim_session_t *sess, aim_conn_t *conn, const char *profile, const char *awaymsg, guint32 caps); +int aim_bos_setgroupperm(aim_session_t *, aim_conn_t *, guint32 mask); +int aim_bos_setprivacyflags(aim_session_t *, aim_conn_t *, guint32); +int aim_reqpersonalinfo(aim_session_t *, aim_conn_t *); +int aim_reqservice(aim_session_t *, aim_conn_t *, guint16); +int aim_bos_reqrights(aim_session_t *, aim_conn_t *); +int aim_bos_reqbuddyrights(aim_session_t *, aim_conn_t *); +int aim_bos_reqlocaterights(aim_session_t *, aim_conn_t *); +int aim_setdirectoryinfo(aim_session_t *sess, aim_conn_t *conn, const char *first, const char *middle, const char *last, const char *maiden, const char *nickname, const char *street, const char *city, const char *state, const char *zip, int country, guint16 privacy); +int aim_setuserinterests(aim_session_t *sess, aim_conn_t *conn, const char *interest1, const char *interest2, const char *interest3, const char *interest4, const char *interest5, guint16 privacy); +int aim_setextstatus(aim_session_t *sess, aim_conn_t *conn, guint32 status); + +struct aim_fileheader_t *aim_getlisting(aim_session_t *sess, FILE *); + +#define AIM_CLIENTTYPE_UNKNOWN 0x0000 +#define AIM_CLIENTTYPE_MC 0x0001 +#define AIM_CLIENTTYPE_WINAIM 0x0002 +#define AIM_CLIENTTYPE_WINAIM41 0x0003 +#define AIM_CLIENTTYPE_AOL_TOC 0x0004 +unsigned short aim_fingerprintclient(unsigned char *msghdr, int len); + +#define AIM_RATE_CODE_CHANGE 0x0001 +#define AIM_RATE_CODE_WARNING 0x0002 +#define AIM_RATE_CODE_LIMIT 0x0003 +#define AIM_RATE_CODE_CLEARLIMIT 0x0004 +int aim_ads_requestads(aim_session_t *sess, aim_conn_t *conn); + +/* aim_im.c */ + +aim_conn_t *aim_sendfile_initiate(aim_session_t *, const char *destsn, const char *filename, guint16 numfiles, guint32 totsize); + +aim_conn_t *aim_getfile_initiate(aim_session_t *sess, aim_conn_t *conn, const char *destsn); +int aim_oft_getfile_request(aim_session_t *sess, aim_conn_t *conn, const char *name, int size); +int aim_oft_getfile_ack(aim_session_t *sess, aim_conn_t *conn); +int aim_oft_getfile_end(aim_session_t *sess, aim_conn_t *conn); + +#define AIM_SENDMEMBLOCK_FLAG_ISREQUEST 0 +#define AIM_SENDMEMBLOCK_FLAG_ISHASH 1 + +int aim_sendmemblock(aim_session_t *sess, aim_conn_t *conn, guint32 offset, guint32 len, const guint8 *buf, guint8 flag); + +#define AIM_GETINFO_GENERALINFO 0x00001 +#define AIM_GETINFO_AWAYMESSAGE 0x00003 +#define AIM_GETINFO_CAPABILITIES 0x0004 + +struct aim_invite_priv { + char *sn; + char *roomname; + guint16 exchange; + guint16 instance; +}; + +#define AIM_COOKIETYPE_UNKNOWN 0x00 +#define AIM_COOKIETYPE_ICBM 0x01 +#define AIM_COOKIETYPE_ADS 0x02 +#define AIM_COOKIETYPE_BOS 0x03 +#define AIM_COOKIETYPE_IM 0x04 +#define AIM_COOKIETYPE_CHAT 0x05 +#define AIM_COOKIETYPE_CHATNAV 0x06 +#define AIM_COOKIETYPE_INVITE 0x07 +/* we'll move OFT up a bit to give breathing room. not like it really + * matters. */ +#define AIM_COOKIETYPE_OFTIM 0x10 +#define AIM_COOKIETYPE_OFTGET 0x11 +#define AIM_COOKIETYPE_OFTSEND 0x12 +#define AIM_COOKIETYPE_OFTVOICE 0x13 +#define AIM_COOKIETYPE_OFTIMAGE 0x14 +#define AIM_COOKIETYPE_OFTICON 0x15 + +int aim_handlerendconnect(aim_session_t *sess, aim_conn_t *cur); + +#define AIM_TRANSFER_DENY_NOTSUPPORTED 0x0000 +#define AIM_TRANSFER_DENY_DECLINE 0x0001 +#define AIM_TRANSFER_DENY_NOTACCEPTING 0x0002 +int aim_denytransfer(aim_session_t *sess, const char *sender, const guint8 *cookie, unsigned short code); +aim_conn_t *aim_accepttransfer(aim_session_t *sess, aim_conn_t *conn, const char *sn, const guint8 *cookie, const guint8 *ip, guint16 listingfiles, guint16 listingtotsize, guint16 listingsize, guint32 listingchecksum, guint16 rendid); + +int aim_getinfo(aim_session_t *, aim_conn_t *, const char *, unsigned short); +int aim_sendbuddyoncoming(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *info); +int aim_sendbuddyoffgoing(aim_session_t *sess, aim_conn_t *conn, const char *sn); + +#define AIM_IMPARAM_FLAG_CHANMSGS_ALLOWED 0x00000001 +#define AIM_IMPARAM_FLAG_MISSEDCALLS_ENABLED 0x00000002 + +/* This is what the server will give you if you don't set them yourself. */ +#define AIM_IMPARAM_DEFAULTS { \ + 0, \ + AIM_IMPARAM_FLAG_CHANMSGS_ALLOWED | AIM_IMPARAM_FLAG_MISSEDCALLS_ENABLED, \ + 512, /* !! Note how small this is. */ \ + (99.9)*10, (99.9)*10, \ + 1000 /* !! And how large this is. */ \ +} + +/* This is what most AIM versions use. */ +#define AIM_IMPARAM_REASONABLE { \ + 0, \ + AIM_IMPARAM_FLAG_CHANMSGS_ALLOWED | AIM_IMPARAM_FLAG_MISSEDCALLS_ENABLED, \ + 8000, \ + (99.9)*10, (99.9)*10, \ + 0 \ +} + + +struct aim_icbmparameters { + guint16 maxchan; + guint32 flags; /* AIM_IMPARAM_FLAG_ */ + guint16 maxmsglen; /* message size that you will accept */ + guint16 maxsenderwarn; /* this and below are *10 (999=99.9%) */ + guint16 maxrecverwarn; + guint32 minmsginterval; /* in milliseconds? */ +}; + +int aim_reqicbmparams(aim_session_t *sess); +int aim_seticbmparam(aim_session_t *sess, struct aim_icbmparameters *params); + +/* auth.c */ +int aim_sendcookie(aim_session_t *, aim_conn_t *, const guint8 *); + +int aim_admin_changepasswd(aim_session_t *, aim_conn_t *, const char *newpw, const char *curpw); +int aim_admin_reqconfirm(aim_session_t *sess, aim_conn_t *conn); +int aim_admin_getinfo(aim_session_t *sess, aim_conn_t *conn, guint16 info); +int aim_admin_setemail(aim_session_t *sess, aim_conn_t *conn, const char *newemail); +int aim_admin_setnick(aim_session_t *sess, aim_conn_t *conn, const char *newnick); + +/* These apply to exchanges as well. */ +#define AIM_CHATROOM_FLAG_EVILABLE 0x0001 +#define AIM_CHATROOM_FLAG_NAV_ONLY 0x0002 +#define AIM_CHATROOM_FLAG_INSTANCING_ALLOWED 0x0004 +#define AIM_CHATROOM_FLAG_OCCUPANT_PEEK_ALLOWED 0x0008 + +struct aim_chat_exchangeinfo { + guint16 number; + guint16 flags; + char *name; + char *charset1; + char *lang1; + char *charset2; + char *lang2; +}; + +#define AIM_CHATFLAGS_NOREFLECT 0x0001 +#define AIM_CHATFLAGS_AWAY 0x0002 +int aim_chat_send_im(aim_session_t *sess, aim_conn_t *conn, guint16 flags, const char *msg, int msglen); +int aim_chat_join(aim_session_t *sess, aim_conn_t *conn, guint16 exchange, const char *roomname, guint16 instance); +int aim_chat_attachname(aim_conn_t *conn, guint16 exchange, const char *roomname, guint16 instance); +char *aim_chat_getname(aim_conn_t *conn); +aim_conn_t *aim_chat_getconn(aim_session_t *, const char *name); + +int aim_chatnav_reqrights(aim_session_t *sess, aim_conn_t *conn); + +int aim_chat_invite(aim_session_t *sess, aim_conn_t *conn, const char *sn, const char *msg, guint16 exchange, const char *roomname, guint16 instance); + +int aim_chatnav_createroom(aim_session_t *sess, aim_conn_t *conn, const char *name, guint16 exchange); +int aim_chat_leaveroom(aim_session_t *sess, const char *name); + +/* aim_util.c */ +/* + * These are really ugly. You'd think this was LISP. I wish it was. + * + * XXX With the advent of bstream's, these should be removed to enforce + * their use. + * + */ +#define aimutil_put8(buf, data) ((*(buf) = (u_char)(data)&0xff),1) +#define aimutil_get8(buf) ((*(buf))&0xff) +#define aimutil_put16(buf, data) ( \ + (*(buf) = (u_char)((data)>>8)&0xff), \ + (*((buf)+1) = (u_char)(data)&0xff), \ + 2) +#define aimutil_get16(buf) ((((*(buf))<<8)&0xff00) + ((*((buf)+1)) & 0xff)) +#define aimutil_put32(buf, data) ( \ + (*((buf)) = (u_char)((data)>>24)&0xff), \ + (*((buf)+1) = (u_char)((data)>>16)&0xff), \ + (*((buf)+2) = (u_char)((data)>>8)&0xff), \ + (*((buf)+3) = (u_char)(data)&0xff), \ + 4) +#define aimutil_get32(buf) ((((*(buf))<<24)&0xff000000) + \ + (((*((buf)+1))<<16)&0x00ff0000) + \ + (((*((buf)+2))<< 8)&0x0000ff00) + \ + (((*((buf)+3) )&0x000000ff))) + +/* Little-endian versions (damn ICQ) */ +#define aimutil_putle8(buf, data) ( \ + (*(buf) = (unsigned char)(data) & 0xff), \ + 1) +#define aimutil_getle8(buf) ( \ + (*(buf)) & 0xff \ + ) +#define aimutil_putle16(buf, data) ( \ + (*((buf)+0) = (unsigned char)((data) >> 0) & 0xff), \ + (*((buf)+1) = (unsigned char)((data) >> 8) & 0xff), \ + 2) +#define aimutil_getle16(buf) ( \ + (((*((buf)+0)) << 0) & 0x00ff) + \ + (((*((buf)+1)) << 8) & 0xff00) \ + ) +#define aimutil_putle32(buf, data) ( \ + (*((buf)+0) = (unsigned char)((data) >> 0) & 0xff), \ + (*((buf)+1) = (unsigned char)((data) >> 8) & 0xff), \ + (*((buf)+2) = (unsigned char)((data) >> 16) & 0xff), \ + (*((buf)+3) = (unsigned char)((data) >> 24) & 0xff), \ + 4) +#define aimutil_getle32(buf) ( \ + (((*((buf)+0)) << 0) & 0x000000ff) + \ + (((*((buf)+1)) << 8) & 0x0000ff00) + \ + (((*((buf)+2)) << 16) & 0x00ff0000) + \ + (((*((buf)+3)) << 24) & 0xff000000)) + + +int aimutil_putstr(u_char *, const char *, int); +int aimutil_tokslen(char *toSearch, int index, char dl); +int aimutil_itemcnt(char *toSearch, char dl); +char *aimutil_itemidx(char *toSearch, int index, char dl); +int aim_sncmp(const char *a, const char *b); + +#include <aim_internal.h> + +/* + * SNAC Families. + */ +#define AIM_CB_FAM_ACK 0x0000 +#define AIM_CB_FAM_GEN 0x0001 +#define AIM_CB_FAM_SPECIAL 0xffff /* Internal libfaim use */ + +/* + * SNAC Family: Ack. + * + * Not really a family, but treating it as one really + * helps it fit into the libfaim callback structure better. + * + */ +#define AIM_CB_ACK_ACK 0x0001 + +/* + * SNAC Family: General. + */ +#define AIM_CB_GEN_ERROR 0x0001 +#define AIM_CB_GEN_CLIENTREADY 0x0002 +#define AIM_CB_GEN_SERVERREADY 0x0003 +#define AIM_CB_GEN_SERVICEREQ 0x0004 +#define AIM_CB_GEN_REDIRECT 0x0005 +#define AIM_CB_GEN_RATEINFOREQ 0x0006 +#define AIM_CB_GEN_RATEINFO 0x0007 +#define AIM_CB_GEN_RATEINFOACK 0x0008 +#define AIM_CB_GEN_RATECHANGE 0x000a +#define AIM_CB_GEN_SERVERPAUSE 0x000b +#define AIM_CB_GEN_SERVERRESUME 0x000d +#define AIM_CB_GEN_REQSELFINFO 0x000e +#define AIM_CB_GEN_SELFINFO 0x000f +#define AIM_CB_GEN_EVIL 0x0010 +#define AIM_CB_GEN_SETIDLE 0x0011 +#define AIM_CB_GEN_MIGRATIONREQ 0x0012 +#define AIM_CB_GEN_MOTD 0x0013 +#define AIM_CB_GEN_SETPRIVFLAGS 0x0014 +#define AIM_CB_GEN_WELLKNOWNURL 0x0015 +#define AIM_CB_GEN_NOP 0x0016 +#define AIM_CB_GEN_DEFAULT 0xffff + +/* + * SNAC Family: Advertisement Services + */ +#define AIM_CB_ADS_ERROR 0x0001 +#define AIM_CB_ADS_DEFAULT 0xffff + +/* + * OFT Services + * + * See non-SNAC note below. + */ +#define AIM_CB_OFT_DIRECTIMCONNECTREQ 0x0001/* connect request -- actually an OSCAR CAP*/ +#define AIM_CB_OFT_DIRECTIMINCOMING 0x0002 +#define AIM_CB_OFT_DIRECTIMDISCONNECT 0x0003 +#define AIM_CB_OFT_DIRECTIMTYPING 0x0004 +#define AIM_CB_OFT_DIRECTIMINITIATE 0x0005 + +#define AIM_CB_OFT_GETFILECONNECTREQ 0x0006 /* connect request -- actually an OSCAR CAP*/ +#define AIM_CB_OFT_GETFILELISTINGREQ 0x0007 /* OFT listing.txt request */ +#define AIM_CB_OFT_GETFILEFILEREQ 0x0008 /* received file request */ +#define AIM_CB_OFT_GETFILEFILESEND 0x0009 /* received file request confirm -- send data */ +#define AIM_CB_OFT_GETFILECOMPLETE 0x000a /* received file send complete*/ +#define AIM_CB_OFT_GETFILEINITIATE 0x000b /* request for file get acknowledge */ +#define AIM_CB_OFT_GETFILEDISCONNECT 0x000c /* OFT connection disconnected.*/ +#define AIM_CB_OFT_GETFILELISTING 0x000d /* OFT listing.txt received.*/ +#define AIM_CB_OFT_GETFILERECEIVE 0x000e /* OFT file incoming.*/ +#define AIM_CB_OFT_GETFILELISTINGRXCONFIRM 0x000f +#define AIM_CB_OFT_GETFILESTATE4 0x0010 + +#define AIM_CB_OFT_SENDFILEDISCONNECT 0x0020 /* OFT connection disconnected.*/ + + + +/* + * SNAC Family: Internal Messages + * + * This isn't truely a SNAC family either, but using + * these, we can integrated non-SNAC services into + * the SNAC-centered libfaim callback structure. + * + */ +#define AIM_CB_SPECIAL_AUTHSUCCESS 0x0001 +#define AIM_CB_SPECIAL_AUTHOTHER 0x0002 +#define AIM_CB_SPECIAL_CONNERR 0x0003 +#define AIM_CB_SPECIAL_CONNCOMPLETE 0x0004 +#define AIM_CB_SPECIAL_FLAPVER 0x0005 +#define AIM_CB_SPECIAL_CONNINITDONE 0x0006 +#define AIM_CB_SPECIAL_IMAGETRANSFER 0x007 +#define AIM_CB_SPECIAL_UNKNOWN 0xffff +#define AIM_CB_SPECIAL_DEFAULT AIM_CB_SPECIAL_UNKNOWN + +#endif /* __AIM_H__ */ diff --git a/protocols/oscar/aim_cbtypes.h b/protocols/oscar/aim_cbtypes.h new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/protocols/oscar/aim_cbtypes.h diff --git a/protocols/oscar/aim_internal.h b/protocols/oscar/aim_internal.h new file mode 100644 index 00000000..2e36c961 --- /dev/null +++ b/protocols/oscar/aim_internal.h @@ -0,0 +1,216 @@ +/* + * aim_internal.h -- prototypes/structs for the guts of libfaim + * + */ + +#ifndef __AIM_INTERNAL_H__ +#define __AIM_INTERNAL_H__ 1 + +typedef struct { + guint16 family; + guint16 subtype; + guint16 flags; + guint32 id; +} aim_modsnac_t; + +#define AIM_MODULENAME_MAXLEN 16 +#define AIM_MODFLAG_MULTIFAMILY 0x0001 +typedef struct aim_module_s { + guint16 family; + guint16 version; + guint16 toolid; + guint16 toolversion; + guint16 flags; + char name[AIM_MODULENAME_MAXLEN+1]; + int (*snachandler)(aim_session_t *sess, struct aim_module_s *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs); + void (*shutdown)(aim_session_t *sess, struct aim_module_s *mod); + void *priv; + struct aim_module_s *next; +} aim_module_t; + +int aim__registermodule(aim_session_t *sess, int (*modfirst)(aim_session_t *, aim_module_t *)); +void aim__shutdownmodules(aim_session_t *sess); +aim_module_t *aim__findmodulebygroup(aim_session_t *sess, guint16 group); + +int buddylist_modfirst(aim_session_t *sess, aim_module_t *mod); +int admin_modfirst(aim_session_t *sess, aim_module_t *mod); +int bos_modfirst(aim_session_t *sess, aim_module_t *mod); +int search_modfirst(aim_session_t *sess, aim_module_t *mod); +int stats_modfirst(aim_session_t *sess, aim_module_t *mod); +int auth_modfirst(aim_session_t *sess, aim_module_t *mod); +int msg_modfirst(aim_session_t *sess, aim_module_t *mod); +int misc_modfirst(aim_session_t *sess, aim_module_t *mod); +int chatnav_modfirst(aim_session_t *sess, aim_module_t *mod); +int chat_modfirst(aim_session_t *sess, aim_module_t *mod); +int locate_modfirst(aim_session_t *sess, aim_module_t *mod); +int general_modfirst(aim_session_t *sess, aim_module_t *mod); +int ssi_modfirst(aim_session_t *sess, aim_module_t *mod); +int icq_modfirst(aim_session_t *sess, aim_module_t *mod); + +int aim_genericreq_n(aim_session_t *, aim_conn_t *conn, guint16 family, guint16 subtype); +int aim_genericreq_n_snacid(aim_session_t *, aim_conn_t *conn, guint16 family, guint16 subtype); +int aim_genericreq_l(aim_session_t *, aim_conn_t *conn, guint16 family, guint16 subtype, guint32 *); +int aim_genericreq_s(aim_session_t *, aim_conn_t *conn, guint16 family, guint16 subtype, guint16 *); + +#define AIMBS_CURPOSPAIR(x) ((x)->data + (x)->offset), ((x)->len - (x)->offset) + +void aim_rxqueue_cleanbyconn(aim_session_t *sess, aim_conn_t *conn); +int aim_recv(int fd, void *buf, size_t count); +int aim_bstream_init(aim_bstream_t *bs, guint8 *data, int len); +int aim_bstream_empty(aim_bstream_t *bs); +int aim_bstream_curpos(aim_bstream_t *bs); +int aim_bstream_setpos(aim_bstream_t *bs, int off); +void aim_bstream_rewind(aim_bstream_t *bs); +int aim_bstream_advance(aim_bstream_t *bs, int n); +guint8 aimbs_get8(aim_bstream_t *bs); +guint16 aimbs_get16(aim_bstream_t *bs); +guint32 aimbs_get32(aim_bstream_t *bs); +guint8 aimbs_getle8(aim_bstream_t *bs); +guint16 aimbs_getle16(aim_bstream_t *bs); +guint32 aimbs_getle32(aim_bstream_t *bs); +int aimbs_put8(aim_bstream_t *bs, guint8 v); +int aimbs_put16(aim_bstream_t *bs, guint16 v); +int aimbs_put32(aim_bstream_t *bs, guint32 v); +int aimbs_putle8(aim_bstream_t *bs, guint8 v); +int aimbs_putle16(aim_bstream_t *bs, guint16 v); +int aimbs_putle32(aim_bstream_t *bs, guint32 v); +int aimbs_getrawbuf(aim_bstream_t *bs, guint8 *buf, int len); +guint8 *aimbs_getraw(aim_bstream_t *bs, int len); +char *aimbs_getstr(aim_bstream_t *bs, int len); +int aimbs_putraw(aim_bstream_t *bs, const guint8 *v, int len); +int aimbs_putbs(aim_bstream_t *bs, aim_bstream_t *srcbs, int len); + +int aim_get_command_rendezvous(aim_session_t *sess, aim_conn_t *conn); + +int aim_tx_sendframe(aim_session_t *sess, aim_frame_t *cur); +flap_seqnum_t aim_get_next_txseqnum(aim_conn_t *); +aim_frame_t *aim_tx_new(aim_session_t *sess, aim_conn_t *conn, guint8 framing, guint8 chan, int datalen); +void aim_frame_destroy(aim_frame_t *); +int aim_tx_enqueue(aim_session_t *, aim_frame_t *); +int aim_tx_printqueue(aim_session_t *); +void aim_tx_cleanqueue(aim_session_t *, aim_conn_t *); + +aim_rxcallback_t aim_callhandler(aim_session_t *sess, aim_conn_t *conn, u_short family, u_short type); + +/* + * Generic SNAC structure. Rarely if ever used. + */ +typedef struct aim_snac_s { + aim_snacid_t id; + guint16 family; + guint16 type; + guint16 flags; + void *data; + time_t issuetime; + struct aim_snac_s *next; +} aim_snac_t; + +void aim_initsnachash(aim_session_t *sess); +aim_snacid_t aim_newsnac(aim_session_t *, aim_snac_t *newsnac); +aim_snacid_t aim_cachesnac(aim_session_t *sess, const guint16 family, const guint16 type, const guint16 flags, const void *data, const int datalen); +aim_snac_t *aim_remsnac(aim_session_t *, aim_snacid_t id); +void aim_cleansnacs(aim_session_t *, int maxage); +int aim_putsnac(aim_bstream_t *, guint16 family, guint16 type, guint16 flags, aim_snacid_t id); + +aim_conn_t *aim_cloneconn(aim_session_t *sess, aim_conn_t *src); +void aim_clonehandlers(aim_session_t *sess, aim_conn_t *dest, aim_conn_t *src); + +int aim_oft_buildheader(unsigned char *,struct aim_fileheader_t *); + +int aim_parse_unknown(aim_session_t *, aim_frame_t *, ...); + +/* Stored in ->priv of the service request SNAC for chats. */ +struct chatsnacinfo { + guint16 exchange; + char name[128]; + guint16 instance; +}; + +/* these are used by aim_*_clientready */ +#define AIM_TOOL_JAVA 0x0001 +#define AIM_TOOL_MAC 0x0002 +#define AIM_TOOL_WIN16 0x0003 +#define AIM_TOOL_WIN32 0x0004 +#define AIM_TOOL_MAC68K 0x0005 +#define AIM_TOOL_MACPPC 0x0006 +#define AIM_TOOL_NEWWIN 0x0010 +struct aim_tool_version { + guint16 group; + guint16 version; + guint16 tool; + guint16 toolversion; +}; + +/* + * In SNACland, the terms 'family' and 'group' are synonymous -- the former + * is my term, the latter is AOL's. + */ +struct snacgroup { + guint16 group; + struct snacgroup *next; +}; + +struct snacpair { + guint16 group; + guint16 subtype; + struct snacpair *next; +}; + +struct rateclass { + guint16 classid; + guint32 windowsize; + guint32 clear; + guint32 alert; + guint32 limit; + guint32 disconnect; + guint32 current; + guint32 max; + guint8 unknown[5]; /* only present in versions >= 3 */ + struct snacpair *members; + struct rateclass *next; +}; + +/* + * This is inside every connection. But it is a void * to anything + * outside of libfaim. It should remain that way. It's called data + * abstraction. Maybe you've heard of it. (Probably not if you're a + * libfaim user.) + * + */ +typedef struct aim_conn_inside_s { + struct snacgroup *groups; + struct rateclass *rates; +} aim_conn_inside_t; + +void aim_conn_addgroup(aim_conn_t *conn, guint16 group); + +guint32 aim_getcap(aim_session_t *sess, aim_bstream_t *bs, int len); +int aim_putcap(aim_bstream_t *bs, guint32 caps); + +int aim_cachecookie(aim_session_t *sess, aim_msgcookie_t *cookie); +aim_msgcookie_t *aim_uncachecookie(aim_session_t *sess, guint8 *cookie, int type); +aim_msgcookie_t *aim_mkcookie(guint8 *, int, void *); +aim_msgcookie_t *aim_checkcookie(aim_session_t *sess, const unsigned char *, const int); +int aim_freecookie(aim_session_t *sess, aim_msgcookie_t *cookie); +int aim_msgcookie_gettype(int reqclass); +int aim_cookie_free(aim_session_t *sess, aim_msgcookie_t *cookie); + +int aim_extractuserinfo(aim_session_t *sess, aim_bstream_t *bs, aim_userinfo_t *); +int aim_putuserinfo(aim_bstream_t *bs, aim_userinfo_t *info); + +int aim_chat_readroominfo(aim_bstream_t *bs, struct aim_chat_roominfo *outinfo); + +int aim_request_directim(aim_session_t *sess, const char *destsn, guint8 *ip, guint16 port, guint8 *ckret); +int aim_request_sendfile(aim_session_t *sess, const char *sn, const char *filename, guint16 numfiles, guint32 totsize, guint8 *ip, guint16 port, guint8 *ckret); +void aim_conn_close_rend(aim_session_t *sess, aim_conn_t *conn); +void aim_conn_kill_rend(aim_session_t *sess, aim_conn_t *conn); + +void aim_conn_kill_chat(aim_session_t *sess, aim_conn_t *conn); + +/* These are all handled internally now. */ +int aim_setversions(aim_session_t *sess, aim_conn_t *conn); +int aim_reqrates(aim_session_t *, aim_conn_t *); +int aim_rates_addparam(aim_session_t *, aim_conn_t *); +int aim_rates_delparam(aim_session_t *, aim_conn_t *); + +#endif /* __AIM_INTERNAL_H__ */ diff --git a/protocols/oscar/auth.c b/protocols/oscar/auth.c new file mode 100644 index 00000000..c25a4604 --- /dev/null +++ b/protocols/oscar/auth.c @@ -0,0 +1,543 @@ +/* + * Deals with the authorizer (group 0x0017=23, and old-style non-SNAC login). + * + */ + +#include <aim.h> + +#include "md5.h" + +static int aim_encode_password(const char *password, unsigned char *encoded); + +/* + * This just pushes the passed cookie onto the passed connection, without + * the SNAC header or any of that. + * + * Very commonly used, as every connection except auth will require this to + * be the first thing you send. + * + */ +int aim_sendcookie(aim_session_t *sess, aim_conn_t *conn, const guint8 *chipsahoy) +{ + aim_frame_t *fr; + aim_tlvlist_t *tl = NULL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x0001, 4+2+2+AIM_COOKIELEN))) + return -ENOMEM; + + aimbs_put32(&fr->data, 0x00000001); + aim_addtlvtochain_raw(&tl, 0x0006, AIM_COOKIELEN, chipsahoy); + aim_writetlvchain(&fr->data, &tl); + aim_freetlvchain(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Normally the FLAP version is sent as the first few bytes of the cookie, + * meaning you generally never call this. + * + * But there are times when something might want it seperate. Specifically, + * libfaim sends this internally when doing SNAC login. + * + */ +int aim_sendflapver(aim_session_t *sess, aim_conn_t *conn) +{ + aim_frame_t *fr; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x01, 4))) + return -ENOMEM; + + aimbs_put32(&fr->data, 0x00000001); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * This is a bit confusing. + * + * Normal SNAC login goes like this: + * - connect + * - server sends flap version + * - client sends flap version + * - client sends screen name (17/6) + * - server sends hash key (17/7) + * - client sends auth request (17/2 -- aim_send_login) + * - server yells + * + * XOR login (for ICQ) goes like this: + * - connect + * - server sends flap version + * - client sends auth request which contains flap version (aim_send_login) + * - server yells + * + * For the client API, we make them implement the most complicated version, + * and for the simpler version, we fake it and make it look like the more + * complicated process. + * + * This is done by giving the client a faked key, just so we can convince + * them to call aim_send_login right away, which will detect the session + * flag that says this is XOR login and ignore the key, sending an ICQ + * login request instead of the normal SNAC one. + * + * As soon as AOL makes ICQ log in the same way as AIM, this is /gone/. + * + * XXX This may cause problems if the client relies on callbacks only + * being called from the context of aim_rxdispatch()... + * + */ +static int goddamnicq(aim_session_t *sess, aim_conn_t *conn, const char *sn) +{ + aim_frame_t fr; + aim_rxcallback_t userfunc; + + sess->flags &= ~AIM_SESS_FLAGS_SNACLOGIN; + sess->flags |= AIM_SESS_FLAGS_XORLOGIN; + + fr.conn = conn; + + if ((userfunc = aim_callhandler(sess, conn, 0x0017, 0x0007))) + userfunc(sess, &fr, ""); + + return 0; +} + +/* + * In AIM 3.5 protocol, the first stage of login is to request login from the + * Authorizer, passing it the screen name for verification. If the name is + * invalid, a 0017/0003 is spit back, with the standard error contents. If + * valid, a 0017/0007 comes back, which is the signal to send it the main + * login command (0017/0002). + * + */ +int aim_request_login(aim_session_t *sess, aim_conn_t *conn, const char *sn) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + aim_tlvlist_t *tl = NULL; + + if (!sess || !conn || !sn) + return -EINVAL; + + if ((sn[0] >= '0') && (sn[0] <= '9')) + return goddamnicq(sess, conn, sn); + + sess->flags |= AIM_SESS_FLAGS_SNACLOGIN; + + aim_sendflapver(sess, conn); + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+2+strlen(sn)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0017, 0x0006, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0017, 0x0006, 0x0000, snacid); + + aim_addtlvtochain_raw(&tl, 0x0001, strlen(sn), (guint8 *)sn); + aim_writetlvchain(&fr->data, &tl); + aim_freetlvchain(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Part two of the ICQ hack. Note the ignoring of the key and clientinfo. + */ +static int goddamnicq2(aim_session_t *sess, aim_conn_t *conn, const char *sn, const char *password) +{ + static const char clientstr[] = {"ICQ Inc. - Product of ICQ (TM) 2001b.5.17.1.3642.85"}; + static const char lang[] = {"en"}; + static const char country[] = {"us"}; + aim_frame_t *fr; + aim_tlvlist_t *tl = NULL; + guint8 *password_encoded; + + if (!(password_encoded = (guint8 *) g_malloc(strlen(password)))) + return -ENOMEM; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x01, 1152))) { + g_free(password_encoded); + return -ENOMEM; + } + + aim_encode_password(password, password_encoded); + + aimbs_put32(&fr->data, 0x00000001); + aim_addtlvtochain_raw(&tl, 0x0001, strlen(sn), (guint8 *)sn); + aim_addtlvtochain_raw(&tl, 0x0002, strlen(password), password_encoded); + aim_addtlvtochain_raw(&tl, 0x0003, strlen(clientstr), (guint8 *)clientstr); + aim_addtlvtochain16(&tl, 0x0016, 0x010a); /* cliend ID */ + aim_addtlvtochain16(&tl, 0x0017, 0x0005); /* major version */ + aim_addtlvtochain16(&tl, 0x0018, 0x0011); /* minor version */ + aim_addtlvtochain16(&tl, 0x0019, 0x0001); /* point version */ + aim_addtlvtochain16(&tl, 0x001a, 0x0e3a); /* build */ + aim_addtlvtochain32(&tl, 0x0014, 0x00000055); /* distribution chan */ + aim_addtlvtochain_raw(&tl, 0x000f, strlen(lang), (guint8 *)lang); + aim_addtlvtochain_raw(&tl, 0x000e, strlen(country), (guint8 *)country); + + aim_writetlvchain(&fr->data, &tl); + + g_free(password_encoded); + aim_freetlvchain(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * send_login(int socket, char *sn, char *password) + * + * This is the initial login request packet. + * + * NOTE!! If you want/need to make use of the aim_sendmemblock() function, + * then the client information you send here must exactly match the + * executable that you're pulling the data from. + * + * WinAIM 4.8.2540 + * clientstring = "AOL Instant Messenger (SM), version 4.8.2540/WIN32" + * clientid = 0x0109 + * major = 0x0004 + * minor = 0x0008 + * point = 0x0000 + * build = 0x09ec + * t(0x0014) = 0x000000af + * t(0x004a) = 0x01 + * + * WinAIM 4.3.2188: + * clientstring = "AOL Instant Messenger (SM), version 4.3.2188/WIN32" + * clientid = 0x0109 + * major = 0x0400 + * minor = 0x0003 + * point = 0x0000 + * build = 0x088c + * unknown = 0x00000086 + * lang = "en" + * country = "us" + * unknown4a = 0x01 + * + * Latest WinAIM that libfaim can emulate without server-side buddylists: + * clientstring = "AOL Instant Messenger (SM), version 4.1.2010/WIN32" + * clientid = 0x0004 + * major = 0x0004 + * minor = 0x0001 + * point = 0x0000 + * build = 0x07da + * unknown= 0x0000004b + * + * WinAIM 3.5.1670: + * clientstring = "AOL Instant Messenger (SM), version 3.5.1670/WIN32" + * clientid = 0x0004 + * major = 0x0003 + * minor = 0x0005 + * point = 0x0000 + * build = 0x0686 + * unknown =0x0000002a + * + * Java AIM 1.1.19: + * clientstring = "AOL Instant Messenger (TM) version 1.1.19 for Java built 03/24/98, freeMem 215871 totalMem 1048567, i686, Linus, #2 SMP Sun Feb 11 03:41:17 UTC 2001 2.4.1-ac9, IBM Corporation, 1.1.8, 45.3, Tue Mar 27 12:09:17 PST 2001" + * clientid = 0x0001 + * major = 0x0001 + * minor = 0x0001 + * point = (not sent) + * build = 0x0013 + * unknown= (not sent) + * + * AIM for Linux 1.1.112: + * clientstring = "AOL Instant Messenger (SM)" + * clientid = 0x1d09 + * major = 0x0001 + * minor = 0x0001 + * point = 0x0001 + * build = 0x0070 + * unknown= 0x0000008b + * serverstore = 0x01 + * + */ +int aim_send_login(aim_session_t *sess, aim_conn_t *conn, const char *sn, const char *password, struct client_info_s *ci, const char *key) +{ + aim_frame_t *fr; + aim_tlvlist_t *tl = NULL; + guint8 digest[16]; + aim_snacid_t snacid; + + if (!ci || !sn || !password) + return -EINVAL; + + /* + * What the XORLOGIN flag _really_ means is that its an ICQ login, + * which is really stupid and painful, so its not done here. + * + */ + if (sess->flags & AIM_SESS_FLAGS_XORLOGIN) + return goddamnicq2(sess, conn, sn, password); + + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0017, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0017, 0x0002, 0x0000, snacid); + + aim_addtlvtochain_raw(&tl, 0x0001, strlen(sn), (guint8 *)sn); + + aim_encode_password_md5(password, key, digest); + aim_addtlvtochain_raw(&tl, 0x0025, 16, digest); + + /* + * Newer versions of winaim have an empty type x004c TLV here. + */ + + if (ci->clientstring) + aim_addtlvtochain_raw(&tl, 0x0003, strlen(ci->clientstring), (guint8 *)ci->clientstring); + aim_addtlvtochain16(&tl, 0x0016, (guint16)ci->clientid); + aim_addtlvtochain16(&tl, 0x0017, (guint16)ci->major); + aim_addtlvtochain16(&tl, 0x0018, (guint16)ci->minor); + aim_addtlvtochain16(&tl, 0x0019, (guint16)ci->point); + aim_addtlvtochain16(&tl, 0x001a, (guint16)ci->build); + aim_addtlvtochain_raw(&tl, 0x000e, strlen(ci->country), (guint8 *)ci->country); + aim_addtlvtochain_raw(&tl, 0x000f, strlen(ci->lang), (guint8 *)ci->lang); + + /* + * If set, old-fashioned buddy lists will not work. You will need + * to use SSI. + */ + aim_addtlvtochain8(&tl, 0x004a, 0x01); + + aim_writetlvchain(&fr->data, &tl); + + aim_freetlvchain(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +int aim_encode_password_md5(const char *password, const char *key, guint8 *digest) +{ + md5_state_t state; + + md5_init(&state); + md5_append(&state, (const md5_byte_t *)key, strlen(key)); + md5_append(&state, (const md5_byte_t *)password, strlen(password)); + md5_append(&state, (const md5_byte_t *)AIM_MD5_STRING, strlen(AIM_MD5_STRING)); + md5_finish(&state, (md5_byte_t *)digest); + + return 0; +} + +/** + * aim_encode_password - Encode a password using old XOR method + * @password: incoming password + * @encoded: buffer to put encoded password + * + * This takes a const pointer to a (null terminated) string + * containing the unencoded password. It also gets passed + * an already allocated buffer to store the encoded password. + * This buffer should be the exact length of the password without + * the null. The encoded password buffer /is not %NULL terminated/. + * + * The encoding_table seems to be a fixed set of values. We'll + * hope it doesn't change over time! + * + * This is only used for the XOR method, not the better MD5 method. + * + */ +static int aim_encode_password(const char *password, guint8 *encoded) +{ + guint8 encoding_table[] = { +#if 0 /* old v1 table */ + 0xf3, 0xb3, 0x6c, 0x99, + 0x95, 0x3f, 0xac, 0xb6, + 0xc5, 0xfa, 0x6b, 0x63, + 0x69, 0x6c, 0xc3, 0x9f +#else /* v2.1 table, also works for ICQ */ + 0xf3, 0x26, 0x81, 0xc4, + 0x39, 0x86, 0xdb, 0x92, + 0x71, 0xa3, 0xb9, 0xe6, + 0x53, 0x7a, 0x95, 0x7c +#endif + }; + int i; + + for (i = 0; i < strlen(password); i++) + encoded[i] = (password[i] ^ encoding_table[i]); + + return 0; +} + +/* + * This is sent back as a general response to the login command. + * It can be either an error or a success, depending on the + * precense of certain TLVs. + * + * The client should check the value passed as errorcode. If + * its nonzero, there was an error. + * + */ +static int parse(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_tlvlist_t *tlvlist; + aim_rxcallback_t userfunc; + struct aim_authresp_info info; + int ret = 0; + + memset(&info, 0, sizeof(info)); + + /* + * Read block of TLVs. All further data is derived + * from what is parsed here. + */ + tlvlist = aim_readtlvchain(bs); + + /* + * No matter what, we should have a screen name. + */ + memset(sess->sn, 0, sizeof(sess->sn)); + if (aim_gettlv(tlvlist, 0x0001, 1)) { + info.sn = aim_gettlv_str(tlvlist, 0x0001, 1); + strncpy(sess->sn, info.sn, sizeof(sess->sn)); + } + + /* + * Check for an error code. If so, we should also + * have an error url. + */ + if (aim_gettlv(tlvlist, 0x0008, 1)) + info.errorcode = aim_gettlv16(tlvlist, 0x0008, 1); + if (aim_gettlv(tlvlist, 0x0004, 1)) + info.errorurl = aim_gettlv_str(tlvlist, 0x0004, 1); + + /* + * BOS server address. + */ + if (aim_gettlv(tlvlist, 0x0005, 1)) + info.bosip = aim_gettlv_str(tlvlist, 0x0005, 1); + + /* + * Authorization cookie. + */ + if (aim_gettlv(tlvlist, 0x0006, 1)) { + aim_tlv_t *tmptlv; + + tmptlv = aim_gettlv(tlvlist, 0x0006, 1); + + info.cookie = tmptlv->value; + } + + /* + * The email address attached to this account + * Not available for ICQ logins. + */ + if (aim_gettlv(tlvlist, 0x0011, 1)) + info.email = aim_gettlv_str(tlvlist, 0x0011, 1); + + /* + * The registration status. (Not real sure what it means.) + * Not available for ICQ logins. + * + * 1 = No disclosure + * 2 = Limited disclosure + * 3 = Full disclosure + * + * This has to do with whether your email address is available + * to other users or not. AFAIK, this feature is no longer used. + * + */ + if (aim_gettlv(tlvlist, 0x0013, 1)) + info.regstatus = aim_gettlv16(tlvlist, 0x0013, 1); + + if (aim_gettlv(tlvlist, 0x0040, 1)) + info.latestbeta.build = aim_gettlv32(tlvlist, 0x0040, 1); + if (aim_gettlv(tlvlist, 0x0041, 1)) + info.latestbeta.url = aim_gettlv_str(tlvlist, 0x0041, 1); + if (aim_gettlv(tlvlist, 0x0042, 1)) + info.latestbeta.info = aim_gettlv_str(tlvlist, 0x0042, 1); + if (aim_gettlv(tlvlist, 0x0043, 1)) + info.latestbeta.name = aim_gettlv_str(tlvlist, 0x0043, 1); + if (aim_gettlv(tlvlist, 0x0048, 1)) + ; /* no idea what this is */ + + if (aim_gettlv(tlvlist, 0x0044, 1)) + info.latestrelease.build = aim_gettlv32(tlvlist, 0x0044, 1); + if (aim_gettlv(tlvlist, 0x0045, 1)) + info.latestrelease.url = aim_gettlv_str(tlvlist, 0x0045, 1); + if (aim_gettlv(tlvlist, 0x0046, 1)) + info.latestrelease.info = aim_gettlv_str(tlvlist, 0x0046, 1); + if (aim_gettlv(tlvlist, 0x0047, 1)) + info.latestrelease.name = aim_gettlv_str(tlvlist, 0x0047, 1); + if (aim_gettlv(tlvlist, 0x0049, 1)) + ; /* no idea what this is */ + + + if ((userfunc = aim_callhandler(sess, rx->conn, snac ? snac->family : 0x0017, snac ? snac->subtype : 0x0003))) + ret = userfunc(sess, rx, &info); + + g_free(info.sn); + g_free(info.bosip); + g_free(info.errorurl); + g_free(info.email); + g_free(info.latestrelease.name); + g_free(info.latestrelease.url); + g_free(info.latestrelease.info); + g_free(info.latestbeta.name); + g_free(info.latestbeta.url); + g_free(info.latestbeta.info); + + aim_freetlvchain(&tlvlist); + + return ret; +} + +/* + * Middle handler for 0017/0007 SNACs. Contains the auth key prefixed + * by only its length in a two byte word. + * + * Calls the client, which should then use the value to call aim_send_login. + * + */ +static int keyparse(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int keylen, ret = 1; + aim_rxcallback_t userfunc; + char *keystr; + + keylen = aimbs_get16(bs); + keystr = aimbs_getstr(bs, keylen); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, keystr); + + g_free(keystr); + + return ret; +} + +static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + + if (snac->subtype == 0x0003) + return parse(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0007) + return keyparse(sess, mod, rx, snac, bs); + + return 0; +} + +int auth_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x0017; + mod->version = 0x0000; + mod->flags = 0; + strncpy(mod->name, "auth", sizeof(mod->name)); + mod->snachandler = snachandler; + + return 0; +} + diff --git a/protocols/oscar/bos.c b/protocols/oscar/bos.c new file mode 100644 index 00000000..e7f12f79 --- /dev/null +++ b/protocols/oscar/bos.c @@ -0,0 +1,161 @@ +#include <aim.h> +#include "bos.h" + +/* Request BOS rights (group 9, type 2) */ +int aim_bos_reqrights(aim_session_t *sess, aim_conn_t *conn) +{ + return aim_genericreq_n(sess, conn, 0x0009, 0x0002); +} + +/* BOS Rights (group 9, type 3) */ +static int rights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_rxcallback_t userfunc; + aim_tlvlist_t *tlvlist; + guint16 maxpermits = 0, maxdenies = 0; + int ret = 0; + + /* + * TLVs follow + */ + tlvlist = aim_readtlvchain(bs); + + /* + * TLV type 0x0001: Maximum number of buddies on permit list. + */ + if (aim_gettlv(tlvlist, 0x0001, 1)) + maxpermits = aim_gettlv16(tlvlist, 0x0001, 1); + + /* + * TLV type 0x0002: Maximum number of buddies on deny list. + */ + if (aim_gettlv(tlvlist, 0x0002, 1)) + maxdenies = aim_gettlv16(tlvlist, 0x0002, 1); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, maxpermits, maxdenies); + + aim_freetlvchain(&tlvlist); + + return ret; +} + +/* + * Set group permisson mask (group 9, type 4) + * + * Normally 0x1f (all classes). + * + * The group permission mask allows you to keep users of a certain + * class or classes from talking to you. The mask should be + * a bitwise OR of all the user classes you want to see you. + * + */ +int aim_bos_setgroupperm(aim_session_t *sess, aim_conn_t *conn, guint32 mask) +{ + return aim_genericreq_l(sess, conn, 0x0009, 0x0004, &mask); +} + +/* + * Modify permit/deny lists (group 9, types 5, 6, 7, and 8) + * + * Changes your visibility depending on changetype: + * + * AIM_VISIBILITYCHANGE_PERMITADD: Lets provided list of names see you + * AIM_VISIBILITYCHANGE_PERMIDREMOVE: Removes listed names from permit list + * AIM_VISIBILITYCHANGE_DENYADD: Hides you from provided list of names + * AIM_VISIBILITYCHANGE_DENYREMOVE: Lets list see you again + * + * list should be a list of + * screen names in the form "Screen Name One&ScreenNameTwo&" etc. + * + * Equivelents to options in WinAIM: + * - Allow all users to contact me: Send an AIM_VISIBILITYCHANGE_DENYADD + * with only your name on it. + * - Allow only users on my Buddy List: Send an + * AIM_VISIBILITYCHANGE_PERMITADD with the list the same as your + * buddy list + * - Allow only the uesrs below: Send an AIM_VISIBILITYCHANGE_PERMITADD + * with everyone listed that you want to see you. + * - Block all users: Send an AIM_VISIBILITYCHANGE_PERMITADD with only + * yourself in the list + * - Block the users below: Send an AIM_VISIBILITYCHANGE_DENYADD with + * the list of users to be blocked + * + * XXX ye gods. + */ +int aim_bos_changevisibility(aim_session_t *sess, aim_conn_t *conn, int changetype, const char *denylist) +{ + aim_frame_t *fr; + int packlen = 0; + guint16 subtype; + char *localcpy = NULL, *tmpptr = NULL; + int i; + int listcount; + aim_snacid_t snacid; + + if (!denylist) + return -EINVAL; + + if (changetype == AIM_VISIBILITYCHANGE_PERMITADD) + subtype = 0x05; + else if (changetype == AIM_VISIBILITYCHANGE_PERMITREMOVE) + subtype = 0x06; + else if (changetype == AIM_VISIBILITYCHANGE_DENYADD) + subtype = 0x07; + else if (changetype == AIM_VISIBILITYCHANGE_DENYREMOVE) + subtype = 0x08; + else + return -EINVAL; + + localcpy = g_strdup(denylist); + + listcount = aimutil_itemcnt(localcpy, '&'); + packlen = aimutil_tokslen(localcpy, 99, '&') + listcount + 9; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, packlen))) { + g_free(localcpy); + return -ENOMEM; + } + + snacid = aim_cachesnac(sess, 0x0009, subtype, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0009, subtype, 0x00, snacid); + + for (i = 0; (i < (listcount - 1)) && (i < 99); i++) { + tmpptr = aimutil_itemidx(localcpy, i, '&'); + + aimbs_put8(&fr->data, strlen(tmpptr)); + aimbs_putraw(&fr->data, (guint8 *)tmpptr, strlen(tmpptr)); + + g_free(tmpptr); + } + g_free(localcpy); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + + if (snac->subtype == 0x0003) + return rights(sess, mod, rx, snac, bs); + + return 0; +} + +int bos_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x0009; + mod->version = 0x0001; + mod->toolid = 0x0110; + mod->toolversion = 0x0629; + mod->flags = 0; + strncpy(mod->name, "bos", sizeof(mod->name)); + mod->snachandler = snachandler; + + return 0; +} + + diff --git a/protocols/oscar/bos.h b/protocols/oscar/bos.h new file mode 100644 index 00000000..e7c2cbcd --- /dev/null +++ b/protocols/oscar/bos.h @@ -0,0 +1,14 @@ +#ifndef __OSCAR_BOS_H__ +#define __OSCAR_BOS_H__ + +#define AIM_CB_FAM_BOS 0x0009 + +/* + * SNAC Family: Misc BOS Services. + */ +#define AIM_CB_BOS_ERROR 0x0001 +#define AIM_CB_BOS_RIGHTSQUERY 0x0002 +#define AIM_CB_BOS_RIGHTS 0x0003 +#define AIM_CB_BOS_DEFAULT 0xffff + +#endif /* __OSCAR_BOS_H__ */ diff --git a/protocols/oscar/buddylist.c b/protocols/oscar/buddylist.c new file mode 100644 index 00000000..a7b461f2 --- /dev/null +++ b/protocols/oscar/buddylist.c @@ -0,0 +1,150 @@ +#include <aim.h> +#include "buddylist.h" + +/* + * Oncoming Buddy notifications contain a subset of the + * user information structure. Its close enough to run + * through aim_extractuserinfo() however. + * + * Although the offgoing notification contains no information, + * it is still in a format parsable by extractuserinfo. + * + */ +static int buddychange(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_userinfo_t userinfo; + aim_rxcallback_t userfunc; + + aim_extractuserinfo(sess, bs, &userinfo); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + return userfunc(sess, rx, &userinfo); + + return 0; +} + +static int rights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_rxcallback_t userfunc; + aim_tlvlist_t *tlvlist; + guint16 maxbuddies = 0, maxwatchers = 0; + int ret = 0; + + /* + * TLVs follow + */ + tlvlist = aim_readtlvchain(bs); + + /* + * TLV type 0x0001: Maximum number of buddies. + */ + if (aim_gettlv(tlvlist, 0x0001, 1)) + maxbuddies = aim_gettlv16(tlvlist, 0x0001, 1); + + /* + * TLV type 0x0002: Maximum number of watchers. + * + * Watchers are other users who have you on their buddy + * list. (This is called the "reverse list" by a certain + * other IM protocol.) + * + */ + if (aim_gettlv(tlvlist, 0x0002, 1)) + maxwatchers = aim_gettlv16(tlvlist, 0x0002, 1); + + /* + * TLV type 0x0003: Unknown. + * + * ICQ only? + */ + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, maxbuddies, maxwatchers); + + aim_freetlvchain(&tlvlist); + + return ret; +} + +static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + + if (snac->subtype == 0x0003) + return rights(sess, mod, rx, snac, bs); + else if ((snac->subtype == 0x000b) || (snac->subtype == 0x000c)) + return buddychange(sess, mod, rx, snac, bs); + + return 0; +} + +int buddylist_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x0003; + mod->version = 0x0001; + mod->toolid = 0x0110; + mod->toolversion = 0x0629; + mod->flags = 0; + strncpy(mod->name, "buddylist", sizeof(mod->name)); + mod->snachandler = snachandler; + + return 0; +} + +/* + * aim_add_buddy() + * + * Adds a single buddy to your buddy list after login. + * + * XXX this should just be an extension of setbuddylist() + * + */ +int aim_add_buddy(aim_session_t *sess, aim_conn_t *conn, const char *sn) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sn || !strlen(sn)) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0003, 0x0004, 0x0000, sn, strlen(sn)+1); + aim_putsnac(&fr->data, 0x0003, 0x0004, 0x0000, snacid); + + aimbs_put8(&fr->data, strlen(sn)); + aimbs_putraw(&fr->data, (guint8 *)sn, strlen(sn)); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * XXX generalise to support removing multiple buddies (basically, its + * the same as setbuddylist() but with a different snac subtype). + * + */ +int aim_remove_buddy(aim_session_t *sess, aim_conn_t *conn, const char *sn) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sn || !strlen(sn)) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0003, 0x0005, 0x0000, sn, strlen(sn)+1); + aim_putsnac(&fr->data, 0x0003, 0x0005, 0x0000, snacid); + + aimbs_put8(&fr->data, strlen(sn)); + aimbs_putraw(&fr->data, (guint8 *)sn, strlen(sn)); + + aim_tx_enqueue(sess, fr); + + return 0; +} + diff --git a/protocols/oscar/buddylist.h b/protocols/oscar/buddylist.h new file mode 100644 index 00000000..9a325279 --- /dev/null +++ b/protocols/oscar/buddylist.h @@ -0,0 +1,23 @@ +#ifndef __OSCAR_BUDDYLIST_H__ +#define __OSCAR_BUDDYLIST_H__ + +#define AIM_CB_FAM_BUD 0x0003 + +/* + * SNAC Family: Buddy List Management Services. + */ +#define AIM_CB_BUD_ERROR 0x0001 +#define AIM_CB_BUD_REQRIGHTS 0x0002 +#define AIM_CB_BUD_RIGHTSINFO 0x0003 +#define AIM_CB_BUD_ADDBUDDY 0x0004 +#define AIM_CB_BUD_REMBUDDY 0x0005 +#define AIM_CB_BUD_REJECT 0x000a +#define AIM_CB_BUD_ONCOMING 0x000b +#define AIM_CB_BUD_OFFGOING 0x000c +#define AIM_CB_BUD_DEFAULT 0xffff + +/* aim_buddylist.c */ +int aim_add_buddy(aim_session_t *, aim_conn_t *, const char *); +int aim_remove_buddy(aim_session_t *, aim_conn_t *, const char *); + +#endif /* __OSCAR_BUDDYLIST_H__ */ diff --git a/protocols/oscar/chat.c b/protocols/oscar/chat.c new file mode 100644 index 00000000..60aabc79 --- /dev/null +++ b/protocols/oscar/chat.c @@ -0,0 +1,713 @@ +/* + * aim_chat.c + * + * Routines for the Chat service. + * + */ + +#include <aim.h> +#include <glib.h> +#include "info.h" + +/* Stored in the ->priv of chat connections */ +struct chatconnpriv { + guint16 exchange; + char *name; + guint16 instance; +}; + +void aim_conn_kill_chat(aim_session_t *sess, aim_conn_t *conn) +{ + struct chatconnpriv *ccp = (struct chatconnpriv *)conn->priv; + + if (ccp) + g_free(ccp->name); + g_free(ccp); + + return; +} + +char *aim_chat_getname(aim_conn_t *conn) +{ + struct chatconnpriv *ccp; + + if (!conn) + return NULL; + + if (conn->type != AIM_CONN_TYPE_CHAT) + return NULL; + + ccp = (struct chatconnpriv *)conn->priv; + + return ccp->name; +} + +/* XXX get this into conn.c -- evil!! */ +aim_conn_t *aim_chat_getconn(aim_session_t *sess, const char *name) +{ + aim_conn_t *cur; + + for (cur = sess->connlist; cur; cur = cur->next) { + struct chatconnpriv *ccp = (struct chatconnpriv *)cur->priv; + + if (cur->type != AIM_CONN_TYPE_CHAT) + continue; + if (!cur->priv) { + do_error_dialog(sess->aux_data, "chat connection with no name!", "Gaim"); + continue; + } + + if (strcmp(ccp->name, name) == 0) + break; + } + + return cur; +} + +int aim_chat_attachname(aim_conn_t *conn, guint16 exchange, const char *roomname, guint16 instance) +{ + struct chatconnpriv *ccp; + + if (!conn || !roomname) + return -EINVAL; + + if (conn->priv) + g_free(conn->priv); + + if (!(ccp = g_malloc(sizeof(struct chatconnpriv)))) + return -ENOMEM; + + ccp->exchange = exchange; + ccp->name = g_strdup(roomname); + ccp->instance = instance; + + conn->priv = (void *)ccp; + + return 0; +} + +/* + * Send a Chat Message. + * + * Possible flags: + * AIM_CHATFLAGS_NOREFLECT -- Unset the flag that requests messages + * should be sent to their sender. + * AIM_CHATFLAGS_AWAY -- Mark the message as an autoresponse + * (Note that WinAIM does not honor this, + * and displays the message as normal.) + * + * XXX convert this to use tlvchains + */ +int aim_chat_send_im(aim_session_t *sess, aim_conn_t *conn, guint16 flags, const char *msg, int msglen) +{ + int i; + aim_frame_t *fr; + aim_msgcookie_t *cookie; + aim_snacid_t snacid; + guint8 ckstr[8]; + aim_tlvlist_t *otl = NULL, *itl = NULL; + + if (!sess || !conn || !msg || (msglen <= 0)) + return 0; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x000e, 0x0005, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x000e, 0x0005, 0x0000, snacid); + + + /* + * Generate a random message cookie. + * + * XXX mkcookie should generate the cookie and cache it in one + * operation to preserve uniqueness. + * + */ + for (i = 0; i < sizeof(ckstr); i++) + aimutil_put8(ckstr+i, (guint8) rand()); + + cookie = aim_mkcookie(ckstr, AIM_COOKIETYPE_CHAT, NULL); + cookie->data = NULL; /* XXX store something useful here */ + + aim_cachecookie(sess, cookie); + + for (i = 0; i < sizeof(ckstr); i++) + aimbs_put8(&fr->data, ckstr[i]); + + + /* + * Channel ID. + */ + aimbs_put16(&fr->data, 0x0003); + + + /* + * Type 1: Flag meaning this message is destined to the room. + */ + aim_addtlvtochain_noval(&otl, 0x0001); + + /* + * Type 6: Reflect + */ + if (!(flags & AIM_CHATFLAGS_NOREFLECT)) + aim_addtlvtochain_noval(&otl, 0x0006); + + /* + * Type 7: Autoresponse + */ + if (flags & AIM_CHATFLAGS_AWAY) + aim_addtlvtochain_noval(&otl, 0x0007); + + /* + * SubTLV: Type 1: Message + */ + aim_addtlvtochain_raw(&itl, 0x0001, strlen(msg), (guint8 *)msg); + + /* + * Type 5: Message block. Contains more TLVs. + * + * This could include other information... We just + * put in a message TLV however. + * + */ + aim_addtlvtochain_frozentlvlist(&otl, 0x0005, &itl); + + aim_writetlvchain(&fr->data, &otl); + + aim_freetlvchain(&itl); + aim_freetlvchain(&otl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +static int aim_addtlvtochain_chatroom(aim_tlvlist_t **list, guint16 type, guint16 exchange, const char *roomname, guint16 instance) +{ + guint8 *buf; + int buflen; + aim_bstream_t bs; + + buflen = 2 + 1 + strlen(roomname) + 2; + + if (!(buf = g_malloc(buflen))) + return 0; + + aim_bstream_init(&bs, buf, buflen); + + aimbs_put16(&bs, exchange); + aimbs_put8(&bs, strlen(roomname)); + aimbs_putraw(&bs, (guint8 *)roomname, strlen(roomname)); + aimbs_put16(&bs, instance); + + aim_addtlvtochain_raw(list, type, aim_bstream_curpos(&bs), buf); + + g_free(buf); + + return 0; +} + +/* + * Join a room of name roomname. This is the first step to joining an + * already created room. It's basically a Service Request for + * family 0x000e, with a little added on to specify the exchange and room + * name. + */ +int aim_chat_join(aim_session_t *sess, aim_conn_t *conn, guint16 exchange, const char *roomname, guint16 instance) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + aim_tlvlist_t *tl = NULL; + struct chatsnacinfo csi; + + if (!sess || !conn || !roomname || !strlen(roomname)) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 512))) + return -ENOMEM; + + memset(&csi, 0, sizeof(csi)); + csi.exchange = exchange; + strncpy(csi.name, roomname, sizeof(csi.name)); + csi.instance = instance; + + snacid = aim_cachesnac(sess, 0x0001, 0x0004, 0x0000, &csi, sizeof(csi)); + aim_putsnac(&fr->data, 0x0001, 0x0004, 0x0000, snacid); + + /* + * Requesting service chat (0x000e) + */ + aimbs_put16(&fr->data, 0x000e); + + aim_addtlvtochain_chatroom(&tl, 0x0001, exchange, roomname, instance); + aim_writetlvchain(&fr->data, &tl); + aim_freetlvchain(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +int aim_chat_readroominfo(aim_bstream_t *bs, struct aim_chat_roominfo *outinfo) +{ + int namelen; + + if (!bs || !outinfo) + return 0; + + outinfo->exchange = aimbs_get16(bs); + namelen = aimbs_get8(bs); + outinfo->name = aimbs_getstr(bs, namelen); + outinfo->instance = aimbs_get16(bs); + + return 0; +} + +int aim_chat_leaveroom(aim_session_t *sess, const char *name) +{ + aim_conn_t *conn; + + if (!(conn = aim_chat_getconn(sess, name))) + return -ENOENT; + + aim_conn_close(conn); + + return 0; +} + +/* + * conn must be a BOS connection! + */ +int aim_chat_invite(aim_session_t *sess, aim_conn_t *conn, const char *sn, const char *msg, guint16 exchange, const char *roomname, guint16 instance) +{ + int i; + aim_frame_t *fr; + aim_msgcookie_t *cookie; + struct aim_invite_priv *priv; + guint8 ckstr[8]; + aim_snacid_t snacid; + aim_tlvlist_t *otl = NULL, *itl = NULL; + guint8 *hdr; + int hdrlen; + aim_bstream_t hdrbs; + + if (!sess || !conn || !sn || !msg || !roomname) + return -EINVAL; + + if (conn->type != AIM_CONN_TYPE_BOS) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152+strlen(sn)+strlen(roomname)+strlen(msg)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, sn, strlen(sn)+1); + aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid); + + + /* + * Cookie + */ + for (i = 0; i < sizeof(ckstr); i++) + aimutil_put8(ckstr, (guint8) rand()); + + /* XXX should be uncached by an unwritten 'invite accept' handler */ + if ((priv = g_malloc(sizeof(struct aim_invite_priv)))) { + priv->sn = g_strdup(sn); + priv->roomname = g_strdup(roomname); + priv->exchange = exchange; + priv->instance = instance; + } + + if ((cookie = aim_mkcookie(ckstr, AIM_COOKIETYPE_INVITE, priv))) + aim_cachecookie(sess, cookie); + else + g_free(priv); + + for (i = 0; i < sizeof(ckstr); i++) + aimbs_put8(&fr->data, ckstr[i]); + + + /* + * Channel (2) + */ + aimbs_put16(&fr->data, 0x0002); + + /* + * Dest sn + */ + aimbs_put8(&fr->data, strlen(sn)); + aimbs_putraw(&fr->data, (guint8 *)sn, strlen(sn)); + + /* + * TLV t(0005) + * + * Everything else is inside this TLV. + * + * Sigh. AOL was rather inconsistent right here. So we have + * to play some minor tricks. Right inside the type 5 is some + * raw data, followed by a series of TLVs. + * + */ + hdrlen = 2+8+16+6+4+4+strlen(msg)+4+2+1+strlen(roomname)+2; + hdr = g_malloc(hdrlen); + aim_bstream_init(&hdrbs, hdr, hdrlen); + + aimbs_put16(&hdrbs, 0x0000); /* Unknown! */ + aimbs_putraw(&hdrbs, ckstr, sizeof(ckstr)); /* I think... */ + aim_putcap(&hdrbs, AIM_CAPS_CHAT); + + aim_addtlvtochain16(&itl, 0x000a, 0x0001); + aim_addtlvtochain_noval(&itl, 0x000f); + aim_addtlvtochain_raw(&itl, 0x000c, strlen(msg), (guint8 *)msg); + aim_addtlvtochain_chatroom(&itl, 0x2711, exchange, roomname, instance); + aim_writetlvchain(&hdrbs, &itl); + + aim_addtlvtochain_raw(&otl, 0x0005, aim_bstream_curpos(&hdrbs), hdr); + + aim_writetlvchain(&fr->data, &otl); + + g_free(hdr); + aim_freetlvchain(&itl); + aim_freetlvchain(&otl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * General room information. Lots of stuff. + * + * Values I know are in here but I havent attached + * them to any of the 'Unknown's: + * - Language (English) + * + * SNAC 000e/0002 + */ +static int infoupdate(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_userinfo_t *userinfo = NULL; + aim_rxcallback_t userfunc; + int ret = 0; + int usercount = 0; + guint8 detaillevel = 0; + char *roomname = NULL; + struct aim_chat_roominfo roominfo; + guint16 tlvcount = 0; + aim_tlvlist_t *tlvlist; + char *roomdesc = NULL; + guint16 flags = 0; + guint32 creationtime = 0; + guint16 maxmsglen = 0, maxvisiblemsglen = 0; + guint16 unknown_d2 = 0, unknown_d5 = 0; + + aim_chat_readroominfo(bs, &roominfo); + + detaillevel = aimbs_get8(bs); + + if (detaillevel != 0x02) { + do_error_dialog(sess->aux_data, "Only detaillevel 0x2 is support at the moment", "Gaim"); + return 1; + } + + tlvcount = aimbs_get16(bs); + + /* + * Everything else are TLVs. + */ + tlvlist = aim_readtlvchain(bs); + + /* + * TLV type 0x006a is the room name in Human Readable Form. + */ + if (aim_gettlv(tlvlist, 0x006a, 1)) + roomname = aim_gettlv_str(tlvlist, 0x006a, 1); + + /* + * Type 0x006f: Number of occupants. + */ + if (aim_gettlv(tlvlist, 0x006f, 1)) + usercount = aim_gettlv16(tlvlist, 0x006f, 1); + + /* + * Type 0x0073: Occupant list. + */ + if (aim_gettlv(tlvlist, 0x0073, 1)) { + int curoccupant = 0; + aim_tlv_t *tmptlv; + aim_bstream_t occbs; + + tmptlv = aim_gettlv(tlvlist, 0x0073, 1); + + /* Allocate enough userinfo structs for all occupants */ + userinfo = g_new0(aim_userinfo_t, usercount); + + aim_bstream_init(&occbs, tmptlv->value, tmptlv->length); + + while (curoccupant < usercount) + aim_extractuserinfo(sess, &occbs, &userinfo[curoccupant++]); + } + + /* + * Type 0x00c9: Flags. (AIM_CHATROOM_FLAG) + */ + if (aim_gettlv(tlvlist, 0x00c9, 1)) + flags = aim_gettlv16(tlvlist, 0x00c9, 1); + + /* + * Type 0x00ca: Creation time (4 bytes) + */ + if (aim_gettlv(tlvlist, 0x00ca, 1)) + creationtime = aim_gettlv32(tlvlist, 0x00ca, 1); + + /* + * Type 0x00d1: Maximum Message Length + */ + if (aim_gettlv(tlvlist, 0x00d1, 1)) + maxmsglen = aim_gettlv16(tlvlist, 0x00d1, 1); + + /* + * Type 0x00d2: Unknown. (2 bytes) + */ + if (aim_gettlv(tlvlist, 0x00d2, 1)) + unknown_d2 = aim_gettlv16(tlvlist, 0x00d2, 1); + + /* + * Type 0x00d3: Room Description + */ + if (aim_gettlv(tlvlist, 0x00d3, 1)) + roomdesc = aim_gettlv_str(tlvlist, 0x00d3, 1); + + /* + * Type 0x000d4: Unknown (flag only) + */ + if (aim_gettlv(tlvlist, 0x000d4, 1)) + ; + + /* + * Type 0x00d5: Unknown. (1 byte) + */ + if (aim_gettlv(tlvlist, 0x00d5, 1)) + unknown_d5 = aim_gettlv8(tlvlist, 0x00d5, 1); + + + /* + * Type 0x00d6: Encoding 1 ("us-ascii") + */ + if (aim_gettlv(tlvlist, 0x000d6, 1)) + ; + + /* + * Type 0x00d7: Language 1 ("en") + */ + if (aim_gettlv(tlvlist, 0x000d7, 1)) + ; + + /* + * Type 0x00d8: Encoding 2 ("us-ascii") + */ + if (aim_gettlv(tlvlist, 0x000d8, 1)) + ; + + /* + * Type 0x00d9: Language 2 ("en") + */ + if (aim_gettlv(tlvlist, 0x000d9, 1)) + ; + + /* + * Type 0x00da: Maximum visible message length + */ + if (aim_gettlv(tlvlist, 0x000da, 1)) + maxvisiblemsglen = aim_gettlv16(tlvlist, 0x00da, 1); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) { + ret = userfunc(sess, + rx, + &roominfo, + roomname, + usercount, + userinfo, + roomdesc, + flags, + creationtime, + maxmsglen, + unknown_d2, + unknown_d5, + maxvisiblemsglen); + } + + g_free(roominfo.name); + g_free(userinfo); + g_free(roomname); + g_free(roomdesc); + aim_freetlvchain(&tlvlist); + + return ret; +} + +static int userlistchange(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_userinfo_t *userinfo = NULL; + aim_rxcallback_t userfunc; + int curcount = 0, ret = 0; + + while (aim_bstream_empty(bs)) { + curcount++; + userinfo = g_realloc(userinfo, curcount * sizeof(aim_userinfo_t)); + aim_extractuserinfo(sess, bs, &userinfo[curcount-1]); + } + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, curcount, userinfo); + + g_free(userinfo); + + return ret; +} + +/* + * We could probably include this in the normal ICBM parsing + * code as channel 0x0003, however, since only the start + * would be the same, we might as well do it here. + * + * General outline of this SNAC: + * snac + * cookie + * channel id + * tlvlist + * unknown + * source user info + * name + * evility + * userinfo tlvs + * online time + * etc + * message metatlv + * message tlv + * message string + * possibly others + * + */ +static int incomingmsg(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_userinfo_t userinfo; + aim_rxcallback_t userfunc; + int ret = 0; + guint8 *cookie; + guint16 channel; + aim_tlvlist_t *otl; + char *msg = NULL; + aim_msgcookie_t *ck; + + memset(&userinfo, 0, sizeof(aim_userinfo_t)); + + /* + * ICBM Cookie. Uncache it. + */ + cookie = aimbs_getraw(bs, 8); + + if ((ck = aim_uncachecookie(sess, cookie, AIM_COOKIETYPE_CHAT))) { + g_free(ck->data); + g_free(ck); + } + + /* + * Channel ID + * + * Channels 1 and 2 are implemented in the normal ICBM + * parser. + * + * We only do channel 3 here. + * + */ + channel = aimbs_get16(bs); + + if (channel != 0x0003) { + do_error_dialog(sess->aux_data, "unknown channel!", "Gaim"); + return 0; + } + + /* + * Start parsing TLVs right away. + */ + otl = aim_readtlvchain(bs); + + /* + * Type 0x0003: Source User Information + */ + if (aim_gettlv(otl, 0x0003, 1)) { + aim_tlv_t *userinfotlv; + aim_bstream_t tbs; + + userinfotlv = aim_gettlv(otl, 0x0003, 1); + + aim_bstream_init(&tbs, userinfotlv->value, userinfotlv->length); + aim_extractuserinfo(sess, &tbs, &userinfo); + } + + /* + * Type 0x0001: If present, it means it was a message to the + * room (as opposed to a whisper). + */ + if (aim_gettlv(otl, 0x0001, 1)) + ; + + /* + * Type 0x0005: Message Block. Conains more TLVs. + */ + if (aim_gettlv(otl, 0x0005, 1)) { + aim_tlvlist_t *itl; + aim_tlv_t *msgblock; + aim_bstream_t tbs; + + msgblock = aim_gettlv(otl, 0x0005, 1); + aim_bstream_init(&tbs, msgblock->value, msgblock->length); + itl = aim_readtlvchain(&tbs); + + /* + * Type 0x0001: Message. + */ + if (aim_gettlv(itl, 0x0001, 1)) + msg = aim_gettlv_str(itl, 0x0001, 1); + + aim_freetlvchain(&itl); + } + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, &userinfo, msg); + + g_free(cookie); + g_free(msg); + aim_freetlvchain(&otl); + + return ret; +} + +static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + + if (snac->subtype == 0x0002) + return infoupdate(sess, mod, rx, snac, bs); + else if ((snac->subtype == 0x0003) || (snac->subtype == 0x0004)) + return userlistchange(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0006) + return incomingmsg(sess, mod, rx, snac, bs); + + return 0; +} + +int chat_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x000e; + mod->version = 0x0001; + mod->toolid = 0x0010; + mod->toolversion = 0x0629; + mod->flags = 0; + strncpy(mod->name, "chat", sizeof(mod->name)); + mod->snachandler = snachandler; + + return 0; +} diff --git a/protocols/oscar/chat.h b/protocols/oscar/chat.h new file mode 100644 index 00000000..6b360326 --- /dev/null +++ b/protocols/oscar/chat.h @@ -0,0 +1,17 @@ +#ifndef __OSCAR_CHAT_H__ +#define __OSCAR_CHAT_H__ + +#define AIM_CB_FAM_CHT 0x000e /* Chat */ + +/* + * SNAC Family: Chat Services + */ +#define AIM_CB_CHT_ERROR 0x0001 +#define AIM_CB_CHT_ROOMINFOUPDATE 0x0002 +#define AIM_CB_CHT_USERJOIN 0x0003 +#define AIM_CB_CHT_USERLEAVE 0x0004 +#define AIM_CB_CHT_OUTGOINGMSG 0x0005 +#define AIM_CB_CHT_INCOMINGMSG 0x0006 +#define AIM_CB_CHT_DEFAULT 0xffff + +#endif /* __OSCAR_CHAT_H__ */ diff --git a/protocols/oscar/chatnav.c b/protocols/oscar/chatnav.c new file mode 100644 index 00000000..c7e11765 --- /dev/null +++ b/protocols/oscar/chatnav.c @@ -0,0 +1,421 @@ +/* + * Handle ChatNav. + * + * [The ChatNav(igation) service does various things to keep chat + * alive. It provides room information, room searching and creating, + * as well as giving users the right ("permission") to use chat.] + * + */ + +#include <aim.h> +#include "chatnav.h" + +/* + * conn must be a chatnav connection! + */ +int aim_chatnav_reqrights(aim_session_t *sess, aim_conn_t *conn) +{ + return aim_genericreq_n_snacid(sess, conn, 0x000d, 0x0002); +} + +int aim_chatnav_createroom(aim_session_t *sess, aim_conn_t *conn, const char *name, guint16 exchange) +{ + static const char ck[] = {"create"}; + static const char lang[] = {"en"}; + static const char charset[] = {"us-ascii"}; + aim_frame_t *fr; + aim_snacid_t snacid; + aim_tlvlist_t *tl = NULL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x000d, 0x0008, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x000d, 0x0008, 0x0000, snacid); + + /* exchange */ + aimbs_put16(&fr->data, exchange); + + /* + * This looks to be a big hack. You'll note that this entire + * SNAC is just a room info structure, but the hard room name, + * here, is set to "create". + * + * Either this goes on the "list of questions concerning + * why-the-hell-did-you-do-that", or this value is completly + * ignored. Without experimental evidence, but a good knowledge of + * AOL style, I'm going to guess that it is the latter, and that + * the value of the room name in create requests is ignored. + */ + aimbs_put8(&fr->data, strlen(ck)); + aimbs_putraw(&fr->data, (guint8 *)ck, strlen(ck)); + + /* + * instance + * + * Setting this to 0xffff apparently assigns the last instance. + * + */ + aimbs_put16(&fr->data, 0xffff); + + /* detail level */ + aimbs_put8(&fr->data, 0x01); + + aim_addtlvtochain_raw(&tl, 0x00d3, strlen(name), (guint8 *)name); + aim_addtlvtochain_raw(&tl, 0x00d6, strlen(charset), (guint8 *)charset); + aim_addtlvtochain_raw(&tl, 0x00d7, strlen(lang), (guint8 *)lang); + + /* tlvcount */ + aimbs_put16(&fr->data, aim_counttlvchain(&tl)); + aim_writetlvchain(&fr->data, &tl); + + aim_freetlvchain(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +static int parseinfo_perms(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs, aim_snac_t *snac2) +{ + aim_rxcallback_t userfunc; + int ret = 0; + struct aim_chat_exchangeinfo *exchanges = NULL; + int curexchange; + aim_tlv_t *exchangetlv; + guint8 maxrooms = 0; + aim_tlvlist_t *tlvlist, *innerlist; + + tlvlist = aim_readtlvchain(bs); + + /* + * Type 0x0002: Maximum concurrent rooms. + */ + if (aim_gettlv(tlvlist, 0x0002, 1)) + maxrooms = aim_gettlv8(tlvlist, 0x0002, 1); + + /* + * Type 0x0003: Exchange information + * + * There can be any number of these, each one + * representing another exchange. + * + */ + for (curexchange = 0; ((exchangetlv = aim_gettlv(tlvlist, 0x0003, curexchange+1))); ) { + aim_bstream_t tbs; + + aim_bstream_init(&tbs, exchangetlv->value, exchangetlv->length); + + curexchange++; + + exchanges = g_realloc(exchanges, curexchange * sizeof(struct aim_chat_exchangeinfo)); + + /* exchange number */ + exchanges[curexchange-1].number = aimbs_get16(&tbs); + innerlist = aim_readtlvchain(&tbs); + + /* + * Type 0x000a: Unknown. + * + * Usually three bytes: 0x0114 (exchange 1) or 0x010f (others). + * + */ + if (aim_gettlv(innerlist, 0x000a, 1)) + ; + + /* + * Type 0x000d: Unknown. + */ + if (aim_gettlv(innerlist, 0x000d, 1)) + ; + + /* + * Type 0x0004: Unknown + */ + if (aim_gettlv(innerlist, 0x0004, 1)) + ; + + /* + * Type 0x0002: Unknown + */ + if (aim_gettlv(innerlist, 0x0002, 1)) { + guint16 classperms; + + classperms = aim_gettlv16(innerlist, 0x0002, 1); + + } + + /* + * Type 0x00c9: Flags + * + * 1 Evilable + * 2 Nav Only + * 4 Instancing Allowed + * 8 Occupant Peek Allowed + * + */ + if (aim_gettlv(innerlist, 0x00c9, 1)) + exchanges[curexchange-1].flags = aim_gettlv16(innerlist, 0x00c9, 1); + + /* + * Type 0x00ca: Creation Date + */ + if (aim_gettlv(innerlist, 0x00ca, 1)) + ; + + /* + * Type 0x00d0: Mandatory Channels? + */ + if (aim_gettlv(innerlist, 0x00d0, 1)) + ; + + /* + * Type 0x00d1: Maximum Message length + */ + if (aim_gettlv(innerlist, 0x00d1, 1)) + ; + + /* + * Type 0x00d2: Maximum Occupancy? + */ + if (aim_gettlv(innerlist, 0x00d2, 1)) + ; + + /* + * Type 0x00d3: Exchange Description + */ + if (aim_gettlv(innerlist, 0x00d3, 1)) + exchanges[curexchange-1].name = aim_gettlv_str(innerlist, 0x00d3, 1); + else + exchanges[curexchange-1].name = NULL; + + /* + * Type 0x00d4: Exchange Description URL + */ + if (aim_gettlv(innerlist, 0x00d4, 1)) + ; + + /* + * Type 0x00d5: Creation Permissions + * + * 0 Creation not allowed + * 1 Room creation allowed + * 2 Exchange creation allowed + * + */ + if (aim_gettlv(innerlist, 0x00d5, 1)) { + guint8 createperms; + + createperms = aim_gettlv8(innerlist, 0x00d5, 1); + } + + /* + * Type 0x00d6: Character Set (First Time) + */ + if (aim_gettlv(innerlist, 0x00d6, 1)) + exchanges[curexchange-1].charset1 = aim_gettlv_str(innerlist, 0x00d6, 1); + else + exchanges[curexchange-1].charset1 = NULL; + + /* + * Type 0x00d7: Language (First Time) + */ + if (aim_gettlv(innerlist, 0x00d7, 1)) + exchanges[curexchange-1].lang1 = aim_gettlv_str(innerlist, 0x00d7, 1); + else + exchanges[curexchange-1].lang1 = NULL; + + /* + * Type 0x00d8: Character Set (Second Time) + */ + if (aim_gettlv(innerlist, 0x00d8, 1)) + exchanges[curexchange-1].charset2 = aim_gettlv_str(innerlist, 0x00d8, 1); + else + exchanges[curexchange-1].charset2 = NULL; + + /* + * Type 0x00d9: Language (Second Time) + */ + if (aim_gettlv(innerlist, 0x00d9, 1)) + exchanges[curexchange-1].lang2 = aim_gettlv_str(innerlist, 0x00d9, 1); + else + exchanges[curexchange-1].lang2 = NULL; + + /* + * Type 0x00da: Unknown + */ + if (aim_gettlv(innerlist, 0x00da, 1)) + ; + + aim_freetlvchain(&innerlist); + } + + /* + * Call client. + */ + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, snac2->type, maxrooms, curexchange, exchanges); + + for (curexchange--; curexchange >= 0; curexchange--) { + g_free(exchanges[curexchange].name); + g_free(exchanges[curexchange].charset1); + g_free(exchanges[curexchange].lang1); + g_free(exchanges[curexchange].charset2); + g_free(exchanges[curexchange].lang2); + } + g_free(exchanges); + aim_freetlvchain(&tlvlist); + + return ret; +} + +static int parseinfo_create(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs, aim_snac_t *snac2) +{ + aim_rxcallback_t userfunc; + aim_tlvlist_t *tlvlist, *innerlist; + char *ck = NULL, *fqcn = NULL, *name = NULL; + guint16 exchange = 0, instance = 0, unknown = 0, flags = 0, maxmsglen = 0, maxoccupancy = 0; + guint32 createtime = 0; + guint8 createperms = 0, detaillevel; + int cklen; + aim_tlv_t *bigblock; + int ret = 0; + aim_bstream_t bbbs; + + tlvlist = aim_readtlvchain(bs); + + if (!(bigblock = aim_gettlv(tlvlist, 0x0004, 1))) { + do_error_dialog(sess->aux_data, "no bigblock in top tlv in create room response", "Gaim"); + + aim_freetlvchain(&tlvlist); + return 0; + } + + aim_bstream_init(&bbbs, bigblock->value, bigblock->length); + + exchange = aimbs_get16(&bbbs); + cklen = aimbs_get8(&bbbs); + ck = aimbs_getstr(&bbbs, cklen); + instance = aimbs_get16(&bbbs); + detaillevel = aimbs_get8(&bbbs); + + if (detaillevel != 0x02) { + do_error_dialog(sess->aux_data, "unknown detaillevel in create room response", "Gaim"); + aim_freetlvchain(&tlvlist); + g_free(ck); + return 0; + } + + unknown = aimbs_get16(&bbbs); + + innerlist = aim_readtlvchain(&bbbs); + + if (aim_gettlv(innerlist, 0x006a, 1)) + fqcn = aim_gettlv_str(innerlist, 0x006a, 1); + + if (aim_gettlv(innerlist, 0x00c9, 1)) + flags = aim_gettlv16(innerlist, 0x00c9, 1); + + if (aim_gettlv(innerlist, 0x00ca, 1)) + createtime = aim_gettlv32(innerlist, 0x00ca, 1); + + if (aim_gettlv(innerlist, 0x00d1, 1)) + maxmsglen = aim_gettlv16(innerlist, 0x00d1, 1); + + if (aim_gettlv(innerlist, 0x00d2, 1)) + maxoccupancy = aim_gettlv16(innerlist, 0x00d2, 1); + + if (aim_gettlv(innerlist, 0x00d3, 1)) + name = aim_gettlv_str(innerlist, 0x00d3, 1); + + if (aim_gettlv(innerlist, 0x00d5, 1)) + createperms = aim_gettlv8(innerlist, 0x00d5, 1); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) { + ret = userfunc(sess, rx, snac2->type, fqcn, instance, exchange, flags, createtime, maxmsglen, maxoccupancy, createperms, unknown, name, ck); + } + + g_free(ck); + g_free(name); + g_free(fqcn); + aim_freetlvchain(&innerlist); + aim_freetlvchain(&tlvlist); + + return ret; +} + +/* + * Since multiple things can trigger this callback, we must lookup the + * snacid to determine the original snac subtype that was called. + * + * XXX This isn't really how this works. But this is: Every d/9 response + * has a 16bit value at the beginning. That matches to: + * Short Desc = 1 + * Full Desc = 2 + * Instance Info = 4 + * Nav Short Desc = 8 + * Nav Instance Info = 16 + * And then everything is really asynchronous. There is no specific + * attachment of a response to a create room request, for example. Creating + * the room yields no different a response than requesting the room's info. + * + */ +static int parseinfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_snac_t *snac2; + int ret = 0; + + if (!(snac2 = aim_remsnac(sess, snac->id))) { + do_error_dialog(sess->aux_data, "received response to unknown request!", "Gaim"); + return 0; + } + + if (snac2->family != 0x000d) { + do_error_dialog(sess->aux_data, "recieved response that maps to corrupt request!", "Gaim"); + return 0; + } + + /* + * We now know what the original SNAC subtype was. + */ + if (snac2->type == 0x0002) /* request chat rights */ + ret = parseinfo_perms(sess, mod, rx, snac, bs, snac2); + else if (snac2->type == 0x0003) {} /* request exchange info */ + else if (snac2->type == 0x0004) {} /* request room info */ + else if (snac2->type == 0x0005) {} /* request more room info */ + else if (snac2->type == 0x0006) {} /* request occupant list */ + else if (snac2->type == 0x0007) {} /* search for a room */ + else if (snac2->type == 0x0008) /* create room */ + ret = parseinfo_create(sess, mod, rx, snac, bs, snac2); + else + do_error_dialog(sess->aux_data, "unknown request subtype", "Gaim"); + + if (snac2) + g_free(snac2->data); + g_free(snac2); + + return ret; +} + +static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + + if (snac->subtype == 0x0009) + return parseinfo(sess, mod, rx, snac, bs); + + return 0; +} + +int chatnav_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x000d; + mod->version = 0x0003; + mod->toolid = 0x0010; + mod->toolversion = 0x0629; + mod->flags = 0; + strncpy(mod->name, "chatnav", sizeof(mod->name)); + mod->snachandler = snachandler; + + return 0; +} diff --git a/protocols/oscar/chatnav.h b/protocols/oscar/chatnav.h new file mode 100644 index 00000000..285decad --- /dev/null +++ b/protocols/oscar/chatnav.h @@ -0,0 +1,14 @@ +#ifndef __OSCAR_CHATNAV_H__ +#define __OSCAR_CHATNAV_H__ + +#define AIM_CB_FAM_CTN 0x000d /* ChatNav */ + +/* + * SNAC Family: Chat Navigation Services + */ +#define AIM_CB_CTN_ERROR 0x0001 +#define AIM_CB_CTN_CREATE 0x0008 +#define AIM_CB_CTN_INFO 0x0009 +#define AIM_CB_CTN_DEFAULT 0xffff + +#endif /* __OSCAR_CHATNAV_H__ */ diff --git a/protocols/oscar/conn.c b/protocols/oscar/conn.c new file mode 100644 index 00000000..711e3458 --- /dev/null +++ b/protocols/oscar/conn.c @@ -0,0 +1,690 @@ + +/* + * conn.c + * + * Does all this gloriously nifty connection handling stuff... + * + */ + +#include <aim.h> +#include "sock.h" + +static int aim_logoff(aim_session_t *sess); + +/* + * In OSCAR, every connection has a set of SNAC groups associated + * with it. These are the groups that you can send over this connection + * without being guarenteed a "Not supported" SNAC error. + * + * The grand theory of things says that these associations transcend + * what libfaim calls "connection types" (conn->type). You can probably + * see the elegance here, but since I want to revel in it for a bit, you + * get to hear it all spelled out. + * + * So let us say that you have your core BOS connection running. One + * of your modules has just given you a SNAC of the group 0x0004 to send + * you. Maybe an IM destined for some twit in Greenland. So you start + * at the top of your connection list, looking for a connection that + * claims to support group 0x0004. You find one. Why, that neat BOS + * connection of yours can do that. So you send it on its way. + * + * Now, say, that fellow from Greenland has friends and they all want to + * meet up with you in a lame chat room. This has landed you a SNAC + * in the family 0x000e and you have to admit you're a bit lost. You've + * searched your connection list for someone who wants to make your life + * easy and deliver this SNAC for you, but there isn't one there. + * + * Here comes the good bit. Without even letting anyone know, particularly + * the module that decided to send this SNAC, and definitly not that twit + * in Greenland, you send out a service request. In this request, you have + * marked the need for a connection supporting group 0x000e. A few seconds + * later, you receive a service redirect with an IP address and a cookie in + * it. Great, you say. Now I have something to do. Off you go, making + * that connection. One of the first things you get from this new server + * is a message saying that indeed it does support the group you were looking + * for. So you continue and send rate confirmation and all that. + * + * Then you remember you had that SNAC to send, and now you have a means to + * do it, and you do, and everyone is happy. Except the Greenlander, who is + * still stuck in the bitter cold. + * + * Oh, and this is useful for building the Migration SNACs, too. In the + * future, this may help convince me to implement rate limit mitigation + * for real. We'll see. + * + * Just to make me look better, I'll say that I've known about this great + * scheme for quite some time now. But I still haven't convinced myself + * to make libfaim work that way. It would take a fair amount of effort, + * and probably some client API changes as well. (Whenever I don't want + * to do something, I just say it would change the client API. Then I + * instantly have a couple of supporters of not doing it.) + * + * Generally, addgroup is only called by the internal handling of the + * server ready SNAC. So if you want to do something before that, you'll + * have to be more creative. That is done rather early, though, so I don't + * think you have to worry about it. Unless you're me. I care deeply + * about such inane things. + * + */ +void aim_conn_addgroup(aim_conn_t *conn, guint16 group) +{ + aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside; + struct snacgroup *sg; + + if (!(sg = g_malloc(sizeof(struct snacgroup)))) + return; + + sg->group = group; + + sg->next = ins->groups; + ins->groups = sg; + + return; +} + +aim_conn_t *aim_conn_findbygroup(aim_session_t *sess, guint16 group) +{ + aim_conn_t *cur; + + for (cur = sess->connlist; cur; cur = cur->next) { + aim_conn_inside_t *ins = (aim_conn_inside_t *)cur->inside; + struct snacgroup *sg; + + for (sg = ins->groups; sg; sg = sg->next) { + if (sg->group == group) + return cur; + } + } + + return NULL; +} + +static void connkill_snacgroups(struct snacgroup **head) +{ + struct snacgroup *sg; + + for (sg = *head; sg; ) { + struct snacgroup *tmp; + + tmp = sg->next; + g_free(sg); + sg = tmp; + } + + *head = NULL; + + return; +} + +static void connkill_rates(struct rateclass **head) +{ + struct rateclass *rc; + + for (rc = *head; rc; ) { + struct rateclass *tmp; + struct snacpair *sp; + + tmp = rc->next; + + for (sp = rc->members; sp; ) { + struct snacpair *tmpsp; + + tmpsp = sp->next; + g_free(sp); + sp = tmpsp; + } + g_free(rc); + + rc = tmp; + } + + *head = NULL; + + return; +} + +static void connkill_real(aim_session_t *sess, aim_conn_t **deadconn) +{ + + aim_rxqueue_cleanbyconn(sess, *deadconn); + aim_tx_cleanqueue(sess, *deadconn); + + if ((*deadconn)->fd != -1) + aim_conn_close(*deadconn); + + /* + * XXX ->priv should never be touched by the library. I know + * it used to be, but I'm getting rid of all that. Use + * ->internal instead. + */ + if ((*deadconn)->priv) + g_free((*deadconn)->priv); + + /* + * This will free ->internal if it necessary... + */ + if ((*deadconn)->type == AIM_CONN_TYPE_RENDEZVOUS) + aim_conn_kill_rend(sess, *deadconn); + else if ((*deadconn)->type == AIM_CONN_TYPE_CHAT) + aim_conn_kill_chat(sess, *deadconn); + + if ((*deadconn)->inside) { + aim_conn_inside_t *inside = (aim_conn_inside_t *)(*deadconn)->inside; + + connkill_snacgroups(&inside->groups); + connkill_rates(&inside->rates); + + g_free(inside); + } + + g_free(*deadconn); + *deadconn = NULL; + + return; +} + +/** + * aim_connrst - Clears out connection list, killing remaining connections. + * @sess: Session to be cleared + * + * Clears out the connection list and kills any connections left. + * + */ +static void aim_connrst(aim_session_t *sess) +{ + + if (sess->connlist) { + aim_conn_t *cur = sess->connlist, *tmp; + + while (cur) { + tmp = cur->next; + aim_conn_close(cur); + connkill_real(sess, &cur); + cur = tmp; + } + } + + sess->connlist = NULL; + + return; +} + +/** + * aim_conn_init - Reset a connection to default values. + * @deadconn: Connection to be reset + * + * Initializes and/or resets a connection structure. + * + */ +static void aim_conn_init(aim_conn_t *deadconn) +{ + + if (!deadconn) + return; + + deadconn->fd = -1; + deadconn->subtype = -1; + deadconn->type = -1; + deadconn->seqnum = 0; + deadconn->lastactivity = 0; + deadconn->forcedlatency = 0; + deadconn->handlerlist = NULL; + deadconn->priv = NULL; + memset(deadconn->inside, 0, sizeof(aim_conn_inside_t)); + + return; +} + +/** + * aim_conn_getnext - Gets a new connection structure. + * @sess: Session + * + * Allocate a new empty connection structure. + * + */ +static aim_conn_t *aim_conn_getnext(aim_session_t *sess) +{ + aim_conn_t *newconn; + + if (!(newconn = g_new0(aim_conn_t,1))) + return NULL; + + if (!(newconn->inside = g_new0(aim_conn_inside_t,1))) { + g_free(newconn); + return NULL; + } + + aim_conn_init(newconn); + + newconn->next = sess->connlist; + sess->connlist = newconn; + + return newconn; +} + +/** + * aim_conn_kill - Close and free a connection. + * @sess: Session for the connection + * @deadconn: Connection to be freed + * + * Close, clear, and free a connection structure. Should never be + * called from within libfaim. + * + */ +void aim_conn_kill(aim_session_t *sess, aim_conn_t **deadconn) +{ + aim_conn_t *cur, **prev; + + if (!deadconn || !*deadconn) + return; + + for (prev = &sess->connlist; (cur = *prev); ) { + if (cur == *deadconn) { + *prev = cur->next; + break; + } + prev = &cur->next; + } + + if (!cur) + return; /* oops */ + + connkill_real(sess, &cur); + + return; +} + +/** + * aim_conn_close - Close a connection + * @deadconn: Connection to close + * + * Close (but not free) a connection. + * + * This leaves everything untouched except for clearing the + * handler list and setting the fd to -1 (used to recognize + * dead connections). It will also remove cookies if necessary. + * + */ +void aim_conn_close(aim_conn_t *deadconn) +{ + + if (deadconn->fd >= 3) + closesocket(deadconn->fd); + deadconn->fd = -1; + if (deadconn->handlerlist) + aim_clearhandlers(deadconn); + if (deadconn->type == AIM_CONN_TYPE_RENDEZVOUS) + aim_conn_close_rend((aim_session_t *)deadconn->sessv, deadconn); + + return; +} + +/** + * aim_getconn_type - Find a connection of a specific type + * @sess: Session to search + * @type: Type of connection to look for + * + * Searches for a connection of the specified type in the + * specified session. Returns the first connection of that + * type found. + * + * XXX except for RENDEZVOUS, all uses of this should be removed and + * use aim_conn_findbygroup() instead. + */ +aim_conn_t *aim_getconn_type(aim_session_t *sess, int type) +{ + aim_conn_t *cur; + + for (cur = sess->connlist; cur; cur = cur->next) { + if ((cur->type == type) && + !(cur->status & AIM_CONN_STATUS_INPROGRESS)) + break; + } + + return cur; +} + +aim_conn_t *aim_getconn_type_all(aim_session_t *sess, int type) +{ + aim_conn_t *cur; + + for (cur = sess->connlist; cur; cur = cur->next) { + if (cur->type == type) + break; + } + + return cur; +} + +/** + * aim_cloneconn - clone an aim_conn_t + * @sess: session containing parent + * @src: connection to clone + * + * A new connection is allocated, and the values are filled in + * appropriately. Note that this function sets the new connnection's + * ->priv pointer to be equal to that of its parent: only the pointer + * is copied, not the data it points to. + * + * This function returns a pointer to the new aim_conn_t, or %NULL on + * error + */ +aim_conn_t *aim_cloneconn(aim_session_t *sess, aim_conn_t *src) +{ + aim_conn_t *conn; + + if (!(conn = aim_conn_getnext(sess))) + return NULL; + + conn->fd = src->fd; + conn->type = src->type; + conn->subtype = src->subtype; + conn->seqnum = src->seqnum; + conn->priv = src->priv; + conn->internal = src->internal; + conn->lastactivity = src->lastactivity; + conn->forcedlatency = src->forcedlatency; + conn->sessv = src->sessv; + aim_clonehandlers(sess, conn, src); + + if (src->inside) { + /* + * XXX should clone this section as well, but since currently + * this function only gets called for some of that rendezvous + * crap, and not on SNAC connections, its probably okay for + * now. + * + */ + } + + return conn; +} + +/** + * aim_newconn - Open a new connection + * @sess: Session to create connection in + * @type: Type of connection to create + * @dest: Host to connect to (in "host:port" syntax) + * + * Opens a new connection to the specified dest host of specified + * type, using the proxy settings if available. If @host is %NULL, + * the connection is allocated and returned, but no connection + * is made. + * + * FIXME: Return errors in a more sane way. + * + */ +aim_conn_t *aim_newconn(aim_session_t *sess, int type, const char *dest) +{ + aim_conn_t *connstruct; + guint16 port = AIM_LOGIN_PORT; + char *host; + int i; + + if (!(connstruct = aim_conn_getnext(sess))) + return NULL; + + connstruct->sessv = (void *)sess; + connstruct->type = type; + + if (!dest) { /* just allocate a struct */ + connstruct->fd = -1; + connstruct->status = 0; + return connstruct; + } + + /* + * As of 23 Jul 1999, AOL now sends the port number, preceded by a + * colon, in the BOS redirect. This fatally breaks all previous + * libfaims. Bad, bad AOL. + * + * We put this here to catch every case. + * + */ + + for(i = 0; i < (int)strlen(dest); i++) { + if (dest[i] == ':') { + port = atoi(&(dest[i+1])); + break; + } + } + + host = (char *)g_malloc(i+1); + strncpy(host, dest, i); + host[i] = '\0'; + + connstruct->fd = proxy_connect(host, port, NULL, NULL); + + g_free(host); + + return connstruct; +} + +/** + * aim_conn_setlatency - Set a forced latency value for connection + * @conn: Conn to set latency for + * @newval: Number of seconds to force between transmits + * + * Causes @newval seconds to be spent between transmits on a connection. + * + * This is my lame attempt at overcoming not understanding the rate + * limiting. + * + * XXX: This should really be replaced with something that scales and + * backs off like the real rate limiting does. + * + */ +int aim_conn_setlatency(aim_conn_t *conn, int newval) +{ + + if (!conn) + return -1; + + conn->forcedlatency = newval; + conn->lastactivity = 0; /* reset this just to make sure */ + + return 0; +} + +/** + * aim_session_init - Initializes a session structure + * @sess: Session to initialize + * @flags: Flags to use. Any of %AIM_SESS_FLAGS %OR'd together. + * @debuglevel: Level of debugging output (zero is least) + * + * Sets up the initial values for a session. + * + */ +void aim_session_init(aim_session_t *sess, guint32 flags, int debuglevel) +{ + + if (!sess) + return; + + memset(sess, 0, sizeof(aim_session_t)); + aim_connrst(sess); + sess->queue_outgoing = NULL; + sess->queue_incoming = NULL; + aim_initsnachash(sess); + sess->msgcookies = NULL; + sess->snacid_next = 0x00000001; + + sess->flags = 0; + + sess->modlistv = NULL; + + sess->ssi.received_data = 0; + sess->ssi.waiting_for_ack = 0; + sess->ssi.holding_queue = NULL; + sess->ssi.revision = 0; + sess->ssi.items = NULL; + sess->ssi.timestamp = (time_t)0; + + sess->locate.userinfo = NULL; + sess->locate.torequest = NULL; + sess->locate.requested = NULL; + sess->locate.waiting_for_response = FALSE; + + sess->icq_info = NULL; + sess->authinfo = NULL; + sess->emailinfo = NULL; + sess->oft_info = NULL; + + + /* + * Default to SNAC login unless XORLOGIN is explicitly set. + */ + if (!(flags & AIM_SESS_FLAGS_XORLOGIN)) + sess->flags |= AIM_SESS_FLAGS_SNACLOGIN; + sess->flags |= flags; + + /* + * This must always be set. Default to the queue-based + * version for back-compatibility. + */ + aim_tx_setenqueue(sess, AIM_TX_QUEUED, NULL); + + + /* + * Register all the modules for this session... + */ + aim__registermodule(sess, misc_modfirst); /* load the catch-all first */ + aim__registermodule(sess, general_modfirst); + aim__registermodule(sess, locate_modfirst); + aim__registermodule(sess, buddylist_modfirst); + aim__registermodule(sess, msg_modfirst); + aim__registermodule(sess, admin_modfirst); + aim__registermodule(sess, bos_modfirst); + aim__registermodule(sess, search_modfirst); + aim__registermodule(sess, stats_modfirst); + aim__registermodule(sess, chatnav_modfirst); + aim__registermodule(sess, chat_modfirst); + /* missing 0x0f - 0x12 */ + aim__registermodule(sess, ssi_modfirst); + /* missing 0x14 */ + aim__registermodule(sess, icq_modfirst); + /* missing 0x16 */ + aim__registermodule(sess, auth_modfirst); + + return; +} + +/** + * aim_session_kill - Deallocate a session + * @sess: Session to kill + * + */ +void aim_session_kill(aim_session_t *sess) +{ + aim_cleansnacs(sess, -1); + + aim_logoff(sess); + + aim__shutdownmodules(sess); + + return; +} + +/* + * XXX this is nearly as ugly as proxyconnect(). + */ +int aim_conn_completeconnect(aim_session_t *sess, aim_conn_t *conn) +{ + fd_set fds, wfds; + struct timeval tv; + int res, error = ETIMEDOUT; + aim_rxcallback_t userfunc; + + if (!conn || (conn->fd == -1)) + return -1; + + if (!(conn->status & AIM_CONN_STATUS_INPROGRESS)) + return -1; + + FD_ZERO(&fds); + FD_SET(conn->fd, &fds); + FD_ZERO(&wfds); + FD_SET(conn->fd, &wfds); + tv.tv_sec = 0; + tv.tv_usec = 0; + + if ((res = select(conn->fd+1, &fds, &wfds, NULL, &tv)) == -1) { + error = errno; + aim_conn_close(conn); + errno = error; + return -1; + } else if (res == 0) { + return 0; /* hasn't really completed yet... */ + } + + if (FD_ISSET(conn->fd, &fds) || FD_ISSET(conn->fd, &wfds)) { + unsigned int len = sizeof(error); + + if (getsockopt(conn->fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) + error = errno; + } + + if (error) { + aim_conn_close(conn); + errno = error; + return -1; + } + +#ifndef _WIN32 + fcntl(conn->fd, F_SETFL, 0); /* XXX should restore original flags */ +#endif + + conn->status &= ~AIM_CONN_STATUS_INPROGRESS; + + if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNCOMPLETE))) + userfunc(sess, NULL, conn); + + /* Flush out the queues if there was something waiting for this conn */ + aim_tx_flushqueue(sess); + + return 0; +} + +aim_session_t *aim_conn_getsess(aim_conn_t *conn) +{ + + if (!conn) + return NULL; + + return (aim_session_t *)conn->sessv; +} + +/* + * aim_logoff() + * + * Closes -ALL- open connections. + * + */ +static int aim_logoff(aim_session_t *sess) +{ + + aim_connrst(sess); /* in case we want to connect again */ + + return 0; + +} + +/* + * aim_flap_nop() + * + * No-op. WinAIM 4.x sends these _every minute_ to keep + * the connection alive. + */ +int aim_flap_nop(aim_session_t *sess, aim_conn_t *conn) +{ + aim_frame_t *fr; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x05, 0))) + return -ENOMEM; + + aim_tx_enqueue(sess, fr); + + return 0; +} + + diff --git a/protocols/oscar/faimconfig.h b/protocols/oscar/faimconfig.h new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/protocols/oscar/faimconfig.h diff --git a/protocols/oscar/ft.c b/protocols/oscar/ft.c new file mode 100644 index 00000000..ddb64e7a --- /dev/null +++ b/protocols/oscar/ft.c @@ -0,0 +1,2005 @@ +/* + * File transfer (OFT) and DirectIM (ODC). + * (OSCAR File Transfer, Oscar Direct Connect(ion?) + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <aim.h> +#include <glib.h> +#include "ft.h" + +#ifndef _WIN32 +#include <netdb.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <sys/utsname.h> /* for aim_directim_initiate */ +#include <arpa/inet.h> /* for inet_ntoa */ +#endif + +/* TODO: + o look for memory leaks.. there's going to be shitloads, i'm sure. +*/ + +struct aim_directim_intdata { + guint8 cookie[8]; + char sn[MAXSNLEN+1]; + char ip[22]; +}; + +static int listenestablish(guint16 portnum); + +/** + * aim_handlerendconnect - call this to accept OFT connections and set up the required structures + * @sess: the session + * @cur: the conn the incoming connection is on + * + * call this when you get an outstanding read on a conn with subtype + * AIM_CONN_SUBTYPE_RENDEZVOUS_OUT, it will clone the current + * &aim_conn_t and tweak things as appropriate. the new conn and the + * listener conn are both returned to the client in the + * %AIM_CB_FAM_OFT, %AIM_CB_OFT_<CLASS>INITIATE callback. + */ +int aim_handlerendconnect(aim_session_t *sess, aim_conn_t *cur) +{ + int acceptfd = 0; + struct sockaddr cliaddr; + unsigned int clilen = sizeof(cliaddr); + int ret = 0; + aim_conn_t *newconn; + + if ((acceptfd = accept(cur->fd, &cliaddr, &clilen)) == -1) + return 0; /* not an error */ + + if (cliaddr.sa_family != AF_INET) { /* just in case IPv6 really is happening */ + closesocket(acceptfd); + aim_conn_close(cur); + return -1; + } + + if (!(newconn = aim_cloneconn(sess, cur))) { + closesocket(acceptfd); + aim_conn_close(cur); + return -1; + } + + newconn->type = AIM_CONN_TYPE_RENDEZVOUS; + newconn->fd = acceptfd; + + if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM) { + struct aim_directim_intdata *priv; + aim_rxcallback_t userfunc; + + priv = (struct aim_directim_intdata *)(newconn->internal = cur->internal); + cur->internal = NULL; + + g_snprintf(priv->ip, sizeof(priv->ip), "%s:%u", + inet_ntoa(((struct sockaddr_in *)&cliaddr)->sin_addr), + ntohs(((struct sockaddr_in *)&cliaddr)->sin_port)); + + if ((userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINITIATE))) + ret = userfunc(sess, NULL, newconn, cur); + + } else if (newconn->subtype == AIM_CONN_SUBTYPE_OFT_GETFILE) { +#if 0 + struct aim_filetransfer_priv *priv; + aim_rxcallback_t userfunc; + + + newconn->priv = cur->priv; + cur->priv = NULL; + priv = (struct aim_filetransfer_priv *)newconn->priv; + + g_snprintf(priv->ip, sizeof(priv->ip), "%s:%u", inet_ntoa(((struct sockaddr_in *)&cliaddr)->sin_addr), ntohs(((struct sockaddr_in *)&cliaddr)->sin_port)); + + if ((userfunc = aim_callhandler(sess, newconn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEINITIATE))) + ret = userfunc(sess, NULL, newconn, cur); +#endif + } else { + do_error_dialog(sess->aux_data, "Got a Connection on a listener that's not Rendezvous Closing conn.", "Gaim"); + aim_conn_close(newconn); + ret = -1; + } + + return ret; +} + +/** + * aim_send_typing - send client-to-client typing notification over established connection + * @sess: session to conn + * @conn: directim connection + * @typing: If true, notify user has started typing; if false, notify user has stopped. + * + * The connection must have been previously established. + */ +int aim_send_typing(aim_session_t *sess, aim_conn_t *conn, int typing) +{ + +struct aim_directim_intdata *intdata = (struct aim_directim_intdata *)conn->internal; + aim_frame_t *fr; + aim_bstream_t hdrbs; /* XXX this should be within aim_frame_t */ + + if (!sess || !conn || (conn->type != AIM_CONN_TYPE_RENDEZVOUS)) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x01, 0))) + return -ENOMEM; + + memcpy(fr->hdr.oft.magic, "ODC2", 4); + + fr->hdr.oft.hdr2len = 0x44; + + if (!(fr->hdr.oft.hdr2 = g_malloc(fr->hdr.oft.hdr2len))) { + aim_frame_destroy(fr); + return -ENOMEM; + } + memset(fr->hdr.oft.hdr2, 0, fr->hdr.oft.hdr2len); + + aim_bstream_init(&hdrbs, fr->hdr.oft.hdr2, fr->hdr.oft.hdr2len); + + aimbs_put16(&hdrbs, 0x0006); + aimbs_put16(&hdrbs, 0x0000); + aimbs_putraw(&hdrbs, intdata->cookie, 8); + aimbs_put16(&hdrbs, 0x0000); + aimbs_put16(&hdrbs, 0x0000); + aimbs_put16(&hdrbs, 0x0000); + aimbs_put16(&hdrbs, 0x0000); + aimbs_put32(&hdrbs, 0x00000000); + aimbs_put16(&hdrbs, 0x0000); + aimbs_put16(&hdrbs, 0x0000); + aimbs_put16(&hdrbs, 0x0000); + + /* flags -- 0x000e for "started typing", 0x0002 for "stopped typing */ + aimbs_put16(&hdrbs, ( typing ? 0x000e : 0x0002)); + + aimbs_put16(&hdrbs, 0x0000); + aimbs_put16(&hdrbs, 0x0000); + aimbs_putraw(&hdrbs, (guint8 *)sess->sn, strlen(sess->sn)); + + aim_bstream_setpos(&hdrbs, 52); /* bleeehh */ + + aimbs_put8(&hdrbs, 0x00); + aimbs_put16(&hdrbs, 0x0000); + aimbs_put16(&hdrbs, 0x0000); + aimbs_put16(&hdrbs, 0x0000); + aimbs_put16(&hdrbs, 0x0000); + aimbs_put16(&hdrbs, 0x0000); + aimbs_put16(&hdrbs, 0x0000); + aimbs_put16(&hdrbs, 0x0000); + + /* end of hdr2 */ + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/** + * aim_send_im_direct - send IM client-to-client over established connection + * @sess: session to conn + * @conn: directim connection + * @msg: null-terminated string to send. + * len: The length of the message to send, including binary data. + * + * Call this just like you would aim_send_im, to send a directim. You + * _must_ have previously established the directim connection. + */ +int aim_send_im_direct(aim_session_t *sess, aim_conn_t *conn, const char *msg, int len) +{ + struct aim_directim_intdata *intdata = (struct aim_directim_intdata *)conn->internal; + aim_frame_t *fr; + aim_bstream_t hdrbs; /* XXX this should be within aim_frame_t */ + + if (!sess || !conn || !msg || (conn->type != AIM_CONN_TYPE_RENDEZVOUS)) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x01, len))) + return -ENOMEM; + + memcpy(fr->hdr.oft.magic, "ODC2", 4); + + fr->hdr.oft.hdr2len = 0x44; + + if (!(fr->hdr.oft.hdr2 = g_malloc(fr->hdr.oft.hdr2len))) { + aim_frame_destroy(fr); + return -ENOMEM; + } + memset(fr->hdr.oft.hdr2, 0, fr->hdr.oft.hdr2len); + + aim_bstream_init(&hdrbs, fr->hdr.oft.hdr2, fr->hdr.oft.hdr2len); + + aimbs_put16(&hdrbs, 0x0006); + aimbs_put16(&hdrbs, 0x0000); + aimbs_putraw(&hdrbs, intdata->cookie, 8); + aimbs_put16(&hdrbs, 0x0000); + aimbs_put16(&hdrbs, 0x0000); + aimbs_put16(&hdrbs, 0x0000); + aimbs_put16(&hdrbs, 0x0000); + aimbs_put32(&hdrbs, len); + aimbs_put16(&hdrbs, 0x0000); + aimbs_put16(&hdrbs, 0x0000); + aimbs_put16(&hdrbs, 0x0000); + + /* flags -- 0x000e for "started typing", 0x0002 for "stopped typing, 0x0000 for message */ + aimbs_put16(&hdrbs, 0x0000); + + aimbs_put16(&hdrbs, 0x0000); + aimbs_put16(&hdrbs, 0x0000); + aimbs_putraw(&hdrbs, (guint8 *)sess->sn, strlen(sess->sn)); + + aim_bstream_setpos(&hdrbs, 52); /* bleeehh */ + + aimbs_put8(&hdrbs, 0x00); + aimbs_put16(&hdrbs, 0x0000); + aimbs_put16(&hdrbs, 0x0000); + aimbs_put16(&hdrbs, 0x0000); + aimbs_put16(&hdrbs, 0x0000); + aimbs_put16(&hdrbs, 0x0000); + aimbs_put16(&hdrbs, 0x0000); + aimbs_put16(&hdrbs, 0x0000); + + /* end of hdr2 */ + +#if 0 /* XXX this is how you send buddy icon info... */ + i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0008); + i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x000c); + i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0000); + i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x1466); + i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x0001); + i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x2e0f); + i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0x393e); + i += aimutil_put16(newpacket->hdr.oft.hdr2+i, 0xcac8); +#endif + aimbs_putraw(&fr->data, (guint8 *)msg, len); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +static int getlocalip(guint8 *ip) +{ + struct hostent *hptr; + char localhost[129]; + + /* XXX if available, use getaddrinfo() */ + /* XXX allow client to specify which IP to use for multihomed boxes */ + + if (gethostname(localhost, 128) < 0) + return -1; + + if (!(hptr = gethostbyname(localhost))) + return -1; + + memcpy(ip, hptr->h_addr_list[0], 4); + + return 0; +} + +/** + * aim_directim_intitiate - For those times when we want to open up the directim channel ourselves. + * @sess: your session, + * @conn: the BOS conn, + * @priv: a dummy priv value (we'll let it get filled in later) (if you pass a %NULL, we alloc one) + * @destsn: the SN to connect to. + * + */ +aim_conn_t *aim_directim_initiate(aim_session_t *sess, const char *destsn) +{ + aim_conn_t *newconn; + aim_msgcookie_t *cookie; + struct aim_directim_intdata *priv; + int listenfd; + guint16 port = 4443; + guint8 localip[4]; + guint8 ck[8]; + + if (getlocalip(localip) == -1) + return NULL; + + if ((listenfd = listenestablish(port)) == -1) + return NULL; + + aim_request_directim(sess, destsn, localip, port, ck); + + cookie = (aim_msgcookie_t *)g_new0(aim_msgcookie_t,1); + memcpy(cookie->cookie, ck, 8); + cookie->type = AIM_COOKIETYPE_OFTIM; + + /* this one is for the cookie */ + priv = (struct aim_directim_intdata *)g_new0(struct aim_directim_intdata,1); + + memcpy(priv->cookie, ck, 8); + strncpy(priv->sn, destsn, sizeof(priv->sn)); + cookie->data = priv; + aim_cachecookie(sess, cookie); + + /* XXX switch to aim_cloneconn()? */ + if (!(newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS_OUT, NULL))) { + closesocket(listenfd); + return NULL; + } + + /* this one is for the conn */ + priv = (struct aim_directim_intdata *)g_new0(struct aim_directim_intdata, 1); + + memcpy(priv->cookie, ck, 8); + strncpy(priv->sn, destsn, sizeof(priv->sn)); + + newconn->fd = listenfd; + newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM; + newconn->internal = priv; + newconn->lastactivity = time(NULL); + + return newconn; +} + +/** + * aim_sendfile_intitiate - For those times when we want to send the file ourselves. + * @sess: your session, + * @conn: the BOS conn, + * @destsn: the SN to connect to. + * @filename: the name of the files you want to send + * + */ +aim_conn_t *aim_sendfile_initiate(aim_session_t *sess, const char *destsn, const char *filename, guint16 numfiles, guint32 totsize) +{ + aim_conn_t *newconn; + aim_msgcookie_t *cookie; + struct aim_directim_intdata *priv; + int listenfd; + guint16 port = 4443; + guint8 localip[4]; + guint8 ck[8]; + + if (getlocalip(localip) == -1) + return NULL; + + if ((listenfd = listenestablish(port)) == -1) + return NULL; + + aim_request_sendfile(sess, destsn, filename, numfiles, totsize, localip, port, ck); + + cookie = (aim_msgcookie_t *)g_new0(aim_msgcookie_t,1); + memcpy(cookie->cookie, ck, 8); + cookie->type = AIM_COOKIETYPE_OFTIM; + + /* this one is for the cookie */ + priv = (struct aim_directim_intdata *)g_new0(struct aim_directim_intdata,1); + + memcpy(priv->cookie, ck, 8); + strncpy(priv->sn, destsn, sizeof(priv->sn)); + cookie->data = priv; + aim_cachecookie(sess, cookie); + + /* XXX switch to aim_cloneconn()? */ + if (!(newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS_OUT, NULL))) { + closesocket(listenfd); + return NULL; + } + + /* this one is for the conn */ + priv = (struct aim_directim_intdata *)g_new0(struct aim_directim_intdata,1); + + memcpy(priv->cookie, ck, 8); + strncpy(priv->sn, destsn, sizeof(priv->sn)); + + newconn->fd = listenfd; + newconn->subtype = AIM_CONN_SUBTYPE_OFT_SENDFILE; + newconn->internal = priv; + newconn->lastactivity = time(NULL); + + return newconn; +} + +#if 0 +/** + * unsigned int aim_oft_listener_clean - close up old listeners + * @sess: session to clean up in + * @age: maximum age in seconds + * + * returns number closed, -1 on error. + */ +unsigned int aim_oft_listener_clean(struct aim_session_t *sess, time_t age) +{ + struct aim_conn_t *cur; + time_t now; + unsigned int hit = 0; + + if (!sess) + return -1; + now = time(NULL); + faim_mutex_lock(&sess->connlistlock); + for(cur = sess->connlist;cur; cur = cur->next) + if (cur->type == AIM_CONN_TYPE_RENDEZVOUS_OUT) { + faim_mutex_lock(&cur->active); + if (cur->lastactivity < (now - age) ) { + faim_mutex_unlock(&cur->active); + aim_conn_close(cur); + hit++; + } else + faim_mutex_unlock(&cur->active); + } + faim_mutex_unlock(&sess->connlistlock); + return hit; +} +#endif + +const char *aim_directim_getsn(aim_conn_t *conn) +{ + struct aim_directim_intdata *intdata; + + if (!conn) + return NULL; + + if ((conn->type != AIM_CONN_TYPE_RENDEZVOUS) || + (conn->subtype != AIM_CONN_SUBTYPE_OFT_DIRECTIM)) + return NULL; + + if (!conn->internal) + return NULL; + + intdata = (struct aim_directim_intdata *)conn->internal; + + return intdata->sn; +} + +/** + * aim_directim_connect - connect to buddy for directim + * @sess: the session to append the conn to, + * @sn: the SN we're connecting to + * @addr: address to connect to + * + * This is a wrapper for aim_newconn. + * + * If addr is NULL, the socket is not created, but the connection is + * allocated and setup to connect. + * + */ +aim_conn_t *aim_directim_connect(aim_session_t *sess, const char *sn, const char *addr, const guint8 *cookie) +{ + aim_conn_t *newconn; + struct aim_directim_intdata *intdata; + + if (!sess || !sn) + return NULL; + + if (!(intdata = g_new0(struct aim_directim_intdata, 1))) + return NULL; + + memcpy(intdata->cookie, cookie, 8); + strncpy(intdata->sn, sn, sizeof(intdata->sn)); + if (addr) + strncpy(intdata->ip, addr, sizeof(intdata->ip)); + + /* XXX verify that non-blocking connects actually work */ + if (!(newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS, addr))) { + g_free(intdata); + return NULL; + } + + if (!newconn) { + g_free(intdata); + return newconn; + } + + newconn->subtype = AIM_CONN_SUBTYPE_OFT_DIRECTIM; + newconn->internal = intdata; + + return newconn; +} + +/** + * aim_directim_getconn - find a directim conn for buddy name + * @sess: your session, + * @name: the name to get, + * + * returns conn for directim with name, %NULL if none found. + * + */ +aim_conn_t *aim_directim_getconn(aim_session_t *sess, const char *name) +{ + aim_conn_t *cur; + + if (!sess || !name || !strlen(name)) + return NULL; + + for (cur = sess->connlist; cur; cur = cur->next) { + struct aim_directim_intdata *intdata; + + if ((cur->type != AIM_CONN_TYPE_RENDEZVOUS) || (cur->subtype != AIM_CONN_SUBTYPE_OFT_DIRECTIM)) + continue; + + intdata = cur->internal; + + if (aim_sncmp(intdata->sn, name) == 0) + break; + } + + return cur; +} + +/** + * aim_accepttransfer - accept a file transfer request + * @sess: the session, + * @conn: the BOS conn for the CAP reply + * @sn: the screenname to send it to, + * @cookie: the cookie used + * @ip: the ip to connect to + * @listingfiles: number of files to share + * @listingtotsize: total size of shared files + * @listingsize: length of the listing file(buffer) + * @listingchecksum: checksum of the listing + * @rendid: capability type (%AIM_CAPS_GETFILE or %AIM_CAPS_SENDFILE) + * + * Returns new connection or %NULL on error. + * + * XXX this should take a struct. + */ +aim_conn_t *aim_accepttransfer(aim_session_t *sess, + aim_conn_t *conn, + const char *sn, const guint8 *cookie, + const guint8 *ip, + guint16 listingfiles, + guint16 listingtotsize, + guint16 listingsize, + guint32 listingchecksum, + guint16 rendid) +{ + return NULL; +#if 0 + struct command_tx_struct *newpacket, *newoft; + struct aim_conn_t *newconn; + struct aim_fileheader_t *fh; + struct aim_filetransfer_priv *priv; + struct aim_msgcookie_t *cachedcook; + int curbyte, i; + + if (!sess || !conn || !sn || !cookie || !ip) { + return NULL; + } + + newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS, ip); + + if (!newconn || (newconn->fd == -1)) { + perror("aim_newconn"); + faimdprintf(sess, 2, "could not connect to %s (fd: %i)\n", ip, newconn?newconn->fd:0); + return newconn; + } else { + priv = (struct aim_filetransfer_priv *)g_new0(struct aim_filetransfer_priv,1); + + memcpy(priv->cookie, cookie, 8); + priv->state = 0; + strncpy(priv->sn, sn, MAXSNLEN); + strncpy(priv->ip, ip, sizeof(priv->ip)); + newconn->priv = (void *)priv; + + faimdprintf(sess, 2, "faim: connected to peer (fd = %d)\n", newconn->fd); + } + + if (rendid == AIM_CAPS_GETFILE) { + newconn->subtype = AIM_CONN_SUBTYPE_OFT_GETFILE; + + faimdprintf(sess, 2, "faim: getfile request accept\n"); + + if (!(newoft = aim_tx_new(sess, newconn, AIM_FRAMETYPE_OFT, 0x1108, 0))) { + faimdprintf(sess, 2, "faim: aim_accepttransfer: tx_new OFT failed\n"); + /* XXX: conn leak here */ + return NULL; + } + + newoft->lock = 1; + memcpy(newoft->hdr.oft.magic, "OFT2", 4); + newoft->hdr.oft.hdr2len = 0x100 - 8; + + if (!(fh = (struct aim_fileheader_t*)g_new0(struct aim_fileheader_t,1))) { + /* XXX: conn leak here */ + perror("calloc"); + return NULL; + } + + fh->encrypt = 0x0000; + fh->compress = 0x0000; + fh->totfiles = listingfiles; + fh->filesleft = listingfiles; /* is this right -- total parts and parts left?*/ + fh->totparts = 0x0001; + fh->partsleft = 0x0001; + fh->totsize = listingtotsize; + fh->size = listingsize; /* ls -l listing.txt */ + fh->modtime = (int)time(NULL); /* we'll go with current time for now */ + fh->checksum = listingchecksum; + fh->rfcsum = 0x00000000; + fh->rfsize = 0x00000000; + fh->cretime = 0x00000000; + fh->rfcsum = 0x00000000; + fh->nrecvd = 0x00000000; + fh->recvcsum = 0x00000000; + memset(fh->idstring, 0, sizeof(fh->idstring)); + memcpy(fh->idstring, "OFT_Windows ICBMFT V1.1 32", sizeof(fh->idstring)); + fh->flags = 0x02; + fh->lnameoffset = 0x1a; + fh->lsizeoffset = 0x10; + memset(fh->dummy, 0, sizeof(fh->dummy)); + memset(fh->macfileinfo, 0, sizeof(fh->macfileinfo)); + + /* we need to figure out these encodings for filenames */ + fh->nencode = 0x0000; + fh->nlanguage = 0x0000; + memset(fh->name, 0, sizeof(fh->name)); + memcpy(fh->name, "listing.txt", sizeof(fh->name)); + + if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) { + newoft->lock = 0; + aim_frame_destroy(newoft); + /* XXX: conn leak */ + perror("calloc (1)"); + return NULL; + } + + memcpy(fh->bcookie, cookie, 8); + + if (!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, fh))) + faimdprintf(sess, 1, "eek, bh fail!\n"); + + newoft->lock = 0; + aim_tx_enqueue(sess, newoft); + + if (!(cachedcook = (struct aim_msgcookie_t *)calloc(1, sizeof(struct aim_msgcookie_t)))) { + faimdprintf(sess, 1, "faim: accepttransfer: couldn't calloc cachedcook. yeep!\n"); + /* XXX: more cleanup, conn leak */ + perror("calloc (2)"); + return NULL; + } + + memcpy(&(priv->fh), fh, sizeof(struct aim_fileheader_t)); + memcpy(cachedcook->cookie, cookie, 8); + + cachedcook->type = AIM_COOKIETYPE_OFTGET; + cachedcook->data = (void *)priv; + + if (aim_cachecookie(sess, cachedcook) == -1) + faimdprintf(sess, 1, "faim: ERROR caching message cookie\n"); + + g_free(fh); + + /* OSCAR CAP accept packet */ + + if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+8+2+1+strlen(sn)+4+2+8+16))) { + return NULL; + } + } else { + return NULL; + } + + newpacket->lock = 1; + curbyte = aim_putsnac(newpacket->data, 0x0004, 0x0006, 0x0000, sess->snac_nextid); + + for (i = 0; i < 8; i++) + curbyte += aimutil_put8(newpacket->data+curbyte, cookie[i]); + + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002); + curbyte += aimutil_put8(newpacket->data+curbyte, strlen(sn)); + curbyte += aimutil_putstr(newpacket->data+curbyte, sn, strlen(sn)); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x001a); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002 /* accept*/); + + for (i = 0;i < 8; i++) + curbyte += aimutil_put8(newpacket->data+curbyte, cookie[i]); + + curbyte += aim_putcap(newpacket->data+curbyte, 0x10, rendid); + newpacket->lock = 0; + aim_tx_enqueue(sess, newpacket); + + return newconn; +#endif +} + +/** + * aim_getlisting(FILE *file) -- get an aim_fileheader_t for a given FILE* + * @file is an opened listing file + * + * returns a pointer to the filled-in fileheader_t + * + * Currently omits checksum. we'll fix this when AOL breaks us, i + * guess. + * + */ +struct aim_fileheader_t *aim_getlisting(aim_session_t *sess, FILE *file) +{ + return NULL; +#if 0 + struct aim_fileheader_t *fh; + u_long totsize = 0, size = 0, checksum = 0xffff0000; + short totfiles = 0; + char *linebuf, sizebuf[9]; + + int linelength = 1024; + + /* XXX: if we have a line longer than 1024chars, God help us. */ + if ( (linebuf = (char *)calloc(1, linelength)) == NULL ) { + faimdprintf(sess, 2, "linebuf calloc failed\n"); + return NULL; + } + + if (fseek(file, 0, SEEK_END) == -1) { /* use this for sanity check */ + perror("getlisting END1 fseek:"); + faimdprintf(sess, 2, "getlising fseek END1 error\n"); + } + + if ((size = ftell(file)) == -1) { + perror("getlisting END1 getpos:"); + faimdprintf(sess, 2, "getlising getpos END1 error\n"); + } + + if (fseek(file, 0, SEEK_SET) != 0) { + perror("getlesting fseek(SET):"); + faimdprintf(sess, 2, "faim: getlisting: couldn't seek to beginning of listing file\n"); + } + + memset(linebuf, 0, linelength); + + size = 0; + + while(fgets(linebuf, linelength, file)) { + totfiles++; + memset(sizebuf, 0, 9); + + size += strlen(linebuf); + + if (strlen(linebuf) < 23) { + faimdprintf(sess, 2, "line \"%s\" too short. skipping\n", linebuf); + continue; + } + if (linebuf[strlen(linebuf)-1] != '\n') { + faimdprintf(sess, 2, "faim: OFT: getlisting -- hit EOF or line too long!\n"); + } + + memcpy(sizebuf, linebuf+17, 8); + + totsize += strtol(sizebuf, NULL, 10); + memset(linebuf, 0, linelength); + } + + if (fseek(file, 0, SEEK_SET) == -1) { + perror("getlisting END2 fseek:"); + faimdprintf(sess, 2, "getlising fseek END2 error\n"); + } + + g_free(linebuf); + + /* we're going to ignore checksumming the data for now -- that + * requires walking the whole listing.txt. it should probably be + * done at register time and cached, but, eh. */ + + if (!(fh = (struct aim_fileheader_t*)calloc(1, sizeof(struct aim_fileheader_t)))) + return NULL; + + fh->encrypt = 0x0000; + fh->compress = 0x0000; + fh->totfiles = totfiles; + fh->filesleft = totfiles; /* is this right ?*/ + fh->totparts = 0x0001; + fh->partsleft = 0x0001; + fh->totsize = totsize; + fh->size = size; /* ls -l listing.txt */ + fh->modtime = (int)time(NULL); /* we'll go with current time for now */ + fh->checksum = checksum; /* XXX: checksum ! */ + fh->rfcsum = 0x00000000; + fh->rfsize = 0x00000000; + fh->cretime = 0x00000000; + fh->rfcsum = 0x00000000; + fh->nrecvd = 0x00000000; + fh->recvcsum = 0x00000000; + + /* memset(fh->idstring, 0, sizeof(fh->idstring)); */ + memcpy(fh->idstring, "OFT_Windows ICBMFT V1.1 32", sizeof(fh->idstring)); + memset(fh->idstring+strlen(fh->idstring), 0, sizeof(fh->idstring)-strlen(fh->idstring)); + + fh->flags = 0x02; + fh->lnameoffset = 0x1a; + fh->lsizeoffset = 0x10; + + /* memset(fh->dummy, 0, sizeof(fh->dummy)); */ + memset(fh->macfileinfo, 0, sizeof(fh->macfileinfo)); + + fh->nencode = 0x0000; /* we need to figure out these encodings for filenames */ + fh->nlanguage = 0x0000; + + /* memset(fh->name, 0, sizeof(fh->name)); */ + memcpy(fh->name, "listing.txt", sizeof(fh->name)); + memset(fh->name+strlen(fh->name), 0, 64-strlen(fh->name)); + + faimdprintf(sess, 2, "faim: OFT: listing fh name %s / %s\n", fh->name, (fh->name+(strlen(fh->name)))); + return fh; +#endif +} + +/** + * aim_listenestablish - create a listening socket on a port. + * @portnum: the port number to bind to. + * + * you need to call accept() when it's connected. returns your fd + * + * XXX: give the client author the responsibility of setting up a + * listener, then we no longer have a libfaim problem with broken + * solaris *innocent smile* -jbm + */ +static int listenestablish(guint16 portnum) +{ +#if HAVE_GETADDRINFO + int listenfd; + const int on = 1; + struct addrinfo hints, *res, *ressave; + char serv[5]; + + g_snprintf(serv, sizeof(serv), "%d", portnum); + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + if (getaddrinfo(NULL /*any IP*/, serv, &hints, &res) != 0) { + perror("getaddrinfo"); + return -1; + } + ressave = res; + do { + listenfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (listenfd < 0) + continue; + setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + if (bind(listenfd, res->ai_addr, res->ai_addrlen) == 0) + break; + /* success */ + closesocket(listenfd); + } while ( (res = res->ai_next) ); + + if (!res) + return -1; + + if (listen(listenfd, 1024)!=0) { + perror("listen"); + return -1; + } + + freeaddrinfo(ressave); + return listenfd; +#else + int listenfd; + const int on = 1; + struct sockaddr_in sockin; + + if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("socket(listenfd)"); + return -1; + } + + if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) != 0) { + perror("setsockopt(listenfd)"); + closesocket(listenfd); + return -1; + } + + memset(&sockin, 0, sizeof(struct sockaddr_in)); + sockin.sin_family = AF_INET; + sockin.sin_port = htons(portnum); + + if (bind(listenfd, (struct sockaddr *)&sockin, sizeof(struct sockaddr_in)) != 0) { + perror("bind(listenfd)"); + closesocket(listenfd); + return -1; + } + if (listen(listenfd, 4) != 0) { + perror("listen(listenfd)"); + closesocket(listenfd); + return -1; + } + return listenfd; +#endif +} + +static int getcommand_getfile(aim_session_t *sess, aim_conn_t *conn) +{ +#if 0 + struct aim_filetransfer_priv *ft; + aim_rxcallback_t userfunc; + + ft = conn->priv; + if (ft->state == 2) { + /* waiting on listing data */ + int ret = 0; + char *listing; + struct command_tx_struct *newoft; + + if (!(listing = g_malloc(ft->fh.size))) + return -1; + + ft->state = 0; + if (aim_recv(conn->fd, listing, ft->fh.size) != ft->fh.size) + faimdprintf(sess, 2, "OFT get: file %s was short. (0x%lx)\n", ft->fh.name, ft->fh.size); + + if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x120b, 0))) { + faimdprintf(sess, 2, "faim: aim_get_command_rendezvous: getfile listing: tx_new OFT failed\n"); + faim_mutex_unlock(&conn->active); + g_free(listing); + aim_conn_close(conn); + return -1; + } + + memcpy(newoft->hdr.oft.magic, "OFT2", 4); + newoft->hdr.oft.hdr2len = 0x100 - 8; + + /* Protocol BS - set nrecvd to size of listing, recvcsum to listing checksum, flags to 0 */ + + ft->fh.nrecvd = ft->fh.size; + ft->fh.recvcsum = ft->fh.checksum; + ft->fh.flags = 0; + + if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) { + aim_frame_destroy(newoft); + g_free(listing); + return -1; + } + + if (!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh)))) + faimdprintf(sess, 2, "eek! bh fail listing\n"); + + /* send the 120b */ + aim_tx_enqueue(sess, newoft); + if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILELISTING)) ) + ret = userfunc(sess, NULL, conn, ft, listing); + + g_free(listing); + return ret; + } + + if (ft->state == 3) { + /* waiting on file data */ + if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILERECEIVE)) ) + return userfunc(sess, NULL, conn, ft); + return 0; + } + + if (ft->state == 4) { + if( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILESTATE4)) ) + return userfunc(sess, NULL, conn); + aim_conn_close(conn); + return 0; + } + + return 0; +#else + return -1; +#endif +} + +static void connclose_sendfile(aim_session_t *sess, aim_conn_t *conn) +{ + aim_msgcookie_t *cook; + struct aim_filetransfer_priv *priv = (struct aim_filetransfer_priv *)conn->priv; + + cook = aim_uncachecookie(sess, (guint8 *)priv->cookie, AIM_COOKIETYPE_OFTSEND); + aim_cookie_free(sess, cook); + + return; +} + +static void connkill_sendfile(aim_session_t *sess, aim_conn_t *conn) +{ + + g_free(conn->internal); + + return; +} + +static void connclose_getfile(aim_session_t *sess, aim_conn_t *conn) +{ + aim_msgcookie_t *cook; + struct aim_filetransfer_priv *priv = (struct aim_filetransfer_priv *)conn->priv; + + cook = aim_uncachecookie(sess, (guint8 *)priv->cookie, AIM_COOKIETYPE_OFTGET); + aim_cookie_free(sess, cook); + + return; +} + +static void connkill_getfile(aim_session_t *sess, aim_conn_t *conn) +{ + + g_free(conn->internal); + + return; +} + +static void connclose_directim(aim_session_t *sess, aim_conn_t *conn) +{ + struct aim_directim_intdata *intdata = (struct aim_directim_intdata *)conn->internal; + aim_msgcookie_t *cook; + + cook = aim_uncachecookie(sess, intdata->cookie, AIM_COOKIETYPE_OFTIM); + aim_cookie_free(sess, cook); + + return; +} + +static void connkill_directim(aim_session_t *sess, aim_conn_t *conn) +{ + + g_free(conn->internal); + + return; +} + +void aim_conn_close_rend(aim_session_t *sess, aim_conn_t *conn) +{ + + if (conn->type != AIM_CONN_TYPE_RENDEZVOUS) + return; + + if (conn->subtype == AIM_CONN_SUBTYPE_OFT_SENDFILE) + connclose_sendfile(sess, conn); + else if (conn->subtype == AIM_CONN_SUBTYPE_OFT_GETFILE) + connclose_getfile(sess, conn); + else if (conn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM) + connclose_directim(sess, conn); + + return; +} + +void aim_conn_kill_rend(aim_session_t *sess, aim_conn_t *conn) +{ + + if (conn->type != AIM_CONN_TYPE_RENDEZVOUS) + return; + + if (conn->subtype == AIM_CONN_SUBTYPE_OFT_SENDFILE) + connkill_sendfile(sess, conn); + else if (conn->subtype == AIM_CONN_SUBTYPE_OFT_GETFILE) + connkill_getfile(sess, conn); + else if (conn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM) + connkill_directim(sess, conn); + + return; +} + +static int handlehdr_directim(aim_session_t *sess, aim_conn_t *conn, guint8 *hdr) +{ + aim_frame_t fr; + aim_rxcallback_t userfunc; + guint32 payloadlength; + guint16 flags; + char *snptr = NULL; + + fr.conn = conn; + + payloadlength = aimutil_get32(hdr+22); + flags = aimutil_get16(hdr+32); + snptr = (char *)hdr+38; + + if (flags == 0x000e) { + int ret = 0; + + if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING))) + ret = userfunc(sess, &fr, snptr, 1); + + return ret; + + } else if (flags == 0x0002) { + int ret = 0; + + if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMTYPING))) + ret = userfunc(sess, &fr, snptr, 0); + + return ret; + + } else if ((flags == 0x0000) && payloadlength) { + char *msg, *msg2; + int ret = 0; + int recvd = 0; + int i; + + if (!(msg = g_malloc(payloadlength+1))) + return -1; + memset(msg, 0, payloadlength+1); + msg2 = msg; + + while (payloadlength - recvd) { + if (payloadlength - recvd >= 1024) + i = aim_recv(conn->fd, msg2, 1024); + else + i = aim_recv(conn->fd, msg2, payloadlength - recvd); + if (i <= 0) { + g_free(msg); + return -1; + } + recvd = recvd + i; + msg2 = msg2 + i; + if ((userfunc=aim_callhandler(sess, conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_IMAGETRANSFER))) + userfunc(sess, &fr, snptr, (double)recvd / payloadlength); + } + + if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_DIRECTIMINCOMING)) ) + ret = userfunc(sess, &fr, snptr, msg, payloadlength); + + g_free(msg); + + return ret; + } + + return 0; +} + +static int handlehdr_getfile_listing(aim_session_t *sess, aim_conn_t *conn, guint8 *hdr) +{ +#if 0 + struct aim_filetransfer_priv *ft; + struct aim_fileheader_t *fh; + struct aim_msgcookie_t *cook; + struct command_tx_struct *newoft; + aim_rxcallback_t userfunc; + + faimdprintf(sess, 2,"faim: rend: fileget 0x1108\n"); + fh = aim_oft_getfh(hdr); + + faim_mutex_unlock(&conn->active); + + if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) { + g_free(fh); + return -1; + } + + ft = cook->data; + + /* we're waaaaiiiting.. for listing.txt */ + ft->state = 2; + + memcpy(&(ft->fh), fh, sizeof(struct aim_fileheader_t)); + g_free(fh); + + if(aim_cachecookie(sess, cook) == -1) { + faimdprintf(sess, 1, "error caching cookie\n"); + return -1; + } + + if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x1209, 0))) { + aim_conn_close(conn); + return -1; + } + + memcpy(newoft->hdr.oft.magic, "OFT2", 4); + newoft->hdr.oft.hdr2len = 0x100 - 8; + + if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) { + newoft->lock = 0; + aim_frame_destroy(newoft); + return -1; + } + + if (!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh)))) { + newoft->lock = 0; + aim_frame_destroy(newoft); + return -1; + } + + newoft->lock = 0; + aim_tx_enqueue(sess, newoft); +#endif + return -1; +} + +static int handlehdr_getfile_listing2(aim_session_t *sess, aim_conn_t *conn, guint8 *hdr) +{ +#if 0 + struct aim_filetransfer_priv *ft; + struct aim_fileheader_t *fh; + struct aim_msgcookie_t *cook; + int ret = 0; + aim_rxcallback_t userfunc; + + fh = aim_oft_getfh(hdr); + + if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) + faimdprintf(sess, 2, "shit, no cookie in 0x1209. (%i/%s)going to crash..\n", AIM_COOKIETYPE_OFTGET, fh->bcookie); + + ft = cook->data; + + if (ft->fh.size != fh->size) + faimdprintf(sess, 2, "hrm. ft->fh.size (%ld) != fh->size (%ld). um. using ft->fh.size\n", ft->fh.size, fh->size); + + if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILELISTINGREQ))) + ret = userfunc(sess, NULL, conn, fh); + + faimdprintf(sess, 2, "faim: get_command_rendezvous: hit end of 1209\n"); + + g_free(fh); + + return ret; +#else + return -1; +#endif +} + +static int handlehdr_getfile_listing3(aim_session_t *sess, aim_conn_t *conn, guint8 *hdr) +{ +#if 0 + struct aim_filetransfer_priv *ft; + struct aim_msgcookie_t *cook; + struct aim_fileheader_t *fh; + aim_rxcallback_t userfunc; + + fh = aim_oft_getfh(hdr); + + if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) { + g_free(fh); + return -1; + } + + g_free(fh); + + ft = cook->data; + + if (aim_cachecookie(sess, cook) == -1) + return -1; + + if ((userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILELISTINGRXCONFIRM))) + return userfunc(sess, NULL, conn); +#endif + return -1; +} + +static int handlehdr_getfile_request(aim_session_t *sess, aim_conn_t *conn, guint8 *hdr) +{ +#if 0 + struct aim_filetransfer_priv *ft; + struct aim_msgcookie_t *cook; + struct aim_fileheader_t *fh; + struct command_tx_struct *newoft; + int i = 0; + aim_rxcallback_t userfunc; + + fh = aim_oft_getfh(hdr); + + if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) { + g_free(fh); + return -1; + } + + ft = cook->data; + memcpy(&(ft->fh), fh, sizeof(struct aim_fileheader_t)); + g_free(fh); + + aim_cachecookie(sess, cook); + + faimdprintf(sess, 2, "faim: fileget: %s seems to want %s\n", ft->sn, ft->fh.name); + + if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILEREQ)) ) + i = userfunc(sess, NULL, conn, &(ft->fh), cook->cookie); + + if (i < 0) + return i; + + if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0101, 0))) { + faimdprintf(sess, 2, "faim: send_final_transfer: tx_new OFT failed\n"); + return -1; + } + + newoft->lock = 1; + memcpy(newoft->hdr.oft.magic, "OFT2", 4); + newoft->hdr.oft.hdr2len = 0x100 - 8; + + if (!(newoft->hdr.oft.hdr2 = calloc(1,newoft->hdr.oft.hdr2len))) { + aim_frame_destroy(newoft); + return -1; + } + + /* protocol BS: nrecvd, recvcsum to 0, flags to 0x20. */ + ft->fh.nrecvd = 0; + ft->fh.recvcsum = 0; + ft->fh.flags = 0x20; + + aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh)); + + newoft->lock = 0; + aim_tx_enqueue(sess, newoft); + + faimdprintf(sess, 2, "faim: OFT: OFT file header enqueued.\n"); + + return i; +#else + return -1; +#endif +} + +static int handlehdr_getfile_sending(aim_session_t *sess, aim_conn_t *conn, guint8 *hdr) +{ +#if 0 + struct aim_fileheader_t *fh; + struct aim_filetransfer_priv *ft; + struct aim_msgcookie_t *cook; + struct command_tx_struct *newoft; + aim_rxcallback_t userfunc; + + fh = aim_oft_getfh(hdr); + + if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) { + g_free(fh); + return -1; + } + + g_free(fh); + + ft = cook->data; + + ft->state = 3; + + if (aim_cachecookie(sess, cook) == -1) + return -1; + + faimdprintf(sess, 2, "faim: fileget: %s seems to want to send %s\n", ft->sn, ft->fh.name); + + if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0202, 0))) { + faimdprintf(sess, 2, "faim: send_final_transfer: tx_new OFT failed\n"); + return -1; + } + + newoft->lock = 1; + memcpy(newoft->hdr.oft.magic, "OFT2", 4); + + newoft->hdr.oft.hdr2len = 0x100 - 8; + + if (!(newoft->hdr.oft.hdr2 = calloc(1,newoft->hdr.oft.hdr2len))) { + aim_frame_destroy(newoft); + return -1; + } + + aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh)); + + newoft->lock = 0; + aim_tx_enqueue(sess, newoft); + + faimdprintf(sess, 2, "faim: OFT: OFT 0x0202 enqueued.\n"); + + if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILEREQ)) == NULL) + return 1; +#else + return -1; +#endif +} + +static int handlehdr_getfile_recv(aim_session_t *sess, aim_conn_t *conn, guint8 *hdr) +{ +#if 0 + struct aim_fileheader_t *fh; + struct aim_filetransfer_priv *ft; + struct aim_msgcookie_t *cook; + int ret = 1; + aim_rxcallback_t userfunc; + + fh = aim_oft_getfh(hdr); + + if (!(cook = aim_checkcookie(sess, fh->bcookie, AIM_COOKIETYPE_OFTGET))) { + g_free(fh); + return -1; + } + + ft = cook->data; + + faimdprintf(sess, 2, "faim: get_rend: looks like we're ready to send data.(oft 0x0202)\n"); + + if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILEFILESEND)) ) + ret = userfunc(sess, NULL, conn, fh); + + g_free(fh); + + return ret; +#else + return -1; +#endif +} + +static int handlehdr_getfile_finish(aim_session_t *sess, aim_conn_t *conn, guint8 *hdr) +{ +#if 0 + struct aim_fileheader_t *fh; + aim_rxcallback_t userfunc; + + fh = aim_oft_getfh(hdr); + + faimdprintf(sess, 2, "faim: get_rend: looks like we're done with a transfer (oft 0x0204)\n"); + + if ( (userfunc = aim_callhandler(sess, conn, AIM_CB_FAM_OFT, AIM_CB_OFT_GETFILECOMPLETE)) ) + userfunc(sess, NULL, conn, fh); + + g_free(fh); +#endif + + return -1; +} + +/** + * aim_get_command_rendezvous - OFT equivalent of aim_get_command + * @sess: session to work on + * @conn: conn to pull data from + * + * this reads and handles data from conn->fd. currently a little rough + * around the edges + */ +int aim_get_command_rendezvous(aim_session_t *sess, aim_conn_t *conn) +{ + guint8 hdrbuf1[6]; + guint8 *hdr = NULL; + int hdrlen, hdrtype; + int ret = -1; + + if (!sess || !conn) + return -1; + + memset(hdrbuf1, 0, sizeof(hdrbuf1)); + + /* I guess? I didn't understand any of that mess... */ + if (conn->subtype == AIM_CONN_SUBTYPE_OFT_GETFILE) + return getcommand_getfile(sess, conn); + + /* XXX fix all the error cases here */ + if (aim_recv(conn->fd, hdrbuf1, 6) < 6) { + + do_error_dialog(sess->aux_data, "read error", "Gaim"); + + aim_conn_close(conn); + + return -1; + } + + hdrlen = aimutil_get16(hdrbuf1+4); + hdrlen -= 6; + + hdr = g_malloc(hdrlen); + + if (aim_recv(conn->fd, hdr, hdrlen) < hdrlen) { + do_error_dialog(sess->aux_data, "read error", "Gaim"); + g_free(hdr); + aim_conn_close(conn); + return -1; + } + + hdrtype = aimutil_get16(hdr); + + if (hdrtype == 0x0001) + ret = handlehdr_directim(sess, conn, hdr); + else if (hdrtype == 0x1108) /* getfile listing.txt incoming tx->rx */ + ret = handlehdr_getfile_listing(sess, conn, hdr); + else if (hdrtype == 0x1209) /* get file listing ack rx->tx */ + ret = handlehdr_getfile_listing2(sess, conn, hdr); + else if (hdrtype == 0x120b) /* get file listing rx confirm */ + ret = handlehdr_getfile_listing3(sess, conn, hdr); + else if (hdrtype == 0x120c) /* getfile request */ + ret = handlehdr_getfile_request(sess, conn, hdr); + else if (hdrtype == 0x0101) /* getfile sending data */ + ret = handlehdr_getfile_sending(sess, conn, hdr); + else if (hdrtype == 0x0202) /* getfile recv data */ + ret = handlehdr_getfile_recv(sess, conn, hdr); + else if (hdrtype == 0x0204) /* getfile finished */ + ret = handlehdr_getfile_finish(sess, conn, hdr); + else { + ret = -1; + } + + g_free(hdr); + + if (ret == -1) + aim_conn_close(conn); + + return ret; +} + +#if 0 +/** + * aim_oft_getfh - extracts an &aim_fileheader_t from buffer hdr. + * @hdr: buffer to extract header from + * + * returns pointer to new struct on success; %NULL on error. + * + */ +static struct aim_fileheader_t *aim_oft_getfh(unsigned char *hdr) +{ + struct aim_fileheader_t *fh; + int i, j; + if (!(fh = calloc(1, sizeof(struct aim_fileheader_t)))) + return NULL; + + /* [0] and [1] are the type. we can ignore those here. */ + i = 2; + for(j = 0; j < 8; j++, i++) + fh->bcookie[j] = hdr[i]; + fh->encrypt = aimutil_get16(hdr+i); + i += 2; + fh->compress = aimutil_get16(hdr+i); + i += 2; + fh->totfiles = aimutil_get16(hdr+i); + i += 2; + fh->filesleft = aimutil_get16(hdr+i); + i += 2; + fh->totparts = aimutil_get16(hdr+i); + i += 2; + fh->partsleft = aimutil_get16(hdr+i); + i += 2; + fh->totsize = aimutil_get32(hdr+i); + i += 4; + fh->size = aimutil_get32(hdr+i); + i += 4; + fh->modtime = aimutil_get32(hdr+i); + i += 4; + fh->checksum = aimutil_get32(hdr+i); + i += 4; + fh->rfrcsum = aimutil_get32(hdr+i); + i += 4; + fh->rfsize = aimutil_get32(hdr+i); + i += 4; + fh->cretime = aimutil_get32(hdr+i); + i += 4; + fh->rfcsum = aimutil_get32(hdr+i); + i += 4; + fh->nrecvd = aimutil_get32(hdr+i); + i += 4; + fh->recvcsum = aimutil_get32(hdr+i); + i += 4; + memcpy(fh->idstring, hdr+i, 32); + i += 32; + fh->flags = aimutil_get8(hdr+i); + i += 1; + fh->lnameoffset = aimutil_get8(hdr+i); + i += 1; + fh->lsizeoffset = aimutil_get8(hdr+i); + i += 1; + memcpy(fh->dummy, hdr+i, 69); + i += 69; + memcpy(fh->macfileinfo, hdr+i, 16); + i += 16; + fh->nencode = aimutil_get16(hdr+i); + i += 2; + fh->nlanguage = aimutil_get16(hdr+i); + i += 2; + memcpy(fh->name, hdr+i, 64); + i += 64; + return fh; +} +#endif + +/** + * aim_oft_checksum - calculate oft checksum of buffer + * @buffer: buffer of data to checksum + * @bufsize: size of buffer + * @checksum: pointer to integer to place result in (pointer!) + * + * + * Note that checksum is a pointer. Checksum should be filled with + * 0xFFFF0000 for each new file; you can have this checksum chunks of + * files in series if you just call it repeatedly in a for(; ; ) loop + * and don't reset the checksum between each call. And you thought we + * didn't care about you and your pathetic client's meomry footprint + * ;^) + * + * + * Also, it's been said that this is incorrect as currently + * written. You were warned. + */ +guint32 aim_oft_checksum(aim_session_t *sess, const char *buffer, int bufsize, guint32 *checksum) +{ + return 0xdeadbeef; +#if 0 + guint16 check0, check1; + int i; + + check0 = ((*checksum & 0xFF000000) >> 16); + check1 = ((*checksum & 0x00ff0000) >> 16); + for(i = 0; i < bufsize; i++) { + if (i % 2) { /* use check1 -- second byte */ + if ( (short)buffer[i] > check1 ) { /* wrapping */ + check1 += 0x100; /* this is a cheap way to wrap */ + + /* if we're wrapping, decrement the other one */ + /* XXX: check this corner case */ + if (check0 == 0) + check0 = 0x00ff; + else + check0--; + } + check1 -= buffer[i]; + } else { /* use check0 -- first byte */ + if ( (short)buffer[i] > check0 ) { /* wrapping */ + check0 += 0x100; /* this is a cheap way to wrap */ + + /* if we're wrapping, decrement the other one */ + /* XXX: check this corner case */ + if (check1 == 0) + check1 = 0x00ff; + else + check1--; + } + check0 -= buffer[i]; + } + } + + if (check0 > 0xff || check1 > 0xff) { + /* they shouldn't be able to do this. error! */ + faimdprintf(sess, 2, "check0 or check1 is too high: 0x%04x, 0x%04x\n", check0, check1); + return -1; + } + + /* grab just the lowest byte; this should be clean, but just in + case */ + check0 &= 0xff; + check1 &= 0xff; + + *checksum = ((check0 * 0x1000000) + (check1 * 0x10000)); + return *checksum; +#endif +} + +#if 0 +/** + * aim_oft_buildheader - fills a buffer with network-order fh data + * @dest: buffer to fill -- pre-alloced + * @fh: fh to get data from + * + * returns length written; -1 on error. + * DOES NOT DO BOUNDS CHECKING! + * + */ +static int oft_buildheader(unsigned char *dest, struct aim_fileheader_t *fh) +{ + int i, curbyte; + if (!dest || !fh) + return -1; + curbyte = 0; + for(i = 0; i < 8; i++) + curbyte += aimutil_put8(dest+curbyte, fh->bcookie[i]); + curbyte += aimutil_put16(dest+curbyte, fh->encrypt); + curbyte += aimutil_put16(dest+curbyte, fh->compress); + curbyte += aimutil_put16(dest+curbyte, fh->totfiles); + curbyte += aimutil_put16(dest+curbyte, fh->filesleft); + curbyte += aimutil_put16(dest+curbyte, fh->totparts); + curbyte += aimutil_put16(dest+curbyte, fh->partsleft); + curbyte += aimutil_put32(dest+curbyte, fh->totsize); + curbyte += aimutil_put32(dest+curbyte, fh->size); + curbyte += aimutil_put32(dest+curbyte, fh->modtime); + curbyte += aimutil_put32(dest+curbyte, fh->checksum); + curbyte += aimutil_put32(dest+curbyte, fh->rfrcsum); + curbyte += aimutil_put32(dest+curbyte, fh->rfsize); + curbyte += aimutil_put32(dest+curbyte, fh->cretime); + curbyte += aimutil_put32(dest+curbyte, fh->rfcsum); + curbyte += aimutil_put32(dest+curbyte, fh->nrecvd); + curbyte += aimutil_put32(dest+curbyte, fh->recvcsum); + memcpy(dest+curbyte, fh->idstring, 32); + curbyte += 32; + curbyte += aimutil_put8(dest+curbyte, fh->flags); + curbyte += aimutil_put8(dest+curbyte, fh->lnameoffset); + curbyte += aimutil_put8(dest+curbyte, fh->lsizeoffset); + memcpy(dest+curbyte, fh->dummy, 69); + curbyte += 69; + memcpy(dest+curbyte, fh->macfileinfo, 16); + curbyte += 16; + curbyte += aimutil_put16(dest+curbyte, fh->nencode); + curbyte += aimutil_put16(dest+curbyte, fh->nlanguage); + memset(dest+curbyte, 0x00, 64); + memcpy(dest+curbyte, fh->name, 64); + + /* XXX: Filenames longer than 64B */ + curbyte += 64; + return curbyte; +} +#endif + +/** + * aim_getfile_intitiate - Request an OFT getfile session + * @sess: your session, + * @conn: the BOS conn, + * @destsn is the SN to connect to. + * + * returns a new &aim_conn_t on success, %NULL on error + */ +aim_conn_t *aim_getfile_initiate(aim_session_t *sess, aim_conn_t *conn, const char *destsn) +{ + return NULL; +#if 0 + struct command_tx_struct *newpacket; + struct aim_conn_t *newconn; + struct aim_filetransfer_priv *priv; + struct aim_msgcookie_t *cookie; + int curbyte, i, listenfd; + short port = 4443; + struct hostent *hptr; + struct utsname myname; + char cap[16]; + char d[4]; + + /* Open our socket */ + + if ( (listenfd = aim_listenestablish(port)) == -1) + return NULL; + + /* get our local IP */ + + if (uname(&myname) < 0) + return NULL; + if ( (hptr = gethostbyname(myname.nodename)) == NULL) + return NULL; + memcpy(&d, hptr->h_addr_list[0], 4); + + aim_putcap(cap, 16, AIM_CAPS_GETFILE); + + /* create the OSCAR packet */ + + if (!(newpacket = aim_tx_new(sess, conn, AIM_FRAMETYPE_OSCAR, 0x0002, 10+8+2+1+strlen(destsn)+4+4+0x42))) + return NULL; + newpacket->lock = 1; + + /* lock struct */ + curbyte = 0; + curbyte += aim_putsnac(newpacket->data+curbyte, 0x0004, 0x0006, 0x0000, sess->snac_nextid); + + /* XXX: check the cookie before commiting to using it */ + + /* Generate a random message cookie + * This cookie needs to be alphanumeric and NULL-terminated to be TOC-compatible. */ + for (i=0; i<7; i++) + curbyte += aimutil_put8(newpacket->data+curbyte, 0x30 + ((u_char) random() % 10)); + + curbyte += aimutil_put8(newpacket->data+curbyte, 0x00); + + /* grab all the data for cookie caching. */ + + if (!(cookie = (struct aim_msgcookie_t *)calloc(1, sizeof(struct aim_msgcookie_t)))) + return NULL; + memcpy(cookie->cookie, newpacket->data+curbyte-8, 8); + cookie->type = AIM_COOKIETYPE_OFTGET; + + if (!(priv = (struct aim_filetransfer_priv *)calloc(1, sizeof(struct aim_filetransfer_priv)))) + return NULL; + memcpy(priv->cookie, cookie, 8); + memcpy(priv->sn, destsn, sizeof(priv->sn)); + memcpy(priv->fh.name, "listing.txt", strlen("listing.txt")); + priv->state = 1; + + cookie->data = priv; + + aim_cachecookie(sess, cookie); + + /* Channel ID */ + curbyte += aimutil_put16(newpacket->data+curbyte,0x0002); + + /* Destination SN (prepended with byte length) */ + curbyte += aimutil_put8(newpacket->data+curbyte,strlen(destsn)); + curbyte += aimutil_putstr(newpacket->data+curbyte, destsn, strlen(destsn)); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0003); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); + + /* enTLV start */ + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0042); + + /* Flag data / ICBM Parameters? */ + curbyte += aimutil_put8(newpacket->data+curbyte, 0x00); + curbyte += aimutil_put8(newpacket->data+curbyte, 0x00); + + /* Cookie */ + curbyte += aimutil_putstr(newpacket->data+curbyte, (char *)cookie, 8); + + /* Capability String */ + curbyte += aimutil_putstr(newpacket->data+curbyte, (char *)cap, 0x10); + + /* 000a/0002 : 0001 */ + curbyte += aimutil_put16(newpacket->data+curbyte, 0x000a); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0001); + + /* 0003/0004: IP address */ + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0003); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0004); + for(i = 0; i < 4; i++) + curbyte += aimutil_put8(newpacket->data+curbyte, d[i]); + + /* already in network byte order */ + + /* 0005/0002: Port */ + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0005); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0002); + curbyte += aimutil_put16(newpacket->data+curbyte, port); + + /* 000f/0000: ?? */ + curbyte += aimutil_put16(newpacket->data+curbyte, 0x000f); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x0000); + + /* 2711/000c: ?? */ + curbyte += aimutil_put16(newpacket->data+curbyte, 0x2711); + curbyte += aimutil_put16(newpacket->data+curbyte, 0x000c); + curbyte += aimutil_put32(newpacket->data+curbyte, 0x00120001); + + for(i = 0; i < 0x000c - 4; i++) + curbyte += aimutil_put8(newpacket->data+curbyte, 0x00); + + newpacket->commandlen = curbyte; + newpacket->lock = 0; + aim_tx_enqueue(sess, newpacket); + + /* allocate and set up our connection */ + + i = fcntl(listenfd, F_GETFL, 0); + fcntl(listenfd, F_SETFL, i | O_NONBLOCK); + newconn = aim_newconn(sess, AIM_CONN_TYPE_RENDEZVOUS_OUT, NULL); + + if (!newconn){ + perror("aim_newconn"); + return NULL; + } + + newconn->fd = listenfd; + newconn->subtype = AIM_CONN_SUBTYPE_OFT_GETFILE; + newconn->priv = priv; + + return newconn; +#endif +} + +/** + * aim_oft_getfile_request - request a particular file over an established getfile connection + * @sess: your session + * @conn: the established OFT getfile connection + * @name: filename to request + * @size: size of the file + * + * + * returns -1 on error, 0 on successful enqueuing + */ +int aim_oft_getfile_request(aim_session_t *sess, aim_conn_t *conn, const char *name, int size) +{ + return -EINVAL; +#if 0 + struct command_tx_struct *newoft; + struct aim_filetransfer_priv *ft; + if (!sess || !conn || !conn->priv || !name) + return -1; + + if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x120c, 0))) { + faimdprintf(sess, 2, "faim: aim_accepttransfer: tx_new OFT failed\n"); + return -1; + } + + newoft->lock = 1; + + memcpy(newoft->hdr.oft.magic, "OFT2", 4); + newoft->hdr.oft.hdr2len = 0x100 - 8; + + ft = (struct aim_filetransfer_priv *)conn->priv; + ft->fh.filesleft = 1; + ft->fh.totfiles = 1; + ft->fh.totparts = 1; + ft->fh.partsleft = 1; + ft->fh.totsize = size; + ft->fh.size = size; + ft->fh.checksum = 0; + memcpy(ft->fh.name, name, strlen(name)); + memset(ft->fh.name+strlen(name), 0, 1); + + if (!(newoft->hdr.oft.hdr2 = (unsigned char *)calloc(1,newoft->hdr.oft.hdr2len))) { + newoft->lock = 0; + aim_frame_destroy(newoft); + return -1; + } + + if (!(aim_oft_buildheader(newoft->hdr.oft.hdr2, &(ft->fh)))) { + newoft->lock = 0; + aim_frame_destroy(newoft); + return -1; + } + + newoft->lock = 0; + + aim_tx_enqueue(sess, newoft); + return 0; +#endif +} + +/** + * aim_oft_getfile_ack - acknowledge a getfile download as complete + * @sess: your session + * @conn: the getfile conn to send the ack over + * + * Call this function after you have read all the data in a particular + * filetransfer. Returns -1 on error, 0 on apparent success + * + */ +int aim_oft_getfile_ack(aim_session_t *sess, aim_conn_t *conn) +{ + return -EINVAL; +#if 0 + struct command_tx_struct *newoft; + struct aim_filetransfer_priv *ft; + + if (!sess || !conn || !conn->priv) + return -1; + + if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0202, 0))) { + faimdprintf(sess, 2, "faim: aim_accepttransfer: tx_new OFT failed\n"); + return -1; + } + + newoft->lock = 1; + + memcpy(newoft->hdr.oft.magic, "OFT2", 4); + newoft->hdr.oft.hdr2len = 0x100-8; + + if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) { + newoft->lock = 0; + aim_frame_destroy(newoft); + return -1; + } + + ft = (struct aim_filetransfer_priv *)conn->priv; + + if (!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh)))) { + newoft->lock = 0; + aim_frame_destroy(newoft); + return -1; + } + + newoft->lock = 0; + aim_tx_enqueue(sess, newoft); + return 0; +#endif +} + +/** + * aim_oft_getfile_end - end a getfile. + * @sess: your session + * @conn: the getfile connection + * + * call this before you close the getfile connection if you're on the + * receiving/requesting end. + */ +int aim_oft_getfile_end(aim_session_t *sess, aim_conn_t *conn) +{ + return -EINVAL; +#if 0 + struct command_tx_struct *newoft; + struct aim_filetransfer_priv *ft; + + if (!sess || !conn || !conn->priv) + return -1; + + if (!(newoft = aim_tx_new(sess, conn, AIM_FRAMETYPE_OFT, 0x0204, 0))) { + faimdprintf(sess, 2, "faim: aim_accepttransfer: tx_new OFT failed\n"); + return -1; + } + + newoft->lock = 1; + + memcpy(newoft->hdr.oft.magic, "OFT2", 4); + newoft->hdr.oft.hdr2len = 0x100 - 8; + + if (!(newoft->hdr.oft.hdr2 = (char *)calloc(1,newoft->hdr.oft.hdr2len))) { + newoft->lock = 0; + aim_frame_destroy(newoft); + return -1; + } + + ft = (struct aim_filetransfer_priv *)conn->priv; + ft->state = 4; /* no longer wanting data */ + ft->fh.nrecvd = ft->fh.size; + ft->fh.recvcsum = ft->fh.checksum; + ft->fh.flags = 0x21; + + if (!(aim_oft_buildheader((unsigned char *)newoft->hdr.oft.hdr2, &(ft->fh)))) { + newoft->lock = 0; + aim_frame_destroy(newoft); + return -1; + } + + newoft->lock = 0; + aim_tx_enqueue(sess, newoft); + + return 0; +#endif /* 0 */ +} + diff --git a/protocols/oscar/ft.h b/protocols/oscar/ft.h new file mode 100644 index 00000000..7b1142eb --- /dev/null +++ b/protocols/oscar/ft.h @@ -0,0 +1,78 @@ +#ifndef __OSCAR_FT_H__ +#define __OSCAR_FT_H__ + +#define AIM_CB_FAM_OFT 0xfffe /* OFT/Rvous */ + +/* + * OFT Services + * + * See non-SNAC note below. + */ +#define AIM_CB_OFT_DIRECTIMCONNECTREQ 0x0001/* connect request -- actually an OSCAR CAP*/ +#define AIM_CB_OFT_DIRECTIMINCOMING 0x0002 +#define AIM_CB_OFT_DIRECTIMDISCONNECT 0x0003 +#define AIM_CB_OFT_DIRECTIMTYPING 0x0004 +#define AIM_CB_OFT_DIRECTIMINITIATE 0x0005 + +#define AIM_CB_OFT_GETFILECONNECTREQ 0x0006 /* connect request -- actually an OSCAR CAP*/ +#define AIM_CB_OFT_GETFILELISTINGREQ 0x0007 /* OFT listing.txt request */ +#define AIM_CB_OFT_GETFILEFILEREQ 0x0008 /* received file request */ +#define AIM_CB_OFT_GETFILEFILESEND 0x0009 /* received file request confirm -- send data */ +#define AIM_CB_OFT_GETFILECOMPLETE 0x000a /* received file send complete*/ +#define AIM_CB_OFT_GETFILEINITIATE 0x000b /* request for file get acknowledge */ +#define AIM_CB_OFT_GETFILEDISCONNECT 0x000c /* OFT connection disconnected.*/ +#define AIM_CB_OFT_GETFILELISTING 0x000d /* OFT listing.txt received.*/ +#define AIM_CB_OFT_GETFILERECEIVE 0x000e /* OFT file incoming.*/ +#define AIM_CB_OFT_GETFILELISTINGRXCONFIRM 0x000f +#define AIM_CB_OFT_GETFILESTATE4 0x0010 + +#define AIM_CB_OFT_SENDFILEDISCONNECT 0x0020 /* OFT connection disconnected.*/ + +struct aim_fileheader_t { +#if 0 + char magic[4]; /* 0 */ + short hdrlen; /* 4 */ + short hdrtype; /* 6 */ +#endif + char bcookie[8]; /* 8 */ + short encrypt; /* 16 */ + short compress; /* 18 */ + short totfiles; /* 20 */ + short filesleft; /* 22 */ + short totparts; /* 24 */ + short partsleft; /* 26 */ + long totsize; /* 28 */ + long size; /* 32 */ + long modtime; /* 36 */ + long checksum; /* 40 */ + long rfrcsum; /* 44 */ + long rfsize; /* 48 */ + long cretime; /* 52 */ + long rfcsum; /* 56 */ + long nrecvd; /* 60 */ + long recvcsum; /* 64 */ + char idstring[32]; /* 68 */ + char flags; /* 100 */ + char lnameoffset; /* 101 */ + char lsizeoffset; /* 102 */ + char dummy[69]; /* 103 */ + char macfileinfo[16]; /* 172 */ + short nencode; /* 188 */ + short nlanguage; /* 190 */ + char name[64]; /* 192 */ + /* 256 */ +}; + + + +struct aim_filetransfer_priv { + char sn[MAXSNLEN]; + guint8 cookie[8]; + char ip[30]; + int state; + struct aim_fileheader_t fh; +}; + +#define AIM_CB_FAM_OFT 0xfffe /* OFT/Rvous */ + +#endif /* __OSCAR_FT_H__ */ diff --git a/protocols/oscar/icq.c b/protocols/oscar/icq.c new file mode 100644 index 00000000..23959b75 --- /dev/null +++ b/protocols/oscar/icq.c @@ -0,0 +1,441 @@ +/* + * Encapsulated ICQ. + * + */ + +#include <aim.h> +#include "icq.h" + +int aim_icq_reqofflinemsgs(aim_session_t *sess) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + int bslen; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015))) + return -EINVAL; + + bslen = 2 + 4 + 2 + 2; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid); + + /* For simplicity, don't bother using a tlvlist */ + aimbs_put16(&fr->data, 0x0001); + aimbs_put16(&fr->data, bslen); + + aimbs_putle16(&fr->data, bslen - 2); + aimbs_putle32(&fr->data, atoi(sess->sn)); + aimbs_putle16(&fr->data, 0x003c); /* I command thee. */ + aimbs_putle16(&fr->data, snacid); /* eh. */ + + aim_tx_enqueue(sess, fr); + + return 0; +} + +int aim_icq_ackofflinemsgs(aim_session_t *sess) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + int bslen; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015))) + return -EINVAL; + + bslen = 2 + 4 + 2 + 2; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid); + + /* For simplicity, don't bother using a tlvlist */ + aimbs_put16(&fr->data, 0x0001); + aimbs_put16(&fr->data, bslen); + + aimbs_putle16(&fr->data, bslen - 2); + aimbs_putle32(&fr->data, atoi(sess->sn)); + aimbs_putle16(&fr->data, 0x003e); /* I command thee. */ + aimbs_putle16(&fr->data, snacid); /* eh. */ + + aim_tx_enqueue(sess, fr); + + return 0; +} + +int aim_icq_sendxmlreq(aim_session_t *sess, const char *xml) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + int bslen; + + if (!xml || !strlen(xml)) + return -EINVAL; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015))) + return -EINVAL; + + bslen = 2 + 10 + 2 + strlen(xml) + 1; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid); + + /* For simplicity, don't bother using a tlvlist */ + aimbs_put16(&fr->data, 0x0001); + aimbs_put16(&fr->data, bslen); + + aimbs_putle16(&fr->data, bslen - 2); + aimbs_putle32(&fr->data, atoi(sess->sn)); + aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */ + aimbs_putle16(&fr->data, snacid); /* eh. */ + aimbs_putle16(&fr->data, 0x0998); /* shrug. */ + aimbs_putle16(&fr->data, strlen(xml) + 1); + aimbs_putraw(&fr->data, (guint8 *)xml, strlen(xml) + 1); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +int aim_icq_getallinfo(aim_session_t *sess, const char *uin) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + int bslen; + struct aim_icq_info *info; + + if (!uin || uin[0] < '0' || uin[0] > '9') + return -EINVAL; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015))) + return -EINVAL; + + bslen = 2 + 4 + 2 + 2 + 2 + 4; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid); + + /* For simplicity, don't bother using a tlvlist */ + aimbs_put16(&fr->data, 0x0001); + aimbs_put16(&fr->data, bslen); + + aimbs_putle16(&fr->data, bslen - 2); + aimbs_putle32(&fr->data, atoi(sess->sn)); + aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */ + aimbs_putle16(&fr->data, snacid); /* eh. */ + aimbs_putle16(&fr->data, 0x04b2); /* shrug. */ + aimbs_putle32(&fr->data, atoi(uin)); + + aim_tx_enqueue(sess, fr); + + /* Keep track of this request and the ICQ number and request ID */ + info = g_new0(struct aim_icq_info, 1); + info->reqid = snacid; + info->uin = atoi(uin); + info->next = sess->icq_info; + sess->icq_info = info; + + return 0; +} + +int aim_icq_getsimpleinfo(aim_session_t *sess, const char *uin) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + int bslen; + + if (!uin || uin[0] < '0' || uin[0] > '9') + return -EINVAL; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0015))) + return -EINVAL; + + bslen = 2 + 4 + 2 + 2 + 2 + 4; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 4 + bslen))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0015, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0015, 0x0002, 0x0000, snacid); + + /* For simplicity, don't bother using a tlvlist */ + aimbs_put16(&fr->data, 0x0001); + aimbs_put16(&fr->data, bslen); + + aimbs_putle16(&fr->data, bslen - 2); + aimbs_putle32(&fr->data, atoi(sess->sn)); + aimbs_putle16(&fr->data, 0x07d0); /* I command thee. */ + aimbs_putle16(&fr->data, snacid); /* eh. */ + aimbs_putle16(&fr->data, 0x051f); /* shrug. */ + aimbs_putle32(&fr->data, atoi(uin)); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +static void aim_icq_freeinfo(struct aim_icq_info *info) { + int i; + + if (!info) + return; + g_free(info->nick); + g_free(info->first); + g_free(info->last); + g_free(info->email); + g_free(info->homecity); + g_free(info->homestate); + g_free(info->homephone); + g_free(info->homefax); + g_free(info->homeaddr); + g_free(info->mobile); + g_free(info->homezip); + g_free(info->personalwebpage); + if (info->email2) + for (i = 0; i < info->numaddresses; i++) + g_free(info->email2[i]); + g_free(info->email2); + g_free(info->workcity); + g_free(info->workstate); + g_free(info->workphone); + g_free(info->workfax); + g_free(info->workaddr); + g_free(info->workzip); + g_free(info->workcompany); + g_free(info->workdivision); + g_free(info->workposition); + g_free(info->workwebpage); + g_free(info->info); + g_free(info); +} + +/** + * Subtype 0x0003 - Response to 0x0015/0x002, contains an ICQesque packet. + */ +static int icqresponse(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + aim_tlvlist_t *tl; + aim_tlv_t *datatlv; + aim_bstream_t qbs; + guint32 ouruin; + guint16 cmdlen, cmd, reqid; + + if (!(tl = aim_readtlvchain(bs)) || !(datatlv = aim_gettlv(tl, 0x0001, 1))) { + aim_freetlvchain(&tl); + do_error_dialog(sess->aux_data, "corrupt ICQ response\n", "Gaim"); + return 0; + } + + aim_bstream_init(&qbs, datatlv->value, datatlv->length); + + cmdlen = aimbs_getle16(&qbs); + ouruin = aimbs_getle32(&qbs); + cmd = aimbs_getle16(&qbs); + reqid = aimbs_getle16(&qbs); + + if (cmd == 0x0041) { /* offline message */ + guint16 msglen; + struct aim_icq_offlinemsg msg; + aim_rxcallback_t userfunc; + + memset(&msg, 0, sizeof(msg)); + + msg.sender = aimbs_getle32(&qbs); + msg.year = aimbs_getle16(&qbs); + msg.month = aimbs_getle8(&qbs); + msg.day = aimbs_getle8(&qbs); + msg.hour = aimbs_getle8(&qbs); + msg.minute = aimbs_getle8(&qbs); + msg.type = aimbs_getle16(&qbs); + msglen = aimbs_getle16(&qbs); + msg.msg = aimbs_getstr(&qbs, msglen); + + if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSG))) + ret = userfunc(sess, rx, &msg); + + g_free(msg.msg); + + } else if (cmd == 0x0042) { + aim_rxcallback_t userfunc; + + if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSGCOMPLETE))) + ret = userfunc(sess, rx); + } else if (cmd == 0x07da) { /* information */ + guint16 subtype; + struct aim_icq_info *info; + aim_rxcallback_t userfunc; + + subtype = aimbs_getle16(&qbs); + aim_bstream_advance(&qbs, 1); /* 0x0a */ + + /* find another data from the same request */ + for (info = sess->icq_info; info && (info->reqid != reqid); info = info->next); + + if (!info) { + info = g_new0(struct aim_icq_info, 1); + info->reqid = reqid; + info->next = sess->icq_info; + sess->icq_info = info; + } + + switch (subtype) { + case 0x00a0: { /* hide ip status */ + /* nothing */ + } break; + case 0x00aa: { /* password change status */ + /* nothing */ + } break; + case 0x00c8: { /* general and "home" information */ + info->nick = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->first = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->last = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->email = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->homecity = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->homestate = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->homephone = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->homefax = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->homeaddr = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->mobile = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->homezip = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->homecountry = aimbs_getle16(&qbs); + /* 0x0a 00 02 00 */ + /* 1 byte timezone? */ + /* 1 byte hide email flag? */ + } break; + case 0x00dc: { /* personal information */ + info->age = aimbs_getle8(&qbs); + info->unknown = aimbs_getle8(&qbs); + info->gender = aimbs_getle8(&qbs); + info->personalwebpage = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->birthyear = aimbs_getle16(&qbs); + info->birthmonth = aimbs_getle8(&qbs); + info->birthday = aimbs_getle8(&qbs); + info->language1 = aimbs_getle8(&qbs); + info->language2 = aimbs_getle8(&qbs); + info->language3 = aimbs_getle8(&qbs); + /* 0x00 00 01 00 00 01 00 00 00 00 00 */ + } break; + case 0x00d2: { /* work information */ + info->workcity = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->workstate = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->workphone = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->workfax = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->workaddr = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->workzip = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->workcountry = aimbs_getle16(&qbs); + info->workcompany = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->workdivision = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->workposition = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + aim_bstream_advance(&qbs, 2); /* 0x01 00 */ + info->workwebpage = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + } break; + case 0x00e6: { /* additional personal information */ + info->info = aimbs_getstr(&qbs, aimbs_getle16(&qbs)-1); + } break; + case 0x00eb: { /* email address(es) */ + int i; + info->numaddresses = aimbs_getle16(&qbs); + info->email2 = g_new0(char *, info->numaddresses); + for (i = 0; i < info->numaddresses; i++) { + info->email2[i] = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + if (i+1 != info->numaddresses) + aim_bstream_advance(&qbs, 1); /* 0x00 */ + } + } break; + case 0x00f0: { /* personal interests */ + } break; + case 0x00fa: { /* past background and current organizations */ + } break; + case 0x0104: { /* alias info */ + info->nick = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->first = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->last = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + aim_bstream_advance(&qbs, aimbs_getle16(&qbs)); + /* email address? */ + /* Then 0x00 02 00 */ + } break; + case 0x010e: { /* unknown */ + /* 0x00 00 */ + } break; + + case 0x019a: { /* simple info */ + aim_bstream_advance(&qbs, 2); + info->uin = aimbs_getle32(&qbs); + info->nick = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->first = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->last = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + info->email = aimbs_getstr(&qbs, aimbs_getle16(&qbs)); + /* Then 0x00 02 00 00 00 00 00 */ + } break; + } /* End switch statement */ + + + if (!(snac->flags & 0x0001)) { + if (subtype != 0x0104) + if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_INFO))) + ret = userfunc(sess, rx, info); + + /* Bitlbee - not supported, yet + if (info->uin && info->nick) + if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_ALIAS))) + ret = userfunc(sess, rx, info); + */ + + if (sess->icq_info == info) { + sess->icq_info = info->next; + } else { + struct aim_icq_info *cur; + for (cur=sess->icq_info; (cur->next && (cur->next!=info)); cur=cur->next); + if (cur->next) + cur->next = cur->next->next; + } + aim_icq_freeinfo(info); + } + } + + aim_freetlvchain(&tl); + + return ret; +} + +static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + + if (snac->subtype == 0x0003) + return icqresponse(sess, mod, rx, snac, bs); + + return 0; +} + +int icq_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x0015; + mod->version = 0x0001; + mod->toolid = 0x0110; + mod->toolversion = 0x047c; + mod->flags = 0; + strncpy(mod->name, "icq", sizeof(mod->name)); + mod->snachandler = snachandler; + + return 0; +} + + diff --git a/protocols/oscar/icq.h b/protocols/oscar/icq.h new file mode 100644 index 00000000..c496f39c --- /dev/null +++ b/protocols/oscar/icq.h @@ -0,0 +1,98 @@ +#ifndef __OSCAR_ICQ_H__ +#define __OSCAR_ICQ_H__ + +#define AIM_CB_FAM_ICQ 0x0015 + +/* + * SNAC Family: ICQ + * + * Most of these are actually special. + */ +#define AIM_CB_ICQ_ERROR 0x0001 +#define AIM_CB_ICQ_OFFLINEMSG 0x00f0 +#define AIM_CB_ICQ_OFFLINEMSGCOMPLETE 0x00f1 +#define AIM_CB_ICQ_SIMPLEINFO 0x00f2 +#define AIM_CB_ICQ_INFO 0x00f2 /* just transitional */ +#define AIM_CB_ICQ_DEFAULT 0xffff + +struct aim_icq_offlinemsg { + guint32 sender; + guint16 year; + guint8 month, day, hour, minute; + guint16 type; + char *msg; +}; + +struct aim_icq_simpleinfo { + guint32 uin; + char *nick; + char *first; + char *last; + char *email; +}; + +struct aim_icq_info { + gushort reqid; + + /* simple */ + guint32 uin; + + /* general and "home" information (0x00c8) */ + char *nick; + char *first; + char *last; + char *email; + char *homecity; + char *homestate; + char *homephone; + char *homefax; + char *homeaddr; + char *mobile; + char *homezip; + gushort homecountry; +/* guchar timezone; + guchar hideemail; */ + + /* personal (0x00dc) */ + guchar age; + guchar unknown; + guchar gender; + char *personalwebpage; + gushort birthyear; + guchar birthmonth; + guchar birthday; + guchar language1; + guchar language2; + guchar language3; + + /* work (0x00d2) */ + char *workcity; + char *workstate; + char *workphone; + char *workfax; + char *workaddr; + char *workzip; + gushort workcountry; + char *workcompany; + char *workdivision; + char *workposition; + char *workwebpage; + + /* additional personal information (0x00e6) */ + char *info; + + /* email (0x00eb) */ + gushort numaddresses; + char **email2; + + /* we keep track of these in a linked list because we're 1337 */ + struct aim_icq_info *next; +}; + + +int aim_icq_reqofflinemsgs(aim_session_t *sess); +int aim_icq_ackofflinemsgs(aim_session_t *sess); +int aim_icq_getallinfo(aim_session_t *sess, const char *uin); +int aim_icq_getsimpleinfo(aim_session_t *sess, const char *uin); + +#endif /* __OSCAR_ICQ_H__ */ diff --git a/protocols/oscar/im.c b/protocols/oscar/im.c new file mode 100644 index 00000000..99661846 --- /dev/null +++ b/protocols/oscar/im.c @@ -0,0 +1,2029 @@ +/* + * aim_im.c + * + * The routines for sending/receiving Instant Messages. + * + * Note the term ICBM (Inter-Client Basic Message) which blankets + * all types of genericly routed through-server messages. Within + * the ICBM types (family 4), a channel is defined. Each channel + * represents a different type of message. Channel 1 is used for + * what would commonly be called an "instant message". Channel 2 + * is used for negotiating "rendezvous". These transactions end in + * something more complex happening, such as a chat invitation, or + * a file transfer. + * + * In addition to the channel, every ICBM contains a cookie. For + * standard IMs, these are only used for error messages. However, + * the more complex rendezvous messages make suitably more complex + * use of this field. + * + */ + +#include <aim.h> +#include "im.h" +#include "info.h" + +/* + * Takes a msghdr (and a length) and returns a client type + * code. Note that this is *only a guess* and has a low likelihood + * of actually being accurate. + * + * Its based on experimental data, with the help of Eric Warmenhoven + * who seems to have collected a wide variety of different AIM clients. + * + * + * Heres the current collection: + * 0501 0003 0101 0101 01 AOL Mobile Communicator, WinAIM 1.0.414 + * 0501 0003 0101 0201 01 WinAIM 2.0.847, 2.1.1187, 3.0.1464, + * 4.3.2229, 4.4.2286 + * 0501 0004 0101 0102 0101 WinAIM 4.1.2010, libfaim (right here) + * 0501 0001 0101 01 AOL v6.0, CompuServe 2000 v6.0, any + * TOC client + * + * Note that in this function, only the feature bytes are tested, since + * the rest will always be the same. + * + */ +guint16 aim_fingerprintclient(guint8 *msghdr, int len) +{ + static const struct { + guint16 clientid; + int len; + guint8 data[10]; + } fingerprints[] = { + /* AOL Mobile Communicator, WinAIM 1.0.414 */ + { AIM_CLIENTTYPE_MC, + 3, {0x01, 0x01, 0x01}}, + + /* WinAIM 2.0.847, 2.1.1187, 3.0.1464, 4.3.2229, 4.4.2286 */ + { AIM_CLIENTTYPE_WINAIM, + 3, {0x01, 0x01, 0x02}}, + + /* WinAIM 4.1.2010, libfaim */ + { AIM_CLIENTTYPE_WINAIM41, + 4, {0x01, 0x01, 0x01, 0x02}}, + + /* AOL v6.0, CompuServe 2000 v6.0, any TOC client */ + { AIM_CLIENTTYPE_AOL_TOC, + 1, {0x01}}, + + { 0, 0} + }; + int i; + + if (!msghdr || (len <= 0)) + return AIM_CLIENTTYPE_UNKNOWN; + + for (i = 0; fingerprints[i].len; i++) { + if (fingerprints[i].len != len) + continue; + if (memcmp(fingerprints[i].data, msghdr, fingerprints[i].len) == 0) + return fingerprints[i].clientid; + } + + return AIM_CLIENTTYPE_UNKNOWN; +} + +/* This should be endian-safe now... but who knows... */ +guint16 aim_iconsum(const guint8 *buf, int buflen) +{ + guint32 sum; + int i; + + for (i = 0, sum = 0; i + 1 < buflen; i += 2) + sum += (buf[i+1] << 8) + buf[i]; + if (i < buflen) + sum += buf[i]; + + sum = ((sum & 0xffff0000) >> 16) + (sum & 0x0000ffff); + + return (guint16)sum; +} + +/* + * Send an ICBM (instant message). + * + * + * Possible flags: + * AIM_IMFLAGS_AWAY -- Marks the message as an autoresponse + * AIM_IMFLAGS_ACK -- Requests that the server send an ack + * when the message is received (of type 0x0004/0x000c) + * AIM_IMFLAGS_OFFLINE--If destination is offline, store it until they are + * online (probably ICQ only). + * AIM_IMFLAGS_UNICODE--Instead of ASCII7, the passed message is + * made up of UNICODE duples. If you set + * this, you'd better be damn sure you know + * what you're doing. + * AIM_IMFLAGS_ISO_8859_1 -- The message contains the ASCII8 subset + * known as ISO-8859-1. + * + * Generally, you should use the lowest encoding possible to send + * your message. If you only use basic punctuation and the generic + * Latin alphabet, use ASCII7 (no flags). If you happen to use non-ASCII7 + * characters, but they are all clearly defined in ISO-8859-1, then + * use that. Keep in mind that not all characters in the PC ASCII8 + * character set are defined in the ISO standard. For those cases (most + * notably when the (r) symbol is used), you must use the full UNICODE + * encoding for your message. In UNICODE mode, _all_ characters must + * occupy 16bits, including ones that are not special. (Remember that + * the first 128 UNICODE symbols are equivelent to ASCII7, however they + * must be prefixed with a zero high order byte.) + * + * I strongly discourage the use of UNICODE mode, mainly because none + * of the clients I use can parse those messages (and besides that, + * wchars are difficult and non-portable to handle in most UNIX environments). + * If you really need to include special characters, use the HTML UNICODE + * entities. These are of the form ߪ where 2026 is the hex + * representation of the UNICODE index (in this case, UNICODE + * "Horizontal Ellipsis", or 133 in in ASCII8). + * + * Implementation note: Since this is one of the most-used functions + * in all of libfaim, it is written with performance in mind. As such, + * it is not as clear as it could be in respect to how this message is + * supposed to be layed out. Most obviously, tlvlists should be used + * instead of writing out the bytes manually. + * + * XXX more precise verification that we never send SNACs larger than 8192 + * XXX check SNAC size for multipart + * + */ +int aim_send_im_ext(aim_session_t *sess, struct aim_sendimext_args *args) +{ + static const guint8 deffeatures[] = { + 0x01, 0x01, 0x01, 0x02 + }; + aim_conn_t *conn; + int i, msgtlvlen; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004))) + return -EINVAL; + + if (!args) + return -EINVAL; + + if (args->flags & AIM_IMFLAGS_MULTIPART) { + if (args->mpmsg->numparts <= 0) + return -EINVAL; + } else { + if (!args->msg || (args->msglen <= 0)) + return -EINVAL; + + if (args->msglen >= MAXMSGLEN) + return -E2BIG; + } + + /* Painfully calculate the size of the message TLV */ + msgtlvlen = 1 + 1; /* 0501 */ + + if (args->flags & AIM_IMFLAGS_CUSTOMFEATURES) + msgtlvlen += 2 + args->featureslen; + else + msgtlvlen += 2 + sizeof(deffeatures); + + if (args->flags & AIM_IMFLAGS_MULTIPART) { + aim_mpmsg_section_t *sec; + + for (sec = args->mpmsg->parts; sec; sec = sec->next) { + msgtlvlen += 2 /* 0101 */ + 2 /* block len */; + msgtlvlen += 4 /* charset */ + sec->datalen; + } + + } else { + msgtlvlen += 2 /* 0101 */ + 2 /* block len */; + msgtlvlen += 4 /* charset */ + args->msglen; + } + + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, msgtlvlen+128))) + return -ENOMEM; + + /* XXX should be optional */ + snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, args->destsn, strlen(args->destsn)+1); + aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid); + + /* + * Generate a random message cookie + * + * We could cache these like we do SNAC IDs. (In fact, it + * might be a good idea.) In the message error functions, + * the 8byte message cookie is returned as well as the + * SNAC ID. + * + */ + for (i = 0; i < 8; i++) + aimbs_put8(&fr->data, (guint8) rand()); + + /* + * Channel ID + */ + aimbs_put16(&fr->data, 0x0001); + + /* + * Destination SN (prepended with byte length) + */ + aimbs_put8(&fr->data, strlen(args->destsn)); + aimbs_putraw(&fr->data, (guint8 *)args->destsn, strlen(args->destsn)); + + /* + * Message TLV (type 2). + */ + aimbs_put16(&fr->data, 0x0002); + aimbs_put16(&fr->data, msgtlvlen); + + /* + * Features + * + */ + aimbs_put8(&fr->data, 0x05); + aimbs_put8(&fr->data, 0x01); + + if (args->flags & AIM_IMFLAGS_CUSTOMFEATURES) { + aimbs_put16(&fr->data, args->featureslen); + aimbs_putraw(&fr->data, args->features, args->featureslen); + } else { + aimbs_put16(&fr->data, sizeof(deffeatures)); + aimbs_putraw(&fr->data, deffeatures, sizeof(deffeatures)); + } + + if (args->flags & AIM_IMFLAGS_MULTIPART) { + aim_mpmsg_section_t *sec; + + for (sec = args->mpmsg->parts; sec; sec = sec->next) { + aimbs_put16(&fr->data, 0x0101); + aimbs_put16(&fr->data, sec->datalen + 4); + aimbs_put16(&fr->data, sec->charset); + aimbs_put16(&fr->data, sec->charsubset); + aimbs_putraw(&fr->data, sec->data, sec->datalen); + } + + } else { + + aimbs_put16(&fr->data, 0x0101); + + /* + * Message block length. + */ + aimbs_put16(&fr->data, args->msglen + 0x04); + + /* + * Character set. + */ + if (args->flags & AIM_IMFLAGS_CUSTOMCHARSET) { + + aimbs_put16(&fr->data, args->charset); + aimbs_put16(&fr->data, args->charsubset); + + } else { + if (args->flags & AIM_IMFLAGS_UNICODE) + aimbs_put16(&fr->data, 0x0002); + else if (args->flags & AIM_IMFLAGS_ISO_8859_1) + aimbs_put16(&fr->data, 0x0003); + else + aimbs_put16(&fr->data, 0x0000); + + aimbs_put16(&fr->data, 0x0000); + } + + /* + * Message. Not terminated. + */ + aimbs_putraw(&fr->data, (guint8 *)args->msg, args->msglen); + } + + /* + * Set the Request Acknowledge flag. + */ + if (args->flags & AIM_IMFLAGS_ACK) { + aimbs_put16(&fr->data, 0x0003); + aimbs_put16(&fr->data, 0x0000); + } + + /* + * Set the Autoresponse flag. + */ + if (args->flags & AIM_IMFLAGS_AWAY) { + aimbs_put16(&fr->data, 0x0004); + aimbs_put16(&fr->data, 0x0000); + } + + if (args->flags & AIM_IMFLAGS_OFFLINE) { + aimbs_put16(&fr->data, 0x0006); + aimbs_put16(&fr->data, 0x0000); + } + + /* + * Set the I HAVE A REALLY PURTY ICON flag. + */ + if (args->flags & AIM_IMFLAGS_HASICON) { + aimbs_put16(&fr->data, 0x0008); + aimbs_put16(&fr->data, 0x000c); + aimbs_put32(&fr->data, args->iconlen); + aimbs_put16(&fr->data, 0x0001); + aimbs_put16(&fr->data, args->iconsum); + aimbs_put32(&fr->data, args->iconstamp); + } + + /* + * Set the Buddy Icon Requested flag. + */ + if (args->flags & AIM_IMFLAGS_BUDDYREQ) { + aimbs_put16(&fr->data, 0x0009); + aimbs_put16(&fr->data, 0x0000); + } + + aim_tx_enqueue(sess, fr); + + if (!(sess->flags & AIM_SESS_FLAGS_DONTTIMEOUTONICBM)) + aim_cleansnacs(sess, 60); /* clean out SNACs over 60sec old */ + + return 0; +} + +/* + * Simple wrapper for aim_send_im_ext() + * + * You cannot use aim_send_im if you need the HASICON flag. You must + * use aim_send_im_ext directly for that. + * + * aim_send_im also cannot be used if you require UNICODE messages, because + * that requires an explicit message length. Use aim_send_im_ext(). + * + */ +int aim_send_im(aim_session_t *sess, const char *destsn, guint16 flags, const char *msg) +{ + struct aim_sendimext_args args; + + args.destsn = destsn; + args.flags = flags; + args.msg = msg; + args.msglen = strlen(msg); + + /* Make these don't get set by accident -- they need aim_send_im_ext */ + args.flags &= ~(AIM_IMFLAGS_CUSTOMFEATURES | AIM_IMFLAGS_HASICON | AIM_IMFLAGS_MULTIPART); + + return aim_send_im_ext(sess, &args); +} + +/* + * This is also performance sensitive. (If you can believe it...) + * + */ +int aim_send_icon(aim_session_t *sess, const char *sn, const guint8 *icon, int iconlen, time_t stamp, guint16 iconsum) +{ + aim_conn_t *conn; + int i; + guint8 ck[8]; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004))) + return -EINVAL; + + if (!sn || !icon || (iconlen <= 0) || (iconlen >= MAXICONLEN)) + return -EINVAL; + + for (i = 0; i < 8; i++) + aimutil_put8(ck+i, (guint8) rand()); + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+2+1+strlen(sn)+2+2+2+8+16+2+2+2+2+2+2+2+4+4+4+iconlen+strlen(AIM_ICONIDENT)+2+2))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid); + + /* + * Cookie + */ + aimbs_putraw(&fr->data, ck, 8); + + /* + * Channel (2) + */ + aimbs_put16(&fr->data, 0x0002); + + /* + * Dest sn + */ + aimbs_put8(&fr->data, strlen(sn)); + aimbs_putraw(&fr->data, (guint8 *)sn, strlen(sn)); + + /* + * TLV t(0005) + * + * Encompasses everything below. + */ + aimbs_put16(&fr->data, 0x0005); + aimbs_put16(&fr->data, 2+8+16+6+4+4+iconlen+4+4+4+strlen(AIM_ICONIDENT)); + + aimbs_put16(&fr->data, 0x0000); + aimbs_putraw(&fr->data, ck, 8); + aim_putcap(&fr->data, AIM_CAPS_BUDDYICON); + + /* TLV t(000a) */ + aimbs_put16(&fr->data, 0x000a); + aimbs_put16(&fr->data, 0x0002); + aimbs_put16(&fr->data, 0x0001); + + /* TLV t(000f) */ + aimbs_put16(&fr->data, 0x000f); + aimbs_put16(&fr->data, 0x0000); + + /* TLV t(2711) */ + aimbs_put16(&fr->data, 0x2711); + aimbs_put16(&fr->data, 4+4+4+iconlen+strlen(AIM_ICONIDENT)); + aimbs_put16(&fr->data, 0x0000); + aimbs_put16(&fr->data, iconsum); + aimbs_put32(&fr->data, iconlen); + aimbs_put32(&fr->data, stamp); + aimbs_putraw(&fr->data, icon, iconlen); + aimbs_putraw(&fr->data, (guint8 *)AIM_ICONIDENT, strlen(AIM_ICONIDENT)); + + /* TLV t(0003) */ + aimbs_put16(&fr->data, 0x0003); + aimbs_put16(&fr->data, 0x0000); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * This only works for ICQ 2001b (thats 2001 not 2000). Better, only + * send it to clients advertising the RTF capability. In fact, if you send + * it to a client that doesn't support that capability, the server will gladly + * bounce it back to you. + * + * You'd think this would be in icq.c, but, well, I'm trying to stick with + * the one-group-per-file scheme as much as possible. This could easily + * be an exception, since Rendezvous IMs are external of the Oscar core, + * and therefore are undefined. Really I just need to think of a good way to + * make an interface similar to what AOL actually uses. But I'm not using COM. + * + */ +int aim_send_rtfmsg(aim_session_t *sess, struct aim_sendrtfmsg_args *args) +{ + const char rtfcap[] = {"{97B12751-243C-4334-AD22-D6ABF73F1492}"}; /* AIM_CAPS_ICQRTF capability in string form */ + aim_conn_t *conn; + int i; + guint8 ck[8]; + aim_frame_t *fr; + aim_snacid_t snacid; + int servdatalen; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004))) + return -EINVAL; + + if (!args || !args->destsn || !args->rtfmsg) + return -EINVAL; + + servdatalen = 2+2+16+2+4+1+2 + 2+2+4+4+4 + 2+4+2+strlen(args->rtfmsg)+1 + 4+4+4+strlen(rtfcap)+1; + + for (i = 0; i < 8; i++) + aimutil_put8(ck+i, (guint8) rand()); + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+128+servdatalen))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid); + + /* + * Cookie + */ + aimbs_putraw(&fr->data, ck, 8); + + /* + * Channel (2) + */ + aimbs_put16(&fr->data, 0x0002); + + /* + * Dest sn + */ + aimbs_put8(&fr->data, strlen(args->destsn)); + aimbs_putraw(&fr->data, (guint8 *)args->destsn, strlen(args->destsn)); + + /* + * TLV t(0005) + * + * Encompasses everything below. + */ + aimbs_put16(&fr->data, 0x0005); + aimbs_put16(&fr->data, 2+8+16 + 2+2+2 + 2+2 + 2+2+servdatalen); + + aimbs_put16(&fr->data, 0x0000); + aimbs_putraw(&fr->data, ck, 8); + aim_putcap(&fr->data, AIM_CAPS_ICQSERVERRELAY); + + /* + * t(000a) l(0002) v(0001) + */ + aimbs_put16(&fr->data, 0x000a); + aimbs_put16(&fr->data, 0x0002); + aimbs_put16(&fr->data, 0x0001); + + /* + * t(000f) l(0000) v() + */ + aimbs_put16(&fr->data, 0x000f); + aimbs_put16(&fr->data, 0x0000); + + /* + * Service Data TLV + */ + aimbs_put16(&fr->data, 0x2711); + aimbs_put16(&fr->data, servdatalen); + + aimbs_putle16(&fr->data, 11 + 16 /* 11 + (sizeof CLSID) */); + aimbs_putle16(&fr->data, 9); + aim_putcap(&fr->data, AIM_CAPS_EMPTY); + aimbs_putle16(&fr->data, 0); + aimbs_putle32(&fr->data, 0); + aimbs_putle8(&fr->data, 0); + aimbs_putle16(&fr->data, 0x03ea); /* trid1 */ + + aimbs_putle16(&fr->data, 14); + aimbs_putle16(&fr->data, 0x03eb); /* trid2 */ + aimbs_putle32(&fr->data, 0); + aimbs_putle32(&fr->data, 0); + aimbs_putle32(&fr->data, 0); + + aimbs_putle16(&fr->data, 0x0001); + aimbs_putle32(&fr->data, 0); + aimbs_putle16(&fr->data, strlen(args->rtfmsg)+1); + aimbs_putraw(&fr->data, (guint8 *)args->rtfmsg, strlen(args->rtfmsg)+1); + + aimbs_putle32(&fr->data, args->fgcolor); + aimbs_putle32(&fr->data, args->bgcolor); + aimbs_putle32(&fr->data, strlen(rtfcap)+1); + aimbs_putraw(&fr->data, (guint8 *)rtfcap, strlen(rtfcap)+1); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +int aim_request_directim(aim_session_t *sess, const char *destsn, guint8 *ip, guint16 port, guint8 *ckret) +{ + aim_conn_t *conn; + guint8 ck[8]; + aim_frame_t *fr; + aim_snacid_t snacid; + aim_tlvlist_t *tl = NULL, *itl = NULL; + int hdrlen, i; + guint8 *hdr; + aim_bstream_t hdrbs; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004))) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 256+strlen(destsn)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid); + + /* + * Generate a random message cookie + * + * This cookie needs to be alphanumeric and NULL-terminated to be + * TOC-compatible. + * + * XXX have I mentioned these should be generated in msgcookie.c? + * + */ + for (i = 0; i < 7; i++) + ck[i] = 0x30 + ((guint8) rand() % 10); + ck[7] = '\0'; + + if (ckret) + memcpy(ckret, ck, 8); + + /* Cookie */ + aimbs_putraw(&fr->data, ck, 8); + + /* Channel */ + aimbs_put16(&fr->data, 0x0002); + + /* Destination SN */ + aimbs_put8(&fr->data, strlen(destsn)); + aimbs_putraw(&fr->data, (guint8 *)destsn, strlen(destsn)); + + aim_addtlvtochain_noval(&tl, 0x0003); + + hdrlen = 2+8+16+6+8+6+4; + hdr = g_malloc(hdrlen); + aim_bstream_init(&hdrbs, hdr, hdrlen); + + aimbs_put16(&hdrbs, 0x0000); + aimbs_putraw(&hdrbs, ck, 8); + aim_putcap(&hdrbs, AIM_CAPS_IMIMAGE); + + aim_addtlvtochain16(&itl, 0x000a, 0x0001); + aim_addtlvtochain_raw(&itl, 0x0003, 4, ip); + aim_addtlvtochain16(&itl, 0x0005, port); + aim_addtlvtochain_noval(&itl, 0x000f); + + aim_writetlvchain(&hdrbs, &itl); + + aim_addtlvtochain_raw(&tl, 0x0005, aim_bstream_curpos(&hdrbs), hdr); + + aim_writetlvchain(&fr->data, &tl); + + g_free(hdr); + aim_freetlvchain(&itl); + aim_freetlvchain(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +int aim_request_sendfile(aim_session_t *sess, const char *sn, const char *filename, guint16 numfiles, guint32 totsize, guint8 *ip, guint16 port, guint8 *ckret) +{ + aim_conn_t *conn; + int i; + guint8 ck[8]; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004))) + return -EINVAL; + + if (!sn || !filename) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+2+1+strlen(sn)+2+2+2+8+16+6+8+6+4+2+2+2+2+4+strlen(filename)+4))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid); + + for (i = 0; i < 7; i++) + aimutil_put8(ck+i, 0x30 + ((guint8) rand() % 10)); + ck[7] = '\0'; + + if (ckret) + memcpy(ckret, ck, 8); + + /* + * Cookie + */ + aimbs_putraw(&fr->data, ck, 8); + + /* + * Channel (2) + */ + aimbs_put16(&fr->data, 0x0002); + + /* + * Dest sn + */ + aimbs_put8(&fr->data, strlen(sn)); + aimbs_putraw(&fr->data, (guint8 *)sn, strlen(sn)); + + /* + * TLV t(0005) + * + * Encompasses everything below. Gee. + */ + aimbs_put16(&fr->data, 0x0005); + aimbs_put16(&fr->data, 2+8+16+6+8+6+4+2+2+2+2+4+strlen(filename)+4); + + aimbs_put16(&fr->data, 0x0000); + aimbs_putraw(&fr->data, ck, 8); + aim_putcap(&fr->data, AIM_CAPS_SENDFILE); + + /* TLV t(000a) */ + aimbs_put16(&fr->data, 0x000a); + aimbs_put16(&fr->data, 0x0002); + aimbs_put16(&fr->data, 0x0001); + + /* TLV t(0003) (IP) */ + aimbs_put16(&fr->data, 0x0003); + aimbs_put16(&fr->data, 0x0004); + aimbs_putraw(&fr->data, ip, 4); + + /* TLV t(0005) (port) */ + aimbs_put16(&fr->data, 0x0005); + aimbs_put16(&fr->data, 0x0002); + aimbs_put16(&fr->data, port); + + /* TLV t(000f) */ + aimbs_put16(&fr->data, 0x000f); + aimbs_put16(&fr->data, 0x0000); + + /* TLV t(2711) */ + aimbs_put16(&fr->data, 0x2711); + aimbs_put16(&fr->data, 2+2+4+strlen(filename)+4); + + /* ? */ + aimbs_put16(&fr->data, 0x0001); + aimbs_put16(&fr->data, numfiles); + aimbs_put32(&fr->data, totsize); + aimbs_putraw(&fr->data, (guint8 *)filename, strlen(filename)); + + /* ? */ + aimbs_put32(&fr->data, 0x00000000); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/** + * Request the status message of the given ICQ user. + * + * @param sess The oscar session. + * @param sn The UIN of the user of whom you wish to request info. + * @param type The type of info you wish to request. This should be the current + * state of the user, as one of the AIM_ICQ_STATE_* defines. + * @return Return 0 if no errors, otherwise return the error number. + */ +int aim_send_im_ch2_geticqmessage(aim_session_t *sess, const char *sn, int type) +{ + aim_conn_t *conn; + int i; + guint8 ck[8]; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004)) || !sn) + return -EINVAL; + + for (i = 0; i < 8; i++) + aimutil_put8(ck+i, (guint8) rand()); + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+2+1+strlen(sn) + 4+0x5e + 4))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0004, 0x0006, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0004, 0x0006, 0x0000, snacid); + + /* Cookie */ + aimbs_putraw(&fr->data, ck, 8); + + /* Channel (2) */ + aimbs_put16(&fr->data, 0x0002); + + /* Dest sn */ + aimbs_put8(&fr->data, strlen(sn)); + aimbs_putraw(&fr->data, (guint8 *)sn, strlen(sn)); + + /* TLV t(0005) - Encompasses almost everything below. */ + aimbs_put16(&fr->data, 0x0005); /* T */ + aimbs_put16(&fr->data, 0x005e); /* L */ + { /* V */ + aimbs_put16(&fr->data, 0x0000); + + /* Cookie */ + aimbs_putraw(&fr->data, ck, 8); + + /* Put the 16 byte server relay capability */ + aim_putcap(&fr->data, AIM_CAPS_ICQSERVERRELAY); + + /* TLV t(000a) */ + aimbs_put16(&fr->data, 0x000a); + aimbs_put16(&fr->data, 0x0002); + aimbs_put16(&fr->data, 0x0001); + + /* TLV t(000f) */ + aimbs_put16(&fr->data, 0x000f); + aimbs_put16(&fr->data, 0x0000); + + /* TLV t(2711) */ + aimbs_put16(&fr->data, 0x2711); + aimbs_put16(&fr->data, 0x0036); + { /* V */ + aimbs_putle16(&fr->data, 0x001b); /* L */ + aimbs_putle16(&fr->data, 0x0008); /* AAA - Protocol version */ + aimbs_putle32(&fr->data, 0x00000000); /* Unknown */ + aimbs_putle32(&fr->data, 0x00000000); /* Unknown */ + aimbs_putle32(&fr->data, 0x00000000); /* Unknown */ + aimbs_putle32(&fr->data, 0x00000000); /* Unknown */ + aimbs_putle16(&fr->data, 0x0000); /* Unknown */ + aimbs_putle16(&fr->data, 0x0003); /* Client features? */ + aimbs_putle16(&fr->data, 0x0000); /* Unknown */ + aimbs_putle8(&fr->data, 0x00); /* Unkizown */ + aimbs_putle16(&fr->data, 0xffff); /* Sequence number? XXX - This should decrement by 1 with each request */ + + aimbs_putle16(&fr->data, 0x000e); /* L */ + aimbs_putle16(&fr->data, 0xffff); /* Sequence number? XXX - This should decrement by 1 with each request */ + aimbs_putle32(&fr->data, 0x00000000); /* Unknown */ + aimbs_putle32(&fr->data, 0x00000000); /* Unknown */ + aimbs_putle32(&fr->data, 0x00000000); /* Unknown */ + + /* The type of status message being requested */ + if (type & AIM_ICQ_STATE_CHAT) + aimbs_putle16(&fr->data, 0x03ec); + else if(type & AIM_ICQ_STATE_DND) + aimbs_putle16(&fr->data, 0x03eb); + else if(type & AIM_ICQ_STATE_OUT) + aimbs_putle16(&fr->data, 0x03ea); + else if(type & AIM_ICQ_STATE_BUSY) + aimbs_putle16(&fr->data, 0x03e9); + else if(type & AIM_ICQ_STATE_AWAY) + aimbs_putle16(&fr->data, 0x03e8); + + aimbs_putle16(&fr->data, 0x0000); /* Status? */ + aimbs_putle16(&fr->data, 0x0001); /* Priority of this message? */ + aimbs_putle16(&fr->data, 0x0001); /* L? */ + aimbs_putle8(&fr->data, 0x00); /* Null termination? */ + } /* End TLV t(2711) */ + } /* End TLV t(0005) */ + + /* TLV t(0003) */ + aimbs_put16(&fr->data, 0x0003); + aimbs_put16(&fr->data, 0x0000); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/** + * answers status message requests + * @param sess the oscar session + * @param sender the guy whos asking + * @param cookie message id which we are answering for + * @param message away message + * @param state our current away state the way icq requests it (0xE8 for away, 0xE9 occupied, ...) + * @return 0 if no error + */ +int aim_send_im_ch2_statusmessage(aim_session_t *sess, const char *sender, const guint8 *cookie, + const char *message, const guint8 state, const guint16 dc) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004))) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, + 10+8+2+1+strlen(sender)+2+0x1d+0x10+9+strlen(message)+1))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0004, 0x000b, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0004, 0x000b, 0x0000, snacid); + + aimbs_putraw(&fr->data, cookie, 8); + + aimbs_put16(&fr->data, 0x0002); /* channel */ + aimbs_put8(&fr->data, strlen(sender)); + aimbs_putraw(&fr->data, (guint8 *)sender, strlen(sender)); + + aimbs_put16(&fr->data, 0x0003); /* reason: channel specific */ + + aimbs_putle16(&fr->data, 0x001b); /* length of data SEQ1 */ + aimbs_putle16(&fr->data, 0x0008); /* protocol version */ + + aimbs_putle32(&fr->data, 0x0000); /* no plugin -> 16 times 0x00 */ + aimbs_putle32(&fr->data, 0x0000); + aimbs_putle32(&fr->data, 0x0000); + aimbs_putle32(&fr->data, 0x0000); + + aimbs_putle16(&fr->data, 0x0000); /* unknown */ + aimbs_putle32(&fr->data, 0x0003); /* client features */ + aimbs_putle8(&fr->data, 0x00); /* unknown */ + aimbs_putle16(&fr->data, dc); /* Sequence number? XXX - This should decrement by 1 with each request */ + /* end of SEQ1 */ + + aimbs_putle16(&fr->data, 0x000e); /* Length of SEQ2 */ + aimbs_putle16(&fr->data, dc); /* Sequence number? same as above + * XXX - This should decrement by 1 with each request */ + aimbs_putle32(&fr->data, 0x00000000); /* Unknown */ + aimbs_putle32(&fr->data, 0x00000000); /* Unknown */ + aimbs_putle32(&fr->data, 0x00000000); /* Unknown */ + /* end of SEQ2 */ + + /* now for the real fun */ + aimbs_putle8(&fr->data, state); /* away state */ + aimbs_putle8(&fr->data, 0x03); /* msg-flag: 03 for states */ + aimbs_putle16(&fr->data, 0x0000); /* status code ? */ + aimbs_putle16(&fr->data, 0x0000); /* priority code */ + aimbs_putle16(&fr->data, strlen(message) + 1); /* message length + termination */ + aimbs_putraw(&fr->data, (guint8 *) message, strlen(message) + 1); /* null terminated string */ + + aim_tx_enqueue(sess, fr); + + + return 0; +} + + +static int outgoingim(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int i, ret = 0; + aim_rxcallback_t userfunc; + guint8 cookie[8]; + guint16 channel; + aim_tlvlist_t *tlvlist; + char *sn; + int snlen; + guint16 icbmflags = 0; + guint8 flag1 = 0, flag2 = 0; + char *msg = NULL; + aim_tlv_t *msgblock; + + /* ICBM Cookie. */ + for (i = 0; i < 8; i++) + cookie[i] = aimbs_get8(bs); + + /* Channel ID */ + channel = aimbs_get16(bs); + + if (channel != 0x01) { + do_error_dialog(sess->aux_data, "icbm: ICBM recieved on unsupported channel. Ignoring.", "Gaim"); + return 0; + } + + snlen = aimbs_get8(bs); + sn = aimbs_getstr(bs, snlen); + + tlvlist = aim_readtlvchain(bs); + + if (aim_gettlv(tlvlist, 0x0003, 1)) + icbmflags |= AIM_IMFLAGS_ACK; + if (aim_gettlv(tlvlist, 0x0004, 1)) + icbmflags |= AIM_IMFLAGS_AWAY; + + if ((msgblock = aim_gettlv(tlvlist, 0x0002, 1))) { + aim_bstream_t mbs; + int featurelen, msglen; + + aim_bstream_init(&mbs, msgblock->value, msgblock->length); + + aimbs_get8(&mbs); + aimbs_get8(&mbs); + for (featurelen = aimbs_get16(&mbs); featurelen; featurelen--) + aimbs_get8(&mbs); + aimbs_get8(&mbs); + aimbs_get8(&mbs); + + msglen = aimbs_get16(&mbs) - 4; /* final block length */ + + flag1 = aimbs_get16(&mbs); + flag2 = aimbs_get16(&mbs); + + msg = aimbs_getstr(&mbs, msglen); + } + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, channel, sn, msg, icbmflags, flag1, flag2); + + g_free(sn); + aim_freetlvchain(&tlvlist); + + return ret; +} + +/* + * Ahh, the joys of nearly ridiculous over-engineering. + * + * Not only do AIM ICBM's support multiple channels. Not only do they + * support multiple character sets. But they support multiple character + * sets / encodings within the same ICBM. + * + * These multipart messages allow for complex space savings techniques, which + * seem utterly unnecessary by today's standards. In fact, there is only + * one client still in popular use that still uses this method: AOL for the + * Macintosh, Version 5.0. Obscure, yes, I know. + * + * In modern (non-"legacy") clients, if the user tries to send a character + * that is not ISO-8859-1 or ASCII, the client will send the entire message + * as UNICODE, meaning that every character in the message will occupy the + * full 16 bit UNICODE field, even if the high order byte would be zero. + * Multipart messages prevent this wasted space by allowing the client to + * only send the characters in UNICODE that need to be sent that way, and + * the rest of the message can be sent in whatever the native character + * set is (probably ASCII). + * + * An important note is that sections will be displayed in the order that + * they appear in the ICBM. There is no facility for merging or rearranging + * sections at run time. So if you have, say, ASCII then UNICODE then ASCII, + * you must supply two ASCII sections with a UNICODE in the middle, and incur + * the associated overhead. + * + * Normally I would have laughed and given a firm 'no' to supporting this + * seldom-used feature, but something is attracting me to it. In the future, + * it may be possible to abuse this to send mixed-media messages to other + * open source clients (like encryption or something) -- see faimtest for + * examples of how to do this. + * + * I would definitly recommend avoiding this feature unless you really + * know what you are doing, and/or you have something neat to do with it. + * + */ +int aim_mpmsg_init(aim_session_t *sess, aim_mpmsg_t *mpm) +{ + + memset(mpm, 0, sizeof(aim_mpmsg_t)); + + return 0; +} + +static int mpmsg_addsection(aim_session_t *sess, aim_mpmsg_t *mpm, guint16 charset, guint16 charsubset, guint8 *data, guint16 datalen) +{ + aim_mpmsg_section_t *sec; + + if (!(sec = g_new0(aim_mpmsg_section_t,1))) + return -1; + + sec->charset = charset; + sec->charsubset = charsubset; + sec->data = data; + sec->datalen = datalen; + sec->next = NULL; + + if (!mpm->parts) + mpm->parts = sec; + else { + aim_mpmsg_section_t *cur; + + for (cur = mpm->parts; cur->next; cur = cur->next) + ; + cur->next = sec; + } + + mpm->numparts++; + + return 0; +} + +int aim_mpmsg_addraw(aim_session_t *sess, aim_mpmsg_t *mpm, guint16 charset, guint16 charsubset, const guint8 *data, guint16 datalen) +{ + guint8 *dup; + + if (!(dup = g_malloc(datalen))) + return -1; + memcpy(dup, data, datalen); + + if (mpmsg_addsection(sess, mpm, charset, charsubset, dup, datalen) == -1) { + g_free(dup); + return -1; + } + + return 0; +} + +/* XXX should provide a way of saying ISO-8859-1 specifically */ +int aim_mpmsg_addascii(aim_session_t *sess, aim_mpmsg_t *mpm, const char *ascii) +{ + char *dup; + + if (!(dup = g_strdup(ascii))) + return -1; + + if (mpmsg_addsection(sess, mpm, 0x0000, 0x0000, (guint8 *)dup, (guint16) strlen(ascii)) == -1) { + g_free(dup); + return -1; + } + + return 0; +} + +int aim_mpmsg_addunicode(aim_session_t *sess, aim_mpmsg_t *mpm, const guint16 *unicode, guint16 unicodelen) +{ + guint8 *buf; + aim_bstream_t bs; + int i; + + if (!(buf = g_malloc(unicodelen * 2))) + return -1; + + aim_bstream_init(&bs, buf, unicodelen * 2); + + /* We assume unicode is in /host/ byte order -- convert to network */ + for (i = 0; i < unicodelen; i++) + aimbs_put16(&bs, unicode[i]); + + if (mpmsg_addsection(sess, mpm, 0x0002, 0x0000, buf, aim_bstream_curpos(&bs)) == -1) { + g_free(buf); + return -1; + } + + return 0; +} + +void aim_mpmsg_free(aim_session_t *sess, aim_mpmsg_t *mpm) +{ + aim_mpmsg_section_t *cur; + + for (cur = mpm->parts; cur; ) { + aim_mpmsg_section_t *tmp; + + tmp = cur->next; + g_free(cur->data); + g_free(cur); + cur = tmp; + } + + mpm->numparts = 0; + mpm->parts = NULL; + + return; +} + +/* + * Start by building the multipart structures, then pick the first + * human-readable section and stuff it into args->msg so no one gets + * suspicious. + * + */ +static int incomingim_ch1_parsemsgs(aim_session_t *sess, guint8 *data, int len, struct aim_incomingim_ch1_args *args) +{ + static const guint16 charsetpri[] = { + 0x0000, /* ASCII first */ + 0x0003, /* then ISO-8859-1 */ + 0x0002, /* UNICODE as last resort */ + }; + static const int charsetpricount = 3; + int i; + aim_bstream_t mbs; + aim_mpmsg_section_t *sec; + + aim_bstream_init(&mbs, data, len); + + while (aim_bstream_empty(&mbs)) { + guint16 msglen, flag1, flag2; + char *msgbuf; + + aimbs_get8(&mbs); /* 01 */ + aimbs_get8(&mbs); /* 01 */ + + /* Message string length, including character set info. */ + msglen = aimbs_get16(&mbs); + + /* Character set info */ + flag1 = aimbs_get16(&mbs); + flag2 = aimbs_get16(&mbs); + + /* Message. */ + msglen -= 4; + + /* + * For now, we don't care what the encoding is. Just copy + * it into a multipart struct and deal with it later. However, + * always pad the ending with a NULL. This makes it easier + * to treat ASCII sections as strings. It won't matter for + * UNICODE or binary data, as you should never read past + * the specified data length, which will not include the pad. + * + * XXX There's an API bug here. For sending, the UNICODE is + * given in host byte order (aim_mpmsg_addunicode), but here + * the received messages are given in network byte order. + * + */ + msgbuf = aimbs_getstr(&mbs, msglen); + mpmsg_addsection(sess, &args->mpmsg, flag1, flag2, (guint8 *)msgbuf, (guint16) msglen); + + } /* while */ + + args->icbmflags |= AIM_IMFLAGS_MULTIPART; /* always set */ + + /* + * Clients that support multiparts should never use args->msg, as it + * will point to an arbitrary section. + * + * Here, we attempt to provide clients that do not support multipart + * messages with something to look at -- hopefully a human-readable + * string. But, failing that, a UNICODE message, or nothing at all. + * + * Which means that even if args->msg is NULL, it does not mean the + * message was blank. + * + */ + for (i = 0; i < charsetpricount; i++) { + for (sec = args->mpmsg.parts; sec; sec = sec->next) { + + if (sec->charset != charsetpri[i]) + continue; + + /* Great. We found one. Fill it in. */ + args->charset = sec->charset; + args->charsubset = sec->charsubset; + args->icbmflags |= AIM_IMFLAGS_CUSTOMCHARSET; + + /* Set up the simple flags */ + if (args->charset == 0x0000) + ; /* ASCII */ + else if (args->charset == 0x0002) + args->icbmflags |= AIM_IMFLAGS_UNICODE; + else if (args->charset == 0x0003) + args->icbmflags |= AIM_IMFLAGS_ISO_8859_1; + else if (args->charset == 0xffff) + ; /* no encoding (yeep!) */ + + if (args->charsubset == 0x0000) + ; /* standard subencoding? */ + else if (args->charsubset == 0x000b) + args->icbmflags |= AIM_IMFLAGS_SUBENC_MACINTOSH; + else if (args->charsubset == 0xffff) + ; /* no subencoding */ +#if 0 + /* XXX this isn't really necesary... */ + if ( ((args.flag1 != 0x0000) && + (args.flag1 != 0x0002) && + (args.flag1 != 0x0003) && + (args.flag1 != 0xffff)) || + ((args.flag2 != 0x0000) && + (args.flag2 != 0x000b) && + (args.flag2 != 0xffff))) { + faimdprintf(sess, 0, "icbm: **warning: encoding flags are being used! {%04x, %04x}\n", args.flag1, args.flag2); + } +#endif + + args->msg = (char *)sec->data; + args->msglen = sec->datalen; + + return 0; + } + } + + /* No human-readable sections found. Oh well. */ + args->charset = args->charsubset = 0xffff; + args->msg = NULL; + args->msglen = 0; + + return 0; +} + +static int incomingim_ch1(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, guint16 channel, aim_userinfo_t *userinfo, aim_bstream_t *bs, guint8 *cookie) +{ + guint16 type, length; + aim_rxcallback_t userfunc; + int ret = 0; + struct aim_incomingim_ch1_args args; + int endpos; + + memset(&args, 0, sizeof(args)); + + aim_mpmsg_init(sess, &args.mpmsg); + + /* + * This used to be done using tlvchains. For performance reasons, + * I've changed it to process the TLVs in-place. This avoids lots + * of per-IM memory allocations. + */ + while (aim_bstream_empty(bs)) { + + type = aimbs_get16(bs); + length = aimbs_get16(bs); + + endpos = aim_bstream_curpos(bs) + length; + + if (type == 0x0002) { /* Message Block */ + + /* + * This TLV consists of the following: + * - 0501 -- Unknown + * - Features: Don't know how to interpret these + * - 0101 -- Unknown + * - Message + * + */ + + aimbs_get8(bs); /* 05 */ + aimbs_get8(bs); /* 01 */ + + args.featureslen = aimbs_get16(bs); + /* XXX XXX this is all evil! */ + args.features = bs->data + bs->offset; + aim_bstream_advance(bs, args.featureslen); + args.icbmflags |= AIM_IMFLAGS_CUSTOMFEATURES; + + /* + * The rest of the TLV contains one or more message + * blocks... + */ + incomingim_ch1_parsemsgs(sess, bs->data + bs->offset /* XXX evil!!! */, length - 2 - 2 - args.featureslen, &args); + + } else if (type == 0x0003) { /* Server Ack Requested */ + + args.icbmflags |= AIM_IMFLAGS_ACK; + + } else if (type == 0x0004) { /* Message is Auto Response */ + + args.icbmflags |= AIM_IMFLAGS_AWAY; + + } else if (type == 0x0006) { /* Message was received offline. */ + + /* XXX not sure if this actually gets sent. */ + args.icbmflags |= AIM_IMFLAGS_OFFLINE; + + } else if (type == 0x0008) { /* I-HAVE-A-REALLY-PURTY-ICON Flag */ + + args.iconlen = aimbs_get32(bs); + aimbs_get16(bs); /* 0x0001 */ + args.iconsum = aimbs_get16(bs); + args.iconstamp = aimbs_get32(bs); + + /* + * This looks to be a client bug. MacAIM 4.3 will + * send this tag, but with all zero values, in the + * first message of a conversation. This makes no + * sense whatsoever, so I'm going to say its a bug. + * + * You really shouldn't advertise a zero-length icon + * anyway. + * + */ + if (args.iconlen) + args.icbmflags |= AIM_IMFLAGS_HASICON; + + } else if (type == 0x0009) { + + args.icbmflags |= AIM_IMFLAGS_BUDDYREQ; + + } else if (type == 0x0017) { + + args.extdatalen = length; + args.extdata = aimbs_getraw(bs, args.extdatalen); + + } else { + // do_error_dialog(sess->aux_data, "Unknown TLV encountered", "Gaim"); + } + + /* + * This is here to protect ourselves from ourselves. That + * is, if something above doesn't completly parse its value + * section, or, worse, overparses it, this will set the + * stream where it needs to be in order to land on the next + * TLV when the loop continues. + * + */ + aim_bstream_setpos(bs, endpos); + } + + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, channel, userinfo, &args); + + aim_mpmsg_free(sess, &args.mpmsg); + g_free(args.extdata); + + return ret; +} + +static void incomingim_ch2_icqserverrelay_free(aim_session_t *sess, struct aim_incomingim_ch2_args *args) +{ + + g_free((char *)args->info.rtfmsg.rtfmsg); + + return; +} + +/* + * The relationship between AIM_CAPS_ICQSERVERRELAY and AIM_CAPS_ICQRTF is + * kind of odd. This sends the client ICQRTF since that is all that I've seen + * SERVERRELAY used for. + * + * Note that this is all little-endian. Cringe. + * + * This cap is used for auto status message replies, too [ft] + * + */ +static void incomingim_ch2_icqserverrelay(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args, aim_bstream_t *servdata) +{ + guint16 hdrlen, msglen, dc; + guint8 msgtype, msgflags; + guint8 *plugin; + int i = 0, tmp = 0; + struct gaim_connection *gc = sess->aux_data; + + /* at the moment we just can deal with requests, not with cancel or accept */ + if (args->status != 0) return; + + hdrlen = aimbs_getle16(servdata); + + aim_bstream_advance(servdata, 0x02); /* protocol version */ + plugin = aimbs_getraw(servdata, 0x10); /* following data is a message or + something plugin specific */ + /* as there is no plugin handling, just skip the rest */ + aim_bstream_advance(servdata, hdrlen - 0x12); + + hdrlen = aimbs_getle16(servdata); + dc = aimbs_getle16(servdata); /* save the sequence number */ + aim_bstream_advance(servdata, hdrlen - 0x02); + + /* TODO is it a message or something for a plugin? */ + for (i = 0; i < 0x10; i++) { + tmp |= plugin[i]; + } + + if (!tmp) { /* message follows */ + + msgtype = aimbs_getle8(servdata); + msgflags = aimbs_getle8(servdata); + + aim_bstream_advance(servdata, 0x04); /* status code and priority code */ + + msglen = aimbs_getle16(servdata); /* message string length */ + args->info.rtfmsg.rtfmsg = aimbs_getstr(servdata, msglen); + + switch(msgtype) { + case AIM_MTYPE_PLAIN: + + args->info.rtfmsg.fgcolor = aimbs_getle32(servdata); + args->info.rtfmsg.bgcolor = aimbs_getle32(servdata); + + hdrlen = aimbs_getle32(servdata); + aim_bstream_advance(servdata, hdrlen); + + /* XXX This is such a hack. */ + args->reqclass = AIM_CAPS_ICQRTF; + break; + + case AIM_MTYPE_AUTOAWAY: + case AIM_MTYPE_AUTOBUSY: + case AIM_MTYPE_AUTONA: + case AIM_MTYPE_AUTODND: + case AIM_MTYPE_AUTOFFC: + case 0x9c: /* ICQ 5 seems to send this */ + aim_send_im_ch2_statusmessage(sess, userinfo->sn, args->cookie, + gc->away, sess->aim_icq_state, dc); + break; + + } + } /* message or plugin specific */ + + g_free(plugin); + args->destructor = (void *)incomingim_ch2_icqserverrelay_free; + + return; +} + +typedef void (*ch2_args_destructor_t)(aim_session_t *sess, struct aim_incomingim_ch2_args *args); + +static int incomingim_ch2(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, guint16 channel, aim_userinfo_t *userinfo, aim_tlvlist_t *tlvlist, guint8 *cookie) +{ + aim_rxcallback_t userfunc; + aim_tlv_t *block1, *servdatatlv; + aim_tlvlist_t *list2; + struct aim_incomingim_ch2_args args; + aim_bstream_t bbs, sdbs, *sdbsptr = NULL; + guint8 *cookie2; + int ret = 0; + + char clientip1[30] = {""}; + char clientip2[30] = {""}; + char verifiedip[30] = {""}; + + memset(&args, 0, sizeof(args)); + + /* + * There's another block of TLVs embedded in the type 5 here. + */ + block1 = aim_gettlv(tlvlist, 0x0005, 1); + aim_bstream_init(&bbs, block1->value, block1->length); + + /* + * First two bytes represent the status of the connection. + * + * 0 is a request, 1 is a deny (?), 2 is an accept + */ + args.status = aimbs_get16(&bbs); + + /* + * Next comes the cookie. Should match the ICBM cookie. + */ + cookie2 = aimbs_getraw(&bbs, 8); + if (memcmp(cookie, cookie2, 8) != 0) + do_error_dialog(sess->aux_data, "rend: warning cookies don't match!", "Gaim"); + memcpy(args.cookie, cookie2, 8); + g_free(cookie2); + + /* + * The next 16bytes are a capability block so we can + * identify what type of rendezvous this is. + */ + args.reqclass = aim_getcap(sess, &bbs, 0x10); + + /* + * What follows may be TLVs or nothing, depending on the + * purpose of the message. + * + * Ack packets for instance have nothing more to them. + */ + list2 = aim_readtlvchain(&bbs); + + /* + * IP address from the perspective of the client. + */ + if (aim_gettlv(list2, 0x0002, 1)) { + aim_tlv_t *iptlv; + + iptlv = aim_gettlv(list2, 0x0002, 1); + + g_snprintf(clientip1, sizeof(clientip1), "%d.%d.%d.%d", + aimutil_get8(iptlv->value+0), + aimutil_get8(iptlv->value+1), + aimutil_get8(iptlv->value+2), + aimutil_get8(iptlv->value+3)); + } + + /* + * Secondary IP address from the perspective of the client. + */ + if (aim_gettlv(list2, 0x0003, 1)) { + aim_tlv_t *iptlv; + + iptlv = aim_gettlv(list2, 0x0003, 1); + + g_snprintf(clientip2, sizeof(clientip2), "%d.%d.%d.%d", + aimutil_get8(iptlv->value+0), + aimutil_get8(iptlv->value+1), + aimutil_get8(iptlv->value+2), + aimutil_get8(iptlv->value+3)); + } + + /* + * Verified IP address (from the perspective of Oscar). + * + * This is added by the server. + */ + if (aim_gettlv(list2, 0x0004, 1)) { + aim_tlv_t *iptlv; + + iptlv = aim_gettlv(list2, 0x0004, 1); + + g_snprintf(verifiedip, sizeof(verifiedip), "%d.%d.%d.%d", + aimutil_get8(iptlv->value+0), + aimutil_get8(iptlv->value+1), + aimutil_get8(iptlv->value+2), + aimutil_get8(iptlv->value+3)); + } + + /* + * Port number for something. + */ + if (aim_gettlv(list2, 0x0005, 1)) + args.port = aim_gettlv16(list2, 0x0005, 1); + + /* + * Error code. + */ + if (aim_gettlv(list2, 0x000b, 1)) + args.errorcode = aim_gettlv16(list2, 0x000b, 1); + + /* + * Invitation message / chat description. + */ + if (aim_gettlv(list2, 0x000c, 1)) + args.msg = aim_gettlv_str(list2, 0x000c, 1); + + /* + * Character set. + */ + if (aim_gettlv(list2, 0x000d, 1)) + args.encoding = aim_gettlv_str(list2, 0x000d, 1); + + /* + * Language. + */ + if (aim_gettlv(list2, 0x000e, 1)) + args.language = aim_gettlv_str(list2, 0x000e, 1); + + /* Unknown -- two bytes = 0x0001 */ + if (aim_gettlv(list2, 0x000a, 1)) + ; + + /* Unknown -- no value */ + if (aim_gettlv(list2, 0x000f, 1)) + ; + + if (strlen(clientip1)) + args.clientip = (char *)clientip1; + if (strlen(clientip2)) + args.clientip2 = (char *)clientip2; + if (strlen(verifiedip)) + args.verifiedip = (char *)verifiedip; + + /* + * This is must be present in PROPOSALs, but will probably not + * exist in CANCELs and ACCEPTs. + * + * Service Data blocks are module-specific in format. + */ + if ((servdatatlv = aim_gettlv(list2, 0x2711 /* 10001 */, 1))) { + + aim_bstream_init(&sdbs, servdatatlv->value, servdatatlv->length); + sdbsptr = &sdbs; + } + + if (args.reqclass & AIM_CAPS_ICQSERVERRELAY) + incomingim_ch2_icqserverrelay(sess, mod, rx, snac, userinfo, &args, sdbsptr); + + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, channel, userinfo, &args); + + + if (args.destructor) + ((ch2_args_destructor_t)args.destructor)(sess, &args); + + g_free((char *)args.msg); + g_free((char *)args.encoding); + g_free((char *)args.language); + + aim_freetlvchain(&list2); + + return ret; +} + +static int incomingim_ch4(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, guint16 channel, aim_userinfo_t *userinfo, aim_tlvlist_t *tlvlist, guint8 *cookie) +{ + aim_bstream_t meat; + aim_rxcallback_t userfunc; + aim_tlv_t *block; + struct aim_incomingim_ch4_args args; + int ret = 0; + + /* + * Make a bstream for the meaty part. Yum. Meat. + */ + if (!(block = aim_gettlv(tlvlist, 0x0005, 1))) + return -1; + aim_bstream_init(&meat, block->value, block->length); + + args.uin = aimbs_getle32(&meat); + args.type = aimbs_getle16(&meat); + args.msg = (char *)aimbs_getraw(&meat, aimbs_getle16(&meat)); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, channel, userinfo, &args); + + g_free(args.msg); + + return ret; +} + +/* + * It can easily be said that parsing ICBMs is THE single + * most difficult thing to do in the in AIM protocol. In + * fact, I think I just did say that. + * + * Below is the best damned solution I've come up with + * over the past sixteen months of battling with it. This + * can parse both away and normal messages from every client + * I have access to. Its not fast, its not clean. But it works. + * + */ +static int incomingim(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int i, ret = 0; + guint8 cookie[8]; + guint16 channel; + aim_userinfo_t userinfo; + + memset(&userinfo, 0x00, sizeof(aim_userinfo_t)); + + /* + * Read ICBM Cookie. And throw away. + */ + for (i = 0; i < 8; i++) + cookie[i] = aimbs_get8(bs); + + /* + * Channel ID. + * + * Channel 0x0001 is the message channel. There are + * other channels for things called "rendevous" + * which represent chat and some of the other new + * features of AIM2/3/3.5. + * + * Channel 0x0002 is the Rendevous channel, which + * is where Chat Invitiations and various client-client + * connection negotiations come from. + * + * Channel 0x0004 is used for ICQ authorization, or + * possibly any system notice. + * + */ + channel = aimbs_get16(bs); + + /* + * Extract the standard user info block. + * + * Note that although this contains TLVs that appear contiguous + * with the TLVs read below, they are two different pieces. The + * userinfo block contains the number of TLVs that contain user + * information, the rest are not even though there is no seperation. + * aim_extractuserinfo() returns the number of bytes used by the + * userinfo tlvs, so you can start reading the rest of them right + * afterward. + * + * That also means that TLV types can be duplicated between the + * userinfo block and the rest of the message, however there should + * never be two TLVs of the same type in one block. + * + */ + aim_extractuserinfo(sess, bs, &userinfo); + + /* + * From here on, its depends on what channel we're on. + * + * Technically all channels have a TLV list have this, however, + * for the common channel 1 case, in-place parsing is used for + * performance reasons (less memory allocation). + */ + if (channel == 1) { + + ret = incomingim_ch1(sess, mod, rx, snac, channel, &userinfo, bs, cookie); + + } else if (channel == 2) { + aim_tlvlist_t *tlvlist; + + /* + * Read block of TLVs (not including the userinfo data). All + * further data is derived from what is parsed here. + */ + tlvlist = aim_readtlvchain(bs); + + ret = incomingim_ch2(sess, mod, rx, snac, channel, &userinfo, tlvlist, cookie); + + aim_freetlvchain(&tlvlist); + + } else if (channel == 4) { + aim_tlvlist_t *tlvlist; + + tlvlist = aim_readtlvchain(bs); + ret = incomingim_ch4(sess, mod, rx, snac, channel, &userinfo, tlvlist, cookie); + aim_freetlvchain(&tlvlist); + + } else { + + do_error_dialog(sess->aux_data, "ICBM received on an unsupported channel. Ignoring.", "Gaim"); + + return 0; + } + + return ret; +} + +/* + * Possible codes: + * AIM_TRANSFER_DENY_NOTSUPPORTED -- "client does not support" + * AIM_TRANSFER_DENY_DECLINE -- "client has declined transfer" + * AIM_TRANSFER_DENY_NOTACCEPTING -- "client is not accepting transfers" + * + */ +int aim_denytransfer(aim_session_t *sess, const char *sender, const guint8 *cookie, guint16 code) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + aim_tlvlist_t *tl = NULL; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004))) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+8+2+1+strlen(sender)+6))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0004, 0x000b, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0004, 0x000b, 0x0000, snacid); + + aimbs_putraw(&fr->data, cookie, 8); + + aimbs_put16(&fr->data, 0x0002); /* channel */ + aimbs_put8(&fr->data, strlen(sender)); + aimbs_putraw(&fr->data, (guint8 *)sender, strlen(sender)); + + aim_addtlvtochain16(&tl, 0x0003, code); + aim_writetlvchain(&fr->data, &tl); + aim_freetlvchain(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * aim_reqicbmparaminfo() + * + * Request ICBM parameter information. + * + */ +int aim_reqicbmparams(aim_session_t *sess) +{ + aim_conn_t *conn; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004))) + return -EINVAL; + + return aim_genericreq_n(sess, conn, 0x0004, 0x0004); +} + +/* + * + * I definitly recommend sending this. If you don't, you'll be stuck + * with the rather unreasonable defaults. You don't want those. Send this. + * + */ +int aim_seticbmparam(aim_session_t *sess, struct aim_icbmparameters *params) +{ + aim_conn_t *conn; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !(conn = aim_conn_findbygroup(sess, 0x0004))) + return -EINVAL; + + if (!params) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+16))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0004, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0004, 0x0002, 0x0000, snacid); + + /* This is read-only (see Parameter Reply). Must be set to zero here. */ + aimbs_put16(&fr->data, 0x0000); + + /* These are all read-write */ + aimbs_put32(&fr->data, params->flags); + aimbs_put16(&fr->data, params->maxmsglen); + aimbs_put16(&fr->data, params->maxsenderwarn); + aimbs_put16(&fr->data, params->maxrecverwarn); + aimbs_put32(&fr->data, params->minmsginterval); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +static int paraminfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + struct aim_icbmparameters params; + aim_rxcallback_t userfunc; + + params.maxchan = aimbs_get16(bs); + params.flags = aimbs_get32(bs); + params.maxmsglen = aimbs_get16(bs); + params.maxsenderwarn = aimbs_get16(bs); + params.maxrecverwarn = aimbs_get16(bs); + params.minmsginterval = aimbs_get32(bs); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + return userfunc(sess, rx, ¶ms); + + return 0; +} + +static int missedcall(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + aim_rxcallback_t userfunc; + guint16 channel, nummissed, reason; + aim_userinfo_t userinfo; + + while (aim_bstream_empty(bs)) { + + channel = aimbs_get16(bs); + aim_extractuserinfo(sess, bs, &userinfo); + nummissed = aimbs_get16(bs); + reason = aimbs_get16(bs); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, channel, &userinfo, nummissed, reason); + } + + return ret; +} + +/* + * Receive the response from an ICQ status message request. This contains the + * ICQ status message. Go figure. + */ +static int clientautoresp(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + aim_rxcallback_t userfunc; + guint16 channel, reason; + char *sn; + guint8 *ck, snlen; + + ck = aimbs_getraw(bs, 8); + channel = aimbs_get16(bs); + snlen = aimbs_get8(bs); + sn = aimbs_getstr(bs, snlen); + reason = aimbs_get16(bs); + + switch (reason) { + case 0x0003: { /* ICQ status message. Maybe other stuff too, you never know with these people. */ + guint8 statusmsgtype, *msg; + guint16 len; + guint32 state; + + len = aimbs_getle16(bs); /* Should be 0x001b */ + aim_bstream_advance(bs, len); /* Unknown */ + + len = aimbs_getle16(bs); /* Should be 0x000e */ + aim_bstream_advance(bs, len); /* Unknown */ + + statusmsgtype = aimbs_getle8(bs); + switch (statusmsgtype) { + case 0xe8: + state = AIM_ICQ_STATE_AWAY; + break; + case 0xe9: + state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY; + break; + case 0xea: + state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_OUT; + break; + case 0xeb: + state = AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY; + break; + case 0xec: + state = AIM_ICQ_STATE_CHAT; + break; + default: + state = 0; + break; + } + + aimbs_getle8(bs); /* Unknown - 0x03 Maybe this means this is an auto-reply */ + aimbs_getle16(bs); /* Unknown - 0x0000 */ + aimbs_getle16(bs); /* Unknown - 0x0000 */ + + len = aimbs_getle16(bs); + msg = aimbs_getraw(bs, len); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, channel, sn, reason, state, msg); + + g_free(msg); + } break; + + default: { + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, channel, sn, reason); + } break; + } /* end switch */ + + g_free(ck); + g_free(sn); + + return ret; +} + +static int msgack(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_rxcallback_t userfunc; + guint16 type; + guint8 snlen, *ck; + char *sn; + int ret = 0; + + ck = aimbs_getraw(bs, 8); + type = aimbs_get16(bs); + snlen = aimbs_get8(bs); + sn = aimbs_getstr(bs, snlen); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, type, sn); + + g_free(sn); + g_free(ck); + + return ret; +} + +static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + + if (snac->subtype == 0x0005) + return paraminfo(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0006) + return outgoingim(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0007) + return incomingim(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x000a) + return missedcall(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x000b) + return clientautoresp(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x000c) + return msgack(sess, mod, rx, snac, bs); + + return 0; +} + +int msg_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x0004; + mod->version = 0x0001; + mod->toolid = 0x0110; + mod->toolversion = 0x0629; + mod->flags = 0; + strncpy(mod->name, "messaging", sizeof(mod->name)); + mod->snachandler = snachandler; + + return 0; +} diff --git a/protocols/oscar/im.h b/protocols/oscar/im.h new file mode 100644 index 00000000..061ff5b5 --- /dev/null +++ b/protocols/oscar/im.h @@ -0,0 +1,198 @@ +#ifndef __OSCAR_IM_H__ +#define __OSCAR_IM_H__ + +#define AIM_CB_FAM_MSG 0x0004 + +/* + * SNAC Family: Messaging Services. + */ +#define AIM_CB_MSG_ERROR 0x0001 +#define AIM_CB_MSG_PARAMINFO 0x0005 +#define AIM_CB_MSG_INCOMING 0x0007 +#define AIM_CB_MSG_EVIL 0x0009 +#define AIM_CB_MSG_MISSEDCALL 0x000a +#define AIM_CB_MSG_CLIENTAUTORESP 0x000b +#define AIM_CB_MSG_ACK 0x000c +#define AIM_CB_MSG_DEFAULT 0xffff + +#define AIM_IMFLAGS_AWAY 0x0001 /* mark as an autoreply */ +#define AIM_IMFLAGS_ACK 0x0002 /* request a receipt notice */ +#define AIM_IMFLAGS_UNICODE 0x0004 +#define AIM_IMFLAGS_ISO_8859_1 0x0008 +#define AIM_IMFLAGS_BUDDYREQ 0x0010 /* buddy icon requested */ +#define AIM_IMFLAGS_HASICON 0x0020 /* already has icon */ +#define AIM_IMFLAGS_SUBENC_MACINTOSH 0x0040 /* damn that Steve Jobs! */ +#define AIM_IMFLAGS_CUSTOMFEATURES 0x0080 /* features field present */ +#define AIM_IMFLAGS_EXTDATA 0x0100 +#define AIM_IMFLAGS_CUSTOMCHARSET 0x0200 /* charset fields set */ +#define AIM_IMFLAGS_MULTIPART 0x0400 /* ->mpmsg section valid */ +#define AIM_IMFLAGS_OFFLINE 0x0800 /* send to offline user */ + +/* + * Multipart message structures. + */ +typedef struct aim_mpmsg_section_s { + guint16 charset; + guint16 charsubset; + guint8 *data; + guint16 datalen; + struct aim_mpmsg_section_s *next; +} aim_mpmsg_section_t; + +typedef struct aim_mpmsg_s { + int numparts; + aim_mpmsg_section_t *parts; +} aim_mpmsg_t; + +int aim_mpmsg_init(aim_session_t *sess, aim_mpmsg_t *mpm); +int aim_mpmsg_addraw(aim_session_t *sess, aim_mpmsg_t *mpm, guint16 charset, guint16 charsubset, const guint8 *data, guint16 datalen); +int aim_mpmsg_addascii(aim_session_t *sess, aim_mpmsg_t *mpm, const char *ascii); +int aim_mpmsg_addunicode(aim_session_t *sess, aim_mpmsg_t *mpm, const guint16 *unicode, guint16 unicodelen); +void aim_mpmsg_free(aim_session_t *sess, aim_mpmsg_t *mpm); + +/* + * Arguments to aim_send_im_ext(). + * + * This is really complicated. But immensely versatile. + * + */ +struct aim_sendimext_args { + + /* These are _required_ */ + const char *destsn; + guint32 flags; /* often 0 */ + + /* Only required if not using multipart messages */ + const char *msg; + int msglen; + + /* Required if ->msg is not provided */ + aim_mpmsg_t *mpmsg; + + /* Only used if AIM_IMFLAGS_HASICON is set */ + guint32 iconlen; + time_t iconstamp; + guint32 iconsum; + + /* Only used if AIM_IMFLAGS_CUSTOMFEATURES is set */ + guint8 *features; + guint8 featureslen; + + /* Only used if AIM_IMFLAGS_CUSTOMCHARSET is set and mpmsg not used */ + guint16 charset; + guint16 charsubset; +}; + +/* + * Arguments to aim_send_rtfmsg(). + */ +struct aim_sendrtfmsg_args { + const char *destsn; + guint32 fgcolor; + guint32 bgcolor; + const char *rtfmsg; /* must be in RTF */ +}; + +/* + * This information is provided in the Incoming ICBM callback for + * Channel 1 ICBM's. + * + * Note that although CUSTOMFEATURES and CUSTOMCHARSET say they + * are optional, both are always set by the current libfaim code. + * That may or may not change in the future. It is mainly for + * consistency with aim_sendimext_args. + * + * Multipart messages require some explanation. If you want to use them, + * I suggest you read all the comments in im.c. + * + */ +struct aim_incomingim_ch1_args { + + /* Always provided */ + aim_mpmsg_t mpmsg; + guint32 icbmflags; /* some flags apply only to ->msg, not all mpmsg */ + + /* Only provided if message has a human-readable section */ + char *msg; + int msglen; + + /* Only provided if AIM_IMFLAGS_HASICON is set */ + time_t iconstamp; + guint32 iconlen; + guint16 iconsum; + + /* Only provided if AIM_IMFLAGS_CUSTOMFEATURES is set */ + guint8 *features; + guint8 featureslen; + + /* Only provided if AIM_IMFLAGS_EXTDATA is set */ + guint8 extdatalen; + guint8 *extdata; + + /* Only used if AIM_IMFLAGS_CUSTOMCHARSET is set */ + guint16 charset; + guint16 charsubset; +}; + +/* Valid values for channel 2 args->status */ +#define AIM_RENDEZVOUS_PROPOSE 0x0000 +#define AIM_RENDEZVOUS_CANCEL 0x0001 +#define AIM_RENDEZVOUS_ACCEPT 0x0002 + +struct aim_incomingim_ch2_args { + guint8 cookie[8]; + guint16 reqclass; + guint16 status; + guint16 errorcode; + const char *clientip; + const char *clientip2; + const char *verifiedip; + guint16 port; + const char *msg; /* invite message or file description */ + const char *encoding; + const char *language; + union { + struct { + guint32 checksum; + guint32 length; + time_t timestamp; + guint8 *icon; + } icon; + struct { + struct aim_chat_roominfo roominfo; + } chat; + struct { + guint32 fgcolor; + guint32 bgcolor; + const char *rtfmsg; + } rtfmsg; + } info; + void *destructor; /* used internally only */ +}; + +/* Valid values for channel 4 args->type */ +#define AIM_ICQMSG_AUTHREQUEST 0x0006 +#define AIM_ICQMSG_AUTHDENIED 0x0007 +#define AIM_ICQMSG_AUTHGRANTED 0x0008 + +struct aim_incomingim_ch4_args { + guint32 uin; /* Of the sender of the ICBM */ + guint16 type; + char *msg; /* Reason for auth request, deny, or accept */ +}; + +int aim_send_rtfmsg(aim_session_t *sess, struct aim_sendrtfmsg_args *args); +int aim_send_im_ext(aim_session_t *sess, struct aim_sendimext_args *args); +int aim_send_im(aim_session_t *, const char *destsn, unsigned short flags, const char *msg); +int aim_send_icon(aim_session_t *sess, const char *sn, const guint8 *icon, int iconlen, time_t stamp, guint16 iconsum); +guint16 aim_iconsum(const guint8 *buf, int buflen); +int aim_send_typing(aim_session_t *sess, aim_conn_t *conn, int typing); +int aim_send_im_direct(aim_session_t *, aim_conn_t *, const char *msg, int len); +const char *aim_directim_getsn(aim_conn_t *conn); +aim_conn_t *aim_directim_initiate(aim_session_t *, const char *destsn); +aim_conn_t *aim_directim_connect(aim_session_t *, const char *sn, const char *addr, const guint8 *cookie); + +int aim_send_im_ch2_geticqmessage(aim_session_t *sess, const char *sn, int type); +int aim_send_im_ch2_statusmessage(aim_session_t *sess, const char *sender, const guint8 *cookie, const char *message, const guint8 state, const guint16 dc); + +#endif /* __OSCAR_IM_H__ */ diff --git a/protocols/oscar/info.c b/protocols/oscar/info.c new file mode 100644 index 00000000..ffe29d1f --- /dev/null +++ b/protocols/oscar/info.c @@ -0,0 +1,725 @@ +/* + * aim_info.c + * + * The functions here are responsible for requesting and parsing information- + * gathering SNACs. Or something like that. + * + */ + +#include <aim.h> +#include "info.h" + +struct aim_priv_inforeq { + char sn[MAXSNLEN+1]; + guint16 infotype; +}; + +int aim_getinfo(aim_session_t *sess, aim_conn_t *conn, const char *sn, guint16 infotype) +{ + struct aim_priv_inforeq privdata; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !conn || !sn) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 12+1+strlen(sn)))) + return -ENOMEM; + + strncpy(privdata.sn, sn, sizeof(privdata.sn)); + privdata.infotype = infotype; + snacid = aim_cachesnac(sess, 0x0002, 0x0005, 0x0000, &privdata, sizeof(struct aim_priv_inforeq)); + + aim_putsnac(&fr->data, 0x0002, 0x0005, 0x0000, snacid); + aimbs_put16(&fr->data, infotype); + aimbs_put8(&fr->data, strlen(sn)); + aimbs_putraw(&fr->data, (guint8 *)sn, strlen(sn)); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +const char *aim_userinfo_sn(aim_userinfo_t *ui) +{ + + if (!ui) + return NULL; + + return ui->sn; +} + +guint16 aim_userinfo_flags(aim_userinfo_t *ui) +{ + + if (!ui) + return 0; + + return ui->flags; +} + +guint16 aim_userinfo_idle(aim_userinfo_t *ui) +{ + + if (!ui) + return 0; + + return ui->idletime; +} + +float aim_userinfo_warnlevel(aim_userinfo_t *ui) +{ + + if (!ui) + return 0.00; + + return (ui->warnlevel / 10); +} + +time_t aim_userinfo_membersince(aim_userinfo_t *ui) +{ + + if (!ui) + return 0; + + return (time_t)ui->membersince; +} + +time_t aim_userinfo_onlinesince(aim_userinfo_t *ui) +{ + + if (!ui) + return 0; + + return (time_t)ui->onlinesince; +} + +guint32 aim_userinfo_sessionlen(aim_userinfo_t *ui) +{ + + if (!ui) + return 0; + + return ui->sessionlen; +} + +int aim_userinfo_hascap(aim_userinfo_t *ui, guint32 cap) +{ + + if (!ui || !(ui->present & AIM_USERINFO_PRESENT_CAPABILITIES)) + return -1; + + return !!(ui->capabilities & cap); +} + + +/* + * Capability blocks. + * + * These are CLSIDs. They should actually be of the form: + * + * {0x0946134b, 0x4c7f, 0x11d1, + * {0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}}, + * + * But, eh. + */ +static const struct { + guint32 flag; + guint8 data[16]; +} aim_caps[] = { + + /* + * Chat is oddball. + */ + {AIM_CAPS_CHAT, + {0x74, 0x8f, 0x24, 0x20, 0x62, 0x87, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + /* + * These are mostly in order. + */ + {AIM_CAPS_VOICE, + {0x09, 0x46, 0x13, 0x41, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_SENDFILE, + {0x09, 0x46, 0x13, 0x43, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + /* + * Advertised by the EveryBuddy client. + */ + {AIM_CAPS_ICQ, + {0x09, 0x46, 0x13, 0x44, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_IMIMAGE, + {0x09, 0x46, 0x13, 0x45, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_BUDDYICON, + {0x09, 0x46, 0x13, 0x46, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_SAVESTOCKS, + {0x09, 0x46, 0x13, 0x47, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_GETFILE, + {0x09, 0x46, 0x13, 0x48, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + /* + * Client supports channel 2 extended, TLV(0x2711) based messages. + * Currently used only by ICQ clients. ICQ clients and clones use this GUID + * as message format sign. Trillian client use another GUID in channel 2 + * messages to implement its own message format (trillian doesn't use + * TLV(x2711) in SecureIM channel 2 messages!). + */ + {AIM_CAPS_ICQSERVERRELAY, + {0x09, 0x46, 0x13, 0x49, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + /* + * Indeed, there are two of these. The former appears to be correct, + * but in some versions of winaim, the second one is set. Either they + * forgot to fix endianness, or they made a typo. It really doesn't + * matter which. + */ + {AIM_CAPS_GAMES, + {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + {AIM_CAPS_GAMES2, + {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1, + 0x22, 0x82, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_SENDBUDDYLIST, + {0x09, 0x46, 0x13, 0x4b, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_UTF8, + {0x09, 0x46, 0x13, 0x4E, 0x4C, 0x7F, 0x11, 0xD1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_ICQRTF, + {0x97, 0xb1, 0x27, 0x51, 0x24, 0x3c, 0x43, 0x34, + 0xad, 0x22, 0xd6, 0xab, 0xf7, 0x3f, 0x14, 0x92}}, + + {AIM_CAPS_ICQUNKNOWN, + {0x2e, 0x7a, 0x64, 0x75, 0xfa, 0xdf, 0x4d, 0xc8, + 0x88, 0x6f, 0xea, 0x35, 0x95, 0xfd, 0xb6, 0xdf}}, + + {AIM_CAPS_EMPTY, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + + {AIM_CAPS_TRILLIANCRYPT, + {0xf2, 0xe7, 0xc7, 0xf4, 0xfe, 0xad, 0x4d, 0xfb, + 0xb2, 0x35, 0x36, 0x79, 0x8b, 0xdf, 0x00, 0x00}}, + + {AIM_CAPS_APINFO, + {0xAA, 0x4A, 0x32, 0xB5, 0xF8, 0x84, 0x48, 0xc6, + 0xA3, 0xD7, 0x8C, 0x50, 0x97, 0x19, 0xFD, 0x5B}}, + + {AIM_CAPS_INTEROP, + {0x09, 0x46, 0x13, 0x4d, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_ICHAT, + {0x09, 0x46, 0x00, 0x00, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + {AIM_CAPS_LAST} +}; + +/* + * This still takes a length parameter even with a bstream because capabilities + * are not naturally bounded. + * + */ +guint32 aim_getcap(aim_session_t *sess, aim_bstream_t *bs, int len) +{ + guint32 flags = 0; + int offset; + + for (offset = 0; aim_bstream_empty(bs) && (offset < len); offset += 0x10) { + guint8 *cap; + int i, identified; + + cap = aimbs_getraw(bs, 0x10); + + for (i = 0, identified = 0; !(aim_caps[i].flag & AIM_CAPS_LAST); i++) { + + if (memcmp(&aim_caps[i].data, cap, 0x10) == 0) { + flags |= aim_caps[i].flag; + identified++; + break; /* should only match once... */ + + } + } + + if (!identified) { + /*FIXME*/ + g_strdup_printf("unknown capability: {%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n", + cap[0], cap[1], cap[2], cap[3], + cap[4], cap[5], + cap[6], cap[7], + cap[8], cap[9], + cap[10], cap[11], cap[12], cap[13], + cap[14], cap[15]); + + } + + g_free(cap); + } + + return flags; +} + +int aim_putcap(aim_bstream_t *bs, guint32 caps) +{ + int i; + + if (!bs) + return -EINVAL; + + for (i = 0; aim_bstream_empty(bs); i++) { + + if (aim_caps[i].flag == AIM_CAPS_LAST) + break; + + if (caps & aim_caps[i].flag) + aimbs_putraw(bs, aim_caps[i].data, 0x10); + + } + + return 0; +} + +/* + * AIM is fairly regular about providing user info. This is a generic + * routine to extract it in its standard form. + */ +int aim_extractuserinfo(aim_session_t *sess, aim_bstream_t *bs, aim_userinfo_t *outinfo) +{ + int curtlv, tlvcnt; + guint8 snlen; + + if (!bs || !outinfo) + return -EINVAL; + + /* Clear out old data first */ + memset(outinfo, 0x00, sizeof(aim_userinfo_t)); + + /* + * Screen name. Stored as an unterminated string prepended with a + * byte containing its length. + */ + snlen = aimbs_get8(bs); + aimbs_getrawbuf(bs, (guint8 *)outinfo->sn, snlen); + + /* + * Warning Level. Stored as an unsigned short. + */ + outinfo->warnlevel = aimbs_get16(bs); + + /* + * TLV Count. Unsigned short representing the number of + * Type-Length-Value triples that follow. + */ + tlvcnt = aimbs_get16(bs); + + /* + * Parse out the Type-Length-Value triples as they're found. + */ + for (curtlv = 0; curtlv < tlvcnt; curtlv++) { + int endpos; + guint16 type, length; + + type = aimbs_get16(bs); + length = aimbs_get16(bs); + + endpos = aim_bstream_curpos(bs) + length; + + if (type == 0x0001) { + /* + * Type = 0x0001: User flags + * + * Specified as any of the following ORed together: + * 0x0001 Trial (user less than 60days) + * 0x0002 Unknown bit 2 + * 0x0004 AOL Main Service user + * 0x0008 Unknown bit 4 + * 0x0010 Free (AIM) user + * 0x0020 Away + * 0x0400 ActiveBuddy + * + */ + outinfo->flags = aimbs_get16(bs); + outinfo->present |= AIM_USERINFO_PRESENT_FLAGS; + + } else if (type == 0x0002) { + /* + * Type = 0x0002: Member-Since date. + * + * The time/date that the user originally registered for + * the service, stored in time_t format. + */ + outinfo->membersince = aimbs_get32(bs); + outinfo->present |= AIM_USERINFO_PRESENT_MEMBERSINCE; + + } else if (type == 0x0003) { + /* + * Type = 0x0003: On-Since date. + * + * The time/date that the user started their current + * session, stored in time_t format. + */ + outinfo->onlinesince = aimbs_get32(bs); + outinfo->present |= AIM_USERINFO_PRESENT_ONLINESINCE; + + } else if (type == 0x0004) { + /* + * Type = 0x0004: Idle time. + * + * Number of seconds since the user actively used the + * service. + * + * Note that the client tells the server when to start + * counting idle times, so this may or may not be + * related to reality. + */ + outinfo->idletime = aimbs_get16(bs); + outinfo->present |= AIM_USERINFO_PRESENT_IDLE; + + } else if (type == 0x0006) { + /* + * Type = 0x0006: ICQ Online Status + * + * ICQ's Away/DND/etc "enriched" status. Some decoding + * of values done by Scott <darkagl@pcnet.com> + */ + aimbs_get16(bs); + outinfo->icqinfo.status = aimbs_get16(bs); + outinfo->present |= AIM_USERINFO_PRESENT_ICQEXTSTATUS; + + } else if (type == 0x000a) { + /* + * Type = 0x000a + * + * ICQ User IP Address. + * Ahh, the joy of ICQ security. + */ + outinfo->icqinfo.ipaddr = aimbs_get32(bs); + outinfo->present |= AIM_USERINFO_PRESENT_ICQIPADDR; + + } else if (type == 0x000c) { + /* + * Type = 0x000c + * + * random crap containing the IP address, + * apparently a port number, and some Other Stuff. + * + */ + aimbs_getrawbuf(bs, outinfo->icqinfo.crap, 0x25); + outinfo->present |= AIM_USERINFO_PRESENT_ICQDATA; + + } else if (type == 0x000d) { + /* + * Type = 0x000d + * + * Capability information. + * + */ + outinfo->capabilities = aim_getcap(sess, bs, length); + outinfo->present |= AIM_USERINFO_PRESENT_CAPABILITIES; + + } else if (type == 0x000e) { + /* + * Type = 0x000e + * + * Unknown. Always of zero length, and always only + * on AOL users. + * + * Ignore. + * + */ + + } else if ((type == 0x000f) || (type == 0x0010)) { + /* + * Type = 0x000f: Session Length. (AIM) + * Type = 0x0010: Session Length. (AOL) + * + * The duration, in seconds, of the user's current + * session. + * + * Which TLV type this comes in depends on the + * service the user is using (AIM or AOL). + * + */ + outinfo->sessionlen = aimbs_get32(bs); + outinfo->present |= AIM_USERINFO_PRESENT_SESSIONLEN; + + } else { + + /* + * Reaching here indicates that either AOL has + * added yet another TLV for us to deal with, + * or the parsing has gone Terribly Wrong. + * + * Either way, inform the owner and attempt + * recovery. + * + */ +#ifdef DEBUG + // do_error_dialog(sess->aux_data, G_STRLOC, "Unknown TLV encountered"); +#endif + + } + + /* Save ourselves. */ + aim_bstream_setpos(bs, endpos); + } + + return 0; +} + +/* + * Inverse of aim_extractuserinfo() + */ +int aim_putuserinfo(aim_bstream_t *bs, aim_userinfo_t *info) +{ + aim_tlvlist_t *tlvlist = NULL; + + if (!bs || !info) + return -EINVAL; + + aimbs_put8(bs, strlen(info->sn)); + aimbs_putraw(bs, (guint8 *)info->sn, strlen(info->sn)); + + aimbs_put16(bs, info->warnlevel); + + + aim_addtlvtochain16(&tlvlist, 0x0001, info->flags); + aim_addtlvtochain32(&tlvlist, 0x0002, info->membersince); + aim_addtlvtochain32(&tlvlist, 0x0003, info->onlinesince); + aim_addtlvtochain16(&tlvlist, 0x0004, info->idletime); + +#if ICQ_OSCAR_SUPPORT + if (atoi(info->sn) != 0) { + aim_addtlvtochain16(&tlvlist, 0x0006, info->icqinfo.status); + aim_addtlvtochain32(&tlvlist, 0x000a, info->icqinfo.ipaddr); + } +#endif + + aim_addtlvtochain_caps(&tlvlist, 0x000d, info->capabilities); + + aim_addtlvtochain32(&tlvlist, (guint16)((info->flags & AIM_FLAG_AOL) ? 0x0010 : 0x000f), info->sessionlen); + + aimbs_put16(bs, aim_counttlvchain(&tlvlist)); + aim_writetlvchain(bs, &tlvlist); + aim_freetlvchain(&tlvlist); + + return 0; +} + +int aim_sendbuddyoncoming(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *info) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !conn || !info) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0003, 0x000b, 0x0000, NULL, 0); + + aim_putsnac(&fr->data, 0x0003, 0x000b, 0x0000, snacid); + aim_putuserinfo(&fr->data, info); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +int aim_sendbuddyoffgoing(aim_session_t *sess, aim_conn_t *conn, const char *sn) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !conn || !sn) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0003, 0x000c, 0x0000, NULL, 0); + + aim_putsnac(&fr->data, 0x0003, 0x000c, 0x0000, snacid); + aimbs_put8(&fr->data, strlen(sn)); + aimbs_putraw(&fr->data, (guint8 *)sn, strlen(sn)); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Huh? What is this? + */ +int aim_0002_000b(aim_session_t *sess, aim_conn_t *conn, const char *sn) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !conn || !sn) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+1+strlen(sn)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0002, 0x000b, 0x0000, NULL, 0); + + aim_putsnac(&fr->data, 0x0002, 0x000b, 0x0000, snacid); + aimbs_put8(&fr->data, strlen(sn)); + aimbs_putraw(&fr->data, (guint8 *)sn, strlen(sn)); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Normally contains: + * t(0001) - short containing max profile length (value = 1024) + * t(0002) - short - unknown (value = 16) [max MIME type length?] + * t(0003) - short - unknown (value = 10) + * t(0004) - short - unknown (value = 2048) [ICQ only?] + */ +static int rights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_tlvlist_t *tlvlist; + aim_rxcallback_t userfunc; + int ret = 0; + guint16 maxsiglen = 0; + + tlvlist = aim_readtlvchain(bs); + + if (aim_gettlv(tlvlist, 0x0001, 1)) + maxsiglen = aim_gettlv16(tlvlist, 0x0001, 1); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, maxsiglen); + + aim_freetlvchain(&tlvlist); + + return ret; +} + +static int userinfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_userinfo_t userinfo; + char *text_encoding = NULL, *text = NULL; + guint16 text_length = 0; + aim_rxcallback_t userfunc; + aim_tlvlist_t *tlvlist; + aim_tlv_t *tlv; + aim_snac_t *origsnac = NULL; + struct aim_priv_inforeq *inforeq; + int ret = 0; + + origsnac = aim_remsnac(sess, snac->id); + + if (!origsnac || !origsnac->data) { + do_error_dialog(sess->aux_data, "major problem: no snac stored!", "Gaim"); + return 0; + } + + inforeq = (struct aim_priv_inforeq *)origsnac->data; + + if ((inforeq->infotype != AIM_GETINFO_GENERALINFO) && + (inforeq->infotype != AIM_GETINFO_AWAYMESSAGE) && + (inforeq->infotype != AIM_GETINFO_CAPABILITIES)) { + do_error_dialog(sess->aux_data, "unknown infotype in request!", "Gaim"); + return 0; + } + + aim_extractuserinfo(sess, bs, &userinfo); + + tlvlist = aim_readtlvchain(bs); + + /* + * Depending on what informational text was requested, different + * TLVs will appear here. + * + * Profile will be 1 and 2, away message will be 3 and 4, caps + * will be 5. + */ + if (inforeq->infotype == AIM_GETINFO_GENERALINFO) { + text_encoding = aim_gettlv_str(tlvlist, 0x0001, 1); + if((tlv = aim_gettlv(tlvlist, 0x0002, 1))) { + text = g_new0(char, tlv->length); + memcpy(text, tlv->value, tlv->length); + text_length = tlv->length; + } + } else if (inforeq->infotype == AIM_GETINFO_AWAYMESSAGE) { + text_encoding = aim_gettlv_str(tlvlist, 0x0003, 1); + if((tlv = aim_gettlv(tlvlist, 0x0004, 1))) { + text = g_new0(char, tlv->length); + memcpy(text, tlv->value, tlv->length); + text_length = tlv->length; + } + } else if (inforeq->infotype == AIM_GETINFO_CAPABILITIES) { + aim_tlv_t *ct; + + if ((ct = aim_gettlv(tlvlist, 0x0005, 1))) { + aim_bstream_t cbs; + + aim_bstream_init(&cbs, ct->value, ct->length); + + userinfo.capabilities = aim_getcap(sess, &cbs, ct->length); + userinfo.present = AIM_USERINFO_PRESENT_CAPABILITIES; + } + } + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, &userinfo, inforeq->infotype, text_encoding, text, text_length); + + g_free(text_encoding); + g_free(text); + + aim_freetlvchain(&tlvlist); + + if (origsnac) + g_free(origsnac->data); + g_free(origsnac); + + return ret; +} + +static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + + if (snac->subtype == 0x0003) + return rights(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0006) + return userinfo(sess, mod, rx, snac, bs); + + return 0; +} + +int locate_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x0002; + mod->version = 0x0001; + mod->toolid = 0x0110; + mod->toolversion = 0x0629; + mod->flags = 0; + strncpy(mod->name, "locate", sizeof(mod->name)); + mod->snachandler = snachandler; + + return 0; +} diff --git a/protocols/oscar/info.h b/protocols/oscar/info.h new file mode 100644 index 00000000..b4d99e9f --- /dev/null +++ b/protocols/oscar/info.h @@ -0,0 +1,44 @@ +#ifndef __OSCAR_INFO_H__ +#define __OSCAR_INFO_H__ + +#define AIM_CB_FAM_LOC 0x0002 + +/* + * SNAC Family: Location Services. + */ +#define AIM_CB_LOC_ERROR 0x0001 +#define AIM_CB_LOC_REQRIGHTS 0x0002 +#define AIM_CB_LOC_RIGHTSINFO 0x0003 +#define AIM_CB_LOC_SETUSERINFO 0x0004 +#define AIM_CB_LOC_REQUSERINFO 0x0005 +#define AIM_CB_LOC_USERINFO 0x0006 +#define AIM_CB_LOC_WATCHERSUBREQ 0x0007 +#define AIM_CB_LOC_WATCHERNOT 0x0008 +#define AIM_CB_LOC_DEFAULT 0xffff + +#define AIM_CAPS_BUDDYICON 0x00000001 +#define AIM_CAPS_VOICE 0x00000002 +#define AIM_CAPS_IMIMAGE 0x00000004 +#define AIM_CAPS_CHAT 0x00000008 +#define AIM_CAPS_GETFILE 0x00000010 +#define AIM_CAPS_SENDFILE 0x00000020 +#define AIM_CAPS_GAMES 0x00000040 +#define AIM_CAPS_SAVESTOCKS 0x00000080 +#define AIM_CAPS_SENDBUDDYLIST 0x00000100 +#define AIM_CAPS_GAMES2 0x00000200 +#define AIM_CAPS_ICQ 0x00000400 +#define AIM_CAPS_APINFO 0x00000800 +#define AIM_CAPS_ICQRTF 0x00001000 +#define AIM_CAPS_EMPTY 0x00002000 +#define AIM_CAPS_ICQSERVERRELAY 0x00004000 +#define AIM_CAPS_ICQUNKNOWN 0x00008000 +#define AIM_CAPS_TRILLIANCRYPT 0x00010000 +#define AIM_CAPS_UTF8 0x00020000 +#define AIM_CAPS_INTEROP 0x00040000 +#define AIM_CAPS_ICHAT 0x00080000 +#define AIM_CAPS_EXTCHAN2 0x00100000 +#define AIM_CAPS_LAST 0x00200000 + +int aim_0002_000b(aim_session_t *sess, aim_conn_t *conn, const char *sn); + +#endif /* __OSCAR_INFO_H__ */ diff --git a/protocols/oscar/misc.c b/protocols/oscar/misc.c new file mode 100644 index 00000000..e5c5c26f --- /dev/null +++ b/protocols/oscar/misc.c @@ -0,0 +1,396 @@ + +/* + * aim_misc.c + * + * TODO: Seperate a lot of this into an aim_bos.c. + * + * Other things... + * + * - Idle setting + * + * + */ + +#include <aim.h> + +/* + * aim_bos_setbuddylist(buddylist) + * + * This just builds the "set buddy list" command then queues it. + * + * buddy_list = "Screen Name One&ScreenNameTwo&"; + * + * TODO: Clean this up. + * + * XXX: I can't stress the TODO enough. + * + */ +int aim_bos_setbuddylist(aim_session_t *sess, aim_conn_t *conn, const char *buddy_list) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + int len = 0; + char *localcpy = NULL; + char *tmpptr = NULL; + + if (!buddy_list || !(localcpy = g_strdup(buddy_list))) + return -EINVAL; + + for (tmpptr = strtok(localcpy, "&"); tmpptr; ) { + len += 1 + strlen(tmpptr); + tmpptr = strtok(NULL, "&"); + } + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+len))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0003, 0x0004, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0003, 0x0004, 0x0000, snacid); + + strncpy(localcpy, buddy_list, strlen(buddy_list) + 1); + + for (tmpptr = strtok(localcpy, "&"); tmpptr; ) { + + aimbs_put8(&fr->data, strlen(tmpptr)); + aimbs_putraw(&fr->data, (guint8 *)tmpptr, strlen(tmpptr)); + tmpptr = strtok(NULL, "&"); + } + + aim_tx_enqueue(sess, fr); + + g_free(localcpy); + + return 0; +} + +/* + * aim_bos_setprofile(profile) + * + * Gives BOS your profile. + * + */ +int aim_bos_setprofile(aim_session_t *sess, aim_conn_t *conn, const char *profile, const char *awaymsg, guint32 caps) +{ + static const char defencoding[] = {"text/aolrtf; charset=\"utf-8\""}; + aim_frame_t *fr; + aim_tlvlist_t *tl = NULL; + aim_snacid_t snacid; + + /* Build to packet first to get real length */ + if (profile) { + aim_addtlvtochain_raw(&tl, 0x0001, strlen(defencoding), (guint8 *)defencoding); + aim_addtlvtochain_raw(&tl, 0x0002, strlen(profile), (guint8 *)profile); + } + + /* + * So here's how this works: + * - You are away when you have a non-zero-length type 4 TLV stored. + * - You become unaway when you clear the TLV with a zero-length + * type 4 TLV. + * - If you do not send the type 4 TLV, your status does not change + * (that is, if you were away, you'll remain away). + */ + if (awaymsg) { + if (strlen(awaymsg)) { + aim_addtlvtochain_raw(&tl, 0x0003, strlen(defencoding), (guint8 *)defencoding); + aim_addtlvtochain_raw(&tl, 0x0004, strlen(awaymsg), (guint8 *)awaymsg); + } else + aim_addtlvtochain_noval(&tl, 0x0004); + } + + aim_addtlvtochain_caps(&tl, 0x0005, caps); + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + aim_sizetlvchain(&tl)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0002, 0x0004, 0x0000, NULL, 0); + + aim_putsnac(&fr->data, 0x0002, 0x004, 0x0000, snacid); + aim_writetlvchain(&fr->data, &tl); + aim_freetlvchain(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * aim_bos_reqbuddyrights() + * + * Request Buddy List rights. + * + */ +int aim_bos_reqbuddyrights(aim_session_t *sess, aim_conn_t *conn) +{ + return aim_genericreq_n(sess, conn, 0x0003, 0x0002); +} + +/* + * Send a warning to destsn. + * + * Flags: + * AIM_WARN_ANON Send as an anonymous (doesn't count as much) + * + * returns -1 on error (couldn't alloc packet), 0 on success. + * + */ +int aim_send_warning(aim_session_t *sess, aim_conn_t *conn, const char *destsn, guint32 flags) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + guint16 outflags = 0x0000; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, strlen(destsn)+13))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0004, 0x0008, 0x0000, destsn, strlen(destsn)+1); + + aim_putsnac(&fr->data, 0x0004, 0x0008, 0x0000, snacid); + + if (flags & AIM_WARN_ANON) + outflags |= 0x0001; + + aimbs_put16(&fr->data, outflags); + aimbs_put8(&fr->data, strlen(destsn)); + aimbs_putraw(&fr->data, (guint8 *)destsn, strlen(destsn)); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Generic routine for sending commands. + * + * + * I know I can do this in a smarter way...but I'm not thinking straight + * right now... + * + * I had one big function that handled all three cases, but then it broke + * and I split it up into three. But then I fixed it. I just never went + * back to the single. I don't see any advantage to doing it either way. + * + */ +int aim_genericreq_n(aim_session_t *sess, aim_conn_t *conn, guint16 family, guint16 subtype) +{ + aim_frame_t *fr; + aim_snacid_t snacid = 0x00000000; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10))) + return -ENOMEM; + + aim_putsnac(&fr->data, family, subtype, 0x0000, snacid); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +int aim_genericreq_n_snacid(aim_session_t *sess, aim_conn_t *conn, guint16 family, guint16 subtype) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, family, subtype, 0x0000, NULL, 0); + aim_putsnac(&fr->data, family, subtype, 0x0000, snacid); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +int aim_genericreq_l(aim_session_t *sess, aim_conn_t *conn, guint16 family, guint16 subtype, guint32 *longdata) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!longdata) + return aim_genericreq_n(sess, conn, family, subtype); + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+4))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, family, subtype, 0x0000, NULL, 0); + + aim_putsnac(&fr->data, family, subtype, 0x0000, snacid); + aimbs_put32(&fr->data, *longdata); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +int aim_genericreq_s(aim_session_t *sess, aim_conn_t *conn, guint16 family, guint16 subtype, guint16 *shortdata) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!shortdata) + return aim_genericreq_n(sess, conn, family, subtype); + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, family, subtype, 0x0000, NULL, 0); + + aim_putsnac(&fr->data, family, subtype, 0x0000, snacid); + aimbs_put16(&fr->data, *shortdata); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * aim_bos_reqlocaterights() + * + * Request Location services rights. + * + */ +int aim_bos_reqlocaterights(aim_session_t *sess, aim_conn_t *conn) +{ + return aim_genericreq_n(sess, conn, 0x0002, 0x0002); +} + +/* + * Set directory profile data (not the same as aim_bos_setprofile!) + * + * privacy: 1 to allow searching, 0 to disallow. + */ +int aim_setdirectoryinfo(aim_session_t *sess, aim_conn_t *conn, const char *first, const char *middle, const char *last, const char *maiden, const char *nickname, const char *street, const char *city, const char *state, const char *zip, int country, guint16 privacy) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + aim_tlvlist_t *tl = NULL; + + + aim_addtlvtochain16(&tl, 0x000a, privacy); + + if (first) + aim_addtlvtochain_raw(&tl, 0x0001, strlen(first), (guint8 *)first); + if (last) + aim_addtlvtochain_raw(&tl, 0x0002, strlen(last), (guint8 *)last); + if (middle) + aim_addtlvtochain_raw(&tl, 0x0003, strlen(middle), (guint8 *)middle); + if (maiden) + aim_addtlvtochain_raw(&tl, 0x0004, strlen(maiden), (guint8 *)maiden); + + if (state) + aim_addtlvtochain_raw(&tl, 0x0007, strlen(state), (guint8 *)state); + if (city) + aim_addtlvtochain_raw(&tl, 0x0008, strlen(city), (guint8 *)city); + + if (nickname) + aim_addtlvtochain_raw(&tl, 0x000c, strlen(nickname), (guint8 *)nickname); + if (zip) + aim_addtlvtochain_raw(&tl, 0x000d, strlen(zip), (guint8 *)zip); + + if (street) + aim_addtlvtochain_raw(&tl, 0x0021, strlen(street), (guint8 *)street); + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+aim_sizetlvchain(&tl)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0002, 0x0009, 0x0000, NULL, 0); + + aim_putsnac(&fr->data, 0x0002, 0x0009, 0x0000, snacid); + aim_writetlvchain(&fr->data, &tl); + aim_freetlvchain(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* XXX pass these in better */ +int aim_setuserinterests(aim_session_t *sess, aim_conn_t *conn, const char *interest1, const char *interest2, const char *interest3, const char *interest4, const char *interest5, guint16 privacy) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + aim_tlvlist_t *tl = NULL; + + /* ?? privacy ?? */ + aim_addtlvtochain16(&tl, 0x000a, privacy); + + if (interest1) + aim_addtlvtochain_raw(&tl, 0x0000b, strlen(interest1), (guint8 *)interest1); + if (interest2) + aim_addtlvtochain_raw(&tl, 0x0000b, strlen(interest2), (guint8 *)interest2); + if (interest3) + aim_addtlvtochain_raw(&tl, 0x0000b, strlen(interest3), (guint8 *)interest3); + if (interest4) + aim_addtlvtochain_raw(&tl, 0x0000b, strlen(interest4), (guint8 *)interest4); + if (interest5) + aim_addtlvtochain_raw(&tl, 0x0000b, strlen(interest5), (guint8 *)interest5); + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+aim_sizetlvchain(&tl)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0002, 0x000f, 0x0000, NULL, 0); + + aim_putsnac(&fr->data, 0x0002, 0x000f, 0x0000, 0); + aim_writetlvchain(&fr->data, &tl); + aim_freetlvchain(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Should be generic enough to handle the errors for all groups. + * + */ +static int generror(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + int error = 0; + aim_rxcallback_t userfunc; + aim_snac_t *snac2; + + snac2 = aim_remsnac(sess, snac->id); + + if (aim_bstream_empty(bs)) + error = aimbs_get16(bs); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, error, snac2 ? snac2->data : NULL); + + if (snac2) + g_free(snac2->data); + g_free(snac2); + + return ret; +} + +static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + + if (snac->subtype == 0x0001) + return generror(sess, mod, rx, snac, bs); + else if ((snac->family == 0xffff) && (snac->subtype == 0xffff)) { + aim_rxcallback_t userfunc; + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + return userfunc(sess, rx); + } + + return 0; +} + +int misc_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0xffff; + mod->version = 0x0000; + mod->flags = AIM_MODFLAG_MULTIFAMILY; + strncpy(mod->name, "misc", sizeof(mod->name)); + mod->snachandler = snachandler; + + return 0; +} + + diff --git a/protocols/oscar/msgcookie.c b/protocols/oscar/msgcookie.c new file mode 100644 index 00000000..bb498d72 --- /dev/null +++ b/protocols/oscar/msgcookie.c @@ -0,0 +1,196 @@ +/* + * Cookie Caching stuff. Adam wrote this, apparently just some + * derivatives of n's SNAC work. I cleaned it up, added comments. + * + */ + +/* + * I'm assuming that cookies are type-specific. that is, we can have + * "1234578" for type 1 and type 2 concurrently. if i'm wrong, then we + * lose some error checking. if we assume cookies are not type-specific and are + * wrong, we get quirky behavior when cookies step on each others' toes. + */ + +#include <aim.h> +#include "info.h" + +/** + * aim_cachecookie - appends a cookie to the cookie list + * @sess: session to add to + * @cookie: pointer to struct to append + * + * if cookie->cookie for type cookie->type is found, updates the + * ->addtime of the found structure; otherwise adds the given cookie + * to the cache + * + * returns -1 on error, 0 on append, 1 on update. the cookie you pass + * in may be free'd, so don't count on its value after calling this! + * + */ +int aim_cachecookie(aim_session_t *sess, aim_msgcookie_t *cookie) +{ + aim_msgcookie_t *newcook; + + if (!sess || !cookie) + return -EINVAL; + + newcook = aim_checkcookie(sess, cookie->cookie, cookie->type); + + if (newcook == cookie) { + newcook->addtime = time(NULL); + return 1; + } else if (newcook) + aim_cookie_free(sess, newcook); + + cookie->addtime = time(NULL); + + cookie->next = sess->msgcookies; + sess->msgcookies = cookie; + + return 0; +} + +/** + * aim_uncachecookie - grabs a cookie from the cookie cache (removes it from the list) + * @sess: session to grab cookie from + * @cookie: cookie string to look for + * @type: cookie type to look for + * + * takes a cookie string and a cookie type and finds the cookie struct associated with that duple, removing it from the cookie list ikn the process. + * + * if found, returns the struct; if none found (or on error), returns NULL: + */ +aim_msgcookie_t *aim_uncachecookie(aim_session_t *sess, guint8 *cookie, int type) +{ + aim_msgcookie_t *cur, **prev; + + if (!cookie || !sess->msgcookies) + return NULL; + + for (prev = &sess->msgcookies; (cur = *prev); ) { + if ((cur->type == type) && + (memcmp(cur->cookie, cookie, 8) == 0)) { + *prev = cur->next; + return cur; + } + prev = &cur->next; + } + + return NULL; +} + +/** + * aim_mkcookie - generate an aim_msgcookie_t *struct from a cookie string, a type, and a data pointer. + * @c: pointer to the cookie string array + * @type: cookie type to use + * @data: data to be cached with the cookie + * + * returns NULL on error, a pointer to the newly-allocated cookie on + * success. + * + */ +aim_msgcookie_t *aim_mkcookie(guint8 *c, int type, void *data) +{ + aim_msgcookie_t *cookie; + + if (!c) + return NULL; + + if (!(cookie = g_new0(aim_msgcookie_t,1))) + return NULL; + + cookie->data = data; + cookie->type = type; + memcpy(cookie->cookie, c, 8); + + return cookie; +} + +/** + * aim_checkcookie - check to see if a cookietuple has been cached + * @sess: session to check for the cookie in + * @cookie: pointer to the cookie string array + * @type: type of the cookie to look for + * + * this returns a pointer to the cookie struct (still in the list) on + * success; returns NULL on error/not found + * + */ + +aim_msgcookie_t *aim_checkcookie(aim_session_t *sess, const guint8 *cookie, int type) +{ + aim_msgcookie_t *cur; + + for (cur = sess->msgcookies; cur; cur = cur->next) { + if ((cur->type == type) && + (memcmp(cur->cookie, cookie, 8) == 0)) + return cur; + } + + return NULL; +} + +#if 0 /* debugging feature */ +int aim_dumpcookie(aim_msgcookie_t *cookie) +{ + + if (!cookie) + return -EINVAL; + + printf("\tCookie at %p: %d/%s with %p, next %p\n", + cookie, cookie->type, cookie->cookie, + cookie->data, cookie->next); + + return 0; +} +#endif + +/** + * aim_cookie_free - free an aim_msgcookie_t struct + * @sess: session to remove the cookie from + * @cookiep: the address of a pointer to the cookie struct to remove + * + * this function removes the cookie *cookie from teh list of cookies + * in sess, and then frees all memory associated with it. including + * its data! if you want to use the private data after calling this, + * make sure you copy it first. + * + * returns -1 on error, 0 on success. + * + */ +int aim_cookie_free(aim_session_t *sess, aim_msgcookie_t *cookie) +{ + aim_msgcookie_t *cur, **prev; + + if (!sess || !cookie) + return -EINVAL; + + for (prev = &sess->msgcookies; (cur = *prev); ) { + if (cur == cookie) + *prev = cur->next; + else + prev = &cur->next; + } + + g_free(cookie->data); + g_free(cookie); + + return 0; +} + +/* XXX I hate switch */ +int aim_msgcookie_gettype(int reqclass) +{ + /* XXX: hokey-assed. needs fixed. */ + switch(reqclass) { + case AIM_CAPS_BUDDYICON: return AIM_COOKIETYPE_OFTICON; + case AIM_CAPS_VOICE: return AIM_COOKIETYPE_OFTVOICE; + case AIM_CAPS_IMIMAGE: return AIM_COOKIETYPE_OFTIMAGE; + case AIM_CAPS_CHAT: return AIM_COOKIETYPE_CHAT; + case AIM_CAPS_GETFILE: return AIM_COOKIETYPE_OFTGET; + case AIM_CAPS_SENDFILE: return AIM_COOKIETYPE_OFTSEND; + default: return AIM_COOKIETYPE_UNKNOWN; + } +} + + diff --git a/protocols/oscar/oscar.c b/protocols/oscar/oscar.c new file mode 100644 index 00000000..5a1ddc45 --- /dev/null +++ b/protocols/oscar/oscar.c @@ -0,0 +1,2491 @@ +/* + * gaim + * + * Some code copyright (C) 1998-1999, Mark Spencer <markster@marko.net> + * libfaim code copyright 1998, 1999 Adam Fritzler <afritz@auk.cx> + * + * This program is free software; you can 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 "sock.h" +#include <errno.h> +#include <ctype.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <time.h> +#include <sys/stat.h> +#include <glib.h> +#include "nogaim.h" +#include "bitlbee.h" +#include "proxy.h" + +#include "aim.h" +#include "icq.h" +#include "bos.h" +#include "ssi.h" +#include "im.h" +#include "info.h" +#include "buddylist.h" +#include "chat.h" +#include "chatnav.h" + +/* constants to identify proto_opts */ +#define USEROPT_AUTH 0 +#define USEROPT_AUTHPORT 1 + +#define UC_AOL 0x02 +#define UC_ADMIN 0x04 +#define UC_UNCONFIRMED 0x08 +#define UC_NORMAL 0x10 +#define UC_AB 0x20 +#define UC_WIRELESS 0x40 + +#define AIMHASHDATA "http://gaim.sourceforge.net/aim_data.php3" + +#define OSCAR_GROUP "Friends" + +/* Don't know if support for UTF8 is really working. For now it's UTF16 here. + static int gaim_caps = AIM_CAPS_UTF8; */ + +static int gaim_caps = AIM_CAPS_INTEROP | AIM_CAPS_ICHAT | AIM_CAPS_ICQSERVERRELAY; +static guint8 gaim_features[] = {0x01, 0x01, 0x01, 0x02}; + +struct oscar_data { + aim_session_t *sess; + aim_conn_t *conn; + + guint cnpa; + guint paspa; + + GSList *create_rooms; + + gboolean conf; + gboolean reqemail; + gboolean setemail; + char *email; + gboolean setnick; + char *newsn; + gboolean chpass; + char *oldp; + char *newp; + + GSList *oscar_chats; + + gboolean killme; + gboolean icq; + GSList *evilhack; + + struct { + guint maxbuddies; /* max users you can watch */ + guint maxwatchers; /* max users who can watch you */ + guint maxpermits; /* max users on permit list */ + guint maxdenies; /* max users on deny list */ + guint maxsiglen; /* max size (bytes) of profile */ + guint maxawaymsglen; /* max size (bytes) of posted away message */ + } rights; +}; + +struct create_room { + char *name; + int exchange; +}; + +struct chat_connection { + char *name; + char *show; /* AOL did something funny to us */ + guint16 exchange; + guint16 instance; + int fd; /* this is redundant since we have the conn below */ + aim_conn_t *conn; + int inpa; + int id; + struct gaim_connection *gc; /* i hate this. */ + struct conversation *cnv; /* bah. */ + int maxlen; + int maxvis; +}; + +struct ask_direct { + struct gaim_connection *gc; + char *sn; + char ip[64]; + guint8 cookie[8]; +}; + +struct icq_auth { + struct gaim_connection *gc; + guint32 uin; +}; + +static char *extract_name(const char *name) { + char *tmp; + int i, j; + char *x = strchr(name, '-'); + if (!x) return NULL; + x = strchr(++x, '-'); + if (!x) return NULL; + tmp = g_strdup(++x); + + for (i = 0, j = 0; x[i]; i++) { + char hex[3]; + if (x[i] != '%') { + tmp[j++] = x[i]; + continue; + } + strncpy(hex, x + ++i, 2); hex[2] = 0; + i++; + tmp[j++] = (char)strtol(hex, NULL, 16); + } + + tmp[j] = 0; + return tmp; +} + +#if 0 +static struct chat_connection *find_oscar_chat(struct gaim_connection *gc, int id) { + GSList *g = ((struct oscar_data *)gc->proto_data)->oscar_chats; + struct chat_connection *c = NULL; + + while (g) { + c = (struct chat_connection *)g->data; + if (c->id == id) + break; + g = g->next; + c = NULL; + } + + return c; +} +#endif + +static struct chat_connection *find_oscar_chat_by_conn(struct gaim_connection *gc, + aim_conn_t *conn) { + GSList *g = ((struct oscar_data *)gc->proto_data)->oscar_chats; + struct chat_connection *c = NULL; + + while (g) { + c = (struct chat_connection *)g->data; + if (c->conn == conn) + break; + g = g->next; + c = NULL; + } + + return c; +} + +static int gaim_parse_auth_resp (aim_session_t *, aim_frame_t *, ...); +static int gaim_parse_login (aim_session_t *, aim_frame_t *, ...); +static int gaim_handle_redirect (aim_session_t *, aim_frame_t *, ...); +static int gaim_parse_oncoming (aim_session_t *, aim_frame_t *, ...); +static int gaim_parse_offgoing (aim_session_t *, aim_frame_t *, ...); +static int gaim_parse_incoming_im(aim_session_t *, aim_frame_t *, ...); +static int gaim_parse_misses (aim_session_t *, aim_frame_t *, ...); +static int gaim_parse_motd (aim_session_t *, aim_frame_t *, ...); +static int gaim_chatnav_info (aim_session_t *, aim_frame_t *, ...); +static int gaim_chat_join (aim_session_t *, aim_frame_t *, ...); +static int gaim_chat_leave (aim_session_t *, aim_frame_t *, ...); +static int gaim_chat_info_update (aim_session_t *, aim_frame_t *, ...); +static int gaim_chat_incoming_msg(aim_session_t *, aim_frame_t *, ...); +static int gaim_parse_ratechange (aim_session_t *, aim_frame_t *, ...); +static int gaim_bosrights (aim_session_t *, aim_frame_t *, ...); +static int conninitdone_bos (aim_session_t *, aim_frame_t *, ...); +static int conninitdone_admin (aim_session_t *, aim_frame_t *, ...); +static int conninitdone_chat (aim_session_t *, aim_frame_t *, ...); +static int conninitdone_chatnav (aim_session_t *, aim_frame_t *, ...); +static int gaim_parse_msgerr (aim_session_t *, aim_frame_t *, ...); +static int gaim_parse_locaterights(aim_session_t *, aim_frame_t *, ...); +static int gaim_parse_buddyrights(aim_session_t *, aim_frame_t *, ...); +static int gaim_parse_locerr (aim_session_t *, aim_frame_t *, ...); +static int gaim_icbm_param_info (aim_session_t *, aim_frame_t *, ...); +static int gaim_parse_genericerr (aim_session_t *, aim_frame_t *, ...); +static int gaim_memrequest (aim_session_t *, aim_frame_t *, ...); +static int gaim_selfinfo (aim_session_t *, aim_frame_t *, ...); +static int gaim_offlinemsg (aim_session_t *, aim_frame_t *, ...); +static int gaim_offlinemsgdone (aim_session_t *, aim_frame_t *, ...); +static int gaim_ssi_parserights (aim_session_t *, aim_frame_t *, ...); +static int gaim_ssi_parselist (aim_session_t *, aim_frame_t *, ...); +static int gaim_ssi_parseack (aim_session_t *, aim_frame_t *, ...); + +static int gaim_icqinfo (aim_session_t *, aim_frame_t *, ...); +static int gaim_parseaiminfo (aim_session_t *, aim_frame_t *, ...); + +static char *msgerrreason[] = { + "Invalid error", + "Invalid SNAC", + "Rate to host", + "Rate to client", + "Not logged in", + "Service unavailable", + "Service not defined", + "Obsolete SNAC", + "Not supported by host", + "Not supported by client", + "Refused by client", + "Reply too big", + "Responses lost", + "Request denied", + "Busted SNAC payload", + "Insufficient rights", + "In local permit/deny", + "Too evil (sender)", + "Too evil (receiver)", + "User temporarily unavailable", + "No match", + "List overflow", + "Request ambiguous", + "Queue full", + "Not while on AOL" +}; +static int msgerrreasonlen = 25; + +static void oscar_callback(gpointer data, gint source, + GaimInputCondition condition) { + aim_conn_t *conn = (aim_conn_t *)data; + aim_session_t *sess = aim_conn_getsess(conn); + struct gaim_connection *gc = sess ? sess->aux_data : NULL; + struct oscar_data *odata; + + if (!gc) { + /* gc is null. we return, else we seg SIGSEG on next line. */ + return; + } + + if (!g_slist_find(get_connections(), gc)) { + /* oh boy. this is probably bad. i guess the only thing we + * can really do is return? */ + return; + } + + odata = (struct oscar_data *)gc->proto_data; + + if (condition & GAIM_INPUT_READ) { + if (conn->type == AIM_CONN_TYPE_RENDEZVOUS_OUT) { + if (aim_handlerendconnect(odata->sess, conn) < 0) { + aim_conn_kill(odata->sess, &conn); + } + } else { + if (aim_get_command(odata->sess, conn) >= 0) { + aim_rxdispatch(odata->sess); + if (odata->killme) + signoff(gc); + } else { + if ((conn->type == AIM_CONN_TYPE_BOS) || + !(aim_getconn_type(odata->sess, AIM_CONN_TYPE_BOS))) { + hide_login_progress_error(gc, _("Disconnected.")); + signoff(gc); + } else if (conn->type == AIM_CONN_TYPE_CHAT) { + struct chat_connection *c = find_oscar_chat_by_conn(gc, conn); + char buf[BUF_LONG]; + c->conn = NULL; + if (c->inpa > 0) + gaim_input_remove(c->inpa); + c->inpa = 0; + c->fd = -1; + aim_conn_kill(odata->sess, &conn); + sprintf(buf, _("You have been disconnected from chat room %s."), c->name); + do_error_dialog(sess->aux_data, buf, _("Chat Error!")); + } else if (conn->type == AIM_CONN_TYPE_CHATNAV) { + if (odata->cnpa > 0) + gaim_input_remove(odata->cnpa); + odata->cnpa = 0; + while (odata->create_rooms) { + struct create_room *cr = odata->create_rooms->data; + g_free(cr->name); + odata->create_rooms = + g_slist_remove(odata->create_rooms, cr); + g_free(cr); + do_error_dialog(sess->aux_data, _("Chat is currently unavailable"), + _("Gaim - Chat")); + } + aim_conn_kill(odata->sess, &conn); + } else if (conn->type == AIM_CONN_TYPE_AUTH) { + if (odata->paspa > 0) + gaim_input_remove(odata->paspa); + odata->paspa = 0; + aim_conn_kill(odata->sess, &conn); + } else { + aim_conn_kill(odata->sess, &conn); + } + } + } + } +} + +static void oscar_login_connect(gpointer data, gint source, GaimInputCondition cond) +{ + struct gaim_connection *gc = data; + struct oscar_data *odata; + aim_session_t *sess; + aim_conn_t *conn; + + if (!g_slist_find(get_connections(), gc)) { + closesocket(source); + return; + } + + odata = gc->proto_data; + sess = odata->sess; + conn = aim_getconn_type_all(sess, AIM_CONN_TYPE_AUTH); + + if (source < 0) { + hide_login_progress(gc, _("Couldn't connect to host")); + signoff(gc); + return; + } + + aim_conn_completeconnect(sess, conn); + gc->inpa = gaim_input_add(conn->fd, GAIM_INPUT_READ, + oscar_callback, conn); +} + +static void oscar_login(struct aim_user *user) { + aim_session_t *sess; + aim_conn_t *conn; + char buf[256]; + struct gaim_connection *gc = new_gaim_conn(user); + struct oscar_data *odata = gc->proto_data = g_new0(struct oscar_data, 1); + + if (isdigit(*user->username)) { + odata->icq = TRUE; + /* this is odd but it's necessary for a proper do_import and do_export */ + gc->protocol = PROTO_ICQ; + gc->password[8] = 0; + } else { + gc->protocol = PROTO_TOC; + gc->flags |= OPT_CONN_HTML; + } + + sess = g_new0(aim_session_t, 1); + + aim_session_init(sess, AIM_SESS_FLAGS_NONBLOCKCONNECT, 0); + + /* we need an immediate queue because we don't use a while-loop to + * see if things need to be sent. */ + aim_tx_setenqueue(sess, AIM_TX_IMMEDIATE, NULL); + odata->sess = sess; + sess->aux_data = gc; + + conn = aim_newconn(sess, AIM_CONN_TYPE_AUTH, NULL); + if (conn == NULL) { + hide_login_progress(gc, _("Unable to login to AIM")); + signoff(gc); + return; + } + + if (g_strcasecmp(user->proto_opt[USEROPT_AUTH], "login.icq.com") != 0 && + g_strcasecmp(user->proto_opt[USEROPT_AUTH], "login.oscar.aol.com") != 0) { + serv_got_crap(gc, "Warning: Unknown OSCAR server: `%s'. Please review your configuration if the connection fails."); + } + + g_snprintf(buf, sizeof(buf), _("Signon: %s"), gc->username); + set_login_progress(gc, 2, buf); + + aim_conn_addhandler(sess, conn, 0x0017, 0x0007, gaim_parse_login, 0); + aim_conn_addhandler(sess, conn, 0x0017, 0x0003, gaim_parse_auth_resp, 0); + + conn->status |= AIM_CONN_STATUS_INPROGRESS; + conn->fd = proxy_connect(user->proto_opt[USEROPT_AUTH][0] ? + user->proto_opt[USEROPT_AUTH] : AIM_DEFAULT_LOGIN_SERVER, + user->proto_opt[USEROPT_AUTHPORT][0] ? + atoi(user->proto_opt[USEROPT_AUTHPORT]) : AIM_LOGIN_PORT, + oscar_login_connect, gc); + if (conn->fd < 0) { + hide_login_progress(gc, _("Couldn't connect to host")); + signoff(gc); + return; + } + aim_request_login(sess, conn, gc->username); +} + +static void oscar_close(struct gaim_connection *gc) { + struct oscar_data *odata = (struct oscar_data *)gc->proto_data; + + while (odata->oscar_chats) { + struct chat_connection *n = odata->oscar_chats->data; + if (n->inpa > 0) + gaim_input_remove(n->inpa); + g_free(n->name); + g_free(n->show); + odata->oscar_chats = g_slist_remove(odata->oscar_chats, n); + g_free(n); + } + while (odata->create_rooms) { + struct create_room *cr = odata->create_rooms->data; + g_free(cr->name); + odata->create_rooms = g_slist_remove(odata->create_rooms, cr); + g_free(cr); + } + if (odata->email) + g_free(odata->email); + if (odata->newp) + g_free(odata->newp); + if (odata->oldp) + g_free(odata->oldp); + if (gc->inpa > 0) + gaim_input_remove(gc->inpa); + if (odata->cnpa > 0) + gaim_input_remove(odata->cnpa); + if (odata->paspa > 0) + gaim_input_remove(odata->paspa); + aim_session_kill(odata->sess); + g_free(odata->sess); + odata->sess = NULL; + g_free(gc->proto_data); + gc->proto_data = NULL; +} + +static void oscar_bos_connect(gpointer data, gint source, GaimInputCondition cond) { + struct gaim_connection *gc = data; + struct oscar_data *odata; + aim_session_t *sess; + aim_conn_t *bosconn; + + if (!g_slist_find(get_connections(), gc)) { + closesocket(source); + return; + } + + odata = gc->proto_data; + sess = odata->sess; + bosconn = odata->conn; + + if (source < 0) { + hide_login_progress(gc, _("Could Not Connect")); + signoff(gc); + return; + } + + aim_conn_completeconnect(sess, bosconn); + gc->inpa = gaim_input_add(bosconn->fd, GAIM_INPUT_READ, + oscar_callback, bosconn); + set_login_progress(gc, 4, _("Connection established, cookie sent")); +} + +static int gaim_parse_auth_resp(aim_session_t *sess, aim_frame_t *fr, ...) { + va_list ap; + struct aim_authresp_info *info; + int i; char *host; int port; + struct aim_user *user; + aim_conn_t *bosconn; + + struct gaim_connection *gc = sess->aux_data; + struct oscar_data *od = gc->proto_data; + user = gc->user; + port = user->proto_opt[USEROPT_AUTHPORT][0] ? + atoi(user->proto_opt[USEROPT_AUTHPORT]) : AIM_LOGIN_PORT, + + va_start(ap, fr); + info = va_arg(ap, struct aim_authresp_info *); + va_end(ap); + + if (info->errorcode || !info->bosip || !info->cookie) { + switch (info->errorcode) { + case 0x05: + /* Incorrect nick/password */ + hide_login_progress(gc, _("Incorrect nickname or password.")); +// plugin_event(event_error, (void *)980, 0, 0, 0); + break; + case 0x11: + /* Suspended account */ + hide_login_progress(gc, _("Your account is currently suspended.")); + break; + case 0x18: + /* connecting too frequently */ + hide_login_progress(gc, _("You have been connecting and disconnecting too frequently. Wait ten minutes and try again. If you continue to try, you will need to wait even longer.")); + break; + case 0x1c: + /* client too old */ + hide_login_progress(gc, _("The client version you are using is too old. Please upgrade at " WEBSITE)); + break; + default: + hide_login_progress(gc, _("Authentication Failed")); + break; + } + od->killme = TRUE; + return 1; + } + + + aim_conn_kill(sess, &fr->conn); + + bosconn = aim_newconn(sess, AIM_CONN_TYPE_BOS, NULL); + if (bosconn == NULL) { + hide_login_progress(gc, _("Internal Error")); + od->killme = TRUE; + return 0; + } + + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_bos, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BOS, AIM_CB_BOS_RIGHTS, gaim_bosrights, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ACK, AIM_CB_ACK_ACK, NULL, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_REDIRECT, gaim_handle_redirect, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_RIGHTSINFO, gaim_parse_locaterights, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_RIGHTSINFO, gaim_parse_buddyrights, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_ONCOMING, gaim_parse_oncoming, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_OFFGOING, gaim_parse_offgoing, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_INCOMING, gaim_parse_incoming_im, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_ERROR, gaim_parse_locerr, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_MISSEDCALL, gaim_parse_misses, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_RATECHANGE, gaim_parse_ratechange, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_ERROR, gaim_parse_msgerr, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_MOTD, gaim_parse_motd, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_MSG, AIM_CB_MSG_PARAMINFO, gaim_icbm_param_info, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_ERROR, gaim_parse_genericerr, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_ERROR, gaim_parse_genericerr, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BOS, AIM_CB_BOS_ERROR, gaim_parse_genericerr, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, 0x1f, gaim_memrequest, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_SELFINFO, gaim_selfinfo, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSG, gaim_offlinemsg, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSGCOMPLETE, gaim_offlinemsgdone, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_INFO, gaim_icqinfo, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_RIGHTSINFO, gaim_ssi_parserights, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_LIST, gaim_ssi_parselist, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_SSI, AIM_CB_SSI_SRVACK, gaim_ssi_parseack, 0); + aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_LOC, AIM_CB_LOC_USERINFO, gaim_parseaiminfo, 0); + + ((struct oscar_data *)gc->proto_data)->conn = bosconn; + for (i = 0; i < (int)strlen(info->bosip); i++) { + if (info->bosip[i] == ':') { + port = atoi(&(info->bosip[i+1])); + break; + } + } + host = g_strndup(info->bosip, i); + bosconn->status |= AIM_CONN_STATUS_INPROGRESS; + bosconn->fd = proxy_connect(host, port, oscar_bos_connect, gc); + g_free(host); + if (bosconn->fd < 0) { + hide_login_progress(gc, _("Could Not Connect")); + od->killme = TRUE; + return 0; + } + aim_sendcookie(sess, bosconn, info->cookie); + gaim_input_remove(gc->inpa); + + return 1; +} + +struct pieceofcrap { + struct gaim_connection *gc; + unsigned long offset; + unsigned long len; + char *modname; + int fd; + aim_conn_t *conn; + unsigned int inpa; +}; + +static void damn_you(gpointer data, gint source, GaimInputCondition c) +{ + struct pieceofcrap *pos = data; + struct oscar_data *od = pos->gc->proto_data; + char in = '\0'; + int x = 0; + unsigned char m[17]; + + while (read(pos->fd, &in, 1) == 1) { + if (in == '\n') + x++; + else if (in != '\r') + x = 0; + if (x == 2) + break; + in = '\0'; + } + if (in != '\n') { + do_error_dialog(pos->gc, "Gaim was unable to get a valid hash for logging into AIM." + " You may be disconnected shortly.", "Login Error"); + gaim_input_remove(pos->inpa); + closesocket(pos->fd); + g_free(pos); + return; + } + read(pos->fd, m, 16); + m[16] = '\0'; + gaim_input_remove(pos->inpa); + closesocket(pos->fd); + aim_sendmemblock(od->sess, pos->conn, 0, 16, m, AIM_SENDMEMBLOCK_FLAG_ISHASH); + g_free(pos); +} + +static void straight_to_hell(gpointer data, gint source, GaimInputCondition cond) { + struct pieceofcrap *pos = data; + char buf[BUF_LONG]; + + if (source < 0) { + do_error_dialog(pos->gc, "Gaim was unable to get a valid hash for logging into AIM." + " You may be disconnected shortly.", "Login Error"); + if (pos->modname) + g_free(pos->modname); + g_free(pos); + return; + } + + g_snprintf(buf, sizeof(buf), "GET " AIMHASHDATA + "?offset=%ld&len=%ld&modname=%s HTTP/1.0\n\n", + pos->offset, pos->len, pos->modname ? pos->modname : ""); + write(pos->fd, buf, strlen(buf)); + if (pos->modname) + g_free(pos->modname); + pos->inpa = gaim_input_add(pos->fd, GAIM_INPUT_READ, damn_you, pos); + return; +} + +/* size of icbmui.ocm, the largest module in AIM 3.5 */ +#define AIM_MAX_FILE_SIZE 98304 + +int gaim_memrequest(aim_session_t *sess, aim_frame_t *fr, ...) { + va_list ap; + struct pieceofcrap *pos; + guint32 offset, len; + char *modname; + int fd; + + va_start(ap, fr); + offset = (guint32)va_arg(ap, unsigned long); + len = (guint32)va_arg(ap, unsigned long); + modname = va_arg(ap, char *); + va_end(ap); + + if (len == 0) { + aim_sendmemblock(sess, fr->conn, offset, len, NULL, + AIM_SENDMEMBLOCK_FLAG_ISREQUEST); + return 1; + } + /* uncomment this when you're convinced it's right. remember, it's been wrong before. + if (offset > AIM_MAX_FILE_SIZE || len > AIM_MAX_FILE_SIZE) { + char *buf; + int i = 8; + if (modname) + i += strlen(modname); + buf = g_malloc(i); + i = 0; + if (modname) { + memcpy(buf, modname, strlen(modname)); + i += strlen(modname); + } + buf[i++] = offset & 0xff; + buf[i++] = (offset >> 8) & 0xff; + buf[i++] = (offset >> 16) & 0xff; + buf[i++] = (offset >> 24) & 0xff; + buf[i++] = len & 0xff; + buf[i++] = (len >> 8) & 0xff; + buf[i++] = (len >> 16) & 0xff; + buf[i++] = (len >> 24) & 0xff; + aim_sendmemblock(sess, command->conn, offset, i, buf, AIM_SENDMEMBLOCK_FLAG_ISREQUEST); + g_free(buf); + return 1; + } + */ + + pos = g_new0(struct pieceofcrap, 1); + pos->gc = sess->aux_data; + pos->conn = fr->conn; + + pos->offset = offset; + pos->len = len; + pos->modname = modname ? g_strdup(modname) : NULL; + + fd = proxy_connect("gaim.sourceforge.net", 80, straight_to_hell, pos); + if (fd < 0) { + if (pos->modname) + g_free(pos->modname); + g_free(pos); + do_error_dialog(sess->aux_data, "Gaim was unable to get a valid hash for logging into AIM." + " You may be disconnected shortly.", "Login Error"); + } + pos->fd = fd; + + return 1; +} + +static int gaim_parse_login(aim_session_t *sess, aim_frame_t *fr, ...) { +#if 0 + struct client_info_s info = {"gaim", 4, 1, 2010, "us", "en", 0x0004, 0x0000, 0x04b}; +#else + struct client_info_s info = AIM_CLIENTINFO_KNOWNGOOD; +#endif + char *key; + va_list ap; + struct gaim_connection *gc = sess->aux_data; + + va_start(ap, fr); + key = va_arg(ap, char *); + va_end(ap); + + aim_send_login(sess, fr->conn, gc->username, gc->password, &info, key); + + return 1; +} + +static int conninitdone_chat(aim_session_t *sess, aim_frame_t *fr, ...) { + struct gaim_connection *gc = sess->aux_data; + struct chat_connection *chatcon; + static int id = 1; + + aim_conn_addhandler(sess, fr->conn, 0x000e, 0x0001, gaim_parse_genericerr, 0); + aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERJOIN, gaim_chat_join, 0); + aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_USERLEAVE, gaim_chat_leave, 0); + aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_ROOMINFOUPDATE, gaim_chat_info_update, 0); + aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CHT, AIM_CB_CHT_INCOMINGMSG, gaim_chat_incoming_msg, 0); + + aim_clientready(sess, fr->conn); + + chatcon = find_oscar_chat_by_conn(gc, fr->conn); + chatcon->id = id; + chatcon->cnv = serv_got_joined_chat(gc, id++, chatcon->show); + + return 1; +} + +static int conninitdone_chatnav(aim_session_t *sess, aim_frame_t *fr, ...) { + + aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CTN, AIM_CB_CTN_ERROR, gaim_parse_genericerr, 0); + aim_conn_addhandler(sess, fr->conn, AIM_CB_FAM_CTN, AIM_CB_CTN_INFO, gaim_chatnav_info, 0); + + aim_clientready(sess, fr->conn); + + aim_chatnav_reqrights(sess, fr->conn); + + return 1; +} + +static void oscar_chatnav_connect(gpointer data, gint source, GaimInputCondition cond) { + struct gaim_connection *gc = data; + struct oscar_data *odata; + aim_session_t *sess; + aim_conn_t *tstconn; + + if (!g_slist_find(get_connections(), gc)) { + closesocket(source); + return; + } + + odata = gc->proto_data; + sess = odata->sess; + tstconn = aim_getconn_type_all(sess, AIM_CONN_TYPE_CHATNAV); + + if (source < 0) { + aim_conn_kill(sess, &tstconn); + return; + } + + aim_conn_completeconnect(sess, tstconn); + odata->cnpa = gaim_input_add(tstconn->fd, GAIM_INPUT_READ, + oscar_callback, tstconn); +} + +static void oscar_auth_connect(gpointer data, gint source, GaimInputCondition cond) +{ + struct gaim_connection *gc = data; + struct oscar_data *odata; + aim_session_t *sess; + aim_conn_t *tstconn; + + if (!g_slist_find(get_connections(), gc)) { + closesocket(source); + return; + } + + odata = gc->proto_data; + sess = odata->sess; + tstconn = aim_getconn_type_all(sess, AIM_CONN_TYPE_AUTH); + + if (source < 0) { + aim_conn_kill(sess, &tstconn); + return; + } + + aim_conn_completeconnect(sess, tstconn); + odata->paspa = gaim_input_add(tstconn->fd, GAIM_INPUT_READ, + oscar_callback, tstconn); +} + +static void oscar_chat_connect(gpointer data, gint source, GaimInputCondition cond) +{ + struct chat_connection *ccon = data; + struct gaim_connection *gc = ccon->gc; + struct oscar_data *odata; + aim_session_t *sess; + aim_conn_t *tstconn; + + if (!g_slist_find(get_connections(), gc)) { + closesocket(source); + g_free(ccon->show); + g_free(ccon->name); + g_free(ccon); + return; + } + + odata = gc->proto_data; + sess = odata->sess; + tstconn = ccon->conn; + + if (source < 0) { + aim_conn_kill(sess, &tstconn); + g_free(ccon->show); + g_free(ccon->name); + g_free(ccon); + return; + } + + aim_conn_completeconnect(sess, ccon->conn); + ccon->inpa = gaim_input_add(tstconn->fd, + GAIM_INPUT_READ, + oscar_callback, tstconn); + odata->oscar_chats = g_slist_append(odata->oscar_chats, ccon); +} + +/* Hrmph. I don't know how to make this look better. --mid */ +static int gaim_handle_redirect(aim_session_t *sess, aim_frame_t *fr, ...) { + va_list ap; + struct aim_redirect_data *redir; + struct gaim_connection *gc = sess->aux_data; + struct aim_user *user = gc->user; + aim_conn_t *tstconn; + int i; + char *host; + int port; + + port = user->proto_opt[USEROPT_AUTHPORT][0] ? + atoi(user->proto_opt[USEROPT_AUTHPORT]) : AIM_LOGIN_PORT, + + va_start(ap, fr); + redir = va_arg(ap, struct aim_redirect_data *); + va_end(ap); + + for (i = 0; i < (int)strlen(redir->ip); i++) { + if (redir->ip[i] == ':') { + port = atoi(&(redir->ip[i+1])); + break; + } + } + host = g_strndup(redir->ip, i); + + switch(redir->group) { + case 0x7: /* Authorizer */ + tstconn = aim_newconn(sess, AIM_CONN_TYPE_AUTH, NULL); + if (tstconn == NULL) { + g_free(host); + return 1; + } + aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_admin, 0); +// aim_conn_addhandler(sess, tstconn, 0x0007, 0x0003, gaim_info_change, 0); +// aim_conn_addhandler(sess, tstconn, 0x0007, 0x0005, gaim_info_change, 0); +// aim_conn_addhandler(sess, tstconn, 0x0007, 0x0007, gaim_account_confirm, 0); + + tstconn->status |= AIM_CONN_STATUS_INPROGRESS; + tstconn->fd = proxy_connect(host, port, oscar_auth_connect, gc); + if (tstconn->fd < 0) { + aim_conn_kill(sess, &tstconn); + g_free(host); + return 1; + } + aim_sendcookie(sess, tstconn, redir->cookie); + break; + case 0xd: /* ChatNav */ + tstconn = aim_newconn(sess, AIM_CONN_TYPE_CHATNAV, NULL); + if (tstconn == NULL) { + g_free(host); + return 1; + } + aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_chatnav, 0); + + tstconn->status |= AIM_CONN_STATUS_INPROGRESS; + tstconn->fd = proxy_connect(host, port, oscar_chatnav_connect, gc); + if (tstconn->fd < 0) { + aim_conn_kill(sess, &tstconn); + g_free(host); + return 1; + } + aim_sendcookie(sess, tstconn, redir->cookie); + break; + case 0xe: /* Chat */ + { + struct chat_connection *ccon; + + tstconn = aim_newconn(sess, AIM_CONN_TYPE_CHAT, NULL); + if (tstconn == NULL) { + g_free(host); + return 1; + } + + aim_conn_addhandler(sess, tstconn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE, conninitdone_chat, 0); + + ccon = g_new0(struct chat_connection, 1); + ccon->conn = tstconn; + ccon->gc = gc; + ccon->fd = -1; + ccon->name = g_strdup(redir->chat.room); + ccon->exchange = redir->chat.exchange; + ccon->instance = redir->chat.instance; + ccon->show = extract_name(redir->chat.room); + + ccon->conn->status |= AIM_CONN_STATUS_INPROGRESS; + ccon->conn->fd = proxy_connect(host, port, oscar_chat_connect, ccon); + if (ccon->conn->fd < 0) { + aim_conn_kill(sess, &tstconn); + g_free(host); + g_free(ccon->show); + g_free(ccon->name); + g_free(ccon); + return 1; + } + aim_sendcookie(sess, tstconn, redir->cookie); + } + break; + default: /* huh? */ + break; + } + + g_free(host); + return 1; +} + +static int gaim_parse_oncoming(aim_session_t *sess, aim_frame_t *fr, ...) { + struct gaim_connection *gc = sess->aux_data; + struct oscar_data *od = gc->proto_data; + aim_userinfo_t *info; + time_t time_idle = 0, signon = 0; + int type = 0; + int caps = 0; + char *tmp; + + va_list ap; + va_start(ap, fr); + info = va_arg(ap, aim_userinfo_t *); + va_end(ap); + + if (info->present & AIM_USERINFO_PRESENT_CAPABILITIES) + caps = info->capabilities; + if (info->flags & AIM_FLAG_ACTIVEBUDDY) + type |= UC_AB; + + if ((!od->icq) && (info->present & AIM_USERINFO_PRESENT_FLAGS)) { + if (info->flags & AIM_FLAG_UNCONFIRMED) + type |= UC_UNCONFIRMED; + if (info->flags & AIM_FLAG_ADMINISTRATOR) + type |= UC_ADMIN; + if (info->flags & AIM_FLAG_AOL) + type |= UC_AOL; + if (info->flags & AIM_FLAG_FREE) + type |= UC_NORMAL; + if (info->flags & AIM_FLAG_AWAY) + type |= UC_UNAVAILABLE; + if (info->flags & AIM_FLAG_WIRELESS) + type |= UC_WIRELESS; + } + if (info->present & AIM_USERINFO_PRESENT_ICQEXTSTATUS) { + type = (info->icqinfo.status << 7); + if (!(info->icqinfo.status & AIM_ICQ_STATE_CHAT) && + (info->icqinfo.status != AIM_ICQ_STATE_NORMAL)) { + type |= UC_UNAVAILABLE; + } + } + + if (caps & AIM_CAPS_ICQ) + caps ^= AIM_CAPS_ICQ; + + if (info->present & AIM_USERINFO_PRESENT_IDLE) { + time(&time_idle); + time_idle -= info->idletime*60; + } + + if (info->present & AIM_USERINFO_PRESENT_SESSIONLEN) + signon = time(NULL) - info->sessionlen; + + tmp = g_strdup(normalize(gc->username)); + if (!strcmp(tmp, normalize(info->sn))) + g_snprintf(gc->displayname, sizeof(gc->displayname), "%s", info->sn); + g_free(tmp); + + serv_got_update(gc, info->sn, 1, info->warnlevel/10, signon, + time_idle, type, caps); + + return 1; +} + +static int gaim_parse_offgoing(aim_session_t *sess, aim_frame_t *fr, ...) { + aim_userinfo_t *info; + va_list ap; + struct gaim_connection *gc = sess->aux_data; + + va_start(ap, fr); + info = va_arg(ap, aim_userinfo_t *); + va_end(ap); + + serv_got_update(gc, info->sn, 0, 0, 0, 0, 0, 0); + + return 1; +} + +static int incomingim_chan1(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch1_args *args) { + char *tmp = g_malloc(BUF_LONG + 1); + struct gaim_connection *gc = sess->aux_data; + int flags = 0; + + if (args->icbmflags & AIM_IMFLAGS_AWAY) + flags |= IM_FLAG_AWAY; + + if ((args->icbmflags & AIM_IMFLAGS_UNICODE) || (args->icbmflags & AIM_IMFLAGS_ISO_8859_1)) { + char *src; + + if (args->icbmflags & AIM_IMFLAGS_UNICODE) + src = "UNICODEBIG"; + else + src = "ISO8859-1"; + + /* Try to use iconv first to convert the message to UTF8 - which is what BitlBee expects */ + if (do_iconv(src, "UTF-8", args->msg, tmp, args->msglen, BUF_LONG) >= 0) { + // Successfully converted! + } else if (args->icbmflags & AIM_IMFLAGS_UNICODE) { + int i; + + for (i = 0, tmp[0] = '\0'; i < args->msglen; i += 2) { + unsigned short uni; + + uni = ((args->msg[i] & 0xff) << 8) | (args->msg[i+1] & 0xff); + + if ((uni < 128) || ((uni >= 160) && (uni <= 255))) { /* ISO 8859-1 */ + g_snprintf(tmp+strlen(tmp), BUF_LONG-strlen(tmp), "%c", uni); + } else { /* something else, do UNICODE entity */ + g_snprintf(tmp+strlen(tmp), BUF_LONG-strlen(tmp), "&#%04x;", uni); + } + } + } else { + g_snprintf(tmp, BUF_LONG, "%s", args->msg); + } + } else + g_snprintf(tmp, BUF_LONG, "%s", args->msg); + + strip_linefeed(tmp); + serv_got_im(gc, userinfo->sn, tmp, flags, time(NULL), -1); + g_free(tmp); + + return 1; +} + +static int incomingim_chan2(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args) { +#if 0 + struct gaim_connection *gc = sess->aux_data; +#endif + + if (args->status != AIM_RENDEZVOUS_PROPOSE) + return 1; +#if 0 + if (args->reqclass & AIM_CAPS_CHAT) { + char *name = extract_name(args->info.chat.roominfo.name); + int *exch = g_new0(int, 1); + GList *m = NULL; + m = g_list_append(m, g_strdup(name ? name : args->info.chat.roominfo.name)); + *exch = args->info.chat.roominfo.exchange; + m = g_list_append(m, exch); + serv_got_chat_invite(gc, + name ? name : args->info.chat.roominfo.name, + userinfo->sn, + (char *)args->msg, + m); + if (name) + g_free(name); + } +#endif + return 1; +} + +static void gaim_icq_authgrant(gpointer w, struct icq_auth *data) { + char *uin, message; + struct oscar_data *od = (struct oscar_data *)data->gc->proto_data; + + uin = g_strdup_printf("%u", data->uin); + message = 0; + aim_ssi_auth_reply(od->sess, od->conn, uin, 1, ""); + // aim_send_im_ch4(od->sess, uin, AIM_ICQMSG_AUTHGRANTED, &message); + show_got_added(data->gc, NULL, uin, NULL, NULL); + + g_free(uin); + g_free(data); +} + +static void gaim_icq_authdeny(gpointer w, struct icq_auth *data) { + char *uin, *message; + struct oscar_data *od = (struct oscar_data *)data->gc->proto_data; + + uin = g_strdup_printf("%u", data->uin); + message = g_strdup_printf("No reason given."); + aim_ssi_auth_reply(od->sess, od->conn, uin, 0, ""); + // aim_send_im_ch4(od->sess, uin, AIM_ICQMSG_AUTHDENIED, message); + g_free(message); + + g_free(uin); + g_free(data); +} + +/* + * For when other people ask you for authorization + */ +static void gaim_icq_authask(struct gaim_connection *gc, guint32 uin, char *msg) { + struct icq_auth *data = g_new(struct icq_auth, 1); + char *reason = NULL; + char *dialog_msg; + + if (strlen(msg) > 6) + reason = msg + 6; + + dialog_msg = g_strdup_printf("The user %u wants to add you to their buddy list for the following reason:\n\n%s", uin, reason ? reason : "No reason given."); + data->gc = gc; + data->uin = uin; + do_ask_dialog(gc, dialog_msg, data, gaim_icq_authgrant, gaim_icq_authdeny); + g_free(dialog_msg); +} + +static int incomingim_chan4(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch4_args *args) { + struct gaim_connection *gc = sess->aux_data; + + switch (args->type) { + case 0x0001: { /* An almost-normal instant message. Mac ICQ sends this. It's peculiar. */ + char *uin, *message; + uin = g_strdup_printf("%u", args->uin); + message = g_strdup(args->msg); + strip_linefeed(message); + serv_got_im(gc, uin, message, 0, time(NULL), -1); + g_free(uin); + g_free(message); + } break; + + case 0x0004: { /* Someone sent you a URL */ + char *uin, *message; + char **m; + + uin = g_strdup_printf("%u", args->uin); + m = g_strsplit(args->msg, "\376", 2); + + if ((strlen(m[0]) != 0)) { + message = g_strjoinv(" -- ", m); + } else { + message = m[1]; + } + + strip_linefeed(message); + serv_got_im(gc, uin, message, 0, time(NULL), -1); + g_free(uin); + g_free(m); + g_free(message); + } break; + + case 0x0006: { /* Someone requested authorization */ + gaim_icq_authask(gc, args->uin, args->msg); + } break; + + case 0x0007: { /* Someone has denied you authorization */ + serv_got_crap(sess->aux_data, "The user %u has denied your request to add them to your contact list for the following reason:\n%s", args->uin, args->msg ? args->msg : _("No reason given.") ); + } break; + + case 0x0008: { /* Someone has granted you authorization */ + serv_got_crap(sess->aux_data, "The user %u has granted your request to add them to your contact list for the following reason:\n%s", args->uin, args->msg ? args->msg : _("No reason given.") ); + } break; + + case 0x0012: { + /* Ack for authorizing/denying someone. Or possibly an ack for sending any system notice */ + } break; + + default: {; + } break; + } + + return 1; +} +/* +int handle_cmp_aim(const char * a, const char * b) { + return handle_cmp(a, b, PROTO_TOC); +} +*/ +static int gaim_parse_incoming_im(aim_session_t *sess, aim_frame_t *fr, ...) { + int channel, ret = 0; + aim_userinfo_t *userinfo; + va_list ap; + + va_start(ap, fr); + channel = va_arg(ap, int); + userinfo = va_arg(ap, aim_userinfo_t *); + + if (set_getint(sess->aux_data, "debug")) { + serv_got_crap(sess->aux_data, "channel %i called", channel); + } + + switch (channel) { + case 1: { /* standard message */ + struct aim_incomingim_ch1_args *args; + args = va_arg(ap, struct aim_incomingim_ch1_args *); + ret = incomingim_chan1(sess, fr->conn, userinfo, args); + } break; + + case 2: { /* rendevous */ + struct aim_incomingim_ch2_args *args; + args = va_arg(ap, struct aim_incomingim_ch2_args *); + ret = incomingim_chan2(sess, fr->conn, userinfo, args); + } break; + + case 4: { /* ICQ */ + struct aim_incomingim_ch4_args *args; + args = va_arg(ap, struct aim_incomingim_ch4_args *); + ret = incomingim_chan4(sess, fr->conn, userinfo, args); + } break; + + default: {; + } break; + } + + va_end(ap); + + return ret; +} + +static int gaim_parse_misses(aim_session_t *sess, aim_frame_t *fr, ...) { + va_list ap; + guint16 chan, nummissed, reason; + aim_userinfo_t *userinfo; + char buf[1024]; + + va_start(ap, fr); + chan = (guint16)va_arg(ap, unsigned int); + userinfo = va_arg(ap, aim_userinfo_t *); + nummissed = (guint16)va_arg(ap, unsigned int); + reason = (guint16)va_arg(ap, unsigned int); + va_end(ap); + + switch(reason) { + case 0: + /* Invalid (0) */ + g_snprintf(buf, + sizeof(buf), + nummissed == 1 ? + _("You missed %d message from %s because it was invalid.") : + _("You missed %d messages from %s because they were invalid."), + nummissed, + userinfo->sn); + break; + case 1: + /* Message too large */ + g_snprintf(buf, + sizeof(buf), + nummissed == 1 ? + _("You missed %d message from %s because it was too large.") : + _("You missed %d messages from %s because they were too large."), + nummissed, + userinfo->sn); + break; + case 2: + /* Rate exceeded */ + g_snprintf(buf, + sizeof(buf), + nummissed == 1 ? + _("You missed %d message from %s because the rate limit has been exceeded.") : + _("You missed %d messages from %s because the rate limit has been exceeded."), + nummissed, + userinfo->sn); + break; + case 3: + /* Evil Sender */ + g_snprintf(buf, + sizeof(buf), + nummissed == 1 ? + _("You missed %d message from %s because it was too evil.") : + _("You missed %d messages from %s because they are too evil."), + nummissed, + userinfo->sn); + break; + case 4: + /* Evil Receiver */ + g_snprintf(buf, + sizeof(buf), + nummissed == 1 ? + _("You missed %d message from %s because you are too evil.") : + _("You missed %d messages from %s because you are too evil."), + nummissed, + userinfo->sn); + break; + default: + g_snprintf(buf, + sizeof(buf), + nummissed == 1 ? + _("You missed %d message from %s for unknown reasons.") : + _("You missed %d messages from %s for unknown reasons."), + nummissed, + userinfo->sn); + break; + } + do_error_dialog(sess->aux_data, buf, _("Gaim - Error")); + + return 1; +} + +static int gaim_parse_genericerr(aim_session_t *sess, aim_frame_t *fr, ...) { + va_list ap; + guint16 reason; + char *m; + + va_start(ap, fr); + reason = (guint16)va_arg(ap, unsigned int); + va_end(ap); + + m = g_strdup_printf(_("SNAC threw error: %s\n"), + reason < msgerrreasonlen ? msgerrreason[reason] : "Unknown error"); + do_error_dialog(sess->aux_data, m, _("Gaim - Oscar SNAC Error")); + g_free(m); + + return 1; +} + +static int gaim_parse_msgerr(aim_session_t *sess, aim_frame_t *fr, ...) { + va_list ap; + char *destn; + guint16 reason; + char buf[1024]; + + va_start(ap, fr); + reason = (guint16)va_arg(ap, unsigned int); + destn = va_arg(ap, char *); + va_end(ap); + + sprintf(buf, _("Your message to %s did not get sent: %s"), destn, + (reason < msgerrreasonlen) ? msgerrreason[reason] : _("Reason unknown")); + do_error_dialog(sess->aux_data, buf, _("Gaim - Error")); + + return 1; +} + +static int gaim_parse_locerr(aim_session_t *sess, aim_frame_t *fr, ...) { + va_list ap; + char *destn; + guint16 reason; + char buf[1024]; + + va_start(ap, fr); + reason = (guint16)va_arg(ap, unsigned int); + destn = va_arg(ap, char *); + va_end(ap); + + sprintf(buf, _("User information for %s unavailable: %s"), destn, + (reason < msgerrreasonlen) ? msgerrreason[reason] : _("Reason unknown")); + do_error_dialog(sess->aux_data, buf, _("Gaim - Error")); + + + return 1; +} + +static int gaim_parse_motd(aim_session_t *sess, aim_frame_t *fr, ...) { + char *msg; + guint16 id; + va_list ap; + + va_start(ap, fr); + id = (guint16)va_arg(ap, unsigned int); + msg = va_arg(ap, char *); + va_end(ap); + + if (id < 4) + do_error_dialog(sess->aux_data, _("Your connection may be lost."), + _("AOL error")); + + return 1; +} + +static int gaim_chatnav_info(aim_session_t *sess, aim_frame_t *fr, ...) { + va_list ap; + guint16 type; + struct gaim_connection *gc = sess->aux_data; + struct oscar_data *odata = (struct oscar_data *)gc->proto_data; + + va_start(ap, fr); + type = (guint16)va_arg(ap, unsigned int); + + switch(type) { + case 0x0002: { + guint8 maxrooms; + struct aim_chat_exchangeinfo *exchanges; + int exchangecount; // i; + + maxrooms = (guint8)va_arg(ap, unsigned int); + exchangecount = va_arg(ap, int); + exchanges = va_arg(ap, struct aim_chat_exchangeinfo *); + va_end(ap); + + while (odata->create_rooms) { + struct create_room *cr = odata->create_rooms->data; + aim_chatnav_createroom(sess, fr->conn, cr->name, cr->exchange); + g_free(cr->name); + odata->create_rooms = g_slist_remove(odata->create_rooms, cr); + g_free(cr); + } + } + break; + case 0x0008: { + char *fqcn, *name, *ck; + guint16 instance, flags, maxmsglen, maxoccupancy, unknown, exchange; + guint8 createperms; + guint32 createtime; + + fqcn = va_arg(ap, char *); + instance = (guint16)va_arg(ap, unsigned int); + exchange = (guint16)va_arg(ap, unsigned int); + flags = (guint16)va_arg(ap, unsigned int); + createtime = va_arg(ap, guint32); + maxmsglen = (guint16)va_arg(ap, unsigned int); + maxoccupancy = (guint16)va_arg(ap, unsigned int); + createperms = (guint8)va_arg(ap, int); + unknown = (guint16)va_arg(ap, unsigned int); + name = va_arg(ap, char *); + ck = va_arg(ap, char *); + va_end(ap); + + aim_chat_join(odata->sess, odata->conn, exchange, ck, instance); + } + break; + default: + va_end(ap); + break; + } + return 1; +} + +static int gaim_chat_join(aim_session_t *sess, aim_frame_t *fr, ...) { + va_list ap; + int count, i; + aim_userinfo_t *info; + struct gaim_connection *g = sess->aux_data; + + struct chat_connection *c = NULL; + + va_start(ap, fr); + count = va_arg(ap, int); + info = va_arg(ap, aim_userinfo_t *); + va_end(ap); + + c = find_oscar_chat_by_conn(g, fr->conn); + if (!c) + return 1; + + for (i = 0; i < count; i++) + add_chat_buddy(c->cnv, info[i].sn); + + return 1; +} + +static int gaim_chat_leave(aim_session_t *sess, aim_frame_t *fr, ...) { + va_list ap; + int count, i; + aim_userinfo_t *info; + struct gaim_connection *g = sess->aux_data; + + struct chat_connection *c = NULL; + + va_start(ap, fr); + count = va_arg(ap, int); + info = va_arg(ap, aim_userinfo_t *); + va_end(ap); + + c = find_oscar_chat_by_conn(g, fr->conn); + if (!c) + return 1; + + for (i = 0; i < count; i++) + remove_chat_buddy(c->cnv, info[i].sn, NULL); + + return 1; +} + +static int gaim_chat_info_update(aim_session_t *sess, aim_frame_t *fr, ...) { + va_list ap; + aim_userinfo_t *userinfo; + struct aim_chat_roominfo *roominfo; + char *roomname; + int usercount; + char *roomdesc; + guint16 unknown_c9, unknown_d2, unknown_d5, maxmsglen, maxvisiblemsglen; + guint32 creationtime; + struct gaim_connection *gc = sess->aux_data; + struct chat_connection *ccon = find_oscar_chat_by_conn(gc, fr->conn); + + va_start(ap, fr); + roominfo = va_arg(ap, struct aim_chat_roominfo *); + roomname = va_arg(ap, char *); + usercount= va_arg(ap, int); + userinfo = va_arg(ap, aim_userinfo_t *); + roomdesc = va_arg(ap, char *); + unknown_c9 = (guint16)va_arg(ap, int); + creationtime = (guint32)va_arg(ap, unsigned long); + maxmsglen = (guint16)va_arg(ap, int); + unknown_d2 = (guint16)va_arg(ap, int); + unknown_d5 = (guint16)va_arg(ap, int); + maxvisiblemsglen = (guint16)va_arg(ap, int); + va_end(ap); + + ccon->maxlen = maxmsglen; + ccon->maxvis = maxvisiblemsglen; + + return 1; +} + +static int gaim_chat_incoming_msg(aim_session_t *sess, aim_frame_t *fr, ...) { + va_list ap; + aim_userinfo_t *info; + char *msg; + struct gaim_connection *gc = sess->aux_data; + struct chat_connection *ccon = find_oscar_chat_by_conn(gc, fr->conn); + char *tmp; + + va_start(ap, fr); + info = va_arg(ap, aim_userinfo_t *); + msg = va_arg(ap, char *); + + tmp = g_malloc(BUF_LONG); + g_snprintf(tmp, BUF_LONG, "%s", msg); + serv_got_chat_in(gc, ccon->id, info->sn, 0, tmp, time((time_t)NULL)); + g_free(tmp); + + return 1; +} + +static int gaim_parse_ratechange(aim_session_t *sess, aim_frame_t *fr, ...) { +#if 0 + static const char *codes[5] = { + "invalid", + "change", + "warning", + "limit", + "limit cleared", + }; +#endif + va_list ap; + guint16 code, rateclass; + guint32 windowsize, clear, alert, limit, disconnect, currentavg, maxavg; + + va_start(ap, fr); + code = (guint16)va_arg(ap, unsigned int); + rateclass= (guint16)va_arg(ap, unsigned int); + windowsize = (guint32)va_arg(ap, unsigned long); + clear = (guint32)va_arg(ap, unsigned long); + alert = (guint32)va_arg(ap, unsigned long); + limit = (guint32)va_arg(ap, unsigned long); + disconnect = (guint32)va_arg(ap, unsigned long); + currentavg = (guint32)va_arg(ap, unsigned long); + maxavg = (guint32)va_arg(ap, unsigned long); + va_end(ap); + + /* XXX fix these values */ + if (code == AIM_RATE_CODE_CHANGE) { + if (currentavg >= clear) + aim_conn_setlatency(fr->conn, 0); + } else if (code == AIM_RATE_CODE_WARNING) { + aim_conn_setlatency(fr->conn, windowsize/4); + } else if (code == AIM_RATE_CODE_LIMIT) { + do_error_dialog(sess->aux_data, _("The last message was not sent because you are over the rate limit. " + "Please wait 10 seconds and try again."), _("Gaim - Error")); + aim_conn_setlatency(fr->conn, windowsize/2); + } else if (code == AIM_RATE_CODE_CLEARLIMIT) { + aim_conn_setlatency(fr->conn, 0); + } + + return 1; +} + +static int gaim_selfinfo(aim_session_t *sess, aim_frame_t *fr, ...) { + va_list ap; + aim_userinfo_t *info; + struct gaim_connection *gc = sess->aux_data; + + va_start(ap, fr); + info = va_arg(ap, aim_userinfo_t *); + va_end(ap); + + gc->evil = info->warnlevel/10; + /* gc->correction_time = (info->onlinesince - gc->login_time); */ + + return 1; +} + +static int conninitdone_bos(aim_session_t *sess, aim_frame_t *fr, ...) { + + aim_reqpersonalinfo(sess, fr->conn); + aim_bos_reqlocaterights(sess, fr->conn); + aim_bos_reqbuddyrights(sess, fr->conn); + + aim_reqicbmparams(sess); + + aim_bos_reqrights(sess, fr->conn); + aim_bos_setgroupperm(sess, fr->conn, AIM_FLAG_ALLUSERS); + aim_bos_setprivacyflags(sess, fr->conn, AIM_PRIVFLAGS_ALLOWIDLE | + AIM_PRIVFLAGS_ALLOWMEMBERSINCE); + + return 1; +} + +static int conninitdone_admin(aim_session_t *sess, aim_frame_t *fr, ...) { + struct gaim_connection *gc = sess->aux_data; + struct oscar_data *od = gc->proto_data; + + aim_clientready(sess, fr->conn); + + if (od->chpass) { + aim_admin_changepasswd(sess, fr->conn, od->newp, od->oldp); + g_free(od->oldp); + od->oldp = NULL; + g_free(od->newp); + od->newp = NULL; + od->chpass = FALSE; + } + if (od->setnick) { + aim_admin_setnick(sess, fr->conn, od->newsn); + g_free(od->newsn); + od->newsn = NULL; + od->setnick = FALSE; + } + if (od->conf) { + aim_admin_reqconfirm(sess, fr->conn); + od->conf = FALSE; + } + if (od->reqemail) { + aim_admin_getinfo(sess, fr->conn, 0x0011); + od->reqemail = FALSE; + } + if (od->setemail) { + aim_admin_setemail(sess, fr->conn, od->email); + g_free(od->email); + od->setemail = FALSE; + } + + return 1; +} + +static int gaim_icbm_param_info(aim_session_t *sess, aim_frame_t *fr, ...) { + struct aim_icbmparameters *params; + va_list ap; + + va_start(ap, fr); + params = va_arg(ap, struct aim_icbmparameters *); + va_end(ap); + + /* Maybe senderwarn and recverwarn should be user preferences... */ + params->maxmsglen = 8000; + params->minmsginterval = 0; + + aim_seticbmparam(sess, params); + + return 1; +} + +static int gaim_parse_locaterights(aim_session_t *sess, aim_frame_t *fr, ...) +{ + va_list ap; + guint16 maxsiglen; + struct gaim_connection *gc = sess->aux_data; + struct oscar_data *odata = (struct oscar_data *)gc->proto_data; + + va_start(ap, fr); + maxsiglen = va_arg(ap, int); + va_end(ap); + + odata->rights.maxsiglen = odata->rights.maxawaymsglen = (guint)maxsiglen; + + aim_bos_setprofile(sess, fr->conn, gc->user->user_info, NULL, gaim_caps); + + return 1; +} + +static int gaim_parse_buddyrights(aim_session_t *sess, aim_frame_t *fr, ...) { + va_list ap; + guint16 maxbuddies, maxwatchers; + struct gaim_connection *gc = sess->aux_data; + struct oscar_data *odata = (struct oscar_data *)gc->proto_data; + + va_start(ap, fr); + maxbuddies = (guint16)va_arg(ap, unsigned int); + maxwatchers = (guint16)va_arg(ap, unsigned int); + va_end(ap); + + odata->rights.maxbuddies = (guint)maxbuddies; + odata->rights.maxwatchers = (guint)maxwatchers; + + return 1; +} + +static int gaim_bosrights(aim_session_t *sess, aim_frame_t *fr, ...) { + guint16 maxpermits, maxdenies; + va_list ap; + struct gaim_connection *gc = sess->aux_data; + struct oscar_data *odata = (struct oscar_data *)gc->proto_data; + + va_start(ap, fr); + maxpermits = (guint16)va_arg(ap, unsigned int); + maxdenies = (guint16)va_arg(ap, unsigned int); + va_end(ap); + + odata->rights.maxpermits = (guint)maxpermits; + odata->rights.maxdenies = (guint)maxdenies; + +// serv_finish_login(gc); + + if (bud_list_cache_exists(gc)) + do_import(gc, NULL); + + aim_clientready(sess, fr->conn); + + aim_reqservice(sess, fr->conn, AIM_CONN_TYPE_CHATNAV); + + aim_ssi_reqrights(sess, fr->conn); + aim_ssi_reqalldata(sess, fr->conn); + + return 1; +} + +static int gaim_offlinemsg(aim_session_t *sess, aim_frame_t *fr, ...) { + va_list ap; + struct aim_icq_offlinemsg *msg; + struct gaim_connection *gc = sess->aux_data; + + va_start(ap, fr); + msg = va_arg(ap, struct aim_icq_offlinemsg *); + va_end(ap); + + switch (msg->type) { + case 0x0001: { /* Basic offline message */ + char sender[32]; + char *dialog_msg = g_strdup(msg->msg); + time_t t = get_time(msg->year, msg->month, msg->day, msg->hour, msg->minute, 0); + g_snprintf(sender, sizeof(sender), "%u", msg->sender); + strip_linefeed(dialog_msg); + serv_got_im(gc, sender, dialog_msg, 0, t, -1); + g_free(dialog_msg); + } break; + + case 0x0004: { /* Someone sent you a URL */ + char sender[32]; + char *dialog_msg; + char **m; + + time_t t = get_time(msg->year, msg->month, msg->day, msg->hour, msg->minute, 0); + g_snprintf(sender, sizeof(sender), "%u", msg->sender); + + m = g_strsplit(msg->msg, "\376", 2); + + if ((strlen(m[0]) != 0)) { + dialog_msg = g_strjoinv(" -- ", m); + } else { + dialog_msg = m[1]; + } + + strip_linefeed(dialog_msg); + serv_got_im(gc, sender, dialog_msg, 0, t, -1); + g_free(dialog_msg); + g_free(m); + } break; + + case 0x0006: { /* Authorization request */ + gaim_icq_authask(gc, msg->sender, msg->msg); + } break; + + case 0x0007: { /* Someone has denied you authorization */ + serv_got_crap(sess->aux_data, "The user %u has denied your request to add them to your contact list for the following reason:\n%s", msg->sender, msg->msg ? msg->msg : _("No reason given.") ); + } break; + + case 0x0008: { /* Someone has granted you authorization */ + serv_got_crap(sess->aux_data, "The user %u has granted your request to add them to your contact list for the following reason:\n%s", msg->sender, msg->msg ? msg->msg : _("No reason given.") ); + } break; + + case 0x0012: { + /* Ack for authorizing/denying someone. Or possibly an ack for sending any system notice */ + } break; + + default: {; + } + } + + return 1; +} + +static int gaim_offlinemsgdone(aim_session_t *sess, aim_frame_t *fr, ...) +{ + aim_icq_ackofflinemsgs(sess); + return 1; +} + +static void oscar_keepalive(struct gaim_connection *gc) { + struct oscar_data *odata = (struct oscar_data *)gc->proto_data; + aim_flap_nop(odata->sess, odata->conn); +} + +static int oscar_send_im(struct gaim_connection *gc, char *name, char *message, int len, int imflags) { + struct oscar_data *odata = (struct oscar_data *)gc->proto_data; + int ret = 0; + if (imflags & IM_FLAG_AWAY) { + ret = aim_send_im(odata->sess, name, AIM_IMFLAGS_AWAY, message); + } else { + struct aim_sendimext_args args; + char *s; + + args.flags = AIM_IMFLAGS_ACK; + if (odata->icq) + args.flags |= AIM_IMFLAGS_OFFLINE; + for (s = message; *s; s++) + if (*s & 128) + break; + + /* Message contains high ASCII chars, time for some translation! */ + if (*s) { + s = g_malloc(BUF_LONG); + /* Try if we can put it in an ISO8859-1 string first. + If we can't, fall back to UTF16. */ + if ((ret = do_iconv("UTF-8", "ISO8859-1", message, s, len, BUF_LONG)) >= 0) { + args.flags |= AIM_IMFLAGS_ISO_8859_1; + len = ret; + } else if ((ret = do_iconv("UTF-8", "UNICODEBIG", message, s, len, BUF_LONG)) >= 0) { + args.flags |= AIM_IMFLAGS_UNICODE; + len = ret; + } else { + /* OOF, translation failed... Oh well.. */ + g_free( s ); + s = message; + } + } else { + s = message; + } + + args.features = gaim_features; + args.featureslen = sizeof(gaim_features); + + args.destsn = name; + args.msg = s; + args.msglen = len; + + ret = aim_send_im_ext(odata->sess, &args); + + if (s != message) { + g_free(s); + } + } + if (ret >= 0) + return 1; + return ret; +} + +static void oscar_get_info(struct gaim_connection *g, char *name) { + struct oscar_data *odata = (struct oscar_data *)g->proto_data; + if (odata->icq) + aim_icq_getallinfo(odata->sess, name); + else { + aim_getinfo(odata->sess, odata->conn, name, AIM_GETINFO_AWAYMESSAGE); + aim_getinfo(odata->sess, odata->conn, name, AIM_GETINFO_GENERALINFO); + } +} + +static void oscar_get_away(struct gaim_connection *g, char *who) { + struct oscar_data *odata = (struct oscar_data *)g->proto_data; + if (odata->icq) { + struct buddy *budlight = find_buddy(g, who); + if (budlight) + if ((budlight->uc & 0xff80) >> 7) + if (budlight->caps & AIM_CAPS_ICQSERVERRELAY) + aim_send_im_ch2_geticqmessage(odata->sess, who, (budlight->uc & 0xff80) >> 7); + } else + aim_getinfo(odata->sess, odata->conn, who, AIM_GETINFO_AWAYMESSAGE); +} + +static void oscar_set_away_aim(struct gaim_connection *gc, struct oscar_data *od, const char *state, const char *message) +{ + + if (!g_strcasecmp(state, _("Visible"))) { + aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL); + return; + } else if (!g_strcasecmp(state, _("Invisible"))) { + aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_INVISIBLE); + return; + } /* else... */ + + if (od->rights.maxawaymsglen == 0) + do_error_dialog(gc, "oscar_set_away_aim called before locate rights received", "Protocol Error"); + + aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL); + + if (gc->away) + g_free(gc->away); + gc->away = NULL; + + if (!message) { + aim_bos_setprofile(od->sess, od->conn, NULL, "", gaim_caps); + return; + } + + if (strlen(message) > od->rights.maxawaymsglen) { + gchar *errstr; + + errstr = g_strdup_printf("Maximum away message length of %d bytes exceeded, truncating", od->rights.maxawaymsglen); + + do_error_dialog(gc, errstr, "Away Message Too Long"); + + g_free(errstr); + } + + gc->away = g_strndup(message, od->rights.maxawaymsglen); + aim_bos_setprofile(od->sess, od->conn, NULL, gc->away, gaim_caps); + + return; +} + +static void oscar_set_away_icq(struct gaim_connection *gc, struct oscar_data *od, const char *state, const char *message) +{ + const char *msg = NULL; + gboolean no_message = FALSE; + + /* clean old states */ + if (gc->away) { + g_free(gc->away); + gc->away = NULL; + } + od->sess->aim_icq_state = 0; + + /* if no message, then use an empty message */ + if (message) { + msg = message; + } else { + msg = ""; + no_message = TRUE; + } + + if (!g_strcasecmp(state, "Online")) { + aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL); + } else if (!g_strcasecmp(state, "Away")) { + aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY); + gc->away = g_strdup(msg); + od->sess->aim_icq_state = AIM_MTYPE_AUTOAWAY; + } else if (!g_strcasecmp(state, "Do Not Disturb")) { + aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_DND | AIM_ICQ_STATE_BUSY); + gc->away = g_strdup(msg); + od->sess->aim_icq_state = AIM_MTYPE_AUTODND; + } else if (!g_strcasecmp(state, "Not Available")) { + aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_OUT | AIM_ICQ_STATE_AWAY); + gc->away = g_strdup(msg); + od->sess->aim_icq_state = AIM_MTYPE_AUTONA; + } else if (!g_strcasecmp(state, "Occupied")) { + aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY | AIM_ICQ_STATE_BUSY); + gc->away = g_strdup(msg); + od->sess->aim_icq_state = AIM_MTYPE_AUTOBUSY; + } else if (!g_strcasecmp(state, "Free For Chat")) { + aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_CHAT); + gc->away = g_strdup(msg); + od->sess->aim_icq_state = AIM_MTYPE_AUTOFFC; + } else if (!g_strcasecmp(state, "Invisible")) { + aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_INVISIBLE); + gc->away = g_strdup(msg); + } else if (!g_strcasecmp(state, GAIM_AWAY_CUSTOM)) { + if (no_message) { + aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_NORMAL); + } else { + aim_setextstatus(od->sess, od->conn, AIM_ICQ_STATE_AWAY); + gc->away = g_strdup(msg); + od->sess->aim_icq_state = AIM_MTYPE_AUTOAWAY; + } + } + + return; +} + +static void oscar_set_away(struct gaim_connection *gc, char *state, char *message) +{ + struct oscar_data *od = (struct oscar_data *)gc->proto_data; + + oscar_set_away_aim(gc, od, state, message); + if (od->icq) + oscar_set_away_icq(gc, od, state, message); + + return; +} + +static void oscar_add_buddy(struct gaim_connection *g, char *name) { + struct oscar_data *odata = (struct oscar_data *)g->proto_data; + aim_ssi_addbuddies(odata->sess, odata->conn, OSCAR_GROUP, &name, 1, 0); +} + +static void oscar_remove_buddy(struct gaim_connection *g, char *name, char *group) { + struct oscar_data *odata = (struct oscar_data *)g->proto_data; + struct aim_ssi_item *ssigroup; + while ((ssigroup = aim_ssi_itemlist_findparent(odata->sess->ssi.items, name)) && !aim_ssi_delbuddies(odata->sess, odata->conn, ssigroup->name, &name, 1)); +} + +static int gaim_ssi_parserights(aim_session_t *sess, aim_frame_t *fr, ...) { + return 1; +} + +static int gaim_ssi_parselist(aim_session_t *sess, aim_frame_t *fr, ...) { + struct gaim_connection *gc = sess->aux_data; + struct aim_ssi_item *curitem; + int tmp; + + /* Add from server list to local list */ + tmp = 0; + for (curitem=sess->ssi.items; curitem; curitem=curitem->next) { + switch (curitem->type) { + case 0x0000: /* Buddy */ + if ((curitem->name) && (!find_buddy(gc, curitem->name))) { + char *realname = NULL; + + if (curitem->data && aim_gettlv(curitem->data, 0x0131, 1)) + realname = aim_gettlv_str(curitem->data, 0x0131, 1); + + add_buddy(gc, NULL, curitem->name, realname); + + if (realname) + g_free(realname); + } + break; + + case 0x0002: /* Permit buddy */ + if (curitem->name) { + GSList *list; + for (list=gc->permit; (list && aim_sncmp(curitem->name, list->data)); list=list->next); + if (!list) { + char *name; + name = g_strdup(normalize(curitem->name)); + gc->permit = g_slist_append(gc->permit, name); + build_allow_list(); + tmp++; + } + } + break; + + case 0x0003: /* Deny buddy */ + if (curitem->name) { + GSList *list; + for (list=gc->deny; (list && aim_sncmp(curitem->name, list->data)); list=list->next); + if (!list) { + char *name; + name = g_strdup(normalize(curitem->name)); + gc->deny = g_slist_append(gc->deny, name); + build_block_list(); + tmp++; + } + } + break; + + case 0x0004: /* Permit/deny setting */ + if (curitem->data) { + guint8 permdeny; + if ((permdeny = aim_ssi_getpermdeny(sess->ssi.items)) && (permdeny != gc->permdeny)) { + gc->permdeny = permdeny; + tmp++; + } + } + break; + + case 0x0005: /* Presence setting */ + /* We don't want to change Gaim's setting because it applies to all accounts */ + break; + } /* End of switch on curitem->type */ + } /* End of for loop */ + + if (tmp) + do_export(gc); + aim_ssi_enable(sess, fr->conn); + + /* Request offline messages, now that the buddy list is complete. */ + aim_icq_reqofflinemsgs(sess); + + /* Now that we have a buddy list, we can tell BitlBee that we're online. */ + account_online(gc); + + return 1; +} + +static int gaim_ssi_parseack( aim_session_t *sess, aim_frame_t *fr, ... ) +{ + aim_snac_t *origsnac; + va_list ap; + + va_start( ap, fr ); + origsnac = va_arg( ap, aim_snac_t * ); + va_end( ap ); + + if( origsnac && origsnac->family == AIM_CB_FAM_SSI && origsnac->type == AIM_CB_SSI_ADD && origsnac->data ) + { + int i, st, count = aim_bstream_empty( &fr->data ); + char *list; + + if( count & 1 ) + { + /* Hmm, the length should be even... */ + do_error_dialog( sess->aux_data, "Received SSI ACK package with non-even length", "Gaim - Error" ); + return( 0 ); + } + count >>= 1; + + list = (char *) origsnac->data; + for( i = 0; i < count; i ++ ) + { + st = aimbs_get16( &fr->data ); + if( st == 0x0E ) + { + serv_got_crap( sess->aux_data, "Buddy %s can't be added without authorization, requesting authorization", list ); + + aim_ssi_auth_request( sess, fr->conn, list, "" ); + aim_ssi_addbuddies( sess, fr->conn, OSCAR_GROUP, &list, 1, 1 ); + } + list += strlen( list ) + 1; + } + } + + return( 1 ); +} + +static void oscar_set_permit_deny(struct gaim_connection *gc) { + struct oscar_data *od = (struct oscar_data *)gc->proto_data; + if (od->icq) { + GSList *list; + char buf[MAXMSGLEN]; + int at; + + switch(gc->permdeny) { + case 1: + aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_DENYADD, gc->username); + break; + case 2: + aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_PERMITADD, gc->username); + break; + case 3: + list = gc->permit; + at = 0; + while (list) { + at += g_snprintf(buf + at, sizeof(buf) - at, "%s&", (char *)list->data); + list = list->next; + } + aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_PERMITADD, buf); + break; + case 4: + list = gc->deny; + at = 0; + while (list) { + at += g_snprintf(buf + at, sizeof(buf) - at, "%s&", (char *)list->data); + list = list->next; + } + aim_bos_changevisibility(od->sess, od->conn, AIM_VISIBILITYCHANGE_DENYADD, buf); + break; + default: + break; + } + signoff_blocked(gc); + } else { + if (od->sess->ssi.received_data) + aim_ssi_setpermdeny(od->sess, od->conn, gc->permdeny, 0xffffffff); + } +} + +static void oscar_add_permit(struct gaim_connection *gc, char *who) { + struct oscar_data *od = (struct oscar_data *)gc->proto_data; + if (od->icq) { + aim_ssi_auth_reply(od->sess, od->conn, who, 1, ""); + } else { + if (od->sess->ssi.received_data) + aim_ssi_addpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_PERMIT); + } +} + +static void oscar_add_deny(struct gaim_connection *gc, char *who) { + struct oscar_data *od = (struct oscar_data *)gc->proto_data; + if (od->icq) { + aim_ssi_auth_reply(od->sess, od->conn, who, 0, ""); + } else { + if (od->sess->ssi.received_data) + aim_ssi_addpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_DENY); + } +} + +static void oscar_rem_permit(struct gaim_connection *gc, char *who) { + struct oscar_data *od = (struct oscar_data *)gc->proto_data; + if (!od->icq) { + if (od->sess->ssi.received_data) + aim_ssi_delpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_PERMIT); + } +} + +static void oscar_rem_deny(struct gaim_connection *gc, char *who) { + struct oscar_data *od = (struct oscar_data *)gc->proto_data; + if (!od->icq) { + if (od->sess->ssi.received_data) + aim_ssi_delpord(od->sess, od->conn, &who, 1, AIM_SSI_TYPE_DENY); + } +} + +static GList *oscar_away_states(struct gaim_connection *gc) +{ + struct oscar_data *od = gc->proto_data; + GList *m = NULL; + + if (!od->icq) + return g_list_append(m, GAIM_AWAY_CUSTOM); + + m = g_list_append(m, "Online"); + m = g_list_append(m, "Away"); + m = g_list_append(m, "Do Not Disturb"); + m = g_list_append(m, "Not Available"); + m = g_list_append(m, "Occupied"); + m = g_list_append(m, "Free For Chat"); + m = g_list_append(m, "Invisible"); + + return m; +} + +static int gaim_icqinfo(aim_session_t *sess, aim_frame_t *fr, ...) +{ + struct gaim_connection *gc = sess->aux_data; + gchar who[16]; + GString *str; + va_list ap; + struct aim_icq_info *info; + + va_start(ap, fr); + info = va_arg(ap, struct aim_icq_info *); + va_end(ap); + + if (!info->uin) + return 0; + + str = g_string_sized_new(100); + g_snprintf(who, sizeof(who), "%u", info->uin); + + g_string_sprintfa(str, "%s: %s - %s: %s", _("UIN"), who, _("Nick"), + info->nick ? info->nick : "-"); + info_string_append(str, "\n", _("First Name"), info->first); + info_string_append(str, "\n", _("Last Name"), info->last); + info_string_append(str, "\n", _("Email Address"), info->email); + if (info->numaddresses && info->email2) { + int i; + for (i = 0; i < info->numaddresses; i++) { + info_string_append(str, "\n", _("Email Address"), info->email2[i]); + } + } + info_string_append(str, "\n", _("Mobile Phone"), info->mobile); + info_string_append(str, "\n", _("Gender"), info->gender==1 ? _("Female") : _("Male")); + if (info->birthyear || info->birthmonth || info->birthday) { + char date[30]; + struct tm tm; + tm.tm_mday = (int)info->birthday; + tm.tm_mon = (int)info->birthmonth-1; + tm.tm_year = (int)info->birthyear-1900; + strftime(date, sizeof(date), "%Y-%m-%d", &tm); + info_string_append(str, "\n", _("Birthday"), date); + } + if (info->age) { + char age[5]; + g_snprintf(age, sizeof(age), "%hhd", info->age); + info_string_append(str, "\n", _("Age"), age); + } + info_string_append(str, "\n", _("Personal Web Page"), info->personalwebpage); + if (info->info && info->info[0]) { + g_string_sprintfa(str, "\n%s:\n%s\n%s", _("Additional Information"), + info->info, _("End of Additional Information")); + } + g_string_sprintfa(str, "\n"); + if ((info->homeaddr && (info->homeaddr[0])) || (info->homecity && info->homecity[0]) || (info->homestate && info->homestate[0]) || (info->homezip && info->homezip[0])) { + g_string_sprintfa(str, "%s:", _("Home Address")); + info_string_append(str, "\n", _("Address"), info->homeaddr); + info_string_append(str, "\n", _("City"), info->homecity); + info_string_append(str, "\n", _("State"), info->homestate); + info_string_append(str, "\n", _("Zip Code"), info->homezip); + g_string_sprintfa(str, "\n"); + } + if ((info->workaddr && info->workaddr[0]) || (info->workcity && info->workcity[0]) || (info->workstate && info->workstate[0]) || (info->workzip && info->workzip[0])) { + g_string_sprintfa(str, "%s:", _("Work Address")); + info_string_append(str, "\n", _("Address"), info->workaddr); + info_string_append(str, "\n", _("City"), info->workcity); + info_string_append(str, "\n", _("State"), info->workstate); + info_string_append(str, "\n", _("Zip Code"), info->workzip); + g_string_sprintfa(str, "\n"); + } + if ((info->workcompany && info->workcompany[0]) || (info->workdivision && info->workdivision[0]) || (info->workposition && info->workposition[0]) || (info->workwebpage && info->workwebpage[0])) { + g_string_sprintfa(str, "%s:", _("Work Information")); + info_string_append(str, "\n", _("Company"), info->workcompany); + info_string_append(str, "\n", _("Division"), info->workdivision); + info_string_append(str, "\n", _("Position"), info->workposition); + if (info->workwebpage && info->workwebpage[0]) { + info_string_append(str, "\n", _("Web Page"), info->workwebpage); + } + g_string_sprintfa(str, "\n"); + } + + serv_got_crap(gc, "%s\n%s", _("User Info"), str->str); + g_string_free(str, TRUE); + + return 1; + +} + +static char *oscar_encoding_extract(const char *encoding) +{ + char *ret = NULL; + char *begin, *end; + + g_return_val_if_fail(encoding != NULL, NULL); + + /* Make sure encoding begins with charset= */ + if (strncmp(encoding, "text/plain; charset=", 20) && + strncmp(encoding, "text/aolrtf; charset=", 21) && + strncmp(encoding, "text/x-aolrtf; charset=", 23)) + { + return NULL; + } + + begin = strchr(encoding, '"'); + end = strrchr(encoding, '"'); + + if ((begin == NULL) || (end == NULL) || (begin >= end)) + return NULL; + + ret = g_strndup(begin+1, (end-1) - begin); + + return ret; +} + +static char *oscar_encoding_to_utf8(char *encoding, char *text, int textlen) +{ + char *utf8 = g_new0(char, 8192); + + if ((encoding == NULL) || encoding[0] == '\0') { + /* gaim_debug_info("oscar", "Empty encoding, assuming UTF-8\n");*/ + } else if (!g_strcasecmp(encoding, "iso-8859-1")) { + do_iconv("iso-8859-1", "UTF-8", text, utf8, textlen, 8192); + } else if (!g_strcasecmp(encoding, "ISO-8859-1-Windows-3.1-Latin-1")) { + do_iconv("Windows-1252", "UTF-8", text, utf8, textlen, 8192); + } else if (!g_strcasecmp(encoding, "unicode-2-0")) { + do_iconv("UCS-2BE", "UTF-8", text, utf8, textlen, 8192); + } else if (g_strcasecmp(encoding, "us-ascii") && strcmp(encoding, "utf-8")) { + /* gaim_debug_warning("oscar", "Unrecognized character encoding \"%s\", " + "attempting to convert to UTF-8 anyway\n", encoding);*/ + do_iconv(encoding, "UTF-8", text, utf8, textlen, 8192); + } + + /* + * If utf8 is still NULL then either the encoding is us-ascii/utf-8 or + * we have been unable to convert the text to utf-8 from the encoding + * that was specified. So we assume it's UTF-8 and hope for the best. + */ + if (*utf8 == 0) { + strncpy(utf8, text, textlen); + } + + return utf8; +} + +static int gaim_parseaiminfo(aim_session_t *sess, aim_frame_t *fr, ...) +{ + struct gaim_connection *gc = sess->aux_data; + va_list ap; + aim_userinfo_t *userinfo; + guint16 infotype; + char *text_encoding = NULL, *text = NULL, *extracted_encoding = NULL; + guint16 text_length; + char *utf8 = NULL; + + va_start(ap, fr); + userinfo = va_arg(ap, aim_userinfo_t *); + infotype = va_arg(ap, int); + text_encoding = va_arg(ap, char*); + text = va_arg(ap, char*); + text_length = va_arg(ap, int); + va_end(ap); + + if(text_encoding) + extracted_encoding = oscar_encoding_extract(text_encoding); + if(infotype == AIM_GETINFO_GENERALINFO) { + /*Display idle time*/ + char buff[256]; + struct tm idletime; + if(userinfo->idletime) { + memset(&idletime, 0, sizeof(struct tm)); + idletime.tm_mday = (userinfo->idletime / 60) / 24; + idletime.tm_hour = (userinfo->idletime / 60) % 24; + idletime.tm_min = userinfo->idletime % 60; + idletime.tm_sec = 0; + strftime(buff, 256, _("%d days %H hours %M minutes"), &idletime); + serv_got_crap(gc, "%s: %s", _("Idle Time"), buff); + } + + if(text) { + utf8 = oscar_encoding_to_utf8(extracted_encoding, text, text_length); + serv_got_crap(gc, "%s\n%s", _("User Info"), utf8); + } else { + serv_got_crap(gc, _("No user info available.")); + } + } else if(infotype == AIM_GETINFO_AWAYMESSAGE && userinfo->flags & AIM_FLAG_AWAY) { + utf8 = oscar_encoding_to_utf8(extracted_encoding, text, text_length); + serv_got_crap(gc, "%s\n%s", _("Away Message"), utf8); + } + + g_free(utf8); + + return 1; +} + +static char *oscar_get_status_string( struct gaim_connection *gc, int number ) +{ + struct oscar_data *od = gc->proto_data; + + if( ! number & UC_UNAVAILABLE ) + { + return( NULL ); + } + else if( od->icq ) + { + number >>= 7; + if( number & AIM_ICQ_STATE_DND ) + return( "Do Not Disturb" ); + else if( number & AIM_ICQ_STATE_OUT ) + return( "Not Available" ); + else if( number & AIM_ICQ_STATE_BUSY ) + return( "Occupied" ); + else if( number & AIM_ICQ_STATE_INVISIBLE ) + return( "Invisible" ); + else + return( "Away" ); + } + else + { + return( "Away" ); + } +} + +static struct prpl *my_protocol = NULL; + +void oscar_init(struct prpl *ret) { + ret->protocol = PROTO_OSCAR; + ret->away_states = oscar_away_states; + ret->login = oscar_login; + ret->close = oscar_close; + ret->send_im = oscar_send_im; + ret->get_info = oscar_get_info; + ret->set_away = oscar_set_away; + ret->get_away = oscar_get_away; + ret->add_buddy = oscar_add_buddy; + ret->remove_buddy = oscar_remove_buddy; + ret->add_permit = oscar_add_permit; + ret->add_deny = oscar_add_deny; + ret->rem_permit = oscar_rem_permit; + ret->rem_deny = oscar_rem_deny; + ret->set_permit_deny = oscar_set_permit_deny; + ret->keepalive = oscar_keepalive; + ret->get_status_string = oscar_get_status_string; + + my_protocol = ret; +} diff --git a/protocols/oscar/oscar_util.c b/protocols/oscar/oscar_util.c new file mode 100644 index 00000000..ed8409a4 --- /dev/null +++ b/protocols/oscar/oscar_util.c @@ -0,0 +1,167 @@ +/* + * + * + * + */ + +#include <aim.h> +#include <ctype.h> + +int aimutil_putstr(u_char *dest, const char *src, int len) +{ + memcpy(dest, src, len); + return len; +} + +/* + * Tokenizing functions. Used to portably replace strtok/sep. + * -- DMP. + * + */ +int aimutil_tokslen(char *toSearch, int index, char dl) +{ + int curCount = 1; + char *next; + char *last; + int toReturn; + + last = toSearch; + next = strchr(toSearch, dl); + + while(curCount < index && next != NULL) { + curCount++; + last = next + 1; + next = strchr(last, dl); + } + + if ((curCount < index) || (next == NULL)) + toReturn = strlen(toSearch) - (curCount - 1); + else + toReturn = next - toSearch - (curCount - 1); + + return toReturn; +} + +int aimutil_itemcnt(char *toSearch, char dl) +{ + int curCount; + char *next; + + curCount = 1; + + next = strchr(toSearch, dl); + + while(next != NULL) { + curCount++; + next = strchr(next + 1, dl); + } + + return curCount; +} + +char *aimutil_itemidx(char *toSearch, int index, char dl) +{ + int curCount; + char *next; + char *last; + char *toReturn; + + curCount = 0; + + last = toSearch; + next = strchr(toSearch, dl); + + while (curCount < index && next != NULL) { + curCount++; + last = next + 1; + next = strchr(last, dl); + } + + if (curCount < index) { + toReturn = g_strdup(""); + } + next = strchr(last, dl); + + if (curCount < index) { + toReturn = g_strdup(""); + } else { + if (next == NULL) { + toReturn = g_malloc((strlen(last) + 1) * sizeof(char)); + strcpy(toReturn, last); + } else { + toReturn = g_malloc((next - last + 1) * sizeof(char)); + memcpy(toReturn, last, (next - last)); + toReturn[next - last] = '\0'; + } + } + return toReturn; +} + +/* +* int snlen(const char *) +* +* This takes a screen name and returns its length without +* spaces. If there are no spaces in the SN, then the +* return is equal to that of strlen(). +* +*/ +static int aim_snlen(const char *sn) +{ + int i = 0; + const char *curPtr = NULL; + + if (!sn) + return 0; + + curPtr = sn; + while ( (*curPtr) != (char) NULL) { + if ((*curPtr) != ' ') + i++; + curPtr++; + } + + return i; +} + +/* +* int sncmp(const char *, const char *) +* +* This takes two screen names and compares them using the rules +* on screen names for AIM/AOL. Mainly, this means case and space +* insensitivity (all case differences and spacing differences are +* ignored). +* +* Return: 0 if equal +* non-0 if different +* +*/ + +int aim_sncmp(const char *sn1, const char *sn2) +{ + const char *curPtr1 = NULL, *curPtr2 = NULL; + + if (aim_snlen(sn1) != aim_snlen(sn2)) + return 1; + + curPtr1 = sn1; + curPtr2 = sn2; + while ( (*curPtr1 != (char) NULL) && (*curPtr2 != (char) NULL) ) { + if ( (*curPtr1 == ' ') || (*curPtr2 == ' ') ) { + if (*curPtr1 == ' ') + curPtr1++; + if (*curPtr2 == ' ') + curPtr2++; + } else { + if ( toupper(*curPtr1) != toupper(*curPtr2)) + return 1; + curPtr1++; + curPtr2++; + } + } + + /* Should both be NULL */ + if (*curPtr1 != *curPtr2) + return 1; + + return 0; +} diff --git a/protocols/oscar/rxhandlers.c b/protocols/oscar/rxhandlers.c new file mode 100644 index 00000000..eb0898ec --- /dev/null +++ b/protocols/oscar/rxhandlers.c @@ -0,0 +1,408 @@ +/* + * aim_rxhandlers.c + * + * This file contains most all of the incoming packet handlers, along + * with aim_rxdispatch(), the Rx dispatcher. Queue/list management is + * actually done in aim_rxqueue.c. + * + */ + +#include <aim.h> + +struct aim_rxcblist_s { + guint16 family; + guint16 type; + aim_rxcallback_t handler; + u_short flags; + struct aim_rxcblist_s *next; +}; + +aim_module_t *aim__findmodulebygroup(aim_session_t *sess, guint16 group) +{ + aim_module_t *cur; + + for (cur = (aim_module_t *)sess->modlistv; cur; cur = cur->next) { + if (cur->family == group) + return cur; + } + + return NULL; +} + +static aim_module_t *aim__findmodule(aim_session_t *sess, const char *name) +{ + aim_module_t *cur; + + for (cur = (aim_module_t *)sess->modlistv; cur; cur = cur->next) { + if (strcmp(name, cur->name) == 0) + return cur; + } + + return NULL; +} + +int aim__registermodule(aim_session_t *sess, int (*modfirst)(aim_session_t *, aim_module_t *)) +{ + aim_module_t *mod; + + if (!sess || !modfirst) + return -1; + + if (!(mod = g_new0(aim_module_t,1))) + return -1; + + if (modfirst(sess, mod) == -1) { + g_free(mod); + return -1; + } + + if (aim__findmodule(sess, mod->name)) { + if (mod->shutdown) + mod->shutdown(sess, mod); + g_free(mod); + return -1; + } + + mod->next = (aim_module_t *)sess->modlistv; + sess->modlistv = mod; + + + return 0; +} + +void aim__shutdownmodules(aim_session_t *sess) +{ + aim_module_t *cur; + + for (cur = (aim_module_t *)sess->modlistv; cur; ) { + aim_module_t *tmp; + + tmp = cur->next; + + if (cur->shutdown) + cur->shutdown(sess, cur); + + g_free(cur); + + cur = tmp; + } + + sess->modlistv = NULL; + + return; +} + +static int consumesnac(aim_session_t *sess, aim_frame_t *rx) +{ + aim_module_t *cur; + aim_modsnac_t snac; + + if (aim_bstream_empty(&rx->data) < 10) + return 0; + + snac.family = aimbs_get16(&rx->data); + snac.subtype = aimbs_get16(&rx->data); + snac.flags = aimbs_get16(&rx->data); + snac.id = aimbs_get32(&rx->data); + + /* Contains TLV(s) in the FNAC header */ + if(snac.flags & 0x8000) { + aim_bstream_advance(&rx->data, aimbs_get16(&rx->data)); + } else if(snac.flags & 0x0001) { + /* Following SNAC will be related */ + } + + if (set_getint(sess->aux_data, "debug")) { + serv_got_crap(sess->aux_data, "snac %x/%x received", snac.family, snac.subtype); + } + + for (cur = (aim_module_t *)sess->modlistv; cur; cur = cur->next) { + + if (!(cur->flags & AIM_MODFLAG_MULTIFAMILY) && + (cur->family != snac.family)) + continue; + + if (cur->snachandler(sess, cur, rx, &snac, &rx->data)) + return 1; + + } + + return 0; +} + +static int consumenonsnac(aim_session_t *sess, aim_frame_t *rx, guint16 family, guint16 subtype) +{ + aim_module_t *cur; + aim_modsnac_t snac; + + snac.family = family; + snac.subtype = subtype; + snac.flags = snac.id = 0; + + for (cur = (aim_module_t *)sess->modlistv; cur; cur = cur->next) { + + if (!(cur->flags & AIM_MODFLAG_MULTIFAMILY) && + (cur->family != snac.family)) + continue; + + if (cur->snachandler(sess, cur, rx, &snac, &rx->data)) + return 1; + + } + + return 0; +} + +static int negchan_middle(aim_session_t *sess, aim_frame_t *fr) +{ + aim_tlvlist_t *tlvlist; + char *msg = NULL; + guint16 code = 0; + aim_rxcallback_t userfunc; + int ret = 1; + + if (aim_bstream_empty(&fr->data) == 0) { + /* XXX should do something with this */ + return 1; + } + + /* Used only by the older login protocol */ + /* XXX remove this special case? */ + if (fr->conn->type == AIM_CONN_TYPE_AUTH) + return consumenonsnac(sess, fr, 0x0017, 0x0003); + + tlvlist = aim_readtlvchain(&fr->data); + + if (aim_gettlv(tlvlist, 0x0009, 1)) + code = aim_gettlv16(tlvlist, 0x0009, 1); + + if (aim_gettlv(tlvlist, 0x000b, 1)) + msg = aim_gettlv_str(tlvlist, 0x000b, 1); + + if ((userfunc = aim_callhandler(sess, fr->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNERR))) + ret = userfunc(sess, fr, code, msg); + + aim_freetlvchain(&tlvlist); + + g_free(msg); + + return ret; +} + +/* + * Some SNACs we do not allow to be hooked, for good reason. + */ +static int checkdisallowed(guint16 group, guint16 type) +{ + static const struct { + guint16 group; + guint16 type; + } dontuse[] = { + {0x0001, 0x0002}, + {0x0001, 0x0003}, + {0x0001, 0x0006}, + {0x0001, 0x0007}, + {0x0001, 0x0008}, + {0x0001, 0x0017}, + {0x0001, 0x0018}, + {0x0000, 0x0000} + }; + int i; + + for (i = 0; dontuse[i].group != 0x0000; i++) { + if ((dontuse[i].group == group) && (dontuse[i].type == type)) + return 1; + } + + return 0; +} + +int aim_conn_addhandler(aim_session_t *sess, aim_conn_t *conn, guint16 family, guint16 type, aim_rxcallback_t newhandler, guint16 flags) +{ + struct aim_rxcblist_s *newcb; + + if (!conn) + return -1; + + if (checkdisallowed(family, type)) { + g_assert(0); + return -1; + } + + if (!(newcb = (struct aim_rxcblist_s *)g_new0(struct aim_rxcblist_s, 1))) + return -1; + + newcb->family = family; + newcb->type = type; + newcb->flags = flags; + newcb->handler = newhandler; + newcb->next = NULL; + + if (!conn->handlerlist) + conn->handlerlist = (void *)newcb; + else { + struct aim_rxcblist_s *cur; + + for (cur = (struct aim_rxcblist_s *)conn->handlerlist; cur->next; cur = cur->next) + ; + cur->next = newcb; + } + + return 0; +} + +int aim_clearhandlers(aim_conn_t *conn) +{ + struct aim_rxcblist_s *cur; + + if (!conn) + return -1; + + for (cur = (struct aim_rxcblist_s *)conn->handlerlist; cur; ) { + struct aim_rxcblist_s *tmp; + + tmp = cur->next; + g_free(cur); + cur = tmp; + } + conn->handlerlist = NULL; + + return 0; +} + +aim_rxcallback_t aim_callhandler(aim_session_t *sess, aim_conn_t *conn, guint16 family, guint16 type) +{ + struct aim_rxcblist_s *cur; + + if (!conn) + return NULL; + + for (cur = (struct aim_rxcblist_s *)conn->handlerlist; cur; cur = cur->next) { + if ((cur->family == family) && (cur->type == type)) + return cur->handler; + } + + if (type == AIM_CB_SPECIAL_DEFAULT) { + return NULL; /* prevent infinite recursion */ + } + + return aim_callhandler(sess, conn, family, AIM_CB_SPECIAL_DEFAULT); +} + +void aim_clonehandlers(aim_session_t *sess, aim_conn_t *dest, aim_conn_t *src) +{ + struct aim_rxcblist_s *cur; + + for (cur = (struct aim_rxcblist_s *)src->handlerlist; cur; cur = cur->next) { + aim_conn_addhandler(sess, dest, cur->family, cur->type, + cur->handler, cur->flags); + } + + return; +} + +static int aim_callhandler_noparam(aim_session_t *sess, aim_conn_t *conn,guint16 family, guint16 type, aim_frame_t *ptr) +{ + aim_rxcallback_t userfunc; + + if ((userfunc = aim_callhandler(sess, conn, family, type))) + return userfunc(sess, ptr); + + return 1; /* XXX */ +} + +/* + * aim_rxdispatch() + * + * Basically, heres what this should do: + * 1) Determine correct packet handler for this packet + * 2) Mark the packet handled (so it can be dequeued in purge_queue()) + * 3) Send the packet to the packet handler + * 4) Go to next packet in the queue and start over + * 5) When done, run purge_queue() to purge handled commands + * + * TODO: Clean up. + * TODO: More support for mid-level handlers. + * TODO: Allow for NULL handlers. + * + */ +void aim_rxdispatch(aim_session_t *sess) +{ + int i; + aim_frame_t *cur; + + for (cur = sess->queue_incoming, i = 0; cur; cur = cur->next, i++) { + + /* + * XXX: This is still fairly ugly. + */ + + if (cur->handled) + continue; + + /* + * This is a debugging/sanity check only and probably + * could/should be removed for stable code. + */ + if (((cur->hdrtype == AIM_FRAMETYPE_OFT) && + (cur->conn->type != AIM_CONN_TYPE_RENDEZVOUS)) || + ((cur->hdrtype == AIM_FRAMETYPE_FLAP) && + (cur->conn->type == AIM_CONN_TYPE_RENDEZVOUS))) { + do_error_dialog(sess->aux_data, "incompatible frame type/connection type combination", "Gaim"); + cur->handled = 1; + continue; + } + + if (cur->conn->type == AIM_CONN_TYPE_RENDEZVOUS) { + if (cur->hdrtype != AIM_FRAMETYPE_OFT) { + do_error_dialog(sess->aux_data, "non-OFT frames on OFT connection", "Gaim"); + cur->handled = 1; /* get rid of it */ + } else { + /* FIXME: implement this (OFT frame) */ + cur->handled = 1; /* get rid of it */ + } + continue; + } + + if (cur->conn->type == AIM_CONN_TYPE_RENDEZVOUS_OUT) { + /* not possible */ + do_error_dialog(sess->aux_data, "RENDEZVOUS packet in rxqueue", "Gaim"); + cur->handled = 1; + continue; + } + + if (cur->hdr.flap.type == 0x01) { + + cur->handled = aim_callhandler_noparam(sess, cur->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_FLAPVER, cur); /* XXX use consumenonsnac */ + + continue; + + } else if (cur->hdr.flap.type == 0x02) { + + if ((cur->handled = consumesnac(sess, cur))) + continue; + + } else if (cur->hdr.flap.type == 0x04) { + + cur->handled = negchan_middle(sess, cur); + continue; + + } else if (cur->hdr.flap.type == 0x05) + ; + + if (!cur->handled) { + consumenonsnac(sess, cur, 0xffff, 0xffff); /* last chance! */ + cur->handled = 1; + } + } + + /* + * This doesn't have to be called here. It could easily be done + * by a seperate thread or something. It's an administrative operation, + * and can take a while. Though the less you call it the less memory + * you'll have :) + */ + aim_purge_rxqueue(sess); + + return; +} diff --git a/protocols/oscar/rxqueue.c b/protocols/oscar/rxqueue.c new file mode 100644 index 00000000..d95bc026 --- /dev/null +++ b/protocols/oscar/rxqueue.c @@ -0,0 +1,508 @@ +/* + * aim_rxqueue.c + * + * This file contains the management routines for the receive + * (incoming packet) queue. The actual packet handlers are in + * aim_rxhandlers.c. + */ + +#include <aim.h> + +#ifndef _WIN32 +#include <sys/socket.h> +#endif + +/* + * + */ +int aim_recv(int fd, void *buf, size_t count) +{ + int left, cur; + + for (cur = 0, left = count; left; ) { + int ret; + + ret = recv(fd, ((unsigned char *)buf)+cur, left, 0); + + /* Of course EOF is an error, only morons disagree with that. */ + if (ret <= 0) + return -1; + + cur += ret; + left -= ret; + } + + return cur; +} + +/* + * Read into a byte stream. Will not read more than count, but may read + * less if there is not enough room in the stream buffer. + */ +static int aim_bstream_recv(aim_bstream_t *bs, int fd, size_t count) +{ + int red = 0; + + if (!bs || (fd < 0) || (count < 0)) + return -1; + + if (count > (bs->len - bs->offset)) + count = bs->len - bs->offset; /* truncate to remaining space */ + + if (count) { + + red = aim_recv(fd, bs->data + bs->offset, count); + + if (red <= 0) + return -1; + } + + bs->offset += red; + + return red; +} + +int aim_bstream_init(aim_bstream_t *bs, guint8 *data, int len) +{ + + if (!bs) + return -1; + + bs->data = data; + bs->len = len; + bs->offset = 0; + + return 0; +} + +int aim_bstream_empty(aim_bstream_t *bs) +{ + return bs->len - bs->offset; +} + +int aim_bstream_curpos(aim_bstream_t *bs) +{ + return bs->offset; +} + +int aim_bstream_setpos(aim_bstream_t *bs, int off) +{ + + if (off > bs->len) + return -1; + + bs->offset = off; + + return off; +} + +void aim_bstream_rewind(aim_bstream_t *bs) +{ + + aim_bstream_setpos(bs, 0); + + return; +} + +int aim_bstream_advance(aim_bstream_t *bs, int n) +{ + + if (aim_bstream_empty(bs) < n) + return 0; /* XXX throw an exception */ + + bs->offset += n; + + return n; +} + +guint8 aimbs_get8(aim_bstream_t *bs) +{ + + if (aim_bstream_empty(bs) < 1) + return 0; /* XXX throw an exception */ + + bs->offset++; + + return aimutil_get8(bs->data + bs->offset - 1); +} + +guint16 aimbs_get16(aim_bstream_t *bs) +{ + + if (aim_bstream_empty(bs) < 2) + return 0; /* XXX throw an exception */ + + bs->offset += 2; + + return aimutil_get16(bs->data + bs->offset - 2); +} + +guint32 aimbs_get32(aim_bstream_t *bs) +{ + + if (aim_bstream_empty(bs) < 4) + return 0; /* XXX throw an exception */ + + bs->offset += 4; + + return aimutil_get32(bs->data + bs->offset - 4); +} + +guint8 aimbs_getle8(aim_bstream_t *bs) +{ + + if (aim_bstream_empty(bs) < 1) + return 0; /* XXX throw an exception */ + + bs->offset++; + + return aimutil_getle8(bs->data + bs->offset - 1); +} + +guint16 aimbs_getle16(aim_bstream_t *bs) +{ + + if (aim_bstream_empty(bs) < 2) + return 0; /* XXX throw an exception */ + + bs->offset += 2; + + return aimutil_getle16(bs->data + bs->offset - 2); +} + +guint32 aimbs_getle32(aim_bstream_t *bs) +{ + + if (aim_bstream_empty(bs) < 4) + return 0; /* XXX throw an exception */ + + bs->offset += 4; + + return aimutil_getle32(bs->data + bs->offset - 4); +} + +int aimbs_put8(aim_bstream_t *bs, guint8 v) +{ + + if (aim_bstream_empty(bs) < 1) + return 0; /* XXX throw an exception */ + + bs->offset += aimutil_put8(bs->data + bs->offset, v); + + return 1; +} + +int aimbs_put16(aim_bstream_t *bs, guint16 v) +{ + + if (aim_bstream_empty(bs) < 2) + return 0; /* XXX throw an exception */ + + bs->offset += aimutil_put16(bs->data + bs->offset, v); + + return 2; +} + +int aimbs_put32(aim_bstream_t *bs, guint32 v) +{ + + if (aim_bstream_empty(bs) < 4) + return 0; /* XXX throw an exception */ + + bs->offset += aimutil_put32(bs->data + bs->offset, v); + + return 1; +} + +int aimbs_putle8(aim_bstream_t *bs, guint8 v) +{ + + if (aim_bstream_empty(bs) < 1) + return 0; /* XXX throw an exception */ + + bs->offset += aimutil_putle8(bs->data + bs->offset, v); + + return 1; +} + +int aimbs_putle16(aim_bstream_t *bs, guint16 v) +{ + + if (aim_bstream_empty(bs) < 2) + return 0; /* XXX throw an exception */ + + bs->offset += aimutil_putle16(bs->data + bs->offset, v); + + return 2; +} + +int aimbs_putle32(aim_bstream_t *bs, guint32 v) +{ + + if (aim_bstream_empty(bs) < 4) + return 0; /* XXX throw an exception */ + + bs->offset += aimutil_putle32(bs->data + bs->offset, v); + + return 1; +} + +int aimbs_getrawbuf(aim_bstream_t *bs, guint8 *buf, int len) +{ + + if (aim_bstream_empty(bs) < len) + return 0; + + memcpy(buf, bs->data + bs->offset, len); + bs->offset += len; + + return len; +} + +guint8 *aimbs_getraw(aim_bstream_t *bs, int len) +{ + guint8 *ob; + + if (!(ob = g_malloc(len))) + return NULL; + + if (aimbs_getrawbuf(bs, ob, len) < len) { + g_free(ob); + return NULL; + } + + return ob; +} + +char *aimbs_getstr(aim_bstream_t *bs, int len) +{ + guint8 *ob; + + if (!(ob = g_malloc(len+1))) + return NULL; + + if (aimbs_getrawbuf(bs, ob, len) < len) { + g_free(ob); + return NULL; + } + + ob[len] = '\0'; + + return (char *)ob; +} + +int aimbs_putraw(aim_bstream_t *bs, const guint8 *v, int len) +{ + + if (aim_bstream_empty(bs) < len) + return 0; /* XXX throw an exception */ + + memcpy(bs->data + bs->offset, v, len); + bs->offset += len; + + return len; +} + +int aimbs_putbs(aim_bstream_t *bs, aim_bstream_t *srcbs, int len) +{ + + if (aim_bstream_empty(srcbs) < len) + return 0; /* XXX throw exception (underrun) */ + + if (aim_bstream_empty(bs) < len) + return 0; /* XXX throw exception (overflow) */ + + memcpy(bs->data + bs->offset, srcbs->data + srcbs->offset, len); + bs->offset += len; + srcbs->offset += len; + + return len; +} + +/** + * aim_frame_destroy - free aim_frame_t + * @frame: the frame to free + * + * returns -1 on error; 0 on success. + * + */ +void aim_frame_destroy(aim_frame_t *frame) +{ + + g_free(frame->data.data); /* XXX aim_bstream_free */ + + if (frame->hdrtype == AIM_FRAMETYPE_OFT) + g_free(frame->hdr.oft.hdr2); + g_free(frame); + + return; +} + + +/* + * Grab a single command sequence off the socket, and enqueue + * it in the incoming event queue in a seperate struct. + */ +int aim_get_command(aim_session_t *sess, aim_conn_t *conn) +{ + guint8 flaphdr_raw[6]; + aim_bstream_t flaphdr; + aim_frame_t *newrx; + guint16 payloadlen; + + if (!sess || !conn) + return 0; + + if (conn->fd == -1) + return -1; /* its a aim_conn_close()'d connection */ + + if (conn->fd < 3) /* can happen when people abuse the interface */ + return 0; + + if (conn->status & AIM_CONN_STATUS_INPROGRESS) + return aim_conn_completeconnect(sess, conn); + + /* + * Rendezvous (client-client) connections do not speak + * FLAP, so this function will break on them. + */ + if (conn->type == AIM_CONN_TYPE_RENDEZVOUS) + return aim_get_command_rendezvous(sess, conn); + else if (conn->type == AIM_CONN_TYPE_RENDEZVOUS_OUT) { + do_error_dialog(sess->aux_data,"AIM_CONN_TYPE_RENDEZVOUS_OUT shouldn't use FLAP", "Gaim"); + return 0; + } + + aim_bstream_init(&flaphdr, flaphdr_raw, sizeof(flaphdr_raw)); + + /* + * Read FLAP header. Six bytes: + * + * 0 char -- Always 0x2a + * 1 char -- Channel ID. Usually 2 -- 1 and 4 are used during login. + * 2 short -- Sequence number + * 4 short -- Number of data bytes that follow. + */ + if (aim_bstream_recv(&flaphdr, conn->fd, 6) < 6) { + aim_conn_close(conn); + return -1; + } + + aim_bstream_rewind(&flaphdr); + + /* + * This shouldn't happen unless the socket breaks, the server breaks, + * or we break. We must handle it just in case. + */ + if (aimbs_get8(&flaphdr) != 0x2a) { + guint8 start; + + aim_bstream_rewind(&flaphdr); + start = aimbs_get8(&flaphdr); + do_error_dialog(sess->aux_data, "FLAP framing disrupted", "Gaim"); + aim_conn_close(conn); + return -1; + } + + /* allocate a new struct */ + if (!(newrx = (aim_frame_t *)g_new0(aim_frame_t,1))) + return -1; + + /* we're doing FLAP if we're here */ + newrx->hdrtype = AIM_FRAMETYPE_FLAP; + + newrx->hdr.flap.type = aimbs_get8(&flaphdr); + newrx->hdr.flap.seqnum = aimbs_get16(&flaphdr); + payloadlen = aimbs_get16(&flaphdr); + + newrx->nofree = 0; /* free by default */ + + if (payloadlen) { + guint8 *payload = NULL; + + if (!(payload = (guint8 *) g_malloc(payloadlen))) { + aim_frame_destroy(newrx); + return -1; + } + + aim_bstream_init(&newrx->data, payload, payloadlen); + + /* read the payload */ + if (aim_bstream_recv(&newrx->data, conn->fd, payloadlen) < payloadlen) { + aim_frame_destroy(newrx); /* free's payload */ + aim_conn_close(conn); + return -1; + } + } else + aim_bstream_init(&newrx->data, NULL, 0); + + + aim_bstream_rewind(&newrx->data); + + newrx->conn = conn; + + newrx->next = NULL; /* this will always be at the bottom */ + + if (!sess->queue_incoming) + sess->queue_incoming = newrx; + else { + aim_frame_t *cur; + + for (cur = sess->queue_incoming; cur->next; cur = cur->next) + ; + cur->next = newrx; + } + + newrx->conn->lastactivity = time(NULL); + + return 0; +} + +/* + * Purge recieve queue of all handled commands (->handled==1). Also + * allows for selective freeing using ->nofree so that the client can + * keep the data for various purposes. + * + * If ->nofree is nonzero, the frame will be delinked from the global list, + * but will not be free'ed. The client _must_ keep a pointer to the + * data -- libfaim will not! If the client marks ->nofree but + * does not keep a pointer, it's lost forever. + * + */ +void aim_purge_rxqueue(aim_session_t *sess) +{ + aim_frame_t *cur, **prev; + + for (prev = &sess->queue_incoming; (cur = *prev); ) { + if (cur->handled) { + + *prev = cur->next; + + if (!cur->nofree) + aim_frame_destroy(cur); + + } else + prev = &cur->next; + } + + return; +} + +/* + * Since aim_get_command will aim_conn_kill dead connections, we need + * to clean up the rxqueue of unprocessed connections on that socket. + * + * XXX: this is something that was handled better in the old connection + * handling method, but eh. + */ +void aim_rxqueue_cleanbyconn(aim_session_t *sess, aim_conn_t *conn) +{ + aim_frame_t *currx; + + for (currx = sess->queue_incoming; currx; currx = currx->next) { + if ((!currx->handled) && (currx->conn == conn)) + currx->handled = 1; + } + return; +} + diff --git a/protocols/oscar/search.c b/protocols/oscar/search.c new file mode 100644 index 00000000..9685a3d1 --- /dev/null +++ b/protocols/oscar/search.c @@ -0,0 +1,121 @@ + +/* + * aim_search.c + * + * TODO: Add aim_usersearch_name() + * + */ + +#include <aim.h> + +int aim_usersearch_address(aim_session_t *sess, aim_conn_t *conn, const char *address) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !conn || !address) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+strlen(address)))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x000a, 0x0002, 0x0000, g_strdup(address), strlen(address)+1); + aim_putsnac(&fr->data, 0x000a, 0x0002, 0x0000, snacid); + + aimbs_putraw(&fr->data, (guint8 *)address, strlen(address)); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* XXX can this be integrated with the rest of the error handling? */ +static int error(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + aim_rxcallback_t userfunc; + aim_snac_t *snac2; + + /* XXX the modules interface should have already retrieved this for us */ + if (!(snac2 = aim_remsnac(sess, snac->id))) { + do_error_dialog(sess->aux_data, "couldn't get snac", "Gaim"); + return 0; + } + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, snac2->data /* address */); + + /* XXX freesnac()? */ + if (snac2) + g_free(snac2->data); + g_free(snac2); + + return ret; +} + +static int reply(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int j = 0, m, ret = 0; + aim_tlvlist_t *tlvlist; + char *cur = NULL, *buf = NULL; + aim_rxcallback_t userfunc; + aim_snac_t *snac2; + char *searchaddr = NULL; + + if ((snac2 = aim_remsnac(sess, snac->id))) + searchaddr = (char *)snac2->data; + + tlvlist = aim_readtlvchain(bs); + m = aim_counttlvchain(&tlvlist); + + /* XXX uhm. */ + while ((cur = aim_gettlv_str(tlvlist, 0x0001, j+1)) && j < m) { + buf = g_realloc(buf, (j+1) * (MAXSNLEN+1)); + + strncpy(&buf[j * (MAXSNLEN+1)], cur, MAXSNLEN); + g_free(cur); + + j++; + } + + aim_freetlvchain(&tlvlist); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, searchaddr, j, buf); + + /* XXX freesnac()? */ + if (snac2) + g_free(snac2->data); + g_free(snac2); + + g_free(buf); + + return ret; +} + +static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + + if (snac->subtype == 0x0001) + return error(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0003) + return reply(sess, mod, rx, snac, bs); + + return 0; +} + +int search_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x000a; + mod->version = 0x0001; + mod->toolid = 0x0110; + mod->toolversion = 0x0629; + mod->flags = 0; + strncpy(mod->name, "search", sizeof(mod->name)); + mod->snachandler = snachandler; + + return 0; +} + + diff --git a/protocols/oscar/search.h b/protocols/oscar/search.h new file mode 100644 index 00000000..77eeb265 --- /dev/null +++ b/protocols/oscar/search.h @@ -0,0 +1,6 @@ +#ifndef __OSCAR_SEARCH_H__ +#define __OSCAR_SEARCH_H__ + +int aim_usersearch_address(aim_session_t *, aim_conn_t *, const char *); + +#endif /* __OSCAR_SEARCH_H__ */ diff --git a/protocols/oscar/service.c b/protocols/oscar/service.c new file mode 100644 index 00000000..875a2eb0 --- /dev/null +++ b/protocols/oscar/service.c @@ -0,0 +1,946 @@ +/* + * Group 1. This is a very special group. All connections support + * this group, as it does some particularly good things (like rate limiting). + */ + +#include <aim.h> + +#include "md5.h" + +/* Client Online (group 1, subtype 2) */ +int aim_clientready(aim_session_t *sess, aim_conn_t *conn) +{ + aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside; + struct snacgroup *sg; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!ins) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0001, 0x0002, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0001, 0x0002, 0x0000, snacid); + + /* + * Send only the tool versions that the server cares about (that it + * marked as supporting in the server ready SNAC). + */ + for (sg = ins->groups; sg; sg = sg->next) { + aim_module_t *mod; + + if ((mod = aim__findmodulebygroup(sess, sg->group))) { + aimbs_put16(&fr->data, mod->family); + aimbs_put16(&fr->data, mod->version); + aimbs_put16(&fr->data, mod->toolid); + aimbs_put16(&fr->data, mod->toolversion); + } + } + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Host Online (group 1, type 3) + * + * See comments in conn.c about how the group associations are supposed + * to work, and how they really work. + * + * This info probably doesn't even need to make it to the client. + * + * We don't actually call the client here. This starts off the connection + * initialization routine required by all AIM connections. The next time + * the client is called is the CONNINITDONE callback, which should be + * shortly after the rate information is acknowledged. + * + */ +static int hostonline(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + guint16 *families; + int famcount; + + + if (!(families = g_malloc(aim_bstream_empty(bs)))) + return 0; + + for (famcount = 0; aim_bstream_empty(bs); famcount++) { + families[famcount] = aimbs_get16(bs); + aim_conn_addgroup(rx->conn, families[famcount]); + } + + g_free(families); + + + /* + * Next step is in the Host Versions handler. + * + * Note that we must send this before we request rates, since + * the format of the rate information depends on the versions we + * give it. + * + */ + aim_setversions(sess, rx->conn); + + return 1; +} + +/* Service request (group 1, type 4) */ +int aim_reqservice(aim_session_t *sess, aim_conn_t *conn, guint16 serviceid) +{ + return aim_genericreq_s(sess, conn, 0x0001, 0x0004, &serviceid); +} + +/* Redirect (group 1, type 5) */ +static int redirect(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + struct aim_redirect_data redir; + aim_rxcallback_t userfunc; + aim_tlvlist_t *tlvlist; + aim_snac_t *origsnac = NULL; + int ret = 0; + + memset(&redir, 0, sizeof(redir)); + + tlvlist = aim_readtlvchain(bs); + + if (!aim_gettlv(tlvlist, 0x000d, 1) || + !aim_gettlv(tlvlist, 0x0005, 1) || + !aim_gettlv(tlvlist, 0x0006, 1)) { + aim_freetlvchain(&tlvlist); + return 0; + } + + redir.group = aim_gettlv16(tlvlist, 0x000d, 1); + redir.ip = aim_gettlv_str(tlvlist, 0x0005, 1); + redir.cookie = (guint8 *)aim_gettlv_str(tlvlist, 0x0006, 1); + + /* Fetch original SNAC so we can get csi if needed */ + origsnac = aim_remsnac(sess, snac->id); + + if ((redir.group == AIM_CONN_TYPE_CHAT) && origsnac) { + struct chatsnacinfo *csi = (struct chatsnacinfo *)origsnac->data; + + redir.chat.exchange = csi->exchange; + redir.chat.room = csi->name; + redir.chat.instance = csi->instance; + } + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, &redir); + + g_free((void *)redir.ip); + g_free((void *)redir.cookie); + + if (origsnac) + g_free(origsnac->data); + g_free(origsnac); + + aim_freetlvchain(&tlvlist); + + return ret; +} + +/* Request Rate Information. (group 1, type 6) */ +int aim_reqrates(aim_session_t *sess, aim_conn_t *conn) +{ + return aim_genericreq_n(sess, conn, 0x0001, 0x0006); +} + +/* + * OSCAR defines several 'rate classes'. Each class has seperate + * rate limiting properties (limit level, alert level, disconnect + * level, etc), and a set of SNAC family/type pairs associated with + * it. The rate classes, their limiting properties, and the definitions + * of which SNACs are belong to which class, are defined in the + * Rate Response packet at login to each host. + * + * Logically, all rate offenses within one class count against further + * offenses for other SNACs in the same class (ie, sending messages + * too fast will limit the number of user info requests you can send, + * since those two SNACs are in the same rate class). + * + * Since the rate classes are defined dynamically at login, the values + * below may change. But they seem to be fairly constant. + * + * Currently, BOS defines five rate classes, with the commonly used + * members as follows... + * + * Rate class 0x0001: + * - Everything thats not in any of the other classes + * + * Rate class 0x0002: + * - Buddy list add/remove + * - Permit list add/remove + * - Deny list add/remove + * + * Rate class 0x0003: + * - User information requests + * - Outgoing ICBMs + * + * Rate class 0x0004: + * - A few unknowns: 2/9, 2/b, and f/2 + * + * Rate class 0x0005: + * - Chat room create + * - Outgoing chat ICBMs + * + * The only other thing of note is that class 5 (chat) has slightly looser + * limiting properties than class 3 (normal messages). But thats just a + * small bit of trivia for you. + * + * The last thing that needs to be learned about the rate limiting + * system is how the actual numbers relate to the passing of time. This + * seems to be a big mystery. + * + */ + +static void rc_addclass(struct rateclass **head, struct rateclass *inrc) +{ + struct rateclass *rc, *rc2; + + if (!(rc = g_malloc(sizeof(struct rateclass)))) + return; + + memcpy(rc, inrc, sizeof(struct rateclass)); + rc->next = NULL; + + for (rc2 = *head; rc2 && rc2->next; rc2 = rc2->next) + ; + + if (!rc2) + *head = rc; + else + rc2->next = rc; + + return; +} + +static struct rateclass *rc_findclass(struct rateclass **head, guint16 id) +{ + struct rateclass *rc; + + for (rc = *head; rc; rc = rc->next) { + if (rc->classid == id) + return rc; + } + + return NULL; +} + +static void rc_addpair(struct rateclass *rc, guint16 group, guint16 type) +{ + struct snacpair *sp, *sp2; + + if (!(sp = g_new0(struct snacpair, 1))) + return; + + sp->group = group; + sp->subtype = type; + sp->next = NULL; + + for (sp2 = rc->members; sp2 && sp2->next; sp2 = sp2->next) + ; + + if (!sp2) + rc->members = sp; + else + sp2->next = sp; + + return; +} + +/* Rate Parameters (group 1, type 7) */ +static int rateresp(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_conn_inside_t *ins = (aim_conn_inside_t *)rx->conn->inside; + guint16 numclasses, i; + aim_rxcallback_t userfunc; + + + /* + * First are the parameters for each rate class. + */ + numclasses = aimbs_get16(bs); + for (i = 0; i < numclasses; i++) { + struct rateclass rc; + + memset(&rc, 0, sizeof(struct rateclass)); + + rc.classid = aimbs_get16(bs); + rc.windowsize = aimbs_get32(bs); + rc.clear = aimbs_get32(bs); + rc.alert = aimbs_get32(bs); + rc.limit = aimbs_get32(bs); + rc.disconnect = aimbs_get32(bs); + rc.current = aimbs_get32(bs); + rc.max = aimbs_get32(bs); + + /* + * The server will send an extra five bytes of parameters + * depending on the version we advertised in 1/17. If we + * didn't send 1/17 (evil!), then this will crash and you + * die, as it will default to the old version but we have + * the new version hardcoded here. + */ + if (mod->version >= 3) + aimbs_getrawbuf(bs, rc.unknown, sizeof(rc.unknown)); + + rc_addclass(&ins->rates, &rc); + } + + /* + * Then the members of each class. + */ + for (i = 0; i < numclasses; i++) { + guint16 classid, count; + struct rateclass *rc; + int j; + + classid = aimbs_get16(bs); + count = aimbs_get16(bs); + + rc = rc_findclass(&ins->rates, classid); + + for (j = 0; j < count; j++) { + guint16 group, subtype; + + group = aimbs_get16(bs); + subtype = aimbs_get16(bs); + + if (rc) + rc_addpair(rc, group, subtype); + } + } + + /* + * We don't pass the rate information up to the client, as it really + * doesn't care. The information is stored in the connection, however + * so that we can do more fun stuff later (not really). + */ + + /* + * Last step in the conn init procedure is to acknowledge that we + * agree to these draconian limitations. + */ + aim_rates_addparam(sess, rx->conn); + + /* + * Finally, tell the client it's ready to go... + */ + if ((userfunc = aim_callhandler(sess, rx->conn, AIM_CB_FAM_SPECIAL, AIM_CB_SPECIAL_CONNINITDONE))) + userfunc(sess, rx); + + + return 1; +} + +/* Add Rate Parameter (group 1, type 8) */ +int aim_rates_addparam(aim_session_t *sess, aim_conn_t *conn) +{ + aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside; + aim_frame_t *fr; + aim_snacid_t snacid; + struct rateclass *rc; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 512))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0001, 0x0008, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0001, 0x0008, 0x0000, snacid); + + for (rc = ins->rates; rc; rc = rc->next) + aimbs_put16(&fr->data, rc->classid); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* Delete Rate Parameter (group 1, type 9) */ +int aim_rates_delparam(aim_session_t *sess, aim_conn_t *conn) +{ + aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside; + aim_frame_t *fr; + aim_snacid_t snacid; + struct rateclass *rc; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 512))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0001, 0x0009, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0001, 0x0009, 0x0000, snacid); + + for (rc = ins->rates; rc; rc = rc->next) + aimbs_put16(&fr->data, rc->classid); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* Rate Change (group 1, type 0x0a) */ +static int ratechange(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_rxcallback_t userfunc; + guint16 code, rateclass; + guint32 currentavg, maxavg, windowsize, clear, alert, limit, disconnect; + + code = aimbs_get16(bs); + rateclass = aimbs_get16(bs); + + windowsize = aimbs_get32(bs); + clear = aimbs_get32(bs); + alert = aimbs_get32(bs); + limit = aimbs_get32(bs); + disconnect = aimbs_get32(bs); + currentavg = aimbs_get32(bs); + maxavg = aimbs_get32(bs); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + return userfunc(sess, rx, code, rateclass, windowsize, clear, alert, limit, disconnect, currentavg, maxavg); + + return 0; +} + +/* + * How Migrations work. + * + * The server sends a Server Pause message, which the client should respond to + * with a Server Pause Ack, which contains the families it needs on this + * connection. The server will send a Migration Notice with an IP address, and + * then disconnect. Next the client should open the connection and send the + * cookie. Repeat the normal login process and pretend this never happened. + * + * The Server Pause contains no data. + * + */ + +/* Service Pause (group 1, type 0x0b) */ +static int serverpause(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_rxcallback_t userfunc; + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + return userfunc(sess, rx); + + return 0; +} + +/* + * Service Pause Acknowledgement (group 1, type 0x0c) + * + * It is rather important that aim_sendpauseack() gets called for the exact + * same connection that the Server Pause callback was called for, since + * libfaim extracts the data for the SNAC from the connection structure. + * + * Of course, if you don't do that, more bad things happen than just what + * libfaim can cause. + * + */ +int aim_sendpauseack(aim_session_t *sess, aim_conn_t *conn) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside; + struct snacgroup *sg; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1024))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0001, 0x000c, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0001, 0x000c, 0x0000, snacid); + + /* + * This list should have all the groups that the original + * Host Online / Server Ready said this host supports. And + * we want them all back after the migration. + */ + for (sg = ins->groups; sg; sg = sg->next) + aimbs_put16(&fr->data, sg->group); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* Service Resume (group 1, type 0x0d) */ +static int serverresume(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_rxcallback_t userfunc; + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + return userfunc(sess, rx); + + return 0; +} + +/* Request self-info (group 1, type 0x0e) */ +int aim_reqpersonalinfo(aim_session_t *sess, aim_conn_t *conn) +{ + return aim_genericreq_n(sess, conn, 0x0001, 0x000e); +} + +/* Self User Info (group 1, type 0x0f) */ +static int selfinfo(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_rxcallback_t userfunc; + aim_userinfo_t userinfo; + + aim_extractuserinfo(sess, bs, &userinfo); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + return userfunc(sess, rx, &userinfo); + + return 0; +} + +/* Evil Notification (group 1, type 0x10) */ +static int evilnotify(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_rxcallback_t userfunc; + guint16 newevil; + aim_userinfo_t userinfo; + + memset(&userinfo, 0, sizeof(aim_userinfo_t)); + + newevil = aimbs_get16(bs); + + if (aim_bstream_empty(bs)) + aim_extractuserinfo(sess, bs, &userinfo); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + return userfunc(sess, rx, newevil, &userinfo); + + return 0; +} + +/* + * Idle Notification (group 1, type 0x11) + * + * Should set your current idle time in seconds. Note that this should + * never be called consecutively with a non-zero idle time. That makes + * OSCAR do funny things. Instead, just set it once you go idle, and then + * call it again with zero when you're back. + * + */ +int aim_bos_setidle(aim_session_t *sess, aim_conn_t *conn, guint32 idletime) +{ + return aim_genericreq_l(sess, conn, 0x0001, 0x0011, &idletime); +} + +/* + * Service Migrate (group 1, type 0x12) + * + * This is the final SNAC sent on the original connection during a migration. + * It contains the IP and cookie used to connect to the new server, and + * optionally a list of the SNAC groups being migrated. + * + */ +static int migrate(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_rxcallback_t userfunc; + int ret = 0; + guint16 groupcount, i; + aim_tlvlist_t *tl; + char *ip = NULL; + aim_tlv_t *cktlv; + + /* + * Apparently there's some fun stuff that can happen right here. The + * migration can actually be quite selective about what groups it + * moves to the new server. When not all the groups for a connection + * are migrated, or they are all migrated but some groups are moved + * to a different server than others, it is called a bifurcated + * migration. + * + * Let's play dumb and not support that. + * + */ + groupcount = aimbs_get16(bs); + for (i = 0; i < groupcount; i++) { + guint16 group; + + group = aimbs_get16(bs); + + do_error_dialog(sess->aux_data, "bifurcated migration unsupported", "Gaim"); + } + + tl = aim_readtlvchain(bs); + + if (aim_gettlv(tl, 0x0005, 1)) + ip = aim_gettlv_str(tl, 0x0005, 1); + + cktlv = aim_gettlv(tl, 0x0006, 1); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, ip, cktlv ? cktlv->value : NULL); + + aim_freetlvchain(&tl); + g_free(ip); + + return ret; +} + +/* Message of the Day (group 1, type 0x13) */ +static int motd(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_rxcallback_t userfunc; + char *msg = NULL; + int ret = 0; + aim_tlvlist_t *tlvlist; + guint16 id; + + /* + * Code. + * + * Valid values: + * 1 Mandatory upgrade + * 2 Advisory upgrade + * 3 System bulletin + * 4 Nothing's wrong ("top o the world" -- normal) + * 5 Lets-break-something. + * + */ + id = aimbs_get16(bs); + + /* + * TLVs follow + */ + tlvlist = aim_readtlvchain(bs); + + msg = aim_gettlv_str(tlvlist, 0x000b, 1); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, id, msg); + + g_free(msg); + + aim_freetlvchain(&tlvlist); + + return ret; +} + +/* + * Set privacy flags (group 1, type 0x14) + * + * Normally 0x03. + * + * Bit 1: Allows other AIM users to see how long you've been idle. + * Bit 2: Allows other AIM users to see how long you've been a member. + * + */ +int aim_bos_setprivacyflags(aim_session_t *sess, aim_conn_t *conn, guint32 flags) +{ + return aim_genericreq_l(sess, conn, 0x0001, 0x0014, &flags); +} + +/* + * No-op (group 1, type 0x16) + * + * WinAIM sends these every 4min or so to keep the connection alive. Its not + * real necessary. + * + */ +int aim_nop(aim_session_t *sess, aim_conn_t *conn) +{ + return aim_genericreq_n(sess, conn, 0x0001, 0x0016); +} + +/* + * Set client versions (group 1, subtype 0x17) + * + * If you've seen the clientonline/clientready SNAC you're probably + * wondering what the point of this one is. And that point seems to be + * that the versions in the client online SNAC are sent too late for the + * server to be able to use them to change the protocol for the earlier + * login packets (client versions are sent right after Host Online is + * received, but client online versions aren't sent until quite a bit later). + * We can see them already making use of this by changing the format of + * the rate information based on what version of group 1 we advertise here. + * + */ +int aim_setversions(aim_session_t *sess, aim_conn_t *conn) +{ + aim_conn_inside_t *ins = (aim_conn_inside_t *)conn->inside; + struct snacgroup *sg; + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!ins) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 1152))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0001, 0x0017, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0001, 0x0017, 0x0000, snacid); + + /* + * Send only the versions that the server cares about (that it + * marked as supporting in the server ready SNAC). + */ + for (sg = ins->groups; sg; sg = sg->next) { + aim_module_t *mod; + + if ((mod = aim__findmodulebygroup(sess, sg->group))) { + aimbs_put16(&fr->data, mod->family); + aimbs_put16(&fr->data, mod->version); + } + } + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* Host versions (group 1, subtype 0x18) */ +static int hostversions(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int vercount; + guint8 *versions; + + /* This is frivolous. (Thank you SmarterChild.) */ + vercount = aim_bstream_empty(bs)/4; + versions = aimbs_getraw(bs, aim_bstream_empty(bs)); + g_free(versions); + + /* + * Now request rates. + */ + aim_reqrates(sess, rx->conn); + + return 1; +} + +/* + * Subtype 0x001e - Extended Status + * + * Sets your ICQ status (available, away, do not disturb, etc.) + * + * These are the same TLVs seen in user info. You can + * also set 0x0008 and 0x000c. + */ +int aim_setextstatus(aim_session_t *sess, aim_conn_t *conn, guint32 status) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + aim_tlvlist_t *tl = NULL; + guint32 data; + int tlvlen; + + data = AIM_ICQ_STATE_WEBAWARE | AIM_ICQ_STATE_HIDEIP | status; /* yay for error checking ;^) */ + + tlvlen = aim_addtlvtochain32(&tl, 0x0006, data); + + printf("%d\n", tlvlen); + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10 + 8))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0001, 0x001e, 0x0000, NULL, 0); + aim_putsnac(&fr->data, 0x0001, 0x001e, 0x0000, snacid); + + aim_writetlvchain(&fr->data, &tl); + aim_freetlvchain(&tl); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * Starting this past week (26 Mar 2001, say), AOL has started sending + * this nice little extra SNAC. AFAIK, it has never been used until now. + * + * The request contains eight bytes. The first four are an offset, the + * second four are a length. + * + * The offset is an offset into aim.exe when it is mapped during execution + * on Win32. So far, AOL has only been requesting bytes in static regions + * of memory. (I won't put it past them to start requesting data in + * less static regions -- regions that are initialized at run time, but still + * before the client recieves this request.) + * + * When the client recieves the request, it adds it to the current ds + * (0x00400000) and dereferences it, copying the data into a buffer which + * it then runs directly through the MD5 hasher. The 16 byte output of + * the hash is then sent back to the server. + * + * If the client does not send any data back, or the data does not match + * the data that the specific client should have, the client will get the + * following message from "AOL Instant Messenger": + * "You have been disconnected from the AOL Instant Message Service (SM) + * for accessing the AOL network using unauthorized software. You can + * download a FREE, fully featured, and authorized client, here + * http://www.aol.com/aim/download2.html" + * The connection is then closed, recieving disconnect code 1, URL + * http://www.aim.aol.com/errors/USER_LOGGED_OFF_NEW_LOGIN.html. + * + * Note, however, that numerous inconsistencies can cause the above error, + * not just sending back a bad hash. Do not immediatly suspect this code + * if you get disconnected. AOL and the open/free software community have + * played this game for a couple years now, generating the above message + * on numerous ocassions. + * + * Anyway, neener. We win again. + * + */ +/* Client verification (group 1, subtype 0x1f) */ +static int memrequest(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + aim_rxcallback_t userfunc; + guint32 offset, len; + aim_tlvlist_t *list; + char *modname; + + offset = aimbs_get32(bs); + len = aimbs_get32(bs); + list = aim_readtlvchain(bs); + + modname = aim_gettlv_str(list, 0x0001, 1); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + return userfunc(sess, rx, offset, len, modname); + + g_free(modname); + aim_freetlvchain(&list); + + return 0; +} + +/* Client verification reply (group 1, subtype 0x20) */ +int aim_sendmemblock(aim_session_t *sess, aim_conn_t *conn, guint32 offset, guint32 len, const guint8 *buf, guint8 flag) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !conn) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+2+16))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, 0x0001, 0x0020, 0x0000, NULL, 0); + + aim_putsnac(&fr->data, 0x0001, 0x0020, 0x0000, snacid); + aimbs_put16(&fr->data, 0x0010); /* md5 is always 16 bytes */ + + if ((flag == AIM_SENDMEMBLOCK_FLAG_ISHASH) && buf && (len == 0x10)) { /* we're getting a hash */ + + aimbs_putraw(&fr->data, buf, 0x10); + + } else if (buf && (len > 0)) { /* use input buffer */ + md5_state_t state; + md5_byte_t digest[0x10]; + + md5_init(&state); + md5_append(&state, (const md5_byte_t *)buf, len); + md5_finish(&state, digest); + + aimbs_putraw(&fr->data, (guint8 *)digest, 0x10); + + } else if (len == 0) { /* no length, just hash NULL (buf is optional) */ + md5_state_t state; + guint8 nil = '\0'; + md5_byte_t digest[0x10]; + + /* + * These MD5 routines are stupid in that you have to have + * at least one append. So thats why this doesn't look + * real logical. + */ + md5_init(&state); + md5_append(&state, (const md5_byte_t *)&nil, 0); + md5_finish(&state, digest); + + aimbs_putraw(&fr->data, (guint8 *)digest, 0x10); + + } else { + + /* + * This data is correct for AIM 3.5.1670. + * + * Using these blocks is as close to "legal" as you can get + * without using an AIM binary. + * + */ + if ((offset == 0x03ffffff) && (len == 0x03ffffff)) { + +#if 1 /* with "AnrbnrAqhfzcd" */ + aimbs_put32(&fr->data, 0x44a95d26); + aimbs_put32(&fr->data, 0xd2490423); + aimbs_put32(&fr->data, 0x93b8821f); + aimbs_put32(&fr->data, 0x51c54b01); +#else /* no filename */ + aimbs_put32(&fr->data, 0x1df8cbae); + aimbs_put32(&fr->data, 0x5523b839); + aimbs_put32(&fr->data, 0xa0e10db3); + aimbs_put32(&fr->data, 0xa46d3b39); +#endif + + } else if ((offset == 0x00001000) && (len == 0x00000000)) { + + aimbs_put32(&fr->data, 0xd41d8cd9); + aimbs_put32(&fr->data, 0x8f00b204); + aimbs_put32(&fr->data, 0xe9800998); + aimbs_put32(&fr->data, 0xecf8427e); + + } else + do_error_dialog(sess->aux_data, "WARNING: unknown hash request", "Gaim"); + + } + + aim_tx_enqueue(sess, fr); + + return 0; +} + +static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + + if (snac->subtype == 0x0003) + return hostonline(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0005) + return redirect(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0007) + return rateresp(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x000a) + return ratechange(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x000b) + return serverpause(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x000d) + return serverresume(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x000f) + return selfinfo(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0010) + return evilnotify(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0012) + return migrate(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0013) + return motd(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x0018) + return hostversions(sess, mod, rx, snac, bs); + else if (snac->subtype == 0x001f) + return memrequest(sess, mod, rx, snac, bs); + + return 0; +} + +int general_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x0001; + mod->version = 0x0003; + mod->toolid = 0x0110; + mod->toolversion = 0x0629; + mod->flags = 0; + strncpy(mod->name, "general", sizeof(mod->name)); + mod->snachandler = snachandler; + + return 0; +} + diff --git a/protocols/oscar/snac.c b/protocols/oscar/snac.c new file mode 100644 index 00000000..e2bac179 --- /dev/null +++ b/protocols/oscar/snac.c @@ -0,0 +1,145 @@ +/* + * + * Various SNAC-related dodads... + * + * outstanding_snacs is a list of aim_snac_t structs. A SNAC should be added + * whenever a new SNAC is sent and it should remain in the list until the + * response for it has been receieved. + * + * cleansnacs() should be called periodically by the client in order + * to facilitate the aging out of unreplied-to SNACs. This can and does + * happen, so it should be handled. + * + */ + +#include <aim.h> + +/* + * Called from aim_session_init() to initialize the hash. + */ +void aim_initsnachash(aim_session_t *sess) +{ + int i; + + for (i = 0; i < AIM_SNAC_HASH_SIZE; i++) + sess->snac_hash[i] = NULL; + + return; +} + +aim_snacid_t aim_cachesnac(aim_session_t *sess, const guint16 family, const guint16 type, const guint16 flags, const void *data, const int datalen) +{ + aim_snac_t snac; + + snac.id = sess->snacid_next++; + snac.family = family; + snac.type = type; + snac.flags = flags; + + if (datalen) { + if (!(snac.data = g_malloc(datalen))) + return 0; /* er... */ + memcpy(snac.data, data, datalen); + } else + snac.data = NULL; + + return aim_newsnac(sess, &snac); +} + +/* + * Clones the passed snac structure and caches it in the + * list/hash. + */ +aim_snacid_t aim_newsnac(aim_session_t *sess, aim_snac_t *newsnac) +{ + aim_snac_t *snac; + int index; + + if (!newsnac) + return 0; + + if (!(snac = g_malloc(sizeof(aim_snac_t)))) + return 0; + memcpy(snac, newsnac, sizeof(aim_snac_t)); + snac->issuetime = time(NULL); + + index = snac->id % AIM_SNAC_HASH_SIZE; + + snac->next = (aim_snac_t *)sess->snac_hash[index]; + sess->snac_hash[index] = (void *)snac; + + return snac->id; +} + +/* + * Finds a snac structure with the passed SNAC ID, + * removes it from the list/hash, and returns a pointer to it. + * + * The returned structure must be freed by the caller. + * + */ +aim_snac_t *aim_remsnac(aim_session_t *sess, aim_snacid_t id) +{ + aim_snac_t *cur, **prev; + int index; + + index = id % AIM_SNAC_HASH_SIZE; + + for (prev = (aim_snac_t **)&sess->snac_hash[index]; (cur = *prev); ) { + if (cur->id == id) { + *prev = cur->next; + return cur; + } else + prev = &cur->next; + } + + return cur; +} + +/* + * This is for cleaning up old SNACs that either don't get replies or + * a reply was never received for. Garabage collection. Plain and simple. + * + * maxage is the _minimum_ age in seconds to keep SNACs. + * + */ +void aim_cleansnacs(aim_session_t *sess, int maxage) +{ + int i; + + for (i = 0; i < AIM_SNAC_HASH_SIZE; i++) { + aim_snac_t *cur, **prev; + time_t curtime; + + if (!sess->snac_hash[i]) + continue; + + curtime = time(NULL); /* done here in case we waited for the lock */ + + for (prev = (aim_snac_t **)&sess->snac_hash[i]; (cur = *prev); ) { + if ((curtime - cur->issuetime) > maxage) { + + *prev = cur->next; + + /* XXX should we have destructors here? */ + g_free(cur->data); + g_free(cur); + + } else + prev = &cur->next; + } + } + + return; +} + +int aim_putsnac(aim_bstream_t *bs, guint16 family, guint16 subtype, guint16 flags, aim_snacid_t snacid) +{ + + aimbs_put16(bs, family); + aimbs_put16(bs, subtype); + aimbs_put16(bs, flags); + aimbs_put32(bs, snacid); + + return 10; +} diff --git a/protocols/oscar/ssi.c b/protocols/oscar/ssi.c new file mode 100644 index 00000000..76b5b427 --- /dev/null +++ b/protocols/oscar/ssi.c @@ -0,0 +1,1523 @@ +/* + * Server-Side/Stored Information. + * + * Relatively new facility that allows storing of certain types of information, + * such as a users buddy list, permit/deny list, and permit/deny preferences, + * to be stored on the server, so that they can be accessed from any client. + * + * We keep a copy of the ssi data in sess->ssi, because the data needs to be + * accessed for various reasons. So all the "aim_ssi_itemlist_bleh" functions + * near the top just manage the local data. + * + * The SNAC sending and receiving functions are lower down in the file, and + * they're simpler. They are in the order of the subtypes they deal with, + * starting with the request rights function (subtype 0x0002), then parse + * rights (subtype 0x0003), then--well, you get the idea. + * + * This is entirely too complicated. + * You don't know the half of it. + * + * XXX - Test for memory leaks + * XXX - Better parsing of rights, and use the rights info to limit adds + * + */ + +#include <aim.h> +#include "ssi.h" + +/** + * Locally add a new item to the given item list. + * + * @param list A pointer to a pointer to the current list of items. + * @param parent A pointer to the parent group, or NULL if the item should have no + * parent group (ie. the group ID# should be 0). + * @param name A null terminated string of the name of the new item, or NULL if the + * item should have no name. + * @param type The type of the item, 0x0001 for a contact, 0x0002 for a group, etc. + * @return The newly created item. + */ +static struct aim_ssi_item *aim_ssi_itemlist_add(struct aim_ssi_item **list, struct aim_ssi_item *parent, char *name, guint16 type) +{ + int i; + struct aim_ssi_item *cur, *newitem; + + if (!(newitem = g_new0(struct aim_ssi_item, 1))) + return NULL; + + /* Set the name */ + if (name) { + if (!(newitem->name = (char *)g_malloc((strlen(name)+1)*sizeof(char)))) { + g_free(newitem); + return NULL; + } + strcpy(newitem->name, name); + } else + newitem->name = NULL; + + /* Set the group ID# and the buddy ID# */ + newitem->gid = 0x0000; + newitem->bid = 0x0000; + if (type == AIM_SSI_TYPE_GROUP) { + if (name) + do { + newitem->gid += 0x0001; + for (cur=*list, i=0; ((cur) && (!i)); cur=cur->next) + if ((cur->gid == newitem->gid) && (cur->gid == newitem->gid)) + i=1; + } while (i); + } else { + if (parent) + newitem->gid = parent->gid; + do { + newitem->bid += 0x0001; + for (cur=*list, i=0; ((cur) && (!i)); cur=cur->next) + if ((cur->bid == newitem->bid) && (cur->gid == newitem->gid)) + i=1; + } while (i); + } + + /* Set the rest */ + newitem->type = type; + newitem->data = NULL; + newitem->next = *list; + *list = newitem; + + return newitem; +} + +/** + * Locally rebuild the 0x00c8 TLV in the additional data of the given group. + * + * @param list A pointer to a pointer to the current list of items. + * @param parentgroup A pointer to the group who's additional data you want to rebuild. + * @return Return 0 if no errors, otherwise return the error number. + */ +static int aim_ssi_itemlist_rebuildgroup(struct aim_ssi_item **list, struct aim_ssi_item *parentgroup) +{ + int newlen; //, i; + struct aim_ssi_item *cur; + + /* Free the old additional data */ + if (parentgroup->data) { + aim_freetlvchain((aim_tlvlist_t **)&parentgroup->data); + parentgroup->data = NULL; + } + + /* Find the length for the new additional data */ + newlen = 0; + if (parentgroup->gid == 0x0000) { + for (cur=*list; cur; cur=cur->next) + if ((cur->gid != 0x0000) && (cur->type == AIM_SSI_TYPE_GROUP)) + newlen += 2; + } else { + for (cur=*list; cur; cur=cur->next) + if ((cur->gid == parentgroup->gid) && (cur->type == AIM_SSI_TYPE_BUDDY)) + newlen += 2; + } + + /* Rebuild the additional data */ + if (newlen>0) { + guint8 *newdata; + + if (!(newdata = (guint8 *)g_malloc((newlen)*sizeof(guint8)))) + return -ENOMEM; + newlen = 0; + if (parentgroup->gid == 0x0000) { + for (cur=*list; cur; cur=cur->next) + if ((cur->gid != 0x0000) && (cur->type == AIM_SSI_TYPE_GROUP)) + newlen += aimutil_put16(newdata+newlen, cur->gid); + } else { + for (cur=*list; cur; cur=cur->next) + if ((cur->gid == parentgroup->gid) && (cur->type == AIM_SSI_TYPE_BUDDY)) + newlen += aimutil_put16(newdata+newlen, cur->bid); + } + aim_addtlvtochain_raw((aim_tlvlist_t **)&(parentgroup->data), 0x00c8, newlen, newdata); + + g_free(newdata); + } + + return 0; +} + +/** + * Locally free all of the stored buddy list information. + * + * @param sess The oscar session. + * @return Return 0 if no errors, otherwise return the error number. + */ +static int aim_ssi_freelist(aim_session_t *sess) +{ + struct aim_ssi_item *cur, *delitem; + + cur = sess->ssi.items; + while (cur) { + if (cur->name) g_free(cur->name); + if (cur->data) aim_freetlvchain((aim_tlvlist_t **)&cur->data); + delitem = cur; + cur = cur->next; + g_free(delitem); + } + + sess->ssi.items = NULL; + sess->ssi.revision = 0; + sess->ssi.timestamp = (time_t)0; + + return 0; +} + +/** + * Locally find an item given a group ID# and a buddy ID#. + * + * @param list A pointer to the current list of items. + * @param gid The group ID# of the desired item. + * @param bid The buddy ID# of the desired item. + * @return Return a pointer to the item if found, else return NULL; + */ +struct aim_ssi_item *aim_ssi_itemlist_find(struct aim_ssi_item *list, guint16 gid, guint16 bid) +{ + struct aim_ssi_item *cur; + for (cur=list; cur; cur=cur->next) + if ((cur->gid == gid) && (cur->bid == bid)) + return cur; + return NULL; +} + +/** + * Locally find an item given a group name, screen name, and type. If group name + * and screen name are null, then just return the first item of the given type. + * + * @param list A pointer to the current list of items. + * @param gn The group name of the desired item. + * @param bn The buddy name of the desired item. + * @param type The type of the desired item. + * @return Return a pointer to the item if found, else return NULL; + */ +struct aim_ssi_item *aim_ssi_itemlist_finditem(struct aim_ssi_item *list, char *gn, char *sn, guint16 type) +{ + struct aim_ssi_item *cur; + if (!list) + return NULL; + + if (gn && sn) { /* For finding buddies in groups */ + for (cur=list; cur; cur=cur->next) + if ((cur->type == type) && (cur->name) && !(aim_sncmp(cur->name, sn))) { + struct aim_ssi_item *curg; + for (curg=list; curg; curg=curg->next) + if ((curg->type == AIM_SSI_TYPE_GROUP) && (curg->gid == cur->gid) && (curg->name) && !(aim_sncmp(curg->name, gn))) + return cur; + } + + } else if (sn) { /* For finding groups, permits, denies, and ignores */ + for (cur=list; cur; cur=cur->next) + if ((cur->type == type) && (cur->name) && !(aim_sncmp(cur->name, sn))) + return cur; + + /* For stuff without names--permit deny setting, visibility mask, etc. */ + } else for (cur=list; cur; cur=cur->next) { + if (cur->type == type) + return cur; + } + + return NULL; +} + +/** + * Locally find the parent item of the given buddy name. + * + * @param list A pointer to the current list of items. + * @param bn The buddy name of the desired item. + * @return Return a pointer to the item if found, else return NULL; + */ +struct aim_ssi_item *aim_ssi_itemlist_findparent(struct aim_ssi_item *list, char *sn) +{ + struct aim_ssi_item *cur, *curg; + if (!list || !sn) + return NULL; + if (!(cur = aim_ssi_itemlist_finditem(list, NULL, sn, AIM_SSI_TYPE_BUDDY))) + return NULL; + for (curg=list; curg; curg=curg->next) + if ((curg->type == AIM_SSI_TYPE_GROUP) && (curg->gid == cur->gid)) + return curg; + return NULL; +} + +/** + * Locally find the permit/deny setting item, and return the setting. + * + * @param list A pointer to the current list of items. + * @return Return the current SSI permit deny setting, or 0 if no setting was found. + */ +int aim_ssi_getpermdeny(struct aim_ssi_item *list) +{ + struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, NULL, NULL, AIM_SSI_TYPE_PDINFO); + if (cur) { + aim_tlvlist_t *tlvlist = cur->data; + if (tlvlist) { + aim_tlv_t *tlv = aim_gettlv(tlvlist, 0x00ca, 1); + if (tlv && tlv->value) + return aimutil_get8(tlv->value); + } + } + return 0; +} + +/** + * Locally find the presence flag item, and return the setting. The returned setting is a + * bitmask of the user flags that you are visible to. See the AIM_FLAG_* #defines + * in aim.h + * + * @param list A pointer to the current list of items. + * @return Return the current visibility mask. + */ +guint32 aim_ssi_getpresence(struct aim_ssi_item *list) +{ + struct aim_ssi_item *cur = aim_ssi_itemlist_finditem(list, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS); + if (cur) { + aim_tlvlist_t *tlvlist = cur->data; + if (tlvlist) { + aim_tlv_t *tlv = aim_gettlv(tlvlist, 0x00c9, 1); + if (tlv && tlv->length) + return aimutil_get32(tlv->value); + } + } + return 0xFFFFFFFF; +} + +/** + * Add the given packet to the holding queue. We totally need to send SSI SNACs one at + * a time, so we have a local queue where packets get put before they are sent, and + * then we send stuff one at a time, nice and orderly-like. + * + * @param sess The oscar session. + * @param conn The bos connection for this session. + * @param fr The newly created SNAC that you want to send. + * @return Return 0 if no errors, otherwise return the error number. + */ +static int aim_ssi_enqueue(aim_session_t *sess, aim_conn_t *conn, aim_frame_t *fr) +{ + aim_frame_t *cur; + + if (!sess || !conn || !fr) + return -EINVAL; + + fr->next = NULL; + if (sess->ssi.holding_queue == NULL) { + sess->ssi.holding_queue = fr; + if (!sess->ssi.waiting_for_ack) + aim_ssi_modbegin(sess, conn); + } else { + for (cur = sess->ssi.holding_queue; cur->next; cur = cur->next) ; + cur->next = fr; + } + + return 0; +} + +/** + * Send the next SNAC from the holding queue. This is called + * automatically when an ack from an add, mod, or del is received. + * If the queue is empty, it sends the modend SNAC. + * + * @param sess The oscar session. + * @param conn The bos connection for this session. + * @return Return 0 if no errors, otherwise return the error number. + */ +static int aim_ssi_dispatch(aim_session_t *sess, aim_conn_t *conn) +{ + aim_frame_t *cur; + + if (!sess || !conn) + return -EINVAL; + + if (!sess->ssi.waiting_for_ack) { + if (sess->ssi.holding_queue) { + sess->ssi.waiting_for_ack = 1; + cur = sess->ssi.holding_queue->next; + sess->ssi.holding_queue->next = NULL; + aim_tx_enqueue(sess, sess->ssi.holding_queue); + sess->ssi.holding_queue = cur; + } else + aim_ssi_modend(sess, conn); + } + + return 0; +} + +/** + * Send SNACs necessary to remove all SSI data from the server list, + * and then free the local copy as well. + * + * @param sess The oscar session. + * @param conn The bos connection for this session. + * @return Return 0 if no errors, otherwise return the error number. + */ +int aim_ssi_deletelist(aim_session_t *sess, aim_conn_t *conn) +{ + int num; + struct aim_ssi_item *cur, **items; + + for (cur=sess->ssi.items, num=0; cur; cur=cur->next) + num++; + + if (!(items = g_new0(struct aim_ssi_item *, num))) + return -ENOMEM; + + for (cur=sess->ssi.items, num=0; cur; cur=cur->next) { + items[num] = cur; + num++; + } + + aim_ssi_addmoddel(sess, conn, items, num, AIM_CB_SSI_DEL); + g_free(items); + aim_ssi_dispatch(sess, conn); + aim_ssi_freelist(sess); + + return 0; +} + +/** + * This "cleans" the ssi list. It does a few things, with the intent of making + * sure there ain't nothin' wrong with your SSI. + * -Make sure all buddies are in a group, and all groups have the correct + * additional data. + * -Make sure there are no empty groups in the list. While there is nothing + * wrong empty groups in the SSI, it's wiser to not have them. + * + * @param sess The oscar session. + * @param conn The bos connection for this session. + * @return Return 0 if no errors, otherwise return the error number. + */ +int aim_ssi_cleanlist(aim_session_t *sess, aim_conn_t *conn) +{ + unsigned int i; + struct aim_ssi_item *cur, *parentgroup; + + /* Make sure we actually need to clean out the list */ + for (cur=sess->ssi.items, i=0; cur && !i; cur=cur->next) + /* Any buddies directly in the master group */ + if ((cur->type == AIM_SSI_TYPE_BUDDY) && (cur->gid == 0x0000)) + i++; + if (!i) + return 0; + + /* Remove all the additional data from all groups */ + for (cur=sess->ssi.items; cur; cur=cur->next) + if ((cur->data) && (cur->type == AIM_SSI_TYPE_GROUP)) { + aim_freetlvchain((aim_tlvlist_t **)&cur->data); + cur->data = NULL; + } + + /* If there are buddies directly in the master group, make sure */ + /* there is a group to put them in. Any group, any group at all. */ + for (cur=sess->ssi.items; ((cur) && ((cur->type != AIM_SSI_TYPE_BUDDY) || (cur->gid != 0x0000))); cur=cur->next); + if (!cur) { + for (parentgroup=sess->ssi.items; ((parentgroup) && (parentgroup->type!=AIM_SSI_TYPE_GROUP) && (parentgroup->gid==0x0000)); parentgroup=parentgroup->next); + if (!parentgroup) { + char *newgroup; + newgroup = (char*)g_malloc(strlen("Unknown")*sizeof(char)); + strcpy(newgroup, "Unknown"); + aim_ssi_addgroups(sess, conn, &newgroup, 1); + } + } + + /* Set parentgroup equal to any arbitray group */ + for (parentgroup=sess->ssi.items; parentgroup->gid==0x0000 || parentgroup->type!=AIM_SSI_TYPE_GROUP; parentgroup=parentgroup->next); + + /* If there are any buddies directly in the master group, put them in a real group */ + for (cur=sess->ssi.items; cur; cur=cur->next) + if ((cur->type == AIM_SSI_TYPE_BUDDY) && (cur->gid == 0x0000)) { + aim_ssi_addmoddel(sess, conn, &cur, 1, AIM_CB_SSI_DEL); + cur->gid = parentgroup->gid; + aim_ssi_addmoddel(sess, conn, &cur, 1, AIM_CB_SSI_ADD); + } + + /* Rebuild additional data for all groups */ + for (parentgroup=sess->ssi.items; parentgroup; parentgroup=parentgroup->next) + if (parentgroup->type == AIM_SSI_TYPE_GROUP) + aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, parentgroup); + + /* Send a mod snac for all groups */ + i = 0; + for (cur=sess->ssi.items; cur; cur=cur->next) + if (cur->type == AIM_SSI_TYPE_GROUP) + i++; + if (i > 0) { + /* Allocate an array of pointers to each of the groups */ + struct aim_ssi_item **groups; + if (!(groups = g_new0(struct aim_ssi_item *, i))) + return -ENOMEM; + + for (cur=sess->ssi.items, i=0; cur; cur=cur->next) + if (cur->type == AIM_SSI_TYPE_GROUP) + groups[i] = cur; + + aim_ssi_addmoddel(sess, conn, groups, i, AIM_CB_SSI_MOD); + g_free(groups); + } + + /* Send a del snac for any empty groups */ + i = 0; + for (cur=sess->ssi.items; cur; cur=cur->next) + if ((cur->type == AIM_SSI_TYPE_GROUP) && !(cur->data)) + i++; + if (i > 0) { + /* Allocate an array of pointers to each of the groups */ + struct aim_ssi_item **groups; + if (!(groups = g_new0(struct aim_ssi_item *, i))) + return -ENOMEM; + + for (cur=sess->ssi.items, i=0; cur; cur=cur->next) + if ((cur->type == AIM_SSI_TYPE_GROUP) && !(cur->data)) + groups[i] = cur; + + aim_ssi_addmoddel(sess, conn, groups, i, AIM_CB_SSI_DEL); + g_free(groups); + } + + /* Begin sending SSI SNACs */ + aim_ssi_dispatch(sess, conn); + + return 0; +} + +/** + * Add an array of screen names to the given group. + * + * @param sess The oscar session. + * @param conn The bos connection for this session. + * @param gn The name of the group to which you want to add these names. + * @param sn An array of null terminated strings of the names you want to add. + * @param num The number of screen names you are adding (size of the sn array). + * @param flags 1 - Add with TLV(0x66) + * @return Return 0 if no errors, otherwise return the error number. + */ +int aim_ssi_addbuddies(aim_session_t *sess, aim_conn_t *conn, char *gn, char **sn, unsigned int num, unsigned int flags) +{ + struct aim_ssi_item *parentgroup, **newitems; + guint16 i; + + if (!sess || !conn || !gn || !sn || !num) + return -EINVAL; + + /* Look up the parent group */ + if (!(parentgroup = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, gn, AIM_SSI_TYPE_GROUP))) { + aim_ssi_addgroups(sess, conn, &gn, 1); + if (!(parentgroup = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, gn, AIM_SSI_TYPE_GROUP))) + return -ENOMEM; + } + + /* Allocate an array of pointers to each of the new items */ + if (!(newitems = g_new0(struct aim_ssi_item *, num))) + return -ENOMEM; + + /* Add items to the local list, and index them in the array */ + for (i=0; i<num; i++) + if (!(newitems[i] = aim_ssi_itemlist_add(&sess->ssi.items, parentgroup, sn[i], AIM_SSI_TYPE_BUDDY))) { + g_free(newitems); + return -ENOMEM; + } else if (flags & 1) { + aim_tlvlist_t *tl = NULL; + aim_addtlvtochain_noval(&tl, 0x66); + newitems[i]->data = tl; + } + + /* Send the add item SNAC */ + if ((i = aim_ssi_addmoddel(sess, conn, newitems, num, AIM_CB_SSI_ADD))) { + g_free(newitems); + return -i; + } + + /* Free the array of pointers to each of the new items */ + g_free(newitems); + + /* Rebuild the additional data in the parent group */ + if ((i = aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, parentgroup))) + return i; + + /* Send the mod item SNAC */ + if ((i = aim_ssi_addmoddel(sess, conn, &parentgroup, 1, AIM_CB_SSI_MOD ))) + return i; + + /* Begin sending SSI SNACs */ + if (!(i = aim_ssi_dispatch(sess, conn))) + return i; + + return 0; +} + +/** + * Add the master group (the group containing all groups). This is called by + * aim_ssi_addgroups, if necessary. + * + * @param sess The oscar session. + * @param conn The bos connection for this session. + * @return Return 0 if no errors, otherwise return the error number. + */ +int aim_ssi_addmastergroup(aim_session_t *sess, aim_conn_t *conn) +{ + struct aim_ssi_item *newitem; + + if (!sess || !conn) + return -EINVAL; + + /* Add the item to the local list, and keep a pointer to it */ + if (!(newitem = aim_ssi_itemlist_add(&sess->ssi.items, NULL, NULL, AIM_SSI_TYPE_GROUP))) + return -ENOMEM; + + /* If there are any existing groups (technically there shouldn't be, but */ + /* just in case) then add their group ID#'s to the additional data */ + aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, newitem); + + /* Send the add item SNAC */ + aim_ssi_addmoddel(sess, conn, &newitem, 1, AIM_CB_SSI_ADD); + + /* Begin sending SSI SNACs */ + aim_ssi_dispatch(sess, conn); + + return 0; +} + +/** + * Add an array of groups to the list. + * + * @param sess The oscar session. + * @param conn The bos connection for this session. + * @param gn An array of null terminated strings of the names you want to add. + * @param num The number of groups names you are adding (size of the sn array). + * @return Return 0 if no errors, otherwise return the error number. + */ +int aim_ssi_addgroups(aim_session_t *sess, aim_conn_t *conn, char **gn, unsigned int num) +{ + struct aim_ssi_item *parentgroup, **newitems; + guint16 i; + + if (!sess || !conn || !gn || !num) + return -EINVAL; + + /* Look up the parent group */ + if (!(parentgroup = aim_ssi_itemlist_find(sess->ssi.items, 0, 0))) { + aim_ssi_addmastergroup(sess, conn); + if (!(parentgroup = aim_ssi_itemlist_find(sess->ssi.items, 0, 0))) + return -ENOMEM; + } + + /* Allocate an array of pointers to each of the new items */ + if (!(newitems = g_new0(struct aim_ssi_item *, num))) + return -ENOMEM; + + /* Add items to the local list, and index them in the array */ + for (i=0; i<num; i++) + if (!(newitems[i] = aim_ssi_itemlist_add(&sess->ssi.items, parentgroup, gn[i], AIM_SSI_TYPE_GROUP))) { + g_free(newitems); + return -ENOMEM; + } + + /* Send the add item SNAC */ + if ((i = aim_ssi_addmoddel(sess, conn, newitems, num, AIM_CB_SSI_ADD))) { + g_free(newitems); + return -i; + } + + /* Free the array of pointers to each of the new items */ + g_free(newitems); + + /* Rebuild the additional data in the parent group */ + if ((i = aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, parentgroup))) + return i; + + /* Send the mod item SNAC */ + if ((i = aim_ssi_addmoddel(sess, conn, &parentgroup, 1, AIM_CB_SSI_MOD))) + return i; + + /* Begin sending SSI SNACs */ + if (!(i = aim_ssi_dispatch(sess, conn))) + return i; + + return 0; +} + +/** + * Add an array of a certain type of item to the list. This can be used for + * permit buddies, deny buddies, ICQ's ignore buddies, and probably other + * types, also. + * + * @param sess The oscar session. + * @param conn The bos connection for this session. + * @param sn An array of null terminated strings of the names you want to add. + * @param num The number of groups names you are adding (size of the sn array). + * @param type The type of item you want to add. See the AIM_SSI_TYPE_BLEH + * #defines in aim.h. + * @return Return 0 if no errors, otherwise return the error number. + */ +int aim_ssi_addpord(aim_session_t *sess, aim_conn_t *conn, char **sn, unsigned int num, guint16 type) +{ + struct aim_ssi_item **newitems; + guint16 i; + + if (!sess || !conn || !sn || !num) + return -EINVAL; + + /* Allocate an array of pointers to each of the new items */ + if (!(newitems = g_new0(struct aim_ssi_item *, num))) + return -ENOMEM; + + /* Add items to the local list, and index them in the array */ + for (i=0; i<num; i++) + if (!(newitems[i] = aim_ssi_itemlist_add(&sess->ssi.items, NULL, sn[i], type))) { + g_free(newitems); + return -ENOMEM; + } + + /* Send the add item SNAC */ + if ((i = aim_ssi_addmoddel(sess, conn, newitems, num, AIM_CB_SSI_ADD))) { + g_free(newitems); + return -i; + } + + /* Free the array of pointers to each of the new items */ + g_free(newitems); + + /* Begin sending SSI SNACs */ + if (!(i = aim_ssi_dispatch(sess, conn))) + return i; + + return 0; +} + +/** + * Move a buddy from one group to another group. This basically just deletes the + * buddy and re-adds it. + * + * @param sess The oscar session. + * @param conn The bos connection for this session. + * @param oldgn The group that the buddy is currently in. + * @param newgn The group that the buddy should be moved in to. + * @param sn The name of the buddy to be moved. + * @return Return 0 if no errors, otherwise return the error number. + */ +int aim_ssi_movebuddy(aim_session_t *sess, aim_conn_t *conn, char *oldgn, char *newgn, char *sn) +{ + struct aim_ssi_item **groups, *buddy, *cur; + guint16 i; + + if (!sess || !conn || !oldgn || !newgn || !sn) + return -EINVAL; + + /* Look up the buddy */ + if (!(buddy = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, sn, AIM_SSI_TYPE_BUDDY))) + return -ENOMEM; + + /* Allocate an array of pointers to the two groups */ + if (!(groups = g_new0(struct aim_ssi_item *, 2))) + return -ENOMEM; + + /* Look up the old parent group */ + if (!(groups[0] = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, oldgn, AIM_SSI_TYPE_GROUP))) { + g_free(groups); + return -ENOMEM; + } + + /* Look up the new parent group */ + if (!(groups[1] = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, newgn, AIM_SSI_TYPE_GROUP))) { + g_free(groups); + return -ENOMEM; + } + + /* Send the delete item SNAC */ + aim_ssi_addmoddel(sess, conn, &buddy, 1, AIM_CB_SSI_DEL); + + /* Put the buddy in the new group */ + buddy->gid = groups[1]->gid; + + /* Assign a new buddy ID#, because the new group might already have a buddy with this ID# */ + buddy->bid = 0; + do { + buddy->bid += 0x0001; + for (cur=sess->ssi.items, i=0; ((cur) && (!i)); cur=cur->next) + if ((cur->bid == buddy->bid) && (cur->gid == buddy->gid) && (cur->type == AIM_SSI_TYPE_BUDDY) && (cur->name) && aim_sncmp(cur->name, buddy->name)) + i=1; + } while (i); + + /* Rebuild the additional data in the two parent groups */ + aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, groups[0]); + aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, groups[1]); + + /* Send the add item SNAC */ + aim_ssi_addmoddel(sess, conn, &buddy, 1, AIM_CB_SSI_ADD); + + /* Send the mod item SNAC */ + aim_ssi_addmoddel(sess, conn, groups, 2, AIM_CB_SSI_MOD); + + /* Free the temporary array */ + g_free(groups); + + /* Begin sending SSI SNACs */ + aim_ssi_dispatch(sess, conn); + + return 0; +} + +/** + * Delete an array of screen names from the given group. + * + * @param sess The oscar session. + * @param conn The bos connection for this session. + * @param gn The name of the group from which you want to delete these names. + * @param sn An array of null terminated strings of the names you want to delete. + * @param num The number of screen names you are deleting (size of the sn array). + * @return Return 0 if no errors, otherwise return the error number. + */ +int aim_ssi_delbuddies(aim_session_t *sess, aim_conn_t *conn, char *gn, char **sn, unsigned int num) +{ + struct aim_ssi_item *cur, *parentgroup, **delitems; + int i; + + if (!sess || !conn || !gn || !sn || !num) + return -EINVAL; + + /* Look up the parent group */ + if (!(parentgroup = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, gn, AIM_SSI_TYPE_GROUP))) + return -EINVAL; + + /* Allocate an array of pointers to each of the items to be deleted */ + delitems = g_new0(struct aim_ssi_item *, num); + + /* Make the delitems array a pointer to the aim_ssi_item structs to be deleted */ + for (i=0; i<num; i++) { + if (!(delitems[i] = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, sn[i], AIM_SSI_TYPE_BUDDY))) { + g_free(delitems); + return -EINVAL; + } + + /* Remove the delitems from the item list */ + if (sess->ssi.items == delitems[i]) { + sess->ssi.items = sess->ssi.items->next; + } else { + for (cur=sess->ssi.items; (cur->next && (cur->next!=delitems[i])); cur=cur->next); + if (cur->next) + cur->next = cur->next->next; + } + } + + /* Send the del item SNAC */ + aim_ssi_addmoddel(sess, conn, delitems, num, AIM_CB_SSI_DEL); + + /* Free the items */ + for (i=0; i<num; i++) { + if (delitems[i]->name) + g_free(delitems[i]->name); + if (delitems[i]->data) + aim_freetlvchain((aim_tlvlist_t **)&delitems[i]->data); + g_free(delitems[i]); + } + g_free(delitems); + + /* Rebuild the additional data in the parent group */ + aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, parentgroup); + + /* Send the mod item SNAC */ + aim_ssi_addmoddel(sess, conn, &parentgroup, 1, AIM_CB_SSI_MOD); + + /* Delete the group, but only if it's empty */ + if (!parentgroup->data) + aim_ssi_delgroups(sess, conn, &parentgroup->name, 1); + + /* Begin sending SSI SNACs */ + aim_ssi_dispatch(sess, conn); + + return 0; +} + +/** + * Delete the master group from the item list. There can be only one. + * Er, so just find the one master group and delete it. + * + * @param sess The oscar session. + * @param conn The bos connection for this session. + * @return Return 0 if no errors, otherwise return the error number. + */ +int aim_ssi_delmastergroup(aim_session_t *sess, aim_conn_t *conn) +{ + struct aim_ssi_item *cur, *delitem; + + if (!sess || !conn) + return -EINVAL; + + /* Make delitem a pointer to the aim_ssi_item to be deleted */ + if (!(delitem = aim_ssi_itemlist_find(sess->ssi.items, 0, 0))) + return -EINVAL; + + /* Remove delitem from the item list */ + if (sess->ssi.items == delitem) { + sess->ssi.items = sess->ssi.items->next; + } else { + for (cur=sess->ssi.items; (cur->next && (cur->next!=delitem)); cur=cur->next); + if (cur->next) + cur->next = cur->next->next; + } + + /* Send the del item SNAC */ + aim_ssi_addmoddel(sess, conn, &delitem, 1, AIM_CB_SSI_DEL); + + /* Free the item */ + if (delitem->name) + g_free(delitem->name); + if (delitem->data) + aim_freetlvchain((aim_tlvlist_t **)&delitem->data); + g_free(delitem); + + /* Begin sending SSI SNACs */ + aim_ssi_dispatch(sess, conn); + + return 0; +} + +/** + * Delete an array of groups. + * + * @param sess The oscar session. + * @param conn The bos connection for this session. + * @param gn An array of null terminated strings of the groups you want to delete. + * @param num The number of groups you are deleting (size of the gn array). + * @return Return 0 if no errors, otherwise return the error number. + */ +int aim_ssi_delgroups(aim_session_t *sess, aim_conn_t *conn, char **gn, unsigned int num) { + struct aim_ssi_item *cur, *parentgroup, **delitems; + int i; + + if (!sess || !conn || !gn || !num) + return -EINVAL; + + /* Look up the parent group */ + if (!(parentgroup = aim_ssi_itemlist_find(sess->ssi.items, 0, 0))) + return -EINVAL; + + /* Allocate an array of pointers to each of the items to be deleted */ + delitems = g_new0(struct aim_ssi_item *, num); + + /* Make the delitems array a pointer to the aim_ssi_item structs to be deleted */ + for (i=0; i<num; i++) { + if (!(delitems[i] = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, gn[i], AIM_SSI_TYPE_GROUP))) { + g_free(delitems); + return -EINVAL; + } + + /* Remove the delitems from the item list */ + if (sess->ssi.items == delitems[i]) { + sess->ssi.items = sess->ssi.items->next; + } else { + for (cur=sess->ssi.items; (cur->next && (cur->next!=delitems[i])); cur=cur->next); + if (cur->next) + cur->next = cur->next->next; + } + } + + /* Send the del item SNAC */ + aim_ssi_addmoddel(sess, conn, delitems, num, AIM_CB_SSI_DEL); + + /* Free the items */ + for (i=0; i<num; i++) { + if (delitems[i]->name) + g_free(delitems[i]->name); + if (delitems[i]->data) + aim_freetlvchain((aim_tlvlist_t **)&delitems[i]->data); + g_free(delitems[i]); + } + g_free(delitems); + + /* Rebuild the additional data in the parent group */ + aim_ssi_itemlist_rebuildgroup(&sess->ssi.items, parentgroup); + + /* Send the mod item SNAC */ + aim_ssi_addmoddel(sess, conn, &parentgroup, 1, AIM_CB_SSI_MOD); + + /* Delete the group, but only if it's empty */ + if (!parentgroup->data) + aim_ssi_delmastergroup(sess, conn); + + /* Begin sending SSI SNACs */ + aim_ssi_dispatch(sess, conn); + + return 0; +} + +/** + * Delete an array of a certain type of item from the list. This can be + * used for permit buddies, deny buddies, ICQ's ignore buddies, and + * probably other types, also. + * + * @param sess The oscar session. + * @param conn The bos connection for this session. + * @param sn An array of null terminated strings of the items you want to delete. + * @param num The number of items you are deleting (size of the sn array). + * @return Return 0 if no errors, otherwise return the error number. + */ +int aim_ssi_delpord(aim_session_t *sess, aim_conn_t *conn, char **sn, unsigned int num, guint16 type) { + struct aim_ssi_item *cur, **delitems; + int i; + + if (!sess || !conn || !sn || !num || (type!=AIM_SSI_TYPE_PERMIT && type!=AIM_SSI_TYPE_DENY)) + return -EINVAL; + + /* Allocate an array of pointers to each of the items to be deleted */ + delitems = g_new0(struct aim_ssi_item *, num); + + /* Make the delitems array a pointer to the aim_ssi_item structs to be deleted */ + for (i=0; i<num; i++) { + if (!(delitems[i] = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, sn[i], type))) { + g_free(delitems); + return -EINVAL; + } + + /* Remove the delitems from the item list */ + if (sess->ssi.items == delitems[i]) { + sess->ssi.items = sess->ssi.items->next; + } else { + for (cur=sess->ssi.items; (cur->next && (cur->next!=delitems[i])); cur=cur->next); + if (cur->next) + cur->next = cur->next->next; + } + } + + /* Send the del item SNAC */ + aim_ssi_addmoddel(sess, conn, delitems, num, AIM_CB_SSI_DEL); + + /* Free the items */ + for (i=0; i<num; i++) { + if (delitems[i]->name) + g_free(delitems[i]->name); + if (delitems[i]->data) + aim_freetlvchain((aim_tlvlist_t **)&delitems[i]->data); + g_free(delitems[i]); + } + g_free(delitems); + + /* Begin sending SSI SNACs */ + aim_ssi_dispatch(sess, conn); + + return 0; +} + +/** + * Stores your permit/deny setting on the server, and starts using it. + * + * @param sess The oscar session. + * @param conn The bos connection for this session. + * @param permdeny Your permit/deny setting. Can be one of the following: + * 1 - Allow all users + * 2 - Block all users + * 3 - Allow only the users below + * 4 - Block only the users below + * 5 - Allow only users on my buddy list + * @param vismask A bitmask of the class of users to whom you want to be + * visible. See the AIM_FLAG_BLEH #defines in aim.h + * @return Return 0 if no errors, otherwise return the error number. + */ +int aim_ssi_setpermdeny(aim_session_t *sess, aim_conn_t *conn, guint8 permdeny, guint32 vismask) { + struct aim_ssi_item *cur; //, *tmp; +// guint16 j; + aim_tlv_t *tlv; + + if (!sess || !conn) + return -EINVAL; + + /* Look up the permit/deny settings item */ + cur = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, NULL, AIM_SSI_TYPE_PDINFO); + + if (cur) { + /* The permit/deny item exists */ + if (cur->data && (tlv = aim_gettlv(cur->data, 0x00ca, 1))) { + /* Just change the value of the x00ca TLV */ + if (tlv->length != 1) { + tlv->length = 1; + g_free(tlv->value); + tlv->value = (guint8 *)g_malloc(sizeof(guint8)); + } + tlv->value[0] = permdeny; + } else { + /* Need to add the x00ca TLV to the TLV chain */ + aim_addtlvtochain8((aim_tlvlist_t**)&cur->data, 0x00ca, permdeny); + } + + if (cur->data && (tlv = aim_gettlv(cur->data, 0x00cb, 1))) { + /* Just change the value of the x00cb TLV */ + if (tlv->length != 4) { + tlv->length = 4; + g_free(tlv->value); + tlv->value = (guint8 *)g_malloc(4*sizeof(guint8)); + } + aimutil_put32(tlv->value, vismask); + } else { + /* Need to add the x00cb TLV to the TLV chain */ + aim_addtlvtochain32((aim_tlvlist_t**)&cur->data, 0x00cb, vismask); + } + + /* Send the mod item SNAC */ + aim_ssi_addmoddel(sess, conn, &cur, 1, AIM_CB_SSI_MOD); + } else { + /* Need to add the permit/deny item */ + if (!(cur = aim_ssi_itemlist_add(&sess->ssi.items, NULL, NULL, AIM_SSI_TYPE_PDINFO))) + return -ENOMEM; + aim_addtlvtochain8((aim_tlvlist_t**)&cur->data, 0x00ca, permdeny); + aim_addtlvtochain32((aim_tlvlist_t**)&cur->data, 0x00cb, vismask); + aim_ssi_addmoddel(sess, conn, &cur, 1, AIM_CB_SSI_ADD); + } + + /* Begin sending SSI SNACs */ + aim_ssi_dispatch(sess, conn); + + return 0; +} + +/** + * Stores your setting for whether you should show up as idle or not. + * + * @param sess The oscar session. + * @param conn The bos connection for this session. + * @param presence I think it's a bitmask, but I only know what one of the bits is: + * 0x00000400 - Allow others to see your idle time + * @return Return 0 if no errors, otherwise return the error number. + */ +int aim_ssi_setpresence(aim_session_t *sess, aim_conn_t *conn, guint32 presence) { + struct aim_ssi_item *cur; //, *tmp; +// guint16 j; + aim_tlv_t *tlv; + + if (!sess || !conn) + return -EINVAL; + + /* Look up the item */ + cur = aim_ssi_itemlist_finditem(sess->ssi.items, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS); + + if (cur) { + /* The item exists */ + if (cur->data && (tlv = aim_gettlv(cur->data, 0x00c9, 1))) { + /* Just change the value of the x00c9 TLV */ + if (tlv->length != 4) { + tlv->length = 4; + g_free(tlv->value); + tlv->value = (guint8 *)g_malloc(4*sizeof(guint8)); + } + aimutil_put32(tlv->value, presence); + } else { + /* Need to add the x00c9 TLV to the TLV chain */ + aim_addtlvtochain32((aim_tlvlist_t**)&cur->data, 0x00c9, presence); + } + + /* Send the mod item SNAC */ + aim_ssi_addmoddel(sess, conn, &cur, 1, AIM_CB_SSI_MOD); + } else { + /* Need to add the item */ + if (!(cur = aim_ssi_itemlist_add(&sess->ssi.items, NULL, NULL, AIM_SSI_TYPE_PRESENCEPREFS))) + return -ENOMEM; + aim_addtlvtochain32((aim_tlvlist_t**)&cur->data, 0x00c9, presence); + aim_ssi_addmoddel(sess, conn, &cur, 1, AIM_CB_SSI_ADD); + } + + /* Begin sending SSI SNACs */ + aim_ssi_dispatch(sess, conn); + + return 0; +} + +/* + * Request SSI Rights. + */ +int aim_ssi_reqrights(aim_session_t *sess, aim_conn_t *conn) +{ + return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_REQRIGHTS); +} + +/* + * SSI Rights Information. + */ +static int parserights(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + aim_rxcallback_t userfunc; + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx); + + return ret; +} + +/* + * Request SSI Data. + * + * The data will only be sent if it is newer than the posted local + * timestamp and revision. + * + * Note that the client should never increment the revision, only the server. + * + */ +int aim_ssi_reqdata(aim_session_t *sess, aim_conn_t *conn, time_t localstamp, guint16 localrev) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !conn) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10+4+2))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, AIM_CB_SSI_REQLIST, 0x0000, NULL, 0); + + aim_putsnac(&fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_REQLIST, 0x0000, snacid); + aimbs_put32(&fr->data, localstamp); + aimbs_put16(&fr->data, localrev); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +int aim_ssi_reqalldata(aim_session_t *sess, aim_conn_t *conn) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + + if (!sess || !conn) + return -EINVAL; + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, 10))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, AIM_CB_SSI_REQFULLLIST, 0x0000, NULL, 0); + + aim_putsnac(&fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_REQFULLLIST, 0x0000, snacid); + + aim_tx_enqueue(sess, fr); + + return 0; +} + +/* + * SSI Data. + */ +static int parsedata(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + aim_rxcallback_t userfunc; + struct aim_ssi_item *cur = NULL; + guint8 fmtver; /* guess */ + guint16 revision; + guint32 timestamp; + + /* When you set the version for the SSI family to 2-4, the beginning of this changes. + * Instead of the version and then the revision, there is "0x0006" and then a type + * 0x0001 TLV containing the 2 byte SSI family version that you sent earlier. Also, + * the SNAC flags go from 0x0000 to 0x8000. I guess the 0x0006 is the length of the + * TLV(s) that follow. The rights SNAC does the same thing, with the differing flag + * and everything. + */ + + fmtver = aimbs_get8(bs); /* Version of ssi data. Should be 0x00 */ + revision = aimbs_get16(bs); /* # of times ssi data has been modified */ + if (revision != 0) + sess->ssi.revision = revision; + + for (cur = sess->ssi.items; cur && cur->next; cur=cur->next) ; + + while (aim_bstream_empty(bs) > 4) { /* last four bytes are stamp */ + guint16 namelen, tbslen; + + if (!sess->ssi.items) { + if (!(sess->ssi.items = g_new0(struct aim_ssi_item, 1))) + return -ENOMEM; + cur = sess->ssi.items; + } else { + if (!(cur->next = g_new0(struct aim_ssi_item, 1))) + return -ENOMEM; + cur = cur->next; + } + + if ((namelen = aimbs_get16(bs))) + cur->name = aimbs_getstr(bs, namelen); + cur->gid = aimbs_get16(bs); + cur->bid = aimbs_get16(bs); + cur->type = aimbs_get16(bs); + + if ((tbslen = aimbs_get16(bs))) { + aim_bstream_t tbs; + + aim_bstream_init(&tbs, bs->data + bs->offset /* XXX */, tbslen); + cur->data = (void *)aim_readtlvchain(&tbs); + aim_bstream_advance(bs, tbslen); + } + } + + timestamp = aimbs_get32(bs); + if (timestamp != 0) + sess->ssi.timestamp = timestamp; + sess->ssi.received_data = 1; + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, fmtver, sess->ssi.revision, sess->ssi.timestamp, sess->ssi.items); + + return ret; +} + +/* + * SSI Data Enable Presence. + * + * Should be sent after receiving 13/6 or 13/f to tell the server you + * are ready to begin using the list. It will promptly give you the + * presence information for everyone in your list and put your permit/deny + * settings into effect. + * + */ +int aim_ssi_enable(aim_session_t *sess, aim_conn_t *conn) +{ + return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, 0x0007); +} + +/* + * Stuff for SSI authorizations. The code used to work with the old im_ch4 + * messages, but those are supposed to be obsolete. This is probably + * ICQ-specific. + */ + +/** + * Request authorization to add someone to the server-side buddy list. + * + * @param sess The oscar session. + * @param conn The bos connection for this session. + * @param uin The contact's ICQ UIN. + * @param reason The reason string to send with the request. + * @return Return 0 if no errors, otherwise return the error number. + */ +int aim_ssi_auth_request( aim_session_t *sess, aim_conn_t *conn, char *uin, char *reason ) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + int snaclen; + + snaclen = 10 + 1 + strlen( uin ) + 2 + strlen( reason ) + 2; + + if( !( fr = aim_tx_new( sess, conn, AIM_FRAMETYPE_FLAP, 0x02, snaclen ) ) ) + return -ENOMEM; + + snacid = aim_cachesnac( sess, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREQ, 0x0000, NULL, 0 ); + aim_putsnac( &fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREQ, 0x0000, snacid ); + + aimbs_put8( &fr->data, strlen( uin ) ); + aimbs_putraw( &fr->data, (guint8 *)uin, strlen( uin ) ); + aimbs_put16( &fr->data, strlen( reason ) ); + aimbs_putraw( &fr->data, (guint8 *)reason, strlen( reason ) ); + aimbs_put16( &fr->data, 0 ); + + aim_tx_enqueue( sess, fr ); + + return( 0 ); +} + +/** + * Reply to an authorization request to add someone to the server-side buddy list. + * + * @param sess The oscar session. + * @param conn The bos connection for this session. + * @param uin The contact's ICQ UIN. + * @param yesno 1 == Permit, 0 == Deny + * @param reason The reason string to send with the request. + * @return Return 0 if no errors, otherwise return the error number. + */ +int aim_ssi_auth_reply( aim_session_t *sess, aim_conn_t *conn, char *uin, int yesno, char *reason ) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + int snaclen; + + snaclen = 10 + 1 + strlen( uin ) + 3 + strlen( reason ); + + if( !( fr = aim_tx_new( sess, conn, AIM_FRAMETYPE_FLAP, 0x02, snaclen ) ) ) + return -ENOMEM; + + snacid = aim_cachesnac( sess, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREP, 0x0000, NULL, 0 ); + aim_putsnac( &fr->data, AIM_CB_FAM_SSI, AIM_CB_SSI_SENDAUTHREP, 0x0000, snacid ); + + aimbs_put8( &fr->data, strlen( uin ) ); + aimbs_putraw( &fr->data, (guint8 *)uin, strlen( uin ) ); + aimbs_put8( &fr->data, yesno ); + aimbs_put16( &fr->data, strlen( reason ) ); + aimbs_putraw( &fr->data, (guint8 *)reason, strlen( reason ) ); + + aim_tx_enqueue( sess, fr ); + + return( 0 ); +} + + +/* + * SSI Add/Mod/Del Item(s). + * + * Sends the SNAC to add, modify, or delete an item from the server-stored + * information. These 3 SNACs all have an identical structure. The only + * difference is the subtype that is set for the SNAC. + * + */ +int aim_ssi_addmoddel(aim_session_t *sess, aim_conn_t *conn, struct aim_ssi_item **items, unsigned int num, guint16 subtype) +{ + aim_frame_t *fr; + aim_snacid_t snacid; + int i, snaclen, listlen; + char *list = NULL; + + if (!sess || !conn || !items || !num) + return -EINVAL; + + snaclen = 10; /* For family, subtype, flags, and SNAC ID */ + listlen = 0; + for (i=0; i<num; i++) { + snaclen += 10; /* For length, GID, BID, type, and length */ + if (items[i]->name) { + snaclen += strlen(items[i]->name); + + if (subtype == AIM_CB_SSI_ADD) { + list = g_realloc(list, listlen + strlen(items[i]->name) + 1); + strcpy(list + listlen, items[i]->name); + listlen += strlen(items[i]->name) + 1; + } + } else { + if (subtype == AIM_CB_SSI_ADD) { + list = g_realloc(list, listlen + 1); + list[listlen] = '\0'; + listlen ++; + } + } + if (items[i]->data) + snaclen += aim_sizetlvchain((aim_tlvlist_t **)&items[i]->data); + } + + if (!(fr = aim_tx_new(sess, conn, AIM_FRAMETYPE_FLAP, 0x02, snaclen))) + return -ENOMEM; + + snacid = aim_cachesnac(sess, AIM_CB_FAM_SSI, subtype, 0x0000, list, list ? listlen : 0); + aim_putsnac(&fr->data, AIM_CB_FAM_SSI, subtype, 0x0000, snacid); + + g_free(list); + + for (i=0; i<num; i++) { + aimbs_put16(&fr->data, items[i]->name ? strlen(items[i]->name) : 0); + if (items[i]->name) + aimbs_putraw(&fr->data, (guint8 *)items[i]->name, strlen(items[i]->name)); + aimbs_put16(&fr->data, items[i]->gid); + aimbs_put16(&fr->data, items[i]->bid); + aimbs_put16(&fr->data, items[i]->type); + aimbs_put16(&fr->data, items[i]->data ? aim_sizetlvchain((aim_tlvlist_t **)&items[i]->data) : 0); + if (items[i]->data) + aim_writetlvchain(&fr->data, (aim_tlvlist_t **)&items[i]->data); + } + + aim_ssi_enqueue(sess, conn, fr); + + return 0; +} + +/* + * SSI Add/Mod/Del Ack. + * + * Response to add, modify, or delete SNAC (sent with aim_ssi_addmoddel). + * + */ +static int parseack(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + aim_rxcallback_t userfunc; + aim_snac_t *origsnac; + + sess->ssi.waiting_for_ack = 0; + aim_ssi_dispatch(sess, rx->conn); + + origsnac = aim_remsnac(sess, snac->id); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx, origsnac); + + if (origsnac) { + g_free(origsnac->data); + g_free(origsnac); + } + + return ret; +} + +/* + * SSI Begin Data Modification. + * + * Tells the server you're going to start modifying data. + * + */ +int aim_ssi_modbegin(aim_session_t *sess, aim_conn_t *conn) +{ + return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_EDITSTART); +} + +/* + * SSI End Data Modification. + * + * Tells the server you're done modifying data. + * + */ +int aim_ssi_modend(aim_session_t *sess, aim_conn_t *conn) +{ + return aim_genericreq_n(sess, conn, AIM_CB_FAM_SSI, AIM_CB_SSI_EDITSTOP); +} + +/* + * SSI Data Unchanged. + * + * Response to aim_ssi_reqdata() if the server-side data is not newer than + * posted local stamp/revision. + * + */ +static int parsedataunchanged(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + int ret = 0; + aim_rxcallback_t userfunc; + + sess->ssi.received_data = 1; + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + ret = userfunc(sess, rx); + + return ret; +} + +static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + + if (snac->subtype == AIM_CB_SSI_RIGHTSINFO) + return parserights(sess, mod, rx, snac, bs); + else if (snac->subtype == AIM_CB_SSI_LIST) + return parsedata(sess, mod, rx, snac, bs); + else if (snac->subtype == AIM_CB_SSI_SRVACK) + return parseack(sess, mod, rx, snac, bs); + else if (snac->subtype == AIM_CB_SSI_NOLIST) + return parsedataunchanged(sess, mod, rx, snac, bs); + + return 0; +} + +static void ssi_shutdown(aim_session_t *sess, aim_module_t *mod) +{ + aim_ssi_freelist(sess); + + return; +} + +int ssi_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = AIM_CB_FAM_SSI; + mod->version = 0x0003; + mod->toolid = 0x0110; + mod->toolversion = 0x0629; + mod->flags = 0; + strncpy(mod->name, "ssi", sizeof(mod->name)); + mod->snachandler = snachandler; + mod->shutdown = ssi_shutdown; + + return 0; +} diff --git a/protocols/oscar/ssi.h b/protocols/oscar/ssi.h new file mode 100644 index 00000000..94b18d60 --- /dev/null +++ b/protocols/oscar/ssi.h @@ -0,0 +1,78 @@ +#ifndef __OSCAR_SSI_H__ +#define __OSCAR_SSI_H__ + +#define AIM_CB_FAM_SSI 0x0013 /* Server stored information */ + +/* + * SNAC Family: Server-Stored Buddy Lists + */ +#define AIM_CB_SSI_ERROR 0x0001 +#define AIM_CB_SSI_REQRIGHTS 0x0002 +#define AIM_CB_SSI_RIGHTSINFO 0x0003 +#define AIM_CB_SSI_REQFULLLIST 0x0004 +#define AIM_CB_SSI_REQLIST 0x0005 +#define AIM_CB_SSI_LIST 0x0006 +#define AIM_CB_SSI_ACTIVATE 0x0007 +#define AIM_CB_SSI_ADD 0x0008 +#define AIM_CB_SSI_MOD 0x0009 +#define AIM_CB_SSI_DEL 0x000A +#define AIM_CB_SSI_SRVACK 0x000E +#define AIM_CB_SSI_NOLIST 0x000F +#define AIM_CB_SSI_EDITSTART 0x0011 +#define AIM_CB_SSI_EDITSTOP 0x0012 +#define AIM_CB_SSI_SENDAUTHREQ 0x0018 +#define AIM_CB_SSI_SERVAUTHREQ 0x0019 +#define AIM_CB_SSI_SENDAUTHREP 0x001A +#define AIM_CB_SSI_SERVAUTHREP 0x001B + + +#define AIM_SSI_TYPE_BUDDY 0x0000 +#define AIM_SSI_TYPE_GROUP 0x0001 +#define AIM_SSI_TYPE_PERMIT 0x0002 +#define AIM_SSI_TYPE_DENY 0x0003 +#define AIM_SSI_TYPE_PDINFO 0x0004 +#define AIM_SSI_TYPE_PRESENCEPREFS 0x0005 + +struct aim_ssi_item { + char *name; + guint16 gid; + guint16 bid; + guint16 type; + void *data; + struct aim_ssi_item *next; +}; + +/* These build the actual SNACs and queue them to be sent */ +int aim_ssi_reqrights(aim_session_t *sess, aim_conn_t *conn); +int aim_ssi_reqdata(aim_session_t *sess, aim_conn_t *conn, time_t localstamp, guint16 localrev); +int aim_ssi_reqalldata(aim_session_t *sess, aim_conn_t *conn); +int aim_ssi_enable(aim_session_t *sess, aim_conn_t *conn); +int aim_ssi_addmoddel(aim_session_t *sess, aim_conn_t *conn, struct aim_ssi_item **items, unsigned int num, guint16 subtype); +int aim_ssi_modbegin(aim_session_t *sess, aim_conn_t *conn); +int aim_ssi_modend(aim_session_t *sess, aim_conn_t *conn); + +/* These handle the local variables */ +struct aim_ssi_item *aim_ssi_itemlist_find(struct aim_ssi_item *list, guint16 gid, guint16 bid); +struct aim_ssi_item *aim_ssi_itemlist_finditem(struct aim_ssi_item *list, char *gn, char *sn, guint16 type); +struct aim_ssi_item *aim_ssi_itemlist_findparent(struct aim_ssi_item *list, char *sn); +int aim_ssi_getpermdeny(struct aim_ssi_item *list); +guint32 aim_ssi_getpresence(struct aim_ssi_item *list); + +/* Send packets */ +int aim_ssi_cleanlist(aim_session_t *sess, aim_conn_t *conn); +int aim_ssi_addbuddies(aim_session_t *sess, aim_conn_t *conn, char *gn, char **sn, unsigned int num, unsigned int flags); +int aim_ssi_addmastergroup(aim_session_t *sess, aim_conn_t *conn); +int aim_ssi_addgroups(aim_session_t *sess, aim_conn_t *conn, char **gn, unsigned int num); +int aim_ssi_addpord(aim_session_t *sess, aim_conn_t *conn, char **sn, unsigned int num, guint16 type); +int aim_ssi_movebuddy(aim_session_t *sess, aim_conn_t *conn, char *oldgn, char *newgn, char *sn); +int aim_ssi_delbuddies(aim_session_t *sess, aim_conn_t *conn, char *gn, char **sn, unsigned int num); +int aim_ssi_delmastergroup(aim_session_t *sess, aim_conn_t *conn); +int aim_ssi_delgroups(aim_session_t *sess, aim_conn_t *conn, char **gn, unsigned int num); +int aim_ssi_deletelist(aim_session_t *sess, aim_conn_t *conn); +int aim_ssi_delpord(aim_session_t *sess, aim_conn_t *conn, char **sn, unsigned int num, guint16 type); +int aim_ssi_setpermdeny(aim_session_t *sess, aim_conn_t *conn, guint8 permdeny, guint32 vismask); +int aim_ssi_setpresence(aim_session_t *sess, aim_conn_t *conn, guint32 presence); +int aim_ssi_auth_request(aim_session_t *sess, aim_conn_t *conn, char *uin, char *reason); +int aim_ssi_auth_reply(aim_session_t *sess, aim_conn_t *conn, char *uin, int yesno, char *reason); + +#endif /* __OSCAR_SSI_H__ */ diff --git a/protocols/oscar/stats.c b/protocols/oscar/stats.c new file mode 100644 index 00000000..affd82fe --- /dev/null +++ b/protocols/oscar/stats.c @@ -0,0 +1,38 @@ + +#include <aim.h> + +static int reportinterval(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + guint16 interval; + aim_rxcallback_t userfunc; + + interval = aimbs_get16(bs); + + if ((userfunc = aim_callhandler(sess, rx->conn, snac->family, snac->subtype))) + return userfunc(sess, rx, interval); + + return 0; +} + +static int snachandler(aim_session_t *sess, aim_module_t *mod, aim_frame_t *rx, aim_modsnac_t *snac, aim_bstream_t *bs) +{ + + if (snac->subtype == 0x0002) + return reportinterval(sess, mod, rx, snac, bs); + + return 0; +} + +int stats_modfirst(aim_session_t *sess, aim_module_t *mod) +{ + + mod->family = 0x000b; + mod->version = 0x0001; + mod->toolid = 0x0104; + mod->toolversion = 0x0001; + mod->flags = 0; + strncpy(mod->name, "stats", sizeof(mod->name)); + mod->snachandler = snachandler; + + return 0; +} diff --git a/protocols/oscar/tlv.c b/protocols/oscar/tlv.c new file mode 100644 index 00000000..74d177ad --- /dev/null +++ b/protocols/oscar/tlv.c @@ -0,0 +1,600 @@ +#include <aim.h> + +static aim_tlv_t *createtlv(void) +{ + return g_new0(aim_tlv_t, 1); +} + +static void freetlv(aim_tlv_t **oldtlv) +{ + + if (!oldtlv || !*oldtlv) + return; + + g_free((*oldtlv)->value); + g_free(*oldtlv); + *oldtlv = NULL; + + return; +} + +/** + * aim_readtlvchain - Read a TLV chain from a buffer. + * @buf: Input buffer + * @maxlen: Length of input buffer + * + * Reads and parses a series of TLV patterns from a data buffer; the + * returned structure is manipulatable with the rest of the TLV + * routines. When done with a TLV chain, aim_freetlvchain() should + * be called to free the dynamic substructures. + * + * XXX There should be a flag setable here to have the tlvlist contain + * bstream references, so that at least the ->value portion of each + * element doesn't need to be malloc/memcpy'd. This could prove to be + * just as effecient as the in-place TLV parsing used in a couple places + * in libfaim. + * + */ +aim_tlvlist_t *aim_readtlvchain(aim_bstream_t *bs) +{ + aim_tlvlist_t *list = NULL, *cur; + guint16 type, length; + + while (aim_bstream_empty(bs)) { + + type = aimbs_get16(bs); + length = aimbs_get16(bs); + +#if 0 /* temporarily disabled until I know if they're still doing it or not */ + /* + * Okay, so now AOL has decided that any TLV of + * type 0x0013 can only be two bytes, despite + * what the actual given length is. So here + * we dump any invalid TLVs of that sort. Hopefully + * theres no special cases to this special case. + * - mid (30jun2000) + */ + if ((type == 0x0013) && (length != 0x0002)) + length = 0x0002; +#else + if (0) + ; +#endif + else { + + cur = g_new0(aim_tlvlist_t, 1); + + cur->tlv = createtlv(); + cur->tlv->type = type; + if ((cur->tlv->length = length)) + cur->tlv->value = aimbs_getraw(bs, length); + + cur->next = list; + list = cur; + } + } + + return list; +} + +/** + * aim_freetlvchain - Free a TLV chain structure + * @list: Chain to be freed + * + * Walks the list of TLVs in the passed TLV chain and + * frees each one. Note that any references to this data + * should be removed before calling this. + * + */ +void aim_freetlvchain(aim_tlvlist_t **list) +{ + aim_tlvlist_t *cur; + + if (!list || !*list) + return; + + for (cur = *list; cur; ) { + aim_tlvlist_t *tmp; + + freetlv(&cur->tlv); + + tmp = cur->next; + g_free(cur); + cur = tmp; + } + + list = NULL; + + return; +} + +/** + * aim_counttlvchain - Count the number of TLVs in a chain + * @list: Chain to be counted + * + * Returns the number of TLVs stored in the passed chain. + * + */ +int aim_counttlvchain(aim_tlvlist_t **list) +{ + aim_tlvlist_t *cur; + int count; + + if (!list || !*list) + return 0; + + for (cur = *list, count = 0; cur; cur = cur->next) + count++; + + return count; +} + +/** + * aim_sizetlvchain - Count the number of bytes in a TLV chain + * @list: Chain to be sized + * + * Returns the number of bytes that would be needed to + * write the passed TLV chain to a data buffer. + * + */ +int aim_sizetlvchain(aim_tlvlist_t **list) +{ + aim_tlvlist_t *cur; + int size; + + if (!list || !*list) + return 0; + + for (cur = *list, size = 0; cur; cur = cur->next) + size += (4 + cur->tlv->length); + + return size; +} + +/** + * aim_addtlvtochain_str - Add a string to a TLV chain + * @list: Desination chain (%NULL pointer if empty) + * @type: TLV type + * @str: String to add + * @len: Length of string to add (not including %NULL) + * + * Adds the passed string as a TLV element of the passed type + * to the TLV chain. + * + */ +int aim_addtlvtochain_raw(aim_tlvlist_t **list, const guint16 t, const guint16 l, const guint8 *v) +{ + aim_tlvlist_t *newtlv, *cur; + + if (!list) + return 0; + + if (!(newtlv = g_new0(aim_tlvlist_t, 1))) + return 0; + + if (!(newtlv->tlv = createtlv())) { + g_free(newtlv); + return 0; + } + newtlv->tlv->type = t; + if ((newtlv->tlv->length = l)) { + newtlv->tlv->value = (guint8 *)g_malloc(newtlv->tlv->length); + memcpy(newtlv->tlv->value, v, newtlv->tlv->length); + } + + if (!*list) + *list = newtlv; + else { + for(cur = *list; cur->next; cur = cur->next) + ; + cur->next = newtlv; + } + + return newtlv->tlv->length; +} + +/** + * aim_addtlvtochain8 - Add a 8bit integer to a TLV chain + * @list: Destination chain + * @type: TLV type to add + * @val: Value to add + * + * Adds a one-byte unsigned integer to a TLV chain. + * + */ +int aim_addtlvtochain8(aim_tlvlist_t **list, const guint16 t, const guint8 v) +{ + guint8 v8[1]; + + aimutil_put8(v8, v); + + return aim_addtlvtochain_raw(list, t, 1, v8); +} + +/** + * aim_addtlvtochain16 - Add a 16bit integer to a TLV chain + * @list: Destination chain + * @type: TLV type to add + * @val: Value to add + * + * Adds a two-byte unsigned integer to a TLV chain. + * + */ +int aim_addtlvtochain16(aim_tlvlist_t **list, const guint16 t, const guint16 v) +{ + guint8 v16[2]; + + aimutil_put16(v16, v); + + return aim_addtlvtochain_raw(list, t, 2, v16); +} + +/** + * aim_addtlvtochain32 - Add a 32bit integer to a TLV chain + * @list: Destination chain + * @type: TLV type to add + * @val: Value to add + * + * Adds a four-byte unsigned integer to a TLV chain. + * + */ +int aim_addtlvtochain32(aim_tlvlist_t **list, const guint16 t, const guint32 v) +{ + guint8 v32[4]; + + aimutil_put32(v32, v); + + return aim_addtlvtochain_raw(list, t, 4, v32); +} + +/** + * aim_addtlvtochain_availmsg - Add a ICQ availability message to a TLV chain + * @list: Destination chain + * @type: TLV type to add + * @val: Value to add + * + * Adds a available message to a TLV chain + * + */ +int aim_addtlvtochain_availmsg(aim_tlvlist_t **list, const guint16 t, const char *msg) +{ + int ret; + guint16 unknown_data = 0x00; + guint8 add_data_len = 4; + guint16 msg_len = strlen(msg); + guint8 total_len = strlen(msg) + add_data_len; + guint8 *data, *cur; + guint8 alloc_len = msg_len + (3*sizeof(guint16)) + (2*sizeof(guint8)); + data = cur = g_malloc(alloc_len); + + cur += aimutil_put16(cur, 2); + cur += aimutil_put8(cur, add_data_len); + cur += aimutil_put8(cur, total_len); + cur += aimutil_put16(cur, msg_len); + cur += aimutil_putstr(cur, msg, msg_len); + cur += aimutil_put16(cur, unknown_data); + + ret = aim_addtlvtochain_raw(list, t, alloc_len, data); + g_free(data); + + return ret; +} + +/** + * aim_addtlvtochain_caps - Add a capability block to a TLV chain + * @list: Destination chain + * @type: TLV type to add + * @caps: Bitfield of capability flags to send + * + * Adds a block of capability blocks to a TLV chain. The bitfield + * passed in should be a bitwise %OR of any of the %AIM_CAPS constants: + * + */ +int aim_addtlvtochain_caps(aim_tlvlist_t **list, const guint16 t, const guint32 caps) +{ + guint8 buf[16*16]; /* XXX icky fixed length buffer */ + aim_bstream_t bs; + + if (!caps) + return 0; /* nothing there anyway */ + + aim_bstream_init(&bs, buf, sizeof(buf)); + + aim_putcap(&bs, caps); + + return aim_addtlvtochain_raw(list, t, aim_bstream_curpos(&bs), buf); +} + +int aim_addtlvtochain_userinfo(aim_tlvlist_t **list, guint16 type, aim_userinfo_t *ui) +{ + guint8 buf[1024]; /* bleh */ + aim_bstream_t bs; + + aim_bstream_init(&bs, buf, sizeof(buf)); + + aim_putuserinfo(&bs, ui); + + return aim_addtlvtochain_raw(list, type, aim_bstream_curpos(&bs), buf); +} + +/** + * aim_addtlvtochain_noval - Add a blank TLV to a TLV chain + * @list: Destination chain + * @type: TLV type to add + * + * Adds a TLV with a zero length to a TLV chain. + * + */ +int aim_addtlvtochain_noval(aim_tlvlist_t **list, const guint16 t) +{ + return aim_addtlvtochain_raw(list, t, 0, NULL); +} + +/* + * Note that the inner TLV chain will not be modifiable as a tlvchain once + * it is written using this. Or rather, it can be, but updates won't be + * made to this. + * + * XXX should probably support sublists for real. + * + * This is so neat. + * + */ +int aim_addtlvtochain_frozentlvlist(aim_tlvlist_t **list, guint16 type, aim_tlvlist_t **tl) +{ + guint8 *buf; + int buflen; + aim_bstream_t bs; + + buflen = aim_sizetlvchain(tl); + + if (buflen <= 0) + return 0; + + if (!(buf = g_malloc(buflen))) + return 0; + + aim_bstream_init(&bs, buf, buflen); + + aim_writetlvchain(&bs, tl); + + aim_addtlvtochain_raw(list, type, aim_bstream_curpos(&bs), buf); + + g_free(buf); + + return buflen; +} + +/** + * aim_writetlvchain - Write a TLV chain into a data buffer. + * @buf: Destination buffer + * @buflen: Maximum number of bytes that will be written to buffer + * @list: Source TLV chain + * + * Copies a TLV chain into a raw data buffer, writing only the number + * of bytes specified. This operation does not free the chain; + * aim_freetlvchain() must still be called to free up the memory used + * by the chain structures. + * + * XXX clean this up, make better use of bstreams + */ +int aim_writetlvchain(aim_bstream_t *bs, aim_tlvlist_t **list) +{ + int goodbuflen; + aim_tlvlist_t *cur; + + /* do an initial run to test total length */ + for (cur = *list, goodbuflen = 0; cur; cur = cur->next) { + goodbuflen += 2 + 2; /* type + len */ + goodbuflen += cur->tlv->length; + } + + if (goodbuflen > aim_bstream_empty(bs)) + return 0; /* not enough buffer */ + + /* do the real write-out */ + for (cur = *list; cur; cur = cur->next) { + aimbs_put16(bs, cur->tlv->type); + aimbs_put16(bs, cur->tlv->length); + if (cur->tlv->length) + aimbs_putraw(bs, cur->tlv->value, cur->tlv->length); + } + + return 1; /* XXX this is a nonsensical return */ +} + + +/** + * aim_gettlv - Grab the Nth TLV of type type in the TLV list list. + * @list: Source chain + * @type: Requested TLV type + * @nth: Index of TLV of type to get + * + * Returns a pointer to an aim_tlv_t of the specified type; + * %NULL on error. The @nth parameter is specified starting at %1. + * In most cases, there will be no more than one TLV of any type + * in a chain. + * + */ +aim_tlv_t *aim_gettlv(aim_tlvlist_t *list, const guint16 t, const int n) +{ + aim_tlvlist_t *cur; + int i; + + for (cur = list, i = 0; cur; cur = cur->next) { + if (cur && cur->tlv) { + if (cur->tlv->type == t) + i++; + if (i >= n) + return cur->tlv; + } + } + + return NULL; +} + +/** + * aim_gettlv_str - Retrieve the Nth TLV in chain as a string. + * @list: Source TLV chain + * @type: TLV type to search for + * @nth: Index of TLV to return + * + * Same as aim_gettlv(), except that the return value is a %NULL- + * terminated string instead of an aim_tlv_t. This is a + * dynamic buffer and must be freed by the caller. + * + */ +char *aim_gettlv_str(aim_tlvlist_t *list, const guint16 t, const int n) +{ + aim_tlv_t *tlv; + char *newstr; + + if (!(tlv = aim_gettlv(list, t, n))) + return NULL; + + newstr = (char *) g_malloc(tlv->length + 1); + memcpy(newstr, tlv->value, tlv->length); + *(newstr + tlv->length) = '\0'; + + return newstr; +} + +/** + * aim_gettlv8 - Retrieve the Nth TLV in chain as a 8bit integer. + * @list: Source TLV chain + * @type: TLV type to search for + * @nth: Index of TLV to return + * + * Same as aim_gettlv(), except that the return value is a + * 8bit integer instead of an aim_tlv_t. + * + */ +guint8 aim_gettlv8(aim_tlvlist_t *list, const guint16 t, const int n) +{ + aim_tlv_t *tlv; + + if (!(tlv = aim_gettlv(list, t, n))) + return 0; /* erm */ + return aimutil_get8(tlv->value); +} + +/** + * aim_gettlv16 - Retrieve the Nth TLV in chain as a 16bit integer. + * @list: Source TLV chain + * @type: TLV type to search for + * @nth: Index of TLV to return + * + * Same as aim_gettlv(), except that the return value is a + * 16bit integer instead of an aim_tlv_t. + * + */ +guint16 aim_gettlv16(aim_tlvlist_t *list, const guint16 t, const int n) +{ + aim_tlv_t *tlv; + + if (!(tlv = aim_gettlv(list, t, n))) + return 0; /* erm */ + return aimutil_get16(tlv->value); +} + +/** + * aim_gettlv32 - Retrieve the Nth TLV in chain as a 32bit integer. + * @list: Source TLV chain + * @type: TLV type to search for + * @nth: Index of TLV to return + * + * Same as aim_gettlv(), except that the return value is a + * 32bit integer instead of an aim_tlv_t. + * + */ +guint32 aim_gettlv32(aim_tlvlist_t *list, const guint16 t, const int n) +{ + aim_tlv_t *tlv; + + if (!(tlv = aim_gettlv(list, t, n))) + return 0; /* erm */ + return aimutil_get32(tlv->value); +} + +#if 0 +/** + * aim_puttlv_8 - Write a one-byte TLV. + * @buf: Destination buffer + * @t: TLV type + * @v: Value + * + * Writes a TLV with a one-byte integer value portion. + * + */ +int aim_puttlv_8(guint8 *buf, const guint16 t, const guint8 v) +{ + guint8 v8[1]; + + aimutil_put8(v8, v); + + return aim_puttlv_raw(buf, t, 1, v8); +} + +/** + * aim_puttlv_16 - Write a two-byte TLV. + * @buf: Destination buffer + * @t: TLV type + * @v: Value + * + * Writes a TLV with a two-byte integer value portion. + * + */ +int aim_puttlv_16(guint8 *buf, const guint16 t, const guint16 v) +{ + guint8 v16[2]; + + aimutil_put16(v16, v); + + return aim_puttlv_raw(buf, t, 2, v16); +} + + +/** + * aim_puttlv_32 - Write a four-byte TLV. + * @buf: Destination buffer + * @t: TLV type + * @v: Value + * + * Writes a TLV with a four-byte integer value portion. + * + */ +int aim_puttlv_32(guint8 *buf, const guint16 t, const guint32 v) +{ + guint8 v32[4]; + + aimutil_put32(v32, v); + + return aim_puttlv_raw(buf, t, 4, v32); +} + +/** + * aim_puttlv_raw - Write a raw TLV. + * @buf: Destination buffer + * @t: TLV type + * @l: Length of string + * @v: String to write + * + * Writes a TLV with a raw value portion. (Only the first @l + * bytes of the passed buffer will be written, which should not + * include a terminating NULL.) + * + */ +int aim_puttlv_raw(guint8 *buf, const guint16 t, const guint16 l, const guint8 *v) +{ + int i; + + i = aimutil_put16(buf, t); + i += aimutil_put16(buf+i, l); + if (l) + memcpy(buf+i, v, l); + i += l; + + return i; +} +#endif + diff --git a/protocols/oscar/txqueue.c b/protocols/oscar/txqueue.c new file mode 100644 index 00000000..51415089 --- /dev/null +++ b/protocols/oscar/txqueue.c @@ -0,0 +1,440 @@ +/* + * aim_txqueue.c + * + * Herein lies all the mangement routines for the transmit (Tx) queue. + * + */ + +#include <aim.h> +#include "im.h" + +#ifndef _WIN32 +#include <sys/socket.h> +#endif + +/* + * Allocate a new tx frame. + * + * This is more for looks than anything else. + * + * Right now, that is. If/when we implement a pool of transmit + * frames, this will become the request-an-unused-frame part. + * + * framing = AIM_FRAMETYPE_OFT/FLAP + * chan = channel for FLAP, hdrtype for OFT + * + */ +aim_frame_t *aim_tx_new(aim_session_t *sess, aim_conn_t *conn, guint8 framing, guint8 chan, int datalen) +{ + aim_frame_t *fr; + + if (!conn) { + do_error_dialog(sess->aux_data, "no connection specified", "Gaim"); + return NULL; + } + + /* For sanity... */ + if ((conn->type == AIM_CONN_TYPE_RENDEZVOUS) || + (conn->type == AIM_CONN_TYPE_RENDEZVOUS_OUT)) { + if (framing != AIM_FRAMETYPE_OFT) { + do_error_dialog(sess->aux_data, "attempted to allocate inappropriate frame type for rendezvous connection", "Gaim"); + return NULL; + } + } else { + if (framing != AIM_FRAMETYPE_FLAP) { + do_error_dialog(sess->aux_data, "attempted to allocate inappropriate frame type for FLAP connection", "Gaim"); + return NULL; + } + } + + if (!(fr = (aim_frame_t *)g_new0(aim_frame_t,1))) + return NULL; + + fr->conn = conn; + + fr->hdrtype = framing; + + if (fr->hdrtype == AIM_FRAMETYPE_FLAP) { + + fr->hdr.flap.type = chan; + + } else if (fr->hdrtype == AIM_FRAMETYPE_OFT) { + + fr->hdr.oft.type = chan; + fr->hdr.oft.hdr2len = 0; /* this will get setup by caller */ + + } else + do_error_dialog(sess->aux_data, "unknown framing", "Gaim"); + + if (datalen > 0) { + guint8 *data; + + if (!(data = (unsigned char *)g_malloc(datalen))) { + aim_frame_destroy(fr); + return NULL; + } + + aim_bstream_init(&fr->data, data, datalen); + } + + return fr; +} + +/* + * aim_tx_enqeue__queuebased() + * + * The overall purpose here is to enqueue the passed in command struct + * into the outgoing (tx) queue. Basically... + * 1) Make a scope-irrelevent copy of the struct + * 3) Mark as not-sent-yet + * 4) Enqueue the struct into the list + * 6) Return + * + * Note that this is only used when doing queue-based transmitting; + * that is, when sess->tx_enqueue is set to &aim_tx_enqueue__queuebased. + * + */ +static int aim_tx_enqueue__queuebased(aim_session_t *sess, aim_frame_t *fr) +{ + + if (!fr->conn) { + do_error_dialog(sess->aux_data, "WARNING: enqueueing packet with no connection", "Gaim"); + fr->conn = aim_getconn_type(sess, AIM_CONN_TYPE_BOS); + } + + if (fr->hdrtype == AIM_FRAMETYPE_FLAP) { + /* assign seqnum -- XXX should really not assign until hardxmit */ + fr->hdr.flap.seqnum = aim_get_next_txseqnum(fr->conn); + } + + fr->handled = 0; /* not sent yet */ + + /* see overhead note in aim_rxqueue counterpart */ + if (!sess->queue_outgoing) + sess->queue_outgoing = fr; + else { + aim_frame_t *cur; + + for (cur = sess->queue_outgoing; cur->next; cur = cur->next) + ; + cur->next = fr; + } + + return 0; +} + +/* + * aim_tx_enqueue__immediate() + * + * Parallel to aim_tx_enqueue__queuebased, however, this bypasses + * the whole queue mess when you want immediate writes to happen. + * + * Basically the same as its __queuebased couterpart, however + * instead of doing a list append, it just calls aim_tx_sendframe() + * right here. + * + */ +static int aim_tx_enqueue__immediate(aim_session_t *sess, aim_frame_t *fr) +{ + + if (!fr->conn) { + do_error_dialog(sess->aux_data, "packet has no connection", "Gaim"); + aim_frame_destroy(fr); + return 0; + } + + if (fr->hdrtype == AIM_FRAMETYPE_FLAP) + fr->hdr.flap.seqnum = aim_get_next_txseqnum(fr->conn); + + fr->handled = 0; /* not sent yet */ + + aim_tx_sendframe(sess, fr); + + aim_frame_destroy(fr); + + return 0; +} + +int aim_tx_setenqueue(aim_session_t *sess, int what, int (*func)(aim_session_t *, aim_frame_t *)) +{ + + if (what == AIM_TX_QUEUED) + sess->tx_enqueue = &aim_tx_enqueue__queuebased; + else if (what == AIM_TX_IMMEDIATE) + sess->tx_enqueue = &aim_tx_enqueue__immediate; + else if (what == AIM_TX_USER) { + if (!func) + return -EINVAL; + sess->tx_enqueue = func; + } else + return -EINVAL; /* unknown action */ + + return 0; +} + +int aim_tx_enqueue(aim_session_t *sess, aim_frame_t *fr) +{ + + /* + * If we want to send a connection thats inprogress, we have to force + * them to use the queue based version. Otherwise, use whatever they + * want. + */ + if (fr && fr->conn && + (fr->conn->status & AIM_CONN_STATUS_INPROGRESS)) { + return aim_tx_enqueue__queuebased(sess, fr); + } + + return (*sess->tx_enqueue)(sess, fr); +} + +/* + * aim_get_next_txseqnum() + * + * This increments the tx command count, and returns the seqnum + * that should be stamped on the next FLAP packet sent. This is + * normally called during the final step of packet preparation + * before enqueuement (in aim_tx_enqueue()). + * + */ +flap_seqnum_t aim_get_next_txseqnum(aim_conn_t *conn) +{ + flap_seqnum_t ret; + + ret = ++conn->seqnum; + + return ret; +} + +static int aim_send(int fd, const void *buf, size_t count) +{ + int left, cur; + + for (cur = 0, left = count; left; ) { + int ret; + + ret = send(fd, ((unsigned char *)buf)+cur, left, 0); + if (ret == -1) + return -1; + else if (ret == 0) + return cur; + + cur += ret; + left -= ret; + } + + return cur; +} + +static int aim_bstream_send(aim_bstream_t *bs, aim_conn_t *conn, size_t count) +{ + int wrote = 0; + if (!bs || !conn || (count < 0)) + return -EINVAL; + + if (count > aim_bstream_empty(bs)) + count = aim_bstream_empty(bs); /* truncate to remaining space */ + + if (count) { + if ((conn->type == AIM_CONN_TYPE_RENDEZVOUS) && + (conn->subtype == AIM_CONN_SUBTYPE_OFT_DIRECTIM)) { + /* I strongly suspect that this is a horrible thing to do + * and I feel really guilty doing it. */ + const char *sn = aim_directim_getsn(conn); + aim_rxcallback_t userfunc; + while (count - wrote > 1024) { + wrote = wrote + aim_send(conn->fd, bs->data + bs->offset + wrote, 1024); + if ((userfunc=aim_callhandler(conn->sessv, conn, + AIM_CB_FAM_SPECIAL, + AIM_CB_SPECIAL_IMAGETRANSFER))) + userfunc(conn->sessv, NULL, sn, + count-wrote>1024 ? ((double)wrote / count) : 1); + } + } + if (count - wrote) { + wrote = wrote + aim_send(conn->fd, bs->data + bs->offset + wrote, count - wrote); + } + + } + + bs->offset += wrote; + + return wrote; +} + +static int sendframe_flap(aim_session_t *sess, aim_frame_t *fr) +{ + aim_bstream_t obs; + guint8 *obs_raw; + int payloadlen, err = 0, obslen; + + payloadlen = aim_bstream_curpos(&fr->data); + + if (!(obs_raw = g_malloc(6 + payloadlen))) + return -ENOMEM; + + aim_bstream_init(&obs, obs_raw, 6 + payloadlen); + + /* FLAP header */ + aimbs_put8(&obs, 0x2a); + aimbs_put8(&obs, fr->hdr.flap.type); + aimbs_put16(&obs, fr->hdr.flap.seqnum); + aimbs_put16(&obs, payloadlen); + + /* payload */ + aim_bstream_rewind(&fr->data); + aimbs_putbs(&obs, &fr->data, payloadlen); + + obslen = aim_bstream_curpos(&obs); + aim_bstream_rewind(&obs); + if (aim_bstream_send(&obs, fr->conn, obslen) != obslen) + err = -errno; + + g_free(obs_raw); /* XXX aim_bstream_free */ + + fr->handled = 1; + fr->conn->lastactivity = time(NULL); + + return err; +} + +static int sendframe_oft(aim_session_t *sess, aim_frame_t *fr) +{ + aim_bstream_t hbs; + guint8 *hbs_raw; + int hbslen; + int err = 0; + + hbslen = 8 + fr->hdr.oft.hdr2len; + if (!(hbs_raw = g_malloc(hbslen))) + return -1; + + aim_bstream_init(&hbs, hbs_raw, hbslen); + + aimbs_putraw(&hbs, fr->hdr.oft.magic, 4); + aimbs_put16(&hbs, fr->hdr.oft.hdr2len + 8); + aimbs_put16(&hbs, fr->hdr.oft.type); + aimbs_putraw(&hbs, fr->hdr.oft.hdr2, fr->hdr.oft.hdr2len); + + aim_bstream_rewind(&hbs); + + + if (aim_bstream_send(&hbs, fr->conn, hbslen) != hbslen) { + + err = -errno; + + } else if (aim_bstream_curpos(&fr->data)) { + int len; + + len = aim_bstream_curpos(&fr->data); + aim_bstream_rewind(&fr->data); + + if (aim_bstream_send(&fr->data, fr->conn, len) != len) + err = -errno; + } + + g_free(hbs_raw); /* XXX aim_bstream_free */ + + fr->handled = 1; + fr->conn->lastactivity = time(NULL); + + + return err; + + +} + +int aim_tx_sendframe(aim_session_t *sess, aim_frame_t *fr) +{ + if (fr->hdrtype == AIM_FRAMETYPE_FLAP) + return sendframe_flap(sess, fr); + else if (fr->hdrtype == AIM_FRAMETYPE_OFT) + return sendframe_oft(sess, fr); + return -1; +} + +int aim_tx_flushqueue(aim_session_t *sess) +{ + aim_frame_t *cur; + + for (cur = sess->queue_outgoing; cur; cur = cur->next) { + + if (cur->handled) + continue; /* already been sent */ + + if (cur->conn && (cur->conn->status & AIM_CONN_STATUS_INPROGRESS)) + continue; + + /* + * And now for the meager attempt to force transmit + * latency and avoid missed messages. + */ + if ((cur->conn->lastactivity + cur->conn->forcedlatency) >= time(NULL)) { + /* + * XXX should be a break! we dont want to block the + * upper layers + * + * XXX or better, just do this right. + * + */ + sleep((cur->conn->lastactivity + cur->conn->forcedlatency) - time(NULL)); + } + + /* XXX this should call the custom "queuing" function!! */ + aim_tx_sendframe(sess, cur); + } + + /* purge sent commands from queue */ + aim_tx_purgequeue(sess); + + return 0; +} + +/* + * aim_tx_purgequeue() + * + * This is responsable for removing sent commands from the transmit + * queue. This is not a required operation, but it of course helps + * reduce memory footprint at run time! + * + */ +void aim_tx_purgequeue(aim_session_t *sess) +{ + aim_frame_t *cur, **prev; + + for (prev = &sess->queue_outgoing; (cur = *prev); ) { + + if (cur->handled) { + *prev = cur->next; + + aim_frame_destroy(cur); + + } else + prev = &cur->next; + } + + return; +} + +/** + * aim_tx_cleanqueue - get rid of packets waiting for tx on a dying conn + * @sess: session + * @conn: connection that's dying + * + * for now this simply marks all packets as sent and lets them + * disappear without warning. + * + */ +void aim_tx_cleanqueue(aim_session_t *sess, aim_conn_t *conn) +{ + aim_frame_t *cur; + + for (cur = sess->queue_outgoing; cur; cur = cur->next) { + if (cur->conn == conn) + cur->handled = 1; + } + + return; +} + + diff --git a/protocols/proxy.c b/protocols/proxy.c new file mode 100644 index 00000000..b0196e12 --- /dev/null +++ b/protocols/proxy.c @@ -0,0 +1,672 @@ +/* + * gaim + * + * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net> + * Copyright (C) 2002-2004, Wilmer van der Gaast, Jelmer Vernooij + * + * This program is free software; you can 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 + * + */ + +/* this is a little piece of code to handle proxy connection */ +/* it is intended to : 1st handle http proxy, using the CONNECT command + , 2nd provide an easy way to add socks support */ + +#define BITLBEE_CORE +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#ifndef _WIN32 +#include <sys/socket.h> +#include <netdb.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <unistd.h> +#else +#include "sock.h" +#define ETIMEDOUT WSAETIMEDOUT +#define EINPROGRESS WSAEINPROGRESS +#endif +#include <fcntl.h> +#include <errno.h> +#include "nogaim.h" +#include "proxy.h" + +#define GAIM_READ_COND (G_IO_IN | G_IO_HUP | G_IO_ERR) +#define GAIM_WRITE_COND (G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL) +#define GAIM_ERR_COND (G_IO_HUP | G_IO_ERR | G_IO_NVAL) + +/*FIXME* + #ifndef _WIN32 + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { + closesocket(fd); + g_free(phb); + return -1; + } + fcntl(fd, F_SETFL, 0); +#endif*/ + +char proxyhost[128] = ""; +int proxyport = 0; +int proxytype = PROXY_NONE; +char proxyuser[128] = ""; +char proxypass[128] = ""; + +struct PHB { + GaimInputFunction func, proxy_func; + gpointer data, proxy_data; + char *host; + int port; + int fd; + gint inpa; +}; + +typedef struct _GaimIOClosure { + GaimInputFunction function; + guint result; + gpointer data; +} GaimIOClosure; + + + +static struct sockaddr_in *gaim_gethostbyname(char *host, int port) +{ + static struct sockaddr_in sin; + + if (!inet_aton(host, &sin.sin_addr)) { + struct hostent *hp; + if (!(hp = gethostbyname(host))) { + return NULL; + } + memset(&sin, 0, sizeof(struct sockaddr_in)); + memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); + sin.sin_family = hp->h_addrtype; + } else + sin.sin_family = AF_INET; + sin.sin_port = htons(port); + + return &sin; +} + +static void gaim_io_destroy(gpointer data) +{ + g_free(data); +} + +#ifdef PROXYPROFILER +struct proxyprofiler +{ + GaimInputFunction function; + gpointer data; + + int count; + + struct proxyprofiler *next; +} *pp = NULL; + +void proxyprofiler_dump() +{ + struct proxyprofiler *l; + char s[128]; + FILE *fp; + + sprintf( s, "proxyprofiler.%d", (int) getpid() ); + fp = fopen( s, "w" ); + + fprintf( fp, "%-18s %-18s %10s\n", "Function", "Data", "Count" ); + for( l = pp; l; l = l->next ) + fprintf( fp, "0x%-16x 0x%-16x %10d\n", (int) l->function, (int) l->data, l->count ); + + fclose( fp ); +} +#endif + +static gboolean gaim_io_invoke(GIOChannel *source, GIOCondition condition, gpointer data) +{ + GaimIOClosure *closure = data; + GaimInputCondition gaim_cond = 0; + +#ifdef PROXYPROFILER + struct proxyprofiler *l; + + for( l = pp; l; l = l->next ) + { + if( closure->function == l->function && closure->data == l->data ) + break; + } + if( l ) + { + l->count ++; + } + else + { + l = g_new0( struct proxyprofiler, 1 ); + l->function = closure->function; + l->data = closure->data; + l->count = 1; + + l->next = pp; + pp = l; + } +#endif + + count_io_event(source, "proxy"); + + if (condition & GAIM_READ_COND) + gaim_cond |= GAIM_INPUT_READ; + if (condition & GAIM_WRITE_COND) + gaim_cond |= GAIM_INPUT_WRITE; +// if (condition & GAIM_ERR_COND) +// fprintf( stderr, "ERROR! fd=%d\n", g_io_channel_unix_get_fd( source ) ); + + closure->function(closure->data, g_io_channel_unix_get_fd(source), gaim_cond); + + return TRUE; +} + +static void no_one_calls(gpointer data, gint source, GaimInputCondition cond) +{ + struct PHB *phb = data; + unsigned int len; + int error = ETIMEDOUT; + len = sizeof(error); + +#ifndef _WIN32 + if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { + closesocket(source); + gaim_input_remove(phb->inpa); + if( phb->proxy_func ) + phb->proxy_func(phb->proxy_data, -1, GAIM_INPUT_READ); + else { + phb->func(phb->data, -1, GAIM_INPUT_READ); + g_free(phb); + } + return; + } + fcntl(source, F_SETFL, 0); +#endif + gaim_input_remove(phb->inpa); + if( phb->proxy_func ) + phb->proxy_func(phb->proxy_data, source, GAIM_INPUT_READ); + else { + phb->func(phb->data, source, GAIM_INPUT_READ); + g_free(phb); + } +} + +static int proxy_connect_none(char *host, unsigned short port, struct PHB *phb) +{ + struct sockaddr_in *sin; + int fd = -1; + + if (!(sin = gaim_gethostbyname(host, port))) { + g_free(phb); + return -1; + } + + if ((fd = socket(sin->sin_family, SOCK_STREAM, 0)) < 0) { + g_free(phb); + return -1; + } + + sock_make_nonblocking(fd); + + if (connect(fd, (struct sockaddr *)sin, sizeof(*sin)) < 0) { + if (sockerr_again()) { + phb->inpa = gaim_input_add(fd, GAIM_INPUT_WRITE, no_one_calls, phb); + phb->fd = fd; + } else { + closesocket(fd); + g_free(phb); + return -1; + } + } + + return fd; +} + + +/* Connecting to HTTP proxies */ + +#define HTTP_GOODSTRING "HTTP/1.0 200 Connection established" +#define HTTP_GOODSTRING2 "HTTP/1.1 200 Connection established" + +static void http_canread(gpointer data, gint source, GaimInputCondition cond) +{ + int nlc = 0; + int pos = 0; + struct PHB *phb = data; + char inputline[8192]; + + gaim_input_remove(phb->inpa); + + while ((pos < sizeof(inputline)-1) && (nlc != 2) && (read(source, &inputline[pos++], 1) == 1)) { + if (inputline[pos - 1] == '\n') + nlc++; + else if (inputline[pos - 1] != '\r') + nlc = 0; + } + inputline[pos] = '\0'; + + if ((memcmp(HTTP_GOODSTRING, inputline, strlen(HTTP_GOODSTRING)) == 0) || + (memcmp(HTTP_GOODSTRING2, inputline, strlen(HTTP_GOODSTRING2)) == 0)) { + phb->func(phb->data, source, GAIM_INPUT_READ); + g_free(phb->host); + g_free(phb); + return; + } + + close(source); + phb->func(phb->data, -1, GAIM_INPUT_READ); + g_free(phb->host); + g_free(phb); + return; +} + +static void http_canwrite(gpointer data, gint source, GaimInputCondition cond) +{ + char cmd[384]; + struct PHB *phb = data; + unsigned int len; + int error = ETIMEDOUT; + if (phb->inpa > 0) + gaim_input_remove(phb->inpa); + len = sizeof(error); + if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { + close(source); + phb->func(phb->data, -1, GAIM_INPUT_READ); + g_free(phb->host); + g_free(phb); + return; + } +#ifdef F_SETFL + fcntl(source, F_SETFL, 0); +#endif + + g_snprintf(cmd, sizeof(cmd), "CONNECT %s:%d HTTP/1.1\r\nHost: %s:%d\r\n", phb->host, phb->port, + phb->host, phb->port); + if (send(source, cmd, strlen(cmd), 0) < 0) { + close(source); + phb->func(phb->data, -1, GAIM_INPUT_READ); + g_free(phb->host); + g_free(phb); + return; + } + + if (proxyuser && *proxyuser) { + char *t1, *t2; + t1 = g_strdup_printf("%s:%s", proxyuser, proxypass); + t2 = tobase64(t1); + g_free(t1); + g_snprintf(cmd, sizeof(cmd), "Proxy-Authorization: Basic %s\r\n", t2); + g_free(t2); + if (send(source, cmd, strlen(cmd), 0) < 0) { + close(source); + phb->func(phb->data, -1, GAIM_INPUT_READ); + g_free(phb->host); + g_free(phb); + return; + } + } + + g_snprintf(cmd, sizeof(cmd), "\r\n"); + if (send(source, cmd, strlen(cmd), 0) < 0) { + close(source); + phb->func(phb->data, -1, GAIM_INPUT_READ); + g_free(phb->host); + g_free(phb); + return; + } + + phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, http_canread, phb); +} + +static int proxy_connect_http(char *host, unsigned short port, struct PHB *phb) +{ + phb->host = g_strdup(host); + phb->port = port; + phb->proxy_func = http_canwrite; + phb->proxy_data = phb; + + return( proxy_connect_none( proxyhost, proxyport, phb ) ); +} + + +/* Connecting to SOCKS4 proxies */ + +static void s4_canread(gpointer data, gint source, GaimInputCondition cond) +{ + unsigned char packet[12]; + struct PHB *phb = data; + + gaim_input_remove(phb->inpa); + + memset(packet, 0, sizeof(packet)); + if (read(source, packet, 9) >= 4 && packet[1] == 90) { + phb->func(phb->data, source, GAIM_INPUT_READ); + g_free(phb->host); + g_free(phb); + return; + } + + close(source); + phb->func(phb->data, -1, GAIM_INPUT_READ); + g_free(phb->host); + g_free(phb); +} + +static void s4_canwrite(gpointer data, gint source, GaimInputCondition cond) +{ + unsigned char packet[12]; + struct hostent *hp; + struct PHB *phb = data; + unsigned int len; + int error = ETIMEDOUT; + if (phb->inpa > 0) + gaim_input_remove(phb->inpa); + len = sizeof(error); + if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { + close(source); + phb->func(phb->data, -1, GAIM_INPUT_READ); + g_free(phb->host); + g_free(phb); + return; + } +#ifdef F_SETFL + fcntl(source, F_SETFL, 0); +#endif + + /* XXX does socks4 not support host name lookups by the proxy? */ + if (!(hp = gethostbyname(phb->host))) { + close(source); + phb->func(phb->data, -1, GAIM_INPUT_READ); + g_free(phb->host); + g_free(phb); + return; + } + + packet[0] = 4; + packet[1] = 1; + packet[2] = phb->port >> 8; + packet[3] = phb->port & 0xff; + packet[4] = (unsigned char)(hp->h_addr_list[0])[0]; + packet[5] = (unsigned char)(hp->h_addr_list[0])[1]; + packet[6] = (unsigned char)(hp->h_addr_list[0])[2]; + packet[7] = (unsigned char)(hp->h_addr_list[0])[3]; + packet[8] = 0; + if (write(source, packet, 9) != 9) { + close(source); + phb->func(phb->data, -1, GAIM_INPUT_READ); + g_free(phb->host); + g_free(phb); + return; + } + + phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, s4_canread, phb); +} + +static int proxy_connect_socks4(char *host, unsigned short port, struct PHB *phb) +{ + phb->host = g_strdup(host); + phb->port = port; + phb->proxy_func = s4_canwrite; + phb->proxy_data = phb; + + return( proxy_connect_none( proxyhost, proxyport, phb ) ); +} + + +/* Connecting to SOCKS5 proxies */ + +static void s5_canread_again(gpointer data, gint source, GaimInputCondition cond) +{ + unsigned char buf[512]; + struct PHB *phb = data; + + gaim_input_remove(phb->inpa); + + if (read(source, buf, 10) < 10) { + close(source); + phb->func(phb->data, -1, GAIM_INPUT_READ); + g_free(phb->host); + g_free(phb); + return; + } + if ((buf[0] != 0x05) || (buf[1] != 0x00)) { + close(source); + phb->func(phb->data, -1, GAIM_INPUT_READ); + g_free(phb->host); + g_free(phb); + return; + } + + phb->func(phb->data, source, GAIM_INPUT_READ); + g_free(phb->host); + g_free(phb); + return; +} + +static void s5_sendconnect(gpointer data, gint source) +{ + unsigned char buf[512]; + struct PHB *phb = data; + int hlen = strlen(phb->host); + + buf[0] = 0x05; + buf[1] = 0x01; /* CONNECT */ + buf[2] = 0x00; /* reserved */ + buf[3] = 0x03; /* address type -- host name */ + buf[4] = hlen; + memcpy(buf + 5, phb->host, hlen); + buf[5 + strlen(phb->host)] = phb->port >> 8; + buf[5 + strlen(phb->host) + 1] = phb->port & 0xff; + + if (write(source, buf, (5 + strlen(phb->host) + 2)) < (5 + strlen(phb->host) + 2)) { + close(source); + phb->func(phb->data, -1, GAIM_INPUT_READ); + g_free(phb->host); + g_free(phb); + return; + } + + phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, s5_canread_again, phb); +} + +static void s5_readauth(gpointer data, gint source, GaimInputCondition cond) +{ + unsigned char buf[512]; + struct PHB *phb = data; + + gaim_input_remove(phb->inpa); + + if (read(source, buf, 2) < 2) { + close(source); + phb->func(phb->data, -1, GAIM_INPUT_READ); + g_free(phb->host); + g_free(phb); + return; + } + + if ((buf[0] != 0x01) || (buf[1] != 0x00)) { + close(source); + phb->func(phb->data, -1, GAIM_INPUT_READ); + g_free(phb->host); + g_free(phb); + return; + } + + s5_sendconnect(phb, source); +} + +static void s5_canread(gpointer data, gint source, GaimInputCondition cond) +{ + unsigned char buf[512]; + struct PHB *phb = data; + + gaim_input_remove(phb->inpa); + + if (read(source, buf, 2) < 2) { + close(source); + phb->func(phb->data, -1, GAIM_INPUT_READ); + g_free(phb->host); + g_free(phb); + return; + } + + if ((buf[0] != 0x05) || (buf[1] == 0xff)) { + close(source); + phb->func(phb->data, -1, GAIM_INPUT_READ); + g_free(phb->host); + g_free(phb); + return; + } + + if (buf[1] == 0x02) { + unsigned int i = strlen(proxyuser), j = strlen(proxypass); + buf[0] = 0x01; /* version 1 */ + buf[1] = i; + memcpy(buf + 2, proxyuser, i); + buf[2 + i] = j; + memcpy(buf + 2 + i + 1, proxypass, j); + if (write(source, buf, 3 + i + j) < 3 + i + j) { + close(source); + phb->func(phb->data, -1, GAIM_INPUT_READ); + g_free(phb->host); + g_free(phb); + return; + } + + phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, s5_readauth, phb); + } else { + s5_sendconnect(phb, source); + } +} + +static void s5_canwrite(gpointer data, gint source, GaimInputCondition cond) +{ + unsigned char buf[512]; + int i; + struct PHB *phb = data; + unsigned int len; + int error = ETIMEDOUT; + if (phb->inpa > 0) + gaim_input_remove(phb->inpa); + len = sizeof(error); + if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { + close(source); + phb->func(phb->data, -1, GAIM_INPUT_READ); + g_free(phb->host); + g_free(phb); + return; + } +#ifdef F_SETFL + fcntl(source, F_SETFL, 0); +#endif + + i = 0; + buf[0] = 0x05; /* SOCKS version 5 */ + if (proxyuser[0]) { + buf[1] = 0x02; /* two methods */ + buf[2] = 0x00; /* no authentication */ + buf[3] = 0x02; /* username/password authentication */ + i = 4; + } else { + buf[1] = 0x01; + buf[2] = 0x00; + i = 3; + } + + if (write(source, buf, i) < i) { + close(source); + phb->func(phb->data, -1, GAIM_INPUT_READ); + g_free(phb->host); + g_free(phb); + return; + } + + phb->inpa = gaim_input_add(source, GAIM_INPUT_READ, s5_canread, phb); +} + +static int proxy_connect_socks5(char *host, unsigned short port, struct PHB *phb) +{ + phb->host = g_strdup(host); + phb->port = port; + phb->proxy_func = s5_canwrite; + phb->proxy_data = phb; + + return( proxy_connect_none( proxyhost, proxyport, phb ) ); +} + + +/* Export functions */ + +gint gaim_input_add(gint source, GaimInputCondition condition, GaimInputFunction function, gpointer data) +{ + GaimIOClosure *closure = g_new0(GaimIOClosure, 1); + GIOChannel *channel; + GIOCondition cond = 0; + + closure->function = function; + closure->data = data; + + if (condition & GAIM_INPUT_READ) + cond |= GAIM_READ_COND; + if (condition & GAIM_INPUT_WRITE) + cond |= GAIM_WRITE_COND; + + channel = g_io_channel_unix_new(source); + closure->result = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, cond, + gaim_io_invoke, closure, gaim_io_destroy); + + g_io_channel_unref(channel); + return closure->result; +} + +void gaim_input_remove(gint tag) +{ + if (tag > 0) + g_source_remove(tag); +} + +int proxy_connect(char *host, int port, GaimInputFunction func, gpointer data) +{ + struct PHB *phb; + + if (!host || !port || (port == -1) || !func || strlen(host) > 128) { + return -1; + } + + phb = g_new0(struct PHB, 1); + phb->func = func; + phb->data = data; + +#ifndef _WIN32 + sethostent(1); +#endif + + if ((proxytype == PROXY_NONE) || !proxyhost || !proxyhost[0] || !proxyport || (proxyport == -1)) + return proxy_connect_none(host, port, phb); + else if (proxytype == PROXY_HTTP) + return proxy_connect_http(host, port, phb); + else if (proxytype == PROXY_SOCKS4) + return proxy_connect_socks4(host, port, phb); + else if (proxytype == PROXY_SOCKS5) + return proxy_connect_socks5(host, port, phb); + + if (phb->host) g_free(phb); + g_free(phb); + return -1; +} diff --git a/protocols/proxy.h b/protocols/proxy.h new file mode 100644 index 00000000..7c34fc40 --- /dev/null +++ b/protocols/proxy.h @@ -0,0 +1,60 @@ +/* + * gaim + * + * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net> + * + * This program is free software; you can 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 + * + */ + +/* this is the export part of the proxy.c file. it does a little + prototype-ing stuff and redefine some net function to mask them + with some kind of transparent layer */ + +#ifndef _PROXY_H_ +#define _PROXY_H_ + +#include <sys/types.h> +#ifndef _WIN32 +#include <sys/socket.h> +#include <netdb.h> +#include <netinet/in.h> +#endif +#include <glib.h> +#include <gmodule.h> + +#define PROXY_NONE 0 +#define PROXY_HTTP 1 +#define PROXY_SOCKS4 2 +#define PROXY_SOCKS5 3 + +extern char proxyhost[128]; +extern int proxyport; +extern int proxytype; +extern char proxyuser[128]; +extern char proxypass[128]; + +typedef enum { + GAIM_INPUT_READ = 1 << 0, + GAIM_INPUT_WRITE = 1 << 1 +} GaimInputCondition; +typedef void (*GaimInputFunction)(gpointer, gint, GaimInputCondition); + +G_MODULE_EXPORT gint gaim_input_add(int, GaimInputCondition, GaimInputFunction, gpointer); +G_MODULE_EXPORT void gaim_input_remove(gint); + +G_MODULE_EXPORT int proxy_connect(char *host, int port, GaimInputFunction func, gpointer data); + +#endif /* _PROXY_H_ */ diff --git a/protocols/sha.c b/protocols/sha.c new file mode 100644 index 00000000..895505a1 --- /dev/null +++ b/protocols/sha.c @@ -0,0 +1,173 @@ +/* + * The contents of this file are subject to the Mozilla Public + * License Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS + * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + * implied. See the License for the specific language governing + * rights and limitations under the License. + * + * The Original Code is SHA 180-1 Reference Implementation (Compact version) + * + * The Initial Developer of the Original Code is Paul Kocher of + * Cryptography Research. Portions created by Paul Kocher are + * Copyright (C) 1995-9 by Cryptography Research, Inc. All + * Rights Reserved. + * + * Contributor(s): + * + */ + +#define BITLBEE_CORE +#include "nogaim.h" + +static void shaHashBlock(SHA_CTX *ctx); + +void shaInit(SHA_CTX *ctx) { + int i; + + ctx->lenW = 0; + ctx->sizeHi = ctx->sizeLo = 0; + + /* Initialize H with the magic constants (see FIPS180 for constants) + */ + ctx->H[0] = 0x67452301L; + ctx->H[1] = 0xefcdab89L; + ctx->H[2] = 0x98badcfeL; + ctx->H[3] = 0x10325476L; + ctx->H[4] = 0xc3d2e1f0L; + + for (i = 0; i < 80; i++) + ctx->W[i] = 0; +} + + +void shaUpdate(SHA_CTX *ctx, unsigned char *dataIn, int len) { + int i; + + /* Read the data into W and process blocks as they get full + */ + for (i = 0; i < len; i++) { + ctx->W[ctx->lenW / 4] <<= 8; + ctx->W[ctx->lenW / 4] |= (guint32)dataIn[i]; + if ((++ctx->lenW) % 64 == 0) { + shaHashBlock(ctx); + ctx->lenW = 0; + } + ctx->sizeLo += 8; + ctx->sizeHi += (ctx->sizeLo < 8); + } +} + + +void shaFinal(SHA_CTX *ctx, unsigned char hashout[20]) { + unsigned char pad0x80 = 0x80; + unsigned char pad0x00 = 0x00; + unsigned char padlen[8]; + int i; + + /* Pad with a binary 1 (e.g. 0x80), then zeroes, then length + */ + padlen[0] = (unsigned char)((ctx->sizeHi >> 24) & 255); + padlen[1] = (unsigned char)((ctx->sizeHi >> 16) & 255); + padlen[2] = (unsigned char)((ctx->sizeHi >> 8) & 255); + padlen[3] = (unsigned char)((ctx->sizeHi >> 0) & 255); + padlen[4] = (unsigned char)((ctx->sizeLo >> 24) & 255); + padlen[5] = (unsigned char)((ctx->sizeLo >> 16) & 255); + padlen[6] = (unsigned char)((ctx->sizeLo >> 8) & 255); + padlen[7] = (unsigned char)((ctx->sizeLo >> 0) & 255); + shaUpdate(ctx, &pad0x80, 1); + while (ctx->lenW != 56) + shaUpdate(ctx, &pad0x00, 1); + shaUpdate(ctx, padlen, 8); + + /* Output hash + */ + for (i = 0; i < 20; i++) { + hashout[i] = (unsigned char)(ctx->H[i / 4] >> 24); + ctx->H[i / 4] <<= 8; + } + + /* + * Re-initialize the context (also zeroizes contents) + */ + shaInit(ctx); +} + + +void shaBlock(unsigned char *dataIn, int len, unsigned char hashout[20]) { + SHA_CTX ctx; + + shaInit(&ctx); + shaUpdate(&ctx, dataIn, len); + shaFinal(&ctx, hashout); +} + + +#define SHA_ROTL(X,n) ((((X) << (n)) | ((X) >> (32-(n)))) & 0xffffffffL) + +static void shaHashBlock(SHA_CTX *ctx) { + int t; + guint32 A,B,C,D,E,TEMP; + + for (t = 16; t <= 79; t++) + ctx->W[t] = + SHA_ROTL(ctx->W[t-3] ^ ctx->W[t-8] ^ ctx->W[t-14] ^ ctx->W[t-16], 1); + + A = ctx->H[0]; + B = ctx->H[1]; + C = ctx->H[2]; + D = ctx->H[3]; + E = ctx->H[4]; + + for (t = 0; t <= 19; t++) { + TEMP = (SHA_ROTL(A,5) + (((C^D)&B)^D) + E + ctx->W[t] + 0x5a827999L) & 0xffffffffL; + E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP; + } + for (t = 20; t <= 39; t++) { + TEMP = (SHA_ROTL(A,5) + (B^C^D) + E + ctx->W[t] + 0x6ed9eba1L) & 0xffffffffL; + E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP; + } + for (t = 40; t <= 59; t++) { + TEMP = (SHA_ROTL(A,5) + ((B&C)|(D&(B|C))) + E + ctx->W[t] + 0x8f1bbcdcL) & 0xffffffffL; + E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP; + } + for (t = 60; t <= 79; t++) { + TEMP = (SHA_ROTL(A,5) + (B^C^D) + E + ctx->W[t] + 0xca62c1d6L) & 0xffffffffL; + E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP; + } + + ctx->H[0] += A; + ctx->H[1] += B; + ctx->H[2] += C; + ctx->H[3] += D; + ctx->H[4] += E; +} + +/*---------------------------------------------------------------------------- + * + * This code added by Thomas "temas" Muldowney for Jabber compatability + * + *---------------------------------------------------------------------------*/ +char *shahash(char *str) +{ + static char final[41]; + char *pos; + unsigned char hashval[20]; + int x; + + if(!str || strlen(str) == 0) + return NULL; + + shaBlock((unsigned char *)str, strlen(str), hashval); + + pos = final; + for(x=0;x<20;x++) + { + g_snprintf(pos, 3, "%02x", hashval[x]); + pos += 2; + } + return (char *)final; +} diff --git a/protocols/sha.h b/protocols/sha.h new file mode 100644 index 00000000..e8152b1b --- /dev/null +++ b/protocols/sha.h @@ -0,0 +1,21 @@ +#ifndef __SHA_H__ +#define __SHA_H__ + +#include <gmodule.h> + +G_MODULE_EXPORT int strprintsha(char *dest, int *hashval); + +typedef struct { + guint32 H[5]; + guint32 W[80]; + int lenW; + guint32 sizeHi,sizeLo; +} SHA_CTX; + +G_MODULE_EXPORT void shaInit(SHA_CTX *ctx); +G_MODULE_EXPORT void shaUpdate(SHA_CTX *ctx, unsigned char *dataIn, int len); +G_MODULE_EXPORT void shaFinal(SHA_CTX *ctx, unsigned char hashout[20]); +G_MODULE_EXPORT void shaBlock(unsigned char *dataIn, int len, unsigned char hashout[20]); +G_MODULE_EXPORT char *shahash(char *str); + +#endif diff --git a/protocols/ssl_bogus.c b/protocols/ssl_bogus.c new file mode 100644 index 00000000..226997db --- /dev/null +++ b/protocols/ssl_bogus.c @@ -0,0 +1,50 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* SSL module - dummy version */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "ssl_client.h" + +void *ssl_connect( char *host, int port, SslInputFunction func, gpointer data ) +{ + return( NULL ); +} + +int ssl_read( void *conn, char *buf, int len ) +{ + return( -1 ); +} + +int ssl_write( void *conn, const char *buf, int len ) +{ + return( -1 ); +} + +void ssl_disconnect( void *conn_ ) +{ +} + +int ssl_getfd( void *conn ) +{ + return( -1 ); +} diff --git a/protocols/ssl_client.h b/protocols/ssl_client.h new file mode 100644 index 00000000..a829a7b1 --- /dev/null +++ b/protocols/ssl_client.h @@ -0,0 +1,35 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* SSL module */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <glib.h> +#include "proxy.h" + +typedef void (*SslInputFunction)(gpointer, void*, GaimInputCondition); + +G_MODULE_EXPORT void *ssl_connect( char *host, int port, SslInputFunction func, gpointer data ); +G_MODULE_EXPORT int ssl_read( void *conn, char *buf, int len ); +G_MODULE_EXPORT int ssl_write( void *conn, const char *buf, int len ); +G_MODULE_EXPORT void ssl_disconnect( void *conn_ ); +G_MODULE_EXPORT int ssl_getfd( void *conn ); diff --git a/protocols/ssl_gnutls.c b/protocols/ssl_gnutls.c new file mode 100644 index 00000000..535d126e --- /dev/null +++ b/protocols/ssl_gnutls.c @@ -0,0 +1,136 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* SSL module - GnuTLS version */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <gnutls/gnutls.h> +#include "proxy.h" +#include "ssl_client.h" +#include "sock.h" +#include "stdlib.h" + +static gboolean initialized = FALSE; + +struct scd +{ + SslInputFunction func; + gpointer data; + int fd; + gboolean established; + + gnutls_session session; + gnutls_certificate_credentials xcred; +}; + +static void ssl_connected( gpointer data, gint source, GaimInputCondition cond ); + + + +void *ssl_connect( char *host, int port, SslInputFunction func, gpointer data ) +{ + struct scd *conn = g_new0( struct scd, 1 ); + + conn->fd = proxy_connect( host, port, ssl_connected, conn ); + conn->func = func; + conn->data = data; + + if( conn->fd < 0 ) + { + g_free( conn ); + return( NULL ); + } + + if( !initialized ) + { + gnutls_global_init(); + initialized = TRUE; + atexit( gnutls_global_deinit ); + } + + gnutls_certificate_allocate_credentials( &conn->xcred ); + gnutls_init( &conn->session, GNUTLS_CLIENT ); + gnutls_set_default_priority( conn->session ); + gnutls_credentials_set( conn->session, GNUTLS_CRD_CERTIFICATE, conn->xcred ); + + return( conn ); +} + +static void ssl_connected( gpointer data, gint source, GaimInputCondition cond ) +{ + struct scd *conn = data; + + if( source == -1 ) + goto ssl_connected_failure; + + gnutls_transport_set_ptr( conn->session, (gnutls_transport_ptr) conn->fd ); + + if( gnutls_handshake( conn->session ) < 0 ) + goto ssl_connected_failure; + + conn->established = TRUE; + conn->func( conn->data, conn, cond ); + return; + +ssl_connected_failure: + conn->func( conn->data, NULL, cond ); + + gnutls_deinit( conn->session ); + gnutls_certificate_free_credentials( conn->xcred ); + if( source >= 0 ) closesocket( source ); + g_free( conn ); +} + +int ssl_read( void *conn, char *buf, int len ) +{ + if( !((struct scd*)conn)->established ) + return( 0 ); + + return( gnutls_record_recv( ((struct scd*)conn)->session, buf, len ) ); +} + +int ssl_write( void *conn, const char *buf, int len ) +{ + if( !((struct scd*)conn)->established ) + return( 0 ); + + return( gnutls_record_send( ((struct scd*)conn)->session, buf, len ) ); +} + +void ssl_disconnect( void *conn_ ) +{ + struct scd *conn = conn_; + + if( conn->established ) + gnutls_bye( conn->session, GNUTLS_SHUT_WR ); + + closesocket( conn->fd ); + + gnutls_deinit( conn->session ); + gnutls_certificate_free_credentials( conn->xcred ); + g_free( conn ); +} + +int ssl_getfd( void *conn ) +{ + return( ((struct scd*)conn)->fd ); +} diff --git a/protocols/ssl_nss.c b/protocols/ssl_nss.c new file mode 100644 index 00000000..0815f952 --- /dev/null +++ b/protocols/ssl_nss.c @@ -0,0 +1,180 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* SSL module - NSS version */ + +/* Copyright 2004 Jelmer Vernooij */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "bitlbee.h" +#include "proxy.h" +#include "ssl_client.h" +#include "sock.h" +#include <nspr.h> +#include <prio.h> +#include <sslproto.h> +#include <nss.h> +#include <private/pprio.h> +#include <ssl.h> +#include <secerr.h> +#include <sslerr.h> + +static gboolean initialized = FALSE; + +struct scd +{ + SslInputFunction func; + gpointer data; + int fd; + PRFileDesc *prfd; + gboolean established; +}; + +static void ssl_connected( gpointer data, gint source, GaimInputCondition cond ); + + +static SECStatus nss_auth_cert (void *arg, PRFileDesc *socket, PRBool checksig, PRBool isserver) +{ + return SECSuccess; +} + +static SECStatus nss_bad_cert (void *arg, PRFileDesc *socket) +{ + PRErrorCode err; + + if(!arg) return SECFailure; + + *(PRErrorCode *)arg = err = PORT_GetError(); + + switch(err) { + case SEC_ERROR_INVALID_AVA: + case SEC_ERROR_INVALID_TIME: + case SEC_ERROR_BAD_SIGNATURE: + case SEC_ERROR_EXPIRED_CERTIFICATE: + case SEC_ERROR_UNKNOWN_ISSUER: + case SEC_ERROR_UNTRUSTED_CERT: + case SEC_ERROR_CERT_VALID: + case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE: + case SEC_ERROR_CRL_EXPIRED: + case SEC_ERROR_CRL_BAD_SIGNATURE: + case SEC_ERROR_EXTENSION_VALUE_INVALID: + case SEC_ERROR_CA_CERT_INVALID: + case SEC_ERROR_CERT_USAGES_INVALID: + case SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION: + return SECSuccess; + + default: + return SECFailure; + } +} + + +void *ssl_connect( char *host, int port, SslInputFunction func, gpointer data ) +{ + struct scd *conn = g_new0( struct scd, 1 ); + + conn->fd = proxy_connect( host, port, ssl_connected, conn ); + conn->func = func; + conn->data = data; + + if( conn->fd < 0 ) + { + g_free( conn ); + return( NULL ); + } + + if( !initialized ) + { + PR_Init( PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); + NSS_NoDB_Init(NULL); + NSS_SetDomesticPolicy(); + } + + + return( conn ); +} + +static void ssl_connected( gpointer data, gint source, GaimInputCondition cond ) +{ + struct scd *conn = data; + + if( source == -1 ) + goto ssl_connected_failure; + + + + + conn->prfd = SSL_ImportFD(NULL, PR_ImportTCPSocket(source)); + SSL_OptionSet(conn->prfd, SSL_SECURITY, PR_TRUE); + SSL_OptionSet(conn->prfd, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE); + SSL_BadCertHook(conn->prfd, (SSLBadCertHandler)nss_bad_cert, NULL); + SSL_AuthCertificateHook(conn->prfd, (SSLAuthCertificate)nss_auth_cert, (void *)CERT_GetDefaultCertDB()); + SSL_ResetHandshake(conn->prfd, PR_FALSE); + + if (SSL_ForceHandshake(conn->prfd)) { + goto ssl_connected_failure; + } + + + conn->established = TRUE; + conn->func( conn->data, conn, cond ); + return; + + ssl_connected_failure: + + conn->func( conn->data, NULL, cond ); + + PR_Close( conn -> prfd ); + if( source >= 0 ) closesocket( source ); + g_free( conn ); +} + +int ssl_read( void *conn, char *buf, int len ) +{ + if( !((struct scd*)conn)->established ) + return( 0 ); + + return( PR_Read( ((struct scd*)conn)->prfd, buf, len ) ); +} + +int ssl_write( void *conn, const char *buf, int len ) +{ + if( !((struct scd*)conn)->established ) + return( 0 ); + + return( PR_Write ( ((struct scd*)conn)->prfd, buf, len ) ); +} + +void ssl_disconnect( void *conn_ ) +{ + struct scd *conn = conn_; + + PR_Close( conn->prfd ); + closesocket( conn->fd ); + + g_free( conn ); +} + +int ssl_getfd( void *conn ) +{ + return( ((struct scd*)conn)->fd ); +} diff --git a/protocols/ssl_openssl.c b/protocols/ssl_openssl.c new file mode 100644 index 00000000..d2a7e1fe --- /dev/null +++ b/protocols/ssl_openssl.c @@ -0,0 +1,158 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* SSL module - GnuTLS version */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <openssl/crypto.h> +#include <openssl/rand.h> +#include <openssl/x509.h> +#include <openssl/pem.h> +#include <openssl/ssl.h> +#include <openssl/err.h> + +#include "proxy.h" +#include "ssl_client.h" +#include "sock.h" + +static gboolean initialized = FALSE; + +struct scd +{ + SslInputFunction func; + gpointer data; + int fd; + gboolean established; + + SSL *ssl; + SSL_CTX *ssl_ctx; +}; + +static void ssl_connected( gpointer data, gint source, GaimInputCondition cond ); + + + +void *ssl_connect( char *host, int port, SslInputFunction func, gpointer data ) +{ + struct scd *conn = g_new0( struct scd, 1 ); + SSL_METHOD *meth; + + conn->fd = proxy_connect( host, port, ssl_connected, conn ); + conn->func = func; + conn->data = data; + + if( conn->fd < 0 ) + { + g_free( conn ); + return( NULL ); + } + + if( !initialized ) + { + initialized = TRUE; + SSLeay_add_ssl_algorithms(); + } + + meth = TLSv1_client_method(); + conn->ssl_ctx = SSL_CTX_new( meth ); + if( conn->ssl_ctx == NULL ) + { + conn->fd = -1; + return( NULL ); + } + + conn->ssl = SSL_new( conn->ssl_ctx ); + if( conn->ssl == NULL ) + { + conn->fd = -1; + return( NULL ); + } + + return( conn ); +} + +static void ssl_connected( gpointer data, gint source, GaimInputCondition cond ) +{ + struct scd *conn = data; + + if( source == -1 ) + goto ssl_connected_failure; + + SSL_set_fd( conn->ssl, conn->fd ); + + if( SSL_connect( conn->ssl ) < 0 ) + goto ssl_connected_failure; + + conn->established = TRUE; + conn->func( conn->data, conn, cond ); + return; + +ssl_connected_failure: + conn->func( conn->data, NULL, cond ); + + if( conn->ssl ) + { + SSL_shutdown( conn->ssl ); + SSL_free( conn->ssl ); + } + if( conn->ssl_ctx ) + { + SSL_CTX_free( conn->ssl_ctx ); + } + if( source >= 0 ) closesocket( source ); + g_free( conn ); +} + +int ssl_read( void *conn, char *buf, int len ) +{ + if( !((struct scd*)conn)->established ) + return( 0 ); + + return( SSL_read( ((struct scd*)conn)->ssl, buf, len ) ); +} + +int ssl_write( void *conn, const char *buf, int len ) +{ + if( !((struct scd*)conn)->established ) + return( 0 ); + + return( SSL_write( ((struct scd*)conn)->ssl, buf, len ) ); +} + +void ssl_disconnect( void *conn_ ) +{ + struct scd *conn = conn_; + + if( conn->established ) + SSL_shutdown( conn->ssl ); + + closesocket( conn->fd ); + + SSL_free( conn->ssl ); + SSL_CTX_free( conn->ssl_ctx ); + g_free( conn ); +} + +int ssl_getfd( void *conn ) +{ + return( ((struct scd*)conn)->fd ); +} diff --git a/protocols/util.c b/protocols/util.c new file mode 100644 index 00000000..073ee36d --- /dev/null +++ b/protocols/util.c @@ -0,0 +1,415 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* + * nogaim + * + * Gaim without gaim - for BitlBee + * + * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net> + * (and possibly other members of the Gaim team) + * Copyright 2002-2004 Wilmer van der Gaast <lintux@lintux.cx> + */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +/* Parts from util.c from gaim needed by nogaim */ +#define BITLBEE_CORE +#include "nogaim.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <glib.h> +#include <time.h> + +char *utf8_to_str(const char *in) +{ + int n = 0, i = 0; + int inlen; + char *result; + + if (!in) + return NULL; + + inlen = strlen(in); + + result = g_malloc(inlen + 1); + + while (n <= inlen - 1) { + long c = (long)in[n]; + if (c < 0x80) + result[i++] = (char)c; + else { + if ((c & 0xC0) == 0xC0) + result[i++] = + (char)(((c & 0x03) << 6) | (((unsigned char)in[++n]) & 0x3F)); + else if ((c & 0xE0) == 0xE0) { + if (n + 2 <= inlen) { + result[i] = + (char)(((c & 0xF) << 4) | (((unsigned char)in[++n]) & 0x3F)); + result[i] = + (char)(((unsigned char)result[i]) | + (((unsigned char)in[++n]) & 0x3F)); + i++; + } else + n += 2; + } else if ((c & 0xF0) == 0xF0) + n += 3; + else if ((c & 0xF8) == 0xF8) + n += 4; + else if ((c & 0xFC) == 0xFC) + n += 5; + } + n++; + } + result[i] = '\0'; + + return result; +} + +char *str_to_utf8(const char *in) +{ + int n = 0, i = 0; + int inlen; + char *result = NULL; + + if (!in) + return NULL; + + inlen = strlen(in); + + result = g_malloc(inlen * 2 + 1); + + while (n < inlen) { + long c = (long)in[n]; + if (c == 27) { + n += 2; + if (in[n] == 'x') + n++; + if (in[n] == '3') + n++; + n += 2; + continue; + } + /* why are we removing newlines and carriage returns? + if ((c == 0x0D) || (c == 0x0A)) { + n++; + continue; + } + */ + if (c < 128) + result[i++] = (char)c; + else { + result[i++] = (char)((c >> 6) | 192); + result[i++] = (char)((c & 63) | 128); + } + n++; + } + result[i] = '\0'; + + return result; +} + +void strip_linefeed(gchar *text) +{ + int i, j; + gchar *text2 = g_malloc(strlen(text) + 1); + + for (i = 0, j = 0; text[i]; i++) + if (text[i] != '\r') + text2[j++] = text[i]; + text2[j] = '\0'; + + strcpy(text, text2); + g_free(text2); +} + +char *add_cr(char *text) +{ + char *ret = NULL; + int count = 0, j; + unsigned int i; + + if (text[0] == '\n') + count++; + for (i = 1; i < strlen(text); i++) + if (text[i] == '\n' && text[i - 1] != '\r') + count++; + + if (count == 0) + return g_strdup(text); + + ret = g_malloc0(strlen(text) + count + 1); + + i = 0; j = 0; + if (text[i] == '\n') + ret[j++] = '\r'; + ret[j++] = text[i++]; + for (; i < strlen(text); i++) { + if (text[i] == '\n' && text[i - 1] != '\r') + ret[j++] = '\r'; + ret[j++] = text[i]; + } + + return ret; +} + +static char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" "0123456789+/"; + +/* XXX Find bug */ +char *tobase64(const char *text) +{ + char *out = NULL; + const char *c; + unsigned int tmp = 0; + int len = 0, n = 0; + + c = text; + + while (*c) { + tmp = tmp << 8; + tmp += *c; + n++; + + if (n == 3) { + out = g_realloc(out, len + 4); + out[len] = alphabet[(tmp >> 18) & 0x3f]; + out[len + 1] = alphabet[(tmp >> 12) & 0x3f]; + out[len + 2] = alphabet[(tmp >> 6) & 0x3f]; + out[len + 3] = alphabet[tmp & 0x3f]; + len += 4; + tmp = 0; + n = 0; + } + c++; + } + switch (n) { + + case 2: + tmp <<= 8; + out = g_realloc(out, len + 5); + out[len] = alphabet[(tmp >> 18) & 0x3f]; + out[len + 1] = alphabet[(tmp >> 12) & 0x3f]; + out[len + 2] = alphabet[(tmp >> 6) & 0x3f]; + out[len + 3] = '='; + out[len + 4] = 0; + break; + case 1: + tmp <<= 16; + out = g_realloc(out, len + 5); + out[len] = alphabet[(tmp >> 18) & 0x3f]; + out[len + 1] = alphabet[(tmp >> 12) & 0x3f]; + out[len + 2] = '='; + out[len + 3] = '='; + out[len + 4] = 0; + break; + case 0: + out = g_realloc(out, len + 1); + out[len] = 0; + break; + } + return out; +} + +char *normalize(const char *s) +{ + static char buf[BUF_LEN]; + char *t, *u; + int x = 0; + + g_return_val_if_fail((s != NULL), NULL); + + u = t = g_strdup(s); + + strcpy(t, s); + g_strdown(t); + + while (*t && (x < BUF_LEN - 1)) { + if (*t != ' ') { + buf[x] = *t; + x++; + } + t++; + } + buf[x] = '\0'; + g_free(u); + return buf; +} + +time_t get_time(int year, int month, int day, int hour, int min, int sec) +{ + struct tm tm; + + tm.tm_year = year - 1900; + tm.tm_mon = month - 1; + tm.tm_mday = day; + tm.tm_hour = hour; + tm.tm_min = min; + tm.tm_sec = sec >= 0 ? sec : time(NULL) % 60; + return mktime(&tm); +} + +typedef struct htmlentity +{ + char code[8]; + char is; +} htmlentity_t; + +/* FIXME: This is ISO8859-1(5) centric, so might cause problems with other charsets. */ + +static htmlentity_t ent[] = +{ + { "lt", '<' }, + { "gt", '>' }, + { "amp", '&' }, + { "quot", '"' }, + { "aacute", 'á' }, + { "eacute", 'é' }, + { "iacute", 'é' }, + { "oacute", 'ó' }, + { "uacute", 'ú' }, + { "agrave", 'à' }, + { "egrave", 'è' }, + { "igrave", 'ì' }, + { "ograve", 'ò' }, + { "ugrave", 'ù' }, + { "acirc", 'â' }, + { "ecirc", 'ê' }, + { "icirc", 'î' }, + { "ocirc", 'ô' }, + { "ucirc", 'û' }, + { "nbsp", ' ' }, + { "", 0 } +}; + +void strip_html( char *in ) +{ + char *start = in; + char *out = g_malloc( strlen( in ) + 1 ); + char *s = out, *cs; + int i, matched; + + memset( out, 0, strlen( in ) + 1 ); + + while( *in ) + { + if( *in == '<' && ( isalpha( *(in+1) ) || *(in+1) == '/' ) ) + { + /* If in points at a < and in+1 points at a letter or a slash, this is probably + a HTML-tag. Try to find a closing > and continue there. If the > can't be + found, assume that it wasn't a HTML-tag after all. */ + + cs = in; + + while( *in && *in != '>' ) + in ++; + + if( *in ) + { + if( g_strncasecmp( cs+1, "br", 2) == 0 ) + *(s++) = '\n'; + in ++; + } + else + { + in = cs; + *(s++) = *(in++); + } + } + else if( *in == '&' ) + { + cs = ++in; + while( *in && isalpha( *in ) ) + in ++; + + if( *in == ';' ) in ++; + matched = 0; + + for( i = 0; *ent[i].code; i ++ ) + if( g_strncasecmp( ent[i].code, cs, strlen( ent[i].code ) ) == 0 ) + { + *(s++) = ent[i].is; + matched = 1; + break; + } + + /* None of the entities were matched, so return the string */ + if( !matched ) + { + in = cs - 1; + *(s++) = *(in++); + } + } + else + { + *(s++) = *(in++); + } + } + + strcpy( start, out ); + g_free( out ); +} + +char *escape_html( const char *html ) +{ + const char *c = html; + GString *ret; + char *str; + + if( html == NULL ) + return( NULL ); + if( g_strncasecmp( html, "<html>", 6 ) == 0 ) + return( g_strdup( html ) ); + + ret = g_string_new( "" ); + + while( *c ) + { + switch( *c ) + { + case '&': + ret = g_string_append( ret, "&" ); + break; + case '<': + ret = g_string_append( ret, "<" ); + break; + case '>': + ret = g_string_append( ret, ">" ); + break; + case '"': + ret = g_string_append( ret, """ ); + break; + default: + ret = g_string_append_c( ret, *c ); + } + c ++; + } + + str = ret->str; + g_string_free( ret, FALSE ); + return( str ); +} + +void info_string_append(GString *str, char *newline, char *name, char *value) +{ + if( value && value[0] ) + g_string_sprintfa( str, "%s%s: %s", newline, name, value ); +} diff --git a/protocols/yahoo/Makefile b/protocols/yahoo/Makefile new file mode 100644 index 00000000..39a655a3 --- /dev/null +++ b/protocols/yahoo/Makefile @@ -0,0 +1,37 @@ +########################### +## Makefile for BitlBee ## +## ## +## Copyright 2002 Lintux ## +########################### + +### DEFINITIONS + +-include ../../Makefile.settings + +# [SH] Program variables +objects = yahoo.o crypt.o libyahoo2.o yahoo_fn.o yahoo_httplib.o yahoo_list.o yahoo_util.o + +CFLAGS += -Wall -DSTDC_HEADERS -DHAVE_STRING_H -DHAVE_STRCHR -DHAVE_MEMCPY -DHAVE_GLIB +LFLAGS += -r + +# [SH] Phony targets +all: yahooo.o + +.PHONY: all clean distclean + +clean: + rm -f *.o core + +distclean: clean + +### MAIN PROGRAM + +$(objects): ../../Makefile.settings Makefile + +$(objects): %.o: %.c + @echo '*' Compiling $< + @$(CC) -c $(CFLAGS) $< -o $@ + +yahooo.o: $(objects) + @echo '*' Linking yahooo.o + @$(LD) $(LFLAGS) $(objects) -o yahooo.o diff --git a/protocols/yahoo/crypt.c b/protocols/yahoo/crypt.c new file mode 100644 index 00000000..00eed70b --- /dev/null +++ b/protocols/yahoo/crypt.c @@ -0,0 +1,207 @@ +/* One way encryption based on MD5 sum. + Copyright (C) 1996, 1997, 1999, 2000 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ulrich Drepper <drepper@cygnus.com>, 1996. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* warmenhoven took this file and made it work with the md5.[ch] we + * already had. isn't that lovely. people should just use linux or + * freebsd, crypt works properly on those systems. i hate solaris */ + +#if HAVE_CONFIG_H +# include <config.h> +#endif + +#if HAVE_STRING_H +# include <string.h> +#elif HAVE_STRINGS_H +# include <strings.h> +#endif + +#include <stdlib.h> +#include "yahoo_util.h" + +#include "md5.h" + +/* Define our magic string to mark salt for MD5 "encryption" + replacement. This is meant to be the same as for other MD5 based + encryption implementations. */ +static const char md5_salt_prefix[] = "$1$"; + +/* Table with characters for base64 transformation. */ +static const char b64t[64] = +"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +char *yahoo_crypt(char *key, char *salt) +{ + char *buffer = NULL; + int buflen = 0; + int needed = 3 + strlen (salt) + 1 + 26 + 1; + + md5_byte_t alt_result[16]; + md5_state_t ctx; + md5_state_t alt_ctx; + size_t salt_len; + size_t key_len; + size_t cnt; + char *cp; + + if (buflen < needed) { + buflen = needed; + if ((buffer = realloc(buffer, buflen)) == NULL) + return NULL; + } + + /* Find beginning of salt string. The prefix should normally always + be present. Just in case it is not. */ + if (strncmp (md5_salt_prefix, salt, sizeof (md5_salt_prefix) - 1) == 0) + /* Skip salt prefix. */ + salt += sizeof (md5_salt_prefix) - 1; + + salt_len = MIN (strcspn (salt, "$"), 8); + key_len = strlen (key); + + /* Prepare for the real work. */ + md5_init(&ctx); + + /* Add the key string. */ + md5_append(&ctx, (md5_byte_t *)key, key_len); + + /* Because the SALT argument need not always have the salt prefix we + add it separately. */ + md5_append(&ctx, (md5_byte_t *)md5_salt_prefix, sizeof (md5_salt_prefix) - 1); + + /* The last part is the salt string. This must be at most 8 + characters and it ends at the first `$' character (for + compatibility which existing solutions). */ + md5_append(&ctx, (md5_byte_t *)salt, salt_len); + + /* Compute alternate MD5 sum with input KEY, SALT, and KEY. The + final result will be added to the first context. */ + md5_init(&alt_ctx); + + /* Add key. */ + md5_append(&alt_ctx, (md5_byte_t *)key, key_len); + + /* Add salt. */ + md5_append(&alt_ctx, (md5_byte_t *)salt, salt_len); + + /* Add key again. */ + md5_append(&alt_ctx, (md5_byte_t *)key, key_len); + + /* Now get result of this (16 bytes) and add it to the other + context. */ + md5_finish(&alt_ctx, alt_result); + + /* Add for any character in the key one byte of the alternate sum. */ + for (cnt = key_len; cnt > 16; cnt -= 16) + md5_append(&ctx, alt_result, 16); + md5_append(&ctx, alt_result, cnt); + + /* For the following code we need a NUL byte. */ + alt_result[0] = '\0'; + + /* The original implementation now does something weird: for every 1 + bit in the key the first 0 is added to the buffer, for every 0 + bit the first character of the key. This does not seem to be + what was intended but we have to follow this to be compatible. */ + for (cnt = key_len; cnt > 0; cnt >>= 1) + md5_append(&ctx, (cnt & 1) != 0 ? alt_result : (md5_byte_t *)key, 1); + + /* Create intermediate result. */ + md5_finish(&ctx, alt_result); + + /* Now comes another weirdness. In fear of password crackers here + comes a quite long loop which just processes the output of the + previous round again. We cannot ignore this here. */ + for (cnt = 0; cnt < 1000; ++cnt) { + /* New context. */ + md5_init(&ctx); + + /* Add key or last result. */ + if ((cnt & 1) != 0) + md5_append(&ctx, (md5_byte_t *)key, key_len); + else + md5_append(&ctx, alt_result, 16); + + /* Add salt for numbers not divisible by 3. */ + if (cnt % 3 != 0) + md5_append(&ctx, (md5_byte_t *)salt, salt_len); + + /* Add key for numbers not divisible by 7. */ + if (cnt % 7 != 0) + md5_append(&ctx, (md5_byte_t *)key, key_len); + + /* Add key or last result. */ + if ((cnt & 1) != 0) + md5_append(&ctx, alt_result, 16); + else + md5_append(&ctx, (md5_byte_t *)key, key_len); + + /* Create intermediate result. */ + md5_finish(&ctx, alt_result); + } + + /* Now we can construct the result string. It consists of three + parts. */ + + strncpy(buffer, md5_salt_prefix, MAX (0, buflen)); + cp = buffer + strlen(buffer); + buflen -= sizeof (md5_salt_prefix); + + strncpy(cp, salt, MIN ((size_t) buflen, salt_len)); + cp = cp + strlen(cp); + buflen -= MIN ((size_t) buflen, salt_len); + + if (buflen > 0) { + *cp++ = '$'; + --buflen; + } + +#define b64_from_24bit(B2, B1, B0, N) \ + do { \ + unsigned int w = ((B2) << 16) | ((B1) << 8) | (B0); \ + int n = (N); \ + while (n-- > 0 && buflen > 0) { \ + *cp++ = b64t[w & 0x3f]; \ + --buflen; \ + w >>= 6; \ + }\ + } while (0) + + b64_from_24bit (alt_result[0], alt_result[6], alt_result[12], 4); + b64_from_24bit (alt_result[1], alt_result[7], alt_result[13], 4); + b64_from_24bit (alt_result[2], alt_result[8], alt_result[14], 4); + b64_from_24bit (alt_result[3], alt_result[9], alt_result[15], 4); + b64_from_24bit (alt_result[4], alt_result[10], alt_result[5], 4); + b64_from_24bit (0, 0, alt_result[11], 2); + if (buflen <= 0) { + FREE(buffer); + } else + *cp = '\0'; /* Terminate the string. */ + + /* Clear the buffer for the intermediate result so that people + attaching to processes or reading core dumps cannot get any + information. We do it in this way to clear correct_words[] + inside the MD5 implementation as well. */ + md5_init(&ctx); + md5_finish(&ctx, alt_result); + memset (&ctx, '\0', sizeof (ctx)); + memset (&alt_ctx, '\0', sizeof (alt_ctx)); + + return buffer; +} diff --git a/protocols/yahoo/libyahoo2.c b/protocols/yahoo/libyahoo2.c new file mode 100644 index 00000000..62ceb0af --- /dev/null +++ b/protocols/yahoo/libyahoo2.c @@ -0,0 +1,4591 @@ +/* + * libyahoo2: libyahoo2.c + * + * Some code copyright (C) 2002-2004, Philip S Tellis <philip.tellis AT gmx.net> + * + * Yahoo Search copyright (C) 2003, Konstantin Klyagin <konst AT konst.org.ua> + * + * Much of this code was taken and adapted from the yahoo module for + * gaim released under the GNU GPL. This code is also released under the + * GNU GPL. + * + * This code is derivitive of Gaim <http://gaim.sourceforge.net> + * copyright (C) 1998-1999, Mark Spencer <markster@marko.net> + * 1998-1999, Adam Fritzler <afritz@marko.net> + * 1998-2002, Rob Flynn <rob@marko.net> + * 2000-2002, Eric Warmenhoven <eric@warmenhoven.org> + * 2001-2002, Brian Macke <macke@strangelove.net> + * 2001, Anand Biligiri S <abiligiri@users.sf.net> + * 2001, Valdis Kletnieks + * 2002, Sean Egan <bj91704@binghamton.edu> + * 2002, Toby Gray <toby.gray@ntlworld.com> + * + * This library also uses code from other libraries, namely: + * Portions from libfaim copyright 1998, 1999 Adam Fritzler + * <afritz@auk.cx> + * Portions of Sylpheed copyright 2000-2002 Hiroyuki Yamamoto + * <hiro-y@kcn.ne.jp> + * + * + * This program is free software; you can 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 + * + */ + +#if HAVE_CONFIG_H +# include <config.h> +#endif + +#ifndef _WIN32 +#include <unistd.h> +#endif +#include <errno.h> +#include <stdio.h> +#include <stdarg.h> + +#if STDC_HEADERS +# include <string.h> +#else +# if !HAVE_STRCHR +# define strchr index +# define strrchr rindex +# endif +char *strchr (), *strrchr (); +# if !HAVE_MEMCPY +# define memcpy(d, s, n) bcopy ((s), (d), (n)) +# define memmove(d, s, n) bcopy ((s), (d), (n)) +# endif +#endif + +#include <sys/types.h> + +#ifdef __MINGW32__ +# include <winsock2.h> +# define write(a,b,c) send(a,b,c,0) +# define read(a,b,c) recv(a,b,c,0) +#endif + +#include <stdlib.h> +#include <ctype.h> + +#include "sha.h" +#include "md5.h" +#include "yahoo2.h" +#include "yahoo_httplib.h" +#include "yahoo_util.h" +#include "yahoo_fn.h" + +#include "yahoo2_callbacks.h" +#include "yahoo_debug.h" +#if defined(__MINGW32__) && !defined(HAVE_GLIB) +#define snprintf _snprintf +#define vsnprintf _vsnprintf +#endif + +#ifdef USE_STRUCT_CALLBACKS +struct yahoo_callbacks *yc=NULL; + +void yahoo_register_callbacks(struct yahoo_callbacks * tyc) +{ + yc = tyc; +} + +#define YAHOO_CALLBACK(x) yc->x +#else +#define YAHOO_CALLBACK(x) x +#endif + +int yahoo_log_message(char * fmt, ...) +{ + char out[1024]; + va_list ap; + va_start(ap, fmt); + vsnprintf(out, sizeof(out), fmt, ap); + va_end(ap); + return YAHOO_CALLBACK(ext_yahoo_log)("%s", out); +} + +int yahoo_connect(char * host, int port) +{ + return YAHOO_CALLBACK(ext_yahoo_connect)(host, port); +} + +static enum yahoo_log_level log_level = YAHOO_LOG_NONE; + +enum yahoo_log_level yahoo_get_log_level() +{ + return log_level; +} + +int yahoo_set_log_level(enum yahoo_log_level level) +{ + enum yahoo_log_level l = log_level; + log_level = level; + return l; +} + +/* default values for servers */ +static char pager_host[] = "scs.msg.yahoo.com"; +static int pager_port = 5050; +static int fallback_ports[]={23, 25, 80, 20, 119, 8001, 8002, 5050, 0}; +static char filetransfer_host[]="filetransfer.msg.yahoo.com"; +static int filetransfer_port=80; +static char webcam_host[]="webcam.yahoo.com"; +static int webcam_port=5100; +static char webcam_description[]=""; +static char local_host[]=""; +static int conn_type=Y_WCM_DSL; + +static char profile_url[] = "http://profiles.yahoo.com/"; + +enum yahoo_service { /* these are easier to see in hex */ + YAHOO_SERVICE_LOGON = 1, + YAHOO_SERVICE_LOGOFF, + YAHOO_SERVICE_ISAWAY, + YAHOO_SERVICE_ISBACK, + YAHOO_SERVICE_IDLE, /* 5 (placemarker) */ + YAHOO_SERVICE_MESSAGE, + YAHOO_SERVICE_IDACT, + YAHOO_SERVICE_IDDEACT, + YAHOO_SERVICE_MAILSTAT, + YAHOO_SERVICE_USERSTAT, /* 0xa */ + YAHOO_SERVICE_NEWMAIL, + YAHOO_SERVICE_CHATINVITE, + YAHOO_SERVICE_CALENDAR, + YAHOO_SERVICE_NEWPERSONALMAIL, + YAHOO_SERVICE_NEWCONTACT, + YAHOO_SERVICE_ADDIDENT, /* 0x10 */ + YAHOO_SERVICE_ADDIGNORE, + YAHOO_SERVICE_PING, + YAHOO_SERVICE_GOTGROUPRENAME, /* < 1, 36(old), 37(new) */ + YAHOO_SERVICE_SYSMESSAGE = 0x14, + YAHOO_SERVICE_PASSTHROUGH2 = 0x16, + YAHOO_SERVICE_CONFINVITE = 0x18, + YAHOO_SERVICE_CONFLOGON, + YAHOO_SERVICE_CONFDECLINE, + YAHOO_SERVICE_CONFLOGOFF, + YAHOO_SERVICE_CONFADDINVITE, + YAHOO_SERVICE_CONFMSG, + YAHOO_SERVICE_CHATLOGON, + YAHOO_SERVICE_CHATLOGOFF, + YAHOO_SERVICE_CHATMSG = 0x20, + YAHOO_SERVICE_GAMELOGON = 0x28, + YAHOO_SERVICE_GAMELOGOFF, + YAHOO_SERVICE_GAMEMSG = 0x2a, + YAHOO_SERVICE_FILETRANSFER = 0x46, + YAHOO_SERVICE_VOICECHAT = 0x4A, + YAHOO_SERVICE_NOTIFY, + YAHOO_SERVICE_VERIFY, + YAHOO_SERVICE_P2PFILEXFER, + YAHOO_SERVICE_PEERTOPEER = 0x4F, /* Checks if P2P possible */ + YAHOO_SERVICE_WEBCAM, + YAHOO_SERVICE_AUTHRESP = 0x54, + YAHOO_SERVICE_LIST, + YAHOO_SERVICE_AUTH = 0x57, + YAHOO_SERVICE_ADDBUDDY = 0x83, + YAHOO_SERVICE_REMBUDDY, + YAHOO_SERVICE_IGNORECONTACT, /* > 1, 7, 13 < 1, 66, 13, 0*/ + YAHOO_SERVICE_REJECTCONTACT, + YAHOO_SERVICE_GROUPRENAME = 0x89, /* > 1, 65(new), 66(0), 67(old) */ + YAHOO_SERVICE_CHATONLINE = 0x96, /* > 109(id), 1, 6(abcde) < 0,1*/ + YAHOO_SERVICE_CHATGOTO, + YAHOO_SERVICE_CHATJOIN, /* > 1 104-room 129-1600326591 62-2 */ + YAHOO_SERVICE_CHATLEAVE, + YAHOO_SERVICE_CHATEXIT = 0x9b, + YAHOO_SERVICE_CHATLOGOUT = 0xa0, + YAHOO_SERVICE_CHATPING, + YAHOO_SERVICE_COMMENT = 0xa8 +}; + +struct yahoo_pair { + int key; + char *value; +}; + +struct yahoo_packet { + unsigned short int service; + unsigned int status; + unsigned int id; + YList *hash; +}; + +struct yahoo_search_state { + int lsearch_type; + char *lsearch_text; + int lsearch_gender; + int lsearch_agerange; + int lsearch_photo; + int lsearch_yahoo_only; + int lsearch_nstart; + int lsearch_nfound; + int lsearch_ntotal; +}; + +struct data_queue { + unsigned char *queue; + int len; +}; + +struct yahoo_input_data { + struct yahoo_data *yd; + struct yahoo_webcam *wcm; + struct yahoo_webcam_data *wcd; + struct yahoo_search_state *ys; + + int fd; + enum yahoo_connection_type type; + + unsigned char *rxqueue; + int rxlen; + int read_tag; + + YList *txqueues; + int write_tag; +}; + +struct yahoo_server_settings { + char *pager_host; + int pager_port; + char *filetransfer_host; + int filetransfer_port; + char *webcam_host; + int webcam_port; + char *webcam_description; + char *local_host; + int conn_type; +}; + +static void * _yahoo_default_server_settings() +{ + struct yahoo_server_settings *yss = y_new0(struct yahoo_server_settings, 1); + + yss->pager_host = strdup(pager_host); + yss->pager_port = pager_port; + yss->filetransfer_host = strdup(filetransfer_host); + yss->filetransfer_port = filetransfer_port; + yss->webcam_host = strdup(webcam_host); + yss->webcam_port = webcam_port; + yss->webcam_description = strdup(webcam_description); + yss->local_host = strdup(local_host); + yss->conn_type = conn_type; + + return yss; +} + +static void * _yahoo_assign_server_settings(va_list ap) +{ + struct yahoo_server_settings *yss = _yahoo_default_server_settings(); + char *key; + char *svalue; + int nvalue; + + while(1) { + key = va_arg(ap, char *); + if(key == NULL) + break; + + if(!strcmp(key, "pager_host")) { + svalue = va_arg(ap, char *); + free(yss->pager_host); + yss->pager_host = strdup(svalue); + } else if(!strcmp(key, "pager_port")) { + nvalue = va_arg(ap, int); + yss->pager_port = nvalue; + } else if(!strcmp(key, "filetransfer_host")) { + svalue = va_arg(ap, char *); + free(yss->filetransfer_host); + yss->filetransfer_host = strdup(svalue); + } else if(!strcmp(key, "filetransfer_port")) { + nvalue = va_arg(ap, int); + yss->filetransfer_port = nvalue; + } else if(!strcmp(key, "webcam_host")) { + svalue = va_arg(ap, char *); + free(yss->webcam_host); + yss->webcam_host = strdup(svalue); + } else if(!strcmp(key, "webcam_port")) { + nvalue = va_arg(ap, int); + yss->webcam_port = nvalue; + } else if(!strcmp(key, "webcam_description")) { + svalue = va_arg(ap, char *); + free(yss->webcam_description); + yss->webcam_description = strdup(svalue); + } else if(!strcmp(key, "local_host")) { + svalue = va_arg(ap, char *); + free(yss->local_host); + yss->local_host = strdup(svalue); + } else if(!strcmp(key, "conn_type")) { + nvalue = va_arg(ap, int); + yss->conn_type = nvalue; + } else { + WARNING(("Unknown key passed to yahoo_init, " + "perhaps you didn't terminate the list " + "with NULL")); + } + } + + return yss; +} + +static void yahoo_free_server_settings(struct yahoo_server_settings *yss) +{ + if(!yss) + return; + + free(yss->pager_host); + free(yss->filetransfer_host); + free(yss->webcam_host); + free(yss->webcam_description); + free(yss->local_host); + + free(yss); +} + +static YList *conns=NULL; +static YList *inputs=NULL; +static int last_id=0; + +static void add_to_list(struct yahoo_data *yd) +{ + conns = y_list_prepend(conns, yd); +} +static struct yahoo_data * find_conn_by_id(int id) +{ + YList *l; + for(l = conns; l; l = y_list_next(l)) { + struct yahoo_data *yd = l->data; + if(yd->client_id == id) + return yd; + } + return NULL; +} +static void del_from_list(struct yahoo_data *yd) +{ + conns = y_list_remove(conns, yd); +} + +/* call repeatedly to get the next one */ +/* +static struct yahoo_input_data * find_input_by_id(int id) +{ + YList *l; + for(l = inputs; l; l = y_list_next(l)) { + struct yahoo_input_data *yid = l->data; + if(yid->yd->client_id == id) + return yid; + } + return NULL; +} +*/ + +static struct yahoo_input_data * find_input_by_id_and_webcam_user(int id, const char * who) +{ + YList *l; + LOG(("find_input_by_id_and_webcam_user")); + for(l = inputs; l; l = y_list_next(l)) { + struct yahoo_input_data *yid = l->data; + if(yid->type == YAHOO_CONNECTION_WEBCAM && yid->yd->client_id == id + && yid->wcm && + ((who && yid->wcm->user && !strcmp(who, yid->wcm->user)) || + !(yid->wcm->user && !who))) + return yid; + } + return NULL; +} + +static struct yahoo_input_data * find_input_by_id_and_type(int id, enum yahoo_connection_type type) +{ + YList *l; + LOG(("find_input_by_id_and_type")); + for(l = inputs; l; l = y_list_next(l)) { + struct yahoo_input_data *yid = l->data; + if(yid->type == type && yid->yd->client_id == id) + return yid; + } + return NULL; +} + +static struct yahoo_input_data * find_input_by_id_and_fd(int id, int fd) +{ + YList *l; + LOG(("find_input_by_id_and_fd")); + for(l = inputs; l; l = y_list_next(l)) { + struct yahoo_input_data *yid = l->data; + if(yid->fd == fd && yid->yd->client_id == id) + return yid; + } + return NULL; +} + +static int count_inputs_with_id(int id) +{ + int c=0; + YList *l; + LOG(("counting %d", id)); + for(l = inputs; l; l = y_list_next(l)) { + struct yahoo_input_data *yid = l->data; + if(yid->yd->client_id == id) + c++; + } + LOG(("%d", c)); + return c; +} + + +extern char *yahoo_crypt(char *, char *); + +/* Free a buddy list */ +static void yahoo_free_buddies(YList * list) +{ + YList *l; + + for(l = list; l; l = l->next) + { + struct yahoo_buddy *bud = l->data; + if(!bud) + continue; + + FREE(bud->group); + FREE(bud->id); + FREE(bud->real_name); + if(bud->yab_entry) { + FREE(bud->yab_entry->fname); + FREE(bud->yab_entry->lname); + FREE(bud->yab_entry->nname); + FREE(bud->yab_entry->id); + FREE(bud->yab_entry->email); + FREE(bud->yab_entry->hphone); + FREE(bud->yab_entry->wphone); + FREE(bud->yab_entry->mphone); + FREE(bud->yab_entry); + } + FREE(bud); + l->data = bud = NULL; + } + + y_list_free(list); +} + +/* Free an identities list */ +static void yahoo_free_identities(YList * list) +{ + while (list) { + YList *n = list; + FREE(list->data); + list = y_list_remove_link(list, list); + y_list_free_1(n); + } +} + +/* Free webcam data */ +static void yahoo_free_webcam(struct yahoo_webcam *wcm) +{ + if (wcm) { + FREE(wcm->user); + FREE(wcm->server); + FREE(wcm->key); + FREE(wcm->description); + FREE(wcm->my_ip); + } + FREE(wcm); +} + +static void yahoo_free_data(struct yahoo_data *yd) +{ + FREE(yd->user); + FREE(yd->password); + FREE(yd->cookie_y); + FREE(yd->cookie_t); + FREE(yd->cookie_c); + FREE(yd->login_cookie); + FREE(yd->login_id); + + yahoo_free_buddies(yd->buddies); + yahoo_free_buddies(yd->ignore); + yahoo_free_identities(yd->identities); + + yahoo_free_server_settings(yd->server_settings); + + FREE(yd); +} + +#define YAHOO_PACKET_HDRLEN (4 + 2 + 2 + 2 + 2 + 4 + 4) + +static struct yahoo_packet *yahoo_packet_new(enum yahoo_service service, + enum yahoo_status status, int id) +{ + struct yahoo_packet *pkt = y_new0(struct yahoo_packet, 1); + + pkt->service = service; + pkt->status = status; + pkt->id = id; + + return pkt; +} + +static void yahoo_packet_hash(struct yahoo_packet *pkt, int key, const char *value) +{ + struct yahoo_pair *pair = y_new0(struct yahoo_pair, 1); + pair->key = key; + pair->value = strdup(value); + pkt->hash = y_list_append(pkt->hash, pair); +} + +static int yahoo_packet_length(struct yahoo_packet *pkt) +{ + YList *l; + + int len = 0; + + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + int tmp = pair->key; + do { + tmp /= 10; + len++; + } while (tmp); + len += 2; + len += strlen(pair->value); + len += 2; + } + + return len; +} + +#define yahoo_put16(buf, data) ( \ + (*(buf) = (unsigned char)((data)>>8)&0xff), \ + (*((buf)+1) = (unsigned char)(data)&0xff), \ + 2) +#define yahoo_get16(buf) ((((*(buf))&0xff)<<8) + ((*((buf)+1)) & 0xff)) +#define yahoo_put32(buf, data) ( \ + (*((buf)) = (unsigned char)((data)>>24)&0xff), \ + (*((buf)+1) = (unsigned char)((data)>>16)&0xff), \ + (*((buf)+2) = (unsigned char)((data)>>8)&0xff), \ + (*((buf)+3) = (unsigned char)(data)&0xff), \ + 4) +#define yahoo_get32(buf) ((((*(buf) )&0xff)<<24) + \ + (((*((buf)+1))&0xff)<<16) + \ + (((*((buf)+2))&0xff)<< 8) + \ + (((*((buf)+3))&0xff))) + +static void yahoo_packet_read(struct yahoo_packet *pkt, unsigned char *data, int len) +{ + int pos = 0; + + while (pos + 1 < len) { + char *key, *value = NULL; + int accept; + int x; + + struct yahoo_pair *pair = y_new0(struct yahoo_pair, 1); + + key = malloc(len + 1); + x = 0; + while (pos + 1 < len) { + if (data[pos] == 0xc0 && data[pos + 1] == 0x80) + break; + key[x++] = data[pos++]; + } + key[x] = 0; + pos += 2; + pair->key = strtol(key, NULL, 10); + free(key); + + /* Libyahoo2 developer(s) don't seem to have the time to fix + this problem, so for now try to work around it: + + Sometimes we receive an invalid packet with not any more + data at this point. I don't know how to handle this in a + clean way, but let's hope this is clean enough: */ + + if (pos + 1 < len) { + accept = x; + /* if x is 0 there was no key, so don't accept it */ + if (accept) + value = malloc(len - pos + 1); + x = 0; + while (pos + 1 < len) { + if (data[pos] == 0xc0 && data[pos + 1] == 0x80) + break; + if (accept) + value[x++] = data[pos++]; + } + if (accept) + value[x] = 0; + pos += 2; + } else { + accept = 0; + } + + if (accept) { + pair->value = strdup(value); + FREE(value); + pkt->hash = y_list_append(pkt->hash, pair); + DEBUG_MSG(("Key: %d \tValue: %s", pair->key, pair->value)); + } else { + FREE(pair); + } + } +} + +static void yahoo_packet_write(struct yahoo_packet *pkt, unsigned char *data) +{ + YList *l; + int pos = 0; + + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + unsigned char buf[100]; + + snprintf((char *)buf, sizeof(buf), "%d", pair->key); + strcpy((char *)data + pos, (char *)buf); + pos += strlen((char *)buf); + data[pos++] = 0xc0; + data[pos++] = 0x80; + + strcpy((char *)data + pos, pair->value); + pos += strlen(pair->value); + data[pos++] = 0xc0; + data[pos++] = 0x80; + } +} + +static void yahoo_dump_unhandled(struct yahoo_packet *pkt) +{ + YList *l; + + NOTICE(("Service: 0x%02x\tStatus: %d", pkt->service, pkt->status)); + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + NOTICE(("\t%d => %s", pair->key, pair->value)); + } +} + + +static void yahoo_packet_dump(unsigned char *data, int len) +{ + if(yahoo_get_log_level() >= YAHOO_LOG_DEBUG) { + int i; + for (i = 0; i < len; i++) { + if ((i % 8 == 0) && i) + YAHOO_CALLBACK(ext_yahoo_log)(" "); + if ((i % 16 == 0) && i) + YAHOO_CALLBACK(ext_yahoo_log)("\n"); + YAHOO_CALLBACK(ext_yahoo_log)("%02x ", data[i]); + } + YAHOO_CALLBACK(ext_yahoo_log)("\n"); + for (i = 0; i < len; i++) { + if ((i % 8 == 0) && i) + YAHOO_CALLBACK(ext_yahoo_log)(" "); + if ((i % 16 == 0) && i) + YAHOO_CALLBACK(ext_yahoo_log)("\n"); + if (isprint(data[i])) + YAHOO_CALLBACK(ext_yahoo_log)(" %c ", data[i]); + else + YAHOO_CALLBACK(ext_yahoo_log)(" . "); + } + YAHOO_CALLBACK(ext_yahoo_log)("\n"); + } +} + +static char base64digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789._"; +static void to_y64(unsigned char *out, const unsigned char *in, int inlen) +/* raw bytes in quasi-big-endian order to base 64 string (NUL-terminated) */ +{ + for (; inlen >= 3; inlen -= 3) + { + *out++ = base64digits[in[0] >> 2]; + *out++ = base64digits[((in[0]<<4) & 0x30) | (in[1]>>4)]; + *out++ = base64digits[((in[1]<<2) & 0x3c) | (in[2]>>6)]; + *out++ = base64digits[in[2] & 0x3f]; + in += 3; + } + if (inlen > 0) + { + unsigned char fragment; + + *out++ = base64digits[in[0] >> 2]; + fragment = (in[0] << 4) & 0x30; + if (inlen > 1) + fragment |= in[1] >> 4; + *out++ = base64digits[fragment]; + *out++ = (inlen < 2) ? '-' + : base64digits[(in[1] << 2) & 0x3c]; + *out++ = '-'; + } + *out = '\0'; +} + +static void yahoo_add_to_send_queue(struct yahoo_input_data *yid, void *data, int length) +{ + struct data_queue *tx = y_new0(struct data_queue, 1); + tx->queue = y_new0(unsigned char, length); + tx->len = length; + memcpy(tx->queue, data, length); + + yid->txqueues = y_list_append(yid->txqueues, tx); + + if(!yid->write_tag) + yid->write_tag=YAHOO_CALLBACK(ext_yahoo_add_handler)(yid->yd->client_id, yid->fd, YAHOO_INPUT_WRITE, yid); +} + +static void yahoo_send_packet(struct yahoo_input_data *yid, struct yahoo_packet *pkt, int extra_pad) +{ + int pktlen = yahoo_packet_length(pkt); + int len = YAHOO_PACKET_HDRLEN + pktlen; + + unsigned char *data; + int pos = 0; + + if (yid->fd < 0) + return; + + data = y_new0(unsigned char, len + 1); + + memcpy(data + pos, "YMSG", 4); pos += 4; + pos += yahoo_put16(data + pos, 0x0a00); + pos += yahoo_put16(data + pos, 0x0000); + pos += yahoo_put16(data + pos, pktlen + extra_pad); + pos += yahoo_put16(data + pos, pkt->service); + pos += yahoo_put32(data + pos, pkt->status); + pos += yahoo_put32(data + pos, pkt->id); + + yahoo_packet_write(pkt, data + pos); + + yahoo_packet_dump(data, len); + + yahoo_add_to_send_queue(yid, data, len); + FREE(data); +} + +static void yahoo_packet_free(struct yahoo_packet *pkt) +{ + while (pkt->hash) { + struct yahoo_pair *pair = pkt->hash->data; + YList *tmp; + FREE(pair->value); + FREE(pair); + tmp = pkt->hash; + pkt->hash = y_list_remove_link(pkt->hash, pkt->hash); + y_list_free_1(tmp); + } + FREE(pkt); +} + +static int yahoo_send_data(int fd, void *data, int len) +{ + int ret; + int e; + + if (fd < 0) + return -1; + + yahoo_packet_dump(data, len); + + do { + ret = write(fd, data, len); + } while(ret == -1 && errno==EINTR); + e=errno; + + if (ret == -1) { + LOG(("wrote data: ERR %s", strerror(errno))); + } else { + LOG(("wrote data: OK")); + } + + errno=e; + return ret; +} + +void yahoo_close(int id) +{ + struct yahoo_data *yd = find_conn_by_id(id); + if(!yd) + return; + + del_from_list(yd); + + yahoo_free_data(yd); + if(id == last_id) + last_id--; +} + +static void yahoo_input_close(struct yahoo_input_data *yid) +{ + inputs = y_list_remove(inputs, yid); + + LOG(("yahoo_input_close(read)")); + YAHOO_CALLBACK(ext_yahoo_remove_handler)(yid->yd->client_id, yid->read_tag); + LOG(("yahoo_input_close(write)")); + YAHOO_CALLBACK(ext_yahoo_remove_handler)(yid->yd->client_id, yid->write_tag); + yid->read_tag = yid->write_tag = 0; + if(yid->fd) + close(yid->fd); + yid->fd = 0; + FREE(yid->rxqueue); + if(count_inputs_with_id(yid->yd->client_id) == 0) { + LOG(("closing %d", yid->yd->client_id)); + yahoo_close(yid->yd->client_id); + } + yahoo_free_webcam(yid->wcm); + if(yid->wcd) + FREE(yid->wcd); + if(yid->ys) { + FREE(yid->ys->lsearch_text); + FREE(yid->ys); + } + FREE(yid); +} + +static int is_same_bud(const void * a, const void * b) { + const struct yahoo_buddy *subject = a; + const struct yahoo_buddy *object = b; + + return strcmp(subject->id, object->id); +} + +static YList * bud_str2list(char *rawlist) +{ + YList * l = NULL; + + char **lines; + char **split; + char **buddies; + char **tmp, **bud; + + lines = y_strsplit(rawlist, "\n", -1); + for (tmp = lines; *tmp; tmp++) { + struct yahoo_buddy *newbud; + + split = y_strsplit(*tmp, ":", 2); + if (!split) + continue; + if (!split[0] || !split[1]) { + y_strfreev(split); + continue; + } + buddies = y_strsplit(split[1], ",", -1); + + for (bud = buddies; bud && *bud; bud++) { + newbud = y_new0(struct yahoo_buddy, 1); + newbud->id = strdup(*bud); + newbud->group = strdup(split[0]); + + if(y_list_find_custom(l, newbud, is_same_bud)) { + FREE(newbud->id); + FREE(newbud->group); + FREE(newbud); + continue; + } + + newbud->real_name = NULL; + + l = y_list_append(l, newbud); + + NOTICE(("Added buddy %s to group %s", newbud->id, newbud->group)); + } + + y_strfreev(buddies); + y_strfreev(split); + } + y_strfreev(lines); + + return l; +} + +static char * getcookie(char *rawcookie) +{ + char * cookie=NULL; + char * tmpcookie; + char * cookieend; + + if (strlen(rawcookie) < 2) + return NULL; + + tmpcookie = strdup(rawcookie+2); + cookieend = strchr(tmpcookie, ';'); + + if(cookieend) + *cookieend = '\0'; + + cookie = strdup(tmpcookie); + FREE(tmpcookie); + /* cookieend=NULL; not sure why this was there since the value is not preserved in the stack -dd */ + + return cookie; +} + +static char * getlcookie(char *cookie) +{ + char *tmp; + char *tmpend; + char *login_cookie = NULL; + + tmpend = strstr(cookie, "n="); + if(tmpend) { + tmp = strdup(tmpend+2); + tmpend = strchr(tmp, '&'); + if(tmpend) + *tmpend='\0'; + login_cookie = strdup(tmp); + FREE(tmp); + } + + return login_cookie; +} + +static void yahoo_process_notify(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +{ + struct yahoo_data *yd = yid->yd; + char *msg = NULL; + char *from = NULL; + int stat = 0; + int accept = 0; + char *ind = NULL; + YList *l; + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + if (pair->key == 4) + from = pair->value; + if (pair->key == 49) + msg = pair->value; + if (pair->key == 13) + stat = atoi(pair->value); + if (pair->key == 14) + ind = pair->value; + if (pair->key == 16) { /* status == -1 */ + NOTICE((pair->value)); + return; + } + + } + + if (!msg) + return; + + if (!strncasecmp(msg, "TYPING", strlen("TYPING"))) + YAHOO_CALLBACK(ext_yahoo_typing_notify)(yd->client_id, from, stat); + else if (!strncasecmp(msg, "GAME", strlen("GAME"))) + YAHOO_CALLBACK(ext_yahoo_game_notify)(yd->client_id, from, stat); + else if (!strncasecmp(msg, "WEBCAMINVITE", strlen("WEBCAMINVITE"))) + { + if (!strcmp(ind, " ")) { + YAHOO_CALLBACK(ext_yahoo_webcam_invite)(yd->client_id, from); + } else { + accept = atoi(ind); + /* accept the invitation (-1 = deny 1 = accept) */ + if (accept < 0) + accept = 0; + YAHOO_CALLBACK(ext_yahoo_webcam_invite_reply)(yd->client_id, from, accept); + } + } + else + LOG(("Got unknown notification: %s", msg)); +} + +static void yahoo_process_filetransfer(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +{ + struct yahoo_data *yd = yid->yd; + char *from=NULL; + char *to=NULL; + char *msg=NULL; + char *url=NULL; + long expires=0; + + char *service=NULL; + + char *filename=NULL; + unsigned long filesize=0L; + + YList *l; + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + if (pair->key == 4) + from = pair->value; + if (pair->key == 5) + to = pair->value; + if (pair->key == 14) + msg = pair->value; + if (pair->key == 20) + url = pair->value; + if (pair->key == 38) + expires = atol(pair->value); + + if (pair->key == 27) + filename = pair->value; + if (pair->key == 28) + filesize = atol(pair->value); + + if (pair->key == 49) + service = pair->value; + } + + if(pkt->service == YAHOO_SERVICE_P2PFILEXFER) { + if(strcmp("FILEXFER", service) != 0) { + WARNING(("unhandled service 0x%02x", pkt->service)); + yahoo_dump_unhandled(pkt); + return; + } + } + + if(msg) { + char *tmp; + tmp = strchr(msg, '\006'); + if(tmp) + *tmp = '\0'; + } + if(url && from) + YAHOO_CALLBACK(ext_yahoo_got_file)(yd->client_id, from, url, expires, msg, filename, filesize); + +} + +static void yahoo_process_conference(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +{ + struct yahoo_data *yd = yid->yd; + char *msg = NULL; + char *host = NULL; + char *who = NULL; + char *room = NULL; + char *id = NULL; + int utf8 = 0; + YList *members = NULL; + YList *l; + + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + if (pair->key == 50) + host = pair->value; + + if (pair->key == 52) { /* invite */ + members = y_list_append(members, strdup(pair->value)); + } + if (pair->key == 53) /* logon */ + who = pair->value; + if (pair->key == 54) /* decline */ + who = pair->value; + if (pair->key == 55) /* unavailable (status == 2) */ + who = pair->value; + if (pair->key == 56) /* logoff */ + who = pair->value; + + if (pair->key == 57) + room = pair->value; + + if (pair->key == 58) /* join message */ + msg = pair->value; + if (pair->key == 14) /* decline/conf message */ + msg = pair->value; + + if (pair->key == 13) + ; + if (pair->key == 16) /* error */ + msg = pair->value; + + if (pair->key == 1) /* my id */ + id = pair->value; + if (pair->key == 3) /* message sender */ + who = pair->value; + + if (pair->key == 97) + utf8 = atoi(pair->value); + } + + if(!room) + return; + + if(host) { + for(l = members; l; l = l->next) { + char * w = l->data; + if(!strcmp(w, host)) + break; + } + if(!l) + members = y_list_append(members, strdup(host)); + } + /* invite, decline, join, left, message -> status == 1 */ + + switch(pkt->service) { + case YAHOO_SERVICE_CONFINVITE: + if(pkt->status == 2) + ; + else if(members) + YAHOO_CALLBACK(ext_yahoo_got_conf_invite)(yd->client_id, host, room, msg, members); + else if(msg) + YAHOO_CALLBACK(ext_yahoo_error)(yd->client_id, msg, 0); + break; + case YAHOO_SERVICE_CONFADDINVITE: + if(pkt->status == 2) + ; + else + YAHOO_CALLBACK(ext_yahoo_got_conf_invite)(yd->client_id, host, room, msg, members); + break; + case YAHOO_SERVICE_CONFDECLINE: + if(who) + YAHOO_CALLBACK(ext_yahoo_conf_userdecline)(yd->client_id, who, room, msg); + break; + case YAHOO_SERVICE_CONFLOGON: + if(who) + YAHOO_CALLBACK(ext_yahoo_conf_userjoin)(yd->client_id, who, room); + break; + case YAHOO_SERVICE_CONFLOGOFF: + if(who) + YAHOO_CALLBACK(ext_yahoo_conf_userleave)(yd->client_id, who, room); + break; + case YAHOO_SERVICE_CONFMSG: + if(who) + YAHOO_CALLBACK(ext_yahoo_conf_message)(yd->client_id, who, room, msg, utf8); + break; + } +} + +static void yahoo_process_chat(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +{ + char *msg = NULL; + char *who = NULL; + char *room = NULL; + char *topic = NULL; + YList *members = NULL; + struct yahoo_chat_member *currentmember = NULL; + int msgtype = 1; + int utf8 = 0; + int firstjoin = 0; + int membercount = 0; + int chaterr=0; + YList *l; + + yahoo_dump_unhandled(pkt); + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + + if (pair->key == 104) { + /* Room name */ + room = pair->value; + } + + if (pair->key == 105) { + /* Room topic */ + topic = pair->value; + } + + if (pair->key == 108) { + /* Number of members in this packet */ + membercount = atoi(pair->value); + } + + if (pair->key == 109) { + /* message sender */ + who = pair->value; + + if (pkt->service == YAHOO_SERVICE_CHATJOIN) { + currentmember = y_new0(struct yahoo_chat_member, 1); + currentmember->id = strdup(pair->value); + members = y_list_append(members, currentmember); + } + } + + if (pair->key == 110) { + /* age */ + if (pkt->service == YAHOO_SERVICE_CHATJOIN) + currentmember->age = atoi(pair->value); + } + + if (pair->key == 113) { + /* attribs */ + if (pkt->service == YAHOO_SERVICE_CHATJOIN) + currentmember->attribs = atoi(pair->value); + } + + if (pair->key == 141) { + /* alias */ + if (pkt->service == YAHOO_SERVICE_CHATJOIN) + currentmember->alias = strdup(pair->value); + } + + if (pair->key == 142) { + /* location */ + if (pkt->service == YAHOO_SERVICE_CHATJOIN) + currentmember->location = strdup(pair->value); + } + + + if (pair->key == 130) { + /* first join */ + firstjoin = 1; + } + + if (pair->key == 117) { + /* message */ + msg = pair->value; + } + + if (pair->key == 124) { + /* Message type */ + msgtype = atoi(pair->value); + } + if (pair->key == 114) { + /* message error not sure what all the pair values mean */ + /* but -1 means no session in room */ + chaterr= atoi(pair->value); + } + } + + if(!room) { + if (pkt->service == YAHOO_SERVICE_CHATLOGOUT) { /* yahoo originated chat logout */ + YAHOO_CALLBACK(ext_yahoo_chat_yahoologout)(yid->yd->client_id); + return ; + } + if (pkt->service == YAHOO_SERVICE_COMMENT && chaterr) { + YAHOO_CALLBACK(ext_yahoo_chat_yahooerror)(yid->yd->client_id); + return ; + } + + WARNING(("We didn't get a room name, ignoring packet")); + return; + } + + switch(pkt->service) { + case YAHOO_SERVICE_CHATJOIN: + if(y_list_length(members) != membercount) { + WARNING(("Count of members doesn't match No. of members we got")); + } + if(firstjoin && members) { + YAHOO_CALLBACK(ext_yahoo_chat_join)(yid->yd->client_id, room, topic, members, yid->fd); + } else if(who) { + if(y_list_length(members) != 1) { + WARNING(("Got more than 1 member on a normal join")); + } + /* this should only ever have one, but just in case */ + while(members) { + YList *n = members->next; + currentmember = members->data; + YAHOO_CALLBACK(ext_yahoo_chat_userjoin)(yid->yd->client_id, room, currentmember); + y_list_free_1(members); + members=n; + } + } + break; + case YAHOO_SERVICE_CHATEXIT: + if(who) { + YAHOO_CALLBACK(ext_yahoo_chat_userleave)(yid->yd->client_id, room, who); + } + break; + case YAHOO_SERVICE_COMMENT: + if(who) { + YAHOO_CALLBACK(ext_yahoo_chat_message)(yid->yd->client_id, who, room, msg, msgtype, utf8); + } + break; + } +} + +static void yahoo_process_message(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +{ + struct yahoo_data *yd = yid->yd; + YList *l; + YList * messages = NULL; + + struct m { + int i_31; + int i_32; + char *to; + char *from; + long tm; + char *msg; + int utf8; + } *message = y_new0(struct m, 1); + + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + if (pair->key == 1 || pair->key == 4) + { + if(!message->from) + message->from = pair->value; + } + else if (pair->key == 5) + message->to = pair->value; + else if (pair->key == 15) + message->tm = strtol(pair->value, NULL, 10); + else if (pair->key == 97) + message->utf8 = atoi(pair->value); + /* user message */ /* sys message */ + else if (pair->key == 14 || pair->key == 16) + message->msg = pair->value; + else if (pair->key == 31) { + if(message->i_31) { + messages = y_list_append(messages, message); + message = y_new0(struct m, 1); + } + message->i_31 = atoi(pair->value); + } + else if (pair->key == 32) + message->i_32 = atoi(pair->value); + else + LOG(("yahoo_process_message: status: %d, key: %d, value: %s", + pkt->status, pair->key, pair->value)); + } + + messages = y_list_append(messages, message); + + for (l = messages; l; l=l->next) { + message = l->data; + if (pkt->service == YAHOO_SERVICE_SYSMESSAGE) { + YAHOO_CALLBACK(ext_yahoo_system_message)(yd->client_id, message->msg); + } else if (pkt->status <= 2 || pkt->status == 5) { + YAHOO_CALLBACK(ext_yahoo_got_im)(yd->client_id, message->from, message->msg, message->tm, pkt->status, message->utf8); + } else if (pkt->status == 0xffffffff) { + YAHOO_CALLBACK(ext_yahoo_error)(yd->client_id, message->msg, 0); + } + free(message); + } + + y_list_free(messages); +} + + +static void yahoo_process_status(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +{ + YList *l; + struct yahoo_data *yd = yid->yd; + char *name = NULL; + int state = 0; + int away = 0; + int idle = 0; + char *msg = NULL; + + if(pkt->service == YAHOO_SERVICE_LOGOFF && pkt->status == -1) { + YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, YAHOO_LOGIN_DUPL, NULL); + return; + } + + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + + switch (pair->key) { + case 0: /* we won't actually do anything with this */ + NOTICE(("key %d:%s", pair->key, pair->value)); + break; + case 1: /* we don't get the full buddy list here. */ + if (!yd->logged_in) { + yd->logged_in = TRUE; + if(yd->current_status < 0) + yd->current_status = yd->initial_status; + YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, YAHOO_LOGIN_OK, NULL); + } + break; + case 8: /* how many online buddies we have */ + NOTICE(("key %d:%s", pair->key, pair->value)); + break; + case 7: /* the current buddy */ + name = pair->value; + break; + case 10: /* state */ + state = strtol(pair->value, NULL, 10); + break; + case 19: /* custom status message */ + msg = pair->value; + break; + case 47: /* is it an away message or not */ + away = atoi(pair->value); + break; + case 137: /* seconds idle */ + idle = atoi(pair->value); + break; + case 11: /* what is this? */ + NOTICE(("key %d:%s", pair->key, pair->value)); + break; + case 17: /* in chat? */ + break; + case 13: /* in pager? */ + if (pkt->service == YAHOO_SERVICE_LOGOFF || strtol(pair->value, NULL, 10) == 0) { + YAHOO_CALLBACK(ext_yahoo_status_changed)(yd->client_id, name, YAHOO_STATUS_OFFLINE, NULL, 1); + break; + } + if (state == YAHOO_STATUS_AVAILABLE) { + YAHOO_CALLBACK(ext_yahoo_status_changed)(yd->client_id, name, state, NULL, 0); + } else if (state == YAHOO_STATUS_CUSTOM) { + YAHOO_CALLBACK(ext_yahoo_status_changed)(yd->client_id, name, state, msg, away); + } else { + YAHOO_CALLBACK(ext_yahoo_status_changed)(yd->client_id, name, state, NULL, idle); + } + + break; + case 60: + /* sometimes going offline makes this 2, but invisible never sends it */ + NOTICE(("key %d:%s", pair->key, pair->value)); + break; + case 16: /* Custom error message */ + YAHOO_CALLBACK(ext_yahoo_error)(yd->client_id, pair->value, 0); + break; + default: + WARNING(("unknown status key %d:%s", pair->key, pair->value)); + break; + } + } +} + +static void yahoo_process_list(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +{ + struct yahoo_data *yd = yid->yd; + YList *l; + + if (!yd->logged_in) { + yd->logged_in = TRUE; + if(yd->current_status < 0) + yd->current_status = yd->initial_status; + YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, YAHOO_LOGIN_OK, NULL); + } + + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + + switch(pair->key) { + case 87: /* buddies */ + if(!yd->rawbuddylist) + yd->rawbuddylist = strdup(pair->value); + else { + yd->rawbuddylist = y_string_append(yd->rawbuddylist, pair->value); + } + break; + + case 88: /* ignore list */ + if(!yd->ignorelist) + yd->ignorelist = strdup("Ignore:"); + yd->ignorelist = y_string_append(yd->ignorelist, pair->value); + break; + + case 89: /* identities */ + { + char **identities = y_strsplit(pair->value, ",", -1); + int i; + for(i=0; identities[i]; i++) + yd->identities = y_list_append(yd->identities, + strdup(identities[i])); + y_strfreev(identities); + } + YAHOO_CALLBACK(ext_yahoo_got_identities)(yd->client_id, yd->identities); + break; + case 59: /* cookies */ + if(yd->ignorelist) { + yd->ignore = bud_str2list(yd->ignorelist); + FREE(yd->ignorelist); + YAHOO_CALLBACK(ext_yahoo_got_ignore)(yd->client_id, yd->ignore); + } + if(yd->rawbuddylist) { + yd->buddies = bud_str2list(yd->rawbuddylist); + FREE(yd->rawbuddylist); + YAHOO_CALLBACK(ext_yahoo_got_buddies)(yd->client_id, yd->buddies); + } + + if(pair->value[0]=='Y') { + FREE(yd->cookie_y); + FREE(yd->login_cookie); + + yd->cookie_y = getcookie(pair->value); + yd->login_cookie = getlcookie(yd->cookie_y); + + } else if(pair->value[0]=='T') { + FREE(yd->cookie_t); + yd->cookie_t = getcookie(pair->value); + + } else if(pair->value[0]=='C') { + FREE(yd->cookie_c); + yd->cookie_c = getcookie(pair->value); + } + + if(yd->cookie_y && yd->cookie_t && yd->cookie_c) + YAHOO_CALLBACK(ext_yahoo_got_cookies)(yd->client_id); + + break; + case 3: /* my id */ + case 90: /* 1 */ + case 100: /* 0 */ + case 101: /* NULL */ + case 102: /* NULL */ + case 93: /* 86400/1440 */ + break; + } + } +} + +static void yahoo_process_verify(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +{ + struct yahoo_data *yd = yid->yd; + + if(pkt->status != 0x01) { + DEBUG_MSG(("expected status: 0x01, got: %d", pkt->status)); + YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, YAHOO_LOGIN_LOCK, ""); + return; + } + + pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH, YAHOO_STATUS_AVAILABLE, yd->session_id); + + yahoo_packet_hash(pkt, 1, yd->user); + yahoo_send_packet(yid, pkt, 0); + + yahoo_packet_free(pkt); + +} + +static void yahoo_process_auth_pre_0x0b(struct yahoo_input_data *yid, + const char *seed, const char *sn) +{ + struct yahoo_data *yd = yid->yd; + + /* So, Yahoo has stopped supporting its older clients in India, and + * undoubtedly will soon do so in the rest of the world. + * + * The new clients use this authentication method. I warn you in + * advance, it's bizzare, convoluted, inordinately complicated. + * It's also no more secure than crypt() was. The only purpose this + * scheme could serve is to prevent third part clients from connecting + * to their servers. + * + * Sorry, Yahoo. + */ + + struct yahoo_packet *pack; + + md5_byte_t result[16]; + md5_state_t ctx; + char *crypt_result; + unsigned char *password_hash = malloc(25); + unsigned char *crypt_hash = malloc(25); + unsigned char *hash_string_p = malloc(50 + strlen(sn)); + unsigned char *hash_string_c = malloc(50 + strlen(sn)); + + char checksum; + + int sv; + + unsigned char *result6 = malloc(25); + unsigned char *result96 = malloc(25); + + sv = seed[15]; + sv = (sv % 8) % 5; + + md5_init(&ctx); + md5_append(&ctx, (md5_byte_t *)yd->password, strlen(yd->password)); + md5_finish(&ctx, result); + to_y64(password_hash, result, 16); + + md5_init(&ctx); + crypt_result = yahoo_crypt(yd->password, "$1$_2S43d5f$"); + md5_append(&ctx, (md5_byte_t *)crypt_result, strlen(crypt_result)); + md5_finish(&ctx, result); + to_y64(crypt_hash, result, 16); + free(crypt_result); + + switch (sv) { + case 0: + checksum = seed[seed[7] % 16]; + snprintf((char *)hash_string_p, strlen(sn) + 50, + "%c%s%s%s", checksum, password_hash, yd->user, seed); + snprintf((char *)hash_string_c, strlen(sn) + 50, + "%c%s%s%s", checksum, crypt_hash, yd->user, seed); + break; + case 1: + checksum = seed[seed[9] % 16]; + snprintf((char *)hash_string_p, strlen(sn) + 50, + "%c%s%s%s", checksum, yd->user, seed, password_hash); + snprintf((char *)hash_string_c, strlen(sn) + 50, + "%c%s%s%s", checksum, yd->user, seed, crypt_hash); + break; + case 2: + checksum = seed[seed[15] % 16]; + snprintf((char *)hash_string_p, strlen(sn) + 50, + "%c%s%s%s", checksum, seed, password_hash, yd->user); + snprintf((char *)hash_string_c, strlen(sn) + 50, + "%c%s%s%s", checksum, seed, crypt_hash, yd->user); + break; + case 3: + checksum = seed[seed[1] % 16]; + snprintf((char *)hash_string_p, strlen(sn) + 50, + "%c%s%s%s", checksum, yd->user, password_hash, seed); + snprintf((char *)hash_string_c, strlen(sn) + 50, + "%c%s%s%s", checksum, yd->user, crypt_hash, seed); + break; + case 4: + checksum = seed[seed[3] % 16]; + snprintf((char *)hash_string_p, strlen(sn) + 50, + "%c%s%s%s", checksum, password_hash, seed, yd->user); + snprintf((char *)hash_string_c, strlen(sn) + 50, + "%c%s%s%s", checksum, crypt_hash, seed, yd->user); + break; + } + + md5_init(&ctx); + md5_append(&ctx, (md5_byte_t *)hash_string_p, strlen((char *)hash_string_p)); + md5_finish(&ctx, result); + to_y64(result6, result, 16); + + md5_init(&ctx); + md5_append(&ctx, (md5_byte_t *)hash_string_c, strlen((char *)hash_string_c)); + md5_finish(&ctx, result); + to_y64(result96, result, 16); + + pack = yahoo_packet_new(YAHOO_SERVICE_AUTHRESP, yd->initial_status, yd->session_id); + yahoo_packet_hash(pack, 0, yd->user); + yahoo_packet_hash(pack, 6, (char *)result6); + yahoo_packet_hash(pack, 96, (char *)result96); + yahoo_packet_hash(pack, 1, yd->user); + + yahoo_send_packet(yid, pack, 0); + + FREE(result6); + FREE(result96); + FREE(password_hash); + FREE(crypt_hash); + FREE(hash_string_p); + FREE(hash_string_c); + + yahoo_packet_free(pack); + +} + +/* + * New auth protocol cracked by Cerulean Studios and sent in to Gaim + */ +static void yahoo_process_auth_0x0b(struct yahoo_input_data *yid, const char *seed, const char *sn) +{ + struct yahoo_packet *pack = NULL; + struct yahoo_data *yd = yid->yd; + + md5_byte_t result[16]; + md5_state_t ctx; + + SHA_CTX ctx1; + SHA_CTX ctx2; + + char *alphabet1 = "FBZDWAGHrJTLMNOPpRSKUVEXYChImkwQ"; + char *alphabet2 = "F0E1D2C3B4A59687abcdefghijklmnop"; + + char *challenge_lookup = "qzec2tb3um1olpar8whx4dfgijknsvy5"; + char *operand_lookup = "+|&%/*^-"; + char *delimit_lookup = ",;"; + + unsigned char *password_hash = malloc(25); + unsigned char *crypt_hash = malloc(25); + char *crypt_result = NULL; + unsigned char pass_hash_xor1[64]; + unsigned char pass_hash_xor2[64]; + unsigned char crypt_hash_xor1[64]; + unsigned char crypt_hash_xor2[64]; + unsigned char chal[7]; + char resp_6[100]; + char resp_96[100]; + + unsigned char digest1[20]; + unsigned char digest2[20]; + unsigned char magic_key_char[4]; + const unsigned char *magic_ptr; + + unsigned int magic[64]; + unsigned int magic_work=0; + + char comparison_src[20]; + + int x, j, i; + int cnt = 0; + int magic_cnt = 0; + int magic_len; + int depth =0, table =0; + + memset(&pass_hash_xor1, 0, 64); + memset(&pass_hash_xor2, 0, 64); + memset(&crypt_hash_xor1, 0, 64); + memset(&crypt_hash_xor2, 0, 64); + memset(&digest1, 0, 20); + memset(&digest2, 0, 20); + memset(&magic, 0, 64); + memset(&resp_6, 0, 100); + memset(&resp_96, 0, 100); + memset(&magic_key_char, 0, 4); + + /* + * Magic: Phase 1. Generate what seems to be a 30 + * byte value (could change if base64 + * ends up differently? I don't remember and I'm + * tired, so use a 64 byte buffer. + */ + + magic_ptr = (unsigned char *)seed; + + while (*magic_ptr != (int)NULL) { + char *loc; + + /* Ignore parentheses. */ + + if (*magic_ptr == '(' || *magic_ptr == ')') { + magic_ptr++; + continue; + } + + /* Characters and digits verify against + the challenge lookup. + */ + + if (isalpha(*magic_ptr) || isdigit(*magic_ptr)) { + loc = strchr(challenge_lookup, *magic_ptr); + if (!loc) { + /* This isn't good */ + continue; + } + + /* Get offset into lookup table and lsh 3. */ + + magic_work = loc - challenge_lookup; + magic_work <<= 3; + + magic_ptr++; + continue; + } else { + unsigned int local_store; + + loc = strchr(operand_lookup, *magic_ptr); + if (!loc) { + /* Also not good. */ + continue; + } + + local_store = loc - operand_lookup; + + /* Oops; how did this happen? */ + if (magic_cnt >= 64) + break; + + magic[magic_cnt++] = magic_work | local_store; + magic_ptr++; + continue; + } + } + + magic_len = magic_cnt; + magic_cnt = 0; + + /* Magic: Phase 2. Take generated magic value and + * sprinkle fairy dust on the values. */ + + for (magic_cnt = magic_len-2; magic_cnt >= 0; magic_cnt--) { + unsigned char byte1; + unsigned char byte2; + + /* Bad. Abort. + */ + if (magic_cnt >= magic_len) { + WARNING(("magic_cnt(%d) magic_len(%d)", magic_cnt, magic_len)) + break; + } + + byte1 = magic[magic_cnt]; + byte2 = magic[magic_cnt+1]; + + byte1 *= 0xcd; + byte1 ^= byte2; + + magic[magic_cnt+1] = byte1; + } + + /* Magic: Phase 3. This computes 20 bytes. The first 4 bytes are used as our magic + * key (and may be changed later); the next 16 bytes are an MD5 sum of the magic key + * plus 3 bytes. The 3 bytes are found by looping, and they represent the offsets + * into particular functions we'll later call to potentially alter the magic key. + * + * %-) + */ + + magic_cnt = 1; + x = 0; + + do { + unsigned int bl = 0; + unsigned int cl = magic[magic_cnt++]; + + if (magic_cnt >= magic_len) + break; + + if (cl > 0x7F) { + if (cl < 0xe0) + bl = cl = (cl & 0x1f) << 6; + else { + bl = magic[magic_cnt++]; + cl = (cl & 0x0f) << 6; + bl = ((bl & 0x3f) + cl) << 6; + } + + cl = magic[magic_cnt++]; + bl = (cl & 0x3f) + bl; + } else + bl = cl; + + comparison_src[x++] = (bl & 0xff00) >> 8; + comparison_src[x++] = bl & 0xff; + } while (x < 20); + + /* Dump magic key into a char for SHA1 action. */ + + + for(x = 0; x < 4; x++) + magic_key_char[x] = comparison_src[x]; + + /* Compute values for recursive function table! */ + memcpy( chal, magic_key_char, 4 ); + x = 1; + for( i = 0; i < 0xFFFF && x; i++ ) + { + for( j = 0; j < 5 && x; j++ ) + { + chal[4] = i; + chal[5] = i >> 8; + chal[6] = j; + md5_init( &ctx ); + md5_append( &ctx, chal, 7 ); + md5_finish( &ctx, result ); + if( memcmp( comparison_src + 4, result, 16 ) == 0 ) + { + depth = i; + table = j; + x = 0; + } + } + } + + /* Transform magic_key_char using transform table */ + x = magic_key_char[3] << 24 | magic_key_char[2] << 16 + | magic_key_char[1] << 8 | magic_key_char[0]; + x = yahoo_xfrm( table, depth, x ); + x = yahoo_xfrm( table, depth, x ); + magic_key_char[0] = x & 0xFF; + magic_key_char[1] = x >> 8 & 0xFF; + magic_key_char[2] = x >> 16 & 0xFF; + magic_key_char[3] = x >> 24 & 0xFF; + + /* Get password and crypt hashes as per usual. */ + md5_init(&ctx); + md5_append(&ctx, (md5_byte_t *)yd->password, strlen(yd->password)); + md5_finish(&ctx, result); + to_y64(password_hash, result, 16); + + md5_init(&ctx); + crypt_result = yahoo_crypt(yd->password, "$1$_2S43d5f$"); + md5_append(&ctx, (md5_byte_t *)crypt_result, strlen(crypt_result)); + md5_finish(&ctx, result); + to_y64(crypt_hash, result, 16); + free(crypt_result); + + /* Our first authentication response is based off + * of the password hash. */ + + for (x = 0; x < (int)strlen((char *)password_hash); x++) + pass_hash_xor1[cnt++] = password_hash[x] ^ 0x36; + + if (cnt < 64) + memset(&(pass_hash_xor1[cnt]), 0x36, 64-cnt); + + cnt = 0; + + for (x = 0; x < (int)strlen((char *)password_hash); x++) + pass_hash_xor2[cnt++] = password_hash[x] ^ 0x5c; + + if (cnt < 64) + memset(&(pass_hash_xor2[cnt]), 0x5c, 64-cnt); + + shaInit(&ctx1); + shaInit(&ctx2); + + /* The first context gets the password hash XORed + * with 0x36 plus a magic value + * which we previously extrapolated from our + * challenge. */ + + shaUpdate(&ctx1, pass_hash_xor1, 64); + if (j >= 3 ) + ctx1.sizeLo = 0x1ff; + shaUpdate(&ctx1, magic_key_char, 4); + shaFinal(&ctx1, digest1); + + /* The second context gets the password hash XORed + * with 0x5c plus the SHA-1 digest + * of the first context. */ + + shaUpdate(&ctx2, pass_hash_xor2, 64); + shaUpdate(&ctx2, digest1, 20); + shaFinal(&ctx2, digest2); + + /* Now that we have digest2, use it to fetch + * characters from an alphabet to construct + * our first authentication response. */ + + for (x = 0; x < 20; x += 2) { + unsigned int val = 0; + unsigned int lookup = 0; + char byte[6]; + + memset(&byte, 0, 6); + + /* First two bytes of digest stuffed + * together. + */ + + val = digest2[x]; + val <<= 8; + val += digest2[x+1]; + + lookup = (val >> 0x0b); + lookup &= 0x1f; + if (lookup >= strlen(alphabet1)) + break; + sprintf(byte, "%c", alphabet1[lookup]); + strcat(resp_6, byte); + strcat(resp_6, "="); + + lookup = (val >> 0x06); + lookup &= 0x1f; + if (lookup >= strlen(alphabet2)) + break; + sprintf(byte, "%c", alphabet2[lookup]); + strcat(resp_6, byte); + + lookup = (val >> 0x01); + lookup &= 0x1f; + if (lookup >= strlen(alphabet2)) + break; + sprintf(byte, "%c", alphabet2[lookup]); + strcat(resp_6, byte); + + lookup = (val & 0x01); + if (lookup >= strlen(delimit_lookup)) + break; + sprintf(byte, "%c", delimit_lookup[lookup]); + strcat(resp_6, byte); + } + + /* Our second authentication response is based off + * of the crypto hash. */ + + cnt = 0; + memset(&digest1, 0, 20); + memset(&digest2, 0, 20); + + for (x = 0; x < (int)strlen((char *)crypt_hash); x++) + crypt_hash_xor1[cnt++] = crypt_hash[x] ^ 0x36; + + if (cnt < 64) + memset(&(crypt_hash_xor1[cnt]), 0x36, 64-cnt); + + cnt = 0; + + for (x = 0; x < (int)strlen((char *)crypt_hash); x++) + crypt_hash_xor2[cnt++] = crypt_hash[x] ^ 0x5c; + + if (cnt < 64) + memset(&(crypt_hash_xor2[cnt]), 0x5c, 64-cnt); + + shaInit(&ctx1); + shaInit(&ctx2); + + /* The first context gets the password hash XORed + * with 0x36 plus a magic value + * which we previously extrapolated from our + * challenge. */ + + shaUpdate(&ctx1, crypt_hash_xor1, 64); + if (j >= 3 ) + ctx1.sizeLo = 0x1ff; + shaUpdate(&ctx1, magic_key_char, 4); + shaFinal(&ctx1, digest1); + + /* The second context gets the password hash XORed + * with 0x5c plus the SHA-1 digest + * of the first context. */ + + shaUpdate(&ctx2, crypt_hash_xor2, 64); + shaUpdate(&ctx2, digest1, 20); + shaFinal(&ctx2, digest2); + + /* Now that we have digest2, use it to fetch + * characters from an alphabet to construct + * our first authentication response. */ + + for (x = 0; x < 20; x += 2) { + unsigned int val = 0; + unsigned int lookup = 0; + + char byte[6]; + + memset(&byte, 0, 6); + + /* First two bytes of digest stuffed + * together. */ + + val = digest2[x]; + val <<= 8; + val += digest2[x+1]; + + lookup = (val >> 0x0b); + lookup &= 0x1f; + if (lookup >= strlen(alphabet1)) + break; + sprintf(byte, "%c", alphabet1[lookup]); + strcat(resp_96, byte); + strcat(resp_96, "="); + + lookup = (val >> 0x06); + lookup &= 0x1f; + if (lookup >= strlen(alphabet2)) + break; + sprintf(byte, "%c", alphabet2[lookup]); + strcat(resp_96, byte); + + lookup = (val >> 0x01); + lookup &= 0x1f; + if (lookup >= strlen(alphabet2)) + break; + sprintf(byte, "%c", alphabet2[lookup]); + strcat(resp_96, byte); + + lookup = (val & 0x01); + if (lookup >= strlen(delimit_lookup)) + break; + sprintf(byte, "%c", delimit_lookup[lookup]); + strcat(resp_96, byte); + } + + pack = yahoo_packet_new(YAHOO_SERVICE_AUTHRESP, yd->initial_status, yd->session_id); + yahoo_packet_hash(pack, 0, sn); + yahoo_packet_hash(pack, 6, resp_6); + yahoo_packet_hash(pack, 96, resp_96); + yahoo_packet_hash(pack, 1, sn); + yahoo_send_packet(yid, pack, 0); + yahoo_packet_free(pack); + + free(password_hash); + free(crypt_hash); +} + +static void yahoo_process_auth(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +{ + char *seed = NULL; + char *sn = NULL; + YList *l = pkt->hash; + int m = 0; + + while (l) { + struct yahoo_pair *pair = l->data; + if (pair->key == 94) + seed = pair->value; + if (pair->key == 1) + sn = pair->value; + if (pair->key == 13) + m = atoi(pair->value); + l = l->next; + } + + if (!seed) + return; + + switch (m) { + case 0: + yahoo_process_auth_pre_0x0b(yid, seed, sn); + break; + case 1: + yahoo_process_auth_0x0b(yid, seed, sn); + break; + default: + /* call error */ + WARNING(("unknown auth type %d", m)); + yahoo_process_auth_0x0b(yid, seed, sn); + break; + } +} + +static void yahoo_process_auth_resp(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +{ + struct yahoo_data *yd = yid->yd; + char *login_id; + char *handle; + char *url=NULL; + int login_status=0; + + YList *l; + + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + if (pair->key == 0) + login_id = pair->value; + else if (pair->key == 1) + handle = pair->value; + else if (pair->key == 20) + url = pair->value; + else if (pair->key == 66) + login_status = atoi(pair->value); + } + + if(pkt->status == 0xffffffff) { + YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, login_status, url); + /* yahoo_logoff(yd->client_id);*/ + } +} + +static void yahoo_process_mail(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +{ + struct yahoo_data *yd = yid->yd; + char *who = NULL; + char *email = NULL; + char *subj = NULL; + int count = 0; + YList *l; + + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + if (pair->key == 9) + count = strtol(pair->value, NULL, 10); + else if (pair->key == 43) + who = pair->value; + else if (pair->key == 42) + email = pair->value; + else if (pair->key == 18) + subj = pair->value; + else + LOG(("key: %d => value: %s", pair->key, pair->value)); + } + + if (who && email && subj) { + char from[1024]; + snprintf(from, sizeof(from), "%s (%s)", who, email); + YAHOO_CALLBACK(ext_yahoo_mail_notify)(yd->client_id, from, subj, count); + } else if(count > 0) + YAHOO_CALLBACK(ext_yahoo_mail_notify)(yd->client_id, NULL, NULL, count); +} + +static void yahoo_process_contact(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +{ + struct yahoo_data *yd = yid->yd; + char *id = NULL; + char *who = NULL; + char *msg = NULL; + char *name = NULL; + long tm = 0L; + int state = YAHOO_STATUS_AVAILABLE; + int online = FALSE; + int away = 0; + + YList *l; + + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + if (pair->key == 1) + id = pair->value; + else if (pair->key == 3) + who = pair->value; + else if (pair->key == 14) + msg = pair->value; + else if (pair->key == 7) + name = pair->value; + else if (pair->key == 10) + state = strtol(pair->value, NULL, 10); + else if (pair->key == 15) + tm = strtol(pair->value, NULL, 10); + else if (pair->key == 13) + online = strtol(pair->value, NULL, 10); + else if (pair->key == 47) + away = strtol(pair->value, NULL, 10); + } + + if (id) + YAHOO_CALLBACK(ext_yahoo_contact_added)(yd->client_id, id, who, msg); + else if (name) + YAHOO_CALLBACK(ext_yahoo_status_changed)(yd->client_id, name, state, msg, away); + else if(pkt->status == 0x07) + YAHOO_CALLBACK(ext_yahoo_rejected)(yd->client_id, who, msg); +} + +static void yahoo_process_buddyadd(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +{ + struct yahoo_data *yd = yid->yd; + char *who = NULL; + char *where = NULL; + int status = 0; + char *me = NULL; + + struct yahoo_buddy *bud=NULL; + + YList *l; + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + if (pair->key == 1) + me = pair->value; + if (pair->key == 7) + who = pair->value; + if (pair->key == 65) + where = pair->value; + if (pair->key == 66) + status = strtol(pair->value, NULL, 10); + } + + yahoo_dump_unhandled(pkt); + + if(!who) + return; + if(!where) + where = "Unknown"; + + bud = y_new0(struct yahoo_buddy, 1); + bud->id = strdup(who); + bud->group = strdup(where); + bud->real_name = NULL; + + yd->buddies = y_list_append(yd->buddies, bud); + +/* YAHOO_CALLBACK(ext_yahoo_status_changed)(yd->client_id, who, status, NULL, (status==YAHOO_STATUS_AVAILABLE?0:1)); */ +} + +static void yahoo_process_buddydel(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +{ + struct yahoo_data *yd = yid->yd; + char *who = NULL; + char *where = NULL; + int unk_66 = 0; + char *me = NULL; + struct yahoo_buddy *bud; + + YList *buddy; + + YList *l; + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + if (pair->key == 1) + me = pair->value; + else if (pair->key == 7) + who = pair->value; + else if (pair->key == 65) + where = pair->value; + else if (pair->key == 66) + unk_66 = strtol(pair->value, NULL, 10); + else + DEBUG_MSG(("unknown key: %d = %s", pair->key, pair->value)); + } + + if(!who || !where) + return; + + bud = y_new0(struct yahoo_buddy, 1); + bud->id = strdup(who); + bud->group = strdup(where); + + buddy = y_list_find_custom(yd->buddies, bud, is_same_bud); + + FREE(bud->id); + FREE(bud->group); + FREE(bud); + + if(buddy) { + bud = buddy->data; + yd->buddies = y_list_remove_link(yd->buddies, buddy); + y_list_free_1(buddy); + + FREE(bud->id); + FREE(bud->group); + FREE(bud->real_name); + FREE(bud); + + bud=NULL; + } +} + +static void yahoo_process_ignore(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +{ + char *who = NULL; + int status = 0; + char *me = NULL; + int un_ignore = 0; + + YList *l; + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + if (pair->key == 0) + who = pair->value; + if (pair->key == 1) + me = pair->value; + if (pair->key == 13) /* 1 == ignore, 2 == unignore */ + un_ignore = strtol(pair->value, NULL, 10); + if (pair->key == 66) + status = strtol(pair->value, NULL, 10); + } + + + /* + * status + * 0 - ok + * 2 - already in ignore list, could not add + * 3 - not in ignore list, could not delete + * 12 - is a buddy, could not add + */ + +/* if(status) + YAHOO_CALLBACK(ext_yahoo_error)(yd->client_id, status, who, 0); +*/ +} + +static void yahoo_process_voicechat(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +{ + char *who = NULL; + char *me = NULL; + char *room = NULL; + char *voice_room = NULL; + + YList *l; + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + if (pair->key == 4) + who = pair->value; + if (pair->key == 5) + me = pair->value; + if (pair->key == 13) + voice_room=pair->value; + if (pair->key == 57) + room=pair->value; + } + + NOTICE(("got voice chat invite from %s in %s", who, room)); + /* + * send: s:0 1:me 5:who 57:room 13:1 + * ???? s:4 5:who 10:99 19:-1615114531 + * gotr: s:4 5:who 10:99 19:-1615114615 + * ???? s:1 5:me 4:who 57:room 13:3room + * got: s:1 5:me 4:who 57:room 13:1room + * rej: s:0 1:me 5:who 57:room 13:3 + * rejr: s:4 5:who 10:99 19:-1617114599 + */ +} + +static void _yahoo_webcam_get_server_connected(int fd, int error, void *d) +{ + struct yahoo_input_data *yid = d; + char *who = yid->wcm->user; + char *data = NULL; + char *packet = NULL; + unsigned char magic_nr[] = {0, 1, 0}; + unsigned char header_len = 8; + unsigned int len = 0; + unsigned int pos = 0; + + if(error || fd <= 0) { + FREE(who); + FREE(yid); + return; + } + + yid->fd = fd; + inputs = y_list_prepend(inputs, yid); + + /* send initial packet */ + if (who) + data = strdup("<RVWCFG>"); + else + data = strdup("<RUPCFG>"); + yahoo_add_to_send_queue(yid, data, strlen(data)); + FREE(data); + + /* send data */ + if (who) + { + data = strdup("g="); + data = y_string_append(data, who); + data = y_string_append(data, "\r\n"); + } else { + data = strdup("f=1\r\n"); + } + len = strlen(data); + packet = y_new0(char, header_len + len); + packet[pos++] = header_len; + memcpy(packet + pos, magic_nr, sizeof(magic_nr)); + pos += sizeof(magic_nr); + pos += yahoo_put32(packet + pos, len); + memcpy(packet + pos, data, len); + pos += len; + yahoo_add_to_send_queue(yid, packet, pos); + FREE(packet); + FREE(data); + + yid->read_tag=YAHOO_CALLBACK(ext_yahoo_add_handler)(yid->yd->client_id, fd, YAHOO_INPUT_READ, yid); +} + +static void yahoo_webcam_get_server(struct yahoo_input_data *y, char *who, char *key) +{ + struct yahoo_input_data *yid = y_new0(struct yahoo_input_data, 1); + struct yahoo_server_settings *yss = y->yd->server_settings; + + yid->type = YAHOO_CONNECTION_WEBCAM_MASTER; + yid->yd = y->yd; + yid->wcm = y_new0(struct yahoo_webcam, 1); + yid->wcm->user = who?strdup(who):NULL; + yid->wcm->direction = who?YAHOO_WEBCAM_DOWNLOAD:YAHOO_WEBCAM_UPLOAD; + yid->wcm->key = strdup(key); + + YAHOO_CALLBACK(ext_yahoo_connect_async)(yid->yd->client_id, yss->webcam_host, yss->webcam_port, + _yahoo_webcam_get_server_connected, yid); + +} + +static YList *webcam_queue=NULL; +static void yahoo_process_webcam_key(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +{ + char *me = NULL; + char *key = NULL; + char *who = NULL; + + YList *l; + yahoo_dump_unhandled(pkt); + for (l = pkt->hash; l; l = l->next) { + struct yahoo_pair *pair = l->data; + if (pair->key == 5) + me = pair->value; + if (pair->key == 61) + key=pair->value; + } + + l = webcam_queue; + if(!l) + return; + who = l->data; + webcam_queue = y_list_remove_link(webcam_queue, webcam_queue); + y_list_free_1(l); + yahoo_webcam_get_server(yid, who, key); + FREE(who); +} + +static void yahoo_packet_process(struct yahoo_input_data *yid, struct yahoo_packet *pkt) +{ + DEBUG_MSG(("yahoo_packet_process: 0x%02x", pkt->service)); + switch (pkt->service) + { + case YAHOO_SERVICE_USERSTAT: + case YAHOO_SERVICE_LOGON: + case YAHOO_SERVICE_LOGOFF: + case YAHOO_SERVICE_ISAWAY: + case YAHOO_SERVICE_ISBACK: + case YAHOO_SERVICE_GAMELOGON: + case YAHOO_SERVICE_GAMELOGOFF: + case YAHOO_SERVICE_IDACT: + case YAHOO_SERVICE_IDDEACT: + yahoo_process_status(yid, pkt); + break; + case YAHOO_SERVICE_NOTIFY: + yahoo_process_notify(yid, pkt); + break; + case YAHOO_SERVICE_MESSAGE: + case YAHOO_SERVICE_GAMEMSG: + case YAHOO_SERVICE_SYSMESSAGE: + yahoo_process_message(yid, pkt); + break; + case YAHOO_SERVICE_NEWMAIL: + yahoo_process_mail(yid, pkt); + break; + case YAHOO_SERVICE_NEWCONTACT: + yahoo_process_contact(yid, pkt); + break; + case YAHOO_SERVICE_LIST: + yahoo_process_list(yid, pkt); + break; + case YAHOO_SERVICE_VERIFY: + yahoo_process_verify(yid, pkt); + break; + case YAHOO_SERVICE_AUTH: + yahoo_process_auth(yid, pkt); + break; + case YAHOO_SERVICE_AUTHRESP: + yahoo_process_auth_resp(yid, pkt); + break; + case YAHOO_SERVICE_CONFINVITE: + case YAHOO_SERVICE_CONFADDINVITE: + case YAHOO_SERVICE_CONFDECLINE: + case YAHOO_SERVICE_CONFLOGON: + case YAHOO_SERVICE_CONFLOGOFF: + case YAHOO_SERVICE_CONFMSG: + yahoo_process_conference(yid, pkt); + break; + case YAHOO_SERVICE_CHATONLINE: + case YAHOO_SERVICE_CHATGOTO: + case YAHOO_SERVICE_CHATJOIN: + case YAHOO_SERVICE_CHATLEAVE: + case YAHOO_SERVICE_CHATEXIT: + case YAHOO_SERVICE_CHATLOGOUT: + case YAHOO_SERVICE_CHATPING: + case YAHOO_SERVICE_COMMENT: + yahoo_process_chat(yid, pkt); + break; + case YAHOO_SERVICE_P2PFILEXFER: + case YAHOO_SERVICE_FILETRANSFER: + yahoo_process_filetransfer(yid, pkt); + break; + case YAHOO_SERVICE_ADDBUDDY: + yahoo_process_buddyadd(yid, pkt); + break; + case YAHOO_SERVICE_REMBUDDY: + yahoo_process_buddydel(yid, pkt); + break; + case YAHOO_SERVICE_IGNORECONTACT: + yahoo_process_ignore(yid, pkt); + break; + case YAHOO_SERVICE_VOICECHAT: + yahoo_process_voicechat(yid, pkt); + break; + case YAHOO_SERVICE_WEBCAM: + yahoo_process_webcam_key(yid, pkt); + break; + case YAHOO_SERVICE_IDLE: + case YAHOO_SERVICE_MAILSTAT: + case YAHOO_SERVICE_CHATINVITE: + case YAHOO_SERVICE_CALENDAR: + case YAHOO_SERVICE_NEWPERSONALMAIL: + case YAHOO_SERVICE_ADDIDENT: + case YAHOO_SERVICE_ADDIGNORE: + case YAHOO_SERVICE_PING: + case YAHOO_SERVICE_GOTGROUPRENAME: + case YAHOO_SERVICE_GROUPRENAME: + case YAHOO_SERVICE_PASSTHROUGH2: + case YAHOO_SERVICE_CHATLOGON: + case YAHOO_SERVICE_CHATLOGOFF: + case YAHOO_SERVICE_CHATMSG: + case YAHOO_SERVICE_REJECTCONTACT: + case YAHOO_SERVICE_PEERTOPEER: + WARNING(("unhandled service 0x%02x", pkt->service)); + yahoo_dump_unhandled(pkt); + break; + default: + WARNING(("unknown service 0x%02x", pkt->service)); + yahoo_dump_unhandled(pkt); + break; + } +} + +static struct yahoo_packet * yahoo_getdata(struct yahoo_input_data * yid) +{ + struct yahoo_packet *pkt; + struct yahoo_data *yd = yid->yd; + int pos = 0; + int pktlen; + + if(!yd) + return NULL; + + DEBUG_MSG(("rxlen is %d", yid->rxlen)); + if (yid->rxlen < YAHOO_PACKET_HDRLEN) { + DEBUG_MSG(("len < YAHOO_PACKET_HDRLEN")); + return NULL; + } + + pos += 4; /* YMSG */ + pos += 2; + pos += 2; + + pktlen = yahoo_get16(yid->rxqueue + pos); pos += 2; + DEBUG_MSG(("%d bytes to read, rxlen is %d", + pktlen, yid->rxlen)); + + if (yid->rxlen < (YAHOO_PACKET_HDRLEN + pktlen)) { + DEBUG_MSG(("len < YAHOO_PACKET_HDRLEN + pktlen")); + return NULL; + } + + LOG(("reading packet")); + yahoo_packet_dump(yid->rxqueue, YAHOO_PACKET_HDRLEN + pktlen); + + pkt = yahoo_packet_new(0, 0, 0); + + pkt->service = yahoo_get16(yid->rxqueue + pos); pos += 2; + pkt->status = yahoo_get32(yid->rxqueue + pos); pos += 4; + DEBUG_MSG(("Yahoo Service: 0x%02x Status: %d", pkt->service, + pkt->status)); + pkt->id = yahoo_get32(yid->rxqueue + pos); pos += 4; + + yd->session_id = pkt->id; + + yahoo_packet_read(pkt, yid->rxqueue + pos, pktlen); + + yid->rxlen -= YAHOO_PACKET_HDRLEN + pktlen; + DEBUG_MSG(("rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue)); + if (yid->rxlen>0) { + unsigned char *tmp = y_memdup(yid->rxqueue + YAHOO_PACKET_HDRLEN + + pktlen, yid->rxlen); + FREE(yid->rxqueue); + yid->rxqueue = tmp; + DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue)); + } else { + DEBUG_MSG(("freed rxqueue == %p", yid->rxqueue)); + FREE(yid->rxqueue); + } + + return pkt; +} + +static void yahoo_yab_read(struct yab *yab, unsigned char *d, int len) +{ + char *st, *en; + char *data = (char *)d; + data[len]='\0'; + + DEBUG_MSG(("Got yab: %s", data)); + st = en = strstr(data, "userid=\""); + if(st) { + st += strlen("userid=\""); + en = strchr(st, '"'); *en++ = '\0'; + yab->id = yahoo_xmldecode(st); + } + + st = strstr(en, "fname=\""); + if(st) { + st += strlen("fname=\""); + en = strchr(st, '"'); *en++ = '\0'; + yab->fname = yahoo_xmldecode(st); + } + + st = strstr(en, "lname=\""); + if(st) { + st += strlen("lname=\""); + en = strchr(st, '"'); *en++ = '\0'; + yab->lname = yahoo_xmldecode(st); + } + + st = strstr(en, "nname=\""); + if(st) { + st += strlen("nname=\""); + en = strchr(st, '"'); *en++ = '\0'; + yab->nname = yahoo_xmldecode(st); + } + + st = strstr(en, "email=\""); + if(st) { + st += strlen("email=\""); + en = strchr(st, '"'); *en++ = '\0'; + yab->email = yahoo_xmldecode(st); + } + + st = strstr(en, "hphone=\""); + if(st) { + st += strlen("hphone=\""); + en = strchr(st, '"'); *en++ = '\0'; + yab->hphone = yahoo_xmldecode(st); + } + + st = strstr(en, "wphone=\""); + if(st) { + st += strlen("wphone=\""); + en = strchr(st, '"'); *en++ = '\0'; + yab->wphone = yahoo_xmldecode(st); + } + + st = strstr(en, "mphone=\""); + if(st) { + st += strlen("mphone=\""); + en = strchr(st, '"'); *en++ = '\0'; + yab->mphone = yahoo_xmldecode(st); + } + + st = strstr(en, "dbid=\""); + if(st) { + st += strlen("dbid=\""); + en = strchr(st, '"'); *en++ = '\0'; + yab->dbid = atoi(st); + } +} + +static struct yab * yahoo_getyab(struct yahoo_input_data *yid) +{ + struct yab *yab = NULL; + int pos = 0, end=0; + struct yahoo_data *yd = yid->yd; + + if(!yd) + return NULL; + + DEBUG_MSG(("rxlen is %d", yid->rxlen)); + + if(yid->rxlen <= strlen("<record")) + return NULL; + + /* start with <record */ + while(pos < yid->rxlen-strlen("<record")+1 + && memcmp(yid->rxqueue + pos, "<record", strlen("<record"))) + pos++; + + if(pos >= yid->rxlen-1) + return NULL; + + end = pos+2; + /* end with /> */ + while(end < yid->rxlen-strlen("/>")+1 && memcmp(yid->rxqueue + end, "/>", strlen("/>"))) + end++; + + if(end >= yid->rxlen-1) + return NULL; + + yab = y_new0(struct yab, 1); + yahoo_yab_read(yab, yid->rxqueue + pos, end+2-pos); + + + yid->rxlen -= end+1; + DEBUG_MSG(("rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue)); + if (yid->rxlen>0) { + unsigned char *tmp = y_memdup(yid->rxqueue + end + 1, yid->rxlen); + FREE(yid->rxqueue); + yid->rxqueue = tmp; + DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue)); + } else { + DEBUG_MSG(("freed rxqueue == %p", yid->rxqueue)); + FREE(yid->rxqueue); + } + + + return yab; +} + +static char * yahoo_getwebcam_master(struct yahoo_input_data *yid) +{ + unsigned int pos=0; + unsigned int len=0; + unsigned int status=0; + char *server=NULL; + struct yahoo_data *yd = yid->yd; + + if(!yid || !yd) + return NULL; + + DEBUG_MSG(("rxlen is %d", yid->rxlen)); + + len = yid->rxqueue[pos++]; + if (yid->rxlen < len) + return NULL; + + /* extract status (0 = ok, 6 = webcam not online) */ + status = yid->rxqueue[pos++]; + + if (status == 0) + { + pos += 2; /* skip next 2 bytes */ + server = y_memdup(yid->rxqueue+pos, 16); + pos += 16; + } + else if (status == 6) + { + YAHOO_CALLBACK(ext_yahoo_webcam_closed) + (yd->client_id, yid->wcm->user, 4); + } + + /* skip rest of the data */ + + yid->rxlen -= len; + DEBUG_MSG(("rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue)); + if (yid->rxlen>0) { + unsigned char *tmp = y_memdup(yid->rxqueue + pos, yid->rxlen); + FREE(yid->rxqueue); + yid->rxqueue = tmp; + DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue)); + } else { + DEBUG_MSG(("freed rxqueue == %p", yid->rxqueue)); + FREE(yid->rxqueue); + } + + return server; +} + +static int yahoo_get_webcam_data(struct yahoo_input_data *yid) +{ + unsigned char reason=0; + unsigned int pos=0; + unsigned int begin=0; + unsigned int end=0; + unsigned int closed=0; + unsigned char header_len=0; + char *who; + int connect=0; + struct yahoo_data *yd = yid->yd; + + if(!yd) + return -1; + + if(!yid->wcm || !yid->wcd || !yid->rxlen) + return -1; + + DEBUG_MSG(("rxlen is %d", yid->rxlen)); + + /* if we are not reading part of image then read header */ + if (!yid->wcd->to_read) + { + header_len=yid->rxqueue[pos++]; + yid->wcd->packet_type=0; + + if (yid->rxlen < header_len) + return 0; + + if (header_len >= 8) + { + reason = yid->rxqueue[pos++]; + /* next 2 bytes should always be 05 00 */ + pos += 2; + yid->wcd->data_size = yahoo_get32(yid->rxqueue + pos); + pos += 4; + yid->wcd->to_read = yid->wcd->data_size; + } + if (header_len >= 13) + { + yid->wcd->packet_type = yid->rxqueue[pos++]; + yid->wcd->timestamp = yahoo_get32(yid->rxqueue + pos); + pos += 4; + } + + /* skip rest of header */ + pos = header_len; + } + + begin = pos; + pos += yid->wcd->to_read; + if (pos > yid->rxlen) pos = yid->rxlen; + + /* if it is not an image then make sure we have the whole packet */ + if (yid->wcd->packet_type != 0x02) { + if ((pos - begin) != yid->wcd->data_size) { + yid->wcd->to_read = 0; + return 0; + } else { + yahoo_packet_dump(yid->rxqueue + begin, pos - begin); + } + } + + DEBUG_MSG(("packet type %.2X, data length %d", yid->wcd->packet_type, + yid->wcd->data_size)); + + /* find out what kind of packet we got */ + switch (yid->wcd->packet_type) + { + case 0x00: + /* user requests to view webcam (uploading) */ + if (yid->wcd->data_size && + yid->wcm->direction == YAHOO_WEBCAM_UPLOAD) { + end = begin; + while (end <= yid->rxlen && + yid->rxqueue[end++] != 13); + if (end > begin) + { + who = y_memdup(yid->rxqueue + begin, end - begin); + who[end - begin - 1] = 0; + YAHOO_CALLBACK(ext_yahoo_webcam_viewer)(yd->client_id, who + 2, 2); + FREE(who); + } + } + + if (yid->wcm->direction == YAHOO_WEBCAM_DOWNLOAD) { + /* timestamp/status field */ + /* 0 = declined viewing permission */ + /* 1 = accepted viewing permission */ + if (yid->wcd->timestamp == 0) { + YAHOO_CALLBACK(ext_yahoo_webcam_closed)(yd->client_id, yid->wcm->user, 3); + } + } + break; + case 0x01: /* status packets?? */ + /* timestamp contains status info */ + /* 00 00 00 01 = we have data?? */ + break; + case 0x02: /* image data */ + YAHOO_CALLBACK(ext_yahoo_got_webcam_image)(yd->client_id, + yid->wcm->user, yid->rxqueue + begin, + yid->wcd->data_size, pos - begin, + yid->wcd->timestamp); + break; + case 0x05: /* response packets when uploading */ + if (!yid->wcd->data_size) { + YAHOO_CALLBACK(ext_yahoo_webcam_data_request)(yd->client_id, yid->wcd->timestamp); + } + break; + case 0x07: /* connection is closing */ + switch(reason) + { + case 0x01: /* user closed connection */ + closed = 1; + break; + case 0x0F: /* user cancelled permission */ + closed = 2; + break; + } + YAHOO_CALLBACK(ext_yahoo_webcam_closed)(yd->client_id, yid->wcm->user, closed); + break; + case 0x0C: /* user connected */ + case 0x0D: /* user disconnected */ + if (yid->wcd->data_size) { + who = y_memdup(yid->rxqueue + begin, pos - begin + 1); + who[pos - begin] = 0; + if (yid->wcd->packet_type == 0x0C) + connect=1; + else + connect=0; + YAHOO_CALLBACK(ext_yahoo_webcam_viewer)(yd->client_id, who, connect); + FREE(who); + } + break; + case 0x13: /* user data */ + /* i=user_ip (ip of the user we are viewing) */ + /* j=user_ext_ip (external ip of the user we */ + /* are viewing) */ + break; + case 0x17: /* ?? */ + break; + } + yid->wcd->to_read -= pos - begin; + + yid->rxlen -= pos; + DEBUG_MSG(("rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue)); + if (yid->rxlen>0) { + unsigned char *tmp = y_memdup(yid->rxqueue + pos, yid->rxlen); + FREE(yid->rxqueue); + yid->rxqueue = tmp; + DEBUG_MSG(("new rxlen == %d, rxqueue == %p", yid->rxlen, yid->rxqueue)); + } else { + DEBUG_MSG(("freed rxqueue == %p", yid->rxqueue)); + FREE(yid->rxqueue); + } + + /* If we read a complete packet return success */ + if (!yid->wcd->to_read) + return 1; + + return 0; +} + +int yahoo_write_ready(int id, int fd, void *data) +{ + struct yahoo_input_data *yid = data; + int len; + struct data_queue *tx; + + LOG(("write callback: id=%d fd=%d data=%p", id, fd, data)); + if(!yid || !yid->txqueues) + return -2; + + tx = yid->txqueues->data; + LOG(("writing %d bytes", tx->len)); + len = yahoo_send_data(fd, tx->queue, MIN(1024, tx->len)); + + if(len == -1 && errno == EAGAIN) + return 1; + + if(len <= 0) { + int e = errno; + DEBUG_MSG(("len == %d (<= 0)", len)); + while(yid->txqueues) { + YList *l=yid->txqueues; + tx = l->data; + free(tx->queue); + free(tx); + yid->txqueues = y_list_remove_link(yid->txqueues, yid->txqueues); + y_list_free_1(l); + } + LOG(("yahoo_write_ready(%d, %d) len < 0", id, fd)); + YAHOO_CALLBACK(ext_yahoo_remove_handler)(id, yid->write_tag); + yid->write_tag = 0; + errno=e; + return 0; + } + + + tx->len -= len; + if(tx->len > 0) { + unsigned char *tmp = y_memdup(tx->queue + len, tx->len); + FREE(tx->queue); + tx->queue = tmp; + } else { + YList *l=yid->txqueues; + free(tx->queue); + free(tx); + yid->txqueues = y_list_remove_link(yid->txqueues, yid->txqueues); + y_list_free_1(l); + /* + if(!yid->txqueues) + LOG(("yahoo_write_ready(%d, %d) !yxqueues", id, fd)); + */ + if(!yid->txqueues) { + LOG(("yahoo_write_ready(%d, %d) !yxqueues", id, fd)); + YAHOO_CALLBACK(ext_yahoo_remove_handler)(id, yid->write_tag); + yid->write_tag = 0; + } + } + + return 1; +} + +static void yahoo_process_pager_connection(struct yahoo_input_data *yid, int over) +{ + struct yahoo_packet *pkt; + struct yahoo_data *yd = yid->yd; + int id = yd->client_id; + + if(over) + return; + + while (find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER) + && (pkt = yahoo_getdata(yid)) != NULL) { + + yahoo_packet_process(yid, pkt); + + yahoo_packet_free(pkt); + } +} + +static void yahoo_process_ft_connection(struct yahoo_input_data *yid, int over) +{ +} + +static void yahoo_process_chatcat_connection(struct yahoo_input_data *yid, int over) +{ + if(over) + return; + + if (strstr((char*)yid->rxqueue+(yid->rxlen-20), "</content>")) { + YAHOO_CALLBACK(ext_yahoo_chat_cat_xml)(yid->yd->client_id, (char*)yid->rxqueue); + } +} + +static void yahoo_process_yab_connection(struct yahoo_input_data *yid, int over) +{ + struct yahoo_data *yd = yid->yd; + struct yab *yab; + YList *buds; + int changed=0; + int id = yd->client_id; + + if(over) + return; + + while(find_input_by_id_and_type(id, YAHOO_CONNECTION_YAB) + && (yab = yahoo_getyab(yid)) != NULL) { + if(!yab->id) + continue; + changed=1; + for(buds = yd->buddies; buds; buds=buds->next) { + struct yahoo_buddy * bud = buds->data; + if(!strcmp(bud->id, yab->id)) { + bud->yab_entry = yab; + if(yab->nname) { + bud->real_name = strdup(yab->nname); + } else if(yab->fname && yab->lname) { + bud->real_name = y_new0(char, + strlen(yab->fname)+ + strlen(yab->lname)+2 + ); + sprintf(bud->real_name, "%s %s", + yab->fname, yab->lname); + } else if(yab->fname) { + bud->real_name = strdup(yab->fname); + } + break; /* for */ + } + } + } + + if(changed) + YAHOO_CALLBACK(ext_yahoo_got_buddies)(yd->client_id, yd->buddies); +} + +static void yahoo_process_search_connection(struct yahoo_input_data *yid, int over) +{ + struct yahoo_found_contact *yct=NULL; + char *p = (char *)yid->rxqueue, *np, *cp; + int k, n; + int start=0, found=0, total=0; + YList *contacts=NULL; + struct yahoo_input_data *pyid = find_input_by_id_and_type(yid->yd->client_id, YAHOO_CONNECTION_PAGER); + + if(!over || !pyid) + return; + + if(p && (p=strstr(p, "\r\n\r\n"))) { + p += 4; + + for(k = 0; (p = strchr(p, 4)) && (k < 4); k++) { + p++; + n = atoi(p); + switch(k) { + case 0: found = pyid->ys->lsearch_nfound = n; break; + case 2: start = pyid->ys->lsearch_nstart = n; break; + case 3: total = pyid->ys->lsearch_ntotal = n; break; + } + } + + if(p) + p++; + + k=0; + while(p && *p) { + cp = p; + np = strchr(p, 4); + + if(!np) + break; + *np = 0; + p = np+1; + + switch(k++) { + case 1: + if(strlen(cp) > 2 && y_list_length(contacts) < total) { + yct = y_new0(struct yahoo_found_contact, 1); + contacts = y_list_append(contacts, yct); + yct->id = cp+2; + } else { + *p = 0; + } + break; + case 2: + yct->online = !strcmp(cp, "2") ? 1 : 0; + break; + case 3: + yct->gender = cp; + break; + case 4: + yct->age = atoi(cp); + break; + case 5: + if(cp != "\005") + yct->location = cp; + k = 0; + break; + } + } + } + + YAHOO_CALLBACK(ext_yahoo_got_search_result)(yid->yd->client_id, found, start, total, contacts); + + while(contacts) { + YList *node = contacts; + contacts = y_list_remove_link(contacts, node); + free(node->data); + y_list_free_1(node); + } +} + +static void _yahoo_webcam_connected(int fd, int error, void *d) +{ + struct yahoo_input_data *yid = d; + struct yahoo_webcam *wcm = yid->wcm; + struct yahoo_data *yd = yid->yd; + char conn_type[100]; + char *data=NULL; + char *packet=NULL; + unsigned char magic_nr[] = {1, 0, 0, 0, 1}; + unsigned header_len=0; + unsigned int len=0; + unsigned int pos=0; + + if(error || fd <= 0) { + FREE(yid); + return; + } + + yid->fd = fd; + inputs = y_list_prepend(inputs, yid); + + LOG(("Connected")); + /* send initial packet */ + switch (wcm->direction) + { + case YAHOO_WEBCAM_DOWNLOAD: + data = strdup("<REQIMG>"); + break; + case YAHOO_WEBCAM_UPLOAD: + data = strdup("<SNDIMG>"); + break; + default: + return; + } + yahoo_add_to_send_queue(yid, data, strlen(data)); + FREE(data); + + /* send data */ + switch (wcm->direction) + { + case YAHOO_WEBCAM_DOWNLOAD: + header_len = 8; + data = strdup("a=2\r\nc=us\r\ne=21\r\nu="); + data = y_string_append(data, yd->user); + data = y_string_append(data, "\r\nt="); + data = y_string_append(data, wcm->key); + data = y_string_append(data, "\r\ni="); + data = y_string_append(data, wcm->my_ip); + data = y_string_append(data, "\r\ng="); + data = y_string_append(data, wcm->user); + data = y_string_append(data, "\r\no=w-2-5-1\r\np="); + snprintf(conn_type, sizeof(conn_type), "%d", wcm->conn_type); + data = y_string_append(data, conn_type); + data = y_string_append(data, "\r\n"); + break; + case YAHOO_WEBCAM_UPLOAD: + header_len = 13; + data = strdup("a=2\r\nc=us\r\nu="); + data = y_string_append(data, yd->user); + data = y_string_append(data, "\r\nt="); + data = y_string_append(data, wcm->key); + data = y_string_append(data, "\r\ni="); + data = y_string_append(data, wcm->my_ip); + data = y_string_append(data, "\r\no=w-2-5-1\r\np="); + snprintf(conn_type, sizeof(conn_type), "%d", wcm->conn_type); + data = y_string_append(data, conn_type); + data = y_string_append(data, "\r\nb="); + data = y_string_append(data, wcm->description); + data = y_string_append(data, "\r\n"); + break; + } + + len = strlen(data); + packet = y_new0(char, header_len + len); + packet[pos++] = header_len; + packet[pos++] = 0; + switch (wcm->direction) + { + case YAHOO_WEBCAM_DOWNLOAD: + packet[pos++] = 1; + packet[pos++] = 0; + break; + case YAHOO_WEBCAM_UPLOAD: + packet[pos++] = 5; + packet[pos++] = 0; + break; + } + + pos += yahoo_put32(packet + pos, len); + if (wcm->direction == YAHOO_WEBCAM_UPLOAD) + { + memcpy(packet + pos, magic_nr, sizeof(magic_nr)); + pos += sizeof(magic_nr); + } + memcpy(packet + pos, data, len); + yahoo_add_to_send_queue(yid, packet, header_len + len); + FREE(packet); + FREE(data); + + yid->read_tag=YAHOO_CALLBACK(ext_yahoo_add_handler)(yid->yd->client_id, yid->fd, YAHOO_INPUT_READ, yid); +} + +static void yahoo_webcam_connect(struct yahoo_input_data *y) +{ + struct yahoo_webcam *wcm = y->wcm; + struct yahoo_input_data *yid; + struct yahoo_server_settings *yss; + + if (!wcm || !wcm->server || !wcm->key) + return; + + yid = y_new0(struct yahoo_input_data, 1); + yid->type = YAHOO_CONNECTION_WEBCAM; + yid->yd = y->yd; + + /* copy webcam data to new connection */ + yid->wcm = y->wcm; + y->wcm = NULL; + + yss = y->yd->server_settings; + + yid->wcd = y_new0(struct yahoo_webcam_data, 1); + + LOG(("Connecting to: %s:%d", wcm->server, wcm->port)); + YAHOO_CALLBACK(ext_yahoo_connect_async)(y->yd->client_id, wcm->server, wcm->port, + _yahoo_webcam_connected, yid); + +} + +static void yahoo_process_webcam_master_connection(struct yahoo_input_data *yid, int over) +{ + char* server; + struct yahoo_server_settings *yss; + + if(over) + return; + + server = yahoo_getwebcam_master(yid); + + if (server) + { + yss = yid->yd->server_settings; + yid->wcm->server = strdup(server); + yid->wcm->port = yss->webcam_port; + yid->wcm->conn_type = yss->conn_type; + yid->wcm->my_ip = strdup(yss->local_host); + if (yid->wcm->direction == YAHOO_WEBCAM_UPLOAD) + yid->wcm->description = strdup(yss->webcam_description); + yahoo_webcam_connect(yid); + FREE(server); + } +} + +static void yahoo_process_webcam_connection(struct yahoo_input_data *yid, int over) +{ + int id = yid->yd->client_id; + int fd = yid->fd; + + if(over) + return; + + /* as long as we still have packets available keep processing them */ + while (find_input_by_id_and_fd(id, fd) + && yahoo_get_webcam_data(yid) == 1); +} + +static void (*yahoo_process_connection[])(struct yahoo_input_data *, int over) = { + yahoo_process_pager_connection, + yahoo_process_ft_connection, + yahoo_process_yab_connection, + yahoo_process_webcam_master_connection, + yahoo_process_webcam_connection, + yahoo_process_chatcat_connection, + yahoo_process_search_connection +}; + +int yahoo_read_ready(int id, int fd, void *data) +{ + struct yahoo_input_data *yid = data; + char buf[1024]; + int len; + + LOG(("read callback: id=%d fd=%d data=%p", id, fd, data)); + if(!yid) + return -2; + + + do { + len = read(fd, buf, sizeof(buf)); + } while(len == -1 && errno == EINTR); + + if(len == -1 && errno == EAGAIN) /* we'll try again later */ + return 1; + + if (len <= 0) { + int e = errno; + DEBUG_MSG(("len == %d (<= 0)", len)); + + if(yid->type == YAHOO_CONNECTION_PAGER) { + YAHOO_CALLBACK(ext_yahoo_login_response)(yid->yd->client_id, YAHOO_LOGIN_SOCK, NULL); + } + + yahoo_process_connection[yid->type](yid, 1); + yahoo_input_close(yid); + + /* no need to return an error, because we've already fixed it */ + if(len == 0) + return 1; + + errno=e; + LOG(("read error: %s", strerror(errno))); + return -1; + } + + yid->rxqueue = y_renew(unsigned char, yid->rxqueue, len + yid->rxlen); + memcpy(yid->rxqueue + yid->rxlen, buf, len); + yid->rxlen += len; + + yahoo_process_connection[yid->type](yid, 0); + + return len; +} + +int yahoo_init_with_attributes(const char *username, const char *password, ...) +{ + va_list ap; + struct yahoo_data *yd; + + yd = y_new0(struct yahoo_data, 1); + + if(!yd) + return 0; + + yd->user = strdup(username); + yd->password = strdup(password); + + yd->initial_status = -1; + yd->current_status = -1; + + yd->client_id = ++last_id; + + add_to_list(yd); + + va_start(ap, password); + yd->server_settings = _yahoo_assign_server_settings(ap); + va_end(ap); + + return yd->client_id; +} + +int yahoo_init(const char *username, const char *password) +{ + return yahoo_init_with_attributes(username, password, NULL); +} + +struct connect_callback_data { + struct yahoo_data *yd; + int tag; + int i; +}; + +static void yahoo_connected(int fd, int error, void *data) +{ + struct connect_callback_data *ccd = data; + struct yahoo_data *yd = ccd->yd; + struct yahoo_packet *pkt; + struct yahoo_input_data *yid; + struct yahoo_server_settings *yss = yd->server_settings; + + if(error) { + if(fallback_ports[ccd->i]) { + int tag; + yss->pager_port = fallback_ports[ccd->i++]; + tag = YAHOO_CALLBACK(ext_yahoo_connect_async)(yd->client_id, yss->pager_host, + yss->pager_port, yahoo_connected, ccd); + + if(tag > 0) + ccd->tag=tag; + } else { + FREE(ccd); + YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, YAHOO_LOGIN_SOCK, NULL); + } + return; + } + + FREE(ccd); + + /* fd < 0 && error == 0 means connect was cancelled */ + if(fd < 0) + return; + + pkt = yahoo_packet_new(YAHOO_SERVICE_AUTH, YAHOO_STATUS_AVAILABLE, yd->session_id); + NOTICE(("Sending initial packet")); + + yahoo_packet_hash(pkt, 1, yd->user); + + yid = y_new0(struct yahoo_input_data, 1); + yid->yd = yd; + yid->fd = fd; + inputs = y_list_prepend(inputs, yid); + + yahoo_send_packet(yid, pkt, 0); + + yahoo_packet_free(pkt); + + yid->read_tag=YAHOO_CALLBACK(ext_yahoo_add_handler)(yid->yd->client_id, yid->fd, YAHOO_INPUT_READ, yid); +} + +void yahoo_login(int id, int initial) +{ + struct yahoo_data *yd = find_conn_by_id(id); + struct connect_callback_data *ccd; + struct yahoo_server_settings *yss; + int tag; + + if(!yd) + return; + + yss = yd->server_settings; + + yd->initial_status = initial; + + ccd = y_new0(struct connect_callback_data, 1); + ccd->yd = yd; + tag = YAHOO_CALLBACK(ext_yahoo_connect_async)(yd->client_id, yss->pager_host, yss->pager_port, + yahoo_connected, ccd); + + /* + * if tag <= 0, then callback has already been called + * so ccd will have been freed + */ + if(tag > 0) + ccd->tag = tag; + else if(tag < 0) + YAHOO_CALLBACK(ext_yahoo_login_response)(yd->client_id, YAHOO_LOGIN_SOCK, NULL); +} + + +int yahoo_get_fd(int id) +{ + struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + if(!yid) + return 0; + else + return yid->fd; +} + +void yahoo_send_im(int id, const char *from, const char *who, const char *what, int utf8) +{ + struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_packet *pkt = NULL; + struct yahoo_data *yd; + + if(!yid) + return; + + yd = yid->yd; + + pkt = yahoo_packet_new(YAHOO_SERVICE_MESSAGE, YAHOO_STATUS_OFFLINE, yd->session_id); + + if(from && strcmp(from, yd->user)) + yahoo_packet_hash(pkt, 0, yd->user); + yahoo_packet_hash(pkt, 1, from?from:yd->user); + yahoo_packet_hash(pkt, 5, who); + yahoo_packet_hash(pkt, 14, what); + + if(utf8) + yahoo_packet_hash(pkt, 97, "1"); + + yahoo_packet_hash(pkt, 63, ";0"); /* imvironment name; or ;0 */ + yahoo_packet_hash(pkt, 64, "0"); + + + yahoo_send_packet(yid, pkt, 0); + + yahoo_packet_free(pkt); +} + +void yahoo_send_typing(int id, const char *from, const char *who, int typ) +{ + struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt = NULL; + if(!yid) + return; + + yd = yid->yd; + pkt = yahoo_packet_new(YAHOO_SERVICE_NOTIFY, YAHOO_STATUS_NOTIFY, yd->session_id); + + yahoo_packet_hash(pkt, 5, who); + yahoo_packet_hash(pkt, 4, from?from:yd->user); + yahoo_packet_hash(pkt, 14, " "); + yahoo_packet_hash(pkt, 13, typ ? "1" : "0"); + yahoo_packet_hash(pkt, 49, "TYPING"); + + yahoo_send_packet(yid, pkt, 0); + + yahoo_packet_free(pkt); +} + +void yahoo_set_away(int id, enum yahoo_status state, const char *msg, int away) +{ + struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt = NULL; + int service; + char s[4]; + + if(!yid) + return; + + yd = yid->yd; + + if (msg) { + yd->current_status = YAHOO_STATUS_CUSTOM; + } else { + yd->current_status = state; + } + + if (yd->current_status == YAHOO_STATUS_AVAILABLE) + service = YAHOO_SERVICE_ISBACK; + else + service = YAHOO_SERVICE_ISAWAY; + pkt = yahoo_packet_new(service, yd->current_status, yd->session_id); + snprintf(s, sizeof(s), "%d", yd->current_status); + yahoo_packet_hash(pkt, 10, s); + if (yd->current_status == YAHOO_STATUS_CUSTOM) { + yahoo_packet_hash(pkt, 19, msg); + yahoo_packet_hash(pkt, 47, away?"1":"0"); + } + + yahoo_send_packet(yid, pkt, 0); + yahoo_packet_free(pkt); +} + +void yahoo_logoff(int id) +{ + struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt = NULL; + + if(!yid) + return; + yd = yid->yd; + + LOG(("yahoo_logoff: current status: %d", yd->current_status)); + + if(yd->current_status != -1) { + pkt = yahoo_packet_new(YAHOO_SERVICE_LOGOFF, YAHOO_STATUS_AVAILABLE, yd->session_id); + yd->current_status = -1; + + if (pkt) { + yahoo_send_packet(yid, pkt, 0); + yahoo_packet_free(pkt); + } + } + + +/* do { + yahoo_input_close(yid); + } while((yid = find_input_by_id(id)));*/ + +} + +void yahoo_get_list(int id) +{ + struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt = NULL; + + if(!yid) + return; + yd = yid->yd; + + pkt = yahoo_packet_new(YAHOO_SERVICE_LIST, YAHOO_STATUS_AVAILABLE, yd->session_id); + yahoo_packet_hash(pkt, 1, yd->user); + if (pkt) { + yahoo_send_packet(yid, pkt, 0); + yahoo_packet_free(pkt); + } +} + +static void _yahoo_http_connected(int id, int fd, int error, void *data) +{ + struct yahoo_input_data *yid = data; + if(fd <= 0) { + inputs = y_list_remove(inputs, yid); + FREE(yid); + return; + } + + yid->fd = fd; + yid->read_tag=YAHOO_CALLBACK(ext_yahoo_add_handler)(yid->yd->client_id, fd, YAHOO_INPUT_READ, yid); +} + +void yahoo_get_yab(int id) +{ + struct yahoo_data *yd = find_conn_by_id(id); + struct yahoo_input_data *yid; + char url[1024]; + char buff[1024]; + + if(!yd) + return; + + yid = y_new0(struct yahoo_input_data, 1); + yid->yd = yd; + yid->type = YAHOO_CONNECTION_YAB; + + snprintf(url, 1024, "http://insider.msg.yahoo.com/ycontent/?ab2=0"); + + snprintf(buff, sizeof(buff), "Y=%s; T=%s", + yd->cookie_y, yd->cookie_t); + + inputs = y_list_prepend(inputs, yid); + + yahoo_http_get(yid->yd->client_id, url, buff, + _yahoo_http_connected, yid); +} + +void yahoo_set_yab(int id, struct yab * yab) +{ + struct yahoo_data *yd = find_conn_by_id(id); + struct yahoo_input_data *yid; + char url[1024]; + char buff[1024]; + char *temp; + int size = sizeof(url)-1; + + if(!yd) + return; + + yid = y_new0(struct yahoo_input_data, 1); + yid->type = YAHOO_CONNECTION_YAB; + yid->yd = yd; + + strncpy(url, "http://insider.msg.yahoo.com/ycontent/?addab2=0", size); + + if(yab->dbid) { + /* change existing yab */ + char tmp[32]; + strncat(url, "&ee=1&ow=1&id=", size - strlen(url)); + snprintf(tmp, sizeof(tmp), "%d", yab->dbid); + strncat(url, tmp, size - strlen(url)); + } + + if(yab->fname) { + strncat(url, "&fn=", size - strlen(url)); + temp = yahoo_urlencode(yab->fname); + strncat(url, temp, size - strlen(url)); + free(temp); + } + if(yab->lname) { + strncat(url, "&ln=", size - strlen(url)); + temp = yahoo_urlencode(yab->lname); + strncat(url, temp, size - strlen(url)); + free(temp); + } + strncat(url, "&yid=", size - strlen(url)); + temp = yahoo_urlencode(yab->id); + strncat(url, temp, size - strlen(url)); + free(temp); + if(yab->nname) { + strncat(url, "&nn=", size - strlen(url)); + temp = yahoo_urlencode(yab->nname); + strncat(url, temp, size - strlen(url)); + free(temp); + } + if(yab->email) { + strncat(url, "&e=", size - strlen(url)); + temp = yahoo_urlencode(yab->email); + strncat(url, temp, size - strlen(url)); + free(temp); + } + if(yab->hphone) { + strncat(url, "&hp=", size - strlen(url)); + temp = yahoo_urlencode(yab->hphone); + strncat(url, temp, size - strlen(url)); + free(temp); + } + if(yab->wphone) { + strncat(url, "&wp=", size - strlen(url)); + temp = yahoo_urlencode(yab->wphone); + strncat(url, temp, size - strlen(url)); + free(temp); + } + if(yab->mphone) { + strncat(url, "&mp=", size - strlen(url)); + temp = yahoo_urlencode(yab->mphone); + strncat(url, temp, size - strlen(url)); + free(temp); + } + strncat(url, "&pp=0", size - strlen(url)); + + snprintf(buff, sizeof(buff), "Y=%s; T=%s", + yd->cookie_y, yd->cookie_t); + + inputs = y_list_prepend(inputs, yid); + + yahoo_http_get(yid->yd->client_id, url, buff, + _yahoo_http_connected, yid); +} + +void yahoo_set_identity_status(int id, const char * identity, int active) +{ + struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt = NULL; + + if(!yid) + return; + yd = yid->yd; + + pkt = yahoo_packet_new(active?YAHOO_SERVICE_IDACT:YAHOO_SERVICE_IDDEACT, + YAHOO_STATUS_AVAILABLE, yd->session_id); + yahoo_packet_hash(pkt, 3, identity); + if (pkt) { + yahoo_send_packet(yid, pkt, 0); + yahoo_packet_free(pkt); + } +} + +void yahoo_refresh(int id) +{ + struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt = NULL; + + if(!yid) + return; + yd = yid->yd; + + pkt = yahoo_packet_new(YAHOO_SERVICE_USERSTAT, YAHOO_STATUS_AVAILABLE, yd->session_id); + if (pkt) { + yahoo_send_packet(yid, pkt, 0); + yahoo_packet_free(pkt); + } +} + +void yahoo_keepalive(int id) +{ + struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt=NULL; + if(!yid) + return; + yd = yid->yd; + + pkt = yahoo_packet_new(YAHOO_SERVICE_PING, YAHOO_STATUS_AVAILABLE, yd->session_id); + yahoo_send_packet(yid, pkt, 0); + yahoo_packet_free(pkt); +} + +void yahoo_chat_keepalive (int id) +{ + struct yahoo_input_data *yid = find_input_by_id_and_type (id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt = NULL; + + if (!yid) + return; + + yd = yid->yd; + + pkt = yahoo_packet_new (YAHOO_SERVICE_CHATPING, YAHOO_STATUS_AVAILABLE, yd->session_id); + yahoo_send_packet (yid, pkt, 0); + yahoo_packet_free (pkt); +} + +void yahoo_add_buddy(int id, const char *who, const char *group) +{ + struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt; + + if(!yid) + return; + yd = yid->yd; + + if (!yd->logged_in) + return; + + pkt = yahoo_packet_new(YAHOO_SERVICE_ADDBUDDY, YAHOO_STATUS_AVAILABLE, yd->session_id); + yahoo_packet_hash(pkt, 1, yd->user); + yahoo_packet_hash(pkt, 7, who); + yahoo_packet_hash(pkt, 65, group); + yahoo_send_packet(yid, pkt, 0); + yahoo_packet_free(pkt); +} + +void yahoo_remove_buddy(int id, const char *who, const char *group) +{ + struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt = NULL; + + if(!yid) + return; + yd = yid->yd; + + pkt = yahoo_packet_new(YAHOO_SERVICE_REMBUDDY, YAHOO_STATUS_AVAILABLE, yd->session_id); + + yahoo_packet_hash(pkt, 1, yd->user); + yahoo_packet_hash(pkt, 7, who); + yahoo_packet_hash(pkt, 65, group); + yahoo_send_packet(yid, pkt, 0); + yahoo_packet_free(pkt); +} + +void yahoo_reject_buddy(int id, const char *who, const char *msg) +{ + struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt; + + if(!yid) + return; + yd = yid->yd; + + if (!yd->logged_in) + return; + + pkt = yahoo_packet_new(YAHOO_SERVICE_REJECTCONTACT, YAHOO_STATUS_AVAILABLE, yd->session_id); + yahoo_packet_hash(pkt, 1, yd->user); + yahoo_packet_hash(pkt, 7, who); + yahoo_packet_hash(pkt, 14, msg); + yahoo_send_packet(yid, pkt, 0); + yahoo_packet_free(pkt); +} + +void yahoo_ignore_buddy(int id, const char *who, int unignore) +{ + struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt; + + if(!yid) + return; + yd = yid->yd; + + if (!yd->logged_in) + return; + + pkt = yahoo_packet_new(YAHOO_SERVICE_IGNORECONTACT, YAHOO_STATUS_AVAILABLE, yd->session_id); + yahoo_packet_hash(pkt, 1, yd->user); + yahoo_packet_hash(pkt, 7, who); + yahoo_packet_hash(pkt, 13, unignore?"2":"1"); + yahoo_send_packet(yid, pkt, 0); + yahoo_packet_free(pkt); +} + +void yahoo_change_buddy_group(int id, const char *who, const char *old_group, const char *new_group) +{ + struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt = NULL; + + if(!yid) + return; + yd = yid->yd; + + pkt = yahoo_packet_new(YAHOO_SERVICE_ADDBUDDY, YAHOO_STATUS_AVAILABLE, yd->session_id); + yahoo_packet_hash(pkt, 1, yd->user); + yahoo_packet_hash(pkt, 7, who); + yahoo_packet_hash(pkt, 65, new_group); + yahoo_packet_hash(pkt, 14, " "); + + yahoo_send_packet(yid, pkt, 0); + yahoo_packet_free(pkt); + + pkt = yahoo_packet_new(YAHOO_SERVICE_REMBUDDY, YAHOO_STATUS_AVAILABLE, yd->session_id); + yahoo_packet_hash(pkt, 1, yd->user); + yahoo_packet_hash(pkt, 7, who); + yahoo_packet_hash(pkt, 65, old_group); + yahoo_send_packet(yid, pkt, 0); + yahoo_packet_free(pkt); +} + +void yahoo_group_rename(int id, const char *old_group, const char *new_group) +{ + struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt = NULL; + + if(!yid) + return; + yd = yid->yd; + + pkt = yahoo_packet_new(YAHOO_SERVICE_GROUPRENAME, YAHOO_STATUS_AVAILABLE, yd->session_id); + yahoo_packet_hash(pkt, 1, yd->user); + yahoo_packet_hash(pkt, 65, old_group); + yahoo_packet_hash(pkt, 67, new_group); + + yahoo_send_packet(yid, pkt, 0); + yahoo_packet_free(pkt); +} + +void yahoo_conference_addinvite(int id, const char * from, const char *who, const char *room, const YList * members, const char *msg) +{ + struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt; + + if(!yid) + return; + yd = yid->yd; + + pkt = yahoo_packet_new(YAHOO_SERVICE_CONFADDINVITE, YAHOO_STATUS_AVAILABLE, yd->session_id); + + yahoo_packet_hash(pkt, 1, (from?from:yd->user)); + yahoo_packet_hash(pkt, 51, who); + yahoo_packet_hash(pkt, 57, room); + yahoo_packet_hash(pkt, 58, msg); + yahoo_packet_hash(pkt, 13, "0"); + for(; members; members = members->next) { + yahoo_packet_hash(pkt, 52, (char *)members->data); + yahoo_packet_hash(pkt, 53, (char *)members->data); + } + /* 52, 53 -> other members? */ + + yahoo_send_packet(yid, pkt, 0); + + yahoo_packet_free(pkt); +} + +void yahoo_conference_invite(int id, const char * from, YList *who, const char *room, const char *msg) +{ + struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt; + + if(!yid) + return; + yd = yid->yd; + + pkt = yahoo_packet_new(YAHOO_SERVICE_CONFINVITE, YAHOO_STATUS_AVAILABLE, yd->session_id); + + yahoo_packet_hash(pkt, 1, (from?from:yd->user)); + yahoo_packet_hash(pkt, 50, yd->user); + for(; who; who = who->next) { + yahoo_packet_hash(pkt, 52, (char *)who->data); + } + yahoo_packet_hash(pkt, 57, room); + yahoo_packet_hash(pkt, 58, msg); + yahoo_packet_hash(pkt, 13, "0"); + + yahoo_send_packet(yid, pkt, 0); + + yahoo_packet_free(pkt); +} + +void yahoo_conference_logon(int id, const char *from, YList *who, const char *room) +{ + struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt; + + if(!yid) + return; + yd = yid->yd; + + pkt = yahoo_packet_new(YAHOO_SERVICE_CONFLOGON, YAHOO_STATUS_AVAILABLE, yd->session_id); + + yahoo_packet_hash(pkt, 1, (from?from:yd->user)); + for(; who; who = who->next) { + yahoo_packet_hash(pkt, 3, (char *)who->data); + } + yahoo_packet_hash(pkt, 57, room); + + yahoo_send_packet(yid, pkt, 0); + + yahoo_packet_free(pkt); +} + +void yahoo_conference_decline(int id, const char * from, YList *who, const char *room, const char *msg) +{ + struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt; + + if(!yid) + return; + yd = yid->yd; + + pkt = yahoo_packet_new(YAHOO_SERVICE_CONFDECLINE, YAHOO_STATUS_AVAILABLE, yd->session_id); + + yahoo_packet_hash(pkt, 1, (from?from:yd->user)); + for(; who; who = who->next) { + yahoo_packet_hash(pkt, 3, (char *)who->data); + } + yahoo_packet_hash(pkt, 57, room); + yahoo_packet_hash(pkt, 14, msg); + + yahoo_send_packet(yid, pkt, 0); + + yahoo_packet_free(pkt); +} + +void yahoo_conference_logoff(int id, const char * from, YList *who, const char *room) +{ + struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt; + + if(!yid) + return; + yd = yid->yd; + + pkt = yahoo_packet_new(YAHOO_SERVICE_CONFLOGOFF, YAHOO_STATUS_AVAILABLE, yd->session_id); + + yahoo_packet_hash(pkt, 1, (from?from:yd->user)); + for(; who; who = who->next) { + yahoo_packet_hash(pkt, 3, (char *)who->data); + } + yahoo_packet_hash(pkt, 57, room); + + yahoo_send_packet(yid, pkt, 0); + + yahoo_packet_free(pkt); +} + +void yahoo_conference_message(int id, const char * from, YList *who, const char *room, const char *msg, int utf8) +{ + struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt; + + if(!yid) + return; + yd = yid->yd; + + pkt = yahoo_packet_new(YAHOO_SERVICE_CONFMSG, YAHOO_STATUS_AVAILABLE, yd->session_id); + + yahoo_packet_hash(pkt, 1, (from?from:yd->user)); + for(; who; who = who->next) { + yahoo_packet_hash(pkt, 53, (char *)who->data); + } + yahoo_packet_hash(pkt, 57, room); + yahoo_packet_hash(pkt, 14, msg); + + if(utf8) + yahoo_packet_hash(pkt, 97, "1"); + + yahoo_send_packet(yid, pkt, 0); + + yahoo_packet_free(pkt); +} + +void yahoo_get_chatrooms(int id, int chatroomid) +{ + struct yahoo_data *yd = find_conn_by_id(id); + struct yahoo_input_data *yid; + char url[1024]; + char buff[1024]; + + if(!yd) + return; + + yid = y_new0(struct yahoo_input_data, 1); + yid->yd = yd; + yid->type = YAHOO_CONNECTION_CHATCAT; + + if (chatroomid == 0) { + snprintf(url, 1024, "http://insider.msg.yahoo.com/ycontent/?chatcat=0"); + } else { + snprintf(url, 1024, "http://insider.msg.yahoo.com/ycontent/?chatroom_%d=0",chatroomid); + } + + snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, yd->cookie_t); + + inputs = y_list_prepend(inputs, yid); + + yahoo_http_get(yid->yd->client_id, url, buff, _yahoo_http_connected, yid); +} + +void yahoo_chat_logon(int id, const char *from, const char *room, const char *roomid) +{ + struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt; + + if(!yid) + return; + + yd = yid->yd; + + pkt = yahoo_packet_new(YAHOO_SERVICE_CHATONLINE, YAHOO_STATUS_AVAILABLE, yd->session_id); + + yahoo_packet_hash(pkt, 1, (from?from:yd->user)); + yahoo_packet_hash(pkt, 109, yd->user); + yahoo_packet_hash(pkt, 6, "abcde"); + + yahoo_send_packet(yid, pkt, 0); + + yahoo_packet_free(pkt); + + pkt = yahoo_packet_new(YAHOO_SERVICE_CHATJOIN, YAHOO_STATUS_AVAILABLE, yd->session_id); + + yahoo_packet_hash(pkt, 1, (from?from:yd->user)); + yahoo_packet_hash(pkt, 104, room); + yahoo_packet_hash(pkt, 129, roomid); + yahoo_packet_hash(pkt, 62, "2"); /* ??? */ + + yahoo_send_packet(yid, pkt, 0); + + yahoo_packet_free(pkt); +} + + +void yahoo_chat_message(int id, const char *from, const char *room, const char *msg, const int msgtype, const int utf8) +{ + struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt; + char buf[2]; + + if(!yid) + return; + + yd = yid->yd; + + pkt = yahoo_packet_new(YAHOO_SERVICE_COMMENT, YAHOO_STATUS_AVAILABLE, yd->session_id); + + yahoo_packet_hash(pkt, 1, (from?from:yd->user)); + yahoo_packet_hash(pkt, 104, room); + yahoo_packet_hash(pkt, 117, msg); + + snprintf(buf, sizeof(buf), "%d", msgtype); + yahoo_packet_hash(pkt, 124, buf); + + if(utf8) + yahoo_packet_hash(pkt, 97, "1"); + + yahoo_send_packet(yid, pkt, 0); + + yahoo_packet_free(pkt); +} + + +void yahoo_chat_logoff(int id, const char *from) +{ + struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt; + + if(!yid) + return; + + yd = yid->yd; + + pkt = yahoo_packet_new(YAHOO_SERVICE_CHATLOGOUT, YAHOO_STATUS_AVAILABLE, yd->session_id); + + yahoo_packet_hash(pkt, 1, (from?from:yd->user)); + + yahoo_send_packet(yid, pkt, 0); + + yahoo_packet_free(pkt); +} + +void yahoo_webcam_close_feed(int id, const char *who) +{ + struct yahoo_input_data *yid = find_input_by_id_and_webcam_user(id, who); + + if(yid) + yahoo_input_close(yid); +} + +void yahoo_webcam_get_feed(int id, const char *who) +{ + struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_data *yd; + struct yahoo_packet *pkt; + + if(!yid) + return; + + /* + * add the user to the queue. this is a dirty hack, since + * the yahoo server doesn't tell us who's key it's returning, + * we have to just hope that it sends back keys in the same + * order that we request them. + * The queue is popped in yahoo_process_webcam_key + */ + webcam_queue = y_list_append(webcam_queue, who?strdup(who):NULL); + + yd = yid->yd; + + pkt = yahoo_packet_new(YAHOO_SERVICE_WEBCAM, YAHOO_STATUS_AVAILABLE, yd->session_id); + + yahoo_packet_hash(pkt, 1, yd->user); + if (who != NULL) + yahoo_packet_hash(pkt, 5, who); + yahoo_send_packet(yid, pkt, 0); + + yahoo_packet_free(pkt); +} + +void yahoo_webcam_send_image(int id, unsigned char *image, unsigned int length, unsigned int timestamp) +{ + struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_WEBCAM); + unsigned char *packet; + unsigned char header_len = 13; + unsigned int pos = 0; + + if (!yid) + return; + + packet = y_new0(unsigned char, header_len); + + packet[pos++] = header_len; + packet[pos++] = 0; + packet[pos++] = 5; /* version byte?? */ + packet[pos++] = 0; + pos += yahoo_put32(packet + pos, length); + packet[pos++] = 2; /* packet type, image */ + pos += yahoo_put32(packet + pos, timestamp); + yahoo_add_to_send_queue(yid, packet, header_len); + FREE(packet); + + if (length) + yahoo_add_to_send_queue(yid, image, length); +} + +void yahoo_webcam_accept_viewer(int id, const char* who, int accept) +{ + struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_WEBCAM); + char *packet = NULL; + char *data = NULL; + unsigned char header_len = 13; + unsigned int pos = 0; + unsigned int len = 0; + + if (!yid) + return; + + data = strdup("u="); + data = y_string_append(data, (char*)who); + data = y_string_append(data, "\r\n"); + len = strlen(data); + + packet = y_new0(char, header_len + len); + packet[pos++] = header_len; + packet[pos++] = 0; + packet[pos++] = 5; /* version byte?? */ + packet[pos++] = 0; + pos += yahoo_put32(packet + pos, len); + packet[pos++] = 0; /* packet type */ + pos += yahoo_put32(packet + pos, accept); + memcpy(packet + pos, data, len); + FREE(data); + yahoo_add_to_send_queue(yid, packet, header_len + len); + FREE(packet); +} + +void yahoo_webcam_invite(int id, const char *who) +{ + struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_packet *pkt; + + if(!yid) + return; + + pkt = yahoo_packet_new(YAHOO_SERVICE_NOTIFY, YAHOO_STATUS_NOTIFY, yid->yd->session_id); + + yahoo_packet_hash(pkt, 49, "WEBCAMINVITE"); + yahoo_packet_hash(pkt, 14, " "); + yahoo_packet_hash(pkt, 13, "0"); + yahoo_packet_hash(pkt, 1, yid->yd->user); + yahoo_packet_hash(pkt, 5, who); + yahoo_send_packet(yid, pkt, 0); + + yahoo_packet_free(pkt); +} + +static void yahoo_search_internal(int id, int t, const char *text, int g, int ar, int photo, int yahoo_only, int startpos, int total) +{ + struct yahoo_data *yd = find_conn_by_id(id); + struct yahoo_input_data *yid; + char url[1024]; + char buff[1024]; + char *ctext, *p; + + if(!yd) + return; + + yid = y_new0(struct yahoo_input_data, 1); + yid->yd = yd; + yid->type = YAHOO_CONNECTION_SEARCH; + + /* + age range + .ar=1 - 13-18, 2 - 18-25, 3 - 25-35, 4 - 35-50, 5 - 50-70, 6 - 70+ + */ + + snprintf(buff, sizeof(buff), "&.sq=%%20&.tt=%d&.ss=%d", total, startpos); + + ctext = strdup(text); + while((p = strchr(ctext, ' '))) + *p = '+'; + + snprintf(url, 1024, "http://members.yahoo.com/interests?.oc=m&.kw=%s&.sb=%d&.g=%d&.ar=0%s%s%s", + ctext, t, g, photo ? "&.p=y" : "", yahoo_only ? "&.pg=y" : "", + startpos ? buff : ""); + + FREE(ctext); + + snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, yd->cookie_t); + + inputs = y_list_prepend(inputs, yid); + yahoo_http_get(yid->yd->client_id, url, buff, _yahoo_http_connected, yid); +} + +void yahoo_search(int id, enum yahoo_search_type t, const char *text, enum yahoo_search_gender g, enum yahoo_search_agerange ar, + int photo, int yahoo_only) +{ + struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_search_state *yss; + + if(!yid) + return; + + if(!yid->ys) + yid->ys = y_new0(struct yahoo_search_state, 1); + + yss = yid->ys; + + FREE(yss->lsearch_text); + yss->lsearch_type = t; + yss->lsearch_text = strdup(text); + yss->lsearch_gender = g; + yss->lsearch_agerange = ar; + yss->lsearch_photo = photo; + yss->lsearch_yahoo_only = yahoo_only; + + yahoo_search_internal(id, t, text, g, ar, photo, yahoo_only, 0, 0); +} + +void yahoo_search_again(int id, int start) +{ + struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_PAGER); + struct yahoo_search_state *yss; + + if(!yid || !yid->ys) + return; + + yss = yid->ys; + + if(start == -1) + start = yss->lsearch_nstart + yss->lsearch_nfound; + + yahoo_search_internal(id, yss->lsearch_type, yss->lsearch_text, + yss->lsearch_gender, yss->lsearch_agerange, + yss->lsearch_photo, yss->lsearch_yahoo_only, + start, yss->lsearch_ntotal); +} + +struct send_file_data { + struct yahoo_packet *pkt; + yahoo_get_fd_callback callback; + void *user_data; +}; + +static void _yahoo_send_file_connected(int id, int fd, int error, void *data) +{ + struct yahoo_input_data *yid = find_input_by_id_and_type(id, YAHOO_CONNECTION_FT); + struct send_file_data *sfd = data; + struct yahoo_packet *pkt = sfd->pkt; + unsigned char buff[1024]; + + if(fd <= 0) { + sfd->callback(id, fd, error, sfd->user_data); + FREE(sfd); + yahoo_packet_free(pkt); + inputs = y_list_remove(inputs, yid); + FREE(yid); + return; + } + + yid->fd = fd; + yahoo_send_packet(yid, pkt, 8); + yahoo_packet_free(pkt); + + snprintf((char *)buff, sizeof(buff), "29"); + buff[2] = 0xc0; + buff[3] = 0x80; + + write(yid->fd, buff, 4); + +/* YAHOO_CALLBACK(ext_yahoo_add_handler)(nyd->fd, YAHOO_INPUT_READ); */ + + sfd->callback(id, fd, error, sfd->user_data); + FREE(sfd); + inputs = y_list_remove(inputs, yid); + /* + while(yahoo_tcp_readline(buff, sizeof(buff), nyd->fd) > 0) { + if(!strcmp(buff, "")) + break; + } + + */ + yahoo_input_close(yid); +} + +void yahoo_send_file(int id, const char *who, const char *msg, + const char *name, unsigned long size, + yahoo_get_fd_callback callback, void *data) +{ + struct yahoo_data *yd = find_conn_by_id(id); + struct yahoo_input_data *yid; + struct yahoo_server_settings *yss; + struct yahoo_packet *pkt = NULL; + char size_str[10]; + long content_length=0; + unsigned char buff[1024]; + char url[255]; + struct send_file_data *sfd; + + if(!yd) + return; + + yss = yd->server_settings; + + yid = y_new0(struct yahoo_input_data, 1); + yid->yd = yd; + yid->type = YAHOO_CONNECTION_FT; + + pkt = yahoo_packet_new(YAHOO_SERVICE_FILETRANSFER, YAHOO_STATUS_AVAILABLE, yd->session_id); + + snprintf(size_str, sizeof(size_str), "%ld", size); + + yahoo_packet_hash(pkt, 0, yd->user); + yahoo_packet_hash(pkt, 5, who); + yahoo_packet_hash(pkt, 14, msg); + yahoo_packet_hash(pkt, 27, name); + yahoo_packet_hash(pkt, 28, size_str); + + content_length = YAHOO_PACKET_HDRLEN + yahoo_packet_length(pkt); + + snprintf(url, sizeof(url), "http://%s:%d/notifyft", + yss->filetransfer_host, yss->filetransfer_port); + snprintf((char *)buff, sizeof(buff), "Y=%s; T=%s", + yd->cookie_y, yd->cookie_t); + inputs = y_list_prepend(inputs, yid); + + sfd = y_new0(struct send_file_data, 1); + sfd->pkt = pkt; + sfd->callback = callback; + sfd->user_data = data; + yahoo_http_post(yid->yd->client_id, url, (char *)buff, content_length+4+size, + _yahoo_send_file_connected, sfd); +} + + +enum yahoo_status yahoo_current_status(int id) +{ + struct yahoo_data *yd = find_conn_by_id(id); + if(!yd) + return YAHOO_STATUS_OFFLINE; + return yd->current_status; +} + +const YList * yahoo_get_buddylist(int id) +{ + struct yahoo_data *yd = find_conn_by_id(id); + if(!yd) + return NULL; + return yd->buddies; +} + +const YList * yahoo_get_ignorelist(int id) +{ + struct yahoo_data *yd = find_conn_by_id(id); + if(!yd) + return NULL; + return yd->ignore; +} + +const YList * yahoo_get_identities(int id) +{ + struct yahoo_data *yd = find_conn_by_id(id); + if(!yd) + return NULL; + return yd->identities; +} + +const char * yahoo_get_cookie(int id, const char *which) +{ + struct yahoo_data *yd = find_conn_by_id(id); + if(!yd) + return NULL; + if(!strncasecmp(which, "y", 1)) + return yd->cookie_y; + if(!strncasecmp(which, "t", 1)) + return yd->cookie_t; + if(!strncasecmp(which, "c", 1)) + return yd->cookie_c; + if(!strncasecmp(which, "login", 5)) + return yd->login_cookie; + return NULL; +} + +void yahoo_get_url_handle(int id, const char *url, + yahoo_get_url_handle_callback callback, void *data) +{ + struct yahoo_data *yd = find_conn_by_id(id); + if(!yd) + return; + + yahoo_get_url_fd(id, url, yd, callback, data); +} + +const char * yahoo_get_profile_url( void ) +{ + return profile_url; +} + diff --git a/protocols/yahoo/yahoo.c b/protocols/yahoo/yahoo.c new file mode 100644 index 00000000..77fd86cc --- /dev/null +++ b/protocols/yahoo/yahoo.c @@ -0,0 +1,958 @@ +/* + * libyahoo2 wrapper to BitlBee + * + * Mostly Copyright 2004 Wilmer van der Gaast <wilmer@gaast.net> + * + * This program is free software; you can 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 <errno.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <time.h> +#include <sys/stat.h> +#include <ctype.h> +#include "nogaim.h" +#include "yahoo2.h" +#include "yahoo2_callbacks.h" + +#define BYAHOO_DEFAULT_GROUP "Buddies" + +/* A hack to handle removal of buddies not in the group "Buddies" correctly */ +struct byahoo_buddygroups +{ + char *buddy; + char *group; +}; + +struct byahoo_data +{ + int y2_id; + int current_status; + gboolean logged_in; + GSList *buddygroups; +}; + +struct byahoo_input_data +{ + int h; + void *d; +}; + +struct byahoo_conf_invitation +{ + char *name; + struct conversation *c; + int yid; + YList *members; + struct gaim_connection *gc; +}; + +static char *yahoo_name() +{ + return "Yahoo"; +} + +static struct prpl *my_protocol = NULL; +static GSList *byahoo_inputs = NULL; +static int byahoo_chat_id = 0; + +static char *byahoo_strip( char *in ) +{ + int len; + + /* This should get rid of HTML tags at the beginning of the string. */ + while( *in ) + { + if( g_strncasecmp( in, "<font", 5 ) == 0 || + g_strncasecmp( in, "<fade", 5 ) == 0 || + g_strncasecmp( in, "<alt", 4 ) == 0 ) + { + char *s = strchr( in, '>' ); + if( !s ) + break; + + in = s + 1; + } + else if( strncmp( in, "\e[", 2 ) == 0 ) + { + char *s; + + for( s = in + 2; *s && *s != 'm'; s ++ ); + + if( *s != 'm' ) + break; + + in = s + 1; + } + else + { + break; + } + } + + /* This is supposed to get rid of the closing HTML tags at the end of the line. */ + len = strlen( in ); + while( len > 0 && in[len-1] == '>' ) + { + int blen = len; + + len --; + while( len > 0 && ( in[len] != '<' || in[len+1] != '/' ) ) + len --; + + if( len == 0 && ( in[len] != '<' || in[len+1] != '/' ) ) + { + len = blen; + break; + } + } + + return( g_strndup( in, len ) ); +} + +static void byahoo_login( struct aim_user *user ) +{ + struct gaim_connection *gc = new_gaim_conn( user ); + struct byahoo_data *yd = gc->proto_data = g_new0( struct byahoo_data, 1 ); + + yd->logged_in = FALSE; + yd->current_status = YAHOO_STATUS_AVAILABLE; + + set_login_progress( gc, 1, "Connecting" ); + yd->y2_id = yahoo_init( user->username, user->password ); + yahoo_login( yd->y2_id, yd->current_status ); +} + +static void byahoo_close( struct gaim_connection *gc ) +{ + struct byahoo_data *yd = (struct byahoo_data *) gc->proto_data; + GSList *l; + + while( gc->conversations ) + serv_got_chat_left( gc, gc->conversations->id ); + + for( l = yd->buddygroups; l; l = l->next ) + { + struct byahoo_buddygroups *bg = l->data; + + g_free( bg->buddy ); + g_free( bg->group ); + g_free( bg ); + } + g_slist_free( yd->buddygroups ); + + if( yd->logged_in ) + yahoo_logoff( yd->y2_id ); + else + yahoo_close( yd->y2_id ); + + g_free( yd ); +} + +static void byahoo_get_info(struct gaim_connection *gc, char *who) +{ + /* Just make an URL and let the user fetch the info */ + serv_got_crap(gc, "%s\n%s: %s%s", _("User Info"), + _("For now, fetch yourself"), yahoo_get_profile_url(), + who); +} + +static int byahoo_send_im( struct gaim_connection *gc, char *who, char *what, int len, int flags ) +{ + struct byahoo_data *yd = gc->proto_data; + + yahoo_send_im( yd->y2_id, NULL, who, what, 1 ); + + return 1; +} + +static int byahoo_send_typing( struct gaim_connection *gc, char *who, int typing ) +{ + struct byahoo_data *yd = gc->proto_data; + + yahoo_send_typing( yd->y2_id, NULL, who, typing ); + + return 1; +} + +static void byahoo_set_away( struct gaim_connection *gc, char *state, char *msg ) +{ + struct byahoo_data *yd = (struct byahoo_data *) gc->proto_data; + + gc->away = NULL; + + if (msg) + { + yd->current_status = YAHOO_STATUS_CUSTOM; + gc->away = ""; + } + else if (state) + { + gc->away = ""; + if( g_strcasecmp(state, "Available" ) == 0 ) + { + yd->current_status = YAHOO_STATUS_AVAILABLE; + gc->away = NULL; + } + else if( g_strcasecmp( state, "Be Right Back" ) == 0 ) + yd->current_status = YAHOO_STATUS_BRB; + else if( g_strcasecmp( state, "Busy" ) == 0 ) + yd->current_status = YAHOO_STATUS_BUSY; + else if( g_strcasecmp( state, "Not At Home" ) == 0 ) + yd->current_status = YAHOO_STATUS_NOTATHOME; + else if( g_strcasecmp( state, "Not At Desk" ) == 0 ) + yd->current_status = YAHOO_STATUS_NOTATDESK; + else if( g_strcasecmp( state, "Not In Office" ) == 0 ) + yd->current_status = YAHOO_STATUS_NOTINOFFICE; + else if( g_strcasecmp( state, "On Phone" ) == 0 ) + yd->current_status = YAHOO_STATUS_ONPHONE; + else if( g_strcasecmp( state, "On Vacation" ) == 0 ) + yd->current_status = YAHOO_STATUS_ONVACATION; + else if( g_strcasecmp( state, "Out To Lunch" ) == 0 ) + yd->current_status = YAHOO_STATUS_OUTTOLUNCH; + else if( g_strcasecmp( state, "Stepped Out" ) == 0 ) + yd->current_status = YAHOO_STATUS_STEPPEDOUT; + else if( g_strcasecmp( state, "Invisible" ) == 0 ) + yd->current_status = YAHOO_STATUS_INVISIBLE; + else if( g_strcasecmp( state, GAIM_AWAY_CUSTOM ) == 0 ) + { + if (gc->is_idle) + yd->current_status = YAHOO_STATUS_IDLE; + else + yd->current_status = YAHOO_STATUS_AVAILABLE; + + gc->away = NULL; + } + } + else if ( gc->is_idle ) + yd->current_status = YAHOO_STATUS_IDLE; + else + yd->current_status = YAHOO_STATUS_AVAILABLE; + + yahoo_set_away( yd->y2_id, yd->current_status, msg, gc->away != NULL ); +} + +static GList *byahoo_away_states( struct gaim_connection *gc ) +{ + GList *m = NULL; + + m = g_list_append( m, "Available" ); + m = g_list_append( m, "Be Right Back" ); + m = g_list_append( m, "Busy" ); + m = g_list_append( m, "Not At Home" ); + m = g_list_append( m, "Not At Desk" ); + m = g_list_append( m, "Not In Office" ); + m = g_list_append( m, "On Phone" ); + m = g_list_append( m, "On Vacation" ); + m = g_list_append( m, "Out To Lunch" ); + m = g_list_append( m, "Stepped Out" ); + m = g_list_append( m, "Invisible" ); + m = g_list_append( m, GAIM_AWAY_CUSTOM ); + + return m; +} + +static void byahoo_keepalive( struct gaim_connection *gc ) +{ + struct byahoo_data *yd = gc->proto_data; + + yahoo_keepalive( yd->y2_id ); +} + +static void byahoo_add_buddy( struct gaim_connection *gc, char *who ) +{ + struct byahoo_data *yd = (struct byahoo_data *) gc->proto_data; + + yahoo_add_buddy( yd->y2_id, who, BYAHOO_DEFAULT_GROUP ); +} + +static void byahoo_remove_buddy( struct gaim_connection *gc, char *who, char *group ) +{ + struct byahoo_data *yd = (struct byahoo_data *) gc->proto_data; + GSList *bgl; + + yahoo_remove_buddy( yd->y2_id, who, BYAHOO_DEFAULT_GROUP ); + + for( bgl = yd->buddygroups; bgl; bgl = bgl->next ) + { + struct byahoo_buddygroups *bg = bgl->data; + + if( g_strcasecmp( bg->buddy, who ) == 0 ) + yahoo_remove_buddy( yd->y2_id, who, bg->group ); + } +} + +static char *byahoo_get_status_string( struct gaim_connection *gc, int stat ) +{ + enum yahoo_status a = stat >> 1; + + switch (a) + { + case YAHOO_STATUS_BRB: + return "Be Right Back"; + case YAHOO_STATUS_BUSY: + return "Busy"; + case YAHOO_STATUS_NOTATHOME: + return "Not At Home"; + case YAHOO_STATUS_NOTATDESK: + return "Not At Desk"; + case YAHOO_STATUS_NOTINOFFICE: + return "Not In Office"; + case YAHOO_STATUS_ONPHONE: + return "On Phone"; + case YAHOO_STATUS_ONVACATION: + return "On Vacation"; + case YAHOO_STATUS_OUTTOLUNCH: + return "Out To Lunch"; + case YAHOO_STATUS_STEPPEDOUT: + return "Stepped Out"; + case YAHOO_STATUS_INVISIBLE: + return "Invisible"; + case YAHOO_STATUS_CUSTOM: + return "Away"; + case YAHOO_STATUS_IDLE: + return "Idle"; + case YAHOO_STATUS_OFFLINE: + return "Offline"; + case YAHOO_STATUS_NOTIFY: + return "Notify"; + default: + return "Away"; + } +} + +static int byahoo_chat_send( struct gaim_connection *gc, int id, char *message ) +{ + struct byahoo_data *yd = (struct byahoo_data *) gc->proto_data; + struct conversation *c; + + for( c = gc->conversations; c && c->id != id; c = c->next ); + + yahoo_conference_message( yd->y2_id, NULL, c->data, c->title, message, 1 ); + + return( 0 ); +} + +static void byahoo_chat_invite( struct gaim_connection *gc, int id, char *msg, char *who ) +{ + struct byahoo_data *yd = (struct byahoo_data *) gc->proto_data; + struct conversation *c; + + for( c = gc->conversations; c && c->id != id; c = c->next ); + + yahoo_conference_invite( yd->y2_id, NULL, c->data, c->title, msg ); +} + +static void byahoo_chat_leave( struct gaim_connection *gc, int id ) +{ + struct byahoo_data *yd = (struct byahoo_data *) gc->proto_data; + struct conversation *c; + + for( c = gc->conversations; c && c->id != id; c = c->next ); + + yahoo_conference_logoff( yd->y2_id, NULL, c->data, c->title ); + serv_got_chat_left( gc, c->id ); +} + +static int byahoo_chat_open( struct gaim_connection *gc, char *who ) +{ + struct byahoo_data *yd = (struct byahoo_data *) gc->proto_data; + struct conversation *c; + char *roomname; + YList *members; + + roomname = g_new0( char, strlen( gc->username ) + 16 ); + g_snprintf( roomname, strlen( gc->username ) + 16, "%s-Bee-%d", gc->username, byahoo_chat_id ); + + c = serv_got_joined_chat( gc, ++byahoo_chat_id, roomname ); + add_chat_buddy( c, gc->username ); + + /* FIXME: Free this thing when the chat's destroyed. We can't *always* + do this because it's not always created here. */ + c->data = members = g_new0( YList, 1 ); + members->data = g_strdup( who ); + + yahoo_conference_invite( yd->y2_id, NULL, members, roomname, "Please join my groupchat..." ); + + g_free( roomname ); + + return( 1 ); +} + +void byahoo_init( struct prpl *ret ) +{ + ret->protocol = PROTO_YAHOO; + ret->name = yahoo_name; + + ret->login = byahoo_login; + ret->close = byahoo_close; + ret->send_im = byahoo_send_im; + ret->send_typing = byahoo_send_typing; + ret->get_info = byahoo_get_info; + ret->away_states = byahoo_away_states; + ret->set_away = byahoo_set_away; + ret->keepalive = byahoo_keepalive; + ret->add_buddy = byahoo_add_buddy; + ret->remove_buddy = byahoo_remove_buddy; + ret->get_status_string = byahoo_get_status_string; + + ret->chat_send = byahoo_chat_send; + ret->chat_invite = byahoo_chat_invite; + ret->chat_leave = byahoo_chat_leave; + ret->chat_open = byahoo_chat_open; + + my_protocol = ret; +} + +static struct gaim_connection *byahoo_get_gc_by_id( int id ) +{ + GSList *l; + struct gaim_connection *gc; + struct byahoo_data *yd; + + for( l = get_connections(); l; l = l->next ) + { + gc = l->data; + yd = gc->proto_data; + + if( gc->protocol == PROTO_YAHOO && yd->y2_id == id ) + return( gc ); + } + + return( NULL ); +} + + +/* Now it's callback time! */ + +struct byahoo_connect_callback_data +{ + int fd; + yahoo_connect_callback callback; + gpointer data; + int id; +}; + +void byahoo_connect_callback( gpointer data, gint source, GaimInputCondition cond ) +{ + struct byahoo_connect_callback_data *d = data; + + if( !byahoo_get_gc_by_id( d->id ) ) + { + g_free( d ); + return; + } + + d->callback( d->fd, 0, d->data ); + g_free( d ); +} + +struct byahoo_read_ready_data +{ + int id; + int fd; + int tag; + gpointer data; +}; + +void byahoo_read_ready_callback( gpointer data, gint source, GaimInputCondition cond ) +{ + struct byahoo_read_ready_data *d = data; + + if( !byahoo_get_gc_by_id( d->id ) ) + { + /* WTF doesn't libyahoo clean this up? */ + ext_yahoo_remove_handler( d->id, d->tag ); + return; + } + + yahoo_read_ready( d->id, d->fd, d->data ); +} + +struct byahoo_write_ready_data +{ + int id; + int fd; + int tag; + gpointer data; +}; + +void byahoo_write_ready_callback( gpointer data, gint source, GaimInputCondition cond ) +{ + struct byahoo_write_ready_data *d = data; + + if( !byahoo_get_gc_by_id( d->id ) ) + { + /* WTF doesn't libyahoo clean this up? */ + ext_yahoo_remove_handler( d->id, d->tag ); + return; + } + + yahoo_write_ready( d->id, d->fd, d->data ); +} + +void ext_yahoo_login_response( int id, int succ, char *url ) +{ + struct gaim_connection *gc = byahoo_get_gc_by_id( id ); + struct byahoo_data *yd = NULL; + + if( gc == NULL ) + { + /* libyahoo2 seems to call this one twice when something + went wrong sometimes. Don't know why. Because we clean + up the connection on the first failure, the second + should be ignored. */ + + return; + } + + yd = (struct byahoo_data *) gc->proto_data; + + if( succ == YAHOO_LOGIN_OK ) + { + account_online( gc ); + + yd->logged_in = TRUE; + } + else + { + char *errstr; + char *s; + + yd->logged_in = FALSE; + + if( succ == YAHOO_LOGIN_UNAME ) + errstr = "Incorrect Yahoo! username"; + else if( succ == YAHOO_LOGIN_PASSWD ) + errstr = "Incorrect Yahoo! password"; + else if( succ == YAHOO_LOGIN_LOCK ) + errstr = "Yahoo! account locked"; + else if( succ == YAHOO_LOGIN_DUPL ) + { + errstr = "Logged in on a different machine or device"; + gc->wants_to_die = TRUE; + } + else if( succ == YAHOO_LOGIN_SOCK ) + errstr = "Socket problem"; + else + errstr = "Unknown error"; + + if( url && *url ) + { + s = g_malloc( strlen( "Error %d (%s). See %s for more information." ) + strlen( url ) + strlen( errstr ) + 16 ); + sprintf( s, "Error %d (%s). See %s for more information.", succ, errstr, url ); + } + else + { + s = g_malloc( strlen( "Error %d (%s)" ) + strlen( errstr ) + 16 ); + sprintf( s, "Error %d (%s)", succ, errstr ); + } + + if( yd->logged_in ) + hide_login_progress_error( gc, s ); + else + hide_login_progress( gc, s ); + + g_free( s ); + + signoff( gc ); + } +} + +void ext_yahoo_got_buddies( int id, YList *buds ) +{ + struct gaim_connection *gc = byahoo_get_gc_by_id( id ); + struct byahoo_data *yd = gc->proto_data; + YList *bl = buds; + + while( bl ) + { + struct yahoo_buddy *b = bl->data; + struct byahoo_buddygroups *bg; + + if( strcmp( b->group, BYAHOO_DEFAULT_GROUP ) != 0 ) + { + bg = g_new0( struct byahoo_buddygroups, 1 ); + + bg->buddy = g_strdup( b->id ); + bg->group = g_strdup( b->group ); + yd->buddygroups = g_slist_append( yd->buddygroups, bg ); + } + + add_buddy( gc, b->group, b->id, b->real_name ); + bl = bl->next; + } +} + +void ext_yahoo_got_ignore( int id, YList *igns ) +{ +} + +void ext_yahoo_got_identities( int id, YList *ids ) +{ +} + +void ext_yahoo_got_cookies( int id ) +{ +} + +void ext_yahoo_status_changed( int id, char *who, int stat, char *msg, int away ) +{ + struct gaim_connection *gc = byahoo_get_gc_by_id( id ); + + serv_got_update( gc, who, stat != YAHOO_STATUS_OFFLINE, 0, 0, 0, + ( stat != YAHOO_STATUS_AVAILABLE ) | ( stat << 1 ), 0 ); +} + +void ext_yahoo_got_im( int id, char *who, char *msg, long tm, int stat, int utf8 ) +{ + struct gaim_connection *gc = byahoo_get_gc_by_id( id ); + char *m = byahoo_strip( msg ); + + serv_got_im( gc, who, m, 0, 0, strlen( m ) ); + g_free( m ); +} + +void ext_yahoo_got_file( int id, char *who, char *url, long expires, char *msg, char *fname, unsigned long fesize ) +{ + struct gaim_connection *gc = byahoo_get_gc_by_id( id ); + + serv_got_crap( gc, "Got a file transfer (file = %s) from %s. Ignoring for now due to lack of support.", fname, who ); +} + +void ext_yahoo_typing_notify( int id, char *who, int stat ) +{ + struct gaim_connection *gc = byahoo_get_gc_by_id( id ); + + serv_got_typing( gc, who, 1 ); +} + +void ext_yahoo_system_message( int id, char *msg ) +{ + struct gaim_connection *gc = byahoo_get_gc_by_id( id ); + + serv_got_crap( gc, "Yahoo! system message: %s", msg ); +} + +void ext_yahoo_webcam_invite( int id, char *from ) +{ + struct gaim_connection *gc = byahoo_get_gc_by_id( id ); + + serv_got_crap( gc, "Got a webcam invitation from %s. IRC+webcams is a no-no though...", from ); +} + +void ext_yahoo_error( int id, char *err, int fatal ) +{ + struct gaim_connection *gc = byahoo_get_gc_by_id( id ); + + if( fatal ) + { + hide_login_progress_error( gc, err ); + signoff( gc ); + } + else + { + do_error_dialog( gc, err, "Yahoo! error" ); + } +} + +/* TODO: Clear up the mess of inp and d structures */ +int ext_yahoo_add_handler( int id, int fd, yahoo_input_condition cond, void *data ) +{ + struct byahoo_input_data *inp = g_new0( struct byahoo_input_data, 1 ); + + if( cond == YAHOO_INPUT_READ ) + { + struct byahoo_read_ready_data *d = g_new0( struct byahoo_read_ready_data, 1 ); + + d->id = id; + d->fd = fd; + d->data = data; + + inp->d = d; + d->tag = inp->h = gaim_input_add( fd, GAIM_INPUT_READ, (GaimInputFunction) byahoo_read_ready_callback, (gpointer) d ); + } + else if( cond == YAHOO_INPUT_WRITE ) + { + struct byahoo_write_ready_data *d = g_new0( struct byahoo_write_ready_data, 1 ); + + d->id = id; + d->fd = fd; + d->data = data; + + inp->d = d; + d->tag = inp->h = gaim_input_add( fd, GAIM_INPUT_WRITE, (GaimInputFunction) byahoo_write_ready_callback, (gpointer) d ); + } + else + { + g_free( inp ); + return( -1 ); + /* Panic... */ + } + + byahoo_inputs = g_slist_append( byahoo_inputs, inp ); + return( inp->h ); +} + +void ext_yahoo_remove_handler( int id, int tag ) +{ + struct byahoo_input_data *inp; + GSList *l = byahoo_inputs; + + while( l ) + { + inp = l->data; + if( inp->h == tag ) + { + g_free( inp->d ); + g_free( inp ); + byahoo_inputs = g_slist_remove( byahoo_inputs, inp ); + break; + } + l = l->next; + } + + gaim_input_remove( tag ); +} + +int ext_yahoo_connect_async( int id, char *host, int port, yahoo_connect_callback callback, void *data ) +{ + struct byahoo_connect_callback_data *d; + int fd; + + d = g_new0( struct byahoo_connect_callback_data, 1 ); + if( ( fd = proxy_connect( host, port, (GaimInputFunction) byahoo_connect_callback, (gpointer) d ) ) < 0 ) + { + g_free( d ); + return( fd ); + } + d->fd = fd; + d->callback = callback; + d->data = data; + d->id = id; + + return( fd ); +} + +/* Because we don't want asynchronous connects in BitlBee, and because + libyahoo doesn't seem to use this one anyway, this one is now defunct. */ +int ext_yahoo_connect(char *host, int port) +{ +#if 0 + struct sockaddr_in serv_addr; + static struct hostent *server; + static char last_host[256]; + int servfd; + char **p; + + if(last_host[0] || g_strcasecmp(last_host, host)!=0) { + if(!(server = gethostbyname(host))) { + return -1; + } + strncpy(last_host, host, 255); + } + + if((servfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + return -1; + } + + for (p = server->h_addr_list; *p; p++) + { + memset(&serv_addr, 0, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + memcpy(&serv_addr.sin_addr.s_addr, *p, server->h_length); + serv_addr.sin_port = htons(port); + + if(connect(servfd, (struct sockaddr *) &serv_addr, + sizeof(serv_addr)) == -1) { + return -1; + } else { + return servfd; + } + } + + closesocket(servfd); +#endif + return -1; +} + +static void byahoo_accept_conf( gpointer w, struct byahoo_conf_invitation *inv ) +{ + yahoo_conference_logon( inv->yid, NULL, inv->members, inv->name ); + add_chat_buddy( inv->c, inv->gc->username ); + g_free( inv->name ); + g_free( inv ); +} + +static void byahoo_reject_conf( gpointer w, struct byahoo_conf_invitation *inv ) +{ + yahoo_conference_decline( inv->yid, NULL, inv->members, inv->name, "User rejected groupchat" ); + serv_got_chat_left( inv->gc, inv->c->id ); + g_free( inv->name ); + g_free( inv ); +} + +void ext_yahoo_got_conf_invite( int id, char *who, char *room, char *msg, YList *members ) +{ + struct gaim_connection *gc = byahoo_get_gc_by_id( id ); + struct byahoo_conf_invitation *inv; + char txt[1024]; + YList *m; + + inv = g_malloc( sizeof( struct byahoo_conf_invitation ) ); + memset( inv, 0, sizeof( struct byahoo_conf_invitation ) ); + inv->name = g_strdup( room ); + inv->c = serv_got_joined_chat( gc, ++byahoo_chat_id, room ); + inv->c->data = members; + inv->yid = id; + inv->members = members; + inv->gc = gc; + + for( m = members; m; m = m->next ) + if( g_strcasecmp( m->data, gc->username ) != 0 ) + add_chat_buddy( inv->c, m->data ); + + g_snprintf( txt, 1024, "Got an invitation to chatroom %s from %s: %s", room, who, msg ); + + do_ask_dialog( gc, txt, inv, byahoo_accept_conf, byahoo_reject_conf ); +} + +void ext_yahoo_conf_userdecline( int id, char *who, char *room, char *msg ) +{ + struct gaim_connection *gc = byahoo_get_gc_by_id( id ); + + serv_got_crap( gc, "Invite to chatroom %s rejected by %s: %s", room, who, msg ); +} + +void ext_yahoo_conf_userjoin( int id, char *who, char *room ) +{ + struct gaim_connection *gc = byahoo_get_gc_by_id( id ); + struct conversation *c; + + for( c = gc->conversations; c && strcmp( c->title, room ) != 0; c = c->next ); + + if( c ) + add_chat_buddy( c, who ); + else if( set_getint( gc->irc, "debug" ) ) + serv_got_crap( gc, "Got ext_yahoo_conf_userjoin() from %s for unknown conference %s", who, room ); +} + +void ext_yahoo_conf_userleave( int id, char *who, char *room ) +{ + struct gaim_connection *gc = byahoo_get_gc_by_id( id ); + struct conversation *c; + + for( c = gc->conversations; c && strcmp( c->title, room ) != 0; c = c->next ); + + if( c ) + remove_chat_buddy( c, who, "" ); + else if( set_getint( gc->irc, "debug" ) ) + serv_got_crap( gc, "Got ext_yahoo_conf_userleave() from %s for unknown conference %s", who, room ); +} + +void ext_yahoo_conf_message( int id, char *who, char *room, char *msg, int utf8 ) +{ + struct gaim_connection *gc = byahoo_get_gc_by_id( id ); + char *m = byahoo_strip( msg ); + struct conversation *c; + + for( c = gc->conversations; c && strcmp( c->title, room ) != 0; c = c->next ); + + serv_got_chat_in( gc, c ? c->id : 0, who, 0, m, 0 ); + g_free( m ); +} + +void ext_yahoo_chat_cat_xml( int id, char *xml ) +{ +} + +void ext_yahoo_chat_join( int id, char *room, char *topic, YList *members, int fd ) +{ +} + +void ext_yahoo_chat_userjoin( int id, char *room, struct yahoo_chat_member *who ) +{ +} + +void ext_yahoo_chat_userleave( int id, char *room, char *who ) +{ +} + +void ext_yahoo_chat_message( int id, char *who, char *room, char *msg, int msgtype, int utf8 ) +{ +} + +void ext_yahoo_chat_yahoologout( int id ) +{ +} + +void ext_yahoo_chat_yahooerror( int id ) +{ +} + +void ext_yahoo_contact_added( int id, char *myid, char *who, char *msg ) +{ +} + +void ext_yahoo_rejected( int id, char *who, char *msg ) +{ +} + +void ext_yahoo_game_notify( int id, char *who, int stat ) +{ +} + +void ext_yahoo_mail_notify( int id, char *from, char *subj, int cnt ) +{ + struct gaim_connection *gc = byahoo_get_gc_by_id( id ); + + if( from && subj ) + serv_got_crap( gc, "Received e-mail message from %s with subject `%s'", from, subj ); + else if( cnt > 0 ) + serv_got_crap( gc, "Received %d new e-mails", cnt ); +} + +void ext_yahoo_webcam_invite_reply( int id, char *from, int accept ) +{ +} + +void ext_yahoo_webcam_closed( int id, char *who, int reason ) +{ +} + +void ext_yahoo_got_search_result( int id, int found, int start, int total, YList *contacts ) +{ +} + +void ext_yahoo_webcam_viewer( int id, char *who, int connect ) +{ +} + +void ext_yahoo_webcam_data_request( int id, int send ) +{ +} + +int ext_yahoo_log( char *fmt, ... ) +{ + return( 0 ); +} + +void ext_yahoo_got_webcam_image( int id, const char * who, const unsigned char *image, unsigned int image_size, unsigned int real_size, unsigned int timestamp ) +{ +} diff --git a/protocols/yahoo/yahoo2.h b/protocols/yahoo/yahoo2.h new file mode 100644 index 00000000..5ac5e4f9 --- /dev/null +++ b/protocols/yahoo/yahoo2.h @@ -0,0 +1,222 @@ +/* + * libyahoo2: yahoo2.h + * + * Copyright (C) 2002-2004, Philip S Tellis <philip.tellis AT gmx.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef YAHOO2_H +#define YAHOO2_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* *** BitlBee: *** */ +#include "bitlbee.h" +#undef free +#define free( x ) g_free( x ) +#undef malloc +#define malloc( x ) g_malloc( x ) +#undef calloc +#define calloc( x, y ) g_calloc( x, y ) +#undef realloc +#define realloc( x, y ) g_realloc( x, y ) +#undef strdup +#define strdup( x ) g_strdup( x ) +#undef strndup +#define strndup( x,y ) g_strndup( x,y ) +#undef snprintf +// #define snprintf( x... ) g_snprintf( x ) +#undef strcasecmp +#define strcasecmp( x,y ) g_strcasecmp( x,y ) +#undef strncasecmp +#define strncasecmp( x,y,z ) g_strncasecmp( x,y,z ) + + +#include "yahoo2_types.h" + +/* returns the socket descriptor for a given pager connection. shouldn't be needed */ +int yahoo_get_fd(int id); + +/* says how much logging to do */ +/* see yahoo2_types.h for the different values */ +int yahoo_set_log_level(enum yahoo_log_level level); +enum yahoo_log_level yahoo_get_log_level( void ); + +/* these functions should be self explanatory */ +/* who always means the buddy you're acting on */ +/* id is the successful value returned by yahoo_init */ + + +/* init returns a connection id used to identify the connection hereon */ +/* or 0 on failure */ +/* you must call init before calling any other function */ +/* + * The optional parameters to init are key/value pairs that specify + * server settings to use. This list must be NULL terminated - even + * if the list is empty. If a parameter isn't set, a default value + * will be used. Parameter keys are strings, parameter values are + * either strings or ints, depending on the key. Values passed in + * are copied, so you can use const/auto/static/pointers/whatever + * you want. Parameters are: + * NAME TYPE DEFAULT + * pager_host char * scs.msg.yahoo.com + * pager_port int 5050 + * filetransfer_host char * filetransfer.msg.yahoo.com + * filetransfer_port int 80 + * webcam_host char * webcam.yahoo.com + * webcam_port int 5100 + * webcam_description char * "" + * local_host char * "" + * conn_type int Y_WCM_DSL + * + * You should set at least local_host if you intend to use webcams + */ +int yahoo_init_with_attributes(const char *username, const char *password, ...); + +/* yahoo_init does the same as yahoo_init_with_attributes, assuming defaults + * for all attributes */ +int yahoo_init(const char *username, const char *password); + + + +/* release all resources held by this session */ +/* you need to call yahoo_close for a session only if + * yahoo_logoff is never called for it (ie, it was never logged in) */ +void yahoo_close(int id); +/* login logs in to the server */ +/* initial is of type enum yahoo_status. see yahoo2_types.h */ +void yahoo_login(int id, int initial); +void yahoo_logoff(int id); +/* reloads status of all buddies */ +void yahoo_refresh(int id); +/* activates/deactivates an identity */ +void yahoo_set_identity_status(int id, const char * identity, int active); +/* regets the entire buddy list from the server */ +void yahoo_get_list(int id); +/* download buddy contact information from your yahoo addressbook */ +void yahoo_get_yab(int id); +/* add/modify an address book entry. if yab->dbid is set, it will */ +/* modify that entry else it creates a new entry */ +void yahoo_set_yab(int id, struct yab * yab); +void yahoo_keepalive(int id); +void yahoo_chat_keepalive(int id); + +/* from is the identity you're sending from. if NULL, the default is used */ +/* utf8 is whether msg is a utf8 string or not. */ +void yahoo_send_im(int id, const char *from, const char *who, const char *msg, int utf8); +/* if type is true, send typing notice, else send stopped typing notice */ +void yahoo_send_typing(int id, const char *from, const char *who, int typ); + +/* used to set away/back status. */ +/* away says whether the custom message is an away message or a sig */ +void yahoo_set_away(int id, enum yahoo_status state, const char *msg, int away); + +void yahoo_add_buddy(int id, const char *who, const char *group); +void yahoo_remove_buddy(int id, const char *who, const char *group); +void yahoo_reject_buddy(int id, const char *who, const char *msg); +/* if unignore is true, unignore, else ignore */ +void yahoo_ignore_buddy(int id, const char *who, int unignore); +void yahoo_change_buddy_group(int id, const char *who, const char *old_group, const char *new_group); +void yahoo_group_rename(int id, const char *old_group, const char *new_group); + +void yahoo_conference_invite(int id, const char * from, YList *who, const char *room, const char *msg); +void yahoo_conference_addinvite(int id, const char * from, const char *who, const char *room, const YList * members, const char *msg); +void yahoo_conference_decline(int id, const char * from, YList *who, const char *room, const char *msg); +void yahoo_conference_message(int id, const char * from, YList *who, const char *room, const char *msg, int utf8); +void yahoo_conference_logon(int id, const char * from, YList *who, const char *room); +void yahoo_conference_logoff(int id, const char * from, YList *who, const char *room); + +/* Get a list of chatrooms */ +void yahoo_get_chatrooms(int id,int chatroomid); +/* join room with specified roomname and roomid */ +void yahoo_chat_logon(int id, const char *from, const char *room, const char *roomid); +/* Send message "msg" to room with specified roomname, msgtype is 1-normal message or 2-/me mesage */ +void yahoo_chat_message(int id, const char *from, const char *room, const char *msg, const int msgtype, const int utf8); +/* Log off chat */ +void yahoo_chat_logoff(int id, const char *from); + +/* requests a webcam feed */ +/* who is the person who's webcam you would like to view */ +/* if who is null, then you're the broadcaster */ +void yahoo_webcam_get_feed(int id, const char *who); +void yahoo_webcam_close_feed(int id, const char *who); + +/* sends an image when uploading */ +/* image points to a JPEG-2000 image, length is the length of the image */ +/* in bytes. The timestamp is the time in milliseconds since we started the */ +/* webcam. */ +void yahoo_webcam_send_image(int id, unsigned char *image, unsigned int length, unsigned int timestamp); + +/* this function should be called if we want to allow a user to watch the */ +/* webcam. Who is the user we want to accept. */ +/* Accept user (accept = 1), decline user (accept = 0) */ +void yahoo_webcam_accept_viewer(int id, const char* who, int accept); + +/* send an invitation to a user to view your webcam */ +void yahoo_webcam_invite(int id, const char *who); + +/* will set up a connection and initiate file transfer. + * callback will be called with the fd that you should write + * the file data to + */ +void yahoo_send_file(int id, const char *who, const char *msg, const char *name, unsigned long size, + yahoo_get_fd_callback callback, void *data); + +/* send a search request + */ +void yahoo_search(int id, enum yahoo_search_type t, const char *text, enum yahoo_search_gender g, enum yahoo_search_agerange ar, + int photo, int yahoo_only); + +/* continue last search + * should be called if only (start+found >= total) + * + * where the above three are passed to ext_yahoo_got_search_result + */ +void yahoo_search_again(int id, int start); + +/* returns a socket fd to a url for downloading a file. */ +void yahoo_get_url_handle(int id, const char *url, + yahoo_get_url_handle_callback callback, void *data); + +/* these should be called when input is available on a fd */ +/* registered by ext_yahoo_add_handler */ +/* if these return negative values, errno may be set */ +int yahoo_read_ready(int id, int fd, void *data); +int yahoo_write_ready(int id, int fd, void *data); + +/* utility functions. these do not hit the server */ +enum yahoo_status yahoo_current_status(int id); +const YList * yahoo_get_buddylist(int id); +const YList * yahoo_get_ignorelist(int id); +const YList * yahoo_get_identities(int id); +/* 'which' could be y, t, c or login. This may change in later versions. */ +const char * yahoo_get_cookie(int id, const char *which); + +/* returns the url used to get user profiles - you must append the user id */ +/* as of now this is http://profiles.yahoo.com/ */ +/* You'll have to do urlencoding yourself, but see yahoo_httplib.h first */ +const char * yahoo_get_profile_url( void ); + +#include "yahoo_httplib.h" + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/protocols/yahoo/yahoo2_callbacks.h b/protocols/yahoo/yahoo2_callbacks.h new file mode 100644 index 00000000..1ab8a9d7 --- /dev/null +++ b/protocols/yahoo/yahoo2_callbacks.h @@ -0,0 +1,697 @@ +/* + * libyahoo2: yahoo2_callbacks.h + * + * Copyright (C) 2002-2004, Philip S Tellis <philip.tellis AT gmx.net> + * + * This program is free software; you can 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 + * + */ + +/* + * The functions in this file *must* be defined in your client program + * If you want to use a callback structure instead of direct functions, + * then you must define USE_STRUCT_CALLBACKS in all files that #include + * this one. + * + * Register the callback structure by calling yahoo_register_callbacks - + * declared in this file and defined in libyahoo2.c + */ + + + +#ifndef YAHOO2_CALLBACKS_H +#define YAHOO2_CALLBACKS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "yahoo2_types.h" + +/* + * yahoo2_callbacks.h + * + * Callback interface for libyahoo2 + */ + +typedef enum { + YAHOO_INPUT_READ = 1 << 0, + YAHOO_INPUT_WRITE = 1 << 1, + YAHOO_INPUT_EXCEPTION = 1 << 2 +} yahoo_input_condition; + +/* + * A callback function called when an asynchronous connect completes. + * + * Params: + * fd - The file descriptor that has been connected, or -1 on error + * error - The value of errno set by the call to connect or 0 if no error + * Set both fd and error to 0 if the connect was cancelled by the + * user + * callback_data - the callback_data passed to the ext_yahoo_connect_async + * function + */ +typedef void (*yahoo_connect_callback)(int fd, int error, void *callback_data); + + + +/* + * The following functions need to be implemented in the client + * interface. They will be called by the library when each + * event occurs. + */ + +/* + * should we use a callback structure or directly call functions + * if you want the structure, you *must* define USE_STRUCT_CALLBACKS + * both when you compile the library, and when you compile your code + * that uses the library + */ + +#ifdef USE_STRUCT_CALLBACKS +#define YAHOO_CALLBACK_TYPE(x) (*x) +struct yahoo_callbacks { +#else +#define YAHOO_CALLBACK_TYPE(x) x +#endif + +/* + * Name: ext_yahoo_login_response + * Called when the login process is complete + * Params: + * id - the id that identifies the server connection + * succ - enum yahoo_login_status + * url - url to reactivate account if locked + */ +void YAHOO_CALLBACK_TYPE(ext_yahoo_login_response)(int id, int succ, char *url); + + + + +/* + * Name: ext_yahoo_got_buddies + * Called when the contact list is got from the server + * Params: + * id - the id that identifies the server connection + * buds - the buddy list + */ +void YAHOO_CALLBACK_TYPE(ext_yahoo_got_buddies)(int id, YList * buds); + + + + +/* + * Name: ext_yahoo_got_ignore + * Called when the ignore list is got from the server + * Params: + * id - the id that identifies the server connection + * igns - the ignore list + */ +void YAHOO_CALLBACK_TYPE(ext_yahoo_got_ignore)(int id, YList * igns); + + + + + +/* + * Name: ext_yahoo_got_identities + * Called when the contact list is got from the server + * Params: + * id - the id that identifies the server connection + * ids - the identity list + */ +void YAHOO_CALLBACK_TYPE(ext_yahoo_got_identities)(int id, YList * ids); + + + + + +/* + * Name: ext_yahoo_got_cookies + * Called when the cookie list is got from the server + * Params: + * id - the id that identifies the server connection + */ +void YAHOO_CALLBACK_TYPE(ext_yahoo_got_cookies)(int id); + + + + +/* + * Name: ext_yahoo_status_changed + * Called when remote user's status changes. + * Params: + * id - the id that identifies the server connection + * who - the handle of the remote user + * stat - status code (enum yahoo_status) + * msg - the message if stat == YAHOO_STATUS_CUSTOM + * away - whether the contact is away or not (YAHOO_STATUS_CUSTOM) + * for YAHOO_STATUS_IDLE, this is the number of seconds he is idle + */ +void YAHOO_CALLBACK_TYPE(ext_yahoo_status_changed)(int id, char *who, int stat, char *msg, int away); + + + + +/* + * Name: ext_yahoo_got_im + * Called when remote user sends you a message. + * Params: + * id - the id that identifies the server connection + * who - the handle of the remote user + * msg - the message - NULL if stat == 2 + * tm - timestamp of message if offline + * stat - message status - 0 + * 1 + * 2 == error sending message + * 5 + * utf8 - whether the message is encoded as utf8 or not + */ +void YAHOO_CALLBACK_TYPE(ext_yahoo_got_im)(int id, char *who, char *msg, long tm, int stat, int utf8); + + + + +/* + * Name: ext_yahoo_got_conf_invite + * Called when remote user sends you a conference invitation. + * Params: + * id - the id that identifies the server connection + * who - the user inviting you + * room - the room to join + * msg - the message + * members - the initial members of the conference (null terminated list) + */ +void YAHOO_CALLBACK_TYPE(ext_yahoo_got_conf_invite)(int id, char *who, char *room, char *msg, YList *members); + + + + +/* + * Name: ext_yahoo_conf_userdecline + * Called when someone declines to join the conference. + * Params: + * id - the id that identifies the server connection + * who - the user who has declined + * room - the room + * msg - the declining message + */ +void YAHOO_CALLBACK_TYPE(ext_yahoo_conf_userdecline)(int id, char *who, char *room, char *msg); + + + + +/* + * Name: ext_yahoo_conf_userjoin + * Called when someone joins the conference. + * Params: + * id - the id that identifies the server connection + * who - the user who has joined + * room - the room joined + */ +void YAHOO_CALLBACK_TYPE(ext_yahoo_conf_userjoin)(int id, char *who, char *room); + + + + +/* + * Name: ext_yahoo_conf_userleave + * Called when someone leaves the conference. + * Params: + * id - the id that identifies the server connection + * who - the user who has left + * room - the room left + */ +void YAHOO_CALLBACK_TYPE(ext_yahoo_conf_userleave)(int id, char *who, char *room); + + + + + + +/* + * Name: ext_yahoo_chat_cat_xml + * Called when joining the chatroom. + * Params: + * id - the id that identifies the server connection + * room - the room joined, used in all other chat calls, freed by + * library after call + * topic - the topic of the room, freed by library after call + * members - the initial members of the chatroom (null terminated YList of + * yahoo_chat_member's) Must be freed by the client + */ +void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_cat_xml)(int id, char *xml); + + + + + + + +/* + * Name: ext_yahoo_chat_join + * Called when joining the chatroom. + * Params: + * id - the id that identifies the server connection + * room - the room joined, used in all other chat calls, freed by + * library after call + * topic - the topic of the room, freed by library after call + * members - the initial members of the chatroom (null terminated YList + * of yahoo_chat_member's) Must be freed by the client + * fd - the socket where the connection is coming from (for tracking) + */ +void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_join)(int id, char *room, char *topic, YList *members, int fd); + + + + + + +/* + * Name: ext_yahoo_chat_userjoin + * Called when someone joins the chatroom. + * Params: + * id - the id that identifies the server connection + * room - the room joined + * who - the user who has joined, Must be freed by the client + */ +void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_userjoin)(int id, char *room, struct yahoo_chat_member *who); + + + + +/* + * Name: ext_yahoo_chat_userleave + * Called when someone leaves the chatroom. + * Params: + * id - the id that identifies the server connection + * room - the room left + * who - the user who has left (Just the User ID) + */ +void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_userleave)(int id, char *room, char *who); + + + + +/* + * Name: ext_yahoo_chat_message + * Called when someone messages in the chatroom. + * Params: + * id - the id that identifies the server connection + * room - the room + * who - the user who messaged (Just the user id) + * msg - the message + * msgtype - 1 = Normal message + * 2 = /me type message + * utf8 - whether the message is utf8 encoded or not + */ +void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_message)(int id, char *who, char *room, char *msg, int msgtype, int utf8); + +/* + * + * Name: ext_yahoo_chat_yahoologout + * called when yahoo disconnects your chat session + * Note this is called whenver a disconnect happens, client or server + * requested. Care should be taken to make sure you know the origin + * of the disconnect request before doing anything here (auto-join's etc) + * Params: + * id - the id that identifies this connection + * Returns: + * nothing. + */ +void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_yahoologout)(int id); + +/* + * + * Name: ext_yahoo_chat_yahooerror + * called when yahoo sends back an error to you + * Note this is called whenver chat message is sent into a room + * in error (fd not connected, room doesn't exists etc) + * Care should be taken to make sure you know the origin + * of the error before doing anything about it. + * Params: + * id - the id that identifies this connection + * Returns: + * nothing. + */ + +void YAHOO_CALLBACK_TYPE(ext_yahoo_chat_yahooerror)(int id); + +/* + * Name: ext_yahoo_conf_message + * Called when someone messages in the conference. + * Params: + * id - the id that identifies the server connection + * who - the user who messaged + * room - the room + * msg - the message + * utf8 - whether the message is utf8 encoded or not + */ +void YAHOO_CALLBACK_TYPE(ext_yahoo_conf_message)(int id, char *who, char *room, char *msg, int utf8); + + + + +/* + * Name: ext_yahoo_got_file + * Called when someone sends you a file + * Params: + * id - the id that identifies the server connection + * who - the user who sent the file + * url - the file url + * expires - the expiry date of the file on the server (timestamp) + * msg - the message + * fname- the file name if direct transfer + * fsize- the file size if direct transfer + */ +void YAHOO_CALLBACK_TYPE(ext_yahoo_got_file)(int id, char *who, char *url, long expires, char *msg, char *fname, unsigned long fesize); + + + + +/* + * Name: ext_yahoo_contact_added + * Called when a contact is added to your list + * Params: + * id - the id that identifies the server connection + * myid - the identity he was added to + * who - who was added + * msg - any message sent + */ +void YAHOO_CALLBACK_TYPE(ext_yahoo_contact_added)(int id, char *myid, char *who, char *msg); + + + + +/* + * Name: ext_yahoo_rejected + * Called when a contact rejects your add + * Params: + * id - the id that identifies the server connection + * who - who rejected you + * msg - any message sent + */ +void YAHOO_CALLBACK_TYPE(ext_yahoo_rejected)(int id, char *who, char *msg); + + + + +/* + * Name: ext_yahoo_typing_notify + * Called when remote user starts or stops typing. + * Params: + * id - the id that identifies the server connection + * who - the handle of the remote user + * stat - 1 if typing, 0 if stopped typing + */ +void YAHOO_CALLBACK_TYPE(ext_yahoo_typing_notify)(int id, char *who, int stat); + + + + +/* + * Name: ext_yahoo_game_notify + * Called when remote user starts or stops a game. + * Params: + * id - the id that identifies the server connection + * who - the handle of the remote user + * stat - 1 if game, 0 if stopped gaming + */ +void YAHOO_CALLBACK_TYPE(ext_yahoo_game_notify)(int id, char *who, int stat); + + + + +/* + * Name: ext_yahoo_mail_notify + * Called when you receive mail, or with number of messages + * Params: + * id - the id that identifies the server connection + * from - who the mail is from - NULL if only mail count + * subj - the subject of the mail - NULL if only mail count + * cnt - mail count - 0 if new mail notification + */ +void YAHOO_CALLBACK_TYPE(ext_yahoo_mail_notify)(int id, char *from, char *subj, int cnt); + + + + +/* + * Name: ext_yahoo_system_message + * System message + * Params: + * id - the id that identifies the server connection + * msg - the message + */ +void YAHOO_CALLBACK_TYPE(ext_yahoo_system_message)(int id, char *msg); + + + + + + + + + + + +/* + * Name: ext_yahoo_got_webcam_image + * Called when you get a webcam update + * An update can either be receiving an image, a part of an image or + * just an update with a timestamp + * Params: + * id - the id that identifies the server connection + * who - the user who's webcam we're viewing + * image - image data + * image_size - length of the image in bytes + * real_size - actual length of image data + * timestamp - milliseconds since the webcam started + * + * If the real_size is smaller then the image_size then only part of + * the image has been read. This function will keep being called till + * the total amount of bytes in image_size has been read. The image + * received is in JPEG-2000 Code Stream Syntax (ISO/IEC 15444-1). + * The size of the image will be either 160x120 or 320x240. + * Each webcam image contains a timestamp. This timestamp should be + * used to keep the image in sync since some images can take longer + * to transport then others. When image_size is 0 we can still receive + * a timestamp to stay in sync + */ +void YAHOO_CALLBACK_TYPE(ext_yahoo_got_webcam_image)(int id, const char * who, + const unsigned char *image, unsigned int image_size, unsigned int real_size, + unsigned int timestamp); + + + + +/* + * Name: ext_yahoo_webcam_invite + * Called when you get a webcam invitation + * Params: + * id - the id that identifies the server connection + * from - who the invitation is from + */ +void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_invite)(int id, char *from); + + + + +/* + * Name: ext_yahoo_webcam_invite_reply + * Called when you get a response to a webcam invitation + * Params: + * id - the id that identifies the server connection + * from - who the invitation response is from + * accept - 0 (decline), 1 (accept) + */ +void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_invite_reply)(int id, char *from, int accept); + + + +/* + * Name: ext_yahoo_webcam_closed + * Called when the webcam connection closed + * Params: + * id - the id that identifies the server connection + * who - the user who we where connected to + * reason - reason why the connection closed + * 1 = user stopped broadcasting + * 2 = user cancelled viewing permission + * 3 = user declines permission + * 4 = user does not have webcam online + */ +void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_closed)(int id, char *who, int reason); + + +/* + * Name: ext_yahoo_got_search_result + * Called when the search result received from server + * Params: + * id - the id that identifies the server connection + * found - total number of results returned in the current result set + * start - offset from where the current result set starts + * total - total number of results available (start + found <= total) + * contacts - the list of results as a YList of yahoo_found_contact + * these will be freed after this function returns, so + * if you need to use the information, make a copy + */ +void YAHOO_CALLBACK_TYPE(ext_yahoo_got_search_result)(int id, int found, int start, int total, YList *contacts); + + + +/* + * Name: ext_yahoo_error + * Called on error. + * Params: + * id - the id that identifies the server connection + * err - the error message + * fatal- whether this error is fatal to the connection or not + */ +void YAHOO_CALLBACK_TYPE(ext_yahoo_error)(int id, char *err, int fatal); + + + + +/* + * Name: ext_yahoo_webcam_viewer + * Called when a viewer disconnects/connects/requests to connect + * Params: + * id - the id that identifies the server connection + * who - the viewer + * connect - 0=disconnect 1=connect 2=request + */ +void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_viewer)(int id, char *who, int connect); + + + + +/* + * Name: ext_yahoo_webcam_data_request + * Called when you get a request for webcam images + * Params: + * id - the id that identifies the server connection + * send - whether to send images or not + */ +void YAHOO_CALLBACK_TYPE(ext_yahoo_webcam_data_request)(int id, int send); + + + + +/* + * Name: ext_yahoo_log + * Called to log a message. + * Params: + * fmt - the printf formatted message + * Returns: + * 0 + */ +int YAHOO_CALLBACK_TYPE(ext_yahoo_log)(char *fmt, ...); + + + + + + + + +/* + * Name: ext_yahoo_add_handler + * Add a listener for the fd. Must call yahoo_read_ready + * when a YAHOO_INPUT_READ fd is ready and yahoo_write_ready + * when a YAHOO_INPUT_WRITE fd is ready. + * Params: + * id - the id that identifies the server connection + * fd - the fd on which to listen + * cond - the condition on which to call the callback + * data - callback data to pass to yahoo_*_ready + * + * Returns: a tag to be used when removing the handler + */ +int YAHOO_CALLBACK_TYPE(ext_yahoo_add_handler)(int id, int fd, yahoo_input_condition cond, void *data); + + + + +/* + * Name: ext_yahoo_remove_handler + * Remove the listener for the fd. + * Params: + * id - the id that identifies the connection + * tag - the handler tag to remove + */ +void YAHOO_CALLBACK_TYPE(ext_yahoo_remove_handler)(int id, int tag); + + + + + +/* + * Name: ext_yahoo_connect + * Connect to a host:port + * Params: + * host - the host to connect to + * port - the port to connect on + * Returns: + * a unix file descriptor to the socket + */ +int YAHOO_CALLBACK_TYPE(ext_yahoo_connect)(char *host, int port); + + + + + + + + +/* + * Name: ext_yahoo_connect_async + * Connect to a host:port asynchronously. This function should return + * immediately returing a tag used to identify the connection handler, + * or a pre-connect error (eg: host name lookup failure). + * Once the connect completes (successfully or unsuccessfully), callback + * should be called (see the signature for yahoo_connect_callback). + * The callback may safely be called before this function returns, but + * it should not be called twice. + * Params: + * id - the id that identifies this connection + * host - the host to connect to + * port - the port to connect on + * callback - function to call when connect completes + * callback_data - data to pass to the callback function + * Returns: + * a unix file descriptor to the socket + */ +int YAHOO_CALLBACK_TYPE(ext_yahoo_connect_async)(int id, char *host, int port, + yahoo_connect_callback callback, void *callback_data); + +#ifdef USE_STRUCT_CALLBACKS +}; + +/* + * if using a callback structure, call yahoo_register_callbacks + * before doing anything else + */ +void yahoo_register_callbacks(struct yahoo_callbacks * tyc); + +#undef YAHOO_CALLBACK_TYPE + +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/protocols/yahoo/yahoo2_types.h b/protocols/yahoo/yahoo2_types.h new file mode 100644 index 00000000..1a92b267 --- /dev/null +++ b/protocols/yahoo/yahoo2_types.h @@ -0,0 +1,241 @@ +/* + * libyahoo2: yahoo2_types.h + * + * Copyright (C) 2002-2004, Philip S Tellis <philip.tellis AT gmx.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef YAHOO2_TYPES_H +#define YAHOO2_TYPES_H + +#include "yahoo_list.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum yahoo_status { + YAHOO_STATUS_AVAILABLE = 0, + YAHOO_STATUS_BRB, + YAHOO_STATUS_BUSY, + YAHOO_STATUS_NOTATHOME, + YAHOO_STATUS_NOTATDESK, + YAHOO_STATUS_NOTINOFFICE, + YAHOO_STATUS_ONPHONE, + YAHOO_STATUS_ONVACATION, + YAHOO_STATUS_OUTTOLUNCH, + YAHOO_STATUS_STEPPEDOUT, + YAHOO_STATUS_INVISIBLE = 12, + YAHOO_STATUS_CUSTOM = 99, + YAHOO_STATUS_IDLE = 999, + YAHOO_STATUS_OFFLINE = 0x5a55aa56, /* don't ask */ + YAHOO_STATUS_NOTIFY = 0x16 +}; +#define YAHOO_STATUS_GAME 0x2 /* Games don't fit into the regular status model */ + +enum yahoo_login_status { + YAHOO_LOGIN_OK = 0, + YAHOO_LOGIN_UNAME = 3, + YAHOO_LOGIN_PASSWD = 13, + YAHOO_LOGIN_LOCK = 14, + YAHOO_LOGIN_DUPL = 99, + YAHOO_LOGIN_SOCK = -1 +}; + +enum yahoo_error { + E_CUSTOM = 0, + + /* responses from ignore buddy */ + E_IGNOREDUP = 2, + E_IGNORENONE = 3, + E_IGNORECONF = 12, + + /* conference */ + E_CONFNOTAVAIL = 20 +}; + +enum yahoo_log_level { + YAHOO_LOG_NONE = 0, + YAHOO_LOG_FATAL, + YAHOO_LOG_ERR, + YAHOO_LOG_WARNING, + YAHOO_LOG_NOTICE, + YAHOO_LOG_INFO, + YAHOO_LOG_DEBUG +}; + + +/* Yahoo style/color directives */ +#define YAHOO_COLOR_BLACK "\033[30m" +#define YAHOO_COLOR_BLUE "\033[31m" +#define YAHOO_COLOR_LIGHTBLUE "\033[32m" +#define YAHOO_COLOR_GRAY "\033[33m" +#define YAHOO_COLOR_GREEN "\033[34m" +#define YAHOO_COLOR_PINK "\033[35m" +#define YAHOO_COLOR_PURPLE "\033[36m" +#define YAHOO_COLOR_ORANGE "\033[37m" +#define YAHOO_COLOR_RED "\033[38m" +#define YAHOO_COLOR_OLIVE "\033[39m" +#define YAHOO_COLOR_ANY "\033[#" +#define YAHOO_STYLE_ITALICON "\033[2m" +#define YAHOO_STYLE_ITALICOFF "\033[x2m" +#define YAHOO_STYLE_BOLDON "\033[1m" +#define YAHOO_STYLE_BOLDOFF "\033[x1m" +#define YAHOO_STYLE_UNDERLINEON "\033[4m" +#define YAHOO_STYLE_UNDERLINEOFF "\033[x4m" +#define YAHOO_STYLE_URLON "\033[lm" +#define YAHOO_STYLE_URLOFF "\033[xlm" + +enum yahoo_connection_type { + YAHOO_CONNECTION_PAGER=0, + YAHOO_CONNECTION_FT, + YAHOO_CONNECTION_YAB, + YAHOO_CONNECTION_WEBCAM_MASTER, + YAHOO_CONNECTION_WEBCAM, + YAHOO_CONNECTION_CHATCAT, + YAHOO_CONNECTION_SEARCH +}; + +enum yahoo_webcam_direction_type { + YAHOO_WEBCAM_DOWNLOAD=0, + YAHOO_WEBCAM_UPLOAD +}; + +/* chat member attribs */ +#define YAHOO_CHAT_MALE 0x8000 +#define YAHOO_CHAT_FEMALE 0x10000 +#define YAHOO_CHAT_FEMALE 0x10000 +#define YAHOO_CHAT_DUNNO 0x400 +#define YAHOO_CHAT_WEBCAM 0x10 + +enum yahoo_webcam_conn_type { Y_WCM_DIALUP, Y_WCM_DSL, Y_WCM_T1 }; + +struct yahoo_webcam { + int direction; /* Uploading or downloading */ + int conn_type; /* 0=Dialup, 1=DSL/Cable, 2=T1/Lan */ + + char *user; /* user we are viewing */ + char *server; /* webcam server to connect to */ + int port; /* webcam port to connect on */ + char *key; /* key to connect to the server with */ + char *description; /* webcam description */ + char *my_ip; /* own ip number */ +}; + +struct yahoo_webcam_data { + unsigned int data_size; + unsigned int to_read; + unsigned int timestamp; + unsigned char packet_type; +}; + +struct yahoo_data { + char *user; + char *password; + + char *cookie_y; + char *cookie_t; + char *cookie_c; + char *login_cookie; + + YList *buddies; + YList *ignore; + YList *identities; + char *login_id; + + int current_status; + int initial_status; + int logged_in; + + int session_id; + + int client_id; + + char *rawbuddylist; + char *ignorelist; + + void *server_settings; +}; + +struct yab { + char *id; + char *fname; + char *lname; + char *nname; + char *email; + char *hphone; + char *wphone; + char *mphone; + int dbid; +}; + +struct yahoo_buddy { + char *group; + char *id; + char *real_name; + struct yab *yab_entry; +}; + +enum yahoo_search_type { + YAHOO_SEARCH_KEYWORD = 0, + YAHOO_SEARCH_YID, + YAHOO_SEARCH_NAME +}; + +enum yahoo_search_gender { + YAHOO_GENDER_NONE = 0, + YAHOO_GENDER_MALE, + YAHOO_GENDER_FEMALE +}; + +enum yahoo_search_agerange { + YAHOO_AGERANGE_NONE = 0 +}; + +struct yahoo_found_contact { + char *id; + char *gender; + char *location; + int age; + int online; +}; + +/* + * Function pointer to be passed to http get/post and send file + */ +typedef void (*yahoo_get_fd_callback)(int id, int fd, int error, void *data); + +/* + * Function pointer to be passed to yahoo_get_url_handle + */ +typedef void (*yahoo_get_url_handle_callback)(int id, int fd, int error, + const char *filename, unsigned long size, void *data); + + +struct yahoo_chat_member { + char *id; + int age; + int attribs; + char *alias; + char *location; +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/protocols/yahoo/yahoo_debug.h b/protocols/yahoo/yahoo_debug.h new file mode 100644 index 00000000..59d92901 --- /dev/null +++ b/protocols/yahoo/yahoo_debug.h @@ -0,0 +1,38 @@ +/* + * libyahoo2: yahoo_debug.h + * + * Copyright (C) 2002-2004, Philip S Tellis <philip.tellis AT gmx.net> + * + * This program is free software; you can 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 + * + */ + +extern int yahoo_log_message(char *fmt, ...); + +#define NOTICE(x) if(yahoo_get_log_level() >= YAHOO_LOG_NOTICE) { yahoo_log_message x; yahoo_log_message("\n"); } + +#define LOG(x) if(yahoo_get_log_level() >= YAHOO_LOG_INFO) { yahoo_log_message("%s:%d: ", __FILE__, __LINE__); \ + yahoo_log_message x; \ + yahoo_log_message("\n"); } + +#define WARNING(x) if(yahoo_get_log_level() >= YAHOO_LOG_WARNING) { yahoo_log_message("%s:%d: warning: ", __FILE__, __LINE__); \ + yahoo_log_message x; \ + yahoo_log_message("\n"); } + +#define DEBUG_MSG(x) if(yahoo_get_log_level() >= YAHOO_LOG_DEBUG) { yahoo_log_message("%s:%d: debug: ", __FILE__, __LINE__); \ + yahoo_log_message x; \ + yahoo_log_message("\n"); } + + diff --git a/protocols/yahoo/yahoo_fn.c b/protocols/yahoo/yahoo_fn.c new file mode 100644 index 00000000..6f14c263 --- /dev/null +++ b/protocols/yahoo/yahoo_fn.c @@ -0,0 +1,4622 @@ +/* + * libyahoo2 - originally from gaim patches by Amatus + * + * Copyright (C) 2003-2004 + * + * Some code copyright (C) 1998-1999, Mark Spencer <markster@marko.net> + * libfaim code copyright 1998, 1999 Adam Fritzler <afritz@auk.cx> + * + * This program is free software; you can 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 "yahoo_fn.h" + +unsigned char table_0[256] = { + 0x5A, 0x41, 0x11, 0x77, 0x29, 0x9C, 0x31, 0xAD, + 0x4A, 0x32, 0x1A, 0x6D, 0x56, 0x9F, 0x39, 0xA6, + 0x0C, 0xE8, 0x49, 0x40, 0xA4, 0x21, 0xE9, 0x01, + 0x91, 0x86, 0x2F, 0xB9, 0xED, 0x80, 0x51, 0xAB, + 0x7F, 0x92, 0xF2, 0x73, 0xCD, 0xD9, 0x75, 0x2A, + 0x70, 0x34, 0x35, 0x8D, 0xA8, 0x72, 0x7D, 0x9B, + 0x2E, 0xC5, 0x2D, 0x76, 0x1E, 0xBB, 0xE7, 0x37, + 0xBA, 0xB7, 0xB2, 0x03, 0x20, 0x17, 0x8A, 0x07, + 0xD6, 0x96, 0x13, 0x95, 0xE5, 0xF1, 0x18, 0x3B, + 0xA5, 0x62, 0x33, 0xC1, 0x44, 0x3D, 0x6C, 0xA7, + 0xBF, 0x1C, 0x60, 0xFF, 0x5B, 0xF5, 0x8E, 0xE6, + 0x5C, 0xCC, 0xF7, 0x69, 0x15, 0x0F, 0x0B, 0xBD, + 0x12, 0x9D, 0xB3, 0x65, 0x53, 0xB1, 0x14, 0xF4, + 0x19, 0x3E, 0xB6, 0x45, 0xCB, 0xA2, 0x7A, 0xD3, + 0xF8, 0xD1, 0x61, 0xEE, 0xBC, 0xC6, 0xB0, 0x5D, + 0x4B, 0x09, 0x26, 0xE1, 0x1D, 0x6E, 0xC3, 0xFB, + 0x68, 0x4C, 0x42, 0x52, 0x5F, 0xDE, 0xFD, 0xEF, + 0x81, 0x04, 0x6F, 0xE0, 0xF0, 0x1F, 0x0D, 0x7C, + 0x58, 0x4F, 0x1B, 0x30, 0xCF, 0x9A, 0x2B, 0x05, + 0xF6, 0x3F, 0x78, 0xAC, 0xD8, 0xEC, 0xE2, 0x25, + 0x93, 0xDA, 0x84, 0x8C, 0x4E, 0xD5, 0x38, 0x0A, + 0x06, 0x7E, 0xD4, 0x59, 0x98, 0xE3, 0x36, 0xC2, + 0xD2, 0xA3, 0x10, 0x79, 0xFA, 0xC9, 0x16, 0x27, + 0x66, 0x89, 0xFE, 0x57, 0xF3, 0x83, 0xB8, 0x28, + 0x3C, 0xC7, 0xCE, 0x71, 0xC8, 0xDB, 0x22, 0xE4, + 0xDD, 0xDF, 0x02, 0x8F, 0x5E, 0xEB, 0x48, 0x2C, + 0x08, 0xC4, 0x43, 0xEA, 0x50, 0x55, 0x90, 0x54, + 0x87, 0xCA, 0x00, 0x24, 0x6B, 0x85, 0x97, 0xD7, + 0xDC, 0x6A, 0x67, 0xD0, 0x88, 0xA1, 0x9E, 0xC0, + 0x46, 0xAE, 0x64, 0x74, 0x4D, 0xA0, 0x99, 0xB5, + 0x0E, 0x8B, 0xAA, 0x3A, 0xB4, 0xFC, 0xA9, 0x94, + 0x7B, 0xBE, 0xF9, 0xAF, 0x82, 0x63, 0x47, 0x23 }; + +unsigned char table_1[256] = { + 0x08, 0xCB, 0x54, 0xCF, 0x97, 0x53, 0x59, 0xF1, + 0x66, 0xEC, 0xDB, 0x1B, 0xB1, 0xE2, 0x36, 0xEB, + 0xB3, 0x8F, 0x71, 0xA8, 0x90, 0x7D, 0xDA, 0xDC, + 0x2C, 0x2F, 0xE8, 0x6A, 0x73, 0x37, 0xAE, 0xCC, + 0xA1, 0x16, 0xE6, 0xFC, 0x9C, 0xA9, 0x2A, 0x3F, + 0x58, 0xFD, 0x56, 0x4C, 0xA5, 0xF2, 0x33, 0x99, + 0x1A, 0xB7, 0xFE, 0xA6, 0x1E, 0x32, 0x9E, 0x48, + 0x03, 0x4A, 0x78, 0xEE, 0xCA, 0xC3, 0x88, 0x7A, + 0xAC, 0x23, 0xAA, 0xBD, 0xDE, 0xD3, 0x67, 0x43, + 0xFF, 0x64, 0x8A, 0xF9, 0x04, 0xD0, 0x7B, 0xC2, + 0xBC, 0xF3, 0x89, 0x0E, 0xDD, 0xAB, 0x9D, 0x84, + 0x5A, 0x62, 0x7F, 0x6D, 0x82, 0x68, 0xA3, 0xED, + 0x2E, 0x07, 0x41, 0xEF, 0x2D, 0x70, 0x4F, 0x69, + 0x8E, 0xE7, 0x0F, 0x11, 0x19, 0xAF, 0x31, 0xFB, + 0x8D, 0x4B, 0x5F, 0x96, 0x75, 0x42, 0x6C, 0x46, + 0xE4, 0x55, 0xD6, 0x3B, 0xE1, 0xD1, 0xB0, 0xB5, + 0x45, 0x29, 0xC0, 0x94, 0x9F, 0xD4, 0x15, 0x17, + 0x3C, 0x47, 0xC8, 0xD9, 0xC6, 0x76, 0xB9, 0x02, + 0xE0, 0xC9, 0xB2, 0x01, 0xC1, 0x5D, 0x4E, 0x14, + 0xF4, 0xAD, 0xB6, 0x00, 0x72, 0xF0, 0x49, 0x0D, + 0xD8, 0x5E, 0x6F, 0x2B, 0x8C, 0x51, 0x83, 0xC5, + 0x0A, 0x85, 0xE5, 0x38, 0x7E, 0x26, 0xEA, 0x22, + 0x6B, 0x06, 0xD5, 0x8B, 0xBF, 0xC7, 0x35, 0x1D, + 0xF6, 0x24, 0x28, 0xCE, 0x9B, 0x77, 0x20, 0x60, + 0xF5, 0x87, 0x3D, 0x65, 0x86, 0x0C, 0xDF, 0xBA, + 0x12, 0xA4, 0x3A, 0x34, 0xD7, 0xA0, 0xF8, 0x63, + 0x52, 0x27, 0xB8, 0x18, 0xA7, 0x13, 0x91, 0x09, + 0x93, 0x5C, 0x10, 0x9A, 0xB4, 0xE9, 0x44, 0xC4, + 0x21, 0x57, 0x1C, 0x0B, 0xA2, 0x74, 0x4D, 0xBE, + 0xD2, 0x1F, 0xCD, 0xE3, 0x6E, 0x7C, 0x40, 0x50, + 0x39, 0x80, 0x98, 0xFA, 0x25, 0x92, 0x30, 0x5B, + 0x05, 0x95, 0xBB, 0x79, 0x61, 0x3E, 0x81, 0xF7 }; + +unsigned char table_2[32] = { + 0x19, 0x05, 0x09, 0x1C, 0x0B, 0x1A, 0x12, 0x03, + 0x06, 0x04, 0x0D, 0x1D, 0x15, 0x0E, 0x1B, 0x18, + 0x00, 0x07, 0x08, 0x02, 0x13, 0x1F, 0x0C, 0x1E, + 0x16, 0x0A, 0x10, 0x0F, 0x01, 0x14, 0x11, 0x17 }; + +unsigned char table_3[256] = { + 0xBC, 0x1B, 0xCC, 0x1E, 0x5B, 0x59, 0x4F, 0xA8, + 0x62, 0xC6, 0xC1, 0xBB, 0x83, 0x2D, 0xA3, 0xA6, + 0x5A, 0xDC, 0xE5, 0x93, 0xFB, 0x5C, 0xD6, 0x2A, + 0x97, 0xC7, 0x1C, 0x73, 0x08, 0x45, 0xD2, 0x89, + 0x4A, 0xD4, 0xCF, 0x0C, 0x1D, 0xD8, 0xCD, 0x26, + 0x8F, 0x11, 0x55, 0x8B, 0xD3, 0x53, 0xCE, 0x00, + 0xB5, 0x3B, 0x2E, 0x39, 0x88, 0x7B, 0x85, 0x46, + 0x54, 0xA5, 0x31, 0x40, 0x3E, 0x0A, 0x4C, 0x68, + 0x70, 0x0F, 0xBA, 0x0E, 0x75, 0x8A, 0xEB, 0x44, + 0x60, 0x6C, 0x05, 0xC9, 0xF0, 0xDD, 0x0D, 0x66, + 0xAB, 0xA1, 0xAD, 0xF2, 0x12, 0x6A, 0xE6, 0x27, + 0xF6, 0x9F, 0xDB, 0xB8, 0xF4, 0x56, 0x5E, 0x2C, + 0xDA, 0xFE, 0x34, 0x86, 0xF5, 0xC2, 0xB0, 0xF1, + 0xCB, 0xF3, 0x78, 0x9B, 0x7F, 0xB4, 0xD7, 0x58, + 0x74, 0x07, 0x72, 0x96, 0x02, 0xCA, 0xAC, 0xE8, + 0x5D, 0xA7, 0x32, 0xBD, 0x81, 0x43, 0x18, 0xF8, + 0x15, 0x0B, 0xE9, 0x76, 0x30, 0xBF, 0x3A, 0x22, + 0x9E, 0xD1, 0x79, 0x37, 0xBE, 0x8C, 0x7A, 0x98, + 0x21, 0x95, 0x10, 0x8D, 0xDF, 0xC0, 0x69, 0xC8, + 0x03, 0x6E, 0x4B, 0x36, 0xFC, 0x6F, 0xA9, 0x48, + 0x63, 0xE1, 0xB9, 0x24, 0x87, 0x13, 0xB2, 0xA4, + 0x84, 0x06, 0x14, 0x61, 0x3D, 0x92, 0xB1, 0x41, + 0xE2, 0x71, 0xAF, 0x16, 0xDE, 0x25, 0x82, 0xD9, + 0x2B, 0x33, 0x51, 0xA2, 0x4E, 0x7D, 0x94, 0xFF, + 0xFD, 0x5F, 0x80, 0xED, 0x64, 0xE7, 0x50, 0x6D, + 0xD0, 0x3C, 0x6B, 0x65, 0x77, 0x17, 0x1A, 0xEC, + 0xD5, 0xAA, 0xF9, 0xC4, 0x9C, 0x35, 0xE3, 0x42, + 0xE4, 0x19, 0x52, 0x67, 0xB7, 0x9D, 0x28, 0xC5, + 0x47, 0x38, 0x91, 0x57, 0xAE, 0x3F, 0x29, 0x9A, + 0x2F, 0xF7, 0x90, 0x04, 0xEE, 0xFA, 0x20, 0xB6, + 0xEA, 0x49, 0x23, 0x4D, 0xB3, 0x8E, 0xC3, 0x1F, + 0x7C, 0xEF, 0xE0, 0x99, 0x09, 0xA0, 0x01, 0x7E }; + +unsigned char table_4[32] = { + 0x1F, 0x0B, 0x00, 0x1E, 0x03, 0x0E, 0x15, 0x01, + 0x1A, 0x17, 0x1D, 0x1B, 0x11, 0x0F, 0x0A, 0x12, + 0x13, 0x18, 0x02, 0x04, 0x09, 0x06, 0x0D, 0x07, + 0x08, 0x05, 0x10, 0x19, 0x0C, 0x14, 0x16, 0x1C }; + +unsigned char table_5[256] = { + 0x9A, 0xAB, 0x61, 0x28, 0x0A, 0x23, 0xFC, 0xBA, + 0x90, 0x22, 0xB7, 0x62, 0xD9, 0x09, 0x91, 0xF4, + 0x7B, 0x5D, 0x6B, 0x80, 0xAC, 0x9E, 0x21, 0x72, + 0x64, 0x2D, 0xFF, 0x66, 0xEB, 0x5B, 0x05, 0xC8, + 0x1B, 0xD1, 0x55, 0xF5, 0x97, 0x08, 0xAE, 0xC7, + 0x00, 0xDE, 0xE1, 0x78, 0xD8, 0xB6, 0xF0, 0x17, + 0xE4, 0x32, 0xCD, 0x76, 0x07, 0x14, 0x7F, 0x7A, + 0xBF, 0xB4, 0x1D, 0x94, 0x48, 0x75, 0xFA, 0xA7, + 0x99, 0x7E, 0x65, 0x38, 0x29, 0x51, 0xC3, 0x83, + 0x7C, 0x0D, 0xA0, 0xCC, 0xF1, 0xDD, 0xE2, 0x49, + 0xF8, 0xD2, 0x25, 0x54, 0x9B, 0x0E, 0xB9, 0xFE, + 0x67, 0xC4, 0xCE, 0x13, 0xD4, 0xE7, 0xB8, 0x41, + 0x77, 0xDB, 0xA6, 0xB0, 0x11, 0x6A, 0x5E, 0x68, + 0x8D, 0xF9, 0x36, 0xD3, 0xC2, 0x3A, 0xAA, 0x59, + 0x03, 0xE0, 0xE3, 0xF3, 0x42, 0x2C, 0x04, 0x47, + 0xE6, 0x93, 0xCB, 0x6E, 0x20, 0xCA, 0x01, 0xA1, + 0x40, 0x2B, 0x2F, 0x5F, 0x87, 0xD0, 0xEC, 0x88, + 0x27, 0x58, 0xC6, 0x3E, 0xDF, 0x26, 0x5C, 0xE9, + 0x1F, 0x0F, 0x95, 0x1C, 0xFB, 0xA5, 0x12, 0x39, + 0x1E, 0x3C, 0x33, 0x43, 0x56, 0xE8, 0x82, 0xF7, + 0x7D, 0x89, 0xF2, 0xD7, 0x50, 0x92, 0x60, 0x4C, + 0x2A, 0x86, 0x16, 0x6C, 0x37, 0xC0, 0xAD, 0xB3, + 0x24, 0x45, 0xB1, 0xA2, 0x71, 0xA4, 0xA3, 0xED, + 0xC9, 0x5A, 0x4D, 0x84, 0x0C, 0x3F, 0xC5, 0x9D, + 0x63, 0x19, 0x79, 0x57, 0x96, 0x30, 0x74, 0xBB, + 0xDA, 0x1A, 0x9F, 0x44, 0xC1, 0x98, 0xE5, 0x81, + 0xD6, 0x18, 0x8F, 0xFD, 0x8E, 0x06, 0x6F, 0xF6, + 0x2E, 0x3B, 0xB5, 0x85, 0x8A, 0x9C, 0x53, 0x4A, + 0xA9, 0x52, 0x3D, 0x4E, 0xBE, 0xAF, 0xBC, 0xA8, + 0x4F, 0x6D, 0x15, 0x35, 0x8C, 0xBD, 0x34, 0x8B, + 0xDC, 0x0B, 0xCF, 0x31, 0xEA, 0xB2, 0x70, 0x4B, + 0x46, 0x73, 0x69, 0xD5, 0x10, 0xEE, 0x02, 0xEF }; + +unsigned char table_6[32] = { + 0x1A, 0x1C, 0x0F, 0x0C, 0x00, 0x02, 0x13, 0x09, + 0x11, 0x05, 0x0D, 0x12, 0x18, 0x0B, 0x04, 0x10, + 0x14, 0x1B, 0x1E, 0x16, 0x07, 0x08, 0x03, 0x17, + 0x19, 0x1F, 0x01, 0x0E, 0x15, 0x06, 0x0A, 0x1D }; + +unsigned char table_7[256] = { + 0x52, 0x11, 0x72, 0xD0, 0x76, 0xD7, 0xAE, 0x03, + 0x7F, 0x19, 0xF4, 0xB8, 0xB3, 0x5D, 0xCA, 0x2D, + 0x5C, 0x30, 0x53, 0x1A, 0x57, 0xF6, 0xAD, 0x83, + 0x29, 0x79, 0xD5, 0xF0, 0x0F, 0xC3, 0x8B, 0xD3, + 0x8E, 0x37, 0x01, 0xA6, 0xF1, 0x10, 0x04, 0x71, + 0xCC, 0xC6, 0xE7, 0xC2, 0x85, 0x94, 0xBD, 0x6F, + 0xCB, 0xEA, 0xFC, 0xA1, 0x38, 0x5E, 0x08, 0x2E, + 0x35, 0x42, 0x67, 0xD4, 0x56, 0x6D, 0x7C, 0xE5, + 0x0E, 0x7D, 0x12, 0x65, 0xF5, 0x33, 0x82, 0xC4, + 0x1D, 0xD2, 0x16, 0x58, 0xEC, 0xCD, 0xA8, 0xBF, + 0xAB, 0x07, 0x45, 0x55, 0xB7, 0x6A, 0x70, 0xF2, + 0xBE, 0x05, 0x6B, 0x9D, 0xEB, 0x13, 0x0D, 0x9F, + 0xE8, 0xA7, 0xC8, 0x31, 0x3C, 0xB6, 0x21, 0xC0, + 0x20, 0x60, 0x6C, 0xE2, 0xCE, 0x8C, 0xFD, 0x95, + 0xE3, 0x4A, 0xB5, 0xB2, 0x40, 0xB1, 0xF3, 0x17, + 0xF9, 0x24, 0x06, 0x22, 0x2F, 0x25, 0x93, 0x8A, + 0x2A, 0x7E, 0x28, 0x3D, 0x47, 0xF8, 0x89, 0xA5, + 0x7B, 0x9B, 0xC5, 0x84, 0x59, 0x46, 0x90, 0x74, + 0x69, 0xC7, 0xAA, 0xEE, 0x6E, 0xD6, 0xB0, 0x18, + 0x66, 0xA0, 0x7A, 0x1E, 0xFB, 0xDB, 0x4E, 0x51, + 0x92, 0xE4, 0xE0, 0x3E, 0xB4, 0xD8, 0x23, 0x3B, + 0xC1, 0x5F, 0xFE, 0x98, 0x99, 0x73, 0x09, 0xA9, + 0xA3, 0xDF, 0x14, 0x5A, 0x26, 0x8F, 0x0B, 0xAF, + 0x4C, 0x97, 0x54, 0xE1, 0x63, 0x48, 0xED, 0xBA, + 0xCF, 0xBB, 0x1F, 0xDC, 0xA4, 0xFA, 0x64, 0x75, + 0xDE, 0x81, 0x9A, 0xFF, 0x49, 0x41, 0x27, 0x62, + 0x02, 0x15, 0xD9, 0x86, 0xAC, 0x3F, 0x0C, 0x61, + 0xD1, 0x77, 0x2B, 0x1B, 0x96, 0xDA, 0x68, 0x1C, + 0x44, 0x32, 0xBC, 0xA2, 0x87, 0xF7, 0x91, 0x8D, + 0x80, 0xDD, 0x0A, 0x50, 0x34, 0x4B, 0x00, 0xB9, + 0x36, 0xE6, 0x78, 0x4F, 0xC9, 0xE9, 0x2C, 0x43, + 0x88, 0x9E, 0x9C, 0x5B, 0x4D, 0x3A, 0x39, 0xEF }; + +unsigned char table_8[32] = { + 0x13, 0x08, 0x1E, 0x1D, 0x17, 0x16, 0x07, 0x1F, + 0x0E, 0x03, 0x1A, 0x19, 0x01, 0x12, 0x11, 0x10, + 0x09, 0x0C, 0x0F, 0x14, 0x0B, 0x05, 0x00, 0x04, + 0x1C, 0x18, 0x0A, 0x15, 0x02, 0x1B, 0x06, 0x0D }; + +unsigned char table_9[256] = { + 0x20, 0x2A, 0xDA, 0xFE, 0x76, 0x0D, 0xED, 0x39, + 0x51, 0x4C, 0x46, 0x9A, 0xF1, 0xB0, 0x10, 0xC7, + 0xD1, 0x6F, 0x18, 0x24, 0xB9, 0x7A, 0x4F, 0x47, + 0xE0, 0x4E, 0x88, 0x09, 0x8A, 0xBA, 0x60, 0xBD, + 0xC2, 0x27, 0x93, 0x7D, 0x94, 0x40, 0xCB, 0x80, + 0xB8, 0x41, 0x84, 0x5D, 0xC1, 0x0F, 0x5E, 0x78, + 0x2B, 0x48, 0x28, 0x29, 0xEE, 0x81, 0x90, 0x86, + 0x50, 0x9C, 0xF3, 0xB2, 0x35, 0x52, 0x0C, 0x9D, + 0xFC, 0x69, 0xD6, 0xA6, 0x06, 0xD7, 0xC6, 0xFF, + 0x1C, 0x14, 0x57, 0x33, 0xE2, 0x1F, 0x83, 0xA8, + 0xF7, 0x99, 0xC5, 0xDC, 0x70, 0x9E, 0xF4, 0x6B, + 0x0A, 0x77, 0x95, 0x4A, 0x2E, 0x53, 0xF2, 0x62, + 0x98, 0xF8, 0x96, 0xDB, 0xE6, 0x32, 0x3C, 0x58, + 0xD5, 0x6D, 0xE7, 0x4B, 0xCE, 0x91, 0x43, 0xD8, + 0xFA, 0xE3, 0x4D, 0xD9, 0x68, 0xDE, 0xEC, 0x01, + 0x08, 0xD3, 0x8F, 0x19, 0xC4, 0xA7, 0x6E, 0x3E, + 0x63, 0x12, 0x72, 0x42, 0x9F, 0xB4, 0x04, 0x1B, + 0x7E, 0x11, 0x17, 0x73, 0xB5, 0x22, 0x56, 0xA1, + 0x89, 0xDD, 0xF5, 0x3F, 0x49, 0x26, 0x8D, 0x15, + 0x85, 0x75, 0x5F, 0x65, 0x82, 0xB6, 0xF6, 0xD2, + 0xA4, 0x55, 0x37, 0xC8, 0xA0, 0xCC, 0x66, 0x5C, + 0xC9, 0x25, 0x36, 0x67, 0x7C, 0xE1, 0xA3, 0xCF, + 0xA9, 0x59, 0x2F, 0xFB, 0xBB, 0x07, 0x87, 0xA2, + 0x44, 0x92, 0x13, 0x00, 0x16, 0x61, 0x38, 0xEB, + 0xAE, 0xD4, 0x1E, 0x64, 0x6A, 0xE4, 0xCA, 0x1D, + 0x6C, 0xDF, 0xAB, 0x5B, 0x03, 0x7B, 0x9B, 0x8C, + 0x5A, 0xFD, 0xC3, 0xB3, 0x0B, 0xAA, 0xAC, 0x8B, + 0xBE, 0xBC, 0x3D, 0x97, 0xCD, 0x05, 0x21, 0x8E, + 0xAD, 0xEA, 0x54, 0x30, 0xAF, 0x02, 0xB1, 0x34, + 0x0E, 0xA5, 0x3B, 0x45, 0x1A, 0x23, 0xE8, 0x7F, + 0xEF, 0xB7, 0x31, 0xD0, 0xBF, 0x3A, 0x79, 0xE5, + 0xF9, 0xF0, 0x2C, 0x74, 0xE9, 0x71, 0xC0, 0x2D }; + +unsigned char table_10[32] = { + 0x1D, 0x12, 0x11, 0x0D, 0x1E, 0x19, 0x16, 0x1B, + 0x18, 0x13, 0x07, 0x17, 0x0C, 0x02, 0x00, 0x15, + 0x0E, 0x08, 0x05, 0x01, 0x10, 0x06, 0x04, 0x0F, + 0x1F, 0x1A, 0x0B, 0x09, 0x0A, 0x14, 0x1C, 0x03 }; + +unsigned char table_11[256] = { + 0x6B, 0x1D, 0xC6, 0x0A, 0xB7, 0xAC, 0xB2, 0x11, + 0x29, 0xD3, 0xA2, 0x4D, 0xCB, 0x03, 0xEF, 0xA6, + 0xC1, 0x5D, 0x75, 0x48, 0x35, 0x6C, 0xE2, 0x84, + 0xAB, 0xAA, 0xD8, 0x2C, 0x0E, 0x95, 0x25, 0x27, + 0x7D, 0x0B, 0xD0, 0xFB, 0x14, 0xE5, 0xF2, 0x4E, + 0x7F, 0x2A, 0x63, 0x3C, 0xC9, 0xF6, 0xDC, 0x07, + 0x26, 0x55, 0xCF, 0x2B, 0xCD, 0xA7, 0x17, 0xD2, + 0x9A, 0x7B, 0x93, 0x78, 0x9E, 0xE6, 0x2F, 0x49, + 0x1E, 0xFD, 0xF0, 0xFE, 0x7C, 0x33, 0x92, 0xA3, + 0xC8, 0xA0, 0xA9, 0xC4, 0xA1, 0x94, 0x6D, 0x44, + 0x0C, 0x90, 0x3A, 0x8C, 0x8E, 0x85, 0xAF, 0x40, + 0x36, 0xA4, 0xD1, 0xB9, 0x19, 0x6F, 0xF4, 0xBA, + 0x1A, 0x73, 0xD9, 0xB5, 0xB4, 0x7A, 0xF9, 0x83, + 0x58, 0xAD, 0xCE, 0x60, 0x98, 0xDB, 0x1C, 0x1B, + 0x52, 0xB8, 0xF3, 0x96, 0xED, 0xDE, 0xB3, 0xEE, + 0x4F, 0xBD, 0x10, 0xD4, 0x43, 0xEA, 0xE7, 0x37, + 0x12, 0x3D, 0xA8, 0x22, 0x65, 0xEC, 0x5B, 0x08, + 0x9D, 0x0D, 0x5C, 0xB6, 0x8A, 0x79, 0x3F, 0x04, + 0xD6, 0x01, 0xE1, 0xBE, 0xDD, 0x50, 0xFA, 0x41, + 0x13, 0x91, 0xF7, 0xDA, 0x18, 0xB0, 0x45, 0x81, + 0x4C, 0xF5, 0x32, 0x23, 0x56, 0x5A, 0xEB, 0x97, + 0x34, 0x00, 0x77, 0x71, 0x4B, 0x70, 0xD5, 0x31, + 0x72, 0x05, 0xDF, 0xE8, 0x15, 0x3B, 0x54, 0x16, + 0x89, 0xE4, 0xF1, 0xD7, 0x80, 0x82, 0x4A, 0xE3, + 0x39, 0x06, 0x47, 0x28, 0xC2, 0x86, 0x87, 0xB1, + 0x62, 0x74, 0x53, 0x21, 0x67, 0x38, 0x42, 0xCA, + 0x9B, 0xC3, 0x51, 0x99, 0x8B, 0x1F, 0x24, 0x8D, + 0xF8, 0x68, 0x3E, 0x59, 0xBB, 0x61, 0x5F, 0xBC, + 0x09, 0x6E, 0x8F, 0x0F, 0x2D, 0xC0, 0xE0, 0x46, + 0x66, 0x69, 0xA5, 0xE9, 0x30, 0x9C, 0x5E, 0xAE, + 0xBF, 0xC7, 0x20, 0x7E, 0x6A, 0xC5, 0x88, 0xFC, + 0x64, 0x76, 0xFF, 0x9F, 0x2E, 0x02, 0xCC, 0x57 }; + +unsigned char table_12[32] = { + 0x14, 0x1B, 0x18, 0x00, 0x1F, 0x15, 0x17, 0x07, + 0x11, 0x1A, 0x0E, 0x13, 0x12, 0x06, 0x01, 0x03, + 0x1C, 0x0C, 0x0B, 0x1D, 0x10, 0x0F, 0x09, 0x19, + 0x0D, 0x1E, 0x04, 0x05, 0x08, 0x16, 0x0A, 0x02 }; + +unsigned char table_13[256] = { + 0x37, 0x8A, 0x1B, 0x91, 0xA5, 0x2B, 0x2D, 0x88, + 0x8E, 0xFE, 0x0E, 0xD3, 0xF3, 0xE9, 0x7D, 0xD1, + 0x24, 0xEA, 0xB1, 0x8B, 0x5C, 0xA4, 0x44, 0x7E, + 0x8C, 0x2C, 0x73, 0xD5, 0x50, 0x3E, 0xD7, 0x18, + 0xB9, 0xD6, 0xBA, 0x94, 0x0C, 0xFC, 0xCB, 0xB4, + 0x0D, 0x63, 0x4C, 0xDE, 0x77, 0x16, 0xFD, 0x81, + 0x3C, 0x11, 0x45, 0x36, 0xF6, 0x67, 0x95, 0x6D, + 0x6A, 0x1A, 0xA3, 0xC5, 0x92, 0x10, 0x28, 0x84, + 0x48, 0xA6, 0x23, 0xE3, 0x4B, 0xE1, 0xF5, 0x19, + 0xE0, 0x2E, 0x00, 0x61, 0x74, 0xCC, 0xF7, 0xB0, + 0x68, 0xC8, 0x40, 0x6F, 0x59, 0x52, 0x26, 0x99, + 0xC9, 0xF9, 0xC4, 0x53, 0x9B, 0xEC, 0x03, 0x17, + 0xE2, 0x06, 0x30, 0x7B, 0xBE, 0xCD, 0x1D, 0x3B, + 0xD2, 0x5B, 0x65, 0x21, 0x49, 0xB7, 0x79, 0xCF, + 0x82, 0x86, 0xC7, 0x62, 0xEE, 0x8D, 0xFF, 0xD4, + 0xC3, 0x85, 0xA7, 0xFA, 0xA9, 0x6B, 0xF2, 0x69, + 0x9C, 0x38, 0x78, 0xBD, 0x7F, 0xDD, 0xCE, 0xA1, + 0x33, 0xC2, 0x43, 0xEB, 0xD8, 0xE6, 0x2A, 0xE4, + 0x76, 0x6C, 0xAA, 0x46, 0x05, 0xE7, 0xA0, 0x0A, + 0x71, 0x98, 0x41, 0x5F, 0x0F, 0xEF, 0x51, 0xAD, + 0xF0, 0xED, 0x96, 0x5A, 0x42, 0x3F, 0xBF, 0x6E, + 0xBC, 0x5D, 0xC1, 0x15, 0x70, 0x54, 0x4D, 0x14, + 0xB5, 0xCA, 0x27, 0x80, 0x87, 0x39, 0x60, 0x47, + 0x9D, 0x2F, 0x56, 0x1F, 0xBB, 0x31, 0xF1, 0xE8, + 0xB3, 0x9E, 0x5E, 0x7C, 0xD0, 0xC6, 0xB2, 0x57, + 0x83, 0xAC, 0x09, 0x8F, 0xA2, 0x90, 0x13, 0x25, + 0x01, 0x08, 0x64, 0xB6, 0x02, 0xDB, 0x55, 0x32, + 0xAF, 0x9A, 0xC0, 0x1C, 0x12, 0x29, 0x0B, 0x72, + 0x4F, 0xDA, 0xAB, 0x35, 0xF8, 0x22, 0xD9, 0x4E, + 0x3D, 0x1E, 0xDC, 0x58, 0x20, 0x34, 0xAE, 0x66, + 0x75, 0x93, 0x9F, 0x3A, 0x07, 0xE5, 0x89, 0xDF, + 0x97, 0x4A, 0xB8, 0x7A, 0xF4, 0xFB, 0x04, 0xA8 }; + +unsigned char table_14[32] = { + 0x04, 0x14, 0x13, 0x15, 0x1A, 0x1B, 0x0F, 0x16, + 0x02, 0x0D, 0x0C, 0x06, 0x10, 0x17, 0x01, 0x0B, + 0x1E, 0x08, 0x1C, 0x18, 0x19, 0x0A, 0x1F, 0x05, + 0x11, 0x09, 0x1D, 0x07, 0x0E, 0x12, 0x03, 0x00 }; + +unsigned char table_15[256] = { + 0x61, 0x48, 0x58, 0x41, 0x7F, 0x88, 0x43, 0x42, + 0xD9, 0x80, 0x81, 0xFE, 0xC6, 0x49, 0xD7, 0x2C, + 0xE6, 0x5B, 0xEE, 0xFF, 0x2A, 0x6F, 0xBF, 0x98, + 0xD6, 0x20, 0xB9, 0xB1, 0x5D, 0x95, 0x72, 0x1E, + 0x82, 0x96, 0xDE, 0xC1, 0x40, 0xD8, 0x70, 0xA3, + 0xD1, 0x1F, 0xF0, 0x9F, 0x2D, 0xDC, 0x3F, 0xF9, + 0x5E, 0x0D, 0x15, 0x2F, 0x67, 0x31, 0x9D, 0x84, + 0x97, 0x0C, 0xF6, 0x79, 0xC2, 0xA7, 0xC0, 0x32, + 0xB3, 0xEB, 0xED, 0x71, 0x30, 0xCC, 0x4B, 0xA0, + 0xF5, 0xC4, 0xCD, 0x27, 0xFA, 0x11, 0x25, 0xDB, + 0x4F, 0xE2, 0x7E, 0xA6, 0xAF, 0x34, 0x69, 0x63, + 0x8F, 0x08, 0x1C, 0x85, 0xF1, 0x57, 0x78, 0xC8, + 0xA2, 0x83, 0xB5, 0x68, 0xF7, 0x64, 0x45, 0x26, + 0x3B, 0x03, 0xAD, 0x3C, 0x50, 0xD5, 0x77, 0xFC, + 0xFB, 0x18, 0xC9, 0xD2, 0x9C, 0xBB, 0xBA, 0x76, + 0x23, 0x55, 0xD3, 0x5A, 0x01, 0xE9, 0x87, 0x07, + 0x19, 0x09, 0x39, 0x8A, 0x91, 0x93, 0x12, 0xDF, + 0x22, 0xA8, 0xCF, 0x4E, 0x4D, 0x65, 0xB0, 0x0F, + 0x13, 0x53, 0x21, 0x8C, 0xE5, 0xB7, 0x0B, 0x0E, + 0x6C, 0x44, 0xCA, 0x7B, 0xC5, 0x6E, 0xCE, 0xE3, + 0x14, 0x29, 0xAC, 0x2E, 0xE7, 0x59, 0xE8, 0x0A, + 0xEA, 0x66, 0x7C, 0x94, 0x6D, 0x05, 0x9E, 0x9A, + 0x2B, 0x38, 0x6A, 0xCB, 0x51, 0xEF, 0x06, 0xDA, + 0xFD, 0x47, 0x92, 0x1D, 0xA5, 0x37, 0x33, 0xEC, + 0xB4, 0x52, 0x56, 0xC3, 0xF4, 0xF8, 0x8B, 0xD0, + 0xA4, 0x5F, 0x28, 0x89, 0x75, 0xC7, 0x04, 0x00, + 0xE4, 0x86, 0x36, 0x3A, 0x99, 0x16, 0x7D, 0xE0, + 0x7A, 0x4C, 0x54, 0x46, 0x73, 0xB2, 0xF3, 0xE1, + 0x62, 0xBE, 0x90, 0x4A, 0x24, 0x6B, 0x3E, 0xAA, + 0x1B, 0xF2, 0x60, 0xD4, 0xA9, 0x9B, 0x1A, 0xB8, + 0xA1, 0x35, 0xAE, 0xB6, 0x10, 0x5C, 0x17, 0xBC, + 0xAB, 0x8D, 0x02, 0x74, 0xBD, 0x3D, 0x8E, 0xDD }; + +unsigned char table_16[256] = { + 0x3F, 0x9C, 0x17, 0xC1, 0x59, 0xC6, 0x23, 0x93, + 0x4B, 0xDF, 0xCB, 0x55, 0x2B, 0xDE, 0xCD, 0xAD, + 0xB3, 0xE7, 0x42, 0x2F, 0x02, 0x5A, 0x7B, 0x5C, + 0x8F, 0xD1, 0x11, 0xCE, 0xEC, 0xF6, 0xA4, 0xE6, + 0x58, 0x98, 0x6A, 0x99, 0xFB, 0x9B, 0x53, 0x21, + 0x8A, 0x09, 0x2E, 0x3C, 0x22, 0x38, 0xAC, 0x07, + 0x91, 0x46, 0xA9, 0x95, 0xC3, 0x14, 0x84, 0xDB, + 0x36, 0x68, 0x1D, 0xDD, 0xF9, 0x12, 0xE0, 0x3D, + 0x8D, 0x4D, 0x05, 0x86, 0x69, 0xC0, 0xD3, 0xD5, + 0xA5, 0xC9, 0xE5, 0x67, 0x6D, 0xE2, 0x7F, 0xFE, + 0xB2, 0x0F, 0x62, 0xCF, 0x37, 0x35, 0xF3, 0x28, + 0x16, 0xA6, 0x50, 0x76, 0x80, 0x00, 0x31, 0x97, + 0x39, 0x7C, 0x25, 0x0C, 0x64, 0xF2, 0x52, 0x1A, + 0x92, 0x4F, 0x2A, 0x56, 0x03, 0x4C, 0xBD, 0x10, + 0xB7, 0x2C, 0x8C, 0xAE, 0x73, 0xB9, 0xE9, 0xF7, + 0xA7, 0xE1, 0x75, 0xBC, 0xC5, 0x1C, 0x3A, 0x63, + 0x7A, 0x4A, 0x29, 0xD2, 0x71, 0xE8, 0x08, 0xA1, + 0xD4, 0xFD, 0x13, 0xFA, 0xA0, 0x27, 0x41, 0x72, + 0x82, 0x18, 0x51, 0x60, 0x5E, 0x66, 0x0D, 0xAA, + 0xD8, 0x1F, 0xAF, 0x45, 0xD0, 0xF1, 0x9F, 0x6B, + 0xE4, 0x44, 0x89, 0xEE, 0xC4, 0x0B, 0x6C, 0xCC, + 0x83, 0x77, 0xA2, 0x87, 0x0A, 0xA8, 0xED, 0x90, + 0x74, 0x6E, 0xF5, 0xAB, 0xA3, 0xB6, 0x5F, 0x0E, + 0x04, 0x9A, 0xB4, 0x8E, 0xF0, 0xFF, 0x88, 0xB5, + 0xF8, 0xBF, 0x8B, 0x6F, 0x4E, 0x79, 0x40, 0xCA, + 0x24, 0x26, 0xDC, 0x33, 0xEB, 0x2D, 0x5B, 0x1B, + 0x9D, 0xC7, 0x49, 0x48, 0x54, 0x85, 0xEF, 0xD7, + 0xC2, 0xB8, 0xC8, 0x5D, 0xD9, 0x3B, 0x15, 0xBB, + 0x65, 0xE3, 0xD6, 0x30, 0x3E, 0x1E, 0x32, 0x9E, + 0x57, 0x81, 0x34, 0x06, 0xFC, 0xBA, 0x7D, 0x20, + 0x70, 0xDA, 0x7E, 0x47, 0x94, 0x61, 0xB0, 0x78, + 0xF4, 0xBE, 0xEA, 0x19, 0x43, 0x01, 0xB1, 0x96 }; + +unsigned char table_17[256] = { + 0x7E, 0xF1, 0xD3, 0x75, 0x87, 0xA6, 0xED, 0x9E, + 0xA9, 0xD5, 0xC6, 0xBF, 0xE6, 0x6A, 0xEE, 0x4B, + 0x34, 0xDF, 0x4C, 0x7D, 0xDD, 0xFE, 0x3F, 0xAF, + 0x66, 0x2D, 0x74, 0x6F, 0xFC, 0x4F, 0x5F, 0x88, + 0x29, 0x7B, 0xC7, 0x2A, 0x70, 0xE8, 0x1D, 0xDE, + 0xD0, 0x55, 0x71, 0x81, 0xC4, 0x0D, 0x50, 0x4E, + 0x58, 0x00, 0x96, 0x97, 0xBB, 0xD7, 0x53, 0x15, + 0x6C, 0x40, 0x17, 0xC9, 0xFF, 0x8F, 0x94, 0xFB, + 0x19, 0x9A, 0x3E, 0xB5, 0x5A, 0x5E, 0x86, 0x24, + 0xB8, 0x77, 0xBA, 0x85, 0x51, 0x18, 0xBE, 0x59, + 0x79, 0xF3, 0xD4, 0xC3, 0xAB, 0x28, 0xFD, 0x25, + 0x41, 0x91, 0x07, 0x8D, 0xAE, 0x49, 0xF5, 0x80, + 0x35, 0xA1, 0x9C, 0x3C, 0xE2, 0x65, 0xB3, 0xE0, + 0x16, 0xCB, 0x12, 0x6B, 0xF7, 0xB1, 0x93, 0x8A, + 0xCE, 0x54, 0x4D, 0xF8, 0x13, 0xA2, 0x95, 0x46, + 0xEA, 0x61, 0x57, 0x9D, 0x27, 0x8B, 0x3D, 0x60, + 0x36, 0x68, 0x06, 0x56, 0xB6, 0x1B, 0xD2, 0x89, + 0x10, 0xA7, 0xC5, 0x1A, 0x0B, 0x2C, 0xBD, 0x14, + 0x0A, 0xDC, 0x23, 0xA8, 0xE1, 0x04, 0x02, 0xC0, + 0xB2, 0x9B, 0xE3, 0x2E, 0x33, 0x7C, 0x32, 0xAC, + 0x7A, 0x39, 0xB0, 0xF9, 0x98, 0x5B, 0x3A, 0x48, + 0x21, 0x90, 0xB9, 0x20, 0xF0, 0xA0, 0x09, 0x1F, + 0x2F, 0xEF, 0xEB, 0x22, 0x78, 0x82, 0x37, 0xD6, + 0xD1, 0x84, 0x76, 0x01, 0xDB, 0x43, 0xC2, 0xB7, + 0x7F, 0xA4, 0xE5, 0xC1, 0x1C, 0x69, 0x05, 0xEC, + 0xD8, 0x38, 0x67, 0x42, 0x72, 0xBC, 0x73, 0xAD, + 0xA3, 0xE9, 0x4A, 0x8E, 0x47, 0x1E, 0xC8, 0x6E, + 0xDA, 0x5D, 0x2B, 0xF6, 0x30, 0x63, 0xCC, 0xF4, + 0xCD, 0x8C, 0x0F, 0x3B, 0xE7, 0xD9, 0xCF, 0xB4, + 0x03, 0x92, 0x0E, 0x31, 0xE4, 0x08, 0xF2, 0x45, + 0xCA, 0x83, 0x26, 0x5C, 0xA5, 0x44, 0x64, 0x6D, + 0x9F, 0x99, 0x62, 0xAA, 0xFA, 0x11, 0x0C, 0x52 }; + +unsigned char table_18[256] = { + 0x0F, 0x42, 0x3D, 0x86, 0x3E, 0x66, 0xFE, 0x5C, + 0x52, 0xE2, 0xA3, 0xB3, 0xCE, 0x16, 0xCC, 0x95, + 0xB0, 0x8B, 0x82, 0x3B, 0x93, 0x7D, 0x62, 0x08, + 0x1C, 0x6E, 0xBB, 0xCB, 0x1D, 0x88, 0x69, 0xD4, + 0xC9, 0x40, 0x1F, 0xBE, 0x27, 0xBC, 0xDB, 0x38, + 0xE5, 0xA1, 0x71, 0xBA, 0x8A, 0x5E, 0xFD, 0x36, + 0x8F, 0x26, 0x6B, 0xE4, 0x20, 0x6D, 0xC5, 0xDE, + 0xE0, 0x83, 0x7C, 0xD5, 0xD9, 0x4D, 0xDC, 0xE3, + 0x0D, 0x32, 0xED, 0x0E, 0x2F, 0x21, 0xA7, 0x79, + 0xA0, 0xD3, 0x8C, 0x14, 0x6F, 0xB7, 0xF8, 0x85, + 0x5D, 0x37, 0x24, 0xD6, 0x25, 0xD2, 0x8E, 0xA5, + 0xB8, 0xCD, 0x5A, 0x9F, 0x05, 0xAD, 0x65, 0x9E, + 0x4F, 0x5B, 0x56, 0xF0, 0xAA, 0xC2, 0x28, 0xA8, + 0x6A, 0x01, 0x99, 0x2E, 0xA6, 0x77, 0x74, 0x64, + 0x76, 0x15, 0x90, 0x75, 0xAF, 0xE8, 0x39, 0x48, + 0x09, 0x11, 0xE1, 0x2D, 0xEC, 0xB5, 0x7A, 0xB1, + 0x94, 0x13, 0x41, 0x4C, 0x02, 0xA9, 0x97, 0xDF, + 0xC3, 0x8D, 0xEA, 0x3A, 0x9C, 0xD1, 0xA2, 0x9A, + 0xD7, 0x59, 0xD8, 0x18, 0xDA, 0x47, 0x89, 0x81, + 0xC7, 0xF5, 0xFC, 0x98, 0xCA, 0x91, 0x06, 0x68, + 0xC8, 0x07, 0x4A, 0x84, 0x0A, 0xE7, 0x33, 0x2C, + 0xEB, 0xDD, 0x5F, 0xAC, 0x23, 0x1A, 0x35, 0x70, + 0x43, 0x80, 0x61, 0xAE, 0xC1, 0xD0, 0x7B, 0x92, + 0x49, 0x51, 0x53, 0xC4, 0x34, 0x30, 0x0C, 0x4B, + 0x00, 0x04, 0x10, 0xFF, 0x63, 0x44, 0xB4, 0x0B, + 0x57, 0x72, 0xF1, 0x9D, 0x19, 0xF6, 0xB2, 0x87, + 0x1B, 0xEE, 0x46, 0x2A, 0xF3, 0xBF, 0x12, 0x96, + 0x58, 0x2B, 0xF9, 0xB6, 0xCF, 0x22, 0x3C, 0xAB, + 0x1E, 0x6C, 0x31, 0xC6, 0xF7, 0x78, 0x45, 0x17, + 0xE9, 0x7E, 0x73, 0xF2, 0x55, 0xFB, 0x3F, 0x9B, + 0xF4, 0xBD, 0xA4, 0x29, 0x60, 0x03, 0xB9, 0x50, + 0xFA, 0x4E, 0xEF, 0x54, 0xE6, 0x7F, 0xC0, 0x67 }; + +unsigned char table_19[256] = { + 0xEA, 0xE7, 0x13, 0x14, 0xB9, 0xC0, 0xC4, 0x42, + 0x49, 0x6E, 0x2A, 0xA6, 0x65, 0x3C, 0x6A, 0x40, + 0x07, 0xCD, 0x4F, 0xFE, 0xF2, 0x2D, 0xC8, 0x30, + 0x9D, 0xBE, 0x1B, 0x9B, 0x4A, 0x7E, 0x9F, 0xA7, + 0x78, 0xAB, 0x4D, 0x1D, 0xF1, 0x96, 0x32, 0x84, + 0xFB, 0x80, 0x88, 0xE8, 0x41, 0x97, 0xDC, 0xD0, + 0x4E, 0x33, 0xA4, 0x3B, 0xE0, 0xDD, 0x36, 0xC9, + 0x72, 0x48, 0x8A, 0x2F, 0x35, 0xF0, 0xDF, 0x21, + 0xE1, 0xE5, 0x6C, 0x9A, 0x60, 0x8F, 0xB7, 0x24, + 0xE4, 0x9E, 0x8C, 0x0F, 0x3D, 0x28, 0xBB, 0xD6, + 0x69, 0xA0, 0x66, 0xC7, 0xE3, 0xD8, 0x11, 0x27, + 0xD9, 0x37, 0xF4, 0xF5, 0x8E, 0xD4, 0x76, 0xE2, + 0xDB, 0x15, 0xA2, 0x5C, 0x9C, 0xEE, 0x44, 0xED, + 0x2B, 0xB3, 0x75, 0x74, 0x71, 0x8B, 0x3A, 0x91, + 0x06, 0x19, 0xC1, 0x57, 0x89, 0xCC, 0x82, 0x10, + 0x17, 0xB2, 0x08, 0x70, 0x39, 0xCA, 0xBA, 0xB5, + 0xAA, 0xBF, 0x02, 0xBD, 0x26, 0x58, 0x04, 0x54, + 0x23, 0x4B, 0x90, 0x51, 0x6D, 0x98, 0xD5, 0xB0, + 0xAF, 0x22, 0xDA, 0xB4, 0x87, 0xFC, 0x7D, 0x18, + 0x6F, 0x64, 0x59, 0x09, 0x0C, 0xA5, 0x5D, 0x03, + 0x0A, 0xD3, 0xCE, 0x99, 0x8D, 0xC2, 0xC3, 0x62, + 0xD2, 0x83, 0x1A, 0xAC, 0x7C, 0x93, 0xD7, 0xA9, + 0x16, 0xF7, 0x77, 0xE6, 0x3E, 0x05, 0x73, 0x55, + 0x43, 0x95, 0x7A, 0x6B, 0x38, 0x67, 0x3F, 0xC6, + 0xAD, 0x0E, 0x29, 0x46, 0x45, 0xFA, 0xBC, 0xEC, + 0x5B, 0x7F, 0x0B, 0x1C, 0x01, 0x12, 0x85, 0x50, + 0xF9, 0xEF, 0x25, 0x34, 0x79, 0x2E, 0xEB, 0x00, + 0x5F, 0x86, 0xF8, 0x4C, 0xA8, 0x56, 0xB6, 0x5A, + 0xF3, 0x31, 0x94, 0x92, 0xB1, 0xB8, 0x52, 0xD1, + 0xCF, 0xCB, 0xA1, 0x81, 0x68, 0x47, 0xFF, 0xC5, + 0xFD, 0x1F, 0xDE, 0x53, 0xA3, 0x2C, 0x20, 0xF6, + 0x1E, 0x0D, 0xAE, 0x7B, 0x5E, 0x61, 0xE9, 0x63 }; + +unsigned char table_20[32] = { + 0x0D, 0x0B, 0x11, 0x02, 0x05, 0x1B, 0x08, 0x1D, + 0x04, 0x14, 0x01, 0x09, 0x00, 0x19, 0x1E, 0x15, + 0x1F, 0x0A, 0x0F, 0x1C, 0x10, 0x16, 0x0C, 0x07, + 0x13, 0x1A, 0x06, 0x17, 0x0E, 0x12, 0x18, 0x03 }; + +unsigned char table_21[256] = { + 0x4C, 0x94, 0xAD, 0x66, 0x9E, 0x69, 0x04, 0xA8, + 0x61, 0xE0, 0xE1, 0x3D, 0xFD, 0x9C, 0xFB, 0x19, + 0x1E, 0x80, 0x8C, 0xA0, 0xFC, 0x27, 0x26, 0x3B, + 0x48, 0x6D, 0x07, 0xE4, 0xEA, 0x17, 0x64, 0x9B, + 0xD0, 0xE2, 0xD1, 0x13, 0x39, 0xF5, 0x73, 0xD3, + 0x0C, 0x3A, 0x6E, 0x77, 0xFA, 0xE3, 0x2F, 0x44, + 0x7E, 0x72, 0x30, 0x43, 0xD4, 0x7F, 0x36, 0xD9, + 0xBD, 0x3E, 0x3F, 0x91, 0xBE, 0x54, 0x79, 0xA6, + 0x7C, 0x0E, 0xC5, 0x7A, 0x70, 0xC4, 0xD7, 0xCE, + 0xDA, 0xAA, 0x68, 0x8F, 0xBC, 0x96, 0x1B, 0x16, + 0xA2, 0xC6, 0x67, 0x09, 0x45, 0x9F, 0xCF, 0x41, + 0xC8, 0x60, 0x74, 0x99, 0x5D, 0x85, 0x5F, 0x50, + 0x33, 0x52, 0x22, 0xA9, 0xB5, 0x2D, 0x98, 0x87, + 0x15, 0x9A, 0xAC, 0x2C, 0xDE, 0xC0, 0xB8, 0x37, + 0x88, 0x1F, 0xC1, 0x4F, 0x65, 0x0F, 0x3C, 0x84, + 0x4B, 0x1A, 0xAB, 0xA4, 0x23, 0xCB, 0xB1, 0xC7, + 0xDB, 0xEF, 0x40, 0x0D, 0x46, 0xE8, 0xF4, 0x71, + 0x38, 0x01, 0x5C, 0x0B, 0x5E, 0xC9, 0xAF, 0xC3, + 0xF6, 0xB6, 0x10, 0x1D, 0xE5, 0x8A, 0x90, 0xA7, + 0xA3, 0x05, 0x4E, 0x14, 0x63, 0x25, 0x34, 0xEC, + 0x6B, 0x95, 0x21, 0x55, 0xF2, 0xF0, 0x47, 0x9D, + 0xF8, 0x8E, 0x02, 0x0A, 0xED, 0x97, 0xAE, 0x00, + 0x2A, 0xEB, 0xB2, 0xA5, 0x32, 0x06, 0x2E, 0xFE, + 0x8D, 0x7B, 0x7D, 0x35, 0x5A, 0xD2, 0xF1, 0xE9, + 0xF9, 0x62, 0xB7, 0xB9, 0x53, 0x75, 0x5B, 0x8B, + 0xCC, 0x6C, 0x18, 0x49, 0x89, 0x31, 0xB0, 0x92, + 0x6F, 0xDF, 0x03, 0x57, 0xF3, 0x58, 0xCA, 0x2B, + 0x93, 0xA1, 0xD6, 0x24, 0x29, 0xCD, 0x59, 0x1C, + 0x83, 0xB3, 0x42, 0xBF, 0x82, 0xB4, 0x11, 0x4A, + 0x08, 0xEE, 0x76, 0x4D, 0x12, 0xDC, 0xE6, 0xC2, + 0x56, 0xBA, 0x86, 0x28, 0x6A, 0x20, 0x51, 0xF7, + 0xFF, 0xD8, 0xE7, 0xDD, 0xBB, 0x78, 0xD5, 0x81 }; + +unsigned char table_22[32] = { + 0x0B, 0x15, 0x1C, 0x0C, 0x06, 0x0A, 0x1D, 0x16, + 0x12, 0x0E, 0x04, 0x11, 0x1F, 0x0F, 0x07, 0x02, + 0x17, 0x13, 0x19, 0x18, 0x0D, 0x10, 0x1A, 0x05, + 0x03, 0x00, 0x01, 0x08, 0x09, 0x14, 0x1B, 0x1E }; + +unsigned char table_23[256] = { + 0x36, 0x53, 0x2D, 0xD0, 0x7A, 0xF0, 0xD5, 0x1C, + 0x50, 0x61, 0x9A, 0x90, 0x0B, 0x29, 0x20, 0x77, + 0xF1, 0x82, 0xFE, 0xC1, 0xA7, 0xB6, 0x78, 0x87, + 0x02, 0x05, 0xCB, 0x28, 0xAE, 0xD6, 0x17, 0x1A, + 0x91, 0x5D, 0xB9, 0xE2, 0xDE, 0x6A, 0x4E, 0x07, + 0xAC, 0x38, 0x13, 0x3B, 0x46, 0xFD, 0xB7, 0xD1, + 0x79, 0xFB, 0x58, 0x76, 0x08, 0x47, 0x95, 0xA6, + 0x99, 0x9E, 0x12, 0x67, 0xC2, 0xED, 0x9C, 0x1B, + 0x89, 0x71, 0xB5, 0x4A, 0xAA, 0x5F, 0x34, 0x85, + 0x40, 0x2B, 0x9F, 0x37, 0x7C, 0x0F, 0xD4, 0x75, + 0x48, 0x27, 0x2E, 0xC9, 0xEB, 0x06, 0xDF, 0x8C, + 0x14, 0xAF, 0xEE, 0xA2, 0x74, 0x45, 0x8D, 0x70, + 0x6B, 0xD7, 0x56, 0xCF, 0xBC, 0x7B, 0x01, 0xC8, + 0x54, 0xB0, 0x3C, 0x39, 0xFA, 0x81, 0xDC, 0xBB, + 0x0D, 0xB2, 0xAD, 0x93, 0xC7, 0x8A, 0x73, 0x6C, + 0xC3, 0x04, 0x2F, 0xEF, 0x52, 0x33, 0x9D, 0x1E, + 0xC5, 0x65, 0x23, 0xD8, 0xB1, 0xD2, 0xE5, 0x25, + 0x2C, 0xE6, 0x92, 0xB4, 0xF7, 0xF4, 0x8F, 0x6E, + 0xE8, 0x5A, 0x8E, 0x7D, 0x4C, 0xB3, 0xFF, 0x41, + 0x26, 0xE3, 0x30, 0x69, 0xF8, 0x80, 0x57, 0x4F, + 0xA0, 0x7F, 0x66, 0x68, 0xE1, 0x7E, 0x0E, 0x31, + 0xE7, 0xEA, 0x3E, 0x8B, 0x4B, 0x94, 0xE9, 0xCD, + 0x19, 0x35, 0xA3, 0x98, 0xD9, 0x5B, 0x44, 0x2A, + 0xE0, 0x6D, 0xF3, 0xE4, 0x72, 0x18, 0x03, 0x59, + 0x84, 0x09, 0xA1, 0x9B, 0xBD, 0xDA, 0x4D, 0x63, + 0xCC, 0x3A, 0x10, 0xFC, 0x3F, 0x0A, 0x88, 0x24, + 0xF5, 0x21, 0xC4, 0x6F, 0x1F, 0x42, 0x62, 0x64, + 0x51, 0xDD, 0xCA, 0xF9, 0x22, 0xCE, 0xA8, 0x86, + 0xBA, 0xB8, 0x5C, 0xAB, 0x32, 0x00, 0x0C, 0xF2, + 0x83, 0xDB, 0xF6, 0x60, 0x3D, 0x16, 0xEC, 0x11, + 0xA4, 0xBE, 0x96, 0x5E, 0x97, 0xD3, 0xA5, 0x55, + 0x1D, 0x15, 0xC6, 0xBF, 0xA9, 0x43, 0xC0, 0x49 }; + +unsigned char table_24[256] = { + 0xDC, 0x5A, 0xE6, 0x59, 0x64, 0xDA, 0x58, 0x40, + 0x95, 0xF8, 0x2A, 0xE0, 0x39, 0x7E, 0x32, 0x89, + 0x09, 0x93, 0xED, 0x55, 0xC3, 0x5B, 0x1A, 0xD1, + 0xA5, 0x8B, 0x0F, 0x13, 0xC9, 0xE1, 0x34, 0xD0, + 0xB6, 0xA2, 0xD9, 0x52, 0x57, 0x83, 0xFD, 0xE9, + 0xAC, 0x73, 0x6E, 0x21, 0xF1, 0x0E, 0x25, 0xCC, + 0x36, 0xFB, 0xF7, 0x92, 0x15, 0x30, 0x54, 0x91, + 0xD6, 0x9E, 0xAA, 0x35, 0x70, 0xB2, 0xC0, 0x27, + 0xFE, 0x04, 0xBC, 0xC7, 0x02, 0xFA, 0x7D, 0xE3, + 0xBE, 0x62, 0x79, 0x2B, 0x31, 0x6A, 0x8F, 0x7F, + 0x56, 0xF0, 0xB4, 0x0C, 0x1F, 0x68, 0xB7, 0xB9, + 0x0B, 0x14, 0x3E, 0xA9, 0x4B, 0x03, 0x10, 0xEE, + 0x2C, 0xAB, 0x8A, 0x77, 0xB1, 0xE7, 0xCA, 0xD4, + 0x98, 0x01, 0xAD, 0x1E, 0x50, 0x26, 0x82, 0x44, + 0xF3, 0xBF, 0xD3, 0x6B, 0x33, 0x0A, 0x3C, 0x5D, + 0xCE, 0x81, 0xC5, 0x78, 0x9F, 0xB8, 0x23, 0xDB, + 0x4E, 0xA1, 0x41, 0x76, 0xAE, 0x51, 0x86, 0x06, + 0x7A, 0x66, 0xA0, 0x5E, 0x29, 0x17, 0x84, 0x4A, + 0xB0, 0x3B, 0x3D, 0x71, 0x07, 0x7B, 0x0D, 0x9A, + 0x6F, 0x9B, 0x5C, 0x88, 0xB3, 0xD7, 0x24, 0xD5, + 0x48, 0xF5, 0xE8, 0xE4, 0xCF, 0x16, 0xA4, 0xC8, + 0xEF, 0x42, 0x22, 0xEC, 0x47, 0x69, 0x90, 0x63, + 0xE2, 0x1B, 0x87, 0x85, 0x3F, 0xDE, 0x8C, 0x60, + 0x99, 0xE5, 0x8E, 0x4F, 0xF4, 0xBA, 0xB5, 0x9C, + 0x37, 0x67, 0xBD, 0xA6, 0x97, 0xDD, 0xCB, 0x43, + 0x45, 0x19, 0x49, 0x1C, 0x75, 0xC1, 0xBB, 0xF2, + 0x46, 0xFC, 0x53, 0x9D, 0xD8, 0xA3, 0xDF, 0x2F, + 0xEB, 0x72, 0x94, 0xA8, 0x6D, 0xC6, 0x28, 0x4C, + 0x00, 0x38, 0xC2, 0x65, 0x05, 0x2E, 0xD2, 0x12, + 0xFF, 0x18, 0x61, 0x6C, 0x7C, 0x11, 0xAF, 0x96, + 0xCD, 0x20, 0x74, 0x08, 0x1D, 0xC4, 0xF9, 0x4D, + 0xEA, 0x8D, 0x2D, 0x5F, 0xF6, 0xA7, 0x80, 0x3A }; + +unsigned char table_25[32] = { + 0x0A, 0x11, 0x17, 0x03, 0x05, 0x0B, 0x18, 0x13, + 0x09, 0x02, 0x00, 0x1C, 0x0C, 0x08, 0x1B, 0x14, + 0x06, 0x0E, 0x01, 0x0D, 0x16, 0x1E, 0x1D, 0x19, + 0x0F, 0x1A, 0x10, 0x04, 0x12, 0x15, 0x07, 0x1F }; + +unsigned char table_26[32] = { + 0x19, 0x13, 0x1B, 0x01, 0x1C, 0x0D, 0x0C, 0x15, + 0x0B, 0x00, 0x1A, 0x0F, 0x12, 0x16, 0x08, 0x0A, + 0x03, 0x06, 0x14, 0x10, 0x18, 0x04, 0x11, 0x1D, + 0x1F, 0x07, 0x17, 0x05, 0x02, 0x0E, 0x1E, 0x09 }; + +unsigned char table_27[256] = { + 0x72, 0xF0, 0x14, 0xCB, 0x61, 0xA5, 0xB2, 0x02, + 0x75, 0x22, 0xC3, 0x9D, 0x5A, 0x63, 0xFA, 0x5F, + 0xD9, 0x55, 0x58, 0x43, 0x24, 0x7D, 0x77, 0x93, + 0xBA, 0x50, 0x1D, 0xF7, 0x49, 0x18, 0xB0, 0x42, + 0xBB, 0xEC, 0x52, 0x38, 0xDC, 0xC8, 0x16, 0x54, + 0x17, 0x19, 0x89, 0x67, 0x33, 0x3C, 0x0A, 0xAD, + 0xC9, 0xDE, 0x81, 0xED, 0xBD, 0x0E, 0x0B, 0x6D, + 0x46, 0x30, 0x35, 0x2B, 0x8C, 0xA0, 0x1C, 0x0D, + 0xFD, 0xA1, 0x70, 0xC6, 0xD8, 0x41, 0xB3, 0xC0, + 0x44, 0xEB, 0x92, 0xBE, 0x6B, 0x98, 0x1A, 0x76, + 0x71, 0xC5, 0x51, 0x56, 0x80, 0xFC, 0x01, 0x53, + 0x4B, 0xD0, 0x8B, 0xD2, 0x7B, 0xE7, 0x15, 0x5D, + 0xE5, 0xA6, 0x8A, 0xD3, 0x9B, 0xF4, 0x69, 0x23, + 0xE8, 0xB6, 0xC7, 0xE2, 0x73, 0x9F, 0x88, 0xDF, + 0xB4, 0x28, 0xEE, 0xC2, 0x94, 0xB8, 0xF9, 0x7F, + 0x4A, 0x57, 0x06, 0xF6, 0xBF, 0xC1, 0xAB, 0xFB, + 0xA4, 0x8E, 0xD1, 0xD7, 0xF5, 0x7C, 0xA3, 0x1E, + 0x3B, 0x32, 0x03, 0xAA, 0x90, 0x5C, 0x48, 0xE0, + 0xE3, 0xCF, 0xD4, 0xEF, 0x59, 0xD5, 0x1B, 0x34, + 0x1F, 0x95, 0xCE, 0x7A, 0x20, 0x26, 0x87, 0xB7, + 0x78, 0x9C, 0x4F, 0xA2, 0x12, 0x97, 0x27, 0x3F, + 0xFF, 0x07, 0x84, 0x96, 0x04, 0xAF, 0xA8, 0xEA, + 0x2C, 0x6C, 0xAE, 0x37, 0x91, 0xA9, 0x10, 0xDB, + 0xCD, 0xDA, 0x08, 0x99, 0xF1, 0x4D, 0xCC, 0x68, + 0x79, 0x2E, 0xB1, 0x39, 0x9E, 0xE9, 0x2F, 0x6A, + 0x3D, 0x0F, 0x85, 0x8D, 0xCA, 0x29, 0x86, 0xD6, + 0xDD, 0x05, 0x25, 0x3A, 0x40, 0x21, 0x45, 0xAC, + 0x11, 0xF3, 0xA7, 0x09, 0x2A, 0x31, 0xE4, 0x0C, + 0xF8, 0x6E, 0x3E, 0xB5, 0x82, 0xFE, 0x74, 0x13, + 0x65, 0xE1, 0x2D, 0x8F, 0xE6, 0xC4, 0x00, 0x5B, + 0x4E, 0xB9, 0x66, 0xF2, 0x62, 0x36, 0x4C, 0x83, + 0x5E, 0x6F, 0x47, 0x64, 0xBC, 0x9A, 0x60, 0x7E }; + +unsigned char table_28[32] = { + 0x15, 0x05, 0x08, 0x19, 0x02, 0x18, 0x1E, 0x07, + 0x0D, 0x0C, 0x1A, 0x06, 0x17, 0x03, 0x10, 0x09, + 0x01, 0x11, 0x1C, 0x04, 0x0F, 0x1F, 0x12, 0x0B, + 0x1B, 0x13, 0x0A, 0x16, 0x0E, 0x00, 0x1D, 0x14 }; + +unsigned char table_29[256] = { + 0x34, 0x59, 0x05, 0x13, 0x09, 0x1D, 0xDF, 0x77, + 0x11, 0xA5, 0x92, 0x27, 0xCD, 0x7B, 0x5E, 0x80, + 0xF9, 0x50, 0x18, 0x24, 0xD4, 0x70, 0x4A, 0x39, + 0x66, 0xA4, 0xDB, 0xE9, 0xED, 0x48, 0xD9, 0xE7, + 0x32, 0xDA, 0x53, 0x8F, 0x72, 0xE1, 0xF6, 0xFE, + 0xD3, 0xAD, 0xA6, 0x1F, 0xB9, 0xD1, 0x0F, 0x4C, + 0x23, 0x90, 0x68, 0xBC, 0x4B, 0x9B, 0x3D, 0xAB, + 0xF0, 0x94, 0x4F, 0x1C, 0x07, 0x65, 0x7F, 0x01, + 0x5C, 0xD7, 0x21, 0x8C, 0xBF, 0x8E, 0xB8, 0x86, + 0x6C, 0x33, 0x36, 0xC1, 0x06, 0x74, 0x37, 0x84, + 0x41, 0xAE, 0x67, 0x29, 0xB4, 0x85, 0xCE, 0x2A, + 0xCB, 0x1E, 0x61, 0x9E, 0x7A, 0x44, 0x3E, 0x89, + 0x14, 0x20, 0x19, 0xBB, 0xE0, 0xAA, 0xCF, 0x83, + 0xA8, 0x93, 0x43, 0xF2, 0xAC, 0x0E, 0xD2, 0xCC, + 0xDD, 0x47, 0x58, 0xC9, 0xCA, 0x1B, 0x54, 0x6E, + 0x8A, 0x79, 0xF8, 0xC4, 0xFB, 0xD5, 0x91, 0xDE, + 0x12, 0x31, 0x99, 0xFA, 0x6D, 0xC8, 0x57, 0xEC, + 0xB7, 0x28, 0x0C, 0x52, 0xF1, 0x0D, 0xB1, 0x9A, + 0x26, 0x98, 0x16, 0x7D, 0xD0, 0x2E, 0x8B, 0xD8, + 0xE6, 0xE8, 0x30, 0xFD, 0x7C, 0x64, 0x5A, 0xBD, + 0x87, 0xE2, 0xA1, 0x3F, 0xC3, 0x38, 0x96, 0xA3, + 0x2D, 0xF3, 0x3A, 0xEE, 0xC0, 0x10, 0xEA, 0x6F, + 0x8D, 0x03, 0xF4, 0x51, 0x97, 0x7E, 0x56, 0x42, + 0x3C, 0x5D, 0x5F, 0xF5, 0x6A, 0xAF, 0xE4, 0xBE, + 0xBA, 0x78, 0xA0, 0x5B, 0x49, 0xA7, 0xC7, 0x9C, + 0x63, 0x6B, 0x00, 0x17, 0x69, 0x75, 0x3B, 0x40, + 0xEF, 0x45, 0xB5, 0x2B, 0x2F, 0x02, 0xC6, 0x22, + 0x9F, 0xFC, 0x73, 0x08, 0x81, 0xB2, 0x2C, 0x71, + 0x35, 0xA2, 0xE3, 0xB3, 0x9D, 0xC5, 0x0A, 0xC2, + 0x25, 0x82, 0xDC, 0x88, 0xA9, 0xE5, 0xF7, 0xEB, + 0xD6, 0x60, 0x76, 0x55, 0x0B, 0x4E, 0xFF, 0x1A, + 0x46, 0x62, 0xB6, 0xB0, 0x15, 0x04, 0x95, 0x4D }; + +unsigned char table_30[32] = { + 0x00, 0x1C, 0x0E, 0x0C, 0x06, 0x16, 0x09, 0x12, + 0x01, 0x13, 0x0B, 0x14, 0x11, 0x08, 0x04, 0x18, + 0x10, 0x1B, 0x15, 0x03, 0x02, 0x19, 0x1A, 0x17, + 0x1E, 0x1F, 0x0F, 0x07, 0x0D, 0x05, 0x1D, 0x0A }; + +unsigned char table_31[256] = { + 0xDF, 0xD8, 0x3F, 0xBC, 0x5F, 0xC9, 0x8E, 0x4C, + 0x0B, 0x3C, 0xE5, 0xBF, 0x39, 0xD5, 0x30, 0xDD, + 0x23, 0xC7, 0x72, 0x63, 0x1F, 0xF8, 0x96, 0x31, + 0x70, 0xD6, 0x9E, 0xE8, 0x9D, 0xF5, 0xEF, 0x65, + 0xC2, 0x50, 0x62, 0x77, 0xD3, 0x6C, 0x1A, 0x91, + 0xBB, 0xFF, 0xCD, 0x9B, 0xB6, 0xBA, 0xB8, 0x7A, + 0x14, 0xA7, 0x74, 0x89, 0xD4, 0x6E, 0x19, 0x69, + 0xAB, 0x01, 0x15, 0x0E, 0x87, 0x55, 0x79, 0x1C, + 0x18, 0xBE, 0xA8, 0xDB, 0x52, 0xD2, 0x8F, 0x7E, + 0x81, 0xAF, 0xFD, 0x5C, 0x3E, 0x1B, 0xB9, 0xB2, + 0xB7, 0x51, 0x57, 0x8C, 0xCF, 0x5B, 0xA4, 0x75, + 0xDE, 0x22, 0x8B, 0x10, 0x12, 0xC8, 0x35, 0x2D, + 0x45, 0xB5, 0xF0, 0x47, 0x88, 0x16, 0xEB, 0x67, + 0xD9, 0x0C, 0xF1, 0xC1, 0x34, 0x33, 0xC6, 0x78, + 0xB3, 0x26, 0xE3, 0xBD, 0x5D, 0x4E, 0x66, 0xE4, + 0xD7, 0xC4, 0xE6, 0xA1, 0xB0, 0x95, 0x2B, 0x9A, + 0x4A, 0x3A, 0xCB, 0x40, 0xE1, 0x60, 0x49, 0xCC, + 0x03, 0xAC, 0xF4, 0x97, 0x32, 0x0F, 0x38, 0x17, + 0xF9, 0xE0, 0xD1, 0xFB, 0x04, 0x5E, 0x68, 0x06, + 0xAE, 0xFA, 0xAA, 0xED, 0x24, 0x0D, 0x00, 0x61, + 0x20, 0xA3, 0x7B, 0x6B, 0x76, 0x27, 0xEA, 0xCE, + 0x6A, 0x82, 0x9F, 0x6D, 0x9C, 0x64, 0xA2, 0x11, + 0x37, 0x2A, 0xCA, 0x84, 0x25, 0x7C, 0x2F, 0x8D, + 0x90, 0xE7, 0x09, 0x93, 0xF3, 0x43, 0x71, 0xEC, + 0xA9, 0x7D, 0x94, 0xA6, 0x3D, 0x7F, 0x54, 0x44, + 0x99, 0x80, 0x41, 0xC0, 0xA0, 0x8A, 0x1E, 0xDC, + 0x08, 0xD0, 0x2E, 0x42, 0x05, 0x85, 0x86, 0xFE, + 0x3B, 0x59, 0xC3, 0x58, 0x13, 0xB4, 0x36, 0xA5, + 0x73, 0x28, 0x29, 0xDA, 0x4F, 0x1D, 0xB1, 0x53, + 0x46, 0x2C, 0xF2, 0x4D, 0xAD, 0xFC, 0x83, 0x02, + 0x6F, 0x07, 0xE9, 0xEE, 0x21, 0x98, 0x5A, 0xC5, + 0x92, 0x48, 0xF7, 0x0A, 0xF6, 0xE2, 0x4B, 0x56 }; + +unsigned char table_32[256] = { + 0x7B, 0x0F, 0x56, 0x2F, 0x1E, 0x2A, 0x7A, 0xD1, + 0x02, 0x91, 0x4E, 0x37, 0x6C, 0x10, 0xA7, 0xF2, + 0x38, 0xAC, 0x9E, 0x2B, 0x5E, 0x23, 0xE3, 0x19, + 0x9B, 0xF6, 0xB0, 0x59, 0x14, 0xB9, 0xA9, 0x46, + 0x84, 0x1D, 0xC0, 0x98, 0xF3, 0xE1, 0xE8, 0x94, + 0x52, 0x35, 0xBA, 0xD8, 0x07, 0xEF, 0x31, 0xF8, + 0x03, 0x76, 0x9C, 0xD7, 0xE4, 0x8B, 0xAF, 0x60, + 0xDD, 0x51, 0x00, 0xDF, 0x11, 0x7F, 0x1C, 0xED, + 0x49, 0xC9, 0xF4, 0x87, 0x64, 0xFC, 0x5D, 0xAD, + 0x88, 0x85, 0xF7, 0x5A, 0x92, 0xDB, 0x72, 0x1A, + 0x83, 0x15, 0x30, 0x24, 0x9F, 0xFF, 0x5B, 0xF1, + 0xD2, 0xFD, 0xC2, 0xB5, 0x25, 0x22, 0x18, 0x3D, + 0xCD, 0x97, 0x8C, 0xCC, 0x78, 0x90, 0xAA, 0x5F, + 0x0A, 0x57, 0x05, 0x61, 0xD4, 0xA0, 0x3A, 0xDE, + 0x3B, 0xF9, 0x65, 0x68, 0x4F, 0x28, 0xFA, 0xEB, + 0x63, 0x2D, 0x8D, 0xD0, 0xA1, 0xFE, 0x12, 0x96, + 0x3C, 0x42, 0x29, 0xD6, 0xA4, 0x34, 0xBD, 0x70, + 0x89, 0xBE, 0xF5, 0x79, 0xAB, 0x8F, 0x32, 0xB4, + 0xEE, 0xE7, 0x2C, 0x04, 0x4B, 0xD5, 0xB1, 0x54, + 0xF0, 0xDA, 0x16, 0x77, 0xA6, 0x53, 0xB2, 0xE2, + 0x73, 0xBF, 0x17, 0xA8, 0x75, 0x26, 0xE0, 0xBC, + 0x0C, 0x71, 0xFB, 0x6D, 0x7E, 0xC5, 0xEA, 0x21, + 0x9D, 0x95, 0x8E, 0xA5, 0x48, 0xB8, 0x7D, 0xCB, + 0x01, 0x99, 0xE5, 0xBB, 0x82, 0xC4, 0xCA, 0xC1, + 0x58, 0x6E, 0x5C, 0x7C, 0xDC, 0x33, 0xB6, 0xC3, + 0x09, 0xC7, 0x1F, 0x0D, 0x43, 0x6F, 0xE9, 0x86, + 0x27, 0xC8, 0x44, 0xB3, 0xD3, 0xCF, 0x08, 0x66, + 0x1B, 0x20, 0x4D, 0xD9, 0xC6, 0x36, 0x40, 0x74, + 0x62, 0x6A, 0x55, 0xEC, 0x06, 0x2E, 0xE6, 0x80, + 0x13, 0x93, 0x50, 0xCE, 0x69, 0x3E, 0x67, 0x4A, + 0x81, 0x4C, 0x0B, 0x3F, 0xB7, 0x0E, 0x39, 0xAE, + 0x47, 0x6B, 0x8A, 0xA2, 0x9A, 0xA3, 0x45, 0x41 }; + +unsigned char table_33[256] = { + 0xDE, 0xD3, 0x79, 0x67, 0x13, 0x5C, 0x04, 0xF2, + 0xD9, 0x9F, 0x65, 0x56, 0xCC, 0x3B, 0xA4, 0x9A, + 0x08, 0xBF, 0x26, 0xB2, 0xA7, 0x5E, 0xAA, 0xCA, + 0xBB, 0x2B, 0x38, 0x3F, 0xD8, 0x87, 0xFA, 0x5D, + 0x73, 0x8E, 0x1E, 0x93, 0x05, 0xAF, 0x3E, 0x4E, + 0x90, 0xDB, 0x0B, 0x33, 0x0D, 0x2F, 0x86, 0x4F, + 0xFD, 0xD0, 0x39, 0xB1, 0x8A, 0x1A, 0x20, 0xE6, + 0xCF, 0xA2, 0x82, 0xDF, 0x42, 0x9C, 0x30, 0x40, + 0xE3, 0xB0, 0x88, 0x5A, 0xEC, 0x25, 0xE2, 0xC4, + 0x12, 0x54, 0x50, 0x97, 0x96, 0x21, 0x23, 0x7B, + 0x1D, 0x61, 0x52, 0x34, 0x7D, 0x69, 0x16, 0xC3, + 0x31, 0xF8, 0x48, 0x19, 0x95, 0x01, 0x29, 0x8C, + 0x15, 0xAC, 0x84, 0x74, 0xAB, 0x70, 0xDA, 0x36, + 0xD6, 0x8F, 0xFE, 0x35, 0xD7, 0x2E, 0x89, 0x07, + 0x62, 0x17, 0xDC, 0x92, 0x45, 0x83, 0xB5, 0xE5, + 0x8B, 0xC0, 0x27, 0x85, 0x7C, 0x9D, 0x55, 0x81, + 0x71, 0xCD, 0xC9, 0x00, 0x02, 0xC1, 0x0A, 0x37, + 0xED, 0xEA, 0xC2, 0x98, 0x49, 0x06, 0x1C, 0x78, + 0x64, 0xCE, 0x9E, 0x4C, 0x7A, 0xB4, 0x43, 0x0F, + 0xE0, 0x7E, 0xBC, 0x5B, 0x51, 0xE7, 0x18, 0xF9, + 0x11, 0xA1, 0xF5, 0xC7, 0xCB, 0x4D, 0x6A, 0x0E, + 0x57, 0xF1, 0xFB, 0xB3, 0x99, 0xF0, 0x32, 0xD5, + 0xA9, 0x4B, 0x6F, 0x6D, 0xA8, 0xC5, 0xDD, 0x7F, + 0xEB, 0xBE, 0xFC, 0x2C, 0x22, 0x58, 0x03, 0x9B, + 0x77, 0xF7, 0xBD, 0xBA, 0xD2, 0x6B, 0xAD, 0x5F, + 0x10, 0x6E, 0x09, 0xD1, 0x1B, 0x24, 0xEF, 0x72, + 0x3D, 0x59, 0x28, 0xE1, 0xB7, 0x44, 0x8D, 0xB8, + 0xAE, 0x2D, 0x60, 0xA6, 0xC8, 0x0C, 0xF4, 0x41, + 0xA3, 0x68, 0x46, 0x6C, 0x76, 0xA0, 0xB6, 0x66, + 0xE4, 0x1F, 0x75, 0x4A, 0xFF, 0x2A, 0x94, 0xD4, + 0xF3, 0xE9, 0x91, 0x63, 0xA5, 0xB9, 0xE8, 0x14, + 0x80, 0x3C, 0xEE, 0x47, 0xC6, 0x3A, 0x53, 0xF6 }; + +unsigned char table_34[256] = { + 0xF0, 0xE9, 0x3E, 0xD6, 0x89, 0xC8, 0xC7, 0x23, + 0x75, 0x26, 0x5F, 0x9C, 0x57, 0xB8, 0x2A, 0x29, + 0xE5, 0xB5, 0x68, 0xA4, 0x92, 0x46, 0x40, 0x7F, + 0xF2, 0xBC, 0x6A, 0xE0, 0x8F, 0x0F, 0xE4, 0x3A, + 0xE1, 0x30, 0x84, 0x6E, 0x82, 0x8E, 0x56, 0xC5, + 0x32, 0x85, 0xFB, 0x59, 0x43, 0x41, 0xC2, 0xF6, + 0x67, 0x5A, 0x7C, 0x34, 0xA1, 0xD0, 0x4B, 0xAC, + 0x61, 0x72, 0x6B, 0xAF, 0xC4, 0x20, 0x9A, 0xD4, + 0x74, 0x8D, 0x87, 0x83, 0xE2, 0x62, 0x6D, 0xE6, + 0xE7, 0xF9, 0x76, 0xCB, 0x18, 0x90, 0x4F, 0xFF, + 0xD3, 0x3C, 0x08, 0x79, 0x93, 0x2D, 0x95, 0xA3, + 0xDD, 0x5B, 0xDA, 0x7A, 0x39, 0x4D, 0xC1, 0x2E, + 0xCC, 0x53, 0xE8, 0xA2, 0xCF, 0x15, 0x78, 0x1C, + 0xEB, 0x9B, 0x7B, 0xAD, 0x31, 0x2F, 0xE3, 0xC9, + 0x3B, 0xEC, 0x2C, 0x49, 0x02, 0x52, 0x28, 0xBA, + 0x0C, 0x19, 0x24, 0xF7, 0x97, 0x09, 0xA6, 0xA0, + 0xDF, 0xD1, 0xD2, 0xDC, 0x51, 0xA5, 0x94, 0xFD, + 0x71, 0xF5, 0x50, 0x0A, 0x69, 0x25, 0x88, 0x5C, + 0x91, 0xD5, 0x47, 0x0B, 0x27, 0x13, 0x96, 0xD9, + 0xF1, 0xA9, 0x70, 0xC3, 0xBE, 0x42, 0x4E, 0x4A, + 0xB1, 0x07, 0xA7, 0x54, 0xFE, 0x48, 0x9F, 0x63, + 0x17, 0xAE, 0xB9, 0x58, 0x21, 0x35, 0xED, 0x5D, + 0x9D, 0x3D, 0xB4, 0xFC, 0xEA, 0x8C, 0x80, 0xA8, + 0x1E, 0xB0, 0xDE, 0x0D, 0x11, 0x6F, 0x04, 0x12, + 0xF4, 0x10, 0x64, 0x0E, 0xD7, 0x2B, 0xB3, 0x8B, + 0xB7, 0x01, 0x86, 0xCA, 0xFA, 0x9E, 0xEE, 0x66, + 0x37, 0x65, 0x81, 0x38, 0x1F, 0xAA, 0x73, 0xAB, + 0xBD, 0xDB, 0x14, 0xCD, 0x00, 0xBB, 0x98, 0x44, + 0x45, 0xB6, 0x99, 0x5E, 0xD8, 0x1D, 0x36, 0xF8, + 0x55, 0x6C, 0x16, 0x7E, 0x77, 0x3F, 0x22, 0xEF, + 0xF3, 0x7D, 0xC6, 0xCE, 0x8A, 0xB2, 0x33, 0x4C, + 0x03, 0x05, 0xBF, 0x06, 0x1B, 0xC0, 0x1A, 0x60 }; + +unsigned char table_35[256] = { + 0xCC, 0x40, 0xEF, 0x1F, 0xDB, 0xE5, 0x71, 0x51, + 0x3B, 0x0F, 0x7D, 0x9C, 0x83, 0x17, 0x6F, 0x8F, + 0x13, 0xDC, 0x7F, 0xA9, 0xA5, 0xA2, 0x9D, 0xDF, + 0xE7, 0x97, 0x2A, 0x30, 0xF2, 0x73, 0xCF, 0x87, + 0x29, 0xB3, 0x86, 0x43, 0x09, 0xB0, 0x2E, 0x10, + 0x8E, 0xBC, 0x57, 0xBA, 0x68, 0xF5, 0xCB, 0x89, + 0x32, 0xC1, 0x6B, 0x1E, 0xAC, 0xB2, 0x2D, 0x6A, + 0x50, 0xEB, 0x18, 0x06, 0xD8, 0xC7, 0x36, 0x31, + 0xC5, 0xAF, 0x12, 0x15, 0xB7, 0x37, 0x4E, 0x01, + 0x14, 0x21, 0x44, 0x5E, 0xF4, 0xB4, 0xE4, 0x65, + 0xFE, 0x8A, 0xEA, 0x0D, 0xBB, 0x45, 0x8B, 0x25, + 0x80, 0x35, 0x61, 0xA8, 0x4A, 0x47, 0xAB, 0x91, + 0x1B, 0x1C, 0x05, 0x4D, 0x5A, 0xD4, 0xF1, 0x9B, + 0x0E, 0x98, 0xCA, 0x96, 0x42, 0x7E, 0x03, 0x5F, + 0xE2, 0x90, 0xBF, 0x82, 0xC9, 0x3D, 0xE0, 0x5C, + 0xFA, 0x3E, 0x41, 0x11, 0x79, 0x58, 0x24, 0x2C, + 0xC0, 0x28, 0x5D, 0xA3, 0xDE, 0x67, 0xFF, 0xA4, + 0x63, 0xB1, 0x22, 0x04, 0xFD, 0x70, 0x39, 0x46, + 0xAA, 0x0A, 0x34, 0x6C, 0xD7, 0x92, 0xA1, 0x3C, + 0x19, 0xD5, 0xFC, 0xAD, 0x85, 0x07, 0x00, 0x23, + 0xF8, 0x69, 0x56, 0x53, 0x55, 0x7A, 0xB8, 0xC8, + 0xDA, 0xCE, 0xF3, 0x5B, 0x49, 0xE1, 0xBE, 0xEC, + 0x1A, 0x88, 0x02, 0xBD, 0xF7, 0x1D, 0x64, 0xA0, + 0x4F, 0xD9, 0xE3, 0x95, 0xC6, 0x48, 0x2B, 0xED, + 0x9A, 0x9E, 0x26, 0x6E, 0xD1, 0x94, 0xB9, 0x93, + 0xDD, 0xF6, 0xA6, 0xFB, 0xC2, 0xB6, 0x0C, 0xE9, + 0x77, 0xF9, 0xCD, 0x08, 0xEE, 0x3F, 0xE6, 0x75, + 0xD6, 0x84, 0x76, 0x8C, 0xF0, 0xAE, 0xD2, 0x78, + 0x2F, 0x4B, 0x16, 0x4C, 0x27, 0x81, 0x6D, 0x99, + 0x38, 0xD3, 0x54, 0x62, 0x74, 0x20, 0x60, 0xC3, + 0x7C, 0x8D, 0x72, 0x0B, 0x52, 0xE8, 0xA7, 0x3A, + 0x59, 0xC4, 0x9F, 0xD0, 0x66, 0x7B, 0x33, 0xB5 }; + +unsigned char table_36[256] = { + 0xDB, 0x6F, 0xFE, 0xB3, 0x5C, 0x1F, 0xB8, 0xBF, + 0xA3, 0x71, 0x11, 0x56, 0x90, 0xE2, 0x63, 0x18, + 0x83, 0x51, 0x21, 0xEB, 0x66, 0x08, 0xA6, 0xA5, + 0x1C, 0xF5, 0x14, 0x24, 0x41, 0x33, 0xA7, 0xB5, + 0xC7, 0x79, 0x57, 0x50, 0x85, 0xE1, 0x6D, 0xF7, + 0x0E, 0xDE, 0x67, 0xAB, 0xA1, 0x0B, 0xD9, 0x4A, + 0xCA, 0x36, 0xEA, 0xDA, 0x16, 0xEF, 0x9F, 0x0A, + 0x09, 0x9A, 0x1D, 0xC5, 0xD7, 0x5F, 0x19, 0xDC, + 0x15, 0x06, 0xE8, 0x94, 0x0C, 0x0D, 0xC9, 0x7C, + 0xD6, 0x62, 0xBB, 0x49, 0xF9, 0x61, 0x07, 0x9B, + 0x28, 0xC3, 0x9E, 0xF4, 0x38, 0x78, 0x20, 0x03, + 0xA2, 0x7F, 0xC2, 0x9D, 0x5E, 0x65, 0x52, 0x17, + 0x2E, 0x1B, 0xB0, 0x42, 0xBC, 0xFD, 0xF1, 0xD2, + 0xF6, 0x60, 0xD3, 0x29, 0x97, 0x3D, 0x0F, 0xB1, + 0x2F, 0x22, 0xDD, 0x80, 0x32, 0xF8, 0xAD, 0x70, + 0xB9, 0x8F, 0x37, 0xCE, 0x46, 0x58, 0xB7, 0x30, + 0xED, 0x7A, 0xE9, 0xC0, 0x7D, 0x13, 0x64, 0x23, + 0x4E, 0xC8, 0xF0, 0xCC, 0x3B, 0x45, 0x68, 0x8D, + 0xBE, 0x8B, 0xD8, 0x43, 0x02, 0x27, 0xE4, 0xAA, + 0x10, 0xF2, 0x59, 0x72, 0x40, 0x26, 0x69, 0xE5, + 0x05, 0x84, 0x4F, 0xE0, 0x6B, 0xC1, 0xAC, 0x4C, + 0xFB, 0x31, 0x77, 0x8E, 0xD4, 0x12, 0xA9, 0xB4, + 0xEC, 0x00, 0x76, 0x1E, 0x25, 0xAE, 0xE7, 0x3C, + 0x35, 0x93, 0x9C, 0xC4, 0xFC, 0x2D, 0x91, 0x04, + 0xAF, 0x53, 0x3F, 0xE6, 0xA4, 0xD0, 0x1A, 0xDF, + 0x3A, 0x55, 0x99, 0x01, 0xCB, 0x6C, 0x82, 0x3E, + 0x5D, 0xA8, 0x88, 0x54, 0x5B, 0x95, 0xCD, 0x8C, + 0x81, 0x34, 0xD1, 0x39, 0xFF, 0xEE, 0xFA, 0x8A, + 0x6E, 0x86, 0x92, 0x89, 0xF3, 0x6A, 0xBA, 0x2C, + 0xD5, 0x44, 0xC6, 0x96, 0xBD, 0xB2, 0x2B, 0x87, + 0x74, 0xA0, 0x73, 0x5A, 0x2A, 0x98, 0x75, 0x47, + 0x4B, 0xB6, 0x7B, 0x4D, 0xCF, 0x7E, 0x48, 0xE3 }; + +unsigned char table_37[256] = { + 0x1F, 0xD6, 0xB1, 0xB3, 0x40, 0xAD, 0xDE, 0xB7, + 0x19, 0xB4, 0xE7, 0x0B, 0x9C, 0x2D, 0xE0, 0xF5, + 0xCF, 0x2C, 0x30, 0x65, 0x2F, 0xCD, 0x02, 0x91, + 0xCE, 0x2B, 0xBF, 0x78, 0xE6, 0xFA, 0x51, 0x48, + 0xFB, 0x4D, 0xBE, 0x71, 0x1A, 0x56, 0xFD, 0x81, + 0x33, 0x75, 0x89, 0x96, 0x37, 0x82, 0x9E, 0x93, + 0x41, 0x18, 0x5B, 0x2E, 0x22, 0x0F, 0xAF, 0x4B, + 0xB9, 0xD5, 0xEE, 0x6C, 0xE4, 0x05, 0xCC, 0x99, + 0xE5, 0x3B, 0x62, 0xBD, 0x7B, 0xAA, 0x4A, 0xE2, + 0x34, 0x43, 0xF7, 0x39, 0xFE, 0x14, 0x1D, 0xE3, + 0xF0, 0xA7, 0x77, 0xDF, 0xA0, 0xD3, 0xAC, 0xD9, + 0xEA, 0x76, 0xDD, 0xA4, 0xC5, 0xC9, 0x61, 0xF3, + 0xA8, 0xB0, 0x35, 0xE8, 0x68, 0xD4, 0x15, 0xF9, + 0x97, 0xED, 0x25, 0x0A, 0x88, 0x8F, 0x06, 0xA3, + 0x16, 0x36, 0x32, 0xA2, 0xC6, 0x64, 0xD7, 0x94, + 0xD2, 0x6D, 0x74, 0xFC, 0x44, 0x27, 0x5C, 0xFF, + 0x60, 0x1E, 0x58, 0x8B, 0x5E, 0xC7, 0x90, 0x17, + 0x63, 0xAE, 0xC3, 0x12, 0x13, 0x84, 0xEC, 0x49, + 0xA5, 0x9B, 0x31, 0x8D, 0xE1, 0x79, 0xF1, 0x00, + 0x28, 0x3D, 0xC2, 0x55, 0x20, 0x52, 0x95, 0x7E, + 0x42, 0x1C, 0x66, 0x92, 0x7D, 0xB6, 0xC4, 0xF4, + 0x80, 0xB2, 0x72, 0x6E, 0x11, 0xF6, 0x0D, 0x5A, + 0xEF, 0x9D, 0x69, 0x9A, 0x45, 0x67, 0x3F, 0xDA, + 0x8E, 0x57, 0x09, 0x7C, 0x38, 0xA6, 0x83, 0x87, + 0x7A, 0x08, 0x4C, 0x5F, 0x85, 0x7F, 0xD0, 0x04, + 0x50, 0xCB, 0xB8, 0x07, 0x24, 0x26, 0x29, 0x46, + 0x01, 0x03, 0xC1, 0xD8, 0xDC, 0x0E, 0x3C, 0x4F, + 0x53, 0x4E, 0xB5, 0xF8, 0xC0, 0x8A, 0xF2, 0xBB, + 0xE9, 0x5D, 0x2A, 0xBA, 0x0C, 0x1B, 0x3A, 0xA9, + 0x21, 0x6A, 0x70, 0xBC, 0xEB, 0xA1, 0x54, 0x10, + 0x98, 0x9F, 0x23, 0xD1, 0x6B, 0x59, 0x3E, 0xCA, + 0x73, 0xC8, 0x86, 0x47, 0xDB, 0xAB, 0x6F, 0x8C }; + +unsigned char table_38[256] = { + 0xAA, 0x8D, 0x37, 0x94, 0x99, 0xDD, 0x70, 0x77, + 0x78, 0xC9, 0x0F, 0xFA, 0xE2, 0x05, 0xC2, 0x16, + 0x02, 0x4D, 0x44, 0x65, 0xAC, 0xB0, 0x39, 0xF8, + 0x06, 0x60, 0xD8, 0xE1, 0x19, 0xB4, 0x36, 0x20, + 0x59, 0x1D, 0xAD, 0xE4, 0xE8, 0xFF, 0x9D, 0x0D, + 0x51, 0x28, 0xE7, 0x8C, 0x0E, 0x97, 0xE3, 0xAE, + 0x6A, 0x27, 0x98, 0xDB, 0x26, 0xF6, 0xEC, 0xC6, + 0xC0, 0xBD, 0x68, 0x61, 0x83, 0x86, 0xE0, 0x2C, + 0xEE, 0x47, 0xF9, 0x5F, 0x6D, 0xBA, 0xE9, 0x72, + 0x8A, 0xBB, 0x08, 0x29, 0xAF, 0x1C, 0xD3, 0x5D, + 0xF7, 0x87, 0x6F, 0x9A, 0x2F, 0x11, 0xD9, 0x90, + 0x66, 0x8E, 0xEB, 0xB1, 0x2E, 0xEA, 0xA3, 0x55, + 0x2B, 0xCC, 0x4C, 0x4B, 0x48, 0x71, 0x3B, 0xFC, + 0xA4, 0x45, 0x0A, 0x8F, 0x7A, 0x13, 0x01, 0x22, + 0xC1, 0xF1, 0xA2, 0xB8, 0x7C, 0xF4, 0xB3, 0xB7, + 0x5B, 0xE5, 0x07, 0x50, 0x7E, 0x18, 0xEF, 0x91, + 0x5C, 0x15, 0x69, 0xBE, 0x0C, 0x93, 0x56, 0x35, + 0x7B, 0xCF, 0x34, 0x74, 0x3E, 0x5E, 0x31, 0x21, + 0x12, 0x63, 0x7F, 0x2A, 0x9B, 0xD4, 0x6B, 0xBC, + 0x33, 0x62, 0x30, 0x75, 0x17, 0x23, 0xB2, 0xF0, + 0x57, 0x67, 0x95, 0x3D, 0xCD, 0x10, 0xE6, 0xC8, + 0x8B, 0xA9, 0x73, 0xC4, 0x43, 0xBF, 0xA7, 0xCA, + 0xB5, 0xD5, 0xD6, 0x3F, 0x1A, 0x7D, 0x82, 0xA8, + 0x40, 0x64, 0xAB, 0x04, 0xC3, 0x1F, 0xA0, 0x5A, + 0x85, 0xF3, 0xDE, 0xFE, 0xDA, 0x1E, 0x81, 0x92, + 0x9C, 0x2D, 0x9F, 0x32, 0xB9, 0xA1, 0x96, 0xD0, + 0x4F, 0x38, 0x80, 0xCB, 0x6C, 0x14, 0x84, 0x1B, + 0xD7, 0xC5, 0xED, 0xD2, 0x3A, 0x0B, 0x88, 0xFD, + 0xDC, 0x49, 0x9E, 0xF5, 0xF2, 0x52, 0xA6, 0x24, + 0xC7, 0xB6, 0x03, 0x3C, 0xD1, 0x54, 0x41, 0xDF, + 0x89, 0x58, 0x79, 0xFB, 0x6E, 0xA5, 0x42, 0x25, + 0x09, 0x76, 0x00, 0x46, 0x4E, 0x53, 0xCE, 0x4A }; + +unsigned char table_39[32] = { + 0x12, 0x18, 0x0E, 0x08, 0x16, 0x05, 0x06, 0x00, + 0x11, 0x17, 0x15, 0x1B, 0x14, 0x01, 0x1F, 0x19, + 0x04, 0x0D, 0x0A, 0x0F, 0x10, 0x07, 0x1D, 0x03, + 0x0B, 0x13, 0x0C, 0x09, 0x1E, 0x02, 0x1A, 0x1C }; + +unsigned char table_40[32] = { + 0x16, 0x02, 0x06, 0x0E, 0x0D, 0x1C, 0x08, 0x0A, + 0x0F, 0x13, 0x0B, 0x18, 0x07, 0x04, 0x14, 0x01, + 0x1B, 0x05, 0x17, 0x1E, 0x11, 0x1A, 0x10, 0x1F, + 0x12, 0x19, 0x1D, 0x03, 0x0C, 0x00, 0x09, 0x15 }; + +unsigned char table_41[32] = { + 0x13, 0x18, 0x04, 0x1F, 0x1D, 0x11, 0x03, 0x00, + 0x10, 0x12, 0x06, 0x0A, 0x1C, 0x07, 0x15, 0x0E, + 0x08, 0x05, 0x0C, 0x09, 0x01, 0x02, 0x16, 0x0B, + 0x1A, 0x17, 0x14, 0x1E, 0x0D, 0x0F, 0x19, 0x1B }; + +unsigned char table_42[32] = { + 0x00, 0x08, 0x15, 0x1D, 0x05, 0x18, 0x06, 0x07, + 0x1F, 0x01, 0x0B, 0x03, 0x19, 0x13, 0x02, 0x1C, + 0x17, 0x11, 0x0E, 0x1E, 0x0C, 0x0F, 0x09, 0x1A, + 0x1B, 0x16, 0x10, 0x0D, 0x0A, 0x14, 0x12, 0x04 }; + +unsigned char table_43[256] = { + 0x34, 0xB7, 0x36, 0x85, 0x5F, 0x93, 0x98, 0x70, + 0x1E, 0x59, 0x83, 0x60, 0x6F, 0xBF, 0xF9, 0xD0, + 0xB3, 0x22, 0x12, 0x38, 0xF5, 0x01, 0xC9, 0x5B, + 0xEF, 0x1D, 0x81, 0x64, 0xFA, 0x8F, 0x7F, 0xBC, + 0x05, 0x08, 0xE0, 0x8B, 0xE8, 0x86, 0x95, 0xCB, + 0xCA, 0x5A, 0xEB, 0x10, 0x92, 0xE2, 0x7E, 0x28, + 0xD9, 0xC7, 0x0D, 0x24, 0xA7, 0x02, 0x0B, 0xF1, + 0x7B, 0xD3, 0xFE, 0x2B, 0x89, 0x0E, 0xAE, 0xAD, + 0xC8, 0x82, 0x79, 0x43, 0x96, 0xDE, 0x0C, 0x9A, + 0x57, 0x84, 0xB4, 0x19, 0xF8, 0xF0, 0xAF, 0xBE, + 0x99, 0x9F, 0x46, 0xE4, 0x31, 0xDF, 0x30, 0x51, + 0xD4, 0xE5, 0xFC, 0x32, 0x04, 0x56, 0x7D, 0x33, + 0xF7, 0x18, 0x23, 0x4E, 0xC2, 0x7C, 0x6C, 0xD2, + 0xB1, 0x9B, 0x40, 0xA2, 0x88, 0x00, 0xA1, 0xAB, + 0xC6, 0x5C, 0x87, 0x3B, 0xD7, 0x27, 0x2E, 0x45, + 0xDA, 0x8E, 0x61, 0x5E, 0xFB, 0x09, 0x5D, 0x6B, + 0xA3, 0x29, 0x4F, 0xAC, 0xD1, 0x77, 0x4A, 0xA9, + 0xC4, 0x7A, 0x15, 0xD8, 0xAA, 0x17, 0xB9, 0x2D, + 0xE7, 0xBD, 0x2C, 0x62, 0x2F, 0xB2, 0xED, 0x3F, + 0x48, 0x26, 0x1B, 0x35, 0x20, 0x72, 0x4D, 0xFF, + 0xBB, 0x78, 0x1F, 0xCC, 0xEC, 0xA8, 0x9D, 0x90, + 0x4B, 0x13, 0xE1, 0xBA, 0xF3, 0x3C, 0x42, 0x65, + 0x14, 0xDD, 0x75, 0xE3, 0x4C, 0x74, 0x94, 0xCD, + 0xF2, 0x66, 0x06, 0xE9, 0x49, 0xB8, 0x71, 0x41, + 0xA0, 0x25, 0x55, 0x47, 0x97, 0x9E, 0x11, 0x54, + 0x1A, 0xB0, 0x3E, 0x37, 0x39, 0x1C, 0x8D, 0x03, + 0x6E, 0xF6, 0x80, 0x6D, 0x8C, 0x9C, 0xB6, 0xCF, + 0xC3, 0x91, 0x63, 0xC0, 0x07, 0x67, 0xE6, 0xF4, + 0xCE, 0x3D, 0xDB, 0x16, 0xFD, 0xEA, 0xD6, 0x68, + 0xD5, 0xA6, 0x0F, 0x58, 0x44, 0x52, 0xB5, 0xDC, + 0x0A, 0x69, 0xC5, 0xA5, 0xC1, 0x8A, 0x2A, 0xEE, + 0x73, 0x76, 0x3A, 0x21, 0x53, 0xA4, 0x50, 0x6A }; + +unsigned char table_44[32] = { + 0x1A, 0x0E, 0x0A, 0x17, 0x1F, 0x08, 0x10, 0x14, + 0x0C, 0x0F, 0x09, 0x1C, 0x06, 0x18, 0x1E, 0x12, + 0x15, 0x00, 0x11, 0x13, 0x0D, 0x01, 0x0B, 0x03, + 0x16, 0x19, 0x05, 0x1D, 0x02, 0x07, 0x04, 0x1B }; + +unsigned char table_45[256] = { + 0x5E, 0xD6, 0xE2, 0x54, 0x35, 0xC2, 0xAC, 0x9D, + 0x92, 0x64, 0x57, 0x65, 0xC8, 0xAE, 0x21, 0xA9, + 0x89, 0x48, 0x12, 0x59, 0xEC, 0xEF, 0x9F, 0xF7, + 0x19, 0x03, 0x83, 0xC0, 0x79, 0x5D, 0x4A, 0x10, + 0x8C, 0xEB, 0xFF, 0xB5, 0x3B, 0x51, 0x2D, 0xD1, + 0x6B, 0xC5, 0x24, 0x5C, 0xE6, 0x11, 0x94, 0x3F, + 0xD0, 0x2F, 0x0E, 0x95, 0x3C, 0xFE, 0x5B, 0x20, + 0x23, 0xE0, 0x91, 0x6F, 0xCA, 0x56, 0x0C, 0x73, + 0xDA, 0x67, 0x37, 0xA3, 0xA5, 0x70, 0x93, 0x1C, + 0x18, 0xD9, 0x42, 0x5F, 0x44, 0xF0, 0xF2, 0x14, + 0x58, 0x8A, 0x1D, 0x40, 0x4E, 0x0B, 0x74, 0x84, + 0x52, 0xCB, 0x60, 0xED, 0xAD, 0x66, 0x43, 0x6C, + 0x81, 0xA1, 0x27, 0xB9, 0xBA, 0x4D, 0xF5, 0x04, + 0xB8, 0x96, 0xA6, 0xA2, 0x7D, 0xD4, 0xEA, 0x45, + 0x4F, 0x55, 0xD3, 0x3E, 0x8E, 0x4C, 0xBF, 0x8B, + 0x9A, 0x06, 0x7A, 0xF4, 0x02, 0x88, 0x80, 0x22, + 0xF3, 0xBD, 0x78, 0xEE, 0xAF, 0xF8, 0x15, 0x09, + 0x0F, 0xB0, 0xDD, 0x99, 0x72, 0xE7, 0x90, 0xE1, + 0x25, 0x62, 0x8D, 0x9C, 0x13, 0x08, 0xC9, 0x28, + 0x2A, 0x47, 0x69, 0xDE, 0x77, 0x87, 0xBB, 0xE9, + 0xAA, 0x33, 0x05, 0x29, 0x34, 0x97, 0xFD, 0xA0, + 0x1E, 0xFC, 0xBE, 0xB1, 0x71, 0x9B, 0x50, 0xDC, + 0xB7, 0x31, 0x63, 0x3A, 0xDF, 0xC3, 0x1B, 0x7C, + 0x0A, 0xD7, 0xF6, 0xDB, 0x49, 0x53, 0x7F, 0xD2, + 0x30, 0xA4, 0xB3, 0x6E, 0xB2, 0x6D, 0xCD, 0x7E, + 0x26, 0xE8, 0x76, 0xCF, 0xE5, 0xCE, 0x16, 0xF1, + 0xC6, 0x68, 0x36, 0x46, 0x1F, 0x38, 0x0D, 0x41, + 0x17, 0xBC, 0x86, 0x9E, 0x6A, 0x7B, 0xB4, 0x01, + 0xCC, 0x2C, 0xE3, 0x5A, 0xB6, 0xFA, 0x00, 0x75, + 0x39, 0xA7, 0xC1, 0xD5, 0x98, 0xAB, 0x1A, 0x85, + 0xD8, 0xE4, 0xC4, 0xA8, 0x4B, 0x61, 0x2E, 0x3D, + 0xF9, 0x2B, 0x32, 0x8F, 0xFB, 0xC7, 0x07, 0x82 }; + +unsigned char table_46[256] = { + 0x85, 0x78, 0xFE, 0x6C, 0x61, 0xA0, 0x71, 0xCC, + 0x45, 0x54, 0x7A, 0xE6, 0x82, 0x1D, 0xA6, 0x02, + 0x47, 0xD0, 0x23, 0x55, 0x62, 0xFA, 0x76, 0x3E, + 0xE3, 0x66, 0x74, 0x10, 0x5D, 0x49, 0x69, 0x0B, + 0x75, 0x12, 0x8D, 0x9F, 0xEE, 0x93, 0x50, 0x70, + 0x32, 0xBC, 0x1E, 0xD3, 0xEF, 0x7B, 0xB4, 0x92, + 0xFD, 0x16, 0xC2, 0xD8, 0xDE, 0x68, 0xD1, 0x64, + 0xC3, 0xA3, 0xB3, 0xC9, 0x08, 0xFB, 0x84, 0xC1, + 0x28, 0x53, 0xCF, 0xD2, 0x35, 0xD7, 0x4A, 0x01, + 0x44, 0xA4, 0x07, 0xAC, 0x98, 0xF1, 0xB2, 0x9A, + 0x94, 0x2D, 0xD4, 0x34, 0x27, 0x60, 0x1A, 0xB9, + 0xAF, 0x89, 0xEB, 0x8F, 0x6A, 0x13, 0x05, 0xF0, + 0x77, 0x5F, 0x4F, 0x58, 0x2C, 0xE7, 0xCE, 0xED, + 0xC0, 0x0D, 0x3A, 0xA7, 0xE2, 0x38, 0x5B, 0xE9, + 0x3D, 0xF2, 0xDF, 0x86, 0xE0, 0x72, 0xF7, 0x88, + 0xAD, 0xB7, 0x11, 0xDB, 0x73, 0x87, 0xC5, 0x22, + 0xE1, 0x5C, 0xD6, 0x57, 0x7E, 0x7D, 0xA2, 0xF9, + 0xF5, 0x9C, 0x25, 0x6F, 0x26, 0x51, 0xC8, 0x80, + 0x2B, 0xA8, 0x19, 0xD9, 0x65, 0xCD, 0x97, 0xEA, + 0xFF, 0x5E, 0x24, 0x3B, 0x4D, 0xB1, 0x1C, 0x79, + 0x39, 0x6B, 0xA5, 0x2A, 0x09, 0xCA, 0x04, 0xEC, + 0xBA, 0x18, 0x31, 0x46, 0x20, 0xBE, 0x1F, 0x3C, + 0x6D, 0xAA, 0xF6, 0xDD, 0xF4, 0x96, 0x03, 0x0A, + 0x9E, 0x83, 0xA1, 0x9D, 0xD5, 0xB0, 0x17, 0xBF, + 0x56, 0xAB, 0xAE, 0x1B, 0x52, 0xC6, 0x81, 0x4B, + 0xDC, 0x90, 0x5A, 0x9B, 0xB6, 0x0F, 0xF3, 0x67, + 0x30, 0x63, 0x7C, 0x40, 0x0E, 0x7F, 0x95, 0x36, + 0xC4, 0x4E, 0x43, 0xCB, 0x15, 0xB8, 0x00, 0x91, + 0x8A, 0x4C, 0x8E, 0x14, 0x06, 0x6E, 0xA9, 0x2E, + 0x3F, 0x48, 0x2F, 0x0C, 0xB5, 0x21, 0xBB, 0xDA, + 0x8B, 0x42, 0x29, 0x8C, 0x33, 0x59, 0xE8, 0xF8, + 0xC7, 0xE4, 0x37, 0xE5, 0xFC, 0xBD, 0x99, 0x41 }; + +unsigned char table_47[32] = { + 0x18, 0x1D, 0x16, 0x10, 0x11, 0x04, 0x1E, 0x08, + 0x19, 0x0E, 0x0F, 0x02, 0x14, 0x1C, 0x07, 0x17, + 0x0D, 0x09, 0x12, 0x1A, 0x05, 0x01, 0x0B, 0x0A, + 0x13, 0x15, 0x0C, 0x00, 0x06, 0x1F, 0x03, 0x1B }; + +unsigned char table_48[32] = { + 0x13, 0x08, 0x15, 0x01, 0x17, 0x10, 0x0F, 0x1F, + 0x1D, 0x0D, 0x12, 0x03, 0x06, 0x0A, 0x1C, 0x19, + 0x1A, 0x04, 0x1B, 0x02, 0x16, 0x1E, 0x11, 0x00, + 0x14, 0x09, 0x0C, 0x18, 0x05, 0x07, 0x0E, 0x0B }; + +unsigned char table_49[32] = { + 0x1F, 0x0F, 0x19, 0x07, 0x18, 0x05, 0x1E, 0x1D, + 0x15, 0x08, 0x17, 0x10, 0x0A, 0x0E, 0x0C, 0x1B, + 0x02, 0x13, 0x03, 0x0D, 0x04, 0x1A, 0x06, 0x09, + 0x12, 0x1C, 0x0B, 0x16, 0x14, 0x01, 0x11, 0x00 }; + +unsigned char table_50[32] = { + 0x16, 0x18, 0x1C, 0x0E, 0x12, 0x00, 0x04, 0x1B, + 0x1F, 0x13, 0x17, 0x0A, 0x1E, 0x03, 0x0C, 0x01, + 0x0F, 0x10, 0x02, 0x08, 0x14, 0x09, 0x19, 0x15, + 0x06, 0x0D, 0x0B, 0x1D, 0x05, 0x07, 0x11, 0x1A }; + +unsigned char table_51[32] = { + 0x1C, 0x0D, 0x1B, 0x07, 0x17, 0x0E, 0x06, 0x01, + 0x12, 0x19, 0x03, 0x0B, 0x10, 0x08, 0x00, 0x1E, + 0x0A, 0x04, 0x1A, 0x1D, 0x0C, 0x18, 0x02, 0x13, + 0x0F, 0x11, 0x05, 0x09, 0x15, 0x16, 0x1F, 0x14 }; + +unsigned char table_52[256] = { + 0x34, 0x0B, 0x47, 0xA3, 0x56, 0x30, 0x73, 0xD4, + 0x4B, 0xF6, 0xA6, 0x80, 0x22, 0x95, 0xA5, 0xBB, + 0xFE, 0xCD, 0x27, 0x88, 0x87, 0x18, 0x86, 0x6E, + 0xB9, 0x07, 0x37, 0x52, 0x0A, 0x28, 0x2C, 0xC4, + 0x75, 0xA1, 0x29, 0x54, 0x84, 0x08, 0x72, 0x51, + 0xDD, 0xF1, 0x4E, 0x1A, 0x90, 0x57, 0x20, 0xAD, + 0x68, 0x61, 0xAF, 0x50, 0x6B, 0x1B, 0x71, 0xEB, + 0x63, 0xC9, 0xB0, 0x58, 0x26, 0x40, 0xC7, 0xD9, + 0x70, 0xA2, 0x9A, 0x09, 0x3F, 0x92, 0x0D, 0x8C, + 0xC1, 0x96, 0x9F, 0x77, 0x4D, 0x5A, 0xEA, 0x11, + 0xD7, 0xF3, 0x33, 0x93, 0x10, 0xF2, 0x9D, 0x83, + 0xFF, 0x7E, 0xD2, 0x41, 0x24, 0xB4, 0x8D, 0x5C, + 0xCF, 0xEF, 0xE9, 0x64, 0x76, 0xD1, 0xDE, 0xE4, + 0x91, 0x35, 0x89, 0x19, 0x02, 0x0E, 0xF4, 0x2A, + 0x0F, 0xE1, 0xA8, 0x2D, 0x21, 0x23, 0xAA, 0x7C, + 0x78, 0x45, 0xA9, 0xDC, 0x06, 0xF9, 0xDF, 0xF7, + 0x03, 0xAB, 0xB5, 0x1C, 0x36, 0x7B, 0x97, 0xFA, + 0xE5, 0x3B, 0x2F, 0x1F, 0x9E, 0xED, 0xA7, 0x55, + 0x42, 0x6F, 0x1E, 0xB7, 0xE6, 0xFB, 0x12, 0xD5, + 0x99, 0xC6, 0x66, 0x4A, 0xE8, 0x48, 0x60, 0xB1, + 0x05, 0x53, 0x8A, 0xB6, 0x25, 0x8F, 0xA4, 0xD8, + 0x9C, 0xC0, 0x59, 0x3A, 0xBD, 0xDB, 0x44, 0x5E, + 0xE3, 0xDA, 0x1D, 0x32, 0xF5, 0xBA, 0x43, 0x13, + 0x82, 0x4C, 0xE7, 0x17, 0x15, 0x3E, 0x69, 0x2E, + 0xC3, 0xF0, 0x5F, 0xFD, 0xCE, 0xD3, 0xCA, 0x39, + 0xD6, 0x79, 0x3D, 0xC8, 0x67, 0x8B, 0x31, 0x4F, + 0xB3, 0xBC, 0x65, 0x00, 0x7A, 0x98, 0xC5, 0x6C, + 0x2B, 0x94, 0x6D, 0x74, 0x14, 0xAC, 0xCC, 0xA0, + 0x5B, 0xF8, 0xCB, 0x7F, 0xB2, 0xEC, 0xBF, 0x3C, + 0xE0, 0xAE, 0xFC, 0x62, 0x04, 0x8E, 0x85, 0x49, + 0x9B, 0xC2, 0x38, 0xD0, 0xEE, 0x81, 0x46, 0xE2, + 0x01, 0x0C, 0x5D, 0x7D, 0xB8, 0xBE, 0x6A, 0x16 }; + +unsigned char table_53[256] = { + 0xE3, 0xF4, 0x8D, 0x72, 0x45, 0x32, 0x9D, 0xCE, + 0x1F, 0x6B, 0xBC, 0xDC, 0xF1, 0xEC, 0x5A, 0x3B, + 0xA5, 0xA2, 0x2B, 0xDD, 0x8A, 0xA3, 0x76, 0xE4, + 0xAF, 0xE9, 0xE1, 0x21, 0xDB, 0x9F, 0x19, 0xD3, + 0x26, 0x80, 0x15, 0xC2, 0x46, 0xB8, 0x17, 0x56, + 0x99, 0x81, 0x08, 0xD7, 0xEF, 0x8E, 0x04, 0x05, + 0x97, 0x2F, 0x78, 0xAD, 0xA1, 0x52, 0x36, 0x58, + 0x53, 0x68, 0x22, 0x70, 0x0B, 0x79, 0xE6, 0xFA, + 0xC3, 0x91, 0xE2, 0xF7, 0xF6, 0x75, 0x2D, 0x0A, + 0x90, 0xEB, 0xA6, 0x35, 0xA7, 0x10, 0xB5, 0xFB, + 0xE7, 0xAA, 0x1E, 0x43, 0xBB, 0x3C, 0x65, 0x25, + 0x2C, 0x59, 0x62, 0x2A, 0xF9, 0x4B, 0x95, 0x5E, + 0x20, 0x11, 0x42, 0x27, 0x44, 0xE8, 0x14, 0x6F, + 0xD1, 0xD8, 0x00, 0x3A, 0x5B, 0x18, 0x89, 0x02, + 0x61, 0xD6, 0xC5, 0x98, 0xD0, 0x5F, 0x34, 0x29, + 0xFD, 0x31, 0x1A, 0xCD, 0x0F, 0x9E, 0xCA, 0x7B, + 0xEA, 0x93, 0x71, 0x5C, 0x0E, 0x57, 0x33, 0xC4, + 0x37, 0xF5, 0x83, 0xB0, 0xDF, 0x49, 0x74, 0x54, + 0x1D, 0x24, 0xB9, 0x16, 0x1C, 0x28, 0xDE, 0x4A, + 0xF0, 0x01, 0x86, 0x82, 0xCC, 0x12, 0x8C, 0x06, + 0x30, 0xA8, 0x7A, 0x73, 0x66, 0x7C, 0xC6, 0xB6, + 0xF2, 0x13, 0xBF, 0x40, 0x85, 0x77, 0x09, 0x3D, + 0x67, 0x63, 0x3F, 0x7F, 0xF3, 0x87, 0x8F, 0xFF, + 0x92, 0xC7, 0x4C, 0x23, 0xBA, 0xCB, 0xB1, 0xED, + 0x0C, 0x60, 0x47, 0xFE, 0x38, 0x5D, 0xCF, 0x8B, + 0x4D, 0xA9, 0x2E, 0xE5, 0xA4, 0x1B, 0x88, 0x3E, + 0x7D, 0xF8, 0xC0, 0xD5, 0x6D, 0x6C, 0x48, 0xAC, + 0x9B, 0x51, 0x7E, 0x6E, 0x50, 0x0D, 0x9A, 0xB3, + 0xEE, 0x07, 0x4F, 0x69, 0x9C, 0x03, 0xD9, 0xD4, + 0xB4, 0xD2, 0xAE, 0x4E, 0x55, 0xB7, 0xC9, 0x41, + 0x39, 0x6A, 0xC8, 0xA0, 0xB2, 0xC1, 0x84, 0xFC, + 0xAB, 0x64, 0xE0, 0xBE, 0xDA, 0xBD, 0x96, 0x94 }; + +unsigned char table_54[32] = { + 0x01, 0x02, 0x1D, 0x10, 0x0E, 0x11, 0x08, 0x14, + 0x12, 0x09, 0x15, 0x17, 0x16, 0x04, 0x06, 0x1B, + 0x07, 0x1A, 0x18, 0x13, 0x0A, 0x1E, 0x1C, 0x1F, + 0x0C, 0x0B, 0x0D, 0x05, 0x0F, 0x00, 0x19, 0x03 }; + +unsigned char table_55[32] = { + 0x01, 0x12, 0x13, 0x09, 0x0B, 0x19, 0x03, 0x0E, + 0x02, 0x1F, 0x1D, 0x1B, 0x1E, 0x11, 0x06, 0x05, + 0x00, 0x16, 0x07, 0x0C, 0x15, 0x0D, 0x1A, 0x08, + 0x18, 0x10, 0x0F, 0x17, 0x1C, 0x0A, 0x04, 0x14 }; + +unsigned char table_56[256] = { + 0xEF, 0x06, 0x5F, 0x11, 0x4B, 0x60, 0x13, 0xBB, + 0x79, 0xD7, 0xE4, 0x6D, 0x22, 0xB4, 0x15, 0x50, + 0x29, 0x17, 0xD2, 0xE3, 0x37, 0x8C, 0x46, 0x7C, + 0xA2, 0xF5, 0x65, 0x16, 0xCB, 0x04, 0x3E, 0xDF, + 0x8E, 0xDE, 0x53, 0xF1, 0xF4, 0xD1, 0x3B, 0xEE, + 0x9A, 0x09, 0x9B, 0x6C, 0xF6, 0xCC, 0xFB, 0x40, + 0xE0, 0xFD, 0x2B, 0x1D, 0x73, 0x18, 0xCD, 0x31, + 0x3F, 0x9E, 0xAD, 0xC9, 0x43, 0x4E, 0x99, 0x3A, + 0x8F, 0x92, 0x85, 0xFC, 0x12, 0x41, 0x20, 0xE8, + 0x2A, 0xC0, 0x1C, 0x38, 0x74, 0x0B, 0xF3, 0x05, + 0x0D, 0x1F, 0x94, 0x9C, 0xAC, 0x00, 0x59, 0x0C, + 0xB3, 0x8D, 0xA8, 0x75, 0xB7, 0x68, 0x2F, 0x27, + 0x6F, 0x69, 0x76, 0xD8, 0xEC, 0xA5, 0xB2, 0x6A, + 0x19, 0x72, 0x1A, 0xB6, 0xE5, 0x77, 0xC6, 0x44, + 0x9D, 0xCA, 0x82, 0x35, 0x36, 0x5E, 0xA9, 0x25, + 0xFA, 0x5C, 0x24, 0x30, 0x39, 0x0E, 0x2C, 0x7D, + 0xE6, 0x88, 0xA0, 0x63, 0xB8, 0x6B, 0x01, 0xDD, + 0xDA, 0x9F, 0x45, 0x83, 0xE2, 0x7F, 0x1B, 0x56, + 0xAF, 0x14, 0xC3, 0x49, 0xBF, 0x78, 0x70, 0x58, + 0x23, 0xA3, 0xBD, 0x34, 0x47, 0x2D, 0x0A, 0xD4, + 0x33, 0x03, 0x1E, 0xC1, 0x87, 0xAE, 0x3C, 0x95, + 0xB0, 0x42, 0x91, 0xB9, 0x5A, 0x61, 0xAA, 0xCF, + 0xF2, 0x51, 0xA6, 0xF8, 0xDC, 0x71, 0xAB, 0x48, + 0x66, 0x90, 0x97, 0xC4, 0x08, 0xF9, 0xD0, 0x7B, + 0xDB, 0xBA, 0x8B, 0xC2, 0xC5, 0x2E, 0xF7, 0x5B, + 0xFF, 0x21, 0x81, 0x54, 0xD3, 0x62, 0x57, 0x4C, + 0x6E, 0x02, 0x98, 0xFE, 0x7E, 0xE7, 0xBC, 0x07, + 0x28, 0x5D, 0x86, 0xCE, 0xEA, 0x84, 0xF0, 0xE1, + 0x93, 0x80, 0xE9, 0xC7, 0x4A, 0xED, 0xB1, 0x26, + 0x89, 0x3D, 0x4F, 0xA7, 0xA1, 0xD6, 0xB5, 0x4D, + 0x67, 0xA4, 0x55, 0x10, 0x0F, 0xD9, 0x52, 0x32, + 0x96, 0xD5, 0xEB, 0x64, 0x8A, 0xC8, 0x7A, 0xBE }; + +unsigned char table_57[256] = { + 0xD1, 0x9B, 0x15, 0x06, 0xB4, 0xF6, 0x97, 0xF0, + 0xC6, 0x5B, 0x88, 0x12, 0x25, 0xFA, 0x7B, 0x79, + 0xD6, 0xAB, 0xDC, 0x47, 0x85, 0x61, 0x67, 0x0B, + 0xF3, 0x20, 0x44, 0x53, 0x2A, 0x3B, 0x2D, 0xE8, + 0x17, 0x71, 0xC3, 0xB7, 0x7F, 0x35, 0xEB, 0x10, + 0x03, 0x0D, 0x60, 0x96, 0x27, 0xBB, 0x39, 0x50, + 0x95, 0x55, 0xCC, 0xD4, 0x2F, 0x51, 0xB3, 0x05, + 0xA5, 0xAD, 0xBC, 0x18, 0xE2, 0xAE, 0x07, 0x87, + 0xC4, 0x8D, 0xBE, 0x77, 0xC2, 0x16, 0xFC, 0x33, + 0x4C, 0x4F, 0xE6, 0xA6, 0x57, 0x9F, 0x37, 0x91, + 0xED, 0x4A, 0xF7, 0xB5, 0x52, 0x7C, 0xBD, 0x30, + 0xA0, 0x2C, 0x8C, 0xB0, 0x0C, 0xDA, 0x6F, 0x9E, + 0xEE, 0x43, 0x40, 0x8F, 0x8B, 0x76, 0xA4, 0x68, + 0xFF, 0x6D, 0x58, 0xC9, 0xF9, 0x6E, 0x3F, 0x56, + 0xCA, 0x49, 0xC8, 0x5D, 0xCD, 0xC7, 0x99, 0xEC, + 0x72, 0x38, 0x0A, 0xA9, 0xC5, 0x04, 0x64, 0xBF, + 0xB6, 0x29, 0x80, 0x2E, 0x19, 0x0E, 0x82, 0x45, + 0xBA, 0xD7, 0x1E, 0x86, 0xA8, 0xD8, 0x24, 0xDB, + 0xCF, 0xE1, 0x54, 0xB2, 0x3E, 0x4D, 0x90, 0x42, + 0x5F, 0x59, 0x0F, 0xCE, 0x8E, 0xA2, 0xA7, 0x1D, + 0x22, 0xFD, 0x81, 0x63, 0xE5, 0x6A, 0xE7, 0x93, + 0x41, 0x46, 0x66, 0x89, 0x13, 0xEA, 0x69, 0x1C, + 0x83, 0xF2, 0x08, 0xB8, 0x01, 0x23, 0x26, 0xFB, + 0x78, 0xAA, 0x31, 0x11, 0x1B, 0x98, 0xDD, 0xAC, + 0xB9, 0xFE, 0x94, 0x74, 0xAF, 0x32, 0xD0, 0x5A, + 0xA1, 0xF4, 0x6B, 0x8A, 0xE3, 0x65, 0xDE, 0xCB, + 0x73, 0x3D, 0xA3, 0x7E, 0xDF, 0xD2, 0x6C, 0x7A, + 0x36, 0xD9, 0x62, 0x4B, 0xEF, 0xC1, 0x1F, 0x00, + 0x34, 0xB1, 0xF8, 0xE4, 0xD5, 0x09, 0x1A, 0x9A, + 0x70, 0x48, 0x9D, 0xF1, 0xE0, 0x9C, 0xD3, 0x5C, + 0x75, 0x02, 0x2B, 0x92, 0x21, 0x7D, 0xF5, 0x5E, + 0x4E, 0x3C, 0x84, 0x14, 0x28, 0x3A, 0xE9, 0xC0 }; + +unsigned char table_58[256] = { + 0xE9, 0x81, 0x60, 0xA7, 0x18, 0xA0, 0x0F, 0x55, + 0x2B, 0x52, 0xE0, 0x8B, 0x9D, 0x85, 0xD2, 0xA3, + 0x3F, 0x6E, 0xB1, 0xAF, 0xE3, 0x36, 0xE2, 0x19, + 0x56, 0xB0, 0x09, 0xB5, 0x79, 0x43, 0xE1, 0x06, + 0x45, 0xB6, 0xC0, 0x22, 0xEE, 0x41, 0xEC, 0x01, + 0x66, 0x2D, 0x87, 0x38, 0x16, 0x37, 0xFA, 0x29, + 0x96, 0xA4, 0xC3, 0x23, 0x59, 0x7E, 0x92, 0x78, + 0x10, 0x2A, 0x4C, 0x0E, 0x9B, 0x4A, 0x35, 0xF4, + 0x42, 0x0C, 0xD8, 0xD7, 0x24, 0x2C, 0xDD, 0x8E, + 0x5B, 0xF5, 0x33, 0x48, 0xEF, 0xDE, 0x4B, 0xBC, + 0x51, 0xAB, 0x7C, 0xE4, 0x63, 0x70, 0x9A, 0xAC, + 0x54, 0x1D, 0x25, 0xC5, 0xEA, 0xB3, 0x05, 0xF7, + 0xC1, 0x1F, 0xE8, 0x97, 0xBB, 0x32, 0x6D, 0xC7, + 0x28, 0x61, 0xDB, 0x4D, 0x77, 0x72, 0x65, 0x8C, + 0x80, 0x3A, 0x76, 0x47, 0xA8, 0x03, 0x04, 0x12, + 0xCE, 0xA9, 0x75, 0x3C, 0x49, 0xF8, 0x64, 0xDF, + 0x57, 0xA2, 0x69, 0x44, 0xAD, 0x3E, 0x4F, 0x0B, + 0x74, 0x67, 0xC9, 0x1A, 0x17, 0xAA, 0x02, 0x6F, + 0xDA, 0xF2, 0xC6, 0x27, 0x53, 0xD6, 0xFD, 0xCA, + 0x8D, 0x93, 0x89, 0xD5, 0x6B, 0x4E, 0x90, 0x82, + 0x30, 0xE7, 0xC4, 0xD9, 0x8A, 0x7F, 0xB4, 0xFC, + 0xCF, 0xA1, 0xAE, 0x1C, 0x39, 0x1B, 0x7B, 0x5E, + 0x88, 0x7D, 0xD3, 0x71, 0x2E, 0x98, 0x13, 0x8F, + 0xCC, 0x84, 0x73, 0xCD, 0x21, 0x0D, 0x5C, 0xA5, + 0x3D, 0x9E, 0x99, 0xC2, 0xF3, 0x34, 0x14, 0x62, + 0x46, 0x0A, 0x07, 0x08, 0xFF, 0xFB, 0xB7, 0xBF, + 0x5D, 0x91, 0xB8, 0x83, 0xBE, 0x94, 0xBA, 0xF9, + 0xEB, 0xE5, 0xCB, 0x95, 0x40, 0x31, 0xE6, 0x86, + 0xD4, 0xFE, 0xD0, 0x7A, 0x26, 0xB9, 0xDC, 0x2F, + 0xBD, 0xF0, 0x5F, 0x00, 0x9C, 0x6A, 0x5A, 0x3B, + 0xF1, 0xC8, 0x9F, 0xED, 0x50, 0x20, 0x15, 0x11, + 0x68, 0x1E, 0xF6, 0xA6, 0x6C, 0xB2, 0xD1, 0x58 }; + +unsigned char table_59[256] = { + 0x4C, 0x85, 0x2B, 0x14, 0xCC, 0x4D, 0x5F, 0xD7, + 0xCE, 0x28, 0xC5, 0x0B, 0xA1, 0x99, 0x08, 0xDE, + 0x42, 0xD1, 0x82, 0x5C, 0xC9, 0x8F, 0x72, 0x12, + 0xCB, 0x0D, 0x04, 0xFA, 0xCD, 0xE5, 0x9A, 0x6F, + 0xCF, 0x92, 0xB5, 0x88, 0x87, 0xBF, 0x90, 0x7C, + 0xAC, 0xBE, 0x36, 0x21, 0x7D, 0x7F, 0xC7, 0x9F, + 0x75, 0xBB, 0x61, 0x16, 0x17, 0x63, 0xAE, 0xC4, + 0x23, 0x89, 0xE0, 0x37, 0x91, 0x5E, 0xC8, 0xE4, + 0xFD, 0xD5, 0xA2, 0xC6, 0x5A, 0xEF, 0x9B, 0xD6, + 0x27, 0xEE, 0x60, 0x1C, 0xDF, 0xDA, 0xF1, 0xD2, + 0x1E, 0x01, 0x9D, 0x44, 0x03, 0xD8, 0x11, 0x53, + 0x4F, 0x6C, 0x8B, 0xB7, 0x40, 0xF2, 0x79, 0x20, + 0x74, 0x97, 0x3E, 0x3D, 0x05, 0xD4, 0x70, 0x30, + 0x54, 0x59, 0xE7, 0x15, 0xE1, 0xEB, 0x71, 0x83, + 0xFE, 0x66, 0xB1, 0xA6, 0xF7, 0x8E, 0x6A, 0xEA, + 0x65, 0x7E, 0xA3, 0xCA, 0x2D, 0x4B, 0xB8, 0x9C, + 0x35, 0xC3, 0xB6, 0x49, 0x32, 0x25, 0xB3, 0xB0, + 0x76, 0xC0, 0xF5, 0x00, 0x8A, 0xAF, 0x19, 0xDB, + 0xDD, 0x47, 0xDC, 0x07, 0xB2, 0x4A, 0x55, 0xE6, + 0x69, 0xEC, 0xED, 0x06, 0x94, 0xB9, 0xA7, 0x56, + 0x2C, 0xAA, 0xE3, 0x22, 0x3B, 0x98, 0x77, 0x52, + 0x3C, 0x64, 0xF8, 0x13, 0x78, 0xFC, 0xFB, 0xF3, + 0xD3, 0xF9, 0x29, 0x45, 0x51, 0x8C, 0xA0, 0x38, + 0xD9, 0xA5, 0x62, 0x3A, 0x6E, 0xD0, 0xE8, 0x7A, + 0x33, 0x1D, 0xB4, 0x73, 0x02, 0xFF, 0x10, 0x80, + 0x6B, 0xF0, 0xA4, 0xBA, 0xF6, 0xC2, 0x0E, 0xE2, + 0x81, 0x43, 0x84, 0x86, 0x1F, 0x31, 0x2F, 0xA9, + 0x1B, 0x2A, 0x4E, 0xF4, 0x95, 0x5B, 0x3F, 0x34, + 0x39, 0x7B, 0x0A, 0x26, 0x6D, 0x57, 0x50, 0x09, + 0x9E, 0xA8, 0xBC, 0x24, 0x93, 0x67, 0x41, 0x96, + 0x0C, 0x46, 0xBD, 0xE9, 0x68, 0x18, 0xAB, 0x2E, + 0x5D, 0x1A, 0x8D, 0xC1, 0x58, 0x48, 0xAD, 0x0F }; + +unsigned char table_60[32] = { + 0x1C, 0x06, 0x1E, 0x10, 0x1D, 0x05, 0x00, 0x0E, + 0x0C, 0x02, 0x11, 0x19, 0x15, 0x18, 0x16, 0x07, + 0x1F, 0x0B, 0x14, 0x01, 0x0F, 0x09, 0x0D, 0x13, + 0x03, 0x08, 0x12, 0x04, 0x1B, 0x0A, 0x17, 0x1A }; + +unsigned char table_61[256] = { + 0xC5, 0xA6, 0xF2, 0x6B, 0x4B, 0x58, 0xE0, 0x41, + 0xC6, 0x2F, 0x13, 0xFE, 0xC1, 0x34, 0x3F, 0x24, + 0x10, 0xBF, 0x8B, 0xC9, 0x26, 0x2E, 0x68, 0xBE, + 0x28, 0x54, 0x93, 0x11, 0x21, 0x03, 0xFF, 0x50, + 0x31, 0x71, 0x2C, 0x6C, 0x91, 0x8F, 0x3B, 0x40, + 0x3E, 0xE5, 0xA5, 0x80, 0xEA, 0x7C, 0x9D, 0x18, + 0x84, 0x5A, 0x73, 0x3A, 0x33, 0x43, 0xA1, 0x47, + 0xB1, 0xEE, 0xFB, 0x79, 0x5E, 0xAF, 0xB9, 0x48, + 0x0F, 0x88, 0x65, 0x67, 0x6F, 0xDB, 0x25, 0xE4, + 0xB0, 0x87, 0xD0, 0x46, 0xB5, 0xB7, 0x53, 0xD4, + 0x1E, 0x76, 0xB4, 0x90, 0xDD, 0xA3, 0xF7, 0x57, + 0xD2, 0xCC, 0x5D, 0xE3, 0xB3, 0xD8, 0x5F, 0x2B, + 0x69, 0x4A, 0x9B, 0x39, 0x1A, 0x8D, 0x05, 0x8A, + 0x44, 0x15, 0xAE, 0xF3, 0xA8, 0x92, 0x02, 0xAB, + 0xB8, 0xDA, 0x0A, 0x0C, 0xED, 0xD7, 0x77, 0x98, + 0x3D, 0x19, 0x95, 0x36, 0xE7, 0x7F, 0x66, 0xEF, + 0x86, 0xDC, 0xCB, 0x9C, 0x63, 0xE6, 0x1D, 0x14, + 0x9A, 0x22, 0xBD, 0xD6, 0x89, 0x2D, 0xD1, 0xF9, + 0xA2, 0xDE, 0xF5, 0x5C, 0x8E, 0x2A, 0x29, 0xCA, + 0x7A, 0x8C, 0x38, 0x9F, 0xBB, 0xDF, 0xEC, 0x30, + 0x00, 0xFC, 0xAC, 0x81, 0xB2, 0xE8, 0xC0, 0xA7, + 0x7B, 0x07, 0x52, 0x74, 0x70, 0x0E, 0x51, 0x6A, + 0x62, 0x0D, 0x85, 0x1B, 0x4F, 0x96, 0x55, 0x1C, + 0x32, 0x6E, 0x01, 0xF6, 0x08, 0xFD, 0x17, 0x35, + 0xF0, 0x16, 0xC8, 0x23, 0xE9, 0x59, 0x3C, 0x37, + 0x5B, 0x42, 0xD3, 0x49, 0x7D, 0x83, 0x78, 0xAD, + 0x94, 0x9E, 0x56, 0xB6, 0xF1, 0xC3, 0x75, 0xF8, + 0xFA, 0x09, 0x4C, 0xD9, 0x97, 0xF4, 0x7E, 0x6D, + 0xBC, 0x4D, 0x64, 0xCD, 0x12, 0x99, 0x45, 0xCE, + 0x61, 0x20, 0x0B, 0xA0, 0x82, 0xD5, 0xE1, 0x72, + 0xA9, 0x1F, 0x06, 0x27, 0xC7, 0x04, 0xE2, 0xBA, + 0xCF, 0x60, 0xAA, 0xA4, 0xEB, 0xC4, 0x4E, 0xC2 }; + +unsigned char table_62[256] = { + 0x01, 0x59, 0xEC, 0xFC, 0x51, 0xD2, 0xE4, 0x9D, + 0xAA, 0x61, 0xD5, 0xCA, 0x63, 0x5D, 0xCE, 0x36, + 0xB9, 0x49, 0x76, 0xA9, 0x14, 0x4C, 0x90, 0x28, + 0x66, 0x17, 0x4F, 0x1E, 0x1A, 0x47, 0x30, 0xE8, + 0xFD, 0x86, 0x2E, 0x7B, 0x7E, 0xCC, 0x34, 0x13, + 0x94, 0x45, 0x38, 0x74, 0x29, 0xB0, 0x37, 0xC3, + 0x26, 0x6C, 0x39, 0xA3, 0x89, 0xEB, 0xA2, 0x20, + 0x00, 0xE0, 0x73, 0xE7, 0xB5, 0xCB, 0xED, 0x3E, + 0x79, 0x09, 0xFA, 0x32, 0x54, 0xBA, 0x05, 0x96, + 0xDE, 0x23, 0xD0, 0xA1, 0xAB, 0xFE, 0xF2, 0x22, + 0xB2, 0x9B, 0x7D, 0x44, 0x12, 0x3D, 0x40, 0x82, + 0xA0, 0xA8, 0x33, 0xDC, 0xF7, 0xFB, 0xAC, 0x41, + 0x8A, 0x9C, 0x60, 0x11, 0xC8, 0xF0, 0xEA, 0x57, + 0x3A, 0x42, 0xCD, 0x1D, 0x3C, 0xC6, 0x97, 0x62, + 0x55, 0x9F, 0xF3, 0x93, 0x91, 0xDA, 0x6A, 0xE5, + 0x27, 0x8E, 0x4E, 0xFF, 0xA4, 0x80, 0x04, 0xE1, + 0x2B, 0x5E, 0xC0, 0x64, 0xC2, 0xD8, 0x46, 0x8C, + 0xD4, 0x0F, 0xC4, 0x43, 0xD9, 0x9E, 0x4B, 0x5C, + 0x0A, 0x8B, 0xBF, 0xD7, 0x7A, 0x81, 0x3B, 0x4A, + 0x58, 0xB6, 0x21, 0x1F, 0xC1, 0xBD, 0xB1, 0x77, + 0x72, 0x1C, 0x4D, 0xBC, 0xA5, 0x65, 0xC7, 0xF5, + 0xB4, 0x2D, 0x69, 0x71, 0xE6, 0x8F, 0xBB, 0x03, + 0xAF, 0xD6, 0x08, 0x75, 0xB7, 0x31, 0xF4, 0x2A, + 0x48, 0x70, 0x0C, 0x8D, 0xD1, 0x87, 0x2F, 0x16, + 0x5A, 0x5B, 0x98, 0xA6, 0xC5, 0x99, 0x50, 0x07, + 0xDD, 0x92, 0x25, 0x68, 0x0D, 0xBE, 0x78, 0x0B, + 0xAD, 0x84, 0x6B, 0x19, 0x52, 0x7C, 0xF6, 0xB3, + 0x56, 0x83, 0x88, 0xEE, 0x2C, 0x1B, 0x6E, 0x53, + 0x67, 0xE2, 0x6F, 0x15, 0x06, 0x10, 0x18, 0x85, + 0xF1, 0x6D, 0xF9, 0xC9, 0xAE, 0x3F, 0xB8, 0x95, + 0x35, 0xDF, 0xEF, 0xA7, 0x7F, 0x24, 0xF8, 0xE3, + 0xCF, 0xE9, 0xDB, 0xD3, 0x02, 0x9A, 0x0E, 0x5F }; + +unsigned char table_63[256] = { + 0x0C, 0x02, 0xEE, 0x94, 0x2D, 0x76, 0x96, 0x75, + 0x21, 0xDC, 0x37, 0x03, 0xC0, 0xF7, 0xDF, 0xEF, + 0xB1, 0x1D, 0xCF, 0x15, 0x5A, 0xB4, 0xCC, 0x81, + 0x89, 0x6B, 0xA5, 0x2E, 0x6D, 0xD4, 0x08, 0x44, + 0x2A, 0x60, 0x50, 0xBF, 0x40, 0x7D, 0x5F, 0x64, + 0x93, 0x70, 0xA4, 0x7F, 0xC9, 0xEB, 0x0A, 0xF8, + 0x9F, 0xA8, 0xBC, 0x25, 0xE5, 0xF3, 0x1B, 0xD7, + 0x29, 0x13, 0x0D, 0x69, 0x20, 0x5C, 0x0F, 0x91, + 0x4F, 0x62, 0x06, 0x26, 0x41, 0xED, 0xDA, 0x53, + 0x65, 0xFF, 0xCD, 0x3F, 0xF6, 0x01, 0xCE, 0xA2, + 0x04, 0xDE, 0x27, 0x87, 0xBA, 0x86, 0x24, 0x78, + 0xAF, 0xE1, 0x3D, 0xD0, 0xC8, 0x1F, 0x4A, 0x2C, + 0x9A, 0xF0, 0xCB, 0xAD, 0x0B, 0x59, 0xC5, 0x58, + 0xEA, 0x8A, 0xA1, 0x45, 0xB7, 0x5D, 0xB5, 0x77, + 0x2B, 0x47, 0x05, 0x00, 0xAC, 0x61, 0xFA, 0x33, + 0x74, 0x31, 0xCA, 0x22, 0x42, 0x8B, 0xFE, 0x09, + 0xB2, 0x6E, 0x1A, 0xBE, 0xAA, 0x7B, 0xEC, 0xF4, + 0x51, 0x66, 0x28, 0x12, 0xFC, 0x5E, 0x67, 0xF5, + 0xB9, 0x82, 0x90, 0x8E, 0x8D, 0x17, 0xE7, 0xE8, + 0xB0, 0xC3, 0x16, 0xA0, 0x4B, 0xB6, 0xFB, 0x7E, + 0xC4, 0x85, 0x4C, 0x1E, 0xC7, 0x39, 0x4E, 0xA9, + 0xE3, 0x4D, 0x32, 0x72, 0x35, 0x80, 0xE0, 0x34, + 0xB8, 0x73, 0x98, 0x49, 0x92, 0x30, 0xD5, 0xD2, + 0xA3, 0x54, 0x7A, 0x84, 0x8F, 0x6C, 0xFD, 0x43, + 0x3A, 0x36, 0x3B, 0xD9, 0x48, 0x6A, 0x14, 0x79, + 0xD1, 0x57, 0x88, 0xDB, 0xE4, 0x9B, 0xF9, 0x99, + 0x10, 0x71, 0xC1, 0x68, 0x9E, 0x11, 0xAB, 0xBD, + 0x7C, 0x3E, 0x3C, 0x18, 0x9D, 0x97, 0xF2, 0xE6, + 0xA6, 0xF1, 0x46, 0xC2, 0x19, 0xBB, 0x52, 0xD8, + 0x95, 0xD3, 0x23, 0xAE, 0x07, 0x2F, 0xE9, 0x63, + 0x1C, 0x55, 0x6F, 0x9C, 0x56, 0x38, 0xC6, 0x5B, + 0x8C, 0xE2, 0x83, 0xA7, 0xD6, 0x0E, 0xB3, 0xDD }; + +unsigned char table_64[32] = { + 0x03, 0x05, 0x0D, 0x09, 0x1A, 0x16, 0x08, 0x10, + 0x06, 0x1E, 0x1C, 0x15, 0x02, 0x04, 0x17, 0x0C, + 0x18, 0x0B, 0x19, 0x11, 0x1B, 0x14, 0x13, 0x0A, + 0x0E, 0x00, 0x1D, 0x1F, 0x01, 0x0F, 0x07, 0x12 }; + +unsigned char table_65[32] = { + 0x01, 0x0A, 0x1E, 0x14, 0x10, 0x1D, 0x0D, 0x17, + 0x0E, 0x0C, 0x0F, 0x12, 0x04, 0x1A, 0x05, 0x02, + 0x08, 0x1C, 0x09, 0x1F, 0x0B, 0x13, 0x19, 0x1B, + 0x11, 0x00, 0x16, 0x06, 0x03, 0x18, 0x15, 0x07 }; + +unsigned char table_66[32] = { + 0x1C, 0x18, 0x0C, 0x09, 0x05, 0x03, 0x15, 0x12, + 0x0D, 0x02, 0x08, 0x0E, 0x19, 0x07, 0x13, 0x17, + 0x1E, 0x1D, 0x1F, 0x11, 0x06, 0x0A, 0x0B, 0x14, + 0x0F, 0x10, 0x01, 0x1B, 0x00, 0x04, 0x1A, 0x16 }; + +unsigned char table_67[256] = { + 0x6B, 0x49, 0xC8, 0x86, 0xFF, 0xC0, 0x5D, 0xEF, + 0xF7, 0x06, 0xE0, 0x98, 0xA9, 0x72, 0x71, 0xD5, + 0xBA, 0x7F, 0x10, 0xD1, 0xBE, 0x41, 0x9C, 0x40, + 0x28, 0x8E, 0xE5, 0x74, 0x47, 0x9E, 0x3E, 0x7C, + 0xB5, 0xCD, 0x3F, 0x20, 0xF2, 0xA6, 0xDC, 0x97, + 0x32, 0x6D, 0x52, 0xF5, 0x16, 0x05, 0xFE, 0x04, + 0x3D, 0x53, 0x50, 0x23, 0x39, 0x77, 0x08, 0x60, + 0x75, 0x18, 0x4A, 0xC6, 0xBB, 0xE7, 0xF1, 0xAB, + 0xEB, 0x88, 0xB6, 0x82, 0x6E, 0x91, 0xF3, 0x34, + 0x3A, 0x42, 0x1A, 0xDF, 0xA1, 0xB3, 0x92, 0xBF, + 0xB7, 0x00, 0xD4, 0xDE, 0x31, 0xF0, 0x1C, 0xDA, + 0x4F, 0x61, 0x67, 0x2C, 0x07, 0xF9, 0x15, 0xA4, + 0x7A, 0x26, 0x45, 0x2A, 0x12, 0x9F, 0xF4, 0x14, + 0x8C, 0x90, 0xFC, 0xC5, 0x4B, 0x87, 0xE2, 0xC7, + 0xD0, 0x8A, 0xE8, 0xDD, 0xEE, 0x3C, 0x2F, 0x22, + 0x6A, 0x54, 0x37, 0x9B, 0x84, 0x25, 0x8F, 0xE3, + 0xD7, 0xD8, 0x4E, 0xAD, 0x0F, 0x4C, 0x56, 0xA2, + 0xD3, 0xB0, 0x73, 0x0B, 0xAE, 0xEA, 0x1D, 0x01, + 0x36, 0xB4, 0x2D, 0xC4, 0x19, 0x58, 0x1E, 0x62, + 0xE9, 0xB2, 0x5B, 0x5A, 0xBD, 0xD6, 0x65, 0x94, + 0x9A, 0x55, 0xCC, 0x99, 0x1B, 0x85, 0x2B, 0xBC, + 0x8D, 0x46, 0x81, 0xB8, 0xA3, 0x29, 0x5F, 0x35, + 0x5C, 0xB1, 0x1F, 0x13, 0x17, 0xCB, 0x51, 0x02, + 0x09, 0x7E, 0xA7, 0x69, 0x6F, 0x95, 0x30, 0x7B, + 0xCA, 0x48, 0xAF, 0xAA, 0x0E, 0x44, 0x38, 0xB9, + 0x0D, 0x11, 0xA0, 0xD9, 0x0C, 0xDB, 0xF8, 0x68, + 0x33, 0x79, 0x59, 0x66, 0x4D, 0x03, 0xE1, 0x89, + 0xE4, 0x3B, 0x78, 0xC2, 0x64, 0x6C, 0x27, 0xC9, + 0xCF, 0xAC, 0xED, 0xFA, 0x5E, 0x2E, 0x76, 0x57, + 0x93, 0xEC, 0x80, 0xA8, 0xE6, 0xCE, 0xC1, 0xA5, + 0x9D, 0xD2, 0xC3, 0x0A, 0x7D, 0x70, 0xF6, 0x63, + 0x24, 0x43, 0x21, 0x83, 0xFB, 0xFD, 0x8B, 0x96 }; + +unsigned char table_68[256] = { + 0x93, 0xFF, 0x83, 0x70, 0x12, 0x2D, 0x1C, 0xD6, + 0xF9, 0xEE, 0xCF, 0x94, 0x7B, 0xB5, 0xA4, 0x84, + 0x99, 0xF7, 0x67, 0x32, 0xFC, 0x8A, 0xE3, 0xE4, + 0xCE, 0xC6, 0x77, 0x7E, 0xDA, 0x42, 0x85, 0xF0, + 0x7D, 0x48, 0x28, 0x79, 0xDE, 0x5B, 0xE2, 0x0F, + 0x75, 0xC5, 0x2C, 0x4F, 0xF3, 0xEC, 0x14, 0x10, + 0x9C, 0x6E, 0x59, 0x4A, 0x20, 0x34, 0xA3, 0x89, + 0xE0, 0x4E, 0x52, 0x88, 0x81, 0x5F, 0x6F, 0x71, + 0x17, 0x3B, 0x21, 0xB4, 0xCB, 0x9B, 0x18, 0x13, + 0xE8, 0xE1, 0x02, 0x2E, 0xED, 0x00, 0xA7, 0x1B, + 0x06, 0xF4, 0x27, 0xDC, 0x35, 0x2F, 0x08, 0x9D, + 0x7C, 0xC0, 0x36, 0xA6, 0x6B, 0xDF, 0x4C, 0xBC, + 0xFE, 0xDB, 0xA5, 0xA8, 0x8D, 0x73, 0x7F, 0xC7, + 0x8E, 0x60, 0x31, 0x61, 0x4B, 0x29, 0xD7, 0xE9, + 0xBD, 0xAB, 0xCC, 0xFA, 0xD9, 0xEF, 0xC2, 0xD4, + 0x19, 0x11, 0x15, 0xC9, 0xB1, 0xD5, 0x64, 0x97, + 0xE7, 0x8F, 0x05, 0x44, 0xF8, 0xF1, 0x58, 0x47, + 0x2A, 0x03, 0x1F, 0xAF, 0x0D, 0x04, 0x23, 0xB8, + 0x24, 0x51, 0xB2, 0x54, 0x41, 0x53, 0x5C, 0xAE, + 0xB7, 0xB3, 0xB6, 0x3D, 0x37, 0x39, 0x55, 0xBF, + 0x0B, 0x7A, 0x57, 0x3C, 0x0E, 0x40, 0x6A, 0xF5, + 0x72, 0xDD, 0xBB, 0x8B, 0xAA, 0x46, 0xA0, 0x30, + 0x56, 0x78, 0x38, 0xBA, 0x9E, 0x92, 0x87, 0xFB, + 0x66, 0x90, 0x1E, 0xB9, 0x96, 0x65, 0xA2, 0x50, + 0x1D, 0xC3, 0x26, 0x22, 0xD0, 0x0A, 0x43, 0xF2, + 0xB0, 0xEB, 0xAC, 0x62, 0x98, 0x3F, 0xD3, 0x69, + 0xA1, 0x9F, 0x16, 0x95, 0xE6, 0xF6, 0x2B, 0x25, + 0x1A, 0xD2, 0xBE, 0x09, 0x5D, 0x45, 0xC4, 0xFD, + 0x5A, 0x07, 0x0C, 0x82, 0x3E, 0x49, 0x74, 0x6C, + 0x68, 0x5E, 0xCA, 0xEA, 0xCD, 0x9A, 0xAD, 0xD1, + 0x33, 0x86, 0x76, 0x80, 0xE5, 0xC8, 0xD8, 0xA9, + 0x8C, 0x6D, 0x91, 0x63, 0x3A, 0x4D, 0xC1, 0x01 }; + +unsigned char table_69[256] = { + 0x21, 0x6B, 0x9B, 0xAE, 0x11, 0x5A, 0x91, 0xC2, + 0x47, 0x8E, 0x87, 0x86, 0x4F, 0xFC, 0x8F, 0x66, + 0x97, 0x2F, 0x61, 0x9C, 0x5B, 0x4C, 0xB3, 0x14, + 0x77, 0x48, 0x62, 0xE1, 0x54, 0x64, 0xDD, 0xCD, + 0x30, 0xB7, 0x2D, 0xD2, 0xC3, 0xC0, 0x0B, 0xD8, + 0x53, 0x98, 0x16, 0x56, 0x7A, 0x35, 0x50, 0xD9, + 0xE8, 0x2C, 0x32, 0x55, 0x17, 0x5D, 0x79, 0xEB, + 0xC8, 0x75, 0x67, 0xE2, 0x4B, 0xBA, 0xFE, 0x57, + 0x10, 0xF4, 0x70, 0x2A, 0xBB, 0xA6, 0x72, 0x36, + 0xAF, 0x8D, 0xAB, 0x90, 0xE3, 0x2B, 0xB2, 0x26, + 0x93, 0x01, 0xBD, 0x71, 0xF9, 0x05, 0xC7, 0x80, + 0x29, 0xCC, 0x3B, 0x22, 0xF2, 0x12, 0x81, 0x34, + 0xF6, 0x1A, 0x8B, 0xDF, 0x28, 0x46, 0x9E, 0x6A, + 0x23, 0x85, 0x74, 0xE7, 0xE6, 0x52, 0xA0, 0x49, + 0xF0, 0x19, 0x25, 0xAC, 0x78, 0x42, 0xD6, 0xA2, + 0x37, 0x65, 0x4D, 0x94, 0x02, 0x6F, 0xB4, 0xC6, + 0x99, 0xD3, 0x9A, 0x33, 0xB8, 0x00, 0xCA, 0xE4, + 0x45, 0xAD, 0x1B, 0x6C, 0x03, 0xA8, 0x07, 0x8A, + 0x60, 0x69, 0xFF, 0xF7, 0xA7, 0x27, 0x95, 0xF5, + 0x82, 0xCB, 0xEC, 0xED, 0x4E, 0xFB, 0xA4, 0x59, + 0xDA, 0xCF, 0x2E, 0x20, 0xFA, 0x31, 0xD1, 0xEA, + 0x4A, 0xE9, 0x5E, 0xA9, 0xA1, 0x08, 0x1C, 0x96, + 0x38, 0xB9, 0xEE, 0x7F, 0xAA, 0xF1, 0x7D, 0x3A, + 0xA5, 0x43, 0xC5, 0xE0, 0x24, 0x39, 0x0D, 0xDE, + 0xB0, 0xF8, 0xBE, 0x58, 0x7E, 0x51, 0xD4, 0x89, + 0x15, 0x40, 0x3E, 0xB1, 0x1F, 0x5F, 0x68, 0x63, + 0x84, 0x3D, 0x88, 0xBC, 0x41, 0xEF, 0xB5, 0xBF, + 0x06, 0x6E, 0x9D, 0x3F, 0x0E, 0x76, 0x5C, 0xDC, + 0x13, 0xF3, 0xE5, 0x8C, 0x7C, 0x04, 0x0A, 0xD5, + 0x18, 0xC4, 0x44, 0x09, 0xC9, 0x1D, 0x9F, 0xFD, + 0xD0, 0x0F, 0x6D, 0xD7, 0x92, 0x7B, 0x0C, 0xA3, + 0x73, 0xDB, 0xB6, 0x83, 0xCE, 0x1E, 0xC1, 0x3C }; + +unsigned char table_70[256] = { + 0x54, 0x23, 0xF1, 0x09, 0x9D, 0xEB, 0x26, 0xD9, + 0x6C, 0xC1, 0xBC, 0x3D, 0x6E, 0xB0, 0x5F, 0xE2, + 0x59, 0x4D, 0x95, 0xFA, 0xD8, 0x29, 0xAA, 0x8E, + 0xF5, 0xEF, 0x43, 0x76, 0xFD, 0x0D, 0x4F, 0xAD, + 0xB7, 0xFC, 0xA8, 0x9F, 0x62, 0xC2, 0x7B, 0x10, + 0x0B, 0xF2, 0x73, 0xA9, 0x46, 0x4C, 0x53, 0xD7, + 0x0A, 0x50, 0x89, 0x63, 0x48, 0xD6, 0xA2, 0x44, + 0xE6, 0x8D, 0x69, 0x2C, 0xF9, 0xC0, 0x35, 0x06, + 0x66, 0x21, 0x9E, 0xD2, 0x98, 0xF7, 0x9B, 0xE7, + 0x12, 0xB8, 0xA5, 0xBA, 0xE0, 0x79, 0x71, 0x7E, + 0x8C, 0x24, 0xED, 0x7C, 0x60, 0x81, 0xC3, 0x5C, + 0x2B, 0xE5, 0xEE, 0xB5, 0xA4, 0x05, 0x03, 0x34, + 0x16, 0x2A, 0xA3, 0x2D, 0x3F, 0xDF, 0x07, 0x5B, + 0xAE, 0x47, 0x61, 0x08, 0x18, 0xDB, 0x6D, 0x3C, + 0x96, 0xD5, 0xAB, 0x78, 0x94, 0x45, 0x20, 0x9A, + 0xE4, 0x13, 0x68, 0xDD, 0xDE, 0x31, 0x14, 0x57, + 0x02, 0x52, 0x56, 0x1C, 0x1B, 0xE9, 0xD0, 0xA1, + 0x22, 0x64, 0xB2, 0x7A, 0xCF, 0x5D, 0x00, 0x0F, + 0xF8, 0x5E, 0x36, 0x58, 0x40, 0xAF, 0x19, 0x32, + 0x2E, 0xB3, 0x72, 0xBE, 0xB9, 0xD3, 0xCD, 0x7D, + 0x4A, 0x1D, 0x33, 0x2F, 0xAC, 0x27, 0x41, 0xE8, + 0x55, 0xCB, 0x0E, 0x5A, 0x77, 0xFB, 0x8B, 0x86, + 0x75, 0x8A, 0x51, 0xEC, 0xDA, 0xC6, 0xA6, 0xCC, + 0x91, 0x4B, 0x11, 0xF6, 0xEA, 0xD1, 0xB6, 0x4E, + 0x82, 0x04, 0x92, 0x30, 0xF4, 0x25, 0x88, 0x1E, + 0x9C, 0xA0, 0xC8, 0x6A, 0x93, 0x87, 0x1F, 0xB4, + 0xB1, 0x8F, 0x65, 0xCA, 0xFE, 0xFF, 0x97, 0x15, + 0x99, 0x28, 0x80, 0x42, 0x70, 0x85, 0x0C, 0x3B, + 0xBD, 0xE1, 0xA7, 0x17, 0xC9, 0x3A, 0xBB, 0x6B, + 0x37, 0xF0, 0xC5, 0x39, 0x6F, 0x01, 0x83, 0x67, + 0x74, 0xCE, 0xDC, 0x90, 0x3E, 0xF3, 0x7F, 0xC4, + 0x49, 0x84, 0x38, 0xC7, 0xE3, 0xD4, 0x1A, 0xBF }; + +unsigned char table_71[32] = { + 0x17, 0x13, 0x0E, 0x1A, 0x0D, 0x18, 0x19, 0x10, + 0x14, 0x11, 0x16, 0x05, 0x04, 0x00, 0x12, 0x0A, + 0x02, 0x07, 0x03, 0x0B, 0x09, 0x1F, 0x1C, 0x0F, + 0x0C, 0x06, 0x1B, 0x08, 0x1D, 0x01, 0x15, 0x1E }; + +unsigned char table_72[256] = { + 0xC9, 0xA7, 0x1B, 0xEC, 0x2B, 0x8B, 0xB0, 0xEB, + 0x7F, 0x39, 0x25, 0xD9, 0x1D, 0xD5, 0x67, 0xA0, + 0xB3, 0xAC, 0x3B, 0xC8, 0x82, 0xC0, 0xE3, 0x9E, + 0x4C, 0x9B, 0xAF, 0xFD, 0x91, 0x86, 0x5F, 0x92, + 0xB4, 0x42, 0x3C, 0x45, 0x12, 0xC4, 0xE2, 0xE1, + 0x6C, 0x1F, 0xC6, 0x40, 0x93, 0x2A, 0xC2, 0x72, + 0x2E, 0x14, 0x51, 0xA5, 0x70, 0xBD, 0xA2, 0xC7, + 0x7D, 0xF1, 0x9F, 0x64, 0xC1, 0xF7, 0x80, 0xFF, + 0x50, 0x49, 0x8C, 0x66, 0x13, 0x48, 0x6A, 0x0A, + 0x26, 0x94, 0x83, 0x1E, 0x84, 0xBB, 0x57, 0x27, + 0x44, 0x5B, 0x62, 0xF6, 0x09, 0x4F, 0x77, 0x76, + 0x2D, 0x7E, 0xCD, 0x0B, 0x24, 0xFE, 0x81, 0xB8, + 0x21, 0x85, 0xCF, 0xA8, 0x75, 0x56, 0x37, 0x17, + 0xAA, 0x23, 0xE5, 0xE8, 0x9A, 0x9D, 0x2F, 0x04, + 0x31, 0x4A, 0x7C, 0xFC, 0xD6, 0xE4, 0x29, 0xC3, + 0xFB, 0x36, 0x1C, 0x0C, 0xCE, 0xEE, 0x0D, 0xF3, + 0x46, 0xF8, 0x41, 0x0E, 0x68, 0xAB, 0x2C, 0x69, + 0x96, 0x90, 0x28, 0xED, 0x02, 0x63, 0x07, 0xAD, + 0xB2, 0xDC, 0x05, 0xE6, 0x78, 0x03, 0xA4, 0x7A, + 0x5C, 0x52, 0x95, 0x5D, 0x88, 0x01, 0xDF, 0x35, + 0x5E, 0xB6, 0x06, 0x4D, 0x15, 0x89, 0x59, 0x3F, + 0xF0, 0xA1, 0xA3, 0x99, 0x19, 0xEA, 0xDB, 0xE0, + 0x6B, 0x71, 0x6E, 0xB7, 0x65, 0x54, 0x9C, 0xBC, + 0x98, 0xDD, 0x4B, 0x60, 0x3D, 0xBF, 0xF5, 0xD1, + 0xD7, 0xF9, 0x55, 0x61, 0xA9, 0xB1, 0x6D, 0xDE, + 0x79, 0xAE, 0x1A, 0x34, 0x3A, 0x4E, 0xCB, 0x38, + 0xBA, 0x97, 0x00, 0x74, 0xEF, 0xD8, 0x18, 0x33, + 0x7B, 0xFA, 0x22, 0x32, 0x20, 0xCA, 0x8A, 0xBE, + 0xA6, 0x43, 0x11, 0x10, 0xD0, 0xD3, 0x87, 0x73, + 0x6F, 0xF4, 0x8D, 0xCC, 0x30, 0x0F, 0x16, 0xDA, + 0xB5, 0xC5, 0xD4, 0x47, 0x8E, 0xE7, 0x58, 0x8F, + 0x08, 0x53, 0xF2, 0xB9, 0x5A, 0x3E, 0xE9, 0xD2 }; + +unsigned char table_73[256] = { + 0x36, 0x37, 0xED, 0xD8, 0xBF, 0xD7, 0x12, 0xB7, + 0x40, 0x32, 0x19, 0x4A, 0x44, 0x2A, 0xCE, 0xA5, + 0x29, 0x13, 0x43, 0x51, 0x5C, 0xD0, 0x76, 0x6E, + 0x41, 0xD6, 0xE2, 0x4F, 0xB8, 0x27, 0x2E, 0xCF, + 0xD9, 0xE0, 0x69, 0xC0, 0x59, 0x77, 0x62, 0x6F, + 0x53, 0xE7, 0x93, 0xD4, 0xAD, 0xC8, 0x4C, 0xC2, + 0x2C, 0xBE, 0xAA, 0xA0, 0x22, 0x78, 0x14, 0xB3, + 0xB0, 0xEA, 0xBA, 0x9A, 0x33, 0x1B, 0x31, 0x6C, + 0xFC, 0x0A, 0x0B, 0xA1, 0xE4, 0x75, 0x7C, 0xE3, + 0x65, 0x21, 0xA9, 0xA4, 0x4E, 0x3C, 0x5F, 0x39, + 0x74, 0xA2, 0x9E, 0x03, 0x70, 0xD2, 0xFD, 0x1D, + 0x25, 0x72, 0x73, 0x8E, 0x7B, 0xB2, 0x6A, 0x92, + 0x81, 0xF3, 0xF0, 0x46, 0x08, 0x85, 0xE6, 0x30, + 0x05, 0x7E, 0xEC, 0x0D, 0xDD, 0x42, 0x2F, 0x5B, + 0xB9, 0xCB, 0x84, 0x0C, 0x16, 0xC7, 0x24, 0xFA, + 0xF9, 0x8F, 0x20, 0xAC, 0x10, 0x55, 0xC3, 0x1A, + 0x8B, 0x94, 0x3D, 0xDB, 0xC9, 0x04, 0xB5, 0xCC, + 0xC6, 0x98, 0xB6, 0x8D, 0x0F, 0x3A, 0x06, 0x4B, + 0xEF, 0x35, 0x68, 0x3F, 0xEE, 0xE5, 0x63, 0xC5, + 0x60, 0x88, 0x52, 0x2D, 0x6D, 0xAB, 0xCD, 0xC4, + 0x1F, 0xF4, 0xCA, 0x67, 0x7D, 0x1C, 0xDA, 0x34, + 0xDE, 0x86, 0xAE, 0xF1, 0x61, 0x09, 0xF5, 0xF6, + 0x49, 0xE9, 0xF2, 0x48, 0x1E, 0xD3, 0x56, 0x18, + 0x9B, 0xB1, 0x57, 0x9D, 0xBB, 0x5E, 0xAF, 0x87, + 0x9F, 0x8A, 0xC1, 0x79, 0xA7, 0xA8, 0xFB, 0xDC, + 0x47, 0x3E, 0x97, 0x80, 0x91, 0xA6, 0x7A, 0xA3, + 0x9C, 0x11, 0x02, 0x2B, 0x58, 0xD1, 0xF7, 0x00, + 0x83, 0x01, 0xE8, 0xFE, 0x50, 0x23, 0x66, 0x4D, + 0xD5, 0x82, 0x89, 0x3B, 0xEB, 0xE1, 0xF8, 0x5A, + 0x15, 0x7F, 0x8C, 0x17, 0x96, 0x28, 0x5D, 0x64, + 0x26, 0x38, 0x71, 0x0E, 0x45, 0xDF, 0xB4, 0x99, + 0xFF, 0x90, 0x6B, 0xBC, 0x54, 0x95, 0xBD, 0x07 }; + +unsigned char table_74[256] = { + 0xA7, 0xCF, 0x99, 0x1A, 0x13, 0xC7, 0xE9, 0xC4, + 0xB6, 0x0E, 0x15, 0x09, 0xFF, 0xDF, 0xBE, 0x03, + 0xAD, 0xF1, 0xB0, 0x3C, 0x4A, 0x9B, 0xF5, 0x12, + 0xA1, 0x2C, 0xDB, 0x51, 0x5E, 0x6F, 0xE6, 0x49, + 0x27, 0xBB, 0xAE, 0x56, 0xC0, 0x0C, 0x77, 0x60, + 0x5B, 0x69, 0xA2, 0xF0, 0x24, 0x8E, 0xE1, 0xA4, + 0xBC, 0x9F, 0x50, 0xD4, 0x61, 0x19, 0x67, 0x00, + 0x7B, 0xAB, 0xDD, 0x26, 0xCD, 0x6C, 0xE8, 0xA8, + 0x7A, 0x93, 0xEF, 0x20, 0x52, 0x1F, 0x1B, 0x46, + 0x25, 0x3B, 0x1E, 0x65, 0xC2, 0xF9, 0x10, 0xB2, + 0xB3, 0xD9, 0x21, 0xD2, 0x11, 0x94, 0xE2, 0xFC, + 0x38, 0x9E, 0x36, 0x87, 0xAA, 0x53, 0x45, 0x68, + 0x2B, 0xE7, 0x07, 0xFA, 0xD3, 0x8D, 0x3F, 0x17, + 0xC1, 0x06, 0x72, 0x62, 0x8C, 0x55, 0x73, 0x8A, + 0xC9, 0x2E, 0x5A, 0x7D, 0x02, 0x6D, 0xF8, 0x4B, + 0xE4, 0xBF, 0xEC, 0xB7, 0x31, 0xDC, 0xF4, 0xB8, + 0x47, 0x64, 0x0A, 0x33, 0x48, 0xAC, 0xFB, 0x05, + 0x3E, 0x34, 0x1C, 0x97, 0x1D, 0x63, 0x37, 0x2D, + 0xB1, 0x92, 0xED, 0x9D, 0x4C, 0xD5, 0x4E, 0x9A, + 0x0D, 0x79, 0x0F, 0xBD, 0x95, 0xBA, 0x08, 0x2A, + 0xC6, 0x7E, 0x88, 0xCB, 0xA6, 0x29, 0x70, 0x35, + 0x66, 0xCA, 0x89, 0x75, 0x6A, 0x4F, 0xB5, 0x6B, + 0x74, 0xDE, 0x01, 0x04, 0x81, 0x91, 0x90, 0x18, + 0x32, 0x0B, 0x7F, 0x44, 0xB4, 0xAF, 0xF2, 0xEB, + 0x22, 0xFD, 0x14, 0xA0, 0xFE, 0x8B, 0xB9, 0x16, + 0x86, 0xE3, 0xD7, 0xDA, 0xC5, 0x3A, 0x41, 0x83, + 0xD1, 0x28, 0x54, 0x30, 0xE0, 0x40, 0xA5, 0x57, + 0x8F, 0x84, 0xD6, 0x96, 0x39, 0xE5, 0x42, 0x80, + 0xA9, 0x58, 0xCE, 0x5D, 0xEE, 0x5F, 0xA3, 0xD0, + 0xC8, 0x59, 0x43, 0x4D, 0x5C, 0xF7, 0xCC, 0x76, + 0x6E, 0xF3, 0x23, 0x3D, 0x85, 0x82, 0x78, 0xF6, + 0x2F, 0xD8, 0xC3, 0x7C, 0x9C, 0x98, 0xEA, 0x71 }; + +unsigned char table_75[256] = { + 0xE7, 0xA5, 0x30, 0xE1, 0x9D, 0x81, 0xBE, 0x83, + 0xB2, 0x1E, 0xE4, 0x69, 0x2F, 0x2B, 0x0D, 0xEB, + 0x7C, 0x59, 0x2D, 0xAA, 0x01, 0x0C, 0xDB, 0xED, + 0xC4, 0xEE, 0x5D, 0x38, 0x72, 0xD8, 0x70, 0xCE, + 0x0B, 0xF6, 0x7F, 0x48, 0x26, 0x9E, 0xA3, 0x44, + 0xD6, 0xCF, 0x0F, 0x6B, 0xFD, 0x23, 0x98, 0xAB, + 0x11, 0xD4, 0x92, 0x91, 0x5E, 0x08, 0x4D, 0xC6, + 0xF0, 0xA8, 0x7E, 0x8A, 0x1D, 0xA1, 0x97, 0x76, + 0x3E, 0x64, 0x07, 0x24, 0xDE, 0x75, 0xA4, 0xCC, + 0x1A, 0x04, 0x4B, 0x6C, 0xFA, 0xB0, 0xC7, 0x35, + 0xE2, 0x56, 0x61, 0xA0, 0xE9, 0x27, 0xDF, 0xC3, + 0xE5, 0xF4, 0x8D, 0xB4, 0xD3, 0x52, 0xD7, 0x49, + 0xCD, 0x31, 0x6E, 0x3F, 0x4E, 0x6A, 0x5B, 0x65, + 0xCA, 0x14, 0x71, 0x53, 0xD9, 0x47, 0x28, 0x7D, + 0x17, 0x06, 0x5C, 0xFE, 0xBA, 0xB8, 0xAC, 0x15, + 0xE8, 0xE0, 0x9A, 0xDD, 0x1F, 0xBC, 0x95, 0x42, + 0xCB, 0x58, 0x00, 0x85, 0xD5, 0x62, 0xC9, 0xB6, + 0x05, 0x80, 0x4C, 0x3C, 0x1C, 0xF5, 0x03, 0xF8, + 0x96, 0x77, 0x02, 0x19, 0xF2, 0xFB, 0x5F, 0xC2, + 0xAE, 0x60, 0x1B, 0xAD, 0x8F, 0xC1, 0x33, 0xA6, + 0x20, 0xBF, 0xA7, 0xC8, 0x74, 0x18, 0x90, 0xE3, + 0x68, 0x09, 0x7A, 0x79, 0xB5, 0xDA, 0xF3, 0x0E, + 0x66, 0x84, 0xB3, 0xBB, 0xE6, 0xF7, 0xB7, 0x7B, + 0x39, 0x4A, 0x12, 0x4F, 0xC5, 0x41, 0x54, 0xD0, + 0xFF, 0x87, 0x63, 0x40, 0x99, 0x21, 0x29, 0xD2, + 0x3D, 0x37, 0x3A, 0x93, 0xFC, 0x25, 0xF1, 0xD1, + 0x2C, 0x6D, 0x8C, 0x5A, 0x8E, 0x9B, 0xBD, 0xAF, + 0x10, 0x55, 0xF9, 0x9F, 0x43, 0x0A, 0x50, 0x16, + 0x57, 0xB1, 0xC0, 0x73, 0x82, 0xEF, 0x88, 0x6F, + 0xEA, 0x2A, 0xEC, 0x2E, 0x86, 0x45, 0x51, 0x22, + 0xA9, 0x34, 0x94, 0x3B, 0xB9, 0x9C, 0xA2, 0x13, + 0x89, 0x46, 0x78, 0xDC, 0x32, 0x8B, 0x67, 0x36 }; + +unsigned char table_76[256] = { + 0x3D, 0x66, 0x40, 0xC5, 0x1D, 0xF5, 0xE7, 0xB7, + 0x2C, 0x23, 0x09, 0xC2, 0x68, 0xE6, 0xD3, 0x8D, + 0x35, 0x94, 0x93, 0xF0, 0x43, 0x97, 0x2B, 0x4B, + 0x1A, 0xEB, 0x00, 0x4C, 0x6F, 0xE4, 0x92, 0xEA, + 0xB8, 0xA3, 0xA6, 0xEC, 0x11, 0x5E, 0x61, 0x81, + 0xE1, 0x48, 0xC9, 0xCB, 0xDB, 0x2E, 0x3B, 0xED, + 0x36, 0x52, 0x3A, 0xD2, 0x4F, 0x4E, 0x22, 0x96, + 0x57, 0x2D, 0x62, 0x53, 0xCF, 0xD9, 0x5B, 0x9F, + 0x8E, 0x78, 0xC6, 0x07, 0x7D, 0xA1, 0x02, 0xB4, + 0xF4, 0xB6, 0x34, 0x98, 0xDA, 0xA9, 0xD4, 0x54, + 0x99, 0x82, 0x0A, 0xD8, 0x88, 0x5D, 0x3C, 0xD0, + 0xAB, 0x31, 0xFB, 0x03, 0x17, 0x46, 0xE8, 0xE2, + 0xA4, 0xFF, 0xB0, 0xAA, 0xAD, 0x7C, 0x55, 0x49, + 0x75, 0x6B, 0x10, 0x24, 0xC0, 0x04, 0xB1, 0xBF, + 0x6A, 0xF6, 0x15, 0xEF, 0x5C, 0x60, 0x27, 0x3E, + 0x38, 0x63, 0xC1, 0x76, 0xFD, 0x84, 0xE0, 0xCD, + 0xFE, 0x30, 0xCE, 0xBB, 0xDC, 0x1E, 0x1B, 0xBC, + 0xB5, 0xE9, 0x9E, 0x8F, 0x0D, 0x3F, 0x91, 0x19, + 0x28, 0x37, 0x26, 0x42, 0x08, 0x9A, 0x0C, 0x83, + 0x90, 0x6D, 0x74, 0x65, 0xF2, 0x4A, 0xDE, 0x8B, + 0x67, 0x0E, 0x8C, 0x5F, 0xF9, 0x7F, 0x5A, 0x86, + 0x69, 0x45, 0x44, 0xD5, 0xF7, 0xE5, 0x8A, 0xA8, + 0xC8, 0x7E, 0x05, 0x64, 0xEE, 0x79, 0xBE, 0x7A, + 0x14, 0xD6, 0x50, 0x18, 0x25, 0xBD, 0x85, 0xE3, + 0xA2, 0x70, 0xCC, 0x59, 0x71, 0x77, 0xFA, 0x47, + 0x9B, 0x1F, 0x9D, 0xBA, 0x29, 0x4D, 0xF8, 0xDF, + 0xC4, 0x72, 0x2F, 0xAE, 0x06, 0x51, 0x41, 0xAF, + 0xF3, 0xDD, 0x87, 0xB2, 0x9C, 0xC7, 0x12, 0x16, + 0x20, 0xA7, 0x21, 0x73, 0xF1, 0x58, 0xD7, 0x7B, + 0xB9, 0xB3, 0x32, 0x01, 0x80, 0x1C, 0x39, 0x0B, + 0x13, 0x56, 0x6C, 0x89, 0x33, 0x6E, 0x2A, 0xA5, + 0xD1, 0x95, 0xC3, 0xA0, 0x0F, 0xCA, 0xAC, 0xFC }; + +unsigned char table_77[32] = { + 0x1C, 0x0D, 0x1E, 0x01, 0x06, 0x16, 0x18, 0x17, + 0x0B, 0x1F, 0x04, 0x0F, 0x00, 0x19, 0x08, 0x0A, + 0x11, 0x03, 0x05, 0x07, 0x09, 0x0C, 0x15, 0x14, + 0x1A, 0x12, 0x13, 0x0E, 0x1D, 0x10, 0x02, 0x1B }; + +unsigned char table_78[32] = { + 0x0E, 0x02, 0x17, 0x12, 0x1E, 0x09, 0x15, 0x03, + 0x01, 0x0B, 0x0F, 0x11, 0x10, 0x0A, 0x16, 0x06, + 0x07, 0x00, 0x1C, 0x1D, 0x1F, 0x0C, 0x18, 0x04, + 0x13, 0x0D, 0x1B, 0x08, 0x19, 0x14, 0x05, 0x1A }; + +unsigned char table_79[32] = { + 0x12, 0x0B, 0x11, 0x01, 0x07, 0x0E, 0x1A, 0x0D, + 0x1E, 0x18, 0x14, 0x1F, 0x0A, 0x17, 0x19, 0x1B, + 0x00, 0x10, 0x0C, 0x08, 0x13, 0x02, 0x0F, 0x1D, + 0x09, 0x06, 0x04, 0x16, 0x15, 0x1C, 0x05, 0x03 }; + +unsigned char table_80[256] = { + 0x14, 0xE7, 0x31, 0x0F, 0xD1, 0x5F, 0xED, 0x1E, + 0xA6, 0x77, 0x20, 0x57, 0x34, 0x64, 0x33, 0x0B, + 0x5A, 0xB4, 0x83, 0x62, 0xFD, 0x8E, 0xE4, 0xF3, + 0xBD, 0xA5, 0xC8, 0x6D, 0x3E, 0x4F, 0x01, 0x7A, + 0xD3, 0x45, 0x3C, 0xF2, 0x68, 0xFF, 0xE6, 0x84, + 0xC2, 0xC1, 0x53, 0x72, 0x8C, 0xA1, 0xC7, 0x00, + 0x89, 0x97, 0x69, 0xA4, 0xF8, 0xAA, 0xAD, 0x8F, + 0x24, 0xC6, 0x9A, 0xAC, 0xE5, 0xAB, 0x6B, 0x79, + 0x99, 0x60, 0x28, 0x2B, 0x3B, 0xAF, 0x1C, 0x80, + 0xA3, 0x8A, 0x1A, 0xB5, 0xE1, 0x9F, 0xDA, 0x78, + 0xD7, 0xC4, 0x87, 0x5D, 0xE9, 0x27, 0xFB, 0x18, + 0x94, 0x3A, 0xCE, 0x3F, 0xF6, 0x12, 0x75, 0x37, + 0x6E, 0x9E, 0x29, 0x6C, 0xF7, 0x7D, 0x92, 0x08, + 0x42, 0xB2, 0xBF, 0x0C, 0xB6, 0x25, 0xE0, 0x49, + 0x43, 0x91, 0x98, 0xBB, 0xDC, 0x63, 0xEA, 0xA8, + 0x74, 0x38, 0x35, 0xCD, 0x07, 0x70, 0x81, 0x41, + 0xC9, 0x51, 0xBC, 0xA9, 0x59, 0xD4, 0xB8, 0x2C, + 0x7C, 0x2D, 0xB3, 0x6F, 0x11, 0x86, 0x9D, 0x46, + 0xF0, 0x65, 0x76, 0x04, 0x0E, 0xCA, 0xBE, 0x5C, + 0xF9, 0x71, 0x9C, 0x21, 0x4C, 0x02, 0xFE, 0x8D, + 0xD5, 0x26, 0x40, 0xC3, 0x32, 0x9B, 0xB0, 0x5E, + 0x48, 0xC5, 0x85, 0x4B, 0x0A, 0xCC, 0x58, 0x52, + 0x61, 0x13, 0xEF, 0x4A, 0xEE, 0x03, 0xD9, 0xDE, + 0xA7, 0x19, 0x09, 0x7F, 0x5B, 0x96, 0xBA, 0x0D, + 0xCF, 0xD2, 0x06, 0x1F, 0xD8, 0xDB, 0xEC, 0xA0, + 0xDD, 0x66, 0x10, 0xA2, 0xDF, 0x30, 0xF4, 0x88, + 0xCB, 0x36, 0x82, 0xE3, 0x73, 0x17, 0x55, 0x15, + 0xF5, 0xB7, 0x23, 0xB1, 0xD6, 0xE2, 0x47, 0x7E, + 0x67, 0xE8, 0x1D, 0x16, 0x8B, 0xEB, 0xD0, 0x3D, + 0x6A, 0x54, 0x2A, 0x4E, 0x93, 0xFA, 0x44, 0x05, + 0x2F, 0x50, 0x2E, 0x95, 0xAE, 0x1B, 0x56, 0x7B, + 0x39, 0xB9, 0xC0, 0x22, 0xF1, 0x4D, 0x90, 0xFC }; + +unsigned char table_81[32] = { + 0x03, 0x02, 0x1D, 0x0E, 0x09, 0x1A, 0x0C, 0x11, + 0x1C, 0x0D, 0x08, 0x12, 0x19, 0x10, 0x04, 0x17, + 0x15, 0x05, 0x0A, 0x00, 0x13, 0x16, 0x1B, 0x18, + 0x1E, 0x0B, 0x0F, 0x01, 0x07, 0x14, 0x1F, 0x06 }; + +unsigned char table_82[256] = { + 0x53, 0xD3, 0x64, 0x89, 0x7D, 0xA5, 0x66, 0xA4, + 0x09, 0x46, 0x17, 0x2C, 0xAF, 0x8C, 0x21, 0x5F, + 0x3B, 0x22, 0xE3, 0x05, 0x07, 0x28, 0x2F, 0xAB, + 0xF4, 0x8E, 0x51, 0x31, 0x02, 0xC7, 0x48, 0x13, + 0x24, 0x12, 0xB8, 0xE5, 0xBD, 0xAE, 0x7E, 0xCC, + 0xC9, 0x98, 0x08, 0xEE, 0xDB, 0x1B, 0xE8, 0x3D, + 0x8F, 0xF2, 0xFB, 0x36, 0x4D, 0x94, 0x9C, 0x16, + 0xF7, 0x42, 0x9B, 0x2B, 0xFD, 0x7B, 0x77, 0x3F, + 0xC3, 0xFC, 0x23, 0x93, 0x50, 0x0C, 0x79, 0x18, + 0x47, 0xE1, 0xCB, 0xA7, 0xB6, 0x85, 0xE6, 0x61, + 0x2D, 0xD8, 0x9F, 0x80, 0xE9, 0x14, 0x0B, 0x1C, + 0x40, 0x76, 0x2A, 0x25, 0x0E, 0x99, 0xAC, 0xC4, + 0xEB, 0x29, 0x41, 0x8A, 0x73, 0x06, 0x57, 0xC6, + 0x8D, 0xFA, 0x5A, 0xCD, 0x67, 0xB2, 0xD9, 0x0A, + 0x1E, 0xEF, 0x3E, 0xA0, 0x45, 0x03, 0x27, 0xF1, + 0x38, 0x54, 0xC1, 0x7A, 0xFE, 0x52, 0x75, 0xD4, + 0x74, 0x7C, 0xD2, 0x68, 0xEA, 0x4C, 0x97, 0xF9, + 0xF5, 0x8B, 0x0F, 0x84, 0xA8, 0x6E, 0x9E, 0x11, + 0x6B, 0xBC, 0x4B, 0x6C, 0x9A, 0xF0, 0xA3, 0x1F, + 0x92, 0x19, 0xA2, 0x3A, 0x15, 0x04, 0xC5, 0x62, + 0xD5, 0x96, 0x90, 0x32, 0xAA, 0xD6, 0xCF, 0x35, + 0xB4, 0x81, 0x2E, 0x01, 0x10, 0x49, 0x70, 0xDE, + 0xDD, 0x88, 0xB9, 0x6D, 0x60, 0xBB, 0x44, 0xF8, + 0x3C, 0xEC, 0x34, 0x82, 0x95, 0x72, 0x58, 0x4E, + 0xE4, 0x0D, 0xBE, 0xDA, 0x83, 0x4A, 0x00, 0xBF, + 0xD0, 0xC8, 0x26, 0xB3, 0x65, 0x1A, 0x69, 0xCA, + 0xF3, 0xD7, 0x6F, 0x55, 0xE2, 0xFF, 0x5D, 0xDC, + 0x20, 0xF6, 0x63, 0xED, 0xE0, 0x59, 0x9D, 0xB1, + 0x1D, 0xAD, 0x91, 0xA1, 0xB7, 0xA9, 0xDF, 0xC0, + 0x39, 0xD1, 0x43, 0xCE, 0x4F, 0x5C, 0xE7, 0x37, + 0x5E, 0x33, 0x5B, 0xA6, 0xC2, 0xB0, 0xBA, 0x30, + 0x6A, 0x78, 0xB5, 0x71, 0x56, 0x87, 0x7F, 0x86 }; + +unsigned char table_83[32] = { + 0x1B, 0x0A, 0x1F, 0x01, 0x10, 0x08, 0x0E, 0x18, + 0x06, 0x04, 0x00, 0x1C, 0x0C, 0x19, 0x0D, 0x16, + 0x02, 0x03, 0x09, 0x07, 0x13, 0x0F, 0x05, 0x12, + 0x17, 0x1E, 0x1A, 0x1D, 0x0B, 0x11, 0x14, 0x15 }; + +unsigned char table_84[32] = { + 0x02, 0x1A, 0x0D, 0x15, 0x01, 0x16, 0x1E, 0x00, + 0x08, 0x1B, 0x04, 0x10, 0x1C, 0x18, 0x19, 0x14, + 0x0C, 0x11, 0x0B, 0x0E, 0x03, 0x0A, 0x07, 0x12, + 0x1D, 0x17, 0x13, 0x06, 0x0F, 0x05, 0x09, 0x1F }; + +unsigned char table_85[256] = { + 0xC6, 0x7C, 0xCE, 0xBD, 0x84, 0x3E, 0x0B, 0xD8, + 0xFE, 0xCC, 0x46, 0x50, 0xD1, 0xFB, 0xA0, 0x6D, + 0xEA, 0xE2, 0x40, 0x51, 0x13, 0xB0, 0xD6, 0xB1, + 0xA8, 0xDF, 0x61, 0xA4, 0x80, 0x21, 0xB3, 0x33, + 0x06, 0x6B, 0xE3, 0x8C, 0xA1, 0x18, 0xBA, 0x03, + 0xD7, 0x8D, 0x54, 0x12, 0x4C, 0xEE, 0x9E, 0xCF, + 0x04, 0x2A, 0x08, 0xBB, 0xC2, 0xD4, 0xC3, 0x4A, + 0xD5, 0xFA, 0x36, 0x2F, 0x14, 0x3F, 0xED, 0x05, + 0x17, 0x28, 0x75, 0xFC, 0xA2, 0x1F, 0x4B, 0x6F, + 0x91, 0x7E, 0x4E, 0x96, 0x3B, 0xF3, 0x1D, 0x78, + 0xEB, 0x68, 0xF1, 0xA7, 0x9F, 0xC7, 0x59, 0x6C, + 0x92, 0xE6, 0x66, 0x07, 0x8A, 0x25, 0x26, 0x72, + 0x30, 0x5A, 0x81, 0x2C, 0x58, 0x32, 0xCB, 0xE0, + 0xF9, 0x48, 0x83, 0x9B, 0xA5, 0xE1, 0xA6, 0x64, + 0xFF, 0xC9, 0x8F, 0x53, 0x3D, 0x24, 0xC8, 0xDE, + 0x02, 0x7D, 0x09, 0xB4, 0x0A, 0x95, 0x0F, 0xE4, + 0xDB, 0xB7, 0x71, 0x4D, 0x1C, 0xAC, 0x35, 0xCD, + 0x29, 0xDD, 0xC1, 0xF2, 0xF4, 0xC0, 0x5C, 0x74, + 0xDC, 0x87, 0xFD, 0x4F, 0x11, 0x0E, 0x5D, 0x3C, + 0x01, 0x73, 0xE9, 0xD9, 0x10, 0x9A, 0x5B, 0xC5, + 0x98, 0x34, 0x15, 0xAE, 0xF7, 0xAA, 0x67, 0x23, + 0xBC, 0x8B, 0x7B, 0x65, 0xA9, 0xB6, 0x77, 0x00, + 0x19, 0x0C, 0x5E, 0x99, 0xF0, 0x55, 0x86, 0x97, + 0x69, 0xDA, 0x38, 0x9C, 0x16, 0xE8, 0x27, 0xAF, + 0x2E, 0x47, 0x6A, 0xD0, 0x79, 0x44, 0x45, 0x2B, + 0x5F, 0x85, 0xF5, 0x62, 0x70, 0x22, 0x7F, 0xF6, + 0x88, 0x93, 0x60, 0x42, 0x3A, 0x39, 0x49, 0x6E, + 0x89, 0x52, 0x20, 0xF8, 0xCA, 0xD2, 0x76, 0xB9, + 0xAB, 0x7A, 0x9D, 0xD3, 0xBE, 0x1A, 0xAD, 0x41, + 0x56, 0x31, 0x90, 0xB5, 0xB2, 0xEC, 0xA3, 0xE5, + 0x8E, 0x1B, 0xEF, 0xBF, 0x94, 0xC4, 0x0D, 0xB8, + 0x2D, 0x57, 0xE7, 0x82, 0x1E, 0x37, 0x63, 0x43 }; + +unsigned char table_86[32] = { + 0x11, 0x07, 0x0F, 0x0A, 0x19, 0x1D, 0x0B, 0x09, + 0x1C, 0x1E, 0x14, 0x06, 0x0C, 0x16, 0x13, 0x04, + 0x15, 0x18, 0x00, 0x0D, 0x12, 0x05, 0x08, 0x02, + 0x10, 0x1A, 0x1F, 0x01, 0x17, 0x0E, 0x03, 0x1B }; + +unsigned char table_87[32] = { + 0x17, 0x0E, 0x1D, 0x13, 0x0B, 0x19, 0x03, 0x06, + 0x09, 0x01, 0x0D, 0x15, 0x1C, 0x16, 0x18, 0x1B, + 0x11, 0x10, 0x00, 0x1E, 0x1F, 0x08, 0x12, 0x0F, + 0x02, 0x04, 0x07, 0x1A, 0x14, 0x0A, 0x0C, 0x05 }; + +unsigned char table_88[32] = { + 0x09, 0x08, 0x17, 0x10, 0x0A, 0x07, 0x1C, 0x1F, + 0x04, 0x0E, 0x01, 0x0C, 0x0D, 0x1B, 0x03, 0x15, + 0x02, 0x1E, 0x18, 0x19, 0x0F, 0x06, 0x1A, 0x0B, + 0x05, 0x11, 0x14, 0x00, 0x16, 0x1D, 0x12, 0x13 }; + +unsigned char table_89[32] = { + 0x15, 0x1C, 0x1D, 0x14, 0x0F, 0x1A, 0x05, 0x02, + 0x07, 0x09, 0x06, 0x08, 0x1F, 0x00, 0x10, 0x13, + 0x0D, 0x03, 0x0C, 0x18, 0x0E, 0x16, 0x1B, 0x1E, + 0x12, 0x04, 0x11, 0x0A, 0x01, 0x0B, 0x17, 0x19 }; + +unsigned char table_90[256] = { + 0x62, 0x36, 0x64, 0x0E, 0x4C, 0x6C, 0xBE, 0xCF, + 0x25, 0x5A, 0x3D, 0x12, 0x54, 0x9F, 0xE7, 0xA5, + 0xDE, 0xD7, 0xB2, 0x60, 0x18, 0x8D, 0x89, 0x70, + 0x48, 0x66, 0x1C, 0xA6, 0x17, 0x9B, 0xDF, 0x9A, + 0x82, 0xB9, 0x2E, 0xFA, 0x83, 0x5B, 0x7A, 0x61, + 0xFC, 0x6B, 0x8B, 0x4E, 0x0F, 0xAD, 0x78, 0xE1, + 0xE8, 0x15, 0x1A, 0xF7, 0xA3, 0x3A, 0x04, 0xE3, + 0x30, 0x8C, 0x06, 0xC4, 0x05, 0x32, 0x1F, 0x6A, + 0xB8, 0x37, 0x58, 0xF5, 0x74, 0x63, 0xD4, 0xAC, + 0xA4, 0xF3, 0xEC, 0xBB, 0x8E, 0x65, 0xA0, 0xEE, + 0x6D, 0x11, 0xDD, 0xEA, 0x68, 0x2B, 0xDA, 0x0B, + 0xEF, 0xC3, 0x8F, 0x03, 0x77, 0x1B, 0xFB, 0x1E, + 0x5C, 0xD9, 0xCB, 0x33, 0x55, 0xF1, 0xA1, 0xF9, + 0x7C, 0x38, 0x95, 0x00, 0x6E, 0x85, 0xC2, 0x7F, + 0xBF, 0x84, 0x2A, 0x13, 0x72, 0x81, 0xE9, 0x59, + 0x41, 0x69, 0x3B, 0x0C, 0x90, 0xB4, 0x51, 0x2F, + 0xA2, 0xFE, 0xF8, 0x49, 0x57, 0xE5, 0x96, 0xFF, + 0xCD, 0xD5, 0xCE, 0xAA, 0x40, 0xB0, 0x4D, 0xBA, + 0xDB, 0xC7, 0x46, 0x86, 0xD1, 0xCA, 0xC0, 0x67, + 0x9C, 0x21, 0xAE, 0xB3, 0x7B, 0x87, 0xE2, 0x71, + 0xE6, 0x39, 0xA8, 0x22, 0x07, 0x2C, 0x44, 0x52, + 0xA7, 0xF0, 0x4A, 0x92, 0x56, 0x28, 0x43, 0x8A, + 0x5E, 0x53, 0x93, 0x47, 0x97, 0x88, 0x76, 0x79, + 0x91, 0x26, 0xC1, 0x3F, 0xB7, 0xF6, 0x3E, 0x80, + 0xA9, 0xC6, 0x01, 0xD2, 0xEB, 0x9E, 0x4B, 0xBC, + 0xC8, 0xB5, 0x02, 0x5F, 0x98, 0x9D, 0x5D, 0x35, + 0xD0, 0x16, 0xB1, 0x23, 0x7D, 0xAF, 0x10, 0x3C, + 0xAB, 0x14, 0x09, 0x2D, 0x0D, 0xC5, 0x1D, 0xD6, + 0x42, 0xF2, 0x34, 0x73, 0xF4, 0xFD, 0xE0, 0x24, + 0x6F, 0xD3, 0x75, 0xD8, 0xCC, 0xB6, 0x99, 0x4F, + 0x29, 0x0A, 0x08, 0xE4, 0x27, 0x19, 0x31, 0xC9, + 0x20, 0x94, 0x45, 0xED, 0xDC, 0xBD, 0x7E, 0x50 }; + +unsigned char table_91[32] = { + 0x03, 0x04, 0x0C, 0x18, 0x10, 0x0D, 0x13, 0x1B, + 0x1F, 0x07, 0x11, 0x17, 0x1C, 0x1D, 0x05, 0x06, + 0x0A, 0x12, 0x02, 0x1A, 0x0B, 0x01, 0x0E, 0x08, + 0x14, 0x16, 0x00, 0x15, 0x19, 0x09, 0x0F, 0x1E }; + +unsigned char table_92[32] = { + 0x1E, 0x10, 0x01, 0x07, 0x11, 0x16, 0x15, 0x17, + 0x1F, 0x14, 0x0C, 0x1C, 0x06, 0x03, 0x00, 0x18, + 0x08, 0x0E, 0x02, 0x1B, 0x09, 0x0D, 0x19, 0x05, + 0x0F, 0x12, 0x0B, 0x13, 0x0A, 0x04, 0x1D, 0x1A }; + +unsigned char table_93[256] = { + 0x76, 0x78, 0xA2, 0x94, 0x0E, 0x7F, 0xDF, 0xC1, + 0xB9, 0xE1, 0x3D, 0x59, 0x6F, 0x1E, 0x53, 0x99, + 0x80, 0xE3, 0x21, 0xF8, 0x65, 0xB8, 0x08, 0xBC, + 0x29, 0x17, 0xFD, 0x33, 0x35, 0xF2, 0x70, 0xC7, + 0x25, 0xD0, 0xCD, 0x7A, 0xB7, 0x9B, 0xA5, 0xC3, + 0x00, 0x90, 0xDC, 0xB1, 0x0C, 0x20, 0x67, 0x8D, + 0x43, 0x49, 0xF3, 0x96, 0x14, 0x1A, 0xC8, 0x19, + 0x72, 0xD7, 0x8A, 0x38, 0x66, 0xDA, 0xDD, 0x2E, + 0xBE, 0xD5, 0x91, 0x7C, 0x3A, 0x92, 0x8E, 0xE7, + 0x51, 0xB5, 0xA8, 0xD9, 0x0B, 0x2A, 0xBA, 0x81, + 0x41, 0x0F, 0xBD, 0x4E, 0x31, 0x23, 0x9C, 0x8B, + 0x2B, 0x1D, 0x04, 0x3E, 0x8C, 0xF0, 0x45, 0xA0, + 0x1C, 0x44, 0x55, 0x5E, 0xF1, 0x98, 0x54, 0x5D, + 0x9D, 0x84, 0xAE, 0x09, 0xA9, 0xC5, 0x83, 0x60, + 0x86, 0x95, 0xB4, 0xFA, 0x6B, 0xA7, 0x9A, 0xCA, + 0x8F, 0x4F, 0x0A, 0x7B, 0xB0, 0x02, 0xEA, 0xA4, + 0x18, 0xDB, 0xD3, 0x64, 0xEB, 0xFC, 0xC4, 0xC9, + 0xF5, 0xD6, 0xCC, 0x75, 0x0D, 0x5C, 0x93, 0x4A, + 0x6D, 0xC0, 0x1F, 0x50, 0xE6, 0x16, 0xEE, 0x07, + 0xFB, 0x74, 0x56, 0x58, 0x52, 0x89, 0x79, 0x68, + 0xB6, 0xFE, 0x01, 0xD4, 0x7E, 0x06, 0xBF, 0xCB, + 0x5B, 0xC2, 0xC6, 0x32, 0xAC, 0x26, 0x22, 0xD2, + 0x82, 0x46, 0x69, 0x15, 0x2C, 0xF7, 0xAD, 0x13, + 0x4D, 0xA3, 0xF6, 0x2D, 0x48, 0x71, 0x57, 0x11, + 0x63, 0x05, 0x5F, 0x9E, 0x4B, 0xAB, 0xA6, 0x61, + 0xBB, 0xA1, 0x3C, 0x97, 0xF9, 0x03, 0x40, 0x12, + 0xCF, 0x37, 0xE4, 0x10, 0x6A, 0xED, 0xFF, 0x62, + 0x42, 0x4C, 0xAF, 0x9F, 0xE5, 0xE8, 0xD8, 0xD1, + 0x28, 0x3F, 0x1B, 0xE9, 0xCE, 0x6C, 0x27, 0x88, + 0xEF, 0x2F, 0xE0, 0x30, 0x87, 0x5A, 0x73, 0xB3, + 0x6E, 0x3B, 0x7D, 0x77, 0x36, 0xAA, 0x39, 0xDE, + 0x24, 0x34, 0xE2, 0xEC, 0x85, 0x47, 0xF4, 0xB2 }; + +unsigned char table_94[32] = { + 0x1C, 0x07, 0x05, 0x1A, 0x10, 0x1D, 0x14, 0x12, + 0x08, 0x0F, 0x0C, 0x01, 0x04, 0x1B, 0x16, 0x0A, + 0x11, 0x02, 0x1F, 0x13, 0x0D, 0x1E, 0x17, 0x06, + 0x0E, 0x09, 0x15, 0x19, 0x03, 0x18, 0x00, 0x0B }; + +unsigned char table_95[32] = { + 0x12, 0x10, 0x11, 0x15, 0x03, 0x0A, 0x14, 0x05, + 0x1D, 0x07, 0x17, 0x0D, 0x09, 0x08, 0x1B, 0x1F, + 0x0B, 0x06, 0x19, 0x0E, 0x18, 0x04, 0x00, 0x02, + 0x1E, 0x1C, 0x01, 0x0C, 0x1A, 0x0F, 0x13, 0x16 }; + +unsigned char table_96[256] = { + 0x1C, 0x6E, 0xCD, 0xB4, 0xB3, 0x93, 0xA8, 0x2E, + 0x4F, 0x09, 0xE3, 0x72, 0x64, 0x13, 0x21, 0xF5, + 0x89, 0xB2, 0xD2, 0x22, 0x5D, 0x63, 0x90, 0xC4, + 0x42, 0x9B, 0x07, 0xCA, 0x16, 0x19, 0x5C, 0x2B, + 0x3D, 0xA0, 0x69, 0x5F, 0x52, 0x41, 0x66, 0xC0, + 0x55, 0xDA, 0x82, 0x40, 0x25, 0x02, 0x3C, 0xDD, + 0xAE, 0xD7, 0xD6, 0xDB, 0x04, 0x78, 0x05, 0x4A, + 0x4C, 0x81, 0x00, 0xBE, 0x45, 0xC5, 0x30, 0xB0, + 0x65, 0x5A, 0xA9, 0x38, 0x75, 0x26, 0x85, 0x4E, + 0xF0, 0xA2, 0x91, 0x8A, 0x54, 0xD0, 0x3E, 0x0D, + 0xFE, 0xF2, 0x0A, 0x23, 0x24, 0x37, 0x32, 0x0B, + 0xCB, 0xB5, 0x28, 0x6A, 0x95, 0x49, 0x53, 0x9A, + 0xEE, 0x2C, 0x9D, 0xD4, 0x1D, 0x46, 0xC9, 0x79, + 0xCC, 0xDF, 0x17, 0xE8, 0x6D, 0x29, 0x0E, 0x80, + 0xE0, 0x62, 0xA1, 0xFA, 0x10, 0xF6, 0x03, 0xC1, + 0x15, 0x14, 0x1F, 0x99, 0x97, 0xD5, 0x9E, 0x3F, + 0x7B, 0x2F, 0xEF, 0x2A, 0x68, 0x83, 0xE2, 0x1B, + 0xC8, 0x87, 0x12, 0x70, 0xC7, 0x36, 0xD3, 0x73, + 0x8B, 0x7D, 0x47, 0x9F, 0xD9, 0xFB, 0x6C, 0x5B, + 0xFC, 0xAA, 0xB9, 0xB1, 0x0C, 0x31, 0x8E, 0xF3, + 0x92, 0xA3, 0x4B, 0xF1, 0xC2, 0x3A, 0x67, 0xEA, + 0x77, 0x11, 0xB6, 0xE4, 0x1A, 0x33, 0xD1, 0xBA, + 0xF9, 0xAC, 0x43, 0xE5, 0xC3, 0xC6, 0xFD, 0xF4, + 0x44, 0x6F, 0xB7, 0x88, 0xA7, 0xF8, 0x34, 0x94, + 0x6B, 0x27, 0xDE, 0x1E, 0xDC, 0x01, 0x61, 0x50, + 0xAD, 0x74, 0x4D, 0x86, 0xF7, 0x8D, 0x9C, 0x0F, + 0x5E, 0xBD, 0x08, 0x84, 0x18, 0xED, 0xA5, 0x39, + 0xAB, 0x98, 0x48, 0xE6, 0x2D, 0x96, 0xCF, 0x7F, + 0xFF, 0xBB, 0x8F, 0xEC, 0xBF, 0xE7, 0x56, 0xA4, + 0x35, 0x76, 0xA6, 0xAF, 0xBC, 0x71, 0xE9, 0xB8, + 0x7E, 0x7C, 0x06, 0x3B, 0xEB, 0x60, 0x7A, 0x8C, + 0x59, 0xCE, 0xE1, 0x57, 0x20, 0x58, 0x51, 0xD8 }; + +unsigned char table_97[256] = { + 0x15, 0x2D, 0xAF, 0x36, 0xCF, 0xD3, 0xD0, 0xED, + 0xB2, 0x1B, 0xFE, 0x92, 0xBD, 0xAD, 0x58, 0x0F, + 0x76, 0x3C, 0x47, 0x03, 0x2E, 0x4C, 0x40, 0xF7, + 0x39, 0xA7, 0x72, 0x22, 0x95, 0xF3, 0x8C, 0xE0, + 0x79, 0xB6, 0x75, 0x82, 0x94, 0x8F, 0x44, 0xFC, + 0xB0, 0x05, 0xE9, 0x10, 0x68, 0xE7, 0xF1, 0xA5, + 0xA8, 0xE2, 0x6F, 0xBE, 0xE5, 0x54, 0xA2, 0xC6, + 0xDB, 0x1C, 0x9E, 0x6D, 0x14, 0xA1, 0x26, 0x34, + 0x1E, 0x1A, 0x06, 0x53, 0xEE, 0x67, 0xA9, 0x73, + 0xD5, 0x59, 0x2F, 0x61, 0xE6, 0x74, 0xD6, 0x97, + 0xC0, 0x0C, 0xB1, 0x6E, 0x6C, 0x33, 0xC8, 0x77, + 0x8B, 0x49, 0x43, 0xE3, 0xB5, 0xDE, 0x6A, 0xA0, + 0x78, 0x2A, 0xC9, 0xF9, 0x9A, 0xDC, 0x90, 0x55, + 0xF4, 0x16, 0x5E, 0x3F, 0xC5, 0x7C, 0xFA, 0x09, + 0x8E, 0x87, 0xF2, 0x9D, 0x70, 0x27, 0x9B, 0xC4, + 0xCD, 0x91, 0x4B, 0xB4, 0x18, 0xE1, 0x3D, 0x5D, + 0x7A, 0xEA, 0xF0, 0x65, 0xB9, 0xF6, 0xC3, 0x66, + 0x21, 0x96, 0xD1, 0xB8, 0x56, 0x62, 0x48, 0x28, + 0x3A, 0x86, 0x63, 0xD4, 0xD7, 0x41, 0x8D, 0x20, + 0xC2, 0x98, 0x37, 0xD8, 0x85, 0x42, 0x0D, 0x31, + 0x84, 0x4E, 0x11, 0x46, 0x2B, 0x19, 0xCC, 0xB7, + 0x69, 0x13, 0x6B, 0x29, 0x38, 0x7E, 0x0E, 0xD2, + 0x3B, 0x60, 0x89, 0x7F, 0xEF, 0x07, 0x08, 0xCA, + 0xBF, 0x3E, 0xA3, 0xAA, 0x52, 0x4A, 0x45, 0x00, + 0xC7, 0xF8, 0x57, 0xEB, 0x93, 0x9C, 0x4D, 0x7B, + 0x2C, 0xBB, 0xFB, 0xFF, 0x35, 0x4F, 0x32, 0xA6, + 0x23, 0x8A, 0xDD, 0x12, 0xA4, 0x81, 0x17, 0x1D, + 0x1F, 0xCB, 0x0A, 0x71, 0x02, 0xAC, 0xDF, 0x24, + 0xAB, 0x7D, 0x30, 0x5C, 0x01, 0x5A, 0xBA, 0xEC, + 0x51, 0xF5, 0x0B, 0x64, 0xCE, 0xAE, 0x5B, 0x50, + 0x80, 0x88, 0xE8, 0x5F, 0x04, 0xDA, 0xE4, 0xBC, + 0x83, 0x25, 0x9F, 0xD9, 0x99, 0xC1, 0xFD, 0xB3 }; + +unsigned char table_98[256] = { + 0xC8, 0xE6, 0x38, 0x93, 0xE5, 0x03, 0x18, 0x1F, + 0xE9, 0x5A, 0xB6, 0xAF, 0xC3, 0x95, 0x00, 0x51, + 0xC0, 0xFD, 0x32, 0xE8, 0x96, 0x57, 0xF0, 0xAA, + 0xDC, 0x71, 0xF8, 0x01, 0x40, 0x0A, 0x4F, 0xB0, + 0x1B, 0x9D, 0x16, 0x92, 0xF3, 0x5E, 0xA9, 0x3C, + 0xBE, 0x6A, 0xA7, 0xE3, 0x35, 0x0D, 0xAD, 0xDB, + 0x48, 0xE0, 0x7E, 0xC6, 0xB4, 0x6D, 0x17, 0x41, + 0x3E, 0xE2, 0x87, 0x12, 0xE1, 0x53, 0xD9, 0x8A, + 0xAC, 0xA6, 0xD8, 0xFA, 0x36, 0x0B, 0x06, 0xDF, + 0x6C, 0x4E, 0xA4, 0xBC, 0xC9, 0xEE, 0x44, 0x26, + 0xF2, 0xE4, 0x9E, 0x34, 0xEF, 0x05, 0x0F, 0x7F, + 0xD1, 0xCD, 0x67, 0x28, 0xC1, 0x8E, 0x7D, 0x90, + 0x8F, 0x60, 0x1E, 0x19, 0xBD, 0x77, 0xB8, 0xD5, + 0x3D, 0x8C, 0x31, 0x99, 0x08, 0xDD, 0x04, 0x30, + 0x61, 0xFB, 0xEB, 0x98, 0x15, 0xFC, 0x10, 0xDE, + 0x20, 0xBA, 0xA1, 0xB3, 0xD4, 0x91, 0x6F, 0x9F, + 0x94, 0x5B, 0x42, 0xCB, 0x75, 0x1C, 0xBB, 0x5C, + 0x5D, 0xD6, 0x66, 0x50, 0xB9, 0xF1, 0x82, 0x7B, + 0x33, 0x23, 0x4A, 0xA5, 0x55, 0x97, 0xEA, 0x37, + 0xF4, 0x64, 0x6E, 0xBF, 0x8B, 0xB1, 0x07, 0x9A, + 0x43, 0x11, 0x65, 0xC2, 0x02, 0xDA, 0x9B, 0x25, + 0xCA, 0x3B, 0x7A, 0xCE, 0xA8, 0xCF, 0xF7, 0x56, + 0x6B, 0xF9, 0x47, 0x2A, 0x2E, 0x1D, 0x2D, 0xE7, + 0x46, 0xD0, 0x62, 0x4C, 0x80, 0x4B, 0x2B, 0xF5, + 0x69, 0x9C, 0x45, 0xED, 0x83, 0xAB, 0x74, 0x39, + 0xA3, 0x85, 0xD7, 0x5F, 0xB2, 0x86, 0x22, 0x29, + 0x89, 0x49, 0x1A, 0xC4, 0x52, 0xEC, 0x8D, 0x73, + 0xD3, 0x7C, 0x79, 0xD2, 0x14, 0x4D, 0x84, 0xA2, + 0x0E, 0x70, 0x78, 0x72, 0xB7, 0xA0, 0xC5, 0x81, + 0x58, 0x0C, 0x68, 0x27, 0xFF, 0xF6, 0xAE, 0xCC, + 0x88, 0xFE, 0x24, 0x2F, 0x76, 0x3F, 0x59, 0x21, + 0x54, 0x3A, 0x13, 0x09, 0x2C, 0xB5, 0xC7, 0x63 }; + +unsigned char table_99[32] = { + 0x19, 0x00, 0x10, 0x18, 0x09, 0x11, 0x13, 0x1D, + 0x08, 0x1A, 0x02, 0x05, 0x03, 0x17, 0x12, 0x01, + 0x1F, 0x14, 0x06, 0x07, 0x15, 0x0D, 0x0F, 0x0B, + 0x0E, 0x16, 0x1E, 0x04, 0x1B, 0x0A, 0x0C, 0x1C }; + +unsigned char table_100[256] = { + 0x9B, 0x3A, 0xAE, 0x60, 0x27, 0x67, 0x1E, 0x4E, + 0x91, 0xDA, 0x85, 0x43, 0x5C, 0xCC, 0x89, 0x55, + 0x75, 0x56, 0xF2, 0x86, 0xEB, 0xC4, 0x0D, 0xE6, + 0x63, 0x88, 0x38, 0x59, 0x68, 0xD0, 0x18, 0xF0, + 0xBA, 0x28, 0xF5, 0x80, 0x02, 0x5B, 0xE1, 0xA4, + 0x7A, 0x4B, 0x8E, 0xF7, 0x9E, 0x99, 0x70, 0xEF, + 0x66, 0x50, 0xB1, 0xCD, 0x9A, 0xAF, 0x5F, 0x21, + 0xE5, 0x5D, 0x14, 0xD4, 0x34, 0x22, 0xC3, 0x0F, + 0x44, 0xB6, 0x92, 0xCE, 0xB4, 0x6E, 0xB0, 0x00, + 0xF9, 0xB5, 0x10, 0xEA, 0x45, 0x2F, 0x2B, 0xF4, + 0xF6, 0xFE, 0xCB, 0x0A, 0x42, 0xF8, 0xE7, 0xFD, + 0xC8, 0xC2, 0x6C, 0x9C, 0x57, 0xA1, 0x46, 0x04, + 0xE9, 0x97, 0x40, 0x32, 0x19, 0xFA, 0x51, 0xD1, + 0x6D, 0x4C, 0x2A, 0xD9, 0x95, 0x26, 0x72, 0x1B, + 0x83, 0x93, 0x5A, 0x15, 0x33, 0xC5, 0x77, 0x13, + 0xE0, 0x36, 0x37, 0xDB, 0xA7, 0xC7, 0x81, 0x62, + 0xC1, 0x47, 0x64, 0x74, 0x1D, 0x84, 0x29, 0x39, + 0x41, 0x35, 0x09, 0x90, 0x20, 0x9F, 0x8C, 0x7D, + 0x3E, 0x07, 0xB9, 0x76, 0x06, 0xA3, 0x31, 0x7F, + 0x49, 0x6F, 0x3D, 0xD5, 0x25, 0xAC, 0xDF, 0x0B, + 0x3C, 0x79, 0x01, 0x8F, 0x82, 0x2E, 0xFC, 0x98, + 0xA5, 0x58, 0xA0, 0x4A, 0x7C, 0x24, 0xDD, 0x05, + 0x4D, 0x12, 0xBC, 0xAA, 0xE2, 0xAB, 0xD3, 0xBF, + 0x94, 0x2D, 0x54, 0xBB, 0xAD, 0xB7, 0x6A, 0xE3, + 0xBD, 0x5E, 0x8D, 0x08, 0x3B, 0xB8, 0x73, 0x8A, + 0x16, 0xD2, 0x69, 0xE8, 0xEE, 0x53, 0xD8, 0xDC, + 0x48, 0xCF, 0xC6, 0xA9, 0x1A, 0xCA, 0x17, 0x11, + 0xED, 0xC0, 0xA6, 0x1F, 0x96, 0x8B, 0xFF, 0x78, + 0x03, 0x61, 0x1C, 0xA8, 0x3F, 0x9D, 0x0E, 0xC9, + 0xE4, 0xA2, 0x52, 0xEC, 0x4F, 0xD6, 0xF3, 0x6B, + 0x87, 0xB3, 0x7E, 0xDE, 0xD7, 0x71, 0x65, 0xF1, + 0x30, 0x0C, 0xB2, 0x7B, 0xBE, 0xFB, 0x23, 0x2C }; + +unsigned char table_101[32] = { + 0x18, 0x08, 0x14, 0x17, 0x03, 0x10, 0x19, 0x04, + 0x0D, 0x1C, 0x06, 0x1D, 0x1E, 0x12, 0x11, 0x0B, + 0x0F, 0x02, 0x0E, 0x1B, 0x13, 0x05, 0x07, 0x16, + 0x15, 0x0A, 0x0C, 0x1A, 0x00, 0x01, 0x1F, 0x09 }; + +unsigned char table_102[32] = { + 0x17, 0x1F, 0x0E, 0x05, 0x13, 0x0C, 0x14, 0x1A, + 0x0F, 0x01, 0x12, 0x1C, 0x00, 0x07, 0x0D, 0x02, + 0x10, 0x16, 0x04, 0x11, 0x1D, 0x03, 0x1E, 0x18, + 0x06, 0x15, 0x0A, 0x19, 0x09, 0x08, 0x1B, 0x0B }; + +unsigned char table_103[32] = { + 0x0F, 0x09, 0x1E, 0x11, 0x0D, 0x08, 0x10, 0x00, + 0x01, 0x1F, 0x1D, 0x1C, 0x12, 0x04, 0x07, 0x05, + 0x19, 0x14, 0x1B, 0x02, 0x1A, 0x15, 0x17, 0x16, + 0x18, 0x0B, 0x0A, 0x13, 0x0C, 0x0E, 0x03, 0x06 }; + +unsigned char table_104[256] = { + 0xA4, 0x9F, 0x78, 0x39, 0x3D, 0x81, 0x51, 0x24, + 0x46, 0x2A, 0x56, 0xE8, 0xDF, 0x73, 0xA8, 0xA2, + 0x0D, 0xDC, 0xA5, 0x4F, 0xF0, 0x93, 0xC0, 0x76, + 0x38, 0x70, 0xB0, 0x30, 0x98, 0x13, 0x8B, 0x14, + 0x26, 0x45, 0x0F, 0x7D, 0x34, 0x72, 0x6B, 0x89, + 0x43, 0xE2, 0x96, 0x5B, 0xEF, 0x2B, 0xF9, 0xDE, + 0x82, 0xB5, 0x61, 0x4A, 0x17, 0xC2, 0x5A, 0xCB, + 0xB2, 0x8D, 0xE4, 0xEC, 0xD9, 0x80, 0xBC, 0x62, + 0x67, 0x11, 0xA9, 0x3A, 0xE1, 0xC4, 0xEA, 0xD2, + 0x71, 0xD0, 0xDB, 0xE5, 0x7B, 0x08, 0x77, 0xD6, + 0x10, 0x19, 0x48, 0xEB, 0xAA, 0x2C, 0x0C, 0x59, + 0xBE, 0xF6, 0x28, 0x50, 0x90, 0x87, 0xCD, 0x04, + 0x1F, 0x79, 0x99, 0x5C, 0x49, 0x06, 0x8A, 0x3E, + 0x5F, 0x5E, 0x15, 0x23, 0x2D, 0xB6, 0xA6, 0x7A, + 0x03, 0x20, 0xDA, 0xFB, 0x35, 0x75, 0xC7, 0x47, + 0xB9, 0x7C, 0xA1, 0xCE, 0xC5, 0xDD, 0xFD, 0x6C, + 0x05, 0xAC, 0x09, 0xB4, 0x95, 0xD1, 0xB1, 0x63, + 0xFF, 0xAE, 0xD5, 0x25, 0x1E, 0x6E, 0x57, 0x18, + 0x74, 0xE6, 0x2F, 0x9A, 0xE7, 0x42, 0x65, 0xF5, + 0x58, 0x27, 0x33, 0x9C, 0xCF, 0xB7, 0xC3, 0xF1, + 0x12, 0x1D, 0xB8, 0xF4, 0x64, 0x4D, 0xD4, 0xBD, + 0xE3, 0xAB, 0x44, 0x60, 0xAF, 0xCC, 0x0A, 0xFC, + 0xD3, 0x21, 0x0B, 0x1A, 0x6D, 0x83, 0xA7, 0x8E, + 0x3C, 0xC1, 0xED, 0xF3, 0x2E, 0x86, 0xC9, 0x41, + 0x02, 0xF7, 0xC8, 0x40, 0x1B, 0xF8, 0xF2, 0x07, + 0x5D, 0x4E, 0xC6, 0x29, 0xD7, 0x4B, 0x7E, 0x31, + 0x94, 0x32, 0x01, 0x92, 0xE9, 0x36, 0x0E, 0x7F, + 0x85, 0x16, 0xFA, 0x00, 0x88, 0x3F, 0x68, 0x4C, + 0x22, 0x55, 0xBF, 0x9D, 0xE0, 0x6A, 0xAD, 0xBA, + 0x91, 0xCA, 0xA3, 0x1C, 0xEE, 0xD8, 0x3B, 0x66, + 0x69, 0x9B, 0x84, 0xA0, 0xB3, 0x6F, 0xFE, 0x52, + 0x97, 0xBB, 0x37, 0x8C, 0x54, 0x53, 0x9E, 0x8F }; + +unsigned char table_105[256] = { + 0x7B, 0x35, 0x11, 0x79, 0x07, 0x2F, 0xF6, 0x82, + 0x8E, 0xB4, 0x6E, 0xD2, 0x6D, 0xC5, 0x8C, 0x1C, + 0xE0, 0xD6, 0x34, 0xF0, 0x4F, 0x25, 0x59, 0xE8, + 0xDF, 0x1D, 0xEB, 0x32, 0x86, 0x51, 0xA4, 0xF2, + 0x5C, 0xD1, 0xC8, 0x41, 0xEC, 0x9D, 0x62, 0xAC, + 0xDD, 0x3E, 0xB8, 0x65, 0x75, 0x89, 0x12, 0x6C, + 0x40, 0x4E, 0xC7, 0x27, 0xE1, 0x37, 0xCF, 0x09, + 0x16, 0x78, 0xAA, 0x58, 0x0D, 0xE6, 0x54, 0xFE, + 0x8F, 0xFD, 0xF9, 0x61, 0x26, 0x3F, 0x2E, 0xCD, + 0x2C, 0x04, 0xB2, 0x80, 0x0F, 0x14, 0x6F, 0xC6, + 0xAB, 0xFB, 0x13, 0xDB, 0x9A, 0x21, 0xB3, 0xC0, + 0xA9, 0x19, 0x70, 0xF3, 0x2B, 0xAE, 0x9B, 0x49, + 0xB7, 0xA8, 0x24, 0x1B, 0x48, 0xEA, 0xED, 0xD9, + 0x47, 0x9E, 0x9C, 0x69, 0x3C, 0x66, 0xBB, 0x06, + 0x46, 0x38, 0x17, 0xB5, 0xCB, 0x05, 0x4A, 0x5E, + 0x15, 0x20, 0xB9, 0xB6, 0x33, 0x4C, 0x7D, 0xA3, + 0xD7, 0xB1, 0x23, 0x72, 0xC3, 0x4B, 0x63, 0xBE, + 0xF7, 0x5B, 0x74, 0x64, 0x77, 0xCC, 0xD3, 0x85, + 0xDE, 0x1A, 0x31, 0x97, 0xA2, 0x8B, 0xFC, 0x10, + 0x5F, 0xDC, 0xD5, 0xB0, 0xBD, 0x55, 0xC1, 0xE7, + 0x0C, 0x50, 0x43, 0x39, 0x71, 0x52, 0xE5, 0xAF, + 0x8A, 0x60, 0x92, 0x2D, 0xD8, 0x03, 0xF5, 0x28, + 0xCA, 0xEF, 0xD0, 0xC2, 0x53, 0x91, 0xA6, 0x73, + 0x56, 0xA5, 0xF1, 0x57, 0x42, 0xF4, 0xD4, 0x36, + 0x8D, 0xBC, 0xE9, 0x7E, 0x02, 0x76, 0x18, 0x0B, + 0x84, 0x5A, 0xE2, 0xBF, 0x68, 0x95, 0x29, 0x98, + 0xAD, 0x88, 0x1F, 0x81, 0x67, 0xA1, 0x3A, 0xA7, + 0x22, 0xF8, 0x01, 0xA0, 0xCE, 0x7A, 0xDA, 0x30, + 0xC4, 0xE4, 0xEE, 0x7C, 0x3B, 0x4D, 0x3D, 0xE3, + 0xFA, 0x6A, 0x7F, 0x99, 0x00, 0x93, 0x0E, 0xFF, + 0x90, 0x0A, 0x2A, 0x5D, 0x96, 0x08, 0x6B, 0x83, + 0xBA, 0x1E, 0x44, 0x87, 0x45, 0x9F, 0xC9, 0x94 }; + +unsigned char table_106[32] = { + 0x03, 0x11, 0x07, 0x1B, 0x0F, 0x14, 0x0C, 0x01, + 0x04, 0x02, 0x09, 0x0A, 0x05, 0x12, 0x06, 0x1F, + 0x1C, 0x0E, 0x0D, 0x15, 0x18, 0x08, 0x00, 0x10, + 0x1E, 0x1D, 0x17, 0x19, 0x13, 0x16, 0x0B, 0x1A }; + +unsigned char table_107[32] = { + 0x13, 0x1B, 0x06, 0x11, 0x1C, 0x07, 0x08, 0x0E, + 0x10, 0x05, 0x09, 0x18, 0x04, 0x15, 0x1E, 0x0F, + 0x1F, 0x12, 0x02, 0x00, 0x17, 0x19, 0x1A, 0x0D, + 0x03, 0x0C, 0x0A, 0x1D, 0x14, 0x01, 0x16, 0x0B }; + +unsigned char table_108[256] = { + 0x99, 0xA3, 0x48, 0xE8, 0x5A, 0x7D, 0x97, 0xCA, + 0x7F, 0x06, 0x9B, 0x04, 0xE0, 0xF3, 0x18, 0xAE, + 0x59, 0xA0, 0x2B, 0x15, 0x85, 0x3E, 0x12, 0x93, + 0x3D, 0x28, 0x32, 0xF5, 0x20, 0x5D, 0x86, 0x00, + 0x1B, 0x2E, 0x36, 0x10, 0x5E, 0x6C, 0xD8, 0x29, + 0xB6, 0x3F, 0x05, 0x1C, 0xCE, 0xC2, 0x34, 0x5F, + 0x5C, 0x79, 0xD1, 0x1F, 0xA2, 0xEE, 0x8A, 0x69, + 0xB5, 0x87, 0x96, 0x6D, 0x4D, 0xC1, 0x61, 0x2C, + 0x11, 0xE7, 0x8E, 0xBF, 0x1E, 0x53, 0xD0, 0x58, + 0x76, 0xA4, 0x60, 0xA9, 0xB0, 0xF9, 0xEA, 0x3C, + 0x52, 0x9A, 0x24, 0xF1, 0x9F, 0xD3, 0x40, 0x0A, + 0x63, 0x78, 0x6A, 0x8B, 0x08, 0x22, 0x16, 0x83, + 0x6B, 0xD2, 0x49, 0x19, 0xBD, 0xFD, 0x62, 0x72, + 0xA8, 0x55, 0xAB, 0x0C, 0xB9, 0x13, 0xD5, 0xF0, + 0xF2, 0x84, 0xAF, 0x2F, 0x7B, 0x2A, 0x21, 0x0F, + 0xDA, 0x30, 0x71, 0xD6, 0x81, 0xE6, 0xEC, 0x41, + 0x90, 0x50, 0x66, 0x0E, 0xA7, 0xB8, 0xF7, 0x3A, + 0xB2, 0xCF, 0x3B, 0xFC, 0x56, 0x6F, 0xC3, 0xA6, + 0xC9, 0xA1, 0x8D, 0xBB, 0x9D, 0x75, 0xF6, 0xAA, + 0x7E, 0xF8, 0x33, 0xEF, 0xBC, 0x7C, 0x23, 0x1A, + 0x92, 0x6E, 0x2D, 0x8F, 0xED, 0xB7, 0xB1, 0x1D, + 0x67, 0x39, 0xAC, 0x0D, 0x74, 0xDB, 0x7A, 0x94, + 0x07, 0x09, 0xC0, 0xD7, 0xAD, 0xFE, 0x54, 0x91, + 0xDE, 0x45, 0xA5, 0x77, 0xCB, 0x37, 0xC6, 0x38, + 0x89, 0x88, 0x17, 0xD9, 0x4F, 0xDF, 0x25, 0xFB, + 0xFA, 0x4C, 0x80, 0x35, 0x82, 0xF4, 0x95, 0xC8, + 0xFF, 0xE9, 0x31, 0x01, 0x14, 0xB3, 0x02, 0x9E, + 0x4E, 0x43, 0x46, 0xC7, 0xEB, 0x51, 0xE5, 0x47, + 0xB4, 0xE3, 0xDC, 0x57, 0xC4, 0x98, 0x03, 0xE1, + 0xBA, 0x68, 0xCD, 0x27, 0xC5, 0x0B, 0xD4, 0x64, + 0x4B, 0x9C, 0x70, 0x65, 0x4A, 0xE4, 0x42, 0xDD, + 0xCC, 0xE2, 0x44, 0x73, 0xBE, 0x26, 0x8C, 0x5B }; + +unsigned char table_109[256] = { + 0xE3, 0x95, 0xDB, 0x09, 0x82, 0x0A, 0x8F, 0x9E, + 0xC9, 0xDC, 0x28, 0x35, 0x0F, 0x8B, 0xA8, 0xA5, + 0x7F, 0x3D, 0x8C, 0xD1, 0x93, 0x57, 0x04, 0xAA, + 0x6A, 0x98, 0x81, 0xDD, 0x16, 0x67, 0x2E, 0xDF, + 0xED, 0xF7, 0xB2, 0xBD, 0x14, 0xB6, 0x76, 0xC8, + 0x75, 0x9F, 0x48, 0xAE, 0xBB, 0xB0, 0xF3, 0xE2, + 0xD4, 0x59, 0xD8, 0x9C, 0x64, 0xC1, 0x73, 0x21, + 0x6D, 0x96, 0x7B, 0x62, 0x56, 0x55, 0xCC, 0xFD, + 0xCE, 0x41, 0xA3, 0x43, 0x33, 0xAF, 0x23, 0x9D, + 0x6F, 0x65, 0x19, 0x52, 0xAD, 0xC6, 0xD3, 0x3F, + 0x66, 0xFF, 0xD0, 0x30, 0x6C, 0xC0, 0xEB, 0xCF, + 0x51, 0x88, 0x38, 0x72, 0x69, 0x77, 0x3B, 0xFA, + 0xBA, 0xB7, 0xA1, 0x91, 0xE0, 0x89, 0xAB, 0x44, + 0x1B, 0x05, 0x5B, 0xB9, 0x71, 0x47, 0x7E, 0xFB, + 0x02, 0xC7, 0x99, 0x6E, 0x42, 0x20, 0x90, 0x1F, + 0x4A, 0x85, 0x1A, 0xEA, 0x0C, 0x0D, 0xB3, 0xDA, + 0xE7, 0x13, 0xE6, 0xD7, 0x6B, 0x12, 0x46, 0x53, + 0xB5, 0xF8, 0x1D, 0x83, 0x54, 0x49, 0x8A, 0x26, + 0x4D, 0xDE, 0xF6, 0x03, 0xA2, 0x7D, 0x0E, 0xA0, + 0x68, 0x79, 0xCA, 0x0B, 0x5D, 0x40, 0x4F, 0x80, + 0xC2, 0xD6, 0x87, 0x70, 0xF0, 0xD2, 0x92, 0xEE, + 0xBE, 0x74, 0x5F, 0xBC, 0xA4, 0x4B, 0xFE, 0x37, + 0x60, 0xA9, 0x06, 0xA7, 0xE1, 0xF5, 0x2B, 0x10, + 0xEF, 0x2C, 0x07, 0x86, 0x7A, 0x27, 0xE9, 0xC5, + 0xAC, 0x32, 0x22, 0xF2, 0xE5, 0x8D, 0x31, 0x01, + 0x34, 0xA6, 0xB8, 0xC3, 0x3C, 0xE4, 0x08, 0x94, + 0x15, 0x4E, 0xB4, 0x39, 0x58, 0x00, 0x3E, 0x29, + 0x45, 0x3A, 0x84, 0x36, 0xF1, 0x2A, 0x50, 0x11, + 0xC4, 0x5A, 0xFC, 0xBF, 0xD9, 0xF9, 0x17, 0x9B, + 0x8E, 0x18, 0x63, 0x4C, 0x2F, 0x78, 0x2D, 0x5E, + 0x9A, 0xCD, 0x24, 0xEC, 0x7C, 0x97, 0x61, 0xCB, + 0x1E, 0xF4, 0xD5, 0xB1, 0x5C, 0x25, 0xE8, 0x1C }; + +unsigned char table_110[256] = { + 0xC3, 0x06, 0x3C, 0xCB, 0xD2, 0x44, 0x9D, 0x48, + 0x28, 0xAA, 0xA9, 0xD0, 0x64, 0x25, 0x56, 0xCA, + 0xC2, 0xF8, 0x5C, 0xAE, 0x4E, 0x63, 0xB2, 0xE9, + 0x35, 0x11, 0xA8, 0x1A, 0x76, 0x15, 0xE0, 0x26, + 0x97, 0x99, 0xD4, 0x43, 0x80, 0xEE, 0xC1, 0x69, + 0xA6, 0x1E, 0x7A, 0x42, 0x55, 0x38, 0xBF, 0x75, + 0x0E, 0x29, 0xF5, 0xF3, 0x36, 0x7D, 0x51, 0xE8, + 0xE5, 0xEB, 0x68, 0x60, 0x0C, 0x70, 0xFD, 0xCC, + 0xE3, 0x23, 0x09, 0x6D, 0x2D, 0x6C, 0x5E, 0xB6, + 0x98, 0x8B, 0x1F, 0x50, 0x34, 0x8D, 0x10, 0x92, + 0x82, 0x85, 0xD5, 0x79, 0x02, 0xA4, 0x0A, 0xBC, + 0x40, 0xC6, 0xA3, 0x72, 0x8F, 0xC4, 0xA5, 0xE4, + 0x49, 0xD6, 0xCE, 0xA1, 0x12, 0x4F, 0x30, 0x31, + 0xDE, 0x2A, 0xF7, 0x95, 0xB5, 0x96, 0x14, 0x08, + 0xE6, 0x3D, 0x86, 0xF2, 0x47, 0x74, 0xB8, 0x5D, + 0x1D, 0x2B, 0x3A, 0x93, 0x7C, 0x6A, 0x01, 0xA0, + 0x9A, 0x4D, 0xB7, 0x71, 0xA7, 0x41, 0xC5, 0x65, + 0xC8, 0x89, 0xD1, 0x3E, 0x0D, 0xD8, 0xFF, 0x6F, + 0x7F, 0xA2, 0xFE, 0xD9, 0xF0, 0x4A, 0x07, 0x1C, + 0x0F, 0x6E, 0x03, 0x81, 0x1B, 0x05, 0xDF, 0x52, + 0xF1, 0x8A, 0xF9, 0xDD, 0x91, 0x3B, 0xD7, 0xE1, + 0x54, 0xAD, 0x90, 0x5A, 0x7B, 0xC7, 0x32, 0x62, + 0x16, 0x27, 0xB9, 0x66, 0x21, 0x88, 0xBD, 0x18, + 0x77, 0x8E, 0x94, 0x8C, 0x9B, 0x46, 0x9C, 0xB1, + 0xD3, 0x53, 0xB0, 0xBE, 0xAC, 0xAF, 0x73, 0x24, + 0xDA, 0x58, 0xE2, 0xFC, 0x78, 0xEA, 0xCD, 0xFA, + 0x37, 0xED, 0x13, 0x19, 0xC0, 0x59, 0x83, 0xBA, + 0x3F, 0x57, 0x00, 0x7E, 0xC9, 0x2E, 0x17, 0x5B, + 0x84, 0xF6, 0xE7, 0x22, 0xFB, 0x5F, 0x4C, 0x2C, + 0x61, 0x9F, 0x45, 0x39, 0xB3, 0xEC, 0x04, 0x87, + 0x67, 0xDC, 0x0B, 0xF4, 0x20, 0xAB, 0x6B, 0x9E, + 0x4B, 0xCF, 0xB4, 0x2F, 0xBB, 0xEF, 0xDB, 0x33 }; + +unsigned char table_111[32] = { + 0x09, 0x0F, 0x00, 0x15, 0x12, 0x17, 0x1A, 0x0D, + 0x1C, 0x0B, 0x01, 0x0A, 0x05, 0x1E, 0x1D, 0x0C, + 0x1B, 0x08, 0x19, 0x18, 0x14, 0x07, 0x0E, 0x03, + 0x10, 0x16, 0x11, 0x1F, 0x04, 0x06, 0x02, 0x13 }; + +unsigned char table_112[256] = { + 0xF9, 0x7D, 0xBE, 0xD5, 0x9F, 0xB8, 0x95, 0x43, + 0xDB, 0xAE, 0x7E, 0xEC, 0x5B, 0x58, 0x18, 0x49, + 0x4B, 0x9D, 0x1C, 0x3E, 0x61, 0xD1, 0xF6, 0x2F, + 0x41, 0x82, 0x51, 0x37, 0x72, 0x79, 0x05, 0x2A, + 0xC2, 0xB0, 0xE2, 0xE7, 0xB2, 0xF3, 0x1B, 0x92, + 0x86, 0xBB, 0xDC, 0x90, 0x1A, 0x19, 0xD7, 0xBA, + 0x2C, 0x7B, 0xEF, 0xC7, 0x8A, 0x81, 0xEB, 0xDE, + 0x73, 0x4E, 0xB7, 0x97, 0xCA, 0x29, 0x85, 0xC1, + 0xA5, 0x7F, 0xFE, 0x56, 0xE9, 0x9E, 0x21, 0x76, + 0x3A, 0x88, 0x70, 0xC6, 0xD3, 0x8C, 0x47, 0xC8, + 0x83, 0x48, 0xC3, 0x6A, 0x9C, 0x80, 0x53, 0xBD, + 0xFD, 0x54, 0x09, 0x91, 0x94, 0xAA, 0x7A, 0x59, + 0x71, 0xDD, 0xA8, 0x07, 0xCB, 0x0F, 0xE0, 0x9A, + 0x36, 0x4C, 0x4D, 0x0D, 0xA4, 0x96, 0x6F, 0x14, + 0x22, 0x38, 0xAD, 0x02, 0xF4, 0x0B, 0xEA, 0x93, + 0x20, 0x04, 0xBC, 0xE8, 0x6C, 0xFB, 0x10, 0x6B, + 0x40, 0xB6, 0x24, 0x17, 0x06, 0x31, 0xD9, 0x33, + 0xF5, 0x99, 0x57, 0xCD, 0xAB, 0x67, 0x5C, 0x30, + 0x1E, 0x34, 0xB4, 0x3F, 0x16, 0x42, 0xA2, 0x68, + 0x27, 0xB3, 0x1D, 0xED, 0x5F, 0x52, 0xF7, 0x3C, + 0x65, 0x5D, 0xE5, 0x23, 0x0C, 0x6D, 0x84, 0x6E, + 0xDA, 0x77, 0xF8, 0x15, 0xFA, 0x69, 0xD0, 0xA7, + 0x11, 0xAC, 0xA6, 0xA3, 0x1F, 0x2E, 0xBF, 0x4A, + 0x8F, 0xFC, 0xEE, 0xC9, 0x26, 0x12, 0xC0, 0xB1, + 0x45, 0x0E, 0x3D, 0x7C, 0xCE, 0x13, 0x8E, 0x98, + 0x46, 0x2B, 0xC5, 0x66, 0x28, 0x32, 0xD2, 0x03, + 0xE3, 0xC4, 0x9B, 0x89, 0x5E, 0xF0, 0xCF, 0x3B, + 0x2D, 0x50, 0xB5, 0x00, 0x0A, 0xD6, 0x55, 0xE1, + 0x62, 0x63, 0x64, 0x87, 0xAF, 0x78, 0xB9, 0xF2, + 0x25, 0x44, 0xFF, 0x39, 0xF1, 0x08, 0x4F, 0x74, + 0xA9, 0x8B, 0x75, 0x01, 0xA0, 0xE4, 0x35, 0x8D, + 0xA1, 0xCC, 0xDF, 0x60, 0xD8, 0x5A, 0xE6, 0xD4 }; + +unsigned char table_113[256] = { + 0x46, 0x9D, 0x39, 0xB2, 0x8D, 0x3B, 0x59, 0x5A, + 0xD0, 0x9C, 0xE4, 0x04, 0x01, 0xE2, 0xB3, 0xD2, + 0xD7, 0x18, 0x40, 0xD8, 0xF1, 0xEF, 0x3A, 0x1D, + 0x8E, 0xE5, 0xD9, 0xD3, 0xCB, 0x49, 0x4C, 0xCF, + 0xC0, 0xD6, 0xB5, 0x73, 0x77, 0x82, 0x54, 0xA2, + 0xB1, 0xB0, 0x84, 0x5D, 0xC7, 0xDE, 0x31, 0x2F, + 0x50, 0x78, 0xBE, 0x94, 0x64, 0x44, 0x60, 0x7A, + 0x1A, 0x6E, 0x09, 0x6F, 0xBF, 0x76, 0x81, 0x38, + 0x22, 0xC3, 0xEE, 0x8F, 0xFB, 0x32, 0xED, 0x92, + 0xAE, 0xE6, 0x5F, 0xAA, 0xAC, 0x0D, 0xA3, 0x47, + 0x1F, 0x11, 0xC1, 0x29, 0xAF, 0xFD, 0x1C, 0xDB, + 0x00, 0x23, 0xB9, 0xB8, 0x91, 0x41, 0x27, 0x37, + 0x43, 0x02, 0x26, 0xF6, 0x7D, 0x0A, 0x85, 0x93, + 0x97, 0x2E, 0x20, 0x55, 0x13, 0x4B, 0x6C, 0xE7, + 0xFC, 0x25, 0xFA, 0x9E, 0x5B, 0xA1, 0xDF, 0x2C, + 0x3E, 0xBC, 0xEA, 0x42, 0x7C, 0x36, 0x30, 0xEB, + 0xBD, 0x8B, 0x87, 0x16, 0x3D, 0x5C, 0x07, 0xBA, + 0xB4, 0x1B, 0xC2, 0xE3, 0x71, 0x9A, 0x5E, 0x4D, + 0xF2, 0xCC, 0x0E, 0xE1, 0x34, 0x75, 0x58, 0x89, + 0x17, 0xD4, 0x68, 0x80, 0x2B, 0x74, 0x70, 0x8A, + 0x63, 0xE8, 0x56, 0x24, 0xD1, 0x57, 0x35, 0x6D, + 0x3C, 0xA6, 0xC8, 0x7E, 0xA8, 0x4E, 0xC4, 0x33, + 0xA9, 0x62, 0x61, 0x7F, 0x21, 0x98, 0x2A, 0xAD, + 0xB6, 0xA7, 0xF5, 0x3F, 0x15, 0x45, 0xF8, 0xA4, + 0x95, 0x88, 0xDC, 0x96, 0x90, 0x08, 0x9B, 0xF9, + 0x06, 0x14, 0x05, 0xF0, 0xF7, 0xA0, 0xE0, 0x65, + 0xCA, 0xA5, 0x9F, 0x79, 0xCD, 0x4F, 0x72, 0xB7, + 0x4A, 0x0F, 0x66, 0xC5, 0x0C, 0x52, 0xF3, 0x69, + 0x83, 0x03, 0x99, 0x1E, 0x2D, 0xDA, 0x8C, 0x53, + 0x28, 0xDD, 0xE9, 0x0B, 0xC9, 0xF4, 0x48, 0x12, + 0x6A, 0x19, 0xCE, 0xAB, 0x51, 0xD5, 0x6B, 0xBB, + 0xFE, 0x7B, 0x67, 0xFF, 0x10, 0xEC, 0xC6, 0x86 }; + +unsigned char table_114[32] = { + 0x11, 0x10, 0x04, 0x1D, 0x08, 0x15, 0x1A, 0x1B, + 0x14, 0x18, 0x0F, 0x17, 0x16, 0x07, 0x1E, 0x0E, + 0x12, 0x0A, 0x13, 0x0B, 0x0C, 0x00, 0x06, 0x02, + 0x1F, 0x19, 0x09, 0x1C, 0x01, 0x0D, 0x03, 0x05 }; + +unsigned char table_115[256] = { + 0xB7, 0xBB, 0x63, 0x0D, 0xF0, 0x33, 0x5A, 0x05, + 0xF2, 0x7F, 0x64, 0xDB, 0x51, 0xC9, 0x2C, 0x85, + 0x4F, 0x41, 0xA4, 0x42, 0xCF, 0xA6, 0x52, 0x2F, + 0x26, 0xEF, 0xFB, 0x29, 0x40, 0x16, 0xF7, 0xED, + 0x23, 0x69, 0x8A, 0xDF, 0x77, 0x28, 0x93, 0x14, + 0x82, 0x0C, 0xBE, 0x3D, 0x20, 0xB4, 0x79, 0x94, + 0x54, 0xF8, 0x07, 0xB1, 0xE1, 0x66, 0x73, 0xD3, + 0x19, 0x15, 0xFF, 0x03, 0x6A, 0x9A, 0xDC, 0x1C, + 0xB3, 0x5D, 0x76, 0x68, 0x47, 0x6C, 0xF9, 0xFD, + 0xE9, 0xDD, 0x01, 0x65, 0xBD, 0x80, 0x0E, 0x7A, + 0x8D, 0x99, 0x13, 0x7C, 0xA5, 0xA7, 0x1A, 0xCC, + 0xB8, 0xE6, 0x2B, 0xB2, 0xB6, 0xD0, 0x62, 0x2D, + 0x4D, 0xD2, 0xB9, 0x04, 0x46, 0xAE, 0xAA, 0x44, + 0xDA, 0x92, 0x4B, 0x4E, 0xC4, 0xE2, 0xFE, 0xA2, + 0x75, 0x7B, 0xC3, 0xFA, 0x9F, 0x37, 0x9D, 0x1E, + 0x72, 0xD4, 0x1F, 0x4A, 0x9B, 0xE5, 0x6D, 0xEC, + 0x5C, 0x7D, 0x98, 0xE8, 0xEE, 0x86, 0xD1, 0xC8, + 0xEA, 0x55, 0xBF, 0xAF, 0xDE, 0x32, 0x09, 0x3A, + 0x8F, 0x57, 0x83, 0x43, 0x61, 0xC6, 0x8E, 0x96, + 0x22, 0xA3, 0x97, 0x91, 0x5F, 0x11, 0x3B, 0x5B, + 0x1B, 0x34, 0x49, 0x95, 0xF1, 0x6F, 0x89, 0xA8, + 0xC0, 0x36, 0x0A, 0x3F, 0x60, 0x50, 0xE7, 0x08, + 0xCE, 0x25, 0xC1, 0x71, 0xF6, 0x59, 0x58, 0x56, + 0x4C, 0xAB, 0x27, 0xAC, 0x06, 0xCB, 0x00, 0x30, + 0x84, 0x3E, 0xC2, 0x1D, 0x02, 0xE0, 0xC5, 0xD6, + 0x18, 0x70, 0xA9, 0x88, 0xD9, 0x39, 0x8B, 0x6E, + 0xF4, 0x24, 0xA0, 0x48, 0x45, 0x21, 0x87, 0x78, + 0x38, 0x90, 0xE3, 0xCA, 0xF5, 0xD7, 0x2A, 0x53, + 0x9C, 0xCD, 0x31, 0x35, 0xAD, 0x74, 0xD8, 0x12, + 0xBC, 0x9E, 0x6B, 0x67, 0xB0, 0xBA, 0xE4, 0x10, + 0x5E, 0xFC, 0xC7, 0x0F, 0x2E, 0x81, 0x7E, 0xA1, + 0x8C, 0x17, 0xB5, 0xEB, 0xD5, 0xF3, 0x0B, 0x3C }; + +unsigned char table_116[32] = { + 0x00, 0x05, 0x10, 0x1C, 0x0C, 0x1A, 0x04, 0x1B, + 0x0A, 0x0D, 0x14, 0x0B, 0x07, 0x03, 0x12, 0x1E, + 0x06, 0x11, 0x01, 0x08, 0x15, 0x09, 0x1F, 0x0F, + 0x19, 0x18, 0x16, 0x02, 0x13, 0x0E, 0x17, 0x1D }; + +unsigned char table_117[256] = { + 0xD0, 0x9A, 0xAB, 0xA8, 0xA7, 0xDF, 0x28, 0xCE, + 0x3E, 0x51, 0xBF, 0x76, 0x03, 0xA0, 0x53, 0x3F, + 0x90, 0x93, 0x87, 0x67, 0x98, 0x3D, 0xEA, 0x8B, + 0x55, 0xCF, 0x10, 0xF3, 0x25, 0xFC, 0x9F, 0x41, + 0x6B, 0x54, 0x6E, 0x0B, 0x83, 0x35, 0x69, 0x7D, + 0xE0, 0x88, 0x4B, 0xE9, 0x1E, 0x96, 0x91, 0x57, + 0xBD, 0x72, 0x21, 0x3C, 0xA6, 0x99, 0x6C, 0xF6, + 0x13, 0xFA, 0x29, 0xED, 0xDB, 0x16, 0x4D, 0x07, + 0x45, 0xA5, 0xE3, 0x0E, 0x31, 0xBC, 0x56, 0x5C, + 0xB2, 0x23, 0xDA, 0x74, 0xFF, 0x02, 0x8F, 0xF4, + 0x2A, 0xC9, 0x89, 0xAA, 0x05, 0xB1, 0xD1, 0x1F, + 0x4F, 0xB0, 0x7A, 0x2C, 0x14, 0xD9, 0xE7, 0x66, + 0x62, 0x1A, 0x4C, 0xC0, 0xC6, 0x63, 0x7F, 0xB4, + 0xF1, 0x43, 0xFE, 0x61, 0xA3, 0xCC, 0xE8, 0x6D, + 0xBA, 0x65, 0x42, 0x2B, 0xCA, 0xD5, 0x52, 0x3A, + 0xCD, 0x1D, 0x24, 0xD7, 0x47, 0xDE, 0x9E, 0x95, + 0x85, 0x48, 0x86, 0xE1, 0xC5, 0xD2, 0x34, 0xAF, + 0x40, 0xFB, 0xE6, 0x4E, 0xC8, 0xF5, 0x7B, 0x5A, + 0xCB, 0xD4, 0x97, 0x6F, 0x0C, 0x79, 0x9C, 0x20, + 0x59, 0x19, 0x68, 0x2E, 0x09, 0x64, 0x73, 0x50, + 0xC2, 0x2F, 0x0D, 0xEF, 0x9D, 0x94, 0x00, 0x81, + 0xE2, 0x46, 0x5F, 0xB8, 0x0A, 0x12, 0x75, 0x1C, + 0x8C, 0xB6, 0x71, 0xAC, 0x04, 0x60, 0xA9, 0x5B, + 0xF8, 0x30, 0x49, 0x44, 0x4A, 0xBE, 0x6A, 0xEB, + 0xD3, 0xD8, 0x36, 0xB3, 0x3B, 0x17, 0x80, 0xA4, + 0xEC, 0x26, 0x82, 0xB5, 0x37, 0x5D, 0x1B, 0x2D, + 0xE5, 0xA2, 0x0F, 0xB7, 0xC4, 0xF2, 0x70, 0x39, + 0xF9, 0xC7, 0xBB, 0x8A, 0x32, 0x78, 0xC3, 0x5E, + 0xD6, 0xE4, 0x22, 0x9B, 0x18, 0x8E, 0xEE, 0x27, + 0x8D, 0x33, 0x11, 0x77, 0x01, 0x06, 0x38, 0xF0, + 0x7E, 0x08, 0x15, 0xB9, 0x7C, 0xAD, 0x84, 0xDD, + 0xC1, 0xFD, 0x92, 0xA1, 0xF7, 0xAE, 0xDC, 0x58 }; + +unsigned char table_118[256] = { + 0x38, 0xA0, 0xA6, 0xFC, 0x7C, 0x5A, 0x97, 0x1D, + 0xFD, 0x00, 0x20, 0xA2, 0x72, 0x10, 0x1F, 0x48, + 0x98, 0x7E, 0xDF, 0x2D, 0x80, 0x0A, 0x27, 0xDC, + 0xCF, 0xBF, 0x92, 0x94, 0x53, 0xCC, 0x0E, 0x74, + 0xA7, 0x60, 0x08, 0x15, 0x87, 0x6F, 0xB3, 0xA3, + 0xED, 0x59, 0x09, 0x4F, 0x9E, 0x9A, 0xEE, 0x83, + 0x56, 0x32, 0x34, 0xC7, 0x24, 0xE7, 0x96, 0x4D, + 0xAE, 0xE3, 0xBD, 0xE2, 0x36, 0x4A, 0xB6, 0x8B, + 0xF2, 0xC1, 0xD7, 0x40, 0x31, 0x4B, 0xDA, 0xF1, + 0xB1, 0x70, 0xA8, 0xC3, 0xC6, 0x8A, 0xE6, 0x77, + 0x21, 0x7D, 0xD5, 0x0C, 0x43, 0xC4, 0xF0, 0x1B, + 0x18, 0xA1, 0x85, 0xE1, 0xFF, 0x8D, 0xE5, 0x6E, + 0x9B, 0x51, 0x1C, 0xA4, 0x5C, 0x8E, 0x69, 0x49, + 0x23, 0xCD, 0x52, 0xF8, 0x3E, 0x91, 0x5E, 0x1E, + 0x25, 0xB4, 0x93, 0xCB, 0xE0, 0x47, 0xBC, 0x4E, + 0x33, 0xB7, 0x75, 0x1A, 0x11, 0x9C, 0x3F, 0xEC, + 0xD1, 0x46, 0xDD, 0xAA, 0xB8, 0x99, 0x86, 0x67, + 0x58, 0xF9, 0x16, 0x17, 0x6D, 0x5F, 0x2B, 0xA5, + 0xD3, 0x8F, 0x55, 0x71, 0xD2, 0xBA, 0x5B, 0x3C, + 0x82, 0xB5, 0x41, 0xE4, 0x90, 0x45, 0x6C, 0xF6, + 0xDE, 0xA9, 0x84, 0x62, 0x19, 0x3B, 0xB9, 0xC8, + 0x2C, 0xB0, 0x76, 0x57, 0xD8, 0x26, 0x9D, 0x89, + 0xC9, 0x54, 0xFB, 0x07, 0xCE, 0x22, 0x5D, 0x64, + 0x65, 0xAD, 0x01, 0xDB, 0x14, 0x4C, 0x37, 0x03, + 0x6B, 0xAF, 0xD0, 0x7F, 0x9F, 0xBB, 0xEB, 0xC0, + 0x50, 0x66, 0x68, 0x0B, 0x42, 0x2A, 0xD4, 0xF5, + 0x61, 0x63, 0xF3, 0x39, 0xBE, 0xC5, 0xEF, 0x28, + 0x3A, 0xAB, 0x79, 0x05, 0xE9, 0x12, 0x73, 0x3D, + 0xB2, 0x8C, 0xCA, 0x29, 0x0F, 0xF4, 0x7B, 0x13, + 0x88, 0x44, 0xC2, 0x2E, 0xFA, 0xFE, 0x04, 0x35, + 0xE8, 0x06, 0x7A, 0x78, 0x0D, 0x81, 0xF7, 0xEA, + 0xD9, 0x2F, 0x02, 0xAC, 0x30, 0x6A, 0xD6, 0x95 }; + +unsigned char table_119[32] = { + 0x14, 0x0A, 0x1C, 0x00, 0x0C, 0x1F, 0x1E, 0x0B, + 0x12, 0x1D, 0x17, 0x08, 0x07, 0x04, 0x09, 0x10, + 0x03, 0x1B, 0x0E, 0x1A, 0x05, 0x0D, 0x11, 0x15, + 0x18, 0x02, 0x06, 0x01, 0x19, 0x16, 0x13, 0x0F }; + +unsigned char table_120[256] = { + 0xCE, 0x89, 0xB2, 0x72, 0x04, 0x77, 0x64, 0xAE, + 0x80, 0x99, 0xB5, 0x00, 0x7B, 0x50, 0x9D, 0xE3, + 0x87, 0x37, 0x6D, 0x3D, 0x32, 0xBA, 0x20, 0xF0, + 0xDC, 0xBD, 0x61, 0x26, 0xD4, 0xA6, 0x70, 0x54, + 0xC1, 0x7D, 0x82, 0xFF, 0x81, 0x83, 0x2F, 0xF5, + 0x3B, 0x42, 0x08, 0x5C, 0x30, 0x59, 0xBB, 0xC2, + 0x33, 0x5D, 0xEE, 0xB7, 0xF7, 0x2B, 0x76, 0xD0, + 0x43, 0x1C, 0x48, 0xFC, 0x01, 0xCD, 0x27, 0x1D, + 0x5A, 0x96, 0x95, 0x03, 0xC6, 0x1F, 0x09, 0xCB, + 0xF6, 0x47, 0xA9, 0x93, 0xA7, 0xD2, 0xDB, 0x51, + 0xB0, 0x7A, 0xE6, 0x62, 0x0F, 0x12, 0x57, 0xF4, + 0x35, 0xFE, 0xA4, 0xDF, 0x5B, 0xF3, 0x67, 0x85, + 0x98, 0xE4, 0xAB, 0x75, 0x4C, 0xE2, 0x25, 0x74, + 0x3A, 0x45, 0xDE, 0xEF, 0x4A, 0x97, 0x86, 0x24, + 0xE9, 0x8F, 0xD8, 0xD7, 0x60, 0xAD, 0x36, 0x8E, + 0x1E, 0xB9, 0x4F, 0x6B, 0x8C, 0x06, 0x23, 0x94, + 0x0E, 0xD3, 0x49, 0x14, 0x90, 0xAF, 0x65, 0xEC, + 0xF9, 0x0D, 0xED, 0x6C, 0xBE, 0x7F, 0xA5, 0xC5, + 0xEA, 0x78, 0x2E, 0xBC, 0xD5, 0xDA, 0x18, 0xE1, + 0x10, 0x2D, 0xB4, 0x16, 0x4B, 0xE8, 0xC4, 0x8D, + 0x19, 0x1B, 0x02, 0x66, 0xB6, 0xE7, 0x9C, 0x7C, + 0xC9, 0xA0, 0x2A, 0x53, 0x13, 0xDD, 0xF8, 0xA8, + 0x0A, 0x6E, 0xCF, 0x6F, 0x7E, 0xE0, 0x3E, 0xE5, + 0x07, 0xCC, 0x38, 0xD1, 0xF2, 0x2C, 0x9A, 0xAC, + 0x88, 0x79, 0xB8, 0xC8, 0xBF, 0x63, 0x71, 0x69, + 0x52, 0x39, 0x9F, 0x22, 0x3F, 0x9E, 0x44, 0xFA, + 0x73, 0x6A, 0x8B, 0xA2, 0xD6, 0x1A, 0x9B, 0xB1, + 0x8A, 0x4D, 0x58, 0xA1, 0x46, 0x5F, 0x55, 0x56, + 0x21, 0x05, 0x15, 0x92, 0xAA, 0xEB, 0x31, 0x68, + 0xFB, 0x41, 0xC3, 0x4E, 0xB3, 0x40, 0x34, 0x17, + 0xD9, 0x29, 0x3C, 0x0C, 0xF1, 0x0B, 0x28, 0x84, + 0x5E, 0xCA, 0xFD, 0x11, 0xA3, 0xC7, 0xC0, 0x91 }; + +unsigned char table_121[32] = { + 0x1E, 0x12, 0x06, 0x1D, 0x15, 0x1F, 0x13, 0x0B, + 0x10, 0x0D, 0x1C, 0x01, 0x0A, 0x0E, 0x02, 0x19, + 0x04, 0x1A, 0x03, 0x11, 0x00, 0x16, 0x0C, 0x17, + 0x14, 0x08, 0x18, 0x05, 0x09, 0x0F, 0x1B, 0x07 }; + +unsigned char table_122[256] = { + 0x85, 0xDF, 0x7F, 0x7C, 0x56, 0xF0, 0x0C, 0x7D, + 0x76, 0xA8, 0x58, 0x31, 0x25, 0x8A, 0x0D, 0x23, + 0x05, 0x0F, 0x12, 0x64, 0x8E, 0x5D, 0xF4, 0x2C, + 0x18, 0xFA, 0x4B, 0xFE, 0x91, 0xBF, 0x95, 0x0B, + 0xF1, 0x88, 0x10, 0xD8, 0x3E, 0x53, 0x96, 0xB5, + 0x75, 0x24, 0x8F, 0xD6, 0x68, 0x5C, 0x93, 0x1F, + 0x6B, 0xC2, 0xAB, 0xED, 0x1E, 0xC0, 0xBC, 0x47, + 0xE9, 0xD1, 0xDE, 0xCA, 0xF6, 0x62, 0x43, 0xEB, + 0xA2, 0xB4, 0x08, 0xE6, 0x74, 0x0E, 0xA1, 0x72, + 0x66, 0x61, 0x21, 0x2E, 0x32, 0x63, 0x29, 0xD7, + 0x1C, 0x22, 0xAC, 0xE7, 0x54, 0xF3, 0x65, 0x17, + 0x9F, 0x78, 0x79, 0x4C, 0xDD, 0x27, 0x90, 0x36, + 0x19, 0x44, 0x03, 0xD9, 0x4A, 0x5A, 0x34, 0xF9, + 0x97, 0xA6, 0x70, 0x39, 0x28, 0x77, 0x6E, 0xB7, + 0x8C, 0x02, 0x5E, 0x9B, 0x8D, 0x59, 0x6F, 0xA5, + 0x07, 0xE2, 0x41, 0x51, 0xC9, 0x3C, 0xE8, 0xE1, + 0xB3, 0x16, 0x50, 0x04, 0xE3, 0x1D, 0x3B, 0xD2, + 0x4D, 0x35, 0x71, 0xDA, 0x9E, 0xA7, 0xE4, 0xE0, + 0xB6, 0x2B, 0xEA, 0x84, 0x55, 0xF8, 0x57, 0x3D, + 0x73, 0x42, 0xC6, 0x0A, 0x92, 0x6A, 0xAE, 0xF5, + 0xFC, 0xD5, 0x15, 0x52, 0x7E, 0x14, 0x81, 0x13, + 0xE5, 0x49, 0x38, 0x2A, 0x94, 0x5B, 0xA3, 0x11, + 0x8B, 0x80, 0xBB, 0x01, 0x9C, 0xA4, 0xDB, 0xF7, + 0xA9, 0x20, 0xF2, 0x1A, 0xDC, 0x33, 0x3A, 0xEF, + 0xD3, 0xFD, 0x30, 0xB0, 0x1B, 0xC4, 0x06, 0xD4, + 0x6D, 0x87, 0x2F, 0x60, 0x5F, 0xC5, 0x09, 0x37, + 0xAF, 0x00, 0xCB, 0x9D, 0xA0, 0xB9, 0x45, 0x86, + 0x4F, 0x6C, 0x67, 0xFB, 0x40, 0x3F, 0xCC, 0xB8, + 0xC8, 0x82, 0x98, 0x99, 0x7B, 0xB1, 0xCD, 0xD0, + 0xBD, 0x48, 0xAD, 0x26, 0x7A, 0x9A, 0x46, 0xFF, + 0x89, 0xC7, 0xC1, 0xCF, 0xBE, 0xAA, 0xEC, 0xBA, + 0xCE, 0x2D, 0x4E, 0x83, 0xC3, 0x69, 0xEE, 0xB2 }; + +unsigned char table_123[256] = { + 0x9D, 0xFB, 0x3C, 0x81, 0xAA, 0x05, 0xB2, 0xBE, + 0xD1, 0x5F, 0x4C, 0xE0, 0xA3, 0xF4, 0xDE, 0x35, + 0xFE, 0x1B, 0x37, 0x99, 0x94, 0x7A, 0x10, 0xAB, + 0xC0, 0xA4, 0xB5, 0xFF, 0x8F, 0x3B, 0xB4, 0x51, + 0x04, 0xE9, 0xB9, 0xC1, 0x98, 0xC5, 0x82, 0x38, + 0x4D, 0x71, 0xFC, 0x33, 0xC4, 0x50, 0x5D, 0x88, + 0xB8, 0x5C, 0x32, 0xE2, 0xBB, 0xCD, 0x60, 0x2C, + 0xD4, 0x7E, 0x27, 0x59, 0x2B, 0x1F, 0x53, 0xF6, + 0x25, 0x86, 0xAE, 0x21, 0xFA, 0x31, 0xD7, 0x0F, + 0x17, 0xDA, 0x7F, 0xC9, 0x46, 0x19, 0x08, 0xA8, + 0xCF, 0x13, 0xCC, 0x03, 0x3F, 0x22, 0x6E, 0xEB, + 0x4A, 0x63, 0x73, 0xBD, 0x36, 0xED, 0x30, 0x57, + 0x65, 0xF8, 0x41, 0x61, 0x1E, 0xA0, 0xC6, 0x45, + 0x3E, 0x75, 0x28, 0x87, 0xCB, 0xD6, 0x16, 0xD8, + 0xDF, 0xEF, 0xEA, 0xA7, 0x58, 0xB0, 0x1D, 0xE6, + 0x47, 0x76, 0xD9, 0x96, 0xE7, 0xDC, 0x00, 0x80, + 0xDD, 0xB7, 0x9A, 0xE1, 0xF5, 0x9C, 0x4B, 0xE3, + 0xBC, 0x8D, 0xF2, 0x2F, 0x9F, 0x6C, 0x93, 0xAF, + 0xA9, 0xC2, 0x5E, 0x24, 0x15, 0xD2, 0x09, 0x0D, + 0xDB, 0x4F, 0x91, 0x0E, 0x64, 0x34, 0x4E, 0xAD, + 0x62, 0x44, 0x23, 0x85, 0xB6, 0xAC, 0xC7, 0xCA, + 0x84, 0xF9, 0x8C, 0xBF, 0x14, 0x7C, 0x8E, 0x92, + 0xF0, 0x0B, 0xCE, 0x90, 0x7D, 0x70, 0x9E, 0x54, + 0x39, 0x5B, 0x6D, 0x52, 0xEE, 0xA2, 0x6F, 0x78, + 0x2D, 0x95, 0x8B, 0x02, 0x3D, 0x7B, 0x69, 0xC3, + 0x49, 0xA5, 0x1A, 0x26, 0xD5, 0x6B, 0xE8, 0xFD, + 0xB3, 0xD3, 0x20, 0x55, 0x18, 0x06, 0xF3, 0xB1, + 0x0C, 0xC8, 0x07, 0x12, 0xF7, 0x01, 0x2E, 0x72, + 0x97, 0xA6, 0x11, 0x89, 0x56, 0x5A, 0x29, 0xBA, + 0x67, 0x42, 0x83, 0x6A, 0x2A, 0xF1, 0xA1, 0x9B, + 0xE5, 0xE4, 0x74, 0x66, 0x1C, 0x68, 0xEC, 0x40, + 0x48, 0x77, 0xD0, 0x0A, 0x8A, 0x3A, 0x43, 0x79 }; + +unsigned char table_124[256] = { + 0x6C, 0xC3, 0x28, 0x2F, 0x42, 0x4B, 0x7C, 0x3C, + 0xCE, 0x24, 0xC8, 0x51, 0x25, 0x3F, 0x49, 0x8D, + 0x1E, 0x5C, 0x89, 0x3A, 0x98, 0x47, 0x0B, 0x12, + 0xA9, 0xB1, 0xD7, 0xB6, 0x5D, 0xF9, 0x5A, 0xBC, + 0xFA, 0x06, 0x7D, 0x08, 0xFC, 0x37, 0x54, 0x4F, + 0xD4, 0xCD, 0xA7, 0x5E, 0xE0, 0x92, 0x82, 0x56, + 0xF1, 0x2B, 0xC4, 0xE2, 0x29, 0xEA, 0x35, 0x57, + 0x33, 0x4E, 0x1A, 0x17, 0x8B, 0x85, 0xBF, 0xD5, + 0x18, 0xB3, 0x0D, 0x71, 0x45, 0x81, 0xB4, 0x27, + 0xD1, 0xE1, 0xFF, 0x44, 0x9E, 0xA4, 0x15, 0x9A, + 0x90, 0xC7, 0x79, 0xE3, 0x4C, 0xE9, 0x3D, 0x6B, + 0xF5, 0xF4, 0xEE, 0xAA, 0xDB, 0x07, 0x09, 0xCF, + 0x7B, 0x95, 0xA0, 0x53, 0x8F, 0xA1, 0x9D, 0xBE, + 0x6F, 0xAE, 0x96, 0x46, 0x59, 0x01, 0x84, 0xCC, + 0x3B, 0x8E, 0xF7, 0x4D, 0x6E, 0xDC, 0xE8, 0x36, + 0x7A, 0xE5, 0xBD, 0xE7, 0x9F, 0x2C, 0x52, 0xAB, + 0x55, 0x13, 0x1D, 0xFB, 0x58, 0x9C, 0xDF, 0xC0, + 0x30, 0x73, 0x67, 0x39, 0x74, 0xD3, 0x11, 0xD2, + 0x0E, 0x20, 0xB7, 0x02, 0xB9, 0x1C, 0x86, 0x76, + 0x10, 0x68, 0x9B, 0x63, 0x48, 0x8A, 0xB2, 0xB8, + 0xAF, 0x26, 0x99, 0x04, 0xB0, 0xE4, 0xEF, 0xEB, + 0xEC, 0x6D, 0x61, 0xC1, 0xD0, 0x38, 0xC9, 0x19, + 0x60, 0xA8, 0xA6, 0xF8, 0x80, 0xC5, 0x03, 0x0F, + 0x22, 0x2D, 0x88, 0x32, 0x77, 0x70, 0xFE, 0x0C, + 0x31, 0x40, 0x5F, 0xED, 0xA5, 0x93, 0x43, 0xF0, + 0x8C, 0xE6, 0x34, 0x21, 0xD9, 0xC2, 0xD8, 0xC6, + 0x6A, 0xD6, 0xCB, 0xAC, 0x75, 0xB5, 0x78, 0x0A, + 0xA3, 0x69, 0x16, 0xBA, 0x50, 0x2A, 0x41, 0x83, + 0xF6, 0x64, 0x00, 0x65, 0x7E, 0xDD, 0x5B, 0xDA, + 0x14, 0xFD, 0x3E, 0x7F, 0xCA, 0x66, 0x4A, 0x1F, + 0xA2, 0xAD, 0xF2, 0x23, 0xBB, 0x72, 0xF3, 0x94, + 0x62, 0x1B, 0xDE, 0x91, 0x87, 0x97, 0x05, 0x2E }; + +unsigned char table_125[32] = { + 0x1A, 0x18, 0x12, 0x15, 0x00, 0x1C, 0x01, 0x0B, + 0x19, 0x1B, 0x1F, 0x11, 0x07, 0x10, 0x1E, 0x06, + 0x17, 0x04, 0x0A, 0x0E, 0x0D, 0x0C, 0x16, 0x08, + 0x02, 0x03, 0x13, 0x14, 0x09, 0x1D, 0x05, 0x0F }; + +unsigned char table_126[32] = { + 0x1C, 0x1D, 0x07, 0x12, 0x18, 0x1A, 0x19, 0x09, + 0x0F, 0x14, 0x1F, 0x0B, 0x13, 0x04, 0x0E, 0x1E, + 0x0C, 0x0D, 0x01, 0x17, 0x1B, 0x16, 0x0A, 0x05, + 0x15, 0x10, 0x11, 0x08, 0x00, 0x03, 0x06, 0x02 }; + +unsigned char table_127[256] = { + 0xA0, 0x66, 0xD8, 0x08, 0xEA, 0x39, 0x78, 0xAB, + 0x61, 0x4E, 0xC7, 0xD1, 0xA3, 0x1C, 0x9F, 0xCB, + 0x19, 0x51, 0x15, 0x92, 0x23, 0xFD, 0x7D, 0x1D, + 0x95, 0xAE, 0x0E, 0x8B, 0xE6, 0x7F, 0x86, 0x6D, + 0x06, 0xBD, 0x20, 0x1F, 0x3A, 0xE4, 0x54, 0x91, + 0x69, 0xD3, 0xE3, 0x3D, 0x4D, 0x31, 0x49, 0xA4, + 0x41, 0xF3, 0xE0, 0x11, 0x14, 0x9B, 0x96, 0x5A, + 0xC4, 0x8E, 0x34, 0xDB, 0xBA, 0x83, 0xD9, 0x81, + 0xAF, 0x58, 0x8A, 0x79, 0x13, 0xBC, 0x85, 0x37, + 0x9E, 0x6C, 0x57, 0x71, 0x8D, 0x97, 0x5F, 0x6F, + 0x1E, 0x74, 0x27, 0xFC, 0x5C, 0x7A, 0x64, 0x87, + 0xF5, 0xC6, 0xF2, 0x4F, 0xDE, 0x80, 0xAA, 0x84, + 0x2E, 0xDC, 0xE7, 0x40, 0x75, 0xC5, 0xB3, 0xC8, + 0xCE, 0x21, 0x02, 0x67, 0xB7, 0x10, 0x47, 0x6A, + 0xEE, 0x53, 0x2C, 0x16, 0x05, 0xC0, 0x63, 0x4C, + 0x0D, 0xBB, 0xC3, 0x38, 0x46, 0x68, 0x7E, 0xF9, + 0xB8, 0xB4, 0x3E, 0x36, 0xD5, 0xEC, 0x0B, 0xF6, + 0x33, 0x0A, 0x0F, 0x5B, 0xFB, 0x45, 0xEB, 0xA9, + 0x6E, 0x6B, 0xCF, 0x55, 0x99, 0xAC, 0x22, 0xBE, + 0xB1, 0xA2, 0x3F, 0x25, 0x77, 0x8F, 0x7C, 0xF1, + 0xD4, 0x59, 0xA8, 0xE5, 0xD7, 0xCA, 0xA1, 0x93, + 0xE9, 0xAD, 0xF7, 0x94, 0xEF, 0xED, 0x3C, 0x2A, + 0x88, 0xB5, 0x35, 0x9D, 0x9C, 0x32, 0x5E, 0xB6, + 0x48, 0x9A, 0x7B, 0x26, 0x50, 0x90, 0x04, 0xA7, + 0xDD, 0x09, 0xB9, 0x98, 0xB2, 0xFE, 0xDF, 0x44, + 0x89, 0x29, 0x5D, 0xE2, 0x72, 0xC9, 0x28, 0x03, + 0x43, 0x8C, 0x52, 0x18, 0xC1, 0x56, 0x1B, 0x1A, + 0x01, 0x65, 0xDA, 0xBF, 0x07, 0xFF, 0x76, 0xE8, + 0x30, 0xA5, 0x4A, 0xA6, 0x12, 0x62, 0x24, 0x60, + 0x4B, 0x73, 0x0C, 0xF0, 0xFA, 0x42, 0xF4, 0x00, + 0xD2, 0xD0, 0xD6, 0x3B, 0xC2, 0x2F, 0xE1, 0x2B, + 0x70, 0xF8, 0x17, 0xCD, 0xB0, 0xCC, 0x82, 0x2D }; + +unsigned char table_128[32] = { + 0x1A, 0x1C, 0x09, 0x17, 0x1B, 0x0B, 0x16, 0x1E, + 0x14, 0x0C, 0x12, 0x0E, 0x05, 0x03, 0x1F, 0x15, + 0x19, 0x0D, 0x10, 0x13, 0x0A, 0x01, 0x00, 0x11, + 0x02, 0x08, 0x0F, 0x18, 0x07, 0x04, 0x1D, 0x06 }; + +unsigned char table_129[256] = { + 0x9D, 0x5F, 0xE8, 0x99, 0x57, 0x07, 0x16, 0xA6, + 0x9F, 0xB6, 0xDE, 0xED, 0x2D, 0xB3, 0xC0, 0x8E, + 0xCC, 0x49, 0xCE, 0xB0, 0x1B, 0xB1, 0x7A, 0xE0, + 0xEB, 0x28, 0xDB, 0x7D, 0x88, 0xC8, 0x06, 0x6C, + 0x02, 0xD0, 0x85, 0x7E, 0xDF, 0xF5, 0x78, 0xE5, + 0xA9, 0x71, 0xD9, 0xDD, 0xDC, 0xEE, 0x8C, 0x54, + 0xA0, 0x86, 0xFE, 0x0E, 0x55, 0xF7, 0x41, 0x47, + 0x1D, 0x15, 0xD6, 0xA4, 0xFF, 0x1F, 0x25, 0xF8, + 0x12, 0xE9, 0x74, 0x7B, 0x04, 0xE6, 0x4C, 0x31, + 0xA2, 0xBE, 0x0C, 0xB9, 0x17, 0xBD, 0x3D, 0xF0, + 0x9E, 0x4D, 0x4E, 0xB2, 0xE7, 0x40, 0xC9, 0x8A, + 0x67, 0x5E, 0x19, 0x0F, 0xB7, 0x22, 0x8D, 0xBA, + 0xFC, 0x93, 0x14, 0xEA, 0xFD, 0x0D, 0xD5, 0x38, + 0xA1, 0x84, 0x1C, 0x35, 0x60, 0x37, 0x43, 0x9C, + 0xCF, 0xEF, 0x3A, 0x72, 0xF2, 0x61, 0x75, 0x6A, + 0x42, 0xAC, 0xD3, 0x48, 0x77, 0xC5, 0x29, 0xF6, + 0x58, 0x79, 0xFA, 0x5D, 0xC7, 0x70, 0x53, 0x9A, + 0x6F, 0xC1, 0x0A, 0x90, 0x8F, 0x3E, 0x3B, 0x8B, + 0xEC, 0xBC, 0x20, 0x27, 0xC3, 0x66, 0x3F, 0x33, + 0xA5, 0x44, 0x2E, 0x32, 0x65, 0x18, 0xFB, 0x59, + 0x52, 0x50, 0xE2, 0x63, 0x2B, 0xCD, 0x64, 0xCB, + 0xD2, 0x68, 0x10, 0xA7, 0xAE, 0x11, 0xA8, 0x96, + 0x69, 0xAF, 0xC2, 0x34, 0x5C, 0x56, 0xE3, 0xF9, + 0xDA, 0x51, 0x81, 0x4A, 0x05, 0x00, 0xB8, 0x7C, + 0x30, 0x2F, 0x46, 0xB4, 0xC6, 0x87, 0x4B, 0x94, + 0x80, 0xF4, 0x7F, 0x3C, 0x26, 0xF1, 0x5B, 0xAB, + 0x91, 0x6E, 0x08, 0x76, 0x98, 0xD1, 0xE1, 0x36, + 0x21, 0xCA, 0xD8, 0x24, 0x9B, 0x39, 0xBB, 0xAD, + 0x13, 0x62, 0x97, 0x1A, 0x6D, 0x2C, 0x5A, 0xC4, + 0xD4, 0xA3, 0x03, 0xBF, 0x1E, 0xE4, 0xF3, 0x95, + 0x23, 0x73, 0x92, 0xB5, 0x01, 0x83, 0x82, 0xAA, + 0x09, 0x45, 0x6B, 0xD7, 0x0B, 0x89, 0x4F, 0x2A }; + +unsigned char table_130[32] = { + 0x07, 0x03, 0x15, 0x0B, 0x02, 0x11, 0x17, 0x14, + 0x05, 0x10, 0x0A, 0x0F, 0x01, 0x1C, 0x1D, 0x0E, + 0x12, 0x06, 0x18, 0x16, 0x1A, 0x09, 0x13, 0x19, + 0x1B, 0x00, 0x08, 0x0D, 0x0C, 0x1E, 0x04, 0x1F }; + +unsigned char table_131[32] = { + 0x1D, 0x13, 0x1B, 0x10, 0x07, 0x03, 0x0A, 0x02, + 0x00, 0x0C, 0x0E, 0x0B, 0x0D, 0x18, 0x12, 0x1F, + 0x1A, 0x04, 0x15, 0x11, 0x1E, 0x08, 0x1C, 0x14, + 0x19, 0x05, 0x0F, 0x17, 0x06, 0x01, 0x09, 0x16 }; + +unsigned char table_132[256] = { + 0x33, 0x8D, 0x45, 0x6F, 0xFF, 0xF5, 0xB6, 0x53, + 0x3B, 0xF3, 0x07, 0xA4, 0x97, 0xEB, 0x6B, 0xA5, + 0xD3, 0xDC, 0x7B, 0x79, 0x93, 0xE7, 0xF7, 0x67, + 0x9C, 0x4F, 0x88, 0xF9, 0x3A, 0x2B, 0x27, 0x48, + 0x47, 0x18, 0xF4, 0xAD, 0xB4, 0x8F, 0x2A, 0x76, + 0x17, 0xE9, 0x1F, 0x40, 0x0C, 0x59, 0xD1, 0x4C, + 0x20, 0x31, 0x73, 0x54, 0xCD, 0x68, 0x08, 0x52, + 0x10, 0x62, 0x3D, 0xD2, 0x77, 0xF2, 0xD7, 0x30, + 0xCA, 0x16, 0x01, 0x50, 0x9F, 0x3F, 0x75, 0xED, + 0x90, 0x6A, 0x34, 0xCE, 0x05, 0x78, 0x5E, 0xD6, + 0x85, 0xCC, 0x29, 0xB8, 0xC1, 0x0D, 0xCB, 0x80, + 0x2E, 0x04, 0x00, 0x44, 0x32, 0x95, 0xBF, 0xFE, + 0x6E, 0x7C, 0xFD, 0xA7, 0x3C, 0x5C, 0xF0, 0xEC, + 0xAC, 0xF8, 0xB9, 0xC0, 0x1B, 0x3E, 0xE8, 0x66, + 0x5D, 0xDE, 0x49, 0x71, 0xAA, 0xAF, 0x21, 0x64, + 0x28, 0x8A, 0x4E, 0x98, 0x58, 0xA2, 0x23, 0xCF, + 0x9E, 0x63, 0x61, 0x91, 0x12, 0xC6, 0x8C, 0x19, + 0xA8, 0xD4, 0xC7, 0xDD, 0xFC, 0xBD, 0x38, 0xDF, + 0xEA, 0x2D, 0x7E, 0x7D, 0xE3, 0xE0, 0xC3, 0xD9, + 0x8B, 0x11, 0xF1, 0x4D, 0xC8, 0xB5, 0x55, 0xAE, + 0xE1, 0x89, 0xE5, 0xB3, 0xBC, 0x69, 0x9D, 0xA6, + 0x09, 0x9A, 0x74, 0x35, 0x1A, 0xFB, 0x24, 0xB7, + 0x13, 0x14, 0x94, 0x0A, 0x86, 0x0F, 0x60, 0x51, + 0xB0, 0x84, 0x22, 0x5B, 0x87, 0x43, 0x57, 0x0B, + 0x2F, 0x5F, 0x02, 0xD0, 0xBB, 0xA3, 0xC9, 0x7A, + 0xBE, 0xC2, 0x26, 0x46, 0xDB, 0x1E, 0x1D, 0x92, + 0xE2, 0xB2, 0x37, 0x6D, 0xD5, 0x4A, 0x0E, 0x4B, + 0x8E, 0xC5, 0x42, 0x99, 0xEE, 0xE4, 0xB1, 0x06, + 0xAB, 0x5A, 0x56, 0x41, 0x65, 0xBA, 0xFA, 0x83, + 0x15, 0xDA, 0x72, 0xA1, 0x81, 0x1C, 0xA9, 0x36, + 0x25, 0x96, 0x6C, 0x39, 0x82, 0xE6, 0x2C, 0x9B, + 0xC4, 0x7F, 0xA0, 0xD8, 0xEF, 0x03, 0x70, 0xF6 }; + +unsigned char table_133[256] = { + 0x02, 0xF0, 0xED, 0xC4, 0xE4, 0x67, 0x60, 0x8B, + 0xF3, 0x77, 0x92, 0xE0, 0x85, 0x93, 0x1E, 0x8E, + 0x9A, 0x38, 0x61, 0x20, 0xB7, 0x68, 0xE1, 0x5E, + 0xD5, 0x63, 0xA9, 0xA5, 0xBE, 0x36, 0x12, 0x4D, + 0x86, 0x16, 0xD6, 0xB1, 0x23, 0x64, 0x4F, 0x62, + 0xFC, 0xA3, 0xD3, 0x04, 0x7D, 0x8C, 0xE2, 0xFF, + 0x5D, 0x30, 0xF5, 0x95, 0x1B, 0x5F, 0x73, 0xAA, + 0xE8, 0x07, 0x87, 0xDC, 0x54, 0x7C, 0xEE, 0x00, + 0xB8, 0xDE, 0x55, 0xBA, 0xD0, 0x50, 0xBB, 0x89, + 0x1C, 0xCC, 0x0E, 0xC0, 0x42, 0x11, 0xD8, 0xA2, + 0x2E, 0x33, 0xFE, 0x26, 0xD4, 0x10, 0xDA, 0xC5, + 0xFB, 0xAF, 0x98, 0x78, 0xB5, 0xBD, 0xC8, 0x8D, + 0x46, 0xA0, 0xD1, 0x7B, 0xBC, 0x75, 0xAB, 0x25, + 0xB2, 0x43, 0x57, 0xB6, 0xEC, 0xF4, 0x66, 0x05, + 0x9C, 0x08, 0x53, 0x80, 0xEA, 0x21, 0x2C, 0x6C, + 0x17, 0x71, 0xD2, 0x70, 0x76, 0x9E, 0x6B, 0x7A, + 0x58, 0xA7, 0xBF, 0x29, 0x03, 0x1F, 0x06, 0xC1, + 0xDD, 0x2F, 0x5C, 0x0B, 0x0D, 0x8A, 0x0A, 0xCB, + 0xCA, 0x6F, 0x19, 0x6A, 0xFA, 0xF7, 0xA8, 0xA1, + 0xEB, 0x88, 0x44, 0xAC, 0x01, 0x4E, 0x59, 0x94, + 0x72, 0x2B, 0xE9, 0x0F, 0x22, 0x9B, 0x27, 0x37, + 0x41, 0xF9, 0xF2, 0xE3, 0xEF, 0xB3, 0xD9, 0x2A, + 0x31, 0xC2, 0x0C, 0x15, 0x90, 0x14, 0xF6, 0x83, + 0xFD, 0x96, 0x9D, 0x7F, 0xA4, 0x39, 0xE7, 0x3F, + 0xE6, 0xC7, 0xCD, 0x1A, 0xCF, 0x48, 0x3C, 0x51, + 0x6D, 0x5B, 0x74, 0xC3, 0xC9, 0x09, 0x3D, 0x9F, + 0xDB, 0x32, 0x40, 0x18, 0xD7, 0xCE, 0x69, 0x49, + 0x3A, 0xF1, 0xB9, 0x56, 0x91, 0x99, 0x84, 0x24, + 0x7E, 0x34, 0x4B, 0xA6, 0x47, 0xB4, 0x6E, 0xDF, + 0x65, 0x3B, 0xAD, 0x45, 0x13, 0xC6, 0x81, 0xF8, + 0x4A, 0x2D, 0x8F, 0x4C, 0x97, 0x28, 0x3E, 0xE5, + 0x5A, 0x35, 0xB0, 0xAE, 0x82, 0x79, 0x1D, 0x52 }; + +unsigned char table_134[32] = { + 0x09, 0x0F, 0x10, 0x0C, 0x03, 0x15, 0x07, 0x17, + 0x0E, 0x0B, 0x1D, 0x08, 0x19, 0x11, 0x00, 0x0A, + 0x01, 0x06, 0x18, 0x16, 0x0D, 0x13, 0x14, 0x12, + 0x02, 0x1B, 0x1A, 0x04, 0x05, 0x1F, 0x1C, 0x1E }; + +unsigned char table_135[256] = { + 0x14, 0x34, 0xEA, 0x02, 0x2B, 0x5A, 0x10, 0x51, + 0xF3, 0x8F, 0x28, 0xB2, 0x50, 0x8B, 0x01, 0xCC, + 0x80, 0x15, 0x29, 0x42, 0xF4, 0x1D, 0xFB, 0xBB, + 0x1F, 0x43, 0x8C, 0x17, 0x1E, 0x81, 0x04, 0x98, + 0x46, 0xD8, 0xD5, 0x65, 0x4C, 0x1C, 0xDB, 0x40, + 0x5F, 0x1A, 0x31, 0x74, 0xF1, 0x64, 0x19, 0x05, + 0xFC, 0xF0, 0x73, 0xB6, 0x23, 0x77, 0x9C, 0xCE, + 0x70, 0xEF, 0xDA, 0xE0, 0xA2, 0x78, 0x84, 0xEB, + 0x9E, 0xC5, 0x95, 0xA3, 0xF6, 0xCA, 0xAD, 0x52, + 0xD0, 0x3F, 0x54, 0xA7, 0x33, 0xA9, 0x09, 0x6A, + 0x89, 0x7E, 0x75, 0xA8, 0xD6, 0x79, 0x9F, 0xAB, + 0x8E, 0x11, 0x0E, 0x3B, 0xAA, 0xE6, 0x85, 0x53, + 0x0A, 0x59, 0xEC, 0x94, 0xD7, 0x41, 0x86, 0x7D, + 0x2F, 0xC7, 0xDE, 0x06, 0xCB, 0x13, 0xBA, 0x58, + 0xC8, 0xC9, 0x07, 0x67, 0x7F, 0xA5, 0xB4, 0x2C, + 0x48, 0x6C, 0xB8, 0xD1, 0x30, 0xD3, 0x35, 0x4F, + 0x88, 0x26, 0x93, 0x32, 0x71, 0x3E, 0x3D, 0xF7, + 0x6D, 0x03, 0xED, 0x8A, 0x36, 0x55, 0x9B, 0x66, + 0x8D, 0x27, 0x7C, 0xF9, 0xA6, 0xC3, 0x20, 0x69, + 0x4A, 0xE3, 0x99, 0x5C, 0xBC, 0x45, 0x16, 0x6B, + 0xB9, 0x49, 0x82, 0xFF, 0xBD, 0xDD, 0xE9, 0x0C, + 0xD4, 0x44, 0xFD, 0x22, 0xE5, 0xAC, 0x61, 0xC4, + 0x90, 0x47, 0x37, 0x72, 0xA4, 0x7A, 0x24, 0x4D, + 0x5B, 0x12, 0x38, 0x92, 0x87, 0x1B, 0xE1, 0xA0, + 0x91, 0x3C, 0xEE, 0x6F, 0xC1, 0x0F, 0x56, 0xC2, + 0x9A, 0xF8, 0x18, 0xE8, 0xD2, 0xDC, 0x4B, 0xCF, + 0x39, 0xF5, 0xFE, 0x2A, 0x2D, 0x9D, 0xA1, 0xFA, + 0xE7, 0xBF, 0x6E, 0xE4, 0x2E, 0xB3, 0xCD, 0xE2, + 0xAF, 0x7B, 0xC0, 0x68, 0x97, 0xB5, 0x5D, 0xB7, + 0x21, 0x57, 0x83, 0x76, 0xB1, 0xAE, 0x5E, 0x0D, + 0x96, 0x4E, 0x08, 0xC6, 0x0B, 0xDF, 0x3A, 0xB0, + 0x00, 0x63, 0xD9, 0xBE, 0xF2, 0x60, 0x25, 0x62 }; + +unsigned char table_136[256] = { + 0xD3, 0x1A, 0x00, 0xED, 0x59, 0x24, 0xA3, 0xF2, + 0xBA, 0x58, 0x4C, 0x5C, 0x75, 0x48, 0x98, 0xB0, + 0xCF, 0xC3, 0xF7, 0x88, 0x70, 0xB3, 0x3D, 0x3E, + 0x03, 0xF9, 0xC9, 0xFD, 0x80, 0x44, 0x7F, 0x3B, + 0x95, 0x5F, 0x31, 0x47, 0x15, 0x07, 0xB8, 0x08, + 0xCE, 0xDA, 0x71, 0x9F, 0x83, 0xB1, 0x55, 0x16, + 0xE6, 0xB2, 0xC7, 0xBE, 0x54, 0xE7, 0x2E, 0x8D, + 0x12, 0x21, 0x41, 0x69, 0xFE, 0x28, 0x11, 0x56, + 0x5A, 0xDD, 0xB6, 0x87, 0x78, 0x82, 0x4D, 0x7B, + 0x50, 0x9A, 0x9E, 0x62, 0xF8, 0x0A, 0x64, 0xF1, + 0x4E, 0x33, 0xAD, 0xBB, 0x79, 0x76, 0xD8, 0xCD, + 0x86, 0x34, 0x29, 0xD5, 0x7D, 0x72, 0xC5, 0xC1, + 0xDF, 0x09, 0x4A, 0xB4, 0xD2, 0x7A, 0xF0, 0xCC, + 0x0F, 0xA7, 0xD6, 0x2B, 0x20, 0x26, 0xEF, 0xAB, + 0x74, 0x1E, 0xE3, 0x77, 0xCB, 0x7C, 0x73, 0x5E, + 0x6B, 0x0D, 0x65, 0xA6, 0x30, 0xFB, 0xD0, 0xB7, + 0xAA, 0x94, 0x9D, 0x85, 0x13, 0x18, 0xA8, 0xF3, + 0xE0, 0xBC, 0x45, 0xCA, 0xC8, 0xDC, 0xE2, 0x3C, + 0x23, 0xE5, 0xB9, 0x90, 0x49, 0xA5, 0xE4, 0x36, + 0xFC, 0x53, 0xF6, 0xE8, 0xC6, 0x2C, 0x02, 0x25, + 0xC0, 0x8F, 0x61, 0xA4, 0x39, 0x8C, 0x5D, 0xAE, + 0x22, 0x1C, 0x2F, 0xD4, 0x6C, 0xD1, 0x51, 0xEA, + 0x4F, 0x7E, 0xA0, 0xF5, 0x6A, 0x32, 0xA2, 0x01, + 0xB5, 0x10, 0x2A, 0xAC, 0xA9, 0x06, 0xC4, 0x91, + 0x68, 0xE1, 0xBD, 0x14, 0x38, 0xFA, 0x6E, 0x3F, + 0x37, 0x66, 0xDB, 0x57, 0x43, 0x1B, 0x67, 0xAF, + 0x1F, 0x0B, 0x6D, 0x2D, 0x89, 0x04, 0x4B, 0x52, + 0xC2, 0xBF, 0xA1, 0x92, 0x99, 0x6F, 0x63, 0x81, + 0x27, 0x05, 0x96, 0x3A, 0xEC, 0x0E, 0x97, 0xD9, + 0xDE, 0x46, 0x35, 0x8B, 0x8E, 0x8A, 0xF4, 0xFF, + 0x60, 0xD7, 0xE9, 0x17, 0xEB, 0x9C, 0x84, 0x0C, + 0x93, 0x1D, 0x9B, 0x5B, 0x40, 0xEE, 0x42, 0x19 }; + +unsigned char table_137[32] = { + 0x0F, 0x09, 0x02, 0x06, 0x18, 0x0B, 0x1E, 0x05, + 0x11, 0x1D, 0x16, 0x01, 0x13, 0x10, 0x0E, 0x1A, + 0x1B, 0x00, 0x0D, 0x08, 0x15, 0x14, 0x19, 0x17, + 0x03, 0x1F, 0x0A, 0x12, 0x0C, 0x07, 0x04, 0x1C }; + +unsigned char table_138[32] = { + 0x0D, 0x1C, 0x1F, 0x15, 0x0F, 0x14, 0x1B, 0x12, + 0x09, 0x0B, 0x19, 0x07, 0x11, 0x16, 0x0C, 0x04, + 0x13, 0x05, 0x1D, 0x03, 0x0E, 0x0A, 0x08, 0x1E, + 0x01, 0x06, 0x18, 0x17, 0x10, 0x1A, 0x02, 0x00 }; + +unsigned char table_139[32] = { + 0x05, 0x15, 0x1D, 0x02, 0x0F, 0x03, 0x17, 0x1A, + 0x0A, 0x00, 0x1F, 0x12, 0x0E, 0x11, 0x1B, 0x13, + 0x0B, 0x0D, 0x09, 0x18, 0x1E, 0x08, 0x14, 0x07, + 0x0C, 0x04, 0x16, 0x19, 0x1C, 0x06, 0x10, 0x01 }; + +unsigned char table_140[32] = { + 0x06, 0x1E, 0x0C, 0x11, 0x13, 0x08, 0x15, 0x01, + 0x1D, 0x03, 0x0F, 0x19, 0x18, 0x04, 0x00, 0x14, + 0x12, 0x1A, 0x0B, 0x0E, 0x02, 0x1B, 0x07, 0x05, + 0x1F, 0x17, 0x09, 0x0A, 0x0D, 0x16, 0x10, 0x1C }; + +unsigned char table_141[256] = { + 0xE1, 0x0A, 0x28, 0xCD, 0x8A, 0x1E, 0x26, 0x10, + 0xC0, 0x6F, 0x06, 0x2C, 0xF8, 0x51, 0x6C, 0x8F, + 0xA8, 0x8C, 0x41, 0xF4, 0xED, 0x36, 0xAC, 0x89, + 0xBD, 0x9D, 0x42, 0x50, 0x95, 0x07, 0x2A, 0x9B, + 0x7E, 0xA3, 0x6B, 0x30, 0x72, 0x4E, 0xBE, 0xD8, + 0x8B, 0x5B, 0x1A, 0x56, 0x05, 0xEF, 0xEE, 0x64, + 0xFF, 0xFD, 0x93, 0xB5, 0xD6, 0x04, 0x57, 0xAE, + 0x4D, 0x6D, 0x2F, 0xBA, 0x40, 0xE0, 0xDB, 0xF2, + 0xCC, 0x08, 0x35, 0x02, 0xC4, 0x65, 0x66, 0x76, + 0xA1, 0x97, 0x9F, 0x6A, 0x90, 0xA7, 0x34, 0x1B, + 0x18, 0xB9, 0xA2, 0xDE, 0x23, 0x1F, 0xCB, 0xE6, + 0xAB, 0xCF, 0xAD, 0x4A, 0xF7, 0x24, 0xD0, 0xE8, + 0x8D, 0x49, 0xEA, 0x0F, 0x94, 0x22, 0xD3, 0x74, + 0x71, 0x0D, 0x21, 0x14, 0x39, 0x4B, 0x16, 0x25, + 0x5A, 0xB7, 0x17, 0x67, 0x59, 0x47, 0x27, 0x4F, + 0x32, 0x3B, 0x63, 0x0C, 0xF0, 0xF3, 0x7B, 0xC7, + 0xCA, 0x3A, 0x9A, 0xE2, 0xD5, 0xFA, 0x91, 0xFC, + 0x86, 0x81, 0x99, 0xB4, 0xBC, 0x7C, 0xC5, 0xBF, + 0xC1, 0xF5, 0x77, 0xA4, 0x79, 0x11, 0x8E, 0x75, + 0x55, 0x3D, 0x78, 0x20, 0x37, 0x3E, 0x85, 0xE4, + 0x2E, 0x82, 0xA9, 0x7A, 0x31, 0xC9, 0xB3, 0xFE, + 0x4C, 0x7D, 0xC3, 0xA0, 0x0E, 0x96, 0x5C, 0xC6, + 0x1C, 0x5F, 0xD7, 0xDD, 0x83, 0xC8, 0x9E, 0xEC, + 0x3F, 0xAF, 0x38, 0x9C, 0xD9, 0xB6, 0xDA, 0xD4, + 0x61, 0x44, 0x43, 0xAA, 0xB1, 0xCE, 0xE7, 0x84, + 0x00, 0x0B, 0xFB, 0x68, 0xC2, 0x3C, 0x58, 0xB2, + 0x69, 0x7F, 0x33, 0x2B, 0x80, 0x03, 0xE9, 0x88, + 0x29, 0x12, 0x01, 0x6E, 0x62, 0xF1, 0xA6, 0xF9, + 0x5D, 0xD2, 0xE3, 0x53, 0x09, 0x2D, 0xBB, 0x15, + 0xEB, 0x13, 0xA5, 0xF6, 0x73, 0x19, 0x60, 0xB0, + 0xD1, 0x48, 0x92, 0x1D, 0x52, 0x5E, 0x45, 0x70, + 0x98, 0x54, 0xB8, 0xDC, 0x46, 0xDF, 0x87, 0xE5 }; + +unsigned char table_142[256] = { + 0x90, 0x94, 0xBE, 0x14, 0x99, 0xEB, 0x45, 0x0F, + 0x34, 0x4A, 0xE3, 0x79, 0xD2, 0x64, 0x4D, 0x69, + 0x91, 0xDE, 0xB9, 0x1C, 0x59, 0x20, 0x6C, 0x0B, + 0x16, 0xC7, 0x1D, 0x18, 0x02, 0x7D, 0x13, 0xB2, + 0x7B, 0x81, 0xCF, 0x61, 0xA3, 0x33, 0x00, 0x73, + 0x5A, 0x8A, 0xA1, 0xA8, 0x31, 0xAC, 0xF0, 0x67, + 0xAE, 0xA5, 0x2A, 0x96, 0x58, 0xF4, 0xB7, 0x0E, + 0xE1, 0x54, 0x27, 0x83, 0x09, 0x85, 0xF8, 0x84, + 0xEA, 0xAD, 0x06, 0xED, 0x43, 0xFF, 0xA2, 0x6E, + 0x68, 0x46, 0x74, 0x47, 0x3C, 0xAA, 0xBC, 0x55, + 0xA7, 0xC3, 0x82, 0xDC, 0xBF, 0x38, 0x80, 0x15, + 0xF6, 0xB3, 0x92, 0x7C, 0x93, 0x3F, 0xE9, 0x4C, + 0x35, 0x30, 0x32, 0xF3, 0x88, 0xC0, 0x49, 0x6D, + 0xCE, 0x42, 0xDF, 0xFD, 0x78, 0x6A, 0x24, 0xCA, + 0xB8, 0xFC, 0xA6, 0x5F, 0x29, 0xFE, 0x0C, 0x5C, + 0x0D, 0x23, 0x8B, 0x9D, 0xD4, 0x03, 0x2C, 0x9C, + 0x77, 0xD8, 0x39, 0x8C, 0x57, 0xD5, 0xE0, 0x8F, + 0xC6, 0xB0, 0xCD, 0x48, 0xC9, 0xA0, 0xDA, 0xC8, + 0xD1, 0x5B, 0xAB, 0x37, 0x5D, 0x63, 0xAF, 0xF9, + 0x17, 0x1B, 0xE5, 0xF1, 0x36, 0xC1, 0x04, 0x26, + 0x6F, 0x9E, 0xD9, 0x2F, 0x7F, 0xB5, 0x3A, 0xD6, + 0xE6, 0x40, 0x07, 0xCB, 0x7E, 0x3E, 0xC5, 0x22, + 0xEC, 0xE2, 0xD3, 0x4E, 0x65, 0x2D, 0x70, 0xE7, + 0x10, 0x19, 0xD0, 0xEF, 0xBD, 0xC2, 0x44, 0xB4, + 0xF7, 0xA4, 0x53, 0x9F, 0x86, 0xFA, 0xE8, 0x4B, + 0x28, 0x3D, 0x9B, 0x56, 0x89, 0x6B, 0x25, 0x71, + 0x60, 0x11, 0x9A, 0x5E, 0x1A, 0x52, 0x08, 0x4F, + 0xB1, 0xDD, 0xBB, 0x98, 0xFB, 0x12, 0x3B, 0x0A, + 0x2E, 0xDB, 0x62, 0x8D, 0xC4, 0x75, 0xA9, 0x2B, + 0xE4, 0x97, 0x72, 0xF5, 0xEE, 0xF2, 0xB6, 0x21, + 0xBA, 0x7A, 0x76, 0x41, 0x50, 0x66, 0x05, 0x8E, + 0xCC, 0x1E, 0x87, 0xD7, 0x01, 0x1F, 0x51, 0x95 }; + +unsigned char table_143[32] = { + 0x0E, 0x16, 0x18, 0x11, 0x0C, 0x01, 0x12, 0x1F, + 0x08, 0x15, 0x0A, 0x06, 0x1C, 0x1E, 0x02, 0x1A, + 0x17, 0x03, 0x07, 0x13, 0x05, 0x19, 0x10, 0x0F, + 0x0D, 0x14, 0x09, 0x0B, 0x1B, 0x00, 0x1D, 0x04 }; + +unsigned char table_144[32] = { + 0x00, 0x1B, 0x17, 0x19, 0x1D, 0x11, 0x0D, 0x1A, + 0x13, 0x03, 0x1E, 0x09, 0x10, 0x0E, 0x15, 0x05, + 0x0B, 0x1C, 0x1F, 0x08, 0x0A, 0x06, 0x01, 0x0F, + 0x16, 0x14, 0x02, 0x04, 0x07, 0x18, 0x12, 0x0C }; + +unsigned char table_145[256] = { + 0xF9, 0x2C, 0x38, 0x74, 0xDA, 0x65, 0x85, 0x0E, + 0xBA, 0x64, 0xDB, 0xE3, 0xB6, 0x8B, 0x0B, 0x5E, + 0x01, 0x0F, 0x12, 0x8C, 0xD4, 0xCC, 0xB1, 0x7B, + 0xE7, 0xBC, 0x2E, 0x87, 0x84, 0x3B, 0xF8, 0x4C, + 0x8E, 0x59, 0x2D, 0xAA, 0xCE, 0x28, 0x1B, 0xEE, + 0x7F, 0x5C, 0xFB, 0x62, 0x05, 0xD9, 0xDD, 0x9D, + 0x49, 0x66, 0x82, 0x71, 0xD2, 0xC7, 0xEB, 0xCF, + 0x5B, 0x41, 0x25, 0xC8, 0x6C, 0xFF, 0x78, 0x97, + 0x0C, 0xA2, 0x50, 0x7A, 0xAF, 0x2F, 0xB0, 0x7E, + 0xBB, 0x73, 0xA0, 0x9B, 0x09, 0xDE, 0x35, 0xE9, + 0x5A, 0x70, 0x56, 0xC5, 0x81, 0x19, 0x55, 0xAB, + 0xC1, 0xB4, 0x2A, 0x30, 0x54, 0x6F, 0x3E, 0x46, + 0x5D, 0x37, 0xF5, 0x57, 0x6B, 0x7C, 0x43, 0xE1, + 0x4A, 0x3F, 0xB2, 0x4B, 0x77, 0xB5, 0x44, 0xD6, + 0x91, 0x11, 0x72, 0xE8, 0xBE, 0xA5, 0xA8, 0xD3, + 0x9A, 0x17, 0x86, 0x88, 0x16, 0x3C, 0x36, 0xD8, + 0x6E, 0x07, 0x8D, 0x5F, 0xFA, 0xF1, 0x24, 0x7D, + 0x20, 0x60, 0x0D, 0x89, 0xC9, 0x29, 0xA7, 0x2B, + 0x4E, 0x10, 0x9F, 0xE5, 0x61, 0x32, 0x3A, 0xBF, + 0x93, 0xE6, 0xF3, 0x52, 0x80, 0xC4, 0x02, 0x22, + 0xA4, 0xBD, 0xF0, 0x48, 0x51, 0xF2, 0xD7, 0x33, + 0x00, 0x53, 0x98, 0xEC, 0x47, 0x39, 0xB9, 0x90, + 0x76, 0x4F, 0x68, 0x3D, 0x9C, 0x92, 0xD5, 0xB8, + 0xAE, 0xD0, 0xF4, 0x67, 0x58, 0xC0, 0x06, 0x08, + 0x14, 0x31, 0xDC, 0xA1, 0x15, 0xDF, 0xCA, 0xE2, + 0x23, 0xFE, 0xE4, 0x8F, 0x0A, 0xFC, 0x8A, 0xA3, + 0xC6, 0xCD, 0x6A, 0x75, 0xFD, 0x42, 0xB7, 0x79, + 0x96, 0x1D, 0x63, 0x18, 0xA9, 0x1C, 0x83, 0x6D, + 0xE0, 0x34, 0x04, 0xA6, 0x13, 0xAC, 0xD1, 0xF7, + 0x26, 0xC3, 0x1F, 0x27, 0x45, 0x95, 0xCB, 0x21, + 0xED, 0x1A, 0x9E, 0x99, 0xEA, 0x40, 0x94, 0x4D, + 0x69, 0xF6, 0xEF, 0xC2, 0xAD, 0x03, 0xB3, 0x1E }; + +unsigned char table_146[256] = { + 0x1C, 0xF5, 0x16, 0xD2, 0xCC, 0xDC, 0x1E, 0x29, + 0xE3, 0x17, 0x3B, 0x66, 0x6A, 0xF7, 0x03, 0xB2, + 0x92, 0x45, 0x4D, 0xD6, 0x0C, 0x5E, 0xE6, 0x01, + 0xDE, 0xCE, 0x83, 0xFA, 0x35, 0x02, 0x85, 0xC4, + 0x2E, 0x89, 0x8D, 0xE7, 0x30, 0x93, 0xDD, 0x70, + 0x80, 0xD9, 0x6D, 0x81, 0x07, 0x8E, 0xA9, 0xA6, + 0x5F, 0xC9, 0xF3, 0x9D, 0x65, 0xE8, 0x88, 0x0B, + 0x49, 0xAA, 0xB7, 0x6C, 0x11, 0xFC, 0x6F, 0xA3, + 0xF8, 0x52, 0x0E, 0xD4, 0x08, 0x25, 0x27, 0x33, + 0x2F, 0xF0, 0x2B, 0x47, 0xDA, 0x4C, 0x39, 0x54, + 0xB9, 0xC1, 0xEA, 0x7C, 0x44, 0xEB, 0x06, 0xE1, + 0x8C, 0x9B, 0x74, 0x42, 0x4F, 0x0A, 0x69, 0x2A, + 0x2D, 0xA1, 0x19, 0xD5, 0xC3, 0x87, 0x68, 0xFF, + 0xEC, 0xE4, 0x86, 0xCF, 0xF6, 0x79, 0x34, 0xA8, + 0x72, 0xF4, 0x8B, 0xAF, 0xA5, 0x00, 0xBA, 0x5C, + 0x23, 0xB8, 0xC8, 0x59, 0xBF, 0x6E, 0xCB, 0x20, + 0x1F, 0x53, 0x97, 0x4B, 0xD0, 0x55, 0x5B, 0xDF, + 0x8A, 0xED, 0x9A, 0x62, 0xC5, 0xD7, 0x18, 0x82, + 0xC7, 0x12, 0x15, 0x1B, 0xC0, 0x38, 0xCA, 0x26, + 0xDB, 0xAE, 0xF9, 0x90, 0x1A, 0xF2, 0x56, 0x32, + 0x21, 0x3C, 0x43, 0xEE, 0xA4, 0x13, 0x94, 0xA2, + 0x46, 0x77, 0xBC, 0xB6, 0x9C, 0x0D, 0xCD, 0x37, + 0x63, 0x60, 0x6B, 0x3A, 0x3E, 0xA7, 0xD8, 0xFE, + 0xFB, 0xEF, 0x67, 0xFD, 0xAD, 0xF1, 0x09, 0x1D, + 0xE9, 0x51, 0xB4, 0x95, 0x75, 0x0F, 0xB3, 0xD3, + 0xAB, 0x22, 0xBB, 0x61, 0x7F, 0x5A, 0x58, 0x7B, + 0x73, 0xC2, 0x05, 0xE0, 0x14, 0xE2, 0xAC, 0x91, + 0xBE, 0x4E, 0xC6, 0x7A, 0x84, 0x50, 0x28, 0x3F, + 0xB0, 0x04, 0x7E, 0xD1, 0x40, 0xBD, 0xE5, 0x71, + 0xB1, 0x78, 0x41, 0x9E, 0x57, 0x64, 0x8F, 0x24, + 0x4A, 0x9F, 0x3D, 0x31, 0x36, 0x5D, 0xA0, 0x2C, + 0x7D, 0x96, 0x76, 0x99, 0xB5, 0x48, 0x98, 0x10 }; + +unsigned char table_147[32] = { + 0x17, 0x07, 0x0D, 0x16, 0x00, 0x1B, 0x1F, 0x09, + 0x10, 0x11, 0x14, 0x0A, 0x02, 0x06, 0x13, 0x0C, + 0x08, 0x1E, 0x0F, 0x12, 0x05, 0x15, 0x19, 0x01, + 0x1C, 0x1A, 0x03, 0x18, 0x04, 0x0B, 0x1D, 0x0E }; + +unsigned char table_148[256] = { + 0xFB, 0x23, 0xBC, 0x5A, 0x8C, 0x02, 0x42, 0x3B, + 0x95, 0x0C, 0x21, 0x0E, 0x14, 0xDF, 0x11, 0xC0, + 0xDB, 0x5E, 0xD3, 0xEA, 0xCE, 0xB4, 0x32, 0x12, + 0x70, 0x68, 0xA3, 0x25, 0x5B, 0x4B, 0x47, 0xA5, + 0x84, 0x9B, 0xFA, 0xD1, 0xE1, 0x3C, 0x20, 0x93, + 0x41, 0x26, 0x81, 0x39, 0x17, 0xA4, 0xCF, 0xB9, + 0xC5, 0x5F, 0x1C, 0xB3, 0x88, 0xC2, 0x92, 0x30, + 0x0A, 0xB8, 0xA0, 0xE2, 0x50, 0x2B, 0x48, 0x1E, + 0xD5, 0x13, 0xC7, 0x46, 0x9E, 0x2A, 0xF7, 0x7E, + 0xE8, 0x82, 0x60, 0x7A, 0x36, 0x97, 0x0F, 0x8F, + 0x8B, 0x80, 0xE0, 0xEB, 0xB1, 0xC6, 0x6E, 0xAE, + 0x90, 0x76, 0xA7, 0x31, 0xBE, 0x9C, 0x18, 0x6D, + 0xAB, 0x6C, 0x7B, 0xFE, 0x62, 0x05, 0xE9, 0x66, + 0x2E, 0x38, 0xB5, 0xB2, 0xFD, 0xFC, 0x7F, 0xE3, + 0xA1, 0xF1, 0x99, 0x4D, 0x79, 0x22, 0xD2, 0x37, + 0x29, 0x01, 0x54, 0x00, 0xBD, 0x51, 0x1B, 0x07, + 0x0B, 0x4A, 0xEE, 0x57, 0xDA, 0x1A, 0x06, 0xCA, + 0xCB, 0x9A, 0xC9, 0x7D, 0xE4, 0xDC, 0xE5, 0x8D, + 0x75, 0x4F, 0xF6, 0xA2, 0x65, 0x7C, 0xD9, 0x9D, + 0x03, 0x27, 0x2D, 0x4C, 0x49, 0xD4, 0x5D, 0x3E, + 0xBA, 0x1D, 0xD8, 0x91, 0x74, 0x10, 0xF8, 0xDE, + 0xEF, 0xF0, 0x6A, 0x04, 0x72, 0x08, 0x78, 0x3A, + 0x53, 0xC4, 0x34, 0xF2, 0x64, 0xAF, 0x86, 0xC3, + 0xF3, 0x73, 0x67, 0xCC, 0x58, 0xF4, 0x96, 0xAC, + 0x3D, 0xE7, 0x15, 0x8E, 0x19, 0x61, 0xF9, 0xB6, + 0xCD, 0x87, 0xAA, 0xB0, 0x1F, 0x6F, 0xAD, 0x28, + 0xC8, 0x69, 0x56, 0xC1, 0x71, 0xED, 0xE6, 0x98, + 0x6B, 0x59, 0xB7, 0xF5, 0x2C, 0xEC, 0xA8, 0x94, + 0x89, 0xBB, 0xA9, 0xD7, 0x2F, 0x8A, 0x4E, 0xD6, + 0x33, 0x16, 0x0D, 0x83, 0x5C, 0x52, 0x85, 0xA6, + 0x40, 0x45, 0x9F, 0x44, 0x63, 0x35, 0x77, 0xFF, + 0x09, 0x43, 0xBF, 0xD0, 0x55, 0xDD, 0x3F, 0x24 }; + +unsigned char table_149[32] = { + 0x1B, 0x0B, 0x0C, 0x06, 0x1F, 0x17, 0x04, 0x1A, + 0x1E, 0x02, 0x0F, 0x16, 0x0E, 0x09, 0x10, 0x01, + 0x13, 0x19, 0x11, 0x00, 0x0A, 0x05, 0x03, 0x1C, + 0x18, 0x1D, 0x14, 0x0D, 0x07, 0x08, 0x15, 0x12 }; + +unsigned char table_150[256] = { + 0x57, 0xBC, 0x9D, 0x46, 0x14, 0xD0, 0x94, 0x95, + 0x1B, 0x12, 0xB8, 0xD4, 0x53, 0x73, 0x83, 0xE6, + 0x75, 0xE1, 0xD1, 0x0D, 0xDF, 0x23, 0x13, 0x40, + 0xF1, 0x0C, 0xA0, 0xC1, 0x22, 0xDA, 0xE8, 0xFB, + 0xE5, 0xC4, 0x16, 0x9C, 0x3F, 0xC3, 0x78, 0x3A, + 0x06, 0xC7, 0xA8, 0x79, 0xA4, 0xB3, 0x55, 0x88, + 0xA9, 0x82, 0xE3, 0x68, 0xFC, 0x3B, 0x26, 0x81, + 0xB4, 0x0A, 0x7D, 0x96, 0xDB, 0x2C, 0xE2, 0xCD, + 0x92, 0x5C, 0xED, 0x0E, 0x42, 0x98, 0xBE, 0xB7, + 0x63, 0x25, 0x7B, 0xD9, 0xEF, 0x11, 0xB9, 0xA3, + 0xFA, 0x00, 0x2A, 0x91, 0x71, 0xBF, 0xB2, 0x3D, + 0x20, 0x4C, 0xB0, 0x8C, 0x3C, 0x27, 0xAF, 0x09, + 0x10, 0x5D, 0x2B, 0x1D, 0xBD, 0x4B, 0x54, 0xD3, + 0xAB, 0x1A, 0xE7, 0xF8, 0x56, 0x65, 0xA5, 0xAD, + 0xEC, 0x17, 0x45, 0x28, 0xCA, 0xEA, 0x01, 0xF5, + 0x34, 0x84, 0x43, 0x8B, 0x03, 0x02, 0x90, 0x6B, + 0x60, 0xCE, 0x19, 0x86, 0x4F, 0x08, 0x35, 0x9A, + 0xAE, 0x07, 0xE0, 0xB6, 0xD6, 0x2D, 0xD2, 0x89, + 0x5F, 0xA6, 0x72, 0x05, 0x36, 0xB5, 0xC0, 0x5A, + 0x4D, 0xD7, 0x30, 0x37, 0x87, 0x50, 0xA2, 0x48, + 0x29, 0xAC, 0xDE, 0x93, 0x24, 0x6E, 0x1E, 0xF7, + 0x52, 0x5E, 0x41, 0xC8, 0xEB, 0x31, 0x7E, 0xE9, + 0x67, 0x7A, 0x47, 0x85, 0x8D, 0x74, 0x9E, 0x64, + 0x38, 0x9B, 0xBA, 0xCC, 0x9F, 0x8E, 0xEE, 0x0F, + 0xB1, 0x7C, 0x6A, 0xBB, 0x2E, 0x58, 0x70, 0x7F, + 0x4E, 0x4A, 0x1C, 0x5B, 0xF0, 0xA1, 0x61, 0xF6, + 0x15, 0x33, 0xE4, 0xF9, 0x2F, 0x62, 0x1F, 0x76, + 0x32, 0xCB, 0x49, 0xFE, 0x8F, 0xD5, 0xDC, 0x66, + 0x0B, 0x3E, 0xC5, 0x21, 0xC6, 0x6C, 0x18, 0xC2, + 0x6D, 0xFF, 0x51, 0x99, 0xCF, 0xFD, 0x59, 0xA7, + 0xAA, 0x8A, 0xF2, 0x69, 0x39, 0x6F, 0x77, 0xDD, + 0x97, 0xC9, 0xF3, 0x04, 0xD8, 0xF4, 0x80, 0x44 }; + +unsigned char table_151[256] = { + 0x78, 0x6C, 0xC5, 0x0C, 0x2D, 0xA7, 0x97, 0x9C, + 0x22, 0x76, 0x3E, 0x81, 0x51, 0x47, 0x59, 0x71, + 0xB1, 0xA2, 0x4A, 0x3C, 0xB5, 0x16, 0x06, 0x95, + 0xB9, 0x01, 0xE6, 0x91, 0x96, 0x1C, 0x1B, 0xAD, + 0x61, 0x64, 0xB2, 0xE7, 0x29, 0x19, 0x52, 0x3B, + 0xFA, 0xAF, 0x30, 0xDB, 0xD4, 0x0B, 0xFE, 0x75, + 0x1F, 0xBE, 0xCB, 0xF6, 0xEA, 0x31, 0xF8, 0xD8, + 0xA3, 0x82, 0x73, 0x1D, 0x99, 0xF0, 0xCC, 0xB6, + 0x46, 0x26, 0xAA, 0x8C, 0x87, 0x90, 0x24, 0x8F, + 0x7A, 0x13, 0xEE, 0xD1, 0xA9, 0x05, 0xB3, 0xF7, + 0x02, 0x7C, 0x4C, 0x1E, 0xFF, 0xE5, 0x77, 0xAB, + 0xD6, 0x98, 0x20, 0x4D, 0xC4, 0x23, 0xF4, 0xA4, + 0x85, 0x9A, 0x8E, 0x1A, 0x0E, 0xF5, 0x15, 0x60, + 0x38, 0x72, 0xE9, 0xF1, 0xC3, 0x68, 0xF2, 0x93, + 0xD3, 0x2A, 0x48, 0x74, 0xC2, 0x57, 0xA1, 0x7D, + 0x94, 0x37, 0x92, 0x5C, 0xE1, 0x41, 0x83, 0xD5, + 0x65, 0x14, 0xA6, 0xDC, 0x44, 0x27, 0xEF, 0xD7, + 0x25, 0x10, 0x2C, 0x7F, 0x40, 0xA5, 0x55, 0xBD, + 0x2B, 0x0D, 0xD0, 0xFC, 0xDF, 0xA0, 0x04, 0x00, + 0x62, 0xB4, 0x5A, 0xEB, 0x6B, 0x84, 0x7E, 0x6A, + 0xDE, 0xED, 0x66, 0x03, 0xFB, 0x2E, 0x4F, 0x4E, + 0xBB, 0x36, 0x5B, 0x18, 0xE3, 0x69, 0x3F, 0xEC, + 0xE4, 0xD2, 0x0A, 0x34, 0x63, 0xCF, 0xA8, 0xF9, + 0x9B, 0x7B, 0x6F, 0xE8, 0x49, 0xC1, 0x09, 0x54, + 0xF3, 0x50, 0x67, 0x79, 0xC0, 0x9F, 0x8D, 0x5F, + 0x17, 0x70, 0x11, 0xC8, 0xBC, 0xC6, 0xE0, 0x35, + 0x39, 0xC7, 0x6E, 0x21, 0xBF, 0xDA, 0x6D, 0x28, + 0x0F, 0xDD, 0x33, 0xAC, 0x8A, 0x12, 0xC9, 0xCD, + 0xB8, 0x45, 0xAE, 0x32, 0xCE, 0xE2, 0x56, 0xFD, + 0x42, 0x89, 0x86, 0xCA, 0x4B, 0x3D, 0x5E, 0xBA, + 0x8B, 0x5D, 0xB0, 0xB7, 0xD9, 0x58, 0x2F, 0x08, + 0x43, 0x3A, 0x53, 0x9E, 0x80, 0x88, 0x07, 0x9D }; + +unsigned char table_152[32] = { + 0x02, 0x1A, 0x17, 0x1D, 0x01, 0x03, 0x13, 0x1E, + 0x05, 0x18, 0x06, 0x0A, 0x0C, 0x04, 0x1B, 0x00, + 0x1C, 0x09, 0x1F, 0x16, 0x07, 0x0F, 0x0B, 0x0E, + 0x14, 0x12, 0x0D, 0x10, 0x19, 0x11, 0x08, 0x15 }; + +unsigned char table_153[32] = { + 0x0E, 0x14, 0x12, 0x1E, 0x1C, 0x02, 0x06, 0x16, + 0x18, 0x0D, 0x17, 0x0C, 0x1D, 0x11, 0x08, 0x19, + 0x07, 0x0F, 0x13, 0x04, 0x03, 0x1B, 0x0B, 0x1F, + 0x1A, 0x0A, 0x05, 0x10, 0x00, 0x01, 0x15, 0x09 }; + +unsigned char table_154[256] = { + 0x27, 0x5A, 0x08, 0x5B, 0xF4, 0x39, 0x13, 0x6F, + 0x67, 0xEA, 0x22, 0xCA, 0x5C, 0xCF, 0x18, 0x7C, + 0x05, 0x87, 0x60, 0xCC, 0x40, 0xC6, 0xE8, 0x6D, + 0xF5, 0x2A, 0x2D, 0xA2, 0x8C, 0x82, 0xE9, 0xDC, + 0xD6, 0x65, 0x74, 0x8E, 0x42, 0x4F, 0x3E, 0x55, + 0xFF, 0xC7, 0x9D, 0x0F, 0x81, 0xE2, 0x4C, 0xE6, + 0xEB, 0x4D, 0x70, 0xD1, 0x49, 0x43, 0x3D, 0x69, + 0x0C, 0x45, 0x28, 0x00, 0x99, 0xAE, 0xEC, 0xB8, + 0xC3, 0x17, 0x93, 0x8D, 0x36, 0x3C, 0x46, 0x2B, + 0x29, 0xC5, 0xB4, 0xB1, 0xD0, 0x0D, 0xAD, 0xFE, + 0xE5, 0xA8, 0x3B, 0x1A, 0x2C, 0xDF, 0x07, 0x86, + 0xB0, 0xD3, 0x7A, 0x59, 0x79, 0x8B, 0xC1, 0x9A, + 0x30, 0xDB, 0x24, 0xF3, 0xD8, 0x04, 0x25, 0xC2, + 0xA3, 0x98, 0x96, 0x7B, 0x71, 0x4E, 0x5E, 0x58, + 0xA5, 0x51, 0x88, 0xDA, 0xF8, 0xC0, 0x7D, 0xF6, + 0x31, 0x5F, 0x09, 0x16, 0x21, 0x62, 0x01, 0x64, + 0x9B, 0x3A, 0x2F, 0x61, 0x19, 0xA1, 0xB7, 0xE0, + 0xB9, 0x12, 0xA0, 0xBA, 0x6E, 0x8A, 0xFB, 0xD9, + 0x38, 0x1B, 0xD5, 0xB3, 0x10, 0xED, 0xE4, 0x6A, + 0x32, 0xBD, 0x75, 0xD4, 0x1C, 0xFD, 0x73, 0x77, + 0x54, 0xC8, 0x97, 0x47, 0x35, 0x94, 0xE3, 0xCD, + 0x6B, 0xBB, 0xF9, 0xAC, 0x11, 0x14, 0xAF, 0x78, + 0x3F, 0xCE, 0x26, 0x44, 0xEE, 0xFC, 0x15, 0x66, + 0x4B, 0xA6, 0x20, 0x23, 0xBE, 0x84, 0x1D, 0x7E, + 0x0B, 0x56, 0x92, 0x0A, 0xFA, 0xF7, 0x48, 0x33, + 0x9E, 0x8F, 0xAB, 0x5D, 0x41, 0x50, 0xA4, 0x7F, + 0x80, 0x4A, 0x68, 0x06, 0x2E, 0x6C, 0xC4, 0x02, + 0x0E, 0x63, 0xF0, 0xC9, 0x91, 0xB2, 0xD2, 0x03, + 0x37, 0xEF, 0x9C, 0x90, 0x83, 0x76, 0x1E, 0xA9, + 0x85, 0xB6, 0x57, 0xD7, 0xF2, 0xF1, 0xE7, 0xDE, + 0xCB, 0xAA, 0xBF, 0x89, 0x1F, 0xA7, 0xBC, 0x9F, + 0x53, 0xE1, 0xDD, 0x72, 0x95, 0x52, 0x34, 0xB5 }; + +unsigned char table_155[256] = { + 0x75, 0x58, 0xC5, 0xA5, 0x83, 0x16, 0xF3, 0x7F, + 0x94, 0xDE, 0xA0, 0xF6, 0xFD, 0x89, 0xA8, 0x06, + 0x98, 0x01, 0xD9, 0x69, 0xB7, 0x0F, 0xEA, 0x73, + 0x32, 0xF0, 0x49, 0xBF, 0x02, 0xE7, 0x22, 0x3F, + 0xDB, 0x30, 0x5F, 0x20, 0x6A, 0x93, 0x07, 0xBC, + 0x09, 0x0D, 0x37, 0x24, 0x90, 0x15, 0x80, 0xAF, + 0x8F, 0x59, 0x28, 0xFF, 0x6D, 0x1E, 0x52, 0x62, + 0xE2, 0xDD, 0x85, 0x48, 0xB5, 0xAB, 0x68, 0xAC, + 0x7E, 0x26, 0x2C, 0xF9, 0x2A, 0xBE, 0x5B, 0xCE, + 0x87, 0x1D, 0x96, 0xBD, 0xEF, 0x29, 0xA9, 0xC3, + 0x9D, 0x57, 0x79, 0x6B, 0x7A, 0x82, 0x78, 0x0A, + 0x91, 0xF2, 0x7C, 0xC2, 0x25, 0x88, 0xE3, 0x47, + 0x64, 0x46, 0x8D, 0x19, 0xF4, 0xE6, 0xF1, 0x53, + 0x9C, 0x54, 0x23, 0xAD, 0xA3, 0x86, 0x3A, 0x04, + 0x67, 0x1C, 0xF5, 0x43, 0x05, 0x42, 0xD6, 0x4B, + 0xFB, 0xD4, 0x2B, 0x08, 0x45, 0xD8, 0xCD, 0xEB, + 0x31, 0x4A, 0x5A, 0x34, 0x9B, 0xEC, 0x4D, 0xB4, + 0xC6, 0xFE, 0xD5, 0x5E, 0xC1, 0x39, 0x81, 0xCF, + 0x03, 0x6E, 0x95, 0x50, 0xA1, 0x3B, 0xB3, 0xE5, + 0x3D, 0xB1, 0xB2, 0x41, 0x17, 0x2F, 0x2E, 0xE4, + 0x1F, 0xDC, 0xB0, 0xB6, 0x18, 0x6F, 0x44, 0x12, + 0x0B, 0xCC, 0x4E, 0xC0, 0x51, 0x14, 0x76, 0x3C, + 0xB9, 0x9F, 0xA4, 0xD3, 0xA7, 0xE8, 0x13, 0x55, + 0xC8, 0x8C, 0xD2, 0xEE, 0x65, 0xB8, 0xAA, 0x6C, + 0x2D, 0x4F, 0x56, 0xFA, 0x61, 0x4C, 0xE0, 0x5C, + 0xA6, 0x1A, 0xD1, 0x38, 0xD7, 0x72, 0x60, 0x74, + 0xE1, 0xBA, 0x84, 0x3E, 0x40, 0xF8, 0xC7, 0x36, + 0x27, 0x0C, 0x70, 0x97, 0x9A, 0x7D, 0x35, 0x71, + 0xCA, 0x1B, 0x99, 0x8E, 0xAE, 0x66, 0x63, 0xE9, + 0xC9, 0x11, 0x8A, 0x21, 0x92, 0x5D, 0x77, 0x10, + 0xD0, 0xC4, 0xF7, 0x7B, 0x9E, 0xCB, 0xED, 0x0E, + 0x8B, 0x33, 0xFC, 0xBB, 0x00, 0xA2, 0xDF, 0xDA }; + +unsigned char table_156[256] = { + 0x31, 0x25, 0xB1, 0xD3, 0xAF, 0xAE, 0x84, 0x2C, + 0x71, 0x5E, 0xD8, 0x80, 0x6F, 0x3E, 0x48, 0x86, + 0xED, 0x54, 0x6A, 0xC3, 0xBC, 0xBF, 0x0E, 0xEA, + 0x10, 0xA2, 0x9D, 0x91, 0x32, 0xE2, 0x7E, 0x1B, + 0x49, 0x27, 0xFF, 0xDD, 0x8A, 0x2F, 0x8D, 0x38, + 0xFA, 0x3C, 0x03, 0x14, 0x0F, 0x89, 0xCC, 0x07, + 0x1A, 0xA0, 0x97, 0x37, 0xA6, 0xD6, 0x63, 0x87, + 0xA1, 0xC2, 0x4B, 0x39, 0xCB, 0xCF, 0x69, 0x4E, + 0xC9, 0x28, 0x1C, 0xBB, 0x42, 0x2B, 0xA9, 0x78, + 0x5B, 0xF6, 0xE0, 0xD0, 0x5F, 0x46, 0x98, 0xCE, + 0x1F, 0x7A, 0x34, 0x8B, 0xFD, 0x9B, 0xEF, 0x74, + 0x05, 0xF2, 0x02, 0xC6, 0xDF, 0x73, 0x5C, 0x8E, + 0xDE, 0x88, 0x57, 0x3B, 0x85, 0xBD, 0xC0, 0x3A, + 0x45, 0x4D, 0x2D, 0x72, 0x0C, 0x60, 0xCA, 0x5D, + 0x06, 0x04, 0x3D, 0x51, 0x15, 0xAD, 0xE8, 0x67, + 0xBA, 0x43, 0x7D, 0xF8, 0xB2, 0xE6, 0xAB, 0xF4, + 0x23, 0x6E, 0xF0, 0x6B, 0x0B, 0x2E, 0xC8, 0xC4, + 0x4F, 0xA8, 0x6D, 0x26, 0xE9, 0x9C, 0x22, 0xB7, + 0x00, 0xB3, 0x0A, 0x7C, 0x44, 0x55, 0x75, 0xD5, + 0xAA, 0x66, 0x56, 0x24, 0x83, 0x90, 0xA4, 0xF5, + 0xCD, 0xEC, 0x18, 0xDC, 0xFE, 0x96, 0xA3, 0xF7, + 0xD2, 0xFB, 0xD1, 0x65, 0xC5, 0x08, 0x7B, 0x70, + 0x16, 0x9A, 0x20, 0x09, 0x29, 0xDA, 0x52, 0x5A, + 0x59, 0xB4, 0x77, 0x62, 0x9E, 0x19, 0x7F, 0x82, + 0x4C, 0xB6, 0x0D, 0x58, 0xEE, 0x1D, 0xB9, 0x93, + 0x50, 0xD9, 0x30, 0xE4, 0x13, 0x01, 0x36, 0x8F, + 0x53, 0x3F, 0x64, 0xA5, 0xB5, 0xD7, 0x81, 0x41, + 0x17, 0xE5, 0x94, 0xE3, 0xF9, 0x61, 0x76, 0xE1, + 0x9F, 0xFC, 0x1E, 0x12, 0xDB, 0x21, 0x79, 0x2A, + 0xAC, 0xF3, 0x6C, 0xC1, 0x95, 0x92, 0xEB, 0xA7, + 0x11, 0xC7, 0xB8, 0x4A, 0x33, 0xB0, 0x99, 0xE7, + 0xF1, 0x68, 0xBE, 0x35, 0x40, 0x8C, 0xD4, 0x47 }; + +unsigned char table_157[32] = { + 0x00, 0x0D, 0x03, 0x02, 0x11, 0x04, 0x18, 0x0B, + 0x14, 0x1D, 0x1C, 0x13, 0x1B, 0x17, 0x10, 0x15, + 0x01, 0x19, 0x07, 0x09, 0x1A, 0x16, 0x12, 0x1E, + 0x08, 0x06, 0x0C, 0x0E, 0x1F, 0x0F, 0x0A, 0x05 }; + +unsigned char table_158[256] = { + 0x68, 0x26, 0x80, 0x0B, 0xB8, 0xD5, 0x8C, 0xB7, + 0x65, 0xEF, 0xBC, 0x94, 0x28, 0xB9, 0xB2, 0xD2, + 0x92, 0xA4, 0x55, 0x27, 0xE0, 0x40, 0x6C, 0x41, + 0x25, 0xBD, 0xAF, 0xEA, 0xB1, 0x19, 0xA5, 0xC9, + 0x0E, 0xED, 0xB4, 0xF9, 0x8B, 0x6A, 0xAE, 0xD8, + 0x64, 0x83, 0xC1, 0xD3, 0x04, 0xF4, 0xFA, 0xC3, + 0x46, 0x2C, 0xA8, 0xBB, 0x3A, 0x47, 0x33, 0x8F, + 0x52, 0x86, 0x08, 0x9D, 0x1D, 0x59, 0x8E, 0x91, + 0x32, 0xCF, 0x6B, 0x75, 0xB0, 0x7F, 0xC7, 0x24, + 0x05, 0x6F, 0x00, 0x1C, 0x2D, 0xAC, 0xDA, 0x45, + 0x73, 0xB3, 0x3E, 0xD6, 0x54, 0x61, 0x03, 0x77, + 0xF8, 0xD9, 0xE2, 0x4B, 0xFF, 0xF2, 0x0C, 0x4F, + 0x93, 0x71, 0xA7, 0x3D, 0x66, 0x88, 0x98, 0xF1, + 0xB6, 0x7A, 0x2B, 0xCD, 0x44, 0x3C, 0x37, 0x5A, + 0x96, 0x23, 0x9F, 0xBF, 0x7D, 0x5E, 0x2A, 0x35, + 0x72, 0x79, 0xE1, 0xA3, 0x84, 0x99, 0x38, 0x49, + 0xC8, 0xDB, 0x30, 0xDC, 0xAD, 0x3F, 0xF6, 0x09, + 0x69, 0x95, 0xE5, 0x67, 0xA1, 0xFD, 0xF7, 0x1B, + 0xEC, 0x17, 0xD4, 0xEB, 0x29, 0x36, 0x3B, 0x15, + 0xDE, 0x2E, 0xC5, 0x70, 0x6D, 0x53, 0x56, 0xAB, + 0xC0, 0x43, 0xC2, 0xE7, 0x31, 0xE6, 0xA6, 0x78, + 0x5C, 0x7C, 0x48, 0x10, 0x87, 0xCC, 0x9E, 0x7E, + 0x5F, 0xE9, 0x07, 0x5B, 0xF5, 0xEE, 0xB5, 0xCA, + 0x62, 0x18, 0xBE, 0x20, 0x16, 0xDF, 0x13, 0x4E, + 0x7B, 0x02, 0x11, 0x4C, 0x51, 0x85, 0x0D, 0x22, + 0xF3, 0x14, 0x63, 0x76, 0xD0, 0x0F, 0xE4, 0xCB, + 0xCE, 0xA0, 0x82, 0xE3, 0x01, 0xAA, 0x5D, 0x4A, + 0x4D, 0xFB, 0x39, 0x8A, 0x2F, 0xDD, 0xE8, 0x06, + 0x1A, 0x90, 0x81, 0x50, 0x8D, 0x89, 0x97, 0x1E, + 0xFC, 0x60, 0x12, 0x42, 0x9C, 0xF0, 0x34, 0xD7, + 0xD1, 0x1F, 0x0A, 0x21, 0xA9, 0x6E, 0xC4, 0xBA, + 0x9A, 0x57, 0xA2, 0x74, 0xC6, 0xFE, 0x9B, 0x58 }; + +unsigned char table_159[256] = { + 0xE5, 0xBF, 0x84, 0x56, 0xD6, 0x43, 0x3E, 0xA5, + 0x64, 0x87, 0x44, 0x63, 0x4A, 0x4C, 0x8D, 0x24, + 0x1C, 0xDA, 0x89, 0x52, 0x80, 0x4F, 0xE4, 0xBC, + 0xC5, 0xF4, 0x27, 0x75, 0x9C, 0xF0, 0xE1, 0x06, + 0x99, 0x48, 0xF2, 0x57, 0x34, 0x9A, 0xA8, 0x62, + 0xC9, 0xD5, 0x16, 0x6D, 0x55, 0xFA, 0x37, 0x5A, + 0x2A, 0xC6, 0x45, 0xDD, 0x1B, 0x76, 0x50, 0xE2, + 0x69, 0x41, 0x6C, 0xC4, 0x3C, 0x47, 0xA9, 0x92, + 0x00, 0x3D, 0x6F, 0xE7, 0x7A, 0x3A, 0x33, 0x53, + 0xF7, 0x03, 0xA7, 0xB1, 0x15, 0x78, 0x0B, 0x67, + 0x2E, 0x21, 0xF1, 0xD4, 0xB3, 0x98, 0x60, 0x58, + 0xBB, 0x82, 0x1E, 0x70, 0x0A, 0xA2, 0x02, 0x17, + 0xFF, 0x9F, 0xD2, 0xAF, 0xC7, 0xDC, 0x68, 0x83, + 0x42, 0xCA, 0x08, 0x39, 0x20, 0xEC, 0x77, 0x96, + 0x5B, 0xAD, 0x09, 0x6B, 0x40, 0xC2, 0x91, 0x51, + 0x10, 0xD9, 0xF9, 0xC1, 0xB5, 0xDF, 0xDB, 0xC0, + 0x7D, 0xAB, 0xAE, 0x54, 0x35, 0xF3, 0xA1, 0xE6, + 0xEA, 0x14, 0xBA, 0xFC, 0xE8, 0xEB, 0xF6, 0xBD, + 0x8C, 0x72, 0x1F, 0xE9, 0xFB, 0x7C, 0xCF, 0x49, + 0xE3, 0xA3, 0x22, 0x9D, 0x46, 0x71, 0x94, 0x31, + 0x2D, 0x65, 0x2B, 0x32, 0x18, 0xB6, 0x90, 0xF8, + 0x11, 0x5F, 0xA0, 0xEF, 0xED, 0x1A, 0x25, 0x2C, + 0x3B, 0xFD, 0x2F, 0x73, 0xB9, 0x7E, 0xDE, 0xB4, + 0x97, 0x0F, 0x7F, 0x86, 0x93, 0x07, 0x19, 0xCE, + 0xE0, 0xB7, 0xEE, 0x26, 0xD1, 0x01, 0x59, 0x5C, + 0xC3, 0x79, 0x8B, 0xD3, 0x4B, 0x04, 0xD0, 0x29, + 0x0D, 0x3F, 0xB2, 0x30, 0xCC, 0x36, 0xFE, 0xB0, + 0xF5, 0x8E, 0xA6, 0x8A, 0xC8, 0xD8, 0x05, 0xB8, + 0x12, 0xBE, 0x81, 0x4D, 0x38, 0xAC, 0x1D, 0x9E, + 0x66, 0x5E, 0x7B, 0x6E, 0x0C, 0xCD, 0x6A, 0x88, + 0xAA, 0x0E, 0x61, 0x5D, 0x95, 0x4E, 0xD7, 0x74, + 0xCB, 0x9B, 0x13, 0x8F, 0xA4, 0x28, 0x23, 0x85 }; + +unsigned char table_160[256] = { + 0x35, 0x44, 0x0E, 0x92, 0x75, 0x83, 0x9D, 0x53, + 0xA5, 0x90, 0xF8, 0xF7, 0x54, 0x74, 0xDF, 0x3D, + 0x5A, 0xAA, 0xC6, 0x26, 0x7A, 0xFC, 0x79, 0x6C, + 0x56, 0xB3, 0x32, 0xE3, 0x1C, 0xF9, 0xDC, 0xE6, + 0xA2, 0x93, 0x71, 0xFF, 0x1D, 0xEB, 0xB2, 0x04, + 0x96, 0x46, 0x0C, 0x2B, 0x17, 0xEE, 0x28, 0x25, + 0xD9, 0xAE, 0x11, 0xA7, 0x40, 0x45, 0xFB, 0x80, + 0x18, 0xF1, 0xCB, 0x2E, 0x24, 0xF3, 0xEC, 0x4F, + 0xAB, 0xD7, 0xD4, 0xC4, 0xFD, 0x4B, 0xAD, 0xC9, + 0x4C, 0x08, 0xAC, 0xF4, 0xCD, 0xB7, 0xF2, 0x15, + 0x02, 0x2F, 0x16, 0x34, 0x65, 0x8A, 0x87, 0xCC, + 0x50, 0x0F, 0x9B, 0xC2, 0xC8, 0x7B, 0xEA, 0x8E, + 0xE4, 0xD6, 0x97, 0x30, 0xA8, 0xA0, 0x94, 0xC5, + 0xE8, 0x12, 0x27, 0xCE, 0x84, 0xDD, 0xB1, 0x47, + 0x7E, 0xE7, 0xE1, 0x3A, 0x37, 0x21, 0x2D, 0x3B, + 0x20, 0x60, 0x1E, 0x1B, 0x82, 0xBE, 0xA3, 0x70, + 0x98, 0xBF, 0xA6, 0x4D, 0x76, 0x86, 0x42, 0x9F, + 0xCF, 0xE0, 0x14, 0x4A, 0x0B, 0xB4, 0x36, 0xF5, + 0x85, 0xB8, 0xC0, 0x6A, 0xE9, 0x7D, 0xBD, 0x4E, + 0x8F, 0x51, 0x0D, 0x5B, 0x6B, 0x58, 0x5F, 0x03, + 0x6F, 0xBC, 0x5D, 0x1F, 0x7F, 0xDB, 0x00, 0xC1, + 0x13, 0xF0, 0xD1, 0xFA, 0xDA, 0x05, 0x39, 0xD3, + 0x38, 0xD2, 0x89, 0xE2, 0x88, 0x5E, 0x5C, 0x6D, + 0xCA, 0xB0, 0x01, 0x63, 0x8B, 0x59, 0xA4, 0xD0, + 0x78, 0x19, 0xB5, 0x62, 0x1A, 0x69, 0x8D, 0x9C, + 0x22, 0x3F, 0x9E, 0x33, 0x72, 0x2A, 0x41, 0x29, + 0xFE, 0xF6, 0x64, 0x7C, 0x66, 0xB6, 0xAF, 0x23, + 0x8C, 0x68, 0x6E, 0x49, 0x07, 0x99, 0x77, 0x3E, + 0x9A, 0x73, 0xD8, 0x55, 0x0A, 0x3C, 0xBA, 0xA9, + 0x52, 0xED, 0x91, 0x09, 0x95, 0xC7, 0x43, 0xD5, + 0x57, 0x61, 0x81, 0xEF, 0x06, 0xDE, 0x48, 0x31, + 0xBB, 0x2C, 0xE5, 0xC3, 0x67, 0xA1, 0x10, 0xB9 }; + +unsigned char table_161[256] = { + 0x8F, 0x1A, 0x81, 0xA2, 0x2C, 0x56, 0x6D, 0xCD, + 0x4A, 0x33, 0x50, 0xE9, 0xE0, 0x12, 0x5A, 0x43, + 0x2D, 0x4F, 0xEA, 0x95, 0xFD, 0x49, 0xAB, 0xA3, + 0x79, 0x42, 0x0B, 0xB8, 0x89, 0x40, 0x71, 0x14, + 0x80, 0x55, 0xAF, 0xCF, 0x3E, 0x64, 0x8B, 0x74, + 0xBF, 0x9C, 0x24, 0x97, 0xD1, 0xBA, 0x48, 0xD2, + 0x08, 0x1F, 0xDD, 0xA7, 0xDC, 0x92, 0x30, 0x75, + 0x31, 0x37, 0x67, 0x06, 0x68, 0x72, 0x6F, 0x05, + 0x8A, 0x7C, 0x4C, 0x3C, 0x19, 0x28, 0x86, 0x3D, + 0x93, 0xDA, 0xF4, 0xC7, 0x17, 0x85, 0xAC, 0x02, + 0x78, 0x04, 0xAD, 0x03, 0x8D, 0x11, 0xC5, 0x9D, + 0x3A, 0x73, 0x82, 0x59, 0x51, 0x9F, 0x27, 0x47, + 0xE7, 0xED, 0x1E, 0xFF, 0x34, 0x01, 0x5B, 0x4B, + 0xCA, 0x6C, 0x69, 0xBB, 0x3B, 0xC4, 0x5F, 0xDF, + 0x09, 0x6B, 0x7D, 0xC9, 0x88, 0x45, 0x57, 0xD3, + 0x2A, 0x4E, 0xF1, 0xC2, 0xA9, 0xB6, 0x18, 0xD4, + 0xA0, 0x1C, 0x4D, 0x0E, 0xE5, 0xE1, 0xD7, 0xB2, + 0x0C, 0x3F, 0x00, 0x61, 0x16, 0x0D, 0x32, 0x62, + 0x58, 0x63, 0xEE, 0xEF, 0x2F, 0x5D, 0xB0, 0x20, + 0x7A, 0x10, 0xE6, 0xA1, 0xF9, 0xD8, 0x6E, 0xCB, + 0xF0, 0x9B, 0x84, 0x8E, 0xF2, 0xFE, 0xC8, 0x7F, + 0xBD, 0xF8, 0x07, 0xC6, 0x39, 0xBC, 0xCC, 0x22, + 0x54, 0x15, 0x9A, 0xA4, 0xC1, 0x2B, 0x1B, 0x25, + 0xDE, 0x6A, 0xDB, 0x90, 0xEB, 0xB7, 0xD0, 0x44, + 0xA6, 0xB9, 0xB1, 0x23, 0x9E, 0x65, 0x83, 0xFA, + 0x96, 0xB5, 0x0F, 0xF6, 0xD6, 0xE8, 0x53, 0x13, + 0x76, 0xD5, 0x35, 0x87, 0xE3, 0x38, 0xF5, 0xAE, + 0xB3, 0xCE, 0xE2, 0x70, 0xD9, 0x66, 0x5C, 0x26, + 0xC3, 0xFC, 0xF7, 0x94, 0xF3, 0xEC, 0xFB, 0x99, + 0x91, 0x77, 0xB4, 0x46, 0xA5, 0x98, 0x7B, 0x1D, + 0x52, 0x2E, 0xA8, 0x60, 0x5E, 0x29, 0x21, 0x7E, + 0xBE, 0x0A, 0x36, 0x41, 0xC0, 0x8C, 0xE4, 0xAA }; + +unsigned char table_162[256] = { + 0xF7, 0x1B, 0xC0, 0x31, 0x5A, 0x23, 0xEA, 0xE9, + 0xFB, 0x14, 0x6A, 0xE8, 0x04, 0x65, 0x5B, 0x2C, + 0x41, 0xD9, 0xEB, 0xE4, 0x8D, 0x1D, 0xCA, 0x8F, + 0x5E, 0x43, 0xAF, 0x46, 0x0A, 0x01, 0x0C, 0xB4, + 0x95, 0x52, 0x92, 0xE0, 0x10, 0x57, 0x0F, 0x71, + 0xB1, 0x26, 0xD8, 0x05, 0x69, 0x3C, 0x54, 0xDF, + 0xFF, 0x9D, 0x51, 0xA0, 0xA1, 0x0B, 0xC1, 0x20, + 0x6D, 0xFA, 0x47, 0x15, 0x09, 0xD3, 0xE1, 0xA9, + 0x66, 0x12, 0x5C, 0x49, 0x1E, 0x3B, 0xD0, 0x8B, + 0x62, 0xBD, 0x06, 0xE5, 0x00, 0x98, 0x4E, 0x32, + 0xB0, 0x2D, 0x2A, 0x7F, 0x03, 0xD5, 0x99, 0x7E, + 0xAB, 0x22, 0xC6, 0xC3, 0x2F, 0x4C, 0x33, 0x45, + 0xE3, 0x3F, 0xF9, 0xB2, 0xFE, 0x36, 0xE7, 0xF8, + 0x55, 0x0D, 0x56, 0x1F, 0x4B, 0xE6, 0x50, 0x81, + 0xCE, 0x80, 0xCD, 0x67, 0x6B, 0xCF, 0x2E, 0x9B, + 0xBC, 0xBE, 0x11, 0x75, 0x4D, 0xAC, 0x59, 0x40, + 0x85, 0x0E, 0xC9, 0x17, 0xA3, 0x60, 0xED, 0x16, + 0xA4, 0xDD, 0xEE, 0x96, 0x77, 0x83, 0x34, 0xD2, + 0xCB, 0xFC, 0x6C, 0x08, 0xEC, 0x35, 0xF2, 0x6F, + 0x3A, 0x7B, 0x21, 0x4A, 0x70, 0xEF, 0xAD, 0xDE, + 0x90, 0x9E, 0x7D, 0x64, 0x2B, 0x79, 0xF5, 0xF3, + 0x13, 0x1C, 0x7A, 0x07, 0x4F, 0x78, 0x89, 0xB6, + 0x97, 0xF1, 0xD7, 0x7C, 0x48, 0xAE, 0x39, 0xA8, + 0xA6, 0x86, 0x3E, 0x27, 0x87, 0x73, 0x82, 0x24, + 0x30, 0x74, 0x5F, 0xD1, 0x9F, 0x9C, 0x1A, 0x8C, + 0x42, 0x6E, 0x28, 0xB9, 0xF0, 0xC4, 0x68, 0x25, + 0xC5, 0xDC, 0xB8, 0x29, 0xD6, 0x84, 0x3D, 0xBB, + 0x88, 0x76, 0xFD, 0x61, 0x94, 0x91, 0xDA, 0xB7, + 0x72, 0xBA, 0xC2, 0xDB, 0xB5, 0xA5, 0xE2, 0x18, + 0xF6, 0xAA, 0x8A, 0x19, 0x63, 0x9A, 0xA7, 0xC8, + 0xD4, 0x02, 0x8E, 0x37, 0xF4, 0xB3, 0xA2, 0x53, + 0x38, 0xCC, 0x58, 0x44, 0xBF, 0x93, 0x5D, 0xC7 }; + +unsigned char table_163[32] = { + 0x1B, 0x14, 0x12, 0x15, 0x11, 0x1D, 0x17, 0x19, + 0x10, 0x09, 0x08, 0x06, 0x1A, 0x16, 0x07, 0x13, + 0x1F, 0x0B, 0x1C, 0x05, 0x0E, 0x00, 0x18, 0x0A, + 0x04, 0x01, 0x03, 0x0C, 0x0D, 0x1E, 0x02, 0x0F }; + +unsigned char table_164[32] = { + 0x15, 0x00, 0x10, 0x0B, 0x1D, 0x0A, 0x06, 0x1C, + 0x0D, 0x1F, 0x17, 0x0F, 0x03, 0x14, 0x13, 0x12, + 0x1B, 0x18, 0x08, 0x1E, 0x16, 0x09, 0x1A, 0x04, + 0x02, 0x0C, 0x0E, 0x01, 0x07, 0x19, 0x11, 0x05 }; + +unsigned char table_165[256] = { + 0x98, 0xF5, 0x1D, 0xFB, 0x13, 0x20, 0x41, 0xA3, + 0xE3, 0x76, 0x49, 0x7E, 0x60, 0xD8, 0x68, 0x30, + 0x88, 0x45, 0xD5, 0x77, 0x00, 0xC3, 0x09, 0x31, + 0x44, 0x18, 0xD4, 0x14, 0xC8, 0x1B, 0x8B, 0x38, + 0x08, 0x52, 0xD1, 0xF3, 0x69, 0x9F, 0xDA, 0x61, + 0x16, 0x1C, 0xE4, 0x7D, 0xEE, 0xD9, 0x5E, 0x4C, + 0xA7, 0xAA, 0xA6, 0xF6, 0xCF, 0xA0, 0xBA, 0x10, + 0xE2, 0xDE, 0x0F, 0xEA, 0xBC, 0x32, 0x63, 0xC0, + 0x54, 0xC5, 0xBE, 0x71, 0x80, 0x56, 0x5C, 0xA4, + 0xAD, 0x15, 0x9D, 0x11, 0x43, 0x67, 0x95, 0xAE, + 0xC6, 0xC4, 0x91, 0x9C, 0xE5, 0x37, 0xE1, 0x7A, + 0xDB, 0xEF, 0x03, 0x65, 0x86, 0x66, 0x2A, 0xB5, + 0xBF, 0xB4, 0x0D, 0xB3, 0xD7, 0x2D, 0x01, 0xEB, + 0x8C, 0xF2, 0x5A, 0x2E, 0x64, 0x25, 0x02, 0xCB, + 0x4A, 0xB0, 0xCE, 0x35, 0xA8, 0x47, 0x85, 0x33, + 0x34, 0x24, 0x23, 0x7B, 0xB6, 0x48, 0x83, 0x40, + 0x87, 0x57, 0x3C, 0xD6, 0xCD, 0x2C, 0x6D, 0xE7, + 0xBB, 0xED, 0x81, 0x5D, 0x55, 0x46, 0xDD, 0xD3, + 0x70, 0xBD, 0xB8, 0x75, 0x53, 0x6E, 0xD0, 0x99, + 0xCA, 0x58, 0xC7, 0x4B, 0x3D, 0xA5, 0x50, 0x7C, + 0x93, 0x51, 0xB7, 0xFD, 0x05, 0x3A, 0xE8, 0x8F, + 0x28, 0x74, 0x39, 0xF0, 0x7F, 0x4F, 0x06, 0x36, + 0xB2, 0x19, 0x2F, 0x1F, 0x8D, 0x0C, 0xB9, 0xFC, + 0x89, 0x21, 0x12, 0xF7, 0x3F, 0x94, 0x6F, 0xDC, + 0x3E, 0x4E, 0x3B, 0xC9, 0x07, 0x9B, 0x17, 0x9A, + 0x73, 0x6A, 0x5B, 0xA1, 0x1E, 0x8A, 0x04, 0x72, + 0x6C, 0xA2, 0xEC, 0x96, 0xFE, 0xF8, 0x84, 0xC1, + 0x79, 0x0E, 0x62, 0x90, 0x8E, 0xF4, 0x42, 0x29, + 0x92, 0x9E, 0xAC, 0x82, 0x4D, 0xAF, 0x2B, 0x6B, + 0xA9, 0xFF, 0x0A, 0xAB, 0x22, 0x5F, 0xDF, 0xD2, + 0x0B, 0x78, 0xF1, 0xE6, 0x59, 0x27, 0xC2, 0xE0, + 0x1A, 0x26, 0xCC, 0xB1, 0xF9, 0xFA, 0x97, 0xE9 }; + +unsigned char table_166[256] = { + 0xCB, 0xEA, 0x2A, 0x36, 0x6D, 0x93, 0x4E, 0xD5, + 0xBC, 0x6A, 0xD4, 0x68, 0xF7, 0x18, 0xAB, 0x8B, + 0x66, 0x95, 0x94, 0x64, 0xB7, 0x00, 0x4D, 0x97, + 0x38, 0xB3, 0xFC, 0xE1, 0xBB, 0x63, 0xF3, 0x1F, + 0x6B, 0x2C, 0x2F, 0x5E, 0xA4, 0x7E, 0xFB, 0xF4, + 0xA8, 0x8A, 0x65, 0x53, 0x90, 0x58, 0x40, 0x60, + 0x28, 0x8E, 0x35, 0x49, 0xED, 0xBD, 0x1B, 0x0B, + 0xBA, 0xB8, 0x61, 0x50, 0xE9, 0x39, 0xEF, 0xC3, + 0x74, 0xB6, 0x46, 0x8D, 0xD9, 0x32, 0x92, 0x9A, + 0x30, 0x01, 0xF2, 0x41, 0xB9, 0xE7, 0x3A, 0xB0, + 0x80, 0x15, 0xDE, 0x7D, 0x7F, 0x09, 0xC2, 0x76, + 0xF8, 0x12, 0x59, 0xDD, 0x1D, 0xE6, 0x75, 0xBE, + 0xA3, 0x04, 0xCA, 0x78, 0x7B, 0xAC, 0xD8, 0x70, + 0xD3, 0xC1, 0x25, 0x6F, 0x03, 0x6C, 0x14, 0x45, + 0xE5, 0x2B, 0x87, 0x83, 0xAA, 0x77, 0x5F, 0x4A, + 0x9C, 0x27, 0x0C, 0x10, 0xAE, 0x56, 0x85, 0x0D, + 0xE3, 0xFA, 0x71, 0xEE, 0x9F, 0x21, 0xC0, 0xCD, + 0xFD, 0xDC, 0x5B, 0x11, 0x02, 0x0F, 0x96, 0x3D, + 0x3C, 0x26, 0xEB, 0x08, 0x7A, 0x82, 0xA7, 0x19, + 0xD7, 0xC5, 0xF6, 0x52, 0x57, 0x88, 0xFF, 0x47, + 0x8F, 0xC6, 0x33, 0xB5, 0x2E, 0x8C, 0x81, 0x91, + 0x44, 0xA6, 0x17, 0xF0, 0x4B, 0x9D, 0x34, 0x73, + 0x72, 0x67, 0xD2, 0x0E, 0xA0, 0x99, 0xA5, 0xAF, + 0xFE, 0x9E, 0x6E, 0xDA, 0x3B, 0xE2, 0x23, 0xD6, + 0xD0, 0x13, 0x89, 0x5A, 0x42, 0x98, 0x5C, 0xD1, + 0x86, 0x24, 0xDF, 0x37, 0xF9, 0xCC, 0xF5, 0xA9, + 0x2D, 0xBF, 0x5D, 0xF1, 0x69, 0xE8, 0xA2, 0x06, + 0x48, 0xC7, 0xDB, 0x29, 0xE4, 0xAD, 0x3E, 0xA1, + 0xC9, 0x4C, 0x1A, 0xCE, 0x62, 0x4F, 0x7C, 0xC8, + 0x05, 0xC4, 0xB1, 0x1E, 0x79, 0x55, 0x84, 0xB2, + 0x20, 0x31, 0x9B, 0xEC, 0xB4, 0xCF, 0x54, 0x22, + 0x1C, 0xE0, 0x51, 0x16, 0x43, 0x07, 0x0A, 0x3F }; + +unsigned char table_167[256] = { + 0x91, 0xEA, 0x4F, 0x6A, 0x6E, 0x2D, 0x27, 0x22, + 0x44, 0xA5, 0x6D, 0xE3, 0x45, 0x06, 0xE2, 0x87, + 0x9A, 0xC9, 0x2C, 0x4A, 0x93, 0x6F, 0x00, 0xEB, + 0x7C, 0x7F, 0xA2, 0xFE, 0x40, 0x3C, 0x3F, 0xC0, + 0xC7, 0xFB, 0x8B, 0xDF, 0xA3, 0x28, 0x78, 0x48, + 0x46, 0xD5, 0x70, 0x5C, 0x35, 0x4E, 0xD7, 0x3A, + 0x42, 0x47, 0x5B, 0x26, 0x8E, 0xE0, 0x21, 0xB1, + 0x77, 0x1E, 0x53, 0x4B, 0xCC, 0xE5, 0x65, 0xF6, + 0x66, 0x2A, 0xA0, 0x5E, 0x3E, 0xAD, 0xA8, 0x95, + 0x1B, 0x0D, 0x8A, 0x05, 0x68, 0x59, 0x0C, 0x38, + 0x18, 0xC3, 0x81, 0xA4, 0xFD, 0x13, 0x50, 0xCA, + 0xE8, 0xDD, 0xD9, 0x76, 0x8C, 0xC5, 0xF4, 0x17, + 0xB4, 0x3D, 0xEC, 0x0B, 0x67, 0xC6, 0x8D, 0xE1, + 0xBB, 0x7E, 0xCB, 0x10, 0x99, 0xE9, 0x39, 0xF3, + 0x75, 0xFA, 0xAC, 0x16, 0x54, 0x51, 0xBC, 0x24, + 0x58, 0x08, 0xA7, 0x0F, 0x5D, 0xBF, 0xBA, 0xE7, + 0x9D, 0x2B, 0xB5, 0x29, 0xE4, 0xCD, 0x37, 0x30, + 0x55, 0xAE, 0x1D, 0x4D, 0x94, 0x34, 0x92, 0x1C, + 0x6B, 0xBE, 0x52, 0x7B, 0x33, 0xB0, 0x0A, 0x5A, + 0x03, 0x23, 0x41, 0x49, 0x61, 0x64, 0x73, 0x97, + 0xC2, 0x9F, 0x5F, 0x07, 0x04, 0xF8, 0xC1, 0xFC, + 0x74, 0x02, 0x0E, 0x60, 0x9E, 0xD4, 0x85, 0x88, + 0xC4, 0xF5, 0x90, 0x31, 0xF7, 0xEE, 0x9B, 0xB9, + 0x20, 0xE6, 0xA6, 0x63, 0x79, 0x56, 0x62, 0xF0, + 0x2F, 0xD8, 0x4C, 0x83, 0xF9, 0x36, 0x3B, 0x84, + 0xDE, 0x57, 0xB8, 0xB7, 0x11, 0xF2, 0xC8, 0xD3, + 0xD1, 0x96, 0x19, 0x2E, 0x72, 0x9C, 0xDB, 0xB3, + 0xA1, 0xAA, 0xCE, 0x09, 0x98, 0xED, 0xA9, 0xDA, + 0xAF, 0x86, 0xD0, 0x12, 0xFF, 0xDC, 0x1F, 0xD6, + 0x01, 0xF1, 0xD2, 0x80, 0x43, 0x7A, 0x71, 0x82, + 0xB6, 0xAB, 0x89, 0xBD, 0x8F, 0xEF, 0x7D, 0xB2, + 0x14, 0x15, 0x25, 0x32, 0x6C, 0x69, 0x1A, 0xCF }; + +unsigned char table_168[256] = { + 0x28, 0xEE, 0xB1, 0xFD, 0xB3, 0xEF, 0x36, 0x8E, + 0x85, 0x5D, 0x1C, 0x53, 0x1E, 0xDA, 0xBA, 0x3C, + 0xA8, 0x90, 0x99, 0x49, 0x45, 0xE0, 0x27, 0x8D, + 0x22, 0xE4, 0x51, 0x3E, 0xAB, 0xE8, 0x70, 0xF5, + 0x81, 0xE6, 0x34, 0x29, 0xF3, 0x11, 0x46, 0x5F, + 0x5C, 0xA0, 0xD1, 0xE3, 0x15, 0x68, 0x3A, 0x01, + 0xE9, 0xD7, 0x24, 0x5A, 0x18, 0x16, 0x88, 0x3B, + 0x64, 0xA1, 0xDB, 0xBF, 0xAA, 0x43, 0xEA, 0x19, + 0xA2, 0xD5, 0x7B, 0xBD, 0x2A, 0x0E, 0x4F, 0xB5, + 0x4B, 0xB7, 0x5B, 0x73, 0xC9, 0xAC, 0x1B, 0x67, + 0xC7, 0xB4, 0x69, 0x00, 0xBC, 0x6D, 0xC1, 0x04, + 0xF4, 0x74, 0xD6, 0xD0, 0x60, 0xAE, 0x17, 0xFE, + 0x63, 0xB6, 0x89, 0x41, 0x7C, 0x44, 0x8B, 0xDC, + 0x50, 0xE5, 0x79, 0x77, 0x47, 0x9F, 0xA6, 0x3D, + 0x09, 0x8A, 0x2F, 0xC0, 0x0F, 0xCD, 0x2B, 0x4D, + 0x0D, 0xC2, 0x5E, 0xB0, 0x57, 0x62, 0xAF, 0x1A, + 0x21, 0x82, 0x48, 0x9E, 0x38, 0xB9, 0xB8, 0xF2, + 0x37, 0x07, 0xCA, 0xC5, 0x84, 0xDF, 0xF9, 0xEC, + 0x42, 0x6B, 0x8F, 0x6C, 0x3F, 0xC4, 0x94, 0xED, + 0x7A, 0x2D, 0xA3, 0x83, 0xD9, 0x55, 0x02, 0x9A, + 0xA9, 0x75, 0x10, 0x2C, 0xCB, 0x95, 0xBB, 0x6E, + 0x23, 0x65, 0x35, 0x97, 0x56, 0xAD, 0xCE, 0xF8, + 0xF0, 0x0C, 0xE2, 0x52, 0x05, 0x91, 0xCC, 0xC8, + 0x78, 0x06, 0x96, 0x4E, 0x03, 0xD3, 0x98, 0xA7, + 0x13, 0x58, 0x93, 0xD4, 0xDD, 0xC6, 0xFC, 0x25, + 0x9C, 0x86, 0x1F, 0xCF, 0x76, 0xA4, 0x6A, 0xFA, + 0x0B, 0x4A, 0x54, 0x40, 0x59, 0xD8, 0x61, 0xFF, + 0x7F, 0x80, 0x6F, 0x7D, 0xF1, 0x8C, 0x92, 0xDE, + 0x9D, 0xC3, 0xB2, 0xE7, 0xFB, 0x20, 0x31, 0x72, + 0x12, 0xBE, 0x1D, 0xF6, 0x9B, 0x14, 0x26, 0x0A, + 0xEB, 0xF7, 0x71, 0x39, 0x30, 0xA5, 0x87, 0xD2, + 0x66, 0x2E, 0x08, 0x32, 0x4C, 0x33, 0x7E, 0xE1 }; + +unsigned char table_169[256] = { + 0xA4, 0x31, 0xA9, 0x3F, 0x13, 0x4D, 0x1B, 0x29, + 0x73, 0x43, 0xF1, 0xE7, 0x9C, 0xC2, 0xF6, 0xCD, + 0xA1, 0x94, 0x0D, 0x27, 0xFE, 0x7B, 0x9B, 0x0B, + 0x89, 0xBA, 0x23, 0xEC, 0x76, 0xC3, 0x6C, 0xD8, + 0x8D, 0xF8, 0xF9, 0x7D, 0x68, 0x5B, 0x61, 0x87, + 0x28, 0x14, 0x55, 0x0C, 0xFC, 0xD9, 0x07, 0xE8, + 0x36, 0x88, 0x67, 0x4C, 0xEA, 0xBD, 0xF5, 0x9D, + 0xB6, 0xC6, 0x24, 0x32, 0x93, 0x03, 0x79, 0x8C, + 0x12, 0x84, 0xFF, 0x7E, 0x42, 0xE4, 0x3C, 0xF2, + 0x50, 0xEB, 0x1F, 0x47, 0xB0, 0xA5, 0xB1, 0x71, + 0x30, 0x5F, 0x5C, 0x53, 0xF7, 0x10, 0xC5, 0x6E, + 0xE0, 0xDE, 0xC8, 0x58, 0xB7, 0x90, 0xA6, 0x95, + 0x70, 0x8F, 0xFD, 0xC1, 0x48, 0xB5, 0x19, 0x92, + 0xBC, 0x15, 0x4E, 0xE6, 0x11, 0xDD, 0x81, 0x0E, + 0xBB, 0x75, 0x5D, 0x4A, 0xAB, 0x2D, 0x02, 0x54, + 0x4B, 0x66, 0xD6, 0x2B, 0x2A, 0xE5, 0x26, 0xE1, + 0xEE, 0xE9, 0x8B, 0x6A, 0x7A, 0xF4, 0x51, 0x39, + 0x1C, 0xC9, 0xCF, 0x77, 0x00, 0xF3, 0x25, 0xCC, + 0x08, 0xFB, 0x0F, 0x3E, 0xCE, 0xED, 0x3D, 0x56, + 0xEF, 0x1D, 0x85, 0x96, 0x52, 0xA8, 0xD3, 0xCB, + 0xE3, 0x33, 0x06, 0x7C, 0xAE, 0x72, 0x09, 0x04, + 0x91, 0xC4, 0x5A, 0x69, 0x98, 0xB4, 0x40, 0xDF, + 0x7F, 0x9F, 0xAA, 0x83, 0xE2, 0x78, 0x74, 0x20, + 0xAD, 0x6D, 0xDC, 0xD4, 0xCA, 0x60, 0xF0, 0x35, + 0x37, 0xD0, 0x18, 0x1A, 0x64, 0x3A, 0x99, 0xDB, + 0x62, 0x44, 0x2C, 0x82, 0x8E, 0xD7, 0xD1, 0xFA, + 0x16, 0xD5, 0x46, 0xBF, 0xA7, 0xC0, 0x2E, 0x3B, + 0x01, 0x63, 0xB2, 0x1E, 0x05, 0x21, 0xB8, 0x17, + 0x22, 0x97, 0xAF, 0x4F, 0x86, 0x34, 0xDA, 0xC7, + 0xA3, 0xA0, 0xB3, 0x2F, 0xAC, 0x49, 0xD2, 0x57, + 0x6F, 0x9A, 0x65, 0xB9, 0x41, 0xBE, 0x8A, 0xA2, + 0x6B, 0x0A, 0x59, 0x9E, 0x5E, 0x38, 0x45, 0x80 }; + +unsigned char table_170[256] = { + 0xE3, 0x00, 0x99, 0x03, 0xF6, 0xDD, 0xD1, 0x41, + 0x58, 0x7E, 0xD9, 0x46, 0x04, 0xAF, 0x5C, 0x43, + 0xDE, 0x5E, 0xFC, 0x97, 0x3D, 0x68, 0xC8, 0x37, + 0x3C, 0xFB, 0x0F, 0x5A, 0xBE, 0xFA, 0x4C, 0x82, + 0x0C, 0xA0, 0x0A, 0xD4, 0x9D, 0xCE, 0x78, 0xA8, + 0x55, 0x56, 0x60, 0xAA, 0xC9, 0x96, 0x62, 0xEA, + 0x0D, 0xB8, 0xE2, 0x84, 0x17, 0xAE, 0x2B, 0x2C, + 0x91, 0x57, 0x38, 0x01, 0xA9, 0xCD, 0x34, 0xBA, + 0x8D, 0xC0, 0xD6, 0xFF, 0xF2, 0xD3, 0x5F, 0x26, + 0xCA, 0x9B, 0x21, 0x75, 0x4E, 0x49, 0x20, 0x59, + 0x39, 0xBF, 0x90, 0x6C, 0xFE, 0x8F, 0x2F, 0x18, + 0x36, 0xD7, 0xB4, 0xAC, 0xBD, 0xF3, 0x1D, 0x4F, + 0xA3, 0x74, 0x5B, 0x44, 0x05, 0x9C, 0x6D, 0x6B, + 0x1E, 0xE8, 0x25, 0x16, 0x80, 0xCC, 0x29, 0xC7, + 0x94, 0x4A, 0xF5, 0xF4, 0x27, 0x85, 0xBB, 0x24, + 0xDA, 0xB5, 0x76, 0x69, 0xA5, 0x54, 0x23, 0x31, + 0x11, 0xA4, 0x09, 0xE4, 0x64, 0x10, 0xC5, 0xC1, + 0x7D, 0xE7, 0x92, 0xF8, 0x9E, 0x6A, 0x15, 0x8B, + 0x98, 0x42, 0x52, 0x66, 0x0B, 0xA1, 0x35, 0x1A, + 0x14, 0x7C, 0xE1, 0x9F, 0x28, 0xF1, 0x1B, 0xA6, + 0x71, 0x73, 0x81, 0xAB, 0xE6, 0x95, 0x06, 0x1F, + 0xC6, 0xB0, 0x51, 0x0E, 0xEE, 0x77, 0xF0, 0xD8, + 0xC2, 0x89, 0x7B, 0x07, 0xA2, 0xB7, 0x19, 0x67, + 0x2E, 0x8E, 0x47, 0xA7, 0xEF, 0x32, 0xD2, 0x93, + 0xDC, 0x9A, 0xB2, 0xED, 0x45, 0xC4, 0x50, 0x3F, + 0xE5, 0xCF, 0x88, 0x1C, 0x7A, 0x79, 0xEB, 0x70, + 0x2A, 0x7F, 0xBC, 0xDB, 0xD0, 0xB1, 0xCB, 0x08, + 0x86, 0x5D, 0x53, 0x72, 0xB6, 0x4B, 0xB3, 0x22, + 0xC3, 0x6F, 0xB9, 0xD5, 0x3B, 0x13, 0x2D, 0xAD, + 0x33, 0xFD, 0x02, 0x40, 0x8A, 0x3A, 0xF7, 0xE0, + 0x8C, 0x3E, 0x61, 0x6E, 0xE9, 0x63, 0xF9, 0xEC, + 0x48, 0x30, 0x87, 0x83, 0x12, 0x4D, 0x65, 0xDF }; + +unsigned char table_171[32] = { + 0x07, 0x06, 0x11, 0x08, 0x0C, 0x1F, 0x19, 0x02, + 0x14, 0x04, 0x0D, 0x18, 0x1A, 0x05, 0x17, 0x13, + 0x1C, 0x1B, 0x15, 0x03, 0x01, 0x0F, 0x16, 0x1E, + 0x1D, 0x10, 0x00, 0x12, 0x0B, 0x0E, 0x09, 0x0A }; + +unsigned char table_172[32] = { + 0x11, 0x01, 0x1F, 0x06, 0x1A, 0x04, 0x02, 0x09, + 0x05, 0x0D, 0x0B, 0x18, 0x0E, 0x12, 0x1B, 0x17, + 0x07, 0x08, 0x1D, 0x1E, 0x14, 0x19, 0x16, 0x15, + 0x03, 0x0C, 0x00, 0x10, 0x0A, 0x1C, 0x0F, 0x13 }; + +unsigned char table_173[32] = { + 0x1F, 0x0B, 0x13, 0x00, 0x16, 0x15, 0x14, 0x0A, + 0x1D, 0x05, 0x1E, 0x1A, 0x0F, 0x04, 0x0E, 0x01, + 0x19, 0x07, 0x02, 0x12, 0x0C, 0x17, 0x08, 0x09, + 0x03, 0x11, 0x18, 0x10, 0x1C, 0x1B, 0x06, 0x0D }; + +unsigned char table_174[32] = { + 0x02, 0x1B, 0x0C, 0x17, 0x1F, 0x05, 0x15, 0x1E, + 0x16, 0x09, 0x1A, 0x12, 0x0F, 0x1C, 0x18, 0x0A, + 0x19, 0x10, 0x0D, 0x13, 0x04, 0x11, 0x08, 0x14, + 0x1D, 0x0E, 0x06, 0x00, 0x01, 0x07, 0x0B, 0x03 }; + +unsigned char table_175[32] = { + 0x00, 0x06, 0x0B, 0x08, 0x0C, 0x04, 0x1A, 0x1C, + 0x05, 0x1E, 0x14, 0x03, 0x0A, 0x18, 0x12, 0x1D, + 0x16, 0x1F, 0x07, 0x09, 0x0F, 0x0E, 0x17, 0x13, + 0x11, 0x19, 0x10, 0x0D, 0x1B, 0x02, 0x01, 0x15 }; + +unsigned char table_176[32] = { + 0x12, 0x03, 0x1A, 0x15, 0x04, 0x19, 0x0B, 0x1B, + 0x17, 0x1E, 0x0D, 0x05, 0x11, 0x14, 0x1C, 0x00, + 0x18, 0x10, 0x0A, 0x06, 0x0E, 0x08, 0x02, 0x07, + 0x13, 0x09, 0x16, 0x1D, 0x0F, 0x0C, 0x01, 0x1F }; + +unsigned char table_177[256] = { + 0x5E, 0x4D, 0x76, 0xFE, 0xB5, 0x50, 0x83, 0x23, + 0x72, 0xDD, 0x93, 0x08, 0x69, 0xAD, 0xEC, 0x3B, + 0x0B, 0x9A, 0x36, 0xC9, 0xCA, 0xBE, 0xF7, 0x30, + 0x19, 0x39, 0x2C, 0xAB, 0xE3, 0x7B, 0xBC, 0x32, + 0xA0, 0xE4, 0xA6, 0xB6, 0xCB, 0xC8, 0x37, 0x07, + 0xD2, 0xA1, 0xD9, 0xF6, 0xBF, 0xF5, 0x88, 0x01, + 0x95, 0x0F, 0x03, 0xFD, 0xE6, 0x68, 0x90, 0x61, + 0x21, 0x6D, 0x3C, 0x62, 0x34, 0x2B, 0x71, 0x4B, + 0x44, 0x64, 0x75, 0xA2, 0x6A, 0xFF, 0x29, 0xBD, + 0x35, 0x15, 0xF9, 0xC1, 0x09, 0x45, 0xB2, 0xF2, + 0x3F, 0xCE, 0xB0, 0xC0, 0xB8, 0x00, 0x05, 0xD7, + 0x11, 0xC6, 0x78, 0x53, 0x9E, 0xB3, 0xED, 0x56, + 0x22, 0x5C, 0x9D, 0x6C, 0x99, 0x43, 0x2F, 0xAE, + 0xEB, 0x40, 0x8C, 0x1F, 0xC2, 0xDF, 0x92, 0x65, + 0x6F, 0x79, 0x5D, 0x5B, 0xAA, 0xDB, 0xF1, 0x96, + 0xD4, 0xF4, 0x8B, 0x51, 0xD5, 0xE2, 0xBB, 0x80, + 0x17, 0x7C, 0x2A, 0x6E, 0xDE, 0xEA, 0x94, 0x31, + 0xA4, 0x2D, 0xC3, 0x8D, 0x55, 0x14, 0x9B, 0x0E, + 0x7D, 0xC4, 0x06, 0x33, 0x73, 0xE9, 0x7A, 0x38, + 0x5F, 0x89, 0x84, 0xD6, 0xA8, 0x13, 0xE8, 0xCF, + 0x46, 0xD0, 0x7F, 0x24, 0x8F, 0xF8, 0x87, 0x1B, + 0x47, 0x02, 0x0C, 0x97, 0x52, 0xFB, 0x8E, 0x20, + 0x70, 0x3E, 0x7E, 0xD1, 0xE5, 0xEE, 0xCC, 0x91, + 0x74, 0xCD, 0x42, 0x04, 0x8A, 0xEF, 0xE1, 0x10, + 0x4F, 0x1C, 0x28, 0x9F, 0xD8, 0x0A, 0x18, 0x49, + 0x9C, 0x16, 0xF3, 0x82, 0x57, 0x1D, 0x26, 0x66, + 0x27, 0x86, 0xE7, 0x59, 0xFA, 0x25, 0x54, 0x0D, + 0x98, 0xDC, 0xF0, 0x3D, 0x63, 0x1E, 0x77, 0x3A, + 0xDA, 0xB7, 0x6B, 0x2E, 0x48, 0x4C, 0xBA, 0xC7, + 0x60, 0xAC, 0x1A, 0xB9, 0xFC, 0xA3, 0xA7, 0xA5, + 0xB4, 0x67, 0xA9, 0x81, 0xB1, 0x12, 0xD3, 0x85, + 0x5A, 0xC5, 0xE0, 0x58, 0x41, 0x4E, 0x4A, 0xAF }; + +unsigned char table_178[256] = { + 0x33, 0xBA, 0x98, 0xDA, 0x07, 0x2C, 0x22, 0x9B, + 0xE0, 0xED, 0xB7, 0xA1, 0x93, 0xEB, 0xDC, 0x49, + 0xDF, 0xE1, 0x6C, 0xC2, 0x64, 0x52, 0xD0, 0x8F, + 0xA2, 0x48, 0x26, 0x21, 0x6E, 0x5E, 0x0B, 0x7C, + 0x0D, 0x90, 0xA4, 0xCE, 0xF5, 0x5F, 0xF9, 0x1D, + 0x55, 0x83, 0x8D, 0xFB, 0x38, 0xB3, 0xF2, 0x67, + 0xDE, 0x0A, 0xBE, 0xEC, 0x5B, 0x35, 0x08, 0x50, + 0xE7, 0x56, 0x4A, 0x02, 0xBC, 0x5A, 0xBD, 0x43, + 0x6F, 0x79, 0xB2, 0xF7, 0x60, 0xE9, 0xA0, 0x1B, + 0xC8, 0xDD, 0x9D, 0xA3, 0x5C, 0x61, 0x77, 0x72, + 0x9C, 0x31, 0x0E, 0x05, 0x1E, 0x12, 0xF1, 0xC9, + 0x78, 0x4E, 0x15, 0x7D, 0x54, 0xCB, 0x73, 0xEA, + 0xC5, 0x2B, 0x0F, 0x7E, 0x42, 0x96, 0xC6, 0x74, + 0x09, 0x65, 0x34, 0xE6, 0x63, 0xA6, 0x70, 0xD3, + 0x27, 0x87, 0x3A, 0x16, 0x7B, 0x13, 0x06, 0x40, + 0x46, 0x69, 0xAD, 0x88, 0x81, 0xC0, 0x37, 0x58, + 0xD1, 0x8A, 0x8E, 0x9A, 0x5D, 0x6D, 0xC7, 0xC3, + 0xD2, 0xF4, 0x3F, 0x57, 0x3C, 0x4F, 0xA9, 0x6A, + 0x92, 0xA5, 0x97, 0x0C, 0x2A, 0x36, 0x47, 0xDB, + 0x8C, 0xEE, 0x03, 0x89, 0x7F, 0x91, 0x24, 0x80, + 0x2F, 0x62, 0xE4, 0xAF, 0x17, 0x99, 0xD6, 0xCD, + 0xFE, 0x76, 0x1C, 0xD4, 0x3E, 0xFF, 0xD8, 0xC4, + 0x39, 0x32, 0xCF, 0xE2, 0xE3, 0x53, 0xD7, 0xCC, + 0xD9, 0x11, 0xAA, 0x1F, 0x01, 0x3B, 0x51, 0xB5, + 0x94, 0x4B, 0x28, 0xF0, 0xAC, 0x44, 0x14, 0x4C, + 0xB9, 0xA7, 0xB8, 0x1A, 0xD5, 0xCA, 0xE8, 0x82, + 0x9F, 0x2D, 0xAB, 0x2E, 0x29, 0xFD, 0x68, 0xB1, + 0x66, 0xC1, 0x7A, 0xFA, 0x71, 0x04, 0xA8, 0xB0, + 0x59, 0x18, 0xAE, 0x25, 0x3D, 0xE5, 0xF6, 0x41, + 0x86, 0x75, 0x6B, 0xBB, 0xFC, 0x84, 0x8B, 0x85, + 0x10, 0x23, 0xB6, 0xF3, 0x19, 0x30, 0x20, 0x4D, + 0x95, 0x9E, 0xBF, 0xEF, 0xF8, 0x45, 0x00, 0xB4 }; + +unsigned char table_179[256] = { + 0x50, 0x3D, 0x41, 0x42, 0x06, 0x5B, 0xD6, 0x34, + 0x9D, 0x3C, 0x7B, 0x14, 0xE2, 0x9B, 0x80, 0x15, + 0x51, 0x01, 0x6A, 0x30, 0xD7, 0xFC, 0x61, 0x4B, + 0x8A, 0xEC, 0x38, 0x71, 0x70, 0x2E, 0x1C, 0x72, + 0x79, 0x26, 0x4C, 0x48, 0xED, 0xAD, 0x25, 0x53, + 0x03, 0xD9, 0xB5, 0x0D, 0x8E, 0x19, 0xCC, 0xBE, + 0xE1, 0x91, 0x64, 0xA6, 0x21, 0xCE, 0x76, 0xAB, + 0x9F, 0xD1, 0xB6, 0x23, 0x6D, 0xB0, 0x90, 0xBD, + 0x09, 0x3A, 0x5E, 0xD0, 0x73, 0x10, 0x44, 0x08, + 0xFF, 0xB8, 0x24, 0x58, 0xDB, 0x65, 0x95, 0xAA, + 0xE9, 0xC4, 0x32, 0x2B, 0x84, 0xC9, 0xC7, 0xB1, + 0x4F, 0x0C, 0xCB, 0x11, 0x4E, 0x22, 0x4A, 0x16, + 0xDE, 0xBC, 0xEE, 0x68, 0x13, 0xFA, 0xC3, 0x98, + 0xEB, 0x29, 0x43, 0x9A, 0xA1, 0xE0, 0xF0, 0x3F, + 0x2F, 0x1B, 0xC2, 0x66, 0x35, 0xF5, 0xC8, 0xD8, + 0x5A, 0xE5, 0x87, 0x47, 0xD3, 0x7A, 0xE6, 0x39, + 0x77, 0x81, 0xF2, 0x0E, 0x83, 0x7E, 0x17, 0x6C, + 0xB3, 0x5C, 0xE8, 0xD2, 0xC0, 0xA4, 0xF9, 0x86, + 0xCD, 0xFB, 0x54, 0x7C, 0xBF, 0x2D, 0x82, 0xDA, + 0x96, 0x74, 0x97, 0xC5, 0x7D, 0x27, 0x57, 0x56, + 0xDC, 0xBA, 0x69, 0x8C, 0x9C, 0x88, 0xB4, 0x8D, + 0x37, 0xEA, 0x3B, 0x33, 0x2C, 0xB2, 0x45, 0xF7, + 0xC1, 0x1E, 0x46, 0x02, 0x6B, 0x3E, 0xA7, 0xD5, + 0x05, 0x0A, 0xA9, 0x1D, 0xA3, 0x4D, 0xAE, 0x6F, + 0x49, 0xDD, 0x8F, 0xEF, 0xBB, 0x67, 0x0B, 0x40, + 0x9E, 0xF1, 0x78, 0x28, 0xDF, 0x52, 0xF4, 0x92, + 0x94, 0x0F, 0xB9, 0x93, 0xF6, 0x1F, 0xAF, 0xA8, + 0xCA, 0xE4, 0x59, 0x7F, 0x85, 0x75, 0xC6, 0xFD, + 0x00, 0xB7, 0x55, 0xFE, 0x8B, 0x62, 0x5F, 0x12, + 0xF8, 0xD4, 0x89, 0xA0, 0x20, 0xE7, 0xCF, 0x60, + 0x5D, 0xAC, 0x1A, 0x36, 0x63, 0x99, 0x31, 0xF3, + 0x2A, 0x04, 0x18, 0xA5, 0xA2, 0x6E, 0x07, 0xE3 }; + +unsigned char table_180[256] = { + 0xDA, 0xCC, 0x72, 0xA6, 0xE7, 0x07, 0xFD, 0x25, + 0x92, 0x39, 0x49, 0x02, 0xD6, 0x09, 0xA8, 0x65, + 0x2E, 0x6C, 0xA1, 0x19, 0xBF, 0x21, 0x11, 0xC7, + 0x3F, 0x9F, 0xF4, 0x51, 0xAF, 0x8C, 0xFE, 0xCD, + 0x7A, 0xEB, 0x5A, 0xF7, 0x18, 0x69, 0xB9, 0xED, + 0x37, 0x45, 0x13, 0xB4, 0xAA, 0x75, 0x47, 0x42, + 0xA3, 0x81, 0x88, 0x70, 0xC1, 0x36, 0x73, 0x1D, + 0x3B, 0x22, 0xB6, 0x35, 0xE9, 0x31, 0x56, 0x23, + 0xE1, 0xF5, 0xAD, 0x46, 0x99, 0x32, 0xE4, 0x40, + 0x00, 0x0F, 0x05, 0xC6, 0x33, 0x84, 0x7B, 0x4D, + 0x4B, 0x7D, 0x91, 0x3D, 0xCE, 0x64, 0x77, 0x55, + 0xD7, 0x2B, 0x2F, 0x2C, 0xB8, 0xD3, 0x85, 0xD1, + 0xB5, 0x6A, 0xF9, 0x41, 0x08, 0xBB, 0x87, 0xEC, + 0x78, 0xE0, 0xEE, 0x8D, 0x01, 0x58, 0x15, 0x8F, + 0x06, 0xF0, 0x8B, 0x27, 0x0D, 0x0B, 0x6D, 0xBD, + 0xCA, 0x2A, 0xA2, 0xE6, 0xDD, 0xBC, 0x4E, 0x5D, + 0x74, 0x04, 0x3A, 0x96, 0x66, 0x12, 0x1E, 0xF2, + 0xF6, 0xC4, 0xAE, 0x3C, 0x0C, 0x90, 0x68, 0xD8, + 0x24, 0x5E, 0x79, 0x10, 0xAC, 0xDF, 0x9B, 0xC5, + 0x44, 0xC3, 0x50, 0x5C, 0xA5, 0x89, 0x60, 0x5F, + 0x48, 0x17, 0x34, 0xA7, 0xE2, 0xF3, 0xD9, 0x3E, + 0x9C, 0xB7, 0x7C, 0x1F, 0xA9, 0xD4, 0xA4, 0x0E, + 0x8E, 0x4C, 0xDC, 0xF8, 0xF1, 0x98, 0xDE, 0x2D, + 0x61, 0xCB, 0xD5, 0x43, 0x86, 0x26, 0xB0, 0x7F, + 0x7E, 0xFF, 0xAB, 0x83, 0x14, 0x9A, 0x80, 0x16, + 0x30, 0xA0, 0x53, 0x97, 0x52, 0x9E, 0xB1, 0x1B, + 0xD0, 0x1A, 0xC8, 0x57, 0xBA, 0x6E, 0xFA, 0x94, + 0xE8, 0x63, 0x5B, 0x29, 0xEF, 0x71, 0x8A, 0x03, + 0xB3, 0x76, 0xC9, 0xD2, 0xBE, 0xE5, 0x82, 0x1C, + 0x95, 0x9D, 0x4A, 0x28, 0xEA, 0x0A, 0xC0, 0xE3, + 0x6F, 0x20, 0x54, 0xFB, 0x93, 0xFC, 0x6B, 0x38, + 0x62, 0x4F, 0xCF, 0xB2, 0xC2, 0x59, 0xDB, 0x67 }; + +unsigned char table_181[256] = { + 0x2B, 0xED, 0x14, 0x05, 0x80, 0xCC, 0x5A, 0xF8, + 0x43, 0xB7, 0x86, 0xC6, 0xEE, 0xA6, 0xD7, 0xD6, + 0xA0, 0xC4, 0x21, 0x34, 0xB1, 0x8C, 0xF9, 0xF4, + 0x7C, 0x53, 0x06, 0xD4, 0x6B, 0x3F, 0xE1, 0x12, + 0x6A, 0xCE, 0xCF, 0xBF, 0x74, 0x3E, 0xD5, 0xCB, + 0x97, 0x01, 0xA2, 0x2D, 0xAE, 0xF7, 0x17, 0x29, + 0x47, 0x03, 0x0E, 0xE9, 0x82, 0x46, 0x94, 0xAF, + 0x2A, 0x90, 0xFE, 0x4A, 0x7E, 0x0C, 0x71, 0xB6, + 0xA5, 0xF2, 0x67, 0x41, 0xBA, 0xC2, 0x8A, 0x9D, + 0x36, 0xFF, 0x50, 0x2E, 0xC3, 0x91, 0x9C, 0x37, + 0x66, 0xAD, 0xB2, 0x1F, 0xE4, 0xE3, 0x9F, 0xDD, + 0x87, 0xC0, 0xE6, 0xEF, 0x13, 0x70, 0x5B, 0xDE, + 0x5C, 0x75, 0x7F, 0x4F, 0x44, 0xCA, 0x55, 0x57, + 0xF0, 0x26, 0xA7, 0xC7, 0x10, 0x51, 0x00, 0xB3, + 0x5D, 0x99, 0x81, 0x3B, 0xB9, 0x1C, 0x64, 0x7B, + 0xFB, 0xD9, 0x8D, 0x4E, 0xAC, 0x25, 0xBB, 0x69, + 0xDF, 0x02, 0x9E, 0x2C, 0xAB, 0xF3, 0x65, 0x09, + 0xA3, 0x6C, 0xC1, 0x76, 0x52, 0x30, 0xD8, 0x3A, + 0x40, 0x18, 0x59, 0xD0, 0xE5, 0xB4, 0x5F, 0x33, + 0x68, 0x92, 0x2F, 0xB8, 0x93, 0xD1, 0xEB, 0xA4, + 0xFC, 0x77, 0x19, 0x62, 0xC9, 0x49, 0x84, 0x1A, + 0x9A, 0xE7, 0x31, 0xE8, 0xE2, 0x58, 0xF1, 0x4B, + 0x1E, 0x0B, 0x39, 0xFD, 0x42, 0x7A, 0x89, 0x38, + 0x11, 0x98, 0x63, 0x08, 0xE0, 0xEA, 0xBE, 0xB0, + 0x45, 0x1B, 0x4C, 0x54, 0xC8, 0x27, 0x3D, 0x73, + 0x04, 0x8F, 0x79, 0xBC, 0x6F, 0x0D, 0x0F, 0xA1, + 0x60, 0xDC, 0xC5, 0xFA, 0x8E, 0xDA, 0x15, 0x96, + 0xD3, 0x07, 0xF5, 0x3C, 0x88, 0x72, 0x1D, 0x4D, + 0x8B, 0x61, 0x0A, 0xDB, 0xAA, 0x20, 0x23, 0xEC, + 0x6E, 0x22, 0x48, 0x28, 0xBD, 0xA9, 0x56, 0x5E, + 0x85, 0xA8, 0x95, 0x6D, 0x16, 0x78, 0xB5, 0xF6, + 0x32, 0x24, 0x7D, 0x9B, 0xD2, 0x83, 0x35, 0xCD }; + +unsigned char table_182[256] = { + 0x06, 0x7F, 0x66, 0xB5, 0xBA, 0x1E, 0xFD, 0x51, + 0x81, 0x8D, 0x28, 0xA3, 0x15, 0x37, 0xDC, 0x58, + 0xE6, 0x3D, 0xB4, 0xB9, 0x2E, 0xA0, 0x2F, 0xC4, + 0xCB, 0xB1, 0x25, 0xBF, 0xC1, 0x4E, 0x5A, 0xE4, + 0x0F, 0x10, 0x7C, 0x52, 0xA7, 0x29, 0x76, 0x55, + 0xAA, 0x70, 0x62, 0x54, 0x43, 0x93, 0x3A, 0x7D, + 0x5B, 0x56, 0x33, 0x64, 0x74, 0x2A, 0xD9, 0x9B, + 0x88, 0xC0, 0x3C, 0x63, 0xDE, 0xF4, 0x73, 0xDF, + 0x9E, 0xB2, 0xA8, 0x4F, 0x04, 0x57, 0x47, 0x87, + 0x14, 0xFC, 0x27, 0x53, 0x83, 0xDB, 0xD7, 0x20, + 0x96, 0x31, 0xD0, 0xCF, 0x30, 0x19, 0x69, 0x1A, + 0xAE, 0x3B, 0x11, 0x0C, 0xA6, 0x95, 0x8A, 0xF2, + 0x1B, 0xCC, 0x78, 0xEF, 0xB3, 0x71, 0x84, 0xA2, + 0xF1, 0x7A, 0x92, 0x61, 0xCA, 0x90, 0x94, 0x89, + 0x68, 0xEE, 0x97, 0x38, 0x0D, 0xF9, 0x1F, 0x8E, + 0xE9, 0x26, 0xBD, 0xC9, 0xFF, 0x4C, 0x44, 0x1D, + 0x98, 0xE5, 0x86, 0xF3, 0x18, 0xB6, 0x09, 0xD2, + 0x7E, 0xC5, 0xE7, 0x2B, 0x8C, 0x8B, 0x60, 0x3F, + 0x2C, 0x6A, 0x08, 0x0E, 0x50, 0x32, 0x9F, 0xF0, + 0x9A, 0xC2, 0x39, 0xBE, 0xEA, 0x12, 0x16, 0xBB, + 0x5E, 0x67, 0xE3, 0xB8, 0x79, 0x46, 0xDA, 0x00, + 0xD3, 0xBC, 0xCE, 0x1C, 0x80, 0xFA, 0xAB, 0x65, + 0x4A, 0xF8, 0xAC, 0x72, 0x01, 0xC6, 0x35, 0x85, + 0x3E, 0x5C, 0xA1, 0x05, 0xA5, 0xA9, 0xE1, 0x40, + 0xEB, 0xE8, 0x5F, 0xF5, 0xC3, 0xD1, 0x34, 0xFB, + 0xEC, 0xF7, 0x9C, 0xC7, 0xDD, 0x6C, 0x36, 0x9D, + 0x42, 0x59, 0x99, 0x5D, 0xD8, 0x82, 0x07, 0x24, + 0x6D, 0xAD, 0x13, 0x48, 0x6B, 0x6E, 0x75, 0x4D, + 0xD5, 0x02, 0xED, 0xFE, 0x91, 0xCD, 0x77, 0xB0, + 0xF6, 0xC8, 0x6F, 0x23, 0xAF, 0xB7, 0x2D, 0xD6, + 0xA4, 0xE2, 0x45, 0x8F, 0x21, 0xE0, 0x49, 0x22, + 0x7B, 0x17, 0x0B, 0x0A, 0x41, 0x03, 0xD4, 0x4B }; + +unsigned char table_183[32] = { + 0x1E, 0x1B, 0x11, 0x07, 0x08, 0x06, 0x18, 0x17, + 0x0D, 0x0F, 0x12, 0x03, 0x1D, 0x04, 0x0A, 0x1A, + 0x0C, 0x13, 0x14, 0x1F, 0x0B, 0x19, 0x10, 0x01, + 0x16, 0x05, 0x1C, 0x0E, 0x02, 0x00, 0x09, 0x15 }; + +unsigned char table_184[32] = { + 0x0F, 0x1D, 0x17, 0x16, 0x0D, 0x05, 0x13, 0x1F, + 0x1B, 0x09, 0x1C, 0x1E, 0x15, 0x01, 0x06, 0x08, + 0x0C, 0x10, 0x0B, 0x02, 0x04, 0x0A, 0x07, 0x1A, + 0x18, 0x0E, 0x03, 0x11, 0x12, 0x14, 0x19, 0x00 }; + +unsigned char table_185[256] = { + 0xA5, 0xEE, 0x2E, 0x28, 0xA7, 0xAC, 0xD9, 0xB2, + 0x6E, 0x04, 0xB4, 0x03, 0xE8, 0x92, 0x5F, 0x4D, + 0x73, 0x20, 0x71, 0xE0, 0x43, 0x53, 0x3F, 0xF8, + 0x96, 0xA1, 0x24, 0x97, 0xAD, 0x7B, 0xE5, 0xE6, + 0xF2, 0xCE, 0xE3, 0x76, 0x2F, 0xA2, 0x48, 0x0E, + 0x4B, 0x4A, 0x8B, 0x5A, 0x81, 0x2C, 0xBF, 0xD7, + 0xFB, 0x7D, 0x4C, 0x16, 0xF4, 0x00, 0xF5, 0x40, + 0x64, 0x74, 0xA9, 0x37, 0x86, 0xD3, 0x1B, 0xCD, + 0xF1, 0x1A, 0x90, 0x9F, 0x54, 0x79, 0x29, 0xC3, + 0x77, 0x85, 0x02, 0xB1, 0x70, 0xFE, 0x5B, 0xDA, + 0x6B, 0x01, 0x0C, 0x07, 0xB8, 0x58, 0x47, 0x42, + 0x09, 0xE4, 0x27, 0xDD, 0xF3, 0x1E, 0x10, 0x9E, + 0x49, 0x30, 0x05, 0xBE, 0x59, 0xEB, 0xD2, 0xAA, + 0xC8, 0x9D, 0x8C, 0x5E, 0x14, 0x56, 0x8E, 0xF7, + 0x38, 0x55, 0x87, 0xA3, 0x5D, 0x41, 0x4F, 0x1F, + 0xF6, 0x0F, 0x57, 0x91, 0xAE, 0xBA, 0xB3, 0x95, + 0x9B, 0x69, 0xC1, 0x11, 0xD0, 0x25, 0x7F, 0x3B, + 0x62, 0xCF, 0xC0, 0xA0, 0xFC, 0xB6, 0x12, 0x6C, + 0xF0, 0x13, 0x93, 0xAB, 0xC6, 0x78, 0x6D, 0x88, + 0x22, 0x08, 0x2A, 0xE2, 0xB7, 0x65, 0x31, 0x3A, + 0xA6, 0x7C, 0xF9, 0xDC, 0xE7, 0xA4, 0xC9, 0x63, + 0xA8, 0x0B, 0xED, 0x50, 0x36, 0xD8, 0x3E, 0xB0, + 0x6A, 0x5C, 0x45, 0x4E, 0x23, 0x84, 0x34, 0x9A, + 0xCC, 0x3D, 0xB5, 0xEA, 0xDE, 0x75, 0xD6, 0xFF, + 0x6F, 0xC2, 0xDB, 0x8D, 0x7A, 0x1C, 0xE9, 0x61, + 0x0A, 0x1D, 0x32, 0x52, 0x3C, 0x19, 0xFA, 0xD1, + 0xD4, 0x68, 0xC7, 0x0D, 0x99, 0x83, 0xEF, 0x80, + 0x82, 0xBD, 0xD5, 0x7E, 0x39, 0x72, 0x51, 0xAF, + 0x8A, 0x2D, 0xB9, 0x89, 0xC4, 0x67, 0x35, 0xE1, + 0x44, 0x06, 0xEC, 0xCB, 0x8F, 0x17, 0xDF, 0x94, + 0x60, 0xCA, 0x26, 0xFD, 0x33, 0x46, 0x21, 0xBB, + 0x2B, 0xC5, 0x98, 0x18, 0x66, 0x15, 0x9C, 0xBC }; + +unsigned char table_186[256] = { + 0xB7, 0xFA, 0x03, 0x7C, 0x76, 0x43, 0xA7, 0x15, + 0x4B, 0x4F, 0x04, 0xAA, 0x4E, 0xD2, 0x52, 0xC8, + 0x79, 0x16, 0xF6, 0x61, 0x01, 0x5D, 0xD6, 0x47, + 0xDE, 0xC5, 0x4D, 0x2F, 0xF5, 0x29, 0x21, 0xE6, + 0x97, 0x35, 0xDC, 0x0E, 0x8B, 0xF4, 0x0F, 0xBE, + 0x30, 0x07, 0x1D, 0x46, 0x75, 0xCE, 0x56, 0x42, + 0x28, 0x93, 0x84, 0x20, 0xA5, 0xC2, 0x87, 0x45, + 0x1C, 0x6B, 0x55, 0x06, 0xEB, 0xB0, 0xF9, 0x14, + 0x23, 0xF1, 0xFC, 0xD7, 0x98, 0xD1, 0xA4, 0xED, + 0x5B, 0xB1, 0x12, 0x7A, 0xD5, 0x5F, 0x53, 0x88, + 0x95, 0x71, 0xE7, 0x5C, 0xF8, 0x83, 0xC7, 0x49, + 0xDD, 0xDA, 0x0B, 0xC1, 0x70, 0xEC, 0x67, 0xE2, + 0xEA, 0x72, 0x4C, 0x92, 0xA6, 0xE5, 0x59, 0xA9, + 0x3C, 0xFE, 0x0A, 0x65, 0x6E, 0xF3, 0xA3, 0x22, + 0x24, 0x81, 0xF2, 0xCC, 0xD3, 0xA0, 0xDF, 0xDB, + 0xAB, 0x09, 0x13, 0x96, 0x36, 0x9C, 0xEE, 0xD4, + 0x33, 0x5E, 0x26, 0xAE, 0x48, 0x38, 0xFF, 0x08, + 0x1F, 0x6D, 0x02, 0xEF, 0x7E, 0x57, 0x2A, 0x8A, + 0xBA, 0x90, 0xAF, 0xA8, 0x37, 0x8E, 0x9B, 0xC0, + 0x69, 0x32, 0x86, 0xBD, 0x73, 0x6C, 0xB9, 0x31, + 0x66, 0xBF, 0x1B, 0x44, 0x9E, 0xB2, 0xD0, 0xE0, + 0xF0, 0x2C, 0x3F, 0xE1, 0x91, 0x18, 0x19, 0x50, + 0xCA, 0x8F, 0x54, 0xB5, 0x8D, 0x0C, 0x17, 0x39, + 0x8C, 0x00, 0x7F, 0x41, 0xE3, 0x2E, 0x1A, 0x9D, + 0x27, 0xA1, 0x10, 0x34, 0x1E, 0x3A, 0x60, 0x77, + 0xBB, 0xB6, 0x0D, 0x4A, 0x3E, 0x6A, 0xB4, 0xA2, + 0xB3, 0xFD, 0xCD, 0x80, 0x51, 0xAD, 0xCF, 0xBC, + 0x40, 0x74, 0x6F, 0x68, 0x2B, 0xC3, 0xF7, 0x63, + 0xB8, 0x25, 0xC4, 0x62, 0xE9, 0xFB, 0x58, 0x85, + 0x78, 0xCB, 0x9A, 0x3D, 0xE4, 0xC9, 0x89, 0x2D, + 0x64, 0x82, 0xC6, 0x05, 0xD8, 0xAC, 0x99, 0x9F, + 0x11, 0x3B, 0x94, 0xE8, 0x7D, 0x7B, 0xD9, 0x5A }; + +unsigned char table_187[32] = { + 0x0F, 0x04, 0x1D, 0x1B, 0x15, 0x10, 0x01, 0x0B, + 0x00, 0x17, 0x13, 0x07, 0x1E, 0x1F, 0x08, 0x0A, + 0x19, 0x09, 0x05, 0x06, 0x0C, 0x1A, 0x14, 0x16, + 0x0E, 0x18, 0x03, 0x1C, 0x12, 0x11, 0x0D, 0x02 }; + +struct yahoo_fn yahoo_fntable[5][96] = + {{{ IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }, + { IDENT, 0, 0 }}, + {{ MULADD, 0x36056CD7, 0x4387 }, + { LOOKUP, (long)table_0, 0 }, + { LOOKUP, (long)table_1, 0 }, + { BITFLD, (long)table_2, 0 }, + { LOOKUP, (long)table_3, 0 }, + { BITFLD, (long)table_4, 0 }, + { MULADD, 0x4ABB534D, 0x3769 }, + { XOR, 0x1D242DA5, 0 }, + { MULADD, 0x3C23132D, 0x339B }, + { XOR, 0x0191265C, 0 }, + { XOR, 0x3DB979DB, 0 }, + { LOOKUP, (long)table_5, 0 }, + { XOR, 0x1A550E1E, 0 }, + { XOR, 0x2F140A2D, 0 }, + { MULADD, 0x7C466A4B, 0x29BF }, + { XOR, 0x2D3F30D3, 0 }, + { MULADD, 0x7E823B21, 0x6BB3 }, + { BITFLD, (long)table_6, 0 }, + { LOOKUP, (long)table_7, 0 }, + { BITFLD, (long)table_8, 0 }, + { LOOKUP, (long)table_9, 0 }, + { BITFLD, (long)table_10, 0 }, + { LOOKUP, (long)table_11, 0 }, + { BITFLD, (long)table_12, 0 }, + { LOOKUP, (long)table_13, 0 }, + { BITFLD, (long)table_14, 0 }, + { MULADD, 0x5B756AB9, 0x7E9B }, + { LOOKUP, (long)table_15, 0 }, + { XOR, 0x1D1C4911, 0 }, + { LOOKUP, (long)table_16, 0 }, + { LOOKUP, (long)table_17, 0 }, + { XOR, 0x46BD7771, 0 }, + { XOR, 0x51AE2B42, 0 }, + { MULADD, 0x2417591B, 0x177B }, + { MULADD, 0x57F27C5F, 0x2433 }, + { LOOKUP, (long)table_18, 0 }, + { LOOKUP, (long)table_19, 0 }, + { XOR, 0x71422261, 0 }, + { BITFLD, (long)table_20, 0 }, + { MULADD, 0x58E937F9, 0x1075 }, + { LOOKUP, (long)table_21, 0 }, + { BITFLD, (long)table_22, 0 }, + { LOOKUP, (long)table_23, 0 }, + { LOOKUP, (long)table_24, 0 }, + { MULADD, 0x0B4C3D13, 0x1597 }, + { BITFLD, (long)table_25, 0 }, + { XOR, 0x0FE07D38, 0 }, + { MULADD, 0x689B4017, 0x3CFB }, + { BITFLD, (long)table_26, 0 }, + { LOOKUP, (long)table_27, 0 }, + { XOR, 0x35413DF3, 0 }, + { MULADD, 0x05B611AB, 0x570B }, + { MULADD, 0x0DA5334F, 0x3AC7 }, + { XOR, 0x47706008, 0 }, + { BITFLD, (long)table_28, 0 }, + { LOOKUP, (long)table_29, 0 }, + { BITFLD, (long)table_30, 0 }, + { XOR, 0x57611B36, 0 }, + { MULADD, 0x314C2CD1, 0x2B5B }, + { XOR, 0x1EF33946, 0 }, + { MULADD, 0x28EA041F, 0x638F }, + { LOOKUP, (long)table_31, 0 }, + { LOOKUP, (long)table_32, 0 }, + { LOOKUP, (long)table_33, 0 }, + { MULADD, 0x511537CB, 0x7135 }, + { MULADD, 0x1CF71007, 0x5E17 }, + { XOR, 0x583D4BCF, 0 }, + { LOOKUP, (long)table_34, 0 }, + { XOR, 0x373E6856, 0 }, + { MULADD, 0x4D595519, 0x1A7D }, + { LOOKUP, (long)table_35, 0 }, + { LOOKUP, (long)table_36, 0 }, + { XOR, 0x0E2A36A7, 0 }, + { LOOKUP, (long)table_37, 0 }, + { LOOKUP, (long)table_38, 0 }, + { BITFLD, (long)table_39, 0 }, + { BITFLD, (long)table_40, 0 }, + { XOR, 0x53F3604F, 0 }, + { BITFLD, (long)table_41, 0 }, + { BITFLD, (long)table_42, 0 }, + { MULADD, 0x1EDC0BA3, 0x7531 }, + { LOOKUP, (long)table_43, 0 }, + { XOR, 0x10DF1038, 0 }, + { BITFLD, (long)table_44, 0 }, + { LOOKUP, (long)table_45, 0 }, + { XOR, 0x4EDE0CAC, 0 }, + { MULADD, 0x2F076EEB, 0x5BCF }, + { XOR, 0x6D86030F, 0 }, + { XOR, 0x3F331713, 0 }, + { LOOKUP, (long)table_46, 0 }, + { MULADD, 0x41CD726F, 0x3F79 }, + { BITFLD, (long)table_47, 0 }, + { XOR, 0x0ECE0054, 0 }, + { MULADD, 0x19B32B03, 0x4AD1 }, + { BITFLD, (long)table_48, 0 }, + { BITFLD, (long)table_49, 0 }}, + {{ MULADD, 0x39731111, 0x419B }, + { XOR, 0x54F7757A, 0 }, + { BITFLD, (long)table_50, 0 }, + { BITFLD, (long)table_51, 0 }, + { LOOKUP, (long)table_52, 0 }, + { LOOKUP, (long)table_53, 0 }, + { MULADD, 0x3CC0256B, 0x7CE7 }, + { XOR, 0x79991847, 0 }, + { MULADD, 0x228F7FB5, 0x472D }, + { MULADD, 0x32DA290B, 0x7745 }, + { XOR, 0x7A28180D, 0 }, + { BITFLD, (long)table_54, 0 }, + { BITFLD, (long)table_55, 0 }, + { MULADD, 0x5C814F8B, 0x227F }, + { LOOKUP, (long)table_56, 0 }, + { MULADD, 0x0B496F6D, 0x412D }, + { XOR, 0x6F4B62DA, 0 }, + { LOOKUP, (long)table_57, 0 }, + { XOR, 0x64973977, 0 }, + { LOOKUP, (long)table_58, 0 }, + { LOOKUP, (long)table_59, 0 }, + { BITFLD, (long)table_60, 0 }, + { LOOKUP, (long)table_61, 0 }, + { LOOKUP, (long)table_62, 0 }, + { XOR, 0x6DD14C92, 0 }, + { LOOKUP, (long)table_63, 0 }, + { BITFLD, (long)table_64, 0 }, + { BITFLD, (long)table_65, 0 }, + { BITFLD, (long)table_66, 0 }, + { LOOKUP, (long)table_67, 0 }, + { XOR, 0x5E6324D8, 0 }, + { LOOKUP, (long)table_68, 0 }, + { LOOKUP, (long)table_69, 0 }, + { LOOKUP, (long)table_70, 0 }, + { BITFLD, (long)table_71, 0 }, + { XOR, 0x62745ED0, 0 }, + { MULADD, 0x102C215B, 0x0581 }, + { LOOKUP, (long)table_72, 0 }, + { LOOKUP, (long)table_73, 0 }, + { LOOKUP, (long)table_74, 0 }, + { MULADD, 0x19511111, 0x12C1 }, + { LOOKUP, (long)table_75, 0 }, + { MULADD, 0x2A6E2953, 0x6977 }, + { LOOKUP, (long)table_76, 0 }, + { XOR, 0x55CD5445, 0 }, + { BITFLD, (long)table_77, 0 }, + { BITFLD, (long)table_78, 0 }, + { MULADD, 0x646C21EB, 0x43E5 }, + { XOR, 0x71DC4898, 0 }, + { XOR, 0x167519CB, 0 }, + { XOR, 0x6D3158F8, 0 }, + { XOR, 0x7EA95BEA, 0 }, + { BITFLD, (long)table_79, 0 }, + { XOR, 0x47377587, 0 }, + { XOR, 0x2D8B6E8F, 0 }, + { MULADD, 0x5E6105DB, 0x1605 }, + { XOR, 0x65B543C8, 0 }, + { LOOKUP, (long)table_80, 0 }, + { BITFLD, (long)table_81, 0 }, + { MULADD, 0x48AF73CB, 0x0A67 }, + { XOR, 0x4FB96154, 0 }, + { LOOKUP, (long)table_82, 0 }, + { BITFLD, (long)table_83, 0 }, + { XOR, 0x622C4954, 0 }, + { BITFLD, (long)table_84, 0 }, + { XOR, 0x20D220F3, 0 }, + { XOR, 0x361D4F0D, 0 }, + { XOR, 0x2B2000D1, 0 }, + { XOR, 0x6FB8593E, 0 }, + { LOOKUP, (long)table_85, 0 }, + { BITFLD, (long)table_86, 0 }, + { XOR, 0x2B7F7DFC, 0 }, + { MULADD, 0x5FC41A57, 0x0693 }, + { MULADD, 0x17154387, 0x2489 }, + { BITFLD, (long)table_87, 0 }, + { BITFLD, (long)table_88, 0 }, + { BITFLD, (long)table_89, 0 }, + { LOOKUP, (long)table_90, 0 }, + { XOR, 0x7E221470, 0 }, + { XOR, 0x7A600061, 0 }, + { BITFLD, (long)table_91, 0 }, + { BITFLD, (long)table_92, 0 }, + { LOOKUP, (long)table_93, 0 }, + { BITFLD, (long)table_94, 0 }, + { MULADD, 0x00E813A5, 0x2CE5 }, + { MULADD, 0x3D707E25, 0x3827 }, + { MULADD, 0x77A53E07, 0x6A5F }, + { BITFLD, (long)table_95, 0 }, + { LOOKUP, (long)table_96, 0 }, + { LOOKUP, (long)table_97, 0 }, + { XOR, 0x43A73788, 0 }, + { LOOKUP, (long)table_98, 0 }, + { BITFLD, (long)table_99, 0 }, + { LOOKUP, (long)table_100, 0 }, + { XOR, 0x55F4606B, 0 }, + { BITFLD, (long)table_101, 0 }}, + {{ BITFLD, (long)table_102, 0 }, + { MULADD, 0x32CA58E3, 0x04F9 }, + { XOR, 0x11756B30, 0 }, + { MULADD, 0x218B2569, 0x5DB1 }, + { XOR, 0x77D64B90, 0 }, + { BITFLD, (long)table_103, 0 }, + { LOOKUP, (long)table_104, 0 }, + { MULADD, 0x7D1428CB, 0x3D }, + { XOR, 0x6F872C49, 0 }, + { XOR, 0x2E484655, 0 }, + { MULADD, 0x1E3349F7, 0x41F5 }, + { LOOKUP, (long)table_105, 0 }, + { BITFLD, (long)table_106, 0 }, + { XOR, 0x61640311, 0 }, + { BITFLD, (long)table_107, 0 }, + { LOOKUP, (long)table_108, 0 }, + { LOOKUP, (long)table_109, 0 }, + { LOOKUP, (long)table_110, 0 }, + { XOR, 0x007044D3, 0 }, + { BITFLD, (long)table_111, 0 }, + { MULADD, 0x5C221625, 0x576F }, + { LOOKUP, (long)table_112, 0 }, + { LOOKUP, (long)table_113, 0 }, + { XOR, 0x2D406BB1, 0 }, + { MULADD, 0x680B1F17, 0x12CD }, + { BITFLD, (long)table_114, 0 }, + { MULADD, 0x12564D55, 0x32B9 }, + { MULADD, 0x21A67897, 0x6BAB }, + { LOOKUP, (long)table_115, 0 }, + { MULADD, 0x06405119, 0x7143 }, + { XOR, 0x351D01ED, 0 }, + { MULADD, 0x46356F6B, 0x0A49 }, + { MULADD, 0x32C77969, 0x72F3 }, + { BITFLD, (long)table_116, 0 }, + { LOOKUP, (long)table_117, 0 }, + { LOOKUP, (long)table_118, 0 }, + { BITFLD, (long)table_119, 0 }, + { LOOKUP, (long)table_120, 0 }, + { BITFLD, (long)table_121, 0 }, + { MULADD, 0x74D52C55, 0x5F43 }, + { XOR, 0x26201CA8, 0 }, + { XOR, 0x7AEB3255, 0 }, + { LOOKUP, (long)table_122, 0 }, + { MULADD, 0x578F1047, 0x640B }, + { LOOKUP, (long)table_123, 0 }, + { LOOKUP, (long)table_124, 0 }, + { BITFLD, (long)table_125, 0 }, + { BITFLD, (long)table_126, 0 }, + { XOR, 0x4A1352CF, 0 }, + { MULADD, 0x4BFB6EF3, 0x704F }, + { MULADD, 0x1B4C7FE7, 0x5637 }, + { MULADD, 0x04091A3B, 0x4917 }, + { XOR, 0x270C2F52, 0 }, + { LOOKUP, (long)table_127, 0 }, + { BITFLD, (long)table_128, 0 }, + { LOOKUP, (long)table_129, 0 }, + { BITFLD, (long)table_130, 0 }, + { MULADD, 0x127549D5, 0x579B }, + { MULADD, 0x0AB54121, 0x7A47 }, + { BITFLD, (long)table_131, 0 }, + { XOR, 0x751E6E49, 0 }, + { LOOKUP, (long)table_132, 0 }, + { LOOKUP, (long)table_133, 0 }, + { XOR, 0x670C3F74, 0 }, + { MULADD, 0x6B080851, 0x7E8B }, + { XOR, 0x71CD789E, 0 }, + { XOR, 0x3EB20B7B, 0 }, + { BITFLD, (long)table_134, 0 }, + { LOOKUP, (long)table_135, 0 }, + { MULADD, 0x58A67753, 0x272B }, + { MULADD, 0x1AB54AD7, 0x4D33 }, + { MULADD, 0x07D30A45, 0x0569 }, + { MULADD, 0x737616BF, 0x70C7 }, + { LOOKUP, (long)table_136, 0 }, + { MULADD, 0x45C4485D, 0x2063 }, + { BITFLD, (long)table_137, 0 }, + { XOR, 0x2598043D, 0 }, + { MULADD, 0x223A4FE3, 0x49A7 }, + { XOR, 0x1EED619F, 0 }, + { BITFLD, (long)table_138, 0 }, + { XOR, 0x6F477561, 0 }, + { BITFLD, (long)table_139, 0 }, + { BITFLD, (long)table_140, 0 }, + { LOOKUP, (long)table_141, 0 }, + { MULADD, 0x4BC13C4F, 0x45C1 }, + { XOR, 0x3B547BFB, 0 }, + { LOOKUP, (long)table_142, 0 }, + { MULADD, 0x71406AB3, 0x7A5F }, + { XOR, 0x2F1467E9, 0 }, + { MULADD, 0x009366D1, 0x22D1 }, + { MULADD, 0x587D1B75, 0x2CA5 }, + { MULADD, 0x213A4BE7, 0x4499 }, + { MULADD, 0x62653E89, 0x2D5D }, + { BITFLD, (long)table_143, 0 }, + { MULADD, 0x4F5F3257, 0x444F }, + { MULADD, 0x4C0E2B2B, 0x19D3 }}, + {{ MULADD, 0x3F867B35, 0x7B3B }, + { MULADD, 0x32D25CB1, 0x3D6D }, + { BITFLD, (long)table_144, 0 }, + { MULADD, 0x50FA1C51, 0x5F4F }, + { LOOKUP, (long)table_145, 0 }, + { XOR, 0x05FE7AF1, 0 }, + { MULADD, 0x14067C29, 0x10C5 }, + { LOOKUP, (long)table_146, 0 }, + { MULADD, 0x4A5558C5, 0x271F }, + { XOR, 0x3C0861B1, 0 }, + { BITFLD, (long)table_147, 0 }, + { LOOKUP, (long)table_148, 0 }, + { MULADD, 0x18837C9D, 0x6335 }, + { BITFLD, (long)table_149, 0 }, + { XOR, 0x7DAB5033, 0 }, + { LOOKUP, (long)table_150, 0 }, + { MULADD, 0x03B87321, 0x7225 }, + { XOR, 0x7F906745, 0 }, + { LOOKUP, (long)table_151, 0 }, + { BITFLD, (long)table_152, 0 }, + { XOR, 0x21C46C2C, 0 }, + { MULADD, 0x2B36757D, 0x028D }, + { BITFLD, (long)table_153, 0 }, + { LOOKUP, (long)table_154, 0 }, + { XOR, 0x106B4A85, 0 }, + { XOR, 0x17640F11, 0 }, + { LOOKUP, (long)table_155, 0 }, + { XOR, 0x69E60486, 0 }, + { LOOKUP, (long)table_156, 0 }, + { MULADD, 0x3782017D, 0x05BF }, + { BITFLD, (long)table_157, 0 }, + { LOOKUP, (long)table_158, 0 }, + { XOR, 0x6BCA53B0, 0 }, + { LOOKUP, (long)table_159, 0 }, + { LOOKUP, (long)table_160, 0 }, + { LOOKUP, (long)table_161, 0 }, + { LOOKUP, (long)table_162, 0 }, + { XOR, 0x0B8236E3, 0 }, + { BITFLD, (long)table_163, 0 }, + { MULADD, 0x5EE51C43, 0x4553 }, + { BITFLD, (long)table_164, 0 }, + { LOOKUP, (long)table_165, 0 }, + { LOOKUP, (long)table_166, 0 }, + { LOOKUP, (long)table_167, 0 }, + { MULADD, 0x42B14C6F, 0x5531 }, + { XOR, 0x4A2548E8, 0 }, + { MULADD, 0x5C071D85, 0x2437 }, + { LOOKUP, (long)table_168, 0 }, + { MULADD, 0x29195861, 0x108B }, + { XOR, 0x24012258, 0 }, + { LOOKUP, (long)table_169, 0 }, + { XOR, 0x63CC2377, 0 }, + { XOR, 0x08D04B59, 0 }, + { MULADD, 0x3FD30CF5, 0x7027 }, + { XOR, 0x7C3E0478, 0 }, + { MULADD, 0x457776B7, 0x24B3 }, + { XOR, 0x086652BC, 0 }, + { MULADD, 0x302F5B13, 0x371D }, + { LOOKUP, (long)table_170, 0 }, + { MULADD, 0x58692D47, 0x0671 }, + { XOR, 0x6601178E, 0 }, + { MULADD, 0x0F195B9B, 0x1369 }, + { XOR, 0x07BA21D8, 0 }, + { BITFLD, (long)table_171, 0 }, + { BITFLD, (long)table_172, 0 }, + { XOR, 0x13AC3D21, 0 }, + { MULADD, 0x5BCF3275, 0x6E1B }, + { MULADD, 0x62725C5B, 0x16B9 }, + { MULADD, 0x5B950FDF, 0x2D35 }, + { BITFLD, (long)table_173, 0 }, + { BITFLD, (long)table_174, 0 }, + { MULADD, 0x73BA5335, 0x1C13 }, + { BITFLD, (long)table_175, 0 }, + { BITFLD, (long)table_176, 0 }, + { XOR, 0x3E144154, 0 }, + { MULADD, 0x4EED7B27, 0x38AB }, + { LOOKUP, (long)table_177, 0 }, + { MULADD, 0x627C7E0F, 0x7F01 }, + { MULADD, 0x5D7E1F73, 0x2C0F }, + { LOOKUP, (long)table_178, 0 }, + { MULADD, 0x55C9525F, 0x4659 }, + { XOR, 0x3765334C, 0 }, + { MULADD, 0x5DF66DDF, 0x7C25 }, + { LOOKUP, (long)table_179, 0 }, + { LOOKUP, (long)table_180, 0 }, + { XOR, 0x16AE5776, 0 }, + { LOOKUP, (long)table_181, 0 }, + { LOOKUP, (long)table_182, 0 }, + { BITFLD, (long)table_183, 0 }, + { BITFLD, (long)table_184, 0 }, + { LOOKUP, (long)table_185, 0 }, + { MULADD, 0x4392327B, 0x7E0D }, + { LOOKUP, (long)table_186, 0 }, + { MULADD, 0x3D8B0CB5, 0x640D }, + { MULADD, 0x32865601, 0x4D43 }, + { BITFLD, (long)table_187, 0 }}}; + +#define A( x ) (( x ) & 0xFF ) +#define B( x ) (( x ) >> 8 & 0xFF ) +#define C( x ) (( x ) >> 16 & 0xFF ) +#define D( x ) (( x ) >> 24 & 0xFF ) + +int yahoo_xfrm( int table, int depth, int seed ) +{ + struct yahoo_fn *xfrm; + int i, j, z; + unsigned int n = seed; + unsigned char *arg; + + for( i = 0; i < depth; i++ ) + { + xfrm = &yahoo_fntable[table][n % 96]; + switch( xfrm->type ) + { + case IDENT: + return seed; + case XOR: + seed ^= xfrm->arg1; + break; + case MULADD: + seed = seed * xfrm->arg1 + xfrm->arg2; + break; + case LOOKUP: + arg = (unsigned char *)xfrm->arg1; + seed = arg[A( seed )] | arg[B( seed )] << 8 | arg[C( seed )] << 16 + | arg[D( seed )] << 24; + break; + case BITFLD: + arg = (unsigned char *)xfrm->arg1; + for( j = 0, z = 0; j < 32; j++ ) + z = ((( seed >> j ) & 1 ) << arg[j] ) | ( ~( 1 << arg[j] ) & z ); + seed = z; + break; + } + if( depth - i == 1 ) + return seed; + z = (((((( A( seed ) * 0x9E3779B1 ) ^ B( seed )) * 0x9E3779B1 ) + ^ C( seed )) * 0x9E3779B1 ) ^ D( seed )) * 0x9E3779B1; + n = (((( z ^ ( z >> 8 )) >> 16 ) ^ z ) ^ ( z >> 8 )) & 0xFF; + seed *= 0x00010DCD; + } + return seed; +} diff --git a/protocols/yahoo/yahoo_fn.h b/protocols/yahoo/yahoo_fn.h new file mode 100644 index 00000000..3f79f524 --- /dev/null +++ b/protocols/yahoo/yahoo_fn.h @@ -0,0 +1,33 @@ +/* + * libyahoo2 - originally from gaim patches by Amatus + * + * Copyright (C) 2003-2004 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define IDENT 1 /* identify function */ +#define XOR 2 /* xor with arg1 */ +#define MULADD 3 /* multipy by arg1 then add arg2 */ +#define LOOKUP 4 /* lookup each byte in the table pointed to by arg1 */ +#define BITFLD 5 /* reorder bits according to table pointed to by arg1 */ + +struct yahoo_fn +{ + int type; + long arg1, arg2; +}; + +int yahoo_xfrm( int table, int depth, int seed ); diff --git a/protocols/yahoo/yahoo_httplib.c b/protocols/yahoo/yahoo_httplib.c new file mode 100644 index 00000000..41e31a23 --- /dev/null +++ b/protocols/yahoo/yahoo_httplib.c @@ -0,0 +1,437 @@ +/* + * libyahoo2: yahoo_httplib.c + * + * Copyright (C) 2002-2004, Philip S Tellis <philip.tellis AT gmx.net> + * + * This program is free software; you can 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 + * + */ + +#if HAVE_CONFIG_H +# include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> + +#if STDC_HEADERS +# include <string.h> +#else +# if !HAVE_STRCHR +# define strchr index +# define strrchr rindex +# endif +char *strchr (), *strrchr (); +# if !HAVE_MEMCPY +# define memcpy(d, s, n) bcopy ((s), (d), (n)) +# define memmove(d, s, n) bcopy ((s), (d), (n)) +# endif +#endif + + +#include <errno.h> +#ifndef _WIN32 +#include <unistd.h> +#endif +#include <ctype.h> +#include "yahoo2.h" +#include "yahoo2_callbacks.h" +#include "yahoo_httplib.h" +#include "yahoo_util.h" + +#include "yahoo_debug.h" +#ifdef __MINGW32__ +# include <winsock2.h> +# define write(a,b,c) send(a,b,c,0) +# define read(a,b,c) recv(a,b,c,0) +# define snprintf _snprintf +#endif + +#ifdef USE_STRUCT_CALLBACKS +extern struct yahoo_callbacks *yc; +#define YAHOO_CALLBACK(x) yc->x +#else +#define YAHOO_CALLBACK(x) x +#endif + +extern enum yahoo_log_level log_level; + +int yahoo_tcp_readline(char *ptr, int maxlen, int fd) +{ + int n, rc; + char c; + + for (n = 1; n < maxlen; n++) { + + do { + rc = read(fd, &c, 1); + } while(rc == -1 && (errno == EINTR || errno == EAGAIN)); /* this is bad - it should be done asynchronously */ + + if (rc == 1) { + if(c == '\r') /* get rid of \r */ + continue; + *ptr = c; + if (c == '\n') + break; + ptr++; + } else if (rc == 0) { + if (n == 1) + return (0); /* EOF, no data */ + else + break; /* EOF, w/ data */ + } else { + return -1; + } + } + + *ptr = 0; + return (n); +} + +static int url_to_host_port_path(const char *url, + char *host, int *port, char *path) +{ + char *urlcopy=NULL; + char *slash=NULL; + char *colon=NULL; + + /* + * http://hostname + * http://hostname/ + * http://hostname/path + * http://hostname/path:foo + * http://hostname:port + * http://hostname:port/ + * http://hostname:port/path + * http://hostname:port/path:foo + */ + + if(strstr(url, "http://") == url) { + urlcopy = strdup(url+7); + } else { + WARNING(("Weird url - unknown protocol: %s", url)); + return 0; + } + + slash = strchr(urlcopy, '/'); + colon = strchr(urlcopy, ':'); + + if(!colon || (slash && slash < colon)) { + *port = 80; + } else { + *colon = 0; + *port = atoi(colon+1); + } + + if(!slash) { + strcpy(path, "/"); + } else { + strcpy(path, slash); + *slash = 0; + } + + strcpy(host, urlcopy); + + FREE(urlcopy); + + return 1; +} + +static int isurlchar(unsigned char c) +{ + return (isalnum(c) || '-' == c || '_' == c); +} + +char *yahoo_urlencode(const char *instr) +{ + int ipos=0, bpos=0; + char *str = NULL; + int len = strlen(instr); + + if(!(str = y_new(char, 3*len + 1) )) + return ""; + + while(instr[ipos]) { + while(isurlchar(instr[ipos])) + str[bpos++] = instr[ipos++]; + if(!instr[ipos]) + break; + + snprintf(&str[bpos], 4, "%%%.2x", instr[ipos]); + bpos+=3; + ipos++; + } + str[bpos]='\0'; + + /* free extra alloc'ed mem. */ + len = strlen(str); + str = y_renew(char, str, len+1); + + return (str); +} + +char *yahoo_urldecode(const char *instr) +{ + int ipos=0, bpos=0; + char *str = NULL; + char entity[3]={0,0,0}; + unsigned dec; + int len = strlen(instr); + + if(!(str = y_new(char, len+1) )) + return ""; + + while(instr[ipos]) { + while(instr[ipos] && instr[ipos]!='%') + if(instr[ipos]=='+') { + str[bpos++]=' '; + ipos++; + } else + str[bpos++] = instr[ipos++]; + if(!instr[ipos]) + break; + + if(instr[ipos+1] && instr[ipos+2]) { + ipos++; + entity[0]=instr[ipos++]; + entity[1]=instr[ipos++]; + sscanf(entity, "%2x", &dec); + str[bpos++] = (char)dec; + } else { + str[bpos++] = instr[ipos++]; + } + } + str[bpos]='\0'; + + /* free extra alloc'ed mem. */ + len = strlen(str); + str = y_renew(char, str, len+1); + + return (str); +} + +char *yahoo_xmldecode(const char *instr) +{ + int ipos=0, bpos=0, epos=0; + char *str = NULL; + char entity[4]={0,0,0,0}; + char *entitymap[5][2]={ + {"amp;", "&"}, + {"quot;", "\""}, + {"lt;", "<"}, + {"gt;", "<"}, + {"nbsp;", " "} + }; + unsigned dec; + int len = strlen(instr); + + if(!(str = y_new(char, len+1) )) + return ""; + + while(instr[ipos]) { + while(instr[ipos] && instr[ipos]!='&') + if(instr[ipos]=='+') { + str[bpos++]=' '; + ipos++; + } else + str[bpos++] = instr[ipos++]; + if(!instr[ipos] || !instr[ipos+1]) + break; + ipos++; + + if(instr[ipos] == '#') { + ipos++; + epos=0; + while(instr[ipos] != ';') + entity[epos++]=instr[ipos++]; + sscanf(entity, "%u", &dec); + str[bpos++] = (char)dec; + ipos++; + } else { + int i; + for (i=0; i<5; i++) + if(!strncmp(instr+ipos, entitymap[i][0], + strlen(entitymap[i][0]))) { + str[bpos++] = entitymap[i][1][0]; + ipos += strlen(entitymap[i][0]); + break; + } + } + } + str[bpos]='\0'; + + /* free extra alloc'ed mem. */ + len = strlen(str); + str = y_renew(char, str, len+1); + + return (str); +} + +typedef void (*http_connected)(int id, int fd, int error); + +struct callback_data { + int id; + yahoo_get_fd_callback callback; + char *request; + void *user_data; +}; + +static void connect_complete(int fd, int error, void *data) +{ + struct callback_data *ccd = data; + if(error == 0 && fd > 0) + write(fd, ccd->request, strlen(ccd->request)); + FREE(ccd->request); + ccd->callback(ccd->id, fd, error, ccd->user_data); + FREE(ccd); +} + +static void yahoo_send_http_request(int id, char *host, int port, char *request, + yahoo_get_fd_callback callback, void *data) +{ + struct callback_data *ccd=y_new0(struct callback_data, 1); + ccd->callback = callback; + ccd->id = id; + ccd->request = strdup(request); + ccd->user_data = data; + + YAHOO_CALLBACK(ext_yahoo_connect_async)(id, host, port, connect_complete, ccd); +} + +void yahoo_http_post(int id, const char *url, const char *cookies, long content_length, + yahoo_get_fd_callback callback, void *data) +{ + char host[255]; + int port = 80; + char path[255]; + char buff[1024]; + + if(!url_to_host_port_path(url, host, &port, path)) + return; + + snprintf(buff, sizeof(buff), + "POST %s HTTP/1.0\r\n" + "Content-length: %ld\r\n" + "User-Agent: Mozilla/4.5 [en] (" PACKAGE "/" VERSION ")\r\n" + "Host: %s:%d\r\n" + "Cookie: %s\r\n" + "\r\n", + path, content_length, + host, port, + cookies); + + yahoo_send_http_request(id, host, port, buff, callback, data); +} + +void yahoo_http_get(int id, const char *url, const char *cookies, + yahoo_get_fd_callback callback, void *data) +{ + char host[255]; + int port = 80; + char path[255]; + char buff[1024]; + + if(!url_to_host_port_path(url, host, &port, path)) + return; + + snprintf(buff, sizeof(buff), + "GET %s HTTP/1.0\r\n" + "Host: %s:%d\r\n" + "User-Agent: Mozilla/4.5 [en] (" PACKAGE "/" VERSION ")\r\n" + "Cookie: %s\r\n" + "\r\n", + path, host, port, cookies); + + yahoo_send_http_request(id, host, port, buff, callback, data); +} + +struct url_data { + yahoo_get_url_handle_callback callback; + void *user_data; +}; + +static void yahoo_got_url_fd(int id, int fd, int error, void *data) +{ + char *tmp=NULL; + char buff[1024]; + unsigned long filesize=0; + char *filename=NULL; + int n; + + struct url_data *ud = data; + + if(error || fd < 0) { + ud->callback(id, fd, error, filename, filesize, ud->user_data); + FREE(ud); + return; + } + + while((n=yahoo_tcp_readline(buff, sizeof(buff), fd)) > 0) { + LOG(("Read:%s:\n", buff)); + if(!strcmp(buff, "")) + break; + + if( !strncasecmp(buff, "Content-length:", + strlen("Content-length:")) ) { + tmp = strrchr(buff, ' '); + if(tmp) + filesize = atol(tmp); + } + + if( !strncasecmp(buff, "Content-disposition:", + strlen("Content-disposition:")) ) { + tmp = strstr(buff, "name="); + if(tmp) { + tmp+=strlen("name="); + if(tmp[0] == '"') { + char *tmp2; + tmp++; + tmp2 = strchr(tmp, '"'); + if(tmp2) + *tmp2 = '\0'; + } else { + char *tmp2; + tmp2 = strchr(tmp, ';'); + if(!tmp2) + tmp2 = strchr(tmp, '\r'); + if(!tmp2) + tmp2 = strchr(tmp, '\n'); + if(tmp2) + *tmp2 = '\0'; + } + + filename = strdup(tmp); + } + } + } + + LOG(("n == %d\n", n)); + LOG(("Calling callback, filename:%s, size: %ld\n", filename, filesize)); + ud->callback(id, fd, error, filename, filesize, ud->user_data); + FREE(ud); + FREE(filename); +} + +void yahoo_get_url_fd(int id, const char *url, const struct yahoo_data *yd, + yahoo_get_url_handle_callback callback, void *data) +{ + char buff[1024]; + struct url_data *ud = y_new0(struct url_data, 1); + snprintf(buff, sizeof(buff), "Y=%s; T=%s", yd->cookie_y, yd->cookie_t); + ud->callback = callback; + ud->user_data = data; + yahoo_http_get(id, url, buff, yahoo_got_url_fd, ud); +} + diff --git a/protocols/yahoo/yahoo_httplib.h b/protocols/yahoo/yahoo_httplib.h new file mode 100644 index 00000000..fd28ad48 --- /dev/null +++ b/protocols/yahoo/yahoo_httplib.h @@ -0,0 +1,48 @@ +/* + * libyahoo2: yahoo_httplib.h + * + * Copyright (C) 2002-2004, Philip S Tellis <philip.tellis AT gmx.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef YAHOO_HTTPLIB_H +#define YAHOO_HTTPLIB_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "yahoo2_types.h" + +char *yahoo_urlencode(const char *instr); +char *yahoo_urldecode(const char *instr); +char *yahoo_xmldecode(const char *instr); + +int yahoo_tcp_readline(char *ptr, int maxlen, int fd); +void yahoo_http_post(int id, const char *url, const char *cookies, long size, + yahoo_get_fd_callback callback, void *data); +void yahoo_http_get(int id, const char *url, const char *cookies, + yahoo_get_fd_callback callback, void *data); +void yahoo_get_url_fd(int id, const char *url, const struct yahoo_data *yd, + yahoo_get_url_handle_callback callback, void *data); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/protocols/yahoo/yahoo_list.c b/protocols/yahoo/yahoo_list.c new file mode 100644 index 00000000..cda631c6 --- /dev/null +++ b/protocols/yahoo/yahoo_list.c @@ -0,0 +1,236 @@ +/* + * yahoo_list.c: linked list routines + * + * Some code copyright (C) 2002-2004, Philip S Tellis <philip.tellis AT gmx.net> + * Other code copyright Meredydd Luff <meredydd AT everybuddy.com> + * + * This program is free software; you can 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 + * + * Some of this code was borrowed from elist.c in the eb-lite sources + * + */ + +#include <stdlib.h> + +#include "yahoo_list.h" + +YList *y_list_append(YList * list, void *data) +{ + YList *n; + YList *new_list = malloc(sizeof(YList)); + YList *attach_to = NULL; + + new_list->next = NULL; + new_list->data = data; + + for (n = list; n != NULL; n = n->next) { + attach_to = n; + } + + if (attach_to == NULL) { + new_list->prev = NULL; + return new_list; + } else { + new_list->prev = attach_to; + attach_to->next = new_list; + return list; + } +} + +YList *y_list_prepend(YList * list, void *data) +{ + YList *n = malloc(sizeof(YList)); + + n->next = list; + n->prev = NULL; + n->data = data; + if (list) + list->prev = n; + + return n; +} + +YList *y_list_concat(YList * list, YList * add) +{ + YList *l; + + if(!list) + return add; + + if(!add) + return list; + + for (l = list; l->next; l = l->next) + ; + + l->next = add; + add->prev = l; + + return list; +} + +YList *y_list_remove(YList * list, void *data) +{ + YList *n; + + for (n = list; n != NULL; n = n->next) { + if (n->data == data) { + list=y_list_remove_link(list, n); + y_list_free_1(n); + break; + } + } + + return list; +} + +/* Warning */ +/* link MUST be part of list */ +/* caller must free link using y_list_free_1 */ +YList *y_list_remove_link(YList * list, const YList * link) +{ + if (!link) + return list; + + if (link->next) + link->next->prev = link->prev; + if (link->prev) + link->prev->next = link->next; + + if (link == list) + list = link->next; + + return list; +} + +int y_list_length(const YList * list) +{ + int retval = 0; + const YList *n = list; + + for (n = list; n != NULL; n = n->next) { + retval++; + } + + return retval; +} + +/* well, you could just check for list == NULL, but that would be + * implementation dependent + */ +int y_list_empty(const YList * list) +{ + if(!list) + return 1; + else + return 0; +} + +int y_list_singleton(const YList * list) +{ + if(!list || list->next) + return 0; + return 1; +} + +YList *y_list_copy(YList * list) +{ + YList *n; + YList *copy = NULL; + + for (n = list; n != NULL; n = n->next) { + copy = y_list_append(copy, n->data); + } + + return copy; +} + +void y_list_free_1(YList * list) +{ + free(list); +} + +void y_list_free(YList * list) +{ + YList *n = list; + + while (n != NULL) { + YList *next = n->next; + free(n); + n = next; + } +} + +YList *y_list_find(YList * list, const void *data) +{ + YList *l; + for (l = list; l && l->data != data; l = l->next) + ; + + return l; +} + +void y_list_foreach(YList * list, YListFunc fn, void * user_data) +{ + for (; list; list = list->next) + fn(list->data, user_data); +} + +YList *y_list_find_custom(YList * list, const void *data, YListCompFunc comp) +{ + YList *l; + for (l = list; l; l = l->next) + if (comp(l->data, data) == 0) + return l; + + return NULL; +} + +YList *y_list_nth(YList * list, int n) +{ + int i=n; + for ( ; list && i; list = list->next, i--) + ; + + return list; +} + +YList *y_list_insert_sorted(YList * list, void *data, YListCompFunc comp) +{ + YList *l, *n, *prev = NULL; + if (!list) + return y_list_append(list, data); + + n = malloc(sizeof(YList)); + n->data = data; + for (l = list; l && comp(l->data, n->data) <= 0; l = l->next) + prev = l; + + if (l) { + n->prev = l->prev; + l->prev = n; + } else + n->prev = prev; + + n->next = l; + + if(n->prev) { + n->prev->next = n; + return list; + } else { + return n; + } + +} diff --git a/protocols/yahoo/yahoo_list.h b/protocols/yahoo/yahoo_list.h new file mode 100644 index 00000000..a7a69635 --- /dev/null +++ b/protocols/yahoo/yahoo_list.h @@ -0,0 +1,74 @@ +/* + * yahoo_list.h: linked list routines + * + * Some code copyright (C) 2002-2004, Philip S Tellis <philip.tellis AT gmx.net> + * Other code copyright Meredydd Luff <meredydd AT everybuddy.com> + * + * This program is free software; you can 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 + * + */ + +/* + * This is a replacement for the GList. It only provides functions that + * we use in Ayttm. Thanks to Meredyyd from everybuddy dev for doing + * most of it. + */ + +#ifndef __YLIST_H__ +#define __YLIST_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _YList { + struct _YList *next; + struct _YList *prev; + void *data; +} YList; + +typedef int (*YListCompFunc) (const void *, const void *); +typedef void (*YListFunc) (void *, void *); + +YList *y_list_append(YList * list, void *data); +YList *y_list_prepend(YList * list, void *data); +YList *y_list_remove_link(YList * list, const YList * link); +YList *y_list_remove(YList * list, void *data); + +YList *y_list_insert_sorted(YList * list, void * data, YListCompFunc comp); + +YList *y_list_copy(YList * list); + +YList *y_list_concat(YList * list, YList * add); + +YList *y_list_find(YList * list, const void *data); +YList *y_list_find_custom(YList * list, const void *data, YListCompFunc comp); + +YList *y_list_nth(YList * list, int n); + +void y_list_foreach(YList * list, YListFunc fn, void *user_data); + +void y_list_free_1(YList * list); +void y_list_free(YList * list); +int y_list_length(const YList * list); +int y_list_empty(const YList * list); +int y_list_singleton(const YList * list); + +#define y_list_next(list) list->next + +#ifdef __cplusplus +} +#endif +#endif diff --git a/protocols/yahoo/yahoo_util.c b/protocols/yahoo/yahoo_util.c new file mode 100644 index 00000000..cb155e3c --- /dev/null +++ b/protocols/yahoo/yahoo_util.c @@ -0,0 +1,159 @@ +/* + * libyahoo2: yahoo_util.c + * + * Copyright (C) 2002-2004, Philip S Tellis <philip.tellis AT gmx.net> + * + * This program is free software; you can 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 + * + */ + +#if HAVE_CONFIG_H +# include <config.h> +#endif + +#if STDC_HEADERS +# include <string.h> +#else +# if !HAVE_STRCHR +# define strchr index +# define strrchr rindex +# endif +char *strchr (), *strrchr (); +# if !HAVE_MEMCPY +# define memcpy(d, s, n) bcopy ((s), (d), (n)) +# define memmove(d, s, n) bcopy ((s), (d), (n)) +# endif +#endif + +#include "yahoo_util.h" + +char * y_string_append(char * string, char * append) +{ + int size = strlen(string) + strlen(append) + 1; + char * new_string = y_renew(char, string, size); + + if(new_string == NULL) { + new_string = y_new(char, size); + strcpy(new_string, string); + FREE(string); + } + + strcat(new_string, append); + + return new_string; +} + +char * y_str_to_utf8(const char *in) +{ + unsigned int n, i = 0; + char *result = NULL; + + if(in == NULL || *in == '\0') + return ""; + + result = y_new(char, strlen(in) * 2 + 1); + + /* convert a string to UTF-8 Format */ + for (n = 0; n < strlen(in); n++) { + unsigned char c = (unsigned char)in[n]; + + if (c < 128) { + result[i++] = (char) c; + } else { + result[i++] = (char) ((c >> 6) | 192); + result[i++] = (char) ((c & 63) | 128); + } + } + result[i] = '\0'; + return result; +} + +char * y_utf8_to_str(const char *in) +{ + int i = 0; + unsigned int n; + char *result = NULL; + + if(in == NULL || *in == '\0') + return ""; + + result = y_new(char, strlen(in) + 1); + + /* convert a string from UTF-8 Format */ + for (n = 0; n < strlen(in); n++) { + unsigned char c = in[n]; + + if (c < 128) { + result[i++] = (char) c; + } else { + result[i++] = (c << 6) | (in[++n] & 63); + } + } + result[i] = '\0'; + return result; +} + +#if !HAVE_GLIB + +void y_strfreev(char ** vector) +{ + char **v; + for(v = vector; *v; v++) { + FREE(*v); + } + FREE(vector); +} + +char ** y_strsplit(char * str, char * sep, int nelem) +{ + char ** vector; + char *s, *p; + int i=0; + int l = strlen(sep); + if(nelem < 0) { + char * s; + nelem=0; + for(s=strstr(str, sep); s; s=strstr(s+l, sep),nelem++) + ; + if(strcmp(str+strlen(str)-l, sep)) + nelem++; + } + + vector = y_new(char *, nelem + 1); + + for(p=str, s=strstr(p,sep); i<nelem && s; p=s+l, s=strstr(p,sep), i++) { + int len = s-p; + vector[i] = y_new(char, len+1); + strncpy(vector[i], p, len); + vector[i][len] = '\0'; + } + + if(i<nelem) /* str didn't end with sep */ + vector[i++] = strdup(p); + + vector[i] = NULL; + + return vector; +} + +void * y_memdup(const void * addr, int n) +{ + void * new_chunk = malloc(n); + if(new_chunk) + memcpy(new_chunk, addr, n); + return new_chunk; +} + +#endif diff --git a/protocols/yahoo/yahoo_util.h b/protocols/yahoo/yahoo_util.h new file mode 100644 index 00000000..0046fe16 --- /dev/null +++ b/protocols/yahoo/yahoo_util.h @@ -0,0 +1,102 @@ +/* + * libyahoo2: yahoo_util.h + * + * Copyright (C) 2002-2004, Philip S Tellis <philip.tellis AT gmx.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __YAHOO_UTIL_H__ +#define __YAHOO_UTIL_H__ + +#if HAVE_CONFIG_H +# include <config.h> +#endif + +#if HAVE_GLIB +# include <glib.h> + +# define FREE(x) if(x) {g_free(x); x=NULL;} + +# define y_new g_new +# define y_new0 g_new0 +# define y_renew g_renew + +# define y_memdup g_memdup +# define y_strsplit g_strsplit +# define y_strfreev g_strfreev +# ifndef strdup +# define strdup g_strdup +# endif +# ifndef strncasecmp +# define strncasecmp g_strncasecmp +# define strcasecmp g_strcasecmp +# endif + +# define snprintf g_snprintf +# define vsnprintf g_vsnprintf + +#else + +# include <stdlib.h> +# include <stdarg.h> + +# define FREE(x) if(x) {free(x); x=NULL;} + +# define y_new(type, n) (type *)malloc(sizeof(type) * (n)) +# define y_new0(type, n) (type *)calloc((n), sizeof(type)) +# define y_renew(type, mem, n) (type *)realloc(mem, n) + +void * y_memdup(const void * addr, int n); +char ** y_strsplit(char * str, char * sep, int nelem); +void y_strfreev(char ** vector); + +int strncasecmp(const char * s1, const char * s2, size_t n); +int strcasecmp(const char * s1, const char * s2); + +char * strdup(const char *s); + +int snprintf(char *str, size_t size, const char *format, ...); +int vsnprintf(char *str, size_t size, const char *format, va_list ap); + +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef MIN +#define MIN(x,y) ((x)<(y)?(x):(y)) +#endif + +#ifndef MAX +#define MAX(x,y) ((x)>(y)?(x):(y)) +#endif + +/* + * The following three functions return newly allocated memory. + * You must free it yourself + */ +char * y_string_append(char * str, char * append); +char * y_str_to_utf8(const char * in); +char * y_utf8_to_str(const char * in); + +#endif + diff --git a/query.c b/query.c new file mode 100644 index 00000000..0eda3584 --- /dev/null +++ b/query.c @@ -0,0 +1,168 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Questions to the user (mainly authorization requests from IM) */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#define BITLBEE_CORE +#include "bitlbee.h" + +static void query_display( irc_t *irc, query_t *q ); +static query_t *query_default( irc_t *irc ); + +query_t *query_add( irc_t *irc, struct gaim_connection *gc, char *question, void *yes, void *no, void *data ) +{ + query_t *q = g_new0( query_t, 1 ); + + q->gc = gc; + q->question = g_strdup( question ); + q->yes = yes; + q->no = no; + q->data = data; + + if( irc->queries ) + { + query_t *l = irc->queries; + + while( l->next ) l = l->next; + l->next = q; + } + else + { + irc->queries = q; + } + + if( g_strcasecmp( set_getstr( irc, "query_order" ), "lifo" ) == 0 || irc->queries == q ) + query_display( irc, q ); + + return( q ); +} + +void query_del( irc_t *irc, query_t *q ) +{ + query_t *l; + + if( irc->queries == q ) + { + irc->queries = q->next; + } + else + { + for( l = irc->queries; l; l = l->next ) + { + if( l->next == q ) + { + l->next = q->next; + break; + } + } + + if( !l ) + return; /* Hrmmm... */ + } + + g_free( q->question ); + if( q->data ) g_free( q->data ); /* Memory leak... */ + g_free( q ); +} + +void query_del_by_gc( irc_t *irc, struct gaim_connection *gc ) +{ + query_t *q, *n, *def; + int count = 0; + + q = irc->queries; + def = query_default( irc ); + + while( q ) + { + if( q->gc == gc ) + { + n = q->next; + query_del( irc, q ); + q = n; + + count ++; + } + else + { + q = q->next; + } + } + + if( count > 0 ) + serv_got_crap( gc, "Flushed %d unanswered question(s) for this connection.", count ); + + q = query_default( irc ); + if( q && q != def ) + query_display( irc, q ); +} + +void query_answer( irc_t *irc, query_t *q, int ans ) +{ + int disp = 0; + + if( !q ) + { + q = query_default( irc ); + disp = 1; + } + + if( ans ) + { + q->yes( NULL, q->data ); + irc_usermsg( irc, "Accepted: %s", q->question ); + } + else + { + q->no( NULL, q->data ); + irc_usermsg( irc, "Rejected: %s", q->question ); + } + q->data = NULL; + + query_del( irc, q ); + + if( disp && ( q = query_default( irc ) ) ) + query_display( irc, q ); +} + +static void query_display( irc_t *irc, query_t *q ) +{ + if( q->gc ) + irc_usermsg( irc, "Question on %s connection (handle %s):", proto_name[q->gc->protocol], q->gc->username ); + else + irc_usermsg( irc, "Question:" ); + + irc_usermsg( irc, "%s\nYou can use the yes/no commands to answer this question.", q->question ); +} + +static query_t *query_default( irc_t *irc ) +{ + query_t *q; + + if( g_strcasecmp( set_getstr( irc, "query_order" ), "fifo" ) == 0 ) + q = irc->queries; + else + for( q = irc->queries; q && q->next; q = q->next ); + + return( q ); +} diff --git a/query.h b/query.h new file mode 100644 index 00000000..2f29a739 --- /dev/null +++ b/query.h @@ -0,0 +1,44 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Questions to the user (mainly authorization requests from IM) */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef _QUERY_H +#define _QUERY_H + +typedef struct query +{ + struct gaim_connection *gc; + char *question; + void (* yes) ( gpointer w, void *data ); + void (* no) ( gpointer w, void *data ); + void *data; + struct query *next; +} query_t; + +query_t *query_add( irc_t *irc, struct gaim_connection *gc, char *question, void *yes, void *no, void *data ); +void query_del( irc_t *irc, query_t *q ); +void query_del_by_gc( irc_t *irc, struct gaim_connection *gc ); +void query_answer( irc_t *irc, query_t *q, int ans ); + +#endif @@ -0,0 +1,221 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Some stuff to register, handle and save user preferences */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ +#define BITLBEE_CORE +#include "bitlbee.h" + +set_t *set_add( irc_t *irc, char *key, char *def, void *eval ) +{ + set_t *s = set_find( irc, key ); + + if( !s ) + { + if( ( s = irc->set ) ) + { + while( s->next ) s = s->next; + s->next = g_new ( set_t, 1 ); + s = s->next; + } + else + { + s = irc->set = g_new( set_t, 1 ); + } + memset( s, 0, sizeof( set_t ) ); + s->key = g_strdup( key ); + } + + if( s->def ) + { + g_free( s->def ); + s->def = NULL; + } + if( def ) s->def = g_strdup( def ); + + if( s->eval ) + { + g_free( s->eval ); + s->eval = NULL; + } + if( eval ) s->eval = eval; + + return( s ); +} + +set_t *set_find( irc_t *irc, char *key ) +{ + set_t *s = irc->set; + + while( s ) + { + if( g_strcasecmp( s->key, key ) == 0 ) + break; + s = s->next; + } + + return( s ); +} + +char *set_getstr( irc_t *irc, char *key ) +{ + set_t *s = set_find( irc, key ); + + if( !s || ( !s->value && !s->def ) ) + return( NULL ); + + return( s->value?s->value:s->def ); +} + +int set_getint( irc_t *irc, char *key ) +{ + char *s = set_getstr( irc, key ); + int i = 0; + + if( !s ) + return( 0 ); + + if( ( g_strcasecmp( s, "true" ) == 0 ) || ( g_strcasecmp( s, "yes" ) == 0 ) || ( g_strcasecmp( s, "on" ) == 0 ) ) + return( 1 ); + + if( sscanf( s, "%d", &i ) != 1 ) + return( 0 ); + + return( i ); +} + +int set_setstr( irc_t *irc, char *key, char *value ) +{ + set_t *s = set_find( irc, key ); + char *nv = value; + + if( !s ) + s = set_add( irc, key, NULL, NULL ); + + if( s->eval && !( nv = s->eval( irc, s, value ) ) ) + return( 0 ); + + if( s->value ) + { + g_free( s->value ); + s->value = NULL; + } + + if( !s->def || ( strcmp( nv, s->def ) != 0 ) ) + s->value = g_strdup( nv ); + + if( nv != value ) + g_free( nv ); + + return( 1 ); +} + +int set_setint( irc_t *irc, char *key, int value ) +{ + char s[24]; /* Not quite 128-bit clean eh? ;-) */ + + sprintf( s, "%d", value ); + return( set_setstr( irc, key, s ) ); +} + +void set_del( irc_t *irc, char *key ) +{ + set_t *s = irc->set, *t = NULL; + + while( s ) + { + if( g_strcasecmp( s->key, key ) == 0 ) + break; + s = (t=s)->next; + } + if( s ) + { + t->next = s->next; + g_free( s->key ); + if( s->value ) g_free( s->value ); + if( s->def ) g_free( s->def ); + g_free( s ); + } +} + +char *set_eval_int( irc_t *irc, set_t *set, char *value ) +{ + char *s = value; + + for( ; *s; s ++ ) + if( *s < '0' || *s > '9' ) + return( NULL ); + + return( value ); +} + +char *set_eval_bool( irc_t *irc, set_t *set, char *value ) +{ + if( ( g_strcasecmp( value, "true" ) == 0 ) || ( g_strcasecmp( value, "yes" ) == 0 ) || ( g_strcasecmp( value, "on" ) == 0 ) ) + return( value ); + if( ( g_strcasecmp( value, "false" ) == 0 ) || ( g_strcasecmp( value, "no" ) == 0 ) || ( g_strcasecmp( value, "off" ) == 0 ) ) + return( value ); + return( set_eval_int( irc, set, value ) ); +} + +char *set_eval_to_char( irc_t *irc, set_t *set, char *value ) +{ + char *s = g_new( char, 3 ); + + if( *value == ' ' ) + strcpy( s, " " ); + else + sprintf( s, "%c ", *value ); + + return( s ); +} + +char *set_eval_ops( irc_t *irc, set_t *set, char *value ) +{ + if( g_strcasecmp( value, "user" ) == 0 ) + { + irc_write( irc, ":%s!%s@%s MODE %s %s %s %s", irc->mynick, irc->mynick, irc->myhost, + irc->channel, "+o-o", irc->nick, irc->mynick ); + return( value ); + } + else if( g_strcasecmp( value, "root" ) == 0 ) + { + irc_write( irc, ":%s!%s@%s MODE %s %s %s %s", irc->mynick, irc->mynick, irc->myhost, + irc->channel, "-o+o", irc->nick, irc->mynick ); + return( value ); + } + else if( g_strcasecmp( value, "both" ) == 0 ) + { + irc_write( irc, ":%s!%s@%s MODE %s %s %s %s", irc->mynick, irc->mynick, irc->myhost, + irc->channel, "+oo", irc->nick, irc->mynick ); + return( value ); + } + else if( g_strcasecmp( value, "none" ) == 0 ) + { + irc_write( irc, ":%s!%s@%s MODE %s %s %s %s", irc->mynick, irc->mynick, irc->myhost, + irc->channel, "-oo", irc->nick, irc->mynick ); + return( value ); + } + + return( NULL ); +} + @@ -0,0 +1,52 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Some stuff to register, handle and save user preferences */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +typedef struct set +{ + char *key; + char *value; + char *def; /* Default */ + + /* Eval: Returns NULL if the value is incorrect. Can return a + corrected value. set_setstr() should be able to free() the + returned string! */ + char *(*eval) ( irc_t *irc, struct set *set, char *value ); + struct set *next; +} set_t; + +set_t *set_add( irc_t *irc, char *key, char *def, void *eval ); +G_MODULE_EXPORT set_t *set_find( irc_t *irc, char *key ); +G_MODULE_EXPORT char *set_getstr( irc_t *irc, char *key ); +G_MODULE_EXPORT int set_getint( irc_t *irc, char *key ); +int set_setstr( irc_t *irc, char *key, char *value ); +int set_setint( irc_t *irc, char *key, int value ); +void set_del( irc_t *irc, char *key ); + +char *set_eval_int( irc_t *irc, set_t *set, char *value ); +char *set_eval_bool( irc_t *irc, set_t *set, char *value ); +char *set_eval_to_char( irc_t *irc, set_t *set, char *value ); +char *set_eval_ops( irc_t *irc, set_t *set, char *value ); + + @@ -0,0 +1,27 @@ +#ifndef _WIN32 +#include <unistd.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#define sock_make_nonblocking(fd) fcntl(fd, F_SETFL, O_NONBLOCK) +#define sockerr_again() (errno == EINPROGRESS || errno == EINTR) +#define closesocket(a) close(a) +#else +# include <winsock2.h> +# ifndef _MSC_VER +# include <ws2tcpip.h> +# endif +# if !defined(BITLBEE_CORE) && defined(_MSC_VER) +# pragma comment(lib,"bitlbee.lib") +# endif +# include <io.h> +# define read(a,b,c) recv(a,b,c,0) +# define write(a,b,c) send(a,b,c,0) +# define umask _umask +# define mode_t int +# define sock_make_nonblocking(fd) { int non_block = 1; ioctlsocket(fd, FIONBIO, &non_block); } +# define sockerr_again() (WSAGetLastError() == WSAEINTR || WSAGetLastError() == WSAEINPROGRESS || WSAGetLastError() == WSAEWOULDBLOCK) +# define ETIMEDOUT WSAETIMEDOUT +# define sleep(a) Sleep(a*1000) +#endif @@ -0,0 +1,165 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Main file (Unix specific part) */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "bitlbee.h" +#include "commands.h" +#include "crypting.h" +#include "protocols/nogaim.h" +#include "help.h" +#include <signal.h> +#include <unistd.h> +#include <sys/time.h> + +global_t global; /* Against global namespace pollution */ + +static void sighandler( int signal ); +gboolean bitlbee_dirty_workaround( gpointer data ); + +int main( int argc, char *argv[] ) +{ + int i = 0; + struct sigaction sig, old; + + memset( &global, 0, sizeof( global_t ) ); + + global.loop = g_main_new( FALSE ); + + log_init( ); + nogaim_init( ); + + CONF_FILE = g_strdup( CONF_FILE_DEF ); + + global.helpfile = g_strdup( HELP_FILE ); + + global.conf = conf_load( argc, argv ); + if( global.conf == NULL ) + return( 1 ); + + if( global.conf->runmode == RUNMODE_INETD ) + { + i = bitlbee_inetd_init(); + log_message( LOGLVL_INFO, "Bitlbee %s starting in inetd mode.", BITLBEE_VERSION ); + + } + else if( global.conf->runmode == RUNMODE_DAEMON ) + { + i = bitlbee_daemon_init(); + log_message( LOGLVL_INFO, "Bitlbee %s starting in daemon mode.", BITLBEE_VERSION ); + } + if( i != 0 ) + return( i ); + + /* Catch some signals to tell the user what's happening before quitting */ + memset( &sig, 0, sizeof( sig ) ); + sig.sa_handler = sighandler; + sigaction( SIGPIPE, &sig, &old ); + sig.sa_flags = SA_RESETHAND; + sigaction( SIGINT, &sig, &old ); + sigaction( SIGILL, &sig, &old ); + sigaction( SIGBUS, &sig, &old ); + sigaction( SIGFPE, &sig, &old ); + sigaction( SIGSEGV, &sig, &old ); + sigaction( SIGTERM, &sig, &old ); + sigaction( SIGQUIT, &sig, &old ); + sigaction( SIGXCPU, &sig, &old ); + + if( !getuid() || !geteuid() ) + log_message( LOGLVL_WARNING, "BitlBee is running with root privileges. Why?" ); + if( access( global.conf->configdir, F_OK ) != 0 ) + log_message( LOGLVL_WARNING, "The configuration directory %s does not exist. Configuration won't be saved.", CONFIG ); + else if( access( global.conf->configdir, R_OK ) != 0 || access( global.conf->configdir, W_OK ) != 0 ) + log_message( LOGLVL_WARNING, "Permission problem: Can't read/write from/to %s.", global.conf->configdir ); + if( help_init( &(global.help) ) == NULL ) + log_message( LOGLVL_WARNING, "Error opening helpfile %s.", HELP_FILE ); + + /* Workaround against runaway problems. Bah, this is really dirty, + but in the end not really different from the <=0.91 situation, + which makes it an acceptable temporary "solution". */ + // g_timeout_add( 0, bitlbee_dirty_workaround, NULL ); + + g_main_run( global.loop ); + + return( 0 ); +} + +gboolean bitlbee_dirty_workaround( gpointer data ) +{ + usleep( 50000 ); + return( TRUE ); +} + +void proxyprofiler_dump(); + +static void sighandler( int signal ) +{ + /* FIXME: In fact, calling log_message() here can be dangerous. But well, let's take the risk for now. */ + + if( signal == SIGTERM ) + { + static int first = 1; + + if( first ) + { + /* We don't know what we were doing when this signal came in. It's not safe to touch + the user data now (not to mention writing them to disk), so add a timer. */ + + log_message( LOGLVL_ERROR, "SIGTERM received, cleaning up process." ); + g_timeout_add_full( G_PRIORITY_LOW, 1, (GSourceFunc) bitlbee_shutdown, NULL, NULL ); + + first = 0; + } + else + { + /* Well, actually, for now we'll never need this part because this signal handler + will never be called more than once in a session for a non-SIGPIPE signal... + But just in case we decide to change that: */ + + log_message( LOGLVL_ERROR, "SIGTERM received twice, so long for a clean shutdown." ); + raise( signal ); + } + } +#ifdef PROXYPROFILER + else if( signal == SIGXCPU ) + { + write_io_activity(); + proxyprofiler_dump(); + log_message( LOGLVL_ERROR, "Received SIGXCPU, dumping some debugging info." ); + exit( 1 ); + } +#endif + else if( signal != SIGPIPE ) + { + log_message( LOGLVL_ERROR, "Fatal signal received: %d. That's probably a bug.", signal ); + raise( signal ); + } +} + +double gettime() +{ + struct timeval time[1]; + + gettimeofday( time, 0 ); + return( (double) time->tv_sec + (double) time->tv_usec / 1000000 ); +} @@ -0,0 +1,131 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2001-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* URL/mirror stuff - Stolen from Axel */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "url.h" + +/* Convert an URL to a url_t structure */ +int url_set( url_t *url, char *set_url ) +{ + char s[MAX_STRING]; + char *i, *j; + + /* protocol:// */ + if( ( i = strstr( set_url, "://" ) ) == NULL ) + { + url->proto = PROTO_DEFAULT; + strncpy( s, set_url, MAX_STRING ); + } + else + { + if( g_strncasecmp( set_url, "http", i - set_url ) == 0 ) + url->proto = PROTO_HTTP; + else if( g_strncasecmp( set_url, "socks4", i - set_url ) == 0 ) + url->proto = PROTO_SOCKS4; + else if( g_strncasecmp( set_url, "socks5", i - set_url ) == 0 ) + url->proto = PROTO_SOCKS5; + else + { + return( 0 ); + } + strncpy( s, i + 3, MAX_STRING ); + } + + /* Split */ + if( ( i = strchr( s, '/' ) ) == NULL ) + { + strcpy( url->dir, "/" ); + } + else + { + *i = 0; + g_snprintf( url->dir, MAX_STRING, "/%s", i + 1 ); + if( url->proto == PROTO_HTTP ) + http_encode( url->dir ); + } + strncpy( url->host, s, MAX_STRING ); + j = strchr( url->dir, '?' ); + if( j != NULL ) + *j = 0; + i = strrchr( url->dir, '/' ); + *i = 0; + if( j != NULL ) + *j = '?'; + if( i == NULL ) + { + strcpy( url->file, url->dir ); + strcpy( url->dir, "/" ); + } + else + { + strcpy( url->file, i + 1 ); + strcat( url->dir, "/" ); + } + + /* Check for username in host field */ + if( strrchr( url->host, '@' ) != NULL ) + { + strncpy( url->user, url->host, MAX_STRING ); + i = strrchr( url->user, '@' ); + *i = 0; + strcpy( url->host, i + 1 ); + *url->pass = 0; + } + /* If not: Fill in defaults */ + else + { + if( url->proto == PROTO_FTP ) + { + strcpy( url->user, "anonymous" ); + strcpy( url->pass, "-p.artmaps@lintux.cx" ); + } + else + { + *url->user = *url->pass = 0; + } + } + + /* Password? */ + if( ( i = strchr( url->user, ':' ) ) != NULL ) + { + *i = 0; + strcpy( url->pass, i + 1 ); + } + /* Port number? */ + if( ( i = strchr( url->host, ':' ) ) != NULL ) + { + *i = 0; + sscanf( i + 1, "%i", &url->port ); + } + /* Take default port numbers from /etc/services */ + else + { + if( url->proto == PROTO_HTTP ) + url->port = 8080; + else if( url->proto == PROTO_SOCKS4 || url->proto == PROTO_SOCKS4 ) + url->port = 1080; + } + + return( url->port > 0 ); +} @@ -0,0 +1,44 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2001-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* URL/mirror stuff - Stolen from Axel */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "bitlbee.h" + +#define PROTO_HTTP 2 +#define PROTO_SOCKS4 3 +#define PROTO_SOCKS5 4 +#define PROTO_DEFAULT PROTO_HTTP + +typedef struct url +{ + int proto; + int port; + char host[MAX_STRING]; + char dir[MAX_STRING]; + char file[MAX_STRING]; + char user[MAX_STRING]; + char pass[MAX_STRING]; +} url_t; + +int url_set( url_t *url, char *set_url ); @@ -0,0 +1,223 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Stuff to handle, save and search buddies */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#define BITLBEE_CORE +#include "bitlbee.h" + +user_t *user_add( irc_t *irc, char *nick ) +{ + user_t *u, *lu = NULL; + char *key; + + if( !nick_ok( nick ) ) + return( NULL ); + + if( user_find( irc, nick ) != NULL ) + return( NULL ); + + if( ( u = irc->users ) ) + { + while( u ) + { + if( nick_cmp( nick, u->nick ) < 0 ) + break; + + lu = u; + u = u->next; + } + + u = g_new0( user_t, 1 ); + if( lu ) + { + u->next = lu->next; + lu->next = u; + } + else + { + u->next = irc->users; + irc->users = u; + } + } + else + { + irc->users = u = g_new0( user_t, 1 ); + } + + u->user = u->realname = u->host = u->nick = g_strdup( nick ); + u->is_private = set_getint( irc, "private" ); + + key = g_strdup( nick ); + nick_lc( key ); + g_hash_table_insert( irc->userhash, key, u ); + + return( u ); +} + +int user_del( irc_t *irc, char *nick ) +{ + user_t *u, *t; + char *key; + gpointer okey, ovalue; + + if( !nick_ok( nick ) ) + return( 0 ); + + u = irc->users; + t = NULL; + while( u ) + { + if( nick_cmp( u->nick, nick ) == 0 ) + { + /* Get this key now already, since "nick" might be free()d + at the time we start playing with the hash... */ + key = g_strdup( nick ); + nick_lc( key ); + + if( t ) + t->next = u->next; + else + irc->users = u->next; + if( u->online ) + irc_kill( irc, u ); + g_free( u->nick ); + if( u->nick != u->user ) g_free( u->user ); + if( u->nick != u->host ) g_free( u->host ); + if( u->nick != u->realname ) g_free( u->realname ); + if( u->away ) g_free( u->away ); + if( u->handle ) g_free( u->handle ); + if( u->sendbuf ) g_free( u->sendbuf ); + if( u->sendbuf_timer ) g_source_remove( u->sendbuf_timer ); + g_free( u ); + + if( !g_hash_table_lookup_extended( irc->userhash, key, &okey, &ovalue ) || ovalue != u ) + { + g_free( key ); + return( 1 ); /* Although this is a severe error, the user is removed from the list... */ + } + g_hash_table_remove( irc->userhash, key ); + g_free( key ); + g_free( okey ); + + return( 1 ); + } + u = (t=u)->next; + } + + return( 0 ); +} + +user_t *user_find( irc_t *irc, char *nick ) +{ + char key[512] = ""; + + strncpy( key, nick, sizeof( key ) - 1 ); + if( nick_lc( key ) ) + return( g_hash_table_lookup( irc->userhash, key ) ); + else + return( NULL ); +} + +user_t *user_findhandle( struct gaim_connection *gc, char *handle ) +{ + user_t *u = gc->irc->users; + + while( u ) + { + if( u->gc == gc && u->handle && handle_cmp( u->handle, handle, gc->protocol ) == 0 ) + break; + u = u->next; + } + + return( u ); +} + +void user_rename( irc_t *irc, char *oldnick, char *newnick ) +{ + user_t *u = user_find( irc, oldnick ); + gpointer okey, ovalue; + char *key; + + if( !u ) return; /* Should've been checked by the caller... */ + + g_free( u->nick ); + if( u->nick == u->user ) u->user = NULL; + if( u->nick == u->host ) u->host = NULL; + if( u->nick == u->realname ) u->realname = NULL; + u->nick = g_strdup( newnick ); + if( !u->user ) u->user = u->nick; + if( !u->host ) u->user = u->host; + if( !u->realname ) u->user = u->realname; + + /* Remove the old reference to this user from the hash and create a + new one with the new nick. This is indeed a bit messy. */ + key = g_strdup( oldnick ); + nick_lc( key ); + if( !g_hash_table_lookup_extended( irc->userhash, key, &okey, &ovalue ) || ovalue != u ) + { + g_free( key ); + return; /* This really shouldn't happen! */ + } + g_hash_table_remove( irc->userhash, key ); + g_free( key ); + g_free( okey ); + + key = g_strdup( newnick ); + nick_lc( key ); + g_hash_table_insert( irc->userhash, key, u ); + + /* Also, let's try to keep the linked list nicely sorted. Fear this + code. If my teacher would see this, she would cry. ;-) */ + { + user_t *u1, *lu1; + + /* Remove the user from the old position in the chain. */ + if( u == irc->users ) + { + irc->users = u->next; + } + else + { + u1 = u; + for( lu1 = irc->users; lu1->next != u1; lu1 = lu1->next ); + lu1->next = u1->next; + } + + /* Search for the new position. */ + for( lu1 = NULL, u1 = irc->users; u1; u1 = u1->next ) + { + if( nick_cmp( newnick, u1->nick ) < 0 ) + break; + + lu1 = u1; + } + + /* Insert it at this new position. */ + u->next = u1; + if( lu1 ) + lu1->next = u; + else + irc->users = u; + } +} @@ -0,0 +1,56 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* Stuff to handle, save and search buddies */ + +/* + This program is free software; you can 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +typedef struct __USER +{ + char *nick; + char *user; + char *host; + char *realname; + + char *away; + + char is_private; + char online; + + char *handle; + struct gaim_connection *gc; + + char *sendbuf; + time_t last_typing_notice; + int sendbuf_len; + guint sendbuf_timer; + int sendbuf_flags; + + int (*send_handler) ( irc_t *irc, struct __USER *u, char *msg, int flags ); + + struct __USER *next; +} user_t; + +user_t *user_add( struct irc *irc, char *nick ); +int user_del( irc_t *irc, char *nick ); +G_MODULE_EXPORT user_t *user_find( irc_t *irc, char *nick ); +G_MODULE_EXPORT user_t *user_findhandle( struct gaim_connection *gc, char *handle ); +void user_rename( irc_t *irc, char *oldnick, char *newnick ); diff --git a/utils/README b/utils/README new file mode 100644 index 00000000..92b0348b --- /dev/null +++ b/utils/README @@ -0,0 +1,58 @@ +This directory contains tiny additional programs which you might just like +or need to run BitlBee: + + +* bitlbeed.c + +If you want to run BitlBee on a machine you don't have root access to, this +utility will help you. Compiling it is easy: 'gcc bitlbeed.c -o bitlbeed', +you don't need any special flags. Use 'bitlbeed -h' to get more help. + +For example, 'bitlbeed -p6669 -n1 /home/wilmer/bin/bitlbee' will start +listening on TCP port 6669 (on any interface, you might not want that!) +and connect the specified BitlBee program to this socket as soon as +someone connects. The -n1 makes sure only one person can be connected +at once. + +Of course this program can be used for other programs too, not just BitlBee. + + +* create_nicksfile.pl (Christian Friedl <christian.friedl@chello.at>) + +This program reads your ~/.licq/ configuration data and convert it to a +correct .nicks file. This program can be extended to read other contact +list file formats as well. + + +* centericq2bitlbee.sh (geno <geno@xenyon.com>) + +Converter script for CenterICQ ICQ contact lists. See the documentation +for more information. + + +* convert_gnomeicu.txt + +Not a program, but this one contains a regex which should correctly +convert GnomeICU configuration files into the BitlBee format. + + +* Dynamic MOTD for BitlBee (Geert Hauwaerts <geert@hauwaerts.be>) + +Originally, I wanted to put this program here, but Geert put it online +on his own server, with docs and stuff, so I guess it's better to put +a link here. dmotd is a little script which generates a motd with some +nice statistics, especially nice for servers with many people on it. + +See http://dmotd.hauwaerts.be/ for more information. + + +* BitlBee-specific Irssi scripts for: tab completion, typing notifica- +tions, auto-away and more, by Tijmen Ruizendaal <tijmen@fokdat.nl>. + +There are too many scripts to include them all with BitlBee (and keep +them up-to-date), so you should get them from Tijmen's site: + +http://fokdat.nl/~tijmen/software/irssi-bitlbee.html + + +Please do send your sources if you write anything useful for the Bee! diff --git a/utils/bitlbeed.c b/utils/bitlbeed.c new file mode 100644 index 00000000..b8db348e --- /dev/null +++ b/utils/bitlbeed.c @@ -0,0 +1,455 @@ +/****************************************************************\ +* * +* bitlbeed.c * +* * +* A tiny daemon to allow you to run The Bee as a non-root user * +* (without access to /etc/inetd.conf or whatever) * +* * +* Copyright 2002-2004 Wilmer van der Gaast <lintux@debian.org> * +* * +* Licensed under the GNU General Public License * +* * +* Modified by M. Dennis, 20040627 * +\****************************************************************/ + +/* + ChangeLog: + + 2004-06-27: Added support for AF_LOCAL (UNIX domain) sockets + Renamed log to do_log to fix conflict warning + Changed protocol to 0 (6 is not supported?) + Added error check for socket() + Added a no-fork (debug) mode + 2004-05-15: Added rate limiting + 2003-12-26: Added the SO_REUSEADDR sockopt, logging and CPU-time limiting + for clients using setrlimit(), fixed the execv() call + 2002-11-29: Added the timeout so old child processes clean up faster + 2002-11-28: First version +*/ + +#define SELECT_TIMEOUT 2 +#define MAX_LOG_LEN 128 + +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <fcntl.h> +#include <stdarg.h> +#include <time.h> + +#include <sys/wait.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/resource.h> +#include <sys/stat.h> + +#include <sys/un.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +typedef struct settings +{ + char local; + char debug; + char *interface; + signed int port; + + unsigned char max_conn; + int seconds; + + int rate_seconds; + int rate_times; + int rate_ignore; + + char **call; +} settings_t; + +typedef struct ipstats +{ + unsigned int ip; + + time_t rate_start; + int rate_times; + time_t rate_ignore; + + struct ipstats *next; +} ipstats_t; + +FILE *logfile; +ipstats_t *ipstats; + +settings_t *set_load( int argc, char *argv[] ); +void do_log( char *fmt, ... ); +ipstats_t *ip_get( char *ip_txt ); + +int main( int argc, char *argv[] ) +{ + const int rebind_on = 1; + settings_t *set; + + int serv_fd, serv_len; + struct sockaddr_in serv_addr; + struct sockaddr_un local_addr; + + pid_t st; + + if( !( set = set_load( argc, argv ) ) ) + return( 1 ); + + if( !logfile ) + if( !( logfile = fopen( "/dev/null", "w" ) ) ) + { + perror( "fopen" ); + return( 1 ); + } + + fcntl( fileno( logfile ), F_SETFD, FD_CLOEXEC ); + + if( set->local ) + serv_fd = socket( PF_LOCAL, SOCK_STREAM, 0 ); + else + serv_fd = socket( PF_INET, SOCK_STREAM, 0 ); + if( serv_fd < 0 ) + { + perror( "socket" ); + return( 1 ); + } + setsockopt( serv_fd, SOL_SOCKET, SO_REUSEADDR, &rebind_on, sizeof( rebind_on ) ); + fcntl( serv_fd, F_SETFD, FD_CLOEXEC ); + if (set->local) { + local_addr.sun_family = AF_LOCAL; + strncpy( local_addr.sun_path, set->interface, sizeof( local_addr.sun_path ) - 1 ); + local_addr.sun_path[sizeof( local_addr.sun_path ) - 1] = '\0'; + + /* warning - don't let untrusted users run this program if it + is setuid/setgid! Arbitrary file deletion risk! */ + unlink( set->interface ); + if( bind( serv_fd, (struct sockaddr *) &local_addr, SUN_LEN( &local_addr ) ) != 0 ) + { + perror( "bind" ); + return( 1 ); + } + chmod( set->interface, S_IRWXO|S_IRWXG|S_IRWXU ); + + } else { + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = inet_addr( set->interface ); + serv_addr.sin_port = htons( set->port ); + serv_len = sizeof( serv_addr ); + + if( bind( serv_fd, (struct sockaddr *) &serv_addr, serv_len ) != 0 ) + { + perror( "bind" ); + return( 1 ); + } + } + + if( listen( serv_fd, set->max_conn ) != 0 ) + { + perror( "listen" ); + return( 1 ); + } + + if ( ! set->debug ) { + st = fork(); + if( st < 0 ) + { + perror( "fork" ); + return( 1 ); + } + else if( st > 0 ) + { + return( 0 ); + } + + setsid(); + close( 0 ); + close( 1 ); + close( 2 ); + } + + do_log( "bitlbeed running" ); + + /* The Daemon */ + while( 1 ) + { + int cli_fd, cli_len, i, st; + struct sockaddr_in cli_addr; + struct sockaddr_un cli_local; + ipstats_t *ip; + char *cli_txt; + pid_t child; + + static int running = 0; + + fd_set rd; + struct timeval tm; + + /* accept() only returns after someone connects. To clean up old + processes (by running waitpid()) it's better to use select() + with a timeout. */ + FD_ZERO( &rd ); + FD_SET( serv_fd, &rd ); + tm.tv_sec = SELECT_TIMEOUT; + tm.tv_usec = 0; + if( select( serv_fd + 1, &rd, NULL, NULL, &tm ) > 0 ) + { + if (set->local) { + cli_len = SUN_LEN( &cli_local ); + cli_fd = accept( serv_fd, (struct sockaddr *) &cli_local, &cli_len ); + cli_txt = "127.0.0.1"; + } else { + cli_len = sizeof( cli_addr ); + cli_fd = accept( serv_fd, (struct sockaddr *) &cli_addr, &cli_len ); + cli_txt = inet_ntoa( cli_addr.sin_addr ); + } + + ip = ip_get( cli_txt ); + + if( set->rate_times == 0 || time( NULL ) > ip->rate_ignore ) + { + /* We want this socket on stdout and stderr too! */ + dup( cli_fd ); dup( cli_fd ); + + if( ( child = fork() ) == 0 ) + { + if( set->seconds ) + { + struct rlimit li; + + li.rlim_cur = (rlim_t) set->seconds; + li.rlim_max = (rlim_t) set->seconds + 1; + setrlimit( RLIMIT_CPU, &li ); + } + execv( set->call[0], set->call ); + do_log( "Error while executing %s!", set->call[0] ); + return( 1 ); + } + + running ++; + close( 0 ); + close( 1 ); + close( 2 ); + + do_log( "Started child process for client %s (PID=%d), got %d clients now", cli_txt, child, running ); + + if( time( NULL ) < ( ip->rate_start + set->rate_seconds ) ) + { + ip->rate_times ++; + if( ip->rate_times >= set->rate_times ) + { + do_log( "Client %s crossed the limit; ignoring for the next %d seconds", cli_txt, set->rate_ignore ); + ip->rate_ignore = time( NULL ) + set->rate_ignore; + ip->rate_start = 0; + } + } + else + { + ip->rate_start = time( NULL ); + ip->rate_times = 1; + } + } + else + { + do_log( "Ignoring connection from %s", cli_txt ); + close( cli_fd ); + } + } + + /* If the max. number of connection is reached, don't accept + new connections until one expires -> Not always WNOHANG + + Cleaning up child processes is a good idea anyway... :-) */ + while( ( i = waitpid( 0, &st, ( ( running < set->max_conn ) || ( set->max_conn == 0 ) ) ? WNOHANG : 0 ) ) > 0 ) + { + running --; + if( WIFEXITED( st ) ) + { + do_log( "Child process (PID=%d) exited normally with status %d. %d Clients left now", + i, WEXITSTATUS( st ), running ); + } + else if( WIFSIGNALED( st ) ) + { + do_log( "Child process (PID=%d) killed by signal %d. %d Clients left now", + i, WTERMSIG( st ), running ); + } + else + { + /* Should not happen AFAIK... */ + do_log( "Child process (PID=%d) stopped for unknown reason, %d clients left now", + i, running ); + } + } + } + + return( 0 ); +} + +settings_t *set_load( int argc, char *argv[] ) +{ + settings_t *set; + int opt, i; + + set = malloc( sizeof( settings_t ) ); + memset( set, 0, sizeof( settings_t ) ); + set->interface = NULL; /* will be filled in later */ + set->port = 6667; + set->local = 0; + set->debug = 0; + + set->rate_seconds = 600; + set->rate_times = 5; + set->rate_ignore = 900; + + while( ( opt = getopt( argc, argv, "i:p:n:t:l:r:hud" ) ) >= 0 ) + { + if( opt == 'i' ) + { + set->interface = strdup( optarg ); + } + else if( opt == 'p' ) + { + if( ( sscanf( optarg, "%d", &i ) != 1 ) || ( i <= 0 ) || ( i > 65535 ) ) + { + fprintf( stderr, "Invalid port number: %s\n", optarg ); + return( NULL ); + } + set->port = i; + } + else if( opt == 'n' ) + { + if( ( sscanf( optarg, "%d", &i ) != 1 ) || ( i < 0 ) ) + { + fprintf( stderr, "Invalid number of connections: %s\n", optarg ); + return( NULL ); + } + set->max_conn = i; + } + else if( opt == 't' ) + { + if( ( sscanf( optarg, "%d", &i ) != 1 ) || ( i < 0 ) || ( i > 600 ) ) + { + fprintf( stderr, "Invalid number of seconds: %s\n", optarg ); + return( NULL ); + } + set->seconds = i; + } + else if( opt == 'l' ) + { + if( !( logfile = fopen( optarg, "a" ) ) ) + { + perror( "fopen" ); + fprintf( stderr, "Error opening logfile, giving up.\n" ); + return( NULL ); + } + setbuf( logfile, NULL ); + } + else if( opt == 'r' ) + { + if( sscanf( optarg, "%d,%d,%d", &set->rate_seconds, &set->rate_times, &set->rate_ignore ) != 3 ) + { + fprintf( stderr, "Invalid argument to -r.\n" ); + return( NULL ); + } + } + else if( opt == 'u' ) + set->local = 1; + else if( opt == 'd' ) + set->debug = 1; + else if( opt == 'h' ) + { + printf( "Usage: %s [-i <interface>] [-p <port>] [-n <num>] [-r x,y,z] ...\n" + " ... <command> <args...>\n" + "A simple inetd-like daemon to have a program listening on a TCP socket without\n" + "needing root access to the machine\n" + "\n" + " -i Specify the interface (by IP address) to listen on.\n" + " (Default: 0.0.0.0 (any interface))\n" + " -p Port number to listen on. (Default: 6667)\n" + " -n Maximum number of connections. (Default: 0 (unlimited))\n" + " -t Specify the maximum number of CPU seconds per process.\n" + " (Default: 0 (unlimited))\n" + " -l Specify a logfile. (Default: none)\n" + " -r Rate limiting: Ignore a host for z seconds when it connects for more\n" + " than y times in x seconds. (Default: 600,5,900. Disable: 0,0,0)\n" + " -u Use a local socket, by default /tmp/bitlbee (override with -i <filename>)\n" + " -d Don't fork for listening (for debugging purposes)\n" + " -h This information\n", argv[0] ); + return( NULL ); + } + } + + if( set->interface == NULL ) + set->interface = (set->local) ? "/tmp/bitlbee" : "0.0.0.0"; + + if( optind == argc ) + { + fprintf( stderr, "Missing program parameter!\n" ); + return( NULL ); + } + + /* The remaining arguments are the executable and its arguments */ + set->call = malloc( ( argc - optind + 1 ) * sizeof( char* ) ); + memcpy( set->call, argv + optind, sizeof( char* ) * ( argc - optind ) ); + set->call[argc-optind] = NULL; + + return( set ); +} + +void do_log( char *fmt, ... ) +{ + va_list params; + char line[MAX_LOG_LEN]; + time_t tm; + int l; + + memset( line, 0, MAX_LOG_LEN ); + + tm = time( NULL ); + strcpy( line, ctime( &tm ) ); + l = strlen( line ); + line[l-1] = ' '; + + va_start( params, fmt ); + vsnprintf( line + l, MAX_LOG_LEN - l - 2, fmt, params ); + va_end( params ); + strcat( line, "\n" ); + + fprintf( logfile, "%s", line ); +} + +ipstats_t *ip_get( char *ip_txt ) +{ + unsigned int ip; + ipstats_t *l; + int p[4]; + + sscanf( ip_txt, "%d.%d.%d.%d", p + 0, p + 1, p + 2, p + 3 ); + ip = ( p[0] << 24 ) | ( p[1] << 16 ) | ( p[2] << 8 ) | ( p[3] ); + + for( l = ipstats; l; l = l->next ) + { + if( l->ip == ip ) + return( l ); + } + + if( ipstats ) + { + for( l = ipstats; l->next; l = l->next ); + + l->next = malloc( sizeof( ipstats_t ) ); + l = l->next; + } + else + { + l = malloc( sizeof( ipstats_t ) ); + ipstats = l; + } + memset( l, 0, sizeof( ipstats_t ) ); + + l->ip = ip; + + return( l ); +} diff --git a/utils/centericq2bitlbee.sh b/utils/centericq2bitlbee.sh new file mode 100755 index 00000000..b8c134e8 --- /dev/null +++ b/utils/centericq2bitlbee.sh @@ -0,0 +1,115 @@ +#!/bin/bash +# +# Author geno, <geno@xenyon.com> +# Date 2004-04-24 +# Version 0.1c +# + +show_help() +{ +cat << _EOF_ + +This script converts your CenterICQ contacts (AIM/ICQ) to BitlBee's contacts. +The use of this script is on you own risk. You agree by using this script. :-) + +SYNTAX: `basename $0` <protoname> [<add_proto_tag>] + + protoname - Choose the protocol you want to get your contacts from + by using "aim" or "icq" here. + + add_proto_tag - This is optional and adds a suffix to each nickname. + For an AIM contact it will look like this: geno|aim + For an ICQ contact it will be |icq , WOW! :-D + To enable this option use "on". + +NOTE: + After the conversion of one protocol is done you will find a file + called bitlbee_[protoname] in ~/.centericq . Append the content of + this file to /var/lib/bitlbee/[username].nicks . + + [username] is your username you use to talk to the BitlBee Server. + You will have to be root to edit this file! + +CREDITS: + This script was written by geno (geno@xenyon.com). + I hope it will help you to make the switch to BitlBee a bit easier. :-) + +_EOF_ +exit 0 +} + +case $1 in + "") show_help ;; + "icq") + nick_protocol="[1-9]*/" + protocol_const="3" + ;; + + "aim") + nick_protocol="a*/" + protocol_const="1" + ;; + + *) show_help ;; +esac + +# can we see CenterICQ's directory ? +if [ ! -d ~/.centericq ]; then + echo "The directory of CenterICQ (~/.centericq) was not found!" + echo "Maybe you are logged in with the wrong username." + exit 1 +fi + +# change to the center of all evil ;) +cd ~/.centericq + +# get the listing of all nicks +nick_listing=`ls -d $nick_protocol | sed 's/\ /_DuMmY_/g' | sed 's/\/_DuMmY_/\/ /g'` + +echo -e "\nConverting ...\n" + +# remove old conversion +rm -f ~/.centericq/bitlbee_$1 + +for nick_accountname in $nick_listing; do + # get rid of the slash and replace _DuMmY_ with space + nick_accountname=`echo "$nick_accountname" | sed 's/\/$//' | sed 's/_DuMmY_/\ /g'` + + # find centericq alias + nick_cicq_alias=`cat "$nick_accountname/info" | sed '46!d'` + + # if the centericq alias is the same as the account's name then + # it's not a real alias; search for account nickname + if [ "$nick_accountname" == "$nick_cicq_alias" ]; then + nick_accountalias=`cat "$nick_accountname/info" | sed '1!d'` + fi + + # save the best nickname for conversion + if [ "x$nick_accountalias" == "x" ]; then + nick="$nick_cicq_alias" + else + nick="$nick_accountalias" + fi + + # cut off the prefix 'a' of the accountname + if [ "$1" == "aim" ]; then + nick_accountname=`echo "$nick_accountname" | sed 's/^a//'` + fi + + # replace each space with an underscore (spaces are not allowed in irc nicknames) + nick=`echo "$nick" | sed 's/\ /_/g'` + + # if tags are wanted we will add them here + if [ "$2" == "on" ]; then + nick=`echo "$nick"\|$1` + fi + + # print output to std + echo "Found '$nick_accountname' with alias '$nick'" + # save output to file + echo "$nick_accountname" $protocol_const "$nick" >> ~/.centericq/bitlbee_$1 +done + +echo -e "\nYou can find this list as a file in ~/.centericq/bitlbee_$1." +echo -e "See help if you don't know what you have to do next.\n" + diff --git a/utils/convert_gnomeicu.txt b/utils/convert_gnomeicu.txt new file mode 100644 index 00000000..e2bd1377 --- /dev/null +++ b/utils/convert_gnomeicu.txt @@ -0,0 +1,7 @@ +15:03:38 zoo| wilmer: watch this: +15:03:40 zoo| cat ~/.icq/contacts.xml | sed "s/<\/user>/\n/g" | + sed "s/^.*<uin>//g" | sed "s/<\/nick>//" | sed "s/ /_/g" | + sed "s/<\/uin><nick>/ 3 /g" | grep -v -e "^<" +15:04:23 zoo| it does output your gnomeicu nicks to stdout + +Thanks to Claas Langbehn. Use this at your own risk, it's not tested by us. diff --git a/utils/create_nicksfile.pl b/utils/create_nicksfile.pl new file mode 100755 index 00000000..abd6d3d2 --- /dev/null +++ b/utils/create_nicksfile.pl @@ -0,0 +1,210 @@ +#!/usr/bin/perl +use strict; +use Getopt::Long; + + +my $conn = undef; +my %readline_funcs = ( 'licq' => \&import_readline_licq ); +my %open_funcs = ( 'licq' => \&import_open_licq ); +my %close_funcs = ( 'licq' => \&import_close_licq ); +my $funcname = undef; +my $dirname = undef; +my $filename = undef; +my $imported = 0; +my $not_imported = 0; +my $debug = 0; + +main(); +exit(0); + + +sub main { + my ($server,$port,$nick,$pass,$func,$dir,$file,$outfile); + my $dirfile; + if (($dirfile=pop @ARGV) =~ /^\-/ || !$dirfile) { + tell_usage(); + exit(0); + } + if ($dirfile =~ m|/|) { + $dirfile =~ m|^(.*)(/.+)$|; + $dir=$1; + $file=$2; + } else { + $dir=undef; + $file = $dirfile; + } + GetOptions( + 'from=s', => \$func, + 'of=s', => \$outfile, + 'debug', => \$debug + ); + if (!import_start($func,$dir,$file,$outfile,$debug)) { + tell_usage(); + } +} + +sub tell_usage { + print "Usage: create_nicksfile.pl [--from=FROM] [--of=OUTPUTFILE] [--debug] FILENAME\n"; + print " FROM defines which application we import from.\n", + print " Note that currently the only valid value for FROM is licq.\n"; + print " For further information, you might want to do perldoc create_nicksfile.pl\n"; +} + +sub import_start { + $funcname = (shift) || 'licq'; + $dirname = shift; + $filename = shift; + my $outfile = shift || 'bitlbee.nicks'; + $debug = shift; + my ($alias,$protocol,$name,$found); + open(OUT,'>'.$outfile) || die "unable to open $outfile"; + if (defined $open_funcs{$funcname}) { + if (&{$open_funcs{$funcname}}($dirname,$filename)) { + do { + ($alias,$protocol,$name,$found)=&{$readline_funcs{$funcname}}(); + print OUT "$alias $protocol $name\n" if $found; + } while ($found); + } else { + import_err('Unable to open '.$filename); + return 0; + } + } else { + import_err($funcname.' is no defined import function.'); + return 0; + } + close OUT; + &{$close_funcs{$funcname}}(); + return 1; +} + +sub import_err { + my $msg=shift; + print "\nError: $msg\n"; +} + +sub import_open_licq { + my ($dir,$name)=@_; + return open(IN,'<'.$dir.'/users.conf'); +} +sub import_close_licq { + close IN; +} +sub import_readline_licq { + my ($uin,$alias); + my $line; +GETLINE: + $line=<IN>; + if ($line) { + while ($line && $line !~ /^User\d+/) { + $line=<IN>; + } + if ($line) { + if ($line =~ /^User\d+\s*=\s*(\d+)(\.Licq)?$/) { # getting UIN + $uin=$1; + open(ALIAS,'<'.$dirname.'/users/'.$uin.'.Licq') || + open(ALIAS,'<'.$dirname.'/users/'.$uin.'.uin') || do { + warn "unable to open userfile for $uin"; + return (undef,undef,0); + }; + while (<ALIAS>) { + if (/^Alias\s*=\s*(.*)$/) { + $alias=$1; + $alias =~ s/\s+/_/g; + last; + } + } + close ALIAS; + $imported++; + return ($uin,3,$alias,1); + } else { + warn('Unknown line format: '.$line); + $not_imported++; + goto GETLINE; #### grrrr, sometimes there are negative uins in licq files... + } + } else { + return (undef,undef,0); + } + } else { + return undef; + } +} + +__END__ + +=head1 NAME + +create_nicksfile.pl - Create a valid bitlbee .nicks file + +=head1 SYNOPSIS + +create_nicksfile.pl [--from=FROM] [--of=OUTPUTFILE] [--debug] FILENAME + + FROM defines which application we import from. + Note that currently the only valid value for FROM + is licq. + + If of is missing, we write to bitlbee.nicks. + +=head1 DESCRIPTION + +We run thru the +files where the contacts reside and create +a bitlbee .nicks-file from them. + +=head1 DEPENDENCIES + +On the perlside, we need Getopt::Long. + +=head1 CAVEATS + +=head1 TODO + +&import_readline_... should take a filehandle as argument. + +Add more import functions. If you are interested, +to do so, you need to write the following functions: + +=over + +=item * + +import_open_<WHATEVER>(DIR,FILENAME) + +=item * + +import_close_<WHATEVER>() + +=item * + +import_readline_<WHATEVER>() + +=back + +and add them to the hashes + +=over + +=item * + +%readline_funcs + +=item * + +%open_funcs + +=item * + +%close_funcs + +=back + +at the top of this script. + + +=head1 AUTHORS + +Christian Friedl <vijeno@chello.at> + +Updated for the new Licq list firmat by Hugo Buddelmeijer <kmail@hugo.doemaarwat.nl> + +=cut |