diff options
author | Wilmer van der Gaast <wilmer@gaast.net> | 2010-05-02 22:20:09 +0100 |
---|---|---|
committer | Wilmer van der Gaast <wilmer@gaast.net> | 2010-05-02 22:20:09 +0100 |
commit | 3b878a1c3d0888b0669c30433efab914061fa61d (patch) | |
tree | 629406231f5b1be0236e89b928611f15e0ecba91 | |
parent | 839189b091ee82937666bcaa328a7d3ecd1c573b (diff) | |
parent | f4b0911d037c02f8b9190518b5efda4368dcc25b (diff) |
OAuth sanity fix: Twitter-specific stuff should *not* be in lib/oauth.c.
Somewhat intrusive, should've done this right immediately. :-/
-rw-r--r-- | lib/oauth.c | 118 | ||||
-rw-r--r-- | lib/oauth.h | 26 | ||||
-rw-r--r-- | protocols/twitter/twitter.c | 42 | ||||
-rw-r--r-- | protocols/twitter/twitter.h | 5 | ||||
-rw-r--r-- | protocols/twitter/twitter_http.c | 10 | ||||
-rw-r--r-- | protocols/twitter/twitter_http.h | 4 | ||||
-rw-r--r-- | protocols/twitter/twitter_lib.c | 12 |
7 files changed, 127 insertions, 90 deletions
diff --git a/lib/oauth.c b/lib/oauth.c index 4a465a2f..c60a5a52 100644 --- a/lib/oauth.c +++ b/lib/oauth.c @@ -32,14 +32,10 @@ #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 ) + const char *params, struct oauth_info *oi ) { sha1_state_t sha1; uint8_t hash[sha1_hash_size]; @@ -50,20 +46,20 @@ 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( oi->sp->consumer_secret ) + 1 + ( oi->token_secret ? strlen( oi->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*) oi->sp->consumer_secret, strlen( oi->sp->consumer_secret ) ); sha1_append( &sha1, (uint8_t*) "&", 1 ); - if( token_secret ) - sha1_append( &sha1, (uint8_t*) token_secret, strlen( token_secret ) ); + if( oi->token_secret ) + sha1_append( &sha1, (uint8_t*) oi->token_secret, strlen( oi->token_secret ) ); sha1_finish( &sha1, key ); } else { g_snprintf( (gchar*) key, HMAC_BLOCK_SIZE + 1, "%s&%s", - CONSUMER_SECRET, token_secret ? : "" ); + oi->sp->consumer_secret, oi->token_secret ? : "" ); } /* Inner part: H(K XOR 0x36, text) */ @@ -228,18 +224,19 @@ void oauth_info_free( struct oauth_info *info ) { if( info ) { - g_free( info->auth_params ); + g_free( info->auth_url ); g_free( info->request_token ); - g_free( info->access_token ); + g_free( info->token ); + g_free( info->token_secret ); g_free( info ); } } -static void oauth_add_default_params( GSList **params ) +static void oauth_add_default_params( GSList **params, const struct oauth_service *sp ) { char *s; - oauth_params_set( params, "oauth_consumer_key", CONSUMER_KEY ); + oauth_params_set( params, "oauth_consumer_key", sp->consumer_key ); oauth_params_set( params, "oauth_signature_method", "HMAC-SHA1" ); s = g_strdup_printf( "%d", (int) time( NULL ) ); @@ -253,7 +250,7 @@ static void oauth_add_default_params( GSList **params ) oauth_params_set( params, "oauth_version", "1.0" ); } -static void *oauth_post_request( const char *url, GSList **params_, http_input_function func, void *data ) +static void *oauth_post_request( const char *url, GSList **params_, http_input_function func, struct oauth_info *oi ) { GSList *params = NULL; char *s, *params_s, *post; @@ -269,12 +266,12 @@ static void *oauth_post_request( const char *url, GSList **params_, http_input_f if( params_ ) params = *params_; - oauth_add_default_params( ¶ms ); + oauth_add_default_params( ¶ms, oi->sp ); params_s = oauth_params_string( params ); oauth_params_free( ¶ms ); - s = oauth_sign( "POST", url, params_s, NULL ); + s = oauth_sign( "POST", url, params_s, oi ); post = g_strdup_printf( "%s&oauth_signature=%s", params_s, s ); g_free( params_s ); g_free( s ); @@ -288,7 +285,7 @@ static void *oauth_post_request( const char *url, GSList **params_, http_input_f g_free( post ); req = http_dorequest( url_p.host, url_p.port, url_p.proto == PROTO_HTTPS, - s, func, data ); + s, func, oi ); g_free( s ); return req; @@ -296,17 +293,18 @@ static void *oauth_post_request( const char *url, GSList **params_, http_input_f 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 *oauth_request_token( const struct oauth_service *sp, oauth_cb func, void *data ) { struct oauth_info *st = g_new0( struct oauth_info, 1 ); GSList *params = NULL; st->func = func; st->data = data; + st->sp = sp; oauth_params_add( ¶ms, "oauth_callback", "oob" ); - if( !oauth_post_request( url, ¶ms, oauth_request_token_done, st ) ) + if( !oauth_post_request( sp->url_request_token, ¶ms, oauth_request_token_done, st ) ) { oauth_info_free( st ); return NULL; @@ -325,28 +323,26 @@ static void oauth_request_token_done( struct http_request *req ) { GSList *params = NULL; - st->auth_params = g_strdup( req->reply_body ); - oauth_params_parse( ¶ms, st->auth_params ); + st->auth_url = g_strdup_printf( "%s?%s", st->sp->url_authorize, req->reply_body ); + oauth_params_parse( ¶ms, req->reply_body ); 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 ); + 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 ) +gboolean oauth_access_token( 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 ); + return oauth_post_request( st->sp->url_access_token, ¶ms, oauth_access_token_done, st ) != NULL; } static void oauth_access_token_done( struct http_request *req ) @@ -358,39 +354,32 @@ static void oauth_access_token_done( struct http_request *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 ); + st->token = g_strdup( oauth_params_get( ¶ms, "oauth_token" ) ); + st->token_secret = g_strdup( oauth_params_get( ¶ms, "oauth_token_secret" ) ); oauth_params_free( ¶ms ); } st->stage = OAUTH_ACCESS_TOKEN; - st->func( st ); - oauth_info_free( st ); + if( st->func( st ) ) + { + /* Don't need these anymore, but keep the rest. */ + g_free( st->auth_url ); + st->auth_url = NULL; + g_free( st->request_token ); + st->request_token = NULL; + } } -char *oauth_http_header( char *access_token, const char *method, const char *url, char *args ) +char *oauth_http_header( struct oauth_info *oi, const char *method, const char *url, char *args ) { GSList *params = NULL, *l; - char *token_secret = NULL, *sig = NULL, *params_s, *s; + char *sig = NULL, *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 ); + oauth_params_add( ¶ms, "oauth_token", oi->token ); + oauth_add_default_params( ¶ms, oi->sp ); /* Start building the OAuth header. 'key="value", '... */ ret = g_string_new( "OAuth " ); @@ -420,20 +409,45 @@ char *oauth_http_header( char *access_token, const char *method, const char *url if( ( s = strchr( url, '?' ) ) ) { s = g_strdup( s + 1 ); - oauth_params_parse( ¶ms, s + 1 ); + oauth_params_parse( ¶ms, s ); 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 ); + sig = oauth_sign( method, url, params_s, oi ); 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; } + +char *oauth_to_string( struct oauth_info *oi ) +{ + GSList *params = NULL; + char *ret; + + oauth_params_add( ¶ms, "oauth_token", oi->token ); + oauth_params_add( ¶ms, "oauth_token_secret", oi->token_secret ); + ret = oauth_params_string( params ); + oauth_params_free( ¶ms ); + + return ret; +} + +struct oauth_info *oauth_from_string( char *in, const struct oauth_service *sp ) +{ + struct oauth_info *oi = g_new0( struct oauth_info, 1 ); + GSList *params = NULL; + + oauth_params_parse( ¶ms, in ); + oi->token = g_strdup( oauth_params_get( ¶ms, "oauth_token" ) ); + oi->token_secret = g_strdup( oauth_params_get( ¶ms, "oauth_token_secret" ) ); + oauth_params_free( ¶ms ); + oi->sp = sp; + + return oi; +} diff --git a/lib/oauth.h b/lib/oauth.h index a61650cc..5dfe0ae5 100644 --- a/lib/oauth.h +++ b/lib/oauth.h @@ -39,36 +39,52 @@ typedef enum struct oauth_info { oauth_stage_t stage; + const struct oauth_service *sp; oauth_cb func; void *data; struct http_request *http; - char *auth_params; + char *auth_url; char *request_token; - char *access_token; + char *token; + char *token_secret; +}; + +struct oauth_service +{ + char *url_request_token; + char *url_access_token; + char *url_authorize; + + char *consumer_key; + char *consumer_secret; }; /* 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 ); +struct oauth_info *oauth_request_token( const struct oauth_service *sp, 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 ); +gboolean oauth_access_token( 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 ); +char *oauth_http_header( struct oauth_info *oi, 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 ); + +/* Convert to and back from strings, for easier saving. */ +char *oauth_to_string( struct oauth_info *oi ); +struct oauth_info *oauth_from_string( char *in, const struct oauth_service *sp ); diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index f5baeeea..9c7b060c 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -27,7 +27,6 @@ #include "twitter_http.h" #include "twitter_lib.h" - /** * Main loop function */ @@ -65,6 +64,16 @@ static void twitter_main_loop_start( struct im_connection *ic ) td->main_loop_id = b_timeout_add(60000, twitter_main_loop, ic); } + +static const struct oauth_service twitter_oauth = +{ + "http://api.twitter.com/oauth/request_token", + "http://api.twitter.com/oauth/access_token", + "http://api.twitter.com/oauth/authorize", + .consumer_key = "xsDNKJuNZYkZyMcu914uEA", + .consumer_secret = "FCxqcr0pXKzsF9ajmP57S3VQ8V6Drk4o2QYtqMcOszo", +}; + static gboolean twitter_oauth_callback( struct oauth_info *info ); static void twitter_oauth_start( struct im_connection *ic ) @@ -73,8 +82,7 @@ static void twitter_oauth_start( struct im_connection *ic ) imcb_log( ic, "Requesting OAuth request token" ); - td->oauth_info = oauth_request_token( - TWITTER_OAUTH_REQUEST_TOKEN, twitter_oauth_callback, ic ); + td->oauth_info = oauth_request_token( &twitter_oauth, twitter_oauth_callback, ic ); } static gboolean twitter_oauth_callback( struct oauth_info *info ) @@ -99,26 +107,24 @@ static gboolean twitter_oauth_callback( struct 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.", - TWITTER_OAUTH_AUTHORIZE, info->auth_params ); + "%s and respond with the resulting PIN code.", + info->auth_url ); imcb_buddy_msg( ic, name, msg, 0, 0 ); g_free( msg ); } else if( info->stage == OAUTH_ACCESS_TOKEN ) { - if( info->access_token == NULL ) + if( info->token == NULL || info->token_secret == 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 ); + ic->acc->pass = oauth_to_string( info ); twitter_main_loop_start( ic ); } @@ -126,6 +132,7 @@ static gboolean twitter_oauth_callback( struct oauth_info *info ) return TRUE; } + static char *set_eval_mode( set_t *set, char *value ) { if( g_strcasecmp( value, "one" ) == 0 || @@ -164,14 +171,14 @@ static void twitter_login( account_t *acc ) 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->oauth_info = oauth_from_string( acc->pass, &twitter_oauth ); td->home_timeline_id = 0; 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 ) + if( td->pass || td->oauth_info ) twitter_main_loop_start( ic ); else twitter_oauth_start( ic ); @@ -196,9 +203,7 @@ 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 ); } @@ -215,10 +220,15 @@ 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_info ) + if( set_getbool( &ic->acc->set, "oauth" ) && + td->oauth_info && td->oauth_info->token == NULL ) { - oauth_access_token( TWITTER_OAUTH_ACCESS_TOKEN, message, td->oauth_info ); - td->oauth_info = NULL; + if( !oauth_access_token( message, td->oauth_info ) ) + { + imcb_error( ic, "OAuth error: %s", "Failed to send access token request" ); + imc_logout( ic, TRUE ); + return FALSE; + } } else twitter_post_status(ic, message); diff --git a/protocols/twitter/twitter.h b/protocols/twitter/twitter.h index 9c046b66..24f61e42 100644 --- a/protocols/twitter/twitter.h +++ b/protocols/twitter/twitter.h @@ -36,7 +36,6 @@ struct twitter_data { char* user; char* pass; - char* oauth; struct oauth_info *oauth_info; guint64 home_timeline_id; gint main_loop_id; @@ -51,8 +50,4 @@ 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 93e315fa..51f437df 100644 --- a/protocols/twitter/twitter_http.c +++ b/protocols/twitter/twitter_http.c @@ -28,7 +28,6 @@ * * ****************************************************************************/ -#include "twitter_http.h" #include "twitter.h" #include "bitlbee.h" #include "url.h" @@ -38,6 +37,8 @@ #include <ctype.h> #include <errno.h> +#include "twitter_http.h" + char *twitter_url_append(char *url, char *key, char* value); @@ -45,7 +46,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* oauth_token, char** arguments, int arguments_len) +void *twitter_http(char *url_string, http_input_function func, gpointer data, int is_post, char* user, char* pass, struct oauth_info* oi, char** arguments, int arguments_len) { url_t *url = g_new0( url_t, 1 ); char *tmp; @@ -110,12 +111,11 @@ 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 (oauth_token) + if (oi) { char *full_header; - full_header = oauth_http_header(oauth_token, - is_post ? "POST" : "GET", + full_header = oauth_http_header(oi, is_post ? "POST" : "GET", url_string, url_arguments); tmp = g_strdup_printf("%sAuthorization: %s\r\n", request, full_header); diff --git a/protocols/twitter/twitter_http.h b/protocols/twitter/twitter_http.h index 1e29fb99..5ef2530f 100644 --- a/protocols/twitter/twitter_http.h +++ b/protocols/twitter/twitter_http.h @@ -27,8 +27,10 @@ #include "nogaim.h" #include "http_client.h" +struct oauth_info; + 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); + char* user, char* pass, struct oauth_info *oi, char** arguments, int arguments_len); #endif //_TWITTER_HTTP_H diff --git a/protocols/twitter/twitter_lib.c b/protocols/twitter/twitter_lib.c index c91f8bbf..ee6e39fe 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, td->oauth, args, 2); + twitter_http(TWITTER_FRIENDS_IDS_URL, twitter_http_get_friends_ids, ic, 0, td->user, td->pass, td->oauth_info, 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, td->oauth, 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_info, args, td->home_timeline_id ? 4 : 2); g_free(args[1]); if (td->home_timeline_id) { @@ -509,7 +509,7 @@ static void twitter_http_get_home_timeline(struct http_request *req) if (req->status_code == 200) { td->http_fails = 0; - if (!ic->flags & OPT_LOGGED_IN) + if (!(ic->flags & OPT_LOGGED_IN)) imcb_connected(ic); } else if (req->status_code == 401) @@ -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, td->oauth, args, 2); + twitter_http(TWITTER_SHOW_FRIENDS_URL, twitter_http_get_statuses_friends, ic, 0, td->user, td->pass, td->oauth_info, 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, td->oauth, args, 2); + twitter_http(TWITTER_STATUS_UPDATE_URL, twitter_http_post_status, ic, 1, td->user, td->pass, td->oauth_info, 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, td->oauth, args, 4); + twitter_http(TWITTER_DIRECT_MESSAGES_NEW_URL, twitter_http_post_status, ic, 1, td->user, td->pass, td->oauth_info, args, 4); // g_free(args[1]); // g_free(args[3]); } |