diff options
author | Wilmer van der Gaast <wilmer@gaast.net> | 2010-05-16 00:28:16 +0100 |
---|---|---|
committer | Wilmer van der Gaast <wilmer@gaast.net> | 2010-05-16 00:28:16 +0100 |
commit | ec86b2232da72f7b0c1c0a217d94b6e15698c795 (patch) | |
tree | 008111e5ae7d47ba5547be974769bd0751ee9fd0 | |
parent | ca0981ad1e427644a33fc31fe78d63ea834f0fa0 (diff) | |
parent | 6e6b3d7c7300c5cf5cf7f1538154925fd2fe3953 (diff) |
Mainline merge. (Probably mostly irrelevant for this branch, oh well.)
-rw-r--r-- | doc/user-guide/commands.xml | 48 | ||||
-rw-r--r-- | irc.c | 1 | ||||
-rw-r--r-- | lib/Makefile | 2 | ||||
-rw-r--r-- | lib/misc.c | 3 | ||||
-rw-r--r-- | lib/oauth.c | 453 | ||||
-rw-r--r-- | lib/oauth.h | 90 | ||||
-rw-r--r-- | lib/url.c | 2 | ||||
-rw-r--r-- | lib/url.h | 2 | ||||
-rw-r--r-- | lib/xmltree.c | 22 | ||||
-rw-r--r-- | protocols/jabber/conference.c | 4 | ||||
-rw-r--r-- | protocols/msn/sb.c | 49 | ||||
-rw-r--r-- | protocols/nogaim.c | 92 | ||||
-rw-r--r-- | protocols/oscar/oscar.c | 139 | ||||
-rw-r--r-- | protocols/twitter/twitter.c | 150 | ||||
-rw-r--r-- | protocols/twitter/twitter.h | 1 | ||||
-rw-r--r-- | protocols/twitter/twitter_http.c | 20 | ||||
-rw-r--r-- | protocols/twitter/twitter_http.h | 4 | ||||
-rw-r--r-- | protocols/twitter/twitter_lib.c | 12 | ||||
-rw-r--r-- | protocols/yahoo/yahoo.c | 9 |
19 files changed, 888 insertions, 215 deletions
diff --git a/doc/user-guide/commands.xml b/doc/user-guide/commands.xml index 260b4c40..f3633971 100644 --- a/doc/user-guide/commands.xml +++ b/doc/user-guide/commands.xml @@ -78,6 +78,10 @@ <para> To send tweets yourself, send them to the twitter_(yourusername) contact, or just write in the groupchat channel if you enabled that option. </para> + + <para> + Since Twitter now requires OAuth authentication, you should not enter your Twitter password into BitlBee. Just type a bogus password. The first time you log in, BitlBee will start OAuth authentication. (See <emphasis>help set oauth</emphasis>.) + </para> </description> </bitlbee-command> @@ -658,6 +662,21 @@ </bitlbee-setting> + <bitlbee-setting name="message_length" type="integer" scope="account"> + <default>140</default> + + <description> + <para> + 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. + </para> + + <para> + 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. + </para> + </description> + + </bitlbee-setting> + <bitlbee-setting name="mode" type="string" scope="account"> <possible-values>one, many, chat</possible-values> <default>one</default> @@ -702,6 +721,25 @@ </description> </bitlbee-setting> + <bitlbee-setting name="oauth" type="boolean" scope="account"> + <default>true</default> + + <description> + <para> + This enables OAuth authentication for Twitter accounts. From June 2010 this will be mandatory. + </para> + + <para> + With OAuth enabled, you shouldn't tell BitlBee your Twitter password. Just add your account with a bogus password and type <emphasis>account on</emphasis>. BitlBee will then give you a URL to authenticate with Twitter. If this succeeds, Twitter will return a PIN code which you can give back to BitlBee to finish the process. + </para> + + <para> + The resulting access token will be saved permanently, so you have to do this only once. + </para> + </description> + + </bitlbee-setting> + <bitlbee-setting name="ops" type="string" scope="global"> <default>both</default> <possible-values>both, root, user, none</possible-values> @@ -837,6 +875,16 @@ </description> </bitlbee-setting> + <bitlbee-setting name="show_offline" type="boolean" scope="global"> + <default>false</default> + + <description> + <para> + 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. + </para> + </description> + </bitlbee-setting> + <bitlbee-setting name="simulate_netsplit" type="boolean" scope="global"> <default>true</default> @@ -201,6 +201,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/lib/Makefile b/lib/Makefile index 3d128b5a..b686f886 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 ftutil.o +objects = arc.o base64.o $(EVENT_HANDLER) ftutil.o http_client.o ini.o md5.o misc.o oauth.o proxy.o sha1.o $(SSL_CLIENT) url.o xmltree.o CFLAGS += -Wall LFLAGS += -r @@ -305,8 +305,7 @@ void http_encode( char *s ) for( i = j = 0; t[i]; i ++, j ++ ) { - /* if( t[i] <= ' ' || ((unsigned char *)t)[i] >= 128 || t[i] == '%' ) */ - if( !isalnum( t[i] ) ) + if( !isalnum( t[i] ) && !strchr( "._-~", t[i] ) ) { sprintf( s + j, "%%%02X", ((unsigned char*)t)[i] ); j += 2; diff --git a/lib/oauth.c b/lib/oauth.c new file mode 100644 index 00000000..c60a5a52 --- /dev/null +++ b/lib/oauth.c @@ -0,0 +1,453 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* Simple OAuth client (consumer) implementation. * +* * +* Copyright 2010 Wilmer van der Gaast <wilmer@gaast.net> * +* * +* This library is free software; you can redistribute it and/or * +* modify it under the terms of the GNU Lesser General Public * +* License as published by the Free Software Foundation, version * +* 2.1. * +* * +* This library is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * +* Lesser General Public License for more details. * +* * +* You should have received a copy of the GNU Lesser General Public License * +* along with this library; if not, write to the Free Software Foundation, * +* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * +* * +\***************************************************************************/ + +#include <glib.h> +#include <gmodule.h> +#include <stdlib.h> +#include <string.h> +#include "http_client.h" +#include "base64.h" +#include "misc.h" +#include "sha1.h" +#include "url.h" +#include "oauth.h" + +#define HMAC_BLOCK_SIZE 64 + +static char *oauth_sign( const char *method, const char *url, + const char *params, struct oauth_info *oi ) +{ + 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( 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*) oi->sp->consumer_secret, strlen( oi->sp->consumer_secret ) ); + sha1_append( &sha1, (uint8_t*) "&", 1 ); + 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", + oi->sp->consumer_secret, oi->token_secret ? : "" ); + } + + /* Inner part: H(K XOR 0x36, text) */ + sha1_init( &sha1 ); + + for( i = 0; i < HMAC_BLOCK_SIZE; i ++ ) + key[i] ^= 0x36; + sha1_append( &sha1, key, HMAC_BLOCK_SIZE ); + + /* OAuth: text = method&url¶ms, all http_encoded. */ + sha1_append( &sha1, (const uint8_t*) method, strlen( method ) ); + sha1_append( &sha1, (const uint8_t*) "&", 1 ); + + s = g_new0( char, strlen( url ) * 3 + 1 ); + strcpy( s, url ); + http_encode( s ); + sha1_append( &sha1, (const uint8_t*) s, strlen( s ) ); + sha1_append( &sha1, (const uint8_t*) "&", 1 ); + g_free( s ); + + s = g_new0( char, strlen( params ) * 3 + 1 ); + strcpy( s, params ); + http_encode( s ); + sha1_append( &sha1, (const uint8_t*) s, strlen( s ) ); + g_free( s ); + + sha1_finish( &sha1, hash ); + + /* Final result: H(K XOR 0x5C, inner stuff) */ + sha1_init( &sha1 ); + for( i = 0; i < HMAC_BLOCK_SIZE; i ++ ) + key[i] ^= 0x36 ^ 0x5c; + sha1_append( &sha1, key, HMAC_BLOCK_SIZE ); + sha1_append( &sha1, hash, sha1_hash_size ); + sha1_finish( &sha1, hash ); + + /* base64_encode + HTTP escape it (both consumers + need it that away) and we're done. */ + s = base64_encode( hash, sha1_hash_size ); + s = g_realloc( s, strlen( s ) * 3 + 1 ); + http_encode( s ); + + return s; +} + +static char *oauth_nonce() +{ + unsigned char bytes[9]; + + random_bytes( bytes, sizeof( bytes ) ); + return base64_encode( bytes, sizeof( bytes ) ); +} + +void oauth_params_add( GSList **params, const char *key, const char *value ) +{ + char *item; + + item = g_strdup_printf( "%s=%s", key, value ); + *params = g_slist_insert_sorted( *params, item, (GCompareFunc) strcmp ); +} + +void oauth_params_del( GSList **params, const char *key ) +{ + int key_len = strlen( key ); + GSList *l, *n; + + for( l = *params; l; l = n ) + { + n = l->next; + + if( strncmp( (char*) l->data, key, key_len ) == 0 && + ((char*)l->data)[key_len] == '=' ) + { + g_free( l->data ); + *params = g_slist_remove( *params, l->data ); + } + } +} + +void oauth_params_set( GSList **params, const char *key, const char *value ) +{ + oauth_params_del( params, key ); + oauth_params_add( params, key, value ); +} + +const char *oauth_params_get( GSList **params, const char *key ) +{ + int key_len = strlen( key ); + GSList *l; + + for( l = *params; l; l = l->next ) + { + if( strncmp( (char*) l->data, key, key_len ) == 0 && + ((char*)l->data)[key_len] == '=' ) + return (const char*) l->data + key_len + 1; + } + + return NULL; +} + +static void oauth_params_parse( GSList **params, char *in ) +{ + char *amp, *eq, *s; + + while( in && *in ) + { + eq = strchr( in, '=' ); + if( !eq ) + break; + + *eq = '\0'; + if( ( amp = strchr( eq + 1, '&' ) ) ) + *amp = '\0'; + + s = g_strdup( eq + 1 ); + http_decode( s ); + oauth_params_add( params, in, s ); + g_free( s ); + + *eq = '='; + if( amp == NULL ) + break; + + *amp = '&'; + in = amp + 1; + } +} + +void oauth_params_free( GSList **params ) +{ + while( params && *params ) + { + g_free( (*params)->data ); + *params = g_slist_remove( *params, (*params)->data ); + } +} + +char *oauth_params_string( GSList *params ) +{ + GSList *l; + GString *str = g_string_new( "" ); + + for( l = params; l; l = l->next ) + { + char *s, *eq; + + s = g_malloc( strlen( l->data ) * 3 + 1 ); + strcpy( s, l->data ); + if( ( eq = strchr( s, '=' ) ) ) + http_encode( eq + 1 ); + g_string_append( str, s ); + g_free( s ); + + if( l->next ) + g_string_append_c( str, '&' ); + } + + return g_string_free( str, FALSE ); +} + +void oauth_info_free( struct oauth_info *info ) +{ + if( info ) + { + g_free( info->auth_url ); + g_free( info->request_token ); + g_free( info->token ); + g_free( info->token_secret ); + g_free( info ); + } +} + +static void oauth_add_default_params( GSList **params, const struct oauth_service *sp ) +{ + char *s; + + 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 ) ); + 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, struct oauth_info *oi ) +{ + GSList *params = NULL; + char *s, *params_s, *post; + void *req; + url_t url_p; + + if( !url_set( &url_p, url ) ) + { + oauth_params_free( params_ ); + return NULL; + } + + if( params_ ) + params = *params_; + + oauth_add_default_params( ¶ms, oi->sp ); + + params_s = oauth_params_string( params ); + oauth_params_free( ¶ms ); + + 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 ); + + 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, oi ); + g_free( s ); + + return req; +} + +static void oauth_request_token_done( struct http_request *req ); + +struct oauth_info *oauth_request_token( const struct oauth_service *sp, oauth_cb func, void *data ) +{ + struct oauth_info *st = g_new0( struct oauth_info, 1 ); + GSList *params = NULL; + + st->func = func; + st->data = data; + st->sp = sp; + + oauth_params_add( ¶ms, "oauth_callback", "oob" ); + + if( !oauth_post_request( sp->url_request_token, ¶ms, oauth_request_token_done, st ) ) + { + oauth_info_free( st ); + return NULL; + } + + return st; +} + +static void oauth_request_token_done( struct http_request *req ) +{ + struct oauth_info *st = req->data; + + st->http = req; + + if( req->status_code == 200 ) + { + GSList *params = NULL; + + st->auth_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; + st->func( st ); +} + +static void oauth_access_token_done( struct http_request *req ); + +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 ); + + 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 ) +{ + struct oauth_info *st = req->data; + + st->http = req; + + if( req->status_code == 200 ) + { + GSList *params = NULL; + + oauth_params_parse( ¶ms, req->reply_body ); + 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; + 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( struct oauth_info *oi, const char *method, const char *url, char *args ) +{ + GSList *params = NULL, *l; + char *sig = NULL, *params_s, *s; + GString *ret = NULL; + + 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 " ); + for( l = params; l; l = l->next ) + { + char *kv = l->data; + char *eq = strchr( kv, '=' ); + char esc[strlen(kv)*3+1]; + + if( eq == NULL ) + break; /* WTF */ + + strcpy( esc, eq + 1 ); + http_encode( esc ); + + g_string_append_len( ret, kv, eq - kv + 1 ); + g_string_append_c( ret, '"' ); + g_string_append( ret, esc ); + g_string_append( ret, "\", " ); + } + + /* Now, before generating the signature, add GET/POST arguments to params + since they should be included in the base signature string (but not in + the HTTP header). */ + if( args ) + oauth_params_parse( ¶ms, args ); + if( ( s = strchr( url, '?' ) ) ) + { + s = g_strdup( s + 1 ); + oauth_params_parse( ¶ms, s ); + g_free( s ); + } + + /* Append the signature and we're done! */ + params_s = oauth_params_string( params ); + sig = oauth_sign( method, url, params_s, oi ); + g_string_append_printf( ret, "oauth_signature=\"%s\"", sig ); + g_free( params_s ); + + oauth_params_free( ¶ms ); + g_free( sig ); + + return ret ? g_string_free( ret, FALSE ) : NULL; +} + +char *oauth_to_string( struct oauth_info *oi ) +{ + GSList *params = NULL; + char *ret; + + oauth_params_add( ¶ms, "oauth_token", oi->token ); + oauth_params_add( ¶ms, "oauth_token_secret", oi->token_secret ); + ret = oauth_params_string( params ); + oauth_params_free( ¶ms ); + + return ret; +} + +struct oauth_info *oauth_from_string( char *in, const struct oauth_service *sp ) +{ + struct oauth_info *oi = g_new0( struct oauth_info, 1 ); + GSList *params = NULL; + + oauth_params_parse( ¶ms, in ); + oi->token = g_strdup( oauth_params_get( ¶ms, "oauth_token" ) ); + oi->token_secret = g_strdup( oauth_params_get( ¶ms, "oauth_token_secret" ) ); + oauth_params_free( ¶ms ); + oi->sp = sp; + + return oi; +} diff --git a/lib/oauth.h b/lib/oauth.h new file mode 100644 index 00000000..5dfe0ae5 --- /dev/null +++ b/lib/oauth.h @@ -0,0 +1,90 @@ +/***************************************************************************\ +* * +* BitlBee - An IRC to IM gateway * +* Simple OAuth client (consumer) implementation. * +* * +* Copyright 2010 Wilmer van der Gaast <wilmer@gaast.net> * +* * +* This program is free software; you can redistribute it and/or modify * +* it under the terms of the GNU General Public License as published by * +* the Free Software Foundation; either version 2 of the License, or * +* (at your option) any later version. * +* * +* This program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License along * +* with this program; if not, write to the Free Software Foundation, Inc., * +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * +* * +\***************************************************************************/ + +/* http://oauth.net/core/1.0a/ */ + +struct oauth_info; + +/* Callback function called twice during the access token request process. + Return FALSE if something broke and the process must be aborted. */ +typedef gboolean (*oauth_cb)( struct oauth_info * ); + +typedef enum +{ + OAUTH_INIT, + OAUTH_REQUEST_TOKEN, + OAUTH_ACCESS_TOKEN, +} oauth_stage_t; + +struct oauth_info +{ + oauth_stage_t stage; + const struct oauth_service *sp; + + oauth_cb func; + void *data; + + struct http_request *http; + + char *auth_url; + char *request_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 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. */ +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( struct oauth_info *oi, const char *method, const char *url, char *args ); + +/* Shouldn't normally be required unless the process is aborted by the user. */ +void oauth_info_free( struct oauth_info *info ); + +/* Convert to and back from strings, for easier saving. */ +char *oauth_to_string( struct oauth_info *oi ); +struct oauth_info *oauth_from_string( char *in, const struct oauth_service *sp ); @@ -26,7 +26,7 @@ #include "url.h" /* Convert an URL to a url_t structure */ -int url_set( url_t *url, char *set_url ) +int url_set( url_t *url, const char *set_url ) { char s[MAX_STRING+1]; char *i; @@ -41,4 +41,4 @@ typedef struct url char pass[MAX_STRING+1]; } url_t; -int url_set( url_t *url, char *set_url ); +int url_set( url_t *url, const char *set_url ); diff --git a/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; } 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 ); } diff --git a/protocols/msn/sb.c b/protocols/msn/sb.c index e23acc09..1614f69f 100644 --- a/protocols/msn/sb.c +++ b/protocols/msn/sb.c @@ -333,9 +333,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 ) { @@ -352,37 +356,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 ) diff --git a/protocols/nogaim.c b/protocols/nogaim.c index 6be6b9ba..cd57a289 100644 --- a/protocols/nogaim.c +++ b/protocols/nogaim.c @@ -664,7 +664,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; @@ -673,14 +684,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 ) @@ -705,11 +732,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; @@ -722,9 +746,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 ); + } } } @@ -1201,7 +1259,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 ); } diff --git a/protocols/oscar/oscar.c b/protocols/oscar/oscar.c index e8e4a027..5633d399 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, B_EV_IO_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}; @@ -2650,7 +2514,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); diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c index 726c7cb1..98e85641 100644 --- a/protocols/twitter/twitter.c +++ b/protocols/twitter/twitter.c @@ -22,11 +22,11 @@ ****************************************************************************/ #include "nogaim.h" +#include "oauth.h" #include "twitter.h" #include "twitter_http.h" #include "twitter_lib.h" - /** * Main loop function */ @@ -50,6 +50,89 @@ 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 const struct oauth_service twitter_oauth = +{ + "http://api.twitter.com/oauth/request_token", + "http://api.twitter.com/oauth/access_token", + "http://api.twitter.com/oauth/authorize", + .consumer_key = "xsDNKJuNZYkZyMcu914uEA", + .consumer_secret = "FCxqcr0pXKzsF9ajmP57S3VQ8V6Drk4o2QYtqMcOszo", +}; + +static gboolean twitter_oauth_callback( struct oauth_info *info ); + +static void twitter_oauth_start( struct im_connection *ic ) +{ + struct twitter_data *td = ic->proto_data; + + imcb_log( ic, "Requesting OAuth request token" ); + + td->oauth_info = oauth_request_token( &twitter_oauth, twitter_oauth_callback, ic ); +} + +static gboolean twitter_oauth_callback( struct oauth_info *info ) +{ + struct im_connection *ic = info->data; + struct twitter_data *td; + + if( !g_slist_find( twitter_connections, ic ) ) + return FALSE; + + td = ic->proto_data; + if( info->stage == OAUTH_REQUEST_TOKEN ) + { + char name[strlen(ic->acc->user)+9], *msg; + + if( info->request_token == NULL ) + { + imcb_error( ic, "OAuth error: %s", info->http->status_string ); + imc_logout( ic, TRUE ); + return FALSE; + } + + sprintf( name, "twitter_%s", ic->acc->user ); + msg = g_strdup_printf( "To finish OAuth authentication, please visit " + "%s 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->token == NULL || info->token_secret == NULL ) + { + imcb_error( ic, "OAuth error: %s", info->http->status_string ); + imc_logout( ic, TRUE ); + return FALSE; + } + + /* 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 = oauth_to_string( info ); + + twitter_main_loop_start( ic ); + } + + return TRUE; +} + + static char *set_eval_mode( set_t *set, char *value ) { if( g_strcasecmp( value, "one" ) == 0 || @@ -60,12 +143,28 @@ 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; + + s = set_add( &acc->set, "oauth", "true", set_eval_bool, acc ); } /** @@ -79,25 +178,24 @@ 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; + ic->flags |= OPT_DOES_HTML; + td->user = acc->user; - td->pass = acc->pass; + if( !set_getbool( &acc->set, "oauth" ) ) + td->pass = g_strdup( acc->pass ); + else if( strstr( acc->pass, "oauth_token=" ) ) + td->oauth_info = oauth_from_string( acc->pass, &twitter_oauth ); 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_info ) + twitter_main_loop_start( ic ); + else + twitter_oauth_start( ic ); } /** @@ -118,6 +216,8 @@ static void twitter_logout( struct im_connection *ic ) if( td ) { + oauth_info_free( td->oauth_info ); + g_free( td->pass ); g_free( td ); } @@ -129,12 +229,28 @@ static void twitter_logout( struct im_connection *ic ) */ static int twitter_buddy_msg( struct im_connection *ic, char *who, char *message, int away ) { + struct twitter_data *td = ic->proto_data; + if (g_strncasecmp(who, "twitter_", 8) == 0 && g_strcasecmp(who + 8, ic->acc->user) == 0) - twitter_post_status(ic, message); + { + if( set_getbool( &ic->acc->set, "oauth" ) && + td->oauth_info && td->oauth_info->token == 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 if( twitter_length_check(ic, message) ) + twitter_post_status(ic, message); + } else + { twitter_direct_messages_new(ic, who, message); - + } return( 0 ); } @@ -159,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); } diff --git a/protocols/twitter/twitter.h b/protocols/twitter/twitter.h index 88caa104..24f61e42 100644 --- a/protocols/twitter/twitter.h +++ b/protocols/twitter/twitter.h @@ -36,6 +36,7 @@ struct twitter_data { char* user; char* pass; + struct oauth_info *oauth_info; 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..51f437df 100644 --- a/protocols/twitter/twitter_http.c +++ b/protocols/twitter/twitter_http.c @@ -28,15 +28,17 @@ * * ****************************************************************************/ -#include "twitter_http.h" #include "twitter.h" #include "bitlbee.h" #include "url.h" #include "misc.h" #include "base64.h" +#include "oauth.h" #include <ctype.h> #include <errno.h> +#include "twitter_http.h" + char *twitter_url_append(char *url, char *key, char* value); @@ -44,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** 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; @@ -109,7 +111,19 @@ 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 (oi) + { + char *full_header; + + 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); + 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..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** 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 d58afd73..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, 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, 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, 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, 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, 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]); } diff --git a/protocols/yahoo/yahoo.c b/protocols/yahoo/yahoo.c index b3d6ea1b..c3ec7bff 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 ); @@ -827,6 +832,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 ); |