aboutsummaryrefslogtreecommitdiffstats
path: root/doc/FAQ
blob: a47e066e604960836459179ccf47cdc9af75f182 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
Frequently Asked Questions about BitlBee
========================================

Well, maybe not exactly "Frequently", but definitely "Asked" ... mostly by
the developers :-)

Q: WTH were you guys on when you thought of that _weird_ name?
A: Though we live in The Netherlands and one of us even lives in Amsterdam,
   we're not on drugs ... most of the time.

Q: Okay, so the cops are so evil there, you can't even admit the truth, but
   WTH does BitlBee mean then?
A: There are a few explanations. But the most symbolical goes like: the two
   colors of the bee symbolize the two worlds betwee which the Bee flies. On
   the one hand there's the IM-networks, on the other is IRC.
   
   Truth be told, it's absolute nonsense. The biggest nutcase in the
   development team just played around with words for half an hour or so.
   BitlBee was the result. We liked it, we kept it. We lovingly shorten it
   to "the Bee" or even "het Bijtje" (Dutch for "the little Bee") sometimes.

Q: What is 'root' doing in my control channel? I didn't start the Bee as
   root.
A: 'root' is just the name for the most powerful user in BitlBee. Just like
   in the system, it is root who is the ... eh ... root of the
   functionality. Luckily, in BitlBee, root follows your orders (mostly), so
   no BOFHs there.
   
   We get some complaints from time to time that 'root' is a confusing name.
   Because of that name, some package maintainers have renamed root to, for
   example, BitlBee. We recognize that some people see that need. If the
   package maintainer hasn't renamed root, you can do this yourself with the
   'rename' command.
   
   The name root is not likely to change in the 'official' releases, though.
   We find the metaphor of root correct and feel that there is no important
   (security threatening) reason to change this non-creative piece of
   artistic creativity.

Q: When is $random_feature going to be implemented?
A: It depends on the feature. We keep a list of all wishlist "bugs" in our
   Bug Tracking system at http://bugs.bitlbee.org/

Q: The messages I send and/or receive look weird. I see weird characters and
   annoying HTML codes. Or, BitlBee does evil things when I send messages with
   non-ASCII characters!
A: You probably have to change some settings. To get rid of HTML in messages,
   see "help set strip_html". If you seem to have problems with your charset,
   see "help set charset".
   
   Although actually most of these problems should be gone by now. So if you
   can't get things to work well, you might have found a bug.

Q: Is BitlBee forked from Gaim?
A: BitlBee 0.7 was, sort-of. It contained a lot of code from Gaim 0.58
   (mainly the IM-code), although heavily modified, to make it work better
   with BitlBee. We were planning to keep BitlBee up-to-date with later Gaim
   versions, but this turned out to be very time-consuming because the API
   changed a lot, and we don't have the time to keep up with those changes
   all the time.
   
   These days, we replaced the Yahoo! code with libyahoo2 (which is a
   separate Yahoo! module. It's derived from Gaim, but separately
   maintained) and wrote our own MSN module. More modules are probably going
   to be changed, so in the near future, the API might be the only thing
   left from Gaim.

Q: What's that Gaim doing in BitlBee error messages and my Jabber resource?
A: Ah, well, as you probably know we use some of Gaim's IM-modules, and we
   don't think it's worth our time to do a search-and-replace over the whole
   source to get rid of every reference to Gaim. In fact, we don't want to,
   since we don't want to pretend we wrote all that code.
   
   About Jabber: If you want a different resource string, you can set it
   when logging in by appending it to your Jabber ID, like:
   lintux@jabber.com/BitlBee
a> 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453
  /********************************************************************\
  * 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 "http_client.h"
#include "url.h"
#include "sock.h"


static gboolean http_connected( gpointer data, int source, b_input_condition cond );
static gboolean http_ssl_connected( gpointer data, void *source, b_input_condition cond );
static gboolean http_incoming_data( gpointer data, int source, b_input_condition cond );


void *http_dorequest( char *host, int port, int ssl, char *request, http_input_function func, 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 );
}

void *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"
	                           "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;
}

/* This one is actually pretty simple... Might get more calls if we can't write 
   the whole request at once. */
static gboolean http_connected( gpointer data, int source, b_input_condition cond )
{
	struct http_request *req = data;
	int st;
	
	if( source < 0 )
		goto error;
	
	if( req->inpa > 0 )
		b_event_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 = b_input_add( source,
		                         req->ssl ? ssl_getdirection( req->ssl ) : GAIM_INPUT_WRITE,
	        	                 http_connected, req );
	else
		req->inpa = b_input_add( source, GAIM_INPUT_READ, http_incoming_data, req );
	
	return FALSE;
	
error:
	req->status_string = g_strdup( "Error while writing HTTP request" );
	
	req->func( req );
	
	g_free( req->request );
	g_free( req );
	
	return FALSE;
}

static gboolean http_ssl_connected( gpointer data, void *source, b_input_condition 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 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 ) : GAIM_INPUT_READ,
	                         http_incoming_data, req );
	
	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( 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 == 302 )
	{
		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;
			
			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;
			}
			
			/* 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 )
			{
				req->status_string = g_strdup( "Error while rebuilding request string" );
				g_free( new_request );
				g_free( url );
				goto cleanup;
			}
			
			strcat( new_request, s );
			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 = NULL;
		
		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;
		}
		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 );
	
	req->func( req );
	
	g_free( req->request );
	g_free( req->reply_headers );
	g_free( req->status_string );
	g_free( req );
	
	return FALSE;
}