changeset 21909:6648cfa72842

merge of '72df99671f84834d934f83b25ce492f289d77063' and 'c00604416a0d01f2fbf75b5cc6b6d4f9371e7a67'
author Sadrul Habib Chowdhury <imadil@gmail.com>
date Fri, 21 Dec 2007 09:33:45 +0000
parents a64a7bc69f42 (current diff) f786e478e08b (diff)
children eb2e2585f917 6765b2b65d30
files
diffstat 10 files changed, 1400 insertions(+), 116 deletions(-) [+]
line wrap: on
line diff
--- a/AUTHORS	Fri Dec 21 09:32:30 2007 +0000
+++ b/AUTHORS	Fri Dec 21 09:33:45 2007 +0000
@@ -35,6 +35,7 @@
 Megan 'Cae' Schneider - support/QA
 Evan Schoenberg - Developer
 Kevin 'SimGuy' Stange - Developer & Webmaster
+Will 'resiak' Thompson - Developer
 Stu 'nosnilmot' Tomlinson - Developer
 Nathan 'faceprint' Walp - Developer
 
@@ -42,8 +43,8 @@
 -------------------
 Dennis 'EvilDennisR' Ristuccia
 Peter 'Fmoo' Ruibal
+Elliott 'QuLogic' Sales de Andrade
 Gabriel 'Nix' Schulhof
-Will 'resiak' Thompson
 
 Retired Developers:
 ------------------
--- a/doc/pidgin.1.in	Fri Dec 21 09:32:30 2007 +0000
+++ b/doc/pidgin.1.in	Fri Dec 21 09:33:45 2007 +0000
@@ -590,6 +590,8 @@
 .br
   Kevin 'SimGuy' Stange (developer and webmaster)
 .br
+  Will 'resiak' Thompson (developer)
+.br
   Stu 'nosnilmot' Tomlinson (developer)
 .br
   Nathan 'faceprint' Walp (developer)
@@ -602,9 +604,9 @@
 .br
   Peter 'fmoo' Ruibal
 .br
-  Gabriel 'Nix' Schulhof
+  Elliott 'QuLogic' Sales de Andrade
 .br
-  Will 'resiak' Thompson
+  Gabriel 'Nix' Schulhof
 .br
 
 
--- a/libpurple/cipher.c	Fri Dec 21 09:32:30 2007 +0000
+++ b/libpurple/cipher.c	Fri Dec 21 09:33:45 2007 +0000
@@ -64,6 +64,8 @@
 /*******************************************************************************
  * MD5
  ******************************************************************************/
+#define MD5_HMAC_BLOCK_SIZE	64
+
 struct MD5Context {
 	guint32 total[2];
 	guint32 state[4];
@@ -325,6 +327,13 @@
 	return TRUE;
 }
 
