Index: Makefile
===================================================================
--- Makefile	(revision a312b6bcbc6aa836850d94fc2abc70ceffe275cd)
+++ Makefile	(revision 85e9644fe03cef7b83f3b3200943364e4eecaf14)
@@ -13,4 +13,6 @@
 headers = account.h bitlbee.h commands.h conf.h config.h crypting.h help.h ini.h ipc.h irc.h log.h nick.h query.h set.h sock.h storage.h url.h user.h protocols/http_client.h protocols/md5.h protocols/nogaim.h protocols/proxy.h protocols/sha.h protocols/ssl_client.h
 subdirs = protocols
+
+objects += $(LDAP_OBJ)
 
 # Expansion of variables
Index: configure
===================================================================
--- configure	(revision a312b6bcbc6aa836850d94fc2abc70ceffe275cd)
+++ configure	(revision 85e9644fe03cef7b83f3b3200943364e4eecaf14)
@@ -31,4 +31,5 @@
 
 events=glib
+ldap=auto
 ssl=auto
 
@@ -66,4 +67,6 @@
 
 --ipv6=0/1	IPv6 socket support			$ipv6
+
+--ldap=0/1/auto	LDAP support				$ldap
 
 --events=...	Event handler (glib, libevent)		$events
@@ -146,13 +149,15 @@
 
 if [ -n "$CC" ]; then
-	echo "CC=$CC" >> Makefile.settings;
+	CC=$CC
 elif type gcc > /dev/null 2> /dev/null; then
-	echo "CC=gcc" >> Makefile.settings;
+	CC=gcc
 elif type cc > /dev/null 2> /dev/null; then
-	echo "CC=cc" >> Makefile.settings;
+	CC=cc
 else
 	echo 'Cannot find a C compiler, aborting.'
 	exit 1;
 fi
+
+echo "CC=$CC" >> Makefile.settings;
 
 if [ -n "$LD" ]; then
@@ -230,4 +235,21 @@
 		ret=0;
 	fi;
+}
+
+detect_ldap()
+{
+	TMPFILE=`mktemp`
+	if $CC -o $TMPFILE -shared -lldap 2>/dev/null >/dev/null; then
+		cat<<EOF>>Makefile.settings
+EFLAGS+=-lldap
+CFLAGS+=
+EOF
+		ldap=1
+		rm -f $TMPFILE
+		ret=1
+	else
+		ldap=0
+		ret=0
+	fi
 }
 
@@ -292,4 +314,16 @@
 fi
 
+if [ "$ldap" = "auto" ]; then
+	detect_ldap
+fi
+
+if [ "$ldap" = 0 ]; then
+	echo "LDAP_OBJ=\# no ldap" >> Makefile.settings
+	echo "#undef WITH_LDAP" >> config.h
+elif [ "$ldap" = 1 ]; then
+	echo "#define WITH_LDAP 1" >> config.h
+	echo "LDAP_OBJ=storage_ldap.o" >> Makefile.settings
+fi
+
 if [ "$strip" = 0 ]; then
 	echo "STRIP=\# skip strip" >> Makefile.settings;
@@ -444,2 +478,8 @@
 	echo '  Building without IM-protocol support. We wish you a lot of fun...';
 fi
