diff options
author | Sven Moritz Hallberg <pesco@khjk.org> | 2008-07-17 01:22:52 +0200 |
---|---|---|
committer | Sven Moritz Hallberg <pesco@khjk.org> | 2008-07-17 01:22:52 +0200 |
commit | 6738a676c7a3895988de4bd9eacfe8fa0ef73cc3 (patch) | |
tree | d95d913484cf79ff4a3c6d920a4d9b92ecd66de9 /lib | |
parent | 9730d7250bb9e938ca00b72efdd8e8b3c03b2753 (diff) | |
parent | 6a78c0eed44820a2fefe1e96516e335eddc9c70b (diff) |
merge in latest trunk
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Makefile | 2 | ||||
-rw-r--r-- | lib/arc.c | 27 | ||||
-rw-r--r-- | lib/arc.h | 8 | ||||
-rw-r--r-- | lib/base64.c | 2 | ||||
-rw-r--r-- | lib/events_glib.c | 5 | ||||
-rw-r--r-- | lib/misc.c | 64 | ||||
-rw-r--r-- | lib/misc.h | 3 | ||||
-rw-r--r-- | lib/proxy.c | 15 | ||||
-rw-r--r-- | lib/ssl_bogus.c | 5 | ||||
-rw-r--r-- | lib/ssl_client.h | 3 | ||||
-rw-r--r-- | lib/ssl_gnutls.c | 6 | ||||
-rw-r--r-- | lib/ssl_nss.c | 6 | ||||
-rw-r--r-- | lib/ssl_openssl.c | 23 | ||||
-rw-r--r-- | lib/ssl_sspi.c | 278 | ||||
-rw-r--r-- | lib/url.c | 24 | ||||
-rw-r--r-- | lib/url.h | 18 | ||||
-rw-r--r-- | lib/xmltree.c | 590 | ||||
-rw-r--r-- | lib/xmltree.h | 97 |
18 files changed, 1116 insertions, 60 deletions
diff --git a/lib/Makefile b/lib/Makefile index 975deceb..03fef1ab 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -9,7 +9,7 @@ -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 +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 @@ -130,18 +130,40 @@ unsigned char arc_getbyte( struct arc_state *st ) 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 arc_encode( char *clear, int clear_len, unsigned char **crypt, char *password, int pad_to ) { struct arc_state *st; unsigned char *key; - int key_len, i; + 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 ); @@ -160,6 +182,7 @@ int arc_encode( char *clear, int clear_len, unsigned char **crypt, char *passwor crypt[0][i+ARC_IV_LEN] = clear[i] ^ arc_getbyte( st ); g_free( st ); + g_free( padded ); return clear_len + ARC_IV_LEN; } @@ -30,7 +30,11 @@ struct arc_state unsigned char i, j; }; -struct arc_state *arc_keymaker( unsigned char *key, int kl, int cycles ); +#ifndef G_GNUC_MALLOC +#define G_GNUC_MALLOC +#endif + +G_GNUC_MALLOC struct arc_state *arc_keymaker( unsigned char *key, int kl, int cycles ); unsigned char arc_getbyte( struct arc_state *st ); -int arc_encode( char *clear, int clear_len, unsigned char **crypt, char *password ); +int 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 index 64e9692a..ea0db6b9 100644 --- a/lib/base64.c +++ b/lib/base64.c @@ -117,7 +117,7 @@ int base64_decode_real(const unsigned char *in, unsigned char *out, char *b64rev { int i, outlen = 0; - for( i = 0; in[i]; i += 4 ) + for( i = 0; in[i] && in[i+1] && in[i+2] && in[i+3]; i += 4 ) { int sx; diff --git a/lib/events_glib.c b/lib/events_glib.c index 1198dba6..3e194e98 100644 --- a/lib/events_glib.c +++ b/lib/events_glib.c @@ -50,11 +50,12 @@ typedef struct _GaimIOClosure { gpointer data; } GaimIOClosure; -static GMainLoop *loop; +static GMainLoop *loop = NULL; void b_main_init() { - loop = g_main_new( FALSE ); + if( loop == NULL ) + loop = g_main_new( FALSE ); } void b_main_run() @@ -32,6 +32,7 @@ #define BITLBEE_CORE #include "nogaim.h" +#include "base64.h" #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -60,31 +61,6 @@ void strip_linefeed(gchar *text) 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; @@ -407,6 +383,7 @@ signed int do_iconv( char *from_cs, char *to_cs, char *src, char *dst, size_t si lack of entropy won't halt BitlBee. */ void random_bytes( unsigned char *buf, int count ) { +#ifndef _WIN32 static int use_dev = -1; /* Actually this probing code isn't really necessary, is it? */ @@ -456,6 +433,7 @@ void random_bytes( unsigned char *buf, int count ) } if( !use_dev ) +#endif { int i; @@ -607,3 +585,39 @@ gboolean ssl_sockerr_again( void *ssl ) else return sockerr_again(); } + +/* Returns values: -1 == Failure (base64-decoded to something unexpected) + 0 == Okay + 1 == Password doesn't match the hash. */ +int md5_verify_password( char *password, char *hash ) +{ + md5_byte_t *pass_dec = NULL; + md5_byte_t pass_md5[16]; + md5_state_t md5_state; + int ret = -1, i; + + if( base64_decode( hash, &pass_dec ) == 21 ) + { + md5_init( &md5_state ); + md5_append( &md5_state, (md5_byte_t*) password, strlen( password ) ); + md5_append( &md5_state, (md5_byte_t*) pass_dec + 16, 5 ); /* Hmmm, salt! */ + md5_finish( &md5_state, pass_md5 ); + + for( i = 0; i < 16; i ++ ) + { + if( pass_dec[i] != pass_md5[i] ) + { + ret = 1; + break; + } + } + + /* If we reached the end of the loop, it was a match! */ + if( i == 16 ) + ret = 0; + } + + g_free( pass_dec ); + + return ret; +} @@ -40,7 +40,6 @@ struct ns_srv_reply 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 ); @@ -66,4 +65,6 @@ 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 index 0e1c8f07..91493557 100644 --- a/lib/proxy.c +++ b/lib/proxy.c @@ -113,6 +113,7 @@ static gboolean gaim_io_connected(gpointer data, gint source, b_input_condition static int proxy_connect_none(const char *host, unsigned short port, struct PHB *phb) { struct sockaddr_in *sin; + struct sockaddr_in me; int fd = -1; if (!(sin = gaim_gethostbyname(host, port))) { @@ -127,6 +128,16 @@ static int proxy_connect_none(const char *host, unsigned short port, struct PHB sock_make_nonblocking(fd); + if( global.conf->iface_out ) + { + me.sin_family = AF_INET; + me.sin_port = 0; + me.sin_addr.s_addr = inet_addr( global.conf->iface_out ); + + if( bind( fd, (struct sockaddr *) &me, sizeof( me ) ) != 0 ) + event_debug( "bind( %d, \"%s\" ) failure\n", fd, global.conf->iface_out ); + } + event_debug("proxy_connect_none( \"%s\", %d ) = %d\n", host, port, fd); if (connect(fd, (struct sockaddr *)sin, sizeof(*sin)) < 0 && !sockerr_again()) { @@ -529,7 +540,7 @@ int proxy_connect(const char *host, int port, b_event_handler func, gpointer dat { struct PHB *phb; - if (!host || !port || (port == -1) || !func || strlen(host) > 128) { + if (!host || port <= 0 || !func || strlen(host) > 128) { return -1; } @@ -537,7 +548,7 @@ int proxy_connect(const char *host, int port, b_event_handler func, gpointer dat phb->func = func; phb->data = data; - if ((proxytype == PROXY_NONE) || strlen(proxyhost) > 0 || !proxyport || (proxyport == -1)) + 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); diff --git a/lib/ssl_bogus.c b/lib/ssl_bogus.c index 391e634a..6eea18c7 100644 --- a/lib/ssl_bogus.c +++ b/lib/ssl_bogus.c @@ -64,3 +64,8 @@ b_input_condition ssl_getdirection( void *conn ) { return GAIM_INPUT_READ; } + +int ssl_pending( void *conn ) +{ + return 0; +} diff --git a/lib/ssl_client.h b/lib/ssl_client.h index 44fd658c..ef0b280c 100644 --- a/lib/ssl_client.h +++ b/lib/ssl_client.h @@ -62,6 +62,9 @@ G_MODULE_EXPORT void *ssl_starttls( int fd, ssl_input_function func, gpointer da 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_ ); diff --git a/lib/ssl_gnutls.c b/lib/ssl_gnutls.c index ae6f46a4..0d87b7ad 100644 --- a/lib/ssl_gnutls.c +++ b/lib/ssl_gnutls.c @@ -215,6 +215,12 @@ int ssl_write( void *conn, const char *buf, int len ) 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_; diff --git a/lib/ssl_nss.c b/lib/ssl_nss.c index 16560e63..c8bfd744 100644 --- a/lib/ssl_nss.c +++ b/lib/ssl_nss.c @@ -174,6 +174,12 @@ int ssl_write( void *conn, const char *buf, int len ) 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_; diff --git a/lib/ssl_openssl.c b/lib/ssl_openssl.c index 0ec9865f..cf81fb02 100644 --- a/lib/ssl_openssl.c +++ b/lib/ssl_openssl.c @@ -67,16 +67,16 @@ 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; } + conn->func = func; + conn->data = data; + conn->inpa = -1; + return conn; } @@ -235,6 +235,21 @@ int ssl_write( void *conn, const char *buf, int len ) 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_; diff --git a/lib/ssl_sspi.c b/lib/ssl_sspi.c new file mode 100644 index 00000000..a16423b1 --- /dev/null +++ b/lib/ssl_sspi.c @@ -0,0 +1,278 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2004 Wilmer van der Gaast and others * + \********************************************************************/ + +/* SSL module - SSPI backend */ + +/* Copyright (C) 2005 Jelmer Vernooij <jelmer@samba.org> */ + +/* + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "ssl_client.h" +#include <windows.h> +#define SECURITY_WIN32 +#include <security.h> +#include <sspi.h> +#include <schannel.h> +#include "sock.h" + +static gboolean initialized = FALSE; +int ssl_errno; + +struct scd +{ + int fd; + ssl_input_function func; + gpointer data; + gboolean established; + CredHandle cred; /* SSL credentials */ + CtxtHandle context; /* SSL context */ + SecPkgContext_StreamSizes sizes; + + char *host; + + char *pending_raw_data; + gsize pending_raw_data_len; + char *pending_data; + gsize pending_data_len; +}; + +static void ssl_connected(gpointer, gint, GaimInputCondition); + +void sspi_global_init(void) +{ + /* FIXME */ +} + +void sspi_global_deinit(void) +{ + /* FIXME */ +} + +void *ssl_connect(char *host, int port, ssl_input_function func, gpointer data) +{ + struct scd *conn = g_new0(struct scd, 1); + + conn->fd = proxy_connect(host, port, ssl_connected, conn); + sock_make_nonblocking(conn->fd); + conn->func = func; + conn->data = data; + conn->host = g_strdup(host); + + if (conn->fd < 0) + { + g_free(conn); + return NULL; + } + + if (!initialized) + { + sspi_global_init(); + initialized = TRUE; + atexit(sspi_global_deinit); + } + + return conn; +} + +static void ssl_connected(gpointer _conn, gint fd, GaimInputCondition cond) +{ + struct scd *conn = _conn; + SCHANNEL_CRED ssl_cred; + TimeStamp timestamp; + SecBuffer ibuf[2],obuf[1]; + SecBufferDesc ibufs,obufs; + ULONG req = ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT | + ISC_REQ_CONFIDENTIALITY | ISC_REQ_USE_SESSION_KEY | + ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_STREAM | ISC_REQ_EXTENDED_ERROR | + ISC_REQ_MANUAL_CRED_VALIDATION; + ULONG a; + gsize size = 0; + gchar *data = NULL; + + memset(&ssl_cred, 0, sizeof(SCHANNEL_CRED)); + ssl_cred.dwVersion = SCHANNEL_CRED_VERSION; + ssl_cred.grbitEnabledProtocols = SP_PROT_SSL3_CLIENT; + + SECURITY_STATUS st = AcquireCredentialsHandle(NULL, UNISP_NAME, SECPKG_CRED_OUTBOUND, NULL, &ssl_cred, NULL, NULL, &conn->cred, ×tamp); + + if (st != SEC_E_OK) { + conn->func(conn->data, NULL, cond); + return; + } + + do { + /* initialize buffers */ + ibuf[0].cbBuffer = size; ibuf[0].pvBuffer = data; + ibuf[1].cbBuffer = 0; ibuf[1].pvBuffer = NULL; + obuf[0].cbBuffer = 0; obuf[0].pvBuffer = NULL; + ibuf[0].BufferType = obuf[0].BufferType = SECBUFFER_TOKEN; + ibuf[1].BufferType = SECBUFFER_EMPTY; + + /* initialize buffer descriptors */ + ibufs.ulVersion = obufs.ulVersion = SECBUFFER_VERSION; + ibufs.cBuffers = 2; obufs.cBuffers = 1; + ibufs.pBuffers = ibuf; obufs.pBuffers = obuf; + + st = InitializeSecurityContext(&conn->cred, size?&conn->context:NULL, conn->host, req, 0, SECURITY_NETWORK_DREP, size?&ibufs:NULL, 0, &conn->context, &obufs, &a, ×tamp); + if (obuf[0].pvBuffer && obuf[0].cbBuffer) { + /* FIXME: Check return value */ + send(conn->fd, obuf[0].pvBuffer, obuf[0].cbBuffer, 0); + } + + switch (st) { + case SEC_I_INCOMPLETE_CREDENTIALS: + break; + case SEC_I_CONTINUE_NEEDED: + break; + case SEC_E_INCOMPLETE_MESSAGE: + break; + case SEC_E_OK: + break; + } + + QueryContextAttributes(&conn->context, SECPKG_ATTR_STREAM_SIZES, &conn->sizes); + } while (1); + + conn->func(conn->data, conn, cond); +} + +int ssl_read(void *conn, char *retdata, int len) +{ + struct scd *scd = conn; + SecBufferDesc msg; + SecBuffer buf[4]; + int ret = -1, i; + char *data = g_malloc(scd->sizes.cbHeader + scd->sizes.cbMaximumMessage + scd->sizes.cbTrailer); + + /* FIXME: Try to read some data */ + + msg.ulVersion = SECBUFFER_VERSION; + msg.cBuffers = 4; + msg.pBuffers = buf; + + buf[0].BufferType = SECBUFFER_DATA; + buf[0].cbBuffer = len; + buf[0].pvBuffer = data; + + buf[1].BufferType = SECBUFFER_EMPTY; + buf[2].BufferType = SECBUFFER_EMPTY; + buf[3].BufferType = SECBUFFER_EMPTY; + + SECURITY_STATUS st = DecryptMessage(&scd->context, &msg, 0, NULL); + + if (st != SEC_E_OK) { + /* FIXME */ + return -1; + } + + for (i = 0; i < 4; i++) { + if (buf[i].BufferType == SECBUFFER_DATA) { + memcpy(retdata, buf[i].pvBuffer, len); + ret = len; + } + } + + g_free(data); + return -1; +} + +int ssl_write(void *conn, const char *userdata, int len) +{ + struct scd *scd = conn; + SecBuffer buf[4]; + SecBufferDesc msg; + char *data; + int ret; + + msg.ulVersion = SECBUFFER_VERSION; + msg.cBuffers = 4; + msg.pBuffers = buf; + + data = g_malloc(scd->sizes.cbHeader + scd->sizes.cbMaximumMessage + scd->sizes.cbTrailer); + memcpy(data + scd->sizes.cbHeader, userdata, len); + + buf[0].BufferType = SECBUFFER_STREAM_HEADER; + buf[0].cbBuffer = scd->sizes.cbHeader; + buf[0].pvBuffer = data; + + buf[1].BufferType = SECBUFFER_DATA; + buf[1].cbBuffer = len; + buf[1].pvBuffer = data + scd->sizes.cbHeader; + + buf[2].BufferType = SECBUFFER_STREAM_TRAILER; + buf[2].cbBuffer = scd->sizes.cbTrailer; + buf[2].pvBuffer = data + scd->sizes.cbHeader + len; + buf[3].BufferType = SECBUFFER_EMPTY; + + SECURITY_STATUS st = EncryptMessage(&scd->context, 0, &msg, 0); + + ret = send(scd->fd, data, + buf[0].cbBuffer + buf[1].cbBuffer + buf[2].cbBuffer, 0); + + g_free(data); + + return ret; +} + +void ssl_disconnect(void *conn) +{ + struct scd *scd = conn; + + SecBufferDesc msg; + SecBuffer buf; + DWORD dw; + + dw = SCHANNEL_SHUTDOWN; + buf.cbBuffer = sizeof(dw); + buf.BufferType = SECBUFFER_TOKEN; + buf.pvBuffer = &dw; + + msg.ulVersion = SECBUFFER_VERSION; + msg.cBuffers = 1; + msg.pBuffers = &buf; + + SECURITY_STATUS st = ApplyControlToken(&scd->context, &msg); + + if (st != SEC_E_OK) { + /* FIXME */ + } + + /* FIXME: call InitializeSecurityContext(Schannel), passing + * in empty buffers*/ + + DeleteSecurityContext(&scd->context); + + FreeCredentialsHandle(&scd->cred); + + closesocket(scd->fd); + g_free(scd->host); + g_free(scd); +} + +int ssl_getfd(void *conn) +{ + return ((struct scd*)conn)->fd; +} + +GaimInputCondition ssl_getdirection( void *conn ) +{ + return GAIM_INPUT_WRITE; /* FIXME: or GAIM_INPUT_READ */ +} @@ -25,13 +25,16 @@ #include "url.h" -/* Convert an URL to a url_t structure */ +/* Convert an URL to a url_t structure */ int url_set( url_t *url, char *set_url ) { - char s[MAX_STRING]; + char s[MAX_STRING+1]; char *i; - /* protocol:// */ + memset( url, 0, sizeof( url_t ) ); + memset( s, 0, sizeof( s ) ); + + /* protocol:// */ if( ( i = strstr( set_url, "://" ) ) == NULL ) { url->proto = PROTO_DEFAULT; @@ -48,13 +51,12 @@ int url_set( url_t *url, char *set_url ) else if( g_strncasecmp( set_url, "socks5", i - set_url ) == 0 ) url->proto = PROTO_SOCKS5; else - { - return( 0 ); - } + return 0; + strncpy( s, i + 3, MAX_STRING ); } - /* Split */ + /* Split */ if( ( i = strchr( s, '/' ) ) == NULL ) { strcpy( url->file, "/" ); @@ -66,7 +68,7 @@ int url_set( url_t *url, char *set_url ) } strncpy( url->host, s, MAX_STRING ); - /* Check for username in host field */ + /* Check for username in host field */ if( strrchr( url->host, '@' ) != NULL ) { strncpy( url->user, url->host, MAX_STRING ); @@ -75,19 +77,19 @@ int url_set( url_t *url, char *set_url ) strcpy( url->host, i + 1 ); *url->pass = 0; } - /* If not: Fill in defaults */ + /* If not: Fill in defaults */ else { *url->user = *url->pass = 0; } - /* Password? */ + /* Password? */ if( ( i = strchr( url->user, ':' ) ) != NULL ) { *i = 0; strcpy( url->pass, i + 1 ); } - /* Port number? */ + /* Port number? */ if( ( i = strchr( url->host, ':' ) ) != NULL ) { *i = 0; @@ -25,20 +25,20 @@ #include "bitlbee.h" -#define PROTO_HTTP 2 -#define PROTO_HTTPS 5 -#define PROTO_SOCKS4 3 -#define PROTO_SOCKS5 4 -#define PROTO_DEFAULT PROTO_HTTP +#define PROTO_HTTP 2 +#define PROTO_HTTPS 5 +#define PROTO_SOCKS4 3 +#define PROTO_SOCKS5 4 +#define PROTO_DEFAULT PROTO_HTTP typedef struct url { int proto; int port; - char host[MAX_STRING]; - char file[MAX_STRING]; - char user[MAX_STRING]; - char pass[MAX_STRING]; + 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 |