+static size_t
+md5_get_block_size(PurpleCipherContext *context)
+{
+	/* This does not change (in this case) */
+	return MD5_HMAC_BLOCK_SIZE;
+}
+
 static PurpleCipherOps MD5Ops = {
 	NULL,			/* Set option */
 	NULL,			/* Get option */
@@ -340,12 +349,10 @@
 	NULL,			/* get salt size */
 	NULL,			/* set key */
 	NULL,			/* get key size */
-
-	/* padding */
-	NULL,
-	NULL,
-	NULL,
-	NULL
+	NULL,			/* set batch mode */
+	NULL,			/* get batch mode */
+	md5_get_block_size,	/* get block size */
+	NULL			/* set key with len */
 };
 
 /*******************************************************************************
@@ -580,6 +587,13 @@
 	md4_context = NULL;
 }
 
+static size_t
+md4_get_block_size(PurpleCipherContext *context)
+{
+	/* This does not change (in this case) */
+	return MD4_HMAC_BLOCK_SIZE;
+}
+
 static PurpleCipherOps MD4Ops = {
 	NULL,                   /* Set option */
 	NULL,                   /* Get option */
@@ -595,12 +609,202 @@
 	NULL,                   /* get salt size */
 	NULL,                   /* set key */
 	NULL,                   /* get key size */
-
-	/* padding */
-	NULL,
-	NULL,
-	NULL,
-	NULL
+	NULL,                   /* set batch mode */
+	NULL,                   /* get batch mode */
+	md4_get_block_size,     /* get block size */
+	NULL                    /* set key with len */
+};
+
+/*******************************************************************************
+ * HMAC
+ ******************************************************************************/
+
+struct HMAC_Context {
+	PurpleCipherContext *hash;
+	char *name;
+	int blocksize;
+	guchar *opad;
+};
+
+static void
+hmac_init(PurpleCipherContext *context, gpointer extra)
+{
+	struct HMAC_Context *hctx;
+	hctx = g_new0(struct HMAC_Context, 1);
+	purple_cipher_context_set_data(context, hctx);
+	purple_cipher_context_reset(context, extra);
+}
+
+static void
+hmac_reset(PurpleCipherContext *context, gpointer extra)
+{
+	struct HMAC_Context *hctx;
+
+	hctx = purple_cipher_context_get_data(context);
+
+	g_free(hctx->name);
+	hctx->name = NULL;
+	if (hctx->hash)
+		purple_cipher_context_destroy(hctx->hash);
+	hctx->hash = NULL;
+	hctx->blocksize = 0;
+	g_free(hctx->opad);
+	hctx->opad = NULL;
+}
+
+static void
+hmac_set_opt(PurpleCipherContext *context, const gchar *name, void *value)
+{
+	struct HMAC_Context *hctx;
+
+	hctx = purple_cipher_context_get_data(context);
+
+	if (!strcmp(name, "hash")) {
+		g_free(hctx->name);
+		if (hctx->hash)
+			purple_cipher_context_destroy(hctx->hash);
+		hctx->name = g_strdup((char*)value);
+		hctx->hash = purple_cipher_context_new_by_name((char *)value, NULL);
+		hctx->blocksize = purple_cipher_context_get_block_size(hctx->hash);
+	}
+}
+
+static void *
+hmac_get_opt(PurpleCipherContext *context, const gchar *name)
+{
+	struct HMAC_Context *hctx;
+
+	hctx = purple_cipher_context_get_data(context);
+
+	if (!strcmp(name, "hash")) {
+		return hctx->name;
+	}
+
+	return NULL;
+}
+
+static void
+hmac_append(PurpleCipherContext *context, const guchar *data, size_t len) 
+{
+	struct HMAC_Context *hctx = purple_cipher_context_get_data(context);
+
+	g_return_if_fail(hctx->hash != NULL);
+
+	purple_cipher_context_append(hctx->hash, data, len);
+}
+
+static gboolean
+hmac_digest(PurpleCipherContext *context, size_t in_len, guchar *out, size_t *out_len)
+{
+	struct HMAC_Context *hctx = purple_cipher_context_get_data(context);
+	PurpleCipherContext *hash = hctx->hash;
+	guchar *inner_hash;
+	size_t hash_len;
+	gboolean result;
+
+	g_return_val_if_fail(hash != NULL, FALSE);
+
+	inner_hash = g_malloc(100); /* TODO: Should be enough for now... */
+	result = purple_cipher_context_digest(hash, 100, inner_hash, &hash_len);
+
+	purple_cipher_context_reset(hash, NULL);
+
+	purple_cipher_context_append(hash, hctx->opad, hctx->blocksize);
+	purple_cipher_context_append(hash, inner_hash, hash_len);
+
+	g_free(inner_hash);
+
+	result = result && purple_cipher_context_digest(hash, in_len, out, out_len);
+
+	return result;
+}
+
+static void
+hmac_uninit(PurpleCipherContext *context)
+{
+	struct HMAC_Context *hctx;
+
+	purple_cipher_context_reset(context, NULL);
+
+	hctx = purple_cipher_context_get_data(context);
+
+	g_free(hctx);
+}
+
+static void
+hmac_set_key_with_len(PurpleCipherContext *context, const guchar * key, size_t key_len)
+{
+	struct HMAC_Context *hctx = purple_cipher_context_get_data(context);
+	int blocksize, i;
+	guchar *ipad;
+	guchar *full_key;
+
+	g_return_if_fail(hctx->hash != NULL);
+
+	g_free(hctx->opad);
+
+	blocksize = hctx->blocksize;
+	ipad = g_malloc(blocksize);
+	hctx->opad = g_malloc(blocksize);
+
+	if (key_len > blocksize) {
+		purple_cipher_context_reset(hctx->hash, NULL);
+		purple_cipher_context_append(hctx->hash, key, key_len);
+		full_key = g_malloc(100); /* TODO: Should be enough for now... */
+		purple_cipher_context_digest(hctx->hash, 100, full_key, &key_len);
+	} else
+		full_key = g_memdup(key, key_len);
+
+    if (key_len < blocksize) {
+    	full_key = g_realloc(full_key, blocksize);
+    	memset(full_key + key_len, 0, blocksize - key_len);
+    }
+
+	for(i = 0; i < blocksize; i++) {
+		ipad[i] = 0x36 ^ full_key[i];
+		hctx->opad[i] = 0x5c ^ full_key[i];
+	}
+
+	g_free(full_key);
+
+	purple_cipher_context_reset(hctx->hash, NULL);
+	purple_cipher_context_append(hctx->hash, ipad, blocksize);
+	g_free(ipad);
+}
+
+static void
+hmac_set_key(PurpleCipherContext *context, const guchar * key)
+{
+	hmac_set_key_with_len(context, key, strlen(key));
+}
+
+static size_t 
+hmac_get_block_size(PurpleCipherContext *context)
+{
+	struct HMAC_Context *hctx = purple_cipher_context_get_data(context);
+
+	return hctx->blocksize;
+}
+
+static PurpleCipherOps HMACOps = {
+	hmac_set_opt,           /* Set option */
+	hmac_get_opt,           /* Get option */
+	hmac_init,               /* init */
+	hmac_reset,              /* reset */
+	hmac_uninit,             /* uninit */
+	NULL,                   /* set iv */
+	hmac_append,             /* append */
+	hmac_digest,             /* digest */
+	NULL,                   /* encrypt */
+	NULL,                   /* decrypt */
+	NULL,                   /* set salt */
+	NULL,                   /* get salt size */
+	hmac_set_key,           /* set key */
+	NULL,                   /* get key size */
+	NULL,                   /* set batch mode */
+	NULL,                   /* get batch mode */
+	hmac_get_block_size,    /* get block size */
+	hmac_set_key_with_len   /* set key with len */
 };
 
 /******************************************************************************
@@ -986,6 +1190,36 @@
 	return 0;
 }
 
+static gint
+des_decrypt(PurpleCipherContext *context, const guchar data[],
+	    size_t len, guchar output[], size_t *outlen) {
+	int offset = 0;
+	int i = 0;
+	int tmp;
+	guint8 buf[8] = {0,0,0,0,0,0,0,0};
+	while(offset+8<=len) {
+		des_ecb_crypt(purple_cipher_context_get_data(context),
+				data+offset,
+				output+offset,
+				1);
+		offset+=8;
+	}
+	*outlen = len;
+	if(offset<len) {
+		*outlen += len - offset;
+		tmp = offset;
+		while(tmp<len) {
+			buf[i++] = data[tmp];
+			tmp++;
+		}
+		des_ecb_crypt(purple_cipher_context_get_data(context),
+				buf,
+				output+offset,
+				1);
+	}	
+	return 0;
+}
+
 static void
 des_init(PurpleCipherContext *context, gpointer extra) {
 	struct _des_ctx *mctx;
@@ -1005,32 +1239,380 @@
 }
 
 static PurpleCipherOps DESOps = {
-	NULL,                   /* Set option */
-	NULL,                   /* Get option */
-	des_init,               /* init */
+	NULL,              /* Set option */
+	NULL,              /* Get option */
+	des_init,          /* init */
+ 	NULL,              /* reset */
+	des_uninit,        /* uninit */
+	NULL,              /* set iv */
+	NULL,              /* append */
+	NULL,              /* digest */
+	des_encrypt,       /* encrypt */
+	des_decrypt,       /* decrypt */
+	NULL,              /* set salt */
+	NULL,              /* get salt size */
+	des_set_key,       /* set key */
+	NULL,              /* get key size */
+	NULL,              /* set batch mode */
+	NULL,              /* get batch mode */
+	NULL,              /* get block size */
+	NULL               /* set key with len */
+};
+
+/******************************************************************************
+ * Triple-DES
+ *****************************************************************************/
+
+typedef struct _des3_ctx
+{
+	PurpleCipherBatchMode mode;
+	guchar iv[8];
+	/* First key for encryption */
+	struct _des_ctx key1;
+	/* Second key for decryption */
+	struct _des_ctx key2;
+	/* Third key for encryption */
+	struct _des_ctx key3;
+} des3_ctx[1];
+
+/*
+ *  Fill a DES3 context with subkeys calculated from 3 64bit key.
+ *  Does not check parity bits, but simply ignore them.
+ *  Does not check for weak keys.
+ **/
+static void
+des3_set_key(PurpleCipherContext *context, const guchar * key)
+{
+	struct _des3_ctx *ctx = purple_cipher_context_get_data(context);
+	int i;
+
+	des_key_schedule (key +  0, ctx->key1.encrypt_subkeys);
+	des_key_schedule (key +  8, ctx->key2.encrypt_subkeys);
+	des_key_schedule (key + 16, ctx->key3.encrypt_subkeys);
+
+	for (i = 0; i < 32; i += 2)
+	{
+		ctx->key1.decrypt_subkeys[i]	= ctx->key1.encrypt_subkeys[30-i];
+		ctx->key1.decrypt_subkeys[i+1]	= ctx->key1.encrypt_subkeys[31-i];
+		ctx->key2.decrypt_subkeys[i]	= ctx->key2.encrypt_subkeys[30-i];
+		ctx->key2.decrypt_subkeys[i+1]	= ctx->key2.encrypt_subkeys[31-i];
+		ctx->key3.decrypt_subkeys[i]	= ctx->key3.encrypt_subkeys[30-i];
+		ctx->key3.decrypt_subkeys[i+1]	= ctx->key3.encrypt_subkeys[31-i];
+	}
+}
+
+static gint
+des3_ecb_encrypt(struct _des3_ctx *ctx, const guchar data[],
+                 size_t len, guchar output[], size_t *outlen)
+{
+	int offset = 0;
+	int i = 0;
+	int tmp;
+	guint8 buf[8] = {0,0,0,0,0,0,0,0};
+	while (offset + 8 <= len) {
+		des_ecb_crypt(&ctx->key1,
+		              data+offset,
+		              output+offset,
+		              0);
+		des_ecb_crypt(&ctx->key2,
+		              output+offset,
+		              buf,
+		              1);
+		des_ecb_crypt(&ctx->key3,
+		              buf,
+		              output+offset,
+		              0);
+		offset += 8;
+	}
+	*outlen = len;
+	if (offset < len) {
+		*outlen += len - offset;
+		tmp = offset;
+		memset(buf, 0, 8);
+		while (tmp < len) {
+			buf[i++] = data[tmp];
+			tmp++;
+		}
+		des_ecb_crypt(&ctx->key1,
+		              buf,
+		              output+offset,
+		              0);
+		des_ecb_crypt(&ctx->key2,
+		              output+offset,
+		              buf,
+		              1);
+		des_ecb_crypt(&ctx->key3,
+		              buf,
+		              output+offset,
+		              0);
+	}
+	return 0;
+}
+
+static gint
+des3_cbc_encrypt(struct _des3_ctx *ctx, const guchar data[],
+                 size_t len, guchar output[], size_t *outlen)
+{
+	int offset = 0;
+	int i = 0;
+	int tmp;
+	guint8 buf[8];
+	memcpy(buf, ctx->iv, 8);
+	while (offset + 8 <= len) {
+		for (i = 0; i < 8; i++)
+			buf[i] ^= data[offset + i];
+		des_ecb_crypt(&ctx->key1,
+		              buf,
+		              output+offset,
+		              0);
+		des_ecb_crypt(&ctx->key2,
+		              output+offset,
+		              buf,
+		              1);
+		des_ecb_crypt(&ctx->key3,
+		              buf,
+		              output+offset,
+		              0);
+		memcpy(buf, output+offset, 8);
+		offset += 8;
+	}
+	*outlen = len;
+	if (offset < len) {
+		*outlen += len - offset;
+		tmp = offset;
+		i = 0;
+		while (tmp < len) {
+			buf[i++] ^= data[tmp];
+			tmp++;
+		}
+		des_ecb_crypt(&ctx->key1,
+		              buf,
+		              output+offset,
+		              0);
+		des_ecb_crypt(&ctx->key2,
+		              output+offset,
+		              buf,
+		              1);
+		des_ecb_crypt(&ctx->key3,
+		              buf,
+		              output+offset,
+		              0);
+	}
+	return 0;
+}
+
+static gint
+des3_encrypt(PurpleCipherContext *context, const guchar data[],
+             size_t len, guchar output[], size_t *outlen)
+{
+	struct _des3_ctx *ctx = purple_cipher_context_get_data(context);
+
+	if (ctx->mode == PURPLE_CIPHER_BATCH_MODE_ECB) {
+		return des3_ecb_encrypt(ctx, data, len, output, outlen);
+	} else if (ctx->mode == PURPLE_CIPHER_BATCH_MODE_CBC) {
+		return des3_cbc_encrypt(ctx, data, len, output, outlen);
+	} else {
+		g_return_val_if_reached(0);
+	}
+
+	return 0;
+}
+
+static gint
+des3_ecb_decrypt(struct _des3_ctx *ctx, const guchar data[],
+                 size_t len, guchar output[], size_t *outlen)
+{
+	int offset = 0;
+	int i = 0;
+	int tmp;
+	guint8 buf[8] = {0,0,0,0,0,0,0,0};
+	while (offset + 8 <= len) {
+		/* NOTE: Apply key in reverse */
+		des_ecb_crypt(&ctx->key3,
+		              data+offset,
+		              output+offset,
+		              1);
+		des_ecb_crypt(&ctx->key2,
+		              output+offset,
+		              buf,
+		              0);
+		des_ecb_crypt(&ctx->key1,
+		              buf,
+		              output+offset,
+		              1);
+		offset+=8;
+	}
+	*outlen = len;
+	if (offset < len) {
+		*outlen += len - offset;
+		tmp = offset;
+		memset(buf, 0, 8);
+		while (tmp < len) {
+			buf[i++] = data[tmp];
+			tmp++;
+		}
+		des_ecb_crypt(&ctx->key3,
+		              buf,
+		              output+offset,
+		              1);
+		des_ecb_crypt(&ctx->key2,
+		              output+offset,
+		              buf,
+		              0);
+		des_ecb_crypt(&ctx->key1,
+		              buf,
+		              output+offset,
+		              1);
+	}
+	return 0;
+}
+
+static gint
+des3_cbc_decrypt(struct _des3_ctx *ctx, const guchar data[],
+                 size_t len, guchar output[], size_t *outlen)
+{
+	int offset = 0;
+	int i = 0;
+	int tmp;
+	guint8 buf[8] = {0,0,0,0,0,0,0,0};
+	guint8 link[8];
+	memcpy(link, ctx->iv, 8);
+	while (offset + 8 <= len) {
+		des_ecb_crypt(&ctx->key3,
+		              data+offset,
+		              output+offset,
+		              1);
+		des_ecb_crypt(&ctx->key2,
+		              output+offset,
+		              buf,
+		              0);
+		des_ecb_crypt(&ctx->key1,
+		              buf,
+		              output+offset,
+		              1);
+		for (i = 0; i < 8; i++)
+			output[offset + i] ^= link[i];
+		memcpy(link, data + offset, 8);
+		offset+=8;
+	}
+	*outlen = len;
+	if(offset<len) {
+		*outlen += len - offset;
+		tmp = offset;
+		memset(buf, 0, 8);
+		i = 0;
+		while(tmp<len) {
+			buf[i++] = data[tmp];
+			tmp++;
+		}
+		des_ecb_crypt(&ctx->key3,
+		              buf,
+		              output+offset,
+		              1);
+		des_ecb_crypt(&ctx->key2,
+		              output+offset,
+		              buf,
+		              0);
+		des_ecb_crypt(&ctx->key1,
+		              buf,
+		              output+offset,
+		              1);
+		for (i = 0; i < 8; i++)
+			output[offset + i] ^= link[i];
+	}
+	return 0;
+}
+
+static gint
+des3_decrypt(PurpleCipherContext *context, const guchar data[],
+             size_t len, guchar output[], size_t *outlen)
+{
+	struct _des3_ctx *ctx = purple_cipher_context_get_data(context);
+
+	if (ctx->mode == PURPLE_CIPHER_BATCH_MODE_ECB) {
+		return des3_ecb_decrypt(ctx, data, len, output, outlen);
+	} else if (ctx->mode == PURPLE_CIPHER_BATCH_MODE_CBC) {
+		return des3_cbc_decrypt(ctx, data, len, output, outlen);
+	} else {
+		g_return_val_if_reached(0);
+	}
+
+	return 0;
+}
+
+static void
+des3_set_batch(PurpleCipherContext *context, PurpleCipherBatchMode mode)
+{
+	struct _des3_ctx *ctx = purple_cipher_context_get_data(context);
+
+	ctx->mode = mode;
+}
+
+static PurpleCipherBatchMode
+des3_get_batch(PurpleCipherContext *context)
+{
+	struct _des3_ctx *ctx = purple_cipher_context_get_data(context);
+
+	return ctx->mode;
+}
+
+static void
+des3_set_iv(PurpleCipherContext *context, guchar *iv, size_t len)
+{
+	struct _des3_ctx *ctx;
+
+	g_return_if_fail(len == 8);
+
+	ctx = purple_cipher_context_get_data(context);
+
+	memcpy(ctx->iv, iv, len);
+}
+
+static void
+des3_init(PurpleCipherContext *context, gpointer extra)
+{
+	struct _des3_ctx *mctx;
+	mctx = g_new0(struct _des3_ctx, 1);
+	purple_cipher_context_set_data(context, mctx);
+}
+
+static void
+des3_uninit(PurpleCipherContext *context)
+{
+	struct _des3_ctx *des3_context;
+
+	des3_context = purple_cipher_context_get_data(context);
+	memset(des3_context, 0, sizeof(des3_context));
+
+	g_free(des3_context);
+	des3_context = NULL;
+}
+
+static PurpleCipherOps DES3Ops = {
+	NULL,              /* Set option */
+	NULL,              /* Get option */
+	des3_init,         /* init */
 	NULL,              /* reset */
-	des_uninit,             /* uninit */
-	NULL,                   /* set iv */
-	NULL,             /* append */
-	NULL,             /* digest */
-	des_encrypt,                   /* encrypt */
-	NULL,                   /* decrypt */
-	NULL,                   /* set salt */
-	NULL,                   /* get salt size */
-	des_set_key,		/* set key */
-	NULL,                   /* get key size */
-
-	/* padding */
-	NULL,
-	NULL,
-	NULL,
-	NULL
+	des3_uninit,       /* uninit */
+	des3_set_iv,       /* set iv */
+	NULL,              /* append */
+	NULL,              /* digest */
+	des3_encrypt,      /* encrypt */
+	des3_decrypt,      /* decrypt */
+	NULL,              /* set salt */
+	NULL,              /* get salt size */
+	des3_set_key,      /* set key */
+	NULL,              /* get key size */
+	des3_set_batch,    /* set batch mode */
+	des3_get_batch,    /* get batch mode */
+	NULL,              /* get block size */
+	NULL               /* set key with len */
 };
 
