aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorWilmer van der Gaast <wilmer@gaast.net>2006-06-25 14:15:42 +0200
committerWilmer van der Gaast <wilmer@gaast.net>2006-06-25 14:15:42 +0200
commitdf1694b9559d4abec748b0506b5f44e684d022a8 (patch)
tree4a86737f6edc6a72c0cf8ddf6c946c64c36572b4 /lib
parent812a41362a9316da1734fdaa8b1aad36bde9cb5c (diff)
Moving all generic files to lib/ instead of having some in / and some in
protocols/, and adding RC4 code.
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile37
-rw-r--r--lib/events.h66
-rw-r--r--lib/events_glib.c137
-rw-r--r--lib/events_libevent.c247
-rw-r--r--lib/http_client.c453
-rw-r--r--lib/http_client.h57
-rw-r--r--lib/ini.c90
-rw-r--r--lib/ini.h43
-rw-r--r--lib/md5.c392
-rw-r--r--lib/md5.h85
-rw-r--r--lib/misc.c481
-rw-r--r--lib/misc.h51
-rw-r--r--lib/proxy.c556
-rw-r--r--lib/proxy.h53
-rw-r--r--lib/rc4.c179
-rw-r--r--lib/rc4.h35
-rw-r--r--lib/sha.c173
-rw-r--r--lib/sha.h21
-rw-r--r--lib/ssl_bogus.c57
-rw-r--r--lib/ssl_client.h42
-rw-r--r--lib/ssl_gnutls.c206
-rw-r--r--lib/ssl_nss.c190
-rw-r--r--lib/ssl_openssl.c221
-rw-r--r--lib/url.c107
-rw-r--r--lib/url.h44
25 files changed, 4023 insertions, 0 deletions
diff --git a/lib/Makefile b/lib/Makefile
new file mode 100644
index 00000000..80cdd9a5
--- /dev/null
+++ b/lib/Makefile
@@ -0,0 +1,37 @@
+###########################
+## Makefile for BitlBee ##
+## ##
+## Copyright 2006 Lintux ##
+###########################
+
+### DEFINITIONS
+
+-include ../Makefile.settings
+
+# [SH] Program variables
+objects = $(EVENT_HANDLER) http_client.o ini.o md5.o misc.o proxy.o rc4.o sha.o $(SSL_CLIENT) url.o
+
+CFLAGS += -Wall
+LFLAGS += -r
+
+# [SH] Phony targets
+all: lib.o
+
+.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/events.h b/lib/events.h
new file mode 100644
index 00000000..781fca6a
--- /dev/null
+++ b/lib/events.h
@@ -0,0 +1,66 @@
+/*
+ * 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
+ *
+ */
+
+/*
+ * Split off the event handling things from proxy.[ch] (and adding timer
+ * stuff. This to allow BitlBee to use other libs than GLib for event
+ * handling.
+ */
+
+
+#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>
+
+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);
+
+#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... )
+
+G_MODULE_EXPORT void b_main_init();
+G_MODULE_EXPORT void b_main_run();
+G_MODULE_EXPORT void b_main_quit();
+
+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);
+
+#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..620720cd
--- /dev/null
+++ b/lib/events_glib.c
@@ -0,0 +1,137 @@
+ /********************************************************************\
+ * 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;
+ guint result;
+ gpointer data;
+} GaimIOClosure;
+
+static GMainLoop *loop;
+
+void b_main_init()
+{
+ 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;
+
+ 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);
+ closure->result = 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 (0x%x)\n", source, condition, function, data, closure->result, closure );
+
+ g_io_channel_unref(channel);
+ return closure->result;
+}
+
+gint b_timeout_add(gint timeout, b_event_handler func, gpointer data)
+{
+ gint st = g_timeout_add(timeout, 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..1119c2ab
--- /dev/null
+++ b/lib/events_libevent.c
@@ -0,0 +1,247 @@
+ /********************************************************************\
+ * 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 "proxy.h"
+
+#include <sys/time.h>
+#include <event.h>
+
+static guint id_next;
+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 b_event_data
+{
+ guint id;
+ struct event evinfo;
+ gint timeout;
+ b_event_handler function;
+ void *data;
+};
+
+void b_main_init()
+{
+ event_init();
+
+ id_next = 1;
+ 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()
+{
+ event_dispatch();
+}
+
+void b_main_quit()
+{
+ struct timeval tv;
+
+ /* libevent sometimes generates events before really quitting,
+ we want to stop them. */
+ quitting = 1;
+
+ memset( &tv, 0, sizeof( struct timeval ) );
+ event_loopexit( &tv );
+}
+
+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..50ee80cf
--- /dev/null
+++ b/lib/http_client.h
@@ -0,0 +1,57 @@
+ /********************************************************************\
+ * 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 <glib.h>
+
+#include "ssl_client.h"
+
+struct http_request;
+
+typedef void (*http_input_function)( struct http_request * );
+
+struct http_request
+{
+ char *request;
+ int request_length;
+ int status_code;
+ char *status_string;
+ char *reply_headers;
+ char *reply_body;
+ int body_size;
+ int finished;
+
+ void *ssl;
+ int fd;
+
+ int inpa;
+ int bytes_written;
+ int bytes_read;
+
+ http_input_function func;
+ gpointer data;
+};
+
+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..e6273585
--- /dev/null
+++ b/lib/md5.c
@@ -0,0 +1,392 @@
+/*
+ Copyright (C) 1999 Aladdin Enterprises. All rights reserved.
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ L. Peter Deutsch
+ ghost@aladdin.com
+
+ */
+/*
+ Independent implementation of MD5 (RFC 1321).
+
+ This code implements the MD5 Algorithm defined in RFC 1321.
+ It is derived directly from the text of the RFC and not from the
+ reference implementation.
+
+ The original and principal author of md5.c is L. Peter Deutsch
+ <ghost@aladdin.com>. Other authors are noted in the change history
+ that follows (in reverse chronological order):
+
+ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
+ 1999-05-03 lpd Original version.
+ */
+
+#include "md5.h"
+#include <string.h>
+
+#ifdef TEST
+/*
+ * Compile with -DTEST to create a self-contained executable test program.
+ * The test program should print out the same values as given in section
+ * A.5 of RFC 1321, reproduced below.
+ */
+#include <string.h>
+main()
+{
+ static const char *const test[7] = {
+ "", /*d41d8cd98f00b204e9800998ecf8427e*/
+ "945399884.61923487334tuvga", /*0cc175b9c0f1b6a831c399e269772661*/
+ "abc", /*900150983cd24fb0d6963f7d28e17f72*/
+ "message digest", /*f96b697d7cb7938d525a2f31aaf161d0*/
+ "abcdefghijklmnopqrstuvwxyz", /*c3fcd3d76192e4007dfb496cca67e13b*/
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+ /*d174ab98d277d9f5a5611c2c9f419d9f*/
+ "12345678901234567890123456789012345678901234567890123456789012345678901234567890" /*57edf4a22be3c955ac49da2e2107b67a*/
+ };
+ int i;
+
+ for (i = 0; i < 7; ++i) {
+ md5_state_t state;
+ md5_byte_t digest[16];
+ int di;
+
+ md5_init(&state);
+ md5_append(&state, (const md5_byte_t *)test[i], strlen(test[i]));
+ md5_finish(&state, digest);
+ printf("MD5 (\"%s\") = ", test[i]);
+ for (di = 0; di < 16; ++di)
+ printf("%02x", digest[di]);
+ printf("\n");
+ }
+ return 0;
+}
+#endif /* TEST */
+
+
+/*
+ * For reference, here is the program that computed the T values.
+ */
+#if 0
+#include <math.h>
+main()
+{
+ int i;
+ for (i = 1; i <= 64; ++i) {
+ unsigned long v = (unsigned long)(4294967296.0 * fabs(sin((double)i)));
+ printf("#define T%d 0x%08lx\n", i, v);
+ }
+ return 0;
+}
+#endif
+/*
+ * End of T computation program.
+ */
+#define T1 0xd76aa478
+#define T2 0xe8c7b756
+#define T3 0x242070db
+#define T4 0xc1bdceee
+#define T5 0xf57c0faf
+#define T6 0x4787c62a
+#define T7 0xa8304613
+#define T8 0xfd469501
+#define T9 0x698098d8
+#define T10 0x8b44f7af
+#define T11 0xffff5bb1
+#define T12 0x895cd7be
+#define T13 0x6b901122
+#define T14 0xfd987193
+#define T15 0xa679438e
+#define T16 0x49b40821
+#define T17 0xf61e2562
+#define T18 0xc040b340
+#define T19 0x265e5a51
+#define T20 0xe9b6c7aa
+#define T21 0xd62f105d
+#define T22 0x02441453
+#define T23 0xd8a1e681
+#define T24 0xe7d3fbc8
+#define T25 0x21e1cde6
+#define T26 0xc33707d6
+#define T27 0xf4d50d87
+#define T28 0x455a14ed
+#define T29 0xa9e3e905
+#define T30 0xfcefa3f8
+#define T31 0x676f02d9
+#define T32 0x8d2a4c8a
+#define T33 0xfffa3942
+#define T34 0x8771f681
+#define T35 0x6d9d6122
+#define T36 0xfde5380c
+#define T37 0xa4beea44
+#define T38 0x4bdecfa9
+#define T39 0xf6bb4b60
+#define T40 0xbebfbc70
+#define T41 0x289b7ec6
+#define T42 0xeaa127fa
+#define T43 0xd4ef3085
+#define T44 0x04881d05
+#define T45 0xd9d4d039
+#define T46 0xe6db99e5
+#define T47 0x1fa27cf8
+#define T48 0xc4ac5665
+#define T49 0xf4292244
+#define T50 0x432aff97
+#define T51 0xab9423a7
+#define T52 0xfc93a039
+#define T53 0x655b59c3
+#define T54 0x8f0ccc92
+#define T55 0xffeff47d
+#define T56 0x85845dd1
+#define T57 0x6fa87e4f
+#define T58 0xfe2ce6e0
+#define T59 0xa3014314
+#define T60 0x4e0811a1
+#define T61 0xf7537e82
+#define T62 0xbd3af235
+#define T63 0x2ad7d2bb
+#define T64 0xeb86d391
+
+static void
+md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
+{
+ md5_word_t
+ a = pms->abcd[0], b = pms->abcd[1],
+ c = pms->abcd[2], d = pms->abcd[3];
+ md5_word_t t;
+
+#ifndef ARCH_IS_BIG_ENDIAN
+# define ARCH_IS_BIG_ENDIAN 1 /* slower, default implementation */
+#endif
+#if ARCH_IS_BIG_ENDIAN
+
+ /*
+ * On big-endian machines, we must arrange the bytes in the right
+ * order. (This also works on machines of unknown byte order.)
+ */
+ md5_word_t X[16];
+ const md5_byte_t *xp = data;
+ int i;
+
+ for (i = 0; i < 16; ++i, xp += 4)
+ X[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
+
+#else /* !ARCH_IS_BIG_ENDIAN */
+
+ /*
+ * On little-endian machines, we can process properly aligned data
+ * without copying it.
+ */
+ md5_word_t xbuf[16];
+ const md5_word_t *X;
+
+ if (!((data - (const md5_byte_t *)0) & 3)) {
+ /* data are properly aligned */
+ X = (const md5_word_t *)data;
+ } else {
+ /* not aligned */
+ memcpy(xbuf, data, 64);
+ X = xbuf;
+ }
+#endif
+
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+ /* Round 1. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + F(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 7, T1);
+ SET(d, a, b, c, 1, 12, T2);
+ SET(c, d, a, b, 2, 17, T3);
+ SET(b, c, d, a, 3, 22, T4);
+ SET(a, b, c, d, 4, 7, T5);
+ SET(d, a, b, c, 5, 12, T6);
+ SET(c, d, a, b, 6, 17, T7);
+ SET(b, c, d, a, 7, 22, T8);
+ SET(a, b, c, d, 8, 7, T9);
+ SET(d, a, b, c, 9, 12, T10);
+ SET(c, d, a, b, 10, 17, T11);
+ SET(b, c, d, a, 11, 22, T12);
+ SET(a, b, c, d, 12, 7, T13);
+ SET(d, a, b, c, 13, 12, T14);
+ SET(c, d, a, b, 14, 17, T15);
+ SET(b, c, d, a, 15, 22, T16);
+#undef SET
+
+ /* Round 2. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
+#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + G(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 1, 5, T17);
+ SET(d, a, b, c, 6, 9, T18);
+ SET(c, d, a, b, 11, 14, T19);
+ SET(b, c, d, a, 0, 20, T20);
+ SET(a, b, c, d, 5, 5, T21);
+ SET(d, a, b, c, 10, 9, T22);
+ SET(c, d, a, b, 15, 14, T23);
+ SET(b, c, d, a, 4, 20, T24);
+ SET(a, b, c, d, 9, 5, T25);
+ SET(d, a, b, c, 14, 9, T26);
+ SET(c, d, a, b, 3, 14, T27);
+ SET(b, c, d, a, 8, 20, T28);
+ SET(a, b, c, d, 13, 5, T29);
+ SET(d, a, b, c, 2, 9, T30);
+ SET(c, d, a, b, 7, 14, T31);
+ SET(b, c, d, a, 12, 20, T32);
+#undef SET
+
+ /* Round 3. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + H(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 5, 4, T33);
+ SET(d, a, b, c, 8, 11, T34);
+ SET(c, d, a, b, 11, 16, T35);
+ SET(b, c, d, a, 14, 23, T36);
+ SET(a, b, c, d, 1, 4, T37);
+ SET(d, a, b, c, 4, 11, T38);
+ SET(c, d, a, b, 7, 16, T39);
+ SET(b, c, d, a, 10, 23, T40);
+ SET(a, b, c, d, 13, 4, T41);
+ SET(d, a, b, c, 0, 11, T42);
+ SET(c, d, a, b, 3, 16, T43);
+ SET(b, c, d, a, 6, 23, T44);
+ SET(a, b, c, d, 9, 4, T45);
+ SET(d, a, b, c, 12, 11, T46);
+ SET(c, d, a, b, 15, 16, T47);
+ SET(b, c, d, a, 2, 23, T48);
+#undef SET
+
+ /* Round 4. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + I(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 6, T49);
+ SET(d, a, b, c, 7, 10, T50);
+ SET(c, d, a, b, 14, 15, T51);
+ SET(b, c, d, a, 5, 21, T52);
+ SET(a, b, c, d, 12, 6, T53);
+ SET(d, a, b, c, 3, 10, T54);
+ SET(c, d, a, b, 10, 15, T55);
+ SET(b, c, d, a, 1, 21, T56);
+ SET(a, b, c, d, 8, 6, T57);
+ SET(d, a, b, c, 15, 10, T58);
+ SET(c, d, a, b, 6, 15, T59);
+ SET(b, c, d, a, 13, 21, T60);
+ SET(a, b, c, d, 4, 6, T61);
+ SET(d, a, b, c, 11, 10, T62);
+ SET(c, d, a, b, 2, 15, T63);
+ SET(b, c, d, a, 9, 21, T64);
+#undef SET
+
+ /* Then perform the following additions. (That is increment each
+ of the four registers by the value it had before this block
+ was started.) */
+ pms->abcd[0] += a;
+ pms->abcd[1] += b;
+ pms->abcd[2] += c;
+ pms->abcd[3] += d;
+}
+
+void
+md5_init(md5_state_t *pms)
+{
+ pms->count[0] = pms->count[1] = 0;
+ pms->abcd[0] = 0x67452301;
+ pms->abcd[1] = 0xefcdab89;
+ pms->abcd[2] = 0x98badcfe;
+ pms->abcd[3] = 0x10325476;
+}
+
+void
+md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
+{
+ const md5_byte_t *p = data;
+ int left = nbytes;
+ int offset = (pms->count[0] >> 3) & 63;
+ md5_word_t nbits = (md5_word_t)(nbytes << 3);
+
+ if (nbytes <= 0)
+ return;
+
+ /* Update the message length. */
+ pms->count[1] += nbytes >> 29;
+ pms->count[0] += nbits;
+ if (pms->count[0] < nbits)
+ pms->count[1]++;
+
+ /* Process an initial partial block. */
+ if (offset) {
+ int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
+
+ memcpy(pms->buf + offset, p, copy);
+ if (offset + copy < 64)
+ return;
+ p += copy;
+ left -= copy;
+ md5_process(pms, pms->buf);
+ }
+
+ /* Process full blocks. */
+ for (; left >= 64; p += 64, left -= 64)
+ md5_process(pms, p);
+
+ /* Process a final partial block. */
+ if (left)
+ memcpy(pms->buf, p, left);
+}
+
+void
+md5_finish(md5_state_t *pms, md5_byte_t digest[16])
+{
+ static const md5_byte_t pad[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ md5_byte_t data[8];
+ int i;
+
+ /* Save the length before padding. */
+ for (i = 0; i < 8; ++i)
+ data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
+ /* Pad to 56 bytes mod 64. */
+ md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
+ /* Append the length. */
+ md5_append(pms, data, 8);
+ for (i = 0; i < 16; ++i)
+ digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
+}
diff --git a/lib/md5.h b/lib/md5.h
new file mode 100644
index 00000000..f24f2ff1
--- /dev/null
+++ b/lib/md5.h
@@ -0,0 +1,85 @@
+/*
+ Copyright (C) 1999 Aladdin Enterprises. All rights reserved.
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ L. Peter Deutsch
+ ghost@aladdin.com
+
+ */
+/*
+ Independent implementation of MD5 (RFC 1321).
+
+ This code implements the MD5 Algorithm defined in RFC 1321.
+ It is derived directly from the text of the RFC and not from the
+ reference implementation.
+
+ The original and principal author of md5.h is L. Peter Deutsch
+ <ghost@aladdin.com>. Other authors are noted in the change history
+ that follows (in reverse chronological order):
+
+ 2004-03-09 Jelmer Vernooij add G_MODULE_EXPORT for Bitlbee
+ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
+ added conditionalization for C++ compilation from Martin
+ Purschke <purschke@bnl.gov>.
+ 1999-05-03 lpd Original version.
+ */
+
+#ifndef md5_INCLUDED
+# define md5_INCLUDED
+
+#include <glib.h>
+#include <gmodule.h>
+
+/*
+ * This code has some adaptations for the Ghostscript environment, but it
+ * will compile and run correctly in any environment with 8-bit chars and
+ * 32-bit ints. Specifically, it assumes that if the following are
+ * defined, they have the same meaning as in Ghostscript: P1, P2, P3,
+ * ARCH_IS_BIG_ENDIAN.
+ */
+
+typedef unsigned char md5_byte_t; /* 8-bit byte */
+typedef unsigned int md5_word_t; /* 32-bit word */
+
+/* Define the state of the MD5 Algorithm. */
+typedef struct md5_state_s {
+ md5_word_t count[2]; /* message length in bits, lsw first */
+ md5_word_t abcd[4]; /* digest buffer */
+ md5_byte_t buf[64]; /* accumulate block */
+} md5_state_t;
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/* Initialize the algorithm. */
+G_MODULE_EXPORT void md5_init(md5_state_t *pms);
+
+/* Append a string to the message. */
+G_MODULE_EXPORT void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
+
+/* Finish the message and return the digest. */
+G_MODULE_EXPORT void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
+
+#ifdef __cplusplus
+} /* end extern "C" */
+#endif
+
+#endif /* md5_INCLUDED */
diff --git a/lib/misc.c b/lib/misc.c
new file mode 100644
index 00000000..d8d6a4c7
--- /dev/null
+++ b/lib/misc.c
@@ -0,0 +1,481 @@
+ /********************************************************************\
+ * BitlBee -- An IRC to other IM-networks gateway *
+ * *
+ * Copyright 2002-2004 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-2005 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <glib.h>
+#include <time.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 *add_cr(char *text)
+{
+ char *ret = NULL;
+ int count = 0, j;
+ unsigned int i;
+
+ if (text[0] == '\n')
+ count++;
+ for (i = 1; i < strlen(text); i++)
+ if (text[i] == '\n' && text[i - 1] != '\r')
+ count++;
+
+ if (count == 0)
+ return g_strdup(text);
+
+ ret = g_malloc0(strlen(text) + count + 1);
+
+ i = 0; j = 0;
+ if (text[i] == '\n')
+ ret[j++] = '\r';
+ ret[j++] = text[i++];
+ for (; i < strlen(text); i++) {
+ if (text[i] == '\n' && text[i - 1] != '\r')
+ ret[j++] = '\r';
+ ret[j++] = text[i];
+ }
+
+ return ret;
+}
+
+char *tobase64(const char *text)
+{
+ char *out;
+ int len;
+
+ len = strlen(text);
+ 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(text, len, out, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=");
+
+ return out;
+}
+
+void base64_encode_real(const unsigned char *in, int inlen, unsigned char *out, char *b64digits)
+{
+ for (; inlen >= 3; inlen -= 3)
+ {
+ *out++ = b64digits[in[0] >> 2];
+ *out++ = b64digits[((in[0]<<4) & 0x30) | (in[1]>>4)];
+ *out++ = b64digits[((in[1]<<2) & 0x3c) | (in[2]>>6)];
+ *out++ = b64digits[in[2] & 0x3f];
+ in += 3;
+ }
+ if (inlen > 0)
+ {
+ *out++ = b64digits[in[0] >> 2];
+ if (inlen > 1)
+ {
+ *out++ = b64digits[((in[0]<<4) & 0x30) | (in[1]>>4)];
+ *out++ = b64digits[((in[1]<<2) & 0x3c)];
+ }
+ else
+ {
+ *out++ = b64digits[((in[0]<<4) & 0x30) | (in[1]>>4)];
+ *out++ = b64digits[64];
+ }
+ *out++ = b64digits[64];
+ }
+ *out = '\0';
+}
+
+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;
+
+ 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[8];
+ char is[4];
+} htmlentity_t;
+
+/* FIXME: This is ISO8859-1(5) centric, so might cause problems with other charsets. */
+
+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 );
+}
+
+void info_string_append(GString *str, char *newline, char *name, char *value)
+{
+ if( value && value[0] )
+ g_string_sprintfa( str, "%s%s: %s", newline, name, value );
+}
+
+/* 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;
+}
+
+#ifdef IPV6
+/* 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 );
+}
+#endif
+
+/* 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 );
+}
+
+char *set_eval_charset( irc_t *irc, set_t *set, char *value )
+{
+ GIConv cd;
+
+ if ( g_strncasecmp( value, "none", 4 ) == 0 )
+ return( value );
+
+ cd = g_iconv_open( "UTF-8", value );
+ if( cd == (GIConv) -1 )
+ return( NULL );
+
+ g_iconv_close( cd );
+ return( value );
+}
diff --git a/lib/misc.h b/lib/misc.h
new file mode 100644
index 00000000..c7eec19b
--- /dev/null
+++ b/lib/misc.h
@@ -0,0 +1,51 @@
+ /********************************************************************\
+ * 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 _UTIL_H
+#define _UTIL_H
+
+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 *tobase64( const char *text );
+G_MODULE_EXPORT void base64_encode_real( const unsigned char *in, int inlen, unsigned char *out, char *b64digits );
+G_MODULE_EXPORT char *normalize( const char *s );
+G_MODULE_EXPORT void info_string_append( GString *str, char *newline, char *name, char *value );
+
+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 );
+char *set_eval_charset( irc_t *irc, set_t *set, char *value );
+
+#endif
diff --git a/lib/proxy.c b/lib/proxy.c
new file mode 100644
index 00000000..b8aa304d
--- /dev/null
+++ b/lib/proxy.c
@@ -0,0 +1,556 @@
+/*
+ * 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"
+
+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) {
+ if (sockerr_again()) {
+ phb->inpa = b_input_add(fd, GAIM_INPUT_WRITE, gaim_io_connected, phb);
+ phb->fd = fd;
+ } else {
+ closesocket(fd);
+ g_free(phb);
+ return -1;
+ }
+ }
+
+ 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 (proxyuser && *proxyuser) {
+ 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 || (port == -1) || !func || strlen(host) > 128) {
+ return -1;
+ }
+
+ phb = g_new0(struct PHB, 1);
+ phb->func = func;
+ phb->data = data;
+
+#ifndef _WIN32
+ sethostent(1);
+#endif
+
+ if ((proxytype == PROXY_NONE) || !proxyhost || !proxyhost[0] || !proxyport || (proxyport == -1))
+ 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/rc4.c b/lib/rc4.c
new file mode 100644
index 00000000..3559f71e
--- /dev/null
+++ b/lib/rc4.c
@@ -0,0 +1,179 @@
+/***************************************************************************\
+* *
+* BitlBee - An IRC to IM gateway *
+* Simple (but secure) RC4 implementation for safer password storage. *
+* *
+* 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. *
+* *
+\***************************************************************************/
+
+/*
+ This file implements RC4-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 chose for RC4 is because it's pretty simple but effective,
+ so it will work without adding several KBs or an extra library dependency.
+*/
+
+
+#include <glib.h>
+#include <stdlib.h>
+#include <string.h>
+#include "rc4.h"
+
+/* Add some seed to the password, to make sure we *never* use the same key.
+ This defines how many byes we use as a seed. */
+#define RC4_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 RC4_CYCLES 1024
+
+struct rc4_state *rc4_keymaker( unsigned char *key, int kl, int cycles )
+{
+ struct rc4_state *st;
+ int i, j, tmp;
+
+ st = g_malloc( sizeof( struct rc4_state ) );
+ st->i = st->j = 0;
+ for( i = 0; i < 256; i ++ )
+ st->S[i] = i;
+
+ if( kl <= 0 )
+ kl = strlen( (char*) key );
+
+ for( i = j = 0; i < 256; i ++ )
+ {
+ j = ( j + st->S[i] + key[i%kl] ) & 0xff;
+ tmp = st->S[i];
+ st->S[i] = st->S[j];
+ st->S[j] = tmp;
+ }
+
+ for( i = 0; i < cycles; i ++ )
+ rc4_getbyte( st );
+
+ return st;
+}
+
+/*
+ For those who don't know, RC4 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 RC4 byte generator, the next function
+ can be used to get bytes from the generator (and shuffle things a bit).
+*/
+
+unsigned char rc4_getbyte( struct rc4_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;
+
+ return st->S[(st->S[st->i] + st->S[st->j]) & 0xff];
+}
+
+/*
+ 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 RC4
+ structures. These 6 bytes are also saved in the results, because of
+ course we'll need them in rc4_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.
+*/
+
+int rc4_encode( unsigned char *clear, int clear_len, unsigned char **crypt, char *password )
+{
+ struct rc4_state *st;
+ unsigned char *key;
+ int key_len, i;
+
+ key_len = strlen( password ) + RC4_IV_LEN;
+ if( clear_len <= 0 )
+ clear_len = strlen( (char*) clear );
+
+ /* Prepare buffers and the key + IV */
+ *crypt = g_malloc( clear_len + RC4_IV_LEN );
+ key = g_malloc( key_len );
+ strcpy( (char*) key, password );
+ for( i = 0; i < RC4_IV_LEN; i ++ )
+ key[key_len-RC4_IV_LEN+i] = crypt[0][i] = rand() & 0xff;
+
+ /* Generate the initial S[] from the IVed key. */
+ st = rc4_keymaker( key, key_len, RC4_CYCLES );
+ g_free( key );
+
+ for( i = 0; i < clear_len; i ++ )
+ crypt[0][i+RC4_IV_LEN] = clear[i] ^ rc4_getbyte( st );
+
+ g_free( st );
+
+ return clear_len + RC4_IV_LEN;
+}
+
+int rc4_decode( unsigned char *crypt, int crypt_len, unsigned char **clear, char *password )
+{
+ struct rc4_state *st;
+ unsigned char *key;
+ int key_len, clear_len, i;
+
+ key_len = strlen( password ) + RC4_IV_LEN;
+ clear_len = crypt_len - RC4_IV_LEN;
+
+ /* 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 < RC4_IV_LEN; i ++ )
+ key[key_len-RC4_IV_LEN+i] = crypt[i];
+
+ /* Generate the initial S[] from the IVed key. */
+ st = rc4_keymaker( key, key_len, RC4_CYCLES );
+ g_free( key );
+
+ for( i = 0; i < clear_len; i ++ )
+ clear[0][i] = crypt[i+RC4_IV_LEN] ^ rc4_getbyte( st );
+ clear[0][i] = 0; /* Nice to have for plaintexts. */
+
+ g_free( st );
+
+ return clear_len;
+}
diff --git a/lib/rc4.h b/lib/rc4.h
new file mode 100644
index 00000000..8d50b508
--- /dev/null
+++ b/lib/rc4.h
@@ -0,0 +1,35 @@
+/***************************************************************************\
+* *
+* BitlBee - An IRC to IM gateway *
+* Simple (but secure) RC4 implementation for safer password storage. *
+* *
+* 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. *
+* *
+\***************************************************************************/
+
+
+struct rc4_state
+{
+ unsigned char S[256];
+ unsigned char i, j;
+};
+
+struct rc4_state *rc4_keymaker( unsigned char *key, int kl, int cycles );
+unsigned char rc4_getbyte( struct rc4_state *st );
+int rc4_encode( unsigned char *clear, int clear_len, unsigned char **crypt, char *password );
+int rc4_decode( unsigned char *crypt, int crypt_len, unsigned char **clear, char *password );
diff --git a/lib/sha.c b/lib/sha.c
new file mode 100644
index 00000000..895505a1
--- /dev/null
+++ b/lib/sha.c
@@ -0,0 +1,173 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is SHA 180-1 Reference Implementation (Compact version)
+ *
+ * The Initial Developer of the Original Code is Paul Kocher of
+ * Cryptography Research. Portions created by Paul Kocher are
+ * Copyright (C) 1995-9 by Cryptography Research, Inc. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ */
+
+#define BITLBEE_CORE
+#include "nogaim.h"
+
+static void shaHashBlock(SHA_CTX *ctx);
+
+void shaInit(SHA_CTX *ctx) {
+ int i;
+
+ ctx->lenW = 0;
+ ctx->sizeHi = ctx->sizeLo = 0;
+
+ /* Initialize H with the magic constants (see FIPS180 for constants)
+ */
+ ctx->H[0] = 0x67452301L;
+ ctx->H[1] = 0xefcdab89L;
+ ctx->H[2] = 0x98badcfeL;
+ ctx->H[3] = 0x10325476L;
+ ctx->H[4] = 0xc3d2e1f0L;
+
+ for (i = 0; i < 80; i++)
+ ctx->W[i] = 0;
+}
+
+
+void shaUpdate(SHA_CTX *ctx, unsigned char *dataIn, int len) {
+ int i;
+
+ /* Read the data into W and process blocks as they get full
+ */
+ for (i = 0; i < len; i++) {
+ ctx->W[ctx->lenW / 4] <<= 8;
+ ctx->W[ctx->lenW / 4] |= (guint32)dataIn[i];
+ if ((++ctx->lenW) % 64 == 0) {
+ shaHashBlock(ctx);
+ ctx->lenW = 0;
+ }
+ ctx->sizeLo += 8;
+ ctx->sizeHi += (ctx->sizeLo < 8);
+ }
+}
+
+
+void shaFinal(SHA_CTX *ctx, unsigned char hashout[20]) {
+ unsigned char pad0x80 = 0x80;
+ unsigned char pad0x00 = 0x00;
+ unsigned char padlen[8];
+ int i;
+
+ /* Pad with a binary 1 (e.g. 0x80), then zeroes, then length
+ */
+ padlen[0] = (unsigned char)((ctx->sizeHi >> 24) & 255);
+ padlen[1] = (unsigned char)((ctx->sizeHi >> 16) & 255);
+ padlen[2] = (unsigned char)((ctx->sizeHi >> 8) & 255);
+ padlen[3] = (unsigned char)((ctx->sizeHi >> 0) & 255);
+ padlen[4] = (unsigned char)((ctx->sizeLo >> 24) & 255);
+ padlen[5] = (unsigned char)((ctx->sizeLo >> 16) & 255);
+ padlen[6] = (unsigned char)((ctx->sizeLo >> 8) & 255);
+ padlen[7] = (unsigned char)((ctx->sizeLo >> 0) & 255);
+ shaUpdate(ctx, &pad0x80, 1);
+ while (ctx->lenW != 56)
+ shaUpdate(ctx, &pad0x00, 1);
+ shaUpdate(ctx, padlen, 8);
+
+ /* Output hash
+ */
+ for (i = 0; i < 20; i++) {
+ hashout[i] = (unsigned char)(ctx->H[i / 4] >> 24);
+ ctx->H[i / 4] <<= 8;
+ }
+
+ /*
+ * Re-initialize the context (also zeroizes contents)
+ */
+ shaInit(ctx);
+}
+
+
+void shaBlock(unsigned char *dataIn, int len, unsigned char hashout[20]) {
+ SHA_CTX ctx;
+
+ shaInit(&ctx);
+ shaUpdate(&ctx, dataIn, len);
+ shaFinal(&ctx, hashout);
+}
+
+
+#define SHA_ROTL(X,n) ((((X) << (n)) | ((X) >> (32-(n)))) & 0xffffffffL)
+
+static void shaHashBlock(SHA_CTX *ctx) {
+ int t;
+ guint32 A,B,C,D,E,TEMP;
+
+ for (t = 16; t <= 79; t++)
+ ctx->W[t] =
+ SHA_ROTL(ctx->W[t-3] ^ ctx->W[t-8] ^ ctx->W[t-14] ^ ctx->W[t-16], 1);
+
+ A = ctx->H[0];
+ B = ctx->H[1];
+ C = ctx->H[2];
+ D = ctx->H[3];
+ E = ctx->H[4];
+
+ for (t = 0; t <= 19; t++) {
+ TEMP = (SHA_ROTL(A,5) + (((C^D)&B)^D) + E + ctx->W[t] + 0x5a827999L) & 0xffffffffL;
+ E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP;
+ }
+ for (t = 20; t <= 39; t++) {
+ TEMP = (SHA_ROTL(A,5) + (B^C^D) + E + ctx->W[t] + 0x6ed9eba1L) & 0xffffffffL;
+ E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP;
+ }
+ for (t = 40; t <= 59; t++) {
+ TEMP = (SHA_ROTL(A,5) + ((B&C)|(D&(B|C))) + E + ctx->W[t] + 0x8f1bbcdcL) & 0xffffffffL;
+ E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP;
+ }
+ for (t = 60; t <= 79; t++) {
+ TEMP = (SHA_ROTL(A,5) + (B^C^D) + E + ctx->W[t] + 0xca62c1d6L) & 0xffffffffL;
+ E = D; D = C; C = SHA_ROTL(B, 30); B = A; A = TEMP;
+ }
+
+ ctx->H[0] += A;
+ ctx->H[1] += B;
+ ctx->H[2] += C;
+ ctx->H[3] += D;
+ ctx->H[4] += E;
+}
+
+/*----------------------------------------------------------------------------
+ *
+ * This code added by Thomas "temas" Muldowney for Jabber compatability
+ *
+ *---------------------------------------------------------------------------*/
+char *shahash(char *str)
+{
+ static char final[41];
+ char *pos;
+ unsigned char hashval[20];
+ int x;
+
+ if(!str || strlen(str) == 0)
+ return NULL;
+
+ shaBlock((unsigned char *)str, strlen(str), hashval);
+
+ pos = final;
+ for(x=0;x<20;x++)
+ {
+ g_snprintf(pos, 3, "%02x", hashval[x]);
+ pos += 2;
+ }
+ return (char *)final;
+}
diff --git a/lib/sha.h b/lib/sha.h
new file mode 100644
index 00000000..e8152b1b
--- /dev/null
+++ b/lib/sha.h
@@ -0,0 +1,21 @@
+#ifndef __SHA_H__
+#define __SHA_H__
+
+#include <gmodule.h>
+
+G_MODULE_EXPORT int strprintsha(char *dest, int *hashval);
+
+typedef struct {
+ guint32 H[5];
+ guint32 W[80];
+ int lenW;
+ guint32 sizeHi,sizeLo;
+} SHA_CTX;
+
+G_MODULE_EXPORT void shaInit(SHA_CTX *ctx);
+G_MODULE_EXPORT void shaUpdate(SHA_CTX *ctx, unsigned char *dataIn, int len);
+G_MODULE_EXPORT void shaFinal(SHA_CTX *ctx, unsigned char hashout[20]);
+G_MODULE_EXPORT void shaBlock(unsigned char *dataIn, int len, unsigned char hashout[20]);
+G_MODULE_EXPORT char *shahash(char *str);
+
+#endif
diff --git a/lib/ssl_bogus.c b/lib/ssl_bogus.c
new file mode 100644
index 00000000..00aaa7c4
--- /dev/null
+++ b/lib/ssl_bogus.c
@@ -0,0 +1,57 @@
+ /********************************************************************\
+ * 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 );
+}
+
+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..1a9c79e9
--- /dev/null
+++ b/lib/ssl_client.h
@@ -0,0 +1,42 @@
+ /********************************************************************\
+ * 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
+*/
+
+#include <glib.h>
+#include "proxy.h"
+
+#define SSL_OK 0
+#define SSL_NOHANDSHAKE 1
+#define SSL_AGAIN 2
+
+extern int ssl_errno;
+
+typedef gboolean (*ssl_input_function)(gpointer, void*, b_input_condition);
+
+G_MODULE_EXPORT void *ssl_connect( char *host, int port, ssl_input_function func, gpointer 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 );
+G_MODULE_EXPORT void ssl_disconnect( void *conn_ );
+G_MODULE_EXPORT int ssl_getfd( void *conn );
+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..3ebe1756
--- /dev/null
+++ b/lib/ssl_gnutls.c
@@ -0,0 +1,206 @@
+ /********************************************************************\
+ * 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;
+
+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 );
+
+
+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 );
+ }
+
+ 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 );
+
+ return( conn );
+}
+
+static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond );
+
+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 );
+
+ gnutls_deinit( conn->session );
+ gnutls_certificate_free_credentials( conn->xcred );
+
+ g_free( conn );
+
+ return FALSE;
+ }
+
+ sock_make_nonblocking( conn->fd );
+ gnutls_transport_set_ptr( conn->session, (gnutls_transport_ptr) 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;
+}
+
+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 );
+
+ gnutls_deinit( conn->session );
+ 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..218b3a80
--- /dev/null
+++ b/lib/ssl_nss.c
@@ -0,0 +1,190 @@
+ /********************************************************************\
+ * 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 ) );
+}
+
+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..b6f6c520
--- /dev/null
+++ b/lib/ssl_openssl.c
@@ -0,0 +1,221 @@
+ /********************************************************************\
+ * 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 );
+
+
+void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data )
+{
+ struct scd *conn = g_new0( struct scd, 1 );
+ SSL_METHOD *meth;
+
+ 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 )
+ {
+ initialized = TRUE;
+ SSLeay_add_ssl_algorithms();
+ }
+
+ meth = TLSv1_client_method();
+ conn->ssl_ctx = SSL_CTX_new( meth );
+ if( conn->ssl_ctx == NULL )
+ {
+ conn->fd = -1;
+ return( NULL );
+ }
+
+ conn->ssl = SSL_new( conn->ssl_ctx );
+ if( conn->ssl == NULL )
+ {
+ conn->fd = -1;
+ return( NULL );
+ }
+
+ return( conn );
+}
+
+static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond );
+
+static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond )
+{
+ struct scd *conn = data;
+
+ if( source == -1 )
+ return ssl_handshake( data, -1, cond );
+
+ /* 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 );
+}
+
+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 )
+ goto ssl_connected_failure;
+
+ 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;
+
+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;
+}
+
+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;
+}
+
+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..e4deac78
--- /dev/null
+++ b/lib/url.c
@@ -0,0 +1,107 @@
+ /********************************************************************\
+ * 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];
+ char *i;
+
+ /* 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_SOCKS4 )
+ url->port = 1080;
+ }
+
+ return( url->port > 0 );
+}
diff --git a/lib/url.h b/lib/url.h
new file mode 100644
index 00000000..e9e1ecfe
--- /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];
+ char file[MAX_STRING];
+ char user[MAX_STRING];
+ char pass[MAX_STRING];
+} url_t;
+
+int url_set( url_t *url, char *set_url );