Index: lib/http_client.c
===================================================================
--- lib/http_client.c	(revision 11ec07811cc41e1b244467d25772ef021be9db1b)
+++ lib/http_client.c	(revision 8bd866f3d336cb695006bc48592cf7475139aa36)
@@ -193,10 +193,11 @@
 }
 
+static gboolean http_handle_headers( struct http_request *req );
+
 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, *s;
+	char *s;
 	size_t content_length;
 	int st;
@@ -218,10 +219,10 @@
 				   packets that abort connections! \o/ */
 				
-				goto got_reply;
+				goto eof;
 			}
 		}
 		else if( st == 0 )
 		{
-			goto got_reply;
+			goto eof;
 		}
 	}
@@ -239,14 +240,56 @@
 		else if( st == 0 )
 		{
-			goto got_reply;
-		}
-	}
-	
-	if( st > 0 )
+			goto eof;
+		}
+	}
+	
+	if( st > 0 && !req->sbuf )
 	{
 		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;
-	}
+		
+		st = 0;
+	}
+	
+	if( st >= 0 && ( req->flags & HTTPC_STREAMING ) )
+	{
+		if( !req->reply_body &&
+		    ( strstr( req->reply_headers, "\r\n\r\n" ) ||
+		      strstr( req->reply_headers, "\n\n" ) ) )
+		{
+			size_t hlen;
+			
+			/* We've now received all headers, so process them once
+			   before we start feeding back data. */
+			if( !http_handle_headers( req ) )
+				return FALSE;
+			
+			hlen = req->reply_body - req->reply_headers;
+			
+			req->sblen = req->bytes_read - hlen;
+			req->sbuf = g_memdup( req->reply_body, req->sblen + 1 );
+			req->reply_headers = g_realloc( req->reply_headers, hlen + 1 );
+			
+			req->reply_body = req->sbuf;
+		}
+		
+		if( st > 0 )
+		{
+			int pos = req->reply_body - req->sbuf;
+			req->sbuf = g_realloc( req->sbuf, req->sblen + st + 1 );
+			memcpy( req->sbuf + req->sblen, buffer, st );
+			req->bytes_read += st;
+			req->sblen += st;
+			req->reply_body = req->sbuf + pos;
+			req->body_size = req->sblen - pos;
+		}
+		
+		if( req->reply_body )
+			req->func( req );
+	}
+	
+	if( ssl_pending( req->ssl ) )
+		return http_incoming_data( data, source, cond );
 	
 	/* There will be more! */
@@ -255,10 +298,7 @@
 	                         http_incoming_data, req );
 	
-	if( ssl_pending( req->ssl ) )
-		return http_incoming_data( data, source, cond );
-	else
-		return FALSE;
-
-got_reply:
+	return FALSE;
+
+eof:
 	/* Maybe if the webserver is overloaded, or when there's bad SSL
 	   support... */
@@ -269,6 +309,48 @@
 	}
 	
+	if( !( req->flags & HTTPC_STREAMING ) )
+	{
+		/* Returns FALSE if we were redirected, in which case we should abort
+		   and not run any callback yet. */
+		if( !http_handle_headers( req ) )
+			return FALSE;
+	}
+
+cleanup:
+	if( req->ssl )
+		ssl_disconnect( req->ssl );
+	else
+		closesocket( req->fd );
+	
+	if( ( s = get_rfc822_header( req->reply_headers, "Content-Length", 0 ) ) &&
+	    sscanf( s, "%zd", &content_length ) == 1 )
+	{
+		if( content_length < req->body_size )
+		{
+			req->status_code = -1;
+			g_free( req->status_string );
+			req->status_string = g_strdup( "Response truncated" );
+		}
+	}
+	g_free( s );
+	
+	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;
+}
+
+/* Splits headers and body. Checks result code, in case of 300s it'll handle
+   redirects. If this returns FALSE, don't call any callbacks! */
+static gboolean http_handle_headers( struct http_request *req )
+{
+	char *end1, *end2;
+	int evil_server = 0;
+	
 	/* Zero termination is very convenient. */
-	req->reply_headers[req->bytes_read] = 0;
+	req->reply_headers[req->bytes_read] = '\0';
 	
 	/* Find the separation between headers and body, and keep stupid
@@ -289,5 +371,5 @@
 	{
 		req->status_string = g_strdup( "Malformed HTTP reply" );
-		goto cleanup;
+		return TRUE;
 	}
 	
@@ -306,5 +388,5 @@
 	if( ( end1 = strchr( req->reply_headers, ' ' ) ) != NULL )
 	{
-		if( sscanf( end1 + 1, "%d", &req->status_code ) != 1 )
+		if( sscanf( end1 + 1, "%hd", &req->status_code ) != 1 )
 		{
 			req->status_string = g_strdup( "Can't parse status code" );
@@ -349,5 +431,5 @@
 		{
 			req->status_string = g_strdup( "Can't locate Location: header" );
-			goto cleanup;
+			return TRUE;
 		}
 		
@@ -369,5 +451,5 @@
 			req->status_string = g_strdup( "Can't handle recursive redirects" );
 			
-			goto cleanup;
+			return TRUE;
 		}
 		else
@@ -380,5 +462,5 @@
 			s = strstr( loc, "\r\n" );
 			if( s == NULL )
-				goto cleanup;
+				return TRUE;
 			
 			url = g_new0( url_t, 1 );
@@ -389,5 +471,5 @@
 				req->status_string = g_strdup( "Malformed redirect URL" );
 				g_free( url );
-				goto cleanup;
+				return TRUE;
 			}
 			
@@ -401,5 +483,5 @@
 				req->status_string = g_strdup( "Error while rebuilding request string" );
 				g_free( url );
-				goto cleanup;
+				return TRUE;
 			}
 			
@@ -467,5 +549,5 @@
 			req->status_string = g_strdup( "Connection problem during redirect" );
 			g_free( new_request );
-			goto cleanup;
+			return TRUE;
 		}
 		
@@ -480,33 +562,14 @@
 	}
 	
-	/* 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 );
-	
-	if( ( s = get_rfc822_header( req->reply_headers, "Content-Length", 0 ) ) &&
-	    sscanf( s, "%zd", &content_length ) == 1 )
-	{
-		if( content_length < req->body_size )
-		{
-			req->status_code = -1;
-			g_free( req->status_string );
-			req->status_string = g_strdup( "Response truncated" );
-		}
-	}
-	g_free( s );
-	
-	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;
+	return TRUE;
+}
+
+void http_flush_bytes( struct http_request *req, size_t len )
+{
+	if( len > 0 && len <= req->body_size )
+	{
+		req->reply_body += len;
+		req->body_size -= len;
+	}
 }
 
@@ -516,5 +579,5 @@
 	g_free( req->reply_headers );
 	g_free( req->status_string );
+	g_free( req->sbuf );
 	g_free( req );
 }
-