-
 /*******************************************************************************
  * SHA-1
  ******************************************************************************/
+#define SHA1_HMAC_BLOCK_SIZE	64
 #define SHA1_ROTL(X,n) ((((X) << (n)) | ((X) >> (32-(n)))) & 0xFFFFFFFF)
 
 struct SHA1Context {
@@ -1251,6 +1833,13 @@
 	return TRUE;
 }
 
+static size_t
+sha1_get_block_size(PurpleCipherContext *context)
+{
+	/* This does not change (in this case) */
+	return SHA1_HMAC_BLOCK_SIZE;
+}
+
 static PurpleCipherOps SHA1Ops = {
 	sha1_set_opt,	/* Set Option		*/
 	sha1_get_opt,	/* Get Option		*/
@@ -1266,12 +1855,10 @@
 	NULL,			/* get salt size	*/
 	NULL,			/* set key			*/
 	NULL,			/* get key size		*/
-
-	/* padding */
-	NULL,
-	NULL,
-	NULL,
-	NULL
+	NULL,			/* set batch mode */
+	NULL,			/* get batch mode */
+	sha1_get_block_size,	/* get block size */
+	NULL			/* set key with len */
 };
 
 /*******************************************************************************
@@ -1435,12 +2022,10 @@
 	NULL,          /* get salt size */
 	rc4_set_key,   /* set key       */
 	rc4_get_key_size, /* get key size  */
