From 0f64ca78c8ec9ee9be2ffa8ec203e138f7a00804 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 25 Apr 2010 13:56:20 +0100 Subject: Make http_encode() RFC3986-compliant. (Escape everything except alphanumeric characters plus [-_~.].) --- lib/misc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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; -- cgit v1.2.3 From be28fe7da3e197d91ec00a6c1017c053041f5a85 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 25 Apr 2010 16:12:13 +0100 Subject: Code to calculate OAuth signatures. I hope that after wrapping my mind around all of this the rest is going to be easier.. --- lib/Makefile | 2 +- lib/oauth.c | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/oauth.h | 22 +++++++++++++ 3 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 lib/oauth.c create mode 100644 lib/oauth.h 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/oauth.c b/lib/oauth.c new file mode 100644 index 00000000..ef303f14 --- /dev/null +++ b/lib/oauth.c @@ -0,0 +1,106 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* Simple OAuth client (consumer) implementation. * +* * +* Copyright 2010 Wilmer van der Gaast * +* * +* 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 +#include +#include +#include +#include "base64.h" +#include "misc.h" +#include "sha1.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 + +struct oauth_state +{ +}; + +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, CONSUMER_SECRET, strlen( CONSUMER_SECRET ) ); + sha1_append( &sha1, "&", 1 ); + if( token_secret ) + sha1_append( &sha1, token_secret, strlen( token_secret ) ); + sha1_finish( &sha1, key ); + } + else + { + g_snprintf( 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 it and we're done. */ + return base64_encode( hash, sha1_hash_size ); +} diff --git a/lib/oauth.h b/lib/oauth.h new file mode 100644 index 00000000..160e30f1 --- /dev/null +++ b/lib/oauth.h @@ -0,0 +1,22 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* Simple OAuth client (consumer) implementation. * +* * +* Copyright 2010 Wilmer van der Gaast * +* * +* 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. * +* * +\***************************************************************************/ -- cgit v1.2.3 From e9eaee6eb50cd566d699b61a4d643f64800b488b Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 25 Apr 2010 19:56:53 +0100 Subject: constified. --- lib/url.c | 2 +- lib/url.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 ); -- cgit v1.2.3 From da2efd4eaaf6a87afc35402d96d98d8001e43af7 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 25 Apr 2010 19:57:06 +0100 Subject: Some HTTP stuff. Via gdb I can make this request a token. --- lib/oauth.c | 218 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 212 insertions(+), 6 deletions(-) diff --git a/lib/oauth.c b/lib/oauth.c index ef303f14..6240d514 100644 --- a/lib/oauth.c +++ b/lib/oauth.c @@ -25,9 +25,11 @@ #include #include #include +#include "http_client.h" #include "base64.h" #include "misc.h" #include "sha1.h" +#include "url.h" #define CONSUMER_KEY "xsDNKJuNZYkZyMcu914uEA" #define CONSUMER_SECRET "FCxqcr0pXKzsF9ajmP57S3VQ8V6Drk4o2QYtqMcOszo" @@ -35,8 +37,18 @@ #define HMAC_BLOCK_SIZE 64 -struct oauth_state +struct oauth_info; +typedef void (*oauth_cb)( struct oauth_info * ); + +struct oauth_info { + oauth_cb func; + void *data; + + struct http_request *http; + + char *auth_params; + char *token; }; static char *oauth_sign( const char *method, const char *url, @@ -51,19 +63,19 @@ static char *oauth_sign( const char *method, const char *url, /* 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; + i = strlen( CONSUMER_SECRET ) + 1 + ( token_secret ? strlen( token_secret ) : 0 ); if( i > HMAC_BLOCK_SIZE ) { sha1_init( &sha1 ); - sha1_append( &sha1, CONSUMER_SECRET, strlen( CONSUMER_SECRET ) ); - sha1_append( &sha1, "&", 1 ); + sha1_append( &sha1, (uint8_t*) CONSUMER_SECRET, strlen( CONSUMER_SECRET ) ); + sha1_append( &sha1, (uint8_t*) "&", 1 ); if( token_secret ) - sha1_append( &sha1, token_secret, strlen( token_secret ) ); + sha1_append( &sha1, (uint8_t*) token_secret, strlen( token_secret ) ); sha1_finish( &sha1, key ); } else { - g_snprintf( key, HMAC_BLOCK_SIZE + 1, "%s&%s", + g_snprintf( (gchar*) key, HMAC_BLOCK_SIZE + 1, "%s&%s", CONSUMER_SECRET, token_secret ? : "" ); } @@ -104,3 +116,197 @@ static char *oauth_sign( const char *method, const char *url, /* base64_encode it and we're done. */ return base64_encode( hash, sha1_hash_size ); } + +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 ); + } + } +} + +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; +} + +GSList *oauth_params_parse( char *in ) +{ + GSList *ret = NULL; + char *amp, *eq; + + while( in && *in ) + { + eq = strchr( in, '=' ); + if( !eq ) + break; + + *eq = '\0'; + if( ( amp = strchr( eq + 1, '&' ) ) ) + *amp = '\0'; + + oauth_params_add( &ret, in, eq + 1 ); + + *eq = '='; + if( amp == NULL ) + break; + + *amp = '&'; + in = amp + 1; + } + + return ret; +} + +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 ) + { + g_string_append( str, l->data ); + if( l->next ) + g_string_append_c( str, '&' ); + } + + return g_string_free( str, FALSE ); +} + +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_params_set( ¶ms, "oauth_consumer_key", CONSUMER_KEY ); + oauth_params_set( ¶ms, "oauth_signature_method", "HMAC-SHA1" ); + + s = g_strdup_printf( "%d", (int) time( NULL ) ); + oauth_params_set( ¶ms, "oauth_timestamp", s ); + g_free( s ); + + s = oauth_nonce(); + oauth_params_set( ¶ms, "oauth_nonce", s ); + g_free( s ); + + oauth_params_set( ¶ms, "oauth_version", "1.0" ); + oauth_params_set( ¶ms, "oauth_callback", "oob" ); + + params_s = oauth_params_string( params ); + oauth_params_free( params_ ); + + s = oauth_sign( "POST", url, params_s, NULL ); + s = g_realloc( s, strlen( s ) * 3 + 1 ); + http_encode( s ); + + 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; +} + +void oauth_request_token_done( struct http_request *req ); + +void *oauth_request_token( const char *url, oauth_cb func, void *data ) +{ + struct oauth_info *st = g_new0( struct oauth_info, 1 ); + + st->func = func; + st->data = data; + + return oauth_post_request( url, NULL, oauth_request_token_done, st ); +} + +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; + + st->auth_params = g_strdup( req->reply_body ); + params = oauth_params_parse( st->auth_params ); + st->token = g_strdup( oauth_params_get( ¶ms, "oauth_token" ) ); + oauth_params_free( ¶ms ); + } + + st->func( st ); +} -- cgit v1.2.3 From 346dfd940c1439f6113c3cf80340791567de5d25 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 25 Apr 2010 20:22:02 +0100 Subject: oauth_access_token() added. I managed to increase the counter on http://twitter.com/oauth_clients/details/127170 . \o/ --- lib/oauth.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/lib/oauth.c b/lib/oauth.c index 6240d514..bc3974c1 100644 --- a/lib/oauth.c +++ b/lib/oauth.c @@ -252,7 +252,6 @@ static void *oauth_post_request( const char *url, GSList **params_, http_input_f g_free( s ); oauth_params_set( ¶ms, "oauth_version", "1.0" ); - oauth_params_set( ¶ms, "oauth_callback", "oob" ); params_s = oauth_params_string( params ); oauth_params_free( params_ ); @@ -280,19 +279,22 @@ static void *oauth_post_request( const char *url, GSList **params_, http_input_f return req; } -void oauth_request_token_done( struct http_request *req ); +static void oauth_request_token_done( struct http_request *req ); void *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" ); + return oauth_post_request( url, NULL, oauth_request_token_done, st ); } -void oauth_request_token_done( struct http_request *req ) +static void oauth_request_token_done( struct http_request *req ) { struct oauth_info *st = req->data; @@ -308,5 +310,21 @@ void oauth_request_token_done( struct http_request *req ) oauth_params_free( ¶ms ); } - st->func( st ); + //st->func( 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->token ); + oauth_params_add( ¶ms, "oauth_verifier", pin ); + + return oauth_post_request( url, ¶ms, oauth_access_token_done, st ); +} + +static void oauth_access_token_done( struct http_request *req ) +{ } -- cgit v1.2.3 From b2bc25c4483f4c2b419d91d479f38c3f07ef4226 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 26 Apr 2010 00:21:00 +0100 Subject: Added a function that generates an OAuth Authorization: HTTP header. --- lib/oauth.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 79 insertions(+), 13 deletions(-) diff --git a/lib/oauth.c b/lib/oauth.c index bc3974c1..c166d96a 100644 --- a/lib/oauth.c +++ b/lib/oauth.c @@ -49,6 +49,8 @@ struct oauth_info char *auth_params; char *token; + + char *access_token; }; static char *oauth_sign( const char *method, const char *url, @@ -146,7 +148,7 @@ void oauth_params_del( GSList **params, const char *key ) ((char*)l->data)[key_len] == '=' ) { g_free( l->data ); - *params = g_slist_remove( *params, l ); + *params = g_slist_remove( *params, l->data ); } } } @@ -224,6 +226,24 @@ char *oauth_params_string( GSList *params ) return g_string_free( str, FALSE ); } +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; @@ -240,18 +260,7 @@ static void *oauth_post_request( const char *url, GSList **params_, http_input_f if( params_ ) params = *params_; - oauth_params_set( ¶ms, "oauth_consumer_key", CONSUMER_KEY ); - oauth_params_set( ¶ms, "oauth_signature_method", "HMAC-SHA1" ); - - s = g_strdup_printf( "%d", (int) time( NULL ) ); - oauth_params_set( ¶ms, "oauth_timestamp", s ); - g_free( s ); - - s = oauth_nonce(); - oauth_params_set( ¶ms, "oauth_nonce", s ); - g_free( s ); - - oauth_params_set( ¶ms, "oauth_version", "1.0" ); + oauth_add_default_params( ¶ms ); params_s = oauth_params_string( params ); oauth_params_free( params_ ); @@ -327,4 +336,61 @@ void *oauth_access_token( const char *url, const char *pin, struct oauth_info *s static void oauth_access_token_done( struct http_request *req ) { + struct oauth_info *st = req->data; + + if( req->status_code == 200 ) + st->access_token = g_strdup( req->reply_body ); + + //st->func( st ); +} + +char *oauth_http_header( char *access_token, const char *method, const char *url ) +{ + GSList *params, *l; + char *token_secret, *sig, *params_s; + GString *ret = NULL; + + params = oauth_params_parse( access_token ); + if( params == NULL ) + goto err; + 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 ); + + params_s = oauth_params_string( params ); + sig = oauth_sign( method, url, params_s, token_secret ); + g_free( params_s ); + sig = g_realloc( sig, strlen( sig ) * 3 + 1 ); + http_encode( sig ); + + 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, "\", " ); + } + + g_string_append_printf( ret, "oauth_signature=\"%s\"", sig ); + +err: + oauth_params_free( ¶ms ); + g_free( sig ); + g_free( token_secret ); + + return ret ? g_string_free( ret, FALSE ) : NULL; } -- cgit v1.2.3 From 508c340d1d12d3ca932001d7ee1dea1a4c81bf02 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 26 Apr 2010 01:42:37 +0100 Subject: Successfully posted a tweet! Twitter's tricky. It returns vars (user_id, screen_name) in the access token that, the way I read the spec, should be included in all subsequent queries. However, stuff only started to work when I dropped those vars. This code's definitely not pretty ATM. Need to clean up now that it actually works. --- lib/oauth.c | 40 +++++++++++++++++++++++----------------- lib/oauth.h | 2 ++ protocols/twitter/twitter.c | 7 ++++++- protocols/twitter/twitter.h | 1 + protocols/twitter/twitter_http.c | 18 ++++++++++++++++-- protocols/twitter/twitter_http.h | 2 +- protocols/twitter/twitter_lib.c | 10 +++++----- 7 files changed, 54 insertions(+), 26 deletions(-) diff --git a/lib/oauth.c b/lib/oauth.c index c166d96a..6731fe7e 100644 --- a/lib/oauth.c +++ b/lib/oauth.c @@ -174,9 +174,8 @@ const char *oauth_params_get( GSList **params, const char *key ) return NULL; } -GSList *oauth_params_parse( char *in ) +static void oauth_params_parse( GSList **params, char *in ) { - GSList *ret = NULL; char *amp, *eq; while( in && *in ) @@ -189,7 +188,7 @@ GSList *oauth_params_parse( char *in ) if( ( amp = strchr( eq + 1, '&' ) ) ) *amp = '\0'; - oauth_params_add( &ret, in, eq + 1 ); + oauth_params_add( params, in, eq + 1 ); *eq = '='; if( amp == NULL ) @@ -198,8 +197,6 @@ GSList *oauth_params_parse( char *in ) *amp = '&'; in = amp + 1; } - - return ret; } void oauth_params_free( GSList **params ) @@ -311,10 +308,10 @@ static void oauth_request_token_done( struct http_request *req ) if( req->status_code == 200 ) { - GSList *params; + GSList *params = NULL; st->auth_params = g_strdup( req->reply_body ); - params = oauth_params_parse( st->auth_params ); + oauth_params_parse( ¶ms, st->auth_params ); st->token = g_strdup( oauth_params_get( ¶ms, "oauth_token" ) ); oauth_params_free( ¶ms ); } @@ -344,13 +341,13 @@ static void oauth_access_token_done( struct http_request *req ) //st->func( st ); } -char *oauth_http_header( char *access_token, const char *method, const char *url ) +char *oauth_http_header( char *access_token, const char *method, const char *url, char *args ) { - GSList *params, *l; - char *token_secret, *sig, *params_s; + GSList *params = NULL, *l; + char *token_secret, *sig, *params_s, *s; GString *ret = NULL; - params = oauth_params_parse( access_token ); + oauth_params_parse( ¶ms, access_token ); if( params == NULL ) goto err; token_secret = g_strdup( oauth_params_get( ¶ms, "oauth_token_secret" ) ); @@ -360,12 +357,6 @@ char *oauth_http_header( char *access_token, const char *method, const char *url oauth_add_default_params( ¶ms ); - params_s = oauth_params_string( params ); - sig = oauth_sign( method, url, params_s, token_secret ); - g_free( params_s ); - sig = g_realloc( sig, strlen( sig ) * 3 + 1 ); - http_encode( sig ); - ret = g_string_new( "OAuth " ); for( l = params; l; l = l->next ) { @@ -385,6 +376,21 @@ char *oauth_http_header( char *access_token, const char *method, const char *url g_string_append( ret, "\", " ); } + 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 ); + } + + params_s = oauth_params_string( params ); + sig = oauth_sign( method, url, params_s, token_secret ); + g_free( params_s ); + sig = g_realloc( sig, strlen( sig ) * 3 + 1 ); + http_encode( sig ); + g_string_append_printf( ret, "oauth_signature=\"%s\"", sig ); err: diff --git a/lib/oauth.h b/lib/oauth.h index 160e30f1..98e58c64 100644 --- a/lib/oauth.h +++ b/lib/oauth.h @@ -20,3 +20,5 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * * \***************************************************************************/ + +char *oauth_http_header( char *access_token, const char *method, const char *url, char *args ); diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index 726c7cb1..ead03c56 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -81,7 +81,10 @@ static void twitter_login( account_t *acc ) twitter_connections = g_slist_append( twitter_connections, ic ); td->user = acc->user; - td->pass = acc->pass; + if( strstr( acc->pass, "oauth_token=" ) == NULL ) + td->pass = g_strdup( acc->pass ); + else + td->oauth = g_strdup( acc->pass ); td->home_timeline_id = 0; ic->proto_data = td; @@ -118,6 +121,8 @@ static void twitter_logout( struct im_connection *ic ) if( td ) { + g_free( td->pass ); + g_free( td->oauth ); g_free( td ); } diff --git a/protocols/twitter/twitter.h b/protocols/twitter/twitter.h index 88caa104..16620e1d 100644 --- a/protocols/twitter/twitter.h +++ b/protocols/twitter/twitter.h @@ -36,6 +36,7 @@ struct twitter_data { char* user; char* pass; + char* oauth; guint64 home_timeline_id; gint main_loop_id; struct groupchat *home_timeline_gc; 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 #include @@ -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]); } -- cgit v1.2.3 From acba168df90f28fdaee26bc09d621364878dd099 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 26 Apr 2010 22:20:09 +0100 Subject: Moving two public OAuth functions into the header file. --- lib/oauth.c | 17 +---------------- lib/oauth.h | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/lib/oauth.c b/lib/oauth.c index 6731fe7e..7897b760 100644 --- a/lib/oauth.c +++ b/lib/oauth.c @@ -30,6 +30,7 @@ #include "misc.h" #include "sha1.h" #include "url.h" +#include "oauth.h" #define CONSUMER_KEY "xsDNKJuNZYkZyMcu914uEA" #define CONSUMER_SECRET "FCxqcr0pXKzsF9ajmP57S3VQ8V6Drk4o2QYtqMcOszo" @@ -37,22 +38,6 @@ #define HMAC_BLOCK_SIZE 64 -struct oauth_info; -typedef void (*oauth_cb)( struct oauth_info * ); - -struct oauth_info -{ - oauth_cb func; - void *data; - - struct http_request *http; - - char *auth_params; - char *token; - - char *access_token; -}; - static char *oauth_sign( const char *method, const char *url, const char *params, const char *token_secret ) { diff --git a/lib/oauth.h b/lib/oauth.h index 98e58c64..91696d83 100644 --- a/lib/oauth.h +++ b/lib/oauth.h @@ -21,4 +21,39 @@ * * \***************************************************************************/ +/* http://oauth.net/core/1.0a/ */ + +struct oauth_info; +typedef void (*oauth_cb)( struct oauth_info * ); + +struct oauth_info +{ + oauth_cb func; + void *data; + + struct http_request *http; + + char *auth_params; + char *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. */ +void *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 ); -- cgit v1.2.3 From 713d6111cd754ff9eae4cfa1e6de82c9824d16db Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 26 Apr 2010 22:50:48 +0100 Subject: Twitter module now generates authorize URLs. --- lib/oauth.c | 8 +++--- lib/oauth.h | 2 +- protocols/twitter/twitter.c | 64 +++++++++++++++++++++++++++++++++++---------- protocols/twitter/twitter.h | 4 +++ 4 files changed, 59 insertions(+), 19 deletions(-) diff --git a/lib/oauth.c b/lib/oauth.c index 7897b760..828d713d 100644 --- a/lib/oauth.c +++ b/lib/oauth.c @@ -297,11 +297,11 @@ static void oauth_request_token_done( struct http_request *req ) st->auth_params = g_strdup( req->reply_body ); oauth_params_parse( ¶ms, st->auth_params ); - st->token = g_strdup( oauth_params_get( ¶ms, "oauth_token" ) ); + st->request_token = g_strdup( oauth_params_get( ¶ms, "oauth_token" ) ); oauth_params_free( ¶ms ); } - //st->func( st ); + st->func( st ); } static void oauth_access_token_done( struct http_request *req ); @@ -310,7 +310,7 @@ void *oauth_access_token( const char *url, const char *pin, struct oauth_info *s { GSList *params = NULL; - oauth_params_add( ¶ms, "oauth_token", st->token ); + oauth_params_add( ¶ms, "oauth_token", st->request_token ); oauth_params_add( ¶ms, "oauth_verifier", pin ); return oauth_post_request( url, ¶ms, oauth_access_token_done, st ); @@ -323,7 +323,7 @@ static void oauth_access_token_done( struct http_request *req ) if( req->status_code == 200 ) st->access_token = g_strdup( req->reply_body ); - //st->func( st ); + st->func( st ); } char *oauth_http_header( char *access_token, const char *method, const char *url, char *args ) diff --git a/lib/oauth.h b/lib/oauth.h index 91696d83..4151d73f 100644 --- a/lib/oauth.h +++ b/lib/oauth.h @@ -34,7 +34,7 @@ struct oauth_info struct http_request *http; char *auth_params; - char *token; + char *request_token; char *access_token; }; diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index ead03c56..e2a5986b 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,44 @@ 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 void twitter_oauth_callback( struct oauth_info *info ); + +static void twitter_oauth_start( struct im_connection *ic ) +{ + oauth_request_token( TWITTER_OAUTH_REQUEST_TOKEN, twitter_oauth_callback, ic ); +} + +static void twitter_oauth_callback( struct oauth_info *info ) +{ + struct im_connection *ic = info->data; + + if( info->request_token && info->access_token == NULL ) + { + char name[strlen(ic->acc->user)+9], *msg; + + 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 ); + } +} + static char *set_eval_mode( set_t *set, char *value ) { if( g_strcasecmp( value, "one" ) == 0 || @@ -66,6 +105,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,28 +120,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; - if( strstr( acc->pass, "oauth_token=" ) == NULL ) + if( !set_getbool( &acc->set, "oauth" ) ) td->pass = g_strdup( acc->pass ); - else + 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 ); } /** diff --git a/protocols/twitter/twitter.h b/protocols/twitter/twitter.h index 16620e1d..f1937bd0 100644 --- a/protocols/twitter/twitter.h +++ b/protocols/twitter/twitter.h @@ -50,4 +50,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 -- cgit v1.2.3 From c42e8b907817fc76df4dc3b48d85858555102654 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 26 Apr 2010 23:40:11 +0100 Subject: OAuth, it lives! --- lib/oauth.c | 14 +++++++++++++- lib/oauth.h | 9 +++++++++ protocols/twitter/twitter.c | 39 ++++++++++++++++++++++++++++++++++++--- protocols/twitter/twitter.h | 1 + 4 files changed, 59 insertions(+), 4 deletions(-) diff --git a/lib/oauth.c b/lib/oauth.c index 828d713d..7f60f4f5 100644 --- a/lib/oauth.c +++ b/lib/oauth.c @@ -301,6 +301,7 @@ static void oauth_request_token_done( struct http_request *req ) oauth_params_free( ¶ms ); } + st->stage = OAUTH_REQUEST_TOKEN; st->func( st ); } @@ -321,8 +322,19 @@ static void oauth_access_token_done( struct http_request *req ) struct oauth_info *st = req->data; if( req->status_code == 200 ) - st->access_token = g_strdup( req->reply_body ); + { + GSList *params; + 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 ); } diff --git a/lib/oauth.h b/lib/oauth.h index 4151d73f..30486b98 100644 --- a/lib/oauth.h +++ b/lib/oauth.h @@ -26,8 +26,17 @@ struct oauth_info; typedef void (*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; diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index e2a5986b..eb05d9d5 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -69,17 +69,29 @@ static void twitter_oauth_callback( struct oauth_info *info ); static void twitter_oauth_start( struct im_connection *ic ) { + imcb_log( ic, "Requesting OAuth request token" ); + oauth_request_token( TWITTER_OAUTH_REQUEST_TOKEN, twitter_oauth_callback, ic ); } static void twitter_oauth_callback( struct oauth_info *info ) { struct im_connection *ic = info->data; + struct twitter_data *td = ic->proto_data; - if( info->request_token && info->access_token == NULL ) + 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; + } + + td->oauth_info = info; + 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.", @@ -87,6 +99,19 @@ static void twitter_oauth_callback( struct oauth_info *info ) 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; + } + + td->oauth = g_strdup( info->access_token ); + + twitter_main_loop_start( ic ); + } } static char *set_eval_mode( set_t *set, char *value ) @@ -170,12 +195,20 @@ 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 == NULL ) + oauth_access_token( TWITTER_OAUTH_ACCESS_TOKEN, message, td->oauth_info ); + 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 f1937bd0..9c046b66 100644 --- a/protocols/twitter/twitter.h +++ b/protocols/twitter/twitter.h @@ -37,6 +37,7 @@ 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; -- cgit v1.2.3 From 78a2f1e4ac9aeb2c041059eb7a9578d34bd60d50 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 26 Apr 2010 23:47:35 +0100 Subject: Escaping in oauth_nonce(). Not sure if the escaping is entirely right ATM... :-( --- lib/oauth.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/oauth.c b/lib/oauth.c index 7f60f4f5..9a372082 100644 --- a/lib/oauth.c +++ b/lib/oauth.c @@ -107,9 +107,14 @@ static char *oauth_sign( const char *method, const char *url, static char *oauth_nonce() { unsigned char bytes[9]; + char *ret; random_bytes( bytes, sizeof( bytes ) ); - return base64_encode( bytes, sizeof( bytes ) ); + ret = base64_encode( bytes, sizeof( bytes ) ); + ret = g_realloc( ret, strlen( ret ) * 3 + 1 ); + http_encode( ret ); + + return ret; } void oauth_params_add( GSList **params, const char *key, const char *value ) -- cgit v1.2.3 From 288b215ca64d09ea6a49cf9ff1fcc7682b7607ec Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 26 Apr 2010 23:50:25 +0100 Subject: Save the OAuth token in the acct structs so it doesn't have to be rerequested every time. --- protocols/twitter/twitter.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index eb05d9d5..54bee2f1 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -110,6 +110,11 @@ static void twitter_oauth_callback( struct oauth_info *info ) 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 ); } } -- cgit v1.2.3 From ee84bdbc2510fa09bd0f67e14d06e69c2626d0f1 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 27 Apr 2010 23:11:11 +0100 Subject: The escaping, I fixed it for you. More expensive code this way and most of the vars don't need escaping. But this shouldn't be so fragile anymore. --- lib/oauth.c | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/lib/oauth.c b/lib/oauth.c index 9a372082..0ab94c96 100644 --- a/lib/oauth.c +++ b/lib/oauth.c @@ -100,21 +100,21 @@ static char *oauth_sign( const char *method, const char *url, sha1_append( &sha1, hash, sha1_hash_size ); sha1_finish( &sha1, hash ); - /* base64_encode it and we're done. */ - return base64_encode( hash, sha1_hash_size ); + /* 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]; - char *ret; random_bytes( bytes, sizeof( bytes ) ); - ret = base64_encode( bytes, sizeof( bytes ) ); - ret = g_realloc( ret, strlen( ret ) * 3 + 1 ); - http_encode( ret ); - - return ret; + return base64_encode( bytes, sizeof( bytes ) ); } void oauth_params_add( GSList **params, const char *key, const char *value ) @@ -166,7 +166,7 @@ const char *oauth_params_get( GSList **params, const char *key ) static void oauth_params_parse( GSList **params, char *in ) { - char *amp, *eq; + char *amp, *eq, *s; while( in && *in ) { @@ -178,7 +178,10 @@ static void oauth_params_parse( GSList **params, char *in ) if( ( amp = strchr( eq + 1, '&' ) ) ) *amp = '\0'; - oauth_params_add( params, in, eq + 1 ); + s = g_strdup( eq + 1 ); + http_decode( s ); + oauth_params_add( params, in, s ); + g_free( s ); *eq = '='; if( amp == NULL ) @@ -205,7 +208,15 @@ char *oauth_params_string( GSList *params ) for( l = params; l; l = l->next ) { - g_string_append( str, l->data ); + 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, '&' ); } @@ -253,9 +264,6 @@ static void *oauth_post_request( const char *url, GSList **params_, http_input_f oauth_params_free( params_ ); s = oauth_sign( "POST", url, params_s, NULL ); - s = g_realloc( s, strlen( s ) * 3 + 1 ); - http_encode( s ); - post = g_strdup_printf( "%s&oauth_signature=%s", params_s, s ); g_free( params_s ); g_free( s ); @@ -389,11 +397,8 @@ char *oauth_http_header( char *access_token, const char *method, const char *url params_s = oauth_params_string( params ); sig = oauth_sign( method, url, params_s, token_secret ); - g_free( params_s ); - sig = g_realloc( sig, strlen( sig ) * 3 + 1 ); - http_encode( sig ); - g_string_append_printf( ret, "oauth_signature=\"%s\"", sig ); + g_free( params_s ); err: oauth_params_free( ¶ms ); -- cgit v1.2.3 From 18dbb20242719f7537ad1a18a9a3befa2eefd502 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 27 Apr 2010 23:42:07 +0100 Subject: Valgrind cleanup. --- lib/oauth.c | 40 ++++++++++++++++++++++++++++++++++------ lib/oauth.h | 12 +++++++++--- protocols/twitter/twitter.c | 30 +++++++++++++++++++++--------- 3 files changed, 64 insertions(+), 18 deletions(-) diff --git a/lib/oauth.c b/lib/oauth.c index 0ab94c96..97017043 100644 --- a/lib/oauth.c +++ b/lib/oauth.c @@ -224,6 +224,17 @@ char *oauth_params_string( GSList *params ) 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; @@ -285,7 +296,7 @@ static void *oauth_post_request( const char *url, GSList **params_, http_input_f static void oauth_request_token_done( struct http_request *req ); -void *oauth_request_token( const char *url, oauth_cb func, void *data ) +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; @@ -295,7 +306,13 @@ void *oauth_request_token( const char *url, oauth_cb func, void *data ) oauth_params_add( ¶ms, "oauth_callback", "oob" ); - return oauth_post_request( url, NULL, oauth_request_token_done, st ); + 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 ) @@ -315,19 +332,21 @@ static void oauth_request_token_done( struct http_request *req ) } st->stage = OAUTH_REQUEST_TOKEN; - st->func( st ); + 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 ) +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 ); - return oauth_post_request( url, ¶ms, oauth_access_token_done, st ); + 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 ) @@ -336,7 +355,7 @@ static void oauth_access_token_done( struct http_request *req ) if( req->status_code == 200 ) { - GSList *params; + GSList *params = NULL; const char *token, *token_secret; oauth_params_parse( ¶ms, req->reply_body ); @@ -349,6 +368,7 @@ static void oauth_access_token_done( struct http_request *req ) 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 ) @@ -357,9 +377,12 @@ char *oauth_http_header( char *access_token, const char *method, const char *url 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; @@ -367,6 +390,7 @@ char *oauth_http_header( char *access_token, const char *method, const char *url oauth_add_default_params( ¶ms ); + /* Start building the OAuth header. 'key="value", '... */ ret = g_string_new( "OAuth " ); for( l = params; l; l = l->next ) { @@ -386,6 +410,9 @@ char *oauth_http_header( char *access_token, const char *method, const char *url 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, '?' ) ) ) @@ -395,6 +422,7 @@ char *oauth_http_header( char *access_token, const char *method, const char *url 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 ); diff --git a/lib/oauth.h b/lib/oauth.h index 30486b98..a61650cc 100644 --- a/lib/oauth.h +++ b/lib/oauth.h @@ -24,7 +24,10 @@ /* http://oauth.net/core/1.0a/ */ struct oauth_info; -typedef void (*oauth_cb)( 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 { @@ -52,13 +55,13 @@ struct oauth_info 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. */ -void *oauth_request_token( const char *url, oauth_cb func, void *data ); +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 ); +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 @@ -66,3 +69,6 @@ void *oauth_access_token( const char *url, const char *pin, struct oauth_info *s 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/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index 54bee2f1..8ea1a56e 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -65,20 +65,27 @@ static void twitter_main_loop_start( struct im_connection *ic ) td->main_loop_id = b_timeout_add(60000, twitter_main_loop, ic); } -static void twitter_oauth_callback( struct oauth_info *info ); +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" ); - oauth_request_token( TWITTER_OAUTH_REQUEST_TOKEN, twitter_oauth_callback, ic ); + td->oauth_info = oauth_request_token( + TWITTER_OAUTH_REQUEST_TOKEN, twitter_oauth_callback, ic ); } -static void twitter_oauth_callback( struct oauth_info *info ) +static gboolean twitter_oauth_callback( struct oauth_info *info ) { struct im_connection *ic = info->data; - struct twitter_data *td = ic->proto_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; @@ -87,11 +94,9 @@ static void twitter_oauth_callback( struct oauth_info *info ) { imcb_error( ic, "OAuth error: %s", info->http->status_string ); imc_logout( ic, TRUE ); - return; + return FALSE; } - td->oauth_info = info; - 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.", @@ -105,7 +110,7 @@ static void twitter_oauth_callback( struct oauth_info *info ) { imcb_error( ic, "OAuth error: %s", info->http->status_string ); imc_logout( ic, TRUE ); - return; + return FALSE; } td->oauth = g_strdup( info->access_token ); @@ -117,6 +122,8 @@ static void twitter_oauth_callback( struct oauth_info *info ) twitter_main_loop_start( ic ); } + + return TRUE; } static char *set_eval_mode( set_t *set, char *value ) @@ -187,6 +194,8 @@ 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 ); @@ -205,8 +214,11 @@ static int twitter_buddy_msg( struct im_connection *ic, char *who, char *message if (g_strncasecmp(who, "twitter_", 8) == 0 && g_strcasecmp(who + 8, ic->acc->user) == 0) { - if( set_getbool( &ic->acc->set, "oauth" ) && td->oauth == NULL ) + 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); } -- cgit v1.2.3 From 0bff877cf81754283eac52ee49fedc3872cd0d4c Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 27 Apr 2010 23:49:58 +0100 Subject: Valgrind-clean now. And decent handling of errors (wrong PIN). --- lib/oauth.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/oauth.c b/lib/oauth.c index 97017043..9fb83922 100644 --- a/lib/oauth.c +++ b/lib/oauth.c @@ -272,7 +272,7 @@ static void *oauth_post_request( const char *url, GSList **params_, http_input_f oauth_add_default_params( ¶ms ); params_s = oauth_params_string( params ); - oauth_params_free( params_ ); + oauth_params_free( ¶ms ); s = oauth_sign( "POST", url, params_s, NULL ); post = g_strdup_printf( "%s&oauth_signature=%s", params_s, s ); @@ -353,6 +353,8 @@ 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; -- cgit v1.2.3 From 3f668e478bbaa062cf6b01e627cb39885200e5ff Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 28 Apr 2010 08:38:25 +0100 Subject: OAuth documentation update. --- doc/user-guide/commands.xml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) 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 @@ To send tweets yourself, send them to the twitter_(yourusername) contact, or just write in the groupchat channel if you enabled that option. + + + 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 help set oauth.) + @@ -702,6 +706,25 @@ + + true + + + + This enables OAuth authentication for Twitter accounts. From June 2010 this will be mandatory. + + + + With OAuth enabled, you shouldn't tell BitlBee your Twitter password. Just add your account with a bogus password and type account on. 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. + + + + The resulting access token will be saved permanently, so you have to do this only once. + + + + + both both, root, user, none -- cgit v1.2.3