source: protocols/twitter/twitter.c @ a91550c

Last change on this file since a91550c was 534e406, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-07-10T00:30:39Z

Translate "nick:" to "@nick" in Twitter rooms to make tab completion easier.
Not working yet with stripped/mangled nicknames.

  • Property mode set to 100644
File size: 11.4 KB
Line 
1/***************************************************************************\
2*                                                                           *
3*  BitlBee - An IRC to IM gateway                                           *
4*  Simple module to facilitate twitter functionality.                       *
5*                                                                           *
6*  Copyright 2009 Geert Mulders <g.c.w.m.mulders@gmail.com>                 *
7*                                                                           *
8*  This library is free software; you can redistribute it and/or            *
9*  modify it under the terms of the GNU Lesser General Public               *
10*  License as published by the Free Software Foundation, version            *
11*  2.1.                                                                     *
12*                                                                           *
13*  This library is distributed in the hope that it will be useful,          *
14*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
15*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU        *
16*  Lesser General Public License for more details.                          *
17*                                                                           *
18*  You should have received a copy of the GNU Lesser General Public License *
19*  along with this library; if not, write to the Free Software Foundation,  *
20*  Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA           *
21*                                                                           *
22****************************************************************************/
23
24#include "nogaim.h"
25#include "oauth.h"
26#include "twitter.h"
27#include "twitter_http.h"
28#include "twitter_lib.h"
29#include "url.h"
30
31/**
32 * Main loop function
33 */
34gboolean twitter_main_loop(gpointer data, gint fd, b_input_condition cond)
35{
36        struct im_connection *ic = data;
37       
38        // Check if we are still logged in...
39        if (!g_slist_find( twitter_connections, ic ))
40                return 0;
41
42        // Do stuff..
43        twitter_get_home_timeline(ic, -1);
44
45        // If we are still logged in run this function again after timeout.
46        return (ic->flags & OPT_LOGGED_IN) == OPT_LOGGED_IN;
47}
48
49static void twitter_main_loop_start( struct im_connection *ic )
50{
51        struct twitter_data *td = ic->proto_data;
52       
53        imcb_log( ic, "Getting initial statuses" );
54
55        // Run this once. After this queue the main loop function.
56        twitter_main_loop(ic, -1, 0);
57
58        // Queue the main_loop
59        // Save the return value, so we can remove the timeout on logout.
60        td->main_loop_id = b_timeout_add(60000, twitter_main_loop, ic);
61}
62
63static void twitter_oauth_start( struct im_connection *ic );
64
65void twitter_login_finish( struct im_connection *ic )
66{
67        struct twitter_data *td = ic->proto_data;
68       
69        if( set_getbool( &ic->acc->set, "oauth" ) && !td->oauth_info )
70                twitter_oauth_start( ic );
71        else if( g_strcasecmp( set_getstr( &ic->acc->set, "mode" ), "one" ) != 0 &&
72                 !( td->flags & TWITTER_HAVE_FRIENDS ) )
73        {
74                imcb_log( ic, "Getting contact list" );
75                twitter_get_statuses_friends( ic, -1 );
76        }
77        else
78                twitter_main_loop_start( ic );
79}
80
81static const struct oauth_service twitter_oauth =
82{
83        "http://api.twitter.com/oauth/request_token",
84        "http://api.twitter.com/oauth/access_token",
85        "https://api.twitter.com/oauth/authorize",
86        .consumer_key = "xsDNKJuNZYkZyMcu914uEA",
87        .consumer_secret = "FCxqcr0pXKzsF9ajmP57S3VQ8V6Drk4o2QYtqMcOszo",
88};
89
90static gboolean twitter_oauth_callback( struct oauth_info *info );
91
92static void twitter_oauth_start( struct im_connection *ic )
93{
94        struct twitter_data *td = ic->proto_data;
95       
96        imcb_log( ic, "Requesting OAuth request token" );
97
98        td->oauth_info = oauth_request_token( &twitter_oauth, twitter_oauth_callback, ic );
99}
100
101static gboolean twitter_oauth_callback( struct oauth_info *info )
102{
103        struct im_connection *ic = info->data;
104        struct twitter_data *td;
105       
106        if( !g_slist_find( twitter_connections, ic ) )
107                return FALSE;
108       
109        td = ic->proto_data;
110        if( info->stage == OAUTH_REQUEST_TOKEN )
111        {
112                char name[strlen(ic->acc->user)+9], *msg;
113               
114                if( info->request_token == NULL )
115                {
116                        imcb_error( ic, "OAuth error: %s", info->http->status_string );
117                        imc_logout( ic, TRUE );
118                        return FALSE;
119                }
120               
121                sprintf( name, "twitter_%s", ic->acc->user );
122                msg = g_strdup_printf( "To finish OAuth authentication, please visit "
123                                       "%s and respond with the resulting PIN code.",
124                                       info->auth_url );
125                imcb_buddy_msg( ic, name, msg, 0, 0 );
126                g_free( msg );
127        }
128        else if( info->stage == OAUTH_ACCESS_TOKEN )
129        {
130                if( info->token == NULL || info->token_secret == NULL )
131                {
132                        imcb_error( ic, "OAuth error: %s", info->http->status_string );
133                        imc_logout( ic, TRUE );
134                        return FALSE;
135                }
136               
137                /* IM mods didn't do this so far and it's ugly but I should
138                   be able to get away with it... */
139                g_free( ic->acc->pass );
140                ic->acc->pass = oauth_to_string( info );
141               
142                twitter_login_finish( ic );
143        }
144       
145        return TRUE;
146}
147
148
149static char *set_eval_mode( set_t *set, char *value )
150{
151        if( g_strcasecmp( value, "one" ) == 0 ||
152            g_strcasecmp( value, "many" ) == 0 ||
153            g_strcasecmp( value, "chat" ) == 0 )
154                return value;
155        else
156                return NULL;
157}
158
159static gboolean twitter_length_check( struct im_connection *ic, gchar *msg )
160{
161        int max = set_getint( &ic->acc->set, "message_length" ), len;
162       
163        if( max == 0 || ( len = g_utf8_strlen( msg, -1 ) ) <= max )
164                return TRUE;
165       
166        imcb_error( ic, "Maximum message length exceeded: %d > %d", len, max );
167       
168        return FALSE;
169}
170
171static void twitter_init( account_t *acc )
172{
173        set_t *s;
174       
175        s = set_add( &acc->set, "base_url", TWITTER_API_URL, NULL, acc );
176        s->flags |= ACC_SET_OFFLINE_ONLY;
177       
178        s = set_add( &acc->set, "message_length", "140", set_eval_int, acc );
179       
180        s = set_add( &acc->set, "mode", "one", set_eval_mode, acc );
181        s->flags |= ACC_SET_OFFLINE_ONLY;
182       
183        s = set_add( &acc->set, "oauth", "true", set_eval_bool, acc );
184}
185
186/**
187 * Login method. Since the twitter API works with seperate HTTP request we
188 * only save the user and pass to the twitter_data object.
189 */
190static void twitter_login( account_t *acc )
191{
192        struct im_connection *ic = imcb_new( acc );
193        struct twitter_data *td;
194        char name[strlen(acc->user)+9];
195        url_t url;
196
197        if( !url_set( &url, set_getstr( &ic->acc->set, "base_url" ) ) ||
198            ( url.proto != PROTO_HTTP && url.proto != PROTO_HTTPS ) )
199        {
200                imcb_error( ic, "Incorrect API base URL: %s", set_getstr( &ic->acc->set, "base_url" ) );
201                imc_logout( ic, FALSE );
202                return;
203        }
204       
205        twitter_connections = g_slist_append( twitter_connections, ic );
206        td = g_new0( struct twitter_data, 1 );
207        ic->proto_data = td;
208       
209        td->url_ssl = url.proto == PROTO_HTTPS;
210        td->url_port = url.port;
211        td->url_host = g_strdup( url.host );
212        if( strcmp( url.file, "/" ) != 0 )
213                td->url_path = g_strdup( url.file );
214        else
215                td->url_path = g_strdup( "" );
216       
217        td->user = acc->user;
218        if( strstr( acc->pass, "oauth_token=" ) )
219                td->oauth_info = oauth_from_string( acc->pass, &twitter_oauth );
220       
221        sprintf( name, "twitter_%s", acc->user );
222        imcb_add_buddy( ic, name, NULL );
223        imcb_buddy_status( ic, name, OPT_LOGGED_IN, NULL, NULL );
224       
225        imcb_log( ic, "Connecting" );
226       
227        twitter_login_finish( ic );
228}
229
230/**
231 * Logout method. Just free the twitter_data.
232 */
233static void twitter_logout( struct im_connection *ic )
234{
235        struct twitter_data *td = ic->proto_data;
236       
237        // Set the status to logged out.
238        ic->flags = 0;
239
240        // Remove the main_loop function from the function queue.
241        b_event_remove(td->main_loop_id);
242
243        if(td->home_timeline_gc)
244                imcb_chat_free(td->home_timeline_gc);
245
246        if( td )
247        {
248                oauth_info_free( td->oauth_info );
249                g_free( td->url_host );
250                g_free( td->url_path );
251                g_free( td->pass );
252                g_free( td );
253        }
254
255        twitter_connections = g_slist_remove( twitter_connections, ic );
256}
257
258/**
259 *
260 */
261static int twitter_buddy_msg( struct im_connection *ic, char *who, char *message, int away )
262{
263        struct twitter_data *td = ic->proto_data;
264       
265        if (g_strncasecmp(who, "twitter_", 8) == 0 &&
266            g_strcasecmp(who + 8, ic->acc->user) == 0)
267        {
268                if( set_getbool( &ic->acc->set, "oauth" ) &&
269                    td->oauth_info && td->oauth_info->token == NULL )
270                {
271                        char pin[strlen(message)+1], *s;
272                       
273                        strcpy( pin, message );
274                        for( s = pin + sizeof( pin ) - 2; s > pin && isspace( *s ); s -- )
275                                *s = '\0';
276                        for( s = pin; *s && isspace( *s ); s ++ ) {}
277                       
278                        if( !oauth_access_token( s, td->oauth_info ) )
279                        {
280                                imcb_error( ic, "OAuth error: %s", "Failed to send access token request" );
281                                imc_logout( ic, TRUE );
282                                return FALSE;
283                        }
284                }
285                else if( twitter_length_check(ic, message) )
286                        twitter_post_status(ic, message);
287        }
288        else
289        {
290                twitter_direct_messages_new(ic, who, message);
291        }
292        return( 0 );
293}
294
295/**
296 *
297 */
298static void twitter_set_my_name( struct im_connection *ic, char *info )
299{
300}
301
302static void twitter_get_info(struct im_connection *ic, char *who) 
303{
304}
305
306static void twitter_add_buddy( struct im_connection *ic, char *who, char *group )
307{
308        twitter_friendships_create_destroy(ic, who, 1);
309}
310
311static void twitter_remove_buddy( struct im_connection *ic, char *who, char *group )
312{
313        twitter_friendships_create_destroy(ic, who, 0);
314}
315
316static void twitter_chat_msg( struct groupchat *c, char *message, int flags )
317{
318        if( c && message && twitter_length_check( c->ic, message ) )
319        {
320                char *s, *new = NULL;
321               
322                if( ( s = strchr( message, ':' ) ) ||
323                    ( s = strchr( message, ',' ) ) )
324                {
325                        bee_user_t *bu;
326                       
327                        new = g_strdup( message );
328                        new[s-message] = '\0';
329                        if( ( bu = bee_user_by_handle( c->ic->bee, c->ic, new ) ) )
330                        {
331                                sprintf( new, "@%s", bu->handle );
332                                new[s-message+1] = ' ';
333                                message = new;
334                        }
335                }
336               
337                twitter_post_status( c->ic, message );
338                g_free( new );
339        }
340}
341
342static void twitter_chat_invite( struct groupchat *c, char *who, char *message )
343{
344}
345
346static void twitter_chat_leave( struct groupchat *c )
347{
348        struct twitter_data *td = c->ic->proto_data;
349       
350        if( c != td->home_timeline_gc )
351                return; /* WTF? */
352       
353        /* If the user leaves the channel: Fine. Rejoin him/her once new
354           tweets come in. */
355        imcb_chat_free(td->home_timeline_gc);
356        td->home_timeline_gc = NULL;
357}
358
359static struct groupchat *twitter_chat_with( struct im_connection *ic, char *who )
360{
361        return NULL;
362}
363
364static void twitter_keepalive( struct im_connection *ic )
365{
366}
367
368static void twitter_add_permit( struct im_connection *ic, char *who )
369{
370}
371
372static void twitter_rem_permit( struct im_connection *ic, char *who )
373{
374}
375
376static void twitter_add_deny( struct im_connection *ic, char *who )
377{
378}
379
380static void twitter_rem_deny( struct im_connection *ic, char *who )
381{
382}
383
384static int twitter_send_typing( struct im_connection *ic, char *who, int typing )
385{
386        return( 1 );
387}
388
389//static char *twitter_set_display_name( set_t *set, char *value )
390//{
391//      return value;
392//}
393
394void twitter_initmodule()
395{
396        struct prpl *ret = g_new0(struct prpl, 1);
397       
398        ret->name = "twitter";
399        ret->login = twitter_login;
400        ret->init = twitter_init;
401        ret->logout = twitter_logout;
402        ret->buddy_msg = twitter_buddy_msg;
403        ret->get_info = twitter_get_info;
404        ret->set_my_name = twitter_set_my_name;
405        ret->add_buddy = twitter_add_buddy;
406        ret->remove_buddy = twitter_remove_buddy;
407        ret->chat_msg = twitter_chat_msg;
408        ret->chat_invite = twitter_chat_invite;
409        ret->chat_leave = twitter_chat_leave;
410        ret->chat_with = twitter_chat_with;
411        ret->keepalive = twitter_keepalive;
412        ret->add_permit = twitter_add_permit;
413        ret->rem_permit = twitter_rem_permit;
414        ret->add_deny = twitter_add_deny;
415        ret->rem_deny = twitter_rem_deny;
416        ret->send_typing = twitter_send_typing;
417        ret->handle_cmp = g_strcasecmp;
418
419        register_protocol(ret);
420
421        // Initialise the twitter_connections GSList.
422        twitter_connections = NULL;
423}
424
Note: See TracBrowser for help on using the repository browser.