diff -urN bitlbee-0.92/Makefile bitlbee-0.92-crypto_storage/Makefile
--- bitlbee-0.92/Makefile	Sat Dec 25 21:53:27 2004
+++ bitlbee-0.92-crypto_storage/Makefile	Sat Sep  3 16:26:04 2005
@@ -9,7 +9,7 @@
 -include Makefile.settings

 # Program variables
-objects = account.o bitlbee.o commands.o conf.o crypting.o help.o ini.o irc.o log.o nick.o query.o set.o unix.o url.o user.o debug.o
+objects = account.o bitlbee.o bitlbee-openssl.o commands.o conf.o crypting.o help.o ini.o irc.o log.o nick.o query.o set.o unix.o url.o user.o debug.o
 subdirs = protocols

 # Expansion of variables
diff -urN bitlbee-0.92/bitlbee-openssl.c bitlbee-0.92-crypto_storage/bitlbee-openssl.c
--- bitlbee-0.92/bitlbee-openssl.c	Thu Jan  1 01:00:00 1970
+++ bitlbee-0.92-crypto_storage/bitlbee-openssl.c	Sun Sep  4 18:48:10 2005
@@ -0,0 +1,527 @@
+  /********************************************************************\
+  * BitlBee -- An IRC to other IM-networks gateway                     *
+  *                                                                    *
+  * Copyright 2002-2004 Wilmer van der Gaast and others                *
+  * Copyright 2005      Zeljko Vrba <zvrba@globalnet.hr>               *
+  \********************************************************************/
+
+/* Support for encrypted data files.                                  */
+
+/*
+  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
+
+/* someone was nasty and has chosen the same names OpenSSL uses. dirty trick
+ * to not include protocols/sha.h. better than messing with bitlbee.h. */
+#define __SHA_H__
+
+#include "bitlbee.h"
+#include "crypting.h"
+#include <syslog.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <time.h>
+#include <stdarg.h>
+
+/*
+ * The strong encryption stuff is intended to prevent the theft of 'cold data'.
+ * This is data not in active use, i.e. not loaded in memory in any process.
+ * The user is at mercy of the sysadmin, network sniffers and all other 'usual'
+ * stuff. The strength of protection ultimately depends on the user's password.
+ *
+ * The encryption scheme used is cast5-cbc with PKCS#5v2 password-based key
+ * derivation algorithm in 2^16 iterations.
+ */
+
+#ifdef HAVE_OPENSSL
+
+static int _openssl = 1;
+
+#include <openssl/asn1.h>
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+#include <openssl/err.h>
+
+/* in the similar vein as in OpenSSL headers */
+#define d2i_X509_ALGOR_bio(bp, algor) (X509_ALGOR*)ASN1_d2i_bio(\
+	(char *(*)())X509_ALGOR_new,  (char *(*)())d2i_X509_ALGOR,\
+	bp, (unsigned char**)algor)
+#define i2d_X509_ALGOR_bio(bp, algor) ASN1_i2d_bio(i2d_X509_ALGOR,\
+	bp, (unsigned char*)algor)
+
+static FILE *log_file = NULL;
+
+/* automatically places PID, username and \n in output */
+static void logmsg(const char *msg, ...)
+{
+	char timebuf[512];
+	time_t now_ticks = time(NULL);
+	struct tm *now = localtime(&now_ticks);
+	va_list ap;
+	
+	va_start(ap, msg);
+	strftime(timebuf, 512, "%Y-%m-%dT%T", now);
+	fprintf(log_file, "%s;%lu: ", timebuf, (unsigned long)getpid());
+	vfprintf(log_file, msg, ap);
+	fputc('\n', log_file);
+	va_end(ap);
+}
+
+static void cleanup_auth_subsystem(void)
+{
+	if(fclose(log_file) == EOF)
+		syslog(LOG_ERR | LOG_DAEMON, "ERROR: can't fclose log file: %s",
+			strerror(errno));
+}
+
+/* I don't want to use the GCC __attribute__((constructor)) extension so this
+ * is called at the start of both bitlbee_load and bitlbee_save. */
+static void init_auth_subsystem(irc_t *irc)
+{
+	char path[512];
+
+	if(log_file)
+		return;
+	umask(0177);
+
+	ERR_load_crypto_strings();
+	OpenSSL_add_all_algorithms();
+
+	g_snprintf(path, 511, "%s%s", global.conf->configdir, "error.log");
+	if(!(log_file = fopen(path, "a"))) {
+		syslog(LOG_ERR | LOG_DAEMON, "can't open log file %s: %m", path);
+		irc_usermsg(irc, "ERROR: insufficient system resources. Try again later.");
+		exit(1);
+	}
+	setbuf(log_file, NULL);	/* no buffering for log */
+	if(atexit(cleanup_auth_subsystem) < 0) {
+		syslog(LOG_ERR | LOG_DAEMON, "can't register atexit handler: %m");
+		irc_usermsg(irc, "ERROR: insufficient system resources. Try again later.");
+		exit(1);
+	}
+}
+
+/* Generate and return PKCS#5 parameters with a random salt. */
+static X509_ALGOR *generate_params(void)
+{
+	return PKCS5_pbe2_set(EVP_cast5_cbc(), 1U << 16, NULL, 0);
+}
+
+/*
+ * Load PKCS#5 parameters given an open parameters file. The file stores TWO
+ * parameters: 1st is for the .accounts file, 2nd is for the .nicks file.
+ */
+static int load_p5_params(BIO *b, X509_ALGOR **params)
+{
+	return
+		(params[0] = d2i_X509_ALGOR_bio(b, NULL)) &&
+		(params[1] = d2i_X509_ALGOR_bio(b, NULL));
+}
+
+/*
+ * This is tricky: a chain of BIOs needs to be constructed. The function
+ * returns NULL on failure, or usable BIO which should be freed with
+ * BIO_free_all. If the BIO is for writing, then it MUST be MANUALLY
+ * FLUSHED before freeing it.
+ */
+static BIO *open_crypt_file(
+	const char *fname,		/* file name */
+	const char *mode,		/* "r" -> read/decrypt ; "w" -> write/encrypt */
+	const char *password,	/* encryption password */
+	X509_ALGOR *params		/* encryption parameters. if NULL: no encryption */
+)
+{
+	BIO *buffer_bio = NULL, *cipher_bio = NULL, *file_bio;
+	EVP_CIPHER_CTX *ctx;
+	
+	/* usage check */
+	if(strcmp(mode, "r") && strcmp(mode, "w")) abort();
+
+	/* common: always open the file */
+	if(!(file_bio = BIO_new_file(fname, mode)))
+		goto err;
+
+	/* simple case: no encryption */
+	if(!params)
+		return file_bio;
+
+	/* encryption */
+	buffer_bio = BIO_new(BIO_f_buffer());
+	cipher_bio = BIO_new(BIO_f_cipher());
+	if(!buffer_bio || !cipher_bio)
+		goto err;
+
+	/* set context key based upon parameters */
+	if(!BIO_get_cipher_ctx(cipher_bio, &ctx))
+		goto err;
+	if(!PKCS5_v2_PBE_keyivgen(ctx, password, strlen(password),
+			params->parameter, NULL, NULL, mode[0] == 'w'))
+		goto err;
+		
+	/* the chain we're constructing: buffer -> cipher -> file */
+	BIO_push(cipher_bio, file_bio);
+	return BIO_push(buffer_bio, cipher_bio);
+
+err:
+	logmsg("open_crypt_file");
+	ERR_print_errors_fp(log_file);
+	if(buffer_bio) BIO_free(buffer_bio);
+	if(cipher_bio) BIO_free(cipher_bio);
+	if(file_bio) BIO_free(file_bio);
+	return NULL;
+}
+
+/*
+ * We use obfucrypt even in strongly-encrypted file. Just makes life simple
+ * and there is no difference in reading/writing plaintext or encrypted file.
+ * The whole thing is parametrized entirely through the BIO.
+ */
+static void load_accounts(BIO *bio, irc_t *irc, user_t *ru)
+{
+	char buf[512], *line;
+	int i;
+
+	while((i = BIO_gets(bio, buf, 511)) > 0) {
+#ifdef DEBUG_CRYPTO
+		irc_usermsg(buf);
+#endif
+		if(buf[i-1] != '\n') {
+			irc_usermsg(irc, "WARNING: loading accounts: corrupt file. Some accounts may be lost.");
+			logmsg("WARNING[%s]: loading accounts: corrupt file", irc->nick);
+		} else {
+			/* BIO_gets retains \n but it shouldn't get to deobfucrypt. */
+			buf[i-1] = 0;
+
+			line = deobfucrypt(irc, buf);
+			root_command_string( irc, ru, line );
+			g_free(line);
+		}
+	}
+}
+
+static void load_nicks(BIO *bio, irc_t *irc)
+{
+	int proto;
+	char buf[512], handle[512];
+	char nick[MAX_NICK_LENGTH+1];
+
+	while(BIO_gets(bio, buf, 511) > 0) {
+#ifdef DEBUG_CRYPTO
+		irc_usermsg(buf);
+#endif
+		if(sscanf(buf, "%s %d %s", handle, &proto, nick) != 3) {
+			irc_usermsg(irc, "WARNING: loading nicks: corrupt file. Some nicks may be lost.\n");
+			logmsg("WARNING[%s]: loading nicks: corrupt file", irc->nick);
+		} else {
+			http_decode(handle);
+			nick_set(irc, handle, proto, nick);
+		}
+	}
+}
+
+int bitlbee_load( irc_t *irc, char* password )
+{
+	char s[512];
+	BIO *bio;
+	X509_ALGOR *p5_params[2] = { NULL, NULL };	/* 0: accounts; 1: nicks */
+	int ret = 1;
+	user_t *ru = user_find( irc, ROOT_NICK );
+
+	init_auth_subsystem(irc);
+	
+	if( irc->status == USTATUS_IDENTIFIED )
+		return( 1 );
+
+#ifdef DEBUG_CRYPTO
+	irc_usermsg("DEBUG: displaying all information while loading.");
+#endif
+
+	g_snprintf( s, 511, "%s%s%s", global.conf->configdir, irc->nick, ".p5params" );
+   	if(!(bio = BIO_new_file(s, "r")) || !(load_p5_params(bio, p5_params))) {
+		/* either the file doesn't exist or parameters can't be read */
+		irc_usermsg(irc, "INFO: can't load encryption parameters; reverting to obfucrypt.");
+		logmsg("INFO[%s]: can't load encryption parameters", irc->nick);
+		ERR_print_errors_fp(log_file);
+		if(p5_params[0]) X509_ALGOR_free(p5_params[0]);
+		if(p5_params[1]) X509_ALGOR_free(p5_params[1]);
+		p5_params[0] = p5_params[1] = NULL;
+	}
+	BIO_free(bio); bio = NULL;
+	
+	g_snprintf(s, 511, "%s%s%s", global.conf->configdir, irc->nick, ".accounts");
+	if(!(bio = open_crypt_file(s, "r", password, p5_params[0]))) {
+		irc_usermsg(irc, "ERROR: can't open accounts file.");
+		logmsg("ERROR[%s]: can't open accounts file", irc->nick);
+		ret = 0; goto end;
+	}
+	if(BIO_read(bio, s, 32) !=32) {
+		irc_usermsg(irc, "ERROR: loading accounts: can't read hash.\n");
+		logmsg("ERROR[%s]: loading accounts: can't read hash", irc->nick);
+		ret = 0; goto end;
+	}
+	if(setpass(irc, password, s) < 0) {
+		/* XXX: return value is nowhere documented! */
+		logmsg("ERROR[%s]: invalid login attempt", irc->nick);
+		ret = -1; goto end;
+	}
+	
+	/* Do this now. If the user runs with AuthMode = Registered, the
+	   account command will not work otherwise. */
+	irc->status = USTATUS_IDENTIFIED;
+	load_accounts(bio, irc, ru);
+	BIO_free_all(bio); bio = NULL;
+	
+	g_snprintf(s, 511, "%s%s%s", global.conf->configdir, irc->nick, ".nicks");
+	if(!(bio = open_crypt_file(s, "r", password, p5_params[1]))) {
+		irc_usermsg(irc, "ERROR: can't open nicks file.");
+		logmsg("ERROR[%s]: can't open nicks file", irc->nick);
+		ret = 0; goto end;
+	}
+	load_nicks(bio, irc);
+	BIO_free_all(bio); bio = NULL;
+
+	if(set_getint(irc, "auto_connect")) {
+		/* Can't do this directly because r_c_s alters the string */
+		strcpy(s, "account on");
+		root_command_string(irc, ru, s);
+	}
+
+	/* no unwind-protect in C so I resort to simple goto... */
+end:
+	if(p5_params[0]) X509_ALGOR_free(p5_params[0]);
+	if(p5_params[1]) X509_ALGOR_free(p5_params[1]);
+	if(bio) BIO_free_all(bio);
+	return ret;
+}
+
+static int BIO_checked_puts(BIO *bio, irc_t *irc, const char *str, const char *func)
+{
+	if(BIO_puts(bio, str) < strlen(str)) {
+		irc_usermsg(irc, "ERROR: write failed in %s.\n", func);
+		logmsg("ERROR[%s]: write failed in %s", irc->nick, func);
+		ERR_print_errors_fp(log_file);
+		syslog(LOG_ERR | LOG_DAEMON, "ERROR[%s]: write failed in: %s; %m",
+			irc->nick, ERR_error_string(ERR_get_error(), NULL));
+		return 0;
+	}
+	return 1;
+}
+
+#define BIO_CHECKED_PUTS(bio, irc, str) BIO_checked_puts(bio, irc, str, __FUNCTION__)
+#define BIO_CHECKED_PUTS_NL(bio, irc, str) \
+	(BIO_checked_puts(bio, irc, str, __FUNCTION__) && \
+	BIO_checked_puts(bio, irc, "\n", __FUNCTION__))
+
+static int save_nicks(BIO *bio, irc_t *irc)
+{
+	char s[512];
+	nick_t *n;
+
+	for( n = irc->nicks; n; n = n->next )
+	{
+		strcpy( s, n->handle );
+		s[169] = 0; /* Prevent any overflow (169 ~ 512 / 3) */
+		http_encode( s );
+		g_snprintf( s + strlen( s ), 510 - strlen( s ), " %d %s", n->proto, n->nick );
+		if(!BIO_CHECKED_PUTS_NL(bio, irc, s))
+			return 0;
+	}
+	return 1;
+}
+
+static int save_accounts(BIO *bio, irc_t *irc)
+{
+	char s[512], *line;
+	set_t *set;
+	account_t *a;
+
+	for( a = irc->accounts; a; a = a->next )
+	{
+		if( a->protocol == PROTO_OSCAR || a->protocol == PROTO_ICQ || a->protocol == PROTO_TOC )
+			g_snprintf( s, sizeof( s ), "account add oscar \"%s\" \"%s\" %s", a->user, a->pass, a->server );
+		else
+			g_snprintf( s, sizeof( s ), "account add %s \"%s\" \"%s\" \"%s\"",
+			            proto_name[a->protocol], a->user, a->pass, a->server ? a->server : "" );
+		
+		line = obfucrypt( irc, s );
+		if(*line && !BIO_CHECKED_PUTS_NL(bio, irc, line)) {
+			g_free(line);
+			return 0;
+		}
+		g_free( line );
+	}
+	
+	for(set = irc->set; set; set = set->next) if(set->value && set->def) {
+		g_snprintf( s, sizeof( s ), "set %s \"%s\"", set->key, set->value );
+		line = obfucrypt( irc, s );
+		if(*line && !BIO_CHECKED_PUTS_NL(bio, irc, line)) {
+			g_free(line);
+			return 0;
+		}
+		g_free( line );
+	}
+	
+	line = NULL;
+	if(strcmp( irc->mynick, ROOT_NICK ) != 0) {
+		g_snprintf(s, sizeof( s ), "rename %s %s", ROOT_NICK, irc->mynick);
+		line = obfucrypt( irc, s );
+		if(*line && !BIO_CHECKED_PUTS_NL(bio, irc, line)) {
+			g_free(line);
+			return 0;
+		}
+	}
+	if(line)
+		g_free(line);
+
+	return 1;
+}
+
+static int mv(const char *oldname, const char *newname)
+{
+	if((unlink(newname) < 0) && (errno != ENOENT)) {
+		logmsg("ERROR: unlinking %s: %s", newname, strerror(errno));
+		return 0;
+	}
+	if(rename(oldname, newname) < 0) {
+		logmsg("ERROR: rename %s -> %s: %s", oldname, newname, strerror(errno));
+		return 0;
+	}
+	return 1;
+}
+
+#define RENAME(fileext) \
+	g_snprintf(path, 511, "%s%s%s", global.conf->configdir, irc->nick, fileext "~"); \
+	g_snprintf(new_path, 511, "%s%s%s", global.conf->configdir, irc->nick, fileext); \
+	if(!mv(path, new_path)) { \
+		irc_usermsg(irc, "ERROR: renaming new .p5params file"); \
+		ret = 0; rollback = 1; \
+	} else { \
+		changed = 1; \
+	}
+
+int bitlbee_save(irc_t *irc)
+{
+	char path[512], new_path[512];
+	char *hash;
+	BIO *bio;
+	int ret = 1;
+	int changed, rollback;
+	X509_ALGOR *p5_params[2];	/* 0: accounts; 1: nicks */
+	
+	/*\
+	 *  [SH] Nothing should be saved if no password is set, because the
+	 *  password is not set if it was wrong, or if one is not identified
+	 *  yet. This means that a malicious user could easily overwrite
+	 *  files owned by someone else:
+	 *  a Bad Thing, methinks
+	\*/
+
+	/* [WVG] No? Really? */
+
+	/*\
+	 *  [SH] Okay, okay, it wasn't really Wilmer who said that, it was
+	 *  me. I just thought it was funny.
+	\*/
+
+	init_auth_subsystem(irc);
+
+	hash = hashpass( irc );
+	if( hash == NULL )
+	{
+		irc_usermsg( irc, "Please register yourself if you want to save your settings." );
+		return( 0 );
+	}
+	
+	/* Always generate new set of parameters and write them to .p5params */
+	g_snprintf(path, 511, "%s%s%s", global.conf->configdir, irc->nick, ".p5params~");
+	p5_params[0] = generate_params();
+	p5_params[1] = generate_params();
+   	if(!p5_params[0] || !p5_params[1]	/* file open is deliberately after so */
+	|| !(bio = BIO_new_file(path, "w"))	/* that generate_params err can be caught */
+	|| !i2d_X509_ALGOR_bio(bio, p5_params[0])
+	|| !i2d_X509_ALGOR_bio(bio, p5_params[1])) {
+		irc_usermsg(irc, "ERROR: Can't write encryption parameters. Reverting to obfucrypt.");
+		logmsg("ERROR[%s]: Can't write encryption parameters", irc->nick);
+		ERR_print_errors_fp(log_file);
+
+		if(p5_params[0]) X509_ALGOR_free(p5_params[0]);
+		if(p5_params[1]) X509_ALGOR_free(p5_params[1]);
+		p5_params[0] = p5_params[1] = NULL;
+		unlink(path);	/* get rid of temp file */
+	}
+	if(bio) {
+		BIO_flush(bio);
+		BIO_free_all(bio); bio = NULL;
+	}
+
+	/* Write nicks */
+	g_snprintf(path, 511, "%s%s%s", global.conf->configdir, irc->nick, ".nicks~");
+	if(!(bio = open_crypt_file(path, "w", irc->password, p5_params[1]))
+	|| !save_nicks(bio, irc)) {
+		irc_usermsg(irc, "ERROR: Can't write new nicks file.");
+		logmsg("ERROR[%s]: Can't write new nicks", irc->nick);
+		ERR_print_errors_fp(log_file);
+
+		ret = 0; goto end;
+	}
+	BIO_flush(bio);
+	BIO_free_all(bio); bio = NULL;
+
+	/* Write accounts. */
+	g_snprintf(path, 511, "%s%s%s", global.conf->configdir, irc->nick, ".accounts~");
+	if(!(bio = open_crypt_file(path, "w", irc->password, p5_params[0]))
+	|| !BIO_CHECKED_PUTS(bio, irc, hash)
+	|| !save_accounts(bio, irc)) {
+		irc_usermsg(irc, "ERROR: Can't write new accounts file.");
+		logmsg("ERROR[%s]: Can't write new accounts file", irc->nick);
+		ERR_print_errors_fp(log_file);
+
+		ret = 0; goto end;
+	}
+	BIO_flush(bio);
+	BIO_free_all(bio); bio = NULL;
+	
+	/* perform all renaming at the end, after all new temp data files have
+	 * been written. unfortunately, this can't be done atomically, but at
+	 * least no data is lost if failure happens. all backup (~) files should
+	 * be copied manually over 'real' files in case of incosistency. */
+	changed = rollback = 0;
+	RENAME(".p5params")
+	RENAME(".accounts")
+	RENAME(".nicks")
+
+	if(changed && rollback) {
+		irc_usermsg(irc, "WARNING: Inconsistent data files have been created. Ask your sysadm to fix it.");
+		irc_usermsg(irc, "WARNING: You won't be able to log in again, until it is fixed.");
+		logmsg("WARNING[%s]: inconsistent data files have been created.", irc->nick);
+	}
+	
+end:
+	if(hash) g_free(hash);
+	if(p5_params[0]) X509_ALGOR_free(p5_params[0]);
+	if(p5_params[1]) X509_ALGOR_free(p5_params[1]);
+	if(bio) BIO_free_all(bio);
+	return ret;
+}
+
+#else
+
+static int _openssl = 0;
+
+#endif /* HAVE_OPENSSL */
diff -urN bitlbee-0.92/bitlbee.c bitlbee-0.92-crypto_storage/bitlbee.c
--- bitlbee-0.92/bitlbee.c	Wed Feb 23 16:48:31 2005
+++ bitlbee-0.92-crypto_storage/bitlbee.c	Fri Sep  2 20:29:52 2005
@@ -234,6 +234,8 @@
 	}
 }

+#ifndef HAVE_OPENSSL
+
 int bitlbee_load( irc_t *irc, char* password )
 {
 	char s[512];
@@ -440,6 +442,8 @@
 	
 	return( 1 );
 }
+
+#endif /* HAVE_OPENSSL */

 void bitlbee_shutdown( gpointer data )
 {
Binary files bitlbee-0.92/bitlbee.core and bitlbee-0.92-crypto_storage/bitlbee.core differ
diff -urN bitlbee-0.92/configure bitlbee-0.92-crypto_storage/configure
--- bitlbee-0.92/configure	Wed Feb 23 23:26:53 2005
+++ bitlbee-0.92-crypto_storage/configure	Fri Sep  2 20:29:03 2005
@@ -224,6 +224,7 @@
 		echo ' *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"'
 		
 		echo 'EFLAGS+=-lssl -lcrypto' >> Makefile.settings
+		echo 'CFLAGS+=-DHAVE_OPENSSL' >> Makefile.settings
 		
 		ret=1;
 	elif [ "$ssl" = "bogus" ]; then