-
-	/* padding */
-	NULL,
-	NULL,
-	NULL,
-	NULL
+	NULL,          /* set batch mode */
+	NULL,          /* get batch mode */
+	NULL,          /* get block size */
+	NULL           /* set key with len */
 };
 
 /*******************************************************************************
@@ -1510,6 +2095,14 @@
 		caps |= PURPLE_CIPHER_CAPS_SET_KEY;
 	if(ops->get_key_size)
 		caps |= PURPLE_CIPHER_CAPS_GET_KEY_SIZE;
+	if(ops->set_batch_mode)
+		caps |= PURPLE_CIPHER_CAPS_SET_BATCH_MODE;
+	if(ops->get_batch_mode)
+		caps |= PURPLE_CIPHER_CAPS_GET_BATCH_MODE;
+	if(ops->get_block_size)
+		caps |= PURPLE_CIPHER_CAPS_GET_BLOCK_SIZE;
+	if(ops->set_key_with_len)
+		caps |= PURPLE_CIPHER_CAPS_SET_KEY_WITH_LEN;
 
 	return caps;
 }
@@ -1636,7 +2229,9 @@
 	purple_ciphers_register_cipher("md5", &MD5Ops);
 	purple_ciphers_register_cipher("sha1", &SHA1Ops);
 	purple_ciphers_register_cipher("md4", &MD4Ops);
+	purple_ciphers_register_cipher("hmac", &HMACOps);
 	purple_ciphers_register_cipher("des", &DESOps);
+	purple_ciphers_register_cipher("des3", &DES3Ops);
 	purple_ciphers_register_cipher("rc4", &RC4Ops);
 }
 
@@ -1967,6 +2562,80 @@
 }
 
 void
+purple_cipher_context_set_batch_mode(PurpleCipherContext *context,
+                                     PurpleCipherBatchMode mode)
+{
+	PurpleCipher *cipher = NULL;
+
+	g_return_if_fail(context);
+
+	cipher = context->cipher;
+	g_return_if_fail(cipher);
+
+	if(cipher->ops && cipher->ops->set_batch_mode)
+		cipher->ops->set_batch_mode(context, mode);
+	else
+		purple_debug_info("cipher", "The %s cipher does not support the "
+		                            "set_batch_mode operation\n", cipher->name);
+}
+
+PurpleCipherBatchMode
+purple_cipher_context_get_batch_mode(PurpleCipherContext *context)
+{
+	PurpleCipher *cipher = NULL;
+
+	g_return_val_if_fail(context, -1);
+
+	cipher = context->cipher;
+	g_return_val_if_fail(cipher, -1);
+
+	if(cipher->ops && cipher->ops->get_batch_mode)
+		return cipher->ops->get_batch_mode(context);
+	else {
+		purple_debug_info("cipher", "The %s cipher does not support the "
+		                            "get_batch_mode operation\n", cipher->name);
+		return -1;
+	}
+}
+
+size_t
+purple_cipher_context_get_block_size(PurpleCipherContext *context)
+{
+	PurpleCipher *cipher = NULL;
+
+	g_return_val_if_fail(context, -1);
+
+	cipher = context->cipher;
+	g_return_val_if_fail(cipher, -1);
+
+	if(cipher->ops && cipher->ops->get_block_size)
+		return cipher->ops->get_block_size(context);
+	else {
+		purple_debug_info("cipher", "The %s cipher does not support the "
+		                            "get_block_size operation\n", cipher->name);
+		return -1;
+	}
+}
+
+void
+purple_cipher_context_set_key_with_len(PurpleCipherContext *context,
+                                       const guchar *key, size_t len)
+{
+	PurpleCipher *cipher = NULL;
+
+	g_return_if_fail(context);
+
+	cipher = context->cipher;
+	g_return_if_fail(cipher);
+
+	if(cipher->ops && cipher->ops->set_key_with_len)
+		cipher->ops->set_key_with_len(context, key, len);
+	else
+		purple_debug_info("cipher", "The %s cipher does not support the "
+		                            "set_key_with_len operation\n", cipher->name);
+}
+
+void
 purple_cipher_context_set_data(PurpleCipherContext *context, gpointer data) {
 	g_return_if_fail(context);
 
--- a/libpurple/cipher.h	Fri Dec 21 09:32:30 2007 +0000
+++ b/libpurple/cipher.h	Fri Dec 21 09:33:45 2007 +0000
@@ -37,26 +37,37 @@
 typedef struct _PurpleCipherOps		PurpleCipherOps;		/**< Ops for a PurpleCipher		*/
 typedef struct _PurpleCipherContext	PurpleCipherContext;	/**< A context for a PurpleCipher	*/
 
+/**
+ * Modes for batch encrypters
+ */
+typedef enum _PurpleCipherBatchMode {
+	PURPLE_CIPHER_BATCH_MODE_ECB,
+	PURPLE_CIPHER_BATCH_MODE_CBC
+} PurpleCipherBatchMode;
 
 /**
  * The operation flags for a cipher
  */
 typedef enum _PurpleCipherCaps {
-	PURPLE_CIPHER_CAPS_SET_OPT			= 1 << 1,		/**< Set option flag	*/
-	PURPLE_CIPHER_CAPS_GET_OPT			= 1 << 2,		/**< Get option flag	*/
-	PURPLE_CIPHER_CAPS_INIT				= 1 << 3,		/**< Init flag			*/
-	PURPLE_CIPHER_CAPS_RESET				= 1 << 4,		/**< Reset flag			*/
-	PURPLE_CIPHER_CAPS_UNINIT				= 1 << 5,		/**< Uninit flag		*/
-	PURPLE_CIPHER_CAPS_SET_IV				= 1 << 6,		/**< Set IV flag		*/
-	PURPLE_CIPHER_CAPS_APPEND				= 1 << 7,		/**< Append flag		*/
-	PURPLE_CIPHER_CAPS_DIGEST				= 1 << 8,		/**< Digest flag		*/
-	PURPLE_CIPHER_CAPS_ENCRYPT			= 1 << 9,		/**< Encrypt flag		*/
-	PURPLE_CIPHER_CAPS_DECRYPT			= 1 << 10,		/**< Decrypt flag		*/
-	PURPLE_CIPHER_CAPS_SET_SALT			= 1 << 11,		/**< Set salt flag		*/
-	PURPLE_CIPHER_CAPS_GET_SALT_SIZE		= 1 << 12,		/**< Get salt size flag	*/
-	PURPLE_CIPHER_CAPS_SET_KEY			= 1 << 13,		/**< Set key flag		*/
-	PURPLE_CIPHER_CAPS_GET_KEY_SIZE		= 1 << 14,		/**< Get key size flag	*/
-	PURPLE_CIPHER_CAPS_UNKNOWN			= 1 << 16		/**< Unknown			*/
+	PURPLE_CIPHER_CAPS_SET_OPT          = 1 << 1,   /**< Set option flag	*/
+	PURPLE_CIPHER_CAPS_GET_OPT          = 1 << 2,   /**< Get option flag	*/
+	PURPLE_CIPHER_CAPS_INIT             = 1 << 3,   /**< Init flag			*/
+	PURPLE_CIPHER_CAPS_RESET            = 1 << 4,   /**< Reset flag			*/
+	PURPLE_CIPHER_CAPS_UNINIT           = 1 << 5,   /**< Uninit flag		*/
+	PURPLE_CIPHER_CAPS_SET_IV           = 1 << 6,   /**< Set IV flag		*/
+	PURPLE_CIPHER_CAPS_APPEND           = 1 << 7,   /**< Append flag		*/
+	PURPLE_CIPHER_CAPS_DIGEST           = 1 << 8,   /**< Digest flag		*/
+	PURPLE_CIPHER_CAPS_ENCRYPT          = 1 << 9,   /**< Encrypt flag		*/
+	PURPLE_CIPHER_CAPS_DECRYPT          = 1 << 10,  /**< Decrypt flag		*/
+	PURPLE_CIPHER_CAPS_SET_SALT         = 1 << 11,  /**< Set salt flag		*/
+	PURPLE_CIPHER_CAPS_GET_SALT_SIZE    = 1 << 12,  /**< Get salt size flag	*/
+	PURPLE_CIPHER_CAPS_SET_KEY          = 1 << 13,  /**< Set key flag		*/
+	PURPLE_CIPHER_CAPS_GET_KEY_SIZE     = 1 << 14,  /**< Get key size flag	*/
+	PURPLE_CIPHER_CAPS_SET_BATCH_MODE   = 1 << 15,  /**< Set batch mode flag */
+	PURPLE_CIPHER_CAPS_GET_BATCH_MODE   = 1 << 16,  /**< Get batch mode flag */
+	PURPLE_CIPHER_CAPS_GET_BLOCK_SIZE   = 1 << 17,  /**< The get block size flag */
+	PURPLE_CIPHER_CAPS_SET_KEY_WITH_LEN = 1 << 18,  /**< The set key with length flag */
+	PURPLE_CIPHER_CAPS_UNKNOWN          = 1 << 19   /**< Unknown			*/
 } PurpleCipherCaps;
 
 /**
@@ -105,10 +116,17 @@
 	/** The get key size function */
 	size_t (*get_key_size)(PurpleCipherContext *context);
 
-	void (*_purple_reserved1)(void);
-	void (*_purple_reserved2)(void);
-	void (*_purple_reserved3)(void);
-	void (*_purple_reserved4)(void);
+	/** The set batch mode function */
+	void (*set_batch_mode)(PurpleCipherContext *context, PurpleCipherBatchMode mode);
+
+	/** The get batch mode function */
+	PurpleCipherBatchMode (*get_batch_mode)(PurpleCipherContext *context);
+
+	/** The get block size function */
+	size_t (*get_block_size)(PurpleCipherContext *context);
+
+	/** The set key with length function */
+	void (*set_key_with_len)(PurpleCipherContext *context, const guchar *key, size_t len);
 };
 
 #ifdef __cplusplus
@@ -345,7 +363,7 @@
 /**
  * Sets the salt on a context
  *
- * @param context The context who's salt to set
+ * @param context The context whose salt to set
  * @param salt    The salt
  */
 void purple_cipher_context_set_salt(PurpleCipherContext *context, guchar *salt);
