aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile41
-rw-r--r--lib/arc.c223
-rw-r--r--lib/arc.h36
-rw-r--r--lib/base64.c153
-rw-r--r--lib/base64.h33
-rw-r--r--lib/events.h87
-rw-r--r--lib/events_glib.c141
-rw-r--r--lib/events_libevent.c278
-rw-r--r--lib/http_client.c453
-rw-r--r--lib/http_client.h82
-rw-r--r--lib/ini.c90
-rw-r--r--lib/ini.h43
-rw-r--r--lib/md5.c262
-rw-r--r--lib/md5.h42
-rw-r--r--lib/misc.c639
-rw-r--r--lib/misc.h71
-rw-r--r--lib/proxy.c552
-rw-r--r--lib/proxy.h53
-rw-r--r--lib/sha1.c375
-rw-r--r--lib/sha1.h66
-rw-r--r--lib/ssl_bogus.c62
-rw-r--r--lib/ssl_client.h79
-rw-r--r--lib/ssl_gnutls.c247
-rw-r--r--lib/ssl_nss.c196
-rw-r--r--lib/ssl_openssl.c273
-rw-r--r--lib/url.c109
-rw-r--r--lib/url.h44
-rw-r--r--lib/xmltree.c590
-rw-r--r--lib/xmltree.h97
29 files changed, 5417 insertions, 0 deletions
diff --git a/lib/Makefile b/lib/Makefile
new file mode 100644
index 00000000..03fef1ab
--- /dev/null
+++ b/lib/Makefile
@@ -0,0 +1,41 @@
+###########################
+## Makefile for BitlBee ##
+## ##
+## Copyright 2006 Lintux ##
+###########################
+
+### DEFINITIONS
+
+-include ../Makefile.settings
+
+# [SH] Program variables
+objects = arc.o base64.o $(EVENT_HANDLER) http_client.o ini.o md5.o misc.o proxy.o sha1.o $(SSL_CLIENT) url.o xmltree.o
+
+CFLAGS += -Wall
+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)
+
+### MAIN PROGRAM
+
+lib.o: $(objects) $(subdirs)
+ @echo '*' Linking lib.o
+ @$(LD) $(LFLAGS) $(objects) -o lib.o
+
+$(objects): ../Makefile.settings Makefile
+
+$(objects): %.o: %.c
+ @echo '*' Compiling $<
+ @$(CC) -c $(CFLAGS) $< -o $@
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..58f30d3d
--- /dev/null
+++ b/lib/arc.h
@@ -0,0 +1,36 @@
+/***************************************************************************\
+* *
+* 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;
+};
+
+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/events.h b/lib/events.h
new file mode 100644
index 00000000..4baea7b6
--- /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 {
+ GAIM_INPUT_READ = 1 << 1,
+ GAIM_INPUT_WRITE = 1 << 2
+} 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);
+
+/* For now, closesocket() is only a function when using libevent. With GLib
+ it's a preprocessor macro. */
+#ifdef EVENTS_LIBEVENT
+G_MODULE_EXPORT void closesocket(int fd);
+#endif
+
+#endif /* _EVENTS_H_ */
diff --git a/lib/events_glib.c b/lib/events_glib.c
new file mode 100644
index 00000000..3e194e98
--- /dev/null
+++ b/lib/events_glib.c
@@ -0,0 +1,141 @@
+ /********************************************************************\
+ * 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;
+} 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 |= GAIM_INPUT_READ;
+ if (condition & GAIM_WRITE_COND)
+ gaim_cond |= GAIM_INPUT_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" );
+
+ 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;
+
+ if (condition & GAIM_INPUT_READ)
+ cond |= GAIM_READ_COND;
+ if (condition & GAIM_INPUT_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);
+}
diff --git a/lib/events_libevent.c b/lib/events_libevent.c
new file mode 100644
index 00000000..d3403152
--- /dev/null
+++ b/lib/events_libevent.c
@@ -0,0 +1,278 @@
+ /********************************************************************\
+ * 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;
+static GHashTable *id_hash;
+static int quitting = 0;
+
+/* 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;
+};
+
+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;
+ int id;
+
+ if( fd >= 0 )
+ {
+ if( event & EV_READ )
+ cond |= GAIM_INPUT_READ;
+ if( event & EV_WRITE )
+ cond |= GAIM_INPUT_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 = b_ev->id;
+
+ if( quitting )
+ {
+ b_event_remove( id );
+ return;
+ }
+
+ if( !b_ev->function( b_ev->data, fd, cond ) )
+ {
+ event_debug( "Handler returned FALSE: " );
+ b_event_remove( id );
+ }
+ else if( fd == -1 )
+ {
+ 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 & GAIM_INPUT_READ && ( b_ev = g_hash_table_lookup( read_hash, &fd ) ) ) ||
+ ( condition & GAIM_INPUT_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 & GAIM_INPUT_READ )
+ out_cond |= EV_READ;
+ if( condition & GAIM_INPUT_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 );
+ }
+
+ 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 )
+ {
+ 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/http_client.c b/lib/http_client.c
new file mode 100644
index 00000000..b00fcf98
--- /dev/null
+++ b/lib/http_client.c
@@ -0,0 +1,453 @@
+ /********************************************************************\
+ * 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 );
+
+
+void *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 )
+ {
+ g_free( req );
+ return( NULL );
+ }
+
+ req->func = func;
+ req->data = data;
+ req->request = g_strdup( request );
+ req->request_length = strlen( request );
+
+ return( req );
+}
+
+void *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 ) : GAIM_INPUT_WRITE,
+ http_connected, req );
+ else
+ req->inpa = b_input_add( source, GAIM_INPUT_READ, http_incoming_data, req );
+
+ return FALSE;
+
+error:
+ req->status_string = g_strdup( "Error while writing HTTP request" );
+
+ req->func( req );
+
+ g_free( req->request );
+ g_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 ) : GAIM_INPUT_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 )
+ {
+ 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 );
+
+ g_free( req->request );
+ g_free( req->reply_headers );
+ g_free( req->status_string );
+ g_free( req );
+
+ return FALSE;
+}
diff --git a/lib/http_client.h b/lib/http_client.h
new file mode 100644
index 00000000..78d6dbd1
--- /dev/null
+++ b/lib/http_client.h
@@ -0,0 +1,82 @@
+ /********************************************************************\
+ * 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. */
+
+ 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). */
+void *http_dorequest( char *host, int port, int ssl, char *request, http_input_function func, gpointer data );
+void *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..c63a132e
--- /dev/null
+++ b/lib/ini.c
@@ -0,0 +1,90 @@
+ /********************************************************************\
+ * BitlBee -- An IRC to other IM-networks gateway *
+ * *
+ * Copyright 2002-2005 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 )
+{
+ ini_t *ini = g_new0( ini_t, 1 );
+
+ if( ( ini->fp = fopen( file, "r" ) ) == NULL )
+ {
+ g_free( ini );
+ return( NULL );
+ }
+
+ return( ini );
+}
+
+int ini_read( ini_t *file )
+{
+ char key[MAX_STRING], s[MAX_STRING], *t;
+ int i;
+
+ while( !feof( file->fp ) )
+ {
+ *s = 0;
+ fscanf( file->fp, "%127[^\n#]s", s );
+ fscanf( file->fp, "%*[^\n]s" );
+ fgetc( file->fp ); /* Skip newline */
+ file->line ++;
+ if( strchr( s, '=' ) )
+ {
+ sscanf( s, "%[^ =]s", key );
+ if( ( t = strchr( key, '.' ) ) )
+ {
+ *t = 0;
+ strcpy( file->section, key );
+ t ++;
+ }
+ else
+ {
+ strcpy( file->section, file->c_section );
+ t = key;
+ }
+ sscanf( t, "%s", file->key );
+ t = strchr( s, '=' ) + 1;
+ for( i = 0; t[i] == ' '; i ++ );
+ strcpy( file->value, &t[i] );
+ for( i = strlen( file->value ) - 1; file->value[i] == 32; i -- )
+ file->value[i] = 0;
+
+ return( 1 );
+ }
+ else if( ( t = strchr( s, '[' ) ) )
+ {
+ strcpy( file->c_section, t + 1 );
+ t = strchr( file->c_section, ']' );
+ *t = 0;
+ }
+ }
+ return( 0 );
+}
+
+void ini_close( ini_t *file )
+{
+ fclose( file->fp );
+ g_free( file );
+}
diff --git a/lib/ini.h b/lib/ini.h
new file mode 100644
index 00000000..5eab472b
--- /dev/null
+++ b/lib/ini.h
@@ -0,0 +1,43 @@
+ /********************************************************************\
+ * 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
+{
+ FILE *fp;
+ int line;
+ char c_section[MAX_STRING];
+ char section[MAX_STRING];
+ char key[MAX_STRING];
+ char value[MAX_STRING];
+} 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..094507b2
--- /dev/null
+++ b/lib/md5.h
@@ -0,0 +1,42 @@
+/*
+ * 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>
+#include <stdint.h>
+
+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..ccf208b5
--- /dev/null
+++ b/lib/misc.c
@@ -0,0 +1,639 @@
+ /********************************************************************\
+ * 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 <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 "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);
+}
+
+char *normalize(const char *s)
+{
+ static char buf[BUF_LEN];
+ char *t, *u;
+ int x = 0;
+
+ g_return_val_if_fail((s != NULL), NULL);
+
+ u = t = g_strdup(s);
+
+ strcpy(t, s);
+ g_strdown(t);
+
+ while (*t && (x < BUF_LEN - 1)) {
+ if (*t != ' ') {
+ buf[x] = *t;
+ x++;
+ }
+ t++;
+ }
+ buf[x] = '\0';
+ g_free(u);
+ return buf;
+}
+
+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);
+}
+
+typedef struct htmlentity
+{
+ char code[7];
+ char is[3];
+} htmlentity_t;
+
+static const htmlentity_t ent[] =
+{
+ { "lt", "<" },
+ { "gt", ">" },
+ { "amp", "&" },
+ { "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 = g_malloc( strlen( in ) + 1 );
+ char *s = out, *cs;
+ int i, matched;
+
+ memset( out, 0, strlen( in ) + 1 );
+
+ 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 ++;
+
+ if( *in )
+ {
+ if( g_strncasecmp( cs+1, "br", 2) == 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 );
+ g_free( 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;
+ int i, j;
+
+ t = g_strdup( s );
+
+ for( i = j = 0; t[i]; i ++, j ++ )
+ {
+ /* if( t[i] <= ' ' || ((unsigned char *)t)[i] >= 128 || t[i] == '%' ) */
+ if( !isalnum( t[i] ) )
+ {
+ sprintf( s + j, "%%%02X", ((unsigned char*)t)[i] );
+ j += 2;
+ }
+ else
+ {
+ s[j] = t[i];
+ }
+ }
+ s[j] = 0;
+
+ g_free( t );
+}
+
+/* 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 )
+{
+ 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 )
+ {
+ 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 *reply = NULL;
+#ifdef HAVE_RESOLV_A
+ char name[1024];
+ unsigned char querybuf[1024];
+ const unsigned char *buf;
+ ns_msg nsh;
+ ns_rr rr;
+ int i, 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;
+
+ if( ns_parserr( &nsh, ns_s_an, 0, &rr ) != 0 )
+ return NULL;
+
+ 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 )
+ return NULL;
+
+ 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 );
+ return NULL;
+ }
+
+ reply->prio = ( buf[0] << 8 ) | buf[1];
+ reply->weight = ( buf[2] << 8 ) | buf[3];
+ reply->port = ( buf[4] << 8 ) | buf[5];
+#endif
+
+ return reply;
+}
+
+/* Word wrapping. Yes, I know this isn't UTF-8 clean. I'm willing to take the risk. */
+char *word_wrap( 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, i;
+
+ if( base64_decode( hash, &pass_dec ) != 21 )
+ {
+ ret = -1;
+ }
+ else
+ {
+ md5_init( &md5_state );
+ md5_append( &md5_state, (md5_byte_t*) password, strlen( password ) );
+ md5_append( &md5_state, (md5_byte_t*) pass_dec + 16, 5 ); /* Hmmm, salt! */
+ md5_finish( &md5_state, pass_md5 );
+
+ for( i = 0; i < 16; i ++ )
+ {
+ if( pass_dec[i] != pass_md5[i] )
+ {
+ ret = 1;
+ break;
+ }
+ }
+
+ /* If we reached the end of the loop, it was a match! */
+ if( i == 16 )
+ ret = 0;
+ }
+
+ g_free( pass_dec );
+
+ return ret;
+}
diff --git a/lib/misc.h b/lib/misc.h
new file mode 100644
index 00000000..a2acada6
--- /dev/null
+++ b/lib/misc.h
@@ -0,0 +1,71 @@
+ /********************************************************************\
+ * 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 char *normalize( const char *s );
+
+G_MODULE_EXPORT time_t get_time( int year, int month, int day, int hour, int min, int sec );
+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 char *word_wrap( char *msg, int line_len );
+
+G_MODULE_EXPORT gboolean ssl_sockerr_again( void *ssl );
+
+G_MODULE_EXPORT int md5_verify_password( char *password, char *hash );
+
+#endif
diff --git a/lib/proxy.c b/lib/proxy.c
new file mode 100644
index 00000000..53b89d64
--- /dev/null
+++ b/lib/proxy.c
@@ -0,0 +1,552 @@
+/*
+ * 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] = "";
+
+struct PHB {
+ b_event_handler func, proxy_func;
+ gpointer data, proxy_data;
+ char *host;
+ int port;
+ int fd;
+ gint inpa;
+};
+
+
+
+static struct sockaddr_in *gaim_gethostbyname(const char *host, int port)
+{
+ static struct sockaddr_in sin;
+
+ if (!inet_aton(host, &sin.sin_addr)) {
+ struct hostent *hp;
+ if (!(hp = gethostbyname(host))) {
+ return NULL;
+ }
+ memset(&sin, 0, sizeof(struct sockaddr_in));
+ memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length);
+ sin.sin_family = hp->h_addrtype;
+ } else
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(port);
+
+ return &sin;
+}
+
+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) {
+ closesocket(source);
+ b_event_remove(phb->inpa);
+ if( phb->proxy_func )
+ phb->proxy_func(phb->proxy_data, -1, GAIM_INPUT_READ);
+ else {
+ phb->func(phb->data, -1, GAIM_INPUT_READ);
+ g_free(phb);
+ }
+ return FALSE;
+ }
+#endif
+ sock_make_blocking(source);
+ b_event_remove(phb->inpa);
+ if( phb->proxy_func )
+ phb->proxy_func(phb->proxy_data, source, GAIM_INPUT_READ);
+ else {
+ phb->func(phb->data, source, GAIM_INPUT_READ);
+ g_free(phb);
+ }
+
+ return FALSE;
+}
+
+static int proxy_connect_none(const char *host, unsigned short port, struct PHB *phb)
+{
+ struct sockaddr_in *sin;
+ int fd = -1;
+
+ if (!(sin = gaim_gethostbyname(host, port))) {
+ g_free(phb);
+ return -1;
+ }
+
+ if ((fd = socket(sin->sin_family, SOCK_STREAM, 0)) < 0) {
+ g_free(phb);
+ return -1;
+ }
+
+ sock_make_nonblocking(fd);
+
+ event_debug("proxy_connect_none( \"%s\", %d ) = %d\n", host, port, fd);
+
+ if (connect(fd, (struct sockaddr *)sin, sizeof(*sin)) < 0 && !sockerr_again()) {
+ closesocket(fd);
+ g_free(phb);
+
+ return -1;
+ } else {
+ phb->inpa = b_input_add(fd, GAIM_INPUT_WRITE, gaim_io_connected, phb);
+ phb->fd = fd;
+
+ 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, GAIM_INPUT_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return FALSE;
+ }
+
+ close(source);
+ phb->func(phb->data, -1, GAIM_INPUT_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, GAIM_INPUT_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, GAIM_INPUT_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, GAIM_INPUT_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, GAIM_INPUT_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return FALSE;
+ }
+
+ phb->inpa = b_input_add(source, GAIM_INPUT_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, GAIM_INPUT_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return FALSE;
+ }
+
+ close(source);
+ phb->func(phb->data, -1, GAIM_INPUT_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, GAIM_INPUT_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, GAIM_INPUT_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, GAIM_INPUT_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return FALSE;
+ }
+
+ phb->inpa = b_input_add(source, GAIM_INPUT_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, GAIM_INPUT_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return FALSE;
+ }
+ if ((buf[0] != 0x05) || (buf[1] != 0x00)) {
+ close(source);
+ phb->func(phb->data, -1, GAIM_INPUT_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return FALSE;
+ }
+
+ phb->func(phb->data, source, GAIM_INPUT_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, GAIM_INPUT_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return;
+ }
+
+ phb->inpa = b_input_add(source, GAIM_INPUT_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, GAIM_INPUT_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return FALSE;
+ }
+
+ if ((buf[0] != 0x01) || (buf[1] != 0x00)) {
+ close(source);
+ phb->func(phb->data, -1, GAIM_INPUT_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, GAIM_INPUT_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return FALSE;
+ }
+
+ if ((buf[0] != 0x05) || (buf[1] == 0xff)) {
+ close(source);
+ phb->func(phb->data, -1, GAIM_INPUT_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, GAIM_INPUT_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return FALSE;
+ }
+
+ phb->inpa = b_input_add(source, GAIM_INPUT_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, GAIM_INPUT_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, GAIM_INPUT_READ);
+ g_free(phb->host);
+ g_free(phb);
+ return FALSE;
+ }
+
+ phb->inpa = b_input_add(source, GAIM_INPUT_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);
+
+ if (phb->host) g_free(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..ee4fcc19
--- /dev/null
+++ b/lib/sha1.c
@@ -0,0 +1,375 @@
+/*
+ * 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 "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);
+}
diff --git a/lib/sha1.h b/lib/sha1.h
new file mode 100644
index 00000000..368c0669
--- /dev/null
+++ b/lib/sha1.h
@@ -0,0 +1,66 @@
+/*
+ * 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_
+
+#include <stdint.h>
+#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]);
+
+#endif
diff --git a/lib/ssl_bogus.c b/lib/ssl_bogus.c
new file mode 100644
index 00000000..5bae3496
--- /dev/null
+++ b/lib/ssl_bogus.c
@@ -0,0 +1,62 @@
+ /********************************************************************\
+ * 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_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 GAIM_INPUT_READ;
+}
diff --git a/lib/ssl_client.h b/lib/ssl_client.h
new file mode 100644
index 00000000..f91d0d70
--- /dev/null
+++ b/lib/ssl_client.h
@@ -0,0 +1,79 @@
+ /********************************************************************\
+ * 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);
+
+
+/* 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 GAIM_INPUT_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 );
diff --git a/lib/ssl_gnutls.c b/lib/ssl_gnutls.c
new file mode 100644
index 00000000..f5945442
--- /dev/null
+++ b/lib/ssl_gnutls.c
@@ -0,0 +1,247 @@
+ /********************************************************************\
+ * 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 <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_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, GAIM_INPUT_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;
+ }
+
+ if( !initialized )
+ {
+ gnutls_global_init();
+ initialized = TRUE;
+ atexit( gnutls_global_deinit );
+ }
+
+ 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;
+
+ 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;
+
+ 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 ) ?
+ GAIM_INPUT_WRITE : GAIM_INPUT_READ );
+}
diff --git a/lib/ssl_nss.c b/lib/ssl_nss.c
new file mode 100644
index 00000000..eba3c441
--- /dev/null
+++ b/lib/ssl_nss.c
@@ -0,0 +1,196 @@
+ /********************************************************************\
+ * 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 <private/pprio.h>
+#include <ssl.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 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_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 )
+ {
+ PR_Init( PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
+ NSS_NoDB_Init(NULL);
+ NSS_SetDomesticPolicy();
+ }
+
+
+ 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 )
+{
+ return 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 GAIM_INPUT_READ;
+}
diff --git a/lib/ssl_openssl.c b/lib/ssl_openssl.c
new file mode 100644
index 00000000..fc6d433e
--- /dev/null
+++ b/lib/ssl_openssl.c
@@ -0,0 +1,273 @@
+ /********************************************************************\
+ * 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_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, GAIM_INPUT_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 )
+ {
+ initialized = TRUE;
+ SSLeay_add_ssl_algorithms();
+ }
+
+ 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;
+ }
+
+ 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 );
+
+ 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 ? GAIM_INPUT_WRITE : GAIM_INPUT_READ );
+}
diff --git a/lib/url.c b/lib/url.c
new file mode 100644
index 00000000..de9966b4
--- /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, 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..8c038c91
--- /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, char *set_url );
diff --git a/lib/xmltree.c b/lib/xmltree.c
new file mode 100644
index 00000000..e65b4f41
--- /dev/null
+++ b/lib/xmltree.c
@@ -0,0 +1,590 @@
+/***************************************************************************\
+* *
+* 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, 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 ) )
+ {
+ 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. */
+ g_strcasecmp( 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 )
+ return xt_cleanup( xt, xt->root, depth );
+
+ 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 );
+ }
+ }
+}
+
+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( "\t" );
+
+ /* Start the tag */
+ printf( "<%s", node->name );
+
+ /* Print the attributes */
+ for( i = 0; node->attr[i].key; i ++ )
+ printf( " %s=\"%s\"", node->attr[i].key, g_markup_escape_text( node->attr[i].value, -1 ) );
+
+ /* /> 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] )
+ printf( "%s", g_markup_escape_text( node->text, -1 ) );
+ }
+
+ 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( "\t" );
+
+ /* 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 )
+ {
+ if( g_strcasecmp( node->name, name ) == 0 )
+ break;
+
+ node = node->next;
+ }
+
+ return node;
+}
+
+char *xt_find_attr( struct xt_node *node, const char *key )
+{
+ int i;
+
+ if( !node )
+ return NULL;
+
+ for( i = 0; node->attr[i].key; i ++ )
+ if( g_strcasecmp( node->attr[i].key, key ) == 0 )
+ break;
+
+ return node->attr[i].value;
+}
+
+struct xt_node *xt_new_node( char *name, 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;
+ }
+}
+
+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..10677412
--- /dev/null
+++ b/lib/xmltree.h
@@ -0,0 +1,97 @@
+/***************************************************************************\
+* *
+* 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, 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 );
+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 );
+char *xt_find_attr( struct xt_node *node, const char *key );
+
+struct xt_node *xt_new_node( char *name, char *text, struct xt_node *children );
+void xt_add_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