diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/http_client.c | 69 | ||||
| -rw-r--r-- | lib/ssl_client.h | 10 | ||||
| -rw-r--r-- | lib/ssl_gnutls.c | 26 | ||||
| -rw-r--r-- | lib/ssl_nss.c | 1 | ||||
| -rw-r--r-- | lib/ssl_openssl.c | 9 | ||||
| -rw-r--r-- | lib/xmltree.c | 47 | 
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 ) | 
