Esempio n. 1
0
struct http_request *http_dorequest_url( char *url_string, http_input_function func, gpointer data )
{
	url_t *url = g_new0( url_t, 1 );
	char *request;
	void *ret;
	
	if( !url_set( url, url_string ) )
	{
		g_free( url );
		return NULL;
	}
	
	if( url->proto != PROTO_HTTP && url->proto != PROTO_HTTPS )
	{
		g_free( url );
		return NULL;
	}
	
	request = g_strdup_printf( "GET %s HTTP/1.0\r\n"
	                           "Host: %s\r\n"
	                           "Connection: close\r\n"
	                           "User-Agent: BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\r\n"
	                           "\r\n", url->file, url->host );
	
	ret = http_dorequest( url->host, url->port,
	                      url->proto == PROTO_HTTPS, request, func, data );
	
	g_free( url );
	g_free( request );
	return ret;
}
Esempio n. 2
0
/*
 * Initializes hardware and looks for repository/inst-sys.
 *
 * If ok, the instsys (and repo, if possible) are mounted.
 *
 * return:
 *   0: not found
 *   1: ok
 */
int auto2_init()
{
  int ok, win_old, install_unset = 0;
  char *device;
  slist_t *sl;

  auto2_scan_hardware();

  /* set default repository: try dvd drives */
  if(!config.url.install) {
    install_unset = 1;
    config.url.install = url_set(config.defaultrepo ? config.defaultrepo->key : "cd:/");
  }
  if(!config.url.instsys) {
    config.url.instsys = url_set(config.url.instsys_default ?: config.rescue ? config.rescueimage : config.rootimage);
  }
Esempio n. 3
0
/*
 * return values:
 * -1    : abort (aka ESC)
 *  0    : ok
 *  other: stay in menu
 */
int set_settings_cb (dia_item_t di)
{
  int rc = 0;
  char *s;
  url_t *url;

  di_set_settings_last = di;

  switch(di) {
    case di_set_lang:
      set_choose_language();
      break;

    case di_set_display:
      set_choose_display();
      util_print_banner();
      break;

    case di_set_keymap:
      set_choose_keytable(1);
      rc = 1;
      break;

    case di_set_animate:
      rc = dia_yesno("Use animated windows?", config.explode_win ? YES : NO);
      if(rc == YES)
        config.explode_win = 1;
      else if(rc == NO)
        config.explode_win = 0;
      rc = 1;
      break;

    case di_set_forceroot:
      rc = dia_yesno("Should the root image be loaded into the RAM disk?", config.download.instsys ? YES : NO);
      config.download.instsys_set = 1;
      if(rc == YES)
        config.download.instsys = 1;
      else if(rc == NO)
        config.download.instsys = 0;
      rc = 1;
      break;

    case di_set_rootimage:
      (void) dia_input2("Enter the path and name of the file to load into the RAM disk as the root file system.", &config.rootimage, 30, 0);
      rc = 1;
      break;

    case di_set_vnc:
      rc = dia_yesno("Use VNC for install?", config.vnc ? YES : NO);
      if(rc != ESCAPE) {
        if((config.vnc = rc == YES ? 1 : 0)) {
          config.net.do_setup |= DS_VNC;
        }
        else {
          config.net.do_setup &= ~DS_VNC;
        }
      }
      rc = 1;
      break;

    case di_set_usessh:
      rc = dia_yesno("Start SSH for Text Install?", config.usessh ? YES : NO);
      if(rc != ESCAPE) {
        if((config.usessh = rc == YES ? 1 : 0)) {
          config.net.do_setup |= DS_SSH;
        }
        else {
          config.net.do_setup &= ~DS_SSH;
        }
      }
      rc = 1;
      break;

    case di_set_startshell:
      rc = dia_yesno("Start shell before and after YaST?", config.startshell ? YES : NO);
      if(rc != ESCAPE) config.startshell = rc == YES ? 1 : 0;
      rc = 1;
      break;

    case di_set_slp:
      if(net_config_needed(1) && net_config()) {
        rc = 1;
        break;
      }
      url = url_set("slp:");
      s = slp_get_install(url);
      url_free(url);
      rc = 1;
      if(s) {
        url = url_set(s);
        if(url->scheme) {
          url_free(config.url.install);
          config.url.install = url;
          rc = 0;
        }
        else {
          url_free(url);
        }
      }
      if(rc) dia_message("SLP failed", MSGTYPE_ERROR);
      rc = 1;
      break;

    case di_inst_net_config:
      net_config();
      rc = 1;
      break;

    default:
      break;
  }

  return rc;
}
Esempio n. 4
0
	for (i = 0; get[i]; i++) {
		char copy[20], *ret;
		strcpy(copy, get[i]);
		ret = strip_newlines(copy);
		fail_unless (strcmp(copy, expected[i]) == 0, 
					 "(%d) strip_newlines broken: %s -> %s (expected: %s)", 
					 i, get[i], copy, expected[i]);
		fail_unless (copy == ret, "Original string not returned"); 
	}
}
END_TEST

