aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/http_client.c69
-rw-r--r--lib/ssl_client.h10
-rw-r--r--lib/ssl_gnutls.c26
-rw-r--r--lib/ssl_nss.c1
-rw-r--r--lib/ssl_openssl.c9
-rw-r--r--lib/xmltree.c47
6 files changed, 116 insertions, 46 deletions
diff --git a/lib/http_client.c b/lib/http_client.c
index 8b045414..9d986412 100644
--- a/lib/http_client.c
+++ b/lib/http_client.c
@@ -1,7 +1,7 @@
/********************************************************************\
* BitlBee -- An IRC to other IM-networks gateway *
* *
- * Copyright 2002-2005 Wilmer van der Gaast and others *
+ * Copyright 2002-2011 Wilmer van der Gaast and others *
\********************************************************************/
/* HTTP(S) module */
@@ -69,6 +69,9 @@ struct http_request *http_dorequest( char *host, int port, int ssl, char *reques
req->request_length = strlen( request );
req->redir_ttl = 3;
+ if( getenv( "BITLBEE_DEBUG" ) )
+ printf( "About to send HTTP request:\n%s\n", req->request );
+
return( req );
}
@@ -239,7 +242,10 @@ static gboolean http_incoming_data( gpointer data, int source, b_input_condition
req->ssl ? ssl_getdirection( req->ssl ) : B_EV_IO_READ,
http_incoming_data, req );
- return FALSE;
+ if( ssl_pending( req->ssl ) )
+ return http_incoming_data( data, source, cond );
+ else
+ return FALSE;
got_reply:
/* Maybe if the webserver is overloaded, or when there's bad SSL
@@ -275,6 +281,9 @@ got_reply:
*end1 = 0;
+ if( getenv( "BITLBEE_DEBUG" ) )
+ printf( "HTTP response headers:\n%s\n", req->reply_headers );
+
if( evil_server )
req->reply_body = end1 + 1;
else
@@ -313,7 +322,8 @@ got_reply:
req->status_code = -1;
}
- if( ( req->status_code == 301 || req->status_code == 302 ) && req->redir_ttl-- > 0 )
+ if( ( ( req->status_code >= 301 && req->status_code <= 303 ) ||
+ req->status_code == 307 ) && req->redir_ttl-- > 0 )
{
char *loc, *new_request, *new_host;
int error = 0, new_port, new_proto;
@@ -353,6 +363,7 @@ got_reply:
/* A whole URL */
url_t *url;
char *s;
+ const char *new_method;
s = strstr( loc, "\r\n" );
if( s == NULL )
@@ -368,28 +379,49 @@ got_reply:
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 )
+ /* Find all headers and, if necessary, the POST request contents.
+ Skip the old Host: header though. This crappy code here means
+ anything using this http_client MUST put the Host: header at
+ the top. */
+ if( !( ( s = strstr( req->request, "\r\nHost: " ) ) &&
+ ( s = strstr( s + strlen( "\r\nHost: " ), "\r\n" ) ) ) )
{
req->status_string = g_strdup( "Error while rebuilding request string" );
- g_free( new_request );
g_free( url );
goto cleanup;
}
- strcat( new_request, s );
+ /* More or less HTTP/1.0 compliant, from my reading of RFC 2616.
+ Always perform a GET request unless we received a 301. 303 was
+ meant for this but it's HTTP/1.1-only and we're specifically
+ speaking HTTP/1.0. ...
+
+ Well except someone at identi.ca's didn't bother reading any
+ RFCs and just return HTTP/1.1-specific status codes to HTTP/1.0
+ requests. Fuckers. So here we are, handle 301..303,307. */
+ if( strncmp( req->request, "GET", 3 ) == 0 )
+ /* GETs never become POSTs. */
+ new_method = "GET";
+ else if( req->status_code == 302 || req->status_code == 303 )
+ /* 302 de-facto becomes GET, 303 as specified by RFC 2616#10.3.3 */
+ new_method = "GET";
+ else
+ /* 301 de-facto should stay POST, 307 specifally RFC 2616#10.3.8 */
+ new_method = "POST";
+
+ /* Okay, this isn't fun! We have to rebuild the request... :-( */
+ new_request = g_strdup_printf( "%s %s HTTP/1.0\r\nHost: %s%s",
+ new_method, url->file, url->host, s );
+
new_host = g_strdup( url->host );
new_port = url->port;
new_proto = url->proto;
+ /* If we went from POST to GET, truncate the request content. */
+ if( new_request[0] != req->request[0] && new_request[0] == 'G' &&
+ ( s = strstr( new_request, "\r\n\r\n" ) ) )
+ s[4] = '\0';
+
g_free( url );
}
@@ -401,6 +433,9 @@ got_reply:
req->fd = -1;
req->ssl = NULL;
+ if( getenv( "BITLBEE_DEBUG" ) )
+ printf( "New headers for redirected HTTP request:\n%s\n", new_request );
+
if( new_proto == PROTO_HTTPS )
{
req->ssl = ssl_connect( new_host, new_port, http_ssl_connected, req );
@@ -442,6 +477,10 @@ cleanup:
else
closesocket( req->fd );
+ if( getenv( "BITLBEE_DEBUG" ) && req )
+ printf( "Finishing HTTP request with status: %s\n",
+ req->status_string ? req->status_string : "NULL" );
+
req->func( req );
http_free( req );
return FALSE;
diff --git a/lib/ssl_client.h b/lib/ssl_client.h
index d0340840..091335c5 100644
--- a/lib/ssl_client.h
+++ b/lib/ssl_client.h
@@ -62,7 +62,15 @@ 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. */
+/* Now needed by most SSL libs. 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. Or maybe you didn't ask
+ for all of it because your buffer is too small.
+
+ Returns 0 if there's nothing left, 1 if there's more data. */
G_MODULE_EXPORT int ssl_pending( void *conn );
/* Abort the SSL connection and disconnect the socket. Do not use close()
diff --git a/lib/ssl_gnutls.c b/lib/ssl_gnutls.c
index 72517e72..ccab8aca 100644
--- a/lib/ssl_gnutls.c
+++ b/lib/ssl_gnutls.c
@@ -44,6 +44,8 @@ static gboolean initialized = FALSE;
#define GNUTLS_STUPID_CAST (int)
#endif
+#define SSLDEBUG 0
+
struct scd
{
ssl_input_function func;
@@ -134,7 +136,9 @@ static gboolean ssl_connected( gpointer data, gint source, b_input_condition con
gnutls_certificate_allocate_credentials( &conn->xcred );
gnutls_init( &conn->session, GNUTLS_CLIENT );
- gnutls_transport_set_lowat( conn->session, 1 );
+#if GNUTLS_VERSION_NUMBER < 0x020c00
+ gnutls_transport_set_lowat( conn->session, 0 );
+#endif
gnutls_set_default_priority( conn->session );
gnutls_credentials_set( conn->session, GNUTLS_CRD_CERTIFICATE, conn->xcred );
@@ -186,7 +190,7 @@ int ssl_read( void *conn, char *buf, int len )
if( !((struct scd*)conn)->established )
{
ssl_errno = SSL_NOHANDSHAKE;
- return( -1 );
+ return -1;
}
st = gnutls_record_recv( ((struct scd*)conn)->session, buf, len );
@@ -195,7 +199,7 @@ int ssl_read( void *conn, char *buf, int len )
if( st == GNUTLS_E_AGAIN || st == GNUTLS_E_INTERRUPTED )
ssl_errno = SSL_AGAIN;
- if( 0 && getenv( "BITLBEE_DEBUG" ) && st > 0 ) len = write( 1, buf, st );
+ if( SSLDEBUG && getenv( "BITLBEE_DEBUG" ) && st > 0 ) len = write( 2, buf, st );
return st;
}
@@ -207,7 +211,7 @@ int ssl_write( void *conn, const char *buf, int len )
if( !((struct scd*)conn)->established )
{
ssl_errno = SSL_NOHANDSHAKE;
- return( -1 );
+ return -1;
}
st = gnutls_record_send( ((struct scd*)conn)->session, buf, len );
@@ -216,15 +220,23 @@ int ssl_write( void *conn, const char *buf, int len )
if( st == GNUTLS_E_AGAIN || st == GNUTLS_E_INTERRUPTED )
ssl_errno = SSL_AGAIN;
- if( 0 && getenv( "BITLBEE_DEBUG" ) && st > 0 ) len = write( 1, buf, st );
+ if( SSLDEBUG && getenv( "BITLBEE_DEBUG" ) && st > 0 ) len = write( 2, buf, st );
return st;
}
-/* See ssl_openssl.c for an explanation. */
int ssl_pending( void *conn )
{
- return 0;
+ if( conn == NULL )
+ return 0;
+
+ if( !((struct scd*)conn)->established )
+ {
+ ssl_errno = SSL_NOHANDSHAKE;
+ return 0;
+ }
+
+ return gnutls_record_check_pending( ((struct scd*)conn)->session ) != 0;
}
void ssl_disconnect( void *conn_ )
diff --git a/lib/ssl_nss.c b/lib/ssl_nss.c
index 512c7655..ec524ca6 100644
--- a/lib/ssl_nss.c
+++ b/lib/ssl_nss.c
@@ -206,7 +206,6 @@ 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 )
{
struct scd *c = (struct scd *) conn;
diff --git a/lib/ssl_openssl.c b/lib/ssl_openssl.c
index 64bc9257..5f64042d 100644
--- a/lib/ssl_openssl.c
+++ b/lib/ssl_openssl.c
@@ -240,15 +240,6 @@ 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 ) ?
diff --git a/lib/xmltree.c b/lib/xmltree.c
index 54a7dd13..e2654579 100644
--- a/lib/xmltree.c
+++ b/lib/xmltree.c
@@ -322,7 +322,6 @@ char *xt_to_string( struct xt_node *node )
return real;
}
-#ifdef DEBUG
void xt_print( struct xt_node *node )
{
int i;
@@ -330,16 +329,16 @@ void xt_print( struct xt_node *node )
/* Indentation */
for( c = node; c->parent; c = c->parent )
- printf( " " );
+ fprintf( stderr, " " );
/* Start the tag */
- printf( "<%s", node->name );
+ fprintf( stderr, "<%s", node->name );
/* Print the attributes */
for( i = 0; node->attr[i].key; i ++ )
{
char *v = g_markup_escape_text( node->attr[i].value, -1 );
- printf( " %s=\"%s\"", node->attr[i].key, v );
+ fprintf( stderr, " %s=\"%s\"", node->attr[i].key, v );
g_free( v );
}
@@ -348,13 +347,13 @@ void xt_print( struct xt_node *node )
/* If this tag doesn't have any content at all... */
if( node->text == NULL && node->children == NULL )
{
- printf( "/>\n" );
+ fprintf( stderr, "/>\n" );
return;
/* Then we're finished! */
}
/* Otherwise... */
- printf( ">" );
+ fprintf( stderr, ">" );
/* Only print the text if it contains more than whitespace (TEST). */
if( node->text_len > 0 )
@@ -363,25 +362,24 @@ void xt_print( struct xt_node *node )
if( node->text[i] )
{
char *v = g_markup_escape_text( node->text, -1 );
- printf( "%s", v );
+ fprintf( stderr, "%s", v );
g_free( v );
}
}
if( node->children )
- printf( "\n" );
+ fprintf( stderr, "\n" );
for( c = node->children; c; c = c->next )
xt_print( c );
if( node->children )
for( c = node; c->parent; c = c->parent )
- printf( " " );
+ fprintf( stderr, " " );
/* Non-empty tag is now finished. */
- printf( "</%s>\n", node->name );
+ fprintf( stderr, "</%s>\n", node->name );
}
-#endif
struct xt_node *xt_dup( struct xt_node *node )
{
@@ -556,6 +554,28 @@ char *xt_find_attr( struct xt_node *node, const char *key )
return node->attr[i].value;
}
+/* Strip a few non-printable characters that aren't allowed in XML streams
+ (and upset some XMPP servers for example). */
+void xt_strip_text( char *in )
+{
+ char *out = in;
+ static const char nonprint[32] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, /* 0..7 */
+ 0, 1, 1, 0, 0, 1, 0, 0, /* 9 (tab), 10 (\n), 13 (\r) */
+ };
+
+ if( !in )
+ return;
+
+ while( *in )
+ {
+ if( (unsigned int) *in >= ' ' || nonprint[(unsigned int) *in] )
+ *out ++ = *in;
+ in ++;
+ }
+ *out = *in;
+}
+
struct xt_node *xt_new_node( char *name, const char *text, struct xt_node *children )
{
struct xt_node *node, *c;
@@ -567,8 +587,9 @@ struct xt_node *xt_new_node( char *name, const char *text, struct xt_node *child
if( text )
{
- node->text_len = strlen( text );
- node->text = g_memdup( text, node->text_len + 1 );
+ node->text = g_strdup( text );
+ xt_strip_text( node->text );
+ node->text_len = strlen( node->text );
}
for( c = children; c; c = c->next )