@@ -353,7 +371,7 @@
 /**
  * Gets the size of the salt if the cipher supports it
  *
- * @param context The context who's salt size to get
+ * @param context The context whose salt size to get
  *
  * @return The size of the salt
  */
@@ -362,7 +380,7 @@
 /**
  * Sets the key on a context
  *
- * @param context The context who's key to set
+ * @param context The context whose key to set
  * @param key     The key
  */
 void purple_cipher_context_set_key(PurpleCipherContext *context, const guchar *key);
@@ -370,16 +388,53 @@
 /**
  * Gets the key size for a context
  *
- * @param context The context who's key size to get
+ * @param context The context whose key size to get
  *
  * @return The size of the key
  */
 size_t purple_cipher_context_get_key_size(PurpleCipherContext *context);
 
 /**
+ * Sets the batch mode of a context
+ *
+ * @param context The context whose batch mode to set
+ * @param mode    The batch mode under which the cipher should operate
+ *
+ */
+void purple_cipher_context_set_batch_mode(PurpleCipherContext *context, PurpleCipherBatchMode mode);
+
+/**
+ * Gets the batch mode of a context
+ *
+ * @param context The context whose batch mode to get
+ *
+ * @return The batch mode under which the cipher is operating
+ */
+PurpleCipherBatchMode purple_cipher_context_get_batch_mode(PurpleCipherContext *context);
+
+/**
+ * Gets the block size of a context
+ *
+ * @param context The context whose block size to get
+ *
+ * @return The block size of the context
+ */
+size_t purple_cipher_context_get_block_size(PurpleCipherContext *context);
+
+/**
+ * Sets the key with a given length on a context 
+ *
+ * @param context The context whose key to set
+ * @param key     The key
+ * @param len     The length of the key
+ *
+ */
+void purple_cipher_context_set_key_with_len(PurpleCipherContext *context, const guchar *key, size_t len);
+
+/**
  * Sets the cipher data for a context
  *
- * @param context The context who's cipher data to set
+ * @param context The context whose cipher data to set
  * @param data    The cipher data to set
  */
 void purple_cipher_context_set_data(PurpleCipherContext *context, gpointer data);
@@ -387,7 +442,7 @@
 /**
  * Gets the cipher data for a context
  *
- * @param context The context who's cipher data to get
+ * @param context The context whose cipher data to get
  *
  * @return The cipher data
  */
--- a/libpurple/protocols/jabber/auth.c	Fri Dec 21 09:32:30 2007 +0000
+++ b/libpurple/protocols/jabber/auth.c	Fri Dec 21 09:33:45 2007 +0000
@@ -690,6 +690,10 @@
 			char h[17], *p;
 			int i;
 
+			challenge = xmlnode_get_attrib(xmlnode_get_child(query, "crammd5"), "challenge");
+			auth_hmac_md5(challenge, strlen(challenge), pw, strlen(pw), &digest);
+
+			/* Create the response query */
 			iq = jabber_iq_new_query(js, JABBER_IQ_SET, "jabber:iq:auth");
 			query = xmlnode_get_child(iq->node, "query");
 
@@ -699,8 +703,6 @@
 			xmlnode_insert_data(x, js->user->resource, -1);
 
 			x = xmlnode_new_child(query, "crammd5");
-			challenge = xmlnode_get_attrib(xmlnode_get_child(query, "crammd5"), "challenge");
-			auth_hmac_md5(challenge, strlen(challenge), pw, strlen(pw), &digest);
 
 			/* Translate the digest to a hexadecimal notation */
 			p = h;