START_TEST(test_set_url_http)
	url_t url;
	
	fail_if (0 == url_set(&url, "http://host/"));
	fail_unless (!strcmp(url.host, "host"));
	fail_unless (!strcmp(url.file, "/"));
	fail_unless (!strcmp(url.user, ""));
	fail_unless (!strcmp(url.pass, ""));
	fail_unless (url.proto == PROTO_HTTP);
	fail_unless (url.port == 80);
END_TEST

START_TEST(test_set_url_https)
	url_t url;
	
	fail_if (0 == url_set(&url, "https://ahost/AimeeMann"));
	fail_unless (!strcmp(url.host, "ahost"));
	fail_unless (!strcmp(url.file, "/AimeeMann"));
	fail_unless (!strcmp(url.user, ""));
Esempio n. 5
0
/**
 * Login method. Since the twitter API works with separate HTTP request we
 * only save the user and pass to the twitter_data object.
 */
static void twitter_login(account_t * acc)
{
	struct im_connection *ic = imcb_new(acc);
	struct twitter_data *td;
	char name[strlen(acc->user) + 9];
	url_t url;
	char *s;
	
	if (!url_set(&url, set_getstr(&ic->acc->set, "base_url")) ||
	    (url.proto != PROTO_HTTP && url.proto != PROTO_HTTPS)) {
		imcb_error(ic, "Incorrect API base URL: %s", set_getstr(&ic->acc->set, "base_url"));
		imc_logout(ic, FALSE);
		return;
	}

	if (!strstr(url.host, "twitter.com") &&
	    set_getbool(&ic->acc->set, "stream")) {
		imcb_error(ic, "Warning: The streaming API is only supported by Twitter, "
		               "and you seem to be connecting to a different service.");
	}

	imcb_log(ic, "Connecting");

	twitter_connections = g_slist_append(twitter_connections, ic);
	td = g_new0(struct twitter_data, 1);
	ic->proto_data = td;
	td->user = g_strdup(acc->user);

	td->url_ssl = url.proto == PROTO_HTTPS;
	td->url_port = url.port;
	td->url_host = g_strdup(url.host);
	if (strcmp(url.file, "/") != 0)
		td->url_path = g_strdup(url.file);
	else {
		td->url_path = g_strdup("");
		if (g_str_has_suffix(url.host, "twitter.com"))
			/* May fire for people who turned on HTTPS. */
			imcb_error(ic, "Warning: Twitter requires a version number in API calls "
			               "now. Try resetting the base_url account setting.");
	}
	
	/* Hacky string mangling: Turn identi.ca into identi.ca and api.twitter.com
	   into twitter, and try to be sensible if we get anything else. */
	td->prefix = g_strdup(url.host);
	if (g_str_has_suffix(td->prefix, ".com"))
		td->prefix[strlen(url.host) - 4] = '\0';
	if ((s = strrchr(td->prefix, '.')) && strlen(s) > 4) {
		/* If we have at least 3 chars after the last dot, cut off the rest.
		   (mostly a www/api prefix or sth) */
		s = g_strdup(s + 1);
		g_free(td->prefix);
		td->prefix = s;
	}
	
	if (strstr(acc->pass, "oauth_token="))
		td->oauth_info = oauth_from_string(acc->pass, get_oauth_service(ic));

	sprintf(name, "%s_%s", td->prefix, acc->user);
	imcb_add_buddy(ic, name, NULL);
	imcb_buddy_status(ic, name, OPT_LOGGED_IN, NULL, NULL);

	td->log = g_new0(struct twitter_log_data, TWITTER_LOG_LENGTH);
	td->log_id = -1;
	
	s = set_getstr(&ic->acc->set, "mode");
	if (g_strcasecmp(s, "one") == 0)
		td->flags |= TWITTER_MODE_ONE;
	else if (g_strcasecmp(s, "many") == 0)
		td->flags |= TWITTER_MODE_MANY;
	else
		td->flags |= TWITTER_MODE_CHAT;

	twitter_login_finish(ic);
}
Esempio n. 6
0
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;
	int st;
	
	if( req->inpa > 0 )
		b_event_remove( req->inpa );
	
	if( req->ssl )
	{
		st = ssl_read( req->ssl, buffer, sizeof( buffer ) );
		if( st < 0 )
		{
			if( ssl_errno != SSL_AGAIN )
			{
				/* goto cleanup; */
				
				/* YAY! We have to deal with crappy Microsoft
				   servers that LOVE to send invalid TLS
				   packets that abort connections! \o/ */
				
				goto got_reply;
			}
		}
		else if( st == 0 )
		{
			goto got_reply;
		}
	}
	else
	{
		st = read( req->fd, buffer, sizeof( buffer ) );
		if( st < 0 )
		{
			if( !sockerr_again() )
			{
				req->status_string = g_strdup( strerror( errno ) );
				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 = b_input_add( req->fd,
	                         req->ssl ? ssl_getdirection( req->ssl ) : B_EV_IO_READ,
	                         http_incoming_data, req );
	
	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
	   support... */
	if( req->bytes_read == 0 )
	{
		req->status_string = g_strdup( "Empty HTTP reply" );
		goto cleanup;
	}
	
	/* 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 if( end1 )
	{
		end1 += 2;
	}
	else
	{
		req->status_string = g_strdup( "Malformed HTTP reply" );
		goto cleanup;
	}
	
	*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
		req->reply_body = end1 + 2;
	
	req->body_size = req->reply_headers + req->bytes_read - req->reply_body;
	
	if( ( end1 = strchr( req->reply_headers, ' ' ) ) != NULL )
	{
		if( sscanf( end1 + 1, "%d", &req->status_code ) != 1 )
		{
			req->status_string = g_strdup( "Can't parse status code" );
			req->status_code = -1;
		}
		else
		{
			char *eol;
			
			if( evil_server )
				eol = strchr( end1, '\n' );
			else
				eol = strchr( end1, '\r' );
			
			req->status_string = g_strndup( end1 + 1, eol - end1 - 1 );
			
			/* Just to be sure... */
			if( ( eol = strchr( req->status_string, '\r' ) ) )
				*eol = 0;
			if( ( eol = strchr( req->status_string, '\n' ) ) )
				*eol = 0;
		}
	}
	else
	{
		req->status_string = g_strdup( "Can't locate status code" );
		req->status_code = -1;
	}
	
	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;
		
		/* We might fill it again, so let's not leak any memory. */
		g_free( req->status_string );
		req->status_string = NULL;
		
		loc = strstr( req->reply_headers, "\nLocation: " );
		if( loc == NULL ) /* We can't handle this redirect... */
		{
			req->status_string = g_strdup( "Can't locate Location: header" );
			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. */
			
			req->status_string = g_strdup( "Can't handle recursive redirects" );
			
			goto cleanup;
		}
		else
		{
			/* A whole URL */
			url_t *url;
			char *s;
			const char *new_method;
			
			s = strstr( loc, "\r\n" );
			if( s == NULL )
				goto cleanup;
			
			url = g_new0( url_t, 1 );
			*s = 0;
			
			if( !url_set( url, loc ) )
			{
				req->status_string = g_strdup( "Malformed redirect URL" );
				g_free( url );
				goto cleanup;
			}
			
			/* 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( url );
				goto cleanup;
			}
			
			/* 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 );
		}
		
		if( req->ssl )
			ssl_disconnect( req->ssl );
		else
			closesocket( req->fd );
		
		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, TRUE, 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;
		}
		g_free( new_host );
		
		if( error )
		{
			req->status_string = g_strdup( "Connection problem during redirect" );
			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 FALSE;
	}
	
	/* 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( 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;
}
Esempio n. 7
0
/**
 * Do a request.
 * This is actually pretty generic function... Perhaps it should move to the lib/http_client.c
 */
struct http_request *twitter_http(struct im_connection *ic, char *url_string, http_input_function func,
                                  gpointer data, int is_post, char **arguments, int arguments_len)
{
	struct twitter_data *td = ic->proto_data;
	char *tmp;
	GString *request = g_string_new("");
	void *ret;
	char *url_arguments;
	url_t *base_url = NULL;

	url_arguments = g_strdup("");

	// Construct the url arguments.
	if (arguments_len != 0) {
		int i;
		for (i = 0; i < arguments_len; i += 2) {
			tmp = twitter_url_append(url_arguments, arguments[i], arguments[i + 1]);
			g_free(url_arguments);
			url_arguments = tmp;
		}
	}

	if (strstr(url_string, "://")) {
		base_url = g_new0(url_t, 1);
		if (!url_set(base_url, url_string)) {
			g_free(base_url);
			return NULL;
		}
	}

	// Make the request.
	g_string_printf(request, "%s %s%s%s%s HTTP/1.1\r\n"
	                "Host: %s\r\n"
	                "User-Agent: BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\r\n",
	                is_post ? "POST" : "GET",
	                base_url ? base_url->file : td->url_path,
	                base_url ? "" : url_string,
	                is_post ? "" : "?", is_post ? "" : url_arguments,
	                base_url ? base_url->host : td->url_host);

	// If a pass and user are given we append them to the request.
	if (td->oauth_info) {
		char *full_header;
		char *full_url;

		if (base_url) {
			full_url = g_strdup(url_string);
		} else {
			full_url = g_strconcat(set_getstr(&ic->acc->set, "base_url"), url_string, NULL);
		}
		full_header = oauth_http_header(td->oauth_info, is_post ? "POST" : "GET",
		                                full_url, url_arguments);

		g_string_append_printf(request, "Authorization: %s\r\n", full_header);
		g_free(full_header);
		g_free(full_url);
	} else {
		char userpass[strlen(ic->acc->user) + 2 + strlen(ic->acc->pass)];
		char *userpass_base64;

		g_snprintf(userpass, sizeof(userpass), "%s:%s", ic->acc->user, ic->acc->pass);
		userpass_base64 = base64_encode((unsigned char *) userpass, strlen(userpass));
		g_string_append_printf(request, "Authorization: Basic %s\r\n", userpass_base64);
		g_free(userpass_base64);
	}

	// Do POST stuff..
	if (is_post) {
		// Append the Content-Type and url-encoded arguments.
		g_string_append_printf(request,
		                       "Content-Type: application/x-www-form-urlencoded\r\n"
		                       "Content-Length: %zd\r\n\r\n%s",
		                       strlen(url_arguments), url_arguments);
	} else {
		// Append an extra \r\n to end the request...
		g_string_append(request, "\r\n");
	}

	if (base_url) {
		ret = http_dorequest(base_url->host, base_url->port, base_url->proto == PROTO_HTTPS, request->str, func,
		                     data);
	} else {
		ret = http_dorequest(td->url_host, td->url_port, td->url_ssl, request->str, func, data);
	}

	g_free(url_arguments);
	g_string_free(request, TRUE);
	g_free(base_url);
	return ret;
}
Esempio n. 8
0
/* 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, *s;
	int evil_server = 0;
	
	/* 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 if( end1 )
	{
		end1 += 2;
	}
	else
	{
		req->status_string = g_strdup( "Malformed HTTP reply" );
		return TRUE;
	}
	
	*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
		req->reply_body = end1 + 2;
	
	/* Separately allocated space for headers and body. */
	req->sblen = req->body_size = req->reply_headers + req->bytes_read - req->reply_body;
	req->sbuf = req->reply_body = g_memdup( req->reply_body, req->body_size + 1 );
	req->reply_headers = g_realloc( req->reply_headers, end1 - req->reply_headers + 1 );
	
	if( ( end1 = strchr( req->reply_headers, ' ' ) ) != NULL )
	{
		if( sscanf( end1 + 1, "%hd", &req->status_code ) != 1 )
		{
			req->status_string = g_strdup( "Can't parse status code" );
			req->status_code = -1;
		}
		else
		{
			char *eol;
			
			if( evil_server )
				eol = strchr( end1, '\n' );
			else
				eol = strchr( end1, '\r' );
			
			req->status_string = g_strndup( end1 + 1, eol - end1 - 1 );
			
			/* Just to be sure... */
			if( ( eol = strchr( req->status_string, '\r' ) ) )
				*eol = 0;
			if( ( eol = strchr( req->status_string, '\n' ) ) )
				*eol = 0;
		}
	}
	else
	{
		req->status_string = g_strdup( "Can't locate status code" );
		req->status_code = -1;
	}
	
	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;
		
		/* We might fill it again, so let's not leak any memory. */
		g_free( req->status_string );
		req->status_string = NULL;
		
		loc = strstr( req->reply_headers, "\nLocation: " );
		if( loc == NULL ) /* We can't handle this redirect... */
		{
			req->status_string = g_strdup( "Can't locate Location: header" );
			return TRUE;
		}
		
		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. */
			
			req->status_string = g_strdup( "Can't handle relative redirects" );
			
			return TRUE;
		}
		else
		{
			/* A whole URL */
			url_t *url;
			char *s, *version, *headers;
			const char *new_method;
			
			s = strstr( loc, "\r\n" );
			if( s == NULL )
				return TRUE;
			
			url = g_new0( url_t, 1 );
			*s = 0;
			
			if( !url_set( url, loc ) )
			{
				req->status_string = g_strdup( "Malformed redirect URL" );
				g_free( url );
				return TRUE;
			}
			
			/* 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( url );
				return TRUE;
			}
			headers = 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";
			
			if( ( version = strstr( req->request, " HTTP/" ) ) &&
			    ( s = strstr( version, "\r\n" ) ) )
			{
				version ++;
				version = g_strndup( version, s - version );
			}
			else
				version = g_strdup( "HTTP/1.0" );
			
			/* Okay, this isn't fun! We have to rebuild the request... :-( */
			new_request = g_strdup_printf( "%s %s %s\r\nHost: %s%s",
			                               new_method, url->file, version,
			                               url->host, headers );
			
			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 );
			g_free( version );
		}
		
		if( req->ssl )
			ssl_disconnect( req->ssl );
		else
			closesocket( req->fd );
		
		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, TRUE, 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;
		}
		g_free( new_host );
		
		if( error )
		{
			req->status_string = g_strdup( "Connection problem during redirect" );
			g_free( new_request );
			return TRUE;
		}
		
		g_free( req->request );
		g_free( req->reply_headers );
		g_free( req->sbuf );
		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;
		req->sbuf = req->cbuf = NULL;
		req->sblen = req->cblen = 0;
		
		return FALSE;
	}

	if( ( s = get_rfc822_header( req->reply_headers, "Content-Length", 0 ) ) &&
	    sscanf( s, "%d", &req->content_length ) != 1 )
		req->content_length = -1;
	g_free( s );
	
	if( ( s = get_rfc822_header( req->reply_headers, "Transfer-Encoding", 0 ) ) )
	{
		if( strcasestr( s, "chunked" ) )
		{
			req->flags |= HTTPC_CHUNKED;
			req->cbuf = req->sbuf;
			req->cblen = req->sblen;
			
			req->reply_body = req->sbuf = g_strdup( "" );
			req->body_size = req->sblen = 0;
		}
		g_free( s );
	}
	
	return TRUE;
}