aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--protocols/Makefile2
-rw-r--r--protocols/http_client.c375
-rw-r--r--protocols/http_client.h54
-rw-r--r--protocols/proxy.c2
-rw-r--r--protocols/ssl_bogus.c2
-rw-r--r--protocols/ssl_client.h5
-rw-r--r--protocols/ssl_gnutls.c33
-rw-r--r--protocols/ssl_nss.c4
-rw-r--r--protocols/ssl_openssl.c4
-rw-r--r--sock.h3
-rw-r--r--url.c48
-rw-r--r--url.h3
12 files changed, 480 insertions, 55 deletions
diff --git a/protocols/Makefile b/protocols/Makefile
index c5f938fd..1ed6b52e 100644
--- a/protocols/Makefile
+++ b/protocols/Makefile
@@ -9,7 +9,7 @@
-include ../Makefile.settings
# [SH] Program variables
-objects = md5.o nogaim.o proxy.o sha.o util.o $(SSL_CLIENT)
+objects = http_client.o md5.o nogaim.o proxy.o sha.o $(SSL_CLIENT) util.o
# [SH] The next two lines should contain the directory name (in $(subdirs))
# and the name of the object file, which should be linked into
diff --git a/protocols/http_client.c b/protocols/http_client.c
new file mode 100644
index 00000000..fa735426
--- /dev/null
+++ b/protocols/http_client.c
@@ -0,0 +1,375 @@
+ /********************************************************************\
+ * 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 "sock.h"
+#include "http_client.h"
+#include "url.h"
+
+
+static void http_connected( gpointer data, int source, GaimInputCondition cond );
+static void http_ssl_connected( gpointer data, void *source, GaimInputCondition cond );
+static void http_incoming_data( gpointer data, int source, GaimInputCondition cond );
+
+
+void *http_dorequest( char *host, int port, http_input_function func, int ssl, char *request, 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 );
+}
+
+/* This one is actually pretty simple... Might get more calls if we can't write
+ the whole request at once. */
+static void http_connected( gpointer data, int source, GaimInputCondition cond )
+{
+ struct http_request *req = data;
+ int st;
+
+ if( source < 0 )
+ goto error;
+
+ if( req->inpa > 0 )
+ gaim_input_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 = gaim_input_add( source,
+ req->ssl ? ssl_getdirection( req->ssl ) : GAIM_INPUT_WRITE,
+ http_connected, req );
+ else
+ req->inpa = gaim_input_add( source, GAIM_INPUT_READ, http_incoming_data, req );
+
+ return;
+
+error:
+ req->func( req );
+
+ g_free( req->request );
+ g_free( req );
+
+ return;
+}
+
+static void http_ssl_connected( gpointer data, void *source, GaimInputCondition 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 void http_incoming_data( gpointer data, int source, GaimInputCondition cond )
+{
+ struct http_request *req = data;
+ int evil_server = 0;
+ char buffer[2048];
+ char *end1, *end2;
+ int st;
+
+ if( req->inpa > 0 )
+ gaim_input_remove( req->inpa );
+
+ if( req->ssl )
+ {
+ st = ssl_read( req->ssl, buffer, sizeof( buffer ) );
+ if( st < 0 )
+ {
+ if( ssl_errno != SSL_AGAIN )
+ {
+ goto cleanup;
+ }
+ }
+ else if( st == 0 )
+ {
+ goto got_reply;
+ }
+ }
+ else
+ {
+ st = read( req->fd, buffer, sizeof( buffer ) );
+ if( st < 0 )
+ {
+ if( !sockerr_again() )
+ {
+ 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 = gaim_input_add( req->fd,
+ req->ssl ? ssl_getdirection( req->ssl ) : GAIM_INPUT_READ,
+ http_incoming_data, req );
+
+ return;
+
+got_reply:
+ /* 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
+ {
+ end1 += 2;
+ }
+
+ if( end1 )
+ {
+ *end1 = 0;
+
+ if( evil_server )
+ req->reply_body = end1 + 1;
+ else
+ req->reply_body = end1 + 2;
+ }
+
+ if( ( end1 = strchr( req->reply_headers, ' ' ) ) != NULL )
+ {
+ if( sscanf( end1 + 1, "%d", &req->status_code ) != 1 )
+ req->status_code = -1;
+ }
+ else
+ {
+ 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;
+
+ loc = strstr( req->reply_headers, "\nLocation: " );
+ if( loc == NULL ) /* We can't handle this redirect... */
+ 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. */
+
+ 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 ) )
+ {
+ 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. :-) */
+
+ /* First, find the GET/POST/whatever from the original request. */
+ s = strchr( req->request, ' ' );
+ if( s == NULL )
+ {
+ g_free( new_request );
+ g_free( url );
+ goto cleanup;
+ }
+
+ *s = 0;
+ sprintf( new_request, "%s %s HTTP/1.0\r\n", req->request, url->file );
+ *s = ' ';
+
+ s = strstr( req->request, "\r\n" );
+ if( s == NULL )
+ {
+ g_free( new_request );
+ g_free( url );
+ goto cleanup;
+ }
+
+ strcat( new_request, s + 2 );
+ 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 = 0;
+
+ 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;
+ }
+
+ if( error )
+ {
+ 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;
+ }
+
+ /* 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 );
+}
diff --git a/protocols/http_client.h b/protocols/http_client.h
new file mode 100644
index 00000000..440fdc9d
--- /dev/null
+++ b/protocols/http_client.h
@@ -0,0 +1,54 @@
+ /********************************************************************\
+ * BitlBee -- An IRC to other IM-networks gateway *
+ * *
+ * Copyright 2002-2005 Wilmer van der Gaast and others *
+ \********************************************************************/
+
+/* HTTP(S) module (actually, it only does HTTPS right now) */
+
+/*
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License with
+ the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
+ if not, write to the Free Software Foundation, Inc., 59 Temple Place,
+ Suite 330, Boston, MA 02111-1307 USA
+*/
+
+#include <glib.h>
+
+#include "ssl_client.h"
+
+struct http_request;
+
+typedef void (*http_input_function)( struct http_request * );
+
+struct http_request
+{
+ char *request;
+ int request_length;
+ int status_code;
+ char *reply_headers;
+ char *reply_body;
+ int finished;
+
+ void *ssl;
+ int fd;
+
+ int inpa;
+ int bytes_written;
+ int bytes_read;
+
+ http_input_function func;
+ gpointer data;
+};
+
+void *http_dorequest( char *host, int port, http_input_function func, int ssl, char *request, gpointer data );
diff --git a/protocols/proxy.c b/protocols/proxy.c
index c658a163..1ca35dfe 100644
--- a/protocols/proxy.c
+++ b/protocols/proxy.c
@@ -105,8 +105,6 @@ static gboolean gaim_io_invoke(GIOChannel *source, GIOCondition condition, gpoin
gaim_cond |= GAIM_INPUT_READ;
if (condition & GAIM_WRITE_COND)
gaim_cond |= GAIM_INPUT_WRITE;
-// if (condition & GAIM_ERR_COND)
-// fprintf( stderr, "ERROR! fd=%d\n", g_io_channel_unix_get_fd( source ) );
closure->function(closure->data, g_io_channel_unix_get_fd(source), gaim_cond);
diff --git a/protocols/ssl_bogus.c b/protocols/ssl_bogus.c
index 1ee0df4c..3766baaa 100644
--- a/protocols/ssl_bogus.c
+++ b/protocols/ssl_bogus.c
@@ -27,7 +27,7 @@
int ssl_errno;
-void *ssl_connect( char *host, int port, SslInputFunction func, gpointer data )
+void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data )
{
return( NULL );
}
diff --git a/protocols/ssl_client.h b/protocols/ssl_client.h
index 719cd0c4..89189db9 100644
--- a/protocols/ssl_client.h
+++ b/protocols/ssl_client.h
@@ -32,10 +32,11 @@
extern int ssl_errno;
-typedef void (*SslInputFunction)(gpointer, void*, GaimInputCondition);
+typedef void (*ssl_input_function)(gpointer, void*, GaimInputCondition);
-G_MODULE_EXPORT void *ssl_connect( char *host, int port, SslInputFunction func, gpointer data );
+G_MODULE_EXPORT void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data );
G_MODULE_EXPORT int ssl_read( void *conn, char *buf, int len );
G_MODULE_EXPORT int ssl_write( void *conn, const char *buf, int len );
G_MODULE_EXPORT void ssl_disconnect( void *conn_ );
G_MODULE_EXPORT int ssl_getfd( void *conn );
+G_MODULE_EXPORT GaimInputCondition ssl_getdirection( void *conn );
diff --git a/protocols/ssl_gnutls.c b/protocols/ssl_gnutls.c
index c2eb6906..2e307aab 100644
--- a/protocols/ssl_gnutls.c
+++ b/protocols/ssl_gnutls.c
@@ -37,7 +37,7 @@ static gboolean initialized = FALSE;
struct scd
{
- SslInputFunction func;
+ ssl_input_function func;
gpointer data;
int fd;
gboolean established;
@@ -50,7 +50,7 @@ struct scd
static void ssl_connected( gpointer data, gint source, GaimInputCondition cond );
-void *ssl_connect( char *host, int port, SslInputFunction func, gpointer data )
+void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data )
{
struct scd *conn = g_new0( struct scd, 1 );
@@ -116,9 +116,7 @@ static void ssl_handshake( gpointer data, gint source, GaimInputCondition cond )
{
if( st == GNUTLS_E_AGAIN || st == GNUTLS_E_INTERRUPTED )
{
- conn->inpa = gaim_input_add( conn->fd,
- gnutls_record_get_direction( conn->session ) ?
- GAIM_INPUT_WRITE : GAIM_INPUT_READ,
+ conn->inpa = gaim_input_add( conn->fd, ssl_getdirection( conn ),
ssl_handshake, data );
}
else
@@ -144,25 +142,40 @@ static void ssl_handshake( gpointer data, gint source, GaimInputCondition cond )
int ssl_read( void *conn, char *buf, int len )
{
+ int st;
+
if( !((struct scd*)conn)->established )
{
ssl_errno = SSL_NOHANDSHAKE;
return( -1 );
}
- return( gnutls_record_recv( ((struct scd*)conn)->session, buf, len ) );
+ 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 );
}
- return( gnutls_record_send( ((struct scd*)conn)->session, buf, len ) );
+ st = gnutls_record_send( ((struct scd*)conn)->session, buf, len );
+
+ ssl_errno = SSL_OK;
+ if( st == GNUTLS_E_AGAIN || st == GNUTLS_E_INTERRUPTED )
+ ssl_errno = SSL_AGAIN;
+
+ return st;
}
void ssl_disconnect( void *conn_ )
@@ -183,3 +196,9 @@ int ssl_getfd( void *conn )
{
return( ((struct scd*)conn)->fd );
}
+
+GaimInputCondition ssl_getdirection( void *conn )
+{
+ return( gnutls_record_get_direction( ((struct scd*)conn)->session ) ?
+ GAIM_INPUT_WRITE : GAIM_INPUT_READ );
+}
diff --git a/protocols/ssl_nss.c b/protocols/ssl_nss.c
index d28983fc..dfd32622 100644
--- a/protocols/ssl_nss.c
+++ b/protocols/ssl_nss.c
@@ -44,7 +44,7 @@ static gboolean initialized = FALSE;
struct scd
{
- SslInputFunction func;
+ ssl_input_function func;
gpointer data;
int fd;
PRFileDesc *prfd;
@@ -90,7 +90,7 @@ static SECStatus nss_bad_cert (void *arg, PRFileDesc *socket)
}
-void *ssl_connect( char *host, int port, SslInputFunction func, gpointer data )
+void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data )
{
struct scd *conn = g_new0( struct scd, 1 );
diff --git a/protocols/ssl_openssl.c b/protocols/ssl_openssl.c
index bf87ab73..5a107fc5 100644
--- a/protocols/ssl_openssl.c
+++ b/protocols/ssl_openssl.c
@@ -40,7 +40,7 @@ static gboolean initialized = FALSE;
struct scd
{
- SslInputFunction func;
+ ssl_input_function func;
gpointer data;
int fd;
gboolean established;
@@ -53,7 +53,7 @@ static void ssl_connected( gpointer data, gint source, GaimInputCondition cond )
-void *ssl_connect( char *host, int port, SslInputFunction func, gpointer data )
+void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data )
{
struct scd *conn = g_new0( struct scd, 1 );
SSL_METHOD *meth;
diff --git a/sock.h b/sock.h
index 9a9cc223..23a08bb4 100644
--- a/sock.h
+++ b/sock.h
@@ -1,3 +1,6 @@
+#include <errno.h>
+#include <fcntl.h>
+
#ifndef _WIN32
#include <unistd.h>
#include <sys/socket.h>
diff --git a/url.c b/url.c
index 61e7f370..816f4ef3 100644
--- a/url.c
+++ b/url.c
@@ -1,7 +1,7 @@
/********************************************************************\
* BitlBee -- An IRC to other IM-networks gateway *
* *
- * Copyright 2001-2004 Wilmer van der Gaast and others *
+ * Copyright 2001-2005 Wilmer van der Gaast and others *
\********************************************************************/
/* URL/mirror stuff - Stolen from Axel */
@@ -29,7 +29,7 @@
int url_set( url_t *url, char *set_url )
{
char s[MAX_STRING];
- char *i, *j;
+ char *i;
/* protocol:// */
if( ( i = strstr( set_url, "://" ) ) == NULL )
@@ -39,7 +39,9 @@ int url_set( url_t *url, char *set_url )
}
else
{
- if( g_strncasecmp( set_url, "http", i - set_url ) == 0 )
+ if( g_strncasecmp( set_url, "https", i - set_url ) == 0 )
+ url->proto = PROTO_HTTPS;
+ else if( g_strncasecmp( set_url, "http", i - set_url ) == 0 )
url->proto = PROTO_HTTP;
else if( g_strncasecmp( set_url, "socks4", i - set_url ) == 0 )
url->proto = PROTO_SOCKS4;
@@ -55,33 +57,14 @@ int url_set( url_t *url, char *set_url )
/* Split */
if( ( i = strchr( s, '/' ) ) == NULL )
{
- strcpy( url->dir, "/" );
+ strcpy( url->file, "/" );
}
else
{
+ strncpy( url->file, i, MAX_STRING );
*i = 0;
- g_snprintf( url->dir, MAX_STRING, "/%s", i + 1 );
- if( url->proto == PROTO_HTTP )
- http_encode( url->dir );
}
strncpy( url->host, s, MAX_STRING );
- j = strchr( url->dir, '?' );
- if( j != NULL )
- *j = 0;
- i = strrchr( url->dir, '/' );
- *i = 0;
- if( j != NULL )
- *j = '?';
- if( i == NULL )
- {
- strcpy( url->file, url->dir );
- strcpy( url->dir, "/" );
- }
- else
- {
- strcpy( url->file, i + 1 );
- strcat( url->dir, "/" );
- }
/* Check for username in host field */
if( strrchr( url->host, '@' ) != NULL )
@@ -95,15 +78,7 @@ int url_set( url_t *url, char *set_url )
/* If not: Fill in defaults */
else
{
- if( url->proto == PROTO_FTP )
- {
- strcpy( url->user, "anonymous" );
- strcpy( url->pass, "-p.artmaps@lintux.cx" );
- }
- else
- {
- *url->user = *url->pass = 0;
- }
+ *url->user = *url->pass = 0;
}
/* Password? */
@@ -116,13 +91,14 @@ int url_set( url_t *url, char *set_url )
if( ( i = strchr( url->host, ':' ) ) != NULL )
{
*i = 0;
- sscanf( i + 1, "%i", &url->port );
+ sscanf( i + 1, "%d", &url->port );
}
- /* Take default port numbers from /etc/services */
else
{
if( url->proto == PROTO_HTTP )
- url->port = 8080;
+ url->port = 80;
+ else if( url->proto == PROTO_HTTPS )
+ url->port = 443;
else if( url->proto == PROTO_SOCKS4 || url->proto == PROTO_SOCKS4 )
url->port = 1080;
}
diff --git a/url.h b/url.h
index c8426876..e9e1ecfe 100644
--- a/url.h
+++ b/url.h
@@ -25,8 +25,8 @@
#include "bitlbee.h"
-#define PROTO_FTP 1
#define PROTO_HTTP 2
+#define PROTO_HTTPS 5
#define PROTO_SOCKS4 3
#define PROTO_SOCKS5 4
#define PROTO_DEFAULT PROTO_HTTP
@@ -36,7 +36,6 @@ typedef struct url
int proto;
int port;
char host[MAX_STRING];
- char dir[MAX_STRING];
char file[MAX_STRING];
char user[MAX_STRING];
char pass[MAX_STRING];