diff options
| -rw-r--r-- | doc/user-guide/commands.xml | 23 | ||||
| -rw-r--r-- | lib/Makefile | 2 | ||||
| -rw-r--r-- | lib/misc.c | 3 | ||||
| -rw-r--r-- | lib/oauth.c | 439 | ||||
| -rw-r--r-- | lib/oauth.h | 74 | ||||
| -rw-r--r-- | lib/url.c | 2 | ||||
| -rw-r--r-- | lib/url.h | 2 | ||||
| -rw-r--r-- | protocols/twitter/twitter.c | 121 | ||||
| -rw-r--r-- | protocols/twitter/twitter.h | 6 | ||||
| -rw-r--r-- | protocols/twitter/twitter_http.c | 18 | ||||
| -rw-r--r-- | protocols/twitter/twitter_http.h | 2 | ||||
| -rw-r--r-- | protocols/twitter/twitter_lib.c | 10 | 
12 files changed, 674 insertions, 28 deletions
| diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml index a8f030b2..9fcc91da 100644 --- a/doc/user-guide/commands.xml +++ b/doc/user-guide/commands.xml @@ -78,6 +78,10 @@  					<para>  						To send tweets yourself, send them to the twitter_(yourusername) contact, or just write in the groupchat channel if you enabled that option.  					</para> + +					<para> +						Since Twitter now requires OAuth authentication, you should not enter your Twitter password into BitlBee. Just type a bogus password. The first time you log in, BitlBee will start OAuth authentication. (See <emphasis>help set oauth</emphasis>.) +					</para>  				</description>  			</bitlbee-command> @@ -702,6 +706,25 @@  		</description>  	</bitlbee-setting> +	<bitlbee-setting name="oauth" type="boolean" scope="account"> +		<default>true</default> + +		<description> +			<para> +				This enables OAuth authentication for Twitter accounts. From June 2010 this will be mandatory. +			</para> + +			<para> +				With OAuth enabled, you shouldn't tell BitlBee your Twitter password. Just add your account with a bogus password and type <emphasis>account on</emphasis>. BitlBee will then give you a URL to authenticate with Twitter. If this succeeds, Twitter will return a PIN code which you can give back to BitlBee to finish the process. +			</para> + +			<para> +				The resulting access token will be saved permanently, so you have to do this only once. +			</para> +		</description> + +	</bitlbee-setting> +  	<bitlbee-setting name="ops" type="string" scope="global">  		<default>both</default>  		<possible-values>both, root, user, none</possible-values> diff --git a/lib/Makefile b/lib/Makefile index 03fef1ab..441634cd 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -9,7 +9,7 @@  -include ../Makefile.settings  # [SH] Program variables -objects = arc.o base64.o $(EVENT_HANDLER) http_client.o ini.o md5.o misc.o proxy.o sha1.o $(SSL_CLIENT) url.o xmltree.o +objects = arc.o base64.o $(EVENT_HANDLER) http_client.o ini.o md5.o misc.o oauth.o proxy.o sha1.o $(SSL_CLIENT) url.o xmltree.o  CFLAGS += -Wall  LFLAGS += -r @@ -305,8 +305,7 @@ void http_encode( char *s )  	for( i = j = 0; t[i]; i ++, j ++ )  	{ -		/* if( t[i] <= ' ' || ((unsigned char *)t)[i] >= 128 || t[i] == '%' ) */ -		if( !isalnum( t[i] ) ) +		if( !isalnum( t[i] ) && !strchr( "._-~", t[i] ) )  		{  			sprintf( s + j, "%%%02X", ((unsigned char*)t)[i] );  			j += 2; diff --git a/lib/oauth.c b/lib/oauth.c new file mode 100644 index 00000000..9fb83922 --- /dev/null +++ b/lib/oauth.c @@ -0,0 +1,439 @@ +/***************************************************************************\ +*                                                                           * +*  BitlBee - An IRC to IM gateway                                           * +*  Simple OAuth client (consumer) implementation.                           * +*                                                                           * +*  Copyright 2010 Wilmer van der Gaast <wilmer@gaast.net>                   * +*                                                                           * +*  This library is free software; you can redistribute it and/or            * +*  modify it under the terms of the GNU Lesser General Public               * +*  License as published by the Free Software Foundation, version            * +*  2.1.                                                                     * +*                                                                           * +*  This library 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        * +*  Lesser General Public License for more details.                          * +*                                                                           * +*  You should have received a copy of the GNU Lesser General Public License * +*  along with this library; if not, write to the Free Software Foundation,  * +*  Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           * +*                                                                           * +\***************************************************************************/ + +#include <glib.h> +#include <gmodule.h> +#include <stdlib.h> +#include <string.h> +#include "http_client.h" +#include "base64.h" +#include "misc.h" +#include "sha1.h" +#include "url.h" +#include "oauth.h" + +#define CONSUMER_KEY "xsDNKJuNZYkZyMcu914uEA" +#define CONSUMER_SECRET "FCxqcr0pXKzsF9ajmP57S3VQ8V6Drk4o2QYtqMcOszo" +/* How can it be a secret if it's right here in the source code? No clue... */ + +#define HMAC_BLOCK_SIZE 64 + +static char *oauth_sign( const char *method, const char *url, +                         const char *params, const char *token_secret ) +{ +	sha1_state_t sha1; +	uint8_t hash[sha1_hash_size]; +	uint8_t key[HMAC_BLOCK_SIZE+1]; +	char *s; +	int i; +	 +	/* Create K. If our current key is >64 chars we have to hash it, +	   otherwise just pad. */ +	memset( key, 0, HMAC_BLOCK_SIZE ); +	i = strlen( CONSUMER_SECRET ) + 1 + ( token_secret ? strlen( token_secret ) : 0 ); +	if( i > HMAC_BLOCK_SIZE ) +	{ +		sha1_init( &sha1 ); +		sha1_append( &sha1, (uint8_t*) CONSUMER_SECRET, strlen( CONSUMER_SECRET ) ); +		sha1_append( &sha1, (uint8_t*) "&", 1 ); +		if( token_secret ) +			sha1_append( &sha1, (uint8_t*) token_secret, strlen( token_secret ) ); +		sha1_finish( &sha1, key ); +	} +	else +	{ +		g_snprintf( (gchar*) key, HMAC_BLOCK_SIZE + 1, "%s&%s", +		            CONSUMER_SECRET, token_secret ? : "" ); +	} +	 +	/* Inner part: H(K XOR 0x36, text) */ +	sha1_init( &sha1 ); +	 +	for( i = 0; i < HMAC_BLOCK_SIZE; i ++ ) +		key[i] ^= 0x36; +	sha1_append( &sha1, key, HMAC_BLOCK_SIZE ); +	 +	/* OAuth: text = method&url¶ms, all http_encoded. */ +	sha1_append( &sha1, (const uint8_t*) method, strlen( method ) ); +	sha1_append( &sha1, (const uint8_t*) "&", 1 ); +	 +	s = g_new0( char, strlen( url ) * 3 + 1 ); +	strcpy( s, url ); +	http_encode( s ); +	sha1_append( &sha1, (const uint8_t*) s, strlen( s ) ); +	sha1_append( &sha1, (const uint8_t*) "&", 1 ); +	g_free( s ); +	 +	s = g_new0( char, strlen( params ) * 3 + 1 ); +	strcpy( s, params ); +	http_encode( s ); +	sha1_append( &sha1, (const uint8_t*) s, strlen( s ) ); +	g_free( s ); +	 +	sha1_finish( &sha1, hash ); +	 +	/* Final result: H(K XOR 0x5C, inner stuff) */ +	sha1_init( &sha1 ); +	for( i = 0; i < HMAC_BLOCK_SIZE; i ++ ) +		key[i] ^= 0x36 ^ 0x5c; +	sha1_append( &sha1, key, HMAC_BLOCK_SIZE ); +	sha1_append( &sha1, hash, sha1_hash_size ); +	sha1_finish( &sha1, hash ); +	 +	/* base64_encode + HTTP escape it (both consumers  +	   need it that away) and we're done. */ +	s = base64_encode( hash, sha1_hash_size ); +	s = g_realloc( s, strlen( s ) * 3 + 1 ); +	http_encode( s ); +	 +	return s; +} + +static char *oauth_nonce() +{ +	unsigned char bytes[9]; +	 +	random_bytes( bytes, sizeof( bytes ) ); +	return base64_encode( bytes, sizeof( bytes ) ); +} + +void oauth_params_add( GSList **params, const char *key, const char *value ) +{ +	char *item; +	 +	item = g_strdup_printf( "%s=%s", key, value ); +	*params = g_slist_insert_sorted( *params, item, (GCompareFunc) strcmp ); +} + +void oauth_params_del( GSList **params, const char *key ) +{ +	int key_len = strlen( key ); +	GSList *l, *n; +	 +	for( l = *params; l; l = n ) +	{ +		n = l->next; +		 +		if( strncmp( (char*) l->data, key, key_len ) == 0 && +		    ((char*)l->data)[key_len] == '=' ) +		{ +			g_free( l->data ); +			*params = g_slist_remove( *params, l->data ); +		} +	} +} + +void oauth_params_set( GSList **params, const char *key, const char *value ) +{ +	oauth_params_del( params, key ); +	oauth_params_add( params, key, value ); +} + +const char *oauth_params_get( GSList **params, const char *key ) +{ +	int key_len = strlen( key ); +	GSList *l; +	 +	for( l = *params; l; l = l->next ) +	{ +		if( strncmp( (char*) l->data, key, key_len ) == 0 && +		    ((char*)l->data)[key_len] == '=' ) +			return (const char*) l->data + key_len + 1; +	} +	 +	return NULL; +} + +static void oauth_params_parse( GSList **params, char *in ) +{ +	char *amp, *eq, *s; +	 +	while( in && *in ) +	{ +		eq = strchr( in, '=' ); +		if( !eq ) +			break; +		 +		*eq = '\0'; +		if( ( amp = strchr( eq + 1, '&' ) ) ) +			*amp = '\0'; +		 +		s = g_strdup( eq + 1 ); +		http_decode( s ); +		oauth_params_add( params, in, s ); +		g_free( s ); +		 +		*eq = '='; +		if( amp == NULL ) +			break; +		 +		*amp = '&'; +		in = amp + 1; +	} +} + +void oauth_params_free( GSList **params ) +{ +	while( params && *params ) +	{ +		g_free( (*params)->data ); +		*params = g_slist_remove( *params, (*params)->data ); +	} +} + +char *oauth_params_string( GSList *params ) +{ +	GSList *l; +	GString *str = g_string_new( "" ); +	 +	for( l = params; l; l = l->next ) +	{ +		char *s, *eq; +		 +		s = g_malloc( strlen( l->data ) * 3 + 1 ); +		strcpy( s, l->data ); +		if( ( eq = strchr( s, '=' ) ) ) +			http_encode( eq + 1 ); +		g_string_append( str, s ); +		g_free( s ); +		 +		if( l->next ) +			g_string_append_c( str, '&' ); +	} +	 +	return g_string_free( str, FALSE ); +} + +void oauth_info_free( struct oauth_info *info ) +{ +	if( info ) +	{ +		g_free( info->auth_params ); +		g_free( info->request_token ); +		g_free( info->access_token ); +		g_free( info ); +	} +} + +static void oauth_add_default_params( GSList **params ) +{ +	char *s; +	 +	oauth_params_set( params, "oauth_consumer_key", CONSUMER_KEY ); +	oauth_params_set( params, "oauth_signature_method", "HMAC-SHA1" ); +	 +	s = g_strdup_printf( "%d", (int) time( NULL ) ); +	oauth_params_set( params, "oauth_timestamp", s ); +	g_free( s ); +	 +	s = oauth_nonce(); +	oauth_params_set( params, "oauth_nonce", s ); +	g_free( s ); +	 +	oauth_params_set( params, "oauth_version", "1.0" ); +} + +static void *oauth_post_request( const char *url, GSList **params_, http_input_function func, void *data ) +{ +	GSList *params = NULL; +	char *s, *params_s, *post; +	void *req; +	url_t url_p; +	 +	if( !url_set( &url_p, url ) ) +	{ +		oauth_params_free( params_ ); +		return NULL; +	} +	 +	if( params_ ) +		params = *params_; +	 +	oauth_add_default_params( ¶ms ); +	 +	params_s = oauth_params_string( params ); +	oauth_params_free( ¶ms ); +	 +	s = oauth_sign( "POST", url, params_s, NULL ); +	post = g_strdup_printf( "%s&oauth_signature=%s", params_s, s ); +	g_free( params_s ); +	g_free( s ); +	 +	s = g_strdup_printf( "POST %s HTTP/1.0\r\n" +	                     "Host: %s\r\n" +	                     "Content-Type: application/x-www-form-urlencoded\r\n" +	                     "Content-Length: %zd\r\n" +	                     "\r\n" +	                     "%s", url_p.file, url_p.host, strlen( post ), post ); +	g_free( post ); +	 +	req = http_dorequest( url_p.host, url_p.port, url_p.proto == PROTO_HTTPS, +	                      s, func, data ); +	g_free( s ); +	 +	return req; +} + +static void oauth_request_token_done( struct http_request *req ); + +struct oauth_info *oauth_request_token( const char *url, oauth_cb func, void *data ) +{ +	struct oauth_info *st = g_new0( struct oauth_info, 1 ); +	GSList *params = NULL; +	 +	st->func = func; +	st->data = data; +	 +	oauth_params_add( ¶ms, "oauth_callback", "oob" ); +	 +	if( !oauth_post_request( url, ¶ms, oauth_request_token_done, st ) ) +	{ +		oauth_info_free( st ); +		return NULL; +	} +	 +	return st; +} + +static void oauth_request_token_done( struct http_request *req ) +{ +	struct oauth_info *st = req->data; +	 +	st->http = req; +	 +	if( req->status_code == 200 ) +	{ +		GSList *params = NULL; +		 +		st->auth_params = g_strdup( req->reply_body ); +		oauth_params_parse( ¶ms, st->auth_params ); +		st->request_token = g_strdup( oauth_params_get( ¶ms, "oauth_token" ) ); +		oauth_params_free( ¶ms ); +	} +	 +	st->stage = OAUTH_REQUEST_TOKEN; +	if( !st->func( st ) ) +		oauth_info_free( st ); +} + +static void oauth_access_token_done( struct http_request *req ); + +void oauth_access_token( const char *url, const char *pin, struct oauth_info *st ) +{ +	GSList *params = NULL; +	 +	oauth_params_add( ¶ms, "oauth_token", st->request_token ); +	oauth_params_add( ¶ms, "oauth_verifier", pin ); +	 +	if( !oauth_post_request( url, ¶ms, oauth_access_token_done, st ) ) +		oauth_info_free( st ); +} + +static void oauth_access_token_done( struct http_request *req ) +{ +	struct oauth_info *st = req->data; +	 +	st->http = req; +	 +	if( req->status_code == 200 ) +	{ +		GSList *params = NULL; +		const char *token, *token_secret; +		 +		oauth_params_parse( ¶ms, req->reply_body ); +		token = oauth_params_get( ¶ms, "oauth_token" ); +		token_secret = oauth_params_get( ¶ms, "oauth_token_secret" ); +		st->access_token = g_strdup_printf( +			"oauth_token=%s&oauth_token_secret=%s", token, token_secret ); +		oauth_params_free( ¶ms ); +	} +	 +	st->stage = OAUTH_ACCESS_TOKEN; +	st->func( st ); +	oauth_info_free( st ); +} + +char *oauth_http_header( char *access_token, const char *method, const char *url, char *args ) +{ +	GSList *params = NULL, *l; +	char *token_secret, *sig, *params_s, *s; +	GString *ret = NULL; +	 +	/* First, get the two pieces of info from the access token that we need. */ +	oauth_params_parse( ¶ms, access_token ); +	if( params == NULL ) +		goto err; +	 +	/* Pick out the token secret, we shouldn't include it but use it for signing. */ +	token_secret = g_strdup( oauth_params_get( ¶ms, "oauth_token_secret" ) ); +	if( token_secret == NULL ) +		goto err; +	oauth_params_del( ¶ms, "oauth_token_secret" ); +	 +	oauth_add_default_params( ¶ms ); +	 +	/* Start building the OAuth header. 'key="value", '... */ +	ret = g_string_new( "OAuth " ); +	for( l = params; l; l = l->next ) +	{ +		char *kv = l->data; +		char *eq = strchr( kv, '=' ); +		char esc[strlen(kv)*3+1]; +		 +		if( eq == NULL ) +			break; /* WTF */ +		 +		strcpy( esc, eq + 1 ); +		http_encode( esc ); +		 +		g_string_append_len( ret, kv, eq - kv + 1 ); +		g_string_append_c( ret, '"' ); +		g_string_append( ret, esc ); +		g_string_append( ret, "\", " ); +	} +	 +	/* Now, before generating the signature, add GET/POST arguments to params +	   since they should be included in the base signature string (but not in +	   the HTTP header). */ +	if( args ) +		oauth_params_parse( ¶ms, args ); +	if( ( s = strchr( url, '?' ) ) ) +	{ +		s = g_strdup( s + 1 ); +		oauth_params_parse( ¶ms, s + 1 ); +		g_free( s ); +	} +	 +	/* Append the signature and we're done! */ +	params_s = oauth_params_string( params ); +	sig = oauth_sign( method, url, params_s, token_secret ); +	g_string_append_printf( ret, "oauth_signature=\"%s\"", sig ); +	g_free( params_s ); +	 +err: +	oauth_params_free( ¶ms ); +	g_free( sig ); +	g_free( token_secret ); +	 +	return ret ? g_string_free( ret, FALSE ) : NULL; +} diff --git a/lib/oauth.h b/lib/oauth.h new file mode 100644 index 00000000..a61650cc --- /dev/null +++ b/lib/oauth.h @@ -0,0 +1,74 @@ +/***************************************************************************\ +*                                                                           * +*  BitlBee - An IRC to IM gateway                                           * +*  Simple OAuth client (consumer) implementation.                           * +*                                                                           * +*  Copyright 2010 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     * +*  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 along  * +*  with this program; if not, write to the Free Software Foundation, Inc.,  * +*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              * +*                                                                           * +\***************************************************************************/ + +/* http://oauth.net/core/1.0a/ */ + +struct oauth_info; + +/* Callback function called twice during the access token request process. +   Return FALSE if something broke and the process must be aborted. */ +typedef gboolean (*oauth_cb)( struct oauth_info * ); + +typedef enum +{ +	OAUTH_INIT, +	OAUTH_REQUEST_TOKEN, +	OAUTH_ACCESS_TOKEN, +} oauth_stage_t; + +struct oauth_info +{ +	oauth_stage_t stage; +	 +	oauth_cb func; +	void *data; +	 +	struct http_request *http; +	 +	char *auth_params; +	char *request_token; +	 +	char *access_token; +}; + +/* http://oauth.net/core/1.0a/#auth_step1 (section 6.1)  +   Request an initial anonymous token which can be used to construct an +   authorization URL for the user. This is passed to the callback function +   in a struct oauth_info. */ +struct oauth_info *oauth_request_token( const char *url, oauth_cb func, void *data ); + +/* http://oauth.net/core/1.0a/#auth_step3 (section 6.3) +   The user gets a PIN or so which we now exchange for the final access +   token. This is passed to the callback function in the same +   struct oauth_info. */ +void oauth_access_token( const char *url, const char *pin, struct oauth_info *st ); + +/* http://oauth.net/core/1.0a/#anchor12 (section 7) +   Generate an OAuth Authorization: HTTP header. access_token should be +   saved/fetched using the functions above. args can be a string with +   whatever's going to be in the POST body of the request. GET args will +   automatically be grabbed from url. */ +char *oauth_http_header( char *access_token, const char *method, const char *url, char *args ); + +/* Shouldn't normally be required unless the process is aborted by the user. */ +void oauth_info_free( struct oauth_info *info ); @@ -26,7 +26,7 @@  #include "url.h"  /* Convert an URL to a url_t structure */ -int url_set( url_t *url, char *set_url ) +int url_set( url_t *url, const char *set_url )  {  	char s[MAX_STRING+1];  	char *i; @@ -41,4 +41,4 @@ typedef struct url  	char pass[MAX_STRING+1];  } url_t; -int url_set( url_t *url, char *set_url ); +int url_set( url_t *url, const char *set_url ); diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index 726c7cb1..8ea1a56e 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -22,6 +22,7 @@  ****************************************************************************/  #include "nogaim.h" +#include "oauth.h"  #include "twitter.h"  #include "twitter_http.h"  #include "twitter_lib.h" @@ -50,6 +51,81 @@ gboolean twitter_main_loop(gpointer data, gint fd, b_input_condition cond)  	return (ic->flags & OPT_LOGGED_IN) == OPT_LOGGED_IN;  } +static void twitter_main_loop_start( struct im_connection *ic ) +{ +	struct twitter_data *td = ic->proto_data; +	 +	imcb_log( ic, "Connecting to Twitter" ); + +	// Run this once. After this queue the main loop function. +	twitter_main_loop(ic, -1, 0); + +	// Queue the main_loop +	// Save the return value, so we can remove the timeout on logout. +	td->main_loop_id = b_timeout_add(60000, twitter_main_loop, ic); +} + +static gboolean twitter_oauth_callback( struct oauth_info *info ); + +static void twitter_oauth_start( struct im_connection *ic ) +{ +	struct twitter_data *td = ic->proto_data; +	 +	imcb_log( ic, "Requesting OAuth request token" ); + +	td->oauth_info = oauth_request_token( +		TWITTER_OAUTH_REQUEST_TOKEN, twitter_oauth_callback, ic ); +} + +static gboolean twitter_oauth_callback( struct oauth_info *info ) +{ +	struct im_connection *ic = info->data; +	struct twitter_data *td; +	 +	if( !g_slist_find( twitter_connections, ic ) ) +		return FALSE; +	 +	td = ic->proto_data; +	if( info->stage == OAUTH_REQUEST_TOKEN ) +	{ +		char name[strlen(ic->acc->user)+9], *msg; +		 +		if( info->request_token == NULL ) +		{ +			imcb_error( ic, "OAuth error: %s", info->http->status_string ); +			imc_logout( ic, TRUE ); +			return FALSE; +		} +		 +		sprintf( name, "twitter_%s", ic->acc->user ); +		msg = g_strdup_printf( "To finish OAuth authentication, please visit " +		                       "%s?%s and respond with the resulting PIN code.", +		                       TWITTER_OAUTH_AUTHORIZE, info->auth_params ); +		imcb_buddy_msg( ic, name, msg, 0, 0 ); +		g_free( msg ); +	} +	else if( info->stage == OAUTH_ACCESS_TOKEN ) +	{ +		if( info->access_token == NULL ) +		{ +			imcb_error( ic, "OAuth error: %s", info->http->status_string ); +			imc_logout( ic, TRUE ); +			return FALSE; +		} +		 +		td->oauth = g_strdup( info->access_token ); +		 +		/* IM mods didn't do this so far and it's ugly but I should +		   be able to get away with it... */ +		g_free( ic->acc->pass ); +		ic->acc->pass = g_strdup( info->access_token ); +		 +		twitter_main_loop_start( ic ); +	} +	 +	return TRUE; +} +  static char *set_eval_mode( set_t *set, char *value )  {  	if( g_strcasecmp( value, "one" ) == 0 || @@ -66,6 +142,8 @@ static void twitter_init( account_t *acc )  	s = set_add( &acc->set, "mode", "one", set_eval_mode, acc );  	s->flags |= ACC_SET_OFFLINE_ONLY; +	 +	s = set_add( &acc->set, "oauth", "true", set_eval_bool, acc );  }  /** @@ -79,25 +157,23 @@ static void twitter_login( account_t *acc )  	char name[strlen(acc->user)+9];  	twitter_connections = g_slist_append( twitter_connections, ic ); - +	ic->proto_data = td; +	  	td->user = acc->user; -	td->pass = acc->pass; +	if( !set_getbool( &acc->set, "oauth" ) ) +		td->pass = g_strdup( acc->pass ); +	else if( strstr( acc->pass, "oauth_token=" ) ) +		td->oauth = g_strdup( acc->pass );  	td->home_timeline_id = 0; - -	ic->proto_data = td; - -	imcb_log( ic, "Connecting to Twitter" ); - -	// Run this once. After this queue the main loop function. -	twitter_main_loop(ic, -1, 0); - -	// Queue the main_loop -	// Save the return value, so we can remove the timeout on logout. -	td->main_loop_id = b_timeout_add(60000, twitter_main_loop, ic);  	sprintf( name, "twitter_%s", acc->user );  	imcb_add_buddy( ic, name, NULL );  	imcb_buddy_status( ic, name, OPT_LOGGED_IN, NULL, NULL ); +	 +	if( td->pass || td->oauth ) +		twitter_main_loop_start( ic ); +	else +		twitter_oauth_start( ic );  }  /** @@ -118,6 +194,10 @@ static void twitter_logout( struct im_connection *ic )  	if( td )  	{ +		oauth_info_free( td->oauth_info ); +		 +		g_free( td->pass ); +		g_free( td->oauth );  		g_free( td );  	} @@ -129,12 +209,23 @@ static void twitter_logout( struct im_connection *ic )   */  static int twitter_buddy_msg( struct im_connection *ic, char *who, char *message, int away )  { +	struct twitter_data *td = ic->proto_data; +	  	if (g_strncasecmp(who, "twitter_", 8) == 0 &&  	    g_strcasecmp(who + 8, ic->acc->user) == 0) -		twitter_post_status(ic, message); +	{ +		if( set_getbool( &ic->acc->set, "oauth" ) && td->oauth_info ) +		{ +			oauth_access_token( TWITTER_OAUTH_ACCESS_TOKEN, message, td->oauth_info ); +			td->oauth_info = NULL; +		} +		else +			twitter_post_status(ic, message); +	}  	else +	{  		twitter_direct_messages_new(ic, who, message); -	 +	}  	return( 0 );  } diff --git a/protocols/twitter/twitter.h b/protocols/twitter/twitter.h index 88caa104..9c046b66 100644 --- a/protocols/twitter/twitter.h +++ b/protocols/twitter/twitter.h @@ -36,6 +36,8 @@ struct twitter_data  {  	char* user;  	char* pass; +	char* oauth; +	struct oauth_info *oauth_info;  	guint64 home_timeline_id;  	gint main_loop_id;  	struct groupchat *home_timeline_gc; @@ -49,4 +51,8 @@ struct twitter_data   */  GSList *twitter_connections; +#define TWITTER_OAUTH_REQUEST_TOKEN "http://api.twitter.com/oauth/request_token" +#define TWITTER_OAUTH_ACCESS_TOKEN  "http://api.twitter.com/oauth/access_token" +#define TWITTER_OAUTH_AUTHORIZE     "http://api.twitter.com/oauth/authorize" +  #endif //_TWITTER_H diff --git a/protocols/twitter/twitter_http.c b/protocols/twitter/twitter_http.c index 3632140f..93e315fa 100644 --- a/protocols/twitter/twitter_http.c +++ b/protocols/twitter/twitter_http.c @@ -34,6 +34,7 @@  #include "url.h"  #include "misc.h"  #include "base64.h" +#include "oauth.h"  #include <ctype.h>  #include <errno.h> @@ -44,7 +45,7 @@ char *twitter_url_append(char *url, char *key, char* value);   * Do a request.   * This is actually pretty generic function... Perhaps it should move to the lib/http_client.c   */ -void *twitter_http(char *url_string, http_input_function func, gpointer data, int is_post, char* user, char* pass, char** arguments, int arguments_len) +void *twitter_http(char *url_string, http_input_function func, gpointer data, int is_post, char* user, char* pass, char* oauth_token, char** arguments, int arguments_len)  {  	url_t *url = g_new0( url_t, 1 );  	char *tmp; @@ -109,7 +110,20 @@ void *twitter_http(char *url_string, http_input_function func, gpointer data, in  	                            is_post ? "POST" : "GET", url->file, url->host );  	// If a pass and user are given we append them to the request. -	if (userpass_base64) +	if (oauth_token) +	{ +		char *full_header; +		 +		full_header = oauth_http_header(oauth_token, +		                                is_post ? "POST" : "GET", +		                                url_string, url_arguments); +		 +		tmp = g_strdup_printf("%sAuthorization: %s\r\n", request, full_header); +		g_free(request); +		g_free(full_header); +		request = tmp; +	} +	else if (userpass_base64)  	{  		tmp = g_strdup_printf("%sAuthorization: Basic %s\r\n", request, userpass_base64);  		g_free(request); diff --git a/protocols/twitter/twitter_http.h b/protocols/twitter/twitter_http.h index ec4a0b7c..1e29fb99 100644 --- a/protocols/twitter/twitter_http.h +++ b/protocols/twitter/twitter_http.h @@ -28,7 +28,7 @@  #include "http_client.h"  void *twitter_http(char *url_string, http_input_function func, gpointer data, int is_post,  -					char* user, char* pass, char** arguments, int arguments_len); +                   char* user, char* pass, char *oauth_token, char** arguments, int arguments_len);  #endif //_TWITTER_HTTP_H diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index d58afd73..c91f8bbf 100644 --- a/protocols/twitter/twitter_lib.c +++ b/protocols/twitter/twitter_lib.c @@ -129,7 +129,7 @@ void twitter_get_friends_ids(struct im_connection *ic, int next_cursor)  	char* args[2];  	args[0] = "cursor";  	args[1] = g_strdup_printf ("%d", next_cursor); -	twitter_http(TWITTER_FRIENDS_IDS_URL, twitter_http_get_friends_ids, ic, 0, td->user, td->pass, args, 2); +	twitter_http(TWITTER_FRIENDS_IDS_URL, twitter_http_get_friends_ids, ic, 0, td->user, td->pass, td->oauth, args, 2);  	g_free(args[1]);  } @@ -395,7 +395,7 @@ void twitter_get_home_timeline(struct im_connection *ic, int next_cursor)  		args[3] = g_strdup_printf ("%llu", (long long unsigned int) td->home_timeline_id);  	} -	twitter_http(TWITTER_HOME_TIMELINE_URL, twitter_http_get_home_timeline, ic, 0, td->user, td->pass, args, td->home_timeline_id ? 4 : 2); +	twitter_http(TWITTER_HOME_TIMELINE_URL, twitter_http_get_home_timeline, ic, 0, td->user, td->pass, td->oauth, args, td->home_timeline_id ? 4 : 2);  	g_free(args[1]);  	if (td->home_timeline_id) { @@ -619,7 +619,7 @@ void twitter_get_statuses_friends(struct im_connection *ic, int next_cursor)  	args[0] = "cursor";  	args[1] = g_strdup_printf ("%d", next_cursor); -	twitter_http(TWITTER_SHOW_FRIENDS_URL, twitter_http_get_statuses_friends, ic, 0, td->user, td->pass, args, 2); +	twitter_http(TWITTER_SHOW_FRIENDS_URL, twitter_http_get_statuses_friends, ic, 0, td->user, td->pass, td->oauth, args, 2);  	g_free(args[1]);  } @@ -653,7 +653,7 @@ void twitter_post_status(struct im_connection *ic, char* msg)  	char* args[2];  	args[0] = "status";  	args[1] = msg; -	twitter_http(TWITTER_STATUS_UPDATE_URL, twitter_http_post_status, ic, 1, td->user, td->pass, args, 2); +	twitter_http(TWITTER_STATUS_UPDATE_URL, twitter_http_post_status, ic, 1, td->user, td->pass, td->oauth, args, 2);  //	g_free(args[1]);  } @@ -671,7 +671,7 @@ void twitter_direct_messages_new(struct im_connection *ic, char *who, char *msg)  	args[2] = "text";  	args[3] = msg;  	// Use the same callback as for twitter_post_status, since it does basically the same. -	twitter_http(TWITTER_DIRECT_MESSAGES_NEW_URL, twitter_http_post_status, ic, 1, td->user, td->pass, args, 4); +	twitter_http(TWITTER_DIRECT_MESSAGES_NEW_URL, twitter_http_post_status, ic, 1, td->user, td->pass, td->oauth, args, 4);  //	g_free(args[1]);  //	g_free(args[3]);  } | 
