From db57e7c0d4a88e2a2b0862afa20a1255dfffc58d Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 19 Apr 2010 15:08:34 +0200 Subject: 1.2.6a. I fail. :-( The 1.2.6 I just released has a groupchat mode in the Twitter module that doesn't actually work... --- bitlbee.h | 2 +- doc/CHANGES | 6 ++++++ protocols/twitter/twitter.c | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/bitlbee.h b/bitlbee.h index 0b31bbc1..f4e03d76 100644 --- a/bitlbee.h +++ b/bitlbee.h @@ -34,7 +34,7 @@ #define _WIN32_WINNT 0x0501 #define PACKAGE "BitlBee" -#define BITLBEE_VERSION "1.2.6" +#define BITLBEE_VERSION "1.2.6a" #define VERSION BITLBEE_VERSION #define BITLBEE_VER(a,b,c) (((a) << 16) + ((b) << 8) + (c)) #define BITLBEE_VERSION_CODE BITLBEE_VER(1, 2, 6) diff --git a/doc/CHANGES b/doc/CHANGES index 94330bf9..45b16e28 100644 --- a/doc/CHANGES +++ b/doc/CHANGES @@ -3,6 +3,12 @@ found in the bzr commit logs, for example you can try: http://bugs.bitlbee.org/bitlbee/timeline?daysback=90&changeset=on +Version 1.2.6a: +- Fixed a typo that renders the Twitter groupchat mode unusable. A last- + minute change that came a few minutes late. + +Finished 19 Apr 2010 + Version 1.2.6: - Native (very basic) support for Twitter, implemented by Geert Mulders. Currently supported are posting tweets, reading the ones of people you diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index 29be8a96..726c7cb1 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -54,7 +54,7 @@ static char *set_eval_mode( set_t *set, char *value ) { if( g_strcasecmp( value, "one" ) == 0 || g_strcasecmp( value, "many" ) == 0 || - g_strcasecmp( value, "char" ) == 0 ) + g_strcasecmp( value, "chat" ) == 0 ) return value; else return NULL; -- cgit v1.2.3 From dd0dcac7a5814a4358e4a11388bd9155eeca9c53 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 24 Apr 2010 12:51:08 +0100 Subject: Debian package changelog entry. --- debian/changelog | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/debian/changelog b/debian/changelog index f969b410..56d0a551 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,12 @@ +bitlbee (1.2.6a-1) unstable; urgency=low + + * New upstream version. + * Native support for Twitter. + * Fixed /WHOIS response format. (Closes: #576120) + * Problems with bitlbee-skype are solved by now. (Closes: #575572) + + -- Wilmer van der Gaast Tue, 20 Apr 2010 00:34:51 +0200 + bitlbee (1.2.5-1) unstable; urgency=low * New upstream version. -- cgit v1.2.3 From f1b7711f566163ff27a8f13ae3ccc7214a24fe70 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 24 Apr 2010 15:15:54 +0100 Subject: Also override package version number for bitlbee-dev when $BITLBEE_VERSION is set. --- debian/rules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/rules b/debian/rules index 788e5006..0c656fd8 100755 --- a/debian/rules +++ b/debian/rules @@ -98,7 +98,7 @@ binary-indep: install-indep cd debian/bitlbee-dev; \ find usr -type f -exec md5sum {} \; > DEBIAN/md5sums - dpkg-gencontrol -ldebian/changelog -isp -pbitlbee-dev -Pdebian/bitlbee-dev + dpkg-gencontrol -ldebian/changelog -isp -pbitlbee-dev -Pdebian/bitlbee-dev -v1:$(BITLBEE_VERSION)-0 dpkg --build debian/bitlbee-dev .. -- cgit v1.2.3 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 From a7c6d0e9a9d985534dc18d450399d6a7ece6c592 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 30 Apr 2010 23:51:18 +0100 Subject: Set HTML-flag on Twitter accounts so </> and all get converted back. (Probably also fixes potential problems with outgoing messages.) --- protocols/twitter/twitter.c | 1 + 1 file changed, 1 insertion(+) diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index 8ea1a56e..f5baeeea 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -158,6 +158,7 @@ static void twitter_login( account_t *acc ) twitter_connections = g_slist_append( twitter_connections, ic ); ic->proto_data = td; + ic->flags |= OPT_DOES_HTML; td->user = acc->user; if( !set_getbool( &acc->set, "oauth" ) ) -- cgit v1.2.3 From 85ef57f94436f23447c0d8603b52977824381854 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 30 Apr 2010 23:53:29 +0100 Subject: NULL-initialize two vars that weren't and should. --- lib/oauth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/oauth.c b/lib/oauth.c index 9fb83922..4a465a2f 100644 --- a/lib/oauth.c +++ b/lib/oauth.c @@ -376,7 +376,7 @@ static void oauth_access_token_done( struct http_request *req ) 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; + char *token_secret = NULL, *sig = NULL, *params_s, *s; GString *ret = NULL; /* First, get the two pieces of info from the access token that we need. */ -- cgit v1.2.3 From c2ecadc08daa5163f4c90aef36de0e33d0d44f16 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 1 May 2010 14:53:59 +0100 Subject: Cleaned up OAuth stuff: consumer key/secret should *not* be in lib/oauth.c. Keep it in the Twitter module, and use the oauth_info struct through the whole session to keep all this together. --- lib/oauth.c | 91 +++++++++++++++++----------------------- lib/oauth.h | 22 +++++++--- protocols/twitter/twitter.c | 46 ++++++++++++-------- protocols/twitter/twitter.h | 5 --- protocols/twitter/twitter_http.c | 10 ++--- protocols/twitter/twitter_http.h | 4 +- protocols/twitter/twitter_lib.c | 12 +++--- 7 files changed, 98 insertions(+), 92 deletions(-) diff --git a/lib/oauth.c b/lib/oauth.c index 4a465a2f..4c93cbf4 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, 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( 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,18 @@ 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; } diff --git a/lib/oauth.h b/lib/oauth.h index a61650cc..849375be 100644 --- a/lib/oauth.h +++ b/lib/oauth.h @@ -39,36 +39,48 @@ typedef enum struct oauth_info { oauth_stage_t stage; + 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( 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 ); diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index f5baeeea..6d43d819 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 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 ) { 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 ); + //g_free( ic->acc->pass ); + //ic->acc->pass = g_strdup( info->access_token ); 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 || @@ -163,15 +170,15 @@ static void twitter_login( account_t *acc ) td->user = acc->user; 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 ); + //else if( strstr( acc->pass, "oauth_token=" ) ) + // td->oauth = g_strdup( acc->pass ); 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 #include +#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]); } -- cgit v1.2.3 From f4b0911d037c02f8b9190518b5efda4368dcc25b Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 1 May 2010 15:10:32 +0100 Subject: Save the credentials again. --- lib/oauth.c | 27 +++++++++++++++++++++++++++ lib/oauth.h | 4 ++++ protocols/twitter/twitter.c | 8 ++++---- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/lib/oauth.c b/lib/oauth.c index 4c93cbf4..8012c37a 100644 --- a/lib/oauth.c +++ b/lib/oauth.c @@ -424,3 +424,30 @@ char *oauth_http_header( struct oauth_info *oi, const char *method, const char * 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, 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 849375be..6b51dc2c 100644 --- a/lib/oauth.h +++ b/lib/oauth.h @@ -84,3 +84,7 @@ char *oauth_http_header( struct oauth_info *oi, const char *method, const char * /* 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, struct oauth_service *sp ); diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index 6d43d819..589f2088 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -123,8 +123,8 @@ static gboolean twitter_oauth_callback( struct oauth_info *info ) /* 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 ); + g_free( ic->acc->pass ); + ic->acc->pass = oauth_to_string( info ); twitter_main_loop_start( ic ); } @@ -170,8 +170,8 @@ static void twitter_login( account_t *acc ) td->user = acc->user; 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 ); + else if( strstr( acc->pass, "oauth_token=" ) ) + td->oauth_info = oauth_from_string( acc->pass, &twitter_oauth ); td->home_timeline_id = 0; sprintf( name, "twitter_%s", acc->user ); -- cgit v1.2.3 From 4273158e2bd219602e502d593040cccb234d4805 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 1 May 2010 15:16:26 +0100 Subject: Oops. rt_mon start at 0=Jan, causing off-by-a-month timestamps shown to the user. Temporary work-around: set timezone to +720 or so. :-P --- protocols/nogaim.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protocols/nogaim.c b/protocols/nogaim.c index fca8b302..98325c89 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -1193,7 +1193,7 @@ static char *format_timestamp( irc_t *irc, time_t msg_ts ) else return g_strdup_printf( "\x02[\x02\x02\x02%04d-%02d-%02d " "%02d:%02d:%02d\x02]\x02 ", - msg.tm_year + 1900, msg.tm_mon, msg.tm_mday, + msg.tm_year + 1900, msg.tm_mon + 1, msg.tm_mday, msg.tm_hour, msg.tm_min, msg.tm_sec ); } -- cgit v1.2.3 From 839189b091ee82937666bcaa328a7d3ecd1c573b Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 2 May 2010 22:06:25 +0100 Subject: Applied show-offline patch from Florian E.J. Fruth, adapted for a few changes that happened since 1.2.4. --- doc/user-guide/commands.xml | 10 +++++ irc.c | 1 + protocols/nogaim.c | 90 +++++++++++++++++++++++++++++++++++++-------- 3 files changed, 85 insertions(+), 16 deletions(-) diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml index 9fcc91da..af2c5eb2 100644 --- a/doc/user-guide/commands.xml +++ b/doc/user-guide/commands.xml @@ -860,6 +860,16 @@ + + true + + + + If enabled causes BitlBee to also show offline users in Channel. Online-users will get op, away-users voice and offline users none of both. This option takes effect as soon as you reconnect. + + + + true diff --git a/irc.c b/irc.c index aa8bc140..22bb9fa6 100644 --- a/irc.c +++ b/irc.c @@ -200,6 +200,7 @@ irc_t *irc_new( int fd ) s = set_add( &irc->set, "query_order", "lifo", NULL, irc ); s = set_add( &irc->set, "root_nick", irc->mynick, set_eval_root_nick, irc ); s = set_add( &irc->set, "save_on_quit", "true", set_eval_bool, irc ); + s = set_add( &irc->set, "show_offline", "false", set_eval_bool, irc ); s = set_add( &irc->set, "simulate_netsplit", "true", set_eval_bool, irc ); s = set_add( &irc->set, "status", NULL, set_eval_away_status, irc ); s->flags |= SET_NULL_OK; diff --git a/protocols/nogaim.c b/protocols/nogaim.c index 98325c89..2248d11e 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -656,7 +656,18 @@ void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, g_free( u->status_msg ); u->away = u->status_msg = NULL; - if( ( flags & OPT_LOGGED_IN ) && !u->online ) + if( set_getbool( &ic->irc->set, "show_offline" ) && !u->online ) + { + /* always set users as online */ + irc_spawn( ic->irc, u ); + u->online = 1; + if( !( flags & OPT_LOGGED_IN ) ) + { + /* set away message if user isn't really online */ + u->away = g_strdup( "User is offline" ); + } + } + else if( ( flags & OPT_LOGGED_IN ) && !u->online ) { irc_spawn( ic->irc, u ); u->online = 1; @@ -665,14 +676,30 @@ void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, { struct groupchat *c; - irc_kill( ic->irc, u ); - u->online = 0; - - /* Remove him/her from the groupchats to prevent PART messages after he/she QUIT already */ - for( c = ic->groupchats; c; c = c->next ) - remove_chat_buddy_silent( c, handle ); + if( set_getbool( &ic->irc->set, "show_offline" ) ) + { + /* keep offline users in channel and set away message to "offline" */ + u->away = g_strdup( "User is offline" ); + + /* Keep showing him/her in the control channel but not in groupchats. */ + for( c = ic->groupchats; c; c = c->next ) + { + if( remove_chat_buddy_silent( c, handle ) && c->joined ) + irc_part( c->ic->irc, u, c->channel ); + } + } + else + { + /* kill offline users */ + irc_kill( ic->irc, u ); + u->online = 0; + + /* Remove him/her from the groupchats to prevent PART messages after he/she QUIT already */ + for( c = ic->groupchats; c; c = c->next ) + remove_chat_buddy_silent( c, handle ); + } } - + if( flags & OPT_AWAY ) { if( state && message ) @@ -697,11 +724,8 @@ void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, u->status_msg = g_strdup( message ); } - /* LISPy... */ - if( ( set_getbool( &ic->irc->set, "away_devoice" ) ) && /* Don't do a thing when user doesn't want it */ - ( u->online ) && /* Don't touch offline people */ - ( ( ( u->online != oo ) && !u->away ) || /* Voice joining people */ - ( ( u->online == oo ) && ( oa == !u->away ) ) ) ) /* (De)voice people changing state */ + /* early if-clause for show_offline even if there is some redundant code here because this isn't LISP but C ;) */ + if( set_getbool( &ic->irc->set, "show_offline" ) && set_getbool( &ic->irc->set, "away_devoice" ) ) { char *from; @@ -714,9 +738,43 @@ void imcb_buddy_status( struct im_connection *ic, const char *handle, int flags, from = g_strdup_printf( "%s!%s@%s", ic->irc->mynick, ic->irc->mynick, ic->irc->myhost ); } - irc_write( ic->irc, ":%s MODE %s %cv %s", from, ic->irc->channel, - u->away?'-':'+', u->nick ); - g_free( from ); + + /* if we use show_offline, we op online users, voice away users, and devoice/deop offline users */ + if( flags & OPT_LOGGED_IN ) + { + /* user is "online" (either really online or away) */ + irc_write( ic->irc, ":%s MODE %s %cv%co %s %s", from, ic->irc->channel, + u->away?'+':'-', u->away?'-':'+', u->nick, u->nick ); + } + else + { + /* user is offline */ + irc_write( ic->irc, ":%s MODE %s -vo %s %s", from, ic->irc->channel, u->nick, u->nick ); + } + } + else + { + /* LISPy... */ + if( ( set_getbool( &ic->irc->set, "away_devoice" ) ) && /* Don't do a thing when user doesn't want it */ + ( u->online ) && /* Don't touch offline people */ + ( ( ( u->online != oo ) && !u->away ) || /* Voice joining people */ + ( ( u->online == oo ) && ( oa == !u->away ) ) ) ) /* (De)voice people changing state */ + { + char *from; + + if( set_getbool( &ic->irc->set, "simulate_netsplit" ) ) + { + from = g_strdup( ic->irc->myhost ); + } + else + { + from = g_strdup_printf( "%s!%s@%s", ic->irc->mynick, ic->irc->mynick, + ic->irc->myhost ); + } + irc_write( ic->irc, ":%s MODE %s %cv %s", from, ic->irc->channel, + u->away?'-':'+', u->nick ); + g_free( from ); + } } } -- cgit v1.2.3 From 6824fb355a31ed46c22740ef184332440d3981ce Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 2 May 2010 23:00:46 +0100 Subject: Fixed show_offline documentation. (The default is off, not on.) --- doc/user-guide/commands.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml index af2c5eb2..b84e87aa 100644 --- a/doc/user-guide/commands.xml +++ b/doc/user-guide/commands.xml @@ -861,7 +861,7 @@ - true + false -- cgit v1.2.3 From 999769119e85518cc46b3ed64cb8781695fefbdc Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Mon, 3 May 2010 22:36:43 +0100 Subject: Check Tweet length on the BitlBee side already. --- doc/user-guide/commands.xml | 15 +++++++++++++++ protocols/twitter/twitter.c | 18 ++++++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml index b84e87aa..ba2b4e70 100644 --- a/doc/user-guide/commands.xml +++ b/doc/user-guide/commands.xml @@ -662,6 +662,21 @@ + + 140 + + + + Since Twitter rejects messages longer than 140 characters, BitlBee can count message length and emit a warning instead of waiting for Twitter to reject it. + + + + You can change this limit here but this won't disable length checks on Twitter's side. You can also set it to 0 to disable the check in case you believe BitlBee doesn't count the characters correctly. + + + + + one, many, chat one diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index 9c7b060c..98e85641 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -143,10 +143,24 @@ static char *set_eval_mode( set_t *set, char *value ) return NULL; } +static gboolean twitter_length_check( struct im_connection *ic, gchar *msg ) +{ + int max = set_getint( &ic->acc->set, "message_length" ), len; + + if( max == 0 || ( len = g_utf8_strlen( msg, -1 ) ) <= max ) + return TRUE; + + imcb_error( ic, "Maximum message length exceeded: %d > %d", len, max ); + + return FALSE; +} + static void twitter_init( account_t *acc ) { set_t *s; + s = set_add( &acc->set, "message_length", "140", set_eval_int, acc ); + s = set_add( &acc->set, "mode", "one", set_eval_mode, acc ); s->flags |= ACC_SET_OFFLINE_ONLY; @@ -230,7 +244,7 @@ static int twitter_buddy_msg( struct im_connection *ic, char *who, char *message return FALSE; } } - else + else if( twitter_length_check(ic, message) ) twitter_post_status(ic, message); } else @@ -261,7 +275,7 @@ static void twitter_remove_buddy( struct im_connection *ic, char *who, char *gro static void twitter_chat_msg( struct groupchat *c, char *message, int flags ) { - if( c && message ) + if( c && message && twitter_length_check(c->ic, message)) twitter_post_status(c->ic, message); } -- cgit v1.2.3 From aa7ce1b3dd961e948e907460bd42bf1592ea1717 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 9 May 2010 00:50:12 +0100 Subject: Yahoo! seems to echo conference room invitations now, huh? No idea what this is but let's ignore them. --- protocols/yahoo/yahoo.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/protocols/yahoo/yahoo.c b/protocols/yahoo/yahoo.c index b61f6ff9..922ac17b 100644 --- a/protocols/yahoo/yahoo.c +++ b/protocols/yahoo/yahoo.c @@ -827,6 +827,10 @@ void ext_yahoo_got_conf_invite( int id, const char *ignored, char txt[1024]; YList *m; + if( g_strcasecmp( who, ic->acc->user ) == 0 ) + /* WTF, Yahoo! seems to echo these now? */ + return; + inv = g_malloc( sizeof( struct byahoo_conf_invitation ) ); memset( inv, 0, sizeof( struct byahoo_conf_invitation ) ); inv->name = g_strdup( room ); -- cgit v1.2.3 From 5a599a1550c670649dba681e702864d55d2e3795 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 9 May 2010 01:40:03 +0100 Subject: Room names on OSCAR can't start with digits, this broke the "chat with" command on ICQ so far. Just prepend "icq_" and it'll work. --- protocols/oscar/oscar.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/protocols/oscar/oscar.c b/protocols/oscar/oscar.c index e0c32257..5d23c36a 100644 --- a/protocols/oscar/oscar.c +++ b/protocols/oscar/oscar.c @@ -2650,7 +2650,8 @@ struct groupchat *oscar_chat_with(struct im_connection * ic, char *who) static int chat_id = 0; char * chatname; - chatname = g_strdup_printf("%s%d", ic->acc->user, chat_id++); + chatname = g_strdup_printf("%s%s_%d", isdigit(*ic->acc->user) ? "icq_" : "", + ic->acc->user, chat_id++); ret = oscar_chat_join(ic, chatname, NULL, NULL); -- cgit v1.2.3 From 7ee07c3191d1eb21d1d8317f7bfc3d9d98359ab1 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sun, 9 May 2010 17:06:19 +0100 Subject: It looks like AIM mem requests (implemented as damn_you() and straight_to_hell() back in the days - and still more or less the same in libpurple it seems) are no longer necessary; the script no longer exists at the location used in the source and nobody complained. Nice try, AOL. And hats of to the Gaim developer who thought of this clever way around it. --- protocols/oscar/oscar.c | 136 ------------------------------------------------ 1 file changed, 136 deletions(-) diff --git a/protocols/oscar/oscar.c b/protocols/oscar/oscar.c index 5d23c36a..00c5e5ef 100644 --- a/protocols/oscar/oscar.c +++ b/protocols/oscar/oscar.c @@ -204,7 +204,6 @@ static int gaim_parse_buddyrights(aim_session_t *, aim_frame_t *, ...); static int gaim_parse_locerr (aim_session_t *, aim_frame_t *, ...); static int gaim_icbm_param_info (aim_session_t *, aim_frame_t *, ...); static int gaim_parse_genericerr (aim_session_t *, aim_frame_t *, ...); -static int gaim_memrequest (aim_session_t *, aim_frame_t *, ...); static int gaim_selfinfo (aim_session_t *, aim_frame_t *, ...); static int gaim_offlinemsg (aim_session_t *, aim_frame_t *, ...); static int gaim_offlinemsgdone (aim_session_t *, aim_frame_t *, ...); @@ -569,7 +568,6 @@ static int gaim_parse_auth_resp(aim_session_t *sess, aim_frame_t *fr, ...) { aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_ERROR, gaim_parse_genericerr, 0); aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BUD, AIM_CB_BUD_ERROR, gaim_parse_genericerr, 0); aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_BOS, AIM_CB_BOS_ERROR, gaim_parse_genericerr, 0); - aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, 0x1f, gaim_memrequest, 0); aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_GEN, AIM_CB_GEN_SELFINFO, gaim_selfinfo, 0); aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSG, gaim_offlinemsg, 0); aim_conn_addhandler(sess, bosconn, AIM_CB_FAM_ICQ, AIM_CB_ICQ_OFFLINEMSGCOMPLETE, gaim_offlinemsgdone, 0); @@ -603,143 +601,9 @@ static int gaim_parse_auth_resp(aim_session_t *sess, aim_frame_t *fr, ...) { return 1; } -struct pieceofcrap { - struct im_connection *ic; - unsigned long offset; - unsigned long len; - char *modname; - int fd; - aim_conn_t *conn; - unsigned int inpa; -}; - -static gboolean damn_you(gpointer data, gint source, b_input_condition c) -{ - struct pieceofcrap *pos = data; - struct oscar_data *od = pos->ic->proto_data; - char in = '\0'; - int x = 0; - unsigned char m[17]; - - while (read(pos->fd, &in, 1) == 1) { - if (in == '\n') - x++; - else if (in != '\r') - x = 0; - if (x == 2) - break; - in = '\0'; - } - if (in != '\n') { - imcb_error(pos->ic, "Gaim was unable to get a valid hash for logging into AIM." - " You may be disconnected shortly."); - b_event_remove(pos->inpa); - closesocket(pos->fd); - g_free(pos); - return FALSE; - } - /* [WvG] Wheeeee! Who needs error checking anyway? ;-) */ - read(pos->fd, m, 16); - m[16] = '\0'; - b_event_remove(pos->inpa); - closesocket(pos->fd); - aim_sendmemblock(od->sess, pos->conn, 0, 16, m, AIM_SENDMEMBLOCK_FLAG_ISHASH); - g_free(pos); - - return FALSE; -} - -static gboolean straight_to_hell(gpointer data, gint source, b_input_condition cond) { - struct pieceofcrap *pos = data; - char buf[BUF_LONG]; - - if (source < 0) { - imcb_error(pos->ic, "Gaim was unable to get a valid hash for logging into AIM." - " You may be disconnected shortly."); - if (pos->modname) - g_free(pos->modname); - g_free(pos); - return FALSE; - } - - g_snprintf(buf, sizeof(buf), "GET " AIMHASHDATA - "?offset=%ld&len=%ld&modname=%s HTTP/1.0\n\n", - pos->offset, pos->len, pos->modname ? pos->modname : ""); - write(pos->fd, buf, strlen(buf)); - if (pos->modname) - g_free(pos->modname); - pos->inpa = b_input_add(pos->fd, GAIM_INPUT_READ, damn_you, pos); - return FALSE; -} - /* size of icbmui.ocm, the largest module in AIM 3.5 */ #define AIM_MAX_FILE_SIZE 98304 -int gaim_memrequest(aim_session_t *sess, aim_frame_t *fr, ...) { - va_list ap; - struct pieceofcrap *pos; - guint32 offset, len; - char *modname; - int fd; - - va_start(ap, fr); - offset = (guint32)va_arg(ap, unsigned long); - len = (guint32)va_arg(ap, unsigned long); - modname = va_arg(ap, char *); - va_end(ap); - - if (len == 0) { - aim_sendmemblock(sess, fr->conn, offset, len, NULL, - AIM_SENDMEMBLOCK_FLAG_ISREQUEST); - return 1; - } - /* uncomment this when you're convinced it's right. remember, it's been wrong before. - if (offset > AIM_MAX_FILE_SIZE || len > AIM_MAX_FILE_SIZE) { - char *buf; - int i = 8; - if (modname) - i += strlen(modname); - buf = g_malloc(i); - i = 0; - if (modname) { - memcpy(buf, modname, strlen(modname)); - i += strlen(modname); - } - buf[i++] = offset & 0xff; - buf[i++] = (offset >> 8) & 0xff; - buf[i++] = (offset >> 16) & 0xff; - buf[i++] = (offset >> 24) & 0xff; - buf[i++] = len & 0xff; - buf[i++] = (len >> 8) & 0xff; - buf[i++] = (len >> 16) & 0xff; - buf[i++] = (len >> 24) & 0xff; - aim_sendmemblock(sess, command->conn, offset, i, buf, AIM_SENDMEMBLOCK_FLAG_ISREQUEST); - g_free(buf); - return 1; - } - */ - - pos = g_new0(struct pieceofcrap, 1); - pos->ic = sess->aux_data; - pos->conn = fr->conn; - - pos->offset = offset; - pos->len = len; - pos->modname = modname ? g_strdup(modname) : NULL; - - fd = proxy_connect("gaim.sourceforge.net", 80, straight_to_hell, pos); - if (fd < 0) { - if (pos->modname) - g_free(pos->modname); - g_free(pos); - imcb_error(sess->aux_data, "Gaim was unable to get a valid hash for logging into AIM." - " You may be disconnected shortly."); - } - pos->fd = fd; - - return 1; -} - static int gaim_parse_login(aim_session_t *sess, aim_frame_t *fr, ...) { #if 0 struct client_info_s info = {"gaim", 4, 1, 2010, "us", "en", 0x0004, 0x0000, 0x04b}; -- cgit v1.2.3 From e73a501ce0cf49d8073d7d25656561400f2aca79 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 11 May 2010 00:35:58 +0100 Subject: Fixed a long-standing bug where non-anonymous Jabber chatrooms could cause full JIDs to be added to the local contact list, but not removed when leaving the room, eventually triggering errors like the one in #499. People kept sending me these dumps and I finally took time to make what turned out to be this one-line fix. This error should never appear again unless people are doing really crazy stuff. --- protocols/jabber/conference.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/protocols/jabber/conference.c b/protocols/jabber/conference.c index f434c58a..affe8aef 100644 --- a/protocols/jabber/conference.c +++ b/protocols/jabber/conference.c @@ -271,8 +271,10 @@ void jabber_chat_pkt_presence( struct im_connection *ic, struct jabber_buddy *bu bud->flags |= JBFLAG_IS_ANONYMOUS; } - if( bud != jc->me ) + if( bud != jc->me && bud->flags & JBFLAG_IS_ANONYMOUS ) { + /* If JIDs are anonymized, add them to the local + list for the duration of this chat. */ imcb_add_buddy( ic, bud->ext_jid, NULL ); imcb_buddy_nick_hint( ic, bud->ext_jid, bud->resource ); } -- cgit v1.2.3 From f8cb76dea3554fd82c01c5cae61f35d2b6cb0936 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Tue, 11 May 2010 22:33:37 +0100 Subject: Stop those "Many switchboard failures" messages. No point in showing them unless the lost switchboard actually had something queued. --- protocols/msn/sb.c | 49 ++++++++++++++++++++++--------------------------- 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/protocols/msn/sb.c b/protocols/msn/sb.c index e2ee8570..49eed601 100644 --- a/protocols/msn/sb.c +++ b/protocols/msn/sb.c @@ -327,9 +327,13 @@ static gboolean msn_sb_callback( gpointer data, gint source, b_input_condition c struct im_connection *ic = sb->ic; struct msn_data *md = ic->proto_data; - if( msn_handler( sb->handler ) == -1 ) + if( msn_handler( sb->handler ) != -1 ) + return TRUE; + + if( sb->msgq != NULL ) { time_t now = time( NULL ); + char buf[1024]; if( now - md->first_sb_failure > 600 ) { @@ -346,37 +350,28 @@ static gboolean msn_sb_callback( gpointer data, gint source, b_input_condition c imcb_log( ic, "Warning: Many switchboard failures on MSN connection. " "There might be problems delivering your messages." ); - if( sb->msgq != NULL ) + if( md->msgq == NULL ) { - char buf[1024]; - - if( md->msgq == NULL ) - { - md->msgq = sb->msgq; - } - else - { - GSList *l; - - for( l = md->msgq; l->next; l = l->next ); - l->next = sb->msgq; - } - sb->msgq = NULL; + md->msgq = sb->msgq; + } + else + { + GSList *l; - debug( "Moved queued messages back to the main queue, creating a new switchboard to retry." ); - g_snprintf( buf, sizeof( buf ), "XFR %d SB\r\n", ++md->trId ); - if( !msn_write( ic, buf, strlen( buf ) ) ) - return FALSE; + for( l = md->msgq; l->next; l = l->next ); + l->next = sb->msgq; } + sb->msgq = NULL; - msn_sb_destroy( sb ); - - return FALSE; - } - else - { - return TRUE; + debug( "Moved queued messages back to the main queue, " + "creating a new switchboard to retry." ); + g_snprintf( buf, sizeof( buf ), "XFR %d SB\r\n", ++md->trId ); + if( !msn_write( ic, buf, strlen( buf ) ) ) + return FALSE; } + + msn_sb_destroy( sb ); + return FALSE; } static int msn_sb_command( gpointer data, char **cmd, int num_parts ) -- cgit v1.2.3 From 3742fb6ae99c2e5e25cff272a154b9834866e8f9 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Wed, 12 May 2010 00:27:11 +0100 Subject: Implement some kind of ignorant awareness of XML namespaces: Enough to not break backward compatibility (hopefully) but be able to pick up inappropriate uses of XML namespace prefixes. Main reason for this change: Fix XMPP typing notification compatibility with GMail. --- lib/xmltree.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/lib/xmltree.c b/lib/xmltree.c index 67fe46e1..31f8ee9c 100644 --- a/lib/xmltree.c +++ b/lib/xmltree.c @@ -448,7 +448,11 @@ struct xt_node *xt_find_node( struct xt_node *node, const char *name ) { while( node ) { - if( g_strcasecmp( node->name, name ) == 0 ) + char *colon; + + if( g_strcasecmp( node->name, name ) == 0 || + ( ( colon = strchr( node->name, ':' ) ) && + g_strcasecmp( colon + 1, name ) == 0 ) ) break; node = node->next; @@ -460,6 +464,7 @@ struct xt_node *xt_find_node( struct xt_node *node, const char *name ) char *xt_find_attr( struct xt_node *node, const char *key ) { int i; + char *colon; if( !node ) return NULL; @@ -468,6 +473,21 @@ char *xt_find_attr( struct xt_node *node, const char *key ) if( g_strcasecmp( node->attr[i].key, key ) == 0 ) break; + /* This is an awful hack that only takes care of namespace prefixes + inside a tag. Since IMHO excessive namespace usage in XMPP is + massive overkill anyway (this code exists for almost four years + now and never really missed it): Meh. */ + if( !node->attr[i].key && strcmp( key, "xmlns" ) == 0 && + ( colon = strchr( node->name, ':' ) ) ) + { + *colon = '\0'; + for( i = 0; node->attr[i].key; i ++ ) + if( strncmp( node->attr[i].key, "xmlns:", 6 ) == 0 && + strcmp( node->attr[i].key + 6, node->name ) == 0 ) + break; + *colon = ':'; + } + return node->attr[i].value; } -- cgit v1.2.3 From 6e6b3d7c7300c5cf5cf7f1538154925fd2fe3953 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Fri, 14 May 2010 01:09:29 +0100 Subject: Add a warning when the user included a domain part in his/her Yahoo! username. --- protocols/yahoo/yahoo.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/protocols/yahoo/yahoo.c b/protocols/yahoo/yahoo.c index 922ac17b..e4d541d5 100644 --- a/protocols/yahoo/yahoo.c +++ b/protocols/yahoo/yahoo.c @@ -137,10 +137,15 @@ static void byahoo_login( account_t *acc ) { struct im_connection *ic = imcb_new( acc ); struct byahoo_data *yd = ic->proto_data = g_new0( struct byahoo_data, 1 ); + char *s; yd->logged_in = FALSE; yd->current_status = YAHOO_STATUS_AVAILABLE; + if( ( s = strchr( acc->user, '@' ) ) && g_strcasecmp( s, "@yahoo.com" ) == 0 ) + imcb_error( ic, "Your Yahoo! username should just be a username. " + "Do not include any @domain part." ); + imcb_log( ic, "Connecting" ); yd->y2_id = yahoo_init( acc->user, acc->pass ); yahoo_login( yd->y2_id, yd->current_status ); -- cgit v1.2.3 From f8ec8903b66fad1355f90316bf46b7ca2be43762 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 15 May 2010 01:20:33 +0100 Subject: Passport 3.0 authentication turns out to be slightly different from what the msnpiki MSNP13 doc says and stuff is breaking now. This should fix the main problem now. --- protocols/msn/passport.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/protocols/msn/passport.c b/protocols/msn/passport.c index 565d15f3..7c896db1 100644 --- a/protocols/msn/passport.c +++ b/protocols/msn/passport.c @@ -144,7 +144,9 @@ static xt_status passport_xt_extract_token( struct xt_node *node, gpointer data struct msn_auth_data *mad = data; char *s; - if( ( s = xt_find_attr( node, "Id" ) ) && strcmp( s, "PPToken1" ) == 0 ) + if( ( s = xt_find_attr( node, "Id" ) ) && + ( strncmp( s, "Compact", 7 ) == 0 || + strncmp( s, "PPToken", 7 ) == 0 ) ) mad->token = g_memdup( node->text, node->text_len + 1 ); return XT_HANDLED; -- cgit v1.2.3 From df1ae6223a5fdb3c18d5438670834285101c6213 Mon Sep 17 00:00:00 2001 From: Wilmer van der Gaast Date: Sat, 15 May 2010 12:53:09 +0100 Subject: 1.2.7 changelog. Have to release soon, now that 1.2.6a is mostly broken for MSN Messenger for everyone. --- bitlbee.h | 4 ++-- doc/CHANGES | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/bitlbee.h b/bitlbee.h index f4e03d76..2f4fa6be 100644 --- a/bitlbee.h +++ b/bitlbee.h @@ -34,10 +34,10 @@ #define _WIN32_WINNT 0x0501 #define PACKAGE "BitlBee" -#define BITLBEE_VERSION "1.2.6a" +#define BITLBEE_VERSION "1.2.7" #define VERSION BITLBEE_VERSION #define BITLBEE_VER(a,b,c) (((a) << 16) + ((b) << 8) + (c)) -#define BITLBEE_VERSION_CODE BITLBEE_VER(1, 2, 6) +#define BITLBEE_VERSION_CODE BITLBEE_VER(1, 2, 7) #define MAX_STRING 511 diff --git a/doc/CHANGES b/doc/CHANGES index 45b16e28..7359a050 100644 --- a/doc/CHANGES +++ b/doc/CHANGES @@ -3,6 +3,20 @@ found in the bzr commit logs, for example you can try: http://bugs.bitlbee.org/bitlbee/timeline?daysback=90&changeset=on +Version 1.2.7: +- Fixed problems with MSN Messenger authentication. ("Could not parse + Passport server response") +- Fixed broken typing notifications when talking to GTalk contacts. +- Fixed an issue with non-anonymous Jabber chatrooms polluting the nick + namespace, sometimes generating odd warning messages. +- Restored ability to start groupchats on ICQ. +- Added show_offline setting that will also show offline contacts in the + control channel. +- OAuth support for Twitter: This means the module will keep working after + June (this also changes "via API" under your tweets into "via BitlBee"). + +Finished 15 May 2010 + Version 1.2.6a: - Fixed a typo that renders the Twitter groupchat mode unusable. A last- minute change that came a few minutes late. -- cgit v1.2.3