--- a/libpurple/tests/test_cipher.c	Fri Dec 21 09:32:30 2007 +0000
+++ b/libpurple/tests/test_cipher.c	Fri Dec 21 09:33:45 2007 +0000
@@ -190,6 +190,511 @@
 END_TEST
 
 /******************************************************************************
+ * DES Tests
+ *****************************************************************************/
+#define DES_TEST(in, keyz, out, len) { \
+	PurpleCipher *cipher = NULL; \
+	PurpleCipherContext *context = NULL; \
+	guchar answer[len+1]; \
+	gint ret = 0; \
+	guchar decrypt[len+1] = in; \
+	guchar key[8+1] = keyz;\
+	guchar encrypt[len+1] = out;\
+	size_t outlen; \
+	\
+	cipher = purple_ciphers_find_cipher("des"); \
+	context = purple_cipher_context_new(cipher, NULL); \
+	purple_cipher_context_set_key(context, key); \
+	\
+	ret = purple_cipher_context_encrypt(context, decrypt, len, answer, &outlen); \
+	fail_unless(ret == 0, NULL); \
+	fail_unless(outlen == (len), NULL); \
+	fail_unless(memcmp(encrypt, answer, len) == 0, NULL); \
+	\
+	ret = purple_cipher_context_decrypt(context, encrypt, len, answer, &outlen); \
+	fail_unless(ret == 0, NULL); \
+	fail_unless(outlen == (len), NULL); \
+	fail_unless(memcmp(decrypt, answer, len) == 0, NULL); \
+	\
+	purple_cipher_context_destroy(context); \
+}
+
+START_TEST(test_des_12345678) {
+	DES_TEST("12345678",
+	         "\x3b\x38\x98\x37\x15\x20\xf7\x5e",
+	         "\x06\x22\x05\xac\x6a\x0d\x55\xdd",
+	         8);
+}
+END_TEST
+
+START_TEST(test_des_abcdefgh) {
+	DES_TEST("abcdefgh",
+	         "\x3b\x38\x98\x37\x15\x20\xf7\x5e",
+	         "\x62\xe0\xc6\x8c\x48\xe4\x75\xed",
+	         8);
+}
+END_TEST
+
+/******************************************************************************
+ * DES3 Tests
+ * See http://csrc.nist.gov/groups/ST/toolkit/examples.html
+ * and some NULL things I made up
+ *****************************************************************************/
+
+#define DES3_TEST(in, key, iv, out, len, mode) { \
+	PurpleCipher *cipher = NULL; \
+	PurpleCipherContext *context = NULL; \
+	guchar answer[len+1]; \
+	guchar decrypt[len+1] = in; \
+	guchar encrypt[len+1] = out; \
+	size_t outlen; \
+	gint ret = 0; \
+	\
+	cipher = purple_ciphers_find_cipher("des3"); \
+	context = purple_cipher_context_new(cipher, NULL); \
+	purple_cipher_context_set_key(context, (guchar *)key); \
+	purple_cipher_context_set_batch_mode(context, (mode)); \
+	purple_cipher_context_set_iv(context, (guchar *)iv, 8); \
+	\
+	ret = purple_cipher_context_encrypt(context, decrypt, len, answer, &outlen); \
+	fail_unless(ret == 0, NULL); \
+	fail_unless(outlen == (len), NULL); \
+	fail_unless(memcmp(encrypt, answer, len) == 0, NULL); \
+	\
+	ret = purple_cipher_context_decrypt(context, encrypt, len, answer, &outlen); \
+	fail_unless(ret == 0, NULL); \
+	fail_unless(outlen == (len), NULL); \
+	fail_unless(memcmp(decrypt, answer, len) == 0, NULL); \
+	\
+	purple_cipher_context_destroy(context); \
+}
+
+START_TEST(test_des3_ecb_nist1) {
+	DES3_TEST(
+	          "\x6B\xC1\xBE\xE2\x2E\x40\x9F\x96\xE9\x3D\x7E\x11\x73\x93\x17\x2A"
+	          "\xAE\x2D\x8A\x57\x1E\x03\xAC\x9C\x9E\xB7\x6F\xAC\x45\xAF\x8E\x51",
+	          "\x01\x23\x45\x67\x89\xAB\xCD\xEF"
+	          "\x23\x45\x67\x89\xAB\xCD\xEF\x01"
+	          "\x45\x67\x89\xAB\xCD\xEF\x01\x23",
+	          "00000000", /* ignored */
+	          "\x71\x47\x72\xF3\x39\x84\x1D\x34\x26\x7F\xCC\x4B\xD2\x94\x9C\xC3"
+	          "\xEE\x11\xC2\x2A\x57\x6A\x30\x38\x76\x18\x3F\x99\xC0\xB6\xDE\x87",
+	          32,
+	          PURPLE_CIPHER_BATCH_MODE_ECB);
+}
+END_TEST
+
+START_TEST(test_des3_ecb_nist2) {
+	DES3_TEST(
+	          "\x6B\xC1\xBE\xE2\x2E\x40\x9F\x96\xE9\x3D\x7E\x11\x73\x93\x17\x2A"
+	          "\xAE\x2D\x8A\x57\x1E\x03\xAC\x9C\x9E\xB7\x6F\xAC\x45\xAF\x8E\x51",
+	          "\x01\x23\x45\x67\x89\xAB\xCD\xEF"
+	          "\x23\x45\x67\x89\xAB\xCD\xEF\x01"
+	          "\x01\x23\x45\x67\x89\xAB\xCD\xEF",
+	          "00000000", /* ignored */
+	          "\x06\xED\xE3\xD8\x28\x84\x09\x0A\xFF\x32\x2C\x19\xF0\x51\x84\x86"
+	          "\x73\x05\x76\x97\x2A\x66\x6E\x58\xB6\xC8\x8C\xF1\x07\x34\x0D\x3D",
+	          32,
+	          PURPLE_CIPHER_BATCH_MODE_ECB);
+}
+END_TEST
+
+START_TEST(test_des3_ecb_null_key) {
+	DES3_TEST(
+	          "\x16\xf4\xb3\x77\xfd\x4b\x9e\xca",
+	          "\x38\x00\x88\x6a\xef\xcb\x00\xad"
+	          "\x5d\xe5\x29\x00\x7d\x98\x64\x4c"
+	          "\x86\x00\x7b\xd3\xc7\x00\x7b\x32",
+	          "00000000", /* ignored */
+	          "\xc0\x60\x30\xa1\xb7\x25\x42\x44",
+	          8,
+	          PURPLE_CIPHER_BATCH_MODE_ECB);
+}
+END_TEST
+
+START_TEST(test_des3_ecb_null_text) {
+	DES3_TEST(
+	          "\x65\x73\x34\xc1\x19\x00\x79\x65",
+	          "\x32\x64\xda\x10\x13\x6a\xfe\x1e"
+	          "\x37\x54\xd1\x2c\x41\x04\x10\x40"
+	          "\xaf\x1c\x75\x2b\x51\x3a\x03\xf5",
+	          "00000000", /* ignored */
+	          "\xe5\x80\xf6\x12\xf8\x4e\xd9\x6c",
+	          8,
+	          PURPLE_CIPHER_BATCH_MODE_ECB);
+}
+END_TEST
+
+START_TEST(test_des3_ecb_null_key_and_text) {
+	DES3_TEST(
+	          "\xdf\x7f\x00\x92\xe7\xc1\x49\xd2",
+	          "\x0e\x41\x00\xc4\x8b\xf0\x6e\xa1"
+	          "\x66\x49\x42\x63\x22\x00\xf0\x99"
+	          "\x6b\x22\xc1\x37\x9c\x00\xe4\x8f",
+	          "00000000", /* ignored */
+	          "\x73\xd8\x1f\x1f\x50\x01\xe4\x79",
+	          8,
+	          PURPLE_CIPHER_BATCH_MODE_ECB);
+}
+END_TEST
+
+START_TEST(test_des3_cbc_nist1) {
+	DES3_TEST(
+	          "\x6B\xC1\xBE\xE2\x2E\x40\x9F\x96\xE9\x3D\x7E\x11\x73\x93\x17\x2A"
+	          "\xAE\x2D\x8A\x57\x1E\x03\xAC\x9C\x9E\xB7\x6F\xAC\x45\xAF\x8E\x51",
+	          "\x01\x23\x45\x67\x89\xAB\xCD\xEF"
+	          "\x23\x45\x67\x89\xAB\xCD\xEF\x01"
+	          "\x45\x67\x89\xAB\xCD\xEF\x01\x23",
+	          "\xF6\x9F\x24\x45\xDF\x4F\x9B\x17",
+	          "\x20\x79\xC3\xD5\x3A\xA7\x63\xE1\x93\xB7\x9E\x25\x69\xAB\x52\x62"
+	          "\x51\x65\x70\x48\x1F\x25\xB5\x0F\x73\xC0\xBD\xA8\x5C\x8E\x0D\xA7",
+	          32,
+	          PURPLE_CIPHER_BATCH_MODE_CBC);
+}
+END_TEST
+
+START_TEST(test_des3_cbc_nist2) {
+	DES3_TEST(
+	          "\x6B\xC1\xBE\xE2\x2E\x40\x9F\x96\xE9\x3D\x7E\x11\x73\x93\x17\x2A"
+	          "\xAE\x2D\x8A\x57\x1E\x03\xAC\x9C\x9E\xB7\x6F\xAC\x45\xAF\x8E\x51",
+	          "\x01\x23\x45\x67\x89\xAB\xCD\xEF"
+	          "\x23\x45\x67\x89\xAB\xCD\xEF\x01"
+	          "\x01\x23\x45\x67\x89\xAB\xCD\xEF",
+	          "\xF6\x9F\x24\x45\xDF\x4F\x9B\x17",
+	          "\x74\x01\xCE\x1E\xAB\x6D\x00\x3C\xAF\xF8\x4B\xF4\x7B\x36\xCC\x21"
+	          "\x54\xF0\x23\x8F\x9F\xFE\xCD\x8F\x6A\xCF\x11\x83\x92\xB4\x55\x81",
+	          32,
+	          PURPLE_CIPHER_BATCH_MODE_CBC);
+}
+END_TEST
+
+START_TEST(test_des3_cbc_null_key) {
+	DES3_TEST(
+	          "\x16\xf4\xb3\x77\xfd\x4b\x9e\xca",
+	          "\x38\x00\x88\x6a\xef\xcb\x00\xad"
+	          "\x5d\xe5\x29\x00\x7d\x98\x64\x4c"
+	          "\x86\x00\x7b\xd3\xc7\x00\x7b\x32",
+	          "\x31\x32\x33\x34\x35\x36\x37\x38",
+	          "\x52\xe7\xde\x96\x39\x87\x87\xdb",
+	          8,
+	          PURPLE_CIPHER_BATCH_MODE_CBC);
+}
+END_TEST
+
+START_TEST(test_des3_cbc_null_text) {
+	DES3_TEST(
+	          "\x65\x73\x34\xc1\x19\x00\x79\x65",
+	          "\x32\x64\xda\x10\x13\x6a\xfe\x1e"
+	          "\x37\x54\xd1\x2c\x41\x04\x10\x40"
+	          "\xaf\x1c\x75\x2b\x51\x3a\x03\xf5",
+	          "\x7C\xAF\x0D\x57\x1E\x57\x10\xDA",
+	          "\x40\x12\x0e\x00\x85\xff\x6c\xc2",
+	          8,
+	          PURPLE_CIPHER_BATCH_MODE_CBC);
+}
+END_TEST
+
+START_TEST(test_des3_cbc_null_key_and_text) {
+	DES3_TEST(
+	          "\xdf\x7f\x00\x92\xe7\xc1\x49\xd2",
+	          "\x0e\x41\x00\xc4\x8b\xf0\x6e\xa1"
+	          "\x66\x49\x42\x63\x22\x00\xf0\x99"
+	          "\x6b\x22\xc1\x37\x9c\x00\xe4\x8f",
+	          "\x01\x19\x0D\x2c\x40\x67\x89\x67",
+	          "\xa7\xc1\x10\xbe\x9b\xd5\x8a\x67",
+	          8,
+	          PURPLE_CIPHER_BATCH_MODE_CBC);
+}
+END_TEST
+
+/******************************************************************************
+ * HMAC Tests
+ * See RFC2202 and some other NULL tests I made up
+ *****************************************************************************/
+
+#define HMAC_TEST(data, data_len, key, key_len, type, digest) { \
+	PurpleCipher *cipher = NULL; \
+	PurpleCipherContext *context = NULL; \
+	gchar cdigest[41]; \
+	gboolean ret = FALSE; \
+	\
+	cipher = purple_ciphers_find_cipher("hmac"); \
+	context = purple_cipher_context_new(cipher, NULL); \
+	purple_cipher_context_set_option(context, "hash", type); \
+	purple_cipher_context_set_key_with_len(context, (guchar *)key, (key_len)); \
+	\
+	purple_cipher_context_append(context, (guchar *)(data), (data_len)); \
+	ret = purple_cipher_context_digest_to_str(context, sizeof(cdigest), cdigest, \
+	                                        NULL); \
+	\
+	fail_unless(ret == TRUE, NULL); \
+	fail_unless(strcmp((digest), cdigest) == 0, NULL); \
+	\
+	purple_cipher_context_destroy(context); \
+}
+
+/* HMAC MD5 */
+
+START_TEST(test_hmac_md5_Hi) {
+	HMAC_TEST("Hi There",
+	          8,
+	          "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b",
+	          16,
+	          "md5",
+	          "9294727a3638bb1c13f48ef8158bfc9d");
+}
+END_TEST
+
+START_TEST(test_hmac_md5_what) {
+	HMAC_TEST("what do ya want for nothing?",
+	          28,
+	          "Jefe",
+	          4,
+	          "md5",
+	          "750c783e6ab0b503eaa86e310a5db738");
+}
+END_TEST
+
+START_TEST(test_hmac_md5_dd) {
+	HMAC_TEST("\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+	          "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+	          "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+	          "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+	          "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd",
+	          50,
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa",
+	          16,
+	          "md5",
+	          "56be34521d144c88dbb8c733f0e8b3f6");
+}
+END_TEST
+
+START_TEST(test_hmac_md5_cd) {
+	HMAC_TEST("\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+	          "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+	          "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+	          "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+	          "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd",
+	          50,
+	          "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a"
+	          "\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14"
+	          "\x15\x16\x17\x18\x19",
+	          25,
+	          "md5",
+	          "697eaf0aca3a3aea3a75164746ffaa79");
+}
+END_TEST
+
+START_TEST(test_hmac_md5_truncation) {
+	HMAC_TEST("Test With Truncation",
+	          20,
+	          "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c",
+	          16,
+	          "md5",
+	          "56461ef2342edc00f9bab995690efd4c");
+}
+END_TEST
+
+START_TEST(test_hmac_md5_large_key) {
+	HMAC_TEST("Test Using Larger Than Block-Size Key - Hash Key First",
+	          54,
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa",
+	          80,
+	          "md5",
+	          "6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd");
+}
+END_TEST
+
+START_TEST(test_hmac_md5_large_key_and_data) {
+	HMAC_TEST("Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data",
+	          73,
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa",
+	          80,
+	          "md5",
+	          "6f630fad67cda0ee1fb1f562db3aa53e");
+}
+END_TEST
+
+START_TEST(test_hmac_md5_null_key) {
+	HMAC_TEST("Hi There",
+	          8,
+	          "\x0a\x0b\x00\x0d\x0e\x0f\x1a\x2f\x0b\x0b"
+	          "\x0b\x00\x00\x0b\x0b\x49\x5f\x6e\x0b\x0b",
+	          20,
+	          "md5",
+	          "597bfd644b797a985561eeb03a169e59");
+}
+END_TEST
+
+START_TEST(test_hmac_md5_null_text) {
+	HMAC_TEST("Hi\x00There",
+	          8,
+	          "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
+	          "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b",
+	          20,
+	          "md5",
+	          "70be8e1b7b50dfcc335d6cd7992c564f");
+}
+END_TEST
+
+START_TEST(test_hmac_md5_null_key_and_text) {
+	HMAC_TEST("Hi\x00Th\x00re",
+	          8,
+	          "\x0c\x0d\x00\x0f\x10\x1a\x3a\x3a\xe6\x34"
+	          "\x0b\x00\x00\x0b\x0b\x49\x5f\x6e\x0b\x0b",
+	          20,
+	          "md5",
+	          "b31bcbba35a33a067cbba9131cba4889");
+}
+END_TEST
+
+/* HMAC SHA1 */
+
+START_TEST(test_hmac_sha1_Hi) {
+	HMAC_TEST("Hi There",
+	          8,
+	          "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
+	          "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b",
+	          20,
+	          "sha1",
+	          "b617318655057264e28bc0b6fb378c8ef146be00");
+}
+END_TEST
+
+START_TEST(test_hmac_sha1_what) {
+	HMAC_TEST("what do ya want for nothing?",
+	          28,
+	          "Jefe",
+	          4,
+	          "sha1",
+	          "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79");
+}
+END_TEST
+
+START_TEST(test_hmac_sha1_dd) {
+	HMAC_TEST("\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+	          "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+	          "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+	          "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd"
+	          "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd",
+	          50,
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa",
+	          20,
+	          "sha1",
+	          "125d7342b9ac11cd91a39af48aa17b4f63f175d3");
+}
+END_TEST
+
+START_TEST(test_hmac_sha1_cd) {
+	HMAC_TEST("\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+	          "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+	          "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+	          "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd"
+	          "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd",
+	          50,
+	          "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a"
+	          "\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14"
+	          "\x15\x16\x17\x18\x19",
+	          25,
+	          "sha1",
+	          "4c9007f4026250c6bc8414f9bf50c86c2d7235da");
+}
+END_TEST
+
+START_TEST(test_hmac_sha1_truncation) {
+	HMAC_TEST("Test With Truncation",
+	          20,
+	          "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c"
+	          "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c",
+	          20,
+	          "sha1",
+	          "4c1a03424b55e07fe7f27be1d58bb9324a9a5a04");
+}
+END_TEST
+
+START_TEST(test_hmac_sha1_large_key) {
+	HMAC_TEST("Test Using Larger Than Block-Size Key - Hash Key First",
+	          54,
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa",
+	          80,
+	          "sha1",
+	          "aa4ae5e15272d00e95705637ce8a3b55ed402112");
+}
+END_TEST
+
+START_TEST(test_hmac_sha1_large_key_and_data) {
+	HMAC_TEST("Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data",
+	          73,
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+	          "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa",
+	          80,
+	          "sha1",
+	          "e8e99d0f45237d786d6bbaa7965c7808bbff1a91");
+}
+END_TEST
+
+START_TEST(test_hmac_sha1_null_key) {
+	HMAC_TEST("Hi There",
+	          8,
+	          "\x0a\x0b\x00\x0d\x0e\x0f\x1a\x2f\x0b\x0b"
+	          "\x0b\x00\x00\x0b\x0b\x49\x5f\x6e\x0b\x0b",
+	          20,
+	          "sha1",
+	          "eb62a2e0e33d300be669c52aab3f591bc960aac5");
+}
+END_TEST
+
+START_TEST(test_hmac_sha1_null_text) {
+	HMAC_TEST("Hi\x00There",
+	          8,
+	          "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b"
+	          "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b",
+	          20,
+	          "sha1",
+	          "31ca58d849e971e418e3439de2c6f83144b6abb7");
+}
+END_TEST
+
+START_TEST(test_hmac_sha1_null_key_and_text) {
+	HMAC_TEST("Hi\x00Th\x00re",
+	          8,
+	          "\x0c\x0d\x00\x0f\x10\x1a\x3a\x3a\xe6\x34"
+	          "\x0b\x00\x00\x0b\x0b\x49\x5f\x6e\x0b\x0b",
+	          20,
+	          "sha1",
+	          "e6b8e2fede87aa09dcb13e554df1435e056eae36");
+}
+END_TEST
+
+/******************************************************************************
  * Suite
  *****************************************************************************/
 Suite *
