diff options
28 files changed, 529 insertions, 193 deletions
@@ -10,7 +10,7 @@  # Program variables  objects = bitlbee.o dcc.o help.o ipc.o irc.o irc_im.o irc_channel.o irc_commands.o irc_send.o irc_user.o irc_util.o nick.o $(OTR_BI) query.o root_commands.o set.o storage.o $(STORAGE_OBJS) -headers = bitlbee.h commands.h conf.h config.h help.h ipc.h irc.h log.h nick.h query.h set.h sock.h storage.h lib/events.h lib/ftutil.h lib/http_client.h lib/ini.h lib/md5.h lib/misc.h lib/proxy.h lib/sha1.h lib/ssl_client.h lib/url.h protocols/account.h protocols/bee.h protocols/ft.h protocols/nogaim.h +headers = bitlbee.h commands.h conf.h config.h help.h ipc.h irc.h log.h nick.h query.h set.h sock.h storage.h lib/events.h lib/ftutil.h lib/http_client.h lib/ini.h lib/json.h lib/json_util.h lib/md5.h lib/misc.h lib/proxy.h lib/sha1.h lib/ssl_client.h lib/url.h protocols/account.h protocols/bee.h protocols/ft.h protocols/nogaim.h  subdirs = lib protocols  ifeq ($(TARGET),i586-mingw32msvc) @@ -210,7 +210,7 @@ fi  echo CFLAGS=$CFLAGS $CPPFLAGS >> Makefile.settings  echo CFLAGS+=-I${srcdir} -I${srcdir}/lib -I${srcdir}/protocols -I. >> Makefile.settings -echo CFLAGS+=-DHAVE_CONFIG_H >> Makefile.settings +echo CFLAGS+=-DHAVE_CONFIG_H -D_GNU_SOURCE >> Makefile.settings  if [ -n "$CC" ]; then  	CC=$CC diff --git a/lib/http_client.c b/lib/http_client.c index b384e1f0..b509c839 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-2012 Wilmer van der Gaast and others                * +  * Copyright 2002-2013 Wilmer van der Gaast and others                *    \********************************************************************/  /* HTTP(S) module                                                       */ @@ -68,6 +68,7 @@ struct http_request *http_dorequest( char *host, int port, int ssl, char *reques  	req->request = g_strdup( request );  	req->request_length = strlen( request );  	req->redir_ttl = 3; +	req->content_length = -1;  	if( getenv( "BITLBEE_DEBUG" ) )  		printf( "About to send HTTP request:\n%s\n", req->request ); @@ -95,7 +96,6 @@ struct http_request *http_dorequest_url( char *url_string, http_input_function f  	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 ); @@ -192,14 +192,21 @@ static gboolean http_ssl_connected( gpointer data, int returncode, void *source,  	return http_connected( data, req->fd, cond );  } +typedef enum { +	CR_OK, +	CR_EOF, +	CR_ERROR, +	CR_ABORT, +} http_ret_t; +  static gboolean http_handle_headers( struct http_request *req ); +static http_ret_t http_process_chunked_data( struct http_request *req, const char *buffer, int len ); +static http_ret_t http_process_data( struct http_request *req, const char *buffer, int len );  static gboolean http_incoming_data( gpointer data, int source, b_input_condition cond )  {  	struct http_request *req = data;  	char buffer[4096]; -	char *s; -	size_t content_length;  	int st;  	if( req->inpa > 0 ) @@ -243,53 +250,25 @@ static gboolean http_incoming_data( gpointer data, int source, b_input_condition  		}  	} -	if( st > 0 && !req->sbuf ) +	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; +		http_ret_t c; -		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->sbuf[req->sblen] = '\0'; -			req->reply_body = req->sbuf + pos; -			req->body_size = req->sblen - pos; -		} +		if( req->flags & HTTPC_CHUNKED ) +			c = http_process_chunked_data( req, buffer, st ); +		else +			c = http_process_data( req, buffer, st ); -		if( req->reply_body ) -			req->func( req ); +		if( c == CR_EOF ) +			goto eof; +		else if( c == CR_ERROR || c == CR_ABORT ) +			return FALSE;  	} +	if( req->content_length != -1 && +	    req->body_size >= req->content_length ) +		goto eof; +	  	if( ssl_pending( req->ssl ) )  		return http_incoming_data( data, source, cond ); @@ -310,14 +289,6 @@ eof:  		req->status_string = g_strdup( "Empty HTTP reply" );  		goto cleanup;  	} -	 -	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 ) @@ -325,17 +296,12 @@ cleanup:  	else  		closesocket( req->fd ); -	if( ( s = get_rfc822_header( req->reply_headers, "Content-Length", 0 ) ) && -	    sscanf( s, "%zd", &content_length ) == 1 ) +	if( req->body_size < req->content_length )  	{ -		if( content_length < req->body_size ) -		{ -			req->status_code = -1; -			g_free( req->status_string ); -			req->status_string = g_strdup( "Response truncated" ); -		} +		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", @@ -346,11 +312,120 @@ cleanup:  	return FALSE;  } +static http_ret_t http_process_chunked_data( struct http_request *req, const char *buffer, int len ) +{ +	char *chunk, *eos, *s; +	 +	if( len < 0 ) +		return TRUE; +	 +	if( len > 0 ) +	{ +		req->cbuf = g_realloc( req->cbuf, req->cblen + len + 1 ); +		memcpy( req->cbuf + req->cblen, buffer, len ); +		req->cblen += len; +		req->cbuf[req->cblen] = '\0'; +	} +	 +	/* Turns out writing a proper chunked-encoding state machine is not +	   that simple. :-( I've tested this one feeding it byte by byte so +	   I hope it's solid now. */ +	chunk = req->cbuf; +	eos = req->cbuf + req->cblen; +	while( TRUE ) +	{ +		int clen = 0; +		 +		/* Might be a \r\n from the last chunk. */ +		s = chunk; +		while( isspace( *s ) ) +			s ++; +		/* Chunk length. Might be incomplete. */ +		if( s < eos && sscanf( s, "%x", &clen ) != 1 ) +			return CR_ERROR; +		while( isxdigit( *s ) ) +			s ++; +		 +		/* If we read anything here, it *must* be \r\n. */ +		if( strncmp( s, "\r\n", MIN( 2, eos - s ) ) != 0 ) +			return CR_ERROR; +		s += 2; +		 +		if( s >= eos ) +			break; +		 +		/* 0-length chunk means end of response. */	 +		if( clen == 0 ) +			return CR_EOF; +		 +		/* Wait for the whole chunk to arrive. */ +		if( s + clen > eos ) +			break; +		if( http_process_data( req, s, clen ) != CR_OK ) +			return CR_ABORT; +		 +		chunk = s + clen; +	} +	 +	if( chunk != req->cbuf ) +	{ +		req->cblen = eos - chunk; +		s = g_memdup( chunk, req->cblen + 1 ); +		g_free( req->cbuf ); +		req->cbuf = s; +	} +	 +	return CR_OK; +} + +static http_ret_t http_process_data( struct http_request *req, const char *buffer, int len ) +{ +	if( len <= 0 ) +		return CR_OK; +	 +	if( !req->reply_body ) +	{ +		req->reply_headers = g_realloc( req->reply_headers, req->bytes_read + len + 1 ); +		memcpy( req->reply_headers + req->bytes_read, buffer, len ); +		req->bytes_read += len; +		req->reply_headers[req->bytes_read] = '\0'; +		 +		if( strstr( req->reply_headers, "\r\n\r\n" ) || +		    strstr( req->reply_headers, "\n\n" ) ) +		{ +			/* We've now received all headers. Look for something +			   interesting. */ +			if( !http_handle_headers( req ) ) +				return CR_ABORT; +			 +			/* Start parsing the body as chunked if required. */ +			if( req->flags & HTTPC_CHUNKED ) +				return http_process_chunked_data( req, NULL, 0 ); +		} +	} +	else +	{ +		int pos = req->reply_body - req->sbuf; +		req->sbuf = g_realloc( req->sbuf, req->sblen + len + 1 ); +		memcpy( req->sbuf + req->sblen, buffer, len ); +		req->bytes_read += len; +		req->sblen += len; +		req->sbuf[req->sblen] = '\0'; +		req->reply_body = req->sbuf + pos; +		req->body_size = req->sblen - pos; +	} +	 +	if( ( req->flags & HTTPC_STREAMING ) && req->reply_body ) +		req->func( req ); +	 +	return CR_OK; +} +  /* 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; +	char *end1, *end2, *s;  	int evil_server = 0;  	/* Zero termination is very convenient. */ @@ -376,7 +451,7 @@ static gboolean http_handle_headers( struct http_request *req )  		return TRUE;  	} -	*end1 = 0; +	*end1 = '\0';  	if( getenv( "BITLBEE_DEBUG" ) )  		printf( "HTTP response headers:\n%s\n", req->reply_headers ); @@ -386,7 +461,10 @@ static gboolean http_handle_headers( struct http_request *req )  	else  		req->reply_body = end1 + 2; -	req->body_size = req->reply_headers + req->bytes_read - req->reply_body; +	/* 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 )  	{ @@ -451,7 +529,7 @@ static gboolean http_handle_headers( struct http_request *req )  			/* 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" ); +			req->status_string = g_strdup( "Can't handle relative redirects" );  			return TRUE;  		} @@ -459,7 +537,7 @@ static gboolean http_handle_headers( struct http_request *req )  		{  			/* A whole URL */  			url_t *url; -			char *s; +			char *s, *version, *headers;  			const char *new_method;  			s = strstr( loc, "\r\n" ); @@ -487,6 +565,7 @@ static gboolean http_handle_headers( struct http_request *req )  				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 @@ -506,9 +585,19 @@ static gboolean http_handle_headers( struct http_request *req )  				/* 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 HTTP/1.0\r\nHost: %s%s", -			                               new_method, url->file, url->host, s ); +			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; @@ -520,6 +609,7 @@ static gboolean http_handle_headers( struct http_request *req )  				s[4] = '\0';  			g_free( url ); +			g_free( version );  		}  		if( req->ssl ) @@ -556,13 +646,35 @@ static gboolean http_handle_headers( struct http_request *req )  		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;  } @@ -606,5 +718,6 @@ static void http_free( struct http_request *req )  	g_free( req->reply_headers );  	g_free( req->status_string );  	g_free( req->sbuf ); +	g_free( req->cbuf );  	g_free( req );  } diff --git a/lib/http_client.h b/lib/http_client.h index ca427118..1b86f228 100644 --- a/lib/http_client.h +++ b/lib/http_client.h @@ -41,6 +41,7 @@ typedef enum http_client_flags  {  	HTTPC_STREAMING = 1,  	HTTPC_EOF = 2, +	HTTPC_CHUNKED = 4,  	/* Let's reserve 0x1000000+ for lib users. */  } http_client_flags_t; @@ -76,10 +77,15 @@ struct http_request  	int inpa;  	int bytes_written;  	int bytes_read; +	int content_length;     /* "Content-Length:" header or -1 */  	/* Used in streaming mode. Caller should read from reply_body. */  	char *sbuf;  	size_t sblen; +	 +	/* Chunked encoding only. Raw chunked stream is decoded from here. */ +	char *cbuf; +	size_t cblen;  };  /* The _url variant is probably more useful than the raw version. The raw diff --git a/lib/oauth.c b/lib/oauth.c index 04949e1b..c78b4a43 100644 --- a/lib/oauth.c +++ b/lib/oauth.c @@ -261,7 +261,6 @@ static void *oauth_post_request( const char *url, GSList **params_, http_input_f  	                     "Host: %s\r\n"  	                     "Content-Type: application/x-www-form-urlencoded\r\n"  	                     "Content-Length: %zd\r\n" -	                     "Connection: close\r\n"  	                     "\r\n"  	                     "%s", url_p.file, url_p.host, strlen( post ), post );  	g_free( post ); diff --git a/lib/oauth2.c b/lib/oauth2.c index 6921a6d5..bfd4b143 100644 --- a/lib/oauth2.c +++ b/lib/oauth2.c @@ -1,9 +1,9 @@  /***************************************************************************\  *                                                                           *  *  BitlBee - An IRC to IM gateway                                           * -*  Simple OAuth client (consumer) implementation.                           * +*  Simple OAuth2 client (consumer) implementation.                          *  *                                                                           * -*  Copyright 2010-2012 Wilmer van der Gaast <wilmer@gaast.net>              * +*  Copyright 2010-2013 Wilmer van der Gaast <wilmer@gaast.net>              *  *                                                                           *  *  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     * @@ -21,11 +21,28 @@  *                                                                           *  \***************************************************************************/ +/* Out of protest, I should rename this file. OAuth2 is a pathetic joke, and +   of all things, DEFINITELY NOT A STANDARD. The only thing various OAuth2 +   implementations have in common is that name, wrongfully stolen from +   a pretty nice standard called OAuth 1.0a. That, and the fact that they +   use JSON. Wait, no, Facebook's version doesn't use JSON. For some of its +   responses. +    +   Apparently too many people were too retarded to comprehend the elementary +   bits of crypto in OAuth 1.0a (took me one afternoon to implement) so +   the standard was replaced with what comes down to a complicated scheme +   around what's really just application-specific passwords. +    +   And then a bunch of mostly incompatible implementations. Great work, guys. +    +   http://hueniverse.com/2012/07/oauth-2-0-and-the-road-to-hell/ */ +  #include <glib.h>  #include "http_client.h"  #include "oauth2.h"  #include "oauth.h"  #include "json.h" +#include "json_util.h"  #include "url.h"  char *oauth2_url( const struct oauth2_service *sp ) @@ -78,7 +95,6 @@ int oauth2_access_token( const struct oauth2_service *sp,  	                     "Host: %s\r\n"  	                     "Content-Type: application/x-www-form-urlencoded\r\n"  	                     "Content-Length: %zd\r\n" -	                     "Connection: close\r\n"  	                     "\r\n"  	                     "%s", url_p.file, url_p.host, strlen( args_s ), args_s );  	g_free( args_s ); @@ -98,10 +114,35 @@ int oauth2_access_token( const struct oauth2_service *sp,  	return req != NULL;  } +static char* oauth2_parse_error( json_value *e ) +{ +	/* This does a reasonable job with some of the flavours of error +	   responses I've seen. Because apparently it's not standardised. */ +	 +	if( e->type == json_object ) +	{ +		/* Facebook style */ +		const char *msg = json_o_str( e, "message" ); +		const char *type = json_o_str( e, "type" ); +		json_value *code_o = json_o_get( e, "code" ); +		int code = 0; +		 +		if( code_o && code_o->type == json_integer ) +			code = code_o->u.integer; +		 +		return g_strdup_printf( "Error %d: %s", code, msg ? msg : type ? type : "Unknown error" ); +	} +	else if( e->type == json_string ) +	{ +		return g_strdup( e->u.string.ptr ); +	} +	return NULL; +} +  static void oauth2_access_token_done( struct http_request *req )  {  	struct oauth2_access_token_data *cb_data = req->data; -	char *atoken = NULL, *rtoken = NULL; +	char *atoken = NULL, *rtoken = NULL, *error = NULL;  	char *content_type;  	if( getenv( "BITLBEE_DEBUG" ) && req->reply_body ) @@ -109,24 +150,22 @@ static void oauth2_access_token_done( struct http_request *req )  	content_type = get_rfc822_header( req->reply_headers, "Content-Type", 0 ); -	if( req->status_code != 200 ) -	{ -	} -	else if( content_type && strstr( content_type, "application/json" ) ) +	if( content_type && ( strstr( content_type, "application/json" ) || +	                      strstr( content_type, "text/javascript" ) ) )  	{  		json_value *js = json_parse( req->reply_body );  		if( js && js->type == json_object )  		{ -			int i; -			 -			for( i = 0; i < js->u.object.length; i ++ ) +			JSON_O_FOREACH( js, k, v )  			{ -				if( js->u.object.values[i].value->type != json_string ) +				if( strcmp( k, "error" ) == 0 ) +					error = oauth2_parse_error( v ); +				if( v->type != json_string )  					continue; -				if( strcmp( js->u.object.values[i].name, "access_token" ) == 0 ) -					atoken = g_strdup( js->u.object.values[i].value->u.string.ptr ); -				if( strcmp( js->u.object.values[i].name, "refresh_token" ) == 0 ) -					rtoken = g_strdup( js->u.object.values[i].value->u.string.ptr ); +				if( strcmp( k, "access_token" ) == 0 ) +					atoken = g_strdup( v->u.string.ptr ); +				if( strcmp( k, "refresh_token" ) == 0 ) +					rtoken = g_strdup( v->u.string.ptr );  			}  		}  		json_value_free( js ); @@ -143,10 +182,13 @@ static void oauth2_access_token_done( struct http_request *req )  	}  	if( getenv( "BITLBEE_DEBUG" ) )  		printf( "Extracted atoken=%s rtoken=%s\n", atoken, rtoken ); +	if( !atoken && !rtoken && !error ) +		error = g_strdup( "Unusuable response" ); -	cb_data->func( cb_data->data, atoken, rtoken ); +	cb_data->func( cb_data->data, atoken, rtoken, error );  	g_free( content_type );  	g_free( atoken );  	g_free( rtoken ); +	g_free( error );  	g_free( cb_data );  } diff --git a/lib/oauth2.h b/lib/oauth2.h index c8d18963..b3811f49 100644 --- a/lib/oauth2.h +++ b/lib/oauth2.h @@ -3,7 +3,7 @@  *  BitlBee - An IRC to IM gateway                                           *  *  Simple OAuth2 client (consumer) implementation.                          *  *                                                                           * -*  Copyright 2010-2011 Wilmer van der Gaast <wilmer@gaast.net>              * +*  Copyright 2010-2013 Wilmer van der Gaast <wilmer@gaast.net>              *  *                                                                           *  *  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     * @@ -24,7 +24,8 @@  /* Implementation mostly based on my experience with writing the previous OAuth     module, and from http://code.google.com/apis/accounts/docs/OAuth2.html . */ -typedef void (*oauth2_token_callback)( gpointer data, const char *atoken, const char *rtoken ); +typedef void (*oauth2_token_callback)( gpointer data, const char *atoken, +                                       const char *rtoken, const char *error );  struct oauth2_service  { diff --git a/lib/proxy.c b/lib/proxy.c index 3e5c9d49..b6b02d72 100644 --- a/lib/proxy.c +++ b/lib/proxy.c @@ -157,7 +157,7 @@ static int proxy_connect_none(const char *host, unsigned short port_, struct PHB  				event_debug("bind( %d, \"%s\" ) failure\n", fd, global.conf->iface_out);  		} -		event_debug("proxy_connect_none( \"%s\", %d ) = %d\n", host, port, fd); +		event_debug("proxy_connect_none( \"%s\", %d ) = %d\n", host, port_, fd);  		if (connect(fd, phb->gai_cur->ai_addr, phb->gai_cur->ai_addrlen) < 0 && !sockerr_again()) {  			event_debug( "connect failed: %s\n", strerror(errno)); diff --git a/lib/ssl_nss.c b/lib/ssl_nss.c index e8de884f..045cd322 100644 --- a/lib/ssl_nss.c +++ b/lib/ssl_nss.c @@ -151,7 +151,7 @@ void *ssl_starttls(int fd, char *hostname, gboolean verify,  	conn->fd = fd;  	conn->func = func;  	conn->data = data; -	conn->hostname = hostname; +	conn->hostname = g_strdup(hostname);  	/* For now, SSL verification is globally enabled by setting the cafile  	   setting in bitlbee.conf. Commented out by default because probably @@ -295,7 +295,7 @@ void ssl_disconnect(void *conn_)  	if (conn->prfd)  		PR_Close(conn->prfd); -        g_free(conn->hostname); +	g_free(conn->hostname);  	g_free(conn);  } @@ -391,7 +391,8 @@ int nick_lc( irc_t *irc, char *nick )  	}  	for( i = 0; nick[i]; i ++ ) -		nick[i] = tab[(int)nick[i]]; +		if( nick[i] < 0x7f ) +			nick[i] = tab[(int)nick[i]];  	return nick_ok( irc, nick );  } diff --git a/protocols/jabber/sasl.c b/protocols/jabber/sasl.c index 450b5b28..12111fc7 100644 --- a/protocols/jabber/sasl.c +++ b/protocols/jabber/sasl.c @@ -482,7 +482,7 @@ static gboolean sasl_oauth2_remove_contact( gpointer data, gint fd, b_input_cond  	return FALSE;  } -static void sasl_oauth2_got_token( gpointer data, const char *access_token, const char *refresh_token ); +static void sasl_oauth2_got_token( gpointer data, const char *access_token, const char *refresh_token, const char *error );  int sasl_oauth2_get_refresh_token( struct im_connection *ic, const char *msg )  { @@ -513,7 +513,7 @@ int sasl_oauth2_refresh( struct im_connection *ic, const char *refresh_token )  	                            refresh_token, sasl_oauth2_got_token, ic );  } -static void sasl_oauth2_got_token( gpointer data, const char *access_token, const char *refresh_token ) +static void sasl_oauth2_got_token( gpointer data, const char *access_token, const char *refresh_token, const char *error )  {  	struct im_connection *ic = data;  	struct jabber_data *jd; @@ -526,7 +526,7 @@ static void sasl_oauth2_got_token( gpointer data, const char *access_token, cons  	if( access_token == NULL )  	{ -		imcb_error( ic, "OAuth failure (missing access token)" ); +		imcb_error( ic, "OAuth failure (%s)", error );  		imc_logout( ic, TRUE );  		return;  	} diff --git a/protocols/oscar/oscar.c b/protocols/oscar/oscar.c index 05be086d..021792b3 100644 --- a/protocols/oscar/oscar.c +++ b/protocols/oscar/oscar.c @@ -980,7 +980,7 @@ static int incomingim_chan1(aim_session_t *sess, aim_conn_t *conn, aim_userinfo_  		char *src;  		if (args->icbmflags & AIM_IMFLAGS_UNICODE) -			src = "UNICODEBIG"; +			src = "UCS-2BE";  		else  			src = "ISO8859-1"; @@ -1768,7 +1768,7 @@ static int oscar_buddy_msg(struct im_connection *ic, char *name, char *message,  			if ((ret = do_iconv("UTF-8", "ISO8859-1", message, s, len, BUF_LONG)) >= 0) {  				args.flags |= AIM_IMFLAGS_ISO_8859_1;  				len = ret; -			} else if ((ret = do_iconv("UTF-8", "UNICODEBIG", message, s, len, BUF_LONG)) >= 0) { +			} else if ((ret = do_iconv("UTF-8", "UCS-2BE", message, s, len, BUF_LONG)) >= 0) {  				args.flags |= AIM_IMFLAGS_UNICODE;  				len = ret;  			} else { @@ -2405,7 +2405,7 @@ void oscar_chat_msg(struct groupchat *c, char *message, int msgflags)  		if ((ret = do_iconv("UTF-8", "ISO8859-1", message, s, len, BUF_LONG)) >= 0) {  			flags |= AIM_CHATFLAGS_ISO_8859_1;  			len = ret; -		} else if ((ret = do_iconv("UTF-8", "UNICODEBIG", message, s, len, BUF_LONG)) >= 0) { +		} else if ((ret = do_iconv("UTF-8", "UCS-2BE", message, s, len, BUF_LONG)) >= 0) {  			flags |= AIM_CHATFLAGS_UNICODE;  			len = ret;  		} else { diff --git a/protocols/skype/README b/protocols/skype/README index b9f22481..f780ca32 100644 --- a/protocols/skype/README +++ b/protocols/skype/README @@ -57,90 +57,17 @@ a public server) and/or your IRC client.  NOTE: The order is important. First `skyped` starts Skype. Then `skyped`  connects to Skype, finally BitlBee can connect to `skyped`. -=== Installing under Frugalware or Debian +=== Installing -- Install the necessary packages: - ----- -# pacman-g2 -S bitlbee-skype ----- - -or +Either use your package manager to install the Skype plugin, using something +like:  ----  # apt-get install skyped bitlbee-plugin-skype  ---- -(the later from the unstable repo) - -and you don't have to compile anything manually. - -=== Installing under OS X - -- Install the necessary packages from ports: - -NOTE: You have to edit the Portfile manually to include the install-dev target, -just append install-dev after install-etc. - ----- -# port -v install bitlbee ----- - -and you have to install `bitlbee-skype` and `skype4py` from -source. - -=== Installing from source - -NOTE: bitlbee-skype by default builds and installs skyped and the -plugin. In case you just want to install the plugin for a public server -or you want to use skyped with a public server (like -`bitlbee1.asnetinc.net`), you don't need both. - -- You need the latest stable BitlBee release (unless you want to use a -  public server): - ----- -$ wget http://get.bitlbee.org/src/bitlbee-@BITLBEE_VERSION@.tar.gz -$ tar xf bitlbee-@BITLBEE_VERSION@.tar.gz -$ cd bitlbee-@BITLBEE_VERSION@ ----- - -- Now compile and install it: - ----- -$ ./configure -$ make -# make install install-dev ----- - -- To install http://skype4py.sourceforge.net/[Skype4Py] from source -  (unless you want to install the plugin for a public server): - ----- -$ tar -zxvf Skype4Py-x.x.x.x.tar.gz -$ cd Skype4Py-x.x.x.x -# python setup.py install ----- - -- Get the plugin code (in an empty dir, or whereever you want, it does -  not matter): - ----- -$ wget http://vmiklos.hu/project/bitlbee-skype/bitlbee-skype-@VERSION@.tar.gz -$ tar xf bitlbee-skype-@VERSION@.tar.gz -$ cd bitlbee-skype-@VERSION@ ----- - -- Compile and install it: - ----- -$ ./configure -$ make -# make install ----- - -This will install the plugin to where BitlBee expects them, which is -`/usr/local/lib/bitlbee` if you installed BitlBee from source. +Or install http://sourceforge.net/projects/skype4py/[Skype4Py], and build +BitlBee with `--skype=1`.  === Configuring diff --git a/protocols/skype/skype.c b/protocols/skype/skype.c index 7ce562d4..714babf4 100644 --- a/protocols/skype/skype.c +++ b/protocols/skype/skype.c @@ -343,11 +343,11 @@ static void skype_parse_user(struct im_connection *ic, char *line)  	*ptr = '\0';  	ptr++;  	if (!strncmp(ptr, "ONLINESTATUS ", 13)) { -			if (!strcmp(user, sd->username)) -				return; -			if (!set_getbool(&ic->acc->set, "test_join") +		if (!strlen(user) || !strcmp(user, sd->username)) +			return; +		if (!set_getbool(&ic->acc->set, "test_join")  				&& !strcmp(user, "echo123")) -				return; +			return;  		ptr = g_strdup_printf("%s@skype.com", user);  		imcb_add_buddy(ic, ptr, skype_group_by_username(ic, user));  		if (strcmp(status, "OFFLINE") && (strcmp(status, "SKYPEOUT") || @@ -1015,7 +1015,10 @@ static void skype_parse_chat(struct im_connection *ic, char *line)  			sd->adder = NULL;  		}  	} else if (!strncmp(info, "MEMBERS ", 8) || !strncmp(info, "ACTIVEMEMBERS ", 14) ) { -		info += 8; +		if (!strncmp(info, "MEMBERS ", 8)) +			info += 8; +		else +			info += 14;  		gc = bee_chat_by_title(ic->bee, ic, id);  		/* Hack! We set ->data to TRUE  		 * while we're on the channel @@ -1563,7 +1566,7 @@ static void skype_init(account_t *acc)  	s->flags |= SET_NOSAVE | ACC_SET_ONLINE_ONLY;  	s = set_add(&acc->set, "mood_text", NULL, skype_set_mood_text, acc); -	s->flags |= ACC_SET_NOSAVE | ACC_SET_ONLINE_ONLY; +	s->flags |= SET_NOSAVE | ACC_SET_ONLINE_ONLY;  	s = set_add(&acc->set, "call", NULL, skype_set_call, acc);  	s->flags |= SET_NOSAVE | ACC_SET_ONLINE_ONLY; diff --git a/protocols/skype/skyped.py b/protocols/skype/skyped.py index 86b1e96a..c9bb103b 100644 --- a/protocols/skype/skyped.py +++ b/protocols/skype/skyped.py @@ -272,7 +272,7 @@ def dprint(msg):  		if options.log:  			sock = open(options.log, "a") -			sock.write(prefix + ": " + sanitized) +			sock.write("%s: %s\n" % (prefix, sanitized))  			sock.close()  		sys.stdout.flush() diff --git a/protocols/skype/t/away-set-bitlbee.mock b/protocols/skype/t/away-set-bitlbee.mock new file mode 100644 index 00000000..bf073400 --- /dev/null +++ b/protocols/skype/t/away-set-bitlbee.mock @@ -0,0 +1,10 @@ +>> NOTICE AUTH +<< NICK alice +<< USER alice alice localhost :Alice +>> PRIVMSG &bitlbee +<< PRIVMSG &bitlbee :account add skype alice foo +<< PRIVMSG &bitlbee :account skype set skypeconsole_receive true +<< PRIVMSG &bitlbee :account skype on +>> PRIVMSG &bitlbee :skype - Logging in: Logged in +<< AWAY :work +>> PRIVMSG &bitlbee :alice: USERSTATUS AWAY diff --git a/protocols/skype/t/away-set-skyped.mock b/protocols/skype/t/away-set-skyped.mock new file mode 100644 index 00000000..0c2cc494 --- /dev/null +++ b/protocols/skype/t/away-set-skyped.mock @@ -0,0 +1,20 @@ +>> SEARCH GROUPS CUSTOM +<< GROUPS 48, 49 +>> SEARCH FRIENDS +<< USERS echo123, bob +>> SET USERSTATUS ONLINE +<< USERSTATUS ONLINE +>> SET USERSTATUS ONLINE +<< USERSTATUS ONLINE +>> GET USER echo123 ONLINESTATUS +<< USER echo123 ONLINESTATUS ONLINE +>> GET USER echo123 FULLNAME +<< USER echo123 FULLNAME Echo / Sound Test Service +>> GET USER bob ONLINESTATUS +<< USER bob ONLINESTATUS ONLINE +>> GET USER bob FULLNAME +<< USER bob FULLNAME Bob +>> SET USERSTATUS AWAY +<< USERSTATUS AWAY +<< USER alice ONLINESTATUS AWAY +<< USERSTATUS AWAY diff --git a/protocols/skype/t/group-add-bitlbee.mock b/protocols/skype/t/group-add-bitlbee.mock new file mode 100644 index 00000000..05c9f987 --- /dev/null +++ b/protocols/skype/t/group-add-bitlbee.mock @@ -0,0 +1,14 @@ +>> NOTICE AUTH +<< NICK alice +<< USER alice alice localhost :Alice +>> PRIVMSG &bitlbee +<< PRIVMSG &bitlbee :account add skype alice foo +<< PRIVMSG &bitlbee :account skype set read_groups true +<< PRIVMSG &bitlbee :account skype on +>> :bob!bob@skype.com JOIN :&bitlbee +>> :cecil!cecil@skype.com JOIN :&bitlbee +>> :daniel!daniel@skype.com JOIN :&bitlbee +<< JOIN &family +>> 353 alice = &family :@alice +bob +cecil @root +<< INVITE daniel &family +>> :daniel!daniel@skype.com JOIN :&family diff --git a/protocols/skype/t/group-add-skyped.mock b/protocols/skype/t/group-add-skyped.mock new file mode 100644 index 00000000..c47f6629 --- /dev/null +++ b/protocols/skype/t/group-add-skyped.mock @@ -0,0 +1,38 @@ +>> SEARCH GROUPS CUSTOM +<< GROUPS 70, 71 +>> SEARCH FRIENDS +<< USERS echo123, bob, cecil, daniel, emily +>> SET USERSTATUS ONLINE +<< USERSTATUS ONLINE +>> SET USERSTATUS ONLINE +<< USERSTATUS ONLINE +>> GET GROUP 70 DISPLAYNAME +<< GROUP 70 DISPLAYNAME Family +>> GET GROUP 70 USERS +<< GROUP 70 USERS bob, cecil +>> GET GROUP 71 DISPLAYNAME +<< GROUP 71 DISPLAYNAME Work +>> GET GROUP 71 USERS +<< GROUP 71 USERS daniel, emily +>> GET USER echo123 ONLINESTATUS +<< USER echo123 ONLINESTATUS ONLINE +>> GET USER echo123 FULLNAME +<< USER echo123 FULLNAME Echo / Sound Test Service +>> GET USER bob ONLINESTATUS +<< USER bob ONLINESTATUS ONLINE +>> GET USER bob FULLNAME +<< USER bob FULLNAME Bob +>> GET USER cecil ONLINESTATUS +<< USER cecil ONLINESTATUS ONLINE +>> GET USER cecil FULLNAME +<< USER cecil FULLNAME Cecil +>> GET USER daniel ONLINESTATUS +<< USER daniel ONLINESTATUS ONLINE +>> GET USER daniel FULLNAME +<< USER daniel FULLNAME Daniel +>> GET USER emily ONLINESTATUS +<< USER emily ONLINESTATUS OFFLINE +>> GET USER emily FULLNAME +<< USER emily FULLNAME Emily +>> ALTER GROUP 70 ADDUSER daniel +<< ALTER GROUP 70 ADDUSER daniel diff --git a/protocols/skype/t/groupchat-msg-bitlbee.mock b/protocols/skype/t/groupchat-msg-bitlbee.mock new file mode 100644 index 00000000..d6fac10d --- /dev/null +++ b/protocols/skype/t/groupchat-msg-bitlbee.mock @@ -0,0 +1,11 @@ +>> NOTICE AUTH +<< NICK alice +<< USER alice alice localhost :Alice +>> PRIVMSG &bitlbee +<< PRIVMSG &bitlbee :account add skype alice foo +<< PRIVMSG &bitlbee :account skype set skypeconsole_receive true +<< PRIVMSG &bitlbee :account skype on +>> JOIN :##cecil/$bob;4d8cc9965791 +>> 353 alice = ##cecil/$bob;4d8cc9965791 :@alice bob cecil @root +<< PRIVMSG ##cecil/$bob;4d8cc9965791 :hello +>> PRIVMSG &bitlbee :alice: CHAT #cecil/$bob;4d8cc9965791c6b9 ACTIVITY_TIMESTAMP diff --git a/protocols/skype/t/groupchat-msg-skyped.mock b/protocols/skype/t/groupchat-msg-skyped.mock new file mode 100644 index 00000000..e939585c --- /dev/null +++ b/protocols/skype/t/groupchat-msg-skyped.mock @@ -0,0 +1,60 @@ +>> SEARCH GROUPS CUSTOM +<< GROUPS 48, 49 +>> SEARCH FRIENDS +<< USERS echo123, bob, cecil +>> SET USERSTATUS ONLINE +<< USERSTATUS ONLINE +>> SET USERSTATUS ONLINE +<< USERSTATUS ONLINE +>> GET USER echo123 ONLINESTATUS +<< USER echo123 ONLINESTATUS ONLINE +>> GET USER echo123 FULLNAME +<< USER echo123 FULLNAME Echo / Sound Test Service +>> GET USER bob ONLINESTATUS +<< USER bob ONLINESTATUS OFFLINE +>> GET USER bob FULLNAME +<< USER bob FULLNAME Bob +>> GET USER cecil ONLINESTATUS +<< USER cecil ONLINESTATUS OFFLINE +>> GET USER cecil FULLNAME +<< USER cecil FULLNAME Cecil +<< CHAT #cecil/$bob;4d8cc9965791c6b9 NAME #cecil/$bob;4d8cc9965791c6b9 +<< CHAT #cecil/$bob;4d8cc9965791c6b9 STATUS MULTI_SUBSCRIBED +<< CHAT #cecil/$bob;4d8cc9965791c6b9 STATUS MULTI_SUBSCRIBED +<< CHATMEMBER 186 ROLE USER +<< CHAT #cecil/$bob;4d8cc9965791c6b9 MYROLE USER +<< CHAT #cecil/$bob;4d8cc9965791c6b9 MEMBERS bob cecil alice +<< CHAT #cecil/$bob;4d8cc9965791c6b9 FRIENDLYNAME bob, cecil +<< CHAT #cecil/$bob;4d8cc9965791c6b9 ACTIVEMEMBERS bob alice +<< CHAT #cecil/$bob;4d8cc9965791c6b9 TIMESTAMP 1358276196 +<< CHAT #cecil/$bob;4d8cc9965791c6b9 ADDER bob +<< CHAT #cecil/$bob;4d8cc9965791c6b9 TOPIC  +<< CHAT #cecil/$bob;4d8cc9965791c6b9 STATUS MULTI_SUBSCRIBED +<< CHATMESSAGE 188 STATUS RECEIVED +>> GET CHAT #cecil/$bob;4d8cc9965791c6b9 ADDER +<< CHAT #cecil/$bob;4d8cc9965791c6b9 ADDER bob +>> GET CHAT #cecil/$bob;4d8cc9965791c6b9 TOPIC +<< CHAT #cecil/$bob;4d8cc9965791c6b9 TOPIC  +>> GET CHAT #cecil/$bob;4d8cc9965791c6b9 ADDER +<< CHAT #cecil/$bob;4d8cc9965791c6b9 ADDER bob +>> GET CHAT #cecil/$bob;4d8cc9965791c6b9 TOPIC +<< CHAT #cecil/$bob;4d8cc9965791c6b9 TOPIC  +>> GET CHAT #cecil/$bob;4d8cc9965791c6b9 ADDER +<< CHAT #cecil/$bob;4d8cc9965791c6b9 ADDER bob +>> GET CHAT #cecil/$bob;4d8cc9965791c6b9 TOPIC +<< CHAT #cecil/$bob;4d8cc9965791c6b9 TOPIC  +>> GET CHATMESSAGE 188 FROM_HANDLE +<< CHATMESSAGE 188 FROM_HANDLE bob +>> GET CHATMESSAGE 188 BODY +<< CHATMESSAGE 188 BODY  +>> GET CHATMESSAGE 188 TYPE +<< CHATMESSAGE 188 TYPE ADDEDMEMBERS +>> GET CHATMESSAGE 188 CHATNAME +<< CHATMESSAGE 188 CHATNAME #cecil/$bob;4d8cc9965791c6b9 +<< CHATMESSAGE 189 STATUS READ +<< CHATMESSAGE 189 STATUS READ +<< CHATMEMBER 186 IS_ACTIVE TRUE +<< CHAT #cecil/$bob;4d8cc9965791c6b9 ACTIVEMEMBERS bob cecil alice +<< CHATMESSAGE 190 STATUS SENT +>> CHATMESSAGE #cecil/$bob;4d8cc9965791c6b9 hello +<< CHAT #cecil/$bob;4d8cc9965791c6b9 ACTIVITY_TIMESTAMP 1364652882 diff --git a/protocols/skype/t/groupchat-topic-bitlbee.mock b/protocols/skype/t/groupchat-topic-bitlbee.mock new file mode 100644 index 00000000..637e4d98 --- /dev/null +++ b/protocols/skype/t/groupchat-topic-bitlbee.mock @@ -0,0 +1,10 @@ +>> NOTICE AUTH +<< NICK alice +<< USER alice alice localhost :Alice +>> PRIVMSG &bitlbee +<< PRIVMSG &bitlbee :account add skype alice foo +<< PRIVMSG &bitlbee :account skype on +>> JOIN :##cecil/$bob;4d8cc9965791 +>> 353 alice = ##cecil/$bob;4d8cc9965791 :@alice bob cecil @root +<< TOPIC ##cecil/$bob;4d8cc9965791 :topic +>> TOPIC ##cecil/$bob;4d8cc9965791 :topic diff --git a/protocols/skype/t/groupchat-topic-skyped.mock b/protocols/skype/t/groupchat-topic-skyped.mock new file mode 100644 index 00000000..26c4b53b --- /dev/null +++ b/protocols/skype/t/groupchat-topic-skyped.mock @@ -0,0 +1,60 @@ +>> SEARCH GROUPS CUSTOM +<< GROUPS 48, 49 +>> SEARCH FRIENDS +<< USERS echo123, bob, cecil +>> SET USERSTATUS ONLINE +<< USERSTATUS ONLINE +>> SET USERSTATUS ONLINE +<< USERSTATUS ONLINE +>> GET USER echo123 ONLINESTATUS +<< USER echo123 ONLINESTATUS ONLINE +>> GET USER echo123 FULLNAME +<< USER echo123 FULLNAME Echo / Sound Test Service +>> GET USER bob ONLINESTATUS +<< USER bob ONLINESTATUS OFFLINE +>> GET USER bob FULLNAME +<< USER bob FULLNAME Bob +>> GET USER cecil ONLINESTATUS +<< USER cecil ONLINESTATUS OFFLINE +>> GET USER cecil FULLNAME +<< USER cecil FULLNAME Cecil +<< CHAT #cecil/$bob;4d8cc9965791c6b9 NAME #cecil/$bob;4d8cc9965791c6b9 +<< CHAT #cecil/$bob;4d8cc9965791c6b9 STATUS MULTI_SUBSCRIBED +<< CHAT #cecil/$bob;4d8cc9965791c6b9 STATUS MULTI_SUBSCRIBED +<< CHATMEMBER 186 ROLE USER +<< CHAT #cecil/$bob;4d8cc9965791c6b9 MYROLE USER +<< CHAT #cecil/$bob;4d8cc9965791c6b9 MEMBERS bob cecil alice +<< CHAT #cecil/$bob;4d8cc9965791c6b9 FRIENDLYNAME bob, cecil +<< CHAT #cecil/$bob;4d8cc9965791c6b9 ACTIVEMEMBERS bob alice +<< CHAT #cecil/$bob;4d8cc9965791c6b9 TIMESTAMP 1358276196 +<< CHAT #cecil/$bob;4d8cc9965791c6b9 ADDER bob +<< CHAT #cecil/$bob;4d8cc9965791c6b9 TOPIC  +<< CHAT #cecil/$bob;4d8cc9965791c6b9 STATUS MULTI_SUBSCRIBED +<< CHATMESSAGE 188 STATUS RECEIVED +>> GET CHAT #cecil/$bob;4d8cc9965791c6b9 ADDER +<< CHAT #cecil/$bob;4d8cc9965791c6b9 ADDER bob +>> GET CHAT #cecil/$bob;4d8cc9965791c6b9 TOPIC +<< CHAT #cecil/$bob;4d8cc9965791c6b9 TOPIC  +>> GET CHAT #cecil/$bob;4d8cc9965791c6b9 ADDER +<< CHAT #cecil/$bob;4d8cc9965791c6b9 ADDER bob +>> GET CHAT #cecil/$bob;4d8cc9965791c6b9 TOPIC +<< CHAT #cecil/$bob;4d8cc9965791c6b9 TOPIC  +>> GET CHAT #cecil/$bob;4d8cc9965791c6b9 ADDER +<< CHAT #cecil/$bob;4d8cc9965791c6b9 ADDER bob +>> GET CHAT #cecil/$bob;4d8cc9965791c6b9 TOPIC +<< CHAT #cecil/$bob;4d8cc9965791c6b9 TOPIC  +>> GET CHATMESSAGE 188 FROM_HANDLE +<< CHATMESSAGE 188 FROM_HANDLE bob +>> GET CHATMESSAGE 188 BODY +<< CHATMESSAGE 188 BODY  +>> GET CHATMESSAGE 188 TYPE +<< CHATMESSAGE 188 TYPE ADDEDMEMBERS +>> GET CHATMESSAGE 188 CHATNAME +<< CHATMESSAGE 188 CHATNAME #cecil/$bob;4d8cc9965791c6b9 +<< CHATMESSAGE 189 STATUS READ +<< CHATMESSAGE 189 STATUS READ +<< CHATMEMBER 186 IS_ACTIVE TRUE +<< CHAT #cecil/$bob;4d8cc9965791c6b9 ACTIVEMEMBERS bob cecil alice +<< CHATMESSAGE 190 STATUS SENT +>> ALTER CHAT #cecil/$bob;4d8cc9965791c6b9 SETTOPIC topic +<< CHAT #cecil/$bob;4d8cc9965791c6b9 TOPIC topic diff --git a/protocols/skype/test.py b/protocols/skype/test.py index 63652f76..233c41c8 100755 --- a/protocols/skype/test.py +++ b/protocols/skype/test.py @@ -112,6 +112,12 @@ class Test(unittest.TestCase):  	def testGroupchatLeave(self):  		self.mock("groupchat-leave") +	def testGroupchatMsg(self): +		self.mock("groupchat-msg") +	 +	def testGroupchatTopic(self): +		self.mock("groupchat-topic") +	  	def testCalledYes(self):  		self.mock("called-yes") @@ -124,11 +130,17 @@ class Test(unittest.TestCase):  	def testGroupRead(self):  		self.mock("group-read") +	def testGroupAdd(self): +		self.mock("group-add") +  	def testCtcpHelp(self):  		self.mock("ctcp-help")  	def testSetMoodText(self):  		self.mock("set-mood-text") +	 +	def testAwaySet(self): +		self.mock("away-set")  if __name__ == '__main__':  	setupSkyped() diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index db61ba7c..4626cf55 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -157,8 +157,13 @@ static const struct oauth_service *get_oauth_service(struct im_connection *ic)  static void twitter_oauth_start(struct im_connection *ic)  {  	struct twitter_data *td = ic->proto_data; +	const char *url = set_getstr(&ic->acc->set, "base_url");  	imcb_log(ic, "Requesting OAuth request token"); +	 +	if (!strstr(url, "twitter.com") && !strstr(url, "identi.ca")) +		imcb_log(ic, "Warning: OAuth only works with identi.ca and " +		             "Twitter.");  	td->oauth_info = oauth_request_token(get_oauth_service(ic), twitter_oauth_callback, ic); @@ -177,7 +182,7 @@ static gboolean twitter_oauth_callback(struct oauth_info *info)  	td = ic->proto_data;  	if (info->stage == OAUTH_REQUEST_TOKEN) { -		char name[strlen(ic->acc->user) + 9], *msg; +		char *name, *msg;  		if (info->request_token == NULL) {  			imcb_error(ic, "OAuth error: %s", twitter_parse_error(info->http)); @@ -185,11 +190,12 @@ static gboolean twitter_oauth_callback(struct oauth_info *info)  			return FALSE;  		} -		sprintf(name, "%s_%s", td->prefix, ic->acc->user); +		name = g_strdup_printf("%s_%s", td->prefix, ic->acc->user);  		msg = g_strdup_printf("To finish OAuth authentication, please visit "  				      "%s and respond with the resulting PIN code.",  				      info->auth_url);  		imcb_buddy_msg(ic, name, msg, 0, 0); +		g_free(name);  		g_free(msg);  	} else if (info->stage == OAUTH_ACCESS_TOKEN) {  		const char *sn; @@ -282,13 +288,16 @@ static void twitter_init(account_t * acc)  	set_t *s;  	char *def_url;  	char *def_tul; +	char *def_mentions;  	if (strcmp(acc->prpl->name, "twitter") == 0) {  		def_url = TWITTER_API_URL;  		def_tul = "20"; +		def_mentions = "true";  	} else {		/* if( strcmp( acc->prpl->name, "identica" ) == 0 ) */  		def_url = IDENTICA_API_URL;  		def_tul = "0"; +		def_mentions = "false";  	}  	s = set_add(&acc->set, "auto_reply_timeout", "10800", set_eval_int, acc); @@ -301,7 +310,7 @@ static void twitter_init(account_t * acc)  	s = set_add(&acc->set, "fetch_interval", "60", set_eval_int, acc);  	s->flags |= ACC_SET_OFFLINE_ONLY; -	s = set_add(&acc->set, "fetch_mentions", "true", set_eval_bool, acc); +	s = set_add(&acc->set, "fetch_mentions", def_mentions, set_eval_bool, acc);  	s = set_add(&acc->set, "message_length", "140", set_eval_int, acc); diff --git a/protocols/twitter/twitter_http.c b/protocols/twitter/twitter_http.c index 0f1ab518..f7ab6e18 100644 --- a/protocols/twitter/twitter_http.c +++ b/protocols/twitter/twitter_http.c @@ -77,7 +77,7 @@ struct http_request *twitter_http(struct im_connection *ic, char *url_string, ht  	}  	// Make the request. -	g_string_printf(request, "%s %s%s%s%s HTTP/1.0\r\n" +	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", diff --git a/protocols/twitter/twitter_lib.h b/protocols/twitter/twitter_lib.h index f59c81ef..e593d229 100644 --- a/protocols/twitter/twitter_lib.h +++ b/protocols/twitter/twitter_lib.h @@ -28,7 +28,7 @@  #include "nogaim.h"  #include "twitter_http.h" -#define TWITTER_API_URL "http://api.twitter.com/1.1" +#define TWITTER_API_URL "https://api.twitter.com/1.1"  #define IDENTICA_API_URL "https://identi.ca/api"  /* Status URLs */ diff --git a/storage_xml.c b/storage_xml.c index 3ee8ae1d..d32ed25f 100644 --- a/storage_xml.c +++ b/storage_xml.c @@ -173,7 +173,7 @@ static storage_status_t xml_load_real( irc_t *irc, const char *my_nick, const ch  	struct xml_parsedata xd[1];  	char *fn, buf[2048];  	int fd, st; -	struct xt_parser *xp; +	struct xt_parser *xp = NULL;  	struct xt_node *node;  	storage_status_t ret = STORAGE_OTHER_ERROR;  | 
