aboutsummaryrefslogtreecommitdiffstats
path: root/protocols/twitter
diff options
context:
space:
mode:
Diffstat (limited to 'protocols/twitter')
-rw-r--r--protocols/twitter/twitter.c160
-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.c49
5 files changed, 198 insertions, 36 deletions
diff --git a/protocols/twitter/twitter.c b/protocols/twitter/twitter.c
index 727eff91..9c7b060c 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
*/
@@ -40,7 +40,7 @@ gboolean twitter_main_loop(gpointer data, gint fd, b_input_condition cond)
// If the user uses multiple private message windows we need to get the
// users buddies.
- if (!set_getbool( &ic->acc->set, "use_groupchat" ))
+ if (g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "many") == 0)
twitter_get_statuses_friends(ic, -1);
// Do stuff..
@@ -50,12 +50,107 @@ 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 ||
+ g_strcasecmp( value, "many" ) == 0 ||
+ g_strcasecmp( value, "chat" ) == 0 )
+ return value;
+ else
+ return NULL;
+}
static void twitter_init( account_t *acc )
{
set_t *s;
- s = set_add( &acc->set, "use_groupchat", "false", set_eval_bool, 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 );
}
/**
@@ -66,23 +161,27 @@ static void twitter_login( account_t *acc )
{
struct im_connection *ic = imcb_new( acc );
struct twitter_data *td = g_new0( struct twitter_data, 1 );
+ 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 );
}
/**
@@ -103,6 +202,8 @@ static void twitter_logout( struct im_connection *ic )
if( td )
{
+ oauth_info_free( td->oauth_info );
+ g_free( td->pass );
g_free( td );
}
@@ -114,11 +215,28 @@ static void twitter_logout( struct im_connection *ic )
*/
static int twitter_buddy_msg( struct im_connection *ic, char *who, char *message, int away )
{
- // Let's just update the status.
-// if ( g_strcasecmp(who, ic->acc->user) == 0 )
- twitter_post_status(ic, message);
-// else
-// twitter_direct_messages_new(ic, who, message);
+ struct twitter_data *td = ic->proto_data;
+
+ if (g_strncasecmp(who, "twitter_", 8) == 0 &&
+ g_strcasecmp(who + 8, ic->acc->user) == 0)
+ {
+ if( set_getbool( &ic->acc->set, "oauth" ) &&
+ td->oauth_info && 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
+ twitter_post_status(ic, message);
+ }
+ else
+ {
+ twitter_direct_messages_new(ic, who, message);
+ }
return( 0 );
}
diff --git a/protocols/twitter/twitter.h b/protocols/twitter/twitter.h
index 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 b6863c02..40352893 100644
--- a/protocols/twitter/twitter_lib.c
+++ b/protocols/twitter/twitter_lib.c
@@ -104,12 +104,14 @@ static void twitter_add_buddy(struct im_connection *ic, char *name, const char *
// Check if the buddy is allready in the buddy list.
if (!bee_user_by_handle( ic->bee, ic, name ))
{
+ char *mode = set_getstr(&ic->acc->set, "mode");
+
// The buddy is not in the list, add the buddy and set the status to logged in.
imcb_add_buddy( ic, name, NULL );
imcb_rename_buddy( ic, name, fullname );
- if (set_getbool( &ic->acc->set, "use_groupchat" ))
+ if (g_strcasecmp(mode, "chat") == 0)
imcb_chat_add_buddy( td->home_timeline_gc, name );
- else
+ else if (g_strcasecmp(mode, "many") == 0)
imcb_buddy_status( ic, name, OPT_LOGGED_IN, NULL, NULL );
}
}
@@ -127,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]);
}
@@ -393,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) {
@@ -451,14 +453,39 @@ static void twitter_private_message_chat(struct im_connection *ic, GSList *list)
struct twitter_data *td = ic->proto_data;
GSList *l = NULL;
struct twitter_xml_status *status;
+ char from[MAX_STRING];
+ gboolean mode_one;
+
+ mode_one = g_strcasecmp( set_getstr( &ic->acc->set, "mode" ), "one" ) == 0;
+ if( mode_one )
+ {
+ g_snprintf( from, sizeof( from ) - 1, "twitter_%s", ic->acc->user );
+ from[MAX_STRING-1] = '\0';
+ }
+
for ( l = list; l ; l = g_slist_next(l) )
{
+ char *text = NULL;
+
status = l->data;
- imcb_buddy_msg( ic, status->user->screen_name, status->text, 0, status->created_at );
+
+ if( mode_one )
+ text = g_strdup_printf( "\002<\002%s\002>\002 %s",
+ status->user->screen_name, status->text );
+ else
+ twitter_add_buddy(ic, status->user->screen_name, status->user->name);
+
+ imcb_buddy_msg( ic,
+ mode_one ? from : status->user->screen_name,
+ mode_one ? text : status->text,
+ 0, status->created_at );
+
// Update the home_timeline_id to hold the highest id, so that by the next request
// we won't pick up the updates allready in the list.
td->home_timeline_id = td->home_timeline_id < status->id ? status->id : td->home_timeline_id;
+
+ g_free( text );
}
}
@@ -482,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)
@@ -511,7 +538,7 @@ static void twitter_http_get_home_timeline(struct http_request *req)
xt_free( parser );
// See if the user wants to see the messages in a groupchat window or as private messages.
- if (set_getbool( &ic->acc->set, "use_groupchat" ))
+ if (g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "chat") == 0)
twitter_groupchat(ic, txl->list);
else
twitter_private_message_chat(ic, txl->list);
@@ -592,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]);
}
@@ -611,7 +638,7 @@ static void twitter_http_post_status(struct http_request *req)
// Check if the HTTP request went well.
if (req->status_code != 200) {
// It didn't go well, output the error and return.
- imcb_error(ic, "Could not post tweet... HTTP STATUS: %d", req->status_code);
+ imcb_error(ic, "Could not post message... HTTP STATUS: %d", req->status_code);
return;
}
}
@@ -626,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]);
}
@@ -644,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]);
}