@@ -227,6 +732,53 @@
 	tcase_add_test(tc, test_sha1_1000_as_1000_times);
 	suite_add_tcase(s, tc);
 
+	/* des tests */
+	tc = tcase_create("DES");
+	tcase_add_test(tc, test_des_12345678);
+	tcase_add_test(tc, test_des_abcdefgh);
+	suite_add_tcase(s, tc);
+
+	/* des3 ecb tests */
+	tc = tcase_create("DES3 ECB");
+	tcase_add_test(tc, test_des3_ecb_nist1);
+	tcase_add_test(tc, test_des3_ecb_nist2);
+	tcase_add_test(tc, test_des3_ecb_null_key);
+	tcase_add_test(tc, test_des3_ecb_null_text);
+	tcase_add_test(tc, test_des3_ecb_null_key_and_text);
+	suite_add_tcase(s, tc);
+	/* des3 cbc tests */
+	tc = tcase_create("DES3 CBC");
+	tcase_add_test(tc, test_des3_cbc_nist1);
+	tcase_add_test(tc, test_des3_cbc_nist2);
+	tcase_add_test(tc, test_des3_cbc_null_key);
+	tcase_add_test(tc, test_des3_cbc_null_text);
+	tcase_add_test(tc, test_des3_cbc_null_key_and_text);
+	suite_add_tcase(s, tc);
+
+	/* hmac tests */
+	tc = tcase_create("HMAC");
+	tcase_add_test(tc, test_hmac_md5_Hi);
+	tcase_add_test(tc, test_hmac_md5_what);
+	tcase_add_test(tc, test_hmac_md5_dd);
+	tcase_add_test(tc, test_hmac_md5_cd);
+	tcase_add_test(tc, test_hmac_md5_truncation);
+	tcase_add_test(tc, test_hmac_md5_large_key);
+	tcase_add_test(tc, test_hmac_md5_large_key_and_data);
+	tcase_add_test(tc, test_hmac_md5_null_key);
+	tcase_add_test(tc, test_hmac_md5_null_text);
+	tcase_add_test(tc, test_hmac_md5_null_key_and_text);
+	tcase_add_test(tc, test_hmac_sha1_Hi);
+	tcase_add_test(tc, test_hmac_sha1_what);
+	tcase_add_test(tc, test_hmac_sha1_dd);
+	tcase_add_test(tc, test_hmac_sha1_cd);
+	tcase_add_test(tc, test_hmac_sha1_truncation);
+	tcase_add_test(tc, test_hmac_sha1_large_key);
+	tcase_add_test(tc, test_hmac_sha1_large_key_and_data);
+	tcase_add_test(tc, test_hmac_sha1_null_key);
+	tcase_add_test(tc, test_hmac_sha1_null_text);
+	tcase_add_test(tc, test_hmac_sha1_null_key_and_text);
+	suite_add_tcase(s, tc);
+
 	return s;
 }
 
