aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/user-guide/commands.xml23
-rw-r--r--lib/Makefile2
-rw-r--r--lib/misc.c3
-rw-r--r--lib/oauth.c439
-rw-r--r--lib/oauth.h74
-rw-r--r--lib/url.c2
-rw-r--r--lib/url.h2
-rw-r--r--protocols/twitter/twitter.c121
-rw-r--r--protocols/twitter/twitter.h6
-rw-r--r--protocols/twitter/twitter_http.c18
-rw-r--r--protocols/twitter/twitter_http.h2
-rw-r--r--protocols/twitter/twitter_lib.c10
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
diff --git a/lib/misc.c b/lib/misc.c
index 1d36d639..c56b31f3 100644
--- a/lib/misc.c
+++ b/lib/misc.c
@@ -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&params, 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( &params );
+
+ params_s = oauth_params_string( params );
+ oauth_params_free( &params );
+
+ 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( &params, "oauth_callback", "oob" );
+
+ if( !oauth_post_request( url, &params, 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( &params, st->auth_params );
+ st->request_token = g_strdup( oauth_params_get( &params, "oauth_token" ) );
+ oauth_params_free( &params );
+ }
+
+ 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( &params, "oauth_token", st->request_token );
+ oauth_params_add( &params, "oauth_verifier", pin );
+
+ if( !oauth_post_request( url, &params, 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( &params, req->reply_body );
+ token = oauth_params_get( &params, "oauth_token" );
+ token_secret = oauth_params_get( &params, "oauth_token_secret" );
+ st->access_token = g_strdup_printf(
+ "oauth_token=%s&oauth_token_secret=%s", token, token_secret );
+ oauth_params_free( &params );
+ }
+
+ 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( &params, 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( &params, "oauth_token_secret" ) );
+ if( token_secret == NULL )
+ goto err;
+ oauth_params_del( &params, "oauth_token_secret" );
+
+ oauth_add_default_params( &params );
+
+ /* 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( &params, args );
+ if( ( s = strchr( url, '?' ) ) )
+ {
+ s = g_strdup( s + 1 );
+ oauth_params_parse( &params, 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( &params );
+ 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 );
diff --git a/lib/url.c b/lib/url.c
index de9966b4..9e330f8c 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -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;
diff --git a/lib/url.h b/lib/url.h
index 8c038c91..55107ad2 100644
--- a/lib/url.h
+++ b/lib/url.h
@@ -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]);
}