aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/user-guide/commands.xml48
-rw-r--r--irc.c1
-rw-r--r--lib/Makefile2
-rw-r--r--lib/misc.c3
-rw-r--r--lib/oauth.c453
-rw-r--r--lib/oauth.h90
-rw-r--r--lib/url.c2
-rw-r--r--lib/url.h2
-rw-r--r--lib/xmltree.c22
-rw-r--r--protocols/jabber/conference.c4
-rw-r--r--protocols/msn/sb.c49
-rw-r--r--protocols/nogaim.c92
-rw-r--r--protocols/oscar/oscar.c139
-rw-r--r--protocols/twitter/twitter.c150
-rw-r--r--protocols/twitter/twitter.h1
-rw-r--r--protocols/twitter/twitter_http.c20
-rw-r--r--protocols/twitter/twitter_http.h4
-rw-r--r--protocols/twitter/twitter_lib.c12
-rw-r--r--protocols/yahoo/yahoo.c9
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>
diff --git a/irc.c b/irc.c
index 4aa8d595..48770a46 100644
--- a/irc.c
+++ b/irc.c
@@ -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
diff --git a/lib/misc.c b/lib/misc.c
index 1d36d639..c56b31f3 100644
--- a/lib/misc.c
+++ b/lib/misc.c
@@ -305,8 +305,7 @@ void http_encode( char *s )
for( i = j = 0; t[i]; i ++, j ++ )
{
- /* if( t[i] <= ' ' || ((unsigned char *)t)[i] >= 128 || t[i] == '%' ) */
- if( !isalnum( t[i] ) )
+ if( !isalnum( t[i] ) && !strchr( "._-~", t[i] ) )
{
sprintf( s + j, "%%%02X", ((unsigned char*)t)[i] );
j += 2;
diff --git a/lib/oauth.c b/lib/oauth.c
new file mode 100644
index 00000000..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&params, all http_encoded. */
+ sha1_append( &sha1, (const uint8_t*) method, strlen( method ) );
+ sha1_append( &sha1, (const uint8_t*) "&", 1 );
+
+ s = g_new0( char, strlen( url ) * 3 + 1 );
+ strcpy( s, url );
+ http_encode( s );
+ sha1_append( &sha1, (const uint8_t*) s, strlen( s ) );
+ sha1_append( &sha1, (const uint8_t*) "&", 1 );
+ g_free( s );
+
+ s = g_new0( char, strlen( params ) * 3 + 1 );
+ strcpy( s, params );
+ http_encode( s );
+ sha1_append( &sha1, (const uint8_t*) s, strlen( s ) );
+ g_free( s );
+
+ sha1_finish( &sha1, hash );
+
+ /* Final result: H(K XOR 0x5C, inner stuff) */
+ sha1_init( &sha1 );
+ for( i = 0; i < HMAC_BLOCK_SIZE; i ++ )
+ key[i] ^= 0x36 ^ 0x5c;
+ sha1_append( &sha1, key, HMAC_BLOCK_SIZE );
+ sha1_append( &sha1, hash, sha1_hash_size );
+ sha1_finish( &sha1, hash );
+
+ /* base64_encode + HTTP escape it (both consumers
+ need it that away) and we're done. */
+ s = base64_encode( hash, sha1_hash_size );
+ s = g_realloc( s, strlen( s ) * 3 + 1 );
+ http_encode( s );
+
+ return s;
+}
+
+static char *oauth_nonce()
+{
+ unsigned char bytes[9];
+
+ random_bytes( bytes, sizeof( bytes ) );
+ return base64_encode( bytes, sizeof( bytes ) );
+}
+
+void oauth_params_add( GSList **params, const char *key, const char *value )
+{
+ char *item;
+
+ item = g_strdup_printf( "%s=%s", key, value );
+ *params = g_slist_insert_sorted( *params, item, (GCompareFunc) strcmp );
+}
+
+void oauth_params_del( GSList **params, const char *key )
+{
+ int key_len = strlen( key );
+ GSList *l, *n;
+
+ for( l = *params; l; l = n )
+ {
+ n = l->next;
+
+ if( strncmp( (char*) l->data, key, key_len ) == 0 &&
+ ((char*)l->data)[key_len] == '=' )
+ {
+ g_free( l->data );
+ *params = g_slist_remove( *params, l->data );
+ }
+ }
+}
+
+void oauth_params_set( GSList **params, const char *key, const char *value )
+{
+ oauth_params_del( params, key );
+ oauth_params_add( params, key, value );
+}
+
+const char *oauth_params_get( GSList **params, const char *key )
+{
+ int key_len = strlen( key );
+ GSList *l;
+
+ for( l = *params; l; l = l->next )
+ {
+ if( strncmp( (char*) l->data, key, key_len ) == 0 &&
+ ((char*)l->data)[key_len] == '=' )
+ return (const char*) l->data + key_len + 1;
+ }
+
+ return NULL;
+}
+
+static void oauth_params_parse( GSList **params, char *in )
+{
+ char *amp, *eq, *s;
+
+ while( in && *in )
+ {
+ eq = strchr( in, '=' );
+ if( !eq )
+ break;
+
+ *eq = '\0';
+ if( ( amp = strchr( eq + 1, '&' ) ) )
+ *amp = '\0';
+
+ s = g_strdup( eq + 1 );
+ http_decode( s );
+ oauth_params_add( params, in, s );
+ g_free( s );
+
+ *eq = '=';
+ if( amp == NULL )
+ break;
+
+ *amp = '&';
+ in = amp + 1;
+ }
+}
+
+void oauth_params_free( GSList **params )
+{
+ while( params && *params )
+ {
+ g_free( (*params)->data );
+ *params = g_slist_remove( *params, (*params)->data );
+ }
+}
+
+char *oauth_params_string( GSList *params )
+{
+ GSList *l;
+ GString *str = g_string_new( "" );
+
+ for( l = params; l; l = l->next )
+ {
+ char *s, *eq;
+
+ s = g_malloc( strlen( l->data ) * 3 + 1 );
+ strcpy( s, l->data );
+ if( ( eq = strchr( s, '=' ) ) )
+ http_encode( eq + 1 );
+ g_string_append( str, s );
+ g_free( s );
+
+ if( l->next )
+ g_string_append_c( str, '&' );
+ }
+
+ return g_string_free( str, FALSE );
+}
+
+void oauth_info_free( struct oauth_info *info )
+{
+ if( info )
+ {
+ g_free( info->auth_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( &params, oi->sp );
+
+ params_s = oauth_params_string( params );
+ oauth_params_free( &params );
+
+ 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( &params, "oauth_callback", "oob" );
+
+ if( !oauth_post_request( sp->url_request_token, &params, oauth_request_token_done, st ) )
+ {
+ oauth_info_free( st );
+ return NULL;
+ }
+
+ return st;
+}
+
+static void oauth_request_token_done( struct http_request *req )
+{
+ struct oauth_info *st = req->data;
+
+ st->http = req;
+
+ if( req->status_code == 200 )
+ {
+ GSList *params = NULL;
+
+ st->auth_url = g_strdup_printf( "%s?%s", st->sp->url_authorize, req->reply_body );
+ oauth_params_parse( &params, req->reply_body );
+ st->request_token = g_strdup( oauth_params_get( &params, "oauth_token" ) );
+ oauth_params_free( &params );
+ }
+
+ 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( &params, "oauth_token", st->request_token );
+ oauth_params_add( &params, "oauth_verifier", pin );
+
+ return oauth_post_request( st->sp->url_access_token, &params, 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( &params, req->reply_body );
+ st->token = g_strdup( oauth_params_get( &params, "oauth_token" ) );
+ st->token_secret = g_strdup( oauth_params_get( &params, "oauth_token_secret" ) );
+ oauth_params_free( &params );
+ }
+
+ 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( &params, "oauth_token", oi->token );
+ oauth_add_default_params( &params, 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( &params, args );
+ if( ( s = strchr( url, '?' ) ) )
+ {
+ s = g_strdup( s + 1 );
+ oauth_params_parse( &params, 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( &params );
+ 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( &params, "oauth_token", oi->token );
+ oauth_params_add( &params, "oauth_token_secret", oi->token_secret );
+ ret = oauth_params_string( params );
+ oauth_params_free( &params );
+
+ 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( &params, in );
+ oi->token = g_strdup( oauth_params_get( &params, "oauth_token" ) );
+ oi->token_secret = g_strdup( oauth_params_get( &params, "oauth_token_secret" ) );
+ oauth_params_free( &params );
+ 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 );
diff --git a/lib/url.c b/lib/url.c
index de9966b4..9e330f8c 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -26,7 +26,7 @@
#include "url.h"
/* Convert an URL to a url_t structure */
-int url_set( url_t *url, char *set_url )
+int url_set( url_t *url, const char *set_url )
{
char s[MAX_STRING+1];
char *i;
diff --git a/lib/url.h b/lib/url.h
index 8c038c91..55107ad2 100644
--- a/lib/url.h
+++ b/lib/url.h
@@ -41,4 +41,4 @@ typedef struct url
char pass[MAX_STRING+1];
} url_t;
-int url_set( url_t *url, char *set_url );
+int url_set( url_t *url, const char *set_url );
diff --git a/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 );