aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorulim <a.sporto+bee@gmail.com>2008-04-14 15:10:53 +0200
committerulim <a.sporto+bee@gmail.com>2008-04-14 15:10:53 +0200
commitb79308b943149d729b1daea8c56cff9fc02711a0 (patch)
treea5f80445ed63d864703941474dc6cf8998df3136
parent6cac643f6933e431b90fcb937dec505f989e6a53 (diff)
parentaa311173a85020bcbbbf61135a5451e171d422f5 (diff)
merged in upstream r379 (somewhere after 1.2-3).
Just one trivial conflict in the jabber Makefile, went smoothly.
-rw-r--r--Makefile1
-rw-r--r--account.c12
-rw-r--r--bitlbee.c12
-rw-r--r--bitlbee.conf7
-rw-r--r--bitlbee.h6
-rw-r--r--conf.c2
-rw-r--r--debian/README.Debian20
-rwxr-xr-xdebian/bitlbee.init13
-rw-r--r--debian/changelog53
-rw-r--r--debian/conffiles1
-rw-r--r--debian/control2
-rw-r--r--debian/po/it.po36
-rwxr-xr-xdebian/postinst31
-rwxr-xr-xdebian/postrm5
-rwxr-xr-xdebian/rules9
-rw-r--r--debian/watch2
-rw-r--r--doc/CHANGES39
-rw-r--r--doc/README14
-rw-r--r--doc/user-guide/Support.xml9
-rw-r--r--doc/user-guide/commands.xml36
-rw-r--r--doc/user-guide/misc.xml28
-rw-r--r--doc/user-guide/quickstart.xml12
-rw-r--r--ipc.c60
-rw-r--r--ipc.h3
-rw-r--r--irc.c297
-rw-r--r--irc.h5
-rw-r--r--irc_commands.c35
-rw-r--r--lib/Makefile4
-rw-r--r--lib/arc.c27
-rw-r--r--lib/arc.h8
-rw-r--r--lib/base64.c2
-rw-r--r--lib/events_glib.c5
-rw-r--r--lib/misc.c41
-rw-r--r--lib/misc.h2
-rw-r--r--lib/proxy.c4
-rw-r--r--lib/ssl_client.h3
-rw-r--r--lib/ssl_gnutls.c6
-rw-r--r--lib/ssl_nss.c6
-rw-r--r--lib/ssl_openssl.c23
-rw-r--r--lib/url.c24
-rw-r--r--lib/url.h18
-rw-r--r--lib/xmltree.c (renamed from protocols/jabber/xmltree.c)3
-rw-r--r--lib/xmltree.h (renamed from protocols/jabber/xmltree.h)4
-rw-r--r--protocols/Makefile2
-rw-r--r--protocols/jabber/Makefile4
-rw-r--r--protocols/jabber/io.c12
-rw-r--r--protocols/jabber/iq.c27
-rw-r--r--protocols/jabber/jabber.c1
-rw-r--r--protocols/jabber/jabber_util.c8
-rw-r--r--protocols/jabber/sasl.c27
-rw-r--r--protocols/msn/Makefile2
-rw-r--r--protocols/msn/msn.h2
-rw-r--r--protocols/msn/msn_util.c8
-rw-r--r--protocols/msn/ns.c30
-rw-r--r--protocols/msn/passport.c280
-rw-r--r--protocols/msn/passport.h93
-rw-r--r--protocols/nogaim.c17
-rw-r--r--protocols/nogaim.h3
-rw-r--r--protocols/oscar/Makefile2
-rw-r--r--protocols/oscar/oscar.c18
-rw-r--r--protocols/yahoo/Makefile2
-rw-r--r--protocols/yahoo/libyahoo2.c4
-rw-r--r--protocols/yahoo/yahoo.c10
-rw-r--r--query.c17
-rw-r--r--query.h8
-rw-r--r--root_commands.c70
-rw-r--r--set.c15
-rw-r--r--set.h1
-rw-r--r--storage_xml.c43
-rw-r--r--tests/Makefile4
-rw-r--r--tests/check.c4
-rw-r--r--tests/check_arc.c40
-rw-r--r--tests/check_help.c7
-rw-r--r--tests/check_irc.c4
-rw-r--r--tests/check_jabber_sasl.c117
75 files changed, 1228 insertions, 584 deletions
diff --git a/Makefile b/Makefile
index 5d7ce9e0..a1a5e645 100644
--- a/Makefile
+++ b/Makefile
@@ -49,7 +49,6 @@ distclean: clean $(subdirs)
check: all
$(MAKE) -C tests
-lcov:
gcov: check
gcov *.c
diff --git a/account.c b/account.c
index 4eb78faa..2c6e1069 100644
--- a/account.c
+++ b/account.c
@@ -181,19 +181,17 @@ void account_del( irc_t *irc, 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 = irc->accounts; a; a = (l=a)->next )
if( a == acc )
{
- if( a->ic ) return; /* Caller should have checked, accounts still in use can't be deleted. */
-
if( l )
- {
l->next = a->next;
- }
else
- {
irc->accounts = a->next;
- }
while( a->set )
set_del( &a->set, a->set->key );
@@ -202,7 +200,7 @@ void account_del( irc_t *irc, account_t *acc )
g_free( a->user );
g_free( a->pass );
- if( a->server ) g_free( a->server );
+ g_free( a->server );
if( a->reconnect ) /* This prevents any reconnect still queued to happen */
cancel_auto_reconnect( a );
g_free( a );
diff --git a/bitlbee.c b/bitlbee.c
index 59a417f0..230b8ce8 100644
--- a/bitlbee.c
+++ b/bitlbee.c
@@ -225,12 +225,16 @@ gboolean bitlbee_io_current_client_write( gpointer data, gint fd, b_input_condit
if( st == size )
{
- g_free( irc->sendbuffer );
- irc->sendbuffer = NULL;
- irc->w_watch_source_id = 0;
-
if( irc->status & USTATUS_SHUTDOWN )
+ {
irc_free( irc );
+ }
+ else
+ {
+ g_free( irc->sendbuffer );
+ irc->sendbuffer = NULL;
+ irc->w_watch_source_id = 0;
+ }
return FALSE;
}
diff --git a/bitlbee.conf b/bitlbee.conf
index d9f878c8..99e8106d 100644
--- a/bitlbee.conf
+++ b/bitlbee.conf
@@ -48,14 +48,21 @@
## AuthPassword
##
## Password the user should enter when logging into a closed BitlBee server.
+## You can also have an MD5-encrypted password here. Format: "md5:", followed
+## by a hash as generated for the <user password=""> attribute in a BitlBee
+## XML file (for now there's no easier way to generate the hash).
##
# 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
## HostName
##
diff --git a/bitlbee.h b/bitlbee.h
index c118d7fc..60694a2a 100644
--- a/bitlbee.h
+++ b/bitlbee.h
@@ -29,10 +29,10 @@
#define _GNU_SOURCE /* Stupid GNU :-P */
#define PACKAGE "BitlBee"
-#define BITLBEE_VERSION "1.1.1dev"
+#define BITLBEE_VERSION "1.2"
#define VERSION BITLBEE_VERSION
-#define MAX_STRING 128
+#define MAX_STRING 511
#if HAVE_CONFIG_H
#include "config.h"
@@ -159,6 +159,8 @@ void root_command_string( irc_t *irc, user_t *u, char *command, int flags );
void root_command( irc_t *irc, char *command[] );
gboolean bitlbee_shutdown( gpointer data, gint fd, b_input_condition cond );
+char *set_eval_root_nick( set_t *set, char *new_nick );
+
extern global_t global;
#endif
diff --git a/conf.c b/conf.c
index 7aac7d9a..6955b23f 100644
--- a/conf.c
+++ b/conf.c
@@ -252,7 +252,7 @@ static int conf_loadini( conf_t *conf, char *file )
else if( g_strcasecmp( ini->key, "account_storage_migrate" ) == 0 )
{
g_strfreev( conf->migrate_storage );
- conf->migrate_storage = g_strsplit( ini->value, " \t,;", -1 );
+ conf->migrate_storage = g_strsplit_set( ini->value, " \t,;", -1 );
}
else if( g_strcasecmp( ini->key, "pinginterval" ) == 0 )
{
diff --git a/debian/README.Debian b/debian/README.Debian
index e2102fc8..b5a514c0 100644
--- a/debian/README.Debian
+++ b/debian/README.Debian
@@ -1,12 +1,26 @@
- *** NEWS (Version 1.1 and later) ***
+ *** NEWS (Version 1.2 and later) ***
-Starting from version 1.1, BitlBee has a forking daemon mode. The Debian
+Starting from version 1.2, BitlBee has a forking daemon mode. The Debian
package now uses this mode by default, instead of inetd mode. If you don't
want to use this, you can disable the init scripts (best way to do this is
by editing /etc/default/bitlbee) and restore the inetd.conf entry. This
should be necessary only once, it won't be touched during upgrades.
---------------------------------------------------------------------------
+Another important change in BitlBee 1.2 is the file format used for your
+personal settings. Everything's now saved in a single .xml (per account,
+of course) file instead of $nick.accounts and $nick.nicks. One advantage
+of this new format is that the passwords are actually encrypted instead of
+just vaguely obfuscated. BitlBee can still read the old files, and will
+save things in the new format when you save/disconnect. After that, you
+can safely remove the old-style files (this is recommended).
+
+I tried making this transition (the new file format but especially, in this
+case, the inetd->forkdaemon mode change) as smooth as possible, but I'm
+aware that many BitlBee users will have their own hacks already to run the
+program. I hope the package won't break any of this for anyone. 1.2-2
+should fix at least some of the issues.
+
+---------------------------------------------------------------------------
Debconf should have asked you on what port you want BitlBee to run. If it
did not, the port number should be 6667 or 6668. (6668 if you already got
diff --git a/debian/bitlbee.init b/debian/bitlbee.init
index baf1a0c6..904ae5ea 100755
--- a/debian/bitlbee.init
+++ b/debian/bitlbee.init
@@ -1,4 +1,11 @@
#! /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: 1
+### END INIT INFO
#
# Init script for BitlBee Debian package. Based on skeleton init script:
#
@@ -17,7 +24,7 @@ SCRIPTNAME=/etc/init.d/$NAME
# Default value
BITLBEE_PORT=6667
-DAEMON_OPT=-F
+BITLBEE_OPTS=-F
# Read config file if it is present.
if [ -r /etc/default/$NAME ]; then
@@ -36,8 +43,8 @@ d_start() {
chown bitlbee /var/run/bitlbee.pid
start-stop-daemon --start --quiet --pidfile $PIDFILE \
- -c bitlbee -g nogroup \
- --exec $DAEMON -- -p $BITLBEE_PORT -P $PIDFILE $DAEMON_OPT
+ -c bitlbee: \
+ --exec $DAEMON -- -p $BITLBEE_PORT -P $PIDFILE $BITLBEE_OPTS
}
#
diff --git a/debian/changelog b/debian/changelog
index ae524f73..b964ee0e 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,12 +1,61 @@
-bitlbee (1.1.1dev-0pre) unstable; urgency=low
+bitlbee (1.2-4) unstable; urgency=low
+ * Not a real release, just a placeholder for the changelog.
+ * Fixed init script to use the BITLBEE_OPTS variable, not an undefined
+ DAEMON_OPT.
+ * Added dependency information to the init script. (Closes: #472567)
+
+ -- Wilmer van der Gaast <wilmer@gaast.net> Sat, 29 Mar 2008 21:10:33 +0000
+
+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> Fri, 06 Jul 2007 09:09:36 +0100
+ -- Wilmer van der Gaast <wilmer@gaast.net> Wed, 29 Aug 2007 20:24:28 +0100
bitlbee (1.0.3-1.3) unstable; urgency=low
diff --git a/debian/conffiles b/debian/conffiles
index 2ccc958d..dcb4078e 100644
--- a/debian/conffiles
+++ b/debian/conffiles
@@ -1,2 +1,3 @@
/etc/bitlbee/motd.txt
/etc/bitlbee/bitlbee.conf
+/etc/init.d/bitlbee
diff --git a/debian/control b/debian/control
index 139174a6..8383391b 100644
--- a/debian/control
+++ b/debian/control
@@ -3,7 +3,7 @@ Section: net
Priority: optional
Maintainer: Wilmer van der Gaast <wilmer@gaast.net>
Standards-Version: 3.5.9
-Build-Depends: libglib2.0-dev | libglib-dev, libgnutls-dev | libnss-dev (>= 1.6), debconf-2.0, po-debconf
+Build-Depends: libglib2.0-dev (>= 2.4), libevent-dev, libgnutls-dev | libnss-dev (>= 1.6), debconf-2.0, po-debconf
Package: bitlbee
Architecture: any
diff --git a/debian/po/it.po b/debian/po/it.po
new file mode 100644
index 00000000..e149e61a
--- /dev/null
+++ b/debian/po/it.po
@@ -0,0 +1,36 @@
+# Italian translation of the bitlbee debconf template
+# This file is distributed under the same license as the bitlbee package
+# Copyright (C) 2007 Free Software Foundation, Inc.
+# Luca Monducci <luca.mo@tiscali.it>, 2007.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: bitlbee\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2007-08-30 04:31+0200\n"
+"PO-Revision-Date: 2007-10-27 11:52+0200\n"
+"Last-Translator: Luca Monducci <luca.mo@tiscali.it>\n"
+"Language-Team: Italian <debian-l10n-italian@lists.debian.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#. Type: string
+#. Description
+#: ../bitlbee.templates.master:1001
+msgid "On what TCP port should BitlBee listen for connections?"
+msgstr "Su quale porta TCP si deve mettere in ascolto BitlBee?"
+
+#. Type: string
+#. Description
+#: ../bitlbee.templates.master:1001
+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 ""
+"Normalmente BitlBee si mette in ascolto sulla consueta porta IRC, la 6667. "
+"Questa potrebbe non essere una buona idea se รจ in esecuzione anche un reale "
+"demone IRC. La porta 6668 potrebbe essere una valida alternativa. Lasciando "
+"vuoto questo valore BitlBee non viene avviato automaticamente."
diff --git a/debian/postinst b/debian/postinst
index 37608e47..80249bfe 100755
--- a/debian/postinst
+++ b/debian/postinst
@@ -13,11 +13,20 @@ update-rc.d bitlbee defaults > /dev/null 2>&1
BITLBEE_OPTS=-F
BITLBEE_DISABLED=0
BITLBEE_UPGRADE_DONT_RESTART=0
-[ -r /etc/default/bitlbee ] && source /etc/default/bitlbee
+[ -r /etc/default/bitlbee ] && . /etc/default/bitlbee
-if [ "$BITLBEE_DISABLED" = "0" ]; then
- ## In case it's still there (if we're upgrading right now)
+if [ "$BITLBEE_DISABLED" = "0" ] && type update-inetd > /dev/null 2> /dev/null &&
+ ( expr "$2" : '0\..*' > /dev/null || expr "$2" : '1\.0\..*' > /dev/null ); then
+ ## Make sure the inetd entry is gone (can still be there from a
+ ## previous version.
update-inetd --remove '.*/usr/sbin/bitlbee'
+ if grep -q /usr/sbin/bitlbee /etc/inetd.conf 2> /dev/null; then
+ # Thanks for breaking update-inetd! (bugs.debian.org/311111)
+ # I hope that it works at least with xinetd, because this
+ # emergency hack doesn't:
+ perl -pi -e 's:^[^#].*/usr/sbin/bitlbee$:## Now using daemon mode\: # $&:' /etc/inetd.conf
+ killall -HUP inetd || true
+ fi
fi
cat<<EOF>/etc/default/bitlbee
@@ -64,13 +73,21 @@ if [ -d $CONFDIR ] && chown -R bitlbee $CONFDIR; then
exit 0
fi
-adduser --system --home /var/lib/bitlbee/ --disabled-login --disabled-password bitlbee
+adduser --system --group --disabled-login --disabled-password --home /var/lib/bitlbee/ bitlbee
chmod 700 /var/lib/bitlbee/
## 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.
-chmod 600 /etc/bitlbee/bitlbee.conf
-chown bitlbee /etc/bitlbee/bitlbee.conf
+## 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
if [ -z "$2" ]; then
/etc/init.d/bitlbee start
diff --git a/debian/postrm b/debian/postrm
index cef99f13..5c3b4b2e 100755
--- a/debian/postrm
+++ b/debian/postrm
@@ -8,4 +8,7 @@ if [ -e /usr/share/debconf/confmodule ]; then
fi
update-rc.d bitlbee remove > /dev/null 2>&1 || true
-deluser --remove-home bitlbee || true
+rm -f /etc/default/bitlbee
+
+deluser --system --remove-home bitlbee || true
+rm -rf /var/lib/bitlbee ## deluser doesn't seem to do this for homedirs in /var
diff --git a/debian/rules b/debian/rules
index 8d6bd4fa..252fb742 100755
--- a/debian/rules
+++ b/debian/rules
@@ -2,15 +2,18 @@
DEBUG ?= 0
+ifdef BITLBEE_VERSION
+BITLBEE_FORCE_VERSION=1
+else
# Want to use the full package version number instead of just the release.
BITLBEE_VERSION ?= "$(shell dpkg-parsechangelog | grep ^Version: | awk '{print $$2}')"
export BITLBEE_VERSION
-
+endif
build-arch: build-arch-stamp
build-arch-stamp:
if [ ! -d debian ]; then exit 1; fi
- ./configure --debug=$(DEBUG) --prefix=/usr --etcdir=/etc/bitlbee $(DEB_BUILD_OPTIONS)
+ ./configure --debug=$(DEBUG) --prefix=/usr --etcdir=/etc/bitlbee --events=libevent
$(MAKE)
# $(MAKE) -C doc/ all
touch build-arch-stamp
@@ -65,7 +68,7 @@ binary-arch: build-arch install-arch
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
+ifdef BITLBEE_FORCE_VERSION
dpkg-gencontrol -ldebian/changelog -isp -pbitlbee -Tdebian/bitlbee.substvars -Pdebian/bitlbee -v1:$(BITLBEE_VERSION)-0 -V'debconf-depends=debconf (>= 1.2.0) | debconf-2.0'
else
dpkg-gencontrol -ldebian/changelog -isp -pbitlbee -Tdebian/bitlbee.substvars -Pdebian/bitlbee -V'debconf-depends=debconf (>= 1.2.0) | debconf-2.0'
diff --git a/debian/watch b/debian/watch
new file mode 100644
index 00000000..66ab4504
--- /dev/null
+++ b/debian/watch
@@ -0,0 +1,2 @@
+version=2
+http://get.bitlbee.org/src/bitlbee-(.*).tar.gz
diff --git a/doc/CHANGES b/doc/CHANGES
index ee4cde69..93ad35e2 100644
--- a/doc/CHANGES
+++ b/doc/CHANGES
@@ -1,7 +1,18 @@
+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.
+
+Finished ...
+
Version 1.2:
-- First BitlBee development/testing RELEASE. This should be quite stable
- though (and for most people more stable than 1.0.x). It just has a couple
- of rough edges and needs a bit more testing.
- 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
@@ -20,9 +31,18 @@ Version 1.2:
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, fixed lots of little bugs (including bugs that
- affected daemon mode stability). See the bzr logs for more information.
-- Added units tests, will have to add some more before the real release.
+ 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
@@ -68,8 +88,13 @@ Version 1.2:
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 ???
+Finished 17 Mar 2008
Version 1.0.4:
- Removed sethostent(), which causes problems for many people, especially on
diff --git a/doc/README b/doc/README
index bb6596ba..ca392573 100644
--- a/doc/README
+++ b/doc/README
@@ -41,6 +41,20 @@ 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.
+--- (Fork)Daemon mode
+
+If you don't want to run any inetd daemon, you can run BitlBee in Daemon
+mode. Right now, daemon mode may be a bad idea on servers with multiple
+users, since possible fatal BitlBee bugs will crash the BitlBee process and
+disconnect all connected users at once. Instead, you can use ForkDaemon
+mode, which serves every user from a separate process, without depending on
+an inetd daemon.
+
+To use BitlBee in daemon mode, just start it with the right flags or enable
+it in bitlbee.conf. 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)
+
DEPENDENCIES
============
diff --git a/doc/user-guide/Support.xml b/doc/user-guide/Support.xml
index 401a4295..c9f50a5f 100644
--- a/doc/user-guide/Support.xml
+++ b/doc/user-guide/Support.xml
@@ -3,12 +3,13 @@
<title>Support</title>
<sect1>
-<title>BitlBee is beta software</title>
+<title>Disclaimer</title>
<para>
-Although BitlBee has quite some functionality it is still beta. That means it
-can crash at any time, corrupt your data or whatever. Don't use it in
-any production environment and don't rely on it.
+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. :-)
</para>
</sect1>
diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml
index 0832e3d2..7081663f 100644
--- a/doc/user-guide/commands.xml
+++ b/doc/user-guide/commands.xml
@@ -162,11 +162,7 @@
</para>
<para>
- If you want, you can also tell BitlBee what nick to give the new contact. Of course you can also use the <emphasis>rename</emphasis> command for that, but sometimes this might be more convenient.
- </para>
-
- <para>
- Adding -tmp 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.
+ 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>
</description>
@@ -382,16 +378,16 @@
</bitlbee-setting>
<bitlbee-setting name="charset" type="string" scope="global">
- <default>iso8859-1</default>
+ <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>
- 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.
+ 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>
- 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
+ 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>
@@ -592,6 +588,16 @@
</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>
@@ -676,7 +682,7 @@
<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. ;-)
+ 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>
@@ -829,21 +835,17 @@
<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>
+ <syntax>nick &lt;connection&gt;</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>
-
- <para>
- It is recommended to use the per-account <emphasis>display_name</emphasis> setting to read and change this information. The <emphasis>nick</emphasis> command is deprecated.
+ Deprecated: Use the per-account <emphasis>display_name</emphasis> setting to read and change this information.
</para>
</description>
<ircexample>
- <ircline nick="wouter">nick 1 "Wouter Paesen"</ircline>
- <ircline nick="root">Setting your name on connection 1 to `Wouter Paesen'</ircline>
+ <ircline nick="wouter">account set 1/display_name "The majestik mรธรธse"</ircline>
+ <ircline nick="root">display_name = `The majestik mรธรธse'</ircline>
</ircexample>
</bitlbee-command>
diff --git a/doc/user-guide/misc.xml b/doc/user-guide/misc.xml
index d387d4b3..b55a8915 100644
--- a/doc/user-guide/misc.xml
+++ b/doc/user-guide/misc.xml
@@ -46,16 +46,12 @@ All MSN smileys (except one) are case insensitive and work without the nose too.
<varlistentry><term>(O)</term><listitem><para>Clock</para></listitem></varlistentry>
</variablelist>
-<para>
-This list was extracted from <ulink url="http://help.msn.com/!data/en_us/data/messengerv50.its51/$content$/EMOTICONS.HTM?H_APP=">http://help.msn.com/!data/en_us/data/messengerv50.its51/$content$/EMOTICONS.HTM?H_APP=</ulink>.
-</para>
-
</sect1>
<sect1 id="groupchats">
<title>Groupchats</title>
<para>
-Since version 0.8x, BitlBee supports groupchats on the MSN and Yahoo! networks. This text will try to explain you how they work.
+BitlBee now supports groupchats on all IM networks. This text will try to explain you how they work.
</para>
<para>
@@ -72,7 +68,7 @@ Of course you can also create your own groupchats. Type <emphasis>help groupchat
<title>Creating groupchats</title>
<para>
-If you want to start a groupchat with the person <emphasis>jim_msn</emphasis> in it, just join the channel <emphasis>#jim_msn</emphasis>. BitlBee will refuse to join you to the channel with that name, but it will create a new virtual channel with root, you and jim_msn in it.
+If you want to start a groupchat with the person <emphasis>lisa_msn</emphasis> in it, just join the channel <emphasis>#lisa_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 lisa_msn in it.
</para>
<para>
@@ -83,23 +79,6 @@ Of course a channel with only two people isn't really exciting yet. So the next
Some protocols (like Jabber) also support named groupchats. BitlBee now supports these too. You can use the <emphasis>join_chat</emphasis> command to join them. See <emphasis>help join_chat</emphasis> for more information.
</para>
-<para>
-This is all you'll probably need to know. If you have any problems, please read <emphasis>help groupchats3</emphasis>.
-</para>
-
-</sect1>
-
-<sect1 id="groupchats3">
-<title>Groupchat channel names</title>
-
-<para>
-Obviously the (numbered) channel names don't make a lot of sense. Problem is that groupchats usually don't have names at all in the IM-world, while IRC insists on a name. So BitlBee just generates something random, just don't pay attention to it. :-)
-</para>
-
-<para>
-Please also note that BitlBee doesn't support groupchats for all protocols yet. BitlBee will tell you so. Support for other protocols will hopefully come later.
-</para>
-
</sect1>
<sect1 id="away">
@@ -120,6 +99,7 @@ Not all away states are supported by all protocols, and some protocols have diff
<member>Be right back, BRB</member>
<member>On the phone, Phone, On phone</member>
<member>Out to lunch, Lunch, Food</member>
+ <member>Invisible, Hidden</member>
</simplelist>
<para>
@@ -127,7 +107,7 @@ So <emphasis>/away Food</emphasis> will set your state to "Out to lunch" on your
</para>
<para>
-You can also add more information to your away message. Setting it to "Busy - Fixing BitlBee bugs" will set your IM-away-states to Busy, but your away message will be more descriptive for people on IRC. Protocols like Yahoo! and Jabber will also show this complete away message to your buddies.
+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>
</sect1>
diff --git a/doc/user-guide/quickstart.xml b/doc/user-guide/quickstart.xml
index 7735a8d7..0539a7c7 100644
--- a/doc/user-guide/quickstart.xml
+++ b/doc/user-guide/quickstart.xml
@@ -60,11 +60,11 @@ When you are finished adding your account(s) use the <emphasis>account on</empha
</para>
<para>
-For most protocols (currently MSN, Jabber, Yahoo and AOL) BitlBee can download the contact list automatically from the IM server and all the on-line users should appear in the control channel when you log in.
+Now BitlBee logs in and downloads the contact list from the IM server. In a few seconds, all your on-line buddies should show up in the control channel.
</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.
+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>
@@ -126,11 +126,15 @@ First of all, a person must be on your contact list for you to chat with them (u
<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>
-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 &amp;bitlbee channel, use the <emphasis>set private</emphasis> command: <emphasis>set private true</emphasis> (<emphasis>set private false</emphasis> to change back).
+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.
+</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>
diff --git a/ipc.c b/ipc.c
index 384a9c33..54f026ac 100644
--- a/ipc.c
+++ b/ipc.c
@@ -257,19 +257,7 @@ gboolean ipc_master_read( gpointer data, gint source, b_input_condition cond )
}
else
{
- GSList *l;
- struct bitlbee_child *c;
-
- for( l = child_list; l; l = l->next )
- {
- c = l->data;
- if( c->ipc_fd == source )
- {
- ipc_master_free_one( c );
- child_list = g_slist_remove( child_list, c );
- break;
- }
- }
+ ipc_master_free_fd( source );
}
return TRUE;
@@ -287,10 +275,7 @@ gboolean ipc_child_read( gpointer data, gint source, b_input_condition cond )
}
else
{
- b_event_remove( global.listen_watch_source_id );
- close( global.listen_socket );
-
- global.listen_socket = -1;
+ ipc_child_disable();
}
return TRUE;
@@ -325,7 +310,9 @@ void ipc_to_master_str( char *format, ... )
}
else if( global.conf->runmode == RUNMODE_FORKDAEMON )
{
- write( global.listen_socket, msg_buf, strlen( msg_buf ) );
+ 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 )
{
@@ -375,12 +362,18 @@ void ipc_to_children_str( char *format, ... )
else if( global.conf->runmode == RUNMODE_FORKDAEMON )
{
int msg_len = strlen( msg_buf );
- GSList *l;
+ GSList *l, *next;
- for( l = child_list; l; l = l->next )
+ for( l = child_list; l; l = next )
{
struct bitlbee_child *c = l->data;
- write( c->ipc_fd, msg_buf, msg_len );
+
+ next = l->next;
+ if( write( c->ipc_fd, msg_buf, msg_len ) <= 0 )
+ {
+ ipc_master_free_one( c );
+ child_list = g_slist_remove( child_list, c );
+ }
}
}
else if( global.conf->runmode == RUNMODE_DAEMON )
@@ -409,6 +402,23 @@ void ipc_master_free_one( struct bitlbee_child *c )
g_free( c );
}
+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 );
+ child_list = g_slist_remove( child_list, c );
+ break;
+ }
+ }
+}
+
void ipc_master_free_all()
{
GSList *l;
@@ -420,6 +430,14 @@ void ipc_master_free_all()
child_list = NULL;
}
+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" );
diff --git a/ipc.h b/ipc.h
index 68926dfd..f3d24614 100644
--- a/ipc.h
+++ b/ipc.h
@@ -43,8 +43,11 @@ 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();
+
void ipc_to_master( char **cmd );
void ipc_to_master_str( char *format, ... ) G_GNUC_PRINTF( 1, 2 );
void ipc_to_children( char **cmd );
diff --git a/irc.c b/irc.c
index b0ca18d6..e8b08e71 100644
--- a/irc.c
+++ b/irc.c
@@ -45,6 +45,35 @@ static char *passchange( set_t *set, char *value )
return NULL;
}
+static char *set_eval_charset( set_t *set, char *value )
+{
+ irc_t *irc = set->data;
+ GIConv ic, oc;
+
+ if( g_strcasecmp( value, "none" ) == 0 )
+ value = g_strdup( "utf-8" );
+
+ if( ( ic = g_iconv_open( "utf-8", value ) ) == (GIConv) -1 )
+ {
+ return NULL;
+ }
+ if( ( oc = g_iconv_open( value, "utf-8" ) ) == (GIConv) -1 )
+ {
+ g_iconv_close( ic );
+ 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;
+}
+
irc_t *irc_new( int fd )
{
irc_t *irc;
@@ -68,6 +97,9 @@ irc_t *irc_new( int fd )
irc->mynick = g_strdup( ROOT_NICK );
irc->channel = g_strdup( ROOT_CHAN );
+ irc->iconv = (GIConv) -1;
+ irc->oconv = (GIConv) -1;
+
if( global.conf->hostname )
{
irc->myhost = g_strdup( global.conf->hostname );
@@ -122,6 +154,7 @@ irc_t *irc_new( int fd )
set_add( &irc->set, "password", NULL, passchange, irc );
set_add( &irc->set, "private", "true", set_eval_bool, irc );
set_add( &irc->set, "query_order", "lifo", NULL, irc );
+ set_add( &irc->set, "root_nick", irc->mynick, set_eval_root_nick, irc );
set_add( &irc->set, "save_on_quit", "true", set_eval_bool, irc );
set_add( &irc->set, "simulate_netsplit", "true", set_eval_bool, irc );
set_add( &irc->set, "strip_html", "true", NULL, irc );
@@ -130,6 +163,9 @@ irc_t *irc_new( int fd )
conf_loaddefaults( irc );
+ /* Evaluator sets the iconv/oconv structures. */
+ set_eval_charset( set_find( &irc->set, "charset" ), set_getstr( &irc->set, "charset" ) );
+
return( irc );
}
@@ -167,12 +203,14 @@ void irc_abort( irc_t *irc, int immed, char *format, ... )
irc->status |= USTATUS_SHUTDOWN;
if( irc->sendbuffer && !immed )
{
- /* We won't read from this socket anymore. Instead, we'll connect a timer
- to it that should shut down the connection in a second, just in case
- bitlbee_.._write doesn't do it first. */
+ /* Set up a timeout event that should shut down the connection
+ in a second, just in case ..._write doesn't do it first. */
b_event_remove( irc->r_watch_source_id );
- irc->r_watch_source_id = b_timeout_add( 1000, (b_event_handler) irc_free, irc );
+ irc->r_watch_source_id = 0;
+
+ b_event_remove( irc->ping_source_id );
+ irc->ping_source_id = b_timeout_add( 1000, (b_event_handler) irc_free, irc );
}
else
{
@@ -188,9 +226,8 @@ static gboolean irc_free_hashkey( gpointer key, gpointer value, gpointer data )
}
/* Because we have no garbage collection, this is quite annoying */
-void irc_free(irc_t * irc)
+void irc_free( irc_t * irc )
{
- account_t *account;
user_t *user, *usertmp;
log_message( LOGLVL_INFO, "Destroying connection with fd %d", irc->fd );
@@ -199,76 +236,86 @@ void irc_free(irc_t * irc)
if( storage_save( irc, TRUE ) != STORAGE_OK )
irc_usermsg( irc, "Error while saving settings!" );
- closesocket( irc->fd );
-
- if( irc->ping_source_id > 0 )
- b_event_remove( irc->ping_source_id );
- b_event_remove( irc->r_watch_source_id );
- if( irc->w_watch_source_id > 0 )
- b_event_remove( irc->w_watch_source_id );
-
irc_connection_list = g_slist_remove( irc_connection_list, irc );
- for (account = irc->accounts; account; account = account->next) {
- if (account->ic) {
- imc_logout(account->ic, TRUE);
- } else if (account->reconnect) {
- cancel_auto_reconnect(account);
- }
- }
-
- 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);
-
- while (irc->accounts)
- if (irc->accounts->ic == NULL)
- account_del(irc, irc->accounts);
+ while( irc->accounts )
+ {
+ if( irc->accounts->ic )
+ imc_logout( irc->accounts->ic, FALSE );
+ else if( irc->accounts->reconnect )
+ cancel_auto_reconnect( irc->accounts );
+
+ if( irc->accounts->ic == NULL )
+ account_del( irc, irc->accounts );
else
/* Nasty hack, but account_del() doesn't work in this
case and we don't want infinite loops, do we? ;-) */
irc->accounts = irc->accounts->next;
+ }
+
+ while( irc->queries != NULL )
+ query_del( irc, irc->queries );
- while (irc->set)
- set_del(&irc->set, irc->set->key);
+ while( irc->set )
+ set_del( &irc->set, irc->set->key );
- if (irc->users != NULL) {
+ 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);
- b_event_remove(user->sendbuf_timer);
+ 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 );
+ b_event_remove( user->sendbuf_timer );
usertmp = user;
user = user->next;
- g_free(usertmp);
+ g_free( usertmp );
}
}
- g_hash_table_foreach_remove(irc->userhash, irc_free_hashkey, NULL);
- g_hash_table_destroy(irc->userhash);
+ 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_foreach_remove( irc->userhash, irc_free_hashkey, NULL );
+ g_hash_table_destroy( irc->userhash );
+
+ g_hash_table_foreach_remove( irc->watches, irc_free_hashkey, 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_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_hash_table_foreach_remove(irc->watches, irc_free_hashkey, NULL);
- g_hash_table_destroy(irc->watches);
+ g_free( irc->myhost );
+ g_free( irc->mynick );
- g_free(irc);
+ g_free( irc->channel );
+
+ g_free( irc->last_target );
+
+ g_free( irc );
if( global.conf->runmode == RUNMODE_INETD || global.conf->runmode == RUNMODE_FORKDAEMON )
b_main_quit();
@@ -289,7 +336,7 @@ void irc_setpass (irc_t *irc, const char *pass)
void irc_process( irc_t *irc )
{
- char **lines, *temp, **cmd, *cs;
+ char **lines, *temp, **cmd;
int i;
if( irc->readbuffer != NULL )
@@ -298,11 +345,10 @@ void irc_process( irc_t *irc )
for( i = 0; *lines[i] != '\0'; i ++ )
{
- char conv[IRC_MAX_LINE+1];
+ char *conv = NULL;
- /* [WvG] Because irc_tokenize splits at every newline, the lines[] list
- should end with an empty string. This is why this actually works.
- Took me a while to figure out, Maurits. :-P */
+ /* [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] );
@@ -312,10 +358,14 @@ void irc_process( irc_t *irc )
break;
}
- if( ( cs = set_getstr( &irc->set, "charset" ) ) )
+ if( irc->iconv != (GIConv) -1 )
{
- conv[IRC_MAX_LINE] = 0;
- if( do_iconv( cs, "UTF-8", lines[i], conv, 0, IRC_MAX_LINE - 2 ) == -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: */
@@ -327,15 +377,18 @@ void irc_process( irc_t *irc )
"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.", cs );
- *conv = 0;
+ "message was ignored.",
+ set_getstr( &irc->set, "charset" ) );
+
+ g_free( conv );
+ conv = NULL;
}
else
{
irc_write( irc, ":%s NOTICE AUTH :%s", irc->myhost,
- "Warning: invalid (non-UTF8) characters received at login time." );
+ "Warning: invalid characters received at login time." );
- strncpy( conv, lines[i], IRC_MAX_LINE );
+ conv = g_strdup( lines[i] );
for( temp = conv; *temp; temp ++ )
if( *temp & 0x80 )
*temp = '?';
@@ -344,11 +397,15 @@ void irc_process( irc_t *irc )
lines[i] = conv;
}
- if( ( cmd = irc_parse_line( lines[i] ) ) == NULL )
- continue;
- irc_exec( irc, cmd );
+ if( lines[i] )
+ {
+ if( ( cmd = irc_parse_line( lines[i] ) ) == NULL )
+ continue;
+ irc_exec( irc, cmd );
+ g_free( cmd );
+ }
- g_free( cmd );
+ g_free( conv );
/* Shouldn't really happen, but just in case... */
if( !g_slist_find( irc_connection_list, irc ) )
@@ -372,42 +429,41 @@ void irc_process( irc_t *irc )
contains an incomplete line at the end, ends with an empty string. */
char **irc_tokenize( char *buffer )
{
- int i, j;
+ int i, j, n = 3;
char **lines;
- /* Count the number of elements we're gonna need. */
- for( i = 0, j = 1; buffer[i] != '\0'; i ++ )
- {
- if( buffer[i] == '\n' )
- if( buffer[i+1] != '\r' && buffer[i+1] != '\n' )
- j ++;
- }
-
- /* Allocate j+1 elements. */
- lines = g_new( char *, j + 1 );
-
- /* NULL terminate our list. */
- lines[j] = NULL;
+ /* Allocate n+1 elements. */
+ lines = g_new( char *, n + 1 );
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 ++)
+ /* 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] == '\n' )
+ if( buffer[i] == '\r' || buffer[i] == '\n' )
{
- buffer[i] = '\0';
+ while( buffer[i] == '\r' || buffer[i] == '\n' )
+ buffer[i++] = '\0';
+
+ lines[++j] = buffer + i;
- if( i > 0 && buffer[i-1] == '\r' )
- buffer[i-1] = '\0';
- if( buffer[i+1] != '\r' && buffer[i+1] != '\n' )
- lines[++j] = buffer + i + 1;
+ if( j >= n )
+ {
+ n *= 2;
+ lines = g_renew( char *, lines, n + 1 );
+ }
+
+ if( buffer[i] == '\0' )
+ break;
}
}
- return( lines );
+ /* NULL terminate our list. */
+ lines[++j] = NULL;
+
+ return lines;
}
/* Split an IRC-style line into little parts/arguments. */
@@ -541,31 +597,35 @@ void irc_write( irc_t *irc, char *format, ... )
va_end( params );
return;
-
}
void irc_vawrite( irc_t *irc, char *format, va_list params )
{
int size;
- char line[IRC_MAX_LINE+1], *cs;
+ char line[IRC_MAX_LINE+1];
/* Don't try to write anything new anymore when shutting down. */
if( irc->status & USTATUS_SHUTDOWN )
return;
- line[IRC_MAX_LINE] = 0;
+ memset( line, 0, sizeof( line ) );
g_vsnprintf( line, IRC_MAX_LINE - 2, format, params );
-
strip_newlines( line );
- if( ( cs = set_getstr( &irc->set, "charset" ) ) && ( g_strcasecmp( cs, "utf-8" ) != 0 ) )
+
+ if( irc->oconv != (GIConv) -1 )
{
- char conv[IRC_MAX_LINE+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 );
- conv[IRC_MAX_LINE] = 0;
- if( do_iconv( "UTF-8", cs, line, conv, 0, IRC_MAX_LINE - 2 ) != -1 )
- strcpy( line, conv );
+ g_free( conv );
}
- strcat( line, "\r\n" );
+ g_strlcat( line, "\r\n", IRC_MAX_LINE + 1 );
if( irc->sendbuffer != NULL )
{
@@ -738,12 +798,27 @@ void irc_login( irc_t *irc )
u->online = 1;
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 \x02help\x02 command. Lots of FAQs are answered there." );
+ irc_usermsg( irc, "Welcome to the BitlBee gateway!\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." );
if( global.conf->runmode == RUNMODE_FORKDAEMON || global.conf->runmode == RUNMODE_DAEMON )
ipc_to_master_str( "CLIENT %s %s :%s\r\n", irc->host, irc->nick, irc->realname );
irc->status |= USTATUS_LOGGED_IN;
+
+ /* 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] );
+ }
}
void irc_motd( irc_t *irc )
diff --git a/irc.h b/irc.h
index fde19212..0650245a 100644
--- a/irc.h
+++ b/irc.h
@@ -60,6 +60,7 @@ typedef struct irc
int pinging;
char *sendbuffer;
char *readbuffer;
+ GIConv iconv, oconv;
int sentbytes;
time_t oldtime;
@@ -68,7 +69,9 @@ typedef struct irc
char *user;
char *host;
char *realname;
- char *password;
+ 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 umode[8];
diff --git a/irc_commands.c b/irc_commands.c
index 68db4617..6a47007a 100644
--- a/irc_commands.c
+++ b/irc_commands.c
@@ -29,15 +29,36 @@
static void irc_cmd_pass( irc_t *irc, char **cmd )
{
- if( global.conf->auth_pass && strcmp( cmd[1], global.conf->auth_pass ) == 0 )
+ 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. */
+ return root_command( irc, send_cmd );
+ }
+ /* 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
+ else if( global.conf->auth_pass )
{
irc_reply( irc, 464, ":Incorrect password" );
}
+ else
+ {
+ /* Remember the password and try to identify after USER/NICK. */
+ irc_setpass( irc, cmd[1] );
+ irc_check_login( irc );
+ }
}
static void irc_cmd_user( irc_t *irc, char **cmd )
@@ -87,7 +108,10 @@ static void irc_cmd_ping( irc_t *irc, char **cmd )
static void irc_cmd_oper( irc_t *irc, char **cmd )
{
- if( global.conf->oper_pass && strcmp( cmd[2], global.conf->oper_pass ) == 0 )
+ 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_reply( irc, 381, ":Password accepted" );
@@ -253,8 +277,7 @@ static void irc_cmd_privmsg( irc_t *irc, char **cmd )
if( cmd[1] != irc->last_target )
{
- if( irc->last_target )
- g_free( irc->last_target );
+ g_free( irc->last_target );
irc->last_target = g_strdup( cmd[1] );
}
}
@@ -574,7 +597,7 @@ static void irc_cmd_rehash( irc_t *irc, char **cmd )
}
static const command_t irc_commands[] = {
- { "pass", 1, irc_cmd_pass, IRC_CMD_PRE_LOGIN },
+ { "pass", 1, irc_cmd_pass, 0 },
{ "user", 4, irc_cmd_user, IRC_CMD_PRE_LOGIN },
{ "nick", 1, irc_cmd_nick, 0 },
{ "quit", 0, irc_cmd_quit, 0 },
diff --git a/lib/Makefile b/lib/Makefile
index a79f7c4c..03fef1ab 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -9,7 +9,7 @@
-include ../Makefile.settings
# [SH] Program variables
-objects = arc.o base64.o $(EVENT_HANDLER) http_client.o ini.o md5.o misc.o proxy.o sha1.o $(SSL_CLIENT) url.o
+objects = arc.o base64.o $(EVENT_HANDLER) http_client.o ini.o md5.o misc.o proxy.o sha1.o $(SSL_CLIENT) url.o xmltree.o
CFLAGS += -Wall
LFLAGS += -r
@@ -17,7 +17,7 @@ LFLAGS += -r
# [SH] Phony targets
all: lib.o
check: all
-lcov:
+lcov: check
gcov:
gcov *.c
diff --git a/lib/arc.c b/lib/arc.c
index 617f6b96..fd498454 100644
--- a/lib/arc.c
+++ b/lib/arc.c
@@ -130,18 +130,40 @@ unsigned char arc_getbyte( struct arc_state *st )
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 arc_encode( char *clear, int clear_len, unsigned char **crypt, char *password, int pad_to )
{
struct arc_state *st;
unsigned char *key;
- int key_len, i;
+ 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 );
@@ -160,6 +182,7 @@ int arc_encode( char *clear, int clear_len, unsigned char **crypt, char *passwor
crypt[0][i+ARC_IV_LEN] = clear[i] ^ arc_getbyte( st );
g_free( st );
+ g_free( padded );
return clear_len + ARC_IV_LEN;
}
diff --git a/lib/arc.h b/lib/arc.h
index 882372ed..816fa612 100644
--- a/lib/arc.h
+++ b/lib/arc.h
@@ -30,7 +30,11 @@ struct arc_state
unsigned char i, j;
};
-struct arc_state *arc_keymaker( unsigned char *key, int kl, int cycles );
+#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 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, char *password );
diff --git a/lib/base64.c b/lib/base64.c
index 64e9692a..ea0db6b9 100644
--- a/lib/base64.c
+++ b/lib/base64.c
@@ -117,7 +117,7 @@ int base64_decode_real(const unsigned char *in, unsigned char *out, char *b64rev
{
int i, outlen = 0;
- for( i = 0; in[i]; i += 4 )
+ for( i = 0; in[i] && in[i+1] && in[i+2] && in[i+3]; i += 4 )
{
int sx;
diff --git a/lib/events_glib.c b/lib/events_glib.c
index 1198dba6..3e194e98 100644
--- a/lib/events_glib.c
+++ b/lib/events_glib.c
@@ -50,11 +50,12 @@ typedef struct _GaimIOClosure {
gpointer data;
} GaimIOClosure;
-static GMainLoop *loop;
+static GMainLoop *loop = NULL;
void b_main_init()
{
- loop = g_main_new( FALSE );
+ if( loop == NULL )
+ loop = g_main_new( FALSE );
}
void b_main_run()
diff --git a/lib/misc.c b/lib/misc.c
index 18d98f9e..ccf208b5 100644
--- a/lib/misc.c
+++ b/lib/misc.c
@@ -32,6 +32,7 @@
#define BITLBEE_CORE
#include "nogaim.h"
+#include "base64.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -596,3 +597,43 @@ gboolean ssl_sockerr_again( void *ssl )
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 )
+{
+ md5_byte_t *pass_dec = NULL;
+ md5_byte_t pass_md5[16];
+ md5_state_t md5_state;
+ int ret, i;
+
+ if( base64_decode( hash, &pass_dec ) != 21 )
+ {
+ ret = -1;
+ }
+ else
+ {
+ md5_init( &md5_state );
+ md5_append( &md5_state, (md5_byte_t*) password, strlen( password ) );
+ md5_append( &md5_state, (md5_byte_t*) pass_dec + 16, 5 ); /* Hmmm, salt! */
+ md5_finish( &md5_state, pass_md5 );
+
+ 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;
+}
diff --git a/lib/misc.h b/lib/misc.h
index 7804bfb1..a2acada6 100644
--- a/lib/misc.h
+++ b/lib/misc.h
@@ -66,4 +66,6 @@ G_MODULE_EXPORT char *word_wrap( 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 );
+
#endif
diff --git a/lib/proxy.c b/lib/proxy.c
index 0e1c8f07..53b89d64 100644
--- a/lib/proxy.c
+++ b/lib/proxy.c
@@ -529,7 +529,7 @@ int proxy_connect(const char *host, int port, b_event_handler func, gpointer dat
{
struct PHB *phb;
- if (!host || !port || (port == -1) || !func || strlen(host) > 128) {
+ if (!host || port <= 0 || !func || strlen(host) > 128) {
return -1;
}
@@ -537,7 +537,7 @@ int proxy_connect(const char *host, int port, b_event_handler func, gpointer dat
phb->func = func;
phb->data = data;
- if ((proxytype == PROXY_NONE) || strlen(proxyhost) > 0 || !proxyport || (proxyport == -1))
+ if (proxytype == PROXY_NONE || !proxyhost[0] || proxyport <= 0)
return proxy_connect_none(host, port, phb);
else if (proxytype == PROXY_HTTP)
return proxy_connect_http(host, port, phb);
diff --git a/lib/ssl_client.h b/lib/ssl_client.h
index dcbf9a01..f91d0d70 100644
--- a/lib/ssl_client.h
+++ b/lib/ssl_client.h
@@ -59,6 +59,9 @@ G_MODULE_EXPORT void *ssl_starttls( int fd, ssl_input_function func, gpointer da
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 );
+/* See ssl_openssl.c for an explanation. */
+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_ );
diff --git a/lib/ssl_gnutls.c b/lib/ssl_gnutls.c
index b964ab49..f5945442 100644
--- a/lib/ssl_gnutls.c
+++ b/lib/ssl_gnutls.c
@@ -210,6 +210,12 @@ int ssl_write( void *conn, const char *buf, int len )
return st;
}
+/* See ssl_openssl.c for an explanation. */
+int ssl_pending( void *conn )
+{
+ return 0;
+}
+
void ssl_disconnect( void *conn_ )
{
struct scd *conn = conn_;
diff --git a/lib/ssl_nss.c b/lib/ssl_nss.c
index 218b3a80..eba3c441 100644
--- a/lib/ssl_nss.c
+++ b/lib/ssl_nss.c
@@ -168,6 +168,12 @@ int ssl_write( void *conn, const char *buf, int len )
return( PR_Write ( ((struct scd*)conn)->prfd, buf, len ) );
}
+/* See ssl_openssl.c for an explanation. */
+int ssl_pending( void *conn )
+{
+ return 0;
+}
+
void ssl_disconnect( void *conn_ )
{
struct scd *conn = conn_;
diff --git a/lib/ssl_openssl.c b/lib/ssl_openssl.c
index b1ba1db9..fc6d433e 100644
--- a/lib/ssl_openssl.c
+++ b/lib/ssl_openssl.c
@@ -61,16 +61,16 @@ void *ssl_connect( char *host, int port, 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->inpa = -1;
-
if( conn->fd < 0 )
{
g_free( conn );
return NULL;
}
+ conn->func = func;
+ conn->data = data;
+ conn->inpa = -1;
+
return conn;
}
@@ -230,6 +230,21 @@ int ssl_write( void *conn, const char *buf, int len )
return st;
}
+/* Only OpenSSL *really* needs this (and well, maybe NSS). 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.
+
+ Returns 0 if there's nothing left or if we don't have to care (GnuTLS),
+ 1 if there's more data. */
+int ssl_pending( void *conn )
+{
+ return ( ((struct scd*)conn) && ((struct scd*)conn)->established ) ?
+ SSL_pending( ((struct scd*)conn)->ssl ) > 0 : 0;
+}
+
void ssl_disconnect( void *conn_ )
{
struct scd *conn = conn_;
diff --git a/lib/url.c b/lib/url.c
index c6fdc4c8..de9966b4 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -25,13 +25,16 @@
#include "url.h"
-/* Convert an URL to a url_t structure */
+/* Convert an URL to a url_t structure */
int url_set( url_t *url, char *set_url )
{
- char s[MAX_STRING];
+ char s[MAX_STRING+1];
char *i;
- /* protocol:// */
+ memset( url, 0, sizeof( url_t ) );
+ memset( s, 0, sizeof( s ) );
+
+ /* protocol:// */
if( ( i = strstr( set_url, "://" ) ) == NULL )
{
url->proto = PROTO_DEFAULT;
@@ -48,13 +51,12 @@ int url_set( url_t *url, char *set_url )
else if( g_strncasecmp( set_url, "socks5", i - set_url ) == 0 )
url->proto = PROTO_SOCKS5;
else
- {
- return( 0 );
- }
+ return 0;
+
strncpy( s, i + 3, MAX_STRING );
}
- /* Split */
+ /* Split */
if( ( i = strchr( s, '/' ) ) == NULL )
{
strcpy( url->file, "/" );
@@ -66,7 +68,7 @@ int url_set( url_t *url, char *set_url )
}
strncpy( url->host, s, MAX_STRING );
- /* Check for username in host field */
+ /* Check for username in host field */
if( strrchr( url->host, '@' ) != NULL )
{
strncpy( url->user, url->host, MAX_STRING );
@@ -75,19 +77,19 @@ int url_set( url_t *url, char *set_url )
strcpy( url->host, i + 1 );
*url->pass = 0;
}
- /* If not: Fill in defaults */
+ /* If not: Fill in defaults */
else
{
*url->user = *url->pass = 0;
}
- /* Password? */
+ /* Password? */
if( ( i = strchr( url->user, ':' ) ) != NULL )
{
*i = 0;
strcpy( url->pass, i + 1 );
}
- /* Port number? */
+ /* Port number? */
if( ( i = strchr( url->host, ':' ) ) != NULL )
{
*i = 0;
diff --git a/lib/url.h b/lib/url.h
index e9e1ecfe..8c038c91 100644
--- a/lib/url.h
+++ b/lib/url.h
@@ -25,20 +25,20 @@
#include "bitlbee.h"
-#define PROTO_HTTP 2
-#define PROTO_HTTPS 5
-#define PROTO_SOCKS4 3
-#define PROTO_SOCKS5 4
-#define PROTO_DEFAULT PROTO_HTTP
+#define PROTO_HTTP 2
+#define PROTO_HTTPS 5
+#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 file[MAX_STRING];
- char user[MAX_STRING];
- char pass[MAX_STRING];
+ 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, char *set_url );
diff --git a/protocols/jabber/xmltree.c b/lib/xmltree.c
index 62549eb5..e65b4f41 100644
--- a/protocols/jabber/xmltree.c
+++ b/lib/xmltree.c
@@ -110,11 +110,12 @@ GMarkupParser xt_parser_funcs =
NULL
};
-struct xt_parser *xt_new( gpointer data )
+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;
diff --git a/protocols/jabber/xmltree.h b/lib/xmltree.h
index b8b61641..10677412 100644
--- a/protocols/jabber/xmltree.h
+++ b/lib/xmltree.h
@@ -70,13 +70,13 @@ struct xt_parser
struct xt_node *root;
struct xt_node *cur;
- struct xt_handler_entry *handlers;
+ const struct xt_handler_entry *handlers;
gpointer data;
GError *gerr;
};
-struct xt_parser *xt_new( gpointer data );
+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, char *text, int text_len );
int xt_handle( struct xt_parser *xt, struct xt_node *node, int depth );
diff --git a/protocols/Makefile b/protocols/Makefile
index f7d76e0f..18d79e8d 100644
--- a/protocols/Makefile
+++ b/protocols/Makefile
@@ -26,7 +26,7 @@ LFLAGS += -r
# [SH] Phony targets
all: protocols.o
check: all
-lcov:
+lcov: check
gcov:
gcov *.c
diff --git a/protocols/jabber/Makefile b/protocols/jabber/Makefile
index 21d7ef07..891df5eb 100644
--- a/protocols/jabber/Makefile
+++ b/protocols/jabber/Makefile
@@ -9,7 +9,7 @@
-include ../../Makefile.settings
# [SH] Program variables
-objects = conference.o io.o iq.o jabber.o jabber_util.o message.o presence.o sasl.o xmltree.o si.o s5bytestream.o
+objects = conference.o io.o iq.o jabber.o jabber_util.o message.o presence.o sasl.o si.o s5bytestream.o
CFLAGS += -Wall
LFLAGS += -r
@@ -17,7 +17,7 @@ LFLAGS += -r
# [SH] Phony targets
all: jabber_mod.o
check: all
-lcov:
+lcov: check
gcov:
gcov *.c
diff --git a/protocols/jabber/io.c b/protocols/jabber/io.c
index 86c216ef..10efad37 100644
--- a/protocols/jabber/io.c
+++ b/protocols/jabber/io.c
@@ -240,8 +240,13 @@ static gboolean jabber_read_callback( gpointer data, gint fd, b_input_condition
return FALSE;
}
- /* EAGAIN/etc or a successful read. */
- return TRUE;
+ 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 )
@@ -520,8 +525,7 @@ gboolean jabber_start_stream( struct im_connection *ic )
/* 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( ic );
- jd->xt->handlers = (struct xt_handler_entry*) jabber_handlers;
+ jd->xt = xt_new( jabber_handlers, ic );
if( jd->r_inpa <= 0 )
jd->r_inpa = b_input_add( jd->fd, GAIM_INPUT_READ, jabber_read_callback, ic );
diff --git a/protocols/jabber/iq.c b/protocols/jabber/iq.c
index d45b7625..22aa3e7c 100644
--- a/protocols/jabber/iq.c
+++ b/protocols/jabber/iq.c
@@ -533,6 +533,8 @@ static xt_status jabber_iq_display_vcard( struct im_connection *ic, struct xt_no
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, char *handle, char *name )
{
struct xt_node *node;
@@ -548,13 +550,36 @@ int jabber_add_to_roster( struct im_connection *ic, char *handle, char *name )
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 );
- xt_free_node( 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( imcb_find_buddy( 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;
diff --git a/protocols/jabber/jabber.c b/protocols/jabber/jabber.c
index 07d760a5..04d6f906 100644
--- a/protocols/jabber/jabber.c
+++ b/protocols/jabber/jabber.c
@@ -266,6 +266,7 @@ static void jabber_logout( struct im_connection *ic )
xt_free( jd->xt );
+ g_free( jd->cached_id_prefix );
g_free( jd->away_message );
g_free( jd->username );
g_free( jd );
diff --git a/protocols/jabber/jabber_util.c b/protocols/jabber/jabber_util.c
index c8470b57..23a3010f 100644
--- a/protocols/jabber/jabber_util.c
+++ b/protocols/jabber/jabber_util.c
@@ -249,8 +249,10 @@ struct jabber_buddy_ask_data
char *realname;
};
-static void jabber_buddy_ask_yes( gpointer w, struct jabber_buddy_ask_data *bla )
+static void jabber_buddy_ask_yes( void *data )
{
+ struct jabber_buddy_ask_data *bla = data;
+
presence_send_request( bla->ic, bla->handle, "subscribed" );
if( imcb_find_buddy( bla->ic, bla->handle ) == NULL )
@@ -260,8 +262,10 @@ static void jabber_buddy_ask_yes( gpointer w, struct jabber_buddy_ask_data *bla
g_free( bla );
}
-static void jabber_buddy_ask_no( gpointer w, struct jabber_buddy_ask_data *bla )
+static void jabber_buddy_ask_no( void *data )
{
+ struct jabber_buddy_ask_data *bla = data;
+
presence_send_request( bla->ic, bla->handle, "subscribed" );
g_free( bla->handle );
diff --git a/protocols/jabber/sasl.c b/protocols/jabber/sasl.c
index 87059051..53248ef3 100644
--- a/protocols/jabber/sasl.c
+++ b/protocols/jabber/sasl.c
@@ -21,6 +21,8 @@
* *
\***************************************************************************/
+#include <ctype.h>
+
#include "jabber.h"
#include "base64.h"
@@ -106,12 +108,17 @@ xt_status sasl_pkt_mechanisms( struct xt_node *node, gpointer data )
return XT_HANDLED;
}
-static char *sasl_get_part( char *data, char *field )
+/* 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( isspace( *data ) || *data == ',' )
+ data ++;
+
if( g_strncasecmp( data, field, len ) == 0 && data[len] == '=' )
{
i = strlen( field ) + 1;
@@ -128,13 +135,19 @@ static char *sasl_get_part( char *data, char *field )
i ++;
}
- /* If we got a comma, we got a new field. Check it. */
- if( data[i] == ',' &&
- g_strncasecmp( data + i + 1, field, len ) == 0 &&
- data[i+len+1] == '=' )
+ /* If we got a comma, we got a new field. Check it,
+ find the next key after it. */
+ if( data[i] == ',' )
{
- i += len + 2;
- break;
+ while( isspace( data[i] ) || data[i] == ',' )
+ i ++;
+
+ if( g_strncasecmp( data + i, field, len ) == 0 &&
+ data[i+len] == '=' )
+ {
+ i += len + 1;
+ break;
+ }
}
}
}
diff --git a/protocols/msn/Makefile b/protocols/msn/Makefile
index 3440658d..6a588613 100644
--- a/protocols/msn/Makefile
+++ b/protocols/msn/Makefile
@@ -17,7 +17,7 @@ LFLAGS += -r
# [SH] Phony targets
all: msn_mod.o
check: all
-lcov:
+lcov: check
gcov:
gcov *.c
diff --git a/protocols/msn/msn.h b/protocols/msn/msn.h
index c8f4f4c6..63759303 100644
--- a/protocols/msn/msn.h
+++ b/protocols/msn/msn.h
@@ -28,7 +28,7 @@
#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 DEBUG
+#ifdef DEBUG_MSN
#define debug( text... ) imcb_log( ic, text );
#else
#define debug( text... )
diff --git a/protocols/msn/msn_util.c b/protocols/msn/msn_util.c
index fae2877d..58ad22f8 100644
--- a/protocols/msn/msn_util.c
+++ b/protocols/msn/msn_util.c
@@ -89,8 +89,10 @@ struct msn_buddy_ask_data
char *realname;
};
-static void msn_buddy_ask_yes( gpointer w, struct msn_buddy_ask_data *bla )
+static void msn_buddy_ask_yes( void *data )
{
+ struct msn_buddy_ask_data *bla = data;
+
msn_buddy_list_add( bla->ic, "AL", bla->handle, bla->realname );
if( imcb_find_buddy( bla->ic, bla->handle ) == NULL )
@@ -101,8 +103,10 @@ static void msn_buddy_ask_yes( gpointer w, struct msn_buddy_ask_data *bla )
g_free( bla );
}
-static void msn_buddy_ask_no( gpointer w, struct msn_buddy_ask_data *bla )
+static void msn_buddy_ask_no( void *data )
{
+ struct msn_buddy_ask_data *bla = data;
+
msn_buddy_list_add( bla->ic, "BL", bla->handle, bla->realname );
g_free( bla->handle );
diff --git a/protocols/msn/ns.c b/protocols/msn/ns.c
index 0bb84a74..ff7da6ed 100644
--- a/protocols/msn/ns.c
+++ b/protocols/msn/ns.c
@@ -33,7 +33,7 @@ static gboolean msn_ns_callback( gpointer data, gint source, b_input_condition c
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 );
+static void msn_auth_got_passport_token( struct msn_auth_data *mad );
gboolean msn_ns_connected( gpointer data, gint source, b_input_condition cond )
{
@@ -213,7 +213,7 @@ static int msn_ns_command( gpointer data, char **cmd, int num_parts )
if( num_parts == 5 && strcmp( cmd[2], "TWN" ) == 0 && strcmp( cmd[3], "S" ) == 0 )
{
/* Time for some Passport black magic... */
- if( !passport_get_id( msn_auth_got_passport_id, ic, ic->acc->user, ic->acc->pass, cmd[4] ) )
+ if( !passport_get_token( msn_auth_got_passport_token, ic, ic->acc->user, ic->acc->pass, cmd[4] ) )
{
imcb_error( ic, "Error while contacting Passport server" );
imc_logout( ic, TRUE );
@@ -673,22 +673,26 @@ static int msn_ns_message( gpointer data, char *msg, int msglen, char **cmd, int
return( 1 );
}
-static void msn_auth_got_passport_id( struct passport_reply *rep )
+static void msn_auth_got_passport_token( struct msn_auth_data *mad )
{
- struct im_connection *ic = rep->data;
- struct msn_data *md = ic->proto_data;
- char *key = rep->result;
- char buf[1024];
+ struct im_connection *ic = mad->data;
+ struct msn_data *md;
- if( key == NULL )
+ /* Dead connection? */
+ if( g_slist_find( msn_connections, ic ) == NULL )
+ return;
+
+ md = ic->proto_data;
+ if( mad->token )
{
- imcb_error( ic, "Error during Passport authentication (%s)",
- rep->error_string ? rep->error_string : "Unknown error" );
- imc_logout( ic, TRUE );
+ char buf[1024];
+
+ g_snprintf( buf, sizeof( buf ), "USR %d TWN S %s\r\n", ++md->trId, mad->token );
+ msn_write( ic, buf, strlen( buf ) );
}
else
{
- g_snprintf( buf, sizeof( buf ), "USR %d TWN S %s\r\n", ++md->trId, key );
- msn_write( ic, buf, strlen( buf ) );
+ imcb_error( ic, "Error during Passport authentication: %s", mad->error );
+ imc_logout( ic, TRUE );
}
}
diff --git a/protocols/msn/passport.c b/protocols/msn/passport.c
index 9fe6a174..565d15f3 100644
--- a/protocols/msn/passport.c
+++ b/protocols/msn/passport.c
@@ -1,8 +1,7 @@
-/* passport.c
+/** 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>
+ * Functions to login to Microsoft Passport service for Messenger
+ * Copyright (C) 2004-2008 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
@@ -23,208 +22,149 @@
#include "passport.h"
#include "msn.h"
#include "bitlbee.h"
+#include "url.h"
+#include "misc.h"
+#include "xmltree.h"
#include <ctype.h>
#include <errno.h>
-#define MSN_BUF_LEN 8192
+static int passport_get_token_real( struct msn_auth_data *mad );
+static void passport_get_token_ready( struct http_request *req );
-static char *prd_cached = NULL;
-
-static int passport_get_id_real( gpointer func, gpointer data, char *header );
-static void passport_get_id_ready( struct http_request *req );
-
-static int passport_retrieve_dalogin( gpointer data, gpointer func, char *header );
-static void passport_retrieve_dalogin_ready( struct http_request *req );
-
-static char *passport_create_header( char *cookie, char *email, char *pwd );
-static void destroy_reply( struct passport_reply *rep );
-
-int passport_get_id( gpointer func, gpointer data, char *username, char *password, char *cookie )
-{
- char *header = passport_create_header( cookie, username, password );
-
- if( prd_cached == NULL )
- return passport_retrieve_dalogin( func, data, header );
- else
- return passport_get_id_real( func, data, header );
-}
-
-static int passport_get_id_real( gpointer func, gpointer data, char *header )
+int passport_get_token( gpointer func, gpointer data, char *username, char *password, char *cookie )
{
- struct passport_reply *rep;
- char *server, *dummy, *reqs;
- struct http_request *req;
-
- rep = g_new0( struct passport_reply, 1 );
- rep->data = data;
- rep->func = func;
- rep->header = header;
-
- server = g_strdup( prd_cached );
- dummy = strchr( server, '/' );
-
- if( dummy == NULL )
- {
- destroy_reply( rep );
- return( 0 );
- }
-
- reqs = g_strdup_printf( "GET %s HTTP/1.0\r\n%s\r\n\r\n", dummy, header );
+ struct msn_auth_data *mad = g_new0( struct msn_auth_data, 1 );
+ int i;
- *dummy = 0;
- req = http_dorequest( server, 443, 1, reqs, passport_get_id_ready, rep );
+ mad->username = g_strdup( username );
+ mad->password = g_strdup( password );
+ mad->cookie = g_strdup( cookie );
- g_free( server );
- g_free( reqs );
+ mad->callback = func;
+ mad->data = data;
- if( req == NULL )
- destroy_reply( rep );
+ mad->url = g_strdup( SOAP_AUTHENTICATION_URL );
+ mad->ttl = 3; /* Max. # of redirects. */
- return( req != NULL );
-}
-
-static void passport_get_id_ready( struct http_request *req )
-{
- struct passport_reply *rep = req->data;
-
- if( !g_slist_find( msn_connections, rep->data ) )
- {
- destroy_reply( rep );
- return;
- }
+ /* HTTP-escape stuff and s/,/&/ */
+ http_decode( mad->cookie );
+ for( i = 0; mad->cookie[i]; i ++ )
+ if( mad->cookie[i] == ',' )
+ mad->cookie[i] = '&';
- if( req->finished && req->reply_headers && req->status_code == 200 )
- {
- char *dummy;
-
- if( ( dummy = strstr( req->reply_headers, "from-PP='" ) ) )
- {
- char *responseend;
-
- dummy += strlen( "from-PP='" );
- responseend = strchr( dummy, '\'' );
- if( responseend )
- *responseend = 0;
-
- rep->result = g_strdup( dummy );
- }
- else
- {
- rep->error_string = g_strdup( "Could not parse Passport server response" );
- }
- }
- else
- {
- rep->error_string = g_strdup_printf( "HTTP error: %s",
- req->status_string ? req->status_string : "Unknown error" );
- }
+ /* Microsoft doesn't allow password longer than 16 chars and silently
+ fails authentication if you give the "full version" of your passwd. */
+ if( strlen( mad->password ) > MAX_PASSPORT_PWLEN )
+ mad->password[MAX_PASSPORT_PWLEN] = 0;
- rep->func( rep );
- destroy_reply( rep );
+ return passport_get_token_real( mad );
}
-static char *passport_create_header( char *cookie, char *email, char *pwd )
+static int passport_get_token_real( struct msn_auth_data *mad )
{
- char *buffer;
- char *currenttoken;
- char *email_enc, *pwd_enc;
-
- currenttoken = strstr( cookie, "lc=" );
- if( currenttoken == NULL )
- return NULL;
+ char *post_payload, *post_request;
+ struct http_request *req;
+ url_t url;
- email_enc = g_new0( char, strlen( email ) * 3 + 1 );
- strcpy( email_enc, email );
- http_encode( email_enc );
+ url_set( &url, mad->url );
- pwd_enc = g_new0( char, strlen( pwd ) * 3 + 1 );
- strcpy( pwd_enc, pwd );
- http_encode( pwd_enc );
+ post_payload = g_markup_printf_escaped( SOAP_AUTHENTICATION_PAYLOAD,
+ mad->username,
+ mad->password,
+ mad->cookie );
- buffer = g_strdup_printf( "Authorization: Passport1.4 OrgVerb=GET,"
- "OrgURL=http%%3A%%2F%%2Fmessenger%%2Emsn%%2Ecom,"
- "sign-in=%s,pwd=%s,%s", email_enc, pwd_enc,
- currenttoken );
+ post_request = g_strdup_printf( SOAP_AUTHENTICATION_REQUEST,
+ url.file, url.host,
+ (int) strlen( post_payload ),
+ post_payload );
+
+ req = http_dorequest( url.host, url.port, 1, post_request,
+ passport_get_token_ready, mad );
- g_free( email_enc );
- g_free( pwd_enc );
+ g_free( post_request );
+ g_free( post_payload );
- return buffer;
+ return req != NULL;
}
-static int passport_retrieve_dalogin( gpointer func, gpointer data, char *header )
-{
- struct passport_reply *rep = g_new0( struct passport_reply, 1 );
- struct http_request *req;
-
- rep->data = data;
- rep->func = func;
- rep->header = header;
-
- req = http_dorequest_url( "https://nexus.passport.com/rdr/pprdr.asp", passport_retrieve_dalogin_ready, rep );
-
- if( !req )
- destroy_reply( rep );
-
- return( req != NULL );
-}
+static xt_status passport_xt_extract_token( struct xt_node *node, gpointer data );
+static xt_status passport_xt_handle_fault( struct xt_node *node, gpointer data );
-static void passport_retrieve_dalogin_ready( struct http_request *req )
+static const struct xt_handler_entry passport_xt_handlers[] = {
+ { "wsse:BinarySecurityToken", "wst:RequestedSecurityToken", passport_xt_extract_token },
+ { "S:Fault", "S:Envelope", passport_xt_handle_fault },
+ { NULL, NULL, NULL }
+};
+
+static void passport_get_token_ready( struct http_request *req )
{
- struct passport_reply *rep = req->data;
- char *dalogin;
- char *urlend;
+ struct msn_auth_data *mad = req->data;
+ struct xt_parser *parser;
+
+ g_free( mad->url );
+ g_free( mad->error );
+ mad->url = mad->error = NULL;
- if( !g_slist_find( msn_connections, rep->data ) )
+ if( req->status_code == 200 )
{
- destroy_reply( rep );
- return;
+ parser = xt_new( passport_xt_handlers, mad );
+ xt_feed( parser, req->reply_body, req->body_size );
+ xt_handle( parser, NULL, -1 );
+ xt_free( parser );
}
-
- if( !req->finished || !req->reply_headers || req->status_code != 200 )
+ else
{
- rep->error_string = g_strdup_printf( "HTTP error while fetching DALogin: %s",
- req->status_string ? req->status_string : "Unknown error" );
- goto failure;
+ mad->error = g_strdup_printf( "HTTP error %d (%s)", req->status_code,
+ req->status_string ? req->status_string : "unknown" );
}
- dalogin = strstr( req->reply_headers, "DALogin=" );
+ if( mad->error == NULL && mad->token == NULL )
+ mad->error = g_strdup( "Could not parse Passport server response" );
- if( !dalogin )
+ if( mad->url && mad->token == NULL )
{
- rep->error_string = g_strdup( "Parse error while fetching DALogin" );
- goto failure;
+ passport_get_token_real( mad );
}
-
- 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_real( rep->func, rep->data, rep->header ) )
+ else
{
- rep->header = NULL;
- destroy_reply( rep );
- return;
+ mad->callback( mad );
+
+ g_free( mad->url );
+ g_free( mad->username );
+ g_free( mad->password );
+ g_free( mad->cookie );
+ g_free( mad->token );
+ g_free( mad->error );
+ g_free( mad );
}
+}
+
+static xt_status passport_xt_extract_token( struct xt_node *node, gpointer data )
+{
+ struct msn_auth_data *mad = data;
+ char *s;
+
+ if( ( s = xt_find_attr( node, "Id" ) ) && strcmp( s, "PPToken1" ) == 0 )
+ mad->token = g_memdup( node->text, node->text_len + 1 );
-failure:
- rep->func( rep );
- destroy_reply( rep );
+ return XT_HANDLED;
}
-static void destroy_reply( struct passport_reply *rep )
+static xt_status passport_xt_handle_fault( struct xt_node *node, gpointer data )
{
- g_free( rep->result );
- g_free( rep->header );
- g_free( rep->error_string );
- g_free( rep );
+ struct msn_auth_data *mad = data;
+ struct xt_node *code = xt_find_node( node->children, "faultcode" );
+ struct xt_node *string = xt_find_node( node->children, "faultstring" );
+ struct xt_node *redirect = xt_find_node( node->children, "psf:redirectUrl" );
+
+ if( redirect && redirect->text_len && mad->ttl-- > 0 )
+ mad->url = g_memdup( redirect->text, redirect->text_len + 1 );
+
+ if( code == NULL || code->text_len == 0 )
+ mad->error = g_strdup( "Unknown error" );
+ else
+ mad->error = g_strdup_printf( "%s (%s)", code->text, string && string->text_len ?
+ string->text : "no description available" );
+
+ return XT_HANDLED;
}
diff --git a/protocols/msn/passport.h b/protocols/msn/passport.h
index 9fd81a82..517d2e91 100644
--- a/protocols/msn/passport.h
+++ b/protocols/msn/passport.h
@@ -1,10 +1,7 @@
-#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>
+ * Functions to login to Microsoft Passport service for Messenger
+ * Copyright (C) 2004-2008 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
@@ -17,9 +14,15 @@
*
* 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
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+/* Thanks to http://msnpiki.msnfanatic.com/index.php/MSNP13:SOAPTweener
+ for the specs! */
+
+#ifndef __PASSPORT_H__
+#define __PASSPORT_H__
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -32,15 +35,79 @@
#endif
#include "nogaim.h"
-struct passport_reply
+#define MAX_PASSPORT_PWLEN 16
+
+struct msn_auth_data
{
- void (*func)( struct passport_reply * );
- void *data;
- char *result;
- char *header;
- char *error_string;
+ char *url;
+ int ttl;
+
+ char *username;
+ char *password;
+ char *cookie;
+
+ /* The end result, the only thing we'll really be interested in
+ once finished. */
+ char *token;
+ char *error; /* Yeah, or that... */
+
+ void (*callback)( struct msn_auth_data *mad );
+ gpointer data;
};
-int passport_get_id( gpointer func, gpointer data, char *username, char *password, char *cookie );
+#define SOAP_AUTHENTICATION_URL "https://loginnet.passport.com/RST.srf"
+
+#define SOAP_AUTHENTICATION_REQUEST \
+"POST %s HTTP/1.0\r\n" \
+"Accept: text/*\r\n" \
+"User-Agent: BitlBee " BITLBEE_VERSION "\r\n" \
+"Host: %s\r\n" \
+"Content-Length: %d\r\n" \
+"Cache-Control: no-cache\r\n" \
+"\r\n" \
+"%s"
+
+#define SOAP_AUTHENTICATION_PAYLOAD \
+"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" \
+"<Envelope xmlns=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:wsse=\"http://schemas.xmlsoap.org/ws/2003/06/secext\" xmlns:saml=\"urn:oasis:names:tc:SAML:1.0:assertion\" xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2002/12/policy\" xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\" xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/03/addressing\" xmlns:wssc=\"http://schemas.xmlsoap.org/ws/2004/04/sc\" xmlns:wst=\"http://schemas.xmlsoap.org/ws/2004/04/trust\">" \
+ "<Header>" \
+ "<ps:AuthInfo xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" Id=\"PPAuthInfo\">" \
+ "<ps:HostingApp>{7108E71A-9926-4FCB-BCC9-9A9D3F32E423}</ps:HostingApp>" \
+ "<ps:BinaryVersion>4</ps:BinaryVersion>" \
+ "<ps:UIVersion>1</ps:UIVersion>" \
+ "<ps:Cookies></ps:Cookies>" \
+ "<ps:RequestParams>AQAAAAIAAABsYwQAAAAzMDg0</ps:RequestParams>" \
+ "</ps:AuthInfo>" \
+ "<wsse:Security>" \
+ "<wsse:UsernameToken Id=\"user\">" \
+ "<wsse:Username>%s</wsse:Username>" \
+ "<wsse:Password>%s</wsse:Password>" \
+ "</wsse:UsernameToken>" \
+ "</wsse:Security>" \
+ "</Header>" \
+ "<Body>" \
+ "<ps:RequestMultipleSecurityTokens xmlns:ps=\"http://schemas.microsoft.com/Passport/SoapServices/PPCRL\" Id=\"RSTS\">" \
+ "<wst:RequestSecurityToken Id=\"RST0\">" \
+ "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>" \
+ "<wsp:AppliesTo>" \
+ "<wsa:EndpointReference>" \
+ "<wsa:Address>http://Passport.NET/tb</wsa:Address>" \
+ "</wsa:EndpointReference>" \
+ "</wsp:AppliesTo>" \
+ "</wst:RequestSecurityToken>" \
+ "<wst:RequestSecurityToken Id=\"RST1\">" \
+ "<wst:RequestType>http://schemas.xmlsoap.org/ws/2004/04/security/trust/Issue</wst:RequestType>" \
+ "<wsp:AppliesTo>" \
+ "<wsa:EndpointReference>" \
+ "<wsa:Address>messenger.msn.com</wsa:Address>" \
+ "</wsa:EndpointReference>" \
+ "</wsp:AppliesTo>" \
+ "<wsse:PolicyReference URI=\"?%s\"></wsse:PolicyReference>" \
+ "</wst:RequestSecurityToken>" \
+ "</ps:RequestMultipleSecurityTokens>" \
+ "</Body>" \
+"</Envelope>"
+
+int passport_get_token( gpointer func, gpointer data, char *username, char *password, char *cookie );
#endif /* __PASSPORT_H__ */
diff --git a/protocols/nogaim.c b/protocols/nogaim.c
index 3ce15166..7466e93a 100644
--- a/protocols/nogaim.c
+++ b/protocols/nogaim.c
@@ -342,7 +342,8 @@ void imc_logout( struct im_connection *ic, int allow_reconnect )
/* dialogs.c */
-void imcb_ask( struct im_connection *ic, char *msg, void *data, void *doit, void *dont )
+void imcb_ask( struct im_connection *ic, char *msg, void *data,
+ query_callback doit, query_callback dont )
{
query_add( ic->irc, ic, msg, doit, dont, data );
}
@@ -494,18 +495,20 @@ struct show_got_added_data
char *handle;
};
-void show_got_added_no( gpointer w, struct show_got_added_data *data )
+void show_got_added_no( void *data )
{
- g_free( data->handle );
+ g_free( ((struct show_got_added_data*)data)->handle );
g_free( data );
}
-void show_got_added_yes( gpointer w, struct show_got_added_data *data )
+void show_got_added_yes( void *data )
{
- data->ic->acc->prpl->add_buddy( data->ic, data->handle, NULL );
- /* imcb_add_buddy( data->ic, NULL, data->handle, data->handle ); */
+ struct show_got_added_data *sga = data;
- return show_got_added_no( w, data );
+ sga->ic->acc->prpl->add_buddy( sga->ic, sga->handle, NULL );
+ /* imcb_add_buddy( sga->ic, NULL, sga->handle, sga->handle ); */
+
+ return show_got_added_no( data );
}
void imcb_ask_add( struct im_connection *ic, char *handle, const char *realname )
diff --git a/protocols/nogaim.h b/protocols/nogaim.h
index 37232493..17da2b36 100644
--- a/protocols/nogaim.h
+++ b/protocols/nogaim.h
@@ -41,6 +41,7 @@
#include "bitlbee.h"
#include "account.h"
#include "proxy.h"
+#include "query.h"
#include "md5.h"
#include "ft.h"
@@ -264,7 +265,7 @@ G_MODULE_EXPORT void imcb_error( struct im_connection *ic, char *format, ... ) G
* - '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, void *doit, void *dont );
+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_add( struct im_connection *ic, char *handle, const char *realname );
/* Buddy management */
diff --git a/protocols/oscar/Makefile b/protocols/oscar/Makefile
index 95e85ec2..2792f22a 100644
--- a/protocols/oscar/Makefile
+++ b/protocols/oscar/Makefile
@@ -17,7 +17,7 @@ LFLAGS += -r
# [SH] Phony targets
all: oscar_mod.o
check: all
-lcov:
+lcov: check
gcov:
gcov *.c
diff --git a/protocols/oscar/oscar.c b/protocols/oscar/oscar.c
index 9e5de70a..7738c31f 100644
--- a/protocols/oscar/oscar.c
+++ b/protocols/oscar/oscar.c
@@ -1083,8 +1083,8 @@ static int incomingim_chan1(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_
return 1;
}
-void oscar_accept_chat(gpointer w, struct aim_chat_invitation * inv);
-void oscar_reject_chat(gpointer w, struct aim_chat_invitation * inv);
+void oscar_accept_chat(void *data);
+void oscar_reject_chat(void *data);
static int incomingim_chan2(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_t *userinfo, struct aim_incomingim_ch2_args *args) {
struct im_connection *ic = sess->aux_data;
@@ -1118,7 +1118,8 @@ static int incomingim_chan2(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_
return 1;
}
-static void gaim_icq_authgrant(gpointer w, struct icq_auth *data) {
+static void gaim_icq_authgrant(void *data_) {
+ struct icq_auth *data = data_;
char *uin, message;
struct oscar_data *od = (struct oscar_data *)data->ic->proto_data;
@@ -1133,7 +1134,8 @@ static void gaim_icq_authgrant(gpointer w, struct icq_auth *data) {
g_free(data);
}
-static void gaim_icq_authdeny(gpointer w, struct icq_auth *data) {
+static void gaim_icq_authdeny(void *data_) {
+ struct icq_auth *data = data_;
char *uin, *message;
struct oscar_data *od = (struct oscar_data *)data->ic->proto_data;
@@ -2587,15 +2589,19 @@ struct groupchat *oscar_chat_with(struct im_connection * ic, char *who)
return NULL;
}
-void oscar_accept_chat(gpointer w, struct aim_chat_invitation * inv)
+void oscar_accept_chat(void *data)
{
+ struct aim_chat_invitation * inv = data;
+
oscar_chat_join(inv->ic, inv->name, NULL, NULL);
g_free(inv->name);
g_free(inv);
}
-void oscar_reject_chat(gpointer w, struct aim_chat_invitation * inv)
+void oscar_reject_chat(void *data)
{
+ struct aim_chat_invitation * inv = data;
+
g_free(inv->name);
g_free(inv);
}
diff --git a/protocols/yahoo/Makefile b/protocols/yahoo/Makefile
index 2cfd147b..b4fe56e2 100644
--- a/protocols/yahoo/Makefile
+++ b/protocols/yahoo/Makefile
@@ -17,7 +17,7 @@ LFLAGS += -r
# [SH] Phony targets
all: yahoo_mod.o
check: all
-lcov:
+lcov: check
gcov:
gcov *.c
diff --git a/protocols/yahoo/libyahoo2.c b/protocols/yahoo/libyahoo2.c
index ce38bc73..80d88a85 100644
--- a/protocols/yahoo/libyahoo2.c
+++ b/protocols/yahoo/libyahoo2.c
@@ -736,7 +736,7 @@ static void yahoo_send_packet(struct yahoo_input_data *yid, struct yahoo_packet
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, 0x000c);
pos += yahoo_put16(data + pos, 0x0000);
pos += yahoo_put16(data + pos, pktlen + extra_pad);
pos += yahoo_put16(data + pos, pkt->service);
@@ -3350,7 +3350,7 @@ static void yahoo_process_search_connection(struct yahoo_input_data *yid, int ov
yct->age = atoi(cp);
break;
case 5:
- if(cp != "\005")
+ if(strcmp(cp, "5") != 0)
yct->location = cp;
k = 0;
break;
diff --git a/protocols/yahoo/yahoo.c b/protocols/yahoo/yahoo.c
index 9f9ffcf7..ab30df4d 100644
--- a/protocols/yahoo/yahoo.c
+++ b/protocols/yahoo/yahoo.c
@@ -314,7 +314,7 @@ static void byahoo_chat_invite( struct groupchat *c, char *who, char *msg )
{
struct byahoo_data *yd = (struct byahoo_data *) c->ic->proto_data;
- yahoo_conference_invite( yd->y2_id, NULL, c->data, c->title, msg );
+ yahoo_conference_invite( yd->y2_id, NULL, c->data, c->title, msg ? msg : "" );
}
static void byahoo_chat_leave( struct groupchat *c )
@@ -796,16 +796,20 @@ int ext_yahoo_connect(const char *host, int port)
return -1;
}
-static void byahoo_accept_conf( gpointer w, struct byahoo_conf_invitation *inv )
+static void byahoo_accept_conf( void *data )
{
+ struct byahoo_conf_invitation *inv = data;
+
yahoo_conference_logon( inv->yid, NULL, inv->members, inv->name );
imcb_chat_add_buddy( inv->c, inv->ic->acc->user );
g_free( inv->name );
g_free( inv );
}
-static void byahoo_reject_conf( gpointer w, struct byahoo_conf_invitation *inv )
+static void byahoo_reject_conf( void *data )
{
+ struct byahoo_conf_invitation *inv = data;
+
yahoo_conference_decline( inv->yid, NULL, inv->members, inv->name, "User rejected groupchat" );
imcb_chat_free( inv->c );
g_free( inv->name );
diff --git a/query.c b/query.c
index 99be2bee..e8f69572 100644
--- a/query.c
+++ b/query.c
@@ -29,7 +29,8 @@
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, void *yes, void *no, void *data )
+query_t *query_add( irc_t *irc, struct im_connection *ic, char *question,
+ query_callback yes, query_callback no, void *data )
{
query_t *q = g_new0( query_t, 1 );
@@ -139,13 +140,19 @@ void query_answer( irc_t *irc, query_t *q, int ans )
}
if( ans )
{
- imcb_log( q->ic, "Accepted: %s", q->question );
- q->yes( NULL, q->data );
+ if( q->ic )
+ imcb_log( q->ic, "Accepted: %s", q->question );
+ else
+ irc_usermsg( irc, "Accepted: %s", q->question );
+ q->yes( q->data );
}
else
{
- imcb_log( q->ic, "Rejected: %s", q->question );
- q->no( NULL, q->data );
+ if( q->ic )
+ imcb_log( q->ic, "Rejected: %s", q->question );
+ else
+ irc_usermsg( irc, "Rejected: %s", q->question );
+ q->no( q->data );
}
q->data = NULL;
diff --git a/query.h b/query.h
index b64642c2..e0ca32ec 100644
--- a/query.h
+++ b/query.h
@@ -26,17 +26,19 @@
#ifndef _QUERY_H
#define _QUERY_H
+typedef void (*query_callback) ( void *data );
+
typedef struct query
{
struct im_connection *ic;
char *question;
- void (* yes) ( gpointer w, void *data );
- void (* no) ( gpointer w, void *data );
+ query_callback yes, no;
void *data;
struct query *next;
} query_t;
-query_t *query_add( irc_t *irc, struct im_connection *ic, char *question, void *yes, void *no, void *data );
+query_t *query_add( irc_t *irc, struct im_connection *ic, char *question,
+ query_callback yes, query_callback no, 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 );
diff --git a/root_commands.c b/root_commands.c
index 30b01b4a..9dfbc998 100644
--- a/root_commands.c
+++ b/root_commands.c
@@ -203,6 +203,40 @@ static void cmd_drop( irc_t *irc, char **cmd )
}
}
+struct cmd_account_del_data
+{
+ account_t *a;
+ irc_t *irc;
+};
+
+void cmd_account_del_yes( void *data )
+{
+ struct cmd_account_del_data *cad = data;
+ account_t *a;
+
+ for( a = cad->irc->accounts; a && a != cad->a; a = a->next );
+
+ if( a == NULL )
+ {
+ irc_usermsg( cad->irc, "Account already deleted" );
+ }
+ else if( a->ic )
+ {
+ irc_usermsg( cad->irc, "Account is still logged in, can't delete" );
+ }
+ else
+ {
+ account_del( cad->irc, a );
+ irc_usermsg( cad->irc, "Account deleted" );
+ }
+ g_free( data );
+}
+
+void cmd_account_del_no( void *data )
+{
+ g_free( data );
+}
+
static void cmd_account( irc_t *irc, char **cmd )
{
account_t *a;
@@ -257,8 +291,20 @@ static void cmd_account( irc_t *irc, char **cmd )
}
else
{
- account_del( irc, a );
- irc_usermsg( irc, "Account deleted" );
+ struct cmd_account_del_data *cad;
+ char *msg;
+
+ cad = g_malloc( sizeof( struct cmd_account_del_data ) );
+ cad->a = a;
+ cad->irc = irc;
+
+ msg = g_strdup_printf( "If you remove this account (%s(%s)), BitlBee will "
+ "also forget all your saved nicknames. If you want "
+ "to change your username/password, use the `account "
+ "set' command. Are you sure you want to delete this "
+ "account?", a->prpl->name, a->user );
+ query_add( irc, NULL, msg, cmd_account_del_yes, cmd_account_del_no, cad );
+ g_free( msg );
}
}
else if( g_strcasecmp( cmd[1], "list" ) == 0 )
@@ -556,6 +602,9 @@ static void cmd_rename( irc_t *irc, char **cmd )
{
g_free( irc->mynick );
irc->mynick = g_strdup( cmd[2] );
+
+ if( strcmp( cmd[0], "set_rename" ) != 0 )
+ set_setstr( &irc->set, "root_nick", cmd[2] );
}
else if( u->send_handler == buddy_send_handler )
{
@@ -566,6 +615,20 @@ static void cmd_rename( irc_t *irc, char **cmd )
}
}
+char *set_eval_root_nick( set_t *set, char *new_nick )
+{
+ irc_t *irc = set->data;
+
+ if( strcmp( irc->mynick, new_nick ) != 0 )
+ {
+ char *cmd[] = { "set_rename", irc->mynick, new_nick, NULL };
+
+ cmd_rename( irc, cmd );
+ }
+
+ return strcmp( irc->mynick, new_nick ) == 0 ? new_nick : NULL;
+}
+
static void cmd_remove( irc_t *irc, char **cmd )
{
user_t *u;
@@ -768,6 +831,9 @@ static void cmd_set( irc_t *irc, char **cmd )
irc_usermsg( irc, "%s = `%s'", set_name, s );
else
irc_usermsg( irc, "%s is empty", set_name );
+
+ if( strchr( set_name, '/' ) )
+ irc_usermsg( irc, "Warning: / found in setting name, you're probably looking for the `account set' command." );
}
else
{
diff --git a/set.c b/set.c
index 6f09843b..112e6937 100644
--- a/set.c
+++ b/set.c
@@ -229,18 +229,3 @@ char *set_eval_ops( set_t *set, char *value )
return value;
}
-
-char *set_eval_charset( set_t *set, char *value )
-{
- GIConv cd;
-
- if ( g_strncasecmp( value, "none", 4 ) == 0 )
- return value;
-
- cd = g_iconv_open( "UTF-8", value );
- if( cd == (GIConv) -1 )
- return NULL;
-
- g_iconv_close( cd );
- return value;
-}
diff --git a/set.h b/set.h
index 7dcbb869..8c722877 100644
--- a/set.h
+++ b/set.h
@@ -96,6 +96,5 @@ char *set_eval_bool( 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_ops( set_t *set, char *value );
-char *set_eval_charset( set_t *set, char *value );
#endif /* __SET_H__ */
diff --git a/storage_xml.c b/storage_xml.c
index 19070a74..f37fce44 100644
--- a/storage_xml.c
+++ b/storage_xml.c
@@ -79,49 +79,30 @@ static void xml_start_element( GMarkupParseContext *ctx, const gchar *element_na
{
char *nick = xml_attr( attr_names, attr_values, "nick" );
char *pass = xml_attr( attr_names, attr_values, "password" );
- md5_byte_t *pass_dec = NULL;
+ int st;
if( !nick || !pass )
{
g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
"Missing attributes for %s element", element_name );
}
- else if( base64_decode( pass, &pass_dec ) != 21 )
+ else if( ( st = md5_verify_password( xd->given_pass, pass ) ) == -1 )
{
+ xd->pass_st = XML_PASS_WRONG;
g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
"Error while decoding password attribute" );
}
+ else if( st == 0 )
+ {
+ if( xd->pass_st != XML_PASS_CHECK_ONLY )
+ xd->pass_st = XML_PASS_OK;
+ }
else
{
- md5_byte_t pass_md5[16];
- md5_state_t md5_state;
- int i;
-
- md5_init( &md5_state );
- md5_append( &md5_state, (md5_byte_t*) xd->given_pass, strlen( xd->given_pass ) );
- md5_append( &md5_state, (md5_byte_t*) pass_dec + 16, 5 ); /* Hmmm, salt! */
- md5_finish( &md5_state, pass_md5 );
-
- for( i = 0; i < 16; i ++ )
- {
- if( pass_dec[i] != pass_md5[i] )
- {
- xd->pass_st = XML_PASS_WRONG;
- g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
- "Password mismatch" );
- break;
- }
- }
-
- /* If we reached the end of the loop, it was a match! */
- if( i == 16 )
- {
- if( xd->pass_st != XML_PASS_CHECK_ONLY )
- xd->pass_st = XML_PASS_OK;
- }
+ xd->pass_st = XML_PASS_WRONG;
+ g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+ "Password mismatch" );
}
-
- g_free( pass_dec );
}
else if( xd->pass_st < XML_PASS_OK )
{
@@ -427,7 +408,7 @@ static storage_status_t xml_save( irc_t *irc, int overwrite )
char *pass_b64;
int pass_len;
- pass_len = arc_encode( acc->pass, strlen( acc->pass ), (unsigned char**) &pass_cr, irc->password );
+ 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 );
diff --git a/tests/Makefile b/tests/Makefile
index 5bc3fbde..ae76fef5 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -10,9 +10,9 @@ clean:
distclean: clean
-main_objs = account.o bitlbee.o conf.o crypting.o help.o ipc.o irc.o irc_commands.o log.o nick.o query.o root_commands.o set.o storage.o storage_xml.o storage_text.o user.o
+main_objs = account.o bitlbee.o conf.o crypting.o help.o ipc.o irc.o irc_commands.o log.o nick.o query.o root_commands.o set.o storage.o storage_xml.o storage_text.o user.o
-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_crypting.o check_set.o
+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_crypting.o check_set.o check_jabber_sasl.o
check: $(test_objs) $(addprefix ../, $(main_objs)) ../protocols/protocols.o ../lib/lib.o
@echo '*' Linking $@
diff --git a/tests/check.c b/tests/check.c
index 043889d6..b3ffb957 100644
--- a/tests/check.c
+++ b/tests/check.c
@@ -65,6 +65,9 @@ Suite *crypting_suite(void);
/* From check_set.c */
Suite *set_suite(void);
+/* From check_jabber_sasl.c */
+Suite *jabber_sasl_suite(void);
+
int main (int argc, char **argv)
{
int nf;
@@ -110,6 +113,7 @@ int main (int argc, char **argv)
srunner_add_suite(sr, user_suite());
srunner_add_suite(sr, crypting_suite());
srunner_add_suite(sr, set_suite());
+ srunner_add_suite(sr, jabber_sasl_suite());
if (no_fork)
srunner_set_fork_status(sr, CK_NOFORK);
srunner_run_all (sr, verbose?CK_VERBOSE:CK_NORMAL);
diff --git a/tests/check_arc.c b/tests/check_arc.c
index 989a0a66..9d913dcd 100644
--- a/tests/check_arc.c
+++ b/tests/check_arc.c
@@ -6,13 +6,14 @@
#include <stdio.h>
#include "arc.h"
-char *password = "TotT";
+char *password = "ArcVier";
char *clear_tests[] =
{
"Wie dit leest is gek :-)",
"ItllBeBitlBee",
"One more boring password",
+ "Hoi hoi",
NULL
};
@@ -27,7 +28,7 @@ static void check_codec(int l)
char *decrypted;
int len;
- len = arc_encode( clear_tests[i], 0, &crypted, password );
+ 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,
@@ -40,35 +41,44 @@ static void check_codec(int l)
struct
{
- unsigned char crypted[24];
+ unsigned char crypted[30];
int len;
char *decrypted;
} decrypt_tests[] = {
+ /* One block with padding. */
{
{
- 0xc3, 0x0d, 0x43, 0xc3, 0xee, 0x80, 0xe2, 0x8c, 0x0b, 0x29, 0x32, 0x7e,
- 0x38, 0x05, 0x82, 0x10, 0x21, 0x1c, 0x4a, 0x00, 0x2c
- }, 21, "Debugging sucks"
+ 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. */
{
{
- 0xb0, 0x00, 0x57, 0x0d, 0x0d, 0x0d, 0x70, 0xe1, 0xc0, 0x00, 0xa4, 0x25,
- 0x7d, 0xbe, 0x03, 0xcc, 0x24, 0xd1, 0x0c
- }, 19, "Testing rocks"
+ 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. */
{
{
- 0xb6, 0x92, 0x59, 0xe4, 0xf9, 0xc1, 0x7a, 0xf6, 0xf3, 0x18, 0xea, 0x28,
- 0x73, 0x6d, 0xb3, 0x0a, 0x6f, 0x0a, 0x2b, 0x43, 0x57, 0xe9, 0x3e, 0x63
- }, 24, "OSCAR is creepy..."
- }
+ 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 }
};
static void check_decod(int l)
{
int i;
- for( i = 0; clear_tests[i]; i++ )
+ for( i = 0; decrypt_tests[i].len; i++ )
{
tcase_fn_start (decrypt_tests[i].decrypted, __FILE__, __LINE__);
char *decrypted;
@@ -78,7 +88,7 @@ static void check_decod(int l)
&decrypted, password );
fail_if( strcmp( decrypt_tests[i].decrypted, decrypted ) != 0,
- "%s didn't decrypt properly", clear_tests[i] );
+ "`%s' didn't decrypt properly", decrypt_tests[i].decrypted );
g_free( decrypted );
}
diff --git a/tests/check_help.c b/tests/check_help.c
index 7e5283e3..5a2f28d9 100644
--- a/tests/check_help.c
+++ b/tests/check_help.c
@@ -6,11 +6,14 @@
#include <stdio.h>
#include "help.h"
-START_TEST(test_help_none)
+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)
@@ -24,7 +27,7 @@ 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_none);
+ tcase_add_test (tc_core, test_help_initfree);
tcase_add_test (tc_core, test_help_nonexistent);
return s;
}
diff --git a/tests/check_irc.c b/tests/check_irc.c
index c1cf05a5..66fe0021 100644
--- a/tests/check_irc.c
+++ b/tests/check_irc.c
@@ -36,8 +36,8 @@ START_TEST(test_login)
irc = irc_new(g_io_channel_unix_get_fd(ch1));
- fail_unless(g_io_channel_write_chars(ch2, "NICK bla\r\n"
- "USER a a a a\r\n", -1, NULL, NULL) == G_IO_STATUS_NORMAL);
+ 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);
g_main_iteration(FALSE);
diff --git a/tests/check_jabber_sasl.c b/tests/check_jabber_sasl.c
new file mode 100644
index 00000000..6bceeb88
--- /dev/null
+++ b/tests/check_jabber_sasl.c
@@ -0,0 +1,117 @@
+#include <stdlib.h>
+#include <glib.h>
+#include <gmodule.h>
+#include <check.h>
+#include <string.h>
+#include <stdio.h>
+#include "arc.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 }
+};
+
+static void check_get_part(int l)
+{
+ int i;
+
+ for( i = 0; get_part_tests[i].key; i++ )
+ {
+ tcase_fn_start( get_part_tests[i].key, __FILE__, i );
+ char *res;
+ int len;
+
+ 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 );
+ }
+}
+
+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;
+}