Index: dbinit
===================================================================
--- .gdbinit	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,1 +1,0 @@
-set disable-randomization on
Index: ithub/workflows/coverity.yaml
===================================================================
--- .github/workflows/coverity.yaml	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,37 +1,0 @@
-name: Coverity
-
-on:
-  push:
-    branches:
-      - coverity_scan
-
-env:
-  PKG_CONFIG_PATH: $HOME/otr/usr/lib/pkgconfig/
-  LD_LIBRARY_PATH: $HOME/otr/usr/lib/
-
-jobs:
-  coverity:
-    runs-on: ubuntu-latest
-    steps:
-    - uses: actions/checkout@v4
-    - name: Install dependencies
-      run: |
-       sudo apt-get update && sudo apt-get -y install \
-           libevent-dev \
-           libpurple-dev \
-           check \
-           libpam0g-dev \
-           libldap2-dev \
-           fakeroot \
-           debhelper \
-           devscripts \
-           libotr5-dev \
-           libgnutls28-dev
-      if: "matrix.os == 'ubuntu-latest'"
-    - name: Configure
-      run: ./configure --otr=1 --debug=1 --pam=1 --ldap=1
-    - uses: vapier/coverity-scan-action@v1
-      with:
-        email: dx@dxzone.com.ar
-        token: ${{ secrets.COVERITY_SCAN_TOKEN }}
-        command: make
Index: ithub/workflows/test.yaml
===================================================================
--- .github/workflows/test.yaml	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,43 +1,0 @@
-name: Tests
-
-on:
-  - push
-  - pull_request
-
-env:
-  PKG_CONFIG_PATH: $HOME/otr/usr/lib/pkgconfig/
-  LD_LIBRARY_PATH: $HOME/otr/usr/lib/
-
-jobs:
-  shellcheck:
-    runs-on: ubuntu-latest
-    steps:
-    - name: Checkout code
-      uses: actions/checkout@v4
-    - name: Run ShellCheck
-      uses: ludeeus/action-shellcheck@master
-  tests:
-    needs: shellcheck
-    runs-on: ubuntu-latest
-    steps:
-    - uses: actions/checkout@v4
-    - name: Install dependencies
-      run: |
-       sudo apt-get update && sudo apt-get -y install \
-           libevent-dev \
-           libpurple-dev \
-           check \
-           libpam0g-dev \
-           libldap2-dev \
-           fakeroot \
-           debhelper \
-           devscripts \
-           libotr5-dev \
-           libgnutls28-dev \
-           libjsonparser-dev
-    - name: Configure
-      run: ./configure --pam=1 --ldap=1
-    - name: Run testsuite
-      run: make check
-    - name: Build debian package
-      run: dpkg-buildpackage -uc -us -b
Index: itignore
===================================================================
--- .gitignore	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,42 +1,0 @@
-Makefile.settings
-config.h
-bitlbee
-user-guide.txt
-user-guide.html
-help.txt
-build-arch-stamp
-build-stamp
-tags
-decode
-encode
-bitlbee.pc
-.gdb_history
-tests/check
-*.gcda
-*.gcov
-*.gcno
-*.o
-*.so
-coverage
-bitlbee.info
-bitlbee.exe
-.depend
-DEADJOE
-callgrind.out.*
-cov-int
-.bzr
-.bzrignore
-.git
-.gitignore
-bitlbee.service
-bitlbee@.service
-*~
-debian/bitlbee-common
-debian/bitlbee-dev
-debian/bitlbee-libpurple
-debian/bitlbee-plugin-otr
-debian/build-libpurple
-debian/build-native
-debian/files
-.clangd
-compile_commands.json
Index: imrc
===================================================================
--- .vimrc	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,6 +1,0 @@
-" vim can use per directory configuration files. To enable that neat feature only two little lines are needed in your ~/.vimrc:
-" set exrc            " enable per-directory .vimrc files
-" set secure          " disable unsafe commands in local .vimrc files
-
-set ts=8
-set noexpandtab
Index: COPYING
===================================================================
--- COPYING	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ COPYING	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -1,11 +1,11 @@
-                    GNU GENERAL PUBLIC LICENSE
-                       Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+		    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
+			    Preamble
 
   The licenses for most software are designed to take away your
@@ -16,5 +16,5 @@
 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 Lesser General Public License instead.)  You can apply it to
+the GNU Library General Public License instead.)  You can apply it to
 your programs, too.
 
@@ -57,5 +57,6 @@
 modification follow.
 
-                    GNU GENERAL PUBLIC LICENSE
+
+		    GNU GENERAL PUBLIC LICENSE
    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
 
@@ -112,4 +113,5 @@
     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,
@@ -169,4 +171,5 @@
 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
@@ -227,4 +230,5 @@
 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
@@ -256,5 +260,5 @@
 of promoting the sharing and reuse of software generally.
 
-                            NO WARRANTY
+			    NO WARRANTY
 
   11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
@@ -278,7 +282,8 @@
 POSSIBILITY OF SUCH DAMAGES.
 
-                     END OF TERMS AND CONDITIONS
-
-            How to Apply These Terms to Your New Programs
+		     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
@@ -304,7 +309,8 @@
     GNU General Public License for more details.
 
-    You should have received a copy of the GNU General Public License along
-    with this program; if not, write to the Free Software Foundation, Inc.,
-    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+    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.
@@ -313,5 +319,5 @@
 when it starts in an interactive mode:
 
-    Gnomovision version 69, Copyright (C) year name of author
+    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
@@ -336,4 +342,4 @@
 proprietary programs.  If your program is a subroutine library, you may
 consider it more useful to permit linking proprietary applications with the
-library.  If this is what you want to do, use the GNU Lesser General
+library.  If this is what you want to do, use the GNU Library General
 Public License instead of this License.
Index: Makefile
===================================================================
--- Makefile	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ Makefile	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -10,45 +10,24 @@
 
 # Program variables
-objects = bitlbee.o dcc.o help.o ipc.o irc.o irc_im.o irc_cap.o irc_channel.o irc_commands.o irc_send.o irc_user.o irc_util.o nick.o $(OTR_BI) query.o root_commands.o set.o storage.o $(STORAGE_OBJS) auth.o $(AUTH_OBJS) unix.o conf.o log.o
-allheaders = $(wildcard $(_SRCDIR_)*.h $(_SRCDIR_)lib/*.h $(_SRCDIR_)protocols/*.h)
-ifeq ($(EXTERNAL_JSON_PARSER),1)
-headers = $(filter-out $(_SRCDIR_)lib/json.h,$(allheaders))
-else
-headers = $(allheaders)
-endif
-subdirs = lib protocols
-
-OUTFILE = bitlbee
+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) $(OTR_PI) doc systemd
+all: $(OUTFILE)
 
-doc:
-ifdef DOC
-	$(MAKE) -C doc
-endif
-
-uninstall: uninstall-bin uninstall-doc 
+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 install-plugins
-	@echo
-	@echo Installed successfully
-	@echo
-ifndef SYSTEMDSYSTEMUNITDIR
+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
-endif
 	@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
-ifdef SYSTEMDSYSTEMUNITDIR
-	@echo If you want to start BitlBee using systemd, try \"make install-systemd\".
-endif
-	@echo To be able to compile third party plugins, run \"make install-dev\"
 	@echo
 
-.PHONY:   install   install-bin   install-etc   install-doc install-plugins install-systemd install-dev \
-        uninstall uninstall-bin uninstall-etc uninstall-doc uninstall-etc \
-        all clean distclean tar $(subdirs) doc
+.PHONY:   install   install-bin   install-etc   install-doc \
+        uninstall uninstall-bin uninstall-etc uninstall-doc \
+        all clean distclean tar $(subdirs)
 
 Makefile.settings:
@@ -58,66 +37,27 @@
 
 clean: $(subdirs)
-	rm -f *.o $(OUTFILE) core utils/bitlbeed init/bitlbee*.service
-	$(MAKE) -C tests clean
+	rm -f *.o $(OUTFILE) core utils/bitlbeed encode decode
 
 distclean: clean $(subdirs)
-	rm -rf .depend
-	rm -f Makefile.settings config.h bitlbee.pc
-	find . -name 'DEADJOE' -o -name '*.orig' -o -name '*.rej' -o -name '*~' -exec rm -f {} \;
-	@# May still be present in dirs of disabled protocols.
-	-find . -name .depend -exec rm -rf {} \;
-	$(MAKE) -C tests distclean
-
-check: all
-	$(MAKE) -C tests
-
-gcov: check
-	gcov *.c
-
-lcov: check
-	lcov --directory . --capture --output-file bitlbee.info
-	genhtml -o coverage bitlbee.info
+	rm -f Makefile.settings config.h
+	find . -name 'DEADJOE' -o -name '*.orig' -o -name '*.rej' -o -name '*~' | xargs rm -f
 
 install-doc:
-ifdef DOC
 	$(MAKE) -C doc install
-endif
 
 uninstall-doc:
-ifdef DOC
 	$(MAKE) -C doc uninstall
-endif
 
 install-bin:
-	mkdir -p $(DESTDIR)$(SBINDIR)
-	$(INSTALL) -m 0755 $(OUTFILE) $(DESTDIR)$(SBINDIR)/$(OUTFILE)
-ifdef IMPLIB
-	# import library for cygwin
-	mkdir -p $(DESTDIR)$(LIBDIR)
-	$(INSTALL) -m 0644 $(IMPLIB) $(DESTDIR)$(LIBDIR)/$(IMPLIB)
-endif
+	mkdir -p $(DESTDIR)$(BINDIR)
+	install -m 0755 $(OUTFILE) $(DESTDIR)$(BINDIR)/$(OUTFILE)
 
 uninstall-bin:
-	rm -f $(DESTDIR)$(SBINDIR)/$(OUTFILE)
-ifdef IMPLIB
-	rm -f $(DESTDIR)$(LIBDIR)/$(IMPLIB)
-endif
-
-install-dev:
-	mkdir -p $(DESTDIR)$(INCLUDEDIR)
-	$(INSTALL) -m 0644 config.h $(DESTDIR)$(INCLUDEDIR)
-	for i in $(headers); do $(INSTALL) -m 0644 $$i $(DESTDIR)$(INCLUDEDIR); done
-	mkdir -p $(DESTDIR)$(PCDIR)
-	$(INSTALL) -m 0644 bitlbee.pc $(DESTDIR)$(PCDIR)
-
-uninstall-dev:
-	rm -f $(foreach hdr,$(headers),$(DESTDIR)$(INCLUDEDIR)/$(hdr))
-	-rmdir $(DESTDIR)$(INCLUDEDIR)
-	rm -f $(DESTDIR)$(PCDIR)/bitlbee.pc
+	rm -f $(DESTDIR)$(BINDIR)/$(OUTFILE)
 
 install-etc:
 	mkdir -p $(DESTDIR)$(ETCDIR)
-	$(INSTALL) -m 0644 $(_SRCDIR_)motd.txt $(DESTDIR)$(ETCDIR)/motd.txt
-	$(INSTALL) -m 0644 $(_SRCDIR_)bitlbee.conf $(DESTDIR)$(ETCDIR)/bitlbee.conf
+	install -m 0644 motd.txt $(DESTDIR)$(ETCDIR)/motd.txt
+	install -m 0644 bitlbee.conf $(DESTDIR)$(ETCDIR)/bitlbee.conf
 
 uninstall-etc:
@@ -126,51 +66,16 @@
 	-rmdir $(DESTDIR)$(ETCDIR)
 
-install-plugins: install-plugin-otr
-
-install-plugin-otr:
-ifdef OTR_PI
-	mkdir -p $(DESTDIR)$(PLUGINDIR)
-	$(INSTALL) -m 0755 otr.so $(DESTDIR)$(PLUGINDIR)
-endif
-
-systemd:
-ifdef SYSTEMDSYSTEMUNITDIR
-	mkdir -p init
-	sed 's|@sbindir@|$(SBINDIR)|' $(_SRCDIR_)init/bitlbee.service.in > init/bitlbee.service
-	sed 's|@sbindir@|$(SBINDIR)|' $(_SRCDIR_)init/bitlbee@.service.in > init/bitlbee@.service
-endif
-
-install-systemd: systemd
-ifdef SYSTEMDSYSTEMUNITDIR
-	mkdir -p $(DESTDIR)$(SYSTEMDSYSTEMUNITDIR)
-	$(INSTALL) -m 0644 init/bitlbee.service $(DESTDIR)$(SYSTEMDSYSTEMUNITDIR)
-	$(INSTALL) -m 0644 init/bitlbee@.service $(DESTDIR)$(SYSTEMDSYSTEMUNITDIR)
-	$(INSTALL) -m 0644 $(_SRCDIR_)init/bitlbee.socket $(DESTDIR)$(SYSTEMDSYSTEMUNITDIR)
-else
-	@echo SYSTEMDSYSTEMUNITDIR not set, not installing systemd unit files.
-endif
-
-ifdef SYSUSERSDIR
-	$(INSTALL) -Dm644 init/bitlbee.sysusers $(DESTDIR)/$(SYSUSERSDIR)/bitlbee.conf
-else
-	@echo SYSUSERSDIR not set, not install sysusers.d files
-endif
-
 tar:
 	fakeroot debian/rules clean || make distclean
 	x=$$(basename $$(pwd)); \
 	cd ..; \
-	tar czf $$x.tar.gz --exclude=debian --exclude=.git* --exclude=.depend $$x
+	tar czf $$x.tar.gz --exclude=debian $$x
 
 $(subdirs):
-	$(MAKE) -C $@ $(MAKECMDGOALS)
+	@$(MAKE) -C $@ $(MAKECMDGOALS)
 
-$(OTR_PI): %.so: $(_SRCDIR_)%.c
-	@echo '*' Building plugin $@
-	$(VERBOSE) $(CC) $(CFLAGS) -fPIC -shared $(LDFLAGS) $< -o $@ $(OTRFLAGS)
-
-$(objects): %.o: $(_SRCDIR_)%.c
+$(objects): %.o: %.c
 	@echo '*' Compiling $<
-	$(VERBOSE) $(CC) -c $(CFLAGS) $(CFLAGS_BITLBEE) $< -o $@
+	@$(CC) -c $(CFLAGS) $< -o $@
 
 $(objects): Makefile Makefile.settings config.h
@@ -178,25 +83,16 @@
 $(OUTFILE): $(objects) $(subdirs)
 	@echo '*' Linking $(OUTFILE)
-	$(VERBOSE) $(CC) $(objects) $(subdirobjs) -o $(OUTFILE) $(LDFLAGS_BITLBEE) $(LDFLAGS) $(EFLAGS)
-ifneq ($(firstword $(STRIP)), \#)
+	@$(CC) $(objects) $(subdirobjs) -o $(OUTFILE) $(LFLAGS) $(EFLAGS)
+ifndef DEBUG
 	@echo '*' Stripping $(OUTFILE)
-	$(VERBOSE) -$(STRIP) $(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"`
-
-# Using this as a bogus Make target to test if a GNU-compatible version of
-# make is available.
-helloworld:
-	@echo Hello World
-
-# Check if we can load the helpfile. (This fails if some article is >1KB.)
-# If print returns a NULL pointer, the file is unusable.
-testhelp: doc
-	gdb --eval-command='b main' --eval-command='r' \
-	    --eval-command='print help_init(&global->helpfile, "doc/user-guide/help.txt")' \
-	    $(OUTFILE) < /dev/null
-
--include .depend/*.d
-# DO NOT DELETE
Index: ADME.md
===================================================================
--- README.md	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,54 +1,0 @@
-# BitlBee
-
-![](https://www.bitlbee.org/style/logo.png)
-
-[![Tests](https://github.com/bitlbee/bitlbee/actions/workflows/test.yaml/badge.svg)](https://github.com/bitlbee/bitlbee/actions/workflows/test.yaml)
-[![Coverity Scan Build Status](https://scan.coverity.com/projects/4028/badge.svg)](https://scan.coverity.com/projects/4028)
-
-An IRC to other chat networks gateway
-
-Main website: https://www.bitlbee.org/
-
-Bug tracker: https://bugs.bitlbee.org/
-
-Wiki: https://wiki.bitlbee.org/
-
-License: GPLv2
-
-## Installation
-
-BitlBee is available in the package managers of most distros.
-
-For debian/ubuntu/etc you may use the nightly APT repository: https://code.bitlbee.org/debian/
-
-You can also use a public server (such as `im.bitlbee.org`) instead of installing it: https://www.bitlbee.org/main.php/servers.html
-
-## Compiling
-
-If you wish to compile it yourself, ensure you have the following packages and their headers:
-
-* glib 2.32 or newer (not to be confused with glibc)
-* gnutls
-* python 2 or 3 (for the user guide)
-
-Some optional features have additional dependencies, such as libpurple, libotr, libevent, etc.
-NSS and OpenSSL are also available but not as well supported as GnuTLS.
-
-Once you have the dependencies, building should be a matter of:
-
-    ./configure
-    make
-    sudo make install
-
-## Development tips
-
-* To enable debug symbols: `./configure --debug=1`
-* To get some additional debug output for some protocols: `BITLBEE_DEBUG=1 ./bitlbee -Dnv`
-* Use github pull requests against the 'develop' branch to submit patches.
-* The coding style based on K&R with tabs and 120 columns. See `./doc/uncrustify.cfg` for the parameters used to reformat the code.
-* Mappings of bzr revisions to git commits (for historical purposes) are available in `./doc/git-bzr-rev-map`
-* See also `./doc/README` and `./doc/HACKING`
-
-## Help?
-
-Join **#BitlBee** on OFTC (**irc.oftc.net**) (OFTC, *not* freenode!)
Index: account.c
===================================================================
--- account.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ account.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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 );
+	}
+}
Index: account.h
===================================================================
--- account.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ account.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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
Index: th.c
===================================================================
--- auth.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,65 +1,0 @@
-#define BITLBEE_CORE
-#include "bitlbee.h"
-
-#ifdef WITH_PAM
-extern auth_backend_t auth_pam;
-#endif
-#ifdef WITH_LDAP
-extern auth_backend_t auth_ldap;
-#endif
-
-GList *auth_init(const char *backend)
-{
-	GList *gl = NULL;
-	int ok = backend ? 0 : 1;
-#ifdef WITH_PAM
-	gl = g_list_append(gl, &auth_pam);
-	if (backend && !strcmp(backend, "pam")) {
-		ok = 1;
-	}
-#endif
-#ifdef WITH_LDAP
-	gl = g_list_append(gl, &auth_ldap);
-	if (backend && !strcmp(backend, "ldap")) {
-		ok = 1;
-	}
-#endif
-
-	return ok ? gl : NULL;
-}
-
-storage_status_t auth_check_pass(irc_t *irc, const char *nick, const char *password)
-{
-	GList *gl;
-	storage_status_t status = storage_check_pass(irc, nick, password);
-
-	if (status == STORAGE_CHECK_BACKEND) {
-		for (gl = global.auth; gl; gl = gl->next) {
-			auth_backend_t *be = gl->data;
-			if (!strcmp(be->name, irc->auth_backend)) {
-				status = be->check_pass(nick, password);
-				break;
-			}
-		}
-	} else if (status == STORAGE_NO_SUCH_USER && global.conf->auth_backend) {
-		for (gl = global.auth; gl; gl = gl->next) {
-			auth_backend_t *be = gl->data;
-			if (!strcmp(be->name, global.conf->auth_backend)) {
-				status = be->check_pass(nick, password);
-				/* Save the user so storage_load will pick them up, similar to
-				 * what the register command would do */
-				if (status == STORAGE_OK) {
-					irc->auth_backend = g_strdup(global.conf->auth_backend);
-					storage_save(irc, (char *)password, 0);
-				}
-				break;
-			}
-		}
-	}
-
-	if (status == STORAGE_OK) {
-		irc_setpass(irc, password);
-	}
-
-	return status;
-}
Index: th.h
===================================================================
--- auth.h	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,13 +1,0 @@
-#ifndef __BITLBEE_AUTH_H__
-#define __BITLBEE_AUTH_H__
-
-#include "storage.h"
-
-typedef struct {
-	const char *name;
-	storage_status_t (*check_pass)(const char *nick, const char *password);
-} auth_backend_t;
-
-GList *auth_init(const char *backend);
-storage_status_t auth_check_pass(irc_t *irc, const char *nick, const char *password);
-#endif
Index: th_ldap.c
===================================================================
--- auth_ldap.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,77 +1,0 @@
-#define BITLBEE_CORE
-#define LDAP_DEPRECATED 1
-#include "bitlbee.h"
-#include <ldap.h>
-
-static storage_status_t ldap_check_pass(const char *nick, const char *password)
-{
-	LDAP *ldap;
-	LDAPMessage *msg, *entry;
-	char *dn = NULL;
-	char *filter;
-	char *attrs[1] = { NULL };
-	int ret, count;
-
-	if((ret = ldap_initialize(&ldap, NULL)) != LDAP_SUCCESS) {
-		log_message(LOGLVL_WARNING, "ldap_initialize failed: %s", ldap_err2string(ret));
-		return STORAGE_OTHER_ERROR;
-	}
-
-	/* First we do an anonymous bind to map uid=$nick to a DN*/
-	if((ret = ldap_simple_bind_s(ldap, NULL, NULL)) != LDAP_SUCCESS) {
-		ldap_unbind_s(ldap);
-		log_message(LOGLVL_WARNING, "Anonymous bind failed: %s", ldap_err2string(ret));
-		return STORAGE_OTHER_ERROR;
-	}
-
-
-	/* We search and process the result */
-	filter = g_strdup_printf("(uid=%s)", nick);
-	ret = ldap_search_ext_s(ldap, NULL, LDAP_SCOPE_SUBTREE, filter, attrs, 0, NULL, NULL, NULL, 1, &msg);
-	g_free(filter);
-
-	if(ret != LDAP_SUCCESS) {
-		ldap_unbind_s(ldap);
-		log_message(LOGLVL_WARNING, "uid search failed: %s", ldap_err2string(ret));
-		return STORAGE_OTHER_ERROR;
-	}
-
-	count = ldap_count_entries(ldap, msg);
-	if (count == -1) {
-		ldap_get_option(ldap, LDAP_OPT_ERROR_NUMBER, &ret);
-		ldap_msgfree(msg);
-		ldap_unbind_s(ldap);
-		log_message(LOGLVL_WARNING, "uid search failed: %s", ldap_err2string(ret));
-		return STORAGE_OTHER_ERROR;
-	}
-
-	if (!count) {
-		ldap_msgfree(msg);
-		ldap_unbind_s(ldap);
-		return STORAGE_NO_SUCH_USER;
-	}
-
-	entry = ldap_first_entry(ldap, msg);
-	dn = ldap_get_dn(ldap, entry);
-	ldap_msgfree(msg);
-
-	/* And now we bind as the user to authenticate */
-	ret = ldap_simple_bind_s(ldap, dn, password);
-	g_free(dn);
-	ldap_unbind_s(ldap);
-
-	switch (ret) {
-		case LDAP_SUCCESS:
-			return STORAGE_OK;
-		case LDAP_INVALID_CREDENTIALS:
-			return STORAGE_INVALID_PASSWORD;
-		default:
-			log_message(LOGLVL_WARNING, "Authenticated bind failed: %s", ldap_err2string(ret));
-			return STORAGE_OTHER_ERROR;
-	}
-}
-
-auth_backend_t auth_ldap = {
-	.name = "ldap",
-	.check_pass = ldap_check_pass,
-};
Index: th_pam.c
===================================================================
--- auth_pam.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,62 +1,0 @@
-#define BITLBEE_CORE
-#include "bitlbee.h"
-#include <security/pam_appl.h>
-
-#define PAM_CHECK(x) do { \
-	ret = (x); \
-	if(ret != PAM_SUCCESS) { \
-		pam_func = #x; \
-		goto pam_error; \
-	} \
-} while(0)
-
-/* This function fills in the password when PAM asks for it */
-int pamconv(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr) {
-	int i;
-	struct pam_response *rsp = g_new0(struct pam_response, num_msg);
-
-	for (i = 0; i < num_msg; i++) {
-		rsp[i].resp = NULL;
-		rsp[i].resp_retcode = 0;
-		if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF) {
-			rsp[i].resp = g_strdup((char *)appdata_ptr);
-		}
-	}
-	*resp = rsp;
-	return PAM_SUCCESS;
-}
-
-static storage_status_t pam_check_pass(const char *nick, const char *password)
-{
-	int ret;
-	const struct pam_conv pamc = { pamconv, (void*) password };
-	pam_handle_t *pamh = NULL;
-	char *pam_func;
-
-	PAM_CHECK(pam_start("bitlbee", nick, &pamc, &pamh));
-	PAM_CHECK(pam_authenticate(pamh, PAM_DISALLOW_NULL_AUTHTOK));
-	PAM_CHECK(pam_acct_mgmt(pamh, 0));
-
-	pam_end(pamh, ret);
-	return STORAGE_OK;
-
-pam_error:
-	switch (ret) {
-		case PAM_AUTH_ERR:
-			pam_end(pamh, ret);
-			return STORAGE_INVALID_PASSWORD;
-		case PAM_USER_UNKNOWN:
-		case PAM_PERM_DENIED:
-			pam_end(pamh, ret);
-			return STORAGE_NO_SUCH_USER;
-		default:
-			log_message(LOGLVL_WARNING, "%s failed: %s", pam_func, pam_strerror(pamh, ret));
-			pam_end(pamh, ret);
-			return STORAGE_OTHER_ERROR;
-	}
-}
-
-auth_backend_t auth_pam = {
-	.name = "pam",
-	.check_pass = pam_check_pass,
-};
Index: bitlbee.c
===================================================================
--- bitlbee.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ bitlbee.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -1,3 +1,3 @@
-/********************************************************************\
+  /********************************************************************\
   * BitlBee -- An IRC to other IM-networks gateway                     *
   *                                                                    *
@@ -20,6 +20,6 @@
   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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
 */
 
@@ -27,326 +27,575 @@
 #include "bitlbee.h"
 #include "commands.h"
+#include "crypting.h"
 #include "protocols/nogaim.h"
 #include "help.h"
-#include "ipc.h"
 #include <signal.h>
 #include <stdio.h>
 #include <errno.h>
 
-static gboolean bitlbee_io_new_client(gpointer data, gint fd, b_input_condition condition);
-
-static gboolean try_listen(struct addrinfo *res)
-{
+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;
-
-	global.listen_socket = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
-	if (global.listen_socket < 0) {
-		log_error("socket");
-		return FALSE;
-	}
-
-#ifdef IPV6_V6ONLY
-	if (res->ai_family == AF_INET6) {
-		i = 0;
-		setsockopt(global.listen_socket, IPPROTO_IPV6, IPV6_V6ONLY,
-		           (char *) &i, sizeof(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
-
-	/* TIME_WAIT (?) sucks.. */
-	i = 1;
-	setsockopt(global.listen_socket, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i));
-
-	i = bind(global.listen_socket, res->ai_addr, res->ai_addrlen);
-	if (i == -1) {
-		closesocket(global.listen_socket);
-		global.listen_socket = -1;
-
-		log_error("bind");
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-int bitlbee_daemon_init()
-{
-	struct addrinfo *res, hints, *addrinfo_bind;
-	int i;
-	FILE *fp;
-
-	log_link(LOGLVL_ERROR, LOGOUTPUT_CONSOLE);
-	log_link(LOGLVL_WARNING, LOGOUTPUT_CONSOLE);
-
-	memset(&hints, 0, sizeof(hints));
-	hints.ai_family = PF_UNSPEC;
-	hints.ai_socktype = SOCK_STREAM;
-	hints.ai_flags = AI_PASSIVE
-#ifdef AI_ADDRCONFIG
-	                 /* Disabled as it may be doing more harm than good: this flag
-	                    ignores IPv6 addresses on lo (which seems reasonable), but
-	                    the result is that some clients (including irssi) try to
-	                    connect to ::1 and fail.
-	                                | AI_ADDRCONFIG */
-#endif
-	;
-
-	i = getaddrinfo(global.conf->iface_in, global.conf->port, &hints, &addrinfo_bind);
-	if (i) {
-		log_message(LOGLVL_ERROR, "Couldn't parse address `%s': %s",
-		            global.conf->iface_in, gai_strerror(i));
-		return -1;
-	}
-
-	global.listen_socket = -1;
-
-	/* Try IPv6 first (which will become an IPv6+IPv4 socket). */
-	for (res = addrinfo_bind; res; res = res->ai_next) {
-		if (res->ai_family == AF_INET6 && try_listen(res)) {
-			break;
-		}
-	}
-
-	/* The rest (so IPv4, I guess). */
-	if (res == NULL) {
-		for (res = addrinfo_bind; res; res = res->ai_next) {
-			if (res->ai_family != AF_INET6 && try_listen(res)) {
-				break;
-			}
-		}
-	}
-
-	freeaddrinfo(addrinfo_bind);
-
-	if (global.listen_socket == -1 && errno == EADDRINUSE) {
-		log_message(LOGLVL_ERROR, "Can't listen on port %s. Is another bitlbee already running?",
-		            global.conf->port);
-		return(-1);
-	}
-
-	i = listen(global.listen_socket, 10);
-	if (i == -1) {
-		log_error("listen");
-		return(-1);
-	}
-
-	global.listen_watch_source_id = b_input_add(global.listen_socket, B_EV_IO_READ, bitlbee_io_new_client, NULL);
-
-	if (!global.conf->nofork) {
-		i = fork();
-		if (i == -1) {
-			log_error("fork");
-			return(-1);
-		} else if (i != 0) {
-			exit(0);
-		}
-
-		setsid();
-		i = chdir("/");
-		/* Don't use i, just make gcc happy. :-/ */
-
-		if (getenv("_BITLBEE_RESTART_STATE") == NULL) {
-			for (i = 0; i < 3; i++) {
-				if (close(i) == 0) {
-					/* Keep something bogus on those fd's just in case. */
-					open("/dev/null", O_WRONLY);
-				}
-			}
-		}
-	}
-
-	if (global.conf->runmode == RUNMODE_FORKDAEMON) {
-		ipc_master_load_state(getenv("_BITLBEE_RESTART_STATE"));
-	}
-
-	if (global.conf->runmode == RUNMODE_DAEMON || global.conf->runmode == RUNMODE_FORKDAEMON) {
-		ipc_master_listen_socket();
-	}
-
-	if ((fp = fopen(global.conf->pidfile, "w"))) {
-		fprintf(fp, "%d\n", (int) getpid());
-		fclose(fp);
-	} else {
-		log_message(LOGLVL_WARNING, "Warning: Couldn't write PID to `%s'", global.conf->pidfile);
-	}
-
-	if (!global.conf->nofork) {
-		log_link(LOGLVL_ERROR, LOGOUTPUT_SYSLOG);
-		log_link(LOGLVL_WARNING, LOGOUTPUT_SYSLOG);
-	}
-
-	return(0);
-}
-
+	
+	return( 0 );
+}
+ 
 int bitlbee_inetd_init()
 {
-	if (!irc_new(0)) {
-		return(1);
-	}
-
-	return(0);
-}
-
-gboolean bitlbee_io_current_client_read(gpointer data, gint fd, b_input_condition cond)
+	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;
-
-	st = read(irc->fd, line, sizeof(line) - 1);
-	if (st == 0) {
-		irc_abort(irc, 1, "Connection reset by peer");
+	
+	count_io_event(source, "main");
+
+	if( condition & G_IO_ERR || condition & G_IO_HUP )
+	{
+		irc_free( irc );
 		return FALSE;
-	} else if (st < 0) {
-		if (sockerr_again()) {
+	}
+	
+	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_abort(irc, 1, "Read error: %s", strerror(errno));
+		}
+		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);
-	}
-
-	irc_process(irc);
-
-	/* Normally, irc_process() shouldn't call irc_free() but irc_abort(). Just in case: */
-	if (!g_slist_find(irc_connection_list, irc)) {
-		log_message(LOGLVL_WARNING, "Abnormal termination of connection with fd %d.", fd);
+	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;
-	}
-
-	/* Very naughty, go read the RFCs! >:) */
-	if (irc->readbuffer && (strlen(irc->readbuffer) > 1024)) {
-		irc_abort(irc, 0, "Maximum line length exceeded");
-		return FALSE;
-	}
-
+	} 
+		
 	return TRUE;
 }
 
-gboolean bitlbee_io_current_client_write(gpointer data, gint fd, b_input_condition cond)
+gboolean bitlbee_io_current_client_write( GIOChannel *source, GIOCondition condition, gpointer data )
 {
 	irc_t *irc = data;
 	int st, size;
-
-	size = irc->sendbuffer->len;
-
-	if (size == 0) {
-		return FALSE;
-	}
-
-	st = write(irc->fd, irc->sendbuffer->str, size);
-
-	if (st == 0 || (st < 0 && !sockerr_again())) {
-		irc_abort(irc, 1, "Write error: %s", strerror(errno));
-		return FALSE;
-	} else if (st < 0) { /* && sockerr_again() */
-		return TRUE;
-	}
-
-	if (st == size) {
-		g_string_truncate(irc->sendbuffer, 0);
+	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 {
-		g_string_erase(irc->sendbuffer, 0, st);
-
-		return TRUE;
-	}
-}
-
-static gboolean bitlbee_io_new_client(gpointer data, gint fd, b_input_condition condition)
-{
-	socklen_t size = sizeof(struct sockaddr_in);
-	struct sockaddr_in conn_info;
-	int new_socket = accept(global.listen_socket, (struct sockaddr *) &conn_info, &size);
-
-	if (new_socket == -1) {
-		log_message(LOGLVL_WARNING, "Could not accept new connection: %s", strerror(errno));
-		return TRUE;
-	}
-
-	if (global.conf->runmode == RUNMODE_FORKDAEMON) {
-		pid_t client_pid = 0;
-		int fds[2];
-
-		if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == -1) {
-			log_message(LOGLVL_WARNING, "Could not create IPC socket for client: %s", strerror(errno));
-			fds[0] = fds[1] = -1;
-		}
-
-		sock_make_nonblocking(fds[0]);
-		sock_make_nonblocking(fds[1]);
-
-		client_pid = fork();
-
-		if (client_pid > 0 && fds[0] != -1) {
-			struct bitlbee_child *child;
-
-			/* TODO: Stuff like this belongs in ipc.c. */
-			child = g_new0(struct bitlbee_child, 1);
-			child->pid = client_pid;
-			child->ipc_fd = fds[0];
-			child->ipc_inpa = b_input_add(child->ipc_fd, B_EV_IO_READ, ipc_master_read, child);
-			child->to_fd = -1;
-			child_list = g_slist_append(child_list, child);
-
-			log_message(LOGLVL_INFO, "Creating new subprocess with pid %d.", (int) client_pid);
-
-			/* Close some things we don't need in the parent process. */
-			close(new_socket);
-			close(fds[1]);
-		} else if (client_pid == 0) {
-			irc_t *irc;
-
-			b_main_init();
-
-			/* Close the listening socket, we're a client. */
-			close(global.listen_socket);
-			b_event_remove(global.listen_watch_source_id);
-
-			/* Make a new pipe for the shutdown signal handler */
-			sighandler_shutdown_setup();
-
-			/* Make the connection. */
-			irc = irc_new(new_socket);
-
-			/* We can store the IPC fd there now. */
-			global.listen_socket = fds[1];
-			global.listen_watch_source_id = b_input_add(fds[1], B_EV_IO_READ, ipc_child_read, irc);
-
-			close(fds[0]);
-
-			ipc_master_free_all();
-		}
-	} else {
-		log_message(LOGLVL_INFO, "Creating new connection with fd %d.", new_socket);
-		irc_new(new_socket);
-	}
-
-	return TRUE;
-}
-
-gboolean bitlbee_shutdown(gpointer data, gint fd, b_input_condition cond)
-{
-	/* Send the message here with now=TRUE to ensure it arrives */
-	irc_write_all(TRUE, "ERROR :Closing link: BitlBee server shutting down");
-
+		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_abort(irc_connection_list->data, TRUE, NULL);
-	}
-
+	while( irc_connection_list != NULL )
+		irc_free( irc_connection_list->data );
+	
 	/* We'll only reach this point when not running in inetd mode: */
-	b_main_quit();
-
-	return FALSE;
-}
+	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;
+}
Index: bitlbee.conf
===================================================================
--- bitlbee.conf	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ bitlbee.conf	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -10,34 +10,18 @@
 ##
 ##  Inetd -- Run from inetd (default)
-##  Daemon -- Run as a stand-alone daemon, serving all users from one process.
-##    This saves memory if there are more users, the downside is that when one
-##    user hits a crash-bug, all other users will also lose their connection.
-##  ForkDaemon -- Run as a stand-alone daemon, but keep all clients in separate
-##    child processes. This should be pretty safe and reliable to use instead
-##    of inetd mode.
+##  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
 
-## User:
-## 
-## If BitlBee is started by root as a daemon, it can drop root privileges,
-## and change to the specified user.
-##
-# User = bitlbee
-
 ## DaemonPort/DaemonInterface:
 ##
-## For daemon mode, you can specify on what interface and port the daemon
-## should be listening for connections.
+## 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
-
-## ClientInterface:
-##
-## If for any reason, you want BitlBee to use a specific address/interface
-## for outgoing traffic (IM connections, HTTP(S), etc.), set it here.
-##
-# ClientInterface = 0.0.0.0
 
 ## AuthMode
@@ -48,51 +32,13 @@
 ##    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 itself.
+##    the register- and the account command until the user identifies himself.
 ##
 # AuthMode = Open
-
-## AuthBackend
-##
-## By default, the authentication data for a user is stored in the storage
-## backend. If you want to authenticate against another authentication system
-## (e.g. ldap), you can specify that here.
-##
-## Beware that this disables password changes and causes passwords for the
-## accounts people create to be stored in plain text instead of encrypted with
-## their bitlbee password.
-##
-## Currently available backends:
-##
-## - storage (internal storage)
-## - pam (Linux PAM authentication)
-## - ldap (LDAP server configured in the openldap settings)
-#
-# AuthBackend = storage
-#
 
 ## AuthPassword
 ##
 ## Password the user should enter when logging into a closed BitlBee server.
-## You can also have a BitlBee-style MD5 hash here. Format: "md5:", followed
-## by a hash as generated by "bitlbee -x hash <password>".
 ##
 # AuthPassword = ItllBeBitlBee   ## Heh.. Our slogan. ;-)
-## or
-# AuthPassword = md5:gzkK0Ox/1xh+1XTsQjXxBJ571Vgl
-
-## OperPassword
-##
-## Password that unlocks access to special operator commands.
-##
-# OperPassword = ChangeMe!
-## or
-# OperPassword = md5:I0mnZbn1t4R731zzRdDN2/pK7lRX
-
-## AllowAccountAdd
-##
-## Whether to allow registered and identified users to add new accounts using
-## 'account add'
-##
-# AllowAccountAdd 1
 
 ## HostName
@@ -100,5 +46,6 @@
 ## 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.
+## itself with that name instead. Leave it commented out if you want BitlBee to
+## use getsockname() to get a hostname.
 ##
 # HostName = localhost
@@ -142,31 +89,8 @@
 ## (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
+## Proxy = http://john:doe@proxy.localnet.com:8080
+## Proxy = socks4://socksproxy.localnet.com
+## Proxy = socks5://socksproxy.localnet.com
 
-## Protocols offered by bitlbee
-## 
-## As recompiling may be quite unpractical for some people, this option
-## allows to remove the support of protocol, even if compiled in. If
-## nothing is given, there are no restrictions.
-## 
-# Protocols = jabber yahoo
-
-## Trusted CAs
-##
-## Path to a file containing a list of trusted certificate authorities used in
-## the verification of server certificates.
-##
-## Uncomment this and make sure the file actually exists and contains all
-## certificate authorities you're willing to accept (default value should
-## work on at least Debian/Ubuntu systems with the "ca-certificates" package
-## installed). As long as the line is commented out, SSL certificate
-## verification is completely disabled.
-##
-## The location of this file may be different on other distros/OSes. For
-## example, try /etc/ssl/ca-bundle.pem on OpenSUSE.
-##
-# CAfile = /etc/ssl/certs/ca-certificates.crt
 
 [defaults]
Index: bitlbee.h
===================================================================
--- bitlbee.h	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ bitlbee.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -1,6 +1,6 @@
-/********************************************************************\
+  /********************************************************************\
   * BitlBee -- An IRC to other IM-networks gateway                     *
   *                                                                    *
-  * Copyright 2002-2013 Wilmer van der Gaast and others                *
+  * Copyright 2002-2004 Wilmer van der Gaast and others                *
   \********************************************************************/
 
@@ -20,6 +20,6 @@
   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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
 */
 
@@ -27,24 +27,13 @@
 #define _BITLBEE_H
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef _GNU_SOURCE
 #define _GNU_SOURCE /* Stupid GNU :-P */
-#endif
 
 #define PACKAGE "BitlBee"
-#define BITLBEE_VERSION "3.6"
+#define BITLBEE_VERSION "0.99"
 #define VERSION BITLBEE_VERSION
-#define BITLBEE_VER(a, b, c) (((a) << 16) + ((b) << 8) + (c))
-#define BITLBEE_VERSION_CODE BITLBEE_VER(3, 6, 0)
-#define BITLBEE_ABI_VERSION_CODE 1
 
-#define MAX_STRING 511
+#define MAX_STRING 128
 
-#if HAVE_CONFIG_H
-#include <config.h>
-#endif
+#include "config.h"
 
 #include <fcntl.h>
@@ -57,6 +46,9 @@
 #include <stdio.h>
 #include <ctype.h>
+#ifndef _WIN32
+#include <syslog.h>
 #include <errno.h>
-#include <syslog.h>
+#endif
+
 #include <glib.h>
 #include <gmodule.h>
@@ -64,59 +56,30 @@
 /* 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__
+#define free		__PLEASE_USE_THE_GLIB_MEMORY_ALLOCATION_SYSTEM_INSTEAD__
 #undef malloc
-#define malloc          __PLEASE_USE_THE_GLIB_MEMORY_ALLOCATION_SYSTEM__
+#define malloc		__PLEASE_USE_THE_GLIB_MEMORY_ALLOCATION_SYSTEM_INSTEAD__
 #undef calloc
-#define calloc          __PLEASE_USE_THE_GLIB_MEMORY_ALLOCATION_SYSTEM__
+#define calloc		__PLEASE_USE_THE_GLIB_MEMORY_ALLOCATION_SYSTEM_INSTEAD__
 #undef realloc
-#define realloc         __PLEASE_USE_THE_GLIB_MEMORY_ALLOCATION_SYSTEM__
+#define realloc		__PLEASE_USE_THE_GLIB_MEMORY_ALLOCATION_SYSTEM_INSTEAD__
 #undef strdup
-#define strdup          __PLEASE_USE_THE_GLIB_STRDUP_FUNCTIONS_SYSTEM__
+#define strdup		__PLEASE_USE_THE_GLIB_STRDUP_FUNCTIONS_SYSTEM_INSTEAD__
 #undef strndup
-#define strndup         __PLEASE_USE_THE_GLIB_STRDUP_FUNCTIONS_SYSTEM__
+#define strndup		__PLEASE_USE_THE_GLIB_STRDUP_FUNCTIONS_SYSTEM_INSTEAD__
 #undef snprintf
-#define snprintf        __PLEASE_USE_G_SNPRINTF__
+#define snprintf	__PLEASE_USE_G_SNPRINTF_INSTEAD__
 #undef strcasecmp
-#define strcasecmp      __PLEASE_USE_G_STRCASECMP__
+#define strcasecmp	__PLEASE_USE_G_STRCASECMP_INSTEAD__
 #undef strncasecmp
-#define strncasecmp     __PLEASE_USE_G_STRNCASECMP__
+#define strncasecmp	__PLEASE_USE_G_STRNCASECMP_INSTEAD__
 
-/* And the following functions shouldn't be used anymore to keep compatibility
-   with other event handling libs than GLib. */
-#undef g_timeout_add
-#define g_timeout_add           __PLEASE_USE_B_TIMEOUT_ADD__
-#undef g_timeout_add_full
-#define g_timeout_add_full      __PLEASE_USE_B_TIMEOUT_ADD__
-#undef g_io_add_watch
-#define g_io_add_watch          __PLEASE_USE_B_INPUT_ADD__
-#undef g_io_add_watch_full
-#define g_io_add_watch_full     __PLEASE_USE_B_INPUT_ADD__
-#undef g_source_remove
-#define g_source_remove         __PLEASE_USE_B_EVENT_REMOVE__
-#undef g_main_run
-#define g_main_run              __PLEASE_USE_B_MAIN_RUN__
-#undef g_main_quit
-#define g_main_quit             __PLEASE_USE_B_MAIN_QUIT__
-
-/* And now, because GLib folks think everyone loves typing ridiculously long
-   function names ... no I don't or I'd write BitlBee in Java, ffs. */
-#define g_strcasecmp g_ascii_strcasecmp
-#define g_strncasecmp g_ascii_strncasecmp
-
-/* g_memdup() deprecated as of glib 2.68.0 */
-#ifndef GLIB_VERSION_2_68
-#define g_memdup2 g_memdup
+#ifndef F_OK
+#define F_OK 0
 #endif
 
-#ifndef G_GNUC_MALLOC
-/* Doesn't exist in GLib <=2.4 while everything else in BitlBee should
-   work with it, so let's fake this one. */
-#define G_GNUC_MALLOC
-#endif
-
-#define _(x) x
+#define _( x ) x
 
 #define ROOT_NICK "root"
-#define ROOT_CHAN "&bitlbee"
+#define ROOT_CHAN "#bitlbee"
 #define ROOT_FN "User manager"
 
@@ -124,6 +87,6 @@
 
 #define DEFAULT_AWAY "Away from computer"
-#define CONTROL_TOPIC "Welcome to the control channel. Type \2help\2 for help information."
-#define IRCD_INFO PACKAGE " <https://www.bitlbee.org/>"
+#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
@@ -132,62 +95,44 @@
 #define CONF_FILE_DEF ETCDIR "bitlbee.conf"
 
-/* Hack to give a little bit more password security on IRC: If an account has
-   this password set, use /OPER to change it. */
-#define PASSWORD_PENDING "\r\rchangeme\r\r"
+extern char *CONF_FILE;
 
-#include "bee.h"
 #include "irc.h"
-#include "storage.h"
-#include "auth.h"
 #include "set.h"
-#include "nogaim.h"
+#include "protocols/nogaim.h"
 #include "commands.h"
 #include "account.h"
-#include "nick.h"
 #include "conf.h"
 #include "log.h"
 #include "ini.h"
+#include "help.h"
 #include "query.h"
+#include "debug.h"
 #include "sock.h"
-#include "misc.h"
-#include "proxy.h"
 
-typedef struct global {
-	/* In forked mode, child processes store the fd of the IPC socket here. */
+typedef struct global_t {
 	int listen_socket;
-	gint listen_watch_source_id;
-	struct help *help;
-	char *conf_file;
+	help_t *help;
 	conf_t *conf;
-	GList *storage; /* The first backend in the list will be used for saving */
-	GList *auth;    /* Authentication backends */
 	char *helpfile;
-	int restart;
+	GMainLoop *loop;
 } global_t;
 
-void sighandler_shutdown_setup(void);
+int bitlbee_daemon_init( void );
+int bitlbee_inetd_init( void );
 
-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 );
 
-gboolean bitlbee_io_current_client_read(gpointer data, gint source, b_input_condition cond);
-gboolean bitlbee_io_current_client_write(gpointer data, gint source, b_input_condition cond);
-
-void root_command_string(irc_t *irc, char *command);
-void root_command(irc_t *irc, char *command[]);
-gboolean root_command_add(const char *command, int params, void (*func)(irc_t *, char **args), int flags);
-gboolean cmd_identify_finish(gpointer data, gint fd, b_input_condition cond);
-void cmd_chat_list_finish(struct im_connection *ic);
-gboolean bitlbee_shutdown(gpointer data, gint fd, b_input_condition cond);
-
-char *set_eval_root_nick(set_t *set, char *new_nick);
-char *set_eval_control_channel(set_t *set, char *new_name);
+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;
 
-#ifdef __cplusplus
-}
 #endif
-
-#endif
-
Index: commands.c
===================================================================
--- commands.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ commands.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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 );
+}
Index: commands.h
===================================================================
--- commands.h	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ commands.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -1,3 +1,3 @@
-/********************************************************************\
+  /********************************************************************\
   * BitlBee -- An IRC to other IM-networks gateway                     *
   *                                                                    *
@@ -20,6 +20,6 @@
   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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
 */
 
@@ -29,19 +29,36 @@
 #include "bitlbee.h"
 
-typedef struct command {
+/* Hmm... Linked list? Plleeeeaaase?? ;-) */
+
+typedef struct command_t
+{
 	char *command;
 	int required_parameters;
-	void (*execute)(irc_t *, char **args);
-	int flags;
+	int (*execute)(irc_t *, char **args);
 } command_t;
 
-extern command_t root_commands[];
+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 );
 
-#define IRC_CMD_PRE_LOGIN       1
-#define IRC_CMD_LOGGED_IN       2
-#define IRC_CMD_OPER_ONLY       4
-#define IRC_CMD_TO_MASTER       8
 
-#define IPC_CMD_TO_CHILDREN     1
+
+extern command_t commands[];
 
 #endif
Index: conf.c
===================================================================
--- conf.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ conf.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -1,6 +1,6 @@
-/********************************************************************\
+  /********************************************************************\
   * BitlBee -- An IRC to other IM-networks gateway                     *
   *                                                                    *
-  * Copyright 2002-2005 Wilmer van der Gaast and others                *
+  * Copyright 2002-2004 Wilmer van der Gaast and others                *
   \********************************************************************/
 
@@ -20,6 +20,6 @@
   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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
 */
 
@@ -32,363 +32,250 @@
 #include "ini.h"
 #include "url.h"
-#include "ipc.h"
-
-#include "proxy.h"
-
-static int conf_loadini(conf_t *conf, char *file);
-static void conf_free(conf_t *conf);
-
-conf_t *conf_load(int argc, char *argv[])
+
+#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, config_missing = 0;
-	int version_happened = 0;
-
-	conf = g_new0(conf_t, 1);
-
-	conf->iface_in = NULL;
-	conf->iface_out = NULL;
-	conf->port = g_strdup("6667");
+	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->primary_storage = g_strdup("xml");
-	conf->migrate_storage = g_strsplit("text", ",", -1);
 	conf->runmode = RUNMODE_INETD;
 	conf->authmode = AUTHMODE_OPEN;
-	conf->auth_backend = NULL;
-	conf->auth_pass = NULL;
-	conf->oper_pass = NULL;
-	conf->allow_account_add = 1;
-	conf->configdir = g_strdup(CONFIG);
-	conf->plugindir = g_strdup(PLUGINDIR);
-	conf->pidfile = g_strdup(PIDFILE);
-	conf->motdfile = g_strdup(ETCDIR "/motd.txt");
+	conf->password = NULL;
+	conf->configdir = g_strdup( CONFIG );
+	conf->motdfile = g_strdup( ETCDIR "/motd.txt" );
 	conf->ping_interval = 180;
 	conf->ping_timeout = 300;
-	conf->user = NULL;
-	conf->ft_max_size = SIZE_MAX;
-	conf->ft_max_kbps = G_MAXUINT;
-	conf->ft_listen = NULL;
-	conf->protocols = NULL;
-	conf->cafile = NULL;
-	proxytype = 0;
-
-	i = conf_loadini(conf, global.conf_file);
-	if (i == 0) {
-		fprintf(stderr, "Error: Syntax error in configuration file `%s'.\n", global.conf_file);
-		conf_free(conf);
-		return NULL;
-	} else if (i == -1) {
-		config_missing++;
-		/* Whine after parsing the options if there was no -c pointing
-		   at a *valid* configuration file. */
-	}
-
-	while (argc > 0 && (opt = getopt(argc, argv, "i:p:P:nvIDFc:d:hu:V")) >= 0) {
-		/*     ^^^^ Just to make sure we skip this step from the REHASH handler. */
-		if (opt == 'i') {
-			conf->iface_in = g_strdup(optarg);
-		} else if (opt == 'p') {
-			g_free(conf->port);
-			conf->port = g_strdup(optarg);
-		} else if (opt == 'P') {
-			g_free(conf->pidfile);
-			conf->pidfile = g_strdup(optarg);
-		} 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 == 'F') {
-			conf->runmode = RUNMODE_FORKDAEMON;
-		} else if (opt == 'c') {
-			if (strcmp(global.conf_file, optarg) != 0) {
-				g_free(global.conf_file);
-				global.conf_file = g_strdup(optarg);
-				conf_free(conf);
-				/* Re-evaluate arguments. Don't use this option twice,
-				   you'll end up in an infinite loop! Hope this trick
-				   works with all libcs BTW.. */
-				optind = 1;
-				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/-F [-i <interface>] [-p <port>] [-n] [-v]] [-I]\n"
-			       "               [-c <file>] [-d <dir>] [-x] [-h]\n"
-			       "\n"
-			       "An IRC-to-other-chat-networks gateway\n"
-			       "\n"
-			       "  -I  Classic/InetD mode. (Default)\n"
-			       "  -D  Daemon mode. (one process serves all)\n"
-			       "  -F  Forking daemon. (one process per client)\n"
-			       "  -u  Run daemon as specified user.\n"
-			       "  -P  Specify PID-file (not for inetd mode)\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"
-			       "  -x  Command-line interface to password encryption/hashing\n"
-			       "  -h  Show this help page.\n"
-			       "  -V  Show version info.\n");
-			conf_free(conf);
-			return NULL;
-		} else if (opt == 'V') {
-			printf("BitlBee %s\n", BITLBEE_VERSION);
-			/* the rest of the version string is displayed below, for ld -vvv compatibility*/
-			version_happened = TRUE;
-		} else if (opt == 'u') {
-			g_free(conf->user);
-			conf->user = g_strdup(optarg);
-		}
-	}
-
-	if (version_happened) {
-		printf("API version %06x\nConfigure args: %s\n",
-		       BITLBEE_VERSION_CODE, BITLBEE_CONFIGURE_ARGS);
-		conf_free(conf);
-		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);
+	
+	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;
 	}
-
-	if (config_missing) {
-		fprintf(stderr, "Warning: Unable to read configuration file `%s'.\n", global.conf_file);
-	}
-
-	if (conf->cafile && access(conf->cafile, R_OK) != 0) {
-		/* Let's treat this as a serious problem so people won't think
-		   they're secure when in fact they're not. */
-		fprintf(stderr, "Error: Could not read CA file %s: %s\n", conf->cafile, strerror(errno));
-		conf_free(conf);
-		return NULL;
-	}
-
-	return conf;
+	
+	return( conf );
 }
 
-static void conf_free(conf_t *conf)
-{
-	/* Free software means users have the four essential freedoms:
-	   0. to run the program,
-	   2. to study and change the program in source code form,
-	   2. to redistribute exact copies, and
-	   3. to distribute modified versions
-	*/
-	g_free(conf->auth_pass);
-	g_free(conf->cafile);
-	g_free(conf->configdir);
-	g_free(conf->ft_listen);
-	g_free(conf->hostname);
-	g_free(conf->iface_in);
-	g_free(conf->iface_out);
-	g_free(conf->motdfile);
-	g_free(conf->oper_pass);
-	g_free(conf->pidfile);
-	g_free(conf->plugindir);
-	g_free(conf->port);
-	g_free(conf->primary_storage);
-	g_free(conf->user);
-	g_strfreev(conf->migrate_storage);
-	g_strfreev(conf->protocols);
-	g_free(conf);
-
-}
-
-static int conf_loadini(conf_t *conf, char *file)
+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) {
+	
+	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 if (g_strcasecmp(ini->value, "forkdaemon") == 0) {
-					conf->runmode = RUNMODE_FORKDAEMON;
-				} else {
+				else
 					conf->runmode = RUNMODE_INETD;
-				}
-			} else if (g_strcasecmp(ini->key, "pidfile") == 0) {
-				g_free(conf->pidfile);
-				conf->pidfile = g_strdup(ini->value);
-			} else if (g_strcasecmp(ini->key, "daemoninterface") == 0) {
-				g_free(conf->iface_in);
-				conf->iface_in = g_strdup(ini->value);
-			} else if (g_strcasecmp(ini->key, "daemonport") == 0) {
-				g_free(conf->port);
-				conf->port = g_strdup(ini->value);
-			} else if (g_strcasecmp(ini->key, "clientinterface") == 0) {
-				g_free(conf->iface_out);
-				conf->iface_out = g_strdup(ini->value);
-			} else if (g_strcasecmp(ini->key, "authmode") == 0) {
-				if (g_strcasecmp(ini->value, "registered") == 0) {
+			}
+			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) {
+				else if( g_strcasecmp( ini->value, "closed" ) == 0 )
 					conf->authmode = AUTHMODE_CLOSED;
-				} else {
+				else
 					conf->authmode = AUTHMODE_OPEN;
-				}
-			} else if (g_strcasecmp(ini->key, "authbackend") == 0) {
-				if (g_strcasecmp(ini->value, "storage") == 0) {
-					conf->auth_backend = NULL;
-				} else if (g_strcasecmp(ini->value, "pam") == 0 ||
-				         g_strcasecmp(ini->value, "ldap") == 0) {
-					g_free(conf->auth_backend);
-					conf->auth_backend = g_strdup(ini->value);
-				} else {
-					fprintf(stderr, "Invalid %s value: %s\n", ini->key, ini->value);
-					return 0;
-				}
-			} else if (g_strcasecmp(ini->key, "authpassword") == 0) {
-				g_free(conf->auth_pass);
-				conf->auth_pass = g_strdup(ini->value);
-			} else if (g_strcasecmp(ini->key, "operpassword") == 0) {
-				g_free(conf->oper_pass);
-				conf->oper_pass = g_strdup(ini->value);
-			} else if (g_strcasecmp(ini->key, "allowaccountadd") == 0) {
-				if (!is_bool(ini->value)) {
-					fprintf(stderr, "Invalid %s value: %s\n", ini->key, ini->value);
-					return 0;
-				}
-				conf->allow_account_add = bool2int(ini->value);
-			} else if (g_strcasecmp(ini->key, "hostname") == 0) {
-				g_free(conf->hostname);
-				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, "plugindir") == 0) {
-				g_free(conf->plugindir);
-				conf->plugindir = 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, "accountstorage") == 0 ||
-				   g_strcasecmp(ini->key, "account_storage") == 0) {
-				g_free(conf->primary_storage);
-				conf->primary_storage = g_strdup(ini->value);
-			} else if (g_strcasecmp(ini->key, "accountstoragemigrate") == 0 ||
-				   g_strcasecmp(ini->key, "account_storage_migrate") == 0) {
-				g_strfreev(conf->migrate_storage);
-				conf->migrate_storage = g_strsplit_set(ini->value, " \t,;", -1);
-			} 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;
+			}
+			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;
+			}
+			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));
+			}
+			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) {
+				if( url->proto == PROTO_HTTP )
 					proxytype = PROXY_HTTP;
-				} else if (url->proto == PROTO_SOCKS4) {
+				else if( url->proto == PROTO_SOCKS4 )
 					proxytype = PROXY_SOCKS4;
-				} else if (url->proto == PROTO_SOCKS5) {
+				else if( url->proto == PROTO_SOCKS5 )
 					proxytype = PROXY_SOCKS5;
-				} else if (url->proto == PROTO_SOCKS4A) {
-					proxytype = PROXY_SOCKS4A;
-				}
-
-				g_free(url);
-			} else if (g_strcasecmp(ini->key, "user") == 0) {
-				g_free(conf->user);
-				conf->user = g_strdup(ini->value);
-			} else if (g_strcasecmp(ini->key, "ft_max_size") == 0) {
-				size_t ft_max_size;
-				if (sscanf(ini->value, "%zu", &ft_max_size) != 1) {
-					fprintf(stderr, "Invalid %s value: %s\n", ini->key, ini->value);
-					return 0;
-				}
-				conf->ft_max_size = ft_max_size;
-			} else if (g_strcasecmp(ini->key, "ft_max_kbps") == 0) {
-				if (sscanf(ini->value, "%d", &i) != 1) {
-					fprintf(stderr, "Invalid %s value: %s\n", ini->key, ini->value);
-					return 0;
-				}
-				conf->ft_max_kbps = i;
-			} else if (g_strcasecmp(ini->key, "ft_listen") == 0) {
-				g_free(conf->ft_listen);
-				conf->ft_listen = g_strdup(ini->value);
-			} else if (g_strcasecmp(ini->key, "protocols") == 0) {
-				g_strfreev(conf->protocols);
-				conf->protocols = g_strsplit_set(ini->value, " \t,;", -1);
-			} else if (g_strcasecmp(ini->key, "cafile") == 0) {
-				g_free(conf->cafile);
-				conf->cafile = g_strdup(ini->value);
-			} else {
-				fprintf(stderr, "Error: Unknown setting `%s` in configuration file (line %d).\n",
-				        ini->key, ini->line);
-				return 0;
+				
+				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 (line %d). "
-			        "BitlBee configuration must be put in a [settings] section!\n", ini->section,
-			        ini->line);
-			return 0;
-		}
-	}
-	ini_close(ini);
-
-	return 1;
+		}
+		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)
+void conf_loaddefaults( irc_t *irc )
 {
 	ini_t *ini;
-
-	ini = ini_open(global.conf_file);
-	if (ini == NULL) {
-		return;
-	}
-	while (ini_read(ini)) {
-		if (g_strcasecmp(ini->section, "defaults") == 0) {
-			set_t *s = set_find(&irc->b->set, ini->key);
-
-			if (s) {
-				if (s->def) {
-					g_free(s->def);
-				}
-				s->def = g_strdup(ini->value);
-			}
-		}
-	}
-	ini_close(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 );
 }
Index: conf.h
===================================================================
--- conf.h	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ conf.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -1,3 +1,3 @@
-/********************************************************************\
+  /********************************************************************\
   * BitlBee -- An IRC to other IM-networks gateway                     *
   *                                                                    *
@@ -20,6 +20,6 @@
   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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
 */
 
@@ -27,37 +27,25 @@
 #define __CONF_H
 
-typedef enum runmode { RUNMODE_DAEMON, RUNMODE_FORKDAEMON, RUNMODE_INETD } runmode_t;
+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_in, *iface_out;
-	char *port;
+typedef struct conf
+{
+	char *iface;
+	signed int port;
 	int nofork;
 	int verbose;
 	runmode_t runmode;
 	authmode_t authmode;
-	char *auth_backend;
-	char *auth_pass;
-	char *oper_pass;
-	int allow_account_add;
+	char *password;
 	char *hostname;
 	char *configdir;
-	char *plugindir;
-	char *pidfile;
 	char *motdfile;
-	char *primary_storage;
-	char **migrate_storage;
 	int ping_interval;
 	int ping_timeout;
-	char *user;
-	size_t ft_max_size;
-	int ft_max_kbps;
-	char *ft_listen;
-	char **protocols;
-	char *cafile;
 } conf_t;
 
-G_GNUC_MALLOC conf_t *conf_load(int argc, char *argv[]);
-void conf_loaddefaults(irc_t *irc);
+conf_t *conf_load( int argc, char *argv[] );
+void conf_loaddefaults( irc_t *irc );
 
 #endif
Index: configure
===================================================================
--- configure	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ configure	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -8,113 +8,29 @@
 ##############################
 
-# shellcheck disable=SC2016,SC2164,SC2181
 prefix='/usr/local/'
-bindir='$prefix/bin/'
-sbindir='$prefix/sbin/'
+bindir='$prefix/sbin/'
 etcdir='$prefix/etc/bitlbee/'
 mandir='$prefix/share/man/'
 datadir='$prefix/share/bitlbee/'
 config='/var/lib/bitlbee/'
-libdir='$prefix/lib/'
-plugindir='$prefix/lib/bitlbee/'
-includedir='$prefix/include/bitlbee/'
-systemdsystemunitdir=''
-sysusersdir=''
-libevent='/usr/'
-pidfile='/var/run/bitlbee.pid'
-ipcsocket=''
-pcdir='$prefix/lib/pkgconfig'
-systemlibdirs="/lib64 /usr/lib64 /usr/local/lib64 /lib /usr/lib /usr/local/lib"
-sysroot=''
-
-configure_args="$*"
-
-# Set these to default-on to let it be overriden by either the user or purple
-#
-# If the user sets one of these to 1, purple won't disable them.
-# Otherwise, if it's still default-on, it gets included in normal builds,
-# but not purple ones.
-jabber="default-on"
-
-twitter=1
-purple=0
-
-verbose=0
-doc=1
+
+msn=1
+jabber=1
+oscar=1
+yahoo=1
+
 debug=0
-strip=0
-gcov=0
-asan=0
-plugins=1
-otr=0
-
-events=glib
-external_json_parser=auto
+strip=1
+flood=0
+ipv6=1
 ssl=auto
 
-pam=0
-ldap=0
-
-pie=1
-
-systemd=auto
-
-arch=$(uname -s)
-arch_rel=$(uname -r)
-
-GLIB_MIN_VERSION=2.32
-
-# Cygwin, Darwin and SunOS < 5.11 do not support PIC/PIE
-case "$arch" in
-	CYGWIN* )
-		pie=0;;
-	Darwin )
-		pie=0;;
-	SunOS )
-		if [ "$arch_rel" = "5.10" ]; then
-			pie=0
-		fi
-		;;
-esac
-
-get_version() {
-	REAL_BITLBEE_VERSION=$(grep '^#define BITLBEE_VERSION ' "$srcdir/bitlbee.h" | sed 's/.*\"\(.*\)\".*/\1/')
-
-	if [ -n "$BITLBEE_VERSION" ]; then
-		# environment variable already set to something to spoof it
-		# don't replace it with the git stuff
-		return
-	fi
-
-	BITLBEE_VERSION=$REAL_BITLBEE_VERSION
-
-	if [ -d "$srcdir/.git" ] && type git > /dev/null 2> /dev/null; then
-		timestamp=$(cd "$srcdir"; git show -s --format=%ci HEAD | sed 's/ .*$//; s/-//g')
-		branch=$(cd "$srcdir"; git rev-parse --abbrev-ref HEAD)
-
-		search='\(.*\)-\([0-9]*\)-\(g[0-9a-f]*\)'
-		replace="\1+$timestamp+$branch+\2-\3-git"
-
-		if ! (cd "$srcdir"; git describe --long --tags 2>/dev/null); then
-			describe=${REAL_BITLBEE_VERSION}-0-g$(cd "$srcdir"; git rev-parse --short HEAD)
-		fi
-
-		BITLBEE_VERSION=$(echo "$describe" | sed "s#$search#$replace#")
-
-		unset timestamp branch search replace describe
-	fi
-}
-
-if [ "$1" = "--dump-version" ]; then
-	srcdir=$(cd "$(dirname "$0")"; pwd)
-	get_version
-	echo "$BITLBEE_VERSION"
-	exit
-fi
+arch=`uname -s`
+cpu=`uname -m`
 
 echo BitlBee configure
 
 while [ -n "$1" ]; do
-	e="$(expr "X$1" : 'X--\(.*=.*\)')"
+	e="`expr "X$1" : 'X--\(.*=.*\)'`"
 	if [ -z "$e" ]; then
 		cat<<EOF
@@ -126,620 +42,221 @@
 --prefix=...	Directories to put files in		$prefix
 --bindir=...						$bindir
---sbindir=...						$sbindir
 --etcdir=...						$etcdir
 --mandir=...						$mandir
 --datadir=...						$datadir
---plugindir=...						$plugindir
---systemdsystemunitdir=...				$systemdsystemunitdir
---sysusersdir=...					$sysusersdir
---pidfile=...						$pidfile
 --config=...						$config
 
---verbose=0/1	Disable/enable verbose build		$verbose
-
+--msn=0/1	Disable/enable MSN part			$msn
 --jabber=0/1	Disable/enable Jabber part		$jabber
---twitter=0/1	Disable/enable Twitter part		$twitter
-
---purple=0/1	Disable/enable libpurple support	$purple
-		(automatically disables other protocol modules)
-
---pam=0/1	Disable/enable PAM authentication	$pam
---ldap=0/1	Disable/enable LDAP authentication	$ldap
-
---doc=0/1	Disable/enable help.txt generation	$doc
+--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
---pie=0/1       Build position independent executable   $pie
---gcov=0/1	Disable/enable test coverage reporting	$gcov
---asan=0/1	Disable/enable AddressSanitizer		$asan
---plugins=0/1	Disable/enable plugins support		$plugins
---otr=0/1/auto/plugin
-		Disable/enable OTR encryption support	$otr
-
---events=...	Event handler (glib, libevent)		$events
---ssl=...	SSL library to use (gnutls, nss, openssl, auto)
+
+--flood=0/1	Flood protection			$flood
+--ipv6=0/1	IPv6 socket support			$ipv6
+
+--ssl=...	SSL library to use (gnutls, nss, openssl, bogus, auto)
 							$ssl
---external_json_parser=0/1/auto	Use External JSON parser $external_json_parser
---systemd=0/1	Enable/disable systemd 			$systemd
-
-
---target=...	Cross compilation target 		same as host
---sysroot=...	Cross compilation sysroot 		$sysroot
-EOF
-		exit
+EOF
+		exit;
 	fi
 	eval "$e"
-	shift
+	shift;
 done
 
 # Expand $prefix and get rid of double slashes
-bindir=$(eval echo "$bindir/" | sed 's/\/\{1,\}/\//g')
-sbindir=$(eval echo "$sbindir/" | 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')
-libdir=$(eval echo "$libdir/" | sed 's/\/\{1,\}/\//g')
-plugindir=$(eval echo "$plugindir/" | sed 's/\/\{1,\}/\//g')
-includedir=$(eval echo "$includedir"/ | sed 's/\/\{1,\}/\//g')
-libevent=$(eval echo "$libevent"/ | sed 's/\/\{1,\}/\//g')
-
-pidfile=$(eval echo "$pidfile" | sed 's/\/\{1,\}/\//g')
-ipcsocket=$(eval echo "$ipcsocket" | sed 's/\/\{1,\}/\//g')
-pcdir=$(eval echo "$pcdir" | sed 's/\/\{1,\}/\//g')
-
-protocols_mods=""
-
-cat <<EOF >Makefile.settings
+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
-
-# ./configure $configure_args
-
 PREFIX=$prefix
 BINDIR=$bindir
-SBINDIR=$sbindir
 ETCDIR=$etcdir
 MANDIR=$mandir
 DATADIR=$datadir
-PLUGINDIR=$plugindir
 CONFIG=$config
-LIBDIR=$libdir
-INCLUDEDIR=$includedir
-PCDIR=$pcdir
-
-TARGET=$target
-
-INSTALL=install -p
+
+ARCH=$arch
+CPU=$cpu
+OUTFILE=bitlbee
 
 DESTDIR=
 LFLAGS=
-EFLAGS=-lm
-EOF
-
-srcdir=$(cd "$(dirname "$0")"; pwd)
-currdir=$(pwd)
-if [ "$srcdir" != "$currdir" ]; then
-	echo
-	echo "configure script run from a different directory. Will create some symlinks..."
-	if [ ! -e Makefile ] || [ -L Makefile ]; then
-		COPYDIRS="doc lib protocols tests utils"
-		# shellcheck disable=SC2046,SC2086
-		mkdir -p $(cd "$srcdir"; find $COPYDIRS -type d)
-		find . -name Makefile -type l -print0 | xargs -0 rm 2> /dev/null
-		dst="$PWD"
-		cd "$srcdir"
-		# shellcheck disable=SC2044
-		for i in $(find . -name Makefile -type f); do
-			ln -s "$PWD${i#.}" "$dst/$i"
-		done
-		cd "$dst"
-		rm -rf .bzr
-	fi
-	
-	echo "_SRCDIR_=$srcdir/" >> Makefile.settings
-	CFLAGS="$CFLAGS -I${dst}"
-else
-	srcdir=$PWD
-fi
-
-cat<<EOF >config.h
+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 BITLBEE_CONFIGURE_ARGS "$configure_args"
 
 #define CONFIG "$config"
 #define ETCDIR "$etcdir"
 #define VARDIR "$datadir"
-#define PLUGINDIR "$plugindir"
-#define DATADIR "$datadir"
-#define PIDFILE "$pidfile"
-#define IPCSOCKET "$ipcsocket"
-EOF
-
-
-
-if [ -n "$target" ]; then
-	# prepend sysroot to system lib dirs
-
-	systemlibdirs_cross=''
-	for i in $systemlibdirs; do
-		systemlibdirs_cross="$systemlibdirs_cross $sysroot$i"
-	done
-	systemlibdirs=$systemlibdirs_cross
-	unset systemlibdirs_cross
-
-	# backward compatibility
-
-	if [ -z "$PKG_CONFIG_LIBDIR" ]; then
-		PKG_CONFIG_LIBDIR="/usr/$target/lib/pkgconfig"
-		export PKG_CONFIG_LIBDIR
-	fi
-
-	if [ -d "/usr/$target/bin" ]; then
-		PATH="/usr/$target/bin:$PATH"
-	fi
-
-	if [ -d "/usr/$target/lib" ]; then
-		systemlibdirs="$systemlibdirs /usr/$target/lib"
-	fi
-
-	CC=$target-cc
-	LD=$target-ld
-	STRIP=$target-strip
-fi
-
-if [ "$asan" = "1" ]; then
-	CFLAGS="$CFLAGS -fsanitize=address"
-	LDFLAGS="$LDFLAGS -fsanitize=address"
-	debug=1
-fi
-
-if [ "$verbose" = "0" ]; then
-	echo 'VERBOSE=@' >> Makefile.settings
-else
-	echo 'VERBOSE=' >> Makefile.settings
-fi
-
-cat <<EOF >>Makefile.settings
-
-# Enable/disable output verbosity
-ifdef V
-ifeq (\$(V),1)
-	VERBOSE=
-else
-	VERBOSE=@
-endif
-endif
-
-EOF
+#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
-	CFLAGS="$CFLAGS -g3 -DDEBUG -O0"
-else
-	[ -z "$CFLAGS" ] && CFLAGS="-g -O2"
-fi
-
-if [ "$pie" = "1" ]; then
-	echo 'CFLAGS_BITLBEE=-fPIE' >> Makefile.settings
-	echo 'LDFLAGS_BITLBEE=-pie' >> Makefile.settings
-fi
-
-cat<<EOF >>Makefile.settings
-LDFLAGS=$LDFLAGS
-CFLAGS=$CFLAGS $CPPFLAGS
-CFLAGS+=-DHAVE_CONFIG_H -D_GNU_SOURCE
-EOF
+	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
-	CC=gcc
+	echo "CC=gcc" >> Makefile.settings;
 elif type cc > /dev/null 2> /dev/null; then
-	CC=cc
+	echo "CC=cc" >> Makefile.settings;
 else
 	echo 'Cannot find a C compiler, aborting.'
-	exit 1
-fi
-
-echo "CC=$CC" >> Makefile.settings
-if echo "$CC" | grep -qw 'gcc\|clang'; then
-	# Apparently -Wall is gcc-specific?
-	echo 'CFLAGS+=-Wall -Wformat -Werror=format-security' >> Makefile.settings
-fi
-
-if [ -z "$LD" ]; then
-	if type ld > /dev/null 2> /dev/null; then
-		LD=ld
+	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
-		echo 'Cannot find ld, aborting.'
-		exit 1
-	fi
-fi
-
-echo "LD=$LD" >> Makefile.settings
-
-if [ -z "$PKG_CONFIG" ]; then
-	PKG_CONFIG=pkg-config
-fi
-
-if ! $PKG_CONFIG --version > /dev/null 2>/dev/null; then
-	echo
-	echo 'Cannot find pkg-config, aborting.'
-	exit 1
-fi
-
-if $PKG_CONFIG glib-2.0; then
-	if $PKG_CONFIG glib-2.0 --atleast-version=$GLIB_MIN_VERSION; then
-		cat<<EOF >>Makefile.settings
-EFLAGS+=$($PKG_CONFIG --libs glib-2.0 gmodule-2.0)
-CFLAGS+=$($PKG_CONFIG --cflags glib-2.0 gmodule-2.0)
-EOF
+		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
-		echo
-		echo "Found glib2 $($PKG_CONFIG glib-2.0 --modversion), but version $GLIB_MIN_VERSION or newer is required."
-		exit 1
-	fi
-else
-	echo
-	echo 'Cannot find glib2 development libraries, aborting. (Install libglib2-dev?)'
-	exit 1
-fi
-
-if [ "$events" = "libevent" ]; then
-	if ! [ -f "${libevent}include/event.h" ]; then
-		echo
-		echo 'Warning: Could not find event.h, you might have to install it and/or specify'
-		echo 'its location using the --libevent= argument. (Example: If event.h is in'
-		echo '/usr/local/include and binaries are in /usr/local/lib: --libevent=/usr/local)'
+		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
 	
-	echo '#define EVENTS_LIBEVENT' >> config.h
-	cat <<EOF >>Makefile.settings
-EFLAGS+=-levent -L${libevent}lib
-CFLAGS+=-I${libevent}include
-EOF
-elif [ "$events" = "glib" ]; then
-	## We already use glib anyway, so this is all we need (and in fact not even this, but just to be sure...):
-	echo '#define EVENTS_GLIB' >> config.h
-else
-	echo
-	echo 'ERROR: Unknown event handler specified.'
-	exit 1
-fi
-echo 'EVENT_HANDLER=events_'$events'.o' >> Makefile.settings
-
-if [ "$external_json_parser" = "auto" ]; then
-	if pkg-config --exists json-parser; then
-		external_json_parser=1
-	else
-		external_json_parser=0
-	fi
-fi
-echo "EXTERNAL_JSON_PARSER=$external_json_parser" >> Makefile.settings
-if [ "$external_json_parser" = "1" ]; then
-	cat <<EOF >>Makefile.settings
-CFLAGS+=$(pkg-config --cflags json-parser)
-CFLAGS+=-DUSE_EXTERNAL_JSON_PARSER
-LDFLAGS_BITLBEE+=$(pkg-config --libs json-parser)
-LDFLAGS_TESTS+=$(pkg-config --libs json-parser)
-EOF
-fi
-
-echo CFLAGS+=-I"${srcdir}" -I"${srcdir}"/lib -I"${srcdir}"/protocols -I. >> Makefile.settings
-
-detect_gnutls()
-{
-	if $PKG_CONFIG --exists gnutls; then
-		cat <<EOF >>Makefile.settings
-EFLAGS+=$($PKG_CONFIG --libs gnutls) $(libgcrypt-config --libs)
-CFLAGS+=$($PKG_CONFIG --cflags gnutls) $(libgcrypt-config --cflags)
-EOF
-		ssl=gnutls
-		if ! $PKG_CONFIG gnutls --atleast-version=2.8; then
-			echo
-			echo 'Warning: With GnuTLS versions <2.8, certificate expire dates are not verified.'
-		fi
-		ret=1
-	elif libgnutls-config --version > /dev/null 2> /dev/null; then
-		cat <<EOF >>Makefile.settings
-EFLAGS+=$(libgnutls-config --libs) $(libgcrypt-config --libs)
-CFLAGS+=$(libgnutls-config --cflags) $(libgcrypt-config --cflags)
-EOF
-		
-		ssl=gnutls
-		ret=1
-	else
-		ret=0
-	fi
-}
-
-detect_nss()
-{
-	if $PKG_CONFIG --version > /dev/null 2>/dev/null && $PKG_CONFIG nss; then
-		cat<<EOF >>Makefile.settings
-EFLAGS+=$($PKG_CONFIG --libs nss)
-CFLAGS+=$($PKG_CONFIG --cflags nss)
-EOF
-		
-		ssl=nss
-		ret=1
-	else
-		ret=0
-	fi
-}
-
-RESOLV_TESTCODE='
-#include <sys/types.h>
-#include <netinet/in.h>
-#include <arpa/nameser.h>
-#include <resolv.h>
-
-int main()
-{
-
-	res_query( NULL, 0, 0, NULL, 0);
-	dn_expand( NULL, NULL, NULL, NULL, 0);
-	dn_skipname( NULL, NULL);
-}
-'
-RESOLV_NS_TESTCODE='
-#include <sys/types.h>
-#include <netinet/in.h>
-#include <arpa/nameser.h>
-#include <resolv.h>
-
-int main()
-{
-	ns_initparse( NULL, 0, NULL );
-	ns_parserr( NULL, ns_s_an, 0, NULL );
-}
-'
-RESOLV_NS_TYPES_TESTCODE='
-#include <sys/types.h>
-#include <netinet/in.h>
-#include <arpa/nameser.h>
-
-int main()
-{
-	ns_msg nsh;
-	ns_rr rr;
-
-	/* Not all platforms we want to work on have
-	 ns_* routines, so use this to make sure
-	 the compiler uses it.*/
-	return (int)(sizeof(nsh) + sizeof(rr));
-}
-'
-
-BACKTRACE_TESTCODE='
-#include <execinfo.h>
-
-int main()
-{
-	void *trace[16];
-	return backtrace(trace, 16);
-}
-'
-
-detect_resolv_dynamic()
-{
-	case "$arch" in
-	OpenBSD )
-		# In FreeBSD res_*/dn_* routines are present in libc.so
-		LIBRESOLV=;;
-	FreeBSD )
-		# In FreeBSD res_*/dn_* routines are present in libc.so
-		LIBRESOLV=;;
-	CYGWIN* )
-		# In Cygwin res_*/dn_* routines are present in libc.so
-		LIBRESOLV=;;
-	* )
-		LIBRESOLV=-lresolv;;
-	esac
-	TMPFILE=$(mktemp /tmp/bitlbee-configure.XXXXXX)
-	ret=1
-	echo "$RESOLV_TESTCODE" | $CC -o "$TMPFILE" -x c - $LIBRESOLV >/dev/null 2>/dev/null
-	if [ "$?" = "0" ]; then
-		echo "EFLAGS+=$LIBRESOLV" >> Makefile.settings
-		ret=0
-	fi
-
-	rm -f "$TMPFILE"
-	return $ret
-}
-
-detect_resolv_static()
-{
-	TMPFILE=$(mktemp /tmp/bitlbee-configure.XXXXXX)
-	ret=1
-	for i in $systemlibdirs; do
-		if [ -f "$i/libresolv.a" ]; then
-			echo "$RESOLV_TESTCODE" | $CC -o "$TMPFILE" -x c - -Wl,"$i/libresolv.a" >/dev/null 2>/dev/null
-			if [ "$?" = "0" ]; then
-				echo "EFLAGS+=$i/libresolv.a" >> Makefile.settings
-				ret=0
-			fi
-		fi
-	done
-
-	rm -f "$TMPFILE"
-	return $ret
-}
-
-detect_resolv_ns_dynamic()
-{
-	case "$arch" in
-	FreeBSD )
-		# In FreeBSD ns_ routines are present in libc.so
-		LIBRESOLV=;;
-	* )
-		LIBRESOLV=-lresolv;;
-	esac
-	TMPFILE=$(mktemp /tmp/bitlbee-configure.XXXXXX)
-	ret=1
-	echo "$RESOLV_NS_TESTCODE" | $CC -o "$TMPFILE" -x c - $LIBRESOLV >/dev/null 2>/dev/null
-	if [ "$?" = "0" ]; then
-		ret=0
-	fi
-
-	rm -f "$TMPFILE"
-	return $ret
-}
-
-detect_resolv_ns_static()
-{
-	TMPFILE=$(mktemp /tmp/bitlbee-configure.XXXXXX)
-	ret=1
-	for i in $systemlibdirs; do
-		if [ -f "$i/libresolv.a" ]; then
-			echo "$RESOLV_NS_TESTCODE" | $CC -o "$TMPFILE" -x c - -Wl,"$i/libresolv.a" >/dev/null 2>/dev/null
-			if [ "$?" = "0" ]; then
-				ret=0
-			fi
-		fi
-	done
-
-	rm -f "$TMPFILE"
-	return $ret
-}
-
-detect_nameser_has_ns_types()
-{
-	TMPFILE=$(mktemp /tmp/bitlbee-configure.XXXXXX)
-	ret=1
-	# since we aren't actually linking with ns_* routines
-	# we can just compile the test code
-	echo "$RESOLV_NS_TYPES_TESTCODE" | $CC -o "$TMPFILE" -x c - >/dev/null 2>/dev/null
-	if [ "$?" = "0" ]; then
-		ret=0
-	fi
-
-	rm -f "$TMPFILE"
-	return $ret
-}
-
-detect_backtrace()
-{
-	TMPFILE=$(mktemp /tmp/bitlbee-configure.XXXXXX)
-	ret=1
-	echo "$BACKTRACE_TESTCODE" | $CC -o "$TMPFILE" -x c - >/dev/null 2>/dev/null
-	if [ "$?" = "0" ]; then
-		ret=0
-	fi
-
-	rm -f "$TMPFILE"
-	return $ret
-}
-
-if [ "$ssl" = "auto" ]; then
-	detect_gnutls
 	if [ "$ret" = "0" ]; then
-		# Disable NSS for now as it's known to not work very well ATM.
-		#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 'installation 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 is recommended and better supported'
-	echo 'by us. However, on many BSD machines, OpenSSL can be considered part of the'
-	echo '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 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
+		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 'EFLAGS+=-lssl -lcrypto' >> Makefile.settings
-	
-	ret=1
-else
-	echo
-	echo 'ERROR: Unknown SSL library specified.'
-	exit 1
-fi
-
-if [ "$ret" = "0" ]; then
-	echo
-	echo 'ERROR: Could not find a suitable SSL library (GnuTLS, libnss or OpenSSL).'
-	echo '       Please note that this script doesn'\''t have detection code for OpenSSL,'
-	echo '       so if you want to use that, you have to select it by hand.'
-	
-	exit 1
-fi
-
-echo 'SSL_CLIENT=ssl_'$ssl'.o' >> Makefile.settings
-
-if detect_nameser_has_ns_types; then
-	echo '#define NAMESER_HAS_NS_TYPES' >> config.h
-fi
-if detect_resolv_dynamic || detect_resolv_static; then
-	echo '#define HAVE_RESOLV_A' >> config.h
-	if detect_resolv_ns_dynamic || detect_resolv_ns_static; then
-		echo '#define HAVE_RESOLV_A_WITH_NS' >> config.h
-	fi
-else
-	echo 'Insufficient resolv routines. Jabber server must be set explicitly'
-fi
-
-if detect_backtrace; then
-	echo '#define HAVE_BACKTRACE' >> config.h
-	echo '#define CRASHFILE "'"$config"'crash.log"' >> config.h
-fi
-
-STORAGES="xml"
-
-for i in $STORAGES; do
-	STORAGE_OBJS="$STORAGE_OBJS storage_$i.o"
-done
-echo "STORAGE_OBJS=$STORAGE_OBJS" >> Makefile.settings
-
-authobjs=
-authlibs=
-if [ "$pam" = 0 ]; then
-	echo '#undef WITH_PAM' >> config.h
-else
-	if ! echo '#include <security/pam_appl.h>' | $CC -E - >/dev/null 2>/dev/null; then
-		echo 'Cannot find libpam development libraries, aborting. (Install libpam0g-dev?)'
-		exit 1
-	fi
-	echo '#define WITH_PAM' >> config.h
-	authobjs="${authobjs}auth_pam.o "
-	authlibs="${authlibs}-lpam "
-fi
-if [ "$ldap" = 0 ]; then
-	echo '#undef WITH_LDAP' >> config.h
-else
-	if ! echo '#include <ldap.h>' | $CC -E - >/dev/null 2>/dev/null; then
-		echo 'Cannot find libldap development libraries, aborting. (Install libldap2-dev?)'
-		exit 1
-	fi
-	echo '#define WITH_LDAP' >> config.h
-	authobjs="${authobjs}auth_ldap.o "
-	authlibs="${authlibs}-lldap "
-fi
-echo "AUTH_OBJS=$authobjs" >> Makefile.settings
-echo "EFLAGS+=$authlibs" >> Makefile.settings
+	echo 'SSL_CLIENT=ssl_'$ssl'.o' >> Makefile.settings
+fi
 
 if [ "$strip" = 0 ]; then
-	echo "STRIP=\# skip strip" >> Makefile.settings
+	echo "STRIP=\# skip strip" >> Makefile.settings;
 else
 	if [ "$debug" = 1 ]; then
@@ -747,207 +264,40 @@
 		echo 'Stripping binaries does not make sense when debugging. Stripping disabled.'
 		echo 'STRIP=\# skip strip' >> Makefile.settings
-		strip=0
+		strip=0;
 	elif [ -n "$STRIP" ]; then
-		echo "STRIP=$STRIP" >> Makefile.settings
+		echo "STRIP=$STRIP" >> Makefile.settings;
 	elif type strip > /dev/null 2> /dev/null; then
-		echo "STRIP=strip" >> Makefile.settings
+		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 [ ! "$systemd" = "0" ]; then
-	if ! $PKG_CONFIG --exists systemd; then
-		if [ "$systemd" = "1" ]; then
-			echo "systemd requested but not found"
-			exit 1
-		else
-			systemd=0
-		fi
-	elif [ "$systemd" = "auto" ]; then
-		systemd=1
-	fi
-fi
-
-pkgconf_systemd_var() {
-	# First try deprecated variable, use newer variable if not found
-	if $PKG_CONFIG --print-variables systemd | grep -q "$1"; then
-		$PKG_CONFIG --variable="$1" systemd
-	else
-		$PKG_CONFIG --variable="$2" systemd
-	fi
-}
-
-if [ "$systemd" = "1" ]; then
-	if [ -z "$systemdsystemunitdir" ]; then
-		systemdsystemunitdir=$(pkgconf_systemd_var systemdsystemunitdir systemd_system_unit_dir)
-	fi
-	if [ -z "$sysusersdir" ]; then
-		sysusersdir=$(pkgconf_systemd_var sysusersdir sysusers_dir)
-	fi
-fi
-
-if [ -n "$systemdsystemunitdir" ]; then
-	echo "SYSTEMDSYSTEMUNITDIR=$systemdsystemunitdir" >> Makefile.settings
-fi
-if [ -n "$sysusersdir" ]; then
-	echo "SYSUSERSDIR=$sysusersdir" >> Makefile.settings
-fi
-
-if [ "$gcov" = "1" ]; then
-	echo "CFLAGS+=--coverage" >> Makefile.settings
-	echo "EFLAGS+=--coverage" >> Makefile.settings
-fi
-
-if [ "$plugins" = 0 ]; then
-	plugindir=""
-	echo '#undef WITH_PLUGINS' >> config.h
-else
-	echo '#define WITH_PLUGINS' >> config.h
-fi
-
-if [ "$otr" = "auto" ]; then
-	! $PKG_CONFIG --exists libotr
-	otr=$?
-fi
-
-if [ "$otr" != 0 ] && ! $PKG_CONFIG --atleast-version=4.0 --print-errors libotr; then
-	exit 1
-fi
-
-if [ "$otr" = 1 ]; then
-	# BI == built-in
-	echo '#define OTR_BI' >> config.h
-	cat <<EOF >>Makefile.settings
-EFLAGS+=$($PKG_CONFIG --libs libotr) $(libgcrypt-config --libs)
-CFLAGS+=$($PKG_CONFIG --cflags libotr) $(libgcrypt-config --cflags)
-OTR_BI=otr.o
-EOF
-elif [ "$otr" = "plugin" ]; then
-	# for some mysterious reason beyond the comprehension of my mortal mind,
-	# the libgcrypt flags aren't needed when building as plugin. add them anyway.
-	echo '#define OTR_PI' >> config.h
-	cat <<EOF >>Makefile.settings
-OTRFLAGS=$($PKG_CONFIG --libs libotr) $(libgcrypt-config --libs)
-CFLAGS+=$($PKG_CONFIG --cflags libotr) $(libgcrypt-config --cflags)
-OTR_PI=otr.so
-EOF
-fi
-
-if [ -z "$PYTHON" ]; then
-	for pp in python python3 python2; do
-		if command -v $pp > /dev/null; then
-			PYTHON=$pp
-			break
-		fi
-	done
-fi
-
-if [ "$doc" = "1" ]; then
-	# check this here just in case someone tries to install it in python2.4...
-	if [ ! -e "$srcdir/doc/user-guide/help.txt" ] && ! $PYTHON -m xml.etree.ElementTree > /dev/null 2>&1; then
-		echo
-		echo 'ERROR: Python (>=2.5 or 3.x) is required to generate docs'
-		echo "(Use the PYTHON environment variable if it's in a weird location)"
-		exit 1
-	fi
-	echo "DOC=1" >> Makefile.settings
-	echo "PYTHON=$PYTHON" >> Makefile.settings
-fi
-
-get_version
-
-if [ ! "$BITLBEE_VERSION" = "$REAL_BITLBEE_VERSION" ]; then
-	echo "Spoofing version number: $BITLBEE_VERSION"
-	echo "#undef BITLBEE_VERSION" >> config.h
-	echo '#define BITLBEE_VERSION "'"$BITLBEE_VERSION"'"' >> config.h
+		strip=0;
+	fi;
+fi
+
+if [ "$flood" = 1 ]; then
+	echo '#define FLOOD_SEND' >> config.h
+fi
+
+if [ -n "$BITLBEE_VERSION" ]; then
 	echo
-fi
-
-if ! make helloworld > /dev/null 2>&1; then
-	echo "WARNING: Your version of make (BSD make?) does not support BitlBee's makefiles."
-	echo "BitlBee needs GNU make to build properly. On most systems GNU make is available"
-	echo "under the name 'gmake'."
-	echo
-	if gmake helloworld > /dev/null 2>&1; then
-		echo "gmake seems to be available on your machine, great."
-		echo
-	else
-		echo "gmake is not installed (or not working). Please try to install it."
-		echo
-	fi
-fi
-
-pkgconfiglibs=''
-case "$arch" in
-CYGWIN* )
-	# shellcheck disable=SC2016
-	pkgconfiglibs='-L${libdir} -lbitlbee -no-undefined'
-esac
-
-pkgconfigrequires='glib-2.0'
-if [ "$external_json_parser" = '1' ]; then
-	pkgconfigrequires="$pkgconfigrequires json-parser"
-fi
-
-cat <<EOF >bitlbee.pc
-prefix=$prefix
-includedir=$includedir
-plugindir=$plugindir
-libdir=$libdir
-datadir=$datadir
-
-Name: bitlbee
-Description: IRC to IM gateway
-Requires: $pkgconfigrequires
-Version: $BITLBEE_VERSION
-Libs: $pkgconfiglibs
-Cflags: -I\${includedir}
-
-EOF
+	echo 'Spoofing version number: '$BITLBEE_VERSION
+	echo '#undef BITLBEE_VERSION' >> config.h
+	echo '#define BITLBEE_VERSION '$BITLBEE_VERSION >> config.h;
+fi
 
 protocols=''
 protoobjs=''
 
-if [ "$purple" = 0 ]; then
-	echo '#undef WITH_PURPLE' >> config.h
-else
-	if ! $PKG_CONFIG purple; then
-		echo
-		echo 'Cannot find libpurple development libraries, aborting. (Install libpurple-dev?)'
-		exit 1
-	fi
-	echo '#define WITH_PURPLE' >> config.h
-	cat<<EOF >>Makefile.settings
-EFLAGS+=$($PKG_CONFIG purple --libs)
-PURPLE_CFLAGS+=$($PKG_CONFIG purple --cflags)
-EOF
-	protocols=$protocols'purple '
-	protoobjs=$protoobjs'purple_mod.o '
-
-	# only disable these if the user didn't enable them explicitly
-	[ "$jabber" = "default-on" ] && jabber=0
-
-	echo '#undef PACKAGE' >> config.h
-	echo '#define PACKAGE "BitlBee-LIBPURPLE"' >> config.h
-	
-	if [ "$events" = "libevent" ]; then
-		echo 'Warning: Some libpurple modules (including msn-pecan) do their event handling'
-		echo 'outside libpurple, talking to GLib directly. At least for now the combination'
-		echo 'libpurple + libevent is *not* recommended!'
-		echo
-	fi
-fi
-
-case "$CC" in
-*gcc* )
-	echo "CFLAGS+=-MMD -MF .depend/\$@.d" >> Makefile.settings
-	for i in . lib tests protocols protocols/*/; do
-		mkdir -p "$i/.depend"
-	done
-esac
+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
@@ -956,18 +306,27 @@
 	echo '#define WITH_JABBER' >> config.h
 	protocols=$protocols'jabber '
-	protoobjs=$protoobjs'jabber_mod.o '
-fi
-
-if [ "$twitter" = 0 ]; then
-	echo '#undef WITH_TWITTER' >> config.h
-else
-	echo '#define WITH_TWITTER' >> config.h
-	protocols=$protocols'twitter '
-	protoobjs=$protoobjs'twitter_mod.o '
+	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 "Warning: You haven't selected any communication protocol to compile!"
-	echo "         BitlBee will run, but you will be unable to connect to IM servers!"
+	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
 
@@ -975,44 +334,37 @@
 echo "PROTOOBJS = $protoobjs" >> Makefile.settings
 
-echo "Architecture: $arch"
+echo
+echo Architecture: $arch
 case "$arch" in
 Linux )
+	echo 'Linux.'
 ;;
 GNU/* )
+	echo 'Debian with non-Linux kernel?'
 ;;
 *BSD )
-;;
-Darwin )
-	echo 'STRIP=\# skip strip' >> Makefile.settings
-;;
-IRIX )
+	echo '*BSD.'
+	echo 'EFLAGS+=-liconv' >> Makefile.settings;
 ;;
 SunOS )
+	echo 'Solaris.'
 	echo 'EFLAGS+=-lresolv -lnsl -lsocket' >> Makefile.settings
 	echo 'STRIP=\# skip strip' >> Makefile.settings
-	echo '#define NO_FD_PASSING' >> config.h
-	if [ "$arch_rel" = "5.10" ]; then
-		echo '#define NO_STRCASESTR' >> config.h
-	fi
-;;
-AIX )
-	echo 'EFLAGS+=-Wl,-brtl' >> Makefile.settings
+	echo 'EFLAGS+=-liconv' >> Makefile.settings;
+;;
+Darwin )
+	echo 'Darwin/Mac OS X.'
+	echo 'EFLAGS+=-liconv' >> Makefile.settings;
+;;
+IRIX )
+	echo 'IRIX.'
 ;;
 CYGWIN* )
-	echo 'EFLAGS+=-Wl,--export-all,--out-implib,libbitlbee.dll.a' >> Makefile.settings
-	echo 'IMPLIB=libbitlbee.dll.a' >> Makefile.settings
-;;
-Windows )
-	echo 'Native windows compilation is not supported anymore, use cygwin instead.'
+	echo 'Cygwin is not officially supported.'
 ;;
 * )
-	echo 'We haven'\''t tested BitlBee on many platforms yet, yours is untested. YMMV.'
-	echo 'Please report any problems at https://bugs.bitlbee.org/.'
+	echo 'We haven'\''t tested BitlBee on many platforms yet, yours is untested. YMMV. Please report any problems to <wilmer@gaast.net>.'
 ;;
 esac
-
-if [ -n "$target" ]; then
-	echo "Cross-compiling for: $target"
-fi
 
 echo
@@ -1020,62 +372,28 @@
 
 if [ "$debug" = "1" ]; then
-	echo '  Debugging enabled.'
-else
-	echo '  Debugging disabled.'
-fi
-
-if [ "$asan" = "1" ]; then
-	echo '  AddressSanitizer (ASAN) enabled.'
-else
-	echo '  AddressSanitizer (ASAN) disabled.'
-fi
-
-if [ "$pie" = "1" ]; then
-	echo '  Building PIE executable'
-else
-	echo '  Building non-PIE executable'
+	echo '  Debugging enabled.';
+else
+	echo '  Debugging disabled.';
 fi
 
 if [ "$strip" = "1" ]; then
-	echo '  Binary stripping enabled.'
-else
-	echo '  Binary stripping disabled.'
-fi
-
-if [ "$otr" = "1" ]; then
-	echo '  Off-the-Record (OTR) Messaging enabled.'
-elif [ "$otr" = "plugin" ]; then
-	echo '  Off-the-Record (OTR) Messaging enabled (as a plugin).'
-else
-	echo '  Off-the-Record (OTR) Messaging disabled.'
-fi
-
-if [ "$systemd" = "1" ]; then
-	echo '  systemd enabled.'
-else
-	echo '  systemd disabled.'
-fi
-
-echo "  Using python: $PYTHON"
-
-if [ "$external_json_parser" = "1" ]; then
-	echo '  Using system JSON parser.'
-else
-	echo '  Using bundled JSON parser.'
-fi
-
-echo "  Using event handler: $events"
-echo "  Using SSL library: $ssl"
-#echo '  Building with these storage backends: '$STORAGES
+	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$protocols_mods"
-	case "$protocols" in
-	*purple*)
-		echo "    Note that BitlBee-libpurple is supported on a best-effort basis. It's"
-		echo "    not *fully* compatible with normal BitlBee. Don't use it unless you"
-		echo "    absolutely need it (i.e. support for a certain protocol or feature)."
-	esac
-else
-	echo '  Building without IM-protocol support. We wish you a lot of fun...'
-fi
+	echo '  Building with these protocols:' $protocols;
+else
+	echo '  Building without IM-protocol support. We wish you a lot of fun...';
+fi
Index: crypting.c
===================================================================
--- crypting.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ crypting.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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
Index: crypting.h
===================================================================
--- crypting.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ crypting.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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);
Index: c.c
===================================================================
--- dcc.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,591 +1,0 @@
-/********************************************************************\
-* BitlBee -- An IRC to other IM-networks gateway                     *
-*                                                                    *
-* Copyright 2007 Uli Meis <a.sporto+bee@gmail.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 with
-  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
-  if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#define BITLBEE_CORE
-#include "bitlbee.h"
-#include "ft.h"
-#include "dcc.h"
-#include <netinet/tcp.h>
-#include <regex.h>
-#include "lib/ftutil.h"
-
-/*
- * Since that might be confusing a note on naming:
- *
- * Generic dcc functions start with
- *
- *      dcc_
- *
- * ,methods specific to DCC SEND start with
- *
- *      dccs_
- *
- * . Since we can be on both ends of a DCC SEND,
- * functions specific to one end are called
- *
- *      dccs_send and dccs_recv
- *
- * ,respectively.
- */
-
-
-/*
- * used to generate a unique local transfer id the user
- * can use to reject/cancel transfers
- */
-unsigned int local_transfer_id = 1;
-
-/*
- * just for debugging the nr. of chunks we received from im-protocols and the total data
- */
-unsigned int receivedchunks = 0, receiveddata = 0;
-
-void dcc_finish(file_transfer_t *file);
-void dcc_close(file_transfer_t *file);
-gboolean dccs_send_proto(gpointer data, gint fd, b_input_condition cond);
-int dccs_send_request(struct dcc_file_transfer *df, irc_user_t *iu, struct sockaddr_storage *saddr);
-gboolean dccs_recv_proto(gpointer data, gint fd, b_input_condition cond);
-gboolean dccs_recv_write_request(file_transfer_t *ft);
-gboolean dcc_progress(gpointer data, gint fd, b_input_condition cond);
-gboolean dcc_abort(dcc_file_transfer_t *df, char *reason, ...);
-
-dcc_file_transfer_t *dcc_alloc_transfer(const char *file_name, size_t file_size, struct im_connection *ic)
-{
-	file_transfer_t *file = g_new0(file_transfer_t, 1);
-	dcc_file_transfer_t *df = file->priv = g_new0(dcc_file_transfer_t, 1);
-
-	file->file_size = file_size;
-	file->file_name = g_strdup(file_name);
-	file->local_id = local_transfer_id++;
-	file->ic = df->ic = ic;
-	df->ft = file;
-
-	return df;
-}
-
-/* This is where the sending magic starts... */
-file_transfer_t *dccs_send_start(struct im_connection *ic, irc_user_t *iu, const char *file_name, size_t file_size)
-{
-	file_transfer_t *file;
-	dcc_file_transfer_t *df;
-	irc_t *irc = (irc_t *) ic->bee->ui_data;
-	struct sockaddr_storage saddr;
-	char *errmsg;
-	char host[NI_MAXHOST];
-	char port[6];
-
-	if (file_size > global.conf->ft_max_size) {
-		return NULL;
-	}
-
-	df = dcc_alloc_transfer(file_name, file_size, ic);
-	file = df->ft;
-	file->write = dccs_send_write;
-
-	/* listen and request */
-
-	if ((df->fd = ft_listen(&saddr, host, port, irc->fd, TRUE, &errmsg)) == -1) {
-		dcc_abort(df, "Failed to listen locally, check your ft_listen setting in bitlbee.conf: %s", errmsg);
-		return NULL;
-	}
-
-	file->status = FT_STATUS_LISTENING;
-
-	if (!dccs_send_request(df, iu, &saddr)) {
-		return NULL;
-	}
-
-	/* watch */
-	df->watch_in = b_input_add(df->fd, B_EV_IO_READ, dccs_send_proto, df);
-
-	irc->file_transfers = g_slist_prepend(irc->file_transfers, file);
-
-	df->progress_timeout = b_timeout_add(DCC_MAX_STALL * 1000, dcc_progress, df);
-
-	imcb_log(ic, "File transfer request from %s for %s (%zd kb).\n"
-	         "Accept the file transfer if you'd like the file. If you don't, "
-	         "issue the 'transfer reject' command.",
-	         iu->nick, file_name, file_size / 1024);
-
-	return file;
-}
-
-/* Used pretty much everywhere in the code to abort a transfer */
-gboolean dcc_abort(dcc_file_transfer_t *df, char *reason, ...)
-{
-	file_transfer_t *file = df->ft;
-	va_list params;
-
-	va_start(params, reason);
-	char *msg = g_strdup_vprintf(reason, params);
-	va_end(params);
-
-	file->status |= FT_STATUS_CANCELED;
-
-	if (file->canceled) {
-		file->canceled(file, msg);
-	}
-
-	imcb_log(df->ic, "File %s: DCC transfer aborted: %s", file->file_name, msg);
-
-	g_free(msg);
-
-	dcc_close(df->ft);
-
-	return FALSE;
-}
-
-gboolean dcc_progress(gpointer data, gint fd, b_input_condition cond)
-{
-	struct dcc_file_transfer *df = data;
-
-	if (df->bytes_sent == df->progress_bytes_last) {
-		/* no progress. cancel */
-		if (df->bytes_sent == 0) {
-			return dcc_abort(df, "Couldn't establish transfer within %d seconds", DCC_MAX_STALL);
-		} else {
-			return dcc_abort(df, "Transfer stalled for %d seconds at %d kb", DCC_MAX_STALL,
-			                 df->bytes_sent / 1024);
-		}
-
-	}
-
-	df->progress_bytes_last = df->bytes_sent;
-
-	return TRUE;
-}
-
-/* used extensively for socket operations */
-#define ASSERTSOCKOP(op, msg) \
-	if ((op) == -1) { \
-		return dcc_abort(df, msg ": %s", strerror(errno)); }
-
-/* Creates the "DCC SEND" line and sends it to the server */
-int dccs_send_request(struct dcc_file_transfer *df, irc_user_t *iu, struct sockaddr_storage *saddr)
-{
-	char ipaddr[INET6_ADDRSTRLEN];
-	const void *netaddr;
-	int port;
-	char *cmd;
-
-	if (saddr->ss_family == AF_INET) {
-		struct sockaddr_in *saddr_ipv4 = ( struct sockaddr_in *) saddr;
-
-		sprintf(ipaddr, "%d",
-		        ntohl(saddr_ipv4->sin_addr.s_addr));
-		port = saddr_ipv4->sin_port;
-	} else {
-		struct sockaddr_in6 *saddr_ipv6 = ( struct sockaddr_in6 *) saddr;
-
-		netaddr = &saddr_ipv6->sin6_addr.s6_addr;
-		port = saddr_ipv6->sin6_port;
-
-		/*
-		 * Didn't find docs about this, but it seems that's the way irssi does it
-		 */
-		if (!inet_ntop(saddr->ss_family, netaddr, ipaddr, sizeof(ipaddr))) {
-			return dcc_abort(df, "inet_ntop failed: %s", strerror(errno));
-		}
-	}
-
-	port = ntohs(port);
-
-	cmd = g_strdup_printf("\001DCC SEND %s %s %u %zu\001",
-	                      df->ft->file_name, ipaddr, port, df->ft->file_size);
-
-	irc_send_msg_raw(iu, "PRIVMSG", iu->irc->user->nick, cmd);
-
-	g_free(cmd);
-
-	return TRUE;
-}
-
-/*
- * After setup, the transfer itself is handled entirely by this function.
- * There are basically four things to handle: connect, receive, send, and error.
- */
-gboolean dccs_send_proto(gpointer data, gint fd, b_input_condition cond)
-{
-	dcc_file_transfer_t *df = data;
-	file_transfer_t *file = df->ft;
-
-	if ((cond & B_EV_IO_READ) &&
-	    (file->status & FT_STATUS_LISTENING)) {
-		struct sockaddr *clt_addr;
-		socklen_t ssize = sizeof(clt_addr);
-
-		/* Connect */
-
-		ASSERTSOCKOP(df->fd = accept(fd, (struct sockaddr *) &clt_addr, &ssize), "Accepting connection");
-
-		closesocket(fd);
-		fd = df->fd;
-		file->status = FT_STATUS_TRANSFERRING;
-		sock_make_nonblocking(fd);
-
-		/* IM protocol callback */
-		if (file->accept) {
-			file->accept(file);
-		}
-
-		/* reschedule for reading on new fd */
-		df->watch_in = b_input_add(fd, B_EV_IO_READ, dccs_send_proto, df);
-
-		return FALSE;
-	}
-
-	if (cond & B_EV_IO_READ) {
-		int ret;
-
-		ASSERTSOCKOP(ret = recv(fd, ((char *) &df->acked) + df->acked_len,
-		                        sizeof(df->acked) - df->acked_len, 0), "Receiving");
-
-		if (ret == 0) {
-			return dcc_abort(df, "Remote end closed connection");
-		}
-
-		/* How likely is it that a 32-bit integer gets split across
-		   packet boundaries? Chances are rarely 0 so let's be sure. */
-		if ((df->acked_len = (df->acked_len + ret) % 4) > 0) {
-			return TRUE;
-		}
-
-		df->acked = ntohl(df->acked);
-
-		/* If any of this is actually happening, the receiver should buy a new IRC client */
-
-		if (df->acked > df->bytes_sent) {
-			return dcc_abort(df,
-			                 "Receiver magically received more bytes than sent ( %d > %d ) (BUG at receiver?)", df->acked,
-			                 df->bytes_sent);
-		}
-
-		if (df->acked < file->bytes_transferred) {
-			return dcc_abort(df, "Receiver lost bytes? ( has %d, had %d ) (BUG at receiver?)", df->acked,
-			                 file->bytes_transferred);
-		}
-
-		file->bytes_transferred = df->acked;
-
-		if (file->bytes_transferred >= file->file_size) {
-			if (df->proto_finished) {
-				dcc_finish(file);
-			}
-			return FALSE;
-		}
-
-		return TRUE;
-	}
-
-	return TRUE;
-}
-
-gboolean dccs_recv_start(file_transfer_t *ft)
-{
-	dcc_file_transfer_t *df = ft->priv;
-	struct sockaddr_storage *saddr = &df->saddr;
-	int fd;
-	char ipaddr[INET6_ADDRSTRLEN];
-	socklen_t sa_len = saddr->ss_family == AF_INET ?
-	                   sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
-
-	if (!ft->write) {
-		return dcc_abort(df, "BUG: protocol didn't register write()");
-	}
-
-	ASSERTSOCKOP(fd = df->fd = socket(saddr->ss_family, SOCK_STREAM, 0), "Opening Socket");
-
-	sock_make_nonblocking(fd);
-
-	if ((connect(fd, (struct sockaddr *) saddr, sa_len) == -1) &&
-	    (errno != EINPROGRESS)) {
-		return dcc_abort(df, "Connecting to %s:%d : %s",
-		                 inet_ntop(saddr->ss_family,
-		                           saddr->ss_family == AF_INET ?
-		                           ( void * ) &(( struct sockaddr_in *) saddr)->sin_addr.s_addr :
-		                           ( void * ) &(( struct sockaddr_in6 *) saddr)->sin6_addr.s6_addr,
-		                           ipaddr,
-		                           sizeof(ipaddr)),
-		                 ntohs(saddr->ss_family == AF_INET ?
-		                       (( struct sockaddr_in *) saddr)->sin_port :
-		                       (( struct sockaddr_in6 *) saddr)->sin6_port),
-		                 strerror(errno));
-	}
-
-	ft->status = FT_STATUS_CONNECTING;
-
-	/* watch */
-	df->watch_out = b_input_add(df->fd, B_EV_IO_WRITE, dccs_recv_proto, df);
-	ft->write_request = dccs_recv_write_request;
-
-	df->progress_timeout = b_timeout_add(DCC_MAX_STALL * 1000, dcc_progress, df);
-
-	return TRUE;
-}
-
-gboolean dccs_recv_proto(gpointer data, gint fd, b_input_condition cond)
-{
-	dcc_file_transfer_t *df = data;
-	file_transfer_t *ft = df->ft;
-
-	if ((cond & B_EV_IO_WRITE) &&
-	    (ft->status & FT_STATUS_CONNECTING)) {
-		ft->status = FT_STATUS_TRANSFERRING;
-
-		//df->watch_in = b_input_add( df->fd, B_EV_IO_READ, dccs_recv_proto, df );
-
-		df->watch_out = 0;
-		return FALSE;
-	}
-
-	if (cond & B_EV_IO_READ) {
-		int ret, done;
-
-		ASSERTSOCKOP(ret = recv(fd, ft->buffer, sizeof(ft->buffer), 0), "Receiving");
-
-		if (ret == 0) {
-			return dcc_abort(df, "Remote end closed connection");
-		}
-
-		if (!ft->write(df->ft, ft->buffer, ret)) {
-			return FALSE;
-		}
-
-		df->bytes_sent += ret;
-
-		done = df->bytes_sent >= ft->file_size;
-
-		if (((df->bytes_sent - ft->bytes_transferred) > DCC_PACKET_SIZE) ||
-		    done) {
-			guint32 ack = htonl(ft->bytes_transferred = df->bytes_sent);
-			int ackret;
-
-			ASSERTSOCKOP(ackret = send(fd, &ack, 4, 0), "Sending DCC ACK");
-
-			if (ackret != 4) {
-				return dcc_abort(df, "Error sending DCC ACK, sent %d instead of 4 bytes", ackret);
-			}
-		}
-
-		if (df->bytes_sent == ret) {
-			ft->started = time(NULL);
-		}
-
-		if (done) {
-			if (df->watch_out) {
-				b_event_remove(df->watch_out);
-			}
-
-			df->watch_in = 0;
-
-			if (df->proto_finished) {
-				dcc_finish(ft);
-			}
-
-			return FALSE;
-		}
-
-		df->watch_in = 0;
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-gboolean dccs_recv_write_request(file_transfer_t *ft)
-{
-	dcc_file_transfer_t *df = ft->priv;
-
-	if (df->watch_in) {
-		return dcc_abort(df, "BUG: write_request() called while watching");
-	}
-
-	df->watch_in = b_input_add(df->fd, B_EV_IO_READ, dccs_recv_proto, df);
-
-	return TRUE;
-}
-
-gboolean dccs_send_can_write(gpointer data, gint fd, b_input_condition cond)
-{
-	struct dcc_file_transfer *df = data;
-
-	df->watch_out = 0;
-
-	df->ft->write_request(df->ft);
-	return FALSE;
-}
-
-/*
- * Incoming data.
- *
- */
-gboolean dccs_send_write(file_transfer_t *file, char *data, unsigned int data_len)
-{
-	dcc_file_transfer_t *df = file->priv;
-	int ret;
-
-	receivedchunks++; receiveddata += data_len;
-
-	if (df->watch_out) {
-		return dcc_abort(df, "BUG: write() called while watching");
-	}
-
-	ASSERTSOCKOP(ret = send(df->fd, data, data_len, 0), "Sending data");
-
-	if (ret == 0) {
-		return dcc_abort(df, "Remote end closed connection");
-	}
-
-	/* TODO: this should really not be fatal */
-	if (ret < data_len) {
-		return dcc_abort(df, "send() sent %d instead of %d", ret, data_len);
-	}
-
-	if (df->bytes_sent == 0) {
-		file->started = time(NULL);
-	}
-
-	df->bytes_sent += ret;
-
-	if (df->bytes_sent < df->ft->file_size) {
-		df->watch_out = b_input_add(df->fd, B_EV_IO_WRITE, dccs_send_can_write, df);
-	}
-
-	return TRUE;
-}
-
-/*
- * Cleans up after a transfer.
- */
-void dcc_close(file_transfer_t *file)
-{
-	dcc_file_transfer_t *df = file->priv;
-	irc_t *irc = (irc_t *) df->ic->bee->ui_data;
-
-	if (file->free) {
-		file->free(file);
-	}
-
-	closesocket(df->fd);
-
-	if (df->watch_in) {
-		b_event_remove(df->watch_in);
-	}
-
-	if (df->watch_out) {
-		b_event_remove(df->watch_out);
-	}
-
-	if (df->progress_timeout) {
-		b_event_remove(df->progress_timeout);
-	}
-
-	irc->file_transfers = g_slist_remove(irc->file_transfers, file);
-
-	g_free(df);
-	g_free(file->file_name);
-	g_free(file);
-}
-
-void dcc_finish(file_transfer_t *file)
-{
-	dcc_file_transfer_t *df = file->priv;
-	time_t diff = time(NULL) - file->started ? : 1;
-
-	file->status |= FT_STATUS_FINISHED;
-
-	if (file->finished) {
-		file->finished(file);
-	}
-
-	imcb_log(df->ic, "File %s transferred successfully at %d kb/s!", file->file_name,
-	         (int) (file->bytes_transferred / 1024 / diff));
-	dcc_close(file);
-}
-
-/*
- * DCC SEND <filename> <IP> <port> <filesize>
- *
- * filename can be in "" or not. If it is, " can probably be escaped...
- * IP can be an unsigned int (IPV4) or something else (IPV6)
- *
- */
-file_transfer_t *dcc_request(struct im_connection *ic, char* const* ctcp)
-{
-	irc_t *irc = (irc_t *) ic->bee->ui_data;
-	file_transfer_t *ft;
-	dcc_file_transfer_t *df;
-	int gret;
-	size_t filesize;
-
-	if (ctcp[5] != NULL &&
-	    sscanf(ctcp[4], "%zd", &filesize) == 1 &&   /* Just int. validation. */
-	    sscanf(ctcp[5], "%zd", &filesize) == 1) {
-		char *filename, *host, *port;
-		struct addrinfo hints, *rp;
-
-		filename = ctcp[2];
-
-		host = ctcp[3];
-		while (*host && g_ascii_isdigit(*host)) {
-			host++;                                    /* Just digits? */
-		}
-		if (*host == '\0') {
-			struct in_addr ipaddr = { .s_addr = htonl(atoll(ctcp[3])) };
-			host = inet_ntoa(ipaddr);
-		} else {
-			/* Contains non-numbers, hopefully an IPV6 address */
-			host = ctcp[3];
-		}
-
-		port = ctcp[4];
-		filesize = atoll(ctcp[5]);
-
-		memset(&hints, 0, sizeof(struct addrinfo));
-		hints.ai_socktype = SOCK_STREAM;
-		hints.ai_flags = AI_NUMERICSERV;
-
-		if ((gret = getaddrinfo(host, port, &hints, &rp))) {
-			imcb_log(ic, "DCC: getaddrinfo() failed with %s "
-			         "when parsing incoming 'DCC SEND': "
-			         "host %s, port %s",
-			         gai_strerror(gret), host, port);
-			return NULL;
-		}
-
-		df = dcc_alloc_transfer(filename, filesize, ic);
-		ft = df->ft;
-		ft->sending = TRUE;
-		memcpy(&df->saddr, rp->ai_addr, rp->ai_addrlen);
-
-		freeaddrinfo(rp);
-
-		irc->file_transfers = g_slist_prepend(irc->file_transfers, ft);
-
-		return ft;
-	} else {
-		imcb_log(ic, "DCC: couldnt parse `DCC SEND' line");
-	}
-
-	return NULL;
-}
Index: c.h
===================================================================
--- dcc.h	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,105 +1,0 @@
-/********************************************************************\
-* BitlBee -- An IRC to other IM-networks gateway                     *
-*                                                                    *
-* Copyright 2006 Marijn Kruisselbrink and others                     *
-* Copyright 2007 Uli Meis <a.sporto+bee@gmail.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 with
-  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
-  if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-/*
- * DCC SEND
- *
- * Historically, DCC means send 1024 Bytes and wait for a 4 byte reply
- * acknowledging all transferred data. This is ridiculous for two reasons.  The
- * first being that TCP is a stream oriented protocol that doesn't care much
- * about your idea of a packet. The second reason being that TCP is a reliable
- * transfer protocol with its own sophisticated ACK mechanism, making DCCs ACK
- * mechanism look like a joke. For these reasons, DCCs requirements have
- * (hopefully) been relaxed in most implementations and this implementation
- * depends upon at least the following: The 1024 bytes need not be transferred
- * at once, i.e. packets can be smaller. A second relaxation has apparently
- * gotten the name "DCC SEND ahead" which basically means to not give a damn
- * about those DCC ACKs and just send data as you please. This behaviour is
- * enabled by default. Note that this also means that packets may be as large
- * as the maximum segment size.
- */
-
-#ifndef _DCC_H
-#define _DCC_H
-
-/* Send an ACK after receiving this amount of data */
-#define DCC_PACKET_SIZE 1024
-
-/* Time in seconds that a DCC transfer can be stalled before being aborted.
- * By handling this here individual protocols don't have to think about this. */
-#define DCC_MAX_STALL 120
-
-typedef struct dcc_file_transfer {
-
-	struct im_connection *ic;
-
-	/*
-	 * Depending in the status of the file transfer, this is either the socket that is
-	 * being listened on for connections, or the socket over which the file transfer is
-	 * taking place.
-	 */
-	int fd;
-
-	/*
-	 * IDs returned by b_input_add for watch_ing over the above socket.
-	 */
-	gint watch_in;   /* readable */
-	gint watch_out;  /* writable */
-
-	/* the progress watcher cancels any file transfer if nothing happens within DCC_MAX_STALL */
-	gint progress_timeout;
-	size_t progress_bytes_last;
-
-	/*
-	 * The total amount of bytes that have been sent to the irc client.
-	 */
-	size_t bytes_sent;
-
-	/*
-	 * Handle the wonderful sadly-not-deprecated ACKs.
-	 */
-	guint32 acked;
-	int acked_len;
-
-	/* imc's handle */
-	file_transfer_t *ft;
-
-	/* if we're receiving, this is the sender's socket address */
-	struct sockaddr_storage saddr;
-
-	/* set to true if the protocol has finished
-	 * (i.e. called imcb_file_finished)
-	 */
-	int proto_finished;
-} dcc_file_transfer_t;
-
-file_transfer_t *dccs_send_start(struct im_connection *ic, irc_user_t *iu, const char *file_name, size_t file_size);
-void dcc_canceled(file_transfer_t *file, char *reason);
-gboolean dccs_send_write(file_transfer_t *file, char *data, unsigned int data_size);
-file_transfer_t *dcc_request(struct im_connection *ic, char* const* ctcp);
-void dcc_finish(file_transfer_t *file);
-void dcc_close(file_transfer_t *file);
-gboolean dccs_recv_start(file_transfer_t *ft);
-
-#endif
Index: bian/NEWS
===================================================================
--- debian/NEWS	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,10 +1,0 @@
-bitlbee (3.5-1) unstable; urgency=medium
-
-  * /etc/default/bitlbee is now deprecated and no longer used. Any local
-    modifications to the following settings won't be automatically migrated:
-    BITLBEE_PORT, BITLBEE_OPTS, BITLBEE_DISABLED, BITLBEE_UPGRADE_DONT_RESTART
-  * The debian post-installation scripts no longer try to automatically assign
-    an alternate port number if 6667 is being used by another service. Please
-    set it explicitly in /etc/bitlbee/bitlbee.conf
-
- -- dequis <dx@dxzone.com.ar>  Sun, 08 Jan 2017 03:24:13 -0300
Index: debian/README.Debian
===================================================================
--- debian/README.Debian	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ debian/README.Debian	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -1,3 +1,7 @@
-Fire up your favourite IRC client and connect to localhost:6667,
+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).
 
Index: bian/bitlbee-common.docs
===================================================================
--- debian/bitlbee-common.docs	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,4 +1,0 @@
-doc/AUTHORS
-doc/CREDITS
-doc/FAQ
-doc/README
Index: bian/bitlbee-common.examples
===================================================================
--- debian/bitlbee-common.examples	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,1 +1,0 @@
-utils/*
Index: bian/bitlbee-common.init
===================================================================
--- debian/bitlbee-common.init	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,79 +1,0 @@
-#! /bin/sh
-### BEGIN INIT INFO
-# Provides:          bitlbee
-# Required-Start:    $remote_fs $syslog
-# Required-Stop:     $remote_fs $syslog
-# Default-Start:     2 3 4 5
-# Default-Stop:      0 1 6
-# Short-Description:       Start and stop BitlBee IRC to other chat networks gateway
-### END INIT INFO
-#
-#
-# Version:	@(#)skeleton  2.85-23  28-Jul-2004  miquels@cistron.nl
-#
-
-PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
-DESC="BitlBee IRC/IM gateway"
-NAME=bitlbee
-DAEMON=/usr/sbin/$NAME
-PIDFILE=/var/run/$NAME.pid
-SCRIPTNAME=/etc/init.d/$NAME
-
-# Gracefully exit if the package has been removed.
-[ -x $DAEMON ] || exit 0
-
-BITLBEE_OPTS=-F
-
-
-#
-#	Function that starts the daemon/service.
-#
-d_start() {
-	# Make sure BitlBee can actually write its PID...
-	touch $PIDFILE
-	chown bitlbee: $PIDFILE
-	
-	start-stop-daemon --start --quiet --pidfile $PIDFILE \
-		--exec $DAEMON -- -P $PIDFILE $BITLBEE_OPTS
-}
-
-#
-#	Function that stops the daemon/service.
-#
-d_stop() {
-	start-stop-daemon --stop --quiet --pidfile $PIDFILE \
-		--name $NAME
-}
-
-
-case "$1" in
-  start)
-	echo -n "Starting $DESC: $NAME"
-	d_start
-	echo "."
-	;;
-  stop)
-	echo -n "Stopping $DESC: $NAME"
-	d_stop
-	echo "."
-	;;
-  #reload)
-	#
-	#	No reload target, but there's a REHASH command which we
-	#	might use later...
-	#
-  #;;
-  restart|force-reload)
-	echo -n "Restarting $DESC: $NAME"
-	d_stop
-	sleep 1
-	d_start
-	echo "."
-	;;
-  *)
-	echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2
-	exit 1
-	;;
-esac
-
-exit 0
Index: bian/bitlbee-common.postinst
===================================================================
--- debian/bitlbee-common.postinst	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,54 +1,0 @@
-#!/bin/sh
-
-set -e
-
-CONFDIR=/var/lib/bitlbee/
-
-if [ -e /etc/default/bitlbee ]; then
-   cat <<EOF >/etc/default/bitlbee
-## /etc/default/bitlbee: Auto-generated/updated script.
-##
-## This file is deprecated and no longer used.
-## Please edit /etc/bitlbee/bitlbee.conf instead
-EOF
-
-fi
-
-## Restore the helpfile in case we weren't upgrading but just reconfiguring:
-if [ -e /usr/share/bitlbee/help.upgrading ]; then
-	if [ -e /usr/share/bitlbee/help.txt ]; then
-		rm -f /usr/share/bitlbee/help.upgrading
-	else
-		mv /usr/share/bitlbee/help.upgrading /usr/share/bitlbee/help.txt
-	fi
-fi
-
-if [ -n "$2" -a -x "/etc/init.d/bitlbee" ]; then
-	invoke-rc.d bitlbee restart
-fi
-
-## If we're upgrading, we'll probably skip this next part
-if [ -d $CONFDIR ] && chown -R bitlbee: $CONFDIR; then
-	echo 'BitlBee (probably) already installed, skipping user/configdir installation'
-else
-	adduser --system --group --disabled-login --disabled-password --home /var/lib/bitlbee/ bitlbee
-fi
-
-if [ -d /var/lib/bitlbee ]; then
-	chmod 700 /var/lib/bitlbee/
-fi
-
-## Can't do this in packaging phase: Don't know the UID yet. Access to
-## the file should be limited, now that it stores passwords. Added
-## --group later for a little more security, but have to see if I can
-## apply this change to existing installations on upgrades. Will think
-## about that later.
-if getent group bitlbee > /dev/null; then
-	chmod 640 /etc/bitlbee/bitlbee.conf
-	chown root:bitlbee /etc/bitlbee/bitlbee.conf
-else
-	chmod 600 /etc/bitlbee/bitlbee.conf
-	chown bitlbee /etc/bitlbee/bitlbee.conf
-fi
-
-#DEBHELPER#
Index: bian/bitlbee-common.postrm
===================================================================
--- debian/bitlbee-common.postrm	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,11 +1,0 @@
-#!/bin/sh
-
-set -e
-
-if [ "$1" = "purge" ]; then
-	rm -f /etc/default/bitlbee
-	deluser --system bitlbee > /dev/null || true
-	rm -rf /var/lib/bitlbee ## deluser doesn't seem to do this for homedirs in /var
-fi
-
-#DEBHELPER#
Index: bian/bitlbee-common.preinst
===================================================================
--- debian/bitlbee-common.preinst	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,20 +1,0 @@
-#!/bin/sh
-
-set -e
-
-NAME=bitlbee
-PIDFILE=/var/run/$NAME.pid
-
-# git revision 7949d5a introduced dh_systemd.
-DH_SYSTEMD_REV="3.4.2+20160612+master+41-g7949d5a-git"
-
-# if upgrading from something older (including 3.4.2), kill the old daemon.
-# do this just once, this time, to allow systemd to manage it.
-if [ "$1" = 'upgrade' ]; then
-	if dpkg --compare-versions $2 lt $DH_SYSTEMD_REV; then
-		# we may not have init scripts here
-		start-stop-daemon --oknodo --stop --quiet --pidfile $PIDFILE --name $NAME
-	fi
-fi
-
-#DEBHELPER#
Index: bian/bitlbee-common.prerm
===================================================================
--- debian/bitlbee-common.prerm	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,15 +1,0 @@
-#!/bin/sh
-
-set -e
-
-if [ "$1" = "upgrade" ]; then
-	## To prevent the help function from breaking in currently running
-	## BitlBee processes. Have to do it like this because dpkg-reconfigure
-	## looks a lot like an upgrade and we don't want to lose help.txt...
-	if [ -e /usr/share/bitlbee/help.txt ]; then
-		rm -f /usr/share/bitlbee/help.upgrading
-		mv /usr/share/bitlbee/help.txt /usr/share/bitlbee/help.upgrading
-	fi
-fi
-
-#DEBHELPER#
Index: bian/bitlbee-dev.postinst
===================================================================
--- debian/bitlbee-dev.postinst	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,14 +1,0 @@
-#!/bin/sh
-set -e
-
-# Replace documentation directory with symlink
-docdir="/usr/share/doc/bitlbee-dev"
-if [ -d $docdir ] && [ ! -L $docdir ]; then
-    if rmdir $docdir 2>/dev/null; then
-        ln -sf bitlbee-common $docdir
-    fi
-fi
-
-#DEBHELPER#
-
-exit 0
Index: bian/bitlbee-libpurple.lintian-overrides
===================================================================
--- debian/bitlbee-libpurple.lintian-overrides	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,2 +1,0 @@
-# manpage is installed in bitlbee-common, which bitlbee depends on
-bitlbee-libpurple: binary-without-manpage usr/sbin/bitlbee
Index: debian/bitlbee.conffiles
===================================================================
--- debian/bitlbee.conffiles	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ debian/bitlbee.conffiles	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,2 @@
+/etc/bitlbee/motd.txt
+/etc/bitlbee/bitlbee.conf
Index: debian/bitlbee.config
===================================================================
--- debian/bitlbee.config	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ debian/bitlbee.config	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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
Index: bian/bitlbee.lintian-overrides
===================================================================
--- debian/bitlbee.lintian-overrides	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,2 +1,0 @@
-# manpage is installed in bitlbee-common, which bitlbee depends on
-bitlbee: binary-without-manpage usr/sbin/bitlbee
Index: debian/bitlbee.postinst
===================================================================
--- debian/bitlbee.postinst	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ debian/bitlbee.postinst	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -1,14 +1,22 @@
-#!/bin/sh
-set -e
+#!/bin/sh -e
 
-# Replace documentation directory with symlink
-docdir="/usr/share/doc/bitlbee"
-if [ -d $docdir ] && [ ! -L $docdir ]; then
-    if rmdir $docdir 2>/dev/null; then
-        ln -sf bitlbee-common $docdir
-    fi
+. /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
 
-#DEBHELPER#
+if [ -d $CONFDIR ] && chown bitlbee $CONFDIR; then
+	echo 'BitlBee (probably) already installed, skipping user/configdir installation'
+	exit 0;
+fi
 
-exit 0
+adduser --system --home /var/lib/bitlbee/ --disabled-login --disabled-password bitlbee
+chmod 700 /var/lib/bitlbee/
Index: debian/bitlbee.postrm
===================================================================
--- debian/bitlbee.postrm	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ debian/bitlbee.postrm	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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
Index: debian/bitlbee.preinst
===================================================================
--- debian/bitlbee.preinst	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ debian/bitlbee.preinst	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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
Index: bian/bitlbee.prerm
===================================================================
--- debian/bitlbee.prerm	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,9 +1,0 @@
-#!/bin/sh
-
-set -e
-
-if [ "$1" != "upgrade" ]; then
-	invoke-rc.d bitlbee stop
-fi
-
-#DEBHELPER#
Index: debian/bitlbee.templates
===================================================================
--- debian/bitlbee.templates	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ debian/bitlbee.templates	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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.
Index: debian/bitlbee.templates.master
===================================================================
--- debian/bitlbee.templates.master	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ debian/bitlbee.templates.master	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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.
Index: debian/changelog
===================================================================
--- debian/changelog	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ debian/changelog	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -1,571 +1,2 @@
-bitlbee (3.6-2) UNRELEASED; urgency=medium
-
-  [ Debian Janitor ]
-  * Depend on newer debhelper (>= 9.20160709) rather than dh-systemd.
-
-  [ dequis ]
-  * Some more d/copyright fixups (Closes: #883872)
-
-  [ Jelmer Vernooĳ ]
-  * Use secure URI in debian/watch.
-  * Use secure URI in Homepage field.
-  * Add missing colon in closes line.
-  * Remove obsolete fields Name from debian/upstream/metadata.
-  * Fix day-of-week for changelog entries 0.81a-1.
-  * Set debhelper version back to 9, for easier backport building.
-
-  [ Debian Janitor ]
-  * Upgrade to newer source format.
-
-  [ Jelmer Vernooĳ ]
-  * Set upstream metadata fields: Repository.
-  * Use canonical URL in Vcs-Git.
-  * Add missing debian/rules target build-indep.
-  * Use python3 since python2 is being removed. Closes: #942954
-  * Use system libjsonparser rather than bundled one.
-
- -- Debian Janitor <janitor@jelmer.uk>  Thu, 21 Mar 2019 00:10:27 +0000
-
-bitlbee (3.6-1.1) unstable; urgency=medium
-
-  * Non-maintainer upload.
-  * Apply patch to d/copyright provided by Jochen Sprickerhof
-    Closes: #883872
-
- -- Andreas Tille <tille@debian.org>  Thu, 18 Apr 2019 21:27:48 +0200
-
-bitlbee (3.6-1) unstable; urgency=medium
-
-  [ dequis ]
-  * Remove bitlbee-plugin-skype and skyped (obsolete)
-  * Enable only bitlbee.service, not bitlbee.socket too
-
-  [ dequis ]
-  * Fix lintian pkg-config-unavailable-for-cross-compilation
-
-  [ Jelmer Vernooĳ ]
-  * Add debian upstream metadata file.
-  * Trim trailing whitespace.
-
-  [ Debian Janitor ]
-  * Bump debhelper from old 9 to 10.
-  * Trim trailing whitespace.
-
- -- Wilmer van der Gaast <wilmer@gaast.net>  Sat, 02 Mar 2019 22:18:21 +0000
-
-bitlbee (3.5.1-1.2) unstable; urgency=medium
-
-  * Non-maintainer upload.
-  * Comment out bitlbee-plugin-skype and skyped in d/control (Closes: #921895).
-    These binary packages are not built by d/rules by default, and as such
-    they are not currently in the archive.  Commenting them out in order
-    to avoid my NMU hitting binNEW; this caused the -1.1 source-only
-    upload to fail.
-    Thank you to Mattia Rizzolo for suggesting this fix.
-
- -- Sean Whitton <spwhitton@spwhitton.name>  Sat, 09 Feb 2019 17:03:38 -0700
-
-bitlbee (3.5.1-1.1) unstable; urgency=medium
-
-  * Non-maintainer upload.
-  * Add existence check to chmod call in bitlbee-common.postinst
-    (Closes: #816200).
-
- -- Sean Whitton <spwhitton@spwhitton.name>  Fri, 25 Jan 2019 16:50:34 -0700
-
-bitlbee (3.5.1-1) unstable; urgency=medium
-
-  * Crash bug fix. (Closes: #853282)
-
- -- Wilmer van der Gaast <wilmer@gaast.net>  Thu, 09 Feb 2017 00:46:53 +0000
-
-bitlbee (3.5-2) unstable; urgency=medium
-
-  * TFW you find out the corrected fix diff posted on #821967 was still the
-    broken one and you end up doing your third upload in a weekend. :-(
-    Obviously build-* targets shouldn't call into binary-*, as that one
-    starts requiring root. This broke builds on buildd's.
-  * Now contains the same fix like 3.4.2-1.1, which as it turns out 3.5-1.1
-    did not. (Closes: #853017)
-
- -- Wilmer van der Gaast <wilmer@gaast.net>  Sat, 28 Jan 2017 21:06:27 +0000
-
-bitlbee (3.5-1.1) unstable; urgency=medium
-
-  [ dequis ]
-  * Merge back changes from 3.4.2-1.1 (Closes: #852897)
-
- -- dequis <dx@dxzone.com.ar>  Thu, 26 Jan 2017 21:39:24 -0300
-
-bitlbee (3.5-1) unstable; urgency=medium
-
-  [ Jochen Sprickerhof ]
-  * Drop dependency on net-tools
-
-  [ dequis ]
-  * Use dh_systemd
-  * Drop support for /etc/default/bitlbee
-  * Drop debconf and its translations (only used to ask for the port)
-  * Add preinst script for a smoother transition to systemd units
-  * Ignore the stdout of deluser, fixes postrm error exit code
-  * Remove some mentions of yahoo in control and copyright
-  * Remove outdated stuff from README.debian
-
-  [ Wilmer van der Gaast ]
-  * New upstream release.
-
- -- Wilmer van der Gaast <wilmer@gaast.net>  Tue, 24 Jan 2017 22:21:33 +0000
-
-bitlbee (3.4.2-1.1) unstable; urgency=medium
-
-  * Non-maintainer upload.
-  * Adding build-{arch,indep} targets. (Closes: #821967)
-  * Added python to build depends.
-
- -- Michael Lustfield <michael@lustfield.net>  Mon, 18 Jul 2016 01:02:17 -0700
-
-bitlbee (3.4.2-1) unstable; urgency=medium
-
-  [ Jelmer Vernooĳ ]
-  * Make the build reproducible by not encoding ARCH / CPU defines in
-    the binary.
-  * Fix Vcs-* control headers after migration to Git.
-  * Don't start synopsis with an article.
-  * Bump standards version to 3.9.8 (no changes).
-  * Use dh_prep rather than deprecated 'dh_clean -k'.
-
-  [ Wilmer van der Gaast ]
-  * New upstream release.
-
- -- Wilmer van der Gaast <wilmer@gaast.net>  Sun, 12 Jun 2016 22:31:18 +0100
-
-bitlbee (3.4.1-1) unstable; urgency=medium
-
-  * Fix copyright info (Closes: #764181)
-  * "New" upstream release. Apologies for the delay.
-  * Exclude .git not .bzr when building the source package.
-
- -- Wilmer van der Gaast <wilmer@gaast.net>  Mon, 03 Aug 2015 22:33:25 +0100
-
-bitlbee (3.2.2-1) unstable; urgency=medium
-
-  [ Jelmer Vernooij ]
-  * Bump standards version to 3.9.5 (no changes).
-  * Remove obsolete DM-Upload-Allowed header.
-  * Build with dpkg-flags, for hardening.
-  * Force re-building of user guide on Debian.
-  * Don't ignore failure of invoke-rc.d; fixes lintian warning.
-  * Use my Debian e-mail address in Uploaders.
-  * Support building against libgnutls28-dev. Closes: #753020
-
-  [ Wilmer van der Gaast ]
-  * Merged 3.2.2 with a few later Debian-specific fixes for a belated
-    3.2.2-1 upload. Apologies for the delay!
-  * Removed stale workaround on upgrades from bitlbee <3.0 (with no
-    bitlbee-common package).
-
- -- Wilmer van der Gaast <wilmer@gaast.net>  Sun, 05 Oct 2014 22:11:12 +0100
-
-bitlbee (3.2.1+otr4-1) unstable; urgency=low
-
-  * New upstream version, fixes Twitter API compatibility (now using HTTP/1.1).
-    (Closes: #731748)
-  * Define _GNU_SOURCE via cmdline instead of include file. (Closes: #703550)
-  * Restore OTR support. (Closes: #711071)
-    - This is not part of the 3.2.1 release but really should have been.
-    - Package still has a libotr2-dev dependency even though it doesn't work
-      with that anymore. It's a hack so I can at least still do pbuilder
-      builds on older distros. (Just with the otr pkg disabled.)
-
- -- Wilmer van der Gaast <wilmer@gaast.net>  Thu, 19 Dec 2013 01:11:52 +0100
-
-bitlbee (3.2-1.2) unstable; urgency=low
-
-  [ Cyril Brulebois ]
-  * Disable libotr support (hopefully temporarily) for the transition from
-    libotr2 to libotr5 (Closes: #711071):
-    - debian/control: Drop bitlbee-plugin-otr package.
-    - debian/rules: Adjust DH_OPTIONS handling, drop support for
-      bitlbee-plugin-otr, and pass --otr=0 to configure.
-
-  [ Antoine Beaupré ]
-  * Non-maintainer upload.
-
- -- Antoine Beaupré <anarcat@debian.org>  Tue, 24 Sep 2013 16:04:04 +0200
-
-bitlbee (3.2-1.1) unstable; urgency=low
-
-  * Non-maintainer upload.
-  * Switch the order of (lib)gnutls-dev Build-Depends to libgnutls-dev |
-    gnutls-dev. (Closes: #698236)
-
- -- Sebastian Ramacher <sramacher@debian.org>  Mon, 04 Mar 2013 23:52:07 +0100
-
-bitlbee (3.2-1) unstable; urgency=high
-
-  * New upstream release.
-  * Depend on gnutls-dev instead of libgnutls-dev. (Closes: #691766)
-  * Also, drop nss dependency. GnuTLS is supported officially, the rest is
-    use-at-your-own-risk (so compile it yourself).
-
- -- Wilmer van der Gaast <wilmer@gaast.net>  Mon, 14 Jan 2013 22:34:06 +0000
-
-bitlbee (3.0.6-1) unstable; urgency=low
-
-  * New upstream release.
-
- -- Wilmer van der Gaast <wilmer@gaast.net>  Sun, 28 Oct 2012 19:58:33 +0000
-
-bitlbee (3.0.5-1.2) unstable; urgency=low
-
-  * Non-maintainer upload.
-  * debian/bitlbee{,-dev}.postinst: Fix directory to symlink upgrade in
-    postinst. (Closes: #687865)
-
- -- David Prévot <taffit@debian.org>  Wed, 26 Sep 2012 23:20:41 -0400
-
-bitlbee (3.0.5-1.1) unstable; urgency=low
-
-  * Non-maintainer upload.
-  * Fix "fails to install, purge, and install again":
-    remove --remove-home option from deluser call in bitlbee-common.postrm.
-    The option needs perl-modules which is not guaranteed to be there, and the
-    directory gets removed manually anyway.
-    (Closes: #681146)
-
- -- gregor herrmann <gregoa@debian.org>  Thu, 26 Jul 2012 18:44:36 +0200
-
-bitlbee (3.0.5-1) unstable; urgency=low
-
-  * New upstream release.
-  * Full SSL certificate verification. apt-get install ca-certificates and
-    uncomment the CAfile line in bitlbee.conf to activate it.
-  * Removed some version override stuff from Debian build scripts. Instead,
-    my buildbot now uses dch to generate a changelog entry with the right
-    version number.
-
- -- Wilmer van der Gaast <wilmer@gaast.net>  Sat, 18 Feb 2012 12:25:47 +0100
-
-bitlbee (3.0.4+bzr855-1) unstable; urgency=low
-
-  * New upstream release.
-  * This is not a vanilla 3.0.4 tree but a Bazaar snapshot. The source
-    release was a few weeks ago by now. There should be no significant
-    differences.
-  * Added bitlbee-plugin-skype and skyped packages, now part of BitlBee
-    instead of a separate package. Not building these for Debian for now
-    though since python-skype was removed.
-  * ^B and some other things are stripped in outgoing XMPP stanzas.
-    (Closes: #507856)
-  * OTR module linking fix. Not with the fix from the Debian bug but with
-    one from bugs.bitlbee.org. I hope that covers it. (Closes: #646369)
-  * Closing a few old bugs that were filed against the Debian package
-    instead of/as well as upstream:
-    - Joining password-protected MUCs is working for a while already, set
-      the password using "chan set". (Closes: #615624)
-    - "Headline:" msgs (Closes: #605459)
-    - Yahoo! was fixed long ago and Etch is deprecated. (Closes: #476529)
-    - identi.ca support is documented. (Closes: #613789)
-
- -- Wilmer van der Gaast <wilmer@gaast.net>  Tue, 20 Dec 2011 12:46:42 +0100
-
-bitlbee (3.0.3-1.1) unstable; urgency=low
-
-  * Non-maintainer upload.
-  * Use the standard ${source:Version} and ${binary:Version} substvars instead
-    of the custom and broken ${bee:Version} (closes: #651612).
-
- -- Julien Cristau <jcristau@debian.org>  Thu, 15 Dec 2011 20:34:32 +0100
-
-bitlbee (3.0.3-1) unstable; urgency=low
-
-  * New upstream release. (Skipped 3.0.2, sorry!)
-  * Fixes Twitter issues.
-
- -- Wilmer van der Gaast <wilmer@gaast.net>  Tue, 14 Jun 2011 22:39:22 +0100
-
-bitlbee (3.0.1-1) unstable; urgency=low
-
-  * New upstream release.
-  * Includes changes from non-maintainer, see below. (Closes: #603681)
-
- -- Wilmer van der Gaast <wilmer@gaast.net>  Sat, 04 Dec 2010 11:50:53 +0000
-
-bitlbee (3.0-2) UNRELEASED; urgency=low
-
-  * debian/bitlbee-common.postinst: Replace 'type' with 'which' to avoid
-    bashism.
-  * debian/{*.prerm, *.postinst, *.postrm, *.config}: use set -e in the body
-    of maintainer scripts.
-  * debian/control:
-    + Corrected short description of bitlbee-plugin-otr
-    + Bumped Standards-Version to 3.9.1
-    + Added ${misc:Depends} for bitlbee & bitlbee-libpurple
-    + Moved dependency on adduser to bitlbee-common
-    + Bumped debhelper version to 6.0.7~ for dh_lintian
-  * debian/copyright: Refer to /usr/share/common-licenses/GPL-2
-  * Added lintian overrides for bitlbee & bitlbee-libpurple
-  * Explicitly set source format to 1.0
-  * debian/bitlbee-common.init:
-    + Add Short-Description header.
-    + Also stop at runlevels 0 and 6.
-
- -- أحمد المحمودي (Ahmed El-Mahmoudy) <aelmahmoudy@sabily.org>  Mon, 25 Oct 2010 16:56:24 +0200
-
-bitlbee (3.0-1) unstable; urgency=low
-
-  * A milestone upstream release. See the non-Debian changelog.
-  * Now using debhelper to improve maintainability.
-  * Added a bitlbee-libpurple package, and split off docs and stuff into
-    bitlbee-common.
-  * Added a bitlbee-plugin-otr package. (Closes: #541809)
-  * Can use IPv6 on the client side now. (Closes: #547300)
-  * Added Danish debconf translations done by Joe Dalton. (Closes: #592259)
-
- -- Wilmer van der Gaast <wilmer@gaast.net>  Wed, 18 Aug 2010 00:53:11 +0100
-
-bitlbee (1.2.8-1) unstable; urgency=low
-
-  * New upstream version.
-
- -- Wilmer van der Gaast <wilmer@gaast.net>  Sat, 10 Jul 2010 13:54:55 +0100
-
-bitlbee (1.2.7-1) unstable; urgency=high
-
-  * New upstream version.
-  * Fixes MSN Messenger login issues, so uploading at high priority.
-
- -- Wilmer van der Gaast <wilmer@gaast.net>  Sat, 15 May 2010 16:15:57 +0100
-
-bitlbee (1.2.6a-1) unstable; urgency=low
-
-  * New upstream version.
-  * Native support for Twitter.
-  * Fixed /WHOIS response format. (Closes: #576120)
-  * Problems with bitlbee-skype are solved by now. (Closes: #575572)
-
- -- Wilmer van der Gaast <wilmer@peer.gaast.net>  Tue, 20 Apr 2010 00:34:51 +0200
-
-bitlbee (1.2.5-1) unstable; urgency=low
-
-  * New upstream version.
-  * Fixed issues with server-side MSN nickname corruption. (Closes: #538756)
-  * Debconf translation fixes/additions. (Closes: #541754, #563504)
-
- -- Wilmer van der Gaast <wilmer@gaast.net>  Wed, 17 Mar 2010 14:59:27 +0000
-
-bitlbee (1.2.4-2) unstable; urgency=low
-
-  * Merging in some changes from bzr-head:
-  * Use libresolv.so where possible. (Closes: #551775)
-  * Some include file changes that make the bitlbee-dev package useful again.
-
- -- Wilmer van der Gaast <wilmer@gaast.net>  Thu, 19 Nov 2009 23:02:43 +0000
-
-bitlbee (1.2.4-1) unstable; urgency=low
-
-  * New upstream version.
-  * Fixed issues with Yahoo! (Closes: #536178)
-
- -- Wilmer van der Gaast <wilmer@gaast.net>  Sat, 17 Oct 2009 18:12:45 +0100
-
-bitlbee (1.2.3-2) unstable; urgency=low
-
-  * Fixed bitblee typo in prerm (introduced by NMU 1.2.1-1.1).
-    (Closes: #531287)
-  * Fixed bitlbee.deb dep in bitlbee-dev to deal with binary NMUs.
-    (Closes: #531219)
-  * Fixed free port detection code in debian/config which was a bit limited
-    and also buggy.
-  * Removing code that edits bitlbee.conf from postinst (and chown code in
-    the init script), it's not really necessary anymore; bitlbee may only
-    still run as root if the admin doesn't read conffile diffs.
-    (Closes: #514572)
-  * No longer overwriting port number info in /etc/default/bitlbee with
-    what's in debconf. (Closes: #514148)
-  * Added notes about the above two changes to bitlbee.conf.
-
- -- Wilmer van der Gaast <wilmer@gaast.net>  Sun, 07 Jun 2009 21:17:39 +0100
-
-bitlbee (1.2.3-1) unstable; urgency=critical
-
-  * New upstream version.
-  * Fixes another account hijacking issue. (Closes: #498159)
-  * Restored --pidfile argument to start-stop-daemon, otherwise the init
-    script fails to restart BitlBee when users are connected.
-
- -- Wilmer van der Gaast <wilmer@gaast.net>  Sun, 07 Sep 2008 18:53:04 +0100
-
-bitlbee (1.2.2-1) unstable; urgency=critical
-
-  * New upstream version.
-  * Fixes a security issue (account hijacking), hence the high priority.
-  * Using a patch to set the User setting in bitlbee.conf properly to keep
-    upstream and Debian properly separated in my bzr tree.
-
- -- Wilmer van der Gaast <wilmer@gaast.net>  Wed, 27 Aug 2008 23:59:50 +0100
-
-bitlbee (1.2.1-3) unstable; urgency=high
-
-  * chown /var/lib/bitlbee/*.xml to bitlbee:bitlbee to clean up after
-    1.2-5 and the bugfix in 1.2.1-2. (Closes: #495877)
-  * Moved BITLBEE_DISABLED check to only check when trying to *start*
-    the daemon. (Closes: #488611)
-
- -- Wilmer van der Gaast <wilmer@gaast.net>  Sat, 23 Aug 2008 18:53:54 +0100
-
-bitlbee (1.2.1-2) unstable; urgency=low
-
-  * Properly set the User= line to something sensible so BitlBee won't
-    run as root anymore. 1.2-5 was a bad upload. :-( (Closes: #494656)
-
- -- Wilmer van der Gaast <wilmer@gaast.net>  Tue, 12 Aug 2008 00:36:03 +0100
-
-bitlbee (1.2.1-1.1) unstable; urgency=low
-
-  * Non-Maintainer Upload
-  * Use invoke-rc.d as per policy. (Closes: #492637) [Thanks to Matt
-    Kraai]
-
- -- Don Armstrong <don@debian.org>  Wed, 06 Aug 2008 06:57:18 -0700
-
-bitlbee (1.2.1-1) unstable; urgency=low
-
-  * New upstream release.
-  * Add Homepage and Vcs-Bzr fields. (From Jelmer.)
-
- -- Wilmer van der Gaast <wilmer@gaast.net>  Thu, 26 Jun 2008 00:07:50 +0100
-
-bitlbee (1.2-5) unstable; urgency=low
-
-  * Add myself to uploaders.
-  * Bump standards version to 3.8.0.
-  * Fix FSF address.
-  * Avoid changing uid from init script. (Closes: #474589)
-
- -- Jelmer Vernooij <jelmer@samba.org>  Mon, 16 Jun 2008 00:53:20 +0200
-
-bitlbee (1.2-4) unstable; urgency=low
-
-  * Fixed init script to use the BITLBEE_OPTS variable, not an undefined
-    DAEMON_OPT. (Closes: #474583)
-  * Added dependency information to the init script. (Closes: #472567)
-  * Added bitlbee-dev package. Patch from RISKO Gergely <risko@debian.org>
-    with some small modifications. (Closes: #473480)
-
- -- Wilmer van der Gaast <wilmer@gaast.net>  Wed, 07 May 2008 22:40:40 -0700
-
-bitlbee (1.2-3) unstable; urgency=low
-
-  * Removed DEB_BUILD_OPTIONS again (forgot to apply that change to the 1.2
-    branch when I finished 1.0.4-2, things diverged too much anyway.)
-    Closes: #472540.
-
- -- Wilmer van der Gaast <wilmer@gaast.net>  Mon, 24 Mar 2008 21:10:14 +0000
-
-bitlbee (1.2-2) unstable; urgency=low
-
-  * Fixed some packaging issues reported by IRC and e-mail. (Closes: #472373)
-  * Fixed proxy support. (Closes: #472395)
-  * Added a BitlBee group so only root can edit the configs and BitlBee can
-    just *read* it.
-  * Manually deleting /var/lib/bitlbee/ when purging, deluser doesn't want to
-    do it.
-
- -- Wilmer van der Gaast <wilmer@gaast.net>  Mon, 24 Mar 2008 19:48:24 +0000
-
-bitlbee (1.2-1) unstable; urgency=low
-
-  * New upstream release. (Closes: #325017, #386914, #437515)
-  * With hopefully completely sane charset handling (Closes: #296145)
-  * Switched to the new forking daemon mode. Added /etc/default/bitlbee
-    file, an init script. People who want to stick with inetd can do so, see
-    the defaults file.
-    (Closes: #460741, #466171, #294585, #345038, #306452, #392682)
-  * Got rid of debconf Woody compatibility stuff.
-  * No more MPL code in BitlBee, thanks to the Jabber module rewrite!
-  * Added Italian translation, sorry for taking so long! (Closes: #448238)
-  * Added libevent dependency (more reliable event handling).
-  * Removed GLib 1.x dependency because BitlBee really requires GLib >=2.4.
-
- -- Wilmer van der Gaast <wilmer@gaast.net>  Tue, 18 Mar 2008 23:44:19 +0000
-
-bitlbee (1.0.4-2) unstable; urgency=low
-
-  * Removed $DEB_BUILD_OPTIONS because apparently buildds fill it with crap.
-    (Closes: #458717)
-
- -- Wilmer van der Gaast <wilmer@gaast.net>  Mon, 11 Feb 2008 19:15:33 +0000
-
-bitlbee (1.0.4-1) unstable; urgency=low
-
-  * New upstream release.
-  * Changed libnss-dev dependency. (Closes: #370442)
-  * Added build-indep rule to debian/rules. (Closes: #395673)
-
- -- Wilmer van der Gaast <wilmer@gaast.net>  Wed, 29 Aug 2007 20:24:28 +0100
-
-bitlbee (1.0.3-1.3) unstable; urgency=low
-
-  * Non-maintainer upload to fix a minor error.
-  * Remove extra debian/#rules#
-
- -- Christian Perrier <bubulle@debian.org>  Tue, 20 Feb 2007 07:49:18 +0100
-
-bitlbee (1.0.3-1.2) unstable; urgency=low
-
-  * Non-maintainer upload to fix pending l10n issues.
-  * Debconf translations:
-    - Portuguese. Closes: #386348
-
- -- Christian Perrier <bubulle@debian.org>  Sun, 18 Feb 2007 20:23:28 +0100
-
-bitlbee (1.0.3-1.1) unstable; urgency=low
-
-  * Non-maintainer upload.
-  * debian/control: Replace netkit-inetd dependency with a versioned
-    dependency on netbase.  netbase provides the appropriate inetd
-    dependency.  Closes: #382682.
-
- -- Roger Leigh <rleigh@debian.org>  Sun, 20 Aug 2006 17:07:02 +0100
-
-bitlbee (1.0.3-1) unstable; urgency=low
-
-  * New upstream release.
-
- -- Wilmer van der Gaast <wilmer@gaast.net>  Sat,  8 Jul 2006 11:32:57 +0200
-
-bitlbee (1.0.2-2) unstable; urgency=low
-
-  * Added a po-debconf build-dependency, which I forgot when removing the
-    Woody hack from 1.0.2-1. (Closes: #361503)
-
- -- Wilmer van der Gaast <wilmer@gaast.net>  Sat,  8 Apr 2006 22:09:19 +0200
-
-bitlbee (1.0.2-1) unstable; urgency=low
-
-  * New upstream release.
-  * Avoids calling update-inetd if it's unavailable. (Closes: #350463)
-  * Also using userdel instead of deluser now to avoid another bug. :-)
-  * Only creates an inetd.conf entry when installing for the first time.
-    (Closes: #349570) (This unfortunately breaks dpkg-reconfigure, but at
-    least there's a warning...)
-
- -- Wilmer van der Gaast <wilmer@gaast.net>  Sat,  8 Apr 2006 14:17:52 +0200
-
-bitlbee (1.0.1-1) unstable; urgency=low
-
-  * New upstream release
-
- -- Wilmer van der Gaast <wilmer@gaast.net>  Tue, 17 Jan 2006 17:26:20 +0100
-
-bitlbee (1.0-1) unstable; urgency=low
-
-  * New upstream release
-  * bitlbee has a useless Build-Depends: on debconf-2.0 (Closes:
-    #341783)
-
- -- Wilmer van der Gaast <wilmer@gaast.net>  Mon,  5 Dec 2005 17:59:07 +0100
-
 bitlbee (0.99-1) unstable; urgency=low
 
@@ -720,5 +151,5 @@
   * New upstream release.
 
- -- Wilmer van der Gaast <lintux@debian.org>  Thu, 16 Oct 2003 16:21:31 +0200
+ -- Wilmer van der Gaast <lintux@debian.org>  Wed, 16 Oct 2003 16:21:31 +0200
 
 bitlbee (0.81-1) unstable; urgency=low
@@ -799,2 +230,6 @@
 
  -- Wilmer van der Gaast <lintux@debian.org>  Wed, 10 Jul 2002 11:02:28 +0200
+
+Local variables:
+mode: debian-changelog
+End:
Index: bian/compat
===================================================================
--- debian/compat	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,1 +1,0 @@
-9
Index: debian/control
===================================================================
--- debian/control	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ debian/control	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -3,68 +3,11 @@
 Priority: optional
 Maintainer: Wilmer van der Gaast <wilmer@gaast.net>
-Uploaders: Jelmer Vernooĳ <jelmer@debian.org>
-Standards-Version: 3.9.8
-Build-Depends: libglib2.0-dev (>= 2.4), libevent-dev, libgnutls28-dev | libgnutls-dev | gnutls-dev, po-debconf, libpurple-dev, libotr5-dev, debhelper (>= 9), python3, libjsonparser-dev
-Homepage: https://www.bitlbee.org/
-Vcs-Git: https://github.com/bitlbee/bitlbee.git
-Vcs-Browser: https://github.com/bitlbee/bitlbee
+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: ${misc:Depends}, ${shlibs:Depends}, debianutils (>= 1.16), bitlbee-common (= ${binary:Version})
-Conflicts: bitlbee-libpurple
-Replaces: bitlbee-libpurple
-Description: IRC to other chat networks gateway (default version)
+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 (which includes Google
- Talk), and Twitter.
-
-Package: bitlbee-libpurple
-Architecture: any
-Depends: ${misc:Depends}, ${shlibs:Depends}, debianutils (>= 1.16), bitlbee-common (= ${binary:Version})
-Conflicts: bitlbee
-Replaces: bitlbee
-Description: IRC to other chat networks gateway (using libpurple)
- This program can be used as an IRC server which forwards everything you
- say to people on other chat networks: Jabber (which includes Google
- Talk), and Twitter.
- .
- This package contains a version of BitlBee that uses the libpurple instant
- messaging library instead of built-in code, which adds support for more IM
- protocols (all protocols supported by Pidgin/Finch) and features (like file
- transfers), at the price of being less lightweight.
- .
- If you don't need the extra features, it's better to use the built-in code.
-
-Package: bitlbee-common
-Architecture: any
-Depends: ${misc:Depends}, adduser
-Replaces: bitlbee
-Description: IRC to other chat networks gateway (common files/docs)
- This program can be used as an IRC server which forwards everything you
- say to people on other chat networks: Jabber (which includes Google
- Talk), and Twitter.
- .
- This package contains common files (mostly documentation) for bitlbee and
- bitlbee-libpurple.
-
-Package: bitlbee-dev
-Architecture: any
-Depends: ${misc:Depends}, bitlbee (= ${binary:Version}) | bitlbee-libpurple (= ${binary:Version}), bitlbee-common (= ${binary:Version})
-Description: IRC to other chat networks gateway (dev files)
- This program can be used as an IRC server which forwards everything you
- say to people on other chat networks: Jabber (which includes Google
- Talk), and Twitter.
- .
- This package holds development stuff for compiling plug-ins.
-
-Package: bitlbee-plugin-otr
-Architecture: any
-Depends: ${misc:Depends}, ${shlibs:Depends}, bitlbee (= ${binary:Version}) | bitlbee-libpurple (= ${binary:Version}), bitlbee-common (= ${binary:Version})
-Description: IRC to other chat networks gateway (OTR plugin)
- This program can be used as an IRC server which forwards everything you
- say to people on other chat networks: Jabber (which includes Google
- Talk), and Twitter.
- .
- This package contains a plugin that adds support for Off-The-Record
- encryption of instant messages.
+ say to people on other chat networks: Jabber, ICQ, AIM, MSN and Yahoo.
Index: debian/copyright
===================================================================
--- debian/copyright	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ debian/copyright	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -1,196 +1,895 @@
-Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
-
-Files: *
-Copyright: 
-           2002-2006, Jelmer Vernooij <jelmer@samba.org>
-           2001-2013, Wilmer 'Lintux' van der Gaast <wilmer@gaast.net>
-License: GPL-2+
-
-Files: lib/misc.c
-       lib/proxy.?
-       protocols/nogaim.h
-Copyright: 
-           1998-1999, Mark Spencer <markster@marko.net>
-           2002-2006, Jelmer Vernooij <jelmer@samba.org>
-           2001-2013, Wilmer 'Lintux' van der Gaast <wilmer@gaast.net>
-License: GPL-2+
-
-Files: otr.?
-Copyright: 2008-2013, Sven Moritz Hallberg <pesco@khjk.org>
-License: GPL-2+
-
-Files: dcc.?
-       lib/ftutil.?
-       protocols/jabber/si.c
-       protocols/jabber/s5bytestream.c
-Copyright: 2007-2008, Uli Meis <a.sporto+bee@gmail.com>
-License: GPL-2+
-
-Files: dcc.h
-       protocols/ft.h
-Copyright: 
-           2006, Marijn Kruisselbrink
-           2007-2008, Uli Meis <a.sporto+bee@gmail.com>
-License: GPL-2+
-
-Files: protocols/jabber/hipchat.c
-Copyright: 2015, Xamarin Inc
-License: GPL-2+
-
-Files: lib/canohost.c
-Copyright: 1995, Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
-License: OpenSSH
-
-Files: configure
-Copyright: 
-           2007-2013, Miklos Vajna <vmiklos@vmiklos.hu>
-           2002, Sjoerd 'lucumo' Hemminga <sjoerd@hemminga-online.nl>
-           2001-2013, Wilmer 'Lintux' van der Gaast <wilmer@gaast.net>
-License: GPL-2+
-
-Files: protocols/twitter/*
-Copyright: 2009-2010, Geert Mulders <g.c.w.m.mulders@gmail.com>
-           2002-2013, Wilmer 'Lintux' van der Gaast <wilmer@gaast.net>
-License: LGPL-2.1
-
-Files: lib/json.?
-Copyright: 2012-2014, James McLaughlin
-License: BSD-2-clause
-
-Files: lib/ns_parse.c
-Copyright: 1996-1999, Internet Software Consortium
-           2004, Internet Systems Consortium, Inc. ("ISC")
-License: ISC
-
-Files: debian/*
-Copyright: 2002-2019, Wilmer van der Gaast <wilmer@gaast.net>
-License: GPL-2+
-
-License: BSD-2-clause
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- .
- THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- SUCH DAMAGE.
-
-License: GPL-2+
- This program is free software; you can redistribute it and/or modify it under
- the terms of the GNU General Public License as published by the Free Software
- Foundation; either version 2 of the License, or (at your option) any later
- version.
- .
- This program is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
- .
- You should have received a copy of the GNU General Public License along with
- this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
- St, Fifth Floor, Boston, MA 02110-1301 USA
- .
- On Debian systems, the complete text of the GNU General Public License version 2
- can be found in /usr/share/common-licenses/GPL-2.
-
-License: ISC
- Permission to use, copy, modify, and/or distribute this software for any
- purpose with or without fee is hereby granted, provided that the above
- copyright notice and this permission notice appear in all copies.
- .
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-License: LGPL-2.1
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License version 2.1 as published by the Free Software Foundation.
- .
- This 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- .
- On Debian systems, the complete text of the GNU Lesser General Public
- License can be found in /usr/share/common-licenses/LGPL-2.1 file.
-
-License: OpenSSH
- Tatu Ylonen's original licence is as follows (excluding some terms about
- third-party code which are no longer relevant; see the LICENCE file for
- details):
- .
-   As far as I am concerned, the code I have written for this software
-   can be used freely for any purpose.  Any derived versions of this
-   software must be clearly marked as such, and if the derived work is
-   incompatible with the protocol description in the RFC file, it must be
-   called by a name other than "ssh" or "Secure Shell".
- .
-   Note that any information and cryptographic algorithms used in this
-   software are publicly available on the Internet and at any major
-   bookstore, scientific library, and patent office worldwide.  More
-   information can be found e.g. at "http://www.cs.hut.fi/crypto".
- .
-   The legal status of this program is some combination of all these
-   permissions and restrictions.  Use only at your own responsibility.
-   You will be responsible for any legal consequences yourself; I am not
-   making any claims whether possessing or using this is legal or not in
-   your country, and I am not taking any responsibility on your behalf.
- .
- Most remaining components of the software are provided under a standard
- 2-term BSD licence:
- .
-   Redistribution and use in source and binary forms, with or without
-   modification, are permitted provided that the following conditions
-   are met:
-   1. Redistributions of source code must retain the above copyright
-      notice, this list of conditions and the following disclaimer.
-   2. Redistributions in binary form must reproduce the above copyright
-      notice, this list of conditions and the following disclaimer in the
-      documentation and/or other materials provided with the distribution.
- .
-   THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
-   IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-   IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
-   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- .
- Some code is licensed under an ISC-style license, to the following
- copyright holders:
- .
-  Permission to use, copy, modify, and distribute this software for any
-  purpose with or without fee is hereby granted, provided that the above
-  copyright notice and this permission notice appear in all copies.
-  .
-  THE SOFTWARE IS PROVIDED "AS IS" AND TODD C. MILLER DISCLAIMS ALL
-  WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
-  OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL TODD C. MILLER BE LIABLE
-  FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
-  OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
-  CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+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.
+============================================================================
Index: bian/lintian-brush.conf
===================================================================
--- debian/lintian-brush.conf	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,2 +1,0 @@
-# Be at least compatible with trusty, for backports.
-compat-release = trusty
Index: debian/motd.txt
===================================================================
--- debian/motd.txt	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ debian/motd.txt	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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?
Index: bian/patches/bitlbee.conf.diff
===================================================================
--- debian/patches/bitlbee.conf.diff	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,22 +1,0 @@
---- bitlbee.conf	2009-06-01 00:20:24.000000000 +0100
-+++ bitlbee.conf	2009-06-07 21:16:19.000000000 +0100
-@@ -23,13 +23,18 @@
- ## If BitlBee is started by root as a daemon, it can drop root privileges,
- ## and change to the specified user.
- ##
--# User = bitlbee
-+## DEBIAN NOTE: Without this, BitlBee will run as root!
-+##
-+User = bitlbee
- 
- ## DaemonPort/DaemonInterface:
- ##
- ## For daemon mode, you can specify on what interface and port the daemon
- ## should be listening for connections.
- ##
-+## DEBIAN NOTE: The init script passes the -p flag to use the port number
-+## set using debconf, this overrides the DaemonPort setting here.
-+##
- # DaemonInterface = 0.0.0.0
- # DaemonPort = 6667
- 
Index: debian/po/POTFILES.in
===================================================================
--- debian/po/POTFILES.in	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ debian/po/POTFILES.in	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,1 @@
+[type: gettext/rfc822deb] bitlbee.templates.master
Index: debian/po/cs.po
===================================================================
--- debian/po/cs.po	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ debian/po/cs.po	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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."
Index: debian/po/de.po
===================================================================
--- debian/po/de.po	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ debian/po/de.po	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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."
Index: debian/po/es.po
===================================================================
--- debian/po/es.po	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ debian/po/es.po	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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."
+
Index: debian/po/fr.po
===================================================================
--- debian/po/fr.po	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ debian/po/fr.po	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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."
Index: debian/po/ja.po
===================================================================
--- debian/po/ja.po	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ debian/po/ja.po	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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 ¤Ï¼«Æ°Åª¤Ë¤Ïµ¯Æ°¤Ê¤¯¤Ê¤ê¤Þ¤¹¡£"
Index: debian/po/nl.po
===================================================================
--- debian/po/nl.po	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ debian/po/nl.po	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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."
Index: debian/po/pt_BR.po
===================================================================
--- debian/po/pt_BR.po	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ debian/po/pt_BR.po	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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."
Index: debian/po/sv.po
===================================================================
--- debian/po/sv.po	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ debian/po/sv.po	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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."
+
Index: debian/po/templates.pot
===================================================================
--- debian/po/templates.pot	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ debian/po/templates.pot	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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 ""
Index: debian/po/vi.po
===================================================================
--- debian/po/vi.po	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ debian/po/vi.po	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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."
Index: debian/rules
===================================================================
--- debian/rules	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ debian/rules	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -1,128 +1,100 @@
 #!/usr/bin/make -f
-#
-# Finally switching to debhelper.
-#
-# Not using debhelper was an exercise suggested to me by my AM (Gergely
-# Nagy). It was educating at the time but I finally decided that the
-# exercise is over now.
-#
 
-# Include the bitlbee-libpurple variant and OTR plugin by default.
-BITLBEE_LIBPURPLE ?= 1
-BITLBEE_OTR ?= plugin
-BITLBEE_CONFIGURE_FLAGS ?=
 DEBUG ?= 0
 
-ifndef BITLBEE_VERSION
-# Want to use the full package version number instead of just the release.
-BITLBEE_CONFIGURE_VERSION ?= BITLBEE_VERSION="$(shell dpkg-parsechangelog | awk '/^Version:/ {print $$2}')"
+ifeq (,$(wildcard /usr/bin/po2debconf))
+PO2DEBCONF := no
+MINDEBCONFVER := 0.5
+else
+PO2DEBCONF := yes
+MINDEBCONFVER := 1.2.0
 endif
 
-ifneq ($(BITLBEE_LIBPURPLE),1)
-DH_OPTIONS += -Nbitlbee-libpurple
+
+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
 
-ifneq ($(BITLBEE_OTR),plugin)
-DH_OPTIONS += -Nbitlbee-plugin-otr
-endif
+	dpkg --build debian/bitlbee ..
 
-CPPFLAGS:=$(shell dpkg-buildflags --get CPPFLAGS)
-CFLAGS:=$(shell dpkg-buildflags --get CFLAGS)
-LDFLAGS:=$(shell dpkg-buildflags --get LDFLAGS)
-
-CONFIGURE_OVERRIDES:=CPPFLAGS="$(CPPFLAGS)" CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)"
-
-HAS_DH_SYSTEMD:=$(shell dpkg-query -W -f='$${Status}' dh-systemd 2>/dev/null | grep -c "ok installed")
-
-build-arch: build
-
-build: build-stamp
-build-stamp:
-	dh_testdir
-
-	mkdir -p debian/build-native
-	ROOT=$$PWD; cd debian/build-native; $(BITLBEE_CONFIGURE_VERSION) $(CONFIGURE_OVERRIDES) $$ROOT/configure --debug=$(DEBUG) --prefix=/usr --etcdir=/etc/bitlbee --events=libevent --otr=$(BITLBEE_OTR) --systemdsystemunitdir=/lib/systemd/system --pcdir=/usr/lib/${DEB_HOST_MULTIARCH}/pkgconfig $(BITLBEE_CONFIGURE_FLAGS)
-	$(MAKE) -C debian/build-native
-
-ifeq ($(BITLBEE_LIBPURPLE),1)
-	mkdir -p debian/build-libpurple
-	ROOT=$$PWD; cd debian/build-libpurple; $(BITLBEE_CONFIGURE_VERSION) $(CONFIGURE_OVERRIDES) $$ROOT/configure --debug=$(DEBUG) --prefix=/usr --etcdir=/etc/bitlbee --systemdsystemunitdir=/lib/systemd/system --pcdir=/usr/lib/${DEB_HOST_MULTIARCH}/pkgconfig --purple=1 $(BITLBEE_CONFIGURE_FLAGS)
-	$(MAKE) -C debian/build-libpurple
-endif
-
-	$(MAKE) -C doc
-	$(MAKE) -C doc/user-guide
-
-	touch build-stamp
-
-clean:
-	dh_testdir
-	dh_testroot
-	rm -f build-stamp
-
-	rm -rf build-arch-stamp debian/build-* debian/bitlbee-libpurple.prerm
-	$(MAKE) distclean
-
-	dh_clean
-
-install: build
-	dh_testdir
-	dh_testroot
-	dh_prep
-	dh_installdirs
-
-	$(MAKE) -C debian/build-native install-bin DESTDIR=`pwd`/debian/bitlbee
-	$(MAKE) -C debian/build-native install-etc install-doc DESTDIR=`pwd`/debian/bitlbee-common
-	$(MAKE) -C debian/build-native install-dev DESTDIR=`pwd`/debian/bitlbee-dev
-	$(MAKE) -C debian/build-native install-plugin-otr DESTDIR=`pwd`/debian/bitlbee-plugin-otr
-
-ifeq ($(HAS_DH_SYSTEMD),1)
-	$(MAKE) -C debian/build-native install-systemd DESTDIR=`pwd`/debian/bitlbee-common
-endif
-
-ifeq ($(BITLBEE_LIBPURPLE),1)
-	$(MAKE) -C debian/build-libpurple install-bin DESTDIR=`pwd`/debian/bitlbee-libpurple
-	ln -sf debian/bitlbee.prerm debian/bitlbee-libpurple.prerm
-endif
-
-	patch debian/bitlbee-common/etc/bitlbee/bitlbee.conf debian/patches/bitlbee.conf.diff
-	chmod 640 debian/bitlbee-common/etc/bitlbee/bitlbee.conf
-
-binary-common:
-	dh_testdir
-	dh_testroot
-
-	dh_installexamples
-	dh_installdocs --link-doc=bitlbee-common
-	dh_installchangelogs doc/CHANGES
-	dh_installdebconf
-ifeq ($(HAS_DH_SYSTEMD),1)
-	dh_systemd_enable -p bitlbee-common --no-enable bitlbee.socket
-	dh_systemd_enable -p bitlbee-common bitlbee.service
-	dh_installinit -p bitlbee-common --init-script=bitlbee
-	dh_systemd_start -p bitlbee-common
-else
-	dh_installinit -p bitlbee-common --init-script=bitlbee
-endif
-	dh_installman
-	dh_lintian
-	dh_strip
-	dh_link
-	dh_compress
-	dh_fixperms
-	dh_installdeb
-	dh_shlibdeps
-	dh_gencontrol
-	dh_md5sums
-	dh_builddeb
-
-binary-arch: build install
-	DH_OPTIONS=-a $(MAKE) -f debian/rules binary-common
-
-binary-%: build install
-	DH_OPTIONS=-p$* $(MAKE) -f debian/rules binary-common
+debug-build:
+	BITLBEE_VERSION=\"`date +%Y%m%d`-`hostname`-debug\" debian/rules clean binary DEBUG=1
 
 binary: binary-arch
-.PHONY: build clean binary-arch binary-common binary install build-indep
+build: build-arch
+install: install-arch
 
-build-indep:
+.PHONY: build-arch build clean binary-arch binary install-arch install
Index: bian/source/format
===================================================================
--- debian/source/format	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,1 +1,0 @@
-3.0 (quilt)
Index: bian/source/options
===================================================================
--- debian/source/options	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,1 +1,0 @@
---diff-ignore='(^|/)\.git'
Index: bian/upstream/metadata
===================================================================
--- debian/upstream/metadata	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,8 +1,0 @@
-Repository: https://github.com/bitlbee/bitlbee.git
-Repository-Browse: https://github.com/bitlbee/bitlbee
-Homepage: https://www.bitlbee.org/
-Bug-Database: https://bugs.bitlbee.org/
-Bug-Submit: https://bugs.bitlbee.org/newticket
-Documentation: https://wiki.bitlbee.org/
-FAQ: https://wiki.bitlbee.org/FAQ
-Donation: https://www.bitlbee.org/main.php/paypal.html
Index: bian/watch
===================================================================
--- debian/watch	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,2 +1,0 @@
-version=2
-https://get.bitlbee.org/src/bitlbee-(.*).tar.gz
Index: debug.c
===================================================================
--- debug.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ debug.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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));
+}
Index: debug.h
===================================================================
--- debug.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ debug.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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
+
Index: doc/AUTHORS
===================================================================
--- doc/AUTHORS	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ doc/AUTHORS	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -1,26 +1,8 @@
-Core team:
+Current developers:
 
 Wilmer van der Gaast <wilmer@gaast.net>
-	BDFL, patch committer, etc.
+	Main developer
 
-dequis <dx@dxzone.com.ar>
-	Mysterious maintainer
-
-
-Other contributors:
-
-Miklos Vajna <vmiklos@vmiklos.hu>
-	Skype module
-
-pesco <pesco@khjk.org>
-	OTR support
-
-
-Retired developers:
-
-Sjoerd 'lucumo' Hemminga <sjoerd@hemminga-online.nl>
-	NickServ, documentation
-
-Jelmer Vernooĳ <jelmer@jelmer.uk>
+Jelmer 'ctrlsoft' Vernooij <jelmer@nl.linux.org>
 	Documentation, general hacking, Win32 port
 
@@ -28,8 +10,10 @@
 	Daemon dude, plus some other stuff
 
-Geert Mulders <g.c.w.m.mulders@gmail.com>
-	Initial version of the Twitter module
 
-ulim <a.sporto+bee@gmail.com>
-Marijn Kruisselbrink
-	File transfer support
+Retired developer:
+
+Sjoerd 'lucumo' Hemminga <sjoerd@hemminga-online.nl>
+	NickServ, documentation
+
+The development team wishes to thank Sjoerd for his contributions to the
+Bee.
Index: doc/CHANGES
===================================================================
--- doc/CHANGES	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ doc/CHANGES	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -1,796 +1,2 @@
-This ChangeLog mostly lists changes relevant to users. A full log can be
-found in the git commit logs, for example you can try:
-
-https://github.com/bitlbee/bitlbee/commits/master
-
-Version 3.6:
-
-- irc/core:
-  * Add server-time IRCv3 capability (blastrock)
-  * Add PROXY command for haproxy/stunnel
-  * Large performance improvements for large contact lists
-  * Many UX/documentation improvements
-  * Added built-in crash handler that writes to /var/lib/bitlbee/crash.log
-  * Try to join long spaceless lines in paste_buffer without a newline. The
-    main use case for this is pasting long URLs and not breaking them
-  * Fix status message being set to null accidentally
-  * Fix handling utf8 nick renames when loading configs (MaskRay)
-  * Fix SSL's SNI with hostnames starting with a digit (CMB)
-  * Show correct nick when `rename -del` is used (arcnmx)
-
-- twitter:
-  * Disable the stream setting by default. Filter streams still work.
-  * Update default character limit to 280 chars (qyliss)
-  * Fix quote tweet url display.
-
-- jabber:
-  * Try to join anyway after "Already present in chat"
-  * Fix chat joins when ext_jid is provided for your own user.
-    Seen with Biboumi (a gateway from XMPP to IRC)
-  * Handle always_use_nicks more gracefully to reduce nick change noise
-
-- otr:
-  * Don't block attempts to connect/smp/smpq to "offline" users
-
-- Removed dead protocols:
-  * msn: Use the skypeweb purple plugin instead.
-  * skype (the dbus based thing): ditto.
-  * yahoo: It's so dead even the replacement protocol died.
-  * oscar: AIM is dead, for ICQ use the icyque purple plugin instead.
-
-- For plugin devs:
-  * Add datadir to pkgconfig file and config.h (sm00th)
-  * Add "bitlbee-set-account-password" purple signal (for hangouts)
-  * Support libpurple 2.12.0's PURPLE_MESSAGE_REMOTE_SEND for groupchat
-    self-messages (for slack)
-
-- Packaging/distro specific stuff:
-  * bitlbee@.service now sends stderr to syslog instead of the socket
-  * debian: only enable bitlbee.service, not bitlbee.socket too
-  * cygwin: portability fixes for plugins
-  * Support OpenSSL 1.1 built without backwards compat (cotequeiroz)
-
-Finished 7 Feb 2019
-
-Version 3.5.1:
-
-- purple:
-  * Fix crash on file transfer requests from unknown contacts. This was the
-    result of an incomplete fix in the previous release and may result in
-    remote DoS. Read the full security advisory at:
-    https://bugs.bitlbee.org/ticket/1282
-- After some investigation we decided to reclassify a crash fix from the
-  previous release as a security issue. Read the full security advisory at:
-  https://bugs.bitlbee.org/ticket/1281
-- Included help.txt in the release tarball, which was missing in the previous
-  release and resulted in adding python as a build dependency. The release
-  tarball of 3.5.1 does not require python. 
-
-Finished 30 Jan 2017
-
-Version 3.5:
-
-- ui:
-  * "chat list": shows a list of existing server-side chatrooms. With some
-    protocols, this is often the only way to add new channels.
-    See 'help chat list' for details. (jgeboski)
-  * "plugins": lists the installed plugins and their versions (jgeboski)
-  * Add 'nick_lowercase' and 'nick_underscores' settings.
-  * "handle_unknown" can be set per-account, not just globally
-
-- jabber:
-  * Add "always_use_nicks" channel setting, for non-anonymous MUCs (trac #415)
-    See 'help set always_use_nicks' for possible side effects.
-  * Properly handle rejected file transfers
-  * Don't send parts in a chat if someone is still connected from other devices
-  * hipchat: support personal oauth tokens (manually generated ones)
-
-- twitter:
-  * Hide muted tweets / no-retweets, add mute/unmute commands (Flexo)
-  * Show full version of extended tweets (with slightly more than 140 chars)
-
-- purple:
-  * Support setting chat room topics (EionRobb)
-  * Support for extra groupchat settings. Shows an error if any required
-    ones are missing. Look for purple_ prefixed settings in "chan #... set"
-  * SIPE: persistent chats can be joined now, thanks to the "chat list" command
-    and the above ("purple_uri" channel setting)
-  * Fix a file transfer crash bug (Mainly affected telegram)
-  * Honor protocol flag to not require a password (used in hangouts, telegram)
-  * Set the contacts' nicks to the %full_name for a few whitelisted protocols
-    (hangouts, funyahoo, icq, line)
-  * LINE: added a hack to save its auth token, to avoid re-auth every time
-  * Show self-messages in groupchat backlogs (before join)
-
-- yahoo:
-  * Removed because they killed their old protocol on a two month notice.
-    Use EionRobb's funyahoo purple plugin, or better yet, don't use yahoo.
-
-- Stuff for enterprise deployments (all done by Sevas)
-  * Locked down accounts, useful when pregenerating user config files. An
-    account that is marked with the locked="true" attribute can't be removed
-    and its username/password can't be changed.
-  * Locked down settings. Same as above, but for individual account settings.
-  * AllowAccountAdd setting in bitlbee.conf, to disable adding new accounts.
-  * PAM and LDAP authentication backends (not compiled by default)
-
-- For packagers:
-  * Enabled debug symbols in non-debug builds, disabled stripping by default.
-    This is closer to the default behavior of autotools, and --debug=1 is mostly
-    to set the DEBUG macro and disable optimization.
-
-- For plugin devs:
-  * Plugins should now include an "init_plugin_info" function which will be used
-    for ABI version checking in the future. It's optional for now, but will be
-    enforced later. See the commit log of d28fe1c for details. (jgeboski)
-
-Finished 8 Jan 2017
-
-Version 3.4.2:
-- irc:
-  * Self-messages (messages sent by yourself from other IM clients), given
-    support by the IM protocols and your IRC client. See this for details:
-    https://wiki.bitlbee.org/SelfMessages
-  * IRCv3.1 support and part of 3.2: cap-3.2, sasl-3.2, multi-prefix,
-    away-notify, extended-join, userhost-in-names
-  * Send numeric errors when failing to join a channel, to not confuse clients
-  * Channel autojoins should be more reliable now.
-- jabber:
-  * Carbons (XEP-0280), for self-message support. It's not widely supported
-    by most public XMPP servers (easier if you host your own), but this will
-    probably change in the next few years. Thanks kormat for the original patch.
-  * Fix typing notifications between two bitlbee users or with gtalk users
-  * Remove facebook XMPP code, point people at bitlbee-facebook.
-  * Show groupchat kick/ban/leave reasons
-  * SASL ANONYMOUS (XEP-0175), for "guest" logins, see "help set anonymous"
-  * Hipchat: 'chat add hipchat "channel name"' now tries to guess the JID
-- purple:
-  * Fix problems remembering SSL certificates as trusted
-  * Fix /join #channel, which joined a differently named channel
-  * Fix crash when doing "chat with" with skypeweb
-  * Fix html entities appearing in some protocols
-  * Fix setting away states in jabber, which failed silently
-  * Implement notify_message UI op, to be able to show some error messages.
-- skype:
-  * Show all messages as groupchats since we can't tell which ones are private.
-  * This plugin is mostly-deprecated and mostly-broken but it's still useful
-    for p2p-based groupchats, which aren't delivered over newer protocols.
-    Everyone else should use the skypeweb purple plugin or msn instead.
-- msn:
-  * Minor tweaks. Faster login, better error reporting, fixed add/remove.
-    Still MSNP21. Disregard that "Next release!" in the previous release.
-- otr:
-  * Don't use NOTICE for user messages (revmischa)
-  * Fix crashes when using the jabber xmlconsole
-  * A few minor fixes: color multiline messages, filter incoming color codes.
-- Packaging:
-  * Show ./configure args in bitlbee -V, config.h and Makefile.settings
-  * Allow setting the plugin dir in bitlbee.conf, for NixOS (anderspapitto)
-  * Improved cross compiler support (gamaral)
-- Other important bugfixes:
-  * Fix potential crashes when leaving temporary channels
-  * Fix all sorts of crashing bugs when cancelling in-progress connections.
-
-Finished 19 Mar 2016
-
-Version 3.4.1:
-- msn:
-  * Upgraded protocol to MSNP21, works again (dx)
-  * Contact list management, groupchats and skype username logins won't work.
-    Next release!
-- jabber:
-  * Add "hipchat" protocol, for smoother login. Takes the same username as
-    the official client. Note that unlike the 'hip-cat' branch, this doesn't
-    preload channels. https://wiki.bitlbee.org/HowtoHipchat for details (dx)
-  * Fixed a bug when receiving topics set by people who left the channel (dx)
-  * Create fake users instead of showing "unknown participant" errors (dx)
-  * Gmail mail notifications support (Artem Savkov)
-  * Lowering xmpp presence priority on away (Artem Savkov)
-- twitter:
-  * Show quoted tweets/RTs with comment inline (wilmer)
-  * Added "url" command, can be used to quote tweets (wilmer)
-  * Make replies to self work (wilmer)
-- Building documentation is now done with a python script (both 2.x/3.x work)
-  Asciidoc/xsltproc/xmlto are no longer build dependencies (dx)
-
-Finished 16 Jun 2015
-
-Version 3.4:
-- First release pretty much fully prepared by dx instead of Wilmer. Just look
-  at the tightly structured changelog!
-- Main repository migrated from bzr to git
-- Some API/ABI changes. Recompiling third party plugins is required!
-- Important bugfixes:
-  * Fix memory leak when calling word_wrap() on groupchat messages (dx)
-  * Fix segfault after a file transfer is complete (dx)
-  * Fix bug where NSS would refuse to work in forkdaemon mode (dx)
-  * Fix several bugs with UTF8 nicks (dx)
-  * Fix some nasty deadlocks that appared mostly with libpurple (dx)
-- General changes:
-  * Add a 'pattern' parameter to the blist command, to filter it (tribut)
-  * Implemented /kick support, only supported by purple for now (jgeboski)
-  * Add a "special" state to show_users (mapped to the % prefix) (jgeboski)
-  * Improved support for cygwin, openbsd and darwin (jcopenha)
-  * Create temporary users instead of showing "Message from unknown
-    participant" (jgeboski)
-- purple:
-  * Local contact lists for gadugadu and whatsapp (dx)
-  * Add topic and name_hint to groupchats (seirl)
-  * Support for 'input' requests (such as telegram auth codes) (seirl)
-    Note that telegram-purple itself is rather unstable ATM, it may crash.
-- jabber:
-  * Handle compressed DNS responses in SRV lookup (jcopenha)
-  * Fix case sensitivity issues with JIDs (GRMrGecko, dx)
-  * Implement XEP-0203 style message timestamps (dx)
-  * Fix "Server claims your JID is X instead of Y" warnings (dx)
-  * Account-wide display_name setting, mostly for hipchat (dx)
-- twitter:
-  * Filter channels. Search by keyword/hashtag or a list of users (jgeboski)
-  * Fix bug in "reply" command which removed the first quote character (dx)
-  * Add "rawreply" command, like reply but bitlbee won't add @mention (WillP)
-  * Add support for The United States of America (favorite/fav aliases) (dx)
-  * Default show_old_mentions to 0 (dx)
-  * Start stream from last tweet on connect/reconnect (roger)
-- msn:
-  * Disabled module by default. The protocol we used (MSNP18) stopped working
-    last week. This is being worked on, but it's far from ready for release.
-- And lots of small bugfixes, too many to list here.
-
-Finished 25 Mar 2015
-
-Version 3.2.2:
-- The OTR plugin now uses libotr 4.0 (AKA libotr5 in debian based distros)
-- Rejecting buddy requests in jabber won't accept them. Sorry for that.
-- Purple builds can now enable built in protocols when configuring, by
-  passing a parameter such as --jabber=1 to configure
-- You can now use /oper to change passwords with "ac x set -del password"
-- Complex unicode characters (non-BMP) now display correctly in twitter.
-- A few init / build script / pkg-config fixes. Added "install-systemd"
-  make target.
-
-Finished 5 Jul 2014
-
-Version 3.2.1:
-- Most important change: http_client updated to use HTTP/1.1, now required
-  by Twitter.
-- fill_by setting can now be used to fill a channel contacts *not* in a
-  certain group/on a certain account/etc. See "help set fill_by"
-- Added utf8_nicks setting which lets you use non-ASCII nicknames for your
-  contacts. Might not work with all IRC clients, use at your own risk!
-- Lots of bugfixes.
-
-Finished 27 Nov 2013
-
-Version 3.2:
-- By far the most important change, a thorough update to the Twitter
-  module:
-  * Now using Twitter API 1.1,
-  * which means it's now using JSON instead of XML,
-  * which means access to the streaming API (Twitter only, other Twitter
-    API services don't seem to have it). No more 60-second polls, #twitter
-    looks even more like real IRC now!
-  * Also, the streaming API means nice things like receiving DMs.
-  * show_ids, already enabled by default for a while, now uses hexa-
-    decimal numbers, which means a 256-entry backlog instead of just 100.
-  * Added a mode=strict setting which requires everything to be a command.
-    The "post" command should then be used to post a Tweet.
-- Jabber module bugfix that fixes connection issues with at least Google
-  Talk but reportedly some other servers (OpenFire?) as well.
-- SSL modules improved a little bit. GnuTLS module now supports SNI and
-  session caching. Shouldn't change much, but hopefully reduces latency
-  and bandwidth usage a little bit.  
-- A bunch of other fixes/improvements here and there.
-
-Finished 6 Jan 2013
-
-Version 3.0.6:
-- Updated MSN module to speak MSNP18:
-  * Biggest change is that this brings MPOP support (you can sign in to
-    one account from multiple locations).
-  * Restored support for *sending* offline messages.
-  * Some support for federated (i.e. Yahoo!) contacts. (Only messages
-    might work, you won't see them online.)
-- Twitter:
-  * Work-around for stalls that are still happening sometimes.
-  * Added "favourite" command.
-  * "show_ids" enabled by default.
-- Handle see-other-host Jabber messages which should fix support for
-  MSN-XMPP.
-- Misc. fixes and improvements.
-
-Finished 14 Oct 2012
-
-Version 3.0.5:
-- SSL certificate verification (edit your bitlbee.conf to enable it).
-  Works only with GnuTLS!
-- OAuth2 support in Jabber module (works with Google Talk, Facebook and
-  MSN Messenger).
-- Support for ad-hoc Jabber groupchats. Just create a channel and start
-  inviting people, similar to how this works on other IM networks. Works
-  fine with GTalk, depends on a conference server being set up on other
-  networks.
-- Allow old-style Jabber login (non-SASL), this solves problems with some
-  old/buggy Jabber servers. (acc jabber set sasl off)
-- Use HTTPS for OAuth1 authentication traffic.
-- Awareness of Twitter's t.co URL lengt^Wshortening when checking message
-  length.
-- Fixed identi.ca OAuth support. OAuth will now always be used for both
-  Twitter and identi.ca accounts.
-- Fix nick_format=%full_name with libpurple.
-- Instead of "protocol(handle)", use the account tags introduced in 3.0
-  when root wants to refer to an account (in log messages, queries, etc.)
-- Many small bugfixes, improvements, etc.
-
-Finished 18 Feb 2012
-
-Version 3.0.4:
-- Merged Skype support. This used to be a separate plugin, and it still is,
-  but by including it with BitlBee by default it will be easier to keep it
-  in sync with changes to BitlBee.
-- Fixed a file descriptor leak bug that may have caused strange behaviour
-  in BitlBee sessions running for a long time.
-- Now fetches Twitter mentions as well if the "fetch_mentions" account
-  setting is enabled.
-- With t.co now all over Twitter, show the original (but truncated) URL
-  between <brackets>.
-- Fixed MSN Messenger login issues ("timeout" while fetching buddy list).
-- Another (related) GnuTLS compatibility fix (now 2.13+?).
-
-Finished 4 Dec 2011 (Exactly 6 years since 1.0!)
-
-Version 3.0.3:
-- Fixed Twitter compatibility. (The API call used to get the following list
-  was deprecated.)
-- Twitter: Enable the show_ids setting to assign a two-digit short ID to
-  recent tweets to use for retweets and replies (so you can RT/reply to more
-  than just a person's last message).
-- Some other Twitter fixes/improvements.
-- "otr reconnect" command and some other fixes.
-- GnuTLS 2.12 compatibility fix.
-- Include "FLOOD=0/9999" in the 005/ISUPPORT line at login to hint the IRC
-  client that rate limiting is not required. (Next step: Get IRC clients to
-  parse it.)
-- Other stuff too small to mention.
-
-Finished 12 Jun 2011
-
-Version 3.0.2:
-- Fixed MSN login issues with @msn.com accounts.
-- /CTCP support: You can CTCP VERSION Jabber contacts, and CTCP NUDGE MSN
-  contacts. More may come later, ideas are welcome.
-- By default, leave Twitter turned on for libpurple builds.
-- Allow using /OPER to identify/register as well. (Password security hack.)
-- Fixed proxy support with libpurple.
-- Some minor changes/fixes.
-
-Finished 7 Mar 2011
-
-Version 3.0.1:
-- Fixed some bugs that were found only after releasing 3.0, including
-  Jabber contacts never going offline, MSN login issues, compilation
-  issues on some non-Linux systems, a fairly big memory leak in the MSN
-  SOAP code, etc...
-- Better handling of multiple control channels and set private=false.
-- Using login.icq.com for ICQ logins again since AOL sold ICQ so it
-  doesn't live on the AIM servers anymore.
-- Fixed ability to join password-protected Jabber rooms.
-- Time out if logging into an account takes too long.
-- Fixed NSS SSL module.
-- Support for server-side Gadu-Gadu contact lists (via libpurple, there's
-  still no native gg support).
-- Allow omitting password when using "account add", the password can be
-  entered using /OPER to prevent echoing to screen and logs.
-
-Finished 24 Nov 2010
-
-Version 3.0:
-- Important: This version drops backward compatibility with the file format
-  used for user settings in versions before 1.2. If you're upgrading from
-  very old BitlBee versions (like 1.0.x), you'll have to recreate your
-  BitlBee account - or use an 1.2.x BitlBee once to do the conversion.
-- Rewrote the IRC core, which brings:
-  * Support for multiple (control) channels, so you can have one channel per
-    buddy group, per IM account/protocol, or for example a &offline with all
-    offline contacts. See "help channels" for information on how to use this.
-  * You can also leave and rejoin all channels. Being in &bitlbee is no
-    longer required.
-  * Now you can start groupchats by just joining a new channel and inviting
-    contacts. (The "chat with" command still works as well.)
-  * You can change your nickname, whenever you want.
-  * Root commands:
-    + To hopefully resolve confusion about the "account set" syntax, the
-      ordering was changed slightly: "account set acc/setting value"
-      becomes "account acc set setting value". Obviously the same order
-      should now be used for other account subcommands.
-    + Shortcuts: Instead of "account list" you can say "acc li".
-  * /whois shows idle/login times of your contacts when available.
-  * paste_buffer (previously known as buddy_sendbuffer) now works for chats
-    as well.
-  * The nick_source setting was replaced with a nick_format setting, which
-    looks more or less like a format string, and lets you tweak how nicknames
-    for contacts are generated in more detail. It also tries to convert non-
-    ASCII characters (i.e. accents) properly.
-  * A per-channel show_users setting lets you configure exactly which kinds
-    of contacts (online/away/offline) should show up in a channel and what
-    modes (none/voice/etc) they should have. Like show_offline, but together
-    with other new features it can for example create an &offline channel
-    with just offline contacts.
-  * If you connect (and identify) to a BitlBee server you're already
-    connected to, you can take over the existing session instead of starting
-    a new one.
-  * More correct validation of channel names: They can contain pretty much
-    any character, unlike nicknames.
-- Support for using libpurple instead of BitlBee's built-in IM protocol
-  modules. This can be enabled by passing --purple=1 to the configure script.
-  * This adds support for many more IM protocols to BitlBee.
-  * And new functionality to existing protocols.
-  * This is and will always be optional and using it on public servers is
-    *not* recommended. It should be pretty stable, but costs more RAM/etc.
-  * Switching to libpurple should be pretty transparent. See "help purple"
-    for a list of supported protocols (works only in libpurple-enabled
-    binaries).
-- Rewritten MSN module, implementing MSNP15 instead of the old MSNP8:
-  * MSNP8 support from MSN was getting pretty unreliable. There were issues
-    with remembering display names and adding contacts/auth requests (or
-    even contacts silently getting blocked!). This upgrade should fix all
-    of that.
-  * Support for sending offline messages.
-  * Support for setting and reading status messages.
-- Integrated the bitlbee-otr branch in a mostly non-intrusive way. Since
-  this is not end-to-end it should *not* be enabled on public servers.
-  Distro packagers are requested to offer OTR as a separately installable
-  plugin. (Compile with --otr=plugin) Please do not enable it by default as
-  there is no easy way to disable OTR once the plugin is loaded/built in.
-- Support for file transfers, in and out. /DCC SEND a file to a contact and
-  it becomes a file transfer, and incoming file transfers become /DCC SENDs
-  to you. Note that this is mostly useful when combined with libpurple, as
-  only the Jabber native protocol module currently supports file transfers.
-- Updated Yahoo! module to be in sync again with libyahoo2. This mostly
-  fixes issues with authorization requests.
-- Show if a contact is mobile or not. See "help set mobile_is_away".
-- Easier handling of XMPP chatroom invitations.
-- The chatroom mode of the Twitter module is now enabled by default, since
-  this was by far the most popular. To disable it, see "help set mode".
-- Added some Twitter-specific commands that can only be used in the Twitter
-  window. Most important addition: Retweets. See "help set commands".
-- Removed some ancient account/nick migration scripts and added one for
-  easier switching from Pidgin and other libpurple-based clients to BitlBee.
-- Many bug fixes in both the core and IM modules, small feature enhancements
-  and other minor changes.
-
-Finished 22 Oct 2010
-
-Version 1.2.8:
-- Now always using the AIM-style authentication method for OSCAR connections,
-  even when connecting to ICQ. This solves login issues some people were
-  having. (If you have problems, try changing the old_icq_auth setting.)
-- Twitter:
-  * Allow changing the Twitter API base URL so the module can also be used
-    for identi.ca or any other compatible network.
-  * Fetch the full list of Twitter contacts instead of slowly adding all
-    contacts as they post a message.
-  * Fixed message length counting.
-  * Allow following/unfollowing people using the usual add/remove commands.
-  * Better error reporting.
-- Added a user_agent setting to the Jabber module to get around artificial
-  client restrictions.
-- Allow nick changes (although only before register/identify).
-- Some more minor bugfixes/etc.
-
-Finished 4 Jul 2010
-
-Version 1.2.7:
-- Fixed problems with MSN Messenger authentication. ("Could not parse
-  Passport server response")
-- Fixed broken typing notifications when talking to GTalk contacts.
-- Fixed an issue with non-anonymous Jabber chatrooms polluting the nick
-  namespace, sometimes generating odd warning messages.
-- Restored ability to start groupchats on ICQ.
-- Added show_offline setting that will also show offline contacts in the
-  control channel.
-- OAuth support for Twitter: This means the module will keep working after
-  June (this also changes "via API" under your tweets into "via BitlBee").
-
-Finished 15 May 2010
-
-Version 1.2.6a:
-- Fixed a typo that renders the Twitter groupchat mode unusable. A last-
-  minute change that came a few minutes late.
-
-Finished 19 Apr 2010
-
-Version 1.2.6:
-- Native (very basic) support for Twitter, implemented by Geert Mulders.
-  Currently supported are posting tweets, reading the ones of people you
-  follow, and sending (not yet receiving!) direct messages.
-- Fixed format of status messages in /WHOIS to improve IRC client
-  compatibility.
-- Show timestamps of offline messages/channel backlogs.
-- Allow saving MSN display names locally since sometimes this stuff breaks
-  server-side. (Use the local_display_name per-account setting.)
-- Suppress empty "Headline:" messages for certain new XMPP broadcast 
-  messages.
-- Better handling of XMPP contacts with multiple resources on-line. Default
-  behaviour now is to write to wherever the last message came from, or to
-  the bare JID (usually becomes a broadcast) if there wasn't any recent msg.
-- Added a switchboard_keepalives setting which should solve some issues with
-  talking to offline MSN contacts. (Although full support for offline
-  messages is not ready yet!)
-- The usual misc. bug fixes.
-
-Finished 19 Apr 2010
-
-Version 1.2.5:
-- Many bug fixes, including a fix for MSN login issues, Jabber login timing
-  issues, Yahoo! crashes at login time with huge contact lists,
-- Avoid linking in a static version of libresolv now that glibc has all
-  relevant functions available in the dynamic version.
-- Improved away state code and added the ability to set (non-away) status
-  messages using "set status" (also possible per account) and see them in
-  blist and /whois output.
-- Added a post-1.2 equivalent of encode/decode to quickly encrypt/decrypt
-  passwords in a way that BitlBee can read them.
-- Allow using the full name for generating nicknames, instead of just the
-  handle. This is especially useful when using the Facebook XMPP server.
-- Auto reconnect is now enabled by default since all protocols can properly
-  detect cases where auto reconnect should be avoided (i.e. concurrent
-  logins).
-- Changed the default resource_select setting which should reduce message
-  routing issues on Jabber (i.e. messages going someone's phone instead of
-  the main client).
-
-Finished 17 Mar 2010
-
-Version 1.2.4:
-- Most important change (and main reason for releasing now): Upgraded Yahoo!
-  module to a newer version to get it working again.
-- join_chat command replaced with the much better chat command:
-  * Similar to how account replaced login/slist/logout. Add a chatroom once,
-    then just /join it like any other room. Also automatic joining at login
-    time is now possible.
-  * Note that the old way of starting groupchats (/join #nickname) is now
-    also deprecated, use "chat with" instead.
-  * See "help chat" and "help chat add" for more information.
-- Rewrote bitlbee.conf parser to be less dumb.
-- Fixed compatibility (hopefully) with AIM mobile messages, certain kinds
-  of Google Talk chatrooms.
-- Fixed numerous stability/reliability bugs over the last year.
-
-Finished 17 Oct 2009
-
-Version 1.2.3:
-- Fixed one more flaw similar to the previous hijacking bug, caused by incon-
-  sistent handling of the USTATUS_IDENTIFIED state. All code touching these
-  variables was reviewed and should be correct now.
-
-Finished 7 Sep 2008 
-
-Version 1.2.2:
-- Security bugfix: It was possible to hijack accounts (without gaining access
-  to the old account, it's simply an overwrite)
-- Some more stability improvements.
-- Fixed bug where people with non-lowercase nicks couldn't drop their account.
-- Easier upgrades of non-forking daemon mode servers (using the DEAF
-  command).
-- Can be cross-compiled for Win32 now! (No support for SSL yet though, which
-  makes it less useful for now.)
-- Exponential backoff on auto-reconnect.
-- Changing passwords gives less confusing feedback ("password is empty") now.
-
-Finished 26 Aug 2008
-
-Version 1.2.1:
-- Fixed proxy support.
-- Fixed stalling issues while connecting to Jabber when using the OpenSSL
-  module.
-- Fixed problem with GLib and ForkDaemon where processes didn't die when
-  the client disconnects.
-- Fixed handling of "set charset none". (Which pretty much breaks the account
-  completely in 1.2.)
-- You can now automatically identify yourself to BitlBee by setting a server
-  password in your IRC client.
-- Compatible with all crazy kinds of line endings that clients can send.
-- Changed root nicknames are now saved.
-- Added ClientInterface setting to bind() outgoing connections to a specific
-  network interface.
-- Support for receiving Jabber chatroom invitations.
-- Relaxed port restriction of the Jabber module: added ports 80 and 443.
-- Preserving case in Jabber resources of buddies, since these should
-  officially be treated as case sensitive.
-- Fully stripping spaces from AIM screennames, this didn't happen completely
-  which severely breaks the IRC protocol.
-- Removed all the yellow tape around daemon mode, it's pretty mature by now:
-  testing.bitlbee.org serves all (~30) SSL users from one daemon mode
-  process without any serious stability issues.
-- Fixed GLib <2.6 compatibility issue.
-- Misc. memory leak/crash fixes.
-
-Finished 24 Jun 2008
-
-Version 1.2:
-- Added ForkDaemon mode next to the existing Daemon- and inetd modes. With
-  ForkDaemon you can run BitlBee as a stand-alone daemon and every connection
-  will run in its own process. No more need to configure inetd, and still you
-  don't get the stability problems BitlBee unfortunately still has in ordinary
-  (one-process) daemon mode.
-- Added inter-process/connection communication. This made it possible to
-  implement some IRC operator features like WALLOPs, KILL, DIE, REHASH and
-  more.
-- Added hooks for using libevent instead of GLib for event handling. This
-  should improve scalability, although this won't really be useful yet because
-  the one-process daemon mode is not reliable enough.
-- BitlBee now makes the buddy quits when doing "account off" look like a
-  netsplit. Modern IRC clients show this in a different, more compact way.
-  (This can be disabled if your client doesn't support this.)
-- GLib 1.x compatibility was dropped. BitlBee now requires GLib 2.4 or newer.
-  This allows us to use more GLib features (like the XML parser). By now GLib
-  1.x is so old that supporting it really isn't necessary anymore.
-- Many, many, MANY little changes, improvements, fixes. Using non-blocking
-  I/O as much as possible, replaced the Gaim (0.59, IOW heavily outdated)
-  API, fixed lots of little bugs (including bugs that affected daemon mode
-  stability). See the bzr logs for more information.
-- One of the user-visible changes from the API change: You can finally see
-  all away states/messages properly.
-- Added units tests. Test coverage is very minimal for now.
-- Better charset handling: Everything is just converted from/to UTF-8 right
-  in the IRC core, and charset mismatches are detected (if possible) and the
-  user is asked to resolve this before continuing. Also, UTF-8 is the default
-  setting now, since that's how the world seems to work these days.
-- One can now keep hashed passwords in bitlbee.conf instead of the cleartext
-  version.
-- Most important change: New file format for user data (accounts, nicks and
-  settings). Migration to the new format should happen transparently,
-  BitlBee will read the old files and once you quit/save it will save in the
-  new format. It is recommended to delete the old files (BitlBee doesn't do
-  this automatically, it will just ignore them) since they won't be used
-  anymore (and since the old file format is a security risk). Some advantages
-  of this file format switch:
-  * Safer format, since the identify-password is now salted before generating
-    a checksum. This way one can't use MD5 reverse lookup databases to crack
-    passwords. Also, the IM-account passwords are encrypted using RC4 instead
-    of the simple obfuscation scheme which BitlBee used so far.
-  * Easier to extend than the previous format (at least the .nicks format was
-    horribly limited).
-  * Nicknames for buddies are now saved per-account instead of per-protocol.
-    So far having one buddy on multiple accounts of the same protocol was a
-    problem because the nicks generated for the two "instances" of this buddy
-    were very unpredictable.
-    NOTE: This also means that "account del" removes not just the account,
-    BUT ALSO ALL NICKNAMES! If you're changing IM accounts and don't want to
-    lose the nicknames, you can now use "account set" to change the username
-    and password for the existing connection.
-  * Per-account settings (see the new "account set" command).
-- A brand new Jabber module. Besides the major code cleanup, it also has
-  has these new features:
-  * Pretty complete XMPP support: RFC3920, RFC3921 plus a number of XEPs
-    including XEP45, XEP73 and XEP85. (See http://www.xmpp.org/ for what all
-    these things mean exactly.) Privacy lists are not supported for obvious
-    reasons.
-  * This complete support also includes TLS and SASL support and SRV record
-    lookup. This means that specifying a server tag for connections should
-    (almost) never be necessary anymore, BitlBee can find the server and can
-    automatically convert plaintext connections to TLS-encrypted ones.
-  * XEP45: Jabber chatroom support!
-  * XEP85 means typing notifications. The older XEP22 (still used by some
-    clients including Gaim <2.0) is not supported.
-  * Better handling of buddies who have more than one resource on-line. As
-    long as one resource is on-line (and visible), BitlBee will show this. 
-    (The previous module didn't keep track of resources and sent an offline
-    event as soon as any resource disappears.)
-  * You can now set your resource priority.
-  * The info command now gives away state/message information for all
-    resources available for that buddy. (Of course this only works if the
-    buddy is in your contact list.)
-  * An XML console (add xmlconsole to your contact list or see "help set
-    xmlconsole" if you want it permanently).
-- The Yahoo! module now says it supports YMSG protocol version 12, which will
-  hopefully keep the Yahoo module working after 2008-04-02 (when Yahoo! is
-  dropping support for version 6.x of their client).
-- MSN switchboard handling changes. Hopefully less messages will get lost now,
-  although things are still not perfect.
-
-Finished 17 Mar 2008
-
-Version 1.0.4:
-- Removed sethostent(), which causes problems for many people, especially on
-  *BSD. This is basically the reason for this release.
-- "allow" command actually displays the allow list, not the block list.
-- Yahoo away state/msg fix.
-- Don't display "Gender: Male" by default if nothing's filled in (OSCAR
-  "info" command)
-- Fixed account cleanup (possible infinite loop) in irc_free().
-- Fixed configdir error message to not always display the compile-time
-  setting.
-
-Finished 20 Aug 2007
-
-Version 1.0.3:
-- Fixed ugliness in block/allow list commands (still not perfect though, the
-  list is empty or not up-to-date for most protocols).
-- OSCAR module doesn't send the ICQ web-aware flag anymore, which seems to
-  get rid of a lot of ICQ spam.
-- added show_got_added(), BitlBee asks you, after authorizing someone, if you
-  want to add him/her to your list too.
-- add -tmp, mainly convenient if you want to talk to people who are not in
-  your list.
-- Fixed ISON command, should work better with irssi now.
-- Fixed compilation with tcc.
-- Fixed xinetd-file.
-- Misc. (crash)bug fixes, including one in the root command parsing that
-  caused mysterious error messages sometimes.
-
-Finished 24 Jun 2006 (Happy 4th birthday, BitlBee!)
-
-Version 1.0.2:
-- Pieces of code cleanup, fixes for possible problems in error checking.
-- Fixed an auto-reconnect cleanup problem that caused crashes in daemon mode.
-- /AWAY in daemon mode now doesn't set the away state for every connection
-  anymore.
-- Fixed a crash-bug on empty help subjects.
-- Jabber now correctly sets the current away state when connecting.
-- Added Invisible and Hidden to the away state alias list, invisible mode
-  should be pretty usable now.
-- Fixed handling of iconv(): It's now done for everything that goes between
-  BitlBee and the IRC client, instead of doing it (almost) every time
-  something goes to or come from the IM-modules. Should've thought about
-  that before. :-)
-- When cleaning up MSN switchboards with unsent msgs, it now also says which
-  contact those messages were meant for.
-- You can now use the block and allow commands to see your current block/
-  allow list.
-
-Finished 1 Apr 2006
-
-Version 1.0.1:
-- Support for AIM groupchats.
-- Improved typing notification support for at least AIM.
-- BitlBee sends a 005 reply when logging in, this informs modern IRC clients
-  of some of BitlBee's capabilities. This might also solve problems some
-  people were having with the new control channel name.
-- MSN switchboards are now properly reset when talking to a person who is
-  offline. This fixes problems with messages to MSN people that sometimes
-  didn't arrive.
-- Fixed one of the problems that made BitlBee show online Jabber people as
-  offline.
-- Fixed problems with commas in MSN passwords.
-- Added some consts for read-only data, which should make the BitlBee per-
-  process memory footprint a bit smaller.
-- Other bits of code cleanup.
-
-Finished 14 Jan 2006
-
-Version 1.0:
-- Removed some crashy debugging code.
-- QUIT command now works before logging in. (Mainly an RFC-compliancy fix.)
-- Hopefully slightly clearer handling of buddy add requests.
-- set buddy_sendbuffer_delay now also supports milisecond precision.
-- Renamed #bitlbee to &bitlbee to avoid confusion with the channel on OFTC.
-- Reviewed the xinetd file and installation instructions.
-- HTML stripping is configurable again.
-- Quit messages (at least on crashes) should appear again.
-- Cleaned up some unnecessary code in the Jabber module, and implemented
-  handlers for headline messages (which allows you to use RSS-to-Jabber
-  gateways).
-- Lowered the line splitting limit a bit to fix data loss issues.
-- The $proto($handle) format used for messages specific to one IM-connection
-  now only include the ($handle) part when there's more than one $proto-
-  connection.
-- Fix for a crash-bug on broken Jabber/SSL connections.
-- Incoming typing notifications now also come in as CTCP TYPING messages, for
-  better consistency. Don't forget to update your scripts!
-- AIM typing notifications are supported now.
-- Jabber module only accepts ports 5220-5229 now, to prevent people from
-  abusing it as a port scanner. We aren't aware of any Jabber server that
-  runs on other ports than those. If you are, please warn us.
-- Send flood protection can't be enabled anymore. It was disabled by default
-  for a good reason for some time already, but some package maintainers
-  turned it back on while it's way too unreliable and trigger-happy to be
-  used.
-- Removed TODO file, the current to-do list is always in the on-line bug
-  tracking system.
-- Fixed a potential DoS bug in input handling.
-
-Finished 4 Dec 2005
-
 Version 0.99:
 - Fixed memory initialization bug in OSCAR module that caused crashes on
@@ -799,5 +5,5 @@
   some situations.
 - Outgoing MSN typing notifications are now understood correctly by the
-  original MS Mac/Windows clients (again).
+  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.
Index: doc/CREDITS
===================================================================
--- doc/CREDITS	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ doc/CREDITS	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -50,10 +50,7 @@
 - Frank Thieme, for the info-command enhancements and other patches.
 - Marcus Dennis, for some bitlbeed enhancements.
-- infamous41md, for security auditing BitlBee code.
+- 1nfamus, for security auditing BitlBee code.
 - Tijmen Ruizendaal, for some useful BitlBee-related irssi scripts.
 - Ed Schouten, for reporting bugs.
-- Greg (gropeep.org), for updating the Yahoo! module to fix some issues
-  that were there for quite some time already.
-- misc@mandriva.org for lots of Jabber contributions.
 
 - And all other users who help us by sending useful bug reports, positive
Index: doc/FAQ
===================================================================
--- doc/FAQ	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ doc/FAQ	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -12,5 +12,5 @@
    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 between which the Bee flies. On
+   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.
    
@@ -38,7 +38,37 @@
    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: It depends on the feature. We keep a list of all wishlist "bugs" in our
-   Bug Tracking system at https://bugs.bitlbee.org/
+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
@@ -46,9 +76,6 @@
    non-ASCII characters!
 A: You probably have to change some settings. To get rid of HTML in messages,
-   see "help set strip_html". If you seem to have problems with your charset,
-   see "help set charset".
-   
-   Although actually most of these problems should be gone by now. So if you
-   can't get things to work well, you might have found a bug.
+   see "help set html". If you seem to have problems with your charset, see
+   "help set charset".
 
 Q: Is BitlBee forked from Gaim?
@@ -62,10 +89,15 @@
    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, Jabber and Twitter modules from
-   scratch. Most of the API has also been changed, so by now the only traces
-   of Gaim left are in the "nogaim" filename.
+   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.
    
-   There is good news for Gaim (or now Pidgin, of course) fans though:
-   BitlBee can now be compiled to use libpurple for all IM interactions.
-   This makes BitlBee a bit more resource-hungry, but adds support for many
-   IM protocols/networks that couldn't be used from BitlBee so far.
+   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
Index: c/HACKING
===================================================================
--- doc/HACKING	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,125 +1,0 @@
-BitlBee post-1.x "architecture"
-
-DISCLAIMER: The messy architecture is being cleaned up. Although lots of
-progress was made already, this is still a work in progress, and possibly
-parts of this document aren't entirely accurate anymore by the time you
-read this.
-
-It's been a while since BitlBee started, as a semi-fork of Gaim (version
-0.58 at the time). Some people believe nothing changed, but fortunately,
-many things have.
-
-The API is gone for a while already - which wasn't incredibly intrusive,
-just a few functions renamed for slightly better consistency, added some
-calls and arguments where that seemed useful, etc.
-
-However, up to late in the 1.2 series, the IRC core was still spread across
-several files, mostly irc.c + irc_commands.c and pieces and bits in
-nogaim.c. If you're looking for a textbook example of layer violation, start
-there.
-
-This was all finally redone. Most of the IRC protocol code was rewritten,
-as was most of the glue between that and the IM modules.
-
-The core of BitlBee is now protocols/bee*. Some pieces are still left in
-protocols/nogaim*. Most stuff in the "root" directory belongs to the IRC
-UI, which should be considered "a" frontend (although currently, and
-possibly forever, the only one). Every subdirectory of protocols/ is another
-IM protocol backend (including purple/ which uses libpurple to define
-many different protocols).
-
-
-/
-
-The IRC core has code to show an IRC interface to a user, with contacts,
-channels, etc. To make channels and contacts do something, you add event
-handlers (that translate a message sent to a nick into an instant message
-to an IM contact, or translates joining a channel into joining an IM
-chatroom).
-
-To get events back from the BitlBee core, the bee_t object has a bunch of
-functions (struct bee_ui_funcs) that catch them and convert them back to
-IRC.
-
-Short description of what all irc*.c files (and some related ones) do:
-
-bitlbee.c: BitlBee bootstrap code, doing bits of I/O as well.
-ipc.c: For inter-process communication - communication between BitlBee
-    sessions. Also used in daemon mode (in which it's not so much inter-
-    process).
-irc.c: The main core, with bits of I/O handling, parsing, etc.
-irc_channel.c: Most things related to standard channels (also defines some
-    of the control channel behaviour).
-irc_commands.c: Defines all IRC commands (JOIN, NICK, PRIVMSG, etc.).
-irc_im.c: Most of the glue between IRC and the IM core live here. This is
-    where instant messages are converted to IRC and vice versa, contacts
-    coming online is translated to one or more joins and/or mode changes.
-irc_send.c: Simple functions that send pieces of IRC output. Somewhat
-    random, but if an IRC response is slightly more complicated than just a
-    simple line, make it a function here.
-irc_user.c: Defines all IRC user details. Mostly defines the user "object".
-irc_util.c: Misc. stuff. Not much ATM.
-nick.c: Handling of nicknames: compare, ucase/lcase, generating and storing
-    nicks for IM contacts.
-set.c: Settings management, used for user-changeable global/account/channel
-    settings. Should really be considered part of the core.
-storage*.c: Storing user accounts. (The stuff you normally find in
-    /var/lib/bitlbee)
-
-
-/protocols
-
-The IM core lives in protocols/. Whenever you write code there, try to avoid
-using any IRCisms there.
-
-Most header files in there have some of their details explained in comments.
-bee*.c and nogaim.c are the layer between the IM modules and the IRC
-frontend. They keep track of IM accounts, contacts and their status,
-groupchats, etc.
-
-You can control them by calling functions in there if available, and
-otherwise by just calling the functions exported via the prpl struct. Most
-of these functions are briefly explained in the header files, otherwise the
-best documentation is sadly in irc_im.c and root_commands.c.
-
-Events from the IM module go back to the core + frontend via imcb_*
-functions defined in bee*.c and nogaim.c. They're all described in the
-header files.
-
-
-/lib
-
-BitlBee uses GLib, which is a pretty nifty library adding a bunch of things
-that make life of a C coder better. Please try to not use features from
-recent GLib versions as whenever this happens, some people get cranky. :>
-
-There's also a whole bunch of nice stuff in lib/ that you can use:
-
-arc.c: ARC4 encryption, mostly used for encrypting IM passwords in the XML
-    storage module.
-base64.c
-events_*.c: Event handling, using either GLib (default) or libevent (may
-    make non-forking daemon mode with many users a little bit more efficient).
-ftutil.c: Some small utility functions currently just used for file transfers.
-http_client.c: A simple (but asynchronous) HTTP(S) client, used by the MSN,
-    Yahoo! and Twitter module by now.
-ini.c: Simple INI file parser, used to parse bitlbee.conf.
-md5.c
-misc.c: What the name says, really.
-oauth.c: What the name says. If you don't know what OAuth is, ask Google.
-    Currently only used by the Twitter module. Implements just version 1a ATM.
-proxy.c: Used together with events_*.c for asynchronous I/O directly or via
-    proxies.
-sha1.c
-ssl_*.c: SSL client stuff, using GnuTLS (preferred) or OpenSSL. Other modules
-    aren't working well ATM.
-url.c: URL parser.
-xmltree.c: Uses the GLib stream parser to build XML parse trees from a stream
-    and convert the same structs back into XML. Good enough to do Jabber but
-    not very aware of stuff like XML namespaces. Used for Jabber and Twitter.
-
-This, together with the headerfile comments, is most of the documentation we
-have right now. If you're trying to make some changes to BitlBee, do feel
-free to join #bitlbee on irc.oftc.net to ask any question you have. 
-Suggestions for specific parts to document a little bit more are also
-welcome.
Index: doc/Makefile
===================================================================
--- doc/Makefile	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ doc/Makefile	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -1,20 +1,14 @@
 -include ../Makefile.settings
-ifdef _SRCDIR_
-_SRCDIR_ := $(_SRCDIR_)doc/
-endif
-
-all:
-	$(MAKE) -C user-guide
 
 install:
 	mkdir -p $(DESTDIR)$(MANDIR)/man8/ $(DESTDIR)$(MANDIR)/man5/
-	$(INSTALL) -m 0644 $(_SRCDIR_)bitlbee.8 $(DESTDIR)$(MANDIR)/man8/
-	$(INSTALL) -m 0644 $(_SRCDIR_)bitlbee.conf.5 $(DESTDIR)$(MANDIR)/man5/
-	$(MAKE) -C user-guide $@
+	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 $@
+	make -C user-guide $@
 
 .PHONY: install uninstall
Index: doc/README
===================================================================
--- doc/README	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ doc/README	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -9,27 +9,4 @@
 make install will move all the files to the right places.
 
-RUN MODES
-=========
-
---- (Fork)Daemon mode
-
-These days ForkDaemon mode is the recommended way of running BitlBee. The
-difference between Daemon and ForkDaemon mode is that in the latter, a
-separate process is spawned for every user. This costs a little bit more
-memory, but means that if one user hits a bug in the code, not all other
-users get disconnected with him/her.
-
-To use BitlBee in any daemon mode, just start it with the right flags or
-enable it in bitlbee.conf (see the RunMode option). You probably want to
-write an init script to start BitlBee automatically after a reboot. (This
-is where you realise using a package from your distro would've been a better
-idea. :-P)
-
-Please do make sure that the user BitlBee runs as (not root, please!) is
-able to read from and write to the /var/lib/bitlbee directory to save your
-settings!
-
---- inetd installation (more or less deprecated)
-
 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
@@ -41,25 +18,9 @@
 of just 'nobody') might be a good idea.
 
-*BSD/Darwin/OSX NOTE: Most *BSD inetds are more strict than the one that
-comes with Linux systems. Possibly all non-Linux inetds are like this. They
-don't allow you to specify a port number in the inetd.conf entry, instead
-you have to put a service name there (one that is also mentioned in
-/etc/services). So if there's no line in /services for 6667/tcp (or whatever
-you choose), add it and use that name in the inetd.conf entry.
-
--- xinetd installation (equally deprecated)
-
-Most machines use xinetd instead of inetd these days. If your machine runs
-xinetd, you can copy the bitlbee.xinetd file from the doc/ directory to your
-xinetd.d/ directory. Most likely you'll have to change a thing or two before
-it'll work.
-
-After configuring your (x)inetd, send the daemon a SIGHUP and things should
-work. If not, see your syslogs, since both daemons will complain there when
-something's wrong.
-
 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)
 
 
@@ -68,17 +29,12 @@
 
 BitlBee's only real dependency is GLib. This is available on virtually every
-platform. Any recent version of GLib (2.4 or higher) will work.
+platform. Any recent version of GLib (including 1.x versions) will work.
 
-Off-the-Record encryption support can be included if libotr is available on
-your machine. Pass --otr=1 to configure to build it into BitlBee, or
---otr=plugin to build it as a separate loadable plugin (mostly meant for
-distro packages).
-
-These days, many IM protocols use SSL/TLS connections (for authentication
-or for the whole session). BitlBee can use several SSL libraries for this:
-GnuTLS, NSS (which comes with Mozilla) and OpenSSL. OpenSSL is not GPL-
-compatible in some situations, so using GnuTLS is preferred. However,
-especially on *BSD, OpenSSL can be considered part of the operating system,
-which eliminates the GPL incompatibility.
+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
@@ -92,12 +48,51 @@
 ==================
 
-The configure script is may not work very well with some non-bash shells (but
-dash is supported), so if you experience problems, make sure you use bash to
-run the script. Same for the Makefile, it only works well with GNU make. (gmake
-on most BSD systems)
+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.
 
-If someone can tell us how to write Makefiles that work with both/all
-versions of make, we'd love to hear it, but it seems this just isn't
-possible.
+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).
 
 
@@ -126,8 +121,4 @@
 on it almost immediately. And the result is BitlBee.
 
-In the years after, Gaim was renamed to Pidgin, BitlBee got libpurple support
-allowing to use Pidgin plugins and the support for the old chatprotocols ICQ,
-MSN and AIM was finally removed from BitlBee.
-
 
 WEBSITE
@@ -135,27 +126,22 @@
 
 You can find new releases of BitlBee at:
-https://www.bitlbee.org/
-
-The bug tracking system:
-https://bugs.bitlbee.org/
-
-Our version control system is Git. Our repository is at:
-https://github.com/bitlbee/bitlbee
-
-More documentation on the Wiki:
-https://wiki.bitlbee.org/
+http://www.bitlbee.org/
 
 
-A NOTE ON PASSWORD ENCRYPTION
-=============================
+A NOTE ON ENCRYPTION
+====================
 
-BitlBee currently uses salted MD5 and RC4 to store the passwords. This means
-that people who somehow get their hands on your configuration files can't
-easily extract your passwords from them anymore.
+BitlBee stores the accounts and settings (not your contact list though) in
+some sort of encrypted/obfuscated format.
 
-However, once you log into the BitlBee server and send your password, an
-intruder with tcpdump can still read your passwords. This can't really be
-avoided, of course. So if you run a public server, it's most important that you
-don't give root access to people who like to play with tcpdump.
+*** 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
@@ -165,7 +151,22 @@
 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 
-	          <https://www.bitlbee.org/>
-	Copyright (C) 2002-2010  Wilmer van der Gaast <wilmer@gaast.net>
+	          <http://www.bitlbee.org/>
+	Copyright (C) 2002-2004  Wilmer van der Gaast <wilmer@gaast.net>
 	                         and others
Index: doc/TODO
===================================================================
--- doc/TODO	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ doc/TODO	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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?
Index: doc/bitlbee.8
===================================================================
--- doc/bitlbee.8	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ doc/bitlbee.8	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -13,5 +13,5 @@
 .\" the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 .\"
-.TH bitlbee 8 "19 May 2010"
+.TH bitlbee 8 "07 March 2004"
 .SH NAME
 BitlBee \- IRC gateway to IM chat networks
@@ -19,19 +19,19 @@
 .PP
 .B bitlbee
-[\-I]
-[\-c \fIconfiguration file\fP]
-[\-d \fIconfiguration directory\fP]
+[-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]
+-D
+[-i \fIaddress\fP]
+[-p \fIport number\fP]
+[-n]
+[-v]
+[-c \fIconfiguration file\fP]
+[-d \fIconfiguration directory\fP]
 .PP
 .B bitlbee
-\-h
+-h
 .RI
 .SH DESCRIPTION
@@ -39,48 +39,38 @@
 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. BitlBee's protocol support is based on the gaim 
+protocol plugins. BitlBee currently supports Oscar (aim and icq), 
+MSN, Jabber and Yahoo.
 
-\fBbitlbee\fP is normally started as a system service (with init
-scripts, systemd units or equivalent). It can also be run as an
-.BR inetd (8)
-/
-.BR xinetd (8)
-service or a stand-alone daemon with the options below.
-
-To use it, connect to it with an IRC client with a command such as
-"/connect localhost"
-
+\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 RUN MODES
+.SH OPTIONS
 .PP
 .IP "-I"
 Run in 
 .BR inetd (8)
-mode. This is the default setting for historical reasons, but not the
-recommended one (See ForkDaemon below).
+mode. This is the default setting, you usually don't have to specify this
+option.
 .IP "-D"
-Run in \fBDaemon\fP mode. In this mode, BitlBee forks to the background and
+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 mode is also useful for debugging.
-.IP "-F"
-Run in \fBForkDaemon\fP mode. This is similar to ordinary daemon mode, but every
-client gets its own process. Easier to set up than inetd mode, and without
-the possible stability issues. This is the recommended runmode for most
-use cases.
-
-.PP
-.SH OPTIONS
-.PP
-
+This is still experimental. See the note above for more information.
 .IP "-i \fIaddress\fP"
-When running in daemon mode, specifies the network interface
+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"
-When running in daemon mode, specifies the port number on
+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"
-When running in daemon mode, this option prevents BitlBee from
+Only useful when running in daemon mode. This option prevents BitlBee from
 forking into the background.
 .IP "-v"
@@ -96,16 +86,19 @@
 .SH COMMANDS
 To get a complete list of commands, please use the \fBhelp commands\fP
-command in the &bitlbee channel after connecting to bitlbee.
+command in the #bitlbee channel.
 .SH "SEE ALSO"
+.BR ircd (8), 
 .BR inetd (8), 
-.BR inetd.conf (5)
+.BR inetd.conf (5), 
+.BR gaim (1).
 
-.BR https://www.bitlbee.org/
+.BR http://www.bitlbee.org/
 
 For more information on using BitlBee, once connected, you should use
-the \fBhelp\fP command in the &bitlbee channel.
+the on-line help system.
 .SH BUGS
-Of course there are bugs. If you find some, please report them at
-\fBhttps://bugs.bitlbee.org/\fP.
+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
@@ -124,3 +117,7 @@
 .SH AUTHORS
 .PP
- Wilmer van der Gaast <wilmer@gaast.net>
+ Wilmer van der Gaast <lintux@lintux.cx>
+.BR
+ Jelmer Vernooij <jelmer@vernstok.nl>
+.BR
+ Maurits Dijkstra <mauritsd@xs4all.nl>
Index: doc/bitlbee.xinetd
===================================================================
--- doc/bitlbee.xinetd	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ doc/bitlbee.xinetd	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -2,26 +2,11 @@
 ## user, port and/or binary location might be wrong.
 
-## This file assumes you have ircd somewhere in your /etc/services, if things
-## don't work, check that file first.
-service ircd
+service 6667
 {
-	disable = yes
 	socket_type     = stream
 	protocol        = tcp
 	wait            = no
-	
-	user            = bitlbee
-	server          = /usr/sbin/bitlbee
-	server_args     = -I
-	
-	## You might want to limit access to localhost only:
-	bind            = 127.0.0.1
-	
-	## Thanks a lot to friedman@splode.com for telling us about the type
-	## argument, so now this file can be used without having to edit
-	## /etc/services too.
-	type            = UNLISTED
-	port            = 6667
-
-	log_on_failure	+= USERID
+	user            = nobody
+	server          = /usr/local/sbin/bitlbee
+	port		= 6667
 }
Index: c/example_plugin.c
===================================================================
--- doc/example_plugin.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,14 +1,0 @@
-/*
- * This is the most simple possible BitlBee plugin. To use, compile it as
- * a shared library and place it in the plugin directory:
- *
- * gcc -o example.so -shared example.c `pkg-config --cflags bitlbee`
- * cp example.so /usr/local/lib/bitlbee
- */
-#include <stdio.h>
-#include <bitlbee.h>
-
-void init_plugin(void)
-{
-	printf("I am a BitlBee plugin!\n");
-}
Index: c/git-bzr-rev-map
===================================================================
--- doc/git-bzr-rev-map	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,2924 +1,0 @@
-20051106182318	b7d3cc34f68dab7b8f7d0777711317b334fc2219	1	wilmer-gaast.net
-20051106183643	5759edfa4758dd5b4cc25d536a5feb61058622ec	1.1.1	jelmer-samba.org
-20051106184140	9dee6381e5316cb9ac1f3c4e1cf04b271bc3fdcc	2	wilmer-gaast.net
-20051106185257	60d058270d192a6a3f28662e513ae6438c106d8e	3	wilmer-gaast.net
-20051107154125	fe51bcf0ae238e6fde4400b3dd17ddf99f77ae2a	4	wilmer-gaast.net
-20051107161618	7b23afdeead5b873b3e1cfc5ab29ecbf35b8c0ac	4.2.1	jelmer-samba.org
-20051107161924	f7f3ada3350c2a0a99ed307350a230583802cfe0	4.2.2	jelmer-samba.org
-20051107162037	d1d677615e12d759b6cdf7ce23a493bc055ef37f	4.3.1	jelmer-samba.org
-20051107163240	99318adcb88fa3f1dd21882ec364e682fec96c5e	4.3.2	jelmer-samba.org
-20051107164249	abe53d3c48a6552e136ddc8bc554764daf255a05	4.3.3	jelmer-samba.org
-20051107183957	576d6d76c6c730eec71394ab204db2b87bca9888	5	wilmer-gaast.net
-20051108224612	b4a8cd8869262182e85b6112bf7a51ce216a5f2d	4.1.1	jelmer-samba.org
-20051108230225	9c62a7cd418e836664aeee7035a39278687f3a50	4.1.2	jelmer-samba.org
-20051108230534	2095c5749a1024e15a1151db73acd9cf93c9b0cd	6	wilmer-gaast.net
-20051108230646	68b50b5f367de68ca334bb83bf200a3406990410	7	wilmer-gaast.net
-20051109010205	21d09ac864315f80ff42f5b403a22dd833d0dbbc	8	wilmer-gaast.net
-20051109010352	2d99c97d13985576651e84bee7c98877346b8f3a	9	wilmer-gaast.net
-20051112004932	121c9783815ea9f65a48ba971b5c23426b3b362b	10	wilmer-gaast.net
-20051112010532	c92e680146fa2b7506b738f2977d15c59c268b39	11	wilmer-gaast.net
-20051112141414	53575255afa4172cbb089124b32ce7af74a1d3c2	12	wilmer-gaast.net
-20051112141855	06045f66c05241edc13df7652a40f30d15e9f786	13	wilmer-gaast.net
-20051114132914	c1ede6e8035b0338e0254fbfcbbddfeb608b1269	14	wilmer-gaast.net
-20051115132027	b135438c4c6aeb5a7cd3403f0cf37e741d589cd3	4.2.3	jelmer-samba.org
-20051115133524	f56c4917aa26670c03ef9cf4ecdfe2f7fad92aed	4.3.4	jelmer-samba.org
-20051115142947	fbc78447e1e49b42cd1e8ba7e753f0635ec5e021	15	jelmer-samba.org
-20051115142954	9a103a2bdfb9a0afcc4d23e27d39c1a124194e18	16	jelmer-samba.org
-20051115144717	9cb9868256c51a45983aac894428230de6035d79	17	jelmer-samba.org
-20051115145738	c998255d6aa501281c6266a5f1dce69d4c3afab4	4.2.4	jelmer-samba.org
-20051115151653	cc9079ee091707aba3d5b0580a6191eb4541cbec	4.3.5	jelmer-samba.org
-20051115231639	5c09a593072914336dcec3e8e92b28a1d4f03fa0	18	wilmer-gaast.net
-20051115232248	ef6c6a7e01857f9225d4dc198917ba3a09c5e296	19	wilmer-gaast.net
-20051116004125	bb556db516af064649588a771048b004852e4f49	20	wilmer-gaast.net
-20051116142222	ea85a0b99185c53fac40c2150937d5e1a2ca546a	21	wilmer-gaast.net
-20051117125150	685c4efc166b58f21e9782160879a540d00a9641	22	wilmer-gaast.net
-20051117143759	834ff442d1b413fb4eaabcf03bbf6ab78eafdc1e	23	wilmer-gaast.net
-20051118001757	e3a0e7e2d1b3e90ef61afe8ade4e76bf8b2230e1	24	wilmer-gaast.net
-20051118120609	2dff6f72604bc6e7232b215b229f8e7556608651	25	wilmer-Jona.local
-20051118124158	c572dd67167c0365a5bf62899c31efd4f223acaa	26	wilmer-Jona.local
-20051118132144	517ecc45fde83e3b13352fbff5eaa8755296dc7c	27	wilmer-Jona.local
-20051118191020	22d41a26f53527adacc5b314fcdaea0c46a7723d	28	wilmer-gaast.net
-20051118191215	d908e3a5d9c83c415410a8543ab4c074c4a9a072	29	wilmer-gaast.net
-20051118192209	42bdeeced994e3eebc35a849d179d981c45826c0	30	wilmer-gaast.net
-20051119005108	0b2e8435e67b8c9ad94ebf63455d2285474aa656	31	wilmer-gaast.net
-20051119111218	3e1de874e95dae74e970c1c574cf64bda28e04f7	32	wilmer-gaast.net
-20051119114122	4d50898517f2a1449804d477f00b5aa62ee877cc	33	wilmer-gaast.net
-20051119115401	94281efa4280bd3ef1cecb7cd22deca03ecb5935	34	wilmer-gaast.net
-20051119151703	2cdd8ce76ce334327c84516ff78f3b00bef5bebf	4.2.5	jelmer-samba.org
-20051119152446	9c8ae507985a29b41a22cf30b803141e9ba82a9b	4.3.6	jelmer-samba.org
-20051120160923	66c579243cd3273e12decf9744210d7dc1877cec	35	jelmer-samba.org
-20051120210117	831c955cf3ed0731a53543594ffd427a0a8eb036	36	wilmer-gaast.net
-20051120210218	df61847525ba9e8d6d42c4295d034175fd2d0445	37	wilmer-gaast.net
-20051121084928	986d18f2eb1d87009c4b0505e18a335d796e49cb	38	wilmer-gaast.net
-20051121085416	57db63b585619c2cdaf7627207e3141738d7258d	39	wilmer-gaast.net
-20051121115348	689a6e0b8f91eecfc3add9dae769bfcda5a35b77	40	wilmer-gaast.net
-20051123173207	34c0e90839ea426c20838b97e4dbc423ff690eb9	41	wilmer-gaast.net
-20051123173518	43e3368f4ad8249523d9c8a0d57a764ac370daf4	42	wilmer-gaast.net
-20051125223817	8045d37c77f06f40c8d8fa9cc4d06d35b075283d	43	wilmer-gaast.net
-20051126013728	513a3239198d3e4ec26799046f75206a4d9c599c	40.1.1	jelmer-samba.org
-20051126014003	1aa7ec7d1f2cf23c28cae05b32bd7e4a4aaebc4d	40.1.2	jelmer-samba.org
-20051126014220	cb91b7267047beaf54e7a6c20cf71bf2c211e68f	44	wilmer-gaast.net
-20051126020238	30f248a785685491a7e55c804e9221c04c8bdce4	45	wilmer-gaast.net
-20051126022438	dfde8e08f71cfd24c9c247962bb4ddbed0b089fa	46	wilmer-gaast.net
-20051128010756	cf136714caa13e2cd4bf8a69a29c08f3a1518f59	4.3.7	jelmer-samba.org
-20051128011406	b20b32f0785f9a1eff448382a98e64812469e112	4.2.6	jelmer-samba.org
-20051128011435	cfcc5877b737800ab84cd67d9c413e7254ac1f50	4.3.8	jelmer-samba.org
-20051128220051	f712188c6240cb6f77791b4f05e55b1a001d4037	47	jelmer-samba.org
-20051128220450	ed165fe64cd3d7b7c34c536019da3aed2b5e7541	4.3.9	jelmer-rhonwyn
-20051128232709	65e2ce1ffdead577ed90148d33e36794136b3232	4.2.7	jelmer-samba.org
-20051128233250	2983f5e8c2d3046bf01337e5caefa3af55ba6bff	4.3.10	jelmer-rhonwyn
-20051129004315	c7cf9d6f811e299c3659350c6bd21c75ba249de5	48	wilmer-gaast.net
-20051129005214	ac55e509923bf40c04cdba905534a41c198f3771	4.2.8	jelmer-samba.org
-20051129010209	500a1b60d3dd0565d215721e0db7ec7da2dec2e0	4.2.9	jelmer-samba.org
-20051129013819	ef53ba88817395cdfd012e839715f833d2f6cefb	49	wilmer-gaast.net
-20051129090300	a40a2c298b6a45542255aa703ebc441487f05c4e	50	wilmer-gaast.net
-20051130121225	c3c2e1403287380e5e9d520900f761bbfa738b9f	51	wilmer-gaast.net
-20051130133659	6caa56ab8e53f3b954da2ad1bbc779bfba1737ed	52	wilmer-gaast.net
-20051201115225	7c2d798b79041108ae71f86e7dbfdf9bf984dcb0	53	wilmer-gaast.net
-20051201200509	e5663e0aad633278512b2c09606bd6939e094adb	54	wilmer-gaast.net
-20051202113003	626b446e0a4f10fbcf38661013a592bcd3193e08	55	wilmer-gaast.net
-20051202114347	027d2ebf750a011bf544f7d279cfb706594e5d05	56	wilmer-gaast.net
-20051202234157	f1df064766075ac2f36fb4027a4a683964f3be9a	57	wilmer-gaast.net
-20051203140908	46cce70a92b3b541033ea04c854452206bbe7a20	58	wilmer-gaast.net
-20051203234917	8f515c822d4bea8439c5ca076d965ae52010ec2d	59	wilmer-gaast.net
-20051204004857	25d1be7fbfe217b756861b4306ff7a5ae77becb1	60	wilmer-gaast.net
-20051204023213	ed5970042daafa82eb7b500d8bed476a2b230614	61	wilmer-gaast.net
-20051204023755	a37e578151ccf4c34932480df9b52ebf53862bfc	62	wilmer-gaast.net
-20051204023818	bad4a5869128fa8d877d4def4c7e10d044423e73	63	wilmer-gaast.net
-20051204024303	8d6c4b15aa781711dfeab21ecd7fac4da4f0a3bd	64	wilmer-gaast.net
-20051204024907	40657a3c74839652d1ac36844791a0532a5281f8	65	wilmer-gaast.net
-20051204031807	a309db1126d08ecd177492a0687b98be1c8ba0cf	66	wilmer-gaast.net
-20051204032617	ea911ca247dd060f864e09ff54585e91f68c9de3	67	wilmer-gaast.net
-20051204125841	e7f46c56ffa29c6f8f4917c5f367a61706758e2a	60.1.1	root-f0rked.com
-20051204151232	d636233a518fbe46264230866d4b8ea463f1474e	68	wilmer-gaast.net
-20051204193214	b8ef1b1aacfa2f407b6245174a06a67fa2f114af	68.2.1	nelhage-mit.edu
-20051204215515	019c031a8d77fa8f21792ccf0e07c2dfa058ce5f	4.2.10	jelmer-samba.org
-20051206215509	c3ffa45876c584d3e86c0796f2210538bcebc377	69	jelmer-samba.org
-20051208123043	1eddf6b197ba5fbd3f1cce390396efc7d25c9de9	70	jelmer-samba.org
-20051208134153	1ee6c18cfb5eb03f33a5938b37e357dd3fd2c164	68.1.1	jelmer-samba.org
-20051208141428	a1f17d45fae99428c8024168b55b4279c59ac867	68.1.2	jelmer-samba.org
-20051208143739	7989fcf34257201f54538f289cce1c651341e142	68.1.3	jelmer-samba.org
-20051208145106	c2295f7eeac263dbcc19f84e9a61abbe778aa9f8	71	jelmer-samba.org
-20051208145713	8efa2f49816aaac986137a5da1f6c35425282195	68.1.4	jelmer-samba.org
-20051208153125	09adf08684c62fff0f507304ed37680137de4637	68.1.5	jelmer-samba.org
-20051208160008	7cad7b41a0b661c38ae5f6239aaf58361788edc9	68.1.6	jelmer-samba.org
-20051208160029	87c24bab5738824aba97a824b4c02f7a96b555a0	68.1.7	jelmer-samba.org
-20051209204845	9df916f7d178f5b87e8ca35e3eb44343f3d2d955	72	jelmer-samba.org
-20051210145049	ab49fdcec9a09df839ec488e570672f2dd904dc7	68.1.8	jelmer-samba.org
-20051210152841	34759e68501ccf2a885f650df9d35ba3fc84658d	73	jelmer-samba.org
-20051213212034	1aa0bb5648a5e4a1ceef01202bf4bee4234f96c3	74	wilmer-gaast.net
-20051213212206	ab4afbab829a4a5bf520d4e1425a743cdb8c4865	75	wilmer-gaast.net
-20051213213918	568aaf7ce17a4db2dddd13f8baed02a6d2757eb6	4.2.11	jelmer-samba.org
-20051213224359	a301379c2035d9d0dd86926c4bdeebf95db18fac	68.1.9	jelmer-samba.org
-20051213230527	b73ac9c35fb53203b8d0668251dfb66096883863	68.1.10	jelmer-samba.org
-20051213230705	d3307e281afdbed22a561eb2cd73f97a03e3073d	73.1.1	jelmer-samba.org
-20051213232121	6aaa2213588f7c4c00a68c5622f6974679eaf196	76	wilmer-gaast.net
-20051213232758	22bf64eab8a79352317ee190cddfeef3011aa8dc	77	wilmer-gaast.net
-20051214011725	703f0f7f4622194bce8023712a3a9966be6f8827	77.1.1	jelmer-samba.org
-20051214111504	547f9373fe4cda601e53759500d189dd580a0d78	77.1.2	jelmer-samba.org
-20051215091438	11e090b246a86deb7b882217772b90fc52d7e4f6	78	wilmer-gaast.net
-20051215115527	bf02a679c61b0f030ee8f2f01698699a7775f7d5	79	wilmer-gaast.net
-20051215122425	bd69a219c0a618354fd80a98dd0d9a04fee755e0	4.3.11	jelmer-samba.org
-20051216094534	15832cc6c8475ae454a72a2a12f231fef852e886	80	wilmer-gaast.net
-20051216094744	c4168f4cfe553d111941ee385a94facb5449652d	81	wilmer-gaast.net
-20051216175800	701acdd41542656493d753a75480bc0594ea74b9	82	wilmer-gaast.net
-20051217004835	4bfca70ab85926364682a11aef5f921eb337cf54	82.1.1	jelmer-samba.org
-20051217010038	32c632fb46e58043a2d437c44cad783a59b7aea9	82.1.2	jelmer-samba.org
-20051217012532	4146a07de5f44f3ea00a3ef0026098b28e7451de	4.3.12	jelmer-samba.org
-20051217012558	643dfc4be2e84dc4acb1a446867705e150480210	4.3.13	jelmer-samba.org
-20051217100652	12c6634050984e3a7da472b603ddb63e7c7500c8	83	wilmer-gaast.net
-20051217121415	3d64e5bbdcca4df5a3d4aa79041d0b54f68f599f	83.1.1	wilmer-gaast.net
-20051217123611	dd9a51b2ec4ce539721c46b30244fa853564788c	84	wilmer-gaast.net
-20051217123807	54c5ca1c600a7b496163b9c8d9c1901f1d0c8845	85	wilmer-gaast.net
-20051217124047	afe076473f3b18c2d0ec402b43345eda6709c91a	86	wilmer-gaast.net
-20051217165412	8a9afe4eaa42052006fb64c6755ac0321b97ab55	83.1.2	wilmer-gaast.net
-20051217165518	b5a22e33a1aa2500093e783e79de2f44bf53c150	87	wilmer-gaast.net
-20051217185546	52b3a9978681da7c3f2cd21cd6987eb158a54a03	83.1.3	wilmer-gaast.net
-20051217190707	2db811a764d70734cfe2d9e0992430e2f7fc53e9	83.1.4	wilmer-gaast.net
-20051217191332	f3e1e9257bacc129b5ce9d18001f7ee99593d74d	83.1.5	wilmer-gaast.net
-20051217205406	ad8b8a367c8e5dd2816096959682e2187df07c6c	83.1.6	wilmer-gaast.net
-20051217205650	7c0a4975a538ab75405e64919bbdfee51b898b2e	88	wilmer-gaast.net
-20051218010229	a03a9f376fe2fd1737ba7af6a87d99fe43a2a72e	89	wilmer-gaast.net
-20051218010611	43f205b144412ab1186a545321b54792d92d2043	90	wilmer-gaast.net
-20051218103913	3b9390b811d877e50b9e54109c52b60e789bd04e	91	wilmer-gaast.net
-20051218114825	578d627c65491cba8ac6a0bb0f2d05147bf87535	92	wilmer-gaast.net
-20051218142852	00f434f7854d593b8a204724281ec4c794d0098c	93	wilmer-gaast.net
-20051218161024	e3fb6789b0004c7162efde679632bf094b7b0eec	4.3.14	jelmer-samba.org
-20051218162149	7308b63f3300d5b2a326edfde6c50a18bc05e3e5	94	jelmer-samba.org
-20051220205338	1fa6a235283df04233a5dced99ab9a924bfb65f9	93.1.1	wilmer-gaast.net
-20051221121310	d9d36fc09996ed8c2574f9336c3594aa3cc56941	93.6.1	jelmer-samba.org
-20051226124129	ffea9b950b183dd54952b56703506508b0984d4b	93.1.2	wilmer-gaast.net
-20051226140247	d25f6fcbe8e291dd858bf734fa85cde176dc8415	93.1.3	wilmer-gaast.net
-20051226144254	238f828cb3524a2a09337d7502cc6db9556fc67a	93.1.4	wilmer-gaast.net
-20051227143932	e4d62711995840def3a2d51f62923d95cb368cee	93.1.5	wilmer-gaast.net
-20051227151015	e62762bc4b884d576c00fd7a2636b610e7c1b578	93.1.6	wilmer-gaast.net
-20051227152035	c88999c6ea83ffbc8a0eb8f1ceb0ee07612dfe29	93.1.7	wilmer-gaast.net
-20051231202915	a252c1ad43823eb935148a5578ee0d666902b2f1	93.1.8	wilmer-gaast.net
-20060103183054	39cc341b8f6299fbf8a62b243d278d1e48c8def7	93.1.9	wilmer-gaast.net
-20060103190439	13c4cd343f661fae01660fa8b889c9dfac4a36ef	93.1.10	wilmer-gaast.net
-20060104111658	2a6ca4f4d8c8ec86b8bb38442189c85a001a5f6c	93.1.11	wilmer-gaast.net
-20060104113329	0196c47511000b3ee2060af6e7fd600b23876010	93.1.12	wilmer-gaast.net
-20060104120907	7e563ed9a130cce070753f011158ef0714c58d46	93.1.13	wilmer-gaast.net
-20060104120935	08995b0801a00ef5cbcc12d7eb070ef349dde64e	93.1.14	wilmer-gaast.net
-20060109205609	2d75b56071f216cc3c8ff9791326df074ec4b806	93.1.15	wilmer-gaast.net
-20060110142048	4fe4be2723eba3b7f3640396a1c8fa80b39fe2ab	93.1.16	wilmer-gaast.net
-20060110143649	dd8d4c5243eea91dd3b0709ae76abdd3743e99bc	93.1.17	wilmer-gaast.net
-20060110213256	3e91c3ec7d6426c4c2819e78275f935e1a7fce2c	4.3.15	jelmer-samba.org
-20060110213508	8e419cb4f86679636b2d96618e1bec4853636c11	4.3.16	jelmer-samba.org
-20060113164146	277674c82d3dbcb355214cbaceb34599832e1261	93.1.18	wilmer-gaast.net
-20060113221029	5c577bd5e2ee33dbe7389df6f4baf659c34de365	93.3.1	wilmer-gaast.net
-20060113235121	0298d1195bb76c1e400695916b4cf65480b0dd76	93.4.1	wilmer-gaast.net
-20060114171015	d2cbe0a03d2621a3a2b90055ea191cc11766dddf	93.1.19	wilmer-gaast.net
-20060114174829	c22c210f560aee8a43b9fbff63c3dc408b434094	93.4.2	wilmer-gaast.net
-20060114182500	edf965753380a165ec615b79d45251931ac3ea62	93.4.3	wilmer-gaast.net
-20060114191248	b23c5c758097e7c7e9e0141ba9467177c0c32114	93.4.4	wilmer-gaast.net
-20060114203159	de3e100b7fa676bb628ead4cca2b8cee91fc3a84	93.4.5	wilmer-gaast.net
-20060114203410	9d6b229674144d2a48348aeb120604164f371904	93.3.2	wilmer-gaast.net
-20060115014949	0431ea17870f5c382fe5fb692c2852ae80432737	93.3.3	wilmer-gaast.net
-20060115113354	e0ca412a709c80cc7fe26db0576c2bc38be4a45a	93.3.4	wilmer-gaast.net
-20060115141649	13caf0aa5d1e5575b74221e0cd9e4ff9f4cd79a8	93.3.5	wilmer-gaast.net
-20060115154220	74c119dd1b066329eba59d057935ba7ec7249555	93.3.6	wilmer-gaast.net
-20060115164155	6fda35070cadc83a6b78c85896fcaf85299e5ad8	93.3.7	wilmer-gaast.net
-20060115203159	f4a59408250b76173418fad090d4623e5300c90f	93.3.8	wilmer-gaast.net
-20060115233713	d9909972078956f7f362ba479cc22250cc6e1b13	93.1.20	wilmer-gaast.net
-20060117180522	daa9e027e8c8d1bc91e104dba985d5d44fef8c77	93.3.9	wilmer-gaast.net
-20060117211542	48721c328e574e0ff76c41734b78aab217edbf65	93.3.10	wilmer-gaast.net
-20060118075350	ac9f0e965b5002c9eb022f77eb7c6f16cba662b1	93.2.1	wilmer-gaast.net
-20060118181435	1ea13be2cf335a471f85ea54d610fb91b7d14564	93.3.11	wilmer-gaast.net
-20060118182531	c1826c6f72d1fe85e1c5decf5207601dac2c23e7	93.2.2	wilmer-gaast.net
-20060118221509	92ad3d44da447199f45dbc438d061cc4e2261dbd	93.1.21	wilmer-gaast.net
-20060118221759	4c266f2d79212e4e0ff4b9b33088685a2ec452e4	93.3.12	wilmer-gaast.net
-20060119134515	e8f8b187fea053e207224848720514372ede8d4b	93.1.22	wilmer-gaast.net
-20060119163441	2face6292c693afb45e5e04f87ba080931eb16c3	93.3.13	wilmer-gaast.net
-20060119170747	bd9b00f4fed3560eab98f15cf9923aed13467d5d	93.3.14	wilmer-gaast.net
-20060119175219	5424c76c7813f82e2f98546f6a46b73d80181877	93.3.15	wilmer-gaast.net
-20060119222303	2fa825ba7c79a0ab4ed9a534865974e918b49100	93.1.23	wilmer-gaast.net
-20060119222425	b8c2ace5985879a4c13d366756eb5e444a240ec9	93.3.16	wilmer-gaast.net
-20060120122124	fc50d482ae5a7836fbf7c72df168b51d1cf714a5	93.1.24	wilmer-gaast.net
-20060120122230	55ec2d66f04d1ea96e180c5a46e90a4294dea0b1	93.1.25	wilmer-gaast.net
-20060120151549	f73b9697f9be18e04ec7458634520f9dd2e2432f	93.1.26	wilmer-gaast.net
-20060121222358	f1d38f20f760376f43b90a105486cf3ff2fbf2c4	93.1.27	wilmer-gaast.net
-20060121223110	54879ab3b2cbcaf1c9114bddd85ec5d4dc097915	93.5.1	wilmer-gaast.net
-20060122001503	87de505f3203054a2a0becbea5a064d24ab2cde3	93.1.28	wilmer-gaast.net
-20060122091903	57c4fc067a4a43cebee74e9d6076eb538986d5cc	93.5.2	wilmer-gaast.net
-20060122203356	a49dcd5c3c6b79470ad71dc45ccf29f65ba2a7f9	93.5.3	wilmer-gaast.net
-20060123142018	a91ecee010fdbd8e62e874e23a443206a48308c0	93.1.29	wilmer-gaast.net
-20060123142036	68c7c145c281fe3ae734b345bf133d70d1ef8652	93.1.30	wilmer-gaast.net
-20060123232813	9fae35c9cf2d5a319623946705e5d7179ea5c338	95	jelmer-samba.org
-20060130133059	105383688a6605ca40ab4fa987d72809170a2e36	93.1.31	wilmer-gaast.net
-20060130160707	8365610bb2013d3478214511910daceabd29ad09	93.1.32	wilmer-gaast.net
-20060202132144	34b17d9b8901b72439167b99d780c481ce420e33	93.1.33	wilmer-gaast.net
-20060202135914	1d2e3c2ee2fb4c12ad25832b93ae9513d042717a	93.1.34	wilmer-gaast.net
-20060212072049	ec3e4116687f5e990e6d24e2c79e629665ac5e7e	96	jelmer-samba.org
-20060212072438	58bc4e69967a8feec0a60dfab716985191c12817	97	jelmer-samba.org
-20060212072531	a323a22773714a19254db34156500a67e5916451	93.6.2	jelmer-samba.org
-20060212072620	5ebe625399d5116e222d6389434f645e906265ec	93.6.3	jelmer-samba.org
-20060212080203	f665dabdff831743ea35e755b6ec1e2fe2551d9c	93.6.4	jelmer-samba.org
-20060212221335	7cf85e77cf36c2d582fad168996825aae82c9b85	98	wilmer-gaast.net
-20060301210803	6dff9d4ac2cbd279e25db60ae26086c830764682	98.1.1	jelmer-samba.org
-20060301221757	5e713f695f93f7dc88f225bf6a8cd16e228eff11	99	wilmer-gaast.net
-20060301223010	8a56e52a518855150525eab9e0fcdbe776223ffc	98.1.2	jelmer-samba.org
-20060301223143	5fe0207972bb2466a40dcbbc39dd1e93545f4913	98.1.3	jelmer-samba.org
-20060301223550	9a1555dc8521f0973347911bcb26d1038259f967	98.1.4	jelmer-samba.org
-20060301224837	a4dc9f77de03eb46ecabed02dbd1b678319cf11d	4.3.17	jelmer-samba.org
-20060302111240	46ad029950221205d1eb6201ec2f01c7231876c2	4.3.18	jelmer-samba.org
-20060302113833	e506d6ce40dd357e29a7c856ab2b664df69cf015	98.1.5	jelmer-samba.org
-20060302114333	1e5fcac2ef56723dbc0220844d119a89633b479d	98.1.6	jelmer-samba.org
-20060302124336	cdb92c53a9e930bc6c685127d13b743872959e75	98.1.7	jelmer-samba.org
-20060303103253	27ac72d69b605df62e4f56b04ed7aec0c4e4ba1f	100	wilmer-gaast.net
-20060305200733	96ace1b808f4df3f2106fa90c19fdc23408f924d	93.6.5	jelmer-samba.org
-20060305223406	26fdfc5c39ebc2ca75ec3fa6e8d697a98d217edc	101	wilmer-gaast.net
-20060306121546	022e77fee802dfc50b8dce51ac10ea0597f7a64a	102	wilmer-gaast.net
-20060315181701	55cc2be3e83f82b9d26565fce235ccc5a5631c5f	103	wilmer-gaast.net
-20060315181726	58f31364d2ddefee79396ff73126a21fd5022fa8	104	wilmer-gaast.net
-20060315191916	84c1a0ac4530a04113a349dbc3dd15f0e0a4e683	105	wilmer-gaast.net
-20060317153916	8ba511db7b32975df97247474782f82d6a3906f9	106	wilmer-gaast.net
-20060318083333	61dddd0442c0e3c792cf90368c00681051b381a5	107	wilmer-gaast.net
-20060318091044	42d957194cdf8a3437d7d12e50b2c9d33f98b4d3	108	wilmer-gaast.net
-20060318091057	82898afe3d659db4fc921aa22a1b21fddd7b2fd0	109	wilmer-gaast.net
-20060321080012	bc736cfadae460de29553ec0f9bb6e452dfe407d	110	wilmer-gaast.net
-20060321081110	1ad104ab06fe3f7db1b55cc178e92e43cbda9d95	111	wilmer-gaast.net
-20060321081222	aefa533eb48587d9f509a8dcab358061b72c7b3b	112	wilmer-gaast.net
-20060321081901	19a6c756bd064e8955f1a733c4e71f9c41bba36c	113	wilmer-gaast.net
-20060321083546	87b6a3e49a764201011c8d9f802beddd82b86821	114	wilmer-gaast.net
-20060321083610	7b07dc6cae23180e8a06b8663ced71fb4ace7775	115	wilmer-gaast.net
-20060321083815	9b8efab1327b0263871fd95fc8612a21c7bb2389	116	wilmer-gaast.net
-20060322173503	aa5ac01a7ed0808a981cabf81b91a3e6a2ee1c9a	117	wilmer-gaast.net
-20060322182241	9b8a38bfa9a89e9741521ac522927c80b68976cf	118	wilmer-gaast.net
-20060324151638	728a981e422539df38d27d87e33829082d376ac6	93.6.6	jelmer-samba.org
-20060324155329	f32d5578d7039f1e61e99b2e1f7bfd0a47828c8c	93.6.7	jelmer-samba.org
-20060324164800	d5dfc3d7ff2756a5991356bb643630fa7b03f8d9	93.6.8	jelmer-samba.org
-20060324164816	a15c097fa32028394264cf66ef4fd31f56315eb3	93.6.9	jelmer-samba.org
-20060330171438	a014331db88702de88f3327a0b87f9b8258c8d43	119	wilmer-gaast.net
-20060331071227	192b80a1175690187be4bad30c36d286f8e63eb0	120	wilmer-gaast.net
-20060331073908	755ae5b84f4b0806e339a6e1503406bf43028fd5	121	wilmer-gaast.net
-20060331085353	d783e48a831cf5058e2307a382e7e95a06680289	122	wilmer-gaast.net
-20060331175547	e27661d09e8c3fc85c979d4769ba20d1d3c289e2	123	wilmer-gaast.net
-20060331210936	7d31002c391868a24693eadaf1c9c6024e1a30cb	124	wilmer-gaast.net
-20060401154055	e6e1f1815e332f88be3c315f1f8cebf0047721f5	125	wilmer-gaast.net
-20060401181855	6adcb6c6bcd96478ec3ab89efcc43bf15d6b12e0	126	wilmer-gaast.net
-20060402082934	57ef86480b13cec08cec29f8352b168d9a16f4e4	127	wilmer-gaast.net
-20060403202135	e6d6047924c7f6b70d51398a617ada03ac1850de	128	wilmer-gaast.net
-20060403213445	f8de26f98e3c38d3deacb674e533c588e6b48548	129	wilmer-gaast.net
-20060407092221	36fa9bd3e3b918b282421b38735b4d1c024d799a	129.1.1	wilmer-gaast.net
-20060407092248	7bf0f5f0ba40eb120809743220e3f776455fda63	129.1.2	wilmer-gaast.net
-20060407093437	c2fbf86f9b3cdcb417960fd35db4abec09c8e3a4	129.1.3	wilmer-gaast.net
-20060407192758	9e08d5d5ba8958807158d7afaedd40e60cfb40ff	129.1.4	wilmer-gaast.net
-20060408141029	11bcee97b781bac6e38db4e1630ac6934209e73b	130	wilmer-gaast.net
-20060408143749	85616c361bf152a10f774ad65068103d0724e42b	131	wilmer-gaast.net
-20060413071542	88b3a0738032596d74cb98ee6cdc8feab4b50d30	130.1.1	wilmer-gaast.net
-20060413073011	c99af3a10749850864e230b660060f6a0719925a	130.1.2	wilmer-gaast.net
-20060414061823	4d8988640fd540caced98ab21296f16d3b940881	130.1.3	wilmer-gaast.net
-20060417080955	5aa9551e1909e076edf733697f77bb0dc7a7ddde	130.1.4	wilmer-gaast.net
-20060417082516	6e62132fd4433e48ca85ee8ffcc6e848acb2547e	130.1.5	wilmer-gaast.net
-20060425072149	79c6f9f8fff2b4c4627353e8a8eeb591d99a2098	130.1.6	wilmer-gaast.net
-20060425175723	3edaed94d464210dd64e0d337a95287e3f12bb2d	132	wilmer-gaast.net
-20060505161250	1be54a9f9590beeec71e32bd3386c6e8aa60bcad	133	wilmer-gaast.net
-20060505162353	2b13c3c90d246ce032890fd8cd040ddb00c2767e	134	wilmer-gaast.net
-20060505163157	ea3a26d921acd43a627f750eb9aa7949ba2036a5	135	wilmer-gaast.net
-20060507112246	64d1f45ed9ccfd90ba17592ac980dcb3436f9548	136	wilmer-gaast.net
-20060507180743	a0d04d6253cf70877a11156059209e1f9a2efe31	136.3.1	wilmer-gaast.net
-20060509072005	ecf8fa820aa8fa3592bdf96ed3bdeec4ec94f036	136.3.2	wilmer-gaast.net
-20060509072036	67b6766489f1b9b5f2249659b0ddf260e6f8f51b	136.3.3	wilmer-gaast.net
-20060510124755	5d6c17898cbafe1585ac626af5a0fcd5508d9c65	136.1.1	jelmer-samba.org
-20060510125759	764b0aba8754ab488ccc84f5cba0f9bd0c454707	137	wilmer-gaast.net
-20060510173446	ba9edaa568088900145bbd1004c864b7d408c38d	136.3.4	wilmer-gaast.net
-20060512180302	13cc96cd2c57a96c16db9f333d594242f6aa36e3	136.3.5	wilmer-gaast.net
-20060512183144	09f8cd1fedff2022da395d8accc9ac71a16215ea	136.3.6	wilmer-gaast.net
-20060513092349	b642f38161474516c8e9fda1a9942d45a4ea9f22	136.3.7	wilmer-gaast.net
-20060513102953	fc2ee0f84c1746cb17c448ee75c4206dca548325	136.3.8	wilmer-gaast.net
-20060513104237	2b7d2d1537040cac6841a63437e6c04d8a7441bc	136.3.9	wilmer-gaast.net
-20060513142032	309cb9e19395261d020f7ea649807ff1adbe76de	136.3.10	wilmer-gaast.net
-20060513190114	782d988a88582a0f3dd6539664d535fb790df024	136.3.11	wilmer-gaast.net
-20060513194459	19ac9c5c98a8d5adf22bcf5b8e2d4141c82a3275	136.3.12	wilmer-gaast.net
-20060513223051	0356ae3aa10bb6556d0ea881988831cad5e71f38	136.3.13	wilmer-gaast.net
-20060513232831	3f199fcf6242fc0cc0e4e767ce2a505792d80fef	136.3.14	wilmer-gaast.net
-20060513234321	df70eafa7ae30a18d646d918f959341d5bb047b1	136.3.15	wilmer-gaast.net
-20060514084105	919c27cc6f1d7654505ac17e80d1fd7197a233f5	136.3.16	wilmer-gaast.net
-20060514090541	5330e3d7fdb323ee4176b36639b59f6f90c22617	136.3.17	wilmer-gaast.net
-20060514103221	85cf37ff0121898d01e9830e237f41f3ddd7dfaa	136.4.1	wilmer-gaast.net
-20060515091946	b8b8c6c65ce89f676d269a1897e3a938895cabde	136.3.18	wilmer-gaast.net
-20060515092654	7a685f15d0e5774467a8721eb97d059726ce792b	136.3.19	wilmer-gaast.net
-20060515095201	c98be000bb619dd597194b3a50a06f9717649f80	136.3.20	wilmer-gaast.net
-20060515095704	af9980f76b1f79aab90addc0228417093993e96b	136.3.21	wilmer-gaast.net
-20060515175712	dd89a55a9b54e29da43d6adea00fc2c42e3e7ebd	138	wilmer-gaast.net
-20060516070911	5a348c3164f5f2d1a1f759c01e8bc7df2ad0dadb	136.2.1	wilmer-gaast.net
-20060516212134	c2ee85ca305873302588224e14d200ca3dcd3057	136.5.1	wilmer-gaast.net
-20060516212321	10a0abfb5322aafddb21dabb34e8b89eaf4a05e0	139	wilmer-gaast.net
-20060516212759	75cde5d1d58025d6eae027d82b26e896d83cb041	140	wilmer-gaast.net
-20060517131520	0790644bf8ccfbb1f09f4e7209fc18c2e97f10d8	136.2.2	wilmer-gaast.net
-20060518071115	266fe2fe078833bc5489a3fddd970b9307a7bbfa	136.2.3	wilmer-gaast.net
-20060518164118	0eec3866ac883667045cc028d5f0dac0b4872de7	136.2.4	wilmer-gaast.net
-20060518165006	41e520279f8633d11f79623574b40af7f8949403	136.2.5	wilmer-gaast.net
-20060518170528	1b5ab3608488e0cbbc75933a59af1fa7e2ff60b0	136.2.6	wilmer-gaast.net
-20060519074830	ac83732afa83f21915e536c5c6f29f033f8623a3	136.3.22	wilmer-gaast.net
-20060519075303	881fd4e3a6d6405e982239e8b315069b724a1d22	141	wilmer-gaast.net
-20060519075553	41ca004ade77a9c343efbd523dd88deb9231487e	136.3.23	wilmer-gaast.net
-20060520114808	c53911ec23848d13cbbaa10286895d874abd0b08	142	wilmer-gaast.net
-20060521195552	f66c701e85ed9febdd065d0fd5808be3e8af6811	143	wilmer-gaast.net
-20060521225154	619a68171055ca6ec460557176bd59817c09b736	144	wilmer-gaast.net
-20060522091149	73cf7fd5f066d3c0720f58af840affa3e61bad12	145	wilmer-gaast.net
-20060523074514	226fce105c1189bde1aa321b494494d49b463e90	146	wilmer-gaast.net
-20060523080916	da3b53657c8e554fc8c28c8ef61ef44492da24dd	147	wilmer-gaast.net
-20060523081824	a9ca7dd4d4b6dc55be46bbbdd166922c69a73590	148	wilmer-gaast.net
-20060523083104	fc630f9fb47690c30feaf4738727a213d633afc9	149	wilmer-gaast.net
-20060524230418	601e81362bbf4e4d1e686334b35d3bdcd87314d2	4.3.19	jelmer-samba.org
-20060524233120	80c1e4d9e8c82a83499d6b66cdf3a95d15bf0fa1	4.3.20	jelmer-samba.org
-20060525085731	936ded65a76dac7a7af56ef3f1ad6c84a32270c6	150	wilmer-gaast.net
-20060525094636	1c8a7a232935d1c2b7f1916faae11d730624bdc3	149.1.1	jelmer-samba.org
-20060525095353	0a69d7bf97b76852c25b7f6634204c5fb5532487	149.1.2	jelmer-samba.org
-20060525095500	51a4ffb83d6fbe23f1c2b8499cc78584e7213812	4.3.21	jelmer-samba.org
-20060525112206	0602496cb6cedc917abbd0a12468e9329c6967e1	151	wilmer-gaast.net
-20060525113122	5d9b79251c54a73a8a79b2b2aa185e8516d0b402	152	wilmer-gaast.net
-20060525160915	b1bd100c3d3f866e0fa891fc5ce6a4d4c5a284e6	4.3.22	jelmer-samba.org
-20060525161039	1705ec3724462b1ceac91aefb5bef24d7c8def54	4.3.23	jelmer-samba.org
-20060525230815	eecccf1230639dc883c5687b442a89df89040ae2	4.3.24	jelmer-samba.org
-20060525232054	574af7e01a0cc738b4bbe7e903572943a85b9691	152.1.1	jelmer-samba.org
-20060526085736	7bfd574a6dc85b365f4a7a397ad5ca681faeb0e4	153	wilmer-gaast.net
-20060526090338	68b518d6d9a536e2117b399fe4f7fdecdd5d7131	136.3.24	wilmer-gaast.net
-20060526130620	60487448f3d0f3f7bb4710716e0ee257ee2edf16	154	wilmer-gaast.net
-20060526150209	1cda4f348372a755d99b291e6f4f9973a949f441	4.3.25	jelmer-samba.org
-20060526150241	6fb6410be208b9a6ac4a3d407ac7a221e7d852b7	4.3.26	jelmer-samba.org
-20060526154558	84eddeefb197efe9a44d5a866bca209156b52257	4.3.27	jelmer-samba.org
-20060526154623	aec56b0acf7965e7ec484b76ec1902603b8b3ea5	4.3.28	jelmer-samba.org
-20060526154651	fcc2da97bcfa5e0d704179fae9c4ed59cbaf79c5	4.3.29	jelmer-samba.org
-20060526183250	7deb4471891059edf6000ffc7502a2a7bdc70e78	155	wilmer-gaast.net
-20060526183516	fe237200e4b3921e190d13693402e14d63fe2fa4	156	wilmer-gaast.net
-20060526224448	a2582c84bda1ed8940c75bd842f9296cef3f50d4	157	wilmer-gaast.net
-20060526224928	db28304aed1fbe47f0a4076f3c97850059f262e6	158	wilmer-gaast.net
-20060527150222	875ad4201402b1a8f80ba22a6cdcdb152c6e5510	4.3.30	jelmer-samba.org
-20060527153358	cdca30b360c09399f1e5a2556d83ec997006cd75	136.3.25	wilmer-gaast.net
-20060528182443	42616d147d9b6bfb5d09ea6dc237605917765853	159	wilmer-gaast.net
-20060528230700	79b6213c1fa2ffaa102365515551e9f0ea9fdc1a	160	wilmer-gaast.net
-20060528231347	4ff09664d2570be8358a3bde62123cc8e0a17c9e	136.3.26	wilmer-gaast.net
-20060528232142	c38e9656987d0b56f233353d2d0fa63c68dcf365	161	wilmer-gaast.net
-20060602190134	df417ca6657bc824e1dbd4a6026284656a42ce40	136.3.27	wilmer-gaast.net
-20060603195116	fb62f81f947c74e274b05e32d2e88e3a4d7e2613	162	wilmer-gaast.net
-20060603202043	9779c186bd6d396a6fde61cc215f2438d453ee97	93.6.10	jelmer-samba.org
-20060603205240	5973412f44269f6cb796c9d6d38c151290c7367a	93.6.11	jelmer-samba.org
-20060603210858	0025b5148725e524dfdc1da57b18fcd2be2608ee	93.6.12	jelmer-samba.org
-20060607112546	a312b6bcbc6aa836850d94fc2abc70ceffe275cd	136.3.28	wilmer-gaast.net
-20060607133619	85e9644fe03cef7b83f3b3200943364e4eecaf14	136.3.29	wilmer-gaast.net
-20060612065820	5a5c926ec0dffa4b28895929c092089c2a9b9d9f	163	wilmer-gaast.net
-20060612222404	b4e4b958ac5db7f59f8a21c914b02d8d487de2a4	163.1.1	jelmer-samba.org
-20060614203025	c121f8945f7249520342ad86ff00f4986642ca0a	136.3.30	wilmer-gaast.net
-20060615122217	79e826a028f4b4c62c0c16e20af1fb13a9636324	164	wilmer-gaast.net
-20060615123705	3af70b06b2f0fb0fb41a041f6d86e3711b9eea3f	165	wilmer-gaast.net
-20060615124631	5898ef8b07375b71637f585d07fc451e5910c6bf	136.3.31	wilmer-gaast.net
-20060616112651	07e46c92467d0787f1318412186a64cf1c9da562	163.1.2	jelmer-samba.org
-20060616114852	c2fa8279933a103d6576d891e85c1801d90c65ef	163.1.3	jelmer-samba.org
-20060616120751	1fc2958b1e503b782081692c1a503bc7bba19fe1	163.1.4	jelmer-samba.org
-20060617113917	10efa911cbf0d3761d42cc3e57973c8cd1a92821	136.3.32	wilmer-gaast.net
-20060618230728	d28f3b35855c8f8de0be9589334004b30d1ac394	136.3.33	wilmer-gaast.net
-20060619115234	d028a77c97eeccc8d1345af008e2d8920116b637	136.3.34	wilmer-gaast.net
-20060620213653	84e9cea2a80d4e8b05bbadbde923301685d05497	136.3.35	wilmer-gaast.net
-20060620213716	2befb95e3bdaf5306b55342c175abca174e40ffb	136.3.36	wilmer-gaast.net
-20060620214828	ece2cd2eba78088a8cf9d4744aafb3f34dc97c5b	136.3.37	wilmer-gaast.net
-20060620220504	9b46b64b83314a177a7ca3f9f990ff8c78282a5a	136.3.38	wilmer-gaast.net
-20060620221446	2b14eef99faf7e113cc6c17d68bf6402f87ddd66	136.3.39	wilmer-gaast.net
-20060621163433	b72caac09b5944ee9954eb18262fe45228665570	166	wilmer-gaast.net
-20060621165616	59f5c42a86fe73e95aaed0bfe455c7c816f39d2b	167	wilmer-gaast.net
-20060621171449	00ab35016e3646aa936ae0c3d7a8531ec68d6f24	136.3.40	wilmer-gaast.net
-20060623181528	812a41362a9316da1734fdaa8b1aad36bde9cb5c	136.3.41	wilmer-gaast.net
-20060625121542	df1694b9559d4abec748b0506b5f44e684d022a8	136.3.42	wilmer-gaast.net
-20060625121739	125b35db3243f55489a6e88efbdeeefc2c24018d	168	wilmer-gaast.net
-20060625140701	7ed3199067034b4fda4055778e02274f83bcfcb8	136.3.43	wilmer-gaast.net
-20060625151644	4b1a75af236ad968346749d6115c785b74126f85	169	wilmer-gaast.net
-20060625164000	38cc9d4d7a994e4c6b2fc76897591c70e9c14b99	170	wilmer-gaast.net
-20060625170725	6e1fed7057ee26f21b0e59a5aeb292d4f3f0e8ae	136.3.44	wilmer-gaast.net
-20060625172327	88086dbd9002123be39d00c53460f1ec9f2b719a	136.3.45	wilmer-gaast.net
-20060625182513	6ee9d2d65ec05d3871e69c2b03b860c6bdd509e9	136.3.46	wilmer-gaast.net
-20060625192014	9b63df67847e72abb00246fdfc82830137153c3c	171	wilmer-gaast.net
-20060625194314	fd037705664ea03d7a0574c44161ae03b000fb2c	136.3.47	wilmer-gaast.net
-20060625195518	2f1322291d06a3a401f730802d501ea3cae6f4e3	172	wilmer-gaast.net
-20060626120421	c9f0c79af4d1d02e3e37c3d98c0ed33355bce113	136.3.48	wilmer-gaast.net
-20060626150608	471573f916c0cab785707f8313f762ab6e792208	136.3.49	wilmer-gaast.net
-20060626165047	90bbb0efeae19bcc6a1096d8c89fbf3981c83503	136.3.50	wilmer-gaast.net
-20060626215012	3f9440db856c1b1cde54eb919543cfc23ea09983	173	wilmer-gaast.net
-20060628095933	b3c467bc312114eb7cdd45e6bc36a3d87bee6064	136.3.51	wilmer-gaast.net
-20060628144705	171946457cccb7280f0918201093e79bbc9eac72	136.3.52	wilmer-gaast.net
-20060630091718	5c9512ffa716f2bc8bbf9e2c31ee40624a0ff842	136.3.53	wilmer-gaast.net
-20060630231856	0a3c243b6659dc10efb227e507f324c2711d6dcd	136.3.54	wilmer-gaast.net
-20060701155205	5100caa16bb707d89f1873aca99b5f87abc1dd56	136.3.55	wilmer-gaast.net
-20060701191742	fef6116e89bb0651c72454effbb51e04e9e1b8dc	136.3.56	wilmer-gaast.net
-20060702094931	96863f65118767e968469e82ba6b02006e36b81c	136.3.57	wilmer-gaast.net
-20060702232027	911f2eb7060f6af6fe8e4e02144cfb7c4bb4cc8b	136.3.58	wilmer-gaast.net
-20060703211635	bf25fa3627c00f80bad624bb4549c46e4b084279	174	wilmer-gaast.net
-20060703212245	5b52a4895e5a59ff6509f7771f4d8665737688c3	136.3.59	wilmer-gaast.net
-20060705183431	7e3592e96b27ec8375e4b28354bcf9ed4cf5943b	136.3.60	wilmer-gaast.net
-20060707133331	66b9e86eafc3709c491f96c917069db8b6a0c895	174.1.1	jelmer-samba.org
-20060707133401	f4aa393b221dcc709f189ed04945ee67e956872a	174.1.2	jelmer-samba.org
-20060709105445	a93e3c822a697562c2e05906059922ed28f51ca5	175	wilmer-gaast.net
-20060711092844	b0a33a50735d93e1414a2e6c2007884d756429a3	176	wilmer-gaast.net
-20060712080747	639809488bb4ab59a4a4f15ef2d4cd34037a68a4	177	wilmer-gaast.net
-20060714092548	75a4b85ea060c5b63e9742ee9d1591bd618ba5c2	136.3.61	wilmer-gaast.net
-20060714093456	eda02703ff32d461105f0f3ca49264b7c91c5ba3	136.3.62	wilmer-gaast.net
-20060714182459	00a52700d1dbab0736c7ace63c8be2f17b08b8f6	136.3.63	wilmer-gaast.net
-20060714182702	89a180978d3e5d81300b82923e8a208ee085d754	136.3.64	wilmer-gaast.net
-20060715172613	04026d44c29f2b7d64f81bb2d2d061a7d111662f	136.3.65	wilmer-gaast.net
-20060719165238	0aaca60fdc1a6793b438ecc926bd937073d22685	136.3.66	wilmer-gaast.net
-20060727145553	2811940d678bd9340055e08a0462c21e710d5714	178	wilmer-gaast.net
-20060727191833	a36b030d747a39fed8224e6350d56d55b2aec4e2	179	wilmer-gaast.net
-20060804135653	d3a672c0b70eb7b50dcd2908d6cd6928d4f7d874	136.3.67	wilmer-gaast.net
-20060813190838	846cec61f2b94be60c7bae7f4d0de8952e2d45fa	136.3.68	wilmer-gaast.net
-20060813191523	d5ccd83c5235528df2481003502647b86b41fdc4	136.3.69	wilmer-gaast.net
-20060814132505	08cdb93834bcc1b67e4e7f44e8bc90b42c686658	136.3.70	wilmer-gaast.net
-20060814193243	d1f8759e5034dacb489123719ca86c5d802f154d	136.3.71	wilmer-gaast.net
-20060814201754	9829ae028dc1f0d8ab60cb18293234f2d4cc19b8	136.3.72	wilmer-gaast.net
-20060824220652	0383943c38ee308805798974bfccbd3327369c6a	136.3.73	wilmer-gaast.net
-20060825123436	54794b8e01b5cce0675e9cfbd7282d011ebcb99e	180	wilmer-gaast.net
-20060917153035	8320a7a0012f868e5878a5a9f52676a5fece02e1	136.3.74	wilmer-gaast.net
-20060917155616	51fdc4512cc77fd6b59b582f27887657d7c3721e	136.3.75	wilmer-gaast.net
-20060920093402	9544acba1fecb11b0b6a0e6260e302890d4c2337	136.3.76	wilmer-gaast.net
-20060920093653	8f243add8b64f9936c49bbeafac77aa9961cf264	136.6.1	wilmer-gaast.net
-20060920101856	f06894d8f55b50b632c1d81ad878f8581273ba66	136.6.2	wilmer-gaast.net
-20060920194227	21167d2d14c333d67445546bb69dd52dd295287d	136.6.3	wilmer-gaast.net
-20060920200919	70f6aab8f4b6a1bbd9991e800dde91a02cc363f0	136.6.4	wilmer-gaast.net
-20060920202647	0b4a0db448d033b8f35f32060bf261374fd81bd8	136.6.5	wilmer-gaast.net
-20060921073239	dd788bb0b18684be993cc7edf1f0da6f8e36449d	136.6.6	wilmer-gaast.net
-20060921093703	4a0614e65b45364d4d1f631aeaae047a92c752c5	136.6.7	wilmer-gaast.net
-20060921184434	5bcf70a662244dc77af09d2fffbe913ec6f19393	136.6.8	wilmer-gaast.net
-20060921194817	deff0406d501264e1d91203ea8f91411a150e35f	136.6.9	wilmer-gaast.net
-20060922120435	59974884ba72d6e8fa008d07ee93bd228d30a99c	136.6.10	wilmer-gaast.net
-20060922165658	8d7429102adf8dce6844f2f3da2723d1f87c6442	136.6.11	wilmer-gaast.net
-20060922183931	fe7a55434385fd858453dffdbb425a21f41e3859	136.6.12	wilmer-gaast.net
-20060922224940	d8e04849607d4a5ca590752dce37954f12179580	136.6.13	wilmer-gaast.net
-20060923161824	5e202b09f2cd9faff5f316ae6804facb5342eace	136.6.14	wilmer-gaast.net
-20060924102541	172a73f1a4b37fa20d1d50496a3faccb8fe6c769	136.6.15	wilmer-gaast.net
-20060924115745	42127dcd26be4f6746004237eac5333ffbb94f8e	136.6.16	wilmer-gaast.net
-20060924172208	e101506a3e660d3165a89aab0898293b367e2b5b	136.6.17	wilmer-gaast.net
-20060924180807	cfbb3a6e5e11a8d2d162d80958d6ce997104e9d3	136.6.18	wilmer-gaast.net
-20060924192506	8e5e2e9a0ef549c94afc8041dc7d99358f51c9bd	136.6.19	wilmer-gaast.net
-20060924195644	4ecdc69c19546fd7256e138d7be3cf49d299b36b	136.6.20	wilmer-gaast.net
-20060924200009	a214954bda17730b6251e5c5c26f6d4d23eb1ed9	136.6.21	wilmer-gaast.net
-20060925074239	eab2ac45071373751a3041c85b0ab69460109032	136.6.22	wilmer-gaast.net
-20060925101014	ebe7b36af555d644357efbc0e63393927162bf06	136.6.23	wilmer-gaast.net
-20060925170450	281859e83eb7c953b64a970d3156279b323617d0	136.6.24	wilmer-gaast.net
-20060926131211	8e6c7326667f7302bf622555d7c4164d24b89be0	136.6.25	wilmer-gaast.net
-20060926133054	022df46b52683f49a1a48e15a440d1f6b81adfdd	136.6.26	wilmer-gaast.net
-20061001093141	6baca2a2910d0b6663b54ef302820d9ffbbf5eee	136.6.27	wilmer-gaast.net
-20061001094055	0e2d97f6b3bcffd5637eafa9a687e42a7c134f57	136.6.28	wilmer-gaast.net
-20061001161546	88591fd3b95ab21ca016204b49fb80d6d6cdd541	136.6.29	wilmer-gaast.net
-20061002131913	c1ed6527576ac4c6ee1241f662e7db8e59327fd8	136.6.30	wilmer-gaast.net
-20061002134633	d9282b4b3a7a1264bf7952e5de4dbd10b6aa5e4e	136.6.31	wilmer-gaast.net
-20061002164232	501b4e06244dbd333ee207ceade37592482e0fe7	136.6.32	wilmer-gaast.net
-20061002174657	995913b4be70be6e07b8aa7661ac639e5fc0d6e7	136.6.33	wilmer-gaast.net
-20061002183221	6266fcab664c9a907b1d32a1c94ef7fd3cfb9fba	136.6.34	wilmer-gaast.net
-20061004181441	cc2cb2da3f1c0e2ad65708f4110e74e945ea9b66	136.6.35	wilmer-gaast.net
-20061005225554	101d84fe3018ba138a9cb5f0f030997e8ff7bdbe	136.6.36	wilmer-gaast.net
-20061007130102	090f1cbe72373b31e753af4a1442ddd53b02791b	136.6.37	wilmer-gaast.net
-20061007174628	36e9f62a6e6fdb1217b3b819320ac5a94025c448	136.6.38	wilmer-gaast.net
-20061008161116	038d17f834219505cbbdae469b2b150117467dd0	136.6.39	wilmer-gaast.net
-20061008184111	861c199fb60fecf5dab96e0ed9d4b0cf0c57822f	136.6.40	wilmer-gaast.net
-20061009181905	6a1128d1333cf79f1ef9fb1f55b1b8fec67caf2a	136.6.41	wilmer-gaast.net
-20061010120542	a21a8ac4fbd5a234bc8d31d9d487c74a81383c8a	136.6.42	wilmer-gaast.net
-20061010121020	8eb10c9dbdf1497a5e154e3d32734f79f42e8213	136.6.43	wilmer-gaast.net
-20061011084545	58b5f622cd003433dc78b4f510c667baf537424a	136.6.44	wilmer-gaast.net
-20061011182956	b56b220e4280a75577f79b9dbcaf6eb2d7336873	136.6.45	wilmer-gaast.net
-20061012174858	259edd40f5e332791a44f7547346bf799f1f7327	136.6.46	wilmer-gaast.net
-20061012210151	a4effbf8f749459340cb353a29053e6f69850f63	136.6.47	wilmer-gaast.net
-20061012221400	7e83adca0e875710627588bf28ddb60fb61bd43b	136.6.48	wilmer-gaast.net
-20061013214454	695e39232324711816f1db8e25fdba59a0c6456f	181	wilmer-gaast.net
-20061015092606	3ef6410bab141e5c6ea465730a37289991c38f9f	136.3.77	wilmer-gaast.net
-20061015093113	e97827bee83d3a0663aa284e72a4f6c84b4b4dfe	136.3.78	wilmer-gaast.net
-20061015093402	93b7bd4373c7c020f37fb96b547b5eda99daf547	136.6.49	wilmer-gaast.net
-20061015094112	69cb62335f8bbe46b2879aabc5fdbe288891b02b	182	wilmer-gaast.net
-20061015140227	2529faf12adfc414048dc41e949065855d0da945	183	wilmer-gaast.net
-20061015150113	ee5c3558c7db32750e1029a1b4983672dc519bca	136.6.50	wilmer-gaast.net
-20061015153246	3dc9d46cc20d287c266fed97f92d298ed721f7b3	184	wilmer-gaast.net
-20061015153457	e617b35a6771362164aff194cb6e0b757552c0bd	136.6.51	wilmer-gaast.net
-20061015184447	a3265629451475df75a3cd1fbe1805bbb71b2365	185	wilmer-gaast.net
-20061015195342	5eec897b4d962e643e26574d1bffc22ffcaddac6	186	wilmer-gaast.net
-20061015202401	788a1afa9628aeaf9d69fc53f49131a4330253cf	136.6.52	wilmer-gaast.net
-20061015204622	d74c644550bdb7c0d7bb41ce6c01ef2a470465d6	136.6.53	wilmer-gaast.net
-20061015214228	aaaed5ea8950bbecee2f4b2d5ead108308c7a45f	136.6.54	wilmer-gaast.net
-20061015222559	a5ac9f94c2dad85cb0f504172635982368db4d65	187	wilmer-gaast.net
-20061016082103	e7276082fede405633f63d38fe18e010e897a972	136.6.55	wilmer-gaast.net
-20061018174708	1991be623c6c53d2f61f0b15405d41d144c21c8f	136.6.56	wilmer-gaast.net
-20061019075135	f920d9eb003541245e0fc32a381447cbba8fbea5	136.6.57	wilmer-gaast.net
-20061020191214	dfa41a405f0c80549f6dd5c0c111e3b62ce83b07	136.6.58	wilmer-gaast.net
-20061020195809	3b3cd693845539938baf5e26c80234f03ebf870c	136.6.59	wilmer-gaast.net
-20061021194957	2a29eac1dd6c9d5d2ed5083efc1c185cfd750fd7	188	wilmer-gaast.net
-20061021204844	35f6677c07770f0323872e4edddefb7b752e50bd	136.6.60	wilmer-gaast.net
-20061021205621	91bd910ec5ecf95953518246e3fd0adf1f43c1f7	189	wilmer-gaast.net
-20061021211230	4b0d8055fe3fc08cdaa7696f1c4af1e0e471a67e	136.6.61	wilmer-gaast.net
-20061021214006	66f783fa6365fefe7ba449e6409b4dc1359b155b	136.6.62	wilmer-gaast.net
-20061022092455	208715962fcd1b806d42ef7edb47503eb296895b	190	wilmer-gaast.net
-20061022124340	670204fe6b883397208758d1e8e0c3ada699379c	191	wilmer-gaast.net
-20061022135355	28eda862ffd1f59ac3cd214295c366ab939afd46	192	wilmer-gaast.net
-20061022135832	6237ded20b3f3058f1ada9b6afeaa07fcba535eb	193	wilmer-gaast.net
-20061022170015	e8a621101ade8c19383038cd400ab22bfd0de326	136.6.63	wilmer-gaast.net
-20061023200119	f0071b791cc1be18a3236bdc6e363c837210e5cd	136.6.64	wilmer-gaast.net
-20061024104028	bd28e6a2eec0333a866ef2e380d32b1e6ad0c80b	193.1.1	wilmer-gaast.net
-20061024190452	62d0c141f1118d245fe192151e57b2beb739aa5c	136.6.65	wilmer-gaast.net
-20061028205440	abbd8ede1eb5eeb9b82e09357e0b38949bc95b8d	136.6.66	wilmer-gaast.net
-20061031082541	47d3ac46306965e9db66096eef8c60c8e7985950	136.6.67	wilmer-gaast.net
-20061031083536	bbb6ffb4d2f4e02d3f856f731a83cb6b911bf659	194	wilmer-gaast.net
-20061112230608	0d3f30f5449cf1730c006314f3dd60843e911ad1	136.6.68	wilmer-gaast.net
-20061113103823	16b5f86a2cf909628d5b90aa5d36aaac8f869a78	136.6.69	wilmer-gaast.net
-20061123093418	9bcbe48c6e8a3ab80e498b78b486c0e7cb3466fd	136.6.70	wilmer-gaast.net
-20061123184609	25984f203b18ce36aabae431797266e44e9a3ba0	136.6.71	wilmer-gaast.net
-20061124193959	dfc8a468020935a444397129eb5b2579e1001cf2	195	wilmer-gaast.net
-20061125092029	55078f59c8e9f52f1d10bed55511e5e58ec3e53b	196	wilmer-gaast.net
-20061203175717	cb6a6f437f4fb1be3f35b61f2c26e78ecacaedde	136.6.72	wilmer-gaast.net
-20061203175731	c7d0f415e0bed09decce7a0503dce2274c9d94d9	136.6.73	wilmer-gaast.net
-20061205204017	d8d63a27dcfd38245927af95521b6f8918b23795	174.1.3	jelmer-samba.org
-20061205225702	7740c4caae24fe8186947d0f1f6a114098daa171	174.1.4	jelmer-samba.org
-20061206121509	2c7df621d2337437a46b0ccee6a7801bc04db8f4	174.1.5	jelmer-samba.org
-20061206132622	6a909671115ba5fac94c212c249264bf25ec8840	174.1.6	jelmer-samba.org
-20061206133415	3b4cc8f0af7a4d9e1fae69e12a438b15f9206003	174.1.7	jelmer-samba.org
-20061206135725	7bcdde3d31f0e5b53601b90aa33ebd820c770172	174.1.8	jelmer-samba.org
-20061206141656	a51be6448b7a6aa100f27873ce40b835068a66a7	174.1.9	jelmer-samba.org
-20061222171708	a3a37787188b68d17549e4db1ac1561cf7541979	197	wilmer-gaast.net
-20061224014720	9225e08f377026e653e31468d53df9d328dd7951	197.1.1	jelmer-samba.org
-20061224193513	c227706bc921c3bb426eb315c0d097df30aa9d16	197.1.2	jelmer-samba.org
-20061224214718	7bee5af91e56f1e58232b895fd40c367aec67e8a	197.1.3	jelmer-samba.org
-20070121211711	ed5df815732d311b228993627f5d8b8c8e3eb4e0	197.1.4	jelmer-samba.org
-20070121213850	8c073a6e77edd56bc3d58d6b4af2a40f005aacad	197.1.5	jelmer-samba.org
-20070121220856	7738014d6a9ad81806ddb4e06092791dbd10b616	197.1.6	jelmer-samba.org
-20070122094255	348c11b16c156979ef2c7141ca7450af693b3713	197.1.7	jelmer-samba.org
-20070218170529	5c5a586d73330f3f3f2c1b0d6d9f1caddfb79850	198	wilmer-gaast.net
-20070218171014	7e0af5345f398a439459ac1752b8a946fb53a87e	199	wilmer-gaast.net
-20070218171901	f959495182db79b0c3b51c6bc4e9fcacdc591c40	200	wilmer-gaast.net
-20070218173419	46af1074101f0354490ea3940f16d6f84467de0e	201	wilmer-gaast.net
-20070218173754	8de63c3fdfb532e85f7d93778e63032f7c84b631	202	wilmer-gaast.net
-20070218174804	0fd8559c4a6ad84ad0852e07c4ba2e50b2fdf0ad	203	wilmer-gaast.net
-20070218224539	0f4c1bb59f2c939cabddd02e226954a3cfa02a7b	204	wilmer-gaast.net
-20070218224621	34337a71b8d8ad7d50fdf1152efbe5c3dde4bb90	205	wilmer-gaast.net
-20070218224650	dcf0f3e77ffd38ce3cc5d3c1eedfc655ff75a8f9	206	wilmer-gaast.net
-20070218225248	21782b3d081a3ac0d6c7c0e57736e4342b73779a	207	wilmer-gaast.net
-20070218234717	d4589cb29dc06e68e9872316cfcd460c78e6aba3	208	wilmer-gaast.net
-20070218234802	6cfcfdf92b50719e8c08cc933879dae00a484215	209	wilmer-gaast.net
-20070218235712	b6423a0964c121edfb7a4318b1d909916d099d4d	210	wilmer-gaast.net
-20070226044635	723e611603236aab794ffbbc312705b8309b3f81	211	wilmer-gaast.net
-20070328055311	fa29d09342c79b886efacee4cfc3078be5f5a722	212	wilmer-gaast.net
-20070331054045	0da65d5fb37691ed4d31f7ab4058732f1440db6b	213	wilmer-gaast.net
-20070406052031	aef4828a1dc51de10a43bb7dd5d023de9971295b	214	wilmer-gaast.net
-20070406053423	552e641194147078c7858059df9916d5d548b7a1	215	wilmer-gaast.net
-20070415215952	cd4723c257f9f7bd8d4a46c6562f93c2aefc3dbb	216	wilmer-gaast.net
-20070415223935	c2fb38096ea4e75a680e57e103d4a4986aa84c75	217	wilmer-gaast.net
-20070416010308	84b045d409f1e8da6d8bf379c6fef7236dcd9bcd	218	wilmer-gaast.net
-20070416040113	6bbb939e953bbe1ca9ed3101a1569abe4521f543	219	wilmer-gaast.net
-20070416043152	cfc8d58f4c15048e943800272017f69804bb61b7	220	wilmer-gaast.net
-20070416044408	b3cae44de28b62a282f2ba32260048174abc83e7	221	wilmer-gaast.net
-20070417035730	717e3bf045e5ebfb9b71e9260c8e573daefa7900	222	wilmer-gaast.net
-20070417044917	9624fdf0d6f170d8caa7948fb1b3a138b05e1d8c	223	wilmer-gaast.net
-20070418024949	33dc2618520409c0d52efff335fe299c26f6dd42	224	wilmer-gaast.net
-20070419060343	f0cb961652cbd639e89dcd88a86f20a2414146c4	225	wilmer-gaast.net
-20070420044930	d323394cf97afa79bc2a75f5f2dd0f88bf1b8fa4	226	wilmer-gaast.net
-20070420051906	61ae52c5fbe8fbaf75adb148cd05c357590e8807	227	wilmer-gaast.net
-20070421032339	b0eaa5b5a0ab866791992f6f1d7f011d012d103d	228	wilmer-gaast.net
-20070421041321	f6c963b97d3f686ca5410f1896cf8e266b455c22	229	wilmer-gaast.net
-20070421042244	d11dd2f07d869282acfcdf5951f8e82d65afa234	230	wilmer-gaast.net
-20070421050205	17fa798247bf1a9b18de2c4848039f940d819f31	231	wilmer-gaast.net
-20070421051840	df1fb67d1dbf52d138f63e0d917dda2412d4fc0b	232	wilmer-gaast.net
-20070422021536	c737ba70c7b3510ffb6bed8f9373e63b1a150c1b	233	wilmer-gaast.net
-20070422204427	e35d1a121d5fb2da3698fbe4a365fe38d0097665	233.1.1	wilmer-gaast.net
-20070422233937	43671b964b636520a54e343542c5958b30e9f589	233.1.2	wilmer-gaast.net
-20070423025844	0e7ab64dfb66192a875c37322ca6f8a364ec5bc8	233.1.3	wilmer-gaast.net
-20070423051847	2d317bbe8def887fb796b2daaa958c59d8f4c070	233.1.4	wilmer-gaast.net
-20070426044134	6286f80d6dc1dc4cb8106b4e209a8578d7cebe56	233.1.5	wilmer-gaast.net
-20070428052056	05bcd20ba58357673225984d8f308baa6d34b726	234	wilmer-gaast.net
-20070604113237	bb95d43e263530805224005ca246addb6bb199fa	233.1.6	wilmer-gaast.net
-20070604114533	54f2f55f983f4b6bb8a58772bbd1137580e3307f	233.1.7	wilmer-hypnofrog.dub.corp.google.com
-20070604132205	d06eabf19ec3f849d8bab22c13d43e4eba9a48ee	233.1.8	wilmer-gaast.net
-20070604133651	e0e2a71ed3e7937ed5da85af95be016a8441547b	233.1.9	wilmer-gaast.net
-20070604155609	c570c86bd6c08c77d851ac81a9603dcd13c7804a	233.1.10	wilmer-gaast.net
-20070613233059	7e9dc74b15901b182be2a1d20bafdba696e4f5f2	233.1.11	wilmer-gaast.net
-20070613233118	43d8cc5909aa45aee0b3368e70275469b0f8de22	233.1.12	wilmer-gaast.net
-20070613233139	998b1035a6c8349b3661861eeb5d9d1f4082ba0a	233.1.13	wilmer-gaast.net
-20070613233256	bdda9e9ae418e19bd8fa57a019267f567d537018	233.1.14	wilmer-gaast.net
-20070619232845	a3d5766eba9b1225d41cede5d817a48c3a3e0b7d	233.1.15	wilmer-gaast.net
-20070630215244	f7b44f2c094f99f03182485a30d95a029a84f809	235	wilmer-gaast.net
-20070701122023	7bf4326f7a9b2a2aec8b292ecbc876d4349d2624	233.1.16	wilmer-gaast.net
-20070701130847	40ef702d3e500eb38d7410114ace54e8a70b151e	233.1.17	wilmer-gaast.net
-20070701145245	ae3c4fae12b09b30bb49477696b10ae5beb8ef4b	236	wilmer-gaast.net
-20070701152242	2758cfed0abcb529975af34fdb4d2603febbf1a3	233.1.18	wilmer-gaast.net
-20070701162921	b9f8b870f7b884747b747be91ce0ac797a7c6e82	233.1.19	wilmer-gaast.net
-20070701195823	9c9b37cbfa27a038bc57624cb9001f8db019290c	233.1.20	wilmer-gaast.net
-20070702093048	c3774175d29802202afb226a2661d0c3c52fb7b1	233.1.21	wilmer-gaast.net
-20070702210743	1962ac169c14c7b24e276caac0976b8983496fd5	233.1.22	wilmer-gaast.net
-20070702212404	5d7dc007a418be0c897000e888e747047729c756	233.1.23	wilmer-gaast.net
-20070702221203	9da0bbfd42609f0f3864b5a16a3c1c378b7217c9	233.1.24	wilmer-gaast.net
-20070705233648	19a8088455308088139d0b2f6a8d0d4fbf982b29	237	wilmer-gaast.net
-20070705233947	e90044208fc88636f843cdd188faa12e5db8c1c0	233.1.25	wilmer-gaast.net
-20070706081113	c4f2c6a64ee0f2de10c172e337528d6a6a9a3e62	238	wilmer-gaast.net
-20070707171928	3b6eadc990664f9929bceb4f7d14498cf672f0d1	235.1.1	jelmer-samba.org
-20070707172112	b9e4ab55dff62611e825de013b32fadab782362f	235.1.2	jelmer-samba.org
-20070711142200	c9c7ca771d3b06ab448a72bdcddfdacd5be815c0	233.1.26	wilmer-gaast.net
-20070714172018	cbaac62b34403c8bb2bd207e7d91da3d7884f4f7	239	wilmer-gaast.net
-20070714172452	8c4afbfa61848893bf2749943640fe842ad03bc3	233.1.27	wilmer-gaast.net
-20070714234530	1ffb46f027f501da45951428e097f69ced1c80e0	233.1.28	wilmer-gaast.net
-20070714234738	1da00b12d498951219141000947927120b2fdbcc	240	wilmer-gaast.net
-20070714235418	a8a0b4cc1dafff3d822984a4329488f9aa032694	241	wilmer-gaast.net
-20070715154734	85023c65b697d2dab932acbda31258ae5270dbe6	233.1.29	wilmer-gaast.net
-20070730191206	1baaef858136cd3a4799b3ccf1d9961534e0017c	233.1.30	wilmer-gaast.net
-20070808092057	82135c7178b6379f35741991f6c06bb308143194	233.1.31	wilmer-gaast.net
-20070819133745	f06e3ac0ed0aa7a1b616993512fbd0ed12d01e51	0.1.1	vmiklos-frugalware.org
-20070819135732	1323e363b90f525313178a3451d308817bc3f292	0.1.2	vmiklos-frugalware.org
-20070819154314	9fd42411191bb42e51c14c24d454c1855f0b4885	0.1.3	vmiklos-frugalware.org
-20070819155022	be975f86a1b9ab4e41dacad0f6ef5f87edb988ed	0.1.4	vmiklos-frugalware.org
-20070819155255	b6d26acc1e93fec056431788c931e0a452797cf2	0.1.5	vmiklos-frugalware.org
-20070819155424	ddd35025c4b309c9748a4e2a8b13e44a2cecfb46	0.1.6	vmiklos-frugalware.org
-20070819173534	adce2deb210d46b6c7a0deeaf81ff16765d97f64	0.1.7	vmiklos-frugalware.org
-20070819173937	592f824863a8f3e46d776c104c3f55167d6744fb	0.1.8	vmiklos-frugalware.org
-20070820014906	93ece6639942c625cedd5a4f6df63a59730c5187	0.1.9	vmiklos-frugalware.org
-20070820015057	0bb1b7ffcbcb6cc82d7dc4fe1c7661e59218a312	0.1.10	vmiklos-frugalware.org
-20070820040551	77c1abe6e810e3c9ba19181bb4866bcbb7fea6b7	0.1.11	vmiklos-frugalware.org
-20070820040716	8dd21bbdb92a1d95f54a015c363938630b1766df	0.1.12	vmiklos-frugalware.org
-20070820135322	afe221f55da1bf9d48fcdc6d6c1c24016aa8f8fe	0.1.13	vmiklos-frugalware.org
-20070820135426	440665b8879c003948b650218d13964688e419db	0.1.14	vmiklos-frugalware.org
-20070820135713	712a284ed72edec751d2789c42771974fe61beb6	0.1.15	vmiklos-frugalware.org
-20070820140118	4ddda13727a4dd7e8f1398e74193541e05f665a8	0.1.16	vmiklos-frugalware.org
-20070820141232	38fa9883de34bc05fdab2a2f39fff9c7fd23a0ea	0.1.17	vmiklos-frugalware.org
-20070820141304	cb35add0516ccb39707baf490e1094788790ad74	0.1.18	vmiklos-frugalware.org
-20070820142605	94bd28f3f2d24028e4287ba4af5691f902b651e0	0.1.19	vmiklos-frugalware.org
-20070820145050	a316c4e602726f4cf85bee0508a365ce3e28fa7b	0.1.20	vmiklos-frugalware.org
-20070820145305	ba20c3903ee5c47e5fb83fc17a13e5f5ed319372	0.1.21	vmiklos-frugalware.org
-20070820163548	ed2e37f5b8f597fdd8e4e0a9cafb943d23e5469c	0.1.22	vmiklos-frugalware.org
-20070820164505	0c60f96f5a49d9c8b22359729371d08d1c8d8bd4	0.1.23	vmiklos-frugalware.org
-20070820164935	d1c9e3542d4e1b90b89d4850a4c7692d1b09ec9c	0.1.24	vmiklos-frugalware.org
-20070820165531	9e03e556f0c34cc9a2876605a228c7ae539cb8de	0.1.25	vmiklos-frugalware.org
-20070820171223	7338d592a1b40de9ef3f0a99a09987b4c617f02b	0.1.26	vmiklos-frugalware.org
-20070820171324	39a0d6429bd2a5aac36b6393278acd08b7b95893	0.1.27	vmiklos-frugalware.org
-20070820172532	a3d6427d343e5f3de206272a664a126fea1af6bb	0.1.28	vmiklos-frugalware.org
-20070820172600	218ffbd18175cad56805dd5134ac6f41f8dc6cb4	0.1.29	vmiklos-frugalware.org
-20070820172819	751b14941fa2a610031c14711ba5b3cd8111b93f	0.1.30	vmiklos-frugalware.org
-20070820173756	72f697b0e0ea9671f9182a8364d2bf1e97b002b8	0.1.31	vmiklos-frugalware.org
-20070820185025	62bb4e49d4066e7505ee784b404c676906a1ca09	0.1.32	vmiklos-frugalware.org
-20070820185646	f932b65244885b5eee8917192913b3e6172e3a11	0.1.33	vmiklos-frugalware.org
-20070820194303	565982f80f224045c72332d853f24fcb8534f818	0.1.34	vmiklos-frugalware.org
-20070820194311	d5a514a36fc1ad7e54514b84890c2cb7ad36025a	0.1.35	vmiklos-frugalware.org
-20070820194505	314b375c85d1b28382f207850b349fbd41c0d1fa	0.1.36	vmiklos-frugalware.org
-20070820194713	e23a46e4c994b1c01e75582b32826afebdc4c5fc	0.1.37	vmiklos-frugalware.org
-20070820194900	a60c3c2bde4e3e4327f4a4815fee43c714da6298	0.1.38	vmiklos-frugalware.org
-20070820205845	6627d926b16e5c4a92099d9ea896bd00b5b9a680	0.1.39	vmiklos-frugalware.org
-20070820205958	7e4f0ca1fedc2762aa1d2231e92b878f53fbecd3	0.1.40	vmiklos-frugalware.org
-20070820211452	cbec0d6b8cb68fdadaf938922d0062637c6c2e21	0.1.41	vmiklos-frugalware.org
-20070820212752	f78f9490712b11b9395d6cf3817a490aeaa85ef3	0.1.42	vmiklos-frugalware.org
-20070820220016	23411c64ecb0608a2629083504c8e59458c7e0fa	0.1.43	vmiklos-frugalware.org
-20070820223717	98bca363574144c9e488812c61249613fcb5e989	0.1.44	vmiklos-frugalware.org
-20070820223806	348a3a20a06744d919f7b0bf0d392d30511d50b1	0.1.45	vmiklos-frugalware.org
-20070820225159	09be265fcadda0c308952cc46d5dc6c1352292a8	0.1.46	vmiklos-frugalware.org
-20070820225656	1fb89e33a8b514895c22b6476e50616b2c2d907d	0.1.47	vmiklos-frugalware.org
-20070821141025	d3cbd1725540cb6665df678b28dafa8fe4c1c7d6	0.1.48	vmiklos-frugalware.org
-20070821141733	89332c580392bc6bac4b68d3e409b03c93a0000d	0.1.49	vmiklos-frugalware.org
-20070821163907	dd8163ebbde5b38fb59c6b73b304a24465b5d2b6	0.1.50	vmiklos-frugalware.org
-20070821170211	7daec06ebeafa481e57ef99f558cdf9f05f0d8c4	0.1.51	vmiklos-frugalware.org
-20070821173943	b8b0bfd35dcd26c33c86a4a3a54897084f253582	0.1.52	vmiklos-frugalware.org
-20070821174438	56e4ac49b580773b7b7e67199f11a1b30b102ead	0.1.53	vmiklos-frugalware.org
-20070821174517	56b5647801b9993f8ad622bce082050b9ba90e4f	0.1.54	vmiklos-frugalware.org
-20070821174640	877b6f4e6a10e17af08f5bd8ebc93a25d4d9201c	0.1.55	vmiklos-frugalware.org
-20070821175106	75742cc12cf9c59e6b62cf499b62007a00a27603	0.1.56	vmiklos-frugalware.org
-20070821181237	9a4bb33ae748c2221b6eedda24d7defbcdfdef39	0.1.57	vmiklos-frugalware.org
-20070821181808	cd3022cce128e4ff995bd7d76b3013d1909b3332	0.1.58	vmiklos-frugalware.org
-20070821182059	dfec37e7eac14102c6d127b77484cf0eb717eea0	0.1.59	vmiklos-frugalware.org
-20070821184627	bff265c81bf79b535841ac5774472cb82cd762f0	0.1.60	vmiklos-frugalware.org
-20070821184637	68312b81a0adc34e5d868b447393af0ea1fb3ffe	0.1.61	vmiklos-frugalware.org
-20070821185009	bbf1050bd9dc9b92372750aba8dd28d464d247f9	0.1.62	vmiklos-frugalware.org
-20070821185155	8e3058bc70420a21393769de5e98b552bbf16acc	0.1.63	vmiklos-frugalware.org
-20070821185532	2fcad6ee4986bc01d2c526ed84912519d111fa0e	0.1.64	vmiklos-frugalware.org
-20070821190149	05c1bedaec6ee8ae71b311892e8287647095f3a0	0.1.65	vmiklos-frugalware.org
-20070821190255	7e600c9fe75a14aba48cc23fcc4092e5ecece57e	0.1.66	vmiklos-frugalware.org
-20070821190413	04064b6b739839ae389948e275652d8e0659e0a6	0.1.67	vmiklos-frugalware.org
-20070821190947	127966f9709beddff018657dcc5dd78b2cbf9cee	0.1.68	vmiklos-frugalware.org
-20070821191200	1c3f19fea2f75bc8d5f644b75fb3b0118a840e27	0.1.69	vmiklos-frugalware.org
-20070821191708	11eed8f006d66b9904c932d541f7c18bc4ea4288	0.1.70	vmiklos-frugalware.org
-20070821192016	0ac1b3e0a5ad6c2d3ecca685f80a30ba1ebc95e3	0.1.71	vmiklos-frugalware.org
-20070821213743	395317200aad6fd04e3355477b8d5fcb1caa45f8	0.1.72	vmiklos-frugalware.org
-20070821214233	64f8b8eaa606f207a03773082d8c9c6f2d8360dd	0.1.73	vmiklos-frugalware.org
-20070821220739	924d0f2ec83887db9ff5c4cf5ded78c29135a9b5	0.1.74	vmiklos-frugalware.org
-20070821223314	8237df502bc3559c4828708e933bb5e5b9632aa9	0.1.75	vmiklos-frugalware.org
-20070821225253	43e2e9d61306e93c8435de5bba6a631a942645cd	0.1.76	vmiklos-frugalware.org
-20070821232419	025da7a0aa83fea9032950983610f86b1b3d0e9b	0.1.77	vmiklos-frugalware.org
-20070821234006	c24ae8ea799dabb93fccffd6ee8fbd9a8da95085	0.2.1	vmiklos-frugalware.org
-20070822002252	23b84e11715e7c743e56d4a10efe603131761bf6	0.1.78	vmiklos-frugalware.org
-20070822002303	f35022f28fc2c6dd3c8d1858a6e6b7a336e27f5a	0.1.79	vmiklos-frugalware.org
-20070822002525	57087df72ba6c12ba8e7b9d27a99118d78e3f784	0.1.80	vmiklos-frugalware.org
-20070822161228	ecfbc5d8c2266b0e63a3ff5bfdec93a22984f950	0.1.81	vmiklos-frugalware.org
-20070822162113	99426f0428015d8b8bb851b47646baa939c047bf	0.1.82	vmiklos-frugalware.org
-20070822173509	bbba374a5748e2cccca67295aa281a2918a76c2a	0.1.83	vmiklos-frugalware.org
-20070822173917	53b71d3af1bdc9324090cf435160d07188ba861c	0.1.84	vmiklos-frugalware.org
-20070822174454	b8351a2ea35bf7a2479c1cbc0eb5ce41be29809e	0.1.85	vmiklos-frugalware.org
-20070822174648	b7f7100a8ea10d1d213f916b2bfda5e3aa698c30	0.1.86	vmiklos-frugalware.org
-20070829142359	292be68e450ce919c2b13c8c5d25b1c38e1ae52b	0.1.87	vmiklos-frugalware.org
-20070829143012	846ceffbdff25eaa601478f12d72bd1163e55592	0.1.88	vmiklos-frugalware.org
-20070830221751	2abfbc5e9ef942937f9ef662262d00fa5e47301f	242	wilmer-gaast.net
-20070830222243	f2b171d39a6e93e7d05e421f81cd120276906e3e	243	wilmer-gaast.net
-20070830222307	8ad90fbd65d544ac207d52780499df76db9adc6f	244	wilmer-gaast.net
-20070830223027	60c1a4e76a343c0dbc19690b486190681265db4c	245	wilmer-gaast.net
-20070830224833	9c90281593898661e5c35c3c02070102be34cc82	0.1.89	vmiklos-frugalware.org
-20070830225617	478c0518323b48c24ddd2474acc8a870d00c2acb	0.1.90	vmiklos-frugalware.org
-20070901215722	8d32b9e8328605bb52c87b53f1f27028e68336e5	246	wilmer-gaast.net
-20070901222417	7de5a608d82e15ca4e37b9ccce728d3fc4e32250	0.3.1	vmiklos-frugalware.org
-20070903125726	4eaff412eb4656df5a17a35e770b195f24f046f0	0.1.91	vmiklos-frugalware.org
-20070903125750	7879736fb435aa529e4df5b472be0187b5b8b9f1	0.1.92	vmiklos-frugalware.org
-20070903210934	02a531b69a273c4ead692d18b24b3d7c953996e6	0.1.93	vmiklos-frugalware.org
-20070903222255	761367025bcb4a711f07715a91076769a3297531	0.1.94	vmiklos-frugalware.org
-20070903222557	dab0f8ad3de7ebd043a6a6316591988017952eba	0.1.95	vmiklos-frugalware.org
-20070903223015	5d1b077428f1586d9de15cb37e26b65b7ff6b2c0	0.1.96	vmiklos-frugalware.org
-20070904165928	c15f71ad27fb572abc851d6059bf78daa800202d	0.1.97	vmiklos-frugalware.org
-20070904170556	40d2dc483898d5dc3c57a90ea6d00e07648eea41	0.1.98	vmiklos-frugalware.org
-20070904184847	e5c01758b410ff159b9c9c9c563bacb78d9d855e	0.1.99	vmiklos-frugalware.org
-20070904190731	67496f714dace43eb7bd2e2cf123f72b64004454	0.1.100	vmiklos-frugalware.org
-20070904192942	52d779e215047fab1c438356ade5809189fd1cc1	0.1.101	vmiklos-frugalware.org
-20070904193052	f080961134608b9d5ca039f9b8f7d2b8de3499ab	0.1.102	vmiklos-frugalware.org
-20070904193426	19b805c71dd7e942ac8ee05a683ab1ae5dbbee2b	0.1.103	vmiklos-frugalware.org
-20070904195121	22d97b4de81e8f0785ff8c7675bd29de16bf6d01	0.1.104	vmiklos-frugalware.org
-20070904202051	d86dfb15f27086bfb0052c89ca5b0e17388ff66b	0.1.105	vmiklos-frugalware.org
-20070905143430	54c269df03f3fb29e5b5f8c7696090ffa4e5a3a9	0.1.106	vmiklos-frugalware.org
-20070905144009	80d90047d5c60b48dc3bfc7e643b923a9e63d28c	0.1.107	vmiklos-frugalware.org
-20070905180115	bfe5a8aa13738e500e7e3ef337455708d11edf9e	0.1.108	vmiklos-frugalware.org
-20070905180404	5268bd75760eaf0a7120c878312ed6d26189dcf9	0.1.109	vmiklos-frugalware.org
-20070905181128	8b3beefbb63a3e710aaabcd170302d5bdb27c9d9	0.1.110	vmiklos-frugalware.org
-20070905191823	65e4020ac3cfc890d8fe4f1194593b57f518692b	0.1.111	vmiklos-frugalware.org
-20070907162903	dffa24fe3c6f55ae900cf4950e05622860237ab0	0.1.112	vmiklos-frugalware.org
-20070911221104	af8675fa2a9dacb075e1fee0e492a2ea632a68e1	0.1.113	vmiklos-frugalware.org
-20070915222641	8de38e9b1ec123ad0c73e0db1918eb06b1af3388	0.1.114	vmiklos-frugalware.org
-20070921141648	368861e615827f27fb4897dd378b7ac0ffaa2176	0.4.1	vmiklos-frugalware.org
-20070924183139	4c3a4c890d3f532aa3b8acb6dac48057a4fecc19	0.1.115	vmiklos-frugalware.org
-20070924183155	3d9a92b727f0d45a8d3339101e800dd574fdeaea	0.1.116	vmiklos-frugalware.org
-20070924183642	25d87e4d9cedf15e7f1487a437d505f514d97e7e	0.1.117	vmiklos-frugalware.org
-20070925080856	9334cc280474ae55f67e058797e214d30251973a	247	wilmer-gaast.net
-20071003190543	c5dd164f95107dde963d46fefcffd04afc4b114c	0.1.118	vmiklos-frugalware.org
-20071006144659	7e66424c12563029201e7731cdd6e0301523c0aa	0.1.119	vmiklos-frugalware.org
-20071006151606	df9255d5cfe8e41220cabb664fe81c922db1f609	0.1.120	vmiklos-frugalware.org
-20071006152521	3922d44e2b9d4c970ed3697189fa63cdc9cbba6d	0.1.121	vmiklos-frugalware.org
-20071006153555	a75f2a7a6f57da846ab54d6fe5d52ec0c021a042	0.1.122	vmiklos-frugalware.org
-20071006160544	2d0780303e487cbca12ac1443a951b77339068b5	0.1.123	vmiklos-frugalware.org
-20071006192012	72aa7f08b94aada5a15ee1185e468d695ef54106	0.1.124	vmiklos-frugalware.org
-20071006195958	66c9558fd9e178ba54a294138effbaca83988920	0.1.125	vmiklos-frugalware.org
-20071006200349	5a61e43f470068f98ee7b04685376c23b9468974	0.1.126	vmiklos-frugalware.org
-20071006200614	3ef191040a3d88b21e287cf3c64bd9fc7e8c0026	0.1.127	vmiklos-frugalware.org
-20071006202844	c81d0ef2f921e16f74aae98320f551d822dac2e1	0.1.128	vmiklos-frugalware.org
-20071006204400	548bf76666373f661bef6f4c74c57818d79d973a	0.1.129	vmiklos-frugalware.org
-20071006204431	79e20f9d125529daa2ae548c1d6f2be3fcb7b863	0.1.130	vmiklos-frugalware.org
-20071006205332	349ee4a168ad83cab3ac71d7fbd0cf908fa10491	0.1.131	vmiklos-frugalware.org
-20071006222349	2a0f99c369161ab405765912c9ebe14e67e27ce7	0.1.132	vmiklos-frugalware.org
-20071006231209	31870ae4ebd50d79cca9fc18a0ea783c198ff0d3	0.1.133	vmiklos-frugalware.org
-20071006233849	b01dc6cf8e365c0e904fa2b6867021f544d117e4	0.1.134	vmiklos-frugalware.org
-20071007002728	c09d32721e49feda7c459dbba9222252ae56eec7	0.1.135	vmiklos-frugalware.org
-20071007010622	ec159f19dc1ff109c8876500af19d0952663e9a8	0.1.136	vmiklos-frugalware.org
-20071007015517	86f2683fd62482f297793aa711704b772fa5c345	0.1.137	vmiklos-frugalware.org
-20071007015546	760319dd435e3189fd8707fa72ea3a21663e1b69	0.1.138	vmiklos-frugalware.org
-20071007020052	d6a371e841e21ce810205e11fbf392f50c636371	0.1.139	vmiklos-frugalware.org
-20071007143603	86278cddbf922a051f22fb328b62ca9ae328c82b	0.1.140	vmiklos-frugalware.org
-20071007143734	ff584450a8473361fa2ced4ae31ad1641347e9af	0.1.141	vmiklos-frugalware.org
-20071007151332	6adca511491c58f71082f67ec755c83c8a3d1f14	0.1.142	vmiklos-frugalware.org
-20071007204237	a7b59252ddd85810c3b14357fd43602c800b9cb6	247.1.1	wilmer-gaast.net
-20071007205617	5b677eb23a1a29fc218c27bde28138e9b0addd7c	0.1.143	vmiklos-frugalware.org
-20071007214541	2305488d0a81193648dec7304f5a6a768e0c926b	247.1.2	wilmer-gaast.net
-20071007220725	e2869bf14ee7aca93d29edd142d60a7184b4d449	247.1.3	wilmer-gaast.net
-20071008000433	acd61b90ac5da7bcc15506194994116a9378fb14	248	vmiklos-frugalware.org
-20071008135609	2896674ef92f9fff19c312edc7edcd200029ff0a	249	vmiklos-frugalware.org
-20071010003424	527360f248e8085556175b0ce55724e650d0f107	250	wilmer-gaast.net
-20071010215632	8c2008ec2944c3eee13a25656cbd715f352ba4ed	251	wilmer-gaast.net
-20071010221559	118638279f7a39422d9e07365b380fa773c5243e	252	wilmer-gaast.net
-20071010224519	285b55d3b38d6e1c8b3fc4a64945f2bb9ace07f4	253	wilmer-gaast.net
-20071012000650	d444c09e6c7ac6fc3c1686af0e63c09805d8cd00	254	wilmer-gaast.net
-20071012000858	eda54e40d04c83028d84e91c895a550c1929b436	233.1.32	wilmer-gaast.net
-20071012121132	c78c03298b5fd99999dcafeb1e1c377a62d03019	254.1.1	jelmer-samba.org
-20071012121157	f618a4a446d9de22298f45edba53eadf87129efc	254.1.2	jelmer-samba.org
-20071012121816	764b163d6bc0587498c14de8023d70aea076e646	254.1.3	jelmer-samba.org
-20071013231256	428592094d725fd3795488e52a88c9abf79da0c0	0.1.144	vmiklos-frugalware.org
-20071014225742	3933853e12f9f4271c33e68425c3cc4d0aa53728	255	wilmer-gaast.net
-20071016230909	d601f9bc64776fc065a3bd7de3ef9d7868895893	0.1.145	vmiklos-frugalware.org
-20071016230922	09e2a69d7a43d15f3c41dc333133f8ad4d1d6ff0	0.1.146	vmiklos-frugalware.org
-20071017004638	f8674dbaa346e5097d9f56657673edbf80493599	0.1.147	vmiklos-frugalware.org
-20071017225941	a7af5f0501df21053bde7c77e695a544821999cd	0.1.148	vmiklos-frugalware.org
-20071017230107	edf09c6436dd5839528312d026b021a9ed07a07d	0.1.149	vmiklos-frugalware.org
-20071018160730	c511365ddc57359ab3020ccbd3f20e23b55ba5ca	254.1.4	jelmer-samba.org
-20071018164425	e9b755e3726fa41ac2d4ed1c3a6192d1af68edbc	254.1.5	jelmer-samba.org
-20071018190302	7435ccf486eee2f60d6a8b2ab0029b8f4ce17ab7	254.1.6	jelmer-samba.org
-20071022223946	7448e1bdb7ea75ed8a845816bd2f6bc76ce43785	256	wilmer-gaast.net
-20071026140245	56a3616934a8d5f0ed45866cccd131f6bd97ae4c	0.1.150	vmiklos-frugalware.org
-20071026193010	760ed1fb8e6248791615c9aa60d2d2c9282e9e30	0.1.151	vmiklos-frugalware.org
-20071029113552	3f1a946090bf1b5ff03ca933683e13fdb4cdfdd3	0.1.152	vmiklos-frugalware.org
-20071030142456	e103e735bae98a59694429884b802c8e79cead13	0.1.153	vmiklos-frugalware.org
-20071030142607	c4112c4918209ef178d7efa63fe40d28ba204fd2	0.1.154	vmiklos-frugalware.org
-20071030234439	5e2615a2d8a5ae64161727f8a32f6f0949f3fee4	257	wilmer-gaast.net
-20071105225949	22313024f80f7325a7253c64fe49cc6458df7dd4	258	wilmer-gaast.net
-20071110092550	be68d99a58721768033c83d2f0a8f26e1af9bd73	259	wilmer-gaast.net
-20071111200048	7cc31b187b141da628919b8044341ec13f733edc	0.1.155	vmiklos-frugalware.org
-20071114230922	1bf1ae6f25ff56894d67999791802aa864eaa02b	260	wilmer-gaast.net
-20071114232943	a6df0b5d21370549328c7929a008abb68f2ed4db	233.1.33	wilmer-gaast.net
-20071114234207	ebb95b68792dde490a1ea1042209525f176af58d	233.1.34	wilmer-gaast.net
-20071115142440	9fcaedc00fe94de64f97347ee55f92f9db970c8d	0.1.156	vmiklos-frugalware.org
-20071117122051	cd428e473fe4428041722fd373badef890edebd9	261	wilmer-gaast.net
-20071119222358	50e1776cb0c76b3328d458dd8a1bfb379b6b0e43	233.1.35	wilmer-gaast.net
-20071119231439	ef5c1855b406e462fb8b90b517f1672a47bcc4b5	233.1.36	wilmer-gaast.net
-20071119231618	256899f2f9eaa43610af53704d5c7ccbd1aace3b	262	wilmer-gaast.net
-20071119233657	7df5a087131d3b1bbe7add0907cd72217692ffd0	263	wilmer-gaast.net
-20071119234142	3e1e11afc869238d5cfca899d4814fea8a877687	264	wilmer-gaast.net
-20071120001358	d75597b0b31f8aa8ca523a3cfa4869e20fca8466	265	wilmer-gaast.net
-20071120094635	a5f76a29b323d671458cf86767cd9d785dd95309	0.1.157	vmiklos-frugalware.org
-20071120094743	de2f05ebf0d93193fc8217fc917bc63e9d49b81e	0.1.158	vmiklos-frugalware.org
-20071120095422	cbb5fb76c0c6adf12512915382c5db42ed5cf74c	0.1.159	vmiklos-frugalware.org
-20071120095535	b06e6478dc6c6494f19d444842d1625a1b315ee1	0.1.160	vmiklos-frugalware.org
-20071120162238	668a1226df26e9de24c2dd5919c8fb7f79220fd9	0.1.161	vmiklos-frugalware.org
-20071120162849	3d73b027895f5059f695445e1c4f2c21613775a1	0.1.162	vmiklos-frugalware.org
-20071120174141	eb9cbb8fd49380d73161dc197b91dadb974d18e5	0.1.163	vmiklos-frugalware.org
-20071122225652	56f260affd91651cb0c44ee14713f7dfa0717ad4	266	wilmer-gaast.net
-20071123222504	df6d1da013f42caa5f11dbcbb0d54710682811f7	267	wilmer-gaast.net
-20071123230744	77bfd077778c30c70f791752ce3a13d537aedd3b	268	wilmer-gaast.net
-20071123234620	5652d4369f6a807db7301f8d1350b7cc38abd6d1	0.1.164	vmiklos-frugalware.org
-20071123234913	fc26bcbfe29942e7f256d3504bbe53dacc690b78	0.1.165	vmiklos-frugalware.org
-20071123235827	e3d8051c0026557c86bcda7cb585d5610e0e6583	0.1.166	vmiklos-frugalware.org
-20071124011315	3a80471931642374f323e284ce3a5a04b5635a96	269	wilmer-gaast.net
-20071124180239	608f8cf652d0c443ef551ac979bd46096b361663	270	wilmer-gaast.net
-20071124190722	221a27346f768b9626f2a0281ff774790858a0c2	271	wilmer-gaast.net
-20071124191436	d044a5ad221d5c80fef779eb7115868540cc019e	0.1.167	vmiklos-frugalware.org
-20071124193223	1138b25c5134e895ba82310ad09de1eefde23d4e	0.1.168	vmiklos-frugalware.org
-20071124193236	c09f90f82a3f7750eab01dd8a66df7e37453f7ae	0.1.169	vmiklos-frugalware.org
-20071128210730	2c2df7dd91930345a9b22a8bb61327d1dcc7e3d5	271.1.1	a.sporto+bee-gmail.com
-20071128232426	b6a2373c2c9a98594a87c54a4644f3c0e985e420	272	wilmer-gaast.net
-20071129114625	23ba69e1e7e28a265fa0c9f73953b21c9291bb5a	0.1.170	vmiklos-frugalware.org
-20071129215514	9ff5737ec53cca2ba295b58fb74a8b97f496cd19	273	wilmer-gaast.net
-20071202110015	94d52d64c1ad3d55f210e1fc8e6345d54c4e46d5	273.1.1	wilmer-gaast.net
-20071202164357	4306d8b066d335cb6667414bdd6c5b2e78ccbfd5	274	wilmer-gaast.net
-20071202172446	911cc4f16e96c1ea9027bb12b024531a4b588038	275	wilmer-gaast.net
-20071202172557	5bd21df87b195af211c2280ca00391493a669895	276	wilmer-gaast.net
-20071202172746	fc5d06d4540894bafbc681c586f2b8aa5142357f	277	wilmer-gaast.net
-20071202231449	80e9db97776bfd6c6192135d65027abd8f50887c	278	wilmer-gaast.net
-20071202231825	aaf92a9eb64327892e39fdbd7113d797d1d68cec	279	wilmer-gaast.net
-20071203142845	2ff20765990c756533957e8da9c7c29dd3102e79	271.2.1	a.sporto+bee-gmail.com
-20071204002349	4eaadf6de030e402f8c58b2b00341167f2927a70	0.1.171	vmiklos-frugalware.org
-20071204004857	dce390357114e30a424106c99e49cef1e682e1af	271.2.2	a.sporto+bee-gmail.com
-20071204005304	fa30fa598d2e77c998f9156d0f31ccf99b0c781f	271.1.2	a.sporto+bee-gmail.com
-20071204010843	08135dff0e668622fc858d2ad8a7d0bdbdb8cb24	271.1.3	a.sporto+bee-gmail.com
-20071204012145	793cc254ad2479d95d00266d6cb7ab2bcd158834	271.1.4	a.sporto+bee-gmail.com
-20071204225959	de0337481fca3894c406a2724da30af8cc8bae2f	280	wilmer-gaast.net
-20071205013050	8076ec04b1b5699f8266fa0e2e17456974ced554	271.2.3	a.sporto+bee-gmail.com
-20071209214535	20994cb19ef3a4486b816e387b990f756bc607f1	0.1.172	vmiklos-frugalware.org
-20071209214645	2042b562628c17ce5771d1c8891029588001373b	0.1.173	vmiklos-frugalware.org
-20071209231935	c058ff976bbbbf43ef11c262f21440e61244f73e	281	wilmer-gaast.net
-20071209232303	3f2bc2d721c1ac3d6c11a88a350041c2166f71e7	282	wilmer-gaast.net
-20071210214229	6e97a7af2d31e18f53d528fb3c2e89214d0f4f14	0.1.174	vmiklos-frugalware.org
-20071210223308	71dc8543dc4eeafabbe28bae52ec1d05ce9f5fea	283	wilmer-gaast.net
-20071210225713	0f47613a39ad1b5d22d187e63c80c2f70702c217	284	wilmer-gaast.net
-20071212213633	d52111a7b05657e4a4fa8417e6655389a50769cf	285	wilmer-gaast.net
-20071212233051	30ce1ced040c44c528d0a6e6e9c6b10a1caf1052	286	wilmer-gaast.net
-20071212235749	3e79889dceeac1432156dc5fb6ae4a1f20b86d69	287	wilmer-gaast.net
-20071214005942	05cf92706f26eb672577e22a7c52ee839d58c5f7	0.1.175	vmiklos-frugalware.org
-20071214011321	3d9fd79665242851b84b85ebfa042a78bb116079	0.1.176	vmiklos-frugalware.org
-20071214011405	bfbb1aac92c4f0d2d26cf23bab9a83b06ae8a743	0.1.177	vmiklos-frugalware.org
-20071214012643	eb39102f161a6d59533c68c27134200f47b7e982	0.1.178	vmiklos-frugalware.org
-20071214013727	437ddf537f59fba82cb266c3b7ab208297317770	0.1.179	vmiklos-frugalware.org
-20071215222439	31b4793afbb7744c7b72de361fbce840c85ab0d5	0.1.180	vmiklos-frugalware.org
-20071216025555	67454bd5fa026e9c71ad1fb462d757e5890ffbe4	0.1.183	vmiklos-frugalware.org
-20071216025555	737e9d99ca5afe80214ccacf3d4a3737cb443188	0.1.182	vmiklos-frugalware.org
-20071216025555	46e7de3b304e82755d1073471781806b2cfc4bcb	0.1.181	vmiklos-frugalware.org
-20071216025556	6b43dd46d96b452d41b6b2581414ffdeb2c32335	0.1.191	vmiklos-frugalware.org
-20071216025556	885efac6586aa289ce3db2b83313c318715eb09f	0.1.185	vmiklos-frugalware.org
-20071216025556	5b9847d174938e61a76bb19da33ec28f2ca3b931	0.1.190	vmiklos-frugalware.org
-20071216025556	3c5adedc913d32af69a62f48bf368c18fb57ffbe	0.1.187	vmiklos-frugalware.org
-20071216025556	93052e1653e20b2d48aaa4b37b6dfe11644a9d58	0.1.184	vmiklos-frugalware.org
-20071216025556	191470d0245cffba19899660f84f77a2a18d08da	0.1.188	vmiklos-frugalware.org
-20071216025556	f9c3e7b63c2847e381f29572820c4415afb3efd5	0.1.189	vmiklos-frugalware.org
-20071216025556	bdde8058f78ab11a3e0bad48c61de97df720dec8	0.1.186	vmiklos-frugalware.org
-20071216030140	fb36492f9c1ddc58b8c164c32f7a437b60d5453c	0.1.192	vmiklos-frugalware.org
-20071216163538	529078a3e7f701c7b85201380b2d2097ac676ec6	288	wilmer-gaast.net
-20071216210410	33cac975a40690c1c2a40a5e3b9d65c23c49893a	0.1.193	vmiklos-frugalware.org
-20071216210651	969f6437c47d1d4428a501542df614d1213ae811	0.1.194	vmiklos-frugalware.org
-20071216211002	98345979993a59dacdfea030385a42a842488d5e	0.1.195	vmiklos-frugalware.org
-20071216223827	7f93882d16380c9464923ae32fcbbb0df7313d91	0.1.196	vmiklos-frugalware.org
-20071216224154	c01edb786c306dcc3bddd750b9cf495cc52fc5e6	0.1.197	vmiklos-frugalware.org
-20071216225436	6d0b26bbeadda9a60a51d27fdf4216f4756815dc	0.1.198	vmiklos-frugalware.org
-20071218020759	dc0ba9c85539533349353713162f94077fb27be3	271.2.4	kenobi-rhea
-20071218200455	c60b864216ada769f66337040a8f436604071a7d	0.1.199	vmiklos-frugalware.org
-20071218235935	eded1f703a8f5d2272b9d294d8e3dfb48fa302b4	271.2.5	kenobi-rhea
-20071219002230	2379566b07de55bd0f59503c39ba253ce2556877	271.2.6	kenobi-rhea
-20071219002432	0fbd3a6d26d8fe747bd5e061748e75f397801064	271.1.5	a.sporto+bee-gmail.com
-20071228232644	fb4ebcc5e3cffc4683cd3f38d7aba6cd86dbcd50	289	wilmer-gaast.net
-20071228232745	6f7ac174bb8fbde5e07e1c7da3cf691ecb4e172b	290	wilmer-gaast.net
-20071230234059	debc281b8f35015c0491c3a34465f82289a0e919	290.1.1	vmiklos-frugalware.org
-20080105150015	f394500c46003237373a47c5a1dcb4af16029e4d	291	wilmer-gaast.net
-20080105190302	3585c5aa1770147dd4a75a5283793d7908573011	292	wilmer-gaast.net
-20080105195634	6e68a52f92dea3a75ad4daad4867f9542506deae	293	wilmer-gaast.net
-20080105211532	1febf5c93b231942cd9cac8ac6a5a0d830c581cc	294	wilmer-gaast.net
-20080106123227	46dca11bfad72bb9365b85b433f1e812733f31ec	295	wilmer-gaast.net
-20080106123755	434627083613f016d432462fce73f728dd77172e	296	wilmer-gaast.net
-20080106200125	fc82be67d1f785218273e6269ed9d089a89b2bfd	296.1.1	jelmer-samba.org
-20080110003138	181e47a9693d56e4bc0cec65ec05fce6086aebe8	297	wilmer-gaast.net
-20080110161009	4bbd9dbb5bdcda09f05535902dd7ae20f4d4a264	0.1.200	vmiklos-frugalware.org
-20080110161010	61728715f9701af2a516f24fee50a379aca0eb6f	0.1.204	vmiklos-frugalware.org
-20080110161010	6abf3ec985d1ce9d9394fa17fbdd1162e4b1b702	0.1.202	vmiklos-frugalware.org
-20080110161010	54e6207a103a521c49582458dc857fd5c6641346	0.1.203	vmiklos-frugalware.org
-20080110161010	93dffeafa371bc2384dd13229d15fc31fab62d00	0.1.205	vmiklos-frugalware.org
-20080110161010	f5aedd9110935ad7b6ec7d6df4dac125bc0576c4	0.1.201	vmiklos-frugalware.org
-20080110163525	d1eb24c8f34946f47f9e592a75675d2ee01742ab	0.1.206	vmiklos-frugalware.org
-20080110163851	a06a0a55a8040efa5c276ee464025effb30973f9	0.1.207	vmiklos-frugalware.org
-20080110164839	680920f539ba0536c64124dd3ae76bda3f54e8bd	0.1.208	vmiklos-frugalware.org
-20080111004518	e7311208511c391b4f55161b6be93dbffb381c5e	298	wilmer-gaast.net
-20080111131711	e7f8838951c81ba7edf6c6567cf68075de42be6e	299	wilmer-gaast.net
-20080111202928	7e450c3787005c29658b08c7dd8ba684233d177f	0.1.209	vmiklos-frugalware.org
-20080112002446	e64de00bf2d99962182243983e8c09decaa36eb4	300	wilmer-gaast.net
-20080112013327	a0b206b4506ec4f1c30d82fab6a054e162bf7a14	0.1.210	vmiklos-frugalware.org
-20080112013654	5245e9d785fced3ef1232e47f1ee5a7fbef396b4	0.1.211	vmiklos-frugalware.org
-20080112014144	a3262d1e02907d5f53b2654cd121ce4535a7f412	0.1.212	vmiklos-frugalware.org
-20080112014559	e65ceaa6ba982fbfe14cfa191a56279f61c58a91	0.1.213	vmiklos-frugalware.org
-20080112172438	59f527b6eefaae452533170f52a96903ef63a209	301	wilmer-gaast.net
-20080112200710	c7304b27f173fed8e2a68272186b1575937ef98b	0.1.214	vmiklos-frugalware.org
-20080112200954	55664fc0503182dc27913df8ac2232eb8d979ca4	0.1.215	vmiklos-frugalware.org
-20080112201250	52d377d66b103eabecc5ddeab826b51c869b1c43	0.1.216	vmiklos-frugalware.org
-20080112201700	b2fa16be5ab06a2704d03f015ffe06905e0079bc	0.1.217	vmiklos-frugalware.org
-20080112202136	ccb50f556a067aabcac4a21c9300aadd7443f313	0.1.218	vmiklos-frugalware.org
-20080112202242	fdde7d808ddcbfa3d5d823a4e7591d7a61d0716f	0.1.219	vmiklos-frugalware.org
-20080112203425	a74f5b53ebcded2cb5ff06b6036f816b626bae8e	0.1.220	vmiklos-frugalware.org
-20080112210035	d96380bd58e419e758cd9776b9b47fa51e5c1f77	0.1.221	vmiklos-frugalware.org
-20080112210539	10a42d94c44b245588ee24ac87fb81666ce6007e	0.1.222	vmiklos-frugalware.org
-20080112211821	eeeb30e43cdc2b3f648040404bebb557e0075422	0.1.223	vmiklos-frugalware.org
-20080113001512	fc0cf92a81086903914056b32663479909a6fbff	302	wilmer-gaast.net
-20080113144551	07ff8a2c5af9b281fa6cdf6f1273f7c55c83bdb2	303	wilmer-gaast.net
-20080113144832	dded27d9a58237036b23f152eb6dfeaad3ae762d	304	wilmer-gaast.net
-20080113171345	13857c648210a16448619c65e33f9918bfe27cbe	305	wilmer-gaast.net
-20080115030308	57f04408a7ce65084d5a41221bb4f5ed71895a1f	0.1.224	vmiklos-frugalware.org
-20080115031035	3b495c08bd989f5ef3249a41b2caa37f9dabfa7d	0.1.225	vmiklos-frugalware.org
-20080115031426	15282dc421038974b28259f884e7cdb9f99bbea6	0.1.226	vmiklos-frugalware.org
-20080115031726	e72d869f00b464a96718931c37ab0d1d49ce0d37	0.1.227	vmiklos-frugalware.org
-20080115031943	68796a1298da7402560377a5f9cab35d2d4d44e8	0.1.228	vmiklos-frugalware.org
-20080117220655	52744f8f65a278a59a8903d5c594e057d63c7006	306	wilmer-gaast.net
-20080117224832	ac4adf9a546c2e9b247b9de4b753177e6bd479e4	307	wilmer-gaast.net
-20080118132037	83ba3e541acc3c29616fbf982b57633d303d941a	308	wilmer-gaast.net
-20080119123630	bea13052bf43e34b043d868dfdaa84241a8b163a	309	wilmer-gaast.net
-20080119182356	003553b6ac35ce3d9caa44ebe301e0eeb2125ed0	310	wilmer-gaast.net
-20080120000131	0adce2151fc2a4f517e558663e5afe010b86a39a	311	wilmer-gaast.net
-20080120001757	31e584652a5543b87e0953525a742fb067c28e6f	312	wilmer-gaast.net
-20080120111540	a882d6c1eec55fb9a00ce61acd6d776884d0c84a	313	wilmer-gaast.net
-20080121022143	fcef6e693ab4f8ad4329e1398310b9cd4591d0ba	0.1.229	vmiklos-frugalware.org
-20080121134917	3511d80b1706b433aeb61f85e1eb6524162bc406	0.1.230	vmiklos-frugalware.org
-20080121135251	ed88e8e51a4461e1632dbfd7599806ebb63719ac	0.1.231	vmiklos-frugalware.org
-20080121140350	ff990901904d48678150c80b965fd964ded5badd	0.1.232	vmiklos-frugalware.org
-20080121140520	6b266f603ae99e0f85e72bfcb75d2ea864067762	0.1.233	vmiklos-frugalware.org
-20080124222246	613cc5583465b2fa35288a2f28d0e408904c4fd9	314	wilmer-gaast.net
-20080124224947	b5c8a34aeff244ffe7a9f4bd5edf827495d0deea	315	wilmer-gaast.net
-20080128232900	3fd8b71c09e8100374358dddf2f051d7b1a58093	0.1.234	vmiklos-frugalware.org
-20080130230552	f774e01cdb34e339455671f51413ecdc6de3208d	316	wilmer-gaast.net
-20080202214809	0fbda19314c806e3e677847f3c977eb5a1bc2b61	317	wilmer-gaast.net
-20080203135419	979cfb448cc233e29ceb6cd43f99fb4104728be6	318	wilmer-gaast.net
-20080203165939	8c1eb8025f493ce22b4cc58da1ecfd4afa416816	319	wilmer-gaast.net
-20080203171115	8ff0a61e7389db201ff4534f267225af6f66736b	320	wilmer-gaast.net
-20080203213003	764c7d1009feda7db971ea7ac6bcb2a4acef0efc	315.1.1	sm-khjk.org
-20080203222813	3c80a9de52771f14e8aa947688fbf1dfc6ef5895	315.1.2	sm-khjk.org
-20080203233318	63075d7345355dde6e490ad00a886a1b165dbe17	321	wilmer-gaast.net
-20080204235408	69ac78cef9505f334cf61d13825371ce0c67ca16	322	wilmer-gaast.net
-20080206164223	a13855a57daae6eba05c9600b69f640c2949e944	315.1.3	sm-khjk.org
-20080207212518	c84e31ae1e972a327b103ada52cae6a2e2335767	323	wilmer-gaast.net
-20080209175813	5a71d9c5b14aa749b532666b71b25ce2afcdc5bb	315.1.4	sm-khjk.org
-20080209222439	f55cfe93aeae5c24fd067e28733826d7e3029085	315.1.5	sm-khjk.org
-20080209235409	7adc657830fe76df344bf718df45ca05cc015d4e	324	wilmer-gaast.net
-20080210111235	1ecff5ee9ab540584a8b15814dcc15f706a26d4c	325	wilmer-gaast.net
-20080210133643	52e6e17d3b3610afd6d6b997257b72e798d95c47	315.1.6	sm-khjk.org
-20080210164610	8521b02a157c92b9f6cddca043d30f7659ec2c25	315.1.7	sm-khjk.org
-20080210171106	3038e476a93b2be057581b831749eac931a06559	326	wilmer-gaast.net
-20080210175659	94e7eb3aaefc753ea81ea8c9e38570407d4c9790	315.1.8	sm-khjk.org
-20080210211213	5d6204020ac3439388666df54600ad65a384046b	315.1.9	sm-khjk.org
-20080210215428	5bf5edfde81640c1d918a2a1a1bd8bcd450805e1	315.1.10	sm-khjk.org
-20080211123501	eeb85a8a880fefe655eb31b6322136b61ee969e2	327	wilmer-gaast.net
-20080211143353	2a2db6f4876e16309ec3238b4c118db778778781	315.1.11	sm-khjk.org
-20080211143619	8c2b1c32386b9cbb06e1e6f03b3f2926e7761461	315.1.12	sm-khjk.org
-20080211151457	7f4016b5acc44085621f7e077883f969a08f5548	315.1.13	sm-khjk.org
-20080211151614	3b8e810996df506a5acca9ac5a46a23a4229250e	315.1.14	sm-khjk.org
-20080211203127	e6cb1101cbbf6a4c2751f20c2072c9ca3127ad98	315.1.15	sm-khjk.org
-20080211205833	d6fdf44c54ac2aecfd0c94a2d56be04e44a51eb3	315.1.16	sm-khjk.org
-20080211212035	c59530844d25ae814141cf56f0fa810968e8df55	315.1.17	sm-khjk.org
-20080211215310	8bd697c80fe304b9a63f9e87d4d4a4e3b1a6bebd	315.1.18	sm-khjk.org
-20080211231623	5f4eedefd2a444c213f65283a3d942d6287e6ea2	315.1.19	sm-khjk.org
-20080212000135	e2b15bb7ebd347dd044167575068ef87ffd9301c	315.1.20	sm-khjk.org
-20080212000843	0529a7f8d987072756ac727472cc1f8c3dc07035	315.1.21	sm-khjk.org
-20080212002541	3ffc53e13e7d153312ef1daa4c798ed0eed977eb	315.1.22	sm-khjk.org
-20080212013228	f5ac0fb078ee98bd836f1527dc90952f74ab0c18	315.1.23	sm-khjk.org
-20080212020957	1c18ce17500c2952ca1dcae4aa903e20574b3fca	315.1.24	sm-khjk.org
-20080213190521	1a1faa077c3c4ffc2bfe88a4466d748919fcdc45	315.1.25	sm-khjk.org
-20080214184347	a161d33779bb56fabe6466f15a8ae98881f55520	315.1.26	sm-khjk.org
-20080214184834	ef93a2f828b62e74f62638e7fbbe63e47f92194f	315.1.27	sm-khjk.org
-20080214210121	f3597a161cd13e076db626fed3d833cac0d62d6c	315.1.28	sm-khjk.org
-20080215004521	6c91e6e59a1216100d851636d2824ce49ad7ce30	315.1.29	sm-khjk.org
-20080215092726	522a00f1b1163cedf15a86329c0097601eb7940b	315.1.30	sm-khjk.org
-20080215173618	27db43361a3fdd3420b12aa5bf151dce4545273f	315.1.31	sm-khjk.org
-20080215173857	1ba7e8f1d28c4876ea0b787f1e4ebb5607f48895	271.1.6	a.sporto+bee-gmail.com
-20080216122003	2799ff94059f496b59420d04ec4f41f67aab2b9d	328	wilmer-gaast.net
-20080216131752	af97b234ad86fb9d69aa744af426591e4b1eaf97	329	wilmer-gaast.net
-20080216132444	dc9797f7ad4177dc72373ce71d375257fb0271a1	315.1.32	sm-khjk.org
-20080216152058	3064ea42452e7e069bce9fc132ceb8ae4d7d11b4	315.1.33	sm-khjk.org
-20080216152808	1221ef093f35d620123cab82aecf4bb7bcc2e86a	315.1.34	sm-khjk.org
-20080216154512	fc5cf88448d4337d1a5fde418df1c6206a9b0ea2	296.1.2	jelmer-samba.org
-20080216155822	ca605509d0b49e6012d10ae5d1553ced007e6ce7	330	wilmer-gaast.net
-20080216162438	903a2fcc60f82f52fe05c79250e6875dc48f23f0	315.1.35	sm-khjk.org
-20080216162524	896195002cc903ec4b1ef7e1468f73c1dc08df9e	315.1.36	sm-khjk.org
-20080216171531	4eb4c0f4beeb87e07bd6b10daed8fe8e48fb4206	315.1.37	sm-khjk.org
-20080216171729	fd9fa52e0014459079444bd7bfff7a40eef4e27a	315.1.38	sm-khjk.org
-20080216173830	a73e91a40ac1110e772214a3401105aeb86d35e4	331	wilmer-gaast.net
-20080216193817	2d88d25aeb62d90e4288f8b83979baaff96cb7f2	332	wilmer-gaast.net
-20080216220714	add23a26034a7368f4fdc0707488719048322e89	333	wilmer-gaast.net
-20080216224038	4bbcba32aca2948f66c484ab074264fdb67609ae	334	wilmer-gaast.net
-20080217013939	ba5add72f824504a21eb780cae638c3ea2166ba0	315.1.39	sm-khjk.org
-20080217015023	82e8fe8f36b0c0c53389358dca184f6d12184933	315.1.40	sm-khjk.org
-20080217015841	d0faf626e98cf8a332afac5ac7d61c80dd8d3064	315.1.41	sm-khjk.org
-20080217020523	37bff517e244def9f87a711a0154b182c89f07b3	315.1.42	sm-khjk.org
-20080217021209	2ecfe3987effa418806ab23bc24c82e3f14ca30f	315.1.43	sm-khjk.org
-20080217021242	d858d2196a11910382d879527d117c8a181177fc	315.1.44	sm-khjk.org
-20080217022111	59ab2afb144aaad7618fde69e81df40bc7241904	315.1.45	sm-khjk.org
-20080217022805	9e64011b1758bc9dbeddb517eff5eccbb0733674	315.1.46	sm-khjk.org
-20080217105228	12cc58b34a98ae010cf1718e994b63da35ddd7c2	315.1.47	sm-khjk.org
-20080217111549	4d8fbe809475aeed7fd073a328b53414642194b0	315.1.48	sm-khjk.org
-20080217111601	e6648bf3b821ae40c0640857aae069bc0f5d90c4	335	wilmer-gaast.net
-20080217112644	9186d15356a46576fb5b306f49a6acdb64fb7622	336	wilmer-gaast.net
-20080217123447	506e61b853ba969bc2d4d2878e87b975bd9e431c	271.1.7	a.sporto+bee-gmail.com
-20080217132448	fcfd9c56a81a993d86f18c792b1584ec5c5dca99	315.1.49	sm-khjk.org
-20080217184724	6f01885d51ad19296e0ac78b7560d3a4b5789590	0.1.235	vmiklos-frugalware.org
-20080218093623	5933da72f1f04810a1b789f9e1e1c1d7e360f30f	315.1.50	sm-khjk.org
-20080218094225	b14890de5407689555b1fcc293455fb77d306afd	315.1.51	sm-khjk.org
-20080219131324	1b97fc0982b1e72adb1b224606c0c14781109995	0.1.236	vmiklos-frugalware.org
-20080219144035	5e21e0619c0cac4d0634b3831f8e4054c551061d	0.1.237	vmiklos-frugalware.org
-20080220205538	2625d6de47df4054f793990118fa99f3d04d694f	271.1.8	a.sporto+bee-gmail.com
-20080223021359	6af541d6a5bf951b7f0ee874391f1b4948497d77	0.1.238	vmiklos-frugalware.org
-20080223021706	833eaeee91bddd0cf21255a4016b1db2c780acf7	0.1.239	vmiklos-frugalware.org
-20080223021751	e79a94f6d4818767a16c5db50d1b6639accb40ed	0.1.240	vmiklos-frugalware.org
-20080223022414	3a2a0b247bf3adc77ab5067dd261c24583c97e3f	0.1.241	vmiklos-frugalware.org
-20080223022604	1fb0a30dd661c7e4b1472ad8e5628f28f6174585	0.1.242	vmiklos-frugalware.org
-20080223024423	4bf5dfe49d470a95779dd4eac5ddc9f3d1441850	0.1.243	vmiklos-frugalware.org
-20080223024456	c47e56652775af5a6b8d170ac472842f95d76ccd	0.1.244	vmiklos-frugalware.org
-20080223030235	e49da2555ce82b009f3e0e25db2acbbf9789a043	0.1.245	vmiklos-frugalware.org
-20080226001052	ceee9c7593da8831d6c713265b1106ca56ae197e	0.1.246	vmiklos-frugalware.org
-20080226122729	6774d836e8adf5078932da50f434d6fae1a48cef	0.1.247	vmiklos-frugalware.org
-20080228223937	d5bd9c078ae2fb4d2e9354e943e06e017e878776	337	wilmer-gaast.net
-20080228231845	8a2df93eac9fa86d132b709731dc6a3f21af3257	0.1.248	vmiklos-frugalware.org
-20080228232146	789d05591e8497b877bf10551c775252c518d011	0.1.249	vmiklos-frugalware.org
-20080228235209	459160d31d82ae977216f341366aab527fd672d5	0.1.250	vmiklos-frugalware.org
-20080228235241	b68b0238a35c962d1669d3e0c3e61c9144cebad8	0.1.251	vmiklos-frugalware.org
-20080228235304	387884906887f72a3fce5ec8649da018d8339171	0.1.252	vmiklos-frugalware.org
-20080228235428	123e45a0a4dee7ad5b4c3ea0310397f570f2b357	0.1.253	vmiklos-frugalware.org
-20080229000922	9db023404848798a90183c8fc27f6ffe890cdb25	0.1.254	vmiklos-frugalware.org
-20080229003431	2eb4b1ffbb2e8f78913e81fd0e0090eaa161fcc9	0.1.255	vmiklos-frugalware.org
-20080229004246	239b036c7fc0eb9e9484e84caeb6e03594cc9eb6	0.1.256	vmiklos-frugalware.org
-20080229004302	acd94789946b2bc83ab529485a3342255a2cdecf	0.1.257	vmiklos-frugalware.org
-20080229004943	48181f05fef4c5c6624a06bcf5ce3648f63576dd	0.1.258	vmiklos-frugalware.org
-20080229005951	9fd72022b3410118c7ee484016f6321c36268ea8	0.1.259	vmiklos-frugalware.org
-20080229011001	a92fb0722c70432fe3176f398556df64cdae98ca	0.1.260	vmiklos-frugalware.org
-20080229014012	d87daf3e4e73dfbfbb1d669cb990a6325c2fa9d8	0.1.261	vmiklos-frugalware.org
-20080229020814	e0074cbd96725759cafd3c103213220c65195d8d	0.1.262	vmiklos-frugalware.org
-20080229021207	f5782755d6a73bc1b7524aeed06e54e09feee553	0.1.263	vmiklos-frugalware.org
-20080302171332	7bb3afbf24aaae0df9cd2e0e0cc9ebaf8e19f228	338	wilmer-gaast.net
-20080302175821	064e47c5927e1711fc45263d971998a7afca547b	339	wilmer-gaast.net
-20080302221206	b27557b2d23f3cbc0801bc628de24a2a6adcfca9	340	wilmer-gaast.net
-20080302231715	171ef85781e08ccbd8a6a018e88b1ad3cb802f78	341	wilmer-gaast.net
-20080303000849	ed3ae7e7d41f91917fc2ae92051c7c77c357fbab	342	wilmer-gaast.net
-20080303231836	7f421d6b922837857d6aca342da314225023eb46	343	wilmer-gaast.net
-20080307123858	df1a59d4562af4806da5463b788310cd35b84058	0.1.264	vmiklos-frugalware.org
-20080312162154	2dbb76888d137405ddeb268a3ca6fe47a45922e0	0.1.265	vmiklos-frugalware.org
-20080314214148	31fe3229c67499e154e14e6ec6307ac525340e44	0.1.266	vmiklos-frugalware.org
-20080314214402	df13c255db8bb08fe1d485fb18353fa6b8d6c21c	0.1.267	vmiklos-frugalware.org
-20080315022957	63405b47c483c2d5d50e09f308461c0df2b52cd0	0.1.268	vmiklos-frugalware.org
-20080315160950	9ad86bb22e53a40b3ad5bbc57f33d819d2748b0c	344	wilmer-gaast.net
-20080315174421	a869d9147584de96a0ac341416e551953167800f	345	wilmer-gaast.net
-20080315210539	79eae4a9edb343eaad30425289f7737467a535bb	346	wilmer-gaast.net
-20080315235354	4bb50efd1015a04d44c301961710fa08e0eda162	347	wilmer-gaast.net
-20080316004020	d07c3a8cde47096ad69db7139d410dfee29e509b	348	wilmer-gaast.net
-20080316141822	ddcf491fa460fea612c240589c50da864dad6668	349	wilmer-gaast.net
-20080316143934	e960a523f587a83bd22d000b4451c5a21b2951e8	350	wilmer-gaast.net
-20080316152837	50d26f33935aadc556dd3e79829b1ca01bbceab9	351	wilmer-gaast.net
-20080316160352	4e8db1c0141f74dc6156a57739613483344b358d	352	wilmer-gaast.net
-20080316163127	ec0355f6998eb5dee254e4bc60a3207bb661c854	353	wilmer-gaast.net
-20080316171723	c029350d962d95c2d5e9854ca4d82e597addf76d	354	wilmer-gaast.net
-20080317223524	6612cc9dd32475569875030f5acc7cc4b0374ba6	355	wilmer-gaast.net
-20080318013946	36fbaa0f4110d32274b9b88a18f38a4f19350fc0	0.1.269	vmiklos-frugalware.org
-20080318014023	c78476ca65ec085db5bb58169322bd3a82eef7bf	0.1.270	vmiklos-frugalware.org
-20080321002724	379c08a27e637dbcbbe3f39a8723daa765ad0e48	356	wilmer-gaast.net
-20080321003916	a83442a7c4dbf99937e9e5397e5665c671694161	357	wilmer-gaast.net
-20080322120250	851a8c29c681cdc3c9838ed99486822cba927f60	358	wilmer-gaast.net
-20080322140211	0cf867b8fbeb33e1841136a23297337f937da790	315.2.1	pesco-khjk.org
-20080322141933	eb0714d7be4f57b92231fcc1b0798c7fe644c000	315.2.2	pesco-khjk.org
-20080323142919	8a2221a79b177a5c6d0b55dafebd39414d7fe10a	359	wilmer-gaast.net
-20080323150242	dd14ecc29b87b9aefa97b6838f5a8595557d46d1	360	wilmer-gaast.net
-20080323215353	fcd5003bd6c10f176f63df459e8ca479c5292dc1	361	wilmer-gaast.net
-20080324110102	58a1449166ebc9c2913bf47a4bf05c4cf3e258b0	362	wilmer-gaast.net
-20080324135525	bfe7caaa8f7ec37506668b375bfa026b3349a6a6	363	wilmer-gaast.net
-20080324191129	66c51bbf19a599e3fffd2e3dbb5aae829e15af59	362.1.1	wilmer-gaast.net
-20080324194836	628e6018a8387603e67f4ce1c8b3b67126408726	362.1.2	wilmer-gaast.net
-20080324195740	990d5fd6b04c79994b4c1489d922711fd11521f8	364	wilmer-gaast.net
-20080324211323	483f8dd1042e89e02a0eb736f65885e9f74a344d	362.1.3	wilmer-gaast.net
-20080325093406	5e81e60307d9a1bdf18feab512c0056d9dea6352	365	wilmer-gaast.net
-20080329210109	0281c4598b396e837342c6fa6465bc709fc068a3	362.1.4	wilmer-gaast.net
-20080329211123	e74c7fe11099e45da9a17b4d791162d57d2768f8	362.1.5	wilmer-gaast.net
-20080329211141	89681335303b1118eda246c901f3c99470de19fc	366	wilmer-gaast.net
-20080329221917	a199d33ed818820ffba328f718799bbd77392f6a	367	wilmer-gaast.net
-20080329231504	18ff38fad0d6d47853fb43ec5a99b29ba031a8ca	368	wilmer-gaast.net
-20080330161515	5ecf96b935c6f6c0fba00d84cf7616ee04b06aed	369	wilmer-gaast.net
-20080330212616	f9756bd2e2711d58e06ad2a33ad3292ff10fc6da	370	wilmer-gaast.net
-20080330212839	ddba0aea68f90b06bb6278cada982eb88889c31c	371	wilmer-gaast.net
-20080331002033	a317ba64b8fbf85707e7a4e1ecc6a420166b7c32	0.1.271	vmiklos-frugalware.org
-20080331002033	afeb51772c8e801b5ba5c1e3c698529ef6a254e5	0.1.273	vmiklos-frugalware.org
-20080331002033	e06f40890c40ed5798b88cd406c91870257b0bd0	0.1.272	vmiklos-frugalware.org
-20080331003740	c1f3d49c9f7c626f5e17c0c653c56d3d0e7e1831	0.1.274	vmiklos-frugalware.org
-20080401230721	fa75134008bd9206ca02380927c27581feb65c3e	372	wilmer-gaast.net
-20080402132823	dd345753c1742905c9f81aa71d8b09109fbc5456	331.1.4	jelmer-samba.org
-20080402141737	5f5d433900a0eaec54edcd64ab8be0fc2384aa94	372.4.1	jelmer-samba.org
-20080402142257	85d7b857fb8ca8e3c03d4abb3368a0966760630c	4.3.31	jelmer-samba.org
-20080402143457	4af7b4fa9b0cc85ea8b7a00f748c324d5f56b47b	372.2.1	jelmer-samba.org
-20080402143713	f9dbc997f0d1c0ce6f6f265c0a04d693b24642dc	4.3.32	jelmer-samba.org
-20080402143757	d179921d5a014258181b3feeb5ac274e774a4558	4.3.33	jelmer-samba.org
-20080402150302	5be87b2e736962dce2576012b7f1cf215f169f34	372.3.1	jelmer-samba.org
-20080402150337	0a14b8cb44e199e39e04e04d0be1b74b660e7447	4.3.34	jelmer-samba.org
-20080402151226	1a57b893378c31bd94182b161921a38bdacf8300	4.3.35	jelmer-samba.org
-20080402153834	f1e74072b7a69c8ac9dd80c0ca53af72b893b08a	372.1.1	jelmer-samba.org
-20080402153940	e46e077ccbe5e3e13637618934a0f7979db6bc69	4.3.36	jelmer-samba.org
-20080402154033	69aaf14ad893fde14f8ba97c395ac8f6f8b96d94	372.5.1	jelmer-samba.org
-20080402213602	883a398f059f98cb31da77dd6e632e4152dcf87e	373	wilmer-gaast.net
-20080402224401	51dc72dbd88fec640d9838171a4cc5f5d8430274	0.1.275	vmiklos-frugalware.org
-20080402225717	b054fad92df7f0a843451f1178f1393b0d14e7b2	0.1.276	vmiklos-frugalware.org
-20080402230057	76eb071c636704c0223dff25ce0109f763f3296d	0.1.277	vmiklos-frugalware.org
-20080402231121	2af671a788ca8a7b3ec5fd667721438fdc770ee0	0.1.278	vmiklos-frugalware.org
-20080402231805	b7d3dff7ea68460f1a3e5ce22331d622b4df4d76	0.1.279	vmiklos-frugalware.org
-20080402231937	9aed2f1db6f8f08c8dd6523a445079582c2951b2	0.1.280	vmiklos-frugalware.org
-20080402232149	483430791a6b81871730258ca42d058c3989f942	0.1.281	vmiklos-frugalware.org
-20080403150552	6ff51ff7daf7690a58e8c2f2da2d070abf93fd3d	372.5.2	jelmer-samba.org
-20080404141946	d24b73fdab7c4892c93e1f75a094b0f7ebaf97d3	0.1.282	vmiklos-frugalware.org
-20080404143541	bd417a19271d514b66e1abe1bd708634ba1b8400	0.1.283	vmiklos-frugalware.org
-20080404143724	9ace502933e8157d3f8e5c33e0ec5d5de64476c8	0.1.284	vmiklos-frugalware.org
-20080404144026	799679477717b682280695b081d4ec1925e82e16	0.1.285	vmiklos-frugalware.org
-20080404231307	8dbe021fab08902eb202da8da26d183cb0832fca	374	wilmer-gaast.net
-20080404234254	8b0e38863660a743c7bf3cf4f768fc47ba6cf2c2	0.1.286	vmiklos-frugalware.org
-20080405115431	f3351f0ae5c6014e36e529684b81e78cad9a66ce	375	wilmer-gaast.net
-20080405121452	587a921d30ad99260e99f066c795a79f829ac819	0.1.287	vmiklos-frugalware.org
-20080405122604	9143aeb969697e05953b0f60a2fff2581fa0f18c	376	wilmer-gaast.net
-20080405123613	f35aee7fdfc66138d0525a0a7b9e02ccb1aaaec7	377	wilmer-gaast.net
-20080405130331	1195cecc99315c9c38e05c8dd0981792e7663583	378	wilmer-gaast.net
-20080406153425	aefaac3a5d44537bdf804d1f42e491cc2b931889	380	wilmer-gaast.net
-20080406183932	944a941af5b42b669334a9b937067a7e8cd856db	0.1.288	vmiklos-frugalware.org
-20080408204434	aa311173a85020bcbbbf61135a5451e171d422f5	379	wilmer-gaast.net
-20080410165716	93264f042351149eeb2b836beeea6f794397ca0c	0.1.289	vmiklos-frugalware.org
-20080414125915	6cac643f6933e431b90fcb937dec505f989e6a53	271.1.9	a.sporto+bee-gmail.com
-20080414131053	b79308b943149d729b1daea8c56cff9fc02711a0	271.1.10	a.sporto+bee-gmail.com
-20080414153313	0cab3888888c7c6b58af9560a0ae2c74a795727f	271.1.11	a.sporto+bee-gmail.com
-20080416090959	7f1cf705abd32959b9b949338bf4295c360f3891	0.1.290	vmiklos-frugalware.org
-20080420204032	0fbeef1a74f70c5ee7bd5f408ef7bbd861d3a563	0.1.291	vmiklos-frugalware.org
-20080420213816	d184f30fb86fbac067a337086ade2a78c2d44d4e	0.1.292	vmiklos-frugalware.org
-20080421215315	99f929cb5faca663969361e4a142d0c309af1725	381	wilmer-gaast.net
-20080428111028	80dfdce52f8a39ae24e7dc6993a341b559b2f211	0.1.293	vmiklos-frugalware.org
-20080430220715	140ffc8762b385cd0caba57ba192faba46b21094	0.1.294	vmiklos-frugalware.org
-20080430221755	4c340e907d611c13bee56393aa782aa954f758fc	0.1.295	vmiklos-frugalware.org
-20080430235749	b0d40f5b69100fa89498f0f6f496ce7171352261	0.1.296	vmiklos-frugalware.org
-20080504133215	4358b10c11410a27af9458c92067549cafbc4c0b	271.1.12	a.sporto+bee-gmail.com
-20080506001337	29c1456dcadec0d239ffc9d88ea06695b66c435c	271.1.13	a.sporto+bee-gmail.com
-20080506002011	d56ee38d444fb9a4bc0fbf7f699eaf675e019591	271.1.14	a.sporto+bee-gmail.com
-20080506080939	e3d0b10cfa542520c1e91ab7248c7309d7c120f1	0.1.297	vmiklos-frugalware.org
-20080506220622	44961cb326fde3cb681a79eb28becb74a921a29d	271.1.15	a.sporto+bee-gmail.com
-20080508054353	46d42308ee9a1120e39923dd38652681f2242665	362.1.6	wilmer-gaast.net
-20080508212036	b043ad5e69d91730145ea49305a1fab588c83a93	271.1.16	a.sporto+bee-gmail.com
-20080510100919	b5cfc2bdad8a68011bf9215c63293299e4e8dc5c	271.1.17	a.sporto+bee-gmail.com
-20080511121909	a8d28d95e748b5402178c643c4c6e4657e695ce0	372.5.3	jelmer-samba.org
-20080511193734	23c4e648e3d38336f949498d0b93e5b399087e44	382	wilmer-gaast.net
-20080511232336	4966d84194948f553a159cc2898b0895b622d59f	0.1.298	vmiklos-frugalware.org
-20080511233003	71dd27d413dafec9557193b18151235d69547f1c	0.1.299	vmiklos-frugalware.org
-20080513093331	52937074a237514547254f8656abc1a2d2dfed6d	0.1.300	vmiklos-frugalware.org
-20080516110924	bd85ec5184ac405fa923e592127e761900f043c7	0.1.301	vmiklos-frugalware.org
-20080519132549	5588f7c42e76fc4ec72870bc38cc65333ac578cb	0.1.302	vmiklos-frugalware.org
-20080519152418	d0a6a8c6632010a01607a2d729d5539451595543	0.1.303	vmiklos-frugalware.org
-20080519182203	d8919158023fa0d84487973a17f3551f9c325ca5	0.1.304	vmiklos-frugalware.org
-20080519182258	96272765923c395e15948a5d76adf036569843ab	0.1.305	vmiklos-frugalware.org
-20080521044155	92998915be47eab17e548af5c3dd43fa4eab9683	383	wilmer-gaast.net
-20080521102728	51bbec0b0e280be629f7b1086160b7730ebfdb74	372.5.4	jelmer-samba.org
-20080521102834	783e9b76de9a8ec16e8229d7c476b16ba8011554	372.5.5	jelmer-samba.org
-20080521130529	ece66da9a10fecde209c310c7785f266e40cb801	0.1.306	vmiklos-frugalware.org
-20080524005518	7987be8fae658532b5badaea2b5bc7c08c547e3d	0.1.307	vmiklos-frugalware.org
-20080524010227	8cd185c741d92b585576713322cd49ec1417e22f	0.1.308	vmiklos-frugalware.org
-20080524195747	de8e584d7f4fea214934af094a4df2672d7e0be8	384	wilmer-gaast.net
-20080601224426	6344674a7d160c8fccdc811bda9f4ae26c90200d	0.1.309	vmiklos-frugalware.org
-20080602124933	cce0450184b4358ef06d91cca985fa3ca389fcd6	271.1.18	a.sporto+bee-gmail.com
-20080602125857	c821e8ad0d1cf9ab71e6e34c685d0adea2dde923	271.1.19	a.sporto+bee-gmail.com
-20080602132638	3355a82eae40c89024233ccafc5f0c25dee2dc5c	271.1.20	a.sporto+bee-gmail.com
-20080609015228	c4a1036214c5f5c1ce14e937e8cdabc9fdf2c068	4.3.37	jelmer-samba.org
-20080609021815	12ebe748ad292e10c7680f65222c5c145db8a9b3	4.3.38	jelmer-samba.org
-20080609021834	1bf94924d6ae35c4369006ddbb6405ff0af74fe8	4.3.39	jelmer-samba.org
-20080609232936	b6cd9e9fe0aa86e83fc4a43b3db2240839e2103a	385	wilmer-gaast.net
-20080610024920	ddd477993505b1a97b705489c45a3f63594452b8	4.3.40	jelmer-samba.org
-20080610030444	7d3ef7bbfd09ce0a8d8dbdb651fdaa862f75af21	4.3.41	jelmer-samba.org
-20080610030933	55eda086dc21a5ed7f4e13a3b129b817d151df86	4.3.42	jelmer-samba.org
-20080610030949	7f49a8642e162611cf20ab95955098597d1f4472	4.3.43	jelmer-samba.org
-20080610031003	fde7b91536d2d0f5bc8e3e2226fba2188f038fb4	4.3.44	jelmer-samba.org
-20080610031321	8a4ae0da867a142dff6f5c96b6c460b725199ba0	4.3.45	jelmer-samba.org
-20080610031615	21e5d4981de057bae5261720021757d893061652	4.3.46	jelmer-samba.org
-20080610032113	73c2dce8961e937e97c78f0b8b0ee4abd347756b	4.3.47	jelmer-samba.org
-20080610034346	6a72a574df321683ee51f7cef14255cbb4cdbeba	4.3.48	jelmer-samba.org
-20080610210908	2e0f24d00b85c617400413bb2d179fd1028420c4	385.1.1	jelmer-samba.org
-20080614011912	52df5dfbe4761c26326083bef9daa80af9716858	386	wilmer-gaast.net
-20080615000427	5ec4129afd5d47d7cea5d9cb455a364c17c8a8fa	387	wilmer-gaast.net
-20080615181147	7f697401c8261459ce60c985ae1423db7b22c79b	388	wilmer-gaast.net
-20080615212020	945eb1ee5b118669387f415d1a45d766c6f79640	362.1.7	jelmer-samba.org
-20080615214915	7601353f20781f1fb9bba7e61697bc984ed0fd20	362.1.8	jelmer-samba.org
-20080615223008	e565ce246909cbb2aea9a4aa21377a8c0f5246fa	362.1.9	jelmer-samba.org
-20080616002647	f00e3ad082f84bd3e513bffa6ebe6800c4f66282	362.1.10	jelmer-samba.org
-20080617215815	d41907342cdb619c80bb7013136e9609f725e0e4	389	wilmer-gaast.net
-20080618171329	eb4b91c99307eca83c8b11c5af8bc00c2ca5c64d	0.1.310	vmiklos-frugalware.org
-20080618171353	aa4e3ed9d228d22653b5c39af5a8a71f20043efa	0.1.311	vmiklos-frugalware.org
-20080618171530	ca911b47bfff4f63ea745f395153f4c882b87256	0.1.312	vmiklos-frugalware.org
-20080618173459	b7fbeb4da939bc3e0b6aca9a71cf2b738bec2dad	0.1.313	vmiklos-frugalware.org
-20080618173728	89c68c7dcb6e9650af00bfaf336c8db190d762dd	0.1.314	vmiklos-frugalware.org
-20080618174726	21a387fd485dfcdc57047cc134960391e267d17e	0.1.315	vmiklos-frugalware.org
-20080621170920	da7b4847f5561dcf9b4aefa2968d3d27327e5183	390	wilmer-gaast.net
-20080621171927	edc767b8781d7c56ab3cfe67e2c816694722f5d0	391	wilmer-gaast.net
-20080621233411	3e6764ab9c8ebd99683fd3c153161d96b32e05de	392	wilmer-gaast.net
-20080621235118	98de2cca016d458ad2980c59f334fae10164b3bb	393	wilmer-gaast.net
-20080622093246	424e66361e985d05e47a7af42e81cd32b09dd6e2	394	wilmer-gaast.net
-20080622095356	fb4e9a3f0edbabf932c310374b9eb5121c0dab7d	395	wilmer-gaast.net
-20080622100533	c801d25cc574279566b35c3a9fce96962521670a	396	wilmer-gaast.net
-20080622122619	de823359bee02853be86b75ddc8272812502ff5d	397	wilmer-gaast.net
-20080622130215	fab3d2d497e2819c142859a3698e85372e58df14	398	wilmer-gaast.net
-20080622192106	89d736a169cbff4520dcbb475aa7269b2cf4b837	399	wilmer-gaast.net
-20080624090129	dfbb0563ac250c65f74b7bb3f49ca8d2ccc9e9c8	400	wilmer-gaast.net
-20080624091547	05b44da70c6a133676ded338a707465f32c09e09	401	wilmer-gaast.net
-20080624220632	d048ffce031ff9f674f20be37f6bfbb18047004f	402	wilmer-gaast.net
-20080626231831	e0f9170849e9c4aaa679f86703a60686d36607bb	403	wilmer-gaast.net
-20080628000644	b019c23bdd6e360ef5853f05cc964869c327f3f5	0.1.316	vmiklos-frugalware.org
-20080628000720	d276c9a8a1580682b8937b17ff9da3a15c74f803	0.1.317	vmiklos-frugalware.org
-20080628000756	f7c6049180a6e3ee702f7af4254981a60d84069d	0.1.318	vmiklos-frugalware.org
-20080628000925	039116a68fd1e92ee0869f88d4ee52183349062b	0.1.319	vmiklos-frugalware.org
-20080628001042	9765aa67882cebea28e49f8f73bed66dfbd43b15	0.1.320	vmiklos-frugalware.org
-20080628001150	3839517cd8739f4d079ed8573fd58a9244092abe	0.1.321	vmiklos-frugalware.org
-20080628001611	1d14818c6015adb0ec91264360f710e85f1bdbc0	0.1.322	vmiklos-frugalware.org
-20080628173241	178e2f8f71e5ebd4501f455c874f816b9ba19ade	385.1.2	jelmer-samba.org
-20080628173534	47b571d306a3da9932bc2616ab954ee342ec6519	385.1.3	jelmer-samba.org
-20080628183852	913545e54123296fb8229ecad2c77a6e899e0242	385.1.4	jelmer-samba.org
-20080629093541	565a1eaa8b36ca64a7806e6ad74a9a26495c0c6e	404	wilmer-gaast.net
-20080629111150	1145964911d0d7dd5145de6f7b9d4ed8aeeacd79	405	wilmer-gaast.net
-20080629124739	cd63d5822e76a6126bb3017567c9ce2869a44e0b	406	wilmer-gaast.net
-20080629234139	f5d1b3185140e690a6503b9e68c7ea28655b405e	407	wilmer-gaast.net
-20080629234511	dfd442b384ff04de537b399fd7e42ed01b62cf10	408	wilmer-gaast.net
-20080630153712	6a78c0eed44820a2fefe1e96516e335eddc9c70b	409	wilmer-gaast.net
-20080710082922	9033a0ab5c23a5031f45ab4982c762809db41cd6	0.1.323	vmiklos-frugalware.org
-20080712201031	4b0092ef36e57d7043720078482d01212749e9f1	0.1.324	vmiklos-frugalware.org
-20080712201110	6af3e14971bcc890c01895474b00aeced3d1ce26	0.1.325	vmiklos-frugalware.org
-20080716214512	9730d7250bb9e938ca00b72efdd8e8b3c03b2753	315.1.52	pesco-khjk.org
-20080716232252	6738a676c7a3895988de4bd9eacfe8fa0ef73cc3	315.1.53	pesco-khjk.org
-20080716233416	9b55485a6f9d72334b372e6fb6b60bbde943170d	315.1.54	pesco-khjk.org
-20080722123656	9afeeface6363ea81c5f80a7274a691f18550a21	271.1.21	a.sporto+bee-gmail.com
-20080722123749	8a90001ab2cde971abc64b8d5e4174dc4c4b0ae2	271.1.22	a.sporto+bee-gmail.com
-20080724125609	9f829c4b71d5c86db7b59a6b7d8f7dc8c5c23295	0.1.326	vmiklos-frugalware.org
-20080728234458	718e05f842c1af043eb4efded8b0afe429377f70	410	wilmer-gaast.net
-20080729160810	59bf24f8f0373bf190b90f9a2ca6949857b9dc4e	0.1.327	vmiklos-frugalware.org
-20080729161202	df482d406c71ecc539c77fbc109992433ba0ce42	0.1.328	vmiklos-frugalware.org
-20080729190845	75d5e71882789ec27c0615db0e6125cfc3f186aa	0.1.329	vmiklos-frugalware.org
-20080731204443	0f4c2734cd78168c42700d773cca666fab363f66	411	wilmer-gaast.net
-20080731222713	e0798ccc765b309b62844de1ef1ce33bbf558484	412	wilmer-gaast.net
-20080802112136	d84e2a93feae8db909dfbe63b55066cb4e8160dc	413	wilmer-gaast.net
-20080804142149	4ac647dbcef152bebde7209f7c9cbbf8a5e0fc37	271.1.23	a.sporto+bee-gmail.com
-20080804144524	8661caad555f4306cf36ee37979a7637b05d5cd4	271.1.24	a.sporto+bee-gmail.com
-20080805230707	280e655722c8660ec2dff9b08f82b10d5559bfd9	414	wilmer-gaast.net
-20080807131934	446c6fffaf80d86b6c756c728adb0d5fb5b40110	0.1.330	vmiklos-frugalware.org
-20080809230038	4230221725b0cd0f6a9b84f726759d20ff9f0e62	415	wilmer-gaast.net
-20080810091526	a8305126a38eb977c51046dd4ec3ac258a20a98f	416	wilmer-gaast.net
-20080810104252	87f525e0469f80aea715692ea74ebccbe688ae45	271.1.25	a.sporto+bee-gmail.com
-20080810221758	a2b99ec7a1a02c57b2ef44663e56bdfab6063a4f	271.1.26	a.sporto+bee-gmail.com
-20080810235148	92f4ec5d9d480247281c50c163c19b1ea438c1b3	271.1.27	a.sporto+bee-gmail.com
-20080811143103	66be7849ef6b74c39bc8f1dc1d96bc4788eb50a0	271.1.28	a.sporto+bee-gmail.com
-20080811230712	a02f34fb047af728f991ced3688c5e804c130878	271.1.29	a.sporto+bee-gmail.com
-20080811232449	45b9d3e776024bdbda3edddaf85d130367cdeb6e	402.1.1	wilmer-gaast.net
-20080811232540	59c84c20ad924fedb9106599b96d2c48ff1cc316	402.1.2	wilmer-gaast.net
-20080811235011	5d3b4e8b7373416c0f48543a6a97cbb3ea591051	402.1.3	wilmer-gaast.net
-20080812092409	5d550c51a5e9c9f48f26283f0ea3fee2d4945feb	271.1.30	a.sporto+bee-gmail.com
-20080812110437	aac40178a6669e20855b7f5d3cc6a82cba10042e	271.1.31	a.sporto+bee-gmail.com
-20080815160853	1f59f2f7eaefc001b8c30eff12a8c52b94c9b113	0.1.331	vmiklos-frugalware.org
-20080819222107	3b32017ca4d13d1385c8c96eef14fcd62ba17464	417	wilmer-gaast.net
-20080824164540	88d2208221b5128c89d65a6539c2cbcbc1fdba6e	418	wilmer-gaast.net
-20080824164903	d301872cbf032a56c946cb92fa11b511aff3f243	402.1.4	wilmer-gaast.net
-20080824165101	934dddf3614eae2b4f305f42583b070bdbd5bc86	419	wilmer-gaast.net
-20080824180105	7125cb3775a0e384c0f2fc08fd56df9582199502	420	wilmer-gaast.net
-20080824205231	f3579fd061746fe88c53330a2b9002da4193b37a	421	wilmer-gaast.net
-20080825204848	f536a99f4541af0252dff3991ec608e3d3469117	422	wilmer-gaast.net
-20080826223354	b119d5de7e6b53b5000661976dfbc31b09fa5104	423	wilmer-gaast.net
-20080826223609	9ebee7ae59686cc79985c8dd3101451b8a2f22df	424	wilmer-gaast.net
-20080826223624	b9e2539636588906bfd218ce176a38046cacf627	425	wilmer-gaast.net
-20080828222734	0a4f6f4d3eff2944ff36a0bd6ec0986824f23ade	426	wilmer-gaast.net
-20080830185958	ad9feec0895ea0b9acf1be3f24c9a2d5c8cc9782	427	wilmer-gaast.net
-20080830194338	1917a1ec17dde7c159af5ec65d6455c3348bb250	428	wilmer-gaast.net
-20080830213013	b84800d89604dee6fd837a2b8ab549346607e774	429	wilmer-gaast.net
-20080830220429	a9a7287a9698aa6038958da5074da1169d63ea9d	430	wilmer-gaast.net
-20080830222645	131c6b640e9921844fcf528de1a74682cfc6c768	431	wilmer-gaast.net
-20080831000453	e7bc722f096562914f54da2e1f8a0f3614763c1d	432	wilmer-gaast.net
-20080831091356	0e639f5e5245aa807764162b7f1928b641947658	433	wilmer-gaast.net
-20080831134233	39f93f0ce1c0a179b51f5ff6474d57509e9e0d17	434	wilmer-gaast.net
-20080831145439	d995c9b5de1bff5e3eb5de47b7ffbd3e92e2ac3d	435	wilmer-gaast.net
-20080831150035	3611717156f4c9ebfdf829319840d49e59b827ce	436	wilmer-gaast.net
-20080831154151	3b99524d537183f74f34be8fef4e02324707f34e	437	wilmer-gaast.net
-20080831224932	07054a511479f070a274b4c8a488cc79b79abe1f	438	wilmer-gaast.net
-20080831231932	c1333754810c20ff77324c9463ec3efba3c493d2	439	wilmer-gaast.net
-20080831233756	35529ae92ca468d7be6be60663c1f8d6a07b8373	440	wilmer-gaast.net
-20080901102101	f86a3d558e1e69a3728c8f8fd014cbcd6271f98e	441	wilmer-gaast.net
-20080902074856	d4810dff26f128061d122cf120e1e0174d9df1f2	442	wilmer-gaast.net
-20080903104424	21978bce030581756d2eac0abf45235ac03a6baa	0.1.332	vmiklos-frugalware.org
-20080903105054	15e220045ffd8fdc88a953cf4b4bbfc3ca1c5692	0.1.333	vmiklos-frugalware.org
-20080905195445	752a591bea8d2d3b4e9529a8220f46e54389cc0c	0.1.336	vmiklos-frugalware.org
-20080905195445	69939608df5a3af1aba01bf78513137758590cf1	0.1.334	vmiklos-frugalware.org
-20080905195445	ef399c287aa73adc0c9bbd587127b1fafd071179	0.1.335	vmiklos-frugalware.org
-20080905195446	b414e30b7dbc3d5d0f8a5daea6242a681cc30f56	0.1.337	vmiklos-frugalware.org
-20080905195446	56d88baeb271f851441ef8e23647f22a030c8ede	0.1.338	vmiklos-frugalware.org
-20080905195446	038fa18b1c391d0aef97a80be8844fff10cde189	0.1.339	vmiklos-frugalware.org
-20080905200345	93f4dacd9a92d47a618dee182b485298819eb364	0.1.340	vmiklos-frugalware.org
-20080905200445	0ec644d9eb69cf395582104b0072f005a54402b7	0.1.341	vmiklos-frugalware.org
-20080906013525	12e1162ec61e614109266884d45bd82f9fdfc90a	0.1.342	vmiklos-frugalware.org
-20080906014249	58f42365e39a5420726b0d49283b0c8a0615c38a	0.1.343	vmiklos-frugalware.org
-20080906015722	2a8d790c279612dad2f3a0f6ad3732a1cce1a72e	0.1.344	vmiklos-frugalware.org
-20080906020705	720ae585474d544a25598c6d92f9e9184fec80b2	0.1.345	vmiklos-frugalware.org
-20080906025903	74159899faae3dd0a01e7d139bda0b6766d466fd	0.1.346	vmiklos-frugalware.org
-20080906145834	89949c7b1ff9eeeced6561a507370dd92931ff19	0.1.347	vmiklos-frugalware.org
-20080906225932	3183c21afa7700ebc4dac02367653d1398a5b14a	426.1.1	wilmer-gaast.net
-20080906231047	15d146925d949882037c60cab12abcbee6651fab	426.1.2	wilmer-gaast.net
-20080907130351	76b652192e8f1a247bd3152cf62b17966efeb72e	426.1.3	wilmer-gaast.net
-20080907142416	ba3a8a578430418050a7fda3c949d65434b23cb3	426.1.4	wilmer-gaast.net
-20080907143450	ced1e17474366772791186f865bd45d4decfcaf2	443	wilmer-gaast.net
-20080907171349	b99296fbd87be89a60cb653be415c3173ee60ba2	426.2.1	wilmer-gaast.net
-20080907220928	85ebf71a809eb9abdfae779bda89c88f6f0f319f	0.1.348	vmiklos-frugalware.org
-20080907221451	cc5f8acf5e88f9f207170172a22468847ff95f30	0.1.349	vmiklos-frugalware.org
-20080908011000	885e563e68673e4c2daee2861766fc8913120158	0.5.1	vmiklos-frugalware.org
-20080908073148	68c162b44f12aefb11a4c1074ad7366ead1b9846	0.5.2	vmiklos-frugalware.org
-20080924090355	c49820d52fc3e1f2b744a7b513fdf4cafa53bf29	0.5.3	vmiklos-frugalware.org
-20080928001821	2bebe15b447b84fd5705a021f73db609c336fd73	444	wilmer-gaast.net
-20080928105646	e180c59a7796bb651b96ffaa5757e4688f1d3cc6	445	wilmer-gaast.net
-20080928111819	94acdd0d7beaa659a5f6b26673c5dea5dbcc4496	446	wilmer-gaast.net
-20080928112155	b2c062d609becdffc8c8542f68e260ab7b36dbcd	447	wilmer-gaast.net
-20080928130936	fb98634646e0560bc8f99b56502b6f050742a072	448	wilmer-gaast.net
-20080929215305	c05eb5b4711774c4274adafdf21558c3ea0492e4	449	wilmer-gaast.net
-20081107133451	12bfb2731a37d978fed1f971be7896c70b6a525c	0.1.350	vmiklos-frugalware.org
-20081107140628	7199b745fc49ae5a08bdb880b7631f99191e2c6b	0.1.351	vmiklos-frugalware.org
-20081112174746	c1bf7c5f9041f99934d1932fdd62457b88d4ac36	0.1.352	vmiklos-frugalware.org
-20081116011654	6b9cab1b2a6e9fa743ff3528090bb3a00ce14f71	0.1.353	vmiklos-frugalware.org
-20081213120805	de49316a14f16000968b2c12a61304afaf731072	450	wilmer-gaast.net
-20081213194656	77fc000cf5248e3c82d21ee20c02db25191d0f0f	451	wilmer-gaast.net
-20081213200255	549545b3c4c1f60eb977e1c16042aac6fe42736c	452	wilmer-gaast.net
-20081214011455	6d5eb723d73cabcda196189d70bbebc2761eacc3	453	wilmer-gaast.net
-20081214013859	e1720ce8958ae014ce38d03b7b9ab129d947921b	454	wilmer-gaast.net
-20081214101941	939370c674071a198e3115f9fa511fa51e8bea9e	455	wilmer-gaast.net
-20081214103149	c0c43fba49da3d14097f2c7cf3569a829b84125a	456	wilmer-gaast.net
-20081214104017	ac46218e481891b0a1d02a0882a3bf964c4ed21c	457	wilmer-gaast.net
-20081214133902	71d45c298e0757b5f92f0302b987e368ee75891a	458	wilmer-gaast.net
-20081214150448	54699524eda49bb287d5998e443deefd81fce8fc	459	wilmer-gaast.net
-20081221183524	56ae398cc2d137b669a6ef13f4a5969a9d92b70f	0.1.354	vmiklos-frugalware.org
-20081221191934	7258d34ab4d1b1f55c3b52944a0b5264111ad3a2	0.1.355	vmiklos-frugalware.org
-20081221193759	08a355bc2c95bee6ce53b3d6052ccb63684df8a8	0.1.356	vmiklos-frugalware.org
-20081221200030	5acf9ab725181e59f475f17d4647b700b36015cd	0.1.357	vmiklos-frugalware.org
-20081221200254	a2ed574e86a997a9e245eb821add1ee786629921	0.1.358	vmiklos-frugalware.org
-20081221200844	a5f4040ca3d4d70c593875cfedb3c1f6a249ace4	0.1.359	vmiklos-frugalware.org
-20081221205112	5adcc6514dacb4242c1cc89206f95be4f5a200db	0.1.360	vmiklos-frugalware.org
-20081221234551	a349932a6a69d5bb7fbd199b398a1716efd6e5b0	0.1.361	vmiklos-frugalware.org
-20081224090042	72b6783ebf2af43a384364e62eab71b1c5e6f9c1	460	wilmer-gaast.net
-20081225110511	489f996fe52c6969a9574d5ca562a4f5cb5c2585	461	wilmer-gaast.net
-20081230023943	8edfc90fbe2479d7456710139efeab6dc65a9ba5	0.1.362	vmiklos-frugalware.org
-20090107003118	b820226f457d35f6f255252dde80106a95231502	0.1.363	vmiklos-frugalware.org
-20090107003124	83f9aeb3fcac1a90a94631256549bd8f060e0dc1	0.1.364	vmiklos-frugalware.org
-20090107003514	5258dcc95079ee4ae4fab2fa37fb62567f9f2723	0.1.365	vmiklos-frugalware.org
-20090107005815	8c09bb33fe1bbbfeb820f72fc69026a48f061831	0.1.366	vmiklos-frugalware.org
-20090107010253	359f4d9ae19a45d0d7d34bc2ba27c41e0ab6730f	0.1.367	vmiklos-frugalware.org
-20090107011013	078b0b909888bb0a39f7b5365c772866a8ff4d46	0.1.368	vmiklos-frugalware.org
-20090107011144	8bbe52abe983613d5d25e449d2a3fa58301d7089	0.1.369	vmiklos-frugalware.org
-20090107012057	6e14204b2d45074d83c850f7b1cdf4b4138e7fb4	0.1.370	vmiklos-frugalware.org
-20090107012338	9f2f25fe941c6da70c561c8320b6e0e5d9819027	0.1.371	vmiklos-frugalware.org
-20090107012608	e200daf6f8f6d278ea0a3a6ccd7d4395d0a14863	0.1.372	vmiklos-frugalware.org
-20090107012922	c35bf7a7b8b8756ae0388e724c6fba9fe2b0feaf	0.1.373	vmiklos-frugalware.org
-20090107013138	2709f4c2bcc1425e224e1dd917923a40b084746d	0.1.374	vmiklos-frugalware.org
-20090107013544	607f5e3ad13d20fe4788c0d5bda94578a033ccab	0.1.375	vmiklos-frugalware.org
-20090107015618	ff436bab025f8b23f4518f77b34c0449f0182601	0.1.376	vmiklos-frugalware.org
-20090107020220	9580a6fb235580f333d0913ddcdcf574e92bbd59	0.1.377	vmiklos-frugalware.org
-20090107021029	6b9d22abe0ef2ccdc06eced1e82eb8ef35b6d1ed	0.1.378	vmiklos-frugalware.org
-20090107022907	62f51ee9e0bac8184fa140fe46f2ca4a2d84038e	0.1.379	vmiklos-frugalware.org
-20090107023120	16304ab9596145e5b72432769ade8c6b333e82e0	0.1.380	vmiklos-frugalware.org
-20090107023625	bc744df00c65bcebacae04b756392117d4037f2f	0.1.381	vmiklos-frugalware.org
-20090107024019	5365f84b1694342e9ecef071b5189b8d66a32897	0.1.382	vmiklos-frugalware.org
-20090113185928	9e768da723b4a770967efa0d4dcaf58ccef8917f	462	wilmer-gaast.net
-20090118001255	bc9a9b00627226eb4d10a0f98df24075a491085a	0.1.383	vmiklos-frugalware.org
-20090119131004	3e8a4eac56f85d275785360e9c781647aef0a308	0.1.384	vmiklos-frugalware.org
-20090213223627	d7938f9de5b084921f436edeac5de4145aad0e63	0.1.385	vmiklos-frugalware.org
-20090213223938	129a6c59cd14c4db8b91412017db900e937e5d11	0.1.386	vmiklos-frugalware.org
-20090217031009	8cb17ff56afc17cd1ce95162cfe1859ae945fe08	0.1.387	vmiklos-frugalware.org
-20090217211203	072c0fedea47e7c06b69fdfe7031df0cee43cc39	0.1.388	vmiklos-frugalware.org
-20090217233113	25a7eb88368deebb9a595d983570a149e93881dd	0.1.389	vmiklos-frugalware.org
-20090217233800	50d453d4405d52edb1405928286ae9a251518dea	0.1.390	vmiklos-frugalware.org
-20090217234919	fbd6c6932312b8ed926e82dddd972beb2fbc0760	0.1.391	vmiklos-frugalware.org
-20090218000109	acfa364dc48d343822d66da252b43d678943f82b	0.1.392	vmiklos-frugalware.org
-20090218002307	3512d0b5e93b48600ed88152a2170c507a9d7e8b	0.1.393	vmiklos-frugalware.org
-20090218003013	bcdc24be31a1e5aec436590c3da17c0ebfac28b9	0.1.394	vmiklos-frugalware.org
-20090312191006	823de9d44f262ea2364ac8ec6a1e18e0f7dab658	315.1.55	pesco-khjk.org
-20090312193328	673a54c5a78afd1dd41b4cd8811df5ab65042583	315.1.56	pesco-khjk.org
-20090313113147	fc34fb5d0b717d90edfc7ed78f11166eb23c536e	315.1.57	pesco-khjk.org
-20090313143322	8ee6def4055589593d6e414fd26bfeb2f874b66f	0.1.395	vmiklos-frugalware.org
-20090313143510	37ae52a3a0b63efca41cff91082821a1e9a18992	0.1.396	vmiklos-frugalware.org
-20090419192256	7bf0b9adc1e22a47e5cf6eca0871352480a6dfc7	0.1.397	vmiklos-frugalware.org
-20090419192537	88c2cc39a2a928bd79673b2a79a47e61bc4f7228	0.1.398	vmiklos-frugalware.org
-20090424095255	b9f2894f04f3f4793034c43f26c0ded3595017dd	0.1.399	vmiklos-frugalware.org
-20090429172418	1b48afb69b4b523cbf51f678936ed7d33f3f6011	0.1.400	vmiklos-frugalware.org
-20090429172748	c9348c45e9733795bfbd9676e9e5af85a8636036	0.1.401	vmiklos-frugalware.org
-20090429173652	a25bddfcf4d40b18f2763ea7159464041dec2fef	0.1.402	vmiklos-frugalware.org
-20090531231343	92e90b75985fb163a4263cde496d5dc4605e8250	426.2.2	wilmer-gaast.net
-20090603161036	19176513584bf26fa69a8a946982d9c521038a17	463	wilmer-gaast.net
-20090606115436	7ce0373e35e3e0c96ecd2c2ca970e0c2b081bf32	0.1.403	vmiklos-frugalware.org
-20090607120646	9d6702ca7b92ce2f2c38d08c6562581c9b33499f	0.1.404	vmiklos-frugalware.org
-20090607184227	c40580daeea1f08d276dd2677ed8276124f8d048	426.2.3	wilmer-gaast.net
-20090607191355	b6dd429999c8737589f2f689879f8240d9bc048f	426.2.4	wilmer-gaast.net
-20090607194045	59169956e1f7a5dd86e3c1aab41bc6a252f7b653	426.2.5	wilmer-gaast.net
-20090607201925	25dfb16035f12ba15ad5fcfb6d428386ac4c38ce	426.2.6	wilmer-gaast.net
-20090607203910	0c41177b49c04893e0ce88dbb27f5f5b1aeb5896	426.2.7	wilmer-gaast.net
-20090617231715	b9369b55c99246a8feaee0ad24367044d92fa55d	464	wilmer-gaast.net
-20090624214439	8a08d92a092092b5d208616e03fb3fcee1fc3c86	465	wilmer-gaast.net
-20090624214844	2ea8736c7f59db6c183752f1f2e47cd988c08b66	466	wilmer-gaast.net
-20090720134211	01827732cb3d5289e8071d8317b95d5c4043857c	467	wilmer-gaast.net
-20090803111359	bfdcbfb681d04a5152716aa4b0f00f9cfffbdfde	0.1.405	vmiklos-frugalware.org
-20090818105817	61580d73c62578ba04917f3dc9a5df81cc831720	0.1.406	vmiklos-frugalware.org
-20090818113357	ffd078a652b468ad3087bfa52e973a19f6766b9e	0.1.407	vmiklos-frugalware.org
-20090831155700	c99bb05580de9c3710060fbda69d336c646ad97a	0.1.408	vmiklos-frugalware.org
-20090831155700	ab165c512416eb789591e02aab2c4678ccd27f8e	0.1.409	vmiklos-frugalware.org
-20090831170646	1a575f69c852614d53f618a3c95295113029477b	0.1.410	vmiklos-frugalware.org
-20090927112259	398eb78dd25f3f80d54f7d7cdc02ffdb1398041b	0.1.411	vmiklos-frugalware.org
-20090927112909	843fbc98ce3aac4691ca047a84e8120fc7ea26fe	0.1.412	vmiklos-frugalware.org
-20091003192750	4fefb772f530cd15ef3e92606532d3c6b193d96b	468	wilmer-gaast.net
-20091003222536	c3e349e0847b5b936d1040c56ea427a7f2ce0d7c	469	wilmer-gaast.net
-20091004142509	3129b9925dbaa44c98cfcbad2ee16ae802100857	0.1.413	vmiklos-frugalware.org
-20091004182841	bdad4079b4bbc8209bf17c81cafbf699f8c6d90d	470	wilmer-gaast.net
-20091004190053	e59b4f65183a7bee638312a0c96e3d0607cb181f	471	wilmer-gaast.net
-20091004232811	796da03f9f54f8fb193529288592571b371bf0cd	471.1.1	wilmer-gaast.net
-20091005233234	860ba6aaeabb25cd27ec70fb4a37d910dd5b3746	471.1.2	wilmer-gaast.net
-20091006214942	7da726b12a546a5022d8f91fa3a34764335ba037	471.1.3	wilmer-gaast.net
-20091006222601	d250b2a5fa95724613d0176ce85a6c6859407ce6	471.1.4	wilmer-gaast.net
-20091006225546	389f7bed787168abcc8aa5d01cdd49946cb863b6	471.1.5	wilmer-gaast.net
-20091007233732	0cbef26bd1f82787a8107e92b14839a59187e0c2	471.1.6	wilmer-gaast.net
-20091010133951	fa295e3620bdf928b6a853ec7e42b8c7fc5262be	472	wilmer-gaast.net
-20091010134844	ba168953ffe832133cf236df73e574fa54f8d911	473	wilmer-gaast.net
-20091010145705	037b66a34beef59e7c591ee868516f2305cea906	474	wilmer-gaast.net
-20091010150016	7ea8697c345d5d1e3f237a392b293abca948cdfa	475	wilmer-gaast.net
-20091010232554	e046390da36e369c94af607fdedfe7b9f99d9e47	471.1.7	wilmer-gaast.net
-20091010235726	c5c18c155cfdc3edcbd764633761d33e3c5992a3	471.1.8	wilmer-gaast.net
-20091011104040	4164e620b4f593a427a89d9292f4aef5c33e9def	471.1.9	wilmer-gaast.net
-20091011112609	4f103ea401bb6b1ed8963ea33d4924f95e10473b	471.1.10	wilmer-gaast.net
-20091011122223	db4cd40374ade33ccb1feae113f12a1dd0b6bf37	471.1.11	wilmer-gaast.net
-20091011125729	0f7ee7e53f6bcb2d1d262a94c278440413c0103a	471.1.12	wilmer-gaast.net
-20091011210826	b74b287af7ee980b01b89e911e21ec8f163d24b3	471.1.13	wilmer-gaast.net
-20091012000024	ec5e57d6f165a462ac686341f9075243f0a4586a	471.1.14	wilmer-gaast.net
-20091012221941	dd0d57b10a8c2d07001ca2d4228232962ed8b95d	471.1.15	wilmer-gaast.net
-20091012222349	e248c7ff061e1582ed4c2919de6d615c1813e87a	471.1.16	wilmer-gaast.net
-20091012223728	4524f664357f9f15e7535fc6f251c5be98d162da	471.1.17	wilmer-gaast.net
-20091013223312	e71cfbc35f3d2f3aa4da9e72776505d945429cbe	476	wilmer-gaast.net
-20091014213609	6967d012be48db989ce2723a6ecc2b10b537c8f7	471.1.18	wilmer-gaast.net
-20091017144821	99c8f1357f1ab85f3a98727cb8877403d965e3da	477	wilmer-gaast.net
-20091017150430	57d842193767ddbb87531ce9865b23fbdb72eadb	478	wilmer-gaast.net
-20091017151340	74349eb5b77e2143289ef98201d03870e0d2366a	479	wilmer-gaast.net
-20091017152541	3650088f5bc4476b3508b11997ca1f2a620b8afa	480	wilmer-gaast.net
-20091017154312	2e44b1f12fb58a6969a8fbaf2946d6ecdace484a	481	wilmer-gaast.net
-20091017172452	c5bc47b3197d20ec2d73e3024b932db30dfa3533	426.2.8	wilmer-gaast.net
-20091017172540	c48a033d62bb5c5188a876b182d50144ffaed68b	426.2.9	wilmer-gaast.net
-20091022215523	b75acf6367400ff88428618719cefbbee648b0cb	482	wilmer-gaast.net
-20091023104315	4cf80bb798a4159f42a904cb830b99c87a0ca8e0	483	wilmer-gaast.net
-20091023235741	fb51d85751b36098ad4271bc4553ade4dc53f20b	484	wilmer-gaast.net
-20091112114154	36cf9fda6a5cc4bcbfe98319b48af636fa142590	485	wilmer-gaast.net
-20091119131138	76c85b4c79d533ca7a780df381ccda5b9ab2934c	485.1.1	wilmer-gaast.net
-20091119145830	20e830b641638bc580e1a58b68fc3c5837e76807	485.1.2	wilmer-gaast.net
-20091119185053	08e5bb2bb2fcc7125f837be4f225d3a9ebf320ed	485.1.3	wilmer-gaast.net
-20091119230056	c289b6f820ea8006d84b3de13c9052dcb99ec99c	426.2.10	wilmer-gaast.net
-20091120130412	e5e795dae28c7871a47040436454a456fb338820	426.2.11	wilmer-gaast.net
-20091123225820	b3117f2524775ff7c61ead7c3bdb3799064ed97f	471.1.19	wilmer-gaast.net
-20091123230054	4e041946706d3fc3aed405152028b02ec2794902	471.1.20	wilmer-gaast.net
-20091123232337	cd741d8e2bb0b7d08cf36d90f5332a639f190281	471.1.21	wilmer-gaast.net
-20091123233831	45a19e543cc567981df92b62f428e0f89f94cb74	471.1.22	wilmer-gaast.net
-20091125001945	0ac1a37573f966d7a03b85816c583bd6976c402f	471.1.23	wilmer-gaast.net
-20091125004527	e5d8d21fd20516be53f873d269b469be109eca91	471.1.24	wilmer-gaast.net
-20091126000440	487f555c687571cee39d69b3954b4d1f82811d29	471.1.25	wilmer-gaast.net
-20091128004720	3e7b640db6ae2d77122d93dcf5f1a0989ef0b3f1	471.1.26	wilmer-gaast.net
-20091128195139	5674207b09a856149c110040f7f672182a04dee5	471.1.27	wilmer-gaast.net
-20091201210802	1b221e0abd6453e3ca9cf45916ff6d16f94eff2b	485.2.1	g.c.w.m.mulders-gmail.com
-20091202180840	b4dd25398db477b06452be195de14ca352008665	485.2.2	g.c.w.m.mulders-gmail.com
-20091207215419	2288705af462b4aca2d56f228bff269eab8d8b5f	271.1.32	wilmer-gaast.net
-20091207221140	ec29351181b9557c5490d2eed38505d22f9ce6bb	486	wilmer-gaast.net
-20091209102726	d9bca7f677c3e8461171dd9a9ea4522933834787	0.1.414	vmiklos-frugalware.org
-20091209115829	d0e9ceadeffaa42eeb1aecbd565018dda0c72e77	0.1.415	vmiklos-frugalware.org
-20091209120247	c29b62b385aec9004fb45d9bbba8b74aa9ac3afc	0.1.416	vmiklos-frugalware.org
-20091212001839	111fade0e62bba0c00c97e75b5b7f89aa087610e	0.1.417	vmiklos-frugalware.org
-20091212001902	c213d6be2b6f28bd7d9bf759aba819a2f19abdae	0.1.418	vmiklos-frugalware.org
-20091212010443	1f4fc807258b2d0b382534567bad61a3bfb50f3f	0.1.419	vmiklos-frugalware.org
-20091212010822	7c300bb12bbc73473de4d170d5fd40ea2c679e08	0.1.420	vmiklos-frugalware.org
-20091213144856	1c3008ac0b2b29f7e14ec9b874af3277c511c7a4	271.1.33	wilmer-gaast.net
-20091217004225	ccba98084344125e0f89331baced2ba6d1802b29	487	wilmer-gaast.net
-20091223225340	4f8396f9a5770096a52c11361350879d0d4c70ea	0.1.421	vmiklos-frugalware.org
-20091226151452	a19ea7a21e082d0e28aea7198bea0f3bd3e2eb4f	471.1.28	wilmer-gaast.net
-20100104121618	e08e53c9398700309000c6e6b7ff895185d567a9	471.1.29	wilmer-gaast.net
-20100217001704	1c2eaa3c99a2e7fbe264b06e559f3d709b9a080d	488	wilmer-gaast.net
-20100306141514	d1ad6f0973bda799bdf21e36f20dc58f7bb1bf08	488.1.1	wilmer-gaast.net
-20100306145052	840bba81bc4ab7ce37aeeea5f32091684b19e5b8	488.1.2	wilmer-gaast.net
-20100306145512	2bc8ac0680e6a04f8431003ab468bb6e419534ed	488.1.3	wilmer-gaast.net
-20100306154446	b051d39d99219852a1dc79f68ee301fc1fe16a5e	488.1.4	wilmer-gaast.net
-20100306161723	17f952299cd2780a2dfecc4d4b9861f6f0f4b1c2	488.1.5	wilmer-gaast.net
-20100306171032	4049061ea3b1fbe939c623a8501aea60906893ea	488.1.6	wilmer-gaast.net
-20100306174101	0714d51217d999fc67b6ef193e04f7af437cc2b0	488.1.7	wilmer-gaast.net
-20100306181931	be915f54a52dd0f88b2a64d5cef094b0e6c9334d	488.1.8	wilmer-gaast.net
-20100306182220	34fbbf9c6a80ea7bf5e371c6d36c6ed596b15bd7	488.1.9	wilmer-gaast.net
-20100307002233	58adb7e800db87c1e38b810c288f1455654dab3d	488.1.10	wilmer-gaast.net
-20100307003134	68198e97b6a6dd479e88fd1bd85a7b19cf67d149	488.1.11	wilmer-gaast.net
-20100307183231	63770b44953f9fbd7355b81217143375a2db246c	488.1.12	wilmer-gaast.net
-20100307183723	0e99548ba9c6ec9c78367e05b676dab90b5261a4	488.1.13	wilmer-gaast.net
-20100307184145	c32f492758759c04d8b6239a7862648c9d32c4d8	489	wilmer-gaast.net
-20100307184323	fb020ac61b1e457de7a44492bb05a84515828ac7	471.1.30	wilmer-gaast.net
-20100307190436	d1d5b34bfc0ebf4bc495f20cc51c99cd6d973d03	0.1.422	vmiklos-frugalware.org
-20100307190948	d9ce18c19ae539be26e5aa03c2bdc0842cb20642	0.1.423	vmiklos-frugalware.org
-20100307195039	f4d37c6ca3686e538d6615f966320b99353faec3	0.1.424	vmiklos-frugalware.org
-20100307195935	60edadb3ffd5a2c6d361243c974d06fc5be0777a	0.1.425	vmiklos-frugalware.org
-20100307223500	279607e52513bba31b7dfdd508d51e9ac24950c2	471.1.31	wilmer-gaast.net
-20100307230840	52cae01137057f2cc3def802a661fef92cedbcae	471.1.32	wilmer-gaast.net
-20100307232246	1e3120f4691c3c4eb026d39240a1f92959a71370	0.1.426	vmiklos-frugalware.org
-20100307233445	5d9db76cdcafe704304101b176b3731cfd29b8be	0.1.427	vmiklos-frugalware.org
-20100307233706	ea1d79673ffcd39a868147e88230799f9d227728	0.1.428	vmiklos-frugalware.org
-20100308000007	9bc60f0e852ae132cf93d5f76156ee3f67bfeb76	0.1.429	vmiklos-frugalware.org
-20100308012108	bab1c86e9e07553f58ab4ebdaad7f74018052b5e	471.1.33	wilmer-gaast.net
-20100308012510	b52e478f9cfea9c0319d3f0203b31e4b5d03f38b	490	wilmer-gaast.net
-20100308012553	fdba6bcaf699930482d3ec1b30df461c05582925	471.1.34	wilmer-gaast.net
-20100308021041	7c9db249e4d94413f561841495d1e147d53327d5	491	wilmer-gaast.net
-20100308224851	d0169e78bb0d14086f8648d3aa65968a86762c9b	0.1.430	vmiklos-frugalware.org
-20100309131350	3e1ef92cecad385c64c2398355d90225ac54440c	492	wilmer-gaast.net
-20100310235828	a0bd4c247472c1997afbe8683ef460dadbb16b46	493	wilmer-gaast.net
-20100310235847	75ec2c8398afde059e3a2403ca8907229d3720de	494	wilmer-gaast.net
-20100311111536	8b6b7405d3844271f7bff56e527bfeb1a4872975	495	wilmer-gaast.net
-20100312003840	56244c0a88b603a683da9c0cc6abfccdc7616265	471.1.35	wilmer-gaast.net
-20100312010521	4dc6b8d10786baafd3ead9a2ecb22d7065b9c4b9	471.1.36	wilmer-gaast.net
-20100312014744	7c5affcabd08f23e36719afefe736f266b80912b	471.1.37	wilmer-gaast.net
-20100312191016	be609ff2b0ca885612c0e6d81f82c4f26ed4f58d	485.1.4	wilmer-gaast.net
-20100312193551	dde9d5710cd6392592c1417032933f0ba4299d9c	485.1.5	wilmer-gaast.net
-20100312202839	00a0bc5cbed11bab2446267a9f3ad76666ceee75	485.1.6	wilmer-gaast.net
-20100312223255	547c94cf6b3654997ef2570d398468af75380b26	496	wilmer-gaast.net
-20100312230229	70533795ac441b82e40b0cf4b8709994be7ac5ce	497	wilmer-gaast.net
-20100313001207	1be0d260da62542dd27b8a6f5d367a2e79191a8d	498	wilmer-gaast.net
-20100313010247	286b28eabf39d98d642c73c34a16a599e61dfc99	499	wilmer-gaast.net
-20100314163555	bb3477107afcc6f532758bb08c4677d62e17ee57	500	wilmer-gaast.net
-20100314163746	caceb0672b29fa49958d5f8b5338ff8e6ecb74d3	501	wilmer-gaast.net
-20100314164222	e9cf291a52097d5b9428b8d1650cc691d5cc5dc8	502	wilmer-gaast.net
-20100314164700	435f55271b09102e21228a7add1b897025cdb078	503	wilmer-gaast.net
-20100314165600	af7f046b85694db45d670054e28960e4a0d79232	504	wilmer-gaast.net
-20100314174533	8fb1263325c6839b792c352283abac3f63142fa2	505	wilmer-gaast.net
-20100314174924	c6ca3ee33e27feefb8cae0dc1d55a7239baf43cb	409.1.1	wilmer-gaast.net
-20100314175527	fb009891bb60921401ffeb47b33cf35a246cbe34	506	wilmer-gaast.net
-20100314180956	ceebeb12e935e7a6fa72e1375e99d53cc91fcf45	507	wilmer-gaast.net
-20100314182243	90cd6c4780c7e42a0b7caff5d3a2ba1e0bd3f308	508	wilmer-gaast.net
-20100314201910	21d48d977b35d35d0ad86b3ccd051dae0d1fe055	509	wilmer-gaast.net
-20100314231441	7e2b5934976f6d833397f72bd11fbc63616a56f2	510	wilmer-gaast.net
-20100314231505	33304688895db5751f9ef087ff92b0a9dc284627	471.1.38	wilmer-gaast.net
-20100315012547	315dd4c1566dcd4caa9c4ca0eceeceb995a01443	511	wilmer-gaast.net
-20100315102625	9fca06579d61d6360520db98092bce13d30d39ce	512	wilmer-gaast.net
-20100316101802	449a51de265cb3b4f0f5003e09fbbb030247c972	513	wilmer-gaast.net
-20100316102400	ec55a7d76711d6b071e0d1ba043cb120d3e11c24	514	wilmer-gaast.net
-20100316104526	0b510af4fc35c08e7eb693c018c9db141916970a	515	wilmer-gaast.net
-20100317010741	a1ac227895d6e457bf0bc1fe3f4ecd72d2f39b08	516	wilmer-gaast.net
-20100317011323	f9928cb319c2879a56b7280f09723b26035982d0	517	wilmer-gaast.net
-20100317135652	75554d0a484d21f95fcf24394f89c541a650acaf	426.2.12	wilmer-gaast.net
-20100317150027	b788246cec5a718115a0aa620b72cdaf0315cac2	426.2.13	wilmer-gaast.net
-20100317151519	e8c8d00ea43c204ee276bde7fb663a0f0249790f	271.1.34	wilmer-gaast.net
-20100317155058	6ce01bec119c96243a8d43e28681cc512fbd0950	518	wilmer-gaast.net
-20100317180454	4b740c2ddf34c7b4390b5e44496ac19724343ccd	0.1.431	vmiklos-frugalware.org
-20100317181230	a73d6b282f46b7263f80aeb072ec078d40b85377	0.1.432	vmiklos-frugalware.org
-20100317232327	60e4df367e5c3af0eb1aada19f9c39ef7079e8e6	271.1.35	wilmer-gaast.net
-20100317234107	c1a3c27575ac6ae77cbffb1e48d02ebee3f83152	271.1.36	wilmer-gaast.net
-20100317234735	42fc5b6cfed51ac011df8877cf5e24f00828e8be	271.1.37	wilmer-gaast.net
-20100318000716	78d254f110d47f9e0c3a8f12259f93a4faa2130d	271.1.38	wilmer-gaast.net
-20100318003038	b8a491db597ba2d82cc8eddc727509197747f0d5	271.1.39	wilmer-gaast.net
-20100318112217	842cd8dbfb98b61af33b5fe481364c3cfbeaca04	485.1.7	wilmer-gaast.net
-20100318143806	638feab58aebc97d646820dd1bc9b8d9fbeec29d	519	wilmer-gaast.net
-20100318203433	5605be9f7f8571350574adec2e9668b44c29dd8d	520	wilmer-gaast.net
-20100318221852	84622397b1780e4afa7bd36f0d2f089398ec3597	521	wilmer-gaast.net
-20100318222246	5fbec3ddda0e3dcf2c300926978b9b363a77a0f7	522	wilmer-gaast.net
-20100319000950	0baed0da940c0d82280a5674d7fa8ad06d449384	523	wilmer-gaast.net
-20100319164446	f0493b1c23879f88daad689a7d6d5dcca4247dce	0.1.433	vmiklos-frugalware.org
-20100320135627	21029d0fe2b7b11512d2cc67495d590e9df53f85	523.1.1	wilmer-gaast.net
-20100320172723	e5a8118236442d51eb202a8bdebe5866449554e4	523.1.2	wilmer-gaast.net
-20100320180318	81ee561d520e38535fb6947ac0e3fba808e6de4b	471.1.39	wilmer-gaast.net
-20100320215804	ffb6dea650db7671d2414b1a9541cf0baba8ff11	523.1.3	wilmer-gaast.net
-20100320224259	bc090f0c3bc243de277a8e04f906384838d95e35	523.1.4	wilmer-gaast.net
-20100321003945	699376f7c3b3d6aff18af0601fa1f1ac6c5a2892	271.1.40	wilmer-gaast.net
-20100321004618	54a20149778028bff730511c6cc8027f61634124	271.1.41	wilmer-gaast.net
-20100321010203	aed152f005a70e04f7d833dc4fb468e400e54fb3	271.1.42	wilmer-gaast.net
-20100321101828	545d7c058d0604dd6acfa37c68e9867e72f25c2e	524	wilmer-gaast.net
-20100321132020	2e89256f2265676d5d37b92433352ceccf079a7a	271.1.43	wilmer-gaast.net
-20100321151007	4ed9c8c11adb8d1010ed4bb7b3ff9af962f91237	271.1.44	wilmer-gaast.net
-20100321155659	a81d679654e36055ce9913cd7541885b41380947	271.1.45	wilmer-gaast.net
-20100321160631	767a148faa35c18cdf4da77b5919a2f6e2df868a	524.1.1	wilmer-gaast.net
-20100321161724	85693e62c8e847ee0336419c3f229bb5caac29a0	471.1.40	wilmer-gaast.net
-20100321165222	1cc0df34f742f93f995b68210de3d1f2eac2b5ac	524.1.2	wilmer-gaast.net
-20100321165720	0cb71a67163cbca49827855a4a56642f919330fd	471.1.41	wilmer-gaast.net
-20100321213842	437bd9b726339c44aa1a048cd84c2539bfa6cab5	471.1.42	wilmer-gaast.net
-20100322012040	c735200e7727a7b17161c2a205ba6639d61e9b54	471.1.43	wilmer-gaast.net
-20100323010625	edfc6db1415558b7f202cc3fa2654ad58defea78	471.1.44	wilmer-gaast.net
-20100324171253	ba7d16f3c90de2744243efe6373ccebe51cfcb5a	524.1.3	wilmer-makker
-20100325213127	62d2cfb0b7b5e7f3eda9ca13b1877d3ad74fcd5e	485.2.3	g.c.w.m.mulders-gmail.com
-20100326121437	3ddb7477f51d3cf1632e2a8b6f7da4c0609a52cb	524.1.4	wilmer-gaast.net
-20100327015700	ebaebfe35c82460581fa6db518d8848996c9a0f4	524.1.5	wilmer-gaast.net
-20100327023908	4be823968d7f4cb1d11e4f6dda50ef606a0fd7b0	524.1.6	wilmer-gaast.net
-20100327030435	b9e020af6c6a88392caa9edd120fb576ec971430	524.1.7	wilmer-gaast.net
-20100327031102	63a520b8ce1776442f2f79528ddd23fb0de51f94	524.1.8	wilmer-gaast.net
-20100327033923	b95932eb5a897fd264f3762493285dd7037dccba	524.1.9	wilmer-gaast.net
-20100327123000	83e92bf7487623c10567502936ca727f3a4c104c	524.1.10	wilmer-gaast.net
-20100327123444	9b69eb789bdcb3667c4cc4ac74f3404ae3f60869	524.1.11	wilmer-gaast.net
-20100327143103	b91936398b8ada486e246f769f1f8b8836fa3f43	524.1.12	wilmer-gaast.net
-20100327151616	2f53ada73d7d43b538c157563ab5eb39b7592137	524.1.13	wilmer-gaast.net
-20100327173647	280c56a7b24dc08b35a1ecd98c8f4b61435d1100	524.1.14	wilmer-gaast.net
-20100327180538	74f1cdef999356e40e3fa3b6a2d89876b6c0c303	524.1.15	wilmer-gaast.net
-20100327181003	410bf6d47436e83a09378aac2a8de24709b83422	524.1.16	wilmer-gaast.net
-20100328024419	6761a40af82d8134ee9fa2ac1d2b34475d297ad8	524.1.17	wilmer-gaast.net
-20100328024919	3923003fb56f3b13eba1fa44c900175ae2574a1c	524.1.18	wilmer-gaast.net
-20100328030357	38ee0216a7058b0f227d1c32b288e041a397528a	524.1.19	wilmer-gaast.net
-20100329122501	10a96f44efbeb6af09e2728926ce15b6bda12131	524.1.20	wilmer-gaast.net
-20100330002611	c4bc92a42001a05a36678ae14f610ff3857be465	525	wilmer-gaast.net
-20100330013019	e3413cc741d2b0a82183f859d7470922bc581efa	526	wilmer-gaast.net
-20100401023225	81e04e162bdc4517b2f357fd16dfd76f68245464	524.1.21	wilmer-gaast.net
-20100401033850	d860a8ddf5039f7208bff4c179bc9fe1549da6da	524.1.22	wilmer-gaast.net
-20100402020350	e63507a356ac94085bcd00048b81d3ce2f27f287	524.1.23	wilmer-gaast.net
-20100402022945	fb117aee274bccfb6528288599ef81fe72191e12	524.1.24	wilmer-gaast.net
-20100402025439	231b08b07a2807881da1327408e33855370b7b95	524.1.25	wilmer-gaast.net
-20100402032541	f012a9f0bb363cfcbdb6f2d563254ffba26b9fc8	524.1.26	wilmer-gaast.net
-20100405001824	1d3915951bfbcdfa1a7829a4082e90e154d4a486	524.1.27	wilmer-gaast.net
-20100405003904	0b5cc72bc7b4192ff5b474b81038c299d03721f1	524.1.28	wilmer-gaast.net
-20100405010002	57c96f7be2511a0a50015512dc03a30ba0923862	524.1.29	wilmer-gaast.net
-20100405212507	cc7a15326cf57dd14ec99e876c1bd284e08a7693	0.1.434	vmiklos-frugalware.org
-20100405214458	12198acf942c3e4388c60f82f6d1b74b73502677	0.1.435	vmiklos-frugalware.org
-20100405225507	49a3c0247d53efb12859a2ec0e24e9d75ff4f3d4	0.1.436	vmiklos-frugalware.org
-20100406153734	65acff80414032c41c1cc5384054247fe1482feb	0.1.437	vmiklos-frugalware.org
-20100406172551	2abceca711403e8e3308213954b4477ceecd4282	485.2.4	g.c.w.m.mulders-gmail.com
-20100406235400	0519b0a42b5e0ed09f796a92aa7bd3b7d3f06b9d	485.2.5	wilmer-gaast.net
-20100407002751	d5690197326bad1090dbb9f6bfc95470b479fe6b	485.2.6	wilmer-gaast.net
-20100407004638	1014caba0ae2c737e35b8f51cafe77c1967e6b67	485.2.7	wilmer-gaast.net
-20100407021544	7815a2b57887751a7e026747b27abea04b13abae	527	wilmer-gaast.net
-20100407022755	33b306eaaa3e05cbc5d196d0d2f0b741ff11a9e6	528	wilmer-gaast.net
-20100407023920	d4efddfb7d34a8409cf78dd337f3933e0ed11d08	529	wilmer-gaast.net
-20100407035901	3e5766022e8103765d62343956cf1aeba34b4d82	530	wilmer-gaast.net
-20100407105324	ac423d039d49ecd74d70e108e1764e132dc8d1de	0.1.438	vmiklos-frugalware.org
-20100407182704	7825f588d81546015427629fef5694b8ff924d09	0.1.439	vmiklos-frugalware.org
-20100407194039	123cac724c5907365e0c4bd939806cc240e764f0	531	wilmer-gaast.net
-20100407213856	91cec2ff02f956ec248dae6c8b8939f263ff8cfd	532	wilmer-gaast.net
-20100408002742	2e3a8576d6ca511df347426b4319bccde1871d06	533	wilmer-gaast.net
-20100408004211	08579a1e871216a7bd7e50315894b6aeed6354ea	534	wilmer-gaast.net
-20100408005116	37d84b32ca7f02f2e3b05858e090e2470b8c479b	535	wilmer-gaast.net
-20100408005517	5b9b2b6413d66df01a866205af489eca9f8ea308	536	wilmer-gaast.net
-20100408215627	92a9c686a1d2f5f98bfaed97333c9e856ec70166	537	wilmer-gaast.net
-20100408221638	7a90d02eede836f60a119cf516b145c8bf601d3c	538	wilmer-gaast.net
-20100409004038	cca06921729ecd1ab4beaecfef001a218e5d0010	539	wilmer-gaast.net
-20100409011110	16592d8422bbd7acdf39d29525e580fb82d47c36	540	wilmer-gaast.net
-20100409231638	9bf248155cb870be9dce921d58c905f5a5c1dad3	525.1.1	wilmer-gaast.net
-20100410010539	bb839e8ae5b6228f9dcd8dda96b4e3ac5c0f63ba	525.1.2	wilmer-gaast.net
-20100410022750	1f92a5851e0e3b1730e940980f2b0122c506c724	524.1.30	wilmer-gaast.net
-20100410100711	4fca1db5144a17f9313db9ee4103182d2c0f22e4	541	wilmer-gaast.net
-20100411143706	17a6ee93f4fbefe8b4356d884fdd95f4e72ce8cc	524.1.31	wilmer-gaast.net
-20100411181225	3ab1d317831a6c1830bb648a1a8d63a41c92f651	524.2.1	wilmer-gaast.net
-20100411181319	a87e6ba665985301eedbe981a81110846aad8a9d	524.1.32	wilmer-gaast.net
-20100411191012	e00da6322b3a78509d83e8f2c01cfec90465eb9f	524.1.33	wilmer-gaast.net
-20100411193845	dbb0ce32e607dbcd6c5ad4d5ab309e969afd6fd4	524.1.34	wilmer-gaast.net
-20100411195327	ffa1173a0ea836029bae1f41ec13c64b39bfb6a4	524.1.35	wilmer-gaast.net
-20100411221319	ec2ebcc07f4df02aee43712dae1245eb34c48575	542	wilmer-gaast.net
-20100411222217	824084011a9f3740d074957f7f769ab6f3547f79	524.1.36	wilmer-gaast.net
-20100411222645	57119e85387ba80192ccf36756c71b4dbb7947cb	524.1.37	wilmer-gaast.net
-20100411223115	d986463c9e026b2a5f003f44db3105aa5449fc27	524.1.38	wilmer-gaast.net
-20100411224932	eabc9d2c1b1d29aeb47162da64ce2b607c3d43ff	524.1.39	wilmer-gaast.net
-20100411230424	6c56f426fb2823ea6b41d1c9028448b7f8d5db09	524.1.40	wilmer-gaast.net
-20100411231803	aa44bddd276f047edd39fdb3eadc9b1e87f0014f	524.1.41	wilmer-gaast.net
-20100411233143	e21c0f8b276cc3ca177bcf6217eba9c634d410f7	524.1.42	wilmer-gaast.net
-20100412000649	24b8bbb2616d685006a279e46a4bd2e8e7cf6694	524.1.43	wilmer-gaast.net
-20100412153703	32917009b05d4302c9e2dfe88841dbb3f6a32d72	0.1.440	vmiklos-frugalware.org
-20100412153733	e3781b931db87a3ea03ebed6b4818cf6c7d55b98	0.1.441	vmiklos-frugalware.org
-20100412153733	7cc2c1ef13822cf37cfe82cbea03127b74d546a0	0.1.442	vmiklos-frugalware.org
-20100412235455	89c11e735164b7212d27bb64ff1a00ac50b9c746	524.1.44	wilmer-gaast.net
-20100413000455	7b59872d86160099968d0f8cdafc2853b074d39b	524.1.45	wilmer-gaast.net
-20100413081108	3a9b1236ab5e089b7413486d7d6bf61274a61d2a	524.1.46	wilmer-gaast.net
-20100413102004	573dab069d2c35910b3cdede3374a5749cb20a89	524.1.47	wilmer-gaast.net
-20100413113841	81186cab101fa8c2f82137014d0b3c060b658cb0	524.1.48	wilmer-gaast.net
-20100413125105	3e69802454295ffbfbbbe9bca4fcd226b5b63b28	543	wilmer-gaast.net
-20100413173616	3bd4a9327bb2c7c662e6d385b3bf6903ca8ca09a	544	wilmer-gaast.net
-20100413191128	3254c12328517b8b4bc12d8ef412c6b878f7b28d	545	wilmer-gaast.net
-20100413220940	37aa3172c7e9ec6ba7b5985ed60c41c621e8e7b9	546	wilmer-gaast.net
-20100413222036	40bc82df007cb78b155a098dae86b1d5bc7cda9f	547	wilmer-gaast.net
-20100413222540	3d1481f0c57ade74fb1d888bce8ce4e8e17bc269	548	wilmer-gaast.net
-20100413223555	542e44ac43d901c9ad70b7f89dde47eff4d9d8db	549	wilmer-gaast.net
-20100413225542	72b60c7e7d3e109a19aa8d856634f0213a6cedb8	0.1.443	vmiklos-frugalware.org
-20100413225744	c0417e80a965d74944260752f4f58378a4cdd8fd	550	wilmer-gaast.net
-20100413232602	304aa339fdb9078731becfc27aed0bd6ac592198	0.1.444	vmiklos-frugalware.org
-20100413235530	156bbd7b66cf29220c2ff6a86217c4dec5e33765	551	wilmer-gaast.net
-20100413235836	4c674bbbee33a85516a3975e3c5764cc1d01905a	0.1.445	vmiklos-frugalware.org
-20100414001604	7666c873a916ac06ac6926189d2e06ca8d208b43	0.1.446	vmiklos-frugalware.org
-20100414002014	b8c56a847b5d2ad63adba9aacf446401db0041ce	0.1.447	vmiklos-frugalware.org
-20100414092750	21c87a78f1c01fe24610a5d05a65e52bd8eaa796	524.1.49	wilmer-gaast.net
-20100414121712	d33679e4ffd9f36f14f677553b56f9b8ad72dd0d	524.1.50	wilmer-gaast.net
-20100414123439	d7d677dac118a1240ada9ddcc8617c49293e5a5e	524.1.51	wilmer-gaast.net
-20100414133541	003a12bd2361cd1ce4d83eeaa1b81d95101ea778	524.1.52	wilmer-gaast.net
-20100414190823	f9ed3113c4bc5110171295abef9c140e1328aeb1	552	wilmer-gaast.net
-20100414224509	4c3519a8e9f8733577b0ca060a80606955f92cce	524.1.53	wilmer-gaast.net
-20100414225619	e7edbb75b23e200a8b6a8e766ede7af319a75dc9	524.1.54	wilmer-gaast.net
-20100415231010	e88fbe272c097066e538d670fe8fa907e9847321	553	wilmer-gaast.net
-20100415231536	55b1e690c595ac6b027cf4bd1465c6676214bb50	554	wilmer-gaast.net
-20100415233441	b6190ca72d44523bc775ee3fc89b4bdbffe2cff4	555	wilmer-gaast.net
-20100416095822	efbc154f93aeee89877378e8433a035f0a8febb2	556	wilmer-gaast.net
-20100416234457	0f8423414dffedb0a9e8e1e9a041cac356808911	557	wilmer-gaast.net
-20100417224355	a897467549fc75eee8fdd7c255ee5f55c3714ac6	471.1.45	wilmer-gaast.net
-20100418024408	7cf146f37ae8fbc071c255cf20eb14e82186daf3	0.1.448	vmiklos-frugalware.org
-20100419092649	57400f4283fa232d31d14b2c0edd44d1c01cb8b8	558	wilmer-gaast.net
-20100419094032	f4bcc223fea70de8555bbc4d2caf48e0476c0e13	559	wilmer-gaast.net
-20100419130834	db57e7c0d4a88e2a2b0862afa20a1255dfffc58d	560	wilmer-gaast.net
-20100419222533	1aacc0c054c7ac90e15b80a85f239e843cf01bcc	0.1.449	vmiklos-frugalware.org
-20100419222719	0d7bd890f9016c93fb82d87f22d96fccfd58d125	0.1.450	vmiklos-frugalware.org
-20100419224058	3518933a59c27bf8d68ded788cba24012e089604	0.1.451	vmiklos-frugalware.org
-20100420230301	9d4352c51300548b4a053dab85517f0dd0cb386c	524.1.55	wilmer-gaast.net
-20100420231132	2272cb30851366324e844681b97c383c655ccc33	524.1.56	wilmer-gaast.net
-20100424115108	dd0dcac7a5814a4358e4a11388bd9155eeca9c53	561	wilmer-gaast.net
-20100424141554	f1b7711f566163ff27a8f13ae3ccc7214a24fe70	562	wilmer-gaast.net
-20100424165734	b5b40ffd38e315223c6e38e4a291cbd58e471062	471.1.46	wilmer-gaast.net
-20100424170207	ae3dc9996f5a678d6364005cab1517b6324eb67a	471.1.47	wilmer-gaast.net
-20100424170428	c5213622819e578c577158be6b0610ef9486fe45	471.1.48	wilmer-gaast.net
-20100425125620	0f64ca78c8ec9ee9be2ffa8ec203e138f7a00804	562.1.1	wilmer-gaast.net
-20100425151213	be28fe7da3e197d91ec00a6c1017c053041f5a85	562.1.2	wilmer-gaast.net
-20100425185653	e9eaee6eb50cd566d699b61a4d643f64800b488b	562.1.3	wilmer-gaast.net
-20100425185706	da2efd4eaaf6a87afc35402d96d98d8001e43af7	562.1.4	wilmer-gaast.net
-20100425192202	346dfd940c1439f6113c3cf80340791567de5d25	562.1.5	wilmer-gaast.net
-20100425232100	b2bc25c4483f4c2b419d91d479f38c3f07ef4226	562.1.6	wilmer-gaast.net
-20100426004237	508c340d1d12d3ca932001d7ee1dea1a4c81bf02	562.1.7	wilmer-gaast.net
-20100426212009	acba168df90f28fdaee26bc09d621364878dd099	562.1.8	wilmer-gaast.net
-20100426215048	713d6111cd754ff9eae4cfa1e6de82c9824d16db	562.1.9	wilmer-gaast.net
-20100426224011	c42e8b907817fc76df4dc3b48d85858555102654	562.1.10	wilmer-gaast.net
-20100426224735	78a2f1e4ac9aeb2c041059eb7a9578d34bd60d50	562.1.11	wilmer-gaast.net
-20100426225025	288b215ca64d09ea6a49cf9ff1fcc7682b7607ec	562.1.12	wilmer-gaast.net
-20100427221111	ee84bdbc2510fa09bd0f67e14d06e69c2626d0f1	562.1.13	wilmer-gaast.net
-20100427224207	18dbb20242719f7537ad1a18a9a3befa2eefd502	562.1.14	wilmer-gaast.net
-20100427224958	0bff877cf81754283eac52ee49fedc3872cd0d4c	562.1.15	wilmer-gaast.net
-20100428073825	3f668e478bbaa062cf6b01e627cb39885200e5ff	562.1.16	wilmer-gaast.net
-20100428074445	23784065e6e4de2e77bd1793a92ed3dbb73b5203	563	wilmer-gaast.net
-20100430225118	a7c6d0e9a9d985534dc18d450399d6a7ece6c592	564	wilmer-gaast.net
-20100430225329	85ef57f94436f23447c0d8603b52977824381854	565	wilmer-gaast.net
-20100501135359	c2ecadc08daa5163f4c90aef36de0e33d0d44f16	565.1.1	wilmer-gaast.net
-20100501141032	f4b0911d037c02f8b9190518b5efda4368dcc25b	565.1.2	wilmer-gaast.net
-20100501141626	4273158e2bd219602e502d593040cccb234d4805	566	wilmer-gaast.net
-20100501141909	a4cdf43ba4247914985765ef26a1d77239a15a10	524.1.57	wilmer-gaast.net
-20100501235548	f4850088ea8527d8cfd46dedea678bb0d5d93471	471.1.49	wilmer-gaast.net
-20100502154901	a348d00feb203641a3c2e8d2e3596f86d4bdbe0b	471.1.50	wilmer-gaast.net
-20100502155318	8ad5c34dfc1eff0112f73414fd4b566fae0c9ce3	471.1.51	wilmer-gaast.net
-20100502160341	15794dcafac99b2be1c400bc54a510fe61c4ebac	471.1.52	wilmer-gaast.net
-20100502161414	bda2975f43df2fd2409243082f6d98dc8c9e6fde	471.1.53	wilmer-gaast.net
-20100502184826	bce78c8e6b9363175943a1b10df76fdbd87ba0c8	524.1.58	wilmer-gaast.net
-20100502210625	839189b091ee82937666bcaa328a7d3ecd1c573b	567	wilmer-gaast.net
-20100502212009	3b878a1c3d0888b0669c30433efab914061fa61d	568	wilmer-gaast.net
-20100502220046	6824fb355a31ed46c22740ef184332440d3981ce	569	wilmer-gaast.net
-20100502234433	e54112f152c375df81a21181f755ced5f57165bc	524.1.59	wilmer-gaast.net
-20100503003939	6a9d068e73b6d08056302fdc85dd706a2dd647a5	524.1.60	wilmer-gaast.net
-20100503005208	f92456381b2a1487036848fc4e0105b8a439facd	524.1.61	wilmer-gaast.net
-20100503115506	1a3ba05edfd8228908999d7c589f039513558539	524.1.62	wilmer-gaast.net
-20100503213643	999769119e85518cc46b3ed64cb8781695fefbdc	570	wilmer-gaast.net
-20100503225815	0d4a068823e4a205c465f10a05ab699f0cef8e06	524.1.63	wilmer-gaast.net
-20100503235533	9893da32ab881e135748295fc5c48aada552098b	524.1.64	wilmer-gaast.net
-20100504084510	eb504957c2ffa1a3130951d5381672fb3ef9dfd9	524.1.65	wilmer-gaast.net
-20100504233818	94383231eddf56112cf74f2ae65d691821d70803	524.1.66	wilmer-gaast.net
-20100506002856	aea8b68bd0e057441d671c008200e71dd046a211	524.1.67	wilmer-gaast.net
-20100507234149	f1a089067fcc4fcae05cdbfa0f51a8a3cc8f6783	524.1.68	wilmer-gaast.net
-20100508002515	27e2c66f28bc196d766ac179aa5eae0d190565d5	524.1.69	wilmer-gaast.net
-20100508004510	b17ce85d2c4e69637531a7989b30c7011832ccb9	524.1.70	wilmer-gaast.net
-20100508004848	e4816eab28eff86f2303261f8ae292acd84212dd	524.1.71	wilmer-gaast.net
-20100508010212	a87754b68bb1eb07397d71a93ffcb0f3fc089266	524.1.72	wilmer-gaast.net
-20100508121323	bfb99eebd101fff1e15783c8fe4f00398c8052b3	524.1.73	wilmer-gaast.net
-20100508123749	d343eaaa2bf278a530de20a0841967e6e8759e96	524.1.74	wilmer-gaast.net
-20100508131109	9e27f1841b1160d3506a3c48701a661e86f2173b	524.1.75	wilmer-gaast.net
-20100508132136	b0364dc3b881e4b5e9afd1ff62d5b3cc792a87ca	524.1.76	wilmer-gaast.net
-20100508141232	fd45e1d1d63761a0bf18c7ad885d72acd6367746	524.1.77	wilmer-gaast.net
-20100508141912	4a9fd5f7a980831ee2c96a728f4f83137cfc73fe	524.1.78	wilmer-gaast.net
-20100508144838	eaaa9862451175392d6df48c4795b188518bed4b	524.1.79	wilmer-gaast.net
-20100508145832	e68565706f0c2ea710e7ea83cd5a69e538eb061c	524.1.80	wilmer-gaast.net
-20100508215225	66b9e36aa9dfd123c66194d645a3c60cc3dc49bc	524.1.81	wilmer-gaast.net
-20100508235012	aa7ce1b3dd961e948e907460bd42bf1592ea1717	571	wilmer-gaast.net
-20100508235437	eb37735451207895e7e1b5b3dcc0f9cbe178ad38	524.1.82	wilmer-gaast.net
-20100509002838	36577aa5efb2ef3daafd17f9ad179fedef28278e	524.1.83	wilmer-gaast.net
-20100509004003	5a599a1550c670649dba681e702864d55d2e3795	572	wilmer-gaast.net
-20100509004054	75610c3b53a68451d9eaf40fdc8a5e6419a13339	524.1.84	wilmer-gaast.net
-20100509094856	bd5eee34fd1495820fb8440d515cbc86e8d912b2	524.1.85	wilmer-gaast.net
-20100509112657	e5abfd413a797b268db0b107d0748eb7e40da431	524.1.86	wilmer-gaast.net
-20100509120550	47fae0ffdda6ea4509ba00b56d15fb25ffe19eea	524.1.87	wilmer-gaast.net
-20100509130445	5a673f32c7bdf10cec2e0ccabce605ec9c12859e	524.1.88	wilmer-gaast.net
-20100509132124	dcd16c5f8b8788d476bf4193701fc61656dfbf14	524.1.89	wilmer-gaast.net
-20100509160619	7ee07c3191d1eb21d1d8317f7bfc3d9d98359ab1	573	wilmer-gaast.net
-20100509172051	9ac3ed11de72046c318398481603c6680af37cf2	524.1.90	wilmer-gaast.net
-20100509180555	7aadd714313ba3e966720e7565f72118b97bd551	524.1.91	wilmer-gaast.net
-20100509213931	3130e7074e567070fcc7be627a3836fa3f213142	524.1.92	wilmer-gaast.net
-20100509215217	13c1a9f1bbf57bebbab621de609581c4fad54fb2	524.1.93	wilmer-gaast.net
-20100509215639	4e608d6fee8ee39b871338524b6da00aa5a6e86b	524.1.94	wilmer-gaast.net
-20100509220830	a067771fa1799e689653931f6743b3502182b850	524.1.95	wilmer-gaast.net
-20100509232334	d8acfd3ba84e018554d8564f08e9a50cde56b4a4	471.1.54	wilmer-gaast.net
-20100510090526	4c17d19ddb4a039e3ff9e33e87d5f538a0b0c19b	524.1.96	wilmer-gaast.net
-20100510233558	e73a501ce0cf49d8073d7d25656561400f2aca79	574	wilmer-gaast.net
-20100511213337	f8cb76dea3554fd82c01c5cae61f35d2b6cb0936	575	wilmer-gaast.net
-20100511232711	3742fb6ae99c2e5e25cff272a154b9834866e8f9	576	wilmer-gaast.net
-20100513001933	3663bb3ee9bc20d83642103f03a53831caee454d	524.1.97	wilmer-gaast.net
-20100513002228	6fd4d46eb854902391549de95774260a94ae7072	524.1.98	wilmer-gaast.net
-20100513003036	58f5ef70c3824b881ad6b35f854a8c8ac59a5d32	524.1.99	wilmer-gaast.net
-20100513220513	3429b589177f286384dab151c167e16071f8a828	524.1.100	wilmer-gaast.net
-20100513230507	ca0981ad1e427644a33fc31fe78d63ea834f0fa0	471.1.55	wilmer-gaast.net
-20100513233438	7b71feb6f88b7e14199b8f7e9930f76d5324e356	524.1.103	wilmer-gaast.net
-20100514000929	6e6b3d7c7300c5cf5cf7f1538154925fd2fe3953	577	wilmer-gaast.net
-20100515002033	f8ec8903b66fad1355f90316bf46b7ca2be43762	578	wilmer-gaast.net
-20100515115309	df1ae6223a5fdb3c18d5438670834285101c6213	579	wilmer-gaast.net
-20100515153243	6be46b37a167031f7a6888be03c2611f0762639a	580	wilmer-gaast.net
-20100515232816	ec86b2232da72f7b0c1c0a217d94b6e15698c795	471.1.56	wilmer-gaast.net
-20100516094627	d4bc2d90d88527f434f910b9821c145394434d71	581	wilmer-gaast.net
-20100516095057	2334048eaa9ab26fcdab781dbfb1bacb1c01ba8d	582	wilmer-gaast.net
-20100517001414	2309152972f1d52af6adbb0e7e5d9c5ad3ae80f9	471.1.57	wilmer-gaast.net
-20100517203045	553767cacf43e1a4a1038530c47dc0af5fdd0369	471.1.58	wilmer-gaast.net
-20100517232320	8822d23385bf7c35d67eaff1d0ce81470ba73f4f	471.1.59	wilmer-gaast.net
-20100517233839	5d1b3a9529f7aadab62026be0eb9f4ca0f6311ce	471.1.60	wilmer-gaast.net
-20100518000817	c96c72f16ea48ca769400ff91bd2eb434da19f6e	471.1.61	wilmer-gaast.net
-20100518004202	31fc06fbb48b6217186ca441eb9b95add5f783ce	471.1.62	wilmer-gaast.net
-20100519005758	e7dc02a89d846d27b63719a5093c2e2a295fc232	471.1.63	wilmer-gaast.net
-20100519194643	0b3ffb13172c211eed561288d0fd04d76506bb02	583	wilmer-gaast.net
-20100519200133	907afe184215a7ba15b09ef5f130768703cd89bd	584	wilmer-gaast.net
-20100521000929	75c3ff711698ea069f33ffc460c2e7aec650e875	471.1.64	wilmer-gaast.net
-20100521232621	2c5fabcf9066752eed45ad0334fca232a7123629	471.1.65	wilmer-gaast.net
-20100522005859	e77c2647c3f4d8d6518239f070f3989444003a08	471.1.66	wilmer-gaast.net
-20100522010558	dca8effbf732557e28a6a03d2fcf785d69a5a1bd	471.1.67	wilmer-gaast.net
-20100522122127	05a8932daa3e8f6d3dd4c5177fa04d1f17254000	471.1.68	wilmer-gaast.net
-20100522134625	c3caa46bc5c5ac0a372a13c5fe0de79845a7dabf	471.1.69	wilmer-gaast.net
-20100522150036	c01bbd100fb460e34e10635ad43dc0f8f05b535d	585	wilmer-gaast.net
-20100522234400	f9110b483d89b6dbc15d980f8b431863e8a872f3	586	wilmer-gaast.net
-20100523125051	bb5ce4d1ad2aa6cbcf1042ccf87cf280d9645d4c	587	wilmer-gaast.net
-20100523134954	a7b9ec7011cb649be3c34ec681ab98e103dfc686	588	wilmer-gaast.net
-20100523141416	186bd04be06297c1dd480f730eef8fcede9a3991	589	wilmer-gaast.net
-20100523143130	228fc188af4cc292a14672abe519d557d57bb1e6	590	wilmer-gaast.net
-20100523145241	3d93aed32d49bae6c608a71858f00127dccd28e9	591	wilmer-gaast.net
-20100524212453	f85e9d6846d7b4ec0109180e62ae8677e2421fa3	471.1.70	wilmer-gaast.net
-20100525220455	d25ebea73aef7d6b2bbc17212af5109383277e6e	471.1.71	wilmer-gaast.net
-20100525220706	51a799ef9c2a144bf3edc58130bfc1e275cdd53f	471.1.72	wilmer-gaast.net
-20100525222654	f60079b74053a53da3720b992c603fddf75ff1dd	471.1.73	wilmer-gaast.net
-20100525224152	4af305032ba6913449cbd9160db1b6ab228f0a9a	471.1.74	wilmer-gaast.net
-20100529124017	7d53efb7ec5310b2710757cef03d7f53c94a7797	570.1.1	g.c.w.m.mulders-gmail.com
-20100529133612	dbbf34ac084a54f8aac36952050d5d8bb818d884	0.1.452	vmiklos-frugalware.org
-20100529133855	31a35ea3c80355fbecb07c53e8f638da46d8eef9	0.1.453	vmiklos-frugalware.org
-20100530101637	ba3233ce0b1fc34f3ad0a450bb1b661909dafb79	592	wilmer-gaast.net
-20100601215127	704dd38a1440dc9d614df9222d6196048226622c	524.1.101	wilmer-gaast.net
-20100602084718	3dc6d86076dbea16c313bb87aa2f37166f289a8e	593	wilmer-gaast.net
-20100603002053	ad404ab26aa3cfdfc3c76f6926e556e333d02753	524.1.102	wilmer-gaast.net
-20100603103146	3f81999c20852f14a5fb27a6ef6c5ea44db61a4d	315.1.58	pesco-khjk.org
-20100603104103	5f8ab6a9adf09ea7c07f728227bdb6d3953588f1	315.1.59	pesco-khjk.org
-20100603110045	814aa52228c7cad1d036c1a6dc5ea03cce61c048	315.1.60	pesco-khjk.org
-20100603161846	a6b2f13e38e75e7bca00a1e6c1963783f244c0b2	315.1.61	pesco-khjk.org
-20100603211357	bb09b3c6b8190be09e0b7c7ef2f4b1b5a69b504f	315.1.62	pesco-khjk.org
-20100603214753	1dd34701541fb8142402f593dc256ff9b54121bb	315.1.63	pesco-khjk.org
-20100603220823	37598495deeb42e1cd216a0a4ba8068e4a6da453	315.1.64	pesco-khjk.org
-20100603221711	4469e7ecb15f57151452c59040360a483e4f5ee9	524.1.104	wilmer-gaast.net
-20100605002012	2b8473c72b730085c8d6aef3df8fef55c7002080	524.1.105	wilmer-gaast.net
-20100605003517	7e83e8e47ff1ba031928e1e46bf16ec53ca53117	524.1.106	wilmer-gaast.net
-20100605010626	c133d4b82b205b515e0c43be9f9b92ac3dbcd9ce	524.1.107	wilmer-gaast.net
-20100605010704	cf1a9790b4b933f7727c1362285f529f45a755c0	524.1.108	wilmer-gaast.net
-20100605103202	04dc563f2a30476bf96e72ce3e66cb5a8b2606d3	471.1.75	wilmer-gaast.net
-20100605144754	095a5f046c36c4cff689305fef81533a9e9603fc	471.1.76	wilmer-gaast.net
-20100605163958	4c0388134ff88b72e343bd07013554480c2a4ef7	471.1.77	wilmer-gaast.net
-20100605173539	477250038a91c6877838cbd7f0d32b839f64824d	471.1.78	wilmer-gaast.net
-20100605174338	e774815bc621af90bb64ca314b84367659c5a005	471.1.79	wilmer-gaast.net
-20100605182617	f5d87ea5e4db1864cc9dd95c295f166af9944014	524.1.109	wilmer-gaast.net
-20100605223236	5a75d1586478f78446b6c78b161572fc7cabe4d9	524.1.110	wilmer-gaast.net
-20100605231652	c1a8a163703a903c7a69e06286013f12d6bf865e	524.1.111	wilmer-gaast.net
-20100605232102	b308cf9bafbdf76da73a57607b65c4763aa3057b	524.2.2	wilmer-gaast.net
-20100605232607	1fdb0a48438d6dc4a4795d195737890ed3e46a96	524.2.3	wilmer-gaast.net
-20100606003333	18da20bf7690ca3c1e9cf4b70c2749a11c75b339	524.1.112	wilmer-gaast.net
-20100606011113	1f0224cdfd238060810679b3d6ba1a2bc49e4493	524.1.113	wilmer-gaast.net
-20100606012413	88eaf4b49855a8069fce79296f2d271b6c6c654c	524.1.114	wilmer-gaast.net
-20100606013045	16834a53f85be092acc16dee70cb72451b2230f9	524.1.115	wilmer-gaast.net
-20100606095507	bc49ec26b131054d8de5941e49cfce7e026a3941	524.2.4	wilmer-gaast.net
-20100606190114	560d0a0c4cc61e8ed68a80a04491af024254684c	524.2.5	wilmer-gaast.net
-20100606215846	f277225d33d93d228ce1c953e050331c501e646b	594	wilmer-gaast.net
-20100606234746	92cb8c4c251fb04a483b29e7108e7a52388f14dd	524.1.116	wilmer-gaast.net
-20100607004445	36562b0f1a82bd2a5e9a24a7091845847e4242b4	524.1.117	wilmer-gaast.net
-20100607005807	0d9d53ed0b3eb068cf57355a4d1465beaf191f8a	524.1.118	wilmer-gaast.net
-20100607143107	4aa0f6bc5645e124738ab15ad1eb65d4147dba25	524.1.119	wilmer-gaast.net
-20100607151109	56699f009a608ecff3a247a08b3d0105a5e17153	524.1.120	wilmer-gaast.net
-20100607152121	0e8b3e855dfa370fe559729224b3bff1d4cf5b87	524.1.121	wilmer-gaast.net
-20100607153953	c5aefa4463135b3f8720785804fe183817829b00	524.1.122	wilmer-gaast.net
-20100607154542	70f69ecc5f80f060d1780110ed9792f9e19d2507	524.1.123	wilmer-gaast.net
-20100607155131	0a6e5d1fd4e0c33e0529db7f94aae66b3f995f84	524.1.124	wilmer-gaast.net
-20100607164811	a4d920bfc34db9ca9161ff3817385423e52b8c90	524.1.125	wilmer-gaast.net
-20100607184008	619dd1882deb7f507882748982744cc275dd92a9	524.1.126	wilmer-gaast.net
-20100607210933	6ef906557277b71fc278e3f612542bd4d7d75ab5	524.1.127	wilmer-gaast.net
-20100607230458	04a927cb733e2c47424569550a2faeb108094636	595	wilmer-gaast.net
-20100607235042	f1cea66ac5fcd860b2702e6b8ab01fcb79179bd4	524.1.128	wilmer-gaast.net
-20100608222216	d50e22f72987597152198811a22e50a97a902bcc	524.1.129	wilmer-gaast.net
-20100608224416	d7db3468f95d6b8ed2a161c71cb5b6ab1a7b5895	524.1.130	wilmer-gaast.net
-20100608234313	46d215d562f8e1aba2b24e2d1feab27337956d50	524.1.131	wilmer-gaast.net
-20100609003513	6acc0332708b8c844f721dbda9c7ecb94b8dd232	524.1.132	wilmer-gaast.net
-20100610163623	95c3ea9002af8f59a580f887b59413a0b924e1fc	524.1.133	wilmer-gaast.net
-20100610163917	52663546cc019ebdf4a7d56f1425408e20e1d4cb	524.1.134	wilmer-gaast.net
-20100611113439	70ac4771ebf3358d7bec9bd6fe37cde4c23223bc	524.1.135	wilmer-gaast.net
-20100611151227	1c8e5f7fba87b6096a7fd86508ca1821876abb54	524.1.136	wilmer-gaast.net
-20100612224012	12b29dbdce259ad7c65b4b890b5d9a1bd44b60b4	524.1.137	wilmer-gaast.net
-20100614082832	e5b521d07dbe197c2dd7552f0036bdcac2116cde	524.1.138	wilmer-gaast.net
-20100616083140	6b90431eba3820aaa5535523622ba45ca65055f4	524.1.139	wilmer-gaast.net
-20100622234336	7cd2e8a6c0ba091f56e6ee8bc087c799faee3662	524.1.140	wilmer-gaast.net
-20100623001346	4eef27179a98cc1dd478ee9ccd05f30e36ce43bc	596	wilmer-gaast.net
-20100623223724	90ba4161e5c24306233bc4627d1cde39a54006cf	597	wilmer-gaast.net
-20100624004315	d6aa6dd53c3352305508ffbf62d26f59dd19b777	598	wilmer-gaast.net
-20100624010649	38ff846b14f1d1f80983a0818b9bcf60581a2a34	599	wilmer-gaast.net
-20100624091434	8b8def5854857248f66b6a2580ce2e5a8f5597c8	524.1.141	wilmer-gaast.net
-20100624092734	051372c0219f067bb4b116bd06d482420b20e3c8	600	wilmer-gaast.net
-20100625183923	a985369ffb669e57341c88fb35ad05e151815378	0.1.454	vmiklos-frugalware.org
-20100625233330	41d415beaa1fd3bdd67a12686fc35386e5b81108	524.1.142	wilmer-gaast.net
-20100626212641	92c8d410eb1d26bfe876ae119734772f46c9a7da	524.1.143	wilmer-gaast.net
-20100626233931	e907683afea9e2789e0ac6a1eb55bda9c896c255	524.1.144	wilmer-gaast.net
-20100627123907	84c3a72604a292c41348d678eccf1875263cb8dd	524.1.145	wilmer-gaast.net
-20100627153212	547ea687b733113e06d7b941096b60632ac24de9	524.1.146	wilmer-gaast.net
-20100627160428	134a02cd563c395d0026d9d1b07eb136394798ca	524.1.147	wilmer-gaast.net
-20100628000746	1c40aa73b52e4507404c82056170069a859fb0cb	524.1.148	wilmer-gaast.net
-20100628001840	c7eb7719b4642b72669d280d4c24c54109ed0d28	524.1.149	wilmer-gaast.net
-20100628090339	ab6006cfec72ae8669c68f2892cf1c9994740c92	524.1.150	wilmer-gaast.net
-20100628093701	a670aeb34e4f3c4a1fd910aa5944fed4f9c20d7a	524.1.151	wilmer-gaast.net
-20100628093711	58646d9244557320ec811ee0b67519027170eede	524.1.152	wilmer-gaast.net
-20100629230607	64f8c425da20554e6909d969b8748076825ef601	601	wilmer-gaast.net
-20100629233005	bd64716b5687e6652ada7112a43ff3455e32231e	602	wilmer-gaast.net
-20100629234417	7a6ba50d4824c4e33ebf1154e97038c9331f4f58	524.1.153	wilmer-gaast.net
-20100630193042	217bf4e0401435dce8dec87d2644fcadc986fa93	524.1.154	wilmer-gaast.net
-20100630225227	8203da9d2f792252d2144e5e9063391a3ceebfe9	603	wilmer-gaast.net
-20100630230133	d7f850037c6288b5a2eebae789ed0247d0e5f7b3	524.1.155	wilmer-gaast.net
-20100630231544	9052bc147b30a08c8df6799df09b01f922c10be7	524.1.156	wilmer-gaast.net
-20100630233027	06f9548d5f3b0909c8cdbd092dd3118c661be67b	524.1.157	wilmer-gaast.net
-20100630234655	52a252173062bbce66040ef2b79c15dc2e2c03e6	524.1.158	wilmer-gaast.net
-20100630235646	3353b5e52e6d78b1d1a294cea6ef1f6d383e38ba	524.1.159	wilmer-gaast.net
-20100703173937	e1f3f94eaf2f18f4707dc48db9adc541f16d1679	524.1.160	wilmer-gaast.net
-20100703175731	bc4b4694d8ec852ac2e192b794bfcb7b62fbc3fc	524.1.161	wilmer-gaast.net
-20100703203853	917a83ee526b8e5baaf451888cd0a3149894697b	524.1.162	wilmer-gaast.net
-20100703211641	0bd948edfea280cf9f05e21cd5bef4b7fdf3335c	524.1.163	wilmer-gaast.net
-20100703213210	c35143409dfd96e4a8a5de32063e73816b1b8de4	604	wilmer-gaast.net
-20100704101607	c8eeadd72286b87cc1638e388669e00e8c8b9f1e	524.1.164	wilmer-gaast.net
-20100704133608	5c7b45cb652c73a8f2c827116786a1b21519d4b7	524.1.165	wilmer-gaast.net
-20100704143150	f537044f96de6b6553f042e2274252cb834680b3	524.1.166	wilmer-gaast.net
-20100704172505	8eb0b76410b42490028448d22a26647faba6b916	605	wilmer-gaast.net
-20100704172522	00540d40be63b4db537a661d1a17c49a1790f79c	606	wilmer-gaast.net
-20100704204015	006a84f999248d1bc1c1e36fa3437765d4bd1142	524.1.167	wilmer-gaast.net
-20100705120128	69b896b5967e5d13b1c60c68cb3bc7d4a0d5cd06	524.1.168	wilmer-google.com
-20100706214452	6c2404e051cb6a235f985797c149af0791f44bbd	524.3.1	wilmer-gaast.net
-20100706231017	0b09da0dbbdf294799bd5335b327c2601f263b01	524.3.2	wilmer-gaast.net
-20100708223101	9a9b520df6044cfc034f9736fb97660a46e879b9	524.1.169	wilmer-gaast.net
-20100708231043	f545372483cef6a2414bc5bf21260579151a627e	524.3.3	wilmer-gaast.net
-20100709082525	9595d2b9420afc1044c0d5ee93311b8c4ee6dec2	524.3.4	wilmer-gaast.net
-20100709210638	debe871bbd96a1d4215983265d97b8f0df7a4fbf	524.3.5	wilmer-gaast.net
-20100709232423	c5bff810e6919dc3daf7f82f761197a27f04538b	524.3.6	wilmer-gaast.net
-20100709232507	b556e46f9bfeeb630b45a3c0f0951110ac3de0f2	524.3.7	wilmer-gaast.net
-20100709233925	f3b6764f269490f11a8adbddeeb06964331917d0	524.1.170	wilmer-gaast.net
-20100710003039	534e4069e0cfa8aee82f82603dc3ca05562a85dd	524.1.171	wilmer-gaast.net
-20100710150802	f1c2b21af3a51796b747ca4dbc2cdec9611efc5d	524.1.172	wilmer-gaast.net
-20100711002919	e92c4f4f63ca0ba9ac6f959f7ff894ad2fc72a04	524.1.173	wilmer-gaast.net
-20100711103027	1e52e1ff518987092cfe94bc5c9c4479ed535019	524.1.174	wilmer-gaast.net
-20100711105956	af9f2ca883354a47635d130ff5e7bd693a200a29	524.1.175	wilmer-gaast.net
-20100711121127	133cdffff000275c3968b38e5e4cdde02dc400d3	524.1.176	wilmer-gaast.net
-20100711122159	b1f818bcbd50eccd416127ed68346616295f54cd	524.1.177	wilmer-gaast.net
-20100711172121	eb6df6a280c458546ebc57126e63a828674b4cd8	315.1.65	pesco-khjk.org
-20100711231449	2e0eaac657b09f0698bd5779142fad5fe9db9eb6	524.1.178	wilmer-gaast.net
-20100712202711	09dfb686be1297e9ff0a9b434ef865b779a60bc3	524.1.179	wilmer-gaast.net
-20100712232253	badd1484469ab6280de977eb179db00820868c03	524.1.180	wilmer-gaast.net
-20100713000940	db2cef1ab11cc58cc92c16fd9bccd0fe17e413e4	524.1.181	wilmer-gaast.net
-20100713001737	06b39f28ca532b21c9ddb1ce36c17d3f7121c388	524.1.182	wilmer-gaast.net
-20100713201346	a26af5c3cf58a5f896ee5af2061115e07a8eeb87	607	wilmer-gaast.net
-20100713225006	b1af3e8750e81869c8c1d2a12c9c27f6913aa58d	524.1.183	wilmer-gaast.net
-20100713231441	324c378cd2d3f208b000e1279d462665d163edb7	524.1.184	wilmer-gaast.net
-20100714231903	83e2d3048639533acd1d56672dcb1ba76de65e36	524.4.1	doug-saga.ees.com
-20100714235211	3709301b1559aabb3292e8c15bac14a85d8fc31e	524.1.185	wilmer-gaast.net
-20100715221547	e693ac27b08e60c325223a0797dbf49ed2dbc3b3	608	wilmer-gaast.net
-20100715221642	e4e0b3764761c8e204bfdf169d83af950d9e6340	609	wilmer-gaast.net
-20100715232304	7885d0f3dc255cd7900ee1d398366636c48bcda1	610	wilmer-gaast.net
-20100716231137	516a9c69222bed3d6fee7aefb439b461e4173da0	611	wilmer-gaast.net
-20100716231404	ef14a83adbb9036c0006ad460c5e11882a3d7e13	612	wilmer-gaast.net
-20100716233155	4346c3f4343d0cf67a7eefc381c5b10e15011ce8	524.1.186	wilmer-gaast.net
-20100716235553	e43736627ab8fa174352ba6a0122a6dade08a8d7	524.1.187	wilmer-gaast.net
-20100717100451	177ffd7da1570485698f6c105374e86c4471c94a	524.1.188	wilmer-gaast.net
-20100717113424	d0527c1845ed1230bbc3b0f94b8643cd8f9fddc3	524.1.189	wilmer-gaast.net
-20100717120831	a08e8752a64e12f16a9ddc7e583195c3388b8d3f	524.1.190	wilmer-gaast.net
-20100717122951	a91550c216d39b4063941a4d0831fe3a30186d2e	524.1.191	wilmer-gaast.net
-20100717140247	6ef19f7260711d9d28359492f196b0af4825882d	524.1.192	wilmer-gaast.net
-20100717142320	5e98ff07af5a8d89b7c813f55dfd0972fca11297	524.1.193	wilmer-gaast.net
-20100717143733	6f0ea5781ea4bd50324ee81758a9fd72ef780fcd	524.1.194	wilmer-gaast.net
-20100717144150	0d691eaca734902fb6c0f476ad2dbddb72e6ba66	524.1.195	wilmer-gaast.net
-20100717150656	ffcdf1329ad0401ace5a0355160ac7c249869431	524.1.196	wilmer-gaast.net
-20100717155101	5c18a7632da2507ee9f8e63fafa46061163e3e3c	524.1.197	wilmer-gaast.net
-20100717161109	fe4f28f593231f93fe4a2e6365edd45c301a6596	524.1.198	wilmer-gaast.net
-20100718144455	2bfe976616cc80439257959411c3bb20f2ab2e6b	524.1.199	wilmer-gaast.net
-20100718150419	f1d488e9d3f14e68e7df9686acf0840bf7307854	524.1.200	wilmer-gaast.net
-20100718151235	2efb69bed8c96842368c1d82263909c74582399e	524.1.201	wilmer-gaast.net
-20100718153158	3e59c8d79ae39d8c1412c2bbf8dced6ded74af6f	524.1.202	wilmer-gaast.net
-20100718201255	4f22a68c5d1dfd0d1da8b44c3a9d60a7754633b7	524.1.203	wilmer-gaast.net
-20100718221219	94d5da9cc78a89b6292d53c7784a3076500d67e2	524.1.204	wilmer-gaast.net
-20100718235027	6d8cc053c4b247ad721a0760b13cd383d758c2c5	524.1.205	wilmer-gaast.net
-20100719211813	5a61bf59dc372e795163dc5afa4cc91820f559f8	524.1.206	wilmer-gaast.net
-20100721003947	262bc7e0454346b81d28accb207289fcbbae029d	524.1.207	wilmer-gaast.net
-20100722074321	938c30512f4dac4f084fd6bb8b7f41655de9bce4	524.1.208	wilmer-gaast.net
-20100723143545	c36f73bd317dd55d7e70275a6425faa4be7bfd8c	524.5.1	wilmer-gaast.net
-20100724105708	9034ba002b8945aee0f905b928bb0f60da9afe9f	524.5.2	wilmer-gaast.net
-20100724111543	78e2eb7bdaf0ab00321f47b93b37b603ef32bdb0	524.5.3	wilmer-gaast.net
-20100724130400	ccc595b4b445bd8eab7ea454c6eebc3ff2e5a521	524.5.4	wilmer-gaast.net
-20100724132836	c495217e7c02c908d831645b033cf115ccdc3d6d	524.5.5	wilmer-gaast.net
-20100724132948	c8791f25459aeb32f770c46bd3a3613c9fd2d0e2	524.1.209	wilmer-gaast.net
-20100724140622	03f38289e15c27b93f8fdecf22a03353e4d01096	524.1.210	wilmer-gaast.net
-20100724141944	7989d40d5b9c8f05b38e7b07ab7e91335e717309	524.1.211	wilmer-gaast.net
-20100724154659	40e6dac45f29a4c2cc64ce55eecef03d0b185bd5	524.1.212	wilmer-gaast.net
-20100724155827	e135cd0997fb88ae644e63b6b7457ba08a60661a	524.1.213	wilmer-gaast.net
-20100724161027	0f28785760c8e625874d35f79c27eaa19785b809	524.1.214	wilmer-gaast.net
-20100724205941	593971d9ff9f246cec5af5583f29e45fee62edfe	524.1.215	wilmer-gaast.net
-20100724211618	2945c6ff5d1848f6d8e51a0d804a2d769e6894a7	613	wilmer-gaast.net
-20100724225023	f1f7b5e5d7f99419fc9b1a3d3ee3bce6e8602b10	614	wilmer-gaast.net
-20100724232633	8b0121711986494410bb9d224e90a5dab1dcc601	615	wilmer-gaast.net
-20100725085425	42acba19c3d3911a157cda31b18508e1d40680d4	616	wilmer-gaast.net
-20100725085617	5588edf5779d449f0f1ed1b2ef6f2af510d92632	617	wilmer-gaast.net
-20100725091148	51a3d12c3187bb0af530bbf36cf8619b57d0ea1a	618	wilmer-gaast.net
-20100725135912	c6bf805c65caefdc20fe3c7bcad63bf3c4660fb8	619	wilmer-gaast.net
-20100726230323	3fa5a8cf5a5784888030d1072c9cc9a0f8a59335	620	wilmer-gaast.net
-20100727102747	a7dbf453dacf12fe27f361043057caba19b5695f	621	wilmer-gaast.net
-20100727174516	82ca98619fc6fbef41de7235b5cc930961ef0cc0	622	wilmer-gaast.net
-20100727220419	f6f5eee77be1a91563da38337bb80b04cb2ae071	623	wilmer-gaast.net
-20100727221827	42553208d714b80a2a64d334f1659ec379042f8b	624	wilmer-gaast.net
-20100727222459	13fa2db53edbeae0d4638db1de042b8f415f8871	625	wilmer-gaast.net
-20100728082447	3b3c50d9c6709c56c34bbbf9d94717485e2eb04b	626	wilmer-gaast.net
-20100729071957	3963fdd2bc0a8b1a3665b2dfdab574cf53bf071d	627	wilmer-gaast.net
-20100729085701	b40e60db39f0b187774cfd2e0fe1b503f9bf1a54	628	wilmer-gaast.net
-20100729170816	2fe5eb939ff77697b578bf45ba23cd99daee9c5f	629	wilmer-gaast.net
-20100729181854	f7ca5877b69d452ef2e2ab4dc4d06743072deca0	630	wilmer-gaast.net
-20100729191757	1521a85744f1e5a92dff584c861d6da249b86b95	631	wilmer-gaast.net
-20100729193047	b9256662a7ef5660b35ba51bdc8d798befb871f0	632	wilmer-gaast.net
-20100730142822	8b6146945853c38723b3e80ce08fc1bd78a34189	633	wilmer-google.com
-20100730143336	8f984a0bca04f222dc0f63fe84974a3391a6e3fe	634	wilmer-google.com
-20100802193731	3063f8185b354358362665a90967fa15c5407f2b	635	wilmer-gaast.net
-20100802215424	8fcb1967812f90316f45bc7ca461783385dbd9e2	0.1.455	vmiklos-frugalware.org
-20100803111259	78d22cd0d649ab9bcf93344bc7c10924583018a1	0.1.456	vmiklos-frugalware.org
-20100803111355	3adc2470f9893488ac4092fdb6d33e865fba6930	0.1.457	vmiklos-frugalware.org
-20100803115708	3d88f65fb2ff3275bac3eabd124d385a1ed346b2	0.1.458	vmiklos-frugalware.org
-20100803115748	f5ae4a100adcdbf718ca3e7f7183c7fd9ed05914	0.1.459	vmiklos-frugalware.org
-20100803123004	4ab72258f7956f52c3b4750d192ad078dd175973	0.1.460	vmiklos-frugalware.org
-20100803123131	32ba37166daaddd2b39839d3a9e03ded3dd001dc	0.1.461	vmiklos-frugalware.org
-20100803123952	7670b021feaa4fd35499fc27f45b4c7964f6c251	0.1.462	vmiklos-frugalware.org
-20100803134215	be22e7b1faaa29923b2bb48f4296118784b028aa	0.1.463	vmiklos-frugalware.org
-20100804193046	bce20148efd93f3509c1c2e6efdcc273eefb3ee8	636	wilmer-gaast.net
-20100804194518	65016a69bc528ad98ca51d1109627b5a26aa257f	637	wilmer-gaast.net
-20100804200439	23d61653fc9404361b8502a6df521746a2f47d29	638	wilmer-gaast.net
-20100804204847	65a4a649a6798f7e922b0a32de690260c83e0451	639	wilmer-gaast.net
-20100805202044	f7d12f7f8014208c5c0b744d34b753d01bdf2ec2	640	wilmer-gaast.net
-20100806210540	bf2ebd811a3ed93607ec2cd12f6b42b8ae07c19b	641	wilmer-gaast.net
-20100807130846	e193aeb224c2d0d39033ca543235f6530edc0445	642	wilmer-gaast.net
-20100807145518	289bd2d47ff58f42879ad50ebbc1b193831e4a78	643	wilmer-gaast.net
-20100807163302	daae10fbfe16bac26f74af91faf253d377f1b166	644	wilmer-gaast.net
-20100807193901	7b87539aedd967884d4e4eed75d1052a233720eb	645	wilmer-gaast.net
-20100807210624	203a2d2e8857e4c83f8f5e1a89de03ea08538cb2	646	wilmer-gaast.net
-20100808134257	b8906261293b34d8c792bd1f48df10144a8a8f10	647	wilmer-gaast.net
-20100808152513	ee6cc946dc4ee82cb641df94a6ba101e99253af2	523.1.5	wilmer-gaast.net
-20100808153449	2528cdad90271f80d2ffe0e679ff8258f3e94e4c	647.1.1	wilmer-gaast.net
-20100808172943	5fecede7db56a41cf9d6ecb2b821f5a833ce0f59	647.1.2	wilmer-gaast.net
-20100809133950	f32c14c649e854fbfb7b8e495e2ebc8459ee5b6f	648	wilmer-google.com
-20100809190455	7db65b7df08a3c7cab28e065b2ffa3d9941ceccb	647.1.3	wilmer-gaast.net
-20100810000352	ceed95352f174c3061dc6ff6e131cd22df1f97ac	0.1.464	vmiklos-frugalware.org
-20100810000530	2ef7d3731760492ef0f1d1c42a08772d2bb73880	0.1.465	vmiklos-frugalware.org
-20100810111809	ffdf2e71d9e67980727aa994b77fca36ef5246c6	649	wilmer-google.com
-20100811080839	523fb2324a351e9607ad2a803c6e866c5175aa16	647.1.4	wilmer-gaast.net
-20100811085358	72176c140636532347141dc011959d676df50d2f	650	wilmer-gaast.net
-20100811204123	2b02617289671ececbd98a209cb44aca81c22a65	651	wilmer-gaast.net
-20100811230333	7f34ce254c1a1587e5560456dd11acb29345206d	647.1.5	wilmer-gaast.net
-20100812221326	ca7de3ac72f0ef10613badecb7eb6cc9a10f996b	647.1.6	wilmer-gaast.net
-20100812223830	be7a180689ba5dc2b1cd1dc14f55c59246e238ed	647.1.7	wilmer-gaast.net
-20100812224456	b46769d05371e501800a4658a0faf82c4ccdb0dd	647.1.8	wilmer-gaast.net
-20100813091231	91d6e9108bce93925a1bad60613d01c5382d003d	647.1.9	wilmer-gaast.net
-20100813091254	50b8978f0662fc83aa2e3db1d40081c315c9e6cf	647.1.10	wilmer-gaast.net
-20100814084846	4e4af1b0fe073fd2d43f4ea0d55f8deadbeb974d	647.1.11	wilmer-gaast.net
-20100814102144	4ffd757724a657d2dc5c536473523a86f2331d9e	652	wilmer-gaast.net
-20100814105520	ac2717b6a60900d31236b7696f150d0120bda3da	653	wilmer-gaast.net
-20100814105717	136c2bb632715ab83710c93c7b339c5cca7d2679	654	wilmer-gaast.net
-20100814110745	73efe3a6ea04ca19f3a4088990eda60ad852a94f	647.1.12	wilmer-gaast.net
-20100814113040	12767e39dbafc36d54995727ff2c6043e8292f16	647.1.13	wilmer-gaast.net
-20100814122059	d93c0eb9f3f5d2d2cd78f02422d0c0ed25743e3a	647.1.14	wilmer-gaast.net
-20100814130611	4452e69ab1f01793a37205db8227a2de2f211d3e	647.1.15	wilmer-gaast.net
-20100814133246	584867592546f43f857645e02169d135f0df25e8	647.1.16	wilmer-gaast.net
-20100814134435	07874bef9e9c4e6ace44e4d0605ce1aec89cad74	647.1.17	wilmer-gaast.net
-20100814145010	e5854a80111e337be01cf1e506073a231fac1c3d	647.1.18	wilmer-gaast.net
-20100814161652	193dc742d357bb604fff8921417c74ddf9e8729c	647.1.19	wilmer-gaast.net
-20100814195713	6ddb2236943384d45cacd08e02cb4ef9ed03bba3	647.1.20	wilmer-gaast.net
-20100814213333	4fc95c57361193c5338d9916fd37cc8a939067cd	647.1.21	wilmer-gaast.net
-20100814230053	d912fe4783cc9f5c2e7204f810df420359d5bee8	647.1.22	wilmer-gaast.net
-20100814232323	d97f51b59a3ac6d9557ebd1e42a45928fe064b4b	647.1.23	wilmer-gaast.net
-20100815000549	ff27648aee17e649cdd1e47f503271e4fccbff43	647.1.24	wilmer-gaast.net
-20100815130252	5a7af1ba940521a65b3611e5f96ffa9306497e45	647.1.25	wilmer-gaast.net
-20100815132303	660cb00687dbe0abd37e705a2923d1cf8bf6f45c	647.1.26	wilmer-gaast.net
-20100815142744	9679fd8f3141abb0cabe7b036cf96c3de40ee22a	647.1.27	wilmer-gaast.net
-20100815171906	9b01339168bc79d08c4e371552cc44648bd8878d	647.1.28	wilmer-gaast.net
-20100815184610	e0e15468835d5ae5bce54a28bd7a5b7aea66a1e2	647.1.29	wilmer-gaast.net
-20100815220757	762d96f3bc665dd97b037abde59243c3db8da755	655	wilmer-gaast.net
-20100817103921	d20ea9f92f70fa9732baf4520582abfc45124548	656	wilmer-google.com
-20100817233425	5b94e9ecc30e9f3b96c083e41235e4df2d0d75c9	657	wilmer-gaast.net
-20100817235750	57782e4ade94ee1e3a426f18b5e7ad9a9320fca2	658	wilmer-gaast.net
-20100818100110	763a3abb5a255fc22169c3fd86aabb7020e21086	659	wilmer-google.com
-20100818124704	bb9a15f0dae9a5f18b04d278e8df58b733f5e725	0.1.466	vmiklos-frugalware.org
-20100818125215	c9d63900f4db134942a9421b851cea647fc59b51	0.1.467	vmiklos-frugalware.org
-20100818192144	80175a1558f297d5505ed4e91a261781ec9c65a2	647.1.30	wilmer-gaast.net
-20100819122122	acd7959038aa74be7e4c05a206ba2882b15fd16b	660	wilmer-google.com
-20100819234211	bad41f567bd8b8e04d092e8e6771fee74b205032	661	wilmer-gaast.net
-20100820082228	f2520b5ad5a82d9bf08a550fb0e49913f57d4685	647.1.31	wilmer-gaast.net
-20100820193012	801b90b3e76f6eed7027f46a7d11e3d3fe0e04e9	647.1.32	wilmer-gaast.net
-20100820230412	a366cca62d3a55db4f12a94584f7e7f8fa00db02	647.1.33	wilmer-gaast.net
-20100821172732	327af51a28fe292cfc4a68caa086a13175a69719	647.1.34	wilmer-gaast.net
-20100821174647	4d4a7ed7b4af600a6b8ce5528cf7ace9c01bf358	662	wilmer-gaast.net
-20100821175903	5613af716212defcf5ee51f8dc13525f54323382	663	wilmer-gaast.net
-20100821193417	180ab31c09a9990a135b78fd6d52de5b093550f2	664	wilmer-gaast.net
-20100821194841	a758ec197f3a01dda26d394c1e5125787e4831b6	665	wilmer-gaast.net
-20100821222537	0ebf919dd8b47e50ce060f46f2dc5f10f3867207	666	wilmer-gaast.net
-20100821223922	c00dd7117be2a5fda92d6f7d72b0e4e54fa5d615	667	wilmer-gaast.net
-20100821224201	4022b686cce05eb9a42b744335abd09d5ae7d0f0	647.1.35	wilmer-gaast.net
-20100821230833	fd424c891db6da587cce3da8ac98ab7dfa986fb3	647.1.36	wilmer-gaast.net
-20100822080748	237eadd361a7ed1d2a49180056d12b57f6f3cc0a	667.1.1	wilmer-gaast.net
-20100822080748	bd599b9aeabee36ac6fdb379ca09aec27cce13a4	672	wilmer-gaast.net
-20100822104227	9b1d2d6fa7edb911e3dbcd8785aaa797ebb20d0a	647.1.37	wilmer-gaast.net
-20100822113749	f29f50ec080cf6b1f4298465faf5e0348386e9be	668	wilmer-gaast.net
-20100822121553	1bb1e012f1d62db84d331a484abf9cb4c191e8e0	669	wilmer-gaast.net
-20100822133153	241f9f6d9135048578ad603485740b73402edd8a	670	wilmer-gaast.net
-20100823103436	1aa74f559af18e06195f34b721d65d69c29fcc0f	671	wilmer-google.com
-20100823231224	be999a5385ff4e9ac7416de4cca3288f18cee85c	667.1.2	wilmer-gaast.net
-20100824121952	ea728e661f521405c2883c70865d563750223770	647.1.38	wilmer-gaast.net
-20100825001827	a72af0dc9b76451f5aa57ac9443267a97c794f95	673	wilmer-gaast.net
-20100825213549	3cd4016e8580b82bebac59e9a23c8de96330ac64	674	wilmer-gaast.net
-20100825213757	3f10fad9408844802fcfa3caf3e65e248185f5af	675	wilmer-gaast.net
-20100825230311	ad2d8bccb2a27688845d87b59eddff976ade5ef7	667.1.3	wilmer-gaast.net
-20100827042250	b78c4b9e3f3ec11b4ff560f579da764846793a00	647.1.39	wilmer-gaast.net
-20100827084818	b0a89cc6e5ffff050ddecd09e9af8eb6723f9ba6	676	wilmer-gaast.net
-20100828122250	00374acf3061c2c6c34fd2eda42e452211f7c4f9	647.1.40	wilmer-gaast.net
-20100828122437	feb1bad3303f9e5b7d6afa0bfe3ad57e8714dc52	647.1.41	wilmer-gaast.net
-20100829093927	02bb9db2edd535036e030e004a58ed1459c15bb8	647.1.42	wilmer-gaast.net
-20100830192428	df61c35ad36e18965b3b0fdccf0fa830b0768108	677	wilmer-gaast.net
-20100830195833	c55701ecf0b0a9e0c0cb9a4514c90bf426aa5d92	678	wilmer-gaast.net
-20100830201234	fda55fa31f4e5bbf040c712c22f744916c01fca1	679	wilmer-gaast.net
-20100830220246	31dbb90a2d32d6988706ae4b5c2292cd43d89595	680	wilmer-gaast.net
-20100831200536	83586911a0aa768ed196051950ebd8ffce37d467	667.1.4	wilmer-gaast.net
-20100831200614	f5c0d8e4dd3ce01945a6334a5d87c89a9f43b16b	667.1.5	wilmer-gaast.net
-20100831231821	0c85c08d210e16bb10dc283b4a1478ce53c0c1b1	667.1.6	wilmer-gaast.net
-20100901220927	934db064a58ebec2edea83df4fa07e2c83220344	667.1.7	wilmer-gaast.net
-20100901223506	2dcaf9a0fabdb92a191e64759d116f316de7dfc2	667.1.8	wilmer-gaast.net
-20100901230656	4c737ebd1a0224d325ee0ab6b3b374861c708452	681	wilmer-gaast.net
-20100902091544	64768d4ec0c3ad473573c3f3c34871e0081b4e59	647.1.43	wilmer-gaast.net
-20100903212458	bae06178bbe3863b39ec307c34d2781a53472272	647.1.44	wilmer-gaast.net
-20100904155452	4aa8a046968bff0a08dc5ae96e228861fa21fcbe	647.1.45	wilmer-gaast.net
-20100904171355	27053b516db4d0e648d666e6d9e36856af428775	647.1.46	wilmer-gaast.net
-20100904172346	4e1be76617060e89795e381d356f81cd2cbf32dc	647.1.47	wilmer-gaast.net
-20100904233147	52d63dc804571c63069a7c1e42e8c3f1f2a76527	682	wilmer-gaast.net
-20100905102712	41e0c00fd22d1cdace2040be5912d64f51f12ab8	683	wilmer-gaast.net
-20100905113122	fef78131a46462ee1d1457aa690c52a52b23151a	684	wilmer-gaast.net
-20100905114826	ed320e88b1677266f70b8b163c638bebe2a81e0a	685	wilmer-gaast.net
-20100905120812	975708ae9af7e0b0212e7852bb8d3fc93488ac49	647.1.48	wilmer-gaast.net
-20100905140943	2c6b0f4dbc367963d21314636c4b114dc7aadd32	647.1.49	wilmer-gaast.net
-20100905233040	d6657ce1d4fa016bfa0e013f29351229e2cc23d1	686	wilmer-gaast.net
-20100906002001	674a01d4640fd869d54e1b9f1474986fda2994de	687	wilmer-gaast.net
-20100906092639	3c9b095f6b8844369d135ee3998b6a72de76196f	688	wilmer-gaast.net
-20100910114531	ecae65f7337dbf8c0e2dbc6388d6e35804e1c3ce	689	wilmer-gaast.net
-20100919172910	76e2f624b3adee07d6ea970961411d8a7c734745	667.2.1	pesco-khjk.org
-20100919181931	51e9a1dd00c43a18ab9121e27c2631fd02de0188	667.2.2	pesco-khjk.org
-20100919182305	99a01b907b5be9467908bcce09e146e6f38c40d1	667.2.3	pesco-khjk.org
-20100919185237	21710443be24b793dd14dc22ba6899ac25af3eca	667.2.4	pesco-khjk.org
-20100919191249	9cc653c46593ba801e346ef3e3b20e925bc0541b	667.2.5	pesco-khjk.org
-20100919191643	10d89be0b7a035e2fc993bf9ff63d78c8a93f90f	667.2.6	pesco-khjk.org
-20100919200432	0e078a7bfc86516ea3d3927c4ca327d572626089	667.2.7	pesco-khjk.org
-20100919201212	475206376035daf797c59e94c1e945c5b499e1a2	667.2.8	pesco-khjk.org
-20100930052836	665c24f36e591b1011c47b7aaa13a62533e79dac	690	wilmer-gaast.net
-20100930055043	858ea0166108e9cfab3cc9290fc976f041411cce	667.1.9	wilmer-gaast.net
-20100930060201	6ce2240e6dd88d86e90ffbfff078bc55b3f49477	667.1.10	wilmer-gaast.net
-20100930063007	560129adde5041ce5b9379a5225f6b3d4f31a1f9	667.1.11	wilmer-gaast.net
-20101001192548	03e5fb7270815a8aecd77f9df8db3970a2c01195	667.3.1	pesco-khjk.org
-20101001201445	7c91392c8525486876fe381b33f21b7dd508477c	667.3.2	pesco-khjk.org
-20101001203816	f26db4a89ea0a61123a7aa4b70b1d4102a309b2e	667.3.3	pesco-khjk.org
-20101001213644	944d7a58c305440d2afa7807e8642ae052df08e7	667.3.4	pesco-khjk.org
-20101001213931	faa75c0e43dbaaeee2f8c12581899d1a2128d4a0	667.3.5	pesco-khjk.org
-20101002042150	52f5e90f166179d461e29af98d8b2852d9f12be0	647.1.50	wilmer-gaast.net
-20101002042257	ed86165b5681b7c3ed8a7b2ce8bda0dafd6fcd52	647.1.51	wilmer-gaast.net
-20101002045119	05bf2a0d55999c944ac6cf03ad85270cb2165923	691	wilmer-gaast.net
-20101002051927	04cd284bce74c114fde3043c951a5c8ef9eb79ae	647.1.52	wilmer-gaast.net
-20101002052843	88de0c96136313e553113f69cfc6ae544a5f5954	667.1.12	wilmer-gaast.net
-20101002053453	62f53b508742804d5df6533150f17d41e6afcbb2	692	wilmer-gaast.net
-20101002065633	2af3e232ff468b288dd4e0dbdab1a17312d801c5	693	wilmer-gaast.net
-20101003024526	8e9e2b7d9e54744cee32b3724391bf0ad04e8aff	667.1.13	wilmer-gaast.net
-20101003025020	04f0c10b5a45d0bb1a1f8888e0eb2f6db8fc1b84	667.1.14	wilmer-gaast.net
-20101003030319	25b5a4a30f8a767bfd4577aa284bd435f7e5fb97	667.1.15	wilmer-gaast.net
-20101004215518	3ad80364e4d8aac620120b630df8a164d58dd114	667.4.1	pesco-khjk.org
-20101006083115	385fbc4b9624f944691c73a8b4618f069e54bd11	694	wilmer-gaast.net
-20101007061939	c1d40e7ee369e7a6b7f3ab167a50f99bfdcce29e	695	wilmer-gaast.net
-20101007062535	afb9ea93adb5805b580c343bd81cc168697d1d63	696	wilmer-gaast.net
-20101007063206	508588afbdf18f72b8154c3500eedd19cd15c6cc	667.1.16	wilmer-gaast.net
-20101007064150	8a35d4bcb6e54f5c12ac0ff05a605eb77686ec59	697	wilmer-gaast.net
-20101008063244	23b29c67968f3dd39e7d6970acc5669556f4c8b9	698	wilmer-gaast.net
-20101009182833	27b407fde1844a0e03f1a9d92d2a1c4a40435f9b	667.1.17	wilmer-gaast.net
-20101009184119	619770237590e4a760346f2e12681d7e2220dda4	699	wilmer-gaast.net
-20101010184006	d150a9df0b0e84a3fe30af340762f96475f4b1e5	700	wilmer-gaast.net
-20101016051746	9c9a29ceea6734175f9f7693ce86b3ba633f4f7b	701	wilmer-gaast.net
-20101016225821	3fc6c32bede01e02b2ac4541c952f37dbad511b3	702	wilmer-gaast.net
-20101017064435	83e47ec2da907fd3d8e95e4790bdfd0e9fb50836	703	wilmer-gaast.net
-20101021001102	79b5c41187f04cc50a634ebe681e06340869e387	704	wilmer-gaast.net
-20101021221414	e1aaea4357d61557c5d6be99fa3e5a9b3a55f784	705	wilmer-gaast.net
-20101021221500	1d19cdb2ba5d8afa3d5457c6c6c7d3995862c5f5	706	wilmer-gaast.net
-20101021221758	a0626c15f701d81b1843fa37513dc9d12fd5a31d	707	wilmer-gaast.net
-20101022002852	7c5a3be5eacf151d52266e2bc12ccdd8ed8cdb9f	708	wilmer-gaast.net
-20101022005034	8d93b4ad430af75e8ca50116cf7a8ddd19084649	709	wilmer-gaast.net
-20101022234644	03e6c520fc1f9d1fc3f94ff54adc18f725843828	710	wilmer-gaast.net
-20101023030548	90669749e3983993c667d40dad6215045139a711	711	wilmer-gaast.net
-20101023155249	f48600636449d2897e883566a3626888d481ae23	712	wilmer-gaast.net
-20101023232943	9a0ee1a7cd8a9cc50bafe44b5ecf3bbdf5cc50d6	713	wilmer-gaast.net
-20101024222040	389ce9f37de662fa58ae293058e34d469e546790	714	wilmer-gaast.net
-20101024231427	47c590cbc71d80d934349abb495313a652539d89	0.1.468	vmiklos-frugalware.org
-20101025115645	724dfceab6c174c7ba74918733b9fb9a48d70bc9	714.1.1	aelmahmoudy-users.sourceforge.net
-20101025120307	b350aad59aa894b19c05d5cbadf26a72419c6469	714.1.2	aelmahmoudy-users.sourceforge.net
-20101025121843	56ff331c7821c664aa8f5bf9b8d0e42426e5678c	714.1.3	aelmahmoudy-users.sourceforge.net
-20101025122123	6e0e283f3169701eb06e0e27803706500c30ad3d	714.1.4	aelmahmoudy-users.sourceforge.net
-20101025122939	d1c70e774d61a229ba90205631994a0a85e29a63	714.1.5	aelmahmoudy-users.sourceforge.net
-20101025123529	bc37983c230346bdd5d2efb1dc16b94fce634c34	714.1.6	aelmahmoudy-users.sourceforge.net
-20101025124219	1c128118bd53a02d4e14a18d5bd78b2754c881cd	714.1.7	aelmahmoudy-users.sourceforge.net
-20101025142129	6c2b430052de76bc275f455e4c6651b7db05e150	714.1.8	aelmahmoudy-users.sourceforge.net
-20101025142346	7064d28c53cb2cd8b5660d9e397abd934be172ba	714.1.9	aelmahmoudy-users.sourceforge.net
-20101025142555	279454e94fd888fbf65ecfd832e6171ac748412d	714.1.10	aelmahmoudy-users.sourceforge.net
-20101025143404	295c17b1183ef1e36b06428c1468a76c7d2d89fe	714.1.11	aelmahmoudy-users.sourceforge.net
-20101025144320	5020ef656d181c86d854af0525b18451ee24138f	714.1.12	aelmahmoudy-users.sourceforge.net
-20101025144846	920ed08ecc3d9c0c0912c87e1a3356fde0e21fd9	714.1.13	aelmahmoudy-users.sourceforge.net
-20101025145023	9c66db07fd781e905eef03415dab1044bcf911c3	714.1.14	aelmahmoudy-users.sourceforge.net
-20101025145653	5258d364a0aae0336b9a225d69498260bebe7b54	714.1.15	aelmahmoudy-users.sourceforge.net
-20101112224828	6da18acb1373d5555240a0d7779c012a20cf443b	715	wilmer-gaast.net
-20101112225139	70d779590840164e3bda1c183ade00872d9786c2	716	wilmer-gaast.net
-20101113130140	ed0589ce0af33a8e199b6bc75f55e45904c85d1c	717	wilmer-gaast.net
-20101115230032	6b96de6730acc68352e1a7bc4ef5415a4e97ed92	718	wilmer-gaast.net
-20101117112933	4d30309eb02a3078a1fe91902fbd97b58de228aa	0.1.469	vmiklos-frugalware.org
-20101119210014	10685d3b5ff450123b22b0a665ededbf35a18e89	719	wilmer-gaast.net
-20101120113139	4eb75b261750c4ecba83946aae83c914db938c9d	720	wilmer-gaast.net
-20101120114804	6d544a18db369d8f89e817dfda72f96e04494077	721	wilmer-gaast.net
-20101120151340	ef043d3d788fa7e6597eb210fc398251b99daf6d	722	wilmer-gaast.net
-20101120151855	107480646e30614e046a10230c99a376be69100b	723	wilmer-gaast.net
-20101120153958	d68365c5a799556a9375ac7e08d78d7dc80010ce	724	wilmer-gaast.net
-20101120202544	bb151f7aad467bf29c6e5ca552088f7f0b8ec876	725	wilmer-gaast.net
-20101121152354	748bcdde269da5cd8184418f0df334e12338a541	726	wilmer-gaast.net
-20101121155308	495d21b4d09392a84ff4c6fd914a0923029d2462	727	wilmer-gaast.net
-20101121161403	5df65bd866aad7dd7d7b62d7a7ef168a26b002e5	728	wilmer-gaast.net
-20101121190843	694be84bc65240f6125852fc6203d5ffd50c6b05	729	wilmer-gaast.net
-20101121191354	68adaf84564823c1e2d5629c27011302bead3546	730	wilmer-gaast.net
-20101121192437	ca0ce03c6c66f6cda4aa685bb2663da5b3019353	731	wilmer-gaast.net
-20101121192635	bdedc148f84406a4715a1e668ea767927740c790	732	wilmer-gaast.net
-20101121193459	09d4922d3740eb0ad2e42e02ca5d57f03b263eab	733	wilmer-gaast.net
-20101122131745	9564e55a35ebffaad644c13827ec2b795fb21154	734	wilmer-google.com
-20101124223212	bedad20a945ef60dbc46e44e5fb26e3b7047490a	735	wilmer-gaast.net
-20101124224228	b11b781cc66fa7ffeaa96e7a21b9d5b528d2c4a4	736	wilmer-gaast.net
-20101129224054	1ce52ec6a6239efa0327727ffff58c867bb6a4de	0.1.470	vmiklos-frugalware.org
-20101202121409	cc20520bd29c88d424f44ac5669c3026e9fd99fb	737	wilmer-google.com
-20101204235951	b41d5d25af0c6d1f8b777a16bfa83296e395da5b	738	wilmer-gaast.net
-20101205003007	fd65edb3a40d4c73130b0be8dfa08ae8092c6935	739	wilmer-gaast.net
-20101205122807	a429907207d5b8b05463c72a9b8c880ba03ad921	740	wilmer-gaast.net
-20101205204419	7f4149520a1f246eb4221b94c2a078a6c5c71cc5	0.6.1	pcfeb0009-gmx.com
-20101206000349	d88c92a40438e0ac8e897beb3ead44c4404050b3	741	wilmer-gaast.net
-20101206001827	d76e12f6762d0a13cbafd50ebad29baf60b7df8a	742	wilmer-gaast.net
-20101207132902	e8e28926f8de948bce107009aea97d0de0222665	0.1.471	vmiklos-frugalware.org
-20101207231430	a97a336d7d3dbe83f1be750c4f1fe8f1179c7066	743	wilmer-gaast.net
-20101207232432	83f1bd1d195b21617bbc58b3c36e9d23d7905661	744	wilmer-gaast.net
-20101207234149	c775a58faa7d5905b06e2f8900db7337082d5165	745	wilmer-gaast.net
-20101208024221	6d41bc51b675cdc4628796f12c0d2c388d4a208a	0.1.472	vmiklos-frugalware.org
-20101208030527	87e1eba738cfb79649924353caf221bd2ab65b75	0.1.473	vmiklos-frugalware.org
-20101208032204	4ae3ffc4ee7d00f39555f1c68d12777ec1e7df4e	0.1.474	vmiklos-frugalware.org
-20101208032205	35249d64faaad5d581448a52e439b643d6e6a2d3	0.1.475	vmiklos-frugalware.org
-20101211151412	c7000bbd6e5e44a75dba0a4c5a8e584a34b844b3	0.1.476	vmiklos-frugalware.org
-20101211151421	0b77a9b4516d588a2026536e2a8cee2b569a1b46	0.1.477	vmiklos-frugalware.org
-20101211151647	77aa416f8ebf36ccdbbe5855ad3fbbfdf7b4724d	0.1.478	vmiklos-frugalware.org
-20101212002517	76c89dc7e58c9d4806e83c04ab4005faa84e2703	746	wilmer-gaast.net
-20101213003356	8eec79d7d84aa0ba0f615db91c992102bc1bbce6	747	wilmer-gaast.net
-20101213004258	9c846175a71a36cbc49b63ab1668d01675e16203	748	wilmer-gaast.net
-20101213022220	451f1210e64b9e2a8e606dc3103d73d15b89ca8e	0.1.480	vmiklos-frugalware.org
-20101213022220	71c4bb6362db328b9768b801109ffd006445a367	0.1.479	vmiklos-frugalware.org
-20101213033730	a618ea653d159324a9691af06d3da42373f27be9	0.1.481	vmiklos-frugalware.org
-20101216120958	c5920df89b2e407b63e3a7e6825b2ed68ab96af2	749	wilmer-gaast.net
-20101216210216	fe79f7a75d455c6ebf49b5da9f4d6aa905d44294	750	wilmer-gaast.net
-20101216224823	191cfb1556ca94bc0bab9427c5de7b83cb45cd7c	751	wilmer-gaast.net
-20101217205704	eeab8bcdb7e7026caca7d695c358599b3fa58022	0.7.1	pcfeb0009-gmx.com
-20101219111319	b9de97b5fe41cbbb5be3c748b60364443fc8ab91	752	wilmer-gaast.net
-20101219111620	31c28a493b9504c87d2bc316ce832a80d26ba92e	753	wilmer-gaast.net
-20101219201514	5a54ec8ee31a05df25751d929c1b7734ffaaf2d2	0.7.2	pcfeb0009-gmx.com
-20101220205149	e530abd12978a2c7e4b653a983e8088e6f125832	0.7.3	pcfeb0009-gmx.com
-20101222190841	9c511663f874e6694061c2bd8e38bfdfb0b6ec2f	0.7.4	pcfeb0009-gmx.com
-20101224122116	d45adcf1981959f4200ec292d30f163e5b644893	0.7.5	pcfeb0009-gmx.com
-20101224152351	988d75c36aac56a8a5c4b4530b50cf0c5211a502	0.1.482	vmiklos-frugalware.org
-20101228130434	1130561aa6092404f6f08735d3de8c22f120dee9	0.7.6	pcfeb0009-gmx.com
-20101228130616	f85837a655ba1ba169e272e5929242b1bd47f51f	0.7.7	pcfeb0009-gmx.com
-20101229004021	ae8cc50eac47c3ff71000ca4b819fc5d238c5031	0.1.483	vmiklos-frugalware.org
-20101229014932	53eb75c27a4168ac511a30accc526e34de4eedde	0.1.484	vmiklos-frugalware.org
-20101229015446	5eb78c040fb2870bedddb46f3bf1ed5b2d070f68	0.1.485	vmiklos-frugalware.org
-20101229015920	3fa9c259f3977f5e7e1916687a12d80ac6d673a5	0.1.486	vmiklos-frugalware.org
-20101229021616	edbfade989ba853cbd520fa87e396b044e80695c	0.1.487	vmiklos-frugalware.org
-20110101165520	8b8d1bed18306366a9b4c6bf7fc5604c1ae05e1f	0.1.488	vmiklos-frugalware.org
-20110102023238	89d68459fe977cc76e22c09e8b099c7961863883	0.1.489	vmiklos-frugalware.org
-20110102023239	54ca2692dc70535a24e9cee0ff1370db8ae76099	0.1.490	vmiklos-frugalware.org
-20110102024104	fbb15f23566d61d4c26a0b9fbcc00cf397737e52	0.1.491	vmiklos-frugalware.org
-20110103020147	cb6d3c9e9ec93705022ec24451246265c7841ebe	0.1.492	vmiklos-frugalware.org
-20110103022218	a5e6aa1ea309c774d153d0d4cc59cb0c3c0e1cd4	0.1.493	vmiklos-frugalware.org
-20110103024740	46e9822f1f97efe1b2675d85c9281d62f956c4bc	0.1.494	vmiklos-frugalware.org
-20110103031300	46641bf36d50443b5ee1882b77d3e68edf2f08e2	0.1.495	vmiklos-frugalware.org
-20110103031339	2b183e44ed5efd13c4b415ebde303650d370b1e0	0.1.496	vmiklos-frugalware.org
-20110103031951	59f62dce51f127fcd5888fd7211c23a9d8ecfb35	0.1.497	vmiklos-frugalware.org
-20110103033054	c6e02189ad824118f0b5a6bce23a45492db14305	0.1.498	vmiklos-frugalware.org
-20110103033637	bb0775c04379301d68f9724cbc367a62dfa1a87b	0.1.499	vmiklos-frugalware.org
-20110103204401	0e886477725bf6f55c8556e313716131d0adc2e8	0.8.2	pcfeb0009-gmx.com
-20110103204401	10378d81e2d2c9525f95febb1973ba7c8ad831a5	0.8.1	pcfeb0009-gmx.com
-20110103204401	dc3f9ef400a4640fd69d7a7c8bf021399fcae767	0.8.4	pcfeb0009-gmx.com
-20110103204401	51f0c2b8756c63774c507f000dc06d5d56f4cbf7	0.8.3	pcfeb0009-gmx.com
-20110104000646	6f5ab2a7c46f4388817f800b68141ca163412d37	0.1.500	vmiklos-frugalware.org
-20110104010000	c12a5c8202c3391673a4651c769863e6d08ffceb	0.1.501	vmiklos-frugalware.org
-20110104011635	ab094d9750a709e334fa6922d33873eccf74b1b6	0.1.502	vmiklos-frugalware.org
-20110105020958	a86612f10adb66d97c8a725c7f689da1ad7c717b	0.1.503	vmiklos-frugalware.org
-20110108132442	50c9996e00031b921a4a6bb114e6e25712e93315	754	wilmer-gaast.net
-20110121141703	f5035854dcaef6112ff71a111d071e93708bf082	0.1.504	vmiklos-frugalware.org
-20110121143011	7279554e7c2ad1516794d4d690758ccfa3c446f3	0.1.505	vmiklos-frugalware.org
-20110122172955	5282ffd40e07bbcbe4f5c796d054eeb7a1a275ee	755	wilmer-gaast.net
-20110123163207	02e06b5686650ddae5d9b1b3b2a09a89a7644cba	756	wilmer-gaast.net
-20110129205005	00fd005cd04ce4f68ff2d72de75a313868564f54	757	wilmer-gaast.net
-20110201130558	060d0661ff8c1465f394c307d0f4c2a22964c6b2	758	wilmer-google.com
-20110201181716	da60f28cb75634f40936951b1ce65fd7dce70c07	759	wilmer-google.com
-20110207232425	c899eb01dbe590be257a619a3da577a78c08a8a2	0.1.506	vmiklos-frugalware.org
-20110207232437	3423be07392acb960fed3e014141ce5df177c06f	0.1.507	vmiklos-frugalware.org
-20110224185018	4e3df8a68b70d0c76c6ee2a172998775697681f6	760	wilmer-gaast.net
-20110224185218	bb8d7d1106ce91c54c671a1495420d57b9a57caf	761	wilmer-gaast.net
-20110224191544	fd73022292ca51eab2c9838505abbf1d36e81197	762	wilmer-gaast.net
-20110224192054	79ec314c445e6bf9a6fbef7a8681a56df2be0b63	763	wilmer-gaast.net
-20110224194750	8e3890b12f48f4d4c2387cf9ac4cf3405bbf6615	764	wilmer-gaast.net
-20110308062334	9e9140b16ab7551c0588a58f065d7c9fbc8475fe	765	wilmer-gaast.net
-20110308062434	93cc86fe9e3be0ce83d20790327f41df3b0f6949	766	wilmer-gaast.net
-20110308064839	7add7ec49fa90a0642b55918c893fef95208f7b3	767	wilmer-gaast.net
-20110308065057	420d80d17741d5cd65ee96dd120dd28da225e67d	768	wilmer-gaast.net
-20110308065538	f68fd5f0a37883438a13b8043489543f01b78c35	769	wilmer-gaast.net
-20110318005714	417002e1714b97cc3073e3968ee0cee3181dc96c	770	wilmer-gaast.net
-20110318012420	c14959d71119b1fc0d35c8fbd48c47d5d79234a7	771	wilmer-gaast.net
-20110319140249	13688ecb47afd26d7f06b8ec3830154426a0d4ff	772	wilmer-gaast.net
-20110326120238	ff9456329255ef8b227f9322ca8d25ef6c790e7e	773	wilmer-gaast.net
-20110327140955	ce617f0a445df1201808a9cd29722a9cf6c96b95	774	wilmer-gaast.net
-20110327221309	f01bc6f174e15d5dcc530a0136a556bfbc22d236	775	wilmer-gaast.net
-20110328232846	ce81acd6242513c5fcf6cf2224f9b1137701e14e	776	wilmer-gaast.net
-20110329205754	4f50ea5db021b6f627332592a7e43a06f3188172	777	wilmer-gaast.net
-20110329224700	15bc063f3b8cf99f5fdc7983192a521ac115e465	778	wilmer-gaast.net
-20110331102817	6220254c1ecebce81ab765260ec0af3c3818f9a6	779	wilmer-gaast.net
-20110411232609	d5a66f8ae910023efdbfc8343a63dca110b8bf09	0.1.508	vmiklos-frugalware.org
-20110411233202	9c97a03e2ec68559c21f319caca5cee94133f6b7	0.1.509	vmiklos-frugalware.org
-20110412001057	43b1cd7ac2a55145f9a80c5af505308c788f4b90	779.1.1	vmiklos-frugalware.org
-20110412001635	370899f9368cdcb0bb43fd1510b0ea44c08027ec	779.1.2	vmiklos-frugalware.org
-20110418134736	d43d55a4c55b1c75d29831a825978541f8356419	780	wilmer-gaast.net
-20110418140450	6cc36efa70ff179de1468c8cfe614152366fa27b	781	wilmer-gaast.net
-20110418141408	6eca2eb9a06d0991fa0f431839e528177313566a	782	wilmer-gaast.net
-20110418141428	2423c939e096ded3bf6a09f5fc70ba2218df85cd	783	wilmer-gaast.net
-20110422155521	ff139f00a0761b1eb6dbd24a26ae231d6b959270	784	wilmer-gaast.net
-20110422193136	a658f104202a51e8b51b944f9e61370f8e67acfc	785	wilmer-gaast.net
-20110501140802	d1d1b88eac1f5b63c98f7b7e4cf39d81cfc9b4d6	786	wilmer-gaast.net
-20110501145730	6963230895c26d054cc3543b03787118e2334c32	787	wilmer-gaast.net
-20110501162235	69ef042b64ac1cb2f771d3ae02f27a7e64951222	787.1.1	pesco-khjk.org
-20110501163211	983bb666b65425aeb2bc8904b4c7cae11d43ded7	788	wilmer-gaast.net
-20110501170724	2ffc41eab67c061656c8fc3a70ef59e6b5527fdb	787.1.2	pesco-khjk.org
-20110501171237	b194fe7b16e22996b238b333df2b4ded641a683c	787.1.3	pesco-khjk.org
-20110512082859	2370ec267bd45b1192d4b5e85eb6f09e655ca576	789	wilmer-gaast.net
-20110609081932	27ad50bfa16005ee6d02974478ddc56f9e2ceef5	790	wilmer-gaast.net
-20110611112801	5983eca9ac1187eb1407eece7d7ad72ef5e091b0	791	wilmer-gaast.net
-20110611160950	5f74987ee76bf4bdf94f5ea3017078ad65f97aa7	792	wilmer-gaast.net
-20110611171108	de923d59e15bb650c2cb98a6254c79aa7c6af96e	793	wilmer-gaast.net
-20110611175026	c0f33f1ea6aa05543b81f4eb0b9cc3a11446f996	794	wilmer-gaast.net
-20110611175939	4bc66aeeacdc5c585a5816ef8aafffb787b05262	795	wilmer-gaast.net
-20110611232223	14df98f1f4da171b728a87b6c6976844055b4a58	796	wilmer-gaast.net
-20110611235900	7ceb6b267883fe905e7f47d7a131f945fd643012	797	wilmer-gaast.net
-20110612115320	52a735ea84a23fb4508209ed24939ee69fe4f7aa	798	wilmer-gaast.net
-20110614214159	9e7a566e5c98d697636cf9e96cd38cb32d18a6d7	799	wilmer-gaast.net
-20110615121358	17dd2eda62fa0b38907a6f565ab2203c7fbd270c	779.2.1	hrubi13-gmail.com
-20110615121816	c573f1b53466e34c26296227e4c661df9cae1c15	779.2.2	hrubi13-gmail.com
-20110615121849	bd114221b85ae8c1ffec41f1be98570030d33e03	779.2.3	hrubi13-gmail.com
-20110615124716	05d964c32262a8b0ce3f8a5d2087d230a461df86	779.2.4	hrubi13-gmail.com
-20110616215906	bf8ee3981b4de9f3dccf6dd374147c5a6d92a167	779.1.3	vmiklos-frugalware.org
-20110624040422	d4a0a0a2b7853a46f2b91025cd7f378b5916fb14	799.2.1	lyz-princessleia.com
-20110624041336	0c42c65ff4962be4541b5b635b76d20e1511768b	799.2.2	lyz-princessleia.com
-20110625222826	a0c6fc585aecedd236a79e3dc89e2ed67d881ad5	799.1.1	pesco-khjk.org
-20110625223101	1dc00fe6d7f79f43660c5196a0132b0c788858f5	799.1.2	pesco-khjk.org
-20110629004031	9a57b1f353e733bbb5d3c8dd91e551dd307458dd	799.1.3	pesco-khjk.org
-20110629013518	1082395cb29e7d0bde7ee55fe1e5d73b41e0e984	799.1.4	pesco-khjk.org
-20110629015946	f1cf01c5eb91fdd3effe04a27cdc5a268ff5ee66	799.1.5	pesco-khjk.org
-20110629232502	2ff0f378570177fe42e4bf48b63bea52369f80e8	779.1.4	vmiklos-frugalware.org
-20110629232901	66911fce6dc03b0af96c9e04975b6147ae27b6c7	779.1.5	vmiklos-frugalware.org
-20110629233033	004f49486f537d0eac436eb9a750b22e6f9fa1f2	779.1.6	vmiklos-frugalware.org
-20110702163442	f5da476cbc9d22de96e154016fec21f6110dcd41	800	wilmer-gaast.net
-20110703141454	b5a5938849fe4d147cf050685589b47e7b9aedfd	779.1.7	vmiklos-frugalware.org
-20110703141513	51555706adf8a33b1e7f30b9d8d9c381c79b4123	779.1.8	vmiklos-frugalware.org
-20110703141525	b518cdc47eee56a969f157c228fa47f7a47afef9	779.1.9	vmiklos-frugalware.org
-20110712080412	a01049810d7101b8c0014aa7b36826fc95f944a5	801	wilmer-gaast.net
-20110722182925	57b4525653972dc23c8c5ca5ffaa7e44fad64ee9	801.1.1	wilmer-google.com
-20110724125021	17f6079a06eaed2fba1da999332e9e5ac1dbc2a6	779.1.10	wilmer-gaast.net
-20110724125100	c8b8c83fb6a0ae1b59de71ac796a78d73e444988	802	wilmer-gaast.net
-20110725120930	59c9adb4e3e27ded95ec9ccee4f57cf79490bd12	801.1.2	wilmer-google.com
-20110726115838	4a5d88504235e1df5d01a3a5701b83dd82d6695d	801.1.3	wilmer-google.com
-20110731144041	1174899c4f299dd020a8e6489d0384ae24771978	801.1.4	wilmer-gaast.net
-20110731145107	aa9f1acec3f941cbb6b9fa716db1e775e88005c2	801.1.5	wilmer-gaast.net
-20110731145500	39a939ce4ef6717d65c36c97e6a7adf05b125cad	801.1.6	wilmer-gaast.net
-20110731154437	e1c926f53750ca288f30f3d62eecdc763b67d642	801.1.7	wilmer-gaast.net
-20110731202730	f138bd25e9184c3033f405a7bbb5734d82a877c7	801.1.8	wilmer-gaast.net
-20110731203137	f988ad3fd0bb29ae8a16f5d921b92fd90b7792a6	801.1.9	wilmer-gaast.net
-20110731225942	3d952b5cd5562889aa3b9594b7b88352ea30f78c	803	wilmer-gaast.net
-20110801095348	87dddee3a14d7755204d6fc4b321729bad02ce4e	801.1.12	wilmer-gaast.net
-20110803214549	d6b690624885f6bc34e5dfb9a84daa34a1adb7e6	803.1.1	j-jasper.es
-20110804151954	911d97a988d5f3d90c4b15c05adc733ada1fb37a	801.1.10	wilmer-google.com
-20110804185253	773219385d7db48c58556210922eb671e24736aa	801.1.11	wilmer-google.com
-20110817215821	5f1e78d41f68cccf3dc20af0982133dc0b4cc3bc	804	wilmer-gaast.net
-20110817222147	aa2f575d34af957eaf7fac58a54055ba74a1085f	805	wilmer-gaast.net
-20110817222256	fd1ca44a8e3ea9392d6fc5afc5e3ba7c6f4507fe	806	wilmer-gaast.net
-20110817222810	4804b2f2e7270148ab303bf31351a5e2aa829761	807	wilmer-gaast.net
-20110825180807	2322a9f38a2f6c9bf86eb62c2cd68fd3848b694f	808	wilmer-gaast.net
-20110826201617	429a9b17fb256e5fc750104f4c5ffb2dc7fba74b	809	wilmer-gaast.net
-20110827101845	0fff0b22db8860d3e6165f381028b441f26a31fe	810	wilmer-gaast.net
-20110827103048	ff18fc1612e9468a8406728e180b453ca6bd408a	811	wilmer-gaast.net
-20110921200918	f03a49856fd41c621e258d1a969d360880a5e448	811.1.1	pesco-khjk.org
-20110930043232	da44b08376a65c1a7f79776ae4ecf71d47596174	811.1.2	pesco-khjk.org
-20111001145524	fb11647a0dc92198488c709e0444c2aacbf15505	811.1.3	pesco-khjk.org
-20111001145748	3231485f983ea5bffe425205abfb059ee62b0168	811.1.4	pesco-khjk.org
-20111003145658	e67e513a16f3e545fd71eb176aac83d41a1dc271	811.1.5	pesco-khjk.org
-20111003151800	409c2dec87731dae72dfbaae6b3fdf4a9c53dea2	811.1.6	pesco-khjk.org
-20111003154801	aea22cd1e0e955848c143a8fc36978391e9a4846	811.1.7	pesco-khjk.org
-20111014174717	6cdaabb02658ff69eb3fe58f4d54f5b70424dda9	812	wilmer-gaast.net
-20111017132837	9fc017d62eb597a09af340f19fa10b8fc77da1d4	813	wilmer-gaast.net
-20111018035814	733f607bfaa34aa949df2602aec3b46aa2c3a46f	814	wilmer-gaast.net
-20111020025636	c1487019c50fa6ce034523c75150d87955f9a6f2	815	wilmer-gaast.net
-20111020025957	32bea82d26872395634862204f551644233454ae	816	wilmer-gaast.net
-20111020031823	4c225f062d3f7c9474890692caffb34768fb7b15	817	wilmer-gaast.net
-20111020032829	6a451817589df092ef5121bbe2c7156d2d01f669	818	wilmer-gaast.net
-20111021030054	3864c0814cc3e86a8035d27558f6d66af836a5a3	819	wilmer-gaast.net
-20111021035914	e6b41b1db86aded510fa413d3ab1c34e624c9c27	820	wilmer-gaast.net
-20111030113349	de26f3ccb7148d83c7bb8a6d5b84bccc95d765fa	821	wilmer-gaast.net
-20111109000722	ff1616b7f25775ead76768c2b78c2ca01aac830f	822	wilmer-gaast.net
-20111110033840	dac74bdcfcc358cd59a318e34a54a3b17ebf3459	823	wilmer-gaast.net
-20111112154952	dff732d93799db858a5728abbf31d2e6274b8425	824	wilmer-gaast.net
-20111113014142	03a8f8ec82719512f059fdd2c0dca3bb919b059c	825	wilmer-gaast.net
-20111114104303	80acb6db8133ebd16c3257d33721994ed164ba9f	826	wilmer-gaast.net
-20111114110822	5dd725d173ebf2116329b78952169cc8e6660621	827	wilmer-gaast.net
-20111123162759	d45b1813d7a7411585038a79d9636193aabe2d51	827.1.1	vmiklos-frugalware.org
-20111124073822	c945ee3ac109d7ce856c7968e0d5181e7cb2980b	828	wilmer-gaast.net
-20111124081804	90fc864b79ba673623ce96e3e89866e48bc32d7f	829	wilmer-gaast.net
-20111124131132	e4fc4937e6e6e4586defe791716fc86f9b7c3762	829.1.1	vmiklos-frugalware.org
-20111125074644	693ff09ad6d6d5329ea8fad6eb4d1f594db70b67	830	wilmer-gaast.net
-20111125121605	3bd2f17754009d84df1aeb60cfa1d3510182e87c	831	wilmer-gaast.net
-20111126004145	c7b94ef51bcc4585068b568dbe74108ef0340296	831.1.1	pesco-khjk.org
-20111126184124	c77406ae3fd357c3d2eabd36ec524e049e265857	832	wilmer-gaast.net
-20111128083446	ddcff199f55d46ba67ade27e7a35b61f485c1fcd	833	wilmer-gaast.net
-20111128084210	191ac9d7c7f38d4450a2f4f173c5a34be368fd2f	834	wilmer-gaast.net
-20111202103254	bd3166176fdb7ed96211eb539316f47843e0faa7	835	wilmer-gaast.net
-20111204191429	ca974d7f6171a70c5484420bd6c59919383105a6	836	wilmer-gaast.net
-20111204192106	df98ee8b4abaa890a02e706d69b93724e8f7744d	837	wilmer-gaast.net
-20111206005316	5dc7f907bf24e52f83b9d2e7613cf4e1f7aebcdf	837.1.1	vmiklos-frugalware.org
-20111206005326	d18db32f4777d0be6ccb6f46067c0532c07909d5	837.1.2	vmiklos-frugalware.org
-20111206005334	d7edadf94be620d226b569f5e14f45de7f08c03a	837.1.3	vmiklos-frugalware.org
-20111206215043	b041b521c8b347c4660b0920f17b92b9c1484f49	838	wilmer-gaast.net
-20111207213727	aee8c19adbaffb8fe189c626d7e188e5ea008b0d	839	wilmer-gaast.net
-20111207214725	06b58933ad0f46995c2a676671f92a335dfdfe05	840	wilmer-gaast.net
-20111210224310	57da9609d3b24014813ec39af844c70c466bcd9c	841	wilmer-gaast.net
-20111210225412	8b39b1a3684d80c228ae81347e3b62679cc45f5d	842	wilmer-gaast.net
-20111211121224	e46db53456e89bcf9e0baf4c093c4b02e082819a	843	wilmer-gaast.net
-20111211125440	877686b6ada5aaea69ac5e629c385ea60a3f1829	844	wilmer-gaast.net
-20111211163802	3f808ca58e45f2a305e01471cc514957039a865d	845	wilmer-gaast.net
-20111211170706	d717020ca0b62c7045de2a41b6bfcf72d033301a	846	wilmer-gaast.net
-20111211175035	57a6eb5fd993cd067bb04d386bd2f0e6f0601b53	847	wilmer-gaast.net
-20111211232540	6ba00ac2be7f59a730aca244d889cabba8e88bfc	847.1.1	vmiklos-frugalware.org
-20111211232645	b926d88bdbb7e1081fed880fbf8e9b9b7fcf519c	848	wilmer-gaast.net
-20111213003407	17f057d70b1513710e3d765969205625f0fc7b76	849	wilmer-gaast.net
-20111217135001	6e9ae727bcd95eb820fa28becaf9f79ac463de5f	801.1.13	wilmer-gaast.net
-20111218192544	18c6d369d777a1d38ef450f868c22de1d0ebba2d	801.1.14	wilmer-gaast.net
-20111218204411	4efa5ce4d0778c841f957c2beb3458f9e76c5534	849.1.1	millerdevel-gmail.com
-20111218204547	c1a58cce5edf4767af5cc040f9528506727a5e13	849.1.2	millerdevel-gmail.com
-20111218215644	64b663524a465efb7707a2c634be97b9fb6f963b	801.1.15	wilmer-gaast.net
-20111219000031	9b0ad7e8334c7c01e8d9652aa3b1aaa6c5f6d211	801.1.16	wilmer-gaast.net
-20111219001738	bf57cd1bf1019decd67d7c835060675e6a030cde	801.1.17	wilmer-gaast.net
-20111219004140	4be0e3458a001a1c2eb3dd0074d7fd65260f2e6f	801.1.18	wilmer-gaast.net
-20111219105707	a0a3de6d5866e101fcc296f5c31735b47164d561	850	wilmer-gaast.net
-20111219125449	36533bf6bfc01f56afd6a8cd7bd3dfa9de87297b	801.1.19	wilmer-gaast.net
-20111219133910	4b53c65a82eaf2be3741a9551435432d051e2734	801.1.20	wilmer-gaast.net
-20111219135158	5a48afdf1a4dafcda8eecf42fc7cabb12ee48b40	851	wilmer-gaast.net
-20111219145058	486ddb53b93b6677dc3feeb4afaad2ea93a71a81	851.1.1	wilmer-gaast.net
-20111219172237	78b840187cc1e2d370dd758e6a73c21e510107b5	851.1.2	wilmer-gaast.net
-20111219172328	af5764e30a841b82bcba603fc06366442daa9c0a	851.1.3	wilmer-gaast.net
-20111219173406	25b05b75be1acdd4c96a301839be525809f35a47	851.1.4	wilmer-gaast.net
-20111219175720	a72dc2bb447e754295f8efc6f44fc6572f0f8511	851.1.5	wilmer-gaast.net
-20111219180911	9f958f794a1710f17eaae79c64f90f1f66134094	851.1.6	wilmer-gaast.net
-20111219184553	41658da57b611d17030dc7e2c3feb54f99b668ac	851.1.7	wilmer-gaast.net
-20111219204601	9ff0c256ae51f2039abc36940618a5d9fe5e6ba7	852	wilmer-gaast.net
-20111220095735	6c0fe9ba555df4950445775dfdcbd8cd1870d950	853	wilmer-gaast.net
-20111220111830	256164c49f83e85f5c5a04c9b659721b56cd35ec	854	wilmer-gaast.net
-20111220120108	f1c870a20c1c8a1173fe6244952518f015fc1346	855	wilmer-gaast.net
-20111220164217	f9789d46aac59f1ff28bc532d8589c1661fa7c4b	801.1.21	wilmer-gaast.net
-20111220164553	68286eb08dbb6c2aad555f155da6f16ee6f061e8	801.1.22	wilmer-gaast.net
-20111221104808	e14b47b826594772e4f3d0dbec1bf17153aa92b1	801.1.23	wilmer-gaast.net
-20111221112104	ce199b726735374aca84b2111bb19ec103478ebc	801.1.24	wilmer-gaast.net
-20111221114113	0dd65708997f53a829a9830fc584c99ca2e3e57e	801.1.25	wilmer-gaast.net
-20111221190356	31db81651fa3ac5d742c3616efaccf43a1ebcaf2	801.1.26	wilmer-gaast.net
-20111221193513	e306fbf84aa37ab934c5ea18ccfd75da041af052	801.1.27	wilmer-gaast.net
-20111222112318	ad46e4d3ed1997e6b3f718a7a8be9a37eb63388d	856	wilmer-gaast.net
-20111222161027	d6ddff0d4dae3d93de21fc37ca2e4df6d0cc3fab	857	wilmer-gaast.net
-20111223084702	2d93a51e15ac2d6daaac0d6ac1e2c41e33486c53	858	wilmer-gaast.net
-20111223124408	792a93b417c24a206d8995ca8bf51482f20e997e	859	wilmer-gaast.net
-20111223224017	200e151edbbcbb164e7fe2a01a28a0c1c9108972	860	wilmer-gaast.net
-20111224145235	5513f3e56a45d4a227bfc7d01210fdded516458c	861	wilmer-gaast.net
-20111224174912	96f954df218e81f5580257c319b91217dac2f4bf	862	wilmer-gaast.net
-20111224180239	164352ef9d3e8b4b2eea8ec64c3b4590b2388b18	801.1.28	wilmer-gaast.net
-20111224180905	34ded90e19635c7ebf2afd184f36b03abc879bec	801.1.29	wilmer-gaast.net
-20111224181215	9a1c14d8b636510242f81558f7f0a43918636865	801.1.30	wilmer-gaast.net
-20111226105034	644b8080349d7d42ca89946acc207592fd0acc2d	801.1.31	wilmer-gaast.net
-20111226105119	5f40da79f78e444f08387ce53da1b2e471c8552f	863	wilmer-gaast.net
-20111226181751	199fea6629504aac602c0c11782833b7354e629e	864	wilmer-gaast.net
-20111226185610	f1849a8c230909bc0a67faf2ff89d3b4f85ad242	865	wilmer-gaast.net
-20111229201812	3558feab3c18f12abdd1e927e1f20969850abd15	866	wilmer-gaast.net
-20111229203043	59cd92b87d700f943c789b0458c5d311f6fad86c	867	wilmer-gaast.net
-20111229215744	6451d2704fd0742680b485fb1d3690e251860073	868	wilmer-gaast.net
-20120103235328	59c03bd147dfaf5b849d2e68397cb63adf6146b5	869	wilmer-gaast.net
-20120104000151	761572639774ee1642229a24c625a569ef4ab48c	870	wilmer-gaast.net
-20120110151318	ac36463cce01e4c7a9c48a96dcd3fe37d1c89dc8	871	wilmer-gaast.net
-20120110233629	441b9dd255ed67ea78467135266d225964ad7041	872	wilmer-gaast.net
-20120113174923	948ababaf509050a83a32e629c6c327f087f6d07	872.2.1	daniel-lbe.rs
-20120127231615	7c2daf5fac2b9f83f3bd0e289c5cfd3ff0f20ed8	872.1.1	vmiklos-vmiklos.hu
-20120127232513	85341dd1d3c3ceabbdf512371eba912952914aa4	872.1.2	vmiklos-vmiklos.hu
-20120130212358	e618d85baf87763ffe7cc1f2b800a5845cf8dfe7	873	wilmer-gaast.net
-20120130212904	8d96b78e8fac994e4aa030f8696f4871dc6428ca	874	wilmer-gaast.net
-20120130214100	cf0dbddcc18a59682f66b230b1552e04373937b1	875	wilmer-gaast.net
-20120130222345	eb4c81aec5b2dab3f3146debda59e3dde1d4ec4f	876	wilmer-gaast.net
-20120130224225	d2abcae711eb4aca5b67e405607f40156ba0a29a	877	wilmer-gaast.net
-20120208002412	7d27962b17487ec1c783dc9e028859b01a20e6f0	878	wilmer-gaast.net
-20120210133708	e0a0a4277b51204743ffe7ece003602a50dc358c	879	wilmer-google.com
-20120210151438	bb2d198251e2001591e0080ec9500e1078e1cd4f	880	wilmer-google.com
-20120210180000	fc0640ec4530975b95f3fb14aff1fce86ffff121	881	wilmer-google.com
-20120210234928	b5fe39bf8c462cfd19428cc0c099490cef3fcfd3	882	wilmer-gaast.net
-20120211001409	00d6760b5bd611ea70ecacc190a475f5676a02b6	883	wilmer-gaast.net
-20120211002756	23445b601770e612be584294ad066ad45952542f	884	wilmer-gaast.net
-20120211125545	7fa5c192c40cedb4111f0cfd3aaad9126861588b	885	wilmer-gaast.net
-20120211130120	eb54f56f35b611d03913cde2244e7eab431dc271	886	wilmer-gaast.net
-20120211171846	4f37a98fe518297822ac47c09e6c034dbcd49540	887	wilmer-gaast.net
-20120211172641	dcf155d79a41a747ae73e51e028c6085f28dc30f	888	wilmer-gaast.net
-20120217102028	7d4ffc2ebc29ff8e6771722e8fab41cf690711f2	889	wilmer-gaast.net
-20120218105419	c59f594c9a11a427997a1416900372dcef75818d	890	wilmer-gaast.net
-20120218113731	fed12b9c4181ff829c03de812c7b18b3232fd860	891	wilmer-gaast.net
-20120219181809	74c5718c0edf48aa620176e443e89a5562b34d3f	892	wilmer-gaast.net
-20120222083305	52bba155211a9493ec5c97b7f73abd5bf41746f6	891.1.1	vmiklos-frugalware.org
-20120222104243	d3dd4d591b7731f8351110c83a97f36b184e6dc6	893	wilmer-gaast.net
-20120223125112	441a67e2d5032f1f2f9babd027c5fc6bb5676952	894	wilmer-gaast.net
-20120226092030	b958cb50be77eb9084b1701750ad738a62fb04f0	895	wilmer-gaast.net
-20120309233845	e3710118a633654916e28f0ae68064050d565b61	896	wilmer-gaast.net
-20120310000025	0872bb2af342aa078fe5ae1dd3ea116eab6ddb1e	897	wilmer-gaast.net
-20120310000629	57a656009b79afa0efc1fef360e6f7ca8a89ea01	898	wilmer-gaast.net
-20120312223635	d18dee42088f0bef9d09dbc7903590d755d19a50	899	wilmer-gaast.net
-20120319210150	fda194fe19a3e91b12387aeb9eda544580e48381	900	wilmer-gaast.net
-20120325224905	4c73ba621135a748cdb2707c2ef941d3b389b140	901	wilmer-gaast.net
-20120326224338	9b767e914297ca82051afec66a6dc20a4452a20b	902	wilmer-gaast.net
-20120410091458	632f3d45178f0d2810df934c32828a00912900de	903	wilmer-gaast.net
-20120412212754	58b65b33cf6df50b89c3a0cc43ea226c208c0d1e	903.1.1	vmiklos-vmiklos.hu
-20120412213712	6d23c684bbe3d4b919dc75fe39ee8bf0f5fbb916	904	wilmer-gaast.net
-20120414095534	e93fa057d26a4b06d17b619de541e3cba3006008	905	wilmer-gaast.net
-20120422214809	9ce44dd377c6a07f6bed78dcbfe83b5b00e15b6d	905.1.1	vmiklos-vmiklos.hu
-20120422215357	3c88759d7082945ef2d865f6b0c7a01213547a34	906	wilmer-gaast.net
-20120422215733	d527913c458a5e4713af18e019ba356dd369cbcc	907	wilmer-gaast.net
-20120502075822	18e1f3b157062e2daec877157e4868593e984656	908	wilmer-gaast.net
-20120603210106	25b80e9cc7f74ec3df47c2ed25933065832bd7f4	909	wilmer-gaast.net
-20120603230214	7de784c55f796f80523eac5fa25d6728048469b0	910	wilmer-gaast.net
-20120603230843	7a2a486ba0f90fef07c0a758ebdf36acc253e1a9	911	wilmer-gaast.net
-20120603233101	7d2ce9aeac8ddd4ba3116fe38f502491d9ab4f80	912	wilmer-gaast.net
-20120604105851	c43f4886bf2e0c94dca1ac69ac7415e21f59d066	912.2.1	wilmer-gaast.net
-20120604110150	0153ba9a0dbed83fa98281f4d1ad34ad08e379b5	912.2.2	wilmer-gaast.net
-20120604135558	a338faa2e8262c62b3c871c547e91654c5d206ef	912.2.3	wilmer-gaast.net
-20120605190914	00f1e936e59e563d7dac295bb7de524354920853	912.2.4	wilmer-gaast.net
-20120605194722	d219296cacb644eccbbb8bbd58d02eaa89904c74	912.2.5	wilmer-gaast.net
-20120605201956	222b440e3d85e4a45a47f79f017ff53dbcb78276	912.2.6	wilmer-gaast.net
-20120605223413	15581c17687d50b36045e4a0276943b7d1aef188	912.2.7	wilmer-gaast.net
-20120607224059	aa88e500329f2d9bbdb4915c6d46e7078353a879	912.2.8	wilmer-gaast.net
-20120607224117	bb5ce5686638a490d21884c0d0abb7d9ced68a29	912.2.9	wilmer-gaast.net
-20120607224524	e222c36bb432c77da90ce39609ad4ba472550bb7	912.2.10	wilmer-gaast.net
-20120607231115	297d1d029057d06d1e5b36d4a156ddd6edf379ed	912.2.11	wilmer-gaast.net
-20120610221252	bfafb99e6162b72e0f1ca7639de05f2b7bb3b23c	912.2.12	wilmer-gaast.net
-20120720203253	1a0b7340a1df6ec832926bd83580ac248b7af207	912.1.1	vmiklos-vmiklos.hu
-20120720221808	5ffa4f74c458f6e5617d0756ce76fcf56aca3482	913	wilmer-gaast.net
-20120728113232	809cd379e85beaa2117fe56f29d54cf91d4aef59	914	wilmer-gaast.net
-20120819135318	84be1b6eaa18a3b18e550ef50920d87117c2ae4a	915	wilmer-gaast.net
-20120819135501	d3bfe3d0e6a0d0c655556bba838f2dcbf878d20f	916	wilmer-gaast.net
-20120819140748	6ee51a914b26fec1f7036af758be514bbfed9778	917	wilmer-gaast.net
-20120819153355	68709f5618175d0ecb9b2b160cbb2ce2d4ddc7bc	918	wilmer-gaast.net
-20120915155917	7281ad13e9dadb1369590617eb06265b084a1726	919	wilmer-gaast.net
-20120915160333	b3d99e347e66e1559b86e7d3bf78ed8b087ec947	920	wilmer-gaast.net
-20120915162452	b61c74c0e0941577b551cf54d8965893090ca282	921	wilmer-gaast.net
-20120915162632	ddca104c8c6e75e0b70bf804a23c98a78946fbdb	922	wilmer-gaast.net
-20120916000958	bc676acb5f316f0688ab9f99dd2b73315a6ad50c	922.1.1	wilmer-gaast.net
-20120916115235	f9258aead615ef9b87baa1754777fd5faf867fe1	922.1.2	wilmer-gaast.net
-20120916135219	080c43a862bca535e51682423afc9d0f75ea1d02	922.1.3	wilmer-gaast.net
-20120916174044	79bb7e412d28e8afe734762e8bd3e4c488a53562	922.1.4	wilmer-gaast.net
-20120916180815	e9caacd00fc7f7682664d29567a5b7dcfbde7479	922.1.5	wilmer-gaast.net
-20120916204211	fe79de663c230f7d4b525a5052da39b1d10c3b9d	922.1.6	wilmer-gaast.net
-20120916214409	a325ebd591a7c54d5fc34a2ac313d0a723f54e05	922.1.7	wilmer-gaast.net
-20120916234718	daf544ac6a08ce2fc0f9bd2b07c70ea349a41e30	923	wilmer-gaast.net
-20120917214227	c6fc24a81fa14cea4b3f497bbbb0e6e65a3cc6d4	922.1.8	wilmer-gaast.net
-20120922115201	55ccc9a0dffdc96b7a5c6d41de2e97d2cd1741d9	924	wilmer-gaast.net
-20120922121212	d0752e8b08d37395fd036046552fa6b4fb92ac17	925	wilmer-gaast.net
-20120922124447	11ec07811cc41e1b244467d25772ef021be9db1b	926	wilmer-gaast.net
-20120922124755	9c77fbf0850c364c31f80e30c54137252291ec91	922.1.9	wilmer-gaast.net
-20120922125502	6bef2110ddeb9e1f8e8233a4e61b8917fb939b29	922.1.10	wilmer-gaast.net
-20120923220952	a07a8c245e4bd5142e382f029513eb92f040ae6e	927	wilmer-gaast.net
-20120923232532	6f55bec04d9dc076a8e815f51b93b2391d76f973	928	wilmer-gaast.net
-20120925000854	2a6da96544675726c73a60b588dc26e477ce70f5	929	wilmer-gaast.net
-20120925001005	ba8671363c6d3dfbaf54029f35b3455c46cbc7d1	922.1.11	wilmer-gaast.net
-20120925224856	3901b5dc7145de0db36d4a8820836daf91febc24	922.1.12	wilmer-gaast.net
-20120929193818	208db4babb7a9be245cedd3a0a4758891fece03d	922.1.13	wilmer-gaast.net
-20120929194919	4c9d3777d99191786a449912989a66a44a038383	922.1.14	wilmer-gaast.net
-20120929214946	a992d7aa04ce3224429e3648c49abced2edf1b07	930	wilmer-gaast.net
-20121001225139	06aed9a22b0218740f8130486b797f12f36ba605	931	wilmer-gaast.net
-20121005225653	4fdb10292a8e28210cdb08107dfd78a248646df0	932	wilmer-gaast.net
-20121014203534	13df5155b5351d8804d3cf81d6cc9280aa53f387	933	wilmer-gaast.net
-20121017072300	1444be5c948f8b4509cfe3cb30992da74f12b5b8	933.2.1	michael-stapelberg.de
-20121019223910	0eb971a6d1a996fbb76eae815145a88abebad75d	934	wilmer-gaast.net
-20121019231652	386042c08547bbe7f0b62ad97cdd8488cc13e97e	935	wilmer-gaast.net
-20121019233833	6042a54e827b36770e8989d9a8ba49a66ec6f846	936	wilmer-gaast.net
-20121020000245	509cf60140e5d757c79558a7b39145fe3c0cb2b5	937	wilmer-gaast.net
-20121020094459	3cd37d784147808b2d93218b7eca3413a124b1b6	938	wilmer-gaast.net
-20121028201701	3079db84d79f4b332949cad5b8e19e2067924077	933.1.1	wilmer-gaast.net
-20121028201756	4d82e50a6bdc15ed39723c1dac23027b83330be1	939	wilmer-gaast.net
-20121028230502	696dc9e555f335d3a97a5815f9a395e5e4ce0e90	939.1.1	wilmer-gaast.net
-20121028233655	ba654ec24a992828e11a8f0cc22e0ecbf21a6c3f	939.1.2	wilmer-gaast.net
-20121028235421	91ae87dbaf907a767b9bf881c6f7de39d03be45e	940	wilmer-gaast.net
-20121029001612	f21ad3756540aa93c786d1a3e00d927e659daa67	941	wilmer-gaast.net
-20121030232842	addad71d1dd6ac34dd00c2ee0d462f84f9cc2a84	942	wilmer-gaast.net
-20121030234143	8f976e69b218d89999cc4fd58721243380791fbe	943	wilmer-gaast.net
-20121104222307	3b4a22a8ca8a88122fa5fbf51218a5e6e65a90c8	939.1.3	wilmer-gaast.net
-20121104233901	c08d2016c16d1748c4c527c8055f90f5a7669004	939.1.4	wilmer-gaast.net
-20121104233948	e08ae0cff03ff880344cb3dc75cc90576cc314b3	939.1.5	wilmer-gaast.net
-20121105084606	0688e99d12297b6dda980803d1f1a54bf8590299	939.1.6	wilmer-gaast.net
-20121108223820	8e3b7acdfc96e086f1cd1c606e46d9a91d46a842	939.1.7	wilmer-gaast.net
-20121109000723	fb351ce6b6cdc714019f49f693182e32055fd78b	939.1.8	wilmer-gaast.net
-20121109002344	5246133a607561abe8096c471de02bddeb6be67c	939.1.9	wilmer-gaast.net
-20121109234822	5d749adc3e7698ac3904b3e8f823cb9628f9432c	939.1.10	wilmer-gaast.net
-20121109235009	398b2533afe5860e1b83edb0fcf5d7cf80207162	939.1.11	wilmer-gaast.net
-20121109235345	24132ec9af8b275fb4b94e347c6ccce0017c27d0	939.1.12	wilmer-gaast.net
-20121110123133	9e8c945b0c0e794fdc33413ad87feb4606a07981	939.1.13	wilmer-gaast.net
-20121110222558	8bd866f3d336cb695006bc48592cf7475139aa36	939.1.14	wilmer-gaast.net
-20121110235221	ddc2de54664ec25b95bbce997fbbb6a7104f1203	939.1.15	wilmer-gaast.net
-20121111144220	dff0e0bdf3acea7d301242421b4fa906ff46278d	939.1.16	wilmer-gaast.net
-20121111165356	62f6b45d2056b90f57b908cd9bc9b6a995365c43	939.1.17	wilmer-gaast.net
-20121111175720	1388d303ba0d2097ff745d4a17192195cebbd349	939.1.18	wilmer-gaast.net
-20121111182239	2fb1262a8200ec05d1b3334103fb7182dc2b2fa7	939.1.19	wilmer-gaast.net
-20121111215226	dd672e2c4d0dcf73a30be3d8f7fc2ec38cb6450e	939.1.20	wilmer-gaast.net
-20121111221307	536dfa1e71dd9fbe90ea5be8b5b327ae2fed95fd	944	wilmer-gaast.net
-20121111233247	e132b60e77f395463cf95dc4ee09e96e9658ae35	939.1.21	wilmer-gaast.net
-20121112000333	67ebc8e0063e18f88752da04e9576330193e2e13	939.1.22	wilmer-gaast.net
-20121112001429	aef207752aa19700bf5b68531243c36a6c8e02ca	939.1.23	wilmer-gaast.net
-20121112144538	e1d3f986ddad6140a25f3feffc9e28da8fc2318d	945	wilmer-gaast.net
-20121112235743	d1356cb8b0f964ddf7de50e1ba52eecc271e470a	939.1.24	wilmer-gaast.net
-20121117235121	b0064647d06d4169f2b49f4f527ce05af43aacff	939.1.25	wilmer-gaast.net
-20121120002010	5aa96fc81a66224d9218e54d70944b1ef3d26e2d	939.1.26	wilmer-gaast.net
-20121124001517	c751e512afd4dc200c04a70cc6d0edb94e63e8b3	939.1.27	wilmer-gaast.net
-20121124141034	f26d9a3e54a89b640902d6ffad4f3d32b76dc97e	939.1.28	wilmer-gaast.net
-20121124192057	5f2f72849ec0713d65b709e6962aeaac25aa89c7	939.1.29	wilmer-gaast.net
-20121125005547	c9b5817211cfcbf66a44c6a347245d5913806f91	939.1.30	wilmer-gaast.net
-20121125010553	e9bdfbc7906507289a5ce95fb1704e7cdbbb564c	939.1.31	wilmer-gaast.net
-20121125114723	898c08e1d8abde8cc842ae619281b6cf4c1b7fdd	939.1.32	wilmer-gaast.net
-20121125120548	2cd8540c1076c3d0423abb1927bd47fdcbfbd382	939.1.33	wilmer-gaast.net
-20121125131119	96dd574444f2c99bb0a82b2c354804f03e306f23	939.1.34	wilmer-gaast.net
-20121125142623	631ec80ccd9d3deeb590ae9537ad4260d804363d	939.1.35	wilmer-gaast.net
-20121125145829	7f557d53d986c5f688c5781fea04a46464e5b491	939.1.36	wilmer-gaast.net
-20121125174313	29f72b7a4b4fa960b6fccb45243fa3f5d87a99bf	939.1.37	wilmer-gaast.net
-20121125183911	9ed00818d7351ca5bcb4ba16b5dfe900d45831d5	939.1.38	wilmer-gaast.net
-20121125202124	3592b95ac23e243425e20ff9f69f407cb5ec4a74	939.1.39	wilmer-gaast.net
-20121125204352	f97b8e9637acba704e976dff79436a83c0f9c63a	939.1.40	wilmer-gaast.net
-20121125220941	67f68282bb20ad3af6dfa6017b89b89ab0a1767f	939.1.41	wilmer-gaast.net
-20121125222838	b235228768645c11d3ef138ddba839c7dd529567	939.1.42	wilmer-gaast.net
-20121127232034	85cd12daeea9dd537566735ee426974a19533337	939.1.43	wilmer-gaast.net
-20121201121344	e8161ec5bf893d935148fe48925d1315f3aab949	939.1.44	wilmer-gaast.net
-20121202124003	2c18fcde3ac966d9aa49a3520881f25fb8c4f985	946	wilmer-gaast.net
-20121202125312	3ca001bcb054e861ef9ad6e818fbb6615a519804	939.1.45	wilmer-gaast.net
-20121202144046	b3eee9b8d9fb2b1b41fe977fe6b97e3609f6bda8	947	wilmer-gaast.net
-20121202163259	b0ee7200338e3bb41b1c770185f368f0345c2f55	948	wilmer-gaast.net
-20121202173922	3bda2c2bc34fa21ef143f068a4f4f3feb7eb48d5	949	wilmer-gaast.net
-20121203233033	2dcde94625f2ae1af1aef674c2aa1e2bc714d144	939.1.46	wilmer-gaast.net
-20121218012433	92d30446251591a6805168f51a4b07ff65b3cc24	950	wilmer-gaast.net
-20121221215005	573e274c58bf7d154b35ab5cd9d0b711f7ede715	939.1.47	wilmer-gaast.net
-20121222001426	cc6fdf8fe5a044db58ed74e69673cf4270080d45	951	wilmer-gaast.net
-20121222002024	7d5afa6405cbe253fd55d815f2c785c581a13e0f	952	wilmer-gaast.net
-20121223232734	c1bc24a856826973153c386e6ad52dcabb80a21b	953	wilmer-gaast.net
-20121223233656	bbc69f7ab73e28828c775178d1f00398b1bf26dd	954	wilmer-gaast.net
-20121224125126	3f661849f8dd01f0b98f0b5d866b3a603c87e048	955	wilmer-gaast.net
-20121224132802	def3650fba32bdcefd6985ef339f38c3e528b8fe	956	wilmer-gaast.net
-20121224191737	9b672857d1d395307f1bfac988a347d724e13eee	957	wilmer-gaast.net
-20121224220241	c1d9c952063904cc492d9538bdd8b75b20c41cd7	958	wilmer-gaast.net
-20121225000833	14d0b021045b5d92939a795fc244f0b12a66726a	959	wilmer-gaast.net
-20121229104424	3c7af69632578033cc7dfb1b8fa83ee4c1221b97	959.1.1	vmiklos-vmiklos.hu
-20121229105653	e6298e5a2b57a12ff0a10e15030823af89c734cb	960	wilmer-gaast.net
-20130101153122	eab8e52503885305ea652bf99a48f7a584ddc1cd	960.1.1	vmiklos-vmiklos.hu
-20130101154431	7764fb1ce63f1339f005e698ddfeba96186e9925	960.1.2	vmiklos-vmiklos.hu
-20130101160434	e1d6b38fbfa1ba01da45c79a2f1b0951eb5b14bd	960.1.3	vmiklos-vmiklos.hu
-20130101161909	757e1e0fdd0d80818a480d507e41b5a8e32ceb3e	960.1.4	vmiklos-vmiklos.hu
-20130105134756	62a2bf92daff0756244feb3940eeb46b66ebce6f	961	wilmer-gaast.net
-20130106120935	cfbecc914ca34f313302186d4d3f716f9137c431	962	wilmer-gaast.net
-20130106125546	a906b776ced5e28c80e78cd6ed160c43ba722c04	963	wilmer-gaast.net
-20130106233617	955aad87ef1f4b999d2983c75def14e87983ff62	964	wilmer-gaast.net
-20130106234005	106f19c8e09cda4a0cc419d3d8fa05839e66e268	965	wilmer-gaast.net
-20130113111126	4cb21b79b3ade188b628dfc877cfa5fd6f8d5d03	966	wilmer-gaast.net
-20130113114121	9ec6b36ca5b9ee1a2a9311c4c8ebfa18c0c65b1e	966.1.2	vmiklos-vmiklos.hu
-20130113114121	fffabad4863460fde5906e71c6c3cb79a4c4bab3	966.1.1	vmiklos-vmiklos.hu
-20130113115102	650d2b479d6aa079f41c620c15b47764ac91595a	966.1.3	vmiklos-vmiklos.hu
-20130113120601	35571fb5d713f61bba6a4a3a91aed8c41827cc66	967	wilmer-gaast.net
-20130114223259	75bda8b5af30cbe659d7bdd4d06fafbea85782ab	968	wilmer-gaast.net
-20130114234010	a9a659869d1f3a128e99827faad7ec353f99377f	969	wilmer-gaast.net
-20130116202658	db0d979a326f173e46816dcf2d56fa30c15b2adf	969.1.2	vmiklos-vmiklos.hu
-20130116202658	d4b5e5b2d872ff6ce959c7de46ab8cd473129794	969.1.3	vmiklos-vmiklos.hu
-20130116202658	a6a2d818c217a75e7459d645c5229132be2d3295	969.1.5	vmiklos-vmiklos.hu
-20130116202658	daac7551001c309f1d9d6ac25361398f0bd2b471	969.1.6	vmiklos-vmiklos.hu
-20130116202658	24871120bb05d3afe1f80218eef848aa1b94545e	969.1.4	vmiklos-vmiklos.hu
-20130116202658	6efb777b54d3b8db05252e3d299085ab2baf515e	969.1.1	vmiklos-vmiklos.hu
-20130116202659	f6cab7cfccdb6ed517aaaa458482ea465c6c9fd7	969.1.7	vmiklos-vmiklos.hu
-20130116202659	69ae6f2d4394a7df0c7d1b4a27c89da6b7cb99b5	969.1.8	vmiklos-vmiklos.hu
-20130119113627	be98957a88f66215ab6fb84810a1b2d394eac879	970	wilmer-gaast.net
-20130119192519	b2dc8737af37d803edd475ec713bcda1b540124d	970.1.1	vmiklos-suse.cz
-20130120100403	fe9292106940f8cb81dc9aed2263e3a5f5ecd02d	970.1.2	vmiklos-vmiklos.hu
-20130120100410	db34b0671c81b8021639b07a704e2e05b38b9f40	970.1.3	vmiklos-vmiklos.hu
-20130209113143	875ba16b0224b9bf50a0ea15aa96fe545c76b845	970.1.6	vmiklos-suse.cz
-20130209113143	c2cc24cd9d7a976aca383e508f2c6779d6cdb14a	970.1.4	vmiklos-suse.cz
-20130209113143	ef64837002edb06f2e6048424749a52606d9e88e	970.1.5	vmiklos-suse.cz
-20130210124832	e4f5ca868fc4313a6a9a00c5eda0a03767abc703	971	wilmer-gaast.net
-20130211125603	5a0ffa2be7f6e5354975abd6c17fffb43bb4d694	971.1.7	vmiklos-suse.cz
-20130211125603	ee3bccddcc2c197444be1bb685aa893e0ae2f910	971.1.3	vmiklos-suse.cz
-20130211125603	1146e216e8cf8c6fca77f3b5325ccd86078f827a	971.1.2	vmiklos-suse.cz
-20130211125603	3e238504209e998064f0906c9edfced9713ba94d	971.1.6	vmiklos-suse.cz
-20130211125603	65a5d154b052c9ff35a79eaf8e3405522c8737cc	971.1.1	vmiklos-suse.cz
-20130211125603	b56c76ccb4f9b0e2ebb220ff2b99b7a1f841b9a5	971.1.4	vmiklos-suse.cz
-20130211125603	7e5b4bd31383187b04dedec4854d8c89ae2aea7b	971.1.5	vmiklos-suse.cz
-20130211125604	b6ab05bd13a498204ae322c9d9fd1eb5ec6abb30	971.1.11	vmiklos-suse.cz
-20130211125604	4da4e9b8a8260a55f734fefcf80ce64fc6dbdfc5	971.1.10	vmiklos-suse.cz
-20130211125604	8e166ae8264a7c9f98e766c8635faa94cd075eb8	971.1.8	vmiklos-suse.cz
-20130211125604	5a4f22e15ec95696b02d9592fdd1b24adae9674e	971.1.9	vmiklos-suse.cz
-20130214152000	61d2eabb1651e771937ea34794a501115d8cd49f	971.1.12	vmiklos-suse.cz
-20130216132311	33ed4558048f8dd5a29275658b3d1892b7b4dd74	971.1.13	vmiklos-suse.cz
-20130216132745	3bf9f715e80169d117c6f63b273639f91b985ea5	971.1.14	vmiklos-suse.cz
-20130216133346	78e103e7e5c1d58a59d65a4e90cd5f9fbee92cee	971.1.15	vmiklos-suse.cz
-20130216140356	c7336baf84c5122bc8377c7a5e836152187bb5cb	971.1.16	vmiklos-suse.cz
-20130217130651	3a920204f1bbeadf4971530ee91514b89652610b	971.1.17	vmiklos-suse.cz
-20130217195111	94d9511f4e834f6082c2eadf6d1d35a65d162155	971.1.19	vmiklos-suse.cz
-20130217195111	130f7b85cf0d75da8dbb98df6d52595fe24c65c9	971.1.18	vmiklos-suse.cz
-20130218084230	9754c2fd0e4dbe65e13878b947091483bfff102a	971.1.20	vmiklos-suse.cz
-20130221131626	12f500f085fe715b62e13c9b67b23f952c100a21	972	wilmer-gaast.net
-20130221140315	06eef808b0ce5d7d7056240be6286aa79ac6a9ec	973	wilmer-gaast.net
-20130221180919	a5c6ebd43dd69a7c4c2648ed09a7ebaf53cfc1b0	974	wilmer-gaast.net
-20130221183706	03886fc7d06f1414a55b918247ba0310ab16e41c	975	wilmer-gaast.net
-20130221190355	f3fce538d72411d03ce0056b04b2b302fa53e1ed	976	wilmer-gaast.net
-20130221191559	0e788f52f992be8792a4cc1218b378370326e5af	977	wilmer-gaast.net
-20130225203805	c440320f9df04f05ead97bc7a7a016e23e49bb64	977.1.1	vmiklos-suse.cz
-20130228071549	36f6ab3f1534545ceccd5363962a2967667d48fb	977.1.2	vmiklos-vmiklos.hu
-20130301102844	b2b7f5247199ad9b49e8a1f095b040fa0f4b9807	977.1.3	vmiklos-vmiklos.hu
-20130303144859	ed3ff435ac2caf34de6b67ae4b6b3f8a04c7ebb7	978	wilmer-gaast.net
-20130306194118	8407e25f982c69d8b2cd01bc5802f3d45fa9dbf1	978.1.1	vmiklos-vmiklos.hu
-20130306195619	52ceb4b1e90f1b8640e7a232a3fe02b2748ae1b2	978.1.2	vmiklos-vmiklos.hu
-20130306202113	83c246e68c5c6f9c25aa9e56d8fee3a14201bfcc	978.1.3	vmiklos-vmiklos.hu
-20130306205015	f4eb3fc7c5eb6c0c035b728f6e820230fc8a1e2c	978.1.4	vmiklos-vmiklos.hu
-20130307154531	2363ccab98e9bb81414eabad64e60b590f7a5fc0	978.1.5	vmiklos-vmiklos.hu
-20130316202535	6f360a09b781e0c76b2486f5d33c50c507fb8bba	978.1.6	vmiklos-vmiklos.hu
-20130316203132	215e17185699655c10a6f7e190c47f3b3f47d9d0	978.1.7	vmiklos-vmiklos.hu
-20130317130325	9b2a8c10b61540c3c6892a4de7f52bf8657d455e	979	wilmer-gaast.net
-20130420130555	e31e5b8f340a162180830dbe42dd438e59591cfd	980	wilmer-gaast.net
-20130420131722	dd95ce431b5c85eb6d74e501a7796e8a6016ec70	981	wilmer-gaast.net
-20130420225031	e277e80022e9cad3f7a3dbadbc25a6a2da9bf40d	981.2.1	wilmer-gaast.net
-20130421193939	57b534b1cd734e2e55abc74e7c25401cd821aeda	981.1.7	vmiklos-vmiklos.hu
-20130421193939	0a7a7e9d64393c52a301f3adea2f2ddcd7f36be9	981.1.8	vmiklos-vmiklos.hu
-20130421193939	a016221c3dfcd5be64192b00d8c4b38d408d41c3	981.1.1	vmiklos-vmiklos.hu
-20130421193939	505fdaace26255378516723d8586a698c15f5674	981.1.6	vmiklos-vmiklos.hu
-20130421193939	b201c68402b2263d22c85354d3535517763bd1a5	981.1.2	vmiklos-vmiklos.hu
-20130421193939	3d1b06f325c585bab69bfb42920fb8fd9df15e52	981.1.5	vmiklos-vmiklos.hu
-20130421193939	500419b0eefd5b2ab42ba3df82d9b498b843a398	981.1.3	vmiklos-vmiklos.hu
-20130421193939	e2ac666852ccbfb5b635d68562664169cf9b6916	981.1.4	vmiklos-vmiklos.hu
-20130423080610	c2a863db316ef3297bf91608854d8b16c1000f2b	981.1.9	vmiklos-vmiklos.hu
-20130423162006	c6088915e5628bca1de0e5464f54d9d8915e8984	981.2.2	wilmer-gaast.net
-20130423162810	5cb946132871ef97fe9eabacafa62f1064d80423	981.2.3	wilmer-gaast.net
-20130501185550	f539d6ecaa67f154839669ed6d2a7fe9d24e4ab8	982	wilmer-gaast.net
-20130506151349	af496d8e85185e99d3b8f9251508a401d2831c06	983	wilmer-gaast.net
-20130519073354	a7e6ba915ccbd4ade369f61a2b4c8ad082f90eaf	984	wilmer-gaast.net
-20130523214654	be9f3f1b0e84c358d93291ea5d4f91580cbd1ec5	985	wilmer-gaast.net
-20130525103634	c1538088cc9d9a0bb573fcf4a31248acd26254e2	986	wilmer-gaast.net
-20130525105409	ea166fee8cba5a254bbd46f4631f6c3fababc9f0	995	wilmer-gaast.net
-20130525123942	420ddc005a5edd19ee5e0cb299e46b37f283d974	987	wilmer-gaast.net
-20130601000952	f2d8aa21d47f2c5158df7dc51abce19611e9a81e	988	wilmer-gaast.net
-20130601001818	aed00f8ea574486594dd58916da5ef079771b309	989	wilmer-gaast.net
-20130601235855	41a94dd69dcbb5d4ef1fda5949196fed63994c03	990	wilmer-gaast.net
-20130609211745	ca8037e28d09ed96448509316a935eb130e6d3db	990.1.1	wilmer-gaast.net
-20130615233353	777461be5e14d3aab9a51f19b5d2621309141a9d	990.1.2	wilmer-gaast.net
-20130616001411	dd7b931d0fe950c5a6646c72565739fd8835c136	990.1.3	wilmer-gaast.net
-20130616121515	ab19567e25a35beb23f922303d1f60ed13228356	991	wilmer-gaast.net
-20130616124239	1a2c1c0c413ea1124544cdc9a24a0d2faa5dbb8f	992	wilmer-gaast.net
-20130616173358	2f9027c9d6fbc50226e94305afe1cb0fba9deeca	993	wilmer-gaast.net
-20130622215015	7e84168ee2e8eccefe5522def6b0a01c00b13767	994	wilmer-gaast.net
-20130701224302	c92ee728d0d3741ce9094b076bf12a5a2e0e3c66	996	wilmer-gaast.net
-20130713184913	a685409f7e5587fa123b380775afd1586d938358	997	wilmer-gaast.net
-20130801154748	81265e00081cd27e52bdb307eae63fe59193c75d	997.1.1	pesco-aerger.sm.khjk.org
-20130801181702	5d2bc9dfec01f717068b1a38e538c46c2f7e1e70	997.1.2	pesco-khjk.org
-20130801193224	352a6b0bd8ecb0d6dcb32c4f4b767fcd315fdf06	997.1.3	pesco-khjk.org
-20130801203949	090c9b786284d1e2c6f37195aa56fd528f08b2f7	997.1.4	pesco-khjk.org
-20130802104803	b939cff59bfd94243bcde994232c72e5b9e8e8c5	997.1.5	pesco-khjk.org
-20130802111521	6d9f0ba48e127bbc9050d674772248b915f9fe35	997.1.6	pesco-khjk.org
-20130802115222	22ec21d76219508284af193d37cc4a2cbab65e7d	997.1.7	pesco-khjk.org
-20130802153011	fa9478ea664824841c013615c0c4b324c8c78c0a	997.1.8	pesco-khjk.org
-20130802180834	37ed402c7c34c56f9a8d352a67dc15f48906adbf	997.1.9	pesco-khjk.org
-20130802183144	f93e01c92315427c2b8f8c7d03e0ce337e2e80b4	997.1.10	pesco-khjk.org
-20130802205516	c347a1225f8a114ff540a38fed31c5543b4d9fcc	997.1.11	pesco-khjk.org
-20130802215943	fbcb48126238e224ca0eef324136dc20868e428b	997.1.12	pesco-khjk.org
-20130803124903	e65039a9b52902758385a46f8baa4b2395d09636	997.1.13	pesco-khjk.org
-20130803130630	e4752a6162d27e7292df912ab3bbf71f214e6cce	997.1.14	pesco-khjk.org
-20130803133653	51f937efd8653a3dc79ba1dbdb93fc2c69c78504	997.1.15	pesco-khjk.org
-20131014160204	d4a4f1a076eea0bb50ab30fda5323328dd8df568	998	wilmer-gaast.net
-20131111224636	e73e165ba007cd0e0960da2942f4b10334eaf4af	997.1.16	pesco-khjk.org
-20131127225403	08224f59156c251c1ce9c4677d6c0f4eb76de18e	999	wilmer-gaast.net
-20131205091904	943bda7dd7dee1de1295f1e927a55415c633bee6	1000	wilmer-gaast.net
-20131205092022	434a2d0fdc34d9dd70b6001220a8597634efe65a	1001	wilmer-gaast.net
-20131207124928	a8aa823a772724abd7c61ee457b208afa44e4e7e	1002	wilmer-gaast.net
-20131207174852	48b75f17f249376e33a8464d86b6cb750c860009	1003	wilmer-gaast.net
-20131219001626	cb16fe9b4c9bcb20c8b5e3f0433282bd2bf9da18	1004	wilmer-gaast.net
-20131220185841	4dda9e48dfc7a577a5e831e3e5055b578201d114	1005	wilmer-gaast.net
-20140120220053	cc17b764196c160e665574ce5bd1bc6e6daf95ee	1006	wilmer-gaast.net
-20140201235900	367ea3c4533d9e72b783636ab16f0479788f6276	1006.1.1	pesco-khjk.org
-20140202003149	329f9fe22af0e8c69fce051a29e6db4ea9d917a8	1006.1.2	pesco-khjk.org
-20140206225119	71004a373c3533cc643740adf5ad9b626ba4ac72	1007	wilmer-gaast.net
-20140207160040	c239fff45764798331d5ce9b3190fa286c0f83f9	1008	pesco-khjk.org
-20140208134630	e76cf264de6caaca24fa308cab78e770fc4a6508	1009	wilmer-gaast.net
-20140211173107	74c9e7fb4393f160b45166b1c414a1e9996ca11b	1009.1.1	pesco-khjk.org
-20140213084837	7a80925eabe969ffc1e8e24c2ad31c80c33ab3cd	1010	wilmer-gaast.net
-20140213194453	820a2a79b97c96e63b984b17802e690ec16e3265	1009.1.2	pesco-khjk.org
-20140213224102	6ae1056f266bb09750de640903a45b6806477665	1011	wilmer-gaast.net
-20140227234635	cce1b639e35daf28c8e47fd3a094b27990ea508d	1011.1.1	dx-dxzone.com.ar
-20140227235333	6a0cb16838b1f68d698d690424e0f448d1b152fd	1011.1.2	dx-dxzone.com.ar
-20140228000003	1bed2e1eb4c1e06be072f46c128ad05fe4c41342	1011.1.3	dx-dxzone.com.ar
-20140228000307	ba3506ed6a319779e3c1f10355fcafb34354c6a2	1011.1.4	dx-dxzone.com.ar
-20140228000722	b42269a3546bf1eb80c99952bee0cec7f344067b	1011.1.5	dx-dxzone.com.ar
-20140228001858	489847f472faa9418ffc83b4cb211bafb9757494	1011.1.6	dx-dxzone.com.ar
-20140228005343	172aa37f7162ebdca779c96a23611a63b5ff0579	1011.1.7	dx-dxzone.com.ar
-20140228101002	cb90dc9fbd1e2a76ea382376ca69248398f70719	1011.1.8	dx-dxzone.com.ar
-20140228231411	2e815e5627b54696d4a81cddd8162fa0414b118f	1012	wilmer-gaast.net
-20140228231728	35987a1c74b708cd9ba2dc351400edd4d4046a6f	1013	wilmer-gaast.net
-20140302003743	b4b8f1e2bc776969d74ed78bcd44b70a2293a8f3	1014	wilmer-gaast.net
-20140307000232	b4008a52254debf3a2c348c9e4a710d3bb8dc8ec	1015	wilmer-gaast.net
-20140420165923	3e16fb823e4921ef85eac7730794a2c3de184c1a	1015.1.1	jelmer-samba.org
-20140420170048	6a6d7d8f10fb72b195cef814e473917d984023b4	1015.1.2	jelmer-samba.org
-20140425080359	7b05842983ffb53f99d88ae778bffd035b87c613	1016	wilmer-gaast.net
-20140626080711	f287f0424a983b1f542aa2cf95ecb44810dbcfb3	1020	dx-dxzone.com.ar
-20140626080711	3e7a722cecec3700d219cd401a9ca0521485c5a5	1021	dx-dxzone.com.ar
-20140626080711	4e4fa932b450eae70e102abe8f2daeffe60e4614	1019	dx-dxzone.com.ar
-20140626080711	04e2a05051747cea98fabb7a1232a8e64b1e4a68	1018	dx-dxzone.com.ar
-20140626080711	b95b0c8837f3a9416f910e7e90d8e08681bf7c9d	1017	dx-dxzone.com.ar
-20140701222658	0edb57f7d14c2916979f6d77f06ea82843703f90	1022	wilmer-gaast.net
-20140705213723	c564e252a0656682d8fbfbeefdd703eff2d844b2	1023	wilmer-gaast.net
-20140705213825	6f6b6c7237923351e15ff9a69e714d4a33b056bc	1024	wilmer-gaast.net
-20140706081604	1d20e4dd51c150ec96cfccbd4cb7f7499c65906d	1025	wilmer-gaast.net
-20140719231932	77fc1708b72541cfcf95f4d4680c6dca9ca3caec	1026	jelmer-samba.org
-20140719231945	ea2828ffbf38b73d781e7c5b27ec09b50c6caf1b	1027	jelmer-samba.org
-20140719232113	3cabceca09cb2f9c75f8b1a88bf44b0dacef193d	1028	jelmer-samba.org
-20140719233130	0d8add8a3431cfb6b55bc836236e4b3433c8ac27	1029	jelmer-samba.org
-20140719233148	6a55a180a2d9580e21ff943b9c8581e35d5bb565	1030	jelmer-samba.org
-20140719233212	d397000c270364ccb37a7d2b56ca94dc94f8b6d7	1031	jelmer-samba.org
-20140719234043	5f818ae134304381bc39d67ac1f251b01c4be471	1032	jelmer-samba.org
-20140719235524	d203495723441ec129fde8ba792776b8f5691706	1033	jelmer-samba.org
-20140719235741	778ea8abdfd652afad44c3864f9bb02144ce39a0	1034	jelmer-samba.org
-20140724035107	269580c6302a677e07176494bd314c7e2a8f488f	1037	dx-dxzone.com.ar
-20140724035107	1783ab6964c9a8ffc3488bb5243f0b15858f4e74	1036	dx-dxzone.com.ar
-20140724035107	f93fd2db1e8a4c2671ccae3cb9311ff47d4ba7e0	1040	dx-dxzone.com.ar
-20140724035107	757515a793748591e8689167e153ea9ff26ff9e5	1035	dx-dxzone.com.ar
-20140724035107	632627e1ead19fc7dc07effe441b2d543a675229	1039	dx-dxzone.com.ar
-20140724035107	59e66ff766cbef04883c1d7477d66c7e9b515833	1038	dx-dxzone.com.ar
-20140724035108	fb8792474ad6dce88732d43969459717dc718648	1041	dx-dxzone.com.ar
-20140920221833	72721cd2a7142504b2f1249a1e03b2a80a4518ea	1042	wilmer-gaast.net
-20140920221920	b447d2b3a8d9474ffa0bbff93caa57ab45f37e73	1043	wilmer-gaast.net
-20140921103259	9bf02f82214af4c3759b87f6fd2f5b3bc440c2bb	1044	dx-dxzone.com.ar
-20140921103259	a6c59bba98a96fdb78f435a4efc1ffcd91f5b112	1045	dx-dxzone.com.ar
-20140921105344	0406d6a6ae5d96bdc18356d49707ad0a90d6f967	1046	dx-dxzone.com.ar
-20140927145435	0c5727785675d07493dcbd86593ad2049cf4e3a6	1050	dx-dxzone.com.ar
-20140927145435	f75b3a7d497965e4e8a6527a5a536f1784836465	1048	dx-dxzone.com.ar
-20140927145435	d3483776b4b7f45b99127240d76f3c978c260ca2	1052	dx-dxzone.com.ar
-20140927145435	286cd480d844475d545ca90e422e4a0f72f851ca	1051	dx-dxzone.com.ar
-20140927145435	c20353336e6ce3344a41ed5abb3f783ea31ea4e4	1049	dx-dxzone.com.ar
-20140927145435	e252d8cab06e038e5801652bedf02de9170c7945	1053	dx-dxzone.com.ar
-20140927145435	b7cd22dba60316efc1604a2eb257b3df2dcc4807	1047	dx-dxzone.com.ar
-20140927174249	e01ff50356308058948fe5c21126a8f08f76f407	1046.1.1	jelmer-samba.org
-20140928222713	ebe2c5e2b0443b31cf534d6318d728a7cadf1240	1053.1.1	dx-dxzone.com.ar
-20141005203351	f329dfc7baea7e1edc1c664fad70ee0415c2330b	1054	wilmer-gaast.net
-20141005204520	168d3bbe63db4c49835934edd9a74081e313aaec	1055	wilmer-gaast.net
-20141005212436	3a8b8501028b7910ac1d2cfef76f2c72d81dc6e1	1034.1.1	wilmer-gaast.net
-20141005220256	c180110d9108f7d16aeac58aafea5114074ba39b	1034.1.2	wilmer-gaast.net
-20141005220438	5cb21d1cb6590638da27107b84764c888445875b	1056	wilmer-gaast.net
-20141011022052	c27a9235531cfd7580170438f8e2f1f21f39a7f0	1055.1.1	dx-dxzone.com.ar
-20141011022053	7b40f17f9f514127c8a37c35ac3d1c3bd34c13d5	1055.1.3	dx-dxzone.com.ar
-20141011022053	0e35ff67f314fb11522430e17d607ac4f5c13c90	1055.1.2	dx-dxzone.com.ar
-20141011104344	46511b3c928bea3072c8c1012b92a3057fa77517	1055.1.4	dx-dxzone.com.ar
-20141017222618	4f7255d4bed610a77cefd9786fe13c1d673b11bb	1057	wilmer-gaast.net
-20141017223741	9ead105e86e6ae332ebdcbc2f1582775b6ef6d38	1058	wilmer-gaast.net
-20141017224527	fef97af02332d5e7fd099dda8677870c75a406f2	1059	wilmer-gaast.net
-20141027063605	8256ad5ed0c545dfbfd0035d0bac8296ac224467	1059.1.1	dx-dxzone.com.ar
-20141027063609	b6bd99c766d88ef37c735f5bbd08e621288d955d	1059.1.2	dx-dxzone.com.ar
-20141027080544	d2b3f253732b38a4a8e6bb8ffc40f7690009b0fd	1060	wilmer-gaast.net
-20141112062004	8118673608b6046994a0e56b80ee621c49786996	1061	dx-dxzone.com.ar
-20141117055714	a77a9ef9b863d0d464f8461f7221d93a74d0a18b	1062	dx-dxzone.com.ar
-20141117055716	3325df779173020b789f7d4006ae109a75a2b9d2	1063	dx-dxzone.com.ar
-20141117055722	aa6bcd8adcf640ee4c910150d6a3034531fd087d	1064	dx-dxzone.com.ar
-20141124051605	e2472ddb562e9118ed607b5938c7797af6a79e0c	1065	dx-dxzone.com.ar
-20141124051609	b38d399811a556b07a088ec05b947e56397e557b	1066	dx-dxzone.com.ar
-20141124051611	9f8bb178049eaa02023037d40b87d8b6f824477a	1067	dx-dxzone.com.ar
-20141126052500	6f6725c5bbec31d1b9f6937f229f140828e50e45	1068	dx-dxzone.com.ar
-20141126052505	7233f68b3d99c447d4f0cd855b179b9079383bb2	1069	dx-dxzone.com.ar
-20141206175835	bc7a0d46e36a819755ca3f2b38194fb11eca0dd9	1070	dx-dxzone.com.ar
-20150116195023	6f10697380c620065731a5beece95c0f5bd652a0	1070.1.1	dx-dxzone.com.ar
-20150116195023	6b13103dad92d505e038c268af66aeb04b7b4d87	1070.1.2	dx-dxzone.com.ar
-20150116195024	4f161e3abeecee0722487e9d61a85f6c953d11bd	1070.1.8	dx-dxzone.com.ar
-20150116195024	20c9c216b358f97b448282752df171820ff97ab1	1070.1.11	dx-dxzone.com.ar
-20150116195024	4cff28fdfca2eaf71a13715b0fda114796091065	1070.1.13	dx-dxzone.com.ar
-20150116195024	434ffa21f40da03dd9f6efdf1f0713f34df54e89	1070.1.10	dx-dxzone.com.ar
-20150116195024	7549d0074aa4917e62106ac285b05baa1e76c1e9	1070.1.5	dx-dxzone.com.ar
-20150116195024	a6cd799ce11c9792ed3e817d947ccef19cc6aee0	1070.1.7	dx-dxzone.com.ar
-20150116195024	eabe6d4032eada515a1038838b64e34afc84b3e8	1070.1.3	dx-dxzone.com.ar
-20150116195024	840394e847eb4f72443facf8b923741698c3e9a6	1070.1.12	dx-dxzone.com.ar
-20150116195024	25c4c78e2ddad482dfc9d5a104f76325fcc2f8e5	1070.1.6	dx-dxzone.com.ar
-20150116195024	885d294fad822b5275a68127ab2ab925d1df1a0e	1070.1.4	dx-dxzone.com.ar
-20150116195024	85c30041680c8147b8f320ba3eab912027cb0b3e	1070.1.9	dx-dxzone.com.ar
-20150116195025	664bac38fcdf6889d3ceb29b73a0c3a4e27820ce	1070.1.16	dx-dxzone.com.ar
-20150116195025	fed4f766c05e44e99917909b266c99c052ed9c3e	1070.1.14	dx-dxzone.com.ar
-20150116195025	ecbd22a87ca7bc2675e9368445d193c6c81bf3c1	1070.1.15	dx-dxzone.com.ar
-20150117200049	1065dd4f38c81c83934ba51526471072837700ae	1071	wilmer-gaast.net
-20150117201319	eb4ad8d31f7ed4210f24beb8753ecce594b0beef	1072	wilmer-gaast.net
-20251227022103	31fc3970e53409f71ed7d09e277e0e33461ad571	331.1.1	jelmer-samba.org
-20251227022245	e942df125bf47b2cb0f20bed2d5cd6c18b330118	331.1.2	jelmer-samba.org
-20251227022531	0db75ad966458610427dacdd31ecbaddbca18935	331.1.3	jelmer-samba.org
Index: c/uncrustify.cfg
===================================================================
--- doc/uncrustify.cfg	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,48 +1,0 @@
-code_width = 120
-align_enum_equ_span = 4
-cmt_indent_multi = false
-cmt_reflow_mode = 1
-cmt_multi_check_last = false
-mod_add_long_function_closebrace_comment = 0
-mod_full_brace_do = add
-mod_full_brace_for = add
-mod_full_brace_if = add
-mod_full_brace_while = add
-nl_brace_else = remove
-nl_brace_while = remove
-nl_do_brace = remove
-nl_else_brace = remove
-nl_enum_brace = remove
-nl_fcall_brace = remove
-nl_fdef_brace = add
-nl_for_brace = remove
-nl_func_var_def_blk = 1
-nl_if_brace = remove
-nl_struct_brace = remove
-nl_switch_brace = remove
-nl_union_brace = remove
-nl_while_brace = remove
-sp_after_cast = add
-sp_after_comma = add
-sp_after_sparen = force
-sp_arith = add
-sp_assign = add
-sp_brace_else = force
-sp_else_brace = force
-sp_before_sparen = force
-sp_between_ptr_star = remove
-sp_bool = add
-sp_compare = add
-sp_func_call_paren = remove
-sp_func_def_paren = remove
-sp_func_proto_paren = remove
-sp_inside_braces_enum = add
-sp_inside_braces = add
-sp_inside_braces_struct = add
-sp_inside_fparen = remove
-sp_inside_fparens = remove
-sp_inside_paren = remove
-sp_paren_paren = remove
-sp_sizeof_paren = remove
-sp_sparen_brace = force
-sp_inside_sparen = remove
Index: doc/user-guide/Installation.xml
===================================================================
--- doc/user-guide/Installation.xml	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ doc/user-guide/Installation.xml	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -8,8 +8,8 @@
 <para>
 The latest BitlBee release is always available from <ulink
-url="https://www.bitlbee.org/">https://www.bitlbee.org/</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-&lt;version&gt;.tar.gz</command> where &lt;version&gt; is to be
-replaced by the version number of the BitlBee you downloaded (e.g. 3.6).
+replaced by the version number of the BitlBee you downloaded (e.g. 0.91).
 </para>
 
@@ -36,6 +36,7 @@
 settings and buddy information. <filename>/var/lib/bitlbee/</filename>
 is the default value.</para></listitem>
-<listitem><para>jabber, twitter - By default, support for all these IM-protocols
-will be compiled in. To make the binary a bit smaller, you can use these options
+<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,
@@ -49,5 +50,6 @@
 you can use this option. It's not compiled in by default because it needs
 more testing first.</para></listitem>
-<listitem><para>ssl - BitlBee can use three different SSL libraries: GnuTLS,
+<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
@@ -71,5 +73,5 @@
 <para>
 By default, BitlBee runs as the user nobody. You might want 
-to run it as a separate user (some computers run named or apache as nobody). 
+to run it as a seperate user (some computers run named or apache as nobody). 
 </para>
 
Index: doc/user-guide/Makefile
===================================================================
--- doc/user-guide/Makefile	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ doc/user-guide/Makefile	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -1,14 +1,7 @@
 -include ../../Makefile.settings
-ifdef _SRCDIR_
-_SRCDIR_ := $(_SRCDIR_)doc/user-guide/
-endif
+EXTRAPARANEWLINE = 1
+# EXTRAPARANEWLINE = 0
 
-ifndef PYTHON
-PYTHON = python
-endif 
-
-all: help.txt
-
-user-guide: user-guide.txt user-guide.html # user-guide.pdf user-guide.ps user-guide.rtf
+all: user-guide.txt user-guide.html help.txt # user-guide.pdf user-guide.ps user-guide.rtf
 
 %.tex: %.db.xml
@@ -20,5 +13,5 @@
 
 %.html: %.db.xml
-	xsltproc --param generate.consistent.ids 1 --output $@ http://docbook.sourceforge.net/release/xsl/current/xhtml/docbook.xsl $<
+	xsltproc --output $@ http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl $<
 
 %.pdf: %.db.xml
@@ -35,6 +28,6 @@
 	xsltproc --xinclude --output $@ docbook.xsl $< 
 
-help.txt: $(_SRCDIR_)help.xml $(_SRCDIR_)commands.xml $(_SRCDIR_)misc.xml $(_SRCDIR_)quickstart.xml $(_SRCDIR_)twitter.xml
-	$(PYTHON) $(_SRCDIR_)genhelp.py $< $@
+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: 
@@ -45,5 +38,5 @@
 	chmod 0755 $(DESTDIR)$(DATADIR)
 	rm -f $(DESTDIR)$(DATADIR)/help.txt # Prevent help function from breaking in running sessions
-	$(INSTALL) -m 0644 $(_SRCDIR_)help.txt $(DESTDIR)$(DATADIR)/help.txt
+	install -m 0644 help.txt $(DESTDIR)$(DATADIR)/help.txt
 
 uninstall:
@@ -51,3 +44,3 @@
 	-rmdir $(DESTDIR)$(DATADIR)
 
-.PHONY: clean install uninstall user-guide
+.PHONY: clean install uninstall
Index: doc/user-guide/Support.xml
===================================================================
--- doc/user-guide/Support.xml	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ doc/user-guide/Support.xml	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -4,11 +4,10 @@
 
 <sect1>
-<title>Disclaimer</title>
+<title>BitlBee is beta software</title>
 
 <para>
-BitlBee doesn't come with a warranty and is still (and will probably always
-be) under development. 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, or at least don't blame us if things blow up. :-)
+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>
 
@@ -22,5 +21,5 @@
 
 <para>
-<ulink url="https://www.bitlbee.org/">https://www.bitlbee.org/</ulink> 
+<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.
Index: doc/user-guide/Usage.xml
===================================================================
--- doc/user-guide/Usage.xml	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ doc/user-guide/Usage.xml	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -14,26 +14,15 @@
 
 <sect1>
-<title>The &amp;bitlbee control channel</title>
+<title>The #bitlbee control channel</title>
 
 <para>
 Once you are connected to the BitlBee server, you are automatically joined
-to &amp;bitlbee on that server. This channel acts like the 'buddy list' you have
+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 &amp;bitlbee and acts as your interface 
-to bitlbee. All commands you give on &amp;bitlbee are 'answered' by root.
-</para>
-
-<para>
-You might be slightly confused by the &amp; in the channel name. This is,
-however, completely allowed by the IRC standards. Just try it on a regular
-IRC server, it should work. The difference between the standard #channels
-and &amp;channels is that the #channels are distributed over all the servers
-on the IRC network, while &amp;channels are local to one server. Because
-the BitlBee control channel is local to one server (and in fact, to one person),
-this name seems more suitable. Also, with this name, it's harder to confuse
-the control channel with the #bitlbee channel on OFTC.
+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>
 
Index: doc/user-guide/commands.xml
===================================================================
--- doc/user-guide/commands.xml	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ doc/user-guide/commands.xml	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -6,10 +6,10 @@
 	<bitlbee-command name="account">
 		<short-description>IM-account list maintenance</short-description>
-		<syntax>account [&lt;account id&gt;] &lt;action&gt; [&lt;arguments&gt;]</syntax>
-
-		<description>
-
-			<para>
-				Available actions: add, del, list, on, off and set. See <emphasis>help account &lt;action&gt;</emphasis> for more information.
+		<syntax>account &lt;action&gt; [&lt;arguments&gt;]</syntax>
+
+		<description>
+
+			<para>
+				Available actions: add, del, list, on, off. See <emphasis>help account &lt;action&gt;</emphasis> for more information.
 			</para>
 
@@ -17,62 +17,64 @@
 
 		<bitlbee-command name="add">
-			<syntax>account add &lt;protocol&gt; &lt;username&gt; [&lt;password&gt;]</syntax>
+			<syntax>account add &lt;protocol&gt; &lt;username&gt; &lt;password&gt; [&lt;server&gt;]</syntax>
 
 			<description>
 				<para>
-					Adds an account on the given server with the specified protocol, username and password to the account list. For a list of supported protocols, use the <emphasis>plugins</emphasis> command. For more information about adding an account, see <emphasis>help account add &lt;protocol&gt;</emphasis>.
-				</para>
-
-				<para>
-					You can omit the password and enter it separately using the IRC /OPER command. This lets you enter your password without your IRC client echoing it on screen or recording it in logs.
+					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 &lt;protocol&gt;</emphasis>.
 				</para>
 			</description>
 			
 			<bitlbee-command name="jabber">
-				<syntax>account add jabber &lt;handle@server.tld&gt; [&lt;password&gt;]</syntax>
+				<syntax>account add jabber &lt;handle&gt; &lt;password&gt; [&lt;servertag&gt;]</syntax>
 
 				<description>
 					<para>
-						The handle should be a full handle, including the domain name. You can specify a servername if necessary. Normally BitlBee doesn't need this though, since it's able to find out the server by doing DNS SRV lookups.
+						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: [&lt;servername&gt;[:&lt;portnumber&gt;][: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>
-						In previous versions it was also possible to specify port numbers and/or SSL in the server tag. This is deprecated and should now be done using the <emphasis>account set</emphasis> command. This also applies to specifying a resource in the handle (like <emphasis>wilmer@bitlbee.org/work</emphasis>).
+						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="twitter">
-				<syntax>account add twitter &lt;handle&gt;</syntax>
+			<bitlbee-command name="msn">
+				<syntax>account add msn &lt;handle&gt; &lt;password&gt;</syntax>
 
 				<description>
 					<para>
-						This module gives you simple access to Twitter and Twitter API compatible services.
-					</para>
-					
-					<para>
-						By default all your Twitter contacts will appear in a new channel called #twitter_yourusername. You can change this behaviour using the <emphasis>mode</emphasis> setting (see <emphasis>help set mode</emphasis>).
-					</para>
-					
-					<para>
-						To send tweets yourself, send them to the twitter_(yourusername) contact, or just write in the groupchat channel if you enabled that option.
-					</para>
-
-					<para>
-						Since Twitter now requires OAuth authentication, you should not enter your Twitter password into BitlBee. Just type a bogus password. The first time you log in, BitlBee will start OAuth authentication. (See <emphasis>help set oauth</emphasis>.)
-					</para>
-					
-					<para>
-						To use a non-Twitter service, change the <emphasis>base_url</emphasis> setting. For identi.ca, you can simply use <emphasis>account add identica</emphasis>.
+						For MSN connections there are no special arguments.
 					</para>
 				</description>
 			</bitlbee-command>
 			
-			<bitlbee-command name="identica">
-				<syntax>account add identica &lt;handle&gt;</syntax>
+			<bitlbee-command name="oscar">
+				<syntax>account add oscar &lt;handle&gt; &lt;password&gt; [&lt;servername&gt;]</syntax>
 
 				<description>
 					<para>
-						Same protocol as <emphasis>twitter</emphasis>, but defaults to a <emphasis>base_url</emphasis> pointing at identi.ca. It also works with OAuth (so don't specify your password).
+						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 &lt;handle&gt; &lt;password&gt;</syntax>
+
+				<description>
+					<para>
+						For Yahoo! connections there are no special arguments.
 					</para>
 				</description>
@@ -82,14 +84,14 @@
 
 		<bitlbee-command name="del">
-			<syntax>account &lt;account id&gt; del</syntax>
+			<syntax>account del &lt;account id&gt;</syntax>
 
 			<description>
 				<para>
-					This command deletes an account from your account list. You should signoff the account before deleting it.
-				</para>
-
-
-				<para>
-					The account ID can be a number/tag (see <emphasis>account list</emphasis>), the protocol name or (part of) the screenname, as long as it matches only one connection.
+					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>
@@ -97,13 +99,13 @@
 
 		<bitlbee-command name="on">
-			<syntax>account [&lt;account id&gt;] on</syntax>
+			<syntax>account on [&lt;account id&gt;]</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 that have the auto_connect flag set.
-				</para>
-
-				<para>
-					The account ID can be a number/tag (see <emphasis>account list</emphasis>), the protocol name or (part of) the screenname, as long as it matches only one connection.
+					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>
@@ -112,13 +114,13 @@
 
 		<bitlbee-command name="off">
-			<syntax>account [&lt;account id&gt;] off</syntax>
+			<syntax>account off [&lt;account id&gt;]</syntax>
 
 			<description>
 				<para>
-					This command disconnects the connection for the specified account. If no account is specified, BitlBee will deactivate all active accounts and cancel all pending reconnects.
-				</para>
-
-				<para>
-					The account ID can be a number/tag (see <emphasis>account list</emphasis>), the protocol name or (part of) the screenname, as long as it matches only one connection.
+					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>
@@ -130,163 +132,13 @@
 			<description>
 				<para>
-					This command gives you a list of all the accounts known by BitlBee.
+					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 name="set">
-			<syntax>account &lt;account id&gt; set</syntax>
-			<syntax>account &lt;account id&gt; set &lt;setting&gt;</syntax>
-			<syntax>account &lt;account id&gt; set &lt;setting&gt; &lt;value&gt;</syntax>
-			<syntax>account &lt;account id&gt; set -del &lt;setting&gt;</syntax>
-
-			<description>
-				<para>
-					This command can be used to change various settings for IM accounts. For all protocols, this command can be used to change the handle or the password BitlBee uses to log in and if it should be logged in automatically. Some protocols have additional settings. You can see the settings available for a connection by typing <emphasis>account &lt;account id&gt; set</emphasis>.
-				</para>
-				
-				<para>
-					For more information about a setting, see <emphasis>help set &lt;setting&gt;</emphasis>.
-				</para>
-				
-				<para>
-					The account ID can be a number/tag (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>
-
-	<bitlbee-command name="channel">
-		<short-description>Channel list maintenance</short-description>
-		<syntax>channel [&lt;channel id&gt;] &lt;action&gt; [&lt;arguments&gt;]</syntax>
-
-		<description>
-			<para>
-				Available actions: del, list, set. See <emphasis>help channel &lt;action&gt;</emphasis> for more information.
-			</para>
-			
-			<para>
-				There is no <emphasis>channel add</emphasis> command. To create a new channel, just use the IRC <emphasis>/join</emphasis> command. See also <emphasis>help channels</emphasis> and <emphasis>help groupchats</emphasis>.
-			</para>
-		</description>
-
-		<bitlbee-command name="del">
-			<syntax>channel &lt;channel id&gt; del</syntax>
-
-			<description>
-				<para>
-					Remove a channel and forget all its settings. You can only remove channels you're not currently in, and can't remove the main control channel. (You can, however, leave it.)
-				</para>
-			</description>
-
-		</bitlbee-command>
-
-		<bitlbee-command name="list">
-			<syntax>channel list</syntax>
-
-			<description>
-				<para>
-					This command gives you a list of all the channels you configured.
-				</para>
-			</description>
-
-		</bitlbee-command>
-
-		<bitlbee-command name="set">
-			<syntax>channel [&lt;channel id&gt;] set</syntax>
-			<syntax>channel [&lt;channel id&gt;] set &lt;setting&gt;</syntax>
-			<syntax>channel [&lt;channel id&gt;] set &lt;setting&gt; &lt;value&gt;</syntax>
-			<syntax>channel [&lt;channel id&gt;] set -del &lt;setting&gt;</syntax>
-
-			<description>
-				<para>
-					This command can be used to change various settings for channels. Different channel types support different settings. You can see the settings available for a channel by typing <emphasis>channel &lt;channel id&gt; set</emphasis>.
-				</para>
-				
-				<para>
-					For more information about a setting, see <emphasis>help set &lt;setting&gt;</emphasis>.
-				</para>
-				
-				<para>
-					The channel ID can be a number (see <emphasis>channel list</emphasis>), or (part of) its name, as long as it matches only one channel. If you want to change settings of the current channel, you can omit the channel ID.
-				</para>
-			</description>
-		</bitlbee-command>
-
-	</bitlbee-command>
-
-	<bitlbee-command name="chat">
-		<short-description>Chatroom list maintenance</short-description>
-		<syntax>chat &lt;action&gt; [&lt;arguments&gt;]</syntax>
-
-		<description>
-
-			<para>
-				Available actions: add, with, list. See <emphasis>help chat &lt;action&gt;</emphasis> for more information.
-			</para>
-
-		</description>
-
-		<bitlbee-command name="add">
-			<syntax>chat add &lt;account id&gt; &lt;room|!index&gt; [&lt;channel&gt;]</syntax>
-
-			<description>
-				<para>
-					Add a chatroom to the list of chatrooms you're interested in. BitlBee needs this list to map room names to a proper IRC channel name.
-				</para>
-
-				<para>
-					After adding a room to your list, you can simply use the IRC /join command to enter the room. Also, you can tell BitlBee to automatically join the room when you log in. (<emphasis>channel &lt;channel&gt; set auto_join true</emphasis>)
-				</para>
-
-				<para>
-					Password-protected rooms work exactly like on IRC, by passing the password as an extra argument to /join.
-				</para>
-			</description>
-
-		</bitlbee-command>
-
-		<bitlbee-command name="list">
-			<syntax>chat list &lt;account id&gt; [&lt;server&gt;]</syntax>
-
-			<description>
-				<para>
-					List existing named chatrooms provided by an account. Chats from this list can be referenced from <emphasis>chat add</emphasis> by using the number in the index column after a "!" as a shortcut.
-				</para>
-
-				<para>
-					The server parameter is optional and currently only used by jabber.
-				</para>
-			</description>
-
-			<ircexample>
-				<ircline nick="dx">chat list facebook</ircline>
-				<ircline pre="1" nick="root">Index  Title                 Topic</ircline>
-				<ircline pre="1" nick="root">    1  869891016470949       cool kids club</ircline>
-				<ircline pre="1" nick="root">    2  457892181062459       uncool kids club</ircline>
-				<ircline nick="root">2 facebook chatrooms</ircline>
-				<ircline nick="dx">chat add facebook !1 #cool-kids-club</ircline>
-			</ircexample>
-		</bitlbee-command>
-
-		<bitlbee-command name="with">
-			<syntax>chat with &lt;nickname&gt;</syntax>
-
-			<description>
-				<para>
-					While most <emphasis>chat</emphasis> subcommands are about named chatrooms, this command can be used to open an unnamed groupchat with one or more persons. This command is what <emphasis>/join #nickname</emphasis> used to do in older BitlBee versions.
-				</para>
-
-				<para>
-					Another way to do this is to join to a new, empty channel with <emphasis>/join #newchannel</emphasis> and invite the first person with <emphasis>/invite nickname</emphasis>
-				</para>
-			</description>
-		</bitlbee-command>
 	</bitlbee-command>
 
 	<bitlbee-command name="add">
 		<short-description>Add a buddy to your contact list</short-description>
-		<syntax>add &lt;account id&gt; &lt;handle&gt; [&lt;nick&gt;]</syntax>
-		<syntax>add -tmp &lt;account id&gt; &lt;handle&gt; [&lt;nick&gt;]</syntax>
+		<syntax>add &lt;connection&gt; &lt;handle&gt; [&lt;nick&gt;]</syntax>
 
 		<description>
@@ -296,15 +148,11 @@
 
 			<para>
-				If you want, you can also tell BitlBee what nick to give the new contact. The -tmp option adds the buddy to the internal BitlBee structures only, not to the real contact list (like done by <emphasis>set handle_unknown add</emphasis>). This allows you to talk to people who are not in your contact list. This normally won't show you any presence notifications.
-			</para>
-
-			<para>
-				If you use this command in a control channel containing people from only one group, the new contact will be added to that group automatically.
+				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="jelmer">add 3 gryp@jabber.org grijp</ircline>
-			<ircaction nick="grijp" hostmask="gryp@jabber.org">has joined <emphasis>&amp;bitlbee</emphasis></ircaction>
+			<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>
@@ -317,10 +165,10 @@
 		<description>
 			<para>
-				Requests IM-network-specific information about the specified user. The amount of information you'll get differs per protocol. For some protocols it'll give you an URL which you can visit with a normal web browser to get the information.
+				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="jelmer">info 0 72696705</ircline>
+			<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>
@@ -339,5 +187,5 @@
 
 		<ircexample>
-			<ircline nick="jelmer">remove gryp</ircline>
+			<ircline nick="ctrlsoft">remove gryp</ircline>
 			<ircaction nick="gryp" hostmask="gryp@jabber.jabber.org">has quit <emphasis>[Leaving...]</emphasis></ircaction>
 		</ircexample>
@@ -349,13 +197,8 @@
 		<syntax>block &lt;nick&gt;</syntax>
 		<syntax>block &lt;connection&gt; &lt;handle&gt;</syntax>
-		<syntax>block &lt;connection&gt;</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>
-			
-			<para>
-				When called with only a connection specification as an argument, the command displays the current block list for that connection.
 			</para>
 		</description>
@@ -371,218 +214,15 @@
 				Reverse of block. Unignores the specified user or user handle on specified connection.
 			</para>
-			
-			<para>
-				When called with only a connection specification as an argument, the command displays the current allow list for that connection.
-			</para>
-		</description>
-	</bitlbee-command>
-	
-	<bitlbee-command name="otr">
-		<short-description>Off-the-Record encryption control</short-description>
-		<syntax>otr &lt;subcommand&gt; [&lt;arguments&gt;]</syntax>
-
-		<description>
-
-			<para>
-				Available subcommands: connect, disconnect, reconnect, smp, smpq, trust, info, keygen, and forget. See <emphasis>help otr &lt;subcommand&gt;</emphasis> for more information.
-			</para>
-
-		</description>
-		
-		<bitlbee-command name="connect">
-			<syntax>otr connect &lt;nick&gt;</syntax>
-			
-			<description>
-			
-				<para>
-					Attempts to establish an encrypted connection with the specified user by sending a magic string.
-				</para>
-				
-			</description>
-		
-		</bitlbee-command>
-		
-		<bitlbee-command name="disconnect">
-			<syntax>otr disconnect &lt;nick&gt;</syntax>
-			<syntax>otr disconnect *</syntax>
-			
-			<description>
-			
-				<para>
-					Resets the connection with the specified user/all users to cleartext.
-				</para>
-				
-			</description>
-		
-		</bitlbee-command>
-		
-		<bitlbee-command name="reconnect">
-			<syntax>otr reconnect &lt;nick&gt;</syntax>
-			
-			<description>
-			
-				<para>
-					Breaks and re-establishes the encrypted connection with the specified user. Useful if something got desynced.
-				</para>
-				
-				<para>
-					Equivalent to <emphasis>otr disconnect</emphasis> followed by <emphasis>otr connect</emphasis>.
-				</para>
-				
-			</description>
-		
-		</bitlbee-command>
-		
-		<bitlbee-command name="smp">
-			<syntax>otr smp &lt;nick&gt; &lt;secret&gt;</syntax>
-			
-			<description>
-			
-				<para>
-					Attempts to authenticate the given user's active fingerprint via the Socialist Millionaires' Protocol.
-				</para>
-				
-				<para>
-					If an SMP challenge has been received from the given user, responds with the specified secret/answer. Otherwise, sends a challenge for the given secret.
-				</para>
-				
-				<para>
-					Note that there are two flavors of SMP challenges: "shared-secret" and "question &amp; answer". This command is used to respond to both of them, or to initiate a shared-secret style exchange. Use the <emphasis>otr smpq</emphasis> command to initiate a "Q&amp;A" session.
-				</para>
-				
-				<para>
-					When responding to a "Q&amp;A" challenge, the local trust value is not altered. Only the <emphasis>asking party</emphasis> sets trust in the case of success. Use <emphasis>otr smpq</emphasis> to pose your challenge. In a shared-secret exchange, both parties set their trust according to the outcome.
-				</para>
-				
-			</description>
-		
-		</bitlbee-command>
-		
-		<bitlbee-command name="smpq">
-			<syntax>otr smpq &lt;nick&gt; &lt;question&gt; &lt;answer&gt;</syntax>
-			
-			<description>
-			
-				<para>
-					Attempts to authenticate the given user's active fingerprint via the Socialist Millionaires' Protocol, Q&amp;A style.
-				</para>
-
-				<para>
-					Initiates an SMP session in "question &amp; answer" style. The question is transmitted with the initial SMP packet and used to prompt the other party. You must be confident that only they know the answer. If the protocol succeeds (i.e. they answer correctly), the fingerprint will be trusted. Note that the answer must be entered exactly, case and punctuation count!
-				</para>
-				
-				<para>
-					Note that this style of SMP only affects the trust setting on your side. Expect your opponent to send you their own challenge. Alternatively, if you and the other party have a shared secret, use the <emphasis>otr smp</emphasis> command.
-				</para>
-				
-			</description>
-		
-		</bitlbee-command>
-		
-		<bitlbee-command name="trust">
-			<syntax>otr trust &lt;nick&gt; &lt;fp1&gt; &lt;fp2&gt; &lt;fp3&gt; &lt;fp4&gt; &lt;fp5&gt;</syntax>
-			
-			<description>
-			
-				<para>
-					Manually affirms trust in the specified fingerprint, given as five blocks of precisely eight (hexadecimal) digits each.
-				</para>
-				
-			</description>
-		
-		</bitlbee-command>
-		
-		<bitlbee-command name="info">
-			<syntax>otr info</syntax>
-			<syntax>otr info &lt;nick&gt;</syntax>
-			
-			<description>
-			
-				<para>
-					Shows information about the OTR state. The first form lists our private keys and current OTR contexts. The second form displays information about the connection with a given user, including the list of their known fingerprints.
-				</para>
-				
-			</description>
-		
-		</bitlbee-command>
-		
-		<bitlbee-command name="keygen">
-			<syntax>otr keygen &lt;account-no&gt;</syntax>
-			
-			<description>
-			
-				<para>
-					Generates a new OTR private key for the given account.
-				</para>
-				
-			</description>
-		
-		</bitlbee-command>
-		
-		<bitlbee-command name="forget">
-			<syntax>otr forget &lt;thing&gt; &lt;arguments&gt;</syntax>
-			
-			<description>
-			
-				<para>
-					Forgets some part of our OTR userstate. Available things: fingerprint, context, and key. See <emphasis>help otr forget &lt;thing&gt;</emphasis> for more information.
-				</para>
-			
-			</description>
-			
-			<bitlbee-command name="fingerprint">
-				<syntax>otr forget fingerprint &lt;nick&gt; &lt;fingerprint&gt;</syntax>
-				
-				<description>
-				
-					<para>
-						Drops the specified fingerprint from the given user's OTR connection context. It is allowed to specify only a (unique) prefix of the desired fingerprint.
-					</para>
-					
-				</description>
-				
-			</bitlbee-command>
-				
-			<bitlbee-command name="context">
-				<syntax>otr forget context &lt;nick&gt;</syntax>
-				
-				<description>
-				
-					<para>
-						Forgets the entire OTR context associated with the given user. This includes current message and protocol states, as well as any fingerprints for that user.
-					</para>
-					
-				</description>
-				
-			</bitlbee-command>
-
-			<bitlbee-command name="key">
-				<syntax>otr forget key &lt;fingerprint&gt;</syntax>
-				
-				<description>
-				
-					<para>
-						Forgets an OTR private key matching the specified fingerprint. It is allowed to specify only a (unique) prefix of the fingerprint.
-					</para>
-					
-				</description>
-				
-			</bitlbee-command>
-		
-		</bitlbee-command>
-		
+		</description>
 	</bitlbee-command>
 
 	<bitlbee-command name="set">
 		<short-description>Miscellaneous settings</short-description>
-		<syntax>set</syntax>
-		<syntax>set &lt;variable&gt;</syntax>
-		<syntax>set &lt;variable&gt; &lt;value&gt;</syntax>
-		<syntax>set -del &lt;variable&gt;</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. With <emphasis>-del</emphasis> you can reset a setting to its default value.
+		<syntax>set [&lt;variable&gt; [&lt;value&gt;]]</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>
 
@@ -594,5 +234,5 @@
 
 		<ircexample>
-			<ircline nick="jelmer">help set private</ircline>
+			<ircline nick="ctrlsoft">help set private</ircline>
 		</ircexample>
 
@@ -622,43 +262,145 @@
 	</bitlbee-command>
 
-	<bitlbee-setting name="account" type="string" scope="channel">
-
-		<description>
-			<para>
-				For control channels with <emphasis>fill_by</emphasis> set to <emphasis>account</emphasis>: Set this setting to the account id (numeric, or part of the username) of the account containing the contacts you want to see in this channel.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="allow_takeover" type="boolean" scope="global">
-		<default>true</default>
-
-		<description>
-			<para>
-				When you're already connected to a BitlBee server and you connect (and identify) again, BitlBee will offer to migrate your existing session to the new connection. If for whatever reason you don't want this, you can disable this setting.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="always_use_nicks" type="boolean" scope="channel">
-		<default>false</default>
-
-		<description>
-			<para>
-				Jabber groupchat specific. This setting ensures that the nicks defined by the other members of a groupchat are used, instead of the username part of their JID. This only applies to groupchats where their real JID is known (either "non-anonymous" ones, or "semi-anonymous" from the point of view of the channel moderators)
-			</para>
-
-			<para>
-				Enabling this may have the side effect of changing the nick of existing contacts, either in your buddy list or in other groupchats. If a contact is in multiple groupchats with different nicks, enabling this setting for all those would result in multiple nick changes when joining, and the order of those changes may vary.
-			</para>
-
-			<para>
-				Note that manual nick changes done through the <emphasis>rename</emphasis> command always take priority
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="auto_connect" type="boolean" scope="account,global">
-		<default>true</default>
+	<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>
@@ -666,23 +408,9 @@
 				With this option enabled, when you identify BitlBee will automatically connect to your accounts, with this disabled it will not do this.
 			</para>
-			
-			<para>
-				This setting can also be changed for specific accounts using the <emphasis>account set</emphasis> command. (However, these values will be ignored if the global <emphasis>auto_connect</emphasis> setting is disabled!)
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="auto_join" type="boolean" scope="channel">
-		<default>false</default>
-
-		<description>
-			<para>
-				With this option enabled, BitlBee will automatically join this channel when you log in.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="auto_reconnect" type="boolean" scope="account,global">
-		<default>true</default>
+		</description>
+	</bitlbee-setting>
+
+	<bitlbee-setting name="auto_reconnect" type="boolean">
+		<default>False</default>
 
 		<description>
@@ -694,21 +422,15 @@
 				See also the <emphasis>auto_reconnect_delay</emphasis> setting.
 			</para>
-
-			<para>
-				This setting can also be changed for specific accounts using the <emphasis>account set</emphasis> command. (However, these values will be ignored if the global <emphasis>auto_reconnect</emphasis> setting is disabled!)
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="auto_reconnect_delay" type="string" scope="global">
-		<default>5*3&lt;900</default>
-
-		<description>
-			<para>
-				Tell BitlBee after how many seconds it should attempt to bring a broken IM-connection back up.
-			</para>
-
-			<para>
-				This can be one integer, for a constant delay. One can also set it to something like &quot;10*10&quot;, which means wait for ten seconds on the first reconnect, multiply it by ten on every failure. Once successfully connected, this delay is re-set to the initial value. With &lt; you can give a maximum delay.
+		</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>
 
@@ -719,171 +441,39 @@
 	</bitlbee-setting>
 
-	<bitlbee-setting name="auto_reply_timeout" type="integer" scope="account">
-		<default>10800</default>
-
-		<description>
-			<para>
-				For Twitter accounts: If you respond to Tweets IRC-style (like "nickname: reply"), this will automatically be converted to the usual Twitter format ("@screenname reply").
-			</para>
-
-			<para>
-				By default, BitlBee will then also add a reference to that person's most recent Tweet, unless that message is older than the value of this setting in seconds.
-			</para>
-
-			<para>
-				If you want to disable this feature, just set this to 0. Alternatively, if you want to write a message once that is <emphasis>not</emphasis> a reply, use the Twitter reply syntax (@screenname).
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="away" type="string" scope="account,global">
-		<description>
-			<para>
-				To mark yourself as away, it is recommended to just use <emphasis>/away</emphasis>, like on normal IRC networks. If you want to mark yourself as away on only one IM network, you can use this per-account setting.
-			</para>
-
-			<para>
-				You can set it to any value and BitlBee will try to map it to the most appropriate away state for every open IM connection, or set it as a free-form away message where possible.
-			</para>
-
-			<para>
-				Any per-account away setting will override globally set away states. To un-set the setting, use <emphasis>set -del away</emphasis>.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="away_devoice" type="boolean" scope="global">
-		<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>
-			
-			<para>
-				Replaced with the <emphasis>show_users</emphasis> setting. See <emphasis>help show_users</emphasis>.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="away_reply_timeout" type="integer" scope="global">
-		<default>3600</default>
-
-		<description>
-			<para>
-				Most IRC servers send a user's away message every time s/he gets a private message, to inform the sender that they may not get a response immediately. With this setting set to 0, BitlBee will also behave like this.
-			</para>
-
-			<para>
-				Since not all IRC clients do an excellent job at suppressing these messages, this setting lets BitlBee do it instead. BitlBee will wait this many seconds (or until the away state/message changes) before re-informing you that the person's away.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="base_url" type="string" scope="account">
-		<default>http://api.twitter.com/1</default>
-
-		<description>
-			<para>
-				There are more services that understand the Twitter API than just Twitter.com. BitlBee can connect to all Twitter API implementations.
-			</para>
-
-			<para>
-				For example, set this setting to <emphasis>http://identi.ca/api</emphasis> to use Identi.ca.
-			</para>
-
-			<para>
-				Keep two things in mind: When not using Twitter, you <emphasis>must</emphasis> also disable the <emphasis>oauth</emphasis> setting as it currently only works with Twitter. If you're still having issues, make sure there is <emphasis>no</emphasis> slash at the end of the URL you enter here.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="carbons" type="boolean" scope="account">
-		<default>true</default>
-
-		<description>
-			<para>
-				Jabber specific. "Message carbons" (XEP-0280) is a server feature to get copies of outgoing messages sent from other clients connected to the same account. It's not widely supported by most public XMPP servers (easier if you host your own), but this will probably change in the next few years.
-			</para>
-			<para>
-				This defaults to true, which will enable it if the server supports it, or fail silently if it's not. This setting only exists to allow disabling the feature if anyone considers it undesirable.
-			</para>
-			<para>
-				See also the <emphasis>self_messages</emphasis> setting.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="charset" type="string" scope="global">
-		<default>utf-8</default>
-		<possible-values>you can get a list of all possible values by doing 'iconv -l' in a shell</possible-values>
-
-		<description>
-			<para>
-				This setting tells BitlBee what your IRC client sends and expects. It should be equal to the charset setting of your IRC client if you want to be able to send and receive non-ASCII text properly.
-			</para>
-
-			<para>
-				Most systems use UTF-8 these days. On older systems, an iso8859 charset may work better. For example, iso8859-1 is the best choice for most Western countries. You can try to find what works best for you on http://www.unicodecharacter.com/charsets/iso8859.html
-			</para>
-		</description>
-
-	</bitlbee-setting>
-
-	<bitlbee-setting name="color_encrypted" type="boolean" scope="global">
-		<default>true</default>
-
-		<description>
-			<para>
-				If set to true, BitlBee will color incoming encrypted messages according to their fingerprint trust level: untrusted=red, trusted=green.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="chat_type" type="string" scope="channel">
-		<default>groupchat</default>
-		<possible-values>groupchat, room</possible-values>
-
-		<description>
-			<para>
-				There are two kinds of chat channels: simple groupchats (basically normal IM chats with more than two participants) and names chatrooms, more similar to IRC channels.
-			</para>
-			
-			<para>
-				BitlBee supports both types. With this setting set to <emphasis>groupchat</emphasis> (the default), you can just invite people into the room and start talking.
-			</para>
-			
-			<para>
-				For setting up named chatrooms, it's currently easier to just use the <emphasis>chat add</emphasis> command.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="commands" type="boolean" scope="account">
-		<default>true</default>
-		<possible-values>true, false, strict</possible-values>
-
-		<description>
-			<para>
-				With this setting enabled, you can use some commands in your Twitter channel/query. See <emphasis>help twitter</emphasis> for the list of extra commands available.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="debug" type="boolean" scope="global">
-		<default>false</default>
-
-		<description>
-			<para>
-				Some debugging messages can be logged if you wish. They're probably not really useful for you, unless you're doing some development on BitlBee.
-			</para>
-			
-			<para>
-				This feature is not currently used for anything so don't expect this to generate any output.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="default_target" type="string" scope="global">
+	<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>
@@ -891,465 +481,27 @@
 		<description>
 			<para>
-				With this value set to <emphasis>root</emphasis>, lines written in a 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 that control channel, set this to <emphasis>last</emphasis>.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="display_name" type="string" scope="account">
-		<description>
-			<para>
-				Currently only available for jabber groupchats.
-			</para>
-			<para>
-				For jabber groupchats: this sets the default value of 'nick' for newly created groupchats. There is no way to set an account-wide nick.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="display_namechanges" type="boolean" scope="global">
-		<default>false</default>
-
-		<description>
-			<para>
-				With this option enabled, root will inform you when someone in your buddy list changes his/her "friendly name".
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="display_timestamps" type="boolean" scope="global">
-		<default>true</default>
-
-		<description>
-			<para>
-				When incoming messages are old (i.e. offline messages and channel backlogs), BitlBee will prepend them with a timestamp. If you find them ugly or useless, you can use this setting to hide them.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="fill_by" type="string" scope="channel">
-		<default>all</default>
-		<possible-values>all, group, account, protocol</possible-values>
-
-		<description>
-			<para>
-				For control channels only: This setting determines which contacts the channel gets populated with.
-			</para>
-
-			<para>
-				By default, control channels will contain all your contacts. You instead select contacts by buddy group, IM account or IM protocol.
-			</para>
-			
-			<para>
-				Change this setting and the corresponding <emphasis>account</emphasis>/<emphasis>group</emphasis>/<emphasis>protocol</emphasis> setting to set up this selection.
-			</para>
-			
-			<para>
-				With a ! prefix an inverted channel can be created, for example with this setting set to <emphasis>!group</emphasis> you can create a channel with all users <emphasis>not</emphasis> in that group.
-			</para>
-			
-			<para>
-				Note that, when creating a new channel, BitlBee will try to preconfigure the channel for you, based on the channel name. See <emphasis>help channels</emphasis>.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="group" type="string" scope="channel">
-
-		<description>
-			<para>
-				For control channels with <emphasis>fill_by</emphasis> set to <emphasis>group</emphasis>: Set this setting to the name of the group containing the contacts you want to see in this channel.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="handle_unknown" type="string" scope="account,global">
-		<default>add_channel</default>
-		<possible-values>add_private, add_channel, ignore</possible-values>
-
-		<description>
-			<para>
-				By default, messages from people who aren't in your contact list are shown in a control channel (add_channel) instead of as a private message (add_private)
-			</para>
-
-			<para>
-				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>
-
-			<para>
-				This can be set to individual accounts, which is useful to only ignore accounts that are targeted by spammers, without missing messages from legitimate unknown contacts in others. Note that incoming add requests are visible regardless of this setting.
-			</para>
-
-			<note>
-				<para>
-					Although these users will appear in your control channel, they aren't added to your real contact list. When you restart BitlBee, these auto-added users will be gone. If you want to keep someone in your list, you have to fixate the add using the <emphasis>add</emphasis> command.
-				</para>
-			</note>
-		</description>
-
-	</bitlbee-setting>
-
-	<bitlbee-setting name="mail_notifications" type="boolean" scope="account">
-		<default>false</default>
-
-		<description>
-			<para>
-				Some protocols can notify via IM about new e-mail. If you want these notifications, you can enable this setting.
-			</para>
-		</description>
-
-	</bitlbee-setting>
-
-	<bitlbee-setting name="mail_notifications_handle" type="string" scope="account">
-		<default>empty</default>
-
-		<description>
-			<para>
-				This setting is available for protocols with e-mail notification functionality. If set to empty all e-mail notifications will go to control channel, if set to some string - this will be the name of a contact who will PRIVMSG you on every new notification.
-			</para>
-		</description>
-
-	</bitlbee-setting>
-
-	<bitlbee-setting name="message_length" type="integer" scope="account">
-		<default>280</default>
-
-		<description>
-			<para>
-				Since Twitter rejects messages longer than 280 characters, BitlBee can count message length and emit a warning instead of waiting for Twitter to reject it.
-			</para>
-
-			<para>
-				You can change this limit here but this won't disable length checks on Twitter's side. You can also set it to 0 to disable the check in case you believe BitlBee doesn't count the characters correctly.
-			</para>
-		</description>
-
-	</bitlbee-setting>
-
-	<bitlbee-setting name="stream" type="boolean" scope="account">
-		<default>true</default>
-
-		<description>
-			<para>
-				For Twitter accounts, this setting enables use of the Streaming API. This automatically gives you incoming DMs as well.
-			</para>
-			
-			<para>
-				For other Twitter-like services, this setting is not supported.
-			</para>
-		</description>
-
-	</bitlbee-setting>
-	
-	<bitlbee-setting name="target_url_length" type="integer" scope="account">
-		<default>20</default>
-
-		<description>
-			<para>
-				Twitter replaces every URL with fixed-length t.co URLs. BitlBee is able to take t.co urls into account when calculating <emphasis>message_length</emphasis> replacing the actual URL length with target_url_length. Setting target_url_length to 0 disables this feature.
-			</para>
-
-			<para>
-				This setting is disabled for identica accounts by default and will not affect anything other than message safety checks (i.e. Twitter will still replace your URLs with t.co links, even if that makes them longer).
-			</para>
-		</description>
-
-	</bitlbee-setting>
-
-	<bitlbee-setting name="mode" type="string" scope="account">
-		<possible-values>one, many, chat</possible-values>
-		<default>chat</default>
-
-		<description>
-			<para>
-				By default, BitlBee will create a separate channel (called #twitter_yourusername) for all your Twitter contacts/messages.
-			</para>
-
-			<para>
-				If you don't want an extra channel, you can set this setting to "one" (everything will come from one nick, twitter_yourusername), or to "many" (individual nicks for everyone).
-			</para>
-			
-			<para>
-				With modes "chat" and "many", you can send direct messages by /msg'ing your contacts directly. Incoming DMs are only fetched if the "stream" setting is on (default).
-			</para>
-			
-			<para>
-				With modes "many" and "one", you can post tweets by /msg'ing the twitter_yourusername contact. In mode "chat", messages posted in the Twitter channel will also be posted as tweets.
-			</para>
-		</description>
-
-	</bitlbee-setting>
-
-	<bitlbee-setting name="mobile_is_away" type="boolean" scope="global">
-		<default>false</default>
-
-		<description>
-			<para>
-				Most IM networks have a mobile version of their client. People who use these may not be paying that much attention to messages coming in. By enabling this setting, people using mobile clients will always be shown as away.
-			</para>
-		</description>
-
-	</bitlbee-setting>
-
-	<bitlbee-setting name="nick" type="string" scope="chat">
-		<description>
-			<para>
-				You can use this option to set your nickname in a chatroom. You won't see this nickname yourself, but other people in the room will. By default, BitlBee will use your username as the chatroom nickname.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="nick_format" type="string" scope="account,global">
-		<default>%-@nick</default>
-
-		<description>
-			<para>
-				By default, BitlBee tries to derive sensible nicknames for all your contacts from their IM handles. In some cases, IM modules (ICQ for example) will provide a nickname suggestion, which will then be used instead. This setting lets you change this behaviour.
-			</para>
-
-			<para>
-				Whenever this setting is set for an account, it will be used for all its contacts. If it's not set, the global value will be used.
-			</para>
-
-			<para>
-				It's easier to describe this setting using a few examples:
-			</para>
-
-			<para>
-				FB-%full_name will make all nicknames start with "FB-", followed by the person's full name. For example you can set this format for your Facebook account so all Facebook contacts are clearly marked.
-			</para>
-
-			<para>
-				[%group]%-@nick will make all nicknames start with the group the contact is in between square brackets, followed by the nickname suggestions from the IM module if available, or otherwise the handle. Because of the "-@" part, everything from the first @ will be stripped.
-			</para>
-
-			<para>
-				See <emphasis>help nick_format</emphasis> for more information.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="nick_source" type="string" scope="account">
-		<default>handle</default>
-		<possible-values>handle, full_name, first_name</possible-values>
-
-		<description>
-			<para>
-				By default, BitlBee generates a nickname for every contact by taking its handle and chopping off everything after the @. In some cases, this gives very inconvenient nicknames. Some servers use internal identifiers, which are often just numbers.
-			</para>
-
-			<para>
-				With this setting set to <emphasis>full_name</emphasis>, the person's full name is used to generate a nickname. Or if you don't like long nicknames, set this setting to <emphasis>first_name</emphasis> instead and only the first word will be used. Note that the full name can be full of non-ASCII characters which will be stripped off.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="nick_lowercase" type="boolean" scope="global">
-		<default>true</default>
-
-		<description>
-			<para>
-				If enabled, all nicknames are turned into lower case.
-			</para>
-
-			<para>
-				See also the <emphasis>nick_underscores</emphasis> setting. This setting was previously known as <emphasis>lcnicks</emphasis>.
-			</para>
-		</description>
-
-	</bitlbee-setting>
-
-	<bitlbee-setting name="nick_underscores" type="boolean" scope="global">
-		<default>true</default>
-
-		<description>
-			<para>
-				If enabled, spaces in nicknames are turned into underscores instead of being stripped.
-			</para>
-
-			<para>
-				See also the <emphasis>nick_lowercase</emphasis> setting.
-			</para>
-		</description>
-
-	</bitlbee-setting>
-
-	<bitlbee-setting name="oauth" type="boolean" scope="account">
-		<default>true</default>
-
-		<description>
-			<para>
-				This enables OAuth authentication for an IM account; right now the Twitter (working for Twitter only) and Jabber (for Google Talk only) module support it.
-			</para>
-
-			<para>
-				With OAuth enabled, you shouldn't tell BitlBee your account password. Just add your account with a bogus password and type <emphasis>account on</emphasis>. BitlBee will then give you a URL to authenticate with the service. If this succeeds, you will get a PIN code which you can give back to BitlBee to finish the process.
-			</para>
-
-			<para>
-				The resulting access token will be saved permanently, so you have to do this only once. If for any reason you want to/have to reauthenticate, you can use <emphasis>account set</emphasis> to reset the account password to something random.
-			</para>
-		</description>
-
-	</bitlbee-setting>
-
-	<bitlbee-setting name="anonymous" type="boolean" scope="account">
-		<default>false</default>
-
-		<description>
-			<para>
-				This enables SASL ANONYMOUS login for jabber accounts, as specified by XEP-0175.
-			</para>
-
-			<para>
-				With this setting enabled, if the server allows this method, a password isn't required and the username part of the JID is ignored (you can use anonymous@jabber.example.com). Servers will usually assign you a random numeric username instead.
-			</para>
-		</description>
-
-	</bitlbee-setting>
-
-	<bitlbee-setting name="ops" type="string" scope="global">
-		<default>both</default>
-		<possible-values>both, root, user, none</possible-values>
-
-		<description>
-			<para>
-				Some people prefer themself and root to have operator status in &amp;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="otr_policy" type="string" scope="global">
-		<default>opportunistic</default>
-		<possible-values>never, opportunistic, manual, always</possible-values>
-
-		<description>
-			<para>
-				This setting controls the policy for establishing Off-the-Record connections.
-			</para>
-			<para>
-				A value of "never" effectively disables the OTR subsystem. In "opportunistic" mode, a magic whitespace pattern will be appended to the first message sent to any user. If the peer is also running opportunistic OTR, an encrypted connection will be set up automatically. On "manual", on the other hand, OTR connections must be established explicitly using <emphasis>otr connect</emphasis>. Finally, the setting "always" enforces encrypted communication by causing BitlBee to refuse to send any cleartext messages at all.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="password" type="string" scope="account,global">
-		<description>
-			<para>
-				Use this global setting to change your "NickServ" password.
-			</para>
-			
-			<para>
-				This setting is also available for all IM accounts to change the password BitlBee uses to connect to the service.
-			</para>
-			
-			<para>
-				Note that BitlBee will always say this setting is empty. This doesn't mean there is no password, it just means that, for security reasons, BitlBee stores passwords somewhere else so they can't just be retrieved in plain text.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="paste_buffer" type="boolean" scope="global">
-		<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>paste_buffer_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="paste_buffer_delay" type="integer" scope="global">
-		<default>200</default>
-
-		<description>
-
-			<para>
-				Tell BitlBee after how many (mili)seconds a buffered message should be sent. Values greater than 5 will be interpreted as miliseconds, 5 and lower as seconds.
-			</para>
-
-			<para>
-				See also the <emphasis>paste_buffer</emphasis> setting.
-			</para>
-		</description>
-	</bitlbee-setting>
-	
-	<bitlbee-setting name="port" type="integer" scope="account">
-		<description>
-			<para>
-				Currently only available for Jabber connections. Specifies the port number to connect to. Usually this should be set to 5222, or 5223 for SSL-connections.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="priority" type="integer" scope="account">
-		<default>0</default>
-
-		<description>
-			<para>
-				Can be set for Jabber connections. When connecting to one account from multiple places, this priority value will help the server to determine where to deliver incoming messages (that aren't addressed to a specific resource already).
-			</para>
-
-			<para>
-				According to RFC 3921 servers will always deliver messages to the server with the highest priority value. Mmessages will not be delivered to resources with a negative priority setting (and should be saved as an off-line message if all available resources have a negative priority value).
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="private" type="boolean" scope="global">
-		<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 a 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="protocol" type="string" scope="channel">
-
-		<description>
-			<para>
-				For control channels with <emphasis>fill_by</emphasis> set to <emphasis>protocol</emphasis>: Set this setting to the name of the IM protocol of all contacts you want to see in this channel.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="proxy" type="string" scope="account">
-		<default>&lt;local&gt;&lt;auto&gt;</default>
-
-		<description>
-			<para>
-				A list of <emphasis>file transfer proxies</emphasis> for jabber. This isn't the connection proxy. Sorry, look in bitlbee.conf for those.
-			</para>
-
-			<para>
-				It's a semicolon-separated list of items that can be either <emphasis>JID,HOST,PORT</emphasis> or two special values, <emphasis>&lt;local&gt;</emphasis> (to try a direct connection first) and <emphasis>&lt;auto&gt;</emphasis> (to try to discover a proxy). For example, "&lt;local&gt;;proxy.somewhere.org,123.123.123.123,7777".
-			</para>
-			<para>
-				The address should point to a SOCKS5 bytestreams server, usually provided by jabber servers. This is only used for sending files. Note that the host address might not match what DNS tells you, and the port isn't always the same.
-			</para>
-			<para>
-				The correct way to get a socks proxy host/port is a mystery, and the file transfer might fail anyway. Maybe just try using dropbox instead.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="query_order" type="string" scope="global">
+				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>
@@ -1366,373 +518,13 @@
 	</bitlbee-setting>
 
-	<bitlbee-setting name="resource" type="string" scope="account">
-		<default>BitlBee</default>
-
-		<description>
-			<para>
-				Can be set for Jabber connections. You can use this to connect to your Jabber account from multiple clients at once, with every client using a different resource string.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="resource_select" type="string" scope="account">
-		<default>activity</default>
-		<possible-values>priority, activity</possible-values>
-
-		<description>
-			<para>
-				Because the IRC interface makes it pretty hard to specify the resource to talk to (when a buddy is online through different resources), this setting was added.
-			</para>
-
-			<para>
-				Normally it's set to <emphasis>priority</emphasis> which means messages will always be delivered to the buddy's resource with the highest priority. If the setting is set to <emphasis>activity</emphasis>, messages will be delivered to the resource that was last used to send you a message (or the resource that most recently connected).
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="root_nick" type="string" scope="global">
-		<default>root</default>
-
-		<description>
-			<para>
-				Normally the "bot" that takes all your BitlBee commands is called "root". If you don't like this name, you can rename it to anything else using the <emphasis>rename</emphasis> command, or by changing this setting.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="save_on_quit" type="boolean" scope="global">
-		<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="self_messages" type="string" scope="global">
-		<default>true</default>
-		<possible-values>true, false, prefix, prefix_notice</possible-values>
-
-		<description>
-			<para>
-				Change this setting to customize how (or whether) to show self-messages, which are messages sent by yourself from other locations (for example, mobile clients), for IM protocols that support it.
-			</para>
-
-			<para>
-				When this is set to "true", it will send those messages in the "standard" way, which is a PRIVMSG with source and target fields swapped.
-			</para>
-			
-			<para>
-				Since this isn't very well supported by some clients (the messages might appear in the wrong window), you can set it to "prefix" to show them as a normal message prefixed with "-> ", or use "prefix_notice" which is the same thing but with a NOTICE instead.
-			</para>
-
-			<para>
-				You can also set it to "false" to disable these messages completely.
-			</para>
-
-			<para>
-				This setting only applies to private messages. Self messages in groupchats are always shown, since they haven't caused issues in any clients so far.
-			</para>
-
-			<para>
-				More information: <emphasis>https://wiki.bitlbee.org/SelfMessages</emphasis>
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="server" type="string" scope="account">
-		<description>
-			<para>
-				Can be set for Jabber-connections. You might have to set this if the servername isn't equal to the part after the @ in the Jabber handle.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="show_ids" type="boolean" scope="account">
-		<default>true</default>
-
-		<description>
-			<para>
-				Enable this setting on a Twitter account to have BitlBee include a two-digit "id" in front of every message. This id can then be used for replies and retweets.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="show_offline" type="boolean" scope="global">
-		<default>false</default>
-
-		<description>
-			<para>
-				If enabled causes BitlBee to also show offline users in Channel. Online-users will get op, away-users voice and offline users none of both. This option takes effect as soon as you reconnect.
-			</para>
-			
-			<para>
-				Replaced with the <emphasis>show_users</emphasis> setting. See <emphasis>help show_users</emphasis>.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="show_users" type="string" scope="channel">
-		<default>online+,special%,away</default>
-
-		<description>
-			<para>
-				Comma-separated list of statuses of users you want in the channel,
-				and any modes they should have. The following statuses are currently
-				recognised: <emphasis>online</emphasis> (i.e. available, not
-				away), <emphasis>special</emphasis> (specific to the protocol), 
-				<emphasis>away</emphasis>, and <emphasis>offline</emphasis>.
-			</para>
-			
-			<para>
-				If a status is followed by a valid channel mode character
-				(@, % or +), it will be given to users with that status.
-				For example, <emphasis>online@,special%,away+,offline</emphasis> 
-				will show all users in the channel. Online people will
-				have +o, people who are online but away will have +v,
-				and others will have no special modes.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="simulate_netsplit" type="boolean" scope="global">
-		<default>true</default>
-
-		<description>
-			<para>
-				Some IRC clients parse quit messages sent by the IRC server to see if someone really left or just disappeared because of a netsplit. By default, BitlBee tries to simulate netsplit-like quit messages to keep the control channels window clean. If you don't like this (or if your IRC client doesn't support this) you can disable this setting.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="ssl" type="boolean" scope="account">
-		<default>false</default>
-
-		<description>
-			<para>
-				Currently only available for Jabber connections. Set this to true if you want to connect to the server on an SSL-enabled port (usually 5223).
-			</para>
-
-			<para>
-				Please note that this method of establishing a secure connection to the server has long been deprecated. You are encouraged to look at the <emphasis>tls</emphasis> setting instead.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="status" type="string" scope="account,global">
-		<description>
-			<para>
-				Most IM protocols support status messages, similar to away messages. They can be used to indicate things like your location or activity, without showing up as away/busy.
-			</para>
-
-			<para>
-				This setting can be used to set such a message. It will be available as a per-account setting for protocols that support it, and also as a global setting (which will then automatically be used for all protocols that support it).
-			</para>
-
-			<para>
-				Away states set using <emphasis>/away</emphasis> or the <emphasis>away</emphasis> setting will override this setting. To clear the setting, use <emphasis>set -del status</emphasis>.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="strip_html" type="boolean" scope="global">
-		<default>true</default>
-
-		<description>
-			<para>
-				Determines what BitlBee should do with HTML in messages. Normally this is turned on and HTML will be stripped from messages, if BitlBee thinks there is HTML.
-			</para>
-			<para>
-				If BitlBee fails to detect this sometimes (most likely in AIM messages over an ICQ connection), you can set this setting to <emphasis>always</emphasis>, but this might sometimes accidentally strip non-HTML things too.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="strip_newlines" type="boolean" scope="account">
-		<default>false</default>
-
-		<description>
-			<para>
-				Turn on this flag to prevent tweets from spanning over multiple lines.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="show_old_mentions" type="integer" scope="account">
-		<default>20</default>
-
-		<description>
-			<para>
-				This setting specifies the number of old mentions to fetch on connection. Must be less or equal to 200. Setting it to 0 disables this feature.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="tag" type="string" scope="account">
-		<description>
-			<para>
-				For every account you have, you can set a tag you can use to uniquely identify that account. This tag can be used instead of the account number (or protocol name, or part of the screenname) when using commands like <emphasis>account</emphasis>, <emphasis>add</emphasis>, etc. You can't have two accounts with one and the same account tag.
-			</para>
-
-			<para>
-				By default, it will be set to the name of the IM protocol. Once you add a second account on an IM network, a numeric suffix will be added, starting with 2.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="timezone" type="string" scope="global">
-		<default>local</default>
-		<possible-values>local, utc, gmt, timezone-spec</possible-values>
-
-		<description>
-			<para>
-				If message timestamps are available for offline messages or chatroom backlogs, BitlBee will display them as part of the message. By default it will use the local timezone. If you're not in the same timezone as the BitlBee server, you can adjust the timestamps using this setting.
-			</para>
-
-			<para>
-				Values local/utc/gmt should be self-explanatory. timezone-spec is a time offset in hours:minutes, for example: -8 for Pacific Standard Time, +2 for Central European Summer Time, +5:30 for Indian Standard Time.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="tls" type="boolean" scope="account">
-		<default>true</default>
-
-		<description>
-			<para>
-				By default (with this setting enabled), BitlBee will require Jabber servers to offer encryption via StartTLS and refuse to connect if they don't.
-			</para>
-
-			<para>
-				If you set this to "try", BitlBee will use StartTLS only if it's offered. With the setting disabled, StartTLS support will be ignored and avoided entirely.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="tls_verify" type="boolean" scope="account">
-		<default>true</default>
-
-		<description>
-			<para>
-				Currently only available for Jabber connections in combination with the <emphasis>tls</emphasis> setting. Set this to <emphasis>true</emphasis> if you want BitlBee to strictly verify the server's certificate against a list of trusted certificate authorities.
-			</para>
-
-			<para>
-				The hostname used in the certificate verification is the value of the <emphasis>server</emphasis> setting if the latter is nonempty and the domain of the username else. If you get a hostname related error when connecting to Google Talk with a username from the gmail.com or googlemail.com domain, please try to empty the <emphasis>server</emphasis> setting.
-			</para>
-
-			<para>
-				Please note that no certificate verification is performed when the <emphasis>ssl</emphasis> setting is used, or when the <emphasis>CAfile</emphasis> setting in <emphasis>bitlbee.conf</emphasis> is not set.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="to_char" type="string" scope="global">
-		<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="translate_to_nicks" type="boolean" scope="channel">
-		<default>true</default>
-
-		<description>
-			<para>
-				IRC's nickname namespace is quite limited compared to most IM protocols. Not any non-ASCII characters are allowed, in fact nicknames have to be mostly alpha-numeric. Also, BitlBee has to add underscores sometimes to avoid nickname collisions.
-			</para>
-
-			<para>
-				While normally the BitlBee user is the only one seeing these names, they may be exposed to other chatroom participants for example when addressing someone in the channel (with or without tab completion). By default BitlBee will translate these stripped nicknames back to the original nick. If you don't want this, disable this setting.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="type" type="string" scope="channel">
-		<default>control</default>
-		<possible-values>control, chat</possible-values>
-
-		<description>
-			<para>
-				BitlBee supports two kinds of channels: control channels (usually with a name starting with a &amp;) and chatroom channels (name usually starts with a #).
-			</para>
-
-			<para>
-				See <emphasis>help channels</emphasis> for a full description of channel types in BitlBee.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="typing_notice" type="boolean" scope="global">
-		<default>false</default>
-
-		<description>
-			<para>
-				Sends you a /notice when a user starts typing a message (if supported by the IM protocol and the user's client). To use this, you most likely want to use a script in your IRC client to show this information in a more sensible way.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="user_agent" type="string" scope="account">
-		<default>BitlBee</default>
-
-		<description>
-			<para>
-				Some Jabber servers are configured to only allow a few (or even just one) kinds of XMPP clients to connect to them.
-			</para>
-			
-			<para>
-				You can change this setting to make BitlBee present itself as a different client, so that you can still connect to these servers.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="utf8_nicks" type="boolean" scope="global">
-		<default>false</default>
-
-		<description>
-			<para>
-				Officially, IRC nicknames are restricted to ASCII. Recently some clients and servers started supporting Unicode nicknames though. To enable UTF-8 nickname support (contacts only) in BitlBee, enable this setting.
-			</para>
-			
-			<para>
-				To avoid confusing old clients, this setting is disabled by default. Be careful when you try it, and be prepared to be locked out of your BitlBee in case your client interacts poorly with UTF-8 nicknames.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="web_aware" type="string" scope="account">
-		<default>false</default>
-
-		<description>
-			<para>
-				ICQ allows people to see if you're on-line via a CGI-script. (http://status.icq.com/online.gif?icq=UIN) This can be nice to put on your website, but it seems that spammers also use it to see if you're online without having to add you to their contact list. So to prevent ICQ spamming, recent versions of BitlBee disable this feature by default.
-			</para>
-
-			<para>
-				Unless you really intend to use this feature somewhere (on forums or maybe a website), it's probably better to keep this setting disabled.
-			</para>
-		</description>
-	</bitlbee-setting>
-
-	<bitlbee-setting name="xmlconsole" type="boolean" scope="account">
-		<default>false</default>
-
-		<description>
-			<para>
-				The Jabber module allows you to add a buddy <emphasis>xmlconsole</emphasis> to your contact list, which will then show you the raw XMPP stream between you and the server. You can also send XMPP packets to this buddy, which will then be sent to the server.
-			</para>
-			<para>
-				If you want to enable this XML console permanently (and at login time already), you can set this setting.
-			</para>
-		</description>
+	<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>
 
@@ -1740,13 +532,8 @@
 		<short-description>Rename (renick) a buddy</short-description>
 		<syntax>rename &lt;oldnick&gt; &lt;newnick&gt;</syntax>
-		<syntax>rename -del &lt;nick&gt;</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>
-			
-			<para>
-				<emphasis>rename -del</emphasis> can be used to erase your manually set nickname for a contact and reset it to what was automatically generated.
 			</para>
 		</description>
@@ -1769,5 +556,5 @@
 
 			<para>
-				By default, this answers the last unanswered question (as defined by the <emphasis>query_order</emphasis> setting, which defaults to <emphasis>lifo</emphasis>). You can also specify a different question as an argument. You can use the <emphasis>qlist</emphasis> command for a list of questions.
+				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>
@@ -1785,23 +572,7 @@
 
 			<para>
-				By default, this answers the last unanswered question (as defined by the <emphasis>query_order</emphasis> setting, which defaults to <emphasis>lifo</emphasis>). 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="plugins">
-		<short-description>List all the external plugins and protocols</short-description>
-		<syntax>plugins [info &lt;name&gt;]</syntax>
-
-		<description>
-			<para>
-				This gives you a list of all the external plugins and protocols.
-			</para>
-
-			<para>
-				Use the <emphasis>info</emphasis> subcommand to get more details about a plugin.
-			</para>
-		</description>
-
+				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>
 
@@ -1820,5 +591,5 @@
 	<bitlbee-command name="register">
 		<short-description>Register yourself</short-description>
-		<syntax>register [&lt;password&gt;]</syntax>
+		<syntax>register &lt;password&gt;</syntax>
 
 		<description>
@@ -1832,9 +603,5 @@
 
 			<para>
-				To identify yourself in later sessions, you can use the <emphasis>identify</emphasis> command. To change your password later, you can use the <emphasis>set password</emphasis> command.
-			</para>
-
-			<para>
-				You can omit the password and enter it separately using the IRC /OPER command. This lets you enter your password without your IRC client echoing it on screen or recording it in logs.
+				To identify yourself in later sessions, you can use the <emphasis>identify</emphasis> command.
 			</para>
 		</description>
@@ -1843,5 +610,5 @@
 
 	<bitlbee-command name="identify">
-		<syntax>identify [-noload|-force] [&lt;password&gt;]</syntax>
+		<syntax>identify &lt;password&gt;</syntax>
 		<short-description>Identify yourself with your password</short-description>
 
@@ -1853,16 +620,4 @@
 			<para>
 				Once you're registered, you can change your password using <emphasis>set password &lt;password&gt;</emphasis>.
-			</para>
-
-			<para>
-				The <emphasis>-noload</emphasis> and <emphasis>-force</emphasis> flags can be used to identify when you're logged into some IM accounts already. <emphasis>-force</emphasis> will let you identify yourself and load all saved accounts (and keep the accounts you're logged into already).
-			</para>
-			
-			<para>
-				<emphasis>-noload</emphasis> will log you in but not load any accounts and settings saved under your current nickname. These will be overwritten once you save your settings (i.e. when you disconnect).
-			</para>
-
-			<para>
-				You can omit the password and enter it separately using the IRC /OPER command. This lets you enter your password without your IRC client echoing it on screen or recording it in logs.
 			</para>
 		</description>
@@ -1881,79 +636,55 @@
 
 	<bitlbee-command name="blist">
-		<syntax>blist [all|online|offline|away] [&lt;pattern&gt;]</syntax>
-		<short-description>List all the buddies in the current channel</short-description>
-
-		<description>
-			<para>
-				You can get a more 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>
-
-			<para>
-				A perl-compatible regular expression can be supplied as <emphasis>pattern</emphasis> to filter the results (case-insensitive).
-			</para>
-		</description>
-
-	</bitlbee-command>
-
-	<bitlbee-command name="group">
-		<short-description>Contact group management</short-description>
-		<syntax>group [ list | info &lt;group&gt; ]</syntax>
-
-		<description>
-			<para>
-				The <emphasis>group list</emphasis> command shows a list of all groups defined so far.
-			</para>
-			
-			<para>
-				The <emphasis>group info</emphasis> command shows a list of all members of a the group &lt;group&gt;.
-			</para>
-			
-			<para>
-				If you want to move contacts between groups, you can use the IRC <emphasis>/invite</emphasis> command. Also, if you use the <emphasis>add</emphasis> command in a control channel configured to show just one group, the new contact will automatically be added to that group.
-			</para>
-		</description>
-	</bitlbee-command>
-	
-	<bitlbee-command name="transfer">
-		<short-description>Monitor, cancel, or reject file transfers</short-description>
-		<syntax>transfer [&lt;cancel&gt; id | &lt;reject&gt;]</syntax>
-		
-		<description>
-			<para>
-				Without parameters the currently pending file transfers and their status will be listed. Available actions are <emphasis>cancel</emphasis> and <emphasis>reject</emphasis>. See <emphasis>help transfer &lt;action&gt;</emphasis> for more information.
-			</para>
-
-			<ircexample>
-				<ircline nick="ulim">transfer</ircline>
-			</ircexample>
-		</description>
-		
-		<bitlbee-command name="cancel">
-			<short-description>Cancels the file transfer with the given id</short-description>
-			<syntax>transfer &lt;cancel&gt; id</syntax>
-
-			<description>
-				<para>Cancels the file transfer with the given id</para>
-			</description>
-
-			<ircexample>
-				<ircline nick="ulim">transfer cancel 1</ircline>
-				<ircline nick="root">Canceling file transfer for test</ircline>
-			</ircexample>
-		</bitlbee-command>
-
-		<bitlbee-command name="reject">
-			<short-description>Rejects all incoming transfers</short-description>
-			<syntax>transfer &lt;reject&gt;</syntax>
-
-			<description>
-				<para>Rejects all incoming (not already transferring) file transfers. Since you probably have only one incoming transfer at a time, no id is necessary. Or is it?</para>
-			</description>
-
-			<ircexample>
-				<ircline nick="ulim">transfer reject</ircline>
-			</ircexample>
-		</bitlbee-command>
-	</bitlbee-command>
-	
+		<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 &lt;connection&gt; [&lt;new nick&gt;]</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 &lt;connection&gt; [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>
Index: doc/user-guide/docbook.xsl
===================================================================
--- doc/user-guide/docbook.xsl	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ doc/user-guide/docbook.xsl	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -19,5 +19,5 @@
 
 	<xsl:template match="ircaction">
-		<xsl:text> * </xsl:text><xsl:value-of select="@nick"/><xsl:text> </xsl:text><xsl:value-of select="normalize-space(.)"/><xsl:text>&#10;</xsl:text>
+		<xsl:text> * </xsl:text><xsl:value-of select="@nick"/><xsl:value-of select="normalize-space(.)"/><xsl:text>&#10;</xsl:text>
 	</xsl:template>
 
@@ -83,5 +83,4 @@
 
 	<xsl:template name="cmd">
-		<xsl:param name="prefix"/>
 		<xsl:variable name="thiscmd"><xsl:value-of select="$prefix"/><xsl:value-of select="@name"/></xsl:variable>
 		<xsl:attribute name="id">
Index: c/user-guide/genhelp.py
===================================================================
--- doc/user-guide/genhelp.py	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,240 +1,0 @@
-#!/usr/bin/env python
-
-# Usage: python genhelp.py input.xml output.txt
-# (Both python2 (>=2.5) or python3 work)
-#
-# The shebang above isn't used, set the PYTHON environment variable
-# before running ./configure instead
-
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-# 
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-# 
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor,
-# Boston, MA  02110-1301, USA.
-
-
-import os
-import re
-import sys
-import xml.etree.ElementTree as ET
-
-NORMALIZE_RE = re.compile(r"([^<>\s\t])[\s\t]+([^<>\s\t])")
-
-# Helpers
-
-def normalize(x):
-    """Normalize whitespace of a string.
-
-    The regexp turns any sequence of whitespace into a single space if it's in
-    the middle of the tag text, and then all newlines and tabs are removed,
-    keeping spaces.
-    """
-
-    x = NORMALIZE_RE.sub(r"\1 \2", x or '')
-    return x.replace("\n", "").replace("\t", "")
-
-def join(list):
-    """Turns any iterator into a string"""
-    return ''.join([str(x) for x in list])
-
-def fix_tree(tag, debug=False, lvl=''):
-    """Walks the XML tree and modifies it in-place fixing various details"""
-
-    # The include tags have an ugly namespace in the tag name. Simplify that.
-    if tag.tag.count("XInclude"):
-        tag.tag = 'include'
-
-    # Print a pretty tree-like representation of the processed tags
-    if debug:
-        print("%s<%s>%r" % (lvl, tag.tag, [tag.text, normalize(tag.text)]))
-
-    for subtag in tag:
-        fix_tree(subtag, debug, lvl + "  ")
-
-    if debug:
-        print("%s</%s>%r" % (lvl, tag.tag, [tag.tail, normalize(tag.tail)]))
-
-    # Actually normalize whitespace
-    if 'pre' not in tag.attrib:
-        tag.text = normalize(tag.text)
-    tag.tail = normalize(tag.tail)
-
-
-# Main logic
-
-def process_file(filename, parent=None):
-    try:
-        tree = ET.parse(open(filename)).getroot()
-    except:
-        sys.stderr.write("\nException while processing %s\n" % filename)
-        raise
-    fix_tree(tree)
-    return parse_tag(tree, parent)
-
-def parse_tag(tag, parent):
-    """Calls a tag_... function based on the tag name"""
-
-    fun = globals()["tag_%s" % tag.tag.replace("-", "_")]
-    return join(fun(tag, parent))
-
-def parse_subtags(tag, parent=None):
-    yield tag.text
-
-    for subtag in tag:
-        yield parse_tag(subtag, tag)
-
-    yield tag.tail
-
-
-# Main tag handlers
-
-def handle_subject(tag, parent):
-    """Tag handler for preface, chapter, sect1 and sect2 (aliased below)"""
-
-    yield '?%s\n' % tag.attrib['id']
-
-    first = True
-    for element in tag:
-        if element.tag in ["para", "variablelist", "simplelist",
-                           "command-list", "ircexample"]:
-            if not first:
-                # Spaces between paragraphs
-                yield "\n"
-            first = False
-
-            if element.attrib.get('title', ''):
-                yield element.attrib['title']
-                yield "\n"
-            yield join(parse_tag(element, tag)).rstrip("\n")
-            yield "\n"
-
-    yield "%\n"
-
-    for element in tag:
-        if element.tag in ["sect1", "sect2"]:
-            yield join(handle_subject(element, tag))
-
-    for element in tag.findall("bitlbee-command"):
-        yield join(handle_command(element))
-
-    for element in tag.findall("bitlbee-setting"):
-        yield join(handle_setting(element))
-
-def handle_command(tag, prefix=''):
-    """Tag handler for <bitlbee-command> (called from handle_subject)"""
-
-    this_cmd = prefix + tag.attrib['name']
-
-    yield "?%s\n" % this_cmd
-    for syntax in tag.findall("syntax"):
-        yield '\x02Syntax:\x02 %s\n' % syntax.text
-
-    yield "\n"
-    yield join(parse_subtags(tag.find("description"))).rstrip()
-    yield "\n"
-
-    for example in tag.findall("ircexample"):
-        yield "\n\x02Example:\x02\n"
-        yield join(parse_subtags(example)).rstrip()
-        yield "\n"
-
-    yield "%\n"
-
-    for element in tag.findall("bitlbee-command"):
-        yield join(handle_command(element, this_cmd + " "))
-
-def handle_setting(tag):
-    """Tag handler for <bitlbee-setting> (called from handle_subject)"""
-
-    yield "?set %s\n" % tag.attrib['name']
-    yield "\x02Type:\x02 %s\n" % tag.attrib["type"]
-    yield "\x02Scope:\x02 %s\n" % tag.attrib["scope"]
-
-    if tag.find("default") is not None:
-        yield "\x02Default:\x02 %s\n" % tag.findtext("default")
-
-    if tag.find("possible-values") is not None:
-        yield "\x02Possible Values:\x02 %s\n" % tag.findtext("possible-values")
-
-    yield "\n"
-    yield join(parse_subtags(tag.find("description"))).rstrip()
-    yield "\n%\n"
-
-
-# Aliases for tags that behave like subjects
-tag_preface = handle_subject
-tag_chapter = handle_subject
-tag_sect1 = handle_subject
-tag_sect2 = handle_subject
-
-# Aliases for tags that don't have any special behavior
-tag_ulink = parse_subtags
-tag_note = parse_subtags
-tag_book = parse_subtags
-tag_ircexample = parse_subtags
-
-
-# Handlers for specific tags
-
-def tag_include(tag, parent):
-    return process_file(tag.attrib['href'], tag)
-
-def tag_para(tag, parent):
-    return join(parse_subtags(tag)) + "\n\n"
-
-def tag_emphasis(tag, parent):
-    return "\x02%s\x02%s" % (tag.text, tag.tail)
-
-def tag_ircline(tag, parent):
-    return "\x02<%s>\x02 %s\n" % (tag.attrib['nick'], join(parse_subtags(tag)))
-
-def tag_ircaction(tag, parent):
-    return "\x02* %s\x02 %s\n" % (tag.attrib['nick'], join(parse_subtags(tag)))
-
-def tag_command_list(tag, parent):
-    yield "These are all root commands. See \x02help <command name>\x02 " \
-          "for more details on each command.\n\n"
-
-    for subtag in parent.findall("bitlbee-command"):
-        yield " * \x02%s\x02 - %s\n" % \
-            (subtag.attrib['name'],
-             subtag.findtext("short-description"))
-
-    yield "\nMost commands can be shortened. For example instead of " \
-          "\x02account list\x02, try \x02ac l\x02.\n\n"
-
-def tag_variablelist(tag, parent):
-    for subtag in tag:
-        yield " \x02%s\x02 - %s\n" % \
-            (subtag.findtext("term"),
-             join(parse_subtags(subtag.find("listitem/para"))))
-    yield '\n'
-
-def tag_simplelist(tag, parent):
-    for subtag in tag:
-        yield " - %s\n" % join(parse_subtags(subtag))
-    yield '\n'
-
-
-def main():
-    if len(sys.argv) != 3:
-        print("Usage: python genhelp.py input.xml output.txt")
-        return
-
-    # ensure that we really are in the same directory as the input file
-    os.chdir(os.path.dirname(os.path.abspath(sys.argv[1])))
-
-    txt = process_file(sys.argv[1])
-    open(sys.argv[2], "w").write(txt)
-
-if __name__ == '__main__':
-    main()
Index: doc/user-guide/help.txt
===================================================================
--- doc/user-guide/help.txt	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ doc/user-guide/help.txt	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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.
+%
Index: doc/user-guide/help.xml
===================================================================
--- doc/user-guide/help.xml	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ doc/user-guide/help.xml	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -1,3 +1,3 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?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">
 
@@ -14,9 +14,7 @@
   <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>channels</term><listitem><para>About creating and customizing channels</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>
-  <varlistentry><term>nick_changes</term><listitem><para>Changing your nickname without losing any settings</para></listitem></varlistentry>
-  <varlistentry><term>identify_methods</term><listitem><para>A list of ways to (auto-)identify to your account</para></listitem></varlistentry>
 </variablelist>
 
@@ -26,9 +24,9 @@
 
 <para>
-Some more help can be found on <ulink link="https://wiki.bitlbee.org/">https://wiki.bitlbee.org/</ulink>. Bugs can be reported at <ulink link="https://bugs.bitlbee.org/">https://bugs.bitlbee.org/</ulink>.
+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>
-For other things than bug reports, you can join <emphasis>#BitlBee</emphasis> on OFTC (<emphasis>irc.oftc.net</emphasis>) (OFTC, *not* freenode!) and flame us right in the face. :-)
+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>
 
@@ -45,9 +43,7 @@
   <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>channels</term><listitem><para>About creating and customizing channels</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>
-  <varlistentry><term>nick_changes</term><listitem><para>Changing your nickname without losing any settings</para></listitem></varlistentry>
-  <varlistentry><term>identify_methods</term><listitem><para>A list of ways to (auto-)identify to your account</para></listitem></varlistentry>
 </variablelist>
 
@@ -60,5 +56,4 @@
 <xi:include href="quickstart.xml"/>
 <xi:include href="commands.xml"/>
-<xi:include href="twitter.xml"/>
 <xi:include href="misc.xml"/>
 
Index: doc/user-guide/help.xsl
===================================================================
--- doc/user-guide/help.xsl	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ doc/user-guide/help.xsl	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -7,5 +7,5 @@
 	version="1.1">
 
-	<xsl:output method="text" encoding="utf-8" standalone="yes"/>
+	<xsl:output method="text" encoding="iso-8859-1" standalone="yes"/>
 	<xsl:strip-space elements="*"/>
 
@@ -31,5 +31,4 @@
 
 	<xsl:template name="subject">
-		<xsl:param name="id"/>
 		<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>&#10;</xsl:text>
@@ -59,8 +58,5 @@
 			<xsl:text>?set </xsl:text><xsl:value-of select="@name"/><xsl:text>&#10;</xsl:text>
 			<xsl:text>_b_Type:_b_ </xsl:text><xsl:value-of select="@type"/><xsl:text>&#10;</xsl:text>
-			<xsl:text>_b_Scope:_b_ </xsl:text><xsl:value-of select="@scope"/><xsl:text>&#10;</xsl:text>
-			<xsl:if test="default">
-				<xsl:text>_b_Default:_b_ </xsl:text><xsl:value-of select="default"/><xsl:text>&#10;</xsl:text>
-			</xsl:if>
+			<xsl:text>_b_Default:_b_ </xsl:text><xsl:value-of select="default"/><xsl:text>&#10;</xsl:text>
 			<xsl:if test="possible-values">
 				<xsl:text>_b_Possible Values:_b_ </xsl:text><xsl:value-of select="possible-values"/><xsl:text>&#10;</xsl:text>
@@ -73,9 +69,7 @@
 
 	<xsl:template match="command-list">
-		<xsl:text>These are all root commands. See _b_help &lt;command name&gt;_b_ for more details on each command.&#10;&#10;</xsl:text>
 		<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>&#10;</xsl:text>
 		</xsl:for-each>
-		<xsl:text>&#10;Most commands can be shortened. For example instead of _b_account list_b_, try _b_ac l_b_.&#10;</xsl:text>
 		<xsl:text>&#10;</xsl:text>
 	</xsl:template>
@@ -125,5 +119,4 @@
 
 	<xsl:template name="cmd">
-		<xsl:param name="prefix"/>
 		<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>
Index: doc/user-guide/misc.xml
===================================================================
--- doc/user-guide/misc.xml	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ doc/user-guide/misc.xml	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -1,13 +1,64 @@
 <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>(&amp;)</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>
-BitlBee now supports groupchats on all IM networks. This text will try to explain you how they work.
+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 control channels (or to root directly).
+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>
 
@@ -22,18 +73,28 @@
 
 <para>
-To open a groupchat, use the <emphasis>chat with</emphasis> command. For example,  to start a groupchat with the person <emphasis>lisa_msn</emphasis> in it, just type <emphasis>chat with lisa_msn</emphasis>. BitlBee will create a new virtual channel with root, you and lisa_msn in it.
+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>
-Then, just use the ordinary IRC <emphasis>/invite</emphasis> command to invite more people. 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.
+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>
-Some protocols (like Jabber) also support named groupchats. BitlBee supports these too. You can use the <emphasis>chat add</emphasis> command to join them. See <emphasis>help chat add</emphasis> for more information.
+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>
-If you don't know the name of a named groupchat, you can try the <emphasis>chat list</emphasis> command to get a list of chatrooms from a server. See <emphasis>help chat list</emphasis> for usage instructions.
+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>
 
@@ -42,446 +103,29 @@
 
 <para>
-To mark yourself as away, you can just use the <emphasis>/away</emphasis> command in your IRC client. BitlBee supports most away-states supported by the protocols.
+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>
-Away states have different names across different protocols. BitlBee will try to pick the best available option for every connection:
+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</member>
-	<member>NA</member>
-	<member>Busy, DND</member>
-	<member>BRB</member>
-	<member>Phone</member>
-	<member>Lunch, Food</member>
-	<member>Invisible, Hidden</member>
+	<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" will be chosen.
+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. Most IM-protocols can also show this additional information to your buddies.
-</para>
-
-<para>
-If you want to set an away state for only one of your connections, you can use the per-account <emphasis>away</emphasis> setting. See <emphasis>help set away</emphasis>.
+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>
 
-<sect1 id="nick_changes">
-<title>Changing your nickname</title>
-
-<para>
-BitlBee now allows you to change your nickname. So far this was not possible because it made managing saved accounts more complicated.
-</para>
-
-<para>
-The restriction no longer exists now though. When you change your nick (just using the <emphasis>/nick</emphasis> command), your logged-in status will be reset, which means any changes made to your settings/accounts will not be saved.
-</para>
-
-<para>
-To restore your logged-in status, you need to either use the <emphasis>register</emphasis> command to create an account under the new nickname, or use <emphasis>identify -noload</emphasis> to re-identify yourself under the new nickname. The <emphasis>-noload</emphasis> flag tells the command to verify your password and log you in, but not load any new settings. See <emphasis>help identify</emphasis> for more information.
-</para>
-
-</sect1>
-
-<sect1 id="channels">
-<title>Dealing with channels</title>
-
-<para>
-You can have as many channels in BitlBee as you want. You maintain your channel list using the <emphasis>channel</emphasis> command. You can create new channels by just joining them, like on regular IRC networks.
-</para>
-
-<para>
-You can create two kinds of channels. Control channels, and groupchat channels. By default, BitlBee will set up new channels as control channels if their name starts with an &amp;, and as chat channels if it starts with a #.
-</para>
-
-<para>
-Control channels are where you see your contacts. By default, you will have one control channel called &amp;bitlbee, containing all your contacts. But you can create more, if you want, and divide your contact list across several channels.
-</para>
-
-<para>
-For example, you can have one channel with all contacts from your jabber account in it. Or all contacts from the group called "Work".
-</para>
-
-<para>
-Type <emphasis>help channels2</emphasis> to read more.
-</para>
-
-</sect1>
-
-<sect1 id="channels2">
-<title>Creating a channel</title>
-
-<para>
-When you create a new channel, BitlBee will try to guess from its name which contacts to fill it with. For example, if the channel name (excluding the &amp;) matches the name of a group in which you have one or more contacts, the channel will contain all those contacts.
-</para>
-
-<para>
-Any valid account ID (so a number, protocol name or part of screenname, as long as it's unique) can also be used as a channel name. So if you just join &amp;jabber, it will contain all your jabber contacts. And if you have a Facebook account set up, you can see its contacts by just joining &amp;facebook.
-</para>
-
-<para>
-To start a simple group chat, you simply join a channel which a name starting with #, and invite people into it. All people you invite have to be on the same IM network and contact list.
-</para>
-
-<para>
-If you want to configure your own channels, you can use the <emphasis>channel set</emphasis> command. See <emphasis>help channels3</emphasis> for more information.
-</para>
-
-</sect1>
-
-<sect1 id="channels3">
-<title>Configuring a control channel</title>
-
-<para>
-The most important setting for a control channel is <emphasis>fill_by</emphasis>. It
-tells BitlBee what information should be used to decide if someone should be shown
-in the channel or not. After setting this setting to, for example, <emphasis>account</emphasis>, you
-also have to set the <emphasis>account</emphasis> setting. Example:
-</para>
-
-<ircexample>
-	<ircline nick="wilmer">chan &amp;wlm set fill_by account</ircline>
-	<ircline nick="root">fill_by = `account'</ircline>
-	<ircline nick="wilmer">chan &amp;wlm set account msn</ircline>
-	<ircline nick="root">account = `msn'</ircline>
-</ircexample>
-
-<para>
-Also, each channel has a <emphasis>show_users</emphasis> setting which lets you
-choose, for example, if you want to see only online contacts in a channel, or
-also/just offline contacts. Example:
-</para>
-
-<ircexample>
-	<ircline nick="wilmer">chan &amp;offline set show_users offline</ircline>
-	<ircline nick="root">show_users = `offline'</ircline>
-</ircexample>
-
-<para>
-See the help information for all these settings for more information.
-</para>
-
-</sect1>
-
-<sect1 id="nick_format">
-<title>Nickname formatting</title>
-
-<para>
-The <emphasis>nick_format</emphasis> setting can be set globally using
-the <emphasis>set</emphasis> command, or per account using <emphasis>account
-set</emphasis> (so that you can set a per-account suffix/prefix or have 
-nicknames generated from full names for certain accounts).
-</para>
-
-<para>
-The setting is basically some kind of format string. It can contain normal
-text that will be copied to the nick, combined with several variables:
-</para>
-
-<variablelist>
-	<varlistentry><term>%nick</term><listitem><para>Nickname suggested for this contact by the IM protocol, or just the handle if no nickname was suggested.</para></listitem></varlistentry>
-	<varlistentry><term>%handle</term><listitem><para>The handle/screenname of the contact.</para></listitem></varlistentry>
-	<varlistentry><term>%full_name</term><listitem><para>The full name of the contact.</para></listitem></varlistentry>
-	<varlistentry><term>%first_name</term><listitem><para>The first name of the contact (the full name up to the first space).</para></listitem></varlistentry>
-	<varlistentry><term>%group</term><listitem><para>The name of the group this contact is a member of</para></listitem></varlistentry>
-	<varlistentry><term>%account</term><listitem><para>Account tag of the contact</para></listitem></varlistentry>
-</variablelist>
-
-<para>
-Invalid characters (like spaces) will always be stripped. Depending on your
-locale settings, characters with accents will be converted to ASCII.
-</para>
-
-<para>
-See <emphasis>help nick_format2</emphasis> for some more information.
-</para>
-
-</sect1>
-
-<sect1 id="nick_format2">
-<title>Nickname formatting - modifiers</title>
-
-<para>
-Two modifiers are currently available: You can include only the first few
-characters of a variable by putting a number right after the %. For
-example, <emphasis>[%3group]%-@nick</emphasis> will include only the first
-three characters of the group name in the nick.
-</para>
-
-<para>
-Also, you can truncate variables from a certain character using
-the <emphasis>-</emphasis> modifier. For example, you may want to leave out
-everything after the @. <emphasis>%-@handle</emphasis> will expand to
-everything in the handle up to the first @.
-</para>
-
-</sect1>
-
-<sect1 id="identify_methods">
-<title>Identifying to your BitlBee account</title>
-
-<para>There are several methods to identify (log in) to your registered BitlBee account. All of these are equivalent:</para>
-
-<variablelist>
-	<varlistentry>
-		<term>The 'identify' command</term>
-		<listitem><para>sent to &amp;bitlbee or root. See <emphasis>help identify</emphasis> for details.</para></listitem>
-	</varlistentry>
-	<varlistentry>
-		<term>/msg nickserv identify</term>
-		<listitem><para>Same as above, but sent to NickServ. Useful with some auto-identify scripts.</para></listitem>
-	</varlistentry>
-	<varlistentry>
-		<term>/nickserv or /ns</term>
-		<listitem><para>Same as above, but using the command aliases to NickServ.</para></listitem>
-	</varlistentry>
-	<varlistentry>
-		<term>Server password</term>
-		<listitem><para>A field in the server connection settings of your irc client.</para></listitem>
-	</varlistentry>
-	<varlistentry>
-		<term>SASL PLAIN</term>
-		<listitem><para>A newer method, good choice if your client supports it. (Note: "SASL" is <emphasis>not</emphasis> related to "SSL")</para></listitem>
-	</varlistentry>
-</variablelist>
-
-<para>To configure your client to auto-identify, the last two methods are recommended. SASL if you have it, but server password is often the easiest.</para>
-
-<para>Note: If you changed bitlbee.conf to have <emphasis>AuthMode = Closed</emphasis>, server password will be used for that instead. If you have never heard of that setting before, you can ignore it and just use it.</para>
-
-</sect1>
-
-<sect1 id="whatsnew010206">
-<title>New stuff in BitlBee 1.2.6</title>
-
-<para>
-Twitter support. See <emphasis>help account add twitter</emphasis>.
-</para>
-</sect1>
-
-<sect1 id="whatsnew010300">
-<title>New stuff in BitlBee 1.3dev</title>
-
-<para>
-Support for multiple configurable control channels, each with a subset of
-your contact list. See <emphasis>help channels</emphasis> for more
-information.
-</para>
-
-<para>
-File transfer support for some protocols (more if you use libpurple). Just
-/DCC SEND stuff. Incoming files also become DCC transfers.
-</para>
-
-<para>
-Only if you run your own BitlBee instance: You can build a BitlBee that uses
-libpurple for connecting to IM networks instead of its own code, adding
-support for some of the more obscure IM protocols and features.
-</para>
-
-<para>
-Many more things, briefly described in <emphasis>help news1.3</emphasis>.
-</para>
-</sect1>
-
-<sect1 id="whatsnew030000">
-<title>New stuff in BitlBee 3.0</title>
-
-<para>
-BitlBee can be compiled with support for OTR message encryption (not available
-on public servers since encryption should be end-to-end).
-</para>
-
-<para>
-The MSN module was heavily updated to support features added to MSN Messenger
-over the recent years. You can now see/set status messages, send offline
-messages, and many strange issues caused by Microsoft breaking old-protocol
-compatibility should now be resolved.
-</para>
-
-<para>
-Twitter extended: IRC-style replies ("BitlBee:") now get converted to proper
-Twitter replies ("@BitlBee") and get a reference to the original message
-(see <emphasis>help set auto_reply_timeout</emphasis>). Retweets and some
-other stuff is also supported now (see <emphasis>help set commands</emphasis>).
-</para>
-</sect1>
-
-<sect1 id="news1.3">
-<title>New stuff in BitlBee 1.3dev (details)</title>
-
-<para>
-Most of the core of BitlBee was rewritten since the last release. This entry
-should sum up the majority of the changes.
-</para>
-
-<para>
-First of all, you can now have as many control channels as you want. Or you
-can have none, it's finally possible to leave &amp;bitlbee and still talk to
-all your contacts. Or you can have a &amp;work with all your work-related
-contacts, or a &amp;msn with all your MSN Messenger contacts. See <emphasis>help
-channels</emphasis> for more information about this.
-</para>
-
-<para>
-Also, you can change how nicknames are generated for your contacts. Like
-automatically adding a [fb] tag to the nicks of all your Facebook contacts.
-See <emphasis>help nick_format</emphasis>.
-</para>
-
-<para>
-When you're already connected to a BitlBee server and you connect from
-elsewhere, you can take over the old session.
-</para>
-
-<para>
-Instead of account numbers, accounts now also get tags. These are
-automatically generated but can be changed (<emphasis>help set
-tag</emphasis>). You can now use them instead of accounts numbers.
-(Example: <emphasis>acc gtalk on</emphasis>)
-</para>
-
-<para>
-Last of all: You can finally change your nickname and
-shorten root commands (try <emphasis>acc li</emphasis> instead
-of <emphasis>account list</emphasis>).
-</para>
-
-</sect1>
-
-<sect1 id="whatsnew030005">
-<title>New stuff in BitlBee 3.0.5</title>
-
-<para>
-OAuth2 support in Jabber module (see <emphasis>help set oauth</emphasis>).
-For better password security when using Google Talk, Facebook XMPP, or for
-using MSN Messenger via XMPP. Especially recommended on public servers.
-</para>
-
-<para>
-Starting quick groupchats on Jabber is easier now (using the <emphasis>chat
-with</emphasis> command, or <emphasis>/join</emphasis> + <emphasis>/invite</emphasis>).
-</para>
-
-<para>
-SSL certificate verification. Works only with GnuTLS, and needs to be enabled
-by updating your <emphasis>bitlbee.conf</emphasis>.
-</para>
-</sect1>
-
-<sect1 id="whatsnew030200">
-<title>New stuff in BitlBee 3.2</title>
-
-<para>
-Upgradeed to Twitter API version 1.1. This is necessary because previous
-versions will stop working from March 2013. At the same time, BitlBee now
-supports the streaming API and incoming direct messages.
-</para>
-</sect1>
-
-<sect1 id="whatsnew030202">
-<title>New stuff in BitlBee 3.2.2</title>
-
-<para>
-The OTR plugin now uses libotr 4.0 (AKA libotr5 in debian based distros)
-</para>
-
-<para>
-A few minor fixes/additions, like being able to use /oper to change
-passwords with <emphasis>account tag set -del password</emphasis>
-</para>
-</sect1>
-
-<sect1 id="whatsnew030400">
-<title>New stuff in BitlBee 3.4</title>
-
-<para>
-Lots of bugfixes! <emphasis>Important:</emphasis> Recompiling third party plugins such as bitlbee-steam or bitlbee-facebook is <emphasis>required</emphasis>!
-</para>
-
-<simplelist>
-	<member><emphasis>twitter:</emphasis> Filter channels - Search by keyword/hashtag or a list of users. See the <emphasis>HowtoTwitter</emphasis> wiki page for more details!</member>
-	<member><emphasis>twitter:</emphasis> Add "rawreply" command, like reply but bitlbee won't add @mention. Also add "favorite" / "fav" command aliases.</member>
-	<member><emphasis>twitter:</emphasis> Start stream from last tweet on connect/reconnect to avoid showing duplicate tweets</member>
-	<member><emphasis>jabber:</emphasis> Fixed crashes with file transfers (they still fail at bypassing NATs, but at least they fail without crashing)</member>
-	<member><emphasis>purple:</emphasis> Improved support for gadugadu, whatsapp and telegram.</member>
-	<member><emphasis>msn:</emphasis> disabled in this release since the protocol we used (MSNP18) stopped working.</member>
-	<member>Add a 'pattern' parameter to the blist command, to filter it.</member>
-	<member>The <emphasis>utf8_nicks</emphasis> setting should be more reliable now.</member>
-</simplelist>
-
-<para>
-See the full changelog for details!
-</para>
-
-</sect1>
-
-<sect1 id="whatsnew030401">
-<title>New stuff in BitlBee 3.4.1</title>
-
-<simplelist>
-	<member><emphasis>msn:</emphasis> Upgraded protocol version to MSNP21, works again</member>
-	<member><emphasis>jabber:</emphasis> Add "hipchat" protocol, for smoother login. Takes the same username as the official client. Note that unlike the 'hip-cat' branch, this doesn't preload channels. See the <emphasis>HowtoHipchat</emphasis> wiki page for details</member>
-	<member><emphasis>jabber:</emphasis> Gmail notifications support</member>
-	<member><emphasis>twitter:</emphasis> Show quoted tweets inline. Added "url" command, can be used to quote tweets.</member>
-</simplelist>
-
-</sect1>
-
-<sect1 id="whatsnew030402">
-<title>New stuff in BitlBee 3.4.2</title>
-
-<simplelist>
-	<member><emphasis>irc:</emphasis> Self-messages (messages sent by yourself from other IM clients), see <emphasis>help set self_messages</emphasis>. IRCv3.1 support and part of 3.2. Many important groupchat related bugfixes.</member>
-	<member><emphasis>jabber:</emphasis> Carbons, see <emphasis>help set carbons</emphasis>. Removed facebook XMPP, use bitlbee-facebook instead. SASL ANONYMOUS login, see <emphasis>help set anonymous</emphasis>.</member>
-	<member><emphasis>hipchat:</emphasis> Channels can now be added with <emphasis>chat add hipchat "channel name"</emphasis> which tries to guess the channel JID.</member>
-	<member><emphasis>skype:</emphasis> Show all messages as groupchats since we can't tell which ones are private. This plugin is mostly-deprecated and mostly-broken, use the skypeweb purple plugin or msn instead.</member>
-	<member><emphasis>purple:</emphasis> Fix problems remembering SSL certificates as trusted. Groupchat related fixes. Better error reporting. Fixed setting jabber away states.</member>
-</simplelist>
-
-<para>
-	And lots of bugfixes / stability improvements. See the full changelog for details!
-</para>
-
-</sect1>
-
-<sect1 id="whatsnew030500">
-<title>New stuff in BitlBee 3.5</title>
-
-<simplelist>
-	<member>New commands: <emphasis>chat list</emphasis> and <emphasis>plugins</emphasis>. New settings: <emphasis>nick_lowercase</emphasis>, <emphasis>nick_underscores</emphasis></member>
-	<member><emphasis>twitter:</emphasis> Hide muted tweets / no-retweets, add mute/unmute commands. Show full version of extended tweets.</member>
-	<member><emphasis>jabber:</emphasis> <emphasis>always_use_nicks</emphasis> channel setting. Don't send parts in a chat if someone is still connected from other devices. Personal oauth token login for hipchat.</member>
-	<member><emphasis>purple:</emphasis> Setting /topic. Fixes for SIPE and LINE. Don't ask for password if not needed (hangouts, telegram). Set nicks to %full_name for a few protocols (hangouts, funyahoo, icq, line)</member>
-</simplelist>
-
-<para>
-See the full changelog for details!
-</para>
-
-
-<sect1 id="whatsnew030600">
-<title>New stuff in BitlBee 3.6</title>
-
-<simplelist>
-	<member>Removed msn, skype, yahoo, oscar. Use the skypeweb and icyque purple plugins.</member>
-	<member>Fix twitter (disable stream setting, set tweet length to 280)</member>
-	<member>Add server-time IRCv3 capability</member>
-	<member>Large performance improvements for large contact lists</member>
-</simplelist>
-
-<para>
-See the full changelog for details!
-</para>
-
-</sect1>
-
-</sect1>
-
 </chapter>
Index: doc/user-guide/quickstart.xml
===================================================================
--- doc/user-guide/quickstart.xml	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ doc/user-guide/quickstart.xml	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -3,13 +3,13 @@
 
 <para>
-Welcome to BitlBee, your IRC gateway to other instant messaging protocols.
+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>&amp;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>.
+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 to 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 &lt;password&gt;</emphasis> (without the brackets!).
+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 &lt;password&gt;</emphasis> (without the brackets!).
 </para>
 
@@ -34,18 +34,14 @@
 
 <para>
-For instance, suppose you have a Jabber account at jabber.org with handle <emphasis>bitlbee@jabber.org</emphasis> with password <emphasis>QuickStart</emphasis>, you would:
+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 jabber bitlbee@jabber.org QuickStart</ircline>
+	<ircline nick="you">account add oscar 72696705 QuickStart login.icq.com</ircline>
 	<ircline nick="root">Account successfully added</ircline>
 </ircexample>
 
 <para>
-The other built-in IM protocol is twitter. Some protocols may be available as plugins that you can install, such as facebook, steam, discord and omegle. And you can get even more protocols by using the libpurple variant of BitlBee.
-</para>
-
-<para>
-For a list of currently supported protocols, use the <emphasis>plugins</emphasis> command. For more information about the <emphasis>account add</emphasis> command, see <emphasis>help account add</emphasis>.
+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>
 
@@ -57,26 +53,28 @@
 
 <sect1 id="quickstart3">
-<title>Step Four: Managing Contact Lists: Add, Remove and Rename</title>
+<title>Managing Contact Lists: Rename</title>
+
+<!--quickstart3-->
+<para>
+<emphasis>Step Three: Managing Contact Lists: Rename</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 &lt;connection&gt; &lt;handle&gt;</emphasis>
+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 &lt;oldnick&gt; &lt;newnick&gt;</emphasis>
 </para>
 
 <ircexample>
-	<ircline nick="you">add 0 r2d2@example.com</ircline>
-	<ircaction nick="r2d2"> has joined <emphasis>&amp;bitlbee</emphasis></ircaction>
+	<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>
-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 r2d3</emphasis>
-</para>
-
-<para>
-Finally, if you have multiple users with similar names you may use the <emphasis>rename</emphasis> command to make it easier to remember: <emphasis>rename r2d2_ r2d2_aim</emphasis>
-</para>
 
 <para>
@@ -87,4 +85,34 @@
 
 <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 &lt;connection&gt; &lt;handle&gt;</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 &lt;nick&gt;</emphasis>
+</para>
+
+<para>
+When finished, type <emphasis>help quickstart5</emphasis> to continue.
+</para>
+
+</sect1>
+
+<sect1 id="quickstart5">
 <title>Chatting</title>
 
@@ -94,39 +122,27 @@
 
 <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 &amp;bitlbee:
+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>
+	<ircline nick="tux"> you: a bit chilly!</ircline>
 </ircexample>
 
 <para>
-Note that, although all contacts are in the &amp;bitlbee channel, only tux will actually receive this message. The &amp;bitlbee channel shouldn't be confused with a real IRC channel.
+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>
-If you prefer chatting in a separate window, use the <emphasis>/msg</emphasis> or <emphasis>/query</emphasis> command, just like on real IRC. BitlBee will remember how you talk to someone and show his/her responses the same way. If you want to change the default behaviour (for people you haven't talked to yet), see <emphasis>help set private</emphasis>.
-</para>
-
-<para>
-You know the basics. If you want to know about some of the neat features BitlBee offers, please type <emphasis>help quickstart5</emphasis>.
+You know the basics. If you want to get to know more about BitlBee, please type <emphasis>help quickstart6</emphasis>.
 </para>
 
 </sect1>
 
-<sect1 id="quickstart5">
+<sect1 id="quickstart6">
 <title>Further Resources</title>
 
 <para>
-<emphasis>So you want more than just chatting? Or maybe you're just looking for more features?</emphasis>
-</para>
-
-<para>
-With multiple channel support you can have contacts for specific protocols in their own channels, for instance, if you <emphasis>/join &amp;jabber</emphasis> you will join a channel that only contains your jabber contacts.
-</para>
-
-<para>
-Account tagging allows you to use the given account name rather than a number when referencing your account. If you wish to turn off your gtalk account, you may <emphasis>account gtalk off</emphasis> rather than <emphasis>account 3 off</emphasis> where "3" is the account number.
+<emphasis>So you want more than just chatting? Or maybe you're just looking for a feature?</emphasis>
 </para>
 
@@ -140,5 +156,5 @@
 
 <para>
-If you're still looking for something, please visit us in #bitlbee on the OFTC network (irc.oftc.net). Good luck and enjoy the Bee!
+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>
 
Index: c/user-guide/twitter.xml
===================================================================
--- doc/user-guide/twitter.xml	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,29 +1,0 @@
-<chapter id="twitter">
-<title>Twitter</title>
-
-<para>
-Twitter allows you to use a set of commands in the Twitter channel/query. See <emphasis>help set commands</emphasis>.
-</para>
-
-<para>
-Anything that doesn't look like a command will be treated as a new message. Watch out for typos, or to avoid this behaviour, set the <emphasis>commands</emphasis> setting to <emphasis>strict</emphasis>, which causes the <emphasis>post</emphasis> command to become mandatory for posting. See <emphasis>help set commands</emphasis>.
-</para>
-
-<para>
-Here is a short description of the commands available:
-<variablelist>
-<varlistentry><term>undo [&lt;#id&gt;]</term><listitem><para>Delete your last message (or one with the given ID)</para></listitem></varlistentry>
-<varlistentry><term>rt &lt;screenname|#id&gt;</term><listitem><para>Retweet someone's last message (or one with the given ID)</para></listitem></varlistentry>
-<varlistentry><term>reply &lt;screenname|#id&gt;</term><listitem><para>Reply to a message (with a reply-to reference, mentions only one user)</para></listitem></varlistentry>
-<varlistentry><term>rawreply &lt;screenname|#id&gt;</term><listitem><para>Reply to a message (with no reply-to reference)</para></listitem></varlistentry>
-<varlistentry><term>greply &lt;screenname|#id&gt;</term><listitem><para>Group reply to a message (with reply-to, mentions every user in the conversation)</para></listitem></varlistentry>
-<varlistentry><term>report &lt;screenname|#id&gt;</term><listitem><para>Report the given user (or the user who posted the message with the given ID) for sending spam. This will also block them.</para></listitem></varlistentry>
-<varlistentry><term>follow &lt;screenname&gt;</term><listitem><para>Start following a person</para></listitem></varlistentry>
-<varlistentry><term>unfollow &lt;screenname&gt;</term><listitem><para>Stop following a person</para></listitem></varlistentry>
-<varlistentry><term>favourite &lt;screenname|#id&gt;</term><listitem><para>Favo<emphasis>u</emphasis>rite the given user's most recent message, or the given ID.</para></listitem></varlistentry>
-<varlistentry><term>post &lt;message&gt;</term><listitem><para>Post a message</para></listitem></varlistentry>
-<varlistentry><term>url &lt;screenname|#id&gt;</term><listitem><para>Show URL for a message to open it in a browser (and see context)</para></listitem></varlistentry>
-</variablelist>
-</para>
-
-</chapter>
Index: doc/user-guide/user-guide.html
===================================================================
--- doc/user-guide/user-guide.html	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ doc/user-guide/user-guide.html	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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-&lt;version&gt;.tar.gz</strong></span> where &lt;version&gt; 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 &lt;nick&gt; &lt;text&gt;</strong></span>
+or <span><strong class="command">/query &lt;nick&gt;</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">&lt;nick&gt;: &lt;text&gt;</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 &lt;password&gt;</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 &lt;password&gt;</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 &lt;protocol&gt; &lt;username&gt; &lt;password&gt; [&lt;server&gt;]</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">&lt; you&gt; </code><strong class="userinput"><code>account add oscar 72696705 QuickStart login.icq.com</code></strong>
+<code class="prompt">&lt; root&gt; </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 &lt;oldnick&gt; &lt;newnick&gt;</em></span></p><pre class="screen"><code class="prompt">&lt; you&gt; </code><strong class="userinput"><code>rename tux_ bitlbeetux</code></strong>
+ * tux_is now known as bitlbeetux
+<code class="prompt">&lt; root&gt; </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 &lt;connection&gt; &lt;handle&gt;</em></span></p><pre class="screen"><code class="prompt">&lt; you&gt; </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 &lt;nick&gt;</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">&lt; you&gt; </code><strong class="userinput"><code>tux: hey, how's the weather down there?</code></strong>
+<code class="prompt">&lt; tux&gt; </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 &lt;action&gt; [&lt;arguments&gt;]
+</pre><p>
+        </p><p>
+				Available actions: add, del, list, on, off. See <span class="emphasis"><em>help account &lt;action&gt;</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 &lt;protocol&gt; &lt;username&gt; &lt;password&gt; [&lt;server&gt;]
+</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 &lt;protocol&gt;</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 &lt;handle&gt; &lt;password&gt; [&lt;servertag&gt;]
+</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: [&lt;servername&gt;[:&lt;portnumber&gt;][: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">&lt; wilmer&gt; </code><strong class="userinput"><code>account add jabber example@gmail.com hobbelmeeuw talk.google.com:5223:ssl</code></strong>
+<code class="prompt">&lt; root&gt; </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 &lt;handle&gt; &lt;password&gt;
+</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 &lt;handle&gt; &lt;password&gt; [&lt;servername&gt;]
+</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">&lt; wilmer&gt; </code><strong class="userinput"><code>account add oscar 72696705 hobbelmeeuw login.icq.com</code></strong>
+<code class="prompt">&lt; root&gt; </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 &lt;handle&gt; &lt;password&gt;
+</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 &lt;account id&gt;
+</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 [&lt;account id&gt;]
+</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 [&lt;account id&gt;]
+</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 &lt;connection&gt; &lt;handle&gt; [&lt;nick&gt;]
+</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">&lt; ctrlsoft&gt; </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 &lt;connection&gt; &lt;handle&gt;
+info &lt;nick&gt;
+</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">&lt; ctrlsoft&gt; </code><strong class="userinput"><code>info 0 72696705</code></strong>
+<code class="prompt">&lt; root&gt; </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 &lt;nick&gt;
+</pre><p>
+        </p><p>
+				Removes the specified nick from your buddy list. 
+			</p><pre class="screen"><code class="prompt">&lt; ctrlsoft&gt; </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 &lt;nick&gt;
+block &lt;connection&gt; &lt;handle&gt;
+</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 &lt;nick&gt;
+allow &lt;connection&gt; &lt;handle&gt;
+</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 [&lt;variable&gt; [&lt;value&gt;]]
+</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">&lt; ctrlsoft&gt; </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 &lt;oldnick&gt; &lt;newnick&gt;
+</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">&lt; itsme&gt; </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 [&lt;number&gt;]
+</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 [&lt;number&gt;]
+</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 &lt;password&gt;
+</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 &lt;password&gt;
+</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 &lt;password&gt;</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 &lt;password&gt;
+</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 &lt;connection&gt; [&lt;new nick&gt;]
+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">&lt; wouter&gt; </code><strong class="userinput"><code>nick 1 "Wouter Paesen"</code></strong>
+<code class="prompt">&lt; root&gt; </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 &lt;connection&gt; [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">(&amp;)</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>
Index: doc/user-guide/user-guide.txt
===================================================================
--- doc/user-guide/user-guide.txt	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ doc/user-guide/user-guide.txt	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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.
+
Index: doc/user-guide/user-guide.xml
===================================================================
--- doc/user-guide/user-guide.xml	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ doc/user-guide/user-guide.xml	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -1,3 +1,3 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?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">
 
@@ -21,11 +21,22 @@
 
 	<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 BitlBee User Guide. For now, the on-line help is
-		the most up-to-date documentation. Although this document shares
-		some parts with the on-line help system, other parts might be
-		very outdated.
+   		This is the initial release of the BitlBee User Guide.
    </releaseinfo>
 
@@ -37,5 +48,4 @@
   <xi:include href="quickstart.xml"/>
   <xi:include href="commands.xml"/>
-  <xi:include href="twitter.xml"/>
   <xi:include href="misc.xml"/>
 
Index: help.c
===================================================================
--- help.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ help.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -1,6 +1,6 @@
-/********************************************************************\
+  /********************************************************************\
   * BitlBee -- An IRC to other IM-networks gateway                     *
   *                                                                    *
-  * Copyright 2002-2010 Wilmer van der Gaast and others                *
+  * Copyright 2002-2004 Wilmer van der Gaast and others                *
   \********************************************************************/
 
@@ -20,17 +20,16 @@
   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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
+  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 "help.h"
-#undef read
+#undef read 
 #undef write
 
-#define BUFSIZE 2048
+#define BUFSIZE 1100
 
-help_t *help_init(help_t **help, const char *helpfile)
+help_t *help_init( help_t **help )
 {
 	int i, buflen = 0;
@@ -39,85 +38,70 @@
 	time_t mtime;
 	struct stat stat[1];
-
-	*help = h = g_new0(help_t, 1);
-
-	h->fd = open(helpfile, O_RDONLY);
-
-	if (h->fd == -1) {
-		g_free(h);
-		return(*help = NULL);
+	
+	*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);
+	
+	if( fstat( h->fd, stat ) != 0 )
+	{
+		g_free( h );
+		return( *help = NULL );
 	}
 	mtime = stat->st_mtime;
-
-	s = g_new(char, BUFSIZE + 1);
+	
+	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"))) {
+	
+	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] != '?') {
+		memset( s + buflen, 0, BUFSIZE - buflen );
+		if( !( t = strstr( s, "\n%\n" ) ) || s[0] != '?' )
+		{
 			/* FIXME: Clean up */
-			help_free(help);
-			g_free(s);
-			return NULL;
+//			help_close( *help );
+			*help = NULL;
+			g_free( s );
+			return( NULL );
 		}
-		i = strchr(s, '\n') - s;
-
-		if (h->title) {
-			h = h->next = g_new0(help_t, 1);
+		i = strchr( s, '\n' ) - s;
+		
+		if( h->string )
+		{
+			h = h->next = g_new0( help_t, 1 );
 		}
-		h->title = g_new(char, i);
-
-		strncpy(h->title, s + 1, i - 1);
-		h->title[i - 1] = 0;
+		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->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);
+		
+		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);
+	
+	g_free( s );
+	
+	return( *help );
 }
 
-void help_free(help_t **help)
-{
-	help_t *h, *oh;
-	int last_fd = -1; /* Weak de-dupe */
-
-	if (help == NULL || *help == NULL) {
-		return;
-	}
-
-	h = *help;
-	while (h) {
-		if (h->fd == -1) {
-			g_free(h->offset.mem_offset);
-		} else if (h->fd != last_fd) {
-			close(h->fd);
-			last_fd = h->fd;
-		}
-		g_free(h->title);
-		h = (oh = h)->next;
-		g_free(oh);
-	}
-
-	*help = NULL;
-}
-
-char *help_get(help_t **help, char *title)
+char *help_get( help_t **help, char *string )
 {
 	time_t mtime;
@@ -125,83 +109,40 @@
 	help_t *h;
 
-	for (h = *help; h; h = h->next) {
-		if (h->title != NULL && g_strcasecmp(h->title, title) == 0) {
-			break;
+	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 );
 	}
-	if (h && h->length > 0) {
-		char *s = g_new(char, h->length + 1);
-
-		s[h->length] = 0;
-		if (h->fd >= 0) {
-			if (fstat(h->fd, stat) != 0) {
-				g_free(s);
-				return NULL;
-			}
-			mtime = stat->st_mtime;
-
-			if (mtime > h->mtime) {
-				g_free(s);
-				return NULL;
-			}
-
-			if (lseek(h->fd, h->offset.file_offset, SEEK_SET) == -1 ||
-			    read(h->fd, s, h->length) != h->length) {
-				g_free(s);
-				return NULL;
-			}
-		} else {
-			strncpy(s, h->offset.mem_offset, h->length);
-		}
-		return s;
-	}
-
-	return NULL;
+	
+	return( NULL );
 }
-
-int help_add_mem(help_t **help, const char *title, const char *content)
-{
-	help_t *h, *l = NULL;
-
-	for (h = *help; h; h = h->next) {
-		if (g_strcasecmp(h->title, title) == 0) {
-			return 0;
-		}
-
-		l = h;
-	}
-
-	if (l) {
-		h = l->next = g_new0(struct help, 1);
-	} else {
-		*help = h = g_new0(struct help, 1);
-	}
-	h->fd = -1;
-	h->title = g_strdup(title);
-	h->length = strlen(content);
-	h->offset.mem_offset = g_strdup(content);
-
-	return 1;
-}
-
-char *help_get_whatsnew(help_t **help, int old)
-{
-	GString *ret = NULL;
-	help_t *h;
-	int v;
-
-	for (h = *help; h; h = h->next) {
-		if (h->title != NULL && strncmp(h->title, "whatsnew", 8) == 0 &&
-		    sscanf(h->title + 8, "%x", &v) == 1 && v > old) {
-			char *s = help_get(&h, h->title);
-			if (ret == NULL) {
-				ret = g_string_new(s);
-			} else {
-				g_string_append_printf(ret, "\n\n%s", s);
-			}
-			g_free(s);
-		}
-	}
-
-	return ret ? g_string_free(ret, FALSE) : NULL;
-}
Index: help.h
===================================================================
--- help.h	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ help.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -1,3 +1,3 @@
-/********************************************************************\
+  /********************************************************************\
   * BitlBee -- An IRC to other IM-networks gateway                     *
   *                                                                    *
@@ -20,6 +20,6 @@
   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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
 */
 
@@ -27,13 +27,15 @@
 #define _HELP_H
 
-typedef union {
+typedef union
+{
 	off_t file_offset;
 	char *mem_offset;
 } help_off_t;
 
-typedef struct help {
+typedef struct help
+{
 	int fd;
 	time_t mtime;
-	char *title;
+	char *string;
 	help_off_t offset;
 	int length;
@@ -41,9 +43,6 @@
 } help_t;
 
-G_GNUC_MALLOC help_t *help_init(help_t **help, const char *helpfile);
-void help_free(help_t **help);
-char *help_get(help_t **help, char *title);
-int help_add_mem(help_t **help, const char *title, const char *content_);
-char *help_get_whatsnew(help_t **help, int old);
+help_t *help_init( help_t **help );
+char *help_get( help_t **help, char *string );
 
 #endif
Index: ini.c
===================================================================
--- ini.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ ini.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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 );
+}
Index: ini.h
===================================================================
--- ini.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ ini.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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
Index: it/bitlbee.service.in
===================================================================
--- init/bitlbee.service.in	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,12 +1,0 @@
-[Unit]
-Description=BitlBee IRC/IM gateway
-
-[Service]
-ExecStart=@sbindir@bitlbee -F -n
-KillMode=process
-StateDirectory=bitlbee
-RuntimeDirectory=bitlbee
-User=bitlbee
-
-[Install]
-WantedBy=multi-user.target
Index: it/bitlbee.socket
===================================================================
--- init/bitlbee.socket	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,11 +1,0 @@
-[Unit]
-Conflicts=bitlbee.service
-
-[Socket]
-ListenStream=6667
-ListenStream=%t/bitlbee/socket
-BindToDevice=lo
-Accept=yes
-
-[Install]
-WantedBy=sockets.target
Index: it/bitlbee.sysusers
===================================================================
--- init/bitlbee.sysusers	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,2 +1,0 @@
-#Type Name       ID                  GECOS                    Home directory Shell
-u     bitlbee    -  "BitlBee user"                            /var/lib/bitlbee - 
Index: it/bitlbee@.service.in
===================================================================
--- init/bitlbee@.service.in	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,10 +1,0 @@
-[Unit]
-Description=BitlBee Per-Connection Server
-
-[Service]
-ExecStart=@sbindir@bitlbee -I
-StandardInput=socket
-StandardError=syslog
-User=bitlbee
-StateDirectory=bitlbee
-RuntimeDirectory=bitlbee
Index: c.c
===================================================================
--- ipc.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,965 +1,0 @@
-/********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2012 Wilmer van der Gaast and others                *
-  \********************************************************************/
-
-/* IPC - communication between BitlBee processes                        */
-
-/*
-  This program is free software; you can 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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#define BITLBEE_CORE
-#include "bitlbee.h"
-#include "ipc.h"
-#include "commands.h"
-#include <sys/uio.h>
-#include <sys/un.h>
-
-GSList *child_list = NULL;
-static int ipc_child_recv_fd = -1;
-
-static void ipc_master_takeover_fail(struct bitlbee_child *child, gboolean both);
-static gboolean ipc_send_fd(int fd, int send_fd);
-
-/* On Solaris and possibly other systems passing FDs between processes is
- * not possible (or at least not using the method used in this file.
- * Just disable that code, the functionality is not that important. */
-#if defined(NO_FD_PASSING) && !defined(CMSG_SPACE)
-#define CMSG_SPACE(len) 1
-#endif
-
-static void ipc_master_cmd_client(irc_t *data, char **cmd)
-{
-	/* Normally data points at an irc_t block, but for the IPC master
-	   this is different. We think this scary cast is better than
-	   creating a new command_t structure, just to make the compiler
-	   happy. */
-	struct bitlbee_child *child = (void *) data;
-
-	if (child && cmd[1]) {
-		child->host = g_strdup(cmd[1]);
-		child->nick = g_strdup(cmd[2]);
-		child->realname = g_strdup(cmd[3]);
-	}
-
-	/* CLIENT == On initial connects, HELLO is after /RESTARTs. */
-	if (g_strcasecmp(cmd[0], "CLIENT") == 0) {
-		ipc_to_children_str("OPERMSG :Client connecting (PID=%d): %s@%s (%s)\r\n",
-		                    (int) (child ? child->pid : -1), cmd[2], cmd[1], cmd[3]);
-	}
-}
-
-static void ipc_master_cmd_nick(irc_t *data, char **cmd)
-{
-	struct bitlbee_child *child = (void *) data;
-
-	if (child && cmd[1]) {
-		g_free(child->nick);
-		child->nick = g_strdup(cmd[1]);
-	}
-}
-
-static void ipc_master_cmd_die(irc_t *data, char **cmd)
-{
-	if (global.conf->runmode == RUNMODE_FORKDAEMON) {
-		ipc_to_children_str("DIE\r\n");
-	}
-
-	bitlbee_shutdown(NULL, -1, 0);
-}
-
-static void ipc_master_cmd_deaf(irc_t *data, char **cmd)
-{
-	if (global.conf->runmode == RUNMODE_DAEMON) {
-		b_event_remove(global.listen_watch_source_id);
-		close(global.listen_socket);
-
-		global.listen_socket = global.listen_watch_source_id = -1;
-
-		ipc_to_children_str("OPERMSG :Closed listening socket, waiting "
-		                    "for all users to disconnect.");
-	} else {
-		ipc_to_children_str("OPERMSG :The DEAF command only works in "
-		                    "normal daemon mode. Try DIE instead.");
-	}
-}
-
-void ipc_master_cmd_rehash(irc_t *data, char **cmd)
-{
-	runmode_t oldmode;
-
-	oldmode = global.conf->runmode;
-
-	g_free(global.conf);
-	global.conf = conf_load(0, NULL);
-
-	if (global.conf->runmode != oldmode) {
-		log_message(LOGLVL_WARNING, "Can't change RunMode setting at runtime, restoring original setting");
-		global.conf->runmode = oldmode;
-	}
-
-	if (global.conf->runmode == RUNMODE_FORKDAEMON) {
-		ipc_to_children(cmd);
-	}
-}
-
-void ipc_master_cmd_restart(irc_t *data, char **cmd)
-{
-	if (global.conf->runmode != RUNMODE_FORKDAEMON) {
-		/* Tell child that this is unsupported. */
-		return;
-	}
-
-	global.restart = -1;
-	bitlbee_shutdown(NULL, -1, 0);
-}
-
-void ipc_master_cmd_identify(irc_t *data, char **cmd)
-{
-	struct bitlbee_child *child = (void *) data, *old = NULL;
-	char *resp;
-	GSList *l;
-
-	if (!child || !child->nick || strcmp(child->nick, cmd[1]) != 0) {
-		return;
-	}
-
-	g_free(child->password);
-	child->password = g_strdup(cmd[2]);
-
-	for (l = child_list; l; l = l->next) {
-		old = l->data;
-		if (child != old &&
-		    old->nick && nick_cmp(NULL, old->nick, child->nick) == 0 &&
-		    old->password && strcmp(old->password, child->password) == 0) {
-			break;
-		}
-	}
-
-	if (l && !child->to_child && !old->to_child) {
-		resp = "TAKEOVER INIT\r\n";
-		child->to_child = old;
-		old->to_child = child;
-	} else {
-		/* Won't need the fd since we can't send it anywhere. */
-		closesocket(child->to_fd);
-		child->to_fd = -1;
-		resp = "TAKEOVER NO\r\n";
-	}
-
-	if (write(child->ipc_fd, resp, strlen(resp)) != strlen(resp)) {
-		ipc_master_free_one(child);
-	}
-}
-
-
-void ipc_master_cmd_takeover(irc_t *data, char **cmd)
-{
-	struct bitlbee_child *child = (void *) data;
-	char *fwd = NULL;
-
-	/* Normal daemon mode doesn't keep these and has simplified code for
-	   takeovers. */
-	if (child == NULL) {
-		return;
-	}
-
-	if (child->to_child == NULL ||
-	    g_slist_find(child_list, child->to_child) == NULL) {
-		return ipc_master_takeover_fail(child, FALSE);
-	}
-
-	if (strcmp(cmd[1], "AUTH") == 0) {
-		/* New connection -> Master */
-		if (child->to_child &&
-		    child->nick && child->to_child->nick && cmd[2] &&
-		    child->password && child->to_child->password && cmd[3] &&
-		    strcmp(child->nick, child->to_child->nick) == 0 &&
-		    strcmp(child->nick, cmd[2]) == 0 &&
-		    strcmp(child->password, child->to_child->password) == 0 &&
-		    strcmp(child->password, cmd[3]) == 0) {
-			ipc_send_fd(child->to_child->ipc_fd, child->to_fd);
-
-			fwd = irc_build_line(cmd);
-			if (write(child->to_child->ipc_fd, fwd, strlen(fwd)) != strlen(fwd)) {
-				ipc_master_free_one(child);
-			}
-			g_free(fwd);
-		} else {
-			return ipc_master_takeover_fail(child, TRUE);
-		}
-	} else if (strcmp(cmd[1], "DONE") == 0 || strcmp(cmd[1], "FAIL") == 0) {
-		/* Old connection -> Master */
-		int fd;
-
-		/* The copy was successful (or not), we don't need it anymore. */
-		closesocket(child->to_fd);
-		child->to_fd = -1;
-
-		/* Pass it through to the other party, and flush all state. */
-		fwd = irc_build_line(cmd);
-		fd = child->to_child->ipc_fd;
-		child->to_child->to_child = NULL;
-		child->to_child = NULL;
-		if (write(fd, fwd, strlen(fwd)) != strlen(fwd)) {
-			ipc_master_free_one(child);
-		}
-		g_free(fwd);
-	}
-}
-
-static const command_t ipc_master_commands[] = {
-	{ "client",     3, ipc_master_cmd_client,     0 },
-	{ "hello",      0, ipc_master_cmd_client,     0 },
-	{ "nick",       1, ipc_master_cmd_nick,       0 },
-	{ "die",        0, ipc_master_cmd_die,        0 },
-	{ "deaf",       0, ipc_master_cmd_deaf,       0 },
-	{ "wallops",    1, NULL,                      IPC_CMD_TO_CHILDREN },
-	{ "wall",       1, NULL,                      IPC_CMD_TO_CHILDREN },
-	{ "opermsg",    1, NULL,                      IPC_CMD_TO_CHILDREN },
-	{ "rehash",     0, ipc_master_cmd_rehash,     0 },
-	{ "kill",       2, NULL,                      IPC_CMD_TO_CHILDREN },
-	{ "restart",    0, ipc_master_cmd_restart,    0 },
-	{ "identify",   2, ipc_master_cmd_identify,   0 },
-	{ "takeover",   1, ipc_master_cmd_takeover,   0 },
-	{ NULL }
-};
-
-
-static void ipc_child_cmd_die(irc_t *irc, char **cmd)
-{
-	irc_abort(irc, 0, "Shutdown requested by operator");
-}
-
-static void ipc_child_cmd_wallops(irc_t *irc, char **cmd)
-{
-	if (!(irc->status & USTATUS_LOGGED_IN)) {
-		return;
-	}
-
-	if (strchr(irc->umode, 'w')) {
-		irc_write(irc, ":%s WALLOPS :%s", irc->root->host, cmd[1]);
-	}
-}
-
-static void ipc_child_cmd_wall(irc_t *irc, char **cmd)
-{
-	if (!(irc->status & USTATUS_LOGGED_IN)) {
-		return;
-	}
-
-	if (strchr(irc->umode, 's')) {
-		irc_write(irc, ":%s NOTICE %s :%s", irc->root->host, irc->user->nick, cmd[1]);
-	}
-}
-
-static void ipc_child_cmd_opermsg(irc_t *irc, char **cmd)
-{
-	if (!(irc->status & USTATUS_LOGGED_IN)) {
-		return;
-	}
-
-	if (strchr(irc->umode, 'o')) {
-		irc_write(irc, ":%s NOTICE %s :*** OperMsg *** %s", irc->root->host, irc->user->nick, cmd[1]);
-	}
-}
-
-static void ipc_child_cmd_rehash(irc_t *irc, char **cmd)
-{
-	runmode_t oldmode;
-
-	oldmode = global.conf->runmode;
-
-	g_free(global.conf);
-	global.conf = conf_load(0, NULL);
-
-	global.conf->runmode = oldmode;
-}
-
-static void ipc_child_cmd_kill(irc_t *irc, char **cmd)
-{
-	if (!(irc->status & USTATUS_LOGGED_IN)) {
-		return;
-	}
-
-	if (nick_cmp(NULL, cmd[1], irc->user->nick) != 0) {
-		return;         /* It's not for us. */
-
-	}
-	irc_write(irc, ":%s!%s@%s KILL %s :%s", irc->root->nick, irc->root->nick, irc->root->host, irc->user->nick,
-	          cmd[2]);
-	irc_abort(irc, 0, "Killed by operator: %s", cmd[2]);
-}
-
-static void ipc_child_cmd_hello(irc_t *irc, char **cmd)
-{
-	if (!(irc->status & USTATUS_LOGGED_IN)) {
-		ipc_to_master_str("HELLO\r\n");
-	} else {
-		ipc_to_master_str("HELLO %s %s :%s\r\n", irc->user->host, irc->user->nick, irc->user->fullname);
-	}
-}
-
-static void ipc_child_cmd_takeover_yes(void *data);
-static void ipc_child_cmd_takeover_no(void *data);
-
-static void ipc_child_cmd_takeover(irc_t *irc, char **cmd)
-{
-	if (strcmp(cmd[1], "NO") == 0) {
-		/* Master->New connection */
-		/* No takeover, finish the login. */
-	} else if (strcmp(cmd[1], "INIT") == 0) {
-		/* Master->New connection */
-		if (!set_getbool(&irc->b->set, "allow_takeover")) {
-			ipc_child_cmd_takeover_no(irc);
-			return;
-		}
-
-		/* Offer to take over the old session, unless for some reason
-		   we're already logging into IM connections. */
-		if (irc->login_source_id != -1) {
-			query_add(irc, NULL,
-			          "You're already connected to this server. "
-			          "Would you like to take over this session?",
-			          ipc_child_cmd_takeover_yes,
-			          ipc_child_cmd_takeover_no, NULL, irc);
-		}
-
-		/* This one's going to connect to accounts, avoid that. */
-		b_event_remove(irc->login_source_id);
-		irc->login_source_id = -1;
-	} else if (strcmp(cmd[1], "AUTH") == 0) {
-		/* Master->Old connection */
-		if (irc->password && cmd[2] && cmd[3] &&
-		    ipc_child_recv_fd != -1 &&
-		    strcmp(irc->user->nick, cmd[2]) == 0 &&
-		    strcmp(irc->password, cmd[3]) == 0 &&
-		    set_getbool(&irc->b->set, "allow_takeover")) {
-			irc_switch_fd(irc, ipc_child_recv_fd);
-			irc_sync(irc);
-			irc_rootmsg(irc, "You've successfully taken over your old session");
-			ipc_child_recv_fd = -1;
-
-			ipc_to_master_str("TAKEOVER DONE\r\n");
-		} else {
-			ipc_to_master_str("TAKEOVER FAIL\r\n");
-		}
-	} else if (strcmp(cmd[1], "DONE") == 0) {
-		/* Master->New connection (now taken over by old process) */
-		irc_free(irc);
-	} else if (strcmp(cmd[1], "FAIL") == 0) {
-		/* Master->New connection */
-		irc_rootmsg(irc, "Could not take over old session");
-	}
-}
-
-static void ipc_child_cmd_takeover_yes(void *data)
-{
-	irc_t *irc = data, *old = NULL;
-	char *to_auth[] = { "TAKEOVER", "AUTH", irc->user->nick, irc->password, NULL };
-
-	/* Master->New connection */
-	ipc_to_master_str("TAKEOVER AUTH %s :%s\r\n",
-	                  irc->user->nick, irc->password);
-
-	if (global.conf->runmode == RUNMODE_DAEMON) {
-		GSList *l;
-
-		for (l = irc_connection_list; l; l = l->next) {
-			old = l->data;
-
-			if (irc != old &&
-			    irc->user->nick && old->user->nick &&
-			    irc->password && old->password &&
-			    strcmp(irc->user->nick, old->user->nick) == 0 &&
-			    strcmp(irc->password, old->password) == 0) {
-				break;
-			}
-		}
-		if (l == NULL) {
-			to_auth[1] = "FAIL";
-			ipc_child_cmd_takeover(irc, to_auth);
-			return;
-		}
-	}
-
-	/* Drop credentials, we'll shut down soon and shouldn't overwrite
-	   any settings. */
-	irc_rootmsg(irc, "Trying to take over existing session");
-
-	irc_desync(irc);
-
-	if (old) {
-		ipc_child_recv_fd = dup(irc->fd);
-		ipc_child_cmd_takeover(old, to_auth);
-	}
-
-	/* TODO: irc_setpass() should do all of this. */
-	irc_setpass(irc, NULL);
-	irc->status &= ~USTATUS_IDENTIFIED;
-	irc_umode_set(irc, "-R", 1);
-
-	if (old) {
-		irc_abort(irc, FALSE, NULL);
-	}
-}
-
-static void ipc_child_cmd_takeover_no(void *data)
-{
-	ipc_to_master_str("TAKEOVER NO\r\n");
-	cmd_identify_finish(data, 0, 0);
-}
-
-static const command_t ipc_child_commands[] = {
-	{ "die",        0, ipc_child_cmd_die,         0 },
-	{ "wallops",    1, ipc_child_cmd_wallops,     0 },
-	{ "wall",       1, ipc_child_cmd_wall,        0 },
-	{ "opermsg",    1, ipc_child_cmd_opermsg,     0 },
-	{ "rehash",     0, ipc_child_cmd_rehash,      0 },
-	{ "kill",       2, ipc_child_cmd_kill,        0 },
-	{ "hello",      0, ipc_child_cmd_hello,       0 },
-	{ "takeover",   1, ipc_child_cmd_takeover,    0 },
-	{ NULL }
-};
-
-gboolean ipc_child_identify(irc_t *irc)
-{
-	if (global.conf->runmode == RUNMODE_FORKDAEMON) {
-#ifndef NO_FD_PASSING
-		if (!ipc_send_fd(global.listen_socket, irc->fd)) {
-			ipc_child_disable();
-		}
-
-		ipc_to_master_str("IDENTIFY %s :%s\r\n", irc->user->nick, irc->password);
-#endif
-
-		return TRUE;
-	} else if (global.conf->runmode == RUNMODE_DAEMON) {
-		GSList *l;
-		irc_t *old = NULL;
-		char *to_init[] = { "TAKEOVER", "INIT", NULL };
-
-		for (l = irc_connection_list; l; l = l->next) {
-			old = l->data;
-
-			if (irc != old &&
-			    irc->user->nick && old->user->nick &&
-			    irc->password && old->password &&
-			    strcmp(irc->user->nick, old->user->nick) == 0 &&
-			    strcmp(irc->password, old->password) == 0) {
-				break;
-			}
-		}
-		if (l == NULL || old == NULL ||
-		    !set_getbool(&irc->b->set, "allow_takeover") ||
-		    !set_getbool(&old->b->set, "allow_takeover")) {
-			return FALSE;
-		}
-
-		ipc_child_cmd_takeover(irc, to_init);
-
-		return TRUE;
-	} else {
-		return FALSE;
-	}
-}
-
-static void ipc_master_takeover_fail(struct bitlbee_child *child, gboolean both)
-{
-	if (child == NULL || g_slist_find(child_list, child) == NULL) {
-		return;
-	}
-
-	if (both && child->to_child != NULL) {
-		ipc_master_takeover_fail(child->to_child, FALSE);
-	}
-
-	if (child->to_fd > -1) {
-		/* Send this error only to the new connection, which can be
-		   recognised by to_fd being set. */
-		if (write(child->ipc_fd, "TAKEOVER FAIL\r\n", 15) != 15) {
-			ipc_master_free_one(child);
-			return;
-		}
-		close(child->to_fd);
-		child->to_fd = -1;
-	}
-	child->to_child = NULL;
-}
-
-static void ipc_command_exec(void *data, char **cmd, const command_t *commands)
-{
-	int i, j;
-
-	if (!cmd[0]) {
-		return;
-	}
-
-	for (i = 0; commands[i].command; i++) {
-		if (g_strcasecmp(commands[i].command, cmd[0]) == 0) {
-			/* There is no typo in this line: */
-			for (j = 1; cmd[j]; j++) {
-				;
-			}
-			j--;
-
-			if (j < commands[i].required_parameters) {
-				break;
-			}
-
-			if (commands[i].flags & IPC_CMD_TO_CHILDREN) {
-				ipc_to_children(cmd);
-			} else {
-				commands[i].execute(data, cmd);
-			}
-
-			break;
-		}
-	}
-}
-
-/* Return just one line. Returns NULL if something broke, an empty string
-   on temporary "errors" (EAGAIN and friends). */
-static char *ipc_readline(int fd, int *recv_fd)
-{
-	struct msghdr msg;
-	struct iovec iov;
-	char ccmsg[CMSG_SPACE(sizeof(recv_fd))];
-	struct cmsghdr *cmsg;
-	char buf[513], *eol;
-	int size;
-
-	/* Because this is internal communication, it should be pretty safe
-	   to just peek at the message, find its length (by searching for the
-	   end-of-line) and then just read that message. With internal
-	   sockets and limites message length, messages should always be
-	   complete. Saves us quite a lot of code and buffering. */
-	size = recv(fd, buf, sizeof(buf) - 1, MSG_PEEK);
-	if (size == 0 || (size < 0 && !sockerr_again())) {
-		return NULL;
-	} else if (size < 0) { /* && sockerr_again() */
-		return(g_strdup(""));
-	} else {
-		buf[size] = 0;
-	}
-
-	if ((eol = strstr(buf, "\r\n")) == NULL) {
-		return NULL;
-	} else {
-		size = eol - buf + 2;
-	}
-
-	iov.iov_base = buf;
-	iov.iov_len = size;
-
-	memset(&msg, 0, sizeof(msg));
-	msg.msg_iov = &iov;
-	msg.msg_iovlen = 1;
-#ifndef NO_FD_PASSING
-	msg.msg_control = ccmsg;
-	msg.msg_controllen = sizeof(ccmsg);
-#endif
-
-	if (recvmsg(fd, &msg, 0) != size) {
-		return NULL;
-	}
-
-#ifndef NO_FD_PASSING
-	if (recv_fd) {
-		for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
-			if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
-				/* Getting more than one shouldn't happen but if it does,
-				   make sure we don't leave them around. */
-				if (*recv_fd != -1) {
-					close(*recv_fd);
-				}
-
-				memcpy(recv_fd, CMSG_DATA(cmsg), sizeof(int));
-				/*
-				fprintf( stderr, "pid %d received fd %d\n", (int) getpid(), *recv_fd );
-				*/
-			}
-		}
-	}
-#endif
-
-	/*
-	fprintf( stderr, "pid %d received: %s", (int) getpid(), buf );
-	*/
-	return g_strndup(buf, size - 2);
-}
-
-gboolean ipc_master_read(gpointer data, gint source, b_input_condition cond)
-{
-	struct bitlbee_child *child = data;
-	char *buf, **cmd;
-
-	if ((buf = ipc_readline(source, &child->to_fd))) {
-		cmd = irc_parse_line(buf);
-		if (cmd) {
-			ipc_command_exec(child, cmd, ipc_master_commands);
-			g_free(cmd);
-		}
-		g_free(buf);
-	} else {
-		ipc_master_free_fd(source);
-	}
-
-	return TRUE;
-}
-
-gboolean ipc_child_read(gpointer data, gint source, b_input_condition cond)
-{
-	char *buf, **cmd;
-
-	if ((buf = ipc_readline(source, &ipc_child_recv_fd))) {
-		cmd = irc_parse_line(buf);
-		if (cmd) {
-			ipc_command_exec(data, cmd, ipc_child_commands);
-			g_free(cmd);
-		}
-		g_free(buf);
-	} else {
-		ipc_child_disable();
-	}
-
-	return TRUE;
-}
-
-void ipc_to_master(char **cmd)
-{
-	if (global.conf->runmode == RUNMODE_FORKDAEMON) {
-		char *s = irc_build_line(cmd);
-		ipc_to_master_str("%s", s);
-		g_free(s);
-	} else if (global.conf->runmode == RUNMODE_DAEMON) {
-		ipc_command_exec(NULL, cmd, ipc_master_commands);
-	}
-}
-
-void ipc_to_master_str(char *format, ...)
-{
-	char *msg_buf;
-	va_list params;
-
-	va_start(params, format);
-	msg_buf = g_strdup_vprintf(format, params);
-	va_end(params);
-
-	if (strlen(msg_buf) > 512) {
-		/* Don't send it, it's too long... */
-	} else if (global.conf->runmode == RUNMODE_FORKDAEMON) {
-		if (global.listen_socket >= 0) {
-			if (write(global.listen_socket, msg_buf, strlen(msg_buf)) <= 0) {
-				ipc_child_disable();
-			}
-		}
-	} else if (global.conf->runmode == RUNMODE_DAEMON) {
-		char **cmd, *s;
-
-		if ((s = strchr(msg_buf, '\r'))) {
-			*s = 0;
-		}
-
-		cmd = irc_parse_line(msg_buf);
-		ipc_command_exec(NULL, cmd, ipc_master_commands);
-		g_free(cmd);
-	}
-
-	g_free(msg_buf);
-}
-
-void ipc_to_children(char **cmd)
-{
-	if (global.conf->runmode == RUNMODE_FORKDAEMON) {
-		char *msg_buf = irc_build_line(cmd);
-		ipc_to_children_str("%s", msg_buf);
-		g_free(msg_buf);
-	} else if (global.conf->runmode == RUNMODE_DAEMON) {
-		GSList *l;
-
-		for (l = irc_connection_list; l; l = l->next) {
-			ipc_command_exec(l->data, cmd, ipc_child_commands);
-		}
-	}
-}
-
-void ipc_to_children_str(char *format, ...)
-{
-	char *msg_buf;
-	va_list params;
-
-	va_start(params, format);
-	msg_buf = g_strdup_vprintf(format, params);
-	va_end(params);
-
-	if (strlen(msg_buf) > 512) {
-		/* Don't send it, it's too long... */
-	} else if (global.conf->runmode == RUNMODE_FORKDAEMON) {
-		int msg_len = strlen(msg_buf);
-		GSList *l, *next;
-
-		for (l = child_list; l; l = next) {
-			struct bitlbee_child *c = l->data;
-
-			next = l->next;
-			if (write(c->ipc_fd, msg_buf, msg_len) <= 0) {
-				ipc_master_free_one(c);
-			}
-		}
-	} else if (global.conf->runmode == RUNMODE_DAEMON) {
-		char **cmd, *s;
-
-		if ((s = strchr(msg_buf, '\r'))) {
-			*s = 0;
-		}
-
-		cmd = irc_parse_line(msg_buf);
-		ipc_to_children(cmd);
-		g_free(cmd);
-	}
-
-	g_free(msg_buf);
-}
-
-static gboolean ipc_send_fd(int fd, int send_fd)
-{
-	struct msghdr msg;
-	struct iovec iov;
-	char ccmsg[CMSG_SPACE(sizeof(fd))];
-	struct cmsghdr *cmsg;
-
-	memset(&msg, 0, sizeof(msg));
-	iov.iov_base = "0x90\r\n";         /* Ja, noppes */
-	iov.iov_len = 6;
-	msg.msg_iov = &iov;
-	msg.msg_iovlen = 1;
-
-#ifndef NO_FD_PASSING
-	msg.msg_control = ccmsg;
-	msg.msg_controllen = sizeof(ccmsg);
-	cmsg = CMSG_FIRSTHDR(&msg);
-	cmsg->cmsg_level = SOL_SOCKET;
-	cmsg->cmsg_type = SCM_RIGHTS;
-	cmsg->cmsg_len = CMSG_LEN(sizeof(send_fd));
-	memcpy(CMSG_DATA(cmsg), &send_fd, sizeof(int));
-	msg.msg_controllen = cmsg->cmsg_len;
-#endif
-
-	return sendmsg(fd, &msg, 0) == 6;
-}
-
-void ipc_master_free_one(struct bitlbee_child *c)
-{
-	GSList *l;
-
-	b_event_remove(c->ipc_inpa);
-	closesocket(c->ipc_fd);
-
-	if (c->to_fd != -1) {
-		close(c->to_fd);
-	}
-
-	child_list = g_slist_remove(child_list, c);
-
-	g_free(c->host);
-	g_free(c->nick);
-	g_free(c->realname);
-	g_free(c->password);
-	g_free(c);
-
-	/* Also, if any child has a reference to this one, remove it. */
-	for (l = child_list; l; l = l->next) {
-		struct bitlbee_child *oc = l->data;
-
-		if (oc->to_child == c) {
-			ipc_master_takeover_fail(oc, FALSE);
-		}
-	}
-}
-
-void ipc_master_free_fd(int fd)
-{
-	GSList *l;
-	struct bitlbee_child *c;
-
-	for (l = child_list; l; l = l->next) {
-		c = l->data;
-		if (c->ipc_fd == fd) {
-			ipc_master_free_one(c);
-			break;
-		}
-	}
-}
-
-void ipc_master_free_all()
-{
-	while (child_list) {
-		ipc_master_free_one(child_list->data);
-	}
-}
-
-void ipc_child_disable()
-{
-	b_event_remove(global.listen_watch_source_id);
-	close(global.listen_socket);
-
-	global.listen_socket = -1;
-}
-
-char *ipc_master_save_state()
-{
-	char *fn = g_strdup("/tmp/bee-restart.XXXXXX");
-	int fd = mkstemp(fn);
-	GSList *l;
-	FILE *fp;
-	int i;
-
-	if (fd == -1) {
-		log_message(LOGLVL_ERROR, "Could not create temporary file: %s", strerror(errno));
-		g_free(fn);
-		return NULL;
-	}
-
-	/* This is more convenient now. */
-	fp = fdopen(fd, "w");
-
-	for (l = child_list, i = 0; l; l = l->next) {
-		i++;
-	}
-
-	/* Number of client processes. */
-	fprintf(fp, "%d\n", i);
-
-	for (l = child_list; l; l = l->next) {
-		fprintf(fp, "%d %d\n", (int) ((struct bitlbee_child*) l->data)->pid,
-		        ((struct bitlbee_child*) l->data)->ipc_fd);
-	}
-
-	if (fclose(fp) == 0) {
-		return fn;
-	} else {
-		unlink(fn);
-		g_free(fn);
-		return NULL;
-	}
-}
-
-
-static gboolean new_ipc_client(gpointer data, gint serversock, b_input_condition cond)
-{
-	struct bitlbee_child *child = g_new0(struct bitlbee_child, 1);
-
-	child->to_fd = -1;
-	child->ipc_fd = accept(serversock, NULL, 0);
-	if (child->ipc_fd == -1) {
-		log_message(LOGLVL_WARNING, "Unable to accept connection on UNIX domain socket: %s", strerror(errno));
-		return TRUE;
-	}
-
-	child->ipc_inpa = b_input_add(child->ipc_fd, B_EV_IO_READ, ipc_master_read, child);
-
-	child_list = g_slist_prepend(child_list, child);
-
-	return TRUE;
-}
-
-int ipc_master_listen_socket()
-{
-	struct sockaddr_un un_addr;
-	int serversock;
-
-	if (!IPCSOCKET || !*IPCSOCKET) {
-		return 1;
-	}
-
-	/* Clean up old socket files that were hanging around.. */
-	if (unlink(IPCSOCKET) == -1 && errno != ENOENT) {
-		log_message(LOGLVL_ERROR, "Could not remove old IPC socket at %s: %s", IPCSOCKET, strerror(errno));
-		return 0;
-	}
-
-	un_addr.sun_family = AF_UNIX;
-	strcpy(un_addr.sun_path, IPCSOCKET);
-
-	serversock = socket(AF_UNIX, SOCK_STREAM, 0);
-
-	if (serversock == -1) {
-		log_message(LOGLVL_WARNING, "Unable to create UNIX socket: %s", strerror(errno));
-		return 0;
-	}
-
-	if (bind(serversock, (struct sockaddr *) &un_addr, sizeof(un_addr)) == -1) {
-		log_message(LOGLVL_WARNING, "Unable to bind UNIX socket to %s: %s", IPCSOCKET, strerror(errno));
-		return 0;
-	}
-
-	if (listen(serversock, 5) == -1) {
-		log_message(LOGLVL_WARNING, "Unable to listen on UNIX socket: %s", strerror(errno));
-		return 0;
-	}
-
-	b_input_add(serversock, B_EV_IO_READ, new_ipc_client, NULL);
-
-	return 1;
-}
-
-int ipc_master_load_state(char *statefile)
-{
-	struct bitlbee_child *child;
-	FILE *fp;
-	int i, n;
-
-	if (statefile == NULL) {
-		return 0;
-	}
-
-	fp = fopen(statefile, "r");
-	unlink(statefile);      /* Why do it later? :-) */
-	if (fp == NULL) {
-		return 0;
-	}
-
-	if (fscanf(fp, "%d", &n) != 1) {
-		log_message(LOGLVL_WARNING, "Could not import state information for child processes.");
-		fclose(fp);
-		return 0;
-	}
-
-	log_message(LOGLVL_INFO, "Importing information for %d child processes.", n);
-	for (i = 0; i < n; i++) {
-		child = g_new0(struct bitlbee_child, 1);
-
-		if (fscanf(fp, "%d %d", (int *) &child->pid, &child->ipc_fd) != 2) {
-			log_message(LOGLVL_WARNING, "Unexpected end of file: Only processed %d clients.", i);
-			g_free(child);
-			fclose(fp);
-			return 0;
-		}
-		child->ipc_inpa = b_input_add(child->ipc_fd, B_EV_IO_READ, ipc_master_read, child);
-		child->to_fd = -1;
-
-		child_list = g_slist_prepend(child_list, child);
-	}
-
-	ipc_to_children_str("HELLO\r\n");
-	ipc_to_children_str("OPERMSG :New %s master process started (version %s)\r\n", PACKAGE, BITLBEE_VERSION);
-
-	fclose(fp);
-	return 1;
-}
Index: c.h
===================================================================
--- ipc.h	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,70 +1,0 @@
-/********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2010 Wilmer van der Gaast and others                *
-  \********************************************************************/
-
-/* IPC - communication between BitlBee processes                        */
-
-/*
-  This program is free software; you can 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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#define BITLBEE_CORE
-#include "bitlbee.h"
-
-
-struct bitlbee_child {
-	pid_t pid;
-	int ipc_fd;
-	gint ipc_inpa;
-
-	char *host;
-	char *nick;
-	char *realname;
-
-	char *password;
-
-	/* For takeovers: */
-	struct bitlbee_child *to_child;
-	int to_fd;
-};
-
-
-gboolean ipc_master_read(gpointer data, gint source, b_input_condition cond);
-gboolean ipc_child_read(gpointer data, gint source, b_input_condition cond);
-
-void ipc_master_free_one(struct bitlbee_child *child);
-void ipc_master_free_fd(int fd);
-void ipc_master_free_all();
-
-void ipc_child_disable();
-
-gboolean ipc_child_identify(irc_t *irc);
-
-void ipc_to_master(char **cmd);
-void ipc_to_master_str(char *format, ...) G_GNUC_PRINTF(1, 2);
-void ipc_to_children(char **cmd);
-void ipc_to_children_str(char *format, ...) G_GNUC_PRINTF(1, 2);
-
-/* We need this function in inetd mode, so let's just make it non-static. */
-void ipc_master_cmd_rehash(irc_t *data, char **cmd);
-
-char *ipc_master_save_state();
-int ipc_master_load_state(char *statefile);
-int ipc_master_listen_socket();
-
-extern GSList *child_list;
Index: irc.c
===================================================================
--- irc.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ irc.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -1,9 +1,9 @@
-/********************************************************************\
+  /********************************************************************\
   * BitlBee -- An IRC to other IM-networks gateway                     *
   *                                                                    *
-  * Copyright 2002-2012 Wilmer van der Gaast and others                *
+  * Copyright 2002-2004 Wilmer van der Gaast and others                *
   \********************************************************************/
 
-/* The IRC-based UI (for now the only one)                              */
+/* The big hairy IRCd part of the project                               */
 
 /*
@@ -20,505 +20,349 @@
   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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
+  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 "canohost.h"
-#include "ipc.h"
-#include "dcc.h"
-#include "lib/ssl_client.h"
-
-GSList *irc_connection_list;
-GSList *irc_plugins;
-
-static gboolean irc_userping(gpointer _irc, gint fd, b_input_condition cond);
-static char *set_eval_charset(set_t *set, char *value);
-static char *set_eval_password(set_t *set, char *value);
-static char *set_eval_bw_compat(set_t *set, char *value);
-static char *set_eval_utf8_nicks(set_t *set, char *value);
-
-irc_t *irc_new(int fd)
-{
-	irc_t *irc;
-	irc_user_t *iu;
-	GSList *l;
-	set_t *s;
-	bee_t *b;
-
-	irc = g_new0(irc_t, 1);
-
+#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;
-	sock_make_nonblocking(irc->fd);
-
-	irc->r_watch_source_id = b_input_add(irc->fd, B_EV_IO_READ, bitlbee_io_current_client_read, irc);
-
+	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->nick_user_hash = g_hash_table_new(g_str_hash, g_str_equal);
-	irc->watches = g_hash_table_new(g_str_hash, g_str_equal);
-
-	irc->iconv = (GIConv) - 1;
-	irc->oconv = (GIConv) - 1;
-
-	irc->sendbuffer = g_string_sized_new(IRC_MAX_LINE * 2);
-
-	if (global.conf->ping_interval > 0 && global.conf->ping_timeout > 0) {
-		irc->ping_source_id = b_timeout_add(global.conf->ping_interval * 1000, irc_userping, irc);
-	}
-
-	irc_connection_list = g_slist_append(irc_connection_list, irc);
-
-	b = irc->b = bee_new();
-	b->ui_data = irc;
-	b->ui = &irc_ui_funcs;
-
-	s = set_add(&b->set, "allow_takeover", "true", set_eval_bool, irc);
-	s = set_add(&b->set, "away_devoice", "true", set_eval_bw_compat, irc);
-	s->flags |= SET_HIDDEN;
-	s = set_add(&b->set, "away_reply_timeout", "3600", set_eval_int, irc);
-	s = set_add(&b->set, "charset", "utf-8", set_eval_charset, irc);
-	s = set_add(&b->set, "default_target", "root", NULL, irc);
-	s = set_add(&b->set, "display_namechanges", "false", set_eval_bool, irc);
-	s = set_add(&b->set, "display_timestamps", "true", set_eval_bool, irc);
-	s = set_add(&b->set, "handle_unknown", "add_channel", NULL, irc);
-	s = set_add(&b->set, "last_version", "0", NULL, irc);
-	s->flags |= SET_HIDDEN;
-	s = set_add(&b->set, "nick_format", "%-@nick", NULL, irc);
-	s = set_add(&b->set, "nick_lowercase", "false", set_eval_bool, irc);
-	s = set_add(&b->set, "nick_underscores", "false", set_eval_bool, irc);
-	s = set_add(&b->set, "offline_user_quits", "true", set_eval_bool, irc);
-	s = set_add(&b->set, "ops", "both", set_eval_irc_channel_ops, irc);
-	s = set_add(&b->set, "paste_buffer", "false", set_eval_bool, irc);
-	s->old_key = g_strdup("buddy_sendbuffer");
-	s = set_add(&b->set, "paste_buffer_delay", "200", set_eval_int, irc);
-	s->old_key = g_strdup("buddy_sendbuffer_delay");
-	s = set_add(&b->set, "password", NULL, set_eval_password, irc);
-	s->flags |= SET_NULL_OK | SET_PASSWORD;
-	s = set_add(&b->set, "private", "true", set_eval_bool, irc);
-	s = set_add(&b->set, "query_order", "lifo", NULL, irc);
-	s = set_add(&b->set, "root_nick", ROOT_NICK, set_eval_root_nick, irc);
-	s->flags |= SET_HIDDEN;
-	s = set_add(&b->set, "show_offline", "false", set_eval_bw_compat, irc);
-	s->flags |= SET_HIDDEN;
-	s = set_add(&b->set, "self_messages", "true", set_eval_self_messages, irc);
-	s = set_add(&b->set, "simulate_netsplit", "true", set_eval_bool, irc);
-	s = set_add(&b->set, "timezone", "local", set_eval_timezone, irc);
-	s = set_add(&b->set, "to_char", ": ", set_eval_to_char, irc);
-	s = set_add(&b->set, "typing_notice", "false", set_eval_bool, irc);
-	s = set_add(&b->set, "utf8_nicks", "false", set_eval_utf8_nicks, irc);
-
-	irc->root = iu = irc_user_new(irc, ROOT_NICK);
-	iu->fullname = g_strdup(ROOT_FN);
-	iu->f = &irc_user_root_funcs;
-
-	iu = irc_user_new(irc, NS_NICK);
-	iu->fullname = g_strdup(ROOT_FN);
-	iu->f = &irc_user_root_funcs;
-
-	irc->user = g_new0(irc_user_t, 1);
-	
-	irc_set_hosts(irc, NULL, 0);
-
-	conf_loaddefaults(irc);
-
-	/* Evaluator sets the iconv/oconv structures. */
-	set_eval_charset(set_find(&b->set, "charset"), set_getstr(&b->set, "charset"));
-
-	irc_write(irc, ":%s NOTICE * :%s", irc->root->host, "BitlBee-IRCd initialized, please go on");
-	if (isatty(irc->fd)) {
-		irc_write(irc, ":%s NOTICE * :%s", irc->root->host,
-		          "If you read this, you most likely accidentally "
-		          "started BitlBee in inetd mode on the command line.");
-		irc_write(irc, ":%s NOTICE * :%s", irc->root->host,
-		          "You probably want to run it as a system service, "
-		          "or use (Fork)Daemon mode with the -F or -D switches. "
-		          "See doc/README or 'man bitlbee' for more information.");
-	}
-
-	/* libpurple doesn't like fork()s after initializing itself, so this
-	   is the right moment to initialize it. */
-#ifdef WITH_PURPLE
-	nogaim_init();
+	
+	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
-
-	/* SSL library initialization also should be done after the fork, to
-	   avoid shared CSPRNG state. This is required by NSS, which refuses to
-	   work if a fork is detected */
-	ssl_init();
-
-	for (l = irc_plugins; l; l = l->next) {
-		irc_plugin_t *p = l->data;
-		if (p->irc_new) {
-			p->irc_new(irc);
-		}
-	}
-
-	return irc;
-}
-
-void irc_set_hosts(irc_t *irc, const struct sockaddr *remote_addr, const socklen_t remote_addrlen)
-{
-	struct sockaddr_storage sock;
-	socklen_t socklen = sizeof(sock);
-	char *host = NULL, *myhost = NULL;
-	struct irc_user *iu;
-
-	if (global.conf->hostname) {
-		myhost = g_strdup(global.conf->hostname);
-	} else if (getsockname(irc->fd, (struct sockaddr*) &sock, &socklen) == 0) {
-		myhost = reverse_lookup((struct sockaddr*) &sock, socklen);
-	}
-
-	if (remote_addrlen > 0) {
-		host = reverse_lookup(remote_addr, remote_addrlen);
-	} else if (getpeername(irc->fd, (struct sockaddr*) &sock, &socklen) == 0) {
-		host = reverse_lookup((struct sockaddr*) &sock, socklen);
-	}
-
-	if (myhost == NULL) {
-		myhost = g_strdup("localhost.localdomain");
-	}
-	if (host == NULL) {
-		host = g_strdup("localhost.localdomain");
-	}
-	
-	if (irc->root->host != irc->root->nick) {
-		g_free(irc->root->host);
-	}
-	irc->root->host = g_strdup(myhost);
-	if ((iu = irc_user_by_name(irc, NS_NICK))) {
-		if (iu->host != iu->nick) {
-			g_free(iu->host);
-		}
-		iu->host = g_strdup(myhost);
-	}
-	
-	if (irc->user->host != irc->user->nick) {
-		g_free(irc->user->host);
-	}
-	irc->user->host = g_strdup(host);
-
-	g_free(myhost);
-	g_free(host);
-}
-
-/* immed=1 makes this function pretty much equal to irc_free(), except that
-   this one will "log". In case the connection is already broken and we
-   shouldn't try to write to it. */
-void irc_abort(irc_t *irc, int immed, char *format, ...)
-{
-	char *reason = NULL;
-
-	if (format != NULL) {
-		va_list params;
-
-		va_start(params, format);
-		reason = g_strdup_vprintf(format, params);
-		va_end(params);
-	}
-
-	if (reason) {
-		irc_write(irc, "ERROR :Closing link: %s", reason);
-	}
-
-	ipc_to_master_str("OPERMSG :Client exiting: %s@%s [%s]\r\n",
-	                  irc->user->nick ? irc->user->nick : "(NONE)",
-	                  irc->user->host, reason ? : "");
-
-	g_free(reason);
-
-	irc_flush(irc);
-	if (immed) {
-		irc_free(irc);
-	} else {
-		b_event_remove(irc->ping_source_id);
-		irc->ping_source_id = b_timeout_add(1, (b_event_handler) irc_free, irc);
-	}
-}
-
-static gboolean irc_free_hashkey(gpointer key, gpointer value, gpointer data);
-
+	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)
 {
-	GSList *l;
-	GHashTableIter iter;
-	gpointer itervalue;
-
-	irc->status |= USTATUS_SHUTDOWN;
-
-	log_message(LOGLVL_INFO, "Destroying connection with fd %d", irc->fd);
-
-	if (irc->status & USTATUS_IDENTIFIED && set_getbool(&irc->b->set, "save_on_quit")) {
-		if (storage_save(irc, NULL, TRUE) != STORAGE_OK) {
-			log_message(LOGLVL_WARNING, "Error while saving settings for user %s", irc->user->nick);
-		}
-	}
-
-	for (l = irc_plugins; l; l = l->next) {
-		irc_plugin_t *p = l->data;
-		if (p->irc_free) {
-			p->irc_free(irc);
-		}
-	}
-
-	irc_connection_list = g_slist_remove(irc_connection_list, irc);
-
-	while (irc->queries != NULL) {
+	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);
-	}
-
-	/* This is a little bit messy: bee_free() frees all b->users which
-	   calls us back to free the corresponding irc->users. So do this
-	   before we clear the remaining ones ourselves. */
-	bee_free(irc->b);
-
-	g_hash_table_iter_init(&iter, irc->nick_user_hash);
-
-	while (g_hash_table_iter_next(&iter, NULL, &itervalue)) {
-		g_hash_table_iter_remove(&iter);
-		irc_user_free(irc, (irc_user_t *) itervalue);
-	}
-
-	while (irc->channels) {
-		irc_channel_free(irc->channels->data);
-	}
-
-	if (irc->ping_source_id > 0) {
-		b_event_remove(irc->ping_source_id);
-	}
-	if (irc->r_watch_source_id > 0) {
-		b_event_remove(irc->r_watch_source_id);
-	}
-	if (irc->w_watch_source_id > 0) {
-		b_event_remove(irc->w_watch_source_id);
-	}
-
-	closesocket(irc->fd);
-	irc->fd = -1;
-
-	g_hash_table_destroy(irc->nick_user_hash);
-
-	g_hash_table_foreach_remove(irc->watches, irc_free_hashkey, NULL);
+	
+	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->iconv != (GIConv) - 1) {
-		g_iconv_close(irc->iconv);
-	}
-	if (irc->oconv != (GIConv) - 1) {
-		g_iconv_close(irc->oconv);
-	}
-
-	g_string_free(irc->sendbuffer, TRUE);
-	g_free(irc->readbuffer);
-	g_free(irc->password);
-
+	
+	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 ||
-	    global.conf->runmode == RUNMODE_FORKDAEMON ||
-	    (global.conf->runmode == RUNMODE_DAEMON &&
-	     global.listen_socket == -1 &&
-	     irc_connection_list == NULL)) {
-		b_main_quit();
-	}
-}
-
-static gboolean irc_free_hashkey(gpointer key, gpointer value, gpointer data)
-{
-	g_free(key);
-
-	return(TRUE);
-}
-
-/* USE WITH CAUTION!
-   Sets pass without checking */
-void irc_setpass(irc_t *irc, const char *pass)
-{
-	g_free(irc->password);
-
-	if (pass) {
-		irc->password = g_strdup(pass);
-	} else {
-		irc->password = NULL;
-	}
-}
-
-static char *set_eval_password(set_t *set, char *value)
-{
-	irc_t *irc = set->data;
-
-	if (irc->status & USTATUS_IDENTIFIED && value) {
-		irc_setpass(irc, value);
-		return NULL;
-	} else {
-		return SET_INVALID;
-	}
-}
-
-static char **irc_splitlines(char *buffer);
-
-void irc_process(irc_t *irc)
-{
-	char **lines, *temp, **cmd;
+	
+	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_splitlines(irc->readbuffer);
-
-		for (i = 0; *lines[i] != '\0'; i++) {
-			char *conv = NULL;
-
-			/* [WvG] If the last line isn't empty, it's an incomplete line and we
-			   should wait for the rest to come in before processing it. */
-			if (lines[i + 1] == NULL) {
-				temp = g_strdup(lines[i]);
-				g_free(irc->readbuffer);
+	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->iconv != (GIConv) - 1) {
-				gsize bytes_read, bytes_written;
-
-				conv = g_convert_with_iconv(lines[i], -1, irc->iconv,
-				                            &bytes_read, &bytes_written, NULL);
-
-				if (conv == NULL || bytes_read != strlen(lines[i])) {
-					/* GLib can do strange things if things are not in the expected charset,
-					   so let's be a little bit paranoid here: */
-					if (irc->status & USTATUS_LOGGED_IN) {
-						irc_rootmsg(irc, "Error: Charset mismatch detected. The charset "
-						            "setting is currently set to %s, so please make "
-						            "sure your IRC client will send and accept text in "
-						            "that charset, or tell BitlBee which charset to "
-						            "expect by changing the charset setting. See "
-						            "`help set charset' for more information. Your "
-						            "message was ignored.",
-						            set_getstr(&irc->b->set, "charset"));
-
-						g_free(conv);
-						conv = NULL;
-					} else {
-						irc_write(irc, ":%s NOTICE * :%s", irc->root->host,
-						          "Warning: invalid characters received at login time.");
-
-						conv = g_strdup(lines[i]);
-						for (temp = conv; *temp; temp++) {
-							if (*temp & 0x80) {
-								*temp = '?';
-							}
-						}
-					}
-				}
-				lines[i] = conv;
-			}
-
-			if (lines[i] && (cmd = irc_parse_line(lines[i]))) {
-				irc_exec(irc, cmd);
-				g_free(cmd);
-			}
-
-			g_free(conv);
-
-			/* Shouldn't really happen, but just in case... */
-			if (!g_slist_find(irc_connection_list, irc)) {
-				g_free(lines);
-				return;
-			}
-		}
-
-		if (lines[i] != NULL) {
+			}			
+			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);
-	}
-}
-
-/* Splits a long string into separate lines. The array is NULL-terminated
-   and, unless the string contains an incomplete line at the end, ends with
-   an empty string. Could use g_strsplit() but this one does it in-place.
-   (So yes, it's destructive.) */
-static char **irc_splitlines(char *buffer)
-{
-	int i, j, n = 3;
+			irc->readbuffer=NULL;	
+		}
+		g_free( lines );
+	}
+	return 1;	
+}
+
+char **irc_tokenize( char *buffer )
+{
+	int i, j;
 	char **lines;
 
-	/* Allocate n+1 elements. */
-	lines = g_new(char *, n + 1);
-
-	lines[0] = buffer;
-
-	/* Split the buffer in several strings, and accept any kind of line endings,
-	 * knowing that ERC on Windows may send something interesting like \r\r\n,
-	 * and surely there must be clients that think just \n is enough... */
-	for (i = 0, j = 0; buffer[i] != '\0'; i++) {
-		if (buffer[i] == '\r' || buffer[i] == '\n') {
-			while (buffer[i] == '\r' || buffer[i] == '\n') {
-				buffer[i++] = '\0';
-			}
-
-			lines[++j] = buffer + i;
-
-			if (j >= n) {
-				n *= 2;
-				lines = g_renew(char *, lines, n + 1);
-			}
-
-			if (buffer[i] == '\0') {
-				break;
-			}
-		}
-	}
-
-	/* NULL terminate our list. */
-	lines[++j] = NULL;
-
-	return lines;
-}
-
-/* Split an IRC-style line into little parts/arguments. */
-char **irc_parse_line(char *line)
+	/* 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] && line[i] != ' '; i++) {
-			;
-		}
-		line = line + i;
-	}
-	for (i = 0; line[i] == ' '; i++) {
-		;
-	}
-	line = line + i;
+	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 NULL;
-	}
-
-	/* Count the number of char **cmd elements we're going to need. */
-	j = 1;
-	for (i = 0; line[i] != '\0'; i++) {
-		if (line[i] == ' ') {
+	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++;
-
-			if (line[i + 1] == ':') {
-				break;
-			}
-		}
-	}
+		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;
-
+	cmd=g_new(char *, j+1);
+	cmd[j]=NULL;
+	
 	/* Do the actual line splitting, format is:
 	 * Input: "PRIVMSG #bitlbee :foo bar"
@@ -526,336 +370,1168 @@
 	 */
 
-	cmd[0] = line;
-	for (i = 0, j = 0; line[i] != '\0'; i++) {
-		if (line[i] == ' ') {
-			line[i] = '\0';
-			cmd[++j] = line + i + 1;
-
-			if (line[i + 1] == ':') {
-				cmd[j]++;
+	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;
-			}
-		}
-	}
-
-	return cmd;
-}
-
-/* Converts such an array back into a command string. Mainly used for the IPC code right now. */
-char *irc_build_line(char **cmd)
-{
-	int i, len;
+			
+			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;
-
-	if (cmd[0] == NULL) {
-		return NULL;
-	}
-
-	len = 1;
-	for (i = 0; cmd[i]; i++) {
-		len += strlen(cmd[i]) + 1;
-	}
-
-	if (strchr(cmd[i - 1], ' ') != NULL) {
-		len++;
-	}
-
-	s = g_new0(char, len + 1);
-	for (i = 0; cmd[i]; i++) {
-		if (cmd[i + 1] == NULL && strchr(cmd[i], ' ') != NULL) {
-			strcat(s, ":");
-		}
-
-		strcat(s, cmd[i]);
-
-		if (cmd[i + 1]) {
-			strcat(s, " ");
-		}
-	}
-	strcat(s, "\r\n");
-
-	return s;
-}
-
-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_write_all(int now, char *format, ...)
-{
-	va_list params;
-	GSList *temp;
-
-	va_start(params, format);
-
-	temp = irc_connection_list;
-	while (temp != NULL) {
-		irc_t *irc = temp->data;
-
-		if (now) {
-			g_string_assign(irc->sendbuffer, "\r\n");
-		}
-		irc_vawrite(temp->data, format, params);
-		if (now) {
-			bitlbee_io_current_client_write(irc, irc->fd, B_EV_IO_WRITE);
-		}
-		temp = temp->next;
-	}
-
-	va_end(params);
-	return;
-}
-
-void irc_vawrite(irc_t *irc, char *format, va_list params)
-{
-	char line[IRC_MAX_LINE + 1];
-
-	/* Don't try to write anything new anymore when shutting down. */
-	if (irc->status & USTATUS_SHUTDOWN) {
-		return;
-	}
-
-	memset(line, 0, sizeof(line));
-	g_vsnprintf(line, IRC_MAX_LINE - 2, format, params);
-	strip_newlines(line);
-
-	if (irc->oconv != (GIConv) - 1) {
-		gsize bytes_read, bytes_written;
-		char *conv;
-
-		conv = g_convert_with_iconv(line, -1, irc->oconv,
-		                            &bytes_read, &bytes_written, NULL);
-
-		if (bytes_read == strlen(line)) {
-			strncpy(line, conv, IRC_MAX_LINE - 2);
-		}
-
-		g_free(conv);
-	}
-	g_strlcat(line, "\r\n", IRC_MAX_LINE + 1);
-
-	g_string_append(irc->sendbuffer, line);
-
-	if (irc->w_watch_source_id == 0) {
-		/* If the buffer is empty we can probably write, so call the write event handler
-		   immediately. If it returns TRUE, it should be called again, so add the event to
-		   the queue. If it's FALSE, we emptied the buffer and saved ourselves some work
-		   in the event queue. */
-		/* Really can't be done as long as the code doesn't do error checking very well:
-		if( bitlbee_io_current_client_write( irc, irc->fd, B_EV_IO_WRITE ) ) */
-
-		/* So just always do it via the event handler. */
-		irc->w_watch_source_id = b_input_add(irc->fd, B_EV_IO_WRITE, bitlbee_io_current_client_write, irc);
-	}
-
-	return;
-}
-
-/* Flush sendbuffer if you can. If it fails, fail silently and let some
-   I/O event handler clean up. */
-void irc_flush(irc_t *irc)
-{
-	ssize_t n;
-	size_t len = irc->sendbuffer->len;
-
-	if (len == 0) {
-		return;
-	}
-
-	if ((n = send(irc->fd, irc->sendbuffer->str, len, 0)) == len) {
-		g_string_truncate(irc->sendbuffer, 0);
-
-		b_event_remove(irc->w_watch_source_id);
-		irc->w_watch_source_id = 0;
-	} else if (n > 0) {
-		g_string_erase(irc->sendbuffer, 0, n);
-	}
-	/* Otherwise something went wrong and we don't currently care
-	   what the error was. We may or may not succeed later, we
-	   were just trying to flush the buffer immediately. */
-}
-
-/* Meant for takeover functionality. Transfer an IRC connection to a different
-   socket. */
-void irc_switch_fd(irc_t *irc, int fd)
-{
-	irc_write(irc, "ERROR :Transferring session to a new connection");
-	irc_flush(irc);   /* Write it now or forget about it forever. */
-
-	if (irc->sendbuffer) {
-		b_event_remove(irc->w_watch_source_id);
-		irc->w_watch_source_id = 0;
-		g_string_truncate(irc->sendbuffer, 0);
-	}
-
-	b_event_remove(irc->r_watch_source_id);
-	closesocket(irc->fd);
-	irc->fd = fd;
-	irc->r_watch_source_id = b_input_add(irc->fd, B_EV_IO_READ, bitlbee_io_current_client_read, irc);
-}
-
-void irc_sync(irc_t *irc)
-{
-	GSList *l;
-
-	irc_write(irc, ":%s!%s@%s MODE %s :+%s", irc->user->nick,
-	          irc->user->user, irc->user->host, irc->user->nick,
-	          irc->umode);
-
-	for (l = irc->channels; l; l = l->next) {
-		irc_channel_t *ic = l->data;
-		if (ic->flags & IRC_CHANNEL_JOINED) {
-			irc_send_join(ic, irc->user);
-		}
-	}
-
-	/* We may be waiting for a PONG from the previous client connection. */
-	irc->pinging = FALSE;
-}
-
-void irc_desync(irc_t *irc)
-{
-	GSList *l;
-
-	for (l = irc->channels; l; l = l->next) {
-		irc_channel_del_user(l->data, irc->user, IRC_CDU_KICK,
-		                     "Switching to old session");
-	}
-
-	irc_write(irc, ":%s!%s@%s MODE %s :-%s", irc->user->nick,
-	          irc->user->user, irc->user->host, irc->user->nick,
-	          irc->umode);
-}
-
-int irc_check_login(irc_t *irc)
-{
-	if (irc->user->user && irc->user->nick && !(irc->status & USTATUS_CAP_PENDING)) {
-		if (global.conf->authmode == AUTHMODE_CLOSED && !(irc->status & USTATUS_AUTHORIZED)) {
-			irc_send_num(irc, 464, ":This server is password-protected.");
-			return 0;
-		} else {
-			irc_channel_t *ic;
-			irc_user_t *iu = irc->user;
-
-			irc->user = irc_user_new(irc, iu->nick);
-			irc->user->user = iu->user;
-			irc->user->host = iu->host;
-			irc->user->fullname = iu->fullname;
-			irc->user->f = &irc_user_self_funcs;
-			g_free(iu->nick);
-			g_free(iu);
-
-			if (global.conf->runmode == RUNMODE_FORKDAEMON || global.conf->runmode == RUNMODE_DAEMON) {
-				ipc_to_master_str("CLIENT %s %s :%s\r\n", irc->user->host, irc->user->nick,
-				                  irc->user->fullname);
-			}
-
-			irc->status |= USTATUS_LOGGED_IN;
-
-			irc_send_login(irc);
-
-			irc->umode[0] = '\0';
-			irc_umode_set(irc, "+" UMODE, TRUE);
-
-			ic = irc->default_channel = irc_channel_new(irc, ROOT_CHAN);
-			irc_channel_set_topic(ic, CONTROL_TOPIC, irc->root);
-			set_setstr(&ic->set, "auto_join", "true");
-			irc_channel_auto_joins(irc, NULL);
-
-			irc->root->last_channel = irc->default_channel;
-
-			irc_rootmsg(irc,
-			            "Welcome to the BitlBee gateway!\n\n"
-			            "Running %s %s\n\n"
-			            "If you've never used BitlBee before, please do read the help "
-			            "information using the \x02help\x02 command. Lots of FAQs are "
-			            "answered there.\n"
-			            "If you already have an account on this server, just use the "
-			            "\x02identify\x02 command to identify yourself.",
-			            PACKAGE, BITLBEE_VERSION);
-
-			/* This is for bug #209 (use PASS to identify to NickServ). */
-			if (irc->password != NULL) {
-				char *send_cmd[] = { "identify", g_strdup(irc->password), NULL };
-
-				irc_setpass(irc, NULL);
-				root_command(irc, send_cmd);
-				g_free(send_cmd[1]);
-			}
-
-			return 1;
-		}
-	} else {
-		/* More information needed. */
-		return 0;
-	}
-}
-
-/* TODO: This is a mess, but this function is a bit too complicated to be
-   converted to something more generic. */
-void irc_umode_set(irc_t *irc, const char *s, gboolean allow_priv)
-{
-	/* allow_priv: Set to 0 if s contains user input, 1 if you want
-	   to set a "privileged" mode (+o, +R, etc). */
-	char m[128], st = 1;
-	const char *t;
+	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;
-	char changes[512], st2 = 2;
-	char badflag = 0;
-
-	memset(m, 0, sizeof(m));
-
-	/* Keep track of which modes are enabled in this array. */
-	for (t = irc->umode; *t; t++) {
-		if (*t < sizeof(m)) {
-			m[(int) *t] = 1;
-		}
-	}
-
-	i = 0;
-	for (t = s; *t && i < sizeof(changes) - 3; t++) {
-		if (*t == '+' || *t == '-') {
+	
+	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 if ((st == 0 && (!strchr(UMODES_KEEP, *t) || allow_priv)) ||
-		           (st == 1 && strchr(UMODES, *t)) ||
-		           (st == 1 && allow_priv && strchr(UMODES_PRIV, *t))) {
-			if (m[(int) *t] != st) {
-				/* If we're actually making a change, remember this
-				   for the response. */
-				if (st != st2) {
-					st2 = st, changes[i++] = st ? '+' : '-';
+		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;
 				}
-				changes[i++] = *t;
-			}
-			m[(int) *t] = st;
-		} else {
-			badflag = 1;
-		}
-	}
-	changes[i] = '\0';
-
-	/* Convert the m array back into an umode string. */
-	memset(irc->umode, 0, sizeof(irc->umode));
-	for (i = 'A'; i <= 'z' && strlen(irc->umode) < (sizeof(irc->umode) - 1); i++) {
-		if (m[i]) {
-			irc->umode[strlen(irc->umode)] = i;
-		}
-	}
-
-	if (badflag) {
-		irc_send_num(irc, 501, ":Unknown MODE flag");
-	}
-	if (*changes) {
-		irc_write(irc, ":%s!%s@%s MODE %s :%s", irc->user->nick,
-		          irc->user->user, irc->user->host, irc->user->nick,
-		          changes);
-	}
+			}
+			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 );
 }
 
@@ -864,124 +1540,34 @@
    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, gint fd, b_input_condition cond)
-{
-	double now = gettime();
+static gboolean irc_userping( gpointer _irc )
+{
 	irc_t *irc = _irc;
-	int fail = 0;
-
-	if (!(irc->status & USTATUS_LOGGED_IN)) {
-		if (now > (irc->last_pong + IRC_LOGIN_TIMEOUT)) {
-			fail = now - irc->last_pong;
-		}
-	} else {
-		if (now > (irc->last_pong + global.conf->ping_timeout)) {
-			fail = now - irc->last_pong;
-		} else {
-			irc_write(irc, "PING :%s", IRC_PING_STRING);
-		}
-	}
-
-	if (fail > 0) {
-		irc_abort(irc, 0, "Ping Timeout: %d seconds", fail);
+	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;
 }
-
-static char *set_eval_charset(set_t *set, char *value)
-{
-	irc_t *irc = (irc_t *) set->data;
-	char *test;
-	gsize test_bytes = 0;
-	GIConv ic, oc;
-
-	if (g_strcasecmp(value, "none") == 0) {
-		value = g_strdup("utf-8");
-	}
-
-	if ((oc = g_iconv_open(value, "utf-8")) == (GIConv) - 1) {
-		return NULL;
-	}
-
-	/* Do a test iconv to see if the user picked an IRC-compatible
-	   charset (for example utf-16 goes *horribly* wrong). */
-	if ((test = g_convert_with_iconv(" ", 1, oc, NULL, &test_bytes, NULL)) == NULL ||
-	    test_bytes > 1) {
-		g_free(test);
-		g_iconv_close(oc);
-		irc_rootmsg(irc, "Unsupported character set: The IRC protocol "
-		            "only supports 8-bit character sets.");
-		return NULL;
-	}
-	g_free(test);
-
-	if ((ic = g_iconv_open("utf-8", value)) == (GIConv) - 1) {
-		g_iconv_close(oc);
-		return NULL;
-	}
-
-	if (irc->iconv != (GIConv) - 1) {
-		g_iconv_close(irc->iconv);
-	}
-	if (irc->oconv != (GIConv) - 1) {
-		g_iconv_close(irc->oconv);
-	}
-
-	irc->iconv = ic;
-	irc->oconv = oc;
-
-	return value;
-}
-
-/* Mostly meant for upgrades. If one of these is set to the non-default,
-   set show_users of all channels to something with the same effect. */
-static char *set_eval_bw_compat(set_t *set, char *value)
-{
-	irc_t *irc = set->data;
-	char *val;
-	GSList *l;
-
-	irc_rootmsg(irc, "Setting `%s' is obsolete, use the `show_users' "
-	            "channel setting instead.", set->key);
-
-	if (strcmp(set->key, "away_devoice") == 0 && !bool2int(value)) {
-		val = "online,special%,away";
-	} else if (strcmp(set->key, "show_offline") == 0 && bool2int(value)) {
-		val = "online@,special%,away+,offline";
-	} else {
-		val = "online+,special%,away";
-	}
-
-	for (l = irc->channels; l; l = l->next) {
-		irc_channel_t *ic = l->data;
-		/* No need to check channel type, if the setting doesn't exist it
-		   will just be ignored. */
-		set_setstr(&ic->set, "show_users", val);
-	}
-
-	return SET_INVALID;
-}
-
-static char *set_eval_utf8_nicks(set_t *set, char *value)
-{
-	irc_t *irc = set->data;
-	gboolean val = bool2int(value);
-
-	/* Do *NOT* unset this flag in the middle of a session. There will
-	   be UTF-8 nicks around already so if we suddenly disable support
-	   for them, various functions might behave strangely. */
-	if (val) {
-		irc->status |= IRC_UTF8_NICKS;
-	} else if (irc->status & IRC_UTF8_NICKS) {
-		irc_rootmsg(irc, "You need to reconnect to BitlBee for this "
-		            "change to take effect.");
-	}
-
-	return set_eval_bool(set, value);
-}
-
-void register_irc_plugin(const struct irc_plugin *p)
-{
-	irc_plugins = g_slist_prepend(irc_plugins, (gpointer) p);
-}
Index: irc.h
===================================================================
--- irc.h	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ irc.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -1,9 +1,9 @@
-/********************************************************************\
+  /********************************************************************\
   * BitlBee -- An IRC to other IM-networks gateway                     *
   *                                                                    *
-  * Copyright 2002-2013 Wilmer van der Gaast and others                *
+  * Copyright 2002-2004 Wilmer van der Gaast and others                *
   \********************************************************************/
 
-/* The IRC-based UI (for now the only one)                              */
+/* The big hairy IRCd part of the project                               */
 
 /*
@@ -20,6 +20,6 @@
   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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
 */
 
@@ -27,354 +27,121 @@
 #define _IRC_H
 
-#include <sys/socket.h>
-
 #define IRC_MAX_LINE 512
-#define IRC_MAX_ARGS 16
-#define IRC_WORD_WRAP 425
+#define IRC_MAX_ARGS 8
 
 #define IRC_LOGIN_TIMEOUT 60
 #define IRC_PING_STRING "PinglBee"
 
-#define UMODES "abisw"     /* Allowed umodes (although they mostly do nothing) */
-#define UMODES_PRIV "Ro"   /* Allowed, but not by user directly */
-#define UMODES_KEEP "R"    /* Don't allow unsetting using /MODE */
-#define CMODES "ntC"       /* Allowed modes */
-#define CMODE "t"          /* Default mode */
-#define UMODE "s"          /* Default mode */
+/* #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 CTYPES "&#"        /* Valid channel name prefixes */
+#define UMODES "ais"
+#define CMODES "nt"
+#define CMODE "t"
+#define UMODE "s"
 
-typedef enum {
-	USTATUS_OFFLINE = 0,
-	USTATUS_AUTHORIZED = 1, /* Gave the correct server password (PASS). */
-	USTATUS_LOGGED_IN = 2,  /* USER+NICK(+PASS) finished. */
-	USTATUS_IDENTIFIED = 4, /* To NickServ (root). */
-	USTATUS_SHUTDOWN = 8,   /* Now used to indicate we're shutting down.
-	                           Currently just blocks irc_vawrite(). */
-	USTATUS_CAP_PENDING = 16,
-	USTATUS_SASL_PLAIN_PENDING = 32,
-
-	/* Not really status stuff, but other kinds of flags: For slightly
-	   better password security, since the only way to send passwords
-	   to the IRC server securely (i.e. not echoing to screen or written
-	   to logfiles) is the /OPER command, try to use that command for
-	   stuff that matters. */
-	OPER_HACK_IDENTIFY = 0x100,
-	OPER_HACK_IDENTIFY_NOLOAD = 0x01100,
-	OPER_HACK_IDENTIFY_FORCE  = 0x02100,
-	OPER_HACK_REGISTER = 0x200,
-	OPER_HACK_ACCOUNT_PASSWORD = 0x400,
-	OPER_HACK_ANY = 0x3700, /* To check for them all at once. */
-
-	IRC_UTF8_NICKS = 0x10000, /* Disable ASCII restrictions on buddy nicks. */
+typedef enum
+{
+	USTATUS_OFFLINE,
+	USTATUS_AUTHORIZED,
+	USTATUS_LOGGED_IN,
+	USTATUS_IDENTIFIED
 } irc_status_t;
 
-typedef enum {
-	CAP_SASL = (1 << 0),
-	CAP_MULTI_PREFIX = (1 << 1),
-	CAP_EXTENDED_JOIN = (1 << 2),
-	CAP_AWAY_NOTIFY = (1 << 3),
-	CAP_USERHOST_IN_NAMES = (1 << 4),
-	CAP_SERVER_TIME = (1 << 5),
-} irc_cap_flag_t;
+typedef struct channel
+{
+	char *name;
+} channel_t;
 
-struct irc_user;
-
-typedef struct irc {
+typedef struct irc
+{
 	int fd;
 	irc_status_t status;
 	double last_pong;
 	int pinging;
-	GString *sendbuffer;
+	char *sendbuffer;
 	char *readbuffer;
-	GIConv iconv, oconv;
+	int quit;
 
-	struct irc_user *root;
-	struct irc_user *user;
-
-	char *password; /* HACK: Used to save the user's password, but before
-	                   logging in, this may contain a password we should
-	                   send to identify after USER/NICK are received. */
-	char *auth_backend;
-
-	char umode[8];
-
-	struct query *queries;
-	GSList *file_transfers;
-
-	GSList *users G_GNUC_DEPRECATED;
-	GSList *channels;
-	struct irc_channel *default_channel;
-	GHashTable *nick_user_hash;
-	GHashTable *watches; /* See irc_cmd_watch() */
-
-	gint r_watch_source_id;
-	gint w_watch_source_id;
-	gint ping_source_id;
-	gint login_source_id; /* To slightly delay some events at login time. */
-
-	struct otr *otr; /* OTR state and book keeping, used by the OTR plugin.
-	                    TODO: Some mechanism for plugindata. */
-
-	struct bee *b;
-	guint32 caps;
-} irc_t;
-
-typedef enum {
-	/* Replaced with iu->last_channel IRC_USER_PRIVATE = 1, */
-	IRC_USER_AWAY = 2,
-
-	IRC_USER_OTR_ENCRYPTED = 0x10000,
-	IRC_USER_OTR_TRUSTED   = 0x20000,
-} irc_user_flags_t;
-
-typedef struct irc_user {
-	irc_t *irc;
+	int sentbytes;
+	time_t oldtime;
 
 	char *nick;
 	char *user;
 	char *host;
-	char *fullname;
+	char *realname;
+	char *password;
 
-	/* Nickname in lowercase for case insensitive searches */
-	char *key;
+	char umode[8];
+	
+	char *myhost;
+	char *mynick;
 
-	irc_user_flags_t flags;
-	struct irc_channel *last_channel;
+	char *channel;
+	int c_id;
 
-	GString *pastebuf; /* Paste buffer (combine lines into a multiline msg). */
-	guint pastebuf_timer;
-	time_t away_reply_timeout; /* Only send a 301 if this time passed. */
-
-	struct bee_user *bu;
-
-	const struct irc_user_funcs *f;
-} irc_user_t;
-
-struct irc_user_funcs {
-	gboolean (*privmsg)(irc_user_t *iu, const char *msg);
-	gboolean (*ctcp)(irc_user_t *iu, char * const* ctcp);
-};
-
-extern const struct irc_user_funcs irc_user_root_funcs;
-extern const struct irc_user_funcs irc_user_self_funcs;
-
-typedef enum {
-	IRC_CHANNEL_JOINED = 1, /* The user is currently in the channel. */
-	IRC_CHANNEL_TEMP = 2,   /* Erase the channel when the user leaves,
-	                           and don't save it. */
-
-	/* Hack: Set this flag right before jumping into IM when we expect
-	   a call to imcb_chat_new(). */
-	IRC_CHANNEL_CHAT_PICKME = 0x10000,
-} irc_channel_flags_t;
-
-typedef struct irc_channel {
-	irc_t *irc;
-	char *name;
-	char mode[8];
-	int flags;
-
-	char *topic;
-	char *topic_who;
-	time_t topic_time;
-
-	GSList *users; /* struct irc_channel_user */
-	struct irc_user *last_target;
+	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;
 
-	GString *pastebuf; /* Paste buffer (combine lines into a multiline msg). */
-	guint pastebuf_timer;
+	GIOChannel *io_channel;
+	gint r_watch_source_id;
+	gint w_watch_source_id;
+	gint ping_source_id;
+} irc_t;
 
-	const struct irc_channel_funcs *f;
-	void *data;
-} irc_channel_t;
+#include "user.h"
+#include "nick.h"
 
-struct irc_channel_funcs {
-	gboolean (*privmsg)(irc_channel_t *ic, const char *msg);
-	gboolean (*join)(irc_channel_t *ic);
-	gboolean (*part)(irc_channel_t *ic, const char *msg);
-	gboolean (*topic)(irc_channel_t *ic, const char *new_topic);
-	gboolean (*invite)(irc_channel_t *ic, irc_user_t *iu);
-	void (*kick)(irc_channel_t *ic, irc_user_t *iu, const char *msg);
-
-	gboolean (*_init)(irc_channel_t *ic);
-	gboolean (*_free)(irc_channel_t *ic);
-};
-
-typedef enum {
-	IRC_CHANNEL_USER_OP = 1,
-	IRC_CHANNEL_USER_HALFOP = 2,
-	IRC_CHANNEL_USER_VOICE = 4,
-	IRC_CHANNEL_USER_NONE = 8,
-} irc_channel_user_flags_t;
-
-typedef struct irc_channel_user {
-	irc_user_t *iu;
-	int flags;
-} irc_channel_user_t;
-
-typedef enum {
-	IRC_CC_TYPE_DEFAULT  = 0x00001,
-	IRC_CC_TYPE_REST     = 0x00002, /* Still not implemented. */
-	IRC_CC_TYPE_GROUP    = 0x00004,
-	IRC_CC_TYPE_ACCOUNT  = 0x00008,
-	IRC_CC_TYPE_PROTOCOL = 0x00010,
-	IRC_CC_TYPE_MASK     = 0x000ff,
-	IRC_CC_TYPE_INVERT   = 0x00100,
-} irc_control_channel_type_t;
-
-struct irc_control_channel {
-	irc_control_channel_type_t type;
-	struct bee_group *group;
-	struct account *account;
-	struct prpl *protocol;
-	char modes[5];
-};
-
-extern const struct bee_ui_funcs irc_ui_funcs;
-
-typedef enum {
-	IRC_CDU_SILENT,
-	IRC_CDU_PART,
-	IRC_CDU_KICK,
-} irc_channel_del_user_type_t;
-
-/* These are a glued a little bit to the core/bee layer and a little bit to
-   IRC. The first user is OTR, and I guess at some point we'll get to shape
-   this a little bit more as other uses come up. */
-typedef struct irc_plugin {
-	/* Called at the end of irc_new(). Can be used to add settings, etc. */
-	gboolean (*irc_new)(irc_t *irc);
-	/* At the end of irc_free(). */
-	void (*irc_free)(irc_t *irc);
-
-	/* Problem with the following two functions is ordering if multiple
-	   plugins are handling them. Let's keep fixing that problem for
-	   whenever it becomes important. */
-
-	/* Called by bee_irc_user_privmsg_cb(). Return NULL if you want to
-	   abort sending the msg. */
-	char* (*filter_msg_out)(irc_user_t * iu, char *msg, int flags);
-	/* Called by bee_irc_user_msg(). Return NULL if you swallowed the
-	   message and don't want anything to go to the user. */
-	char* (*filter_msg_in)(irc_user_t * iu, char *msg, int flags);
-
-	/* From storage.c functions. Ideally these should not be used
-	   and instead data should be stored in settings which will get
-	   saved automatically. Consider these deprecated! */
-	void (*storage_load)(irc_t *irc);
-	void (*storage_save)(irc_t *irc);
-	void (*storage_remove)(const char *nick);
-} irc_plugin_t;
-
-extern GSList *irc_plugins; /* struct irc_plugin */
-
-/* irc.c */
 extern GSList *irc_connection_list;
 
-irc_t *irc_new(int fd);
-void irc_set_hosts(irc_t *irc, const struct sockaddr *remote_addr, const socklen_t remote_addrlen);
-void irc_abort(irc_t *irc, int immed, char *format, ...) G_GNUC_PRINTF(3, 4);
-void irc_free(irc_t *irc);
-void irc_setpass(irc_t *irc, const char *pass);
+irc_t *irc_new( int fd );
+void irc_free( irc_t *irc );
 
-void irc_process(irc_t *irc);
-char **irc_parse_line(char *line);
-char *irc_build_line(char **cmd);
+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_write(irc_t *irc, char *format, ...) G_GNUC_PRINTF(2, 3);
-void irc_write_all(int now, char *format, ...) G_GNUC_PRINTF(2, 3);
-void irc_vawrite(irc_t *irc, char *format, va_list params);
+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_flush(irc_t *irc);
-void irc_switch_fd(irc_t *irc, int fd);
-void irc_sync(irc_t *irc);
-void irc_desync(irc_t *irc);
+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_check_login(irc_t *irc);
+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 );
 
-void irc_umode_set(irc_t *irc, const char *s, gboolean allow_priv);
-
-void register_irc_plugin(const struct irc_plugin *p);
-
-/* irc_channel.c */
-irc_channel_t *irc_channel_new(irc_t *irc, const char *name);
-irc_channel_t *irc_channel_by_name(irc_t *irc, const char *name);
-irc_channel_t *irc_channel_get(irc_t *irc, char *id);
-int irc_channel_free(irc_channel_t *ic);
-void irc_channel_free_soon(irc_channel_t *ic);
-int irc_channel_add_user(irc_channel_t *ic, irc_user_t *iu);
-int irc_channel_del_user(irc_channel_t *ic, irc_user_t *iu, irc_channel_del_user_type_t type, const char *msg);
-irc_channel_user_t *irc_channel_has_user(irc_channel_t *ic, irc_user_t *iu);
-struct irc_channel *irc_channel_with_user(irc_t *irc, irc_user_t *iu);
-int irc_channel_set_topic(irc_channel_t *ic, const char *topic, const irc_user_t *who);
-void irc_channel_user_set_mode(irc_channel_t *ic, irc_user_t *iu, irc_channel_user_flags_t flags);
-void irc_channel_set_mode(irc_channel_t *ic, const char *s);
-void irc_channel_auto_joins(irc_t *irc, struct account *acc);
-void irc_channel_printf(irc_channel_t *ic, char *format, ...) G_GNUC_PRINTF(2, 3);
-gboolean irc_channel_name_ok(const char *name);
-void irc_channel_name_strip(char *name);
-int irc_channel_name_cmp(const char *a_, const char *b_);
-char *irc_channel_name_gen(irc_t *irc, const char *name);
-gboolean irc_channel_name_hint(irc_channel_t *ic, const char *name);
-void irc_channel_update_ops(irc_channel_t *ic, char *value);
-char irc_channel_user_get_prefix(irc_channel_user_t *icu);
-char *set_eval_irc_channel_ops(struct set *set, char *value);
-gboolean irc_channel_wants_user(irc_channel_t *ic, irc_user_t *iu);
-
-/* irc_commands.c */
-void irc_exec(irc_t *irc, char **cmd);
-
-/* irc_send.c */
-void irc_send_num(irc_t *irc, int code, char *format, ...) G_GNUC_PRINTF(3, 4);
-void irc_send_login(irc_t *irc);
-void irc_send_motd(irc_t *irc);
-const char *irc_user_msgdest(irc_user_t *iu);
-void irc_rootmsg(irc_t *irc, char *format, ...) G_GNUC_PRINTF(2, 3);
-void irc_usermsg(irc_user_t *iu, char *format, ...) G_GNUC_PRINTF(2, 3);
-void irc_usernotice(irc_user_t *iu, char *format, ...) G_GNUC_PRINTF(2, 3);
-void irc_send_join(irc_channel_t *ic, irc_user_t *iu);
-void irc_send_part(irc_channel_t *ic, irc_user_t *iu, const char *reason);
-void irc_send_quit(irc_user_t *iu, const char *reason);
-void irc_send_kick(irc_channel_t *ic, irc_user_t *iu, irc_user_t *kicker, const char *reason);
-void irc_send_names(irc_channel_t *ic);
-void irc_send_topic(irc_channel_t *ic, gboolean topic_change);
-void irc_send_whois(irc_user_t *iu);
-void irc_send_who(irc_t *irc, GSList *l, const char *channel);
-void irc_send_msg(irc_user_t *iu, const char *type, const char *dst, const char *msg, const char *prefix);
-void irc_send_msg_raw(irc_user_t *iu, const char *type, const char *dst, const char *msg);
-void irc_send_msg_f(irc_user_t *iu, const char *type, const char *dst, const char *format, ...) G_GNUC_PRINTF(4, 5);
-void irc_send_nick(irc_user_t *iu, const char *new_nick);
-void irc_send_channel_user_mode_diff(irc_channel_t *ic, irc_user_t *iu,
-                                     irc_channel_user_flags_t old_flags, irc_channel_user_flags_t new_flags);
-void irc_send_invite(irc_user_t *iu, irc_channel_t *ic);
-void irc_send_cap(irc_t *irc, char *subcommand, char *body);
-void irc_send_away_notify(irc_user_t *iu);
-
-G_GNUC_INTERNAL void irc_send_msg_ts(irc_user_t *iu, const char *type, const char *dst, const char *msg, const char *prefix, time_t ts);
-G_GNUC_INTERNAL void irc_send_msg_raw_tags(irc_user_t *iu, const char *type, const char *dst, const char* tags, const char *msg);
-
-/* irc_user.c */
-irc_user_t *irc_user_new(irc_t *irc, const char *nick);
-int irc_user_free(irc_t *irc, irc_user_t *iu);
-irc_user_t *irc_user_by_name(irc_t *irc, const char *nick);
-int irc_user_set_nick(irc_user_t *iu, const char *new_nick);
-gint irc_user_cmp(gconstpointer a_, gconstpointer b_);
-const char *irc_user_get_away(irc_user_t *iu);
-void irc_user_quit(irc_user_t *iu, const char *msg);
-
-/* irc_util.c */
-char *set_eval_timezone(struct set *set, char *value);
-char *irc_format_timestamp(irc_t *irc, time_t msg_ts);
-G_GNUC_INTERNAL char *irc_format_servertime(irc_t *irc, time_t msg_ts);
-char *set_eval_self_messages(struct set *set, char *value);
-
-/* irc_im.c */
-void bee_irc_channel_update(irc_t *irc, irc_channel_t *ic, irc_user_t *iu);
-void bee_irc_user_nick_reset(irc_user_t *iu);
-
-/* irc_cap.c */
-void irc_cmd_cap(irc_t *irc, char **cmd);
+int buddy_send_handler( irc_t *irc, user_t *u, char *msg, int flags );
 
 #endif
Index: c_cap.c
===================================================================
--- irc_cap.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,197 +1,0 @@
-/********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2013 Wilmer van der Gaast and others                *
-  \********************************************************************/
-
-/* IRCv3 CAP command
- *
- * Specs:
- *  - v3.1: http://ircv3.net/specs/core/capability-negotiation-3.1.html
- *  - v3.2: http://ircv3.net/specs/core/capability-negotiation-3.2.html
- *
- * */
-
-/*
-  This program is free software; you can 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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#include "bitlbee.h"
-
-typedef struct {
-	char *name;
-	irc_cap_flag_t flag;
-} cap_info_t;
-
-static const cap_info_t supported_caps[] = {
-	{"sasl", CAP_SASL},
-	{"multi-prefix", CAP_MULTI_PREFIX},
-	{"extended-join", CAP_EXTENDED_JOIN},
-	{"away-notify", CAP_AWAY_NOTIFY},
-	{"userhost-in-names", CAP_USERHOST_IN_NAMES},
-	{"server-time", CAP_SERVER_TIME},
-	{NULL},
-};
-
-static irc_cap_flag_t cap_flag_from_string(char *cap_name)
-{
-	int i;
-
-	if (!cap_name || !cap_name[0]) {
-		return 0;
-	}
-
-	if (cap_name[0] == '-') {
-		cap_name++;
-	}
-
-	for (i = 0; supported_caps[i].name; i++) {
-		if (strcmp(supported_caps[i].name, cap_name) == 0) {
-			return supported_caps[i].flag;
-		}
-	}
-	return 0;
-}
-
-static gboolean irc_cmd_cap_req(irc_t *irc, char *caps)
-{
-	int i;
-	char *lower = NULL;
-	char **split = NULL;
-	irc_cap_flag_t new_caps = irc->caps;
-
-	if (!caps || !caps[0]) {
-		return FALSE;
-	}
-
-	lower = g_ascii_strdown(caps, -1);
-	split = g_strsplit(lower, " ", -1);
-	g_free(lower);
-
-	for (i = 0; split[i]; i++) {
-		gboolean remove;
-		irc_cap_flag_t flag;
-
-		if (!split[i][0]) {
-			continue;   /* skip empty items (consecutive spaces) */
-		}
-
-		remove = (split[i][0] == '-');
-		flag = cap_flag_from_string(split[i]);
-		
-		if (!flag || (remove && !(irc->caps & flag))) {
-			/* unsupported cap, or removing something that isn't there */
-			g_strfreev(split);
-			return FALSE;
-		}
-
-		if (remove) {
-			new_caps &= ~flag;
-		} else {
-			new_caps |= flag;
-		}
-	}
-
-	/* if we got here, set the new caps and ack */
-	irc->caps = new_caps;
-
-	g_strfreev(split);
-	return TRUE;
-}
-
-/* version can be 0, 302, 303, or garbage from user input. thanks user input */
-static void irc_cmd_cap_ls(irc_t *irc, long version)
-{
-	int i;
-	GString *str = g_string_sized_new(256);
-
-	for (i = 0; supported_caps[i].name; i++) {
-		if (i != 0) {
-			g_string_append_c(str, ' ');
-		}
-		g_string_append(str, supported_caps[i].name);
-
-		if (version >= 302 && supported_caps[i].flag == CAP_SASL) {
-			g_string_append(str, "=PLAIN");
-		}
-	}
-
-	irc_send_cap(irc, "LS", str->str);
-
-	g_string_free(str, TRUE);
-}
-
-/* this one looks suspiciously similar to cap ls,
- * but cap-3.2 will make them very different */
-static void irc_cmd_cap_list(irc_t *irc)
-{
-	int i;
-	gboolean first = TRUE;
-	GString *str = g_string_sized_new(256);
-
-	for (i = 0; supported_caps[i].name; i++) {
-		if (irc->caps & supported_caps[i].flag) {
-			if (!first) {
-				g_string_append_c(str, ' ');
-			}
-			first = FALSE;
-
-			g_string_append(str, supported_caps[i].name);
-		}
-	}
-
-	irc_send_cap(irc, "LIST", str->str);
-
-	g_string_free(str, TRUE);
-}
-
-void irc_cmd_cap(irc_t *irc, char **cmd)
-{
-	if (!(irc->status & USTATUS_LOGGED_IN)) {
-		/* Put registration on hold until CAP END */
-		irc->status |= USTATUS_CAP_PENDING;
-	}
-
-	if (g_strcasecmp(cmd[1], "LS") == 0) {
-		irc_cmd_cap_ls(irc, cmd[2] ? strtol(cmd[2], NULL, 10) : 0);
-
-	} else if (g_strcasecmp(cmd[1], "LIST") == 0) {
-		irc_cmd_cap_list(irc);
-
-	} else if (g_strcasecmp(cmd[1], "REQ") == 0) {
-		gboolean ack = irc_cmd_cap_req(irc, cmd[2]);
-
-		irc_send_cap(irc, ack ? "ACK" : "NAK", cmd[2] ? : "");
-
-	} else if (g_strcasecmp(cmd[1], "END") == 0) {
-		if (!(irc->status & USTATUS_CAP_PENDING)) {
-			return;
-		}
-		irc->status &= ~USTATUS_CAP_PENDING;
-
-		if (irc->status & USTATUS_SASL_PLAIN_PENDING) {
-			irc_send_num(irc, 906, ":SASL authentication aborted");
-			irc->status &= ~USTATUS_SASL_PLAIN_PENDING;
-		}
-
-		irc_check_login(irc);
-
-	} else {
-		irc_send_num(irc, 410, "%s :Invalid CAP command", cmd[1]);
-	}
-
-}
-
Index: c_channel.c
===================================================================
--- irc_channel.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,1016 +1,0 @@
-/********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2013 Wilmer van der Gaast and others                *
-  \********************************************************************/
-
-/* The IRC-based UI - Representing (virtual) channels.                  */
-
-/*
-  This program is free software; you can 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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#include "bitlbee.h"
-
-static char *set_eval_channel_type(set_t *set, char *value);
-static gint irc_channel_user_cmp(gconstpointer a_, gconstpointer b_);
-static const struct irc_channel_funcs control_channel_funcs;
-
-extern const struct irc_channel_funcs irc_channel_im_chat_funcs;
-
-irc_channel_t *irc_channel_new(irc_t *irc, const char *name)
-{
-	irc_channel_t *ic;
-	set_t *s;
-
-	if (!irc_channel_name_ok(name) || irc_channel_by_name(irc, name)) {
-		return NULL;
-	}
-
-	ic = g_new0(irc_channel_t, 1);
-	ic->irc = irc;
-	ic->name = g_strdup(name);
-	strcpy(ic->mode, CMODE);
-
-	irc_channel_add_user(ic, irc->root);
-
-	irc->channels = g_slist_append(irc->channels, ic);
-
-	set_add(&ic->set, "auto_join", "false", set_eval_bool, ic);
-
-	s = set_add(&ic->set, "type", "control", set_eval_channel_type, ic);
-	s->flags |= SET_NOSAVE;    /* Layer violation (XML format detail) */
-
-	if (name[0] == '&') {
-		set_setstr(&ic->set, "type", "control");
-	} else { /* if( name[0] == '#' ) */
-		set_setstr(&ic->set, "type", "chat");
-	}
-
-	return ic;
-}
-
-irc_channel_t *irc_channel_by_name(irc_t *irc, const char *name)
-{
-	GSList *l;
-
-	for (l = irc->channels; l; l = l->next) {
-		irc_channel_t *ic = l->data;
-
-		if (irc_channel_name_cmp(name, ic->name) == 0) {
-			return ic;
-		}
-	}
-
-	return NULL;
-}
-
-irc_channel_t *irc_channel_get(irc_t *irc, char *id)
-{
-	irc_channel_t *ic, *ret = NULL;
-	GSList *l;
-	int nr;
-
-	if (sscanf(id, "%d", &nr) == 1 && nr < 1000) {
-		for (l = irc->channels; l; l = l->next) {
-			ic = l->data;
-			if ((nr--) == 0) {
-				return ic;
-			}
-		}
-
-		return NULL;
-	}
-
-	/* Exact match first: Partial match only sucks if there's a channel
-	   #aa and #aabb */
-	if ((ret = irc_channel_by_name(irc, id))) {
-		return ret;
-	}
-
-	for (l = irc->channels; l; l = l->next) {
-		ic = l->data;
-
-		if (strstr(ic->name, id)) {
-			/* Make sure it's a unique match. */
-			if (!ret) {
-				ret = ic;
-			} else {
-				return NULL;
-			}
-		}
-	}
-
-	return ret;
-}
-
-int irc_channel_free(irc_channel_t *ic)
-{
-	irc_t *irc;
-	GHashTableIter iter;
-	gpointer itervalue;
-
-	if (ic == NULL) {
-		return 0;
-	}
-	irc = ic->irc;
-
-	if (ic->flags & IRC_CHANNEL_JOINED) {
-		irc_channel_del_user(ic, irc->user, IRC_CDU_KICK, "Cleaning up channel");
-	}
-
-	if (ic->f->_free) {
-		ic->f->_free(ic);
-	}
-
-	while (ic->set) {
-		set_del(&ic->set, ic->set->key);
-	}
-
-	irc->channels = g_slist_remove(irc->channels, ic);
-	while (ic->users) {
-		g_free(ic->users->data);
-		ic->users = g_slist_remove(ic->users, ic->users->data);
-	}
-
-	g_hash_table_iter_init(&iter, irc->nick_user_hash);
-
-	while (g_hash_table_iter_next(&iter, NULL, &itervalue)) {
-		irc_user_t *iu = itervalue;
-
-		if (iu->last_channel == ic) {
-			iu->last_channel = irc->default_channel;
-		}
-	}
-
-	if (ic->pastebuf_timer) {
-		b_event_remove(ic->pastebuf_timer);
-	}
-
-	g_free(ic->name);
-	g_free(ic->topic);
-	g_free(ic->topic_who);
-	g_free(ic);
-
-	return 1;
-}
-
-struct irc_channel_free_data {
-	irc_t *irc;
-	irc_channel_t *ic;
-	char *name;
-};
-
-static gboolean irc_channel_free_callback(gpointer data, gint fd, b_input_condition cond)
-{
-	struct irc_channel_free_data *d = data;
-
-	if (g_slist_find(irc_connection_list, d->irc) &&
-	    irc_channel_by_name(d->irc, d->name) == d->ic &&
-	    !(d->ic->flags & IRC_CHANNEL_JOINED)) {
-		irc_channel_free(d->ic);
-	}
-
-	g_free(d->name);
-	g_free(d);
-	return FALSE;
-}
-
-/* Free the channel, but via the event loop, so after finishing whatever event
-   we're currently handling. */
-void irc_channel_free_soon(irc_channel_t *ic)
-{
-	struct irc_channel_free_data *d = g_new0(struct irc_channel_free_data, 1);
-
-	d->irc = ic->irc;
-	d->ic = ic;
-	d->name = g_strdup(ic->name);
-
-	b_timeout_add(0, irc_channel_free_callback, d);
-}
-
-static char *set_eval_channel_type(set_t *set, char *value)
-{
-	struct irc_channel *ic = set->data;
-	const struct irc_channel_funcs *new;
-
-	if (strcmp(value, "control") == 0) {
-		new = &control_channel_funcs;
-	} else if (ic != ic->irc->default_channel && strcmp(value, "chat") == 0) {
-		new = &irc_channel_im_chat_funcs;
-	} else {
-		return SET_INVALID;
-	}
-
-	/* Skip the free/init if nothing is being changed */
-	if (ic->f == new) {
-		return value;
-	}
-
-	/* TODO: Return values. */
-	if (ic->f && ic->f->_free) {
-		ic->f->_free(ic);
-	}
-
-	ic->f = new;
-
-	if (ic->f && ic->f->_init) {
-		ic->f->_init(ic);
-	}
-
-	return value;
-}
-
-int irc_channel_add_user(irc_channel_t *ic, irc_user_t *iu)
-{
-	irc_channel_user_t *icu;
-
-	if (irc_channel_has_user(ic, iu)) {
-		return 0;
-	}
-
-	icu = g_new0(irc_channel_user_t, 1);
-	icu->iu = iu;
-
-	ic->users = g_slist_insert_sorted(ic->users, icu, irc_channel_user_cmp);
-
-	if (iu == ic->irc->user || iu == ic->irc->root) {
-		irc_channel_update_ops(ic, set_getstr(&ic->irc->b->set, "ops"));
-	}
-
-	if (iu == ic->irc->user || ic->flags & IRC_CHANNEL_JOINED) {
-		ic->flags |= IRC_CHANNEL_JOINED;
-		irc_send_join(ic, iu);
-	}
-
-	return 1;
-}
-
-int irc_channel_del_user(irc_channel_t *ic, irc_user_t *iu, irc_channel_del_user_type_t type, const char *msg)
-{
-	irc_channel_user_t *icu;
-
-	if (!(icu = irc_channel_has_user(ic, iu))) {
-		if (iu == ic->irc->user && type == IRC_CDU_KICK) {
-			/* an error happened before joining, inform the client with a numeric */
-			irc_send_num(ic->irc, 403, "%s :Error joining channel (check control channel?)", ic->name);
-		}
-		return 0;
-	}
-
-	ic->users = g_slist_remove(ic->users, icu);
-	g_free(icu);
-
-	if (!(ic->flags & IRC_CHANNEL_JOINED) || type == IRC_CDU_SILENT) {
-	}
-	/* Do nothing. The caller should promise it won't screw
-	   up state of the IRC client. :-) */
-	else if (type == IRC_CDU_PART) {
-		irc_send_part(ic, iu, msg);
-	} else if (type == IRC_CDU_KICK) {
-		irc_send_kick(ic, iu, ic->irc->root, msg);
-	}
-
-	if (iu == ic->irc->user) {
-		ic->flags &= ~IRC_CHANNEL_JOINED;
-
-		if (ic->irc->status & USTATUS_SHUTDOWN) {
-			/* Don't do anything fancy when we're shutting down anyway. */
-		} else if (ic->flags & IRC_CHANNEL_TEMP) {
-			irc_channel_free_soon(ic);
-		} else {
-			/* Flush userlist now. The user won't see it anyway. */
-			while (ic->users) {
-				g_free(ic->users->data);
-				ic->users = g_slist_remove(ic->users, ic->users->data);
-			}
-			irc_channel_add_user(ic, ic->irc->root);
-		}
-	}
-
-	return 1;
-}
-
-irc_channel_user_t *irc_channel_has_user(irc_channel_t *ic, irc_user_t *iu)
-{
-	GSList *l;
-
-	for (l = ic->users; l; l = l->next) {
-		irc_channel_user_t *icu = l->data;
-
-		if (icu->iu == iu) {
-			return icu;
-		}
-	}
-
-	return NULL;
-}
-
-/* Find a channel we're currently in, that currently has iu in it. */
-struct irc_channel *irc_channel_with_user(irc_t *irc, irc_user_t *iu)
-{
-	GSList *l;
-
-	for (l = irc->channels; l; l = l->next) {
-		irc_channel_t *ic = l->data;
-
-		if (strcmp(set_getstr(&ic->set, "type"), "control") != 0) {
-			continue;
-		}
-
-		if ((ic->flags & IRC_CHANNEL_JOINED) &&
-		    irc_channel_has_user(ic, iu)) {
-			return ic;
-		}
-	}
-
-	/* If there was no match, try once more but just see if the user
-	   *would* be in the channel, i.e. if s/he were online. */
-	if (iu->bu == NULL) {
-		return NULL;
-	}
-
-	for (l = irc->channels; l; l = l->next) {
-		irc_channel_t *ic = l->data;
-
-		if (strcmp(set_getstr(&ic->set, "type"), "control") != 0) {
-			continue;
-		}
-
-		if ((ic->flags & IRC_CHANNEL_JOINED) &&
-		    irc_channel_wants_user(ic, iu)) {
-			return ic;
-		}
-	}
-
-	return NULL;
-}
-
-int irc_channel_set_topic(irc_channel_t *ic, const char *topic, const irc_user_t *iu)
-{
-	g_free(ic->topic);
-	ic->topic = g_strdup(topic);
-
-	g_free(ic->topic_who);
-	if (iu) {
-		ic->topic_who = g_strdup_printf("%s!%s@%s", iu->nick, iu->user, iu->host);
-	} else {
-		ic->topic_who = NULL;
-	}
-
-	ic->topic_time = time(NULL);
-
-	if (ic->flags & IRC_CHANNEL_JOINED) {
-		irc_send_topic(ic, TRUE);
-	}
-
-	return 1;
-}
-
-void irc_channel_user_set_mode(irc_channel_t *ic, irc_user_t *iu, irc_channel_user_flags_t flags)
-{
-	irc_channel_user_t *icu = irc_channel_has_user(ic, iu);
-
-	if (!icu || icu->flags == flags) {
-		return;
-	}
-
-	if (ic->flags & IRC_CHANNEL_JOINED) {
-		irc_send_channel_user_mode_diff(ic, iu, icu->flags, flags);
-	}
-
-	icu->flags = flags;
-}
-
-void irc_channel_set_mode(irc_channel_t *ic, const char *s)
-{
-	irc_t *irc = ic->irc;
-	char m[128], st = 1;
-	const char *t;
-	int i;
-	char changes[512], *p, st2 = 2;
-
-	memset(m, 0, sizeof(m));
-
-	for (t = ic->mode; *t; t++) {
-		if (*t < sizeof(m)) {
-			m[(int) *t] = 1;
-		}
-	}
-
-	p = changes;
-	for (t = s; *t; t++) {
-		if (*t == '+' || *t == '-') {
-			st = *t == '+';
-		} else if (strchr(CMODES, *t)) {
-			if (m[(int) *t] != st) {
-				if (st != st2) {
-					st2 = st, *p++ = st ? '+' : '-';
-				}
-				*p++ = *t;
-			}
-			m[(int) *t] = st;
-		}
-	}
-	*p = '\0';
-
-	memset(ic->mode, 0, sizeof(ic->mode));
-
-	for (i = 'A'; i <= 'z' && strlen(ic->mode) < (sizeof(ic->mode) - 1); i++) {
-		if (m[i]) {
-			ic->mode[strlen(ic->mode)] = i;
-		}
-	}
-
-	if (*changes && (ic->flags & IRC_CHANNEL_JOINED)) {
-		irc_write(irc, ":%s!%s@%s MODE %s :%s", irc->root->nick,
-		          irc->root->user, irc->root->host, ic->name,
-		          changes);
-	}
-}
-
-char irc_channel_user_get_prefix(irc_channel_user_t *icu)
-{
-	if (icu->flags & IRC_CHANNEL_USER_OP) {
-		return '@';
-	} else if (icu->flags & IRC_CHANNEL_USER_HALFOP) {
-		return '%';
-	} else if (icu->flags & IRC_CHANNEL_USER_VOICE) {
-		return '+';
-	}
-	return 0;
-}
-
-void irc_channel_auto_joins(irc_t *irc, account_t *acc)
-{
-	GSList *l;
-
-	for (l = irc->channels; l; l = l->next) {
-		irc_channel_t *ic = l->data;
-		gboolean aj = set_getbool(&ic->set, "auto_join");
-		char *type;
-
-		if (acc &&
-		    (type = set_getstr(&ic->set, "chat_type")) &&
-		    strcmp(type, "room") == 0) {
-			/* Bit of an ugly special case: Handle chatrooms here, we
-			   can only auto-join them if their account is online. */
-			char *acc_s;
-
-			if (!aj && !(ic->flags & IRC_CHANNEL_JOINED)) {
-				/* Only proceed if this one's marked as auto_join
-				   or if we're in it already. (Very likely the IRC
-				   client auto-(re)joining at reconnect time.) */
-				continue;
-			} else if (!(acc_s = set_getstr(&ic->set, "account"))) {
-				continue;
-			} else if (account_get(irc->b, acc_s) != acc) {
-				continue;
-			} else if (acc->ic == NULL || !(acc->ic->flags & OPT_LOGGED_IN)) {
-				continue;
-			} else {
-				ic->f->join(ic);
-			}
-		} else if (aj) {
-			irc_channel_add_user(ic, irc->user);
-		}
-	}
-}
-
-void irc_channel_printf(irc_channel_t *ic, char *format, ...)
-{
-	va_list params;
-	char *text;
-
-	va_start(params, format);
-	text = g_strdup_vprintf(format, params);
-	va_end(params);
-
-	irc_send_msg(ic->irc->root, "PRIVMSG", ic->name, text, NULL);
-	g_free(text);
-}
-
-gboolean irc_channel_name_ok(const char *name_)
-{
-	const unsigned char *name = (unsigned char *) name_;
-	int i;
-
-	if (name_[0] == '\0') {
-		return FALSE;
-	}
-
-	/* Check if the first character is in CTYPES (#&) */
-	if (strchr(CTYPES, name_[0]) == NULL) {
-		return FALSE;
-	}
-
-	/* RFC 1459 keeps amazing me: While only a "few" chars are allowed
-	   in nicknames, channel names can be pretty much anything as long
-	   as they start with # or &. I'll be a little bit more strict and
-	   disallow all non-printable characters. */
-	for (i = 1; name[i]; i++) {
-		if (name[i] <= ' ' || name[i] == ',') {
-			return FALSE;
-		}
-	}
-
-	return TRUE;
-}
-
-void irc_channel_name_strip(char *name)
-{
-	int i, j;
-
-	for (i = j = 0; name[i]; i++) {
-		if (name[i] > ' ' && name[i] != ',') {
-			name[j++] = name[i];
-		}
-	}
-
-	name[j] = '\0';
-}
-
-int irc_channel_name_cmp(const char *a_, const char *b_)
-{
-	static unsigned char case_map[256];
-	const unsigned char *a = (unsigned char *) a_, *b = (unsigned char *) b_;
-	int i;
-
-	if (case_map['A'] == '\0') {
-		for (i = 33; i < 256; i++) {
-			if (i != ',') {
-				case_map[i] = i;
-			}
-		}
-
-		for (i = 0; i < 26; i++) {
-			case_map['A' + i] = 'a' + i;
-		}
-
-		case_map['['] = '{';
-		case_map[']'] = '}';
-		case_map['~'] = '`';
-		case_map['\\'] = '|';
-	}
-
-	if (!irc_channel_name_ok(a_) || !irc_channel_name_ok(b_)) {
-		return -1;
-	}
-
-	for (i = 0; a[i] && b[i] && case_map[a[i]] && case_map[b[i]]; i++) {
-		if (case_map[a[i]] == case_map[b[i]]) {
-			continue;
-		} else {
-			return case_map[a[i]] - case_map[b[i]];
-		}
-	}
-
-	return case_map[a[i]] - case_map[b[i]];
-}
-
-gboolean irc_channel_is_unused(irc_t *irc, char *name)
-{
-	char *type, *chat_type;
-	irc_channel_t *oic;
-
-	if (!irc_channel_name_ok(name)) {
-		return FALSE;
-	}
-
-	if (!(oic = irc_channel_by_name(irc, name))) {
-		return TRUE;
-	}
-
-	type = set_getstr(&oic->set, "type");
-	chat_type = set_getstr(&oic->set, "chat_type");
-
-	if (type && chat_type && oic->data == FALSE &&
-	    strcmp(type, "chat") == 0 &&
-	    strcmp(chat_type, "groupchat") == 0) {
-		/* There's a channel with this name already, but it looks
-		   like it's not in use yet. Most likely the IRC client
-		   rejoined the channel after a reconnect. Remove it so
-		   we can reuse its name. */
-		irc_channel_free(oic);
-		return TRUE;
-	}
-
-	return FALSE;
-}
-
-char *irc_channel_name_gen(irc_t *irc, const char *hint)
-{
-	char name[MAX_NICK_LENGTH + 1] = { 0 };
-	char *translit_name;
-	gsize bytes_written;
-
-	translit_name = g_convert_with_fallback(hint, -1, "ASCII//TRANSLIT", "UTF-8", "", NULL, &bytes_written, NULL);
-
-	if (!translit_name) {
-		/* Same thing as in nick_gen() in nick.c, try again without //TRANSLIT */
-		translit_name = g_convert_with_fallback(hint, -1, "ASCII", "UTF-8", "", NULL, &bytes_written, NULL);
-	}
-
-	if (!translit_name) {
-		return NULL;
-	}
-
-	if (bytes_written > MAX_NICK_LENGTH) {
-		translit_name[MAX_NICK_LENGTH] = '\0';
-	}
-
-	name[0] = '#';
-	strncpy(name + 1, translit_name, MAX_NICK_LENGTH - 1);
-	name[MAX_NICK_LENGTH] = '\0';
-
-	g_free(translit_name);
-
-	irc_channel_name_strip(name);
-
-	if (set_getbool(&irc->b->set, "nick_lowercase")) {
-		nick_lc(irc, name + 1);
-	}
-
-	while (!irc_channel_is_unused(irc, name)) {
-		underscore_dedupe(name);
-	}
-
-	return g_strdup(name);
-}
-
-gboolean irc_channel_name_hint(irc_channel_t *ic, const char *name)
-{
-	irc_t *irc = ic->irc;
-	char *full_name;
-
-	/* Don't rename a channel if the user's in it already. */
-	if (ic->flags & IRC_CHANNEL_JOINED) {
-		return FALSE;
-	}
-
-	if (!(full_name = irc_channel_name_gen(irc, name))) {
-		return FALSE;
-	}
-
-	g_free(ic->name);
-	ic->name = full_name;
-
-	return TRUE;
-}
-
-static gint irc_channel_user_cmp(gconstpointer a_, gconstpointer b_)
-{
-	const irc_channel_user_t *a = a_, *b = b_;
-
-	return irc_user_cmp(a->iu, b->iu);
-}
-
-void irc_channel_update_ops(irc_channel_t *ic, char *value)
-{
-	irc_channel_user_set_mode(ic, ic->irc->root,
-	                          (strcmp(value, "both") == 0 ||
-	                           strcmp(value, "root") == 0) ? IRC_CHANNEL_USER_OP : 0);
-	irc_channel_user_set_mode(ic, ic->irc->user,
-	                          (strcmp(value, "both") == 0 ||
-	                           strcmp(value, "user") == 0) ? IRC_CHANNEL_USER_OP : 0);
-}
-
-char *set_eval_irc_channel_ops(set_t *set, char *value)
-{
-	irc_t *irc = set->data;
-	GSList *l;
-
-	if (strcmp(value, "both") != 0 && strcmp(value, "none") != 0 &&
-	    strcmp(value, "user") != 0 && strcmp(value, "root") != 0) {
-		return SET_INVALID;
-	}
-
-	for (l = irc->channels; l; l = l->next) {
-		irc_channel_update_ops(l->data, value);
-	}
-
-	return value;
-}
-
-/* Channel-type dependent functions, for control channels: */
-static gboolean control_channel_privmsg(irc_channel_t *ic, const char *msg)
-{
-	irc_t *irc = ic->irc;
-	irc_user_t *iu;
-	const char *s;
-
-	/* Scan for non-whitespace chars followed by a colon: */
-	for (s = msg; *s && !g_ascii_isspace(*s) && *s != ':' && *s != ','; s++) {
-	}
-
-	if (*s == ':' || *s == ',') {
-		char to[s - msg + 1];
-
-		memset(to, 0, sizeof(to));
-		strncpy(to, msg, s - msg);
-		while (*(++s) && g_ascii_isspace(*s)) {
-		}
-		msg = s;
-
-		if (!(iu = irc_user_by_name(irc, to))) {
-			irc_channel_printf(ic, "User does not exist: %s", to);
-		} else {
-			ic->last_target = iu;
-		}
-	} else if (g_strcasecmp(set_getstr(&irc->b->set, "default_target"), "last") == 0 &&
-	           ic->last_target && g_hash_table_contains(irc->nick_user_hash, ic->last_target->key)) {
-		iu = ic->last_target;
-	} else {
-		iu = irc->root;
-	}
-
-	if (iu && iu->f->privmsg) {
-		iu->last_channel = ic;
-		iu->f->privmsg(iu, msg);
-	}
-
-	return TRUE;
-}
-
-static gboolean control_channel_invite(irc_channel_t *ic, irc_user_t *iu)
-{
-	struct irc_control_channel *icc = ic->data;
-	bee_user_t *bu = iu->bu;
-
-	if (bu == NULL) {
-		return FALSE;
-	}
-
-	if (icc->type != IRC_CC_TYPE_GROUP) {
-		irc_send_num(ic->irc, 482, "%s :Invitations are only possible to fill_by=group channels", ic->name);
-		return FALSE;
-	}
-
-	bu->ic->acc->prpl->add_buddy(bu->ic, bu->handle,
-	                             icc->group ? icc->group->name : NULL);
-
-	return TRUE;
-}
-
-static void control_channel_kick(irc_channel_t *ic, irc_user_t *iu, const char *msg)
-{
-	struct irc_control_channel *icc = ic->data;
-	bee_user_t *bu = iu->bu;
-
-	if (bu == NULL) {
-		return;
-	}
-
-	if (icc->type != IRC_CC_TYPE_GROUP) {
-		irc_send_num(ic->irc, 482, "%s :Kicks are only possible to fill_by=group channels", ic->name);
-		return;
-	}
-
-	bu->ic->acc->prpl->remove_buddy(bu->ic, bu->handle,
-	                                icc->group ? icc->group->name : NULL);
-}
-
-static char *set_eval_by_account(set_t *set, char *value);
-static char *set_eval_fill_by(set_t *set, char *value);
-static char *set_eval_by_group(set_t *set, char *value);
-static char *set_eval_by_protocol(set_t *set, char *value);
-static char *set_eval_show_users(set_t *set, char *value);
-
-static gboolean control_channel_init(irc_channel_t *ic)
-{
-	struct irc_control_channel *icc;
-
-	set_add(&ic->set, "account", NULL, set_eval_by_account, ic);
-	set_add(&ic->set, "fill_by", "all", set_eval_fill_by, ic);
-	set_add(&ic->set, "group", NULL, set_eval_by_group, ic);
-	set_add(&ic->set, "protocol", NULL, set_eval_by_protocol, ic);
-
-	/* When changing the default, also change it below. */
-	set_add(&ic->set, "show_users", "online+,special%,away", set_eval_show_users, ic);
-
-	ic->data = icc = g_new0(struct irc_control_channel, 1);
-	icc->type = IRC_CC_TYPE_DEFAULT;
-
-	/* Have to run the evaluator to initialize icc->modes. */
-	set_setstr(&ic->set, "show_users", "online+,special%,away");
-
-	/* For scripts that care. */
-	irc_channel_set_mode(ic, "+C");
-
-	return TRUE;
-}
-
-static gboolean control_channel_join(irc_channel_t *ic)
-{
-	bee_irc_channel_update(ic->irc, ic, NULL);
-
-	return TRUE;
-}
-
-static char *set_eval_by_account(set_t *set, char *value)
-{
-	struct irc_channel *ic = set->data;
-	struct irc_control_channel *icc = ic->data;
-	account_t *acc;
-
-	if (!(acc = account_get(ic->irc->b, value))) {
-		return SET_INVALID;
-	}
-
-	icc->account = acc;
-	if ((icc->type & IRC_CC_TYPE_MASK) == IRC_CC_TYPE_ACCOUNT) {
-		bee_irc_channel_update(ic->irc, ic, NULL);
-	}
-
-	return g_strdup(acc->tag);
-}
-
-static char *set_eval_fill_by(set_t *set, char *value)
-{
-	struct irc_channel *ic = set->data;
-	struct irc_control_channel *icc = ic->data;
-	char *s;
-
-	icc->type &= ~(IRC_CC_TYPE_MASK | IRC_CC_TYPE_INVERT);
-
-	s = value;
-	if (s[0] == '!') {
-		icc->type |= IRC_CC_TYPE_INVERT;
-		s++;
-	}
-
-	if (strcmp(s, "all") == 0) {
-		icc->type |= IRC_CC_TYPE_DEFAULT;
-	} else if (strcmp(s, "rest") == 0) {
-		icc->type |= IRC_CC_TYPE_REST;
-	} else if (strcmp(s, "group") == 0) {
-		icc->type |= IRC_CC_TYPE_GROUP;
-	} else if (strcmp(s, "account") == 0) {
-		icc->type |= IRC_CC_TYPE_ACCOUNT;
-	} else if (strcmp(s, "protocol") == 0) {
-		icc->type |= IRC_CC_TYPE_PROTOCOL;
-	} else {
-		return SET_INVALID;
-	}
-
-	bee_irc_channel_update(ic->irc, ic, NULL);
-	return value;
-}
-
-static char *set_eval_by_group(set_t *set, char *value)
-{
-	struct irc_channel *ic = set->data;
-	struct irc_control_channel *icc = ic->data;
-
-	icc->group = bee_group_by_name(ic->irc->b, value, TRUE);
-	if ((icc->type & IRC_CC_TYPE_MASK) == IRC_CC_TYPE_GROUP) {
-		bee_irc_channel_update(ic->irc, ic, NULL);
-	}
-
-	return g_strdup(icc->group->name);
-}
-
-static char *set_eval_by_protocol(set_t *set, char *value)
-{
-	struct irc_channel *ic = set->data;
-	struct irc_control_channel *icc = ic->data;
-	struct prpl *prpl;
-
-	if (!(prpl = find_protocol(value))) {
-		return SET_INVALID;
-	}
-
-	icc->protocol = prpl;
-	if ((icc->type & IRC_CC_TYPE_MASK) == IRC_CC_TYPE_PROTOCOL) {
-		bee_irc_channel_update(ic->irc, ic, NULL);
-	}
-
-	return value;
-}
-
-static char *set_eval_show_users(set_t *set, char *value)
-{
-	struct irc_channel *ic = set->data;
-	struct irc_control_channel *icc = ic->data;
-	char **parts = g_strsplit(value, ",", 0), **part;
-	char modes[5];
-
-	memset(modes, 0, 5);
-	for (part = parts; *part; part++) {
-		char last, modechar = IRC_CHANNEL_USER_NONE;
-
-		if (**part == '\0') {
-			goto fail;
-		}
-
-		last = (*part)[strlen(*part + 1)];
-		if (last == '+') {
-			modechar = IRC_CHANNEL_USER_VOICE;
-		} else if (last == '%') {
-			modechar = IRC_CHANNEL_USER_HALFOP;
-		} else if (last == '@') {
-			modechar = IRC_CHANNEL_USER_OP;
-		}
-
-		if (strncmp(*part, "offline", 7) == 0) {
-			modes[0] = modechar;
-		} else if (strncmp(*part, "away", 4) == 0) {
-			modes[1] = modechar;
-		} else if (strncmp(*part, "special", 7) == 0) {
-			modes[2] = modechar;
-		} else if (strncmp(*part, "online", 6) == 0) {
-			modes[3] = modechar;
-		} else {
-			goto fail;
-		}
-	}
-	memcpy(icc->modes, modes, 5);
-	bee_irc_channel_update(ic->irc, ic, NULL);
-
-	g_strfreev(parts);
-	return value;
-
-fail:
-	g_strfreev(parts);
-	return SET_INVALID;
-}
-
-/* Figure out if a channel is supposed to have the user, assuming s/he is
-   online or otherwise also selected by the show_users setting. Only works
-   for control channels, but does *not* check if this channel is of that
-   type. Beware! */
-gboolean irc_channel_wants_user(irc_channel_t *ic, irc_user_t *iu)
-{
-	struct irc_control_channel *icc = ic->data;
-	gboolean ret = FALSE;
-
-	if (iu->bu == NULL) {
-		return FALSE;
-	}
-
-	switch (icc->type & IRC_CC_TYPE_MASK) {
-	case IRC_CC_TYPE_GROUP:
-		ret = iu->bu->group == icc->group;
-		break;
-	case IRC_CC_TYPE_ACCOUNT:
-		ret = iu->bu->ic->acc == icc->account;
-		break;
-	case IRC_CC_TYPE_PROTOCOL:
-		ret = iu->bu->ic->acc->prpl == icc->protocol;
-		break;
-	case IRC_CC_TYPE_DEFAULT:
-	default:
-		ret = TRUE;
-		break;
-	}
-
-	if (icc->type & IRC_CC_TYPE_INVERT) {
-		ret = !ret;
-	}
-
-	return ret;
-}
-
-static gboolean control_channel_free(irc_channel_t *ic)
-{
-	struct irc_control_channel *icc = ic->data;
-
-	set_del(&ic->set, "account");
-	set_del(&ic->set, "fill_by");
-	set_del(&ic->set, "group");
-	set_del(&ic->set, "protocol");
-	set_del(&ic->set, "show_users");
-
-	g_free(icc);
-	ic->data = NULL;
-
-	/* For scripts that care. */
-	irc_channel_set_mode(ic, "-C");
-
-	return TRUE;
-}
-
-static const struct irc_channel_funcs control_channel_funcs = {
-	control_channel_privmsg,
-	control_channel_join,
-	NULL,
-	NULL,
-	control_channel_invite,
-	control_channel_kick,
-
-	control_channel_init,
-	control_channel_free,
-};
Index: c_commands.c
===================================================================
--- irc_commands.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,937 +1,0 @@
-/********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2012 Wilmer van der Gaast and others                *
-  \********************************************************************/
-
-/* IRC 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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#define BITLBEE_CORE
-#include "bitlbee.h"
-#include "canohost.h"
-#include "help.h"
-#include "ipc.h"
-#include "base64.h"
-
-static void irc_cmd_pass(irc_t *irc, char **cmd)
-{
-	if (irc->status & USTATUS_LOGGED_IN) {
-		char *send_cmd[] = { "identify", cmd[1], NULL };
-
-		/* We're already logged in, this client seems to send the PASS
-		   command last. (Possibly it won't send it at all if it turns
-		   out we don't require it, which will break this feature.)
-		   Try to identify using the given password. */
-		root_command(irc, send_cmd);
-		return;
-	}
-	/* Handling in pre-logged-in state, first see if this server is
-	   password-protected: */
-	else if (global.conf->auth_pass &&
-	         (strncmp(global.conf->auth_pass, "md5:", 4) == 0 ?
-	          md5_verify_password(cmd[1], global.conf->auth_pass + 4) == 0 :
-	          strcmp(cmd[1], global.conf->auth_pass) == 0)) {
-		irc->status |= USTATUS_AUTHORIZED;
-		irc_check_login(irc);
-	} else if (global.conf->auth_pass) {
-		irc_send_num(irc, 464, ":Incorrect password");
-	} else {
-		/* Remember the password and try to identify after USER/NICK. */
-		irc_setpass(irc, cmd[1]);
-		irc_check_login(irc);
-	}
-}
-
-/* http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
-
-   This isn't actually IRC, it's used by for example stunnel4 to indicate
-   the origin of the secured counterpart of the connection. It'll go wrong
-   with arguments starting with : like for example "::1" but I guess I'm
-   okay with that. */
-static void irc_cmd_proxy(irc_t *irc, char **cmd)
-{
-	struct addrinfo hints, *ai;
-	struct sockaddr_storage sock;
-	socklen_t socklen = sizeof(sock);
-
-	if (getpeername(irc->fd, (struct sockaddr*) &sock, &socklen) != 0) {
-		return;
-	}
-
-	ipv64_normalise_mapped(&sock, &socklen);
-
-	/* Only accept PROXY "command" on localhost sockets. */
-	if (!((sock.ss_family == AF_INET &&
-	       ntohl(((struct sockaddr_in*)&sock)->sin_addr.s_addr) == INADDR_LOOPBACK) ||
-	      (sock.ss_family == AF_INET6 &&
-	       IN6_IS_ADDR_LOOPBACK(&((struct sockaddr_in6*)&sock)->sin6_addr)))) {
-		return;
-	}
-
-	/* And only once. Do this with a pretty dumb regex-match for
-	   now, maybe better to use some sort of flag.. */
-	if (!g_regex_match_simple("^(ip6-)?localhost(.(localdomain.?)?)?$", irc->user->host, 0, 0)) {
-		return;
-	}
-	
-	memset(&hints, 0, sizeof(hints));
-	hints.ai_flags = AI_NUMERICHOST;
-	if (getaddrinfo(cmd[2], NULL, &hints, &ai) != 0) {
-		return;
-	}
-	
-	irc_set_hosts(irc, ai->ai_addr, ai->ai_addrlen);
-	freeaddrinfo(ai);
-}
-
-static gboolean irc_sasl_plain_parse(char *input, char **user, char **pass)
-{
-	int i, part, len;
-	guint8 *decoded;
-	char *parts[3];
-
-	/* bitlbee's base64_decode wrapper adds an extra null terminator at the end */
-	len = base64_decode(input, &decoded);
-
-	/* this loop splits the decoded string into the parts array, like this:
-	   "username\0username\0password" -> {"username", "username", "password"} */
-
-	for (i = 0, part = 0; i < len && part < 3; part++) {
-		/* set each of parts[] to point to the beginning of a string */
-		parts[part] = (char *) decoded + i;
-
-		/* move the cursor forward to the next null terminator*/
-		i += strlen(parts[part]) + 1;
-	}
-
-	/* sanity checks */
-	if (part != 3 || i != (len + 1) || (parts[0][0] && strcmp(parts[0], parts[1]) != 0)) {
-		g_free(decoded);
-		return FALSE;
-	} else {
-		*user = g_strdup(parts[1]);
-		*pass = g_strdup(parts[2]);
-		g_free(decoded);
-		return TRUE;
-	}
-}
-
-static gboolean irc_sasl_check_pass(irc_t *irc, char *user, char *pass)
-{
-	storage_status_t status;
-
-	/* just check the password here to be able to reply with useful numerics
-	 * the actual identification will be handled later */
-	status = auth_check_pass(irc, user, pass);
-
-	if (status == STORAGE_OK) {
-		if (!irc->user->nick) {
-			/* set the nick here so we have it for the following numeric */
-			irc->user->nick = g_strdup(user);
-		}
-		irc_send_num(irc, 900, "%s!%s@%s %s :You are now logged in as %s",
-		             irc->user->nick, irc->user->user, irc->user->host,
-			     irc->user->nick, irc->user->nick);
-		irc_send_num(irc, 903, ":Password accepted");
-		return TRUE;
-
-	} else if (status == STORAGE_INVALID_PASSWORD) {
-		irc_send_num(irc, 904, ":Incorrect password");
-	} else if (status == STORAGE_NO_SUCH_USER) {
-		irc_send_num(irc, 904, ":The nick is (probably) not registered");
-	} else {
-		irc_send_num(irc, 904, ":Unknown SASL authentication error");
-	}
-
-	return FALSE;
-}
-
-static void irc_cmd_authenticate(irc_t *irc, char **cmd)
-{
-	/* require the CAP to be enabled, and don't allow authentication before server password */
-	if (!(irc->caps & CAP_SASL) ||
-	    (global.conf->authmode == AUTHMODE_CLOSED && !(irc->status & USTATUS_AUTHORIZED))) {
-		return;
-	}
-
-	if (irc->status & USTATUS_SASL_PLAIN_PENDING) {
-		char *user, *pass;
-
-		irc->status &= ~USTATUS_SASL_PLAIN_PENDING;
-
-		if (!irc_sasl_plain_parse(cmd[1], &user, &pass)) {
-			irc_send_num(irc, 904, ":SASL authentication failed");
-			return;
-		}
-
-		/* let's not support the nick != user case
-		 * if NICK is received after SASL, it will just fail after registration */
-		if (user && irc->user->nick && strcmp(user, irc->user->nick) != 0) {
-			irc_send_num(irc, 902, ":Your SASL username does not match your nickname");
-
-		} else if (irc_sasl_check_pass(irc, user, pass)) {
-			/* and here we do the same thing as the PASS command*/
-			if (irc->status & USTATUS_LOGGED_IN) {
-				char *send_cmd[] = { "identify", pass, NULL };
-				root_command(irc, send_cmd);
-			} else {
-				/* no check_login here - wait for CAP END */
-				irc_setpass(irc, pass);
-			}
-		}
-
-		g_free(user);
-		g_free(pass);
-
-	} else if (irc->status & USTATUS_IDENTIFIED) {
-		irc_send_num(irc, 907, ":You have already authenticated");
-
-	} else if (strcmp(cmd[1], "*") == 0) {
-		irc_send_num(irc, 906, ":SASL authentication aborted");
-		irc->status &= ~USTATUS_SASL_PLAIN_PENDING;
-
-	} else if (g_strcasecmp(cmd[1], "PLAIN") == 0) {
-		irc_write(irc, "AUTHENTICATE +");
-		irc->status |= USTATUS_SASL_PLAIN_PENDING;
-
-	} else {
-		irc_send_num(irc, 908, "PLAIN :is the available SASL mechanism");
-		irc_send_num(irc, 904, ":SASL authentication failed");
-		irc->status &= ~USTATUS_SASL_PLAIN_PENDING;
-	}
-}
-
-static void irc_cmd_user(irc_t *irc, char **cmd)
-{
-	irc->user->user = g_strdup(cmd[1]);
-	irc->user->fullname = g_strdup(cmd[4]);
-
-	irc_check_login(irc);
-}
-
-static void irc_cmd_nick(irc_t *irc, char **cmd)
-{
-	irc_user_t *iu;
-
-	if ((iu = irc_user_by_name(irc, cmd[1])) && iu != irc->user) {
-		irc_send_num(irc, 433, "%s :This nick is already in use", cmd[1]);
-	} else if (!nick_ok(NULL, cmd[1])) {
-		/* [SH] Invalid characters. */
-		irc_send_num(irc, 432, "%s :This nick contains invalid characters", cmd[1]);
-	} else if (irc->status & USTATUS_LOGGED_IN) {
-		/* WATCH OUT: iu from the first if reused here to check if the
-		   new nickname is the same (other than case, possibly). If it
-		   is, no need to reset identify-status. */
-		if ((irc->status & USTATUS_IDENTIFIED) && iu != irc->user) {
-			irc_setpass(irc, NULL);
-			irc->status &= ~USTATUS_IDENTIFIED;
-			irc_umode_set(irc, "-R", 1);
-
-			if (irc->caps & CAP_SASL) {
-				irc_send_num(irc, 901, "%s!%s@%s :You are now logged out",
-					irc->user->nick, irc->user->user, irc->user->host);
-			}
-
-			irc_rootmsg(irc, "Changing nicks resets your identify status. "
-			            "Re-identify or register a new account if you want "
-			            "your configuration to be saved. See \x02help "
-			            "nick_changes\x02.");
-		}
-
-		if (strcmp(cmd[1], irc->user->nick) != 0) {
-			irc_user_set_nick(irc->user, cmd[1]);
-		}
-	} else {
-		g_free(irc->user->nick);
-		irc->user->nick = g_strdup(cmd[1]);
-
-		irc_check_login(irc);
-	}
-}
-
-static void irc_cmd_quit(irc_t *irc, char **cmd)
-{
-	if (cmd[1] && *cmd[1]) {
-		irc_abort(irc, 0, "Quit: %s", cmd[1]);
-	} else {
-		irc_abort(irc, 0, "Leaving...");
-	}
-}
-
-static void irc_cmd_ping(irc_t *irc, char **cmd)
-{
-	irc_write(irc, ":%s PONG %s :%s", irc->root->host,
-	          irc->root->host, cmd[1] ? cmd[1] : irc->root->host);
-}
-
-static void irc_cmd_pong(irc_t *irc, char **cmd)
-{
-	/* We could check the value we get back from the user, but in
-	   fact we don't care, we're just happy s/he's still alive. */
-	irc->last_pong = gettime();
-	irc->pinging = 0;
-}
-
-static void irc_cmd_join(irc_t *irc, char **cmd)
-{
-	char *comma, *s = cmd[1];
-
-	while (s) {
-		irc_channel_t *ic;
-
-		if ((comma = strchr(s, ','))) {
-			*comma = '\0';
-		}
-
-		if ((ic = irc_channel_by_name(irc, s)) == NULL &&
-		    (ic = irc_channel_new(irc, s))) {
-			if (strcmp(set_getstr(&ic->set, "type"), "control") != 0) {
-				/* Autoconfiguration is for control channels only ATM. */
-			} else if (bee_group_by_name(ic->irc->b, ic->name + 1, FALSE)) {
-				set_setstr(&ic->set, "group", ic->name + 1);
-				set_setstr(&ic->set, "fill_by", "group");
-			} else if (set_setstr(&ic->set, "protocol", ic->name + 1)) {
-				set_setstr(&ic->set, "fill_by", "protocol");
-			} else if (set_setstr(&ic->set, "account", ic->name + 1)) {
-				set_setstr(&ic->set, "fill_by", "account");
-			} else {
-				/* The set commands above will run this already,
-				   but if we didn't hit any, we have to fill the
-				   channel with the default population. */
-				bee_irc_channel_update(ic->irc, ic, NULL);
-			}
-		} else if (ic == NULL) {
-			irc_send_num(irc, 479, "%s :Invalid channel name", s);
-			goto next;
-		}
-
-		if (ic->flags & IRC_CHANNEL_JOINED) {
-			/* Dude, you're already there...
-			   RFC doesn't have any reply for that though? */
-			goto next;
-		}
-
-		if (ic->f->join && !ic->f->join(ic)) {
-			/* The story is: FALSE either means the handler
-			   showed an error message, or is doing some work
-			   before the join should be confirmed. (In the
-			   latter case, the caller should take care of that
-			   confirmation.) TRUE means all's good, let the
-			   user join the channel right away. */
-			goto next;
-		}
-
-		irc_channel_add_user(ic, irc->user);
-
-next:
-		if (comma) {
-			s = comma + 1;
-			*comma = ',';
-		} else {
-			break;
-		}
-	}
-}
-
-static void irc_cmd_names(irc_t *irc, char **cmd)
-{
-	irc_channel_t *ic;
-
-	if (cmd[1] && (ic = irc_channel_by_name(irc, cmd[1]))) {
-		irc_send_names(ic);
-	}
-	/* With no args, we should show /names of all chans. Make the code
-	   below work well if necessary.
-	else
-	{
-	        GSList *l;
-
-	        for( l = irc->channels; l; l = l->next )
-	                irc_send_names( l->data );
-	}
-	*/
-}
-
-static void irc_cmd_part(irc_t *irc, char **cmd)
-{
-	irc_channel_t *ic;
-
-	if ((ic = irc_channel_by_name(irc, cmd[1])) == NULL) {
-		irc_send_num(irc, 403, "%s :No such channel", cmd[1]);
-	} else if (irc_channel_del_user(ic, irc->user, IRC_CDU_PART, cmd[2])) {
-		if (ic->f->part) {
-			ic->f->part(ic, NULL);
-		}
-	} else {
-		irc_send_num(irc, 442, "%s :You're not on that channel", cmd[1]);
-	}
-}
-
-static void irc_cmd_whois(irc_t *irc, char **cmd)
-{
-	char *nick = cmd[1];
-	irc_user_t *iu = irc_user_by_name(irc, nick);
-
-	if (iu) {
-		irc_send_whois(iu);
-	} else {
-		irc_send_num(irc, 401, "%s :Nick does not exist", nick);
-	}
-}
-
-static void irc_cmd_whowas(irc_t *irc, char **cmd)
-{
-	/* 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 */
-
-	irc_send_num(irc, 406, "%s :Nick does not exist", cmd[1]);
-	irc_send_num(irc, 369, "%s :End of WHOWAS", cmd[1]);
-}
-
-static void irc_cmd_motd(irc_t *irc, char **cmd)
-{
-	irc_send_motd(irc);
-}
-
-static void irc_cmd_mode(irc_t *irc, char **cmd)
-{
-	if (irc_channel_name_ok(cmd[1])) {
-		irc_channel_t *ic;
-
-		if ((ic = irc_channel_by_name(irc, cmd[1])) == NULL) {
-			irc_send_num(irc, 403, "%s :No such channel", cmd[1]);
-		} else if (cmd[2]) {
-			if (*cmd[2] == '+' || *cmd[2] == '-') {
-				irc_send_num(irc, 477, "%s :Can't change channel modes", cmd[1]);
-			} else if (*cmd[2] == 'b') {
-				irc_send_num(irc, 368, "%s :No bans possible", cmd[1]);
-			}
-		} else {
-			irc_send_num(irc, 324, "%s +%s", cmd[1], ic->mode);
-		}
-	} else {
-		if (nick_cmp(NULL, cmd[1], irc->user->nick) == 0) {
-			if (cmd[2]) {
-				irc_umode_set(irc, cmd[2], 0);
-			} else {
-				irc_send_num(irc, 221, "+%s", irc->umode);
-			}
-		} else {
-			irc_send_num(irc, 502, ":Don't touch their modes");
-		}
-	}
-}
-
-static void irc_cmd_who(irc_t *irc, char **cmd)
-{
-	char *channel = cmd[1];
-	irc_channel_t *ic;
-	irc_user_t *iu;
-
-	if (!channel || *channel == '0' || *channel == '*' || !*channel) {
-		GList *all_users = g_hash_table_get_values(irc->nick_user_hash);
-		irc_send_who(irc, (GSList *) all_users, "**");
-		g_list_free(all_users);
-	} else if ((ic = irc_channel_by_name(irc, channel))) {
-		irc_send_who(irc, ic->users, channel);
-	} else if ((iu = irc_user_by_name(irc, channel))) {
-		/* Tiny hack! */
-		GSList *l = g_slist_append(NULL, iu);
-		irc_send_who(irc, l, channel);
-		g_slist_free(l);
-	} else {
-		irc_send_num(irc, 403, "%s :No such channel", channel);
-	}
-}
-
-static void irc_cmd_privmsg(irc_t *irc, char **cmd)
-{
-	irc_channel_t *ic;
-	irc_user_t *iu;
-
-	if (!cmd[2]) {
-		irc_send_num(irc, 412, ":No text to send");
-		return;
-	}
-
-	/* Don't treat CTCP actions as real CTCPs, just convert them right now. */
-	if (g_strncasecmp(cmd[2], "\001ACTION", 7) == 0) {
-		cmd[2] += 4;
-		memcpy(cmd[2], "/me", 3);
-		if (cmd[2][strlen(cmd[2]) - 1] == '\001') {
-			cmd[2][strlen(cmd[2]) - 1] = '\0';
-		}
-	}
-
-	if (irc_channel_name_ok(cmd[1]) &&
-	    (ic = irc_channel_by_name(irc, cmd[1]))) {
-		if (cmd[2][0] == '\001') {
-			/* CTCPs to channels? Nah. Maybe later. */
-		} else if (ic->f->privmsg) {
-			ic->f->privmsg(ic, cmd[2]);
-		}
-	} else if ((iu = irc_user_by_name(irc, cmd[1]))) {
-		if (cmd[2][0] == '\001') {
-			char **ctcp;
-
-			if (iu->f->ctcp == NULL) {
-				return;
-			}
-			if (cmd[2][strlen(cmd[2]) - 1] == '\001') {
-				cmd[2][strlen(cmd[2]) - 1] = '\0';
-			}
-
-			ctcp = split_command_parts(cmd[2] + 1, 0);
-			iu->f->ctcp(iu, ctcp);
-		} else if (iu->f->privmsg) {
-			iu->last_channel = NULL;
-			iu->f->privmsg(iu, cmd[2]);
-		}
-	} else {
-		irc_send_num(irc, 401, "%s :No such nick/channel", cmd[1]);
-	}
-}
-
-static void irc_cmd_notice(irc_t *irc, char **cmd)
-{
-	irc_user_t *iu;
-
-	if (!cmd[2]) {
-		irc_send_num(irc, 412, ":No text to send");
-		return;
-	}
-
-	/* At least for now just echo. IIRC some IRC clients use self-notices
-	   for lag checks, so try to support that. */
-	if (nick_cmp(NULL, cmd[1], irc->user->nick) == 0) {
-		irc_send_msg(irc->user, "NOTICE", irc->user->nick, cmd[2], NULL);
-	} else if ((iu = irc_user_by_name(irc, cmd[1]))) {
-		iu->f->privmsg(iu, cmd[2]);
-	}
-}
-
-static void irc_cmd_nickserv(irc_t *irc, char **cmd)
-{
-	/* [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);
-}
-
-static void irc_cmd_oper_hack(irc_t *irc, char **cmd);
-
-static void irc_cmd_oper(irc_t *irc, char **cmd)
-{
-	/* Very non-standard evil but useful/secure hack, see below. */
-	if (irc->status & OPER_HACK_ANY) {
-		return irc_cmd_oper_hack(irc, cmd);
-	}
-
-	if (global.conf->oper_pass &&
-	    (strncmp(global.conf->oper_pass, "md5:", 4) == 0 ?
-	     md5_verify_password(cmd[2], global.conf->oper_pass + 4) == 0 :
-	     strcmp(cmd[2], global.conf->oper_pass) == 0)) {
-		irc_umode_set(irc, "+o", 1);
-		irc_send_num(irc, 381, ":Password accepted");
-	} else {
-		irc_send_num(irc, 491, ":Incorrect password");
-	}
-}
-
-static void irc_cmd_oper_hack(irc_t *irc, char **cmd)
-{
-	char *password = g_strjoinv(" ", cmd + 2);
-
-	/* /OPER can now also be used to enter IM/identify passwords without
-	   echoing. It's a hack but the extra password security is worth it. */
-	if (irc->status & OPER_HACK_ACCOUNT_PASSWORD) {
-		account_t *a;
-
-		for (a = irc->b->accounts; a; a = a->next) {
-			if (strcmp(a->pass, PASSWORD_PENDING) == 0) {
-				set_setstr(&a->set, "password", password);
-				irc_rootmsg(irc, "Password added to IM account "
-				            "%s", a->tag);
-				/* The IRC client may expect this. 491 suggests the OPER
-				   password was wrong, so the client won't expect a +o.
-				   It may however repeat the password prompt. We'll see. */
-				irc_send_num(irc, 491, ":Password added to IM account "
-				             "%s", a->tag);
-			}
-		}
-	} else if (irc->status & OPER_HACK_IDENTIFY) {
-		char *send_cmd[] = { "identify", password, NULL, NULL };
-		irc->status &= ~OPER_HACK_IDENTIFY;
-		if (irc->status & OPER_HACK_IDENTIFY_NOLOAD) {
-			send_cmd[1] = "-noload";
-			send_cmd[2] = password;
-		} else if (irc->status & OPER_HACK_IDENTIFY_FORCE) {
-			send_cmd[1] = "-force";
-			send_cmd[2] = password;
-		}
-		irc_send_num(irc, 491, ":Trying to identify");
-		root_command(irc, send_cmd);
-	} else if (irc->status & OPER_HACK_REGISTER) {
-		char *send_cmd[] = { "register", password, NULL };
-		irc_send_num(irc, 491, ":Trying to identify");
-		root_command(irc, send_cmd);
-	}
-
-	irc->status &= ~OPER_HACK_ANY;
-	g_free(password);
-}
-
-static void irc_cmd_invite(irc_t *irc, char **cmd)
-{
-	irc_channel_t *ic;
-	irc_user_t *iu;
-
-	if ((iu = irc_user_by_name(irc, cmd[1])) == NULL) {
-		irc_send_num(irc, 401, "%s :No such nick", cmd[1]);
-		return;
-	} else if ((ic = irc_channel_by_name(irc, cmd[2])) == NULL) {
-		irc_send_num(irc, 403, "%s :No such channel", cmd[2]);
-		return;
-	}
-
-	if (!ic->f->invite) {
-		irc_send_num(irc, 482, "%s :Can't invite people here", cmd[2]);
-	} else if (ic->f->invite(ic, iu)) {
-		irc_send_num(irc, 341, "%s %s", iu->nick, ic->name);
-	}
-}
-
-static void irc_cmd_kick(irc_t *irc, char **cmd)
-{
-	irc_channel_t *ic;
-	irc_user_t *iu;
-
-	if ((iu = irc_user_by_name(irc, cmd[2])) == NULL) {
-		irc_send_num(irc, 401, "%s :No such nick", cmd[2]);
-		return;
-	} else if ((ic = irc_channel_by_name(irc, cmd[1])) == NULL) {
-		irc_send_num(irc, 403, "%s :No such channel", cmd[1]);
-		return;
-	} else if (!ic->f->kick) {
-		irc_send_num(irc, 482, "%s :Can't kick people here", cmd[1]);
-		return;
-	}
-
-	ic->f->kick(ic, iu, cmd[3] ? cmd[3] : NULL);
-}
-
-static void irc_cmd_userhost(irc_t *irc, char **cmd)
-{
-	int i;
-
-	/* [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++) {
-		irc_user_t *iu = irc_user_by_name(irc, cmd[i]);
-
-		if (iu) {
-			irc_send_num(irc, 302, ":%s=%c%s@%s", iu->nick,
-			             irc_user_get_away(iu) ? '-' : '+',
-			             iu->user, iu->host);
-		}
-	}
-}
-
-static void irc_cmd_ison(irc_t *irc, char **cmd)
-{
-	char buff[IRC_MAX_LINE];
-	int lenleft, i;
-
-	buff[0] = '\0';
-
-	/* [SH] Leave room for : and \0 */
-	lenleft = IRC_MAX_LINE - 2;
-
-	for (i = 1; cmd[i]; i++) {
-		char *this, *next;
-
-		this = cmd[i];
-		while (*this) {
-			irc_user_t *iu;
-
-			if ((next = strchr(this, ' '))) {
-				*next = 0;
-			}
-
-			if ((iu = irc_user_by_name(irc, this)) &&
-			    iu->bu && iu->bu->flags & BEE_USER_ONLINE) {
-				lenleft -= strlen(iu->nick) + 1;
-
-				if (lenleft < 0) {
-					break;
-				}
-
-				strcat(buff, iu->nick);
-				strcat(buff, " ");
-			}
-
-			if (next) {
-				*next = ' ';
-				this = next + 1;
-			} else {
-				break;
-			}
-		}
-
-		/* *sigh* */
-		if (lenleft < 0) {
-			break;
-		}
-	}
-
-	if (strlen(buff) > 0) {
-		buff[strlen(buff) - 1] = '\0';
-	}
-
-	irc_send_num(irc, 303, ":%s", buff);
-}
-
-static void irc_cmd_watch(irc_t *irc, char **cmd)
-{
-	int i;
-
-	/* 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;
-		irc_user_t *iu;
-
-		if (!cmd[i][0] || !cmd[i][1]) {
-			break;
-		}
-
-		nick = g_strdup(cmd[i] + 1);
-		nick_lc(irc, nick);
-
-		iu = irc_user_by_name(irc, nick);
-
-		if (cmd[i][0] == '+') {
-			if (!g_hash_table_lookup(irc->watches, nick)) {
-				g_hash_table_insert(irc->watches, nick, nick);
-			}
-
-			if (iu && iu->bu && iu->bu->flags & BEE_USER_ONLINE) {
-				irc_send_num(irc, 604, "%s %s %s %d :%s", iu->nick, iu->user,
-				             iu->host, (int) time(NULL), "is online");
-			} else {
-				irc_send_num(irc, 605, "%s %s %s %d :%s", nick, "*", "*",
-				             (int) time(NULL), "is offline");
-			}
-		} else if (cmd[i][0] == '-') {
-			gpointer okey, ovalue;
-
-			if (g_hash_table_lookup_extended(irc->watches, nick, &okey, &ovalue)) {
-				g_hash_table_remove(irc->watches, okey);
-				g_free(okey);
-
-				irc_send_num(irc, 602, "%s %s %s %d :%s", nick, "*", "*", 0, "Stopped watching");
-			}
-		}
-	}
-}
-
-static void irc_cmd_topic(irc_t *irc, char **cmd)
-{
-	irc_channel_t *ic = irc_channel_by_name(irc, cmd[1]);
-	const char *new = cmd[2];
-
-	if (ic == NULL) {
-		irc_send_num(irc, 403, "%s :No such channel", cmd[1]);
-	} else if (new) {
-		if (ic->f->topic == NULL) {
-			irc_send_num(irc, 482, "%s :Can't change this channel's topic", ic->name);
-		} else if (ic->f->topic(ic, new)) {
-			irc_send_topic(ic, TRUE);
-		}
-	} else {
-		irc_send_topic(ic, FALSE);
-	}
-}
-
-static void irc_cmd_away(irc_t *irc, char **cmd)
-{
-	if (cmd[1] && *cmd[1]) {
-		char away[strlen(cmd[1]) + 1];
-		int i, j;
-
-		/* Copy away string, but skip control chars. Mainly because
-		   Jabber really doesn't like them. */
-		for (i = j = 0; cmd[1][i]; i++) {
-			if ((unsigned char) (away[j] = cmd[1][i]) >= ' ') {
-				j++;
-			}
-		}
-		away[j] = '\0';
-
-		irc_send_num(irc, 306, ":You're now away: %s", away);
-		set_setstr(&irc->b->set, "away", away);
-	} else {
-		irc_send_num(irc, 305, ":Welcome back");
-		set_setstr(&irc->b->set, "away", NULL);
-	}
-}
-
-static void irc_cmd_list(irc_t *irc, char **cmd)
-{
-	GSList *l;
-
-	for (l = irc->channels; l; l = l->next) {
-		irc_channel_t *ic = l->data;
-
-		irc_send_num(irc, 322, "%s %d :%s",
-		             ic->name, g_slist_length(ic->users), ic->topic ? : "");
-	}
-	irc_send_num(irc, 323, ":%s", "End of /LIST");
-}
-
-static void irc_cmd_version(irc_t *irc, char **cmd)
-{
-	irc_send_num(irc, 351, "%s-%s. %s :",
-	             PACKAGE, BITLBEE_VERSION, irc->root->host);
-}
-
-static void irc_cmd_completions(irc_t *irc, char **cmd)
-{
-	help_t *h;
-	set_t *s;
-	int i;
-
-	irc_send_msg_raw(irc->root, "NOTICE", irc->user->nick, "COMPLETIONS OK");
-
-	for (i = 0; root_commands[i].command; i++) {
-		irc_send_msg_f(irc->root, "NOTICE", irc->user->nick, "COMPLETIONS %s", root_commands[i].command);
-	}
-
-	for (h = global.help; h; h = h->next) {
-		irc_send_msg_f(irc->root, "NOTICE", irc->user->nick, "COMPLETIONS help %s", h->title);
-	}
-
-	for (s = irc->b->set; s; s = s->next) {
-		irc_send_msg_f(irc->root, "NOTICE", irc->user->nick, "COMPLETIONS set %s", s->key);
-	}
-
-	irc_send_msg_raw(irc->root, "NOTICE", irc->user->nick, "COMPLETIONS END");
-}
-
-static void irc_cmd_rehash(irc_t *irc, char **cmd)
-{
-	if (global.conf->runmode == RUNMODE_INETD) {
-		ipc_master_cmd_rehash(NULL, NULL);
-	} else {
-		ipc_to_master(cmd);
-	}
-
-	irc_send_num(irc, 382, "%s :Rehashing", global.conf_file);
-}
-
-static const command_t irc_commands[] = {
-	{ "cap",         1, irc_cmd_cap,         0 },
-	{ "pass",        1, irc_cmd_pass,        0 },
-	{ "proxy",       5, irc_cmd_proxy,       IRC_CMD_PRE_LOGIN },
-	{ "user",        4, irc_cmd_user,        IRC_CMD_PRE_LOGIN },
-	{ "nick",        1, irc_cmd_nick,        0 },
-	{ "quit",        0, irc_cmd_quit,        0 },
-	{ "ping",        0, irc_cmd_ping,        0 },
-	{ "pong",        0, irc_cmd_pong,        IRC_CMD_LOGGED_IN },
-	{ "join",        1, irc_cmd_join,        IRC_CMD_LOGGED_IN },
-	{ "names",       1, irc_cmd_names,       IRC_CMD_LOGGED_IN },
-	{ "part",        1, irc_cmd_part,        IRC_CMD_LOGGED_IN },
-	{ "whois",       1, irc_cmd_whois,       IRC_CMD_LOGGED_IN },
-	{ "whowas",      1, irc_cmd_whowas,      IRC_CMD_LOGGED_IN },
-	{ "motd",        0, irc_cmd_motd,        IRC_CMD_LOGGED_IN },
-	{ "mode",        1, irc_cmd_mode,        IRC_CMD_LOGGED_IN },
-	{ "who",         0, irc_cmd_who,         IRC_CMD_LOGGED_IN },
-	{ "privmsg",     1, irc_cmd_privmsg,     IRC_CMD_LOGGED_IN },
-	{ "notice",      1, irc_cmd_notice,      IRC_CMD_LOGGED_IN },
-	{ "nickserv",    1, irc_cmd_nickserv,    IRC_CMD_LOGGED_IN },
-	{ "ns",          1, irc_cmd_nickserv,    IRC_CMD_LOGGED_IN },
-	{ "away",        0, irc_cmd_away,        IRC_CMD_LOGGED_IN },
-	{ "version",     0, irc_cmd_version,     IRC_CMD_LOGGED_IN },
-	{ "completions", 0, irc_cmd_completions, IRC_CMD_LOGGED_IN },
-	{ "userhost",    1, irc_cmd_userhost,    IRC_CMD_LOGGED_IN },
-	{ "ison",        1, irc_cmd_ison,        IRC_CMD_LOGGED_IN },
-	{ "watch",       1, irc_cmd_watch,       IRC_CMD_LOGGED_IN },
-	{ "invite",      2, irc_cmd_invite,      IRC_CMD_LOGGED_IN },
-	{ "kick",        2, irc_cmd_kick,        IRC_CMD_LOGGED_IN },
-	{ "topic",       1, irc_cmd_topic,       IRC_CMD_LOGGED_IN },
-	{ "oper",        2, irc_cmd_oper,        IRC_CMD_LOGGED_IN },
-	{ "list",        0, irc_cmd_list,        IRC_CMD_LOGGED_IN },
-	{ "die",         0, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
-	{ "deaf",        0, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
-	{ "wallops",     1, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
-	{ "wall",        1, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
-	{ "rehash",      0, irc_cmd_rehash,      IRC_CMD_OPER_ONLY },
-	{ "restart",     0, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
-	{ "kill",        2, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
-	{ "authenticate", 1, irc_cmd_authenticate, 0 },
-	{ NULL }
-};
-
-void irc_exec(irc_t *irc, char *cmd[])
-{
-	int i, n_arg;
-
-	if (!cmd[0]) {
-		return;
-	}
-
-	for (i = 0; irc_commands[i].command; i++) {
-		if (g_strcasecmp(irc_commands[i].command, cmd[0]) == 0) {
-			/* There should be no typo in the next line: */
-			for (n_arg = 0; cmd[n_arg]; n_arg++) {
-				;
-			}
-			n_arg--;
-
-			if (irc_commands[i].flags & IRC_CMD_PRE_LOGIN && irc->status & USTATUS_LOGGED_IN) {
-				irc_send_num(irc, 462, ":Only allowed before logging in");
-			} else if (irc_commands[i].flags & IRC_CMD_LOGGED_IN && !(irc->status & USTATUS_LOGGED_IN)) {
-				irc_send_num(irc, 451, ":Register first");
-			} else if (irc_commands[i].flags & IRC_CMD_OPER_ONLY && !strchr(irc->umode, 'o')) {
-				irc_send_num(irc, 481, ":Permission denied - You're not an IRC operator");
-			} else if (n_arg < irc_commands[i].required_parameters) {
-				irc_send_num(irc, 461, "%s :Need more parameters", cmd[0]);
-			} else if (irc_commands[i].flags & IRC_CMD_TO_MASTER) {
-				/* IPC doesn't make sense in inetd mode,
-				    but the function will catch that. */
-				ipc_to_master(cmd);
-			} else {
-				irc_commands[i].execute(irc, cmd);
-			}
-
-			return;
-		}
-	}
-
-	if (irc->status & USTATUS_LOGGED_IN) {
-		irc_send_num(irc, 421, "%s :Unknown command", cmd[0]);
-	}
-}
Index: c_im.c
===================================================================
--- irc_im.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,1180 +1,0 @@
-/********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2012 Wilmer van der Gaast and others                *
-  \********************************************************************/
-
-/* Some glue to put the IRC and the IM stuff together.                  */
-
-/*
-  This program is free software; you can 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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#include "bitlbee.h"
-#include "dcc.h"
-
-/* IM->IRC callbacks: Simple IM/buddy-related stuff. */
-
-static const struct irc_user_funcs irc_user_im_funcs;
-
-static void bee_irc_imc_connected(struct im_connection *ic)
-{
-	irc_t *irc = (irc_t *) ic->bee->ui_data;
-
-	irc_channel_auto_joins(irc, ic->acc);
-}
-
-static void bee_irc_imc_disconnected(struct im_connection *ic)
-{
-	/* Maybe try to send /QUITs here instead of later on. */
-}
-
-static gboolean bee_irc_user_new(bee_t *bee, bee_user_t *bu)
-{
-	irc_user_t *iu;
-	irc_t *irc = (irc_t *) bee->ui_data;
-	char nick[MAX_NICK_LENGTH + 1], *s;
-
-	memset(nick, 0, MAX_NICK_LENGTH + 1);
-	strncpy(nick, nick_get(bu), MAX_NICK_LENGTH);
-
-	bu->ui_data = iu = irc_user_new(irc, nick);
-	iu->bu = bu;
-
-	if (set_getbool(&irc->b->set, "private")) {
-		iu->last_channel = NULL;
-	} else {
-		iu->last_channel = irc_channel_with_user(irc, iu);
-	}
-
-	if ((s = strchr(bu->handle, '@'))) {
-		iu->host = g_strdup(s + 1);
-		iu->user = g_strndup(bu->handle, s - bu->handle);
-	} else {
-		iu->user = g_strdup(bu->handle);
-		if (bu->ic->acc->server) {
-			iu->host = g_strdup(bu->ic->acc->server);
-		} else {
-			char *s;
-			for (s = bu->ic->acc->tag; g_ascii_isalnum(*s); s++) {
-				;
-			}
-			/* Only use the tag if we got to the end of the string.
-			   (So allow alphanumerics only. Hopefully not too
-			   restrictive.) */
-			if (*s) {
-				iu->host = g_strdup(bu->ic->acc->prpl->name);
-			} else {
-				iu->host = g_strdup(bu->ic->acc->tag);
-			}
-		}
-	}
-
-	/* Sanitize */
-	str_reject_chars(iu->user, " ", '_');
-	str_reject_chars(iu->host, " ", '_');
-
-	if (bu->flags & BEE_USER_LOCAL) {
-		char *s = set_getstr(&bu->ic->acc->set, "handle_unknown") ? :
-		          set_getstr(&bee->set, "handle_unknown");
-
-		if (g_strcasecmp(s, "add_private") == 0) {
-			iu->last_channel = NULL;
-		} else if (g_strcasecmp(s, "add_channel") == 0) {
-			iu->last_channel = irc->default_channel;
-		}
-	}
-
-	iu->f = &irc_user_im_funcs;
-
-	return TRUE;
-}
-
-static gboolean bee_irc_user_free(bee_t *bee, bee_user_t *bu)
-{
-	return irc_user_free(bee->ui_data, (irc_user_t *) bu->ui_data);
-}
-
-static gboolean bee_irc_user_status(bee_t *bee, bee_user_t *bu, bee_user_t *old)
-{
-	irc_t *irc = bee->ui_data;
-	irc_user_t *iu = bu->ui_data;
-
-	/* Do this outside the if below since away state can change without
-	   the online state changing. */
-	iu->flags &= ~IRC_USER_AWAY;
-	if (bu->flags & BEE_USER_AWAY || !(bu->flags & BEE_USER_ONLINE)) {
-		iu->flags |= IRC_USER_AWAY;
-	}
-
-	/* return early if nothing changed */
-	if ((bu->flags == old->flags) &&
-	    (g_strcmp0(bu->status, old->status) == 0) &&
-	    (g_strcmp0(bu->status_msg, old->status_msg) == 0)) {
-		return TRUE;
-	}
-
-	if ((bu->flags & BEE_USER_ONLINE) != (old->flags & BEE_USER_ONLINE)) {
-		if (bu->flags & BEE_USER_ONLINE) {
-			if (g_hash_table_lookup(irc->watches, iu->key)) {
-				irc_send_num(irc, 600, "%s %s %s %d :%s", iu->nick, iu->user,
-				             iu->host, (int) time(NULL), "logged online");
-			}
-		} else {
-			if (g_hash_table_lookup(irc->watches, iu->key)) {
-				irc_send_num(irc, 601, "%s %s %s %d :%s", iu->nick, iu->user,
-				             iu->host, (int) time(NULL), "logged offline");
-			}
-
-			/* Send a QUIT since those will also show up in any
-			   query windows the user may have, plus it's only
-			   one QUIT instead of possibly many (in case of
-			   multiple control chans). If there's a channel that
-			   shows offline people, a JOIN will follow. */
-			if (set_getbool(&bee->set, "offline_user_quits")) {
-				irc_user_quit(iu, "Leaving...");
-			}
-		}
-	}
-
-	iu->away_reply_timeout = 0;
-
-	bee_irc_channel_update(irc, NULL, iu);
-
-	if (irc->caps & CAP_AWAY_NOTIFY) {
-		irc_send_away_notify(iu);
-	}
-
-	return TRUE;
-}
-
-void bee_irc_channel_update(irc_t *irc, irc_channel_t *ic, irc_user_t *iu)
-{
-	GSList *l;
-
-	if (ic == NULL) {
-		for (l = irc->channels; l; l = l->next) {
-			ic = l->data;
-			/* TODO: Just add a type flag or so.. */
-			if (ic->f == irc->default_channel->f &&
-			    (ic->flags & IRC_CHANNEL_JOINED)) {
-				bee_irc_channel_update(irc, ic, iu);
-			}
-		}
-		return;
-	}
-	if (iu == NULL) {
-		GHashTableIter iter;
-		gpointer itervalue;
-		g_hash_table_iter_init(&iter, irc->nick_user_hash);
-
-		while (g_hash_table_iter_next(&iter, NULL, &itervalue)) {
-			iu = itervalue;
-			if (iu->bu) {
-				bee_irc_channel_update(irc, ic, iu);
-			}
-		}
-		return;
-	}
-
-	if (!irc_channel_wants_user(ic, iu)) {
-		irc_channel_del_user(ic, iu, IRC_CDU_PART, NULL);
-	} else {
-		struct irc_control_channel *icc = ic->data;
-		int mode = 0;
-
-		if (!(iu->bu->flags & BEE_USER_ONLINE)) {
-			mode = icc->modes[0];
-		} else if (iu->bu->flags & BEE_USER_AWAY) {
-			mode = icc->modes[1];
-		} else if (iu->bu->flags & BEE_USER_SPECIAL) {
-			mode = icc->modes[2];
-		} else {
-			mode = icc->modes[3];
-		}
-
-		if (!mode) {
-			irc_channel_del_user(ic, iu, IRC_CDU_PART, NULL);
-		} else {
-			irc_channel_add_user(ic, iu);
-			irc_channel_user_set_mode(ic, iu, mode);
-		}
-	}
-}
-
-static gboolean bee_irc_user_msg(bee_t *bee, bee_user_t *bu, const char *msg_, guint32 flags, time_t sent_at)
-{
-	irc_t *irc = bee->ui_data;
-	irc_user_t *iu = (irc_user_t *) bu->ui_data;
-	irc_user_t *src_iu = iu;
-	irc_user_t *dst_iu = irc->user;
-	const char *dst;
-	char *prefix = NULL;
-	char *wrapped, *ts = NULL;
-	char *msg = g_strdup(msg_);
-	char *message_type = "PRIVMSG";
-	GSList *l;
-
-	if (sent_at > 0 &&
-	    !(irc->caps & CAP_SERVER_TIME) &&
-	    set_getbool(&irc->b->set, "display_timestamps")) {
-		ts = irc_format_timestamp(irc, sent_at);
-	}
-
-	dst = irc_user_msgdest(iu);
-
-	if (flags & OPT_SELFMESSAGE) {
-		char *setting = set_getstr(&irc->b->set, "self_messages");
-
-		if (is_bool(setting)) {
-			if (bool2int(setting)) {
-				/* set to true, send it with src/dst flipped */
-				
-				dst_iu = iu;
-				src_iu = irc->user;
-
-				if (dst == irc->user->nick) {
-					dst = dst_iu->nick;
-				}
-			} else {
-				/* set to false, skip the message completely */
-				goto cleanup;
-			}
-		} else if (g_strncasecmp(setting, "prefix", 6) == 0) {
-			/* third state, prefix, loosely imitates the znc privmsg_prefix module */
-
-			g_free(msg);
-			if (g_strncasecmp(msg_, "/me ", 4) == 0) {
-				msg = g_strdup_printf("/me -> %s", msg_ + 4);
-			} else {
-				msg = g_strdup_printf("-> %s", msg_);
-			}
-
-			if (g_strcasecmp(setting, "prefix_notice") == 0) {
-				message_type = "NOTICE";
-			}
-		}
-
-	}
-
-	if (dst != dst_iu->nick) {
-		/* if not messaging directly (control channel), call user by name */
-		prefix = g_strdup_printf("%s%s%s", dst_iu->nick, set_getstr(&bee->set, "to_char"), ts ? : "");
-	} else {
-		prefix = ts;
-		ts = NULL;      /* don't double-free */
-	}
-
-	for (l = irc_plugins; l; l = l->next) {
-		irc_plugin_t *p = l->data;
-		if (p->filter_msg_in) {
-			char *s = p->filter_msg_in(iu, msg, 0);
-			if (s) {
-				if (s != msg) {
-					g_free(msg);
-				}
-				msg = s;
-			} else {
-				/* Modules can swallow messages. */
-				goto cleanup;
-			}
-		}
-	}
-
-	if ((g_strcasecmp(set_getstr(&bee->set, "strip_html"), "always") == 0) ||
-	    ((bu->ic->flags & OPT_DOES_HTML) && set_getbool(&bee->set, "strip_html"))) {
-		char *s = g_strdup(msg);
-		strip_html(s);
-		g_free(msg);
-		msg = s;
-	}
-
-	wrapped = word_wrap(msg, IRC_WORD_WRAP);
-	irc_send_msg_ts(src_iu, message_type, dst, wrapped, prefix, sent_at);
-	g_free(wrapped);
-
-cleanup:
-	g_free(prefix);
-	g_free(msg);
-	g_free(ts);
-
-	return TRUE;
-}
-
-static gboolean bee_irc_user_typing(bee_t *bee, bee_user_t *bu, guint32 flags)
-{
-	irc_t *irc = (irc_t *) bee->ui_data;
-
-	if (set_getbool(&bee->set, "typing_notice")) {
-		irc_send_msg_f((irc_user_t *) bu->ui_data, "PRIVMSG", irc->user->nick,
-		               "\001TYPING %d\001", (flags >> 8) & 3);
-	} else {
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-static gboolean bee_irc_user_action_response(bee_t *bee, bee_user_t *bu, const char *action, char * const args[],
-                                             void *data)
-{
-	irc_t *irc = (irc_t *) bee->ui_data;
-	GString *msg = g_string_new("\001");
-
-	g_string_append(msg, action);
-	while (*args) {
-		if (strchr(*args, ' ')) {
-			g_string_append_printf(msg, " \"%s\"", *args);
-		} else {
-			g_string_append_printf(msg, " %s", *args);
-		}
-		args++;
-	}
-	g_string_append_c(msg, '\001');
-
-	irc_send_msg((irc_user_t *) bu->ui_data, "NOTICE", irc->user->nick, msg->str, NULL);
-
-	g_string_free(msg, TRUE);
-
-	return TRUE;
-}
-
-static gboolean bee_irc_user_nick_update(irc_user_t *iu, gboolean offline_only);
-
-static gboolean bee_irc_user_fullname(bee_t *bee, bee_user_t *bu)
-{
-	irc_user_t *iu = (irc_user_t *) bu->ui_data;
-	char *s;
-
-	if (iu->fullname != iu->nick) {
-		g_free(iu->fullname);
-	}
-	iu->fullname = g_strdup(bu->fullname);
-
-	/* Strip newlines (unlikely, but IRC-unfriendly so they must go)
-	   TODO(wilmer): Do the same with away msgs again! */
-	for (s = iu->fullname; *s; s++) {
-		if (g_ascii_isspace(*s)) {
-			*s = ' ';
-		}
-	}
-
-	if ((bu->ic->flags & OPT_LOGGED_IN) && set_getbool(&bee->set, "display_namechanges")) {
-		/* People don't like this /NOTICE. Meh, let's go back to the old one.
-		char *msg = g_strdup_printf( "<< \002BitlBee\002 - Changed name to `%s' >>", iu->fullname );
-		irc_send_msg( iu, "NOTICE", irc->user->nick, msg, NULL );
-		*/
-		imcb_log(bu->ic, "User `%s' changed name to `%s'", iu->nick, iu->fullname);
-	}
-
-	bee_irc_user_nick_update(iu, TRUE);
-
-	return TRUE;
-}
-
-static gboolean bee_irc_user_nick_hint(bee_t *bee, bee_user_t *bu, const char *hint)
-{
-	bee_irc_user_nick_update((irc_user_t *) bu->ui_data, TRUE);
-
-	return TRUE;
-}
-
-static gboolean bee_irc_user_nick_change(bee_t *bee, bee_user_t *bu, const char *nick)
-{
-	bee_irc_user_nick_update((irc_user_t *) bu->ui_data, FALSE);
-
-	return TRUE;
-}
-
-static gboolean bee_irc_user_group(bee_t *bee, bee_user_t *bu)
-{
-	irc_user_t *iu = (irc_user_t *) bu->ui_data;
-	irc_t *irc = (irc_t *) bee->ui_data;
-
-	bee_irc_channel_update(irc, NULL, iu);
-	bee_irc_user_nick_update(iu, FALSE);
-
-	return TRUE;
-}
-
-static gboolean bee_irc_user_nick_update(irc_user_t *iu, gboolean offline_only)
-{
-	bee_user_t *bu = iu->bu;
-	char *newnick;
-
-	if (offline_only && bu->flags & BEE_USER_ONLINE) {
-		/* Ignore if the user is visible already. */
-		return TRUE;
-	}
-
-	if (nick_saved(bu)) {
-		/* The user already assigned a nickname to this person. */
-		return TRUE;
-	}
-
-	newnick = nick_get(bu);
-
-	if (strcmp(iu->nick, newnick) != 0) {
-		nick_dedupe(bu, newnick);
-		irc_user_set_nick(iu, newnick);
-	}
-
-	return TRUE;
-}
-
-void bee_irc_user_nick_reset(irc_user_t *iu)
-{
-	bee_user_t *bu = iu->bu;
-
-	if (bu == FALSE) {
-		return;
-	}
-
-	nick_del(bu);
-	bee_irc_user_nick_update(iu, FALSE);
-
-}
-
-#define PASTEBUF_LONG_SPACELESS_LINE_LENGTH 350
-
-/* Returns FALSE if the last line of the message is near the typical irc length
- * limit and the message has no spaces, indicating that it's probably desirable
- * to join messages without the newline.
- *
- * The main use case for this is pasting long URLs and not breaking them */
-static gboolean bee_irc_pastebuf_should_start_with_newline(const char *msg)
-{
-	int i;
-	const char *last_line = strrchr(msg, '\n') ? : msg;
-
-	if (*last_line == '\n') {
-		last_line++;
-	}
-
-	for (i = 0; last_line[i]; i++) {
-		if (g_ascii_isspace(last_line[i])) {
-			return TRUE;
-		}
-	}
-
-	if (i < PASTEBUF_LONG_SPACELESS_LINE_LENGTH) {
-		return TRUE;
-	}
-
-	return FALSE;
-}
-
-/* IRC->IM calls */
-
-static gboolean bee_irc_user_privmsg_cb(gpointer data, gint fd, b_input_condition cond);
-
-static gboolean bee_irc_user_privmsg(irc_user_t *iu, const char *msg)
-{
-	const char *away;
-
-	if (iu->bu == NULL) {
-		return FALSE;
-	}
-
-	if (iu->last_channel == NULL &&
-	    (away = irc_user_get_away(iu)) &&
-	    time(NULL) >= iu->away_reply_timeout) {
-		irc_send_num(iu->irc, 301, "%s :%s", iu->nick, away);
-		iu->away_reply_timeout = time(NULL) +
-		                         set_getint(&iu->irc->b->set, "away_reply_timeout");
-	}
-
-	if (iu->pastebuf == NULL) {
-		iu->pastebuf = g_string_new(msg);
-	} else {
-		b_event_remove(iu->pastebuf_timer);
-		if (bee_irc_pastebuf_should_start_with_newline(iu->pastebuf->str)) {
-			g_string_append_c(iu->pastebuf, '\n');
-		}
-		g_string_append(iu->pastebuf, msg);
-	}
-
-	if (set_getbool(&iu->irc->b->set, "paste_buffer")) {
-		int delay;
-
-		if ((delay = set_getint(&iu->irc->b->set, "paste_buffer_delay")) <= 5) {
-			delay *= 1000;
-		}
-
-		iu->pastebuf_timer = b_timeout_add(delay, bee_irc_user_privmsg_cb, iu);
-
-		return TRUE;
-	} else {
-		bee_irc_user_privmsg_cb(iu, 0, 0);
-
-		return TRUE;
-	}
-}
-
-static gboolean bee_irc_user_privmsg_cb(gpointer data, gint fd, b_input_condition cond)
-{
-	irc_user_t *iu = data;
-	char *msg;
-	GSList *l;
-
-	msg = g_string_free(iu->pastebuf, FALSE);
-	iu->pastebuf = NULL;
-	iu->pastebuf_timer = 0;
-
-	for (l = irc_plugins; l; l = l->next) {
-		irc_plugin_t *p = l->data;
-		if (p->filter_msg_out) {
-			char *s = p->filter_msg_out(iu, msg, 0);
-			if (s) {
-				if (s != msg) {
-					g_free(msg);
-				}
-				msg = s;
-			} else {
-				/* Modules can swallow messages. */
-				iu->pastebuf = NULL;
-				g_free(msg);
-				return FALSE;
-			}
-		}
-	}
-
-	bee_user_msg(iu->irc->b, iu->bu, msg, 0);
-
-	g_free(msg);
-
-	return FALSE;
-}
-
-static gboolean bee_irc_user_ctcp(irc_user_t *iu, char *const *ctcp)
-{
-	if (ctcp[1] && g_strcasecmp(ctcp[0], "DCC") == 0
-	    && g_strcasecmp(ctcp[1], "SEND") == 0) {
-		if (iu->bu && iu->bu->ic && iu->bu->ic->acc->prpl->transfer_request) {
-			file_transfer_t *ft = dcc_request(iu->bu->ic, ctcp);
-			if (ft) {
-				iu->bu->ic->acc->prpl->transfer_request(iu->bu->ic, ft, iu->bu->handle);
-			}
-
-			return TRUE;
-		}
-	} else if (g_strcasecmp(ctcp[0], "TYPING") == 0) {
-		if (iu->bu && iu->bu->ic && iu->bu->ic->acc->prpl->send_typing && ctcp[1]) {
-			int st = ctcp[1][0];
-			if (st >= '0' && st <= '2') {
-				st <<= 8;
-				iu->bu->ic->acc->prpl->send_typing(iu->bu->ic, iu->bu->handle, st);
-			}
-
-			return TRUE;
-		}
-	} else if (g_strcasecmp(ctcp[0], "HELP") == 0 && iu->bu) {
-		GString *supp = g_string_new("Supported CTCPs:");
-		GList *l;
-
-		if (iu->bu->ic && iu->bu->ic->acc->prpl->transfer_request) {
-			g_string_append(supp, " DCC SEND,");
-		}
-		if (iu->bu->ic && iu->bu->ic->acc->prpl->send_typing) {
-			g_string_append(supp, " TYPING,");
-		}
-		if (iu->bu->ic->acc->prpl->buddy_action_list) {
-			for (l = iu->bu->ic->acc->prpl->buddy_action_list(iu->bu); l; l = l->next) {
-				struct buddy_action *ba = l->data;
-				g_string_append_printf(supp, " %s (%s),",
-				                       ba->name, ba->description);
-			}
-		}
-		g_string_truncate(supp, supp->len - 1);
-		irc_send_msg_f(iu, "NOTICE", iu->irc->user->nick, "\001HELP %s\001", supp->str);
-		g_string_free(supp, TRUE);
-	} else if (iu->bu && iu->bu->ic && iu->bu->ic->acc->prpl->buddy_action) {
-		iu->bu->ic->acc->prpl->buddy_action(iu->bu, ctcp[0], ctcp + 1, NULL);
-	}
-
-	return FALSE;
-}
-
-static const struct irc_user_funcs irc_user_im_funcs = {
-	bee_irc_user_privmsg,
-	bee_irc_user_ctcp,
-};
-
-
-/* IM->IRC: Groupchats */
-const struct irc_channel_funcs irc_channel_im_chat_funcs;
-
-static gboolean bee_irc_chat_new(bee_t *bee, struct groupchat *c)
-{
-	irc_t *irc = bee->ui_data;
-	irc_channel_t *ic;
-	char *topic;
-	GSList *l;
-	int i;
-
-	/* Try to find a channel that expects to receive a groupchat.
-	   This flag is set earlier in our current call trace. */
-	for (l = irc->channels; l; l = l->next) {
-		ic = l->data;
-		if (ic->flags & IRC_CHANNEL_CHAT_PICKME) {
-			break;
-		}
-	}
-
-	/* If we found none, just generate some stupid name. */
-	if (l == NULL) {
-		for (i = 0; i <= 999; i++) {
-			char name[16];
-			sprintf(name, "#chat_%03d", i);
-			if ((ic = irc_channel_new(irc, name))) {
-				break;
-			}
-		}
-	}
-
-	if (ic == NULL) {
-		return FALSE;
-	}
-
-	c->ui_data = ic;
-	ic->data = c;
-
-	topic = g_strdup_printf(
-	        "BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!",
-	        c->title);
-	irc_channel_set_topic(ic, topic, irc->root);
-	g_free(topic);
-
-	return TRUE;
-}
-
-static gboolean bee_irc_chat_free(bee_t *bee, struct groupchat *c)
-{
-	irc_channel_t *ic = c->ui_data;
-
-	if (ic == NULL) {
-		return FALSE;
-	}
-
-	ic->data = NULL;
-	c->ui_data = NULL;
-	irc_channel_del_user(ic, ic->irc->user, IRC_CDU_KICK, "Chatroom closed by server");
-
-	return TRUE;
-}
-
-static gboolean bee_irc_chat_log(bee_t *bee, struct groupchat *c, const char *text)
-{
-	irc_channel_t *ic = c->ui_data;
-
-	if (ic == NULL) {
-		return FALSE;
-	}
-
-	irc_channel_printf(ic, "%s", text);
-
-	return TRUE;
-}
-
-static gboolean bee_irc_chat_msg(bee_t *bee, struct groupchat *c, bee_user_t *bu, const char *msg, guint32 flags, time_t sent_at)
-{
-	irc_t *irc = bee->ui_data;
-	irc_user_t *iu = flags & OPT_SELFMESSAGE ? irc->user : bu->ui_data;
-	irc_channel_t *ic = c->ui_data;
-	char *wrapped, *ts = NULL;
-
-	if (ic == NULL) {
-		return FALSE;
-	}
-
-	if (sent_at > 0 &&
-	    !(irc->caps & CAP_SERVER_TIME) &&
-	    set_getbool(&bee->set, "display_timestamps")) {
-		ts = irc_format_timestamp(irc, sent_at);
-	}
-
-	wrapped = word_wrap(msg, IRC_WORD_WRAP);
-	irc_send_msg_ts(iu, "PRIVMSG", ic->name, wrapped, ts, sent_at);
-	g_free(ts);
-	g_free(wrapped);
-
-	return TRUE;
-}
-
-static gboolean bee_irc_chat_add_user(bee_t *bee, struct groupchat *c, bee_user_t *bu)
-{
-	irc_t *irc = bee->ui_data;
-	irc_channel_t *ic = c->ui_data;
-
-	if (ic == NULL) {
-		return FALSE;
-	}
-
-	irc_channel_add_user(ic, bu == bee->user ? irc->user : bu->ui_data);
-
-	return TRUE;
-}
-
-static gboolean bee_irc_chat_remove_user(bee_t *bee, struct groupchat *c, bee_user_t *bu, const char *reason)
-{
-	irc_t *irc = bee->ui_data;
-	irc_channel_t *ic = c->ui_data;
-
-	if (ic == NULL || bu == NULL) {
-		return FALSE;
-	}
-
-	/* TODO: Possible bug here: If a module removes $user here instead of just
-	   using imcb_chat_free() and the channel was IRC_CHANNEL_TEMP, we get into
-	   a broken state around here. */
-	irc_channel_del_user(ic, bu == bee->user ? irc->user : bu->ui_data, IRC_CDU_PART, reason);
-
-	return TRUE;
-}
-
-static gboolean bee_irc_chat_topic(bee_t *bee, struct groupchat *c, const char *new, bee_user_t *bu)
-{
-	irc_channel_t *ic = c->ui_data;
-	irc_t *irc = bee->ui_data;
-	irc_user_t *iu;
-
-	if (ic == NULL) {
-		return FALSE;
-	}
-
-	if (bu == NULL) {
-		iu = irc->root;
-	} else if (bu == bee->user) {
-		iu = irc->user;
-	} else {
-		iu = bu->ui_data;
-	}
-
-	irc_channel_set_topic(ic, new, iu);
-
-	return TRUE;
-}
-
-static gboolean bee_irc_chat_name_hint(bee_t *bee, struct groupchat *c, const char *name)
-{
-	return irc_channel_name_hint(c->ui_data, name);
-}
-
-static gboolean bee_irc_chat_invite(bee_t *bee, bee_user_t *bu, const char *name, const char *msg)
-{
-	char *channel, *s;
-	irc_t *irc = bee->ui_data;
-	irc_user_t *iu = bu->ui_data;
-	irc_channel_t *chan;
-
-	if (strchr(CTYPES, name[0])) {
-		channel = g_strdup(name);
-	} else {
-		channel = g_strdup_printf("#%s", name);
-	}
-
-	if ((s = strchr(channel, '@'))) {
-		*s = '\0';
-	}
-
-	if (strlen(channel) > MAX_NICK_LENGTH) {
-		/* If the channel name is very long (like those insane GTalk
-		   UUID names), try if we can use the inviter's nick. */
-		s = g_strdup_printf("#%s", iu->nick);
-		if (irc_channel_by_name(irc, s) == NULL) {
-			g_free(channel);
-			channel = s;
-		} else {
-			g_free(s);
-		}
-	}
-
-	if ((chan = irc_channel_new(irc, channel)) &&
-	    set_setstr(&chan->set, "type", "chat") &&
-	    set_setstr(&chan->set, "chat_type", "room") &&
-	    set_setstr(&chan->set, "account", bu->ic->acc->tag) &&
-	    set_setstr(&chan->set, "room", (char *) name)) {
-		/* I'm assuming that if the user didn't "chat add" the room
-		   himself but got invited, it's temporary, so make this a
-		   temporary mapping that is removed as soon as we /PART. */
-		chan->flags |= IRC_CHANNEL_TEMP;
-	} else {
-		irc_channel_free(chan);
-		chan = NULL;
-	}
-	g_free(channel);
-
-	irc_send_msg_f(iu, "PRIVMSG", irc->user->nick, "<< \002BitlBee\002 - Invitation to chatroom %s >>", name);
-	if (msg) {
-		irc_send_msg(iu, "PRIVMSG", irc->user->nick, msg, NULL);
-	}
-	if (chan) {
-		irc_send_msg_f(iu, "PRIVMSG", irc->user->nick, "To join the room, just /join %s", chan->name);
-		irc_send_invite(iu, chan);
-	}
-
-	return TRUE;
-}
-
-/* IRC->IM */
-static gboolean bee_irc_channel_chat_privmsg_cb(gpointer data, gint fd, b_input_condition cond);
-
-static gboolean bee_irc_channel_chat_privmsg(irc_channel_t *ic, const char *msg)
-{
-	struct groupchat *c = ic->data;
-	char *trans = NULL, *s;
-
-	if (c == NULL) {
-		return FALSE;
-	}
-
-	if (set_getbool(&ic->set, "translate_to_nicks")) {
-		char nick[MAX_NICK_LENGTH + 1];
-		irc_user_t *iu;
-
-		strncpy(nick, msg, MAX_NICK_LENGTH);
-		nick[MAX_NICK_LENGTH] = '\0';
-		if ((s = strchr(nick, ':')) || (s = strchr(nick, ','))) {
-			*s = '\0';
-			if ((iu = irc_user_by_name(ic->irc, nick)) && iu->bu &&
-			    iu->bu->nick && irc_channel_has_user(ic, iu)) {
-				trans = g_strconcat(iu->bu->nick, msg + (s - nick), NULL);
-				msg = trans;
-			}
-		}
-	}
-
-	if (set_getbool(&ic->irc->b->set, "paste_buffer")) {
-		int delay;
-
-		if (ic->pastebuf == NULL) {
-			ic->pastebuf = g_string_new(msg);
-		} else {
-			b_event_remove(ic->pastebuf_timer);
-			g_string_append_printf(ic->pastebuf, "\n%s", msg);
-		}
-
-		if ((delay = set_getint(&ic->irc->b->set, "paste_buffer_delay")) <= 5) {
-			delay *= 1000;
-		}
-
-		ic->pastebuf_timer = b_timeout_add(delay, bee_irc_channel_chat_privmsg_cb, ic);
-
-		g_free(trans);
-		return TRUE;
-	} else {
-		bee_chat_msg(ic->irc->b, c, msg, 0);
-	}
-
-	g_free(trans);
-	return TRUE;
-}
-
-static gboolean bee_irc_channel_chat_privmsg_cb(gpointer data, gint fd, b_input_condition cond)
-{
-	irc_channel_t *ic = data;
-
-	if (ic->data) {
-		bee_chat_msg(ic->irc->b, ic->data, ic->pastebuf->str, 0);
-	}
-
-	g_string_free(ic->pastebuf, TRUE);
-	ic->pastebuf = 0;
-	ic->pastebuf_timer = 0;
-
-	return FALSE;
-}
-
-static gboolean bee_irc_channel_chat_join(irc_channel_t *ic)
-{
-	char *acc_s, *room;
-	account_t *acc;
-
-	if (strcmp(set_getstr(&ic->set, "chat_type"), "room") != 0) {
-		return TRUE;
-	}
-
-	if ((acc_s = set_getstr(&ic->set, "account")) &&
-	    (room = set_getstr(&ic->set, "room")) &&
-	    (acc = account_get(ic->irc->b, acc_s)) &&
-	    acc->ic && (acc->ic->flags & OPT_LOGGED_IN) &&
-	    acc->prpl->chat_join) {
-		char *nick;
-		struct groupchat *gc;
-
-		if (!(nick = set_getstr(&ic->set, "nick"))) {
-			nick = ic->irc->user->nick;
-		}
-
-		ic->flags |= IRC_CHANNEL_CHAT_PICKME;
-		gc = acc->prpl->chat_join(acc->ic, room, nick, NULL, &ic->set);
-		ic->flags &= ~IRC_CHANNEL_CHAT_PICKME;
-
-		if (!gc) {
-			irc_send_num(ic->irc, 403, "%s :Error joining channel (check control channel?)", ic->name);
-		}
-
-		return FALSE;
-	} else {
-		irc_send_num(ic->irc, 403, "%s :Can't join channel, account offline?", ic->name);
-		return FALSE;
-	}
-}
-
-static gboolean bee_irc_channel_chat_part(irc_channel_t *ic, const char *msg)
-{
-	struct groupchat *c = ic->data;
-
-	if (c && c->ic->acc->prpl->chat_leave) {
-		c->ic->acc->prpl->chat_leave(c);
-	}
-
-	if (!(ic->flags & IRC_CHANNEL_TEMP)) {
-		/* Remove the reference.
-		 * We only need it for temp channels that are being freed */
-		ic->data = NULL;
-	}
-
-	return TRUE;
-}
-
-static gboolean bee_irc_channel_chat_topic(irc_channel_t *ic, const char *new)
-{
-	struct groupchat *c = ic->data;
-
-	if (c == NULL) {
-		return FALSE;
-	}
-
-	if (c->ic->acc->prpl->chat_topic == NULL) {
-		irc_send_num(ic->irc, 482, "%s :IM network does not support channel topics", ic->name);
-	} else {
-		/* TODO: Need more const goodness here, sigh */
-		char *topic = g_strdup(new);
-		c->ic->acc->prpl->chat_topic(c, topic);
-		g_free(topic);
-	}
-
-	/* Whatever happened, the IM module should ack the topic change. */
-	return FALSE;
-}
-
-static gboolean bee_irc_channel_chat_invite(irc_channel_t *ic, irc_user_t *iu)
-{
-	struct groupchat *c = ic->data;
-	bee_user_t *bu = iu->bu;
-
-	if (bu == NULL) {
-		return FALSE;
-	}
-
-	if (c) {
-		if (iu->bu->ic != c->ic) {
-			irc_send_num(ic->irc, 482, "%s :Can't mix different IM networks in one groupchat", ic->name);
-		} else if (c->ic->acc->prpl->chat_invite) {
-			c->ic->acc->prpl->chat_invite(c, iu->bu->handle, NULL);
-		} else {
-			irc_send_num(ic->irc, 482, "%s :IM protocol does not support room invitations", ic->name);
-		}
-	} else if (bu->ic->acc->prpl->chat_with &&
-	           strcmp(set_getstr(&ic->set, "chat_type"), "groupchat") == 0) {
-		ic->flags |= IRC_CHANNEL_CHAT_PICKME;
-		iu->bu->ic->acc->prpl->chat_with(bu->ic, bu->handle);
-		ic->flags &= ~IRC_CHANNEL_CHAT_PICKME;
-	} else {
-		irc_send_num(ic->irc, 482, "%s :IM protocol does not support room invitations", ic->name);
-	}
-
-	return TRUE;
-}
-
-static void bee_irc_channel_chat_kick(irc_channel_t *ic, irc_user_t *iu, const char *msg)
-{
-	struct groupchat *c = ic->data;
-	bee_user_t *bu = iu->bu;
-
-	if ((c == NULL) || (bu == NULL)) {
-		return;
-	}
-
-	if (!c->ic->acc->prpl->chat_kick) {
-		irc_send_num(ic->irc, 482, "%s :IM protocol does not support room kicking", ic->name);
-		return;
-	}
-
-	c->ic->acc->prpl->chat_kick(c, iu->bu->handle, msg);
-}
-
-static char *set_eval_room_account(set_t *set, char *value);
-static char *set_eval_chat_type(set_t *set, char *value);
-
-static gboolean bee_irc_channel_init(irc_channel_t *ic)
-{
-	set_t *s;
-
-	set_add(&ic->set, "account", NULL, set_eval_room_account, ic);
-	set_add(&ic->set, "chat_type", "groupchat", set_eval_chat_type, ic);
-
-	s = set_add(&ic->set, "nick", NULL, NULL, ic);
-	s->flags |= SET_NULL_OK;
-
-	set_add(&ic->set, "room", NULL, NULL, ic);
-	set_add(&ic->set, "translate_to_nicks", "true", set_eval_bool, ic);
-
-	/* chat_type == groupchat */
-	ic->flags |= IRC_CHANNEL_TEMP;
-
-	return TRUE;
-}
-
-static char *set_eval_room_account(set_t *set, char *value)
-{
-	struct irc_channel *ic = set->data;
-	account_t *acc, *oa;
-
-	if (!(acc = account_get(ic->irc->b, value))) {
-		return SET_INVALID;
-	} else if (!acc->prpl->chat_join && acc->prpl != &protocol_missing) {
-		irc_rootmsg(ic->irc, "Named chatrooms not supported on that account.");
-		return SET_INVALID;
-	}
-
-	if (set->value && (oa = account_get(ic->irc->b, set->value)) &&
-	    oa->prpl->chat_free_settings) {
-		oa->prpl->chat_free_settings(oa, &ic->set);
-	}
-
-	if (acc->prpl->chat_add_settings) {
-		acc->prpl->chat_add_settings(acc, &ic->set);
-	}
-
-	return g_strdup(acc->tag);
-}
-
-static char *set_eval_chat_type(set_t *set, char *value)
-{
-	struct irc_channel *ic = set->data;
-
-	if (strcmp(value, "groupchat") == 0) {
-		ic->flags |= IRC_CHANNEL_TEMP;
-	} else if (strcmp(value, "room") == 0) {
-		ic->flags &= ~IRC_CHANNEL_TEMP;
-	} else {
-		return NULL;
-	}
-
-	return value;
-}
-
-static gboolean bee_irc_channel_free(irc_channel_t *ic)
-{
-	struct groupchat *c = ic->data;
-
-	set_del(&ic->set, "account");
-	set_del(&ic->set, "chat_type");
-	set_del(&ic->set, "nick");
-	set_del(&ic->set, "room");
-	set_del(&ic->set, "translate_to_nicks");
-
-	ic->flags &= ~IRC_CHANNEL_TEMP;
-
-	/* That one still points at this channel. Don't. */
-	if (c) {
-		c->ui_data = NULL;
-	}
-
-	return TRUE;
-}
-
-const struct irc_channel_funcs irc_channel_im_chat_funcs = {
-	bee_irc_channel_chat_privmsg,
-	bee_irc_channel_chat_join,
-	bee_irc_channel_chat_part,
-	bee_irc_channel_chat_topic,
-	bee_irc_channel_chat_invite,
-	bee_irc_channel_chat_kick,
-
-	bee_irc_channel_init,
-	bee_irc_channel_free,
-};
-
-
-/* IM->IRC: File transfers */
-static file_transfer_t *bee_irc_ft_in_start(bee_t *bee, bee_user_t *bu, const char *file_name, size_t file_size)
-{
-	return dccs_send_start(bu->ic, (irc_user_t *) bu->ui_data, file_name, file_size);
-}
-
-static gboolean bee_irc_ft_out_start(struct im_connection *ic, file_transfer_t *ft)
-{
-	return dccs_recv_start(ft);
-}
-
-static void bee_irc_ft_close(struct im_connection *ic, file_transfer_t *ft)
-{
-	return dcc_close(ft);
-}
-
-static void bee_irc_ft_finished(struct im_connection *ic, file_transfer_t *file)
-{
-	dcc_file_transfer_t *df = file->priv;
-
-	if (file->bytes_transferred >= file->file_size) {
-		dcc_finish(file);
-	} else {
-		df->proto_finished = TRUE;
-	}
-}
-
-static void bee_irc_log(bee_t *bee, const char *tag, const char *msg)
-{
-	irc_t *irc = (irc_t *) bee->ui_data;
-
-	irc_rootmsg(irc, "%s - %s", tag, msg);
-}
-
-const struct bee_ui_funcs irc_ui_funcs = {
-	bee_irc_imc_connected,
-	bee_irc_imc_disconnected,
-
-	bee_irc_user_new,
-	bee_irc_user_free,
-	bee_irc_user_fullname,
-	bee_irc_user_nick_hint,
-	bee_irc_user_group,
-	bee_irc_user_status,
-	bee_irc_user_msg,
-	bee_irc_user_typing,
-	bee_irc_user_action_response,
-
-	bee_irc_chat_new,
-	bee_irc_chat_free,
-	bee_irc_chat_log,
-	bee_irc_chat_msg,
-	bee_irc_chat_add_user,
-	bee_irc_chat_remove_user,
-	bee_irc_chat_topic,
-	bee_irc_chat_name_hint,
-	bee_irc_chat_invite,
-
-	bee_irc_ft_in_start,
-	bee_irc_ft_out_start,
-	bee_irc_ft_close,
-	bee_irc_ft_finished,
-
-	bee_irc_log,
-	bee_irc_user_nick_change,
-};
Index: c_send.c
===================================================================
--- irc_send.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,535 +1,0 @@
-/********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2012 Wilmer van der Gaast and others                *
-  \********************************************************************/
-
-/* The IRC-based UI - Sending responses to commands/etc.                */
-
-/*
-  This program is free software; you can 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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#include "bitlbee.h"
-
-void irc_send_num(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->root->host, code, irc->user->nick ? : "*", text);
-}
-
-void irc_send_login(irc_t *irc)
-{
-	irc_send_num(irc,   1, ":Welcome to the %s gateway, %s", PACKAGE, irc->user->nick);
-	irc_send_num(irc,   2, ":Host %s is running %s %s.", irc->root->host,
-	             PACKAGE, BITLBEE_VERSION);
-	irc_send_num(irc,   3, ":%s", IRCD_INFO);
-	irc_send_num(irc,   4, "%s %s %s %s", irc->root->host, BITLBEE_VERSION, UMODES UMODES_PRIV, CMODES);
-	irc_send_num(irc,   5, "PREFIX=(ohv)@%%+ CHANTYPES=%s CHANMODES=,,,%s NICKLEN=%d CHANNELLEN=%d "
-	             "NETWORK=BitlBee SAFELIST CASEMAPPING=rfc1459 MAXTARGETS=1 WATCH=128 "
-	             "FLOOD=0/9999 :are supported by this server",
-	             CTYPES, CMODES, MAX_NICK_LENGTH - 1, MAX_NICK_LENGTH - 1);
-	irc_send_motd(irc);
-}
-
-void irc_send_motd(irc_t *irc)
-{
-	char *motd;
-	size_t len;
-
-	g_file_get_contents(global.conf->motdfile, &motd, &len, NULL);
-
-	if (!motd || !len) {
-		irc_send_num(irc, 422, ":We don't need MOTDs.");
-	} else {
-		char linebuf[80];
-		char *add = "", max, *in;
-
-		in = motd;
-		linebuf[79] = len = 0;
-		max = sizeof(linebuf) - 1;
-
-		irc_send_num(irc, 375, ":- %s Message Of The Day - ", irc->root->host);
-		while ((linebuf[len] = *(in++))) {
-			if (linebuf[len] == '\n' || len == max) {
-				linebuf[len] = 0;
-				irc_send_num(irc, 372, ":- %s", linebuf);
-				len = 0;
-			} else if (linebuf[len] == '%') {
-				linebuf[len] = *(in++);
-				if (linebuf[len] == 'h') {
-					add = irc->root->host;
-				} else if (linebuf[len] == 'v') {
-					add = BITLBEE_VERSION;
-				} else if (linebuf[len] == 'n') {
-					add = irc->user->nick;
-				} else if (linebuf[len] == '\0') {
-					in--;
-				} else {
-					add = "%";
-				}
-
-				strncpy(linebuf + len, add, max - len);
-				while (linebuf[++len]) {
-					;
-				}
-			} else if (len < max) {
-				len++;
-			}
-		}
-		irc_send_num(irc, 376, ":End of MOTD");
-	}
-
-	g_free(motd);
-
-}
-
-/* Used by some funcs that generate PRIVMSGs to figure out if we're talking to
-   this person in /query or in a control channel. WARNING: callers rely on
-   this returning a pointer at irc->user_nick, not a copy of it. */
-const char *irc_user_msgdest(irc_user_t *iu)
-{
-	irc_t *irc = iu->irc;
-	irc_channel_t *ic = NULL;
-
-	if (iu->last_channel) {
-		if (iu->last_channel->flags & IRC_CHANNEL_JOINED) {
-			ic = iu->last_channel;
-		} else {
-			ic = irc_channel_with_user(irc, iu);
-		}
-	}
-
-	if (ic) {
-		return ic->name;
-	} else {
-		return irc->user->nick;
-	}
-}
-
-/* cmd = "PRIVMSG" or "NOTICE" */
-static void irc_usermsg_(const char *cmd, irc_user_t *iu, const char *format, va_list params)
-{
-	char text[2048];
-	const char *dst;
-
-	g_vsnprintf(text, sizeof(text), format, params);
-
-	dst = irc_user_msgdest(iu);
-	irc_send_msg(iu, cmd, dst, text, NULL);
-}
-
-void irc_usermsg(irc_user_t *iu, char *format, ...)
-{
-	va_list params;
-
-	va_start(params, format);
-	irc_usermsg_("PRIVMSG", iu, format, params);
-	va_end(params);
-}
-
-void irc_usernotice(irc_user_t *iu, char *format, ...)
-{
-	va_list params;
-
-	va_start(params, format);
-	irc_usermsg_("NOTICE", iu, format, params);
-	va_end(params);
-}
-
-void irc_rootmsg(irc_t *irc, char *format, ...)
-{
-	va_list params;
-
-	va_start(params, format);
-	irc_usermsg_("PRIVMSG", irc->root, format, params);
-	va_end(params);
-}
-
-void irc_send_join(irc_channel_t *ic, irc_user_t *iu)
-{
-	irc_t *irc = ic->irc;
-
-	if (irc->caps & CAP_EXTENDED_JOIN) {
-		irc_write(irc, ":%s!%s@%s JOIN %s * :%s", iu->nick, iu->user, iu->host, ic->name, iu->fullname);
-	} else {
-		irc_write(irc, ":%s!%s@%s JOIN :%s", iu->nick, iu->user, iu->host, ic->name);
-	}
-
-	if (iu == irc->user) {
-		if (ic->topic && *ic->topic) {
-			irc_send_topic(ic, FALSE);
-		}
-		irc_send_names(ic);
-	}
-}
-
-void irc_send_part(irc_channel_t *ic, irc_user_t *iu, const char *reason)
-{
-	irc_write(ic->irc, ":%s!%s@%s PART %s :%s", iu->nick, iu->user, iu->host, ic->name, reason ? : "");
-}
-
-void irc_send_quit(irc_user_t *iu, const char *reason)
-{
-	irc_write(iu->irc, ":%s!%s@%s QUIT :%s", iu->nick, iu->user, iu->host, reason ? : "");
-}
-
-void irc_send_kick(irc_channel_t *ic, irc_user_t *iu, irc_user_t *kicker, const char *reason)
-{
-	irc_write(ic->irc, ":%s!%s@%s KICK %s %s :%s", kicker->nick, kicker->user,
-	          kicker->host, ic->name, iu->nick, reason ? : "");
-}
-
-#define IRC_NAMES_LEN 385
-
-void irc_send_names(irc_channel_t *ic)
-{
-	GSList *l;
-	GString *namelist = g_string_sized_new(IRC_NAMES_LEN);
-	gboolean uhnames = (ic->irc->caps & CAP_USERHOST_IN_NAMES);
-
-	/* RFCs say there is no error reply allowed on NAMES, so when the
-	   channel is invalid, just give an empty reply. */
-	for (l = ic->users; l; l = l->next) {
-		irc_channel_user_t *icu = l->data;
-		irc_user_t *iu = icu->iu;
-		size_t extra_len = strlen(iu->nick);
-		char prefix;
-
-		if (uhnames) {
-			extra_len += strlen(iu->user) + strlen(iu->host) + 2;
-		}
-
-		if (namelist->len + extra_len > IRC_NAMES_LEN - 4) {
-			irc_send_num(ic->irc, 353, "= %s :%s", ic->name, namelist->str);
-			g_string_truncate(namelist, 0);
-		}
-
-		if ((prefix = irc_channel_user_get_prefix(icu))) {
-			g_string_append_c(namelist, prefix);
-		}
-
-		if (uhnames) {
-			g_string_append_printf(namelist, "%s!%s@%s ", iu->nick, iu->user, iu->host);
-		} else {
-			g_string_append(namelist, iu->nick);
-			g_string_append_c(namelist, ' ');
-		}
-	}
-
-	if (namelist->len) {
-		irc_send_num(ic->irc, 353, "= %s :%s", ic->name, namelist->str);
-	}
-
-	irc_send_num(ic->irc, 366, "%s :End of /NAMES list", ic->name);
-
-	g_string_free(namelist, TRUE);
-}
-
-void irc_send_topic(irc_channel_t *ic, gboolean topic_change)
-{
-	if (topic_change && ic->topic_who) {
-		irc_write(ic->irc, ":%s TOPIC %s :%s", ic->topic_who,
-		          ic->name, ic->topic && *ic->topic ? ic->topic : "");
-	} else if (ic->topic) {
-		irc_send_num(ic->irc, 332, "%s :%s", ic->name, ic->topic);
-		if (ic->topic_who) {
-			irc_send_num(ic->irc, 333, "%s %s %d",
-			             ic->name, ic->topic_who, (int) ic->topic_time);
-		}
-	} else {
-		irc_send_num(ic->irc, 331, "%s :No topic for this channel", ic->name);
-	}
-}
-
-/* msg1 and msg2 are output parameters. If msg2 is non-null, msg1 is guaranteed to be non-null too.
-   The idea is to defer the formatting of "$msg1 ($msg2)" to later calls to avoid a g_strdup_printf() here. */
-static void get_status_message(bee_user_t *bu, char **msg1, char **msg2)
-{
-	*msg1 = NULL;
-	*msg2 = NULL;
-
-	if (!(bu->flags & BEE_USER_ONLINE)) {
-		*msg1 = "User is offline";
-
-	} else if ((bu->status && *bu->status) ||
-		   (bu->status_msg && *bu->status_msg)) {
-
-		if (bu->status && bu->status_msg) {
-			*msg1 = bu->status;
-			*msg2 = bu->status_msg;
-		} else {
-			*msg1 = bu->status ? : bu->status_msg;
-		}
-	}
-
-	if (*msg1 && !**msg1) {
-		*msg1 = (bu->flags & BEE_USER_AWAY) ? "Away" : NULL;
-	}
-}
-
-void irc_send_whois(irc_user_t *iu)
-{
-	irc_t *irc = iu->irc;
-
-	irc_send_num(irc, 311, "%s %s %s * :%s",
-	             iu->nick, iu->user, iu->host, iu->fullname);
-
-	if (iu->bu) {
-		bee_user_t *bu = iu->bu;
-		char *msg1, *msg2;
-		int num;
-
-		irc_send_num(irc, 312, "%s %s.%s :%s network", iu->nick, bu->ic->acc->user,
-		             bu->ic->acc->server && *bu->ic->acc->server ? bu->ic->acc->server : "",
-		             bu->ic->acc->prpl->name);
-
-		num = (bu->flags & BEE_USER_AWAY || !(bu->flags & BEE_USER_ONLINE)) ? 301 : 320;
-
-		get_status_message(bu, &msg1, &msg2);
-
-		if (msg1 && msg2) {
-			irc_send_num(irc, num, "%s :%s (%s)", iu->nick, msg1, msg2);
-		} else if (msg1) {
-			irc_send_num(irc, num, "%s :%s", iu->nick, msg1);
-		}
-
-		if (bu->idle_time || bu->login_time) {
-			irc_send_num(irc, 317, "%s %d %d :seconds idle, signon time",
-			             iu->nick,
-			             bu->idle_time ? (int) (time(NULL) - bu->idle_time) : 0,
-			             (int) bu->login_time);
-		}
-	} else {
-		irc_send_num(irc, 312, "%s %s :%s", iu->nick, irc->root->host, IRCD_INFO);
-	}
-
-	irc_send_num(irc, 318, "%s :End of /WHOIS list", iu->nick);
-}
-
-void irc_send_who(irc_t *irc, GSList *l, const char *channel)
-{
-	gboolean is_channel = strchr(CTYPES, channel[0]) != NULL;
-
-	while (l) {
-		irc_user_t *iu;
-
-		/* Null terminated string with three chars, respectively:
-		 * { <H|G>, <@|%|+|\0>, \0 } */
-		char status_prefix[3] = {0};
-
-		if (is_channel) {
-			irc_channel_user_t *icu = l->data;
-			status_prefix[1] = irc_channel_user_get_prefix(icu);
-			iu = icu->iu;
-		} else {
-			iu = l->data;
-		}
-
-		/* If this is the account nick, check configuration to see if away */
-		if (iu == irc->user) {
-			/* rfc1459 doesn't mention this: G means gone, H means here */
-			status_prefix[0] = set_getstr(&irc->b->set, "away") ? 'G' : 'H';
-		} else {
-			status_prefix[0] = iu->flags & IRC_USER_AWAY ? 'G' : 'H';
-		}
-
-		irc_send_num(irc, 352, "%s %s %s %s %s %s :0 %s",
-		             is_channel ? channel : "*", iu->user, iu->host, irc->root->host,
-		             iu->nick, status_prefix, iu->fullname);
-		l = l->next;
-	}
-
-	irc_send_num(irc, 315, "%s :End of /WHO list", channel);
-}
-
-void irc_send_msg(irc_user_t *iu, const char *type, const char *dst, const char *msg, const char *prefix)
-{
-	irc_send_msg_ts(iu, type, dst, msg, prefix, 0);
-}
-
-void irc_send_msg_ts(irc_user_t *iu, const char *type, const char *dst, const char *msg, const char *prefix, time_t ts)
-{
-	char last = 0;
-	const char *s = msg, *line = msg;
-	char *tags = NULL;
-	char raw_msg[strlen(msg) + 1024];
-
-	if (!(iu->irc->caps & CAP_SERVER_TIME)) {
-		ts = 0;
-	}
-
-	while (!last) {
-		if (*s == '\r' && *(s + 1) == '\n') {
-			s++;
-		}
-		if (*s == '\n') {
-			last = s[1] == 0;
-		} else {
-			last = s[0] == 0;
-		}
-		if (*s == 0 || *s == '\n') {
-			if (ts) {
-				tags = irc_format_servertime(iu->irc, ts);
-			}
-			if (g_strncasecmp(line, "/me ", 4) == 0 && (!prefix || !*prefix) &&
-			    g_strcasecmp(type, "PRIVMSG") == 0) {
-				strcpy(raw_msg, "\001ACTION ");
-				strncat(raw_msg, line + 4, s - line - 4);
-				strcat(raw_msg, "\001");
-				irc_send_msg_raw_tags(iu, type, dst, tags, raw_msg);
-			} else {
-				*raw_msg = '\0';
-				if (prefix && *prefix) {
-					strcpy(raw_msg, prefix);
-				}
-				strncat(raw_msg, line, s - line);
-				irc_send_msg_raw_tags(iu, type, dst, tags, raw_msg);
-			}
-			if (ts) {
-				g_free(tags);
-			}
-			line = s + 1;
-		}
-		s++;
-	}
-}
-
-void irc_send_msg_raw(irc_user_t *iu, const char *type, const char *dst, const char *msg)
-{
-	irc_send_msg_raw_tags(iu, type, dst, NULL, msg);
-}
-
-void irc_send_msg_raw_tags(irc_user_t *iu, const char *type, const char *dst, const char* tags, const char *msg)
-{
-	irc_write(iu->irc, "%s%s:%s!%s@%s %s %s :%s",
-	          tags ? tags : "", tags ? " " : "", iu->nick, iu->user, iu->host, type, dst, msg && *msg ? msg : " ");
-}
-
-void irc_send_msg_f(irc_user_t *iu, const char *type, const char *dst, const 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(iu->irc, ":%s!%s@%s %s %s :%s",
-	          iu->nick, iu->user, iu->host, type, dst, text);
-}
-
-void irc_send_nick(irc_user_t *iu, const char *new)
-{
-	irc_write(iu->irc, ":%s!%s@%s NICK %s",
-	          iu->nick, iu->user, iu->host, new);
-}
-
-/* Send an update of a user's mode inside a channel, compared to what it was. */
-void irc_send_channel_user_mode_diff(irc_channel_t *ic, irc_user_t *iu,
-                                     irc_channel_user_flags_t old, irc_channel_user_flags_t new)
-{
-	char changes[3 * (5 + strlen(iu->nick))];
-	char from[strlen(ic->irc->root->nick) + strlen(ic->irc->root->user) + strlen(ic->irc->root->host) + 3];
-	int n;
-
-	*changes = '\0'; n = 0;
-	if ((old & IRC_CHANNEL_USER_OP) != (new & IRC_CHANNEL_USER_OP)) {
-		n++;
-		if (new & IRC_CHANNEL_USER_OP) {
-			strcat(changes, "+o");
-		} else {
-			strcat(changes, "-o");
-		}
-	}
-	if ((old & IRC_CHANNEL_USER_HALFOP) != (new & IRC_CHANNEL_USER_HALFOP)) {
-		n++;
-		if (new & IRC_CHANNEL_USER_HALFOP) {
-			strcat(changes, "+h");
-		} else {
-			strcat(changes, "-h");
-		}
-	}
-	if ((old & IRC_CHANNEL_USER_VOICE) != (new & IRC_CHANNEL_USER_VOICE)) {
-		n++;
-		if (new & IRC_CHANNEL_USER_VOICE) {
-			strcat(changes, "+v");
-		} else {
-			strcat(changes, "-v");
-		}
-	}
-	while (n) {
-		strcat(changes, " ");
-		strcat(changes, iu->nick);
-		n--;
-	}
-
-	if (set_getbool(&ic->irc->b->set, "simulate_netsplit")) {
-		g_snprintf(from, sizeof(from), "%s", ic->irc->root->host);
-	} else {
-		g_snprintf(from, sizeof(from), "%s!%s@%s", ic->irc->root->nick,
-		           ic->irc->root->user, ic->irc->root->host);
-	}
-
-	if (*changes) {
-		irc_write(ic->irc, ":%s MODE %s %s", from, ic->name, changes);
-	}
-}
-
-void irc_send_invite(irc_user_t *iu, irc_channel_t *ic)
-{
-	irc_t *irc = iu->irc;
-
-	irc_write(iu->irc, ":%s!%s@%s INVITE %s :%s",
-	          iu->nick, iu->user, iu->host, irc->user->nick, ic->name);
-}
-
-void irc_send_cap(irc_t *irc, char *subcommand, char *body)
-{
-	char *nick = irc->user->nick ? : "*";
-
-	irc_write(irc, ":%s CAP %s %s :%s", irc->root->host, nick, subcommand, body);
-}
-
-void irc_send_away_notify(irc_user_t *iu)
-{
-	bee_user_t *bu = iu->bu;
-
-	if (!bu) {
-		return;
-	}
-
-	if (bu->flags & BEE_USER_AWAY || !(bu->flags & BEE_USER_ONLINE)) {
-		char *msg1, *msg2;
-
-		get_status_message(bu, &msg1, &msg2);
-
-		if (msg2) {
-			irc_write(iu->irc, ":%s!%s@%s AWAY :%s (%s)", iu->nick, iu->user, iu->host, msg1, msg2);
-		} else {
-			irc_write(iu->irc, ":%s!%s@%s AWAY :%s", iu->nick, iu->user, iu->host, msg1);
-		}
-	} else {
-		irc_write(iu->irc, ":%s!%s@%s AWAY", iu->nick, iu->user, iu->host);
-	}
-}
-
Index: c_user.c
===================================================================
--- irc_user.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,270 +1,0 @@
-/********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2012 Wilmer van der Gaast and others                *
-  \********************************************************************/
-
-/* Stuff to handle, save and search IRC 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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#include "bitlbee.h"
-#include "ipc.h"
-
-irc_user_t *irc_user_new(irc_t *irc, const char *nick)
-{
-	irc_user_t *iu = g_new0(irc_user_t, 1);
-
-	iu->irc = irc;
-	iu->nick = g_strdup(nick);
-	iu->user = iu->host = iu->fullname = iu->nick;
-
-	iu->key = g_strdup(nick);
-	nick_lc(irc, iu->key);
-	g_hash_table_insert(irc->nick_user_hash, iu->key, iu);
-
-	return iu;
-}
-
-int irc_user_free(irc_t *irc, irc_user_t *iu)
-{
-	static struct im_connection *last_ic;
-	static char *msg;
-
-	if (!iu) {
-		return 0;
-	}
-
-	if (iu->bu &&
-	    (iu->bu->ic->flags & OPT_LOGGING_OUT) &&
-	    iu->bu->ic != last_ic) {
-		char host_prefix[] = "bitlbee.";
-		char *s;
-
-		/* Irssi recognises netsplits by quitmsgs with two
-		   hostnames, where a hostname is a "word" with one
-		   of more dots. Mangle no-dot hostnames a bit. */
-		if (strchr(irc->root->host, '.')) {
-			*host_prefix = '\0';
-		}
-
-		last_ic = iu->bu->ic;
-		g_free(msg);
-		if (!set_getbool(&irc->b->set, "simulate_netsplit")) {
-			msg = g_strdup("Account off-line");
-		} else if ((s = strchr(iu->bu->ic->acc->user, '@'))) {
-			msg = g_strdup_printf("%s%s %s", host_prefix,
-			                      irc->root->host, s + 1);
-		} else {
-			msg = g_strdup_printf("%s%s %s.%s",
-			                      host_prefix, irc->root->host,
-			                      iu->bu->ic->acc->prpl->name, irc->root->host);
-		}
-	} else if (!iu->bu || !(iu->bu->ic->flags & OPT_LOGGING_OUT)) {
-		g_free(msg);
-		msg = g_strdup("Removed");
-		last_ic = NULL;
-	}
-	irc_user_quit(iu, msg);
-
-	g_hash_table_remove(irc->nick_user_hash, iu->key);
-
-	g_free(iu->nick);
-	if (iu->nick != iu->user) {
-		g_free(iu->user);
-	}
-	if (iu->nick != iu->host) {
-		g_free(iu->host);
-	}
-	if (iu->nick != iu->fullname) {
-		g_free(iu->fullname);
-	}
-	g_free(iu->pastebuf);
-	if (iu->pastebuf_timer) {
-		b_event_remove(iu->pastebuf_timer);
-	}
-	g_free(iu->key);
-	g_free(iu);
-
-	return 1;
-}
-
-irc_user_t *irc_user_by_name(irc_t *irc, const char *nick)
-{
-	char key[strlen(nick) + 1];
-
-	strcpy(key, nick);
-	if (nick_lc(irc, key)) {
-		return g_hash_table_lookup(irc->nick_user_hash, key);
-	} else {
-		return NULL;
-	}
-}
-
-int irc_user_set_nick(irc_user_t *iu, const char *new)
-{
-	irc_t *irc = iu->irc;
-	irc_user_t *new_iu;
-	char key[strlen(new) + 1];
-	GSList *cl;
-
-	strcpy(key, new);
-	if (iu == NULL || !nick_lc(irc, key) ||
-	    ((new_iu = irc_user_by_name(irc, new)) && new_iu != iu)) {
-		return 0;
-	}
-
-	for (cl = irc->channels; cl; cl = cl->next) {
-		irc_channel_t *ic = cl->data;
-
-		/* Send a NICK update if we're renaming our user, or someone
-		   who's in the same channel like our user. */
-		if (iu == irc->user ||
-		    ((ic->flags & IRC_CHANNEL_JOINED) &&
-		     irc_channel_has_user(ic, iu))) {
-			irc_send_nick(iu, new);
-			break;
-		}
-	}
-
-	g_hash_table_remove(irc->nick_user_hash, iu->key);
-
-	if (iu->nick == iu->user) {
-		iu->user = NULL;
-	}
-	if (iu->nick == iu->host) {
-		iu->host = NULL;
-	}
-	if (iu->nick == iu->fullname) {
-		iu->fullname = NULL;
-	}
-	g_free(iu->nick);
-	iu->nick = g_strdup(new);
-	if (iu->user == NULL) {
-		iu->user = g_strdup(iu->nick);
-	}
-	if (iu->host == NULL) {
-		iu->host = g_strdup(iu->nick);
-	}
-	if (iu->fullname == NULL) {
-		iu->fullname = g_strdup(iu->nick);
-	}
-
-	g_free(iu->key);
-	iu->key = g_strdup(key);
-	g_hash_table_insert(irc->nick_user_hash, iu->key, iu);
-
-	if (iu == irc->user) {
-		ipc_to_master_str("NICK :%s\r\n", new);
-	}
-
-	return 1;
-}
-
-gint irc_user_cmp(gconstpointer a_, gconstpointer b_)
-{
-	const irc_user_t *a = a_, *b = b_;
-
-	return strcmp(a->key, b->key);
-}
-
-const char *irc_user_get_away(irc_user_t *iu)
-{
-	irc_t *irc = iu->irc;
-	bee_user_t *bu = iu->bu;
-
-	if (iu == irc->user) {
-		return set_getstr(&irc->b->set, "away");
-	} else if (bu) {
-		if (!bu->flags & BEE_USER_ONLINE) {
-			return "Offline";
-		} else if (bu->flags & BEE_USER_AWAY) {
-			if (bu->status_msg) {
-				static char ret[MAX_STRING];
-				g_snprintf(ret, MAX_STRING - 1, "%s (%s)",
-				           bu->status ? : "Away", bu->status_msg);
-				return ret;
-			} else {
-				return bu->status ? : "Away";
-			}
-		}
-	}
-
-	return NULL;
-}
-
-void irc_user_quit(irc_user_t *iu, const char *msg)
-{
-	GSList *l;
-	gboolean send_quit = FALSE;
-
-	if (!iu) {
-		return;
-	}
-
-	for (l = iu->irc->channels; l; l = l->next) {
-		irc_channel_t *ic = l->data;
-		send_quit |= irc_channel_del_user(ic, iu, IRC_CDU_SILENT, NULL) &&
-		             (ic->flags & IRC_CHANNEL_JOINED);
-	}
-
-	if (send_quit) {
-		irc_send_quit(iu, msg);
-	}
-}
-
-/* User-type dependent functions, for root/NickServ: */
-static gboolean root_privmsg(irc_user_t *iu, const char *msg)
-{
-	char cmd[strlen(msg) + 1];
-
-	strcpy(cmd, msg);
-	root_command_string(iu->irc, cmd);
-
-	return TRUE;
-}
-
-static gboolean root_ctcp(irc_user_t *iu, char * const *ctcp)
-{
-	if (g_strcasecmp(ctcp[0], "VERSION") == 0) {
-		irc_send_msg_f(iu, "NOTICE", iu->irc->user->nick, "\001%s %s\001",
-		               ctcp[0], PACKAGE " " BITLBEE_VERSION);
-	} else if (g_strcasecmp(ctcp[0], "PING") == 0) {
-		irc_send_msg_f(iu, "NOTICE", iu->irc->user->nick, "\001%s %s\001",
-		               ctcp[0], ctcp[1] ? : "");
-	}
-
-	return TRUE;
-}
-
-const struct irc_user_funcs irc_user_root_funcs = {
-	root_privmsg,
-	root_ctcp,
-};
-
-/* Echo to yourself: */
-static gboolean self_privmsg(irc_user_t *iu, const char *msg)
-{
-	irc_send_msg(iu, "PRIVMSG", iu->nick, msg, NULL);
-
-	return TRUE;
-}
-
-const struct irc_user_funcs irc_user_self_funcs = {
-	self_privmsg,
-};
Index: c_util.c
===================================================================
--- irc_util.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,147 +1,0 @@
-/********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2010 Wilmer van der Gaast and others                *
-  \********************************************************************/
-
-/* Some stuff that doesn't belong anywhere else.                        */
-
-/*
-  This program is free software; you can 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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#include "bitlbee.h"
-
-char *set_eval_timezone(set_t *set, char *value)
-{
-	char *s;
-
-	if (strcmp(value, "local") == 0 ||
-	    strcmp(value, "gmt") == 0 || strcmp(value, "utc") == 0) {
-		return value;
-	}
-
-	/* Otherwise: +/- at the beginning optional, then one or more numbers,
-	   possibly followed by a colon and more numbers. Don't bother bound-
-	   checking them since users are free to shoot themselves in the foot. */
-	s = value;
-	if (*s == '+' || *s == '-') {
-		s++;
-	}
-
-	/* \d+ */
-	if (!g_ascii_isdigit(*s)) {
-		return SET_INVALID;
-	}
-	while (*s && g_ascii_isdigit(*s)) {
-		s++;
-	}
-
-	/* EOS? */
-	if (*s == '\0') {
-		return value;
-	}
-
-	/* Otherwise, colon */
-	if (*s != ':') {
-		return SET_INVALID;
-	}
-	s++;
-
-	/* \d+ */
-	if (!g_ascii_isdigit(*s)) {
-		return SET_INVALID;
-	}
-	while (*s && g_ascii_isdigit(*s)) {
-		s++;
-	}
-
-	/* EOS */
-	return *s == '\0' ? value : SET_INVALID;
-}
-
-char *irc_format_timestamp(irc_t *irc, time_t msg_ts)
-{
-	time_t now_ts = time(NULL);
-	struct tm now, msg;
-	char *set;
-
-	/* If the timestamp is <= 0 or less than a minute ago, discard it as
-	   it doesn't seem to add to much useful info and/or might be noise. */
-	if (msg_ts <= 0 || msg_ts > now_ts - 60) {
-		return NULL;
-	}
-
-	set = set_getstr(&irc->b->set, "timezone");
-	if (strcmp(set, "local") == 0) {
-		localtime_r(&now_ts, &now);
-		localtime_r(&msg_ts, &msg);
-	} else {
-		int hr, min = 0, sign = 60;
-
-		if (set[0] == '-') {
-			sign *= -1;
-			set++;
-		} else if (set[0] == '+') {
-			set++;
-		}
-
-		if (sscanf(set, "%d:%d", &hr, &min) >= 1) {
-			msg_ts += sign * (hr * 60 + min);
-			now_ts += sign * (hr * 60 + min);
-		}
-
-		gmtime_r(&now_ts, &now);
-		gmtime_r(&msg_ts, &msg);
-	}
-
-	if (msg.tm_year == now.tm_year && msg.tm_yday == now.tm_yday) {
-		return g_strdup_printf("\x02[\x02\x02\x02%02d:%02d:%02d\x02]\x02 ",
-		                       msg.tm_hour, msg.tm_min, msg.tm_sec);
-	} else {
-		return g_strdup_printf("\x02[\x02\x02\x02%04d-%02d-%02d "
-		                       "%02d:%02d:%02d\x02]\x02 ",
-		                       msg.tm_year + 1900, msg.tm_mon + 1, msg.tm_mday,
-		                       msg.tm_hour, msg.tm_min, msg.tm_sec);
-	}
-}
-
-char *irc_format_servertime(irc_t *irc, time_t msg_ts)
-{
-	struct tm msg;
-
-	if (msg_ts <= 0) {
-		return NULL;
-	}
-
-	gmtime_r(&msg_ts, &msg);
-
-	return g_strdup_printf("@time=%04d-%02d-%02dT"
-	                       "%02d:%02d:%02d.000Z",
-	                       msg.tm_year + 1900, msg.tm_mon + 1, msg.tm_mday,
-	                       msg.tm_hour, msg.tm_min, msg.tm_sec);
-}
-
-char *set_eval_self_messages(set_t *set, char *value)
-{
-	if (is_bool(value) ||
-	    g_strcasecmp(value, "prefix") == 0 ||
-	    g_strcasecmp(value, "prefix_notice") == 0) {
-		return value;
-	} else {
-		return SET_INVALID;
-	}
-}
Index: b/Makefile
===================================================================
--- lib/Makefile	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,50 +1,0 @@
-###########################
-## Makefile for BitlBee  ##
-##                       ##
-## Copyright 2006 Lintux ##
-###########################
-
-### DEFINITIONS
-
--include ../Makefile.settings
-ifdef _SRCDIR_
-_SRCDIR_ := $(_SRCDIR_)lib/
-endif
-
-# [SH] Program variables
-objects = arc.o base64.o canohost.o $(EVENT_HANDLER) ftutil.o http_client.o ini.o json_util.o md5.o misc.o oauth.o oauth2.o proxy.o sha1.o $(SSL_CLIENT) url.o xmltree.o ns_parse.o
-
-ifneq ($(EXTERNAL_JSON_PARSER),1)
-objects += json.o
-endif
-
-LFLAGS += -r
-
-# [SH] Phony targets
-all: lib.o
-check: all
-lcov: check
-gcov:
-	gcov *.c
-
-.PHONY: all clean distclean
-
-clean: $(subdirs)
-	rm -f *.o $(OUTFILE) core
-
-distclean: clean $(subdirs)
-	rm -rf .depend
-
-### MAIN PROGRAM
-
-lib.o: $(objects) $(subdirs)
-	@echo '*' Linking lib.o
-	$(VERBOSE) $(LD) $(LFLAGS) $(objects) -o lib.o
-
-$(objects): ../Makefile.settings Makefile
-
-$(objects): %.o: $(_SRCDIR_)%.c
-	@echo '*' Compiling $<
-	$(VERBOSE) $(CC) -c $(CFLAGS) $(CFLAGS_BITLBEE) $< -o $@
-
--include .depend/*.d
Index: b/arc.c
===================================================================
--- lib/arc.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,225 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Simple (but secure) ArcFour implementation for safer password storage.   *
-*                                                                           *
-*  Copyright 2006 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.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-\***************************************************************************/
-
-/*
-   This file implements ArcFour-encryption, which will mainly be used to
-   save IM passwords safely in the new XML-format. Possibly other uses will
-   come up later. It's supposed to be quite reliable (thanks to the use of a
-   6-byte IV/seed), certainly compared to the old format. The only realistic
-   way to crack BitlBee passwords now is to use a sniffer to get your hands
-   on the user's password.
-
-   If you see that something's wrong in this implementation (I asked a
-   couple of people to look at it already, but who knows), please tell me.
-
-   The reason I picked ArcFour is because it's pretty simple but effective,
-   so it will work without adding several KBs or an extra library dependency.
-
-   (ArcFour is an RC4-compatible cipher. See for details:
-   http://www.mozilla.org/projects/security/pki/nss/draft-kaukonen-cipher-arcfour-03.txt)
-*/
-
-
-#include <glib.h>
-#include <gmodule.h>
-#include <stdlib.h>
-#include <string.h>
-#include "misc.h"
-#include "arc.h"
-
-/* Add some seed to the password, to make sure we *never* use the same key.
-   This defines how many bytes we use as a seed. */
-#define ARC_IV_LEN 6
-
-/* To defend against a "Fluhrer, Mantin and Shamir attack", it is recommended
-   to shuffle S[] just a bit more before you start to use it. This defines how
-   many bytes we'll request before we'll really use them for encryption. */
-#define ARC_CYCLES 1024
-
-struct arc_state *arc_keymaker(unsigned char *key, int kl, int cycles)
-{
-	struct arc_state *st;
-	int i, j, tmp;
-	unsigned char S2[256];
-
-	st = g_malloc(sizeof(struct arc_state));
-	st->i = st->j = 0;
-	if (kl <= 0) {
-		kl = strlen((char *) key);
-	}
-
-	for (i = 0; i < 256; i++) {
-		st->S[i] = i;
-		S2[i] = key[i % kl];
-	}
-
-	for (i = j = 0; i < 256; i++) {
-		j = (j + st->S[i] + S2[i]) & 0xff;
-		tmp = st->S[i];
-		st->S[i] = st->S[j];
-		st->S[j] = tmp;
-	}
-
-	memset(S2, 0, 256);
-	i = j = 0;
-
-	for (i = 0; i < cycles; i++) {
-		arc_getbyte(st);
-	}
-
-	return st;
-}
-
-/*
-   For those who don't know, ArcFour is basically an algorithm that generates
-   a stream of bytes after you give it a key. Just get a byte from it and
-   xor it with your cleartext. To decrypt, just give it the same key again
-   and start xorring.
-
-   The function above initializes the byte generator, the next function can
-   be used to get bytes from the generator (and shuffle things a bit).
-*/
-
-unsigned char arc_getbyte(struct arc_state *st)
-{
-	unsigned char tmp;
-
-	/* Unfortunately the st-> stuff doesn't really improve readability here... */
-	st->i++;
-	st->j += st->S[st->i];
-	tmp = st->S[st->i];
-	st->S[st->i] = st->S[st->j];
-	st->S[st->j] = tmp;
-	tmp = (st->S[st->i] + st->S[st->j]) & 0xff;
-
-	return st->S[tmp];
-}
-
-/*
-   The following two functions can be used for reliable encryption and
-   decryption. Known plaintext attacks are prevented by adding some (6,
-   by default) random bytes to the password before setting up the state
-   structures. These 6 bytes are also saved in the results, because of
-   course we'll need them in arc_decode().
-
-   Because the length of the resulting string is unknown to the caller,
-   it should pass a char**. Since the encode/decode functions allocate
-   memory for the string, make sure the char** points at a NULL-pointer
-   (or at least to something you already free()d), or you'll leak
-   memory. And of course, don't forget to free() the result when you
-   don't need it anymore.
-
-   Both functions return the number of bytes in the result string.
-
-   Note that if you use the pad_to argument, you will need zero-termi-
-   nation to find back the original string length after decryption. So
-   it shouldn't be used if your string contains \0s by itself!
-*/
-
-int arc_encode(char *clear, int clear_len, unsigned char **crypt, char *password, int pad_to)
-{
-	struct arc_state *st;
-	unsigned char *key;
-	char *padded = NULL;
-	int key_len, i, padded_len;
-
-	key_len = strlen(password) + ARC_IV_LEN;
-	if (clear_len <= 0) {
-		clear_len = strlen(clear);
-	}
-
-	/* Pad the string to the closest multiple of pad_to. This makes it
-	   impossible to see the exact length of the password. */
-	if (pad_to > 0 && (clear_len % pad_to) > 0) {
-		padded_len = clear_len + pad_to - (clear_len % pad_to);
-		padded = g_malloc(padded_len);
-		memcpy(padded, clear, clear_len);
-
-		/* First a \0 and then random data, so we don't have to do
-		   anything special when decrypting. */
-		padded[clear_len] = 0;
-		random_bytes((unsigned char *) padded + clear_len + 1, padded_len - clear_len - 1);
-
-		clear = padded;
-		clear_len = padded_len;
-	}
-
-	/* Prepare buffers and the key + IV */
-	*crypt = g_malloc(clear_len + ARC_IV_LEN);
-	key = g_malloc(key_len);
-	strcpy((char *) key, password);
-
-	/* Add the salt. Save it for later (when decrypting) and, of course,
-	   add it to the encryption key. */
-	random_bytes(crypt[0], ARC_IV_LEN);
-	memcpy(key + key_len - ARC_IV_LEN, crypt[0], ARC_IV_LEN);
-
-	/* Generate the initial S[] from the IVed key. */
-	st = arc_keymaker(key, key_len, ARC_CYCLES);
-	g_free(key);
-
-	for (i = 0; i < clear_len; i++) {
-		crypt[0][i + ARC_IV_LEN] = clear[i] ^ arc_getbyte(st);
-	}
-
-	g_free(st);
-	g_free(padded);
-
-	return clear_len + ARC_IV_LEN;
-}
-
-int arc_decode(unsigned char *crypt, int crypt_len, char **clear, const char *password)
-{
-	struct arc_state *st;
-	unsigned char *key;
-	int key_len, clear_len, i;
-
-	key_len = strlen(password) + ARC_IV_LEN;
-	clear_len = crypt_len - ARC_IV_LEN;
-
-	if (clear_len < 0) {
-		*clear = g_strdup("");
-		return -1;
-	}
-
-	/* Prepare buffers and the key + IV */
-	*clear = g_malloc(clear_len + 1);
-	key = g_malloc(key_len);
-	strcpy((char *) key, password);
-	for (i = 0; i < ARC_IV_LEN; i++) {
-		key[key_len - ARC_IV_LEN + i] = crypt[i];
-	}
-
-	/* Generate the initial S[] from the IVed key. */
-	st = arc_keymaker(key, key_len, ARC_CYCLES);
-	g_free(key);
-
-	for (i = 0; i < clear_len; i++) {
-		clear[0][i] = crypt[i + ARC_IV_LEN] ^ arc_getbyte(st);
-	}
-	clear[0][i] = 0; /* Nice to have for plaintexts. */
-
-	g_free(st);
-
-	return clear_len;
-}
Index: b/arc.h
===================================================================
--- lib/arc.h	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,39 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Simple (but secure) ArcFour implementation for safer password storage.   *
-*                                                                           *
-*  Copyright 2007 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.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-\***************************************************************************/
-
-
-/* See arc.c for more information. */
-
-struct arc_state {
-	unsigned char S[256];
-	unsigned char i, j;
-};
-
-#ifndef G_GNUC_MALLOC
-#define G_GNUC_MALLOC
-#endif
-
-G_GNUC_MALLOC struct arc_state *arc_keymaker(unsigned char *key, int kl, int cycles);
-unsigned char arc_getbyte(struct arc_state *st);
-int arc_encode(char *clear, int clear_len, unsigned char **crypt, char *password, int pad_to);
-int arc_decode(unsigned char *crypt, int crypt_len, char **clear, const char *password);
Index: b/base64.c
===================================================================
--- lib/base64.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,60 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Base64 handling functions. encode_real() is mostly based on the y64 en-  *
-*  coder from libyahoo2. Moving it to a new file because it's getting big.  *
-*                                                                           *
-*  Copyright 2006 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.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-\***************************************************************************/
-
-#include <glib.h>
-#include <string.h>
-#include "base64.h"
-
-char *tobase64(const char *text)
-{
-	return base64_encode((const unsigned char *) text, strlen(text));
-}
-
-char *base64_encode(const unsigned char *in, int len)
-{
-	return g_base64_encode(in, len);
-}
-
-/* Just a simple wrapper, but usually not very convenient because of zero
-   termination. */
-char *frombase64(const char *in)
-{
-	unsigned char *out;
-
-	base64_decode(in, &out);
-	return (char *) out;
-}
-
-int base64_decode(const char *in, unsigned char **out)
-{
-	gsize len;
-
-	*out = g_base64_decode(in, &len);
-
-	/* Some silly functions expect it to be zero terminated */
-	*out = g_realloc(*out, len + 1);
-	out[0][len] = 0;
-
-	return len;
-}
Index: b/base64.h
===================================================================
--- lib/base64.h	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,31 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Base64 handling functions. encode_real() is mostly based on the y64 en-  *
-*  coder from libyahoo2. Moving it to a new file because it's getting big.  *
-*                                                                           *
-*  Copyright 2006 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.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-\***************************************************************************/
-
-#include <glib.h>
-#include <gmodule.h>
-
-G_MODULE_EXPORT char *tobase64(const char *text);
-G_MODULE_EXPORT char *base64_encode(const unsigned char *in, int len);
-G_MODULE_EXPORT char *frombase64(const char *in);
-G_MODULE_EXPORT int base64_decode(const char *in, unsigned char **out);
Index: b/canohost.c
===================================================================
--- lib/canohost.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,140 +1,0 @@
-  /********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2017 Wilmer van der Gaast and others                *
-  \********************************************************************/
-
-/*
-  This program is free software; you can 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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-/* These two functions, with some modifications, taken from OpenSSH:
- *
- * $OpenBSD: canohost.c,v 1.73 2016/03/07 19:02:43 djm Exp
- *
- * Author: Tatu Ylonen <ylo@cs.hut.fi>
- * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
- *                    All rights reserved
- * Functions for returning the canonical host name of the remote site.
- *
- * As far as I am concerned, the code I have written for this software
- * can be used freely for any purpose.  Any derived versions of this
- * software must be clearly marked as such, and if the derived work is
- * incompatible with the protocol description in the RFC file, it must be
- * called by a name other than "ssh" or "Secure Shell".
- */
-
-#include "canohost.h"
-
-char *reverse_lookup(const struct sockaddr *from_, const socklen_t fromlen_)
-{
-	struct sockaddr_storage from;
-	socklen_t fromlen;
-	struct addrinfo hints, *ai, *aitop;
-	char name[NI_MAXHOST], ntop2[NI_MAXHOST];
-	char ntop[INET6_ADDRSTRLEN];
-
-	fromlen = sizeof(from);
-	memset(&from, 0, sizeof(from));
-	memcpy(&from, from_, fromlen_);
-	ipv64_normalise_mapped(&from, &fromlen);
-	if (from.ss_family == AF_INET6) {
-		fromlen = sizeof(struct sockaddr_in6);
-	}
-
-	if (getnameinfo((struct sockaddr *)&from, fromlen, ntop, sizeof(ntop),
-	    NULL, 0, NI_NUMERICHOST) != 0) {
-		return NULL;
-	}
-
-	/* Map the IP address to a host name. */
-	if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name),
-	    NULL, 0, NI_NAMEREQD) != 0) {
-		/* Host name not found.  Use ip address. */
-		return g_strdup(ntop);
-	}
-
-	/*
-	 * if reverse lookup result looks like a numeric hostname,
-	 * someone is trying to trick us by PTR record like following:
-	 *	1.1.1.10.in-addr.arpa.	IN PTR	2.3.4.5
-	 */
-	memset(&hints, 0, sizeof(hints));
-	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
-	hints.ai_flags = AI_NUMERICHOST;
-	if (getaddrinfo(name, NULL, &hints, &ai) == 0) {
-		freeaddrinfo(ai);
-		return g_strdup(ntop);
-	}
-
-	/* Names are stored in lowercase. */
-	char *tolower = g_utf8_strdown(name, -1);
-	g_snprintf(name, sizeof(name), "%s", tolower);
-	g_free(tolower);
-
-	/*
-	 * Map it back to an IP address and check that the given
-	 * address actually is an address of this host.  This is
-	 * necessary because anyone with access to a name server can
-	 * define arbitrary names for an IP address. Mapping from
-	 * name to IP address can be trusted better (but can still be
-	 * fooled if the intruder has access to the name server of
-	 * the domain).
-	 */
-	memset(&hints, 0, sizeof(hints));
-	hints.ai_family = from.ss_family;
-	hints.ai_socktype = SOCK_STREAM;
-	if (getaddrinfo(name, NULL, &hints, &aitop) != 0) {
-		return g_strdup(ntop);
-	}
-	/* Look for the address from the list of addresses. */
-	for (ai = aitop; ai; ai = ai->ai_next) {
-		if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2,
-		    sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 &&
-		    (strcmp(ntop, ntop2) == 0))
-			break;
-	}
-	freeaddrinfo(aitop);
-	/* If we reached the end of the list, the address was not there. */
-	if (ai == NULL) {
-		/* Address not found for the host name. */
-		return g_strdup(ntop);
-	}
-	return g_strdup(name);
-}
-
-void
-ipv64_normalise_mapped(struct sockaddr_storage *addr, socklen_t *len)
-{
-	struct sockaddr_in6 *a6 = (struct sockaddr_in6 *)addr;
-	struct sockaddr_in *a4 = (struct sockaddr_in *)addr;
-	struct in_addr inaddr;
-	uint16_t port;
-
-	if (addr->ss_family != AF_INET6 ||
-	    !IN6_IS_ADDR_V4MAPPED(&a6->sin6_addr))
-		return;
-
-	memcpy(&inaddr, ((char *)&a6->sin6_addr) + 12, sizeof(inaddr));
-	port = a6->sin6_port;
-
-	memset(a4, 0, sizeof(*a4));
-
-	a4->sin_family = AF_INET;
-	*len = sizeof(*a4);
-	memcpy(&a4->sin_addr, &inaddr, sizeof(inaddr));
-	a4->sin_port = port;
-}
Index: b/canohost.h
===================================================================
--- lib/canohost.h	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,43 +1,0 @@
-  /********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2017 Wilmer van der Gaast and others                *
-  \********************************************************************/
-
-/*
-  This program is free software; you can 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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-/* These two functions, with some modifications, taken from OpenSSH:
- *
- * $OpenBSD: canohost.c,v 1.73 2016/03/07 19:02:43 djm Exp
- *
- * Author: Tatu Ylonen <ylo@cs.hut.fi>
- * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
- *                    All rights reserved
- * Functions for returning the canonical host name of the remote site.
- *
- * As far as I am concerned, the code I have written for this software
- * can be used freely for any purpose.  Any derived versions of this
- * software must be clearly marked as such, and if the derived work is
- * incompatible with the protocol description in the RFC file, it must be
- * called by a name other than "ssh" or "Secure Shell".
- */
-
-#include <bitlbee.h>
-
-G_MODULE_EXPORT char *reverse_lookup(const struct sockaddr *from_, const socklen_t fromlen_);
-G_MODULE_EXPORT void ipv64_normalise_mapped(struct sockaddr_storage *addr, socklen_t *len);
Index: b/events.h
===================================================================
--- lib/events.h	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,87 +1,0 @@
-/*
- * nogaim
- *
- * Copyright (C) 2006 Wilmer van der Gaast and others
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *
- */
-
-/* This stuff used to be in proxy.c too, but I split it off so BitlBee can
-   use other libraries (like libevent) to handle events. proxy.c is one very
-   nice piece of work from Gaim. It connects to a TCP server in the back-
-   ground and calls a callback function once the connection is ready to use.
-   This function (proxy_connect()) can be found in proxy.c. (It also
-   transparently handles HTTP/SOCKS proxies, when necessary.)
-
-   This file offers some extra event handling toys, which will be handled
-   by GLib or libevent. The advantage of using libevent is that it can use
-   more advanced I/O polling functions like epoll() in recent Linux
-   kernels. This should improve BitlBee's scalability. */
-
-
-#ifndef _EVENTS_H_
-#define _EVENTS_H_
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <glib.h>
-#include <gmodule.h>
-
-/* The conditions you can pass to b_input_add()/that will be passed to
-   the given callback function. */
-typedef enum {
-	B_EV_IO_READ = 1 << 0,
-	        B_EV_IO_WRITE = 1 << 1,
-	        B_EV_FLAG_FORCE_ONCE = 1 << 16,
-	        B_EV_FLAG_FORCE_REPEAT = 1 << 17,
-} b_input_condition;
-typedef gboolean (*b_event_handler)(gpointer data, gint fd, b_input_condition cond);
-
-/* For internal use. */
-#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)
-
-/* #define event_debug( ... ) printf( __VA_ARGS__ ) */
-#define event_debug(...)
-
-/* Call this once when the program starts. It'll initialize the event handler
-   library (if necessary) and then return immediately. */
-G_MODULE_EXPORT void b_main_init();
-
-/* This one enters the event loop. It shouldn't return until one of the event
-   handlers calls b_main_quit(). */
-G_MODULE_EXPORT void b_main_run();
-G_MODULE_EXPORT void b_main_quit();
-
-G_MODULE_EXPORT void b_main_iteration();
-
-
-/* Add event handlers (for I/O or a timeout). The event handler will be called
-   every time the event "happens", until your event handler returns FALSE (or
-   until you remove it using b_event_remove(). As usual, the data argument
-   can be used to pass your own data to the event handler. */
-G_MODULE_EXPORT gint b_input_add(int fd, b_input_condition cond, b_event_handler func, gpointer data);
-G_MODULE_EXPORT gint b_timeout_add(gint timeout, b_event_handler func, gpointer data);
-G_MODULE_EXPORT void b_event_remove(gint id);
-
-/* With libevent, this one also cleans up event handlers if that wasn't already
-   done (the caller is expected to do so but may miss it sometimes). */
-G_MODULE_EXPORT void closesocket(int fd);
-
-#endif /* _EVENTS_H_ */
Index: b/events_glib.c
===================================================================
--- lib/events_glib.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,165 +1,0 @@
-/********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2006 Wilmer van der Gaast and others                *
-  \********************************************************************/
-
-/*
- * Event handling (using GLib)
- */
-
-/*
-  This program is free software; you can 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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#define BITLBEE_CORE
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include "proxy.h"
-
-typedef struct _GaimIOClosure {
-	b_event_handler function;
-	gpointer data;
-	guint flags;
-} GaimIOClosure;
-
-static GMainLoop *loop = NULL;
-
-void b_main_init()
-{
-	if (loop == NULL) {
-		loop = g_main_loop_new(NULL, FALSE);
-	}
-}
-
-void b_main_run()
-{
-	g_main_loop_run(loop);
-}
-
-void b_main_quit()
-{
-	g_main_loop_quit(loop);
-}
-
-void b_main_iteration()
-{
-	g_main_context_iteration(NULL, FALSE);
-	event_debug("b_main_iteration()\n");
-}
-
-static gboolean gaim_io_invoke(GIOChannel *source, GIOCondition condition, gpointer data)
-{
-	GaimIOClosure *closure = data;
-	b_input_condition gaim_cond = 0;
-	gboolean st;
-
-	if (condition & G_IO_NVAL) {
-		return FALSE;
-	}
-
-	if (condition & GAIM_READ_COND) {
-		gaim_cond |= B_EV_IO_READ;
-	}
-	if (condition & GAIM_WRITE_COND) {
-		gaim_cond |= B_EV_IO_WRITE;
-	}
-
-	event_debug("gaim_io_invoke( %d, %d, %p )\n", g_io_channel_unix_get_fd(source), condition, data);
-
-	st = closure->function(closure->data, g_io_channel_unix_get_fd(source), gaim_cond);
-
-	if (!st) {
-		event_debug("Returned FALSE, cancelling.\n");
-	}
-
-	if (closure->flags & B_EV_FLAG_FORCE_ONCE) {
-		return FALSE;
-	} else if (closure->flags & B_EV_FLAG_FORCE_REPEAT) {
-		return TRUE;
-	} else {
-		return st;
-	}
-}
-
-static void gaim_io_destroy(gpointer data)
-{
-	event_debug("gaim_io_destroy( 0%p )\n", data);
-	g_free(data);
-}
-
-gint b_input_add(gint source, b_input_condition condition, b_event_handler function, gpointer data)
-{
-	GaimIOClosure *closure = g_new0(GaimIOClosure, 1);
-	GIOChannel *channel;
-	GIOCondition cond = 0;
-	int st;
-
-	closure->function = function;
-	closure->data = data;
-	closure->flags = condition;
-
-	if (condition & B_EV_IO_READ) {
-		cond |= GAIM_READ_COND;
-	}
-	if (condition & B_EV_IO_WRITE) {
-		cond |= GAIM_WRITE_COND;
-	}
-
-	channel = g_io_channel_unix_new(source);
-	st = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, cond,
-	                         gaim_io_invoke, closure, gaim_io_destroy);
-
-	event_debug("b_input_add( %d, %d, %p, %p ) = %d (%p)\n", source, condition, function, data, st, closure);
-
-	g_io_channel_unref(channel);
-	return st;
-}
-
-gint b_timeout_add(gint timeout, b_event_handler func, gpointer data)
-{
-	/* GSourceFunc and the BitlBee event handler function aren't
-	   really the same, but they're "compatible". ;-) It will do
-	   for now, BitlBee only looks at the "data" argument. */
-	gint st = g_timeout_add(timeout, (GSourceFunc) func, data);
-
-	event_debug("b_timeout_add( %d, %p, %p ) = %d\n", timeout, func, data, st);
-
-	return st;
-}
-
-void b_event_remove(gint tag)
-{
-	event_debug("b_event_remove( %d )\n", tag);
-
-	if (tag > 0) {
-		g_source_remove(tag);
-	}
-}
-
-void closesocket(int fd)
-{
-	close(fd);
-}
Index: b/events_libevent.c
===================================================================
--- lib/events_libevent.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,289 +1,0 @@
-/********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2006 Wilmer van der Gaast and others                *
-  \********************************************************************/
-
-/*
- * Event handling (using libevent)
- */
-
-/*
-  This program is free software; you can 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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#define BITLBEE_CORE
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/time.h>
-#include <event.h>
-#include "proxy.h"
-
-static void b_main_restart();
-static guint id_next = 1; /* Next ID to be allocated to an event handler. */
-static guint id_cur = 0; /* Event ID that we're currently handling. */
-static guint id_dead; /* Set to 1 if b_event_remove removes id_cur. */
-static GHashTable *id_hash;
-static int quitting = 0; /* Prepare to quit, stop handling events. */
-
-/* Since libevent doesn't handle two event handlers for one fd-condition
-   very well (which happens sometimes when BitlBee changes event handlers
-   for a combination), let's buid some indexes so we can delete them here
-   already, just in time. */
-static GHashTable *read_hash;
-static GHashTable *write_hash;
-
-struct event_base *leh;
-struct event_base *old_leh;
-
-struct b_event_data {
-	guint id;
-	struct event evinfo;
-	gint timeout;
-	b_event_handler function;
-	void *data;
-	guint flags;
-};
-
-void b_main_init()
-{
-	if (leh != NULL) {
-		/* Clean up the hash tables? */
-
-		b_main_restart();
-		old_leh = leh;
-	}
-
-	leh = event_init();
-
-	id_hash = g_hash_table_new(g_int_hash, g_int_equal);
-	read_hash = g_hash_table_new(g_int_hash, g_int_equal);
-	write_hash = g_hash_table_new(g_int_hash, g_int_equal);
-}
-
-void b_main_run()
-{
-	/* This while loop is necessary to exit the event loop and start a
-	   different one (necessary for ForkDaemon mode). */
-	while (event_base_dispatch(leh) == 0 && !quitting) {
-		if (old_leh != NULL) {
-			/* For some reason this just isn't allowed...
-			   Possibly a bug in older versions, will see later.
-			event_base_free( old_leh ); */
-			old_leh = NULL;
-		}
-
-		event_debug("New event loop.\n");
-	}
-}
-
-static void b_main_restart()
-{
-	struct timeval tv;
-
-	memset(&tv, 0, sizeof(struct timeval));
-	event_base_loopexit(leh, &tv);
-
-	event_debug("b_main_restart()\n");
-}
-
-void b_main_quit()
-{
-	/* Tell b_main_run() that it shouldn't restart the loop. Also,
-	   libevent sometimes generates events before really quitting,
-	   we want to stop them. */
-	quitting = 1;
-
-	b_main_restart();
-}
-
-void b_main_iteration()
-{
-	event_base_loop(leh, EVLOOP_NONBLOCK);
-	event_debug("b_main_iteration()\n");
-}
-
-static void b_event_passthrough(int fd, short event, void *data)
-{
-	struct b_event_data *b_ev = data;
-	b_input_condition cond = 0;
-	gboolean st;
-
-	if (fd >= 0) {
-		if (event & EV_READ) {
-			cond |= B_EV_IO_READ;
-		}
-		if (event & EV_WRITE) {
-			cond |= B_EV_IO_WRITE;
-		}
-	}
-
-	event_debug("b_event_passthrough( %d, %d, 0x%x ) (%d)\n", fd, event, (int) data, b_ev->id);
-
-	/* Since the called function might cancel this handler already
-	   (which free()s b_ev), we have to remember the ID here. */
-	id_cur = b_ev->id;
-	id_dead = 0;
-
-	if (quitting) {
-		b_event_remove(id_cur);
-		return;
-	}
-
-	st = b_ev->function(b_ev->data, fd, cond);
-	if (id_dead) {
-		/* This event was killed already, don't touch it! */
-		return;
-	} else if (!st && !(b_ev->flags & B_EV_FLAG_FORCE_REPEAT)) {
-		event_debug("Handler returned FALSE: ");
-		b_event_remove(id_cur);
-	} else if (fd == -1) {
-		/* fd == -1 means it was a timer. These can't be auto-repeated
-		   so it has to be recreated every time. */
-		struct timeval tv;
-
-		tv.tv_sec = b_ev->timeout / 1000;
-		tv.tv_usec = (b_ev->timeout % 1000) * 1000;
-
-		evtimer_add(&b_ev->evinfo, &tv);
-	}
-}
-
-gint b_input_add(gint fd, b_input_condition condition, b_event_handler function, gpointer data)
-{
-	struct b_event_data *b_ev;
-
-	event_debug("b_input_add( %d, %d, 0x%x, 0x%x ) ", fd, condition, function, data);
-
-	if ((condition & B_EV_IO_READ  && (b_ev = g_hash_table_lookup(read_hash,  &fd))) ||
-	    (condition & B_EV_IO_WRITE && (b_ev = g_hash_table_lookup(write_hash, &fd)))) {
-		/* We'll stick with this libevent entry, but give it a new BitlBee id. */
-		g_hash_table_remove(id_hash, &b_ev->id);
-
-		event_debug("(replacing old handler (id = %d)) = %d\n", b_ev->id, id_next);
-
-		b_ev->id = id_next++;
-		b_ev->function = function;
-		b_ev->data = data;
-	} else {
-		GIOCondition out_cond;
-
-		event_debug("(new) = %d\n", id_next);
-
-		b_ev = g_new0(struct b_event_data, 1);
-		b_ev->id = id_next++;
-		b_ev->function = function;
-		b_ev->data = data;
-
-		out_cond = EV_PERSIST;
-		if (condition & B_EV_IO_READ) {
-			out_cond |= EV_READ;
-		}
-		if (condition & B_EV_IO_WRITE) {
-			out_cond |= EV_WRITE;
-		}
-
-		event_set(&b_ev->evinfo, fd, out_cond, b_event_passthrough, b_ev);
-		event_add(&b_ev->evinfo, NULL);
-
-		if (out_cond & EV_READ) {
-			g_hash_table_insert(read_hash, &b_ev->evinfo.ev_fd, b_ev);
-		}
-		if (out_cond & EV_WRITE) {
-			g_hash_table_insert(write_hash, &b_ev->evinfo.ev_fd, b_ev);
-		}
-	}
-
-	b_ev->flags = condition;
-	g_hash_table_insert(id_hash, &b_ev->id, b_ev);
-	return b_ev->id;
-}
-
-/* TODO: Persistence for timers! */
-gint b_timeout_add(gint timeout, b_event_handler function, gpointer data)
-{
-	struct b_event_data *b_ev = g_new0(struct b_event_data, 1);
-	struct timeval tv;
-
-	b_ev->id = id_next++;
-	b_ev->timeout = timeout;
-	b_ev->function = function;
-	b_ev->data = data;
-
-	tv.tv_sec = timeout / 1000;
-	tv.tv_usec = (timeout % 1000) * 1000;
-
-	evtimer_set(&b_ev->evinfo, b_event_passthrough, b_ev);
-	evtimer_add(&b_ev->evinfo, &tv);
-
-	event_debug("b_timeout_add( %d, 0x%x, 0x%x ) = %d\n", timeout, function, data, b_ev->id);
-
-	g_hash_table_insert(id_hash, &b_ev->id, b_ev);
-
-	return b_ev->id;
-}
-
-void b_event_remove(gint id)
-{
-	struct b_event_data *b_ev = g_hash_table_lookup(id_hash, &id);
-
-	event_debug("b_event_remove( %d )\n", id);
-	if (b_ev) {
-		if (id == id_cur) {
-			id_dead = TRUE;
-		}
-
-		g_hash_table_remove(id_hash, &b_ev->id);
-		if (b_ev->evinfo.ev_fd >= 0) {
-			if (b_ev->evinfo.ev_events & EV_READ) {
-				g_hash_table_remove(read_hash, &b_ev->evinfo.ev_fd);
-			}
-			if (b_ev->evinfo.ev_events & EV_WRITE) {
-				g_hash_table_remove(write_hash, &b_ev->evinfo.ev_fd);
-			}
-		}
-
-		event_del(&b_ev->evinfo);
-		g_free(b_ev);
-	} else {
-		event_debug("Already removed?\n");
-	}
-}
-
-void closesocket(int fd)
-{
-	struct b_event_data *b_ev;
-
-	/* Since epoll() (the main reason we use libevent) automatically removes sockets from
-	   the epoll() list when a socket gets closed and some modules have a habit of
-	   closing sockets before removing event handlers, our and libevent's administration
-	   get a little bit messed up. So this little function will remove the handlers
-	   properly before closing a socket. */
-
-	if ((b_ev = g_hash_table_lookup(read_hash, &fd))) {
-		event_debug("Warning: fd %d still had a read event handler when shutting down.\n", fd);
-		b_event_remove(b_ev->id);
-	}
-	if ((b_ev = g_hash_table_lookup(write_hash, &fd))) {
-		event_debug("Warning: fd %d still had a write event handler when shutting down.\n", fd);
-		b_event_remove(b_ev->id);
-	}
-
-	close(fd);
-}
Index: b/ftutil.c
===================================================================
--- lib/ftutil.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,134 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Utility functions for file transfer                                      *
-*                                                                           *
-*  Copyright 2008 Uli Meis <a.sporto+bee@gmail.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.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-\***************************************************************************/
-
-#define BITLBEE_CORE
-#include "bitlbee.h"
-#include <poll.h>
-#include <netinet/tcp.h>
-#include "lib/ftutil.h"
-
-#define ASSERTSOCKOP(op, msg) \
-	if ((op) == -1) { \
-		g_snprintf(errmsg, sizeof(errmsg), msg ": %s", strerror(errno)); \
-		return -1; }
-
-/*
- * Creates a listening socket and returns it in saddr_ptr.
- */
-int ft_listen(struct sockaddr_storage *saddr_ptr, char *host, char *port, int copy_fd, int for_bitlbee_client,
-              char **errptr)
-{
-	int fd, gret, saddrlen;
-	struct addrinfo hints, *rp;
-	socklen_t ssize = sizeof(struct sockaddr_storage);
-	struct sockaddr_storage saddrs = {0}, *saddr = &saddrs;
-	static char errmsg[1024];
-	char *ftlisten = global.conf->ft_listen;
-
-	if (errptr) {
-		*errptr = errmsg;
-	}
-
-	strcpy(port, "0");
-
-	/* Format is <IP-A>[:<Port-A>];<IP-B>[:<Port-B>] where
-	 * A is for connections with the bitlbee client (DCC)
-	 * and B is for connections with IM peers.
-	 */
-	if (ftlisten) {
-		char *scolon = strchr(ftlisten, ';');
-		char *colon;
-
-		if (scolon) {
-			if (for_bitlbee_client) {
-				*scolon = '\0';
-				strncpy(host, ftlisten, NI_MAXHOST);
-				*scolon = ';';
-			} else {
-				strncpy(host, scolon + 1, NI_MAXHOST);
-			}
-		} else {
-			strncpy(host, ftlisten, NI_MAXHOST);
-		}
-
-		if ((colon = strchr(host, ':'))) {
-			*colon = '\0';
-			strncpy(port, colon + 1, 5);
-		}
-	} else if (copy_fd >= 0 && getsockname(copy_fd, (struct sockaddr*) &saddrs, &ssize) == 0 &&
-	           (saddrs.ss_family == AF_INET || saddrs.ss_family == AF_INET6) &&
-	           getnameinfo((struct sockaddr*) &saddrs, ssize, host, NI_MAXHOST,
-	                       NULL, 0, NI_NUMERICHOST) == 0) {
-		/* We just took our local address on copy_fd, which is likely to be a
-		   sensible address from which we can do a file transfer now - the
-		   most sensible we can get easily. */
-	} else {
-		ASSERTSOCKOP(gethostname(host, NI_MAXHOST), "gethostname()");
-	}
-
-	memset(&hints, 0, sizeof(struct addrinfo));
-	hints.ai_socktype = SOCK_STREAM;
-	hints.ai_flags = AI_NUMERICSERV;
-
-	if ((gret = getaddrinfo(host, port, &hints, &rp)) != 0) {
-		sprintf(errmsg, "getaddrinfo() failed: %s", gai_strerror(gret));
-		return -1;
-	}
-
-	saddrlen = rp->ai_addrlen;
-
-	memcpy(saddr, rp->ai_addr, saddrlen);
-
-	freeaddrinfo(rp);
-
-	ASSERTSOCKOP(fd = socket(saddr->ss_family, SOCK_STREAM, 0), "Opening socket");
-	ASSERTSOCKOP(bind(fd, ( struct sockaddr *) saddr, saddrlen), "Binding socket");
-	ASSERTSOCKOP(listen(fd, 1), "Making socket listen");
-
-	if (!inet_ntop(saddr->ss_family, saddr->ss_family == AF_INET ?
-	               ( void * ) &(( struct sockaddr_in * ) saddr)->sin_addr.s_addr :
-	               ( void * ) &(( struct sockaddr_in6 * ) saddr)->sin6_addr.s6_addr,
-	               host, NI_MAXHOST)) {
-		strcpy(errmsg, "inet_ntop failed on listening socket");
-		return -1;
-	}
-
-	ssize = sizeof(struct sockaddr_storage);
-	ASSERTSOCKOP(getsockname(fd, ( struct sockaddr *) saddr, &ssize), "Getting socket name");
-
-	if (saddr->ss_family == AF_INET) {
-		g_snprintf(port, 6, "%d", ntohs(((struct sockaddr_in *) saddr)->sin_port));
-	} else {
-		g_snprintf(port, 6, "%d", ntohs(((struct sockaddr_in6 *) saddr)->sin6_port));
-	}
-
-	if (saddr_ptr) {
-		memcpy(saddr_ptr, saddr, saddrlen);
-	}
-
-	/* I hate static-length strings.. */
-	host[NI_MAXHOST - 1] = '\0';
-	port[5] = '\0';
-
-	return fd;
-}
Index: b/ftutil.h
===================================================================
--- lib/ftutil.h	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,31 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Utility functions for file transfer                                      *
-*                                                                           *
-*  Copyright 2008 Uli Meis <a.sporto+bee@gmail.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.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-\***************************************************************************/
-
-#ifndef AI_NUMERICSERV
-#define AI_NUMERICSERV 0x0400   /* Don't use name resolution.  */
-#endif
-
-/* This function should be used with care. host should be AT LEAST a
-   char[NI_MAXHOST+1] and port AT LEAST a char[6]. */
-int ft_listen(struct sockaddr_storage *saddr_ptr, char *host, char *port, int copy_fd, int for_bitlbee_client,
-              char **errptr);
Index: b/http_client.c
===================================================================
--- lib/http_client.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,716 +1,0 @@
-/********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2013 Wilmer van der Gaast and others                *
-  \********************************************************************/
-
-/* HTTP(S) 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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#include <string.h>
-#include <stdio.h>
-
-#include "http_client.h"
-#include "url.h"
-#include "sock.h"
-
-
-static gboolean http_connected(gpointer data, int source, b_input_condition cond);
-static gboolean http_ssl_connected(gpointer data, int returncode, void *source, b_input_condition cond);
-static gboolean http_incoming_data(gpointer data, int source, b_input_condition cond);
-static void http_free(struct http_request *req);
-
-
-struct http_request *http_dorequest(char *host, int port, int ssl, char *request, http_input_function func,
-                                    gpointer data)
-{
-	struct http_request *req;
-	int error = 0;
-
-	req = g_new0(struct http_request, 1);
-
-	if (ssl) {
-		req->ssl = ssl_connect(host, port, TRUE, http_ssl_connected, req);
-		if (req->ssl == NULL) {
-			error = 1;
-		}
-	} else {
-		req->fd = proxy_connect(host, port, http_connected, req);
-		if (req->fd < 0) {
-			error = 1;
-		}
-	}
-
-	if (error) {
-		http_free(req);
-		return NULL;
-	}
-
-	req->func = func;
-	req->data = data;
-	req->request = g_strdup(request);
-	req->request_length = strlen(request);
-	req->redir_ttl = 3;
-	req->content_length = -1;
-
-	if (getenv("BITLBEE_DEBUG")) {
-		printf("About to send HTTP request:\n%s\n", req->request);
-	}
-
-	return req;
-}
-
-struct http_request *http_dorequest_url(char *url_string, http_input_function func, gpointer data)
-{
-	url_t *url = g_new0(url_t, 1);
-	char *request;
-	void *ret;
-
-	if (!url_set(url, url_string)) {
-		g_free(url);
-		return NULL;
-	}
-
-	if (url->proto != PROTO_HTTP && url->proto != PROTO_HTTPS) {
-		g_free(url);
-		return NULL;
-	}
-
-	request = g_strdup_printf("GET %s HTTP/1.0\r\n"
-	                          "Host: %s\r\n"
-	                          "User-Agent: BitlBee " BITLBEE_VERSION "\r\n"
-	                          "\r\n", url->file, url->host);
-
-	ret = http_dorequest(url->host, url->port,
-	                     url->proto == PROTO_HTTPS, request, func, data);
-
-	g_free(url);
-	g_free(request);
-	return ret;
-}
-
-/* This one is actually pretty simple... Might get more calls if we can't write
-   the whole request at once. */
-static gboolean http_connected(gpointer data, int source, b_input_condition cond)
-{
-	struct http_request *req = data;
-	int st;
-
-	if (source < 0) {
-		goto error;
-	}
-
-	if (req->inpa > 0) {
-		b_event_remove(req->inpa);
-	}
-
-	sock_make_nonblocking(req->fd);
-
-	if (req->ssl) {
-		st = ssl_write(req->ssl, req->request + req->bytes_written,
-		               req->request_length - req->bytes_written);
-		if (st < 0) {
-			if (ssl_errno != SSL_AGAIN) {
-				ssl_disconnect(req->ssl);
-				goto error;
-			}
-		}
-	} else {
-		st = write(source, req->request + req->bytes_written,
-		           req->request_length - req->bytes_written);
-		if (st < 0) {
-			if (!sockerr_again()) {
-				closesocket(req->fd);
-				goto error;
-			}
-		}
-	}
-
-	if (st > 0) {
-		req->bytes_written += st;
-	}
-
-	if (req->bytes_written < req->request_length) {
-		req->inpa = b_input_add(source,
-		                        req->ssl ? ssl_getdirection(req->ssl) : B_EV_IO_WRITE,
-		                        http_connected, req);
-	} else {
-		req->inpa = b_input_add(source, B_EV_IO_READ, http_incoming_data, req);
-	}
-
-	return FALSE;
-
-error:
-	if (req->status_string == NULL) {
-		req->status_string = g_strdup("Error while writing HTTP request");
-	}
-
-	if (req->func != NULL) {
-		req->func(req);
-	}
-	http_free(req);
-	return FALSE;
-}
-
-static gboolean http_ssl_connected(gpointer data, int returncode, void *source, b_input_condition cond)
-{
-	struct http_request *req = data;
-
-	if (source == NULL) {
-		if (returncode != 0) {
-			char *err = ssl_verify_strerror(returncode);
-			req->status_string = g_strdup_printf(
-			        "Certificate verification problem 0x%x: %s",
-			        returncode, err ? err : "Unknown");
-			g_free(err);
-		}
-		return http_connected(data, -1, cond);
-	}
-
-	req->fd = ssl_getfd(source);
-
-	return http_connected(data, req->fd, cond);
-}
-
-typedef enum {
-	CR_OK,
-	CR_EOF,
-	CR_ERROR,
-	CR_ABORT,
-} http_ret_t;
-
-static gboolean http_handle_headers(struct http_request *req);
-static http_ret_t http_process_chunked_data(struct http_request *req, const char *buffer, int len);
-static http_ret_t http_process_data(struct http_request *req, const char *buffer, int len);
-
-static gboolean http_incoming_data(gpointer data, int source, b_input_condition cond)
-{
-	struct http_request *req = data;
-	char buffer[4096];
-	int st;
-
-	if (req->inpa > 0) {
-		b_event_remove(req->inpa);
-		req->inpa = 0;
-	}
-
-	if (req->ssl) {
-		st = ssl_read(req->ssl, buffer, sizeof(buffer));
-		if (st < 0) {
-			if (ssl_errno != SSL_AGAIN) {
-				/* goto cleanup; */
-
-				/* YAY! We have to deal with crappy Microsoft
-				   servers that LOVE to send invalid TLS
-				   packets that abort connections! \o/ */
-
-				goto eof;
-			}
-		} else if (st == 0) {
-			goto eof;
-		}
-	} else {
-		st = read(req->fd, buffer, sizeof(buffer));
-		if (st < 0) {
-			if (!sockerr_again()) {
-				req->status_string = g_strdup(strerror(errno));
-				goto cleanup;
-			}
-		} else if (st == 0) {
-			goto eof;
-		}
-	}
-
-	if (st > 0) {
-		http_ret_t c;
-
-		if (req->flags & HTTPC_CHUNKED) {
-			c = http_process_chunked_data(req, buffer, st);
-		} else {
-			c = http_process_data(req, buffer, st);
-		}
-
-		if (c == CR_EOF) {
-			goto eof;
-		} else if (c == CR_ERROR || c == CR_ABORT) {
-			return FALSE;
-		}
-	}
-
-	if (req->content_length != -1 &&
-	    req->body_size >= req->content_length) {
-		goto eof;
-	}
-
-	if (ssl_pending(req->ssl)) {
-		return http_incoming_data(data, source, cond);
-	}
-
-	/* There will be more! */
-	req->inpa = b_input_add(req->fd,
-	                        req->ssl ? ssl_getdirection(req->ssl) : B_EV_IO_READ,
-	                        http_incoming_data, req);
-
-	return FALSE;
-
-eof:
-	req->flags |= HTTPC_EOF;
-
-	/* Maybe if the webserver is overloaded, or when there's bad SSL
-	   support... */
-	if (req->bytes_read == 0) {
-		req->status_string = g_strdup("Empty HTTP reply");
-		goto cleanup;
-	}
-
-cleanup:
-	/* Avoid g_source_remove warnings */
-	req->inpa = 0;
-
-	if (req->ssl) {
-		ssl_disconnect(req->ssl);
-	} else {
-		closesocket(req->fd);
-	}
-
-	if (req->body_size < req->content_length) {
-		req->status_code = -1;
-		g_free(req->status_string);
-		req->status_string = g_strdup("Response truncated");
-	}
-
-	if (getenv("BITLBEE_DEBUG") && req) {
-		printf("Finishing HTTP request with status: %s\n",
-		       req->status_string ? req->status_string : "NULL");
-	}
-
-	if (req->func != NULL) {
-		req->func(req);
-	}
-	http_free(req);
-	return FALSE;
-}
-
-static http_ret_t http_process_chunked_data(struct http_request *req, const char *buffer, int len)
-{
-	char *chunk, *eos, *s;
-
-	if (len < 0) {
-		return TRUE;
-	}
-
-	if (len > 0) {
-		req->cbuf = g_realloc(req->cbuf, req->cblen + len + 1);
-		memcpy(req->cbuf + req->cblen, buffer, len);
-		req->cblen += len;
-		req->cbuf[req->cblen] = '\0';
-	}
-
-	/* Turns out writing a proper chunked-encoding state machine is not
-	   that simple. :-( I've tested this one feeding it byte by byte so
-	   I hope it's solid now. */
-	chunk = req->cbuf;
-	eos = req->cbuf + req->cblen;
-	while (TRUE) {
-		int clen = 0;
-
-		/* Might be a \r\n from the last chunk. */
-		s = chunk;
-		while (g_ascii_isspace(*s)) {
-			s++;
-		}
-		/* Chunk length. Might be incomplete. */
-		if (s < eos && sscanf(s, "%x", &clen) != 1) {
-			return CR_ERROR;
-		}
-		while (g_ascii_isxdigit(*s)) {
-			s++;
-		}
-
-		/* If we read anything here, it *must* be \r\n. */
-		if (strncmp(s, "\r\n", MIN(2, eos - s)) != 0) {
-			return CR_ERROR;
-		}
-		s += 2;
-
-		if (s >= eos) {
-			break;
-		}
-
-		/* 0-length chunk means end of response. */
-		if (clen == 0) {
-			return CR_EOF;
-		}
-
-		/* Wait for the whole chunk to arrive. */
-		if (s + clen > eos) {
-			break;
-		}
-		if (http_process_data(req, s, clen) != CR_OK) {
-			return CR_ABORT;
-		}
-
-		chunk = s + clen;
-	}
-
-	if (chunk != req->cbuf) {
-		req->cblen = eos - chunk;
-		s = g_memdup2(chunk, req->cblen + 1);
-		g_free(req->cbuf);
-		req->cbuf = s;
-	}
-
-	return CR_OK;
-}
-
-static http_ret_t http_process_data(struct http_request *req, const char *buffer, int len)
-{
-	if (len <= 0) {
-		return CR_OK;
-	}
-
-	if (!req->reply_body) {
-		req->reply_headers = g_realloc(req->reply_headers, req->bytes_read + len + 1);
-		memcpy(req->reply_headers + req->bytes_read, buffer, len);
-		req->bytes_read += len;
-		req->reply_headers[req->bytes_read] = '\0';
-
-		if (strstr(req->reply_headers, "\r\n\r\n") ||
-		    strstr(req->reply_headers, "\n\n")) {
-			/* We've now received all headers. Look for something
-			   interesting. */
-			if (!http_handle_headers(req)) {
-				return CR_ABORT;
-			}
-
-			/* Start parsing the body as chunked if required. */
-			if (req->flags & HTTPC_CHUNKED) {
-				return http_process_chunked_data(req, NULL, 0);
-			}
-		}
-	} else {
-		int pos = req->reply_body - req->sbuf;
-		req->sbuf = g_realloc(req->sbuf, req->sblen + len + 1);
-		memcpy(req->sbuf + req->sblen, buffer, len);
-		req->bytes_read += len;
-		req->sblen += len;
-		req->sbuf[req->sblen] = '\0';
-		req->reply_body = req->sbuf + pos;
-		req->body_size = req->sblen - pos;
-	}
-
-	if ((req->flags & HTTPC_STREAMING) && req->reply_body && req->func != NULL) {
-		req->func(req);
-	}
-
-	return CR_OK;
-}
-
-/* Splits headers and body. Checks result code, in case of 300s it'll handle
-   redirects. If this returns FALSE, don't call any callbacks! */
-static gboolean http_handle_headers(struct http_request *req)
-{
-	char *end1, *end2, *s;
-	int evil_server = 0;
-
-	/* Zero termination is very convenient. */
-	req->reply_headers[req->bytes_read] = '\0';
-
-	/* Find the separation between headers and body, and keep stupid
-	   webservers in mind. */
-	end1 = strstr(req->reply_headers, "\r\n\r\n");
-	end2 = strstr(req->reply_headers, "\n\n");
-
-	if (end2 && end2 < end1) {
-		end1 = end2 + 1;
-		evil_server = 1;
-	} else if (end1) {
-		end1 += 2;
-	} else {
-		req->status_string = g_strdup("Malformed HTTP reply");
-		return TRUE;
-	}
-
-	*end1 = '\0';
-
-	if (getenv("BITLBEE_DEBUG")) {
-		printf("HTTP response headers:\n%s\n", req->reply_headers);
-	}
-
-	if (evil_server) {
-		req->reply_body = end1 + 1;
-	} else {
-		req->reply_body = end1 + 2;
-	}
-
-	/* Separately allocated space for headers and body. */
-	req->sblen = req->body_size = req->reply_headers + req->bytes_read - req->reply_body;
-	req->sbuf = req->reply_body = g_memdup2(req->reply_body, req->body_size + 1);
-	req->reply_headers = g_realloc(req->reply_headers, end1 - req->reply_headers + 1);
-
-	if ((end1 = strchr(req->reply_headers, ' ')) != NULL) {
-		if (sscanf(end1 + 1, "%hd", &req->status_code) != 1) {
-			req->status_string = g_strdup("Can't parse status code");
-			req->status_code = -1;
-		} else {
-			char *eol;
-
-			if (evil_server) {
-				eol = strchr(end1, '\n');
-			} else {
-				eol = strchr(end1, '\r');
-			}
-
-			req->status_string = g_strndup(end1 + 1, eol - end1 - 1);
-
-			/* Just to be sure... */
-			if ((eol = strchr(req->status_string, '\r'))) {
-				*eol = 0;
-			}
-			if ((eol = strchr(req->status_string, '\n'))) {
-				*eol = 0;
-			}
-		}
-	} else {
-		req->status_string = g_strdup("Can't locate status code");
-		req->status_code = -1;
-	}
-
-	if (((req->status_code >= 301 && req->status_code <= 303) ||
-	     req->status_code == 307 || req->status_code == 308) && req->redir_ttl-- > 0) {
-		char *loc, *new_request, *new_host;
-		int error = 0, new_port, new_proto;
-
-		/* We might fill it again, so let's not leak any memory. */
-		g_free(req->status_string);
-		req->status_string = NULL;
-
-		loc = strstr(req->reply_headers, "\nLocation: ");
-		if (loc == NULL) { /* We can't handle this redirect... */
-			req->status_string = g_strdup("Can't locate Location: header");
-			return TRUE;
-		}
-
-		loc += 11;
-		while (*loc == ' ') {
-			loc++;
-		}
-
-		/* TODO/FIXME: Possibly have to handle relative redirections,
-		   and rewrite Host: headers. Not necessary for now, it's
-		   enough for passport authentication like this. */
-
-		if (*loc == '/') {
-			/* Just a different pathname... */
-
-			/* Since we don't cache the servername, and since we
-			   don't need this yet anyway, I won't implement it. */
-
-			req->status_string = g_strdup("Can't handle relative redirects");
-
-			return TRUE;
-		} else {
-			/* A whole URL */
-			url_t *url;
-			char *s, *version, *headers;
-			const char *new_method;
-
-			s = strstr(loc, "\r\n");
-			if (s == NULL) {
-				return TRUE;
-			}
-
-			url = g_new0(url_t, 1);
-			*s = 0;
-
-			if (!url_set(url, loc)) {
-				req->status_string = g_strdup("Malformed redirect URL");
-				g_free(url);
-				return TRUE;
-			}
-
-			/* Find all headers and, if necessary, the POST request contents.
-			   Skip the old Host: header though. This crappy code here means
-			   anything using this http_client MUST put the Host: header at
-			   the top. */
-			if (!((s = strstr(req->request, "\r\nHost: ")) &&
-			      (s = strstr(s + strlen("\r\nHost: "), "\r\n")))) {
-				req->status_string = g_strdup("Error while rebuilding request string");
-				g_free(url);
-				return TRUE;
-			}
-			headers = s;
-
-			/* More or less HTTP/1.0 compliant, from my reading of RFC 2616.
-			   Always perform a GET request unless we received a 301. 303 was
-			   meant for this but it's HTTP/1.1-only and we're specifically
-			   speaking HTTP/1.0. ...
-
-			   Well except someone at identi.ca's didn't bother reading any
-			   RFCs and just return HTTP/1.1-specific status codes to HTTP/1.0
-			   requests. Fuckers. So here we are, handle 301..303,307,308. */
-			if (strncmp(req->request, "GET", 3) == 0) {
-				/* GETs never become POSTs. */
-				new_method = "GET";
-			} else if (req->status_code == 302 || req->status_code == 303) {
-				/* 302 de-facto becomes GET, 303 as specified by RFC 2616#10.3.3 */
-				new_method = "GET";
-			} else {
-				/* 301 de-facto should stay POST, 307/308 specifally RFC 2616#10.3.8 */
-				new_method = "POST";
-			}
-
-			if ((version = strstr(req->request, " HTTP/")) &&
-			    (s = strstr(version, "\r\n"))) {
-				version++;
-				version = g_strndup(version, s - version);
-			} else {
-				version = g_strdup("HTTP/1.0");
-			}
-
-			/* Okay, this isn't fun! We have to rebuild the request... :-( */
-			new_request = g_strdup_printf("%s %s %s\r\nHost: %s%s",
-			                              new_method, url->file, version,
-			                              url->host, headers);
-
-			new_host = g_strdup(url->host);
-			new_port = url->port;
-			new_proto = url->proto;
-
-			/* If we went from POST to GET, truncate the request content. */
-			if (new_request[0] != req->request[0] && new_request[0] == 'G' &&
-			    (s = strstr(new_request, "\r\n\r\n"))) {
-				s[4] = '\0';
-			}
-
-			g_free(url);
-			g_free(version);
-		}
-
-		if (req->ssl) {
-			ssl_disconnect(req->ssl);
-		} else {
-			closesocket(req->fd);
-		}
-
-		req->fd = -1;
-		req->ssl = NULL;
-
-		if (getenv("BITLBEE_DEBUG")) {
-			printf("New headers for redirected HTTP request:\n%s\n", new_request);
-		}
-
-		if (new_proto == PROTO_HTTPS) {
-			req->ssl = ssl_connect(new_host, new_port, TRUE, http_ssl_connected, req);
-			if (req->ssl == NULL) {
-				error = 1;
-			}
-		} else {
-			req->fd = proxy_connect(new_host, new_port, http_connected, req);
-			if (req->fd < 0) {
-				error = 1;
-			}
-		}
-		g_free(new_host);
-
-		if (error) {
-			req->status_string = g_strdup("Connection problem during redirect");
-			g_free(new_request);
-			return TRUE;
-		}
-
-		g_free(req->request);
-		g_free(req->reply_headers);
-		g_free(req->sbuf);
-		req->request = new_request;
-		req->request_length = strlen(new_request);
-		req->bytes_read = req->bytes_written = req->inpa = 0;
-		req->reply_headers = req->reply_body = NULL;
-		req->sbuf = req->cbuf = NULL;
-		req->sblen = req->cblen = 0;
-
-		return FALSE;
-	}
-
-	if ((s = get_rfc822_header(req->reply_headers, "Content-Length", 0)) &&
-	    sscanf(s, "%d", &req->content_length) != 1) {
-		req->content_length = -1;
-	}
-	g_free(s);
-
-	if ((s = get_rfc822_header(req->reply_headers, "Transfer-Encoding", 0))) {
-		if (strcasestr(s, "chunked")) {
-			req->flags |= HTTPC_CHUNKED;
-			req->cbuf = req->sbuf;
-			req->cblen = req->sblen;
-
-			req->reply_body = req->sbuf = g_strdup("");
-			req->body_size = req->sblen = 0;
-		}
-		g_free(s);
-	}
-
-	return TRUE;
-}
-
-void http_flush_bytes(struct http_request *req, size_t len)
-{
-	if (len <= 0 || len > req->body_size || !(req->flags & HTTPC_STREAMING)) {
-		return;
-	}
-
-	req->reply_body += len;
-	req->body_size -= len;
-
-	if (req->reply_body - req->sbuf >= 512) {
-		char *new = g_memdup2(req->reply_body, req->body_size + 1);
-		g_free(req->sbuf);
-		req->reply_body = req->sbuf = new;
-		req->sblen = req->body_size;
-	}
-}
-
-void http_close(struct http_request *req)
-{
-	if (!req) {
-		return;
-	}
-
-	if (req->inpa > 0) {
-		b_event_remove(req->inpa);
-	}
-
-	if (req->ssl) {
-		ssl_disconnect(req->ssl);
-	} else {
-		proxy_disconnect(req->fd);
-	}
-
-	http_free(req);
-}
-
-static void http_free(struct http_request *req)
-{
-	g_free(req->request);
-	g_free(req->reply_headers);
-	g_free(req->status_string);
-	g_free(req->sbuf);
-	g_free(req->cbuf);
-	g_free(req);
-}
Index: b/http_client.h
===================================================================
--- lib/http_client.h	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,99 +1,0 @@
-/********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2012 Wilmer van der Gaast and others                *
-  \********************************************************************/
-
-/* HTTP(S) 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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-/* http_client allows you to talk (asynchronously, again) to HTTP servers.
-   In the "background" it will send the whole query and wait for a complete
-   response to come back. Initially written for MS Passport authentication,
-   but used for many other things now like OAuth and Twitter.
-
-   It's very useful for doing quick requests without blocking the whole
-   program. Unfortunately it doesn't support fancy stuff like HTTP keep-
-   alives. */
-
-#include <glib.h>
-#include "ssl_client.h"
-
-struct http_request;
-
-typedef enum http_client_flags {
-	HTTPC_STREAMING = 1,
-	HTTPC_EOF = 2,
-	HTTPC_CHUNKED = 4,
-
-	/* Let's reserve 0x1000000+ for lib users. */
-} http_client_flags_t;
-
-/* Your callback function should look like this: */
-typedef void (*http_input_function)(struct http_request *);
-
-/* This structure will be filled in by the http_dorequest* functions, and
-   it will be passed to the callback function. Use the data field to add
-   your own data. */
-struct http_request {
-	char *request;          /* The request to send to the server. */
-	int request_length;     /* Its size. */
-	short status_code;      /* The numeric HTTP status code. (Or -1
-	                           if something really went wrong) */
-	char *status_string;    /* The error text. */
-	char *reply_headers;
-	char *reply_body;
-	int body_size;          /* The number of bytes in reply_body. */
-	short redir_ttl;        /* You can set it to 0 if you don't want
-	                           http_client to follow them. */
-
-	http_client_flags_t flags;
-
-	http_input_function func;
-	gpointer data;
-
-	/* Please don't touch the things down here, you shouldn't need them. */
-	void *ssl;
-	int fd;
-
-	int inpa;
-	int bytes_written;
-	int bytes_read;
-	int content_length;     /* "Content-Length:" header or -1 */
-
-	/* Used in streaming mode. Caller should read from reply_body. */
-	char *sbuf;
-	size_t sblen;
-
-	/* Chunked encoding only. Raw chunked stream is decoded from here. */
-	char *cbuf;
-	size_t cblen;
-};
-
-/* The _url variant is probably more useful than the raw version. The raw
-   version is probably only useful if you want to do POST requests or if
-   you want to add some extra headers. As you can see, HTTPS connections
-   are also supported (using ssl_client). */
-struct http_request *http_dorequest(char *host, int port, int ssl, char *request, http_input_function func,
-                                    gpointer data);
-struct http_request *http_dorequest_url(char *url_string, http_input_function func, gpointer data);
-
-/* For streaming connections only; flushes len bytes at the start of the buffer. */
-void http_flush_bytes(struct http_request *req, size_t len);
-void http_close(struct http_request *req);
Index: b/ini.c
===================================================================
--- lib/ini.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,137 +1,0 @@
-/********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2008 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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-#define BITLBEE_CORE
-#include "bitlbee.h"
-
-ini_t *ini_open(char *file)
-{
-	int fd = -1;
-	ini_t *ini = NULL;
-	struct stat fi;
-
-	if (file && (fd = open(file, O_RDONLY)) != -1 &&
-	    fstat(fd, &fi) == 0 &&
-	    fi.st_size <= 16384 &&
-	    (ini = g_malloc(sizeof(ini_t) + fi.st_size + 1)) &&
-	    read(fd, ini->file, fi.st_size) == fi.st_size) {
-		memset(ini, 0, sizeof(ini_t));
-		ini->size = fi.st_size;
-		ini->file[ini->size] = 0;
-		ini->cur = ini->file;
-		ini->c_section = "";
-
-		close(fd);
-
-		return ini;
-	}
-
-	if (fd >= 0) {
-		close(fd);
-	}
-
-	ini_close(ini);
-
-	return NULL;
-}
-
-/* Strips leading and trailing whitespace and returns a pointer to the first
-   non-ws character of the given string. */
-static char *ini_strip_whitespace(char *in)
-{
-	char *e;
-
-	while (g_ascii_isspace(*in)) {
-		in++;
-	}
-
-	e = in + strlen(in) - 1;
-	while (e > in && g_ascii_isspace(*e)) {
-		e--;
-	}
-	e[1] = 0;
-
-	return in;
-}
-
-int ini_read(ini_t *file)
-{
-	char *s;
-
-	while (file->cur && file->cur < file->file + file->size) {
-		char *e, *next;
-
-		file->line++;
-
-		/* Find the end of line */
-		if ((e = strchr(file->cur, '\n')) != NULL) {
-			*e = 0;
-			next = e + 1;
-		} else {
-			/* No more lines. */
-			e = file->cur + strlen(file->cur);
-			next = NULL;
-		}
-
-		/* Comment? */
-		if ((s = strchr(file->cur, '#')) != NULL) {
-			*s = 0;
-		}
-
-		file->cur = ini_strip_whitespace(file->cur);
-
-		if (*file->cur == '[') {
-			file->cur++;
-			if ((s = strchr(file->cur, ']')) != NULL) {
-				*s = 0;
-				file->c_section = file->cur;
-			}
-		} else if ((s = strchr(file->cur, '=')) != NULL) {
-			*s = 0;
-			file->key = ini_strip_whitespace(file->cur);
-			file->value = ini_strip_whitespace(s + 1);
-
-			if ((s = strchr(file->key, '.')) != NULL) {
-				*s = 0;
-				file->section = file->key;
-				file->key = s + 1;
-			} else {
-				file->section = file->c_section;
-			}
-
-			file->cur = next;
-			return 1;
-		}
-		/* else: noise/comment/etc, let's just ignore it. */
-
-		file->cur = next;
-	}
-
-	return 0;
-}
-
-void ini_close(ini_t *file)
-{
-	g_free(file);
-}
Index: b/ini.h
===================================================================
--- lib/ini.h	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,44 +1,0 @@
-/********************************************************************\
-  * 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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#ifndef _INI_H
-#define _INI_H
-
-typedef struct {
-	int line;
-	char *c_section;
-	char *section;
-	char *key;
-	char *value;
-	int size;
-	char *cur, *tok;
-	char file[];
-} ini_t;
-
-ini_t *ini_open(char *file);
-int ini_read(ini_t *file);
-void ini_close(ini_t *file);
-
-#endif
Index: b/json.c
===================================================================
--- lib/json.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,973 +1,0 @@
-/* vim: set et ts=3 sw=3 sts=3 ft=c:
- *
- * Copyright (C) 2012, 2013, 2014 James McLaughlin et al.  All rights reserved.
- * https://github.com/udp/json-parser
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <glib.h>
-
-#include "json.h"
-
-#ifdef _MSC_VER
-   #ifndef _CRT_SECURE_NO_WARNINGS
-      #define _CRT_SECURE_NO_WARNINGS
-   #endif
-#endif
-
-#ifdef __cplusplus
-const struct _json_value json_value_none;    /* zero-d by ctor */
-#else
-const struct _json_value json_value_none = { 0 };
-#endif
-
-#include <stdio.h>
-#include <string.h>
-#include <ctype.h>
-#include <math.h>
-
-typedef unsigned int json_uchar;
-
-static unsigned char hex_value(json_char c)
-{
-	if (g_ascii_isdigit(c)) {
-		return c - '0';
-	}
-
-	switch (c) {
-	case 'a': case 'A': return 0x0A;
-	case 'b': case 'B': return 0x0B;
-	case 'c': case 'C': return 0x0C;
-	case 'd': case 'D': return 0x0D;
-	case 'e': case 'E': return 0x0E;
-	case 'f': case 'F': return 0x0F;
-	default: return 0xFF;
-	}
-}
-
-typedef struct {
-	unsigned long used_memory;
-
-	unsigned int uint_max;
-	unsigned long ulong_max;
-
-	json_settings settings;
-	int first_pass;
-
-} json_state;
-
-static void * default_alloc(size_t size, int zero, void * user_data)
-{
-	return zero ? calloc(1, size) : malloc(size);
-}
-
-static void default_free(void * ptr, void * user_data)
-{
-	free(ptr);
-}
-
-static void * json_alloc(json_state * state, unsigned long size, int zero)
-{
-	if ((state->ulong_max - state->used_memory) < size) {
-		return 0;
-	}
-
-	if (state->settings.max_memory
-	    && (state->used_memory += size) > state->settings.max_memory) {
-		return 0;
-	}
-
-	return state->settings.mem_alloc(size, zero, state->settings.user_data);
-}
-
-static int new_value
-        (json_state * state, json_value ** top, json_value ** root, json_value ** alloc, json_type type)
-{
-	json_value * value;
-	int values_size;
-
-	if (!state->first_pass) {
-		value = *top = *alloc;
-		*alloc = (*alloc)->_reserved.next_alloc;
-
-		if (!*root) {
-			*root = value;
-		}
-
-		switch (value->type) {
-		case json_array:
-
-			if (!(value->u.array.values = (json_value **) json_alloc
-			                                      (state, value->u.array.length * sizeof(json_value *),
-			                                      0))) {
-				return 0;
-			}
-
-			value->u.array.length = 0;
-			break;
-
-		case json_object:
-
-			values_size = sizeof(*value->u.object.values) * value->u.object.length;
-
-			if (!((*(void **) &value->u.object.values) = json_alloc
-			                                                     (state, values_size +
-			                                                     ((unsigned long) value->u.object.values),
-			                                                     0))) {
-				return 0;
-			}
-
-			value->_reserved.object_mem = (void *) (((char *) value->u.object.values) + values_size);
-
-			value->u.object.length = 0;
-			break;
-
-		case json_string:
-
-			if (!(value->u.string.ptr = (json_char *) json_alloc
-			                                    (state, (value->u.string.length + 1) * sizeof(json_char),
-			                                    0))) {
-				return 0;
-			}
-
-			value->u.string.length = 0;
-			break;
-
-		default:
-			break;
-		}
-		;
-
-		return 1;
-	}
-
-	value = (json_value *) json_alloc(state, sizeof(json_value), 1);
-
-	if (!value) {
-		return 0;
-	}
-
-	if (!*root) {
-		*root = value;
-	}
-
-	value->type = type;
-	value->parent = *top;
-
-	if (*alloc) {
-		(*alloc)->_reserved.next_alloc = value;
-	}
-
-	*alloc = *top = value;
-
-	return 1;
-}
-
-#define e_off \
-	((int) (i - cur_line_begin))
-
-#define whitespace \
-case '\n': ++cur_line;  cur_line_begin = i; \
-case ' ': case '\t': case '\r'
-
-#define string_add(b)  \
-	do { if (!state.first_pass) { string [string_length] = b; } ++string_length; } while (0);
-
-static const long
-        flag_next             = 1 << 0,
-        flag_reproc           = 1 << 1,
-        flag_need_comma       = 1 << 2,
-        flag_seek_value       = 1 << 3,
-        flag_escaped          = 1 << 4,
-        flag_string           = 1 << 5,
-        flag_need_colon       = 1 << 6,
-        flag_done             = 1 << 7,
-        flag_num_negative     = 1 << 8,
-        flag_num_zero         = 1 << 9,
-        flag_num_e            = 1 << 10,
-        flag_num_e_got_sign   = 1 << 11,
-        flag_num_e_negative   = 1 << 12,
-        flag_line_comment     = 1 << 13,
-        flag_block_comment    = 1 << 14;
-
-json_value * json_parse_ex(json_settings * settings,
-                           const json_char * json,
-                           size_t length,
-                           char * error_buf)
-{
-	json_char error [json_error_max];
-	unsigned int cur_line;
-	const json_char * cur_line_begin, * i, * end;
-	json_value * top, * root, * alloc = 0;
-	json_state state = { 0 };
-	long flags;
-	long num_digits = 0, num_e = 0;
-	json_int_t num_fraction = 0;
-
-	/* Skip UTF-8 BOM
-	 */
-	if (length >= 3 && ((unsigned char) json [0]) == 0xEF
-	    && ((unsigned char) json [1]) == 0xBB
-	    && ((unsigned char) json [2]) == 0xBF) {
-		json += 3;
-		length -= 3;
-	}
-
-	error[0] = '\0';
-	end = (json + length);
-
-	memcpy(&state.settings, settings, sizeof(json_settings));
-
-	if (!state.settings.mem_alloc) {
-		state.settings.mem_alloc = default_alloc;
-	}
-
-	if (!state.settings.mem_free) {
-		state.settings.mem_free = default_free;
-	}
-
-	memset(&state.uint_max, 0xFF, sizeof(state.uint_max));
-	memset(&state.ulong_max, 0xFF, sizeof(state.ulong_max));
-
-	state.uint_max -= 8; /* limit of how much can be added before next check */
-	state.ulong_max -= 8;
-
-	for (state.first_pass = 1; state.first_pass >= 0; --state.first_pass) {
-		json_uchar uchar;
-		unsigned char uc_b1, uc_b2, uc_b3, uc_b4;
-		json_char * string = 0;
-		unsigned int string_length = 0;
-
-		top = root = 0;
-		flags = flag_seek_value;
-
-		cur_line = 1;
-		cur_line_begin = json;
-
-		for (i = json;; ++i) {
-			json_char b = (i == end ? 0 : *i);
-
-			if (flags & flag_string) {
-				if (!b) {
-					sprintf(error, "Unexpected EOF in string (at %d:%d)", cur_line, e_off);
-					goto e_failed;
-				}
-
-				if (string_length > state.uint_max) {
-					goto e_overflow;
-				}
-
-				if (flags & flag_escaped) {
-					flags &= ~flag_escaped;
-
-					switch (b) {
-					case 'b':  string_add('\b');  break;
-					case 'f':  string_add('\f');  break;
-					case 'n':  string_add('\n');  break;
-					case 'r':  string_add('\r');  break;
-					case 't':  string_add('\t');  break;
-					case 'u':
-
-						if (end - i < 4 ||
-						    (uc_b1 = hex_value(*++i)) == 0xFF || (uc_b2 = hex_value(*++i)) ==
-						    0xFF
-						    || (uc_b3 = hex_value(*++i)) == 0xFF || (uc_b4 = hex_value(*++i)) ==
-						    0xFF) {
-							sprintf(error, "Invalid character value `%c` (at %d:%d)", b,
-							        cur_line, e_off);
-							goto e_failed;
-						}
-
-						uc_b1 = (uc_b1 << 4) | uc_b2;
-						uc_b2 = (uc_b3 << 4) | uc_b4;
-						uchar = (uc_b1 << 8) | uc_b2;
-
-						if ((uchar & 0xF800) == 0xD800) {
-							json_uchar uchar2;
-
-							if (end - i < 6 || (*++i) != '\\' || (*++i) != 'u' ||
-							    (uc_b1 = hex_value(*++i)) == 0xFF ||
-							    (uc_b2 = hex_value(*++i)) == 0xFF
-							    || (uc_b3 = hex_value(*++i)) == 0xFF ||
-							    (uc_b4 = hex_value(*++i)) == 0xFF) {
-								sprintf(error,
-								        "Invalid character value `%c` (at %d:%d)", b,
-								        cur_line, e_off);
-								goto e_failed;
-							}
-
-							uc_b1 = (uc_b1 << 4) | uc_b2;
-							uc_b2 = (uc_b3 << 4) | uc_b4;
-							uchar2 = (uc_b1 << 8) | uc_b2;
-
-							uchar = 0x010000 | ((uchar & 0x3FF) << 10) | (uchar2 & 0x3FF);
-						}
-
-						if (sizeof(json_char) >= sizeof(json_uchar) || (uchar <= 0x7F)) {
-							string_add((json_char) uchar);
-							break;
-						}
-
-						if (uchar <= 0x7FF) {
-							if (state.first_pass) {
-								string_length += 2;
-							} else {                    string [string_length++] = 0xC0 |
-								                                               (uchar >>
-								                                                6);
-								                    string [string_length++] = 0x80 |
-								                                               (uchar &
-								                                                0x3F); }
-
-							break;
-						}
-
-						if (uchar <= 0xFFFF) {
-							if (state.first_pass) {
-								string_length += 3;
-							} else {                    string [string_length++] = 0xE0 |
-								                                               (uchar >>
-								                                                12);
-								                    string [string_length++] = 0x80 |
-								                                               ((uchar
-								                                                 >> 6) &
-								                                                0x3F);
-								                    string [string_length++] = 0x80 |
-								                                               (uchar &
-								                                                0x3F); }
-
-							break;
-						}
-
-						if (state.first_pass) {
-							string_length += 4;
-						} else {                string [string_length++] = 0xF0 | (uchar >> 18);
-							                string [string_length++] = 0x80 |
-							                                           ((uchar >>
-							                                             12) & 0x3F);
-							                string [string_length++] = 0x80 |
-							                                           ((uchar >>
-							                                             6) & 0x3F);
-							                string [string_length++] = 0x80 |
-							                                           (uchar & 0x3F); }
-
-						break;
-
-					default:
-						string_add(b);
-					}
-					;
-
-					continue;
-				}
-
-				if (b == '\\') {
-					flags |= flag_escaped;
-					continue;
-				}
-
-				if (b == '"') {
-					if (!state.first_pass) {
-						string [string_length] = 0;
-					}
-
-					flags &= ~flag_string;
-					string = 0;
-
-					switch (top->type) {
-					case json_string:
-
-						top->u.string.length = string_length;
-						flags |= flag_next;
-
-						break;
-
-					case json_object:
-
-						if (state.first_pass) {
-							json_char **chars = (json_char **) &top->u.object.values;
-							chars[0] += string_length + 1;
-						} else {
-							top->u.object.values [top->u.object.length].name
-							        = (json_char *) top->_reserved.object_mem;
-
-							top->u.object.values [top->u.object.length].name_length
-							        = string_length;
-
-							(*(json_char **) &top->_reserved.object_mem) += string_length +
-							                                                1;
-						}
-
-						flags |= flag_seek_value | flag_need_colon;
-						continue;
-
-					default:
-						break;
-					}
-					;
-				} else {
-					string_add(b);
-					continue;
-				}
-			}
-
-			if (state.settings.settings & json_enable_comments) {
-				if (flags & (flag_line_comment | flag_block_comment)) {
-					if (flags & flag_line_comment) {
-						if (b == '\r' || b == '\n' || !b) {
-							flags &= ~flag_line_comment;
-							--i; /* so null can be reproc'd */
-						}
-
-						continue;
-					}
-
-					if (flags & flag_block_comment) {
-						if (!b) {
-							sprintf(error, "%d:%d: Unexpected EOF in block comment",
-							        cur_line, e_off);
-							goto e_failed;
-						}
-
-						if (b == '*' && i < (end - 1) && i [1] == '/') {
-							flags &= ~flag_block_comment;
-							++i; /* skip closing sequence */
-						}
-
-						continue;
-					}
-				} else if (b == '/') {
-					if (!(flags & (flag_seek_value | flag_done)) && top->type != json_object) {
-						sprintf(error, "%d:%d: Comment not allowed here", cur_line, e_off);
-						goto e_failed;
-					}
-
-					if (++i == end) {
-						sprintf(error, "%d:%d: EOF unexpected", cur_line, e_off);
-						goto e_failed;
-					}
-
-					switch (b = *i) {
-					case '/':
-						flags |= flag_line_comment;
-						continue;
-
-					case '*':
-						flags |= flag_block_comment;
-						continue;
-
-					default:
-						sprintf(error, "%d:%d: Unexpected `%c` in comment opening sequence",
-						        cur_line, e_off, b);
-						goto e_failed;
-					}
-					;
-				}
-			}
-
-			if (flags & flag_done) {
-				if (!b) {
-					break;
-				}
-
-				switch (b) {
-whitespace:
-					continue;
-
-				default:
-					sprintf(error, "%d:%d: Trailing garbage: `%c`", cur_line, e_off, b);
-					goto e_failed;
-				}
-				;
-			}
-
-			if (flags & flag_seek_value) {
-				switch (b) {
-whitespace:
-					continue;
-
-				case ']':
-
-					if (top && top->type == json_array) {
-						flags = (flags & ~(flag_need_comma | flag_seek_value)) | flag_next;
-					} else {              sprintf(error, "%d:%d: Unexpected ]", cur_line, e_off);
-						              goto e_failed; }
-
-					break;
-
-				default:
-
-					if (flags & flag_need_comma) {
-						if (b == ',') {
-							flags &= ~flag_need_comma;
-							continue;
-						} else { sprintf(error, "%d:%d: Expected , before %c", cur_line, e_off,
-							         b);
-							 goto e_failed; }
-					}
-
-					if (flags & flag_need_colon) {
-						if (b == ':') {
-							flags &= ~flag_need_colon;
-							continue;
-						} else { sprintf(error, "%d:%d: Expected : before %c", cur_line, e_off,
-							         b);
-							 goto e_failed; }
-					}
-
-					flags &= ~flag_seek_value;
-
-					switch (b) {
-					case '{':
-
-						if (!new_value(&state, &top, &root, &alloc, json_object)) {
-							goto e_alloc_failure;
-						}
-
-						continue;
-
-					case '[':
-
-						if (!new_value(&state, &top, &root, &alloc, json_array)) {
-							goto e_alloc_failure;
-						}
-
-						flags |= flag_seek_value;
-						continue;
-
-					case '"':
-
-						if (!new_value(&state, &top, &root, &alloc, json_string)) {
-							goto e_alloc_failure;
-						}
-
-						flags |= flag_string;
-
-						string = top->u.string.ptr;
-						string_length = 0;
-
-						continue;
-
-					case 't':
-
-						if ((end - i) < 3 || *(++i) != 'r' || *(++i) != 'u' || *(++i) != 'e') {
-							goto e_unknown_value;
-						}
-
-						if (!new_value(&state, &top, &root, &alloc, json_boolean)) {
-							goto e_alloc_failure;
-						}
-
-						top->u.boolean = 1;
-
-						flags |= flag_next;
-						break;
-
-					case 'f':
-
-						if ((end - i) < 4 || *(++i) != 'a' || *(++i) != 'l' || *(++i) != 's' ||
-						    *(++i) != 'e') {
-							goto e_unknown_value;
-						}
-
-						if (!new_value(&state, &top, &root, &alloc, json_boolean)) {
-							goto e_alloc_failure;
-						}
-
-						flags |= flag_next;
-						break;
-
-					case 'n':
-
-						if ((end - i) < 3 || *(++i) != 'u' || *(++i) != 'l' || *(++i) != 'l') {
-							goto e_unknown_value;
-						}
-
-						if (!new_value(&state, &top, &root, &alloc, json_null)) {
-							goto e_alloc_failure;
-						}
-
-						flags |= flag_next;
-						break;
-
-					default:
-
-						if (g_ascii_isdigit(b) || b == '-') {
-							if (!new_value(&state, &top, &root, &alloc, json_integer)) {
-								goto e_alloc_failure;
-							}
-
-							if (!state.first_pass) {
-								while (g_ascii_isdigit(b) || b == '+' || b == '-'
-								       || b == 'e' || b == 'E' || b == '.') {
-									if ((++i) == end) {
-										b = 0;
-										break;
-									}
-
-									b = *i;
-								}
-
-								flags |= flag_next | flag_reproc;
-								break;
-							}
-
-							flags &= ~(flag_num_negative | flag_num_e |
-							           flag_num_e_got_sign | flag_num_e_negative |
-							           flag_num_zero);
-
-							num_digits = 0;
-							num_fraction = 0;
-							num_e = 0;
-
-							if (b != '-') {
-								flags |= flag_reproc;
-								break;
-							}
-
-							flags |= flag_num_negative;
-							continue;
-						} else { sprintf(error, "%d:%d: Unexpected %c when seeking value",
-							         cur_line, e_off, b);
-							 goto e_failed; }
-					}
-					;
-				}
-				;
-			} else {
-				switch (top->type) {
-				case json_object:
-
-					switch (b) {
-whitespace:
-						continue;
-
-					case '"':
-
-						if (flags & flag_need_comma) {
-							sprintf(error, "%d:%d: Expected , before \"", cur_line, e_off);
-							goto e_failed;
-						}
-
-						flags |= flag_string;
-
-						string = (json_char *) top->_reserved.object_mem;
-						string_length = 0;
-
-						break;
-
-					case '}':
-
-						flags = (flags & ~flag_need_comma) | flag_next;
-						break;
-
-					case ',':
-
-						if (flags & flag_need_comma) {
-							flags &= ~flag_need_comma;
-							break;
-						}
-
-					default:
-
-						sprintf(error, "%d:%d: Unexpected `%c` in object", cur_line, e_off, b);
-						goto e_failed;
-					}
-					;
-
-					break;
-
-				case json_integer:
-				case json_double:
-
-					if (g_ascii_isdigit(b)) {
-						++num_digits;
-
-						if (top->type == json_integer || flags & flag_num_e) {
-							if (!(flags & flag_num_e)) {
-								if (flags & flag_num_zero) {
-									sprintf(error,
-									        "%d:%d: Unexpected `0` before `%c`",
-									        cur_line, e_off, b);
-									goto e_failed;
-								}
-
-								if (num_digits == 1 && b == '0') {
-									flags |= flag_num_zero;
-								}
-							} else {
-								flags |= flag_num_e_got_sign;
-								num_e = (num_e * 10) + (b - '0');
-								continue;
-							}
-
-							top->u.integer = (top->u.integer * 10) + (b - '0');
-							continue;
-						}
-
-						num_fraction = (num_fraction * 10) + (b - '0');
-						continue;
-					}
-
-					if (b == '+' || b == '-') {
-						if ((flags & flag_num_e) && !(flags & flag_num_e_got_sign)) {
-							flags |= flag_num_e_got_sign;
-
-							if (b == '-') {
-								flags |= flag_num_e_negative;
-							}
-
-							continue;
-						}
-					} else if (b == '.' && top->type == json_integer) {
-						if (!num_digits) {
-							sprintf(error, "%d:%d: Expected digit before `.`", cur_line,
-							        e_off);
-							goto e_failed;
-						}
-
-						top->type = json_double;
-						top->u.dbl = (double) top->u.integer;
-
-						num_digits = 0;
-						continue;
-					}
-
-					if (!(flags & flag_num_e)) {
-						if (top->type == json_double) {
-							if (!num_digits) {
-								sprintf(error, "%d:%d: Expected digit after `.`",
-								        cur_line, e_off);
-								goto e_failed;
-							}
-
-							top->u.dbl += ((double) num_fraction) /
-							              (pow(10, (double) num_digits));
-						}
-
-						if (b == 'e' || b == 'E') {
-							flags |= flag_num_e;
-
-							if (top->type == json_integer) {
-								top->type = json_double;
-								top->u.dbl = (double) top->u.integer;
-							}
-
-							num_digits = 0;
-							flags &= ~flag_num_zero;
-
-							continue;
-						}
-					} else {
-						if (!num_digits) {
-							sprintf(error, "%d:%d: Expected digit after `e`", cur_line,
-							        e_off);
-							goto e_failed;
-						}
-
-						top->u.dbl *=
-						        pow(10,
-						            (double) (flags & flag_num_e_negative ? -num_e : num_e));
-					}
-
-					if (flags & flag_num_negative) {
-						if (top->type == json_integer) {
-							top->u.integer = -top->u.integer;
-						} else {
-							top->u.dbl = -top->u.dbl;
-						}
-					}
-
-					flags |= flag_next | flag_reproc;
-					break;
-
-				default:
-					break;
-				}
-				;
-			}
-
-			if (flags & flag_reproc) {
-				flags &= ~flag_reproc;
-				--i;
-			}
-
-			if (flags & flag_next) {
-				flags = (flags & ~flag_next) | flag_need_comma;
-
-				if (!top->parent) {
-					/* root value done */
-
-					flags |= flag_done;
-					continue;
-				}
-
-				if (top->parent->type == json_array) {
-					flags |= flag_seek_value;
-				}
-
-				if (!state.first_pass) {
-					json_value * parent = top->parent;
-
-					switch (parent->type) {
-					case json_object:
-
-						parent->u.object.values
-						[parent->u.object.length].value = top;
-
-						break;
-
-					case json_array:
-
-						parent->u.array.values
-						[parent->u.array.length] = top;
-
-						break;
-
-					default:
-						break;
-					}
-					;
-				}
-
-				if ((++top->parent->u.array.length) > state.uint_max) {
-					goto e_overflow;
-				}
-
-				top = top->parent;
-
-				continue;
-			}
-		}
-
-		alloc = root;
-	}
-
-	return root;
-
-e_unknown_value:
-
-	sprintf(error, "%d:%d: Unknown value", cur_line, e_off);
-	goto e_failed;
-
-e_alloc_failure:
-
-	strcpy(error, "Memory allocation failure");
-	goto e_failed;
-
-e_overflow:
-
-	sprintf(error, "%d:%d: Too long (caught overflow)", cur_line, e_off);
-	goto e_failed;
-
-e_failed:
-
-	if (error_buf) {
-		if (*error) {
-			strcpy(error_buf, error);
-		} else {
-			strcpy(error_buf, "Unknown error");
-		}
-	}
-
-	if (state.first_pass) {
-		alloc = root;
-	}
-
-	while (alloc) {
-		top = alloc->_reserved.next_alloc;
-		state.settings.mem_free(alloc, state.settings.user_data);
-		alloc = top;
-	}
-
-	if (!state.first_pass) {
-		json_value_free_ex(&state.settings, root);
-	}
-
-	return 0;
-}
-
-json_value * json_parse(const json_char * json, size_t length)
-{
-	json_settings settings = { 0 };
-
-	return json_parse_ex(&settings, json, length, 0);
-}
-
-void json_value_free_ex(json_settings * settings, json_value * value)
-{
-	json_value * cur_value;
-
-	if (!value) {
-		return;
-	}
-
-	value->parent = 0;
-
-	while (value) {
-		switch (value->type) {
-		case json_array:
-
-			if (!value->u.array.length) {
-				settings->mem_free(value->u.array.values, settings->user_data);
-				break;
-			}
-
-			value = value->u.array.values [--value->u.array.length];
-			continue;
-
-		case json_object:
-
-			if (!value->u.object.length) {
-				settings->mem_free(value->u.object.values, settings->user_data);
-				break;
-			}
-
-			value = value->u.object.values [--value->u.object.length].value;
-			continue;
-
-		case json_string:
-
-			settings->mem_free(value->u.string.ptr, settings->user_data);
-			break;
-
-		default:
-			break;
-		}
-		;
-
-		cur_value = value;
-		value = value->parent;
-		settings->mem_free(cur_value, settings->user_data);
-	}
-}
-
-void json_value_free(json_value * value)
-{
-	json_settings settings = { 0 };
-
-	settings.mem_free = default_free;
-	json_value_free_ex(&settings, value);
-}
-
Index: b/json.h
===================================================================
--- lib/json.h	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,266 +1,0 @@
-
-/* vim: set et ts=3 sw=3 sts=3 ft=c:
- *
- * Copyright (C) 2012, 2013, 2014 James McLaughlin et al.  All rights reserved.
- * https://github.com/udp/json-parser
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifdef USE_EXTERNAL_JSON_PARSER
-#error Bitlbee was configured to use system json-parser, this header file should not be used.
-#endif
-
-#ifndef _JSON_H
-#define _JSON_H
-
-#ifndef json_char
-   #define json_char char
-#endif
-
-#ifndef json_int_t
-   #ifndef _MSC_VER
-      #include <inttypes.h>
-      #define json_int_t int64_t
-   #else
-      #define json_int_t __int64
-   #endif
-#endif
-
-#include <stdlib.h>
-
-#ifdef __cplusplus
-
-   #include <string.h>
-
-extern "C"
-{
-
-#endif
-
-typedef struct {
-	unsigned long max_memory;
-	int settings;
-
-	/* Custom allocator support (leave null to use malloc/free)
-	 */
-
-	void * (*mem_alloc)(size_t, int zero, void * user_data);
-	void (* mem_free)(void *, void * user_data);
-
-	void * user_data; /* will be passed to mem_alloc and mem_free */
-
-} json_settings;
-
-#define json_enable_comments  0x01
-
-typedef enum {
-	json_none,
-	json_object,
-	json_array,
-	json_integer,
-	json_double,
-	json_string,
-	json_boolean,
-	json_null
-
-} json_type;
-
-extern const struct _json_value json_value_none;
-
-typedef struct _json_value {
-	struct _json_value * parent;
-
-	json_type type;
-
-	union {
-		int boolean;
-		json_int_t integer;
-		double dbl;
-
-		struct {
-			unsigned int length;
-			json_char * ptr; /* null terminated */
-
-		} string;
-
-		struct {
-			unsigned int length;
-
-			struct {
-				json_char * name;
-				unsigned int name_length;
-
-				struct _json_value * value;
-
-			} * values;
-
-	 #if defined(__cplusplus) && __cplusplus >= 201103L
-			decltype(values) begin() const
-			{  return values; }
-			decltype(values) end() const
-			{  return values + length; }
-	 #endif
-
-		} object;
-
-		struct {
-			unsigned int length;
-			struct _json_value ** values;
-
-	 #if defined(__cplusplus) && __cplusplus >= 201103L
-			decltype(values) begin() const
-			{  return values; }
-			decltype(values) end() const
-			{  return values + length; }
-	 #endif
-
-		} array;
-
-	} u;
-
-	union {
-		struct _json_value * next_alloc;
-		void * object_mem;
-
-	} _reserved;
-
-
-	/* Some C++ operator sugar */
-
-   #ifdef __cplusplus
-
-public:
-
-	inline _json_value ()
-	{
-		memset(this, 0, sizeof(_json_value));
-	}
-
-	inline const struct _json_value &operator [](int index) const
-	{
-		if (type != json_array || index < 0
-		    || ((unsigned int)index) >= u.array.length) {
-			return json_value_none;
-		}
-
-		return *u.array.values [index];
-	}
-
-	inline const struct _json_value &operator [](const char * index) const
-	{
-		if (type != json_object) {
-			return json_value_none;
-		}
-
-		for (unsigned int i = 0; i < u.object.length; ++i) {
-			if (!strcmp(u.object.values [i].name, index)) {
-				return *u.object.values [i].value;
-			}
-		}
-
-		return json_value_none;
-	}
-
-	inline operator const char *() const
-	{
-		switch (type) {
-		case json_string:
-			return u.string.ptr;
-
-		default:
-			return "";
-		}
-		;
-	}
-
-	inline operator json_int_t() const
-	{
-		switch (type) {
-		case json_integer:
-			return u.integer;
-
-		case json_double:
-			return (json_int_t)u.dbl;
-
-		default:
-			return 0;
-		}
-		;
-	}
-
-	inline operator bool() const
-	{
-		if (type != json_boolean) {
-			return false;
-		}
-
-		return u.boolean != 0;
-	}
-
-	inline operator double() const
-	{
-		switch (type) {
-		case json_integer:
-			return (double)u.integer;
-
-		case json_double:
-			return u.dbl;
-
-		default:
-			return 0;
-		}
-		;
-	}
-
-   #endif
-
-} json_value;
-
-json_value * json_parse(const json_char * json,
-                        size_t length);
-
-#define json_error_max 128
-json_value * json_parse_ex(json_settings * settings,
-                           const json_char * json,
-                           size_t length,
-                           char * error);
-
-void json_value_free(json_value *);
-
-
-/* Not usually necessary, unless you used a custom mem_alloc and now want to
- * use a custom mem_free.
- */
-void json_value_free_ex(json_settings * settings,
-                        json_value *);
-
-
-#ifdef __cplusplus
-}    /* extern "C" */
-#endif
-
-#endif
-
-
Index: b/json_util.c
===================================================================
--- lib/json_util.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,72 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Helper functions for json.c                                              *
-*                                                                           *
-*  Copyright 2012-2012 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.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-****************************************************************************/
-
-#include <stdlib.h>
-#include <string.h>
-#include <glib.h>
-
-#include "json_util.h"
-
-/* g_memdup() deprecated as of glib 2.68.0 */
-#ifndef GLIB_VERSION_2_68
-#define g_memdup2 g_memdup
-#endif
-
-json_value *json_o_get(const json_value *obj, const json_char *name)
-{
-	int i;
-
-	if (!obj || obj->type != json_object) {
-		return NULL;
-	}
-
-	for (i = 0; i < obj->u.object.length; ++i) {
-		if (strcmp(obj->u.object.values[i].name, name) == 0) {
-			return obj->u.object.values[i].value;
-		}
-	}
-
-	return NULL;
-}
-
-const char *json_o_str(const json_value *obj, const json_char *name)
-{
-	json_value *ret = json_o_get(obj, name);
-
-	if (ret && ret->type == json_string) {
-		return ret->u.string.ptr;
-	} else {
-		return NULL;
-	}
-}
-
-char *json_o_strdup(const json_value *obj, const json_char *name)
-{
-	json_value *ret = json_o_get(obj, name);
-
-	if (ret && ret->type == json_string && ret->u.string.ptr) {
-		return g_memdup2(ret->u.string.ptr, ret->u.string.length + 1);
-	} else {
-		return NULL;
-	}
-}
Index: b/json_util.h
===================================================================
--- lib/json_util.h	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,35 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Helper functions for json.c                                              *
-*                                                                           *
-*  Copyright 2012-2012 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.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-****************************************************************************/
-
-#include <json.h>
-
-#define JSON_O_FOREACH(o, k, v) \
-	char *k; json_value *v; int __i; \
-	for (__i = 0; (__i < (o)->u.object.length) && \
-	     (k = (o)->u.object.values[__i].name) && \
-	     (v = (o)->u.object.values[__i].value); \
-	     __i ++)
-
-json_value *json_o_get(const json_value *obj, const json_char *name);
-const char *json_o_str(const json_value *obj, const json_char *name);
-char *json_o_strdup(const json_value *obj, const json_char *name);
Index: b/md5.c
===================================================================
--- lib/md5.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,40 +1,0 @@
-#include "md5.h"
-
-/* Creates a new GChecksum in ctx */
-void md5_init(md5_state_t *ctx)
-{
-	*ctx = g_checksum_new(G_CHECKSUM_MD5);
-}
-
-/* Wrapper for g_checksum_update */
-void md5_append(md5_state_t *ctx, const guint8 *buf, unsigned int len)
-{
-	g_checksum_update(*ctx, buf, len);
-}
-
-/* Wrapper for g_checksum_get_digest
- * Also takes care of g_checksum_free(), since it can't be reused anyway
- * (the GChecksum is closed after get_digest) */
-void md5_finish(md5_state_t *ctx, guint8 digest[MD5_HASH_SIZE])
-{
-	gsize digest_len = MD5_HASH_SIZE;
-
-	g_checksum_get_digest(*ctx, digest, &digest_len);
-	g_checksum_free(*ctx);
-}
-
-/* Variant of md5_finish that copies the GChecksum
- * and finishes that one instead of the original */
-void md5_digest_keep(md5_state_t *ctx, guint8 digest[MD5_HASH_SIZE])
-{
-	gsize digest_len = MD5_HASH_SIZE;
-	GChecksum *copy = g_checksum_copy(*ctx);
-
-	g_checksum_get_digest(copy, digest, &digest_len);
-	g_checksum_free(copy);
-}
-
-void md5_free(md5_state_t *ctx)
-{
-	g_checksum_free(*ctx);
-}
Index: b/md5.h
===================================================================
--- lib/md5.h	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,24 +1,0 @@
-#ifndef _MD5_H
-#define _MD5_H
-
-#include <glib.h>
-#include <gmodule.h>
-
-typedef guint8 md5_byte_t;
-typedef GChecksum *md5_state_t;
-
-#define MD5_HASH_SIZE 16
-
-#ifdef __GNUC__
-#define __MD5_NON_PUBLIC_DEPRECATION__ __attribute__((deprecated("md5.h will be removed from Bitlbee's public API. Please use another library (such as GLib's gchecksum) instead")))
-#else
-#define __MD5_NON_PUBLIC_DEPRECATION__
-#endif
-
-void md5_init(md5_state_t *) __MD5_NON_PUBLIC_DEPRECATION__;
-void md5_append(md5_state_t *, const guint8 *, unsigned int) __MD5_NON_PUBLIC_DEPRECATION__;
-void md5_finish(md5_state_t *, guint8 digest[MD5_HASH_SIZE]) __MD5_NON_PUBLIC_DEPRECATION__;
-void md5_digest_keep(md5_state_t *, guint8 digest[MD5_HASH_SIZE]) __MD5_NON_PUBLIC_DEPRECATION__;
-void md5_free(md5_state_t *) __MD5_NON_PUBLIC_DEPRECATION__;
-
-#endif
Index: b/misc.c
===================================================================
--- lib/misc.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,821 +1,0 @@
-  /********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2012 Wilmer van der Gaast and others                *
-  \********************************************************************/
-
-/*
- * Various utility functions. Some are copied from Gaim to support the
- * IM-modules, most are from BitlBee.
- *
- * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
- *                          (and possibly other members of the Gaim team)
- * Copyright 2002-2012 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 with
-  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
-  if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#define BITLBEE_CORE
-#include "nogaim.h"
-#include "base64.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include <glib.h>
-#include <time.h>
-
-#ifdef HAVE_RESOLV_A
-#include <arpa/nameser.h>
-#include <resolv.h>
-#endif
-
-#include "ssl_client.h"
-
-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);
-}
-
-time_t get_time(int year, int month, int day, int hour, int min, int sec)
-{
-	struct tm tm;
-
-	memset(&tm, 0, sizeof(struct 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);
-}
-
-time_t mktime_utc(struct tm *tp)
-{
-	struct tm utc;
-	time_t res, tres;
-
-	tp->tm_isdst = -1;
-	res = mktime(tp);
-	/* Problem is, mktime() just gave us the GMT timestamp for the
-	   given local time... While the given time WAS NOT local. So
-	   we should fix this now.
-
-	   Now I could choose between messing with environment variables
-	   (kludgy) or using timegm() (not portable)... Or doing the
-	   following, which I actually prefer...
-
-	   tzset() may also work but in other places I actually want to
-	   use local time.
-
-	   FFFFFFFFFFFFFFFFFFFFFUUUUUUUUUUUUUUUUUUUU!! */
-	gmtime_r(&res, &utc);
-	utc.tm_isdst = -1;
-	if (utc.tm_hour == tp->tm_hour && utc.tm_min == tp->tm_min) {
-		/* Sweet! We're in UTC right now... */
-		return res;
-	}
-
-	tres = mktime(&utc);
-	res += res - tres;
-
-	/* Yes, this is a hack. And it will go wrong around DST changes.
-	   BUT this is more likely to be threadsafe than messing with
-	   environment variables, and possibly more portable... */
-
-	return res;
-}
-
-typedef struct htmlentity {
-	char code[7];
-	char is[3];
-} htmlentity_t;
-
-static const htmlentity_t ent[] =
-{
-	{ "lt",     "<" },
-	{ "gt",     ">" },
-	{ "amp",    "&" },
-	{ "apos",   "'" },
-	{ "quot",   "\"" },
-	{ "aacute", "á" },
-	{ "eacute", "é" },
-	{ "iacute", "é" },
-	{ "oacute", "ó" },
-	{ "uacute", "ú" },
-	{ "agrave", "à" },
-	{ "egrave", "è" },
-	{ "igrave", "ì" },
-	{ "ograve", "ò" },
-	{ "ugrave", "ù" },
-	{ "acirc",  "â" },
-	{ "ecirc",  "ê" },
-	{ "icirc",  "î" },
-	{ "ocirc",  "ô" },
-	{ "ucirc",  "û" },
-	{ "auml",   "ä" },
-	{ "euml",   "ë" },
-	{ "iuml",   "ï" },
-	{ "ouml",   "ö" },
-	{ "uuml",   "ü" },
-	{ "nbsp",   " " },
-	{ "#38",    "&" },
-	{ "#39",    "'" },
-	{ "#60",    "<" },
-	{ "#62",    ">" },
-	{ "",        ""  }
-};
-
-void strip_html(char *in)
-{
-	char *start = in;
-	char out[strlen(in) + 1];
-	char *s = out, *cs;
-	int i, matched;
-	int taglen;
-
-	memset(out, 0, sizeof(out));
-
-	while (*in) {
-		if (*in == '<' && (g_ascii_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++;
-			}
-
-			taglen = in - cs - 1;   /* not <0 because the above loop runs at least once */
-			if (*in) {
-				if (g_strncasecmp(cs + 1, "b", taglen) == 0) {
-					*(s++) = '\x02';
-				} else if (g_strncasecmp(cs + 1, "/b", taglen) == 0) {
-					*(s++) = '\x02';
-				} else if (g_strncasecmp(cs + 1, "i", taglen) == 0) {
-					*(s++) = '\x1f';
-				} else if (g_strncasecmp(cs + 1, "/i", taglen) == 0) {
-					*(s++) = '\x1f';
-				} else if (g_strncasecmp(cs + 1, "br", taglen) == 0) {
-					*(s++) = '\n';
-				} else if (g_strncasecmp(cs + 1, "br/", taglen) == 0) {
-					*(s++) = '\n';
-				} else if (g_strncasecmp(cs + 1, "br /", taglen) == 0) {
-					*(s++) = '\n';
-				}
-				in++;
-			} else {
-				in = cs;
-				*(s++) = *(in++);
-			}
-		} else if (*in == '&') {
-			cs = ++in;
-
-			if (*in == '#') {
-			    	in++;
-			}
-
-			while (*in && g_ascii_isalnum(*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) {
-					int j;
-
-					for (j = 0; ent[i].is[j]; j++) {
-						*(s++) = ent[i].is[j];
-					}
-
-					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);
-}
-
-char *escape_html(const char *html)
-{
-	const char *c = html;
-	GString *ret;
-	char *str;
-
-	if (html == NULL) {
-		return(NULL);
-	}
-
-	ret = g_string_new("");
-
-	while (*c) {
-		switch (*c) {
-		case '&':
-			ret = g_string_append(ret, "&amp;");
-			break;
-		case '<':
-			ret = g_string_append(ret, "&lt;");
-			break;
-		case '>':
-			ret = g_string_append(ret, "&gt;");
-			break;
-		case '"':
-			ret = g_string_append(ret, "&quot;");
-			break;
-		default:
-			ret = g_string_append_c(ret, *c);
-		}
-		c++;
-	}
-
-	str = ret->str;
-	g_string_free(ret, FALSE);
-	return(str);
-}
-
-/* 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 function is safe, but make sure you call it safely as well! */
-void http_encode(char *s)
-{
-	char t[strlen(s) + 1];
-	int i, j;
-
-	strcpy(t, s);
-	for (i = j = 0; t[i]; i++, j++) {
-		/* Warning: g_ascii_isalnum() is locale-aware, so don't use it here! */
-		if ((t[i] >= 'A' && t[i] <= 'Z') ||
-		    (t[i] >= 'a' && t[i] <= 'z') ||
-		    (t[i] >= '0' && t[i] <= '9') ||
-		    strchr("._-~", t[i])) {
-			s[j] = t[i];
-		} else {
-			sprintf(s + j, "%%%02X", ((unsigned char *) t)[i]);
-			j += 2;
-		}
-	}
-	s[j] = 0;
-}
-
-/* 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] = ' ';
-		}
-	}
-
-	return source;
-}
-
-/* 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)
-{
-	GIConv cd;
-	size_t res;
-	size_t inbytesleft, outbytesleft;
-	char *inbuf = src;
-	char *outbuf = dst;
-
-	cd = g_iconv_open(to_cs, from_cs);
-	if (cd == (GIConv) - 1) {
-		return -1;
-	}
-
-	inbytesleft = size ? size : strlen(src);
-	outbytesleft = maxbuf - 1;
-	res = g_iconv(cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft);
-	*outbuf = '\0';
-	g_iconv_close(cd);
-
-	if (res != 0) {
-		return -1;
-	} else {
-		return outbuf - dst;
-	}
-}
-
-/* A wrapper for /dev/urandom.
- * If /dev/urandom is not present or not usable, it calls abort()
- * to prevent bitlbee from working without a decent entropy source */
-void random_bytes(unsigned char *buf, int count)
-{
-	int fd;
-
-	if (((fd = open("/dev/urandom", O_RDONLY)) == -1) ||
-	    (read(fd, buf, count) == -1)) {
-		log_message(LOGLVL_ERROR, "/dev/urandom not present - aborting");
-		abort();
-	}
-
-	close(fd);
-}
-
-int is_bool(char *value)
-{
-	if (*value == 0) {
-		return 0;
-	}
-
-	if ((g_strcasecmp(value,
-	                  "true") == 0) || (g_strcasecmp(value, "yes") == 0) || (g_strcasecmp(value, "on") == 0)) {
-		return 1;
-	}
-	if ((g_strcasecmp(value,
-	                  "false") == 0) || (g_strcasecmp(value, "no") == 0) || (g_strcasecmp(value, "off") == 0)) {
-		return 1;
-	}
-
-	while (*value) {
-		if (!g_ascii_isdigit(*value)) {
-			return 0;
-		} else {
-			value++;
-		}
-	}
-
-	return 1;
-}
-
-int bool2int(char *value)
-{
-	int i;
-
-	if ((g_strcasecmp(value,
-	                  "true") == 0) || (g_strcasecmp(value, "yes") == 0) || (g_strcasecmp(value, "on") == 0)) {
-		return 1;
-	}
-	if ((g_strcasecmp(value,
-	                  "false") == 0) || (g_strcasecmp(value, "no") == 0) || (g_strcasecmp(value, "off") == 0)) {
-		return 0;
-	}
-
-	if (sscanf(value, "%d", &i) == 1) {
-		return i;
-	}
-
-	return 0;
-}
-
-struct ns_srv_reply **srv_lookup(char *service, char *protocol, char *domain)
-{
-	struct ns_srv_reply **replies = NULL;
-
-#ifdef HAVE_RESOLV_A
-	struct ns_srv_reply *reply = NULL;
-	char name[1024];
-	unsigned char querybuf[1024];
-	const unsigned char *buf;
-	ns_msg nsh;
-	ns_rr rr;
-	int n, len, size;
-
-	g_snprintf(name, sizeof(name), "_%s._%s.%s", service, protocol, domain);
-
-	if ((size = res_query(name, ns_c_in, ns_t_srv, querybuf, sizeof(querybuf))) <= 0) {
-		return NULL;
-	}
-
-	if (ns_initparse(querybuf, size, &nsh) != 0) {
-		return NULL;
-	}
-
-	n = 0;
-	while (ns_parserr(&nsh, ns_s_an, n, &rr) == 0) {
-		char name[NS_MAXDNAME];
-
-		if (ns_rr_rdlen(rr) < 7) {
-			break;
-		}
-
-		buf = ns_rr_rdata(rr);
-
-		if (dn_expand(querybuf, querybuf + size, &buf[6], name, NS_MAXDNAME) == -1) {
-			break;
-		}
-
-		len = strlen(name) + 1;
-
-		reply = g_malloc(sizeof(struct ns_srv_reply) + len);
-		memcpy(reply->name, name, len);
-
-		reply->prio = (buf[0] << 8) | buf[1];
-		reply->weight = (buf[2] << 8) | buf[3];
-		reply->port = (buf[4] << 8) | buf[5];
-
-		n++;
-		replies = g_renew(struct ns_srv_reply *, replies, n + 1);
-		replies[n - 1] = reply;
-	}
-	if (replies) {
-		replies[n] = NULL;
-	}
-#endif
-
-	return replies;
-}
-
-void srv_free(struct ns_srv_reply **srv)
-{
-	int i;
-
-	if (srv == NULL) {
-		return;
-	}
-
-	for (i = 0; srv[i]; i++) {
-		g_free(srv[i]);
-	}
-	g_free(srv);
-}
-
-char *word_wrap(const char *msg, int line_len)
-{
-	GString *ret = g_string_sized_new(strlen(msg) + 16);
-
-	while (strlen(msg) > line_len) {
-		int i;
-
-		/* First try to find out if there's a newline already. Don't
-		   want to add more splits than necessary. */
-		for (i = line_len; i > 0 && msg[i] != '\n'; i--) {
-			;
-		}
-		if (msg[i] == '\n') {
-			g_string_append_len(ret, msg, i + 1);
-			msg += i + 1;
-			continue;
-		}
-
-		for (i = line_len; i > 0; i--) {
-			if (msg[i] == '-') {
-				g_string_append_len(ret, msg, i + 1);
-				g_string_append_c(ret, '\n');
-				msg += i + 1;
-				break;
-			} else if (msg[i] == ' ') {
-				g_string_append_len(ret, msg, i);
-				g_string_append_c(ret, '\n');
-				msg += i + 1;
-				break;
-			}
-		}
-		if (i == 0) {
-			const char *end;
-			size_t len;
-
-			g_utf8_validate(msg, line_len, &end);
-
-			len = (end != msg) ? end - msg : line_len;
-
-			g_string_append_len(ret, msg, len);
-			g_string_append_c(ret, '\n');
-			msg += len;
-		}
-	}
-	g_string_append(ret, msg);
-
-	return g_string_free(ret, FALSE);
-}
-
-gboolean ssl_sockerr_again(void *ssl)
-{
-	if (ssl) {
-		return ssl_errno == SSL_AGAIN;
-	} else {
-		return sockerr_again();
-	}
-}
-
-/* Returns values: -1 == Failure (base64-decoded to something unexpected)
-                    0 == Okay
-                    1 == Password doesn't match the hash. */
-int md5_verify_password(char *password, char *hash)
-{
-	guint8 *pass_dec = NULL;
-	guint8 pass_md5[16];
-	GChecksum *md5_state;
-	gsize digest_len = MD5_HASH_SIZE;
-	int ret = -1, i;
-
-	if (base64_decode(hash, &pass_dec) == 21) {
-		md5_state = g_checksum_new(G_CHECKSUM_MD5);
-		g_checksum_update(md5_state, (guint8 *) password, strlen(password));
-		g_checksum_update(md5_state, (guint8 *) pass_dec + 16, 5);  /* Hmmm, salt! */
-		g_checksum_get_digest(md5_state, pass_md5, &digest_len);
-		g_checksum_free(md5_state);
-
-		for (i = 0; i < 16; i++) {
-			if (pass_dec[i] != pass_md5[i]) {
-				ret = 1;
-				break;
-			}
-		}
-
-		/* If we reached the end of the loop, it was a match! */
-		if (i == 16) {
-			ret = 0;
-		}
-	}
-
-	g_free(pass_dec);
-
-	return ret;
-}
-
-/* Split commands (root-style, *not* IRC-style). Handles "quoting of"
-   white\ space in 'various ways'. Returns a NULL-terminated static
-   char** so watch out with nested use! Definitely not thread-safe. */
-char **split_command_parts(char *command, int limit)
-{
-	static char *cmd[IRC_MAX_ARGS + 1];
-	char *s, q = 0;
-	int k;
-
-	memset(cmd, 0, sizeof(cmd));
-	cmd[0] = command;
-	k = 1;
-	for (s = command; *s && k < IRC_MAX_ARGS; s++) {
-		if (*s == ' ' && !q) {
-			*s = 0;
-			while (*++s == ' ') {
-				;
-			}
-			if (k != limit && (*s == '"' || *s == '\'')) {
-				q = *s;
-				s++;
-			}
-			if (*s) {
-				cmd[k++] = s;
-				if (limit && k > limit) {
-					break;
-				}
-				s--;
-			} else {
-				break;
-			}
-		} else if (*s == '\\' && ((!q && s[1]) || (q && q == s[1]))) {
-			char *cpy;
-
-			for (cpy = s; *cpy; cpy++) {
-				cpy[0] = cpy[1];
-			}
-		} else if (*s == q) {
-			q = *s = 0;
-		}
-	}
-
-	/* Full zero-padding for easier argc checking. */
-	while (k <= IRC_MAX_ARGS) {
-		cmd[k++] = NULL;
-	}
-
-	return cmd;
-}
-
-char *get_rfc822_header(const char *text, const char *header, int len)
-{
-	int hlen = strlen(header), i;
-	const char *ret;
-
-	if (text == NULL) {
-		return NULL;
-	}
-
-	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++;
-			}
-
-			/* 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 ((i >= 4 && strncmp(text + i - 4, "\r\n\r\n", 4) == 0) ||
-		    (i >= 2 && (strncmp(text + i - 2, "\n\n", 2) == 0 ||
-		                strncmp(text + i - 2, "\r\r", 2) == 0))) {
-			break;
-		}
-	}
-
-	return NULL;
-}
-
-/* Takes a string, truncates it where it's safe, returns the new length */
-int truncate_utf8(char *string, int maxlen)
-{
-	char *end;
-
-	g_utf8_validate((const gchar *) string, maxlen, (const gchar **) &end);
-	*end = '\0';
-	return end - string;
-}
-
-/* Parses a guint64 from string, returns TRUE on success */
-gboolean parse_int64(char *string, int base, guint64 *number)
-{
-	guint64 parsed;
-	char *endptr;
-
-	errno = 0;
-	parsed = g_ascii_strtoull(string, &endptr, base);
-	if (errno || endptr == string || *endptr != '\0') {
-		return FALSE;
-	}
-	*number = parsed;
-	return TRUE;
-}
-
-/* Filters all the characters in 'blacklist' replacing them with 'replacement'.
- * Modifies the string in-place and returns the string itself.
- * For the opposite, use g_strcanon() */
-char *str_reject_chars(char *string, const char *reject, char replacement)
-{
-	char *c = string;
-
-	while (*c) {
-		c += strcspn(c, reject);
-		if (*c) {
-			*c = replacement;
-		}
-	}
-
-	return string;
-}
-
-/* Returns a string that is exactly 'char_len' utf8 characters long (not bytes),
- * padded to the right with spaces or truncated with the 'ellipsis' parameter
- * if specified (can be NULL).
- * Returns a newly allocated string, or NULL on invalid parameters. */
-char *str_pad_and_truncate(const char *string, long char_len, const char *ellipsis)
-{
-	size_t string_len = strlen(string);
-	size_t ellipsis_len = (ellipsis) ? strlen(ellipsis) : 0;
-	long orig_len = g_utf8_strlen(string, -1);
-
-	g_return_val_if_fail(char_len > ellipsis_len, NULL);
-
-	if (orig_len > char_len) {
-		char *ret = g_malloc(string_len + 1);
-		g_utf8_strncpy(ret, string, char_len - ellipsis_len);
-		if (ellipsis) {
-			g_strlcat(ret, ellipsis, string_len);
-		}
-		return ret;
-	} else if (orig_len < char_len) {
-		return g_strdup_printf("%s%*s", string, (int) (char_len - orig_len), "");
-	} else {
-		return g_strdup(string);
-	}
-}
-
-/* copied from irssi's misc.c, by timo sirainen */
-int b_istr_equal(gconstpointer v, gconstpointer v2)
-{
-	return g_ascii_strcasecmp((const char *) v, (const char *) v2) == 0;
-}
-
-/* copied from irssi's misc.c, by lemonboy */
-guint b_istr_hash(gconstpointer v)
-{
-	const signed char *p;
-	guint32 h = 5381;
-
-	for (p = v; *p != '\0'; p++) {
-		h = (h << 5) + h + g_ascii_toupper(*p);
-	}
-
-	return h;
-}
-
-#ifdef NO_STRCASESTR
-char* strcasestr(const char* haystack, const char* needle)
-{
-	size_t haystackn = strlen(haystack);
-	size_t needlen = strlen(needle);
-
-	const char *p = haystack;
-	while (haystackn >= needlen) {
-		if (g_strncasecmp(p, needle, needlen) == 0) {
-		    return (char*) p;
-		}
-		p++;
-		haystackn--;
-	}
-	return NULL;
-}
-#endif
Index: b/misc.h
===================================================================
--- lib/misc.h	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,156 +1,0 @@
-/********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2012 Wilmer van der Gaast and others                *
-  \********************************************************************/
-
-/* Misc. 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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#ifndef _MISC_H
-#define _MISC_H
-
-#include <gmodule.h>
-#include <time.h>
-#include <sys/socket.h>
-
-struct ns_srv_reply {
-	int prio;
-	int weight;
-	int port;
-	char name[];
-};
-
-#ifndef NAMESER_HAS_NS_TYPES
-
-#define NS_MAXDNAME 1025
-#define NS_INT16SZ  2
-#define NS_INT32SZ  4
-
-#define NS_GET16(s, cp) do { \
-		register const unsigned char *t_cp = (const unsigned char *) (cp); \
-		(s) = ((guint16) t_cp[0] << 8) \
-		      | ((guint16) t_cp[1]) \
-		; \
-		(cp) += NS_INT16SZ; \
-} while (0)
-
-#define NS_GET32(s, cp) do { \
-		register const unsigned char *t_cp = (const unsigned char *) (cp); \
-		(s) = ((guint16) t_cp[0] << 24) \
-		      | ((guint16) t_cp[1] << 16) \
-		      | ((guint16) t_cp[2] << 8) \
-		      | ((guint16) t_cp[3]) \
-		; \
-		(cp) += NS_INT32SZ; \
-} while (0)
-
-#define ns_rr_rdlen(rr) ((rr).rdlength + 0)
-#define ns_rr_rdata(rr) ((rr).rdata + 0)
-
-struct _ns_flagdata { int mask, shift; };
-
-typedef struct __ns_rr {
-	char name[NS_MAXDNAME];
-	guint16 type;
-	guint16 rr_class;
-	guint32 ttl;
-	guint16 rdlength;
-	const unsigned char* rdata;
-} ns_rr;
-
-typedef enum __ns_sect {
-	ns_s_qd = 0,
-	ns_s_zn = 0,
-	ns_s_an = 1,
-	ns_s_pr = 1,
-	ns_s_ns = 2,
-	ns_s_ud = 2,
-	ns_s_ar = 3,
-	ns_s_max = 4
-} ns_sect;
-
-typedef struct __ns_msg {
-	const unsigned char* _msg;
-	const unsigned char* _eom;
-	guint16 _id;
-	guint16 _flags;
-	guint16 _counts[ns_s_max];
-	const unsigned char* _sections[ns_s_max];
-	ns_sect _sect;
-	int _rrnum;
-	const unsigned char* _msg_ptr;
-} ns_msg;
-
-typedef enum __ns_class {
-	ns_c_invalid = 0,
-	ns_c_in = 1,
-	ns_c_2 = 2,
-	ns_c_chaos = 3,
-	ns_c_hs = 4,
-	ns_c_none = 254,
-	ns_c_any = 255,
-	ns_c_max = 65536
-} ns_class;
-
-
-/* TODO : fill out the rest */
-typedef enum __ns_type {
-	ns_t_srv = 33
-} ns_type;
-
-#endif /* NAMESER_HAS_NS_INITPARSE */
-
-G_MODULE_EXPORT void strip_linefeed(gchar *text);
-G_MODULE_EXPORT char *add_cr(char *text);
-G_MODULE_EXPORT char *strip_newlines(char *source);
-
-G_MODULE_EXPORT time_t get_time(int year, int month, int day, int hour, int min, int sec);
-G_MODULE_EXPORT time_t mktime_utc(struct tm *tp);
-double gettime(void);
-
-G_MODULE_EXPORT void strip_html(char *msg);
-G_MODULE_EXPORT char *escape_html(const char *html);
-G_MODULE_EXPORT void http_decode(char *s);
-G_MODULE_EXPORT void http_encode(char *s);
-
-G_MODULE_EXPORT signed int do_iconv(char *from_cs, char *to_cs, char *src, char *dst, size_t size, size_t maxbuf);
-
-G_MODULE_EXPORT void random_bytes(unsigned char *buf, int count);
-
-G_MODULE_EXPORT int is_bool(char *value);
-G_MODULE_EXPORT int bool2int(char *value);
-
-G_MODULE_EXPORT struct ns_srv_reply **srv_lookup(char *service, char *protocol, char *domain);
-G_MODULE_EXPORT void srv_free(struct ns_srv_reply **srv);
-
-G_MODULE_EXPORT char *word_wrap(const char *msg, int line_len);
-G_MODULE_EXPORT gboolean ssl_sockerr_again(void *ssl);
-G_MODULE_EXPORT int md5_verify_password(char *password, char *hash);
-G_MODULE_EXPORT char **split_command_parts(char *command, int limit);
-G_MODULE_EXPORT char *get_rfc822_header(const char *text, const char *header, int len);
-G_MODULE_EXPORT int truncate_utf8(char *string, int maxlen);
-G_MODULE_EXPORT gboolean parse_int64(char *string, int base, guint64 *number);
-G_MODULE_EXPORT char *str_reject_chars(char *string, const char *reject, char replacement);
-G_MODULE_EXPORT char *str_pad_and_truncate(const char *string, long char_len, const char *ellipsis);
-
-G_MODULE_EXPORT int b_istr_equal(gconstpointer v, gconstpointer v2);
-G_MODULE_EXPORT guint b_istr_hash(gconstpointer v);
-
-#endif
Index: b/ns_parse.c
===================================================================
--- lib/ns_parse.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,242 +1,0 @@
-/*
- * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
- * Copyright (c) 1996,1999 by Internet Software Consortium.
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
- * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "bitlbee.h"
-
-#ifndef lint
-//static const char rcsid[] = "$Id: ns_parse.c,v 1.10 2009/01/23 19:59:16 each Exp $";
-#endif
-
-#ifdef HAVE_RESOLV_A
-#ifndef HAVE_RESOLV_A_WITH_NS
-/* Import. */
-
-
-#include <sys/types.h>
-
-#include <netinet/in.h>
-#include <arpa/nameser.h>
-
-#include <errno.h>
-#include <resolv.h>
-#include <string.h>
-
-
-/* Forward. */
-
-static void     setsection(ns_msg *msg, ns_sect sect);
-
-/* Macros. */
-
-#if !defined(SOLARIS2) || defined(__COVERITY__)
-#define RETERR(err) do { errno = (err); return (-1); } while (0)
-#else
-#define RETERR(err) \
-	do { errno = (err); if (errno == errno) { return (-1); } } while (0)
-#endif
-
-#define PARSE_FMT_PRESO 0       /* Parse using presentation-format names */
-#define PARSE_FMT_WIRE 1        /* Parse using network-format names */
-
-/* Public. */
-
-/* These need to be in the same order as the nres.h:ns_flag enum. */
-struct _ns_flagdata _ns_flagdata[16] = {
-	{ 0x8000, 15 },         /*%< qr. */
-	{ 0x7800, 11 },         /*%< opcode. */
-	{ 0x0400, 10 },         /*%< aa. */
-	{ 0x0200, 9 },          /*%< tc. */
-	{ 0x0100, 8 },          /*%< rd. */
-	{ 0x0080, 7 },          /*%< ra. */
-	{ 0x0040, 6 },          /*%< z. */
-	{ 0x0020, 5 },          /*%< ad. */
-	{ 0x0010, 4 },          /*%< cd. */
-	{ 0x000f, 0 },          /*%< rcode. */
-	{ 0x0000, 0 },          /*%< expansion (1/6). */
-	{ 0x0000, 0 },          /*%< expansion (2/6). */
-	{ 0x0000, 0 },          /*%< expansion (3/6). */
-	{ 0x0000, 0 },          /*%< expansion (4/6). */
-	{ 0x0000, 0 },          /*%< expansion (5/6). */
-	{ 0x0000, 0 },          /*%< expansion (6/6). */
-};
-
-int ns_msg_getflag(ns_msg handle, int flag)
-{
-	return(((handle)._flags & _ns_flagdata[flag].mask) >> _ns_flagdata[flag].shift);
-}
-
-int
-ns_skiprr(const u_char *ptr, const u_char *eom, ns_sect section, int count)
-{
-	const u_char *optr = ptr;
-
-	for ((void) NULL; count > 0; count--) {
-		int b, rdlength;
-
-		b = dn_skipname(ptr, eom);
-		if (b < 0) {
-			RETERR(EMSGSIZE);
-		}
-		ptr += b /*Name*/ + NS_INT16SZ /*Type*/ + NS_INT16SZ /*Class*/;
-		if (section != ns_s_qd) {
-			if (ptr + NS_INT32SZ + NS_INT16SZ > eom) {
-				RETERR(EMSGSIZE);
-			}
-			ptr += NS_INT32SZ /*TTL*/;
-			NS_GET16(rdlength, ptr);
-			ptr += rdlength /*RData*/;
-		}
-	}
-	if (ptr > eom) {
-		RETERR(EMSGSIZE);
-	}
-	return (ptr - optr);
-}
-
-int
-ns_initparse(const u_char *msg, int msglen, ns_msg *handle)
-{
-	const u_char *eom = msg + msglen;
-	int i;
-
-	handle->_msg = msg;
-	handle->_eom = eom;
-	if (msg + NS_INT16SZ > eom) {
-		RETERR(EMSGSIZE);
-	}
-	NS_GET16(handle->_id, msg);
-	if (msg + NS_INT16SZ > eom) {
-		RETERR(EMSGSIZE);
-	}
-	NS_GET16(handle->_flags, msg);
-	for (i = 0; i < ns_s_max; i++) {
-		if (msg + NS_INT16SZ > eom) {
-			RETERR(EMSGSIZE);
-		}
-		NS_GET16(handle->_counts[i], msg);
-	}
-	for (i = 0; i < ns_s_max; i++) {
-		if (handle->_counts[i] == 0) {
-			handle->_sections[i] = NULL;
-		} else {
-			int b = ns_skiprr(msg, eom, (ns_sect) i,
-			                  handle->_counts[i]);
-
-			if (b < 0) {
-				return (-1);
-			}
-			handle->_sections[i] = msg;
-			msg += b;
-		}
-	}
-	if (msg != eom) {
-		RETERR(EMSGSIZE);
-	}
-	setsection(handle, ns_s_max);
-	return (0);
-}
-
-int
-ns_parserr(ns_msg *handle, ns_sect section, int rrnum, ns_rr *rr)
-{
-	int b;
-	int tmp;
-
-	/* Make section right. */
-	tmp = section;
-	if (tmp < 0 || section >= ns_s_max) {
-		RETERR(ENODEV);
-	}
-	if (section != handle->_sect) {
-		setsection(handle, section);
-	}
-
-	/* Make rrnum right. */
-	if (rrnum == -1) {
-		rrnum = handle->_rrnum;
-	}
-	if (rrnum < 0 || rrnum >= handle->_counts[(int) section]) {
-		RETERR(ENODEV);
-	}
-	if (rrnum < handle->_rrnum) {
-		setsection(handle, section);
-	}
-	if (rrnum > handle->_rrnum) {
-		b = ns_skiprr(handle->_msg_ptr, handle->_eom, section,
-		              rrnum - handle->_rrnum);
-
-		if (b < 0) {
-			return (-1);
-		}
-		handle->_msg_ptr += b;
-		handle->_rrnum = rrnum;
-	}
-
-	/* Do the parse. */
-	b = dn_expand(handle->_msg, handle->_eom,
-	              handle->_msg_ptr, rr->name, NS_MAXDNAME);
-	if (b < 0) {
-		return (-1);
-	}
-	handle->_msg_ptr += b;
-	if (handle->_msg_ptr + NS_INT16SZ + NS_INT16SZ > handle->_eom) {
-		RETERR(EMSGSIZE);
-	}
-	NS_GET16(rr->type, handle->_msg_ptr);
-	NS_GET16(rr->rr_class, handle->_msg_ptr);
-	if (section == ns_s_qd) {
-		rr->ttl = 0;
-		rr->rdlength = 0;
-		rr->rdata = NULL;
-	} else {
-		if (handle->_msg_ptr + NS_INT32SZ + NS_INT16SZ > handle->_eom) {
-			RETERR(EMSGSIZE);
-		}
-		NS_GET32(rr->ttl, handle->_msg_ptr);
-		NS_GET16(rr->rdlength, handle->_msg_ptr);
-		if (handle->_msg_ptr + rr->rdlength > handle->_eom) {
-			RETERR(EMSGSIZE);
-		}
-		rr->rdata = handle->_msg_ptr;
-		handle->_msg_ptr += rr->rdlength;
-	}
-	if (++handle->_rrnum > handle->_counts[(int) section]) {
-		setsection(handle, (ns_sect) ((int) section + 1));
-	}
-
-	/* All done. */
-	return (0);
-}
-
-/* Private. */
-
-static void
-setsection(ns_msg *msg, ns_sect sect)
-{
-	msg->_sect = sect;
-	if (sect == ns_s_max) {
-		msg->_rrnum = -1;
-		msg->_msg_ptr = NULL;
-	} else {
-		msg->_rrnum = 0;
-		msg->_msg_ptr = msg->_sections[(int) sect];
-	}
-}
-
-/*! \file */
-#endif
-#endif
Index: b/oauth.c
===================================================================
--- lib/oauth.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,428 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Simple OAuth client (consumer) implementation.                           *
-*                                                                           *
-*  Copyright 2010 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.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-\***************************************************************************/
-
-#include <glib.h>
-#include <gmodule.h>
-#include <stdlib.h>
-#include <string.h>
-#include "http_client.h"
-#include "base64.h"
-#include "misc.h"
-#include "sha1.h"
-#include "url.h"
-#include "oauth.h"
-
-#define HMAC_BLOCK_SIZE 64
-
-static char *oauth_sign(const char *method, const char *url,
-                        const char *params, struct oauth_info *oi)
-{
-	uint8_t hash[SHA1_HASH_SIZE];
-	GString *payload = g_string_new("");
-	char *key;
-	char *s;
-
-	key = g_strdup_printf("%s&%s", oi->sp->consumer_secret, oi->token_secret ? oi->token_secret : "");
-
-	g_string_append_printf(payload, "%s&", method);
-
-	s = g_new0(char, strlen(url) * 3 + 1);
-	strcpy(s, url);
-	http_encode(s);
-	g_string_append_printf(payload, "%s&", s);
-	g_free(s);
-
-	s = g_new0(char, strlen(params) * 3 + 1);
-	strcpy(s, params);
-	http_encode(s);
-	g_string_append(payload, s);
-	g_free(s);
-
-	sha1_hmac(key, 0, payload->str, 0, hash);
-
-	g_free(key);
-	g_string_free(payload, TRUE);
-
-	/* base64_encode + HTTP escape it (both consumers
-	   need it that away) and we're done. */
-	s = base64_encode(hash, SHA1_HASH_SIZE);
-	s = g_realloc(s, strlen(s) * 3 + 1);
-	http_encode(s);
-
-	return s;
-}
-
-static char *oauth_nonce()
-{
-	unsigned char bytes[21];
-
-	random_bytes(bytes, sizeof(bytes));
-	return base64_encode(bytes, sizeof(bytes));
-}
-
-void oauth_params_add(GSList **params, const char *key, const char *value)
-{
-	char *item;
-
-	if (!key || !value) {
-		return;
-	}
-
-	item = g_strdup_printf("%s=%s", key, value);
-	*params = g_slist_insert_sorted(*params, item, (GCompareFunc) strcmp);
-}
-
-void oauth_params_del(GSList **params, const char *key)
-{
-	int key_len = strlen(key);
-	GSList *l, *n;
-
-	if (!params) {
-		return;
-	}
-
-	for (l = *params; l; l = n) {
-		n = l->next;
-		char *data = l->data;
-
-		if (strncmp(data, key, key_len) == 0 && data[key_len] == '=') {
-			*params = g_slist_remove(*params, data);
-			g_free(data);
-		}
-	}
-}
-
-void oauth_params_set(GSList **params, const char *key, const char *value)
-{
-	oauth_params_del(params, key);
-	oauth_params_add(params, key, value);
-}
-
-const char *oauth_params_get(GSList **params, const char *key)
-{
-	int key_len = strlen(key);
-	GSList *l;
-
-	if (params == NULL) {
-		return NULL;
-	}
-
-	for (l = *params; l; l = l->next) {
-		if (strncmp((char *) l->data, key, key_len) == 0 &&
-		    ((char *) l->data)[key_len] == '=') {
-			return (const char *) l->data + key_len + 1;
-		}
-	}
-
-	return NULL;
-}
-
-void oauth_params_parse(GSList **params, char *in)
-{
-	char *amp, *eq, *s;
-
-	while (in && *in) {
-		eq = strchr(in, '=');
-		if (!eq) {
-			break;
-		}
-
-		*eq = '\0';
-		if ((amp = strchr(eq + 1, '&'))) {
-			*amp = '\0';
-		}
-
-		s = g_strdup(eq + 1);
-		http_decode(s);
-		oauth_params_add(params, in, s);
-		g_free(s);
-
-		*eq = '=';
-		if (amp == NULL) {
-			break;
-		}
-
-		*amp = '&';
-		in = amp + 1;
-	}
-}
-
-void oauth_params_free(GSList **params)
-{
-	while (params && *params) {
-		g_free((*params)->data);
-		*params = g_slist_remove(*params, (*params)->data);
-	}
-}
-
-char *oauth_params_string(GSList *params)
-{
-	GSList *l;
-	GString *str = g_string_new("");
-
-	for (l = params; l; l = l->next) {
-		char *s, *eq;
-
-		s = g_malloc(strlen(l->data) * 3 + 1);
-		strcpy(s, l->data);
-		if ((eq = strchr(s, '='))) {
-			http_encode(eq + 1);
-		}
-		g_string_append(str, s);
-		g_free(s);
-
-		if (l->next) {
-			g_string_append_c(str, '&');
-		}
-	}
-
-	return g_string_free(str, FALSE);
-}
-
-void oauth_info_free(struct oauth_info *info)
-{
-	if (info) {
-		g_free(info->auth_url);
-		g_free(info->request_token);
-		g_free(info->token);
-		g_free(info->token_secret);
-		oauth_params_free(&info->params);
-		g_free(info);
-	}
-}
-
-static void oauth_add_default_params(GSList **params, const struct oauth_service *sp)
-{
-	char *s;
-
-	oauth_params_set(params, "oauth_consumer_key", sp->consumer_key);
-	oauth_params_set(params, "oauth_signature_method", "HMAC-SHA1");
-
-	s = g_strdup_printf("%d", (int) time(NULL));
-	oauth_params_set(params, "oauth_timestamp", s);
-	g_free(s);
-
-	s = oauth_nonce();
-	oauth_params_set(params, "oauth_nonce", s);
-	g_free(s);
-
-	oauth_params_set(params, "oauth_version", "1.0");
-}
-
-static void *oauth_post_request(const char *url, GSList **params_, http_input_function func, struct oauth_info *oi)
-{
-	GSList *params = NULL;
-	char *s, *params_s, *post;
-	void *req;
-	url_t url_p;
-
-	if (!url_set(&url_p, url)) {
-		oauth_params_free(params_);
-		return NULL;
-	}
-
-	if (params_) {
-		params = *params_;
-	}
-
-	oauth_add_default_params(&params, oi->sp);
-
-	params_s = oauth_params_string(params);
-	oauth_params_free(&params);
-
-	s = oauth_sign("POST", url, params_s, oi);
-	post = g_strdup_printf("%s&oauth_signature=%s", params_s, s);
-	g_free(params_s);
-	g_free(s);
-
-	s = g_strdup_printf("POST %s HTTP/1.0\r\n"
-	                    "Host: %s\r\n"
-	                    "Content-Type: application/x-www-form-urlencoded\r\n"
-	                    "Content-Length: %zd\r\n"
-	                    "\r\n"
-	                    "%s", url_p.file, url_p.host, strlen(post), post);
-	g_free(post);
-
-	req = http_dorequest(url_p.host, url_p.port, url_p.proto == PROTO_HTTPS,
-	                     s, func, oi);
-	g_free(s);
-
-	return req;
-}
-
-static void oauth_request_token_done(struct http_request *req);
-
-struct oauth_info *oauth_request_token(const struct oauth_service *sp, oauth_cb func, void *data)
-{
-	struct oauth_info *st = g_new0(struct oauth_info, 1);
-	GSList *params = NULL;
-
-	st->func = func;
-	st->data = data;
-	st->sp = sp;
-
-	oauth_params_add(&params, "oauth_callback", "oob");
-
-	if (!oauth_post_request(sp->url_request_token, &params, oauth_request_token_done, st)) {
-		oauth_info_free(st);
-		return NULL;
-	}
-
-	return st;
-}
-
-static void oauth_request_token_done(struct http_request *req)
-{
-	struct oauth_info *st = req->data;
-
-	st->http = req;
-
-	if (req->status_code == 200) {
-		GSList *params = NULL;
-
-		st->auth_url = g_strdup_printf("%s?%s", st->sp->url_authorize, req->reply_body);
-		oauth_params_parse(&params, req->reply_body);
-		st->request_token = g_strdup(oauth_params_get(&params, "oauth_token"));
-		st->token_secret = g_strdup(oauth_params_get(&params, "oauth_token_secret"));
-		oauth_params_free(&params);
-	}
-
-	st->stage = OAUTH_REQUEST_TOKEN;
-	st->func(st);
-}
-
-static void oauth_access_token_done(struct http_request *req);
-
-gboolean oauth_access_token(const char *pin, struct oauth_info *st)
-{
-	GSList *params = NULL;
-
-	oauth_params_add(&params, "oauth_token", st->request_token);
-	oauth_params_add(&params, "oauth_verifier", pin);
-
-	return oauth_post_request(st->sp->url_access_token, &params, oauth_access_token_done, st) != NULL;
-}
-
-static void oauth_access_token_done(struct http_request *req)
-{
-	struct oauth_info *st = req->data;
-
-	st->http = req;
-
-	if (req->status_code == 200) {
-		oauth_params_parse(&st->params, req->reply_body);
-		st->token = g_strdup(oauth_params_get(&st->params, "oauth_token"));
-		g_free(st->token_secret);
-		st->token_secret = g_strdup(oauth_params_get(&st->params, "oauth_token_secret"));
-	}
-
-	st->stage = OAUTH_ACCESS_TOKEN;
-	if (st->func(st)) {
-		/* Don't need these anymore, but keep the rest. */
-		g_free(st->auth_url);
-		st->auth_url = NULL;
-		g_free(st->request_token);
-		st->request_token = NULL;
-		oauth_params_free(&st->params);
-	}
-}
-
-char *oauth_http_header(struct oauth_info *oi, const char *method, const char *url, char *args)
-{
-	GSList *params = NULL, *l;
-	char *sig = NULL, *params_s, *s;
-	GString *ret = NULL;
-
-	oauth_params_add(&params, "oauth_token", oi->token);
-	oauth_add_default_params(&params, oi->sp);
-
-	/* Start building the OAuth header. 'key="value", '... */
-	ret = g_string_new("OAuth ");
-	for (l = params; l; l = l->next) {
-		char *kv = l->data;
-		char *eq = strchr(kv, '=');
-		char esc[strlen(kv) * 3 + 1];
-
-		if (eq == NULL) {
-			break; /* WTF */
-
-		}
-		strcpy(esc, eq + 1);
-		http_encode(esc);
-
-		g_string_append_len(ret, kv, eq - kv + 1);
-		g_string_append_c(ret, '"');
-		g_string_append(ret, esc);
-		g_string_append(ret, "\", ");
-	}
-
-	/* Now, before generating the signature, add GET/POST arguments to params
-	   since they should be included in the base signature string (but not in
-	   the HTTP header). */
-	if (args) {
-		oauth_params_parse(&params, args);
-	}
-	if ((s = strchr(url, '?'))) {
-		s = g_strdup(s + 1);
-		oauth_params_parse(&params, s);
-		g_free(s);
-	}
-
-	/* Append the signature and we're done! */
-	params_s = oauth_params_string(params);
-	sig = oauth_sign(method, url, params_s, oi);
-	g_string_append_printf(ret, "oauth_signature=\"%s\"", sig);
-	g_free(params_s);
-
-	oauth_params_free(&params);
-	g_free(sig);
-
-	return ret ? g_string_free(ret, FALSE) : NULL;
-}
-
-char *oauth_to_string(struct oauth_info *oi)
-{
-	GSList *params = NULL;
-	char *ret;
-
-	oauth_params_add(&params, "oauth_token", oi->token);
-	oauth_params_add(&params, "oauth_token_secret", oi->token_secret);
-	ret = oauth_params_string(params);
-	oauth_params_free(&params);
-
-	return ret;
-}
-
-struct oauth_info *oauth_from_string(char *in, const struct oauth_service *sp)
-{
-	struct oauth_info *oi = g_new0(struct oauth_info, 1);
-	GSList *params = NULL;
-
-	oauth_params_parse(&params, in);
-	oi->token = g_strdup(oauth_params_get(&params, "oauth_token"));
-	oi->token_secret = g_strdup(oauth_params_get(&params, "oauth_token_secret"));
-	oauth_params_free(&params);
-	oi->sp = sp;
-
-	return oi;
-}
Index: b/oauth.h
===================================================================
--- lib/oauth.h	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,96 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Simple OAuth client (consumer) implementation.                           *
-*                                                                           *
-*  Copyright 2010 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.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-\***************************************************************************/
-
-/* http://oauth.net/core/1.0a/ */
-
-struct oauth_info;
-
-/* Callback function called twice during the access token request process.
-   Return FALSE if something broke and the process must be aborted. */
-typedef gboolean (*oauth_cb)(struct oauth_info *);
-
-typedef enum {
-	OAUTH_INIT,
-	OAUTH_REQUEST_TOKEN,
-	OAUTH_ACCESS_TOKEN,
-} oauth_stage_t;
-
-struct oauth_info {
-	oauth_stage_t stage;
-	const struct oauth_service *sp;
-
-	oauth_cb func;
-	void *data;
-
-	struct http_request *http;
-
-	char *auth_url;
-	char *request_token;
-
-	char *token;
-	char *token_secret;
-	GSList *params;
-};
-
-struct oauth_service {
-	char *url_request_token;
-	char *url_access_token;
-	char *url_authorize;
-
-	char *consumer_key;
-	char *consumer_secret;
-};
-
-/* http://oauth.net/core/1.0a/#auth_step1 (section 6.1)
-   Request an initial anonymous token which can be used to construct an
-   authorization URL for the user. This is passed to the callback function
-   in a struct oauth_info. */
-struct oauth_info *oauth_request_token(const struct oauth_service *sp, oauth_cb func, void *data);
-
-/* http://oauth.net/core/1.0a/#auth_step3 (section 6.3)
-   The user gets a PIN or so which we now exchange for the final access
-   token. This is passed to the callback function in the same
-   struct oauth_info. */
-gboolean oauth_access_token(const char *pin, struct oauth_info *st);
-
-/* http://oauth.net/core/1.0a/#anchor12 (section 7)
-   Generate an OAuth Authorization: HTTP header. access_token should be
-   saved/fetched using the functions above. args can be a string with
-   whatever's going to be in the POST body of the request. GET args will
-   automatically be grabbed from url. */
-char *oauth_http_header(struct oauth_info *oi, const char *method, const char *url, char *args);
-
-/* Shouldn't normally be required unless the process is aborted by the user. */
-void oauth_info_free(struct oauth_info *info);
-
-/* Convert to and back from strings, for easier saving. */
-char *oauth_to_string(struct oauth_info *oi);
-struct oauth_info *oauth_from_string(char *in, const struct oauth_service *sp);
-
-/* For reading misc. data. */
-void oauth_params_add(GSList **params, const char *key, const char *value);
-void oauth_params_parse(GSList **params, char *in);
-void oauth_params_free(GSList **params);
-char *oauth_params_string(GSList *params);
-void oauth_params_set(GSList **params, const char *key, const char *value);
-const char *oauth_params_get(GSList **params, const char *key);
Index: b/oauth2.c
===================================================================
--- lib/oauth2.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,198 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Simple OAuth2 client (consumer) implementation.                          *
-*                                                                           *
-*  Copyright 2010-2013 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.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-\***************************************************************************/
-
-/* Out of protest, I should rename this file. OAuth2 is a pathetic joke, and
-   of all things, DEFINITELY NOT A STANDARD. The only thing various OAuth2
-   implementations have in common is that name, wrongfully stolen from
-   a pretty nice standard called OAuth 1.0a. That, and the fact that they
-   use JSON. Wait, no, Facebook's version doesn't use JSON. For some of its
-   responses.
-
-   Apparently too many people were too retarded to comprehend the elementary
-   bits of crypto in OAuth 1.0a (took me one afternoon to implement) so
-   the standard was replaced with what comes down to a complicated scheme
-   around what's really just application-specific passwords.
-
-   And then a bunch of mostly incompatible implementations. Great work, guys.
-
-   http://hueniverse.com/2012/07/oauth-2-0-and-the-road-to-hell/ */
-
-#include <glib.h>
-#include "http_client.h"
-#include "oauth2.h"
-#include "oauth.h"
-#include <json.h>
-#include "json_util.h"
-#include "url.h"
-
-char *oauth2_url(const struct oauth2_service *sp)
-{
-	return g_strconcat(sp->auth_url,
-	                   "?scope=", sp->scope,
-	                   "&response_type=code"
-	                   "&redirect_uri=", sp->redirect_url,
-	                   "&client_id=", sp->consumer_key,
-	                   NULL);
-}
-
-struct oauth2_access_token_data {
-	oauth2_token_callback func;
-	gpointer data;
-};
-
-static void oauth2_access_token_done(struct http_request *req);
-
-int oauth2_access_token(const struct oauth2_service *sp,
-                        const char *auth_type, const char *auth,
-                        oauth2_token_callback func, gpointer data)
-{
-	GSList *args = NULL;
-	char *args_s, *s;
-	url_t url_p;
-	struct http_request *req;
-	struct oauth2_access_token_data *cb_data;
-
-	if (!url_set(&url_p, sp->token_url)) {
-		return 0;
-	}
-
-	oauth_params_add(&args, "client_id", sp->consumer_key);
-	oauth_params_add(&args, "client_secret", sp->consumer_secret);
-	oauth_params_add(&args, "grant_type", auth_type);
-	if (strcmp(auth_type, OAUTH2_AUTH_CODE) == 0) {
-		oauth_params_add(&args, "redirect_uri", sp->redirect_url);
-		oauth_params_add(&args, "code", auth);
-	} else {
-		oauth_params_add(&args, "refresh_token", auth);
-	}
-	args_s = oauth_params_string(args);
-	oauth_params_free(&args);
-
-	s = g_strdup_printf("POST %s HTTP/1.0\r\n"
-	                    "Host: %s\r\n"
-	                    "Content-Type: application/x-www-form-urlencoded\r\n"
-	                    "Content-Length: %zd\r\n"
-	                    "\r\n"
-	                    "%s", url_p.file, url_p.host, strlen(args_s), args_s);
-	g_free(args_s);
-
-	cb_data = g_new0(struct oauth2_access_token_data, 1);
-	cb_data->func = func;
-	cb_data->data = data;
-
-	req = http_dorequest(url_p.host, url_p.port, url_p.proto == PROTO_HTTPS,
-	                     s, oauth2_access_token_done, cb_data);
-
-	g_free(s);
-
-	if (req == NULL) {
-		g_free(cb_data);
-	}
-
-	return req != NULL;
-}
-
-static char* oauth2_parse_error(json_value *e)
-{
-	/* This does a reasonable job with some of the flavours of error
-	   responses I've seen. Because apparently it's not standardised. */
-
-	if (e->type == json_object) {
-		/* Facebook style */
-		const char *msg = json_o_str(e, "message");
-		const char *type = json_o_str(e, "type");
-		json_value *code_o = json_o_get(e, "code");
-		int code = 0;
-
-		if (code_o && code_o->type == json_integer) {
-			code = code_o->u.integer;
-		}
-
-		return g_strdup_printf("Error %d: %s", code, msg ? msg : type ? type : "Unknown error");
-	} else if (e->type == json_string) {
-		return g_strdup(e->u.string.ptr);
-	}
-	return NULL;
-}
-
-static void oauth2_access_token_done(struct http_request *req)
-{
-	struct oauth2_access_token_data *cb_data = req->data;
-	char *atoken = NULL, *rtoken = NULL, *error = NULL;
-	char *content_type = NULL;
-
-	if (req->status_code <= 0 && !req->reply_body) {
-		cb_data->func(cb_data->data, NULL, NULL, req->status_string);
-		g_free(cb_data);
-		return;
-	}
-
-	if (getenv("BITLBEE_DEBUG")) {
-		printf("%s\n", req->reply_body);
-	}
-
-	content_type = get_rfc822_header(req->reply_headers, "Content-Type", 0);
-
-	if (content_type && (strstr(content_type, "application/json") ||
-	                     strstr(content_type, "text/javascript"))) {
-		json_value *js = json_parse(req->reply_body, req->body_size);
-		if (js && js->type == json_object) {
-			JSON_O_FOREACH(js, k, v){
-				if (strcmp(k, "error") == 0) {
-					error = oauth2_parse_error(v);
-				}
-				if (v->type != json_string) {
-					continue;
-				}
-				if (strcmp(k, "access_token") == 0) {
-					atoken = g_strdup(v->u.string.ptr);
-				}
-				if (strcmp(k, "refresh_token") == 0) {
-					rtoken = g_strdup(v->u.string.ptr);
-				}
-			}
-		}
-		json_value_free(js);
-	} else {
-		/* Facebook use their own odd format here, seems to be URL-encoded. */
-		GSList *p_in = NULL;
-
-		oauth_params_parse(&p_in, req->reply_body);
-		atoken = g_strdup(oauth_params_get(&p_in, "access_token"));
-		rtoken = g_strdup(oauth_params_get(&p_in, "refresh_token"));
-		oauth_params_free(&p_in);
-	}
-	if (getenv("BITLBEE_DEBUG")) {
-		printf("Extracted atoken=%s rtoken=%s\n", atoken, rtoken);
-	}
-	if (!atoken && !rtoken && !error) {
-		error = g_strdup("Unusable response");
-	}
-
-	cb_data->func(cb_data->data, atoken, rtoken, error);
-	g_free(content_type);
-	g_free(atoken);
-	g_free(rtoken);
-	g_free(error);
-	g_free(cb_data);
-}
Index: b/oauth2.h
===================================================================
--- lib/oauth2.h	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,50 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Simple OAuth2 client (consumer) implementation.                          *
-*                                                                           *
-*  Copyright 2010-2013 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.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-\***************************************************************************/
-
-/* Implementation mostly based on my experience with writing the previous OAuth
-   module, and from http://code.google.com/apis/accounts/docs/OAuth2.html . */
-
-typedef void (*oauth2_token_callback)(gpointer data, const char *atoken,
-                                      const char *rtoken, const char *error);
-
-struct oauth2_service {
-	char *auth_url;
-	char *token_url;
-	char *redirect_url;
-	char *scope;
-	char *consumer_key;
-	char *consumer_secret;
-};
-
-#define OAUTH2_AUTH_CODE "authorization_code"
-#define OAUTH2_AUTH_REFRESH "refresh_token"
-
-/* Generate a URL the user should open in his/her browser to get an
-   authorization code. */
-char *oauth2_url(const struct oauth2_service *sp);
-
-/* Exchanges an auth code or refresh token for an access token.
-   auth_type is one of the two OAUTH2_AUTH_.. constants above. */
-int oauth2_access_token(const struct oauth2_service *sp,
-                        const char *auth_type, const char *auth,
-                        oauth2_token_callback func, gpointer data);
Index: b/proxy.c
===================================================================
--- lib/proxy.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,594 +1,0 @@
-/*
- * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- *
- */
-
-#define BITLBEE_CORE
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include "nogaim.h"
-#include "proxy.h"
-#include "base64.h"
-
-char proxyhost[128] = "";
-int proxyport = 0;
-int proxytype = PROXY_NONE;
-char proxyuser[128] = "";
-char proxypass[128] = "";
-
-/* Some systems don't know this one. It's not essential, so set it to 0 then. */
-#ifndef AI_NUMERICSERV
-#define AI_NUMERICSERV 0
-#endif
-#ifndef AI_ADDRCONFIG
-#define AI_ADDRCONFIG 0
-#endif
-
-static GHashTable *phb_hash = NULL;
-
-struct PHB {
-	b_event_handler func, proxy_func;
-	gpointer data, proxy_data;
-	char *host;
-	int port;
-	int fd;
-	gint inpa;
-	struct addrinfo *gai, *gai_cur;
-};
-
-typedef int (*proxy_connect_func)(const char *host, unsigned short port_, struct PHB *phb);
-
-static int proxy_connect_none(const char *host, unsigned short port_, struct PHB *phb);
-
-static gboolean phb_free(struct PHB *phb, gboolean success)
-{
-	g_hash_table_remove(phb_hash, &phb->fd);
-
-	if (!success) {
-		if (phb->fd > 0) {
-			closesocket(phb->fd);
-		}
-		if (phb->func) {
-			phb->func(phb->data, -1, B_EV_IO_READ);
-		}
-	}
-	if (phb->gai) {
-		freeaddrinfo(phb->gai);
-	}
-	g_free(phb->host);
-	g_free(phb);
-	return FALSE;
-}
-
-/* calls phb->func safely by ensuring that the phb struct doesn't exist in the
- * case that proxy_disconnect() is called down there */
-static gboolean phb_connected(struct PHB *phb, gint source)
-{
-	/* save func and data here */
-	b_event_handler func = phb->func;
-	gpointer data = phb->data;
-
-	/* free the struct so that it can't be freed by the callback */
-	phb_free(phb, TRUE);
-
-	/* if any proxy_disconnect() call happens here, it will use the
-	 * fd (still open), look it up in the hash table, get NULL, and
-	 * proceed to close the fd and do nothing else */
-	func(data, source, B_EV_IO_READ);
-
-	return FALSE;
-}
-
-static gboolean proxy_connected(gpointer data, gint source, b_input_condition cond)
-{
-	struct PHB *phb = data;
-	socklen_t len;
-	int error = ETIMEDOUT;
-
-	len = sizeof(error);
-
-	if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0 || error) {
-		if ((phb->gai_cur = phb->gai_cur->ai_next)) {
-			int new_fd;
-			b_event_remove(phb->inpa);
-			if ((new_fd = proxy_connect_none(NULL, 0, phb))) {
-				b_event_remove(phb->inpa);
-				closesocket(source);
-				dup2(new_fd, source);
-				closesocket(new_fd);
-				phb->fd = source;
-				phb->inpa = b_input_add(source, B_EV_IO_WRITE, proxy_connected, phb);
-				return FALSE;
-			}
-		}
-		closesocket(source);
-		source = -1;
-		/* socket is dead, but continue to clean up */
-	} else {
-		sock_make_blocking(source);
-	}
-
-	freeaddrinfo(phb->gai);
-	phb->gai = NULL;
-
-	b_event_remove(phb->inpa);
-	phb->inpa = 0;
-
-	if (phb->proxy_func) {
-		phb->proxy_func(phb->proxy_data, source, B_EV_IO_READ);
-	} else {
-		phb_connected(phb, source);
-	}
-
-	return FALSE;
-}
-
-static int proxy_connect_none(const char *host, unsigned short port_, struct PHB *phb)
-{
-	struct sockaddr_in me;
-	int fd = -1;
-
-	if (phb->gai_cur == NULL) {
-		int ret;
-		char port[6];
-		struct addrinfo hints;
-
-		g_snprintf(port, sizeof(port), "%d", port_);
-
-		memset(&hints, 0, sizeof(struct addrinfo));
-		hints.ai_family = AF_UNSPEC;
-		hints.ai_socktype = SOCK_STREAM;
-		hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
-
-		if (!(ret = getaddrinfo(host, port, &hints, &phb->gai))) {
-			phb->gai_cur = phb->gai;
-		} else {
-			event_debug("gai(): %s\n", gai_strerror(ret));
-		}
-	}
-
-	for (; phb->gai_cur; phb->gai_cur = phb->gai_cur->ai_next) {
-		if ((fd = socket(phb->gai_cur->ai_family, phb->gai_cur->ai_socktype, phb->gai_cur->ai_protocol)) < 0) {
-			event_debug("socket failed: %d\n", errno);
-			continue;
-		}
-
-		sock_make_nonblocking(fd);
-
-		if (global.conf->iface_out) {
-			me.sin_family = AF_INET;
-			me.sin_port = 0;
-			me.sin_addr.s_addr = inet_addr(global.conf->iface_out);
-
-			if (bind(fd, (struct sockaddr *) &me, sizeof(me)) != 0) {
-				event_debug("bind( %d, \"%s\" ) failure\n", fd, global.conf->iface_out);
-			}
-		}
-
-		event_debug("proxy_connect_none( \"%s\", %d ) = %d\n", host, port_, fd);
-
-		if (connect(fd, phb->gai_cur->ai_addr, phb->gai_cur->ai_addrlen) < 0 && !sockerr_again()) {
-			event_debug("connect failed: %s\n", strerror(errno));
-			closesocket(fd);
-			fd = -1;
-			continue;
-		} else {
-			phb->inpa = b_input_add(fd, B_EV_IO_WRITE, proxy_connected, phb);
-			phb->fd = fd;
-
-			break;
-		}
-	}
-
-	if (fd < 0 && host) {
-		phb_free(phb, TRUE);
-	}
-
-	return fd;
-}
-
-
-/* Connecting to HTTP proxies */
-
-#define HTTP_GOODSTRING "HTTP/1.0 200"
-#define HTTP_GOODSTRING2 "HTTP/1.1 200"
-
-static gboolean http_canread(gpointer data, gint source, b_input_condition cond)
-{
-	int nlc = 0;
-	int pos = 0;
-	struct PHB *phb = data;
-	char inputline[8192];
-
-	b_event_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)) {
-		return phb_connected(phb, source);
-	}
-
-	return phb_free(phb, FALSE);
-}
-
-static gboolean http_canwrite(gpointer data, gint source, b_input_condition cond)
-{
-	char cmd[384];
-	struct PHB *phb = data;
-	socklen_t len;
-	int error = ETIMEDOUT;
-
-	if (phb->inpa > 0) {
-		b_event_remove(phb->inpa);
-	}
-	len = sizeof(error);
-	if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
-		return phb_free(phb, FALSE);
-	}
-	sock_make_blocking(source);
-
-	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) {
-		return phb_free(phb, FALSE);
-	}
-
-	if (strlen(proxyuser) > 0) {
-		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) {
-			return phb_free(phb, FALSE);
-		}
-	}
-
-	g_snprintf(cmd, sizeof(cmd), "\r\n");
-	if (send(source, cmd, strlen(cmd), 0) < 0) {
-		return phb_free(phb, FALSE);
-	}
-
-	phb->inpa = b_input_add(source, B_EV_IO_READ, http_canread, phb);
-
-	return FALSE;
-}
-
-static int proxy_connect_http(const 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 gboolean s4_canread(gpointer data, gint source, b_input_condition cond)
-{
-	unsigned char packet[12];
-	struct PHB *phb = data;
-
-	b_event_remove(phb->inpa);
-
-	memset(packet, 0, sizeof(packet));
-	if (read(source, packet, 9) >= 4 && packet[1] == 90) {
-		return phb_connected(phb, source);
-	}
-
-	return phb_free(phb, FALSE);
-}
-
-static gboolean s4_canwrite(gpointer data, gint source, b_input_condition cond)
-{
-	unsigned char packet[12];
-	struct hostent *hp;
-	struct PHB *phb = data;
-	socklen_t len;
-	int error = ETIMEDOUT;
-	gboolean is_socks4a = (proxytype == PROXY_SOCKS4A);
-
-	if (phb->inpa > 0) {
-		b_event_remove(phb->inpa);
-	}
-	len = sizeof(error);
-	if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
-		return phb_free(phb, FALSE);
-	}
-	sock_make_blocking(source);
-
-	if (!is_socks4a && !(hp = gethostbyname(phb->host))) {
-		return phb_free(phb, FALSE);
-	}
-
-	packet[0] = 4;
-	packet[1] = 1;
-	packet[2] = phb->port >> 8;
-	packet[3] = phb->port & 0xff;
-	if (is_socks4a) {
-		packet[4] = 0;
-		packet[5] = 0;
-		packet[6] = 0;
-		packet[7] = 1;
-	} else {
-		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) {
-		return phb_free(phb, FALSE);
-	}
-
-	if (is_socks4a) {
-		size_t host_len = strlen(phb->host) + 1; /* include the \0 */
-
-		if (write(source, phb->host, host_len) != host_len) {
-			return phb_free(phb, FALSE);
-		}
-	}
-
-	phb->inpa = b_input_add(source, B_EV_IO_READ, s4_canread, phb);
-
-	return FALSE;
-}
-
-static int proxy_connect_socks4(const 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 gboolean s5_canread_again(gpointer data, gint source, b_input_condition cond)
-{
-	unsigned char buf[512];
-	struct PHB *phb = data;
-
-	b_event_remove(phb->inpa);
-
-	if (read(source, buf, 10) < 10) {
-		return phb_free(phb, FALSE);
-	}
-	if ((buf[0] != 0x05) || (buf[1] != 0x00)) {
-		return phb_free(phb, FALSE);
-	}
-
-	return phb_connected(phb, source);
-}
-
-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)) {
-		phb_free(phb, FALSE);
-		return;
-	}
-
-	phb->inpa = b_input_add(source, B_EV_IO_READ, s5_canread_again, phb);
-}
-
-static gboolean s5_readauth(gpointer data, gint source, b_input_condition cond)
-{
-	unsigned char buf[512];
-	struct PHB *phb = data;
-
-	b_event_remove(phb->inpa);
-
-	if (read(source, buf, 2) < 2) {
-		return phb_free(phb, FALSE);
-	}
-
-	if ((buf[0] != 0x01) || (buf[1] != 0x00)) {
-		return phb_free(phb, FALSE);
-	}
-
-	s5_sendconnect(phb, source);
-
-	return FALSE;
-}
-
-static gboolean s5_canread(gpointer data, gint source, b_input_condition cond)
-{
-	unsigned char buf[512];
-	struct PHB *phb = data;
-
-	b_event_remove(phb->inpa);
-
-	if (read(source, buf, 2) < 2) {
-		return phb_free(phb, FALSE);
-	}
-
-	if ((buf[0] != 0x05) || (buf[1] == 0xff)) {
-		return phb_free(phb, FALSE);
-	}
-
-	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) {
-			return phb_free(phb, FALSE);
-		}
-
-		phb->inpa = b_input_add(source, B_EV_IO_READ, s5_readauth, phb);
-	} else {
-		s5_sendconnect(phb, source);
-	}
-
-	return FALSE;
-}
-
-static gboolean s5_canwrite(gpointer data, gint source, b_input_condition cond)
-{
-	unsigned char buf[512];
-	int i;
-	struct PHB *phb = data;
-	socklen_t len;
-	int error = ETIMEDOUT;
-
-	if (phb->inpa > 0) {
-		b_event_remove(phb->inpa);
-	}
-	len = sizeof(error);
-	if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
-		return phb_free(phb, FALSE);
-	}
-	sock_make_blocking(source);
-
-	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) {
-		return phb_free(phb, FALSE);
-	}
-
-	phb->inpa = b_input_add(source, B_EV_IO_READ, s5_canread, phb);
-
-	return FALSE;
-}
-
-static int proxy_connect_socks5(const 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));
-}
-
-static const proxy_connect_func proxy_connect_funcs_array[] = {
-	proxy_connect_none,   /* PROXY_NONE */
-	proxy_connect_http,   /* PROXY_HTTP */
-	proxy_connect_socks4, /* PROXY_SOCKS4 */
-	proxy_connect_socks5, /* PROXY_SOCKS5 */
-	proxy_connect_socks4, /* PROXY_SOCKS4A */
-};
-
-/* Export functions */
-
-int proxy_connect(const char *host, int port, b_event_handler func, gpointer data)
-{
-	struct PHB *phb;
-	proxy_connect_func fun;
-	int fd;
-
-	if (!phb_hash) {
-		phb_hash = g_hash_table_new(g_int_hash, g_int_equal);
-	}
-
-	if (!host || port <= 0 || !func || strlen(host) > 128) {
-		return -1;
-	}
-
-	phb = g_new0(struct PHB, 1);
-	phb->func = func;
-	phb->data = data;
-
-	if (proxyhost[0] && proxyport > 0 && proxytype >= 0 && proxytype < G_N_ELEMENTS(proxy_connect_funcs_array)) {
-		fun = proxy_connect_funcs_array[proxytype];
-	} else {
-		fun = proxy_connect_none;
-	}
-
-	fd = fun(host, port, phb);
-
-	if (fd != -1) {
-		g_hash_table_insert(phb_hash, &phb->fd, phb);
-	}
-
-	return fd;
-}
-
-void proxy_disconnect(int fd)
-{
-	struct PHB *phb = g_hash_table_lookup(phb_hash, &fd);
-
-	if (!phb) {
-		/* not in the early part of the connection - just close the fd */
-		closesocket(fd);
-		return;
-	}
-
-	if (phb->inpa) {
-		b_event_remove(phb->inpa);
-		phb->inpa = 0;
-	}
-
-	/* avoid calling the callback, which might result in double-free */
-	phb->func = NULL;
-
-	/* close and free */
-	phb_free(phb, FALSE);
-}
Index: b/proxy.h
===================================================================
--- lib/proxy.h	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,53 +1,0 @@
-/*
- * nogaim
- *
- * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  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>
-#include <sys/socket.h>
-#include <netdb.h>
-#include <netinet/in.h>
-#include <glib.h>
-#include <gmodule.h>
-
-#include "events.h"
-
-#define PROXY_NONE 0
-#define PROXY_HTTP 1
-#define PROXY_SOCKS4 2
-#define PROXY_SOCKS5 3
-#define PROXY_SOCKS4A 4
-
-extern char proxyhost[128];
-extern int proxyport;
-extern int proxytype;
-extern char proxyuser[128];
-extern char proxypass[128];
-
-G_MODULE_EXPORT int proxy_connect(const char *host, int port, b_event_handler func, gpointer data);
-G_MODULE_EXPORT void proxy_disconnect(int fd);
-
-#endif /* _PROXY_H_ */
Index: b/sha1.c
===================================================================
--- lib/sha1.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,124 +1,0 @@
-#include "sha1.h"
-#include <string.h>
-#include <stdio.h>
-
-
-void sha1_init(sha1_state_t *ctx)
-{
-	*ctx = g_checksum_new(G_CHECKSUM_SHA1);
-}
-
-void sha1_append(sha1_state_t *ctx, const guint8 * message_array, guint len)
-{
-	g_checksum_update(*ctx, message_array, len);
-}
-
-void sha1_finish(sha1_state_t *ctx, guint8 digest[SHA1_HASH_SIZE])
-{
-	gsize digest_len = SHA1_HASH_SIZE;
-
-	g_checksum_get_digest(*ctx, digest, &digest_len);
-	g_checksum_free(*ctx);
-}
-
-#define HMAC_BLOCK_SIZE 64
-
-void b_hmac(GChecksumType checksum_type, const char *key_, size_t key_len,
-            const char *payload, size_t payload_len, guint8 **digest)
-{
-	GChecksum *checksum;
-	size_t hash_len;
-	guint8 *hash;
-	guint8 key[HMAC_BLOCK_SIZE + 1];
-	int i;
-
-	hash_len = g_checksum_type_get_length(checksum_type);
-
-	if (hash_len == (size_t) -1) {
-		return;
-	}
-
-	hash = g_malloc(hash_len);
-
-	if (key_len == 0) {
-		key_len = strlen(key_);
-	}
-	if (payload_len == 0) {
-		payload_len = strlen(payload);
-	}
-
-	/* Create K. If our current key is >64 chars we have to hash it,
-	   otherwise just pad. */
-	memset(key, 0, HMAC_BLOCK_SIZE + 1);
-	if (key_len > HMAC_BLOCK_SIZE) {
-		checksum = g_checksum_new(checksum_type);
-		g_checksum_update(checksum, (guint8 *) key_, key_len);
-		g_checksum_get_digest(checksum, key, &hash_len);
-		g_checksum_free(checksum);
-	} else {
-		memcpy(key, key_, key_len);
-	}
-
-	/* Inner part: H(K XOR 0x36, text) */
-	checksum = g_checksum_new(checksum_type);
-	for (i = 0; i < HMAC_BLOCK_SIZE; i++) {
-		key[i] ^= 0x36;
-	}
-	g_checksum_update(checksum, key, HMAC_BLOCK_SIZE);
-	g_checksum_update(checksum, (const guint8 *) payload, payload_len);
-	g_checksum_get_digest(checksum, hash, &hash_len);
-	g_checksum_free(checksum);
-
-	/* Final result: H(K XOR 0x5C, inner stuff) */
-	checksum = g_checksum_new(checksum_type);
-	for (i = 0; i < HMAC_BLOCK_SIZE; i++) {
-		key[i] ^= 0x36 ^ 0x5c;
-	}
-	g_checksum_update(checksum, key, HMAC_BLOCK_SIZE);
-	g_checksum_update(checksum, hash, hash_len);
-	g_checksum_get_digest(checksum, *digest, &hash_len);
-	g_checksum_free(checksum);
-
-	g_free(hash);
-}
-
-void sha1_hmac(const char *key_, size_t key_len, const char *payload, size_t payload_len, guint8 digest[SHA1_HASH_SIZE])
-{
-	b_hmac(G_CHECKSUM_SHA1, key_, key_len, payload, payload_len, &digest);
-}
-
-
-/* I think this follows the scheme described on:
-   http://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_.28random.29
-   My random data comes from a SHA1 generator but hey, it's random enough for
-   me, and RFC 4122 looks way more complicated than I need this to be.
-
-   Returns a value that must be free()d. */
-char *sha1_random_uuid(sha1_state_t * context)
-{
-	guint8 dig[SHA1_HASH_SIZE];
-	char *ret = g_new0(char, 40);   /* 36 chars + \0 */
-	int i, p;
-	gsize digest_len = SHA1_HASH_SIZE;
-
-	g_checksum_get_digest(*context, dig, &digest_len);
-	g_checksum_free(*context);
-
-	for (p = i = 0; i < 16; i++) {
-		if (i == 4 || i == 6 || i == 8 || i == 10) {
-			ret[p++] = '-';
-		}
-		if (i == 6) {
-			dig[i] = (dig[i] & 0x0f) | 0x40;
-		}
-		if (i == 8) {
-			dig[i] = (dig[i] & 0x30) | 0x80;
-		}
-
-		sprintf(ret + p, "%02x", dig[i]);
-		p += 2;
-	}
-	ret[p] = '\0';
-
-	return ret;
-}
Index: b/sha1.h
===================================================================
--- lib/sha1.h	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,24 +1,0 @@
-
-#ifndef _SHA1_H_
-#define _SHA1_H_
-
-#include <glib.h>
-#include <gmodule.h>
-
-#ifdef __GNUC__
-#define __SHA1_NON_PUBLIC_DEPRECATION__ __attribute__((deprecated("sha1.h will be removed from Bitlbee's public API. Please use another library (such as GLib's gchecksum) instead")))
-#else
-#define __SHA1_NON_PUBLIC_DEPRECATION__
-#endif
-
-#define SHA1_HASH_SIZE 20
-
-typedef GChecksum *sha1_state_t;
-
-void sha1_init(sha1_state_t *) __SHA1_NON_PUBLIC_DEPRECATION__;
-void sha1_append(sha1_state_t *, const guint8 *, unsigned int) __SHA1_NON_PUBLIC_DEPRECATION__;
-void sha1_finish(sha1_state_t *, guint8 digest[SHA1_HASH_SIZE]) __SHA1_NON_PUBLIC_DEPRECATION__;
-void sha1_hmac(const char *, size_t, const char *, size_t, guint8 digest[SHA1_HASH_SIZE]) ;
-char *sha1_random_uuid(sha1_state_t *);
-
-#endif
Index: b/ssl_client.h
===================================================================
--- lib/ssl_client.h	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,106 +1,0 @@
-/********************************************************************\
-  * 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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-/* ssl_client makes it easier to open SSL connections to servers. (It
-   doesn't offer SSL server functionality yet, but it could be useful
-   to add it later.) Different ssl_client modules are available, and
-   ssl_client tries to make them all behave the same. It's very simple
-   and basic, it just imitates the proxy_connect() function from the
-   Gaim libs and passes the socket to the program once the handshake
-   is completed. */
-
-#include <glib.h>
-#include "proxy.h"
-
-/* Some generic error codes. Especially SSL_AGAIN is important if you
-   want to do asynchronous I/O. */
-#define SSL_OK            0
-#define SSL_NOHANDSHAKE   1
-#define SSL_AGAIN         2
-#define VERIFY_CERT_ERROR 2
-#define VERIFY_CERT_INVALID 4
-#define VERIFY_CERT_REVOKED 8
-#define VERIFY_CERT_SIGNER_NOT_FOUND 16
-#define VERIFY_CERT_SIGNER_NOT_CA 32
-#define VERIFY_CERT_INSECURE_ALGORITHM 64
-#define VERIFY_CERT_NOT_ACTIVATED 128
-#define VERIFY_CERT_EXPIRED 256
-#define VERIFY_CERT_WRONG_HOSTNAME 512
-
-extern int ssl_errno;
-
-/* This is what your callback function should look like. */
-typedef gboolean (*ssl_input_function)(gpointer, int, void*, b_input_condition);
-
-
-/* Perform any global initialization the SSL library might need. */
-G_MODULE_EXPORT void ssl_init(void);
-
-/* Connect to host:port, call the given function when the connection is
-   ready to be used for SSL traffic. This is all done asynchronously, no
-   blocking I/O! (Except for the DNS lookups, for now...) */
-G_MODULE_EXPORT void *ssl_connect(char *host, int port, gboolean verify, ssl_input_function func, gpointer data);
-
-/* Start an SSL session on an existing fd. Useful for STARTTLS functionality,
-   for example in Jabber. */
-G_MODULE_EXPORT void *ssl_starttls(int fd, char *hostname, gboolean verify, ssl_input_function func, gpointer data);
-
-/* Obviously you need special read/write functions to read 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);
-
-/* Now needed by most SSL libs. See for more info:
-   http://www.gnu.org/software/gnutls/manual/gnutls.html#index-gnutls_005frecord_005fcheck_005fpending-209
-   http://www.openssl.org/docs/ssl/SSL_pending.html
-
-   Required because OpenSSL empties the TCP buffer completely but doesn't
-   necessarily give us all the unencrypted data. Or maybe you didn't ask
-   for all of it because your buffer is too small.
-
-   Returns 0 if there's nothing left, 1 if there's more data. */
-G_MODULE_EXPORT int ssl_pending(void *conn);
-
-/* Abort the SSL connection and disconnect the socket. Do not use close()
-   directly, both the SSL library and the peer will be unhappy! */
-G_MODULE_EXPORT void ssl_disconnect(void *conn_);
-
-/* Get the fd for this connection, you will usually need it for event
-   handling. */
-G_MODULE_EXPORT int ssl_getfd(void *conn);
-
-/* This function returns B_EV_IO_READ/WRITE. With SSL connections it's
-   possible that something has to be read while actually were trying to
-   write something (think about key exchange/refresh/etc). So when an
-   SSL operation returned SSL_AGAIN, *always* use this function when
-   adding an event handler to the queue. (And it should perform exactly
-   the same action as the handler that just received the SSL_AGAIN.) */
-G_MODULE_EXPORT b_input_condition ssl_getdirection(void *conn);
-
-/* Converts a verification bitfield passed to ssl_input_function into
-   a more useful string. Or NULL if it had no useful bits set. */
-G_MODULE_EXPORT char *ssl_verify_strerror(int code);
-
-G_MODULE_EXPORT size_t ssl_des3_encrypt(const unsigned char *key, size_t key_len, const unsigned char *input,
-                                        size_t input_len, const unsigned char *iv, unsigned char **res);
Index: b/ssl_gnutls.c
===================================================================
--- lib/ssl_gnutls.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,493 +1,0 @@
-/********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2012 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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#include <gnutls/gnutls.h>
-#include <gnutls/x509.h>
-#include <gcrypt.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include "proxy.h"
-#include "ssl_client.h"
-#include "sock.h"
-#include "stdlib.h"
-#include "bitlbee.h"
-
-int ssl_errno = 0;
-
-static gboolean initialized = FALSE;
-gnutls_certificate_credentials_t xcred;
-
-#include <limits.h>
-
-#define SSLDEBUG 0
-
-struct scd {
-	ssl_input_function func;
-	gpointer data;
-	int fd;
-	gboolean established;
-	int inpa;
-	char *hostname;
-	gboolean verify;
-
-	gnutls_session_t session;
-};
-
-static GHashTable *session_cache;
-
-static gboolean ssl_connected(gpointer data, gint source, b_input_condition cond);
-static gboolean ssl_starttls_real(gpointer data, gint source, b_input_condition cond);
-static gboolean ssl_handshake(gpointer data, gint source, b_input_condition cond);
-
-static void ssl_deinit(void);
-
-static void ssl_log(int level, const char *line)
-{
-	printf("%d %s", level, line);
-}
-
-void ssl_init(void)
-{
-	if (initialized) {
-		return;
-	}
-
-	gnutls_global_init();
-	gnutls_certificate_allocate_credentials(&xcred);
-	if (global.conf->cafile) {
-		gnutls_certificate_set_x509_trust_file(xcred, global.conf->cafile, GNUTLS_X509_FMT_PEM);
-
-		/* Not needed in GnuTLS 2.11+ (enabled by default there) so
-		   don't do it (resets possible other defaults). */
-		if (!gnutls_check_version("2.11")) {
-			gnutls_certificate_set_verify_flags(xcred, GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
-		}
-	}
-	initialized = TRUE;
-
-	gnutls_global_set_log_function(ssl_log);
-	/*
-	gnutls_global_set_log_level( 3 );
-	*/
-
-	session_cache = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
-
-	atexit(ssl_deinit);
-}
-
-static void ssl_deinit(void)
-{
-	gnutls_global_deinit();
-	gnutls_certificate_free_credentials(xcred);
-	g_hash_table_destroy(session_cache);
-	session_cache = NULL;
-}
-
-void *ssl_connect(char *host, int port, gboolean verify, ssl_input_function func, gpointer data)
-{
-	struct scd *conn = g_new0(struct scd, 1);
-
-	conn->func = func;
-	conn->data = data;
-	conn->inpa = -1;
-	conn->hostname = g_strdup(host);
-	conn->verify = verify && global.conf->cafile;
-	conn->fd = proxy_connect(host, port, ssl_connected, conn);
-
-	if (conn->fd < 0) {
-		g_free(conn->hostname);
-		g_free(conn);
-		return NULL;
-	}
-
-	return conn;
-}
-
-void *ssl_starttls(int fd, char *hostname, gboolean verify, ssl_input_function func, gpointer data)
-{
-	struct scd *conn = g_new0(struct scd, 1);
-
-	conn->fd = fd;
-	conn->func = func;
-	conn->data = data;
-	conn->inpa = -1;
-	conn->hostname = g_strdup(hostname);
-
-	/* For now, SSL verification is globally enabled by setting the cafile
-	   setting in bitlbee.conf. Commented out by default because probably
-	   not everyone has this file in the same place and plenty of folks
-	   may not have the cert of their private Jabber server in it. */
-	conn->verify = verify && global.conf->cafile;
-
-	/* This function should be called via a (short) timeout instead of
-	   directly from here, because these SSL calls are *supposed* to be
-	   *completely* asynchronous and not ready yet when this function
-	   (or *_connect, for examle) returns. Also, errors are reported via
-	   the callback function, not via this function's return value.
-
-	   In short, doing things like this makes the rest of the code a lot
-	   simpler. */
-
-	b_timeout_add(1, ssl_starttls_real, conn);
-
-	return conn;
-}
-
-static gboolean ssl_starttls_real(gpointer data, gint source, b_input_condition cond)
-{
-	struct scd *conn = data;
-
-	return ssl_connected(conn, conn->fd, B_EV_IO_WRITE);
-}
-
-static int verify_certificate_callback(gnutls_session_t session)
-{
-	unsigned int status;
-	const gnutls_datum_t *cert_list;
-	unsigned int cert_list_size;
-	int gnutlsret;
-	int verifyret = 0;
-	gnutls_x509_crt_t cert;
-	struct scd *conn;
-
-	conn = gnutls_session_get_ptr(session);
-
-	gnutlsret = gnutls_certificate_verify_peers2(session, &status);
-	if (gnutlsret < 0) {
-		return VERIFY_CERT_ERROR;
-	}
-
-	if (status & GNUTLS_CERT_INVALID) {
-		verifyret |= VERIFY_CERT_INVALID;
-	}
-
-	if (status & GNUTLS_CERT_REVOKED) {
-		verifyret |= VERIFY_CERT_REVOKED;
-	}
-
-	if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) {
-		verifyret |= VERIFY_CERT_SIGNER_NOT_FOUND;
-	}
-
-	if (status & GNUTLS_CERT_SIGNER_NOT_CA) {
-		verifyret |= VERIFY_CERT_SIGNER_NOT_CA;
-	}
-
-	if (status & GNUTLS_CERT_INSECURE_ALGORITHM) {
-		verifyret |= VERIFY_CERT_INSECURE_ALGORITHM;
-	}
-
-#ifdef GNUTLS_CERT_NOT_ACTIVATED
-	/* Amusingly, the GnuTLS function used above didn't check for expiry
-	   until GnuTLS 2.8 or so. (See CVE-2009-1417) */
-	if (status & GNUTLS_CERT_NOT_ACTIVATED) {
-		verifyret |= VERIFY_CERT_NOT_ACTIVATED;
-	}
-
-	if (status & GNUTLS_CERT_EXPIRED) {
-		verifyret |= VERIFY_CERT_EXPIRED;
-	}
-#endif
-
-	if (gnutls_certificate_type_get(session) != GNUTLS_CRT_X509 || gnutls_x509_crt_init(&cert) < 0) {
-		return VERIFY_CERT_ERROR;
-	}
-
-	cert_list = gnutls_certificate_get_peers(session, &cert_list_size);
-	if (cert_list == NULL || gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0) {
-		return VERIFY_CERT_ERROR;
-	}
-
-	if (!gnutls_x509_crt_check_hostname(cert, conn->hostname)) {
-		verifyret |= VERIFY_CERT_INVALID;
-		verifyret |= VERIFY_CERT_WRONG_HOSTNAME;
-	}
-
-	gnutls_x509_crt_deinit(cert);
-
-	return verifyret;
-}
-
-struct ssl_session {
-	size_t size;
-	char data[];
-};
-
-static void ssl_cache_add(struct scd *conn)
-{
-	size_t data_size = 0;
-	struct ssl_session *data;
-	char *hostname;
-
-	if (!conn->hostname ||
-	    gnutls_session_get_data(conn->session, NULL, &data_size) != 0) {
-		return;
-	}
-
-	data = g_malloc(sizeof(struct ssl_session) + data_size);
-	if (gnutls_session_get_data(conn->session, data->data, &data_size) != 0) {
-		g_free(data);
-		return;
-	}
-
-	hostname = g_strdup(conn->hostname);
-	g_hash_table_insert(session_cache, hostname, data);
-}
-
-static void ssl_cache_resume(struct scd *conn)
-{
-	struct ssl_session *data;
-
-	if (conn->hostname &&
-	    (data = g_hash_table_lookup(session_cache, conn->hostname))) {
-		gnutls_session_set_data(conn->session, data->data, data->size);
-		g_hash_table_remove(session_cache, conn->hostname);
-	}
-}
-
-char *ssl_verify_strerror(int code)
-{
-	GString *ret = g_string_new("");
-
-	if (code & VERIFY_CERT_REVOKED) {
-		g_string_append(ret, "certificate has been revoked, ");
-	}
-	if (code & VERIFY_CERT_SIGNER_NOT_FOUND) {
-		g_string_append(ret, "certificate hasn't got a known issuer, ");
-	}
-	if (code & VERIFY_CERT_SIGNER_NOT_CA) {
-		g_string_append(ret, "certificate's issuer is not a CA, ");
-	}
-	if (code & VERIFY_CERT_INSECURE_ALGORITHM) {
-		g_string_append(ret, "certificate uses an insecure algorithm, ");
-	}
-	if (code & VERIFY_CERT_NOT_ACTIVATED) {
-		g_string_append(ret, "certificate has not been activated, ");
-	}
-	if (code & VERIFY_CERT_EXPIRED) {
-		g_string_append(ret, "certificate has expired, ");
-	}
-	if (code & VERIFY_CERT_WRONG_HOSTNAME) {
-		g_string_append(ret, "certificate hostname mismatch, ");
-	}
-
-	if (ret->len == 0) {
-		g_string_free(ret, TRUE);
-		return NULL;
-	} else {
-		g_string_truncate(ret, ret->len - 2);
-		return g_string_free(ret, FALSE);
-	}
-}
-
-static gboolean ssl_connected(gpointer data, gint source, b_input_condition cond)
-{
-	struct scd *conn = data;
-
-	if (source == -1) {
-		conn->func(conn->data, 0, NULL, cond);
-		g_free(conn->hostname);
-		g_free(conn);
-		return FALSE;
-	}
-
-	ssl_init();
-
-	gnutls_init(&conn->session, GNUTLS_CLIENT);
-	gnutls_session_set_ptr(conn->session, (void *) conn);
-#if GNUTLS_VERSION_NUMBER < 0x020c00
-	gnutls_transport_set_lowat(conn->session, 0);
-#endif
-	gnutls_set_default_priority(conn->session);
-	gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE, xcred);
-	if (conn->hostname && !g_hostname_is_ip_address(conn->hostname)) {
-		gnutls_server_name_set(conn->session, GNUTLS_NAME_DNS,
-		                       conn->hostname, strlen(conn->hostname));
-	}
-
-	sock_make_nonblocking(conn->fd);
-	gnutls_transport_set_ptr(conn->session, (gnutls_transport_ptr_t) (long) conn->fd);
-
-	ssl_cache_resume(conn);
-
-	return ssl_handshake(data, source, cond);
-}
-
-static gboolean ssl_handshake(gpointer data, gint source, b_input_condition cond)
-{
-	struct scd *conn = data;
-	int st, stver;
-
-	/* This function returns false, so avoid calling b_event_remove again */
-	conn->inpa = -1;
-
-	if ((st = gnutls_handshake(conn->session)) < 0) {
-		if (st == GNUTLS_E_AGAIN || st == GNUTLS_E_INTERRUPTED) {
-			conn->inpa = b_input_add(conn->fd, ssl_getdirection(conn),
-			                         ssl_handshake, data);
-		} else {
-			conn->func(conn->data, 0, NULL, cond);
-
-			ssl_disconnect(conn);
-		}
-	} else {
-		if (conn->verify && (stver = verify_certificate_callback(conn->session)) != 0) {
-			conn->func(conn->data, stver, NULL, cond);
-
-			ssl_disconnect(conn);
-		} else {
-			/* For now we can't handle non-blocking perfectly everywhere... */
-			sock_make_blocking(conn->fd);
-
-			ssl_cache_add(conn);
-			conn->established = TRUE;
-			conn->func(conn->data, 0, conn, cond);
-		}
-	}
-
-	return FALSE;
-}
-
-int ssl_read(void *conn, char *buf, int len)
-{
-	int st;
-
-	if (!((struct scd*) conn)->established) {
-		ssl_errno = SSL_NOHANDSHAKE;
-		return -1;
-	}
-
-	st = gnutls_record_recv(((struct scd*) conn)->session, buf, len);
-
-	ssl_errno = SSL_OK;
-	if (st == GNUTLS_E_AGAIN || st == GNUTLS_E_INTERRUPTED) {
-		ssl_errno = SSL_AGAIN;
-	}
-
-	if (SSLDEBUG && getenv("BITLBEE_DEBUG") && st > 0) {
-		len = write(2, buf, st);
-	}
-
-	return st;
-}
-
-int ssl_write(void *conn, const char *buf, int len)
-{
-	int st;
-
-	if (!((struct scd*) conn)->established) {
-		ssl_errno = SSL_NOHANDSHAKE;
-		return -1;
-	}
-
-	st = gnutls_record_send(((struct scd*) conn)->session, buf, len);
-
-	ssl_errno = SSL_OK;
-	if (st == GNUTLS_E_AGAIN || st == GNUTLS_E_INTERRUPTED) {
-		ssl_errno = SSL_AGAIN;
-	}
-
-	if (SSLDEBUG && getenv("BITLBEE_DEBUG") && st > 0) {
-		len = write(2, buf, st);
-	}
-
-	return st;
-}
-
-int ssl_pending(void *conn)
-{
-	if (conn == NULL) {
-		return 0;
-	}
-
-	if (!((struct scd*) conn)->established) {
-		ssl_errno = SSL_NOHANDSHAKE;
-		return 0;
-	}
-
-#if GNUTLS_VERSION_NUMBER >= 0x03000d && GNUTLS_VERSION_NUMBER <= 0x030012
-	if (ssl_errno == SSL_AGAIN) {
-		return 0;
-	}
-#endif
-
-	return gnutls_record_check_pending(((struct scd*) conn)->session) != 0;
-}
-
-void ssl_disconnect(void *conn_)
-{
-	struct scd *conn = conn_;
-
-	if (conn->inpa != -1) {
-		b_event_remove(conn->inpa);
-	}
-
-	if (conn->established) {
-		gnutls_bye(conn->session, GNUTLS_SHUT_WR);
-	}
-
-	proxy_disconnect(conn->fd);
-
-	if (conn->session) {
-		gnutls_deinit(conn->session);
-	}
-	g_free(conn->hostname);
-	g_free(conn);
-}
-
-int ssl_getfd(void *conn)
-{
-	return(((struct scd*) conn)->fd);
-}
-
-b_input_condition ssl_getdirection(void *conn)
-{
-	return(gnutls_record_get_direction(((struct scd*) conn)->session) ?
-	       B_EV_IO_WRITE : B_EV_IO_READ);
-}
-
-size_t ssl_des3_encrypt(const unsigned char *key, size_t key_len, const unsigned char *input,
-                        size_t input_len, const unsigned char *iv, unsigned char **res)
-{
-	gcry_cipher_hd_t gcr;
-	gcry_error_t st;
-
-	ssl_init();
-
-	*res = g_malloc(input_len);
-	st = gcry_cipher_open(&gcr, GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_CBC, 0) ||
-	     gcry_cipher_setkey(gcr, key, key_len) ||
-	     gcry_cipher_setiv(gcr, iv, 8) ||
-	     gcry_cipher_encrypt(gcr, *res, input_len, input, input_len);
-
-	gcry_cipher_close(gcr);
-
-	if (st == 0) {
-		return input_len;
-	}
-
-	g_free(*res);
-	return 0;
-}
Index: b/ssl_nss.c
===================================================================
--- lib/ssl_nss.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,439 +1,0 @@
-/********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2012 Wilmer van der Gaast and others                *
-  \********************************************************************/
-
-/* SSL module - NSS version                                             */
-
-/* Copyright 2005 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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  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 <pk11pub.h>
-#include <private/pprio.h>
-#include <ssl.h>
-#include <seccomon.h>
-#include <secerr.h>
-#include <sslerr.h>
-#include <assert.h>
-#include <unistd.h>
-
-int ssl_errno = 0;
-
-static gboolean initialized = FALSE;
-
-#define SSLDEBUG 0
-
-struct scd {
-	ssl_input_function func;
-	gpointer data;
-	int fd;
-	char *hostname;
-	PRFileDesc *prfd;
-	gboolean established;
-	gboolean verify;
-};
-
-static gboolean ssl_connected(gpointer data, gint source,
-                              b_input_condition cond);
-static gboolean ssl_starttls_real(gpointer data, gint source,
-                                  b_input_condition 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_init(void)
-{
-	PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
-	// https://www.mozilla.org/projects/security/pki/nss/ref/ssl/sslfnc.html#1234224
-	// This NSS function is not intended for use with SSL, which
-	// requires that the certificate and key database files be
-	// opened. Relates to whole non-verification of servers for now.
-	NSS_NoDB_Init(NULL);
-	NSS_SetDomesticPolicy();
-	initialized = TRUE;
-}
-
-void *ssl_connect(char *host, int port, gboolean verify,
-                  ssl_input_function 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;
-	conn->hostname = g_strdup(host);
-
-	if (conn->fd < 0) {
-		g_free(conn->hostname);
-		g_free(conn);
-		return (NULL);
-	}
-
-	if (!initialized) {
-		ssl_init();
-	}
-
-	return (conn);
-}
-
-static gboolean ssl_starttls_real(gpointer data, gint source,
-                                  b_input_condition cond)
-{
-	struct scd *conn = data;
-
-	return ssl_connected(conn, conn->fd, B_EV_IO_WRITE);
-}
-
-void *ssl_starttls(int fd, char *hostname, gboolean verify,
-                   ssl_input_function func, gpointer data)
-{
-	struct scd *conn = g_new0(struct scd, 1);
-
-	conn->fd = fd;
-	conn->func = func;
-	conn->data = data;
-	conn->hostname = g_strdup(hostname);
-
-	/* For now, SSL verification is globally enabled by setting the cafile
-	   setting in bitlbee.conf. Commented out by default because probably
-	   not everyone has this file in the same place and plenty of folks
-	   may not have the cert of their private Jabber server in it. */
-	conn->verify = verify && global.conf->cafile;
-
-	/* This function should be called via a (short) timeout instead of
-	   directly from here, because these SSL calls are *supposed* to be
-	   *completely* asynchronous and not ready yet when this function
-	   (or *_connect, for examle) returns. Also, errors are reported via
-	   the callback function, not via this function's return value.
-
-	   In short, doing things like this makes the rest of the code a lot
-	   simpler. */
-
-	b_timeout_add(1, ssl_starttls_real, conn);
-
-	return conn;
-}
-
-static gboolean ssl_connected(gpointer data, gint source,
-                              b_input_condition cond)
-{
-	struct scd *conn = data;
-
-	/* Right now we don't have any verification functionality for NSS. */
-
-	if (conn->verify) {
-		conn->func(conn->data, 1, NULL, cond);
-		if (source >= 0) {
-			closesocket(source);
-		}
-		g_free(conn->hostname);
-		g_free(conn);
-
-		return FALSE;
-	}
-
-	if (source == -1) {
-		goto ssl_connected_failure;
-	}
-
-	/* Until we find out how to handle non-blocking I/O with NSS... */
-	sock_make_blocking(conn->fd);
-
-	conn->prfd = SSL_ImportFD(NULL, PR_ImportTCPSocket(source));
-	if (!conn->prfd) {
-		goto ssl_connected_failure;
-	}
-	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_SetURL(conn->prfd, conn->hostname);
-	SSL_ResetHandshake(conn->prfd, PR_FALSE);
-
-	if (SSL_ForceHandshake(conn->prfd)) {
-		goto ssl_connected_failure;
-	}
-
-	conn->established = TRUE;
-	conn->func(conn->data, 0, conn, cond);
-	return FALSE;
-
-ssl_connected_failure:
-
-	conn->func(conn->data, 0, NULL, cond);
-
-	if (conn->prfd) {
-		PR_Close(conn->prfd);
-	} else if (source >= 0) {
-		/* proxy_disconnect() would be redundant here */
-		closesocket(source);
-	}
-	g_free(conn->hostname);
-	g_free(conn);
-
-	return FALSE;
-}
-
-int ssl_read(void *conn, char *buf, int len)
-{
-	int st;
-	PRErrorCode PR_err;
-
-	if (!((struct scd *) conn)->established) {
-		ssl_errno = SSL_NOHANDSHAKE;
-		return -1;
-	}
-
-	st = PR_Read(((struct scd *) conn)->prfd, buf, len);
-	PR_err = PR_GetError();
-
-	ssl_errno = SSL_OK;
-	if (PR_err == PR_WOULD_BLOCK_ERROR) {
-		ssl_errno = SSL_AGAIN;
-	}
-
-	if (SSLDEBUG && getenv("BITLBEE_DEBUG") && st > 0) {
-		len = write(STDERR_FILENO, buf, st);
-	}
-
-	return st;
-}
-
-int ssl_write(void *conn, const char *buf, int len)
-{
-	int st;
-	PRErrorCode PR_err;
-
-	if (!((struct scd *) conn)->established) {
-		ssl_errno = SSL_NOHANDSHAKE;
-		return -1;
-	}
-	st = PR_Write(((struct scd *) conn)->prfd, buf, len);
-	PR_err = PR_GetError();
-
-	ssl_errno = SSL_OK;
-	if (PR_err == PR_WOULD_BLOCK_ERROR) {
-		ssl_errno = SSL_AGAIN;
-	}
-
-	if (SSLDEBUG && getenv("BITLBEE_DEBUG") && st > 0) {
-		len = write(2, buf, st);
-	}
-
-	return st;
-}
-
-int ssl_pending(void *conn)
-{
-	struct scd *c = (struct scd *) conn;
-
-	if (c == NULL) {
-		return 0;
-	}
-
-	return (c->established && SSL_DataPending(c->prfd) > 0);
-}
-
-void ssl_disconnect(void *conn_)
-{
-	struct scd *conn = conn_;
-
-	// When we swich to NSS_Init, we should have here
-	// NSS_Shutdown();
-
-	if (conn->prfd) {
-		PR_Close(conn->prfd);
-	} else if (conn->fd) {
-		proxy_disconnect(conn->fd);
-	}
-
-	g_free(conn->hostname);
-	g_free(conn);
-}
-
-int ssl_getfd(void *conn)
-{
-	return (((struct scd *) conn)->fd);
-}
-
-b_input_condition ssl_getdirection(void *conn)
-{
-	/* Just in case someone calls us, let's return the most likely case: */
-	return B_EV_IO_READ;
-}
-
-char *ssl_verify_strerror(int code)
-{
-	return
-	        g_strdup
-	                ("SSL certificate verification not supported by BitlBee NSS code.");
-}
-
-size_t ssl_des3_encrypt(const unsigned char *key, size_t key_len,
-                        const unsigned char *input, size_t input_len,
-                        const unsigned char *iv, unsigned char **res)
-{
-#define CIPHER_MECH CKM_DES3_CBC
-#define MAX_OUTPUT_LEN 72
-
-	int len1;
-	unsigned int len2;
-
-	PK11Context *ctx = NULL;
-	PK11SlotInfo *slot = NULL;
-	SECItem keyItem;
-	SECItem ivItem;
-	SECItem *secParam = NULL;
-	PK11SymKey *symKey = NULL;
-
-	size_t rc;
-	SECStatus rv;
-
-	if (!initialized) {
-		ssl_init();
-	}
-
-	keyItem.data = (unsigned char *) key;
-	keyItem.len = key_len;
-
-	slot = PK11_GetBestSlot(CIPHER_MECH, NULL);
-	if (slot == NULL) {
-		fprintf(stderr, "PK11_GetBestSlot failed (err %d)\n",
-		        PR_GetError());
-		rc = 0;
-		goto out;
-	}
-
-	symKey =
-	        PK11_ImportSymKey(slot, CIPHER_MECH, PK11_OriginUnwrap, CKA_ENCRYPT,
-	                          &keyItem, NULL);
-	if (symKey == NULL) {
-		fprintf(stderr, "PK11_ImportSymKey failed (err %d)\n",
-		        PR_GetError());
-		rc = 0;
-		goto out;
-	}
-
-	ivItem.data = (unsigned char *) iv;
-	/* See msn_soap_passport_sso_handle_response in protocols/msn/soap.c */
-	ivItem.len = 8;
-
-	secParam = PK11_ParamFromIV(CIPHER_MECH, &ivItem);
-	if (secParam == NULL) {
-		fprintf(stderr, "PK11_ParamFromIV failed (err %d)\n",
-		        PR_GetError());
-		rc = 0;
-		goto out;
-	}
-
-	ctx =
-	        PK11_CreateContextBySymKey(CIPHER_MECH, CKA_ENCRYPT, symKey,
-	                                   secParam);
-	if (ctx == NULL) {
-		fprintf(stderr, "PK11_CreateContextBySymKey failed (err %d)\n",
-		        PR_GetError());
-		rc = 0;
-		goto out;
-	}
-
-	*res = g_new0(unsigned char, MAX_OUTPUT_LEN);
-
-	rv = PK11_CipherOp(ctx, *res, &len1, MAX_OUTPUT_LEN,
-	                   (unsigned char *) input, input_len);
-	if (rv != SECSuccess) {
-		fprintf(stderr, "PK11_CipherOp failed (err %d)\n",
-		        PR_GetError());
-		rc = 0;
-		goto out;
-	}
-
-	assert(len1 <= MAX_OUTPUT_LEN);
-
-	rv = PK11_DigestFinal(ctx, *res + len1, &len2,
-	                      (unsigned int) MAX_OUTPUT_LEN - len1);
-	if (rv != SECSuccess) {
-		fprintf(stderr, "PK11_DigestFinal failed (err %d)\n",
-		        PR_GetError());
-		rc = 0;
-		goto out;
-	}
-
-	rc = len1 + len2;
-
-out:
-	if (ctx) {
-		PK11_DestroyContext(ctx, PR_TRUE);
-	}
-	if (symKey) {
-		PK11_FreeSymKey(symKey);
-	}
-	if (secParam) {
-		SECITEM_FreeItem(secParam, PR_TRUE);
-	}
-	if (slot) {
-		PK11_FreeSlot(slot);
-	}
-
-	return rc;
-}
Index: b/ssl_openssl.c
===================================================================
--- lib/ssl_openssl.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,326 +1,0 @@
-/********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2012 Wilmer van der Gaast and others                *
-  \********************************************************************/
-
-/* SSL module - OpenSSL 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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  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 "bitlbee.h"
-#include "proxy.h"
-#include "ssl_client.h"
-#include "sock.h"
-
-int ssl_errno = 0;
-
-static gboolean initialized = FALSE;
-
-struct scd {
-	ssl_input_function func;
-	gpointer data;
-	int fd;
-	gboolean established;
-	gboolean verify;
-	char *hostname;
-
-	int inpa;
-	int lasterr;            /* Necessary for SSL_get_error */
-	SSL *ssl;
-};
-
-static SSL_CTX *ssl_ctx;
-
-static void ssl_conn_free(struct scd *conn);
-static gboolean ssl_connected(gpointer data, gint source, b_input_condition cond);
-static gboolean ssl_starttls_real(gpointer data, gint source, b_input_condition cond);
-static gboolean ssl_handshake(gpointer data, gint source, b_input_condition cond);
-
-
-void ssl_init(void)
-{
-	const SSL_METHOD *meth;
-
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
-	SSL_library_init();
-
-	meth = SSLv23_client_method();
-	ssl_ctx = SSL_CTX_new(meth);
-	SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
-#else
-	meth = TLS_client_method();
-	ssl_ctx = SSL_CTX_new(meth);
-	SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_VERSION);
-#endif
-
-	initialized = TRUE;
-}
-
-void *ssl_connect(char *host, int port, gboolean verify, ssl_input_function func, gpointer data)
-{
-	struct scd *conn = g_new0(struct scd, 1);
-
-	conn->fd = proxy_connect(host, port, ssl_connected, conn);
-	if (conn->fd < 0) {
-		ssl_conn_free(conn);
-		return NULL;
-	}
-
-	conn->func = func;
-	conn->data = data;
-	conn->inpa = -1;
-	conn->hostname = g_strdup(host);
-
-	return conn;
-}
-
-void *ssl_starttls(int fd, char *hostname, gboolean verify, ssl_input_function func, gpointer data)
-{
-	struct scd *conn = g_new0(struct scd, 1);
-
-	conn->fd = fd;
-	conn->func = func;
-	conn->data = data;
-	conn->inpa = -1;
-	conn->verify = verify && global.conf->cafile;
-	conn->hostname = g_strdup(hostname);
-
-	/* This function should be called via a (short) timeout instead of
-	   directly from here, because these SSL calls are *supposed* to be
-	   *completely* asynchronous and not ready yet when this function
-	   (or *_connect, for examle) returns. Also, errors are reported via
-	   the callback function, not via this function's return value.
-
-	   In short, doing things like this makes the rest of the code a lot
-	   simpler. */
-
-	b_timeout_add(1, ssl_starttls_real, conn);
-
-	return conn;
-}
-
-static gboolean ssl_starttls_real(gpointer data, gint source, b_input_condition cond)
-{
-	struct scd *conn = data;
-
-	return ssl_connected(conn, conn->fd, B_EV_IO_WRITE);
-}
-
-static gboolean ssl_connected(gpointer data, gint source, b_input_condition cond)
-{
-	struct scd *conn = data;
-
-	if (conn->verify) {
-		/* Right now we don't have any verification functionality for OpenSSL. */
-		conn->func(conn->data, 1, NULL, cond);
-		if (source >= 0) {
-			proxy_disconnect(source);
-		}
-		ssl_conn_free(conn);
-
-		return FALSE;
-	}
-
-	if (source == -1) {
-		goto ssl_connected_failure;
-	}
-
-	if (!initialized) {
-		ssl_init();
-	}
-
-
-	if (ssl_ctx == NULL) {
-		goto ssl_connected_failure;
-	}
-
-	conn->ssl = SSL_new(ssl_ctx);
-	if (conn->ssl == NULL) {
-		goto ssl_connected_failure;
-	}
-
-	/* We can do at least the handshake with non-blocking I/O */
-	sock_make_nonblocking(conn->fd);
-	SSL_set_fd(conn->ssl, conn->fd);
-
-	if (conn->hostname && !g_hostname_is_ip_address(conn->hostname)) {
-		SSL_set_tlsext_host_name(conn->ssl, conn->hostname);
-	}
-
-	return ssl_handshake(data, source, cond);
-
-ssl_connected_failure:
-	conn->func(conn->data, 0, NULL, cond);
-	ssl_disconnect(conn);
-	return FALSE;
-
-}
-
-static gboolean ssl_handshake(gpointer data, gint source, b_input_condition cond)
-{
-	struct scd *conn = data;
-	int st;
-
-	if ((st = SSL_connect(conn->ssl)) < 0) {
-		conn->lasterr = SSL_get_error(conn->ssl, st);
-		if (conn->lasterr != SSL_ERROR_WANT_READ && conn->lasterr != SSL_ERROR_WANT_WRITE) {
-			conn->func(conn->data, 0, NULL, cond);
-			ssl_disconnect(conn);
-			return FALSE;
-		}
-
-		conn->inpa = b_input_add(conn->fd, ssl_getdirection(conn), ssl_handshake, data);
-		return FALSE;
-	}
-
-	conn->established = TRUE;
-	sock_make_blocking(conn->fd);           /* For now... */
-	conn->func(conn->data, 0, conn, cond);
-	return FALSE;
-}
-
-int ssl_read(void *conn, char *buf, int len)
-{
-	int st;
-
-	if (!((struct scd*) conn)->established) {
-		ssl_errno = SSL_NOHANDSHAKE;
-		return -1;
-	}
-
-	st = SSL_read(((struct scd*) conn)->ssl, buf, len);
-
-	ssl_errno = SSL_OK;
-	if (st <= 0) {
-		((struct scd*) conn)->lasterr = SSL_get_error(((struct scd*) conn)->ssl, st);
-		if (((struct scd*) conn)->lasterr == SSL_ERROR_WANT_READ || ((struct scd*) conn)->lasterr ==
-		    SSL_ERROR_WANT_WRITE) {
-			ssl_errno = SSL_AGAIN;
-		}
-	}
-
-	if (0 && getenv("BITLBEE_DEBUG") && st > 0) {
-		write(1, buf, st);
-	}
-
-	return st;
-}
-
-int ssl_write(void *conn, const char *buf, int len)
-{
-	int st;
-
-	if (!((struct scd*) conn)->established) {
-		ssl_errno = SSL_NOHANDSHAKE;
-		return -1;
-	}
-
-	st = SSL_write(((struct scd*) conn)->ssl, buf, len);
-
-	if (0 && getenv("BITLBEE_DEBUG") && st > 0) {
-		write(1, buf, st);
-	}
-
-	ssl_errno = SSL_OK;
-	if (st <= 0) {
-		((struct scd*) conn)->lasterr = SSL_get_error(((struct scd*) conn)->ssl, st);
-		if (((struct scd*) conn)->lasterr == SSL_ERROR_WANT_READ || ((struct scd*) conn)->lasterr ==
-		    SSL_ERROR_WANT_WRITE) {
-			ssl_errno = SSL_AGAIN;
-		}
-	}
-
-	return st;
-}
-
-int ssl_pending(void *conn)
-{
-	return (((struct scd*) conn) && ((struct scd*) conn)->established) ?
-	       SSL_pending(((struct scd*) conn)->ssl) > 0 : 0;
-}
-
-static void ssl_conn_free(struct scd *conn)
-{
-	SSL_free(conn->ssl);
-	g_free(conn->hostname);
-	g_free(conn);
-
-}
-
-void ssl_disconnect(void *conn_)
-{
-	struct scd *conn = conn_;
-
-	if (conn->inpa != -1) {
-		b_event_remove(conn->inpa);
-	}
-
-	if (conn->established) {
-		SSL_shutdown(conn->ssl);
-	}
-
-	proxy_disconnect(conn->fd);
-
-	ssl_conn_free(conn);
-}
-
-int ssl_getfd(void *conn)
-{
-	return(((struct scd*) conn)->fd);
-}
-
-b_input_condition ssl_getdirection(void *conn)
-{
-	return(((struct scd*) conn)->lasterr == SSL_ERROR_WANT_WRITE ? B_EV_IO_WRITE : B_EV_IO_READ);
-}
-
-char *ssl_verify_strerror(int code)
-{
-	return g_strdup("SSL certificate verification not supported by BitlBee OpenSSL code.");
-}
-
-size_t ssl_des3_encrypt(const unsigned char *key, size_t key_len, const unsigned char *input, size_t input_len,
-                        const unsigned char *iv, unsigned char **res)
-{
-	int output_length = 0;
-	EVP_CIPHER_CTX *ctx;
-
-	*res = g_new0(unsigned char, 72);
-
-	/* Don't set key or IV because we will modify the parameters */
-	ctx = EVP_CIPHER_CTX_new();
-	EVP_CipherInit_ex(ctx, EVP_des_ede3_cbc(), NULL, NULL, NULL, 1);
-	EVP_CIPHER_CTX_set_key_length(ctx, key_len);
-	EVP_CIPHER_CTX_set_padding(ctx, 0);
-	/* We finished modifying parameters so now we can set key and IV */
-	EVP_CipherInit_ex(ctx, NULL, NULL, key, iv, 1);
-	EVP_CipherUpdate(ctx, *res, &output_length, input, input_len);
-	EVP_CipherFinal_ex(ctx, *res, &output_length);
-	EVP_CIPHER_CTX_free(ctx);
-	//EVP_cleanup();
-
-	return output_length;
-}
Index: b/url.c
===================================================================
--- lib/url.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,101 +1,0 @@
-/********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2001-2005 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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#include "url.h"
-
-/* Convert an URL to a url_t structure */
-int url_set(url_t *url, const char *set_url)
-{
-	char s[MAX_STRING + 1];
-	char *i;
-
-	memset(url, 0, sizeof(url_t));
-	memset(s, 0, sizeof(s));
-
-	/* 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, "https", i - set_url) == 0) {
-			url->proto = PROTO_HTTPS;
-		} 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 if (g_strncasecmp(set_url, "socks4a", i - set_url) == 0) {
-			url->proto = PROTO_SOCKS4A;
-		} else {
-			return 0;
-		}
-
-		strncpy(s, i + 3, MAX_STRING);
-	}
-
-	/* Split */
-	if ((i = strchr(s, '/')) == NULL) {
-		strcpy(url->file, "/");
-	} else {
-		strncpy(url->file, i, MAX_STRING);
-		*i = 0;
-	}
-	strncpy(url->host, s, MAX_STRING);
-
-	/* 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 {
-		*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, "%d", &url->port);
-	} else {
-		if (url->proto == PROTO_HTTP) {
-			url->port = 80;
-		} else if (url->proto == PROTO_HTTPS) {
-			url->port = 443;
-		} else if (url->proto == PROTO_SOCKS4 || url->proto == PROTO_SOCKS5) {
-			url->port = 1080;
-		}
-	}
-
-	return(url->port > 0);
-}
Index: b/url.h
===================================================================
--- lib/url.h	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,44 +1,0 @@
-/********************************************************************\
-  * 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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#include "bitlbee.h"
-
-#define PROTO_HTTP      2
-#define PROTO_HTTPS     5
-#define PROTO_SOCKS4    3
-#define PROTO_SOCKS5    4
-#define PROTO_SOCKS4A   5
-#define PROTO_DEFAULT   PROTO_HTTP
-
-typedef struct url {
-	int proto;
-	int port;
-	char host[MAX_STRING + 1];
-	char file[MAX_STRING + 1];
-	char user[MAX_STRING + 1];
-	char pass[MAX_STRING + 1];
-} url_t;
-
-int url_set(url_t *url, const char *set_url);
Index: b/xmltree.c
===================================================================
--- lib/xmltree.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,706 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Simple XML (stream) parse tree handling code (Jabber/XMPP, mainly)       *
-*                                                                           *
-*  Copyright 2006-2012 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.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-****************************************************************************/
-
-#include <glib.h>
-#include <string.h>
-#include <unistd.h>
-#include <ctype.h>
-#include <stdio.h>
-
-#include "xmltree.h"
-
-#define g_strcasecmp g_ascii_strcasecmp
-#define g_strncasecmp g_ascii_strncasecmp
-
-/* g_memdup() deprecated as of glib 2.68.0 */
-#ifndef GLIB_VERSION_2_68
-#define g_memdup2 g_memdup
-#endif
-
-static void xt_start_element(GMarkupParseContext *ctx, const gchar *element_name, const gchar **attr_names,
-                             const gchar **attr_values, gpointer data, GError **error)
-{
-	struct xt_parser *xt = data;
-	struct xt_node *node = g_new0(struct xt_node, 1), *nt;
-	int i;
-
-	node->parent = xt->cur;
-	node->name = g_strdup(element_name);
-
-	/* First count the number of attributes */
-	for (i = 0; attr_names[i]; i++) {
-		;
-	}
-
-	/* Then allocate a NULL-terminated array. */
-	node->attr = g_new0(struct xt_attr, i + 1);
-
-	/* And fill it, saving one variable by starting at the end. */
-	for (i--; i >= 0; i--) {
-		node->attr[i].key = g_strdup(attr_names[i]);
-		node->attr[i].value = g_strdup(attr_values[i]);
-	}
-
-	/* Add it to the linked list of children nodes, if we have a current
-	   node yet. */
-	if (xt->cur) {
-		if (xt->cur->children) {
-			for (nt = xt->cur->children; nt->next; nt = nt->next) {
-				;
-			}
-			nt->next = node;
-		} else {
-			xt->cur->children = node;
-		}
-	} else if (xt->root) {
-		/* ERROR situation: A second root-element??? */
-	}
-
-	/* Now this node will be the new current node. */
-	xt->cur = node;
-	/* And maybe this is the root? */
-	if (xt->root == NULL) {
-		xt->root = node;
-	}
-}
-
-static void xt_text(GMarkupParseContext *ctx, const gchar *text, gsize text_len, gpointer data, GError **error)
-{
-	struct xt_parser *xt = data;
-	struct xt_node *node = xt->cur;
-
-	if (node == NULL) {
-		return;
-	}
-
-	/* FIXME: Does g_renew also OFFICIALLY accept NULL arguments? */
-	node->text = g_renew(char, node->text, node->text_len + text_len + 1);
-	memcpy(node->text + node->text_len, text, text_len);
-	node->text_len += text_len;
-	/* Zero termination is always nice to have. */
-	node->text[node->text_len] = 0;
-}
-
-static void xt_end_element(GMarkupParseContext *ctx, const gchar *element_name, gpointer data, GError **error)
-{
-	struct xt_parser *xt = data;
-
-	xt->cur->flags |= XT_COMPLETE;
-	xt->cur = xt->cur->parent;
-}
-
-GMarkupParser xt_parser_funcs =
-{
-	xt_start_element,
-	xt_end_element,
-	xt_text,
-	NULL,
-	NULL
-};
-
-struct xt_parser *xt_new(const struct xt_handler_entry *handlers, gpointer data)
-{
-	struct xt_parser *xt = g_new0(struct xt_parser, 1);
-
-	xt->data = data;
-	xt->handlers = handlers;
-	xt_reset(xt);
-
-	return xt;
-}
-
-/* Reset the parser, flush everything we have so far. For example, we need
-   this for XMPP when doing TLS/SASL to restart the stream. */
-void xt_reset(struct xt_parser *xt)
-{
-	if (xt->parser) {
-		g_markup_parse_context_free(xt->parser);
-	}
-
-	xt->parser = g_markup_parse_context_new(&xt_parser_funcs, 0, xt, NULL);
-
-	if (xt->root) {
-		xt_free_node(xt->root);
-		xt->root = NULL;
-		xt->cur = NULL;
-	}
-}
-
-/* Feed the parser, don't execute any handler. Returns -1 on errors, 0 on
-   end-of-stream and 1 otherwise. */
-int xt_feed(struct xt_parser *xt, const char *text, int text_len)
-{
-	if (!g_markup_parse_context_parse(xt->parser, text, text_len, &xt->gerr)) {
-		return -1;
-	}
-
-	return !(xt->root && xt->root->flags & XT_COMPLETE);
-}
-
-/* Find completed nodes and see if a handler has to be called. Passing
-   a node isn't necessary if you want to start at the root, just pass
-   NULL. This second argument is needed for recursive calls. */
-int xt_handle(struct xt_parser *xt, struct xt_node *node, int depth)
-{
-	struct xt_node *c;
-	xt_status st;
-	int i;
-
-	if (xt->root == NULL) {
-		return 1;
-	}
-
-	if (node == NULL) {
-		return xt_handle(xt, xt->root, depth);
-	}
-
-	if (depth != 0) {
-		for (c = node->children; c; c = c->next) {
-			if (!xt_handle(xt, c, depth > 0 ? depth - 1 : depth)) {
-				return 0;
-			}
-		}
-	}
-
-	if (node->flags & XT_COMPLETE && !(node->flags & XT_SEEN)) {
-		if (xt->handlers) {
-			for (i = 0; xt->handlers[i].func; i++) {
-				/* This one is fun! \o/ */
-
-				/* If handler.name == NULL it means it should always match. */
-				if ((xt->handlers[i].name == NULL ||
-				     /* If it's not, compare. There should always be a name. */
-				     g_strcasecmp(xt->handlers[i].name, node->name) == 0) &&
-				    /* If handler.parent == NULL, it's a match. */
-				    (xt->handlers[i].parent == NULL ||
-				     /* If there's a parent node, see if the name matches. */
-				     (node->parent ? g_strcasecmp(xt->handlers[i].parent, node->parent->name) == 0 :
-				      /* If there's no parent, the handler should mention <root> as a parent. */
-				      strcmp(xt->handlers[i].parent, "<root>") == 0))) {
-					st = xt->handlers[i].func(node, xt->data);
-
-					if (st == XT_ABORT) {
-						return 0;
-					} else if (st != XT_NEXT) {
-						break;
-					}
-				}
-			}
-		}
-
-		node->flags |= XT_SEEN;
-	}
-
-	return 1;
-}
-
-/* Garbage collection: Cleans up all nodes that are handled. Useful for
-   streams because there's no reason to keep a complete packet history
-   in memory. */
-void xt_cleanup(struct xt_parser *xt, struct xt_node *node, int depth)
-{
-	struct xt_node *c, *prev;
-
-	if (!xt || !xt->root) {
-		return;
-	}
-
-	if (node == NULL) {
-		xt_cleanup(xt, xt->root, depth);
-		return;
-	}
-
-	if (node->flags & XT_SEEN && node == xt->root) {
-		xt_free_node(xt->root);
-		xt->root = xt->cur = NULL;
-		/* xt->cur should be NULL already, BTW... */
-
-		return;
-	}
-
-	/* c contains the current node, prev the previous node (or NULL).
-	   I admit, this one's pretty horrible. */
-	for (c = node->children, prev = NULL; c; prev = c, c = c ? c->next : node->children) {
-		if (c->flags & XT_SEEN) {
-			/* Remove the node from the linked list. */
-			if (prev) {
-				prev->next = c->next;
-			} else {
-				node->children = c->next;
-			}
-
-			xt_free_node(c);
-
-			/* Since the for loop wants to get c->next, make sure
-			   c points at something that exists (and that c->next
-			   will actually be the next item we should check). c
-			   can be NULL now, if we just removed the first item.
-			   That explains the ? thing in for(). */
-			c = prev;
-		} else {
-			/* This node can't be cleaned up yet, but maybe a
-			   subnode can. */
-			if (depth != 0) {
-				xt_cleanup(xt, c, depth > 0 ? depth - 1 : depth);
-			}
-		}
-	}
-}
-
-struct xt_node *xt_from_string(const char *in, int len)
-{
-	struct xt_parser *parser;
-	struct xt_node *ret = NULL;
-
-	if (len == 0) {
-		len = strlen(in);
-	}
-
-	parser = xt_new(NULL, NULL);
-	xt_feed(parser, in, len);
-	if (parser->cur == NULL) {
-		ret = parser->root;
-		parser->root = NULL;
-	}
-	xt_free(parser);
-
-	return ret;
-}
-
-static void xt_to_string_real(struct xt_node *node, GString *str, int indent)
-{
-	char *buf;
-	struct xt_node *c;
-	int i;
-
-	if (indent > 1) {
-		g_string_append_len(str, "\n\t\t\t\t\t\t\t\t",
-		                    indent < 8 ? indent : 8);
-	}
-
-	g_string_append_printf(str, "<%s", node->name);
-
-	for (i = 0; node->attr[i].key; i++) {
-		buf = g_markup_printf_escaped(" %s=\"%s\"", node->attr[i].key, node->attr[i].value);
-		g_string_append(str, buf);
-		g_free(buf);
-	}
-
-	if (node->text == NULL && node->children == NULL) {
-		g_string_append(str, "/>");
-		return;
-	}
-
-	g_string_append(str, ">");
-	if (node->text_len > 0) {
-		buf = g_markup_escape_text(node->text, node->text_len);
-		g_string_append(str, buf);
-		g_free(buf);
-	}
-
-	for (c = node->children; c; c = c->next) {
-		xt_to_string_real(c, str, indent ? indent + 1 : 0);
-	}
-
-	if (indent > 0 && node->children) {
-		g_string_append_len(str, "\n\t\t\t\t\t\t\t\t",
-		                    indent < 8 ? indent : 8);
-	}
-
-	g_string_append_printf(str, "</%s>", node->name);
-}
-
-char *xt_to_string(struct xt_node *node)
-{
-	GString *ret;
-
-	ret = g_string_new("");
-	xt_to_string_real(node, ret, 0);
-	return g_string_free(ret, FALSE);
-}
-
-/* WITH indentation! */
-char *xt_to_string_i(struct xt_node *node)
-{
-	GString *ret;
-
-	ret = g_string_new("");
-	xt_to_string_real(node, ret, 1);
-	return g_string_free(ret, FALSE);
-}
-
-void xt_print(struct xt_node *node)
-{
-	char *str = xt_to_string_i(node);
-
-	fprintf(stderr, "%s", str);
-	g_free(str);
-}
-
-struct xt_node *xt_dup(struct xt_node *node)
-{
-	struct xt_node *dup = g_new0(struct xt_node, 1);
-	struct xt_node *c, *dc = NULL;
-	int i;
-
-	/* Let's NOT copy the parent element here BTW! Only do it for children. */
-
-	dup->name = g_strdup(node->name);
-	dup->flags = node->flags;
-	if (node->text) {
-		dup->text = g_memdup2(node->text, node->text_len + 1);
-		dup->text_len = node->text_len;
-	}
-
-	/* Count the number of attributes and allocate the new array. */
-	for (i = 0; node->attr[i].key; i++) {
-		;
-	}
-	dup->attr = g_new0(struct xt_attr, i + 1);
-
-	/* Copy them all! */
-	for (i--; i >= 0; i--) {
-		dup->attr[i].key = g_strdup(node->attr[i].key);
-		dup->attr[i].value = g_strdup(node->attr[i].value);
-	}
-
-	/* This nice mysterious loop takes care of the children. */
-	for (c = node->children; c; c = c->next) {
-		if (dc == NULL) {
-			dc = dup->children = xt_dup(c);
-		} else {
-			dc = (dc->next = xt_dup(c));
-		}
-
-		dc->parent = dup;
-	}
-
-	return dup;
-}
-
-/* Frees a node. This doesn't clean up references to itself from parents! */
-void xt_free_node(struct xt_node *node)
-{
-	int i;
-
-	if (!node) {
-		return;
-	}
-
-	g_free(node->name);
-	g_free(node->text);
-
-	for (i = 0; node->attr[i].key; i++) {
-		g_free(node->attr[i].key);
-		g_free(node->attr[i].value);
-	}
-	g_free(node->attr);
-
-	while (node->children) {
-		struct xt_node *next = node->children->next;
-
-		xt_free_node(node->children);
-		node->children = next;
-	}
-
-	g_free(node);
-}
-
-void xt_free(struct xt_parser *xt)
-{
-	if (!xt) {
-		return;
-	}
-
-	if (xt->root) {
-		xt_free_node(xt->root);
-	}
-
-	g_markup_parse_context_free(xt->parser);
-
-	g_free(xt);
-}
-
-/* To find a node's child with a specific name, pass the node's children
-   list, not the node itself! The reason you have to do this by hand: So
-   that you can also use this function as a find-next. */
-struct xt_node *xt_find_node(struct xt_node *node, const char *name)
-{
-	while (node) {
-		char *colon;
-
-		if (g_strcasecmp(node->name, name) == 0 ||
-		    ((colon = strchr(node->name, ':')) &&
-		     g_strcasecmp(colon + 1, name) == 0)) {
-			break;
-		}
-
-		node = node->next;
-	}
-
-	return node;
-}
-
-/* More advanced than the one above, understands something like
-   ../foo/bar to find a subnode bar of a node foo which is a child
-   of node's parent. Pass the node directly, not its list of children. */
-struct xt_node *xt_find_path(struct xt_node *node, const char *name)
-{
-	while (name && *name && node) {
-		char *colon, *slash;
-		int n;
-
-		if ((slash = strchr(name, '/'))) {
-			n = slash - name;
-		} else {
-			n = strlen(name);
-		}
-
-		if (strncmp(name, "..", n) == 0) {
-			node = node->parent;
-		} else {
-			node = node->children;
-
-			while (node) {
-				if (g_strncasecmp(node->name, name, n) == 0 ||
-				    ((colon = strchr(node->name, ':')) &&
-				     g_strncasecmp(colon + 1, name, n) == 0)) {
-					break;
-				}
-
-				node = node->next;
-			}
-		}
-
-		name = slash ? slash + 1 : NULL;
-	}
-
-	return node;
-}
-
-char *xt_find_attr(struct xt_node *node, const char *key)
-{
-	int i;
-	char *colon;
-
-	if (!node) {
-		return NULL;
-	}
-
-	for (i = 0; node->attr[i].key; i++) {
-		if (g_strcasecmp(node->attr[i].key, key) == 0) {
-			break;
-		}
-	}
-
-	/* This is an awful hack that only takes care of namespace prefixes
-	   inside a tag. Since IMHO excessive namespace usage in XMPP is
-	   massive overkill anyway (this code exists for almost four years
-	   now and never really missed it): Meh. */
-	if (!node->attr[i].key && strcmp(key, "xmlns") == 0 &&
-	    (colon = strchr(node->name, ':'))) {
-		*colon = '\0';
-		for (i = 0; node->attr[i].key; i++) {
-			if (strncmp(node->attr[i].key, "xmlns:", 6) == 0 &&
-			    strcmp(node->attr[i].key + 6, node->name) == 0) {
-				break;
-			}
-		}
-		*colon = ':';
-	}
-
-	return node->attr[i].value;
-}
-
-struct xt_node *xt_find_node_by_attr(struct xt_node *xt, const char *tag, const char *key, const char *value)
-{
-	struct xt_node *c;
-	char *s;
-
-	for (c = xt; (c = xt_find_node(c, tag)); c = c->next) {
-		if ((s = xt_find_attr(c, key)) && strcmp(s, value) == 0) {
-			return c;
-		}
-	}
-	return NULL;
-}
-
-
-/* Strip a few non-printable characters that aren't allowed in XML streams
-   (and upset some XMPP servers for example). */
-void xt_strip_text(char *in)
-{
-	char *out = in;
-	static const char nonprint[32] = {
-		0, 0, 0, 0, 0, 0, 0, 0, /* 0..7 */
-		0, 1, 1, 0, 0, 1, 0, 0, /* 9 (tab), 10 (\n), 13 (\r) */
-	};
-
-	if (!in) {
-		return;
-	}
-
-	while (*in) {
-		if ((unsigned int) *in >= ' ' || nonprint[(unsigned int) *in]) {
-			*out++ = *in;
-		}
-		in++;
-	}
-	*out = *in;
-}
-
-struct xt_node *xt_new_node(char *name, const char *text, struct xt_node *children)
-{
-	struct xt_node *node, *c;
-
-	node = g_new0(struct xt_node, 1);
-	node->name = g_strdup(name);
-	node->children = children;
-	node->attr = g_new0(struct xt_attr, 1);
-
-	if (text) {
-		node->text = g_strdup(text);
-		xt_strip_text(node->text);
-		node->text_len = strlen(node->text);
-	}
-
-	for (c = children; c; c = c->next) {
-		if (c->parent != NULL) {
-			/* ERROR CONDITION: They seem to have a parent already??? */
-		}
-
-		c->parent = node;
-	}
-
-	return node;
-}
-
-void xt_add_child(struct xt_node *parent, struct xt_node *child)
-{
-	struct xt_node *node;
-
-	/* This function can actually be used to add more than one child, so
-	   do handle this properly. */
-	for (node = child; node; node = node->next) {
-		if (node->parent != NULL) {
-			/* ERROR CONDITION: They seem to have a parent already??? */
-		}
-
-		node->parent = parent;
-	}
-
-	if (parent->children == NULL) {
-		parent->children = child;
-	} else {
-		for (node = parent->children; node->next; node = node->next) {
-			;
-		}
-		node->next = child;
-	}
-}
-
-/* Same, but at the beginning. */
-void xt_insert_child(struct xt_node *parent, struct xt_node *child)
-{
-	struct xt_node *node, *last = NULL;
-
-	if (child == NULL) {
-		return; /* BUG */
-
-	}
-	for (node = child; node; node = node->next) {
-		if (node->parent != NULL) {
-			/* ERROR CONDITION: They seem to have a parent already??? */
-		}
-
-		node->parent = parent;
-		last = node;
-	}
-
-	last->next = parent->children;
-	parent->children = child;
-}
-
-void xt_add_attr(struct xt_node *node, const char *key, const char *value)
-{
-	int i;
-
-	/* Now actually it'd be nice if we can also change existing attributes
-	   (which actually means this function doesn't have the right name).
-	   So let's find out if we have this attribute already... */
-	for (i = 0; node->attr[i].key; i++) {
-		if (strcmp(node->attr[i].key, key) == 0) {
-			break;
-		}
-	}
-
-	if (node->attr[i].key == NULL) {
-		/* If not, allocate space for a new attribute. */
-		node->attr = g_renew(struct xt_attr, node->attr, i + 2);
-		node->attr[i].key = g_strdup(key);
-		node->attr[i + 1].key = NULL;
-	} else {
-		/* Otherwise, free the old value before setting the new one. */
-		g_free(node->attr[i].value);
-	}
-
-	node->attr[i].value = g_strdup(value);
-}
-
-int xt_remove_attr(struct xt_node *node, const char *key)
-{
-	int i, last;
-
-	for (i = 0; node->attr[i].key; i++) {
-		if (strcmp(node->attr[i].key, key) == 0) {
-			break;
-		}
-	}
-
-	/* If we didn't find the attribute... */
-	if (node->attr[i].key == NULL) {
-		return 0;
-	}
-
-	g_free(node->attr[i].key);
-	g_free(node->attr[i].value);
-
-	/* If it's the last, this is easy: */
-	if (node->attr[i + 1].key == NULL) {
-		node->attr[i].key = node->attr[i].value = NULL;
-	} else { /* It's also pretty easy, actually. */
-		/* Find the last item. */
-		for (last = i + 1; node->attr[last + 1].key; last++) {
-			;
-		}
-
-		node->attr[i] = node->attr[last];
-		node->attr[last].key = NULL;
-		node->attr[last].value = NULL;
-	}
-
-	/* Let's not bother with reallocating memory here. It takes time and
-	   most packets don't stay in memory for long anyway. */
-
-	return 1;
-}
Index: b/xmltree.h
===================================================================
--- lib/xmltree.h	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,96 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Simple XML (stream) parse tree handling code (Jabber/XMPP, mainly)       *
-*                                                                           *
-*  Copyright 2006-2012 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.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-****************************************************************************/
-
-#ifndef _XMLTREE_H
-#define _XMLTREE_H
-
-typedef enum {
-	XT_COMPLETE     = 1,    /* </tag> reached */
-	XT_SEEN         = 2,    /* Handler called (or not defined) */
-} xt_flags;
-
-typedef enum {
-	XT_ABORT,               /* Abort, don't handle the rest anymore */
-	XT_HANDLED,             /* Handled this tag properly, go to the next one */
-	XT_NEXT                 /* Try if there's another matching handler */
-} xt_status;
-
-struct xt_attr {
-	char *key, *value;
-};
-
-struct xt_node {
-	struct xt_node *parent;
-	struct xt_node *children;
-
-	char *name;
-	struct xt_attr *attr;
-	char *text;
-	int text_len;
-
-	struct xt_node *next;
-	xt_flags flags;
-};
-
-typedef xt_status (*xt_handler_func) (struct xt_node *node, gpointer data);
-
-struct xt_handler_entry {
-	char *name, *parent;
-	xt_handler_func func;
-};
-
-struct xt_parser {
-	GMarkupParseContext *parser;
-	struct xt_node *root;
-	struct xt_node *cur;
-
-	const struct xt_handler_entry *handlers;
-	gpointer data;
-
-	GError *gerr;
-};
-
-struct xt_parser *xt_new(const struct xt_handler_entry *handlers, gpointer data);
-void xt_reset(struct xt_parser *xt);
-int xt_feed(struct xt_parser *xt, const char *text, int text_len);
-int xt_handle(struct xt_parser *xt, struct xt_node *node, int depth);
-void xt_cleanup(struct xt_parser *xt, struct xt_node *node, int depth);
-struct xt_node *xt_from_string(const char *in, int text_len);
-char *xt_to_string(struct xt_node *node);
-char *xt_to_string_i(struct xt_node *node);
-void xt_print(struct xt_node *node);
-struct xt_node *xt_dup(struct xt_node *node);
-void xt_free_node(struct xt_node *node);
-void xt_free(struct xt_parser *xt);
-struct xt_node *xt_find_node(struct xt_node *node, const char *name);
-struct xt_node *xt_find_path(struct xt_node *node, const char *name);
-char *xt_find_attr(struct xt_node *node, const char *key);
-struct xt_node *xt_find_node_by_attr(struct xt_node *xt, const char *tag, const char *key, const char *value);
-
-struct xt_node *xt_new_node(char *name, const char *text, struct xt_node *children);
-void xt_add_child(struct xt_node *parent, struct xt_node *child);
-void xt_insert_child(struct xt_node *parent, struct xt_node *child);
-void xt_add_attr(struct xt_node *node, const char *key, const char *value);
-int xt_remove_attr(struct xt_node *node, const char *key);
-
-#endif
Index: log.c
===================================================================
--- log.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ log.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -1,9 +1,9 @@
-/********************************************************************\
+  /********************************************************************\
   * BitlBee -- An IRC to other IM-networks gateway                     *
   *                                                                    *
-  * Copyright 2002-2005 Wilmer van der Gaast and others                *
+  * Copyright 2002-2004 Wilmer van der Gaast and others                *
   \********************************************************************/
 
-/* Logging services for the bee                         */
+/* Logging services for the bee 			*/
 
 /*
@@ -20,6 +20,6 @@
   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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
 */
 
@@ -30,18 +30,17 @@
 static log_t logoutput;
 
-static void log_null(int level, const char *logmessage);
-static void log_irc(int level, const char *logmessage);
-static void log_syslog(int level, const char *logmessage);
-static void log_console(int level, const char *logmessage);
+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);
+void log_init(void) {
+	openlog("bitlbee", LOG_PID, LOG_DAEMON);	
 
-	logoutput.informational = &log_null;
-	logoutput.warning = &log_null;
-	logoutput.error = &log_null;
+	logoutput.informational=&log_null;
+	logoutput.warning=&log_null;
+	logoutput.error=&log_null;
 #ifdef DEBUG
-	logoutput.debug = &log_null;
+	logoutput.debug=&log_null;
 #endif
 
@@ -49,36 +48,54 @@
 }
 
-void log_link(int level, int output)
-{
-	void (*output_function)(int level, const char *logmessage) = &log_null;
+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 (output == LOGOUTPUT_NULL) {
-		output_function = &log_null;
-	} else if (output == LOGOUTPUT_IRC) {
-		output_function = &log_irc;
-	} else if (output == LOGOUTPUT_SYSLOG) {
-		output_function = &log_syslog;
-	} else if (output == LOGOUTPUT_CONSOLE) {
-		output_function = &log_console;
+	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;	
 	}
-
-	if (level == LOGLVL_INFO) {
-		logoutput.informational = output_function;
-	} else if (level == LOGLVL_WARNING) {
-		logoutput.warning = output_function;
-	} else if (level == LOGLVL_ERROR) {
-		logoutput.error = output_function;
+	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) {
-		logoutput.debug = output_function;
+	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;
+	return;	
 
 }
 
-void log_message(int level, const char *message, ...)
-{
+void log_message(int level, char *message, ... ) {
 
 	va_list ap;
@@ -89,93 +106,70 @@
 	va_end(ap);
 
-	if (level == LOGLVL_INFO) {
+	if(level==LOGLVL_INFO)
 		(*(logoutput.informational))(level, msgstring);
-	}
-	if (level == LOGLVL_WARNING) {
+	if(level==LOGLVL_WARNING) 
 		(*(logoutput.warning))(level, msgstring);
-	}
-	if (level == LOGLVL_ERROR) {
+	if(level==LOGLVL_ERROR)
 		(*(logoutput.error))(level, msgstring);
-	}
 #ifdef DEBUG
-	if (level == LOGLVL_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;
 }
 
-void log_error(const char *functionname)
-{
-	log_message(LOGLVL_ERROR, "%s: %s", functionname, strerror(errno));
-
-	return;
-}
-
-static void log_null(int level, const char *message)
-{
-	return;
-}
-
-static void log_irc(int level, const char *message)
-{
-	if (level == LOGLVL_ERROR) {
-		irc_write_all(1, "ERROR :Error: %s", message);
-	}
-	if (level == LOGLVL_WARNING) {
-		irc_write_all(0, "ERROR :Warning: %s", message);
-	}
-	if (level == LOGLVL_INFO) {
-		irc_write_all(0, "ERROR :Informational: %s", message);
-	}
+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) {
-		irc_write_all(0, "ERROR :Debug: %s", message);
-	}
-#endif
-
-	return;
-}
-
-static void log_syslog(int level, const 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) {
+	if(level==LOGLVL_DEBUG)
 		syslog(LOG_DEBUG, "%s", message);
-	}
 #endif
 	return;
 }
 
-static void log_console(int level, const char *message)
-{
-	if (level == LOGLVL_ERROR) {
+static void log_console(int level, char *message) {
+	if(level==LOGLVL_ERROR)
 		fprintf(stderr, "Error: %s\n", message);
-	}
-	if (level == LOGLVL_WARNING) {
+	if(level==LOGLVL_WARNING)
 		fprintf(stderr, "Warning: %s\n", message);
-	}
-	if (level == LOGLVL_INFO) {
+	if(level==LOGLVL_INFO)
 		fprintf(stdout, "Informational: %s\n", message);
-	}
 #ifdef DEBUG
-	if (level == LOGLVL_DEBUG) {
+	if(level==LOGLVL_DEBUG)
 		fprintf(stdout, "Debug: %s\n", message);
-	}
 #endif
-	/* Always log stuff in syslogs too. */
-	log_syslog(level, message);
 	return;
 }
Index: log.h
===================================================================
--- log.h	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ log.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -1,6 +1,6 @@
-/********************************************************************\
+  /********************************************************************\
   * BitlBee -- An IRC to other IM-networks gateway                     *
   *                                                                    *
-  * Copyright 2002-2005 Wilmer van der Gaast and others                *
+  * Copyright 2002-2004 Wilmer van der Gaast and others                *
   \********************************************************************/
 
@@ -20,6 +20,6 @@
   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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
 */
 
@@ -44,9 +44,9 @@
 
 typedef struct log_t {
-	void (*error)(int level, const char *logmessage);
-	void (*warning)(int level, const char *logmessage);
-	void (*informational)(int level, const char *logmessage);
+	void (*error)(int level, char *logmessage);
+	void (*warning)(int level, char *logmessage); 
+	void (*informational)(int level, char *logmessage);
 #ifdef DEBUG
-	void (*debug)(int level, const char *logmessage);
+	void (*debug)(int level, char *logmessage);
 #endif
 } log_t;
@@ -54,6 +54,6 @@
 void log_init(void);
 void log_link(int level, int output);
-void log_message(int level, const char *message, ...) G_GNUC_PRINTF(2, 3);
-void log_error(const char *functionname);
+void log_message(int level, char *message, ...);
+void log_error(char *functionname);
 
 #endif
Index: motd.txt
===================================================================
--- motd.txt	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ motd.txt	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -2,5 +2,5 @@
 
 This server is running BitlBee version %v.
-The newest version can be found on https://www.bitlbee.org/
+The newest version can be found on http://www.bitlbee.org/
 
 You are getting this message because the server administrator has not
@@ -11,6 +11,8 @@
 information.
 
+
 The developers of the Bee hope you have a buzzing time.
--- BitlBee development team.
+
+* BitlBee development team: wilmer, ctrlsoft, Maurits
  
 ... Buzzing, haha, get it?
Index: nick.c
===================================================================
--- nick.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ nick.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -1,6 +1,6 @@
-/********************************************************************\
+  /********************************************************************\
   * BitlBee -- An IRC to other IM-networks gateway                     *
   *                                                                    *
-  * Copyright 2002-2012 Wilmer van der Gaast and others                *
+  * Copyright 2002-2004 Wilmer van der Gaast and others                *
   \********************************************************************/
 
@@ -20,6 +20,6 @@
   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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
 */
 
@@ -27,403 +27,237 @@
 #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. These are used to do uc/lc conversions
-   and strip invalid chars. */
-static char *nick_lc_chars = "0123456789abcdefghijklmnopqrstuvwxyz{}^`-_|";
-static char *nick_uc_chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ[]~`-_\\";
-
-/* Store handles in lower case and strip spaces, because AIM is braindead. */
-static char *clean_handle(const char *orig)
-{
-	char *new = g_malloc(strlen(orig) + 1);
-	int i = 0;
-
-	do {
-		if (*orig != ' ') {
-			new[i++] = g_ascii_tolower(*orig);
-		}
-	} while (*(orig++));
-
-	return new;
-}
-
-void nick_set_raw(account_t *acc, const char *handle, const char *nick)
-{
-	char *store_handle, *store_nick = g_malloc(MAX_NICK_LENGTH + 1);
-	irc_t *irc = (irc_t *) acc->bee->ui_data;
-
-	store_handle = clean_handle(handle);
-	store_nick[MAX_NICK_LENGTH] = '\0';
-	strncpy(store_nick, nick, MAX_NICK_LENGTH);
-	nick_strip(irc, store_nick);
-
-	g_hash_table_replace(acc->nicks, store_handle, store_nick);
-}
-
-void nick_set(bee_user_t *bu, const char *nick)
-{
-	nick_set_raw(bu->ic->acc, bu->handle, nick);
-}
-
-char *nick_get(bee_user_t *bu)
-{
-	static char nick[MAX_NICK_LENGTH + 1];
-	char *store_handle, *found_nick;
-	irc_t *irc = (irc_t *) bu->bee->ui_data;
-
-	memset(nick, 0, MAX_NICK_LENGTH + 1);
-
-	store_handle = clean_handle(bu->handle);
-	/* Find out if we stored a nick for this person already. If not, try
-	   to generate a sane nick automatically. */
-	if ((found_nick = g_hash_table_lookup(bu->ic->acc->nicks, store_handle))) {
-		strncpy(nick, found_nick, MAX_NICK_LENGTH);
-	} else if ((found_nick = nick_gen(bu))) {
-		strncpy(nick, found_nick, MAX_NICK_LENGTH);
-		g_free(found_nick);
-	} else {
-		/* Keep this fallback since nick_gen() can return NULL in some cases. */
-		char *s;
-
-		g_snprintf(nick, MAX_NICK_LENGTH, "%s", bu->handle);
-		if ((s = strchr(nick, '@'))) {
-			while (*s) {
-				*(s++) = 0;
-			}
-		}
-
-		nick_strip(irc, nick);
-		if (set_getbool(&bu->bee->set, "nick_lowercase")) {
-			nick_lc(irc, nick);
-		}
-	}
-	g_free(store_handle);
-
-	/* Make sure the nick doesn't collide with an existing one by adding
-	   underscores and that kind of stuff, if necessary. */
-	nick_dedupe(bu, nick);
-
-	return nick;
-}
-
-char *nick_gen(bee_user_t *bu)
-{
-	gboolean ok = FALSE; /* Set to true once the nick contains something unique. */
-	GString *ret = g_string_sized_new(MAX_NICK_LENGTH + 1);
-	char *rets;
-	irc_t *irc = (irc_t *) bu->bee->ui_data;
-	char *fmt = set_getstr(&bu->ic->acc->set, "nick_format") ? :
-	            set_getstr(&bu->bee->set, "nick_format");
-
-	while (fmt && *fmt && ret->len < MAX_NICK_LENGTH) {
-		char *part = NULL, chop = '\0', *asc = NULL, *s;
-		int len = INT_MAX;
-
-		if (*fmt != '%') {
-			g_string_append_c(ret, *fmt);
-			fmt++;
-			continue;
-		}
-
-		fmt++;
-		while (*fmt) {
-			/* -char means chop off everything from char */
-			if (*fmt == '-') {
-				chop = fmt[1];
-				if (chop == '\0') {
-					g_string_free(ret, TRUE);
-					return NULL;
-				}
-				fmt += 2;
-			} else if (g_ascii_isdigit(*fmt)) {
-				len = 0;
-				/* Grab a number. */
-				while (g_ascii_isdigit(*fmt)) {
-					len = len * 10 + (*(fmt++) - '0');
-				}
-			} else if (g_strncasecmp(fmt, "nick", 4) == 0) {
-				part = bu->nick ? : bu->handle;
-				fmt += 4;
-				ok |= TRUE;
-				break;
-			} else if (g_strncasecmp(fmt, "handle", 6) == 0) {
-				part = bu->handle;
-				fmt += 6;
-				ok |= TRUE;
-				break;
-			} else if (g_strncasecmp(fmt, "full_name", 9) == 0) {
-				part = bu->fullname;
-				fmt += 9;
-				ok |= part && *part;
-				break;
-			} else if (g_strncasecmp(fmt, "first_name", 10) == 0) {
-				part = bu->fullname;
-				fmt += 10;
-				ok |= part && *part;
-				chop = ' ';
-				break;
-			} else if (g_strncasecmp(fmt, "group", 5) == 0) {
-				part = bu->group ? bu->group->name : NULL;
-				fmt += 5;
-				break;
-			} else if (g_strncasecmp(fmt, "account", 7) == 0) {
-				part = bu->ic->acc->tag;
-				fmt += 7;
-				break;
-			} else {
-				g_string_free(ret, TRUE);
-				return NULL;
-			}
-		}
-
-		if (!part) {
-			continue;
-		}
-
-		/* Credits to Josay_ in #bitlbee for this idea. //TRANSLIT
-		   should do lossy/approximate conversions, so letters with
-		   accents don't just get stripped. Note that it depends on
-		   LC_CTYPE being set to something other than C/POSIX. */
-		if (!(irc && irc->status & IRC_UTF8_NICKS)) {
-			asc = g_convert_with_fallback(part, -1, "ASCII//TRANSLIT", "UTF-8", "", NULL, NULL, NULL);
-
-			if (!asc) {
-				/* If above failed, try again without //TRANSLIT.
-				   //TRANSLIT is a GNU iconv special and is not POSIX.
-				   Other platforms may not support it. */
-				asc = g_convert_with_fallback(part, -1, "ASCII", "UTF-8", "", NULL, NULL, NULL);
-			}
-
-			part = asc;
-		}
-
-		if (part && chop && (s = strchr(part, chop))) {
-			len = MIN(len, s - part);
-		}
-
-		if (part) {
-			if (len < INT_MAX) {
-				g_string_append_len(ret, part, len);
-			} else {
-				g_string_append(ret, part);
-			}
-		}
-		g_free(asc);
-	}
-
-	rets = g_string_free(ret, FALSE);
-	if (ok && rets && *rets) {
-		nick_strip(irc, rets);
-
-		if (set_getbool(&bu->bee->set, "nick_lowercase")) {
-			nick_lc(irc, rets);
-		}
-
-		truncate_utf8(rets, MAX_NICK_LENGTH);
-		return rets;
-	}
-	g_free(rets);
-	return NULL;
-}
-
-/* Used for nicks and channel names too! */
-void underscore_dedupe(char nick[MAX_NICK_LENGTH + 1])
-{
-	if (strlen(nick) < (MAX_NICK_LENGTH - 1)) {
-		nick[strlen(nick) + 1] = 0;
-		nick[strlen(nick)] = '_';
-	} else {
-		/* We've got no more space for underscores,
-		   so truncate it and replace the last three
-		   chars with a random "_XX" suffix */
-		int len = truncate_utf8(nick, MAX_NICK_LENGTH - 3);
-		nick[len] = '_';
-		g_snprintf(nick + len + 1, 3, "%2x", rand());
-	}
-}
-
-void nick_dedupe(bee_user_t *bu, char nick[MAX_NICK_LENGTH + 1])
-{
-	irc_t *irc = (irc_t *) bu->bee->ui_data;
-	int inf_protection = 256;
-	irc_user_t *iu;
-
-	/* Now, find out if the nick is already in use at the moment, and make
-	   subtle changes to make it unique. */
-	while (!nick_ok(irc, nick) ||
-	       ((iu = irc_user_by_name(irc, nick)) && iu->bu != bu)) {
-
-		underscore_dedupe(nick);
-
-		if (inf_protection-- == 0) {
-			g_snprintf(nick, MAX_NICK_LENGTH + 1, "xx%x", rand());
-
-			irc_rootmsg(irc, "Warning: Something went wrong while trying "
-			            "to generate a nickname for contact %s on %s.",
-			            bu->handle, bu->ic->acc->tag);
-			irc_rootmsg(irc, "This might be a bug in BitlBee, or the result "
-			            "of a faulty nick_format setting. Will use %s "
-			            "instead.", nick);
-
-			break;
-		}
-	}
-}
-
-/* Just check if there is a nickname set for this buddy or if we'd have to
-   generate one. */
-int nick_saved(bee_user_t *bu)
-{
-	char *store_handle, *found;
-
-	store_handle = clean_handle(bu->handle);
-	found = g_hash_table_lookup(bu->ic->acc->nicks, store_handle);
-	g_free(store_handle);
-
-	return found != NULL;
-}
-
-void nick_del(bee_user_t *bu)
-{
-	g_hash_table_remove(bu->ic->acc->nicks, bu->handle);
-}
-
-
-void nick_strip(irc_t *irc, char *nick)
-{
-	int len = 0;
-	gboolean nick_underscores = irc ? set_getbool(&irc->b->set, "nick_underscores") : FALSE;
-
-	if (irc && (irc->status & IRC_UTF8_NICKS)) {
-		gunichar c;
-		char *p = nick, *n, tmp[strlen(nick) + 1];
-
-		while (p && *p) {
-			c = g_utf8_get_char_validated(p, -1);
-			n = g_utf8_find_next_char(p, NULL);
-
-			if (nick_underscores && c == ' ') {
-				*p = '_';
-				p = n;
-			} else if ((c < 0x7f && !(strchr(nick_lc_chars, c) ||
-			                          strchr(nick_uc_chars, c))) ||
-			    !g_unichar_isgraph(c)) {
-				strcpy(tmp, n);
-				strcpy(p, tmp);
-			} else {
-				p = n;
-			}
-		}
-		if (p) {
-			len = p - nick;
-		}
-	} else {
-		int i;
-
-		for (i = len = 0; nick[i] && len < MAX_NICK_LENGTH; i++) {
-			if (nick_underscores && nick[i] == ' ') {
-				nick[len] = '_';
-				len++;
-			} else if (strchr(nick_lc_chars, nick[i]) ||
-			    strchr(nick_uc_chars, nick[i])) {
-				nick[len] = nick[i];
-				len++;
-			}
-		}
-	}
-	if (g_ascii_isdigit(nick[0])) {
-		char *orig;
-
-		/* First character of a nick can't be a digit, so insert an
-		   underscore if necessary. */
-		orig = g_strdup(nick);
-		g_snprintf(nick, MAX_NICK_LENGTH, "_%s", orig);
-		g_free(orig);
-		len++;
-	}
-	while (len <= MAX_NICK_LENGTH) {
-		nick[len++] = '\0';
-	}
-}
-
-gboolean nick_ok(irc_t *irc, const char *nick)
-{
-	const char *s;
-
-	/* Empty/long nicks are not allowed, nor numbers at [0] */
-	if (!*nick || g_ascii_isdigit(nick[0]) || strlen(nick) > MAX_NICK_LENGTH) {
-		return 0;
-	}
-
-	if (irc && (irc->status & IRC_UTF8_NICKS)) {
-		gunichar c;
-		const char *p = nick, *n;
-
-		while (p && *p) {
-			c = g_utf8_get_char_validated(p, -1);
-			n = g_utf8_find_next_char(p, NULL);
-
-			if ((c < 0x7f && !(strchr(nick_lc_chars, c) ||
-			                   strchr(nick_uc_chars, c))) ||
-			    !g_unichar_isgraph(c)) {
-				return FALSE;
-			}
-			p = n;
-		}
-	} else {
-		for (s = nick; *s; s++) {
-			if (!strchr(nick_lc_chars, *s) && !strchr(nick_uc_chars, *s)) {
-				return FALSE;
-			}
-		}
-	}
-
-	return TRUE;
-}
-
-int nick_lc(irc_t *irc, char *nick)
+   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) {
-		/* initialize table so nonchars are mapped to themselves */
-		for (i = 0; i < sizeof(tab); i++) {
-			tab[i] = i;
-		}
-		/* replace uppercase chars with lowercase chars */
-		for (i = 0; nick_lc_chars[i]; i++) {
-			tab[(int) nick_uc_chars[i]] = nick_lc_chars[i];
-		}
-	}
-
-	if (irc && (irc->status & IRC_UTF8_NICKS)) {
-		gchar *down = g_utf8_strdown(nick, -1);
-		if (strlen(down) > strlen(nick)) {
-			truncate_utf8(down, strlen(nick));
-		}
-		strcpy(nick, down);
-		g_free(down);
-	}
-
-	for (i = 0; nick[i]; i++) {
-		if (((guchar) nick[i]) < 0x7f) {
-			nick[i] = tab[(guchar) nick[i]];
-		}
-	}
-
-	return nick_ok(irc, nick);
-}
-
-int nick_cmp(irc_t *irc, const char *a, const char *b)
+	
+	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(irc, aa) && nick_lc(irc, bb)) {
-		return(strcmp(aa, bb));
-	} else {
-		return(-1);     /* Hmm... Not a clear answer.. :-/ */
-	}
-}
+	
+	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 );
+}
Index: nick.h
===================================================================
--- nick.h	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ nick.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -1,3 +1,3 @@
-/********************************************************************\
+  /********************************************************************\
   * BitlBee -- An IRC to other IM-networks gateway                     *
   *                                                                    *
@@ -20,21 +20,24 @@
   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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
 */
 
-void nick_set_raw(account_t *acc, const char *handle, const char *nick);
-void nick_set(bee_user_t *bu, const char *nick);
-char *nick_get(bee_user_t *bu);
-char *nick_gen(bee_user_t *bu);
-void underscore_dedupe(char nick[MAX_NICK_LENGTH + 1]);
-void nick_dedupe(bee_user_t * bu, char nick[MAX_NICK_LENGTH + 1]);
-int nick_saved(bee_user_t *bu);
-void nick_del(bee_user_t *bu);
+typedef struct __NICK
+{
+	char *handle;
+	int proto;
+	char *nick;
+	struct __NICK *next;
+} nick_t;
 
-void nick_strip(irc_t *irc, char *nick);
-gboolean nick_ok(irc_t *irc, const char *nick);
-int nick_lc(irc_t *irc, char *nick);
-int nick_uc(irc_t *irc, char *nick);
-int nick_cmp(irc_t *irc, const char *a, const char *b);
-char *nick_dup(const char *nick);
+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 );
Index: r.c
===================================================================
--- otr.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,2180 +1,0 @@
-/********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2012 Wilmer van der Gaast and others                *
-  \********************************************************************/
-
-/*
-  OTR support (cf. http://www.cypherpunks.ca/otr/)
-
-  (c) 2008-2011,2013 Sven Moritz Hallberg <pesco@khjk.org>
-  funded by stonedcoder.org
-
-  files used to store OTR data:
-    <configdir>/<nick>.otr_keys
-    <configdir>/<nick>.otr_fprints
-    <configdir>/<nick>.otr_instags  <- don't copy this one between hosts
-
-  top-level todos: (search for TODO for more ;-))
-    integrate otr_load/otr_save with existing storage backends
-    per-account policy settings
-    per-user policy settings
-    add a way to select recipient instance
-*/
-
-/*
-  This program is free software; you can 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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#include "bitlbee.h"
-#include "irc.h"
-#include "otr.h"
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <unistd.h>
-#include <assert.h>
-#include <signal.h>
-
-
-/** OTR interface routines for the OtrlMessageAppOps struct: **/
-
-OtrlPolicy op_policy(void *opdata, ConnContext *context);
-
-void op_create_privkey(void *opdata, const char *accountname, const char *protocol);
-
-int op_is_logged_in(void *opdata, const char *accountname, const char *protocol,
-                    const char *recipient);
-
-void op_inject_message(void *opdata, const char *accountname, const char *protocol,
-                       const char *recipient, const char *message);
-
-void op_new_fingerprint(void *opdata, OtrlUserState us, const char *accountname,
-                        const char *protocol, const char *username, unsigned char fingerprint[20]);
-
-void op_write_fingerprints(void *opdata);
-
-void op_gone_secure(void *opdata, ConnContext *context);
-
-void op_gone_insecure(void *opdata, ConnContext *context);
-
-void op_still_secure(void *opdata, ConnContext *context, int is_reply);
-
-void op_log_message(void *opdata, const char *message);
-
-int op_max_message_size(void *opdata, ConnContext *context);
-
-const char *op_account_name(void *opdata, const char *account, const char *protocol);
-
-void op_create_instag(void *opdata, const char *account, const char *protocol);
-
-void op_convert_msg(void *opdata, ConnContext *ctx, OtrlConvertType typ,
-                    char **dst, const char *src);
-void op_convert_free(void *opdata, ConnContext *ctx, char *msg);
-
-void op_handle_smp_event(void *opdata, OtrlSMPEvent ev, ConnContext *ctx,
-                         unsigned short percent, char *question);
-
-void op_handle_msg_event(void *opdata, OtrlMessageEvent ev, ConnContext *ctx,
-                         const char *message, gcry_error_t err);
-
-const char *op_otr_error_message(void *opdata, ConnContext *ctx,
-                                 OtrlErrorCode err_code);
-
-/** otr sub-command handlers: **/
-
-static void cmd_otr(irc_t *irc, char **args);
-void cmd_otr_connect(irc_t *irc, char **args);
-void cmd_otr_disconnect(irc_t *irc, char **args);
-void cmd_otr_reconnect(irc_t *irc, char **args);
-void cmd_otr_smp(irc_t *irc, char **args);
-void cmd_otr_smpq(irc_t *irc, char **args);
-void cmd_otr_trust(irc_t *irc, char **args);
-void cmd_otr_info(irc_t *irc, char **args);
-void cmd_otr_keygen(irc_t *irc, char **args);
-void cmd_otr_forget(irc_t *irc, char **args);
-
-const command_t otr_commands[] = {
-	{ "connect",     1, &cmd_otr_connect,    0 },
-	{ "disconnect",  1, &cmd_otr_disconnect, 0 },
-	{ "reconnect",   1, &cmd_otr_reconnect,  0 },
-	{ "smp",         2, &cmd_otr_smp,        0 },
-	{ "smpq",        3, &cmd_otr_smpq,       0 },
-	{ "trust",       6, &cmd_otr_trust,      0 },
-	{ "info",        0, &cmd_otr_info,       0 },
-	{ "keygen",      1, &cmd_otr_keygen,     0 },
-	{ "forget",      2, &cmd_otr_forget,     0 },
-	{ NULL }
-};
-
-typedef struct {
-	void *fst;
-	void *snd;
-} pair_t;
-
-static OtrlMessageAppOps otr_ops;   /* collects interface functions required by OTR */
-
-
-/** misc. helpers/subroutines: **/
-
-/* check whether we are already generating a key for a given account */
-int keygen_in_progress(irc_t *irc, const char *handle, const char *protocol);
-
-/* start background process to generate a (new) key for a given account */
-void otr_keygen(irc_t *irc, const char *handle, const char *protocol);
-
-/* main function for the forked keygen slave */
-void keygen_child_main(OtrlUserState us, int infd, int outfd);
-
-/* mainloop handler for when a keygen finishes */
-gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond);
-
-/* copy the contents of file a to file b, overwriting it if it exists */
-void copyfile(const char *a, const char *b);
-
-/* read one line of input from a stream, excluding trailing newline */
-void myfgets(char *s, int size, FILE *stream);
-
-/* some yes/no handlers */
-void yes_keygen(void *data);
-void yes_forget_fingerprint(void *data);
-void yes_forget_context(void *data);
-void yes_forget_key(void *data);
-
-/* timeout handler that calls otrl_message_poll */
-gboolean ev_message_poll(gpointer data, gint fd, b_input_condition cond);
-
-/* helper to make sure accountname and protocol match the incoming "opdata" */
-struct im_connection *check_imc(void *opdata, const char *accountname,
-                                const char *protocol);
-
-/* determine the nick for a given handle/protocol pair
-   returns "handle/protocol" if not found */
-const char *peernick(irc_t *irc, const char *handle, const char *protocol);
-
-/* turn a hexadecimal digit into its numerical value */
-int hexval(char a);
-
-/* determine the irc_user_t for a given handle/protocol pair
-   returns NULL if not found */
-irc_user_t *peeruser(irc_t *irc, const char *handle, const char *protocol);
-
-/* show an otr-related message to the user */
-void display_otr_message(void *opdata, ConnContext *ctx, const char *fmt, ...);
-
-/* write an otr-related message to the system log */
-void log_otr_message(void *opdata, const char *fmt, ...);
-
-/* combined handler for the 'otr smp' and 'otr smpq' commands */
-void otr_smp_or_smpq(irc_t *irc, const char *nick, const char *question,
-                     const char *secret);
-
-/* update flags within the irc_user structure to reflect OTR status of context */
-void otr_update_uflags(ConnContext *context, irc_user_t *u);
-
-/* update op/voice flag of given user according to encryption state and settings
-   returns 0 if neither op_buddies nor voice_buddies is set to "encrypted",
-   i.e. msgstate should be announced separately */
-int otr_update_modeflags(irc_t *irc, irc_user_t *u);
-
-/* show general info about the OTR subsystem; called by 'otr info' */
-void show_general_otr_info(irc_t *irc);
-
-/* show info about a given OTR context and subcontexts/instances. bestctx
-   may be either NULL or preferred destination context (this is hilighted
-   in the output as being the target for a message) */
-void show_otr_context_info(irc_t *irc, ConnContext *ctx, ConnContext *bestctx);
-
-/* show the list of fingerprints associated with a given context */
-void show_fingerprints(irc_t *irc, ConnContext *ctx);
-
-/* find a fingerprint by prefix (given as any number of hex strings) */
-Fingerprint *match_fingerprint(irc_t *irc, ConnContext *ctx, const char **args);
-
-/* find a private key by fingerprint prefix (given as any number of hex strings) */
-OtrlPrivKey *match_privkey(irc_t *irc, const char **args);
-
-/* check whether a string is safe to use in a path component */
-int strsane(const char *s);
-
-/* close the OTR connection with the given buddy */
-gboolean otr_disconnect_user(irc_t *irc, irc_user_t *u);
-
-/* close all active OTR connections */
-void otr_disconnect_all(irc_t *irc);
-
-/* modifies string in-place, replacing \x03 with '?',
-   as a quick way to prevent remote users from messing with irc colors */
-static char *otr_filter_colors(char *msg);
-
-/* functions to be called for certain events */
-static const struct irc_plugin otr_plugin;
-
-#define OTR_COLOR_TRUSTED "03"     /* green */
-#define OTR_COLOR_UNTRUSTED "05"   /* red */
-
-/*** routines declared in otr.h: ***/
-
-#ifdef OTR_BI
-#define init_plugin otr_init
-#endif
-
-void init_plugin(void)
-{
-	OTRL_INIT;
-
-	/* fill global OtrlMessageAppOps */
-	otr_ops.policy = &op_policy;
-	otr_ops.create_privkey = &op_create_privkey;
-	otr_ops.is_logged_in = &op_is_logged_in;
-	otr_ops.inject_message = &op_inject_message;
-	otr_ops.update_context_list = NULL;
-	otr_ops.new_fingerprint = &op_new_fingerprint;
-	otr_ops.write_fingerprints = &op_write_fingerprints;
-	otr_ops.gone_secure = &op_gone_secure;
-	otr_ops.gone_insecure = &op_gone_insecure;
-	otr_ops.still_secure = &op_still_secure;
-	otr_ops.max_message_size = &op_max_message_size;
-	otr_ops.account_name = &op_account_name;
-	otr_ops.account_name_free = NULL;
-
-	/* stuff added with libotr 4.0.0 */
-	otr_ops.received_symkey = NULL;         /* we don't use the extra key */
-	otr_ops.otr_error_message = &op_otr_error_message;
-	otr_ops.otr_error_message_free = NULL;
-	otr_ops.resent_msg_prefix = NULL;       /* default: [resent] */
-	otr_ops.resent_msg_prefix_free = NULL;
-	otr_ops.handle_smp_event = &op_handle_smp_event;
-	otr_ops.handle_msg_event = &op_handle_msg_event;
-	otr_ops.create_instag = &op_create_instag;
-	otr_ops.convert_msg = &op_convert_msg;
-	otr_ops.convert_free = &op_convert_free;
-	otr_ops.timer_control = NULL;           /* we just poll */
-
-	root_command_add("otr", 1, cmd_otr, 0);
-	register_irc_plugin(&otr_plugin);
-}
-
-#ifndef OTR_BI
-struct plugin_info *init_plugin_info(void)
-{
-	static struct plugin_info info = {
-		BITLBEE_ABI_VERSION_CODE,
-		"otr",
-		BITLBEE_VERSION,
-		"Off-the-Record communication",
-		NULL,
-		NULL
-	};
-
-	return &info;
-}
-#endif
-
-gboolean otr_irc_new(irc_t *irc)
-{
-	set_t *s;
-	GSList *l;
-
-	irc->otr = g_new0(otr_t, 1);
-	irc->otr->us = otrl_userstate_create();
-
-	s = set_add(&irc->b->set, "otr_color_encrypted", "true", set_eval_bool, irc);
-
-	s = set_add(&irc->b->set, "otr_policy", "opportunistic", set_eval_list, irc);
-	l = g_slist_prepend(NULL, "never");
-	l = g_slist_prepend(l, "opportunistic");
-	l = g_slist_prepend(l, "manual");
-	l = g_slist_prepend(l, "always");
-	s->eval_data = l;
-
-	s = set_add(&irc->b->set, "otr_does_html", "true", set_eval_bool, irc);
-
-	/* regularly call otrl_message_poll */
-	gint definterval = otrl_message_poll_get_default_interval(irc->otr->us);
-	irc->otr->timer = b_timeout_add(definterval, ev_message_poll, irc->otr);
-
-	return TRUE;
-}
-
-void otr_irc_free(irc_t *irc)
-{
-	set_t *s;
-	otr_t *otr = irc->otr;
-
-	otr_disconnect_all(irc);
-	b_event_remove(otr->timer);
-	otrl_userstate_free(otr->us);
-
-	s = set_find(&irc->b->set, "otr_policy");
-	g_slist_free(s->eval_data);
-
-	if (otr->keygen) {
-		kill(otr->keygen, SIGTERM);
-		waitpid(otr->keygen, NULL, 0);
-		/* TODO: remove stale keygen tempfiles */
-	}
-	if (otr->to) {
-		fclose(otr->to);
-	}
-	if (otr->from) {
-		fclose(otr->from);
-	}
-	while (otr->todo) {
-		kg_t *p = otr->todo;
-		otr->todo = p->next;
-		g_free(p);
-	}
-	g_free(otr);
-}
-
-void otr_load(irc_t *irc)
-{
-	char s[512];
-	account_t *a;
-	gcry_error_t e;
-	gcry_error_t enoent = gcry_error_from_errno(ENOENT);
-	int kg = 0;
-
-	if (strsane(irc->user->nick)) {
-		g_snprintf(s, 511, "%s%s.otr_keys", global.conf->configdir, irc->user->nick);
-		e = otrl_privkey_read(irc->otr->us, s);
-		if (e && e != enoent) {
-			irc_rootmsg(irc, "otr load: %s: %s", s, gcry_strerror(e));
-		}
-		g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, irc->user->nick);
-		e = otrl_privkey_read_fingerprints(irc->otr->us, s, NULL, NULL);
-		if (e && e != enoent) {
-			irc_rootmsg(irc, "otr load: %s: %s", s, gcry_strerror(e));
-		}
-		g_snprintf(s, 511, "%s%s.otr_instags", global.conf->configdir, irc->user->nick);
-		e = otrl_instag_read(irc->otr->us, s);
-		if (e && e != enoent) {
-			irc_rootmsg(irc, "otr load: %s: %s", s, gcry_strerror(e));
-		}
-	}
-
-	/* check for otr keys on all accounts */
-	for (a = irc->b->accounts; a; a = a->next) {
-		kg = otr_check_for_key(a) || kg;
-	}
-	if (kg) {
-		irc_rootmsg(irc, "Notice: "
-		            "The accounts above do not have OTR encryption keys associated with them, yet. "
-		            "These keys are now being generated in the background. "
-		            "You will be notified as they are completed. "
-		            "It is not necessary to wait; "
-		            "BitlBee can be used normally during key generation. "
-		            "You may safely ignore this message if you don't know what OTR is. ;)");
-	}
-}
-
-void otr_save(irc_t *irc)
-{
-	char s[512];
-	gcry_error_t e;
-
-	if (strsane(irc->user->nick)) {
-		g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, irc->user->nick);
-		e = otrl_privkey_write_fingerprints(irc->otr->us, s);
-		if (e) {
-			irc_rootmsg(irc, "otr save: %s: %s", s, gcry_strerror(e));
-		}
-		chmod(s, 0600);
-	}
-}
-
-void otr_remove(const char *nick)
-{
-	char s[512];
-
-	if (strsane(nick)) {
-		g_snprintf(s, 511, "%s%s.otr_keys", global.conf->configdir, nick);
-		unlink(s);
-		g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, nick);
-		unlink(s);
-	}
-}
-
-void otr_rename(const char *onick, const char *nnick)
-{
-	char s[512], t[512];
-
-	if (strsane(nnick) && strsane(onick)) {
-		g_snprintf(s, 511, "%s%s.otr_keys", global.conf->configdir, onick);
-		g_snprintf(t, 511, "%s%s.otr_keys", global.conf->configdir, nnick);
-		rename(s, t);
-		g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, onick);
-		g_snprintf(t, 511, "%s%s.otr_fprints", global.conf->configdir, nnick);
-		rename(s, t);
-	}
-}
-
-int otr_check_for_key(account_t *a)
-{
-	irc_t *irc = a->bee->ui_data;
-	OtrlPrivKey *k;
-
-	/* don't do OTR on certain (not classic IM) protocols, e.g. twitter */
-	if (a->prpl->options & PRPL_OPT_NOOTR) {
-		return 0;
-	}
-
-	k = otrl_privkey_find(irc->otr->us, a->user, a->prpl->name);
-	if (k) {
-		irc_rootmsg(irc, "otr: %s/%s ready", a->user, a->prpl->name);
-		return 0;
-	}
-	if (keygen_in_progress(irc, a->user, a->prpl->name)) {
-		irc_rootmsg(irc, "otr: keygen for %s/%s already in progress", a->user, a->prpl->name);
-		return 0;
-	} else {
-		irc_rootmsg(irc, "otr: starting background keygen for %s/%s", a->user, a->prpl->name);
-		otr_keygen(irc, a->user, a->prpl->name);
-		return 1;
-	}
-}
-
-char *otr_filter_msg_in(irc_user_t *iu, char *msg, int flags)
-{
-	int ignore_msg;
-	char *newmsg = NULL;
-	OtrlTLV *tlvs = NULL;
-	irc_t *irc = iu->irc;
-	struct im_connection *ic = iu->bu->ic;
-
-	/* don't do OTR on certain (not classic IM) protocols, e.g. twitter */
-	if (ic->acc->prpl->options & PRPL_OPT_NOOTR ||
-	    iu->bu->flags & BEE_USER_NOOTR) {
-		return msg;
-	}
-
-	ignore_msg = otrl_message_receiving(irc->otr->us, &otr_ops, ic,
-	                                    ic->acc->user, ic->acc->prpl->name, iu->bu->handle, msg, &newmsg,
-	                                    &tlvs, NULL, NULL, NULL);
-
-	if (tlvs) {
-		otrl_tlv_free(tlvs);
-	}
-
-	if (ignore_msg) {
-		/* this was an internal OTR protocol message */
-		return NULL;
-	} else if (!newmsg) {
-		/* this was a non-OTR message */
-		return otr_filter_colors(msg);
-	} else {
-		/* we're done with the original msg, which will be caller-freed. */
-		return newmsg;
-	}
-}
-
-char *otr_filter_msg_out(irc_user_t *iu, char *msg, int flags)
-{
-	int st;
-	char *otrmsg = NULL;
-	ConnContext *ctx = NULL;
-	irc_t *irc = iu->irc;
-	struct im_connection *ic = iu->bu->ic;
-	otrl_instag_t instag = OTRL_INSTAG_BEST; // XXX?
-
-	/* NB: in libotr 4.0.0 OTRL_INSTAG_RECENT will cause a null-pointer deref
-	 * in otrl_message_sending with newly-added OTR contexts.
-	 */
-
-	/* don't do OTR on certain (not classic IM) protocols, e.g. twitter */
-	if (ic->acc->prpl->options & OPT_NOOTR ||
-	    iu->bu->flags & BEE_USER_NOOTR) {
-		return msg;
-	}
-
-	st = otrl_message_sending(irc->otr->us, &otr_ops, ic,
-	                          ic->acc->user, ic->acc->prpl->name, iu->bu->handle, instag,
-	                          msg, NULL, &otrmsg, OTRL_FRAGMENT_SEND_ALL_BUT_LAST, &ctx, NULL, NULL);
-
-	if (otrmsg && otrmsg != msg) {
-		/* libotr wants us to replace our message */
-		/* NB: caller will free old msg */
-		msg = st ? NULL : g_strdup(otrmsg);
-		otrl_message_free(otrmsg);
-	}
-
-	if (st) {
-		irc_usernotice(iu, "otr: error handling outgoing message: %d", st);
-		msg = NULL;     /* do not send plaintext! */
-	}
-
-	return msg;
-}
-
-static const struct irc_plugin otr_plugin =
-{
-	otr_irc_new,
-	otr_irc_free,
-	otr_filter_msg_out,
-	otr_filter_msg_in,
-	otr_load,
-	otr_save,
-	otr_remove,
-};
-
-static void cmd_otr(irc_t *irc, char **args)
-{
-	const command_t *cmd;
-
-	if (!args[0]) {
-		return;
-	}
-
-	if (!args[1]) {
-		return;
-	}
-
-	for (cmd = otr_commands; cmd->command; cmd++) {
-		if (strcmp(cmd->command, args[1]) == 0) {
-			break;
-		}
-	}
-
-	if (!cmd->command) {
-		irc_rootmsg(irc, "%s: unknown subcommand \"%s\", see \x02help otr\x02",
-		            args[0], args[1]);
-		return;
-	}
-
-	if (!args[cmd->required_parameters + 1]) {
-		irc_rootmsg(irc, "%s %s: not enough arguments (%d req.)",
-		            args[0], args[1], cmd->required_parameters);
-		return;
-	}
-
-	cmd->execute(irc, args + 1);
-}
-
-
-/*** OTR "MessageAppOps" callbacks for global.otr_ui: ***/
-
-OtrlPolicy op_policy(void *opdata, ConnContext *context)
-{
-	struct im_connection *ic = check_imc(opdata, context->accountname, context->protocol);
-	irc_t *irc = ic->bee->ui_data;
-	const char *p;
-
-	/* policy override during keygen: if we're missing the key for context but are currently
-	   generating it, then that's as much as we can do. => temporarily return NEVER. */
-	if (keygen_in_progress(irc, context->accountname, context->protocol) &&
-	    !otrl_privkey_find(irc->otr->us, context->accountname, context->protocol)) {
-		return OTRL_POLICY_NEVER;
-	}
-
-	p = set_getstr(&ic->bee->set, "otr_policy");
-	if (!strcmp(p, "never")) {
-		return OTRL_POLICY_NEVER;
-	}
-	if (!strcmp(p, "opportunistic")) {
-		return OTRL_POLICY_OPPORTUNISTIC;
-	}
-	if (!strcmp(p, "manual")) {
-		return OTRL_POLICY_MANUAL;
-	}
-	if (!strcmp(p, "always")) {
-		return OTRL_POLICY_ALWAYS;
-	}
-
-	return OTRL_POLICY_OPPORTUNISTIC;
-}
-
-void op_create_privkey(void *opdata, const char *accountname,
-                       const char *protocol)
-{
-	struct im_connection *ic = check_imc(opdata, accountname, protocol);
-	irc_t *irc = ic->bee->ui_data;
-
-	/* will fail silently if keygen already in progress */
-	otr_keygen(irc, accountname, protocol);
-}
-
-int op_is_logged_in(void *opdata, const char *accountname,
-                    const char *protocol, const char *recipient)
-{
-	struct im_connection *ic = check_imc(opdata, accountname, protocol);
-	bee_user_t *bu;
-
-	/* lookup the irc_user_t for the given recipient */
-	bu = bee_user_by_handle(ic->bee, ic, recipient);
-	if (bu) {
-		if (bu->flags & BEE_USER_ONLINE) {
-			return 1;
-		} else {
-			return 0;
-		}
-	} else {
-		return -1;
-	}
-}
-
-void op_inject_message(void *opdata, const char *accountname,
-                       const char *protocol, const char *recipient, const char *message)
-{
-	struct im_connection *ic = check_imc(opdata, accountname, protocol);
-	irc_t *irc = ic->bee->ui_data;
-
-	if (strcmp(accountname, recipient) == 0) {
-		/* huh? injecting messages to myself? */
-		irc_rootmsg(irc, "note to self: %s", message);
-	} else {
-		/* need to drop some consts here :-( */
-		/* TODO: get flags into op_inject_message?! */
-		ic->acc->prpl->buddy_msg(ic, (char *) recipient, (char *) message, 0);
-		/* ignoring return value :-/ */
-	}
-}
-
-void op_new_fingerprint(void *opdata, OtrlUserState us,
-                        const char *accountname, const char *protocol,
-                        const char *username, unsigned char fingerprint[20])
-{
-	struct im_connection *ic = check_imc(opdata, accountname, protocol);
-	irc_t *irc = ic->bee->ui_data;
-	irc_user_t *u = peeruser(irc, username, protocol);
-	char hunam[45];         /* anybody looking? ;-) */
-
-	otrl_privkey_hash_to_human(hunam, fingerprint);
-	if (u) {
-		irc_usernotice(u, "new fingerprint: %s", hunam);
-	} else {
-		/* this case shouldn't normally happen */
-		irc_rootmsg(irc, "new fingerprint for %s/%s: %s",
-		            username, protocol, hunam);
-	}
-}
-
-void op_write_fingerprints(void *opdata)
-{
-	struct im_connection *ic = (struct im_connection *) opdata;
-	irc_t *irc = ic->bee->ui_data;
-
-	otr_save(irc);
-}
-
-void op_gone_secure(void *opdata, ConnContext *context)
-{
-	struct im_connection *ic =
-	        check_imc(opdata, context->accountname, context->protocol);
-	irc_user_t *u;
-	irc_t *irc = ic->bee->ui_data;
-
-	u = peeruser(irc, context->username, context->protocol);
-	if (!u) {
-		log_message(LOGLVL_ERROR,
-		            "BUG: otr.c: op_gone_secure: irc_user_t for %s/%s/%s not found!",
-		            context->username, context->protocol, context->accountname);
-		return;
-	}
-
-	otr_update_uflags(context, u);
-	if (!otr_update_modeflags(irc, u)) {
-		char *trust = u->flags & IRC_USER_OTR_TRUSTED ? "trusted" : "untrusted!";
-		irc_usernotice(u, "conversation is now off the record (%s)", trust);
-	}
-}
-
-void op_gone_insecure(void *opdata, ConnContext *context)
-{
-	struct im_connection *ic =
-	        check_imc(opdata, context->accountname, context->protocol);
-	irc_t *irc = ic->bee->ui_data;
-	irc_user_t *u;
-
-	u = peeruser(irc, context->username, context->protocol);
-	if (!u) {
-		log_message(LOGLVL_ERROR,
-		            "BUG: otr.c: op_gone_insecure: irc_user_t for %s/%s/%s not found!",
-		            context->username, context->protocol, context->accountname);
-		return;
-	}
-	otr_update_uflags(context, u);
-	if (!otr_update_modeflags(irc, u)) {
-		irc_usernotice(u, "conversation is now in cleartext");
-	}
-}
-
-void op_still_secure(void *opdata, ConnContext *context, int is_reply)
-{
-	struct im_connection *ic =
-	        check_imc(opdata, context->accountname, context->protocol);
-	irc_t *irc = ic->bee->ui_data;
-	irc_user_t *u;
-
-	u = peeruser(irc, context->username, context->protocol);
-	if (!u) {
-		log_message(LOGLVL_ERROR,
-		            "BUG: otr.c: op_still_secure: irc_user_t for %s/%s/%s not found!",
-		            context->username, context->protocol, context->accountname);
-		return;
-	}
-
-	otr_update_uflags(context, u);
-	if (!otr_update_modeflags(irc, u)) {
-		char *trust = u->flags & IRC_USER_OTR_TRUSTED ? "trusted" : "untrusted!";
-		irc_usernotice(u, "otr connection has been refreshed (%s)", trust);
-	}
-}
-
-int op_max_message_size(void *opdata, ConnContext *context)
-{
-	struct im_connection *ic =
-	        check_imc(opdata, context->accountname, context->protocol);
-
-	return ic->acc->prpl->mms;
-}
-
-const char *op_account_name(void *opdata, const char *account, const char *protocol)
-{
-	struct im_connection *ic = (struct im_connection *) opdata;
-	irc_t *irc = ic->bee->ui_data;
-
-	return peernick(irc, account, protocol);
-}
-
-void op_create_instag(void *opdata, const char *account, const char *protocol)
-{
-	struct im_connection *ic =
-	        check_imc(opdata, account, protocol);
-	irc_t *irc = ic->bee->ui_data;
-	gcry_error_t e;
-	char s[512];
-
-	g_snprintf(s, 511, "%s%s.otr_instags", global.conf->configdir,
-	           irc->user->nick);
-	e = otrl_instag_generate(irc->otr->us, s, account, protocol);
-	if (e) {
-		irc_rootmsg(irc, "otr: %s/%s: otrl_instag_generate failed: %s",
-		            account, protocol, gcry_strerror(e));
-	}
-}
-
-static char *otr_filter_colors(char *msg)
-{
-	return str_reject_chars(msg, "\x02\x03", '?');
-}
-
-/* returns newly allocated string */
-static char *otr_color_encrypted(char *msg, char *color, gboolean is_query) {
-	char **lines;
-	GString *out;
-	int i;
-
-	lines = g_strsplit(msg, "\n", -1);
-
-	/* up to 4 extra chars per line (e.g., '\x03' + ("03"|"05") + ' ') */
-	out = g_string_sized_new(strlen(msg) + g_strv_length(lines) * 4);
-	
-	for (i = 0; lines[i]; i++) {
-		char *line = lines[i];
-
-		if (i != 0) {
-			g_string_append_c(out, '\n');
-
-		} else if (is_query && g_strncasecmp(line, "/me ", 4) == 0) {
-			/* in a query window, keep "/me " uncolored at the beginning */
-			line += 4;
-			g_string_append(out, "/me ");
-		}
-
-		g_string_append_c(out, '\x03');
-		g_string_append(out, color);
-
-		/* comma in first place could mess with the color code */
-		if (line[0] == ',') {
-			/* insert a space between color spec and message */
-			g_string_append_c(out, ' ');
-		}
-
-		g_string_append(out, otr_filter_colors(line));
-	}
-
-	g_strfreev(lines);
-
-	return g_string_free(out, FALSE);
-}
-
-void op_convert_msg(void *opdata, ConnContext *ctx, OtrlConvertType typ,
-                    char **dst, const char *src)
-{
-	struct im_connection *ic =
-	        check_imc(opdata, ctx->accountname, ctx->protocol);
-	irc_t *irc = ic->bee->ui_data;
-	irc_user_t *iu = peeruser(irc, ctx->username, ctx->protocol);
-
-	if (typ == OTRL_CONVERT_RECEIVING) {
-		char *msg = g_strdup(src);
-
-		/* HTML decoding */
-		if (set_getbool(&ic->bee->set, "otr_does_html") &&
-		    !(ic->flags & OPT_DOES_HTML) &&
-		    set_getbool(&ic->bee->set, "strip_html")) {
-			strip_html(msg);
-
-			/* msg is borrowed by *dst (unless the next if decides to color it) */
-			*dst = msg;
-		}
-
-		/* coloring */
-		if (set_getbool(&ic->bee->set, "otr_color_encrypted")) {
-			const char *trust = ctx->active_fingerprint->trust;
-			char *color = (trust && *trust) ? OTR_COLOR_TRUSTED : OTR_COLOR_UNTRUSTED;
-			gboolean is_query = (irc_user_msgdest(iu) == irc->user->nick);
-
-			/* the return value of otr_color_encrypted() is borrowed by *dst */
-			*dst = otr_color_encrypted(msg, color, is_query);
-
-			/* this branch doesn't need msg */
-			g_free(msg);
-		}
-	} else {
-		/* HTML encoding */
-		/* consider OTR plaintext to be HTML if otr_does_html is set */
-		if (ctx && ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED &&
-		    set_getbool(&ic->bee->set, "otr_does_html") &&
-		    (g_strncasecmp(src, "<html>", 6) != 0)) {
-			*dst = escape_html(src);
-		}
-	}
-}
-
-void op_convert_free(void *opdata, ConnContext *ctx, char *msg)
-{
-	g_free(msg);
-}
-
-/* Socialist Millionaires' Protocol */
-void op_handle_smp_event(void *opdata, OtrlSMPEvent ev, ConnContext *ctx,
-                         unsigned short percent, char *question)
-{
-	struct im_connection *ic =
-	        check_imc(opdata, ctx->accountname, ctx->protocol);
-	irc_t *irc = ic->bee->ui_data;
-	OtrlUserState us = irc->otr->us;
-	irc_user_t *u = peeruser(irc, ctx->username, ctx->protocol);
-
-	if (!u) {
-		return;
-	}
-
-	switch (ev) {
-	case OTRL_SMPEVENT_ASK_FOR_SECRET:
-		irc_rootmsg(irc, "smp: initiated by %s"
-		            " - respond with \x02otr smp %s \"<secret>\"\x02",
-		            u->nick, u->nick);
-		break;
-	case OTRL_SMPEVENT_ASK_FOR_ANSWER:
-		irc_rootmsg(irc, "smp: initiated by %s with question: \x02\"%s\"\x02", u->nick,
-		            question);
-		irc_rootmsg(irc, "smp: respond with \x02otr smp %s \"<answer>\"\x02",
-		            u->nick);
-		break;
-	case OTRL_SMPEVENT_CHEATED:
-		irc_rootmsg(irc, "smp %s: opponent violated protocol, aborting",
-		            u->nick);
-		otrl_message_abort_smp(us, &otr_ops, u->bu->ic, ctx);
-		otrl_sm_state_free(ctx->smstate);
-		break;
-	case OTRL_SMPEVENT_NONE:
-		break;
-	case OTRL_SMPEVENT_IN_PROGRESS:
-		break;
-	case OTRL_SMPEVENT_SUCCESS:
-		if (ctx->smstate->received_question) {
-			irc_rootmsg(irc, "smp %s: correct answer, you are trusted",
-			            u->nick);
-		} else {
-			irc_rootmsg(irc, "smp %s: secrets proved equal, fingerprint trusted",
-			            u->nick);
-		}
-		otrl_sm_state_free(ctx->smstate);
-		break;
-	case OTRL_SMPEVENT_FAILURE:
-		if (ctx->smstate->received_question) {
-			irc_rootmsg(irc, "smp %s: wrong answer, you are not trusted",
-			            u->nick);
-		} else {
-			irc_rootmsg(irc, "smp %s: secrets did not match, fingerprint not trusted",
-			            u->nick);
-		}
-		otrl_sm_state_free(ctx->smstate);
-		break;
-	case OTRL_SMPEVENT_ABORT:
-		irc_rootmsg(irc, "smp: received abort from %s", u->nick);
-		otrl_sm_state_free(ctx->smstate);
-		break;
-	case OTRL_SMPEVENT_ERROR:
-		irc_rootmsg(irc, "smp %s: protocol error, aborting",
-		            u->nick);
-		otrl_message_abort_smp(us, &otr_ops, u->bu->ic, ctx);
-		otrl_sm_state_free(ctx->smstate);
-		break;
-	}
-}
-
-void op_handle_msg_event(void *opdata, OtrlMessageEvent ev, ConnContext *ctx,
-                         const char *message, gcry_error_t err)
-{
-	switch (ev) {
-	case OTRL_MSGEVENT_ENCRYPTION_REQUIRED:
-		display_otr_message(opdata, ctx,
-		                    "policy requires encryption - message not sent");
-		break;
-	case OTRL_MSGEVENT_ENCRYPTION_ERROR:
-		display_otr_message(opdata, ctx,
-		                    "error during encryption - message not sent");
-		break;
-	case OTRL_MSGEVENT_CONNECTION_ENDED:
-		display_otr_message(opdata, ctx,
-		                    "other end has disconnected OTR - "
-		                    "close connection or reconnect!");
-		break;
-	case OTRL_MSGEVENT_SETUP_ERROR:
-		display_otr_message(opdata, ctx,
-		                    "OTR connection failed: %s", gcry_strerror(err));
-		break;
-	case OTRL_MSGEVENT_MSG_REFLECTED:
-		display_otr_message(opdata, ctx,
-		                    "received our own OTR message (!?)");
-		break;
-	case OTRL_MSGEVENT_MSG_RESENT:
-		display_otr_message(opdata, ctx,
-		                    "the previous message was resent");
-		break;
-	case OTRL_MSGEVENT_RCVDMSG_NOT_IN_PRIVATE:
-		display_otr_message(opdata, ctx,
-		                    "unexpected encrypted message received");
-		break;
-	case OTRL_MSGEVENT_RCVDMSG_UNREADABLE:
-		display_otr_message(opdata, ctx,
-		                    "unreadable encrypted message received");
-		break;
-	case OTRL_MSGEVENT_RCVDMSG_MALFORMED:
-		display_otr_message(opdata, ctx,
-		                    "malformed OTR message received");
-		break;
-	case OTRL_MSGEVENT_LOG_HEARTBEAT_RCVD:
-		if (global.conf->verbose) {
-			log_otr_message(opdata, "%s/%s: heartbeat received",
-			                ctx->accountname, ctx->protocol);
-		}
-		break;
-	case OTRL_MSGEVENT_LOG_HEARTBEAT_SENT:
-		if (global.conf->verbose) {
-			log_otr_message(opdata, "%s/%s: heartbeat sent",
-			                ctx->accountname, ctx->protocol);
-		}
-		break;
-	case OTRL_MSGEVENT_RCVDMSG_GENERAL_ERR:
-		display_otr_message(opdata, ctx,
-		                    "OTR error message received: %s", message);
-		break;
-	case OTRL_MSGEVENT_RCVDMSG_UNENCRYPTED:
-		display_otr_message(opdata, ctx,
-		                    "unencrypted message received: %s", message);
-		break;
-	case OTRL_MSGEVENT_RCVDMSG_UNRECOGNIZED:
-		display_otr_message(opdata, ctx,
-		                    "unrecognized OTR message received");
-		break;
-	case OTRL_MSGEVENT_RCVDMSG_FOR_OTHER_INSTANCE:
-		display_otr_message(opdata, ctx,
-		                    "OTR message for a different instance received");
-		break;
-	default:
-		/* shouldn't happen */
-		break;
-	}
-}
-
-const char *op_otr_error_message(void *opdata, ConnContext *ctx,
-                                 OtrlErrorCode err_code)
-{
-	switch (err_code) {
-	case OTRL_ERRCODE_ENCRYPTION_ERROR:
-		return "i failed to encrypt a message";
-	case OTRL_ERRCODE_MSG_NOT_IN_PRIVATE:
-		return "you sent an encrypted message i didn't expect";
-	case OTRL_ERRCODE_MSG_UNREADABLE:
-		return "could not read encrypted message";
-	case OTRL_ERRCODE_MSG_MALFORMED:
-		return "you sent a malformed OTR message";
-	default:
-		return "i suffered an unexpected OTR error";
-	}
-}
-
-
-
-/*** OTR sub-command handlers ***/
-
-void cmd_otr_reconnect(irc_t *irc, char **args)
-{
-	cmd_otr_disconnect(irc, args);
-	cmd_otr_connect(irc, args);
-}
-
-void cmd_otr_disconnect(irc_t *irc, char **args)
-{
-	irc_user_t *u;
-
-	if (!strcmp("*", args[1])) {
-		otr_disconnect_all(irc);
-		irc_rootmsg(irc, "all conversations are now in cleartext");
-	} else {
-		u = irc_user_by_name(irc, args[1]);
-		if (otr_disconnect_user(irc, u)) {
-			irc_usernotice(u, "conversation is now in cleartext");
-		} else {
-			irc_rootmsg(irc, "%s: unknown user", args[1]);
-		}
-	}
-}
-
-void cmd_otr_connect(irc_t *irc, char **args)
-{
-	irc_user_t *u;
-	char *msg, *query = "?OTR?";
-
-	u = irc_user_by_name(irc, args[1]);
-	if (!u || !u->bu || !u->bu->ic) {
-		irc_rootmsg(irc, "%s: unknown user", args[1]);
-		return;
-	}
-
-	/* passing this through the filter so it goes through libotr which
-	 * will replace the simple query string with a proper one */
-	msg = otr_filter_msg_out(u, query, 0);
-
-	/* send the message */
-	if (msg) {
-		u->bu->ic->acc->prpl->buddy_msg(u->bu->ic, u->bu->handle, msg, 0);  /* XXX flags? */
-		/* XXX error message? */
-
-		if (msg != query) {
-			g_free(msg);
-		}
-	}
-}
-
-void cmd_otr_smp(irc_t *irc, char **args)
-{
-	otr_smp_or_smpq(irc, args[1], NULL, args[2]);   /* no question */
-}
-
-void cmd_otr_smpq(irc_t *irc, char **args)
-{
-	otr_smp_or_smpq(irc, args[1], args[2], args[3]);
-}
-
-void cmd_otr_trust(irc_t *irc, char **args)
-{
-	irc_user_t *u;
-	ConnContext *ctx;
-	unsigned char raw[20];
-	Fingerprint *fp;
-	int i, j;
-
-	u = irc_user_by_name(irc, args[1]);
-	if (!u || !u->bu || !u->bu->ic) {
-		irc_rootmsg(irc, "%s: unknown user", args[1]);
-		return;
-	}
-
-	ctx = otrl_context_find(irc->otr->us, u->bu->handle,
-	                        u->bu->ic->acc->user, u->bu->ic->acc->prpl->name, OTRL_INSTAG_MASTER, 0, NULL, NULL,
-	                        NULL);
-	if (!ctx) {
-		irc_rootmsg(irc, "%s: no otr context with user", args[1]);
-		return;
-	}
-
-	/* convert given fingerprint to raw representation */
-	for (i = 0; i < 5; i++) {
-		for (j = 0; j < 4; j++) {
-			char *p = args[2 + i] + (2 * j);
-			char *q = p + 1;
-			int x, y;
-
-			if (!*p || !*q) {
-				irc_rootmsg(irc, "failed: truncated fingerprint block %d", i + 1);
-				return;
-			}
-
-			x = hexval(*p);
-			y = hexval(*q);
-			if (x < 0) {
-				irc_rootmsg(irc, "failed: %d. hex digit of block %d out of range", 2 * j + 1, i + 1);
-				return;
-			}
-			if (y < 0) {
-				irc_rootmsg(irc, "failed: %d. hex digit of block %d out of range", 2 * j + 2, i + 1);
-				return;
-			}
-
-			raw[i * 4 + j] = x * 16 + y;
-		}
-	}
-	fp = otrl_context_find_fingerprint(ctx, raw, 0, NULL);
-	if (!fp) {
-		irc_rootmsg(irc, "failed: no such fingerprint for %s", args[1]);
-	} else {
-		char *trust = args[7] ? args[7] : "affirmed";
-		otrl_context_set_trust(fp, trust);
-		irc_rootmsg(irc, "fingerprint match, trust set to \"%s\"", trust);
-		if (u->flags & IRC_USER_OTR_ENCRYPTED) {
-			u->flags |= IRC_USER_OTR_TRUSTED;
-		}
-		otr_update_modeflags(irc, u);
-	}
-}
-
-void cmd_otr_info(irc_t *irc, char **args)
-{
-	if (!args[1]) {
-		show_general_otr_info(irc);
-	} else {
-		char *arg = g_strdup(args[1]);
-		char *myhandle, *handle = NULL, *protocol;
-		ConnContext *bestctx = NULL, *ctx;
-
-		/* interpret arg as 'user/protocol/account' if possible */
-		protocol = strchr(arg, '/');
-		myhandle = NULL;
-		if (protocol) {
-			*(protocol++) = '\0';
-			myhandle = strchr(protocol, '/');
-		}
-		if (protocol && myhandle) {
-			*(myhandle++) = '\0';
-			handle = arg;
-			ctx = otrl_context_find(irc->otr->us, handle, myhandle, protocol, OTRL_INSTAG_MASTER, 0, NULL,
-			                        NULL, NULL);
-			if (!ctx) {
-				irc_rootmsg(irc, "no such context");
-				g_free(arg);
-				return;
-			}
-		} else {
-			irc_user_t *u = irc_user_by_name(irc, args[1]);
-			if (!u || !u->bu || !u->bu->ic) {
-				irc_rootmsg(irc, "%s: unknown user", args[1]);
-				g_free(arg);
-				return;
-			}
-			ctx = otrl_context_find(irc->otr->us, u->bu->handle, u->bu->ic->acc->user,
-			                        u->bu->ic->acc->prpl->name, OTRL_INSTAG_MASTER, 0, NULL, NULL, NULL);
-			if (!ctx) {
-				irc_rootmsg(irc, "no otr context with %s", args[1]);
-				g_free(arg);
-				return;
-			}
-			/* This does no harm if it returns NULL */
-			bestctx = otrl_context_find(irc->otr->us, u->bu->handle, u->bu->ic->acc->user,
-			                            u->bu->ic->acc->prpl->name, OTRL_INSTAG_BEST, 0, NULL, NULL, NULL);
-		}
-
-		/* show how we resolved the (nick) argument, if we did */
-		if (handle != arg) {
-			irc_rootmsg(irc, "%s:", args[1]);
-			irc_rootmsg(irc, "  they are: %s/%s", ctx->username, ctx->protocol);
-			irc_rootmsg(irc, "  we are: %s/%s", ctx->accountname, ctx->protocol);
-		}
-		show_otr_context_info(irc, ctx, bestctx);
-		g_free(arg);
-	}
-}
-
-void cmd_otr_keygen(irc_t *irc, char **args)
-{
-	account_t *a;
-
-	if ((a = account_get(irc->b, args[1])) == NULL) {
-		irc_rootmsg(irc, "Could not find account `%s'.", args[1]);
-		return;
-	}
-
-	if (keygen_in_progress(irc, a->user, a->prpl->name)) {
-		irc_rootmsg(irc, "keygen for account `%s' already in progress", a->tag);
-		return;
-	}
-
-	if (otrl_privkey_find(irc->otr->us, a->user, a->prpl->name)) {
-		char *s = g_strdup_printf("account `%s' already has a key, replace it?", a->tag);
-		query_add(irc, NULL, s, yes_keygen, NULL, NULL, a);
-		g_free(s);
-	} else {
-		otr_keygen(irc, a->user, a->prpl->name);
-	}
-}
-
-void yes_forget_fingerprint(void *data)
-{
-	pair_t *p = (pair_t *) data;
-	irc_t *irc = (irc_t *) p->fst;
-	Fingerprint *fp = (Fingerprint *) p->snd;
-
-	g_free(p);
-
-	if (fp == fp->context->active_fingerprint) {
-		irc_rootmsg(irc, "that fingerprint is active, terminate otr connection first");
-		return;
-	}
-
-	otrl_context_forget_fingerprint(fp, 0);
-}
-
-void yes_forget_context(void *data)
-{
-	pair_t *p = (pair_t *) data;
-	irc_t *irc = (irc_t *) p->fst;
-	ConnContext *ctx = (ConnContext *) p->snd;
-
-	g_free(p);
-
-	// XXX forget all contexts
-
-	if (ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
-		irc_rootmsg(irc, "active otr connection with %s, terminate it first",
-		            peernick(irc, ctx->username, ctx->protocol));
-		return;
-	}
-
-	if (ctx->msgstate == OTRL_MSGSTATE_FINISHED) {
-		otrl_context_force_plaintext(ctx);
-	}
-	otrl_context_forget(ctx);
-}
-
-void yes_forget_key(void *data)
-{
-	OtrlPrivKey *key = (OtrlPrivKey *) data;
-
-	otrl_privkey_forget(key);
-	/* Hm, libotr doesn't seem to offer a function for explicitly /writing/
-	   keyfiles. So the key will be back on the next load... */
-	/* TODO: Actually erase forgotten keys from storage? */
-}
-
-void cmd_otr_forget(irc_t *irc, char **args)
-{
-	if (!strcmp(args[1], "fingerprint")) {
-		irc_user_t *u;
-		ConnContext *ctx;
-		Fingerprint *fp;
-		char human[54];
-		char *s;
-		pair_t *p;
-
-		if (!args[3]) {
-			irc_rootmsg(irc, "otr %s %s: not enough arguments (2 req.)", args[0], args[1]);
-			return;
-		}
-
-		/* TODO: allow context specs ("user/proto/account") in 'otr forget fingerprint'? */
-		u = irc_user_by_name(irc, args[2]);
-		if (!u || !u->bu || !u->bu->ic) {
-			irc_rootmsg(irc, "%s: unknown user", args[2]);
-			return;
-		}
-
-		ctx = otrl_context_find(irc->otr->us, u->bu->handle, u->bu->ic->acc->user,
-		                        u->bu->ic->acc->prpl->name, OTRL_INSTAG_MASTER, 0, NULL, NULL, NULL);
-		if (!ctx) {
-			irc_rootmsg(irc, "no otr context with %s", args[2]);
-			return;
-		}
-
-		fp = match_fingerprint(irc, ctx, ((const char **) args) + 3);
-		if (!fp) {
-			/* match_fingerprint does error messages */
-			return;
-		}
-
-		if (fp == ctx->active_fingerprint) {
-			irc_rootmsg(irc, "that fingerprint is active, terminate otr connection first");
-			return;
-		}
-
-		otrl_privkey_hash_to_human(human, fp->fingerprint);
-		s = g_strdup_printf("about to forget fingerprint %s, are you sure?", human);
-		p = g_malloc(sizeof(pair_t));
-		if (!p) {
-			return;
-		}
-		p->fst = irc;
-		p->snd = fp;
-		query_add(irc, NULL, s, yes_forget_fingerprint, NULL, NULL, p);
-		g_free(s);
-	} else if (!strcmp(args[1], "context")) {
-		irc_user_t *u;
-		ConnContext *ctx;
-		char *s;
-		pair_t *p;
-
-		/* TODO: allow context specs ("user/proto/account") in 'otr forget contex'? */
-		u = irc_user_by_name(irc, args[2]);
-		if (!u || !u->bu || !u->bu->ic) {
-			irc_rootmsg(irc, "%s: unknown user", args[2]);
-			return;
-		}
-
-		ctx = otrl_context_find(irc->otr->us, u->bu->handle, u->bu->ic->acc->user,
-		                        u->bu->ic->acc->prpl->name, OTRL_INSTAG_MASTER, 0, NULL, NULL, NULL);
-		if (!ctx) {
-			irc_rootmsg(irc, "no otr context with %s", args[2]);
-			return;
-		}
-
-		if (ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
-			irc_rootmsg(irc, "active otr connection with %s, terminate it first", args[2]);
-			return;
-		}
-
-		s = g_strdup_printf("about to forget otr data about %s, are you sure?", args[2]);
-		p = g_malloc(sizeof(pair_t));
-		if (!p) {
-			return;
-		}
-		p->fst = irc;
-		p->snd = ctx;
-		query_add(irc, NULL, s, yes_forget_context, NULL, NULL, p);
-		g_free(s);
-	} else if (!strcmp(args[1], "key")) {
-		OtrlPrivKey *key;
-		char *s;
-
-		key = match_privkey(irc, ((const char **) args) + 2);
-		if (!key) {
-			/* match_privkey does error messages */
-			return;
-		}
-
-		s = g_strdup_printf("about to forget the private key for %s/%s, are you sure?",
-		                    key->accountname, key->protocol);
-		query_add(irc, NULL, s, yes_forget_key, NULL, NULL, key);
-		g_free(s);
-	} else {
-		irc_rootmsg(irc, "otr %s: unknown subcommand \"%s\", see \x02help otr forget\x02",
-		            args[0], args[1]);
-	}
-}
-
-
-/*** local helpers / subroutines: ***/
-
-void log_otr_message(void *opdata, const char *fmt, ...)
-{
-	va_list va;
-
-	va_start(va, fmt);
-	char *msg = g_strdup_vprintf(fmt, va);
-	va_end(va);
-
-	log_message(LOGLVL_INFO, "otr: %s", msg);
-
-	g_free(msg);
-}
-
-void display_otr_message(void *opdata, ConnContext *ctx, const char *fmt, ...)
-{
-	char *msg_, *msg;
-	struct im_connection *ic =
-	        check_imc(opdata, ctx->accountname, ctx->protocol);
-	irc_t *irc = ic->bee->ui_data;
-	irc_user_t *u = peeruser(irc, ctx->username, ctx->protocol);
-	va_list va;
-
-	va_start(va, fmt);
-	msg_ = g_strdup_vprintf(fmt, va);
-	va_end(va);
-
-	msg = word_wrap(msg_, IRC_WORD_WRAP);
-
-	if (u) {
-		/* just show this as a regular message */
-		irc_usermsg(u, "<<\002OTR\002>> %s", msg);
-	} else {
-		irc_rootmsg(irc, "[otr] %s", msg);
-	}
-
-	g_free(msg_);
-	g_free(msg);
-}
-
-/* combined handler for the 'otr smp' and 'otr smpq' commands */
-void otr_smp_or_smpq(irc_t *irc, const char *nick, const char *question,
-                     const char *secret)
-{
-	irc_user_t *u;
-	ConnContext *ctx;
-	otrl_instag_t instag = OTRL_INSTAG_BEST;  // XXX
-
-	u = irc_user_by_name(irc, nick);
-	if (!u || !u->bu || !u->bu->ic) {
-		irc_rootmsg(irc, "%s: unknown user", nick);
-		return;
-	}
-
-	ctx = otrl_context_find(irc->otr->us, u->bu->handle,
-	                        u->bu->ic->acc->user, u->bu->ic->acc->prpl->name, instag, 0, NULL, NULL, NULL);
-	if (!ctx || ctx->msgstate != OTRL_MSGSTATE_ENCRYPTED) {
-		irc_rootmsg(irc, "smp: otr inactive with %s, try \x02otr connect"
-		            " %s\x02", nick, nick);
-		return;
-	}
-
-	if (ctx->smstate->nextExpected != OTRL_SMP_EXPECT1) {
-		log_message(LOGLVL_INFO,
-		            "SMP already in phase %d, sending abort before reinitiating",
-		            ctx->smstate->nextExpected + 1);
-		otrl_message_abort_smp(irc->otr->us, &otr_ops, u->bu->ic, ctx);
-		otrl_sm_state_free(ctx->smstate);
-	}
-
-	if (question) {
-		/* this was 'otr smpq', just initiate */
-		irc_rootmsg(irc, "smp: initiating with %s...", u->nick);
-		otrl_message_initiate_smp_q(irc->otr->us, &otr_ops, u->bu->ic, ctx,
-		                            question, (unsigned char *) secret, strlen(secret));
-		/* smp is now in EXPECT2 */
-	} else {
-		/* this was 'otr smp', initiate or reply */
-		/* warning: the following assumes that smstates are cleared whenever an SMP
-		   is completed or aborted! */
-		if (ctx->smstate->secret == NULL) {
-			irc_rootmsg(irc, "smp: initiating with %s...", u->nick);
-			otrl_message_initiate_smp(irc->otr->us, &otr_ops,
-			                          u->bu->ic, ctx, (unsigned char *) secret, strlen(secret));
-			/* smp is now in EXPECT2 */
-		} else {
-			/* if we're still in EXPECT1 but smstate is initialized, we must have
-			   received the SMP1, so let's issue a response */
-			irc_rootmsg(irc, "smp: responding to %s...", u->nick);
-			otrl_message_respond_smp(irc->otr->us, &otr_ops,
-			                         u->bu->ic, ctx, (unsigned char *) secret, strlen(secret));
-			/* smp is now in EXPECT3 */
-		}
-	}
-}
-
-/* timeout handler that calls otrl_message_poll */
-gboolean ev_message_poll(gpointer data, gint fd, b_input_condition cond)
-{
-	otr_t *otr = data;
-
-	if (otr && otr->us) {
-		otrl_message_poll(otr->us, &otr_ops, NULL);
-	}
-
-	return TRUE;    /* cycle timer */
-}
-
-/* helper to assert that account and protocol names given to ops below always
-   match the im_connection passed through as opdata */
-struct im_connection *check_imc(void *opdata, const char *accountname,
-                                const char *protocol)
-{
-	struct im_connection *ic = (struct im_connection *) opdata;
-
-	/* libotr 4.0.0 has a bug where it doesn't set opdata, so we catch
-	 * that and try to find the desired connection in the global list. */
-	if (!ic) {
-		GSList *l;
-		for (l = get_connections(); l; l = l->next) {
-			ic = l->data;
-			if (strcmp(accountname, ic->acc->user) == 0 &&
-			    strcmp(protocol, ic->acc->prpl->name) == 0) {
-				break;
-			}
-		}
-		g_return_val_if_fail(l, NULL);
-		if (!l) {
-			return NULL;
-		}
-	}
-
-	if (strcmp(accountname, ic->acc->user) != 0) {
-		log_message(LOGLVL_WARNING,
-		            "otr: internal account name mismatch: '%s' vs '%s'",
-		            accountname, ic->acc->user);
-	}
-	if (strcmp(protocol, ic->acc->prpl->name) != 0) {
-		log_message(LOGLVL_WARNING,
-		            "otr: internal protocol name mismatch: '%s' vs '%s'",
-		            protocol, ic->acc->prpl->name);
-	}
-
-	return ic;
-}
-
-irc_user_t *peeruser(irc_t *irc, const char *handle, const char *protocol)
-{
-	GSList *l;
-
-	for (l = irc->b->users; l; l = l->next) {
-		bee_user_t *bu = l->data;
-		struct prpl *prpl;
-		if (!bu->ui_data || !bu->ic || !bu->handle) {
-			continue;
-		}
-		prpl = bu->ic->acc->prpl;
-		if (strcmp(prpl->name, protocol) == 0
-		    && prpl->handle_cmp(bu->handle, handle) == 0) {
-			return bu->ui_data;
-		}
-	}
-
-	return NULL;
-}
-
-int hexval(char a)
-{
-	int x = g_ascii_tolower(a);
-
-	if (x >= 'a' && x <= 'f') {
-		x = x - 'a' + 10;
-	} else if (x >= '0' && x <= '9') {
-		x = x - '0';
-	} else {
-		return -1;
-	}
-
-	return x;
-}
-
-const char *peernick(irc_t *irc, const char *handle, const char *protocol)
-{
-	static char fallback[512];
-
-	irc_user_t *u = peeruser(irc, handle, protocol);
-
-	if (u) {
-		return u->nick;
-	} else {
-		g_snprintf(fallback, 511, "%s/%s", handle, protocol);
-		return fallback;
-	}
-}
-
-void otr_update_uflags(ConnContext *context, irc_user_t *u)
-{
-	const char *trust;
-
-	if (context->active_fingerprint) {
-		u->flags |= IRC_USER_OTR_ENCRYPTED;
-
-		trust = context->active_fingerprint->trust;
-		if (trust && trust[0]) {
-			u->flags |= IRC_USER_OTR_TRUSTED;
-		} else {
-			u->flags &= ~IRC_USER_OTR_TRUSTED;
-		}
-	} else {
-		u->flags &= ~IRC_USER_OTR_ENCRYPTED;
-	}
-}
-
-int otr_update_modeflags(irc_t *irc, irc_user_t *u)
-{
-	return 0;
-}
-
-void show_fingerprints(irc_t *irc, ConnContext *ctx)
-{
-	char human[45];
-	Fingerprint *fp;
-	const char *trust;
-	int count = 0;
-
-	/* Is this a subcontext? If so, only list the active fingerprint */
-	if (ctx->m_context != ctx) {
-		fp = ctx->active_fingerprint;
-	} else {
-		fp = &ctx->fingerprint_root;
-	}
-
-	while (fp) {
-		if (!fp->fingerprint) {
-			fp = fp->next;
-			continue;
-		}
-		count++;
-		otrl_privkey_hash_to_human(human, fp->fingerprint);
-		if (!fp->trust || fp->trust[0] == '\0') {
-			trust = "untrusted";
-		} else {
-			trust = fp->trust;
-		}
-		if (fp == ctx->active_fingerprint) {
-			irc_rootmsg(irc, "      \x02%s (%s)\x02", human, trust);
-		} else {
-			irc_rootmsg(irc, "      %s (%s)", human, trust);
-		}
-
-		/* Break if this is a subcontext - we only print active fp */
-		if (ctx->m_context != ctx) {
-			break;
-		}
-		fp = fp->next;
-	}
-	if (count == 0) {
-		irc_rootmsg(irc, "      (none)");
-	}
-}
-
-Fingerprint *match_fingerprint(irc_t *irc, ConnContext *ctx, const char **args)
-{
-	Fingerprint *fp, *fp2;
-	char human[45];
-	char prefix[45], *p;
-	int n;
-	int i, j;
-
-	/* assemble the args into a prefix in standard "human" form */
-	n = 0;
-	p = prefix;
-	for (i = 0; args[i]; i++) {
-		for (j = 0; args[i][j]; j++) {
-			char c = g_ascii_toupper(args[i][j]);
-
-			if (n >= 40) {
-				irc_rootmsg(irc, "too many fingerprint digits given, expected at most 40");
-				return NULL;
-			}
-
-			if ((c >= 'A' && c <= 'F') || (c >= '0' && c <= '9')) {
-				*(p++) = c;
-			} else {
-				irc_rootmsg(irc, "invalid hex digit '%c' in block %d", args[i][j], i + 1);
-				return NULL;
-			}
-
-			n++;
-			if (n % 8 == 0) {
-				*(p++) = ' ';
-			}
-		}
-	}
-	*p = '\0';
-
-	/* find first fingerprint with the given prefix */
-	n = strlen(prefix);
-	for (fp = &ctx->fingerprint_root; fp; fp = fp->next) {
-		if (!fp->fingerprint) {
-			continue;
-		}
-		otrl_privkey_hash_to_human(human, fp->fingerprint);
-		if (!strncmp(prefix, human, n)) {
-			break;
-		}
-	}
-	if (!fp) {
-		irc_rootmsg(irc, "%s: no match", prefix);
-		return NULL;
-	}
-
-	/* make sure the match, if any, is unique */
-	for (fp2 = fp->next; fp2; fp2 = fp2->next) {
-		if (!fp2->fingerprint) {
-			continue;
-		}
-		otrl_privkey_hash_to_human(human, fp2->fingerprint);
-		if (!strncmp(prefix, human, n)) {
-			break;
-		}
-	}
-	if (fp2) {
-		irc_rootmsg(irc, "%s: multiple matches", prefix);
-		return NULL;
-	}
-
-	return fp;
-}
-
-OtrlPrivKey *match_privkey(irc_t *irc, const char **args)
-{
-	OtrlPrivKey *k, *k2;
-	char human[45];
-	char prefix[45], *p;
-	int n;
-	int i, j;
-
-	/* assemble the args into a prefix in standard "human" form */
-	n = 0;
-	p = prefix;
-	for (i = 0; args[i]; i++) {
-		for (j = 0; args[i][j]; j++) {
-			char c = g_ascii_toupper(args[i][j]);
-
-			if (n >= 40) {
-				irc_rootmsg(irc, "too many fingerprint digits given, expected at most 40");
-				return NULL;
-			}
-
-			if ((c >= 'A' && c <= 'F') || (c >= '0' && c <= '9')) {
-				*(p++) = c;
-			} else {
-				irc_rootmsg(irc, "invalid hex digit '%c' in block %d", args[i][j], i + 1);
-				return NULL;
-			}
-
-			n++;
-			if (n % 8 == 0) {
-				*(p++) = ' ';
-			}
-		}
-	}
-	*p = '\0';
-
-	/* remove trailing whitespace */
-	g_strchomp(prefix);
-
-	/* find first key which matches the given prefix */
-	n = strlen(prefix);
-	for (k = irc->otr->us->privkey_root; k; k = k->next) {
-		p = otrl_privkey_fingerprint(irc->otr->us, human, k->accountname, k->protocol);
-		if (!p) { /* gah! :-P */
-			continue;
-		}
-		if (!strncmp(prefix, human, n)) {
-			break;
-		}
-	}
-	if (!k) {
-		irc_rootmsg(irc, "%s: no match", prefix);
-		return NULL;
-	}
-
-	/* make sure the match, if any, is unique */
-	for (k2 = k->next; k2; k2 = k2->next) {
-		p = otrl_privkey_fingerprint(irc->otr->us, human, k2->accountname, k2->protocol);
-		if (!p) { /* gah! :-P */
-			continue;
-		}
-		if (!strncmp(prefix, human, n)) {
-			break;
-		}
-	}
-	if (k2) {
-		irc_rootmsg(irc, "%s: multiple matches", prefix);
-		return NULL;
-	}
-
-	return k;
-}
-
-void show_general_otr_info(irc_t *irc)
-{
-	ConnContext *ctx;
-	OtrlPrivKey *key;
-	char human[45];
-	kg_t *kg;
-
-	/* list all privkeys (including ones being generated) */
-	irc_rootmsg(irc, "\x1fprivate keys:\x1f");
-	for (key = irc->otr->us->privkey_root; key; key = key->next) {
-		const char *hash;
-
-		switch (key->pubkey_type) {
-		case OTRL_PUBKEY_TYPE_DSA:
-			irc_rootmsg(irc, "  %s/%s - DSA", key->accountname, key->protocol);
-			break;
-		default:
-			irc_rootmsg(irc, "  %s/%s - type %d", key->accountname, key->protocol,
-			            key->pubkey_type);
-		}
-
-		/* No, it doesn't make much sense to search for the privkey again by
-		   account/protocol, but libotr currently doesn't provide a direct routine
-		   for hashing a given 'OtrlPrivKey'... */
-		hash = otrl_privkey_fingerprint(irc->otr->us, human, key->accountname, key->protocol);
-		if (hash) { /* should always succeed */
-			irc_rootmsg(irc, "    %s", human);
-		}
-	}
-	if (irc->otr->sent_accountname) {
-		irc_rootmsg(irc, "  %s/%s - DSA", irc->otr->sent_accountname,
-		            irc->otr->sent_protocol);
-		irc_rootmsg(irc, "    (being generated)");
-	}
-	for (kg = irc->otr->todo; kg; kg = kg->next) {
-		irc_rootmsg(irc, "  %s/%s - DSA", kg->accountname, kg->protocol);
-		irc_rootmsg(irc, "    (queued)");
-	}
-	if (key == irc->otr->us->privkey_root &&
-	    !irc->otr->sent_accountname &&
-	    kg == irc->otr->todo) {
-		irc_rootmsg(irc, "  (none)");
-	}
-
-	/* list all contexts */
-	/* XXX remove this, or split off as its own command */
-	irc_rootmsg(irc, "%s", "");
-	irc_rootmsg(irc, "\x1f" "connection contexts:\x1f (bold=currently encrypted)");
-
-	ctx = irc->otr->us->context_root;
-	while (ctx) {
-		ConnContext *subctx;
-		irc_user_t *u;
-		char *userstring;
-		char encrypted = 0;
-
-		u = peeruser(irc, ctx->username, ctx->protocol);
-		if (u) {
-			userstring = g_strdup_printf("%s/%s/%s (%s)",
-			                             ctx->username, ctx->protocol, ctx->accountname, u->nick);
-		} else {
-			userstring = g_strdup_printf("%s/%s/%s",
-			                             ctx->username, ctx->protocol, ctx->accountname);
-		}
-
-		subctx = ctx;
-		while (subctx && subctx->m_context == ctx) {
-			if (subctx->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
-				encrypted = 1;
-			}
-			subctx = subctx->next;
-		}
-
-		if(encrypted) {
-			irc_rootmsg(irc, "  \x02%s\x02", userstring);
-		} else {
-			irc_rootmsg(irc, "  %s", userstring);
-		}
-
-		/* Skip subcontexts/instances from output */
-		ctx = subctx;
-
-		g_free(userstring);
-	}
-
-	if (ctx == irc->otr->us->context_root) {
-		irc_rootmsg(irc, "  (none)");
-	}
-}
-
-void show_otr_context_info(irc_t *irc, ConnContext *ctx, ConnContext *bestctx)
-{
-	ConnContext *subctx;
-	int instcount = 0;
-
-	subctx = ctx;
-	while (subctx && subctx->m_context == ctx) {
-		if (subctx->m_context == subctx) {
-			if (subctx == bestctx) {
-				irc_rootmsg(irc, "  \x02master context (target):\x02");
-			} else {
-				irc_rootmsg(irc, "  master context:");
-			}
-			irc_rootmsg(irc, "    known fingerprints (bold = active for v1 or v2):");
-		} else {
-			if (subctx == bestctx) {
-				irc_rootmsg(irc, "  \x02instance %d (target):\x02", instcount);
-			} else {
-				irc_rootmsg(irc, "  instance %d:", instcount);
-			}
-			irc_rootmsg(irc, "    active fingerprint:");
-			instcount++;
-		}
-
-		show_fingerprints(irc, subctx);
-
-		switch (subctx->msgstate) {
-		case OTRL_MSGSTATE_PLAINTEXT:
-			irc_rootmsg(irc, "    connection state: cleartext");
-			break;
-		case OTRL_MSGSTATE_ENCRYPTED:
-			irc_rootmsg(irc, "    connection state: encrypted (v%d)", subctx->protocol_version);
-			break;
-		case OTRL_MSGSTATE_FINISHED:
-			irc_rootmsg(irc, "    connection state: shut down");
-			break;
-		default:
-			irc_rootmsg(irc, "    connection state: %d", subctx->msgstate);
-		}
-
-		subctx = subctx->next;
-	}
-}
-
-int keygen_in_progress(irc_t *irc, const char *handle, const char *protocol)
-{
-	kg_t *kg;
-
-	if (!irc->otr->sent_accountname || !irc->otr->sent_protocol) {
-		return 0;
-	}
-
-	/* are we currently working on this key? */
-	if (!strcmp(handle, irc->otr->sent_accountname) &&
-	    !strcmp(protocol, irc->otr->sent_protocol)) {
-		return 1;
-	}
-
-	/* do we have it queued for later? */
-	for (kg = irc->otr->todo; kg; kg = kg->next) {
-		if (!strcmp(handle, kg->accountname) &&
-		    !strcmp(protocol, kg->protocol)) {
-			return 1;
-		}
-	}
-
-	return 0;
-}
-
-void otr_keygen(irc_t *irc, const char *handle, const char *protocol)
-{
-	/* do nothing if a key for the requested account is already being generated */
-	if (keygen_in_progress(irc, handle, protocol)) {
-		return;
-	}
-
-	/* see if we already have a keygen child running. if not, start one and put a
-	   handler on its output. */
-	if (!irc->otr->keygen || waitpid(irc->otr->keygen, NULL, WNOHANG)) {
-		pid_t p;
-		int to[2], from[2];
-		FILE *tof, *fromf;
-
-		if (pipe(to) < 0 || pipe(from) < 0) {
-			irc_rootmsg(irc, "otr keygen: couldn't create pipe: %s", strerror(errno));
-			return;
-		}
-
-		tof = fdopen(to[1], "w");
-		fromf = fdopen(from[0], "r");
-		if (!tof || !fromf) {
-			irc_rootmsg(irc, "otr keygen: couldn't streamify pipe: %s", strerror(errno));
-			return;
-		}
-
-		p = fork();
-		if (p < 0) {
-			irc_rootmsg(irc, "otr keygen: couldn't fork: %s", strerror(errno));
-			return;
-		}
-
-		if (!p) {
-			/* child process */
-			signal(SIGTERM, exit);
-			keygen_child_main(irc->otr->us, to[0], from[1]);
-			exit(0);
-		}
-
-		irc->otr->keygen = p;
-		irc->otr->to = tof;
-		irc->otr->from = fromf;
-		irc->otr->sent_accountname = NULL;
-		irc->otr->sent_protocol = NULL;
-		irc->otr->todo = NULL;
-		b_input_add(from[0], B_EV_IO_READ, keygen_finish_handler, irc);
-	}
-
-	/* is the keygen slave currently working? */
-	if (irc->otr->sent_accountname) {
-		/* enqueue our job for later transmission */
-		kg_t **kg = &irc->otr->todo;
-		while (*kg) {
-			kg = &((*kg)->next);
-		}
-		*kg = g_new0(kg_t, 1);
-		(*kg)->accountname = g_strdup(handle);
-		(*kg)->protocol = g_strdup(protocol);
-	} else {
-		/* send our job over and remember it */
-		fprintf(irc->otr->to, "%s\n%s\n", handle, protocol);
-		fflush(irc->otr->to);
-		irc->otr->sent_accountname = g_strdup(handle);
-		irc->otr->sent_protocol = g_strdup(protocol);
-	}
-}
-
-void keygen_child_main(OtrlUserState us, int infd, int outfd)
-{
-	FILE *input, *output;
-	char filename[128], accountname[512], protocol[512];
-	gcry_error_t e;
-	int tempfd;
-
-	input = fdopen(infd, "r");
-	output = fdopen(outfd, "w");
-
-	while (!feof(input) && !ferror(input) && !feof(output) && !ferror(output)) {
-		myfgets(accountname, 512, input);
-		myfgets(protocol, 512, input);
-
-		strncpy(filename, "/tmp/bitlbee-XXXXXX", 128);
-		tempfd = mkstemp(filename);
-		close(tempfd);
-
-		e = otrl_privkey_generate(us, filename, accountname, protocol);
-		if (e) {
-			fprintf(output, "\n");  /* this means failure */
-			fprintf(output, "otr keygen: %s\n", gcry_strerror(e));
-			unlink(filename);
-		} else {
-			fprintf(output, "%s\n", filename);
-			fprintf(output, "otr keygen for %s/%s complete\n", accountname, protocol);
-		}
-		fflush(output);
-	}
-
-	fclose(input);
-	fclose(output);
-}
-
-gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond)
-{
-	irc_t *irc = (irc_t *) data;
-	char filename[512], msg[512];
-
-	myfgets(filename, 512, irc->otr->from);
-	myfgets(msg, 512, irc->otr->from);
-
-	irc_rootmsg(irc, "%s", msg);
-	if (filename[0]) {
-		if (strsane(irc->user->nick)) {
-			char *kf = g_strdup_printf("%s%s.otr_keys", global.conf->configdir, irc->user->nick);
-			char *tmp = g_strdup_printf("%s.new", kf);
-			copyfile(filename, tmp);
-			unlink(filename);
-			rename(tmp, kf);
-			otrl_privkey_read(irc->otr->us, kf);
-			g_free(kf);
-			g_free(tmp);
-		} else {
-			otrl_privkey_read(irc->otr->us, filename);
-			unlink(filename);
-		}
-	}
-
-	/* forget this job */
-	g_free(irc->otr->sent_accountname);
-	g_free(irc->otr->sent_protocol);
-	irc->otr->sent_accountname = NULL;
-	irc->otr->sent_protocol = NULL;
-
-	/* see if there are any more in the queue */
-	if (irc->otr->todo) {
-		kg_t *p = irc->otr->todo;
-		/* send the next one over */
-		fprintf(irc->otr->to, "%s\n%s\n", p->accountname, p->protocol);
-		fflush(irc->otr->to);
-		irc->otr->sent_accountname = p->accountname;
-		irc->otr->sent_protocol = p->protocol;
-		irc->otr->todo = p->next;
-		g_free(p);
-		return TRUE;   /* keep watching */
-	} else {
-		/* okay, the slave is idle now, so kill him */
-		fclose(irc->otr->from);
-		fclose(irc->otr->to);
-		irc->otr->from = irc->otr->to = NULL;
-		kill(irc->otr->keygen, SIGTERM);
-		waitpid(irc->otr->keygen, NULL, 0);
-		irc->otr->keygen = 0;
-		return FALSE;  /* unregister ourselves */
-	}
-}
-
-void copyfile(const char *a, const char *b)
-{
-	int fda, fdb;
-	int n;
-	char buf[1024];
-
-	fda = open(a, O_RDONLY);
-	fdb = open(b, O_WRONLY | O_CREAT | O_TRUNC, 0600);
-
-	while ((n = read(fda, buf, 1024)) > 0) {
-		write(fdb, buf, n);
-	}
-
-	close(fda);
-	close(fdb);
-}
-
-void myfgets(char *s, int size, FILE *stream)
-{
-	if (!fgets(s, size, stream)) {
-		s[0] = '\0';
-	} else {
-		int n = strlen(s);
-		if (n > 0 && s[n - 1] == '\n') {
-			s[n - 1] = '\0';
-		}
-	}
-}
-
-void yes_keygen(void *data)
-{
-	account_t *acc = (account_t *) data;
-	irc_t *irc = acc->bee->ui_data;
-
-	if (keygen_in_progress(irc, acc->user, acc->prpl->name)) {
-		irc_rootmsg(irc, "keygen for %s/%s already in progress",
-		            acc->user, acc->prpl->name);
-	} else {
-		irc_rootmsg(irc, "starting background keygen for %s/%s",
-		            acc->user, acc->prpl->name);
-		irc_rootmsg(irc, "you will be notified when it completes");
-		otr_keygen(irc, acc->user, acc->prpl->name);
-	}
-}
-
-/* check whether a string is safe to use in a path component */
-int strsane(const char *s)
-{
-	return strpbrk(s, "/\\") == NULL;
-}
-
-/* close the OTR connection with the given buddy */
-gboolean otr_disconnect_user(irc_t *irc, irc_user_t *u)
-{
-	if (!u || !u->bu || !u->bu->ic) {
-		return FALSE;
-	}
-
-	/* XXX we disconnect all instances; is that what we want? */
-	otrl_message_disconnect_all_instances(irc->otr->us, &otr_ops,
-	                                      u->bu->ic, u->bu->ic->acc->user, u->bu->ic->acc->prpl->name,
-	                                      u->bu->handle);
-
-	u->flags &= ~IRC_USER_OTR_TRUSTED;
-	u->flags &= ~IRC_USER_OTR_ENCRYPTED;
-	otr_update_modeflags(irc, u);
-
-	return TRUE;
-}
-
-/* close all active OTR connections */
-void otr_disconnect_all(irc_t *irc)
-{
-	irc_user_t *u;
-	ConnContext *ctx;
-
-	for (ctx = irc->otr->us->context_root; ctx; ctx = ctx->next) {
-		if (ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
-			u = peeruser(irc, ctx->username, ctx->protocol);
-			(void) otr_disconnect_user(irc, u);
-		}
-	}
-}
Index: r.h
===================================================================
--- otr.h	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,85 +1,0 @@
-/********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2008 Wilmer van der Gaast and others                *
-  \********************************************************************/
-
-/*
-  OTR support (cf. http://www.cypherpunks.ca/otr/)
-
-  (c) 2008,2013 Sven Moritz Hallberg <pesco@khjk.org>
-  funded by stonedcoder.org
-*/
-
-/*
-  This program is free software; you can 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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#ifndef BITLBEE_PROTOCOLS_OTR_H
-#define BITLBEE_PROTOCOLS_OTR_H
-
-#include "bitlbee.h"
-
-
-// forward decls to avoid mutual dependencies
-struct irc;
-struct im_connection;
-struct account;
-
-
-#include <libotr/proto.h>
-#include <libotr/message.h>
-#include <libotr/privkey.h>
-
-/* representing a keygen job */
-typedef struct kg {
-	char *accountname;
-	char *protocol;
-
-	struct kg *next;
-} kg_t;
-
-/* struct to encapsulate our book keeping stuff */
-typedef struct otr {
-	OtrlUserState us;
-	pid_t keygen;    /* pid of keygen slave (0 if none) */
-	FILE *to;        /* pipe to keygen slave */
-	FILE *from;      /* pipe from keygen slave */
-
-	/* active keygen job (NULL if none) */
-	char *sent_accountname;
-	char *sent_protocol;
-
-	/* keygen jobs waiting to be sent to slave */
-	kg_t *todo;
-
-	/* event timer for otrl_message_poll */
-	gint timer;
-} otr_t;
-
-/* called from main() */
-void otr_init(void);
-
-/* called by storage_* functions */
-void otr_load(struct irc *irc);
-void otr_save(struct irc *irc);
-void otr_remove(const char *nick);
-void otr_rename(const char *onick, const char *nnick);
-
-/* called from account_add() */
-int otr_check_for_key(struct account *a);
-
-#endif
Index: protocols/Makefile
===================================================================
--- protocols/Makefile	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ protocols/Makefile	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -8,11 +8,7 @@
 
 -include ../Makefile.settings
-ifdef _SRCDIR_
-_SRCDIR_ := $(_SRCDIR_)protocols/
-endif
 
 # [SH] Program variables
-objects = account.o bee.o bee_chat.o bee_ft.o bee_user.o nogaim.o
-
+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))
@@ -25,12 +21,9 @@
 # Expansion of variables
 subdirobjs := $(join $(subdirs),$(addprefix /,$(subdirobjs)))
+CFLAGS += -Wall
 LFLAGS += -r
 
 # [SH] Phony targets
 all: protocols.o
-check: all
-lcov: check
-gcov:
-	gcov *.c
 
 .PHONY: all clean distclean $(subdirs)
@@ -40,8 +33,7 @@
 
 distclean: clean $(subdirs)
-	rm -rf .depend
 
 $(subdirs):
-	$(MAKE) -C $@ $(MAKECMDGOALS)
+	@$(MAKE) -C $@ $(MAKECMDGOALS)
 
 ### MAIN PROGRAM
@@ -49,11 +41,9 @@
 protocols.o: $(objects) $(subdirs)
 	@echo '*' Linking protocols.o
-	$(VERBOSE) $(LD) $(LFLAGS) $(objects) $(subdirobjs) -o protocols.o
+	@$(LD) $(LFLAGS) $(objects) $(subdirobjs) -o protocols.o
 
 $(objects): ../Makefile.settings Makefile
 
-$(objects): %.o: $(_SRCDIR_)%.c
+$(objects): %.o: %.c
 	@echo '*' Compiling $<
-	$(VERBOSE) $(CC) -c $(CFLAGS) $(CFLAGS_BITLBEE) $< -o $@
-
--include .depend/*.d
+	@$(CC) -c $(CFLAGS) $< -o $@
Index: otocols/account.c
===================================================================
--- protocols/account.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,476 +1,0 @@
-/********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2013 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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#define BITLBEE_CORE
-#include "bitlbee.h"
-#include "account.h"
-
-static const char* account_protocols_local[] = {
-	"gg", "whatsapp", NULL
-};
-
-static char *set_eval_nick_source(set_t *set, char *value);
-
-account_t *account_add(bee_t *bee, struct prpl *prpl, char *user, char *pass)
-{
-	account_t *a;
-	set_t *s;
-	char tag[strlen(prpl->name) + 10];
-
-	if (bee->accounts) {
-		for (a = bee->accounts; a->next; a = a->next) {
-			;
-		}
-		a = a->next = g_new0(account_t, 1);
-	} else {
-		bee->accounts = a = g_new0(account_t, 1);
-	}
-
-	a->prpl = prpl;
-	a->user = g_strdup(user);
-	a->pass = g_strdup(pass);
-	a->auto_connect = 1;
-	a->bee = bee;
-
-	s = set_add(&a->set, "auto_connect", "true", set_eval_account, a);
-	s->flags |= SET_NOSAVE;
-
-	s = set_add(&a->set, "auto_reconnect", "true", set_eval_bool, a);
-
-	s = set_add(&a->set, "handle_unknown", NULL, NULL, a);
-	s->flags |= SET_NULL_OK;
-
-	s = set_add(&a->set, "nick_format", NULL, NULL, a);
-	s->flags |= SET_NULL_OK;
-
-	s = set_add(&a->set, "nick_source", "handle", set_eval_nick_source, a);
-	s->flags |= SET_NOSAVE; /* Just for bw compatibility! */
-
-	s = set_add(&a->set, "password", NULL, set_eval_account, a);
-	s->flags |= SET_NOSAVE | SET_NULL_OK | SET_PASSWORD | ACC_SET_LOCKABLE;
-
-	s = set_add(&a->set, "tag", NULL, set_eval_account, a);
-	s->flags |= SET_NOSAVE;
-
-	s = set_add(&a->set, "username", NULL, set_eval_account, a);
-	s->flags |= SET_NOSAVE | ACC_SET_OFFLINE_ONLY | ACC_SET_LOCKABLE;
-	set_setstr(&a->set, "username", user);
-
-	if (prpl == &protocol_missing) {
-		s = set_add(&a->set, "server", NULL, set_eval_account, a);
-		s->flags |= SET_NOSAVE | SET_HIDDEN | ACC_SET_OFFLINE_ONLY | ACC_SET_ONLINE_ONLY;
-	}
-
-	/* Hardcode some more clever tag guesses. */
-	strcpy(tag, prpl->name);
-	if (strcmp(prpl->name, "jabber") == 0) {
-		if (strstr(a->user, "@gmail.com") ||
-		    strstr(a->user, "@googlemail.com")) {
-			strcpy(tag, "gtalk");
-		}
-	}
-
-	if (account_by_tag(bee, tag)) {
-		char *numpos = tag + strlen(tag);
-		int i;
-
-		for (i = 2; i < 10000; i++) {
-			sprintf(numpos, "%d", i);
-			if (!account_by_tag(bee, tag)) {
-				break;
-			}
-		}
-	}
-	set_setstr(&a->set, "tag", tag);
-
-	a->nicks = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
-
-	/* This function adds some more settings (and might want to do more
-	   things that have to be done now, although I can't think of anything. */
-	if (prpl->init) {
-		prpl->init(a);
-	}
-
-	s = set_add(&a->set, "away", NULL, set_eval_account, a);
-	s->flags |= SET_NULL_OK;
-
-	if (a->flags & ACC_FLAG_STATUS_MESSAGE) {
-		s = set_add(&a->set, "status", NULL, set_eval_account, a);
-		s->flags |= SET_NULL_OK;
-	}
-
-	return a;
-}
-
-char *set_eval_account(set_t *set, char *value)
-{
-	account_t *acc = set->data;
-
-	/* Double-check: We refuse to edit on-line accounts. */
-	if (set->flags & ACC_SET_OFFLINE_ONLY && acc->ic) {
-		return SET_INVALID;
-	}
-
-	if (strcmp(set->key, "server") == 0) {
-		g_free(acc->server);
-		if (value && *value) {
-			acc->server = g_strdup(value);
-			return value;
-		} else {
-			acc->server = g_strdup(set->def);
-			return g_strdup(set->def);
-		}
-	} else if (strcmp(set->key, "username") == 0) {
-		g_free(acc->user);
-		acc->user = g_strdup(value);
-		return value;
-	} else if (strcmp(set->key, "password") == 0) {
-		/* set -del allows /oper to be used to change the password or,
-		   iff oauth is enabled, reset the oauth credential magic.
-		*/
-		if (!value) {
-			if (set_getbool(&(acc->set), "oauth")) {
-				value = "";
-			} else {
-				value = PASSWORD_PENDING;
-				((irc_t *) acc->bee->ui_data)->status |= OPER_HACK_ACCOUNT_PASSWORD;
-				irc_rootmsg((irc_t *) acc->bee->ui_data, "You may now use /OPER to set the password");
-			}
-		}
-
-		g_free(acc->pass);
-		acc->pass = g_strdup(value);
-		return NULL;    /* password shouldn't be visible in plaintext! */
-	} else if (strcmp(set->key, "tag") == 0) {
-		account_t *oa;
-
-		/* Enforce uniqueness. */
-		if ((oa = account_by_tag(acc->bee, value)) && oa != acc) {
-			return SET_INVALID;
-		}
-
-		g_free(acc->tag);
-		acc->tag = g_strdup(value);
-		return value;
-	} else if (strcmp(set->key, "auto_connect") == 0) {
-		if (!is_bool(value)) {
-			return SET_INVALID;
-		}
-
-		acc->auto_connect = bool2int(value);
-		return value;
-	} else if (strcmp(set->key, "away") == 0 ||
-	           strcmp(set->key, "status") == 0) {
-		if (acc->ic && acc->ic->flags & OPT_LOGGED_IN) {
-			/* If we're currently on-line, set the var now already
-			   (bit of a hack) and send an update. */
-			g_free(set->value);
-			set->value = g_strdup(value);
-
-			imc_away_send_update(acc->ic);
-		}
-
-		return value;
-	}
-
-	return SET_INVALID;
-}
-
-/* For bw compatibility, have this write-only setting. */
-static char *set_eval_nick_source(set_t *set, char *value)
-{
-	account_t *a = set->data;
-
-	if (strcmp(value, "full_name") == 0) {
-		set_setstr(&a->set, "nick_format", "%full_name");
-	} else if (strcmp(value, "first_name") == 0) {
-		set_setstr(&a->set, "nick_format", "%first_name");
-	} else {
-		set_setstr(&a->set, "nick_format", "%-@nick");
-	}
-
-	return value;
-}
-
-account_t *account_get(bee_t *bee, const char *id)
-{
-	account_t *a, *ret = NULL;
-	char *handle, *s;
-	int nr;
-
-	/* Tags get priority above anything else. */
-	if ((a = account_by_tag(bee, id))) {
-		return a;
-	}
-
-	/* This checks if the id string ends with (...) */
-	if ((handle = strchr(id, '(')) && (s = strchr(handle, ')')) && s[1] == 0) {
-		struct prpl *proto;
-
-		*s = *handle = 0;
-		handle++;
-
-		if ((proto = find_protocol(id))) {
-			for (a = bee->accounts; a; a = a->next) {
-				if (a->prpl == proto &&
-				    a->prpl->handle_cmp(handle, a->user) == 0) {
-					ret = a;
-				}
-			}
-		}
-
-		/* Restore the string. */
-		handle--;
-		*handle = '(';
-		*s = ')';
-
-		if (ret) {
-			return ret;
-		}
-	}
-
-	if (sscanf(id, "%d", &nr) == 1 && nr < 1000) {
-		for (a = bee->accounts; a; a = a->next) {
-			if ((nr--) == 0) {
-				return(a);
-			}
-		}
-
-		return(NULL);
-	}
-
-	for (a = bee->accounts; a; a = a->next) {
-		if (g_strcasecmp(id, a->prpl->name) == 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);
-}
-
-account_t *account_by_tag(bee_t *bee, const char *tag)
-{
-	account_t *a;
-
-	for (a = bee->accounts; a; a = a->next) {
-		if (a->tag && g_strcasecmp(tag, a->tag) == 0) {
-			return a;
-		}
-	}
-
-	return NULL;
-}
-
-void account_del(bee_t *bee, account_t *acc)
-{
-	account_t *a, *l = NULL;
-
-	if (acc->ic) {
-		/* Caller should have checked, accounts still in use can't be deleted. */
-		return;
-	}
-
-	for (a = bee->accounts; a; a = (l = a)->next) {
-		if (a == acc) {
-			if (l) {
-				l->next = a->next;
-			} else {
-				bee->accounts = a->next;
-			}
-
-			/** FIXME
-			for( c = bee->chatrooms; c; c = nc )
-			{
-			        nc = c->next;
-			        if( acc == c->acc )
-			                chat_del( bee, c );
-			}
-			*/
-
-			while (a->set) {
-				set_del(&a->set, a->set->key);
-			}
-
-			g_hash_table_destroy(a->nicks);
-
-			g_free(a->tag);
-			g_free(a->user);
-			g_free(a->pass);
-			g_free(a->server);
-			if (a->reconnect) {     /* This prevents any reconnect still queued to happen */
-				cancel_auto_reconnect(a);
-			}
-			g_free(a);
-
-			break;
-		}
-	}
-}
-
-static gboolean account_on_timeout(gpointer d, gint fd, b_input_condition cond);
-
-void account_on(bee_t *bee, account_t *a)
-{
-	if (a->ic) {
-		/* Trying to enable an already-enabled account */
-		return;
-	}
-
-	cancel_auto_reconnect(a);
-
-	a->reconnect = 0;
-	a->prpl->login(a);
-
-	if (a->ic && !(a->ic->flags & (OPT_SLOW_LOGIN | OPT_LOGGED_IN))) {
-		a->ic->keepalive = b_timeout_add(120000, account_on_timeout, a->ic);
-	}
-}
-
-void account_off(bee_t *bee, account_t *a)
-{
-	imc_logout(a->ic, FALSE);
-	a->ic = NULL;
-	if (a->reconnect) {
-		/* Shouldn't happen */
-		cancel_auto_reconnect(a);
-	}
-}
-
-static gboolean account_on_timeout(gpointer d, gint fd, b_input_condition cond)
-{
-	struct im_connection *ic = d;
-
-	if (!(ic->flags & (OPT_SLOW_LOGIN | OPT_LOGGED_IN))) {
-		imcb_error(ic, "Connection timeout");
-		imc_logout(ic, TRUE);
-	}
-
-	return FALSE;
-}
-
-struct account_reconnect_delay {
-	int start;
-	char op;
-	int step;
-	int max;
-};
-
-int account_reconnect_delay_parse(char *value, struct account_reconnect_delay *p)
-{
-	memset(p, 0, sizeof(*p));
-	/* A whole day seems like a sane "maximum maximum". */
-	p->max = 86400;
-
-	/* Format: /[0-9]+([*+][0-9]+(<[0-9+])?)?/ */
-	while (*value && g_ascii_isdigit(*value)) {
-		p->start = p->start * 10 + *value++ - '0';
-	}
-
-	/* Sure, call me evil for implementing my own fscanf here, but it's
-	   dead simple and I immediately know where to continue parsing. */
-
-	if (*value == 0) {
-		/* If the string ends now, the delay is constant. */
-		return 1;
-	} else if (*value != '+' && *value != '*') {
-		/* Otherwise allow either a + or a * */
-		return 0;
-	}
-
-	p->op = *value++;
-
-	/* + or * the delay by this number every time. */
-	while (*value && g_ascii_isdigit(*value)) {
-		p->step = p->step * 10 + *value++ - '0';
-	}
-
-	if (*value == 0) {
-		/* Use the default maximum (one day). */
-		return 1;
-	} else if (*value != '<') {
-		return 0;
-	}
-
-	p->max = 0;
-	value++;
-	while (*value && g_ascii_isdigit(*value)) {
-		p->max = p->max * 10 + *value++ - '0';
-	}
-
-	return p->max > 0;
-}
-
-char *set_eval_account_reconnect_delay(set_t *set, char *value)
-{
-	struct account_reconnect_delay p;
-
-	return account_reconnect_delay_parse(value, &p) ? value : SET_INVALID;
-}
-
-int account_reconnect_delay(account_t *a)
-{
-	char *setting = set_getstr(&a->bee->set, "auto_reconnect_delay");
-	struct account_reconnect_delay p;
-
-	if (account_reconnect_delay_parse(setting, &p)) {
-		if (a->auto_reconnect_delay == 0) {
-			a->auto_reconnect_delay = p.start;
-		} else if (p.op == '+') {
-			a->auto_reconnect_delay += p.step;
-		} else if (p.op == '*') {
-			a->auto_reconnect_delay *= p.step;
-		}
-
-		if (a->auto_reconnect_delay > p.max) {
-			a->auto_reconnect_delay = p.max;
-		}
-	} else {
-		a->auto_reconnect_delay = 0;
-	}
-
-	return a->auto_reconnect_delay;
-}
-
-int protocol_account_islocal(const char* protocol)
-{
-	const char** p = account_protocols_local;
-
-	do {
-		if (strcmp(*p, protocol) == 0) {
-			return 1;
-		}
-	} while (*(++p));
-	return 0;
-}
Index: otocols/account.h
===================================================================
--- protocols/account.h	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,76 +1,0 @@
-/********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2013 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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#ifndef _ACCOUNT_H
-#define _ACCOUNT_H
-
-typedef struct account {
-	struct prpl *prpl;
-	char *user;
-	char *pass;
-	char *server;
-	char *tag;
-
-	int auto_connect;
-	int auto_reconnect_delay;
-	int reconnect;
-	int flags;
-
-	set_t *set;
-	GHashTable *nicks;
-
-	struct bee *bee;
-	struct im_connection *ic;
-	struct account *next;
-} account_t;
-
-account_t *account_add(bee_t *bee, struct prpl *prpl, char *user, char *pass);
-account_t *account_get(bee_t *bee, const char *id);
-account_t *account_by_tag(bee_t *bee, const char *tag);
-void account_del(bee_t *bee, account_t *acc);
-void account_on(bee_t *bee, account_t *a);
-void account_off(bee_t *bee, account_t *a);
-
-char *set_eval_account(set_t *set, char *value);
-char *set_eval_account_reconnect_delay(set_t *set, char *value);
-int account_reconnect_delay(account_t *a);
-
-int protocol_account_islocal(const char* protocol);
-
-typedef enum {
-	ACC_SET_OFFLINE_ONLY = 0x02,    /* Allow changes only if the acct is offline. */
-	ACC_SET_ONLINE_ONLY = 0x04,     /* Allow changes only if the acct is online. */
-	ACC_SET_LOCKABLE = 0x08         /* Setting cannot be changed if the account is locked down */
-} account_set_flag_t;
-
-typedef enum {
-	ACC_FLAG_AWAY_MESSAGE = 0x01,   /* Supports away messages instead of just states. */
-	ACC_FLAG_STATUS_MESSAGE = 0x02, /* Supports status messages (without being away). */
-	ACC_FLAG_HANDLE_DOMAINS = 0x04, /* Contact handles need a domain portion. */
-	ACC_FLAG_LOCAL = 0x08,          /* Contact list is local. */
-	ACC_FLAG_LOCKED = 0x10,         /* Account is locked (cannot be deleted, certain settings can't changed) */
-} account_flag_t;
-
-#endif
Index: otocols/bee.c
===================================================================
--- protocols/bee.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,98 +1,0 @@
-/********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2010 Wilmer van der Gaast and others                *
-  \********************************************************************/
-
-/* Some IM-core 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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#define BITLBEE_CORE
-#include "bitlbee.h"
-
-static char *set_eval_away_status(set_t *set, char *value);
-
-bee_t *bee_new()
-{
-	bee_t *b = g_new0(bee_t, 1);
-	set_t *s;
-
-	s = set_add(&b->set, "auto_connect", "true", set_eval_bool, b);
-	s = set_add(&b->set, "auto_reconnect", "true", set_eval_bool, b);
-	s = set_add(&b->set, "auto_reconnect_delay", "5*3<900", set_eval_account_reconnect_delay, b);
-	s = set_add(&b->set, "away", NULL, set_eval_away_status, b);
-	s->flags |= SET_NULL_OK | SET_HIDDEN;
-	s = set_add(&b->set, "debug", "false", set_eval_bool, b);
-	s = set_add(&b->set, "mobile_is_away", "false", set_eval_bool, b);
-	s = set_add(&b->set, "save_on_quit", "true", set_eval_bool, b);
-	s = set_add(&b->set, "status", NULL, set_eval_away_status, b);
-	s->flags |= SET_NULL_OK;
-	s = set_add(&b->set, "strip_html", "true", NULL, b);
-
-	b->user = g_malloc(1);
-
-	return b;
-}
-
-void bee_free(bee_t *b)
-{
-	while (b->accounts) {
-		if (b->accounts->ic) {
-			imc_logout(b->accounts->ic, FALSE);
-		} else if (b->accounts->reconnect) {
-			cancel_auto_reconnect(b->accounts);
-		}
-
-		if (b->accounts->ic == NULL) {
-			account_del(b, b->accounts);
-		} else {
-			/* Nasty hack, but account_del() doesn't work in this
-			   case and we don't want infinite loops, do we? ;-) */
-			b->accounts = b->accounts->next;
-		}
-	}
-
-	while (b->set) {
-		set_del(&b->set, b->set->key);
-	}
-
-	bee_group_free(b);
-
-	g_free(b->user);
-	g_free(b);
-}
-
-static char *set_eval_away_status(set_t *set, char *value)
-{
-	bee_t *bee = set->data;
-	account_t *a;
-
-	g_free(set->value);
-	set->value = g_strdup(value);
-
-	for (a = bee->accounts; a; a = a->next) {
-		struct im_connection *ic = a->ic;
-
-		if (ic && ic->flags & OPT_LOGGED_IN) {
-			imc_away_send_update(ic);
-		}
-	}
-
-	return value;
-}
Index: otocols/bee.h
===================================================================
--- protocols/bee.h	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,196 +1,0 @@
-/********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2010 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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#ifndef __BEE_H__
-#define __BEE_H__
-
-struct bee_ui_funcs;
-struct groupchat;
-
-typedef struct bee {
-	/* Settings. See set.h for how these work. The UI can add its
-	   own settings here. */
-	struct set *set;
-
-	GSList *users;  /* struct bee_user */
-	GSList *groups; /* struct bee_group */
-	struct account *accounts; /* TODO(wilmer): Use GSList here too? */
-
-	/* Symbolic, to refer to the local user (who has no real bee_user
-	   object). Not to be used by anything except so far imcb_chat_add/
-	   remove_buddy(). */
-	struct bee_user *user;
-
-	/* Fill in the callbacks for events you care about. */
-	const struct bee_ui_funcs *ui;
-
-	/* And this one will be passed to every callback for any state the
-	   UI may want to keep. */
-	void *ui_data;
-} bee_t;
-
-bee_t *bee_new();
-void bee_free(bee_t *b);
-
-/* TODO(wilmer): Kill at least the OPT_ flags that have an equivalent here. */
-typedef enum {
-	BEE_USER_ONLINE = 1,    /* Compatibility with old OPT_LOGGED_IN flag */
-	BEE_USER_AWAY = 4,      /* Compatibility with old OPT_AWAY flag */
-	BEE_USER_MOBILE = 8,    /* Compatibility with old OPT_MOBILE flag */
-	BEE_USER_LOCAL = 256,   /* Locally-added contacts (not in real contact list) */
-	BEE_USER_SPECIAL = 512, /* Denotes a user as being special */
-	BEE_USER_NOOTR = 4096,  /* Per-user version of OPT_NOOTR */
-} bee_user_flags_t;
-
-typedef struct bee_user {
-	struct im_connection *ic;
-	char *handle;
-	char *fullname;
-	char *nick;
-	struct bee_group *group;
-
-	bee_user_flags_t flags;
-	char *status;     /* NULL means available, anything else is an away state. */
-	char *status_msg; /* Status and/or away message. */
-
-	/* Set using imcb_buddy_times(). */
-	time_t login_time, idle_time;
-
-	bee_t *bee;
-	void *ui_data;
-	void *data; /* Can be used by the IM module. */
-} bee_user_t;
-
-typedef struct bee_chat_info {
-	char *title;
-	char *topic;
-} bee_chat_info_t;
-
-/* This one's mostly used so save space and make it easier (cheaper) to
-   compare groups of contacts, etc. */
-typedef struct bee_group {
-	char *key;  /* Lower case version of the name. */
-	char *name;
-} bee_group_t;
-
-typedef struct bee_ui_funcs {
-	void (*imc_connected)(struct im_connection *ic);
-	void (*imc_disconnected)(struct im_connection *ic);
-
-	gboolean (*user_new)(bee_t *bee, struct bee_user *bu);
-	gboolean (*user_free)(bee_t *bee, struct bee_user *bu);
-	/* Set the fullname first, then call this one to notify the UI. */
-	gboolean (*user_fullname)(bee_t *bee, bee_user_t *bu);
-	gboolean (*user_nick_hint)(bee_t *bee, bee_user_t *bu, const char *hint);
-	/* Notify the UI when an existing user is moved between groups. */
-	gboolean (*user_group)(bee_t *bee, bee_user_t *bu);
-	/* State info is already updated, old is provided in case the UI needs a diff. */
-	gboolean (*user_status)(bee_t *bee, struct bee_user *bu, struct bee_user *old);
-	/* On every incoming message. sent_at = 0 means unknown. */
-	gboolean (*user_msg)(bee_t *bee, bee_user_t *bu, const char *msg, guint32 flags, time_t sent_at);
-	/* Flags currently defined (OPT_TYPING/THINKING) in nogaim.h. */
-	gboolean (*user_typing)(bee_t *bee, bee_user_t *bu, guint32 flags);
-	/* CTCP-like stuff (buddy action) response */
-	gboolean (*user_action_response)(bee_t *bee, bee_user_t *bu, const char *action, char * const args[],
-	                                 void *data);
-
-	/* Called at creation time. Don't show to the user until s/he is
-	   added using chat_add_user().  UI state can be stored via c->data. */
-	gboolean (*chat_new)(bee_t *bee, struct groupchat *c);
-	gboolean (*chat_free)(bee_t *bee, struct groupchat *c);
-	/* System messages of any kind. */
-	gboolean (*chat_log)(bee_t *bee, struct groupchat *c, const char *text);
-	gboolean (*chat_msg)(bee_t *bee, struct groupchat *c, bee_user_t *bu, const char *msg, guint32 flags, time_t sent_at);
-	gboolean (*chat_add_user)(bee_t *bee, struct groupchat *c, bee_user_t *bu);
-	gboolean (*chat_remove_user)(bee_t *bee, struct groupchat *c, bee_user_t *bu, const char *reason);
-	gboolean (*chat_topic)(bee_t *bee, struct groupchat *c, const char *new_topic, bee_user_t *bu);
-	gboolean (*chat_name_hint)(bee_t *bee, struct groupchat *c, const char *name);
-	gboolean (*chat_invite)(bee_t *bee, bee_user_t *bu, const char *name, const char *msg);
-
-	struct file_transfer* (*ft_in_start)(bee_t * bee, bee_user_t * bu, const char *file_name, size_t file_size);
-	gboolean (*ft_out_start)(struct im_connection *ic, struct file_transfer *ft);
-	void (*ft_close)(struct im_connection *ic, struct file_transfer *ft);
-	void (*ft_finished)(struct im_connection *ic, struct file_transfer *ft);
-
-	void (*log)(bee_t *bee, const char *tag, const char *msg);
-	gboolean (*user_nick_change)(bee_t *bee, bee_user_t *bu, const char *hint);
-} bee_ui_funcs_t;
-
-
-/* bee.c */
-bee_t *bee_new();
-void bee_free(bee_t *b);
-
-/* bee_user.c */
-bee_user_t *bee_user_new(bee_t *bee, struct im_connection *ic, const char *handle, bee_user_flags_t flags);
-int bee_user_free(bee_t *bee, bee_user_t *bu);
-bee_user_t *bee_user_by_handle(bee_t *bee, struct im_connection *ic, const char *handle);
-int bee_user_msg(bee_t *bee, bee_user_t *bu, const char *msg, int flags);
-bee_group_t *bee_group_by_name(bee_t *bee, const char *name, gboolean creat);
-void bee_group_free(bee_t *bee);
-
-/* Callbacks from IM modules to core: */
-/* Buddy activity */
-/* To manipulate the status of a handle.
- * - flags can be |='d with OPT_* constants. You will need at least:
- *   OPT_LOGGED_IN and OPT_AWAY.
- * - 'state' and 'message' can be NULL */
-G_MODULE_EXPORT void imcb_buddy_status(struct im_connection *ic, const char *handle, int flags, const char *state,
-                                       const char *message);
-G_MODULE_EXPORT void imcb_buddy_status_msg(struct im_connection *ic, const char *handle, const char *message);
-G_MODULE_EXPORT void imcb_buddy_times(struct im_connection *ic, const char *handle, time_t login, time_t idle);
-/* Call when a handle says something. 'flags' and 'sent_at may be just 0. */
-G_MODULE_EXPORT void imcb_buddy_msg(struct im_connection *ic, const char *handle, const char *msg, guint32 flags,
-                                    time_t sent_at);
-G_MODULE_EXPORT void imcb_notify_email(struct im_connection *ic, char *format, ...) G_GNUC_PRINTF(2, 3);
-
-/* bee_chat.c */
-/* These two functions are to create a group chat.
- * - imcb_chat_new(): the 'handle' parameter identifies the chat, like the
- *   channel name on IRC.
- * - After you have a groupchat pointer, you should add the handles, finally
- *   the user her/himself. At that point the group chat will be visible to the
- *   user, too. */
-G_MODULE_EXPORT struct groupchat *imcb_chat_new(struct im_connection *ic, const char *handle);
-G_MODULE_EXPORT void imcb_chat_name_hint(struct groupchat *c, const char *name);
-G_MODULE_EXPORT void imcb_chat_free(struct groupchat *c);
-/* To tell BitlBee 'who' said 'msg' in 'c'. 'flags' and 'sent_at' can be 0. */
-G_MODULE_EXPORT void imcb_chat_msg(struct groupchat *c, const char *who, char *msg, guint32 flags, time_t sent_at);
-/* System messages specific to a groupchat, so they can be displayed in the right context. */
-G_MODULE_EXPORT void imcb_chat_log(struct groupchat *c, char *format, ...);
-/* To tell BitlBee 'who' changed the topic of 'c' to 'topic'. */
-G_MODULE_EXPORT void imcb_chat_topic(struct groupchat *c, char *who, char *topic, time_t set_at);
-G_MODULE_EXPORT void imcb_chat_add_buddy(struct groupchat *c, const char *handle);
-/* To remove a handle from a group chat. Reason can be NULL. */
-G_MODULE_EXPORT void imcb_chat_remove_buddy(struct groupchat *c, const char *handle, const char *reason);
-G_MODULE_EXPORT int bee_chat_msg(bee_t *bee, struct groupchat *c, const char *msg, int flags);
-G_MODULE_EXPORT struct groupchat *bee_chat_by_title(bee_t *bee, struct im_connection *ic, const char *title);
-G_MODULE_EXPORT void imcb_chat_invite(struct im_connection *ic, const char *name, const char *who, const char *msg);
-
-G_GNUC_DEPRECATED G_MODULE_EXPORT void bee_chat_list_finish(struct im_connection *ic);
-G_MODULE_EXPORT void imcb_chat_list_finish(struct im_connection *ic);
-G_MODULE_EXPORT void imcb_chat_list_free(struct im_connection *ic);
-
-#endif /* __BEE_H__ */
Index: otocols/bee_chat.c
===================================================================
--- protocols/bee_chat.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,303 +1,0 @@
-/********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2010 Wilmer van der Gaast and others                *
-  \********************************************************************/
-
-/* Stuff to handle rooms                                                */
-
-/*
-  This program is free software; you can 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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#define BITLBEE_CORE
-#include "bitlbee.h"
-
-struct groupchat *imcb_chat_new(struct im_connection *ic, const char *handle)
-{
-	struct groupchat *c = g_new0(struct groupchat, 1);
-	bee_t *bee = ic->bee;
-
-	/* This one just creates the conversation structure, user won't see
-	   anything yet until s/he is joined to the conversation. (This
-	   allows you to add other already present participants first.) */
-
-	ic->groupchats = g_slist_prepend(ic->groupchats, c);
-	c->ic = ic;
-	c->title = g_strdup(handle);
-	c->topic = g_strdup_printf(
-	        "BitlBee groupchat: \"%s\". Please keep in mind that root-commands won't work here. Have fun!",
-	        c->title);
-
-	if (set_getbool(&ic->bee->set, "debug")) {
-		imcb_log(ic, "Creating new conversation: (id=%p,handle=%s)", c, handle);
-	}
-
-	if (bee->ui->chat_new) {
-		bee->ui->chat_new(bee, c);
-	}
-
-	return c;
-}
-
-void imcb_chat_name_hint(struct groupchat *c, const char *name)
-{
-	bee_t *bee = c->ic->bee;
-
-	if (bee->ui->chat_name_hint) {
-		bee->ui->chat_name_hint(bee, c, name);
-	}
-}
-
-void imcb_chat_free(struct groupchat *c)
-{
-	struct im_connection *ic = c->ic;
-	bee_t *bee = ic->bee;
-	GList *ir;
-
-	if (bee->ui->chat_free) {
-		bee->ui->chat_free(bee, c);
-	}
-
-	if (set_getbool(&ic->bee->set, "debug")) {
-		imcb_log(ic, "You were removed from conversation %p", c);
-	}
-
-	ic->groupchats = g_slist_remove(ic->groupchats, c);
-
-	for (ir = c->in_room; ir; ir = ir->next) {
-		g_free(ir->data);
-	}
-	g_list_free(c->in_room);
-	g_free(c->title);
-	g_free(c->topic);
-	g_free(c);
-}
-
-static gboolean handle_is_self(struct im_connection *ic, const char *handle)
-{
-	return (ic->acc->prpl->handle_is_self) ?
-	       ic->acc->prpl->handle_is_self(ic, handle) :
-	       (ic->acc->prpl->handle_cmp(ic->acc->user, handle) == 0);
-}
-
-void imcb_chat_msg(struct groupchat *c, const char *who, char *msg, guint32 flags, time_t sent_at)
-{
-	struct im_connection *ic = c->ic;
-	bee_t *bee = ic->bee;
-	bee_user_t *bu;
-	gboolean temp = FALSE;
-	char *s;
-
-	if (handle_is_self(ic, who) && !(flags & OPT_SELFMESSAGE)) {
-		return;
-	}
-
-	bu = bee_user_by_handle(bee, ic, who);
-	temp = (bu == NULL);
-
-	if (temp) {
-		bu = bee_user_new(bee, ic, who, BEE_USER_ONLINE);
-	}
-
-	s = set_getstr(&ic->bee->set, "strip_html");
-	if ((g_strcasecmp(s, "always") == 0) ||
-	    ((ic->flags & OPT_DOES_HTML) && s)) {
-		strip_html(msg);
-	}
-
-	if (bee->ui->chat_msg) {
-		bee->ui->chat_msg(bee, c, bu, msg, flags, sent_at);
-	}
-
-	if (temp) {
-		bee_user_free(bee, bu);
-	}
-}
-
-void imcb_chat_log(struct groupchat *c, char *format, ...)
-{
-	struct im_connection *ic = c->ic;
-	bee_t *bee = ic->bee;
-	va_list params;
-	char *text;
-
-	if (!bee->ui->chat_log) {
-		return;
-	}
-
-	va_start(params, format);
-	text = g_strdup_vprintf(format, params);
-	va_end(params);
-
-	bee->ui->chat_log(bee, c, text);
-	g_free(text);
-}
-
-void imcb_chat_topic(struct groupchat *c, char *who, char *topic, time_t set_at)
-{
-	struct im_connection *ic = c->ic;
-	bee_t *bee = ic->bee;
-	bee_user_t *bu;
-
-	if (!bee->ui->chat_topic) {
-		return;
-	}
-
-	if (who == NULL) {
-		bu = NULL;
-	} else if (handle_is_self(ic, who)) {
-		bu = bee->user;
-	} else {
-		bu = bee_user_by_handle(bee, ic, who);
-	}
-
-	if ((g_strcasecmp(set_getstr(&ic->bee->set, "strip_html"), "always") == 0) ||
-	    ((ic->flags & OPT_DOES_HTML) && set_getbool(&ic->bee->set, "strip_html"))) {
-		strip_html(topic);
-	}
-
-	bee->ui->chat_topic(bee, c, topic, bu);
-}
-
-void imcb_chat_add_buddy(struct groupchat *c, const char *handle)
-{
-	struct im_connection *ic = c->ic;
-	bee_t *bee = ic->bee;
-	bee_user_t *bu = bee_user_by_handle(bee, ic, handle);
-	gboolean me;
-
-	if (set_getbool(&c->ic->bee->set, "debug")) {
-		imcb_log(c->ic, "User %s added to conversation %p", handle, c);
-	}
-
-	me = handle_is_self(ic, handle);
-
-	/* Most protocols allow people to join, even when they're not in
-	   your contact list. Try to handle that here */
-	if (!me && !bu) {
-		bu = bee_user_new(bee, ic, handle, BEE_USER_LOCAL);
-	}
-
-	/* Add the handle to the room userlist */
-	/* TODO: Use bu instead of a string */
-	c->in_room = g_list_append(c->in_room, g_strdup(handle));
-
-	if (bee->ui->chat_add_user) {
-		bee->ui->chat_add_user(bee, c, me ? bee->user : bu);
-	}
-
-	if (me) {
-		c->joined = 1;
-	}
-}
-
-void imcb_chat_remove_buddy(struct groupchat *c, const char *handle, const char *reason)
-{
-	struct im_connection *ic = c->ic;
-	bee_t *bee = ic->bee;
-	bee_user_t *bu = NULL;
-
-	if (set_getbool(&bee->set, "debug")) {
-		imcb_log(ic, "User %s removed from conversation %p (%s)", handle, c, reason ? reason : "");
-	}
-
-	/* It might be yourself! */
-	if (handle_is_self(ic, handle)) {
-		if (c->joined == 0) {
-			return;
-		}
-
-		bu = bee->user;
-		c->joined = 0;
-	} else {
-		bu = bee_user_by_handle(bee, ic, handle);
-	}
-
-	if (bee->ui->chat_remove_user && bu) {
-		bee->ui->chat_remove_user(bee, c, bu, reason);
-	}
-}
-
-int bee_chat_msg(bee_t *bee, struct groupchat *c, const char *msg, int flags)
-{
-	struct im_connection *ic = c->ic;
-	char *buf = NULL;
-
-	if ((ic->flags & OPT_DOES_HTML) && (g_strncasecmp(msg, "<html>", 6) != 0)) {
-		buf = escape_html(msg);
-		msg = buf;
-	} else {
-		buf = g_strdup(msg);
-	}
-
-	ic->acc->prpl->chat_msg(c, buf, flags);
-	g_free(buf);
-
-	return 1;
-}
-
-struct groupchat *bee_chat_by_title(bee_t *bee, struct im_connection *ic, const char *title)
-{
-	struct groupchat *c;
-	GSList *l;
-
-	for (l = ic->groupchats; l; l = l->next) {
-		c = l->data;
-		if (strcmp(c->title, title) == 0) {
-			return c;
-		}
-	}
-
-	return NULL;
-}
-
-void imcb_chat_invite(struct im_connection *ic, const char *name, const char *who, const char *msg)
-{
-	bee_user_t *bu = bee_user_by_handle(ic->bee, ic, who);
-
-	if (bu && ic->bee->ui->chat_invite) {
-		ic->bee->ui->chat_invite(ic->bee, bu, name, msg);
-	}
-}
-
-void imcb_chat_list_finish(struct im_connection *ic)
-{
-	cmd_chat_list_finish(ic);
-}
-
-void bee_chat_list_finish(struct im_connection *ic)
-{
-	imcb_log(ic, "Warning: using deprecated bee_chat_list_finish. This will be removed in the stable release.");
-	imcb_chat_list_finish(ic);
-}
-
-void imcb_chat_list_free(struct im_connection *ic)
-{
-	bee_chat_info_t *ci;
-	GSList *l = ic->chatlist;
-
-	while (l) {
-		ci = l->data;
-		l = g_slist_delete_link(l, l);
-
-		g_free(ci->title);
-		g_free(ci->topic);
-		g_free(ci);
-	}
-
-	ic->chatlist = NULL;
-}
Index: otocols/bee_ft.c
===================================================================
--- protocols/bee_ft.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,71 +1,0 @@
-/********************************************************************\
-* BitlBee -- An IRC to other IM-networks gateway                     *
-*                                                                    *
-* Copyright 2010 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 with
-  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
-  if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#define BITLBEE_CORE
-#include "bitlbee.h"
-#include "ft.h"
-
-file_transfer_t *imcb_file_send_start(struct im_connection *ic, char *handle, char *file_name, size_t file_size)
-{
-	bee_t *bee = ic->bee;
-	bee_user_t *bu = bee_user_by_handle(bee, ic, handle);
-
-	if (bee->ui->ft_in_start && bu) {
-		return bee->ui->ft_in_start(bee, bu, file_name, file_size);
-	} else {
-		return NULL;
-	}
-}
-
-gboolean imcb_file_recv_start(struct im_connection *ic, file_transfer_t *ft)
-{
-	bee_t *bee = ic->bee;
-
-	if (bee->ui->ft_out_start) {
-		return bee->ui->ft_out_start(ic, ft);
-	} else {
-		return FALSE;
-	}
-}
-
-void imcb_file_canceled(struct im_connection *ic, file_transfer_t *file, char *reason)
-{
-	bee_t *bee = ic->bee;
-
-	if (file->canceled) {
-		file->canceled(file, reason);
-	}
-
-	if (bee->ui->ft_close) {
-		bee->ui->ft_close(ic, file);
-	}
-}
-
-void imcb_file_finished(struct im_connection *ic, file_transfer_t *file)
-{
-	bee_t *bee = ic->bee;
-
-	if (bee->ui->ft_finished) {
-		bee->ui->ft_finished(ic, file);
-	}
-}
Index: otocols/bee_user.c
===================================================================
--- protocols/bee_user.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,332 +1,0 @@
-/********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2010 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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#define BITLBEE_CORE
-#include "bitlbee.h"
-
-bee_user_t *bee_user_new(bee_t *bee, struct im_connection *ic, const char *handle, bee_user_flags_t flags)
-{
-	bee_user_t *bu;
-
-	if (bee_user_by_handle(bee, ic, handle) != NULL) {
-		return NULL;
-	}
-
-	bu = g_new0(bee_user_t, 1);
-	bu->bee = bee;
-	bu->ic = ic;
-	bu->flags = flags;
-	bu->handle = g_strdup(handle);
-	bee->users = g_slist_prepend(bee->users, bu);
-
-	if (ic->bee_users) {
-		g_hash_table_insert(ic->bee_users, bu->handle, bu);
-	}
-	if (bee->ui->user_new) {
-		bee->ui->user_new(bee, bu);
-	}
-	if (ic->acc->prpl->buddy_data_add) {
-		ic->acc->prpl->buddy_data_add(bu);
-	}
-
-	/* Offline by default. This will set the right flags. */
-	imcb_buddy_status(ic, handle, 0, NULL, NULL);
-
-	return bu;
-}
-
-int bee_user_free(bee_t *bee, bee_user_t *bu)
-{
-	if (!bu) {
-		return 0;
-	}
-
-	if (bee->ui->user_free) {
-		bee->ui->user_free(bee, bu);
-	}
-	if (bu->ic->acc->prpl->buddy_data_free) {
-		bu->ic->acc->prpl->buddy_data_free(bu);
-	}
-	if (bu->ic->bee_users) {
-		g_hash_table_remove(bu->ic->bee_users, bu->handle);
-	}
-	bee->users = g_slist_remove(bee->users, bu);
-
-	g_free(bu->handle);
-	g_free(bu->fullname);
-	g_free(bu->nick);
-	g_free(bu->status);
-	g_free(bu->status_msg);
-	g_free(bu);
-
-	return 1;
-}
-
-bee_user_t *bee_user_by_handle_slow(bee_t *bee, struct im_connection *ic, const char *handle)
-{
-	GSList *l;
-
-	for (l = bee->users; l; l = l->next) {
-		bee_user_t *bu = l->data;
-
-		if (bu->ic == ic && ic->acc->prpl->handle_cmp(bu->handle, handle) == 0) {
-			return bu;
-		}
-	}
-
-	return NULL;
-}
-
-bee_user_t *bee_user_by_handle(bee_t *bee, struct im_connection *ic, const char *handle)
-{
-	if (!ic->bee_users) {
-		return bee_user_by_handle_slow(bee, ic, handle);
-	}
-
-	return g_hash_table_lookup(ic->bee_users, handle);
-}
-
-int bee_user_msg(bee_t *bee, bee_user_t *bu, const char *msg, int flags)
-{
-	char *buf = NULL;
-	int st;
-
-	if ((bu->ic->flags & OPT_DOES_HTML) && (g_strncasecmp(msg, "<html>", 6) != 0)) {
-		buf = escape_html(msg);
-		msg = buf;
-	} else {
-		buf = g_strdup(msg);
-	}
-
-	st = bu->ic->acc->prpl->buddy_msg(bu->ic, bu->handle, buf, flags);
-	g_free(buf);
-
-	return st;
-}
-
-
-/* Groups */
-static bee_group_t *bee_group_new(bee_t *bee, const char *name)
-{
-	bee_group_t *bg = g_new0(bee_group_t, 1);
-
-	bg->name = g_strdup(name);
-	bg->key = g_utf8_casefold(name, -1);
-	bee->groups = g_slist_prepend(bee->groups, bg);
-
-	return bg;
-}
-
-bee_group_t *bee_group_by_name(bee_t *bee, const char *name, gboolean creat)
-{
-	GSList *l;
-	char *key;
-
-	if (name == NULL) {
-		return NULL;
-	}
-
-	key = g_utf8_casefold(name, -1);
-	for (l = bee->groups; l; l = l->next) {
-		bee_group_t *bg = l->data;
-		if (strcmp(bg->key, key) == 0) {
-			break;
-		}
-	}
-	g_free(key);
-
-	if (!l) {
-		return creat ? bee_group_new(bee, name) : NULL;
-	} else {
-		return l->data;
-	}
-}
-
-void bee_group_free(bee_t *bee)
-{
-	while (bee->groups) {
-		bee_group_t *bg = bee->groups->data;
-		g_free(bg->name);
-		g_free(bg->key);
-		g_free(bg);
-		bee->groups = g_slist_remove(bee->groups, bee->groups->data);
-	}
-}
-
-
-/* IM->UI callbacks */
-void imcb_buddy_status(struct im_connection *ic, const char *handle, int flags, const char *state, const char *message)
-{
-	bee_t *bee = ic->bee;
-	bee_user_t *bu, *old;
-
-	if (!(bu = bee_user_by_handle(bee, ic, handle))) {
-		char *h = set_getstr(&ic->acc->set, "handle_unknown") ? :
-		          set_getstr(&ic->bee->set, "handle_unknown");
-
-		if (g_strncasecmp(h, "add", 3) == 0) {
-			bu = bee_user_new(bee, ic, handle, BEE_USER_LOCAL);
-		} else {
-			if (g_strcasecmp(h, "ignore") != 0) {
-				imcb_log(ic, "imcb_buddy_status() for unknown handle %s:\n"
-				         "flags = %d, state = %s, message = %s", handle, flags,
-				         state ? state : "NULL", message ? message : "NULL");
-			}
-
-			return;
-		}
-	}
-
-	/* May be nice to give the UI something to compare against. */
-	old = g_memdup2(bu, sizeof(bee_user_t));
-
-	/* TODO(wilmer): OPT_AWAY, or just state == NULL ? */
-	bu->flags = flags;
-	bu->status_msg = g_strdup(message);
-	if (state && *state) {
-		bu->status = g_strdup(state);
-	} else if (flags & OPT_AWAY) {
-		bu->status = g_strdup("Away");
-	} else {
-		bu->status = NULL;
-	}
-
-	if (bu->status == NULL && (flags & OPT_MOBILE) &&
-	    set_getbool(&bee->set, "mobile_is_away")) {
-		bu->flags |= BEE_USER_AWAY;
-		bu->status = g_strdup("Mobile");
-	}
-
-	if (bee->ui->user_status) {
-		bee->ui->user_status(bee, bu, old);
-	}
-
-	g_free(old->status_msg);
-	g_free(old->status);
-	g_free(old);
-}
-
-/* Same, but only change the away/status message, not any away/online state info. */
-void imcb_buddy_status_msg(struct im_connection *ic, const char *handle, const char *message)
-{
-	bee_t *bee = ic->bee;
-	bee_user_t *bu, *old;
-
-	if (!(bu = bee_user_by_handle(bee, ic, handle))) {
-		return;
-	}
-
-	old = g_memdup2(bu, sizeof(bee_user_t));
-
-	bu->status_msg = message && *message ? g_strdup(message) : NULL;
-
-	if (bee->ui->user_status) {
-		bee->ui->user_status(bee, bu, old);
-	}
-
-	g_free(old->status_msg);
-	g_free(old);
-}
-
-void imcb_buddy_times(struct im_connection *ic, const char *handle, time_t login, time_t idle)
-{
-	bee_t *bee = ic->bee;
-	bee_user_t *bu;
-
-	if (!(bu = bee_user_by_handle(bee, ic, handle))) {
-		return;
-	}
-
-	bu->login_time = login;
-	bu->idle_time = idle;
-}
-
-void imcb_buddy_msg(struct im_connection *ic, const char *handle, const char *msg, guint32 flags, time_t sent_at)
-{
-	bee_t *bee = ic->bee;
-	bee_user_t *bu;
-
-	bu = bee_user_by_handle(bee, ic, handle);
-
-	if (!bu && !(ic->flags & OPT_LOGGING_OUT)) {
-		char *h = set_getstr(&ic->acc->set, "handle_unknown") ? :
-		          set_getstr(&ic->bee->set, "handle_unknown");
-
-		if (g_strcasecmp(h, "ignore") == 0) {
-			return;
-		} else if (g_strncasecmp(h, "add", 3) == 0) {
-			bu = bee_user_new(bee, ic, handle, BEE_USER_LOCAL);
-		}
-	}
-
-	if (bee->ui->user_msg && bu) {
-		bee->ui->user_msg(bee, bu, msg, flags, sent_at);
-	} else {
-		imcb_log(ic, "Message from unknown handle %s:\n%s", handle, msg);
-	}
-}
-
-void imcb_notify_email(struct im_connection *ic, char *format, ...)
-{
-	const char *handle;
-	va_list params;
-	char *msg;
-
-	if (!set_getbool(&ic->acc->set, "mail_notifications")) {
-		return;
-	}
-
-	va_start(params, format);
-	msg = g_strdup_vprintf(format, params);
-	va_end(params);
-
-	/* up to the protocol to set_add this if they want to use this */
-	handle = set_getstr(&ic->acc->set, "mail_notifications_handle");
-
-	if (handle != NULL) {
-		imcb_buddy_msg(ic, handle, msg, 0, 0);
-	} else {
-		imcb_log(ic, "%s", msg);
-	}
-
-	g_free(msg);
-}
-
-void imcb_buddy_typing(struct im_connection *ic, const char *handle, guint32 flags)
-{
-	bee_user_t *bu;
-
-	if (ic->bee->ui->user_typing &&
-	    (bu = bee_user_by_handle(ic->bee, ic, handle))) {
-		ic->bee->ui->user_typing(ic->bee, bu, flags);
-	}
-}
-
-void imcb_buddy_action_response(bee_user_t *bu, const char *action, char * const args[], void *data)
-{
-	if (bu->bee->ui->user_action_response) {
-		bu->bee->ui->user_action_response(bu->bee, bu, action, args, data);
-	}
-}
Index: otocols/ft.h
===================================================================
--- protocols/ft.h	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,176 +1,0 @@
-/********************************************************************\
-* BitlBee -- An IRC to other IM-networks gateway                     *
-*                                                                    *
-* Copyright 2006 Marijn Kruisselbrink and others                     *
-\********************************************************************/
-
-/* Generic file transfer header                                     */
-
-/*
-  This program is free software; you can 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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#ifndef _FT_H
-#define _FT_H
-
-/*
- * One buffer is needed for each transfer. The receiver stores a message
- * in it and gives it to the sender. The sender will stall the receiver
- * till the buffer has been sent out.
- */
-#define FT_BUFFER_SIZE 2048
-
-typedef enum {
-	FT_STATUS_LISTENING     = 1,
-	FT_STATUS_TRANSFERRING  = 2,
-	FT_STATUS_FINISHED      = 4,
-	FT_STATUS_CANCELED      = 8,
-	FT_STATUS_CONNECTING    = 16
-} file_status_t;
-
-/*
- * This structure holds all irc specific information regarding an incoming (from the point of view of
- * the irc client) file transfer. New instances of this struct should only be created by calling the
- * imcb_file_send_start() method, which will initialize most of the fields. The data field and the various
- * methods are zero-initialized. Instances will automatically be deleted once the transfer is completed,
- * canceled, or the connection to the irc client has been lost (note that also if only the irc connection
- * and not the file transfer connection is lost, the file transfer will still be canceled and freed).
- *
- * The following (poor ascii-art) diagram illustrates what methods are called for which status-changes:
- *
- *	                        /-----------\                    /----------\
- *	               -------> | LISTENING | -----------------> | CANCELED |
- *	                        \-----------/  [canceled,]free   \----------/
- *	                              |
- *	                              | accept
- *	                              V
- *	               /------ /-------------\                    /--------------------------\
- *	   out_of_data |       | TRANSFERRING | -----------------> | TRANSFERRING | CANCELED |
- *	               \-----> \-------------/  [canceled,]free   \--------------------------/
- *	                              |
- *	                              | finished,free
- *	                              V
- *	                 /-------------------------\
- *	                 | TRANSFERRING | FINISHED |
- *	                 \-------------------------/
- */
-typedef struct file_transfer {
-
-	/* Are we sending something? */
-	int sending;
-
-	/*
-	 * The current status of this file transfer.
-	 */
-	file_status_t status;
-
-	/*
-	 * file size
-	 */
-	size_t file_size;
-
-	/*
-	 * Number of bytes that have been successfully transferred.
-	 */
-	size_t bytes_transferred;
-
-	/*
-	 * Time started. Used to calculate kb/s.
-	 */
-	time_t started;
-
-	/*
-	 * file name
-	 */
-	char *file_name;
-
-	/*
-	 * A unique local ID for this file transfer.
-	 */
-	unsigned int local_id;
-
-	/*
-	 * IM-protocol specific data associated with this file transfer.
-	 */
-	gpointer data;
-	struct im_connection *ic;
-
-	/*
-	 * Private data.
-	 */
-	gpointer priv;
-
-	/*
-	 * If set, called after successful connection setup.
-	 */
-	void (*accept)(struct file_transfer *file);
-
-	/*
-	 * If set, called when the transfer is canceled or finished.
-	 * Subsequently, this structure will be freed.
-	 *
-	 */
-	void (*free)(struct file_transfer *file);
-
-	/*
-	 * If set, called when the transfer is finished and successful.
-	 */
-	void (*finished)(struct file_transfer *file);
-
-	/*
-	 * If set, called when the transfer is canceled.
-	 * ( canceled either by the transfer implementation or by
-	 *  a call to imcb_file_canceled )
-	 */
-	void (*canceled)(struct file_transfer *file, char *reason);
-
-	/*
-	 * called by the sending side to indicate that it is writable.
-	 * The callee should check if data is available and call the
-	 * function(as seen below) if that is the case.
-	 */
-	gboolean (*write_request)(struct file_transfer *file);
-
-	/*
-	 * When sending files, protocols register this function to receive data.
-	 * This should only be called once after write_request is called. The caller
-	 * should not read more data until write_request is called again. This technique
-	 * avoids buffering.
-	 */
-	gboolean (*write)(struct file_transfer *file, char *buffer, unsigned int len);
-
-	/* The send buffer associated with this transfer.
-	 * Since receivers always wait for a write_request call one is enough.
-	 */
-	char buffer[FT_BUFFER_SIZE];
-
-} file_transfer_t;
-
-/*
- * This starts a file transfer from bitlbee to the user.
- */
-file_transfer_t *imcb_file_send_start(struct im_connection *ic, char *user_nick, char *file_name, size_t file_size);
-
-/*
- * This should be called by a protocol when the transfer is canceled. Note that
- * the canceled() and free() callbacks given in file will be called by this function.
- */
-void imcb_file_canceled(struct im_connection *ic, file_transfer_t *file, char *reason);
-
-gboolean imcb_file_recv_start(struct im_connection *ic, file_transfer_t *ft);
-
-void imcb_file_finished(struct im_connection *ic, file_transfer_t *file);
-#endif
Index: protocols/jabber/Makefile
===================================================================
--- protocols/jabber/Makefile	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ protocols/jabber/Makefile	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -8,19 +8,13 @@
 
 -include ../../Makefile.settings
-ifdef _SRCDIR_
-_SRCDIR_ := $(_SRCDIR_)protocols/jabber/
-endif
 
 # [SH] Program variables
-objects = conference.o io.o iq.o jabber.o jabber_util.o message.o presence.o s5bytestream.o sasl.o si.o hipchat.o
+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: jabber_mod.o
-check: all
-lcov: check
-gcov: 
-	gcov *.c
+all: jabberr.o
 
 .PHONY: all clean distclean
@@ -30,5 +24,4 @@
 
 distclean: clean
-	rm -rf .depend
 
 ### MAIN PROGRAM
@@ -36,11 +29,9 @@
 $(objects): ../../Makefile.settings Makefile
 
-$(objects): %.o: $(_SRCDIR_)%.c
+$(objects): %.o: %.c
 	@echo '*' Compiling $<
-	$(VERBOSE) $(CC) -c $(CFLAGS) $(CFLAGS_BITLBEE) $< -o $@
+	@$(CC) -c $(CFLAGS) $< -o $@
 
-jabber_mod.o: $(objects)
-	@echo '*' Linking jabber_mod.o
-	$(VERBOSE) $(LD) $(LFLAGS) $(objects) -o jabber_mod.o
-
--include .depend/*.d
+jabberr.o: $(objects)
+	@echo '*' Linking jabberr.o
+	@$(LD) $(LFLAGS) $(objects) -o jabberr.o
Index: protocols/jabber/asciitab.h
===================================================================
--- protocols/jabber/asciitab.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/asciitab.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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,
Index: otocols/jabber/conference.c
===================================================================
--- protocols/jabber/conference.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,528 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Jabber module - Conference rooms                                         *
-*                                                                           *
-*  Copyright 2007-2012 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.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-\***************************************************************************/
-
-#include "jabber.h"
-
-static xt_status jabber_chat_join_failed(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
-static xt_status jabber_chat_self_message(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
-
-struct groupchat *jabber_chat_join(struct im_connection *ic, const char *room, const char *nick, const char *password,
-                                   gboolean always_use_nicks)
-{
-	struct jabber_chat *jc;
-	struct xt_node *node;
-	struct groupchat *c;
-	char *roomjid;
-
-	roomjid = g_strdup_printf("%s/%s", room, nick);
-	node = xt_new_node("x", NULL, NULL);
-	xt_add_attr(node, "xmlns", XMLNS_MUC);
-	if (password) {
-		xt_add_child(node, xt_new_node("password", password, NULL));
-	}
-	node = jabber_make_packet("presence", NULL, roomjid, node);
-	jabber_cache_add(ic, node, jabber_chat_join_failed);
-
-	if (!jabber_write_packet(ic, node)) {
-		g_free(roomjid);
-		return NULL;
-	}
-
-	jc = g_new0(struct jabber_chat, 1);
-	jc->name = jabber_normalize(room);
-
-	if ((jc->me = jabber_buddy_add(ic, roomjid)) == NULL) {
-		g_free(roomjid);
-		g_free(jc->name);
-		g_free(jc);
-		return NULL;
-	}
-
-	if (always_use_nicks) {
-		jc->flags = JCFLAG_ALWAYS_USE_NICKS;
-	}
-
-	/* roomjid isn't normalized yet, and we need an original version
-	   of the nick to send a proper presence update. */
-	jc->my_full_jid = roomjid;
-
-	c = imcb_chat_new(ic, room);
-	c->data = jc;
-
-	return c;
-}
-
-struct groupchat *jabber_chat_with(struct im_connection *ic, char *who)
-{
-	struct jabber_data *jd = ic->proto_data;
-	struct jabber_chat *jc;
-	struct groupchat *c;
-	GChecksum *sum;
-	double now = gettime();
-	char *uuid, *rjid, *cserv;
-
-	sum = g_checksum_new(G_CHECKSUM_SHA1);
-	g_checksum_update(sum, (uint8_t *) ic->acc->user, strlen(ic->acc->user));
-	g_checksum_update(sum, (uint8_t *) &now, sizeof(now));
-	g_checksum_update(sum, (uint8_t *) who, strlen(who));
-	uuid = sha1_random_uuid(sum);
-
-	if (jd->flags & JFLAG_GTALK) {
-		cserv = g_strdup("groupchat.google.com");
-	} else {
-		/* Guess... */
-		cserv = g_strdup_printf("conference.%s", jd->server);
-	}
-
-	rjid = g_strdup_printf("private-chat-%s@%s", uuid, cserv);
-	g_free(uuid);
-	g_free(cserv);
-
-	c = jabber_chat_join(ic, rjid, jd->username, NULL, FALSE);
-	g_free(rjid);
-	if (c == NULL) {
-		return NULL;
-	}
-
-	jc = c->data;
-	jc->invite = g_strdup(who);
-
-	return c;
-}
-
-static xt_status jabber_chat_join_failed(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
-{
-	struct jabber_error *err;
-	struct jabber_buddy *bud;
-	char *room;
-
-	room = xt_find_attr(orig, "to");
-	bud = jabber_buddy_by_jid(ic, room, 0);
-	err = jabber_error_parse(xt_find_node(node->children, "error"), XMLNS_STANZA_ERROR);
-	if (err) {
-		imcb_error(ic, "Error joining groupchat %s: %s%s%s", room, err->code,
-		           err->text ? ": " : "", err->text ? err->text : "");
-		jabber_error_free(err);
-	}
-	if (bud) {
-		struct groupchat *c = jabber_chat_by_jid(ic, bud->bare_jid);
-		if (c) {
-			jabber_chat_free(c);
-		}
-	}
-
-	return XT_HANDLED;
-}
-
-static xt_status jabber_chat_self_message(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
-{
-	/* This is a self message sent by this bitlbee - just drop it */
-	return XT_ABORT;
-}
-
-struct groupchat *jabber_chat_by_jid(struct im_connection *ic, const char *name)
-{
-	char *normalized = jabber_normalize(name);
-	GSList *l;
-	struct groupchat *ret;
-	struct jabber_chat *jc;
-
-	for (l = ic->groupchats; l; l = l->next) {
-		ret = l->data;
-		jc = ret->data;
-		if (strcmp(normalized, jc->name) == 0) {
-			break;
-		}
-	}
-	g_free(normalized);
-
-	return l ? ret : NULL;
-}
-
-void jabber_chat_free(struct groupchat *c)
-{
-	struct jabber_chat *jc = c->data;
-
-	jabber_buddy_remove_bare(c->ic, jc->name);
-
-	g_free(jc->last_sent_message);
-	g_free(jc->my_full_jid);
-	g_free(jc->name);
-	g_free(jc->invite);
-	g_free(jc);
-
-	imcb_chat_free(c);
-}
-
-int jabber_chat_msg(struct groupchat *c, char *message, int flags)
-{
-	struct im_connection *ic = c->ic;
-	struct jabber_chat *jc = c->data;
-	struct xt_node *node;
-
-	jc->flags |= JCFLAG_MESSAGE_SENT;
-
-	node = xt_new_node("body", message, NULL);
-	node = jabber_make_packet("message", "groupchat", jc->name, node);
-
-	jabber_cache_add(ic, node, jabber_chat_self_message);
-
-	g_free(jc->last_sent_message);
-	jc->last_sent_message = g_strdup(message);
-
-	return !jabber_write_packet(ic, node);
-}
-
-int jabber_chat_topic(struct groupchat *c, char *topic)
-{
-	struct im_connection *ic = c->ic;
-	struct jabber_chat *jc = c->data;
-	struct xt_node *node;
-
-	node = xt_new_node("subject", topic, NULL);
-	node = jabber_make_packet("message", "groupchat", jc->name, node);
-
-	if (!jabber_write_packet(ic, node)) {
-		xt_free_node(node);
-		return 0;
-	}
-	xt_free_node(node);
-
-	return 1;
-}
-
-int jabber_chat_leave(struct groupchat *c, const char *reason)
-{
-	struct im_connection *ic = c->ic;
-	struct jabber_chat *jc = c->data;
-	struct xt_node *node;
-
-	node = xt_new_node("x", NULL, NULL);
-	xt_add_attr(node, "xmlns", XMLNS_MUC);
-	node = jabber_make_packet("presence", "unavailable", jc->my_full_jid, node);
-
-	if (!jabber_write_packet(ic, node)) {
-		xt_free_node(node);
-		return 0;
-	}
-	xt_free_node(node);
-
-	return 1;
-}
-
-void jabber_chat_invite(struct groupchat *c, char *who, char *message)
-{
-	struct xt_node *node;
-	struct im_connection *ic = c->ic;
-	struct jabber_chat *jc = c->data;
-
-	node = xt_new_node("reason", message, NULL);
-
-	node = xt_new_node("invite", NULL, node);
-	xt_add_attr(node, "to", who);
-
-	node = xt_new_node("x", NULL, node);
-	xt_add_attr(node, "xmlns", XMLNS_MUC_USER);
-
-	node = jabber_make_packet("message", NULL, jc->name, node);
-
-	jabber_write_packet(ic, node);
-
-	xt_free_node(node);
-}
-
-static int jabber_chat_has_other_resources(struct im_connection *ic, struct jabber_buddy *bud)
-{
-	struct jabber_buddy *cur;
-
-	for (cur = jabber_buddy_by_jid(ic, bud->bare_jid, GET_BUDDY_FIRST); cur; cur = cur->next) {
-		if (cur != bud && jabber_compare_jid(cur->ext_jid, bud->ext_jid)) {
-			return TRUE;
-		}
-	}
-	
-	return FALSE;
-}
-
-/* Not really the same syntax as the normal pkt_ functions, but this isn't
-   called by the xmltree parser directly and this way I can add some extra
-   parameters so we won't have to repeat too many things done by the caller
-   already. */
-void jabber_chat_pkt_presence(struct im_connection *ic, struct jabber_buddy *bud, struct xt_node *node)
-{
-	struct groupchat *chat;
-	struct xt_node *c;
-	char *type = xt_find_attr(node, "type");
-	struct jabber_data *jd = ic->proto_data;
-	struct jabber_chat *jc;
-	char *s;
-
-	if ((chat = jabber_chat_by_jid(ic, bud->bare_jid)) == NULL) {
-		/* How could this happen?? We could do kill( self, 11 )
-		   now or just wait for the OS to do it. :-) */
-		return;
-	}
-
-	jc = chat->data;
-
-	if (type == NULL && !(bud->flags & JBFLAG_IS_CHATROOM)) {
-		bud->flags |= JBFLAG_IS_CHATROOM;
-		/* If this one wasn't set yet, this buddy just joined the chat.
-		   Slightly hackish way of finding out eh? ;-) */
-
-		/* This is pretty messy... Here it sets ext_jid to the real
-		   JID of the participant. Works for non-anonymized channels.
-		   Might break if someone joins a chat twice, though. */
-		for (c = node->children; (c = xt_find_node(c, "x")); c = c->next) {
-			if ((s = xt_find_attr(c, "xmlns")) &&
-			    (strcmp(s, XMLNS_MUC_USER) == 0)) {
-				struct xt_node *item;
-
-				item = xt_find_node(c->children, "item");
-				if ((s = xt_find_attr(item, "jid"))) {
-					/* Yay, found what we need. :-) */
-					bud->ext_jid = jabber_normalize(s);
-					break;
-				}
-			}
-		}
-
-		/* Make up some other handle, if necessary. */
-		if (bud->ext_jid == NULL) {
-			if (bud == jc->me) {
-				bud->ext_jid = g_strdup(jd->me);
-			} else {
-				int i;
-
-				/* Don't want the nick to be at the end, so let's
-				   think of some slightly different notation to use
-				   for anonymous groupchat participants in BitlBee. */
-				bud->ext_jid = g_strdup_printf("%s=%s", bud->resource, bud->bare_jid);
-
-				/* And strip any unwanted characters. */
-				for (i = 0; bud->resource[i]; i++) {
-					if (bud->ext_jid[i] == '=' || bud->ext_jid[i] == '@') {
-						bud->ext_jid[i] = '_';
-					}
-				}
-			}
-			bud->flags |= JBFLAG_IS_ANONYMOUS;
-		} else if (bud == jc->me) {
-			g_free(bud->ext_jid);
-			bud->ext_jid = g_strdup(jd->me);
-		}
-
-		if (bud != jc->me && bud->flags & JBFLAG_IS_ANONYMOUS) {
-			/* If JIDs are anonymized, add them to the local
-			   list for the duration of this chat. */
-			imcb_add_buddy(ic, bud->ext_jid, NULL);
-			imcb_buddy_nick_hint(ic, bud->ext_jid, bud->resource);
-		}
-
-		if (bud == jc->me && jc->invite != NULL) {
-			char *msg = g_strdup_printf("Please join me in room %s", jc->name);
-			jabber_chat_invite(chat, jc->invite, msg);
-			g_free(jc->invite);
-			g_free(msg);
-			jc->invite = NULL;
-		}
-
-		s = strchr(bud->ext_jid, '/');
-		if (s) {
-			*s = 0; /* Should NEVER be NULL, but who knows... */
-		}
-
-		imcb_chat_add_buddy(chat, bud->ext_jid);
-
-		if (bud != jc->me && (jc->flags & JCFLAG_ALWAYS_USE_NICKS) && !(bud->flags & JBFLAG_IS_ANONYMOUS)) {
-			imcb_buddy_nick_change(ic, bud->ext_jid, bud->resource);
-		}
-
-		if (s) {
-			*s = '/';
-		}
-	} else if (type) { /* type can only be NULL or "unavailable" in this function */
-		if ((bud->flags & JBFLAG_IS_CHATROOM) && bud->ext_jid && !jabber_chat_has_other_resources(ic, bud)) {
-			char *reason = NULL;
-			char *status = NULL;
-			char *status_text = NULL;
-			
-			if ((c = xt_find_node_by_attr(node->children, "x", "xmlns", XMLNS_MUC_USER))) {
-				struct xt_node *c2 = c->children;
-
-				while ((c2 = xt_find_node(c2, "status"))) {
-					char *code = xt_find_attr(c2, "code");
-					if (g_strcmp0(code, "301") == 0) {
-						status = "Banned";
-						break;
-					} else if (g_strcmp0(code, "303") == 0) {
-						/* This could be handled in a cleverer way,
-						 * but let's just show a literal part/join for now */
-						status = "Changing nicks";
-						break;
-					} else if (g_strcmp0(code, "307") == 0) {
-						status = "Kicked";
-						break;
-					}
-					c2 = c2->next;
-				}
-
-				/* Sometimes the status message is in presence/x/item/reason */
-				if ((c2 = xt_find_path(c, "item/reason")) && c2->text && c2->text_len) {
-					status_text = c2->text;
-				}
-			}
-
-			/* Sometimes the status message is right inside <presence> */
-			if ((c = xt_find_node(node->children, "status")) && c->text && c->text_len) {
-				status_text = c->text;
-			}
-
-			if (status_text && status) {
-				reason = g_strdup_printf("%s: %s", status, status_text);
-			} else {
-				reason = g_strdup(status_text ? : status);
-			}
-
-			s = strchr(bud->ext_jid, '/');
-			if (s) {
-				*s = 0;
-			}
-			imcb_chat_remove_buddy(chat, bud->ext_jid, reason);
-			if (bud != jc->me && bud->flags & JBFLAG_IS_ANONYMOUS) {
-				imcb_remove_buddy(ic, bud->ext_jid, reason);
-			}
-			if (s) {
-				*s = '/';
-			}
-
-			g_free(reason);
-		}
-
-		if (bud == jc->me) {
-			jabber_chat_free(chat);
-		}
-	}
-}
-
-void jabber_chat_pkt_message(struct im_connection *ic, struct jabber_buddy *bud, struct xt_node *node)
-{
-	struct xt_node *subject = xt_find_node(node->children, "subject");
-	struct xt_node *body = xt_find_node(node->children, "body");
-	struct groupchat *chat = NULL;
-	struct jabber_chat *jc = NULL;
-	char *from = NULL;
-	char *nick = NULL;
-	char *final_from = NULL;
-	char *bare_jid = NULL;
-	guint32 flags = 0;
-
-	from = (bud) ? bud->full_jid : xt_find_attr(node, "from");
-
-	if (from) {
-		nick = strchr(from, '/');
-		if (nick) {
-			*nick = 0;
-		}
-		chat = jabber_chat_by_jid(ic, from);
-		if (nick) {
-			*nick = '/';
-			nick++;
-		}
-	}
-
-	jc = (chat) ? chat->data : NULL;
-
-	if (!bud) {
-		struct xt_node *c;
-		char *s;
-
-		/* Try some clever stuff to find out the real JID here */
-		c = xt_find_node_by_attr(node->children, "delay", "xmlns", XMLNS_DELAY);
-
-		if (c && ((s = xt_find_attr(c, "from")) ||
-		          (s = xt_find_attr(c, "from_jid")))) {
-			/* This won't be useful if it's the MUC JID */
-			if (!(jc && jabber_compare_jid(s, jc->name))) {
-				/* Hopefully this one makes more sense! */
-				bud = jabber_buddy_by_jid(ic, s, GET_BUDDY_FIRST | GET_BUDDY_CREAT);
-			}
-		}
-
-	}
-
-	if (subject && chat) {
-		char empty[1] = "";
-		char *subject_text = subject->text_len > 0 ? subject->text : empty;
-		if (g_strcmp0(chat->topic, subject_text) != 0) {
-			bare_jid = (bud) ? jabber_get_bare_jid(bud->ext_jid) : NULL;
-			imcb_chat_topic(chat, bare_jid, subject_text,
-			                jabber_get_timestamp(node));
-			g_free(bare_jid);
-			bare_jid = NULL;
-		}
-	}
-
-	if (body == NULL || body->text_len == 0) {
-		/* Meh. Empty messages aren't very interesting, no matter
-		   how much some servers love to send them. */
-		return;
-	}
-
-	if (chat == NULL) {
-		if (nick == NULL) {
-			imcb_log(ic, "System message from unknown groupchat %s: %s", from, body->text);
-		} else {
-			imcb_log(ic, "Groupchat message from unknown JID %s: %s", from, body->text);
-		}
-
-		return;
-	} else if (chat != NULL && bud == NULL && nick == NULL) {
-		imcb_chat_log(chat, "From conference server: %s", body->text);
-		return;
-	} else if (jc && jc->flags & JCFLAG_MESSAGE_SENT && bud == jc->me) {
-		if (jabber_cache_handle_packet(ic, node) == XT_ABORT) {
-			/* Self message marked by this bitlbee, don't show it */
-			return;
-		} else if (xt_find_attr(node, "id") == NULL &&
-		           g_strcmp0(body->text, jc->last_sent_message) == 0) {
-			/* Some misbehaving servers (like slack) eat the ids and echo anyway.
-			 * Try to detect those cases by comparing against the last sent message. */
-			return;
-		}
-	}
-
-	if (bud) {
-		bare_jid = jabber_get_bare_jid(bud->ext_jid ? bud->ext_jid : bud->full_jid);
-		final_from = bare_jid;
-		if (bud == jc->me || (g_strcasecmp(final_from, ic->acc->user) == 0)) {
-			flags = OPT_SELFMESSAGE;
-		}
-	} else {
-		final_from = nick;
-	}
-
-	imcb_chat_msg(chat, final_from, body->text, flags, jabber_get_timestamp(node));
-
-	g_free(bare_jid);
-}
Index: protocols/jabber/expat.c
===================================================================
--- protocols/jabber/expat.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/expat.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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;
+    }
+}
Index: protocols/jabber/hashtable.c
===================================================================
--- protocols/jabber/hashtable.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/hashtable.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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;
+}
+
Index: protocols/jabber/hashtable.h
===================================================================
--- protocols/jabber/hashtable.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/hashtable.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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 *);
Index: otocols/jabber/hipchat.c
===================================================================
--- protocols/jabber/hipchat.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,143 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Jabber module - HipChat specific functions                               *
-*                                                                           *
-*  Copyright 2015 Xamarin Inc                                               *
-*                                                                           *
-*  This program is free software; you can redistribute it and/or modify     *
-*  it under the terms of the GNU General Public License as published by     *
-*  the Free Software Foundation; either version 2 of the License, or        *
-*  (at your option) any later version.                                      *
-*                                                                           *
-*  This program is distributed in the hope that it will be useful,          *
-*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
-*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
-*  GNU General Public License for more details.                             *
-*                                                                           *
-*  You should have received a copy of the GNU General Public License along  *
-*  with this program; if not, write to the Free Software Foundation, Inc.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-\***************************************************************************/
-
-#include "jabber.h"
-
-xt_status hipchat_handle_success(struct im_connection *ic, struct xt_node *node)
-{
-	struct jabber_data *jd = ic->proto_data;
-	char *sep, *jid;
-
-	jid = xt_find_attr(node, "jid");
-
-	sep = strchr(jid, '/');
-	if (sep) {
-		*sep = '\0';
-	}
-
-	jabber_set_me(ic, jid);
-	imcb_log(ic, "Setting Hipchat JID to %s", jid);
-
-	if (sep) {
-		*sep = '/';
-	}
-
-	jd->muc_host = g_strdup(xt_find_attr(node, "muc_host"));
-
-	/* Hipchat's auth doesn't expect a restart here */
-	jd->flags &= ~JFLAG_STREAM_RESTART;
-
-	if (!jabber_get_roster(ic) ||
-	    !jabber_iq_disco_server(ic) ||
-	    !jabber_get_hipchat_profile(ic)) {
-		return XT_ABORT;
-	}
-
-	return XT_HANDLED;
-}
-
-int jabber_get_hipchat_profile(struct im_connection *ic)
-{
-	struct jabber_data *jd = ic->proto_data;
-	struct xt_node *node;
-	int st;
-
-	imcb_log(ic, "Fetching hipchat profile for %s", jd->me);
-
-	node = xt_new_node("query", NULL, NULL);
-	xt_add_attr(node, "xmlns", XMLNS_HIPCHAT_PROFILE);
-	node = jabber_make_packet("iq", "get", jd->me, node);
-
-	jabber_cache_add(ic, node, jabber_parse_hipchat_profile);
-	st = jabber_write_packet(ic, node);
-
-	return st;
-}
-
-xt_status jabber_parse_hipchat_profile(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
-{
-	struct xt_node *query, *name_node;
-
-	if (!(query = xt_find_node(node->children, "query"))) {
-		imcb_log(ic, "Warning: Received NULL profile packet");
-		return XT_ABORT;
-	}
-
-	name_node = xt_find_node(query->children, "name");
-	if (!name_node) {
-		imcb_log(ic, "Warning: Can't find real name in profile. Joining groupchats will not be possible.");
-		return XT_ABORT;
-	}
-
-	set_setstr(&ic->acc->set, "display_name", name_node->text);
-	return XT_HANDLED;
-
-}
-
-/* Returns a newly allocated string that tries to match the "slug" part of the JID using an
- * approximation of the method used by the server. This might fail in some rare conditions
- * (old JIDs generated a different way, locale settings unicode, etc) */
-char *hipchat_make_channel_slug(const char *name)
-{
-	char *lower;
-	char *new = g_malloc(strlen(name) + 1);
-	int i = 0;
-
-	do {
-		if (*name == ' ') {
-			new[i++] = '_';
-		} else if (*name && !strchr("\"&'/:<>@", *name)) {
-			new[i++] = *name;
-		}
-	} while (*(name++));
-
-	new[i] = '\0';
-
-	lower = g_utf8_strdown(new, -1);
-	g_free(new);
-
-	return lower;
-}
-
-char *hipchat_guess_channel_name(struct im_connection *ic, const char *name)
-{
-	struct jabber_data *jd = ic->proto_data;
-	char *slug, *retval, *underscore;
-	
-	if (!(underscore = strchr(jd->username, '_')) || !jd->muc_host) {
-		return NULL;
-	}
-
-	slug = hipchat_make_channel_slug(name);
-
-	/* Get the organization ID from the username, before the underscore */
-	*underscore = '\0';
-
-	retval = g_strdup_printf("%s_%s@%s", jd->username, slug, jd->muc_host);
-
-	*underscore = '_';
-
-	g_free(slug);
-
-	return retval;
-}
Index: protocols/jabber/iasciitab.h
===================================================================
--- protocols/jabber/iasciitab.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/iasciitab.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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,
Index: otocols/jabber/io.c
===================================================================
--- protocols/jabber/io.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,595 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Jabber module - I/O stuff (plain, SSL), queues, etc                      *
-*                                                                           *
-*  Copyright 2006-2012 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.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-\***************************************************************************/
-
-#include "jabber.h"
-#include "ssl_client.h"
-
-static gboolean jabber_write_callback(gpointer data, gint fd, b_input_condition cond);
-static gboolean jabber_write_queue(struct im_connection *ic);
-
-int jabber_write_packet(struct im_connection *ic, struct xt_node *node)
-{
-	char *buf;
-	int st;
-
-	buf = xt_to_string(node);
-	st = jabber_write(ic, buf, strlen(buf));
-	g_free(buf);
-
-	return st;
-}
-
-int jabber_write(struct im_connection *ic, char *buf, int len)
-{
-	struct jabber_data *jd = ic->proto_data;
-	gboolean ret;
-
-	if (jd->flags & JFLAG_XMLCONSOLE && !(ic->flags & OPT_LOGGING_OUT)) {
-		char *msg, *s;
-
-		msg = g_strdup_printf("TX: %s", buf);
-		/* Don't include auth info in XML logs. */
-		if (strncmp(msg, "TX: <auth ", 10) == 0 && (s = strchr(msg, '>'))) {
-			s++;
-			while (*s && *s != '<') {
-				*(s++) = '*';
-			}
-		}
-		imcb_buddy_msg(ic, JABBER_XMLCONSOLE_HANDLE, msg, 0, 0);
-		g_free(msg);
-	}
-
-	if (jd->tx_len == 0) {
-		/* If the queue is empty, allocate a new buffer. */
-		jd->tx_len = len;
-		jd->txq = g_memdup2(buf, len);
-
-		/* Try if we can write it immediately so we don't have to do
-		   it via the event handler. If not, add the handler. (In
-		   most cases it probably won't be necessary.) */
-		if ((ret = jabber_write_queue(ic)) && jd->tx_len > 0) {
-			jd->w_inpa = b_input_add(jd->fd, B_EV_IO_WRITE, jabber_write_callback, ic);
-		}
-	} else {
-		/* Just add it to the buffer if it's already filled. The
-		   event handler is already set. */
-		jd->txq = g_renew(char, jd->txq, jd->tx_len + len);
-		memcpy(jd->txq + jd->tx_len, buf, len);
-		jd->tx_len += len;
-
-		/* The return value for write() doesn't necessarily mean
-		   that everything got sent, it mainly means that the
-		   connection (officially) still exists and can still
-		   be accessed without hitting SIGSEGV. IOW: */
-		ret = TRUE;
-	}
-
-	return ret;
-}
-
-/* Splitting up in two separate functions: One to use as a callback and one
-   to use in the function above to escape from having to wait for the event
-   handler to call us, if possible.
-
-   Two different functions are necessary because of the return values: The
-   callback should only return TRUE if the write was successful AND if the
-   buffer is not empty yet (ie. if the handler has to be called again when
-   the socket is ready for more data). */
-static gboolean jabber_write_callback(gpointer data, gint fd, b_input_condition cond)
-{
-	struct jabber_data *jd = ((struct im_connection *) data)->proto_data;
-
-	return jd->fd != -1 &&
-	       jabber_write_queue(data) &&
-	       jd->tx_len > 0;
-}
-
-static gboolean jabber_write_queue(struct im_connection *ic)
-{
-	struct jabber_data *jd = ic->proto_data;
-	int st;
-
-	if (jd->ssl) {
-		st = ssl_write(jd->ssl, jd->txq, jd->tx_len);
-	} else {
-		st = write(jd->fd, jd->txq, jd->tx_len);
-	}
-
-	if (st == jd->tx_len) {
-		/* We wrote everything, clear the buffer. */
-		g_free(jd->txq);
-		jd->txq = NULL;
-		jd->tx_len = 0;
-
-		return TRUE;
-	} else if (st == 0 || (st < 0 && !ssl_sockerr_again(jd->ssl))) {
-		/* Set fd to -1 to make sure we won't write to it anymore. */
-		closesocket(jd->fd);    /* Shouldn't be necessary after errors? */
-		jd->fd = -1;
-
-		imcb_error(ic, "Short write() to server");
-		imc_logout(ic, TRUE);
-		return FALSE;
-	} else if (st > 0) {
-		char *s;
-
-		s = g_memdup2(jd->txq + st, jd->tx_len - st);
-		jd->tx_len -= st;
-		g_free(jd->txq);
-		jd->txq = s;
-
-		return TRUE;
-	} else {
-		/* Just in case we had EINPROGRESS/EAGAIN: */
-
-		return TRUE;
-	}
-}
-
-static gboolean jabber_feed_input(struct im_connection *ic, char *buf, int size)
-{
-	struct jabber_data *jd = ic->proto_data;
-
-	/* Allow not passing a size for debugging purposes.
-	 * This never happens when reading from the socket */
-	if (size == -1) {
-		size = strlen(buf);
-	}
-
-	/* Parse. */
-	if (xt_feed(jd->xt, buf, size) < 0) {
-		imcb_error(ic, "XML stream error");
-		imc_logout(ic, TRUE);
-		return FALSE;
-	}
-
-	/* Execute all handlers. */
-	if (!xt_handle(jd->xt, NULL, 1)) {
-		/* Don't do anything, the handlers should have
-		   aborted the connection already. */
-		return FALSE;
-	}
-
-	if (jd->flags & JFLAG_STREAM_RESTART) {
-		jd->flags &= ~JFLAG_STREAM_RESTART;
-		jabber_start_stream(ic);
-	}
-
-	/* Garbage collection. */
-	xt_cleanup(jd->xt, NULL, 1);
-
-	/* This is a bit hackish, unfortunately. Although xmltree
-	   has nifty event handler stuff, it only calls handlers
-	   when nodes are complete. Since the server should only
-	   send an opening <stream:stream> tag, we have to check
-	   this by hand. :-( */
-	if (!(jd->flags & JFLAG_STREAM_STARTED) && jd->xt && jd->xt->root) {
-		if (g_strcasecmp(jd->xt->root->name, "stream:stream") == 0) {
-			jd->flags |= JFLAG_STREAM_STARTED;
-
-			/* If there's no version attribute, assume
-			   this is an old server that can't do SASL
-			   authentication. */
-			if (!set_getbool(&ic->acc->set, "sasl") || !sasl_supported(ic)) {
-				/* If there's no version= tag, we suppose
-				   this server does NOT implement: XMPP 1.0,
-				   SASL and TLS. */
-				if (set_getbool(&ic->acc->set, "tls")) {
-					imcb_error(ic, "TLS is turned on for this "
-						   "account, but is not supported by this server");
-					imc_logout(ic, FALSE);
-					return FALSE;
-				} else {
-					if (!jabber_init_iq_auth(ic)) {
-						return FALSE;
-					}
-				}
-			}
-		} else {
-			imcb_error(ic, "XML stream error");
-			imc_logout(ic, TRUE);
-			return FALSE;
-		}
-	}
-
-	return TRUE;
-}
-
-
-static gboolean jabber_read_callback(gpointer data, gint fd, b_input_condition cond)
-{
-	struct im_connection *ic = data;
-	struct jabber_data *jd = ic->proto_data;
-	char buf[512];
-	int st;
-
-	if (jd->fd == -1) {
-		return FALSE;
-	}
-
-	if (jd->ssl) {
-		st = ssl_read(jd->ssl, buf, sizeof(buf));
-	} else {
-		st = read(jd->fd, buf, sizeof(buf));
-	}
-
-	if (st > 0) {
-		if (!jabber_feed_input(ic, buf, st)) {
-			return FALSE;
-		}
-	} else if (st == 0 || (st < 0 && !ssl_sockerr_again(jd->ssl))) {
-		closesocket(jd->fd);
-		jd->fd = -1;
-
-		imcb_error(ic, "Error while reading from server");
-		imc_logout(ic, TRUE);
-		return FALSE;
-	}
-
-	if (ssl_pending(jd->ssl)) {
-		/* OpenSSL empties the TCP buffers completely but may keep some
-		   data in its internap buffers. select() won't see that, but
-		   ssl_pending() does. */
-		return jabber_read_callback(data, fd, cond);
-	} else {
-		return TRUE;
-	}
-}
-
-gboolean jabber_connected_plain(gpointer data, gint source, b_input_condition cond)
-{
-	struct im_connection *ic = data;
-
-	if (g_slist_find(jabber_connections, ic) == NULL) {
-		return FALSE;
-	}
-
-	if (source == -1) {
-		imcb_error(ic, "Could not connect to server");
-		imc_logout(ic, TRUE);
-		return FALSE;
-	}
-
-	imcb_log(ic, "Connected to server, logging in");
-
-	return jabber_start_stream(ic);
-}
-
-gboolean jabber_connected_ssl(gpointer data, int returncode, void *source, b_input_condition cond)
-{
-	struct im_connection *ic = data;
-	struct jabber_data *jd;
-
-	if (g_slist_find(jabber_connections, ic) == NULL) {
-		return FALSE;
-	}
-
-	jd = ic->proto_data;
-
-	if (source == NULL) {
-		/* The SSL connection will be cleaned up by the SSL lib
-		   already, set it to NULL here to prevent a double cleanup: */
-		jd->ssl = NULL;
-
-		if (returncode != 0) {
-			char *err = ssl_verify_strerror(returncode);
-			imcb_error(ic, "Certificate verification problem 0x%x: %s",
-			           returncode, err ? err : "Unknown");
-			g_free(err);
-			imc_logout(ic, FALSE);
-		} else {
-			imcb_error(ic, "Could not connect to server");
-			imc_logout(ic, TRUE);
-		}
-
-		return FALSE;
-	}
-
-	imcb_log(ic, "Connected to server, logging in");
-
-	return jabber_start_stream(ic);
-}
-
-static xt_status jabber_end_of_stream(struct xt_node *node, gpointer data)
-{
-	imc_logout(data, TRUE);
-	return XT_ABORT;
-}
-
-static xt_status jabber_pkt_features(struct xt_node *node, gpointer data)
-{
-	struct im_connection *ic = data;
-	struct jabber_data *jd = ic->proto_data;
-	struct xt_node *c, *reply;
-	int trytls;
-
-	trytls = g_strcasecmp(set_getstr(&ic->acc->set, "tls"), "try") == 0;
-	c = xt_find_node(node->children, "starttls");
-	if (c && !jd->ssl) {
-		/* If the server advertises the STARTTLS feature and if we're
-		   not in a secure connection already: */
-
-		c = xt_find_node(c->children, "required");
-
-		if (c && (!trytls && !set_getbool(&ic->acc->set, "tls"))) {
-			imcb_error(ic, "Server requires TLS connections, but TLS is turned off for this account");
-			imc_logout(ic, FALSE);
-
-			return XT_ABORT;
-		}
-
-		/* Only run this if the tls setting is set to true or try: */
-		if ((trytls || set_getbool(&ic->acc->set, "tls"))) {
-			reply = xt_new_node("starttls", NULL, NULL);
-			xt_add_attr(reply, "xmlns", XMLNS_TLS);
-			if (!jabber_write_packet(ic, reply)) {
-				xt_free_node(reply);
-				return XT_ABORT;
-			}
-			xt_free_node(reply);
-
-			return XT_HANDLED;
-		}
-	} else if (!c && !jd->ssl) {
-		/* If the server does not advertise the STARTTLS feature and
-		   we're not in a secure connection already: (Servers have a
-		   habit of not advertising <starttls/> anymore when already
-		   using SSL/TLS. */
-
-		if (!trytls && set_getbool(&ic->acc->set, "tls")) {
-			imcb_error(ic, "TLS is turned on for this account, but is not supported by this server");
-			imc_logout(ic, FALSE);
-
-			return XT_ABORT;
-		}
-	}
-
-	/* This one used to be in jabber_handlers[], but it has to be done
-	   from here to make sure the TLS session will be initialized
-	   properly before we attempt SASL authentication. */
-	if ((c = xt_find_node(node->children, "mechanisms"))) {
-		if (sasl_pkt_mechanisms(c, data) == XT_ABORT) {
-			return XT_ABORT;
-		}
-	}
-	/* If the server *SEEMS* to support SASL authentication but doesn't
-	   support it after all, we should try to do authentication the
-	   other way. jabber.com doesn't seem to do SASL while it pretends
-	   to be XMPP 1.0 compliant! */
-	else if (!(jd->flags & JFLAG_AUTHENTICATED) && set_getbool(&ic->acc->set, "sasl") && sasl_supported(ic)) {
-		if (!jabber_init_iq_auth(ic)) {
-			return XT_ABORT;
-		}
-	}
-
-	if ((c = xt_find_node(node->children, "bind"))) {
-		jd->flags |= JFLAG_WANT_BIND;
-	}
-
-	if ((c = xt_find_node(node->children, "session"))) {
-		jd->flags |= JFLAG_WANT_SESSION;
-	}
-
-	if (jd->flags & JFLAG_AUTHENTICATED) {
-		return jabber_pkt_bind_sess(ic, NULL, NULL);
-	}
-
-	return XT_HANDLED;
-}
-
-static xt_status jabber_pkt_proceed_tls(struct xt_node *node, gpointer data)
-{
-	struct im_connection *ic = data;
-	struct jabber_data *jd = ic->proto_data;
-	char *xmlns, *tlsname;
-
-	xmlns = xt_find_attr(node, "xmlns");
-
-	/* Just ignore it when it doesn't seem to be TLS-related (is that at
-	   all possible??). */
-	if (!xmlns || strcmp(xmlns, XMLNS_TLS) != 0) {
-		return XT_HANDLED;
-	}
-
-	/* We don't want event handlers to touch our TLS session while it's
-	   still initializing! */
-	b_event_remove(jd->r_inpa);
-	if (jd->tx_len > 0) {
-		/* Actually the write queue should be empty here, but just
-		   to be sure... */
-		b_event_remove(jd->w_inpa);
-		g_free(jd->txq);
-		jd->txq = NULL;
-		jd->tx_len = 0;
-	}
-	jd->w_inpa = jd->r_inpa = 0;
-
-	imcb_log(ic, "Converting stream to TLS");
-
-	jd->flags |= JFLAG_STARTTLS_DONE;
-
-	/* If the user specified a server for the account, use this server as the
-	 * hostname in the certificate verification. Else we use the domain from
-	 * the username. */
-	if (ic->acc->server && *ic->acc->server) {
-		tlsname = ic->acc->server;
-	} else {
-		tlsname = jd->server;
-	}
-
-	jd->ssl = ssl_starttls(jd->fd, tlsname, set_getbool(&ic->acc->set, "tls_verify"),
-	                       jabber_connected_ssl, ic);
-
-	return XT_HANDLED;
-}
-
-static xt_status jabber_pkt_stream_error(struct xt_node *node, gpointer data)
-{
-	struct im_connection *ic = data;
-	struct jabber_data *jd = ic->proto_data;
-	int allow_reconnect = TRUE;
-	struct jabber_error *err;
-	struct xt_node *host;
-
-	if (!(ic->flags & OPT_LOGGED_IN) &&
-	    (host = xt_find_node(node->children, "see-other-host")) &&
-	    host->text) {
-		char *s;
-		int port = set_getint(&ic->acc->set, "port");
-
-		/* Let's try to obey this request, if we're not logged
-		   in yet (i.e. not have too much state yet). */
-		if (jd->ssl) {
-			ssl_disconnect(jd->ssl);
-		}
-		closesocket(jd->fd);
-		b_event_remove(jd->r_inpa);
-		b_event_remove(jd->w_inpa);
-
-		jd->ssl = NULL;
-		jd->r_inpa = jd->w_inpa = 0;
-		jd->flags &= JFLAG_XMLCONSOLE;
-
-		s = strchr(host->text, ':');
-		if (s != NULL) {
-			sscanf(s + 1, "%d", &port);
-		}
-
-		imcb_log(ic, "Redirected to %s", host->text);
-		jd->fd = proxy_connect(host->text, port, jabber_connected_plain, ic);
-
-		return XT_ABORT;
-	}
-
-	err = jabber_error_parse(node, XMLNS_STREAM_ERROR);
-
-	/* Tssk... */
-	if (err->code == NULL) {
-		imcb_error(ic, "Unknown stream error reported by server");
-		imc_logout(ic, allow_reconnect);
-		jabber_error_free(err);
-		return XT_ABORT;
-	}
-
-	/* We know that this is a fatal error. If it's a "conflict" error, we
-	   should turn off auto-reconnect to make sure we won't get some nasty
-	   infinite loop! */
-	if (strcmp(err->code, "conflict") == 0) {
-		imcb_error(ic, "Account and resource used from a different location");
-		allow_reconnect = FALSE;
-	} else if (strcmp(err->code, "not-authorized") == 0) {
-		imcb_error(ic, "Not authorized");
-		allow_reconnect = FALSE;
-	} else {
-		imcb_error(ic, "Stream error: %s%s%s", err->code, err->text ? ": " : "",
-		           err->text ? err->text : "");
-	}
-
-	jabber_error_free(err);
-	imc_logout(ic, allow_reconnect);
-
-	return XT_ABORT;
-}
-
-static xt_status jabber_xmlconsole(struct xt_node *node, gpointer data)
-{
-	struct im_connection *ic = data;
-	struct jabber_data *jd = ic->proto_data;
-
-	if (jd->flags & JFLAG_XMLCONSOLE) {
-		char *msg, *pkt;
-
-		pkt = xt_to_string(node);
-		msg = g_strdup_printf("RX: %s", pkt);
-		imcb_buddy_msg(ic, JABBER_XMLCONSOLE_HANDLE, msg, 0, 0);
-		g_free(msg);
-		g_free(pkt);
-	}
-
-	return XT_NEXT;
-}
-
-static const struct xt_handler_entry jabber_handlers[] = {
-	{ NULL,                 "stream:stream",        jabber_xmlconsole },
-	{ "stream:stream",      "<root>",               jabber_end_of_stream },
-	{ "message",            "stream:stream",        jabber_pkt_message },
-	{ "presence",           "stream:stream",        jabber_pkt_presence },
-	{ "iq",                 "stream:stream",        jabber_pkt_iq },
-	{ "stream:features",    "stream:stream",        jabber_pkt_features },
-	{ "stream:error",       "stream:stream",        jabber_pkt_stream_error },
-	{ "proceed",            "stream:stream",        jabber_pkt_proceed_tls },
-	{ "challenge",          "stream:stream",        sasl_pkt_challenge },
-	{ "success",            "stream:stream",        sasl_pkt_result },
-	{ "failure",            "stream:stream",        sasl_pkt_result },
-	{ NULL,                 NULL,                   NULL }
-};
-
-gboolean jabber_start_stream(struct im_connection *ic)
-{
-	struct jabber_data *jd = ic->proto_data;
-	int st;
-	char *greet;
-
-	/* We'll start our stream now, so prepare everything to receive one
-	   from the server too. */
-	xt_free(jd->xt);        /* In case we're RE-starting. */
-	jd->xt = xt_new(jabber_handlers, ic);
-
-	if (jd->r_inpa <= 0) {
-		jd->r_inpa = b_input_add(jd->fd, B_EV_IO_READ, jabber_read_callback, ic);
-	}
-
-	greet = g_strdup_printf("%s<stream:stream to=\"%s\" xmlns=\"jabber:client\" "
-	                        "xmlns:stream=\"http://etherx.jabber.org/streams\" version=\"1.0\">",
-	                        (jd->flags & JFLAG_STARTTLS_DONE) ? "" : "<?xml version='1.0' ?>",
-	                        jd->server);
-
-	st = jabber_write(ic, greet, strlen(greet));
-
-	g_free(greet);
-
-	return st;
-}
-
-void jabber_end_stream(struct im_connection *ic)
-{
-	struct jabber_data *jd = ic->proto_data;
-
-	/* Let's only do this if the queue is currently empty, otherwise it'd
-	   take too long anyway. */
-	if (jd->tx_len == 0) {
-		char eos[] = "</stream:stream>";
-		struct xt_node *node;
-		int st = 1;
-
-		if (ic->flags & OPT_LOGGED_IN) {
-			node = jabber_make_packet("presence", "unavailable", NULL, NULL);
-			st = jabber_write_packet(ic, node);
-			xt_free_node(node);
-		}
-
-		if (st) {
-			jabber_write(ic, eos, strlen(eos));
-		}
-	}
-}
Index: otocols/jabber/iq.c
===================================================================
--- protocols/jabber/iq.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,1120 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Jabber module - IQ packets                                               *
-*                                                                           *
-*  Copyright 2006-2012 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.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-\***************************************************************************/
-
-#include "jabber.h"
-
-static xt_status jabber_parse_roster(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
-static xt_status jabber_iq_display_vcard(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
-static xt_status jabber_gmail_handle_new(struct im_connection *ic, struct xt_node *node);
-static xt_status jabber_iq_carbons_response(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
-
-xt_status jabber_pkt_iq(struct xt_node *node, gpointer data)
-{
-	struct im_connection *ic = data;
-	struct jabber_data *jd = ic->proto_data;
-	struct xt_node *c, *reply = NULL;
-	char *type, *s;
-	int st, pack = 1;
-
-	type = xt_find_attr(node, "type");
-
-	if (!type) {
-		imcb_error(ic, "Received IQ packet without type.");
-		imc_logout(ic, TRUE);
-		return XT_ABORT;
-	}
-
-	if (strcmp(type, "result") == 0 || strcmp(type, "error") == 0) {
-		return jabber_cache_handle_packet(ic, node);
-	} else if (strcmp(type, "get") == 0) {
-		if (!((c = xt_find_node(node->children, "query")) ||
-		      (c = xt_find_node(node->children, "ping")) ||
-		      (c = xt_find_node(node->children, "time"))) ||
-		    !(s = xt_find_attr(c, "xmlns"))) {
-
-			reply = jabber_make_error_packet(node, "service-unavailable", "cancel", NULL);
-			st = jabber_write_packet(ic, reply);
-			xt_free_node(reply);
-			return st;
-		}
-
-		reply = xt_new_node("query", NULL, NULL);
-		xt_add_attr(reply, "xmlns", s);
-
-		/* Of course this is a very essential query to support. ;-) */
-		if (strcmp(s, XMLNS_VERSION) == 0) {
-			xt_add_child(reply, xt_new_node("name", set_getstr(&ic->acc->set, "user_agent"), NULL));
-			xt_add_child(reply, xt_new_node("version", BITLBEE_VERSION, NULL));
-		} else if (strcmp(s, XMLNS_TIME_OLD) == 0) {
-			time_t time_ep;
-			char buf[1024];
-
-			buf[sizeof(buf) - 1] = 0;
-			time_ep = time(NULL);
-
-			strftime(buf, sizeof(buf) - 1, "%Y%m%dT%H:%M:%S", gmtime(&time_ep));
-			xt_add_child(reply, xt_new_node("utc", buf, NULL));
-
-			strftime(buf, sizeof(buf) - 1, "%Z", localtime(&time_ep));
-			xt_add_child(reply, xt_new_node("tz", buf, NULL));
-		} else if (strcmp(s, XMLNS_TIME) == 0) {
-			time_t time_ep;
-			char buf[1024];
-
-			buf[sizeof(buf) - 1] = 0;
-			time_ep = time(NULL);
-
-			xt_free_node(reply);
-			reply = xt_new_node("time", NULL, NULL);
-			xt_add_attr(reply, "xmlns", XMLNS_TIME);
-
-			strftime(buf, sizeof(buf) - 1, "%Y%m%dT%H:%M:%SZ", gmtime(&time_ep));
-			xt_add_child(reply, xt_new_node("utc", buf, NULL));
-
-			strftime(buf, sizeof(buf) - 1, "%z", localtime(&time_ep));
-			if (strlen(buf) >= 5) {
-				buf[6] = '\0';
-				buf[5] = buf[4];
-				buf[4] = buf[3];
-				buf[3] = ':';
-			}
-			xt_add_child(reply, xt_new_node("tzo", buf, NULL));
-		} else if (strcmp(s, XMLNS_PING) == 0) {
-			xt_free_node(reply);
-			reply = jabber_make_packet("iq", "result", xt_find_attr(node, "from"), NULL);
-			if ((s = xt_find_attr(node, "id"))) {
-				xt_add_attr(reply, "id", s);
-			}
-			pack = 0;
-		} else if (strcmp(s, XMLNS_DISCO_INFO) == 0) {
-			const char *features[] = { XMLNS_DISCO_INFO,
-				                   XMLNS_VERSION,
-				                   XMLNS_TIME_OLD,
-				                   XMLNS_TIME,
-				                   XMLNS_CHATSTATES,
-				                   XMLNS_MUC,
-				                   XMLNS_PING,
-				                   XMLNS_RECEIPTS,
-				                   XMLNS_SI,
-				                   XMLNS_BYTESTREAMS,
-				                   XMLNS_FILETRANSFER,
-				                   XMLNS_CARBONS,
-				                   NULL };
-			const char **f;
-
-			c = xt_new_node("identity", NULL, NULL);
-			xt_add_attr(c, "category", "client");
-			xt_add_attr(c, "type", "pc");
-			xt_add_attr(c, "name", set_getstr(&ic->acc->set, "user_agent"));
-			xt_add_child(reply, c);
-
-			for (f = features; *f; f++) {
-				c = xt_new_node("feature", NULL, NULL);
-				xt_add_attr(c, "var", *f);
-				xt_add_child(reply, c);
-			}
-		} else {
-			xt_free_node(reply);
-			reply = jabber_make_error_packet(node, "service-unavailable", "cancel", NULL);
-			pack = 0;
-		}
-	} else if (strcmp(type, "set") == 0) {
-		if ((c = xt_find_node(node->children, "si")) &&
-		    (s = xt_find_attr(c, "xmlns")) &&
-		    (strcmp(s, XMLNS_SI) == 0)) {
-			return jabber_si_handle_request(ic, node, c);
-		} else if ((c = xt_find_node(node->children, "new-mail")) &&
-		           (s = xt_find_attr(c, "xmlns")) &&
-		           (strcmp(s, XMLNS_GMAILNOTIFY) == 0)) {
-			return jabber_gmail_handle_new(ic, node);
-		} else if (!(c = xt_find_node(node->children, "query")) ||
-		           !(s = xt_find_attr(c, "xmlns"))) {
-			return XT_HANDLED;
-		} else if (strcmp(s, XMLNS_ROSTER) == 0) {
-			/* This is a roster push. XMPP servers send this when someone
-			   was added to (or removed from) the buddy list. AFAIK they're
-			   sent even if we added this buddy in our own session. */
-			int bare_len = strlen(jd->me);
-
-			if ((s = xt_find_attr(node, "from")) == NULL ||
-			    (strncmp(s, jd->me, bare_len) == 0 &&
-			     (s[bare_len] == 0 || s[bare_len] == '/'))) {
-				jabber_parse_roster(ic, node, NULL);
-
-				/* Should we generate a reply here? Don't think it's
-				   very important... */
-			} else {
-				imcb_log(ic, "Warning: %s tried to fake a roster push!", s ? s : "(unknown)");
-
-				xt_free_node(reply);
-				reply = jabber_make_error_packet(node, "not-allowed", "cancel", NULL);
-				pack = 0;
-			}
-		} else if (strcmp(s, XMLNS_BYTESTREAMS) == 0) {
-			/* Bytestream Request (stage 2 of file transfer) */
-			return jabber_bs_recv_request(ic, node, c);
-		} else {
-			xt_free_node(reply);
-			reply = jabber_make_error_packet(node, "feature-not-implemented", "cancel", NULL);
-			pack = 0;
-		}
-	}
-
-	/* If we recognized the xmlns and managed to generate a reply,
-	   finish and send it. */
-	if (reply) {
-		/* Normally we still have to pack it into an iq-result
-		   packet, but for errors, for example, we don't. */
-		if (pack) {
-			reply = jabber_make_packet("iq", "result", xt_find_attr(node, "from"), reply);
-			if ((s = xt_find_attr(node, "id"))) {
-				xt_add_attr(reply, "id", s);
-			}
-		}
-
-		st = jabber_write_packet(ic, reply);
-		xt_free_node(reply);
-		if (!st) {
-			return XT_ABORT;
-		}
-	}
-
-	return XT_HANDLED;
-}
-
-static xt_status jabber_do_iq_auth(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
-static xt_status jabber_finish_iq_auth(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
-
-int jabber_init_iq_auth(struct im_connection *ic)
-{
-	struct jabber_data *jd = ic->proto_data;
-	struct xt_node *node;
-	int st;
-
-	node = xt_new_node("query", NULL, xt_new_node("username", jd->username, NULL));
-	xt_add_attr(node, "xmlns", XMLNS_AUTH);
-	node = jabber_make_packet("iq", "get", NULL, node);
-
-	jabber_cache_add(ic, node, jabber_do_iq_auth);
-	st = jabber_write_packet(ic, node);
-
-	return st;
-}
-
-static xt_status jabber_do_iq_auth(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
-{
-	struct jabber_data *jd = ic->proto_data;
-	struct xt_node *reply, *query;
-	xt_status st;
-	char *s;
-
-	if (!(query = xt_find_node(node->children, "query"))) {
-		imcb_log(ic, "Warning: Received incomplete IQ packet while authenticating");
-		imc_logout(ic, FALSE);
-		return XT_ABORT;
-	}
-
-	/* Time to authenticate ourselves! */
-	reply = xt_new_node("query", NULL, NULL);
-	xt_add_attr(reply, "xmlns", XMLNS_AUTH);
-	xt_add_child(reply, xt_new_node("username", jd->username, NULL));
-	xt_add_child(reply, xt_new_node("resource", set_getstr(&ic->acc->set, "resource"), NULL));
-
-	if (xt_find_node(query->children, "digest") && (s = xt_find_attr(jd->xt->root, "id"))) {
-		/* We can do digest authentication, it seems, and of
-		   course we prefer that. */
-		GChecksum *sha;
-		char hash_hex[41];
-		unsigned char hash[20];
-		int i;
-		gsize digest_len = 20;
-
-		sha = g_checksum_new(G_CHECKSUM_SHA1);
-		g_checksum_update(sha, (unsigned char *) s, strlen(s));
-		g_checksum_update(sha, (unsigned char *) ic->acc->pass, strlen(ic->acc->pass));
-		g_checksum_get_digest(sha, hash, &digest_len);
-		g_checksum_free(sha);
-
-		for (i = 0; i < 20; i++) {
-			sprintf(hash_hex + i * 2, "%02x", hash[i]);
-		}
-
-		xt_add_child(reply, xt_new_node("digest", hash_hex, NULL));
-	} else if (xt_find_node(query->children, "password")) {
-		/* We'll have to stick with plaintext. Let's hope we're using SSL/TLS... */
-		xt_add_child(reply, xt_new_node("password", ic->acc->pass, NULL));
-	} else {
-		xt_free_node(reply);
-
-		imcb_error(ic, "Can't find suitable authentication method");
-		imc_logout(ic, FALSE);
-		return XT_ABORT;
-	}
-
-	reply = jabber_make_packet("iq", "set", NULL, reply);
-	jabber_cache_add(ic, reply, jabber_finish_iq_auth);
-	st = jabber_write_packet(ic, reply);
-
-	return st ? XT_HANDLED : XT_ABORT;
-}
-
-static xt_status jabber_finish_iq_auth(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
-{
-	struct jabber_data *jd = ic->proto_data;
-	char *type;
-
-	if (!(type = xt_find_attr(node, "type"))) {
-		imcb_log(ic, "Warning: Received incomplete IQ packet while authenticating");
-		imc_logout(ic, FALSE);
-		return XT_ABORT;
-	}
-
-	if (strcmp(type, "error") == 0) {
-		imcb_error(ic, "Authentication failure");
-		imc_logout(ic, FALSE);
-		return XT_ABORT;
-	} else if (strcmp(type, "result") == 0) {
-		/* This happens when we just successfully authenticated the
-		   old (non-SASL) way. */
-		jd->flags |= JFLAG_AUTHENTICATED;
-		if (!jabber_get_roster(ic)) {
-			return XT_ABORT;
-		}
-		if (!jabber_iq_disco_server(ic)) {
-			return XT_ABORT;
-		}
-	}
-
-	return XT_HANDLED;
-}
-
-xt_status jabber_pkt_bind_sess(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
-{
-	struct jabber_data *jd = ic->proto_data;
-	struct xt_node *c, *reply = NULL;
-	char *s;
-
-	if (node && (c = xt_find_node(node->children, "bind"))) {
-		c = xt_find_node(c->children, "jid");
-		if (!c || !c->text) {
-			/* Server is crap, but this is no disaster. */
-		} else if (jabber_compare_jid(jd->me, c->text) == 0) {
-			s = strchr(c->text, '/');
-			if (s) {
-				*s = '\0';
-			}
-			jabber_set_me(ic, c->text);
-			if (s) {
-				*s = '/';
-			}
-		} else if (c && c->text_len && (s = strchr(c->text, '/')) &&
-		           strcmp(s + 1, set_getstr(&ic->acc->set, "resource")) != 0) {
-			imcb_log(ic, "Server changed session resource string to `%s'", s + 1);
-		}
-	}
-
-	if (jd->flags & JFLAG_WANT_BIND) {
-		reply = xt_new_node("bind", NULL, xt_new_node("resource", set_getstr(&ic->acc->set, "resource"), NULL));
-		xt_add_attr(reply, "xmlns", XMLNS_BIND);
-		jd->flags &= ~JFLAG_WANT_BIND;
-	} else if (jd->flags & JFLAG_WANT_SESSION) {
-		reply = xt_new_node("session", NULL, NULL);
-		xt_add_attr(reply, "xmlns", XMLNS_SESSION);
-		jd->flags &= ~JFLAG_WANT_SESSION;
-	}
-
-	if (reply != NULL) {
-		reply = jabber_make_packet("iq", "set", NULL, reply);
-		jabber_cache_add(ic, reply, jabber_pkt_bind_sess);
-
-		if (!jabber_write_packet(ic, reply)) {
-			return XT_ABORT;
-		}
-		if (jd->flags & JFLAG_GMAILNOTIFY && node == NULL) {
-			jabber_iq_query_server(ic, jd->server, XMLNS_DISCO_INFO);
-		}
-	} else if ((jd->flags & (JFLAG_WANT_BIND | JFLAG_WANT_SESSION)) == 0) {
-		if (!jabber_get_roster(ic)) {
-			return XT_ABORT;
-		}
-		if (!jabber_iq_disco_server(ic)) {
-			return XT_ABORT;
-		}
-	}
-
-	return XT_HANDLED;
-}
-
-int jabber_get_roster(struct im_connection *ic)
-{
-	struct xt_node *node;
-	int st;
-
-	imcb_log(ic, "Authenticated, requesting buddy list");
-
-	node = xt_new_node("query", NULL, NULL);
-	xt_add_attr(node, "xmlns", XMLNS_ROSTER);
-	node = jabber_make_packet("iq", "get", NULL, node);
-
-	jabber_cache_add(ic, node, jabber_parse_roster);
-	st = jabber_write_packet(ic, node);
-
-	return st;
-}
-
-xt_status jabber_iq_query_gmail(struct im_connection *ic);
-
-static xt_status jabber_gmail_handle_new(struct im_connection *ic, struct xt_node *node)
-{
-	struct xt_node *response;
-	struct jabber_data *jd = ic->proto_data;
-	
-	response = jabber_make_packet("iq", "result", jd->me, NULL);
-
-	jabber_cache_add(ic, response, NULL);
-	if (!jabber_write_packet(ic, response)) {
-		return XT_ABORT;
-	}
-
-	jabber_iq_query_gmail(ic);
-
-	return XT_HANDLED;
-}
-
-static xt_status jabber_parse_roster(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
-{
-	struct jabber_data *jd = ic->proto_data;
-	struct xt_node *query, *c;
-	int initial = (orig != NULL);
-
-	if (!(query = xt_find_node(node->children, "query"))) {
-		imcb_log(ic, "Warning: Received NULL roster packet");
-		return XT_HANDLED;
-	}
-
-	c = query->children;
-	while ((c = xt_find_node(c, "item"))) {
-		struct xt_node *group = xt_find_node(c->children, "group");
-		char *jid = xt_find_attr(c, "jid");
-		char *name = xt_find_attr(c, "name");
-		char *sub = xt_find_attr(c, "subscription");
-		char *mention_name = xt_find_attr(c, "mention_name");
-
-		if (jid && sub) {
-			if ((strcmp(sub, "both") == 0 || strcmp(sub, "to") == 0)) {
-				imcb_add_buddy(ic, jid, (group && group->text_len) ?
-				               group->text : NULL);
-
-				if (name) {
-					imcb_rename_buddy(ic, jid, name);
-				}
-
-				/* This could also be used to set the full name as nick for fb/gtalk,
-				 * but i'm keeping the old (ugly?) default behavior just to be safe */
-				if (mention_name && (jd->flags & JFLAG_HIPCHAT)) {
-					imcb_buddy_nick_hint(ic, jid, mention_name);
-				}
-			} else if (strcmp(sub, "remove") == 0) {
-				jabber_buddy_remove_bare(ic, jid);
-				imcb_remove_buddy(ic, jid, NULL);
-			}
-		}
-
-		c = c->next;
-	}
-
-	if (initial) {
-		imcb_connected(ic);
-	}
-
-	return XT_HANDLED;
-}
-
-int jabber_get_vcard(struct im_connection *ic, char *bare_jid)
-{
-	struct xt_node *node;
-
-	if (strchr(bare_jid, '/')) {
-		return 1;       /* This was an error, but return 0 should only be done if the connection died... */
-
-	}
-	node = xt_new_node("vCard", NULL, NULL);
-	xt_add_attr(node, "xmlns", XMLNS_VCARD);
-	node = jabber_make_packet("iq", "get", bare_jid, node);
-
-	jabber_cache_add(ic, node, jabber_iq_display_vcard);
-	return jabber_write_packet(ic, node);
-}
-
-static xt_status jabber_iq_display_vcard(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
-{
-	struct xt_node *vc, *c, *sc; /* subchild, ic is already in use ;-) */
-	GString *reply;
-	char *s;
-
-	if ((s = xt_find_attr(node, "type")) == NULL ||
-	    strcmp(s, "result") != 0 ||
-	    (vc = xt_find_node(node->children, "vCard")) == NULL) {
-		s = xt_find_attr(orig, "to");   /* If this returns NULL something's wrong.. */
-		imcb_log(ic, "Could not retrieve vCard of %s", s ? s : "(NULL)");
-		return XT_HANDLED;
-	}
-
-	s = xt_find_attr(orig, "to");
-	reply = g_string_new("vCard information for ");
-	reply = g_string_append(reply, s ? s : "(NULL)");
-	reply = g_string_append(reply, ":\n");
-
-	/* I hate this format, I really do... */
-
-	if ((c = xt_find_node(vc->children, "FN")) && c->text_len) {
-		g_string_append_printf(reply, "Name: %s\n", c->text);
-	}
-
-	if ((c = xt_find_node(vc->children, "N")) && c->children) {
-		reply = g_string_append(reply, "Full name:");
-
-		if ((sc = xt_find_node(c->children, "PREFIX")) && sc->text_len) {
-			g_string_append_printf(reply, " %s", sc->text);
-		}
-		if ((sc = xt_find_node(c->children, "GIVEN")) && sc->text_len) {
-			g_string_append_printf(reply, " %s", sc->text);
-		}
-		if ((sc = xt_find_node(c->children, "MIDDLE")) && sc->text_len) {
-			g_string_append_printf(reply, " %s", sc->text);
-		}
-		if ((sc = xt_find_node(c->children, "FAMILY")) && sc->text_len) {
-			g_string_append_printf(reply, " %s", sc->text);
-		}
-		if ((sc = xt_find_node(c->children, "SUFFIX")) && sc->text_len) {
-			g_string_append_printf(reply, " %s", sc->text);
-		}
-
-		reply = g_string_append_c(reply, '\n');
-	}
-
-	if ((c = xt_find_node(vc->children, "NICKNAME")) && c->text_len) {
-		g_string_append_printf(reply, "Nickname: %s\n", c->text);
-	}
-
-	if ((c = xt_find_node(vc->children, "BDAY")) && c->text_len) {
-		g_string_append_printf(reply, "Date of birth: %s\n", c->text);
-	}
-
-	/* Slightly alternative use of for... ;-) */
-	for (c = vc->children; (c = xt_find_node(c, "EMAIL")); c = c->next) {
-		if ((sc = xt_find_node(c->children, "USERID")) == NULL || sc->text_len == 0) {
-			continue;
-		}
-
-		if (xt_find_node(c->children, "HOME")) {
-			s = "Home";
-		} else if (xt_find_node(c->children, "WORK")) {
-			s = "Work";
-		} else {
-			s = "Misc.";
-		}
-
-		g_string_append_printf(reply, "%s e-mail address: %s\n", s, sc->text);
-	}
-
-	if ((c = xt_find_node(vc->children, "URL")) && c->text_len) {
-		g_string_append_printf(reply, "Homepage: %s\n", c->text);
-	}
-
-	/* Slightly alternative use of for... ;-) */
-	for (c = vc->children; (c = xt_find_node(c, "ADR")); c = c->next) {
-		if (xt_find_node(c->children, "HOME")) {
-			s = "Home";
-		} else if (xt_find_node(c->children, "WORK")) {
-			s = "Work";
-		} else {
-			s = "Misc.";
-		}
-
-		g_string_append_printf(reply, "%s address: ", s);
-
-		if ((sc = xt_find_node(c->children, "STREET")) && sc->text_len) {
-			g_string_append_printf(reply, "%s ", sc->text);
-		}
-		if ((sc = xt_find_node(c->children, "EXTADR")) && sc->text_len) {
-			g_string_append_printf(reply, "%s, ", sc->text);
-		}
-		if ((sc = xt_find_node(c->children, "PCODE")) && sc->text_len) {
-			g_string_append_printf(reply, "%s, ", sc->text);
-		}
-		if ((sc = xt_find_node(c->children, "LOCALITY")) && sc->text_len) {
-			g_string_append_printf(reply, "%s, ", sc->text);
-		}
-		if ((sc = xt_find_node(c->children, "REGION")) && sc->text_len) {
-			g_string_append_printf(reply, "%s, ", sc->text);
-		}
-		if ((sc = xt_find_node(c->children, "CTRY")) && sc->text_len) {
-			g_string_append_printf(reply, "%s", sc->text);
-		}
-
-		if (reply->str[reply->len - 2] == ',') {
-			reply = g_string_truncate(reply, reply->len - 2);
-		}
-
-		reply = g_string_append_c(reply, '\n');
-	}
-
-	for (c = vc->children; (c = xt_find_node(c, "TEL")); c = c->next) {
-		if ((sc = xt_find_node(c->children, "NUMBER")) == NULL || sc->text_len == 0) {
-			continue;
-		}
-
-		if (xt_find_node(c->children, "HOME")) {
-			s = "Home";
-		} else if (xt_find_node(c->children, "WORK")) {
-			s = "Work";
-		} else {
-			s = "Misc.";
-		}
-
-		g_string_append_printf(reply, "%s phone number: %s\n", s, sc->text);
-	}
-
-	if ((c = xt_find_node(vc->children, "DESC")) && c->text_len) {
-		g_string_append_printf(reply, "Other information:\n%s", c->text);
-	}
-
-	/* *sigh* */
-
-	imcb_log(ic, "%s", reply->str);
-	g_string_free(reply, TRUE);
-
-	return XT_HANDLED;
-}
-
-static xt_status jabber_add_to_roster_callback(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
-
-int jabber_add_to_roster(struct im_connection *ic, const char *handle, const char *name, const char *group)
-{
-	struct xt_node *node;
-	int st;
-
-	/* Build the item entry */
-	node = xt_new_node("item", NULL, NULL);
-	xt_add_attr(node, "jid", handle);
-	if (name) {
-		xt_add_attr(node, "name", name);
-	}
-	if (group) {
-		xt_add_child(node, xt_new_node("group", group, NULL));
-	}
-
-	/* And pack it into a roster-add packet */
-	node = xt_new_node("query", NULL, node);
-	xt_add_attr(node, "xmlns", XMLNS_ROSTER);
-	node = jabber_make_packet("iq", "set", NULL, node);
-	jabber_cache_add(ic, node, jabber_add_to_roster_callback);
-
-	st = jabber_write_packet(ic, node);
-
-	return st;
-}
-
-static xt_status jabber_add_to_roster_callback(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
-{
-	char *s, *jid = NULL;
-	struct xt_node *c;
-
-	if ((c = xt_find_node(orig->children, "query")) &&
-	    (c = xt_find_node(c->children, "item")) &&
-	    (jid = xt_find_attr(c, "jid")) &&
-	    (s = xt_find_attr(node, "type")) &&
-	    strcmp(s, "result") == 0) {
-		if (bee_user_by_handle(ic->bee, ic, jid) == NULL) {
-			imcb_add_buddy(ic, jid, NULL);
-		}
-	} else {
-		imcb_log(ic, "Error while adding `%s' to your contact list.",
-		         jid ? jid : "(unknown handle)");
-	}
-
-	return XT_HANDLED;
-}
-
-int jabber_remove_from_roster(struct im_connection *ic, char *handle)
-{
-	struct xt_node *node;
-	int st;
-
-	/* Build the item entry */
-	node = xt_new_node("item", NULL, NULL);
-	xt_add_attr(node, "jid", handle);
-	xt_add_attr(node, "subscription", "remove");
-
-	/* And pack it into a roster-add packet */
-	node = xt_new_node("query", NULL, node);
-	xt_add_attr(node, "xmlns", XMLNS_ROSTER);
-	node = jabber_make_packet("iq", "set", NULL, node);
-
-	st = jabber_write_packet(ic, node);
-
-	xt_free_node(node);
-	return st;
-}
-
-xt_status jabber_iq_parse_features(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
-
-xt_status jabber_iq_query_features(struct im_connection *ic, char *bare_jid)
-{
-	struct xt_node *node, *query;
-	struct jabber_buddy *bud;
-
-	if ((bud = jabber_buddy_by_jid(ic, bare_jid, 0)) == NULL) {
-		/* Who cares about the unknown... */
-		imcb_log(ic, "Couldn't find buddy: %s", bare_jid);
-		return XT_HANDLED;
-	}
-
-	if (bud->features) { /* been here already */
-		return XT_HANDLED;
-	}
-
-	node = xt_new_node("query", NULL, NULL);
-	xt_add_attr(node, "xmlns", XMLNS_DISCO_INFO);
-
-	if (!(query = jabber_make_packet("iq", "get", bare_jid, node))) {
-		imcb_log(ic, "WARNING: Couldn't generate feature query");
-		xt_free_node(node);
-		return XT_HANDLED;
-	}
-
-	jabber_cache_add(ic, query, jabber_iq_parse_features);
-
-	return jabber_write_packet(ic, query) ? XT_HANDLED : XT_ABORT;
-}
-
-xt_status jabber_iq_parse_features(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
-{
-	struct xt_node *c;
-	struct jabber_buddy *bud;
-	char *feature, *xmlns, *from;
-
-	if (!(from = xt_find_attr(node, "from")) ||
-	    !(c = xt_find_node(node->children, "query")) ||
-	    !(xmlns = xt_find_attr(c, "xmlns")) ||
-	    !(strcmp(xmlns, XMLNS_DISCO_INFO) == 0)) {
-		imcb_log(ic, "WARNING: Received incomplete IQ-result packet for discover");
-		return XT_HANDLED;
-	}
-	if ((bud = jabber_buddy_by_jid(ic, from, 0)) == NULL) {
-		/* Who cares about the unknown... */
-		imcb_log(ic, "Couldn't find buddy: %s", from);
-		return XT_HANDLED;
-	}
-
-	c = c->children;
-	while ((c = xt_find_node(c, "feature"))) {
-		feature = xt_find_attr(c, "var");
-		if (feature) {
-			bud->features = g_slist_append(bud->features, g_strdup(feature));
-		}
-		c = c->next;
-	}
-
-	return XT_HANDLED;
-}
-
-xt_status jabber_iq_parse_gmail(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
-
-xt_status jabber_iq_query_gmail(struct im_connection *ic)
-{
-	struct xt_node *node, *query;
-	struct jabber_data *jd = ic->proto_data;
-
-	node = xt_new_node("query", NULL, NULL);
-	xt_add_attr(node, "xmlns", XMLNS_GMAILNOTIFY);
-	if (jd->gmail_time) {
-		char *formatted = g_strdup_printf("%" G_GUINT64_FORMAT, (jd->gmail_time + 1));
-		xt_add_attr(node, "newer-than-time", formatted);
-		g_free(formatted);
-	}
-	if (jd->gmail_tid) {
-		xt_add_attr(node, "newer-than-tid", jd->gmail_tid);
-	}
-
-	if (!(query = jabber_make_packet("iq", "get", jd->me, node))) {
-		imcb_log(ic, "WARNING: Couldn't generate server query");
-		xt_free_node(node);
-	}
-
-	jabber_cache_add(ic, query, jabber_iq_parse_gmail);
-
-	return jabber_write_packet(ic, query) ? XT_HANDLED : XT_ABORT;
-}
-
-xt_status jabber_iq_parse_server_features(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
-
-xt_status jabber_iq_query_server(struct im_connection *ic, char *jid, char *xmlns)
-{
-	struct xt_node *node, *query;
-	struct jabber_data *jd = ic->proto_data;
-
-	node = xt_new_node("query", NULL, NULL);
-	xt_add_attr(node, "xmlns", xmlns);
-
-	if (!(query = jabber_make_packet("iq", "get", jid, node))) {
-		imcb_log(ic, "WARNING: Couldn't generate server query");
-		xt_free_node(node);
-	}
-
-	jd->have_streamhosts--;
-	jabber_cache_add(ic, query, jabber_iq_parse_server_features);
-
-	return jabber_write_packet(ic, query) ? XT_HANDLED : XT_ABORT;
-}
-
-xt_status jabber_iq_parse_gmail(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
-{
-	struct xt_node *c;
-	struct jabber_data *jd = ic->proto_data;
-	char *xmlns, *from;
-	guint64 l_time = 0;
-	char *tid = NULL;
-	int max = 0;
-
-	if (!(c = xt_find_node(node->children, "mailbox")) ||
-	    !(from = xt_find_attr(node, "from")) ||
-	    !(xmlns = xt_find_attr(c, "xmlns")) ||
-	    (g_strcmp0(xmlns, XMLNS_GMAILNOTIFY) != 0)) {
-		imcb_log(ic, "WARNING: Received incomplete mailbox packet for gmail notify");
-		return XT_HANDLED;
-	}
-
-	max = set_getint(&ic->acc->set, "mail_notifications_limit");
-	c = c->children;
-
-	while ((max-- > 0) && (c = xt_find_node(c, "mail-thread-info"))) {
-		struct xt_node *s;
-		char *subject = "<no subject>";
-		char *sender = "<no sender>";
-		guint64 t_time;
-
-		t_time = g_ascii_strtoull(xt_find_attr(c, "date"), NULL, 10);
-		if (t_time && t_time > l_time) {
-			l_time = t_time;
-			tid = xt_find_attr(c, "tid");
-		}
-
-		if ((s = xt_find_node(c->children, "senders")) &&
-		    (s = xt_find_node_by_attr(s->children, "sender", "unread", "1"))) {
-			sender = xt_find_attr(s, "name");
-		}
-
-		if ((s = xt_find_node(c->children, "subject")) && s->text) {
-			subject = s->text;
-		}
-
-		imcb_notify_email(ic, "New mail from %s: %s", sender, subject);
-
-		c = c->next;
-	}
-
-	if (l_time && (!jd->gmail_time || l_time > jd->gmail_time)) {
-		jd->gmail_time = l_time;
-		if (tid) {
-			g_free(jd->gmail_tid);
-			jd->gmail_tid = g_strdup(tid);
-		}
-	}
-
-	return XT_HANDLED;
-}
-
-/*
- * Query the server for "items", query each "item" for identities, query each "item" that's a proxy for it's bytestream info
- */
-xt_status jabber_iq_parse_server_features(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
-{
-	struct xt_node *c;
-	struct jabber_data *jd = ic->proto_data;
-	char *xmlns, *from;
-
-	if (!(c = xt_find_node(node->children, "query")) ||
-	    !(from = xt_find_attr(node, "from")) ||
-	    !(xmlns = xt_find_attr(c, "xmlns"))) {
-		imcb_log(ic, "WARNING: Received incomplete IQ-result packet for discover");
-		return XT_HANDLED;
-	}
-
-	jd->have_streamhosts++;
-
-	if (strcmp(xmlns, XMLNS_DISCO_ITEMS) == 0) {
-		char *itemjid;
-
-		/* answer from server */
-
-		c = c->children;
-		while ((c = xt_find_node(c, "item"))) {
-			itemjid = xt_find_attr(c, "jid");
-
-			if (itemjid) {
-				jabber_iq_query_server(ic, itemjid, XMLNS_DISCO_INFO);
-			}
-
-			c = c->next;
-		}
-	} else if (strcmp(xmlns, XMLNS_DISCO_INFO) == 0) {
-		char *category, *type;
-
-		/* answer from potential proxy */
-
-		c = c->children;
-		while ((c = xt_find_node(c, "identity"))) {
-			category = xt_find_attr(c, "category");
-			type = xt_find_attr(c, "type");
-
-			if (type && (strcmp(type, "bytestreams") == 0) &&
-			    category && (strcmp(category, "proxy") == 0)) {
-				jabber_iq_query_server(ic, from, XMLNS_BYTESTREAMS);
-			}
-
-			c = c->next;
-		}
-
-		if (jd->flags & JFLAG_GMAILNOTIFY) {
-			/* search for gmail notification feature */
-			c = xt_find_node(node->children, "query");
-			c = c->children;
-			while ((c = xt_find_node(c, "feature"))) {
-				if (strcmp(xt_find_attr(c, "var"), XMLNS_GMAILNOTIFY) == 0) {
-					jabber_iq_query_gmail(ic);
-				}
-				c = c->next;
-			}
-		}
-
-	} else if (strcmp(xmlns, XMLNS_BYTESTREAMS) == 0) {
-		char *host, *jid, *port_s;
-		int port;
-
-		/* answer from proxy */
-
-		if ((c = xt_find_node(c->children, "streamhost")) &&
-		    (host = xt_find_attr(c, "host")) &&
-		    (port_s = xt_find_attr(c, "port")) &&
-		    (sscanf(port_s, "%d", &port) == 1) &&
-		    (jid = xt_find_attr(c, "jid"))) {
-			jabber_streamhost_t *sh = g_new0(jabber_streamhost_t, 1);
-
-			sh->jid = g_strdup(jid);
-			sh->host = g_strdup(host);
-			g_snprintf(sh->port, sizeof(sh->port), "%u", port);
-
-			imcb_log(ic, "Proxy found: jid %s host %s port %u", jid, host, port);
-			jd->streamhosts = g_slist_append(jd->streamhosts, sh);
-		}
-	}
-
-	if (jd->have_streamhosts == 0) {
-		jd->have_streamhosts++;
-	}
-
-	return XT_HANDLED;
-}
-
-static xt_status jabber_iq_version_response(struct im_connection *ic,
-                                            struct xt_node *node, struct xt_node *orig);
-
-void jabber_iq_version_send(struct im_connection *ic, struct jabber_buddy *bud, void *data)
-{
-	struct xt_node *node, *query;
-
-	node = xt_new_node("query", NULL, NULL);
-	xt_add_attr(node, "xmlns", XMLNS_VERSION);
-	query = jabber_make_packet("iq", "get", bud->full_jid, node);
-	jabber_cache_add(ic, query, jabber_iq_version_response);
-
-	jabber_write_packet(ic, query);
-}
-
-static xt_status jabber_iq_version_response(struct im_connection *ic,
-                                            struct xt_node *node, struct xt_node *orig)
-{
-	struct xt_node *query;
-	GString *rets;
-	char *s;
-	char *ret[2] = {};
-	bee_user_t *bu;
-	struct jabber_buddy *bud = NULL;
-
-	if ((s = xt_find_attr(node, "from")) &&
-	    (bud = jabber_buddy_by_jid(ic, s, 0)) &&
-	    (query = xt_find_node(node->children, "query")) &&
-	    (bu = bee_user_by_handle(ic->bee, ic, bud->bare_jid))) {
-		rets = g_string_new("Resource ");
-		g_string_append(rets, bud->resource);
-	} else {
-		return XT_HANDLED;
-	}
-
-	for (query = query->children; query; query = query->next) {
-		if (query->text_len > 0) {
-			g_string_append_printf(rets, " %s: %s,", query->name, query->text);
-		}
-	}
-
-	g_string_truncate(rets, rets->len - 1);
-	ret[0] = rets->str;
-	imcb_buddy_action_response(bu, "VERSION", ret, NULL);
-	g_string_free(rets, TRUE);
-
-	return XT_HANDLED;
-}
-
-static xt_status jabber_iq_disco_server_response(struct im_connection *ic,
-                                                 struct xt_node *node, struct xt_node *orig);
-
-int jabber_iq_disco_server(struct im_connection *ic)
-{
-	struct xt_node *node, *iq;
-	struct jabber_data *jd = ic->proto_data;
-
-	node = xt_new_node("query", NULL, NULL);
-	xt_add_attr(node, "xmlns", XMLNS_DISCO_INFO);
-	iq = jabber_make_packet("iq", "get", jd->server, node);
-
-	jabber_cache_add(ic, iq, jabber_iq_disco_server_response);
-	return jabber_write_packet(ic, iq);
-}
-
-static xt_status jabber_iq_disco_server_response(struct im_connection *ic,
-                                                 struct xt_node *node, struct xt_node *orig)
-{
-	struct jabber_data *jd = ic->proto_data;
-	struct xt_node *query, *id;
-
-	if (!(query = xt_find_node(node->children, "query"))) {
-		return XT_HANDLED;
-	}
-
-	if (xt_find_node_by_attr(query->children, "feature", "var", XMLNS_CARBONS) &&
-	    set_getbool(&ic->acc->set, "carbons")) {
-
-		struct xt_node *enable, *iq;
-
-		enable = xt_new_node("enable", NULL, NULL);
-		xt_add_attr(enable, "xmlns", XMLNS_CARBONS);
-		iq = jabber_make_packet("iq", "set", NULL, enable);
-
-		jabber_cache_add(ic, iq, jabber_iq_carbons_response);
-		jabber_write_packet(ic, iq);
-	}
-
-	if ((id = xt_find_node(query->children, "identity"))) {
-		char *cat, *type, *name;
-
-		if (!(cat = xt_find_attr(id, "category")) ||
-		    !(type = xt_find_attr(id, "type")) ||
-		    !(name = xt_find_attr(id, "name"))) {
-			return XT_HANDLED;
-		}
-
-		if (strcmp(cat, "server") == 0 && strcmp(type, "im") == 0 &&
-		    strstr(name, "Google") != NULL) {
-			jd->flags |= JFLAG_GTALK;
-		}
-	}
-
-	return XT_HANDLED;
-}
-
-static xt_status jabber_iq_carbons_response(struct im_connection *ic,
-                                            struct xt_node *node, struct xt_node *orig)
-{
-	struct jabber_error *err;
-
-	if ((err = jabber_error_parse(xt_find_node(node->children, "error"), XMLNS_STANZA_ERROR))) {
-		imcb_error(ic, "Error enabling carbons: %s%s%s",
-		           err->code, err->text ? ": " : "", err->text ? err->text : "");
-		jabber_error_free(err);
-	} else {
-		imcb_log(ic, "Carbons enabled");
-	}
-
-	return XT_HANDLED;
-}
-
-xt_status jabber_iq_disco_muc_response(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
-
-int jabber_iq_disco_muc(struct im_connection *ic, const char *muc_server)
-{
-	struct xt_node *node;
-	int st;
-
-	node = xt_new_node("query", NULL, NULL);
-	xt_add_attr(node, "xmlns", XMLNS_DISCO_ITEMS);
-	node = jabber_make_packet("iq", "get", (char *) muc_server, node);
-
-	jabber_cache_add(ic, node, jabber_iq_disco_muc_response);
-	st = jabber_write_packet(ic, node);
-
-	return st;
-}
-
-xt_status jabber_iq_disco_muc_response(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
-{
-	struct xt_node *query, *c;
-	struct jabber_error *err;
-	GSList *rooms =  NULL;
-
-	if ((err = jabber_error_parse(xt_find_node(node->children, "error"), XMLNS_STANZA_ERROR))) {
-		imcb_error(ic, "The server replied with an error: %s%s%s",
-		           err->code, err->text ? ": " : "", err->text ? err->text : "");
-		jabber_error_free(err);
-		return XT_HANDLED;
-	}
-
-	if (!(query = xt_find_node(node->children, "query"))) {
-		imcb_error(ic, "Received incomplete MUC list reply");
-		return XT_HANDLED;
-	}
-
-	c = query->children;
-	while ((c = xt_find_node(c, "item"))) {
-		char *jid = xt_find_attr(c, "jid");
-
-		if (!jid || !strchr(jid, '@')) {
-			c = c->next;
-			continue;
-		}
-
-		bee_chat_info_t *ci = g_new(bee_chat_info_t, 1);
-		ci->title = g_strdup(xt_find_attr(c, "jid"));
-		ci->topic = g_strdup(xt_find_attr(c, "name"));
-		rooms = g_slist_prepend(rooms, ci);
-
-		c = c->next;
-	}
-
-	imcb_chat_list_free(ic);
-	ic->chatlist = g_slist_reverse(rooms);
-	imcb_chat_list_finish(ic);
-
-	return XT_HANDLED;
-}
Index: protocols/jabber/jabber.c
===================================================================
--- protocols/jabber/jabber.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ protocols/jabber/jabber.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -1,808 +1,2445 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Jabber module - Main file                                                *
-*                                                                           *
-*  Copyright 2006-2013 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.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-\***************************************************************************/
-
-#include <glib.h>
+/* -*- 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 <unistd.h>
-#include <ctype.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"
-#include "xmltree.h"
-#include "bitlbee.h"
-#include "jabber.h"
-#include "oauth.h"
-
-GSList *jabber_connections;
-
-/* First enty is the default */
-static const int jabber_port_list[] = {
-	5222,
-	5223,
-	5220,
-	5221,
-	5224,
-	5225,
-	5226,
-	5227,
-	5228,
-	5229,
-	80,
-	443,
-	0
+
+/* 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;
 };
 
-static void jabber_init(account_t *acc)
-{
-	set_t *s;
-	char str[16];
-
-	s = set_add(&acc->set, "activity_timeout", "600", set_eval_int, acc);
-
-	s = set_add(&acc->set, "display_name", NULL, NULL, acc);
-
-	g_snprintf(str, sizeof(str), "%d", jabber_port_list[0]);
-	s = set_add(&acc->set, "port", str, set_eval_int, acc);
-	s->flags |= ACC_SET_OFFLINE_ONLY;
-
-	s = set_add(&acc->set, "priority", "0", set_eval_priority, acc);
-
-	s = set_add(&acc->set, "proxy", "<local>;<auto>", NULL, acc);
-
-	s = set_add(&acc->set, "resource", "BitlBee", NULL, acc);
-	s->flags |= ACC_SET_OFFLINE_ONLY;
-
-	s = set_add(&acc->set, "resource_select", "activity", NULL, acc);
-
-	s = set_add(&acc->set, "sasl", "true", set_eval_bool, acc);
-	s->flags |= ACC_SET_OFFLINE_ONLY | SET_HIDDEN_DEFAULT;
-
-	s = set_add(&acc->set, "server", NULL, set_eval_account, acc);
-	s->flags |= SET_NOSAVE | ACC_SET_OFFLINE_ONLY | SET_NULL_OK;
-
-	set_add(&acc->set, "oauth", "false", set_eval_oauth, acc);
-
-	if (strcmp(acc->prpl->name, "hipchat") == 0) {
-		set_setstr(&acc->set, "server", "chat.hipchat.com");
+/*
+ * 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 {
-		/* this reuses set_eval_oauth, which clears the password */
-		set_add(&acc->set, "anonymous", "false", set_eval_oauth, acc);
-	}
-
-	s = set_add(&acc->set, "ssl", "false", set_eval_bool, acc);
-	s->flags |= ACC_SET_OFFLINE_ONLY;
-
-	s = set_add(&acc->set, "tls", "true", set_eval_tls, acc);
-	s->flags |= ACC_SET_OFFLINE_ONLY;
-
-	s = set_add(&acc->set, "tls_verify", "true", set_eval_bool, acc);
-	s->flags |= ACC_SET_OFFLINE_ONLY;
-
-	s = set_add(&acc->set, "user_agent", "BitlBee", NULL, acc);
-
-	s = set_add(&acc->set, "xmlconsole", "false", set_eval_bool, acc);
-
-	s = set_add(&acc->set, "mail_notifications", "false", set_eval_bool, acc);
-	s->flags |= ACC_SET_OFFLINE_ONLY;
-
-	/* changing this is rarely needed so keeping it secret */
-	s = set_add(&acc->set, "mail_notifications_limit", "5", set_eval_int, acc);
-	s->flags |= SET_HIDDEN_DEFAULT;
-
-	s = set_add(&acc->set, "mail_notifications_handle", NULL, NULL, acc);
-	s->flags |= ACC_SET_OFFLINE_ONLY | SET_NULL_OK;
-
-	s = set_add(&acc->set, "carbons", "true", set_eval_bool, acc);
-	s->flags |= ACC_SET_OFFLINE_ONLY;
-
-	acc->flags |= ACC_FLAG_AWAY_MESSAGE | ACC_FLAG_STATUS_MESSAGE |
-	              ACC_FLAG_HANDLE_DOMAINS;
-}
-
-static void jabber_generate_id_hash(struct jabber_data *jd);
-
-static void jabber_login(account_t *acc)
-{
-	struct im_connection *ic = imcb_new(acc);
-	struct jabber_data *jd = g_new0(struct jabber_data, 1);
-	char *s;
-
-	/* For now this is needed in the _connected() handlers if using
-	   GLib event handling, to make sure we're not handling events
-	   on dead connections. */
-	jabber_connections = g_slist_prepend(jabber_connections, ic);
-
-	jd->ic = ic;
-	ic->proto_data = jd;
-
-	jabber_set_me(ic, acc->user);
-
-	jd->fd = jd->r_inpa = jd->w_inpa = -1;
-
-	if (strcmp(acc->prpl->name, "hipchat") == 0) {
-		jd->flags |= JFLAG_HIPCHAT;
-	}
-
-	if (jd->server == NULL) {
-		imcb_error(ic, "Incomplete account name (format it like <username@jabberserver.name>)");
-		imc_logout(ic, FALSE);
+		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;
-	}
-
-	if (strstr(jd->server, ".facebook.com")) {
-		imcb_error(ic, "Facebook's XMPP service is gone. Try this instead: https://wiki.bitlbee.org/HowtoFacebookMQTT");
-		imc_logout(ic, FALSE);
+
+	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;
-	}
-
-	if ((s = strchr(jd->server, '/'))) {
-		*s = 0;
-		set_setstr(&acc->set, "resource", s + 1);
-
-		/* Also remove the /resource from the original variable so we
-		   won't have to do this again every time. */
-		s = strchr(acc->user, '/');
-		*s = 0;
-	}
-
-	jd->node_cache = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, jabber_cache_entry_free);
-	jd->buddies = g_hash_table_new(g_str_hash, g_str_equal);
-
-	if (set_getbool(&acc->set, "oauth")) {
-		GSList *p_in = NULL;
-		const char *tok;
-
-		jd->fd = jd->r_inpa = jd->w_inpa = -1;
-
-		/* There are no other options atm, so assume google for everything 
-		   Facebook and MSN XMPP used to be here. RIP. */
-		jd->oauth2_service = &oauth2_service_google;
-
-		oauth_params_parse(&p_in, ic->acc->pass);
-
-		/* First see if we have a refresh token, in which case any
-		   access token we *might* have has probably expired already
-		   anyway. */
-		if ((tok = oauth_params_get(&p_in, "refresh_token"))) {
-			sasl_oauth2_refresh(ic, tok);
-		}
-		/* If we don't have a refresh token, let's hope the access
-		   token is still usable. */
-		else if ((tok = oauth_params_get(&p_in, "access_token"))) {
-			jd->oauth2_access_token = g_strdup(tok);
-			jabber_connect(ic);
-		}
-		/* If we don't have any, start the OAuth process now. Don't
-		   even open an XMPP connection yet. */
+
+	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 {
-			sasl_oauth2_init(ic);
-			ic->flags |= OPT_SLOW_LOGIN;
-		}
-
-		oauth_params_free(&p_in);
+			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 {
-		jabber_connect(ic);
-	}
-}
-
-static void jabber_xmlconsole_enable(struct im_connection *ic)
-{
-	struct jabber_data *jd = ic->proto_data;
-	const char *handle = JABBER_XMLCONSOLE_HANDLE;
-	bee_user_t *bu;
+		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);
 	
-	jd->flags |= JFLAG_XMLCONSOLE;
-
-	if (!(bu = bee_user_by_handle(ic->bee, ic, handle))) {
-		bu = bee_user_new(ic->bee, ic, handle, 0);
-		bu->flags |= BEE_USER_NOOTR;
-	}
-}
-
-/* Separate this from jabber_login() so we can do OAuth first if necessary.
-   Putting this in io.c would probably be more correct. */
-void jabber_connect(struct im_connection *ic)
-{
-	account_t *acc = ic->acc;
-	struct jabber_data *jd = ic->proto_data;
-	int i;
-	char *connect_to;
-	struct ns_srv_reply **srvl = NULL, *srv = NULL;
-
-	/* Figure out the hostname to connect to. */
-	if (acc->server && *acc->server) {
-		connect_to = acc->server;
-	} else if ((srvl = srv_lookup("xmpp-client", "tcp", jd->server)) ||
-	           (srvl = srv_lookup("jabber-client", "tcp", jd->server))) {
-		/* Find the lowest-priority one. These usually come
-		   back in random/shuffled order. Not looking at
-		   weights etc for now. */
-		srv = *srvl;
-		for (i = 1; srvl[i]; i++) {
-			if (srvl[i]->prio < srv->prio) {
-				srv = srvl[i];
-			}
-		}
-
-		connect_to = srv->name;
+}
+
+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 {
-		connect_to = jd->server;
-	}
-
-	imcb_log(ic, "Connecting");
-
-	for (i = 0; jabber_port_list[i] > 0; i++) {
-		if (set_getint(&acc->set, "port") == jabber_port_list[i]) {
-			break;
-		}
-	}
-
-	if (jabber_port_list[i] == 0) {
-		imcb_log(ic, "Illegal port number");
-		imc_logout(ic, FALSE);
+		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;
 	}
 
-	/* For non-SSL connections we can try to use the port # from the SRV
-	   reply, but let's not do that when using SSL, SSL usually runs on
-	   non-standard ports... */
-	if (set_getbool(&acc->set, "ssl")) {
-		jd->ssl = ssl_connect(connect_to, set_getint(&acc->set, "port"), set_getbool(&acc->set,
-		                                                                             "tls_verify"), jabber_connected_ssl,
-		                      ic);
-		jd->fd = jd->ssl ? ssl_getfd(jd->ssl) : -1;
+	if(who->resource != NULL && (who->resource)[0] != '\0') {
+		buddy = g_strdup_printf("%s@%s/%s", who->user, who->server, who->resource);
 	} else {
-		jd->fd = proxy_connect(connect_to, srv ? srv->port : set_getint(&acc->set,
-		                                                                "port"), jabber_connected_plain, ic);
-	}
-	srv_free(srvl);
-
-	if (jd->fd == -1) {
-		imcb_error(ic, "Could not connect to server");
-		imc_logout(ic, TRUE);
-
-		return;
-	}
-
-	if (set_getbool(&acc->set, "xmlconsole")) {
-		jabber_xmlconsole_enable(ic);
-	}
-
-	if (set_getbool(&acc->set, "mail_notifications")) {
-		/* It's gmail specific, but it checks for server support before enabling it */
-		jd->flags |= JFLAG_GMAILNOTIFY;
-		if (set_getstr(&acc->set, "mail_notifications_handle")) {
-			imcb_add_buddy(ic, set_getstr(&acc->set, "mail_notifications_handle"), NULL);
-		}
-	}
-
-	jabber_generate_id_hash(jd);
-}
-
-/* This generates an unfinished g_checksum variable. Every time we generate
-   an ID, we finish the state by adding a sequence number and take the hash. */
-static void jabber_generate_id_hash(struct jabber_data *jd)
-{
-	guint8 binbuf[4];
-	char *s;
-
-	jd->cached_id_prefix = g_checksum_new(G_CHECKSUM_MD5);
-	g_checksum_update(jd->cached_id_prefix, jd->username, strlen(jd->username));
-	g_checksum_update(jd->cached_id_prefix, jd->server, strlen(jd->server));
-	s = set_getstr(&jd->ic->acc->set, "resource");
-	g_checksum_update(jd->cached_id_prefix, s, strlen(s));
-	random_bytes(binbuf, 4);
-	g_checksum_update(jd->cached_id_prefix, binbuf, 4);
-}
-
-static void jabber_logout(struct im_connection *ic)
-{
-	struct jabber_data *jd = ic->proto_data;
-
-	imcb_chat_list_free(ic);
-
-	while (jd->filetransfers) {
-		imcb_file_canceled(ic, (( struct jabber_transfer *) jd->filetransfers->data)->ft, "Logging out");
-	}
-
-	while (jd->streamhosts) {
-		jabber_streamhost_t *sh = jd->streamhosts->data;
-		jd->streamhosts = g_slist_remove(jd->streamhosts, sh);
-		g_free(sh->jid);
-		g_free(sh->host);
-		g_free(sh);
-	}
-
-	if (jd->fd >= 0) {
-		jabber_end_stream(ic);
-	}
-
-	while (ic->groupchats) {
-		jabber_chat_free(ic->groupchats->data);
-	}
-
-	if (jd->r_inpa >= 0) {
-		b_event_remove(jd->r_inpa);
-	}
-	if (jd->w_inpa >= 0) {
-		b_event_remove(jd->w_inpa);
-	}
-
-	if (jd->ssl) {
-		ssl_disconnect(jd->ssl);
-	}
-	if (jd->fd >= 0) {
-		proxy_disconnect(jd->fd);
-	}
-
-	if (jd->tx_len) {
-		g_free(jd->txq);
-	}
-
-	if (jd->node_cache) {
-		g_hash_table_destroy(jd->node_cache);
-	}
-
-	if (jd->buddies) {
-		jabber_buddy_remove_all(ic);
-	}
-
-	xt_free(jd->xt);
-
-	g_checksum_free(jd->cached_id_prefix);
-
-	g_free(jd->oauth2_access_token);
-	g_free(jd->away_message);
-	g_free(jd->internal_jid);
-	g_free(jd->gmail_tid);
-	g_free(jd->muc_host);
-	g_free(jd->username);
-	g_free(jd->me);
-	g_free(jd);
-
-	jabber_connections = g_slist_remove(jabber_connections, ic);
-}
-
-static int jabber_buddy_msg(struct im_connection *ic, char *who, char *message, int flags)
-{
-	struct jabber_data *jd = ic->proto_data;
-	struct jabber_buddy *bud;
-	struct xt_node *node;
-	char *s;
-	int st;
-
-	if (g_strcasecmp(who, JABBER_XMLCONSOLE_HANDLE) == 0) {
-		return jabber_write(ic, message, strlen(message));
-	}
-
-	if (g_strcasecmp(who, JABBER_OAUTH_HANDLE) == 0 &&
-	    !(jd->flags & OPT_LOGGED_IN) && jd->fd == -1) {
-
-		if (jd->flags & JFLAG_HIPCHAT) {
-			sasl_oauth2_got_token(ic, message, NULL, NULL);
-			return 1;
-		} else if (sasl_oauth2_get_refresh_token(ic, message)) {
-			return 1;
-		} else {
-			imcb_error(ic, "OAuth failure");
-			imc_logout(ic, TRUE);
-			return 0;
-		}
-	}
-
-	if ((s = strchr(who, '=')) && jabber_chat_by_jid(ic, s + 1)) {
-		bud = jabber_buddy_by_ext_jid(ic, who, 0);
-	} else {
-		bud = jabber_buddy_by_jid(ic, who, GET_BUDDY_BARE_OK);
-	}
-
-	node = xt_new_node("body", message, NULL);
-	node = jabber_make_packet("message", "chat", bud ? bud->full_jid : who, node);
-
-	if (bud && (jd->flags & JFLAG_WANT_TYPING) &&
-	    ((bud->flags & JBFLAG_DOES_XEP85) ||
-	     !(bud->flags & JBFLAG_PROBED_XEP85))) {
-		struct xt_node *act;
-
-		/* If the user likes typing notification and if we don't know
-		   (and didn't probe before) if this resource supports XEP85,
-		   include a probe in this packet now. Also, if we know this
-		   buddy does support XEP85, we have to send this <active/>
-		   tag to tell that the user stopped typing (well, that's what
-		   we guess when s/he pressed Enter...). */
-		act = xt_new_node("active", NULL, NULL);
-		xt_add_attr(act, "xmlns", XMLNS_CHATSTATES);
-		xt_add_child(node, act);
-
-		/* Just make sure we do this only once. */
-		bud->flags |= JBFLAG_PROBED_XEP85;
-	}
-
-	/* XEP-0364 suggests we add message processing hints (XEP-0334) to OTR messages,
-	   mostly to avoid carbons (XEP-0280) and server-side message archiving.
-	   OTR messages are roughly like this: /^\?OTR(.*\?| Error:|:)/
-	   But I'm going to simplify it to messages starting with "?OTR". */
-	if (g_str_has_prefix(message, "?OTR")) {
-		int i;
-		char *hints[] = {
-			"no-copy", XMLNS_HINTS,
-			"no-permanent-store", XMLNS_HINTS,
-			"private", XMLNS_CARBONS,
-			NULL
-		};
-			
-		for (i = 0; hints[i]; i += 2) {
-			struct xt_node *hint;
-			hint = xt_new_node(hints[i], NULL, NULL);
-			xt_add_attr(hint, "xmlns", hints[i + 1]);
-			xt_add_child(node, hint);
-		}
-	}
-
-	st = jabber_write_packet(ic, node);
-	xt_free_node(node);
-
-	return st;
-}
-
-static GList *jabber_away_states(struct im_connection *ic)
-{
-	static GList *l = NULL;
-	int i;
-
-	if (l == NULL) {
-		for (i = 0; jabber_away_state_list[i].full_name; i++) {
-			l = g_list_append(l, (void *) jabber_away_state_list[i].full_name);
-		}
-	}
-
-	return l;
-}
-
-static void jabber_get_info(struct im_connection *ic, char *who)
-{
-	struct jabber_buddy *bud;
-
-	bud = jabber_buddy_by_jid(ic, who, GET_BUDDY_FIRST);
-
-	while (bud) {
-		imcb_log(ic, "Buddy %s (%d) information:", bud->full_jid, bud->priority);
-		if (bud->away_state) {
-			imcb_log(ic, "Away state: %s", bud->away_state->full_name);
-		}
-		imcb_log(ic, "Status message: %s", bud->away_message ? bud->away_message : "(none)");
-
-		bud = bud->next;
-	}
-
-	jabber_get_vcard(ic, who);
-}
-
-static void jabber_set_away(struct im_connection *ic, char *state_txt, char *message)
-{
-	struct jabber_data *jd = ic->proto_data;
-
-	/* state_txt == NULL -> Not away.
-	   Unknown state -> fall back to the first defined away state. */
-	if (state_txt == NULL) {
-		jd->away_state = NULL;
-	} else if ((jd->away_state = jabber_away_state_by_name(state_txt)) == NULL) {
-		jd->away_state = jabber_away_state_list;
-	}
-
-	g_free(jd->away_message);
-	jd->away_message = (message && *message) ? g_strdup(message) : NULL;
-
-	presence_send_update(ic);
-}
-
-static void jabber_add_buddy(struct im_connection *ic, char *who, char *group)
-{
-	if (g_strcasecmp(who, JABBER_XMLCONSOLE_HANDLE) == 0) {
-		jabber_xmlconsole_enable(ic);
-		return;
-	}
-
-	if (jabber_add_to_roster(ic, who, NULL, group)) {
-		presence_send_request(ic, who, "subscribe");
-	}
-}
-
-static void jabber_remove_buddy(struct im_connection *ic, char *who, char *group)
-{
-	struct jabber_data *jd = ic->proto_data;
-
-	if (g_strcasecmp(who, JABBER_XMLCONSOLE_HANDLE) == 0) {
-		jd->flags &= ~JFLAG_XMLCONSOLE;
-		/* Not necessary for now. And for now the code isn't too
-		   happy if the buddy is completely gone right after calling
-		   this function already.
-		imcb_remove_buddy( ic, JABBER_XMLCONSOLE_HANDLE, NULL );
-		*/
-		return;
-	}
-
-	/* We should always do this part. Clean up our administration a little bit. */
-	jabber_buddy_remove_bare(ic, who);
-
-	if (jabber_remove_from_roster(ic, who)) {
-		presence_send_request(ic, who, "unsubscribe");
-	}
-}
-
-static struct groupchat *jabber_chat_join_(struct im_connection *ic, const char *room, const char *nick,
-                                           const char *password, set_t **sets)
-{
-	struct jabber_data *jd = ic->proto_data;
-	char *final_nick;
-
-	/* Ignore the passed nick parameter if we have our own default */
-	if (!(final_nick = set_getstr(sets, "nick")) &&
-	    !(final_nick = set_getstr(&ic->acc->set, "display_name"))) {
-		/* Well, whatever, actually use the provided default, then */
-		final_nick = (char *) nick;
-	}
-
-	if (jd->flags & JFLAG_HIPCHAT && jd->muc_host && !g_str_has_suffix(room, jd->muc_host)) {
-		char *guessed_name = hipchat_guess_channel_name(ic, room);
-		if (guessed_name) {
-			set_setstr(sets, "room", guessed_name);
-			g_free(guessed_name);
-
-			/* call this same function again with the fixed name */
-			return jabber_chat_join_(ic, set_getstr(sets, "room"), nick, password, sets);
-		}
-	}
-
-	if (strchr(room, '@') == NULL) {
-		imcb_error(ic, "%s is not a valid Jabber room name. Maybe you mean %s@conference.%s?",
-		           room, room, jd->server);
-	} else {
-		struct groupchat *old;
-
-		if ((old = jabber_chat_by_jid(ic, room))) {
-			imcb_log(ic, "Warning: Already present in chat `%s' - trying to join anyway", room);
-			jabber_chat_free(old);
-		}
-
-		/* jabber_chat_join without the underscore is the conference.c one */
-		return jabber_chat_join(ic, room, final_nick, set_getstr(sets, "password"),
-		                        set_getbool(sets, "always_use_nicks"));
-	}
-
-	return NULL;
-}
-
-static struct groupchat *jabber_chat_with_(struct im_connection *ic, char *who)
-{
-	return jabber_chat_with(ic, who);
-}
-
-static void jabber_chat_list_(struct im_connection *ic, const char *server)
-{
-	struct jabber_data *jd = ic->proto_data;
-
-	if (server && *server) {
-		jabber_iq_disco_muc(ic, server);
-	} else if (jd->muc_host && *jd->muc_host) {
-		jabber_iq_disco_muc(ic, jd->muc_host);
-	} else {
-		/* throw an error here, don't query conference.[server] directly.
-		 * for things like jabber.org it gets you 18000 results of garbage */
-		imcb_error(ic, "Please specify a server name such as `conference.%s'", jd->server);
-	}
-}
-
-static void jabber_chat_msg_(struct groupchat *c, char *message, int flags)
-{
-	if (c && message) {
-		jabber_chat_msg(c, message, flags);
-	}
-}
-
-static void jabber_chat_topic_(struct groupchat *c, char *topic)
-{
-	if (c && topic) {
-		jabber_chat_topic(c, topic);
-	}
-}
-
-static void jabber_chat_leave_(struct groupchat *c)
-{
-	if (c) {
-		jabber_chat_leave(c, NULL);
-	}
-}
-
-static void jabber_chat_invite_(struct groupchat *c, char *who, char *msg)
-{
-	struct jabber_data *jd = c->ic->proto_data;
-	struct jabber_chat *jc = c->data;
-	gchar *msg_alt = NULL;
-
-	if (msg == NULL) {
-		msg_alt = g_strdup_printf("%s invited you to %s", jd->me, jc->name);
-	}
-
-	if (c && who) {
-		jabber_chat_invite(c, who, msg ? msg : msg_alt);
-	}
-
-	g_free(msg_alt);
-}
-
-static void jabber_keepalive(struct im_connection *ic)
-{
-	/* Just any whitespace character is enough as a keepalive for XMPP sessions. */
-	if (!jabber_write(ic, "\n", 1)) {
-		return;
-	}
-
-	/* This runs the garbage collection every minute, which means every packet
-	   is in the cache for about a minute (which should be enough AFAIK). */
-	jabber_cache_clean(ic);
-}
-
-static int jabber_send_typing(struct im_connection *ic, char *who, int typing)
-{
-	struct jabber_data *jd = ic->proto_data;
-	struct jabber_buddy *bud, *bare;
-
-	/* Enable typing notification related code from now. */
-	jd->flags |= JFLAG_WANT_TYPING;
-
-	if ((bud = jabber_buddy_by_jid(ic, who, 0)) == NULL ||
-	    (bare = jabber_buddy_by_jid(ic, who, GET_BUDDY_BARE)) == NULL) {
-		/* Sending typing notifications to unknown buddies is
-		   unsupported for now. Shouldn't be a problem, I think. */
-		return 0;
-	}
-
-
-	if (bud->flags & JBFLAG_DOES_XEP85 || bare->flags & JBFLAG_DOES_XEP85) {
-		/* We're only allowed to send this stuff if we know the other
-		   side supports it. If the bare JID has the flag, all other
-		   resources get it, too (That is the case in gtalk) */
-
-		struct xt_node *node;
-		char *type;
-		int st;
-
-		if (typing & OPT_TYPING) {
-			type = "composing";
-		} else if (typing & OPT_THINKING) {
-			type = "paused";
-		} else {
-			type = "active";
-		}
-
-		node = xt_new_node(type, NULL, NULL);
-		xt_add_attr(node, "xmlns", XMLNS_CHATSTATES);
-		node = jabber_make_packet("message", "chat", bud->full_jid, node);
-
-		st = jabber_write_packet(ic, node);
-		xt_free_node(node);
-
-		return st;
-	}
-
-	return 1;
-}
-
-void jabber_chat_add_settings(account_t *acc, set_t **head)
-{
-	set_add(head, "always_use_nicks", "false", set_eval_bool, NULL);
-
-	/* Meh. Stupid room passwords. Not trying to obfuscate/hide
-	   them from the user for now. */
-	set_add(head, "password", NULL, NULL, NULL);
-}
-
-void jabber_chat_free_settings(account_t *acc, set_t **head)
-{
-	set_del(head, "always_use_nicks");
-
-	set_del(head, "password");
-}
-
-GList *jabber_buddy_action_list(bee_user_t *bu)
-{
-	static GList *ret = NULL;
-
-	if (ret == NULL) {
-		static const struct buddy_action ba[2] = {
-			{ "VERSION", "Get client (version) information" },
-		};
-
-		ret = g_list_prepend(ret, (void *) ba + 0);
-	}
-
-	return ret;
-}
-
-void *jabber_buddy_action(struct bee_user *bu, const char *action, char * const args[], void *data)
-{
-	if (g_strcasecmp(action, "VERSION") == 0) {
-		struct jabber_buddy *bud;
-
-		if ((bud = jabber_buddy_by_ext_jid(bu->ic, bu->handle, 0)) == NULL) {
-			bud = jabber_buddy_by_jid(bu->ic, bu->handle, GET_BUDDY_FIRST);
-		}
-		for (; bud; bud = bud->next) {
-			jabber_iq_version_send(bu->ic, bud, data);
-		}
-	}
-
-	return NULL;
-}
-
-gboolean jabber_handle_is_self(struct im_connection *ic, const char *who)
-{
-	struct jabber_data *jd = ic->proto_data;
-
-	return ((g_strcasecmp(who, ic->acc->user) == 0) ||
-	        (jd->internal_jid &&
-	         g_strcasecmp(who, jd->internal_jid) == 0));
-}
-
-void jabber_initmodule()
-{
-	struct prpl *ret = g_new0(struct prpl, 1);
-	struct prpl *hipchat = NULL;
-
-	ret->name = "jabber";
-	ret->mms = 0;                        /* no limit */
+		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->init = jabber_init;
-	ret->logout = jabber_logout;
-	ret->buddy_msg = jabber_buddy_msg;
-	ret->away_states = jabber_away_states;
+	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->set_info = jabber_set_info;
-	ret->get_info = jabber_get_info;
+	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->chat_msg = jabber_chat_msg_;
-	ret->chat_topic = jabber_chat_topic_;
-	ret->chat_invite = jabber_chat_invite_;
-	ret->chat_leave = jabber_chat_leave_;
-	ret->chat_join = jabber_chat_join_;
-	ret->chat_with = jabber_chat_with_;
-	ret->chat_list = jabber_chat_list_;
-	ret->chat_add_settings = jabber_chat_add_settings;
-	ret->chat_free_settings = jabber_chat_free_settings;
+	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->send_typing = jabber_send_typing;
-	ret->handle_cmp = g_strcasecmp;
-	ret->handle_is_self = jabber_handle_is_self;
-	ret->transfer_request = jabber_si_transfer_request;
-	ret->buddy_action_list = jabber_buddy_action_list;
-	ret->buddy_action = jabber_buddy_action;
-
-	register_protocol(ret);
-
-	/* Another one for hipchat, which has completely different logins */
-	hipchat = g_memdup2(ret, sizeof(struct prpl));
-	hipchat->name = "hipchat";
-	register_protocol(hipchat);
-}
+	ret->buddy_free = jabber_buddy_free;
+	ret->alias_buddy = jabber_roster_update;
+	ret->group_buddy = jabber_group_change;
+
+	my_protocol = ret;
+}
Index: protocols/jabber/jabber.h
===================================================================
--- protocols/jabber/jabber.h	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ protocols/jabber/jabber.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -1,367 +1,315 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Jabber module - Main file                                                *
-*                                                                           *
-*  Copyright 2006-2013 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.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-\***************************************************************************/
-
-#ifndef _JABBER_H
-#define _JABBER_H
-
-#include <glib.h>
-
-#include "bitlbee.h"
-#include "xmltree.h"
-
-extern GSList *jabber_connections;
-
-typedef enum {
-	JFLAG_STREAM_STARTED = 1,       /* Set when we detected the beginning of the stream
-	                                   and want to do auth. */
-	JFLAG_AUTHENTICATED = 2,        /* Set when we're successfully authenticatd. */
-	JFLAG_STREAM_RESTART = 4,       /* Set when we want to restart the stream (after
-	                                   SASL or TLS). */
-	JFLAG_WANT_SESSION = 8,         /* Set if the server wants a <session/> tag
-	                                   before we continue. */
-	JFLAG_WANT_BIND = 16,           /* ... for <bind> tag. */
-	JFLAG_WANT_TYPING = 32,         /* Set if we ever sent a typing notification, this
-	                                   activates all XEP-85 related code. */
-	JFLAG_XMLCONSOLE = 64,          /* If the user added an xmlconsole buddy. */
-	JFLAG_STARTTLS_DONE = 128,      /* If a plaintext session was converted to TLS. */
-	JFLAG_GMAILNOTIFY = 256,        /* If gmail notification is enabled */
-
-	JFLAG_GTALK =  0x100000,        /* Is Google Talk, as confirmed by iq discovery */
-	JFLAG_HIPCHAT = 0x200000,       /* Is hipchat, because prpl->name says so */
-} jabber_flags_t;
-
-typedef enum {
-	JBFLAG_PROBED_XEP85 = 1,        /* Set this when we sent our probe packet to make
-	                                   sure it gets sent only once. */
-	JBFLAG_DOES_XEP85 = 2,          /* Set this when the resource seems to support
-	                                   XEP85 (typing notification shite). */
-	JBFLAG_IS_CHATROOM = 4,         /* It's convenient to use this JID thingy for
-	                                   groupchat state info too. */
-	JBFLAG_IS_ANONYMOUS = 8,        /* For anonymous chatrooms, when we don't have
-	                                   have a real JID. */
-	JBFLAG_HIDE_SUBJECT = 16,       /* Hide the subject field since we probably
-	                                   showed it already. */
-} jabber_buddy_flags_t;
-
-/* Stores a streamhost's (a.k.a. proxy) data */
-typedef struct {
-	char *jid;
-	char *host;
-	char port[6];
-} jabber_streamhost_t;
-
-typedef enum {
-	JCFLAG_MESSAGE_SENT = 1,        /* Set this after sending the first message, so
-	                                   we can detect echoes/backlogs. */
-	JCFLAG_ALWAYS_USE_NICKS = 2,
-} jabber_chat_flags_t;
-
-struct jabber_data {
-	struct im_connection *ic;
-
-	int fd;
-	void *ssl;
-	char *txq;
-	int tx_len;
-	int r_inpa, w_inpa;
-
-	struct xt_parser *xt;
-	jabber_flags_t flags;
-
-	char *username;         /* USERNAME@server */
-	char *server;           /* username@SERVER -=> server/domain, not hostname */
-	char *me;               /* bare jid */
-	char *internal_jid;
-
-	const struct oauth2_service *oauth2_service;
-	char *oauth2_access_token;
-
-	/* After changing one of these two (or the priority setting), call
-	   presence_send_update() to inform the server about the changes. */
-	const struct jabber_away_state *away_state;
-	char *away_message;
-	guint64 gmail_time;
-	char *gmail_tid;
-
-	GChecksum *cached_id_prefix;
-	GHashTable *node_cache;
-	GHashTable *buddies;
-
-	GSList *filetransfers;
-	GSList *streamhosts;
-	int have_streamhosts;
-
-	char *muc_host;
-};
-
-struct jabber_away_state {
-	char code[5];
-	char *full_name;
-};
-
-typedef xt_status (*jabber_cache_event) (struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
-
-struct jabber_cache_entry {
-	time_t saved_at;
-	struct xt_node *node;
-	jabber_cache_event func;
-};
-
-/* Somewhat messy data structure: We have a hash table with the bare JID as
-   the key and the head of a struct jabber_buddy list as the value. The head
-   is always a bare JID. If the JID has other resources (often the case,
-   except for some transports that don't support multiple resources), those
-   follow. In that case, the bare JID at the beginning doesn't actually
-   refer to a real session and should only be used for operations that
-   support incomplete JIDs. */
-struct jabber_buddy {
-	char *bare_jid;
-	char *full_jid;
-	char *resource;
-
-	char *ext_jid; /* The JID to use in BitlBee. The real JID if possible, */
-	               /* otherwise something similar to the conference JID. */
-
-	int priority;
-	struct jabber_away_state *away_state;
-	char *away_message;
-	GSList *features;
-
-	time_t last_msg;
-	jabber_buddy_flags_t flags;
-
-	struct jabber_buddy *next;
-};
-
-struct jabber_chat {
-	int flags;
-	char *name;
-	char *my_full_jid; /* Separate copy because of case sensitivity. */
-	struct jabber_buddy *me;
-	char *invite;
-	char *last_sent_message;
-};
-
-struct jabber_transfer {
-	/* bitlbee's handle for this transfer */
-	file_transfer_t *ft;
-
-	/* the stream's private handle */
-	gpointer streamhandle;
-
-	/* timeout for discover queries */
-	gint disco_timeout;
-	gint disco_timeout_fired;
-
-	struct im_connection *ic;
-
-	struct jabber_buddy *bud;
-
-	int watch_in;
-	int watch_out;
-
-	char *ini_jid;
-	char *tgt_jid;
-	char *iq_id;
-	char *sid;
-	int accepted;
-
-	size_t bytesread, byteswritten;
-	int fd;
-	struct sockaddr_storage saddr;
-};
-
-#define JABBER_XMLCONSOLE_HANDLE "_xmlconsole"
-#define JABBER_OAUTH_HANDLE "jabber_oauth"
-
-/* Prefixes to use for packet IDs (mainly for IQ packets ATM). Usually the
-   first one should be used, but when storing a packet in the cache, a
-   "special" kind of ID is assigned to make it easier later to figure out
-   if we have to do call an event handler for the response packet. Also
-   we'll append a hash to make sure we won't trigger on cached packets from
-   other BitlBee users. :-) */
-#define JABBER_PACKET_ID "BeeP"
-#define JABBER_CACHED_ID "BeeC"
-
-/* The number of seconds to keep cached packets before garbage collecting
-   them. This gc is done on every keepalive (every minute). */
-#define JABBER_CACHE_MAX_AGE 600
-
-/* RFC 392[01] stuff */
-#define XMLNS_TLS          "urn:ietf:params:xml:ns:xmpp-tls"
-#define XMLNS_SASL         "urn:ietf:params:xml:ns:xmpp-sasl"
-#define XMLNS_BIND         "urn:ietf:params:xml:ns:xmpp-bind"
-#define XMLNS_SESSION      "urn:ietf:params:xml:ns:xmpp-session"
-#define XMLNS_STANZA_ERROR "urn:ietf:params:xml:ns:xmpp-stanzas"
-#define XMLNS_STREAM_ERROR "urn:ietf:params:xml:ns:xmpp-streams"
-#define XMLNS_ROSTER       "jabber:iq:roster"
-
-/* Some supported extensions/legacy stuff */
-#define XMLNS_AUTH         "jabber:iq:auth"                                      /* XEP-0078 */
-#define XMLNS_VERSION      "jabber:iq:version"                                   /* XEP-0092 */
-#define XMLNS_TIME_OLD     "jabber:iq:time"                                      /* XEP-0090 */
-#define XMLNS_TIME         "urn:xmpp:time"                                       /* XEP-0202 */
-#define XMLNS_PING         "urn:xmpp:ping"                                       /* XEP-0199 */
-#define XMLNS_RECEIPTS     "urn:xmpp:receipts"                                   /* XEP-0184 */
-#define XMLNS_VCARD        "vcard-temp"                                          /* XEP-0054 */
-#define XMLNS_DELAY_OLD    "jabber:x:delay"                                      /* XEP-0091 */
-#define XMLNS_DELAY        "urn:xmpp:delay"                                      /* XEP-0203 */
-#define XMLNS_XDATA        "jabber:x:data"                                       /* XEP-0004 */
-#define XMLNS_GMAILNOTIFY  "google:mail:notify"                                  /* Not a XEP */
-#define XMLNS_CARBONS      "urn:xmpp:carbons:2"                                  /* XEP-0280 */
-#define XMLNS_FORWARDING   "urn:xmpp:forward:0"                                  /* XEP-0297 */
-#define XMLNS_HINTS        "urn:xmpp:hints"                                      /* XEP-0334 */
-#define XMLNS_CHATSTATES   "http://jabber.org/protocol/chatstates"               /* XEP-0085 */
-#define XMLNS_DISCO_INFO   "http://jabber.org/protocol/disco#info"               /* XEP-0030 */
-#define XMLNS_DISCO_ITEMS  "http://jabber.org/protocol/disco#items"              /* XEP-0030 */
-#define XMLNS_MUC          "http://jabber.org/protocol/muc"                      /* XEP-0045 */
-#define XMLNS_MUC_USER     "http://jabber.org/protocol/muc#user"                 /* XEP-0045 */
-#define XMLNS_CAPS         "http://jabber.org/protocol/caps"                     /* XEP-0115 */
-#define XMLNS_FEATURE      "http://jabber.org/protocol/feature-neg"              /* XEP-0020 */
-#define XMLNS_SI           "http://jabber.org/protocol/si"                       /* XEP-0095 */
-#define XMLNS_FILETRANSFER "http://jabber.org/protocol/si/profile/file-transfer" /* XEP-0096 */
-#define XMLNS_BYTESTREAMS  "http://jabber.org/protocol/bytestreams"              /* XEP-0065 */
-#define XMLNS_IBB          "http://jabber.org/protocol/ibb"                      /* XEP-0047 */
-
-/* Hipchat protocol extensions*/
-#define XMLNS_HIPCHAT         "http://hipchat.com"
-#define XMLNS_HIPCHAT_PROFILE "http://hipchat.com/protocol/profile"
-
-/* jabber.c */
-void jabber_connect(struct im_connection *ic);
-
-/* iq.c */
-xt_status jabber_pkt_iq(struct xt_node *node, gpointer data);
-int jabber_init_iq_auth(struct im_connection *ic);
-xt_status jabber_pkt_bind_sess(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
-int jabber_get_roster(struct im_connection *ic);
-int jabber_get_vcard(struct im_connection *ic, char *bare_jid);
-int jabber_add_to_roster(struct im_connection *ic, const char *handle, const char *name, const char *group);
-int jabber_remove_from_roster(struct im_connection *ic, char *handle);
-xt_status jabber_iq_query_features(struct im_connection *ic, char *bare_jid);
-xt_status jabber_iq_query_server(struct im_connection *ic, char *jid, char *xmlns);
-void jabber_iq_version_send(struct im_connection *ic, struct jabber_buddy *bud, void *data);
-int jabber_iq_disco_server(struct im_connection *ic);
-int jabber_iq_disco_muc(struct im_connection *ic, const char *muc_server);
-
-/* si.c */
-int jabber_si_handle_request(struct im_connection *ic, struct xt_node *node, struct xt_node *sinode);
-void jabber_si_transfer_request(struct im_connection *ic, file_transfer_t *ft, char *who);
-void jabber_si_free_transfer(file_transfer_t *ft);
-
-/* s5bytestream.c */
-int jabber_bs_recv_request(struct im_connection *ic, struct xt_node *node, struct xt_node *qnode);
-gboolean jabber_bs_send_start(struct jabber_transfer *tf);
-gboolean jabber_bs_send_write(file_transfer_t *ft, char *buffer, unsigned int len);
-
-/* message.c */
-xt_status jabber_pkt_message(struct xt_node *node, gpointer data);
-
-/* presence.c */
-xt_status jabber_pkt_presence(struct xt_node *node, gpointer data);
-int presence_send_update(struct im_connection *ic);
-int presence_send_request(struct im_connection *ic, char *handle, char *request);
-
-/* jabber_util.c */
-char *set_eval_priority(set_t *set, char *value);
-char *set_eval_tls(set_t *set, char *value);
-struct xt_node *jabber_make_packet(char *name, char *type, char *to, struct xt_node *children);
-struct xt_node *jabber_make_error_packet(struct xt_node *orig, char *err_cond, char *err_type, char *err_code);
-void jabber_cache_add(struct im_connection *ic, struct xt_node *node, jabber_cache_event func);
-struct xt_node *jabber_cache_get(struct im_connection *ic, char *id);
-void jabber_cache_entry_free(gpointer entry);
-void jabber_cache_clean(struct im_connection *ic);
-xt_status jabber_cache_handle_packet(struct im_connection *ic, struct xt_node *node);
-const struct jabber_away_state *jabber_away_state_by_code(char *code);
-const struct jabber_away_state *jabber_away_state_by_name(char *name);
-void jabber_buddy_ask(struct im_connection *ic, char *handle);
-int jabber_compare_jid(const char *jid1, const char *jid2);
-char *jabber_normalize(const char *orig);
-
-typedef enum {
-	GET_BUDDY_CREAT = 1,    /* Try to create it, if necessary. */
-	GET_BUDDY_EXACT = 2,    /* Get an exact match (only makes sense with bare JIDs). */
-	GET_BUDDY_FIRST = 4,    /* No selection, simply get the first resource for this JID. */
-	GET_BUDDY_BARE = 8,     /* Get the bare version of the JID (possibly inexistent). */
-	GET_BUDDY_BARE_OK = 16, /* Allow returning a bare JID if that seems better. */
-} get_buddy_flags_t;
-
-struct jabber_error {
-	char *code, *text, *type;
-};
-
-struct jabber_buddy *jabber_buddy_add(struct im_connection *ic, char *full_jid);
-struct jabber_buddy *jabber_buddy_by_jid(struct im_connection *ic, char *jid, get_buddy_flags_t flags);
-struct jabber_buddy *jabber_buddy_by_ext_jid(struct im_connection *ic, char *jid, get_buddy_flags_t flags);
-int jabber_buddy_remove(struct im_connection *ic, char *full_jid);
-int jabber_buddy_remove_bare(struct im_connection *ic, char *bare_jid);
-void jabber_buddy_remove_all(struct im_connection *ic);
-time_t jabber_get_timestamp(struct xt_node *xt);
-struct jabber_error *jabber_error_parse(struct xt_node *node, char *xmlns);
-void jabber_error_free(struct jabber_error *err);
-gboolean jabber_set_me(struct im_connection *ic, const char *me);
-char *jabber_get_bare_jid(char *jid);
-
-extern const struct jabber_away_state jabber_away_state_list[];
-
-/* io.c */
-int jabber_write_packet(struct im_connection *ic, struct xt_node *node);
-int jabber_write(struct im_connection *ic, char *buf, int len);
-gboolean jabber_connected_plain(gpointer data, gint source, b_input_condition cond);
-gboolean jabber_connected_ssl(gpointer data, int returncode, void *source, b_input_condition cond);
-gboolean jabber_start_stream(struct im_connection *ic);
-void jabber_end_stream(struct im_connection *ic);
-
-/* sasl.c */
-xt_status sasl_pkt_mechanisms(struct xt_node *node, gpointer data);
-xt_status sasl_pkt_challenge(struct xt_node *node, gpointer data);
-xt_status sasl_pkt_result(struct xt_node *node, gpointer data);
-gboolean sasl_supported(struct im_connection *ic);
-void sasl_oauth2_init(struct im_connection *ic);
-int sasl_oauth2_get_refresh_token(struct im_connection *ic, const char *msg);
-int sasl_oauth2_refresh(struct im_connection *ic, const char *refresh_token);
-void sasl_oauth2_got_token(gpointer data, const char *access_token, const char *refresh_token, const char *error);
-
-extern const struct oauth2_service oauth2_service_google;
-
-/* conference.c */
-struct groupchat *jabber_chat_join(struct im_connection *ic, const char *room, const char *nick, const char *password,
-                                   gboolean always_use_nicks);
-struct groupchat *jabber_chat_with(struct im_connection *ic, char *who);
-struct groupchat *jabber_chat_by_jid(struct im_connection *ic, const char *name);
-void jabber_chat_free(struct groupchat *c);
-int jabber_chat_msg(struct groupchat *ic, char *message, int flags);
-int jabber_chat_topic(struct groupchat *c, char *topic);
-int jabber_chat_leave(struct groupchat *c, const char *reason);
-void jabber_chat_pkt_presence(struct im_connection *ic, struct jabber_buddy *bud, struct xt_node *node);
-void jabber_chat_pkt_message(struct im_connection *ic, struct jabber_buddy *bud, struct xt_node *node);
-void jabber_chat_invite(struct groupchat *c, char *who, char *message);
-
-/* hipchat.c */
-int jabber_get_hipchat_profile(struct im_connection *ic);
-xt_status jabber_parse_hipchat_profile(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
-xt_status hipchat_handle_success(struct im_connection *ic, struct xt_node *node);
-char *hipchat_make_channel_slug(const char *name);
-char *hipchat_guess_channel_name(struct im_connection *ic, const char *name);
-
+/*
+ *  This program is free software; you can 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 */
Index: otocols/jabber/jabber_util.c
===================================================================
--- protocols/jabber/jabber_util.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,849 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Jabber module - Misc. stuff                                              *
-*                                                                           *
-*  Copyright 2006-2010 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.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-\***************************************************************************/
-
-#include "jabber.h"
-#include "base64.h"
-
-static unsigned int next_id = 1;
-
-char *set_eval_priority(set_t *set, char *value)
-{
-	account_t *acc = set->data;
-	int i;
-
-	if (sscanf(value, "%d", &i) == 1) {
-		/* Priority is a signed 8-bit integer, according to RFC 3921. */
-		if (i < -128 || i > 127) {
-			return SET_INVALID;
-		}
-	} else {
-		return SET_INVALID;
-	}
-
-	/* Only run this stuff if the account is online ATM,
-	   and if the setting seems to be acceptable. */
-	if (acc->ic) {
-		/* Although set_eval functions usually are very nice and
-		   convenient, they have one disadvantage: If I would just
-		   call p_s_u() now to send the new prio setting, it would
-		   send the old setting because the set->value gets changed
-		   after the (this) eval returns a non-NULL value.
-
-		   So now I can choose between implementing post-set
-		   functions next to evals, or just do this little hack: */
-
-		g_free(set->value);
-		set->value = g_strdup(value);
-
-		/* (Yes, sorry, I prefer the hack. :-P) */
-
-		presence_send_update(acc->ic);
-	}
-
-	return value;
-}
-
-char *set_eval_tls(set_t *set, char *value)
-{
-	if (g_strcasecmp(value, "try") == 0) {
-		return value;
-	} else {
-		return set_eval_bool(set, value);
-	}
-}
-
-struct xt_node *jabber_make_packet(char *name, char *type, char *to, struct xt_node *children)
-{
-	struct xt_node *node;
-
-	node = xt_new_node(name, NULL, children);
-
-	if (type) {
-		xt_add_attr(node, "type", type);
-	}
-	if (to) {
-		xt_add_attr(node, "to", to);
-	}
-
-	/* IQ packets should always have an ID, so let's generate one. It
-	   might get overwritten by jabber_cache_add() if this packet has
-	   to be saved until we receive a response. Cached packets get
-	   slightly different IDs so we can recognize them. */
-	if (strcmp(name, "iq") == 0) {
-		char *id = g_strdup_printf("%s%05x", JABBER_PACKET_ID, (next_id++) & 0xfffff);
-		xt_add_attr(node, "id", id);
-		g_free(id);
-	}
-
-	return node;
-}
-
-struct xt_node *jabber_make_error_packet(struct xt_node *orig, char *err_cond, char *err_type, char *err_code)
-{
-	struct xt_node *node, *c;
-	char *to;
-
-	/* Create the "defined-condition" tag. */
-	c = xt_new_node(err_cond, NULL, NULL);
-	xt_add_attr(c, "xmlns", XMLNS_STANZA_ERROR);
-
-	/* Put it in an <error> tag. */
-	c = xt_new_node("error", NULL, c);
-	xt_add_attr(c, "type", err_type);
-
-	/* Add the error code, if present */
-	if (err_code) {
-		xt_add_attr(c, "code", err_code);
-	}
-
-	/* To make the actual error packet, we copy the original packet and
-	   add our <error>/type="error" tag. Including the original packet
-	   is recommended, so let's just do it. */
-	node = xt_dup(orig);
-	xt_add_child(node, c);
-	xt_add_attr(node, "type", "error");
-
-	/* Return to sender. */
-	if ((to = xt_find_attr(node, "from"))) {
-		xt_add_attr(node, "to", to);
-		xt_remove_attr(node, "from");
-	}
-
-	return node;
-}
-
-/* Cache a node/packet for later use. Mainly useful for IQ packets if you need
-   them when you receive the response. Use this BEFORE sending the packet so
-   it'll get a new id= tag, and do NOT free() the packet after sending it! */
-void jabber_cache_add(struct im_connection *ic, struct xt_node *node, jabber_cache_event func)
-{
-	struct jabber_data *jd = ic->proto_data;
-	struct jabber_cache_entry *entry = g_new0(struct jabber_cache_entry, 1);
-	GChecksum *id_hash, *copy;
-	gsize digest_len = MD5_HASH_SIZE;
-	guint8 id_sum[MD5_HASH_SIZE];
-	char *id, *asc_hash;
-
-	next_id++;
-
-	id_hash = jd->cached_id_prefix;
-	g_checksum_update(id_hash, (guint8 *) &next_id, sizeof(next_id));
-
-	copy = g_checksum_copy(id_hash);
-	g_checksum_get_digest(copy, id_sum, &digest_len);
-	g_checksum_free(copy);
-
-	asc_hash = base64_encode(id_sum, 12);
-
-	id = g_strdup_printf("%s%s", JABBER_CACHED_ID, asc_hash);
-	xt_add_attr(node, "id", id);
-	g_free(id);
-	g_free(asc_hash);
-
-	entry->node = node;
-	entry->func = func;
-	entry->saved_at = time(NULL);
-	g_hash_table_insert(jd->node_cache, xt_find_attr(node, "id"), entry);
-}
-
-void jabber_cache_entry_free(gpointer data)
-{
-	struct jabber_cache_entry *entry = data;
-
-	xt_free_node(entry->node);
-	g_free(entry);
-}
-
-gboolean jabber_cache_clean_entry(gpointer key, gpointer entry, gpointer nullpointer);
-
-/* This one should be called from time to time (from keepalive, in this case)
-   to make sure things don't stay in the node cache forever. By marking nodes
-   during the first run and deleting marked nodes during a next run, every
-   node should be available in the cache for at least a minute (assuming the
-   function is indeed called every minute). */
-void jabber_cache_clean(struct im_connection *ic)
-{
-	struct jabber_data *jd = ic->proto_data;
-	time_t threshold = time(NULL) - JABBER_CACHE_MAX_AGE;
-
-	g_hash_table_foreach_remove(jd->node_cache, jabber_cache_clean_entry, &threshold);
-}
-
-gboolean jabber_cache_clean_entry(gpointer key, gpointer entry_, gpointer threshold_)
-{
-	struct jabber_cache_entry *entry = entry_;
-	time_t *threshold = threshold_;
-
-	return entry->saved_at < *threshold;
-}
-
-xt_status jabber_cache_handle_packet(struct im_connection *ic, struct xt_node *node)
-{
-	struct jabber_data *jd = ic->proto_data;
-	struct jabber_cache_entry *entry;
-	char *s;
-
-	if ((s = xt_find_attr(node, "id")) == NULL ||
-	    strncmp(s, JABBER_CACHED_ID, strlen(JABBER_CACHED_ID)) != 0) {
-		/* Silently ignore it, without an ID (or a non-cache
-		   ID) we don't know how to handle the packet and we
-		   probably don't have to. */
-		return XT_HANDLED;
-	}
-
-	entry = g_hash_table_lookup(jd->node_cache, s);
-
-	if (entry == NULL) {
-		/*
-		There's no longer an easy way to see if we generated this
-		one or someone else, and there's a ten-minute timeout anyway,
-		so meh.
-
-		imcb_log( ic, "Warning: Received %s-%s packet with unknown/expired ID %s!",
-		              node->name, xt_find_attr( node, "type" ) ? : "(no type)", s );
-		*/
-	} else if (entry->func) {
-		return entry->func(ic, node, entry->node);
-	}
-
-	return XT_HANDLED;
-}
-
-const struct jabber_away_state jabber_away_state_list[] =
-{
-	{ "away",  "Away" },
-	{ "chat",  "Free for Chat" },   /* WTF actually uses this? */
-	{ "dnd",   "Do not Disturb" },
-	{ "xa",    "Extended Away" },
-	{ "",      NULL }
-};
-
-const struct jabber_away_state *jabber_away_state_by_code(char *code)
-{
-	int i;
-
-	if (code == NULL) {
-		return NULL;
-	}
-
-	for (i = 0; jabber_away_state_list[i].full_name; i++) {
-		if (g_strcasecmp(jabber_away_state_list[i].code, code) == 0) {
-			return jabber_away_state_list + i;
-		}
-	}
-
-	return NULL;
-}
-
-const struct jabber_away_state *jabber_away_state_by_name(char *name)
-{
-	int i;
-
-	if (name == NULL) {
-		return NULL;
-	}
-
-	for (i = 0; jabber_away_state_list[i].full_name; i++) {
-		if (g_strcasecmp(jabber_away_state_list[i].full_name, name) == 0) {
-			return jabber_away_state_list + i;
-		}
-	}
-
-	return NULL;
-}
-
-struct jabber_buddy_ask_data {
-	struct im_connection *ic;
-	char *handle;
-	char *realname;
-};
-
-static void jabber_buddy_ask_yes(void *data)
-{
-	struct jabber_buddy_ask_data *bla = data;
-
-	presence_send_request(bla->ic, bla->handle, "subscribed");
-
-	imcb_ask_add(bla->ic, bla->handle, NULL);
-
-	g_free(bla->handle);
-	g_free(bla);
-}
-
-static void jabber_buddy_ask_no(void *data)
-{
-	struct jabber_buddy_ask_data *bla = data;
-
-	presence_send_request(bla->ic, bla->handle, "unsubscribed");
-
-	g_free(bla->handle);
-	g_free(bla);
-}
-
-void jabber_buddy_ask(struct im_connection *ic, char *handle)
-{
-	struct jabber_buddy_ask_data *bla = g_new0(struct jabber_buddy_ask_data, 1);
-	char *buf;
-
-	bla->ic = ic;
-	bla->handle = g_strdup(handle);
-
-	buf = g_strdup_printf("The user %s wants to add you to his/her buddy list.", handle);
-	imcb_ask(ic, buf, bla, jabber_buddy_ask_yes, jabber_buddy_ask_no);
-	g_free(buf);
-}
-
-/* Compares just the bare portions of two Jabber IDs. */
-int jabber_compare_jid(const char *jid1, const char *jid2)
-{
-	int i;
-
-	if (!jid1 || !jid2) {
-		return FALSE;
-	}
-
-	for (i = 0;; i++) {
-		if (jid1[i] == '\0' || jid1[i] == '/' || jid2[i] == '\0' || jid2[i] == '/') {
-			if ((jid1[i] == '\0' || jid1[i] == '/') && (jid2[i] == '\0' || jid2[i] == '/')) {
-				break;
-			}
-			return FALSE;
-		}
-		if (g_ascii_tolower(jid1[i]) != g_ascii_tolower(jid2[i])) {
-			return FALSE;
-		}
-	}
-
-	return TRUE;
-}
-
-/* The /resource part is case sensitive. This stops once we see a slash.
-   Returns a new string. Don't leak it! */
-char *jabber_normalize(const char *orig)
-{
-	char *lower, *new, *s;
-
-	if (!(s = strchr(orig, '/'))) {
-		return g_utf8_strdown(orig, -1);
-	}
-
-	lower = g_utf8_strdown(orig, (s - orig));    /* stop in s */
-	new = g_strconcat(lower, s, NULL);
-	g_free(lower);
-	return new;
-}
-
-/* Similar to jabber_normalize, but works with addresses in the form
- * resource=chatroom@example.com */
-char *jabber_normalize_ext(const char *orig)
-{
-	char *lower, *new, *s;
-
-	if (!(s = strchr(orig, '='))) {
-		return g_utf8_strdown(orig, -1);
-	}
-
-	lower = g_utf8_strdown(s, -1);   /* start in s */
-
-	*s = 0;
-	new = g_strconcat(orig, lower, NULL);
-	*s = '=';
-
-	g_free(lower);
-	return new;
-}
-
-/* Adds a buddy/resource to our list. Returns NULL if full_jid is not really a
-   FULL jid or if we already have this buddy/resource. XXX: No, great, actually
-   buddies from transports don't (usually) have resources. So we'll really have
-   to deal with that properly. Set their ->resource property to NULL. Do *NOT*
-   allow to mix this stuff, though... */
-struct jabber_buddy *jabber_buddy_add(struct im_connection *ic, char *full_jid_)
-{
-	struct jabber_data *jd = ic->proto_data;
-	struct jabber_buddy *bud, *new, *bi;
-	char *s, *full_jid;
-
-	full_jid = jabber_normalize(full_jid_);
-
-	if ((s = strchr(full_jid, '/'))) {
-		*s = 0;
-	}
-
-	new = g_new0(struct jabber_buddy, 1);
-
-	if ((bud = g_hash_table_lookup(jd->buddies, full_jid))) {
-		/* The first entry is always a bare JID. If there are more, we
-		   should ignore the first one here. */
-		if (bud->next) {
-			bud = bud->next;
-		}
-
-		/* If this is a transport buddy or whatever, it can't have more
-		   than one instance, so this is always wrong: */
-		if (s == NULL || bud->resource == NULL) {
-			if (s) {
-				*s = '/';
-			}
-			g_free(new);
-			g_free(full_jid);
-			return NULL;
-		}
-
-		new->bare_jid = bud->bare_jid;
-
-		/* We already have another resource for this buddy, add the
-		   new one to the list. */
-		for (bi = bud; bi; bi = bi->next) {
-			/* Check for dupes. */
-			if (strcmp(bi->resource, s + 1) == 0) {
-				*s = '/';
-				g_free(new);
-				g_free(full_jid);
-				return NULL;
-			}
-			/* Append the new item to the list. */
-			else if (bi->next == NULL) {
-				bi->next = new;
-				break;
-			}
-		}
-	} else {
-		new->full_jid = new->bare_jid = g_strdup(full_jid);
-		g_hash_table_insert(jd->buddies, new->bare_jid, new);
-
-		if (s) {
-			new->next = g_new0(struct jabber_buddy, 1);
-			new->next->bare_jid = new->bare_jid;
-			new = new->next;
-		}
-	}
-
-	if (s) {
-		*s = '/';
-		new->full_jid = full_jid;
-		new->resource = strchr(new->full_jid, '/') + 1;
-	} else {
-		/* Let's waste some more bytes of RAM instead of to make
-		   memory management a total disaster here. And it saves
-		   me one g_free() call in this function. :-P */
-		new->full_jid = full_jid;
-	}
-
-	return new;
-}
-
-/* Finds a buddy from our structures. Can find both full- and bare JIDs. When
-   asked for a bare JID, it uses the "resource_select" setting to see which
-   resource to pick. */
-struct jabber_buddy *jabber_buddy_by_jid(struct im_connection *ic, char *jid_, get_buddy_flags_t flags)
-{
-	struct jabber_data *jd = ic->proto_data;
-	struct jabber_buddy *bud, *head;
-	char *s, *jid;
-
-	jid = jabber_normalize(jid_);
-
-	if ((s = strchr(jid, '/'))) {
-		int bare_exists = 0;
-
-		*s = 0;
-		if ((bud = g_hash_table_lookup(jd->buddies, jid))) {
-			bare_exists = 1;
-
-			if (bud->next) {
-				bud = bud->next;
-			}
-
-			/* Just return the first one for this bare JID. */
-			if (flags & GET_BUDDY_FIRST) {
-				*s = '/';
-				g_free(jid);
-				return bud;
-			}
-
-			/* Is this one of those no-resource buddies? */
-			if (bud->resource == NULL) {
-				*s = '/';
-				g_free(jid);
-				return NULL;
-			}
-
-			/* See if there's an exact match. */
-			for (; bud; bud = bud->next) {
-				if (strcmp(bud->resource, s + 1) == 0) {
-					break;
-				}
-			}
-		}
-
-		if (bud == NULL && (flags & GET_BUDDY_CREAT) &&
-		    (bare_exists || bee_user_by_handle(ic->bee, ic, jid))) {
-			*s = '/';
-			bud = jabber_buddy_add(ic, jid);
-		}
-
-		g_free(jid);
-		return bud;
-	} else {
-		struct jabber_buddy *best_prio, *best_time;
-		char *set;
-
-		head = g_hash_table_lookup(jd->buddies, jid);
-		bud = (head && head->next) ? head->next : head;
-
-		g_free(jid);
-
-		if (bud == NULL) {
-			/* No match. Create it now? */
-			return ((flags & GET_BUDDY_CREAT) &&
-			        bee_user_by_handle(ic->bee, ic, jid_)) ?
-			       jabber_buddy_add(ic, jid_) : NULL;
-		} else if (bud->resource && (flags & GET_BUDDY_EXACT)) {
-			/* We want an exact match, so in thise case there shouldn't be a /resource. */
-			if (head != bud && head->resource == NULL) {
-				return head;
-			} else {
-				return NULL;
-			}
-		} else if (bud->resource == NULL || bud->next == NULL) {
-			/* No need for selection if there's only one option. */
-			return bud;
-		} else if (flags & GET_BUDDY_FIRST) {
-			/* Looks like the caller doesn't care about details. */
-			return bud;
-		} else if (flags & GET_BUDDY_BARE) {
-			return head;
-		}
-
-		best_prio = best_time = bud;
-		for (; bud; bud = bud->next) {
-			if (bud->priority > best_prio->priority) {
-				best_prio = bud;
-			}
-			if (bud->last_msg > best_time->last_msg) {
-				best_time = bud;
-			}
-		}
-
-		if ((set = set_getstr(&ic->acc->set, "resource_select")) == NULL) {
-			return NULL;
-		} else if (strcmp(set, "priority") == 0) {
-			return best_prio;
-		} else if (flags & GET_BUDDY_BARE_OK) { /* && strcmp( set, "activity" ) == 0 */
-			if (best_time->last_msg + set_getint(&ic->acc->set, "activity_timeout") >= time(NULL)) {
-				return best_time;
-			} else {
-				return head;
-			}
-		} else {
-			return best_time;
-		}
-	}
-}
-
-/* I'm keeping a separate ext_jid attribute to save a JID that makes sense
-   to export to BitlBee. This is mainly for groupchats right now. It's
-   a bit of a hack, but I just think having the user nickname in the hostname
-   part of the hostmask doesn't look nice on IRC. Normally you can convert
-   a normal JID to ext_jid by swapping the part before and after the / and
-   replacing the / with a =. But there should be some stripping (@s are
-   allowed in Jabber nicks...). */
-struct jabber_buddy *jabber_buddy_by_ext_jid(struct im_connection *ic, char *jid_, get_buddy_flags_t flags)
-{
-	struct jabber_buddy *bud;
-	char *s, *jid;
-
-	jid = jabber_normalize_ext(jid_);
-
-	if ((s = strchr(jid, '=')) == NULL) {
-		g_free(jid);
-		return NULL;
-	}
-
-	for (bud = jabber_buddy_by_jid(ic, s + 1, GET_BUDDY_FIRST); bud; bud = bud->next) {
-		/* Hmmm, could happen if not all people in the chat are anonymized? */
-		if (bud->ext_jid == NULL) {
-			continue;
-		}
-
-		if (strcmp(bud->ext_jid, jid) == 0) {
-			break;
-		}
-	}
-
-	g_free(jid);
-
-	return bud;
-}
-
-/* Remove one specific full JID from our list. Use this when a buddy goes
-   off-line (because (s)he can still be online from a different location.
-   XXX: See above, we should accept bare JIDs too... */
-int jabber_buddy_remove(struct im_connection *ic, char *full_jid_)
-{
-	struct jabber_data *jd = ic->proto_data;
-	struct jabber_buddy *bud, *prev = NULL, *bi;
-	char *s, *full_jid;
-
-	full_jid = jabber_normalize(full_jid_);
-
-	if ((s = strchr(full_jid, '/'))) {
-		*s = 0;
-	}
-
-	if ((bud = g_hash_table_lookup(jd->buddies, full_jid))) {
-		if (bud->next) {
-			bud = (prev = bud)->next;
-		}
-
-		/* If there's only one item in the list (and if the resource
-		   matches), removing it is simple. (And the hash reference
-		   should be removed too!) */
-		if (bud->next == NULL &&
-		    ((s == NULL && bud->resource == NULL) ||
-		     (bud->resource && s && strcmp(bud->resource, s + 1) == 0))) {
-			int st = jabber_buddy_remove_bare(ic, full_jid);
-			g_free(full_jid);
-			return st;
-		} else if (s == NULL || bud->resource == NULL) {
-			/* Tried to remove a bare JID while this JID does seem
-			   to have resources... (Or the opposite.) *sigh* */
-			g_free(full_jid);
-			return 0;
-		} else {
-			for (bi = bud; bi; bi = (prev = bi)->next) {
-				if (strcmp(bi->resource, s + 1) == 0) {
-					break;
-				}
-			}
-
-			g_free(full_jid);
-
-			if (bi) {
-				if (prev) {
-					prev->next = bi->next;
-				} else {
-					/* Don't think this should ever happen anymore. */
-					g_hash_table_replace(jd->buddies, bi->bare_jid, bi->next);
-				}
-
-				g_free(bi->ext_jid);
-				g_free(bi->full_jid);
-				g_free(bi->away_message);
-				g_free(bi);
-
-				return 1;
-			} else {
-				return 0;
-			}
-		}
-	} else {
-		g_free(full_jid);
-		return 0;
-	}
-}
-
-/* Remove a buddy completely; removes all resources that belong to the
-   specified bare JID. Use this when removing someone from the contact
-   list, for example. */
-int jabber_buddy_remove_bare(struct im_connection *ic, char *bare_jid)
-{
-	struct jabber_data *jd = ic->proto_data;
-	struct jabber_buddy *bud, *next;
-
-	if (strchr(bare_jid, '/')) {
-		return 0;
-	}
-
-	if ((bud = jabber_buddy_by_jid(ic, bare_jid, GET_BUDDY_FIRST))) {
-		/* Most important: Remove the hash reference. We don't know
-		   this buddy anymore. */
-		g_hash_table_remove(jd->buddies, bud->bare_jid);
-		g_free(bud->bare_jid);
-
-		/* Deallocate the linked list of resources. */
-		while (bud) {
-			/* ext_jid && anonymous means that this buddy is
-			   specific to one groupchat (the one we're
-			   currently cleaning up) so it can be deleted
-			   completely. */
-			if (bud->ext_jid && bud->flags & JBFLAG_IS_ANONYMOUS) {
-				imcb_remove_buddy(ic, bud->ext_jid, NULL);
-			}
-
-			next = bud->next;
-			g_free(bud->ext_jid);
-			g_free(bud->full_jid);
-			g_free(bud->away_message);
-			g_free(bud);
-			bud = next;
-		}
-
-		return 1;
-	} else {
-		return 0;
-	}
-}
-
-static gboolean jabber_buddy_remove_all_cb(gpointer key, gpointer value, gpointer data)
-{
-	struct jabber_buddy *bud, *next;
-
-	bud = value;
-	if (bud->bare_jid != bud->full_jid) {
-		g_free(bud->bare_jid);
-	}
-	while (bud) {
-		next = bud->next;
-		g_free(bud->ext_jid);
-		g_free(bud->full_jid);
-		g_free(bud->away_message);
-		g_free(bud);
-		bud = next;
-	}
-
-	return TRUE;
-}
-
-void jabber_buddy_remove_all(struct im_connection *ic)
-{
-	struct jabber_data *jd = ic->proto_data;
-
-	g_hash_table_foreach_remove(jd->buddies, jabber_buddy_remove_all_cb, NULL);
-	g_hash_table_destroy(jd->buddies);
-}
-
-time_t jabber_get_timestamp(struct xt_node *xt)
-{
-	struct xt_node *c;
-	char *s = NULL;
-	struct tm tp;
-	gboolean is_old = TRUE;
-	const char *format;
-
-	/* XEP-0091 has <x> */
-	c = xt_find_node_by_attr(xt->children, "x", "xmlns", XMLNS_DELAY_OLD);
-
-	if (!c || !(s = xt_find_attr(c, "stamp"))) {
-		is_old = FALSE;
-
-		/* XEP-0203 has <delay> */
-		c = xt_find_node_by_attr(xt->children, "delay", "xmlns", XMLNS_DELAY);
-		if (!c || !(s = xt_find_attr(c, "stamp"))) {
-			return 0;
-		}
-	}
-
-	memset(&tp, 0, sizeof(tp));
-
-	/* The other main difference between XEPs is the timestamp format */
-	format = (is_old) ? "%4d%2d%2dT%2d:%2d:%2d" : "%4d-%2d-%2dT%2d:%2d:%2dZ";
-
-	if (sscanf(s, format, &tp.tm_year, &tp.tm_mon, &tp.tm_mday,
-	           &tp.tm_hour, &tp.tm_min, &tp.tm_sec) != 6) {
-		return 0;
-	}
-
-	tp.tm_year -= 1900;
-	tp.tm_mon--;
-
-	return mktime_utc(&tp);
-}
-
-struct jabber_error *jabber_error_parse(struct xt_node *node, char *xmlns)
-{
-	struct jabber_error *err;
-	struct xt_node *c;
-	char *s;
-
-	if (node == NULL) {
-		return NULL;
-	}
-
-	err = g_new0(struct jabber_error, 1);
-	err->type = xt_find_attr(node, "type");
-
-	for (c = node->children; c; c = c->next) {
-		if (!(s = xt_find_attr(c, "xmlns")) ||
-		    strcmp(s, xmlns) != 0) {
-			continue;
-		}
-
-		if (strcmp(c->name, "text") != 0) {
-			err->code = c->name;
-		}
-		/* Only use the text if it doesn't have an xml:lang attribute,
-		   if it's empty or if it's set to something English. */
-		else if (!(s = xt_find_attr(c, "xml:lang")) ||
-		         !*s || strncmp(s, "en", 2) == 0) {
-			err->text = c->text;
-		}
-	}
-
-	return err;
-}
-
-void jabber_error_free(struct jabber_error *err)
-{
-	g_free(err);
-}
-
-gboolean jabber_set_me(struct im_connection *ic, const char *me)
-{
-	struct jabber_data *jd = ic->proto_data;
-
-	if (strchr(me, '@') == NULL) {
-		return FALSE;
-	}
-
-	g_free(jd->username);
-	g_free(jd->me);
-
-	jd->me = jabber_normalize(me);
-	jd->server = strchr(jd->me, '@');
-	jd->username = g_strndup(jd->me, jd->server - jd->me);
-	jd->server++;
-
-	/* Set the "internal" account username, for groupchats */
-	g_free(jd->internal_jid);
-	jd->internal_jid = g_strdup(jd->me);
-
-	return TRUE;
-}
-
-/* Returns new reference! g_free() afterwards. */
-char *jabber_get_bare_jid(char *jid)
-{
-	char *s = NULL;
-
-	if (jid == NULL) {
-		return NULL;
-	}
-
-	if ((s = strchr(jid, '/'))) {
-		return g_strndup(jid, s - jid);
-	} else {
-		return g_strdup(jid);
-	}
-}
Index: protocols/jabber/jid.c
===================================================================
--- protocols/jabber/jid.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/jid.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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;
+}
Index: protocols/jabber/jpacket.c
===================================================================
--- protocols/jabber/jpacket.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/jpacket.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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;
+}
Index: protocols/jabber/jutil.c
===================================================================
--- protocols/jabber/jutil.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/jutil.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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;
+}
Index: protocols/jabber/latin1tab.h
===================================================================
--- protocols/jabber/latin1tab.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/latin1tab.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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,
Index: protocols/jabber/lib.h
===================================================================
--- protocols/jabber/lib.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/lib.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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 */
Index: protocols/jabber/libxode.h
===================================================================
--- protocols/jabber/libxode.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/libxode.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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 */
Index: protocols/jabber/log.c
===================================================================
--- protocols/jabber/log.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/log.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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 */
Index: protocols/jabber/log.h
===================================================================
--- protocols/jabber/log.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/log.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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 */
+
Index: otocols/jabber/message.c
===================================================================
--- protocols/jabber/message.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,205 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Jabber module - Handling of message(s) (tags), etc                       *
-*                                                                           *
-*  Copyright 2006-2012 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.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-\***************************************************************************/
-
-#include "jabber.h"
-
-static xt_status jabber_pkt_message_normal(struct xt_node *node, gpointer data, gboolean carbons_sent)
-{
-	struct im_connection *ic = data;
-	char *from = xt_find_attr(node, carbons_sent ? "to" : "from");
-	char *type = xt_find_attr(node, "type");
-	char *id = xt_find_attr(node, "id");
-	struct xt_node *body = xt_find_node(node->children, "body"), *c;
-	struct xt_node *request = xt_find_node(node->children, "request");
-	struct jabber_buddy *bud = NULL;
-	char *s, *room = NULL, *reason = NULL;
-
-	if (!from) {
-		return XT_HANDLED; /* Consider this packet corrupted. */
-	}
-
-	if (request && id && g_strcmp0(type, "groupchat") != 0 && !carbons_sent) {
-		/* Send a message receipt (XEP-0184), looking like this:
-		 * <message from='...' id='...' to='...'>
-		 *  <received xmlns='urn:xmpp:receipts' id='richard2-4.1.247'/>
-		 * </message>
-		 *
-		 * MUC messages are excluded, since receipts aren't supposed to be sent over MUCs
-		 * (XEP-0184 section 5.3) and replying to those may result in 'forbidden' errors.
-		 */
-		struct xt_node *received, *receipt;
-
-		received = xt_new_node("received", NULL, NULL);
-		xt_add_attr(received, "xmlns", XMLNS_RECEIPTS);
-		xt_add_attr(received, "id", id);
-		receipt = jabber_make_packet("message", NULL, from, received);
-
-		jabber_write_packet(ic, receipt);
-		xt_free_node(receipt);
-	}
-
-	bud = jabber_buddy_by_jid(ic, from, GET_BUDDY_EXACT);
-
-	if (type && strcmp(type, "error") == 0) {
-		/* Handle type=error packet. */
-	} else if (type && from && strcmp(type, "groupchat") == 0) {
-		jabber_chat_pkt_message(ic, bud, node);
-	} else { /* "chat", "normal", "headline", no-type or whatever. Should all be pretty similar. */
-		GString *fullmsg = g_string_new("");
-
-		for (c = node->children; (c = xt_find_node(c, "x")); c = c->next) {
-			char *ns = xt_find_attr(c, "xmlns");
-			struct xt_node *inv;
-
-			if (ns && strcmp(ns, XMLNS_MUC_USER) == 0 &&
-			    (inv = xt_find_node(c->children, "invite"))) {
-				/* This is an invitation. Set some vars which
-				   will be passed to imcb_chat_invite() below. */
-				room = from;
-				if ((from = xt_find_attr(inv, "from")) == NULL) {
-					from = room;
-				}
-				if ((inv = xt_find_node(inv->children, "reason")) && inv->text_len > 0) {
-					reason = inv->text;
-				}
-			}
-		}
-
-		if ((s = strchr(from, '/'))) {
-			if (bud) {
-				bud->last_msg = time(NULL);
-				from = bud->ext_jid ? bud->ext_jid : bud->bare_jid;
-			} else {
-				*s = 0; /* We need to generate a bare JID now. */
-			}
-		}
-
-		if (type && strcmp(type, "headline") == 0) {
-			if ((c = xt_find_node(node->children, "subject")) && c->text_len > 0) {
-				g_string_append_printf(fullmsg, "Headline: %s\n", c->text);
-			}
-
-			/* <x xmlns="jabber:x:oob"><url>http://....</url></x> can contain a URL, it seems. */
-			for (c = node->children; c; c = c->next) {
-				struct xt_node *url;
-
-				if ((url = xt_find_node(c->children, "url")) && url->text_len > 0) {
-					g_string_append_printf(fullmsg, "URL: %s\n", url->text);
-				}
-			}
-		} else if ((c = xt_find_node(node->children, "subject")) && c->text_len > 0 &&
-		           (!bud || !(bud->flags & JBFLAG_HIDE_SUBJECT))) {
-			g_string_append_printf(fullmsg, "<< \002BitlBee\002 - Message with subject: %s >>\n", c->text);
-			if (bud) {
-				bud->flags |= JBFLAG_HIDE_SUBJECT;
-			}
-		} else if (bud && !c) {
-			/* Yeah, possibly we're hiding changes to this field now. But nobody uses
-			   this for anything useful anyway, except GMail when people reply to an
-			   e-mail via chat, repeating the same subject all the time. I don't want
-			   to have to remember full subject strings for everyone. */
-			bud->flags &= ~JBFLAG_HIDE_SUBJECT;
-		}
-
-		if (body && body->text_len > 0) { /* Could be just a typing notification. */
-			fullmsg = g_string_append(fullmsg, body->text);
-		}
-
-		if (fullmsg->len > 0) {
-			imcb_buddy_msg(ic, from, fullmsg->str,
-			               carbons_sent ? OPT_SELFMESSAGE : 0, jabber_get_timestamp(node));
-		}
-		if (room) {
-			imcb_chat_invite(ic, room, from, reason);
-		}
-
-		g_string_free(fullmsg, TRUE);
-
-		/* Handling of incoming typing notifications. */
-		if (bud == NULL || carbons_sent) {
-			/* Can't handle these for unknown buddies.
-			   And ignore them if it's just carbons */
-		} else if (xt_find_node(node->children, "composing")) {
-			bud->flags |= JBFLAG_DOES_XEP85;
-			imcb_buddy_typing(ic, from, OPT_TYPING);
-		}
-		else if (xt_find_node(node->children, "active")) {
-			bud->flags |= JBFLAG_DOES_XEP85;
-
-			/* No need to send a "stopped typing" signal when there's a message. */
-			if (body == NULL) {
-				imcb_buddy_typing(ic, from, 0);
-			}
-		} else if (xt_find_node(node->children, "paused")) {
-			bud->flags |= JBFLAG_DOES_XEP85;
-			imcb_buddy_typing(ic, from, OPT_THINKING);
-		}
-
-		if (s) {
-			*s = '/'; /* And convert it back to a full JID. */
-		}
-	}
-
-	return XT_HANDLED;
-}
-
-static xt_status jabber_carbons_message(struct xt_node *node, gpointer data)
-{
-	struct im_connection *ic = data;
-	struct xt_node *wrap, *fwd, *msg;
-	gboolean carbons_sent;
-
-	if ((wrap = xt_find_node(node->children, "received"))) {
-		carbons_sent = FALSE;
-	} else if ((wrap = xt_find_node(node->children, "sent"))) {
-		carbons_sent = TRUE;
-	}
-
-	if (wrap == NULL || g_strcmp0(xt_find_attr(wrap, "xmlns"), XMLNS_CARBONS) != 0) {
-		return XT_NEXT;
-	}
-
-	if (!(fwd = xt_find_node(wrap->children, "forwarded")) ||
-	     (g_strcmp0(xt_find_attr(fwd, "xmlns"), XMLNS_FORWARDING) != 0) ||
-	    !(msg = xt_find_node(fwd->children, "message"))) {
-		imcb_log(ic, "Error: Invalid carbons message received");
-		return XT_ABORT;
-	}
-
-	return jabber_pkt_message_normal(msg, data, carbons_sent);
-}
-
-xt_status jabber_pkt_message(struct xt_node *node, gpointer data)
-{
-	struct im_connection *ic = data;
-	struct jabber_data *jd = ic->proto_data;
-	char *from = xt_find_attr(node, "from");
-
-	if (jabber_compare_jid(jd->me, from)) {    /* Probably a Carbons message */
-		xt_status st = jabber_carbons_message(node, data);
-		if (st == XT_HANDLED || st == XT_ABORT) {
-			return st;
-		}
-	}
-	return jabber_pkt_message_normal(node, data, FALSE);
-}
Index: protocols/jabber/nametab.h
===================================================================
--- protocols/jabber/nametab.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/nametab.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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,
+};
Index: protocols/jabber/pool.c
===================================================================
--- protocols/jabber/pool.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/pool.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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);
+
+}
Index: otocols/jabber/presence.c
===================================================================
--- protocols/jabber/presence.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,259 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Jabber module - Handling of presence (tags), etc                         *
-*                                                                           *
-*  Copyright 2006 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.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-\***************************************************************************/
-
-#include "jabber.h"
-
-xt_status jabber_pkt_presence(struct xt_node *node, gpointer data)
-{
-	struct im_connection *ic = data;
-	char *from = xt_find_attr(node, "from");
-	char *type = xt_find_attr(node, "type");        /* NULL should mean the person is online. */
-	struct xt_node *c, *cap;
-	struct jabber_buddy *bud, *send_presence = NULL;
-	int is_chat = 0;
-	char *s;
-
-	if (!from) {
-		return XT_HANDLED;
-	}
-
-	if ((s = strchr(from, '/'))) {
-		*s = 0;
-		if (jabber_chat_by_jid(ic, from)) {
-			is_chat = 1;
-		}
-		*s = '/';
-	}
-
-	if (type == NULL) {
-		if (!(bud = jabber_buddy_by_jid(ic, from, GET_BUDDY_EXACT | GET_BUDDY_CREAT))) {
-			/*
-			imcb_log( ic, "Warning: Could not handle presence information from JID: %s", from );
-			*/
-			return XT_HANDLED;
-		}
-
-		g_free(bud->away_message);
-		if ((c = xt_find_node(node->children, "status")) && c->text_len > 0) {
-			bud->away_message = g_strdup(c->text);
-		} else {
-			bud->away_message = NULL;
-		}
-
-		if ((c = xt_find_node(node->children, "show")) && c->text_len > 0) {
-			bud->away_state = (void *) jabber_away_state_by_code(c->text);
-		} else {
-			bud->away_state = NULL;
-		}
-
-		if ((c = xt_find_node(node->children, "priority")) && c->text_len > 0) {
-			bud->priority = atoi(c->text);
-		} else {
-			bud->priority = 0;
-		}
-
-		if (bud && (cap = xt_find_node(node->children, "c")) &&
-		    (s = xt_find_attr(cap, "xmlns")) && strcmp(s, XMLNS_CAPS) == 0) {
-			/* This <presence> stanza includes an XEP-0115
-			   capabilities part. Not too interesting, but we can
-			   see if it has an ext= attribute. */
-			s = xt_find_attr(cap, "ext");
-			if (s && (strstr(s, "cstates") || strstr(s, "chatstate"))) {
-				bud->flags |= JBFLAG_DOES_XEP85;
-			}
-
-			/* This field can contain more information like xhtml
-			   support, but we don't support that ourselves.
-			   Officially the ext= tag was deprecated, but enough
-			   clients do send it.
-
-			   (I'm aware that this is not the right way to use
-			   this field.) See for an explanation of ext=:
-			   http://www.xmpp.org/extensions/attic/xep-0115-1.3.html*/
-		}
-
-		if (is_chat) {
-			jabber_chat_pkt_presence(ic, bud, node);
-		} else {
-			send_presence = jabber_buddy_by_jid(ic, bud->bare_jid, 0);
-		}
-	} else if (strcmp(type, "unavailable") == 0) {
-		if ((bud = jabber_buddy_by_jid(ic, from, 0)) == NULL) {
-			/*
-			imcb_log( ic, "Warning: Received presence information from unknown JID: %s", from );
-			*/
-			return XT_HANDLED;
-		}
-
-		/* Handle this before we delete the JID. */
-		if (is_chat) {
-			jabber_chat_pkt_presence(ic, bud, node);
-		}
-
-		if (strchr(from, '/') == NULL) {
-			/* Sometimes servers send a type="unavailable" from a
-			   bare JID, which should mean that suddenly all
-			   resources for this JID disappeared. */
-			jabber_buddy_remove_bare(ic, from);
-		} else {
-			jabber_buddy_remove(ic, from);
-		}
-
-		if (is_chat) {
-			/* Nothing else to do for now? */
-		} else if ((s = strchr(from, '/'))) {
-			*s = 0;
-
-			/* If another resource is still available, send its presence
-			   information. */
-			if ((send_presence = jabber_buddy_by_jid(ic, from, 0)) == NULL) {
-				/* Otherwise, count him/her as offline now. */
-				imcb_buddy_status(ic, from, 0, NULL, NULL);
-			}
-
-			*s = '/';
-		} else {
-			imcb_buddy_status(ic, from, 0, NULL, NULL);
-		}
-	} else if (strcmp(type, "subscribe") == 0) {
-		jabber_buddy_ask(ic, from);
-	} else if (strcmp(type, "subscribed") == 0) {
-		/* Not sure about this one, actually... */
-		imcb_log(ic, "%s just accepted your authorization request", from);
-	} else if (strcmp(type, "unsubscribe") == 0 || strcmp(type, "unsubscribed") == 0) {
-		/* Do nothing here. Plenty of control freaks or over-curious
-		   souls get excited when they can see who still has them in
-		   their buddy list and who finally removed them. Somehow I
-		   got the impression that those are the people who get
-		   removed from many buddy lists for "some" reason...
-
-		   If you're one of those people, this is your chance to write
-		   your first line of code in C... */
-	} else if (strcmp(type, "error") == 0) {
-		return jabber_cache_handle_packet(ic, node);
-
-		/*
-		struct jabber_error *err;
-		if( ( c = xt_find_node( node->children, "error" ) ) )
-		{
-		        err = jabber_error_parse( c, XMLNS_STANZA_ERROR );
-		        imcb_error( ic, "Stanza (%s) error: %s%s%s", node->name,
-		                    err->code, err->text ? ": " : "",
-		                    err->text ? err->text : "" );
-		        jabber_error_free( err );
-		} */
-	}
-
-	if (send_presence) {
-		int is_away = 0;
-
-		if (send_presence->away_state &&
-		    strcmp(send_presence->away_state->code, "chat") != 0) {
-			is_away = OPT_AWAY;
-		}
-
-		imcb_buddy_status(ic, send_presence->bare_jid, OPT_LOGGED_IN | is_away,
-		                  is_away ? send_presence->away_state->full_name : NULL,
-		                  send_presence->away_message);
-	}
-
-	return XT_HANDLED;
-}
-
-static char *choose_priority(struct im_connection *ic)
-{
-	struct jabber_data *jd = ic->proto_data;
-	char *prio = set_getstr(&ic->acc->set, "priority");
-
-	if (jd->away_state && jd->away_state->full_name != NULL) {
-		int new_prio = (atoi(prio) - 5);
-		if (new_prio < 0) {
-			new_prio = 0;
-		}
-		return g_strdup_printf("%d", new_prio);
-	}
-
-	return g_strdup(prio);
-}
-
-/* Whenever presence information is updated, call this function to inform the
-   server. */
-int presence_send_update(struct im_connection *ic)
-{
-	struct jabber_data *jd = ic->proto_data;
-	struct xt_node *node, *cap;
-	GSList *l;
-	int st;
-	char *prio = choose_priority(ic);
-
-	node = jabber_make_packet("presence", NULL, NULL, NULL);
-	xt_add_child(node, xt_new_node("priority", prio, NULL));
-	if (jd->away_state) {
-		xt_add_child(node, xt_new_node("show", jd->away_state->code, NULL));
-	}
-	if (jd->away_message) {
-		xt_add_child(node, xt_new_node("status", jd->away_message, NULL));
-	}
-
-	/* This makes the packet slightly bigger, but clients interested in
-	   capabilities can now cache the discovery info. This reduces the
-	   usual post-login iq-flood. See XEP-0115. At least libpurple and
-	   Trillian seem to do this right. */
-	cap = xt_new_node("c", NULL, NULL);
-	xt_add_attr(cap, "xmlns", XMLNS_CAPS);
-	xt_add_attr(cap, "node", "http://bitlbee.org/xmpp/caps");
-	xt_add_attr(cap, "ver", BITLBEE_VERSION);   /* The XEP wants this hashed, but nobody's doing that. */
-	xt_add_child(node, cap);
-
-	st = jabber_write_packet(ic, node);
-
-	/* Have to send this update to all groupchats too, the server won't
-	   do this automatically. */
-	for (l = ic->groupchats; l && st; l = l->next) {
-		struct groupchat *c = l->data;
-		struct jabber_chat *jc = c->data;
-
-		xt_add_attr(node, "to", jc->my_full_jid);
-		st = jabber_write_packet(ic, node);
-	}
-
-	xt_free_node(node);
-	g_free(prio);
-	return st;
-}
-
-/* Send a subscribe/unsubscribe request to a buddy. */
-int presence_send_request(struct im_connection *ic, char *handle, char *request)
-{
-	struct xt_node *node;
-	int st;
-
-	node = jabber_make_packet("presence", NULL, NULL, NULL);
-	xt_add_attr(node, "to", handle);
-	xt_add_attr(node, "type", request);
-
-	st = jabber_write_packet(ic, node);
-
-	xt_free_node(node);
-	return st;
-}
Index: otocols/jabber/s5bytestream.c
===================================================================
--- protocols/jabber/s5bytestream.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,1143 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Jabber module - SOCKS5 Bytestreams ( XEP-0065 )                          *
-*                                                                           *
-*  Copyright 2007 Uli Meis <a.sporto+bee@gmail.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.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-\***************************************************************************/
-
-#include "jabber.h"
-#include "lib/ftutil.h"
-#include <poll.h>
-
-struct bs_transfer {
-
-	struct jabber_transfer *tf;
-
-	jabber_streamhost_t *sh;
-	GSList *streamhosts;
-
-	enum {
-		BS_PHASE_CONNECT,
-		BS_PHASE_CONNECTED,
-		BS_PHASE_REQUEST,
-		BS_PHASE_REPLY
-	} phase;
-
-	/* SHA1( SID + Initiator JID + Target JID) */
-	char *pseudoaddr;
-
-	gint connect_timeout;
-
-	char peek_buf[64];
-	int peek_buf_len;
-};
-
-struct socks5_message {
-	unsigned char ver;
-	union {
-		unsigned char cmd;
-		unsigned char rep;
-	} cmdrep;
-	unsigned char rsv;
-	unsigned char atyp;
-	unsigned char addrlen;
-	unsigned char address[40];
-	in_port_t port;
-} __attribute__ ((packed));
-
-char *socks5_reply_code[] = {
-	"succeeded",
-	"general SOCKS server failure",
-	"connection not allowed by ruleset",
-	"Network unreachable",
-	"Host unreachable",
-	"Connection refused",
-	"TTL expired",
-	"Command not supported",
-	"Address type not supported",
-	"unassigned"
-};
-
-/* connect() timeout in seconds. */
-#define JABBER_BS_CONTIMEOUT 15
-/* listen timeout */
-#define JABBER_BS_LISTEN_TIMEOUT  90
-
-/* very useful */
-#define ASSERTSOCKOP(op, msg) \
-	if ((op) == -1) { \
-		return jabber_bs_abort(bt, msg ": %s", strerror(errno)); }
-
-gboolean jabber_bs_abort(struct bs_transfer *bt, char *format, ...) G_GNUC_PRINTF(2, 3);
-void jabber_bs_canceled(file_transfer_t *ft, char *reason);
-void jabber_bs_free_transfer(file_transfer_t *ft);
-gboolean jabber_bs_connect_timeout(gpointer data, gint fd, b_input_condition cond);
-gboolean jabber_bs_poll(struct bs_transfer *bt, int fd, short *revents);
-gboolean jabber_bs_peek(struct bs_transfer *bt, void *buffer, int buflen);
-
-void jabber_bs_recv_answer_request(struct bs_transfer *bt);
-gboolean jabber_bs_recv_read(gpointer data, gint fd, b_input_condition cond);
-gboolean jabber_bs_recv_write_request(file_transfer_t *ft);
-gboolean jabber_bs_recv_handshake(gpointer data, gint fd, b_input_condition cond);
-gboolean jabber_bs_recv_handshake_abort(struct bs_transfer *bt, char *error);
-int jabber_bs_recv_request(struct im_connection *ic, struct xt_node *node, struct xt_node *qnode);
-
-gboolean jabber_bs_send_handshake_abort(struct bs_transfer *bt, char *error);
-gboolean jabber_bs_send_request(struct jabber_transfer *tf, GSList *streamhosts);
-gboolean jabber_bs_send_handshake(gpointer data, gint fd, b_input_condition cond);
-static xt_status jabber_bs_send_handle_activate(struct im_connection *ic, struct xt_node *node, struct xt_node *orig);
-void jabber_bs_send_activate(struct bs_transfer *bt);
-
-/*
- * Frees a bs_transfer struct and calls the SI free function
- */
-void jabber_bs_free_transfer(file_transfer_t *ft)
-{
-	struct jabber_transfer *tf = ft->data;
-	struct bs_transfer *bt = tf->streamhandle;
-	jabber_streamhost_t *sh;
-
-	if (bt->connect_timeout) {
-		b_event_remove(bt->connect_timeout);
-		bt->connect_timeout = 0;
-	}
-
-	if (tf->watch_in) {
-		b_event_remove(tf->watch_in);
-		tf->watch_in = 0;
-	}
-
-	if (tf->watch_out) {
-		b_event_remove(tf->watch_out);
-		tf->watch_out = 0;
-	}
-
-	g_free(bt->pseudoaddr);
-
-	while (bt->streamhosts) {
-		sh = bt->streamhosts->data;
-		bt->streamhosts = g_slist_remove(bt->streamhosts, sh);
-		g_free(sh->jid);
-		g_free(sh->host);
-		g_free(sh);
-	}
-
-	g_free(bt);
-
-	jabber_si_free_transfer(ft);
-}
-
-/*
- * Checks if buflen data is available on the socket and
- * writes it to buffer if that's the case.
- */
-gboolean jabber_bs_peek(struct bs_transfer *bt, void *buffer, int buflen)
-{
-	int ret;
-	int fd = bt->tf->fd;
-
-	if (buflen > sizeof(bt->peek_buf)) {
-		return jabber_bs_abort(bt, "BUG: %d > sizeof(peek_buf)", buflen);
-	}
-
-	ASSERTSOCKOP(ret = recv(fd, bt->peek_buf + bt->peek_buf_len,
-	                        buflen - bt->peek_buf_len, 0), "recv() on SOCKS5 connection");
-
-	if (ret == 0) {
-		return jabber_bs_abort(bt, "Remote end closed connection");
-	}
-
-	bt->peek_buf_len += ret;
-	memcpy(buffer, bt->peek_buf, bt->peek_buf_len);
-
-	if (bt->peek_buf_len == buflen) {
-		/* If we have everything the caller wanted, reset the peek buffer. */
-		bt->peek_buf_len = 0;
-		return buflen;
-	} else {
-		return bt->peek_buf_len;
-	}
-}
-
-
-/*
- * This function is scheduled in bs_handshake via b_timeout_add after a (non-blocking) connect().
- */
-gboolean jabber_bs_connect_timeout(gpointer data, gint fd, b_input_condition cond)
-{
-	struct bs_transfer *bt = data;
-
-	bt->connect_timeout = 0;
-
-	jabber_bs_abort(bt, "no connection after %d seconds",
-	                bt->tf->ft->sending ? JABBER_BS_LISTEN_TIMEOUT : JABBER_BS_CONTIMEOUT);
-
-	return FALSE;
-}
-
-/*
- * Polls the socket, checks for errors and removes a connect timer
- * if there is one.
- */
-gboolean jabber_bs_poll(struct bs_transfer *bt, int fd, short *revents)
-{
-	struct pollfd pfd = { .fd = fd, .events = POLLHUP | POLLERR };
-
-	if (bt->connect_timeout) {
-		b_event_remove(bt->connect_timeout);
-		bt->connect_timeout = 0;
-	}
-
-	ASSERTSOCKOP(poll(&pfd, 1, 0), "poll()")
-
-	if (pfd.revents & POLLERR) {
-		int sockerror;
-		socklen_t errlen = sizeof(sockerror);
-
-		if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &sockerror, &errlen)) {
-			return jabber_bs_abort(bt,
-			                       "getsockopt() failed, unknown socket error during SOCKS5 handshake (weird!)");
-		}
-
-		if (bt->phase == BS_PHASE_CONNECTED) {
-			return jabber_bs_abort(bt, "connect failed: %s", strerror(sockerror));
-		}
-
-		return jabber_bs_abort(bt, "Socket error during SOCKS5 handshake(weird!): %s", strerror(sockerror));
-	}
-
-	if (pfd.revents & POLLHUP) {
-		return jabber_bs_abort(bt, "Remote end closed connection");
-	}
-
-	*revents = pfd.revents;
-
-	return TRUE;
-}
-
-/*
- * Used for receive and send path.
- */
-gboolean jabber_bs_abort(struct bs_transfer *bt, char *format, ...)
-{
-	va_list params;
-
-	va_start(params, format);
-	char error[128];
-
-	if (vsnprintf(error, 128, format, params) < 0) {
-		sprintf(error, "internal error parsing error string (BUG)");
-	}
-	va_end(params);
-	if (bt->tf->ft->sending) {
-		return jabber_bs_send_handshake_abort(bt, error);
-	} else {
-		return jabber_bs_recv_handshake_abort(bt, error);
-	}
-}
-
-void jabber_bs_remove_events(struct bs_transfer *bt)
-{
-	struct jabber_transfer *tf = bt->tf;
-
-	if (tf->watch_out) {
-		b_event_remove(tf->watch_out);
-		tf->watch_out = 0;
-	}
-
-	if (tf->watch_in) {
-		b_event_remove(tf->watch_in);
-		tf->watch_in = 0;
-	}
-
-	if (tf->fd != -1) {
-		closesocket(tf->fd);
-		tf->fd = -1;
-	}
-
-	if (bt->connect_timeout) {
-		b_event_remove(bt->connect_timeout);
-		bt->connect_timeout = 0;
-	}
-}
-
-/* Bad luck */
-void jabber_bs_canceled(file_transfer_t *ft, char *reason)
-{
-	struct jabber_transfer *tf = ft->data;
-
-	imcb_log(tf->ic, "File transfer aborted: %s", reason);
-}
-
-static struct jabber_transfer *get_ft_by_sid(GSList *tflist, char *sid) {
-	GSList *l;
-	for (l = tflist; l; l = g_slist_next(l)) {
-		struct jabber_transfer *tft = l->data;
-		if ((strcmp(tft->sid, sid) == 0)) { 
-			return tft;
-		}
-	}
-	return NULL;
-}
-
-/* SHA1( SID + Initiator JID + Target JID ) is given to the streamhost which it will match against the initiator's value
- * Returns a newly allocated string */
-static char *generate_pseudoaddr(char *sid, char *ini_jid, char *tgt_jid) {
-	char *contents = g_strconcat(sid, ini_jid, tgt_jid, NULL);
-	char *hash_hex = g_compute_checksum_for_string(G_CHECKSUM_SHA1, contents, -1);
-	g_free(contents);
-	return hash_hex;
-}
-
-static jabber_streamhost_t *jabber_streamhost_new(char *jid, char *host, int port)
-{
-	jabber_streamhost_t *sh = g_new0(jabber_streamhost_t, 1);
-	sh->jid = g_strdup(jid);
-	sh->host = g_strdup(host);
-	if (port) {
-		g_snprintf(sh->port, sizeof(sh->port), "%u", port);
-	}
-	return sh;
-}
-
-/*
- * Parses an incoming bytestream request and calls jabber_bs_handshake on success.
- */
-int jabber_bs_recv_request(struct im_connection *ic, struct xt_node *node, struct xt_node *qnode)
-{
-	char *sid, *ini_jid, *tgt_jid, *mode, *iq_id;
-	struct jabber_data *jd = ic->proto_data;
-	struct jabber_transfer *tf = NULL;
-	struct bs_transfer *bt;
-	GSList *shlist = NULL;
-	struct xt_node *shnode;
-
-	if (!(iq_id   = xt_find_attr(node, "id")) ||
-	    !(ini_jid = xt_find_attr(node, "from")) ||
-	    !(tgt_jid = xt_find_attr(node, "to")) ||
-	    !(sid     = xt_find_attr(qnode, "sid"))) {
-		imcb_log(ic, "WARNING: Received incomplete SI bytestream request");
-		return XT_HANDLED;
-	}
-
-	if ((mode = xt_find_attr(qnode, "mode")) &&
-	    (strcmp(mode, "tcp") != 0)) {
-		imcb_log(ic, "WARNING: Received SI Request for unsupported bytestream mode %s",
-		         xt_find_attr(qnode, "mode"));
-		return XT_HANDLED;
-	}
-
-	shnode = qnode->children;
-	while ((shnode = xt_find_node(shnode, "streamhost"))) {
-		char *jid, *host, *port_s;
-		int port;
-		if ((jid = xt_find_attr(shnode, "jid")) &&
-		    (host = xt_find_attr(shnode, "host")) &&
-		    (port_s = xt_find_attr(shnode, "port")) &&
-		    (sscanf(port_s, "%d", &port) == 1)) {
-			shlist = g_slist_append(shlist, jabber_streamhost_new(jid, host, port));
-		}
-		shnode = shnode->next;
-	}
-
-	if (!shlist) {
-		imcb_log(ic, "WARNING: Received incomplete SI bytestream request, no parseable streamhost entries");
-		return XT_HANDLED;
-	}
-
-	if (!(tf = get_ft_by_sid(jd->filetransfers, sid))) {
-		imcb_log(ic, "WARNING: Received bytestream request from %s that doesn't match an SI request", ini_jid);
-		return XT_HANDLED;
-	}
-
-	/* iq_id and canceled can be reused since SI is done */
-	g_free(tf->iq_id);
-	tf->iq_id = g_strdup(iq_id);
-
-	tf->ft->canceled = jabber_bs_canceled;
-
-	bt = g_new0(struct bs_transfer, 1);
-	bt->tf = tf;
-	bt->streamhosts = shlist;
-	bt->sh = shlist->data;
-	bt->phase = BS_PHASE_CONNECT;
-	bt->pseudoaddr = generate_pseudoaddr(sid, ini_jid, tgt_jid);
-	tf->streamhandle = bt;
-	tf->ft->free = jabber_bs_free_transfer;
-
-	jabber_bs_recv_handshake(bt, -1, 0);
-
-	return XT_HANDLED;
-}
-
-/*
- * This is what a protocol handshake can look like in cooperative multitasking :)
- * Might be confusing at first because it's called from different places and is recursing.
- * (places being the event thread, bs_request, bs_handshake_abort, and itself)
- *
- * All in all, it turned out quite nice :)
- */
-gboolean jabber_bs_recv_handshake(gpointer data, gint fd, b_input_condition cond)
-{
-
-	struct bs_transfer *bt = data;
-	short revents;
-	int gret;
-
-	if ((fd != -1) && !jabber_bs_poll(bt, fd, &revents)) {
-		return FALSE;
-	}
-
-	switch (bt->phase) {
-	case BS_PHASE_CONNECT:
-	{
-		struct addrinfo hints, *rp;
-
-		memset(&hints, 0, sizeof(struct addrinfo));
-		hints.ai_socktype = SOCK_STREAM;
-
-		if ((gret = getaddrinfo(bt->sh->host, bt->sh->port, &hints, &rp)) != 0) {
-			return jabber_bs_abort(bt, "getaddrinfo() failed: %s", gai_strerror(gret));
-		}
-
-		ASSERTSOCKOP(bt->tf->fd = fd = socket(rp->ai_family, rp->ai_socktype, 0), "Opening socket");
-
-		sock_make_nonblocking(fd);
-
-		imcb_log(bt->tf->ic, "File %s: Connecting to streamhost %s:%s", bt->tf->ft->file_name, bt->sh->host,
-		         bt->sh->port);
-
-		if ((connect(fd, rp->ai_addr, rp->ai_addrlen) == -1) &&
-		    (errno != EINPROGRESS)) {
-			return jabber_bs_abort(bt, "connect() failed: %s", strerror(errno));
-		}
-
-		freeaddrinfo(rp);
-
-		bt->phase = BS_PHASE_CONNECTED;
-
-		bt->tf->watch_out = b_input_add(fd, B_EV_IO_WRITE, jabber_bs_recv_handshake, bt);
-
-		/* since it takes forever(3mins?) till connect() fails on itself we schedule a timeout */
-		bt->connect_timeout = b_timeout_add(JABBER_BS_CONTIMEOUT * 1000, jabber_bs_connect_timeout, bt);
-
-		bt->tf->watch_in = 0;
-		return FALSE;
-	}
-	case BS_PHASE_CONNECTED:
-	{
-		struct {
-			unsigned char ver;
-			unsigned char nmethods;
-			unsigned char method;
-		} socks5_hello = {
-			.ver = 5,
-			.nmethods = 1,
-			.method = 0x00         /* no auth */
-			          /* one could also implement username/password. If you know
-			           * a jabber client or proxy that actually does it, tell me.
-			           */
-		};
-
-		ASSERTSOCKOP(send(fd, &socks5_hello, sizeof(socks5_hello), 0), "Sending auth request");
-
-		bt->phase = BS_PHASE_REQUEST;
-
-		bt->tf->watch_in = b_input_add(fd, B_EV_IO_READ, jabber_bs_recv_handshake, bt);
-
-		bt->tf->watch_out = 0;
-		return FALSE;
-	}
-	case BS_PHASE_REQUEST:
-	{
-		struct socks5_message socks5_connect =
-		{
-			.ver = 5,
-			.cmdrep.cmd = 0x01,
-			.rsv = 0,
-			.atyp = 0x03,
-			.addrlen = strlen(bt->pseudoaddr),
-			.port = 0
-		};
-		int ret;
-		char buf[2];
-
-		/* If someone's trying to be funny and sends only one byte at a time we'll fail :) */
-		ASSERTSOCKOP(ret = recv(fd, buf, 2, 0), "Receiving auth reply");
-
-		if (!(ret == 2) ||
-		    !(buf[0] == 5) ||
-		    !(buf[1] == 0)) {
-			return jabber_bs_abort(bt, "Auth not accepted by streamhost (reply: len=%d, ver=%d, status=%d)",
-			                       ret, buf[0], buf[1]);
-		}
-
-		/* copy hash into connect message */
-		memcpy(socks5_connect.address, bt->pseudoaddr, socks5_connect.addrlen);
-
-		ASSERTSOCKOP(send(fd, &socks5_connect, sizeof(struct socks5_message), 0), "Sending SOCKS5 Connect");
-
-		bt->phase = BS_PHASE_REPLY;
-
-		return TRUE;
-	}
-	case BS_PHASE_REPLY:
-	{
-		struct socks5_message socks5_reply = {0};
-		int ret;
-
-		if (!(ret = jabber_bs_peek(bt, &socks5_reply, sizeof(struct socks5_message)))) {
-			return FALSE;
-		}
-
-		if (ret < 5) {         /* header up to address length */
-			return TRUE;
-		} else if (ret < sizeof(struct socks5_message)) {
-			/* Either a buggy proxy or just one that doesnt regard
-			 * the SHOULD in XEP-0065 saying the reply SHOULD
-			 * contain the address. We'll take it, so make sure the
-			 * next jabber_bs_peek starts with an empty buffer. */
-			bt->peek_buf_len = 0;
-		}
-
-		if (!(socks5_reply.ver == 5) ||
-		    !(socks5_reply.cmdrep.rep == 0)) {
-			char errstr[128] = "";
-			if ((socks5_reply.ver == 5) && (socks5_reply.cmdrep.rep <
-			                                (sizeof(socks5_reply_code) / sizeof(socks5_reply_code[0])))) {
-				sprintf(errstr, "with \"%s\" ", socks5_reply_code[ socks5_reply.cmdrep.rep ]);
-			}
-			return jabber_bs_abort(bt,
-			                       "SOCKS5 CONNECT failed %s(reply: ver=%d, rep=%d, atyp=%d, addrlen=%d)",
-			                       errstr,
-			                       socks5_reply.ver,
-			                       socks5_reply.cmdrep.rep,
-			                       socks5_reply.atyp,
-			                       socks5_reply.addrlen);
-		}
-
-		/* usually a proxy sends back the 40 bytes address but I encountered at least one (of jabber.cz)
-		 * that sends atyp=0 addrlen=0 and only 6 bytes (one less than one would expect).
-		 * Therefore I removed the wait for more bytes. Since we don't care about what else the proxy
-		 * is sending, it should not matter */
-
-		if (bt->tf->ft->sending) {
-			jabber_bs_send_activate(bt);
-		} else {
-			jabber_bs_recv_answer_request(bt);
-		}
-
-		return FALSE;
-	}
-	default:
-		/* BUG */
-		imcb_log(bt->tf->ic, "BUG in file transfer code: undefined handshake phase");
-
-		bt->tf->watch_in = 0;
-		return FALSE;
-	}
-}
-
-/*
- * If the handshake failed we can try the next streamhost, if there is one.
- * An intelligent sender would probably specify himself as the first streamhost and
- * a proxy as the second (Kopete and PSI are examples here). That way, a (potentially)
- * slow proxy is only used if necessary. This of course also means, that the timeout
- * per streamhost should be kept short. If one or two firewalled adresses are specified,
- * they have to timeout first before a proxy is tried.
- */
-gboolean jabber_bs_recv_handshake_abort(struct bs_transfer *bt, char *error)
-{
-	struct jabber_transfer *tf = bt->tf;
-	struct xt_node *reply, *iqnode;
-	GSList *shlist;
-
-	imcb_log(tf->ic, "Transferring file %s: connection to streamhost %s:%s failed (%s)",
-	         tf->ft->file_name,
-	         bt->sh->host,
-	         bt->sh->port,
-	         error);
-
-	/* Alright, this streamhost failed, let's try the next... */
-	bt->phase = BS_PHASE_CONNECT;
-	shlist = g_slist_find(bt->streamhosts, bt->sh);
-	if (shlist && shlist->next) {
-		bt->sh = shlist->next->data;
-		jabber_bs_remove_events(bt);
-		return jabber_bs_recv_handshake(bt, -1, 0);
-	}
-
-
-	/* out of stream hosts */
-
-	iqnode = jabber_make_packet("iq", "result", tf->ini_jid, NULL);
-	reply = jabber_make_error_packet(iqnode, "item-not-found", "cancel", "404");
-	xt_free_node(iqnode);
-
-	xt_add_attr(reply, "id", tf->iq_id);
-
-	if (!jabber_write_packet(tf->ic, reply)) {
-		imcb_log(tf->ic, "WARNING: Error transmitting bytestream response");
-	}
-	xt_free_node(reply);
-
-	imcb_file_canceled(tf->ic, tf->ft, "couldn't connect to any streamhosts");
-
-	/* MUST always return FALSE! */
-	return FALSE;
-}
-
-/*
- * After the SOCKS5 handshake succeeds we need to inform the initiator which streamhost we chose.
- * If he is the streamhost himself, he might already know that. However, if it's a proxy,
- * the initiator will have to make a connection himself.
- */
-void jabber_bs_recv_answer_request(struct bs_transfer *bt)
-{
-	struct jabber_transfer *tf = bt->tf;
-	struct xt_node *reply;
-
-	imcb_log(tf->ic, "File %s: established SOCKS5 connection to %s:%s",
-	         tf->ft->file_name,
-	         bt->sh->host,
-	         bt->sh->port);
-
-	tf->ft->data = tf;
-	tf->watch_in = b_input_add(tf->fd, B_EV_IO_READ, jabber_bs_recv_read, bt);
-	tf->ft->write_request = jabber_bs_recv_write_request;
-
-	reply = xt_new_node("streamhost-used", NULL, NULL);
-	xt_add_attr(reply, "jid", bt->sh->jid);
-
-	reply = xt_new_node("query", NULL, reply);
-	xt_add_attr(reply, "xmlns", XMLNS_BYTESTREAMS);
-
-	reply = jabber_make_packet("iq", "result", tf->ini_jid, reply);
-
-	xt_add_attr(reply, "id", tf->iq_id);
-
-	if (!jabber_write_packet(tf->ic, reply)) {
-		imcb_file_canceled(tf->ic, tf->ft, "Error transmitting bytestream response");
-	}
-	xt_free_node(reply);
-}
-
-/*
- * This function is called from write_request directly. If no data is available, it will install itself
- * as a watcher for input on fd and once that happens, deliver the data and unschedule itself again.
- */
-gboolean jabber_bs_recv_read(gpointer data, gint fd, b_input_condition cond)
-{
-	int ret;
-	struct bs_transfer *bt = data;
-	struct jabber_transfer *tf = bt->tf;
-
-	if (fd != -1) { /* called via event thread */
-		tf->watch_in = 0;
-		ASSERTSOCKOP(ret = recv(fd, tf->ft->buffer, sizeof(tf->ft->buffer), 0), "Receiving");
-	} else {
-		/* called directly. There might not be any data available. */
-		if (((ret = recv(tf->fd, tf->ft->buffer, sizeof(tf->ft->buffer), 0)) == -1) &&
-		    (errno != EAGAIN)) {
-			return jabber_bs_abort(bt, "Receiving: %s", strerror(errno));
-		}
-
-		if ((ret == -1) && (errno == EAGAIN)) {
-			tf->watch_in = b_input_add(tf->fd, B_EV_IO_READ, jabber_bs_recv_read, bt);
-			return FALSE;
-		}
-	}
-
-	/* shouldn't happen since we know the file size */
-	if (ret == 0) {
-		return jabber_bs_abort(bt, "Remote end closed connection");
-	}
-
-	tf->bytesread += ret;
-
-	if (tf->bytesread >= tf->ft->file_size) {
-		imcb_file_finished(tf->ic, tf->ft);
-	}
-
-	tf->ft->write(tf->ft, tf->ft->buffer, ret);
-
-	return FALSE;
-}
-
-/*
- * imc callback that is invoked when it is ready to receive some data.
- */
-gboolean jabber_bs_recv_write_request(file_transfer_t *ft)
-{
-	struct jabber_transfer *tf = ft->data;
-
-	if (tf->watch_in) {
-		imcb_file_canceled(tf->ic, ft,
-		                   "BUG in jabber file transfer: write_request called when already watching for input");
-		return FALSE;
-	}
-
-	jabber_bs_recv_read(tf->streamhandle, -1, 0);
-
-	return TRUE;
-}
-
-/*
- * Issues a write_request to imc.
- * */
-gboolean jabber_bs_send_can_write(gpointer data, gint fd, b_input_condition cond)
-{
-	struct bs_transfer *bt = data;
-
-	bt->tf->watch_out = 0;
-
-	bt->tf->ft->write_request(bt->tf->ft);
-
-	return FALSE;
-}
-
-/*
- * This should only be called if we can write, so just do it.
- * Add a write watch so we can write more during the next cycle (if possible).
- */
-gboolean jabber_bs_send_write(file_transfer_t *ft, char *buffer, unsigned int len)
-{
-	struct jabber_transfer *tf = ft->data;
-	struct bs_transfer *bt = tf->streamhandle;
-	int ret;
-
-	if (tf->watch_out) {
-		return jabber_bs_abort(bt, "BUG: write() called while watching ");
-	}
-
-	/* TODO: catch broken pipe */
-	ASSERTSOCKOP(ret = send(tf->fd, buffer, len, 0), "Sending");
-
-	tf->byteswritten += ret;
-
-	/* TODO: this should really not be fatal */
-	if (ret < len) {
-		return jabber_bs_abort(bt, "send() sent %d instead of %d (send buffer too big!)", ret, len);
-	}
-
-	if (tf->byteswritten >= ft->file_size) {
-		imcb_file_finished(tf->ic, ft);
-	} else {
-		bt->tf->watch_out = b_input_add(tf->fd, B_EV_IO_WRITE, jabber_bs_send_can_write, bt);
-	}
-
-	return TRUE;
-}
-
-/*
- * Handles the reply by the receiver containing the used streamhost.
- */
-static xt_status jabber_bs_send_handle_reply(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
-{
-	struct jabber_transfer *tf = NULL;
-	struct jabber_data *jd = ic->proto_data;
-	struct bs_transfer *bt;
-	struct xt_node *c;
-	char *sid, *jid;
-
-	if (!(c = xt_find_node(node->children, "query")) ||
-	    !(c = xt_find_node(c->children, "streamhost-used")) ||
-	    !(jid = xt_find_attr(c, "jid"))) {
-		imcb_log(ic, "WARNING: Received incomplete bytestream reply");
-		return XT_HANDLED;
-	}
-
-	if (!(c = xt_find_node(orig->children, "query")) ||
-	    !(sid = xt_find_attr(c, "sid"))) {
-		imcb_log(ic, "WARNING: Error parsing request corresponding to the incoming bytestream reply");
-		return XT_HANDLED;
-	}
-
-	/* Let's see if we can find out what this bytestream should be for... */
-
-	if (!(tf = get_ft_by_sid(jd->filetransfers, sid))) {
-		imcb_log(ic, "WARNING: Received SOCKS5 bytestream reply to unknown request");
-		return XT_HANDLED;
-	}
-
-	bt = tf->streamhandle;
-
-	tf->accepted = TRUE;
-
-	if (strcmp(jid, tf->ini_jid) == 0) {
-		/* we're streamhost and target */
-		if (bt->phase == BS_PHASE_REPLY) {
-			/* handshake went through, let's start transferring */
-			tf->ft->write_request(tf->ft);
-		}
-	} else {
-		/* using a proxy, abort listen */
-
-		jabber_bs_remove_events(bt);
-
-		GSList *shlist;
-		for (shlist = jd->streamhosts; shlist; shlist = g_slist_next(shlist)) {
-			jabber_streamhost_t *sh = shlist->data;
-			if (strcmp(sh->jid, jid) == 0) {
-				bt->sh = sh;
-				jabber_bs_recv_handshake(bt, -1, 0);
-				return XT_HANDLED;
-			}
-		}
-
-		imcb_log(ic, "WARNING: Received SOCKS5 bytestream reply with unknown streamhost %s", jid);
-	}
-
-	return XT_HANDLED;
-}
-
-/*
- * Tell the proxy to activate the stream. Looks like this:
- *
- * <iq type=set>
- *      <query xmlns=bs sid=sid>
- *              <activate>tgt_jid</activate>
- *      </query>
- * </iq>
- */
-void jabber_bs_send_activate(struct bs_transfer *bt)
-{
-	struct xt_node *node;
-
-	node = xt_new_node("activate", bt->tf->tgt_jid, NULL);
-	node = xt_new_node("query", NULL, node);
-	xt_add_attr(node, "xmlns", XMLNS_BYTESTREAMS);
-	xt_add_attr(node, "sid", bt->tf->sid);
-	node = jabber_make_packet("iq", "set", bt->sh->jid, node);
-
-	jabber_cache_add(bt->tf->ic, node, jabber_bs_send_handle_activate);
-
-	jabber_write_packet(bt->tf->ic, node);
-}
-
-/*
- * The proxy has activated the bytestream.
- * We can finally start pushing some data out.
- */
-static xt_status jabber_bs_send_handle_activate(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
-{
-	char *sid;
-	struct jabber_transfer *tf = NULL;
-	struct xt_node *query;
-	struct jabber_data *jd = ic->proto_data;
-
-	query = xt_find_node(orig->children, "query");
-	sid = xt_find_attr(query, "sid");
-
-	if (!(tf = get_ft_by_sid(jd->filetransfers, sid))) {
-		imcb_log(ic, "WARNING: Received SOCKS5 bytestream activation for unknown stream");
-		return XT_HANDLED;
-	}
-
-	imcb_log(tf->ic, "File %s: SOCKS5 handshake and activation successful! Transfer about to start...",
-	         tf->ft->file_name);
-
-	/* handshake went through, let's start transferring */
-	tf->ft->write_request(tf->ft);
-
-	return XT_HANDLED;
-}
-
-jabber_streamhost_t *jabber_si_parse_proxy(struct im_connection *ic, char *proxy)
-{
-	char *host, *port, *jid;
-	jabber_streamhost_t *sh;
-
-	if (((host = strchr(proxy, ',')) == 0) ||
-	    ((port = strchr(host + 1, ',')) == 0)) {
-		imcb_log(ic, "Error parsing proxy setting: \"%s\" (ignored)", proxy);
-		return NULL;
-	}
-
-	jid = proxy;
-	*host++ = '\0';
-	*port++ = '\0';
-
-	sh = jabber_streamhost_new(jid, host, 0);
-	strncpy(sh->port, port, sizeof(sh->port));
-
-	return sh;
-}
-
-void jabber_si_set_proxies(struct bs_transfer *bt)
-{
-	struct jabber_transfer *tf = bt->tf;
-	struct jabber_data *jd = tf->ic->proto_data;
-	char *proxysetting = g_strdup(set_getstr(&tf->ic->acc->set, "proxy"));
-	char *proxy, *next, *errmsg = NULL;
-	char port[6];
-	char host[NI_MAXHOST + 1];
-	jabber_streamhost_t *sh, *sh2;
-	GSList *streamhosts = jd->streamhosts;
-
-	proxy = proxysetting;
-	while (proxy && (*proxy != '\0')) {
-		if ((next = strchr(proxy, ';'))) {
-			*next++ = '\0';
-		}
-
-		if (strcmp(proxy, "<local>") == 0) {
-			if ((tf->fd = ft_listen(&tf->saddr, host, port, jd->fd, FALSE, &errmsg)) != -1) {
-				sh = jabber_streamhost_new(tf->ini_jid, host, 0);
-				strncpy(sh->port, port, sizeof(sh->port));
-				bt->streamhosts = g_slist_append(bt->streamhosts, sh);
-
-				bt->tf->watch_in = b_input_add(tf->fd, B_EV_IO_READ, jabber_bs_send_handshake, bt);
-				bt->connect_timeout = b_timeout_add(JABBER_BS_LISTEN_TIMEOUT * 1000,
-				                                    jabber_bs_connect_timeout, bt);
-			} else {
-				imcb_log(tf->ic,
-				         "Transferring file %s: couldn't listen locally(non fatal, check your ft_listen setting in bitlbee.conf): %s",
-				         tf->ft->file_name,
-				         errmsg);
-			}
-		} else if (strcmp(proxy, "<auto>") == 0) {
-			while (streamhosts) {
-				sh = g_new0(jabber_streamhost_t, 1);
-				sh2 = streamhosts->data;
-				sh->jid = g_strdup(sh2->jid);
-				sh->host = g_strdup(sh2->host);
-				strcpy(sh->port, sh2->port);
-				bt->streamhosts = g_slist_append(bt->streamhosts, sh);
-				streamhosts = g_slist_next(streamhosts);
-			}
-		} else if ((sh = jabber_si_parse_proxy(tf->ic, proxy))) {
-			bt->streamhosts = g_slist_append(bt->streamhosts, sh);
-		}
-		proxy = next;
-	}
-
-	g_free(proxysetting);
-}
-
-/*
- * Starts a bytestream.
- */
-gboolean jabber_bs_send_start(struct jabber_transfer *tf)
-{
-	struct bs_transfer *bt;
-
-	bt = g_new0(struct bs_transfer, 1);
-	bt->tf = tf;
-	bt->phase = BS_PHASE_CONNECT;
-	bt->pseudoaddr = generate_pseudoaddr(tf->sid, tf->ini_jid, tf->tgt_jid);
-	tf->streamhandle = bt;
-	tf->ft->free = jabber_bs_free_transfer;
-	tf->ft->canceled = jabber_bs_canceled;
-
-	jabber_si_set_proxies(bt);
-
-	return jabber_bs_send_request(tf, bt->streamhosts);
-}
-
-gboolean jabber_bs_send_request(struct jabber_transfer *tf, GSList *streamhosts)
-{
-	struct xt_node *shnode, *query, *iq;
-
-	query = xt_new_node("query", NULL, NULL);
-	xt_add_attr(query, "xmlns", XMLNS_BYTESTREAMS);
-	xt_add_attr(query, "sid", tf->sid);
-	xt_add_attr(query, "mode", "tcp");
-
-	while (streamhosts) {
-		jabber_streamhost_t *sh = streamhosts->data;
-		shnode = xt_new_node("streamhost", NULL, NULL);
-		xt_add_attr(shnode, "jid", sh->jid);
-		xt_add_attr(shnode, "host", sh->host);
-		xt_add_attr(shnode, "port", sh->port);
-
-		xt_add_child(query, shnode);
-
-		streamhosts = g_slist_next(streamhosts);
-	}
-
-
-	iq = jabber_make_packet("iq", "set", tf->tgt_jid, query);
-	xt_add_attr(iq, "from", tf->ini_jid);
-
-	jabber_cache_add(tf->ic, iq, jabber_bs_send_handle_reply);
-
-	if (!jabber_write_packet(tf->ic, iq)) {
-		imcb_file_canceled(tf->ic, tf->ft, "Error transmitting bytestream request");
-	}
-	return TRUE;
-}
-
-gboolean jabber_bs_send_handshake_abort(struct bs_transfer *bt, char *error)
-{
-	struct jabber_transfer *tf = bt->tf;
-	struct jabber_data *jd = tf->ic->proto_data;
-
-	/* TODO: did the receiver get here somehow??? */
-	imcb_log(tf->ic, "Transferring file %s: SOCKS5 handshake failed: %s",
-	         tf->ft->file_name,
-	         error);
-
-	if (jd->streamhosts == NULL) { /* we're done here unless we have a proxy to try */
-		imcb_file_canceled(tf->ic, tf->ft, error);
-	}
-
-	/* MUST always return FALSE! */
-	return FALSE;
-}
-
-/*
- * SOCKS5BYTESTREAM protocol for the sender
- */
-gboolean jabber_bs_send_handshake(gpointer data, gint fd, b_input_condition cond)
-{
-	struct bs_transfer *bt = data;
-	struct jabber_transfer *tf = bt->tf;
-	short revents;
-
-	if (!jabber_bs_poll(bt, fd, &revents)) {
-		return FALSE;
-	}
-
-	switch (bt->phase) {
-	case BS_PHASE_CONNECT:
-	{
-		struct sockaddr_storage clt_addr;
-		socklen_t ssize = sizeof(clt_addr);
-
-		/* Connect */
-
-		ASSERTSOCKOP(tf->fd = accept(fd, (struct sockaddr *) &clt_addr, &ssize), "Accepting connection");
-
-		closesocket(fd);
-		fd = tf->fd;
-		sock_make_nonblocking(fd);
-
-		bt->phase = BS_PHASE_CONNECTED;
-
-		bt->tf->watch_in = b_input_add(fd, B_EV_IO_READ, jabber_bs_send_handshake, bt);
-		return FALSE;
-	}
-	case BS_PHASE_CONNECTED:
-	{
-		int ret, have_noauth = FALSE;
-		struct {
-			unsigned char ver;
-			unsigned char method;
-		} socks5_auth_reply = { .ver = 5, .method = 0 };
-		struct {
-			unsigned char ver;
-			unsigned char nmethods;
-			unsigned char method;
-		} socks5_hello = {0};
-
-		if (!(ret = jabber_bs_peek(bt, &socks5_hello, sizeof(socks5_hello)))) {
-			return FALSE;
-		}
-
-		if (ret < sizeof(socks5_hello)) {
-			return TRUE;
-		}
-
-		if (!(socks5_hello.ver == 5) ||
-		    !(socks5_hello.nmethods >= 1) ||
-		    !(socks5_hello.nmethods < 32)) {
-			return jabber_bs_abort(bt, "Invalid auth request ver=%d nmethods=%d method=%d",
-			                       socks5_hello.ver, socks5_hello.nmethods, socks5_hello.method);
-		}
-
-		have_noauth = socks5_hello.method == 0;
-
-		if (socks5_hello.nmethods > 1) {
-			char mbuf[32];
-			int i;
-			ASSERTSOCKOP(ret = recv(fd, mbuf, socks5_hello.nmethods - 1, 0), "Receiving auth methods");
-			if (ret < (socks5_hello.nmethods - 1)) {
-				return jabber_bs_abort(bt, "Partial auth request");
-			}
-			for (i = 0; !have_noauth && (i < socks5_hello.nmethods - 1); i++) {
-				if (mbuf[i] == 0) {
-					have_noauth = TRUE;
-				}
-			}
-		}
-
-		if (!have_noauth) {
-			return jabber_bs_abort(bt, "Auth request didn't include no authentication");
-		}
-
-		ASSERTSOCKOP(send(fd, &socks5_auth_reply, sizeof(socks5_auth_reply), 0), "Sending auth reply");
-
-		bt->phase = BS_PHASE_REQUEST;
-
-		return TRUE;
-	}
-	case BS_PHASE_REQUEST:
-	{
-		struct socks5_message socks5_connect = {0};
-		int msgsize = sizeof(struct socks5_message);
-		int ret;
-
-		if (!(ret = jabber_bs_peek(bt, &socks5_connect, msgsize))) {
-			return FALSE;
-		}
-
-		if (ret < msgsize) {
-			return TRUE;
-		}
-
-		if (!(socks5_connect.ver == 5) ||
-		    !(socks5_connect.cmdrep.cmd == 1) ||
-		    !(socks5_connect.atyp == 3) ||
-		    !(socks5_connect.addrlen == 40)) {
-			return jabber_bs_abort(bt,
-			                       "Invalid SOCKS5 Connect message (addrlen=%d, ver=%d, cmd=%d, atyp=%d)",
-			                       socks5_connect.addrlen, socks5_connect.ver, socks5_connect.cmdrep.cmd,
-			                       socks5_connect.atyp);
-		}
-		if (!(memcmp(socks5_connect.address, bt->pseudoaddr, 40) == 0)) {
-			return jabber_bs_abort(bt, "SOCKS5 Connect message contained wrong digest");
-		}
-
-		socks5_connect.cmdrep.rep = 0;
-
-		ASSERTSOCKOP(send(fd, &socks5_connect, msgsize, 0), "Sending connect reply");
-
-		bt->phase = BS_PHASE_REPLY;
-
-		imcb_log(tf->ic, "File %s: SOCKS5 handshake successful! Transfer about to start...", tf->ft->file_name);
-
-		if (tf->accepted) {
-			/* streamhost-used message came already in(possible?), let's start sending */
-			tf->ft->write_request(tf->ft);
-		}
-
-		tf->watch_in = 0;
-		return FALSE;
-
-	}
-	default:
-		/* BUG */
-		imcb_log(bt->tf->ic, "BUG in file transfer code: undefined handshake phase");
-
-		bt->tf->watch_in = 0;
-		return FALSE;
-	}
-}
-#undef ASSERTSOCKOP
Index: otocols/jabber/sasl.c
===================================================================
--- protocols/jabber/sasl.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,550 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Jabber module - SASL authentication                                      *
-*                                                                           *
-*  Copyright 2006-2012 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.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-\***************************************************************************/
-
-#include <ctype.h>
-
-#include "jabber.h"
-#include "base64.h"
-#include "oauth2.h"
-#include "oauth.h"
-
-const struct oauth2_service oauth2_service_google =
-{
-	"https://accounts.google.com/o/oauth2/auth",
-	"https://accounts.google.com/o/oauth2/token",
-	"urn:ietf:wg:oauth:2.0:oob",
-	"https://www.googleapis.com/auth/googletalk",
-	"783993391592.apps.googleusercontent.com",
-	"6C-Zgf7Tr7gEQTPlBhMUgo7R",
-};
-
-/* """"""""""""""""""""""""""""""oauth"""""""""""""""""""""""""""""" */
-#define HIPCHAT_SO_CALLED_OAUTH_URL "https://hipchat.com/account/api"
-
-xt_status sasl_pkt_mechanisms(struct xt_node *node, gpointer data)
-{
-	struct im_connection *ic = data;
-	struct jabber_data *jd = ic->proto_data;
-	struct xt_node *c, *reply;
-	char *s;
-	int sup_plain = 0, sup_digest = 0, sup_gtalk = 0, sup_anonymous = 0, sup_hipchat_oauth = 0;
-	int want_oauth = FALSE, want_hipchat = FALSE, want_anonymous = FALSE;
-	GString *mechs;
-
-	if (!sasl_supported(ic)) {
-		/* Should abort this now, since we should already be doing
-		   IQ authentication. Strange things happen when you try
-		   to do both... */
-		imcb_log(ic,
-		         "XMPP 1.0 non-compliant server seems to support SASL, please report this as a BitlBee bug!");
-		return XT_HANDLED;
-	}
-
-	s = xt_find_attr(node, "xmlns");
-	if (!s || strcmp(s, XMLNS_SASL) != 0) {
-		imcb_log(ic, "Stream error while authenticating");
-		imc_logout(ic, FALSE);
-		return XT_ABORT;
-	}
-
-	want_anonymous = set_getbool(&ic->acc->set, "anonymous");
-	want_oauth = set_getbool(&ic->acc->set, "oauth");
-	want_hipchat = (jd->flags & JFLAG_HIPCHAT);
-
-	mechs = g_string_new("");
-	c = node->children;
-	while ((c = xt_find_node(c, "mechanism"))) {
-		if (c->text && g_strcasecmp(c->text, "PLAIN") == 0) {
-			sup_plain = 1;
-		} else if (c->text && g_strcasecmp(c->text, "DIGEST-MD5") == 0) {
-			sup_digest = 1;
-		} else if (c->text && g_strcasecmp(c->text, "ANONYMOUS") == 0) {
-			sup_anonymous = 1;
-		} else if (c->text && g_strcasecmp(c->text, "X-OAUTH2") == 0) {
-			sup_gtalk = 1;
-		} else if (c->text && g_strcasecmp(c->text, "X-HIPCHAT-OAUTH2") == 0) {
-			sup_hipchat_oauth = 1;
-		}
-
-		if (c->text) {
-			g_string_append_printf(mechs, " %s", c->text);
-		}
-
-		c = c->next;
-	}
-
-	if (!want_oauth && !sup_plain && !sup_digest) {
-		if (sup_gtalk || sup_hipchat_oauth) {
-			imcb_error(ic, "This server requires OAuth "
-			           "(supported schemes:%s)", mechs->str);
-		} else {
-			imcb_error(ic, "BitlBee does not support any of the offered SASL "
-			           "authentication schemes:%s", mechs->str);
-		}
-		imc_logout(ic, FALSE);
-		g_string_free(mechs, TRUE);
-		return XT_ABORT;
-	}
-	g_string_free(mechs, TRUE);
-
-	reply = xt_new_node("auth", NULL, NULL);
-	if (!want_hipchat) {
-		xt_add_attr(reply, "xmlns", XMLNS_SASL);
-	} else {
-		xt_add_attr(reply, "xmlns", XMLNS_HIPCHAT);
-	}
-
-	if ((sup_gtalk || sup_hipchat_oauth) && want_oauth) {
-		GString *gs;
-
-		gs = g_string_sized_new(128);
-
-		g_string_append_c(gs, '\0');
-
-		if (sup_gtalk) {
-			/* X-OAUTH2 is not *the* standard OAuth2 SASL/XMPP implementation.
-			   It's currently used by GTalk and vaguely documented on
-			   http://code.google.com/apis/cloudprint/docs/rawxmpp.html */
-			xt_add_attr(reply, "mechanism", "X-OAUTH2");
-
-			g_string_append(gs, jd->username);
-			g_string_append_c(gs, '\0');
-			g_string_append(gs, jd->oauth2_access_token);
-		} else if (sup_hipchat_oauth) {
-			/* Hipchat's variant, not standard either, is documented here:
-			   https://docs.atlassian.com/hipchat.xmpp/latest/xmpp/auth.html */
-			xt_add_attr(reply, "mechanism", "oauth2");
-
-			g_string_append(gs, jd->oauth2_access_token);
-			g_string_append_c(gs, '\0');
-			g_string_append(gs, set_getstr(&ic->acc->set, "resource"));
-		}
-
-		reply->text = base64_encode((unsigned char *) gs->str, gs->len);
-		reply->text_len = strlen(reply->text);
-		g_string_free(gs, TRUE);
-
-	} else if (want_oauth) {
-		imcb_error(ic, "OAuth requested, but not supported by server");
-		imc_logout(ic, FALSE);
-		xt_free_node(reply);
-		return XT_ABORT;
-	} else if (want_anonymous && sup_anonymous) {
-		xt_add_attr(reply, "mechanism", "ANONYMOUS");
-
-		/* Well, that was easy. */
-	} else if (want_anonymous) {
-		imcb_error(ic, "Anonymous login requested, but not supported by server");
-		imc_logout(ic, FALSE);
-		xt_free_node(reply);
-		return XT_ABORT;
-	} else if (sup_digest && !(jd->ssl && sup_plain)) {
-		/* Only try DIGEST-MD5 if there's no SSL/TLS or if PLAIN isn't supported.
-		 * Which in practice means "don't bother with DIGEST-MD5 most of the time".
-		 * It's weak, pointless over TLS, and often breaks with some servers (hi openfire) */
-
-		xt_add_attr(reply, "mechanism", "DIGEST-MD5");
-
-		/* The rest will be done later, when we receive a <challenge/>. */
-	} else if (sup_plain) {
-		GString *gs;
-		char *username;
-
-		if (!want_hipchat) {
-			xt_add_attr(reply, "mechanism", "PLAIN");
-			username = jd->username;
-		} else {
-			username = jd->me;
-		}
-
-		/* set an arbitrary initial size to avoid reallocations */
-		gs = g_string_sized_new(128);
-
-		/* With SASL PLAIN in XMPP, the text should be b64(\0user\0pass) */
-		g_string_append_c(gs, '\0');
-		g_string_append(gs, username);
-		g_string_append_c(gs, '\0');
-		g_string_append(gs, ic->acc->pass);
-		if (want_hipchat) {
-			/* Hipchat's variation adds \0resource at the end */
-			g_string_append_c(gs, '\0');
-			g_string_append(gs, set_getstr(&ic->acc->set, "resource"));
-		}
-
-		reply->text = base64_encode((unsigned char *) gs->str, gs->len);
-		reply->text_len = strlen(reply->text);
-		g_string_free(gs, TRUE);
-	}
-
-	if (reply && !jabber_write_packet(ic, reply)) {
-		xt_free_node(reply);
-		return XT_ABORT;
-	}
-	xt_free_node(reply);
-
-	/* To prevent classic authentication from happening. */
-	jd->flags |= JFLAG_STREAM_STARTED;
-
-	return XT_HANDLED;
-}
-
-/* Non-static function, but not mentioned in jabber.h because it's for internal
-   use, just that the unittest should be able to reach it... */
-char *sasl_get_part(char *data, char *field)
-{
-	int i, len;
-
-	len = strlen(field);
-
-	while (g_ascii_isspace(*data) || *data == ',') {
-		data++;
-	}
-
-	if (g_strncasecmp(data, field, len) == 0 && data[len] == '=') {
-		i = strlen(field) + 1;
-	} else {
-		for (i = 0; data[i]; i++) {
-			/* If we have a ", skip until it's closed again. */
-			if (data[i] == '"') {
-				i++;
-				while (data[i] != '"' || data[i - 1] == '\\') {
-					i++;
-				}
-			}
-
-			/* If we got a comma, we got a new field. Check it,
-			   find the next key after it. */
-			if (data[i] == ',') {
-				while (g_ascii_isspace(data[i]) || data[i] == ',') {
-					i++;
-				}
-
-				if (g_strncasecmp(data + i, field, len) == 0 &&
-				    data[i + len] == '=') {
-					i += len + 1;
-					break;
-				}
-			}
-		}
-	}
-
-	if (data[i] == '"') {
-		int j;
-		char *ret;
-
-		i++;
-		len = 0;
-		while (data[i + len] != '"' || data[i + len - 1] == '\\') {
-			len++;
-		}
-
-		ret = g_strndup(data + i, len);
-		for (i = j = 0; ret[i]; i++) {
-			if (ret[i] == '\\') {
-				ret[j++] = ret[++i];
-			} else {
-				ret[j++] = ret[i];
-			}
-		}
-		ret[j] = 0;
-
-		return ret;
-	} else if (data[i]) {
-		len = 0;
-		while (data[i + len] && data[i + len] != ',') {
-			len++;
-		}
-
-		return g_strndup(data + i, len);
-	} else {
-		return NULL;
-	}
-}
-
-xt_status sasl_pkt_challenge(struct xt_node *node, gpointer data)
-{
-	struct im_connection *ic = data;
-	struct jabber_data *jd = ic->proto_data;
-	struct xt_node *reply_pkt = NULL;
-	char *nonce = NULL, *realm = NULL, *cnonce = NULL;
-	unsigned char cnonce_bin[30];
-	char *digest_uri = NULL;
-	char *dec = NULL;
-	char *s = NULL, *reply = NULL;
-	xt_status ret = XT_ABORT;
-
-	if (node->text_len == 0) {
-		goto error;
-	}
-
-	dec = frombase64(node->text);
-
-	if (!(s = sasl_get_part(dec, "rspauth"))) {
-		/* See RFC 2831 for for information. */
-		GChecksum *A1, *A2, *H;
-		gsize digest_len = MD5_HASH_SIZE;
-		guint8 A1r[16], A2r[16], Hr[16];
-		char A1h[33], A2h[33], Hh[33];
-		int i;
-
-		nonce = sasl_get_part(dec, "nonce");
-		realm = sasl_get_part(dec, "realm");
-
-		if (!nonce) {
-			goto error;
-		}
-
-		/* Jabber.Org considers the realm part optional and doesn't
-		   specify one. Oh well, actually they're right, but still,
-		   don't know if this is right... */
-		if (!realm) {
-			realm = g_strdup(jd->server);
-		}
-
-		random_bytes(cnonce_bin, sizeof(cnonce_bin));
-		cnonce = base64_encode(cnonce_bin, sizeof(cnonce_bin));
-		digest_uri = g_strdup_printf("%s/%s", "xmpp", jd->server);
-
-		/* Generate the MD5 hash of username:realm:password,
-		   I decided to call it H. */
-		H = g_checksum_new(G_CHECKSUM_MD5);
-		s = g_strdup_printf("%s:%s:%s", jd->username, realm, ic->acc->pass);
-		g_checksum_update(H, (guint8 *)s, strlen(s));
-		g_free(s);
-
-		g_checksum_get_digest(H, Hr, &digest_len);
-		g_checksum_free(H);
-
-		/* Now generate the hex. MD5 hash of H:nonce:cnonce, called A1. */
-		A1 = g_checksum_new(G_CHECKSUM_MD5);
-		s = g_strdup_printf(":%s:%s", nonce, cnonce);
-		g_checksum_update(A1, Hr, 16);
-		g_checksum_update(A1, (guint8 *)s, strlen(s));
-		g_free(s);
-		g_checksum_get_digest(A1, A1r, &digest_len);
-		g_checksum_free(A1);
-		for (i = 0; i < 16; i++) {
-			sprintf(A1h + i * 2, "%02x", A1r[i]);
-		}
-
-		/* A2... */
-		A2 = g_checksum_new(G_CHECKSUM_MD5);
-		s = g_strdup_printf("%s:%s", "AUTHENTICATE", digest_uri);
-		g_checksum_update(A2, (guint8 *)s, strlen(s));
-		g_free(s);
-		g_checksum_get_digest(A2, A2r, &digest_len);
-		g_checksum_free(A2);
-		for (i = 0; i < 16; i++) {
-			sprintf(A2h + i * 2, "%02x", A2r[i]);
-		}
-
-		/* Final result: A1:nonce:00000001:cnonce:auth:A2. Let's reuse H for it. */
-		H = g_checksum_new(G_CHECKSUM_MD5);
-		s = g_strdup_printf("%s:%s:%s:%s:%s:%s", A1h, nonce, "00000001", cnonce, "auth", A2h);
-		g_checksum_update(H, (guint8 *)s, strlen(s));
-		g_free(s);
-		g_checksum_get_digest(H, Hr, &digest_len);
-		g_checksum_free(H);
-
-		for (i = 0; i < 16; i++) {
-			sprintf(Hh + i * 2, "%02x", Hr[i]);
-		}
-
-		/* Now build the SASL response string: */
-		reply = g_strdup_printf("username=\"%s\",realm=\"%s\",nonce=\"%s\",cnonce=\"%s\","
-		                        "nc=%08x,qop=auth,digest-uri=\"%s\",response=%s,charset=%s",
-		                        jd->username, realm, nonce, cnonce, 1, digest_uri, Hh, "utf-8");
-	} else {
-		/* We found rspauth, but don't really care... */
-		g_free(s);
-	}
-
-	s = reply ? tobase64(reply) : NULL;
-	reply_pkt = xt_new_node("response", s, NULL);
-	xt_add_attr(reply_pkt, "xmlns", XMLNS_SASL);
-
-	if (!jabber_write_packet(ic, reply_pkt)) {
-		goto silent_error;
-	}
-
-	ret = XT_HANDLED;
-	goto silent_error;
-
-error:
-	imcb_error(ic, "Incorrect SASL challenge received");
-	imc_logout(ic, FALSE);
-
-silent_error:
-	g_free(digest_uri);
-	g_free(cnonce);
-	g_free(nonce);
-	g_free(reply);
-	g_free(realm);
-	g_free(dec);
-	g_free(s);
-	xt_free_node(reply_pkt);
-
-	return ret;
-}
-
-xt_status sasl_pkt_result(struct xt_node *node, gpointer data)
-{
-	struct im_connection *ic = data;
-	struct jabber_data *jd = ic->proto_data;
-	char *s;
-
-	s = xt_find_attr(node, "xmlns");
-	if (!s || strcmp(s, XMLNS_SASL) != 0) {
-		imcb_log(ic, "Stream error while authenticating");
-		imc_logout(ic, FALSE);
-		return XT_ABORT;
-	}
-
-	if (strcmp(node->name, "success") == 0) {
-		imcb_log(ic, "Authentication finished");
-		jd->flags |= JFLAG_AUTHENTICATED | JFLAG_STREAM_RESTART;
-
-		if (jd->flags & JFLAG_HIPCHAT) {
-			return hipchat_handle_success(ic, node);
-		}
-	} else if (strcmp(node->name, "failure") == 0) {
-		imcb_error(ic, "Authentication failure");
-		imc_logout(ic, FALSE);
-		return XT_ABORT;
-	}
-
-	return XT_HANDLED;
-}
-
-/* This one is needed to judge if we'll do authentication using IQ or SASL.
-   It's done by checking if the <stream:stream> from the server has a
-   version attribute. I don't know if this is the right way though... */
-gboolean sasl_supported(struct im_connection *ic)
-{
-	struct jabber_data *jd = ic->proto_data;
-
-	return (jd->xt && jd->xt->root && xt_find_attr(jd->xt->root, "version")) != 0;
-}
-
-void sasl_oauth2_init(struct im_connection *ic)
-{
-	struct jabber_data *jd = ic->proto_data;
-
-	imcb_log(ic, "Starting OAuth authentication");
-
-	/* Temporary contact, just used to receive the OAuth response. */
-	imcb_add_buddy(ic, JABBER_OAUTH_HANDLE, NULL);
-
-	if (jd->flags & JFLAG_HIPCHAT) {
-		imcb_buddy_msg(ic, JABBER_OAUTH_HANDLE,
-			"Open this URL and generate a token with 'View Group' and 'Send Message' scopes: "
-			HIPCHAT_SO_CALLED_OAUTH_URL, 0, 0);
-	} else {
-		char *msg, *url;
-
-		url = oauth2_url(jd->oauth2_service);
-		msg = g_strdup_printf("Open this URL in your browser to authenticate: %s", url);
-		imcb_buddy_msg(ic, JABBER_OAUTH_HANDLE, msg, 0, 0);
-
-		g_free(msg);
-		g_free(url);
-	}
-	imcb_buddy_msg(ic, JABBER_OAUTH_HANDLE, "Respond to this message with the returned "
-	               "authorization token.", 0, 0);
-
-}
-
-static gboolean sasl_oauth2_remove_contact(gpointer data, gint fd, b_input_condition cond)
-{
-	struct im_connection *ic = data;
-
-	if (g_slist_find(jabber_connections, ic)) {
-		imcb_remove_buddy(ic, JABBER_OAUTH_HANDLE, NULL);
-	}
-	return FALSE;
-}
-
-int sasl_oauth2_get_refresh_token(struct im_connection *ic, const char *msg)
-{
-	struct jabber_data *jd = ic->proto_data;
-	char *code;
-	int ret;
-
-	imcb_log(ic, "Requesting OAuth access token");
-
-	/* Don't do it here because the caller may get confused if the contact
-	   we're currently sending a message to is deleted. */
-	b_timeout_add(1, sasl_oauth2_remove_contact, ic);
-
-	code = g_strdup(msg);
-	g_strstrip(code);
-	ret = oauth2_access_token(jd->oauth2_service, OAUTH2_AUTH_CODE,
-	                          code, sasl_oauth2_got_token, ic);
-
-	g_free(code);
-	return ret;
-}
-
-int sasl_oauth2_refresh(struct im_connection *ic, const char *refresh_token)
-{
-	struct jabber_data *jd = ic->proto_data;
-
-	return oauth2_access_token(jd->oauth2_service, OAUTH2_AUTH_REFRESH,
-	                           refresh_token, sasl_oauth2_got_token, ic);
-}
-
-void sasl_oauth2_got_token(gpointer data, const char *access_token, const char *refresh_token, const char *error)
-{
-	struct im_connection *ic = data;
-	struct jabber_data *jd;
-	GSList *auth = NULL;
-
-	if (g_slist_find(jabber_connections, ic) == NULL) {
-		return;
-	}
-
-	jd = ic->proto_data;
-
-	if (access_token == NULL) {
-		imcb_error(ic, "OAuth failure (%s)", error);
-		imc_logout(ic, TRUE);
-		return;
-	}
-
-	oauth_params_parse(&auth, ic->acc->pass);
-	if (refresh_token) {
-		oauth_params_set(&auth, "refresh_token", refresh_token);
-	}
-	if (access_token) {
-		oauth_params_set(&auth, "access_token", access_token);
-	}
-
-	g_free(ic->acc->pass);
-	ic->acc->pass = oauth_params_string(auth);
-	oauth_params_free(&auth);
-
-	g_free(jd->oauth2_access_token);
-	jd->oauth2_access_token = g_strdup(access_token);
-
-	jabber_connect(ic);
-}
Index: otocols/jabber/si.c
===================================================================
--- protocols/jabber/si.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,553 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Jabber module - SI packets                                               *
-*                                                                           *
-*  Copyright 2007 Uli Meis <a.sporto+bee@gmail.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.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-\***************************************************************************/
-
-#include "jabber.h"
-
-void jabber_si_answer_request(file_transfer_t *ft);
-int jabber_si_send_request(struct im_connection *ic, char *who, struct jabber_transfer *tf);
-
-/* file_transfer free() callback */
-void jabber_si_free_transfer(file_transfer_t *ft)
-{
-	struct jabber_transfer *tf = ft->data;
-	struct jabber_data *jd = tf->ic->proto_data;
-
-	if (tf->watch_in) {
-		b_event_remove(tf->watch_in);
-		tf->watch_in = 0;
-	}
-
-	jd->filetransfers = g_slist_remove(jd->filetransfers, tf);
-
-	if (tf->fd != -1) {
-		closesocket(tf->fd);
-		tf->fd = -1;
-	}
-
-	if (tf->disco_timeout) {
-		b_event_remove(tf->disco_timeout);
-	}
-
-	g_free(tf->ini_jid);
-	g_free(tf->tgt_jid);
-	g_free(tf->iq_id);
-	g_free(tf->sid);
-	g_free(tf);
-}
-
-/* file_transfer canceled() callback */
-void jabber_si_canceled(file_transfer_t *ft, char *reason)
-{
-	struct jabber_transfer *tf = ft->data;
-	struct xt_node *reply, *iqnode;
-
-	if (tf->accepted) {
-		return;
-	}
-
-	iqnode = jabber_make_packet("iq", "error", tf->ini_jid, NULL);
-	xt_add_attr(iqnode, "id", tf->iq_id);
-	reply = jabber_make_error_packet(iqnode, "forbidden", "cancel", "403");
-	xt_free_node(iqnode);
-
-	if (!jabber_write_packet(tf->ic, reply)) {
-		imcb_log(tf->ic, "WARNING: Error generating reply to file transfer request");
-	}
-	xt_free_node(reply);
-
-}
-
-int jabber_si_check_features(struct jabber_transfer *tf, GSList *features)
-{
-	int foundft = FALSE, foundbt = FALSE, foundsi = FALSE;
-
-	while (features) {
-		if (!strcmp(features->data, XMLNS_FILETRANSFER)) {
-			foundft = TRUE;
-		}
-		if (!strcmp(features->data, XMLNS_BYTESTREAMS)) {
-			foundbt = TRUE;
-		}
-		if (!strcmp(features->data, XMLNS_SI)) {
-			foundsi = TRUE;
-		}
-
-		features = g_slist_next(features);
-	}
-
-	if (!foundft) {
-		imcb_file_canceled(tf->ic, tf->ft, "Buddy's client doesn't feature file transfers");
-	} else if (!foundbt) {
-		imcb_file_canceled(tf->ic, tf->ft, "Buddy's client doesn't feature byte streams (required)");
-	} else if (!foundsi) {
-		imcb_file_canceled(tf->ic, tf->ft, "Buddy's client doesn't feature stream initiation (required)");
-	}
-
-	return foundft && foundbt && foundsi;
-}
-
-void jabber_si_transfer_start(struct jabber_transfer *tf)
-{
-
-	if (!jabber_si_check_features(tf, tf->bud->features)) {
-		return;
-	}
-
-	/* send the request to our buddy */
-	jabber_si_send_request(tf->ic, tf->bud->full_jid, tf);
-
-	/* and start the receive logic */
-	imcb_file_recv_start(tf->ic, tf->ft);
-
-}
-
-gboolean jabber_si_waitfor_disco(gpointer data, gint fd, b_input_condition cond)
-{
-	struct jabber_transfer *tf = data;
-	struct jabber_data *jd = tf->ic->proto_data;
-
-	tf->disco_timeout_fired++;
-
-	if (tf->bud->features && jd->have_streamhosts == 1) {
-		tf->disco_timeout = 0;
-		jabber_si_transfer_start(tf);
-		return FALSE;
-	}
-
-	/* 8 seconds should be enough for server and buddy to respond */
-	if (tf->disco_timeout_fired < 16) {
-		return TRUE;
-	}
-
-	if (!tf->bud->features && jd->have_streamhosts != 1) {
-		imcb_log(tf->ic, "Couldn't get buddy's features nor discover all services of the server");
-	} else if (!tf->bud->features) {
-		imcb_log(tf->ic, "Couldn't get buddy's features");
-	} else {
-		imcb_log(tf->ic, "Couldn't discover some of the server's services");
-	}
-
-	tf->disco_timeout = 0;
-	jabber_si_transfer_start(tf);
-	return FALSE;
-}
-
-void jabber_si_transfer_request(struct im_connection *ic, file_transfer_t *ft, char *who)
-{
-	struct jabber_transfer *tf;
-	struct jabber_data *jd = ic->proto_data;
-	struct jabber_buddy *bud;
-	char *server = jd->server, *s;
-
-	if ((s = strchr(who, '=')) && jabber_chat_by_jid(ic, s + 1)) {
-		bud = jabber_buddy_by_ext_jid(ic, who, 0);
-	} else {
-		bud = jabber_buddy_by_jid(ic, who, 0);
-	}
-
-	if (bud == NULL) {
-		imcb_file_canceled(ic, ft, "Couldn't find buddy (BUG?)");
-		return;
-	}
-
-	imcb_log(ic, "Trying to send %s(%zd bytes) to %s", ft->file_name, ft->file_size, who);
-
-	tf = g_new0(struct jabber_transfer, 1);
-
-	tf->ic = ic;
-	tf->ft = ft;
-	tf->fd = -1;
-	tf->ft->data = tf;
-	tf->ft->free = jabber_si_free_transfer;
-	tf->bud = bud;
-	ft->write = jabber_bs_send_write;
-
-	jd->filetransfers = g_slist_prepend(jd->filetransfers, tf);
-
-	/* query buddy's features and server's streaming proxies if necessary */
-
-	if (!tf->bud->features) {
-		jabber_iq_query_features(ic, bud->full_jid);
-	}
-
-	/* If <auto> is not set don't check for proxies */
-	if ((jd->have_streamhosts != 1) && (jd->streamhosts == NULL) &&
-	    (strstr(set_getstr(&ic->acc->set, "proxy"), "<auto>") != NULL)) {
-		jd->have_streamhosts = 0;
-		jabber_iq_query_server(ic, server, XMLNS_DISCO_ITEMS);
-	} else if (jd->streamhosts != NULL) {
-		jd->have_streamhosts = 1;
-	}
-
-	/* if we had to do a query, wait for the result.
-	 * Otherwise fire away. */
-	if (!tf->bud->features || jd->have_streamhosts != 1) {
-		tf->disco_timeout = b_timeout_add(500, jabber_si_waitfor_disco, tf);
-	} else {
-		jabber_si_transfer_start(tf);
-	}
-}
-
-/*
- * First function that gets called when a file transfer request comes in.
- * A lot to parse.
- *
- * We choose a stream type from the options given by the initiator.
- * Then we wait for imcb to call the accept or cancel callbacks.
- */
-int jabber_si_handle_request(struct im_connection *ic, struct xt_node *node, struct xt_node *sinode)
-{
-	struct xt_node *c, *d, *reply;
-	char *sid, *ini_jid, *tgt_jid, *iq_id, *s, *ext_jid, *size_s;
-	struct jabber_buddy *bud;
-	int requestok = FALSE;
-	char *name, *cmp;
-	size_t size;
-	struct jabber_transfer *tf;
-	struct jabber_data *jd = ic->proto_data;
-	file_transfer_t *ft;
-
-	/* All this means we expect something like this: ( I think )
-	 * <iq from=... to=... id=...>
-	 *      <si id=id xmlns=si profile=ft>
-	 *              <file xmlns=ft/>
-	 *              <feature xmlns=feature>
-	 *                      <x xmlns=xdata type=submit>
-	 *                              <field var=stream-method>
-	 *
-	 */
-	if (!(ini_jid          = xt_find_attr(node, "from")) ||
-	    !(tgt_jid          = xt_find_attr(node, "to")) ||
-	    !(iq_id            = xt_find_attr(node, "id")) ||
-	    !(sid              = xt_find_attr(sinode, "id")) ||
-	    !(cmp              = xt_find_attr(sinode, "profile")) ||
-	    !(0               == strcmp(cmp, XMLNS_FILETRANSFER)) ||
-	    !(d                = xt_find_node(sinode->children, "file")) ||
-	    !(cmp = xt_find_attr(d, "xmlns")) ||
-	    !(0               == strcmp(cmp, XMLNS_FILETRANSFER)) ||
-	    !(name             = xt_find_attr(d, "name")) ||
-	    !(size_s           = xt_find_attr(d, "size")) ||
-	    !(1               == sscanf(size_s, "%zd", &size)) ||
-	    !(d                = xt_find_node(sinode->children, "feature")) ||
-	    !(cmp              = xt_find_attr(d, "xmlns")) ||
-	    !(0               == strcmp(cmp, XMLNS_FEATURE)) ||
-	    !(d                = xt_find_node(d->children, "x")) ||
-	    !(cmp              = xt_find_attr(d, "xmlns")) ||
-	    !(0               == strcmp(cmp, XMLNS_XDATA)) ||
-	    !(cmp              = xt_find_attr(d, "type")) ||
-	    !(0               == strcmp(cmp, "form")) ||
-	    !(d                = xt_find_node(d->children, "field")) ||
-	    !(cmp              = xt_find_attr(d, "var")) ||
-	    !(0               == strcmp(cmp, "stream-method"))) {
-		imcb_log(ic, "WARNING: Received incomplete Stream Initiation request");
-	} else {
-		/* Check if we support one of the options */
-
-		c = d->children;
-		while ((c = xt_find_node(c, "option"))) {
-			if ((d = xt_find_node(c->children, "value")) &&
-			    (d->text != NULL) &&
-			    (strcmp(d->text, XMLNS_BYTESTREAMS) == 0)) {
-				requestok = TRUE;
-				break;
-			} else {
-				c = c->next;
-			}
-		}
-
-		if (!requestok) {
-			imcb_log(ic, "WARNING: Unsupported file transfer request from %s", ini_jid);
-		}
-	}
-
-	if (requestok) {
-		/* Figure out who the transfer should come from... */
-
-		ext_jid = ini_jid;
-		if ((s = strchr(ini_jid, '/'))) {
-			if ((bud = jabber_buddy_by_jid(ic, ini_jid, GET_BUDDY_EXACT))) {
-				bud->last_msg = time(NULL);
-				ext_jid = bud->ext_jid ? : bud->bare_jid;
-			} else {
-				*s = 0; /* We need to generate a bare JID now. */
-			}
-		}
-
-		if (!(ft = imcb_file_send_start(ic, ext_jid, name, size))) {
-			imcb_log(ic, "WARNING: Error handling transfer request from %s", ini_jid);
-			requestok = FALSE;
-		}
-
-		if (s) {
-			*s = '/';
-		}
-	}
-
-	if (!requestok) {
-		reply = jabber_make_error_packet(node, "item-not-found", "cancel", NULL);
-		if (!jabber_write_packet(ic, reply)) {
-			imcb_log(ic, "WARNING: Error generating reply to file transfer request");
-		}
-		xt_free_node(reply);
-		return XT_HANDLED;
-	}
-
-	/* Request is fine. */
-
-	tf = g_new0(struct jabber_transfer, 1);
-
-	tf->ini_jid = g_strdup(ini_jid);
-	tf->tgt_jid = g_strdup(tgt_jid);
-	tf->iq_id = g_strdup(iq_id);
-	tf->sid = g_strdup(sid);
-	tf->ic = ic;
-	tf->ft = ft;
-	tf->fd = -1;
-	tf->ft->data = tf;
-	tf->ft->accept = jabber_si_answer_request;
-	tf->ft->free = jabber_si_free_transfer;
-	tf->ft->canceled = jabber_si_canceled;
-
-	jd->filetransfers = g_slist_prepend(jd->filetransfers, tf);
-
-	return XT_HANDLED;
-}
-
-/*
- * imc called the accept callback which probably means that the user accepted this file transfer.
- * We send our response to the initiator.
- * In the next step, the initiator will send us a request for the given stream type.
- * (currently that can only be a SOCKS5 bytestream)
- */
-void jabber_si_answer_request(file_transfer_t *ft)
-{
-	struct jabber_transfer *tf = ft->data;
-	struct xt_node *node, *sinode, *reply;
-
-	/* generate response, start with the SI tag */
-	sinode = xt_new_node("si", NULL, NULL);
-	xt_add_attr(sinode, "xmlns", XMLNS_SI);
-	xt_add_attr(sinode, "profile", XMLNS_FILETRANSFER);
-	xt_add_attr(sinode, "id", tf->sid);
-
-	/* now the file tag */
-	node = xt_new_node("file", NULL, NULL);
-	xt_add_attr(node, "xmlns", XMLNS_FILETRANSFER);
-
-	xt_add_child(sinode, node);
-
-	/* and finally the feature tag */
-	node = xt_new_node("field", NULL, NULL);
-	xt_add_attr(node, "var", "stream-method");
-	xt_add_attr(node, "type", "list-single");
-
-	/* Currently all we can do. One could also implement in-band (IBB) */
-	xt_add_child(node, xt_new_node("value", XMLNS_BYTESTREAMS, NULL));
-
-	node = xt_new_node("x", NULL, node);
-	xt_add_attr(node, "xmlns", XMLNS_XDATA);
-	xt_add_attr(node, "type", "submit");
-
-	node = xt_new_node("feature", NULL, node);
-	xt_add_attr(node, "xmlns", XMLNS_FEATURE);
-
-	xt_add_child(sinode, node);
-
-	reply = jabber_make_packet("iq", "result", tf->ini_jid, sinode);
-	xt_add_attr(reply, "id", tf->iq_id);
-
-	if (!jabber_write_packet(tf->ic, reply)) {
-		imcb_log(tf->ic, "WARNING: Error generating reply to file transfer request");
-	} else {
-		tf->accepted = TRUE;
-	}
-	xt_free_node(reply);
-}
-
-static xt_status jabber_si_handle_response(struct im_connection *ic, struct xt_node *node, struct xt_node *orig)
-{
-	struct xt_node *c, *d;
-	char *ini_jid = NULL, *tgt_jid, *iq_id, *cmp;
-	GSList *tflist;
-	struct jabber_transfer *tf = NULL;
-	struct jabber_data *jd = ic->proto_data;
-	struct jabber_error *err;
-
-	if (!(tgt_jid = xt_find_attr(node, "from")) ||
-	    !(ini_jid = xt_find_attr(node, "to")) ||
-	    !(iq_id   = xt_find_attr(node, "id"))) {
-		imcb_log(ic, "Invalid SI response from=%s to=%s", tgt_jid, ini_jid);
-		return XT_HANDLED;
-	}
-
-	/* Let's see if we can find out what this bytestream should be for... */
-
-	for (tflist = jd->filetransfers; tflist; tflist = g_slist_next(tflist)) {
-		struct jabber_transfer *tft = tflist->data;
-		if ((strcmp(tft->iq_id, iq_id) == 0)) {
-			tf = tft;
-			break;
-		}
-	}
-
-	if (!tf) {
-		imcb_log(ic, "WARNING: Received bytestream request from %s that doesn't match an SI request", ini_jid);
-		return XT_HANDLED;
-	}
-
-	err = jabber_error_parse(xt_find_node(node->children, "error"), XMLNS_STANZA_ERROR);
-
-	if (err) {
-		if (g_strcmp0(err->code, "forbidden") == 0) {
-			imcb_log(ic, "File %s: %s rejected the transfer", tf->ft->file_name, tgt_jid);
-		} else {
-			imcb_log(ic, "Error: Stream initiation request failed: %s (%s)", err->code, err->text);
-		}
-		imcb_file_canceled(ic, tf->ft, "Stream initiation request failed");
-		jabber_error_free(err);
-		return XT_HANDLED;
-	}
-
-	/* All this means we expect something like this: ( I think )
-	 * <iq from=... to=... id=...>
-	 *      <si xmlns=si>
-	 *      [	<file xmlns=ft/>    ] <-- not necessary
-	 *              <feature xmlns=feature>
-	 *                      <x xmlns=xdata type=submit>
-	 *                              <field var=stream-method>
-	 *                                      <value>
-	 */
-	if (!(c = xt_find_node(node->children, "si")) ||
-	    !(cmp = xt_find_attr(c, "xmlns")) ||
-	    !(strcmp(cmp, XMLNS_SI) == 0) ||
-	    !(d = xt_find_node(c->children, "feature")) ||
-	    !(cmp = xt_find_attr(d, "xmlns")) ||
-	    !(strcmp(cmp, XMLNS_FEATURE) == 0) ||
-	    !(d = xt_find_node(d->children, "x")) ||
-	    !(cmp = xt_find_attr(d, "xmlns")) ||
-	    !(strcmp(cmp, XMLNS_XDATA) == 0) ||
-	    !(cmp = xt_find_attr(d, "type")) ||
-	    !(strcmp(cmp, "submit") == 0) ||
-	    !(d = xt_find_node(d->children, "field")) ||
-	    !(cmp = xt_find_attr(d, "var")) ||
-	    !(strcmp(cmp, "stream-method") == 0) ||
-	    !(d = xt_find_node(d->children, "value"))) {
-		imcb_log(ic, "WARNING: Received incomplete Stream Initiation response");
-		return XT_HANDLED;
-	}
-
-	if (!(strcmp(d->text, XMLNS_BYTESTREAMS) == 0)) {
-		/* since we should only have advertised what we can do and the peer should
-		 * only have chosen what we offered, this should never happen */
-		imcb_log(ic, "WARNING: Received invalid Stream Initiation response, method %s", d->text);
-
-		return XT_HANDLED;
-	}
-
-	tf->ini_jid = g_strdup(ini_jid);
-	tf->tgt_jid = g_strdup(tgt_jid);
-
-	imcb_log(ic, "File %s: %s accepted the transfer!", tf->ft->file_name, tgt_jid);
-
-	jabber_bs_send_start(tf);
-
-	return XT_HANDLED;
-}
-
-int jabber_si_send_request(struct im_connection *ic, char *who, struct jabber_transfer *tf)
-{
-	struct xt_node *node, *sinode;
-	struct jabber_buddy *bud;
-
-	/* who knows how many bits the future holds :) */
-	char filesizestr[ 1 + ( int ) (0.301029995663981198f * sizeof(size_t) * 8) ];
-
-	const char *methods[] =
-	{
-		XMLNS_BYTESTREAMS,
-		//XMLNS_IBB,
-		NULL
-	};
-	const char **m;
-	char *s;
-
-	/* Maybe we should hash this? */
-	tf->sid = g_strdup_printf("BitlBeeJabberSID%d", tf->ft->local_id);
-
-	if ((s = strchr(who, '=')) && jabber_chat_by_jid(ic, s + 1)) {
-		bud = jabber_buddy_by_ext_jid(ic, who, 0);
-	} else {
-		bud = jabber_buddy_by_jid(ic, who, 0);
-	}
-
-	/* start with the SI tag */
-	sinode = xt_new_node("si", NULL, NULL);
-	xt_add_attr(sinode, "xmlns", XMLNS_SI);
-	xt_add_attr(sinode, "profile", XMLNS_FILETRANSFER);
-	xt_add_attr(sinode, "id", tf->sid);
-
-/*	if( mimetype )
-                xt_add_attr( node, "mime-type", mimetype ); */
-
-	/* now the file tag */
-/*	if( desc )
-                node = xt_new_node( "desc", descr, NULL ); */
-	node = xt_new_node("range", NULL, NULL);
-
-	sprintf(filesizestr, "%zd", tf->ft->file_size);
-	node = xt_new_node("file", NULL, node);
-	xt_add_attr(node, "xmlns", XMLNS_FILETRANSFER);
-	xt_add_attr(node, "name", tf->ft->file_name);
-	xt_add_attr(node, "size", filesizestr);
-/*	if (hash)
-                xt_add_attr( node, "hash", hash );
-        if (date)
-                xt_add_attr( node, "date", date ); */
-
-	xt_add_child(sinode, node);
-
-	/* and finally the feature tag */
-	node = xt_new_node("field", NULL, NULL);
-	xt_add_attr(node, "var", "stream-method");
-	xt_add_attr(node, "type", "list-single");
-
-	for (m = methods; *m; m++) {
-		xt_add_child(node, xt_new_node("option", NULL, xt_new_node("value", (char *) *m, NULL)));
-	}
-
-	node = xt_new_node("x", NULL, node);
-	xt_add_attr(node, "xmlns", XMLNS_XDATA);
-	xt_add_attr(node, "type", "form");
-
-	node = xt_new_node("feature", NULL, node);
-	xt_add_attr(node, "xmlns", XMLNS_FEATURE);
-
-	xt_add_child(sinode, node);
-
-	/* and we are there... */
-	node = jabber_make_packet("iq", "set", bud ? bud->full_jid : who, sinode);
-	jabber_cache_add(ic, node, jabber_si_handle_response);
-	tf->iq_id = g_strdup(xt_find_attr(node, "id"));
-
-	return jabber_write_packet(ic, node);
-}
Index: protocols/jabber/str.c
===================================================================
--- protocols/jabber/str.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/str.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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],"&amp;",5);
+            j += 5;
+            break;
+        case '\'':
+            memcpy(&temp[j],"&apos;",6);
+            j += 6;
+            break;
+        case '\"':
+            memcpy(&temp[j],"&quot;",6);
+            j += 6;
+            break;
+        case '<':
+            memcpy(&temp[j],"&lt;",4);
+            j += 4;
+            break;
+        case '>':
+            memcpy(&temp[j],"&gt;",4);
+            j += 4;
+            break;
+        default:
+            temp[j++] = buf[i];
+        }
+    }
+    temp[j] = '\0';
+    return temp;
+}
Index: protocols/jabber/utf8tab.h
===================================================================
--- protocols/jabber/utf8tab.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/utf8tab.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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,
Index: protocols/jabber/xmldef.h
===================================================================
--- protocols/jabber/xmldef.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/xmldef.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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>
+
Index: protocols/jabber/xmlnode.c
===================================================================
--- protocols/jabber/xmlnode.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/xmlnode.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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);
+}
Index: protocols/jabber/xmlparse.c
===================================================================
--- protocols/jabber/xmlparse.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/xmlparse.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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;
+}
Index: protocols/jabber/xmlparse.h
===================================================================
--- protocols/jabber/xmlparse.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/xmlparse.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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 */
Index: protocols/jabber/xmlrole.c
===================================================================
--- protocols/jabber/xmlrole.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/xmlrole.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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;
+}
Index: protocols/jabber/xmlrole.h
===================================================================
--- protocols/jabber/xmlrole.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/xmlrole.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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 */
Index: protocols/jabber/xmltok.c
===================================================================
--- protocols/jabber/xmltok.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/xmltok.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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 */
Index: protocols/jabber/xmltok.h
===================================================================
--- protocols/jabber/xmltok.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/xmltok.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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 */
Index: protocols/jabber/xmltok_impl.c
===================================================================
--- protocols/jabber/xmltok_impl.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/xmltok_impl.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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
Index: protocols/jabber/xmltok_impl.h
===================================================================
--- protocols/jabber/xmltok_impl.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/xmltok_impl.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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>
Index: protocols/jabber/xmltok_ns.c
===================================================================
--- protocols/jabber/xmltok_ns.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/jabber/xmltok_ns.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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);
+}
Index: protocols/md5.c
===================================================================
--- protocols/md5.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/md5.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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));
+}
Index: protocols/md5.h
===================================================================
--- protocols/md5.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/md5.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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 */
Index: protocols/msn/Makefile
===================================================================
--- protocols/msn/Makefile	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/msn/Makefile	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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
+	
+
Index: protocols/msn/msn.c
===================================================================
--- protocols/msn/msn.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/msn/msn.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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;
+}
Index: protocols/msn/msn.h
===================================================================
--- protocols/msn/msn.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/msn/msn.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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 );
Index: protocols/msn/msn_util.c
===================================================================
--- protocols/msn/msn_util.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/msn/msn_util.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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 );
+}
Index: protocols/msn/ns.c
===================================================================
--- protocols/msn/ns.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/msn/ns.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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 ) );
+	}
+}
Index: protocols/msn/passport.c
===================================================================
--- protocols/msn/passport.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/msn/passport.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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 );
+}
Index: protocols/msn/passport.h
===================================================================
--- protocols/msn/passport.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/msn/passport.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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__ */
Index: protocols/msn/sb.c
===================================================================
--- protocols/msn/sb.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/msn/sb.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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 );
+}
Index: protocols/msn/tables.c
===================================================================
--- protocols/msn/tables.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/msn/tables.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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 );
+}
Index: protocols/nogaim.c
===================================================================
--- protocols/nogaim.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ protocols/nogaim.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -1,6 +1,6 @@
-/********************************************************************\
+  /********************************************************************\
   * BitlBee -- An IRC to other IM-networks gateway                     *
   *                                                                    *
-  * Copyright 2002-2012 Wilmer van der Gaast and others                *
+  * Copyright 2002-2004 Wilmer van der Gaast and others                *
   \********************************************************************/
 
@@ -13,4 +13,6 @@
  * 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>
  */
 
@@ -28,762 +30,17 @@
   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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
+  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 "nogaim.h"
-
-GSList *connections;
-
-#ifdef WITH_PLUGINS
-GList *plugins = NULL;
-
-static gint pluginscmp(gconstpointer a, gconstpointer b, gpointer data)
-{
-	const struct plugin_info *ia = a;
-	const struct plugin_info *ib = b;
-
-	return g_strcasecmp(ia->name, ib->name);
-}
-
-/* semi-private */
-gboolean plugin_info_validate(struct plugin_info *info, const char *path)
-{
-	GList *l;
-	gboolean loaded = FALSE;
-
-	if (!path) {
-		path = "(null)";
-	}
-
-	if (info->abiver != BITLBEE_ABI_VERSION_CODE) {
-		log_message(LOGLVL_ERROR,
-			    "`%s' uses ABI %u but %u is required\n",
-			    path, info->abiver,
-			    BITLBEE_ABI_VERSION_CODE);
-		return FALSE;
-	}
-
-	if (!info->name || !info->version) {
-		log_message(LOGLVL_ERROR,
-			    "Name or version missing from the "
-			    "plugin info in `%s'\n", path);
-		return FALSE;
-	}
-
-	for (l = plugins; l; l = l->next) {
-		struct plugin_info *i = l->data;
-
-		if (g_strcasecmp(i->name, info->name) == 0) {
-			loaded = TRUE;
-			break;
-		}
-	}
-
-	if (loaded) {
-		log_message(LOGLVL_WARNING,
-			    "%s plugin already loaded\n",
-			    info->name);
-		return FALSE;
-	}
-
-	return TRUE;
-}
-
-/* semi-private */
-gboolean plugin_info_add(struct plugin_info *info)
-{
-	plugins = g_list_insert_sorted_with_data(plugins, info, pluginscmp, NULL);
-	return TRUE;
-}
-
-gboolean load_plugin(char *path)
-{
-	struct plugin_info *info = NULL;
-	struct plugin_info * (*info_function) (void) = NULL;
-	void (*init_function) (void);
-
-	GModule *mod = g_module_open(path, G_MODULE_BIND_LAZY);
-
-	if (!mod) {
-		log_message(LOGLVL_ERROR, "Error loading plugin `%s': %s\n", path, g_module_error());
-		return FALSE;
-	}
-
-	if (g_module_symbol(mod, "init_plugin_info", (gpointer *) &info_function)) {
-		info = info_function();
-
-		if (!plugin_info_validate(info, path)) {
-			g_module_close(mod);
-			return FALSE;
-		}
-	} else {
-		log_message(LOGLVL_WARNING, "Can't find function `init_plugin_info' in `%s'\n", path);
-	}
-
-	if (!g_module_symbol(mod, "init_plugin", (gpointer *) &init_function)) {
-		log_message(LOGLVL_WARNING, "Can't find function `init_plugin' in `%s'\n", path);
-		g_module_close(mod);
-		return FALSE;
-	}
-
-	if (info_function) {
-		plugin_info_add(info);
-	}
-
-	init_function();
-	return TRUE;
-}
-
-void load_plugins(void)
-{
-	GDir *dir;
-	GError *error = NULL;
-
-	dir = g_dir_open(global.conf->plugindir, 0, &error);
-
-	if (dir) {
-		const gchar *entry;
-		char *path;
-
-		while ((entry = g_dir_read_name(dir))) {
-			if (!g_str_has_suffix(entry, "." G_MODULE_SUFFIX)) {
-				continue;
-			}
-
-			path = g_build_filename(global.conf->plugindir, entry, NULL);
-			if (!path) {
-				log_message(LOGLVL_WARNING, "Can't build path for %s\n", entry);
-				continue;
-			}
-
-			load_plugin(path);
-
-			g_free(path);
-		}
-
-		g_dir_close(dir);
-	}
-}
-
-GList *get_plugins()
-{
-	return plugins;
-}
-#endif
-
-GList *protocols = NULL;
-GList *disabled_protocols = NULL;
-
-void register_protocol(struct prpl *p)
-{
-	int i;
-	gboolean refused = global.conf->protocols != NULL;
-
-	for (i = 0; global.conf->protocols && global.conf->protocols[i]; i++) {
-		if (g_strcasecmp(p->name, global.conf->protocols[i]) == 0) {
-			refused = FALSE;
-		}
-	}
-
-	if (refused) {
-		disabled_protocols = g_list_append(disabled_protocols, p);
-	} else {
-		protocols = g_list_append(protocols, p);
-	}
-}
-
-static int proto_name_cmp(const void *proto_, const void *name)
-{
-	const struct prpl *proto = proto_;
-	return g_strcasecmp(proto->name, name);
-}
-
-struct prpl *find_protocol(const char *name)
-{
-	GList *gl = g_list_find_custom(protocols, name, proto_name_cmp);
-	return gl ? gl->data: NULL;
-}
-
-gboolean is_protocol_disabled(const char *name)
-{
-	return g_list_find_custom(disabled_protocols, name, proto_name_cmp) != NULL;
-}
-
-/* Returns heap allocated string with text attempting to explain why a protocol is missing 
- * Free the return value with g_free() */
-char *explain_unknown_protocol(const char *name)
-{
-	char *extramsg = NULL;
-
-	if (is_protocol_disabled(name)) {
-		return g_strdup("Protocol disabled in the global config (bitlbee.conf)");
-	}
-
-	if (strcmp(name, "yahoo") == 0) {
-		return g_strdup("The old yahoo protocol is gone, try the funyahoo++ libpurple plugin instead.");
-	}
-
-#ifdef WITH_PURPLE
-	if ((strcmp(name, "msn") == 0) ||
-	    (strcmp(name, "loubserp-mxit") == 0) ||
-	    (strcmp(name, "myspace") == 0)) {
-		return g_strdup("This protocol has been removed from your libpurple version.");
-	}
-
-	if (strcmp(name, "hipchat") == 0) {
-		return g_strdup("This account type isn't supported by libpurple's jabber.");
-	}
-
-#else
-	extramsg = "If this is a libpurple plugin, you might need to install bitlbee-libpurple instead.";
-#endif
-	return g_strconcat("The protocol plugin is not installed or could not be loaded. "
-	                   "Use the `plugins' command to list available protocols. ",
-	                   extramsg, NULL);
-}
-
-void nogaim_init()
-{
-	extern void jabber_initmodule();
-	extern void twitter_initmodule();
-	extern void purple_initmodule();
-
-#ifdef WITH_JABBER
-	jabber_initmodule();
-#endif
-
-#ifdef WITH_TWITTER
-	twitter_initmodule();
-#endif
-
-#ifdef WITH_PURPLE
-	purple_initmodule();
-#endif
-
-#ifdef WITH_PLUGINS
-	load_plugins();
-#endif
-}
-
-GList *get_protocols()
-{
-	return protocols;
-}
-
-GList *get_protocols_disabled()
-{
-	return disabled_protocols;
-}
-
-GSList *get_connections()
-{
-	return connections;
-}
-
-struct im_connection *imcb_new(account_t *acc)
-{
-	struct im_connection *ic;
-	GHashFunc fn_hash = NULL;
-	GEqualFunc fn_equal = NULL;
-
-	ic = g_new0(struct im_connection, 1);
-
-	ic->bee = acc->bee;
-	ic->acc = acc;
-	acc->ic = ic;
-
-	/* figure out if we have hashing functions compatible with handle_cmp */
-	if (acc->prpl->handle_cmp == g_ascii_strcasecmp) {
-		fn_hash = b_istr_hash;
-		fn_equal = b_istr_equal;
-	} else if (acc->prpl->handle_cmp == g_strcmp0 || acc->prpl->handle_cmp == strcmp) {
-		fn_hash = g_str_hash;
-		fn_equal = g_str_equal;
-	}
-
-	/* only create the hash table if we found them */
-	if (fn_hash && fn_equal) {
-		ic->bee_users = g_hash_table_new_full(fn_hash, fn_equal, NULL, NULL);
-	}
-
-	connections = g_slist_append(connections, ic);
-
-	return(ic);
-}
-
-void imc_free(struct im_connection *ic)
-{
-	account_t *a;
-
-	/* Destroy the pointer to this connection from the account list */
-	for (a = ic->bee->accounts; a; a = a->next) {
-		if (a->ic == ic) {
-			a->ic = NULL;
-			break;
-		}
-	}
-
-	if (ic->bee_users) {
-		g_hash_table_destroy(ic->bee_users);
-	}
-
-	connections = g_slist_remove(connections, ic);
-	g_free(ic);
-}
-
-static void serv_got_crap(struct im_connection *ic, char *format, ...)
-{
-	va_list params;
-	char *text;
-	account_t *a;
-
-	if (!ic->bee->ui->log) {
-		return;
-	}
-
-	va_start(params, format);
-	text = g_strdup_vprintf(format, params);
-	va_end(params);
-
-	if ((g_strcasecmp(set_getstr(&ic->bee->set, "strip_html"), "always") == 0) ||
-	    ((ic->flags & OPT_DOES_HTML) && set_getbool(&ic->bee->set, "strip_html"))) {
-		strip_html(text);
-	}
-
-	/* Try to find a different connection on the same protocol. */
-	for (a = ic->bee->accounts; a; a = a->next) {
-		if (a->prpl == ic->acc->prpl && a->ic != ic) {
-			break;
-		}
-	}
-
-	/* If we found one, include the screenname in the message. */
-	if (a) {
-		ic->bee->ui->log(ic->bee, ic->acc->tag, text);
-	} else {
-		ic->bee->ui->log(ic->bee, ic->acc->prpl->name, text);
-	}
-
-	g_free(text);
-}
-
-void imcb_log(struct im_connection *ic, char *format, ...)
-{
-	va_list params;
-	char *text;
-
-	va_start(params, format);
-	text = g_strdup_vprintf(format, params);
-	va_end(params);
-
-	if (ic->flags & OPT_LOGGED_IN) {
-		serv_got_crap(ic, "%s", text);
-	} else {
-		serv_got_crap(ic, "Logging in: %s", text);
-	}
-
-	g_free(text);
-}
-
-void imcb_error(struct im_connection *ic, char *format, ...)
-{
-	va_list params;
-	char *text;
-
-	va_start(params, format);
-	text = g_strdup_vprintf(format, params);
-	va_end(params);
-
-	if (ic->flags & OPT_LOGGED_IN) {
-		serv_got_crap(ic, "Error: %s", text);
-	} else {
-		serv_got_crap(ic, "Login error: %s", text);
-	}
-
-	g_free(text);
-}
-
-static gboolean send_keepalive(gpointer d, gint fd, b_input_condition cond)
-{
-	struct im_connection *ic = d;
-
-	if ((ic->flags & OPT_PONGS) && !(ic->flags & OPT_PONGED)) {
-		/* This protocol is expected to ack keepalives and hasn't
-		   since the last time we were here. */
-		imcb_error(ic, "Connection timeout");
-		imc_logout(ic, TRUE);
-		return FALSE;
-	}
-	ic->flags &= ~OPT_PONGED;
-
-	if (ic->acc->prpl->keepalive) {
-		ic->acc->prpl->keepalive(ic);
-	}
-
-	return TRUE;
-}
-
-void start_keepalives(struct im_connection *ic, int interval)
-{
-	b_event_remove(ic->keepalive);
-	ic->keepalive = b_timeout_add(interval, send_keepalive, ic);
-
-	/* Connecting successfully counts as a first successful pong. */
-	if (ic->flags & OPT_PONGS) {
-		ic->flags |= OPT_PONGED;
-	}
-}
-
-void imcb_connected(struct im_connection *ic)
-{
-	/* MSN servers sometimes redirect you to a different server and do
-	   the whole login sequence again, so these "late" calls to this
-	   function should be handled correctly. (IOW, ignored) */
-	if (ic->flags & OPT_LOGGED_IN) {
-		return;
-	}
-
-	if (ic->acc->flags & ACC_FLAG_LOCAL) {
-		GHashTableIter nicks;
-		gpointer k, v;
-		g_hash_table_iter_init(&nicks, ic->acc->nicks);
-		while (g_hash_table_iter_next(&nicks, &k, &v)) {
-			ic->acc->prpl->add_buddy(ic, (char *) k, NULL);
-		}
-	}
-
-	imcb_log(ic, "Logged in");
-
-	ic->flags |= OPT_LOGGED_IN;
-	start_keepalives(ic, 60000);
-
-	/* Necessary to send initial presence status, even if we're not away. */
-	imc_away_send_update(ic);
-
-	/* Apparently we're connected successfully, so reset the
-	   exponential backoff timer. */
-	ic->acc->auto_reconnect_delay = 0;
-
-	if (ic->bee->ui->imc_connected) {
-		ic->bee->ui->imc_connected(ic);
-	}
-}
-
-gboolean auto_reconnect(gpointer data, gint fd, b_input_condition cond)
-{
-	account_t *a = data;
-
-	a->reconnect = 0;
-	account_on(a->bee, a);
-
-	return(FALSE);          /* Only have to run the timeout once */
-}
-
-void cancel_auto_reconnect(account_t *a)
-{
-	b_event_remove(a->reconnect);
-	a->reconnect = 0;
-}
-
-void imc_logout(struct im_connection *ic, int allow_reconnect)
-{
-	bee_t *bee = ic->bee;
-	account_t *a;
-	GSList *l;
-	int delay;
-
-	/* Nested calls might happen sometimes, this is probably the best
-	   place to catch them. */
-	if (ic->flags & OPT_LOGGING_OUT) {
-		return;
-	} else {
-		ic->flags |= OPT_LOGGING_OUT;
-	}
-
-	if (ic->bee->ui->imc_disconnected) {
-		ic->bee->ui->imc_disconnected(ic);
-	}
-
-	imcb_log(ic, "Signing off..");
-
-	/* TBH I don't remember anymore why I didn't just use ic->acc... */
-	for (a = bee->accounts; a; a = a->next) {
-		if (a->ic == ic) {
-			break;
-		}
-	}
-
-	if (a && !allow_reconnect && !(ic->flags & OPT_LOGGED_IN) &&
-	    set_getbool(&a->set, "oauth")) {
-		/* If this account supports OAuth, we're not logged in yet and
-		   not allowed to retry, assume there were auth issues. Give a
-		   helpful message on what might be necessary to fix this. */
-		imcb_log(ic, "If you're having problems logging in, try re-requesting "
-		         "an OAuth token: account %s set password \"\"", a->tag);
-	}
-
-	for (l = bee->users; l; ) {
-		bee_user_t *bu = l->data;
-		GSList *next = l->next;
-
-		if (bu->ic == ic) {
-			bee_user_free(bee, bu);
-		}
-
-		l = next;
-	}
-
-	b_event_remove(ic->keepalive);
-	ic->keepalive = 0;
-	ic->acc->prpl->logout(ic);
-	b_event_remove(ic->inpa);
-
-	g_free(ic->away);
-	ic->away = NULL;
-
-	query_del_by_conn((irc_t *) ic->bee->ui_data, ic);
-
-	if (!a) {
-		/* Uhm... This is very sick. */
-	} else if (allow_reconnect && set_getbool(&bee->set, "auto_reconnect") &&
-	           set_getbool(&a->set, "auto_reconnect") &&
-	           (delay = account_reconnect_delay(a)) > 0) {
-		imcb_log(ic, "Reconnecting in %d seconds..", delay);
-		a->reconnect = b_timeout_add(delay * 1000, auto_reconnect, a);
-	}
-
-	imc_free(ic);
-}
-
-void imcb_ask(struct im_connection *ic, char *msg, void *data,
-              query_callback doit, query_callback dont)
-{
-	query_add((irc_t *) ic->bee->ui_data, ic, msg, doit, dont, g_free, data);
-}
-
-void imcb_ask_with_free(struct im_connection *ic, char *msg, void *data,
-                        query_callback doit, query_callback dont, query_callback myfree)
-{
-	query_add((irc_t *) ic->bee->ui_data, ic, msg, doit, dont, myfree, data);
-}
-
-void imcb_add_buddy(struct im_connection *ic, const char *handle, const char *group)
-{
-	bee_user_t *bu;
-	bee_t *bee = ic->bee;
-	bee_group_t *oldg;
-
-	if (!(bu = bee_user_by_handle(bee, ic, handle))) {
-		bu = bee_user_new(bee, ic, handle, 0);
-	}
-
-	oldg = bu->group;
-	bu->group = bee_group_by_name(bee, group, TRUE);
-
-	if (bee->ui->user_group && bu->group != oldg) {
-		bee->ui->user_group(bee, bu);
-	}
-}
-
-void imcb_rename_buddy(struct im_connection *ic, const char *handle, const char *fullname)
-{
-	bee_t *bee = ic->bee;
-	bee_user_t *bu = bee_user_by_handle(bee, ic, handle);
-
-	if (!bu || !fullname) {
-		return;
-	}
-
-	if (!bu->fullname || strcmp(bu->fullname, fullname) != 0) {
-		g_free(bu->fullname);
-		bu->fullname = g_strdup(fullname);
-
-		if (bee->ui->user_fullname) {
-			bee->ui->user_fullname(bee, bu);
-		}
-	}
-}
-
-void imcb_remove_buddy(struct im_connection *ic, const char *handle, char *group)
-{
-	bee_user_free(ic->bee, bee_user_by_handle(ic->bee, ic, handle));
-}
-
-/* Implements either imcb_buddy_nick_hint() or imcb_buddy_nick_change() depending on the value of 'change' */
-static void buddy_nick_hint_or_change(struct im_connection *ic, const char *handle, const char *nick, gboolean change)
-{
-	bee_t *bee = ic->bee;
-	bee_user_t *bu = bee_user_by_handle(bee, ic, handle);
-
-	if (!bu || !nick) {
-		return;
-	}
-
-	g_free(bu->nick);
-	bu->nick = g_strdup(nick);
-
-	if (change && bee->ui->user_nick_change) {
-		bee->ui->user_nick_change(bee, bu, nick);
-	} else if (!change && bee->ui->user_nick_hint) {
-		bee->ui->user_nick_hint(bee, bu, nick);
-	}
-}
-
-/* Soft variant, for newly created users. Does nothing if it's already online */
-void imcb_buddy_nick_hint(struct im_connection *ic, const char *handle, const char *nick)
-{
-	buddy_nick_hint_or_change(ic, handle, nick, FALSE);
-}
-
-/* Hard variant, always changes the nick */
-void imcb_buddy_nick_change(struct im_connection *ic, const char *handle, const char *nick)
-{
-	buddy_nick_hint_or_change(ic, handle, nick, TRUE);
-}
-
-struct imcb_ask_cb_data {
-	struct im_connection *ic;
-	char *handle;
-};
-
-static void imcb_ask_cb_free(void *data)
-{
-	struct imcb_ask_cb_data *cbd = data;
-
-	g_free(cbd->handle);
-	g_free(cbd);
-}
-
-static void imcb_ask_auth_cb_no(void *data)
-{
-	struct imcb_ask_cb_data *cbd = data;
-
-	cbd->ic->acc->prpl->auth_deny(cbd->ic, cbd->handle);
-
-	imcb_ask_cb_free(cbd);
-}
-
-static void imcb_ask_auth_cb_yes(void *data)
-{
-	struct imcb_ask_cb_data *cbd = data;
-
-	cbd->ic->acc->prpl->auth_allow(cbd->ic, cbd->handle);
-
-	imcb_ask_cb_free(cbd);
-}
-
-void imcb_ask_auth(struct im_connection *ic, const char *handle, const char *realname)
-{
-	struct imcb_ask_cb_data *data = g_new0(struct imcb_ask_cb_data, 1);
-	char *s, *realname_ = NULL;
-
-	if (realname != NULL) {
-		realname_ = g_strdup_printf(" (%s)", realname);
-	}
-
-	s = g_strdup_printf("The user %s%s wants to add you to his/her buddy list.",
-	                    handle, realname_ ? realname_ : "");
-
-	g_free(realname_);
-
-	data->ic = ic;
-	data->handle = g_strdup(handle);
-	query_add((irc_t *) ic->bee->ui_data, ic, s,
-	          imcb_ask_auth_cb_yes, imcb_ask_auth_cb_no, imcb_ask_cb_free, data);
-
-	g_free(s);
-}
-
-static void imcb_ask_add_cb_yes(void *data)
-{
-	struct imcb_ask_cb_data *cbd = data;
-
-	cbd->ic->acc->prpl->add_buddy(cbd->ic, cbd->handle, NULL);
-
-	imcb_ask_cb_free(data);
-}
-
-void imcb_ask_add(struct im_connection *ic, const char *handle, const char *realname)
-{
-	struct imcb_ask_cb_data *data;
-	char *s;
-
-	/* TODO: Make a setting for this! */
-	if (bee_user_by_handle(ic->bee, ic, handle) != NULL) {
-		return;
-	}
-
-	data = g_new0(struct imcb_ask_cb_data, 1);
-
-	s = g_strdup_printf("The user %s is not in your buddy list yet. Do you want to add him/her now?", handle);
-
-	data->ic = ic;
-	data->handle = g_strdup(handle);
-	query_add((irc_t *) ic->bee->ui_data, ic, s,
-	          imcb_ask_add_cb_yes, imcb_ask_cb_free, imcb_ask_cb_free, data);
-
-	g_free(s);
-}
-
-struct bee_user *imcb_buddy_by_handle(struct im_connection *ic, const char *handle)
-{
-	return bee_user_by_handle(ic->bee, ic, handle);
-}
-
-/* The plan is to not allow straight calls to prpl functions anymore, but do
-   them all from some wrappers. We'll start to define some down here: */
-
-int imc_chat_msg(struct groupchat *c, char *msg, int flags)
-{
-	char *buf = NULL;
-
-	if ((c->ic->flags & OPT_DOES_HTML) && (g_strncasecmp(msg, "<html>", 6) != 0)) {
-		buf = escape_html(msg);
-		msg = buf;
-	}
-
-	c->ic->acc->prpl->chat_msg(c, msg, flags);
-	g_free(buf);
-
-	return 1;
-}
-
-static char *imc_away_state_find(GList *gcm, char *away, char **message);
-
-int imc_away_send_update(struct im_connection *ic)
-{
-	char *away, *msg = NULL;
-
-	if (ic->acc->prpl->away_states == NULL ||
-	    ic->acc->prpl->set_away == NULL) {
-		return 0;
-	}
-
-	away = set_getstr(&ic->acc->set, "away") ?
-	       : set_getstr(&ic->bee->set, "away");
-	if (away && *away) {
-		GList *m = ic->acc->prpl->away_states(ic);
-		if (m == NULL) {
-			return 0;
-		}
-		msg = ic->acc->flags & ACC_FLAG_AWAY_MESSAGE ? away : NULL;
-		away = imc_away_state_find(m, away, &msg) ? :
-		       (imc_away_state_find(m, "away", NULL) ? : m->data);
-	} else if (ic->acc->flags & ACC_FLAG_STATUS_MESSAGE) {
-		away = NULL;
-		msg = set_getstr(&ic->acc->set, "status") ?
-		      : set_getstr(&ic->bee->set, "status");
-	}
-
-	ic->acc->prpl->set_away(ic, away, msg);
-
-	return 1;
-}
-
-static char *imc_away_alias_list[8][5] =
+#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 },
@@ -793,106 +50,1033 @@
 	{ "On the phone", "Phone", "On phone", NULL },
 	{ "Out to lunch", "Lunch", "Food", NULL },
-	{ "Invisible", "Hidden" },
 	{ NULL }
 };
-
-static char *imc_away_state_find(GList *gcm, char *away, char **message)
+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 (m = gcm; m; m = m->next) {
-		if (g_strncasecmp(m->data, away, strlen(m->data)) == 0) {
-			/* At least the Yahoo! module works better if message
-			   contains no data unless it adds something to what
-			   we have in state already. */
-			if (message && strlen(m->data) == strlen(away)) {
-				*message = NULL;
+	
+	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 m->data;
-		}
-	}
-
-	for (i = 0; *imc_away_alias_list[i]; i++) {
-		int keep_message;
-
-		for (j = 0; imc_away_alias_list[i][j]; j++) {
-			if (g_strncasecmp(away, imc_away_alias_list[i][j], strlen(imc_away_alias_list[i][j])) == 0) {
-				keep_message = strlen(away) != strlen(imc_away_alias_list[i][j]);
+		}
+	}
+	
+	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 );
 			}
-		}
-
-		if (!imc_away_alias_list[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; imc_away_alias_list[i][j]; j++) {
-			for (m = gcm; m; m = m->next) {
-				if (g_strcasecmp(imc_away_alias_list[i][j], m->data) == 0) {
-					if (!keep_message && message) {
-						*message = NULL;
-					}
-
-					return m->data;
+			
+			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 ++;
 			}
-		}
-
-		/* No need to look further, apparently this state doesn't
-		   have any good alias for this protocol. */
-		break;
-	}
-
-	return NULL;
-}
-
-void imc_add_allow(struct im_connection *ic, char *handle)
-{
-	if (g_slist_find_custom(ic->permit, handle, (GCompareFunc) ic->acc->prpl->handle_cmp) == NULL) {
-		ic->permit = g_slist_prepend(ic->permit, g_strdup(handle));
-	}
-
-	ic->acc->prpl->add_permit(ic, handle);
-}
-
-void imc_rem_allow(struct im_connection *ic, char *handle)
-{
-	GSList *l;
-
-	if ((l = g_slist_find_custom(ic->permit, handle, (GCompareFunc) ic->acc->prpl->handle_cmp))) {
-		g_free(l->data);
-		ic->permit = g_slist_delete_link(ic->permit, l);
-	}
-
-	ic->acc->prpl->rem_permit(ic, handle);
-}
-
-void imc_add_block(struct im_connection *ic, char *handle)
-{
-	if (g_slist_find_custom(ic->deny, handle, (GCompareFunc) ic->acc->prpl->handle_cmp) == NULL) {
-		ic->deny = g_slist_prepend(ic->deny, g_strdup(handle));
-	}
-
-	ic->acc->prpl->add_deny(ic, handle);
-}
-
-void imc_rem_block(struct im_connection *ic, char *handle)
-{
-	GSList *l;
-
-	if ((l = g_slist_find_custom(ic->deny, handle, (GCompareFunc) ic->acc->prpl->handle_cmp))) {
-		g_free(l->data);
-		ic->deny = g_slist_delete_link(ic->deny, l);
-	}
-
-	ic->acc->prpl->rem_deny(ic, handle);
-}
-
-/* Deprecated: using this function resulted in merging several handles accidentally
- * Also the irc layer handles this decently nowadays */
-void imcb_clean_handle(struct im_connection *ic, char *handle)
-{
-}
+			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 );
+}
Index: protocols/nogaim.h
===================================================================
--- protocols/nogaim.h	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ protocols/nogaim.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -1,11 +1,10 @@
-/********************************************************************\
+  /********************************************************************\
   * BitlBee -- An IRC to other IM-networks gateway                     *
   *                                                                    *
-  * Copyright 2002-2012 Wilmer van der Gaast and others                *
+  * Copyright 2002-2004 Wilmer van der Gaast and others                *
   \********************************************************************/
 
 /*
- * nogaim, soon to be known as im_api. Not a separate product (unless
- * someone would be interested in such a thing), just a new name.
+ * nogaim
  *
  * Gaim without gaim - for BitlBee
@@ -16,5 +15,5 @@
  * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
  *                          (and possibly other members of the Gaim team)
- * Copyright 2002-2012 Wilmer van der Gaast <wilmer@gaast.net>
+ * Copyright 2002-2004 Wilmer van der Gaast <lintux@lintux.cx>
  */
 
@@ -32,6 +31,6 @@
   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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
 */
 
@@ -39,49 +38,45 @@
 #define _NOGAIM_H
 
-#if (__sun)
-#include <inttypes.h>
-#else
-#include <stdint.h>
-#endif
-
 #include "bitlbee.h"
-#include "account.h"
 #include "proxy.h"
-#include "query.h"
 #include "md5.h"
-#include "ft.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 WEBSITE "https://www.bitlbee.org/"
-
-/* Sharing flags between all kinds of things. I just hope I won't hit any
-   limits before 32-bit machines become extinct. ;-) */
-#define OPT_LOGGED_IN   0x00000001
-#define OPT_LOGGING_OUT 0x00000002
-#define OPT_AWAY        0x00000004
-#define OPT_MOBILE      0x00000008
-#define OPT_DOES_HTML   0x00000010
-#define OPT_LOCALBUDDY  0x00000020 /* For nicks local to one groupchat */
-#define OPT_SLOW_LOGIN  0x00000040 /* I.e. Twitter Oauth @ login time */
-#define OPT_TYPING      0x00000100 /* Some pieces of code make assumptions */
-#define OPT_THINKING    0x00000200 /* about these values... Stupid me! */
-#define OPT_NOOTR       0x00001000 /* protocol not suitable for OTR */
-#define OPT_PONGS       0x00010000 /* Service sends us keep-alives */
-#define OPT_PONGED      0x00020000 /* Received a keep-alive during last interval */
-#define OPT_SELFMESSAGE 0x00080000 /* A message sent by self from another location */
+#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 im_connection {
-	account_t *acc;
-	uint32_t flags;
-
-	/* each connection then can have its own protocol-specific data */
-	void *proto_data;
-
+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;
-	guint keepalive;
-
+	
 	/* 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 */
@@ -89,37 +84,51 @@
 	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 */
-	bee_t *bee;
-
-	GSList *groupchats;
-	GSList *chatlist;
-	GHashTable *bee_users;
-};
-
-struct groupchat {
-	struct im_connection *ic;
+	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 */
-	/* The in_room variable is a list of handles (not nicks!), kind of
-	 * "nick list". This is how you can check who is in the group chat
-	 * already, for example to avoid adding somebody two times. */
-	GList *in_room;
-	//GList *ignored;
-
-	//struct groupchat *next;
-	/* The title variable contains the ID you gave when you created the
-	 * chat using imcb_chat_new(). */
-	char *title;
-	/* Use imcb_chat_topic() to change this variable otherwise the user
-	 * won't notice the topic change. */
-	char *topic;
-	char joined;
-	/* This is for you, you can add your own structure here to extend this
-	 * structure for your protocol's needs. */
-	void *data;
-	void *ui_data;
+        GList *in_room;
+        GList *ignored;
+        int id;
+        
+        /* BitlBee */
+        struct conversation *next;
+        char *channel;
+        char *title;
+        char joined;
+        void *data;
 };
 
@@ -127,269 +136,212 @@
 	char name[80];
 	char show[BUDDY_ALIAS_MAXLEN];
-	int present;
+        int present;
+	int evil;
 	time_t signon;
 	time_t idle;
-	int uc;
+        int uc;
 	guint caps; /* woohoo! */
 	void *proto_data; /* what a hack */
-	struct im_connection *ic; /* the connection it belongs to */
-};
-
-struct buddy_action {
-	char *name;
-	char *description;
-};
-
-/* This enum takes a few things from libpurple and a few things from old OPT_ flags.
- * The only flag that was used before this struct was PRPL_OPT_NOOTR.
- *
- * The libpurple ones only use the same values as the PurpleProtocolOptions
- * enum for convenience, but there's no promise of direct compatibility with
- * those values. As of libpurple 2.8.0 they use up to 0x800 (1 << 11), which is
- * a nice coincidence.
- */
-typedef enum {
-	/* The protocol doesn't use passwords
-	 * Mirrors libpurple's OPT_PROTO_NO_PASSWORD */
-	PRPL_OPT_NO_PASSWORD = 1 << 4,
-
-	/* The protocol doesn't require passwords, but may use them
-	 * Mirrors libpurple's OPT_PROTO_PASSWORD_OPTIONAL */
-	PRPL_OPT_PASSWORD_OPTIONAL = 1 << 7,
-
-	/* The protocol is not suitable for OTR, see OPT_NOOTR */
-	PRPL_OPT_NOOTR = 1 << 12,
-} prpl_options_t;
+	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;
-	/* You should set this to the name of your protocol.
-	 * - The user sees this name ie. when imcb_log() is used. */
-	const char *name;
-	void *data;
-	/* Maximum Message Size of this protocol.
-	 * - Introduced for OTR, in order to fragment large protocol messages.
-	 * - 0 means "unlimited". */
-	unsigned int mms;
-
-	/* Added this one to be able to add per-account settings, don't think
-	 * it should be used for anything else. You are supposed to use the
-	 * set_add() function to add new settings. */
-	void (* init)           (account_t *);
-	/* The typical usage of the login() function:
-	 * - Create an im_connection using imcb_new() from the account_t parameter.
-	 * - Initialize your myproto_data struct - you should store all your protocol-specific data there.
-	 * - Save your custom structure to im_connection->proto_data.
-	 * - Use proxy_connect() to connect to the server.
-	 */
-	void (* login)          (account_t *);
-	/* Implementing this function is optional. */
-	void (* keepalive)      (struct im_connection *);
-	/* In this function you should:
-	 * - Tell the server about you are logging out.
-	 * - g_free() your myproto_data struct as BitlBee does not know how to
-	 *   properly do so.
-	 */
-	void (* logout)         (struct im_connection *);
-
-	/* This function is called when the user wants to send a message to a handle.
-	 * - 'to' is a handle, not a nick
-	 * - 'flags' may be ignored
-	 */
-	int (* buddy_msg)      (struct im_connection *, char *to, char *message, int flags);
-	/* This function is called then the user uses the /away IRC command.
-	 * - 'state' contains the away reason.
-	 * - 'message' may be ignored if your protocol does not support it.
-	 */
-	void (* set_away)       (struct im_connection *, char *state, char *message);
-	/* Implementing this function is optional. */
-	int (* send_typing)    (struct im_connection *, char *who, int flags);
-
-	/* 'name' is a handle to add/remove. For now BitlBee doesn't really
-	 * handle groups, just set it to NULL, so you can ignore that
-	 * parameter. */
-	void (* add_buddy)      (struct im_connection *, char *name, char *group);
-	void (* remove_buddy)   (struct im_connection *, char *name, char *group);
-
-	/* Block list stuff. Implementing these are optional. */
-	void (* add_permit)     (struct im_connection *, char *who);
-	void (* add_deny)       (struct im_connection *, char *who);
-	void (* rem_permit)     (struct im_connection *, char *who);
-	void (* rem_deny)       (struct im_connection *, char *who);
-
-	/* Request profile info. Free-formatted stuff, the IM module gives back
-	   this info via imcb_log(). Implementing these are optional. */
-	void (* get_info)       (struct im_connection *, char *who);
-
-	/* Group chat stuff. */
-	/* This is called when the user uses the /invite IRC command.
-	 * - 'who' may be ignored
-	 * - 'message' is a handle to invite
-	 */
-	void (* chat_invite)    (struct groupchat *, char *who, char *message);
-	/* This is called when the user uses the /kick IRC command.
-	 * - 'who' is a handle to kick
-	 * - 'message' is a kick message or NULL
-	 */
-	void (* chat_kick)      (struct groupchat *, char *who, const char *message);
-	/* This is called when the user uses the /part IRC command in a group
-	 * chat. You just should tell the user about it, nothing more. */
-	void (* chat_leave)     (struct groupchat *);
-	/* This is called when the user sends a message to the groupchat.
-	 * 'flags' may be ignored. */
-	void (* chat_msg)       (struct groupchat *, char *message, int flags);
-	/* This is called when the user uses the /join #nick IRC command.
-	 * - 'who' is the handle of the nick
-	 */
-	struct groupchat *
-	(* chat_with)      (struct im_connection *, char *who);
-	/* This is used when the user uses the /join #channel IRC command.  If
-	 * your protocol does not support publicly named group chats, then do
-	 * not implement this. */
-	struct groupchat *
-	(* chat_join)      (struct im_connection *, const char *room,
-	                    const char *nick, const char *password, set_t **sets);
-	/* Change the topic, if supported. Note that BitlBee expects the IM
-	   server to confirm the topic change with a regular topic change
-	   event. If it doesn't do that, you have to fake it to make it
-	   visible to the user. */
-	void (* chat_topic)     (struct groupchat *, char *topic);
-
-	/* If your protocol module needs any special info for joining chatrooms
-	   other than a roomname + nickname, add them here. */
-	void (* chat_add_settings)      (account_t *acc, set_t **head);
-	void (* chat_free_settings)     (account_t *acc, set_t **head);
-
-	/* You can tell what away states your protocol supports, so that
-	 * BitlBee will try to map the IRC away reasons to them. If your
-	 * protocol doesn't have any, just return one generic "Away". */
-	GList *(* away_states)(struct im_connection *ic);
-
-	/* Mainly for AOL, since they think "Bung hole" == "Bu ngho le". *sigh*
-	 * - Most protocols will just want to set this to g_strcasecmp().*/
-	int (* handle_cmp) (const char *who1, const char *who2);
-
-	/* Implement these callbacks if you want to use imcb_ask_auth() */
-	void (* auth_allow)     (struct im_connection *, const char *who);
-	void (* auth_deny)      (struct im_connection *, const char *who);
-
-	/* Incoming transfer request */
-	void (* transfer_request) (struct im_connection *, file_transfer_t *ft, char *handle);
-
-	void (* buddy_data_add) (struct bee_user *bu);
-	void (* buddy_data_free) (struct bee_user *bu);
-
-	GList *(* buddy_action_list) (struct bee_user *bu);
-	void *(* buddy_action) (struct bee_user *bu, const char *action, char * const args[], void *data);
-
-	/* If null, equivalent to handle_cmp( ic->acc->user, who ) */
-	gboolean (* handle_is_self) (struct im_connection *, const char *who);
-
-	/* This sets/updates the im_connection->chatlist field with a
-	 * bee_chat_info_t GSList. This function should ensure the
-	 * bee_chat_list_finish() function gets called at some point
-	 * after the chat list is completely updated.
-	 */
-	void (* chat_list) (struct im_connection *, const char *server);
-
-	/* Some placeholders so eventually older plugins may cooperate with newer BitlBees. */
-	void *resv1;
-	void *resv2;
-	void *resv3;
-	void *resv4;
-	void *resv5;
-};
-
-struct plugin_info
-{
-	guint abiver;
-	const char *name;
-	const char *version;
-	const char *description;
-	const char *author;
-	const char *url;
-};
-
-#ifdef WITH_PLUGINS
-G_MODULE_EXPORT GList *get_plugins();
-#endif
-
-/* im_api core stuff. */
+	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();
-G_MODULE_EXPORT GList *get_protocols();
-G_MODULE_EXPORT GList *get_protocols_disabled();
-G_MODULE_EXPORT GSList *get_connections();
-G_MODULE_EXPORT struct prpl *find_protocol(const char *name);
-G_MODULE_EXPORT gboolean is_protocol_disabled(const char *name);
-G_MODULE_EXPORT char *explain_unknown_protocol(const char *name);
-/* When registering a new protocol, you should allocate space for a new prpl
- * struct, initialize it (set the function pointers to point to your
- * functions), finally call this function. */
-G_MODULE_EXPORT void register_protocol(struct prpl *);
-
-/* Connection management. */
-/* You will need this function in prpl->login() to get an im_connection from
- * the account_t parameter. */
-G_MODULE_EXPORT struct im_connection *imcb_new(account_t *acc);
-G_MODULE_EXPORT void imc_free(struct im_connection *ic);
-/* Once you're connected, you should call this function, so that the user will
- * see the success. */
-G_MODULE_EXPORT void imcb_connected(struct im_connection *ic);
-/* This can be used to disconnect when something went wrong (ie. read error
- * from the server). You probably want to set the second parameter to TRUE. */
-G_MODULE_EXPORT void imc_logout(struct im_connection *ic, int allow_reconnect);
-
-/* Communicating with the user. */
-/* A printf()-like function to tell the user anything you want. */
-G_MODULE_EXPORT void imcb_log(struct im_connection *ic, char *format, ...) G_GNUC_PRINTF(2, 3);
-/* To tell the user an error, ie. before logging out when an error occurs. */
-G_MODULE_EXPORT void imcb_error(struct im_connection *ic, char *format, ...) G_GNUC_PRINTF(2, 3);
-
-/* To ask a your about something.
- * - 'msg' is the question.
- * - 'data' can be your custom struct - it will be passed to the callbacks.
- * - 'doit' or 'dont' will be called depending of the answer of the user.
- */
-G_MODULE_EXPORT void imcb_ask(struct im_connection *ic, char *msg, void *data, query_callback doit,
-                              query_callback dont);
-G_MODULE_EXPORT void imcb_ask_with_free(struct im_connection *ic, char *msg, void *data, query_callback doit,
-                                        query_callback dont, query_callback myfree);
-
-/* Two common questions you may want to ask:
- * - X added you to his contact list, allow?
- * - X is not in your contact list, want to add?
- */
-G_MODULE_EXPORT void imcb_ask_auth(struct im_connection *ic, const char *handle, const char *realname);
-G_MODULE_EXPORT void imcb_ask_add(struct im_connection *ic, const char *handle, const char *realname);
-
-/* Buddy management */
-/* This function should be called for each handle which are visible to the
- * user, usually after a login, or if the user added a buddy and the IM
- * server confirms that the add was successful. Don't forget to do this! */
-G_MODULE_EXPORT void imcb_add_buddy(struct im_connection *ic, const char *handle, const char *group);
-G_MODULE_EXPORT void imcb_remove_buddy(struct im_connection *ic, const char *handle, char *group);
-G_MODULE_EXPORT void imcb_rename_buddy(struct im_connection *ic, const char *handle, const char *realname);
-G_MODULE_EXPORT void imcb_buddy_nick_hint(struct im_connection *ic, const char *handle, const char *nick);
-G_MODULE_EXPORT void imcb_buddy_nick_change(struct im_connection *ic, const char *handle, const char *nick);
-G_MODULE_EXPORT void imcb_buddy_action_response(bee_user_t *bu, const char *action, char * const args[], void *data);
-
-G_MODULE_EXPORT void imcb_buddy_typing(struct im_connection *ic, const char *handle, guint32 flags);
-G_MODULE_EXPORT struct bee_user *imcb_buddy_by_handle(struct im_connection *ic, const char *handle);
-
-G_GNUC_DEPRECATED G_MODULE_EXPORT void imcb_clean_handle(struct im_connection *ic, char *handle);
-
-/* Actions, or whatever. */
-int imc_away_send_update(struct im_connection *ic);
-int imc_chat_msg(struct groupchat *c, char *msg, int flags);
-
-void imc_add_allow(struct im_connection *ic, char *handle);
-void imc_rem_allow(struct im_connection *ic, char *handle);
-void imc_add_block(struct im_connection *ic, char *handle);
-void imc_rem_block(struct im_connection *ic, char *handle);
-
-/* Misc. stuff */
-char *set_eval_timezone(set_t *set, char *value);
-gboolean auto_reconnect(gpointer data, gint fd, b_input_condition cond);
-void cancel_auto_reconnect(struct account *a);
-
-#endif
+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
Index: protocols/oscar/AUTHORS
===================================================================
--- protocols/oscar/AUTHORS	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/AUTHORS	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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
+
+
Index: protocols/oscar/COPYING
===================================================================
--- protocols/oscar/COPYING	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/COPYING	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -0,0 +1,513 @@
+		  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!
+
+
Index: protocols/oscar/Makefile
===================================================================
--- protocols/oscar/Makefile	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/Makefile	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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
Index: protocols/oscar/admin.c
===================================================================
--- protocols/oscar/admin.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/admin.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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;
+}
Index: protocols/oscar/admin.h
===================================================================
--- protocols/oscar/admin.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/admin.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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__ */
Index: protocols/oscar/aim.h
===================================================================
--- protocols/oscar/aim.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/aim.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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__ */
Index: protocols/oscar/aim_internal.h
===================================================================
--- protocols/oscar/aim_internal.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/aim_internal.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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__ */
Index: protocols/oscar/auth.c
===================================================================
--- protocols/oscar/auth.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/auth.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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;
+}
+
Index: protocols/oscar/bos.c
===================================================================
--- protocols/oscar/bos.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/bos.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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;
+}
+
+
Index: protocols/oscar/bos.h
===================================================================
--- protocols/oscar/bos.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/bos.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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__ */
Index: protocols/oscar/buddylist.c
===================================================================
--- protocols/oscar/buddylist.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/buddylist.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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;
+}
+
Index: protocols/oscar/buddylist.h
===================================================================
--- protocols/oscar/buddylist.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/buddylist.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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__ */
Index: protocols/oscar/chat.c
===================================================================
--- protocols/oscar/chat.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/chat.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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;
+}
Index: protocols/oscar/chat.h
===================================================================
--- protocols/oscar/chat.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/chat.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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__ */
Index: protocols/oscar/chatnav.c
===================================================================
--- protocols/oscar/chatnav.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/chatnav.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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;
+}
Index: protocols/oscar/chatnav.h
===================================================================
--- protocols/oscar/chatnav.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/chatnav.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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__ */
Index: protocols/oscar/conn.c
===================================================================
--- protocols/oscar/conn.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/conn.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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;
+}
+
+
Index: protocols/oscar/ft.c
===================================================================
--- protocols/oscar/ft.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/ft.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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 */
+}
+
Index: protocols/oscar/ft.h
===================================================================
--- protocols/oscar/ft.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/ft.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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__ */
Index: protocols/oscar/icq.c
===================================================================
--- protocols/oscar/icq.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/icq.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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;
+}
+
+
Index: protocols/oscar/icq.h
===================================================================
--- protocols/oscar/icq.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/icq.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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__ */
Index: protocols/oscar/im.c
===================================================================
--- protocols/oscar/im.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/im.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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 &#2026; 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, &params);
+
+	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;
+}
Index: protocols/oscar/im.h
===================================================================
--- protocols/oscar/im.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/im.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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__ */
Index: protocols/oscar/info.c
===================================================================
--- protocols/oscar/info.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/info.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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;
+}
Index: protocols/oscar/info.h
===================================================================
--- protocols/oscar/info.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/info.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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__ */
Index: protocols/oscar/misc.c
===================================================================
--- protocols/oscar/misc.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/misc.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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;
+}
+
+
Index: protocols/oscar/msgcookie.c
===================================================================
--- protocols/oscar/msgcookie.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/msgcookie.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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;
+	}           
+}
+
+
Index: protocols/oscar/oscar.c
===================================================================
--- protocols/oscar/oscar.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/oscar.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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;
+}
Index: protocols/oscar/oscar_util.c
===================================================================
--- protocols/oscar/oscar_util.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/oscar_util.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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;
+}
Index: protocols/oscar/rxhandlers.c
===================================================================
--- protocols/oscar/rxhandlers.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/rxhandlers.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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;
+}
Index: protocols/oscar/rxqueue.c
===================================================================
--- protocols/oscar/rxqueue.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/rxqueue.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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;
+}
+
Index: protocols/oscar/search.c
===================================================================
--- protocols/oscar/search.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/search.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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;
+}
+
+
Index: protocols/oscar/search.h
===================================================================
--- protocols/oscar/search.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/search.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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__ */
Index: protocols/oscar/service.c
===================================================================
--- protocols/oscar/service.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/service.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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;
+}
+
Index: protocols/oscar/snac.c
===================================================================
--- protocols/oscar/snac.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/snac.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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;
+}
Index: protocols/oscar/ssi.c
===================================================================
--- protocols/oscar/ssi.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/ssi.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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;
+}
Index: protocols/oscar/ssi.h
===================================================================
--- protocols/oscar/ssi.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/ssi.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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__ */
Index: protocols/oscar/stats.c
===================================================================
--- protocols/oscar/stats.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/stats.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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;
+}
Index: protocols/oscar/tlv.c
===================================================================
--- protocols/oscar/tlv.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/tlv.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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
+
Index: protocols/oscar/txqueue.c
===================================================================
--- protocols/oscar/txqueue.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/oscar/txqueue.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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;
+}
+
+
Index: protocols/proxy.c
===================================================================
--- protocols/proxy.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/proxy.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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;
+}
Index: protocols/proxy.h
===================================================================
--- protocols/proxy.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/proxy.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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_ */
Index: otocols/purple/Makefile
===================================================================
--- protocols/purple/Makefile	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,47 +1,0 @@
-###########################
-## Makefile for BitlBee  ##
-##                       ##
-## Copyright 2002 Lintux ##
-###########################
-
-### DEFINITIONS
-
--include ../../Makefile.settings
-ifdef _SRCDIR_
-_SRCDIR_ := $(_SRCDIR_)protocols/purple/
-endif
-
-# [SH] Program variables
-objects = ft.o purple.o
-
-CFLAGS += -Wall $(PURPLE_CFLAGS)
-LFLAGS += -r
-
-# [SH] Phony targets
-all: purple_mod.o
-check: all
-lcov: check
-gcov: 
-	gcov *.c
-
-.PHONY: all clean distclean
-
-clean:
-	rm -f *.o core
-
-distclean: clean
-	rm -rf .depend
-
-### MAIN PROGRAM
-
-$(objects): ../../Makefile.settings Makefile
-
-$(objects): %.o: $(_SRCDIR_)%.c
-	@echo '*' Compiling $<
-	$(VERBOSE) $(CC) -c $(CFLAGS) $(CFLAGS_BITLBEE) $< -o $@
-
-purple_mod.o: $(objects)
-	@echo '*' Linking purple_mod.o
-	$(VERBOSE) $(LD) $(LFLAGS) $(objects) -o purple_mod.o
-
--include .depend/*.d
Index: otocols/purple/bpurple.h
===================================================================
--- protocols/purple/bpurple.h	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,23 +1,0 @@
-#ifndef BPURPLE_H
-# define BPURPLE_H
-
-#include <purple.h>
-#include <glib.h>
-
-#define PURPLE_REQUEST_HANDLE "purple_request"
-
-#define PURPLE_OPT_SHOULD_SET_NICK 1
-
-struct purple_data
-{
-    PurpleAccount *account;
-
-    GHashTable *input_requests;
-    guint next_request_id;
-    char *chat_list_server;
-    GSList *filetransfers;
-
-    int flags;
-};
-
-#endif /* !BPURPLE_H */
Index: otocols/purple/ft-direct.c
===================================================================
--- protocols/purple/ft-direct.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,239 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  libpurple module - File transfer stuff                                   *
-*                                                                           *
-*  Copyright 2009-2010 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.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-\***************************************************************************/
-
-/* This code tries to do direct file transfers, i.e. without caching the file
-   locally on disk first. Since libpurple can only do this since version 2.6.0
-   and even then very unreliably (and not with all IM modules), I'm canning
-   this code for now. */
-
-#include "bitlbee.h"
-#include "bpurple.h"
-
-#include <stdarg.h>
-
-#include <glib.h>
-#include <purple.h>
-
-struct prpl_xfer_data {
-	PurpleXfer *xfer;
-	file_transfer_t *ft;
-	gint ready_timer;
-	char *buf;
-	int buf_len;
-};
-
-static file_transfer_t *next_ft;
-
-struct im_connection *purple_ic_by_pa(PurpleAccount *pa);
-
-/* Glorious hack: We seem to have to remind at least some libpurple plugins
-   that we're ready because this info may get lost if we give it too early.
-   So just do it ten times a second. :-/ */
-static gboolean prplcb_xfer_write_request_cb(gpointer data, gint fd, b_input_condition cond)
-{
-	struct prpl_xfer_data *px = data;
-
-	purple_xfer_ui_ready(px->xfer);
-
-	return purple_xfer_get_type(px->xfer) == PURPLE_XFER_RECEIVE;
-}
-
-static gboolean prpl_xfer_write_request(struct file_transfer *ft)
-{
-	struct prpl_xfer_data *px = ft->data;
-
-	px->ready_timer = b_timeout_add(100, prplcb_xfer_write_request_cb, px);
-	return TRUE;
-}
-
-static gboolean prpl_xfer_write(struct file_transfer *ft, char *buffer, unsigned int len)
-{
-	struct prpl_xfer_data *px = ft->data;
-
-	px->buf = g_memdup2(buffer, len);
-	px->buf_len = len;
-
-	//purple_xfer_ui_ready( px->xfer );
-	px->ready_timer = b_timeout_add(0, prplcb_xfer_write_request_cb, px);
-
-	return TRUE;
-}
-
-static void prpl_xfer_accept(struct file_transfer *ft)
-{
-	struct prpl_xfer_data *px = ft->data;
-
-	purple_xfer_request_accepted(px->xfer, NULL);
-	prpl_xfer_write_request(ft);
-}
-
-static void prpl_xfer_canceled(struct file_transfer *ft, char *reason)
-{
-	struct prpl_xfer_data *px = ft->data;
-
-	purple_xfer_request_denied(px->xfer);
-}
-
-static gboolean prplcb_xfer_new_send_cb(gpointer data, gint fd, b_input_condition cond)
-{
-	PurpleXfer *xfer = data;
-	struct im_connection *ic = purple_ic_by_pa(xfer->account);
-	struct prpl_xfer_data *px = g_new0(struct prpl_xfer_data, 1);
-	PurpleBuddy *buddy;
-	const char *who;
-
-	buddy = purple_find_buddy(xfer->account, xfer->who);
-	who = buddy ? purple_buddy_get_name(buddy) : xfer->who;
-
-	/* TODO(wilmer): After spreading some more const goodness in BitlBee,
-	   remove the evil cast below. */
-	px->ft = imcb_file_send_start(ic, (char *) who, xfer->filename, xfer->size);
-	px->ft->data = px;
-	px->xfer = data;
-	px->xfer->ui_data = px;
-
-	px->ft->accept = prpl_xfer_accept;
-	px->ft->canceled = prpl_xfer_canceled;
-	px->ft->write_request = prpl_xfer_write_request;
-
-	return FALSE;
-}
-
-static void prplcb_xfer_new(PurpleXfer *xfer)
-{
-	if (purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE) {
-		/* This should suppress the stupid file dialog. */
-		purple_xfer_set_local_filename(xfer, "/tmp/wtf123");
-
-		/* Sadly the xfer struct is still empty ATM so come back after
-		   the caller is done. */
-		b_timeout_add(0, prplcb_xfer_new_send_cb, xfer);
-	} else {
-		struct prpl_xfer_data *px = g_new0(struct prpl_xfer_data, 1);
-
-		px->ft = next_ft;
-		px->ft->data = px;
-		px->xfer = xfer;
-		px->xfer->ui_data = px;
-
-		purple_xfer_set_filename(xfer, px->ft->file_name);
-		purple_xfer_set_size(xfer, px->ft->file_size);
-
-		next_ft = NULL;
-	}
-}
-
-static void prplcb_xfer_progress(PurpleXfer *xfer, double percent)
-{
-	fprintf(stderr, "prplcb_xfer_dbg 0x%p %f\n", xfer, percent);
-}
-
-static void prplcb_xfer_dbg(PurpleXfer *xfer)
-{
-	fprintf(stderr, "prplcb_xfer_dbg 0x%p\n", xfer);
-}
-
-static gssize prplcb_xfer_write(PurpleXfer *xfer, const guchar *buffer, gssize size)
-{
-	struct prpl_xfer_data *px = xfer->ui_data;
-	gboolean st;
-
-	fprintf(stderr, "xfer_write %d %d\n", size, px->buf_len);
-
-	b_event_remove(px->ready_timer);
-	px->ready_timer = 0;
-
-	st = px->ft->write(px->ft, (char *) buffer, size);
-
-	if (st && xfer->bytes_remaining == size) {
-		imcb_file_finished(px->ft);
-	}
-
-	return st ? size : 0;
-}
-
-gssize prplcb_xfer_read(PurpleXfer *xfer, guchar **buffer, gssize size)
-{
-	struct prpl_xfer_data *px = xfer->ui_data;
-
-	fprintf(stderr, "xfer_read %d %d\n", size, px->buf_len);
-
-	if (px->buf) {
-		*buffer = px->buf;
-		px->buf = NULL;
-
-		px->ft->write_request(px->ft);
-
-		return px->buf_len;
-	}
-
-	return 0;
-}
-
-PurpleXferUiOps bee_xfer_uiops =
-{
-	prplcb_xfer_new,           /* new_xfer */
-	prplcb_xfer_dbg,           /* destroy */
-	prplcb_xfer_dbg,           /* add_xfer */
-	prplcb_xfer_progress,      /* update_progress */
-	prplcb_xfer_dbg,           /* cancel_local */
-	prplcb_xfer_dbg,           /* cancel_remote */
-	prplcb_xfer_write,         /* ui_write */
-	prplcb_xfer_read,          /* ui_read */
-	prplcb_xfer_dbg,           /* data_not_sent */
-};
-
-static gboolean prplcb_xfer_send_cb(gpointer data, gint fd, b_input_condition cond);
-
-void purple_transfer_request(struct im_connection *ic, file_transfer_t *ft, char *handle)
-{
-	struct purple_data *pd = ic->proto_data;
-	struct prpl_xfer_data *px;
-
-	/* xfer_new() will pick up this variable. It's a hack but we're not
-	   multi-threaded anyway. */
-	next_ft = ft;
-	serv_send_file(purple_account_get_connection(pd->account), handle,
-	               ft->file_name);
-
-	ft->write = prpl_xfer_write;
-
-	px = ft->data;
-	imcb_file_recv_start(ft);
-
-	px->ready_timer = b_timeout_add(100, prplcb_xfer_send_cb, px);
-}
-
-static gboolean prplcb_xfer_send_cb(gpointer data, gint fd, b_input_condition cond)
-{
-	struct prpl_xfer_data *px = data;
-
-	if (px->ft->status & FT_STATUS_TRANSFERRING) {
-		fprintf(stderr, "The ft, it is ready...\n");
-		px->ft->write_request(px->ft);
-
-		return FALSE;
-	}
-
-	return TRUE;
-}
Index: otocols/purple/ft.c
===================================================================
--- protocols/purple/ft.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,404 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  libpurple module - File transfer stuff                                   *
-*                                                                           *
-*  Copyright 2009-2010 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.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-\***************************************************************************/
-
-/* Do file transfers via disk for now, since libpurple was really designed
-   for straight-to/from disk fts and is only just learning how to pass the
-   file contents the the UI instead (2.6.0 and higher it seems, and with
-   varying levels of success). */
-
-#include "bitlbee.h"
-#include "bpurple.h"
-
-#include <stdarg.h>
-
-#include <glib.h>
-#include <purple.h>
-
-struct prpl_xfer_data {
-	PurpleXfer *xfer;
-	file_transfer_t *ft;
-	struct im_connection *ic;
-	int fd;
-	char *fn, *handle;
-	gboolean ui_wants_data;
-	int timeout;
-};
-
-static file_transfer_t *next_ft;
-
-struct im_connection *purple_ic_by_pa(PurpleAccount *pa);
-static gboolean prplcb_xfer_new_send_cb(gpointer data, gint fd, b_input_condition cond);
-static gboolean prpl_xfer_write_request(struct file_transfer *ft);
-
-
-/* Receiving files (IM->UI): */
-static void prpl_xfer_accept(struct file_transfer *ft)
-{
-	struct prpl_xfer_data *px = ft->data;
-
-	purple_xfer_request_accepted(px->xfer, NULL);
-	prpl_xfer_write_request(ft);
-}
-
-static void prpl_xfer_canceled(struct file_transfer *ft, char *reason)
-{
-	struct prpl_xfer_data *px = ft->data;
-
-	if (px->xfer) {
-		if (!purple_xfer_is_completed(px->xfer) && !purple_xfer_is_canceled(px->xfer)) {
-			purple_xfer_cancel_local(px->xfer);
-		}
-		px->xfer->ui_data = NULL;
-		purple_xfer_unref(px->xfer);
-		px->xfer = NULL;
-	}
-}
-
-static void prpl_xfer_free(struct file_transfer *ft)
-{
-	struct prpl_xfer_data *px = ft->data;
-	struct purple_data *pd = px->ic->proto_data;
-
-	pd->filetransfers = g_slist_remove(pd->filetransfers, px);
-
-	if (px->xfer) {
-		px->xfer->ui_data = NULL;
-		purple_xfer_unref(px->xfer);
-	}
-
-	if (px->timeout) {
-		b_event_remove(px->timeout);
-	}
-
-	g_free(px->fn);
-	g_free(px->handle);
-	if (px->fd >= 0) {
-		close(px->fd);
-	}
-	g_free(px);
-}
-
-static void prplcb_xfer_new(PurpleXfer *xfer)
-{
-	purple_xfer_ref(xfer);
-
-	if (purple_xfer_get_type(xfer) == PURPLE_XFER_RECEIVE) {
-		struct prpl_xfer_data *px = g_new0(struct prpl_xfer_data, 1);
-		struct purple_data *pd;
-
-		xfer->ui_data = px;
-		px->xfer = xfer;
-		px->fn = mktemp(g_strdup("/tmp/bitlbee-purple-ft.XXXXXX"));
-		px->fd = -1;
-		px->ic = purple_ic_by_pa(xfer->account);
-
-		pd = px->ic->proto_data;
-		pd->filetransfers = g_slist_prepend(pd->filetransfers, px);
-
-		purple_xfer_set_local_filename(xfer, px->fn);
-
-		/* Sadly the xfer struct is still empty ATM so come back after
-		   the caller is done. */
-		b_timeout_add(0, prplcb_xfer_new_send_cb, xfer);
-	} else {
-		struct file_transfer *ft = next_ft;
-		struct prpl_xfer_data *px = ft->data;
-
-		xfer->ui_data = px;
-		px->xfer = xfer;
-
-		next_ft = NULL;
-	}
-}
-
-static gboolean prplcb_xfer_new_send_cb(gpointer data, gint fd, b_input_condition cond)
-{
-	PurpleXfer *xfer = data;
-	struct im_connection *ic = purple_ic_by_pa(xfer->account);
-	struct prpl_xfer_data *px = xfer->ui_data;
-	PurpleBuddy *buddy;
-	const char *who;
-
-	buddy = purple_find_buddy(xfer->account, xfer->who);
-	who = buddy ? purple_buddy_get_name(buddy) : xfer->who;
-
-	/* TODO(wilmer): After spreading some more const goodness in BitlBee,
-	   remove the evil cast below. */
-	px->ft = imcb_file_send_start(ic, (char *) who, xfer->filename, xfer->size);
-
-	if (!px->ft) {
-		return FALSE;
-	}
-	px->ft->data = px;
-
-	px->ft->accept = prpl_xfer_accept;
-	px->ft->canceled = prpl_xfer_canceled;
-	px->ft->free = prpl_xfer_free;
-	px->ft->write_request = prpl_xfer_write_request;
-
-	return FALSE;
-}
-
-gboolean try_write_to_ui(gpointer data, gint fd, b_input_condition cond)
-{
-	struct file_transfer *ft = data;
-	struct prpl_xfer_data *px = ft->data;
-	struct stat fs;
-	off_t tx_bytes;
-
-	/* If we don't have the file opened yet, there's no data so wait. */
-	if (px->fd < 0 || !px->ui_wants_data) {
-		return FALSE;
-	}
-
-	tx_bytes = lseek(px->fd, 0, SEEK_CUR);
-	fstat(px->fd, &fs);
-
-	if (fs.st_size > tx_bytes) {
-		char buf[1024];
-		size_t n = MIN(fs.st_size - tx_bytes, sizeof(buf));
-
-		if (read(px->fd, buf, n) == n && ft->write(ft, buf, n)) {
-			px->ui_wants_data = FALSE;
-		} else {
-			purple_xfer_cancel_local(px->xfer);
-			imcb_file_canceled(px->ic, ft, "Read error");
-		}
-	}
-
-	if (lseek(px->fd, 0, SEEK_CUR) == px->xfer->size) {
-		/*purple_xfer_end( px->xfer );*/
-		imcb_file_finished(px->ic, ft);
-	}
-
-	return FALSE;
-}
-
-/* UI calls this when its buffer is empty and wants more data to send to the user. */
-static gboolean prpl_xfer_write_request(struct file_transfer *ft)
-{
-	struct prpl_xfer_data *px = ft->data;
-
-	px->ui_wants_data = TRUE;
-	try_write_to_ui(ft, 0, 0);
-
-	return FALSE;
-}
-
-
-static void prplcb_xfer_destroy(PurpleXfer *xfer)
-{
-	struct prpl_xfer_data *px = xfer->ui_data;
-
-	if (px) {
-		px->xfer = NULL;
-	}
-}
-
-static void prplcb_xfer_progress(PurpleXfer *xfer, double percent)
-{
-	struct prpl_xfer_data *px = xfer->ui_data;
-
-	if (px == NULL) {
-		return;
-	}
-
-	if (purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) {
-		if (*px->fn) {
-			char *slash;
-
-			unlink(px->fn);
-			if ((slash = strrchr(px->fn, '/'))) {
-				*slash = '\0';
-				rmdir(px->fn);
-			}
-			*px->fn = '\0';
-		}
-
-		return;
-	}
-
-	if (px->fd == -1 && percent > 0) {
-		/* Weeeeeeeee, we're getting data! That means the file exists
-		   by now so open it and start sending to the UI. */
-		px->fd = open(px->fn, O_RDONLY);
-
-		/* Unlink it now, because we don't need it after this. */
-		unlink(px->fn);
-	}
-
-	if (percent < 1) {
-		try_write_to_ui(px->ft, 0, 0);
-	} else {
-		/* Another nice problem: If we have the whole file, it only
-		   gets closed when we return. Problem: There may still be
-		   stuff buffered and not written, we'll only see it after
-		   the caller close()s the file. So poll the file after that. */
-		b_timeout_add(0, try_write_to_ui, px->ft);
-	}
-}
-
-static void prplcb_xfer_cancel_remote(PurpleXfer *xfer)
-{
-	struct prpl_xfer_data *px = xfer->ui_data;
-
-	if (px && px->ft) {
-		imcb_file_canceled(px->ic, px->ft, "Canceled by remote end");
-	} else if (px) {
-		/* px->ft == NULL for sends, because of the two stages. :-/ */
-		imcb_error(px->ic, "File transfer cancelled by remote end");
-	}
-}
-
-
-/* Sending files (UI->IM): */
-static gboolean prpl_xfer_write(struct file_transfer *ft, char *buffer, unsigned int len);
-static gboolean purple_transfer_request_cb(gpointer data, gint fd, b_input_condition cond);
-
-void purple_transfer_request(struct im_connection *ic, file_transfer_t *ft, char *handle)
-{
-	struct prpl_xfer_data *px = g_new0(struct prpl_xfer_data, 1);
-	struct purple_data *pd;
-	char *dir, *basename;
-
-	ft->data = px;
-	px->ft = ft;
-	px->ft->free = prpl_xfer_free;
-
-	dir = g_strdup("/tmp/bitlbee-purple-ft.XXXXXX");
-	if (!mkdtemp(dir)) {
-		imcb_error(ic, "Could not create temporary file for file transfer");
-		g_free(px);
-		g_free(dir);
-		return;
-	}
-
-	if ((basename = strrchr(ft->file_name, '/'))) {
-		basename++;
-	} else {
-		basename = ft->file_name;
-	}
-	px->fn = g_strdup_printf("%s/%s", dir, basename);
-	px->fd = open(px->fn, O_WRONLY | O_CREAT, 0600);
-	g_free(dir);
-
-	if (px->fd < 0) {
-		imcb_error(ic, "Could not create temporary file for file transfer");
-		g_free(px);
-		g_free(px->fn);
-		return;
-	}
-
-	px->ic = ic;
-	px->handle = g_strdup(handle);
-
-	pd = px->ic->proto_data;
-	pd->filetransfers = g_slist_prepend(pd->filetransfers, px);
-
-	imcb_log(ic,
-	         "Due to libpurple limitations, the file has to be cached locally before proceeding with the actual file transfer. Please wait...");
-
-	px->timeout = b_timeout_add(0, purple_transfer_request_cb, ft);
-}
-
-static void purple_transfer_forward(struct file_transfer *ft)
-{
-	struct prpl_xfer_data *px = ft->data;
-	struct purple_data *pd = px->ic->proto_data;
-
-	/* xfer_new() will pick up this variable. It's a hack but we're not
-	   multi-threaded anyway. */
-	next_ft = ft;
-	serv_send_file(purple_account_get_connection(pd->account),
-                   px->handle, px->fn);
-}
-
-static gboolean purple_transfer_request_cb(gpointer data, gint fd, b_input_condition cond)
-{
-	file_transfer_t *ft = data;
-	struct prpl_xfer_data *px = ft->data;
-
-	px->timeout = 0;
-
-	if (ft->write == NULL) {
-		ft->write = prpl_xfer_write;
-		imcb_file_recv_start(px->ic, ft);
-	}
-
-	ft->write_request(ft);
-
-	return FALSE;
-}
-
-static gboolean prpl_xfer_write(struct file_transfer *ft, char *buffer, unsigned int len)
-{
-	struct prpl_xfer_data *px = ft->data;
-
-	if (write(px->fd, buffer, len) != len) {
-		imcb_file_canceled(px->ic, ft, "Error while writing temporary file");
-		return FALSE;
-	}
-
-	if (lseek(px->fd, 0, SEEK_CUR) >= ft->file_size) {
-		close(px->fd);
-		px->fd = -1;
-
-		purple_transfer_forward(ft);
-		imcb_file_finished(px->ic, ft);
-		px->ft = NULL;
-	} else {
-		px->timeout = b_timeout_add(0, purple_transfer_request_cb, ft);
-	}
-
-	return TRUE;
-}
-
-void purple_transfer_cancel_all(struct im_connection *ic)
-{
-	struct purple_data *pd = ic->proto_data;
-
-	while (pd->filetransfers) {
-		struct prpl_xfer_data *px = pd->filetransfers->data;
-
-		if (px->ft) {
-			imcb_file_canceled(ic, px->ft, "Logging out");
-		}
-
-		pd->filetransfers = g_slist_remove(pd->filetransfers, px);
-	}
-}
-
-
-
-PurpleXferUiOps bee_xfer_uiops =
-{
-	prplcb_xfer_new,           /* new_xfer */
-	prplcb_xfer_destroy,       /* destroy */
-	NULL,                      /* add_xfer */
-	prplcb_xfer_progress,      /* update_progress */
-	NULL,                      /* cancel_local */
-	prplcb_xfer_cancel_remote, /* cancel_remote */
-	NULL,                      /* ui_write */
-	NULL,                      /* ui_read */
-	NULL,                      /* data_not_sent */
-};
Index: otocols/purple/purple.c
===================================================================
--- protocols/purple/purple.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,1976 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  libpurple module - Main file                                             *
-*                                                                           *
-*  Copyright 2009-2012 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.,  *
-*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
-*                                                                           *
-\***************************************************************************/
-
-#include "bitlbee.h"
-#include "bpurple.h"
-#include "help.h"
-
-#include <stdarg.h>
-
-#include <glib.h>
-#include <purple.h>
-
-#if !PURPLE_VERSION_CHECK(2, 12, 0)
-#define PURPLE_MESSAGE_REMOTE_SEND 0x10000
-#endif
-
-GSList *purple_connections;
-
-/* This makes me VERY sad... :-( But some libpurple callbacks come in without
-   any context so this is the only way to get that. Don't want to support
-   libpurple in daemon mode anyway. */
-static bee_t *local_bee;
-
-static char *set_eval_display_name(set_t *set, char *value);
-
-void purple_request_input_callback(guint id, struct im_connection *ic,
-                                   const char *message, const char *who);
-
-void purple_transfer_cancel_all(struct im_connection *ic);
-
-/* purple_request_input specific stuff */
-typedef void (*ri_callback_t)(gpointer, const gchar *);
-
-struct request_input_data {
-	ri_callback_t data_callback;
-	void *user_data;
-	struct im_connection *ic;
-	char *buddy;
-	guint id;
-};
-
-struct purple_roomlist_data {
-	GSList *chats;
-	gint topic;
-	gboolean initialized;
-};
-
-
-struct im_connection *purple_ic_by_pa(PurpleAccount *pa)
-{
-	GSList *i;
-	struct purple_data *pd;
-
-	for (i = purple_connections; i; i = i->next) {
-		pd = ((struct im_connection *) i->data)->proto_data;
-		if (pd->account == pa) {
-			return i->data;
-		}
-	}
-
-	return NULL;
-}
-
-static struct im_connection *purple_ic_by_gc(PurpleConnection *gc)
-{
-	return purple_ic_by_pa(purple_connection_get_account(gc));
-}
-
-static gboolean purple_menu_cmp(const char *a, const char *b)
-{
-	while (*a && *b) {
-		while (*a == '_') {
-			a++;
-		}
-		while (*b == '_') {
-			b++;
-		}
-		if (g_ascii_tolower(*a) != g_ascii_tolower(*b)) {
-			return FALSE;
-		}
-
-		a++;
-		b++;
-	}
-
-	return (*a == '\0' && *b == '\0');
-}
-
-static gboolean purple_account_should_set_nick(account_t *acc)
-{
-	/* whitelist of protocols that tend to have numeric or meaningless usernames, and should
-	 * always offer the 'alias' as a nick.  this is just so that users don't have to do
-	 * 'account whatever set nick_format %full_name'
-	 */
-	char *whitelist[] = {
-		"prpl-hangouts",
-		"prpl-eionrobb-funyahoo-plusplus",
-		"prpl-line",
-		NULL,
-	};
-	char **p;
-
-	for (p = whitelist; *p; p++) {
-		if (g_strcmp0(acc->prpl->data, *p) == 0) {
-			return TRUE;
-		}
-	}
-
-	return FALSE;
-}
-
-static void purple_init(account_t *acc)
-{
-	char *prpl_id = acc->prpl->data;
-	PurplePlugin *prpl = purple_plugins_find_with_id(prpl_id);
-	PurplePluginProtocolInfo *pi = prpl->info->extra_info;
-	PurpleAccount *pa;
-	GList *i, *st;
-	set_t *s;
-	char help_title[64];
-	GString *help;
-	static gboolean dir_fixed = FALSE;
-
-	/* Layer violation coming up: Making an exception for libpurple here.
-	   Dig in the IRC state a bit to get a username. Ideally we should
-	   check if s/he identified but this info doesn't seem *that* important.
-	   It's just that fecking libpurple can't *not* store this shit.
-
-	   Remember that libpurple is not really meant to be used on public
-	   servers anyway! */
-	if (!dir_fixed) {
-		PurpleCertificatePool *pool;
-		irc_t *irc = acc->bee->ui_data;
-		char *dir;
-
-		dir = g_strdup_printf("%s/purple/%s", global.conf->configdir, irc->user->nick);
-		purple_util_set_user_dir(dir);
-		g_free(dir);
-
-		purple_blist_load();
-		purple_prefs_load();
-
-		if (proxytype == PROXY_SOCKS4A) {
-			/* do this here after loading prefs. yes, i know, it sucks */
-			purple_prefs_set_bool("/purple/proxy/socks4_remotedns", TRUE);
-		}
-
-		/* re-create the certificate cache directory */
-		pool = purple_certificate_find_pool("x509", "tls_peers");
-		dir = purple_certificate_pool_mkpath(pool, NULL);
-		purple_build_dir(dir, 0700);
-		g_free(dir);
-
-		dir_fixed = TRUE;
-	}
-
-	help = g_string_new("");
-	g_string_printf(help, "BitlBee libpurple module %s (%s).\n\nSupported settings:",
-	                (char *) acc->prpl->name, prpl->info->name);
-
-	if (pi->user_splits) {
-		GList *l;
-		g_string_append_printf(help, "\n* username: Username");
-		for (l = pi->user_splits; l; l = l->next) {
-			g_string_append_printf(help, "%c%s",
-			                       purple_account_user_split_get_separator(l->data),
-			                       purple_account_user_split_get_text(l->data));
-		}
-	}
-
-	/* Convert all protocol_options into per-account setting variables. */
-	for (i = pi->protocol_options; i; i = i->next) {
-		PurpleAccountOption *o = i->data;
-		const char *name;
-		char *def = NULL;
-		set_eval eval = NULL;
-		void *eval_data = NULL;
-		GList *io = NULL;
-		GSList *opts = NULL;
-
-		name = purple_account_option_get_setting(o);
-
-		switch (purple_account_option_get_type(o)) {
-		case PURPLE_PREF_STRING:
-			def = g_strdup(purple_account_option_get_default_string(o));
-
-			g_string_append_printf(help, "\n* %s (%s), %s, default: %s",
-			                       name, purple_account_option_get_text(o),
-			                       "string", def);
-
-			break;
-
-		case PURPLE_PREF_INT:
-			def = g_strdup_printf("%d", purple_account_option_get_default_int(o));
-			eval = set_eval_int;
-
-			g_string_append_printf(help, "\n* %s (%s), %s, default: %s",
-			                       name, purple_account_option_get_text(o),
-			                       "integer", def);
-
-			break;
-
-		case PURPLE_PREF_BOOLEAN:
-			if (purple_account_option_get_default_bool(o)) {
-				def = g_strdup("true");
-			} else {
-				def = g_strdup("false");
-			}
-			eval = set_eval_bool;
-
-			g_string_append_printf(help, "\n* %s (%s), %s, default: %s",
-			                       name, purple_account_option_get_text(o),
-			                       "boolean", def);
-
-			break;
-
-		case PURPLE_PREF_STRING_LIST:
-			def = g_strdup(purple_account_option_get_default_list_value(o));
-
-			g_string_append_printf(help, "\n* %s (%s), %s, default: %s",
-			                       name, purple_account_option_get_text(o),
-			                       "list", def);
-			g_string_append(help, "\n  Possible values: ");
-
-			for (io = purple_account_option_get_list(o); io; io = io->next) {
-				PurpleKeyValuePair *kv = io->data;
-				opts = g_slist_append(opts, kv->value);
-				/* TODO: kv->value is not a char*, WTF? */
-				if (strcmp(kv->value, kv->key) != 0) {
-					g_string_append_printf(help, "%s (%s), ", (char *) kv->value, kv->key);
-				} else {
-					g_string_append_printf(help, "%s, ", (char *) kv->value);
-				}
-			}
-			g_string_truncate(help, help->len - 2);
-			eval = set_eval_list;
-			eval_data = opts;
-
-			break;
-
-		default:
-			/** No way to talk to the user right now, invent one when
-			this becomes important.
-			irc_rootmsg( acc->irc, "Setting with unknown type: %s (%d) Expect stuff to break..\n",
-			             name, purple_account_option_get_type( o ) );
-			*/
-			g_string_append_printf(help, "\n* [%s] UNSUPPORTED (type %d)",
-			                       name, purple_account_option_get_type(o));
-			name = NULL;
-		}
-
-		if (name != NULL) {
-			s = set_add(&acc->set, name, def, eval, acc);
-			s->flags |= ACC_SET_OFFLINE_ONLY;
-			s->eval_data = eval_data;
-			g_free(def);
-		}
-	}
-
-	g_snprintf(help_title, sizeof(help_title), "purple %s", (char *) acc->prpl->name);
-	help_add_mem(&global.help, help_title, help->str);
-	g_string_free(help, TRUE);
-
-	s = set_add(&acc->set, "display_name", NULL, set_eval_display_name, acc);
-	s->flags |= ACC_SET_ONLINE_ONLY;
-
-        s = set_add(&acc->set, "no_proxy", "false", set_eval_bool, acc);
-
-	if (pi->options & OPT_PROTO_MAIL_CHECK) {
-		s = set_add(&acc->set, "mail_notifications", "false", set_eval_bool, acc);
-		s->flags |= ACC_SET_OFFLINE_ONLY;
-
-		s = set_add(&acc->set, "mail_notifications_handle", NULL, NULL, acc);
-		s->flags |= ACC_SET_OFFLINE_ONLY | SET_NULL_OK;
-	}
-
-	if (strcmp(prpl->info->name, "Gadu-Gadu") == 0) {
-		s = set_add(&acc->set, "gg_sync_contacts", "true", set_eval_bool, acc);
-	}
-
-	if (g_strcmp0(prpl->info->id, "prpl-line") == 0) {
-		s = set_add(&acc->set, "line-auth-token", NULL, NULL, acc);
-		s->flags |= SET_HIDDEN;
-	}
-
-	/* Go through all away states to figure out if away/status messages
-	   are possible. */
-	pa = purple_account_new(acc->user, prpl_id);
-	for (st = purple_account_get_status_types(pa); st; st = st->next) {
-		PurpleStatusPrimitive prim = purple_status_type_get_primitive(st->data);
-
-		if (prim == PURPLE_STATUS_AVAILABLE) {
-			if (purple_status_type_get_attr(st->data, "message")) {
-				acc->flags |= ACC_FLAG_STATUS_MESSAGE;
-			}
-		} else if (prim != PURPLE_STATUS_OFFLINE) {
-			if (purple_status_type_get_attr(st->data, "message")) {
-				acc->flags |= ACC_FLAG_AWAY_MESSAGE;
-			}
-		}
-	}
-	purple_accounts_remove(pa);
-}
-
-static void purple_sync_settings(account_t *acc, PurpleAccount *pa)
-{
-	PurplePlugin *prpl = purple_plugins_find_with_id(pa->protocol_id);
-	PurplePluginProtocolInfo *pi = prpl->info->extra_info;
-	GList *i;
-
-	for (i = pi->protocol_options; i; i = i->next) {
-		PurpleAccountOption *o = i->data;
-		const char *name;
-		set_t *s;
-
-		name = purple_account_option_get_setting(o);
-		s = set_find(&acc->set, name);
-		if (s->value == NULL) {
-			continue;
-		}
-
-		switch (purple_account_option_get_type(o)) {
-		case PURPLE_PREF_STRING:
-		case PURPLE_PREF_STRING_LIST:
-			purple_account_set_string(pa, name, set_getstr(&acc->set, name));
-			break;
-
-		case PURPLE_PREF_INT:
-			purple_account_set_int(pa, name, set_getint(&acc->set, name));
-			break;
-
-		case PURPLE_PREF_BOOLEAN:
-			purple_account_set_bool(pa, name, set_getbool(&acc->set, name));
-			break;
-
-		default:
-			break;
-		}
-	}
-
-	if (pi->options & OPT_PROTO_MAIL_CHECK) {
-		purple_account_set_check_mail(pa, set_getbool(&acc->set, "mail_notifications"));
-	}
-
-	if (g_strcmp0(prpl->info->id, "prpl-line") == 0) {
-		const char *name = "line-auth-token";
-		purple_account_set_string(pa, name, set_getstr(&acc->set, name));
-	}
-
-        PurpleProxyInfo *proxyinfo = purple_account_get_proxy_info(pa);
-        if (!proxyinfo) {
-          proxyinfo = purple_proxy_info_new();
-        }
-        g_return_if_fail (proxyinfo);
-        purple_proxy_info_set_type(proxyinfo, set_getbool(&acc->set, "no_proxy") ? PURPLE_PROXY_NONE : PURPLE_PROXY_USE_GLOBAL);
-        purple_account_set_proxy_info(pa, proxyinfo);
-}
-
-static void purple_login(account_t *acc)
-{
-	struct im_connection *ic = imcb_new(acc);
-	struct purple_data *pd;
-
-	if ((local_bee != NULL && local_bee != acc->bee) ||
-	    (global.conf->runmode == RUNMODE_DAEMON && !getenv("BITLBEE_DEBUG"))) {
-		imcb_error(ic,  "Daemon mode detected. Do *not* try to use libpurple in daemon mode! "
-		           "Please use inetd or ForkDaemon mode instead.");
-		imc_logout(ic, FALSE);
-		return;
-	}
-	local_bee = acc->bee;
-
-	/* For now this is needed in the _connected() handlers if using
-	   GLib event handling, to make sure we're not handling events
-	   on dead connections. */
-	purple_connections = g_slist_prepend(purple_connections, ic);
-
-	ic->proto_data = pd = g_new0(struct purple_data, 1);
-	pd->account = purple_account_new(acc->user, acc->prpl->data);
-	pd->input_requests = g_hash_table_new_full(g_direct_hash, g_direct_equal,
-	                                           NULL, g_free);
-	pd->next_request_id = 0;
-	purple_account_set_password(pd->account, acc->pass);
-	purple_sync_settings(acc, pd->account);
-
-	if (purple_account_should_set_nick(acc)) {
-		pd->flags = PURPLE_OPT_SHOULD_SET_NICK;
-	}
-
-	purple_account_set_enabled(pd->account, "BitlBee", TRUE);
-
-	if (set_getbool(&acc->set, "mail_notifications") && set_getstr(&acc->set, "mail_notifications_handle")) {
-		imcb_add_buddy(ic, set_getstr(&acc->set, "mail_notifications_handle"), NULL);
-	}
-}
-
-static void purple_logout(struct im_connection *ic)
-{
-	struct purple_data *pd = ic->proto_data;
-
-	if (!pd) {
-		return;
-	}
-
-	while (ic->groupchats) {
-		imcb_chat_free(ic->groupchats->data);
-	}
-
-	if (pd->filetransfers) {
-		purple_transfer_cancel_all(ic);
-	}
-
-	purple_account_set_enabled(pd->account, "BitlBee", FALSE);
-	purple_connections = g_slist_remove(purple_connections, ic);
-	purple_accounts_remove(pd->account);
-	imcb_chat_list_free(ic);
-	g_free(pd->chat_list_server);
-	g_hash_table_destroy(pd->input_requests);
-	g_free(pd);
-}
-
-static int purple_buddy_msg(struct im_connection *ic, char *who, char *message, int flags)
-{
-	PurpleConversation *conv;
-	struct purple_data *pd = ic->proto_data;
-
-	if (!strncmp(who, PURPLE_REQUEST_HANDLE, sizeof(PURPLE_REQUEST_HANDLE) - 1)) {
-		guint request_id = atoi(who + sizeof(PURPLE_REQUEST_HANDLE));
-		purple_request_input_callback(request_id, ic, message, who);
-		return 1;
-	}
-
-	if ((conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM,
-	                                                  who, pd->account)) == NULL) {
-		conv = purple_conversation_new(PURPLE_CONV_TYPE_IM,
-		                               pd->account, who);
-	}
-
-	purple_conv_im_send(purple_conversation_get_im_data(conv), message);
-
-	return 1;
-}
-
-static GList *purple_away_states(struct im_connection *ic)
-{
-	struct purple_data *pd = ic->proto_data;
-	GList *st, *ret = NULL;
-
-	for (st = purple_account_get_status_types(pd->account); st; st = st->next) {
-		PurpleStatusPrimitive prim = purple_status_type_get_primitive(st->data);
-		if (prim != PURPLE_STATUS_AVAILABLE && prim != PURPLE_STATUS_OFFLINE) {
-			ret = g_list_append(ret, (void *) purple_status_type_get_name(st->data));
-		}
-	}
-
-	return ret;
-}
-
-static void purple_set_away(struct im_connection *ic, char *state_txt, char *message)
-{
-	struct purple_data *pd = ic->proto_data;
-	GList *status_types = purple_account_get_status_types(pd->account), *st;
-	PurpleStatusType *pst = NULL;
-	GList *args = NULL;
-
-	for (st = status_types; st; st = st->next) {
-		pst = st->data;
-
-		if (state_txt == NULL &&
-		    purple_status_type_get_primitive(pst) == PURPLE_STATUS_AVAILABLE) {
-			break;
-		}
-
-		if (state_txt != NULL &&
-		    g_strcasecmp(state_txt, purple_status_type_get_name(pst)) == 0) {
-			break;
-		}
-	}
-
-	if (message && purple_status_type_get_attr(pst, "message")) {
-		args = g_list_append(args, "message");
-		args = g_list_append(args, message);
-	}
-
-	purple_account_set_status_list(pd->account,
-	                               st ? purple_status_type_get_id(pst) : "away",
-	                               TRUE, args);
-
-	g_list_free(args);
-}
-
-static char *set_eval_display_name(set_t *set, char *value)
-{
-	account_t *acc = set->data;
-	struct im_connection *ic = acc->ic;
-
-	if (ic) {
-		imcb_log(ic, "Changing display_name not currently supported with libpurple!");
-	}
-
-	return NULL;
-}
-
-/* Bad bad gadu-gadu, not saving buddy list by itself */
-static void purple_gg_buddylist_export(PurpleConnection *gc)
-{
-	struct im_connection *ic = purple_ic_by_gc(gc);
-
-	if (set_getstr(&ic->acc->set, "gg_sync_contacts")) {
-		GList *actions = gc->prpl->info->actions(gc->prpl, gc);
-		GList *p;
-		for (p = g_list_first(actions); p; p = p->next) {
-			if (((PurplePluginAction *) p->data) &&
-			    purple_menu_cmp(((PurplePluginAction *) p->data)->label,
-			                    "Upload buddylist to Server") == 0) {
-				PurplePluginAction action;
-				action.plugin = gc->prpl;
-				action.context = gc;
-				action.user_data = NULL;
-				((PurplePluginAction *) p->data)->callback(&action);
-				break;
-			}
-		}
-		g_list_free(actions);
-	}
-}
-
-static void purple_gg_buddylist_import(PurpleConnection *gc)
-{
-	struct im_connection *ic = purple_ic_by_gc(gc);
-
-	if (set_getstr(&ic->acc->set, "gg_sync_contacts")) {
-		GList *actions = gc->prpl->info->actions(gc->prpl, gc);
-		GList *p;
-		for (p = g_list_first(actions); p; p = p->next) {
-			if (((PurplePluginAction *) p->data) &&
-			    purple_menu_cmp(((PurplePluginAction *) p->data)->label,
-			                    "Download buddylist from Server") == 0) {
-				PurplePluginAction action;
-				action.plugin = gc->prpl;
-				action.context = gc;
-				action.user_data = NULL;
-				((PurplePluginAction *) p->data)->callback(&action);
-				break;
-			}
-		}
-		g_list_free(actions);
-	}
-}
-
-static void purple_add_buddy(struct im_connection *ic, char *who, char *group)
-{
-	PurpleBuddy *pb;
-	PurpleGroup *pg = NULL;
-	struct purple_data *pd = ic->proto_data;
-
-	if (group && !(pg = purple_find_group(group))) {
-		pg = purple_group_new(group);
-		purple_blist_add_group(pg, NULL);
-	}
-
-	pb = purple_buddy_new(pd->account, who, NULL);
-	purple_blist_add_buddy(pb, NULL, pg, NULL);
-	purple_account_add_buddy(pd->account, pb);
-
-	purple_gg_buddylist_export(pd->account->gc);
-}
-
-static void purple_remove_buddy(struct im_connection *ic, char *who, char *group)
-{
-	PurpleBuddy *pb;
-	struct purple_data *pd = ic->proto_data;
-
-	pb = purple_find_buddy(pd->account, who);
-	if (pb != NULL) {
-		PurpleGroup *group;
-
-		group = purple_buddy_get_group(pb);
-		purple_account_remove_buddy(pd->account, pb, group);
-
-		purple_blist_remove_buddy(pb);
-	}
-
-	purple_gg_buddylist_export(pd->account->gc);
-}
-
-static void purple_add_permit(struct im_connection *ic, char *who)
-{
-	struct purple_data *pd = ic->proto_data;
-
-	purple_privacy_permit_add(pd->account, who, FALSE);
-}
-
-static void purple_add_deny(struct im_connection *ic, char *who)
-{
-	struct purple_data *pd = ic->proto_data;
-
-	purple_privacy_deny_add(pd->account, who, FALSE);
-}
-
-static void purple_rem_permit(struct im_connection *ic, char *who)
-{
-	struct purple_data *pd = ic->proto_data;
-
-	purple_privacy_permit_remove(pd->account, who, FALSE);
-}
-
-static void purple_rem_deny(struct im_connection *ic, char *who)
-{
-	struct purple_data *pd = ic->proto_data;
-
-	purple_privacy_deny_remove(pd->account, who, FALSE);
-}
-
-static void purple_get_info(struct im_connection *ic, char *who)
-{
-	struct purple_data *pd = ic->proto_data;
-
-	serv_get_info(purple_account_get_connection(pd->account), who);
-}
-
-static void purple_keepalive(struct im_connection *ic)
-{
-}
-
-static int purple_send_typing(struct im_connection *ic, char *who, int flags)
-{
-	PurpleTypingState state = PURPLE_NOT_TYPING;
-	struct purple_data *pd = ic->proto_data;
-
-	if (flags & OPT_TYPING) {
-		state = PURPLE_TYPING;
-	} else if (flags & OPT_THINKING) {
-		state = PURPLE_TYPED;
-	}
-
-	serv_send_typing(purple_account_get_connection(pd->account), who, state);
-
-	return 1;
-}
-
-static void purple_chat_msg(struct groupchat *gc, char *message, int flags)
-{
-	PurpleConversation *pc = gc->data;
-
-	purple_conv_chat_send(purple_conversation_get_chat_data(pc), message);
-}
-
-struct groupchat *purple_chat_with(struct im_connection *ic, char *who)
-{
-	/* No, "of course" this won't work this way. Or in fact, it almost
-	   does, but it only lets you send msgs to it, you won't receive
-	   any. Instead, we have to click the virtual menu item.
-	PurpleAccount *pa = ic->proto_data;
-	PurpleConversation *pc;
-	PurpleConvChat *pcc;
-	struct groupchat *gc;
-
-	gc = imcb_chat_new( ic, "BitlBee-libpurple groupchat" );
-	gc->data = pc = purple_conversation_new( PURPLE_CONV_TYPE_CHAT, pa, "BitlBee-libpurple groupchat" );
-	pc->ui_data = gc;
-
-	pcc = PURPLE_CONV_CHAT( pc );
-	purple_conv_chat_add_user( pcc, ic->acc->user, "", 0, TRUE );
-	purple_conv_chat_invite_user( pcc, who, "Please join my chat", FALSE );
-	//purple_conv_chat_add_user( pcc, who, "", 0, TRUE );
-	*/
-
-	/* There went my nice afternoon. :-( */
-
-	struct purple_data *pd = ic->proto_data;
-	PurplePlugin *prpl = purple_plugins_find_with_id(pd->account->protocol_id);
-	PurplePluginProtocolInfo *pi = prpl->info->extra_info;
-	PurpleBuddy *pb = purple_find_buddy(pd->account, who);
-	PurpleMenuAction *mi;
-	GList *menu;
-
-	void (*callback)(PurpleBlistNode *, gpointer); /* FFFFFFFFFFFFFUUUUUUUUUUUUUU */
-
-	if (!pb || !pi || !pi->blist_node_menu) {
-		return NULL;
-	}
-
-	menu = pi->blist_node_menu(&pb->node);
-	while (menu) {
-		mi = menu->data;
-		if (purple_menu_cmp(mi->label, "initiate chat") ||
-		    purple_menu_cmp(mi->label, "initiate conference")) {
-			break;
-		}
-		menu = menu->next;
-	}
-
-	if (menu == NULL) {
-		return NULL;
-	}
-
-	/* Call the fucker. */
-	callback = (void *) mi->callback;
-	callback(&pb->node, mi->data);
-
-	return NULL;
-}
-
-void purple_chat_invite(struct groupchat *gc, char *who, char *message)
-{
-	PurpleConversation *pc = gc->data;
-	PurpleConvChat *pcc = PURPLE_CONV_CHAT(pc);
-	struct purple_data *pd = gc->ic->proto_data;
-
-	serv_chat_invite(purple_account_get_connection(pd->account),
-	                 purple_conv_chat_get_id(pcc),
-	                 message && *message ? message : "Please join my chat",
-	                 who);
-}
-
-void purple_chat_set_topic(struct groupchat *gc, char *topic)
-{
-	PurpleConversation *pc = gc->data;
-	PurpleConvChat *pcc = PURPLE_CONV_CHAT(pc);
-	struct purple_data *pd = gc->ic->proto_data;
-	PurplePlugin *prpl = purple_plugins_find_with_id(pd->account->protocol_id);
-	PurplePluginProtocolInfo *pi = prpl->info->extra_info;
-
-	if (pi->set_chat_topic) {
-		pi->set_chat_topic(purple_account_get_connection(pd->account),
-		                   purple_conv_chat_get_id(pcc),
-		                   topic);
-	}
-}
-
-void purple_chat_kick(struct groupchat *gc, char *who, const char *message)
-{
-	PurpleConversation *pc = gc->data;
-	char *str = g_strdup_printf("kick %s %s", who, message);
-
-	purple_conversation_do_command(pc, str, NULL, NULL);
-	g_free(str);
-}
-
-void purple_chat_leave(struct groupchat *gc)
-{
-	PurpleConversation *pc = gc->data;
-
-	purple_conversation_destroy(pc);
-}
-
-struct groupchat *purple_chat_join(struct im_connection *ic, const char *room, const char *nick, const char *password,
-                                   set_t **sets)
-{
-	struct purple_data *pd = ic->proto_data;
-	PurplePlugin *prpl = purple_plugins_find_with_id(pd->account->protocol_id);
-	PurplePluginProtocolInfo *pi = prpl->info->extra_info;
-	GHashTable *chat_hash;
-	PurpleConversation *conv;
-	struct groupchat *gc;
-	GList *info, *l;
-	GString *missing_settings = NULL;
-
-	if (!pi->chat_info || !pi->chat_info_defaults ||
-	    !(info = pi->chat_info(purple_account_get_connection(pd->account)))) {
-		imcb_error(ic, "Joining chatrooms not supported by this protocol");
-		return NULL;
-	}
-
-	if ((conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT,
-	                                                  room, pd->account))) {
-		purple_conversation_destroy(conv);
-	}
-
-	chat_hash = pi->chat_info_defaults(
-	        purple_account_get_connection(pd->account), room
-	);
-
-	for (l = info; l; l = l->next) {
-		struct proto_chat_entry *pce = l->data;
-
-		if (strcmp(pce->identifier, "handle") == 0) {
-			g_hash_table_replace(chat_hash, "handle", g_strdup(nick));
-		} else if (strcmp(pce->identifier, "password") == 0) {
-			g_hash_table_replace(chat_hash, "password", g_strdup(password));
-		} else if (strcmp(pce->identifier, "passwd") == 0) {
-			g_hash_table_replace(chat_hash, "passwd", g_strdup(password));
-		} else {
-			char *key, *value;
-
-			key = g_strdup_printf("purple_%s", pce->identifier);
-			str_reject_chars(key, " -", '_');
-
-			if ((value = set_getstr(sets, key))) {
-				/* sync from bitlbee to the prpl */
-				g_hash_table_replace(chat_hash, (char *) pce->identifier, g_strdup(value));
-			} else if ((value = g_hash_table_lookup(chat_hash, pce->identifier))) {
-				/* if the bitlbee one was empty, sync from prpl to bitlbee */
-				set_setstr(sets, key, value);
-			}
-
-			g_free(key);
-		}
-
-		if (pce->required && !g_hash_table_lookup(chat_hash, pce->identifier)) {
-			if (!missing_settings) {
-				missing_settings = g_string_sized_new(32);
-			}
-			g_string_append_printf(missing_settings, "%s, ", pce->identifier);
-		}
-
-		g_free(pce);
-	}
-
-	g_list_free(info);
-
-	if (missing_settings) {
-		/* remove the ", " from the end */
-		g_string_truncate(missing_settings, missing_settings->len - 2);
-
-		imcb_error(ic, "Can't join %s. The following settings are required: %s", room, missing_settings->str);
-
-		g_string_free(missing_settings, TRUE);
-		g_hash_table_destroy(chat_hash);
-		return NULL;
-	}
-
-	/* do this before serv_join_chat to handle cases where prplcb_conv_new is called immediately (not async) */
-	gc = imcb_chat_new(ic, room);
-
-	serv_join_chat(purple_account_get_connection(pd->account), chat_hash);
-
-	g_hash_table_destroy(chat_hash);
-
-	return gc;
-}
-
-void purple_chat_list(struct im_connection *ic, const char *server)
-{
-	PurpleRoomlist *list;
-	struct purple_data *pd = ic->proto_data;
-	PurplePlugin *prpl = purple_plugins_find_with_id(pd->account->protocol_id);
-	PurplePluginProtocolInfo *pi = prpl->info->extra_info;
-
-	if (!pi || !pi->roomlist_get_list) {
-		imcb_log(ic, "Room listing unsupported by this purple plugin");
-		return;
-	}
-
-	g_free(pd->chat_list_server);
-	pd->chat_list_server = (server && *server) ? g_strdup(server) : NULL;
-
-	list = purple_roomlist_get_list(pd->account->gc);
-
-	if (list) {
-		struct purple_roomlist_data *rld = list->ui_data;
-		rld->initialized = TRUE;
-
-		purple_roomlist_ref(list);
-	}
-}
-
-/* handles either prpl->chat_(add|free)_settings depending on the value of 'add' */
-static void purple_chat_update_settings(account_t *acc, set_t **head, gboolean add)
-{
-	PurplePlugin *prpl = purple_plugins_find_with_id((char *) acc->prpl->data);
-	PurplePluginProtocolInfo *pi = prpl->info->extra_info;
-	GList *info, *l;
-
-	if (!pi->chat_info || !pi->chat_info_defaults) {
-		return;
-	}
-
-	/* hack / leap of faith: pass a NULL here because we don't have a connection yet.
-	 * i reviewed all the built-in prpls and a bunch of third-party ones and none
-	 * of them seem to need this parameter at all, so... i hope it never crashes */
-	info = pi->chat_info(NULL);
-
-	for (l = info; l; l = l->next) {
-		struct proto_chat_entry *pce = l->data;
-		char *key;
-
-		if (strcmp(pce->identifier, "handle") == 0 ||
-		    strcmp(pce->identifier, "password") == 0 ||
-		    strcmp(pce->identifier, "passwd") == 0) {
-			/* skip these, they are handled above */
-			g_free(pce);
-			continue;
-		}
-
-		key = g_strdup_printf("purple_%s", pce->identifier);
-		str_reject_chars(key, " -", '_');
-
-		if (add) {
-			set_add(head, key, NULL, NULL, NULL);
-		} else {
-			set_del(head, key);
-		}
-
-		g_free(key);
-		g_free(pce);
-	}
-
-	g_list_free(NULL);
-	g_list_free(info);
-}
-
-static void purple_chat_add_settings(account_t *acc, set_t **head)
-{
-	purple_chat_update_settings(acc, head, TRUE);
-}
-
-static void purple_chat_free_settings(account_t *acc, set_t **head)
-{
-	purple_chat_update_settings(acc, head, FALSE);
-}
-
-void purple_transfer_request(struct im_connection *ic, file_transfer_t *ft, char *handle);
-
-static void purple_ui_init();
-
-GHashTable *prplcb_ui_info()
-{
-	static GHashTable *ret;
-
-	if (ret == NULL) {
-		ret = g_hash_table_new(g_str_hash, g_str_equal);
-		g_hash_table_insert(ret, "name", "BitlBee");
-		g_hash_table_insert(ret, "version", BITLBEE_VERSION);
-	}
-
-	return ret;
-}
-
-static PurpleCoreUiOps bee_core_uiops =
-{
-	NULL,                      /* ui_prefs_init */
-	NULL,                      /* debug_ui_init */
-	purple_ui_init,            /* ui_init */
-	NULL,                      /* quit */
-	prplcb_ui_info,            /* get_ui_info */
-};
-
-static void prplcb_conn_progress(PurpleConnection *gc, const char *text, size_t step, size_t step_count)
-{
-	struct im_connection *ic = purple_ic_by_gc(gc);
-
-	imcb_log(ic, "%s", text);
-}
-
-static void prplcb_conn_connected(PurpleConnection *gc)
-{
-	struct im_connection *ic = purple_ic_by_gc(gc);
-	struct purple_data *pd = ic->proto_data;
-	const char *dn, *token;
-	set_t *s;
-
-	imcb_connected(ic);
-
-	if ((dn = purple_connection_get_display_name(gc)) &&
-	    (s = set_find(&ic->acc->set, "display_name"))) {
-		g_free(s->value);
-		s->value = g_strdup(dn);
-	}
-
-	// user list needs to be requested for Gadu-Gadu
-	purple_gg_buddylist_import(gc);
-
-	/* more awful hacks, because clearly we didn't have enough of those */
-	if ((s = set_find(&ic->acc->set, "line-auth-token")) &&
-	    (token = purple_account_get_string(pd->account, "line-auth-token", NULL))) {
-		g_free(s->value);
-		s->value = g_strdup(token);
-	}
-
-	ic->flags |= OPT_DOES_HTML;
-}
-
-static void prplcb_conn_disconnected(PurpleConnection *gc)
-{
-	struct im_connection *ic = purple_ic_by_gc(gc);
-
-	if (ic != NULL) {
-		imc_logout(ic, !gc->wants_to_die);
-	}
-}
-
-static void prplcb_conn_notice(PurpleConnection *gc, const char *text)
-{
-	struct im_connection *ic = purple_ic_by_gc(gc);
-
-	if (ic != NULL) {
-		imcb_log(ic, "%s", text);
-	}
-}
-
-static void prplcb_conn_report_disconnect_reason(PurpleConnection *gc, PurpleConnectionError reason, const char *text)
-{
-	struct im_connection *ic = purple_ic_by_gc(gc);
-
-	/* PURPLE_CONNECTION_ERROR_NAME_IN_USE means concurrent login,
-	   should probably handle that. */
-	if (ic != NULL) {
-		imcb_error(ic, "%s", text);
-	}
-}
-
-static PurpleConnectionUiOps bee_conn_uiops =
-{
-	prplcb_conn_progress,                    /* connect_progress */
-	prplcb_conn_connected,                   /* connected */
-	prplcb_conn_disconnected,                /* disconnected */
-	prplcb_conn_notice,                      /* notice */
-	NULL,                                    /* report_disconnect */
-	NULL,                                    /* network_connected */
-	NULL,                                    /* network_disconnected */
-	prplcb_conn_report_disconnect_reason,    /* report_disconnect_reason */
-};
-
-static void prplcb_blist_update(PurpleBuddyList *list, PurpleBlistNode *node)
-{
-	if (node->type == PURPLE_BLIST_BUDDY_NODE) {
-		PurpleBuddy *bud = (PurpleBuddy *) node;
-		PurpleGroup *group = purple_buddy_get_group(bud);
-		struct im_connection *ic = purple_ic_by_pa(bud->account);
-		struct purple_data *pd = ic->proto_data;
-		PurpleStatus *as;
-		int flags = 0;
-		char *alias = NULL;
-
-		if (ic == NULL) {
-			return;
-		}
-
-		alias = bud->server_alias ? : bud->alias;
-
-		if (alias) {
-			imcb_rename_buddy(ic, bud->name, alias);
-			if (pd->flags & PURPLE_OPT_SHOULD_SET_NICK) {
-				imcb_buddy_nick_change(ic, bud->name, alias);
-			}
-		}
-
-		if (group) {
-			imcb_add_buddy(ic, bud->name, purple_group_get_name(group));
-		}
-
-		flags |= purple_presence_is_online(bud->presence) ? OPT_LOGGED_IN : 0;
-		flags |= purple_presence_is_available(bud->presence) ? 0 : OPT_AWAY;
-
-		as = purple_presence_get_active_status(bud->presence);
-
-		imcb_buddy_status(ic, bud->name, flags, purple_status_get_name(as),
-		                  purple_status_get_attr_string(as, "message"));
-
-		imcb_buddy_times(ic, bud->name,
-		                 purple_presence_get_login_time(bud->presence),
-		                 purple_presence_get_idle_time(bud->presence));
-	}
-}
-
-static void prplcb_blist_new(PurpleBlistNode *node)
-{
-	if (node->type == PURPLE_BLIST_BUDDY_NODE) {
-		PurpleBuddy *bud = (PurpleBuddy *) node;
-		struct im_connection *ic = purple_ic_by_pa(bud->account);
-
-		if (ic == NULL) {
-			return;
-		}
-
-		imcb_add_buddy(ic, bud->name, NULL);
-
-		prplcb_blist_update(NULL, node);
-	}
-}
-
-static void prplcb_blist_remove(PurpleBuddyList *list, PurpleBlistNode *node)
-{
-/*
-        PurpleBuddy *bud = (PurpleBuddy*) node;
-
-        if( node->type == PURPLE_BLIST_BUDDY_NODE )
-        {
-                struct im_connection *ic = purple_ic_by_pa( bud->account );
-
-                if( ic == NULL )
-                        return;
-
-                imcb_remove_buddy( ic, bud->name, NULL );
-        }
-*/
-}
-
-static PurpleBlistUiOps bee_blist_uiops =
-{
-	NULL,                      /* new_list */
-	prplcb_blist_new,          /* new_node */
-	NULL,                      /* show */
-	prplcb_blist_update,       /* update */
-	prplcb_blist_remove,       /* remove */
-};
-
-void prplcb_conv_new(PurpleConversation *conv)
-{
-	if (conv->type == PURPLE_CONV_TYPE_CHAT) {
-		struct im_connection *ic = purple_ic_by_pa(conv->account);
-		struct groupchat *gc;
-
-		gc = bee_chat_by_title(ic->bee, ic, conv->name);
-
-		if (!gc) {
-			gc = imcb_chat_new(ic, conv->name);
-			if (conv->title != NULL) {
-				imcb_chat_name_hint(gc, conv->title);
-			}
-		}
-
-		/* don't set the topic if it's just the name */
-		if (conv->title != NULL && strcmp(conv->name, conv->title) != 0) {
-			imcb_chat_topic(gc, NULL, conv->title, 0);
-		}
-
-		conv->ui_data = gc;
-		gc->data = conv;
-
-		/* libpurple brokenness: Whatever. Show that we join right away,
-		   there's no clear "This is you!" signaling in _add_users so
-		   don't even try. */
-		imcb_chat_add_buddy(gc, gc->ic->acc->user);
-	}
-}
-
-void prplcb_conv_free(PurpleConversation *conv)
-{
-	struct groupchat *gc = conv->ui_data;
-
-	imcb_chat_free(gc);
-}
-
-void prplcb_conv_add_users(PurpleConversation *conv, GList *cbuddies, gboolean new_arrivals)
-{
-	struct groupchat *gc = conv->ui_data;
-	GList *b;
-
-	for (b = cbuddies; b; b = b->next) {
-		PurpleConvChatBuddy *pcb = b->data;
-
-		imcb_chat_add_buddy(gc, pcb->name);
-	}
-}
-
-void prplcb_conv_del_users(PurpleConversation *conv, GList *cbuddies)
-{
-	struct groupchat *gc = conv->ui_data;
-	GList *b;
-
-	for (b = cbuddies; b; b = b->next) {
-		imcb_chat_remove_buddy(gc, b->data, "");
-	}
-}
-
-/* Generic handler for IM or chat messages, covers write_chat, write_im and write_conv */
-static void handle_conv_msg(PurpleConversation *conv, const char *who, const char *message_, guint32 bee_flags, time_t mtime)
-{
-	struct im_connection *ic = purple_ic_by_pa(conv->account);
-	struct groupchat *gc = conv->ui_data;
-	char *message = g_strdup(message_);
-	PurpleBuddy *buddy;
-
-	buddy = purple_find_buddy(conv->account, who);
-	if (buddy != NULL) {
-		who = purple_buddy_get_name(buddy);
-	}
-
-	if (conv->type == PURPLE_CONV_TYPE_IM) {
-		imcb_buddy_msg(ic, who, message, bee_flags, mtime);
-	} else if (gc) {
-		imcb_chat_msg(gc, who, message, bee_flags, mtime);
-	}
-
-	g_free(message);
-}
-
-/* Handles write_im and write_chat. Removes echoes of locally sent messages.
- *
- * PURPLE_MESSAGE_DELAYED is used for chat backlogs - if a message has both
- * that flag and _SEND, it's a self-message from before joining the channel.
- * Those are safe to display. The rest (with just _SEND) may be echoes. */
-static void prplcb_conv_msg(PurpleConversation *conv, const char *who, const char *message, PurpleMessageFlags flags, time_t mtime)
-{
-	if ((!(flags & PURPLE_MESSAGE_SEND)) ||
-	    (flags & PURPLE_MESSAGE_DELAYED) ||
-	    (flags & PURPLE_MESSAGE_REMOTE_SEND)
-	) {
-		handle_conv_msg(conv, who, message, (flags & PURPLE_MESSAGE_SEND) ? OPT_SELFMESSAGE : 0, mtime);
-	}
-}
-
-/* Handles write_conv. Only passes self messages from other locations through.
- * That is, only writes of PURPLE_MESSAGE_SEND.
- * There are more events which might be handled in the future, but some are tricky.
- * (images look like <img id="123">, what do i do with that?) */
-static void prplcb_conv_write(PurpleConversation *conv, const char *who, const char *alias, const char *message,
-                              PurpleMessageFlags flags, time_t mtime)
-{
-	if (flags & PURPLE_MESSAGE_SEND) {
-		handle_conv_msg(conv, who, message, OPT_SELFMESSAGE, mtime);
-	}
-}
-
-/* No, this is not a ui_op but a signal. */
-static void prplcb_buddy_typing(PurpleAccount *account, const char *who, gpointer null)
-{
-	PurpleConversation *conv;
-	PurpleConvIm *im;
-	int state;
-
-	if ((conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, who, account)) == NULL) {
-		return;
-	}
-
-	im = PURPLE_CONV_IM(conv);
-	switch (purple_conv_im_get_typing_state(im)) {
-	case PURPLE_TYPING:
-		state = OPT_TYPING;
-		break;
-	case PURPLE_TYPED:
-		state = OPT_THINKING;
-		break;
-	default:
-		state = 0;
-	}
-
-	imcb_buddy_typing(purple_ic_by_pa(account), who, state);
-}
-
-static PurpleConversationUiOps bee_conv_uiops =
-{
-	prplcb_conv_new,           /* create_conversation  */
-	prplcb_conv_free,          /* destroy_conversation */
-	prplcb_conv_msg,           /* write_chat           */
-	prplcb_conv_msg,           /* write_im             */
-	prplcb_conv_write,         /* write_conv           */
-	prplcb_conv_add_users,     /* chat_add_users       */
-	NULL,                      /* chat_rename_user     */
-	prplcb_conv_del_users,     /* chat_remove_users    */
-	NULL,                      /* chat_update_user     */
-	NULL,                      /* present              */
-	NULL,                      /* has_focus            */
-	NULL,                      /* custom_smiley_add    */
-	NULL,                      /* custom_smiley_write  */
-	NULL,                      /* custom_smiley_close  */
-	NULL,                      /* send_confirm         */
-};
-
-struct prplcb_request_action_data {
-	void *user_data, *bee_data;
-	PurpleRequestActionCb yes, no;
-	int yes_i, no_i;
-};
-
-static void prplcb_request_action_yes(void *data)
-{
-	struct prplcb_request_action_data *pqad = data;
-
-	if (pqad->yes) {
-		pqad->yes(pqad->user_data, pqad->yes_i);
-	}
-}
-
-static void prplcb_request_action_no(void *data)
-{
-	struct prplcb_request_action_data *pqad = data;
-
-	if (pqad->no) {
-		pqad->no(pqad->user_data, pqad->no_i);
-	}
-}
-
-/* q->free() callback from query_del()*/
-static void prplcb_request_action_free(void *data)
-{
-	struct prplcb_request_action_data *pqad = data;
-
-	pqad->bee_data = NULL;
-	purple_request_close(PURPLE_REQUEST_ACTION, pqad);
-}
-
-static void *prplcb_request_action(const char *title, const char *primary, const char *secondary,
-                                   int default_action, PurpleAccount *account, const char *who,
-                                   PurpleConversation *conv, void *user_data, size_t action_count,
-                                   va_list actions)
-{
-	struct prplcb_request_action_data *pqad;
-	int i;
-	char *q;
-
-	pqad = g_new0(struct prplcb_request_action_data, 1);
-
-	for (i = 0; i < action_count; i++) {
-		char *caption;
-		void *fn;
-
-		caption = va_arg(actions, char*);
-		fn = va_arg(actions, void*);
-
-		if (strstr(caption, "Accept") || strstr(caption, "OK")) {
-			pqad->yes = fn;
-			pqad->yes_i = i;
-		} else if (strstr(caption, "Reject") || strstr(caption, "Cancel")) {
-			pqad->no = fn;
-			pqad->no_i = i;
-		}
-	}
-
-	pqad->user_data = user_data;
-
-	/* TODO: IRC stuff here :-( */
-	q = g_strdup_printf("Request: %s\n\n%s\n\n%s", title, primary, secondary);
-	pqad->bee_data = query_add(local_bee->ui_data, purple_ic_by_pa(account), q,
-	                           prplcb_request_action_yes, prplcb_request_action_no,
-	                           prplcb_request_action_free, pqad);
-
-	g_free(q);
-
-	return pqad;
-}
-
-/* So it turns out some requests have no account context at all, because
- * libpurple hates us. This means that query_del_by_conn() won't remove those
- * on logout, and will segfault if the user replies. That's why this exists.
- */
-static void prplcb_close_request(PurpleRequestType type, void *data)
-{
-	struct prplcb_request_action_data *pqad;
-	struct request_input_data *ri;
-	struct purple_data *pd;
-
-	if (!data) {
-		return;
-	}
-
-	switch (type) {
-	case PURPLE_REQUEST_ACTION:
-		pqad = data;
-		/* if this is null, it's because query_del was run already */
-		if (pqad->bee_data) {
-			query_del(local_bee->ui_data, pqad->bee_data);
-		}
-		g_free(pqad);
-		break;
-	case PURPLE_REQUEST_INPUT:
-		ri = data;
-		pd = ri->ic->proto_data;
-		imcb_remove_buddy(ri->ic, ri->buddy, NULL);
-		g_free(ri->buddy);
-		g_hash_table_remove(pd->input_requests, GUINT_TO_POINTER(ri->id));
-		break;
-	default:
-		g_free(data);
-		break;
-	}
-
-}
-
-void* prplcb_request_input(const char *title, const char *primary,
-        const char *secondary, const char *default_value, gboolean multiline,
-        gboolean masked, gchar *hint, const char *ok_text, GCallback ok_cb,
-        const char *cancel_text, GCallback cancel_cb, PurpleAccount *account,
-        const char *who, PurpleConversation *conv, void *user_data)
-{
-	struct im_connection *ic = purple_ic_by_pa(account);
-	struct purple_data *pd = ic->proto_data;
-	struct request_input_data *ri;
-	guint id;
-
-	/* hack so that jabber's chat list doesn't ask for conference server twice */
-	if (pd->chat_list_server && title && g_strcmp0(title, "Enter a Conference Server") == 0) {
-		((ri_callback_t) ok_cb)(user_data, pd->chat_list_server);
-		g_free(pd->chat_list_server);
-		pd->chat_list_server = NULL;
-		return NULL;
-	}
-
-	id = pd->next_request_id++;
-	ri = g_new0(struct request_input_data, 1);
-
-	ri->id = id;
-	ri->ic = ic;
-	ri->buddy = g_strdup_printf("%s_%u", PURPLE_REQUEST_HANDLE, id);
-	ri->data_callback = (ri_callback_t) ok_cb;
-	ri->user_data = user_data;
-	g_hash_table_insert(pd->input_requests, GUINT_TO_POINTER(id), ri);
-
-	imcb_add_buddy(ic, ri->buddy, NULL);
-
-	if (title && *title) {
-		imcb_buddy_msg(ic, ri->buddy, title, 0, 0);
-	}
-
-	if (primary && *primary) {
-		imcb_buddy_msg(ic, ri->buddy, primary, 0, 0);
-	}
-
-	if (secondary && *secondary) {
-		imcb_buddy_msg(ic, ri->buddy, secondary, 0, 0);
-	}
-
-	return ri;
-}
-
-void purple_request_input_callback(guint id, struct im_connection *ic,
-                                   const char *message, const char *who)
-{
-	struct purple_data *pd = ic->proto_data;
-	struct request_input_data *ri;
-
-	if (!(ri = g_hash_table_lookup(pd->input_requests, GUINT_TO_POINTER(id)))) {
-		return;
-	}
-
-	ri->data_callback(ri->user_data, message);
-
-	purple_request_close(PURPLE_REQUEST_INPUT, ri);
-}
-
-
-static PurpleRequestUiOps bee_request_uiops =
-{
-	prplcb_request_input,      /* request_input */
-	NULL,                      /* request_choice */
-	prplcb_request_action,     /* request_action */
-	NULL,                      /* request_fields */
-	NULL,                      /* request_file */
-	prplcb_close_request,      /* close_request */
-	NULL,                      /* request_folder */
-};
-
-static void prplcb_privacy_permit_added(PurpleAccount *account, const char *name)
-{
-	struct im_connection *ic = purple_ic_by_pa(account);
-
-	if (!g_slist_find_custom(ic->permit, name, (GCompareFunc) ic->acc->prpl->handle_cmp)) {
-		ic->permit = g_slist_prepend(ic->permit, g_strdup(name));
-	}
-}
-
-static void prplcb_privacy_permit_removed(PurpleAccount *account, const char *name)
-{
-	struct im_connection *ic = purple_ic_by_pa(account);
-	void *n;
-
-	n = g_slist_find_custom(ic->permit, name, (GCompareFunc) ic->acc->prpl->handle_cmp);
-	ic->permit = g_slist_remove(ic->permit, n);
-}
-
-static void prplcb_privacy_deny_added(PurpleAccount *account, const char *name)
-{
-	struct im_connection *ic = purple_ic_by_pa(account);
-
-	if (!g_slist_find_custom(ic->deny, name, (GCompareFunc) ic->acc->prpl->handle_cmp)) {
-		ic->deny = g_slist_prepend(ic->deny, g_strdup(name));
-	}
-}
-
-static void prplcb_privacy_deny_removed(PurpleAccount *account, const char *name)
-{
-	struct im_connection *ic = purple_ic_by_pa(account);
-	void *n;
-
-	n = g_slist_find_custom(ic->deny, name, (GCompareFunc) ic->acc->prpl->handle_cmp);
-	ic->deny = g_slist_remove(ic->deny, n);
-}
-
-static PurplePrivacyUiOps bee_privacy_uiops =
-{
-	prplcb_privacy_permit_added,       /* permit_added */
-	prplcb_privacy_permit_removed,     /* permit_removed */
-	prplcb_privacy_deny_added,         /* deny_added */
-	prplcb_privacy_deny_removed,       /* deny_removed */
-};
-
-static void prplcb_roomlist_create(PurpleRoomlist *list)
-{
-	struct purple_roomlist_data *rld;
-
-	list->ui_data = rld = g_new0(struct purple_roomlist_data, 1);
-	rld->topic = -1;
-}
-
-static void prplcb_roomlist_set_fields(PurpleRoomlist *list, GList *fields)
-{
-	gint topic = -1;
-	GList *l;
-	guint i;
-	PurpleRoomlistField *field;
-	struct purple_roomlist_data *rld = list->ui_data;
-
-	for (i = 0, l = fields; l; i++, l = l->next) {
-		field = l->data;
-
-		/* Use the first visible string field as a fallback topic */
-		if (i != 0 && topic < 0 && !field->hidden &&
-		    field->type == PURPLE_ROOMLIST_FIELD_STRING) {
-			topic = i;
-		}
-
-		if ((g_strcasecmp(field->name, "description") == 0) ||
-		    (g_strcasecmp(field->name, "topic") == 0)) {
-			if (field->type == PURPLE_ROOMLIST_FIELD_STRING) {
-				rld->topic = i;
-			}
-		}
-	}
-
-	if (rld->topic < 0) {
-		rld->topic = topic;
-	}
-}
-
-static char *prplcb_roomlist_get_room_name(PurpleRoomlist *list, PurpleRoomlistRoom *room)
-{
-	struct im_connection *ic = purple_ic_by_pa(list->account);
-	struct purple_data *pd = ic->proto_data;
-	PurplePlugin *prpl = purple_plugins_find_with_id(pd->account->protocol_id);
-	PurplePluginProtocolInfo *pi = prpl->info->extra_info;
-
-	if (pi && pi->roomlist_room_serialize) {
-		return pi->roomlist_room_serialize(room);
-	} else {
-		return g_strdup(purple_roomlist_room_get_name(room));
-	}
-}
-
-static void prplcb_roomlist_add_room(PurpleRoomlist *list, PurpleRoomlistRoom *room)
-{
-	bee_chat_info_t *ci;
-	char *title;
-	const char *topic;
-	GList *fields;
-	struct purple_roomlist_data *rld = list->ui_data;
-
-	fields = purple_roomlist_room_get_fields(room);
-	title = prplcb_roomlist_get_room_name(list, room);
-
-	if (rld->topic >= 0) {
-		topic = g_list_nth_data(fields, rld->topic);
-	} else {
-		topic = NULL;
-	}
-
-	ci = g_new(bee_chat_info_t, 1);
-	ci->title = title;
-	ci->topic = g_strdup(topic);
-	rld->chats = g_slist_prepend(rld->chats, ci);
-}
-
-static void prplcb_roomlist_in_progress(PurpleRoomlist *list, gboolean in_progress)
-{
-	struct im_connection *ic;
-	struct purple_data *pd;
-	struct purple_roomlist_data *rld = list->ui_data;
-
-	if (in_progress || !rld) {
-		return;
-	}
-
-	ic = purple_ic_by_pa(list->account);
-	imcb_chat_list_free(ic);
-
-	pd = ic->proto_data;
-	g_free(pd->chat_list_server);
-	pd->chat_list_server = NULL;
-
-	ic->chatlist = g_slist_reverse(rld->chats);
-	rld->chats = NULL;
-
-	imcb_chat_list_finish(ic);
-
-	if (rld->initialized) {
-		purple_roomlist_unref(list);
-	}
-}
-
-static void prplcb_roomlist_destroy(PurpleRoomlist *list)
-{
-	g_free(list->ui_data);
-	list->ui_data = NULL;
-}
-
-static PurpleRoomlistUiOps bee_roomlist_uiops =
-{
-	NULL,                         /* show_with_account */
-	prplcb_roomlist_create,       /* create */
-	prplcb_roomlist_set_fields,   /* set_fields */
-	prplcb_roomlist_add_room,     /* add_room */
-	prplcb_roomlist_in_progress,  /* in_progress */
-	prplcb_roomlist_destroy,      /* destroy */
-};
-
-static void prplcb_debug_print(PurpleDebugLevel level, const char *category, const char *arg_s)
-{
-	fprintf(stderr, "DEBUG %s: %s", category, arg_s);
-}
-
-static PurpleDebugUiOps bee_debug_uiops =
-{
-	prplcb_debug_print,        /* print */
-};
-
-static guint prplcb_ev_timeout_add(guint interval, GSourceFunc func, gpointer udata)
-{
-	return b_timeout_add(interval, (b_event_handler) func, udata);
-}
-
-static guint prplcb_ev_input_add(int fd, PurpleInputCondition cond, PurpleInputFunction func, gpointer udata)
-{
-	return b_input_add(fd, cond | B_EV_FLAG_FORCE_REPEAT, (b_event_handler) func, udata);
-}
-
-static gboolean prplcb_ev_remove(guint id)
-{
-	b_event_remove((gint) id);
-	return TRUE;
-}
-
-static PurpleEventLoopUiOps glib_eventloops =
-{
-	prplcb_ev_timeout_add,     /* timeout_add */
-	prplcb_ev_remove,          /* timeout_remove */
-	prplcb_ev_input_add,       /* input_add */
-	prplcb_ev_remove,          /* input_remove */
-};
-
-/* Absolutely no connection context at all. Thanks purple! brb crying */
-static void *prplcb_notify_message(PurpleNotifyMsgType type, const char *title,
-                                   const char *primary, const char *secondary)
-{
-	char *text = g_strdup_printf("%s%s - %s%s%s",
-		(type == PURPLE_NOTIFY_MSG_ERROR) ? "Error: " : "",
-		title,
-		primary ?: "",
-		(primary && secondary) ? " - " : "",
-		secondary ?: ""
-	);
-
-	if (local_bee->ui->log) {
-		local_bee->ui->log(local_bee, "purple", text);
-	}
-
-	g_free(text);
-
-	return NULL;
-}
-
-static void *prplcb_notify_email(PurpleConnection *gc, const char *subject, const char *from,
-                                 const char *to, const char *url)
-{
-	struct im_connection *ic = purple_ic_by_gc(gc);
-
-	imcb_notify_email(ic, "Received e-mail from %s for %s: %s <%s>", from, to, subject, url);
-
-	return NULL;
-}
-
-static void *prplcb_notify_userinfo(PurpleConnection *gc, const char *who, PurpleNotifyUserInfo *user_info)
-{
-	struct im_connection *ic = purple_ic_by_gc(gc);
-	GString *info = g_string_new("");
-	GList *l = purple_notify_user_info_get_entries(user_info);
-	char *key;
-	const char *value;
-	int n;
-
-	while (l) {
-		PurpleNotifyUserInfoEntry *e = l->data;
-
-		switch (purple_notify_user_info_entry_get_type(e)) {
-		case PURPLE_NOTIFY_USER_INFO_ENTRY_PAIR:
-		case PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_HEADER:
-			key = g_strdup(purple_notify_user_info_entry_get_label(e));
-			value = purple_notify_user_info_entry_get_value(e);
-
-			if (key) {
-				strip_html(key);
-				g_string_append_printf(info, "%s: ", key);
-
-				if (value) {
-					n = strlen(value) - 1;
-					while (g_ascii_isspace(value[n])) {
-						n--;
-					}
-					g_string_append_len(info, value, n + 1);
-				}
-				g_string_append_c(info, '\n');
-				g_free(key);
-			}
-
-			break;
-		case PURPLE_NOTIFY_USER_INFO_ENTRY_SECTION_BREAK:
-			g_string_append(info, "------------------------\n");
-			break;
-		}
-
-		l = l->next;
-	}
-
-	imcb_log(ic, "User %s info:\n%s", who, info->str);
-	g_string_free(info, TRUE);
-
-	return NULL;
-}
-
-static PurpleNotifyUiOps bee_notify_uiops =
-{
-	prplcb_notify_message,     /* notify_message */
-	prplcb_notify_email,       /* notify_email */
-	NULL,                      /* notify_emails */
-	NULL,                      /* notify_formatted */
-	NULL,                      /* notify_searchresults */
-	NULL,                      /* notify_searchresults_new_rows */
-	prplcb_notify_userinfo,    /* notify_userinfo */
-};
-
-static void *prplcb_account_request_authorize(PurpleAccount *account, const char *remote_user,
-                                              const char *id, const char *alias, const char *message, gboolean on_list,
-                                              PurpleAccountRequestAuthorizationCb authorize_cb,
-                                              PurpleAccountRequestAuthorizationCb deny_cb, void *user_data)
-{
-	struct im_connection *ic = purple_ic_by_pa(account);
-	char *q;
-
-	if (alias) {
-		q = g_strdup_printf("%s (%s) wants to add you to his/her contact "
-		                    "list. (%s)", alias, remote_user, message);
-	} else {
-		q = g_strdup_printf("%s wants to add you to his/her contact "
-		                    "list. (%s)", remote_user, message);
-	}
-
-	imcb_ask_with_free(ic, q, user_data, authorize_cb, deny_cb, NULL);
-	g_free(q);
-
-	return NULL;
-}
-
-static PurpleAccountUiOps bee_account_uiops =
-{
-	NULL,                              /* notify_added */
-	NULL,                              /* status_changed */
-	NULL,                              /* request_add */
-	prplcb_account_request_authorize,  /* request_authorize */
-	NULL,                              /* close_account_request */
-};
-
-static void *prplcb_bitlbee_set_account_password(PurpleAccount *account, char *password)
-{
-	struct im_connection *ic = purple_ic_by_pa(account);
-
-	set_setstr(&ic->acc->set, "password", password ? password : "");
-
-	return GINT_TO_POINTER(TRUE);
-}
-
-extern PurpleXferUiOps bee_xfer_uiops;
-
-static void purple_ui_init()
-{
-	purple_connections_set_ui_ops(&bee_conn_uiops);
-	purple_blist_set_ui_ops(&bee_blist_uiops);
-	purple_conversations_set_ui_ops(&bee_conv_uiops);
-	purple_request_set_ui_ops(&bee_request_uiops);
-	purple_privacy_set_ui_ops(&bee_privacy_uiops);
-	purple_roomlist_set_ui_ops(&bee_roomlist_uiops);
-	purple_notify_set_ui_ops(&bee_notify_uiops);
-	purple_accounts_set_ui_ops(&bee_account_uiops);
-	purple_xfers_set_ui_ops(&bee_xfer_uiops);
-
-	if (getenv("BITLBEE_DEBUG")) {
-		purple_debug_set_ui_ops(&bee_debug_uiops);
-	}
-}
-
-/* borrowing this semi-private function
- * TODO: figure out a better interface later (famous last words) */
-gboolean plugin_info_add(struct plugin_info *info);
-
-void purple_initmodule()
-{
-	struct prpl funcs;
-	GList *prots;
-	GString *help;
-	char *dir;
-	gboolean debug_enabled = !!getenv("BITLBEE_DEBUG");
-
-	if (purple_get_core() != NULL) {
-		log_message(LOGLVL_ERROR, "libpurple already initialized. "
-		            "Please use inetd or ForkDaemon mode instead.");
-		return;
-	}
-
-	g_return_if_fail((int) B_EV_IO_READ == (int) PURPLE_INPUT_READ);
-	g_return_if_fail((int) B_EV_IO_WRITE == (int) PURPLE_INPUT_WRITE);
-
-	dir = g_strdup_printf("%s/purple", global.conf->configdir);
-	purple_util_set_user_dir(dir);
-	g_free(dir);
-
-	dir = g_strdup_printf("%s/purple", global.conf->plugindir);
-	purple_plugins_add_search_path(dir);
-	g_free(dir);
-
-	purple_debug_set_enabled(debug_enabled);
-	purple_core_set_ui_ops(&bee_core_uiops);
-	purple_eventloop_set_ui_ops(&glib_eventloops);
-	if (!purple_core_init("BitlBee")) {
-		/* Initializing the core failed. Terminate. */
-		fprintf(stderr, "libpurple initialization failed.\n");
-		abort();
-	}
-	purple_debug_set_enabled(FALSE);
-
-	if (proxytype != PROXY_NONE) {
-		PurpleProxyInfo *pi = purple_global_proxy_get_info();
-		switch (proxytype) {
-		case PROXY_SOCKS4A:
-		case PROXY_SOCKS4:
-			purple_proxy_info_set_type(pi, PURPLE_PROXY_SOCKS4);
-			break;
-		case PROXY_SOCKS5:
-			purple_proxy_info_set_type(pi, PURPLE_PROXY_SOCKS5);
-			break;
-		case PROXY_HTTP:
-			purple_proxy_info_set_type(pi, PURPLE_PROXY_HTTP);
-			break;
-		}
-		purple_proxy_info_set_host(pi, proxyhost);
-		purple_proxy_info_set_port(pi, proxyport);
-		purple_proxy_info_set_username(pi, proxyuser);
-		purple_proxy_info_set_password(pi, proxypass);
-	}
-
-	purple_set_blist(purple_blist_new());
-
-	/* No, really. So far there were ui_ops for everything, but now suddenly
-	   one needs to use signals for typing notification stuff. :-( */
-	purple_signal_connect(purple_conversations_get_handle(), "buddy-typing",
-	                      &funcs, PURPLE_CALLBACK(prplcb_buddy_typing), NULL);
-	purple_signal_connect(purple_conversations_get_handle(), "buddy-typed",
-	                      &funcs, PURPLE_CALLBACK(prplcb_buddy_typing), NULL);
-	purple_signal_connect(purple_conversations_get_handle(), "buddy-typing-stopped",
-	                      &funcs, PURPLE_CALLBACK(prplcb_buddy_typing), NULL);
-
-	/* "bitlbee-set-account-password" signal:
-	 * Replacement API for purple_account_set_password() to be called by
-	 * prpls that wish to store updated passwords or oauth tokens, since
-	 * our password storage doesn't get notified of calls to
-	 * purple_account_set_password().
-	 *
-	 * When used with purple_signal_emit_return_1() returns:
-	 *  - GINT_TO_POINTER(TRUE) if implemented by this version of bitlbee
-	 *  - NULL otherwise.
-	 *
-	 * Originally made for the hangouts plugin.
-	 */
-
-	purple_signal_register(purple_accounts_get_handle(), "bitlbee-set-account-password",
-	                       purple_marshal_POINTER__POINTER_POINTER,
-	                       purple_value_new(PURPLE_TYPE_BOOLEAN), 2,
-	                       purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_ACCOUNT),
-	                       purple_value_new(PURPLE_TYPE_STRING));
-
-	purple_signal_connect(purple_accounts_get_handle(), "bitlbee-set-account-password",
-	                      &funcs, PURPLE_CALLBACK(prplcb_bitlbee_set_account_password), NULL);
-
-	memset(&funcs, 0, sizeof(funcs));
-	funcs.login = purple_login;
-	funcs.init = purple_init;
-	funcs.logout = purple_logout;
-	funcs.buddy_msg = purple_buddy_msg;
-	funcs.away_states = purple_away_states;
-	funcs.set_away = purple_set_away;
-	funcs.add_buddy = purple_add_buddy;
-	funcs.remove_buddy = purple_remove_buddy;
-	funcs.add_permit = purple_add_permit;
-	funcs.add_deny = purple_add_deny;
-	funcs.rem_permit = purple_rem_permit;
-	funcs.rem_deny = purple_rem_deny;
-	funcs.get_info = purple_get_info;
-	funcs.keepalive = purple_keepalive;
-	funcs.send_typing = purple_send_typing;
-	funcs.handle_cmp = g_strcasecmp;
-	/* TODO(wilmer): Set these only for protocols that support them? */
-	funcs.chat_msg = purple_chat_msg;
-	funcs.chat_with = purple_chat_with;
-	funcs.chat_invite = purple_chat_invite;
-	funcs.chat_topic = purple_chat_set_topic;
-	funcs.chat_kick = purple_chat_kick;
-	funcs.chat_leave = purple_chat_leave;
-	funcs.chat_join = purple_chat_join;
-	funcs.chat_list = purple_chat_list;
-	funcs.chat_add_settings = purple_chat_add_settings;
-	funcs.chat_free_settings = purple_chat_free_settings;
-	funcs.transfer_request = purple_transfer_request;
-
-	help = g_string_new("BitlBee libpurple module supports the following IM protocols:\n");
-
-	/* Add a protocol entry to BitlBee's structures for every protocol
-	   supported by this libpurple instance. */
-	for (prots = purple_plugins_get_protocols(); prots; prots = prots->next) {
-		PurplePlugin *prot = prots->data;
-		PurplePluginProtocolInfo *pi = prot->info->extra_info;
-		struct prpl *ret;
-		struct plugin_info *info;
-
-		/* If we already have this one (as a native module), don't
-		   add a libpurple duplicate. */
-		if (find_protocol(prot->info->id)) {
-			continue;
-		}
-
-		ret = g_memdup2(&funcs, sizeof(funcs));
-		ret->name = ret->data = prot->info->id;
-		if (strncmp(ret->name, "prpl-", 5) == 0) {
-			ret->name += 5;
-		}
-
-		if (pi->options & OPT_PROTO_NO_PASSWORD) {
-			ret->options |= PRPL_OPT_NO_PASSWORD;
-		}
-
-		if (pi->options & OPT_PROTO_PASSWORD_OPTIONAL) {
-			ret->options |= PRPL_OPT_PASSWORD_OPTIONAL;
-		}
-
-		register_protocol(ret);
-
-		g_string_append_printf(help, "\n* %s (%s)", ret->name, prot->info->name);
-
-		info = g_new0(struct plugin_info, 1);
-		info->abiver = BITLBEE_ABI_VERSION_CODE;
-		info->name = ret->name;
-		info->version = prot->info->version;
-		info->description = prot->info->description;
-		info->author = prot->info->author;
-		info->url = prot->info->homepage;
-
-		plugin_info_add(info);
-	}
-
-	g_string_append(help, "\n\nFor used protocols, more information about available "
-	                "settings can be found using \x02help purple <protocol name>\x02 "
-	                "(create an account using that protocol first!)");
-
-	/* Add a simple dynamically-generated help item listing all
-	   the supported protocols. */
-	help_add_mem(&global.help, "purple", help->str);
-	g_string_free(help, TRUE);
-}
Index: protocols/sha.c
===================================================================
--- protocols/sha.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/sha.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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;
+}
Index: protocols/sha.h
===================================================================
--- protocols/sha.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/sha.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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
Index: protocols/ssl_bogus.c
===================================================================
--- protocols/ssl_bogus.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/ssl_bogus.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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 );
+}
Index: protocols/ssl_client.h
===================================================================
--- protocols/ssl_client.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/ssl_client.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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 );
Index: protocols/ssl_gnutls.c
===================================================================
--- protocols/ssl_gnutls.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/ssl_gnutls.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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 );
+}
Index: protocols/ssl_nss.c
===================================================================
--- protocols/ssl_nss.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/ssl_nss.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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 );
+}
Index: protocols/ssl_openssl.c
===================================================================
--- protocols/ssl_openssl.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/ssl_openssl.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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 );
+}
Index: otocols/twitter/Makefile
===================================================================
--- protocols/twitter/Makefile	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,46 +1,0 @@
-###########################
-## Makefile for BitlBee  ##
-##                       ##
-## Copyright 2002 Lintux ##
-###########################
-
-### DEFINITIONS
-
--include ../../Makefile.settings
-ifdef _SRCDIR_
-_SRCDIR_ := $(_SRCDIR_)protocols/twitter/
-endif
-
-# [SH] Program variables
-objects = twitter.o twitter_http.o twitter_lib.o
-
-LFLAGS += -r
-
-# [SH] Phony targets
-all: twitter_mod.o
-check: all
-lcov: check
-gcov: 
-	gcov *.c
-	
-.PHONY: all clean distclean
-
-clean:
-	rm -f *.o core
-
-distclean: clean
-	rm -rf .depend
-
-### MAIN PROGRAM
-
-$(objects): ../../Makefile.settings Makefile
-
-$(objects): %.o: $(_SRCDIR_)%.c
-	@echo '*' Compiling $<
-	$(VERBOSE) $(CC) -c $(CFLAGS) $(CFLAGS_BITLBEE) $< -o $@
-
-twitter_mod.o: $(objects)
-	@echo '*' Linking twitter_mod.o
-	$(VERBOSE) $(LD) $(LFLAGS) $(objects) -o twitter_mod.o
-	
--include .depend/*.d
Index: otocols/twitter/twitter.c
===================================================================
--- protocols/twitter/twitter.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,1123 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Simple module to facilitate twitter functionality.                       *
-*                                                                           *
-*  Copyright 2009-2010 Geert Mulders <g.c.w.m.mulders@gmail.com>            *
-*  Copyright 2010-2013 Wilmer van der Gaast <wilmer@gaast.net>              *
-*                                                                           *
-*  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, version            *
-*  2.1.                                                                     *
-*                                                                           *
-*  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           *
-*                                                                           *
-****************************************************************************/
-
-#include "nogaim.h"
-#include "oauth.h"
-#include "twitter.h"
-#include "twitter_http.h"
-#include "twitter_lib.h"
-#include "url.h"
-
-GSList *twitter_connections = NULL;
-
-static int twitter_filter_cmp(struct twitter_filter *tf1,
-                              struct twitter_filter *tf2)
-{
-	int i1 = 0;
-	int i2 = 0;
-	int i;
-
-	static const twitter_filter_type_t types[] = {
-		/* Order of the types */
-		TWITTER_FILTER_TYPE_FOLLOW,
-		TWITTER_FILTER_TYPE_TRACK
-	};
-
-	for (i = 0; i < G_N_ELEMENTS(types); i++) {
-		if (types[i] == tf1->type) {
-			i1 = i + 1;
-			break;
-		}
-	}
-
-	for (i = 0; i < G_N_ELEMENTS(types); i++) {
-		if (types[i] == tf2->type) {
-			i2 = i + 1;
-			break;
-		}
-	}
-
-	if (i1 != i2) {
-		/* With different types, return their difference */
-		return i1 - i2;
-	}
-
-	/* With the same type, return the text comparison */
-	return g_strcasecmp(tf1->text, tf2->text);
-}
-
-static gboolean twitter_filter_update(gpointer data, gint fd,
-                                      b_input_condition cond)
-{
-	struct im_connection *ic = data;
-	struct twitter_data *td = ic->proto_data;
-
-	if (td->filters) {
-		twitter_open_filter_stream(ic);
-	} else if (td->filter_stream) {
-		http_close(td->filter_stream);
-		td->filter_stream = NULL;
-	}
-
-	td->filter_update_id = 0;
-	return FALSE;
-}
-
-static struct twitter_filter *twitter_filter_get(struct groupchat *c,
-                                                 twitter_filter_type_t type,
-                                                 const char *text)
-{
-	struct twitter_data *td = c->ic->proto_data;
-	struct twitter_filter *tf = NULL;
-	struct twitter_filter tfc = { type, (char *) text };
-	GSList *l;
-
-	for (l = td->filters; l; l = g_slist_next(l)) {
-		tf = l->data;
-
-		if (twitter_filter_cmp(tf, &tfc) == 0) {
-			break;
-		}
-
-		tf = NULL;
-	}
-
-	if (!tf) {
-		tf = g_new0(struct twitter_filter, 1);
-		tf->type = type;
-		tf->text = g_strdup(text);
-		td->filters = g_slist_prepend(td->filters, tf);
-	}
-
-	if (!g_slist_find(tf->groupchats, c)) {
-		tf->groupchats = g_slist_prepend(tf->groupchats, c);
-	}
-
-	if (td->filter_update_id > 0) {
-		b_event_remove(td->filter_update_id);
-	}
-
-	/* Wait for other possible filter changes to avoid request spam */
-	td->filter_update_id = b_timeout_add(TWITTER_FILTER_UPDATE_WAIT,
-	                                     twitter_filter_update, c->ic);
-	return tf;
-}
-
-static void twitter_filter_free(struct twitter_filter *tf)
-{
-	g_slist_free(tf->groupchats);
-	g_free(tf->text);
-	g_free(tf);
-}
-
-static void twitter_filter_remove(struct groupchat *c)
-{
-	struct twitter_data *td = c->ic->proto_data;
-	struct twitter_filter *tf;
-	GSList *l = td->filters;
-	GSList *p;
-
-	while (l != NULL) {
-		tf = l->data;
-		tf->groupchats = g_slist_remove(tf->groupchats, c);
-
-		p = l;
-		l = g_slist_next(l);
-
-		if (!tf->groupchats) {
-			twitter_filter_free(tf);
-			td->filters = g_slist_delete_link(td->filters, p);
-		}
-	}
-
-	if (td->filter_update_id > 0) {
-		b_event_remove(td->filter_update_id);
-	}
-
-	/* Wait for other possible filter changes to avoid request spam */
-	td->filter_update_id = b_timeout_add(TWITTER_FILTER_UPDATE_WAIT,
-	                                     twitter_filter_update, c->ic);
-}
-
-static void twitter_filter_remove_all(struct im_connection *ic)
-{
-	struct twitter_data *td = ic->proto_data;
-	GSList *chats = NULL;
-	struct twitter_filter *tf;
-	GSList *l = td->filters;
-	GSList *p;
-
-	while (l != NULL) {
-		tf = l->data;
-
-		/* Build up a list of groupchats to be freed */
-		for (p = tf->groupchats; p; p = g_slist_next(p)) {
-			if (!g_slist_find(chats, p->data)) {
-				chats = g_slist_prepend(chats, p->data);
-			}
-		}
-
-		p = l;
-		l = g_slist_next(l);
-		twitter_filter_free(p->data);
-		td->filters = g_slist_delete_link(td->filters, p);
-	}
-
-	l = chats;
-
-	while (l != NULL) {
-		p = l;
-		l = g_slist_next(l);
-
-		/* Freed each remaining groupchat */
-		imcb_chat_free(p->data);
-		chats = g_slist_delete_link(chats, p);
-	}
-
-	if (td->filter_stream) {
-		http_close(td->filter_stream);
-		td->filter_stream = NULL;
-	}
-}
-
-static GSList *twitter_filter_parse(struct groupchat *c, const char *text)
-{
-	char **fs = g_strsplit(text, ";", 0);
-	GSList *ret = NULL;
-	struct twitter_filter *tf;
-	char **f;
-	char *v;
-	int i;
-	int t;
-
-	static const twitter_filter_type_t types[] = {
-		TWITTER_FILTER_TYPE_FOLLOW,
-		TWITTER_FILTER_TYPE_TRACK
-	};
-
-	static const char *typestrs[] = {
-		"follow",
-		"track"
-	};
-
-	for (f = fs; *f; f++) {
-		if ((v = strchr(*f, ':')) == NULL) {
-			continue;
-		}
-
-		*(v++) = 0;
-
-		for (t = -1, i = 0; i < G_N_ELEMENTS(types); i++) {
-			if (g_strcasecmp(typestrs[i], *f) == 0) {
-				t = i;
-				break;
-			}
-		}
-
-		if (t < 0 || strlen(v) == 0) {
-			continue;
-		}
-
-		tf = twitter_filter_get(c, types[t], v);
-		ret = g_slist_prepend(ret, tf);
-	}
-
-	g_strfreev(fs);
-	return ret;
-}
-
-/**
- * Main loop function
- */
-gboolean twitter_main_loop(gpointer data, gint fd, b_input_condition cond)
-{
-	struct im_connection *ic = data;
-
-	// Check if we are still logged in...
-	if (!g_slist_find(twitter_connections, ic)) {
-		return FALSE;
-	}
-
-	// Do stuff..
-	return twitter_get_timeline(ic, -1) &&
-	       ((ic->flags & OPT_LOGGED_IN) == OPT_LOGGED_IN);
-}
-
-static void twitter_main_loop_start(struct im_connection *ic)
-{
-	struct twitter_data *td = ic->proto_data;
-
-	char *last_tweet = set_getstr(&ic->acc->set, "_last_tweet");
-
-	if (last_tweet) {
-		td->timeline_id = g_ascii_strtoull(last_tweet, NULL, 0);
-	}
-
-	/* Create the room now that we "logged in". */
-	if (td->flags & TWITTER_MODE_CHAT) {
-		twitter_groupchat_init(ic);
-	}
-
-	imcb_log(ic, "Getting initial statuses");
-
-	// Run this once. After this queue the main loop function (or open the
-	// stream if available).
-	twitter_main_loop(ic, -1, 0);
-
-	if (set_getbool(&ic->acc->set, "stream")) {
-		/* That fetch was just to get backlog, the stream will give
-		   us the rest. \o/ */
-		twitter_open_stream(ic);
-
-		/* Stream sends keepalives (empty lines) or actual data at
-		   least twice a minute. Disconnect if this stops. */
-		ic->flags |= OPT_PONGS;
-	} else {
-		/* Not using the streaming API, so keep polling the old-
-		   fashioned way. :-( */
-		td->main_loop_id =
-		        b_timeout_add(set_getint(&ic->acc->set, "fetch_interval") * 1000,
-		                      twitter_main_loop, ic);
-	}
-}
-
-struct groupchat *twitter_groupchat_init(struct im_connection *ic)
-{
-	char *name_hint;
-	struct groupchat *gc;
-	struct twitter_data *td = ic->proto_data;
-	GSList *l;
-
-	if (td->timeline_gc) {
-		return td->timeline_gc;
-	}
-
-	td->timeline_gc = gc = imcb_chat_new(ic, "twitter/timeline");
-
-	name_hint = g_strdup_printf("%s_%s", td->prefix, ic->acc->user);
-	imcb_chat_name_hint(gc, name_hint);
-	g_free(name_hint);
-
-	for (l = ic->bee->users; l; l = l->next) {
-		bee_user_t *bu = l->data;
-		if (bu->ic == ic) {
-			imcb_chat_add_buddy(gc, bu->handle);
-		}
-	}
-	imcb_chat_add_buddy(gc, ic->acc->user);
-
-	return gc;
-}
-
-static void twitter_oauth_start(struct im_connection *ic);
-
-void twitter_login_finish(struct im_connection *ic)
-{
-	struct twitter_data *td = ic->proto_data;
-
-	td->flags &= ~TWITTER_DOING_TIMELINE;
-
-	if (set_getbool(&ic->acc->set, "oauth") && !td->oauth_info) {
-		twitter_oauth_start(ic);
-	} else if (!(td->flags & TWITTER_MODE_ONE) &&
-	           !(td->flags & TWITTER_HAVE_FRIENDS)) {
-		imcb_log(ic, "Getting contact list");
-		twitter_get_friends_ids(ic, -1);
-		twitter_get_mutes_ids(ic, -1);
-		twitter_get_noretweets_ids(ic, -1);
-	} else {
-		twitter_main_loop_start(ic);
-	}
-}
-
-static const struct oauth_service twitter_oauth = {
-	"https://api.twitter.com/oauth/request_token",
-	"https://api.twitter.com/oauth/access_token",
-	"https://api.twitter.com/oauth/authorize",
-	.consumer_key = "ItChnf5KelJE5DOWVhz9ZuwYb",
-	.consumer_secret = "yoWJ16DRmJlILoB9Tf5T5lIUUPnIarqoO0YUWkYUOD7ttR0J6T",
-};
-
-static gboolean twitter_oauth_callback(struct oauth_info *info);
-
-static const struct oauth_service *get_oauth_service(struct im_connection *ic)
-{
-	return &twitter_oauth;
-
-	/* Could add more services, or allow configuring your own base URL +
-	   API keys. */
-}
-
-static void twitter_oauth_start(struct im_connection *ic)
-{
-	struct twitter_data *td = ic->proto_data;
-	const char *url = set_getstr(&ic->acc->set, "base_url");
-
-	imcb_log(ic, "Requesting OAuth request token");
-
-	if (!strstr(url, "twitter.com")) {
-		imcb_log(ic, "Warning: OAuth only works with Twitter.");
-	}
-
-	td->oauth_info = oauth_request_token(get_oauth_service(ic), twitter_oauth_callback, ic);
-
-	/* We need help from the user to complete OAuth login, so don't time
-	   out on this login. */
-	ic->flags |= OPT_SLOW_LOGIN;
-}
-
-static gboolean twitter_oauth_callback(struct oauth_info *info)
-{
-	struct im_connection *ic = info->data;
-	struct twitter_data *td;
-
-	if (!g_slist_find(twitter_connections, ic)) {
-		return FALSE;
-	}
-
-	td = ic->proto_data;
-	if (info->stage == OAUTH_REQUEST_TOKEN) {
-		char *name, *msg;
-
-		if (info->request_token == NULL) {
-			imcb_error(ic, "OAuth error: %s", twitter_parse_error(info->http));
-			imc_logout(ic, TRUE);
-			return FALSE;
-		}
-
-		name = g_strdup_printf("%s_%s", td->prefix, ic->acc->user);
-		msg = g_strdup_printf("To finish OAuth authentication, please visit "
-		                      "%s and respond with the resulting PIN code.",
-		                      info->auth_url);
-		imcb_buddy_msg(ic, name, msg, 0, 0);
-		g_free(name);
-		g_free(msg);
-	} else if (info->stage == OAUTH_ACCESS_TOKEN) {
-		const char *sn;
-
-		if (info->token == NULL || info->token_secret == NULL) {
-			imcb_error(ic, "OAuth error: %s", twitter_parse_error(info->http));
-			imc_logout(ic, TRUE);
-			return FALSE;
-		}
-
-		if ((sn = oauth_params_get(&info->params, "screen_name"))) {
-			if (ic->acc->prpl->handle_cmp(sn, ic->acc->user) != 0) {
-				imcb_log(ic, "Warning: You logged in via OAuth as %s "
-				         "instead of %s.", sn, ic->acc->user);
-			}
-			g_free(td->user);
-			td->user = g_strdup(sn);
-		}
-
-		/* IM mods didn't do this so far and it's ugly but I should
-		   be able to get away with it... */
-		g_free(ic->acc->pass);
-		ic->acc->pass = oauth_to_string(info);
-
-		twitter_login_finish(ic);
-	}
-
-	return TRUE;
-}
-
-int twitter_url_len_diff(gchar *msg, unsigned int target_len)
-{
-	int url_len_diff = 0;
-
-	static GRegex *regex = NULL;
-	GMatchInfo *match_info;
-
-	if (regex == NULL) {
-		regex = g_regex_new("(^|\\s)(http(s)?://[^\\s$]+)", 0, 0, NULL);
-	}
-
-	g_regex_match(regex, msg, 0, &match_info);
-	while (g_match_info_matches(match_info)) {
-		gchar *url;
-
-		url = g_match_info_fetch(match_info, 2);
-		url_len_diff += target_len - g_utf8_strlen(url, -1);
-
-		g_free(url);
-		g_match_info_next(match_info, NULL);
-	}
-	g_match_info_free(match_info);
-
-	return url_len_diff;
-}
-
-int twitter_message_len(gchar *msg, int target_len)
-{
-	int url_len_diff = 0;
-
-	if (target_len > 0) {
-		url_len_diff = twitter_url_len_diff(msg, target_len);
-	}
-
-	return g_utf8_strlen(msg, -1) + url_len_diff;
-}
-
-static gboolean twitter_length_check(struct im_connection *ic, gchar * msg)
-{
-	int max = set_getint(&ic->acc->set, "message_length");
-	int target_len = set_getint(&ic->acc->set, "target_url_length");
-	int len = twitter_message_len(msg, target_len);
-
-	if (max == 0 || len <= max) {
-		return TRUE;
-	}
-
-	twitter_log(ic, "Maximum message length exceeded: %d > %d", len, max);
-
-	return FALSE;
-}
-
-static char *set_eval_commands(set_t * set, char *value)
-{
-	if (g_strcasecmp(value, "strict") == 0) {
-		return value;
-	} else {
-		return set_eval_bool(set, value);
-	}
-}
-
-static char *set_eval_mode(set_t * set, char *value)
-{
-	if (g_strcasecmp(value, "one") == 0 ||
-	    g_strcasecmp(value, "many") == 0 || g_strcasecmp(value, "chat") == 0) {
-		return value;
-	} else {
-		return NULL;
-	}
-}
-
-static void twitter_init(account_t * acc)
-{
-	set_t *s;
-	char *def_url;
-	char *def_tul;
-	char *def_mentions;
-
-	if (strcmp(acc->prpl->name, "twitter") == 0) {
-		def_url = TWITTER_API_URL;
-		def_tul = "23";
-		def_mentions = "true";
-	} else {
-		def_url = "";
-		def_tul = "0";
-		def_mentions = "false";
-	}
-
-	s = set_add(&acc->set, "auto_reply_timeout", "10800", set_eval_int, acc);
-
-	s = set_add(&acc->set, "base_url", def_url, NULL, acc);
-	s->flags |= ACC_SET_OFFLINE_ONLY;
-
-	s = set_add(&acc->set, "commands", "true", set_eval_commands, acc);
-
-	s = set_add(&acc->set, "fetch_interval", "60", set_eval_int, acc);
-	s->flags |= ACC_SET_OFFLINE_ONLY;
-
-	s = set_add(&acc->set, "fetch_mentions", def_mentions, set_eval_bool, acc);
-
-	s = set_add(&acc->set, "message_length", "280", set_eval_int, acc);
-
-	s = set_add(&acc->set, "target_url_length", def_tul, set_eval_int, acc);
-
-	s = set_add(&acc->set, "mode", "chat", set_eval_mode, acc);
-	s->flags |= ACC_SET_OFFLINE_ONLY;
-
-	s = set_add(&acc->set, "oauth", "true", set_eval_oauth, acc);
-
-	s = set_add(&acc->set, "show_ids", "true", set_eval_bool, acc);
-
-	s = set_add(&acc->set, "show_old_mentions", "0", set_eval_int, acc);
-
-	s = set_add(&acc->set, "strip_newlines", "false", set_eval_bool, acc);
-
-	s = set_add(&acc->set, "_last_tweet", "0", NULL, acc);
-	s->flags |= SET_HIDDEN | SET_NOSAVE;
-
-	s = set_add(&acc->set, "in_korea", "false", set_eval_bool, acc);
-	s->flags |= SET_HIDDEN;
-
-	if (strcmp(acc->prpl->name, "twitter") == 0) {
-		s = set_add(&acc->set, "stream", "false", set_eval_bool, acc);
-		s->flags |= ACC_SET_OFFLINE_ONLY;
-	}
-}
-
-/**
- * Login method. Since the twitter API works with separate HTTP request we
- * only save the user and pass to the twitter_data object.
- */
-static void twitter_login(account_t * acc)
-{
-	struct im_connection *ic = imcb_new(acc);
-	struct twitter_data *td;
-	char name[strlen(acc->user) + 9];
-	url_t url;
-	char *s;
-
-	if (!url_set(&url, set_getstr(&ic->acc->set, "base_url")) ||
-	    (url.proto != PROTO_HTTP && url.proto != PROTO_HTTPS)) {
-		imcb_error(ic, "Incorrect API base URL: %s", set_getstr(&ic->acc->set, "base_url"));
-		imc_logout(ic, FALSE);
-		return;
-	}
-
-	if (!strstr(url.host, "twitter.com") &&
-	    set_getbool(&ic->acc->set, "stream")) {
-		imcb_error(ic, "Warning: The streaming API is only supported by Twitter, "
-		           "and you seem to be connecting to a different service.");
-	}
-
-	imcb_log(ic, "Connecting");
-
-	twitter_connections = g_slist_append(twitter_connections, ic);
-	td = g_new0(struct twitter_data, 1);
-	ic->proto_data = td;
-	td->user = g_strdup(acc->user);
-
-	td->url_ssl = url.proto == PROTO_HTTPS;
-	td->url_port = url.port;
-	td->url_host = g_strdup(url.host);
-	if (strcmp(url.file, "/") != 0) {
-		td->url_path = g_strdup(url.file);
-	} else {
-		td->url_path = g_strdup("");
-		if (g_str_has_suffix(url.host, "twitter.com")) {
-			/* May fire for people who turned on HTTPS. */
-			imcb_error(ic, "Warning: Twitter requires a version number in API calls "
-			           "now. Try resetting the base_url account setting.");
-		}
-	}
-
-	/* Hacky string mangling: Turn identi.ca into identi.ca and api.twitter.com
-	   into twitter, and try to be sensible if we get anything else. */
-	td->prefix = g_strdup(url.host);
-	if (g_str_has_suffix(td->prefix, ".com")) {
-		td->prefix[strlen(url.host) - 4] = '\0';
-	}
-	if ((s = strrchr(td->prefix, '.')) && strlen(s) > 4) {
-		/* If we have at least 3 chars after the last dot, cut off the rest.
-		   (mostly a www/api prefix or sth) */
-		s = g_strdup(s + 1);
-		g_free(td->prefix);
-		td->prefix = s;
-	}
-
-	if (strstr(acc->pass, "oauth_token=")) {
-		td->oauth_info = oauth_from_string(acc->pass, get_oauth_service(ic));
-	}
-
-	sprintf(name, "%s_%s", td->prefix, acc->user);
-	imcb_add_buddy(ic, name, NULL);
-	imcb_buddy_status(ic, name, OPT_LOGGED_IN, NULL, NULL);
-
-	td->log = g_new0(struct twitter_log_data, TWITTER_LOG_LENGTH);
-	td->log_id = -1;
-
-	s = set_getstr(&ic->acc->set, "mode");
-	if (g_strcasecmp(s, "one") == 0) {
-		td->flags |= TWITTER_MODE_ONE;
-	} else if (g_strcasecmp(s, "many") == 0) {
-		td->flags |= TWITTER_MODE_MANY;
-	} else {
-		td->flags |= TWITTER_MODE_CHAT;
-	}
-
-	twitter_login_finish(ic);
-}
-
-/**
- * Logout method. Just free the twitter_data.
- */
-static void twitter_logout(struct im_connection *ic)
-{
-	struct twitter_data *td = ic->proto_data;
-
-	// Set the status to logged out.
-	ic->flags &= ~OPT_LOGGED_IN;
-
-	if (td) {
-		// Remove the main_loop function from the function queue.
-		b_event_remove(td->main_loop_id);
-
-		if (td->timeline_gc) {
-			imcb_chat_free(td->timeline_gc);
-		}
-
-		if (td->filter_update_id > 0) {
-			b_event_remove(td->filter_update_id);
-		}
-
-		g_slist_foreach(td->mutes_ids, (GFunc) g_free, NULL);
-		g_slist_free(td->mutes_ids);
-
-		g_slist_foreach(td->noretweets_ids, (GFunc) g_free, NULL);
-		g_slist_free(td->noretweets_ids);
-
-		http_close(td->stream);
-		twitter_filter_remove_all(ic);
-		oauth_info_free(td->oauth_info);
-		g_free(td->user);
-		g_free(td->prefix);
-		g_free(td->url_host);
-		g_free(td->url_path);
-		g_free(td->log);
-		g_free(td);
-	}
-
-	twitter_connections = g_slist_remove(twitter_connections, ic);
-}
-
-static void twitter_handle_command(struct im_connection *ic, char *message);
-
-/**
- *
- */
-static int twitter_buddy_msg(struct im_connection *ic, char *who, char *message, int away)
-{
-	struct twitter_data *td = ic->proto_data;
-	int plen = strlen(td->prefix);
-
-	if (g_strncasecmp(who, td->prefix, plen) == 0 && who[plen] == '_' &&
-	    g_strcasecmp(who + plen + 1, ic->acc->user) == 0) {
-		if (set_getbool(&ic->acc->set, "oauth") &&
-		    td->oauth_info && td->oauth_info->token == NULL) {
-			char pin[strlen(message) + 1], *s;
-
-			strcpy(pin, message);
-			for (s = pin + sizeof(pin) - 2; s > pin && g_ascii_isspace(*s); s--) {
-				*s = '\0';
-			}
-			for (s = pin; *s && g_ascii_isspace(*s); s++) {
-			}
-
-			if (!oauth_access_token(s, td->oauth_info)) {
-				imcb_error(ic, "OAuth error: %s",
-				           "Failed to send access token request");
-				imc_logout(ic, TRUE);
-				return FALSE;
-			}
-		} else {
-			twitter_handle_command(ic, message);
-		}
-	} else {
-		twitter_direct_messages_new(ic, who, message);
-	}
-	return (0);
-}
-
-static void twitter_get_info(struct im_connection *ic, char *who)
-{
-}
-
-static void twitter_add_buddy(struct im_connection *ic, char *who, char *group)
-{
-	twitter_friendships_create_destroy(ic, who, 1);
-}
-
-static void twitter_remove_buddy(struct im_connection *ic, char *who, char *group)
-{
-	twitter_friendships_create_destroy(ic, who, 0);
-}
-
-static void twitter_chat_msg(struct groupchat *c, char *message, int flags)
-{
-	if (c && message) {
-		twitter_handle_command(c->ic, message);
-	}
-}
-
-static void twitter_chat_invite(struct groupchat *c, char *who, char *message)
-{
-}
-
-static struct groupchat *twitter_chat_join(struct im_connection *ic,
-                                           const char *room, const char *nick,
-                                           const char *password, set_t **sets)
-{
-	struct groupchat *c = imcb_chat_new(ic, room);
-	GSList *fs = twitter_filter_parse(c, room);
-	GString *topic = g_string_new("");
-	struct twitter_filter *tf;
-	GSList *l;
-
-	fs = g_slist_sort(fs, (GCompareFunc) twitter_filter_cmp);
-
-	for (l = fs; l; l = g_slist_next(l)) {
-		tf = l->data;
-
-		if (topic->len > 0) {
-			g_string_append(topic, ", ");
-		}
-
-		if (tf->type == TWITTER_FILTER_TYPE_FOLLOW) {
-			g_string_append_c(topic, '@');
-		}
-
-		g_string_append(topic, tf->text);
-	}
-
-	if (topic->len > 0) {
-		g_string_prepend(topic, "Twitter Filter: ");
-	}
-
-	imcb_chat_topic(c, NULL, topic->str, 0);
-	imcb_chat_add_buddy(c, ic->acc->user);
-
-	if (topic->len == 0) {
-		imcb_error(ic, "Failed to handle any filters");
-		imcb_chat_free(c);
-		c = NULL;
-	}
-
-	g_string_free(topic, TRUE);
-	g_slist_free(fs);
-
-	return c;
-}
-
-static void twitter_chat_leave(struct groupchat *c)
-{
-	struct twitter_data *td = c->ic->proto_data;
-
-	if (c != td->timeline_gc) {
-		twitter_filter_remove(c);
-		imcb_chat_free(c);
-		return;
-	}
-
-	/* If the user leaves the channel: Fine. Rejoin him/her once new
-	   tweets come in. */
-	imcb_chat_free(td->timeline_gc);
-	td->timeline_gc = NULL;
-}
-
-static void twitter_keepalive(struct im_connection *ic)
-{
-}
-
-static void twitter_add_permit(struct im_connection *ic, char *who)
-{
-}
-
-static void twitter_rem_permit(struct im_connection *ic, char *who)
-{
-}
-
-static void twitter_add_deny(struct im_connection *ic, char *who)
-{
-}
-
-static void twitter_rem_deny(struct im_connection *ic, char *who)
-{
-}
-
-//static char *twitter_set_display_name( set_t *set, char *value )
-//{
-//      return value;
-//}
-
-static void twitter_buddy_data_add(struct bee_user *bu)
-{
-	bu->data = g_new0(struct twitter_user_data, 1);
-}
-
-static void twitter_buddy_data_free(struct bee_user *bu)
-{
-	g_free(bu->data);
-}
-
-bee_user_t twitter_log_local_user;
-
-/** Convert the given bitlbee tweet ID, bitlbee username, or twitter tweet ID
- *  into a twitter tweet ID.
- *
- *  Returns 0 if the user provides garbage.
- */
-static guint64 twitter_message_id_from_command_arg(struct im_connection *ic, char *arg, bee_user_t **bu_)
-{
-	struct twitter_data *td = ic->proto_data;
-	struct twitter_user_data *tud;
-	bee_user_t *bu = NULL;
-	guint64 id = 0;
-
-	if (bu_) {
-		*bu_ = NULL;
-	}
-	if (!arg || !arg[0]) {
-		return 0;
-	}
-
-	if (arg[0] != '#' && (bu = bee_user_by_handle(ic->bee, ic, arg))) {
-		if ((tud = bu->data)) {
-			id = tud->last_id;
-		}
-	} else {
-		if (arg[0] == '#') {
-			arg++;
-		}
-		if (parse_int64(arg, 16, &id) && id < TWITTER_LOG_LENGTH) {
-			bu = td->log[id].bu;
-			id = td->log[id].id;
-		} else if (parse_int64(arg, 10, &id)) {
-			/* Allow normal tweet IDs as well; not a very useful
-			   feature but it's always been there. Just ignore
-			   very low IDs to avoid accidents. */
-			if (id < 1000000) {
-				id = 0;
-			}
-		}
-	}
-	if (bu_) {
-		if (bu == &twitter_log_local_user) {
-			/* HACK alert. There's no bee_user object for the local
-			 * user so just fake one for the few cmds that need it. */
-			twitter_log_local_user.handle = td->user;
-		} else {
-			/* Beware of dangling pointers! */
-			if (!g_slist_find(ic->bee->users, bu)) {
-				bu = NULL;
-			}
-		}
-		*bu_ = bu;
-	}
-	return id;
-}
-
-static void twitter_handle_command(struct im_connection *ic, char *message)
-{
-	struct twitter_data *td = ic->proto_data;
-	char *cmds, **cmd, *new = NULL;
-	guint64 in_reply_to = 0, id;
-	gboolean allow_post =
-	        g_strcasecmp(set_getstr(&ic->acc->set, "commands"), "strict") != 0;
-	gboolean auto_populate_reply_metadata = FALSE;
-	bee_user_t *bu = NULL;
-
-	cmds = g_strdup(message);
-	cmd = split_command_parts(cmds, 2);
-
-	if (cmd[0] == NULL) {
-		goto eof;
-	} else if (!set_getbool(&ic->acc->set, "commands") && allow_post) {
-		/* Not supporting commands if "commands" is set to true/strict. */
-	} else if (g_strcasecmp(cmd[0], "undo") == 0) {
-		if (cmd[1] == NULL) {
-			twitter_status_destroy(ic, td->last_status_id);
-		} else if ((id = twitter_message_id_from_command_arg(ic, cmd[1], NULL))) {
-			twitter_status_destroy(ic, id);
-		} else {
-			twitter_log(ic, "Could not undo last action");
-		}
-
-		goto eof;
-	} else if ((g_strcasecmp(cmd[0], "favourite") == 0 ||
-	            g_strcasecmp(cmd[0], "favorite") == 0 ||
-	            g_strcasecmp(cmd[0], "fav") == 0 ||
-	            g_strcasecmp(cmd[0], "like") == 0) && cmd[1]) {
-		if ((id = twitter_message_id_from_command_arg(ic, cmd[1], NULL))) {
-			twitter_favourite_tweet(ic, id);
-		} else {
-			twitter_log(ic, "Please provide a message ID or username.");
-		}
-		goto eof;
-	} else if (g_strcasecmp(cmd[0], "follow") == 0 && cmd[1]) {
-		twitter_add_buddy(ic, cmd[1], NULL);
-		goto eof;
-	} else if (g_strcasecmp(cmd[0], "unfollow") == 0 && cmd[1]) {
-		twitter_remove_buddy(ic, cmd[1], NULL);
-		goto eof;
-	} else if (g_strcasecmp(cmd[0], "mute") == 0 && cmd[1]) {
-		twitter_mute_create_destroy(ic, cmd[1], 1);
-		goto eof;
-	} else if (g_strcasecmp(cmd[0], "unmute") == 0 && cmd[1]) {
-		twitter_mute_create_destroy(ic, cmd[1], 0);
-		goto eof;
-	} else if ((g_strcasecmp(cmd[0], "report") == 0 ||
-	            g_strcasecmp(cmd[0], "spam") == 0) && cmd[1]) {
-		char *screen_name;
-
-		/* Report nominally works on users but look up the user who
-		   posted the given ID if the user wants to do it that way */
-		twitter_message_id_from_command_arg(ic, cmd[1], &bu);
-		if (bu) {
-			screen_name = bu->handle;
-		} else {
-			screen_name = cmd[1];
-		}
-
-		twitter_report_spam(ic, screen_name);
-		goto eof;
-	} else if (g_strcasecmp(cmd[0], "rt") == 0 && cmd[1]) {
-		id = twitter_message_id_from_command_arg(ic, cmd[1], NULL);
-
-		td->last_status_id = 0;
-		if (id) {
-			twitter_status_retweet(ic, id);
-		} else {
-			twitter_log(ic, "User `%s' does not exist or didn't "
-			            "post any statuses recently", cmd[1]);
-		}
-
-		goto eof;
-	} else if (g_strcasecmp(cmd[0], "reply") == 0 && cmd[1] && cmd[2]) {
-		id = twitter_message_id_from_command_arg(ic, cmd[1], &bu);
-		if (!id || !bu) {
-			twitter_log(ic, "User `%s' does not exist or didn't "
-			            "post any statuses recently", cmd[1]);
-			goto eof;
-		}
-		message = new = g_strdup_printf("@%s %s", bu->handle, cmd[2]);
-		in_reply_to = id;
-		allow_post = TRUE;
-	} else if (g_strcasecmp(cmd[0], "greply") == 0 && cmd[1] && cmd[2]) {
-		id = twitter_message_id_from_command_arg(ic, cmd[1], &bu);
-		if (!id || !bu) {
-			twitter_log(ic, "User `%s' does not exist or didn't "
-			            "post any statuses recently", cmd[1]);
-			goto eof;
-		}
-		message = new = g_strdup_printf("%s", cmd[2]);
-		in_reply_to = id;
-		auto_populate_reply_metadata = TRUE;
-		allow_post = TRUE;
-	} else if (g_strcasecmp(cmd[0], "rawreply") == 0 && cmd[1] && cmd[2]) {
-		id = twitter_message_id_from_command_arg(ic, cmd[1], NULL);
-		if (!id) {
-			twitter_log(ic, "Tweet `%s' does not exist", cmd[1]);
-			goto eof;
-		}
-		message = cmd[2];
-		in_reply_to = id;
-		allow_post = TRUE;
-	} else if (g_strcasecmp(cmd[0], "url") == 0) {
-		id = twitter_message_id_from_command_arg(ic, cmd[1], &bu);
-		if (!id) {
-			twitter_log(ic, "Tweet `%s' does not exist", cmd[1]);
-		} else {
-			twitter_status_show_url(ic, id);
-		}
-		goto eof;
-
-	} else if (g_strcasecmp(cmd[0], "post") == 0) {
-		message += 5;
-		allow_post = TRUE;
-	}
-
-	if (allow_post) {
-		char *s;
-
-		if (!twitter_length_check(ic, message)) {
-			goto eof;
-		}
-
-		s = cmd[0] + strlen(cmd[0]) - 1;
-		if (!new && s > cmd[0] && (*s == ':' || *s == ',')) {
-			*s = '\0';
-
-			if ((bu = bee_user_by_handle(ic->bee, ic, cmd[0]))) {
-				struct twitter_user_data *tud = bu->data;
-
-				new = g_strdup_printf("@%s %s", bu->handle,
-				                      message + (s - cmd[0]) + 2);
-				message = new;
-
-				if (time(NULL) < tud->last_time +
-				    set_getint(&ic->acc->set, "auto_reply_timeout")) {
-					in_reply_to = tud->last_id;
-				}
-			}
-		}
-
-		/* If the user runs undo between this request and its response
-		   this would delete the second-last Tweet. Prevent that. */
-		td->last_status_id = 0;
-		twitter_post_status(ic, message, in_reply_to, auto_populate_reply_metadata);
-	} else {
-		twitter_log(ic, "Unknown command: %s", cmd[0]);
-	}
-eof:
-	g_free(new);
-	g_free(cmds);
-}
-
-void twitter_log(struct im_connection *ic, char *format, ...)
-{
-	struct twitter_data *td = ic->proto_data;
-	va_list params;
-	char *text;
-
-	va_start(params, format);
-	text = g_strdup_vprintf(format, params);
-	va_end(params);
-
-	if (td->timeline_gc) {
-		imcb_chat_log(td->timeline_gc, "%s", text);
-	} else {
-		imcb_log(ic, "%s", text);
-	}
-
-	g_free(text);
-}
-
-
-void twitter_initmodule()
-{
-	struct prpl *ret = g_new0(struct prpl, 1);
-
-	ret->options = PRPL_OPT_NOOTR | PRPL_OPT_NO_PASSWORD;
-	ret->name = "twitter";
-	ret->login = twitter_login;
-	ret->init = twitter_init;
-	ret->logout = twitter_logout;
-	ret->buddy_msg = twitter_buddy_msg;
-	ret->get_info = twitter_get_info;
-	ret->add_buddy = twitter_add_buddy;
-	ret->remove_buddy = twitter_remove_buddy;
-	ret->chat_msg = twitter_chat_msg;
-	ret->chat_invite = twitter_chat_invite;
-	ret->chat_join = twitter_chat_join;
-	ret->chat_leave = twitter_chat_leave;
-	ret->keepalive = twitter_keepalive;
-	ret->add_permit = twitter_add_permit;
-	ret->rem_permit = twitter_rem_permit;
-	ret->add_deny = twitter_add_deny;
-	ret->rem_deny = twitter_rem_deny;
-	ret->buddy_data_add = twitter_buddy_data_add;
-	ret->buddy_data_free = twitter_buddy_data_free;
-	ret->handle_cmp = g_strcasecmp;
-
-	register_protocol(ret);
-
-	/* And an identi.ca variant: */
-	ret = g_memdup2(ret, sizeof(struct prpl));
-	ret->name = "identica";
-	ret->options =  PRPL_OPT_NOOTR;
-	register_protocol(ret);
-}
Index: otocols/twitter/twitter.h
===================================================================
--- protocols/twitter/twitter.h	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,133 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Simple module to facilitate twitter functionality.                       *
-*                                                                           *
-*  Copyright 2009-2010 Geert Mulders <g.c.w.m.mulders@gmail.com>            *
-*  Copyright 2010-2012 Wilmer van der Gaast <wilmer@gaast.net>              *
-*                                                                           *
-*  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, version            *
-*  2.1.                                                                     *
-*                                                                           *
-*  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           *
-*                                                                           *
-****************************************************************************/
-
-#include "nogaim.h"
-
-#ifndef _TWITTER_H
-#define _TWITTER_H
-
-#ifdef DEBUG_TWITTER
-#define debug(text ...) imcb_log(ic, text);
-#else
-#define debug(text ...)
-#endif
-
-typedef enum {
-	TWITTER_HAVE_FRIENDS   = 0x00001,
-	TWITTER_MODE_ONE       = 0x00002,
-	TWITTER_MODE_MANY      = 0x00004,
-	TWITTER_MODE_CHAT      = 0x00008,
-	TWITTER_DOING_TIMELINE = 0x10000,
-	TWITTER_GOT_TIMELINE   = 0x20000,
-	TWITTER_GOT_MENTIONS   = 0x40000,
-} twitter_flags_t;
-
-typedef enum {
-	TWITTER_FILTER_TYPE_FOLLOW = 0,
-	TWITTER_FILTER_TYPE_TRACK
-} twitter_filter_type_t;
-
-struct twitter_log_data;
-
-struct twitter_data {
-	char* user;
-	struct oauth_info *oauth_info;
-
-	gpointer home_timeline_obj;
-	gpointer mentions_obj;
-
-	guint64 timeline_id;
-
-	GSList *follow_ids;
-	GSList *mutes_ids;
-	GSList *noretweets_ids;
-	GSList *filters;
-
-	guint64 last_status_id; /* For undo */
-	gint main_loop_id;
-	gint filter_update_id;
-	struct http_request *stream;
-	struct http_request *filter_stream;
-	struct groupchat *timeline_gc;
-	gint http_fails;
-	twitter_flags_t flags;
-
-	/* set base_url */
-	gboolean url_ssl;
-	int url_port;
-	char *url_host;
-	char *url_path;
-
-	char *prefix; /* Used to generate contact + channel name. */
-
-	/* set show_ids */
-	struct twitter_log_data *log;
-	int log_id;
-};
-
-#define TWITTER_FILTER_UPDATE_WAIT 3000
-struct twitter_filter {
-	twitter_filter_type_t type;
-	char *text;
-	guint64 uid;
-	GSList *groupchats;
-};
-
-struct twitter_user_data {
-	guint64 last_id;
-	time_t last_time;
-};
-
-#define TWITTER_LOG_LENGTH 256
-struct twitter_log_data {
-	guint64 id;
-	/* DANGER: bu can be a dead pointer. Check it first.
-	 * twitter_message_id_from_command_arg() will do this. */
-	struct bee_user *bu;
-};
-
-/**
- * This has the same function as the msn_connections GSList. We use this to
- * make sure the connection is still alive in callbacks before we do anything
- * else.
- */
-extern GSList *twitter_connections;
-
-/**
- * Evil hack: Fake bee_user which will always point at the local user.
- * Sometimes used as a return value by twitter_message_id_from_command_arg.
- * NOT thread safe but don't you dare to even think of ever making BitlBee
- * threaded. :-)
- */
-extern bee_user_t twitter_log_local_user;
-
-void twitter_login_finish(struct im_connection *ic);
-
-struct http_request;
-char *twitter_parse_error(struct http_request *req);
-
-void twitter_log(struct im_connection *ic, char *format, ...);
-struct groupchat *twitter_groupchat_init(struct im_connection *ic);
-
-#endif //_TWITTER_H
Index: otocols/twitter/twitter_http.c
===================================================================
--- protocols/twitter/twitter_http.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,171 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Simple module to facilitate twitter functionality.                       *
-*                                                                           *
-*  Copyright 2009 Geert Mulders <g.c.w.m.mulders@gmail.com>                 *
-*                                                                           *
-*  This library is free software; you can redistribute it and/or            *
-*  modify it under the terms of the GNU Lesser General Public               *
-*  License as published by the Free Software Foundation, version            *
-*  2.1.                                                                     *
-*                                                                           *
-*  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           *
-*                                                                           *
-****************************************************************************/
-
-/***************************************************************************\
-*                                                                           *
-*  Some functions within this file have been copied from other files within  *
-*  BitlBee.                                                                 *
-*                                                                           *
-****************************************************************************/
-
-#include "twitter.h"
-#include "bitlbee.h"
-#include "url.h"
-#include "misc.h"
-#include "base64.h"
-#include "oauth.h"
-#include <ctype.h>
-#include <errno.h>
-
-#include "twitter_http.h"
-
-
-static char *twitter_url_append(char *url, char *key, char *value);
-
-/**
- * Do a request.
- * This is actually pretty generic function... Perhaps it should move to the lib/http_client.c
- */
-struct http_request *twitter_http(struct im_connection *ic, char *url_string, http_input_function func,
-                                  gpointer data, int is_post, char **arguments, int arguments_len)
-{
-	struct twitter_data *td = ic->proto_data;
-	char *tmp;
-	GString *request = g_string_new("");
-	void *ret = NULL;
-	char *url_arguments;
-	url_t *base_url = NULL;
-
-	url_arguments = g_strdup("");
-
-	// Construct the url arguments.
-	if (arguments_len != 0) {
-		int i;
-		for (i = 0; i < arguments_len; i += 2) {
-			tmp = twitter_url_append(url_arguments, arguments[i], arguments[i + 1]);
-			g_free(url_arguments);
-			url_arguments = tmp;
-		}
-	}
-
-	if (strstr(url_string, "://")) {
-		base_url = g_new0(url_t, 1);
-		if (!url_set(base_url, url_string)) {
-			goto error;
-		}
-	}
-
-	// Make the request.
-	g_string_printf(request, "%s %s%s%s%s HTTP/1.1\r\n"
-	                "Host: %s\r\n"
-	                "User-Agent: BitlBee " BITLBEE_VERSION "\r\n",
-	                is_post ? "POST" : "GET",
-	                base_url ? base_url->file : td->url_path,
-	                base_url ? "" : url_string,
-	                is_post ? "" : "?", is_post ? "" : url_arguments,
-	                base_url ? base_url->host : td->url_host);
-
-	// If a pass and user are given we append them to the request.
-	if (td->oauth_info) {
-		char *full_header;
-		char *full_url;
-
-		if (base_url) {
-			full_url = g_strdup(url_string);
-		} else {
-			full_url = g_strconcat(set_getstr(&ic->acc->set, "base_url"), url_string, NULL);
-		}
-		full_header = oauth_http_header(td->oauth_info, is_post ? "POST" : "GET",
-		                                full_url, url_arguments);
-
-		g_string_append_printf(request, "Authorization: %s\r\n", full_header);
-		g_free(full_header);
-		g_free(full_url);
-	} else {
-		char userpass[strlen(ic->acc->user) + 2 + strlen(ic->acc->pass)];
-		char *userpass_base64;
-
-		g_snprintf(userpass, sizeof(userpass), "%s:%s", ic->acc->user, ic->acc->pass);
-		userpass_base64 = base64_encode((unsigned char *) userpass, strlen(userpass));
-		g_string_append_printf(request, "Authorization: Basic %s\r\n", userpass_base64);
-		g_free(userpass_base64);
-	}
-
-	// Do POST stuff..
-	if (is_post) {
-		// Append the Content-Type and url-encoded arguments.
-		g_string_append_printf(request,
-		                       "Content-Type: application/x-www-form-urlencoded\r\n"
-		                       "Content-Length: %zd\r\n\r\n%s",
-		                       strlen(url_arguments), url_arguments);
-	} else {
-		// Append an extra \r\n to end the request...
-		g_string_append(request, "\r\n");
-	}
-
-	if (base_url) {
-		ret = http_dorequest(base_url->host, base_url->port, base_url->proto == PROTO_HTTPS, request->str, func,
-		                     data);
-	} else {
-		ret = http_dorequest(td->url_host, td->url_port, td->url_ssl, request->str, func, data);
-	}
-
-error:
-	g_free(url_arguments);
-	g_string_free(request, TRUE);
-	g_free(base_url);
-	return ret;
-}
-
-struct http_request *twitter_http_f(struct im_connection *ic, char *url_string, http_input_function func,
-                                    gpointer data, int is_post, char **arguments, int arguments_len,
-                                    twitter_http_flags_t flags)
-{
-	struct http_request *ret = twitter_http(ic, url_string, func, data, is_post, arguments, arguments_len);
-
-	if (ret) {
-		ret->flags |= flags;
-	}
-	return ret;
-}
-
-static char *twitter_url_append(char *url, char *key, char *value)
-{
-	char *key_encoded = g_strndup(key, 3 * strlen(key));
-
-	http_encode(key_encoded);
-	char *value_encoded = g_strndup(value, 3 * strlen(value));
-	http_encode(value_encoded);
-
-	char *retval;
-	if (strlen(url) != 0) {
-		retval = g_strdup_printf("%s&%s=%s", url, key_encoded, value_encoded);
-	} else {
-		retval = g_strdup_printf("%s=%s", key_encoded, value_encoded);
-	}
-
-	g_free(key_encoded);
-	g_free(value_encoded);
-
-	return retval;
-}
Index: otocols/twitter/twitter_http.h
===================================================================
--- protocols/twitter/twitter_http.h	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,45 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Simple module to facilitate twitter functionality.                       *
-*                                                                           *
-*  Copyright 2009 Geert Mulders <g.c.w.m.mulders@gmail.com>                 *
-*                                                                           *
-*  This library is free software; you can redistribute it and/or            *
-*  modify it under the terms of the GNU Lesser General Public               *
-*  License as published by the Free Software Foundation, version            *
-*  2.1.                                                                     *
-*                                                                           *
-*  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           *
-*                                                                           *
-****************************************************************************/
-
-#ifndef _TWITTER_HTTP_H
-#define _TWITTER_HTTP_H
-
-#include "nogaim.h"
-#include "http_client.h"
-
-typedef enum {
-	/* With this set, twitter_http_post() will post a generic confirmation
-	   message to the user. */
-	TWITTER_HTTP_USER_ACK = 0x1000000,
-} twitter_http_flags_t;
-
-struct oauth_info;
-
-struct http_request *twitter_http(struct im_connection *ic, char *url_string, http_input_function func,
-                                  gpointer data, int is_post, char** arguments, int arguments_len);
-struct http_request *twitter_http_f(struct im_connection *ic, char *url_string, http_input_function func,
-                                    gpointer data, int is_post, char** arguments, int arguments_len,
-                                    twitter_http_flags_t flags);
-
-#endif //_TWITTER_HTTP_H
-
Index: otocols/twitter/twitter_lib.c
===================================================================
--- protocols/twitter/twitter_lib.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,1876 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Simple module to facilitate twitter functionality.                       *
-*                                                                           *
-*  Copyright 2009-2010 Geert Mulders <g.c.w.m.mulders@gmail.com>            *
-*  Copyright 2010-2013 Wilmer van der Gaast <wilmer@gaast.net>              *
-*                                                                           *
-*  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, version            *
-*  2.1.                                                                     *
-*                                                                           *
-*  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           *
-*                                                                           *
-****************************************************************************/
-
-/* For strptime(): */
-#if (__sun)
-#else
-#define _XOPEN_SOURCE
-#endif
-
-#include "twitter_http.h"
-#include "twitter.h"
-#include "bitlbee.h"
-#include "url.h"
-#include "misc.h"
-#include "base64.h"
-#include "twitter_lib.h"
-#include "json_util.h"
-#include <ctype.h>
-#include <errno.h>
-
-#define TXL_STATUS 1
-#define TXL_USER 2
-#define TXL_ID 3
-
-struct twitter_xml_list {
-	int type;
-	gint64 next_cursor;
-	GSList *list;
-};
-
-struct twitter_xml_user {
-	guint64 uid;
-	char *name;
-	char *screen_name;
-};
-
-struct twitter_xml_status {
-	time_t created_at;
-	char *text;
-	struct twitter_xml_user *user;
-	guint64 id, rt_id; /* Usually equal, with RTs id == *original* id */
-	guint64 reply_to;
-	gboolean from_filter;
-};
-
-/**
- * Frees a twitter_xml_user struct.
- */
-static void txu_free(struct twitter_xml_user *txu)
-{
-	if (txu == NULL) {
-		return;
-	}
-
-	g_free(txu->name);
-	g_free(txu->screen_name);
-	g_free(txu);
-}
-
-/**
- * Frees a twitter_xml_status struct.
- */
-static void txs_free(struct twitter_xml_status *txs)
-{
-	if (txs == NULL) {
-		return;
-	}
-
-	g_free(txs->text);
-	txu_free(txs->user);
-	g_free(txs);
-}
-
-/**
- * Free a twitter_xml_list struct.
- * type is the type of list the struct holds.
- */
-static void txl_free(struct twitter_xml_list *txl)
-{
-	GSList *l;
-
-	if (txl == NULL) {
-		return;
-	}
-
-	for (l = txl->list; l; l = g_slist_next(l)) {
-		if (txl->type == TXL_STATUS) {
-			txs_free((struct twitter_xml_status *) l->data);
-		} else if (txl->type == TXL_ID) {
-			g_free(l->data);
-		} else if (txl->type == TXL_USER) {
-			txu_free(l->data);
-		}
-	}
-
-	g_slist_free(txl->list);
-	g_free(txl);
-}
-
-/**
- * Compare status elements
- */
-static gint twitter_compare_elements(gconstpointer a, gconstpointer b)
-{
-	struct twitter_xml_status *a_status = (struct twitter_xml_status *) a;
-	struct twitter_xml_status *b_status = (struct twitter_xml_status *) b;
-
-	if (a_status->created_at < b_status->created_at) {
-		return -1;
-	} else if (a_status->created_at > b_status->created_at) {
-		return 1;
-	} else {
-		return a_status->id < b_status->id ? -1 : 1;
-	}
-}
-
-/**
- * Add a buddy if it is not already added, set the status to logged in.
- */
-static void twitter_add_buddy(struct im_connection *ic, char *name, const char *fullname)
-{
-	struct twitter_data *td = ic->proto_data;
-
-	// Check if the buddy is already in the buddy list.
-	if (!bee_user_by_handle(ic->bee, ic, name)) {
-		// The buddy is not in the list, add the buddy and set the status to logged in.
-		imcb_add_buddy(ic, name, NULL);
-		imcb_rename_buddy(ic, name, fullname);
-		if (td->flags & TWITTER_MODE_CHAT) {
-			/* Necessary so that nicks always get translated to the
-			   exact Twitter username. */
-			imcb_buddy_nick_hint(ic, name, name);
-			if (td->timeline_gc) {
-				imcb_chat_add_buddy(td->timeline_gc, name);
-			}
-		} else if (td->flags & TWITTER_MODE_MANY) {
-			imcb_buddy_status(ic, name, OPT_LOGGED_IN, NULL, NULL);
-		}
-	}
-}
-
-/* Warning: May return a malloc()ed value, which will be free()d on the next
-   call. Only for short-term use. NOT THREADSAFE!  */
-char *twitter_parse_error(struct http_request *req)
-{
-	static char *ret = NULL;
-	json_value *root, *err;
-
-	g_free(ret);
-	ret = NULL;
-
-	if (req->body_size > 0) {
-		root = json_parse(req->reply_body, req->body_size);
-		err = json_o_get(root, "errors");
-		if (err && err->type == json_array && (err = err->u.array.values[0]) &&
-		    err->type == json_object) {
-			const char *msg = json_o_str(err, "message");
-			if (msg) {
-				ret = g_strdup_printf("%s (%s)", req->status_string, msg);
-			}
-		}
-		json_value_free(root);
-	}
-
-	return ret ? ret : req->status_string;
-}
-
-/* WATCH OUT: This function might or might not destroy your connection.
-   Sub-optimal indeed, but just be careful when this returns NULL! */
-static json_value *twitter_parse_response(struct im_connection *ic, struct http_request *req)
-{
-	gboolean logging_in = !(ic->flags & OPT_LOGGED_IN);
-	gboolean periodic;
-	struct twitter_data *td = ic->proto_data;
-	json_value *ret;
-	char path[64] = "", *s;
-
-	if ((s = strchr(req->request, ' '))) {
-		path[sizeof(path) - 1] = '\0';
-		strncpy(path, s + 1, sizeof(path) - 1);
-		if ((s = strchr(path, '?')) || (s = strchr(path, ' '))) {
-			*s = '\0';
-		}
-	}
-
-	/* Kinda nasty. :-( Trying to suppress error messages, but only
-	   for periodic (i.e. mentions/timeline) queries. */
-	periodic = strstr(path, "timeline") || strstr(path, "mentions");
-
-	if (req->status_code == 401 && logging_in) {
-		/* IIRC Twitter once had an outage where they were randomly
-		   throwing 401s so I'll keep treating this one as fatal
-		   only during login. */
-		imcb_error(ic, "Authentication failure (%s)",
-		           twitter_parse_error(req));
-		imc_logout(ic, FALSE);
-		return NULL;
-	} else if (req->status_code != 200) {
-		// It didn't go well, output the error and return.
-		if (!periodic || logging_in || ++td->http_fails >= 5) {
-			twitter_log(ic, "Error: Could not retrieve %s: %s",
-			            path, twitter_parse_error(req));
-		}
-
-		if (logging_in) {
-			imc_logout(ic, TRUE);
-		}
-		return NULL;
-	} else {
-		td->http_fails = 0;
-	}
-
-	if ((ret = json_parse(req->reply_body, req->body_size)) == NULL) {
-		imcb_error(ic, "Could not retrieve %s: %s",
-		           path, "JSON parse error");
-	}
-	return ret;
-}
-
-static void twitter_http_get_friends_ids(struct http_request *req);
-static void twitter_http_get_mutes_ids(struct http_request *req);
-static void twitter_http_get_noretweets_ids(struct http_request *req);
-
-/**
- * Get the friends ids.
- */
-void twitter_get_friends_ids(struct im_connection *ic, gint64 next_cursor)
-{
-	// Primitive, but hey! It works...
-	char *args[2];
-
-	args[0] = "cursor";
-	args[1] = g_strdup_printf("%" G_GINT64_FORMAT, next_cursor);
-	twitter_http(ic, TWITTER_FRIENDS_IDS_URL, twitter_http_get_friends_ids, ic, 0, args, 2);
-
-	g_free(args[1]);
-}
-
-/**
- * Get the muted users ids.
- */
-void twitter_get_mutes_ids(struct im_connection *ic, gint64 next_cursor)
-{
-	char *args[2];
-
-	args[0] = "cursor";
-	args[1] = g_strdup_printf("%" G_GINT64_FORMAT, next_cursor);
-	twitter_http(ic, TWITTER_MUTES_IDS_URL, twitter_http_get_mutes_ids, ic, 0, args, 2);
-
-	g_free(args[1]);
-}
-
-/**
- * Get the ids for users from whom we should ignore retweets.
- */
-void twitter_get_noretweets_ids(struct im_connection *ic, gint64 next_cursor)
-{
-	char *args[2];
-
-	args[0] = "cursor";
-	args[1] = g_strdup_printf("%" G_GINT64_FORMAT, next_cursor);
-	twitter_http(ic, TWITTER_NORETWEETS_IDS_URL, twitter_http_get_noretweets_ids, ic, 0, args, 2);
-
-	g_free(args[1]);
-}
-
-/**
- * Fill a list of ids.
- */
-static gboolean twitter_xt_get_friends_id_list(json_value *node, struct twitter_xml_list *txl)
-{
-	json_value *c;
-	int i;
-
-	// Set the list type.
-	txl->type = TXL_ID;
-
-	c = json_o_get(node, "ids");
-	if (!c || c->type != json_array) {
-		return FALSE;
-	}
-
-	for (i = 0; i < c->u.array.length; i++) {
-		if (c->u.array.values[i]->type != json_integer) {
-			continue;
-		}
-
-		txl->list = g_slist_prepend(txl->list,
-		                            g_strdup_printf("%" PRId64, c->u.array.values[i]->u.integer));
-	}
-
-	c = json_o_get(node, "next_cursor");
-	if (c && c->type == json_integer) {
-		txl->next_cursor = c->u.integer;
-	} else {
-		txl->next_cursor = -1;
-	}
-
-	return TRUE;
-}
-
-static void twitter_get_users_lookup(struct im_connection *ic);
-
-/**
- * Callback for getting the friends ids.
- */
-static void twitter_http_get_friends_ids(struct http_request *req)
-{
-	struct im_connection *ic;
-	json_value *parsed;
-	struct twitter_xml_list *txl;
-	struct twitter_data *td;
-
-	ic = req->data;
-
-	// Check if the connection is still active.
-	if (!g_slist_find(twitter_connections, ic)) {
-		return;
-	}
-
-	td = ic->proto_data;
-
-	// Parse the data.
-	if (!(parsed = twitter_parse_response(ic, req))) {
-		return;
-	}
-
-	txl = g_new0(struct twitter_xml_list, 1);
-	txl->list = td->follow_ids;
-
-	twitter_xt_get_friends_id_list(parsed, txl);
-	json_value_free(parsed);
-
-	td->follow_ids = txl->list;
-	if (txl->next_cursor) {
-		/* These were just numbers. Up to 4000 in a response AFAIK so if we get here
-		   we may be using a spammer account. \o/ */
-		twitter_get_friends_ids(ic, txl->next_cursor);
-	} else {
-		/* Now to convert all those numbers into names.. */
-		twitter_get_users_lookup(ic);
-	}
-
-	txl->list = NULL;
-	txl_free(txl);
-}
-
-/**
- * Callback for getting the mutes ids.
- */
-static void twitter_http_get_mutes_ids(struct http_request *req)
-{
-	struct im_connection *ic = req->data;
-	json_value *parsed;
-	struct twitter_xml_list *txl;
-	struct twitter_data *td;
-
-	// Check if the connection is stil active
-	if (!g_slist_find(twitter_connections, ic)) {
-		return;
-	}
-
-	td = ic->proto_data;
-
-	if (req->status_code != 200) {
-		/* Fail silently */
-		return;
-	}
-
-	// Parse the data.
-	if (!(parsed = twitter_parse_response(ic, req))) {
-		return;
-	}
-
-	txl = g_new0(struct twitter_xml_list, 1);
-	txl->list = td->mutes_ids;
-
-	/* mute ids API response is similar enough to friends response
-	   to reuse this method */
-	twitter_xt_get_friends_id_list(parsed, txl);
-	json_value_free(parsed);
-
-	td->mutes_ids = txl->list;
-	if (txl->next_cursor) {
-		/* Recurse while there are still more pages */
-		twitter_get_mutes_ids(ic, txl->next_cursor);
-	}
-
-	txl->list = NULL;
-	txl_free(txl);
-}
-
-/**
- * Callback for getting the no-retweets ids.
- */
-static void twitter_http_get_noretweets_ids(struct http_request *req)
-{
-	struct im_connection *ic = req->data;
-	json_value *parsed;
-	struct twitter_xml_list *txl;
-	struct twitter_data *td;
-
-	// Check if the connection is stil active
-	if (!g_slist_find(twitter_connections, ic)) {
-		return;
-	}
-
-	if (req->status_code != 200) {
-		/* Fail silently */
-		return;
-	}
-
-	td = ic->proto_data;
-
-	// Parse the data.
-	if (!(parsed = twitter_parse_response(ic, req))) {
-		return;
-	}
-
-	txl = g_new0(struct twitter_xml_list, 1);
-	txl->list = td->noretweets_ids;
-
-	// Process the retweet ids
-	txl->type = TXL_ID;
-	if (parsed->type == json_array) {
-		unsigned int i;
-		for (i = 0; i < parsed->u.array.length; i++) {
-			json_value *c = parsed->u.array.values[i];
-			if (c->type != json_integer) {
-				continue;
-			}
-			txl->list = g_slist_prepend(txl->list,
-			                            g_strdup_printf("%" PRId64, c->u.integer));
-		}
-	}
-
-	json_value_free(parsed);
-	td->noretweets_ids = txl->list;
-
-	txl->list = NULL;
-	txl_free(txl);
-}
-
-static gboolean twitter_xt_get_users(json_value *node, struct twitter_xml_list *txl);
-static void twitter_http_get_users_lookup(struct http_request *req);
-
-static void twitter_get_users_lookup(struct im_connection *ic)
-{
-	struct twitter_data *td = ic->proto_data;
-	char *args[2] = {
-		"user_id",
-		NULL,
-	};
-	GString *ids = g_string_new("");
-	int i;
-
-	/* We can request up to 100 users at a time. */
-	for (i = 0; i < 100 && td->follow_ids; i++) {
-		g_string_append_printf(ids, ",%s", (char *) td->follow_ids->data);
-		g_free(td->follow_ids->data);
-		td->follow_ids = g_slist_remove(td->follow_ids, td->follow_ids->data);
-	}
-	if (ids->len > 0) {
-		args[1] = ids->str + 1;
-		/* POST, because I think ids can be up to 1KB long. */
-		twitter_http(ic, TWITTER_USERS_LOOKUP_URL, twitter_http_get_users_lookup, ic, 1, args, 2);
-	} else {
-		/* We have all users. Continue with login. (Get statuses.) */
-		td->flags |= TWITTER_HAVE_FRIENDS;
-		twitter_login_finish(ic);
-	}
-	g_string_free(ids, TRUE);
-}
-
-/**
- * Callback for getting (twitter)friends...
- *
- * Be afraid, be very afraid! This function will potentially add hundreds of "friends". "Who has
- * hundreds of friends?" you wonder? You probably not, since you are reading the source of
- * BitlBee... Get a life and meet new people!
- */
-static void twitter_http_get_users_lookup(struct http_request *req)
-{
-	struct im_connection *ic = req->data;
-	json_value *parsed;
-	struct twitter_xml_list *txl;
-	GSList *l = NULL;
-	struct twitter_xml_user *user;
-
-	// Check if the connection is still active.
-	if (!g_slist_find(twitter_connections, ic)) {
-		return;
-	}
-
-	// Get the user list from the parsed xml feed.
-	if (!(parsed = twitter_parse_response(ic, req))) {
-		return;
-	}
-
-	txl = g_new0(struct twitter_xml_list, 1);
-	txl->list = NULL;
-
-	twitter_xt_get_users(parsed, txl);
-	json_value_free(parsed);
-
-	// Add the users as buddies.
-	for (l = txl->list; l; l = g_slist_next(l)) {
-		user = l->data;
-		twitter_add_buddy(ic, user->screen_name, user->name);
-	}
-
-	// Free the structure.
-	txl_free(txl);
-
-	twitter_get_users_lookup(ic);
-}
-
-struct twitter_xml_user *twitter_xt_get_user(const json_value *node)
-{
-	struct twitter_xml_user *txu;
-	json_value *jv;
-
-	txu = g_new0(struct twitter_xml_user, 1);
-	txu->name = g_strdup(json_o_str(node, "name"));
-	txu->screen_name = g_strdup(json_o_str(node, "screen_name"));
-
-	jv = json_o_get(node, "id");
-	txu->uid = jv->u.integer;
-
-	return txu;
-}
-
-/**
- * Function to fill a twitter_xml_list struct.
- * It sets:
- *  - all <user>s from the <users> element.
- */
-static gboolean twitter_xt_get_users(json_value *node, struct twitter_xml_list *txl)
-{
-	struct twitter_xml_user *txu;
-	int i;
-
-	// Set the type of the list.
-	txl->type = TXL_USER;
-
-	if (!node || node->type != json_array) {
-		return FALSE;
-	}
-
-	// The root <users> node should hold the list of users <user>
-	// Walk over the nodes children.
-	for (i = 0; i < node->u.array.length; i++) {
-		txu = twitter_xt_get_user(node->u.array.values[i]);
-		if (txu) {
-			txl->list = g_slist_prepend(txl->list, txu);
-		}
-	}
-
-	return TRUE;
-}
-
-#ifdef __GLIBC__
-#define TWITTER_TIME_FORMAT "%a %b %d %H:%M:%S %z %Y"
-#else
-#define TWITTER_TIME_FORMAT "%a %b %d %H:%M:%S +0000 %Y"
-#endif
-
-static void expand_entities(char **text, const json_value *node, const json_value *extended_node);
-
-/**
- * Function to fill a twitter_xml_status struct.
- * It sets:
- *  - the status text and
- *  - the created_at timestamp and
- *  - the status id and
- *  - the user in a twitter_xml_user struct.
- */
-static struct twitter_xml_status *twitter_xt_get_status(const json_value *node)
-{
-	struct twitter_xml_status *txs = {0};
-	const json_value *rt = NULL;
-	const json_value *text_value = NULL;
-	const json_value *extended_node = NULL;
-
-	if (node->type != json_object) {
-		return FALSE;
-	}
-	txs = g_new0(struct twitter_xml_status, 1);
-
-	JSON_O_FOREACH(node, k, v) {
-		if (strcmp("text", k) == 0 && v->type == json_string && text_value == NULL) {
-			text_value = v;
-		} else if (strcmp("full_text", k) == 0 && v->type == json_string) {
-			text_value = v;
-		} else if (strcmp("extended_tweet", k) == 0 && v->type == json_object) {
-			text_value = json_o_get(v, "full_text");
-			extended_node = v;
-		} else if (strcmp("retweeted_status", k) == 0 && v->type == json_object) {
-			rt = v;
-		} else if (strcmp("created_at", k) == 0 && v->type == json_string) {
-			struct tm parsed;
-
-			/* Very sensitive to changes to the formatting of
-			   this field. :-( Also assumes the timezone used
-			   is UTC since C time handling functions suck. */
-			if (strptime(v->u.string.ptr, TWITTER_TIME_FORMAT, &parsed) != NULL) {
-				txs->created_at = mktime_utc(&parsed);
-			}
-		} else if (strcmp("user", k) == 0 && v->type == json_object) {
-			txs->user = twitter_xt_get_user(v);
-		} else if (strcmp("id", k) == 0 && v->type == json_integer) {
-			txs->rt_id = txs->id = v->u.integer;
-		} else if (strcmp("in_reply_to_status_id", k) == 0 && v->type == json_integer) {
-			txs->reply_to = v->u.integer;
-		}
-	}
-
-	/* If it's a (truncated) retweet, get the original. Even if the API claims it
-	   wasn't truncated because it may be lying. */
-	if (rt) {
-		struct twitter_xml_status *rtxs = twitter_xt_get_status(rt);
-		if (rtxs) {
-			txs->text = g_strdup_printf("RT @%s: %s", rtxs->user->screen_name, rtxs->text);
-			txs->id = rtxs->id;
-			txs_free(rtxs);
-		}
-	} else if (text_value && text_value->type == json_string) {
-		txs->text = g_memdup2(text_value->u.string.ptr, text_value->u.string.length + 1);
-		strip_html(txs->text);
-		expand_entities(&txs->text, node, extended_node);
-	}
-
-	if (txs->text && txs->user && txs->id) {
-		return txs;
-	}
-
-	txs_free(txs);
-	return NULL;
-}
-
-/**
- * Function to fill a twitter_xml_status struct (DM variant).
- */
-static struct twitter_xml_status *twitter_xt_get_dm(const json_value *node)
-{
-	struct twitter_xml_status *txs;
-
-	if (node->type != json_object) {
-		return FALSE;
-	}
-	txs = g_new0(struct twitter_xml_status, 1);
-
-	JSON_O_FOREACH(node, k, v) {
-		if (strcmp("text", k) == 0 && v->type == json_string) {
-			txs->text = g_memdup2(v->u.string.ptr, v->u.string.length + 1);
-			strip_html(txs->text);
-		} else if (strcmp("created_at", k) == 0 && v->type == json_string) {
-			struct tm parsed;
-
-			/* Very sensitive to changes to the formatting of
-			   this field. :-( Also assumes the timezone used
-			   is UTC since C time handling functions suck. */
-			if (strptime(v->u.string.ptr, TWITTER_TIME_FORMAT, &parsed) != NULL) {
-				txs->created_at = mktime_utc(&parsed);
-			}
-		} else if (strcmp("sender", k) == 0 && v->type == json_object) {
-			txs->user = twitter_xt_get_user(v);
-		} else if (strcmp("id", k) == 0 && v->type == json_integer) {
-			txs->id = v->u.integer;
-		}
-	}
-
-	expand_entities(&txs->text, node, NULL);
-
-	if (txs->text && txs->user && txs->id) {
-		return txs;
-	}
-
-	txs_free(txs);
-	return NULL;
-}
-
-static void expand_entities(char **text, const json_value *node, const json_value *extended_node)
-{
-	json_value *entities, *extended_entities, *quoted, *quoted_permalink;
-	char *quote_url = NULL, *quote_text = NULL, *quote_kort = NULL;
-	gboolean quote_used = FALSE;
-
-	if (!((entities = json_o_get(node, "entities")) && entities->type == json_object))
-		return;
-	if ((quoted = json_o_get(node, "quoted_status")) && quoted->type == json_object) {
-		/* New "retweets with comments" feature. Grab the
-		 * full message and try to insert it when we run into the
-		 * Tweet entity. */
-		struct twitter_xml_status *txs = twitter_xt_get_status(quoted);
-		quote_text = g_strdup_printf("@%s: %s", txs->user->screen_name, txs->text);
-		quote_url = g_strdup_printf("%s/status/%" G_GUINT64_FORMAT, txs->user->screen_name, txs->id);
-		if ((quoted_permalink = json_o_get(node, "quoted_status_permalink")) && quoted->type == json_object) {
-			quote_kort = json_o_strdup(quoted_permalink, "url");
-		}
-		txs_free(txs);
-	} else {
-		quoted = NULL;
-	}
-
-	if (extended_node) {
-		extended_entities = json_o_get(extended_node, "entities");
-		if (extended_entities && extended_entities->type == json_object) {
-			entities = extended_entities;
-		}
-	}
-
-	JSON_O_FOREACH(entities, k, v) {
-		int i;
-
-		if (v->type != json_array) {
-			continue;
-		}
-		if (strcmp(k, "urls") != 0 && strcmp(k, "media") != 0) {
-			continue;
-		}
-
-		for (i = 0; i < v->u.array.length; i++) {
-			const char *format = "%s%s <%s>%s";
-
-			if (v->u.array.values[i]->type != json_object) {
-				continue;
-			}
-
-			const char *kort = json_o_str(v->u.array.values[i], "url");
-			const char *disp = json_o_str(v->u.array.values[i], "display_url");
-			const char *full = json_o_str(v->u.array.values[i], "expanded_url");
-			char *pos, *new;
-
-			/* Skip if a required field is missing, if the t.co URL is not in fact
-			   in the Tweet at all, or if the full-ish one *is* in it already
-			   (dupes appear, especially in streaming API). */
-			if (!kort || !disp || !(pos = strstr(*text, kort)) || strstr(*text, disp)) {
-				continue;
-			}
-			if (quote_url && strstr(full, quote_url)) {
-				format = "%s<%s> [%s]%s";
-				disp = quote_text;
-				quote_used = TRUE;
-			}
-
-			*pos = '\0';
-			new = g_strdup_printf(format, *text, kort,
-			                      disp, pos + strlen(kort));
-
-			g_free(*text);
-			*text = new;
-		}
-	}
-
-	if (quote_text && !quote_used) {
-		const char *url = quote_kort ?: quote_url;
-		char *new = g_strdup_printf("%s <%s>[%s]", *text, url, quote_text);
-		g_free(*text);
-		*text = new;
-	}
-
-	g_free(quote_text);
-	g_free(quote_url);
-	g_free(quote_kort);
-}
-
-/**
- * Function to fill a twitter_xml_list struct.
- * It sets:
- *  - all <status>es within the <status> element and
- *  - the next_cursor.
- */
-static gboolean twitter_xt_get_status_list(struct im_connection *ic, const json_value *node,
-                                           struct twitter_xml_list *txl)
-{
-	struct twitter_xml_status *txs;
-	int i;
-
-	// Set the type of the list.
-	txl->type = TXL_STATUS;
-
-	if (node->type != json_array) {
-		return FALSE;
-	}
-
-	// The root <statuses> node should hold the list of statuses <status>
-	// Walk over the nodes children.
-	for (i = 0; i < node->u.array.length; i++) {
-		txs = twitter_xt_get_status(node->u.array.values[i]);
-		if (!txs) {
-			continue;
-		}
-
-		txl->list = g_slist_prepend(txl->list, txs);
-	}
-
-	return TRUE;
-}
-
-/* Will log messages either way. Need to keep track of IDs for stream deduping.
-   Plus, show_ids is on by default and I don't see why anyone would disable it. */
-static char *twitter_msg_add_id(struct im_connection *ic,
-                                struct twitter_xml_status *txs, const char *prefix)
-{
-	struct twitter_data *td = ic->proto_data;
-	int reply_to = -1;
-	bee_user_t *bu;
-
-	if (txs->reply_to) {
-		int i;
-		for (i = 0; i < TWITTER_LOG_LENGTH; i++) {
-			if (td->log[i].id == txs->reply_to) {
-				reply_to = i;
-				break;
-			}
-		}
-	}
-
-	if (txs->user && txs->user->screen_name &&
-	    (bu = bee_user_by_handle(ic->bee, ic, txs->user->screen_name))) {
-		struct twitter_user_data *tud = bu->data;
-
-		if (txs->id > tud->last_id) {
-			tud->last_id = txs->id;
-			tud->last_time = txs->created_at;
-		}
-	}
-
-	td->log_id = (td->log_id + 1) % TWITTER_LOG_LENGTH;
-	td->log[td->log_id].id = txs->id;
-	td->log[td->log_id].bu = bee_user_by_handle(ic->bee, ic, txs->user->screen_name);
-
-	/* This is all getting hairy. :-( If we RT'ed something ourselves,
-	   remember OUR id instead so undo will work. In other cases, the
-	   original tweet's id should be remembered for deduplicating. */
-	if (g_strcasecmp(txs->user->screen_name, td->user) == 0) {
-		td->log[td->log_id].id = txs->rt_id;
-		/* More useful than NULL. */
-		td->log[td->log_id].bu = &twitter_log_local_user;
-	}
-
-	if (set_getbool(&ic->acc->set, "show_ids")) {
-		if (reply_to != -1) {
-			return g_strdup_printf("\002[\002%02x->%02x\002]\002 %s%s",
-			                       td->log_id, reply_to, prefix, txs->text);
-		} else {
-			return g_strdup_printf("\002[\002%02x\002]\002 %s%s",
-			                       td->log_id, prefix, txs->text);
-		}
-	} else {
-		if (*prefix) {
-			return g_strconcat(prefix, txs->text, NULL);
-		} else {
-			return NULL;
-		}
-	}
-}
-
-/**
- * Function that is called to see the filter statuses in groupchat windows.
- */
-static void twitter_status_show_filter(struct im_connection *ic, struct twitter_xml_status *status)
-{
-	struct twitter_data *td = ic->proto_data;
-	char *msg = twitter_msg_add_id(ic, status, "");
-	struct twitter_filter *tf;
-	GSList *f;
-	GSList *l;
-
-	for (f = td->filters; f; f = g_slist_next(f)) {
-		tf = f->data;
-
-		switch (tf->type) {
-		case TWITTER_FILTER_TYPE_FOLLOW:
-			if (status->user->uid != tf->uid) {
-				continue;
-			}
-			break;
-
-		case TWITTER_FILTER_TYPE_TRACK:
-			if (strcasestr(status->text, tf->text) == NULL) {
-				continue;
-			}
-			break;
-
-		default:
-			continue;
-		}
-
-		for (l = tf->groupchats; l; l = g_slist_next(l)) {
-			imcb_chat_msg(l->data, status->user->screen_name,
-			              msg ? msg : status->text, 0, 0);
-		}
-	}
-
-	g_free(msg);
-}
-
-/**
- * Function that is called to see the statuses in a groupchat window.
- */
-static void twitter_status_show_chat(struct im_connection *ic, struct twitter_xml_status *status)
-{
-	struct twitter_data *td = ic->proto_data;
-	struct groupchat *gc;
-	gboolean me = g_strcasecmp(td->user, status->user->screen_name) == 0;
-	char *msg;
-
-	// Create a new groupchat if it does not exist.
-	gc = twitter_groupchat_init(ic);
-
-	if (!me) {
-		/* MUST be done before twitter_msg_add_id() to avoid #872. */
-		twitter_add_buddy(ic, status->user->screen_name, status->user->name);
-	}
-	msg = twitter_msg_add_id(ic, status, "");
-
-	// Say it!
-	if (me) {
-		imcb_chat_log(gc, "You: %s", msg ? msg : status->text);
-	} else {
-		imcb_chat_msg(gc, status->user->screen_name,
-		              msg ? msg : status->text, 0, status->created_at);
-	}
-
-	g_free(msg);
-}
-
-/**
- * Function that is called to see statuses as private messages.
- */
-static void twitter_status_show_msg(struct im_connection *ic, struct twitter_xml_status *status)
-{
-	struct twitter_data *td = ic->proto_data;
-	char from[MAX_STRING] = "";
-	char *prefix = NULL, *text = NULL;
-	gboolean me = g_strcasecmp(td->user, status->user->screen_name) == 0;
-
-	if (td->flags & TWITTER_MODE_ONE) {
-		g_snprintf(from, sizeof(from) - 1, "%s_%s", td->prefix, ic->acc->user);
-		from[MAX_STRING - 1] = '\0';
-	}
-
-	if (td->flags & TWITTER_MODE_ONE) {
-		prefix = g_strdup_printf("\002<\002%s\002>\002 ",
-		                         status->user->screen_name);
-	} else if (!me) {
-		twitter_add_buddy(ic, status->user->screen_name, status->user->name);
-	} else {
-		prefix = g_strdup("You: ");
-	}
-
-	text = twitter_msg_add_id(ic, status, prefix ? prefix : "");
-
-	imcb_buddy_msg(ic,
-	               *from ? from : status->user->screen_name,
-	               text ? text : status->text, 0, status->created_at);
-
-	g_free(text);
-	g_free(prefix);
-}
-
-static void twitter_status_show(struct im_connection *ic, struct twitter_xml_status *status)
-{
-	struct twitter_data *td = ic->proto_data;
-	char *last_id_str;
-	char *uid_str;
-
-	if (status->user == NULL || status->text == NULL) {
-		return;
-	}
-
-	/* Check this is not a tweet that should be muted */
-	uid_str = g_strdup_printf("%" G_GUINT64_FORMAT, status->user->uid);
-
-	if (g_slist_find_custom(td->mutes_ids, uid_str, (GCompareFunc)strcmp)) {
-		g_free(uid_str);
-		return;
-	}
-	if (status->id != status->rt_id && g_slist_find_custom(td->noretweets_ids, uid_str, (GCompareFunc)strcmp)) {
-		g_free(uid_str);
-		return;
-	}
-
-	/* Grrrr. Would like to do this during parsing, but can't access
-	   settings from there. */
-	if (set_getbool(&ic->acc->set, "strip_newlines")) {
-		strip_newlines(status->text);
-	}
-
-	if (status->from_filter) {
-		twitter_status_show_filter(ic, status);
-	} else if (td->flags & TWITTER_MODE_CHAT) {
-		twitter_status_show_chat(ic, status);
-	} else {
-		twitter_status_show_msg(ic, status);
-	}
-
-	// Update the timeline_id to hold the highest id, so that by the next request
-	// we won't pick up the updates already in the list.
-	td->timeline_id = MAX(td->timeline_id, status->rt_id);
-
-	last_id_str = g_strdup_printf("%" G_GUINT64_FORMAT, td->timeline_id);
-	set_setstr(&ic->acc->set, "_last_tweet", last_id_str);
-	g_free(last_id_str);
-	g_free(uid_str);
-}
-
-static gboolean twitter_stream_handle_object(struct im_connection *ic, json_value *o, gboolean from_filter);
-
-static void twitter_http_stream(struct http_request *req)
-{
-	struct im_connection *ic = req->data;
-	struct twitter_data *td;
-	json_value *parsed;
-	int len = 0;
-	char c, *nl;
-	gboolean from_filter;
-
-	if (!g_slist_find(twitter_connections, ic)) {
-		return;
-	}
-
-	td = ic->proto_data;
-
-	if ((req->flags & HTTPC_EOF) || !req->reply_body) {
-		if (req == td->stream) {
-			td->stream = NULL;
-		} else if (req == td->filter_stream) {
-			td->filter_stream = NULL;
-		}
-
-		imcb_error(ic, "Stream closed (%s)", req->status_string);
-		if (req->status_code == 401) {
-			imcb_error(ic, "Check your system clock.");
-		}
-		imc_logout(ic, TRUE);
-		return;
-	}
-
-	if (req == td->stream) {
-		ic->flags |= OPT_PONGED;
-	}
-
-	/* MUST search for CRLF, not just LF:
-	   https://dev.twitter.com/docs/streaming-apis/processing#Parsing_responses */
-	if (!(nl = strstr(req->reply_body, "\r\n"))) {
-		return;
-	}
-
-	len = nl - req->reply_body;
-	if (len > 0) {
-		c = req->reply_body[len];
-		req->reply_body[len] = '\0';
-
-		if ((parsed = json_parse(req->reply_body, req->body_size))) {
-			from_filter = (req == td->filter_stream);
-			twitter_stream_handle_object(ic, parsed, from_filter);
-		}
-		json_value_free(parsed);
-		req->reply_body[len] = c;
-	}
-
-	http_flush_bytes(req, len + 2);
-
-	/* One notification might bring multiple events! */
-	if (req->body_size > 0) {
-		twitter_http_stream(req);
-	}
-}
-
-static gboolean twitter_stream_handle_event(struct im_connection *ic, json_value *o);
-static gboolean twitter_stream_handle_status(struct im_connection *ic, struct twitter_xml_status *txs);
-
-static gboolean twitter_stream_handle_object(struct im_connection *ic, json_value *o, gboolean from_filter)
-{
-	struct twitter_data *td = ic->proto_data;
-	struct twitter_xml_status *txs;
-	json_value *c;
-
-	if ((txs = twitter_xt_get_status(o))) {
-		txs->from_filter = from_filter;
-		gboolean ret = twitter_stream_handle_status(ic, txs);
-		txs_free(txs);
-		return ret;
-	} else if ((c = json_o_get(o, "direct_message")) &&
-	           (txs = twitter_xt_get_dm(c))) {
-		if (g_strcasecmp(txs->user->screen_name, td->user) != 0) {
-			imcb_buddy_msg(ic, txs->user->screen_name,
-			               txs->text, 0, txs->created_at);
-		}
-		txs_free(txs);
-		return TRUE;
-	} else if ((c = json_o_get(o, "event")) && c->type == json_string) {
-		twitter_stream_handle_event(ic, o);
-		return TRUE;
-	} else if ((c = json_o_get(o, "disconnect")) && c->type == json_object) {
-		/* HACK: Because we're inside an event handler, we can't just
-		   disconnect here. Instead, just change the HTTP status string
-		   into a Twitter status string. */
-		char *reason = json_o_strdup(c, "reason");
-		if (reason) {
-			g_free(td->stream->status_string);
-			td->stream->status_string = reason;
-		}
-		return TRUE;
-	}
-	return FALSE;
-}
-
-static gboolean twitter_stream_handle_status(struct im_connection *ic, struct twitter_xml_status *txs)
-{
-	struct twitter_data *td = ic->proto_data;
-	int i;
-
-	for (i = 0; i < TWITTER_LOG_LENGTH; i++) {
-		if (td->log[i].id == txs->id) {
-			/* Got a duplicate (RT, probably). Drop it. */
-			return TRUE;
-		}
-	}
-
-	if (!(g_strcasecmp(txs->user->screen_name, td->user) == 0 ||
-	      set_getbool(&ic->acc->set, "fetch_mentions") ||
-	      bee_user_by_handle(ic->bee, ic, txs->user->screen_name))) {
-		/* Tweet is from an unknown person and the user does not want
-		   to see @mentions, so drop it. twitter_stream_handle_event()
-		   picks up new follows so this simple filter should be safe. */
-		/* TODO: The streaming API seems to do poor @mention matching.
-		   I.e. I'm getting mentions for @WilmerSomething, not just for
-		   @Wilmer. But meh. You want spam, you get spam. */
-		return TRUE;
-	}
-
-	twitter_status_show(ic, txs);
-
-	return TRUE;
-}
-
-static gboolean twitter_stream_handle_event(struct im_connection *ic, json_value *o)
-{
-	struct twitter_data *td = ic->proto_data;
-	json_value *source = json_o_get(o, "source");
-	json_value *target = json_o_get(o, "target");
-	const char *type = json_o_str(o, "event");
-	struct twitter_xml_user *us = NULL;
-	struct twitter_xml_user *ut = NULL;
-
-	if (!type || !source || source->type != json_object
-	    || !target || target->type != json_object) {
-		return FALSE;
-	}
-
-	if (strcmp(type, "follow") == 0) {
-		us = twitter_xt_get_user(source);
-		ut = twitter_xt_get_user(target);
-		if (g_strcasecmp(us->screen_name, td->user) == 0) {
-			twitter_add_buddy(ic, ut->screen_name, ut->name);
-		}
-	} else if (strcmp(type, "mute") == 0) {
-		GSList *found;
-		char *uid_str;
-		ut = twitter_xt_get_user(target);
-		uid_str = g_strdup_printf("%" G_GUINT64_FORMAT, ut->uid);
-		if (!(found = g_slist_find_custom(td->mutes_ids, uid_str,
-		                                  (GCompareFunc)strcmp))) {
-			td->mutes_ids = g_slist_prepend(td->mutes_ids, uid_str);
-		}
-		twitter_log(ic, "Muted user %s", ut->screen_name);
-		if (getenv("BITLBEE_DEBUG")) {
-			fprintf(stderr, "New mute: %s %"G_GUINT64_FORMAT"\n",
-			        ut->screen_name, ut->uid);
-		}
-	} else if (strcmp(type, "unmute") == 0) {
-		GSList *found;
-		char *uid_str;
-		ut = twitter_xt_get_user(target);
-		uid_str = g_strdup_printf("%" G_GUINT64_FORMAT, ut->uid);
-		if ((found = g_slist_find_custom(td->mutes_ids, uid_str,
-		                                (GCompareFunc)strcmp))) {
-			char *found_str = found->data;
-			td->mutes_ids = g_slist_delete_link(td->mutes_ids, found);
-			g_free(found_str);
-		}
-		g_free(uid_str);
-		twitter_log(ic, "Unmuted user %s", ut->screen_name);
-		if (getenv("BITLBEE_DEBUG")) {
-			fprintf(stderr, "New unmute: %s %"G_GUINT64_FORMAT"\n",
-			        ut->screen_name, ut->uid);
-		}
-	}
-
-	txu_free(us);
-	txu_free(ut);
-
-	return TRUE;
-}
-
-gboolean twitter_open_stream(struct im_connection *ic)
-{
-	struct twitter_data *td = ic->proto_data;
-	char *args[2] = { "with", "followings" };
-
-	if ((td->stream = twitter_http(ic, TWITTER_USER_STREAM_URL,
-	                               twitter_http_stream, ic, 0, args, 2))) {
-		/* This flag must be enabled or we'll get no data until EOF
-		   (which err, kind of, defeats the purpose of a streaming API). */
-		td->stream->flags |= HTTPC_STREAMING;
-		return TRUE;
-	}
-
-	return FALSE;
-}
-
-static gboolean twitter_filter_stream(struct im_connection *ic)
-{
-	struct twitter_data *td = ic->proto_data;
-	char *args[4] = { "follow", NULL, "track", NULL };
-	GString *followstr = g_string_new("");
-	GString *trackstr = g_string_new("");
-	gboolean ret = FALSE;
-	struct twitter_filter *tf;
-	GSList *l;
-
-	for (l = td->filters; l; l = g_slist_next(l)) {
-		tf = l->data;
-
-		switch (tf->type) {
-		case TWITTER_FILTER_TYPE_FOLLOW:
-			if (followstr->len > 0) {
-				g_string_append_c(followstr, ',');
-			}
-
-			g_string_append_printf(followstr, "%" G_GUINT64_FORMAT,
-			                       tf->uid);
-			break;
-
-		case TWITTER_FILTER_TYPE_TRACK:
-			if (trackstr->len > 0) {
-				g_string_append_c(trackstr, ',');
-			}
-
-			g_string_append(trackstr, tf->text);
-			break;
-
-		default:
-			continue;
-		}
-	}
-
-	args[1] = followstr->str;
-	args[3] = trackstr->str;
-
-	if (td->filter_stream) {
-		http_close(td->filter_stream);
-	}
-
-	if ((td->filter_stream = twitter_http(ic, TWITTER_FILTER_STREAM_URL,
-	                                      twitter_http_stream, ic, 0,
-	                                      args, 4))) {
-		/* This flag must be enabled or we'll get no data until EOF
-		   (which err, kind of, defeats the purpose of a streaming API). */
-		td->filter_stream->flags |= HTTPC_STREAMING;
-		ret = TRUE;
-	}
-
-	g_string_free(followstr, TRUE);
-	g_string_free(trackstr, TRUE);
-
-	return ret;
-}
-
-static void twitter_filter_users_post(struct http_request *req)
-{
-	struct im_connection *ic = req->data;
-	struct twitter_data *td;
-	struct twitter_filter *tf;
-	GList *users = NULL;
-	json_value *parsed;
-	json_value *id;
-	const char *name;
-	GString *fstr;
-	GSList *l;
-	GList *u;
-	int i;
-
-	// Check if the connection is still active.
-	if (!g_slist_find(twitter_connections, ic)) {
-		return;
-	}
-
-	td = ic->proto_data;
-
-	if (!(parsed = twitter_parse_response(ic, req))) {
-		return;
-	}
-
-	for (l = td->filters; l; l = g_slist_next(l)) {
-		tf = l->data;
-
-		if (tf->type == TWITTER_FILTER_TYPE_FOLLOW) {
-			users = g_list_prepend(users, tf);
-		}
-	}
-
-	if (parsed->type != json_array) {
-		goto finish;
-	}
-
-	for (i = 0; i < parsed->u.array.length; i++) {
-		id = json_o_get(parsed->u.array.values[i], "id");
-		name = json_o_str(parsed->u.array.values[i], "screen_name");
-
-		if (!name || !id || id->type != json_integer) {
-			continue;
-		}
-
-		for (u = users; u; u = g_list_next(u)) {
-			tf = u->data;
-
-			if (g_strcasecmp(tf->text, name) == 0) {
-				tf->uid = id->u.integer;
-				users = g_list_delete_link(users, u);
-				break;
-			}
-		}
-	}
-
-finish:
-	json_value_free(parsed);
-	twitter_filter_stream(ic);
-
-	if (!users) {
-		return;
-	}
-
-	fstr = g_string_new("");
-
-	for (u = users; u; u = g_list_next(u)) {
-		if (fstr->len > 0) {
-			g_string_append(fstr, ", ");
-		}
-
-		g_string_append(fstr, tf->text);
-	}
-
-	imcb_error(ic, "Failed UID acquisitions: %s", fstr->str);
-
-	g_string_free(fstr, TRUE);
-	g_list_free(users);
-}
-
-gboolean twitter_open_filter_stream(struct im_connection *ic)
-{
-	struct twitter_data *td = ic->proto_data;
-	char *args[2] = { "screen_name", NULL };
-	GString *ustr = g_string_new("");
-	struct twitter_filter *tf;
-	struct http_request *req;
-	GSList *l;
-
-	for (l = td->filters; l; l = g_slist_next(l)) {
-		tf = l->data;
-
-		if (tf->type != TWITTER_FILTER_TYPE_FOLLOW || tf->uid != 0) {
-			continue;
-		}
-
-		if (ustr->len > 0) {
-			g_string_append_c(ustr, ',');
-		}
-
-		g_string_append(ustr, tf->text);
-	}
-
-	if (ustr->len == 0) {
-		g_string_free(ustr, TRUE);
-		return twitter_filter_stream(ic);
-	}
-
-	args[1] = ustr->str;
-	req = twitter_http(ic, TWITTER_USERS_LOOKUP_URL,
-	                   twitter_filter_users_post,
-	                   ic, 0, args, 2);
-
-	g_string_free(ustr, TRUE);
-	return req != NULL;
-}
-
-static void twitter_get_home_timeline(struct im_connection *ic, gint64 next_cursor);
-static void twitter_get_mentions(struct im_connection *ic, gint64 next_cursor);
-
-/**
- * Get the timeline with optionally mentions
- */
-gboolean twitter_get_timeline(struct im_connection *ic, gint64 next_cursor)
-{
-	struct twitter_data *td = ic->proto_data;
-	gboolean include_mentions = set_getbool(&ic->acc->set, "fetch_mentions");
-
-	if (td->flags & TWITTER_DOING_TIMELINE) {
-		if (++td->http_fails >= 5) {
-			imcb_error(ic, "Fetch timeout (%d)", td->flags);
-			imc_logout(ic, TRUE);
-			return FALSE;
-		}
-	}
-
-	td->flags |= TWITTER_DOING_TIMELINE;
-
-	twitter_get_home_timeline(ic, next_cursor);
-
-	if (include_mentions) {
-		twitter_get_mentions(ic, next_cursor);
-	}
-
-	return TRUE;
-}
-
-/**
- * Call this one after receiving timeline/mentions. Show to user once we have
- * both.
- */
-void twitter_flush_timeline(struct im_connection *ic)
-{
-	struct twitter_data *td = ic->proto_data;
-	gboolean include_mentions = set_getbool(&ic->acc->set, "fetch_mentions");
-	int show_old_mentions = set_getint(&ic->acc->set, "show_old_mentions");
-	struct twitter_xml_list *home_timeline = td->home_timeline_obj;
-	struct twitter_xml_list *mentions = td->mentions_obj;
-	guint64 last_id = 0;
-	GSList *output = NULL;
-	GSList *l;
-
-	imcb_connected(ic);
-
-	if (!(td->flags & TWITTER_GOT_TIMELINE)) {
-		return;
-	}
-
-	if (include_mentions && !(td->flags & TWITTER_GOT_MENTIONS)) {
-		return;
-	}
-
-	if (home_timeline && home_timeline->list) {
-		for (l = home_timeline->list; l; l = g_slist_next(l)) {
-			output = g_slist_insert_sorted(output, l->data, twitter_compare_elements);
-		}
-	}
-
-	if (include_mentions && mentions && mentions->list) {
-		for (l = mentions->list; l; l = g_slist_next(l)) {
-			if (show_old_mentions < 1 && output && twitter_compare_elements(l->data, output->data) < 0) {
-				continue;
-			}
-
-			output = g_slist_insert_sorted(output, l->data, twitter_compare_elements);
-		}
-	}
-
-	// See if the user wants to see the messages in a groupchat window or as private messages.
-	while (output) {
-		struct twitter_xml_status *txs = output->data;
-		if (txs->id != last_id) {
-			twitter_status_show(ic, txs);
-		}
-		last_id = txs->id;
-		output = g_slist_remove(output, txs);
-	}
-
-	txl_free(home_timeline);
-	txl_free(mentions);
-
-	td->flags &= ~(TWITTER_DOING_TIMELINE | TWITTER_GOT_TIMELINE | TWITTER_GOT_MENTIONS);
-	td->home_timeline_obj = td->mentions_obj = NULL;
-}
-
-static void twitter_http_get_home_timeline(struct http_request *req);
-static void twitter_http_get_mentions(struct http_request *req);
-
-/**
- * Get the timeline.
- */
-static void twitter_get_home_timeline(struct im_connection *ic, gint64 next_cursor)
-{
-	struct twitter_data *td = ic->proto_data;
-
-	txl_free(td->home_timeline_obj);
-	td->home_timeline_obj = NULL;
-	td->flags &= ~TWITTER_GOT_TIMELINE;
-
-	char *args[8];
-	args[0] = "cursor";
-	args[1] = g_strdup_printf("%" G_GINT64_FORMAT, next_cursor);
-	args[2] = "include_entities";
-	args[3] = "true";
-	args[4] = "tweet_mode";
-	args[5] = "extended";
-	if (td->timeline_id) {
-		args[6] = "since_id";
-		args[7] = g_strdup_printf("%" G_GUINT64_FORMAT, td->timeline_id);
-	}
-
-	if (twitter_http(ic, TWITTER_HOME_TIMELINE_URL, twitter_http_get_home_timeline, ic, 0, args,
-	                 td->timeline_id ? 8 : 6) == NULL) {
-		if (++td->http_fails >= 5) {
-			imcb_error(ic, "Could not retrieve %s: %s",
-			           TWITTER_HOME_TIMELINE_URL, "connection failed");
-		}
-		td->flags |= TWITTER_GOT_TIMELINE;
-		twitter_flush_timeline(ic);
-	}
-
-	g_free(args[1]);
-	if (td->timeline_id) {
-		g_free(args[7]);
-	}
-}
-
-/**
- * Get mentions.
- */
-static void twitter_get_mentions(struct im_connection *ic, gint64 next_cursor)
-{
-	struct twitter_data *td = ic->proto_data;
-
-	txl_free(td->mentions_obj);
-	td->mentions_obj = NULL;
-	td->flags &= ~TWITTER_GOT_MENTIONS;
-
-	char *args[8];
-	args[0] = "cursor";
-	args[1] = g_strdup_printf("%" G_GINT64_FORMAT, next_cursor);
-	args[2] = "include_entities";
-	args[3] = "true";
-	if (td->timeline_id) {
-		args[4] = "since_id";
-		args[5] = g_strdup_printf("%" G_GUINT64_FORMAT, td->timeline_id);
-	} else {
-		args[4] = "count";
-		args[5] = g_strdup_printf("%d", set_getint(&ic->acc->set, "show_old_mentions"));
-	}
-	args[6] = "tweet_mode";
-	args[7] = "extended";
-
-	if (twitter_http(ic, TWITTER_MENTIONS_URL, twitter_http_get_mentions,
-	                 ic, 0, args, 8) == NULL) {
-		if (++td->http_fails >= 5) {
-			imcb_error(ic, "Could not retrieve %s: %s",
-			           TWITTER_MENTIONS_URL, "connection failed");
-		}
-		td->flags |= TWITTER_GOT_MENTIONS;
-		twitter_flush_timeline(ic);
-	}
-
-	g_free(args[1]);
-	g_free(args[5]);
-}
-
-/**
- * Callback for getting the home timeline.
- */
-static void twitter_http_get_home_timeline(struct http_request *req)
-{
-	struct im_connection *ic = req->data;
-	struct twitter_data *td;
-	json_value *parsed;
-	struct twitter_xml_list *txl;
-
-	// Check if the connection is still active.
-	if (!g_slist_find(twitter_connections, ic)) {
-		return;
-	}
-
-	td = ic->proto_data;
-
-	// The root <statuses> node should hold the list of statuses <status>
-	if (!(parsed = twitter_parse_response(ic, req))) {
-		goto end;
-	}
-
-	txl = g_new0(struct twitter_xml_list, 1);
-	txl->list = NULL;
-
-	twitter_xt_get_status_list(ic, parsed, txl);
-	json_value_free(parsed);
-
-	td->home_timeline_obj = txl;
-
-end:
-	if (!g_slist_find(twitter_connections, ic)) {
-		return;
-	}
-
-	td->flags |= TWITTER_GOT_TIMELINE;
-
-	twitter_flush_timeline(ic);
-}
-
-/**
- * Callback for getting mentions.
- */
-static void twitter_http_get_mentions(struct http_request *req)
-{
-	struct im_connection *ic = req->data;
-	struct twitter_data *td;
-	json_value *parsed;
-	struct twitter_xml_list *txl;
-
-	// Check if the connection is still active.
-	if (!g_slist_find(twitter_connections, ic)) {
-		return;
-	}
-
-	td = ic->proto_data;
-
-	// The root <statuses> node should hold the list of statuses <status>
-	if (!(parsed = twitter_parse_response(ic, req))) {
-		goto end;
-	}
-
-	txl = g_new0(struct twitter_xml_list, 1);
-	txl->list = NULL;
-
-	twitter_xt_get_status_list(ic, parsed, txl);
-	json_value_free(parsed);
-
-	td->mentions_obj = txl;
-
-end:
-	if (!g_slist_find(twitter_connections, ic)) {
-		return;
-	}
-
-	td->flags |= TWITTER_GOT_MENTIONS;
-
-	twitter_flush_timeline(ic);
-}
-
-/**
- * Callback to use after sending a POST request to twitter.
- * (Generic, used for a few kinds of queries.)
- */
-static void twitter_http_post(struct http_request *req)
-{
-	struct im_connection *ic = req->data;
-	struct twitter_data *td;
-	json_value *parsed, *id;
-
-	// Check if the connection is still active.
-	if (!g_slist_find(twitter_connections, ic)) {
-		return;
-	}
-
-	td = ic->proto_data;
-	td->last_status_id = 0;
-
-	if (!(parsed = twitter_parse_response(ic, req))) {
-		return;
-	}
-
-	if ((id = json_o_get(parsed, "id")) && id->type == json_integer) {
-		td->last_status_id = id->u.integer;
-	}
-
-	json_value_free(parsed);
-
-	if (req->flags & TWITTER_HTTP_USER_ACK) {
-		twitter_log(ic, "Command processed successfully");
-	}
-}
-
-/**
- * Function to POST a new status to twitter.
- */
-void twitter_post_status(struct im_connection *ic, char *msg, guint64 in_reply_to, gboolean auto_populate_reply_metadata)
-{
-	size_t args_len = 0;
-	char * in_reply_to_str = NULL;
-	char * place_id_str = NULL;
-	gboolean in_korea = (set_getbool(&ic->acc->set, "in_korea") && !in_reply_to);
-
-	args_len = 2; /* "status", msg */
-	if (in_reply_to) {
-		args_len += 2;
-	}
-	if (auto_populate_reply_metadata) {
-		args_len += 2;
-	}
-	if (in_korea) {
-		args_len += 2;
-	}
-
-	char **args = g_new0(char *, args_len);
-	args_len = 0;
-	args[args_len++] = "status";
-	args[args_len++] = msg;
-	if (in_reply_to) {
-		in_reply_to_str = g_strdup_printf("%" G_GUINT64_FORMAT, in_reply_to);
-		args[args_len++] = "in_reply_to_status_id";
-		args[args_len++] = in_reply_to_str;
-	}
-	if (auto_populate_reply_metadata) {
-		args[args_len++] = "auto_populate_reply_metadata";
-		args[args_len++] = "true";
-	}
-	if (in_korea) {
-		place_id_str = g_strdup("c999e6a453e9ef72");
-		args[args_len++] = "place_id";
-		args[args_len++] = place_id_str;
-		in_reply_to = 1;
-	}
-
-	twitter_http(ic, TWITTER_STATUS_UPDATE_URL, twitter_http_post, ic, 1,
-	             args, args_len);
-	if (in_reply_to_str) {
-		g_free(in_reply_to_str);
-	}
-	if (place_id_str) {
-		g_free(place_id_str);
-	}
-	g_free(args);
-}
-
-
-/**
- * Function to POST a new message to twitter.
- */
-void twitter_direct_messages_new(struct im_connection *ic, char *who, char *msg)
-{
-	char *args[4];
-
-	args[0] = "screen_name";
-	args[1] = who;
-	args[2] = "text";
-	args[3] = msg;
-	// Use the same callback as for twitter_post_status, since it does basically the same.
-	twitter_http(ic, TWITTER_DIRECT_MESSAGES_NEW_URL, twitter_http_post, ic, 1, args, 4);
-}
-
-void twitter_friendships_create_destroy(struct im_connection *ic, char *who, int create)
-{
-	char *args[2];
-
-	args[0] = "screen_name";
-	args[1] = who;
-	twitter_http(ic, create ? TWITTER_FRIENDSHIPS_CREATE_URL : TWITTER_FRIENDSHIPS_DESTROY_URL,
-	             twitter_http_post, ic, 1, args, 2);
-}
-
-/**
- * Mute or unmute a user
- */
-void twitter_mute_create_destroy(struct im_connection *ic, char *who, int create)
-{
-	char *args[2];
-
-	args[0] = "screen_name";
-	args[1] = who;
-	twitter_http(ic, create ? TWITTER_MUTES_CREATE_URL : TWITTER_MUTES_DESTROY_URL,
-		     twitter_http_post, ic, 1, args, 2);
-}
-
-void twitter_status_destroy(struct im_connection *ic, guint64 id)
-{
-	char *url;
-
-	url = g_strdup_printf("%s%" G_GUINT64_FORMAT "%s",
-	                      TWITTER_STATUS_DESTROY_URL, id, ".json");
-	twitter_http_f(ic, url, twitter_http_post, ic, 1, NULL, 0,
-	               TWITTER_HTTP_USER_ACK);
-	g_free(url);
-}
-
-void twitter_status_retweet(struct im_connection *ic, guint64 id)
-{
-	char *url;
-
-	url = g_strdup_printf("%s%" G_GUINT64_FORMAT "%s",
-	                      TWITTER_STATUS_RETWEET_URL, id, ".json");
-	twitter_http_f(ic, url, twitter_http_post, ic, 1, NULL, 0,
-	               TWITTER_HTTP_USER_ACK);
-	g_free(url);
-}
-
-/**
- * Report a user for sending spam.
- */
-void twitter_report_spam(struct im_connection *ic, char *screen_name)
-{
-	char *args[2] = {
-		"screen_name",
-		NULL,
-	};
-
-	args[1] = screen_name;
-	twitter_http_f(ic, TWITTER_REPORT_SPAM_URL, twitter_http_post,
-	               ic, 1, args, 2, TWITTER_HTTP_USER_ACK);
-}
-
-/**
- * Favourite a tweet.
- */
-void twitter_favourite_tweet(struct im_connection *ic, guint64 id)
-{
-	char *args[2] = {
-		"id",
-		NULL,
-	};
-
-	args[1] = g_strdup_printf("%" G_GUINT64_FORMAT, id);
-	twitter_http_f(ic, TWITTER_FAVORITE_CREATE_URL, twitter_http_post,
-	               ic, 1, args, 2, TWITTER_HTTP_USER_ACK);
-	g_free(args[1]);
-}
-
-static void twitter_http_status_show_url(struct http_request *req)
-{
-	struct im_connection *ic = req->data;
-	json_value *parsed, *id;
-	const char *name;
-
-	// Check if the connection is still active.
-	if (!g_slist_find(twitter_connections, ic)) {
-		return;
-	}
-
-	if (!(parsed = twitter_parse_response(ic, req))) {
-		return;
-	}
-
-	/* for the parson branch:
-	name = json_object_dotget_string(json_object(parsed), "user.screen_name");
-	id = json_object_get_integer(json_object(parsed), "id");
-	*/
-
-	name = json_o_str(json_o_get(parsed, "user"), "screen_name");
-	id = json_o_get(parsed, "id");
-
-	if (name && id && id->type == json_integer) {
-		twitter_log(ic, "https://twitter.com/%s/status/%" G_GUINT64_FORMAT, name, id->u.integer);
-	} else {
-		twitter_log(ic, "Error: could not fetch tweet url.");
-	}
-
-	json_value_free(parsed);
-}
-
-void twitter_status_show_url(struct im_connection *ic, guint64 id)
-{
-	char *url = g_strdup_printf("%s%" G_GUINT64_FORMAT "%s", TWITTER_STATUS_SHOW_URL, id, ".json");
-	twitter_http(ic, url, twitter_http_status_show_url, ic, 0, NULL, 0);
-	g_free(url);
-}
Index: otocols/twitter/twitter_lib.h
===================================================================
--- protocols/twitter/twitter_lib.h	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,109 +1,0 @@
-/***************************************************************************\
-*                                                                           *
-*  BitlBee - An IRC to IM gateway                                           *
-*  Simple module to facilitate twitter functionality.                       *
-*                                                                           *
-*  Copyright 2009 Geert Mulders <g.c.w.m.mulders@gmail.com>                 *
-*                                                                           *
-*  This library is free software; you can redistribute it and/or            *
-*  modify it under the terms of the GNU Lesser General Public               *
-*  License as published by the Free Software Foundation, version            *
-*  2.1.                                                                     *
-*                                                                           *
-*  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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           *
-*                                                                           *
-****************************************************************************/
-
-
-#ifndef _TWITTER_LIB_H
-#define _TWITTER_LIB_H
-
-#include "nogaim.h"
-#include "twitter_http.h"
-
-#define TWITTER_API_URL "https://api.twitter.com/1.1"
-
-/* Status URLs */
-#define TWITTER_STATUS_UPDATE_URL "/statuses/update.json"
-#define TWITTER_STATUS_SHOW_URL "/statuses/show/"
-#define TWITTER_STATUS_DESTROY_URL "/statuses/destroy/"
-#define TWITTER_STATUS_RETWEET_URL "/statuses/retweet/"
-
-/* Timeline URLs */
-#define TWITTER_PUBLIC_TIMELINE_URL "/statuses/public_timeline.json"
-#define TWITTER_FEATURED_USERS_URL "/statuses/featured.json"
-#define TWITTER_FRIENDS_TIMELINE_URL "/statuses/friends_timeline.json"
-#define TWITTER_HOME_TIMELINE_URL "/statuses/home_timeline.json"
-#define TWITTER_MENTIONS_URL "/statuses/mentions_timeline.json"
-#define TWITTER_USER_TIMELINE_URL "/statuses/user_timeline.json"
-
-/* Users URLs */
-#define TWITTER_USERS_LOOKUP_URL "/users/lookup.json"
-
-/* Direct messages URLs */
-#define TWITTER_DIRECT_MESSAGES_URL "/direct_messages.json"
-#define TWITTER_DIRECT_MESSAGES_NEW_URL "/direct_messages/new.json"
-#define TWITTER_DIRECT_MESSAGES_SENT_URL "/direct_messages/sent.json"
-#define TWITTER_DIRECT_MESSAGES_DESTROY_URL "/direct_messages/destroy/"
-
-/* Friendships URLs */
-#define TWITTER_FRIENDSHIPS_CREATE_URL "/friendships/create.json"
-#define TWITTER_FRIENDSHIPS_DESTROY_URL "/friendships/destroy.json"
-#define TWITTER_FRIENDSHIPS_SHOW_URL "/friendships/show.json"
-
-/* Social graphs URLs */
-#define TWITTER_FRIENDS_IDS_URL "/friends/ids.json"
-#define TWITTER_FOLLOWERS_IDS_URL "/followers/ids.json"
-#define TWITTER_MUTES_IDS_URL "/mutes/users/ids.json"
-#define TWITTER_NORETWEETS_IDS_URL "/friendships/no_retweets/ids.json"
-
-/* Account URLs */
-#define TWITTER_ACCOUNT_RATE_LIMIT_URL "/account/rate_limit_status.json"
-
-/* Favorites URLs */
-#define TWITTER_FAVORITES_GET_URL "/favorites.json"
-#define TWITTER_FAVORITE_CREATE_URL "/favorites/create.json"
-#define TWITTER_FAVORITE_DESTROY_URL "/favorites/destroy.json"
-
-/* Block URLs */
-#define TWITTER_BLOCKS_CREATE_URL "/blocks/create/"
-#define TWITTER_BLOCKS_DESTROY_URL "/blocks/destroy/"
-
-/* Mute URLs */
-#define TWITTER_MUTES_CREATE_URL "/mutes/users/create.json"
-#define TWITTER_MUTES_DESTROY_URL "/mutes/users/destroy.json"
-
-/* Report spam */
-#define TWITTER_REPORT_SPAM_URL "/users/report_spam.json"
-
-/* Stream URLs */
-#define TWITTER_USER_STREAM_URL "https://userstream.twitter.com/1.1/user.json"
-#define TWITTER_FILTER_STREAM_URL "https://stream.twitter.com/1.1/statuses/filter.json"
-
-gboolean twitter_open_stream(struct im_connection *ic);
-gboolean twitter_open_filter_stream(struct im_connection *ic);
-gboolean twitter_get_timeline(struct im_connection *ic, gint64 next_cursor);
-void twitter_get_friends_ids(struct im_connection *ic, gint64 next_cursor);
-void twitter_get_mutes_ids(struct im_connection *ic, gint64 next_cursor);
-void twitter_get_noretweets_ids(struct im_connection *ic, gint64 next_cursor);
-void twitter_get_statuses_friends(struct im_connection *ic, gint64 next_cursor);
-
-void twitter_post_status(struct im_connection *ic, char *msg, guint64 in_reply_to, gboolean auto_populate_reply_metadata);
-void twitter_direct_messages_new(struct im_connection *ic, char *who, char *message);
-void twitter_friendships_create_destroy(struct im_connection *ic, char *who, int create);
-void twitter_mute_create_destroy(struct im_connection *ic, char *who, int create);
-void twitter_status_destroy(struct im_connection *ic, guint64 id);
-void twitter_status_retweet(struct im_connection *ic, guint64 id);
-void twitter_report_spam(struct im_connection *ic, char *screen_name);
-void twitter_favourite_tweet(struct im_connection *ic, guint64 id);
-void twitter_status_show_url(struct im_connection *ic, guint64 id);
-
-#endif //_TWITTER_LIB_H
-
Index: protocols/util.c
===================================================================
--- protocols/util.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/util.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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, "&amp;" );
+				break;
+			case '<':
+				ret = g_string_append( ret, "&lt;" );
+				break;
+			case '>':
+				ret = g_string_append( ret, "&gt;" );
+				break;
+			case '"':
+				ret = g_string_append( ret, "&quot;" );
+				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 );
+}
Index: protocols/yahoo/Makefile
===================================================================
--- protocols/yahoo/Makefile	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/yahoo/Makefile	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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
Index: protocols/yahoo/crypt.c
===================================================================
--- protocols/yahoo/crypt.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/yahoo/crypt.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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;
+}
Index: protocols/yahoo/libyahoo2.c
===================================================================
--- protocols/yahoo/libyahoo2.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/yahoo/libyahoo2.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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;
+}
+
Index: protocols/yahoo/yahoo.c
===================================================================
--- protocols/yahoo/yahoo.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/yahoo/yahoo.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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 )
+{
+}
Index: protocols/yahoo/yahoo2.h
===================================================================
--- protocols/yahoo/yahoo2.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/yahoo/yahoo2.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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
Index: protocols/yahoo/yahoo2_callbacks.h
===================================================================
--- protocols/yahoo/yahoo2_callbacks.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/yahoo/yahoo2_callbacks.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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
Index: protocols/yahoo/yahoo2_types.h
===================================================================
--- protocols/yahoo/yahoo2_types.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/yahoo/yahoo2_types.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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
Index: protocols/yahoo/yahoo_debug.h
===================================================================
--- protocols/yahoo/yahoo_debug.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/yahoo/yahoo_debug.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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"); }
+
+
Index: protocols/yahoo/yahoo_fn.c
===================================================================
--- protocols/yahoo/yahoo_fn.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/yahoo/yahoo_fn.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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;
+}
Index: protocols/yahoo/yahoo_fn.h
===================================================================
--- protocols/yahoo/yahoo_fn.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/yahoo/yahoo_fn.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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 );
Index: protocols/yahoo/yahoo_httplib.c
===================================================================
--- protocols/yahoo/yahoo_httplib.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/yahoo/yahoo_httplib.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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);
+}
+
Index: protocols/yahoo/yahoo_httplib.h
===================================================================
--- protocols/yahoo/yahoo_httplib.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/yahoo/yahoo_httplib.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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
Index: protocols/yahoo/yahoo_list.c
===================================================================
--- protocols/yahoo/yahoo_list.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/yahoo/yahoo_list.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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;
+	}
+		
+}
Index: protocols/yahoo/yahoo_list.h
===================================================================
--- protocols/yahoo/yahoo_list.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/yahoo/yahoo_list.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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
Index: protocols/yahoo/yahoo_util.c
===================================================================
--- protocols/yahoo/yahoo_util.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/yahoo/yahoo_util.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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
Index: protocols/yahoo/yahoo_util.h
===================================================================
--- protocols/yahoo/yahoo_util.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ protocols/yahoo/yahoo_util.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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
+
Index: query.c
===================================================================
--- query.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ query.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -1,3 +1,3 @@
-/********************************************************************\
+  /********************************************************************\
   * BitlBee -- An IRC to other IM-networks gateway                     *
   *                                                                    *
@@ -20,6 +20,6 @@
   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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
 */
 
@@ -27,171 +27,142 @@
 #include "bitlbee.h"
 
-static void query_display(irc_t *irc, query_t *q);
-static query_t *query_default(irc_t *irc);
+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 im_connection *ic, char *question,
-                   query_callback yes, query_callback no, query_callback free,
-                   void *data)
+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->ic = ic;
-	q->question = g_strdup(question);
+	query_t *q = g_new0( query_t, 1 );
+	
+	q->gc = gc;
+	q->question = g_strdup( question );
 	q->yes = yes;
 	q->no = no;
-	q->free = free;
 	q->data = data;
-
-	if (strchr(irc->umode, 'b') != NULL) {
-		char *s;
-
-		/* At least for the machine-parseable version, get rid of
-		   newlines to make "parsing" easier. */
-		for (s = q->question; *s; s++) {
-			if (*s == '\r' || *s == '\n') {
-				*s = ' ';
-			}
-		}
+	
+	if( irc->queries )
+	{
+		query_t *l = irc->queries;
+		
+		while( l->next ) l = l->next;
+		l->next = q;
 	}
-
-	if (irc->queries) {
-		query_t *l = irc->queries;
-
-		while (l->next) {
-			l = l->next;
-		}
-		l->next = q;
-	} else {
+	else
+	{
 		irc->queries = q;
 	}
-
-	if (g_strcasecmp(set_getstr(&irc->b->set, "query_order"), "lifo") == 0 || irc->queries == q) {
-		query_display(irc, q);
-	}
-
-	return(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)
+void query_del( irc_t *irc, query_t *q )
 {
 	query_t *l;
-
-	if (irc->queries == q) {
+	
+	if( irc->queries == q )
+	{
 		irc->queries = q->next;
-	} else {
-		for (l = irc->queries; l; l = l->next) {
-			if (l->next == q) {
+	}
+	else
+	{
+		for( l = irc->queries; l; l = l->next )
+		{
+			if( l->next == q )
+			{
 				l->next = q->next;
 				break;
 			}
 		}
-
-		if (!l) {
+		
+		if( !l )
 			return; /* Hrmmm... */
-		}
 	}
-
-	g_free(q->question);
-	if (q->free && q->data) {
-		q->free(q->data);
-	}
-	g_free(q);
+	
+	g_free( q->question );
+	if( q->data ) g_free( q->data ); /* Memory leak... */
+	g_free( q );
 }
 
-void query_del_by_conn(irc_t *irc, struct im_connection *ic)
+void query_del_by_gc( irc_t *irc, struct gaim_connection *gc )
 {
 	query_t *q, *n, *def;
 	int count = 0;
-
-	if (!ic) {
-		return;
-	}
-
+	
 	q = irc->queries;
-	def = query_default(irc);
-
-	while (q) {
-		if (q->ic == ic) {
+	def = query_default( irc );
+	
+	while( q )
+	{
+		if( q->gc == gc )
+		{
 			n = q->next;
-			query_del(irc, q);
+			query_del( irc, q );
 			q = n;
-
-			count++;
-		} else {
+			
+			count ++;
+		}
+		else
+		{
 			q = q->next;
 		}
 	}
-
-	if (count > 0) {
-		imcb_log(ic, "Flushed %d unanswered question(s) for this connection.", count);
-	}
-
-	q = query_default(irc);
-	if (q && q != def) {
-		query_display(irc, q);
-	}
+	
+	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)
+void query_answer( irc_t *irc, query_t *q, int ans )
 {
 	int disp = 0;
-
-	if (!q) {
-		q = query_default(irc);
+	
+	if( !q )
+	{
+		q = query_default( irc );
 		disp = 1;
 	}
-	if (ans) {
-		if (q->ic) {
-			imcb_log(q->ic, "Accepted: %s", q->question);
-		} else {
-			irc_rootmsg(irc, "Accepted: %s", q->question);
-		}
-		if (q->yes) {
-			q->yes(q->data);
-		}
-	} else {
-		if (q->ic) {
-			imcb_log(q->ic, "Rejected: %s", q->question);
-		} else {
-			irc_rootmsg(irc, "Rejected: %s", q->question);
-		}
-		if (q->no) {
-			q->no(q->data);
-		}
+	
+	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);
-	}
+	
+	query_del( irc, q );
+	
+	if( disp && ( q = query_default( irc ) ) )
+		query_display( irc, q );
 }
 
-static void query_display(irc_t *irc, query_t *q)
+static void query_display( irc_t *irc, query_t *q )
 {
-	if (q->ic) {
-		imcb_log(q->ic,
-		         "New request: %s\nYou can use the \2yes\2/\2no\2 commands to accept/reject this request.",
-		         q->question);
-	} else {
-		irc_rootmsg(irc,
-		            "New request: %s\nYou can use the \2yes\2/\2no\2 commands to accept/reject this request.",
-		            q->question);
-	}
+	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)
+static query_t *query_default( irc_t *irc )
 {
 	query_t *q;
-
-	if (g_strcasecmp(set_getstr(&irc->b->set, "query_order"), "fifo") == 0) {
+	
+	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);
+	else
+		for( q = irc->queries; q && q->next; q = q->next );
+	
+	return( q );
 }
Index: query.h
===================================================================
--- query.h	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ query.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -1,3 +1,3 @@
-/********************************************************************\
+  /********************************************************************\
   * BitlBee -- An IRC to other IM-networks gateway                     *
   *                                                                    *
@@ -20,6 +20,6 @@
   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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
 */
 
@@ -27,20 +27,18 @@
 #define _QUERY_H
 
-typedef void (*query_callback) (void *data);
-
-typedef struct query {
-	struct im_connection *ic;
+typedef struct query
+{
+	struct gaim_connection *gc;
 	char *question;
-	query_callback yes, no, free;
+	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 im_connection *ic, char *question,
-                   query_callback yes, query_callback no, query_callback free,
-                   void *data);
-void query_del(irc_t *irc, query_t *q);
-void query_del_by_conn(irc_t *irc, struct im_connection *ic);
-void query_answer(irc_t *irc, query_t *q, int ans);
+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
Index: ot_commands.c
===================================================================
--- root_commands.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,1661 +1,0 @@
-/********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2013 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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#define BITLBEE_CORE
-#include "commands.h"
-#include "bitlbee.h"
-#include "help.h"
-#include "ipc.h"
-
-void root_command_string(irc_t *irc, char *command)
-{
-	root_command(irc, split_command_parts(command, 0));
-}
-
-#define MIN_ARGS(x, y ...)                                                    \
-	do                                                                     \
-	{                                                                      \
-		int blaat;                                                     \
-		for (blaat = 0; blaat <= x; blaat++) {                         \
-			if (cmd[blaat] == NULL)                               \
-			{                                                      \
-				irc_rootmsg(irc, "Not enough parameters given (need %d).", x); \
-				return y;                                      \
-			} }                                                      \
-	} while (0)
-
-void root_command(irc_t *irc, char *cmd[])
-{
-	int i, len;
-
-	if (!cmd[0]) {
-		return;
-	}
-
-	len = strlen(cmd[0]);
-	for (i = 0; root_commands[i].command; i++) {
-		if (g_strncasecmp(root_commands[i].command, cmd[0], len) == 0) {
-			if (root_commands[i + 1].command &&
-			    g_strncasecmp(root_commands[i + 1].command, cmd[0], len) == 0) {
-				/* Only match on the first letters if the match is unique. */
-				break;
-			}
-
-			MIN_ARGS(root_commands[i].required_parameters);
-
-			root_commands[i].execute(irc, cmd);
-			return;
-		}
-	}
-
-	irc_rootmsg(irc, "Unknown command: %s. Please use \x02help commands\x02 to get a list of available commands.",
-	            cmd[0]);
-}
-
-static void 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_rootmsg(irc, "%s", s);
-		g_free(s);
-	} else {
-		irc_rootmsg(irc, "Error opening helpfile.");
-	}
-}
-
-static void cmd_account(irc_t *irc, char **cmd);
-static void bitlbee_whatsnew(irc_t *irc);
-
-static void cmd_identify(irc_t *irc, char **cmd)
-{
-	storage_status_t status;
-	gboolean load = TRUE;
-	char *password = cmd[1];
-
-	if (irc->status & USTATUS_IDENTIFIED) {
-		irc_rootmsg(irc, "You're already logged in.");
-		return;
-	}
-
-	if (cmd[1] == NULL) {
-	} else if (strncmp(cmd[1], "-no", 3) == 0) {
-		load = FALSE;
-		password = cmd[2];
-		if (password == NULL) {
-			irc->status |= OPER_HACK_IDENTIFY_NOLOAD;
-		}
-	} else if (strncmp(cmd[1], "-force", 6) == 0) {
-		password = cmd[2];
-		if (password == NULL) {
-			irc->status |= OPER_HACK_IDENTIFY_FORCE;
-		}
-	} else if (irc->b->accounts != NULL) {
-		irc_rootmsg(irc,
-		            "You're trying to identify yourself, but already have "
-		            "at least one IM account set up. "
-		            "Use \x02identify -noload\x02 or \x02identify -force\x02 "
-		            "instead (see \x02help identify\x02).");
-		return;
-	}
-
-	if (password == NULL) {
-		irc_rootmsg(irc, "About to identify, use /OPER to enter the password");
-		irc->status |= OPER_HACK_IDENTIFY;
-		return;
-	}
-
-	status = auth_check_pass(irc, irc->user->nick, password);
-	if (load && (status == STORAGE_OK)) {
-		status = storage_load(irc, password);
-	}
-
-	switch (status) {
-	case STORAGE_INVALID_PASSWORD:
-		irc_rootmsg(irc, "Incorrect password");
-		break;
-	case STORAGE_NO_SUCH_USER:
-		irc_rootmsg(irc, "The nick is (probably) not registered");
-		break;
-	case STORAGE_OK:
-		irc_rootmsg(irc, "Password accepted%s",
-		            load ? ", settings and accounts loaded" : "");
-		irc->status |= USTATUS_IDENTIFIED;
-		irc_umode_set(irc, "+R", 1);
-
-		if (irc->caps & CAP_SASL) {
-			irc_user_t *iu = irc->user;
-			irc_send_num(irc, 900, "%s!%s@%s %s :You are now logged in as %s",
-				iu->nick, iu->user, iu->host, iu->nick, iu->nick);
-		}
-
-		bitlbee_whatsnew(irc);
-
-		/* The following code is a bit hairy now. With takeover
-		   support, we shouldn't immediately auto_connect in case
-		   we're going to offer taking over an existing session.
-		   Do it in 200ms since that should give the parent process
-		   enough time to come back to us. */
-		if (load) {
-			irc_channel_auto_joins(irc, NULL);
-			if (!set_getbool(&irc->default_channel->set, "auto_join")) {
-				irc_channel_del_user(irc->default_channel, irc->user,
-				                     IRC_CDU_PART, "auto_join disabled "
-				                     "for this channel.");
-			}
-			if (set_getbool(&irc->b->set, "auto_connect")) {
-				irc->login_source_id = b_timeout_add(200,
-				                                     cmd_identify_finish, irc);
-			}
-		}
-
-		/* If ipc_child_identify() returns FALSE, it means we're
-		   already sure that there's no takeover target (only
-		   possible in 1-process daemon mode). Start auto_connect
-		   immediately. */
-		if (!ipc_child_identify(irc) && load) {
-			cmd_identify_finish(irc, 0, 0);
-		}
-
-		break;
-	case STORAGE_OTHER_ERROR:
-	default:
-		irc_rootmsg(irc, "Unknown error while loading configuration");
-		break;
-	}
-}
-
-gboolean cmd_identify_finish(gpointer data, gint fd, b_input_condition cond)
-{
-	char *account_on[] = { "account", "on", NULL };
-	irc_t *irc = data;
-
-	if (set_getbool(&irc->b->set, "auto_connect")) {
-		cmd_account(irc, account_on);
-	}
-
-	b_event_remove(irc->login_source_id);
-	irc->login_source_id = -1;
-	return FALSE;
-}
-
-static void cmd_register(irc_t *irc, char **cmd)
-{
-	char s[16];
-
-	if (global.conf->authmode == AUTHMODE_REGISTERED) {
-		irc_rootmsg(irc, "This server does not allow registering new accounts");
-		return;
-	}
-
-	if (cmd[1] == NULL) {
-		irc_rootmsg(irc, "About to register, use /OPER to enter the password");
-		irc->status |= OPER_HACK_REGISTER;
-		return;
-	}
-
-	switch (storage_save(irc, cmd[1], FALSE)) {
-	case STORAGE_ALREADY_EXISTS:
-		irc_rootmsg(irc, "Nick is already registered");
-		break;
-
-	case STORAGE_OK:
-		irc_rootmsg(irc, "Account successfully created");
-		irc_setpass(irc, cmd[1]);
-		irc->status |= USTATUS_IDENTIFIED;
-		irc_umode_set(irc, "+R", 1);
-
-		if (irc->caps & CAP_SASL) {
-			irc_user_t *iu = irc->user;
-			irc_send_num(irc, 900, "%s!%s@%s %s :You are now logged in as %s",
-				iu->nick, iu->user, iu->host, iu->nick, iu->nick);
-		}
-
-		/* Set this var now, or anyone who logs in to his/her
-		   newly created account for the first time gets the
-		   whatsnew story. */
-		g_snprintf(s, sizeof(s), "%d", BITLBEE_VERSION_CODE);
-		set_setstr(&irc->b->set, "last_version", s);
-		break;
-
-	default:
-		irc_rootmsg(irc, "Error registering");
-		break;
-	}
-}
-
-static void cmd_drop(irc_t *irc, char **cmd)
-{
-	storage_status_t status;
-
-	status = auth_check_pass(irc, irc->user->nick, cmd[1]);
-	if (status == STORAGE_OK) {
-		status = storage_remove(irc->user->nick);
-	}
-
-	switch (status) {
-	case STORAGE_NO_SUCH_USER:
-		irc_rootmsg(irc, "That account does not exist");
-		break;
-	case STORAGE_INVALID_PASSWORD:
-		irc_rootmsg(irc, "Password invalid");
-		break;
-	case STORAGE_OK:
-		irc_setpass(irc, NULL);
-		irc->status &= ~USTATUS_IDENTIFIED;
-		irc_umode_set(irc, "-R", 1);
-		irc_rootmsg(irc, "Account `%s' removed", irc->user->nick);
-		break;
-	default:
-		irc_rootmsg(irc, "Error: `%d'", status);
-		break;
-	}
-}
-
-static void cmd_save(irc_t *irc, char **cmd)
-{
-	if ((irc->status & USTATUS_IDENTIFIED) == 0) {
-		irc_rootmsg(irc, "Please create an account first (see \x02help register\x02)");
-	} else if (storage_save(irc, NULL, TRUE) == STORAGE_OK) {
-		irc_rootmsg(irc, "Configuration saved");
-	} else {
-		irc_rootmsg(irc, "Configuration could not be saved!");
-	}
-}
-
-static void cmd_showset(irc_t *irc, set_t **head, char *key)
-{
-	set_t *set;
-	char *val;
-
-	if ((val = set_getstr(head, key))) {
-		irc_rootmsg(irc, "%s = `%s'", key, val);
-	} else if (!(set = set_find(head, key))) {
-		irc_rootmsg(irc, "Setting `%s' does not exist.", key);
-		if (*head == irc->b->set) {
-			irc_rootmsg(irc, "It might be an account or channel setting. "
-			            "See \x02help account set\x02 and \x02help channel set\x02.");
-		}
-	} else if (set->flags & SET_PASSWORD) {
-		irc_rootmsg(irc, "%s = `********' (hidden)", key);
-	} else {
-		irc_rootmsg(irc, "%s is empty", key);
-	}
-}
-
-typedef set_t** (*cmd_set_findhead)(irc_t*, char*);
-typedef int (*cmd_set_checkflags)(irc_t*, set_t *set);
-
-static int cmd_set_real(irc_t *irc, char **cmd, set_t **head, cmd_set_checkflags checkflags)
-{
-	char *set_name = NULL, *value = NULL;
-	gboolean del = FALSE;
-
-	if (cmd[1] && g_strncasecmp(cmd[1], "-del", 4) == 0) {
-		MIN_ARGS(2, 0);
-		set_name = cmd[2];
-		del = TRUE;
-	} else {
-		set_name = cmd[1];
-		value = cmd[2];
-	}
-
-	if (set_name && (value || del)) {
-		set_t *s = set_find(head, set_name);
-		int st;
-
-		if (s && s->flags & SET_LOCKED) {
-			irc_rootmsg(irc, "This setting can not be changed");
-			return 0;
-		}
-		if (s && checkflags && checkflags(irc, s) == 0) {
-			return 0;
-		}
-
-		if (del) {
-			st = set_reset(head, set_name);
-		} else {
-			st = set_setstr(head, set_name, value);
-		}
-
-		if (set_getstr(head, set_name) == NULL &&
-		    set_find(head, set_name)) {
-			/* This happens when changing the passwd, for example.
-			   Showing these msgs instead gives slightly clearer
-			   feedback. */
-			if (st) {
-				irc_rootmsg(irc, "Setting changed successfully");
-			} else {
-				irc_rootmsg(irc, "Failed to change setting");
-			}
-		} else {
-			cmd_showset(irc, head, set_name);
-		}
-	} else if (set_name) {
-		cmd_showset(irc, head, set_name);
-	} else {
-		set_t *s = *head;
-		while (s) {
-			if (set_isvisible(s)) {
-				cmd_showset(irc, &s, s->key);
-			}
-			s = s->next;
-		}
-	}
-
-	return 1;
-}
-
-static int cmd_account_set_checkflags(irc_t *irc, set_t *s)
-{
-	account_t *a = s->data;
-
-	if (a->ic && s && s->flags & ACC_SET_OFFLINE_ONLY) {
-		irc_rootmsg(irc, "This setting can only be changed when the account is %s-line", "off");
-		return 0;
-	} else if (!a->ic && s && s->flags & ACC_SET_ONLINE_ONLY) {
-		irc_rootmsg(irc, "This setting can only be changed when the account is %s-line", "on");
-		return 0;
-	} else if (a->flags & ACC_FLAG_LOCKED && s && s->flags & ACC_SET_LOCKABLE) {
-		irc_rootmsg(irc, "This setting can not be changed for locked accounts");
-		return 0;
-	}
-
-	return 1;
-}
-
-static void cmd_account(irc_t *irc, char **cmd)
-{
-	account_t *a;
-	int len;
-
-	if (global.conf->authmode == AUTHMODE_REGISTERED && !(irc->status & USTATUS_IDENTIFIED)) {
-		irc_rootmsg(irc, "This server only accepts registered users");
-		return;
-	}
-
-	len = strlen(cmd[1]);
-
-	if (len >= 1 && g_strncasecmp(cmd[1], "add", len) == 0) {
-		struct prpl *prpl;
-
-		MIN_ARGS(3);
-
-		if (!global.conf->allow_account_add) {
-			irc_rootmsg(irc, "This server does not allow adding new accounts");
-			return;
-		}
-
-		if (cmd[4] == NULL) {
-			for (a = irc->b->accounts; a; a = a->next) {
-				if (strcmp(a->pass, PASSWORD_PENDING) == 0) {
-					irc_rootmsg(irc, "Enter password for account %s "
-					            "first (use /OPER)", a->tag);
-					return;
-				}
-			}
-
-			irc->status |= OPER_HACK_ACCOUNT_PASSWORD;
-		}
-
-		prpl = find_protocol(cmd[2]);
-
-		if (prpl == NULL) {
-			char *msg = explain_unknown_protocol(cmd[2]);
-			irc_rootmsg(irc, "Unknown protocol");
-			irc_rootmsg(irc, "%s", msg);
-			g_free(msg);
-			return;
-		}
-
-		for (a = irc->b->accounts; a; a = a->next) {
-			if (a->prpl == prpl && prpl->handle_cmp(a->user, cmd[3]) == 0) {
-				irc_rootmsg(irc, "Warning: You already have an account with "
-				            "protocol `%s' and username `%s'. Are you accidentally "
-				            "trying to add it twice?", prpl->name, cmd[3]);
-			}
-		}
-
-		a = account_add(irc->b, prpl, cmd[3], cmd[4] ? cmd[4] : PASSWORD_PENDING);
-		if (cmd[5]) {
-			irc_rootmsg(irc, "Warning: Passing a servername/other flags to `account add' "
-			            "is now deprecated. Use `account set' instead.");
-			set_setstr(&a->set, "server", cmd[5]);
-		}
-
-		irc_rootmsg(irc, "Account successfully added with tag %s", a->tag);
-
-		if (cmd[4] == NULL) {
-			set_t *oauth = set_find(&a->set, "oauth");
-			if (oauth && bool2int(set_value(oauth))) {
-				*a->pass = '\0';
-				irc_rootmsg(irc, "No need to enter a password for this "
-				            "account since it's using OAuth");
-			} else if (prpl->options & PRPL_OPT_NO_PASSWORD) {
-				*a->pass = '\0';
-			} else if (prpl->options & PRPL_OPT_PASSWORD_OPTIONAL) {
-				*a->pass = '\0';
-				irc_rootmsg(irc, "Passwords are optional for this account. "
-				            "If you wish to enter the password with /OPER, do "
-				            "account %s set -del password", a->tag);
-			} else {
-				irc_rootmsg(irc, "You can now use the /OPER command to "
-				            "enter the password");
-				if (oauth) {
-					irc_rootmsg(irc, "Alternatively, enable OAuth if "
-					            "the account supports it: account %s "
-					            "set oauth on", a->tag);
-				}
-			}
-		} else if (prpl->options & PRPL_OPT_NO_PASSWORD) {
-			irc_rootmsg(irc, "Note: this account doesn't use password for login");
-		}
-
-		return;
-	} else if (len >= 1 && g_strncasecmp(cmd[1], "list", len) == 0) {
-		int i = 0;
-
-		if (strchr(irc->umode, 'b')) {
-			irc_rootmsg(irc, "Account list:");
-		}
-
-		for (a = irc->b->accounts; a; a = a->next) {
-			char *con = NULL, *protocol = NULL;
-
-			if (a->ic && (a->ic->flags & OPT_LOGGED_IN)) {
-				con = " (connected)";
-			} else if (a->ic) {
-				con = " (connecting)";
-			} else if (a->reconnect) {
-				con = " (awaiting reconnect)";
-			} else {
-				con = "";
-			}
-
-			if (a->prpl == &protocol_missing) {
-				protocol = g_strdup_printf("%s (missing!)", set_getstr(&a->set, "_protocol_name"));
-			} else {
-				protocol = g_strdup(a->prpl->name);
-			}
-
-			irc_rootmsg(irc, "%2d (%s): %s, %s%s", i, a->tag, protocol, a->user, con);
-			g_free(protocol);
-
-			i++;
-		}
-		irc_rootmsg(irc, "End of account list");
-
-		return;
-	} else if (cmd[2]) {
-		/* Try the following two only if cmd[2] == NULL */
-	} else if (len >= 2 && g_strncasecmp(cmd[1], "on", len) == 0) {
-		if (irc->b->accounts) {
-			irc_rootmsg(irc, "Trying to get all accounts connected...");
-
-			for (a = irc->b->accounts; a; a = a->next) {
-				if (!a->ic && a->auto_connect && a->prpl != &protocol_missing) {
-					if (strcmp(a->pass, PASSWORD_PENDING) == 0) {
-						irc_rootmsg(irc, "Enter password for account %s "
-						            "first (use /OPER)", a->tag);
-					} else {
-						account_on(irc->b, a);
-					}
-				}
-			}
-		} else {
-			irc_rootmsg(irc, "No accounts known. Use `account add' to add one.");
-		}
-
-		return;
-	} else if (len >= 2 && g_strncasecmp(cmd[1], "off", len) == 0) {
-		irc_rootmsg(irc, "Deactivating all active (re)connections...");
-
-		for (a = irc->b->accounts; a; a = a->next) {
-			if (a->ic) {
-				account_off(irc->b, a);
-			} else if (a->reconnect) {
-				cancel_auto_reconnect(a);
-			}
-		}
-
-		return;
-	}
-
-	MIN_ARGS(2);
-	len = strlen(cmd[2]);
-
-	/* At least right now, don't accept on/off/set/del as account IDs even
-	   if they're a proper match, since people not familiar with the new
-	   syntax yet may get a confusing/nasty surprise. */
-	if (g_strcasecmp(cmd[1], "on") == 0 ||
-	    g_strcasecmp(cmd[1], "off") == 0 ||
-	    g_strcasecmp(cmd[1], "set") == 0 ||
-	    g_strcasecmp(cmd[1], "del") == 0 ||
-	    (a = account_get(irc->b, cmd[1])) == NULL) {
-		irc_rootmsg(irc, "Could not find account `%s'.", cmd[1]);
-
-		return;
-	}
-
-	if (len >= 1 && g_strncasecmp(cmd[2], "del", len) == 0) {
-		if (a->flags & ACC_FLAG_LOCKED) {
-			irc_rootmsg(irc, "Account is locked, can't delete");
-		}
-		else if (a->ic) {
-			irc_rootmsg(irc, "Account is still logged in, can't delete");
-		} else {
-			account_del(irc->b, a);
-			irc_rootmsg(irc, "Account deleted");
-		}
-	} else if (len >= 2 && g_strncasecmp(cmd[2], "on", len) == 0) {
-		if (a->ic) {
-			irc_rootmsg(irc, "Account already online");
-		} else if (strcmp(a->pass, PASSWORD_PENDING) == 0) {
-			irc_rootmsg(irc, "Enter password for account %s "
-			            "first (use /OPER)", a->tag);
-		} else if (a->prpl == &protocol_missing) {
-			char *proto = set_getstr(&a->set, "_protocol_name");
-			char *msg = explain_unknown_protocol(proto);
-			irc_rootmsg(irc, "Unknown protocol `%s'", proto);
-			irc_rootmsg(irc, "%s", msg);
-			g_free(msg);
-		} else {
-			account_on(irc->b, a);
-		}
-	} else if (len >= 2 && g_strncasecmp(cmd[2], "off", len) == 0) {
-		if (a->ic) {
-			account_off(irc->b, a);
-		} else if (a->reconnect) {
-			cancel_auto_reconnect(a);
-			irc_rootmsg(irc, "Reconnect cancelled");
-		} else {
-			irc_rootmsg(irc, "Account already offline");
-		}
-	} else if (len >= 1 && g_strncasecmp(cmd[2], "set", len) == 0) {
-		cmd_set_real(irc, cmd + 2, &a->set, cmd_account_set_checkflags);
-	} else {
-		irc_rootmsg(irc,
-		            "Unknown command: %s [...] %s. Please use \x02help commands\x02 to get a list of available commands.", "account",
-		            cmd[2]);
-	}
-}
-
-static void cmd_channel(irc_t *irc, char **cmd)
-{
-	irc_channel_t *ic;
-	int len;
-
-	len = strlen(cmd[1]);
-
-	if (len >= 1 && g_strncasecmp(cmd[1], "list", len) == 0) {
-		GSList *l;
-		int i = 0;
-
-		if (strchr(irc->umode, 'b')) {
-			irc_rootmsg(irc, "Channel list:");
-		}
-
-		for (l = irc->channels; l; l = l->next) {
-			irc_channel_t *ic = l->data;
-
-			irc_rootmsg(irc, "%2d. %s, %s channel%s", i, ic->name,
-			            set_getstr(&ic->set, "type"),
-			            ic->flags & IRC_CHANNEL_JOINED ? " (joined)" : "");
-
-			i++;
-		}
-		irc_rootmsg(irc, "End of channel list");
-
-		return;
-	}
-
-	if ((ic = irc_channel_get(irc, cmd[1])) == NULL) {
-		/* If this doesn't match any channel, maybe this is the short
-		   syntax (only works when used inside a channel). */
-		if ((ic = irc->root->last_channel) &&
-		    (len = strlen(cmd[1])) &&
-		    g_strncasecmp(cmd[1], "set", len) == 0) {
-			cmd_set_real(irc, cmd + 1, &ic->set, NULL);
-		} else {
-			irc_rootmsg(irc, "Could not find channel `%s'", cmd[1]);
-		}
-
-		return;
-	}
-
-	MIN_ARGS(2);
-	len = strlen(cmd[2]);
-
-	if (len >= 1 && g_strncasecmp(cmd[2], "set", len) == 0) {
-		cmd_set_real(irc, cmd + 2, &ic->set, NULL);
-	} else if (len >= 1 && g_strncasecmp(cmd[2], "del", len) == 0) {
-		if (!(ic->flags & IRC_CHANNEL_JOINED) &&
-		    ic != ic->irc->default_channel) {
-			irc_rootmsg(irc, "Channel %s deleted.", ic->name);
-			irc_channel_free(ic);
-		} else {
-			irc_rootmsg(irc, "Couldn't remove channel (main channel %s or "
-			            "channels you're still in cannot be deleted).",
-			            irc->default_channel->name);
-		}
-	} else {
-		irc_rootmsg(irc,
-		            "Unknown command: %s [...] %s. Please use \x02help commands\x02 to get a list of available commands.", "channel",
-		            cmd[1]);
-	}
-}
-
-static void cmd_add(irc_t *irc, char **cmd)
-{
-	account_t *a;
-	int add_on_server = 1;
-	char *handle = NULL, *s;
-
-	if (g_strcasecmp(cmd[1], "-tmp") == 0) {
-		MIN_ARGS(3);
-		add_on_server = 0;
-		cmd++;
-	}
-
-	if (!(a = account_get(irc->b, cmd[1]))) {
-		irc_rootmsg(irc, "Invalid account");
-		return;
-	} else if (!(a->ic && (a->ic->flags & OPT_LOGGED_IN))) {
-		irc_rootmsg(irc, "That account is not on-line");
-		return;
-	}
-
-	if (cmd[3]) {
-		if (!nick_ok(irc, cmd[3])) {
-			irc_rootmsg(irc, "The requested nick `%s' is invalid", cmd[3]);
-			return;
-		} else if (irc_user_by_name(irc, cmd[3])) {
-			irc_rootmsg(irc, "The requested nick `%s' already exists", cmd[3]);
-			return;
-		} else {
-			nick_set_raw(a, cmd[2], cmd[3]);
-		}
-	}
-
-	if ((a->flags & ACC_FLAG_HANDLE_DOMAINS) && cmd[2][0] != '_' &&
-	    (!(s = strchr(cmd[2], '@')) || s[1] == '\0')) {
-		/* If there's no @ or it's the last char, append the user's
-		   domain name now. Exclude handles starting with a _ so
-		   adding _xmlconsole will keep working. */
-		if (s) {
-			*s = '\0';
-		}
-		if ((s = strchr(a->user, '@'))) {
-			cmd[2] = handle = g_strconcat(cmd[2], s, NULL);
-		}
-	}
-
-	if (add_on_server) {
-		irc_channel_t *ic;
-		char *s, *group = NULL;;
-
-		if ((ic = irc->root->last_channel) &&
-		    (s = set_getstr(&ic->set, "fill_by")) &&
-		    strcmp(s, "group") == 0 &&
-		    (group = set_getstr(&ic->set, "group"))) {
-			irc_rootmsg(irc, "Adding `%s' to contact list (group %s)",
-			            cmd[2], group);
-		} else {
-			irc_rootmsg(irc, "Adding `%s' to contact list", cmd[2]);
-		}
-
-		a->prpl->add_buddy(a->ic, cmd[2], group);
-	} else {
-		bee_user_t *bu;
-		irc_user_t *iu;
-
-		/* Only for add -tmp. For regular adds, this callback will
-		   be called once the IM server confirms. */
-		if ((bu = bee_user_new(irc->b, a->ic, cmd[2], BEE_USER_LOCAL)) &&
-		    (iu = bu->ui_data)) {
-			irc_rootmsg(irc, "Temporarily assigned nickname `%s' "
-			            "to contact `%s'", iu->nick, cmd[2]);
-		}
-	}
-
-	g_free(handle);
-}
-
-static void cmd_remove(irc_t *irc, char **cmd)
-{
-	irc_user_t *iu;
-	bee_user_t *bu;
-	char *s;
-
-	if (!(iu = irc_user_by_name(irc, cmd[1])) || !(bu = iu->bu)) {
-		irc_rootmsg(irc, "Buddy `%s' not found", cmd[1]);
-		return;
-	}
-	s = g_strdup(bu->handle);
-
-	bu->ic->acc->prpl->remove_buddy(bu->ic, bu->handle, NULL);
-	nick_del(bu);
-	if (g_hash_table_contains(irc->nick_user_hash, iu->key)) {
-		bee_user_free(irc->b, bu);
-	}
-
-	irc_rootmsg(irc, "Buddy `%s' (nick %s) removed from contact list", s, cmd[1]);
-	g_free(s);
-
-	return;
-}
-
-static void cmd_info(irc_t *irc, char **cmd)
-{
-	struct im_connection *ic;
-	account_t *a;
-
-	if (!cmd[2]) {
-		irc_user_t *iu = irc_user_by_name(irc, cmd[1]);
-		if (!iu || !iu->bu) {
-			irc_rootmsg(irc, "Nick `%s' does not exist", cmd[1]);
-			return;
-		}
-		ic = iu->bu->ic;
-		cmd[2] = iu->bu->handle;
-	} else if (!(a = account_get(irc->b, cmd[1]))) {
-		irc_rootmsg(irc, "Invalid account");
-		return;
-	} else if (!((ic = a->ic) && (a->ic->flags & OPT_LOGGED_IN))) {
-		irc_rootmsg(irc, "That account is not on-line");
-		return;
-	}
-
-	if (!ic->acc->prpl->get_info) {
-		irc_rootmsg(irc, "Command `%s' not supported by this protocol", cmd[0]);
-	} else {
-		ic->acc->prpl->get_info(ic, cmd[2]);
-	}
-}
-
-static void cmd_rename(irc_t *irc, char **cmd)
-{
-	irc_user_t *iu, *old;
-	gboolean del = g_strcasecmp(cmd[1], "-del") == 0;
-
-	iu = irc_user_by_name(irc, cmd[del ? 2 : 1]);
-
-	if (iu == NULL) {
-		irc_rootmsg(irc, "Nick `%s' does not exist", cmd[del ? 2 : 1]);
-	} else if (del) {
-		if (iu->bu) {
-			bee_irc_user_nick_reset(iu);
-		}
-		irc_rootmsg(irc, "Nickname reset to `%s'", iu->nick);
-	} else if (iu == irc->user) {
-		irc_rootmsg(irc, "Use /nick to change your own nickname");
-	} else if (!nick_ok(irc, cmd[2])) {
-		irc_rootmsg(irc, "Nick `%s' is invalid", cmd[2]);
-	} else if ((old = irc_user_by_name(irc, cmd[2])) && old != iu) {
-		irc_rootmsg(irc, "Nick `%s' already exists", cmd[2]);
-	} else {
-		if (!irc_user_set_nick(iu, cmd[2])) {
-			irc_rootmsg(irc, "Error while changing nick");
-			return;
-		}
-
-		if (iu == irc->root) {
-			/* If we're called internally (user did "set root_nick"),
-			   let's not go O(INF). :-) */
-			if (strcmp(cmd[0], "set_rename") != 0) {
-				set_setstr(&irc->b->set, "root_nick", cmd[2]);
-			}
-		} else if (iu->bu) {
-			nick_set(iu->bu, cmd[2]);
-		}
-
-		irc_rootmsg(irc, "Nick successfully changed");
-	}
-}
-
-char *set_eval_root_nick(set_t *set, char *new_nick)
-{
-	irc_t *irc = set->data;
-
-	if (strcmp(irc->root->nick, new_nick) != 0) {
-		char *cmd[] = { "set_rename", irc->root->nick, new_nick, NULL };
-
-		cmd_rename(irc, cmd);
-	}
-
-	return strcmp(irc->root->nick, new_nick) == 0 ? new_nick : SET_INVALID;
-}
-
-static void cmd_block(irc_t *irc, char **cmd)
-{
-	struct im_connection *ic;
-	account_t *a;
-
-	if (!cmd[2] && (a = account_get(irc->b, cmd[1])) && a->ic) {
-		char *format;
-		GSList *l;
-
-		if (strchr(irc->umode, 'b') != NULL) {
-			format = "%s\t%s";
-		} else {
-			format = "%-32.32s  %-16.16s";
-		}
-
-		irc_rootmsg(irc, format, "Handle", "Nickname");
-		for (l = a->ic->deny; l; l = l->next) {
-			bee_user_t *bu = bee_user_by_handle(irc->b, a->ic, l->data);
-			irc_user_t *iu = bu ? bu->ui_data : NULL;
-			irc_rootmsg(irc, format, l->data, iu ? iu->nick : "(none)");
-		}
-		irc_rootmsg(irc, "End of list.");
-
-		return;
-	} else if (!cmd[2]) {
-		irc_user_t *iu = irc_user_by_name(irc, cmd[1]);
-		if (!iu || !iu->bu) {
-			irc_rootmsg(irc, "Nick `%s' does not exist", cmd[1]);
-			return;
-		}
-		ic = iu->bu->ic;
-		cmd[2] = iu->bu->handle;
-	} else if (!(a = account_get(irc->b, cmd[1]))) {
-		irc_rootmsg(irc, "Invalid account");
-		return;
-	} else if (!((ic = a->ic) && (a->ic->flags & OPT_LOGGED_IN))) {
-		irc_rootmsg(irc, "That account is not on-line");
-		return;
-	}
-
-	if (!ic->acc->prpl->add_deny || !ic->acc->prpl->rem_permit) {
-		irc_rootmsg(irc, "Command `%s' not supported by this protocol", cmd[0]);
-	} else {
-		imc_rem_allow(ic, cmd[2]);
-		imc_add_block(ic, cmd[2]);
-		irc_rootmsg(irc, "Buddy `%s' moved from allow- to block-list", cmd[2]);
-	}
-}
-
-static void cmd_allow(irc_t *irc, char **cmd)
-{
-	struct im_connection *ic;
-	account_t *a;
-
-	if (!cmd[2] && (a = account_get(irc->b, cmd[1])) && a->ic) {
-		char *format;
-		GSList *l;
-
-		if (strchr(irc->umode, 'b') != NULL) {
-			format = "%s\t%s";
-		} else {
-			format = "%-32.32s  %-16.16s";
-		}
-
-		irc_rootmsg(irc, format, "Handle", "Nickname");
-		for (l = a->ic->permit; l; l = l->next) {
-			bee_user_t *bu = bee_user_by_handle(irc->b, a->ic, l->data);
-			irc_user_t *iu = bu ? bu->ui_data : NULL;
-			irc_rootmsg(irc, format, l->data, iu ? iu->nick : "(none)");
-		}
-		irc_rootmsg(irc, "End of list.");
-
-		return;
-	} else if (!cmd[2]) {
-		irc_user_t *iu = irc_user_by_name(irc, cmd[1]);
-		if (!iu || !iu->bu) {
-			irc_rootmsg(irc, "Nick `%s' does not exist", cmd[1]);
-			return;
-		}
-		ic = iu->bu->ic;
-		cmd[2] = iu->bu->handle;
-	} else if (!(a = account_get(irc->b, cmd[1]))) {
-		irc_rootmsg(irc, "Invalid account");
-		return;
-	} else if (!((ic = a->ic) && (a->ic->flags & OPT_LOGGED_IN))) {
-		irc_rootmsg(irc, "That account is not on-line");
-		return;
-	}
-
-	if (!ic->acc->prpl->rem_deny || !ic->acc->prpl->add_permit) {
-		irc_rootmsg(irc, "Command `%s' not supported by this protocol", cmd[0]);
-	} else {
-		imc_rem_block(ic, cmd[2]);
-		imc_add_allow(ic, cmd[2]);
-
-		irc_rootmsg(irc, "Buddy `%s' moved from block- to allow-list", cmd[2]);
-	}
-}
-
-static void cmd_yesno(irc_t *irc, char **cmd)
-{
-	query_t *q = NULL;
-	int numq = 0;
-
-	if (irc->queries == NULL) {
-		/* Alright, alright, let's add a tiny easter egg here. */
-		static irc_t *last_irc = NULL;
-		static time_t last_time = 0;
-		static int times = 0;
-		static const char *msg[] = {
-			"Oh yeah, that's right.",
-			"Alright, alright. Now go back to work.",
-			"Buuuuuuuuuuuuuuuurp... Excuse me!",
-			"Yes?",
-			"No?",
-		};
-
-		if (last_irc == irc && time(NULL) - last_time < 15) {
-			if ((++times >= 3)) {
-				irc_rootmsg(irc, "%s", msg[rand() % (sizeof(msg) / sizeof(char*))]);
-				last_irc = NULL;
-				times = 0;
-				return;
-			}
-		} else {
-			last_time = time(NULL);
-			last_irc = irc;
-			times = 0;
-		}
-
-		irc_rootmsg(irc, "Did I ask you something?");
-		return;
-	}
-
-	/* 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_rootmsg(irc, "Invalid query number");
-			return;
-		}
-
-		for (q = irc->queries; q; q = q->next, numq--) {
-			if (numq == 0) {
-				break;
-			}
-		}
-
-		if (!q) {
-			irc_rootmsg(irc, "Uhm, I never asked you something like that...");
-			return;
-		}
-	}
-
-	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);
-	}
-}
-
-static void cmd_set(irc_t *irc, char **cmd)
-{
-	cmd_set_real(irc, cmd, &irc->b->set, NULL);
-}
-
-static void cmd_blist(irc_t *irc, char **cmd)
-{
-	int online = 0, away = 0, offline = 0, ismatch = 0;
-	GList *l, *users;
-	GRegex *regex = NULL;
-	GError *error = NULL;
-	char s[256];
-	char *format;
-	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;
-	}
-
-	if (cmd[2]) {
-		regex = g_regex_new(cmd[2], G_REGEX_CASELESS, 0, &error);
-	}
-
-	if (error) {
-		irc_rootmsg(irc, "%s", error->message);
-		g_error_free(error);
-	}
-
-	if (strchr(irc->umode, 'b') != NULL) {
-		format = "%s\t%s\t%s";
-	} else {
-		format = "%-24.24s  %-40.40s  %s";
-	}
-
-	irc_rootmsg(irc, format, "Nick", "Handle/Account", "Status");
-
-	if (irc->root->last_channel &&
-	    strcmp(set_getstr(&irc->root->last_channel->set, "type"), "control") != 0) {
-		irc->root->last_channel = NULL;
-	}
-
-	users = g_hash_table_get_values(irc->nick_user_hash);
-	users = g_list_sort(users, irc_user_cmp);
-
-	for (l = users; l; l = l->next) {
-		irc_user_t *iu = l->data;
-		bee_user_t *bu = iu->bu;
-
-		if (!regex || g_regex_match(regex, iu->nick, 0, NULL)) {
-			ismatch = 1;
-		} else {
-			ismatch = 0;
-		}
-
-		if (!bu || (irc->root->last_channel && !irc_channel_wants_user(irc->root->last_channel, iu))) {
-			continue;
-		}
-
-		if ((bu->flags & (BEE_USER_ONLINE | BEE_USER_AWAY)) == BEE_USER_ONLINE) {
-			if (ismatch == 1 && online == 1) {
-				char st[256] = "Online";
-
-				if (bu->status_msg) {
-					g_snprintf(st, sizeof(st) - 1, "Online (%s)", bu->status_msg);
-				}
-
-				g_snprintf(s, sizeof(s) - 1, "%s %s", bu->handle, bu->ic->acc->tag);
-				irc_rootmsg(irc, format, iu->nick, s, st);
-			}
-
-			n_online++;
-		}
-
-		if ((bu->flags & BEE_USER_ONLINE) && (bu->flags & BEE_USER_AWAY)) {
-			if (ismatch == 1 && away == 1) {
-				g_snprintf(s, sizeof(s) - 1, "%s %s", bu->handle, bu->ic->acc->tag);
-				irc_rootmsg(irc, format, iu->nick, s, irc_user_get_away(iu));
-			}
-			n_away++;
-		}
-
-		if (!(bu->flags & BEE_USER_ONLINE)) {
-			if (ismatch == 1 && offline == 1) {
-				g_snprintf(s, sizeof(s) - 1, "%s %s", bu->handle, bu->ic->acc->tag);
-				irc_rootmsg(irc, format, iu->nick, s, "Offline");
-			}
-			n_offline++;
-		}
-	}
-
-	g_list_free(users);
-
-	irc_rootmsg(irc, "%d buddies (%d available, %d away, %d offline)", n_online + n_away + n_offline, n_online,
-	            n_away, n_offline);
-
-	if (regex) {
-		g_regex_unref(regex);
-	}
-}
-
-static gint prplcmp(gconstpointer a, gconstpointer b)
-{
-	const struct prpl *pa = a;
-	const struct prpl *pb = b;
-
-	return g_strcasecmp(pa->name, pb->name);
-}
-
-static void prplstr(GList *prpls, GString *gstr)
-{
-	const char *last = NULL;
-	GList *l;
-	struct prpl *p;
-
-	prpls = g_list_copy(prpls);
-	prpls = g_list_sort(prpls, prplcmp);
-
-	for (l = prpls; l; l = l->next) {
-		p = l->data;
-
-		if (last && g_strcasecmp(p->name, last) == 0) {
-			/* Ignore duplicates (mainly for libpurple) */
-			continue;
-		}
-
-		if (gstr->len != 0) {
-			g_string_append(gstr, ", ");
-		}
-
-		g_string_append(gstr, p->name);
-		last = p->name;
-	}
-
-	g_list_free(prpls);
-}
-
-#ifdef WITH_PLUGINS
-static void cmd_plugins_info(irc_t *irc, char **cmd)
-{
-	GList *l;
-	struct plugin_info *info;
-
-	MIN_ARGS(2);
-
-	for (l = get_plugins(); l; l = l->next) {
-		info = l->data;
-		if (g_strcasecmp(cmd[2], info->name) == 0) {
-			break;
-		}
-	}
-
-	if (!l) {
-		return;
-	}
-
-	irc_rootmsg(irc, "%s:", info->name);
-	irc_rootmsg(irc, "  Version: %s", info->version);
-
-	if (info->description) {
-		irc_rootmsg(irc, "  Description: %s", info->description);
-	}
-
-	if (info->author) {
-		irc_rootmsg(irc, "  Author: %s", info->author);
-	}
-
-	if (info->url) {
-		irc_rootmsg(irc, "  URL: %s", info->url);
-	}
-}
-#endif
-
-static void cmd_plugins(irc_t *irc, char **cmd)
-{
-	GList *prpls;
-	GString *gstr;
-
-	if (cmd[1] && g_strcasecmp(cmd[1], "info") == 0) {
-#ifdef WITH_PLUGINS
-		cmd_plugins_info(irc, cmd);
-#endif
-		return;
-	}
-
-#ifdef WITH_PLUGINS
-	GList *l;
-	struct plugin_info *info;
-	char *format;
-
-	if (strchr(irc->umode, 'b') != NULL) {
-		format = "%s\t%s";
-	} else {
-		format = "%-30s  %s";
-	}
-
-	irc_rootmsg(irc, format, "Plugin", "Version");
-
-	for (l = get_plugins(); l; l = l->next) {
-		char *c;
-		info = l->data;
-
-		/* some purple plugins like to include several versions separated by newlines... */
-		if ((c = strchr(info->version, '\n'))) {
-			char *version = g_strndup(info->version, c - info->version);
-			irc_rootmsg(irc, format, info->name, version);
-			g_free(version);
-		} else {
-			irc_rootmsg(irc, format, info->name, info->version);
-		}
-	}
-#endif
-
-	irc_rootmsg(irc, " ");
-
-	gstr = g_string_new(NULL);
-	prpls = get_protocols();
-
-	if (prpls) {
-		prplstr(prpls, gstr);
-		irc_rootmsg(irc, "Enabled Protocols: %s", gstr->str);
-		g_string_truncate(gstr, 0);
-	}
-
-	prpls = get_protocols_disabled();
-
-	if (prpls) {
-		prplstr(prpls, gstr);
-		irc_rootmsg(irc, "Disabled Protocols: %s", gstr->str);
-	}
-
-	g_string_free(gstr, TRUE);
-}
-
-static void cmd_qlist(irc_t *irc, char **cmd)
-{
-	query_t *q = irc->queries;
-	int num;
-
-	if (!q) {
-		irc_rootmsg(irc, "There are no pending questions.");
-		return;
-	}
-
-	irc_rootmsg(irc, "Pending queries:");
-
-	for (num = 0; q; q = q->next, num++) {
-		if (q->ic) { /* Not necessary yet, but it might come later */
-			irc_rootmsg(irc, "%d, %s: %s", num, q->ic->acc->tag, q->question);
-		} else {
-			irc_rootmsg(irc, "%d, BitlBee: %s", num, q->question);
-		}
-	}
-}
-
-static void cmd_chat(irc_t *irc, char **cmd)
-{
-	account_t *acc;
-
-	if (g_strcasecmp(cmd[1], "add") == 0) {
-		bee_chat_info_t *ci;
-		char *channel, *room, *s;
-		struct irc_channel *ic;
-		guint i;
-
-		MIN_ARGS(3);
-
-		if (!(acc = account_get(irc->b, cmd[2]))) {
-			irc_rootmsg(irc, "Invalid account");
-			return;
-		} else if (!acc->prpl->chat_join) {
-			irc_rootmsg(irc, "Named chatrooms not supported on that account.");
-			return;
-		}
-
-		if (cmd[3][0] == '!') {
-			if (!acc->ic || !(acc->ic->flags & OPT_LOGGED_IN)) {
-				irc_rootmsg(irc, "Not logged in to account.");
-				return;
-			} else if (!acc->prpl->chat_list) {
-				irc_rootmsg(irc, "Listing chatrooms not supported on that account.");
-				return;
-			}
-
-			i = g_ascii_strtoull(cmd[3] + 1, NULL, 10);
-			ci = g_slist_nth_data(acc->ic->chatlist, i - 1);
-
-			if (ci == NULL) {
-				irc_rootmsg(irc, "Invalid chatroom index");
-				return;
-			}
-
-			room = ci->title;
-		} else {
-			room = cmd[3];
-		}
-
-		if (cmd[4] == NULL) {
-			channel = g_strdup(room);
-			if ((s = strchr(channel, '@'))) {
-				*s = 0;
-			}
-		} else {
-			channel = g_strdup(cmd[4]);
-		}
-
-		if (strchr(CTYPES, channel[0]) == NULL) {
-			s = g_strdup_printf("#%s", channel);
-			g_free(channel);
-			channel = s;
-
-			irc_channel_name_strip(channel);
-		}
-
-		if ((ic = irc_channel_new(irc, channel)) &&
-		    set_setstr(&ic->set, "type", "chat") &&
-		    set_setstr(&ic->set, "chat_type", "room") &&
-		    set_setstr(&ic->set, "account", cmd[2]) &&
-		    set_setstr(&ic->set, "room", room)) {
-			irc_rootmsg(irc, "Chatroom successfully added, join with \002/join %s\002", channel);
-		} else {
-			if (ic) {
-				irc_channel_free(ic);
-
-				irc_rootmsg(irc, "Error adding chatroom.");
-			} else if (irc_channel_by_name(irc, channel)) {
-				irc_rootmsg(irc, "A channel named `%s' already exists. "
-				            "Join with \002/join %s\002 or see \002help channel\002 to modify it",
-				            channel, channel);
-			} else {
-				irc_rootmsg(irc, "Error creating channel for chatroom.");
-			}
-		}
-		g_free(channel);
-	} else if (g_strcasecmp(cmd[1], "list") == 0) {
-		MIN_ARGS(2);
-
-		if (!(acc = account_get(irc->b, cmd[2]))) {
-			irc_rootmsg(irc, "Invalid account");
-			return;
-		} else if (!acc->ic || !(acc->ic->flags & OPT_LOGGED_IN)) {
-			irc_rootmsg(irc, "Not logged in to account.");
-			return;
-		} else if (!acc->prpl->chat_list) {
-			irc_rootmsg(irc, "Listing chatrooms not supported on that account.");
-			return;
-		}
-
-		acc->prpl->chat_list(acc->ic, cmd[3]);
-	} else if (g_strcasecmp(cmd[1], "with") == 0) {
-		irc_user_t *iu;
-
-		MIN_ARGS(2);
-
-		if ((iu = irc_user_by_name(irc, cmd[2])) &&
-		    iu->bu && iu->bu->ic->acc->prpl->chat_with) {
-			if (!iu->bu->ic->acc->prpl->chat_with(iu->bu->ic, iu->bu->handle)) {
-				irc_rootmsg(irc, "(Possible) failure while trying to open "
-				            "a groupchat with %s.", iu->nick);
-			}
-		} else {
-			irc_rootmsg(irc, "Can't open a groupchat with %s.", cmd[2]);
-		}
-	} else if (g_strcasecmp(cmd[1], "set") == 0 ||
-	           g_strcasecmp(cmd[1], "del") == 0) {
-		irc_rootmsg(irc, "Unknown command: chat %s. Did you mean \002channel %s\002?", cmd[1], cmd[1]);
-	} else {
-		irc_rootmsg(irc,
-		            "Unknown command: %s %s. Please use \x02help commands\x02 to get a list of available commands.", "chat",
-		            cmd[1]);
-	}
-}
-
-/* some arbitrary numbers */
-#define CHAT_TITLE_LEN_MIN 20
-#define CHAT_TITLE_LEN_MAX 100
-
-void cmd_chat_list_finish(struct im_connection *ic)
-{
-	account_t *acc = ic->acc;
-	bee_chat_info_t *ci;
-	char *hformat, *iformat, *topic, *padded;
-	GSList *l;
-	guint i = 0;
-	long title_len, new_len;
-	irc_t *irc = ic->bee->ui_data;
-
-	if (ic->chatlist == NULL) {
-		irc_rootmsg(irc, "No existing chatrooms");
-		return;
-	}
-
-	/* find a reasonable width for the table */
-	title_len = CHAT_TITLE_LEN_MIN;
-
-	for (l = ic->chatlist; l; l = l->next) {
-		ci = l->data;
-		new_len = g_utf8_strlen(ci->title, -1);
-
-		if (new_len >= CHAT_TITLE_LEN_MAX) {
-			title_len = CHAT_TITLE_LEN_MAX;
-			break;
-		} else if (title_len < new_len) {
-			title_len = new_len;
-		}
-	}
-
-	if (strchr(irc->umode, 'b') != NULL) {
-		hformat = "%s\t%s\t%s";
-		iformat = "%u\t%s\t%s";
-	} else {
-		hformat = "%s  %s  %s";
-		iformat = "%5u  %s  %s";
-	}
-
-	padded = str_pad_and_truncate("Title", title_len, NULL);
-	irc_rootmsg(irc, hformat, "Index", padded, "Topic");
-	g_free(padded);
-
-	for (l = ic->chatlist; l; l = l->next) {
-		ci = l->data;
-		topic = ci->topic ? ci->topic : "";
-
-		padded = str_pad_and_truncate(ci->title ? ci->title : "", title_len, "[...]");
-		irc_rootmsg(irc, iformat, ++i, padded, topic);
-		g_free(padded);
-	}
-
-	irc_rootmsg(irc, "%u %s chatrooms", i, acc->tag);
-}
-
-static void cmd_group(irc_t *irc, char **cmd)
-{
-	GSList *l;
-	int len;
-
-	len = strlen(cmd[1]);
-	if (g_strncasecmp(cmd[1], "list", len) == 0) {
-		int n = 0;
-
-		if (strchr(irc->umode, 'b')) {
-			irc_rootmsg(irc, "Group list:");
-		}
-
-		for (l = irc->b->groups; l; l = l->next) {
-			bee_group_t *bg = l->data;
-			irc_rootmsg(irc, "%d. %s", n++, bg->name);
-		}
-		irc_rootmsg(irc, "End of group list");
-	} else if (g_strncasecmp(cmd[1], "info", len) == 0) {
-		bee_group_t *bg;
-		int n = 0;
-
-		MIN_ARGS(2);
-		bg = bee_group_by_name(irc->b, cmd[2], FALSE);
-
-		if (bg) {
-			if (strchr(irc->umode, 'b')) {
-				irc_rootmsg(irc, "Members of %s:", cmd[2]);
-			}
-			for (l = irc->b->users; l; l = l->next) {
-				bee_user_t *bu = l->data;
-				if (bu->group == bg) {
-					irc_rootmsg(irc, "%d. %s", n++, bu->nick ? : bu->handle);
-				}
-			}
-			irc_rootmsg(irc, "End of member list");
-		} else {
-			irc_rootmsg(irc,
-			            "Unknown group: %s. Please use \x02group list\x02 to get a list of available groups.",
-			            cmd[2]);
-		}
-	} else {
-		irc_rootmsg(irc,
-		            "Unknown command: %s %s. Please use \x02help commands\x02 to get a list of available commands.", "group",
-		            cmd[1]);
-	}
-}
-
-static void cmd_transfer(irc_t *irc, char **cmd)
-{
-	GSList *files = irc->file_transfers;
-	GSList *next;
-
-	enum { LIST, REJECT, CANCEL };
-	int subcmd = LIST;
-	int fid;
-
-	if (!files) {
-		irc_rootmsg(irc, "No pending transfers");
-		return;
-	}
-
-	if (cmd[1] && (strcmp(cmd[1], "reject") == 0)) {
-		subcmd = REJECT;
-	} else if (cmd[1] && (strcmp(cmd[1], "cancel") == 0) &&
-	           cmd[2] && (sscanf(cmd[2], "%d", &fid) == 1)) {
-		subcmd = CANCEL;
-	}
-
-	for (; files; files = next) {
-		next = files->next;
-		file_transfer_t *file = files->data;
-
-		switch (subcmd) {
-		case LIST:
-			if (file->status == FT_STATUS_LISTENING) {
-				irc_rootmsg(irc,
-				            "Pending file(id %d): %s (Listening...)", file->local_id, file->file_name);
-			} else {
-				int kb_per_s = 0;
-				time_t diff = time(NULL) - file->started ? : 1;
-				if ((file->started > 0) && (file->bytes_transferred > 0)) {
-					kb_per_s = file->bytes_transferred / 1024 / diff;
-				}
-
-				irc_rootmsg(irc,
-				            "Pending file(id %d): %s (%10zd/%zd kb, %d kb/s)", file->local_id,
-				            file->file_name,
-				            file->bytes_transferred / 1024, file->file_size / 1024, kb_per_s);
-			}
-			break;
-		case REJECT:
-			if (file->status == FT_STATUS_LISTENING) {
-				irc_rootmsg(irc, "Rejecting file transfer for %s", file->file_name);
-				imcb_file_canceled(file->ic, file, "Denied by user");
-			}
-			break;
-		case CANCEL:
-			if (file->local_id == fid) {
-				irc_rootmsg(irc, "Canceling file transfer for %s", file->file_name);
-				imcb_file_canceled(file->ic, file, "Canceled by user");
-			}
-			break;
-		}
-	}
-}
-
-/* Maybe this should be a stand-alone command as well? */
-static void bitlbee_whatsnew(irc_t *irc)
-{
-	int last = set_getint(&irc->b->set, "last_version");
-	char s[16], *msg;
-
-	if (last >= BITLBEE_VERSION_CODE) {
-		return;
-	}
-
-	msg = help_get_whatsnew(&(global.help), last);
-
-	if (msg) {
-		irc_rootmsg(irc, "%s: This seems to be your first time using this "
-		            "this version of BitlBee. Here's a list of new "
-		            "features you may like to know about:\n\n%s\n",
-		            irc->user->nick, msg);
-	}
-
-	g_free(msg);
-
-	g_snprintf(s, sizeof(s), "%d", BITLBEE_VERSION_CODE);
-	set_setstr(&irc->b->set, "last_version", s);
-}
-
-/* IMPORTANT: Keep this list sorted! The short command logic needs that. */
-command_t root_commands[] = {
-	{ "account",        1, cmd_account,        0 },
-	{ "add",            2, cmd_add,            0 },
-	{ "allow",          1, cmd_allow,          0 },
-	{ "blist",          0, cmd_blist,          0 },
-	{ "block",          1, cmd_block,          0 },
-	{ "channel",        1, cmd_channel,        0 },
-	{ "chat",           1, cmd_chat,           0 },
-	{ "drop",           1, cmd_drop,           0 },
-	{ "ft",             0, cmd_transfer,       0 },
-	{ "group",          1, cmd_group,          0 },
-	{ "help",           0, cmd_help,           0 },
-	{ "identify",       0, cmd_identify,       0 },
-	{ "info",           1, cmd_info,           0 },
-	{ "no",             0, cmd_yesno,          0 },
-	{ "plugins",        0, cmd_plugins,        0 },
-	{ "qlist",          0, cmd_qlist,          0 },
-	{ "register",       0, cmd_register,       0 },
-	{ "remove",         1, cmd_remove,         0 },
-	{ "rename",         2, cmd_rename,         0 },
-	{ "save",           0, cmd_save,           0 },
-	{ "set",            0, cmd_set,            0 },
-	{ "transfer",       0, cmd_transfer,       0 },
-	{ "yes",            0, cmd_yesno,          0 },
-	/* Not expecting too many plugins adding root commands so just make a
-	   dumb array with some empty entried at the end. */
-	{ NULL },
-	{ NULL },
-	{ NULL },
-	{ NULL },
-	{ NULL },
-	{ NULL },
-	{ NULL },
-	{ NULL },
-	{ NULL },
-};
-static const int num_root_commands = sizeof(root_commands) / sizeof(command_t);
-
-gboolean root_command_add(const char *command, int params, void (*func)(irc_t *, char **args), int flags)
-{
-	int i;
-
-	if (root_commands[num_root_commands - 2].command) {
-		/* Planning fail! List is full. */
-		return FALSE;
-	}
-
-	for (i = 0; root_commands[i].command; i++) {
-		if (g_strcasecmp(root_commands[i].command, command) == 0) {
-			return FALSE;
-		} else if (g_strcasecmp(root_commands[i].command, command) > 0) {
-			break;
-		}
-	}
-	memmove(root_commands + i + 1, root_commands + i,
-	        sizeof(command_t) * (num_root_commands - i - 1));
-
-	root_commands[i].command = g_strdup(command);
-	root_commands[i].required_parameters = params;
-	root_commands[i].execute = func;
-	root_commands[i].flags = flags;
-
-	return TRUE;
-}
Index: set.c
===================================================================
--- set.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ set.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -1,6 +1,6 @@
-/********************************************************************\
+  /********************************************************************\
   * BitlBee -- An IRC to other IM-networks gateway                     *
   *                                                                    *
-  * Copyright 2002-2013 Wilmer van der Gaast and others                *
+  * Copyright 2002-2004 Wilmer van der Gaast and others                *
   \********************************************************************/
 
@@ -20,267 +20,202 @@
   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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
+  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"
 
-/* Used to use NULL for this, but NULL is actually a "valid" value. */
-char *SET_INVALID = "nee";
-
-set_t *set_add(set_t **head, const char *key, const char *def, set_eval eval, void *data)
-{
-	set_t *s = set_find(head, key);
-
-	/* Possibly the setting already exists. If it doesn't exist yet,
-	   we create it. If it does, we'll just change the default. */
-	if (!s) {
-		if ((s = *head)) {
-			/* Sorted insertion. Special-case insertion at the start. */
-			if (strcmp(key, s->key) < 0) {
-				s = g_new0(set_t, 1);
-				s->next = *head;
-				*head = s;
-			} else {
-				while (s->next && strcmp(key, s->next->key) > 0) {
-					s = s->next;
-				}
-				set_t *last_next = s->next;
-				s->next = g_new0(set_t, 1);
-				s = s->next;
-				s->next = last_next;
-			}
-		} else {
-			s = *head = g_new0(set_t, 1);
+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;
 		}
-		s->key = g_strdup(key);
-	}
-
-	if (s->def) {
-		g_free(s->def);
+		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);
-	}
-
-	s->eval = eval;
-	s->data = data;
-
-	return s;
-}
-
-set_t *set_find(set_t **head, const char *key)
-{
-	set_t *s = *head;
-
-	while (s) {
-		if (g_strcasecmp(s->key, key) == 0 ||
-		    (s->old_key && g_strcasecmp(s->old_key, key) == 0)) {
+	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(set_t **head, const char *key)
-{
-	set_t *s = set_find(head, key);
-
-	if (!s || (!s->value && !s->def)) {
-		return NULL;
-	}
-
-	return set_value(s);
-}
-
-int set_getint(set_t **head, const char *key)
-{
-	char *s = set_getstr(head, key);
+	
+	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 (sscanf(s, "%d", &i) != 1) {
-		return 0;
-	}
-
-	return i;
-}
-
-int set_getbool(set_t **head, const char *key)
-{
-	char *s = set_getstr(head, key);
-
-	if (!s) {
-		return 0;
-	}
-
-	return bool2int(s);
-}
-
-int set_isvisible(set_t *set)
-{
-	/* the default value is not stored in value, only in def */
-	return !((set->flags & SET_HIDDEN) ||
-	         ((set->flags & SET_HIDDEN_DEFAULT) &&
-	          (set->value == NULL)));
-}
-
-int set_setstr(set_t **head, const char *key, char *value)
-{
-	set_t *s = set_find(head, key);
+	
+	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) {
-		/*
-		Used to do this, but it never really made sense.
-		s = set_add( head, key, NULL, NULL, NULL );
-		*/
-		return 0;
-	}
-
-	if (value == NULL && (s->flags & SET_NULL_OK) == 0) {
-		return 0;
-	}
-
-	/* Call the evaluator. For invalid values, evaluators should now
-	   return SET_INVALID, but previously this was NULL. Try to handle
-	   that too if NULL is not an allowed value for this setting. */
-	if (s->eval && ((nv = s->eval(s, value)) == SET_INVALID ||
-	                ((s->flags & SET_NULL_OK) == 0 && nv == NULL))) {
-		return 0;
-	}
-
-	if (s->value) {
-		g_free(s->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 there's a default setting and it's equal to what we're trying to
-	   set, stick with s->value = NULL. Otherwise, remember the setting. */
-	if (!s->def || (g_strcmp0(nv, s->def) != 0)) {
-		s->value = g_strdup(nv);
-	}
-
-	if (nv != value) {
-		g_free(nv);
-	}
-
-	return 1;
-}
-
-int set_setint(set_t **head, const char *key, int value)
-{
-	char *s = g_strdup_printf("%d", value);
-	int retval = set_setstr(head, key, s);
-
-	g_free(s);
-	return retval;
-}
-
-void set_del(set_t **head, const char *key)
-{
-	set_t *s = *head, *t = NULL;
-
-	while (s) {
-		if (g_strcasecmp(s->key, key) == 0) {
+	
+	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) {
-		if (t) {
-			t->next = s->next;
-		} else {
-			*head = s->next;
-		}
-
-		g_free(s->key);
-		g_free(s->old_key);
-		g_free(s->value);
-		g_free(s->def);
-		g_free(s);
-	}
-}
-
-int set_reset(set_t **head, const char *key)
-{
-	set_t *s;
-
-	s = set_find(head, key);
-	if (s) {
-		return set_setstr(head, key, s->def);
-	}
-
-	return 0;
-}
-
-char *set_eval_int(set_t *set, char *value)
+		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;
-
-	/* Allow a minus at the first position. */
-	if (*s == '-') {
-		s++;
-	}
-
-	for (; *s; s++) {
-		if (!g_ascii_isdigit(*s)) {
-			return SET_INVALID;
-		}
-	}
-
-	return value;
-}
-
-char *set_eval_bool(set_t *set, char *value)
-{
-	return is_bool(value) ? value : SET_INVALID;
-}
-
-char *set_eval_list(set_t *set, char *value)
-{
-	GSList *options = set->eval_data, *opt;
-
-	for (opt = options; opt; opt = opt->next) {
-		if (strcmp(value, opt->data) == 0) {
-			return value;
-		}
-	}
-
-	/* TODO: It'd be nice to show the user a list of allowed values,
-	         but we don't have enough context here to do that. May
-	         want to fix that. */
-
-	return NULL;
-}
-
-char *set_eval_to_char(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_oauth(set_t *set, char *value)
-{
-	account_t *acc = set->data;
-
-	if (bool2int(value) && strcmp(acc->pass, PASSWORD_PENDING) == 0) {
-		*acc->pass = '\0';
-	}
-
-	return set_eval_bool(set, 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 );
+}
+
Index: set.h
===================================================================
--- set.h	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ set.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -1,6 +1,6 @@
-/********************************************************************\
+  /********************************************************************\
   * BitlBee -- An IRC to other IM-networks gateway                     *
   *                                                                    *
-  * Copyright 2002-2006 Wilmer van der Gaast and others                *
+  * Copyright 2002-2004 Wilmer van der Gaast and others                *
   \********************************************************************/
 
@@ -20,97 +20,33 @@
   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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
 */
 
-#ifndef __SET_H__
-#define __SET_H__
-
-struct set;
-
-/* This used to be specific to irc_t structures, but it's more generic now
-   (so it can also be used for account_t structs). It's pretty simple, but
-   so far pretty useful.
-
-   In short, it just keeps a linked list of settings/variables and it also
-   remembers a default value for every setting. And to prevent the user
-   from setting invalid values, you can write an evaluator function for
-   every setting, which can check a new value and block it by returning
-   NULL, or replace it by returning a new value. See struct set.eval. */
-
-typedef char *(*set_eval) (struct set *set, char *value);
-
-extern char *SET_INVALID;
-
-typedef enum {
-	SET_NOSAVE = 0x0001,   /* Don't save this setting (i.e. stored elsewhere). */
-	SET_NULL_OK = 0x0100,  /* set->value == NULL is allowed. */
-	SET_HIDDEN = 0x0200,   /* Don't show up in setting lists. Mostly for internal storage. */
-	SET_PASSWORD = 0x0400, /* Value shows up in settings list as "********". */
-	SET_HIDDEN_DEFAULT = 0x0800, /* Hide unless changed from default. */
-	SET_LOCKED = 0x1000    /* Setting is locked, don't allow changing it */
-} set_flags_t;
-
-typedef struct set {
-	void *data;     /* Here you can save a pointer to the
-	                   object this settings belongs to. */
-
+typedef struct set
+{
 	char *key;
-	char *old_key;  /* Previously known as; for smooth upgrades. */
 	char *value;
-	char *def;      /* Default value. If the set_setstr() function
-	                   notices a new value is exactly the same as
-	                   the default, value gets set to NULL. So when
-	                   you read a setting, don't forget about this!
-	                   In fact, you should only read values using
-	                   set_getstr/int(). */
-
-	set_flags_t flags; /* Mostly defined per user. */
-
-	/* Eval: Returns SET_INVALID if the value is incorrect, exactly
-	   the passed value variable, or a corrected value. In case of
-	   the latter, set_setstr() will free() the returned string! */
-	set_eval eval;
-	void *eval_data;
+	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;
 
-#define set_value(set) ((set)->value) ? ((set)->value) : ((set)->def)
+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 );
 
-/* Should be pretty clear. */
-set_t *set_add(set_t **head, const char *key, const char *def, set_eval eval, void *data);
+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 );
 
-/* Returns the raw set_t. Might be useful sometimes. */
-set_t *set_find(set_t **head, const char *key);
 
-/* Returns a pointer to the string value of this setting. Don't modify the
-   returned string, and don't free() it! */
-G_MODULE_EXPORT char *set_getstr(set_t **head, const char *key);
-
-/* Get an integer. In previous versions set_getint() was also used to read
-   boolean values, but this SHOULD be done with set_getbool() now! */
-G_MODULE_EXPORT int set_getint(set_t **head, const char *key);
-G_MODULE_EXPORT int set_getbool(set_t **head, const char *key);
-
-/* set_setstr() strdup()s the given value, so after using this function
-   you can free() it, if you want. */
-int set_setstr(set_t **head, const char *key, char *value);
-int set_setint(set_t **head, const char *key, int value);
-void set_del(set_t **head, const char *key);
-int set_reset(set_t **head, const char *key);
-
-/* returns true if a setting shall be shown to the user */
-int set_isvisible(set_t *set);
-
-/* Two very useful generic evaluators. */
-char *set_eval_int(set_t *set, char *value);
-char *set_eval_bool(set_t *set, char *value);
-
-/* Another more complicated one. */
-char *set_eval_list(set_t *set, char *value);
-
-/* Some not very generic evaluators that really shouldn't be here... */
-char *set_eval_to_char(set_t *set, char *value);
-char *set_eval_oauth(set_t *set, char *value);
-
-#endif /* __SET_H__ */
Index: sock.h
===================================================================
--- sock.h	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ sock.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -1,5 +1,3 @@
-#include <errno.h>
-#include <fcntl.h>
-
+#ifndef _WIN32
 #include <unistd.h>
 #include <sys/socket.h>
@@ -8,5 +6,22 @@
 #include <netdb.h>
 #define sock_make_nonblocking(fd) fcntl(fd, F_SETFL, O_NONBLOCK)
-#define sock_make_blocking(fd) fcntl(fd, F_SETFL, 0)
-#define sockerr_again() (errno == EINPROGRESS || errno == EINTR || errno == EAGAIN)
-void closesocket(int fd);
+#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
Index: orage.c
===================================================================
--- storage.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,209 +1,0 @@
-/********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2004 Wilmer van der Gaast and others                *
-  \********************************************************************/
-
-/* Support for multiple storage backends */
-
-/* Copyright (C) 2005 Jelmer Vernooij <jelmer@samba.org> */
-
-/*
-  This program is free software; you can 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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#define BITLBEE_CORE
-#include "bitlbee.h"
-
-extern storage_t storage_xml;
-
-static GList *storage_backends = NULL;
-
-const struct prpl protocol_missing = {
-	.name = "_unknown",
-};
-
-void register_storage_backend(storage_t *backend)
-{
-	storage_backends = g_list_append(storage_backends, backend);
-}
-
-static storage_t *storage_init_single(const char *name)
-{
-	GList *gl;
-	storage_t *st = NULL;
-
-	for (gl = storage_backends; gl; gl = gl->next) {
-		st = gl->data;
-		if (strcmp(st->name, name) == 0) {
-			break;
-		}
-	}
-
-	if (gl == NULL) {
-		return NULL;
-	}
-
-	if (st->init) {
-		st->init();
-	}
-
-	return st;
-}
-
-GList *storage_init(const char *primary, char **migrate)
-{
-	GList *ret = NULL;
-	int i;
-	storage_t *storage;
-
-	register_storage_backend(&storage_xml);
-
-	storage = storage_init_single(primary);
-	if (storage == NULL || storage->save == NULL) {
-		return NULL;
-	}
-
-	ret = g_list_append(ret, storage);
-
-	for (i = 0; migrate && migrate[i]; i++) {
-		storage = storage_init_single(migrate[i]);
-
-		if (storage) {
-			ret = g_list_append(ret, storage);
-		}
-	}
-
-	return ret;
-}
-
-storage_status_t storage_check_pass(irc_t *irc, const char *nick, const char *password)
-{
-	GList *gl;
-
-	/* Loop until we don't get NO_SUCH_USER */
-
-	for (gl = global.storage; gl; gl = gl->next) {
-		storage_t *st = gl->data;
-		storage_status_t status;
-
-		status = st->check_pass(irc, nick, password);
-		if (status != STORAGE_NO_SUCH_USER) {
-			return status;
-		}
-	}
-
-	return STORAGE_NO_SUCH_USER;
-}
-
-storage_status_t storage_load(irc_t * irc, const char *password)
-{
-	GList *gl;
-
-	if (irc && irc->status & USTATUS_IDENTIFIED) {
-		return STORAGE_OTHER_ERROR;
-	}
-
-	/* Loop until we don't get NO_SUCH_USER */
-	for (gl = global.storage; gl; gl = gl->next) {
-		storage_t *st = gl->data;
-		storage_status_t status;
-
-		status = st->load(irc, password);
-		if (status == STORAGE_OK) {
-			GSList *l;
-			for (l = irc_plugins; l; l = l->next) {
-				irc_plugin_t *p = l->data;
-				if (p->storage_load) {
-					p->storage_load(irc);
-				}
-			}
-			return status;
-		}
-
-		if (status != STORAGE_NO_SUCH_USER) {
-			return status;
-		}
-	}
-
-	return STORAGE_NO_SUCH_USER;
-}
-
-storage_status_t storage_save(irc_t *irc, char *password, int overwrite)
-{
-	storage_status_t st;
-	GSList *l;
-
-	if (password != NULL) {
-		/* Should only use this in the "register" command. */
-		if (irc->password || overwrite) {
-			return STORAGE_OTHER_ERROR;
-		}
-
-		irc_setpass(irc, password);
-	} else if ((irc->status & USTATUS_IDENTIFIED) == 0) {
-		return STORAGE_NO_SUCH_USER;
-	}
-
-	st = ((storage_t *) global.storage->data)->save(irc, overwrite);
-
-	for (l = irc_plugins; l; l = l->next) {
-		irc_plugin_t *p = l->data;
-		if (p->storage_save) {
-			p->storage_save(irc);
-		}
-	}
-
-	if (password != NULL) {
-		irc_setpass(irc, NULL);
-	}
-
-	return st;
-}
-
-storage_status_t storage_remove(const char *nick)
-{
-	GList *gl;
-	storage_status_t ret = STORAGE_OK;
-	gboolean ok = FALSE;
-	GSList *l;
-
-	/* Remove this account from all storage backends. If this isn't
-	 * done, the account will still be usable, it'd just be
-	 * loaded from a different backend. */
-	for (gl = global.storage; gl; gl = gl->next) {
-		storage_t *st = gl->data;
-		storage_status_t status;
-
-		status = st->remove(nick);
-		ok |= status == STORAGE_OK;
-		if (status != STORAGE_NO_SUCH_USER && status != STORAGE_OK) {
-			ret = status;
-		}
-	}
-
-	/* If at least one succeeded, remove plugin data. */
-	if (ok) {
-		for (l = irc_plugins; l; l = l->next) {
-			irc_plugin_t *p = l->data;
-			if (p->storage_remove) {
-				p->storage_remove(nick);
-			}
-		}
-	}
-
-	return ret;
-}
Index: orage.h
===================================================================
--- storage.h	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,67 +1,0 @@
-/********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2004 Wilmer van der Gaast and others                *
-  \********************************************************************/
-
-/* Layer for retrieving and storing buddy information */
-
-/*
-  This program is free software; you can 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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#ifndef __STORAGE_H__
-#define __STORAGE_H__
-
-typedef enum {
-	STORAGE_OK = 0,
-	STORAGE_NO_SUCH_USER,
-	STORAGE_INVALID_PASSWORD,
-	STORAGE_CHECK_BACKEND,
-	STORAGE_ALREADY_EXISTS,
-	STORAGE_OTHER_ERROR /* Error that isn't caused by user input, such as
-	                       a database that is unreachable. log() will be
-	                       used for the exact error message */
-} storage_status_t;
-
-typedef struct {
-	const char *name;
-
-	/* May be set to NULL if not required */
-	void (*init)(void);
-
-	storage_status_t (*check_pass)(irc_t *irc, const char *nick, const char *password);
-
-	storage_status_t (*load)(irc_t *irc, const char *password);
-	storage_status_t (*save)(irc_t *irc, int overwrite);
-	storage_status_t (*remove)(const char *nick);
-
-	/* May be NULL if not supported by backend */
-	storage_status_t (*rename)(const char *onick, const char *nnick, const char *password);
-} storage_t;
-
-storage_status_t storage_check_pass(irc_t *irc, const char *nick, const char *password);
-
-storage_status_t storage_load(irc_t * irc, const char *password);
-storage_status_t storage_save(irc_t *irc, char *password, int overwrite);
-storage_status_t storage_remove(const char *nick);
-
-void register_storage_backend(storage_t *);
-G_GNUC_MALLOC GList *storage_init(const char *primary, char **migrate);
-
-extern const struct prpl protocol_missing;
-
-#endif /* __STORAGE_H__ */
Index: orage_xml.c
===================================================================
--- storage_xml.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,516 +1,0 @@
-/********************************************************************\
-  * BitlBee -- An IRC to other IM-networks gateway                     *
-  *                                                                    *
-  * Copyright 2002-2012 Wilmer van der Gaast and others                *
-  \********************************************************************/
-
-/* Storage backend that uses an XMLish format for all 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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
-*/
-
-#define BITLBEE_CORE
-#include "bitlbee.h"
-#include "base64.h"
-#include "arc.h"
-#include "xmltree.h"
-
-#include <glib/gstdio.h>
-
-typedef enum {
-	XML_PASS_CHECK = 0,
-	XML_LOAD
-} xml_action;
-
-/* To make it easier later when extending the format: */
-#define XML_FORMAT_VERSION "1"
-
-struct xml_parsedata {
-	irc_t *irc;
-	char given_nick[MAX_NICK_LENGTH + 1];
-	char *given_pass;
-};
-
-static void xml_init(void)
-{
-	if (g_access(global.conf->configdir, F_OK) != 0) {
-		log_message(LOGLVL_WARNING,
-		            "The configuration directory `%s' does not exist. Configuration won't be saved.",
-		            global.conf->configdir);
-	} else if (g_access(global.conf->configdir, F_OK) != 0 ||
-	           g_access(global.conf->configdir, W_OK) != 0) {
-		log_message(LOGLVL_WARNING, "Permission problem: Can't read/write from/to `%s'.",
-		            global.conf->configdir);
-	}
-}
-
-static void handle_settings(struct xt_node *node, set_t **head)
-{
-	struct xt_node *c;
-	struct set *s;
-
-	for (c = node->children; (c = xt_find_node(c, "setting")); c = c->next) {
-		char *name = xt_find_attr(c, "name");
-		char *locked = xt_find_attr(c, "locked");
-
-		if (!name) {
-			continue;
-		}
-
-		if (strcmp(node->name, "account") == 0) {
-			set_t *s = set_find(head, name);
-			if (s && (s->flags & ACC_SET_ONLINE_ONLY)) {
-				continue; /* U can't touch this! */
-			}
-		}
-		set_setstr(head, name, c->text);
-		if (locked && !g_strcasecmp(locked, "true")) {
-			s = set_find(head, name);
-			if (s) {
-				s->flags |= SET_LOCKED;
-			}
-		}
-	}
-}
-
-/* Use for unsupported/not-found protocols. Save settings as-is but don't allow changes. */
-static void handle_settings_raw(struct xt_node *node, set_t **head)
-{
-	struct xt_node *c;
-
-	for (c = node->children; (c = xt_find_node(c, "setting")); c = c->next) {
-		char *name = xt_find_attr(c, "name");
-
-		if (!name) {
-			continue;
-		}
-
-		set_t *s = set_add(head, name, NULL, NULL, NULL);
-		set_setstr(head, name, c->text);
-		s->flags |= SET_HIDDEN |
-		            ACC_SET_OFFLINE_ONLY | ACC_SET_ONLINE_ONLY;
-	}
-}
-
-static xt_status handle_account(struct xt_node *node, gpointer data)
-{
-	struct xml_parsedata *xd = data;
-	char *protocol, *handle, *server, *password = NULL, *autoconnect, *tag, *locked;
-	char *pass_b64 = NULL;
-	unsigned char *pass_cr = NULL;
-	int pass_len, local = 0;
-	struct prpl *prpl = NULL;
-	account_t *acc;
-	struct xt_node *c;
-
-	handle = xt_find_attr(node, "handle");
-	pass_b64 = xt_find_attr(node, "password");
-	server = xt_find_attr(node, "server");
-	autoconnect = xt_find_attr(node, "autoconnect");
-	tag = xt_find_attr(node, "tag");
-	locked = xt_find_attr(node, "locked");
-
-	protocol = xt_find_attr(node, "protocol");
-	if (protocol) {
-		prpl = find_protocol(protocol);
-		if (!prpl) {
-			irc_rootmsg(xd->irc, "Warning: Protocol not found: `%s'", protocol);
-			prpl = (struct prpl*) &protocol_missing;
-		}
-		local = protocol_account_islocal(protocol);
-	}
-
-	if (!handle || !pass_b64 || !protocol || !prpl) {
-		return XT_ABORT;
-	}
-
-	pass_len = base64_decode(pass_b64, (unsigned char **) &pass_cr);
-	if (xd->irc->auth_backend) {
-		password = g_strdup((char *)pass_cr);
-	} else {
-		pass_len = arc_decode(pass_cr, pass_len, &password, xd->given_pass);
-		if (pass_len < 0) {
-			g_free(pass_cr);
-			g_free(password);
-			return XT_ABORT;
-		}
-	}
-
-	acc = account_add(xd->irc->b, prpl, handle, password);
-	if (server) {
-		set_setstr(&acc->set, "server", server);
-	}
-	if (autoconnect) {
-		set_setstr(&acc->set, "auto_connect", autoconnect);
-	}
-	if (tag) {
-		set_setstr(&acc->set, "tag", tag);
-	}
-	if (local) {
-		acc->flags |= ACC_FLAG_LOCAL;
-	}
-	if (locked && !g_strcasecmp(locked, "true")) {
-		acc->flags |= ACC_FLAG_LOCKED;
-	}
-	if (prpl == &protocol_missing) {
-		set_t *s = set_add(&acc->set, "_protocol_name", protocol, NULL, NULL);
-		s->flags |= SET_HIDDEN | SET_NOSAVE |
-			    ACC_SET_OFFLINE_ONLY | ACC_SET_ONLINE_ONLY;
-	}
-
-	g_free(pass_cr);
-	g_free(password);
-
-	if (prpl == &protocol_missing) {
-		handle_settings_raw(node, &acc->set);
-	} else {
-		handle_settings(node, &acc->set);
-	}
-
-	for (c = node->children; (c = xt_find_node(c, "buddy")); c = c->next) {
-		char *handle, *nick;
-
-		handle = xt_find_attr(c, "handle");
-		nick = xt_find_attr(c, "nick");
-
-		if (handle && nick) {
-			nick_set_raw(acc, handle, nick);
-		} else {
-			return XT_ABORT;
-		}
-	}
-	return XT_HANDLED;
-}
-
-static xt_status handle_channel(struct xt_node *node, gpointer data)
-{
-	struct xml_parsedata *xd = data;
-	irc_channel_t *ic;
-	char *name, *type;
-
-	name = xt_find_attr(node, "name");
-	type = xt_find_attr(node, "type");
-
-	if (!name || !type) {
-		return XT_ABORT;
-	}
-
-	/* The channel may exist already, for example if it's &bitlbee.
-	   Also, it's possible that the user just reconnected and the
-	   IRC client already rejoined all channels it was in. They
-	   should still get the right settings. */
-	if ((ic = irc_channel_by_name(xd->irc, name)) ||
-	    (ic = irc_channel_new(xd->irc, name))) {
-		set_setstr(&ic->set, "type", type);
-	}
-
-	handle_settings(node, &ic->set);
-
-	return XT_HANDLED;
-}
-
-static const struct xt_handler_entry handlers[] = {
-	{ "account", "user", handle_account, },
-	{ "channel", "user", handle_channel, },
-	{ NULL,      NULL,   NULL, },
-};
-
-static storage_status_t xml_load_real(irc_t *irc, const char *my_nick, const char *password, xml_action action)
-{
-	struct xml_parsedata xd[1];
-	char *fn, buf[2048];
-	int fd, st;
-	struct xt_parser *xp = NULL;
-	struct xt_node *node;
-	storage_status_t ret = STORAGE_OTHER_ERROR;
-
-	xd->irc = irc;
-	strncpy(xd->given_nick, my_nick, MAX_NICK_LENGTH);
-	xd->given_nick[MAX_NICK_LENGTH] = '\0';
-	nick_lc(NULL, xd->given_nick);
-	xd->given_pass = (char *) password;
-
-	fn = g_strconcat(global.conf->configdir, xd->given_nick, ".xml", NULL);
-	if ((fd = open(fn, O_RDONLY)) < 0) {
-		if (errno == ENOENT) {
-			ret = STORAGE_NO_SUCH_USER;
-		} else {
-			irc_rootmsg(irc, "Error loading user config: %s", g_strerror(errno));
-		}
-		goto error;
-	}
-
-	xp = xt_new(handlers, xd);
-	while ((st = read(fd, buf, sizeof(buf))) > 0) {
-		st = xt_feed(xp, buf, st);
-		if (st != 1) {
-			break;
-		}
-	}
-	close(fd);
-	if (st != 0) {
-		goto error;
-	}
-
-	node = xp->root;
-	if (node == NULL || node->next != NULL || strcmp(node->name, "user") != 0) {
-		goto error;
-	}
-
-	if (action == XML_PASS_CHECK) {
-		char *nick = xt_find_attr(node, "nick");
-		char *pass = xt_find_attr(node, "password");
-		char *backend = xt_find_attr(node, "auth_backend");
-
-		if (!nick || !(pass || backend)) {
-			goto error;
-		}
-
-		if (backend) {
-			g_free(xd->irc->auth_backend);
-			xd->irc->auth_backend = g_strdup(backend);
-			ret = STORAGE_CHECK_BACKEND;
-		} else if ((st = md5_verify_password(xd->given_pass, pass)) != 0) {
-			ret = STORAGE_INVALID_PASSWORD;
-		} else {
-			ret = STORAGE_OK;
-		}
-		goto error;
-	}
-
-	handle_settings(node, &xd->irc->b->set);
-
-	if (xt_handle(xp, NULL, 1) == XT_HANDLED) {
-		ret = STORAGE_OK;
-	}
-
-error:
-	xt_free(xp);
-	g_free(fn);
-	return ret;
-}
-
-static storage_status_t xml_load(irc_t *irc, const char *password)
-{
-	return xml_load_real(irc, irc->user->nick, password, XML_LOAD);
-}
-
-static storage_status_t xml_check_pass(irc_t *irc, const char *my_nick, const char *password)
-{
-	return xml_load_real(irc, my_nick, password, XML_PASS_CHECK);
-}
-
-
-static void xml_generate_settings(struct xt_node *cur, set_t **head);
-
-struct xt_node *xml_generate(irc_t *irc)
-{
-	char *pass_buf = NULL;
-	account_t *acc;
-	guint8 pass_md5[21];
-	GChecksum *md5_state;
-	GSList *l;
-	struct xt_node *root, *cur;
-	gsize digest_len = MD5_HASH_SIZE;
-
-	root = cur = xt_new_node("user", NULL, NULL);
-	if (irc->auth_backend) {
-		xt_add_attr(cur, "auth_backend", irc->auth_backend);
-	} else {
-		/* Generate a salted md5sum of the password. Use 5 bytes for the salt
-		   (to prevent dictionary lookups of passwords) to end up with a 21-
-		   byte password hash, more convenient for base64 encoding. */
-		random_bytes(pass_md5 + 16, 5);
-		md5_state = g_checksum_new(G_CHECKSUM_MD5);
-		g_checksum_update(md5_state, (guint8 *) irc->password, strlen(irc->password));
-		g_checksum_update(md5_state, pass_md5 + 16, 5);   /* Add the salt. */
-		g_checksum_get_digest(md5_state, pass_md5, &digest_len);
-		g_checksum_free(md5_state);
-		/* Save the hash in base64-encoded form. */
-		pass_buf = base64_encode(pass_md5, 21);
-		xt_add_attr(cur, "password", pass_buf);
-		g_free(pass_buf);
-	}
-
-	xt_add_attr(cur, "nick", irc->user->nick);
-	xt_add_attr(cur, "version", XML_FORMAT_VERSION);
-
-	xml_generate_settings(cur, &irc->b->set);
-
-	for (acc = irc->b->accounts; acc; acc = acc->next) {
-		GHashTableIter iter;
-		gpointer key, value;
-		unsigned char *pass_cr;
-		char *pass_b64;
-		int pass_len;
-
-		if(irc->auth_backend) {
-			/* If we don't "own" the password, it may change without us
-			 * knowing, so we cannot encrypt the data, as we then may not be
-			 * able to decrypt it */
-			pass_b64 = base64_encode((unsigned char *)acc->pass, strlen(acc->pass));
-		} else {
-			pass_len = arc_encode(acc->pass, strlen(acc->pass), (unsigned char **) &pass_cr, irc->password, 12);
-			pass_b64 = base64_encode(pass_cr, pass_len);
-			g_free(pass_cr);
-		}
-
-		cur = xt_new_node("account", NULL, NULL);
-		if (acc->prpl == &protocol_missing) {
-			xt_add_attr(cur, "protocol", set_getstr(&acc->set, "_protocol_name"));
-		} else {
-			xt_add_attr(cur, "protocol", acc->prpl->name);
-		}
-		xt_add_attr(cur, "handle", acc->user);
-		xt_add_attr(cur, "password", pass_b64);
-		xt_add_attr(cur, "autoconnect", acc->auto_connect ? "true" : "false");
-		xt_add_attr(cur, "tag", acc->tag);
-		if (acc->server && acc->server[0]) {
-			xt_add_attr(cur, "server", acc->server);
-		}
-		if (acc->flags & ACC_FLAG_LOCKED) {
-			xt_add_attr(cur, "locked", "true");
-		}
-
-		g_free(pass_b64);
-
-		g_hash_table_iter_init(&iter, acc->nicks);
-		while (g_hash_table_iter_next(&iter, &key, &value)) {
-			struct xt_node *node = xt_new_node("buddy", NULL, NULL);
-			xt_add_attr(node, "handle", key);
-			xt_add_attr(node, "nick", value);
-			xt_add_child(cur, node);
-		}
-
-		xml_generate_settings(cur, &acc->set);
-
-		xt_add_child(root, cur);
-	}
-
-	for (l = irc->channels; l; l = l->next) {
-		irc_channel_t *ic = l->data;
-
-		if (ic->flags & IRC_CHANNEL_TEMP) {
-			continue;
-		}
-
-		cur = xt_new_node("channel", NULL, NULL);
-		xt_add_attr(cur, "name", ic->name);
-		xt_add_attr(cur, "type", set_getstr(&ic->set, "type"));
-
-		xml_generate_settings(cur, &ic->set);
-
-		xt_add_child(root, cur);
-	}
-
-	return root;
-}
-
-static void xml_generate_settings(struct xt_node *cur, set_t **head)
-{
-	set_t *set;
-
-	for (set = *head; set; set = set->next) {
-		if (set->value && !(set->flags & SET_NOSAVE)) {
-			struct xt_node *xset;
-			xt_add_child(cur, xset = xt_new_node("setting", set->value, NULL));
-			xt_add_attr(xset, "name", set->key);
-			if (set->flags & SET_LOCKED) {
-				xt_add_attr(xset, "locked", "true");
-			}
-		}
-	}
-}
-
-static storage_status_t xml_save(irc_t *irc, int overwrite)
-{
-	storage_status_t ret = STORAGE_OK;
-	char path[512], *path2 = NULL, *xml = NULL;
-	struct xt_node *tree = NULL;
-	size_t len;
-	int fd;
-
-	path2 = g_strdup(irc->user->nick);
-	nick_lc(NULL, path2);
-	g_snprintf(path, sizeof(path) - 20, "%s%s%s", global.conf->configdir, path2, ".xml");
-	g_free(path2);
-
-	if (!overwrite && g_access(path, F_OK) == 0) {
-		return STORAGE_ALREADY_EXISTS;
-	}
-
-	strcat(path, ".XXXXXX");
-	if ((fd = mkstemp(path)) < 0) {
-		goto error;
-	}
-
-	tree = xml_generate(irc);
-	xml = xt_to_string_i(tree);
-	len = strlen(xml);
-	if (write(fd, xml, len) != len ||
-	    fsync(fd) != 0 ||   /* #559 */
-	    close(fd) != 0) {
-		goto error;
-	}
-
-	path2 = g_strndup(path, strlen(path) - 7);
-	if (rename(path, path2) != 0) {
-		g_free(path2);
-		goto error;
-	}
-	g_free(path2);
-
-	goto finish;
-
-error:
-	irc_rootmsg(irc, "Write error: %s", g_strerror(errno));
-	ret = STORAGE_OTHER_ERROR;
-
-finish:
-	close(fd);
-	unlink(path);
-	g_free(xml);
-	xt_free_node(tree);
-
-	return ret;
-}
-
-
-static storage_status_t xml_remove(const char *nick)
-{
-	char s[512], *lc;
-
-	lc = g_strdup(nick);
-	nick_lc(NULL, lc);
-	g_snprintf(s, 511, "%s%s%s", global.conf->configdir, lc, ".xml");
-	g_free(lc);
-
-	if (unlink(s) == -1) {
-		return STORAGE_OTHER_ERROR;
-	}
-
-	return STORAGE_OK;
-}
-
-storage_t storage_xml = {
-	.name = "xml",
-	.init = xml_init,
-	.check_pass = xml_check_pass,
-	.remove = xml_remove,
-	.load = xml_load,
-	.save = xml_save
-};
Index: sts/Makefile
===================================================================
--- tests/Makefile	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,29 +1,0 @@
--include ../Makefile.settings
-ifdef _SRCDIR_
-_SRCDIR_ := $(_SRCDIR_)tests/
-endif
-
-LFLAGS += $(LDFLAGS_TESTS)
-
-CFLAGS += $(shell pkg-config --cflags check)
-LFLAGS += $(shell pkg-config --libs check)
-
-all: check
-	./check $(CHECKFLAGS)
-
-clean:
-	rm -f check *.o
-
-distclean: clean
-
-main_objs = bitlbee.o conf.o dcc.o help.o ipc.o irc.o irc_cap.o irc_channel.o irc_commands.o irc_im.o irc_send.o irc_user.o irc_util.o irc_commands.o log.o nick.o query.o root_commands.o set.o storage.o $(STORAGE_OBJS) auth.o $(AUTH_OBJS)
-
-test_objs = check.o check_util.o check_nick.o check_md5.o check_arc.o check_irc.o check_help.o check_user.o check_set.o check_jabber_sasl.o check_jabber_util.o
-
-check: $(test_objs) $(addprefix ../, $(main_objs)) ../protocols/protocols.o ../lib/lib.o
-	@echo '*' Linking $@
-	$(VERBOSE) $(CC) $(CFLAGS) -o $@ $^ $(LFLAGS) $(EFLAGS)
-
-%.o: $(_SRCDIR_)%.c
-	@echo '*' Compiling $<
-	$(VERBOSE) $(CC) -c $(CFLAGS) $< -o $@
Index: sts/check.c
===================================================================
--- tests/check.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,136 +1,0 @@
-#include <stdlib.h>
-#include <glib.h>
-#include <gmodule.h>
-#include <check.h>
-#include <locale.h>
-#include <sys/time.h>
-#include "bitlbee.h"
-#include "testsuite.h"
-
-global_t global;        /* Against global namespace pollution */
-
-gboolean g_io_channel_pair(GIOChannel **ch1, GIOChannel **ch2)
-{
-	int sock[2];
-
-	if (socketpair(AF_UNIX, SOCK_STREAM, 0, sock) < 0) {
-		perror("socketpair");
-		return FALSE;
-	}
-
-	*ch1 = g_io_channel_unix_new(sock[0]);
-	*ch2 = g_io_channel_unix_new(sock[1]);
-	return TRUE;
-}
-
-irc_t *torture_irc(void)
-{
-	irc_t *irc;
-	GIOChannel *ch1, *ch2;
-
-	if (!g_io_channel_pair(&ch1, &ch2)) {
-		return NULL;
-	}
-	irc = irc_new(g_io_channel_unix_get_fd(ch1));
-	return irc;
-}
-
-double gettime()
-{
-	struct timeval time[1];
-
-	gettimeofday(time, 0);
-	return((double) time->tv_sec + (double) time->tv_usec / 1000000);
-}
-
-void sighandler_shutdown_setup()
-{
-	/* no-op. originally defined in unix.c, needed by bitlbee.c */
-}
-
-/* From check_util.c */
-Suite *util_suite(void);
-
-/* From check_nick.c */
-Suite *nick_suite(void);
-
-/* From check_md5.c */
-Suite *md5_suite(void);
-
-/* From check_arc.c */
-Suite *arc_suite(void);
-
-/* From check_irc.c */
-Suite *irc_suite(void);
-
-/* From check_help.c */
-Suite *help_suite(void);
-
-/* From check_user.c */
-Suite *user_suite(void);
-
-/* From check_set.c */
-Suite *set_suite(void);
-
-/* From check_jabber_sasl.c */
-Suite *jabber_sasl_suite(void);
-
-/* From check_jabber_sasl.c */
-Suite *jabber_util_suite(void);
-
-int main(int argc, char **argv)
-{
-	int nf;
-	SRunner *sr;
-	GOptionContext *pc;
-	gboolean no_fork = FALSE;
-	gboolean verbose = FALSE;
-	GOptionEntry options[] = {
-		{ "no-fork", 'n', 0, G_OPTION_ARG_NONE, &no_fork, "Don't fork" },
-		{ "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, "Be verbose", NULL },
-		{ NULL }
-	};
-
-	pc = g_option_context_new("");
-	g_option_context_add_main_entries(pc, options, NULL);
-
-	if (!g_option_context_parse(pc, &argc, &argv, NULL)) {
-		return 1;
-	}
-
-	g_option_context_free(pc);
-
-	log_init();
-	b_main_init();
-	setlocale(LC_CTYPE, "");
-
-	if (verbose) {
-		log_link(LOGLVL_ERROR, LOGOUTPUT_CONSOLE);
-#ifdef DEBUG
-		log_link(LOGLVL_DEBUG, LOGOUTPUT_CONSOLE);
-#endif
-		log_link(LOGLVL_INFO, LOGOUTPUT_CONSOLE);
-		log_link(LOGLVL_WARNING, LOGOUTPUT_CONSOLE);
-	}
-
-	global.conf = conf_load(0, NULL);
-	global.conf->runmode = RUNMODE_DAEMON;
-
-	sr = srunner_create(util_suite());
-	srunner_add_suite(sr, nick_suite());
-	srunner_add_suite(sr, md5_suite());
-	srunner_add_suite(sr, arc_suite());
-	srunner_add_suite(sr, irc_suite());
-	srunner_add_suite(sr, help_suite());
-	srunner_add_suite(sr, user_suite());
-	srunner_add_suite(sr, set_suite());
-	srunner_add_suite(sr, jabber_sasl_suite());
-	srunner_add_suite(sr, jabber_util_suite());
-	if (no_fork) {
-		srunner_set_fork_status(sr, CK_NOFORK);
-	}
-	srunner_run_all(sr, verbose ? CK_VERBOSE : CK_NORMAL);
-	nf = srunner_ntests_failed(sr);
-	srunner_free(sr);
-	return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
-}
Index: sts/check_arc.c
===================================================================
--- tests/check_arc.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,107 +1,0 @@
-#include <stdlib.h>
-#include <glib.h>
-#include <gmodule.h>
-#include <check.h>
-#include <string.h>
-#include <stdio.h>
-#include "arc.h"
-
-char *password = "ArcVier";
-
-char *clear_tests[] =
-{
-	"Wie dit leest is gek :-)",
-	"ItllBeBitlBee",
-	"One more boring password",
-	"Hoi hoi",
-	NULL
-};
-
-START_TEST(check_codec)
-{
-	int i;
-
-	for (i = 0; clear_tests[i]; i++) {
-		tcase_fn_start(clear_tests[i], __FILE__, __LINE__);
-		unsigned char *crypted;
-		char *decrypted;
-		int len;
-
-		len = arc_encode(clear_tests[i], 0, &crypted, password, 12);
-		len = arc_decode(crypted, len, &decrypted, password);
-
-		fail_if(strcmp(clear_tests[i], decrypted) != 0,
-		        "%s didn't decrypt back properly", clear_tests[i]);
-
-		g_free(crypted);
-		g_free(decrypted);
-	}
-}
-END_TEST
-
-struct {
-	unsigned char crypted[30];
-	int len;
-	char *decrypted;
-} decrypt_tests[] = {
-	/* One block with padding. */
-	{
-		{
-			0x3f, 0x79, 0xb0, 0xf5, 0x91, 0x56, 0xd2, 0x1b, 0xd1, 0x4b, 0x67, 0xac,
-			0xb1, 0x31, 0xc9, 0xdb, 0xf9, 0xaa
-		}, 18, "short pass"
-	},
-
-	/* Two blocks with padding. */
-	{
-		{
-			0xf9, 0xa6, 0xec, 0x5d, 0xc7, 0x06, 0xb8, 0x6b, 0x63, 0x9f, 0x2d, 0xb5,
-			0x7d, 0xaa, 0x32, 0xbb, 0xd8, 0x08, 0xfd, 0x81, 0x2e, 0xca, 0xb4, 0xd7,
-			0x2f, 0x36, 0x9c, 0xac, 0xa0, 0xbc
-		}, 30, "longer password"
-	},
-
-	/* This string is exactly two "blocks" long, to make sure unpadded strings also decrypt
-	   properly. */
-	{
-		{
-			0x95, 0x4d, 0xcf, 0x4d, 0x5e, 0x6c, 0xcf, 0xef, 0xb9, 0x80, 0x00, 0xef,
-			0x25, 0xe9, 0x17, 0xf6, 0x29, 0x6a, 0x82, 0x79, 0x1c, 0xca, 0x68, 0xb5,
-			0x4e, 0xd0, 0xc1, 0x41, 0x8e, 0xe6
-		}, 30, "OSCAR is really creepy.."
-	},
-	{ "", 0, NULL }
-};
-
-START_TEST(check_decod)
-{
-	int i;
-
-	for (i = 0; decrypt_tests[i].len; i++) {
-		tcase_fn_start(decrypt_tests[i].decrypted, __FILE__, __LINE__);
-		char *decrypted;
-		int len;
-
-		len = arc_decode(decrypt_tests[i].crypted, decrypt_tests[i].len,
-		                 &decrypted, password);
-
-		fail_if(len == -1,
-		        "`%s' didn't decrypt properly", decrypt_tests[i].decrypted);
-		fail_if(strcmp(decrypt_tests[i].decrypted, decrypted) != 0,
-		        "`%s' didn't decrypt properly", decrypt_tests[i].decrypted);
-
-		g_free(decrypted);
-	}
-}
-END_TEST
-
-Suite *arc_suite(void)
-{
-	Suite *s = suite_create("ArcFour");
-	TCase *tc_core = tcase_create("Core");
-
-	suite_add_tcase(s, tc_core);
-	tcase_add_test(tc_core, check_codec);
-	tcase_add_test(tc_core, check_decod);
-	return s;
-}
Index: sts/check_help.c
===================================================================
--- tests/check_help.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,39 +1,0 @@
-#include <stdlib.h>
-#include <glib.h>
-#include <gmodule.h>
-#include <check.h>
-#include <string.h>
-#include <stdio.h>
-#include "help.h"
-
-START_TEST(test_help_initfree)
-{
-    help_t * h, *r;
-    r = help_init(&h, "/dev/null");
-    fail_if(r == NULL);
-    fail_if(r != h);
-
-    help_free(&h);
-    fail_if(h != NULL);
-}
-END_TEST
-
-START_TEST(test_help_nonexistent)
-{
-    help_t * h, *r;
-    r = help_init(&h, "/dev/null");
-    fail_unless(help_get(&h, "nonexistent") == NULL);
-    fail_if(r == NULL);
-}
-END_TEST
-
-Suite *help_suite(void)
-{
-	Suite *s = suite_create("Help");
-	TCase *tc_core = tcase_create("Core");
-
-	suite_add_tcase(s, tc_core);
-	tcase_add_test(tc_core, test_help_initfree);
-	tcase_add_test(tc_core, test_help_nonexistent);
-	return s;
-}
Index: sts/check_irc.c
===================================================================
--- tests/check_irc.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,70 +1,0 @@
-#include <stdlib.h>
-#include <glib.h>
-#include <gmodule.h>
-#include <check.h>
-#include <string.h>
-#include <stdio.h>
-#include "irc.h"
-#include "events.h"
-#include "testsuite.h"
-
-START_TEST(test_connect)
-{
-    GIOChannel * ch1, *ch2;
-    irc_t *irc;
-    char *raw;
-    fail_unless(g_io_channel_pair(&ch1, &ch2));
-
-    irc = irc_new(g_io_channel_unix_get_fd(ch1));
-
-    irc_free(irc);
-
-    fail_unless(g_io_channel_read_to_end(ch2, &raw, NULL, NULL) == G_IO_STATUS_NORMAL);
-
-    fail_if(strcmp(raw, "") != 0);
-
-    g_free(raw);
-}
-END_TEST
-
-START_TEST(test_login)
-{
-    GIOChannel * ch1, *ch2;
-    irc_t *irc;
-    char *raw;
-    fail_unless(g_io_channel_pair(&ch1, &ch2));
-
-    g_io_channel_set_flags(ch1, G_IO_FLAG_NONBLOCK, NULL);
-    g_io_channel_set_flags(ch2, G_IO_FLAG_NONBLOCK, NULL);
-
-    irc = irc_new(g_io_channel_unix_get_fd(ch1));
-
-    fail_unless(g_io_channel_write_chars(ch2, "NICK bla\r\r\n"
-                "USER a a a a\n", -1, NULL, NULL) == G_IO_STATUS_NORMAL);
-    fail_unless(g_io_channel_flush(ch2, NULL) == G_IO_STATUS_NORMAL);
-
-    b_main_iteration();
-    irc_free(irc);
-
-    fail_unless(g_io_channel_read_to_end(ch2, &raw, NULL, NULL) == G_IO_STATUS_NORMAL);
-
-    fail_unless(strstr(raw, "001") != NULL);
-    fail_unless(strstr(raw, "002") != NULL);
-    fail_unless(strstr(raw, "003") != NULL);
-    fail_unless(strstr(raw, "004") != NULL);
-    fail_unless(strstr(raw, "005") != NULL);
-
-    g_free(raw);
-}
-END_TEST
-
-Suite *irc_suite(void)
-{
-	Suite *s = suite_create("IRC");
-	TCase *tc_core = tcase_create("Core");
-
-	suite_add_tcase(s, tc_core);
-	tcase_add_test(tc_core, test_connect);
-	tcase_add_test(tc_core, test_login);
-	return s;
-}
Index: sts/check_jabber_sasl.c
===================================================================
--- tests/check_jabber_sasl.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,116 +1,0 @@
-#include <stdlib.h>
-#include <glib.h>
-#include <gmodule.h>
-#include <check.h>
-#include <string.h>
-#include <stdio.h>
-
-char *sasl_get_part(char *data, char *field);
-
-#define challenge1 "nonce=\"1669585310\",qop=\"auth\",charset=utf-8,algorithm=md5-sess," \
-	"something=\"Not \\\"standardized\\\"\""
-#define challenge2 "realm=\"quadpoint.org\", nonce=\"NPotlQpQf9RNYodOwierkQ==\", " \
-	"qop=\"auth, auth-int\", charset=utf-8, algorithm=md5-sess"
-#define challenge3 ", realm=\"localhost\", nonce=\"LlBV2txnO8RbB5hgs3KgiQ==\", " \
-	"qop=\"auth, auth-int, \", ,\n, charset=utf-8, algorithm=md5-sess,"
-
-struct {
-	char *challenge;
-	char *key;
-	char *value;
-} get_part_tests[] = {
-	{
-		challenge1,
-		"nonce",
-		"1669585310"
-	},
-	{
-		challenge1,
-		"charset",
-		"utf-8"
-	},
-	{
-		challenge1,
-		"harset",
-		NULL
-	},
-	{
-		challenge1,
-		"something",
-		"Not \"standardized\""
-	},
-	{
-		challenge1,
-		"something_else",
-		NULL
-	},
-	{
-		challenge2,
-		"realm",
-		"quadpoint.org",
-	},
-	{
-		challenge2,
-		"real",
-		NULL
-	},
-	{
-		challenge2,
-		"qop",
-		"auth, auth-int"
-	},
-	{
-		challenge3,
-		"realm",
-		"localhost"
-	},
-	{
-		challenge3,
-		"qop",
-		"auth, auth-int, "
-	},
-	{
-		challenge3,
-		"charset",
-		"utf-8"
-	},
-	{ NULL, NULL, NULL }
-};
-
-START_TEST(check_get_part)
-{
-	int i;
-
-	for (i = 0; get_part_tests[i].key; i++) {
-		tcase_fn_start(get_part_tests[i].key, __FILE__, i);
-		char *res;
-
-		res = sasl_get_part(get_part_tests[i].challenge,
-		                    get_part_tests[i].key);
-
-		if (get_part_tests[i].value == NULL) {
-			fail_if(res != NULL, "Found key %s in %s while it shouldn't be there!",
-			        get_part_tests[i].key, get_part_tests[i].challenge);
-		} else if (res) {
-			fail_unless(strcmp(res, get_part_tests[i].value) == 0,
-			            "Incorrect value for key %s in %s: %s",
-			            get_part_tests[i].key, get_part_tests[i].challenge, res);
-		} else {
-			fail("Could not find key %s in %s",
-			     get_part_tests[i].key, get_part_tests[i].challenge);
-		}
-
-		g_free(res);
-	}
-}
-END_TEST
-
-Suite *jabber_sasl_suite(void)
-{
-	Suite *s = suite_create("jabber/sasl");
-	TCase *tc_core = tcase_create("Core");
-
-	suite_add_tcase(s, tc_core);
-	tcase_add_test(tc_core, check_get_part);
-	return s;
-}
Index: sts/check_jabber_util.c
===================================================================
--- tests/check_jabber_util.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,149 +1,0 @@
-#include <stdlib.h>
-#include <glib.h>
-#include <gmodule.h>
-#include <check.h>
-#include <string.h>
-#include <stdio.h>
-#include "jabber/jabber.h"
-
-static struct im_connection *ic;
-
-START_TEST(check_buddy_add)
-{
-	struct jabber_buddy *budw1, *budw2, *budw3, *budn, *bud;
-
-	budw1 = jabber_buddy_add(ic, "wilmer@gaast.net/BitlBee");
-	budw1->last_msg = time(NULL) - 100;
-	budw2 = jabber_buddy_add(ic, "WILMER@gaast.net/Telepathy");
-	budw2->priority = 2;
-	budw2->last_msg = time(NULL);
-	budw3 = jabber_buddy_add(ic, "wilmer@GAAST.NET/bitlbee");
-	budw3->last_msg = time(NULL) - 200;
-	budw3->priority = 4;
-	/* TODO(wilmer): Shouldn't this just return budw3? */
-	fail_if(jabber_buddy_add(ic, "wilmer@gaast.net/Telepathy") != NULL);
-
-	budn = jabber_buddy_add(ic, "nekkid@lamejab.net");
-	/* Shouldn't be allowed if there's already a bare JID. */
-	fail_if(jabber_buddy_add(ic, "nekkid@lamejab.net/Illegal"));
-
-	/* Case sensitivity: Case only matters after the / */
-	fail_if(jabber_buddy_by_jid(ic, "wilmer@gaast.net/BitlBee", 0) ==
-	        jabber_buddy_by_jid(ic, "wilmer@gaast.net/bitlbee", 0));
-	fail_if(jabber_buddy_by_jid(ic, "wilmer@gaast.net/telepathy", 0));
-
-	fail_unless(jabber_buddy_by_jid(ic, "wilmer@gaast.net/BitlBee", 0) == budw1);
-	fail_unless(jabber_buddy_by_jid(ic, "WILMER@GAAST.NET/BitlBee", GET_BUDDY_EXACT) == budw1);
-	fail_unless(jabber_buddy_by_jid(ic, "wilmer@GAAST.NET/BitlBee", GET_BUDDY_CREAT) == budw1);
-
-	fail_unless(jabber_buddy_by_jid(ic, "wilmer@gaast.net", GET_BUDDY_EXACT) != NULL);
-	fail_unless(jabber_buddy_by_jid(ic, "WILMER@gaast.net", 0) == budw3);
-
-	/* Check O_FIRST and see if it's indeed the first item from the list. */
-	fail_unless((bud = jabber_buddy_by_jid(ic, "wilmer@gaast.net", GET_BUDDY_FIRST)) == budw1);
-	fail_unless(bud->next == budw2 && bud->next->next == budw3 && bud->next->next->next == NULL);
-
-	/* Change the resource_select setting, now we should get a different resource. */
-	set_setstr(&ic->acc->set, "resource_select", "activity");
-	fail_unless(jabber_buddy_by_jid(ic, "wilmer@GAAST.NET", 0) == budw2);
-
-	/* Some testing of bare JID handling (which is horrible). */
-	fail_if(jabber_buddy_by_jid(ic, "nekkid@lamejab.net/Illegal", 0));
-	fail_if(jabber_buddy_by_jid(ic, "NEKKID@LAMEJAB.NET/Illegal", GET_BUDDY_CREAT));
-	fail_unless(jabber_buddy_by_jid(ic, "nekkid@lamejab.net", 0) == budn);
-	fail_unless(jabber_buddy_by_jid(ic, "NEKKID@lamejab.net", GET_BUDDY_EXACT) == budn);
-	fail_unless(jabber_buddy_by_jid(ic, "nekkid@LAMEJAB.NET", GET_BUDDY_CREAT) == budn);
-
-	/* More case sensitivity testing, and see if remove works properly. */
-	fail_if(jabber_buddy_remove(ic, "wilmer@gaast.net/telepathy"));
-	fail_if(jabber_buddy_by_jid(ic, "wilmer@GAAST.NET/telepathy", GET_BUDDY_CREAT) == budw2);
-	fail_unless(jabber_buddy_remove(ic, "wilmer@gaast.net/Telepathy"));
-	fail_unless(jabber_buddy_remove(ic, "wilmer@gaast.net/telepathy"));
-
-	/* Test activity_timeout and GET_BUDDY_BARE_OK. */
-	fail_unless(jabber_buddy_by_jid(ic, "wilmer@gaast.net", GET_BUDDY_BARE_OK) == budw1);
-	budw1->last_msg -= 50;
-	fail_unless((bud = jabber_buddy_by_jid(ic, "wilmer@gaast.net", GET_BUDDY_BARE_OK)) != NULL);
-	fail_unless(strcmp(bud->full_jid, "wilmer@gaast.net") == 0);
-
-	fail_if(jabber_buddy_remove(ic, "wilmer@gaast.net"));
-	fail_unless(jabber_buddy_by_jid(ic, "wilmer@gaast.net", 0) == budw1);
-
-	fail_if(jabber_buddy_remove(ic, "wilmer@gaast.net"));
-	fail_unless(jabber_buddy_remove(ic, "wilmer@gaast.net/bitlbee"));
-	fail_unless(jabber_buddy_remove(ic, "wilmer@gaast.net/BitlBee"));
-	fail_if(jabber_buddy_by_jid(ic, "wilmer@gaast.net", GET_BUDDY_BARE_OK));
-
-	/* Check if remove_bare() indeed gets rid of all. */
-	/* disable this one for now.
-	fail_unless( jabber_buddy_remove_bare( ic, "wilmer@gaast.net" ) );
-	fail_if( jabber_buddy_by_jid( ic, "wilmer@gaast.net", 0 ) );
-	*/
-
-	fail_if(jabber_buddy_remove(ic, "nekkid@lamejab.net/Illegal"));
-	fail_unless(jabber_buddy_remove(ic, "nekkid@lamejab.net"));
-	fail_if(jabber_buddy_by_jid(ic, "nekkid@lamejab.net", 0) != NULL);
-
-	/* Fixing a bug in this branch that caused information to get lost when
-	   removing the first full JID from a list. */
-	jabber_buddy_add(ic, "bugtest@google.com/A");
-	jabber_buddy_add(ic, "bugtest@google.com/B");
-	jabber_buddy_add(ic, "bugtest@google.com/C");
-	fail_unless(jabber_buddy_remove(ic, "bugtest@google.com/A"));
-	fail_unless(jabber_buddy_remove(ic, "bugtest@google.com/B"));
-	fail_unless(jabber_buddy_remove(ic, "bugtest@google.com/C"));
-}
-END_TEST
-
-START_TEST(check_compareJID)
-{
-	fail_unless(jabber_compare_jid("bugtest@google.com/B", "bugtest@google.com/A"));
-	fail_if(jabber_compare_jid("bugtest1@google.com/B", "bugtest@google.com/A"));
-	fail_if(jabber_compare_jid("bugtest@google.com/B", "bugtest1@google.com/A"));
-	fail_if(jabber_compare_jid("bugtest1@google.com/B", "bugtest2@google.com/A"));
-	fail_unless(jabber_compare_jid("bugtest@google.com/A", "bugtest@google.com/A"));
-	fail_if(jabber_compare_jid("", "bugtest@google.com/A"));
-	fail_if(jabber_compare_jid(NULL, ""));
-	fail_if(jabber_compare_jid("", NULL));
-}
-END_TEST
-
-START_TEST(check_hipchat_slug)
-{
-	int i;
-
-	const char *tests[] = {
-		"test !\"#$%&\'()*+,-./0123456789:;<=>?@ABC", "test_!#$%\()*+,-.0123456789;=?abc",
-		"test XYZ[\\]^_`abc", "test_xyz[\\]^_`abc",
-		"test {|}~¡¢£¤¥¦§¨©ª«¬\xad®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆ", "test_{|}~¡¢£¤¥¦§¨©ª«¬\xad®¯°±²³´µ¶·¸¹º»¼½¾¿àáâãäåæ",
-		"test Ĳ ĳ I ı I ı", "test_ĳ_ĳ_i_ı_i_ı",
-		NULL,
-	};
-
-	for (i = 0; tests[i]; i += 2) {
-		char *new = hipchat_make_channel_slug(tests[i]);
-		fail_unless(!strcmp(tests[i + 1], new));
-		g_free(new);
-	}
-}
-END_TEST
-
-Suite *jabber_util_suite(void)
-{
-	Suite *s = suite_create("jabber/util");
-	TCase *tc_core = tcase_create("Buddy");
-	struct jabber_data *jd;
-
-	ic = g_new0(struct im_connection, 1);
-	ic->acc = g_new0(account_t, 1);
-	ic->proto_data = jd = g_new0(struct jabber_data, 1);
-	jd->buddies = g_hash_table_new(g_str_hash, g_str_equal);
-	set_add(&ic->acc->set, "resource_select", "priority", NULL, ic->acc);
-	set_add(&ic->acc->set, "activity_timeout", "120", NULL, ic->acc);
-
-	suite_add_tcase(s, tc_core);
-	tcase_add_test(tc_core, check_buddy_add);
-	tcase_add_test(tc_core, check_compareJID);
-	tcase_add_test(tc_core, check_hipchat_slug);
-	return s;
-}
Index: sts/check_md5.c
===================================================================
--- tests/check_md5.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,58 +1,0 @@
-#include <stdlib.h>
-#include <glib.h>
-#include <gmodule.h>
-#include <check.h>
-#include <string.h>
-#include <stdio.h>
-#include "md5.h"
-
-/* From RFC 1321 */
-struct md5_test {
-	const char *str;
-	md5_byte_t expected[16];
-} tests[] = {
-	{ "",
-	  { 0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04, 0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e } },
-	{ "a",
-	  { 0x0c, 0xc1, 0x75, 0xb9, 0xc0, 0xf1, 0xb6, 0xa8, 0x31, 0xc3, 0x99, 0xe2, 0x69, 0x77, 0x26, 0x61 } },
-	{ "abc",
-	  { 0x90, 0x01, 0x50, 0x98, 0x3c, 0xd2, 0x4f, 0xb0, 0xd6, 0x96, 0x3f, 0x7d, 0x28, 0xe1, 0x7f, 0x72 } },
-	{ "message digest",
-	  { 0xf9, 0x6b, 0x69, 0x7d, 0x7c, 0xb7, 0x93, 0x8d, 0x52, 0x5a, 0x2f, 0x31, 0xaa, 0xf1, 0x61, 0xd0 } },
-	{ "abcdefghijklmnopqrstuvwxyz",
-	  { 0xc3, 0xfc, 0xd3, 0xd7, 0x61, 0x92, 0xe4, 0x00, 0x7d, 0xfb, 0x49, 0x6c, 0xca, 0x67, 0xe1, 0x3b } },
-	{ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
-	  { 0xd1, 0x74, 0xab, 0x98, 0xd2, 0x77, 0xd9, 0xf5, 0xa5, 0x61, 0x1c, 0x2c, 0x9f, 0x41, 0x9d, 0x9f } },
-	{ "12345678901234567890123456789012345678901234567890123456789012345678901234567890",
-	  { 0x57, 0xed, 0xf4, 0xa2, 0x2b, 0xe3, 0xc9, 0x55, 0xac, 0x49, 0xda, 0x2e, 0x21, 0x07, 0xb6, 0x7a } },
-
-	{ NULL },
-};
-
-START_TEST(check_sums)
-{
-	int i;
-
-	for (i = 0; tests[i].str; i++) {
-		md5_byte_t sum[16];
-		tcase_fn_start(tests[i].str, __FILE__, __LINE__);
-		md5_state_t state;
-
-		md5_init(&state);
-		md5_append(&state, (const md5_byte_t *) tests[i].str, strlen(tests[i].str));
-		md5_finish(&state, sum);
-
-		fail_if(memcmp(tests[i].expected, sum, 16) != 0, "%s failed", tests[i].str);
-	}
-}
-END_TEST
-
-Suite *md5_suite(void)
-{
-	Suite *s = suite_create("MD5");
-	TCase *tc_core = tcase_create("Core");
-
-	suite_add_tcase(s, tc_core);
-	tcase_add_test(tc_core, check_sums);
-	return s;
-}
Index: sts/check_nick.c
===================================================================
--- tests/check_nick.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,77 +1,0 @@
-#include <stdlib.h>
-#include <glib.h>
-#include <gmodule.h>
-#include <check.h>
-#include <string.h>
-#include "irc.h"
-#include "set.h"
-#include "misc.h"
-#include "bitlbee.h"
-
-START_TEST(test_nick_strip){
-	int i;
-	const char *get[] = { "test:", "test", "test\n",
-		              "thisisaveryveryveryverylongnick",
-		              "thisisave:ryveryveryverylongnick",
-		              "t::::est",
-		              "test123",
-		              "123test",
-		              "123",
-		              NULL };
-	const char *expected[] = { "test", "test", "test",
-		                   "thisisaveryveryveryveryl",
-		                   "thisisaveryveryveryveryl",
-		                   "test",
-		                   "test123",
-		                   "_123test",
-		                   "_123",
-		                   NULL };
-
-	for (i = 0; get[i]; i++) {
-		char copy[60];
-		strcpy(copy, get[i]);
-		nick_strip(NULL, copy);
-		fail_unless(strcmp(copy, expected[i]) == 0,
-		            "(%d) nick_strip broken: %s -> %s (expected: %s)",
-		            i, get[i], copy, expected[i]);
-	}
-}
-END_TEST
-
-START_TEST(test_nick_ok_ok)
-{
-	const char *nicks[] = { "foo", "bar123", "bla[", "blie]", "BreEZaH",
-		                "\\od^~", "_123", "_123test", NULL };
-	int i;
-
-	for (i = 0; nicks[i]; i++) {
-		fail_unless(nick_ok(NULL, nicks[i]) == 1,
-		            "nick_ok() failed: %s", nicks[i]);
-	}
-}
-END_TEST
-
-START_TEST(test_nick_ok_notok)
-{
-	const char *nicks[] = { "thisisaveryveryveryveryveryveryverylongnick",
-		                "\nillegalchar", "", "nick%", "123test", NULL };
-	int i;
-
-	for (i = 0; nicks[i]; i++) {
-		fail_unless(nick_ok(NULL, nicks[i]) == 0,
-		            "nick_ok() succeeded for invalid: %s", nicks[i]);
-	}
-}
-END_TEST
-
-Suite *nick_suite(void)
-{
-	Suite *s = suite_create("Nick");
-	TCase *tc_core = tcase_create("Core");
-
-	suite_add_tcase(s, tc_core);
-	tcase_add_test(tc_core, test_nick_ok_ok);
-	tcase_add_test(tc_core, test_nick_ok_notok);
-	tcase_add_test(tc_core, test_nick_strip);
-	return s;
-}
Index: sts/check_set.c
===================================================================
--- tests/check_set.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,149 +1,0 @@
-#include <stdlib.h>
-#include <glib.h>
-#include <gmodule.h>
-#include <check.h>
-#include <string.h>
-#include "set.h"
-#include "testsuite.h"
-
-START_TEST(test_set_add)
-{
-    void *data = "data";
-    set_t *s = NULL, *t;
-    t = set_add(&s, "name", "default", NULL, data);
-    fail_unless(s == t);
-    fail_unless(t->data == data);
-    fail_unless(strcmp(t->def, "default") == 0);
-}
-END_TEST
-
-START_TEST(test_set_add_existing)
-{
-    void *data = "data";
-    set_t *s = NULL, *t;
-    t = set_add(&s, "name", "default", NULL, data);
-    t = set_add(&s, "name", "newdefault", NULL, data);
-    fail_unless(s == t);
-    fail_unless(strcmp(t->def, "newdefault") == 0);
-}
-END_TEST
-
-START_TEST(test_set_find_unknown)
-{
-    set_t * s = NULL;
-    fail_unless(set_find(&s, "foo") == NULL);
-}
-END_TEST
-
-START_TEST(test_set_find)
-{
-    void *data = "data";
-    set_t *s = NULL, *t;
-    t = set_add(&s, "name", "default", NULL, data);
-    fail_unless(s == t);
-    fail_unless(set_find(&s, "name") == t);
-}
-END_TEST
-
-START_TEST(test_set_get_str_default)
-{
-    void *data = "data";
-    set_t *s = NULL, *t;
-    t = set_add(&s, "name", "default", NULL, data);
-    fail_unless(s == t);
-    fail_unless(strcmp(set_getstr(&s, "name"), "default") == 0);
-}
-END_TEST
-
-START_TEST(test_set_get_bool_default)
-{
-    void *data = "data";
-    set_t *s = NULL, *t;
-    t = set_add(&s, "name", "true", NULL, data);
-    fail_unless(s == t);
-    fail_unless(set_getbool(&s, "name"));
-}
-END_TEST
-
-START_TEST(test_set_get_bool_integer)
-{
-    void *data = "data";
-    set_t *s = NULL, *t;
-    t = set_add(&s, "name", "3", NULL, data);
-    fail_unless(s == t);
-    fail_unless(set_getbool(&s, "name") == 3);
-}
-END_TEST
-
-START_TEST(test_set_get_bool_unknown)
-{
-    set_t * s = NULL;
-    fail_unless(set_getbool(&s, "name") == 0);
-}
-END_TEST
-
-START_TEST(test_set_get_str_value)
-{
-    void *data = "data";
-    set_t *s = NULL;
-    set_add(&s, "name", "default", NULL, data);
-    set_setstr(&s, "name", "foo");
-    fail_unless(strcmp(set_getstr(&s, "name"), "foo") == 0);
-}
-END_TEST
-
-START_TEST(test_set_get_str_unknown)
-{
-    set_t * s = NULL;
-    fail_unless(set_getstr(&s, "name") == NULL);
-}
-END_TEST
-
-START_TEST(test_setint)
-{
-    void *data = "data";
-    set_t *s = NULL;
-    set_add(&s, "name", "10", NULL, data);
-    set_setint(&s, "name", 3);
-    fail_unless(set_getint(&s, "name") == 3);
-}
-END_TEST
-
-START_TEST(test_setstr)
-{
-    void *data = "data";
-    set_t *s = NULL;
-    set_add(&s, "name", "foo", NULL, data);
-    set_setstr(&s, "name", "bloe");
-    fail_unless(strcmp(set_getstr(&s, "name"), "bloe") == 0);
-}
-END_TEST
-
-START_TEST(test_set_get_int_unknown)
-{
-    set_t * s = NULL;
-    fail_unless(set_getint(&s, "foo") == 0);
-}
-END_TEST
-
-Suite *set_suite(void)
-{
-	Suite *s = suite_create("Set");
-	TCase *tc_core = tcase_create("Core");
-
-	suite_add_tcase(s, tc_core);
-	tcase_add_test(tc_core, test_set_add);
-	tcase_add_test(tc_core, test_set_add_existing);
-	tcase_add_test(tc_core, test_set_find_unknown);
-	tcase_add_test(tc_core, test_set_find);
-	tcase_add_test(tc_core, test_set_get_str_default);
-	tcase_add_test(tc_core, test_set_get_str_value);
-	tcase_add_test(tc_core, test_set_get_str_unknown);
-	tcase_add_test(tc_core, test_set_get_bool_default);
-	tcase_add_test(tc_core, test_set_get_bool_integer);
-	tcase_add_test(tc_core, test_set_get_bool_unknown);
-	tcase_add_test(tc_core, test_set_get_int_unknown);
-	tcase_add_test(tc_core, test_setint);
-	tcase_add_test(tc_core, test_setstr);
-	return s;
-}
Index: sts/check_user.c
===================================================================
--- tests/check_user.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,78 +1,0 @@
-#include <stdlib.h>
-#include <glib.h>
-#include <gmodule.h>
-#include <check.h>
-#include <string.h>
-#include "bitlbee.h"
-#include "testsuite.h"
-
-#if 0
-START_TEST(test_user_add)
-irc_t * irc = torture_irc();
-user_t *user;
-user = user_add(irc, "foo");
-fail_if(user == NULL);
-fail_if(strcmp(user->nick, "foo") != 0);
-fail_unless(user_find(irc, "foo") == user);
-END_TEST
-
-START_TEST(test_user_add_exists)
-irc_t * irc = torture_irc();
-user_t *user;
-user = user_add(irc, "foo");
-fail_if(user == NULL);
-user = user_add(irc, "foo");
-fail_unless(user == NULL);
-END_TEST
-
-START_TEST(test_user_add_invalid)
-irc_t * irc = torture_irc();
-user_t *user;
-user = user_add(irc, ":foo");
-fail_unless(user == NULL);
-END_TEST
-
-START_TEST(test_user_del_invalid)
-irc_t * irc = torture_irc();
-fail_unless(user_del(irc, ":foo") == 0);
-END_TEST
-
-START_TEST(test_user_del)
-irc_t * irc = torture_irc();
-user_t *user;
-user = user_add(irc, "foo");
-fail_unless(user_del(irc, "foo") == 1);
-fail_unless(user_find(irc, "foo") == NULL);
-END_TEST
-
-START_TEST(test_user_del_nonexistent)
-irc_t * irc = torture_irc();
-fail_unless(user_del(irc, "foo") == 0);
-END_TEST
-
-START_TEST(test_user_rename)
-irc_t * irc = torture_irc();
-user_t *user;
-user = user_add(irc, "foo");
-user_rename(irc, "foo", "bar");
-fail_unless(user_find(irc, "foo") == NULL);
-fail_if(user_find(irc, "bar") == NULL);
-END_TEST
-#endif
-Suite *user_suite(void)
-{
-	Suite *s = suite_create("User");
-	TCase *tc_core = tcase_create("Core");
-
-	suite_add_tcase(s, tc_core);
-#if 0
-	tcase_add_test(tc_core, test_user_add);
-	tcase_add_test(tc_core, test_user_add_invalid);
-	tcase_add_test(tc_core, test_user_add_exists);
-	tcase_add_test(tc_core, test_user_del_invalid);
-	tcase_add_test(tc_core, test_user_del_nonexistent);
-	tcase_add_test(tc_core, test_user_del);
-	tcase_add_test(tc_core, test_user_rename);
-#endif
-	return s;
-}
Index: sts/check_util.c
===================================================================
--- tests/check_util.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,251 +1,0 @@
-#include <stdlib.h>
-#include <glib.h>
-#include <gmodule.h>
-#include <check.h>
-#include <string.h>
-#include "irc.h"
-#include "set.h"
-#include "misc.h"
-#include "url.h"
-
-START_TEST(test_strip_linefeed)
-{
-	int i;
-	const char *get[] = { "Test", "Test\r", "Test\rX\r", NULL };
-	const char *expected[] = { "Test", "Test", "TestX", NULL };
-
-	for (i = 0; get[i]; i++) {
-		char copy[20];
-		strcpy(copy, get[i]);
-		strip_linefeed(copy);
-		fail_unless(strcmp(copy, expected[i]) == 0,
-		            "(%d) strip_linefeed broken: %s -> %s (expected: %s)",
-		            i, get[i], copy, expected[i]);
-	}
-}
-END_TEST
-
-START_TEST(test_strip_newlines)
-{
-	int i;
-	const char *get[] = { "Test", "Test\r\n", "Test\nX\n", NULL };
-	const char *expected[] = { "Test", "Test  ", "Test X ", NULL };
-
-	for (i = 0; get[i]; i++) {
-		char copy[20], *ret;
-		strcpy(copy, get[i]);
-		ret = strip_newlines(copy);
-		fail_unless(strcmp(copy, expected[i]) == 0,
-		            "(%d) strip_newlines broken: %s -> %s (expected: %s)",
-		            i, get[i], copy, expected[i]);
-		fail_unless(copy == ret, "Original string not returned");
-	}
-}
-END_TEST
-
-START_TEST(test_set_url_http)
-{
-    url_t url;
-
-    fail_if(0 == url_set(&url, "http://host/"));
-    fail_unless(!strcmp(url.host, "host"));
-    fail_unless(!strcmp(url.file, "/"));
-    fail_unless(!strcmp(url.user, ""));
-    fail_unless(!strcmp(url.pass, ""));
-    fail_unless(url.proto == PROTO_HTTP);
-    fail_unless(url.port == 80);
-}
-END_TEST
-
-START_TEST(test_set_url_https)
-{
-    url_t url;
-
-    fail_if(0 == url_set(&url, "https://ahost/AimeeMann"));
-    fail_unless(!strcmp(url.host, "ahost"));
-    fail_unless(!strcmp(url.file, "/AimeeMann"));
-    fail_unless(!strcmp(url.user, ""));
-    fail_unless(!strcmp(url.pass, ""));
-    fail_unless(url.proto == PROTO_HTTPS);
-    fail_unless(url.port == 443);
-}
-END_TEST
-
-START_TEST(test_set_url_port)
-{
-    url_t url;
-
-    fail_if(0 == url_set(&url, "https://ahost:200/Lost/In/Space"));
-    fail_unless(!strcmp(url.host, "ahost"));
-    fail_unless(!strcmp(url.file, "/Lost/In/Space"));
-    fail_unless(!strcmp(url.user, ""));
-    fail_unless(!strcmp(url.pass, ""));
-    fail_unless(url.proto == PROTO_HTTPS);
-    fail_unless(url.port == 200);
-}
-END_TEST
-
-START_TEST(test_set_url_username)
-{
-    url_t url;
-
-    fail_if(0 == url_set(&url, "socks4://user@ahost/Space"));
-    fail_unless(!strcmp(url.host, "ahost"));
-    fail_unless(!strcmp(url.file, "/Space"));
-    fail_unless(!strcmp(url.user, "user"));
-    fail_unless(!strcmp(url.pass, ""));
-    fail_unless(url.proto == PROTO_SOCKS4);
-    fail_unless(url.port == 1080);
-}
-END_TEST
-
-START_TEST(test_set_url_username_pwd)
-{
-    url_t url;
-
-    fail_if(0 == url_set(&url, "socks5://user:pass@ahost/"));
-    fail_unless(!strcmp(url.host, "ahost"));
-    fail_unless(!strcmp(url.file, "/"));
-    fail_unless(!strcmp(url.user, "user"));
-    fail_unless(!strcmp(url.pass, "pass"));
-    fail_unless(url.proto == PROTO_SOCKS5);
-    fail_unless(url.port == 1080);
-}
-END_TEST
-
-struct {
-	char *orig;
-	int line_len;
-	char *wrapped;
-} word_wrap_tests[] = {
-	{
-		"Line-wrapping is not as easy as it seems?",
-		16,
-		"Line-wrapping is\nnot as easy as\nit seems?"
-	},
-	{
-		"Line-wrapping is not as easy as it seems?",
-		8,
-		"Line-\nwrapping\nis not\nas easy\nas it\nseems?"
-	},
-	{
-		"Line-wrapping is\nnot as easy as it seems?",
-		8,
-		"Line-\nwrapping\nis\nnot as\neasy as\nit\nseems?"
-	},
-	{
-		"a aa aaa aaaa aaaaa aaaaaa aaaaaaa aaaaaaaa",
-		5,
-		"a aa\naaa\naaaa\naaaaa\naaaaa\na\naaaaa\naa\naaaaa\naaa",
-	},
-	{
-		"aaaaaaaa aaaaaaa aaaaaa aaaaa aaaa aaa aa a",
-		5,
-		"aaaaa\naaa\naaaaa\naa\naaaaa\na\naaaaa\naaaa\naaa\naa a",
-	},
-	{
-		"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
-		5,
-		"aaaaa\naaaaa\naaaaa\naaaaa\naaaaa\naaaaa\naaaaa\na",
-	},
-	{
-		"áááááááááá",
-		11,
-		"ááááá\nááááá",
-	},
-	{
-		NULL
-	}
-};
-
-START_TEST(test_word_wrap)
-{
-    int i;
-
-    for (i = 0; word_wrap_tests[i].orig && *word_wrap_tests[i].orig; i++) {
-        char *wrapped = word_wrap(word_wrap_tests[i].orig, word_wrap_tests[i].line_len);
-
-        fail_unless(strcmp(word_wrap_tests[i].wrapped, wrapped) == 0,
-                "%s (line_len = %d) should wrap to `%s', not to `%s'",
-                word_wrap_tests[i].orig, word_wrap_tests[i].line_len,
-                word_wrap_tests[i].wrapped, wrapped);
-
-        g_free(wrapped);
-    }
-}
-END_TEST
-
-START_TEST(test_http_encode)
-{
-    char s[80];
-
-    strcpy(s, "ee\xc3" "\xab" "ee!!...");
-    http_encode(s);
-    fail_unless(strcmp(s, "ee%C3%ABee%21%21...") == 0);
-}
-END_TEST
-
-struct {
-	int limit;
-	char *command;
-	char *expected[IRC_MAX_ARGS + 1];
-} split_tests[] = {
-	{
-		0, "account add etc \"user name with spaces\" 'pass\\ word'",
-		{ "account", "add", "etc", "user name with spaces", "pass\\ word", NULL },
-	},
-	{
-		0, "channel set group Close\\ friends",
-		{ "channel", "set", "group", "Close friends", NULL },
-	},
-	{
-		2, "reply wilmer \"testing in C is a PITA\", you said.",
-		{ "reply", "wilmer", "\"testing in C is a PITA\", you said.", NULL },
-	},
-	{
-		4, "one space  two  spaces  limit  limit",
-		{ "one", "space", "two", "spaces", "limit  limit", NULL },
-	},
-	{
-		0, NULL,
-		{ NULL }
-	},
-};
-
-START_TEST(test_split_command_parts)
-{
-    int i;
-    for (i = 0; split_tests[i].command; i++) {
-        char *cmd = g_strdup(split_tests[i].command);
-        char **split = split_command_parts(cmd, split_tests[i].limit);
-        char **expected = split_tests[i].expected;
-
-        int j;
-        for (j = 0; split[j] && expected[j]; j++) {
-            fail_unless(strcmp(split[j], expected[j]) == 0,
-                    "(%d) split_command_parts broken: split(\"%s\")[%d] -> %s (expected: %s)",
-                    i, split_tests[i].command, j, split[j], expected[j]);
-        }
-        g_free(cmd);
-    }
-}
-END_TEST
-
-Suite *util_suite(void)
-{
-	Suite *s = suite_create("Util");
-	TCase *tc_core = tcase_create("Core");
-
-	suite_add_tcase(s, tc_core);
-	tcase_add_test(tc_core, test_strip_linefeed);
-	tcase_add_test(tc_core, test_strip_newlines);
-	tcase_add_test(tc_core, test_set_url_http);
-	tcase_add_test(tc_core, test_set_url_https);
-	tcase_add_test(tc_core, test_set_url_port);
-	tcase_add_test(tc_core, test_set_url_username);
-	tcase_add_test(tc_core, test_set_url_username_pwd);
-	tcase_add_test(tc_core, test_word_wrap);
-	tcase_add_test(tc_core, test_http_encode);
-	tcase_add_test(tc_core, test_split_command_parts);
-	return s;
-}
Index: sts/testsuite.h
===================================================================
--- tests/testsuite.h	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,9 +1,0 @@
-#ifndef __BITLBEE_CHECK_H__
-#define __BITLBEE_CHECK_H__
-
-#include "irc.h"
-
-irc_t *torture_irc(void);
-gboolean g_io_channel_pair(GIOChannel **ch1, GIOChannel **ch2);
-
-#endif /* __BITLBEE_CHECK_H__ */
Index: unix.c
===================================================================
--- unix.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ unix.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -1,6 +1,6 @@
-/********************************************************************\
+  /********************************************************************\
   * BitlBee -- An IRC to other IM-networks gateway                     *
   *                                                                    *
-  * Copyright 2002-2012 Wilmer van der Gaast and others                *
+  * Copyright 2002-2004 Wilmer van der Gaast and others                *
   \********************************************************************/
 
@@ -20,376 +20,139 @@
   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., 51 Franklin St.,
-  Fifth Floor, Boston, MA  02110-1301  USA
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
 */
 
 #include "bitlbee.h"
-
-#include "arc.h"
-#include "base64.h"
 #include "commands.h"
+#include "crypting.h"
 #include "protocols/nogaim.h"
 #include "help.h"
-#include "ipc.h"
-#include "md5.h"
-#include "misc.h"
 #include <signal.h>
 #include <unistd.h>
 #include <sys/time.h>
-#include <sys/wait.h>
-#include <pwd.h>
-#include <locale.h>
-#include <grp.h>
 
-#if defined(OTR_BI) || defined(OTR_PI)
-#include "otr.h"
-#endif
+global_t global;	/* Against global namespace pollution */
 
-#ifdef HAVE_BACKTRACE
-#include <execinfo.h>
-#endif
+static void sighandler( int signal );
+gboolean bitlbee_dirty_workaround( gpointer data );
 
-global_t global;        /* Against global namespace pollution */
-
-static struct {
-	int fd[2];
-	int tag;
-} shutdown_pipe = {{-1 , -1}, 0};
-
-static void sighandler_shutdown(int signal);
-static void sighandler_crash(int signal);
-
-static int crypt_main(int argc, char *argv[]);
-
-int main(int argc, char *argv[])
+int main( int argc, char *argv[] )
 {
 	int i = 0;
-	char *old_cwd = NULL;
 	struct sigaction sig, old;
-#ifdef HAVE_BACKTRACE
-	void *unused[1];
-#endif
+	
+	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 );
 
-	/* Required to make iconv to ASCII//TRANSLIT work. This makes BitlBee
-	   system-locale-sensitive. :-( */
-	setlocale(LC_CTYPE, "");
+	}
+	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 );
+}
 
-	if (argc > 1 && strcmp(argv[1], "-x") == 0) {
-		return crypt_main(argc, argv);
-	}
+gboolean bitlbee_dirty_workaround( gpointer data )
+{
+	usleep( 50000 );
+	return( TRUE );
+}
 
-	log_init();
-	/* Catch early errors */
-	log_link(LOGLVL_ERROR, LOGOUTPUT_CONSOLE);
-	log_link(LOGLVL_WARNING, LOGOUTPUT_CONSOLE);
+void proxyprofiler_dump();
 
-	global.conf_file = g_strdup(CONF_FILE_DEF);
-	global.conf = conf_load(argc, argv);
-	if (global.conf == NULL) {
-		return(1);
-	}
-
-	if (global.conf->runmode == RUNMODE_INETD) {
-		log_link(LOGLVL_ERROR, LOGOUTPUT_IRC);
-		log_link(LOGLVL_WARNING, LOGOUTPUT_IRC);
-	} else {
-		log_link(LOGLVL_ERROR, LOGOUTPUT_CONSOLE);
-		log_link(LOGLVL_WARNING, LOGOUTPUT_CONSOLE);
-	}
-
-	b_main_init();
-
-	/* libpurple doesn't like fork()s after initializing itself, so if
-	   we use it, do this init a little later (in case we're running in
-	   ForkDaemon mode). */
-#ifndef WITH_PURPLE
-	nogaim_init();
-#endif
-
-#ifdef OTR_BI
-	otr_init();
-#endif
-
-	global.helpfile = g_strdup(HELP_FILE);
-	if (help_init(&global.help, global.helpfile) == NULL) {
-		log_message(LOGLVL_WARNING, "Error opening helpfile %s.", HELP_FILE);
-	}
-
-	global.storage = storage_init(global.conf->primary_storage, global.conf->migrate_storage);
-	if (global.storage == NULL) {
-		log_message(LOGLVL_ERROR, "Unable to load storage backend '%s'", global.conf->primary_storage);
-		return(1);
-	}
-
-	global.auth = auth_init(global.conf->auth_backend);
-	if (global.conf->auth_backend && global.auth == NULL) {
-		log_message(LOGLVL_ERROR, "Unable to load authentication backend '%s'", global.conf->auth_backend);
-		return(1);
-	}
-
-	if (global.conf->runmode == RUNMODE_INETD) {
-		i = bitlbee_inetd_init();
-		log_message(LOGLVL_INFO, "%s %s starting in inetd mode.", PACKAGE, BITLBEE_VERSION);
-
-	} else if (global.conf->runmode == RUNMODE_DAEMON) {
-		i = bitlbee_daemon_init();
-		log_message(LOGLVL_INFO, "%s %s starting in daemon mode.", PACKAGE, BITLBEE_VERSION);
-	} else if (global.conf->runmode == RUNMODE_FORKDAEMON) {
-		/* In case the operator requests a restart, we need this. */
-		old_cwd = g_malloc(256);
-		if (getcwd(old_cwd, 255) == NULL) {
-			log_message(LOGLVL_WARNING, "Could not save current directory: %s", strerror(errno));
-			g_free(old_cwd);
-			old_cwd = NULL;
+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;
 		}
-
-		i = bitlbee_daemon_init();
-		log_message(LOGLVL_INFO, "%s %s starting in forking daemon mode.", PACKAGE, BITLBEE_VERSION);
-	}
-	if (i != 0) {
-		return(i);
-	}
-
-	if ((global.conf->user && *global.conf->user) &&
-	    (global.conf->runmode == RUNMODE_DAEMON ||
-	     global.conf->runmode == RUNMODE_FORKDAEMON) &&
-	    (!getuid() || !geteuid())) {
-		struct passwd *pw = NULL;
-		pw = getpwnam(global.conf->user);
-		if (!pw) {
-			log_message(LOGLVL_ERROR, "Failed to look up user %s.", global.conf->user);
-			return(EXIT_FAILURE);
-
-		} else if (initgroups(global.conf->user, pw->pw_gid) != 0) {
-			log_message(LOGLVL_ERROR, "initgroups: %s.", strerror(errno));
-			return(EXIT_FAILURE);
-
-		} else if (setgid(pw->pw_gid) != 0) {
-			log_message(LOGLVL_ERROR, "setgid(%d): %s.", pw->pw_gid, strerror(errno));
-			return(EXIT_FAILURE);
-
-		} else if (setuid(pw->pw_uid) != 0) {
-			log_message(LOGLVL_ERROR, "setuid(%d): %s.", pw->pw_uid, strerror(errno));
-			return(EXIT_FAILURE);
+		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 );
 		}
 	}
-
-	/* Catch some signals to tell the user what's happening before quitting */
-	memset(&sig, 0, sizeof(sig));
-	sig.sa_handler = SIG_IGN;
-	sigaction(SIGCHLD, &sig, &old);
-	sigaction(SIGPIPE, &sig, &old);
-	sig.sa_flags = SA_RESETHAND;
-	sig.sa_handler = sighandler_crash;
-	sigaction(SIGSEGV, &sig, &old);
-
-	sighandler_shutdown_setup();
-
-	sig.sa_handler = sighandler_shutdown;
-	sigaction(SIGINT, &sig, &old);
-	sigaction(SIGTERM, &sig, &old);
-
-#ifdef HAVE_BACKTRACE
-	/* As per the backtrace(3) man page, call this outside of the signal
-	 * handler once to ensure any dynamic libraries are loaded in an
-	 * async-signal-safe environment to prevent deadlocks */
-	backtrace(unused, 1);
+#ifdef PROXYPROFILER
+	else if( signal == SIGXCPU )
+	{
+		write_io_activity();
+		proxyprofiler_dump();
+		log_message( LOGLVL_ERROR, "Received SIGXCPU, dumping some debugging info." );
+		exit( 1 );
+	}
 #endif
-
-	if (!getuid() || !geteuid()) {
-		log_message(LOGLVL_WARNING, "BitlBee is running with root privileges. Why?");
+	else if( signal != SIGPIPE )
+	{
+		log_message( LOGLVL_ERROR, "Fatal signal received: %d. That's probably a bug.", signal );
+		raise( signal );
 	}
-
-	b_main_run();
-
-	/* Mainly good for restarting, to make sure we close the help.txt fd. */
-	help_free(&global.help);
-
-	if (global.restart) {
-		char *fn = ipc_master_save_state();
-		char *env;
-
-		env = g_strdup_printf("_BITLBEE_RESTART_STATE=%s", fn);
-		putenv(env);
-		g_free(fn);
-		/* Looks like env should *not* be freed here as putenv
-		   doesn't make a copy. Odd. */
-
-		i = chdir(old_cwd);
-		close(global.listen_socket);
-
-		if (execv(argv[0], argv) == -1) {
-			/* Apparently the execve() failed, so let's just
-			   jump back into our own/current main(). */
-			/* Need more cleanup code to make this work. */
-			return 1; /* main( argc, argv ); */
-		}
-	}
-
-	return(0);
-}
-
-static int crypt_main(int argc, char *argv[])
-{
-	int pass_len;
-	unsigned char *pass_cr, *pass_cl;
-
-	if (argc < 4 || (strcmp(argv[2], "hash") != 0 &&
-	                 strcmp(argv[2], "unhash") != 0 && argc < 5)) {
-		printf("Supported:\n"
-		       "  %s -x enc <key> <cleartext password>\n"
-		       "  %s -x dec <key> <encrypted password>\n"
-		       "  %s -x hash <cleartext password>\n"
-		       "  %s -x unhash <hashed password>\n"
-		       "  %s -x chkhash <hashed password> <cleartext password>\n",
-		       argv[0], argv[0], argv[0], argv[0], argv[0]);
-	} else if (strcmp(argv[2], "enc") == 0) {
-		char *encoded;
-
-		pass_len = arc_encode(argv[4], strlen(argv[4]), &pass_cr, argv[3], 12);
-
-		encoded = base64_encode(pass_cr, pass_len);
-		printf("%s\n", encoded);
-		g_free(encoded);
-		g_free(pass_cr);
-	} else if (strcmp(argv[2], "dec") == 0) {
-		pass_len = base64_decode(argv[4], &pass_cr);
-		arc_decode(pass_cr, pass_len, (char **) &pass_cl, argv[3]);
-		printf("%s\n", pass_cl);
-
-		g_free(pass_cr);
-		g_free(pass_cl);
-	} else if (strcmp(argv[2], "hash") == 0) {
-		guint8 pass_md5[21];
-		GChecksum *md5_state;
-		gsize digest_len = MD5_HASH_SIZE;
-		char *encoded;
-
-		random_bytes(pass_md5 + 16, 5);
-		md5_state = g_checksum_new(G_CHECKSUM_MD5);
-		g_checksum_update(md5_state, (guint8 *) argv[3], strlen(argv[3]));
-		g_checksum_update(md5_state, pass_md5 + 16, 5);   /* Add the salt. */
-		g_checksum_get_digest(md5_state, pass_md5, &digest_len);
-		g_checksum_free(md5_state);
-
-		encoded = base64_encode(pass_md5, 21);
-		printf("%s\n", encoded);
-		g_free(encoded);
-	} else if (strcmp(argv[2], "unhash") == 0) {
-		printf("Hash %s submitted to a massive Beowulf cluster of\n"
-		       "overclocked 486s. Expect your answer next year somewhere around this time. :-)\n", argv[3]);
-	} else if (strcmp(argv[2], "chkhash") == 0) {
-		char *hash = strncmp(argv[3], "md5:", 4) == 0 ? argv[3] + 4 : argv[3];
-		int st = md5_verify_password(argv[4], hash);
-
-		printf("Hash %s given password.\n", st == 0 ? "matches" : "does not match");
-
-		return st;
-	}
-
-	return 0;
-}
-
-/* Set up a pipe for SIGTERM/SIGINT so the actual signal handler doesn't do anything unsafe */
-void sighandler_shutdown_setup()
-{
-	if (shutdown_pipe.fd[0] != -1) {
-		/* called again from a forked process, clean up to avoid propagating the signal */
-		b_event_remove(shutdown_pipe.tag);
-		close(shutdown_pipe.fd[0]);
-		close(shutdown_pipe.fd[1]);
-	}
-
-	if (pipe(shutdown_pipe.fd) == 0) {
-		shutdown_pipe.tag = b_input_add(shutdown_pipe.fd[0], B_EV_IO_READ, bitlbee_shutdown, NULL);
-	}
-}
-
-/* Signal handler for SIGTERM and SIGINT */
-static void sighandler_shutdown(int signal)
-{
-	int unused G_GNUC_UNUSED;
-	/* Write a single null byte to the pipe, just to send a message to the main loop.
-	 * This gets handled by bitlbee_shutdown (the b_input_add callback for this pipe) */
-	unused = write(shutdown_pipe.fd[1], "", 1);
-}
-
-#ifdef HAVE_BACKTRACE
-/* Writes a backtrace to (usually) /var/lib/bitlbee/crash.log
- * No malloc allowed means not a lot can be written to that file */
-static void sighandler_crash_backtrace()
-{
-	int fd, mapsfd;
-	int size;
-	void *trace[128];
-	const char message[] = "## " PACKAGE " crashed\n"
-		"## Version: " BITLBEE_VERSION "\n"
-		"## Configure args: " BITLBEE_CONFIGURE_ARGS "\n"
-		"##\n"
-		"## Backtrace:\n\n";
-	const char message2[] = "\n"
-		"## Hint: To get details on addresses use\n"
-		"##   addr2line -e <binary> <address>\n"
-		"## or\n"
-		"##   gdb <binary> -ex 'l *<address>' -ex q\n"
-		"## where <binary> is a filename from above and <address> is the part between (...)\n"
-		"##\n\n";
-	const char message3[] = "\n## End of memory maps. See above for the backtrace\n\n";
-
-	fd = open(CRASHFILE, O_WRONLY | O_APPEND | O_CREAT, 0600);
-
-	if (fd == -1 || write(fd, message, sizeof(message) - 1) == -1) {
-		return;
-	}
-
-	size = backtrace(trace, 128);
-	backtrace_symbols_fd(trace, size, fd);
-
-	(void) write(fd, message2, sizeof(message2) - 1);
-
-	/* a bit too linux-specific, so fail gracefully */
-	mapsfd = open("/proc/self/maps", O_RDONLY, 0);
-
-	if (mapsfd != -1) {
-		char buf[4096] = {0};
-		ssize_t bytes;
-
-		while ((bytes = read(mapsfd, buf, sizeof(buf))) > 0) {
-			(void) write(fd, buf, bytes);
-		}
-		(void) close(mapsfd);
-		(void) write(fd, message3, sizeof(message3) - 1);
-	}
-
-	(void) close(fd);
-}
-#endif
-
-/* Signal handler for SIGSEGV
- * A desperate attempt to tell the user that everything is wrong in the world.
- * Avoids using irc_abort() because it has several unsafe calls to malloc */
-static void sighandler_crash(int signal)
-{
-	GSList *l;
-	const char message[] = "ERROR :BitlBee crashed! (SIGSEGV received)\r\n"
-#ifdef HAVE_BACKTRACE
-		"ERROR :Writing backtrace to " CRASHFILE "\r\n"
-#endif
-		"ERROR :This is a bug either in BitlBee or a plugin, ask us on IRC if unsure\r\n";
-
-	for (l = irc_connection_list; l; l = l->next) {
-		irc_t *irc = l->data;
-		sock_make_blocking(irc->fd);
-		if (irc->sendbuffer) {
-			(void) write(irc->fd, irc->sendbuffer->str, irc->sendbuffer->len);
-		}
-		(void) write(irc->fd, message, sizeof(message) - 1);
-	}
-
-#ifdef HAVE_BACKTRACE
-	sighandler_crash_backtrace();
-#endif
-
-	raise(signal);
 }
 
@@ -398,5 +161,5 @@
 	struct timeval time[1];
 
-	gettimeofday(time, 0);
-	return((double) time->tv_sec + (double) time->tv_usec / 1000000);
+	gettimeofday( time, 0 );
+	return( (double) time->tv_sec + (double) time->tv_usec / 1000000 );
 }
Index: url.c
===================================================================
--- url.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ url.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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 );
+}
Index: url.h
===================================================================
--- url.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ url.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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 );
Index: user.c
===================================================================
--- user.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ user.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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;
+	}
+}
Index: user.h
===================================================================
--- user.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ user.h	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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 );
Index: utils/README
===================================================================
--- utils/README	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ utils/README	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -18,17 +18,40 @@
 
 
-* convert_purple.py
+* create_nicksfile.pl (Christian Friedl <christian.friedl@chello.at>)
 
-Converts libpurple configs into something BitlBee can use, so you don't
-have to re-add all your accounts by hand.
+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.ruizendaal@gmail.com>.
+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://the-timing.nl/stuff/irssi-bitlbee
+http://fokdat.nl/~tijmen/software/irssi-bitlbee.html
 
 
Index: ils/bitlbee-ctl.pl
===================================================================
--- utils/bitlbee-ctl.pl	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,59 +1,0 @@
-#!/usr/bin/perl
-# Simple front-end to BitlBee's administration commands
-# Copyright (C) 2006 Jelmer Vernooij <jelmer@samba.org>
-
-use IO::Socket;
-use Getopt::Long;
-use strict;
-use warnings;
-
-my $opt_help;
-my $opt_socketfile = "/var/run/bitlbee";
-
-sub ShowHelp 
-{
-	print 
-"bitlbee-ctl.pl [options] command ...
-
-Available options:
-
-  --ipc-socket=SOCKET	Override path to IPC socket [$opt_socketfile]
-  --help				Show this help message
-
-Available commands:
-	
-	die
-
-";
-	exit (0);
-}
-
-GetOptions (
-	    'help|h|?' => \&ShowHelp,
-		'ipc-socket=s' => \$opt_socketfile
-	    ) or exit(1);
-
-my $client = IO::Socket::UNIX->new(Peer => $opt_socketfile,
-								Type => SOCK_STREAM,
-								Timeout => 10);
-								
-if (not $client) {
-	print "Error connecting to $opt_socketfile: $@\n";
-	exit(1);
-}
-
-my $cmd = shift @ARGV;
-
-if (not defined($cmd)) {
-	print "Usage: bitlbee-ctl.pl [options] command ...\n";
-	exit(1);
-}
-
-if ($cmd eq "die") {
-	$client->send("DIE\r\n");
-} else {
-	print "No such command: $cmd\n";
-	exit(1);
-}
-
-$client->close();
Index: utils/bitlbeed.c
===================================================================
--- utils/bitlbeed.c	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ utils/bitlbeed.c	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -6,5 +6,5 @@
 *  (without access to /etc/inetd.conf or whatever)               *
 *                                                                *
-*  Copyright 2002-2004 Wilmer van der Gaast <wilmer@gaast.net>   *
+*  Copyright 2002-2004 Wilmer van der Gaast <lintux@debian.org>  *
 *                                                                *
 *  Licensed under the GNU General Public License                 *
@@ -13,7 +13,7 @@
 \****************************************************************/
 
-/*
+/* 
    ChangeLog:
-
+   
    2004-06-27: Added support for AF_LOCAL (UNIX domain) sockets
                Renamed log to do_log to fix conflict warning
@@ -50,27 +50,29 @@
 #include <arpa/inet.h>
 
-typedef struct settings {
+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 {
+typedef struct ipstats
+{
 	unsigned int ip;
-
+	
 	time_t rate_start;
 	int rate_times;
 	time_t rate_ignore;
-
+	
 	struct ipstats *next;
 } ipstats_t;
@@ -79,93 +81,99 @@
 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[])
+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 = 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';
-
+		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);
+		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) {
+		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);
-		}
-
+		if( st < 0 )
+		{
+			perror( "fork" );
+			return( 1 );
+		}
+		else if( st > 0 )
+		{
+			return( 0 );
+		}
+		
 		setsid();
-		close(0);
-		close(1);
-		close(2);
-	}
-
-	do_log("bitlbeed running");
-
+		close( 0 );
+		close( 1 );
+		close( 2 );
+	}
+	
+	do_log( "bitlbeed running" );
+	
 	/* The Daemon */
-	while (1) {
+	while( 1 )
+	{
 		int cli_fd, cli_len, i, st;
 		struct sockaddr_in cli_addr;
@@ -174,189 +182,222 @@
 		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);
+		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( 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_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) {
+				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) {
+				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);
+						setrlimit( RLIMIT_CPU, &li );
 					}
-					execv(set->call[0], set->call);
-					do_log("Error while executing %s!", set->call[0]);
-					return(1);
+					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;
+				
+				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);
+				}
+				else
+				{
+					ip->rate_start = time( NULL );
 					ip->rate_times = 1;
 				}
-			} else {
-				do_log("Ignoring connection from %s", cli_txt);
-				close(cli_fd);
-			}
-		}
-
+			}
+			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 {
+		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);
+				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_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 = 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);
+	
+	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);
+		}
+		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);
+		}
+		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') {
+		}
+		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') {
+		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) {
+		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);
-	}
-
+	
+	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);
+	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, ...)
+void do_log( char *fmt, ... )
 {
 	va_list params;
@@ -364,50 +405,51 @@
 	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);
+	
+	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)
+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));
+	
+	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));
+	}
+	else
+	{
+		l = malloc( sizeof( ipstats_t ) );
 		ipstats = l;
 	}
-	memset(l, 0, sizeof(ipstats_t));
-
+	memset( l, 0, sizeof( ipstats_t ) );
+	
 	l->ip = ip;
-
-	return(l);
+	
+	return( l );
 }
Index: utils/centericq2bitlbee.sh
===================================================================
--- utils/centericq2bitlbee.sh	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ utils/centericq2bitlbee.sh	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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"
+
Index: utils/convert_gnomeicu.txt
===================================================================
--- utils/convert_gnomeicu.txt	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ utils/convert_gnomeicu.txt	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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.
Index: ils/convert_purple.py
===================================================================
--- utils/convert_purple.py	(revision 210d1d9cf4366d9933c02383300175e4b573be61)
+++ 	(revision )
@@ -1,116 +1,0 @@
-#!/usr/bin/python
-#
-# Part of BitlBee. Reads a libpurple accounts.xml file and generates some
-# commands/XML that BitlBee understands. For easy migration from Pidgin/
-# Finch/whatever to BitlBee, be it a public server or your own.
-#
-# Licensed under the GPL2 like the rest of BitlBee.
-#
-# Copyright 2010 Wilmer van der Gaast <wilmer@gaast.net>
-#
-
-import getopt
-import getpass
-import os
-import subprocess
-import sys
-
-import xml.dom.minidom
-
-BITLBEE = '/usr/sbin/bitlbee'
-
-def parse_purple(f):
-	protomap = {
-		'msn-pecan': 'msn',
-		'aim': 'oscar',
-		'icq': 'oscar',
-	}
-	supported = ('msn', 'jabber', 'oscar', 'yahoo', 'twitter')
-	accs = list()
-	
-	if os.path.isdir(f):
-		f = f + '/accounts.xml'
-	xt = xml.dom.minidom.parse(f)
-	for acc in xt.getElementsByTagName('account')[1:]:
-		protocol = acc.getElementsByTagName('protocol')[0].firstChild.wholeText
-		name = acc.getElementsByTagName('name')[0].firstChild.wholeText
-		try:
-			password = acc.getElementsByTagName('password')[0].firstChild.wholeText
-		except IndexError:
-			password = ''
-		if protocol.startswith('prpl-'):
-			protocol = protocol[5:]
-		if name.endswith('/'):
-			name = name[:-1]
-		if protocol in protomap:
-			protocol = protomap[protocol]
-		if protocol not in supported:
-			print 'Warning: protocol probably not supported by BitlBee: ' + protocol
-		accs.append((protocol, name, password))
-	
-	return accs
-
-def print_commands(accs):
-	print 'To copy all your Pidgin accounts to BitlBee, just copy-paste the following'
-	print 'commands into your &bitlbee channel:'
-	print
-	for acc in accs:
-		print 'account add %s %s "%s"' % acc
-
-def bitlbee_x(*args):
-	bb = subprocess.Popen([BITLBEE, '-x'] + list(args), stdout=subprocess.PIPE)
-	return bb.stdout.read().strip()
-
-def print_xml(accs):
-	try:
-		bitlbee_x('hash', 'blaataap')
-	except:
-		print "Can't find/use BitlBee binary. It has to be a 1.2.5 binary or higher."
-		print
-		usage()
-	
-	print 'BitlBee .xml files are encrypted using the identify password. Please type your'
-	print 'preferred identify password.'
-	user = getpass.getuser()
-	pwd = getpass.getpass()
-	
-	root = xml.dom.minidom.Element('user')
-	root.setAttribute('nick', user)
-	root.setAttribute('password', bitlbee_x('hash', pwd))
-	root.setAttribute('version', '1')
-	for acc in accs:
-		accx = xml.dom.minidom.Element('account')
-		accx.setAttribute('protocol', acc[0])
-		accx.setAttribute('handle', acc[1])
-		accx.setAttribute('password', bitlbee_x('enc', pwd, acc[2]))
-		accx.setAttribute('autoconnect', '1')
-		root.appendChild(accx)
-	
-	print
-	print 'Write the following XML data to a file called %s.xml (rename it if' % user.lower()
-	print 'you want to use a different nickname). It should be in the directory where'
-	print 'your BitlBee account files are stored (most likely /var/lib/bitlbee).'
-	print
-	print root.toprettyxml()
-
-def usage():
-	print 'Usage: %s [-f <purple accounts file>] [-b <bitlbee executable>] [-x]' % sys.argv[0]
-	print
-	print 'Generates "account add" commands by default. -x generates a .xml file instead.'
-	print 'The accounts file can normally be found in ~/.purple/.'
-	sys.exit(os.EX_USAGE)
-
-try:
-	flags = dict(getopt.getopt(sys.argv[1:], 'f:b:x')[0])
-except getopt.GetoptError:
-	usage()
-if '-f' not in flags:
-	usage()
-if '-b' in flags:
-	BITLBEE = flags['-b']
-
-parsed = parse_purple(flags['-f'])
-if '-x' in flags:
-	print_xml(parsed)
-else:
-	print_commands(parsed)
Index: utils/create_nicksfile.pl
===================================================================
--- utils/create_nicksfile.pl	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
+++ utils/create_nicksfile.pl	(revision b7d3cc34f68dab7b8f7d0777711317b334fc2219)
@@ -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
