aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile46
-rw-r--r--lib/arc.c223
-rw-r--r--lib/arc.h40
-rw-r--r--lib/base64.c153
-rw-r--r--lib/base64.h33
-rw-r--r--lib/des.c646
-rw-r--r--lib/des.h51
-rw-r--r--lib/events.h87
-rw-r--r--lib/events_glib.c153
-rw-r--r--lib/events_libevent.c294
-rw-r--r--lib/ftutil.c144
-rw-r--r--lib/ftutil.h40
-rw-r--r--lib/http_client.c456
-rw-r--r--lib/http_client.h84
-rw-r--r--lib/ini.c145
-rw-r--r--lib/ini.h45
-rw-r--r--lib/md5.c262
-rw-r--r--lib/md5.h46
-rw-r--r--lib/misc.c730
-rw-r--r--lib/misc.h74
-rw-r--r--lib/oauth.c456
-rw-r--r--lib/oauth.h94
-rw-r--r--lib/proxy.c586
-rw-r--r--lib/proxy.h53
-rw-r--r--lib/sha1.c422
-rw-r--r--lib/sha1.h71
-rw-r--r--lib/ssl_bogus.c71
-rw-r--r--lib/ssl_client.h84
-rw-r--r--lib/ssl_gnutls.c280
-rw-r--r--lib/ssl_nss.c240
-rw-r--r--lib/ssl_openssl.c305
-rw-r--r--lib/ssl_sspi.c278
-rw-r--r--lib/url.c109
-rw-r--r--lib/url.h44
-rw-r--r--lib/xmltree.c698
-rw-r--r--lib/xmltree.h100
36 files changed, 7643 insertions, 0 deletions
diff --git a/lib/Makefile b/lib/Makefile
new file mode 100644
index 00000000..3ae43935
--- /dev/null
+++ b/lib/Makefile
@@ -0,0 +1,46 @@
+###########################
+## Makefile for BitlBee ##
+## ##
+## Copyright 2006 Lintux ##
+###########################
+
+### DEFINITIONS
+
+-include ../Makefile.settings
+ifdef SRCDIR
+SRCDIR := $(SRCDIR)lib/
+endif
+
+# [SH] Program variables
+objects = arc.o base64.o $(DES) $(EVENT_HANDLER) ftutil.o http_client.o ini.o md5.o misc.o oauth.o proxy.o sha1.o $(SSL_CLIENT) url.o xmltree.o
+
+LFLAGS += -r
+
+# [SH] Phony targets
+all: lib.o
+check: all
+lcov: check
+gcov:
+ gcov *.c
+
+.PHONY: all clean distclean
+
+clean: $(subdirs)
+ rm -f *.o $(OUTFILE) core
+
+distclean: clean $(subdirs)
+ rm -rf .depend
+
+### MAIN PROGRAM
+
+lib.o: $(objects) $(subdirs)
+ @echo '*' Linking lib.o
+ @$(LD) $(LFLAGS) $(objects) -o lib.o
+
+$(objects): ../Makefile.settings Makefile
+
+$(objects): %.o: $(SRCDIR)%.c
+ @echo '*' Compiling $<
+ @$(CC) -c $(CFLAGS) $< -o $@
+
+-include .depend/*.d
diff --git a/lib/arc.c b/lib/arc.c
new file mode 100644
index 00000000..fd498454
--- /dev/null
+++ b/lib/arc.c
@@ -0,0 +1,223 @@
+/***************************************************************************\
+* *
+* BitlBee - An IRC to IM gateway *
+* Simple (but secure) ArcFour implementation for safer password storage. *
+* *
+* Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net> *
+* *
+* This library is free software; you can redistribute it and/or *
+* modify it under the terms of the GNU Lesser General Public *
+* License as published by the Free Software Foundation, version *
+* 2.1. *
+* *
+* This library is distributed in the hope that it will be useful, *
+* but WITHOUT ANY WARRANTY; without even the implied warranty of *
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
+* Lesser General Public License for more details. *
+* *
+* You should have received a copy of the GNU Lesser General Public License *
+* along with this library; if not, write to the Free Software Foundation, *
+* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *
+* *
+\***************************************************************************/
+
+/*
+ This file implements ArcFour-encryption, which will mainly be used to
+ save IM passwords safely in the new XML-format. Possibly other uses will
+ come up later. It's supposed to be quite reliable (thanks to the use of a
+ 6-byte IV/seed), certainly compared to the old format. The only realistic
+ way to crack BitlBee passwords now is to use a sniffer to get your hands
+ on the user's password.
+
+ If you see that something's wrong in this implementation (I asked a
+ couple of people to look at it already, but who knows), please tell me.
+
+ The reason I picked ArcFour is because it's pretty simple but effective,
+ so it will work without adding several KBs or an extra library dependency.
+
+ (ArcFour is an RC4-compatible cipher. See for details:
+ http://www.mozilla.org/projects/security/pki/nss/draft-kaukonen-cipher-arcfour-03.txt)
+*/
+
+
+#include <glib.h>
+#include <gmodule.h>
+#include <stdlib.h>
+#include <string.h>
+#include "misc.h"
+#include "arc.h"
+
+/* Add some seed to the password, to make sure we *never* use the same key.
+ This defines how many bytes we use as a seed. */
+#define ARC_IV_LEN 6
+
+/* To defend against a "Fluhrer, Mantin and Shamir attack", it is recommended
+ to shuffle S[] just a bit more before you start to use it. This defines how
+ many bytes we'll request before we'll really use them for encryption. */
+#define ARC_CYCLES 1024
+
+struct arc_state *arc_keymaker( unsigned char *key, int kl, int cycles )
+{
+ struct arc_state *st;
+ int i, j, tmp;
+ unsigned char S2[256];
+
+ st = g_malloc( sizeof( struct arc_state ) );
+ st->i = st->j = 0;
+ if( kl <= 0 )
+ kl = strlen( (char*) key );
+
+ for( i = 0; i < 256; i ++ )
+ {
+ st->S[i] = i;
+ S2[i] = key[i%kl];
+ }
+
+ for( i = j = 0; i < 256; i ++ )
+ {
+ j = ( j + st->S[i] + S2[i] ) & 0xff;
+ tmp = st->S[i];
+ st->S[i] = st->S[j];
+ st->S[j] = tmp;
+ }
+
+ memset( S2, 0, 256 );
+ i = j = 0;
+
+ for( i = 0; i < cycles; i ++ )
+ arc_getbyte( st );
+
+ return st;
+}
+
+/*
+ For those who don't know, ArcFour is basically an algorithm that generates
+ a stream of bytes after you give it a key. Just get a byte from it and
+ xor it with your cleartext. To decrypt, just give it the same key again
+ and start xorring.
+
+ The function above initializes the byte generator, the next function can
+ be used to get bytes from the generator (and shuffle things a bit).
+*/
+
+unsigned char arc_getbyte( struct arc_state *st )
+{
+ unsigned char tmp;
+
+ /* Unfortunately the st-> stuff doesn't really improve readability here... */
+ st->i ++;
+ st->j += st->S[st->i];
+ tmp = st->S[st->i];
+ st->S[st->i] = st->S[st->j];
+ st->S[st->j] = tmp;
+ tmp = (st->S[st->i] + st->S[st->j]) & 0xff;
+
+ return st->S[tmp];
+}
+
+/*
+ The following two functions can be used for reliable encryption and
+ decryption. Known plaintext attacks are prevented by adding some (6,
+ by default) random bytes to the password before setting up the state
+ structures. These 6 bytes are also saved in the results, because of
+ course we'll need them in arc_decode().
+
+ Because the length of the resulting string is unknown to the caller,
+ it should pass a char**. Since the encode/decode functions allocate
+ memory for the string, make sure the char** points at a NULL-pointer
+ (or at least to something you already free()d), or you'll leak
+ memory. And of course, don't forget to free() the result when you
+ don't need it anymore.
+
+ Both functions return the number of bytes in the result string.
+
+ Note that if you use the pad_to argument, you will need zero-termi-
+ nation to find back the original string length after decryption. So
+ it shouldn't be used if your string contains \0s by itself!
+*/
+
+int arc_encode( char *clear, int clear_len, unsigned char **crypt, char *password, int pad_to )
+{
+ struct arc_state *st;
+ unsigned char *key;
+ char *padded = NULL;
+ int key_len, i, padded_len;
+
+ key_len = strlen( password ) + ARC_IV_LEN;
+ if( clear_len <= 0 )
+ clear_len = strlen( clear );
+
+ /* Pad the string to the closest multiple of pad_to. This makes it
+ impossible to see the exact length of the password. */
+ if( pad_to > 0 && ( clear_len % pad_to ) > 0 )
+ {
+ padded_len = clear_len + pad_to - ( clear_len % pad_to );
+ padded = g_malloc( padded_len );
+ memcpy( padded, clear, clear_len );
+
+ /* First a \0 and then random data, so we don't have to do
+ anything special when decrypting. */
+ padded[clear_len] = 0;
+ random_bytes( (unsigned char*) padded + clear_len + 1, padded_len - clear_len - 1 );
+
+ clear = padded;
+ clear_len = padded_len;
+ }
+
+ /* Prepare buffers and the key + IV */
+ *crypt = g_malloc( clear_len + ARC_IV_LEN );
+ key = g_malloc( key_len );
+ strcpy( (char*) key, password );
+
+ /* Add the salt. Save it for later (when decrypting) and, of course,
+ add it to the encryption key. */
+ random_bytes( crypt[0], ARC_IV_LEN );
+ memcpy( key + key_len - ARC_IV_LEN, crypt[0], ARC_IV_LEN );
+
+ /* Generate the initial S[] from the IVed key. */
+ st = arc_keymaker( key, key_len, ARC_CYCLES );
+ g_free( key );
+
+ for( i = 0; i < clear_len; i ++ )
+ crypt[0][i+ARC_IV_LEN] = clear[i] ^ arc_getbyte( st );
+
+ g_free( st );
+ g_free( padded );
+
+ return clear_len + ARC_IV_LEN;
+}
+
+int arc_decode( unsigned char *crypt, int crypt_len, char **clear, char *password )
+{
+ struct arc_state *st;
+ unsigned char *key;
+ int key_len, clear_len, i;
+
+ key_len = strlen( password ) + ARC_IV_LEN;
+ clear_len = crypt_len - ARC_IV_LEN;
+
+ if( clear_len < 0 )
+ {
+ *clear = g_strdup( "" );
+ return 0;
+ }
+
+ /* Prepare buffers and the key + IV */
+ *clear = g_malloc( clear_len + 1 );
+ key = g_malloc( key_len );
+ strcpy( (char*) key, password );
+ for( i = 0; i < ARC_IV_LEN; i ++ )
+ key[key_len-ARC_IV_LEN+i] = crypt[i];
+
+ /* Generate the initial S[] from the IVed key. */
+ st = arc_keymaker( key, key_len, ARC_CYCLES );
+ g_free( key );
+
+ for( i = 0; i < clear_len; i ++ )
+ clear[0][i] = crypt[i+ARC_IV_LEN] ^ arc_getbyte( st );
+ clear[0][i] = 0; /* Nice to have for plaintexts. */
+
+ g_free( st );
+
+ return clear_len;
+}
diff --git a/lib/arc.h b/lib/arc.h
new file mode 100644
index 00000000..816fa612
--- /dev/null
+++ b/lib/arc.h
@@ -0,0 +1,40 @@
+/***************************************************************************\
+* *
+* BitlBee - An IRC to IM gateway *
+* Simple (but secure) ArcFour implementation for safer password storage. *
+* *
+* Copyright 2007 Wilmer van der Gaast <wilmer@gaast.net> *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+* *
+* This program is distributed in the hope that it will be useful, *
+* but WITHOUT ANY WARRANTY; without even the implied warranty of *
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+* GNU General Public License for more details. *
+* *
+* You should have received a copy of the GNU General Public License along *
+* with this program; if not, write to the Free Software Foundation, Inc., *
+* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
+* *
+\***************************************************************************/
+
+
+/* See arc.c for more information. */
+
+struct arc_state
+{
+ unsigned char S[256];
+ unsigned char i, j;
+};
+
+#ifndef G_GNUC_MALLOC
+#define G_GNUC_MALLOC
+#endif
+
+G_GNUC_MALLOC struct arc_state *arc_keymaker( unsigned char *key, int kl, int cycles );
+unsigned char arc_getbyte( struct arc_state *st );
+int arc_encode( char *clear, int clear_len, unsigned char **crypt, char *password, int pad_to );
+int arc_decode( unsigned char *crypt, int crypt_len, char **clear, char *password );
diff --git a/lib/base64.c b/lib/base64.c
new file mode 100644
index 00000000..ea0db6b9
--- /dev/null
+++ b/lib/base64.c
@@ -0,0 +1,153 @@
+/***************************************************************************\
+* *
+* BitlBee - An IRC to IM gateway *
+* Base64 handling functions. encode_real() is mostly based on the y64 en- *
+* coder from libyahoo2. Moving it to a new file because it's getting big. *
+* *
+* Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net> *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+* *
+* This program is distributed in the hope that it will be useful, *
+* but WITHOUT ANY WARRANTY; without even the implied warranty of *
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+* GNU General Public License for more details. *
+* *
+* You should have received a copy of the GNU General Public License along *
+* with this program; if not, write to the Free Software Foundation, Inc., *
+* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
+* *
+\***************************************************************************/
+
+#include <glib.h>
+#include <string.h>
+#include "base64.h"
+
+static const char real_b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+
+char *tobase64(const char *text)
+{
+ return base64_encode((const unsigned char *)text, strlen(text));
+}
+
+char *base64_encode(const unsigned char *in, int len)
+{
+ char *out;
+
+ out = g_malloc((len + 2) /* the == padding */
+ / 3 /* every 3-byte block */
+ * 4 /* becomes a 4-byte one */
+ + 1); /* and of course, ASCIIZ! */
+
+ base64_encode_real((unsigned char*) in, len, (unsigned char*) out, real_b64);
+
+ return out;
+}
+
+int base64_encode_real(const unsigned char *in, int inlen, unsigned char *out, const char *b64digits)
+{
+ int outlen = 0;
+
+ for (; inlen >= 3; inlen -= 3)
+ {
+ out[outlen++] = b64digits[in[0] >> 2];
+ out[outlen++] = b64digits[((in[0]<<4) & 0x30) | (in[1]>>4)];
+ out[outlen++] = b64digits[((in[1]<<2) & 0x3c) | (in[2]>>6)];
+ out[outlen++] = b64digits[in[2] & 0x3f];
+ in += 3;
+ }
+ if (inlen > 0)
+ {
+ out[outlen++] = b64digits[in[0] >> 2];
+ if (inlen > 1)
+ {
+ out[outlen++] = b64digits[((in[0]<<4) & 0x30) | (in[1]>>4)];
+ out[outlen++] = b64digits[((in[1]<<2) & 0x3c)];
+ }
+ else
+ {
+ out[outlen++] = b64digits[((in[0]<<4) & 0x30)];
+ out[outlen++] = b64digits[64];
+ }
+ out[outlen++] = b64digits[64];
+ }
+ out[outlen] = 0;
+
+ return outlen;
+}
+
+/* Just a simple wrapper, but usually not very convenient because of zero
+ termination. */
+char *frombase64(const char *in)
+{
+ unsigned char *out;
+
+ base64_decode(in, &out);
+
+ return (char*) out;
+}
+
+/* FIXME: Lookup table stuff is not threadsafe! (But for now BitlBee is not threaded.) */
+int base64_decode(const char *in, unsigned char **out)
+{
+ static char b64rev[256] = { 0 };
+ int len, i;
+
+ /* Create a reverse-lookup for the Base64 sequence. */
+ if( b64rev[0] == 0 )
+ {
+ memset( b64rev, 0xff, 256 );
+ for( i = 0; i <= 64; i ++ )
+ b64rev[(int)real_b64[i]] = i;
+ }
+
+ len = strlen( in );
+ *out = g_malloc( ( len + 6 ) / 4 * 3 );
+ len = base64_decode_real( (unsigned char*) in, *out, b64rev );
+ *out = g_realloc( *out, len + 1 );
+ out[0][len] = 0; /* Zero termination can't hurt. */
+
+ return len;
+}
+
+int base64_decode_real(const unsigned char *in, unsigned char *out, char *b64rev)
+{
+ int i, outlen = 0;
+
+ for( i = 0; in[i] && in[i+1] && in[i+2] && in[i+3]; i += 4 )
+ {
+ int sx;
+
+ sx = b64rev[(int)in[i+0]];
+ if( sx >= 64 )
+ break;
+ out[outlen] = ( sx << 2 ) & 0xfc;
+
+ sx = b64rev[(int)in[i+1]];
+ if( sx >= 64 )
+ break;
+ out[outlen] |= ( sx >> 4 ) & 0x03;
+ outlen ++;
+ out[outlen] = ( sx << 4 ) & 0xf0;
+
+ sx = b64rev[(int)in[i+2]];
+ if( sx >= 64 )
+ break;
+ out[outlen] |= ( sx >> 2 ) & 0x0f;
+ outlen ++;
+ out[outlen] = ( sx << 6 ) & 0xc0;
+
+ sx = b64rev[(int)in[i+3]];
+ if( sx >= 64 )
+ break;
+ out[outlen] |= sx;
+ outlen ++;
+ }
+
+ /* If sx > 64 the base64 string was damaged. Should we ignore this? */
+
+ return outlen;
+}
diff --git a/lib/base64.h b/lib/base64.h
new file mode 100644
index 00000000..ebd74bf1
--- /dev/null
+++ b/lib/base64.h
@@ -0,0 +1,33 @@
+/***************************************************************************\
+* *
+* BitlBee - An IRC to IM gateway *
+* Base64 handling functions. encode_real() is mostly based on the y64 en- *
+* coder from libyahoo2. Moving it to a new file because it's getting big. *
+* *
+* Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net> *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+* *
+* This program is distributed in the hope that it will be useful, *
+* but WITHOUT ANY WARRANTY; without even the implied warranty of *
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+* GNU General Public License for more details. *
+* *
+* You should have received a copy of the GNU General Public License along *
+* with this program; if not, write to the Free Software Foundation, Inc., *
+* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
+* *
+\***************************************************************************/
+
+#include <glib.h>
+#include <gmodule.h>
+
+G_MODULE_EXPORT char *tobase64( const char *text );
+G_MODULE_EXPORT char *base64_encode( const unsigned char *in, int len );
+G_MODULE_EXPORT int base64_encode_real( const unsigned char *in, int inlen, unsigned char *out, const char *b64digits );
+G_MODULE_EXPORT char *frombase64( const char *in );
+G_MODULE_EXPORT int base64_decode( const char *in, unsigned char **out );
+G_MODULE_EXPORT int base64_decode_real( const unsigned char *in, unsigned char *out, char *b64reverse );
diff --git a/lib/des.c b/lib/des.c
new file mode 100644
index 00000000..3b9cc8d5
--- /dev/null
+++ b/lib/des.c
@@ -0,0 +1,646 @@
+/*
+ * FIPS-46-3 compliant 3DES implementation
+ *
+ * Copyright (C) 2001-2003 Christophe Devine
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Modified for BitlBee: Added a function compatible with the existing
+ * function in ssl_openssl.c, fairly specialised for MSN auth (since that's
+ * all this is used for at least for now).
+ *
+ * Added some consts to the tables at the top, and disabled some 64-bit
+ * and 128-bit key code that I don't need.
+ *
+ * *Many* thanks to Christophe for this compact and easy to import code.
+ */
+
+#include <string.h>
+#include <glib.h>
+#include "des.h"
+
+/* the eight DES S-boxes */
+
+static const uint32_t SB1[64] =
+{
+ 0x01010400, 0x00000000, 0x00010000, 0x01010404,
+ 0x01010004, 0x00010404, 0x00000004, 0x00010000,
+ 0x00000400, 0x01010400, 0x01010404, 0x00000400,
+ 0x01000404, 0x01010004, 0x01000000, 0x00000004,
+ 0x00000404, 0x01000400, 0x01000400, 0x00010400,
+ 0x00010400, 0x01010000, 0x01010000, 0x01000404,
+ 0x00010004, 0x01000004, 0x01000004, 0x00010004,
+ 0x00000000, 0x00000404, 0x00010404, 0x01000000,
+ 0x00010000, 0x01010404, 0x00000004, 0x01010000,
+ 0x01010400, 0x01000000, 0x01000000, 0x00000400,
+ 0x01010004, 0x00010000, 0x00010400, 0x01000004,
+ 0x00000400, 0x00000004, 0x01000404, 0x00010404,
+ 0x01010404, 0x00010004, 0x01010000, 0x01000404,
+ 0x01000004, 0x00000404, 0x00010404, 0x01010400,
+ 0x00000404, 0x01000400, 0x01000400, 0x00000000,
+ 0x00010004, 0x00010400, 0x00000000, 0x01010004
+};
+
+static const uint32_t SB2[64] =
+{
+ 0x80108020, 0x80008000, 0x00008000, 0x00108020,
+ 0x00100000, 0x00000020, 0x80100020, 0x80008020,
+ 0x80000020, 0x80108020, 0x80108000, 0x80000000,
+ 0x80008000, 0x00100000, 0x00000020, 0x80100020,
+ 0x00108000, 0x00100020, 0x80008020, 0x00000000,
+ 0x80000000, 0x00008000, 0x00108020, 0x80100000,
+ 0x00100020, 0x80000020, 0x00000000, 0x00108000,
+ 0x00008020, 0x80108000, 0x80100000, 0x00008020,
+ 0x00000000, 0x00108020, 0x80100020, 0x00100000,
+ 0x80008020, 0x80100000, 0x80108000, 0x00008000,
+ 0x80100000, 0x80008000, 0x00000020, 0x80108020,
+ 0x00108020, 0x00000020, 0x00008000, 0x80000000,
+ 0x00008020, 0x80108000, 0x00100000, 0x80000020,
+ 0x00100020, 0x80008020, 0x80000020, 0x00100020,
+ 0x00108000, 0x00000000, 0x80008000, 0x00008020,
+ 0x80000000, 0x80100020, 0x80108020, 0x00108000
+};
+
+static const uint32_t SB3[64] =
+{
+ 0x00000208, 0x08020200, 0x00000000, 0x08020008,
+ 0x08000200, 0x00000000, 0x00020208, 0x08000200,
+ 0x00020008, 0x08000008, 0x08000008, 0x00020000,
+ 0x08020208, 0x00020008, 0x08020000, 0x00000208,
+ 0x08000000, 0x00000008, 0x08020200, 0x00000200,
+ 0x00020200, 0x08020000, 0x08020008, 0x00020208,
+ 0x08000208, 0x00020200, 0x00020000, 0x08000208,
+ 0x00000008, 0x08020208, 0x00000200, 0x08000000,
+ 0x08020200, 0x08000000, 0x00020008, 0x00000208,
+ 0x00020000, 0x08020200, 0x08000200, 0x00000000,
+ 0x00000200, 0x00020008, 0x08020208, 0x08000200,
+ 0x08000008, 0x00000200, 0x00000000, 0x08020008,
+ 0x08000208, 0x00020000, 0x08000000, 0x08020208,
+ 0x00000008, 0x00020208, 0x00020200, 0x08000008,
+ 0x08020000, 0x08000208, 0x00000208, 0x08020000,
+ 0x00020208, 0x00000008, 0x08020008, 0x00020200
+};
+
+static const uint32_t SB4[64] =
+{
+ 0x00802001, 0x00002081, 0x00002081, 0x00000080,
+ 0x00802080, 0x00800081, 0x00800001, 0x00002001,
+ 0x00000000, 0x00802000, 0x00802000, 0x00802081,
+ 0x00000081, 0x00000000, 0x00800080, 0x00800001,
+ 0x00000001, 0x00002000, 0x00800000, 0x00802001,
+ 0x00000080, 0x00800000, 0x00002001, 0x00002080,
+ 0x00800081, 0x00000001, 0x00002080, 0x00800080,
+ 0x00002000, 0x00802080, 0x00802081, 0x00000081,
+ 0x00800080, 0x00800001, 0x00802000, 0x00802081,
+ 0x00000081, 0x00000000, 0x00000000, 0x00802000,
+ 0x00002080, 0x00800080, 0x00800081, 0x00000001,
+ 0x00802001, 0x00002081, 0x00002081, 0x00000080,
+ 0x00802081, 0x00000081, 0x00000001, 0x00002000,
+ 0x00800001, 0x00002001, 0x00802080, 0x00800081,
+ 0x00002001, 0x00002080, 0x00800000, 0x00802001,
+ 0x00000080, 0x00800000, 0x00002000, 0x00802080
+};
+
+static const uint32_t SB5[64] =
+{
+ 0x00000100, 0x02080100, 0x02080000, 0x42000100,
+ 0x00080000, 0x00000100, 0x40000000, 0x02080000,
+ 0x40080100, 0x00080000, 0x02000100, 0x40080100,
+ 0x42000100, 0x42080000, 0x00080100, 0x40000000,
+ 0x02000000, 0x40080000, 0x40080000, 0x00000000,
+ 0x40000100, 0x42080100, 0x42080100, 0x02000100,
+ 0x42080000, 0x40000100, 0x00000000, 0x42000000,
+ 0x02080100, 0x02000000, 0x42000000, 0x00080100,
+ 0x00080000, 0x42000100, 0x00000100, 0x02000000,
+ 0x40000000, 0x02080000, 0x42000100, 0x40080100,
+ 0x02000100, 0x40000000, 0x42080000, 0x02080100,
+ 0x40080100, 0x00000100, 0x02000000, 0x42080000,
+ 0x42080100, 0x00080100, 0x42000000, 0x42080100,
+ 0x02080000, 0x00000000, 0x40080000, 0x42000000,
+ 0x00080100, 0x02000100, 0x40000100, 0x00080000,
+ 0x00000000, 0x40080000, 0x02080100, 0x40000100
+};
+
+static const uint32_t SB6[64] =
+{
+ 0x20000010, 0x20400000, 0x00004000, 0x20404010,
+ 0x20400000, 0x00000010, 0x20404010, 0x00400000,
+ 0x20004000, 0x00404010, 0x00400000, 0x20000010,
+ 0x00400010, 0x20004000, 0x20000000, 0x00004010,
+ 0x00000000, 0x00400010, 0x20004010, 0x00004000,
+ 0x00404000, 0x20004010, 0x00000010, 0x20400010,
+ 0x20400010, 0x00000000, 0x00404010, 0x20404000,
+ 0x00004010, 0x00404000, 0x20404000, 0x20000000,
+ 0x20004000, 0x00000010, 0x20400010, 0x00404000,
+ 0x20404010, 0x00400000, 0x00004010, 0x20000010,
+ 0x00400000, 0x20004000, 0x20000000, 0x00004010,
+ 0x20000010, 0x20404010, 0x00404000, 0x20400000,
+ 0x00404010, 0x20404000, 0x00000000, 0x20400010,
+ 0x00000010, 0x00004000, 0x20400000, 0x00404010,
+ 0x00004000, 0x00400010, 0x20004010, 0x00000000,
+ 0x20404000, 0x20000000, 0x00400010, 0x20004010
+};
+
+static const uint32_t SB7[64] =
+{
+ 0x00200000, 0x04200002, 0x04000802, 0x00000000,
+ 0x00000800, 0x04000802, 0x00200802, 0x04200800,
+ 0x04200802, 0x00200000, 0x00000000, 0x04000002,
+ 0x00000002, 0x04000000, 0x04200002, 0x00000802,
+ 0x04000800, 0x00200802, 0x00200002, 0x04000800,
+ 0x04000002, 0x04200000, 0x04200800, 0x00200002,
+ 0x04200000, 0x00000800, 0x00000802, 0x04200802,
+ 0x00200800, 0x00000002, 0x04000000, 0x00200800,
+ 0x04000000, 0x00200800, 0x00200000, 0x04000802,
+ 0x04000802, 0x04200002, 0x04200002, 0x00000002,
+ 0x00200002, 0x04000000, 0x04000800, 0x00200000,
+ 0x04200800, 0x00000802, 0x00200802, 0x04200800,
+ 0x00000802, 0x04000002, 0x04200802, 0x04200000,
+ 0x00200800, 0x00000000, 0x00000002, 0x04200802,
+ 0x00000000, 0x00200802, 0x04200000, 0x00000800,
+ 0x04000002, 0x04000800, 0x00000800, 0x00200002
+};
+
+static const uint32_t SB8[64] =
+{
+ 0x10001040, 0x00001000, 0x00040000, 0x10041040,
+ 0x10000000, 0x10001040, 0x00000040, 0x10000000,
+ 0x00040040, 0x10040000, 0x10041040, 0x00041000,
+ 0x10041000, 0x00041040, 0x00001000, 0x00000040,
+ 0x10040000, 0x10000040, 0x10001000, 0x00001040,
+ 0x00041000, 0x00040040, 0x10040040, 0x10041000,
+ 0x00001040, 0x00000000, 0x00000000, 0x10040040,
+ 0x10000040, 0x10001000, 0x00041040, 0x00040000,
+ 0x00041040, 0x00040000, 0x10041000, 0x00001000,
+ 0x00000040, 0x10040040, 0x00001000, 0x00041040,
+ 0x10001000, 0x00000040, 0x10000040, 0x10040000,
+ 0x10040040, 0x10000000, 0x00040000, 0x10001040,
+ 0x00000000, 0x10041040, 0x00040040, 0x10000040,
+ 0x10040000, 0x10001000, 0x10001040, 0x00000000,
+ 0x10041040, 0x00041000, 0x00041000, 0x00001040,
+ 0x00001040, 0x00040040, 0x10000000, 0x10041000
+};
+
+/* PC1: left and right halves bit-swap */
+
+static const uint32_t LHs[16] =
+{
+ 0x00000000, 0x00000001, 0x00000100, 0x00000101,
+ 0x00010000, 0x00010001, 0x00010100, 0x00010101,
+ 0x01000000, 0x01000001, 0x01000100, 0x01000101,
+ 0x01010000, 0x01010001, 0x01010100, 0x01010101
+};
+
+static const uint32_t RHs[16] =
+{
+ 0x00000000, 0x01000000, 0x00010000, 0x01010000,
+ 0x00000100, 0x01000100, 0x00010100, 0x01010100,
+ 0x00000001, 0x01000001, 0x00010001, 0x01010001,
+ 0x00000101, 0x01000101, 0x00010101, 0x01010101,
+};
+
+/* platform-independant 32-bit integer manipulation macros */
+
+#define GET_UINT32(n,b,i) \
+{ \
+ (n) = ( (uint32_t) (b)[(i) ] << 24 ) \
+ | ( (uint32_t) (b)[(i) + 1] << 16 ) \
+ | ( (uint32_t) (b)[(i) + 2] << 8 ) \
+ | ( (uint32_t) (b)[(i) + 3] ); \
+}
+
+#define PUT_UINT32(n,b,i) \
+{ \
+ (b)[(i) ] = (uint8_t) ( (n) >> 24 ); \
+ (b)[(i) + 1] = (uint8_t) ( (n) >> 16 ); \
+ (b)[(i) + 2] = (uint8_t) ( (n) >> 8 ); \
+ (b)[(i) + 3] = (uint8_t) ( (n) ); \
+}
+
+/* Initial Permutation macro */
+
+#define DES_IP(X,Y) \
+{ \
+ T = ((X >> 4) ^ Y) & 0x0F0F0F0F; Y ^= T; X ^= (T << 4); \
+ T = ((X >> 16) ^ Y) & 0x0000FFFF; Y ^= T; X ^= (T << 16); \
+ T = ((Y >> 2) ^ X) & 0x33333333; X ^= T; Y ^= (T << 2); \
+ T = ((Y >> 8) ^ X) & 0x00FF00FF; X ^= T; Y ^= (T << 8); \
+ Y = ((Y << 1) | (Y >> 31)) & 0xFFFFFFFF; \
+ T = (X ^ Y) & 0xAAAAAAAA; Y ^= T; X ^= T; \
+ X = ((X << 1) | (X >> 31)) & 0xFFFFFFFF; \
+}
+
+/* Final Permutation macro */
+
+#define DES_FP(X,Y) \
+{ \
+ X = ((X << 31) | (X >> 1)) & 0xFFFFFFFF; \
+ T = (X ^ Y) & 0xAAAAAAAA; X ^= T; Y ^= T; \
+ Y = ((Y << 31) | (Y >> 1)) & 0xFFFFFFFF; \
+ T = ((Y >> 8) ^ X) & 0x00FF00FF; X ^= T; Y ^= (T << 8); \
+ T = ((Y >> 2) ^ X) & 0x33333333; X ^= T; Y ^= (T << 2); \
+ T = ((X >> 16) ^ Y) & 0x0000FFFF; Y ^= T; X ^= (T << 16); \
+ T = ((X >> 4) ^ Y) & 0x0F0F0F0F; Y ^= T; X ^= (T << 4); \
+}
+
+/* DES round macro */
+
+#define DES_ROUND(X,Y) \
+{ \
+ T = *SK++ ^ X; \
+ Y ^= SB8[ (T ) & 0x3F ] ^ \
+ SB6[ (T >> 8) & 0x3F ] ^ \
+ SB4[ (T >> 16) & 0x3F ] ^ \
+ SB2[ (T >> 24) & 0x3F ]; \
+ \
+ T = *SK++ ^ ((X << 28) | (X >> 4)); \
+ Y ^= SB7[ (T ) & 0x3F ] ^ \
+ SB5[ (T >> 8) & 0x3F ] ^ \
+ SB3[ (T >> 16) & 0x3F ] ^ \
+ SB1[ (T >> 24) & 0x3F ]; \
+}
+
+/* DES key schedule */
+
+int des_main_ks( uint32_t SK[32], const uint8_t key[8] )
+{
+ int i;
+ uint32_t X, Y, T;
+
+ GET_UINT32( X, key, 0 );
+ GET_UINT32( Y, key, 4 );
+
+ /* Permuted Choice 1 */
+
+ T = ((Y >> 4) ^ X) & 0x0F0F0F0F; X ^= T; Y ^= (T << 4);
+ T = ((Y ) ^ X) & 0x10101010; X ^= T; Y ^= (T );
+
+ X = (LHs[ (X ) & 0xF] << 3) | (LHs[ (X >> 8) & 0xF ] << 2)
+ | (LHs[ (X >> 16) & 0xF] << 1) | (LHs[ (X >> 24) & 0xF ] )
+ | (LHs[ (X >> 5) & 0xF] << 7) | (LHs[ (X >> 13) & 0xF ] << 6)
+ | (LHs[ (X >> 21) & 0xF] << 5) | (LHs[ (X >> 29) & 0xF ] << 4);
+
+ Y = (RHs[ (Y >> 1) & 0xF] << 3) | (RHs[ (Y >> 9) & 0xF ] << 2)
+ | (RHs[ (Y >> 17) & 0xF] << 1) | (RHs[ (Y >> 25) & 0xF ] )
+ | (RHs[ (Y >> 4) & 0xF] << 7) | (RHs[ (Y >> 12) & 0xF ] << 6)
+ | (RHs[ (Y >> 20) & 0xF] << 5) | (RHs[ (Y >> 28) & 0xF ] << 4);
+
+ X &= 0x0FFFFFFF;
+ Y &= 0x0FFFFFFF;
+
+ /* calculate subkeys */
+
+ for( i = 0; i < 16; i++ )
+ {
+ if( i < 2 || i == 8 || i == 15 )
+ {
+ X = ((X << 1) | (X >> 27)) & 0x0FFFFFFF;
+ Y = ((Y << 1) | (Y >> 27)) & 0x0FFFFFFF;
+ }
+ else
+ {
+ X = ((X << 2) | (X >> 26)) & 0x0FFFFFFF;
+ Y = ((Y << 2) | (Y >> 26)) & 0x0FFFFFFF;
+ }
+
+ *SK++ = ((X << 4) & 0x24000000) | ((X << 28) & 0x10000000)
+ | ((X << 14) & 0x08000000) | ((X << 18) & 0x02080000)
+ | ((X << 6) & 0x01000000) | ((X << 9) & 0x00200000)
+ | ((X >> 1) & 0x00100000) | ((X << 10) & 0x00040000)
+ | ((X << 2) & 0x00020000) | ((X >> 10) & 0x00010000)
+ | ((Y >> 13) & 0x00002000) | ((Y >> 4) & 0x00001000)
+ | ((Y << 6) & 0x00000800) | ((Y >> 1) & 0x00000400)
+ | ((Y >> 14) & 0x00000200) | ((Y ) & 0x00000100)
+ | ((Y >> 5) & 0x00000020) | ((Y >> 10) & 0x00000010)
+ | ((Y >> 3) & 0x00000008) | ((Y >> 18) & 0x00000004)
+ | ((Y >> 26) & 0x00000002) | ((Y >> 24) & 0x00000001);
+
+ *SK++ = ((X << 15) & 0x20000000) | ((X << 17) & 0x10000000)
+ | ((X << 10) & 0x08000000) | ((X << 22) & 0x04000000)
+ | ((X >> 2) & 0x02000000) | ((X << 1) & 0x01000000)
+ | ((X << 16) & 0x00200000) | ((X << 11) & 0x00100000)
+ | ((X << 3) & 0x00080000) | ((X >> 6) & 0x00040000)
+ | ((X << 15) & 0x00020000) | ((X >> 4) & 0x00010000)
+ | ((Y >> 2) & 0x00002000) | ((Y << 8) & 0x00001000)
+ | ((Y >> 14) & 0x00000808) | ((Y >> 9) & 0x00000400)
+ | ((Y ) & 0x00000200) | ((Y << 7) & 0x00000100)
+ | ((Y >> 7) & 0x00000020) | ((Y >> 3) & 0x00000011)
+ | ((Y << 2) & 0x00000004) | ((Y >> 21) & 0x00000002);
+ }
+
+ return( 0 );
+}
+
+#if TEST
+int des_set_key( des_context *ctx, uint8_t key[8] )
+{
+ int i;
+
+ /* setup encryption subkeys */
+
+ des_main_ks( ctx->esk, key );
+
+ /* setup decryption subkeys */
+
+ for( i = 0; i < 32; i += 2 )
+ {
+ ctx->dsk[i ] = ctx->esk[30 - i];
+ ctx->dsk[i + 1] = ctx->esk[31 - i];
+ }
+
+ return( 0 );
+}
+
+/* DES 64-bit block encryption/decryption */
+
+void des_crypt( uint32_t SK[32], uint8_t input[8], uint8_t output[8] )
+{
+ uint32_t X, Y, T;
+
+ GET_UINT32( X, input, 0 );
+ GET_UINT32( Y, input, 4 );
+
+ DES_IP( X, Y );
+
+ DES_ROUND( Y, X ); DES_ROUND( X, Y );
+ DES_ROUND( Y, X ); DES_ROUND( X, Y );
+ DES_ROUND( Y, X ); DES_ROUND( X, Y );
+ DES_ROUND( Y, X ); DES_ROUND( X, Y );
+ DES_ROUND( Y, X ); DES_ROUND( X, Y );
+ DES_ROUND( Y, X ); DES_ROUND( X, Y );
+ DES_ROUND( Y, X ); DES_ROUND( X, Y );
+ DES_ROUND( Y, X ); DES_ROUND( X, Y );
+
+ DES_FP( Y, X );
+
+ PUT_UINT32( Y, output, 0 );
+ PUT_UINT32( X, output, 4 );
+}
+
+void des_encrypt( des_context *ctx, uint8_t input[8], uint8_t output[8] )
+{
+ des_crypt( ctx->esk, input, output );
+}
+
+void des_decrypt( des_context *ctx, uint8_t input[8], uint8_t output[8] )
+{
+ des_crypt( ctx->dsk, input, output );
+}
+
+/* Triple-DES key schedule */
+
+int des3_set_2keys( des3_context *ctx, const uint8_t key1[8], const uint8_t key2[8] )
+{
+ int i;
+
+ des_main_ks( ctx->esk , key1 );
+ des_main_ks( ctx->dsk + 32, key2 );
+
+ for( i = 0; i < 32; i += 2 )
+ {
+ ctx->dsk[i ] = ctx->esk[30 - i];
+ ctx->dsk[i + 1] = ctx->esk[31 - i];
+
+ ctx->esk[i + 32] = ctx->dsk[62 - i];
+ ctx->esk[i + 33] = ctx->dsk[63 - i];
+
+ ctx->esk[i + 64] = ctx->esk[ i];
+ ctx->esk[i + 65] = ctx->esk[ 1 + i];
+
+ ctx->dsk[i + 64] = ctx->dsk[ i];
+ ctx->dsk[i + 65] = ctx->dsk[ 1 + i];
+ }
+
+ return( 0 );
+}
+#endif
+
+int des3_set_3keys( des3_context *ctx, const uint8_t key1[8], const uint8_t key2[8],
+ const uint8_t key3[8] )
+{
+ int i;
+
+ des_main_ks( ctx->esk , key1 );
+ des_main_ks( ctx->dsk + 32, key2 );
+ des_main_ks( ctx->esk + 64, key3 );
+
+ for( i = 0; i < 32; i += 2 )
+ {
+ ctx->dsk[i ] = ctx->esk[94 - i];
+ ctx->dsk[i + 1] = ctx->esk[95 - i];
+
+ ctx->esk[i + 32] = ctx->dsk[62 - i];
+ ctx->esk[i + 33] = ctx->dsk[63 - i];
+
+ ctx->dsk[i + 64] = ctx->esk[30 - i];
+ ctx->dsk[i + 65] = ctx->esk[31 - i];
+ }
+
+ return( 0 );
+}
+
+/* Triple-DES 64-bit block encryption/decryption */
+
+void des3_crypt( uint32_t SK[96], uint8_t input[8], uint8_t output[8] )
+{
+ uint32_t X, Y, T;
+
+ GET_UINT32( X, input, 0 );
+ GET_UINT32( Y, input, 4 );
+
+ DES_IP( X, Y );
+
+ DES_ROUND( Y, X ); DES_ROUND( X, Y );
+ DES_ROUND( Y, X ); DES_ROUND( X, Y );
+ DES_ROUND( Y, X ); DES_ROUND( X, Y );
+ DES_ROUND( Y, X ); DES_ROUND( X, Y );
+ DES_ROUND( Y, X ); DES_ROUND( X, Y );
+ DES_ROUND( Y, X ); DES_ROUND( X, Y );
+ DES_ROUND( Y, X ); DES_ROUND( X, Y );
+ DES_ROUND( Y, X ); DES_ROUND( X, Y );
+
+ DES_ROUND( X, Y ); DES_ROUND( Y, X );
+ DES_ROUND( X, Y ); DES_ROUND( Y, X );
+ DES_ROUND( X, Y ); DES_ROUND( Y, X );
+ DES_ROUND( X, Y ); DES_ROUND( Y, X );
+ DES_ROUND( X, Y ); DES_ROUND( Y, X );
+ DES_ROUND( X, Y ); DES_ROUND( Y, X );
+ DES_ROUND( X, Y ); DES_ROUND( Y, X );
+ DES_ROUND( X, Y ); DES_ROUND( Y, X );
+
+ DES_ROUND( Y, X ); DES_ROUND( X, Y );
+ DES_ROUND( Y, X ); DES_ROUND( X, Y );
+ DES_ROUND( Y, X ); DES_ROUND( X, Y );
+ DES_ROUND( Y, X ); DES_ROUND( X, Y );
+ DES_ROUND( Y, X ); DES_ROUND( X, Y );
+ DES_ROUND( Y, X ); DES_ROUND( X, Y );
+ DES_ROUND( Y, X ); DES_ROUND( X, Y );
+ DES_ROUND( Y, X ); DES_ROUND( X, Y );
+
+ DES_FP( Y, X );
+
+ PUT_UINT32( Y, output, 0 );
+ PUT_UINT32( X, output, 4 );
+}
+
+void des3_encrypt( des3_context *ctx, uint8_t input[8], uint8_t output[8] )
+{
+ des3_crypt( ctx->esk, input, output );
+}
+
+void des3_decrypt( des3_context *ctx, uint8_t input[8], uint8_t output[8] )
+{
+ des3_crypt( ctx->dsk, input, output );
+}
+
+size_t ssl_des3_encrypt( const unsigned char *key, size_t key_len, const unsigned char *input,
+ size_t input_len, const unsigned char *iv, unsigned char **res )
+{
+ des3_context ctx3;
+ size_t off;
+ uint8_t buf[8];
+
+ /* Keep it simple, for as long as this is just used for MSN auth anyway. */
+ if( key_len != 24 || ( input_len % 8 ) != 0 )
+ return 0;
+
+ *res = g_malloc( input_len );
+ des3_set_3keys( &ctx3, key, key + 8, key + 16 );
+
+ /* This loop does CBC 3DES. */
+ memcpy( buf, iv, 8 );
+ for( off = 0; off < input_len; off += 8 )
+ {
+ int i;
+
+ for( i = 0; i < 8; i ++ )
+ buf[i] ^= input[off+i];
+ des3_encrypt( &ctx3, buf, buf );
+ memcpy( *res + off, buf, 8 );
+ }
+
+ return input_len;
+}
+
+#ifdef TEST
+
+#include <string.h>
+#include <stdio.h>
+
+/*
+ * Triple-DES Monte Carlo Test: ECB mode
+ * source: NIST - tripledes-vectors.zip
+ */
+
+static const unsigned char DES3_keys[3][8] =
+{
+ { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF },
+ { 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01 },
+ { 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0x01, 0x23 }
+};
+
+static const unsigned char DES3_init[8] =
+{
+ 0x4E, 0x6F, 0x77, 0x20, 0x69, 0x73, 0x20, 0x74
+};
+
+static const unsigned char DES3_enc_test[3][8] =
+{
+ { 0x6A, 0x2A, 0x19, 0xF4, 0x1E, 0xCA, 0x85, 0x4B },
+ { 0x03, 0xE6, 0x9F, 0x5B, 0xFA, 0x58, 0xEB, 0x42 },
+ { 0xDD, 0x17, 0xE8, 0xB8, 0xB4, 0x37, 0xD2, 0x32 }
+};
+
+static const unsigned char DES3_dec_test[3][8] =
+{
+ { 0xCD, 0xD6, 0x4F, 0x2F, 0x94, 0x27, 0xC1, 0x5D },
+ { 0x69, 0x96, 0xC8, 0xFA, 0x47, 0xA2, 0xAB, 0xEB },
+ { 0x83, 0x25, 0x39, 0x76, 0x44, 0x09, 0x1A, 0x0A }
+};
+
+int main( void )
+{
+ int m, n, i;
+ des_context ctx;
+ des3_context ctx3;
+ unsigned char buf[8];
+
+ for( m = 0; m < 2; m++ )
+ {
+ printf( "\n Triple-DES Monte Carlo Test (ECB mode) - " );
+
+ if( m == 0 ) printf( "encryption\n\n" );
+ if( m == 1 ) printf( "decryption\n\n" );
+
+ for( n = 0; n < 3; n++ )
+ {
+ printf( " Test %d, key size = %3d bits: ",
+ n + 1, 64 + n * 64 );
+
+ fflush( stdout );
+
+ memcpy( buf, DES3_init, 8 );
+
+ switch( n )
+ {
+ case 0:
+ des_set_key( &ctx, DES3_keys[0] );
+ break;
+
+ case 1:
+ des3_set_2keys( &ctx3, DES3_keys[0],
+ DES3_keys[1] );
+ break;
+
+ case 2:
+ des3_set_3keys( &ctx3, DES3_keys[0],
+ DES3_keys[1],
+ DES3_keys[2] );
+ break;
+ }
+
+ for( i = 0; i < 10000; i++ )
+ {
+ if( n == 0 )
+ {
+ if( m == 0 ) des_encrypt( &ctx, buf, buf );
+ if( m == 1 ) des_decrypt( &ctx, buf, buf );
+ }
+ else
+ {
+ if( m == 0 ) des3_encrypt( &ctx3, buf, buf );
+ if( m == 1 ) des3_decrypt( &ctx3, buf, buf );
+ }
+ }
+
+ if( ( m == 0 && memcmp( buf, DES3_enc_test[n], 8 ) ) ||
+ ( m == 1 && memcmp( buf, DES3_dec_test[n], 8 ) ) )
+ {
+ printf( "failed!\n" );
+ return( 1 );
+ }
+
+ printf( "passed.\n" );
+ }
+ }
+
+ printf( "\n" );
+
+ return( 0 );
+}
+
+#endif
diff --git a/lib/des.h b/lib/des.h
new file mode 100644
index 00000000..92fbfd22
--- /dev/null
+++ b/lib/des.h
@@ -0,0 +1,51 @@
+/*
+ * FIPS-46-3 compliant 3DES implementation
+ *
+ * Copyright (C) 2001-2003 Christophe Devine
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _DES_H
+#define _DES_H
+
+#include <stdint.h>
+
+typedef struct
+{
+ uint32_t esk[32]; /* DES encryption subkeys */
+ uint32_t dsk[32]; /* DES decryption subkeys */
+}
+des_context;
+
+typedef struct
+{
+ uint32_t esk[96]; /* Triple-DES encryption subkeys */
+ uint32_t dsk[96]; /* Triple-DES decryption subkeys */
+}
+des3_context;
+
+int des_set_key( des_context *ctx, uint8_t key[8] );
+void des_encrypt( des_context *ctx, uint8_t input[8], uint8_t output[8] );
+void des_decrypt( des_context *ctx, uint8_t input[8], uint8_t output[8] );
+
+int des3_set_2keys( des3_context *ctx, const uint8_t key1[8], const uint8_t key2[8] );
+int des3_set_3keys( des3_context *ctx, const uint8_t key1[8], const uint8_t key2[8],
+ const uint8_t key3[8] );
+
+void des3_encrypt( des3_context *ctx, uint8_t input[8], uint8_t output[8] );
+void des3_decrypt( des3_context *ctx, uint8_t input[8], uint8_t output[8] );
+
+#endif /* des.h */
diff --git a/lib/events.h b/lib/events.h
new file mode 100644
index 00000000..66c4c6b4
--- /dev/null
+++ b/lib/events.h
@@ -0,0 +1,87 @@
+/*
+ * nogaim
+ *
+ * Copyright (C) 2006 Wilmer van der Gaast and others
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* This stuff used to be in proxy.c too, but I split it off so BitlBee can
+ use other libraries (like libevent) to handle events. proxy.c is one very
+ nice piece of work from Gaim. It connects to a TCP server in the back-
+ ground and calls a callback function once the connection is ready to use.
+ This function (proxy_connect()) can be found in proxy.c. (It also
+ transparently handles HTTP/SOCKS proxies, when necessary.)
+
+ This file offers some extra event handling toys, which will be handled
+ by GLib or libevent. The advantage of using libevent is that it can use
+ more advanced I/O polling functions like epoll() in recent Linux
+ kernels. This should improve BitlBee's scalability. */
+
+
+#ifndef _EVENTS_H_
+#define _EVENTS_H_
+
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#endif
+#include <glib.h>
+#include <gmodule.h>
+
+/* The conditions you can pass to b_input_add()/that will be passed to
+ the given callback function. */
+typedef enum {
+ B_EV_IO_READ = 1 << 0,
+ B_EV_IO_WRITE = 1 << 1,
+ B_EV_FLAG_FORCE_ONCE = 1 << 16,
+ B_EV_FLAG_FORCE_REPEAT = 1 << 17,
+} b_input_condition;
+typedef gboolean (*b_event_handler)(gpointer data, gint fd, b_input_condition cond);
+
+/* For internal use. */
+#define GAIM_READ_COND (G_IO_IN | G_IO_HUP | G_IO_ERR)
+#define GAIM_WRITE_COND (G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL)
+#define GAIM_ERR_COND (G_IO_HUP | G_IO_ERR | G_IO_NVAL)
+
+/* #define event_debug( x... ) printf( x ) */
+#define event_debug( x... )
+
+/* Call this once when the program starts. It'll initialize the event handler
+ library (if necessary) and then return immediately. */
+G_MODULE_EXPORT void b_main_init();
+
+/* This one enters the event loop. It shouldn't return until one of the event
+ handlers calls b_main_quit(). */
+G_MODULE_EXPORT void b_main_run();
+G_MODULE_EXPORT void b_main_quit();
+
+
+/* Add event handlers (for I/O or a timeout). The event handler will be called
+ every time the event "happens", until your event handler returns FALSE (or
+ until you remove it using b_event_remove(). As usual, the data argument
+ can be used to pass your own data to the event handler. */
+G_MODULE_EXPORT gint b_input_add(int fd, b_input_condition cond, b_event_handler func, gpointer data);
+G_MODULE_EXPORT gint b_timeout_add(gint timeout, b_event_handler func, gpointer data);
+G_MODULE_EXPORT void b_event_remove(gint id);
+
+/* With libevent, this one also cleans up event handlers if that wasn't already
+ done (the caller is expected to do so but may miss it sometimes). */
+G_MODULE_EXPORT void closesocket(int fd);
+
+#endif /* _EVENTS_H_ */
diff --git a/lib/events_glib.c b/lib/events_glib.c
new file mode 100644
index 00000000..3fafc872
--- /dev/null
+++ b/lib/events_glib.c
@@ -0,0 +1,153 @@
+ /********************************************************************\
+ * BitlBee -- An IRC to other IM-networks gateway *
+ * *
+ * Copyright 2002-2006 Wilmer van der Gaast and others *
+ \********************************************************************/
+
+/*
+ * Event handling (using GLib)
+ */
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License with
+ the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+ if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#define BITLBEE_CORE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#else
+#include "sock.h"
+#define ETIMEDOUT WSAETIMEDOUT
+#define EINPROGRESS WSAEINPROGRESS
+#endif
+#include <fcntl.h>
+#include <errno.h>
+#include "proxy.h"
+
+typedef struct _GaimIOClosure {
+ b_event_handler function;
+ gpointer data;
+ guint flags;
+} GaimIOClosure;
+
+static GMainLoop *loop = NULL;
+
+void b_main_init()
+{
+ if( loop == NULL )
+ loop = g_main_new( FALSE );
+}
+
+void b_main_run()
+{
+ g_main_run( loop );
+}
+
+void b_main_quit()
+{
+ g_main_quit( loop );
+}
+
+static gboolean gaim_io_invoke(GIOChannel *source, GIOCondition condition, gpointer data)
+{
+ GaimIOClosure *closure = data;
+ b_input_condition gaim_cond = 0;
+ gboolean st;
+
+ if (condition & GAIM_READ_COND)
+ gaim_cond |= B_EV_IO_READ;
+ if (condition & GAIM_WRITE_COND)
+ gaim_cond |= B_EV_IO_WRITE;
+
+ event_debug( "gaim_io_invoke( %d, %d, 0x%x )\n", g_io_channel_unix_get_fd(source), condition, data );
+
+ st = closure->function(closure->data, g_io_channel_unix_get_fd(source), gaim_cond);
+
+ if( !st )
+ event_debug( "Returned FALSE, cancelling.\n" );
+
+ if (closure->flags & B_EV_FLAG_FORCE_ONCE)
+ return FALSE;
+ else if (closure->flags & B_EV_FLAG_FORCE_REPEAT)
+ return TRUE;
+ else
+ return st;
+}
+
+static void gaim_io_destroy(gpointer data)
+{
+ event_debug( "gaim_io_destroy( 0x%x )\n", data );
+ g_free(data);
+}
+
+gint b_input_add(gint source, b_input_condition condition, b_event_handler function, gpointer data)
+{
+ GaimIOClosure *closure = g_new0(GaimIOClosure, 1);
+ GIOChannel *channel;
+ GIOCondition cond = 0;
+ int st;
+
+ closure->function = function;
+ closure->data = data;
+ closure->flags = condition;
+
+ if (condition & B_EV_IO_READ)
+ cond |= GAIM_READ_COND;
+ if (condition & B_EV_IO_WRITE)
+ cond |= GAIM_WRITE_COND;
+
+ channel = g_io_channel_unix_new(source);
+ st = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, cond,
+ gaim_io_invoke, closure, gaim_io_destroy);
+
+ event_debug( "b_input_add( %d, %d, 0x%x, 0x%x ) = %d (%p)\n", source, condition, function, data, st, closure );
+
+ g_io_channel_unref(channel);
+ return st;
+}
+
+gint b_timeout_add(gint timeout, b_event_handler func, gpointer data)
+{
+ /* GSourceFunc and the BitlBee event handler function aren't
+ really the same, but they're "compatible". ;-) It will do
+ for now, BitlBee only looks at the "data" argument. */
+ gint st = g_timeout_add(timeout, (GSourceFunc) func, data);
+
+ event_debug( "b_timeout_add( %d, %d, %d ) = %d\n", timeout, func, data, st );
+
+ return st;
+}
+
+void b_event_remove(gint tag)
+{
+ event_debug( "b_event_remove( %d )\n", tag );
+
+ if (tag > 0)
+ g_source_remove(tag);
+}
+
+void closesocket( int fd )
+{
+ close( fd );
+}
diff --git a/lib/events_libevent.c b/lib/events_libevent.c
new file mode 100644
index 00000000..43d770ea
--- /dev/null
+++ b/lib/events_libevent.c
@@ -0,0 +1,294 @@
+ /********************************************************************\
+ * BitlBee -- An IRC to other IM-networks gateway *
+ * *
+ * Copyright 2002-2006 Wilmer van der Gaast and others *
+ \********************************************************************/
+
+/*
+ * Event handling (using libevent)
+ */
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License with
+ the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+ if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#define BITLBEE_CORE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <event.h>
+#include "proxy.h"
+
+static void b_main_restart();
+static guint id_next = 1; /* Next ID to be allocated to an event handler. */
+static guint id_cur = 0; /* Event ID that we're currently handling. */
+static guint id_dead; /* Set to 1 if b_event_remove removes id_cur. */
+static GHashTable *id_hash;
+static int quitting = 0; /* Prepare to quit, stop handling events. */
+
+/* Since libevent doesn't handle two event handlers for one fd-condition
+ very well (which happens sometimes when BitlBee changes event handlers
+ for a combination), let's buid some indexes so we can delete them here
+ already, just in time. */
+static GHashTable *read_hash;
+static GHashTable *write_hash;
+
+struct event_base *leh;
+struct event_base *old_leh;
+
+struct b_event_data
+{
+ guint id;
+ struct event evinfo;
+ gint timeout;
+ b_event_handler function;
+ void *data;
+ guint flags;
+};
+
+void b_main_init()
+{
+ if( leh != NULL )
+ {
+ /* Clean up the hash tables? */
+
+ b_main_restart();
+ old_leh = leh;
+ }
+
+ leh = event_init();
+
+ id_hash = g_hash_table_new( g_int_hash, g_int_equal );
+ read_hash = g_hash_table_new( g_int_hash, g_int_equal );
+ write_hash = g_hash_table_new( g_int_hash, g_int_equal );
+}
+
+void b_main_run()
+{
+ /* This while loop is necessary to exit the event loop and start a
+ different one (necessary for ForkDaemon mode). */
+ while( event_base_dispatch( leh ) == 0 && !quitting )
+ {
+ if( old_leh != NULL )
+ {
+ /* For some reason this just isn't allowed...
+ Possibly a bug in older versions, will see later.
+ event_base_free( old_leh ); */
+ old_leh = NULL;
+ }
+
+ event_debug( "New event loop.\n" );
+ }
+}
+
+static void b_main_restart()
+{
+ struct timeval tv;
+
+ memset( &tv, 0, sizeof( struct timeval ) );
+ event_base_loopexit( leh, &tv );
+
+ event_debug( "b_main_restart()\n" );
+}
+
+void b_main_quit()
+{
+ /* Tell b_main_run() that it shouldn't restart the loop. Also,
+ libevent sometimes generates events before really quitting,
+ we want to stop them. */
+ quitting = 1;
+
+ b_main_restart();
+}
+
+static void b_event_passthrough( int fd, short event, void *data )
+{
+ struct b_event_data *b_ev = data;
+ b_input_condition cond = 0;
+ gboolean st;
+
+ if( fd >= 0 )
+ {
+ if( event & EV_READ )
+ cond |= B_EV_IO_READ;
+ if( event & EV_WRITE )
+ cond |= B_EV_IO_WRITE;
+ }
+
+ event_debug( "b_event_passthrough( %d, %d, 0x%x ) (%d)\n", fd, event, (int) data, b_ev->id );
+
+ /* Since the called function might cancel this handler already
+ (which free()s b_ev), we have to remember the ID here. */
+ id_cur = b_ev->id;
+ id_dead = 0;
+
+ if( quitting )
+ {
+ b_event_remove( id_cur );
+ return;
+ }
+
+ st = b_ev->function( b_ev->data, fd, cond );
+ if( id_dead )
+ {
+ /* This event was killed already, don't touch it! */
+ return;
+ }
+ else if( !st && !( b_ev->flags & B_EV_FLAG_FORCE_REPEAT ) )
+ {
+ event_debug( "Handler returned FALSE: " );
+ b_event_remove( id_cur );
+ }
+ else if( fd == -1 )
+ {
+ /* fd == -1 means it was a timer. These can't be auto-repeated
+ so it has to be recreated every time. */
+ struct timeval tv;
+
+ tv.tv_sec = b_ev->timeout / 1000;
+ tv.tv_usec = ( b_ev->timeout % 1000 ) * 1000;
+
+ evtimer_add( &b_ev->evinfo, &tv );
+ }
+}
+
+gint b_input_add( gint fd, b_input_condition condition, b_event_handler function, gpointer data )
+{
+ struct b_event_data *b_ev;
+
+ event_debug( "b_input_add( %d, %d, 0x%x, 0x%x ) ", fd, condition, function, data );
+
+ if( ( condition & B_EV_IO_READ && ( b_ev = g_hash_table_lookup( read_hash, &fd ) ) ) ||
+ ( condition & B_EV_IO_WRITE && ( b_ev = g_hash_table_lookup( write_hash, &fd ) ) ) )
+ {
+ /* We'll stick with this libevent entry, but give it a new BitlBee id. */
+ g_hash_table_remove( id_hash, &b_ev->id );
+
+ event_debug( "(replacing old handler (id = %d)) = %d\n", b_ev->id, id_next );
+
+ b_ev->id = id_next++;
+ b_ev->function = function;
+ b_ev->data = data;
+ }
+ else
+ {
+ GIOCondition out_cond;
+
+ event_debug( "(new) = %d\n", id_next );
+
+ b_ev = g_new0( struct b_event_data, 1 );
+ b_ev->id = id_next++;
+ b_ev->function = function;
+ b_ev->data = data;
+
+ out_cond = EV_PERSIST;
+ if( condition & B_EV_IO_READ )
+ out_cond |= EV_READ;
+ if( condition & B_EV_IO_WRITE )
+ out_cond |= EV_WRITE;
+
+ event_set( &b_ev->evinfo, fd, out_cond, b_event_passthrough, b_ev );
+ event_add( &b_ev->evinfo, NULL );
+
+ if( out_cond & EV_READ )
+ g_hash_table_insert( read_hash, &b_ev->evinfo.ev_fd, b_ev );
+ if( out_cond & EV_WRITE )
+ g_hash_table_insert( write_hash, &b_ev->evinfo.ev_fd, b_ev );
+ }
+
+ b_ev->flags = condition;
+ g_hash_table_insert( id_hash, &b_ev->id, b_ev );
+ return b_ev->id;
+}
+
+/* TODO: Persistence for timers! */
+gint b_timeout_add( gint timeout, b_event_handler function, gpointer data )
+{
+ struct b_event_data *b_ev = g_new0( struct b_event_data, 1 );
+ struct timeval tv;
+
+ b_ev->id = id_next++;
+ b_ev->timeout = timeout;
+ b_ev->function = function;
+ b_ev->data = data;
+
+ tv.tv_sec = timeout / 1000;
+ tv.tv_usec = ( timeout % 1000 ) * 1000;
+
+ evtimer_set( &b_ev->evinfo, b_event_passthrough, b_ev );
+ evtimer_add( &b_ev->evinfo, &tv );
+
+ event_debug( "b_timeout_add( %d, 0x%x, 0x%x ) = %d\n", timeout, function, data, b_ev->id );
+
+ g_hash_table_insert( id_hash, &b_ev->id, b_ev );
+
+ return b_ev->id;
+}
+
+void b_event_remove( gint id )
+{
+ struct b_event_data *b_ev = g_hash_table_lookup( id_hash, &id );
+
+ event_debug( "b_event_remove( %d )\n", id );
+ if( b_ev )
+ {
+ if( id == id_cur )
+ id_dead = TRUE;
+
+ g_hash_table_remove( id_hash, &b_ev->id );
+ if( b_ev->evinfo.ev_fd >= 0 )
+ {
+ if( b_ev->evinfo.ev_events & EV_READ )
+ g_hash_table_remove( read_hash, &b_ev->evinfo.ev_fd );
+ if( b_ev->evinfo.ev_events & EV_WRITE )
+ g_hash_table_remove( write_hash, &b_ev->evinfo.ev_fd );
+ }
+
+ event_del( &b_ev->evinfo );
+ g_free( b_ev );
+ }
+ else
+ {
+ event_debug( "Already removed?\n" );
+ }
+}
+
+void closesocket( int fd )
+{
+ struct b_event_data *b_ev;
+
+ /* Since epoll() (the main reason we use libevent) automatically removes sockets from
+ the epoll() list when a socket gets closed and some modules have a habit of
+ closing sockets before removing event handlers, our and libevent's administration
+ get a little bit messed up. So this little function will remove the handlers
+ properly before closing a socket. */
+
+ if( ( b_ev = g_hash_table_lookup( read_hash, &fd ) ) )
+ {
+ event_debug( "Warning: fd %d still had a read event handler when shutting down.\n", fd );
+ b_event_remove( b_ev->id );
+ }
+ if( ( b_ev = g_hash_table_lookup( write_hash, &fd ) ) )
+ {
+ event_debug( "Warning: fd %d still had a write event handler when shutting down.\n", fd );
+ b_event_remove( b_ev->id );
+ }
+
+ close( fd );
+}
diff --git a/lib/ftutil.c b/lib/ftutil.c
new file mode 100644
index 00000000..71c09b50
--- /dev/null
+++ b/lib/ftutil.c
@@ -0,0 +1,144 @@
+/***************************************************************************\
+* *
+* BitlBee - An IRC to IM gateway *
+* Utility functions for file transfer *
+* *
+* Copyright 2008 Uli Meis <a.sporto+bee@gmail.com> *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+* *
+* This program is distributed in the hope that it will be useful, *
+* but WITHOUT ANY WARRANTY; without even the implied warranty of *
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+* GNU General Public License for more details. *
+* *
+* You should have received a copy of the GNU General Public License along *
+* with this program; if not, write to the Free Software Foundation, Inc., *
+* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
+* *
+\***************************************************************************/
+
+#define BITLBEE_CORE
+#include "bitlbee.h"
+#include <poll.h>
+#include <netinet/tcp.h>
+#include "lib/ftutil.h"
+
+#define ASSERTSOCKOP(op, msg) \
+ if( (op) == -1 ) {\
+ g_snprintf( errmsg, sizeof( errmsg ), msg ": %s", strerror( errno ) ); \
+ return -1; }
+
+/*
+ * Creates a listening socket and returns it in saddr_ptr.
+ */
+int ft_listen( struct sockaddr_storage *saddr_ptr, char *host, char *port, int copy_fd, int for_bitlbee_client, char **errptr )
+{
+ int fd, gret, saddrlen;
+ struct addrinfo hints, *rp;
+ socklen_t ssize = sizeof( struct sockaddr_storage );
+ struct sockaddr_storage saddrs, *saddr = &saddrs;
+ static char errmsg[1024];
+ char *ftlisten = global.conf->ft_listen;
+
+ if( errptr )
+ *errptr = errmsg;
+
+ strcpy( port, "0" );
+
+ /* Format is <IP-A>[:<Port-A>];<IP-B>[:<Port-B>] where
+ * A is for connections with the bitlbee client (DCC)
+ * and B is for connections with IM peers.
+ */
+ if( ftlisten )
+ {
+ char *scolon = strchr( ftlisten, ';' );
+ char *colon;
+
+ if( scolon )
+ {
+ if( for_bitlbee_client )
+ {
+ *scolon = '\0';
+ strncpy( host, ftlisten, HOST_NAME_MAX );
+ *scolon = ';';
+ }
+ else
+ {
+ strncpy( host, scolon + 1, HOST_NAME_MAX );
+ }
+ }
+ else
+ {
+ strncpy( host, ftlisten, HOST_NAME_MAX );
+ }
+
+ if( ( colon = strchr( host, ':' ) ) )
+ {
+ *colon = '\0';
+ strncpy( port, colon + 1, 5 );
+ }
+ }
+ else if( copy_fd >= 0 && getsockname( copy_fd, (struct sockaddr*) &saddrs, &ssize ) == 0 &&
+ ( saddrs.ss_family == AF_INET || saddrs.ss_family == AF_INET6 ) &&
+ getnameinfo( (struct sockaddr*) &saddrs, ssize, host, HOST_NAME_MAX,
+ NULL, 0, NI_NUMERICHOST ) == 0 )
+ {
+ /* We just took our local address on copy_fd, which is likely to be a
+ sensible address from which we can do a file transfer now - the
+ most sensible we can get easily. */
+ }
+ else
+ {
+ ASSERTSOCKOP( gethostname( host, HOST_NAME_MAX + 1 ), "gethostname()" );
+ }
+
+ memset( &hints, 0, sizeof( struct addrinfo ) );
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_NUMERICSERV;
+
+ if ( ( gret = getaddrinfo( host, port, &hints, &rp ) ) != 0 )
+ {
+ sprintf( errmsg, "getaddrinfo() failed: %s", gai_strerror( gret ) );
+ return -1;
+ }
+
+ saddrlen = rp->ai_addrlen;
+
+ memcpy( saddr, rp->ai_addr, saddrlen );
+
+ freeaddrinfo( rp );
+
+ ASSERTSOCKOP( fd = socket( saddr->ss_family, SOCK_STREAM, 0 ), "Opening socket" );
+ ASSERTSOCKOP( bind( fd, ( struct sockaddr *)saddr, saddrlen ), "Binding socket" );
+ ASSERTSOCKOP( listen( fd, 1 ), "Making socket listen" );
+
+ if ( !inet_ntop( saddr->ss_family, saddr->ss_family == AF_INET ?
+ ( void * )&( ( struct sockaddr_in * ) saddr )->sin_addr.s_addr :
+ ( void * )&( ( struct sockaddr_in6 * ) saddr )->sin6_addr.s6_addr,
+ host, HOST_NAME_MAX ) )
+ {
+ strcpy( errmsg, "inet_ntop failed on listening socket" );
+ return -1;
+ }
+
+ ssize = sizeof( struct sockaddr_storage );
+ ASSERTSOCKOP( getsockname( fd, ( struct sockaddr *)saddr, &ssize ), "Getting socket name" );
+
+ if( saddr->ss_family == AF_INET )
+ g_snprintf( port, 6, "%d", ntohs( ( (struct sockaddr_in *) saddr )->sin_port ) );
+ else
+ g_snprintf( port, 6, "%d", ntohs( ( (struct sockaddr_in6 *) saddr )->sin6_port ) );
+
+ if( saddr_ptr )
+ memcpy( saddr_ptr, saddr, saddrlen );
+
+ /* I hate static-length strings.. */
+ host[HOST_NAME_MAX] = '\0';
+ port[5] = '\0';
+
+ return fd;
+}
diff --git a/lib/ftutil.h b/lib/ftutil.h
new file mode 100644
index 00000000..09c1104e
--- /dev/null
+++ b/lib/ftutil.h
@@ -0,0 +1,40 @@
+/***************************************************************************\
+* *
+* BitlBee - An IRC to IM gateway *
+* Utility functions for file transfer *
+* *
+* Copyright 2008 Uli Meis <a.sporto+bee@gmail.com> *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+* *
+* This program is distributed in the hope that it will be useful, *
+* but WITHOUT ANY WARRANTY; without even the implied warranty of *
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+* GNU General Public License for more details. *
+* *
+* You should have received a copy of the GNU General Public License along *
+* with this program; if not, write to the Free Software Foundation, Inc., *
+* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
+* *
+\***************************************************************************/
+
+#ifndef AI_NUMERICSERV
+#define AI_NUMERICSERV 0x0400 /* Don't use name resolution. */
+#endif
+
+/* Some ifdefs for ulibc and apparently also BSD (Thanks to Whoopie) */
+#ifndef HOST_NAME_MAX
+#include <sys/param.h>
+#ifdef MAXHOSTNAMELEN
+#define HOST_NAME_MAX MAXHOSTNAMELEN
+#else
+#define HOST_NAME_MAX 255
+#endif
+#endif
+
+/* This function should be used with care. host should be AT LEAST a
+ char[HOST_NAME_MAX+1] and port AT LEAST a char[6]. */
+int ft_listen( struct sockaddr_storage *saddr_ptr, char *host, char *port, int copy_fd, int for_bitlbee_client, char **errptr );
diff --git a/lib/http_client.c b/lib/http_client.c
new file mode 100644
index 00000000..69f06ec5
--- /dev/null
+++ b/lib/http_client.c
@@ -0,0 +1,456 @@
+ /********************************************************************\
+ * BitlBee -- An IRC to other IM-networks gateway *
+ * *
+ * Copyright 2002-2005 Wilmer van der Gaast and others *
+ \********************************************************************/
+
+/* HTTP(S) module */
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License with
+ the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+ if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <string.h>
+#include <stdio.h>
+
+#include "http_client.h"
+#include "url.h"
+#include "sock.h"
+
+
+static gboolean http_connected( gpointer data, int source, b_input_condition cond );
+static gboolean http_ssl_connected( gpointer data, void *source, b_input_condition cond );
+static gboolean http_incoming_data( gpointer data, int source, b_input_condition cond );
+static void http_free( struct http_request *req );
+
+
+struct http_request *http_dorequest( char *host, int port, int ssl, char *request, http_input_function func, gpointer data )
+{
+ struct http_request *req;
+ int error = 0;
+
+ req = g_new0( struct http_request, 1 );
+
+ if( ssl )
+ {
+ req->ssl = ssl_connect( host, port, http_ssl_connected, req );
+ if( req->ssl == NULL )
+ error = 1;
+ }
+ else
+ {
+ req->fd = proxy_connect( host, port, http_connected, req );
+ if( req->fd < 0 )
+ error = 1;
+ }
+
+ if( error )
+ {
+ http_free( req );
+ return NULL;
+ }
+
+ req->func = func;
+ req->data = data;
+ req->request = g_strdup( request );
+ req->request_length = strlen( request );
+ req->redir_ttl = 3;
+
+ return( req );
+}
+
+struct http_request *http_dorequest_url( char *url_string, http_input_function func, gpointer data )
+{
+ url_t *url = g_new0( url_t, 1 );
+ char *request;
+ void *ret;
+
+ if( !url_set( url, url_string ) )
+ {
+ g_free( url );
+ return NULL;
+ }
+
+ if( url->proto != PROTO_HTTP && url->proto != PROTO_HTTPS )
+ {
+ g_free( url );
+ return NULL;
+ }
+
+ request = g_strdup_printf( "GET %s HTTP/1.0\r\n"
+ "Host: %s\r\n"
+ "User-Agent: BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\r\n"
+ "\r\n", url->file, url->host );
+
+ ret = http_dorequest( url->host, url->port,
+ url->proto == PROTO_HTTPS, request, func, data );
+
+ g_free( url );
+ g_free( request );
+ return ret;
+}
+
+/* This one is actually pretty simple... Might get more calls if we can't write
+ the whole request at once. */
+static gboolean http_connected( gpointer data, int source, b_input_condition cond )
+{
+ struct http_request *req = data;
+ int st;
+
+ if( source < 0 )
+ goto error;
+
+ if( req->inpa > 0 )
+ b_event_remove( req->inpa );
+
+ sock_make_nonblocking( req->fd );
+
+ if( req->ssl )
+ {
+ st = ssl_write( req->ssl, req->request + req->bytes_written,
+ req->request_length - req->bytes_written );
+ if( st < 0 )
+ {
+ if( ssl_errno != SSL_AGAIN )
+ {
+ ssl_disconnect( req->ssl );
+ goto error;
+ }
+ }
+ }
+ else
+ {
+ st = write( source, req->request + req->bytes_written,
+ req->request_length - req->bytes_written );
+ if( st < 0 )
+ {
+ if( !sockerr_again() )
+ {
+ closesocket( req->fd );
+ goto error;
+ }
+ }
+ }
+
+ if( st > 0 )
+ req->bytes_written += st;
+
+ if( req->bytes_written < req->request_length )
+ req->inpa = b_input_add( source,
+ req->ssl ? ssl_getdirection( req->ssl ) : B_EV_IO_WRITE,
+ http_connected, req );
+ else
+ req->inpa = b_input_add( source, B_EV_IO_READ, http_incoming_data, req );
+
+ return FALSE;
+
+error:
+ req->status_string = g_strdup( "Error while writing HTTP request" );
+
+ req->func( req );
+ http_free( req );
+ return FALSE;
+}
+
+static gboolean http_ssl_connected( gpointer data, void *source, b_input_condition cond )
+{
+ struct http_request *req = data;
+
+ if( source == NULL )
+ return http_connected( data, -1, cond );
+
+ req->fd = ssl_getfd( source );
+
+ return http_connected( data, req->fd, cond );
+}
+
+static gboolean http_incoming_data( gpointer data, int source, b_input_condition cond )
+{
+ struct http_request *req = data;
+ int evil_server = 0;
+ char buffer[2048];
+ char *end1, *end2;
+ int st;
+
+ if( req->inpa > 0 )
+ b_event_remove( req->inpa );
+
+ if( req->ssl )
+ {
+ st = ssl_read( req->ssl, buffer, sizeof( buffer ) );
+ if( st < 0 )
+ {
+ if( ssl_errno != SSL_AGAIN )
+ {
+ /* goto cleanup; */
+
+ /* YAY! We have to deal with crappy Microsoft
+ servers that LOVE to send invalid TLS
+ packets that abort connections! \o/ */
+
+ goto got_reply;
+ }
+ }
+ else if( st == 0 )
+ {
+ goto got_reply;
+ }
+ }
+ else
+ {
+ st = read( req->fd, buffer, sizeof( buffer ) );
+ if( st < 0 )
+ {
+ if( !sockerr_again() )
+ {
+ req->status_string = g_strdup( strerror( errno ) );
+ goto cleanup;
+ }
+ }
+ else if( st == 0 )
+ {
+ goto got_reply;
+ }
+ }
+
+ if( st > 0 )
+ {
+ req->reply_headers = g_realloc( req->reply_headers, req->bytes_read + st + 1 );
+ memcpy( req->reply_headers + req->bytes_read, buffer, st );
+ req->bytes_read += st;
+ }
+
+ /* There will be more! */
+ req->inpa = b_input_add( req->fd,
+ req->ssl ? ssl_getdirection( req->ssl ) : B_EV_IO_READ,
+ http_incoming_data, req );
+
+ return FALSE;
+
+got_reply:
+ /* Maybe if the webserver is overloaded, or when there's bad SSL
+ support... */
+ if( req->bytes_read == 0 )
+ {
+ req->status_string = g_strdup( "Empty HTTP reply" );
+ goto cleanup;
+ }
+
+ /* Zero termination is very convenient. */
+ req->reply_headers[req->bytes_read] = 0;
+
+ /* Find the separation between headers and body, and keep stupid
+ webservers in mind. */
+ end1 = strstr( req->reply_headers, "\r\n\r\n" );
+ end2 = strstr( req->reply_headers, "\n\n" );
+
+ if( end2 && end2 < end1 )
+ {
+ end1 = end2 + 1;
+ evil_server = 1;
+ }
+ else if( end1 )
+ {
+ end1 += 2;
+ }
+ else
+ {
+ req->status_string = g_strdup( "Malformed HTTP reply" );
+ goto cleanup;
+ }
+
+ *end1 = 0;
+
+ if( evil_server )
+ req->reply_body = end1 + 1;
+ else
+ req->reply_body = end1 + 2;
+
+ req->body_size = req->reply_headers + req->bytes_read - req->reply_body;
+
+ if( ( end1 = strchr( req->reply_headers, ' ' ) ) != NULL )
+ {
+ if( sscanf( end1 + 1, "%d", &req->status_code ) != 1 )
+ {
+ req->status_string = g_strdup( "Can't parse status code" );
+ req->status_code = -1;
+ }
+ else
+ {
+ char *eol;
+
+ if( evil_server )
+ eol = strchr( end1, '\n' );
+ else
+ eol = strchr( end1, '\r' );
+
+ req->status_string = g_strndup( end1 + 1, eol - end1 - 1 );
+
+ /* Just to be sure... */
+ if( ( eol = strchr( req->status_string, '\r' ) ) )
+ *eol = 0;
+ if( ( eol = strchr( req->status_string, '\n' ) ) )
+ *eol = 0;
+ }
+ }
+ else
+ {
+ req->status_string = g_strdup( "Can't locate status code" );
+ req->status_code = -1;
+ }
+
+ if( ( req->status_code == 301 || req->status_code == 302 ) && req->redir_ttl-- > 0 )
+ {
+ char *loc, *new_request, *new_host;
+ int error = 0, new_port, new_proto;
+
+ /* We might fill it again, so let's not leak any memory. */
+ g_free( req->status_string );
+ req->status_string = NULL;
+
+ loc = strstr( req->reply_headers, "\nLocation: " );
+ if( loc == NULL ) /* We can't handle this redirect... */
+ {
+ req->status_string = g_strdup( "Can't locate Location: header" );
+ goto cleanup;
+ }
+
+ loc += 11;
+ while( *loc == ' ' )
+ loc ++;
+
+ /* TODO/FIXME: Possibly have to handle relative redirections,
+ and rewrite Host: headers. Not necessary for now, it's
+ enough for passport authentication like this. */
+
+ if( *loc == '/' )
+ {
+ /* Just a different pathname... */
+
+ /* Since we don't cache the servername, and since we
+ don't need this yet anyway, I won't implement it. */
+
+ req->status_string = g_strdup( "Can't handle recursive redirects" );
+
+ goto cleanup;
+ }
+ else
+ {
+ /* A whole URL */
+ url_t *url;
+ char *s;
+
+ s = strstr( loc, "\r\n" );
+ if( s == NULL )
+ goto cleanup;
+
+ url = g_new0( url_t, 1 );
+ *s = 0;
+
+ if( !url_set( url, loc ) )
+ {
+ req->status_string = g_strdup( "Malformed redirect URL" );
+ g_free( url );
+ goto cleanup;
+ }
+
+ /* Okay, this isn't fun! We have to rebuild the request... :-( */
+ new_request = g_malloc( req->request_length + strlen( url->file ) );
+
+ /* So, now I just allocated enough memory, so I'm
+ going to use strcat(), whether you like it or not. :-) */
+
+ sprintf( new_request, "GET %s HTTP/1.0", url->file );
+
+ s = strstr( req->request, "\r\n" );
+ if( s == NULL )
+ {
+ req->status_string = g_strdup( "Error while rebuilding request string" );
+ g_free( new_request );
+ g_free( url );
+ goto cleanup;
+ }
+
+ strcat( new_request, s );
+ new_host = g_strdup( url->host );
+ new_port = url->port;
+ new_proto = url->proto;
+
+ g_free( url );
+ }
+
+ if( req->ssl )
+ ssl_disconnect( req->ssl );
+ else
+ closesocket( req->fd );
+
+ req->fd = -1;
+ req->ssl = NULL;
+
+ if( new_proto == PROTO_HTTPS )
+ {
+ req->ssl = ssl_connect( new_host, new_port, http_ssl_connected, req );
+ if( req->ssl == NULL )
+ error = 1;
+ }
+ else
+ {
+ req->fd = proxy_connect( new_host, new_port, http_connected, req );
+ if( req->fd < 0 )
+ error = 1;
+ }
+ g_free( new_host );
+
+ if( error )
+ {
+ req->status_string = g_strdup( "Connection problem during redirect" );
+ g_free( new_request );
+ goto cleanup;
+ }
+
+ g_free( req->request );
+ g_free( req->reply_headers );
+ req->request = new_request;
+ req->request_length = strlen( new_request );
+ req->bytes_read = req->bytes_written = req->inpa = 0;
+ req->reply_headers = req->reply_body = NULL;
+
+ return FALSE;
+ }
+
+ /* Assume that a closed connection means we're finished, this indeed
+ breaks with keep-alive connections and faulty connections. */
+ req->finished = 1;
+
+cleanup:
+ if( req->ssl )
+ ssl_disconnect( req->ssl );
+ else
+ closesocket( req->fd );
+
+ req->func( req );
+ http_free( req );
+ return FALSE;
+}
+
+static void http_free( struct http_request *req )
+{
+ g_free( req->request );
+ g_free( req->reply_headers );
+ g_free( req->status_string );
+ g_free( req );
+}
+
diff --git a/lib/http_client.h b/lib/http_client.h
new file mode 100644
index 00000000..27c484ff
--- /dev/null
+++ b/lib/http_client.h
@@ -0,0 +1,84 @@
+ /********************************************************************\
+ * BitlBee -- An IRC to other IM-networks gateway *
+ * *
+ * Copyright 2002-2005 Wilmer van der Gaast and others *
+ \********************************************************************/
+
+/* HTTP(S) module */
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License with
+ the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+ if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/* http_client allows you to talk (asynchronously, again) to HTTP servers.
+ In the "background" it will send the whole query and wait for a complete
+ response to come back. Right now it's only used by the MSN Passport
+ authentication code, but it might be useful for other things too (for
+ example the AIM usericon patch uses this so icons can be stored on
+ webservers instead of the local filesystem).
+
+ Didn't test this too much, but it seems to work well. Just don't look
+ at the code that handles HTTP 30x redirects. ;-) The function is
+ probably not very useful for downloading lots of data since it keeps
+ everything in a memory buffer until the download is completed (and
+ can't pass any data or whatever before then). It's very useful for
+ doing quick requests without blocking the whole program, though. */
+
+#include <glib.h>
+#include "ssl_client.h"
+
+struct http_request;
+
+/* Your callback function should look like this: */
+typedef void (*http_input_function)( struct http_request * );
+
+/* This structure will be filled in by the http_dorequest* functions, and
+ it will be passed to the callback function. Use the data field to add
+ your own data. */
+struct http_request
+{
+ char *request; /* The request to send to the server. */
+ int request_length; /* Its size. */
+ int status_code; /* The numeric HTTP status code. (Or -1
+ if something really went wrong) */
+ char *status_string; /* The error text. */
+ char *reply_headers;
+ char *reply_body;
+ int body_size; /* The number of bytes in reply_body. */
+ int finished; /* Set to non-0 if the request was completed
+ successfully. */
+ int redir_ttl; /* You can set it to 0 if you don't want
+ http_client to follow them. */
+
+ http_input_function func;
+ gpointer data;
+
+ /* Please don't touch the things down here, you shouldn't need them. */
+
+ void *ssl;
+ int fd;
+
+ int inpa;
+ int bytes_written;
+ int bytes_read;
+};
+
+/* The _url variant is probably more useful than the raw version. The raw
+ version is probably only useful if you want to do POST requests or if
+ you want to add some extra headers. As you can see, HTTPS connections
+ are also supported (using ssl_client). */
+struct http_request *http_dorequest( char *host, int port, int ssl, char *request, http_input_function func, gpointer data );
+struct http_request *http_dorequest_url( char *url_string, http_input_function func, gpointer data );
diff --git a/lib/ini.c b/lib/ini.c
new file mode 100644
index 00000000..aa291bb2
--- /dev/null
+++ b/lib/ini.c
@@ -0,0 +1,145 @@
+ /********************************************************************\
+ * BitlBee -- An IRC to other IM-networks gateway *
+ * *
+ * Copyright 2002-2008 Wilmer van der Gaast and others *
+ \********************************************************************/
+
+/* INI file reading code */
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License with
+ the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+ if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ Suite 330, Boston, MA 02111-1307 USA
+*/
+#define BITLBEE_CORE
+#include "bitlbee.h"
+
+ini_t *ini_open( char *file )
+{
+ int fd;
+ ini_t *ini = NULL;
+ struct stat fi;
+
+ if( ( fd = open( file, O_RDONLY ) ) != -1 &&
+ fstat( fd, &fi ) == 0 &&
+ fi.st_size <= 16384 &&
+ ( ini = g_malloc( sizeof( ini_t ) + fi.st_size + 1 ) ) &&
+ read( fd, ini->file, fi.st_size ) == fi.st_size )
+ {
+ memset( ini, 0, sizeof( ini_t ) );
+ ini->size = fi.st_size;
+ ini->file[ini->size] = 0;
+ ini->cur = ini->file;
+ ini->c_section = "";
+
+ close( fd );
+
+ return ini;
+ }
+
+ if( fd >= 0 )
+ close( fd );
+
+ ini_close( ini );
+
+ return NULL;
+}
+
+/* Strips leading and trailing whitespace and returns a pointer to the first
+ non-ws character of the given string. */
+static char *ini_strip_whitespace( char *in )
+{
+ char *e;
+
+ while( isspace( *in ) )
+ in++;
+
+ e = in + strlen( in ) - 1;
+ while( e > in && isspace( *e ) )
+ e--;
+ e[1] = 0;
+
+ return in;
+}
+
+int ini_read( ini_t *file )
+{
+ char *s;
+
+ while( file->cur && file->cur < file->file + file->size )
+ {
+ char *e, *next;
+
+ file->line++;
+
+ /* Find the end of line */
+ if( ( e = strchr( file->cur, '\n' ) ) != NULL )
+ {
+ *e = 0;
+ next = e + 1;
+ }
+ else
+ {
+ /* No more lines. */
+ e = file->cur + strlen( file->cur );
+ next = NULL;
+ }
+
+ /* Comment? */
+ if( ( s = strchr( file->cur, '#' ) ) != NULL )
+ *s = 0;
+
+ file->cur = ini_strip_whitespace( file->cur );
+
+ if( *file->cur == '[' )
+ {
+ file->cur++;
+ if( ( s = strchr( file->cur, ']' ) ) != NULL )
+ {
+ *s = 0;
+ file->c_section = file->cur;
+ }
+ }
+ else if( ( s = strchr( file->cur, '=' ) ) != NULL )
+ {
+ *s = 0;
+ file->key = ini_strip_whitespace( file->cur );
+ file->value = ini_strip_whitespace( s + 1 );
+
+ if( ( s = strchr( file->key, '.' ) ) != NULL )
+ {
+ *s = 0;
+ file->section = file->key;
+ file->key = s + 1;
+ }
+ else
+ {
+ file->section = file->c_section;
+ }
+
+ file->cur = next;
+ return 1;
+ }
+ /* else: noise/comment/etc, let's just ignore it. */
+
+ file->cur = next;
+ }
+
+ return 0;
+}
+
+void ini_close( ini_t *file )
+{
+ g_free( file );
+}
diff --git a/lib/ini.h b/lib/ini.h
new file mode 100644
index 00000000..6ae0bde5
--- /dev/null
+++ b/lib/ini.h
@@ -0,0 +1,45 @@
+ /********************************************************************\
+ * BitlBee -- An IRC to other IM-networks gateway *
+ * *
+ * Copyright 2002-2004 Wilmer van der Gaast and others *
+ \********************************************************************/
+
+/* INI file reading code */
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License with
+ the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+ if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef _INI_H
+#define _INI_H
+
+typedef struct
+{
+ int line;
+ char *c_section;
+ char *section;
+ char *key;
+ char *value;
+ int size;
+ char *cur, *tok;
+ char file[];
+} ini_t;
+
+ini_t *ini_open( char *file );
+int ini_read( ini_t *file );
+void ini_close( ini_t *file );
+
+#endif
diff --git a/lib/md5.c b/lib/md5.c
new file mode 100644
index 00000000..3c39eccd
--- /dev/null
+++ b/lib/md5.c
@@ -0,0 +1,262 @@
+/*
+ * MD5 hashing code copied from Lepton's crack <http://usuarios.lycos.es/reinob/>
+ *
+ * Adapted to be API-compatible with the previous (GPL-incompatible) code.
+ */
+
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+#include <sys/types.h>
+#include <string.h> /* for memcpy() */
+#include "md5.h"
+
+static void md5_transform(uint32_t buf[4], uint32_t const in[16]);
+
+/*
+ * Wrapper function for all-in-one MD5
+ *
+ * Bernardo Reino, aka Lepton.
+ * 20021120
+ */
+
+/* Turns out MD5 was designed for little-endian machines. If we're running
+ on a big-endian machines, we have to swap some bytes. Since detecting
+ endianness at compile time reliably seems pretty hard, let's do it at
+ run-time. It's not like we're going to checksum megabytes of data... */
+static uint32_t cvt32(uint32_t val)
+{
+ static int little_endian = -1;
+
+ if (little_endian == -1)
+ {
+ little_endian = 1;
+ little_endian = *((char*) &little_endian);
+ }
+
+ if (little_endian)
+ return val;
+ else
+ return (val >> 24) |
+ ((val >> 8) & 0xff00) |
+ ((val << 8) & 0xff0000) |
+ (val << 24);
+}
+
+void md5_init(struct MD5Context *ctx)
+{
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
+
+ ctx->bits[0] = 0;
+ ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void md5_append(struct MD5Context *ctx, const md5_byte_t *buf,
+ unsigned int len)
+{
+ uint32_t t;
+
+ /* Update bitcount */
+
+ t = ctx->bits[0];
+ if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
+ ctx->bits[1]++; /* Carry from low to high */
+ ctx->bits[1] += len >> 29;
+
+ t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
+
+ /* Handle any leading odd-sized chunks */
+
+ if (t) {
+ unsigned char *p = (unsigned char *) ctx->in + t;
+
+ t = 64 - t;
+ if (len < t) {
+ memcpy(p, buf, len);
+ return;
+ }
+ memcpy(p, buf, t);
+ md5_transform(ctx->buf, (uint32_t *) ctx->in);
+ buf += t;
+ len -= t;
+ }
+ /* Process data in 64-byte chunks */
+
+ while (len >= 64) {
+ memcpy(ctx->in, buf, 64);
+ md5_transform(ctx->buf, (uint32_t *) ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ /* Handle any remaining bytes of data. */
+
+ memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void md5_finish(struct MD5Context *ctx, md5_byte_t digest[16])
+{
+ unsigned count;
+ unsigned char *p;
+
+ /* Compute number of bytes mod 64 */
+ count = (ctx->bits[0] >> 3) & 0x3F;
+
+ /* Set the first char of padding to 0x80. This is safe since there is
+ always at least one byte free */
+ p = ctx->in + count;
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 64 bytes */
+ count = 64 - 1 - count;
+
+ /* Pad out to 56 mod 64 */
+ if (count < 8) {
+ /* Two lots of padding: Pad the first block to 64 bytes */
+ memset(p, 0, count);
+ md5_transform(ctx->buf, (uint32_t *) ctx->in);
+
+ /* Now fill the next block with 56 bytes */
+ memset(ctx->in, 0, 56);
+ } else {
+ /* Pad block to 56 bytes */
+ memset(p, 0, count - 8);
+ }
+
+ /* Append length in bits and transform */
+ ((uint32_t *) ctx->in)[14] = cvt32(ctx->bits[0]);
+ ((uint32_t *) ctx->in)[15] = cvt32(ctx->bits[1]);
+
+ md5_transform(ctx->buf, (uint32_t *) ctx->in);
+ ctx->buf[0] = cvt32(ctx->buf[0]);
+ ctx->buf[1] = cvt32(ctx->buf[1]);
+ ctx->buf[2] = cvt32(ctx->buf[2]);
+ ctx->buf[3] = cvt32(ctx->buf[3]);
+ memcpy(digest, ctx->buf, 16);
+ memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */
+}
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+ ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+static void md5_transform(uint32_t buf[4], uint32_t const in[16])
+{
+ register uint32_t a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, cvt32(in[0]) + 0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, cvt32(in[1]) + 0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, cvt32(in[2]) + 0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, cvt32(in[3]) + 0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, cvt32(in[4]) + 0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, cvt32(in[5]) + 0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, cvt32(in[6]) + 0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, cvt32(in[7]) + 0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, cvt32(in[8]) + 0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, cvt32(in[9]) + 0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, cvt32(in[10]) + 0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, cvt32(in[11]) + 0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, cvt32(in[12]) + 0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, cvt32(in[13]) + 0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, cvt32(in[14]) + 0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, cvt32(in[15]) + 0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, cvt32(in[1]) + 0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, cvt32(in[6]) + 0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, cvt32(in[11]) + 0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, cvt32(in[0]) + 0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, cvt32(in[5]) + 0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, cvt32(in[10]) + 0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, cvt32(in[15]) + 0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, cvt32(in[4]) + 0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, cvt32(in[9]) + 0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, cvt32(in[14]) + 0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, cvt32(in[3]) + 0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, cvt32(in[8]) + 0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, cvt32(in[13]) + 0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, cvt32(in[2]) + 0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, cvt32(in[7]) + 0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, cvt32(in[12]) + 0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, cvt32(in[5]) + 0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, cvt32(in[8]) + 0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, cvt32(in[11]) + 0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, cvt32(in[14]) + 0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, cvt32(in[1]) + 0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, cvt32(in[4]) + 0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, cvt32(in[7]) + 0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, cvt32(in[10]) + 0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, cvt32(in[13]) + 0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, cvt32(in[0]) + 0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, cvt32(in[3]) + 0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, cvt32(in[6]) + 0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, cvt32(in[9]) + 0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, cvt32(in[12]) + 0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, cvt32(in[15]) + 0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, cvt32(in[2]) + 0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, cvt32(in[0]) + 0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, cvt32(in[7]) + 0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, cvt32(in[14]) + 0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, cvt32(in[5]) + 0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, cvt32(in[12]) + 0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, cvt32(in[3]) + 0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, cvt32(in[10]) + 0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, cvt32(in[1]) + 0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, cvt32(in[8]) + 0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, cvt32(in[15]) + 0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, cvt32(in[6]) + 0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, cvt32(in[13]) + 0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, cvt32(in[4]) + 0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, cvt32(in[11]) + 0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, cvt32(in[2]) + 0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, cvt32(in[9]) + 0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
diff --git a/lib/md5.h b/lib/md5.h
new file mode 100644
index 00000000..3ba28586
--- /dev/null
+++ b/lib/md5.h
@@ -0,0 +1,46 @@
+/*
+ * MD5 hashing code copied from Lepton's crack <http://usuarios.lycos.es/reinob/>
+ *
+ * Adapted to be API-compatible with the previous (GPL-incompatible) code.
+ */
+
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+#ifndef _MD5_H
+#define _MD5_H
+
+#include <sys/types.h>
+#include <gmodule.h>
+#if(__sun)
+#include <inttypes.h>
+#else
+#include <stdint.h>
+#endif
+
+typedef uint8_t md5_byte_t;
+typedef struct MD5Context {
+ uint32_t buf[4];
+ uint32_t bits[2];
+ unsigned char in[64];
+} md5_state_t;
+
+G_MODULE_EXPORT void md5_init(struct MD5Context *context);
+G_MODULE_EXPORT void md5_append(struct MD5Context *context, const md5_byte_t *buf, unsigned int len);
+G_MODULE_EXPORT void md5_finish(struct MD5Context *context, md5_byte_t digest[16]);
+
+#endif
diff --git a/lib/misc.c b/lib/misc.c
new file mode 100644
index 00000000..05192d9c
--- /dev/null
+++ b/lib/misc.c
@@ -0,0 +1,730 @@
+ /********************************************************************\
+ * BitlBee -- An IRC to other IM-networks gateway *
+ * *
+ * Copyright 2002-2006 Wilmer van der Gaast and others *
+ \********************************************************************/
+
+/*
+ * Various utility functions. Some are copied from Gaim to support the
+ * IM-modules, most are from BitlBee.
+ *
+ * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
+ * (and possibly other members of the Gaim team)
+ * Copyright 2002-2006 Wilmer van der Gaast <wilmer@gaast.net>
+ */
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License 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 "nogaim.h"
+#include "base64.h"
+#include "md5.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <glib.h>
+#include <time.h>
+
+#ifdef HAVE_RESOLV_A
+#include <arpa/nameser.h>
+#include <resolv.h>
+#endif
+
+#include "md5.h"
+#include "ssl_client.h"
+
+void strip_linefeed(gchar *text)
+{
+ int i, j;
+ gchar *text2 = g_malloc(strlen(text) + 1);
+
+ for (i = 0, j = 0; text[i]; i++)
+ if (text[i] != '\r')
+ text2[j++] = text[i];
+ text2[j] = '\0';
+
+ strcpy(text, text2);
+ g_free(text2);
+}
+
+time_t get_time(int year, int month, int day, int hour, int min, int sec)
+{
+ struct tm tm;
+
+ memset(&tm, 0, sizeof(struct tm));
+ tm.tm_year = year - 1900;
+ tm.tm_mon = month - 1;
+ tm.tm_mday = day;
+ tm.tm_hour = hour;
+ tm.tm_min = min;
+ tm.tm_sec = sec >= 0 ? sec : time(NULL) % 60;
+
+ return mktime(&tm);
+}
+
+time_t mktime_utc( struct tm *tp )
+{
+ struct tm utc;
+ time_t res, tres;
+
+ tp->tm_isdst = -1;
+ res = mktime( tp );
+ /* Problem is, mktime() just gave us the GMT timestamp for the
+ given local time... While the given time WAS NOT local. So
+ we should fix this now.
+
+ Now I could choose between messing with environment variables
+ (kludgy) or using timegm() (not portable)... Or doing the
+ following, which I actually prefer...
+
+ tzset() may also work but in other places I actually want to
+ use local time.
+
+ FFFFFFFFFFFFFFFFFFFFFUUUUUUUUUUUUUUUUUUUU!! */
+ gmtime_r( &res, &utc );
+ utc.tm_isdst = -1;
+ if( utc.tm_hour == tp->tm_hour && utc.tm_min == tp->tm_min )
+ /* Sweet! We're in UTC right now... */
+ return res;
+
+ tres = mktime( &utc );
+ res += res - tres;
+
+ /* Yes, this is a hack. And it will go wrong around DST changes.
+ BUT this is more likely to be threadsafe than messing with
+ environment variables, and possibly more portable... */
+
+ return res;
+}
+
+typedef struct htmlentity
+{
+ char code[7];
+ char is[3];
+} htmlentity_t;
+
+static const htmlentity_t ent[] =
+{
+ { "lt", "<" },
+ { "gt", ">" },
+ { "amp", "&" },
+ { "apos", "'" },
+ { "quot", "\"" },
+ { "aacute", "á" },
+ { "eacute", "é" },
+ { "iacute", "é" },
+ { "oacute", "ó" },
+ { "uacute", "ú" },
+ { "agrave", "à" },
+ { "egrave", "è" },
+ { "igrave", "ì" },
+ { "ograve", "ò" },
+ { "ugrave", "ù" },
+ { "acirc", "â" },
+ { "ecirc", "ê" },
+ { "icirc", "î" },
+ { "ocirc", "ô" },
+ { "ucirc", "û" },
+ { "auml", "ä" },
+ { "euml", "ë" },
+ { "iuml", "ï" },
+ { "ouml", "ö" },
+ { "uuml", "ü" },
+ { "nbsp", " " },
+ { "", "" }
+};
+
+void strip_html( char *in )
+{
+ char *start = in;
+ char out[strlen(in)+1];
+ char *s = out, *cs;
+ int i, matched;
+ int taglen;
+
+ memset( out, 0, sizeof( out ) );
+
+ while( *in )
+ {
+ if( *in == '<' && ( isalpha( *(in+1) ) || *(in+1) == '/' ) )
+ {
+ /* If in points at a < and in+1 points at a letter or a slash, this is probably
+ a HTML-tag. Try to find a closing > and continue there. If the > can't be
+ found, assume that it wasn't a HTML-tag after all. */
+
+ cs = in;
+
+ while( *in && *in != '>' )
+ in ++;
+
+ taglen = in - cs - 1; /* not <0 because the above loop runs at least once */
+ if( *in )
+ {
+ if( g_strncasecmp( cs+1, "b", taglen) == 0 )
+ *(s++) = '\x02';
+ else if( g_strncasecmp( cs+1, "/b", taglen) == 0 )
+ *(s++) = '\x02';
+ else if( g_strncasecmp( cs+1, "i", taglen) == 0 )
+ *(s++) = '\x1f';
+ else if( g_strncasecmp( cs+1, "/i", taglen) == 0 )
+ *(s++) = '\x1f';
+ else if( g_strncasecmp( cs+1, "br", taglen) == 0 )
+ *(s++) = '\n';
+ in ++;
+ }
+ else
+ {
+ in = cs;
+ *(s++) = *(in++);
+ }
+ }
+ else if( *in == '&' )
+ {
+ cs = ++in;
+ while( *in && isalpha( *in ) )
+ in ++;
+
+ if( *in == ';' ) in ++;
+ matched = 0;
+
+ for( i = 0; *ent[i].code; i ++ )
+ if( g_strncasecmp( ent[i].code, cs, strlen( ent[i].code ) ) == 0 )
+ {
+ int j;
+
+ for( j = 0; ent[i].is[j]; j ++ )
+ *(s++) = ent[i].is[j];
+
+ matched = 1;
+ break;
+ }
+
+ /* None of the entities were matched, so return the string */
+ if( !matched )
+ {
+ in = cs - 1;
+ *(s++) = *(in++);
+ }
+ }
+ else
+ {
+ *(s++) = *(in++);
+ }
+ }
+
+ strcpy( start, out );
+}
+
+char *escape_html( const char *html )
+{
+ const char *c = html;
+ GString *ret;
+ char *str;
+
+ if( html == NULL )
+ return( NULL );
+
+ ret = g_string_new( "" );
+
+ while( *c )
+ {
+ switch( *c )
+ {
+ case '&':
+ ret = g_string_append( ret, "&amp;" );
+ break;
+ case '<':
+ ret = g_string_append( ret, "&lt;" );
+ break;
+ case '>':
+ ret = g_string_append( ret, "&gt;" );
+ break;
+ case '"':
+ ret = g_string_append( ret, "&quot;" );
+ break;
+ default:
+ ret = g_string_append_c( ret, *c );
+ }
+ c ++;
+ }
+
+ str = ret->str;
+ g_string_free( ret, FALSE );
+ return( str );
+}
+
+/* Decode%20a%20file%20name */
+void http_decode( char *s )
+{
+ char *t;
+ int i, j, k;
+
+ t = g_new( char, strlen( s ) + 1 );
+
+ for( i = j = 0; s[i]; i ++, j ++ )
+ {
+ if( s[i] == '%' )
+ {
+ if( sscanf( s + i + 1, "%2x", &k ) )
+ {
+ t[j] = k;
+ i += 2;
+ }
+ else
+ {
+ *t = 0;
+ break;
+ }
+ }
+ else
+ {
+ t[j] = s[i];
+ }
+ }
+ t[j] = 0;
+
+ strcpy( s, t );
+ g_free( t );
+}
+
+/* Warning: This one explodes the string. Worst-cases can make the string 3x its original size! */
+/* This fuction is safe, but make sure you call it safely as well! */
+void http_encode( char *s )
+{
+ char t[strlen(s)+1];
+ int i, j;
+
+ strcpy( t, s );
+ for( i = j = 0; t[i]; i ++, j ++ )
+ {
+ /* Warning: isalnum() is locale-aware, so don't use it here! */
+ if( ( t[i] >= 'A' && t[i] <= 'Z' ) ||
+ ( t[i] >= 'a' && t[i] <= 'z' ) ||
+ ( t[i] >= '0' && t[i] <= '9' ) ||
+ strchr( "._-~", t[i] ) )
+ {
+ s[j] = t[i];
+ }
+ else
+ {
+ sprintf( s + j, "%%%02X", ((unsigned char*)t)[i] );
+ j += 2;
+ }
+ }
+ s[j] = 0;
+}
+
+/* Strip newlines from a string. Modifies the string passed to it. */
+char *strip_newlines( char *source )
+{
+ int i;
+
+ for( i = 0; source[i] != '\0'; i ++ )
+ if( source[i] == '\n' || source[i] == '\r' )
+ source[i] = ' ';
+
+ return source;
+}
+
+/* Wrap an IPv4 address into IPv6 space. Not thread-safe... */
+char *ipv6_wrap( char *src )
+{
+ static char dst[64];
+ int i;
+
+ for( i = 0; src[i]; i ++ )
+ if( ( src[i] < '0' || src[i] > '9' ) && src[i] != '.' )
+ break;
+
+ /* Hmm, it's not even an IP... */
+ if( src[i] )
+ return src;
+
+ g_snprintf( dst, sizeof( dst ), "::ffff:%s", src );
+
+ return dst;
+}
+
+/* Unwrap an IPv4 address into IPv6 space. Thread-safe, because it's very simple. :-) */
+char *ipv6_unwrap( char *src )
+{
+ int i;
+
+ if( g_strncasecmp( src, "::ffff:", 7 ) != 0 )
+ return src;
+
+ for( i = 7; src[i]; i ++ )
+ if( ( src[i] < '0' || src[i] > '9' ) && src[i] != '.' )
+ break;
+
+ /* Hmm, it's not even an IP... */
+ if( src[i] )
+ return src;
+
+ return ( src + 7 );
+}
+
+/* Convert from one charset to another.
+
+ from_cs, to_cs: Source and destination charsets
+ src, dst: Source and destination strings
+ size: Size if src. 0 == use strlen(). strlen() is not reliable for UNICODE/UTF16 strings though.
+ maxbuf: Maximum number of bytes to write to dst
+
+ Returns the number of bytes written to maxbuf or -1 on an error.
+*/
+signed int do_iconv( char *from_cs, char *to_cs, char *src, char *dst, size_t size, size_t maxbuf )
+{
+ GIConv cd;
+ size_t res;
+ size_t inbytesleft, outbytesleft;
+ char *inbuf = src;
+ char *outbuf = dst;
+
+ cd = g_iconv_open( to_cs, from_cs );
+ if( cd == (GIConv) -1 )
+ return( -1 );
+
+ inbytesleft = size ? size : strlen( src );
+ outbytesleft = maxbuf - 1;
+ res = g_iconv( cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft );
+ *outbuf = '\0';
+ g_iconv_close( cd );
+
+ if( res == (size_t) -1 )
+ return( -1 );
+ else
+ return( outbuf - dst );
+}
+
+/* A pretty reliable random number generator. Tries to use the /dev/random
+ devices first, and falls back to the random number generator from libc
+ when it fails. Opens randomizer devices with O_NONBLOCK to make sure a
+ lack of entropy won't halt BitlBee. */
+void random_bytes( unsigned char *buf, int count )
+{
+#ifndef _WIN32
+ static int use_dev = -1;
+
+ /* Actually this probing code isn't really necessary, is it? */
+ if( use_dev == -1 )
+ {
+ if( access( "/dev/random", R_OK ) == 0 || access( "/dev/urandom", R_OK ) == 0 )
+ use_dev = 1;
+ else
+ {
+ use_dev = 0;
+ srand( ( getpid() << 16 ) ^ time( NULL ) );
+ }
+ }
+
+ if( use_dev )
+ {
+ int fd;
+
+ /* At least on Linux, /dev/random can block if there's not
+ enough entropy. We really don't want that, so if it can't
+ give anything, use /dev/urandom instead. */
+ if( ( fd = open( "/dev/random", O_RDONLY | O_NONBLOCK ) ) >= 0 )
+ if( read( fd, buf, count ) == count )
+ {
+ close( fd );
+ return;
+ }
+ close( fd );
+
+ /* urandom isn't supposed to block at all, but just to be
+ sure. If it blocks, we'll disable use_dev and use the libc
+ randomizer instead. */
+ if( ( fd = open( "/dev/urandom", O_RDONLY | O_NONBLOCK ) ) >= 0 )
+ if( read( fd, buf, count ) == count )
+ {
+ close( fd );
+ return;
+ }
+ close( fd );
+
+ /* If /dev/random blocks once, we'll still try to use it
+ again next time. If /dev/urandom also fails for some
+ reason, stick with libc during this session. */
+
+ use_dev = 0;
+ srand( ( getpid() << 16 ) ^ time( NULL ) );
+ }
+
+ if( !use_dev )
+#endif
+ {
+ int i;
+
+ /* Possibly the LSB of rand() isn't very random on some
+ platforms. Seems okay on at least Linux and OSX though. */
+ for( i = 0; i < count; i ++ )
+ buf[i] = rand() & 0xff;
+ }
+}
+
+int is_bool( char *value )
+{
+ if( *value == 0 )
+ return 0;
+
+ if( ( g_strcasecmp( value, "true" ) == 0 ) || ( g_strcasecmp( value, "yes" ) == 0 ) || ( g_strcasecmp( value, "on" ) == 0 ) )
+ return 1;
+ if( ( g_strcasecmp( value, "false" ) == 0 ) || ( g_strcasecmp( value, "no" ) == 0 ) || ( g_strcasecmp( value, "off" ) == 0 ) )
+ return 1;
+
+ while( *value )
+ if( !isdigit( *value ) )
+ return 0;
+ else
+ value ++;
+
+ return 1;
+}
+
+int bool2int( char *value )
+{
+ int i;
+
+ if( ( g_strcasecmp( value, "true" ) == 0 ) || ( g_strcasecmp( value, "yes" ) == 0 ) || ( g_strcasecmp( value, "on" ) == 0 ) )
+ return 1;
+ if( ( g_strcasecmp( value, "false" ) == 0 ) || ( g_strcasecmp( value, "no" ) == 0 ) || ( g_strcasecmp( value, "off" ) == 0 ) )
+ return 0;
+
+ if( sscanf( value, "%d", &i ) == 1 )
+ return i;
+
+ return 0;
+}
+
+struct ns_srv_reply **srv_lookup( char *service, char *protocol, char *domain )
+{
+ struct ns_srv_reply **replies = NULL;
+#ifdef HAVE_RESOLV_A
+ struct ns_srv_reply *reply = NULL;
+ char name[1024];
+ unsigned char querybuf[1024];
+ const unsigned char *buf;
+ ns_msg nsh;
+ ns_rr rr;
+ int i, n, len, size;
+
+ g_snprintf( name, sizeof( name ), "_%s._%s.%s", service, protocol, domain );
+
+ if( ( size = res_query( name, ns_c_in, ns_t_srv, querybuf, sizeof( querybuf ) ) ) <= 0 )
+ return NULL;
+
+ if( ns_initparse( querybuf, size, &nsh ) != 0 )
+ return NULL;
+
+ n = 0;
+ while( ns_parserr( &nsh, ns_s_an, n, &rr ) == 0 )
+ {
+ size = ns_rr_rdlen( rr );
+ buf = ns_rr_rdata( rr );
+
+ len = 0;
+ for( i = 6; i < size && buf[i]; i += buf[i] + 1 )
+ len += buf[i] + 1;
+
+ if( i > size )
+ break;
+
+ reply = g_malloc( sizeof( struct ns_srv_reply ) + len );
+ memcpy( reply->name, buf + 7, len );
+
+ for( i = buf[6]; i < len && buf[7+i]; i += buf[7+i] + 1 )
+ reply->name[i] = '.';
+
+ if( i > len )
+ {
+ g_free( reply );
+ break;
+ }
+
+ reply->prio = ( buf[0] << 8 ) | buf[1];
+ reply->weight = ( buf[2] << 8 ) | buf[3];
+ reply->port = ( buf[4] << 8 ) | buf[5];
+
+ n ++;
+ replies = g_renew( struct ns_srv_reply *, replies, n + 1 );
+ replies[n-1] = reply;
+ }
+ if( replies )
+ replies[n] = NULL;
+#endif
+
+ return replies;
+}
+
+void srv_free( struct ns_srv_reply **srv )
+{
+ int i;
+
+ if( srv == NULL )
+ return;
+
+ for( i = 0; srv[i]; i ++ )
+ g_free( srv[i] );
+ g_free( srv );
+}
+
+/* Word wrapping. Yes, I know this isn't UTF-8 clean. I'm willing to take the risk. */
+char *word_wrap( const char *msg, int line_len )
+{
+ GString *ret = g_string_sized_new( strlen( msg ) + 16 );
+
+ while( strlen( msg ) > line_len )
+ {
+ int i;
+
+ /* First try to find out if there's a newline already. Don't
+ want to add more splits than necessary. */
+ for( i = line_len; i > 0 && msg[i] != '\n'; i -- );
+ if( msg[i] == '\n' )
+ {
+ g_string_append_len( ret, msg, i + 1 );
+ msg += i + 1;
+ continue;
+ }
+
+ for( i = line_len; i > 0; i -- )
+ {
+ if( msg[i] == '-' )
+ {
+ g_string_append_len( ret, msg, i + 1 );
+ g_string_append_c( ret, '\n' );
+ msg += i + 1;
+ break;
+ }
+ else if( msg[i] == ' ' )
+ {
+ g_string_append_len( ret, msg, i );
+ g_string_append_c( ret, '\n' );
+ msg += i + 1;
+ break;
+ }
+ }
+ if( i == 0 )
+ {
+ g_string_append_len( ret, msg, line_len );
+ g_string_append_c( ret, '\n' );
+ msg += line_len;
+ }
+ }
+ g_string_append( ret, msg );
+
+ return g_string_free( ret, FALSE );
+}
+
+gboolean ssl_sockerr_again( void *ssl )
+{
+ if( ssl )
+ return ssl_errno == SSL_AGAIN;
+ else
+ return sockerr_again();
+}
+
+/* Returns values: -1 == Failure (base64-decoded to something unexpected)
+ 0 == Okay
+ 1 == Password doesn't match the hash. */
+int md5_verify_password( char *password, char *hash )
+{
+ md5_byte_t *pass_dec = NULL;
+ md5_byte_t pass_md5[16];
+ md5_state_t md5_state;
+ int ret = -1, i;
+
+ if( base64_decode( hash, &pass_dec ) == 21 )
+ {
+ 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;
+}
+
+/* Split commands (root-style, *not* IRC-style). Handles "quoting of"
+ white\ space in 'various ways'. Returns a NULL-terminated static
+ char** so watch out with nested use! Definitely not thread-safe. */
+char **split_command_parts( char *command )
+{
+ static char *cmd[IRC_MAX_ARGS+1];
+ char *s, q = 0;
+ int k;
+
+ memset( cmd, 0, sizeof( cmd ) );
+ cmd[0] = command;
+ k = 1;
+ for( s = command; *s && k < IRC_MAX_ARGS; s ++ )
+ if( *s == ' ' && !q )
+ {
+ *s = 0;
+ while( *++s == ' ' );
+ if( *s == '"' || *s == '\'' )
+ {
+ q = *s;
+ s ++;
+ }
+ if( *s )
+ {
+ cmd[k++] = s;
+ s --;
+ }
+ else
+ {
+ break;
+ }
+ }
+ else if( *s == '\\' && ( ( !q && s[1] ) || ( q && q == s[1] ) ) )
+ {
+ char *cpy;
+
+ for( cpy = s; *cpy; cpy ++ )
+ cpy[0] = cpy[1];
+ }
+ else if( *s == q )
+ {
+ q = *s = 0;
+ }
+
+ /* Full zero-padding for easier argc checking. */
+ while( k <= IRC_MAX_ARGS )
+ cmd[k++] = NULL;
+
+ return cmd;
+}
diff --git a/lib/misc.h b/lib/misc.h
new file mode 100644
index 00000000..83ba9e67
--- /dev/null
+++ b/lib/misc.h
@@ -0,0 +1,74 @@
+ /********************************************************************\
+ * BitlBee -- An IRC to other IM-networks gateway *
+ * *
+ * Copyright 2002-2004 Wilmer van der Gaast and others *
+ \********************************************************************/
+
+/* Misc. functions */
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License with
+ the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+ if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#ifndef _MISC_H
+#define _MISC_H
+
+#include <gmodule.h>
+#include <time.h>
+
+struct ns_srv_reply
+{
+ int prio;
+ int weight;
+ int port;
+ char name[];
+};
+
+G_MODULE_EXPORT void strip_linefeed( gchar *text );
+G_MODULE_EXPORT char *add_cr( char *text );
+G_MODULE_EXPORT char *strip_newlines(char *source);
+
+G_MODULE_EXPORT time_t get_time( int year, int month, int day, int hour, int min, int sec );
+G_MODULE_EXPORT time_t mktime_utc( struct tm *tp );
+double gettime( void );
+
+G_MODULE_EXPORT void strip_html( char *msg );
+G_MODULE_EXPORT char *escape_html( const char *html );
+G_MODULE_EXPORT void http_decode( char *s );
+G_MODULE_EXPORT void http_encode( char *s );
+
+G_MODULE_EXPORT char *ipv6_wrap( char *src );
+G_MODULE_EXPORT char *ipv6_unwrap( char *src );
+
+G_MODULE_EXPORT signed int do_iconv( char *from_cs, char *to_cs, char *src, char *dst, size_t size, size_t maxbuf );
+
+G_MODULE_EXPORT void random_bytes( unsigned char *buf, int count );
+
+G_MODULE_EXPORT int is_bool( char *value );
+G_MODULE_EXPORT int bool2int( char *value );
+
+G_MODULE_EXPORT struct ns_srv_reply **srv_lookup( char *service, char *protocol, char *domain );
+G_MODULE_EXPORT void srv_free( struct ns_srv_reply **srv );
+
+G_MODULE_EXPORT char *word_wrap( const char *msg, int line_len );
+
+G_MODULE_EXPORT gboolean ssl_sockerr_again( void *ssl );
+
+G_MODULE_EXPORT int md5_verify_password( char *password, char *hash );
+
+G_MODULE_EXPORT char **split_command_parts( char *command );
+
+#endif
diff --git a/lib/oauth.c b/lib/oauth.c
new file mode 100644
index 00000000..9c67363a
--- /dev/null
+++ b/lib/oauth.c
@@ -0,0 +1,456 @@
+/***************************************************************************\
+* *
+* BitlBee - An IRC to IM gateway *
+* Simple OAuth client (consumer) implementation. *
+* *
+* Copyright 2010 Wilmer van der Gaast <wilmer@gaast.net> *
+* *
+* This library is free software; you can redistribute it and/or *
+* modify it under the terms of the GNU Lesser General Public *
+* License as published by the Free Software Foundation, version *
+* 2.1. *
+* *
+* This library is distributed in the hope that it will be useful, *
+* but WITHOUT ANY WARRANTY; without even the implied warranty of *
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
+* Lesser General Public License for more details. *
+* *
+* You should have received a copy of the GNU Lesser General Public License *
+* along with this library; if not, write to the Free Software Foundation, *
+* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *
+* *
+\***************************************************************************/
+
+#include <glib.h>
+#include <gmodule.h>
+#include <stdlib.h>
+#include <string.h>
+#include "http_client.h"
+#include "base64.h"
+#include "misc.h"
+#include "sha1.h"
+#include "url.h"
+#include "oauth.h"
+
+#define HMAC_BLOCK_SIZE 64
+
+static char *oauth_sign( const char *method, const char *url,
+ const char *params, struct oauth_info *oi )
+{
+ sha1_state_t sha1;
+ uint8_t hash[sha1_hash_size];
+ uint8_t key[HMAC_BLOCK_SIZE+1];
+ char *s;
+ int i;
+
+ /* Create K. If our current key is >64 chars we have to hash it,
+ otherwise just pad. */
+ memset( key, 0, HMAC_BLOCK_SIZE );
+ i = strlen( oi->sp->consumer_secret ) + 1 + ( oi->token_secret ? strlen( oi->token_secret ) : 0 );
+ if( i > HMAC_BLOCK_SIZE )
+ {
+ sha1_init( &sha1 );
+ sha1_append( &sha1, (uint8_t*) oi->sp->consumer_secret, strlen( oi->sp->consumer_secret ) );
+ sha1_append( &sha1, (uint8_t*) "&", 1 );
+ if( oi->token_secret )
+ sha1_append( &sha1, (uint8_t*) oi->token_secret, strlen( oi->token_secret ) );
+ sha1_finish( &sha1, key );
+ }
+ else
+ {
+ g_snprintf( (gchar*) key, HMAC_BLOCK_SIZE + 1, "%s&%s",
+ oi->sp->consumer_secret, oi->token_secret ? oi->token_secret : "" );
+ }
+
+ /* Inner part: H(K XOR 0x36, text) */
+ sha1_init( &sha1 );
+
+ for( i = 0; i < HMAC_BLOCK_SIZE; i ++ )
+ key[i] ^= 0x36;
+ sha1_append( &sha1, key, HMAC_BLOCK_SIZE );
+
+ /* OAuth: text = method&url&params, all http_encoded. */
+ sha1_append( &sha1, (const uint8_t*) method, strlen( method ) );
+ sha1_append( &sha1, (const uint8_t*) "&", 1 );
+
+ s = g_new0( char, strlen( url ) * 3 + 1 );
+ strcpy( s, url );
+ http_encode( s );
+ sha1_append( &sha1, (const uint8_t*) s, strlen( s ) );
+ sha1_append( &sha1, (const uint8_t*) "&", 1 );
+ g_free( s );
+
+ s = g_new0( char, strlen( params ) * 3 + 1 );
+ strcpy( s, params );
+ http_encode( s );
+ sha1_append( &sha1, (const uint8_t*) s, strlen( s ) );
+ g_free( s );
+
+ sha1_finish( &sha1, hash );
+
+ /* Final result: H(K XOR 0x5C, inner stuff) */
+ sha1_init( &sha1 );
+ for( i = 0; i < HMAC_BLOCK_SIZE; i ++ )
+ key[i] ^= 0x36 ^ 0x5c;
+ sha1_append( &sha1, key, HMAC_BLOCK_SIZE );
+ sha1_append( &sha1, hash, sha1_hash_size );
+ sha1_finish( &sha1, hash );
+
+ /* base64_encode + HTTP escape it (both consumers
+ need it that away) and we're done. */
+ s = base64_encode( hash, sha1_hash_size );
+ s = g_realloc( s, strlen( s ) * 3 + 1 );
+ http_encode( s );
+
+ return s;
+}
+
+static char *oauth_nonce()
+{
+ unsigned char bytes[21];
+ char *ret = g_new0( char, sizeof( bytes) / 3 * 4 + 1 );
+
+ random_bytes( bytes, sizeof( bytes ) );
+ base64_encode_real( bytes, sizeof( bytes), (unsigned char*) ret, "0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0A" );
+
+ return ret;
+}
+
+void oauth_params_add( GSList **params, const char *key, const char *value )
+{
+ char *item;
+
+ item = g_strdup_printf( "%s=%s", key, value );
+ *params = g_slist_insert_sorted( *params, item, (GCompareFunc) strcmp );
+}
+
+void oauth_params_del( GSList **params, const char *key )
+{
+ int key_len = strlen( key );
+ GSList *l, *n;
+
+ for( l = *params; l; l = n )
+ {
+ n = l->next;
+
+ if( strncmp( (char*) l->data, key, key_len ) == 0 &&
+ ((char*)l->data)[key_len] == '=' )
+ {
+ g_free( l->data );
+ *params = g_slist_remove( *params, l->data );
+ }
+ }
+}
+
+void oauth_params_set( GSList **params, const char *key, const char *value )
+{
+ oauth_params_del( params, key );
+ oauth_params_add( params, key, value );
+}
+
+const char *oauth_params_get( GSList **params, const char *key )
+{
+ int key_len = strlen( key );
+ GSList *l;
+
+ for( l = *params; l; l = l->next )
+ {
+ if( strncmp( (char*) l->data, key, key_len ) == 0 &&
+ ((char*)l->data)[key_len] == '=' )
+ return (const char*) l->data + key_len + 1;
+ }
+
+ return NULL;
+}
+
+static void oauth_params_parse( GSList **params, char *in )
+{
+ char *amp, *eq, *s;
+
+ while( in && *in )
+ {
+ eq = strchr( in, '=' );
+ if( !eq )
+ break;
+
+ *eq = '\0';
+ if( ( amp = strchr( eq + 1, '&' ) ) )
+ *amp = '\0';
+
+ s = g_strdup( eq + 1 );
+ http_decode( s );
+ oauth_params_add( params, in, s );
+ g_free( s );
+
+ *eq = '=';
+ if( amp == NULL )
+ break;
+
+ *amp = '&';
+ in = amp + 1;
+ }
+}
+
+void oauth_params_free( GSList **params )
+{
+ while( params && *params )
+ {
+ g_free( (*params)->data );
+ *params = g_slist_remove( *params, (*params)->data );
+ }
+}
+
+char *oauth_params_string( GSList *params )
+{
+ GSList *l;
+ GString *str = g_string_new( "" );
+
+ for( l = params; l; l = l->next )
+ {
+ char *s, *eq;
+
+ s = g_malloc( strlen( l->data ) * 3 + 1 );
+ strcpy( s, l->data );
+ if( ( eq = strchr( s, '=' ) ) )
+ http_encode( eq + 1 );
+ g_string_append( str, s );
+ g_free( s );
+
+ if( l->next )
+ g_string_append_c( str, '&' );
+ }
+
+ return g_string_free( str, FALSE );
+}
+
+void oauth_info_free( struct oauth_info *info )
+{
+ if( info )
+ {
+ g_free( info->auth_url );
+ g_free( info->request_token );
+ g_free( info->token );
+ g_free( info->token_secret );
+ oauth_params_free( &info->params );
+ g_free( info );
+ }
+}
+
+static void oauth_add_default_params( GSList **params, const struct oauth_service *sp )
+{
+ char *s;
+
+ oauth_params_set( params, "oauth_consumer_key", sp->consumer_key );
+ oauth_params_set( params, "oauth_signature_method", "HMAC-SHA1" );
+
+ s = g_strdup_printf( "%d", (int) time( NULL ) );
+ oauth_params_set( params, "oauth_timestamp", s );
+ g_free( s );
+
+ s = oauth_nonce();
+ oauth_params_set( params, "oauth_nonce", s );
+ g_free( s );
+
+ oauth_params_set( params, "oauth_version", "1.0" );
+}
+
+static void *oauth_post_request( const char *url, GSList **params_, http_input_function func, struct oauth_info *oi )
+{
+ GSList *params = NULL;
+ char *s, *params_s, *post;
+ void *req;
+ url_t url_p;
+
+ if( !url_set( &url_p, url ) )
+ {
+ oauth_params_free( params_ );
+ return NULL;
+ }
+
+ if( params_ )
+ params = *params_;
+
+ oauth_add_default_params( &params, oi->sp );
+
+ params_s = oauth_params_string( params );
+ oauth_params_free( &params );
+
+ s = oauth_sign( "POST", url, params_s, oi );
+ post = g_strdup_printf( "%s&oauth_signature=%s", params_s, s );
+ g_free( params_s );
+ g_free( s );
+
+ s = g_strdup_printf( "POST %s HTTP/1.0\r\n"
+ "Host: %s\r\n"
+ "Content-Type: application/x-www-form-urlencoded\r\n"
+ "Content-Length: %zd\r\n"
+ "\r\n"
+ "%s", url_p.file, url_p.host, strlen( post ), post );
+ g_free( post );
+
+ req = http_dorequest( url_p.host, url_p.port, url_p.proto == PROTO_HTTPS,
+ s, func, oi );
+ g_free( s );
+
+ return req;
+}
+
+static void oauth_request_token_done( struct http_request *req );
+
+struct oauth_info *oauth_request_token( const struct oauth_service *sp, oauth_cb func, void *data )
+{
+ struct oauth_info *st = g_new0( struct oauth_info, 1 );
+ GSList *params = NULL;
+
+ st->func = func;
+ st->data = data;
+ st->sp = sp;
+
+ oauth_params_add( &params, "oauth_callback", "oob" );
+
+ if( !oauth_post_request( sp->url_request_token, &params, oauth_request_token_done, st ) )
+ {
+ oauth_info_free( st );
+ return NULL;
+ }
+
+ return st;
+}
+
+static void oauth_request_token_done( struct http_request *req )
+{
+ struct oauth_info *st = req->data;
+
+ st->http = req;
+
+ if( req->status_code == 200 )
+ {
+ GSList *params = NULL;
+
+ st->auth_url = g_strdup_printf( "%s?%s", st->sp->url_authorize, req->reply_body );
+ oauth_params_parse( &params, req->reply_body );
+ st->request_token = g_strdup( oauth_params_get( &params, "oauth_token" ) );
+ oauth_params_free( &params );
+ }
+
+ st->stage = OAUTH_REQUEST_TOKEN;
+ st->func( st );
+}
+
+static void oauth_access_token_done( struct http_request *req );
+
+gboolean oauth_access_token( const char *pin, struct oauth_info *st )
+{
+ GSList *params = NULL;
+
+ oauth_params_add( &params, "oauth_token", st->request_token );
+ oauth_params_add( &params, "oauth_verifier", pin );
+
+ return oauth_post_request( st->sp->url_access_token, &params, oauth_access_token_done, st ) != NULL;
+}
+
+static void oauth_access_token_done( struct http_request *req )
+{
+ struct oauth_info *st = req->data;
+
+ st->http = req;
+
+ if( req->status_code == 200 )
+ {
+ oauth_params_parse( &st->params, req->reply_body );
+ st->token = g_strdup( oauth_params_get( &st->params, "oauth_token" ) );
+ st->token_secret = g_strdup( oauth_params_get( &st->params, "oauth_token_secret" ) );
+ }
+
+ st->stage = OAUTH_ACCESS_TOKEN;
+ if( st->func( st ) )
+ {
+ /* Don't need these anymore, but keep the rest. */
+ g_free( st->auth_url );
+ st->auth_url = NULL;
+ g_free( st->request_token );
+ st->request_token = NULL;
+ oauth_params_free( &st->params );
+ }
+}
+
+char *oauth_http_header( struct oauth_info *oi, const char *method, const char *url, char *args )
+{
+ GSList *params = NULL, *l;
+ char *sig = NULL, *params_s, *s;
+ GString *ret = NULL;
+
+ oauth_params_add( &params, "oauth_token", oi->token );
+ oauth_add_default_params( &params, oi->sp );
+
+ /* Start building the OAuth header. 'key="value", '... */
+ ret = g_string_new( "OAuth " );
+ for( l = params; l; l = l->next )
+ {
+ char *kv = l->data;
+ char *eq = strchr( kv, '=' );
+ char esc[strlen(kv)*3+1];
+
+ if( eq == NULL )
+ break; /* WTF */
+
+ strcpy( esc, eq + 1 );
+ http_encode( esc );
+
+ g_string_append_len( ret, kv, eq - kv + 1 );
+ g_string_append_c( ret, '"' );
+ g_string_append( ret, esc );
+ g_string_append( ret, "\", " );
+ }
+
+ /* Now, before generating the signature, add GET/POST arguments to params
+ since they should be included in the base signature string (but not in
+ the HTTP header). */
+ if( args )
+ oauth_params_parse( &params, args );
+ if( ( s = strchr( url, '?' ) ) )
+ {
+ s = g_strdup( s + 1 );
+ oauth_params_parse( &params, s );
+ g_free( s );
+ }
+
+ /* Append the signature and we're done! */
+ params_s = oauth_params_string( params );
+ sig = oauth_sign( method, url, params_s, oi );
+ g_string_append_printf( ret, "oauth_signature=\"%s\"", sig );
+ g_free( params_s );
+
+ oauth_params_free( &params );
+ g_free( sig );
+
+ return ret ? g_string_free( ret, FALSE ) : NULL;
+}
+
+char *oauth_to_string( struct oauth_info *oi )
+{
+ GSList *params = NULL;
+ char *ret;
+
+ oauth_params_add( &params, "oauth_token", oi->token );
+ oauth_params_add( &params, "oauth_token_secret", oi->token_secret );
+ ret = oauth_params_string( params );
+ oauth_params_free( &params );
+
+ return ret;
+}
+
+struct oauth_info *oauth_from_string( char *in, const struct oauth_service *sp )
+{
+ struct oauth_info *oi = g_new0( struct oauth_info, 1 );
+ GSList *params = NULL;
+
+ oauth_params_parse( &params, in );
+ oi->token = g_strdup( oauth_params_get( &params, "oauth_token" ) );
+ oi->token_secret = g_strdup( oauth_params_get( &params, "oauth_token_secret" ) );
+ oauth_params_free( &params );
+ oi->sp = sp;
+
+ return oi;
+}
diff --git a/lib/oauth.h b/lib/oauth.h
new file mode 100644
index 00000000..8270a545
--- /dev/null
+++ b/lib/oauth.h
@@ -0,0 +1,94 @@
+/***************************************************************************\
+* *
+* BitlBee - An IRC to IM gateway *
+* Simple OAuth client (consumer) implementation. *
+* *
+* Copyright 2010 Wilmer van der Gaast <wilmer@gaast.net> *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+* *
+* This program is distributed in the hope that it will be useful, *
+* but WITHOUT ANY WARRANTY; without even the implied warranty of *
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+* GNU General Public License for more details. *
+* *
+* You should have received a copy of the GNU General Public License along *
+* with this program; if not, write to the Free Software Foundation, Inc., *
+* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
+* *
+\***************************************************************************/
+
+/* http://oauth.net/core/1.0a/ */
+
+struct oauth_info;
+
+/* Callback function called twice during the access token request process.
+ Return FALSE if something broke and the process must be aborted. */
+typedef gboolean (*oauth_cb)( struct oauth_info * );
+
+typedef enum
+{
+ OAUTH_INIT,
+ OAUTH_REQUEST_TOKEN,
+ OAUTH_ACCESS_TOKEN,
+} oauth_stage_t;
+
+struct oauth_info
+{
+ oauth_stage_t stage;
+ const struct oauth_service *sp;
+
+ oauth_cb func;
+ void *data;
+
+ struct http_request *http;
+
+ char *auth_url;
+ char *request_token;
+
+ char *token;
+ char *token_secret;
+ GSList *params;
+};
+
+struct oauth_service
+{
+ char *url_request_token;
+ char *url_access_token;
+ char *url_authorize;
+
+ char *consumer_key;
+ char *consumer_secret;
+};
+
+/* http://oauth.net/core/1.0a/#auth_step1 (section 6.1)
+ Request an initial anonymous token which can be used to construct an
+ authorization URL for the user. This is passed to the callback function
+ in a struct oauth_info. */
+struct oauth_info *oauth_request_token( const struct oauth_service *sp, oauth_cb func, void *data );
+
+/* http://oauth.net/core/1.0a/#auth_step3 (section 6.3)
+ The user gets a PIN or so which we now exchange for the final access
+ token. This is passed to the callback function in the same
+ struct oauth_info. */
+gboolean oauth_access_token( const char *pin, struct oauth_info *st );
+
+/* http://oauth.net/core/1.0a/#anchor12 (section 7)
+ Generate an OAuth Authorization: HTTP header. access_token should be
+ saved/fetched using the functions above. args can be a string with
+ whatever's going to be in the POST body of the request. GET args will
+ automatically be grabbed from url. */
+char *oauth_http_header( struct oauth_info *oi, const char *method, const char *url, char *args );
+
+/* Shouldn't normally be required unless the process is aborted by the user. */
+void oauth_info_free( struct oauth_info *info );
+
+/* Convert to and back from strings, for easier saving. */
+char *oauth_to_string( struct oauth_info *oi );
+struct oauth_info *oauth_from_string( char *in, const struct oauth_service *sp );
+
+/* For reading misc. data. */
+const char *oauth_params_get( GSList **params, const char *key );
diff --git a/lib/proxy.c b/lib/proxy.c
new file mode 100644
index 00000000..b79afea4
--- /dev/null
+++ b/lib/proxy.c
@@ -0,0 +1,586 @@
+/*
+ * gaim
+ *
+ * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
+ * Copyright (C) 2002-2004, Wilmer van der Gaast, Jelmer Vernooij
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#define BITLBEE_CORE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#else
+#include "sock.h"
+#define ETIMEDOUT WSAETIMEDOUT
+#define EINPROGRESS WSAEINPROGRESS
+#endif
+#include <fcntl.h>
+#include <errno.h>
+#include "nogaim.h"
+#include "proxy.h"
+#include "base64.h"
+
+char proxyhost[128] = "";
+int proxyport = 0;
+int proxytype = PROXY_NONE;
+char proxyuser[128] = "";
+char proxypass[128] = "";
+
+/* Some systems don't know this one. It's not essential, so set it to 0 then. */
+#ifndef AI_NUMERICSERV
+#define AI_NUMERICSERV 0
+#endif
+#ifndef AI_ADDRCONFIG
+#define AI_ADDRCONFIG 0
+#endif
+
+struct PHB {
+ b_event_handler func, proxy_func;
+ gpointer data, proxy_data;
+ char *host;
+ int port;
+ int fd;
+ gint inpa;
+ struct addrinfo *gai, *gai_cur;
+};
+
+static int proxy_connect_none(const char *host, unsigned short port_, struct PHB *phb);
+
+static gboolean gaim_io_connected(gpointer data, gint source, b_input_condition cond)
+{
+ struct PHB *phb = data;
+ unsigned int len;
+ int error = ETIMEDOUT;
+ len = sizeof(error);
+
+#ifndef _WIN32
+ if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0 || error) {
+ if ((phb->gai_cur = phb->gai_cur->ai_next)) {
+ int new_fd;
+ b_event_remove(phb->inpa);
+ if ((new_fd = proxy_connect_none(NULL, 0, phb))) {
+ b_event_remove(phb->inpa);
+ closesocket(source);
+ dup2(new_fd, source);
+ phb->inpa = b_input_add(source, B_EV_IO_WRITE, gaim_io_connected, phb);
+ return FALSE;
+ }
+ }
+ freeaddrinfo(phb->gai);
+ closesocket(source);
+ b_event_remove(phb->inpa);
+ if( phb->proxy_func )
+ phb->proxy_func(phb->proxy_data, -1, B_EV_IO_READ);
+ else {
+ phb->func(phb->data, -1, B_EV_IO_READ);
+ g_free(phb);
+ }
+ return FALSE;
+ }
+#endif
+ freeaddrinfo(phb->gai);
+ sock_make_blocking(source);
+ b_event_remove(phb->inpa);
+ if( phb->proxy_func )
+ phb->proxy_func(phb->proxy_data, source, B_EV_IO_READ);
+ else {
+ phb->func(phb->data, source, B_EV_IO_READ);
+ g_free(phb);
+ }
+
+ return FALSE;
+}
+
+static int proxy_connect_none(const char *host, unsigned short port_, struct PHB *phb)
+{
+ struct sockaddr_in me;
+ int fd = -1;
+
+ if (phb->gai_cur == NULL)
+ {
+ int ret;
+ char port[6];
+ struct addrinfo hints;
+
+ g_snprintf(port, sizeof(port), "%d", port_);
+
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV;
+
+ if (!(ret = getaddrinfo(host, port, &hints, &phb->gai)))
+ phb->gai_cur = phb->gai;
+ else
+ event_debug("gai(): %s\n", gai_strerror(ret));
+ }
+
+ for (; phb->gai_cur; phb->gai_cur = phb->gai_cur->ai_next)
+ {
+ if ((fd = socket(phb->gai_cur->ai_family, phb->gai_cur->ai_socktype, phb->gai_cur->ai_protocol)) < 0) {
+ event_debug( "socket failed: %d\n", errno);
+ continue;
+ }
+
+ sock_make_nonblocking(fd);
+
+ if (global.conf->iface_out)
+ {
+ me.sin_family = AF_INET;
+ me.sin_port = 0;
+ me.sin_addr.s_addr = inet_addr( global.conf->iface_out );
+
+ if (bind(fd, (struct sockaddr *) &me, sizeof(me)) != 0)
+ event_debug("bind( %d, \"%s\" ) failure\n", fd, global.conf->iface_out);
+ }
+
+ event_debug("proxy_connect_none( \"%s\", %d ) = %d\n", host, port, fd);
+
+ if (connect(fd, phb->gai_cur->ai_addr, phb->gai_cur->ai_addrlen) < 0 && !sockerr_again()) {
+ event_debug( "connect failed: %s\n", strerror(errno));
+ closesocket(fd);
+ fd = -1;
+ continue;
+ } else {
+ phb->inpa = b_input_add(fd, B_EV_IO_WRITE, gaim_io_connected, phb);
+ phb->fd = fd;
+
+ break;
+ }
+ }
+
+ if(fd < 0 && host)
+ g_free(phb);
+
+ return fd;
+}
+
+
+/* Connecting to HTTP proxies */
+
+#define HTTP_GOODSTRING "HTTP/1.0 200 Connection established"
+#define HTTP_GOODSTRING2 "HTTP/1.1 200 Connection established"
+
+static gboolean http_canread(gpointer data, gint source, b_input_condition cond)
+{
+ int nlc = 0;
+ int pos = 0;
+ struct PHB *phb = data;
+ char inputline[8192];
+
+ b_event_remove(phb->inpa);
+
+ while ((pos < sizeof(inputline)-1) && (nlc != 2) && (read(source, &inputline[pos++], 1) == 1)) {
+ if (inputline[pos - 1] == '\n')
+ nlc++;
+ else if (inputline[pos - 1] != '\r')
+ nlc = 0;
+ }
+ inputline[pos] = '\0';
+
+ if ((memcmp(HTTP_GOODSTRING, inputline, strlen(HTTP_GOODSTRING)) == 0) ||
+ (memcmp(HTTP_GOODSTRING2, inputline, strlen(HTTP_GOODSTRING2)) == 0)) {
+ phb->func(phb->data, source, B_EV_IO_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return FALSE;
+ }
+
+ close(source);
+ phb->func(phb->data, -1, B_EV_IO_READ);
+ g_free(phb->host);
+ g_free(phb);
+
+ return FALSE;
+}
+
+static gboolean http_canwrite(gpointer data, gint source, b_input_condition cond)
+{
+ char cmd[384];
+ struct PHB *phb = data;
+ unsigned int len;
+ int error = ETIMEDOUT;
+ if (phb->inpa > 0)
+ b_event_remove(phb->inpa);
+ len = sizeof(error);
+ if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
+ close(source);
+ phb->func(phb->data, -1, B_EV_IO_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return FALSE;
+ }
+ sock_make_blocking(source);
+
+ g_snprintf(cmd, sizeof(cmd), "CONNECT %s:%d HTTP/1.1\r\nHost: %s:%d\r\n", phb->host, phb->port,
+ phb->host, phb->port);
+ if (send(source, cmd, strlen(cmd), 0) < 0) {
+ close(source);
+ phb->func(phb->data, -1, B_EV_IO_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return FALSE;
+ }
+
+ if (strlen(proxyuser) > 0) {
+ char *t1, *t2;
+ t1 = g_strdup_printf("%s:%s", proxyuser, proxypass);
+ t2 = tobase64(t1);
+ g_free(t1);
+ g_snprintf(cmd, sizeof(cmd), "Proxy-Authorization: Basic %s\r\n", t2);
+ g_free(t2);
+ if (send(source, cmd, strlen(cmd), 0) < 0) {
+ close(source);
+ phb->func(phb->data, -1, B_EV_IO_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return FALSE;
+ }
+ }
+
+ g_snprintf(cmd, sizeof(cmd), "\r\n");
+ if (send(source, cmd, strlen(cmd), 0) < 0) {
+ close(source);
+ phb->func(phb->data, -1, B_EV_IO_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return FALSE;
+ }
+
+ phb->inpa = b_input_add(source, B_EV_IO_READ, http_canread, phb);
+
+ return FALSE;
+}
+
+static int proxy_connect_http(const char *host, unsigned short port, struct PHB *phb)
+{
+ phb->host = g_strdup(host);
+ phb->port = port;
+ phb->proxy_func = http_canwrite;
+ phb->proxy_data = phb;
+
+ return( proxy_connect_none( proxyhost, proxyport, phb ) );
+}
+
+
+/* Connecting to SOCKS4 proxies */
+
+static gboolean s4_canread(gpointer data, gint source, b_input_condition cond)
+{
+ unsigned char packet[12];
+ struct PHB *phb = data;
+
+ b_event_remove(phb->inpa);
+
+ memset(packet, 0, sizeof(packet));
+ if (read(source, packet, 9) >= 4 && packet[1] == 90) {
+ phb->func(phb->data, source, B_EV_IO_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return FALSE;
+ }
+
+ close(source);
+ phb->func(phb->data, -1, B_EV_IO_READ);
+ g_free(phb->host);
+ g_free(phb);
+
+ return FALSE;
+}
+
+static gboolean s4_canwrite(gpointer data, gint source, b_input_condition cond)
+{
+ unsigned char packet[12];
+ struct hostent *hp;
+ struct PHB *phb = data;
+ unsigned int len;
+ int error = ETIMEDOUT;
+ if (phb->inpa > 0)
+ b_event_remove(phb->inpa);
+ len = sizeof(error);
+ if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
+ close(source);
+ phb->func(phb->data, -1, B_EV_IO_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return FALSE;
+ }
+ sock_make_blocking(source);
+
+ /* XXX does socks4 not support host name lookups by the proxy? */
+ if (!(hp = gethostbyname(phb->host))) {
+ close(source);
+ phb->func(phb->data, -1, B_EV_IO_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return FALSE;
+ }
+
+ packet[0] = 4;
+ packet[1] = 1;
+ packet[2] = phb->port >> 8;
+ packet[3] = phb->port & 0xff;
+ packet[4] = (unsigned char)(hp->h_addr_list[0])[0];
+ packet[5] = (unsigned char)(hp->h_addr_list[0])[1];
+ packet[6] = (unsigned char)(hp->h_addr_list[0])[2];
+ packet[7] = (unsigned char)(hp->h_addr_list[0])[3];
+ packet[8] = 0;
+ if (write(source, packet, 9) != 9) {
+ close(source);
+ phb->func(phb->data, -1, B_EV_IO_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return FALSE;
+ }
+
+ phb->inpa = b_input_add(source, B_EV_IO_READ, s4_canread, phb);
+
+ return FALSE;
+}
+
+static int proxy_connect_socks4(const char *host, unsigned short port, struct PHB *phb)
+{
+ phb->host = g_strdup(host);
+ phb->port = port;
+ phb->proxy_func = s4_canwrite;
+ phb->proxy_data = phb;
+
+ return( proxy_connect_none( proxyhost, proxyport, phb ) );
+}
+
+
+/* Connecting to SOCKS5 proxies */
+
+static gboolean s5_canread_again(gpointer data, gint source, b_input_condition cond)
+{
+ unsigned char buf[512];
+ struct PHB *phb = data;
+
+ b_event_remove(phb->inpa);
+
+ if (read(source, buf, 10) < 10) {
+ close(source);
+ phb->func(phb->data, -1, B_EV_IO_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return FALSE;
+ }
+ if ((buf[0] != 0x05) || (buf[1] != 0x00)) {
+ close(source);
+ phb->func(phb->data, -1, B_EV_IO_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return FALSE;
+ }
+
+ phb->func(phb->data, source, B_EV_IO_READ);
+ g_free(phb->host);
+ g_free(phb);
+
+ return FALSE;
+}
+
+static void s5_sendconnect(gpointer data, gint source)
+{
+ unsigned char buf[512];
+ struct PHB *phb = data;
+ int hlen = strlen(phb->host);
+
+ buf[0] = 0x05;
+ buf[1] = 0x01; /* CONNECT */
+ buf[2] = 0x00; /* reserved */
+ buf[3] = 0x03; /* address type -- host name */
+ buf[4] = hlen;
+ memcpy(buf + 5, phb->host, hlen);
+ buf[5 + strlen(phb->host)] = phb->port >> 8;
+ buf[5 + strlen(phb->host) + 1] = phb->port & 0xff;
+
+ if (write(source, buf, (5 + strlen(phb->host) + 2)) < (5 + strlen(phb->host) + 2)) {
+ close(source);
+ phb->func(phb->data, -1, B_EV_IO_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return;
+ }
+
+ phb->inpa = b_input_add(source, B_EV_IO_READ, s5_canread_again, phb);
+}
+
+static gboolean s5_readauth(gpointer data, gint source, b_input_condition cond)
+{
+ unsigned char buf[512];
+ struct PHB *phb = data;
+
+ b_event_remove(phb->inpa);
+
+ if (read(source, buf, 2) < 2) {
+ close(source);
+ phb->func(phb->data, -1, B_EV_IO_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return FALSE;
+ }
+
+ if ((buf[0] != 0x01) || (buf[1] != 0x00)) {
+ close(source);
+ phb->func(phb->data, -1, B_EV_IO_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return FALSE;
+ }
+
+ s5_sendconnect(phb, source);
+
+ return FALSE;
+}
+
+static gboolean s5_canread(gpointer data, gint source, b_input_condition cond)
+{
+ unsigned char buf[512];
+ struct PHB *phb = data;
+
+ b_event_remove(phb->inpa);
+
+ if (read(source, buf, 2) < 2) {
+ close(source);
+ phb->func(phb->data, -1, B_EV_IO_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return FALSE;
+ }
+
+ if ((buf[0] != 0x05) || (buf[1] == 0xff)) {
+ close(source);
+ phb->func(phb->data, -1, B_EV_IO_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return FALSE;
+ }
+
+ if (buf[1] == 0x02) {
+ unsigned int i = strlen(proxyuser), j = strlen(proxypass);
+ buf[0] = 0x01; /* version 1 */
+ buf[1] = i;
+ memcpy(buf + 2, proxyuser, i);
+ buf[2 + i] = j;
+ memcpy(buf + 2 + i + 1, proxypass, j);
+ if (write(source, buf, 3 + i + j) < 3 + i + j) {
+ close(source);
+ phb->func(phb->data, -1, B_EV_IO_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return FALSE;
+ }
+
+ phb->inpa = b_input_add(source, B_EV_IO_READ, s5_readauth, phb);
+ } else {
+ s5_sendconnect(phb, source);
+ }
+
+ return FALSE;
+}
+
+static gboolean s5_canwrite(gpointer data, gint source, b_input_condition cond)
+{
+ unsigned char buf[512];
+ int i;
+ struct PHB *phb = data;
+ unsigned int len;
+ int error = ETIMEDOUT;
+ if (phb->inpa > 0)
+ b_event_remove(phb->inpa);
+ len = sizeof(error);
+ if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
+ close(source);
+ phb->func(phb->data, -1, B_EV_IO_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return FALSE;
+ }
+ sock_make_blocking(source);
+
+ i = 0;
+ buf[0] = 0x05; /* SOCKS version 5 */
+ if (proxyuser[0]) {
+ buf[1] = 0x02; /* two methods */
+ buf[2] = 0x00; /* no authentication */
+ buf[3] = 0x02; /* username/password authentication */
+ i = 4;
+ } else {
+ buf[1] = 0x01;
+ buf[2] = 0x00;
+ i = 3;
+ }
+
+ if (write(source, buf, i) < i) {
+ close(source);
+ phb->func(phb->data, -1, B_EV_IO_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return FALSE;
+ }
+
+ phb->inpa = b_input_add(source, B_EV_IO_READ, s5_canread, phb);
+
+ return FALSE;
+}
+
+static int proxy_connect_socks5(const char *host, unsigned short port, struct PHB *phb)
+{
+ phb->host = g_strdup(host);
+ phb->port = port;
+ phb->proxy_func = s5_canwrite;
+ phb->proxy_data = phb;
+
+ return( proxy_connect_none( proxyhost, proxyport, phb ) );
+}
+
+
+/* Export functions */
+
+int proxy_connect(const char *host, int port, b_event_handler func, gpointer data)
+{
+ struct PHB *phb;
+
+ if (!host || port <= 0 || !func || strlen(host) > 128) {
+ return -1;
+ }
+
+ phb = g_new0(struct PHB, 1);
+ phb->func = func;
+ phb->data = data;
+
+ 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);
+ else if (proxytype == PROXY_SOCKS4)
+ return proxy_connect_socks4(host, port, phb);
+ else if (proxytype == PROXY_SOCKS5)
+ return proxy_connect_socks5(host, port, phb);
+
+ g_free(phb);
+ return -1;
+}
diff --git a/lib/proxy.h b/lib/proxy.h
new file mode 100644
index 00000000..680790a5
--- /dev/null
+++ b/lib/proxy.h
@@ -0,0 +1,53 @@
+/*
+ * nogaim
+ *
+ * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/* this is the export part of the proxy.c file. it does a little
+ prototype-ing stuff and redefine some net function to mask them
+ with some kind of transparent layer */
+
+#ifndef _PROXY_H_
+#define _PROXY_H_
+
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#endif
+#include <glib.h>
+#include <gmodule.h>
+
+#include "events.h"
+
+#define PROXY_NONE 0
+#define PROXY_HTTP 1
+#define PROXY_SOCKS4 2
+#define PROXY_SOCKS5 3
+
+extern char proxyhost[128];
+extern int proxyport;
+extern int proxytype;
+extern char proxyuser[128];
+extern char proxypass[128];
+
+G_MODULE_EXPORT int proxy_connect(const char *host, int port, b_event_handler func, gpointer data);
+
+#endif /* _PROXY_H_ */
diff --git a/lib/sha1.c b/lib/sha1.c
new file mode 100644
index 00000000..7ee90640
--- /dev/null
+++ b/lib/sha1.c
@@ -0,0 +1,422 @@
+/*
+ * SHA1 hashing code copied from Lepton's crack <http://usuarios.lycos.es/reinob/>
+ *
+ * Adapted to be API-compatible with the previous (GPL-incompatible) code.
+ */
+
+/*
+ * sha1.c
+ *
+ * Description:
+ * This file implements the Secure Hashing Algorithm 1 as
+ * defined in FIPS PUB 180-1 published April 17, 1995.
+ *
+ * The SHA-1, produces a 160-bit message digest for a given
+ * data stream. It should take about 2**n steps to find a
+ * message with the same digest as a given message and
+ * 2**(n/2) to find any two messages with the same digest,
+ * when n is the digest size in bits. Therefore, this
+ * algorithm can serve as a means of providing a
+ * "fingerprint" for a message.
+ *
+ * Portability Issues:
+ * SHA-1 is defined in terms of 32-bit "words". This code
+ * uses <stdint.h> (included via "sha1.h" to define 32 and 8
+ * bit unsigned integer types. If your C compiler does not
+ * support 32 bit unsigned integers, this code is not
+ * appropriate.
+ *
+ * Caveats:
+ * SHA-1 is designed to work with messages less than 2^64 bits
+ * long. Although SHA-1 allows a message digest to be generated
+ * for messages of any number of bits less than 2^64, this
+ * implementation only works with messages with a length that is
+ * a multiple of the size of an 8-bit character.
+ *
+ */
+
+#include <string.h>
+#include "sha1.h"
+
+/*
+ * Define the SHA1 circular left shift macro
+ */
+#define SHA1CircularShift(bits,word) \
+ (((word) << (bits)) | ((word) >> (32-(bits))))
+
+/* Local Function Prototyptes */
+static void sha1_pad(sha1_state_t *);
+static void sha1_process_block(sha1_state_t *);
+
+/*
+ * sha1_init
+ *
+ * Description:
+ * This function will initialize the sha1_state_t in preparation
+ * for computing a new SHA1 message digest.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The context to reset.
+ *
+ * Returns:
+ * sha Error Code.
+ *
+ */
+int sha1_init(sha1_state_t * context)
+{
+ context->Length_Low = 0;
+ context->Length_High = 0;
+ context->Message_Block_Index = 0;
+
+ context->Intermediate_Hash[0] = 0x67452301;
+ context->Intermediate_Hash[1] = 0xEFCDAB89;
+ context->Intermediate_Hash[2] = 0x98BADCFE;
+ context->Intermediate_Hash[3] = 0x10325476;
+ context->Intermediate_Hash[4] = 0xC3D2E1F0;
+
+ context->Computed = 0;
+ context->Corrupted = 0;
+
+ return shaSuccess;
+}
+
+/*
+ * sha1_finish
+ *
+ * Description:
+ * This function will return the 160-bit message digest into the
+ * Message_Digest array provided by the caller.
+ * NOTE: The first octet of hash is stored in the 0th element,
+ * the last octet of hash in the 19th element.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The context to use to calculate the SHA-1 hash.
+ * Message_Digest: [out]
+ * Where the digest is returned.
+ *
+ * Returns:
+ * sha Error Code.
+ *
+ */
+int sha1_finish(sha1_state_t * context, uint8_t Message_Digest[sha1_hash_size])
+{
+ int i;
+
+ if (!context || !Message_Digest) {
+ return shaNull;
+ }
+
+ if (context->Corrupted) {
+ return context->Corrupted;
+ }
+
+ if (!context->Computed) {
+ sha1_pad(context);
+ for (i = 0; i < 64; ++i) {
+ /* message may be sensitive, clear it out */
+ context->Message_Block[i] = 0;
+ }
+ context->Length_Low = 0; /* and clear length */
+ context->Length_High = 0;
+ context->Computed = 1;
+
+ }
+
+ for (i = 0; i < sha1_hash_size; ++i) {
+ Message_Digest[i] = context->Intermediate_Hash[i >> 2]
+ >> 8 * (3 - (i & 0x03));
+ }
+
+ return shaSuccess;
+}
+
+/*
+ * sha1_append
+ *
+ * Description:
+ * This function accepts an array of octets as the next portion
+ * of the message.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The SHA context to update
+ * message_array: [in]
+ * An array of characters representing the next portion of
+ * the message.
+ * length: [in]
+ * The length of the message in message_array
+ *
+ * Returns:
+ * sha Error Code.
+ *
+ */
+int
+sha1_append(sha1_state_t * context,
+ const uint8_t * message_array, unsigned length)
+{
+ if (!length) {
+ return shaSuccess;
+ }
+
+ if (!context || !message_array) {
+ return shaNull;
+ }
+
+ if (context->Computed) {
+ context->Corrupted = shaStateError;
+
+ return shaStateError;
+ }
+
+ if (context->Corrupted) {
+ return context->Corrupted;
+ }
+ while (length-- && !context->Corrupted) {
+ context->Message_Block[context->Message_Block_Index++] =
+ (*message_array & 0xFF);
+
+ context->Length_Low += 8;
+ if (context->Length_Low == 0) {
+ context->Length_High++;
+ if (context->Length_High == 0) {
+ /* Message is too long */
+ context->Corrupted = 1;
+ }
+ }
+
+ if (context->Message_Block_Index == 64) {
+ sha1_process_block(context);
+ }
+
+ message_array++;
+ }
+
+ return shaSuccess;
+}
+
+/*
+ * sha1_process_block
+ *
+ * Description:
+ * This function will process the next 512 bits of the message
+ * stored in the Message_Block array.
+ *
+ * Parameters:
+ * None.
+ *
+ * Returns:
+ * Nothing.
+ *
+ * Comments:
+ * Many of the variable names in this code, especially the
+ * single character names, were used because those were the
+ * names used in the publication.
+ *
+ *
+ */
+static void sha1_process_block(sha1_state_t * context)
+{
+ const uint32_t K[] = { /* Constants defined in SHA-1 */
+ 0x5A827999,
+ 0x6ED9EBA1,
+ 0x8F1BBCDC,
+ 0xCA62C1D6
+ };
+ int t; /* Loop counter */
+ uint32_t temp; /* Temporary word value */
+ uint32_t W[80]; /* Word sequence */
+ uint32_t A, B, C, D, E; /* Word buffers */
+
+ /*
+ * Initialize the first 16 words in the array W
+ */
+ for (t = 0; t < 16; t++) {
+ W[t] = context->Message_Block[t * 4] << 24;
+ W[t] |= context->Message_Block[t * 4 + 1] << 16;
+ W[t] |= context->Message_Block[t * 4 + 2] << 8;
+ W[t] |= context->Message_Block[t * 4 + 3];
+ }
+
+ for (t = 16; t < 80; t++) {
+ W[t] =
+ SHA1CircularShift(1,
+ W[t - 3] ^ W[t - 8] ^ W[t -
+ 14] ^ W[t -
+ 16]);
+ }
+
+ A = context->Intermediate_Hash[0];
+ B = context->Intermediate_Hash[1];
+ C = context->Intermediate_Hash[2];
+ D = context->Intermediate_Hash[3];
+ E = context->Intermediate_Hash[4];
+
+ for (t = 0; t < 20; t++) {
+ temp = SHA1CircularShift(5, A) +
+ ((B & C) | ((~B) & D)) + E + W[t] + K[0];
+ E = D;
+ D = C;
+ C = SHA1CircularShift(30, B);
+
+ B = A;
+ A = temp;
+ }
+
+ for (t = 20; t < 40; t++) {
+ temp =
+ SHA1CircularShift(5,
+ A) + (B ^ C ^ D) + E + W[t] + K[1];
+ E = D;
+ D = C;
+ C = SHA1CircularShift(30, B);
+ B = A;
+ A = temp;
+ }
+
+ for (t = 40; t < 60; t++) {
+ temp = SHA1CircularShift(5, A) +
+ ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
+ E = D;
+ D = C;
+ C = SHA1CircularShift(30, B);
+ B = A;
+ A = temp;
+ }
+
+ for (t = 60; t < 80; t++) {
+ temp =
+ SHA1CircularShift(5,
+ A) + (B ^ C ^ D) + E + W[t] + K[3];
+ E = D;
+ D = C;
+ C = SHA1CircularShift(30, B);
+ B = A;
+ A = temp;
+ }
+
+ context->Intermediate_Hash[0] += A;
+ context->Intermediate_Hash[1] += B;
+ context->Intermediate_Hash[2] += C;
+ context->Intermediate_Hash[3] += D;
+ context->Intermediate_Hash[4] += E;
+
+ context->Message_Block_Index = 0;
+}
+
+/*
+ * sha1_pad
+ *
+ * Description:
+ * According to the standard, the message must be padded to an even
+ * 512 bits. The first padding bit must be a '1'. The last 64
+ * bits represent the length of the original message. All bits in
+ * between should be 0. This function will pad the message
+ * according to those rules by filling the Message_Block array
+ * accordingly. It will also call the ProcessMessageBlock function
+ * provided appropriately. When it returns, it can be assumed that
+ * the message digest has been computed.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The context to pad
+ * ProcessMessageBlock: [in]
+ * The appropriate SHA*ProcessMessageBlock function
+ * Returns:
+ * Nothing.
+ *
+ */
+
+static void sha1_pad(sha1_state_t * context)
+{
+ /*
+ * Check to see if the current message block is too small to hold
+ * the initial padding bits and length. If so, we will pad the
+ * block, process it, and then continue padding into a second
+ * block.
+ */
+ if (context->Message_Block_Index > 55) {
+ context->Message_Block[context->Message_Block_Index++] =
+ 0x80;
+ while (context->Message_Block_Index < 64) {
+ context->Message_Block[context->
+ Message_Block_Index++] = 0;
+ }
+
+ sha1_process_block(context);
+
+ while (context->Message_Block_Index < 56) {
+ context->Message_Block[context->
+ Message_Block_Index++] = 0;
+ }
+ } else {
+ context->Message_Block[context->Message_Block_Index++] =
+ 0x80;
+ while (context->Message_Block_Index < 56) {
+
+ context->Message_Block[context->
+ Message_Block_Index++] = 0;
+ }
+ }
+
+ /*
+ * Store the message length as the last 8 octets
+ */
+ context->Message_Block[56] = context->Length_High >> 24;
+ context->Message_Block[57] = context->Length_High >> 16;
+ context->Message_Block[58] = context->Length_High >> 8;
+ context->Message_Block[59] = context->Length_High;
+ context->Message_Block[60] = context->Length_Low >> 24;
+ context->Message_Block[61] = context->Length_Low >> 16;
+ context->Message_Block[62] = context->Length_Low >> 8;
+ context->Message_Block[63] = context->Length_Low;
+
+ sha1_process_block(context);
+}
+
+#define HMAC_BLOCK_SIZE 64
+
+/* BitlBee addition: */
+void sha1_hmac(const char *key_, size_t key_len, const char *payload, size_t payload_len, uint8_t Message_Digest[sha1_hash_size])
+{
+ sha1_state_t sha1;
+ uint8_t hash[sha1_hash_size];
+ uint8_t key[HMAC_BLOCK_SIZE+1];
+ int i;
+
+ if( key_len == 0 )
+ key_len = strlen( key_ );
+ if( payload_len == 0 )
+ payload_len = strlen( payload );
+
+ /* Create K. If our current key is >64 chars we have to hash it,
+ otherwise just pad. */
+ memset( key, 0, HMAC_BLOCK_SIZE + 1 );
+ if( key_len > HMAC_BLOCK_SIZE )
+ {
+ sha1_init( &sha1 );
+ sha1_append( &sha1, (uint8_t*) key_, key_len );
+ sha1_finish( &sha1, key );
+ }
+ else
+ {
+ memcpy( key, key_, key_len );
+ }
+
+ /* Inner part: H(K XOR 0x36, text) */
+ sha1_init( &sha1 );
+ for( i = 0; i < HMAC_BLOCK_SIZE; i ++ )
+ key[i] ^= 0x36;
+ sha1_append( &sha1, key, HMAC_BLOCK_SIZE );
+ sha1_append( &sha1, (const uint8_t*) payload, payload_len );
+ sha1_finish( &sha1, hash );
+
+ /* Final result: H(K XOR 0x5C, inner stuff) */
+ sha1_init( &sha1 );
+ for( i = 0; i < HMAC_BLOCK_SIZE; i ++ )
+ key[i] ^= 0x36 ^ 0x5c;
+ sha1_append( &sha1, key, HMAC_BLOCK_SIZE );
+ sha1_append( &sha1, hash, sha1_hash_size );
+ sha1_finish( &sha1, Message_Digest );
+}
diff --git a/lib/sha1.h b/lib/sha1.h
new file mode 100644
index 00000000..a87410eb
--- /dev/null
+++ b/lib/sha1.h
@@ -0,0 +1,71 @@
+/*
+ * SHA1 hashing code copied from Lepton's crack <http://usuarios.lycos.es/reinob/>
+ *
+ * Adapted to be API-compatible with the previous (GPL-incompatible) code.
+ */
+
+/*
+ * sha1.h
+ *
+ * Description:
+ * This is the header file for code which implements the Secure
+ * Hashing Algorithm 1 as defined in FIPS PUB 180-1 published
+ * April 17, 1995.
+ *
+ * Many of the variable names in this code, especially the
+ * single character names, were used because those were the names
+ * used in the publication.
+ *
+ * Please read the file sha1.c for more information.
+ *
+ */
+
+#ifndef _SHA1_H_
+#define _SHA1_H_
+
+#if(__sun)
+#include <inttypes.h>
+#else
+#include <stdint.h>
+#endif
+#include <gmodule.h>
+
+#ifndef _SHA_enum_
+#define _SHA_enum_
+enum {
+ shaSuccess = 0,
+ shaNull, /* Null pointer parameter */
+ shaInputTooLong, /* input data too long */
+ shaStateError /* called Input after Result */
+};
+#endif
+#define sha1_hash_size 20
+
+/*
+ * This structure will hold context information for the SHA-1
+ * hashing operation
+ */
+typedef struct SHA1Context {
+ uint32_t Intermediate_Hash[sha1_hash_size/4]; /* Message Digest */
+
+ uint32_t Length_Low; /* Message length in bits */
+ uint32_t Length_High; /* Message length in bits */
+
+ /* Index into message block array */
+ int_least16_t Message_Block_Index;
+ uint8_t Message_Block[64]; /* 512-bit message blocks */
+
+ int Computed; /* Is the digest computed? */
+ int Corrupted; /* Is the message digest corrupted? */
+} sha1_state_t;
+
+/*
+ * Function Prototypes
+ */
+
+G_MODULE_EXPORT int sha1_init(sha1_state_t *);
+G_MODULE_EXPORT int sha1_append(sha1_state_t *, const uint8_t *, unsigned int);
+G_MODULE_EXPORT int sha1_finish(sha1_state_t *, uint8_t Message_Digest[sha1_hash_size]);
+G_MODULE_EXPORT void sha1_hmac(const char *key_, size_t key_len, const char *payload, size_t payload_len, uint8_t Message_Digest[sha1_hash_size]);
+
+#endif
diff --git a/lib/ssl_bogus.c b/lib/ssl_bogus.c
new file mode 100644
index 00000000..f4ce5d4d
--- /dev/null
+++ b/lib/ssl_bogus.c
@@ -0,0 +1,71 @@
+ /********************************************************************\
+ * BitlBee -- An IRC to other IM-networks gateway *
+ * *
+ * Copyright 2002-2004 Wilmer van der Gaast and others *
+ \********************************************************************/
+
+/* SSL module - dummy version */
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License with
+ the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+ if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "ssl_client.h"
+
+int ssl_errno;
+
+void ssl_init( void )
+{
+}
+
+void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data )
+{
+ return( NULL );
+}
+
+int ssl_read( void *conn, char *buf, int len )
+{
+ return( -1 );
+}
+
+int ssl_write( void *conn, const char *buf, int len )
+{
+ return( -1 );
+}
+
+void ssl_disconnect( void *conn_ )
+{
+}
+
+int ssl_getfd( void *conn )
+{
+ return( -1 );
+}
+
+void *ssl_starttls( int fd, ssl_input_function func, gpointer data )
+{
+ return NULL;
+}
+
+b_input_condition ssl_getdirection( void *conn )
+{
+ return B_EV_IO_READ;
+}
+
+int ssl_pending( void *conn )
+{
+ return 0;
+}
diff --git a/lib/ssl_client.h b/lib/ssl_client.h
new file mode 100644
index 00000000..d0340840
--- /dev/null
+++ b/lib/ssl_client.h
@@ -0,0 +1,84 @@
+ /********************************************************************\
+ * BitlBee -- An IRC to other IM-networks gateway *
+ * *
+ * Copyright 2002-2004 Wilmer van der Gaast and others *
+ \********************************************************************/
+
+/* SSL module */
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License with
+ the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+ if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/* ssl_client makes it easier to open SSL connections to servers. (It
+ doesn't offer SSL server functionality yet, but it could be useful
+ to add it later.) Different ssl_client modules are available, and
+ ssl_client tries to make them all behave the same. It's very simple
+ and basic, it just imitates the proxy_connect() function from the
+ Gaim libs and passes the socket to the program once the handshake
+ is completed. */
+
+#include <glib.h>
+#include "proxy.h"
+
+/* Some generic error codes. Especially SSL_AGAIN is important if you
+ want to do asynchronous I/O. */
+#define SSL_OK 0
+#define SSL_NOHANDSHAKE 1
+#define SSL_AGAIN 2
+
+extern int ssl_errno;
+
+/* This is what your callback function should look like. */
+typedef gboolean (*ssl_input_function)(gpointer, void*, b_input_condition);
+
+
+/* Perform any global initialization the SSL library might need. */
+G_MODULE_EXPORT void ssl_init( void );
+
+/* Connect to host:port, call the given function when the connection is
+ ready to be used for SSL traffic. This is all done asynchronously, no
+ blocking I/O! (Except for the DNS lookups, for now...) */
+G_MODULE_EXPORT void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data );
+
+/* Start an SSL session on an existing fd. Useful for STARTTLS functionality,
+ for example in Jabber. */
+G_MODULE_EXPORT void *ssl_starttls( int fd, ssl_input_function func, gpointer data );
+
+/* Obviously you need special read/write functions to read data. */
+G_MODULE_EXPORT int ssl_read( void *conn, char *buf, int len );
+G_MODULE_EXPORT int ssl_write( void *conn, const char *buf, int len );
+
+/* 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_ );
+
+/* Get the fd for this connection, you will usually need it for event
+ handling. */
+G_MODULE_EXPORT int ssl_getfd( void *conn );
+
+/* This function returns B_EV_IO_READ/WRITE. With SSL connections it's
+ possible that something has to be read while actually were trying to
+ write something (think about key exchange/refresh/etc). So when an
+ SSL operation returned SSL_AGAIN, *always* use this function when
+ adding an event handler to the queue. (And it should perform exactly
+ the same action as the handler that just received the SSL_AGAIN.) */
+G_MODULE_EXPORT b_input_condition ssl_getdirection( void *conn );
+
+G_MODULE_EXPORT size_t ssl_des3_encrypt(const unsigned char *key, size_t key_len, const unsigned char *input, size_t input_len, const unsigned char *iv, unsigned char **res);
diff --git a/lib/ssl_gnutls.c b/lib/ssl_gnutls.c
new file mode 100644
index 00000000..cdc7c498
--- /dev/null
+++ b/lib/ssl_gnutls.c
@@ -0,0 +1,280 @@
+ /********************************************************************\
+ * BitlBee -- An IRC to other IM-networks gateway *
+ * *
+ * Copyright 2002-2004 Wilmer van der Gaast and others *
+ \********************************************************************/
+
+/* SSL module - GnuTLS version */
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License with
+ the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+ if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <gnutls/gnutls.h>
+#include <gcrypt.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include "proxy.h"
+#include "ssl_client.h"
+#include "sock.h"
+#include "stdlib.h"
+
+int ssl_errno = 0;
+
+static gboolean initialized = FALSE;
+
+#include <limits.h>
+
+#if defined(ULONG_MAX) && ULONG_MAX > 4294967295UL
+#define GNUTLS_STUPID_CAST (long)
+#else
+#define GNUTLS_STUPID_CAST (int)
+#endif
+
+struct scd
+{
+ ssl_input_function func;
+ gpointer data;
+ int fd;
+ gboolean established;
+ int inpa;
+
+ gnutls_session session;
+ gnutls_certificate_credentials xcred;
+};
+
+static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond );
+static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond );
+static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond );
+
+
+void ssl_init( void )
+{
+ if( initialized )
+ return;
+
+ gnutls_global_init();
+ initialized = TRUE;
+ atexit( gnutls_global_deinit );
+}
+
+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;
+ }
+
+ return conn;
+}
+
+void *ssl_starttls( int fd, ssl_input_function func, gpointer data )
+{
+ struct scd *conn = g_new0( struct scd, 1 );
+
+ conn->fd = fd;
+ conn->func = func;
+ conn->data = data;
+ conn->inpa = -1;
+
+ /* This function should be called via a (short) timeout instead of
+ directly from here, because these SSL calls are *supposed* to be
+ *completely* asynchronous and not ready yet when this function
+ (or *_connect, for examle) returns. Also, errors are reported via
+ the callback function, not via this function's return value.
+
+ In short, doing things like this makes the rest of the code a lot
+ simpler. */
+
+ b_timeout_add( 1, ssl_starttls_real, conn );
+
+ return conn;
+}
+
+static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond )
+{
+ struct scd *conn = data;
+
+ return ssl_connected( conn, conn->fd, B_EV_IO_WRITE );
+}
+
+static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond )
+{
+ struct scd *conn = data;
+
+ if( source == -1 )
+ {
+ conn->func( conn->data, NULL, cond );
+ g_free( conn );
+ return FALSE;
+ }
+
+ ssl_init();
+
+ gnutls_certificate_allocate_credentials( &conn->xcred );
+ gnutls_init( &conn->session, GNUTLS_CLIENT );
+ gnutls_set_default_priority( conn->session );
+ gnutls_credentials_set( conn->session, GNUTLS_CRD_CERTIFICATE, conn->xcred );
+
+ sock_make_nonblocking( conn->fd );
+ gnutls_transport_set_ptr( conn->session, (gnutls_transport_ptr) GNUTLS_STUPID_CAST conn->fd );
+
+ return ssl_handshake( data, source, cond );
+}
+
+static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond )
+{
+ struct scd *conn = data;
+ int st;
+
+ if( ( st = gnutls_handshake( conn->session ) ) < 0 )
+ {
+ if( st == GNUTLS_E_AGAIN || st == GNUTLS_E_INTERRUPTED )
+ {
+ conn->inpa = b_input_add( conn->fd, ssl_getdirection( conn ),
+ ssl_handshake, data );
+ }
+ else
+ {
+ conn->func( conn->data, NULL, cond );
+
+ gnutls_deinit( conn->session );
+ gnutls_certificate_free_credentials( conn->xcred );
+ closesocket( conn->fd );
+
+ g_free( conn );
+ }
+ }
+ else
+ {
+ /* For now we can't handle non-blocking perfectly everywhere... */
+ sock_make_blocking( conn->fd );
+
+ conn->established = TRUE;
+ conn->func( conn->data, conn, cond );
+ }
+
+ return FALSE;
+}
+
+int ssl_read( void *conn, char *buf, int len )
+{
+ int st;
+
+ if( !((struct scd*)conn)->established )
+ {
+ ssl_errno = SSL_NOHANDSHAKE;
+ return( -1 );
+ }
+
+ st = gnutls_record_recv( ((struct scd*)conn)->session, buf, len );
+
+ ssl_errno = SSL_OK;
+ if( st == GNUTLS_E_AGAIN || st == GNUTLS_E_INTERRUPTED )
+ ssl_errno = SSL_AGAIN;
+
+ if( 0 && getenv( "BITLBEE_DEBUG" ) && st > 0 ) len = write( 1, buf, st );
+
+ return st;
+}
+
+int ssl_write( void *conn, const char *buf, int len )
+{
+ int st;
+
+ if( !((struct scd*)conn)->established )
+ {
+ ssl_errno = SSL_NOHANDSHAKE;
+ return( -1 );
+ }
+
+ st = gnutls_record_send( ((struct scd*)conn)->session, buf, len );
+
+ ssl_errno = SSL_OK;
+ if( st == GNUTLS_E_AGAIN || st == GNUTLS_E_INTERRUPTED )
+ ssl_errno = SSL_AGAIN;
+
+ if( 0 && getenv( "BITLBEE_DEBUG" ) && st > 0 ) len = write( 1, buf, st );
+
+ 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_;
+
+ if( conn->inpa != -1 )
+ b_event_remove( conn->inpa );
+
+ if( conn->established )
+ gnutls_bye( conn->session, GNUTLS_SHUT_WR );
+
+ closesocket( conn->fd );
+
+ if( conn->session )
+ gnutls_deinit( conn->session );
+ if( conn->xcred )
+ gnutls_certificate_free_credentials( conn->xcred );
+ g_free( conn );
+}
+
+int ssl_getfd( void *conn )
+{
+ return( ((struct scd*)conn)->fd );
+}
+
+b_input_condition ssl_getdirection( void *conn )
+{
+ return( gnutls_record_get_direction( ((struct scd*)conn)->session ) ?
+ B_EV_IO_WRITE : B_EV_IO_READ );
+}
+
+size_t ssl_des3_encrypt( const unsigned char *key, size_t key_len, const unsigned char *input,
+ size_t input_len, const unsigned char *iv, unsigned char **res )
+{
+ gcry_cipher_hd_t gcr;
+ gcry_error_t st;
+
+ ssl_init();
+
+ *res = g_malloc( input_len );
+ st = gcry_cipher_open( &gcr, GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_CBC, 0 ) ||
+ gcry_cipher_setkey( gcr, key, key_len ) ||
+ gcry_cipher_setiv( gcr, iv, 8 ) ||
+ gcry_cipher_encrypt( gcr, *res, input_len, input, input_len );
+
+ gcry_cipher_close( gcr );
+
+ if( st == 0 )
+ return input_len;
+
+ g_free( *res );
+ return 0;
+}
diff --git a/lib/ssl_nss.c b/lib/ssl_nss.c
new file mode 100644
index 00000000..512c7655
--- /dev/null
+++ b/lib/ssl_nss.c
@@ -0,0 +1,240 @@
+ /********************************************************************\
+ * BitlBee -- An IRC to other IM-networks gateway *
+ * *
+ * Copyright 2002-2005 Wilmer van der Gaast and others *
+ \********************************************************************/
+
+/* SSL module - NSS version */
+
+/* Copyright 2005 Jelmer Vernooij */
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License with
+ the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+ if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "bitlbee.h"
+#include "proxy.h"
+#include "ssl_client.h"
+#include "sock.h"
+#include <nspr.h>
+#include <prio.h>
+#include <sslproto.h>
+#include <nss.h>
+#include <pk11pub.h>
+#include <private/pprio.h>
+#include <ssl.h>
+#include <seccomon.h>
+#include <secerr.h>
+#include <sslerr.h>
+
+int ssl_errno = 0;
+
+static gboolean initialized = FALSE;
+
+struct scd
+{
+ ssl_input_function func;
+ gpointer data;
+ int fd;
+ PRFileDesc *prfd;
+ gboolean established;
+};
+
+static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond );
+static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond );
+
+
+static SECStatus nss_auth_cert (void *arg, PRFileDesc *socket, PRBool checksig, PRBool isserver)
+{
+ return SECSuccess;
+}
+
+static SECStatus nss_bad_cert (void *arg, PRFileDesc *socket)
+{
+ PRErrorCode err;
+
+ if(!arg) return SECFailure;
+
+ *(PRErrorCode *)arg = err = PORT_GetError();
+
+ switch(err) {
+ case SEC_ERROR_INVALID_AVA:
+ case SEC_ERROR_INVALID_TIME:
+ case SEC_ERROR_BAD_SIGNATURE:
+ case SEC_ERROR_EXPIRED_CERTIFICATE:
+ case SEC_ERROR_UNKNOWN_ISSUER:
+ case SEC_ERROR_UNTRUSTED_CERT:
+ case SEC_ERROR_CERT_VALID:
+ case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
+ case SEC_ERROR_CRL_EXPIRED:
+ case SEC_ERROR_CRL_BAD_SIGNATURE:
+ case SEC_ERROR_EXTENSION_VALUE_INVALID:
+ case SEC_ERROR_CA_CERT_INVALID:
+ case SEC_ERROR_CERT_USAGES_INVALID:
+ case SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION:
+ return SECSuccess;
+
+ default:
+ return SECFailure;
+ }
+}
+
+
+void ssl_init( void )
+{
+ PR_Init( PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
+ NSS_NoDB_Init(NULL);
+ NSS_SetDomesticPolicy();
+ initialized = TRUE;
+}
+
+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;
+
+ if( conn->fd < 0 )
+ {
+ g_free( conn );
+ return( NULL );
+ }
+
+ if( !initialized )
+ {
+ ssl_init();
+ }
+
+
+ return( conn );
+}
+
+static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond )
+{
+ struct scd *conn = data;
+
+ return ssl_connected( conn, conn->fd, B_EV_IO_WRITE );
+}
+
+void *ssl_starttls( int fd, ssl_input_function func, gpointer data )
+{
+ struct scd *conn = g_new0( struct scd, 1 );
+
+ conn->fd = fd;
+ conn->func = func;
+ conn->data = data;
+
+ /* This function should be called via a (short) timeout instead of
+ directly from here, because these SSL calls are *supposed* to be
+ *completely* asynchronous and not ready yet when this function
+ (or *_connect, for examle) returns. Also, errors are reported via
+ the callback function, not via this function's return value.
+
+ In short, doing things like this makes the rest of the code a lot
+ simpler. */
+
+ b_timeout_add( 1, ssl_starttls_real, conn );
+
+ return conn;
+}
+
+static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond )
+{
+ struct scd *conn = data;
+
+ if( source == -1 )
+ goto ssl_connected_failure;
+
+ /* Until we find out how to handle non-blocking I/O with NSS... */
+ sock_make_blocking( conn->fd );
+
+ conn->prfd = SSL_ImportFD(NULL, PR_ImportTCPSocket(source));
+ SSL_OptionSet(conn->prfd, SSL_SECURITY, PR_TRUE);
+ SSL_OptionSet(conn->prfd, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE);
+ SSL_BadCertHook(conn->prfd, (SSLBadCertHandler)nss_bad_cert, NULL);
+ SSL_AuthCertificateHook(conn->prfd, (SSLAuthCertificate)nss_auth_cert, (void *)CERT_GetDefaultCertDB());
+ SSL_ResetHandshake(conn->prfd, PR_FALSE);
+
+ if (SSL_ForceHandshake(conn->prfd)) {
+ goto ssl_connected_failure;
+ }
+
+
+ conn->established = TRUE;
+ conn->func( conn->data, conn, cond );
+ return FALSE;
+
+ ssl_connected_failure:
+
+ conn->func( conn->data, NULL, cond );
+
+ PR_Close( conn -> prfd );
+ if( source >= 0 ) closesocket( source );
+ g_free( conn );
+
+ return FALSE;
+}
+
+int ssl_read( void *conn, char *buf, int len )
+{
+ if( !((struct scd*)conn)->established )
+ return( 0 );
+
+ return( PR_Read( ((struct scd*)conn)->prfd, buf, len ) );
+}
+
+int ssl_write( void *conn, const char *buf, int len )
+{
+ if( !((struct scd*)conn)->established )
+ return( 0 );
+
+ return( PR_Write ( ((struct scd*)conn)->prfd, buf, len ) );
+}
+
+/* See ssl_openssl.c for an explanation. */
+int ssl_pending( void *conn )
+{
+ struct scd *c = (struct scd *) conn;
+
+ if( c == NULL ) {
+ return 0;
+ }
+
+ return ( c->established && SSL_DataPending( c->prfd ) > 0 );
+}
+
+void ssl_disconnect( void *conn_ )
+{
+ struct scd *conn = conn_;
+
+ PR_Close( conn->prfd );
+ closesocket( conn->fd );
+
+ g_free( conn );
+}
+
+int ssl_getfd( void *conn )
+{
+ return( ((struct scd*)conn)->fd );
+}
+
+b_input_condition ssl_getdirection( void *conn )
+{
+ /* Just in case someone calls us, let's return the most likely case: */
+ return B_EV_IO_READ;
+}
diff --git a/lib/ssl_openssl.c b/lib/ssl_openssl.c
new file mode 100644
index 00000000..64bc9257
--- /dev/null
+++ b/lib/ssl_openssl.c
@@ -0,0 +1,305 @@
+ /********************************************************************\
+ * BitlBee -- An IRC to other IM-networks gateway *
+ * *
+ * Copyright 2002-2004 Wilmer van der Gaast and others *
+ \********************************************************************/
+
+/* SSL module - OpenSSL version */
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License with
+ the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+ if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <openssl/crypto.h>
+#include <openssl/rand.h>
+#include <openssl/x509.h>
+#include <openssl/pem.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+
+#include "proxy.h"
+#include "ssl_client.h"
+#include "sock.h"
+
+int ssl_errno = 0;
+
+static gboolean initialized = FALSE;
+
+struct scd
+{
+ ssl_input_function func;
+ gpointer data;
+ int fd;
+ gboolean established;
+
+ int inpa;
+ int lasterr; /* Necessary for SSL_get_error */
+ SSL *ssl;
+ SSL_CTX *ssl_ctx;
+};
+
+static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond );
+static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond );
+static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond );
+
+
+void ssl_init( void )
+{
+ initialized = TRUE;
+ SSL_library_init();
+ // SSLeay_add_ssl_algorithms();
+}
+
+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 );
+ if( conn->fd < 0 )
+ {
+ g_free( conn );
+ return NULL;
+ }
+
+ conn->func = func;
+ conn->data = data;
+ conn->inpa = -1;
+
+ return conn;
+}
+
+void *ssl_starttls( int fd, ssl_input_function func, gpointer data )
+{
+ struct scd *conn = g_new0( struct scd, 1 );
+
+ conn->fd = fd;
+ conn->func = func;
+ conn->data = data;
+ conn->inpa = -1;
+
+ /* This function should be called via a (short) timeout instead of
+ directly from here, because these SSL calls are *supposed* to be
+ *completely* asynchronous and not ready yet when this function
+ (or *_connect, for examle) returns. Also, errors are reported via
+ the callback function, not via this function's return value.
+
+ In short, doing things like this makes the rest of the code a lot
+ simpler. */
+
+ b_timeout_add( 1, ssl_starttls_real, conn );
+
+ return conn;
+}
+
+static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond )
+{
+ struct scd *conn = data;
+
+ return ssl_connected( conn, conn->fd, B_EV_IO_WRITE );
+}
+
+static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond )
+{
+ struct scd *conn = data;
+ SSL_METHOD *meth;
+
+ if( source == -1 )
+ goto ssl_connected_failure;
+
+ if( !initialized )
+ {
+ ssl_init();
+ }
+
+ meth = TLSv1_client_method();
+ conn->ssl_ctx = SSL_CTX_new( meth );
+ if( conn->ssl_ctx == NULL )
+ goto ssl_connected_failure;
+
+ conn->ssl = SSL_new( conn->ssl_ctx );
+ if( conn->ssl == NULL )
+ goto ssl_connected_failure;
+
+ /* We can do at least the handshake with non-blocking I/O */
+ sock_make_nonblocking( conn->fd );
+ SSL_set_fd( conn->ssl, conn->fd );
+
+ return ssl_handshake( data, source, cond );
+
+ssl_connected_failure:
+ conn->func( conn->data, NULL, cond );
+
+ if( conn->ssl )
+ {
+ SSL_shutdown( conn->ssl );
+ SSL_free( conn->ssl );
+ }
+ if( conn->ssl_ctx )
+ {
+ SSL_CTX_free( conn->ssl_ctx );
+ }
+ if( source >= 0 ) closesocket( source );
+ g_free( conn );
+
+ return FALSE;
+
+}
+
+static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond )
+{
+ struct scd *conn = data;
+ int st;
+
+ if( ( st = SSL_connect( conn->ssl ) ) < 0 )
+ {
+ conn->lasterr = SSL_get_error( conn->ssl, st );
+ if( conn->lasterr != SSL_ERROR_WANT_READ && conn->lasterr != SSL_ERROR_WANT_WRITE )
+ {
+ conn->func( conn->data, NULL, cond );
+
+ SSL_shutdown( conn->ssl );
+ SSL_free( conn->ssl );
+ SSL_CTX_free( conn->ssl_ctx );
+
+ if( source >= 0 ) closesocket( source );
+ g_free( conn );
+
+ return FALSE;
+ }
+
+ conn->inpa = b_input_add( conn->fd, ssl_getdirection( conn ), ssl_handshake, data );
+ return FALSE;
+ }
+
+ conn->established = TRUE;
+ sock_make_blocking( conn->fd ); /* For now... */
+ conn->func( conn->data, conn, cond );
+ return FALSE;
+}
+
+int ssl_read( void *conn, char *buf, int len )
+{
+ int st;
+
+ if( !((struct scd*)conn)->established )
+ {
+ ssl_errno = SSL_NOHANDSHAKE;
+ return -1;
+ }
+
+ st = SSL_read( ((struct scd*)conn)->ssl, buf, len );
+
+ ssl_errno = SSL_OK;
+ if( st <= 0 )
+ {
+ ((struct scd*)conn)->lasterr = SSL_get_error( ((struct scd*)conn)->ssl, st );
+ if( ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_READ || ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_WRITE )
+ ssl_errno = SSL_AGAIN;
+ }
+
+ if( 0 && getenv( "BITLBEE_DEBUG" ) && st > 0 ) write( 1, buf, st );
+
+ return st;
+}
+
+int ssl_write( void *conn, const char *buf, int len )
+{
+ int st;
+
+ if( !((struct scd*)conn)->established )
+ {
+ ssl_errno = SSL_NOHANDSHAKE;
+ return -1;
+ }
+
+ st = SSL_write( ((struct scd*)conn)->ssl, buf, len );
+
+ if( 0 && getenv( "BITLBEE_DEBUG" ) && st > 0 ) write( 1, buf, st );
+
+ ssl_errno = SSL_OK;
+ if( st <= 0 )
+ {
+ ((struct scd*)conn)->lasterr = SSL_get_error( ((struct scd*)conn)->ssl, st );
+ if( ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_READ || ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_WRITE )
+ ssl_errno = SSL_AGAIN;
+ }
+
+ return st;
+}
+
+/* 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_;
+
+ if( conn->inpa != -1 )
+ b_event_remove( conn->inpa );
+
+ if( conn->established )
+ SSL_shutdown( conn->ssl );
+
+ closesocket( conn->fd );
+
+ SSL_free( conn->ssl );
+ SSL_CTX_free( conn->ssl_ctx );
+ g_free( conn );
+}
+
+int ssl_getfd( void *conn )
+{
+ return( ((struct scd*)conn)->fd );
+}
+
+b_input_condition ssl_getdirection( void *conn )
+{
+ return( ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_WRITE ? B_EV_IO_WRITE : B_EV_IO_READ );
+}
+
+size_t ssl_des3_encrypt(const unsigned char *key, size_t key_len, const unsigned char *input, size_t input_len, const unsigned char *iv, unsigned char **res)
+{
+ int output_length = 0;
+ EVP_CIPHER_CTX ctx;
+
+ *res = g_new0(unsigned char, 72);
+
+ /* Don't set key or IV because we will modify the parameters */
+ EVP_CIPHER_CTX_init(&ctx);
+ EVP_CipherInit_ex(&ctx, EVP_des_ede3_cbc(), NULL, NULL, NULL, 1);
+ EVP_CIPHER_CTX_set_key_length(&ctx, key_len);
+ EVP_CIPHER_CTX_set_padding(&ctx, 0);
+ /* We finished modifying parameters so now we can set key and IV */
+ EVP_CipherInit_ex(&ctx, NULL, NULL, key, iv, 1);
+ EVP_CipherUpdate(&ctx, *res, &output_length, input, input_len);
+ EVP_CipherFinal_ex(&ctx, *res, &output_length);
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ //EVP_cleanup();
+
+ return output_length;
+}
diff --git a/lib/ssl_sspi.c b/lib/ssl_sspi.c
new file mode 100644
index 00000000..e14c451e
--- /dev/null
+++ b/lib/ssl_sspi.c
@@ -0,0 +1,278 @@
+ /********************************************************************\
+ * BitlBee -- An IRC to other IM-networks gateway *
+ * *
+ * Copyright 2002-2004 Wilmer van der Gaast and others *
+ \********************************************************************/
+
+/* SSL module - SSPI backend */
+
+/* Copyright (C) 2005 Jelmer Vernooij <jelmer@samba.org> */
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License with
+ the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+ if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "ssl_client.h"
+#include <windows.h>
+#define SECURITY_WIN32
+#include <security.h>
+#include <sspi.h>
+#include <schannel.h>
+#include "sock.h"
+
+static gboolean initialized = FALSE;
+int ssl_errno;
+
+struct scd
+{
+ int fd;
+ ssl_input_function func;
+ gpointer data;
+ gboolean established;
+ CredHandle cred; /* SSL credentials */
+ CtxtHandle context; /* SSL context */
+ SecPkgContext_StreamSizes sizes;
+
+ char *host;
+
+ char *pending_raw_data;
+ gsize pending_raw_data_len;
+ char *pending_data;
+ gsize pending_data_len;
+};
+
+static void ssl_connected(gpointer, gint, GaimInputCondition);
+
+void sspi_global_init(void)
+{
+ /* FIXME */
+}
+
+void sspi_global_deinit(void)
+{
+ /* FIXME */
+}
+
+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);
+ sock_make_nonblocking(conn->fd);
+ conn->func = func;
+ conn->data = data;
+ conn->host = g_strdup(host);
+
+ if (conn->fd < 0)
+ {
+ g_free(conn);
+ return NULL;
+ }
+
+ if (!initialized)
+ {
+ sspi_global_init();
+ initialized = TRUE;
+ atexit(sspi_global_deinit);
+ }
+
+ return conn;
+}
+
+static void ssl_connected(gpointer _conn, gint fd, GaimInputCondition cond)
+{
+ struct scd *conn = _conn;
+ SCHANNEL_CRED ssl_cred;
+ TimeStamp timestamp;
+ SecBuffer ibuf[2],obuf[1];
+ SecBufferDesc ibufs,obufs;
+ ULONG req = ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT |
+ ISC_REQ_CONFIDENTIALITY | ISC_REQ_USE_SESSION_KEY |
+ ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM | ISC_REQ_EXTENDED_ERROR |
+ ISC_REQ_MANUAL_CRED_VALIDATION;
+ ULONG a;
+ gsize size = 0;
+ gchar *data = NULL;
+
+ memset(&ssl_cred, 0, sizeof(SCHANNEL_CRED));
+ ssl_cred.dwVersion = SCHANNEL_CRED_VERSION;
+ ssl_cred.grbitEnabledProtocols = SP_PROT_SSL3_CLIENT;
+
+ SECURITY_STATUS st = AcquireCredentialsHandle(NULL, UNISP_NAME, SECPKG_CRED_OUTBOUND, NULL, &ssl_cred, NULL, NULL, &conn->cred, &timestamp);
+
+ if (st != SEC_E_OK) {
+ conn->func(conn->data, NULL, cond);
+ return;
+ }
+
+ do {
+ /* initialize buffers */
+ ibuf[0].cbBuffer = size; ibuf[0].pvBuffer = data;
+ ibuf[1].cbBuffer = 0; ibuf[1].pvBuffer = NULL;
+ obuf[0].cbBuffer = 0; obuf[0].pvBuffer = NULL;
+ ibuf[0].BufferType = obuf[0].BufferType = SECBUFFER_TOKEN;
+ ibuf[1].BufferType = SECBUFFER_EMPTY;
+
+ /* initialize buffer descriptors */
+ ibufs.ulVersion = obufs.ulVersion = SECBUFFER_VERSION;
+ ibufs.cBuffers = 2; obufs.cBuffers = 1;
+ ibufs.pBuffers = ibuf; obufs.pBuffers = obuf;
+
+ st = InitializeSecurityContext(&conn->cred, size?&conn->context:NULL, conn->host, req, 0, SECURITY_NETWORK_DREP, size?&ibufs:NULL, 0, &conn->context, &obufs, &a, &timestamp);
+ if (obuf[0].pvBuffer && obuf[0].cbBuffer) {
+ /* FIXME: Check return value */
+ send(conn->fd, obuf[0].pvBuffer, obuf[0].cbBuffer, 0);
+ }
+
+ switch (st) {
+ case SEC_I_INCOMPLETE_CREDENTIALS:
+ break;
+ case SEC_I_CONTINUE_NEEDED:
+ break;
+ case SEC_E_INCOMPLETE_MESSAGE:
+ break;
+ case SEC_E_OK:
+ break;
+ }
+
+ QueryContextAttributes(&conn->context, SECPKG_ATTR_STREAM_SIZES, &conn->sizes);
+ } while (1);
+
+ conn->func(conn->data, conn, cond);
+}
+
+int ssl_read(void *conn, char *retdata, int len)
+{
+ struct scd *scd = conn;
+ SecBufferDesc msg;
+ SecBuffer buf[4];
+ int ret = -1, i;
+ char *data = g_malloc(scd->sizes.cbHeader + scd->sizes.cbMaximumMessage + scd->sizes.cbTrailer);
+
+ /* FIXME: Try to read some data */
+
+ msg.ulVersion = SECBUFFER_VERSION;
+ msg.cBuffers = 4;
+ msg.pBuffers = buf;
+
+ buf[0].BufferType = SECBUFFER_DATA;
+ buf[0].cbBuffer = len;
+ buf[0].pvBuffer = data;
+
+ buf[1].BufferType = SECBUFFER_EMPTY;
+ buf[2].BufferType = SECBUFFER_EMPTY;
+ buf[3].BufferType = SECBUFFER_EMPTY;
+
+ SECURITY_STATUS st = DecryptMessage(&scd->context, &msg, 0, NULL);
+
+ if (st != SEC_E_OK) {
+ /* FIXME */
+ return -1;
+ }
+
+ for (i = 0; i < 4; i++) {
+ if (buf[i].BufferType == SECBUFFER_DATA) {
+ memcpy(retdata, buf[i].pvBuffer, len);
+ ret = len;
+ }
+ }
+
+ g_free(data);
+ return -1;
+}
+
+int ssl_write(void *conn, const char *userdata, int len)
+{
+ struct scd *scd = conn;
+ SecBuffer buf[4];
+ SecBufferDesc msg;
+ char *data;
+ int ret;
+
+ msg.ulVersion = SECBUFFER_VERSION;
+ msg.cBuffers = 4;
+ msg.pBuffers = buf;
+
+ data = g_malloc(scd->sizes.cbHeader + scd->sizes.cbMaximumMessage + scd->sizes.cbTrailer);
+ memcpy(data + scd->sizes.cbHeader, userdata, len);
+
+ buf[0].BufferType = SECBUFFER_STREAM_HEADER;
+ buf[0].cbBuffer = scd->sizes.cbHeader;
+ buf[0].pvBuffer = data;
+
+ buf[1].BufferType = SECBUFFER_DATA;
+ buf[1].cbBuffer = len;
+ buf[1].pvBuffer = data + scd->sizes.cbHeader;
+
+ buf[2].BufferType = SECBUFFER_STREAM_TRAILER;
+ buf[2].cbBuffer = scd->sizes.cbTrailer;
+ buf[2].pvBuffer = data + scd->sizes.cbHeader + len;
+ buf[3].BufferType = SECBUFFER_EMPTY;
+
+ SECURITY_STATUS st = EncryptMessage(&scd->context, 0, &msg, 0);
+
+ ret = send(scd->fd, data,
+ buf[0].cbBuffer + buf[1].cbBuffer + buf[2].cbBuffer, 0);
+
+ g_free(data);
+
+ return ret;
+}
+
+void ssl_disconnect(void *conn)
+{
+ struct scd *scd = conn;
+
+ SecBufferDesc msg;
+ SecBuffer buf;
+ DWORD dw;
+
+ dw = SCHANNEL_SHUTDOWN;
+ buf.cbBuffer = sizeof(dw);
+ buf.BufferType = SECBUFFER_TOKEN;
+ buf.pvBuffer = &dw;
+
+ msg.ulVersion = SECBUFFER_VERSION;
+ msg.cBuffers = 1;
+ msg.pBuffers = &buf;
+
+ SECURITY_STATUS st = ApplyControlToken(&scd->context, &msg);
+
+ if (st != SEC_E_OK) {
+ /* FIXME */
+ }
+
+ /* FIXME: call InitializeSecurityContext(Schannel), passing
+ * in empty buffers*/
+
+ DeleteSecurityContext(&scd->context);
+
+ FreeCredentialsHandle(&scd->cred);
+
+ closesocket(scd->fd);
+ g_free(scd->host);
+ g_free(scd);
+}
+
+int ssl_getfd(void *conn)
+{
+ return ((struct scd*)conn)->fd;
+}
+
+GaimInputCondition ssl_getdirection( void *conn )
+{
+ return B_EV_IO_WRITE; /* FIXME: or B_EV_IO_READ */
+}
diff --git a/lib/url.c b/lib/url.c
new file mode 100644
index 00000000..9e330f8c
--- /dev/null
+++ b/lib/url.c
@@ -0,0 +1,109 @@
+ /********************************************************************\
+ * BitlBee -- An IRC to other IM-networks gateway *
+ * *
+ * Copyright 2001-2005 Wilmer van der Gaast and others *
+ \********************************************************************/
+
+/* URL/mirror stuff - Stolen from Axel */
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License with
+ the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+ if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "url.h"
+
+/* Convert an URL to a url_t structure */
+int url_set( url_t *url, const char *set_url )
+{
+ char s[MAX_STRING+1];
+ char *i;
+
+ memset( url, 0, sizeof( url_t ) );
+ memset( s, 0, sizeof( s ) );
+
+ /* protocol:// */
+ if( ( i = strstr( set_url, "://" ) ) == NULL )
+ {
+ url->proto = PROTO_DEFAULT;
+ strncpy( s, set_url, MAX_STRING );
+ }
+ else
+ {
+ if( g_strncasecmp( set_url, "http", i - set_url ) == 0 )
+ url->proto = PROTO_HTTP;
+ else if( g_strncasecmp( set_url, "https", i - set_url ) == 0 )
+ url->proto = PROTO_HTTPS;
+ else if( g_strncasecmp( set_url, "socks4", i - set_url ) == 0 )
+ url->proto = PROTO_SOCKS4;
+ else if( g_strncasecmp( set_url, "socks5", i - set_url ) == 0 )
+ url->proto = PROTO_SOCKS5;
+ else
+ return 0;
+
+ strncpy( s, i + 3, MAX_STRING );
+ }
+
+ /* Split */
+ if( ( i = strchr( s, '/' ) ) == NULL )
+ {
+ strcpy( url->file, "/" );
+ }
+ else
+ {
+ strncpy( url->file, i, MAX_STRING );
+ *i = 0;
+ }
+ strncpy( url->host, s, MAX_STRING );
+
+ /* Check for username in host field */
+ if( strrchr( url->host, '@' ) != NULL )
+ {
+ strncpy( url->user, url->host, MAX_STRING );
+ i = strrchr( url->user, '@' );
+ *i = 0;
+ strcpy( url->host, i + 1 );
+ *url->pass = 0;
+ }
+ /* If not: Fill in defaults */
+ else
+ {
+ *url->user = *url->pass = 0;
+ }
+
+ /* Password? */
+ if( ( i = strchr( url->user, ':' ) ) != NULL )
+ {
+ *i = 0;
+ strcpy( url->pass, i + 1 );
+ }
+ /* Port number? */
+ if( ( i = strchr( url->host, ':' ) ) != NULL )
+ {
+ *i = 0;
+ sscanf( i + 1, "%d", &url->port );
+ }
+ else
+ {
+ if( url->proto == PROTO_HTTP )
+ url->port = 80;
+ else if( url->proto == PROTO_HTTPS )
+ url->port = 443;
+ else if( url->proto == PROTO_SOCKS4 || url->proto == PROTO_SOCKS5 )
+ url->port = 1080;
+ }
+
+ return( url->port > 0 );
+}
diff --git a/lib/url.h b/lib/url.h
new file mode 100644
index 00000000..55107ad2
--- /dev/null
+++ b/lib/url.h
@@ -0,0 +1,44 @@
+ /********************************************************************\
+ * BitlBee -- An IRC to other IM-networks gateway *
+ * *
+ * Copyright 2001-2004 Wilmer van der Gaast and others *
+ \********************************************************************/
+
+/* URL/mirror stuff - Stolen from Axel */
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License with
+ the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+ if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include "bitlbee.h"
+
+#define PROTO_HTTP 2
+#define PROTO_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+1];
+ char file[MAX_STRING+1];
+ char user[MAX_STRING+1];
+ char pass[MAX_STRING+1];
+} url_t;
+
+int url_set( url_t *url, const char *set_url );
diff --git a/lib/xmltree.c b/lib/xmltree.c
new file mode 100644
index 00000000..54a7dd13
--- /dev/null
+++ b/lib/xmltree.c
@@ -0,0 +1,698 @@
+/***************************************************************************\
+* *
+* BitlBee - An IRC to IM gateway *
+* Simple XML (stream) parse tree handling code (Jabber/XMPP, mainly) *
+* *
+* Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net> *
+* *
+* This library is free software; you can redistribute it and/or *
+* modify it under the terms of the GNU Lesser General Public *
+* License as published by the Free Software Foundation, version *
+* 2.1. *
+* *
+* This library is distributed in the hope that it will be useful, *
+* but WITHOUT ANY WARRANTY; without even the implied warranty of *
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
+* Lesser General Public License for more details. *
+* *
+* You should have received a copy of the GNU Lesser General Public License *
+* along with this library; if not, write to the Free Software Foundation, *
+* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *
+* *
+****************************************************************************/
+
+#include <glib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <stdio.h>
+
+#include "xmltree.h"
+
+static void xt_start_element( GMarkupParseContext *ctx, const gchar *element_name, const gchar **attr_names, const gchar **attr_values, gpointer data, GError **error )
+{
+ struct xt_parser *xt = data;
+ struct xt_node *node = g_new0( struct xt_node, 1 ), *nt;
+ int i;
+
+ node->parent = xt->cur;
+ node->name = g_strdup( element_name );
+
+ /* First count the number of attributes */
+ for( i = 0; attr_names[i]; i ++ );
+
+ /* Then allocate a NULL-terminated array. */
+ node->attr = g_new0( struct xt_attr, i + 1 );
+
+ /* And fill it, saving one variable by starting at the end. */
+ for( i --; i >= 0; i -- )
+ {
+ node->attr[i].key = g_strdup( attr_names[i] );
+ node->attr[i].value = g_strdup( attr_values[i] );
+ }
+
+ /* Add it to the linked list of children nodes, if we have a current
+ node yet. */
+ if( xt->cur )
+ {
+ if( xt->cur->children )
+ {
+ for( nt = xt->cur->children; nt->next; nt = nt->next );
+ nt->next = node;
+ }
+ else
+ {
+ xt->cur->children = node;
+ }
+ }
+ else if( xt->root )
+ {
+ /* ERROR situation: A second root-element??? */
+ }
+
+ /* Now this node will be the new current node. */
+ xt->cur = node;
+ /* And maybe this is the root? */
+ if( xt->root == NULL )
+ xt->root = node;
+}
+
+static void xt_text( GMarkupParseContext *ctx, const gchar *text, gsize text_len, gpointer data, GError **error )
+{
+ struct xt_parser *xt = data;
+ struct xt_node *node = xt->cur;
+
+ if( node == NULL )
+ return;
+
+ /* FIXME: Does g_renew also OFFICIALLY accept NULL arguments? */
+ node->text = g_renew( char, node->text, node->text_len + text_len + 1 );
+ memcpy( node->text + node->text_len, text, text_len );
+ node->text_len += text_len;
+ /* Zero termination is always nice to have. */
+ node->text[node->text_len] = 0;
+}
+
+static void xt_end_element( GMarkupParseContext *ctx, const gchar *element_name, gpointer data, GError **error )
+{
+ struct xt_parser *xt = data;
+
+ xt->cur->flags |= XT_COMPLETE;
+ xt->cur = xt->cur->parent;
+}
+
+GMarkupParser xt_parser_funcs =
+{
+ xt_start_element,
+ xt_end_element,
+ xt_text,
+ NULL,
+ NULL
+};
+
+struct xt_parser *xt_new( const struct xt_handler_entry *handlers, gpointer data )
+{
+ struct xt_parser *xt = g_new0( struct xt_parser, 1 );
+
+ xt->data = data;
+ xt->handlers = handlers;
+ xt_reset( xt );
+
+ return xt;
+}
+
+/* Reset the parser, flush everything we have so far. For example, we need
+ this for XMPP when doing TLS/SASL to restart the stream. */
+void xt_reset( struct xt_parser *xt )
+{
+ if( xt->parser )
+ g_markup_parse_context_free( xt->parser );
+
+ xt->parser = g_markup_parse_context_new( &xt_parser_funcs, 0, xt, NULL );
+
+ if( xt->root )
+ {
+ xt_free_node( xt->root );
+ xt->root = NULL;
+ xt->cur = NULL;
+ }
+}
+
+/* Feed the parser, don't execute any handler. Returns -1 on errors, 0 on
+ end-of-stream and 1 otherwise. */
+int xt_feed( struct xt_parser *xt, const char *text, int text_len )
+{
+ if( !g_markup_parse_context_parse( xt->parser, text, text_len, &xt->gerr ) )
+ {
+ return -1;
+ }
+
+ return !( xt->root && xt->root->flags & XT_COMPLETE );
+}
+
+/* Find completed nodes and see if a handler has to be called. Passing
+ a node isn't necessary if you want to start at the root, just pass
+ NULL. This second argument is needed for recursive calls. */
+int xt_handle( struct xt_parser *xt, struct xt_node *node, int depth )
+{
+ struct xt_node *c;
+ xt_status st;
+ int i;
+
+ /* Just in case someone likes infinite loops... */
+ if( xt->root == NULL )
+ return 0;
+
+ if( node == NULL )
+ return xt_handle( xt, xt->root, depth );
+
+ if( depth != 0 )
+ for( c = node->children; c; c = c->next )
+ if( !xt_handle( xt, c, depth > 0 ? depth - 1 : depth ) )
+ return 0;
+
+ if( node->flags & XT_COMPLETE && !( node->flags & XT_SEEN ) )
+ {
+ if( xt->handlers ) for( i = 0; xt->handlers[i].func; i ++ )
+ {
+ /* This one is fun! \o/ */
+
+ /* If handler.name == NULL it means it should always match. */
+ if( ( xt->handlers[i].name == NULL ||
+ /* If it's not, compare. There should always be a name. */
+ g_strcasecmp( xt->handlers[i].name, node->name ) == 0 ) &&
+ /* If handler.parent == NULL, it's a match. */
+ ( xt->handlers[i].parent == NULL ||
+ /* If there's a parent node, see if the name matches. */
+ ( node->parent ? g_strcasecmp( xt->handlers[i].parent, node->parent->name ) == 0 :
+ /* If there's no parent, the handler should mention <root> as a parent. */
+ strcmp( xt->handlers[i].parent, "<root>" ) == 0 ) ) )
+ {
+ st = xt->handlers[i].func( node, xt->data );
+
+ if( st == XT_ABORT )
+ return 0;
+ else if( st != XT_NEXT )
+ break;
+ }
+ }
+
+ node->flags |= XT_SEEN;
+ }
+
+ return 1;
+}
+
+/* Garbage collection: Cleans up all nodes that are handled. Useful for
+ streams because there's no reason to keep a complete packet history
+ in memory. */
+void xt_cleanup( struct xt_parser *xt, struct xt_node *node, int depth )
+{
+ struct xt_node *c, *prev;
+
+ if( !xt || !xt->root )
+ return;
+
+ if( node == NULL )
+ {
+ xt_cleanup( xt, xt->root, depth );
+ return;
+ }
+
+ if( node->flags & XT_SEEN && node == xt->root )
+ {
+ xt_free_node( xt->root );
+ xt->root = xt->cur = NULL;
+ /* xt->cur should be NULL already, BTW... */
+
+ return;
+ }
+
+ /* c contains the current node, prev the previous node (or NULL).
+ I admit, this one's pretty horrible. */
+ for( c = node->children, prev = NULL; c; prev = c, c = c ? c->next : node->children )
+ {
+ if( c->flags & XT_SEEN )
+ {
+ /* Remove the node from the linked list. */
+ if( prev )
+ prev->next = c->next;
+ else
+ node->children = c->next;
+
+ xt_free_node( c );
+
+ /* Since the for loop wants to get c->next, make sure
+ c points at something that exists (and that c->next
+ will actually be the next item we should check). c
+ can be NULL now, if we just removed the first item.
+ That explains the ? thing in for(). */
+ c = prev;
+ }
+ else
+ {
+ /* This node can't be cleaned up yet, but maybe a
+ subnode can. */
+ if( depth != 0 )
+ xt_cleanup( xt, c, depth > 0 ? depth - 1 : depth );
+ }
+ }
+}
+
+struct xt_node *xt_from_string( const char *in )
+{
+ struct xt_parser *parser;
+ struct xt_node *ret;
+
+ parser = xt_new( NULL, NULL );
+ xt_feed( parser, in, strlen( in ) );
+ ret = parser->root;
+ parser->root = NULL;
+ xt_free( parser );
+
+ return ret;
+}
+
+static void xt_to_string_real( struct xt_node *node, GString *str )
+{
+ char *buf;
+ struct xt_node *c;
+ int i;
+
+ g_string_append_printf( str, "<%s", node->name );
+
+ for( i = 0; node->attr[i].key; i ++ )
+ {
+ buf = g_markup_printf_escaped( " %s=\"%s\"", node->attr[i].key, node->attr[i].value );
+ g_string_append( str, buf );
+ g_free( buf );
+ }
+
+ if( node->text == NULL && node->children == NULL )
+ {
+ g_string_append( str, "/>" );
+ return;
+ }
+
+ g_string_append( str, ">" );
+ if( node->text_len > 0 )
+ {
+ buf = g_markup_escape_text( node->text, node->text_len );
+ g_string_append( str, buf );
+ g_free( buf );
+ }
+
+ for( c = node->children; c; c = c->next )
+ xt_to_string_real( c, str );
+
+ g_string_append_printf( str, "</%s>", node->name );
+}
+
+char *xt_to_string( struct xt_node *node )
+{
+ GString *ret;
+ char *real;
+
+ ret = g_string_new( "" );
+ xt_to_string_real( node, ret );
+
+ real = ret->str;
+ g_string_free( ret, FALSE );
+
+ return real;
+}
+
+#ifdef DEBUG
+void xt_print( struct xt_node *node )
+{
+ int i;
+ struct xt_node *c;
+
+ /* Indentation */
+ for( c = node; c->parent; c = c->parent )
+ printf( " " );
+
+ /* Start the tag */
+ printf( "<%s", node->name );
+
+ /* Print the attributes */
+ for( i = 0; node->attr[i].key; i ++ )
+ {
+ char *v = g_markup_escape_text( node->attr[i].value, -1 );
+ printf( " %s=\"%s\"", node->attr[i].key, v );
+ g_free( v );
+ }
+
+ /* /> in case there's really *nothing* inside this tag, otherwise
+ just >. */
+ /* If this tag doesn't have any content at all... */
+ if( node->text == NULL && node->children == NULL )
+ {
+ printf( "/>\n" );
+ return;
+ /* Then we're finished! */
+ }
+
+ /* Otherwise... */
+ printf( ">" );
+
+ /* Only print the text if it contains more than whitespace (TEST). */
+ if( node->text_len > 0 )
+ {
+ for( i = 0; node->text[i] && isspace( node->text[i] ); i ++ );
+ if( node->text[i] )
+ {
+ char *v = g_markup_escape_text( node->text, -1 );
+ printf( "%s", v );
+ g_free( v );
+ }
+ }
+
+ if( node->children )
+ printf( "\n" );
+
+ for( c = node->children; c; c = c->next )
+ xt_print( c );
+
+ if( node->children )
+ for( c = node; c->parent; c = c->parent )
+ printf( " " );
+
+ /* Non-empty tag is now finished. */
+ printf( "</%s>\n", node->name );
+}
+#endif
+
+struct xt_node *xt_dup( struct xt_node *node )
+{
+ struct xt_node *dup = g_new0( struct xt_node, 1 );
+ struct xt_node *c, *dc = NULL;
+ int i;
+
+ /* Let's NOT copy the parent element here BTW! Only do it for children. */
+
+ dup->name = g_strdup( node->name );
+ dup->flags = node->flags;
+ if( node->text )
+ {
+ dup->text = g_memdup( node->text, node->text_len + 1 );
+ dup->text_len = node->text_len;
+ }
+
+ /* Count the number of attributes and allocate the new array. */
+ for( i = 0; node->attr[i].key; i ++ );
+ dup->attr = g_new0( struct xt_attr, i + 1 );
+
+ /* Copy them all! */
+ for( i --; i >= 0; i -- )
+ {
+ dup->attr[i].key = g_strdup( node->attr[i].key );
+ dup->attr[i].value = g_strdup( node->attr[i].value );
+ }
+
+ /* This nice mysterious loop takes care of the children. */
+ for( c = node->children; c; c = c->next )
+ {
+ if( dc == NULL )
+ dc = dup->children = xt_dup( c );
+ else
+ dc = ( dc->next = xt_dup( c ) );
+
+ dc->parent = dup;
+ }
+
+ return dup;
+}
+
+/* Frees a node. This doesn't clean up references to itself from parents! */
+void xt_free_node( struct xt_node *node )
+{
+ int i;
+
+ if( !node )
+ return;
+
+ g_free( node->name );
+ g_free( node->text );
+
+ for( i = 0; node->attr[i].key; i ++ )
+ {
+ g_free( node->attr[i].key );
+ g_free( node->attr[i].value );
+ }
+ g_free( node->attr );
+
+ while( node->children )
+ {
+ struct xt_node *next = node->children->next;
+
+ xt_free_node( node->children );
+ node->children = next;
+ }
+
+ g_free( node );
+}
+
+void xt_free( struct xt_parser *xt )
+{
+ if( !xt )
+ return;
+
+ if( xt->root )
+ xt_free_node( xt->root );
+
+ g_markup_parse_context_free( xt->parser );
+
+ g_free( xt );
+}
+
+/* To find a node's child with a specific name, pass the node's children
+ list, not the node itself! The reason you have to do this by hand: So
+ that you can also use this function as a find-next. */
+struct xt_node *xt_find_node( struct xt_node *node, const char *name )
+{
+ while( node )
+ {
+ char *colon;
+
+ if( g_strcasecmp( node->name, name ) == 0 ||
+ ( ( colon = strchr( node->name, ':' ) ) &&
+ g_strcasecmp( colon + 1, name ) == 0 ) )
+ break;
+
+ node = node->next;
+ }
+
+ return node;
+}
+
+/* More advanced than the one above, understands something like
+ ../foo/bar to find a subnode bar of a node foo which is a child
+ of node's parent. Pass the node directly, not its list of children. */
+struct xt_node *xt_find_path( struct xt_node *node, const char *name )
+{
+ while( name && *name && node )
+ {
+ char *colon, *slash;
+ int n;
+
+ if( ( slash = strchr( name, '/' ) ) )
+ n = slash - name;
+ else
+ n = strlen( name );
+
+ if( strncmp( name, "..", n ) == 0 )
+ {
+ node = node->parent;
+ }
+ else
+ {
+ node = node->children;
+
+ while( node )
+ {
+ if( g_strncasecmp( node->name, name, n ) == 0 ||
+ ( ( colon = strchr( node->name, ':' ) ) &&
+ g_strncasecmp( colon + 1, name, n ) == 0 ) )
+ break;
+
+ node = node->next;
+ }
+ }
+
+ name = slash ? slash + 1 : NULL;
+ }
+
+ return node;
+}
+
+char *xt_find_attr( struct xt_node *node, const char *key )
+{
+ int i;
+ char *colon;
+
+ if( !node )
+ return NULL;
+
+ for( i = 0; node->attr[i].key; i ++ )
+ if( g_strcasecmp( node->attr[i].key, key ) == 0 )
+ break;
+
+ /* This is an awful hack that only takes care of namespace prefixes
+ inside a tag. Since IMHO excessive namespace usage in XMPP is
+ massive overkill anyway (this code exists for almost four years
+ now and never really missed it): Meh. */
+ if( !node->attr[i].key && strcmp( key, "xmlns" ) == 0 &&
+ ( colon = strchr( node->name, ':' ) ) )
+ {
+ *colon = '\0';
+ for( i = 0; node->attr[i].key; i ++ )
+ if( strncmp( node->attr[i].key, "xmlns:", 6 ) == 0 &&
+ strcmp( node->attr[i].key + 6, node->name ) == 0 )
+ break;
+ *colon = ':';
+ }
+
+ return node->attr[i].value;
+}
+
+struct xt_node *xt_new_node( char *name, const char *text, struct xt_node *children )
+{
+ struct xt_node *node, *c;
+
+ node = g_new0( struct xt_node, 1 );
+ node->name = g_strdup( name );
+ node->children = children;
+ node->attr = g_new0( struct xt_attr, 1 );
+
+ if( text )
+ {
+ node->text_len = strlen( text );
+ node->text = g_memdup( text, node->text_len + 1 );
+ }
+
+ for( c = children; c; c = c->next )
+ {
+ if( c->parent != NULL )
+ {
+ /* ERROR CONDITION: They seem to have a parent already??? */
+ }
+
+ c->parent = node;
+ }
+
+ return node;
+}
+
+void xt_add_child( struct xt_node *parent, struct xt_node *child )
+{
+ struct xt_node *node;
+
+ /* This function can actually be used to add more than one child, so
+ do handle this properly. */
+ for( node = child; node; node = node->next )
+ {
+ if( node->parent != NULL )
+ {
+ /* ERROR CONDITION: They seem to have a parent already??? */
+ }
+
+ node->parent = parent;
+ }
+
+ if( parent->children == NULL )
+ {
+ parent->children = child;
+ }
+ else
+ {
+ for( node = parent->children; node->next; node = node->next );
+ node->next = child;
+ }
+}
+
+/* Same, but at the beginning. */
+void xt_insert_child( struct xt_node *parent, struct xt_node *child )
+{
+ struct xt_node *node, *last = NULL;
+
+ if( child == NULL )
+ return; /* BUG */
+
+ for( node = child; node; node = node->next )
+ {
+ if( node->parent != NULL )
+ {
+ /* ERROR CONDITION: They seem to have a parent already??? */
+ }
+
+ node->parent = parent;
+ last = node;
+ }
+
+ last->next = parent->children;
+ parent->children = child;
+}
+
+void xt_add_attr( struct xt_node *node, const char *key, const char *value )
+{
+ int i;
+
+ /* Now actually it'd be nice if we can also change existing attributes
+ (which actually means this function doesn't have the right name).
+ So let's find out if we have this attribute already... */
+ for( i = 0; node->attr[i].key; i ++ )
+ if( strcmp( node->attr[i].key, key ) == 0 )
+ break;
+
+ if( node->attr[i].key == NULL )
+ {
+ /* If not, allocate space for a new attribute. */
+ node->attr = g_renew( struct xt_attr, node->attr, i + 2 );
+ node->attr[i].key = g_strdup( key );
+ node->attr[i+1].key = NULL;
+ }
+ else
+ {
+ /* Otherwise, free the old value before setting the new one. */
+ g_free( node->attr[i].value );
+ }
+
+ node->attr[i].value = g_strdup( value );
+}
+
+int xt_remove_attr( struct xt_node *node, const char *key )
+{
+ int i, last;
+
+ for( i = 0; node->attr[i].key; i ++ )
+ if( strcmp( node->attr[i].key, key ) == 0 )
+ break;
+
+ /* If we didn't find the attribute... */
+ if( node->attr[i].key == NULL )
+ return 0;
+
+ g_free( node->attr[i].key );
+ g_free( node->attr[i].value );
+
+ /* If it's the last, this is easy: */
+ if( node->attr[i+1].key == NULL )
+ {
+ node->attr[i].key = node->attr[i].value = NULL;
+ }
+ else /* It's also pretty easy, actually. */
+ {
+ /* Find the last item. */
+ for( last = i + 1; node->attr[last+1].key; last ++ );
+
+ node->attr[i] = node->attr[last];
+ node->attr[last].key = NULL;
+ node->attr[last].value = NULL;
+ }
+
+ /* Let's not bother with reallocating memory here. It takes time and
+ most packets don't stay in memory for long anyway. */
+
+ return 1;
+}
diff --git a/lib/xmltree.h b/lib/xmltree.h
new file mode 100644
index 00000000..5a0dbc8e
--- /dev/null
+++ b/lib/xmltree.h
@@ -0,0 +1,100 @@
+/***************************************************************************\
+* *
+* BitlBee - An IRC to IM gateway *
+* Simple XML (stream) parse tree handling code (Jabber/XMPP, mainly) *
+* *
+* Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net> *
+* *
+* This library is free software; you can redistribute it and/or *
+* modify it under the terms of the GNU Lesser General Public *
+* License as published by the Free Software Foundation, version *
+* 2.1. *
+* *
+* This library is distributed in the hope that it will be useful, *
+* but WITHOUT ANY WARRANTY; without even the implied warranty of *
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
+* Lesser General Public License for more details. *
+* *
+* You should have received a copy of the GNU Lesser General Public License *
+* along with this library; if not, write to the Free Software Foundation, *
+* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *
+* *
+****************************************************************************/
+
+#ifndef _XMLTREE_H
+#define _XMLTREE_H
+
+typedef enum
+{
+ XT_COMPLETE = 1, /* </tag> reached */
+ XT_SEEN = 2, /* Handler called (or not defined) */
+} xt_flags;
+
+typedef enum
+{
+ XT_ABORT, /* Abort, don't handle the rest anymore */
+ XT_HANDLED, /* Handled this tag properly, go to the next one */
+ XT_NEXT /* Try if there's another matching handler */
+} xt_status;
+
+struct xt_attr
+{
+ char *key, *value;
+};
+
+struct xt_node
+{
+ struct xt_node *parent;
+ struct xt_node *children;
+
+ char *name;
+ struct xt_attr *attr;
+ char *text;
+ int text_len;
+
+ struct xt_node *next;
+ xt_flags flags;
+};
+
+typedef xt_status (*xt_handler_func) ( struct xt_node *node, gpointer data );
+
+struct xt_handler_entry
+{
+ char *name, *parent;
+ xt_handler_func func;
+};
+
+struct xt_parser
+{
+ GMarkupParseContext *parser;
+ struct xt_node *root;
+ struct xt_node *cur;
+
+ const struct xt_handler_entry *handlers;
+ gpointer data;
+
+ GError *gerr;
+};
+
+struct xt_parser *xt_new( const struct xt_handler_entry *handlers, gpointer data );
+void xt_reset( struct xt_parser *xt );
+int xt_feed( struct xt_parser *xt, const char *text, int text_len );
+int xt_handle( struct xt_parser *xt, struct xt_node *node, int depth );
+void xt_cleanup( struct xt_parser *xt, struct xt_node *node, int depth );
+struct xt_node *xt_from_string( const char *in );
+char *xt_to_string( struct xt_node *node );
+void xt_print( struct xt_node *node );
+struct xt_node *xt_dup( struct xt_node *node );
+void xt_free_node( struct xt_node *node );
+void xt_free( struct xt_parser *xt );
+struct xt_node *xt_find_node( struct xt_node *node, const char *name );
+struct xt_node *xt_find_path( struct xt_node *node, const char *name );
+char *xt_find_attr( struct xt_node *node, const char *key );
+
+struct xt_node *xt_new_node( char *name, const char *text, struct xt_node *children );
+void xt_add_child( struct xt_node *parent, struct xt_node *child );
+void xt_insert_child( struct xt_node *parent, struct xt_node *child );
+void xt_add_attr( struct xt_node *node, const char *key, const char *value );
+int xt_remove_attr( struct xt_node *node, const char *key );
+
+#endif