--- a/pidgin/gtkaccount.c	Fri Dec 21 09:32:30 2007 +0000
+++ b/pidgin/gtkaccount.c	Fri Dec 21 09:33:45 2007 +0000
@@ -55,7 +55,6 @@
 	COLUMN_ENABLED,
 	COLUMN_PROTOCOL,
 	COLUMN_DATA,
-	COLUMN_PULSE_DATA,
 	NUM_COLUMNS
 };
 
@@ -139,18 +138,6 @@
 
 } AccountPrefsDialog;
 
-typedef struct
-{
-	GdkPixbuf *online_pixbuf;
-	gboolean pulse_to_grey;
-	float pulse_value;
-	int timeout;
-	PurpleAccount *account;
-	GtkTreeModel *model;
-
-} PidginPulseData;
-
-
 static AccountsWindow *accounts_window = NULL;
 static GHashTable *account_pref_wins;
 
@@ -1550,7 +1537,6 @@
 signed_on_off_cb(PurpleConnection *gc, gpointer user_data)
 {
 	PurpleAccount *account;
-	PidginPulseData *pulse_data;
 	GtkTreeModel *model;
 	GtkTreeIter iter;
 	GdkPixbuf *pixbuf;
@@ -1566,29 +1552,14 @@
 
 	if (gtk_tree_model_iter_nth_child(model, &iter, NULL, index))
 	{
-		gtk_tree_model_get(GTK_TREE_MODEL(accounts_window->model), &iter,
-						   COLUMN_PULSE_DATA, &pulse_data, -1);
-
-		if (pulse_data != NULL)
-		{
-			if (pulse_data->timeout > 0)
-				g_source_remove(pulse_data->timeout);
-
-			g_object_unref(G_OBJECT(pulse_data->online_pixbuf));
-
-			g_free(pulse_data);
-		}
-
 		pixbuf = pidgin_create_prpl_icon(account, PIDGIN_PRPL_ICON_MEDIUM);
 		if ((pixbuf != NULL) && purple_account_is_disconnected(account))
 			gdk_pixbuf_saturate_and_pixelate(pixbuf, pixbuf, 0.0, FALSE);
 
 		gtk_list_store_set(accounts_window->model, &iter,
 				   COLUMN_ICON, pixbuf,
-				   COLUMN_PULSE_DATA, NULL,
 				   -1);
 
-
 		if (pixbuf != NULL)
 			g_object_unref(G_OBJECT(pixbuf));
 	}
--- a/pidgin/gtkdialogs.c	Fri Dec 21 09:32:30 2007 +0000
+++ b/pidgin/gtkdialogs.c	Fri Dec 21 09:33:45 2007 +0000
@@ -89,6 +89,7 @@
 	{"Megan 'Cae' Schneider",       N_("support/QA"), NULL},
 	{"Evan Schoenberg",		N_("developer"), NULL},
 	{"Kevin 'SimGuy' Stange",	N_("developer & webmaster"),	NULL},
+	{"Will 'resiak' Thompson",	N_("developer"),	NULL},
 	{"Stu 'nosnilmot' Tomlinson",	N_("developer"), NULL},
 	{"Nathan 'faceprint' Walp",		N_("developer"), NULL},
 	{NULL, NULL, NULL}
@@ -98,8 +99,8 @@
 static const struct developer patch_writers[] = {
 	{"Dennis 'EvilDennisR' Ristuccia",	N_("Senior Contributor/QA"),	NULL},
 	{"Peter 'Fmoo' Ruibal",		NULL,	NULL},
+	{"Elliott 'QuLogic' Sales de Andrade",	NULL,	NULL},
 	{"Gabriel 'Nix' Schulhof", 	NULL, 	NULL},
-	{"Will 'resiak' Thompson",	NULL,	NULL},
 	{NULL, NULL, NULL}
 };
 
--- a/pidgin/pidgintooltip.c	Fri Dec 21 09:32:30 2007 +0000
+++ b/pidgin/pidgintooltip.c	Fri Dec 21 09:33:45 2007 +0000
@@ -83,7 +83,9 @@
 
 	tipwindow = gtk_window_new(GTK_WINDOW_POPUP);
 	name = gtk_window_get_title(GTK_WINDOW(pidgin_tooltip.widget));
+#if GTK_CHECK_VERSION(2,10,0)
 	gtk_window_set_type_hint(GTK_WINDOW(tipwindow), GDK_WINDOW_TYPE_HINT_TOOLTIP);
+#endif
 	gtk_widget_set_app_paintable(tipwindow, TRUE);
 	gtk_window_set_title(GTK_WINDOW(tipwindow), name ? name : _("Pidgin Tooltip"));
 	gtk_window_set_resizable(GTK_WINDOW(tipwindow), FALSE);
--- a/po/de.po	Fri Dec 21 09:32:30 2007 +0000
+++ b/po/de.po	Fri Dec 21 09:33:45 2007 +0000
@@ -11,8 +11,8 @@
 msgstr ""
 "Project-Id-Version: de\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2007-12-18 16:51+0100\n"
-"PO-Revision-Date: 2007-12-18 16:50+0100\n"
+"POT-Creation-Date: 2007-12-19 10:17+0100\n"
+"PO-Revision-Date: 2007-12-19 10:17+0100\n"
 "Last-Translator: Jochen Kemnade <jochenkemnade@web.de>\n"
 "Language-Team: Deutsch <de@li.org>\n"
 "MIME-Version: 1.0\n"
@@ -1019,6 +1019,9 @@
 msgid "Open File..."
 msgstr "Datei öffnen..."
 
+msgid "Choose Location..."
+msgstr "Wählen Sie einen Ort..."
+
 msgid "Buddy logs in"
 msgstr "Buddy meldet sich an"
 
@@ -9630,7 +9633,7 @@
 msgstr "Verbindung zu %s nicht möglich: %s"
 
 #. 10053
-#, fuzzy, c-format
+#, c-format
 msgid "Connection interrupted by other software on your computer."
 msgstr ""
 "Die Verbindung wurde von einer anderen Software auf ihrem Computer "
@@ -10460,6 +10463,14 @@
 msgid "User has typed something and stopped"
 msgstr "Benutzer hat etwas getippt und wartet nun"
 
+#, c-format
+msgid ""
+"\n"
+"%s has typed something and stopped"
+msgstr ""
+"\n"
+"%s hat etwas getippt und wartet nun"
+
 #. Build the Send To menu
 msgid "S_end To"
 msgstr "S_enden an"
@@ -11425,6 +11436,33 @@
 
 #, c-format
 msgid ""
+"%s %s\n"
+"Usage: %s [OPTION]...\n"
+"\n"
+"  -c, --config=DIR    use DIR for config files\n"
+"  -d, --debug         print debugging messages to stdout\n"
+"  -h, --help          display this help and exit\n"
+"  -m, --multiple      do not ensure single instance\n"
+"  -n, --nologin       don't automatically login\n"
+"  -l, --login[=NAME]  automatically login (optional argument NAME specifies\n"
+"                      account(s) to use, separated by commas)\n"
+"  -v, --version       display the current version and exit\n"
+msgstr ""
+"%s %s\n"
+"Benutzung: %s [OPTION]...\n"
+"\n"
+"  -c, --config=VERZ   benutze VERZ als Konfigurationsverzeichnis\n"
+"  -d, --debug         gibt Debugging-Meldungen nach stdout aus\n"
+"  -h, --help          zeigt diese Hilfe und beendet das Programm\n"
+"  -m, --multiple      mehrere Instanzen erlauben\n"
+"  -n, --nologin       nicht automatisch anmelden\n"
+"  -l, --login[=NAME]  automatische Anmeldung (optionales Argument \n"
+"                      NAME bestimmt Konto(n), die benutzt werden\n"
+"                      sollen, getrennt durch Kommata)\n"
+"  -v, --version       zeigt aktuelle Version und beendet das Programm\n"
+
+#, c-format
+msgid ""
 "%s %s has segfaulted and attempted to dump a core file.\n"
 "This is a bug in the software and has happened through\n"
 "no fault of your own.\n"
@@ -13203,12 +13241,3 @@
 msgid "This plugin is useful for debbuging XMPP servers or clients."
 msgstr ""
 "Dieses Plugin ist nützlich zur Fehlersuche in XMPP-Servern oder -Clients."
-
-#~ msgid "%s changed status from %s to %s"
-#~ msgstr "%s hat den Status von %s zu %s geändert"
-
-#~ msgid "%s is now %s"
-#~ msgstr "%s ist jetzt %s"
-
-#~ msgid "%s is no longer %s"
-#~ msgstr "%s ist nicht mehr %s"