+
+if [ "$ldap" = "0" ]; then
+	echo "  LDAP storage backend disabled."
+else
+	echo "  LDAP storage backend enabled."
+fi
Index: doc/bitlbee.schema
===================================================================
--- doc/bitlbee.schema	(revision 85e9644fe03cef7b83f3b3200943364e4eecaf14)
+++ doc/bitlbee.schema	(revision 85e9644fe03cef7b83f3b3200943364e4eecaf14)
@@ -0,0 +1,62 @@
+## LDAP Schema file for BitlBee
+## Copyright (C) 2006 Jelmer Vernooij <jelmer@samba.org>
+##
+## We need the following object classes and related attributes:
+##
+## bitlBeeBuddy:
+##  - nick
+##  - handle
+
+## each bitlBeeNick has zero or more bitlBeeAccount subentries
+## and bitlBeeAccount entries contain zero or more bitlBeeBuddy entries
+
+## The admin needs to setup the LDAP server to:
+## - allow anonymous users to auth against bitlBeeNick objects on the 
+##   password field
+## - allow anonymous users to create new objects that start with nick=
+## - allow read/write for a user that is authenticated only to his/her own 
+##   object and subentries
+
+##  - userid 
+##  - userPassword
+##  - setting (multiple values)
+##  depends: top, account
+
+attributetype ( 1.3.6.1.4.1.25873.2.1.1 NAME 'bitlBeeAutoConnect'
+	DESC 'Autoconnect setting'
+	EQUALITY booleanMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.25873.2.1.2 NAME 'bitlBeeAccountNo'
+	DESC 'Account number'
+	EQUALITY integerMatch
+	SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
+
+objectclass ( 1.3.6.1.4.1.25873.2.2.3 NAME 'bitlBeeAccount' SUP account STRUCTURAL
+	DESC 'BitlBee User Account '
+	MUST ( userid, userPassword )
+	MAY ( ) )
+
+## bitlBeeAccount:
+##  - accountNo 1.3.6.1.4.1.1466.115.121.1.27
+##  - protocol (msn, oscar, jabber, yahoo, ...)
+##  - username
+##  - password
+##  - server name
+##  - autoconnect (true/false) 1.3.6.1.4.1.1466.115.121.1.7
+##  depends: top
+
+objectclass ( 1.3.6.1.4.1.25873.2.2.1 NAME 'bitlBeeIMAccount' SUP account STRUCTURAL
+	DESC 'BitlBee IM Account '
+	MUST ( bitlBeeAccountNo, userid, userPassword )
+	MAY ( host, bitlBeeAutoconnect ) )
+
+objectclass ( 1.3.6.1.4.1.25873.2.2.2 NAME 'bitlBeeSetting' SUP top STRUCTURAL
+	DESC 'BitlBee Configuration Setting'
+	MUST ( bitlBeeSettingName )
+	MAY ( bitlBeeSettingValue ) )
+
+objectclass ( 1.3.6.1.4.1.25873.2.2.3 NAME 'bitlBeeBuddy' SUP top STRUCTURAL
+	DESC 'BitlBee Nick Mapping'
+	MUST ( bitlBeeBuddyHandle )
+	MAY ( ircNick ) )
Index: irc.c
===================================================================
--- irc.c	(revision a312b6bcbc6aa836850d94fc2abc70ceffe275cd)
+++ irc.c	(revision 85e9644fe03cef7b83f3b3200943364e4eecaf14)
@@ -905,7 +905,29 @@
 void irc_kill( irc_t *irc, user_t *u )
 {
-	char *nick;
-	
-	irc_write( irc, ":%s!%s@%s QUIT :%s", u->nick, u->user, u->host, "Leaving..." );
+	char *nick, *s;
+	char reason[64];
+	
+	if( u->gc && u->gc->flags & OPT_LOGGING_OUT )
+	{
+		if( u->gc->user->proto_opt[0][0] )
+			g_snprintf( reason, sizeof( reason ), "%s %s", irc->myhost,
+			            u->gc->user->proto_opt[0] );
+		else if( ( s = strchr( u->gc->username, '@' ) ) )
+			g_snprintf( reason, sizeof( reason ), "%s %s", irc->myhost,
+			            s + 1 );
+		else
+			g_snprintf( reason, sizeof( reason ), "%s %s.%s", irc->myhost,
+			            u->gc->prpl->name, irc->myhost );
+		
+		/* proto_opt might contain garbage after the : */
+		if( ( s = strchr( reason, ':' ) ) )
+			*s = 0;
+	}
+	else
+	{
+		strcpy( reason, "Leaving..." );
+	}
+	
+	irc_write( irc, ":%s!%s@%s QUIT :%s", u->nick, u->user, u->host, reason );
 	
 	nick = g_strdup( u->nick );
Index: protocols/nogaim.c
===================================================================
--- protocols/nogaim.c	(revision a312b6bcbc6aa836850d94fc2abc70ceffe275cd)
+++ protocols/nogaim.c	(revision 85e9644fe03cef7b83f3b3200943364e4eecaf14)
@@ -294,5 +294,6 @@
 	
 	serv_got_crap( gc, "Signing off.." );
-
+	gc->flags |= OPT_LOGGING_OUT;
+	
 	b_event_remove( gc->keepalive );
 	gc->keepalive = 0;
Index: protocols/nogaim.h
===================================================================
--- protocols/nogaim.h	(revision a312b6bcbc6aa836850d94fc2abc70ceffe275cd)
+++ protocols/nogaim.h	(revision 85e9644fe03cef7b83f3b3200943364e4eecaf14)
@@ -54,7 +54,9 @@
 #define WEBSITE "http://www.bitlbee.org/"
 #define IM_FLAG_AWAY 0x0020
-#define OPT_CONN_HTML 0x00000001
-#define OPT_LOGGED_IN 0x00010000
 #define GAIM_AWAY_CUSTOM "Custom"
+
+#define OPT_CONN_HTML   0x00000001
+#define OPT_LOGGED_IN   0x00010000
+#define OPT_LOGGING_OUT 0x00020000
 
 /* ok. now the fun begins. first we create a connection structure */
Index: storage.c
===================================================================
--- storage.c	(revision a312b6bcbc6aa836850d94fc2abc70ceffe275cd)
+++ storage.c	(revision 85e9644fe03cef7b83f3b3200943364e4eecaf14)
@@ -6,4 +6,6 @@
 
 /* Support for multiple storage backends */
+
+/* Copyright (C) 2005 Jelmer Vernooij <jelmer@samba.org> */
 
 /*
Index: storage_ldap.c
===================================================================
--- storage_ldap.c	(revision 85e9644fe03cef7b83f3b3200943364e4eecaf14)
+++ storage_ldap.c	(revision 85e9644fe03cef7b83f3b3200943364e4eecaf14)
@@ -0,0 +1,177 @@
+  /********************************************************************\
+  * BitlBee -- An IRC to other IM-networks gateway                     *
+  *                                                                    *
+  * Copyright 2002-2004 Wilmer van der Gaast and others                *
+  \********************************************************************/
+
+/* Storage backend that uses a LDAP database */
+
+/* Copyright (C) 2006 Jelmer Vernooij <jelmer@samba.org> */
+
+/*
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License with
+  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+  Suite 330, Boston, MA  02111-1307  USA
+*/
+
+#define BITLBEE_CORE
+#include "bitlbee.h"
+#include <ldap.h>
+
+#define BB_LDAP_HOST "localhost"
+#define BB_LDAP_BASE ""
+
+static char *nick_dn(const char *nick)
+{
+	return g_strdup_printf("bitlBeeNick=%s%s%s", nick, BB_LDAP_BASE?",":"", BB_LDAP_BASE?BB_LDAP_BASE:"");
+}
+
+static storage_status_t nick_connect(const char *nick, const char *password, LDAP **ld)
+{
+	char *mydn;
+	int ret;
+	storage_status_t status;
+	*ld = ldap_init(BB_LDAP_HOST, LDAP_PORT);
+
+	if (!ld) {
+		log_message( LOGLVL_WARNING, "Unable to connect to LDAP server at %s", BB_LDAP_HOST );
+		return STORAGE_OTHER_ERROR;
+	}
+
+	mydn = nick_dn(nick);
+
+	ret = ldap_simple_bind_s(*ld, mydn, password);
+
+	switch (ret) {
+	 case LDAP_SUCCESS: status = STORAGE_OK; break;
+	 case LDAP_INVALID_CREDENTIALS: status = STORAGE_INVALID_PASSWORD; break;
+	 default: 
+		log_message( LOGLVL_WARNING, "Unable to authenticate %s: %s", mydn, ldap_err2string(ret) );
+		status = STORAGE_OTHER_ERROR;
+		break;
+	}
+
+	g_free(mydn);
+
+	return status;
+}
+
+static storage_status_t sldap_load ( const char *my_nick, const char* password, irc_t *irc )
+{
+	LDAPMessage *res, *msg;
+	LDAP *ld;
+	int ret, i;
+	storage_status_t status;
+	char *mydn; 
+
+	status = nick_connect(my_nick, password, &ld);
+	if (status != STORAGE_OK)
+		return status;
+
+	mydn = nick_dn(my_nick);
+
+	ret = ldap_search_s(ld, mydn, LDAP_SCOPE_BASE, "(objectClass=*)", NULL, 0, &res);
+
+	if (ret != LDAP_SUCCESS) {
+		log_message( LOGLVL_WARNING, "Unable to search for %s: %s", mydn, ldap_err2string(ret) );
+		ldap_unbind_s(ld);
+		return STORAGE_OTHER_ERROR;
+	}
+
+	g_free(mydn);
+
+	for (msg = ldap_first_entry(ld, res); msg; msg = ldap_next_entry(ld, msg)) {
+	}
+
+	/* FIXME: Store in irc_t */
+
+	ldap_unbind_s(ld);
+	
+	return STORAGE_OK;
+}
+
+static storage_status_t sldap_check_pass( const char *nick, const char *password )
+{
+	LDAP *ld;
+	storage_status_t status;
+
+	status = nick_connect(nick, password, &ld);
+
+	ldap_unbind_s(ld);
+
+	return status;
+}
+
+static storage_status_t sldap_remove( const char *nick, const char *password )
+{
+	storage_status_t status;
+	LDAP *ld;
+	char *mydn;
+	int ret;
+	
+	status = nick_connect(nick, password, &ld);
+
+	if (status != STORAGE_OK)
+		return status;
+
+	mydn = nick_dn(nick);
+	
+	ret = ldap_delete(ld, mydn);
+
+	if (ret != LDAP_SUCCESS) {
+		log_message( LOGLVL_WARNING, "Error removing %s: %s", mydn, ldap_err2string(ret) );
+		ldap_unbind_s(ld);
+		return STORAGE_OTHER_ERROR;
+	}
+
+	ldap_unbind_s(ld);
+
+	g_free(mydn);
+	return STORAGE_OK;
+}
+
+static storage_status_t sldap_save( irc_t *irc, int overwrite )
+{
+	LDAP *ld;
+	char *mydn;
+	storage_status_t status;
+	LDAPMessage *msg;
+
+	status = nick_connect(irc->nick, irc->password, &ld);
+	if (status != STORAGE_OK)
+		return status;
+
+	mydn = nick_dn(irc->nick);
+
+	/* FIXME: Make this a bit more atomic? What if we crash after 
+	 * removing the old account but before adding the new one ? */
+	if (overwrite) 
+		sldap_remove(irc->nick, irc->password);
+
+	g_free(mydn);
+
+	ldap_unbind_s(ld);
+	
+	return STORAGE_OK;
+}
+
+
+
+storage_t storage_ldap = {
+	.name = "ldap",
+	.check_pass = sldap_check_pass,
+	.remove = sldap_remove,
+	.load = sldap_load,
+	.save = sldap_save
+};
