diff options
| author | Jelmer Vernooij <jelmer@samba.org> | 2008-04-02 16:22:57 +0200 | 
|---|---|---|
| committer | Jelmer Vernooij <jelmer@samba.org> | 2008-04-02 16:22:57 +0200 | 
| commit | 85d7b857fb8ca8e3c03d4abb3368a0966760630c (patch) | |
| tree | a16163e557bcae3af41bde7d2d771d64ca248a97 /lib | |
| parent | 875ad4201402b1a8f80ba22a6cdcdb152c6e5510 (diff) | |
| parent | dd345753c1742905c9f81aa71d8b09109fbc5456 (diff) | |
Merge trunk.
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/Makefile | 41 | ||||
| -rw-r--r-- | lib/arc.c | 223 | ||||
| -rw-r--r-- | lib/arc.h | 36 | ||||
| -rw-r--r-- | lib/base64.c | 153 | ||||
| -rw-r--r-- | lib/base64.h | 33 | ||||
| -rw-r--r-- | lib/events.h | 87 | ||||
| -rw-r--r-- | lib/events_glib.c | 141 | ||||
| -rw-r--r-- | lib/events_libevent.c | 278 | ||||
| -rw-r--r-- | lib/http_client.c | 453 | ||||
| -rw-r--r-- | lib/http_client.h | 82 | ||||
| -rw-r--r-- | lib/ini.c | 90 | ||||
| -rw-r--r-- | lib/ini.h | 43 | ||||
| -rw-r--r-- | lib/md5.c | 262 | ||||
| -rw-r--r-- | lib/md5.h | 42 | ||||
| -rw-r--r-- | lib/misc.c | 639 | ||||
| -rw-r--r-- | lib/misc.h | 71 | ||||
| -rw-r--r-- | lib/proxy.c | 552 | ||||
| -rw-r--r-- | lib/proxy.h | 53 | ||||
| -rw-r--r-- | lib/sha1.c | 375 | ||||
| -rw-r--r-- | lib/sha1.h | 66 | ||||
| -rw-r--r-- | lib/ssl_bogus.c | 62 | ||||
| -rw-r--r-- | lib/ssl_client.h | 79 | ||||
| -rw-r--r-- | lib/ssl_gnutls.c | 247 | ||||
| -rw-r--r-- | lib/ssl_nss.c | 196 | ||||
| -rw-r--r-- | lib/ssl_openssl.c | 273 | ||||
| -rw-r--r-- | lib/url.c | 109 | ||||
| -rw-r--r-- | lib/url.h | 44 | ||||
| -rw-r--r-- | lib/xmltree.c | 590 | ||||
| -rw-r--r-- | lib/xmltree.h | 97 | 
29 files changed, 5417 insertions, 0 deletions
| diff --git a/lib/Makefile b/lib/Makefile new file mode 100644 index 00000000..03fef1ab --- /dev/null +++ b/lib/Makefile @@ -0,0 +1,41 @@ +########################### +## Makefile for BitlBee  ## +##                       ## +## Copyright 2006 Lintux ## +########################### + +### DEFINITIONS + +-include ../Makefile.settings + +# [SH] Program variables +objects = arc.o base64.o $(EVENT_HANDLER) http_client.o ini.o md5.o misc.o proxy.o sha1.o $(SSL_CLIENT) url.o xmltree.o + +CFLAGS += -Wall +LFLAGS += -r + +# [SH] Phony targets +all: lib.o +check: all +lcov: check +gcov: +	gcov *.c + +.PHONY: all clean distclean + +clean: $(subdirs) +	rm -f *.o $(OUTFILE) core + +distclean: clean $(subdirs) + +### MAIN PROGRAM + +lib.o: $(objects) $(subdirs) +	@echo '*' Linking lib.o +	@$(LD) $(LFLAGS) $(objects) -o lib.o + +$(objects): ../Makefile.settings Makefile + +$(objects): %.o: %.c +	@echo '*' Compiling $< +	@$(CC) -c $(CFLAGS) $< -o $@ diff --git a/lib/arc.c b/lib/arc.c new file mode 100644 index 00000000..fd498454 --- /dev/null +++ b/lib/arc.c @@ -0,0 +1,223 @@ +/***************************************************************************\ +*                                                                           * +*  BitlBee - An IRC to IM gateway                                           * +*  Simple (but secure) ArcFour implementation for safer password storage.   * +*                                                                           * +*  Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net>                   * +*                                                                           * +*  This library is free software; you can redistribute it and/or            * +*  modify it under the terms of the GNU Lesser General Public               * +*  License as published by the Free Software Foundation, version            * +*  2.1.                                                                     * +*                                                                           * +*  This library is distributed in the hope that it will be useful,          * +*  but WITHOUT ANY WARRANTY; without even the implied warranty of           * +*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU        * +*  Lesser General Public License for more details.                          * +*                                                                           * +*  You should have received a copy of the GNU Lesser General Public License * +*  along with this library; if not, write to the Free Software Foundation,  * +*  Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           * +*                                                                           * +\***************************************************************************/ + +/*  +   This file implements ArcFour-encryption, which will mainly be used to +   save IM passwords safely in the new XML-format. Possibly other uses will +   come up later. It's supposed to be quite reliable (thanks to the use of a +   6-byte IV/seed), certainly compared to the old format. The only realistic +   way to crack BitlBee passwords now is to use a sniffer to get your hands +   on the user's password. +    +   If you see that something's wrong in this implementation (I asked a +   couple of people to look at it already, but who knows), please tell me. +    +   The reason I picked ArcFour is because it's pretty simple but effective, +   so it will work without adding several KBs or an extra library dependency. +    +   (ArcFour is an RC4-compatible cipher. See for details: +   http://www.mozilla.org/projects/security/pki/nss/draft-kaukonen-cipher-arcfour-03.txt) +*/ + + +#include <glib.h> +#include <gmodule.h> +#include <stdlib.h> +#include <string.h> +#include "misc.h" +#include "arc.h" + +/* Add some seed to the password, to make sure we *never* use the same key. +   This defines how many bytes we use as a seed. */ +#define ARC_IV_LEN 6 + +/* To defend against a "Fluhrer, Mantin and Shamir attack", it is recommended +   to shuffle S[] just a bit more before you start to use it. This defines how +   many bytes we'll request before we'll really use them for encryption. */ +#define ARC_CYCLES 1024 + +struct arc_state *arc_keymaker( unsigned char *key, int kl, int cycles ) +{ +	struct arc_state *st; +	int i, j, tmp; +	unsigned char S2[256]; +	 +	st = g_malloc( sizeof( struct arc_state ) ); +	st->i = st->j = 0; +	if( kl <= 0 ) +		kl = strlen( (char*) key ); +	 +	for( i = 0; i < 256; i ++ ) +	{ +		st->S[i] = i; +		S2[i] = key[i%kl]; +	} +	 +	for( i = j = 0; i < 256; i ++ ) +	{ +		j = ( j + st->S[i] + S2[i] ) & 0xff; +		tmp = st->S[i]; +		st->S[i] = st->S[j]; +		st->S[j] = tmp; +	} +	 +	memset( S2, 0, 256 ); +	i = j = 0; +	 +	for( i = 0; i < cycles; i ++ ) +		arc_getbyte( st ); +	 +	return st; +} + +/* +   For those who don't know, ArcFour is basically an algorithm that generates +   a stream of bytes after you give it a key. Just get a byte from it and +   xor it with your cleartext. To decrypt, just give it the same key again +   and start xorring. +    +   The function above initializes the byte generator, the next function can +   be used to get bytes from the generator (and shuffle things a bit). +*/ + +unsigned char arc_getbyte( struct arc_state *st ) +{ +	unsigned char tmp; +	 +	/* Unfortunately the st-> stuff doesn't really improve readability here... */ +	st->i ++; +	st->j += st->S[st->i]; +	tmp = st->S[st->i]; +	st->S[st->i] = st->S[st->j]; +	st->S[st->j] = tmp; +	tmp = (st->S[st->i] + st->S[st->j]) & 0xff; +	 +	return st->S[tmp]; +} + +/* +   The following two functions can be used for reliable encryption and +   decryption. Known plaintext attacks are prevented by adding some (6, +   by default) random bytes to the password before setting up the state +   structures. These 6 bytes are also saved in the results, because of +   course we'll need them in arc_decode(). +    +   Because the length of the resulting string is unknown to the caller, +   it should pass a char**. Since the encode/decode functions allocate +   memory for the string, make sure the char** points at a NULL-pointer +   (or at least to something you already free()d), or you'll leak +   memory. And of course, don't forget to free() the result when you +   don't need it anymore. +    +   Both functions return the number of bytes in the result string. +    +   Note that if you use the pad_to argument, you will need zero-termi- +   nation to find back the original string length after decryption. So +   it shouldn't be used if your string contains \0s by itself! +*/ + +int arc_encode( char *clear, int clear_len, unsigned char **crypt, char *password, int pad_to ) +{ +	struct arc_state *st; +	unsigned char *key; +	char *padded = NULL; +	int key_len, i, padded_len; +	 +	key_len = strlen( password ) + ARC_IV_LEN; +	if( clear_len <= 0 ) +		clear_len = strlen( clear ); +	 +	/* Pad the string to the closest multiple of pad_to. This makes it +	   impossible to see the exact length of the password. */ +	if( pad_to > 0 && ( clear_len % pad_to ) > 0 ) +	{ +		padded_len = clear_len + pad_to - ( clear_len % pad_to ); +		padded = g_malloc( padded_len ); +		memcpy( padded, clear, clear_len ); +		 +		/* First a \0 and then random data, so we don't have to do +		   anything special when decrypting. */ +		padded[clear_len] = 0; +		random_bytes( (unsigned char*) padded + clear_len + 1, padded_len - clear_len - 1 ); +		 +		clear = padded; +		clear_len = padded_len; +	} +	 +	/* Prepare buffers and the key + IV */ +	*crypt = g_malloc( clear_len + ARC_IV_LEN ); +	key = g_malloc( key_len ); +	strcpy( (char*) key, password ); +	 +	/* Add the salt. Save it for later (when decrypting) and, of course, +	   add it to the encryption key. */ +	random_bytes( crypt[0], ARC_IV_LEN ); +	memcpy( key + key_len - ARC_IV_LEN, crypt[0], ARC_IV_LEN ); +	 +	/* Generate the initial S[] from the IVed key. */ +	st = arc_keymaker( key, key_len, ARC_CYCLES ); +	g_free( key ); +	 +	for( i = 0; i < clear_len; i ++ ) +		crypt[0][i+ARC_IV_LEN] = clear[i] ^ arc_getbyte( st ); +	 +	g_free( st ); +	g_free( padded ); +	 +	return clear_len + ARC_IV_LEN; +} + +int arc_decode( unsigned char *crypt, int crypt_len, char **clear, char *password ) +{ +	struct arc_state *st; +	unsigned char *key; +	int key_len, clear_len, i; +	 +	key_len = strlen( password ) + ARC_IV_LEN; +	clear_len = crypt_len - ARC_IV_LEN; +	 +	if( clear_len < 0 ) +	{ +		*clear = g_strdup( "" ); +		return 0; +	} +	 +	/* Prepare buffers and the key + IV */ +	*clear = g_malloc( clear_len + 1 ); +	key = g_malloc( key_len ); +	strcpy( (char*) key, password ); +	for( i = 0; i < ARC_IV_LEN; i ++ ) +		key[key_len-ARC_IV_LEN+i] = crypt[i]; +	 +	/* Generate the initial S[] from the IVed key. */ +	st = arc_keymaker( key, key_len, ARC_CYCLES ); +	g_free( key ); +	 +	for( i = 0; i < clear_len; i ++ ) +		clear[0][i] = crypt[i+ARC_IV_LEN] ^ arc_getbyte( st ); +	clear[0][i] = 0; /* Nice to have for plaintexts. */ +	 +	g_free( st ); +	 +	return clear_len; +} diff --git a/lib/arc.h b/lib/arc.h new file mode 100644 index 00000000..58f30d3d --- /dev/null +++ b/lib/arc.h @@ -0,0 +1,36 @@ +/***************************************************************************\ +*                                                                           * +*  BitlBee - An IRC to IM gateway                                           * +*  Simple (but secure) ArcFour implementation for safer password storage.   * +*                                                                           * +*  Copyright 2007 Wilmer van der Gaast <wilmer@gaast.net>                   * +*                                                                           * +*  This program is free software; you can redistribute it and/or modify     * +*  it under the terms of the GNU General Public License as published by     * +*  the Free Software Foundation; either version 2 of the License, or        * +*  (at your option) any later version.                                      * +*                                                                           * +*  This program is distributed in the hope that it will be useful,          * +*  but WITHOUT ANY WARRANTY; without even the implied warranty of           * +*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            * +*  GNU General Public License for more details.                             * +*                                                                           * +*  You should have received a copy of the GNU General Public License along  * +*  with this program; if not, write to the Free Software Foundation, Inc.,  * +*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              * +*                                                                           * +\***************************************************************************/ + + +/* See arc.c for more information. */ + +struct arc_state +{ +	unsigned char S[256]; +	unsigned char i, j; +}; + +G_GNUC_MALLOC struct arc_state *arc_keymaker( unsigned char *key, int kl, int cycles ); +unsigned char arc_getbyte( struct arc_state *st ); +int arc_encode( char *clear, int clear_len, unsigned char **crypt, char *password, int pad_to ); +int arc_decode( unsigned char *crypt, int crypt_len, char **clear, char *password ); diff --git a/lib/base64.c b/lib/base64.c new file mode 100644 index 00000000..ea0db6b9 --- /dev/null +++ b/lib/base64.c @@ -0,0 +1,153 @@ +/***************************************************************************\ +*                                                                           * +*  BitlBee - An IRC to IM gateway                                           * +*  Base64 handling functions. encode_real() is mostly based on the y64 en-  * +*  coder from libyahoo2. Moving it to a new file because it's getting big.  * +*                                                                           * +*  Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net>                   * +*                                                                           * +*  This program is free software; you can redistribute it and/or modify     * +*  it under the terms of the GNU General Public License as published by     * +*  the Free Software Foundation; either version 2 of the License, or        * +*  (at your option) any later version.                                      * +*                                                                           * +*  This program is distributed in the hope that it will be useful,          * +*  but WITHOUT ANY WARRANTY; without even the implied warranty of           * +*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            * +*  GNU General Public License for more details.                             * +*                                                                           * +*  You should have received a copy of the GNU General Public License along  * +*  with this program; if not, write to the Free Software Foundation, Inc.,  * +*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              * +*                                                                           * +\***************************************************************************/ + +#include <glib.h> +#include <string.h> +#include "base64.h" + +static const char real_b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + +char *tobase64(const char *text) +{ +	return base64_encode((const unsigned char *)text, strlen(text)); +} + +char *base64_encode(const unsigned char *in, int len) +{ +	char *out; +	 +	out = g_malloc((len + 2)    /* the == padding */ +	                    / 3     /* every 3-byte block */ +	                    * 4     /* becomes a 4-byte one */ +	                    + 1);   /* and of course, ASCIIZ! */ +	 +	base64_encode_real((unsigned char*) in, len, (unsigned char*) out, real_b64); +	 +	return out; +} + +int base64_encode_real(const unsigned char *in, int inlen, unsigned char *out, const char *b64digits) +{ +	int outlen = 0; +	 +	for (; inlen >= 3; inlen -= 3) +	{ +		out[outlen++] = b64digits[in[0] >> 2]; +		out[outlen++] = b64digits[((in[0]<<4) & 0x30) | (in[1]>>4)]; +		out[outlen++] = b64digits[((in[1]<<2) & 0x3c) | (in[2]>>6)]; +		out[outlen++] = b64digits[in[2] & 0x3f]; +		in += 3; +	} +	if (inlen > 0) +	{ +		out[outlen++] = b64digits[in[0] >> 2]; +		if (inlen > 1) +		{ +			out[outlen++] = b64digits[((in[0]<<4) & 0x30) | (in[1]>>4)]; +			out[outlen++] = b64digits[((in[1]<<2) & 0x3c)]; +		} +		else +		{ +			out[outlen++] = b64digits[((in[0]<<4) & 0x30)]; +			out[outlen++] = b64digits[64]; +		} +		out[outlen++] = b64digits[64]; +	} +	out[outlen] = 0; +	 +	return outlen; +} + +/* Just a simple wrapper, but usually not very convenient because of zero +   termination. */ +char *frombase64(const char *in) +{ +	unsigned char *out; +	 +	base64_decode(in, &out); +	 +	return (char*) out; +} + +/* FIXME: Lookup table stuff is not threadsafe! (But for now BitlBee is not threaded.) */ +int base64_decode(const char *in, unsigned char **out) +{ +	static char b64rev[256] = { 0 }; +	int len, i; +	 +	/* Create a reverse-lookup for the Base64 sequence. */ +	if( b64rev[0] == 0 ) +	{ +		memset( b64rev, 0xff, 256 ); +		for( i = 0; i <= 64; i ++ ) +			b64rev[(int)real_b64[i]] = i; +	} +	 +	len = strlen( in ); +	*out = g_malloc( ( len + 6 ) / 4 * 3 ); +	len = base64_decode_real( (unsigned char*) in, *out, b64rev ); +	*out = g_realloc( *out, len + 1 ); +	out[0][len] = 0;	/* Zero termination can't hurt. */ +	 +	return len; +} + +int base64_decode_real(const unsigned char *in, unsigned char *out, char *b64rev) +{ +	int i, outlen = 0; +	 +	for( i = 0; in[i] && in[i+1] && in[i+2] && in[i+3]; i += 4 ) +	{ +		int sx; +		 +		sx = b64rev[(int)in[i+0]]; +		if( sx >= 64 ) +			break; +		out[outlen] = ( sx << 2 ) & 0xfc; +		 +		sx = b64rev[(int)in[i+1]]; +		if( sx >= 64 ) +			break; +		out[outlen] |= ( sx >> 4 ) & 0x03; +		outlen ++; +		out[outlen] = ( sx << 4 ) & 0xf0; +		 +		sx = b64rev[(int)in[i+2]]; +		if( sx >= 64 ) +			break; +		out[outlen] |= ( sx >> 2 ) & 0x0f; +		outlen ++; +		out[outlen] = ( sx << 6 ) & 0xc0; +		 +		sx = b64rev[(int)in[i+3]]; +		if( sx >= 64 ) +			break; +		out[outlen] |= sx; +		outlen ++; +	} +	 +	/* If sx > 64 the base64 string was damaged. Should we ignore this? */ +	 +	return outlen; +} diff --git a/lib/base64.h b/lib/base64.h new file mode 100644 index 00000000..ebd74bf1 --- /dev/null +++ b/lib/base64.h @@ -0,0 +1,33 @@ +/***************************************************************************\ +*                                                                           * +*  BitlBee - An IRC to IM gateway                                           * +*  Base64 handling functions. encode_real() is mostly based on the y64 en-  * +*  coder from libyahoo2. Moving it to a new file because it's getting big.  * +*                                                                           * +*  Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net>                   * +*                                                                           * +*  This program is free software; you can redistribute it and/or modify     * +*  it under the terms of the GNU General Public License as published by     * +*  the Free Software Foundation; either version 2 of the License, or        * +*  (at your option) any later version.                                      * +*                                                                           * +*  This program is distributed in the hope that it will be useful,          * +*  but WITHOUT ANY WARRANTY; without even the implied warranty of           * +*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            * +*  GNU General Public License for more details.                             * +*                                                                           * +*  You should have received a copy of the GNU General Public License along  * +*  with this program; if not, write to the Free Software Foundation, Inc.,  * +*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              * +*                                                                           * +\***************************************************************************/ + +#include <glib.h> +#include <gmodule.h> + +G_MODULE_EXPORT char *tobase64( const char *text ); +G_MODULE_EXPORT char *base64_encode( const unsigned char *in, int len ); +G_MODULE_EXPORT int base64_encode_real( const unsigned char *in, int inlen, unsigned char *out, const char *b64digits ); +G_MODULE_EXPORT char *frombase64( const char *in ); +G_MODULE_EXPORT int base64_decode( const char *in, unsigned char **out ); +G_MODULE_EXPORT int base64_decode_real( const unsigned char *in, unsigned char *out, char *b64reverse ); diff --git a/lib/events.h b/lib/events.h new file mode 100644 index 00000000..4baea7b6 --- /dev/null +++ b/lib/events.h @@ -0,0 +1,87 @@ +/* + * nogaim + * + * Copyright (C) 2006 Wilmer van der Gaast and others + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * + */ + +/* This stuff used to be in proxy.c too, but I split it off so BitlBee can +   use other libraries (like libevent) to handle events. proxy.c is one very +   nice piece of work from Gaim. It connects to a TCP server in the back- +   ground and calls a callback function once the connection is ready to use. +   This function (proxy_connect()) can be found in proxy.c. (It also +   transparently handles HTTP/SOCKS proxies, when necessary.) +    +   This file offers some extra event handling toys, which will be handled +   by GLib or libevent. The advantage of using libevent is that it can use +   more advanced I/O polling functions like epoll() in recent Linux +   kernels. This should improve BitlBee's scalability. */ + + +#ifndef _EVENTS_H_ +#define _EVENTS_H_ + +#include <sys/types.h> +#ifndef _WIN32 +#include <sys/socket.h> +#include <netdb.h> +#include <netinet/in.h> +#endif +#include <glib.h> +#include <gmodule.h> + +/* The conditions you can pass to b_input_add()/that will be passed to +   the given callback function. */ +typedef enum { +	GAIM_INPUT_READ = 1 << 1, +	GAIM_INPUT_WRITE = 1 << 2 +} b_input_condition; +typedef gboolean (*b_event_handler)(gpointer data, gint fd, b_input_condition cond); + +/* For internal use. */ +#define GAIM_READ_COND  (G_IO_IN | G_IO_HUP | G_IO_ERR) +#define GAIM_WRITE_COND (G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL) +#define GAIM_ERR_COND   (G_IO_HUP | G_IO_ERR | G_IO_NVAL) + +/* #define event_debug( x... ) printf( x ) */ +#define event_debug( x... ) + +/* Call this once when the program starts. It'll initialize the event handler +   library (if necessary) and then return immediately. */ +G_MODULE_EXPORT void b_main_init(); + +/* This one enters the event loop. It shouldn't return until one of the event +   handlers calls b_main_quit(). */ +G_MODULE_EXPORT void b_main_run(); +G_MODULE_EXPORT void b_main_quit(); + + +/* Add event handlers (for I/O or a timeout). The event handler will be called +   every time the event "happens", until your event handler returns FALSE (or +   until you remove it using b_event_remove(). As usual, the data argument +   can be used to pass your own data to the event handler. */ +G_MODULE_EXPORT gint b_input_add(int fd, b_input_condition cond, b_event_handler func, gpointer data); +G_MODULE_EXPORT gint b_timeout_add(gint timeout, b_event_handler func, gpointer data); +G_MODULE_EXPORT void b_event_remove(gint id); + +/* For now, closesocket() is only a function when using libevent. With GLib +   it's a preprocessor macro. */ +#ifdef EVENTS_LIBEVENT +G_MODULE_EXPORT void closesocket(int fd); +#endif + +#endif /* _EVENTS_H_ */ diff --git a/lib/events_glib.c b/lib/events_glib.c new file mode 100644 index 00000000..3e194e98 --- /dev/null +++ b/lib/events_glib.c @@ -0,0 +1,141 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2002-2006 Wilmer van der Gaast and others                * +  \********************************************************************/ + +/* + * Event handling (using GLib) + */ + +/* +  This program is free software; you can redistribute it and/or modify +  it under the terms of the GNU General Public License as published by +  the Free Software Foundation; either version 2 of the License, or +  (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License with +  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; +  if not, write to the Free Software Foundation, Inc., 59 Temple Place, +  Suite 330, Boston, MA  02111-1307  USA +*/ + +#define BITLBEE_CORE +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#ifndef _WIN32 +#include <sys/socket.h> +#include <netdb.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <unistd.h> +#else +#include "sock.h" +#define ETIMEDOUT WSAETIMEDOUT +#define EINPROGRESS WSAEINPROGRESS +#endif +#include <fcntl.h> +#include <errno.h> +#include "proxy.h" + +typedef struct _GaimIOClosure { +	b_event_handler function; +	gpointer data; +} GaimIOClosure; + +static GMainLoop *loop = NULL; + +void b_main_init() +{ +	if( loop == NULL ) +		loop = g_main_new( FALSE ); +} + +void b_main_run() +{ +	g_main_run( loop ); +} + +void b_main_quit() +{ +	g_main_quit( loop ); +} + +static gboolean gaim_io_invoke(GIOChannel *source, GIOCondition condition, gpointer data) +{ +	GaimIOClosure *closure = data; +	b_input_condition gaim_cond = 0; +	gboolean st; + +	if (condition & GAIM_READ_COND) +		gaim_cond |= GAIM_INPUT_READ; +	if (condition & GAIM_WRITE_COND) +		gaim_cond |= GAIM_INPUT_WRITE; +	 +	event_debug( "gaim_io_invoke( %d, %d, 0x%x )\n", g_io_channel_unix_get_fd(source), condition, data ); + +	st = closure->function(closure->data, g_io_channel_unix_get_fd(source), gaim_cond); +	 +	if( !st ) +		event_debug( "Returned FALSE, cancelling.\n" ); +	 +	return st; +} + +static void gaim_io_destroy(gpointer data) +{ +	event_debug( "gaim_io_destroy( 0x%x )\n", data ); +	g_free(data); +} + +gint b_input_add(gint source, b_input_condition condition, b_event_handler function, gpointer data) +{ +	GaimIOClosure *closure = g_new0(GaimIOClosure, 1); +	GIOChannel *channel; +	GIOCondition cond = 0; +	int st; +	 +	closure->function = function; +	closure->data = data; +	 +	if (condition & GAIM_INPUT_READ) +		cond |= GAIM_READ_COND; +	if (condition & GAIM_INPUT_WRITE) +		cond |= GAIM_WRITE_COND; +	 +	channel = g_io_channel_unix_new(source); +	st = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, cond, +	                         gaim_io_invoke, closure, gaim_io_destroy); +	 +	event_debug( "b_input_add( %d, %d, 0x%x, 0x%x ) = %d (%p)\n", source, condition, function, data, st, closure ); +	 +	g_io_channel_unref(channel); +	return st; +} + +gint b_timeout_add(gint timeout, b_event_handler func, gpointer data) +{ +	/* GSourceFunc and the BitlBee event handler function aren't +	   really the same, but they're "compatible". ;-) It will do +	   for now, BitlBee only looks at the "data" argument. */ +	gint st = g_timeout_add(timeout, (GSourceFunc) func, data); +	 +	event_debug( "b_timeout_add( %d, %d, %d ) = %d\n", timeout, func, data, st ); +	 +	return st; +} + +void b_event_remove(gint tag) +{ +	event_debug( "b_event_remove( %d )\n", tag ); +	 +	if (tag > 0) +		g_source_remove(tag); +} diff --git a/lib/events_libevent.c b/lib/events_libevent.c new file mode 100644 index 00000000..d3403152 --- /dev/null +++ b/lib/events_libevent.c @@ -0,0 +1,278 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2002-2006 Wilmer van der Gaast and others                * +  \********************************************************************/ + +/* + * Event handling (using libevent) + */ + +/* +  This program is free software; you can redistribute it and/or modify +  it under the terms of the GNU General Public License as published by +  the Free Software Foundation; either version 2 of the License, or +  (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License with +  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; +  if not, write to the Free Software Foundation, Inc., 59 Temple Place, +  Suite 330, Boston, MA  02111-1307  USA +*/ + +#define BITLBEE_CORE +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/time.h> +#include <event.h> +#include "proxy.h" + +static void b_main_restart(); +static guint id_next = 1; +static GHashTable *id_hash; +static int quitting = 0; + +/* Since libevent doesn't handle two event handlers for one fd-condition +   very well (which happens sometimes when BitlBee changes event handlers +   for a combination), let's buid some indexes so we can delete them here +   already, just in time. */ +static GHashTable *read_hash; +static GHashTable *write_hash; + +struct event_base *leh; +struct event_base *old_leh; + +struct b_event_data +{ +	guint id; +	struct event evinfo; +	gint timeout; +	b_event_handler function; +	void *data; +}; + +void b_main_init() +{ +	if( leh != NULL ) +	{ +		/* Clean up the hash tables? */ +		 +		b_main_restart(); +		old_leh = leh; +	} +	 +	leh = event_init(); +	 +	id_hash = g_hash_table_new( g_int_hash, g_int_equal ); +	read_hash = g_hash_table_new( g_int_hash, g_int_equal ); +	write_hash = g_hash_table_new( g_int_hash, g_int_equal ); +} + +void b_main_run() +{ +	/* This while loop is necessary to exit the event loop and start a +	   different one (necessary for ForkDaemon mode). */ +	while( event_base_dispatch( leh ) == 0 && !quitting ) +	{ +		if( old_leh != NULL ) +		{ +			/* For some reason this just isn't allowed... +			   Possibly a bug in older versions, will see later. +			event_base_free( old_leh ); */ +			old_leh = NULL; +		} +		 +		event_debug( "New event loop.\n" ); +	} +} + +static void b_main_restart() +{ +	struct timeval tv; +	 +	memset( &tv, 0, sizeof( struct timeval ) ); +	event_base_loopexit( leh, &tv ); +	 +	event_debug( "b_main_restart()\n" ); +} + +void b_main_quit() +{ +	/* Tell b_main_run() that it shouldn't restart the loop. Also, +	   libevent sometimes generates events before really quitting, +	   we want to stop them. */ +	quitting = 1; +	 +	b_main_restart(); +} + +static void b_event_passthrough( int fd, short event, void *data ) +{ +	struct b_event_data *b_ev = data; +	b_input_condition cond = 0; +	int id; +	 +	if( fd >= 0 ) +	{ +		if( event & EV_READ ) +			cond |= GAIM_INPUT_READ; +		if( event & EV_WRITE ) +			cond |= GAIM_INPUT_WRITE; +	} +	 +	event_debug( "b_event_passthrough( %d, %d, 0x%x ) (%d)\n", fd, event, (int) data, b_ev->id ); +	 +	/* Since the called function might cancel this handler already +	   (which free()s b_ev), we have to remember the ID here. */ +	id = b_ev->id; +	 +	if( quitting ) +	{ +		b_event_remove( id ); +		return; +	} +	 +	if( !b_ev->function( b_ev->data, fd, cond ) ) +	{ +		event_debug( "Handler returned FALSE: " ); +		b_event_remove( id ); +	} +	else if( fd == -1 ) +	{ +		struct timeval tv; +		 +		tv.tv_sec = b_ev->timeout / 1000; +		tv.tv_usec = ( b_ev->timeout % 1000 ) * 1000; +		 +		evtimer_add( &b_ev->evinfo, &tv ); +	} +} + +gint b_input_add( gint fd, b_input_condition condition, b_event_handler function, gpointer data ) +{ +	struct b_event_data *b_ev; +	 +	event_debug( "b_input_add( %d, %d, 0x%x, 0x%x ) ", fd, condition, function, data ); +	 +	if( ( condition & GAIM_INPUT_READ  && ( b_ev = g_hash_table_lookup( read_hash,  &fd ) ) ) || +	    ( condition & GAIM_INPUT_WRITE && ( b_ev = g_hash_table_lookup( write_hash, &fd ) ) ) ) +	{ +		/* We'll stick with this libevent entry, but give it a new BitlBee id. */ +		g_hash_table_remove( id_hash, &b_ev->id ); +		 +		event_debug( "(replacing old handler (id = %d)) = %d\n", b_ev->id, id_next ); +		 +		b_ev->id = id_next++; +		b_ev->function = function; +		b_ev->data = data; +	} +	else +	{ +		GIOCondition out_cond; +		 +		event_debug( "(new) = %d\n", id_next ); +		 +		b_ev = g_new0( struct b_event_data, 1 ); +		b_ev->id = id_next++; +		b_ev->function = function; +		b_ev->data = data; +		 +		out_cond = EV_PERSIST; +		if( condition & GAIM_INPUT_READ ) +			out_cond |= EV_READ; +		if( condition & GAIM_INPUT_WRITE ) +			out_cond |= EV_WRITE; +		 +		event_set( &b_ev->evinfo, fd, out_cond, b_event_passthrough, b_ev ); +		event_add( &b_ev->evinfo, NULL ); +		 +		if( out_cond & EV_READ ) +			g_hash_table_insert( read_hash, &b_ev->evinfo.ev_fd, b_ev ); +		if( out_cond & EV_WRITE ) +			g_hash_table_insert( write_hash, &b_ev->evinfo.ev_fd, b_ev ); +	} +	 +	g_hash_table_insert( id_hash, &b_ev->id, b_ev ); +	return b_ev->id; +} + +/* TODO: Persistence for timers! */ +gint b_timeout_add( gint timeout, b_event_handler function, gpointer data ) +{ +	struct b_event_data *b_ev = g_new0( struct b_event_data, 1 ); +	struct timeval tv; +	 +	b_ev->id = id_next++; +	b_ev->timeout = timeout; +	b_ev->function = function; +	b_ev->data = data; +	 +	tv.tv_sec = timeout / 1000; +	tv.tv_usec = ( timeout % 1000 ) * 1000; +	 +	evtimer_set( &b_ev->evinfo, b_event_passthrough, b_ev ); +	evtimer_add( &b_ev->evinfo, &tv ); +	 +	event_debug( "b_timeout_add( %d, 0x%x, 0x%x ) = %d\n", timeout, function, data, b_ev->id ); +	 +	g_hash_table_insert( id_hash, &b_ev->id, b_ev ); +	 +	return b_ev->id; +} + +void b_event_remove( gint id ) +{ +	struct b_event_data *b_ev = g_hash_table_lookup( id_hash, &id ); +	 +	event_debug( "b_event_remove( %d )\n", id ); +	if( b_ev ) +	{ +		g_hash_table_remove( id_hash, &b_ev->id ); +		if( b_ev->evinfo.ev_fd >= 0 ) +		{ +			if( b_ev->evinfo.ev_events & EV_READ ) +				g_hash_table_remove( read_hash, &b_ev->evinfo.ev_fd ); +			if( b_ev->evinfo.ev_events & EV_WRITE ) +				g_hash_table_remove( write_hash, &b_ev->evinfo.ev_fd ); +		} +		 +		event_del( &b_ev->evinfo ); +		g_free( b_ev ); +	} +	else +	{ +		event_debug( "Already removed?\n" ); +	} +} + +void closesocket( int fd ) +{ +	struct b_event_data *b_ev; +	 +	/* Since epoll() (the main reason we use libevent) automatically removes sockets from +	   the epoll() list when a socket gets closed and some modules have a habit of +	   closing sockets before removing event handlers, our and libevent's administration +	   get a little bit messed up. So this little function will remove the handlers +	   properly before closing a socket. */ +	 +	if( ( b_ev = g_hash_table_lookup( read_hash, &fd ) ) ) +	{ +		event_debug( "Warning: fd %d still had a read event handler when shutting down.\n", fd ); +		b_event_remove( b_ev->id ); +	} +	if( ( b_ev = g_hash_table_lookup( write_hash, &fd ) ) ) +	{ +		event_debug( "Warning: fd %d still had a write event handler when shutting down.\n", fd ); +		b_event_remove( b_ev->id ); +	} +	 +	close( fd ); +} diff --git a/lib/http_client.c b/lib/http_client.c new file mode 100644 index 00000000..b00fcf98 --- /dev/null +++ b/lib/http_client.c @@ -0,0 +1,453 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2002-2005 Wilmer van der Gaast and others                * +  \********************************************************************/ + +/* HTTP(S) module                                                       */ + +/* +  This program is free software; you can redistribute it and/or modify +  it under the terms of the GNU General Public License as published by +  the Free Software Foundation; either version 2 of the License, or +  (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License with +  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; +  if not, write to the Free Software Foundation, Inc., 59 Temple Place, +  Suite 330, Boston, MA  02111-1307  USA +*/ + +#include <string.h> +#include <stdio.h> + +#include "http_client.h" +#include "url.h" +#include "sock.h" + + +static gboolean http_connected( gpointer data, int source, b_input_condition cond ); +static gboolean http_ssl_connected( gpointer data, void *source, b_input_condition cond ); +static gboolean http_incoming_data( gpointer data, int source, b_input_condition cond ); + + +void *http_dorequest( char *host, int port, int ssl, char *request, http_input_function func, gpointer data ) +{ +	struct http_request *req; +	int error = 0; +	 +	req = g_new0( struct http_request, 1 ); +	 +	if( ssl ) +	{ +		req->ssl = ssl_connect( host, port, http_ssl_connected, req ); +		if( req->ssl == NULL ) +			error = 1; +	} +	else +	{ +		req->fd = proxy_connect( host, port, http_connected, req ); +		if( req->fd < 0 ) +			error = 1; +	} +	 +	if( error ) +	{ +		g_free( req ); +		return( NULL ); +	} +	 +	req->func = func; +	req->data = data; +	req->request = g_strdup( request ); +	req->request_length = strlen( request ); +	 +	return( req ); +} + +void *http_dorequest_url( char *url_string, http_input_function func, gpointer data ) +{ +	url_t *url = g_new0( url_t, 1 ); +	char *request; +	void *ret; +	 +	if( !url_set( url, url_string ) ) +	{ +		g_free( url ); +		return NULL; +	} +	 +	if( url->proto != PROTO_HTTP && url->proto != PROTO_HTTPS ) +	{ +		g_free( url ); +		return NULL; +	} +	 +	request = g_strdup_printf( "GET %s HTTP/1.0\r\n" +	                           "Host: %s\r\n" +	                           "User-Agent: BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\r\n" +	                           "\r\n", url->file, url->host ); +	 +	ret = http_dorequest( url->host, url->port, +	                      url->proto == PROTO_HTTPS, request, func, data ); +	 +	g_free( url ); +	g_free( request ); +	return ret; +} + +/* This one is actually pretty simple... Might get more calls if we can't write  +   the whole request at once. */ +static gboolean http_connected( gpointer data, int source, b_input_condition cond ) +{ +	struct http_request *req = data; +	int st; +	 +	if( source < 0 ) +		goto error; +	 +	if( req->inpa > 0 ) +		b_event_remove( req->inpa ); +	 +	sock_make_nonblocking( req->fd ); +	 +	if( req->ssl ) +	{ +		st = ssl_write( req->ssl, req->request + req->bytes_written, +		                req->request_length - req->bytes_written ); +		if( st < 0 ) +		{ +			if( ssl_errno != SSL_AGAIN ) +			{ +				ssl_disconnect( req->ssl ); +				goto error; +			} +		} +	} +	else +	{ +		st = write( source, req->request + req->bytes_written, +		                    req->request_length - req->bytes_written ); +		if( st < 0 ) +		{ +			if( !sockerr_again() ) +			{ +				closesocket( req->fd ); +				goto error; +			} +		} +	} +	 +	if( st > 0 ) +		req->bytes_written += st; +	 +	if( req->bytes_written < req->request_length ) +		req->inpa = b_input_add( source, +		                         req->ssl ? ssl_getdirection( req->ssl ) : GAIM_INPUT_WRITE, +	        	                 http_connected, req ); +	else +		req->inpa = b_input_add( source, GAIM_INPUT_READ, http_incoming_data, req ); +	 +	return FALSE; +	 +error: +	req->status_string = g_strdup( "Error while writing HTTP request" ); +	 +	req->func( req ); +	 +	g_free( req->request ); +	g_free( req ); +	 +	return FALSE; +} + +static gboolean http_ssl_connected( gpointer data, void *source, b_input_condition cond ) +{ +	struct http_request *req = data; +	 +	if( source == NULL ) +		return http_connected( data, -1, cond ); +	 +	req->fd = ssl_getfd( source ); +	 +	return http_connected( data, req->fd, cond ); +} + +static gboolean http_incoming_data( gpointer data, int source, b_input_condition cond ) +{ +	struct http_request *req = data; +	int evil_server = 0; +	char buffer[2048]; +	char *end1, *end2; +	int st; +	 +	if( req->inpa > 0 ) +		b_event_remove( req->inpa ); +	 +	if( req->ssl ) +	{ +		st = ssl_read( req->ssl, buffer, sizeof( buffer ) ); +		if( st < 0 ) +		{ +			if( ssl_errno != SSL_AGAIN ) +			{ +				/* goto cleanup; */ +				 +				/* YAY! We have to deal with crappy Microsoft +				   servers that LOVE to send invalid TLS +				   packets that abort connections! \o/ */ +				 +				goto got_reply; +			} +		} +		else if( st == 0 ) +		{ +			goto got_reply; +		} +	} +	else +	{ +		st = read( req->fd, buffer, sizeof( buffer ) ); +		if( st < 0 ) +		{ +			if( !sockerr_again() ) +			{ +				req->status_string = g_strdup( strerror( errno ) ); +				goto cleanup; +			} +		} +		else if( st == 0 ) +		{ +			goto got_reply; +		} +	} +	 +	if( st > 0 ) +	{ +		req->reply_headers = g_realloc( req->reply_headers, req->bytes_read + st + 1 ); +		memcpy( req->reply_headers + req->bytes_read, buffer, st ); +		req->bytes_read += st; +	} +	 +	/* There will be more! */ +	req->inpa = b_input_add( req->fd, +	                         req->ssl ? ssl_getdirection( req->ssl ) : GAIM_INPUT_READ, +	                         http_incoming_data, req ); +	 +	return FALSE; + +got_reply: +	/* Maybe if the webserver is overloaded, or when there's bad SSL +	   support... */ +	if( req->bytes_read == 0 ) +	{ +		req->status_string = g_strdup( "Empty HTTP reply" ); +		goto cleanup; +	} +	 +	/* Zero termination is very convenient. */ +	req->reply_headers[req->bytes_read] = 0; +	 +	/* Find the separation between headers and body, and keep stupid +	   webservers in mind. */ +	end1 = strstr( req->reply_headers, "\r\n\r\n" ); +	end2 = strstr( req->reply_headers, "\n\n" ); +	 +	if( end2 && end2 < end1 ) +	{ +		end1 = end2 + 1; +		evil_server = 1; +	} +	else if( end1 ) +	{ +		end1 += 2; +	} +	else +	{ +		req->status_string = g_strdup( "Malformed HTTP reply" ); +		goto cleanup; +	} +	 +	*end1 = 0; +	 +	if( evil_server ) +		req->reply_body = end1 + 1; +	else +		req->reply_body = end1 + 2; +	 +	req->body_size = req->reply_headers + req->bytes_read - req->reply_body; +	 +	if( ( end1 = strchr( req->reply_headers, ' ' ) ) != NULL ) +	{ +		if( sscanf( end1 + 1, "%d", &req->status_code ) != 1 ) +		{ +			req->status_string = g_strdup( "Can't parse status code" ); +			req->status_code = -1; +		} +		else +		{ +			char *eol; +			 +			if( evil_server ) +				eol = strchr( end1, '\n' ); +			else +				eol = strchr( end1, '\r' ); +			 +			req->status_string = g_strndup( end1 + 1, eol - end1 - 1 ); +			 +			/* Just to be sure... */ +			if( ( eol = strchr( req->status_string, '\r' ) ) ) +				*eol = 0; +			if( ( eol = strchr( req->status_string, '\n' ) ) ) +				*eol = 0; +		} +	} +	else +	{ +		req->status_string = g_strdup( "Can't locate status code" ); +		req->status_code = -1; +	} +	 +	if( req->status_code == 301 || req->status_code == 302 ) +	{ +		char *loc, *new_request, *new_host; +		int error = 0, new_port, new_proto; +		 +		/* We might fill it again, so let's not leak any memory. */ +		g_free( req->status_string ); +		req->status_string = NULL; +		 +		loc = strstr( req->reply_headers, "\nLocation: " ); +		if( loc == NULL ) /* We can't handle this redirect... */ +		{ +			req->status_string = g_strdup( "Can't locate Location: header" ); +			goto cleanup; +		} +		 +		loc += 11; +		while( *loc == ' ' ) +			loc ++; +		 +		/* TODO/FIXME: Possibly have to handle relative redirections, +		   and rewrite Host: headers. Not necessary for now, it's +		   enough for passport authentication like this. */ +		 +		if( *loc == '/' ) +		{ +			/* Just a different pathname... */ +			 +			/* Since we don't cache the servername, and since we +			   don't need this yet anyway, I won't implement it. */ +			 +			req->status_string = g_strdup( "Can't handle recursive redirects" ); +			 +			goto cleanup; +		} +		else +		{ +			/* A whole URL */ +			url_t *url; +			char *s; +			 +			s = strstr( loc, "\r\n" ); +			if( s == NULL ) +				goto cleanup; +			 +			url = g_new0( url_t, 1 ); +			*s = 0; +			 +			if( !url_set( url, loc ) ) +			{ +				req->status_string = g_strdup( "Malformed redirect URL" ); +				g_free( url ); +				goto cleanup; +			} +			 +			/* Okay, this isn't fun! We have to rebuild the request... :-( */ +			new_request = g_malloc( req->request_length + strlen( url->file ) ); +			 +			/* So, now I just allocated enough memory, so I'm +			   going to use strcat(), whether you like it or not. :-) */ +			 +			sprintf( new_request, "GET %s HTTP/1.0", url->file ); +			 +			s = strstr( req->request, "\r\n" ); +			if( s == NULL ) +			{ +				req->status_string = g_strdup( "Error while rebuilding request string" ); +				g_free( new_request ); +				g_free( url ); +				goto cleanup; +			} +			 +			strcat( new_request, s ); +			new_host = g_strdup( url->host ); +			new_port = url->port; +			new_proto = url->proto; +			 +			g_free( url ); +		} +		 +		if( req->ssl ) +			ssl_disconnect( req->ssl ); +		else +			closesocket( req->fd ); +		 +		req->fd = -1; +		req->ssl = NULL; +		 +		if( new_proto == PROTO_HTTPS ) +		{ +			req->ssl = ssl_connect( new_host, new_port, http_ssl_connected, req ); +			if( req->ssl == NULL ) +				error = 1; +		} +		else +		{ +			req->fd = proxy_connect( new_host, new_port, http_connected, req ); +			if( req->fd < 0 ) +				error = 1; +		} +		g_free( new_host ); +		 +		if( error ) +		{ +			req->status_string = g_strdup( "Connection problem during redirect" ); +			g_free( new_request ); +			goto cleanup; +		} +		 +		g_free( req->request ); +		g_free( req->reply_headers ); +		req->request = new_request; +		req->request_length = strlen( new_request ); +		req->bytes_read = req->bytes_written = req->inpa = 0; +		req->reply_headers = req->reply_body = NULL; +		 +		return FALSE; +	} +	 +	/* Assume that a closed connection means we're finished, this indeed +	   breaks with keep-alive connections and faulty connections. */ +	req->finished = 1; + +cleanup: +	if( req->ssl ) +		ssl_disconnect( req->ssl ); +	else +		closesocket( req->fd ); +	 +	req->func( req ); +	 +	g_free( req->request ); +	g_free( req->reply_headers ); +	g_free( req->status_string ); +	g_free( req ); +	 +	return FALSE; +} diff --git a/lib/http_client.h b/lib/http_client.h new file mode 100644 index 00000000..78d6dbd1 --- /dev/null +++ b/lib/http_client.h @@ -0,0 +1,82 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2002-2005 Wilmer van der Gaast and others                * +  \********************************************************************/ + +/* HTTP(S) module                                                       */ + +/* +  This program is free software; you can redistribute it and/or modify +  it under the terms of the GNU General Public License as published by +  the Free Software Foundation; either version 2 of the License, or +  (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License with +  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; +  if not, write to the Free Software Foundation, Inc., 59 Temple Place, +  Suite 330, Boston, MA  02111-1307  USA +*/ + +/* http_client allows you to talk (asynchronously, again) to HTTP servers. +   In the "background" it will send the whole query and wait for a complete +   response to come back. Right now it's only used by the MSN Passport +   authentication code, but it might be useful for other things too (for +   example the AIM usericon patch uses this so icons can be stored on +   webservers instead of the local filesystem). +    +   Didn't test this too much, but it seems to work well. Just don't look +   at the code that handles HTTP 30x redirects. ;-) The function is +   probably not very useful for downloading lots of data since it keeps  +   everything in a memory buffer until the download is completed (and +   can't pass any data or whatever before then). It's very useful for +   doing quick requests without blocking the whole program, though. */ + +#include <glib.h> +#include "ssl_client.h" + +struct http_request; + +/* Your callback function should look like this: */ +typedef void (*http_input_function)( struct http_request * ); + +/* This structure will be filled in by the http_dorequest* functions, and +   it will be passed to the callback function. Use the data field to add +   your own data. */ +struct http_request +{ +	char *request;          /* The request to send to the server. */ +	int request_length;     /* Its size. */ +	int status_code;        /* The numeric HTTP status code. (Or -1 +	                           if something really went wrong) */ +	char *status_string;    /* The error text. */ +	char *reply_headers; +	char *reply_body; +	int body_size;          /* The number of bytes in reply_body. */ +	int finished;           /* Set to non-0 if the request was completed +	                           successfully. */ +	 +	http_input_function func; +	gpointer data; +	 +	/* Please don't touch the things down here, you shouldn't need them. */ +	 +	void *ssl; +	int fd; +	 +	int inpa; +	int bytes_written; +	int bytes_read; +}; + +/* The _url variant is probably more useful than the raw version. The raw +   version is probably only useful if you want to do POST requests or if +   you want to add some extra headers. As you can see, HTTPS connections +   are also supported (using ssl_client). */ +void *http_dorequest( char *host, int port, int ssl, char *request, http_input_function func, gpointer data ); +void *http_dorequest_url( char *url_string, http_input_function func, gpointer data ); diff --git a/lib/ini.c b/lib/ini.c new file mode 100644 index 00000000..c63a132e --- /dev/null +++ b/lib/ini.c @@ -0,0 +1,90 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2002-2005 Wilmer van der Gaast and others                * +  \********************************************************************/ + +/* INI file reading code						*/ + +/* +  This program is free software; you can redistribute it and/or modify +  it under the terms of the GNU General Public License as published by +  the Free Software Foundation; either version 2 of the License, or +  (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License with +  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; +  if not, write to the Free Software Foundation, Inc., 59 Temple Place, +  Suite 330, Boston, MA  02111-1307  USA +*/ +#define BITLBEE_CORE +#include "bitlbee.h" + +ini_t *ini_open( char *file ) +{ +	ini_t *ini = g_new0( ini_t, 1 ); +	 +	if( ( ini->fp = fopen( file, "r" ) ) == NULL ) +	{ +		g_free( ini ); +		return( NULL ); +	} +	 +	return( ini ); +} + +int ini_read( ini_t *file ) +{ +	char key[MAX_STRING], s[MAX_STRING], *t; +	int i; +	 +	while( !feof( file->fp ) ) +	{ +		*s = 0; +		fscanf( file->fp, "%127[^\n#]s", s ); +		fscanf( file->fp, "%*[^\n]s" ); +		fgetc( file->fp );		/* Skip newline		*/ +		file->line ++; +		if( strchr( s, '=' ) ) +		{ +			sscanf( s, "%[^ =]s", key ); +			if( ( t = strchr( key, '.' ) ) ) +			{ +				*t = 0; +				strcpy( file->section, key ); +				t ++; +			} +			else +			{ +				strcpy( file->section, file->c_section ); +				t = key; +			} +			sscanf( t, "%s", file->key ); +			t = strchr( s, '=' ) + 1; +			for( i = 0; t[i] == ' '; i ++ ); +			strcpy( file->value, &t[i] ); +			for( i = strlen( file->value ) - 1; file->value[i] == 32; i -- ) +				file->value[i] = 0; +			 +			return( 1 ); +		} +		else if( ( t = strchr( s, '[' ) ) ) +		{ +			strcpy( file->c_section, t + 1 ); +			t = strchr( file->c_section, ']' ); +			*t = 0; +		} +	} +	return( 0 ); +} + +void ini_close( ini_t *file ) +{ +	fclose( file->fp ); +	g_free( file ); +} diff --git a/lib/ini.h b/lib/ini.h new file mode 100644 index 00000000..5eab472b --- /dev/null +++ b/lib/ini.h @@ -0,0 +1,43 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2002-2004 Wilmer van der Gaast and others                * +  \********************************************************************/ + +/* INI file reading code						*/ + +/* +  This program is free software; you can redistribute it and/or modify +  it under the terms of the GNU General Public License as published by +  the Free Software Foundation; either version 2 of the License, or +  (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License with +  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; +  if not, write to the Free Software Foundation, Inc., 59 Temple Place, +  Suite 330, Boston, MA  02111-1307  USA +*/ + +#ifndef _INI_H +#define _INI_H + +typedef struct +{ +	FILE *fp; +	int line; +	char c_section[MAX_STRING]; +	char section[MAX_STRING]; +	char key[MAX_STRING]; +	char value[MAX_STRING]; +} ini_t; + +ini_t *ini_open( char *file ); +int ini_read( ini_t *file ); +void ini_close( ini_t *file ); + +#endif diff --git a/lib/md5.c b/lib/md5.c new file mode 100644 index 00000000..3c39eccd --- /dev/null +++ b/lib/md5.c @@ -0,0 +1,262 @@ +/* + * MD5 hashing code copied from Lepton's crack <http://usuarios.lycos.es/reinob/> + * + * Adapted to be API-compatible with the previous (GPL-incompatible) code. + */ + +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest.  This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +#include <sys/types.h> +#include <string.h>		/* for memcpy() */ +#include "md5.h" + +static void md5_transform(uint32_t buf[4], uint32_t const in[16]); + +/* + * Wrapper function for all-in-one MD5 + * + * Bernardo Reino, aka Lepton. + * 20021120 + */ + +/* Turns out MD5 was designed for little-endian machines. If we're running +   on a big-endian machines, we have to swap some bytes. Since detecting +   endianness at compile time reliably seems pretty hard, let's do it at +   run-time. It's not like we're going to checksum megabytes of data... */ +static uint32_t cvt32(uint32_t val) +{ +	static int little_endian = -1; +	 +	if (little_endian == -1) +	{ +		little_endian = 1; +		little_endian = *((char*) &little_endian); +	} +	 +	if (little_endian) +		return val; +	else +		return (val >> 24) | +		       ((val >> 8) & 0xff00) | +		       ((val << 8) & 0xff0000) | +		       (val << 24); +} + +void md5_init(struct MD5Context *ctx) +{ +	ctx->buf[0] = 0x67452301; +	ctx->buf[1] = 0xefcdab89; +	ctx->buf[2] = 0x98badcfe; +	ctx->buf[3] = 0x10325476; + +	ctx->bits[0] = 0; +	ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void md5_append(struct MD5Context *ctx, const md5_byte_t *buf, +		unsigned int len) +{ +	uint32_t t; + +	/* Update bitcount */ + +	t = ctx->bits[0]; +	if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t) +		ctx->bits[1]++;	/* Carry from low to high */ +	ctx->bits[1] += len >> 29; + +	t = (t >> 3) & 0x3f;	/* Bytes already in shsInfo->data */ + +	/* Handle any leading odd-sized chunks */ + +	if (t) { +		unsigned char *p = (unsigned char *) ctx->in + t; + +		t = 64 - t; +		if (len < t) { +			memcpy(p, buf, len); +			return; +		} +		memcpy(p, buf, t); +		md5_transform(ctx->buf, (uint32_t *) ctx->in); +		buf += t; +		len -= t; +	} +	/* Process data in 64-byte chunks */ + +	while (len >= 64) { +		memcpy(ctx->in, buf, 64); +		md5_transform(ctx->buf, (uint32_t *) ctx->in); +		buf += 64; +		len -= 64; +	} + +	/* Handle any remaining bytes of data. */ + +	memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern  + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void md5_finish(struct MD5Context *ctx, md5_byte_t digest[16]) +{ +	unsigned count; +	unsigned char *p; + +	/* Compute number of bytes mod 64 */ +	count = (ctx->bits[0] >> 3) & 0x3F; + +	/* Set the first char of padding to 0x80.  This is safe since there is +	   always at least one byte free */ +	p = ctx->in + count; +	*p++ = 0x80; + +	/* Bytes of padding needed to make 64 bytes */ +	count = 64 - 1 - count; + +	/* Pad out to 56 mod 64 */ +	if (count < 8) { +		/* Two lots of padding:  Pad the first block to 64 bytes */ +		memset(p, 0, count); +		md5_transform(ctx->buf, (uint32_t *) ctx->in); + +		/* Now fill the next block with 56 bytes */ +		memset(ctx->in, 0, 56); +	} else { +		/* Pad block to 56 bytes */ +		memset(p, 0, count - 8); +	} + +	/* Append length in bits and transform */ +	((uint32_t *) ctx->in)[14] = cvt32(ctx->bits[0]); +	((uint32_t *) ctx->in)[15] = cvt32(ctx->bits[1]); + +	md5_transform(ctx->buf, (uint32_t *) ctx->in); +	ctx->buf[0] = cvt32(ctx->buf[0]); +	ctx->buf[1] = cvt32(ctx->buf[1]); +	ctx->buf[2] = cvt32(ctx->buf[2]); +	ctx->buf[3] = cvt32(ctx->buf[3]); +	memcpy(digest, ctx->buf, 16); +	memset(ctx, 0, sizeof(ctx));	/* In case it's sensitive */ +} + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ +	( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data.  MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +static void md5_transform(uint32_t buf[4], uint32_t const in[16]) +{ +	register uint32_t a, b, c, d; + +	a = buf[0]; +	b = buf[1]; +	c = buf[2]; +	d = buf[3]; + +	MD5STEP(F1, a, b, c, d, cvt32(in[0]) + 0xd76aa478, 7); +	MD5STEP(F1, d, a, b, c, cvt32(in[1]) + 0xe8c7b756, 12); +	MD5STEP(F1, c, d, a, b, cvt32(in[2]) + 0x242070db, 17); +	MD5STEP(F1, b, c, d, a, cvt32(in[3]) + 0xc1bdceee, 22); +	MD5STEP(F1, a, b, c, d, cvt32(in[4]) + 0xf57c0faf, 7); +	MD5STEP(F1, d, a, b, c, cvt32(in[5]) + 0x4787c62a, 12); +	MD5STEP(F1, c, d, a, b, cvt32(in[6]) + 0xa8304613, 17); +	MD5STEP(F1, b, c, d, a, cvt32(in[7]) + 0xfd469501, 22); +	MD5STEP(F1, a, b, c, d, cvt32(in[8]) + 0x698098d8, 7); +	MD5STEP(F1, d, a, b, c, cvt32(in[9]) + 0x8b44f7af, 12); +	MD5STEP(F1, c, d, a, b, cvt32(in[10]) + 0xffff5bb1, 17); +	MD5STEP(F1, b, c, d, a, cvt32(in[11]) + 0x895cd7be, 22); +	MD5STEP(F1, a, b, c, d, cvt32(in[12]) + 0x6b901122, 7); +	MD5STEP(F1, d, a, b, c, cvt32(in[13]) + 0xfd987193, 12); +	MD5STEP(F1, c, d, a, b, cvt32(in[14]) + 0xa679438e, 17); +	MD5STEP(F1, b, c, d, a, cvt32(in[15]) + 0x49b40821, 22); + +	MD5STEP(F2, a, b, c, d, cvt32(in[1]) + 0xf61e2562, 5); +	MD5STEP(F2, d, a, b, c, cvt32(in[6]) + 0xc040b340, 9); +	MD5STEP(F2, c, d, a, b, cvt32(in[11]) + 0x265e5a51, 14); +	MD5STEP(F2, b, c, d, a, cvt32(in[0]) + 0xe9b6c7aa, 20); +	MD5STEP(F2, a, b, c, d, cvt32(in[5]) + 0xd62f105d, 5); +	MD5STEP(F2, d, a, b, c, cvt32(in[10]) + 0x02441453, 9); +	MD5STEP(F2, c, d, a, b, cvt32(in[15]) + 0xd8a1e681, 14); +	MD5STEP(F2, b, c, d, a, cvt32(in[4]) + 0xe7d3fbc8, 20); +	MD5STEP(F2, a, b, c, d, cvt32(in[9]) + 0x21e1cde6, 5); +	MD5STEP(F2, d, a, b, c, cvt32(in[14]) + 0xc33707d6, 9); +	MD5STEP(F2, c, d, a, b, cvt32(in[3]) + 0xf4d50d87, 14); +	MD5STEP(F2, b, c, d, a, cvt32(in[8]) + 0x455a14ed, 20); +	MD5STEP(F2, a, b, c, d, cvt32(in[13]) + 0xa9e3e905, 5); +	MD5STEP(F2, d, a, b, c, cvt32(in[2]) + 0xfcefa3f8, 9); +	MD5STEP(F2, c, d, a, b, cvt32(in[7]) + 0x676f02d9, 14); +	MD5STEP(F2, b, c, d, a, cvt32(in[12]) + 0x8d2a4c8a, 20); + +	MD5STEP(F3, a, b, c, d, cvt32(in[5]) + 0xfffa3942, 4); +	MD5STEP(F3, d, a, b, c, cvt32(in[8]) + 0x8771f681, 11); +	MD5STEP(F3, c, d, a, b, cvt32(in[11]) + 0x6d9d6122, 16); +	MD5STEP(F3, b, c, d, a, cvt32(in[14]) + 0xfde5380c, 23); +	MD5STEP(F3, a, b, c, d, cvt32(in[1]) + 0xa4beea44, 4); +	MD5STEP(F3, d, a, b, c, cvt32(in[4]) + 0x4bdecfa9, 11); +	MD5STEP(F3, c, d, a, b, cvt32(in[7]) + 0xf6bb4b60, 16); +	MD5STEP(F3, b, c, d, a, cvt32(in[10]) + 0xbebfbc70, 23); +	MD5STEP(F3, a, b, c, d, cvt32(in[13]) + 0x289b7ec6, 4); +	MD5STEP(F3, d, a, b, c, cvt32(in[0]) + 0xeaa127fa, 11); +	MD5STEP(F3, c, d, a, b, cvt32(in[3]) + 0xd4ef3085, 16); +	MD5STEP(F3, b, c, d, a, cvt32(in[6]) + 0x04881d05, 23); +	MD5STEP(F3, a, b, c, d, cvt32(in[9]) + 0xd9d4d039, 4); +	MD5STEP(F3, d, a, b, c, cvt32(in[12]) + 0xe6db99e5, 11); +	MD5STEP(F3, c, d, a, b, cvt32(in[15]) + 0x1fa27cf8, 16); +	MD5STEP(F3, b, c, d, a, cvt32(in[2]) + 0xc4ac5665, 23); + +	MD5STEP(F4, a, b, c, d, cvt32(in[0]) + 0xf4292244, 6); +	MD5STEP(F4, d, a, b, c, cvt32(in[7]) + 0x432aff97, 10); +	MD5STEP(F4, c, d, a, b, cvt32(in[14]) + 0xab9423a7, 15); +	MD5STEP(F4, b, c, d, a, cvt32(in[5]) + 0xfc93a039, 21); +	MD5STEP(F4, a, b, c, d, cvt32(in[12]) + 0x655b59c3, 6); +	MD5STEP(F4, d, a, b, c, cvt32(in[3]) + 0x8f0ccc92, 10); +	MD5STEP(F4, c, d, a, b, cvt32(in[10]) + 0xffeff47d, 15); +	MD5STEP(F4, b, c, d, a, cvt32(in[1]) + 0x85845dd1, 21); +	MD5STEP(F4, a, b, c, d, cvt32(in[8]) + 0x6fa87e4f, 6); +	MD5STEP(F4, d, a, b, c, cvt32(in[15]) + 0xfe2ce6e0, 10); +	MD5STEP(F4, c, d, a, b, cvt32(in[6]) + 0xa3014314, 15); +	MD5STEP(F4, b, c, d, a, cvt32(in[13]) + 0x4e0811a1, 21); +	MD5STEP(F4, a, b, c, d, cvt32(in[4]) + 0xf7537e82, 6); +	MD5STEP(F4, d, a, b, c, cvt32(in[11]) + 0xbd3af235, 10); +	MD5STEP(F4, c, d, a, b, cvt32(in[2]) + 0x2ad7d2bb, 15); +	MD5STEP(F4, b, c, d, a, cvt32(in[9]) + 0xeb86d391, 21); + +	buf[0] += a; +	buf[1] += b; +	buf[2] += c; +	buf[3] += d; +} diff --git a/lib/md5.h b/lib/md5.h new file mode 100644 index 00000000..094507b2 --- /dev/null +++ b/lib/md5.h @@ -0,0 +1,42 @@ +/* + * MD5 hashing code copied from Lepton's crack <http://usuarios.lycos.es/reinob/> + * + * Adapted to be API-compatible with the previous (GPL-incompatible) code. + */ + +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest.  This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +#ifndef _MD5_H +#define _MD5_H + +#include <sys/types.h> +#include <gmodule.h> +#include <stdint.h> + +typedef uint8_t md5_byte_t; +typedef struct MD5Context { +	uint32_t buf[4]; +	uint32_t bits[2]; +	unsigned char in[64]; +} md5_state_t; + +G_MODULE_EXPORT void md5_init(struct MD5Context *context); +G_MODULE_EXPORT void md5_append(struct MD5Context *context, const md5_byte_t *buf, unsigned int len); +G_MODULE_EXPORT void md5_finish(struct MD5Context *context, md5_byte_t digest[16]); + +#endif diff --git a/lib/misc.c b/lib/misc.c new file mode 100644 index 00000000..ccf208b5 --- /dev/null +++ b/lib/misc.c @@ -0,0 +1,639 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2002-2006 Wilmer van der Gaast and others                * +  \********************************************************************/ + +/* + * Various utility functions. Some are copied from Gaim to support the + * IM-modules, most are from BitlBee. + * + * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net> + *                          (and possibly other members of the Gaim team) + * Copyright 2002-2006 Wilmer van der Gaast <wilmer@gaast.net> + */ + +/* +  This program is free software; you can redistribute it and/or modify +  it under the terms of the GNU General Public License as published by +  the Free Software Foundation; either version 2 of the License, or +  (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License with +  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; +  if not, write to the Free Software Foundation, Inc., 59 Temple Place, +  Suite 330, Boston, MA  02111-1307  USA +*/ + +#define BITLBEE_CORE +#include "nogaim.h" +#include "base64.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <glib.h> +#include <time.h> + +#ifdef HAVE_RESOLV_A +#include <arpa/nameser.h> +#include <resolv.h> +#endif + +#include "ssl_client.h" + +void strip_linefeed(gchar *text) +{ +	int i, j; +	gchar *text2 = g_malloc(strlen(text) + 1); + +	for (i = 0, j = 0; text[i]; i++) +		if (text[i] != '\r') +			text2[j++] = text[i]; +	text2[j] = '\0'; + +	strcpy(text, text2); +	g_free(text2); +} + +char *normalize(const char *s) +{ +	static char buf[BUF_LEN]; +	char *t, *u; +	int x = 0; + +	g_return_val_if_fail((s != NULL), NULL); + +	u = t = g_strdup(s); + +	strcpy(t, s); +	g_strdown(t); + +	while (*t && (x < BUF_LEN - 1)) { +		if (*t != ' ') { +			buf[x] = *t; +			x++; +		} +		t++; +	} +	buf[x] = '\0'; +	g_free(u); +	return buf; +} + +time_t get_time(int year, int month, int day, int hour, int min, int sec) +{ +	struct tm tm; + +	memset(&tm, 0, sizeof(struct tm)); +	tm.tm_year = year - 1900; +	tm.tm_mon = month - 1; +	tm.tm_mday = day; +	tm.tm_hour = hour; +	tm.tm_min = min; +	tm.tm_sec = sec >= 0 ? sec : time(NULL) % 60; +	 +	return mktime(&tm); +} + +typedef struct htmlentity +{ +	char code[7]; +	char is[3]; +} htmlentity_t; + +static const htmlentity_t ent[] = +{ +	{ "lt",     "<" }, +	{ "gt",     ">" }, +	{ "amp",    "&" }, +	{ "quot",   "\"" }, +	{ "aacute", "á" }, +	{ "eacute", "é" }, +	{ "iacute", "é" }, +	{ "oacute", "ó" }, +	{ "uacute", "ú" }, +	{ "agrave", "à" }, +	{ "egrave", "è" }, +	{ "igrave", "ì" }, +	{ "ograve", "ò" }, +	{ "ugrave", "ù" }, +	{ "acirc",  "â" }, +	{ "ecirc",  "ê" }, +	{ "icirc",  "î" }, +	{ "ocirc",  "ô" }, +	{ "ucirc",  "û" }, +	{ "auml",   "ä" }, +	{ "euml",   "ë" }, +	{ "iuml",   "ï" }, +	{ "ouml",   "ö" }, +	{ "uuml",   "ü" }, +	{ "nbsp",   " " }, +	{ "",        ""  } +}; + +void strip_html( char *in ) +{ +	char *start = in; +	char *out = g_malloc( strlen( in ) + 1 ); +	char *s = out, *cs; +	int i, matched; +	 +	memset( out, 0, strlen( in ) + 1 ); +	 +	while( *in ) +	{ +		if( *in == '<' && ( isalpha( *(in+1) ) || *(in+1) == '/' ) ) +		{ +			/* If in points at a < and in+1 points at a letter or a slash, this is probably +			   a HTML-tag. Try to find a closing > and continue there. If the > can't be +			   found, assume that it wasn't a HTML-tag after all. */ +			 +			cs = in; +			 +			while( *in && *in != '>' ) +				in ++; +			 +			if( *in ) +			{ +				if( g_strncasecmp( cs+1, "br", 2) == 0 ) +					*(s++) = '\n'; +				in ++; +			} +			else +			{ +				in = cs; +				*(s++) = *(in++); +			} +		} +		else if( *in == '&' ) +		{ +			cs = ++in; +			while( *in && isalpha( *in ) ) +				in ++; +			 +			if( *in == ';' ) in ++; +			matched = 0; +			 +			for( i = 0; *ent[i].code; i ++ ) +				if( g_strncasecmp( ent[i].code, cs, strlen( ent[i].code ) ) == 0 ) +				{ +					int j; +					 +					for( j = 0; ent[i].is[j]; j ++ ) +						*(s++) = ent[i].is[j]; +					 +					matched = 1; +					break; +				} + +			/* None of the entities were matched, so return the string */ +			if( !matched ) +			{ +				in = cs - 1; +				*(s++) = *(in++); +			} +		} +		else +		{ +			*(s++) = *(in++); +		} +	} +	 +	strcpy( start, out ); +	g_free( out ); +} + +char *escape_html( const char *html ) +{ +	const char *c = html; +	GString *ret; +	char *str; +	 +	if( html == NULL ) +		return( NULL ); +	 +	ret = g_string_new( "" ); +	 +	while( *c ) +	{ +		switch( *c ) +		{ +			case '&': +				ret = g_string_append( ret, "&" ); +				break; +			case '<': +				ret = g_string_append( ret, "<" ); +				break; +			case '>': +				ret = g_string_append( ret, ">" ); +				break; +			case '"': +				ret = g_string_append( ret, """ ); +				break; +			default: +				ret = g_string_append_c( ret, *c ); +		} +		c ++; +	} +	 +	str = ret->str; +	g_string_free( ret, FALSE ); +	return( str ); +} + +/* Decode%20a%20file%20name						*/ +void http_decode( char *s ) +{ +	char *t; +	int i, j, k; +	 +	t = g_new( char, strlen( s ) + 1 ); +	 +	for( i = j = 0; s[i]; i ++, j ++ ) +	{ +		if( s[i] == '%' ) +		{ +			if( sscanf( s + i + 1, "%2x", &k ) ) +			{ +				t[j] = k; +				i += 2; +			} +			else +			{ +				*t = 0; +				break; +			} +		} +		else +		{ +			t[j] = s[i]; +		} +	} +	t[j] = 0; +	 +	strcpy( s, t ); +	g_free( t ); +} + +/* Warning: This one explodes the string. Worst-cases can make the string 3x its original size! */ +/* This fuction is safe, but make sure you call it safely as well! */ +void http_encode( char *s ) +{ +	char *t; +	int i, j; +	 +	t = g_strdup( s ); +	 +	for( i = j = 0; t[i]; i ++, j ++ ) +	{ +		/* if( t[i] <= ' ' || ((unsigned char *)t)[i] >= 128 || t[i] == '%' ) */ +		if( !isalnum( t[i] ) ) +		{ +			sprintf( s + j, "%%%02X", ((unsigned char*)t)[i] ); +			j += 2; +		} +		else +		{ +			s[j] = t[i]; +		} +	} +	s[j] = 0; +	 +	g_free( t ); +} + +/* Strip newlines from a string. Modifies the string passed to it. */  +char *strip_newlines( char *source ) +{ +	int i;	 + +	for( i = 0; source[i] != '\0'; i ++ ) +		if( source[i] == '\n' || source[i] == '\r' ) +			source[i] = ' '; +	 +	return source; +} + +/* Wrap an IPv4 address into IPv6 space. Not thread-safe... */ +char *ipv6_wrap( char *src ) +{ +	static char dst[64]; +	int i; +	 +	for( i = 0; src[i]; i ++ ) +		if( ( src[i] < '0' || src[i] > '9' ) && src[i] != '.' ) +			break; +	 +	/* Hmm, it's not even an IP... */ +	if( src[i] ) +		return src; +	 +	g_snprintf( dst, sizeof( dst ), "::ffff:%s", src ); +	 +	return dst; +} + +/* Unwrap an IPv4 address into IPv6 space. Thread-safe, because it's very simple. :-) */ +char *ipv6_unwrap( char *src ) +{ +	int i; +	 +	if( g_strncasecmp( src, "::ffff:", 7 ) != 0 ) +		return src; +	 +	for( i = 7; src[i]; i ++ ) +		if( ( src[i] < '0' || src[i] > '9' ) && src[i] != '.' ) +			break; +	 +	/* Hmm, it's not even an IP... */ +	if( src[i] ) +		return src; +	 +	return ( src + 7 ); +} + +/* Convert from one charset to another. +    +   from_cs, to_cs: Source and destination charsets +   src, dst: Source and destination strings +   size: Size if src. 0 == use strlen(). strlen() is not reliable for UNICODE/UTF16 strings though. +   maxbuf: Maximum number of bytes to write to dst +    +   Returns the number of bytes written to maxbuf or -1 on an error. +*/ +signed int do_iconv( char *from_cs, char *to_cs, char *src, char *dst, size_t size, size_t maxbuf ) +{ +	GIConv cd; +	size_t res; +	size_t inbytesleft, outbytesleft; +	char *inbuf = src; +	char *outbuf = dst; +	 +	cd = g_iconv_open( to_cs, from_cs ); +	if( cd == (GIConv) -1 ) +		return( -1 ); +	 +	inbytesleft = size ? size : strlen( src ); +	outbytesleft = maxbuf - 1; +	res = g_iconv( cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft ); +	*outbuf = '\0'; +	g_iconv_close( cd ); +	 +	if( res == (size_t) -1 ) +		return( -1 ); +	else +		return( outbuf - dst ); +} + +/* A pretty reliable random number generator. Tries to use the /dev/random +   devices first, and falls back to the random number generator from libc +   when it fails. Opens randomizer devices with O_NONBLOCK to make sure a +   lack of entropy won't halt BitlBee. */ +void random_bytes( unsigned char *buf, int count ) +{ +	static int use_dev = -1; +	 +	/* Actually this probing code isn't really necessary, is it? */ +	if( use_dev == -1 ) +	{ +		if( access( "/dev/random", R_OK ) == 0 || access( "/dev/urandom", R_OK ) == 0 ) +			use_dev = 1; +		else +		{ +			use_dev = 0; +			srand( ( getpid() << 16 ) ^ time( NULL ) ); +		} +	} +	 +	if( use_dev ) +	{ +		int fd; +		 +		/* At least on Linux, /dev/random can block if there's not +		   enough entropy. We really don't want that, so if it can't +		   give anything, use /dev/urandom instead. */ +		if( ( fd = open( "/dev/random", O_RDONLY | O_NONBLOCK ) ) >= 0 ) +			if( read( fd, buf, count ) == count ) +			{ +				close( fd ); +				return; +			} +		close( fd ); +		 +		/* urandom isn't supposed to block at all, but just to be +		   sure. If it blocks, we'll disable use_dev and use the libc +		   randomizer instead. */ +		if( ( fd = open( "/dev/urandom", O_RDONLY | O_NONBLOCK ) ) >= 0 ) +			if( read( fd, buf, count ) == count ) +			{ +				close( fd ); +				return; +			} +		close( fd ); +		 +		/* If /dev/random blocks once, we'll still try to use it +		   again next time. If /dev/urandom also fails for some +		   reason, stick with libc during this session. */ +		 +		use_dev = 0; +		srand( ( getpid() << 16 ) ^ time( NULL ) ); +	} +	 +	if( !use_dev ) +	{ +		int i; +		 +		/* Possibly the LSB of rand() isn't very random on some +		   platforms. Seems okay on at least Linux and OSX though. */ +		for( i = 0; i < count; i ++ ) +			buf[i] = rand() & 0xff; +	} +} + +int is_bool( char *value ) +{ +	if( *value == 0 ) +		return 0; +	 +	if( ( g_strcasecmp( value, "true" ) == 0 ) || ( g_strcasecmp( value, "yes" ) == 0 ) || ( g_strcasecmp( value, "on" ) == 0 ) ) +		return 1; +	if( ( g_strcasecmp( value, "false" ) == 0 ) || ( g_strcasecmp( value, "no" ) == 0 ) || ( g_strcasecmp( value, "off" ) == 0 ) ) +		return 1; +	 +	while( *value ) +		if( !isdigit( *value ) ) +			return 0; +		else +			value ++; +	 +	return 1; +} + +int bool2int( char *value ) +{ +	int i; +	 +	if( ( g_strcasecmp( value, "true" ) == 0 ) || ( g_strcasecmp( value, "yes" ) == 0 ) || ( g_strcasecmp( value, "on" ) == 0 ) ) +		return 1; +	if( ( g_strcasecmp( value, "false" ) == 0 ) || ( g_strcasecmp( value, "no" ) == 0 ) || ( g_strcasecmp( value, "off" ) == 0 ) ) +		return 0; +	 +	if( sscanf( value, "%d", &i ) == 1 ) +		return i; +	 +	return 0; +} + +struct ns_srv_reply *srv_lookup( char *service, char *protocol, char *domain ) +{	 +	struct ns_srv_reply *reply = NULL; +#ifdef HAVE_RESOLV_A +	char name[1024]; +	unsigned char querybuf[1024]; +	const unsigned char *buf; +	ns_msg nsh; +	ns_rr rr; +	int i, len, size; +	 +	g_snprintf( name, sizeof( name ), "_%s._%s.%s", service, protocol, domain ); +	 +	if( ( size = res_query( name, ns_c_in, ns_t_srv, querybuf, sizeof( querybuf ) ) ) <= 0 ) +		return NULL; +	 +	if( ns_initparse( querybuf, size, &nsh ) != 0 ) +		return NULL; +	 +	if( ns_parserr( &nsh, ns_s_an, 0, &rr ) != 0 ) +		return NULL; +	 +	size = ns_rr_rdlen( rr ); +	buf = ns_rr_rdata( rr ); +	 +	len = 0; +	for( i = 6; i < size && buf[i]; i += buf[i] + 1 ) +		len += buf[i] + 1; +	 +	if( i > size ) +		return NULL; +	 +	reply = g_malloc( sizeof( struct ns_srv_reply ) + len ); +	memcpy( reply->name, buf + 7, len ); +	 +	for( i = buf[6]; i < len && buf[7+i]; i += buf[7+i] + 1 ) +		reply->name[i] = '.'; +	 +	if( i > len ) +	{ +		g_free( reply ); +		return NULL; +	} +	 +	reply->prio = ( buf[0] << 8 ) | buf[1]; +	reply->weight = ( buf[2] << 8 ) | buf[3]; +	reply->port = ( buf[4] << 8 ) | buf[5]; +#endif +	 +	return reply; +} + +/* Word wrapping. Yes, I know this isn't UTF-8 clean. I'm willing to take the risk. */ +char *word_wrap( char *msg, int line_len ) +{ +	GString *ret = g_string_sized_new( strlen( msg ) + 16 ); +	 +	while( strlen( msg ) > line_len ) +	{ +		int i; +		 +		/* First try to find out if there's a newline already. Don't +		   want to add more splits than necessary. */ +		for( i = line_len; i > 0 && msg[i] != '\n'; i -- ); +		if( msg[i] == '\n' ) +		{ +			g_string_append_len( ret, msg, i + 1 ); +			msg += i + 1; +			continue; +		} +		 +		for( i = line_len; i > 0; i -- ) +		{ +			if( msg[i] == '-' ) +			{ +				g_string_append_len( ret, msg, i + 1 ); +				g_string_append_c( ret, '\n' ); +				msg += i + 1; +				break; +			} +			else if( msg[i] == ' ' ) +			{ +				g_string_append_len( ret, msg, i ); +				g_string_append_c( ret, '\n' ); +				msg += i + 1; +				break; +			} +		} +		if( i == 0 ) +		{ +			g_string_append_len( ret, msg, line_len ); +			g_string_append_c( ret, '\n' ); +			msg += line_len; +		} +	} +	g_string_append( ret, msg ); +	 +	return g_string_free( ret, FALSE ); +} + +gboolean ssl_sockerr_again( void *ssl ) +{ +	if( ssl ) +		return ssl_errno == SSL_AGAIN; +	else +		return sockerr_again(); +} + +/* Returns values: -1 == Failure (base64-decoded to something unexpected) +                    0 == Okay +                    1 == Password doesn't match the hash. */ +int md5_verify_password( char *password, char *hash ) +{ +	md5_byte_t *pass_dec = NULL; +	md5_byte_t pass_md5[16]; +	md5_state_t md5_state; +	int ret, i; +	 +	if( base64_decode( hash, &pass_dec ) != 21 ) +	{ +		ret = -1; +	} +	else +	{ +		md5_init( &md5_state ); +		md5_append( &md5_state, (md5_byte_t*) password, strlen( password ) ); +		md5_append( &md5_state, (md5_byte_t*) pass_dec + 16, 5 ); /* Hmmm, salt! */ +		md5_finish( &md5_state, pass_md5 ); +		 +		for( i = 0; i < 16; i ++ ) +		{ +			if( pass_dec[i] != pass_md5[i] ) +			{ +				ret = 1; +				break; +			} +		} +		 +		/* If we reached the end of the loop, it was a match! */ +		if( i == 16 ) +			ret = 0; +	} +	 +	g_free( pass_dec ); + +	return ret; +} diff --git a/lib/misc.h b/lib/misc.h new file mode 100644 index 00000000..a2acada6 --- /dev/null +++ b/lib/misc.h @@ -0,0 +1,71 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2002-2004 Wilmer van der Gaast and others                * +  \********************************************************************/ + +/* Misc. functions                                                      */ + +/* +  This program is free software; you can redistribute it and/or modify +  it under the terms of the GNU General Public License as published by +  the Free Software Foundation; either version 2 of the License, or +  (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License with +  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; +  if not, write to the Free Software Foundation, Inc., 59 Temple Place, +  Suite 330, Boston, MA  02111-1307  USA +*/ + +#ifndef _MISC_H +#define _MISC_H + +#include <gmodule.h> +#include <time.h> + +struct ns_srv_reply +{ +	int prio; +	int weight; +	int port; +	char name[]; +}; + +G_MODULE_EXPORT void strip_linefeed( gchar *text ); +G_MODULE_EXPORT char *add_cr( char *text ); +G_MODULE_EXPORT char *strip_newlines(char *source); +G_MODULE_EXPORT char *normalize( const char *s ); + +G_MODULE_EXPORT time_t get_time( int year, int month, int day, int hour, int min, int sec ); +double gettime( void ); + +G_MODULE_EXPORT void strip_html( char *msg ); +G_MODULE_EXPORT char *escape_html( const char *html ); +G_MODULE_EXPORT void http_decode( char *s ); +G_MODULE_EXPORT void http_encode( char *s ); + +G_MODULE_EXPORT char *ipv6_wrap( char *src ); +G_MODULE_EXPORT char *ipv6_unwrap( char *src ); + +G_MODULE_EXPORT signed int do_iconv( char *from_cs, char *to_cs, char *src, char *dst, size_t size, size_t maxbuf ); + +G_MODULE_EXPORT void random_bytes( unsigned char *buf, int count ); + +G_MODULE_EXPORT int is_bool( char *value ); +G_MODULE_EXPORT int bool2int( char *value ); + +G_MODULE_EXPORT struct ns_srv_reply *srv_lookup( char *service, char *protocol, char *domain ); + +G_MODULE_EXPORT char *word_wrap( char *msg, int line_len ); + +G_MODULE_EXPORT gboolean ssl_sockerr_again( void *ssl ); + +G_MODULE_EXPORT int md5_verify_password( char *password, char *hash ); + +#endif diff --git a/lib/proxy.c b/lib/proxy.c new file mode 100644 index 00000000..53b89d64 --- /dev/null +++ b/lib/proxy.c @@ -0,0 +1,552 @@ +/* + * gaim + * + * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net> + * Copyright (C) 2002-2004, Wilmer van der Gaast, Jelmer Vernooij + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * + */ + +#define BITLBEE_CORE +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#ifndef _WIN32 +#include <sys/socket.h> +#include <netdb.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <unistd.h> +#else +#include "sock.h" +#define ETIMEDOUT WSAETIMEDOUT +#define EINPROGRESS WSAEINPROGRESS +#endif +#include <fcntl.h> +#include <errno.h> +#include "nogaim.h" +#include "proxy.h" +#include "base64.h" + +char proxyhost[128] = ""; +int proxyport = 0; +int proxytype = PROXY_NONE; +char proxyuser[128] = ""; +char proxypass[128] = ""; + +struct PHB { +	b_event_handler func, proxy_func; +	gpointer data, proxy_data; +	char *host; +	int port; +	int fd; +	gint inpa; +}; + + + +static struct sockaddr_in *gaim_gethostbyname(const char *host, int port) +{ +	static struct sockaddr_in sin; + +	if (!inet_aton(host, &sin.sin_addr)) { +		struct hostent *hp; +		if (!(hp = gethostbyname(host))) { +			return NULL; +		} +		memset(&sin, 0, sizeof(struct sockaddr_in)); +		memcpy(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length); +		sin.sin_family = hp->h_addrtype; +	} else +		sin.sin_family = AF_INET; +	sin.sin_port = htons(port); + +	return &sin; +} + +static gboolean gaim_io_connected(gpointer data, gint source, b_input_condition cond) +{ +	struct PHB *phb = data; +	unsigned int len; +	int error = ETIMEDOUT; +	len = sizeof(error); +	 +#ifndef _WIN32 +	if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { +		closesocket(source); +		b_event_remove(phb->inpa); +		if( phb->proxy_func ) +			phb->proxy_func(phb->proxy_data, -1, GAIM_INPUT_READ); +		else { +			phb->func(phb->data, -1, GAIM_INPUT_READ); +			g_free(phb); +		} +		return FALSE; +	} +#endif +	sock_make_blocking(source); +	b_event_remove(phb->inpa); +	if( phb->proxy_func ) +		phb->proxy_func(phb->proxy_data, source, GAIM_INPUT_READ); +	else { +		phb->func(phb->data, source, GAIM_INPUT_READ); +		g_free(phb); +	} +	 +	return FALSE; +} + +static int proxy_connect_none(const char *host, unsigned short port, struct PHB *phb) +{ +	struct sockaddr_in *sin; +	int fd = -1; + +	if (!(sin = gaim_gethostbyname(host, port))) { +		g_free(phb); +		return -1; +	} + +	if ((fd = socket(sin->sin_family, SOCK_STREAM, 0)) < 0) { +		g_free(phb); +		return -1; +	} + +	sock_make_nonblocking(fd); +	 +	event_debug("proxy_connect_none( \"%s\", %d ) = %d\n", host, port, fd); +	 +	if (connect(fd, (struct sockaddr *)sin, sizeof(*sin)) < 0 && !sockerr_again()) { +		closesocket(fd); +		g_free(phb); +		 +		return -1; +	} else { +		phb->inpa = b_input_add(fd, GAIM_INPUT_WRITE, gaim_io_connected, phb); +		phb->fd = fd; +		 +		return fd; +	} +} + + +/* Connecting to HTTP proxies */ + +#define HTTP_GOODSTRING "HTTP/1.0 200 Connection established" +#define HTTP_GOODSTRING2 "HTTP/1.1 200 Connection established" + +static gboolean http_canread(gpointer data, gint source, b_input_condition cond) +{ +	int nlc = 0; +	int pos = 0; +	struct PHB *phb = data; +	char inputline[8192]; + +	b_event_remove(phb->inpa); + +	while ((pos < sizeof(inputline)-1) && (nlc != 2) && (read(source, &inputline[pos++], 1) == 1)) { +		if (inputline[pos - 1] == '\n') +			nlc++; +		else if (inputline[pos - 1] != '\r') +			nlc = 0; +	} +	inputline[pos] = '\0'; + +	if ((memcmp(HTTP_GOODSTRING, inputline, strlen(HTTP_GOODSTRING)) == 0) || +	    (memcmp(HTTP_GOODSTRING2, inputline, strlen(HTTP_GOODSTRING2)) == 0)) { +		phb->func(phb->data, source, GAIM_INPUT_READ); +		g_free(phb->host); +		g_free(phb); +		return FALSE; +	} + +	close(source); +	phb->func(phb->data, -1, GAIM_INPUT_READ); +	g_free(phb->host); +	g_free(phb); +	 +	return FALSE; +} + +static gboolean http_canwrite(gpointer data, gint source, b_input_condition cond) +{ +	char cmd[384]; +	struct PHB *phb = data; +	unsigned int len; +	int error = ETIMEDOUT; +	if (phb->inpa > 0) +		b_event_remove(phb->inpa); +	len = sizeof(error); +	if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { +		close(source); +		phb->func(phb->data, -1, GAIM_INPUT_READ); +		g_free(phb->host); +		g_free(phb); +		return FALSE; +	} +	sock_make_blocking(source); + +	g_snprintf(cmd, sizeof(cmd), "CONNECT %s:%d HTTP/1.1\r\nHost: %s:%d\r\n", phb->host, phb->port, +		   phb->host, phb->port); +	if (send(source, cmd, strlen(cmd), 0) < 0) { +		close(source); +		phb->func(phb->data, -1, GAIM_INPUT_READ); +		g_free(phb->host); +		g_free(phb); +		return FALSE; +	} + +	if (strlen(proxyuser) > 0) { +		char *t1, *t2; +		t1 = g_strdup_printf("%s:%s", proxyuser, proxypass); +		t2 = tobase64(t1); +		g_free(t1); +		g_snprintf(cmd, sizeof(cmd), "Proxy-Authorization: Basic %s\r\n", t2); +		g_free(t2); +		if (send(source, cmd, strlen(cmd), 0) < 0) { +			close(source); +			phb->func(phb->data, -1, GAIM_INPUT_READ); +			g_free(phb->host); +			g_free(phb); +			return FALSE; +		} +	} + +	g_snprintf(cmd, sizeof(cmd), "\r\n"); +	if (send(source, cmd, strlen(cmd), 0) < 0) { +		close(source); +		phb->func(phb->data, -1, GAIM_INPUT_READ); +		g_free(phb->host); +		g_free(phb); +		return FALSE; +	} + +	phb->inpa = b_input_add(source, GAIM_INPUT_READ, http_canread, phb); +	 +	return FALSE; +} + +static int proxy_connect_http(const char *host, unsigned short port, struct PHB *phb) +{ +	phb->host = g_strdup(host); +	phb->port = port; +	phb->proxy_func = http_canwrite; +	phb->proxy_data = phb; +	 +	return( proxy_connect_none( proxyhost, proxyport, phb ) ); +} + + +/* Connecting to SOCKS4 proxies */ + +static gboolean s4_canread(gpointer data, gint source, b_input_condition cond) +{ +	unsigned char packet[12]; +	struct PHB *phb = data; + +	b_event_remove(phb->inpa); + +	memset(packet, 0, sizeof(packet)); +	if (read(source, packet, 9) >= 4 && packet[1] == 90) { +		phb->func(phb->data, source, GAIM_INPUT_READ); +		g_free(phb->host); +		g_free(phb); +		return FALSE; +	} + +	close(source); +	phb->func(phb->data, -1, GAIM_INPUT_READ); +	g_free(phb->host); +	g_free(phb); +	 +	return FALSE; +} + +static gboolean s4_canwrite(gpointer data, gint source, b_input_condition cond) +{ +	unsigned char packet[12]; +	struct hostent *hp; +	struct PHB *phb = data; +	unsigned int len; +	int error = ETIMEDOUT; +	if (phb->inpa > 0) +		b_event_remove(phb->inpa); +	len = sizeof(error); +	if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { +		close(source); +		phb->func(phb->data, -1, GAIM_INPUT_READ); +		g_free(phb->host); +		g_free(phb); +		return FALSE; +	} +	sock_make_blocking(source); + +	/* XXX does socks4 not support host name lookups by the proxy? */ +	if (!(hp = gethostbyname(phb->host))) { +		close(source); +		phb->func(phb->data, -1, GAIM_INPUT_READ); +		g_free(phb->host); +		g_free(phb); +		return FALSE; +	} + +	packet[0] = 4; +	packet[1] = 1; +	packet[2] = phb->port >> 8; +	packet[3] = phb->port & 0xff; +	packet[4] = (unsigned char)(hp->h_addr_list[0])[0]; +	packet[5] = (unsigned char)(hp->h_addr_list[0])[1]; +	packet[6] = (unsigned char)(hp->h_addr_list[0])[2]; +	packet[7] = (unsigned char)(hp->h_addr_list[0])[3]; +	packet[8] = 0; +	if (write(source, packet, 9) != 9) { +		close(source); +		phb->func(phb->data, -1, GAIM_INPUT_READ); +		g_free(phb->host); +		g_free(phb); +		return FALSE; +	} + +	phb->inpa = b_input_add(source, GAIM_INPUT_READ, s4_canread, phb); +	 +	return FALSE; +} + +static int proxy_connect_socks4(const char *host, unsigned short port, struct PHB *phb) +{ +	phb->host = g_strdup(host); +	phb->port = port; +	phb->proxy_func = s4_canwrite; +	phb->proxy_data = phb; +	 +	return( proxy_connect_none( proxyhost, proxyport, phb ) ); +} + + +/* Connecting to SOCKS5 proxies */ + +static gboolean s5_canread_again(gpointer data, gint source, b_input_condition cond) +{ +	unsigned char buf[512]; +	struct PHB *phb = data; + +	b_event_remove(phb->inpa); + +	if (read(source, buf, 10) < 10) { +		close(source); +		phb->func(phb->data, -1, GAIM_INPUT_READ); +		g_free(phb->host); +		g_free(phb); +		return FALSE; +	} +	if ((buf[0] != 0x05) || (buf[1] != 0x00)) { +		close(source); +		phb->func(phb->data, -1, GAIM_INPUT_READ); +		g_free(phb->host); +		g_free(phb); +		return FALSE; +	} + +	phb->func(phb->data, source, GAIM_INPUT_READ); +	g_free(phb->host); +	g_free(phb); +	 +	return FALSE; +} + +static void s5_sendconnect(gpointer data, gint source) +{ +	unsigned char buf[512]; +	struct PHB *phb = data; +	int hlen = strlen(phb->host); +	 +	buf[0] = 0x05; +	buf[1] = 0x01;		/* CONNECT */ +	buf[2] = 0x00;		/* reserved */ +	buf[3] = 0x03;		/* address type -- host name */ +	buf[4] = hlen; +	memcpy(buf + 5, phb->host, hlen); +	buf[5 + strlen(phb->host)] = phb->port >> 8; +	buf[5 + strlen(phb->host) + 1] = phb->port & 0xff; + +	if (write(source, buf, (5 + strlen(phb->host) + 2)) < (5 + strlen(phb->host) + 2)) { +		close(source); +		phb->func(phb->data, -1, GAIM_INPUT_READ); +		g_free(phb->host); +		g_free(phb); +		return; +	} + +	phb->inpa = b_input_add(source, GAIM_INPUT_READ, s5_canread_again, phb); +} + +static gboolean s5_readauth(gpointer data, gint source, b_input_condition cond) +{ +	unsigned char buf[512]; +	struct PHB *phb = data; + +	b_event_remove(phb->inpa); + +	if (read(source, buf, 2) < 2) { +		close(source); +		phb->func(phb->data, -1, GAIM_INPUT_READ); +		g_free(phb->host); +		g_free(phb); +		return FALSE; +	} + +	if ((buf[0] != 0x01) || (buf[1] != 0x00)) { +		close(source); +		phb->func(phb->data, -1, GAIM_INPUT_READ); +		g_free(phb->host); +		g_free(phb); +		return FALSE; +	} + +	s5_sendconnect(phb, source); +	 +	return FALSE; +} + +static gboolean s5_canread(gpointer data, gint source, b_input_condition cond) +{ +	unsigned char buf[512]; +	struct PHB *phb = data; + +	b_event_remove(phb->inpa); + +	if (read(source, buf, 2) < 2) { +		close(source); +		phb->func(phb->data, -1, GAIM_INPUT_READ); +		g_free(phb->host); +		g_free(phb); +		return FALSE; +	} + +	if ((buf[0] != 0x05) || (buf[1] == 0xff)) { +		close(source); +		phb->func(phb->data, -1, GAIM_INPUT_READ); +		g_free(phb->host); +		g_free(phb); +		return FALSE; +	} + +	if (buf[1] == 0x02) { +		unsigned int i = strlen(proxyuser), j = strlen(proxypass); +		buf[0] = 0x01;	/* version 1 */ +		buf[1] = i; +		memcpy(buf + 2, proxyuser, i); +		buf[2 + i] = j; +		memcpy(buf + 2 + i + 1, proxypass, j); +		if (write(source, buf, 3 + i + j) < 3 + i + j) { +			close(source); +			phb->func(phb->data, -1, GAIM_INPUT_READ); +			g_free(phb->host); +			g_free(phb); +			return FALSE; +		} + +		phb->inpa = b_input_add(source, GAIM_INPUT_READ, s5_readauth, phb); +	} else { +		s5_sendconnect(phb, source); +	} +	 +	return FALSE; +} + +static gboolean s5_canwrite(gpointer data, gint source, b_input_condition cond) +{ +	unsigned char buf[512]; +	int i; +	struct PHB *phb = data; +	unsigned int len; +	int error = ETIMEDOUT; +	if (phb->inpa > 0) +		b_event_remove(phb->inpa); +	len = sizeof(error); +	if (getsockopt(source, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { +		close(source); +		phb->func(phb->data, -1, GAIM_INPUT_READ); +		g_free(phb->host); +		g_free(phb); +		return FALSE; +	} +	sock_make_blocking(source); + +	i = 0; +	buf[0] = 0x05;		/* SOCKS version 5 */ +	if (proxyuser[0]) { +		buf[1] = 0x02;	/* two methods */ +		buf[2] = 0x00;	/* no authentication */ +		buf[3] = 0x02;	/* username/password authentication */ +		i = 4; +	} else { +		buf[1] = 0x01; +		buf[2] = 0x00; +		i = 3; +	} + +	if (write(source, buf, i) < i) { +		close(source); +		phb->func(phb->data, -1, GAIM_INPUT_READ); +		g_free(phb->host); +		g_free(phb); +		return FALSE; +	} + +	phb->inpa = b_input_add(source, GAIM_INPUT_READ, s5_canread, phb); +	 +	return FALSE; +} + +static int proxy_connect_socks5(const char *host, unsigned short port, struct PHB *phb) +{ +	phb->host = g_strdup(host); +	phb->port = port; +	phb->proxy_func = s5_canwrite; +	phb->proxy_data = phb; +	 +	return( proxy_connect_none( proxyhost, proxyport, phb ) ); +} + + +/* Export functions */ + +int proxy_connect(const char *host, int port, b_event_handler func, gpointer data) +{ +	struct PHB *phb; +	 +	if (!host || port <= 0 || !func || strlen(host) > 128) { +		return -1; +	} +	 +	phb = g_new0(struct PHB, 1); +	phb->func = func; +	phb->data = data; +	 +	if (proxytype == PROXY_NONE || !proxyhost[0] || proxyport <= 0) +		return proxy_connect_none(host, port, phb); +	else if (proxytype == PROXY_HTTP) +		return proxy_connect_http(host, port, phb); +	else if (proxytype == PROXY_SOCKS4) +		return proxy_connect_socks4(host, port, phb); +	else if (proxytype == PROXY_SOCKS5) +		return proxy_connect_socks5(host, port, phb); +	 +	if (phb->host) g_free(phb); +	g_free(phb); +	return -1; +} diff --git a/lib/proxy.h b/lib/proxy.h new file mode 100644 index 00000000..680790a5 --- /dev/null +++ b/lib/proxy.h @@ -0,0 +1,53 @@ +/* + * nogaim + * + * Copyright (C) 1998-1999, Mark Spencer <markster@marko.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * + */ + +/* this is the export part of the proxy.c file. it does a little +   prototype-ing stuff and redefine some net function to mask them +   with some kind of transparent layer */  + +#ifndef _PROXY_H_ +#define _PROXY_H_ + +#include <sys/types.h> +#ifndef _WIN32 +#include <sys/socket.h> +#include <netdb.h> +#include <netinet/in.h> +#endif +#include <glib.h> +#include <gmodule.h> + +#include "events.h" + +#define PROXY_NONE 0 +#define PROXY_HTTP 1 +#define PROXY_SOCKS4 2 +#define PROXY_SOCKS5 3 + +extern char proxyhost[128]; +extern int  proxyport; +extern int  proxytype; +extern char proxyuser[128]; +extern char proxypass[128]; + +G_MODULE_EXPORT int proxy_connect(const char *host, int port, b_event_handler func, gpointer data); + +#endif /* _PROXY_H_ */ diff --git a/lib/sha1.c b/lib/sha1.c new file mode 100644 index 00000000..ee4fcc19 --- /dev/null +++ b/lib/sha1.c @@ -0,0 +1,375 @@ +/* + * SHA1 hashing code copied from Lepton's crack <http://usuarios.lycos.es/reinob/> + * + * Adapted to be API-compatible with the previous (GPL-incompatible) code. + */ + +/* + *  sha1.c + * + *  Description: + *      This file implements the Secure Hashing Algorithm 1 as + *      defined in FIPS PUB 180-1 published April 17, 1995. + * + *      The SHA-1, produces a 160-bit message digest for a given + *      data stream.  It should take about 2**n steps to find a + *      message with the same digest as a given message and + *      2**(n/2) to find any two messages with the same digest, + *      when n is the digest size in bits.  Therefore, this + *      algorithm can serve as a means of providing a + *      "fingerprint" for a message. + * + *  Portability Issues: + *      SHA-1 is defined in terms of 32-bit "words".  This code + *      uses <stdint.h> (included via "sha1.h" to define 32 and 8 + *      bit unsigned integer types.  If your C compiler does not + *      support 32 bit unsigned integers, this code is not + *      appropriate. + * + *  Caveats: + *      SHA-1 is designed to work with messages less than 2^64 bits + *      long.  Although SHA-1 allows a message digest to be generated + *      for messages of any number of bits less than 2^64, this + *      implementation only works with messages with a length that is + *      a multiple of the size of an 8-bit character. + * + */ + +#include "sha1.h" + +/* + *  Define the SHA1 circular left shift macro + */ +#define SHA1CircularShift(bits,word) \ +       (((word) << (bits)) | ((word) >> (32-(bits)))) + +/* Local Function Prototyptes */ +static void sha1_pad(sha1_state_t *); +static void sha1_process_block(sha1_state_t *); + +/* + *  sha1_init + * + *  Description: + *      This function will initialize the sha1_state_t in preparation + *      for computing a new SHA1 message digest. + * + *  Parameters: + *      context: [in/out] + *          The context to reset. + * + *  Returns: + *      sha Error Code. + * + */ +int sha1_init(sha1_state_t * context) +{ +	context->Length_Low = 0; +	context->Length_High = 0; +	context->Message_Block_Index = 0; + +	context->Intermediate_Hash[0] = 0x67452301; +	context->Intermediate_Hash[1] = 0xEFCDAB89; +	context->Intermediate_Hash[2] = 0x98BADCFE; +	context->Intermediate_Hash[3] = 0x10325476; +	context->Intermediate_Hash[4] = 0xC3D2E1F0; + +	context->Computed = 0; +	context->Corrupted = 0; +	 +	return shaSuccess; +} + +/* + *  sha1_finish + * + *  Description: + *      This function will return the 160-bit message digest into the + *      Message_Digest array  provided by the caller. + *      NOTE: The first octet of hash is stored in the 0th element, + *            the last octet of hash in the 19th element. + * + *  Parameters: + *      context: [in/out] + *          The context to use to calculate the SHA-1 hash. + *      Message_Digest: [out] + *          Where the digest is returned. + * + *  Returns: + *      sha Error Code. + * + */ +int sha1_finish(sha1_state_t * context, uint8_t Message_Digest[sha1_hash_size]) +{ +	int i; + +	if (!context || !Message_Digest) { +		return shaNull; +	} + +	if (context->Corrupted) { +		return context->Corrupted; +	} + +	if (!context->Computed) { +		sha1_pad(context); +		for (i = 0; i < 64; ++i) { +			/* message may be sensitive, clear it out */ +			context->Message_Block[i] = 0; +		} +		context->Length_Low = 0;	/* and clear length */ +		context->Length_High = 0; +		context->Computed = 1; + +	} + +	for (i = 0; i < sha1_hash_size; ++i) { +		Message_Digest[i] = context->Intermediate_Hash[i >> 2] +		    >> 8 * (3 - (i & 0x03)); +	} + +	return shaSuccess; +} + +/* + *  sha1_append + * + *  Description: + *      This function accepts an array of octets as the next portion + *      of the message. + * + *  Parameters: + *      context: [in/out] + *          The SHA context to update + *      message_array: [in] + *          An array of characters representing the next portion of + *          the message. + *      length: [in] + *          The length of the message in message_array + * + *  Returns: + *      sha Error Code. + * + */ +int +sha1_append(sha1_state_t * context, +	  const uint8_t * message_array, unsigned length) +{ +	if (!length) { +		return shaSuccess; +	} + +	if (!context || !message_array) { +		return shaNull; +	} + +	if (context->Computed) { +		context->Corrupted = shaStateError; + +		return shaStateError; +	} + +	if (context->Corrupted) { +		return context->Corrupted; +	} +	while (length-- && !context->Corrupted) { +		context->Message_Block[context->Message_Block_Index++] = +		    (*message_array & 0xFF); + +		context->Length_Low += 8; +		if (context->Length_Low == 0) { +			context->Length_High++; +			if (context->Length_High == 0) { +				/* Message is too long */ +				context->Corrupted = 1; +			} +		} + +		if (context->Message_Block_Index == 64) { +			sha1_process_block(context); +		} + +		message_array++; +	} + +	return shaSuccess; +} + +/* + *  sha1_process_block + * + *  Description: + *      This function will process the next 512 bits of the message + *      stored in the Message_Block array. + * + *  Parameters: + *      None. + * + *  Returns: + *      Nothing. + * + *  Comments: + *      Many of the variable names in this code, especially the + *      single character names, were used because those were the + *      names used in the publication. + * + * + */ +static void sha1_process_block(sha1_state_t * context) +{ +	const uint32_t K[] = {	/* Constants defined in SHA-1   */ +		0x5A827999, +		0x6ED9EBA1, +		0x8F1BBCDC, +		0xCA62C1D6 +	}; +	int t;			/* Loop counter                */ +	uint32_t temp;		/* Temporary word value        */ +	uint32_t W[80];		/* Word sequence               */ +	uint32_t A, B, C, D, E;	/* Word buffers                */ + +	/* +	 *  Initialize the first 16 words in the array W +	 */ +	for (t = 0; t < 16; t++) { +		W[t] = context->Message_Block[t * 4] << 24; +		W[t] |= context->Message_Block[t * 4 + 1] << 16; +		W[t] |= context->Message_Block[t * 4 + 2] << 8; +		W[t] |= context->Message_Block[t * 4 + 3]; +	} + +	for (t = 16; t < 80; t++) { +		W[t] = +		    SHA1CircularShift(1, +				      W[t - 3] ^ W[t - 8] ^ W[t - +							      14] ^ W[t - +								      16]); +	} + +	A = context->Intermediate_Hash[0]; +	B = context->Intermediate_Hash[1]; +	C = context->Intermediate_Hash[2]; +	D = context->Intermediate_Hash[3]; +	E = context->Intermediate_Hash[4]; + +	for (t = 0; t < 20; t++) { +		temp = SHA1CircularShift(5, A) + +		    ((B & C) | ((~B) & D)) + E + W[t] + K[0]; +		E = D; +		D = C; +		C = SHA1CircularShift(30, B); + +		B = A; +		A = temp; +	} + +	for (t = 20; t < 40; t++) { +		temp = +		    SHA1CircularShift(5, +				      A) + (B ^ C ^ D) + E + W[t] + K[1]; +		E = D; +		D = C; +		C = SHA1CircularShift(30, B); +		B = A; +		A = temp; +	} + +	for (t = 40; t < 60; t++) { +		temp = SHA1CircularShift(5, A) + +		    ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2]; +		E = D; +		D = C; +		C = SHA1CircularShift(30, B); +		B = A; +		A = temp; +	} + +	for (t = 60; t < 80; t++) { +		temp = +		    SHA1CircularShift(5, +				      A) + (B ^ C ^ D) + E + W[t] + K[3]; +		E = D; +		D = C; +		C = SHA1CircularShift(30, B); +		B = A; +		A = temp; +	} + +	context->Intermediate_Hash[0] += A; +	context->Intermediate_Hash[1] += B; +	context->Intermediate_Hash[2] += C; +	context->Intermediate_Hash[3] += D; +	context->Intermediate_Hash[4] += E; + +	context->Message_Block_Index = 0; +} + +/* + *  sha1_pad + * + *  Description: + *      According to the standard, the message must be padded to an even + *      512 bits.  The first padding bit must be a '1'.  The last 64 + *      bits represent the length of the original message.  All bits in + *      between should be 0.  This function will pad the message + *      according to those rules by filling the Message_Block array + *      accordingly.  It will also call the ProcessMessageBlock function + *      provided appropriately.  When it returns, it can be assumed that + *      the message digest has been computed. + * + *  Parameters: + *      context: [in/out] + *          The context to pad + *      ProcessMessageBlock: [in] + *          The appropriate SHA*ProcessMessageBlock function + *  Returns: + *      Nothing. + * + */ + +static void sha1_pad(sha1_state_t * context) +{ +	/* +	 *  Check to see if the current message block is too small to hold +	 *  the initial padding bits and length.  If so, we will pad the +	 *  block, process it, and then continue padding into a second +	 *  block. +	 */ +	if (context->Message_Block_Index > 55) { +		context->Message_Block[context->Message_Block_Index++] = +		    0x80; +		while (context->Message_Block_Index < 64) { +			context->Message_Block[context-> +					       Message_Block_Index++] = 0; +		} + +		sha1_process_block(context); + +		while (context->Message_Block_Index < 56) { +			context->Message_Block[context-> +					       Message_Block_Index++] = 0; +		} +	} else { +		context->Message_Block[context->Message_Block_Index++] = +		    0x80; +		while (context->Message_Block_Index < 56) { + +			context->Message_Block[context-> +					       Message_Block_Index++] = 0; +		} +	} + +	/* +	 *  Store the message length as the last 8 octets +	 */ +	context->Message_Block[56] = context->Length_High >> 24; +	context->Message_Block[57] = context->Length_High >> 16; +	context->Message_Block[58] = context->Length_High >> 8; +	context->Message_Block[59] = context->Length_High; +	context->Message_Block[60] = context->Length_Low >> 24; +	context->Message_Block[61] = context->Length_Low >> 16; +	context->Message_Block[62] = context->Length_Low >> 8; +	context->Message_Block[63] = context->Length_Low; + +	sha1_process_block(context); +} diff --git a/lib/sha1.h b/lib/sha1.h new file mode 100644 index 00000000..368c0669 --- /dev/null +++ b/lib/sha1.h @@ -0,0 +1,66 @@ +/* + * SHA1 hashing code copied from Lepton's crack <http://usuarios.lycos.es/reinob/> + * + * Adapted to be API-compatible with the previous (GPL-incompatible) code. + */ + +/* + *  sha1.h + * + *  Description: + *      This is the header file for code which implements the Secure + *      Hashing Algorithm 1 as defined in FIPS PUB 180-1 published + *      April 17, 1995. + * + *      Many of the variable names in this code, especially the + *      single character names, were used because those were the names + *      used in the publication. + * + *      Please read the file sha1.c for more information. + * + */ + +#ifndef _SHA1_H_ +#define _SHA1_H_ + +#include <stdint.h> +#include <gmodule.h> + +#ifndef _SHA_enum_ +#define _SHA_enum_ +enum { +	shaSuccess = 0, +	shaNull,		/* Null pointer parameter */ +	shaInputTooLong,	/* input data too long */ +	shaStateError		/* called Input after Result */ +}; +#endif +#define sha1_hash_size 20 + +/* + *  This structure will hold context information for the SHA-1 + *  hashing operation + */ +typedef struct SHA1Context { +	uint32_t Intermediate_Hash[sha1_hash_size/4];	/* Message Digest   */ + +	uint32_t Length_Low;            /* Message length in bits           */ +	uint32_t Length_High;           /* Message length in bits           */ + +	/* Index into message block array   */ +	int_least16_t Message_Block_Index; +	uint8_t Message_Block[64];	/* 512-bit message blocks           */ + +	int Computed;                   /* Is the digest computed?          */ +	int Corrupted;                  /* Is the message digest corrupted? */ +} sha1_state_t; + +/* + *  Function Prototypes + */ + +G_MODULE_EXPORT int sha1_init(sha1_state_t *); +G_MODULE_EXPORT int sha1_append(sha1_state_t *, const uint8_t *, unsigned int); +G_MODULE_EXPORT int sha1_finish(sha1_state_t *, uint8_t Message_Digest[sha1_hash_size]); + +#endif diff --git a/lib/ssl_bogus.c b/lib/ssl_bogus.c new file mode 100644 index 00000000..5bae3496 --- /dev/null +++ b/lib/ssl_bogus.c @@ -0,0 +1,62 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2002-2004 Wilmer van der Gaast and others                * +  \********************************************************************/ + +/* SSL module - dummy version                                           */ + +/* +  This program is free software; you can redistribute it and/or modify +  it under the terms of the GNU General Public License as published by +  the Free Software Foundation; either version 2 of the License, or +  (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License with +  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; +  if not, write to the Free Software Foundation, Inc., 59 Temple Place, +  Suite 330, Boston, MA  02111-1307  USA +*/ + +#include "ssl_client.h" + +int ssl_errno; + +void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data ) +{ +	return( NULL ); +} + +int ssl_read( void *conn, char *buf, int len ) +{ +	return( -1 ); +} + +int ssl_write( void *conn, const char *buf, int len ) +{ +	return( -1 ); +} + +void ssl_disconnect( void *conn_ ) +{ +} + +int ssl_getfd( void *conn ) +{ +	return( -1 ); +} + +void *ssl_starttls( int fd, ssl_input_function func, gpointer data )  +{ +	return NULL; +} + +b_input_condition ssl_getdirection( void *conn ) +{ +	return GAIM_INPUT_READ; +} diff --git a/lib/ssl_client.h b/lib/ssl_client.h new file mode 100644 index 00000000..f91d0d70 --- /dev/null +++ b/lib/ssl_client.h @@ -0,0 +1,79 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2002-2004 Wilmer van der Gaast and others                * +  \********************************************************************/ + +/* SSL module                                                           */ + +/* +  This program is free software; you can redistribute it and/or modify +  it under the terms of the GNU General Public License as published by +  the Free Software Foundation; either version 2 of the License, or +  (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License with +  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; +  if not, write to the Free Software Foundation, Inc., 59 Temple Place, +  Suite 330, Boston, MA  02111-1307  USA +*/ + +/* ssl_client makes it easier to open SSL connections to servers. (It +   doesn't offer SSL server functionality yet, but it could be useful +   to add it later.) Different ssl_client modules are available, and +   ssl_client tries to make them all behave the same. It's very simple +   and basic, it just imitates the proxy_connect() function from the +   Gaim libs and passes the socket to the program once the handshake +   is completed. */ + +#include <glib.h> +#include "proxy.h" + +/* Some generic error codes. Especially SSL_AGAIN is important if you +   want to do asynchronous I/O. */ +#define SSL_OK            0 +#define SSL_NOHANDSHAKE   1 +#define SSL_AGAIN         2 + +extern int ssl_errno; + +/* This is what your callback function should look like. */ +typedef gboolean (*ssl_input_function)(gpointer, void*, b_input_condition); + + +/* Connect to host:port, call the given function when the connection is +   ready to be used for SSL traffic. This is all done asynchronously, no +   blocking I/O! (Except for the DNS lookups, for now...) */ +G_MODULE_EXPORT void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data ); + +/* Start an SSL session on an existing fd. Useful for STARTTLS functionality, +   for example in Jabber. */ +G_MODULE_EXPORT void *ssl_starttls( int fd, ssl_input_function func, gpointer data ); + +/* Obviously you need special read/write functions to read data. */ +G_MODULE_EXPORT int ssl_read( void *conn, char *buf, int len ); +G_MODULE_EXPORT int ssl_write( void *conn, const char *buf, int len ); + +/* See ssl_openssl.c for an explanation. */ +G_MODULE_EXPORT int ssl_pending( void *conn ); + +/* Abort the SSL connection and disconnect the socket. Do not use close() +   directly, both the SSL library and the peer will be unhappy! */ +G_MODULE_EXPORT void ssl_disconnect( void *conn_ ); + +/* Get the fd for this connection, you will usually need it for event +   handling. */ +G_MODULE_EXPORT int ssl_getfd( void *conn ); + +/* This function returns GAIM_INPUT_READ/WRITE. With SSL connections it's +   possible that something has to be read while actually were trying to +   write something (think about key exchange/refresh/etc). So when an +   SSL operation returned SSL_AGAIN, *always* use this function when +   adding an event handler to the queue. (And it should perform exactly +   the same action as the handler that just received the SSL_AGAIN.) */ +G_MODULE_EXPORT b_input_condition ssl_getdirection( void *conn ); diff --git a/lib/ssl_gnutls.c b/lib/ssl_gnutls.c new file mode 100644 index 00000000..f5945442 --- /dev/null +++ b/lib/ssl_gnutls.c @@ -0,0 +1,247 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2002-2004 Wilmer van der Gaast and others                * +  \********************************************************************/ + +/* SSL module - GnuTLS version                                          */ + +/* +  This program is free software; you can redistribute it and/or modify +  it under the terms of the GNU General Public License as published by +  the Free Software Foundation; either version 2 of the License, or +  (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License with +  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; +  if not, write to the Free Software Foundation, Inc., 59 Temple Place, +  Suite 330, Boston, MA  02111-1307  USA +*/ + +#include <gnutls/gnutls.h> +#include <fcntl.h> +#include <unistd.h> +#include "proxy.h" +#include "ssl_client.h" +#include "sock.h" +#include "stdlib.h" + +int ssl_errno = 0; + +static gboolean initialized = FALSE; + +#include <limits.h> + +#if defined(ULONG_MAX) && ULONG_MAX > 4294967295UL +#define GNUTLS_STUPID_CAST (long) +#else +#define GNUTLS_STUPID_CAST (int) +#endif + +struct scd +{ +	ssl_input_function func; +	gpointer data; +	int fd; +	gboolean established; +	int inpa; +	 +	gnutls_session session; +	gnutls_certificate_credentials xcred; +}; + +static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond ); +static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond ); +static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond ); + + +void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data ) +{ +	struct scd *conn = g_new0( struct scd, 1 ); +	 +	conn->fd = proxy_connect( host, port, ssl_connected, conn ); +	conn->func = func; +	conn->data = data; +	conn->inpa = -1; +	 +	if( conn->fd < 0 ) +	{ +		g_free( conn ); +		return NULL; +	} +	 +	return conn; +} + +void *ssl_starttls( int fd, ssl_input_function func, gpointer data ) +{ +	struct scd *conn = g_new0( struct scd, 1 ); +	 +	conn->fd = fd; +	conn->func = func; +	conn->data = data; +	conn->inpa = -1; +	 +	/* This function should be called via a (short) timeout instead of +	   directly from here, because these SSL calls are *supposed* to be +	   *completely* asynchronous and not ready yet when this function +	   (or *_connect, for examle) returns. Also, errors are reported via +	   the callback function, not via this function's return value. +	    +	   In short, doing things like this makes the rest of the code a lot +	   simpler. */ +	 +	b_timeout_add( 1, ssl_starttls_real, conn ); +	 +	return conn; +} + +static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond ) +{ +	struct scd *conn = data; +	 +	return ssl_connected( conn, conn->fd, GAIM_INPUT_WRITE ); +} + +static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond ) +{ +	struct scd *conn = data; +	 +	if( source == -1 ) +	{ +		conn->func( conn->data, NULL, cond ); +		g_free( conn ); +		return FALSE; +	} +	 +	if( !initialized ) +	{ +		gnutls_global_init(); +		initialized = TRUE; +		atexit( gnutls_global_deinit ); +	} +	 +	gnutls_certificate_allocate_credentials( &conn->xcred ); +	gnutls_init( &conn->session, GNUTLS_CLIENT ); +	gnutls_set_default_priority( conn->session ); +	gnutls_credentials_set( conn->session, GNUTLS_CRD_CERTIFICATE, conn->xcred ); +	 +	sock_make_nonblocking( conn->fd ); +	gnutls_transport_set_ptr( conn->session, (gnutls_transport_ptr) GNUTLS_STUPID_CAST conn->fd ); +	 +	return ssl_handshake( data, source, cond ); +} + +static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond ) +{ +	struct scd *conn = data; +	int st; +	 +	if( ( st = gnutls_handshake( conn->session ) ) < 0 ) +	{ +		if( st == GNUTLS_E_AGAIN || st == GNUTLS_E_INTERRUPTED ) +		{ +			conn->inpa = b_input_add( conn->fd, ssl_getdirection( conn ), +			                          ssl_handshake, data ); +		} +		else +		{ +			conn->func( conn->data, NULL, cond ); +			 +			gnutls_deinit( conn->session ); +			gnutls_certificate_free_credentials( conn->xcred ); +			closesocket( conn->fd ); +			 +			g_free( conn ); +		} +	} +	else +	{ +		/* For now we can't handle non-blocking perfectly everywhere... */ +		sock_make_blocking( conn->fd ); +		 +		conn->established = TRUE; +		conn->func( conn->data, conn, cond ); +	} +	 +	return FALSE; +} + +int ssl_read( void *conn, char *buf, int len ) +{ +	int st; +	 +	if( !((struct scd*)conn)->established ) +	{ +		ssl_errno = SSL_NOHANDSHAKE; +		return( -1 ); +	} +	 +	st = gnutls_record_recv( ((struct scd*)conn)->session, buf, len ); +	 +	ssl_errno = SSL_OK; +	if( st == GNUTLS_E_AGAIN || st == GNUTLS_E_INTERRUPTED ) +		ssl_errno = SSL_AGAIN; +	 +	return st; +} + +int ssl_write( void *conn, const char *buf, int len ) +{ +	int st; +	 +	if( !((struct scd*)conn)->established ) +	{ +		ssl_errno = SSL_NOHANDSHAKE; +		return( -1 ); +	} +	 +	st = gnutls_record_send( ((struct scd*)conn)->session, buf, len ); +	 +	ssl_errno = SSL_OK; +	if( st == GNUTLS_E_AGAIN || st == GNUTLS_E_INTERRUPTED ) +		ssl_errno = SSL_AGAIN; +	 +	return st; +} + +/* See ssl_openssl.c for an explanation. */ +int ssl_pending( void *conn ) +{ +	return 0; +} + +void ssl_disconnect( void *conn_ ) +{ +	struct scd *conn = conn_; +	 +	if( conn->inpa != -1 ) +		b_event_remove( conn->inpa ); +	 +	if( conn->established ) +		gnutls_bye( conn->session, GNUTLS_SHUT_WR ); +	 +	closesocket( conn->fd ); +	 +	if( conn->session ) +		gnutls_deinit( conn->session ); +	if( conn->xcred ) +		gnutls_certificate_free_credentials( conn->xcred ); +	g_free( conn ); +} + +int ssl_getfd( void *conn ) +{ +	return( ((struct scd*)conn)->fd ); +} + +b_input_condition ssl_getdirection( void *conn ) +{ +	return( gnutls_record_get_direction( ((struct scd*)conn)->session ) ? +	        GAIM_INPUT_WRITE : GAIM_INPUT_READ ); +} diff --git a/lib/ssl_nss.c b/lib/ssl_nss.c new file mode 100644 index 00000000..eba3c441 --- /dev/null +++ b/lib/ssl_nss.c @@ -0,0 +1,196 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2002-2005 Wilmer van der Gaast and others                * +  \********************************************************************/ + +/* SSL module - NSS version                                             */ + +/* Copyright 2005 Jelmer Vernooij                                       */ + +/* +  This program is free software; you can redistribute it and/or modify +  it under the terms of the GNU General Public License as published by +  the Free Software Foundation; either version 2 of the License, or +  (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License with +  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; +  if not, write to the Free Software Foundation, Inc., 59 Temple Place, +  Suite 330, Boston, MA  02111-1307  USA +*/ + +#include "bitlbee.h" +#include "proxy.h" +#include "ssl_client.h" +#include "sock.h" +#include <nspr.h> +#include <prio.h> +#include <sslproto.h> +#include <nss.h> +#include <private/pprio.h> +#include <ssl.h> +#include <secerr.h> +#include <sslerr.h> + +int ssl_errno = 0; + +static gboolean initialized = FALSE; + +struct scd +{ +	ssl_input_function func; +	gpointer data; +	int fd; +	PRFileDesc *prfd; +	gboolean established; +}; + +static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond ); + + +static SECStatus nss_auth_cert (void *arg, PRFileDesc *socket, PRBool checksig, PRBool isserver) +{ +	return SECSuccess; +} + +static SECStatus nss_bad_cert (void *arg, PRFileDesc *socket)  +{ +	PRErrorCode err; + +	if(!arg) return SECFailure; + +	*(PRErrorCode *)arg = err = PORT_GetError(); + +	switch(err) { +	case SEC_ERROR_INVALID_AVA: +	case SEC_ERROR_INVALID_TIME: +	case SEC_ERROR_BAD_SIGNATURE: +	case SEC_ERROR_EXPIRED_CERTIFICATE: +	case SEC_ERROR_UNKNOWN_ISSUER: +	case SEC_ERROR_UNTRUSTED_CERT: +	case SEC_ERROR_CERT_VALID: +	case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE: +	case SEC_ERROR_CRL_EXPIRED: +	case SEC_ERROR_CRL_BAD_SIGNATURE: +	case SEC_ERROR_EXTENSION_VALUE_INVALID: +	case SEC_ERROR_CA_CERT_INVALID: +	case SEC_ERROR_CERT_USAGES_INVALID: +	case SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION: +		return SECSuccess; + +	default: +		return SECFailure; +	} +} + + +void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data ) +{ +	struct scd *conn = g_new0( struct scd, 1 ); +	 +	conn->fd = proxy_connect( host, port, ssl_connected, conn ); +	conn->func = func; +	conn->data = data; +	 +	if( conn->fd < 0 ) +	{ +		g_free( conn ); +		return( NULL ); +	} +	 +	if( !initialized ) +	{ +		PR_Init( PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); +		NSS_NoDB_Init(NULL); +		NSS_SetDomesticPolicy(); +	} + +	 +	return( conn ); +} + +static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond ) +{ +	struct scd *conn = data; +	 +	if( source == -1 ) +		goto ssl_connected_failure; +	 +	/* Until we find out how to handle non-blocking I/O with NSS... */ +	sock_make_blocking( conn->fd ); +	 +	conn->prfd = SSL_ImportFD(NULL, PR_ImportTCPSocket(source)); +	SSL_OptionSet(conn->prfd, SSL_SECURITY, PR_TRUE); +	SSL_OptionSet(conn->prfd, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE); +	SSL_BadCertHook(conn->prfd, (SSLBadCertHandler)nss_bad_cert, NULL); +	SSL_AuthCertificateHook(conn->prfd, (SSLAuthCertificate)nss_auth_cert, (void *)CERT_GetDefaultCertDB()); +	SSL_ResetHandshake(conn->prfd, PR_FALSE); + +	if (SSL_ForceHandshake(conn->prfd)) { +		goto ssl_connected_failure; +	} +	 +	 +	conn->established = TRUE; +	conn->func( conn->data, conn, cond ); +	return FALSE; +	 +	ssl_connected_failure: +	 +	conn->func( conn->data, NULL, cond ); +	 +	PR_Close( conn -> prfd ); +	if( source >= 0 ) closesocket( source ); +	g_free( conn ); +	 +	return FALSE; +} + +int ssl_read( void *conn, char *buf, int len ) +{ +	if( !((struct scd*)conn)->established ) +		return( 0 ); +	 +	return( PR_Read( ((struct scd*)conn)->prfd, buf, len ) ); +} + +int ssl_write( void *conn, const char *buf, int len ) +{ +	if( !((struct scd*)conn)->established ) +		return( 0 ); +	 +	return( PR_Write ( ((struct scd*)conn)->prfd, buf, len ) ); +} + +/* See ssl_openssl.c for an explanation. */ +int ssl_pending( void *conn ) +{ +	return 0; +} + +void ssl_disconnect( void *conn_ ) +{ +	struct scd *conn = conn_; +	 +	PR_Close( conn->prfd ); +	closesocket( conn->fd ); +	 +	g_free( conn ); +} + +int ssl_getfd( void *conn ) +{ +	return( ((struct scd*)conn)->fd ); +} + +b_input_condition ssl_getdirection( void *conn ) +{ +	/* Just in case someone calls us, let's return the most likely case: */ +	return GAIM_INPUT_READ; +} diff --git a/lib/ssl_openssl.c b/lib/ssl_openssl.c new file mode 100644 index 00000000..fc6d433e --- /dev/null +++ b/lib/ssl_openssl.c @@ -0,0 +1,273 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2002-2004 Wilmer van der Gaast and others                * +  \********************************************************************/ + +/* SSL module - OpenSSL version                                         */ + +/* +  This program is free software; you can redistribute it and/or modify +  it under the terms of the GNU General Public License as published by +  the Free Software Foundation; either version 2 of the License, or +  (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License with +  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; +  if not, write to the Free Software Foundation, Inc., 59 Temple Place, +  Suite 330, Boston, MA  02111-1307  USA +*/ + +#include <openssl/crypto.h> +#include <openssl/rand.h> +#include <openssl/x509.h> +#include <openssl/pem.h> +#include <openssl/ssl.h> +#include <openssl/err.h> + +#include "proxy.h" +#include "ssl_client.h" +#include "sock.h" + +int ssl_errno = 0; + +static gboolean initialized = FALSE; + +struct scd +{ +	ssl_input_function func; +	gpointer data; +	int fd; +	gboolean established; +	 +	int inpa; +	int lasterr;		/* Necessary for SSL_get_error */ +	SSL *ssl; +	SSL_CTX *ssl_ctx; +}; + +static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond ); +static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond ); +static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond ); + + +void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data ) +{ +	struct scd *conn = g_new0( struct scd, 1 ); +	 +	conn->fd = proxy_connect( host, port, ssl_connected, conn ); +	if( conn->fd < 0 ) +	{ +		g_free( conn ); +		return NULL; +	} +	 +	conn->func = func; +	conn->data = data; +	conn->inpa = -1; +	 +	return conn; +} + +void *ssl_starttls( int fd, ssl_input_function func, gpointer data ) +{ +	struct scd *conn = g_new0( struct scd, 1 ); +	 +	conn->fd = fd; +	conn->func = func; +	conn->data = data; +	conn->inpa = -1; +	 +	/* This function should be called via a (short) timeout instead of +	   directly from here, because these SSL calls are *supposed* to be +	   *completely* asynchronous and not ready yet when this function +	   (or *_connect, for examle) returns. Also, errors are reported via +	   the callback function, not via this function's return value. +	    +	   In short, doing things like this makes the rest of the code a lot +	   simpler. */ +	 +	b_timeout_add( 1, ssl_starttls_real, conn ); +	 +	return conn; +} + +static gboolean ssl_starttls_real( gpointer data, gint source, b_input_condition cond ) +{ +	struct scd *conn = data; +	 +	return ssl_connected( conn, conn->fd, GAIM_INPUT_WRITE ); +} + +static gboolean ssl_connected( gpointer data, gint source, b_input_condition cond ) +{ +	struct scd *conn = data; +	SSL_METHOD *meth; +	 +	if( source == -1 ) +		goto ssl_connected_failure; +	 +	if( !initialized ) +	{ +		initialized = TRUE; +		SSLeay_add_ssl_algorithms(); +	} +	 +	meth = TLSv1_client_method(); +	conn->ssl_ctx = SSL_CTX_new( meth ); +	if( conn->ssl_ctx == NULL ) +		goto ssl_connected_failure; +	 +	conn->ssl = SSL_new( conn->ssl_ctx ); +	if( conn->ssl == NULL ) +		goto ssl_connected_failure; +	 +	/* We can do at least the handshake with non-blocking I/O */ +	sock_make_nonblocking( conn->fd ); +	SSL_set_fd( conn->ssl, conn->fd ); +	 +	return ssl_handshake( data, source, cond ); + +ssl_connected_failure: +	conn->func( conn->data, NULL, cond ); +	 +	if( conn->ssl ) +	{ +		SSL_shutdown( conn->ssl ); +		SSL_free( conn->ssl ); +	} +	if( conn->ssl_ctx ) +	{ +		SSL_CTX_free( conn->ssl_ctx ); +	} +	if( source >= 0 ) closesocket( source ); +	g_free( conn ); +	 +	return FALSE; + +}	 + +static gboolean ssl_handshake( gpointer data, gint source, b_input_condition cond ) +{ +	struct scd *conn = data; +	int st; +	 +	if( ( st = SSL_connect( conn->ssl ) ) < 0 ) +	{ +		conn->lasterr = SSL_get_error( conn->ssl, st ); +		if( conn->lasterr != SSL_ERROR_WANT_READ && conn->lasterr != SSL_ERROR_WANT_WRITE ) +		{ +			conn->func( conn->data, NULL, cond ); +			 +			SSL_shutdown( conn->ssl ); +			SSL_free( conn->ssl ); +			SSL_CTX_free( conn->ssl_ctx ); +			 +			if( source >= 0 ) closesocket( source ); +			g_free( conn ); +			 +			return FALSE; +		} +		 +		conn->inpa = b_input_add( conn->fd, ssl_getdirection( conn ), ssl_handshake, data ); +		return FALSE; +	} +	 +	conn->established = TRUE; +	sock_make_blocking( conn->fd );		/* For now... */ +	conn->func( conn->data, conn, cond ); +	return FALSE; +} + +int ssl_read( void *conn, char *buf, int len ) +{ +	int st; +	 +	if( !((struct scd*)conn)->established ) +	{ +		ssl_errno = SSL_NOHANDSHAKE; +		return -1; +	} +	 +	st = SSL_read( ((struct scd*)conn)->ssl, buf, len ); +	 +	ssl_errno = SSL_OK; +	if( st <= 0 ) +	{ +		((struct scd*)conn)->lasterr = SSL_get_error( ((struct scd*)conn)->ssl, st ); +		if( ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_READ || ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_WRITE ) +			ssl_errno = SSL_AGAIN; +	} +	 +	return st; +} + +int ssl_write( void *conn, const char *buf, int len ) +{ +	int st; +	 +	if( !((struct scd*)conn)->established ) +	{ +		ssl_errno = SSL_NOHANDSHAKE; +		return -1; +	} +	 +	st = SSL_write( ((struct scd*)conn)->ssl, buf, len ); +	 +	ssl_errno = SSL_OK; +	if( st <= 0 ) +	{ +		((struct scd*)conn)->lasterr = SSL_get_error( ((struct scd*)conn)->ssl, st ); +		if( ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_READ || ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_WRITE ) +			ssl_errno = SSL_AGAIN; +	} +	 +	return st; +} + +/* Only OpenSSL *really* needs this (and well, maybe NSS). See for more info: +   http://www.gnu.org/software/gnutls/manual/gnutls.html#index-gnutls_005frecord_005fcheck_005fpending-209 +   http://www.openssl.org/docs/ssl/SSL_pending.html +    +   Required because OpenSSL empties the TCP buffer completely but doesn't +   necessarily give us all the unencrypted data. +    +   Returns 0 if there's nothing left or if we don't have to care (GnuTLS), +   1 if there's more data. */ +int ssl_pending( void *conn ) +{ +	return ( ((struct scd*)conn) && ((struct scd*)conn)->established ) ? +	       SSL_pending( ((struct scd*)conn)->ssl ) > 0 : 0; +} + +void ssl_disconnect( void *conn_ ) +{ +	struct scd *conn = conn_; +	 +	if( conn->inpa != -1 ) +		b_event_remove( conn->inpa ); +	 +	if( conn->established ) +		SSL_shutdown( conn->ssl ); +	 +	closesocket( conn->fd ); +	 +	SSL_free( conn->ssl ); +	SSL_CTX_free( conn->ssl_ctx ); +	g_free( conn ); +} + +int ssl_getfd( void *conn ) +{ +	return( ((struct scd*)conn)->fd ); +} + +b_input_condition ssl_getdirection( void *conn ) +{ +	return( ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_WRITE ? GAIM_INPUT_WRITE : GAIM_INPUT_READ ); +} diff --git a/lib/url.c b/lib/url.c new file mode 100644 index 00000000..de9966b4 --- /dev/null +++ b/lib/url.c @@ -0,0 +1,109 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2001-2005 Wilmer van der Gaast and others                * +  \********************************************************************/ + +/* URL/mirror stuff - Stolen from Axel                                  */ + +/* +  This program is free software; you can redistribute it and/or modify +  it under the terms of the GNU General Public License as published by +  the Free Software Foundation; either version 2 of the License, or +  (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License with +  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; +  if not, write to the Free Software Foundation, Inc., 59 Temple Place, +  Suite 330, Boston, MA  02111-1307  USA +*/ + +#include "url.h" + +/* Convert an URL to a url_t structure */ +int url_set( url_t *url, char *set_url ) +{ +	char s[MAX_STRING+1]; +	char *i; +	 +	memset( url, 0, sizeof( url_t ) ); +	memset( s, 0, sizeof( s ) ); +	 +	/* protocol:// */ +	if( ( i = strstr( set_url, "://" ) ) == NULL ) +	{ +		url->proto = PROTO_DEFAULT; +		strncpy( s, set_url, MAX_STRING ); +	} +	else +	{ +		if( g_strncasecmp( set_url, "http", i - set_url ) == 0 ) +			url->proto = PROTO_HTTP; +		else if( g_strncasecmp( set_url, "https", i - set_url ) == 0 ) +			url->proto = PROTO_HTTPS; +		else if( g_strncasecmp( set_url, "socks4", i - set_url ) == 0 ) +			url->proto = PROTO_SOCKS4; +		else if( g_strncasecmp( set_url, "socks5", i - set_url ) == 0 ) +			url->proto = PROTO_SOCKS5; +		else +			return 0; +		 +		strncpy( s, i + 3, MAX_STRING ); +	} +	 +	/* Split */ +	if( ( i = strchr( s, '/' ) ) == NULL ) +	{ +		strcpy( url->file, "/" ); +	} +	else +	{ +		strncpy( url->file, i, MAX_STRING ); +		*i = 0; +	} +	strncpy( url->host, s, MAX_STRING ); +	 +	/* Check for username in host field */ +	if( strrchr( url->host, '@' ) != NULL ) +	{ +		strncpy( url->user, url->host, MAX_STRING ); +		i = strrchr( url->user, '@' ); +		*i = 0; +		strcpy( url->host, i + 1 ); +		*url->pass = 0; +	} +	/* If not: Fill in defaults */ +	else +	{ +		*url->user = *url->pass = 0; +	} +	 +	/* Password? */ +	if( ( i = strchr( url->user, ':' ) ) != NULL ) +	{ +		*i = 0; +		strcpy( url->pass, i + 1 ); +	} +	/* Port number? */ +	if( ( i = strchr( url->host, ':' ) ) != NULL ) +	{ +		*i = 0; +		sscanf( i + 1, "%d", &url->port ); +	} +	else +	{ +		if( url->proto == PROTO_HTTP ) +			url->port = 80; +		else if( url->proto == PROTO_HTTPS ) +			url->port = 443; +		else if( url->proto == PROTO_SOCKS4 || url->proto == PROTO_SOCKS5 ) +			url->port = 1080; +	} +	 +	return( url->port > 0 ); +} diff --git a/lib/url.h b/lib/url.h new file mode 100644 index 00000000..8c038c91 --- /dev/null +++ b/lib/url.h @@ -0,0 +1,44 @@ +  /********************************************************************\ +  * BitlBee -- An IRC to other IM-networks gateway                     * +  *                                                                    * +  * Copyright 2001-2004 Wilmer van der Gaast and others                * +  \********************************************************************/ + +/* URL/mirror stuff - Stolen from Axel                                  */ + +/* +  This program is free software; you can redistribute it and/or modify +  it under the terms of the GNU General Public License as published by +  the Free Software Foundation; either version 2 of the License, or +  (at your option) any later version. + +  This program is distributed in the hope that it will be useful, +  but WITHOUT ANY WARRANTY; without even the implied warranty of +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +  GNU General Public License for more details. + +  You should have received a copy of the GNU General Public License with +  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; +  if not, write to the Free Software Foundation, Inc., 59 Temple Place, +  Suite 330, Boston, MA  02111-1307  USA +*/ + +#include "bitlbee.h" + +#define PROTO_HTTP      2 +#define PROTO_HTTPS     5 +#define PROTO_SOCKS4    3 +#define PROTO_SOCKS5    4 +#define PROTO_DEFAULT   PROTO_HTTP + +typedef struct url +{ +	int proto; +	int port; +	char host[MAX_STRING+1]; +	char file[MAX_STRING+1]; +	char user[MAX_STRING+1]; +	char pass[MAX_STRING+1]; +} url_t; + +int url_set( url_t *url, char *set_url ); diff --git a/lib/xmltree.c b/lib/xmltree.c new file mode 100644 index 00000000..e65b4f41 --- /dev/null +++ b/lib/xmltree.c @@ -0,0 +1,590 @@ +/***************************************************************************\ +*                                                                           * +*  BitlBee - An IRC to IM gateway                                           * +*  Simple XML (stream) parse tree handling code (Jabber/XMPP, mainly)       * +*                                                                           * +*  Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net>                   * +*                                                                           * +*  This library is free software; you can redistribute it and/or            * +*  modify it under the terms of the GNU Lesser General Public               * +*  License as published by the Free Software Foundation, version            * +*  2.1.                                                                     * +*                                                                           * +*  This library is distributed in the hope that it will be useful,          * +*  but WITHOUT ANY WARRANTY; without even the implied warranty of           * +*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU        * +*  Lesser General Public License for more details.                          * +*                                                                           * +*  You should have received a copy of the GNU Lesser General Public License * +*  along with this library; if not, write to the Free Software Foundation,  * +*  Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           * +*                                                                           * +****************************************************************************/ + +#include <glib.h> +#include <string.h> +#include <unistd.h> +#include <ctype.h> +#include <stdio.h> + +#include "xmltree.h" + +static void xt_start_element( GMarkupParseContext *ctx, const gchar *element_name, const gchar **attr_names, const gchar **attr_values, gpointer data, GError **error ) +{ +	struct xt_parser *xt = data; +	struct xt_node *node = g_new0( struct xt_node, 1 ), *nt; +	int i; +	 +	node->parent = xt->cur; +	node->name = g_strdup( element_name ); +	 +	/* First count the number of attributes */ +	for( i = 0; attr_names[i]; i ++ ); +	 +	/* Then allocate a NULL-terminated array. */ +	node->attr = g_new0( struct xt_attr, i + 1 ); +	 +	/* And fill it, saving one variable by starting at the end. */ +	for( i --; i >= 0; i -- ) +	{ +		node->attr[i].key = g_strdup( attr_names[i] ); +		node->attr[i].value = g_strdup( attr_values[i] ); +	} +	 +	/* Add it to the linked list of children nodes, if we have a current +	   node yet. */ +	if( xt->cur ) +	{ +		if( xt->cur->children ) +		{ +			for( nt = xt->cur->children; nt->next; nt = nt->next ); +			nt->next = node; +		} +		else +		{ +			xt->cur->children = node; +		} +	} +	else if( xt->root ) +	{ +		/* ERROR situation: A second root-element??? */ +	} +	 +	/* Now this node will be the new current node. */ +	xt->cur = node; +	/* And maybe this is the root? */ +	if( xt->root == NULL ) +		xt->root = node; +} + +static void xt_text( GMarkupParseContext *ctx, const gchar *text, gsize text_len, gpointer data, GError **error ) +{ +	struct xt_parser *xt = data; +	struct xt_node *node = xt->cur; +	 +	if( node == NULL ) +		return; +	 +	/* FIXME: Does g_renew also OFFICIALLY accept NULL arguments? */ +	node->text = g_renew( char, node->text, node->text_len + text_len + 1 ); +	memcpy( node->text + node->text_len, text, text_len ); +	node->text_len += text_len; +	/* Zero termination is always nice to have. */ +	node->text[node->text_len] = 0; +} + +static void xt_end_element( GMarkupParseContext *ctx, const gchar *element_name, gpointer data, GError **error ) +{ +	struct xt_parser *xt = data; +	 +	xt->cur->flags |= XT_COMPLETE; +	xt->cur = xt->cur->parent; +} + +GMarkupParser xt_parser_funcs = +{ +	xt_start_element, +	xt_end_element, +	xt_text, +	NULL, +	NULL +}; + +struct xt_parser *xt_new( const struct xt_handler_entry *handlers, gpointer data ) +{ +	struct xt_parser *xt = g_new0( struct xt_parser, 1 ); +	 +	xt->data = data; +	xt->handlers = handlers; +	xt_reset( xt ); +	 +	return xt; +} + +/* Reset the parser, flush everything we have so far. For example, we need +   this for XMPP when doing TLS/SASL to restart the stream. */ +void xt_reset( struct xt_parser *xt ) +{ +	if( xt->parser ) +		g_markup_parse_context_free( xt->parser ); +	 +	xt->parser = g_markup_parse_context_new( &xt_parser_funcs, 0, xt, NULL ); +	 +	if( xt->root ) +	{ +		xt_free_node( xt->root ); +		xt->root = NULL; +		xt->cur = NULL; +	} +} + +/* Feed the parser, don't execute any handler. Returns -1 on errors, 0 on +   end-of-stream and 1 otherwise. */ +int xt_feed( struct xt_parser *xt, char *text, int text_len ) +{ +	if( !g_markup_parse_context_parse( xt->parser, text, text_len, &xt->gerr ) ) +	{ +		return -1; +	} +	 +	return !( xt->root && xt->root->flags & XT_COMPLETE ); +} + +/* Find completed nodes and see if a handler has to be called. Passing +   a node isn't necessary if you want to start at the root, just pass +   NULL. This second argument is needed for recursive calls. */ +int xt_handle( struct xt_parser *xt, struct xt_node *node, int depth ) +{ +	struct xt_node *c; +	xt_status st; +	int i; +	 +	/* Just in case someone likes infinite loops... */ +	if( xt->root == NULL ) +		return 0; +	 +	if( node == NULL ) +		return xt_handle( xt, xt->root, depth ); +	 +	if( depth != 0 ) +		for( c = node->children; c; c = c->next ) +			if( !xt_handle( xt, c, depth > 0 ? depth - 1 : depth ) ) +				return 0; +	 +	if( node->flags & XT_COMPLETE && !( node->flags & XT_SEEN ) ) +	{ +		for( i = 0; xt->handlers[i].func; i ++ ) +		{ +			/* This one is fun! \o/ */ +			 +						/* If handler.name == NULL it means it should always match. */ +			if( ( xt->handlers[i].name == NULL ||  +						/* If it's not, compare. There should always be a name. */ +			      g_strcasecmp( xt->handlers[i].name, node->name ) == 0 ) && +						/* If handler.parent == NULL, it's a match. */ +			    ( xt->handlers[i].parent == NULL || +						/* If there's a parent node, see if the name matches. */ +			      ( node->parent ? g_strcasecmp( xt->handlers[i].parent, node->parent->name ) == 0 :  +						/* If there's no parent, the handler should mention <root> as a parent. */ +			                       g_strcasecmp( xt->handlers[i].parent, "<root>" ) == 0 ) ) ) +			{ +				st = xt->handlers[i].func( node, xt->data ); +				 +				if( st == XT_ABORT ) +					return 0; +				else if( st != XT_NEXT ) +					break; +			} +		} +		 +		node->flags |= XT_SEEN; +	} +	 +	return 1; +} + +/* Garbage collection: Cleans up all nodes that are handled. Useful for +   streams because there's no reason to keep a complete packet history +   in memory. */ +void xt_cleanup( struct xt_parser *xt, struct xt_node *node, int depth ) +{ +	struct xt_node *c, *prev; +	 +	if( !xt || !xt->root ) +		return; +	 +	if( node == NULL ) +		return xt_cleanup( xt, xt->root, depth ); +	 +	if( node->flags & XT_SEEN && node == xt->root ) +	{ +		xt_free_node( xt->root ); +		xt->root = xt->cur = NULL; +		/* xt->cur should be NULL already, BTW... */ +		 +		return; +	} +	 +	/* c contains the current node, prev the previous node (or NULL). +	   I admit, this one's pretty horrible. */ +	for( c = node->children, prev = NULL; c; prev = c, c = c ? c->next : node->children ) +	{ +		if( c->flags & XT_SEEN ) +		{ +			/* Remove the node from the linked list. */ +			if( prev ) +				prev->next = c->next; +			else +				node->children = c->next; +			 +			xt_free_node( c ); +			 +			/* Since the for loop wants to get c->next, make sure +			   c points at something that exists (and that c->next +			   will actually be the next item we should check). c +			   can be NULL now, if we just removed the first item. +			   That explains the ? thing in for(). */ +			c = prev; +		} +		else +		{ +			/* This node can't be cleaned up yet, but maybe a +			   subnode can. */ +			if( depth != 0 ) +				xt_cleanup( xt, c, depth > 0 ? depth - 1 : depth ); +		} +	} +} + +static void xt_to_string_real( struct xt_node *node, GString *str ) +{ +	char *buf; +	struct xt_node *c; +	int i; +	 +	g_string_append_printf( str, "<%s", node->name ); +	 +	for( i = 0; node->attr[i].key; i ++ ) +	{ +		buf = g_markup_printf_escaped( " %s=\"%s\"", node->attr[i].key, node->attr[i].value ); +		g_string_append( str, buf ); +		g_free( buf ); +	} +	 +	if( node->text == NULL && node->children == NULL ) +	{ +		g_string_append( str, "/>" ); +		return; +	} +	 +	g_string_append( str, ">" ); +	if( node->text_len > 0 ) +	{ +		buf = g_markup_escape_text( node->text, node->text_len ); +		g_string_append( str, buf ); +		g_free( buf ); +	} +	 +	for( c = node->children; c; c = c->next ) +		xt_to_string_real( c, str ); +	 +	g_string_append_printf( str, "</%s>", node->name ); +} + +char *xt_to_string( struct xt_node *node ) +{ +	GString *ret; +	char *real; +	 +	ret = g_string_new( "" ); +	xt_to_string_real( node, ret ); +	 +	real = ret->str; +	g_string_free( ret, FALSE ); +	 +	return real; +} + +#ifdef DEBUG +void xt_print( struct xt_node *node ) +{ +	int i; +	struct xt_node *c; +	 +	/* Indentation */ +	for( c = node; c->parent; c = c->parent ) +		printf( "\t" ); +	 +	/* Start the tag */ +	printf( "<%s", node->name ); +	 +	/* Print the attributes */ +	for( i = 0; node->attr[i].key; i ++ ) +		printf( " %s=\"%s\"", node->attr[i].key, g_markup_escape_text( node->attr[i].value, -1 ) ); +	 +	/* /> in case there's really *nothing* inside this tag, otherwise +	   just >. */ +	/* If this tag doesn't have any content at all... */ +	if( node->text == NULL && node->children == NULL ) +	{ +		printf( "/>\n" ); +		return; +		/* Then we're finished! */ +	} +	 +	/* Otherwise... */ +	printf( ">" ); +	 +	/* Only print the text if it contains more than whitespace (TEST). */ +	if( node->text_len > 0 ) +	{ +		for( i = 0; node->text[i] && isspace( node->text[i] ); i ++ ); +		if( node->text[i] ) +			printf( "%s", g_markup_escape_text( node->text, -1 ) ); +	} +	 +	if( node->children ) +		printf( "\n" ); +	 +	for( c = node->children; c; c = c->next ) +		xt_print( c ); +	 +	if( node->children ) +		for( c = node; c->parent; c = c->parent ) +			printf( "\t" ); +	 +	/* Non-empty tag is now finished. */ +	printf( "</%s>\n", node->name ); +} +#endif + +struct xt_node *xt_dup( struct xt_node *node ) +{ +	struct xt_node *dup = g_new0( struct xt_node, 1 ); +	struct xt_node *c, *dc = NULL; +	int i; +	 +	/* Let's NOT copy the parent element here BTW! Only do it for children. */ +	 +	dup->name = g_strdup( node->name ); +	dup->flags = node->flags; +	if( node->text ) +	{ +		dup->text = g_memdup( node->text, node->text_len + 1 ); +		dup->text_len = node->text_len; +	} +	 +	/* Count the number of attributes and allocate the new array. */ +	for( i = 0; node->attr[i].key; i ++ ); +	dup->attr = g_new0( struct xt_attr, i + 1 ); +	 +	/* Copy them all! */ +	for( i --; i >= 0; i -- ) +	{ +		dup->attr[i].key = g_strdup( node->attr[i].key ); +		dup->attr[i].value = g_strdup( node->attr[i].value ); +	} +	 +	/* This nice mysterious loop takes care of the children. */ +	for( c = node->children; c; c = c->next ) +	{ +		if( dc == NULL ) +			dc = dup->children = xt_dup( c ); +		else +			dc = ( dc->next = xt_dup( c ) ); +		 +		dc->parent = dup; +	} +	 +	return dup; +} + +/* Frees a node. This doesn't clean up references to itself from parents! */ +void xt_free_node( struct xt_node *node ) +{ +	int i; +	 +	if( !node ) +		return; +	 +	g_free( node->name ); +	g_free( node->text ); +	 +	for( i = 0; node->attr[i].key; i ++ ) +	{ +		g_free( node->attr[i].key ); +		g_free( node->attr[i].value ); +	} +	g_free( node->attr ); +	 +	while( node->children ) +	{ +		struct xt_node *next = node->children->next; +		 +		xt_free_node( node->children ); +		node->children = next; +	} +	 +	g_free( node ); +} + +void xt_free( struct xt_parser *xt ) +{ +	if( !xt ) +		return; +	 +	if( xt->root ) +		xt_free_node( xt->root ); +	 +	g_markup_parse_context_free( xt->parser ); +	 +	g_free( xt ); +} + +/* To find a node's child with a specific name, pass the node's children +   list, not the node itself! The reason you have to do this by hand: So +   that you can also use this function as a find-next. */ +struct xt_node *xt_find_node( struct xt_node *node, const char *name ) +{ +	while( node ) +	{ +		if( g_strcasecmp( node->name, name ) == 0 ) +			break; +		 +		node = node->next; +	} +	 +	return node; +} + +char *xt_find_attr( struct xt_node *node, const char *key ) +{ +	int i; +	 +	if( !node ) +		return NULL; +	 +	for( i = 0; node->attr[i].key; i ++ ) +		if( g_strcasecmp( node->attr[i].key, key ) == 0 ) +			break; +	 +	return node->attr[i].value; +} + +struct xt_node *xt_new_node( char *name, char *text, struct xt_node *children ) +{ +	struct xt_node *node, *c; +	 +	node = g_new0( struct xt_node, 1 ); +	node->name = g_strdup( name ); +	node->children = children; +	node->attr = g_new0( struct xt_attr, 1 ); +	 +	if( text ) +	{ +		node->text_len = strlen( text ); +		node->text = g_memdup( text, node->text_len + 1 ); +	} +	 +	for( c = children; c; c = c->next ) +	{ +		if( c->parent != NULL ) +		{ +			/* ERROR CONDITION: They seem to have a parent already??? */ +		} +		 +		c->parent = node; +	} +	 +	return node; +} + +void xt_add_child( struct xt_node *parent, struct xt_node *child ) +{ +	struct xt_node *node; +	 +	/* This function can actually be used to add more than one child, so +	   do handle this properly. */ +	for( node = child; node; node = node->next ) +	{ +		if( node->parent != NULL ) +		{ +			/* ERROR CONDITION: They seem to have a parent already??? */ +		} +		 +		node->parent = parent; +	} +	 +	if( parent->children == NULL ) +	{ +		parent->children = child; +	} +	else +	{ +		for( node = parent->children; node->next; node = node->next ); +		node->next = child; +	} +} + +void xt_add_attr( struct xt_node *node, const char *key, const char *value ) +{ +	int i; +	 +	/* Now actually it'd be nice if we can also change existing attributes +	   (which actually means this function doesn't have the right name). +	   So let's find out if we have this attribute already... */ +	for( i = 0; node->attr[i].key; i ++ ) +		if( strcmp( node->attr[i].key, key ) == 0 ) +			break; +	 +	if( node->attr[i].key == NULL ) +	{ +		/* If not, allocate space for a new attribute. */ +		node->attr = g_renew( struct xt_attr, node->attr, i + 2 ); +		node->attr[i].key = g_strdup( key ); +		node->attr[i+1].key = NULL; +	} +	else +	{ +		/* Otherwise, free the old value before setting the new one. */ +		g_free( node->attr[i].value ); +	} +	 +	node->attr[i].value = g_strdup( value ); +} + +int xt_remove_attr( struct xt_node *node, const char *key ) +{ +	int i, last; +	 +	for( i = 0; node->attr[i].key; i ++ ) +		if( strcmp( node->attr[i].key, key ) == 0 ) +			break; +	 +	/* If we didn't find the attribute... */ +	if( node->attr[i].key == NULL ) +		return 0; +	 +	g_free( node->attr[i].key ); +	g_free( node->attr[i].value ); +	 +	/* If it's the last, this is easy: */ +	if( node->attr[i+1].key == NULL ) +	{ +		node->attr[i].key = node->attr[i].value = NULL; +	} +	else /* It's also pretty easy, actually. */ +	{ +		/* Find the last item. */ +		for( last = i + 1; node->attr[last+1].key; last ++ ); +		 +		node->attr[i] = node->attr[last]; +		node->attr[last].key = NULL; +		node->attr[last].value = NULL; +	} +	 +	/* Let's not bother with reallocating memory here. It takes time and +	   most packets don't stay in memory for long anyway. */ +	 +	return 1; +} diff --git a/lib/xmltree.h b/lib/xmltree.h new file mode 100644 index 00000000..10677412 --- /dev/null +++ b/lib/xmltree.h @@ -0,0 +1,97 @@ +/***************************************************************************\ +*                                                                           * +*  BitlBee - An IRC to IM gateway                                           * +*  Simple XML (stream) parse tree handling code (Jabber/XMPP, mainly)       * +*                                                                           * +*  Copyright 2006 Wilmer van der Gaast <wilmer@gaast.net>                   * +*                                                                           * +*  This library is free software; you can redistribute it and/or            * +*  modify it under the terms of the GNU Lesser General Public               * +*  License as published by the Free Software Foundation, version            * +*  2.1.                                                                     * +*                                                                           * +*  This library is distributed in the hope that it will be useful,          * +*  but WITHOUT ANY WARRANTY; without even the implied warranty of           * +*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU        * +*  Lesser General Public License for more details.                          * +*                                                                           * +*  You should have received a copy of the GNU Lesser General Public License * +*  along with this library; if not, write to the Free Software Foundation,  * +*  Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           * +*                                                                           * +****************************************************************************/ + +#ifndef _XMLTREE_H +#define _XMLTREE_H + +typedef enum +{ +	XT_COMPLETE	= 1,	/* </tag> reached */ +	XT_SEEN		= 2,	/* Handler called (or not defined) */ +} xt_flags; + +typedef enum +{ +	XT_ABORT,		/* Abort, don't handle the rest anymore */ +	XT_HANDLED,		/* Handled this tag properly, go to the next one */ +	XT_NEXT			/* Try if there's another matching handler */ +} xt_status; + +struct xt_attr +{ +	char *key, *value; +}; + +struct xt_node +{ +	struct xt_node *parent; +	struct xt_node *children; +	 +	char *name; +	struct xt_attr *attr; +	char *text; +	int text_len; +	 +	struct xt_node *next; +	xt_flags flags; +}; + +typedef xt_status (*xt_handler_func) ( struct xt_node *node, gpointer data ); + +struct xt_handler_entry +{ +	char *name, *parent; +	xt_handler_func func; +}; + +struct xt_parser +{ +	GMarkupParseContext *parser; +	struct xt_node *root; +	struct xt_node *cur; +	 +	const struct xt_handler_entry *handlers; +	gpointer data; +	 +	GError *gerr; +}; + +struct xt_parser *xt_new( const struct xt_handler_entry *handlers, gpointer data ); +void xt_reset( struct xt_parser *xt ); +int xt_feed( struct xt_parser *xt, char *text, int text_len ); +int xt_handle( struct xt_parser *xt, struct xt_node *node, int depth ); +void xt_cleanup( struct xt_parser *xt, struct xt_node *node, int depth ); +char *xt_to_string( struct xt_node *node ); +void xt_print( struct xt_node *node ); +struct xt_node *xt_dup( struct xt_node *node ); +void xt_free_node( struct xt_node *node ); +void xt_free( struct xt_parser *xt ); +struct xt_node *xt_find_node( struct xt_node *node, const char *name ); +char *xt_find_attr( struct xt_node *node, const char *key ); + +struct xt_node *xt_new_node( char *name, char *text, struct xt_node *children ); +void xt_add_child( struct xt_node *parent, struct xt_node *child ); +void xt_add_attr( struct xt_node *node, const char *key, const char *value ); +int xt_remove_attr( struct xt_node *node, const char *key ); + +#endif | 
