source: protocols/twitter/twitter.c @ 04a927c

Last change on this file since 04a927c was 04a927c, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-06-07T23:04:58Z

Fixing some memory leakage.

  • Property mode set to 100644
File size: 10.6 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        // If the user uses multiple private message windows we need to get the
43        // users buddies.
44        if (g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "many") == 0)
45                twitter_get_statuses_friends(ic, -1);
46
47        // Do stuff..
48        twitter_get_home_timeline(ic, -1);
49
50        // If we are still logged in run this function again after timeout.
51        return (ic->flags & OPT_LOGGED_IN) == OPT_LOGGED_IN;
52}
53
54static void twitter_main_loop_start( struct im_connection *ic )
55{
56        struct twitter_data *td = ic->proto_data;
57       
58        imcb_log( ic, "Connecting to Twitter" );
59
60        // Run this once. After this queue the main loop function.
61        twitter_main_loop(ic, -1, 0);
62
63        // Queue the main_loop
64        // Save the return value, so we can remove the timeout on logout.
65        td->main_loop_id = b_timeout_add(60000, twitter_main_loop, ic);
66}
67
68
69static const struct oauth_service twitter_oauth =
70{
71        "http://api.twitter.com/oauth/request_token",
72        "http://api.twitter.com/oauth/access_token",
73        "https://api.twitter.com/oauth/authorize",
74        .consumer_key = "xsDNKJuNZYkZyMcu914uEA",
75        .consumer_secret = "FCxqcr0pXKzsF9ajmP57S3VQ8V6Drk4o2QYtqMcOszo",
76};
77
78static gboolean twitter_oauth_callback( struct oauth_info *info );
79
80static void twitter_oauth_start( struct im_connection *ic )
81{
82        struct twitter_data *td = ic->proto_data;
83       
84        imcb_log( ic, "Requesting OAuth request token" );
85
86        td->oauth_info = oauth_request_token( &twitter_oauth, twitter_oauth_callback, ic );
87}
88
89static gboolean twitter_oauth_callback( struct oauth_info *info )
90{
91        struct im_connection *ic = info->data;
92        struct twitter_data *td;
93       
94        if( !g_slist_find( twitter_connections, ic ) )
95                return FALSE;
96       
97        td = ic->proto_data;
98        if( info->stage == OAUTH_REQUEST_TOKEN )
99        {
100                char name[strlen(ic->acc->user)+9], *msg;
101               
102                if( info->request_token == NULL )
103                {
104                        imcb_error( ic, "OAuth error: %s", info->http->status_string );
105                        imc_logout( ic, TRUE );
106                        return FALSE;
107                }
108               
109                sprintf( name, "twitter_%s", ic->acc->user );
110                msg = g_strdup_printf( "To finish OAuth authentication, please visit "
111                                       "%s and respond with the resulting PIN code.",
112                                       info->auth_url );
113                imcb_buddy_msg( ic, name, msg, 0, 0 );
114                g_free( msg );
115        }
116        else if( info->stage == OAUTH_ACCESS_TOKEN )
117        {
118                if( info->token == NULL || info->token_secret == NULL )
119                {
120                        imcb_error( ic, "OAuth error: %s", info->http->status_string );
121                        imc_logout( ic, TRUE );
122                        return FALSE;
123                }
124               
125                /* IM mods didn't do this so far and it's ugly but I should
126                   be able to get away with it... */
127                g_free( ic->acc->pass );
128                ic->acc->pass = oauth_to_string( info );
129               
130                twitter_main_loop_start( ic );
131        }
132       
133        return TRUE;
134}
135
136
137static char *set_eval_mode( set_t *set, char *value )
138{
139        if( g_strcasecmp( value, "one" ) == 0 ||
140            g_strcasecmp( value, "many" ) == 0 ||
141            g_strcasecmp( value, "chat" ) == 0 )
142                return value;
143        else
144                return NULL;
145}
146
147static gboolean twitter_length_check( struct im_connection *ic, gchar *msg )
148{
149        int max = set_getint( &ic->acc->set, "message_length" ), len;
150       
151        if( max == 0 || ( len = g_utf8_strlen( msg, -1 ) ) <= max )
152                return TRUE;
153       
154        imcb_error( ic, "Maximum message length exceeded: %d > %d", len, max );
155       
156        return FALSE;
157}
158
159static void twitter_init( account_t *acc )
160{
161        set_t *s;
162       
163        s = set_add( &acc->set, "base_url", TWITTER_API_URL, NULL, acc );
164        s->flags |= ACC_SET_OFFLINE_ONLY;
165       
166        s = set_add( &acc->set, "message_length", "140", set_eval_int, acc );
167       
168        s = set_add( &acc->set, "mode", "one", set_eval_mode, acc );
169        s->flags |= ACC_SET_OFFLINE_ONLY;
170       
171        s = set_add( &acc->set, "oauth", "true", set_eval_bool, acc );
172}
173
174/**
175 * Login method. Since the twitter API works with seperate HTTP request we
176 * only save the user and pass to the twitter_data object.
177 */
178static void twitter_login( account_t *acc )
179{
180        struct im_connection *ic = imcb_new( acc );
181        struct twitter_data *td;
182        char name[strlen(acc->user)+9];
183        url_t url;
184
185        if( !url_set( &url, set_getstr( &ic->acc->set, "base_url" ) ) ||
186            ( url.proto != PROTO_HTTP && url.proto != PROTO_HTTPS ) )
187        {
188                imcb_error( ic, "Incorrect API base URL: %s", set_getstr( &ic->acc->set, "base_url" ) );
189                imc_logout( ic, FALSE );
190                return;
191        }
192       
193        twitter_connections = g_slist_append( twitter_connections, ic );
194        td = g_new0( struct twitter_data, 1 );
195        ic->proto_data = td;
196       
197        td->url_ssl = url.proto == PROTO_HTTPS;
198        td->url_port = url.port;
199        td->url_host = g_strdup( url.host );
200        if( strcmp( url.file, "/" ) != 0 )
201                td->url_path = g_strdup( url.file );
202        else
203                td->url_path = g_strdup( "" );
204       
205        td->user = acc->user;
206        if( strstr( acc->pass, "oauth_token=" ) )
207                td->oauth_info = oauth_from_string( acc->pass, &twitter_oauth );
208       
209        sprintf( name, "twitter_%s", acc->user );
210        imcb_add_buddy( ic, name, NULL );
211        imcb_buddy_status( ic, name, OPT_LOGGED_IN, NULL, NULL );
212       
213        if( td->oauth_info || !set_getbool( &acc->set, "oauth" ) )
214                twitter_main_loop_start( ic );
215        else
216                twitter_oauth_start( ic );
217}
218
219/**
220 * Logout method. Just free the twitter_data.
221 */
222static void twitter_logout( struct im_connection *ic )
223{
224        struct twitter_data *td = ic->proto_data;
225       
226        // Set the status to logged out.
227        ic->flags = 0;
228
229        // Remove the main_loop function from the function queue.
230        b_event_remove(td->main_loop_id);
231
232        if(td->home_timeline_gc)
233                imcb_chat_free(td->home_timeline_gc);
234
235        if( td )
236        {
237                oauth_info_free( td->oauth_info );
238                g_free( td->url_host );
239                g_free( td->url_path );
240                g_free( td->pass );
241                g_free( td );
242        }
243
244        twitter_connections = g_slist_remove( twitter_connections, ic );
245}
246
247/**
248 *
249 */
250static int twitter_buddy_msg( struct im_connection *ic, char *who, char *message, int away )
251{
252        struct twitter_data *td = ic->proto_data;
253       
254        if (g_strncasecmp(who, "twitter_", 8) == 0 &&
255            g_strcasecmp(who + 8, ic->acc->user) == 0)
256        {
257                if( set_getbool( &ic->acc->set, "oauth" ) &&
258                    td->oauth_info && td->oauth_info->token == NULL )
259                {
260                        if( !oauth_access_token( message, td->oauth_info ) )
261                        {
262                                imcb_error( ic, "OAuth error: %s", "Failed to send access token request" );
263                                imc_logout( ic, TRUE );
264                                return FALSE;
265                        }
266                }
267                else if( twitter_length_check(ic, message) )
268                        twitter_post_status(ic, message);
269        }
270        else
271        {
272                twitter_direct_messages_new(ic, who, message);
273        }
274        return( 0 );
275}
276
277/**
278 *
279 */
280static void twitter_set_my_name( struct im_connection *ic, char *info )
281{
282}
283
284static void twitter_get_info(struct im_connection *ic, char *who) 
285{
286}
287
288static void twitter_add_buddy( struct im_connection *ic, char *who, char *group )
289{
290        twitter_friendships_create_destroy(ic, who, 1);
291}
292
293static void twitter_remove_buddy( struct im_connection *ic, char *who, char *group )
294{
295        twitter_friendships_create_destroy(ic, who, 0);
296}
297
298static void twitter_chat_msg( struct groupchat *c, char *message, int flags )
299{
300        if( c && message && twitter_length_check(c->ic, message))
301                twitter_post_status(c->ic, message);
302}
303
304static void twitter_chat_invite( struct groupchat *c, char *who, char *message )
305{
306}
307
308static void twitter_chat_leave( struct groupchat *c )
309{
310        struct twitter_data *td = c->ic->proto_data;
311       
312        if( c != td->home_timeline_gc )
313                return; /* WTF? */
314       
315        /* If the user leaves the channel: Fine. Rejoin him/her once new
316           tweets come in. */
317        imcb_chat_free(td->home_timeline_gc);
318        td->home_timeline_gc = NULL;
319}
320
321static struct groupchat *twitter_chat_with( struct im_connection *ic, char *who )
322{
323        return NULL;
324}
325
326static void twitter_keepalive( struct im_connection *ic )
327{
328}
329
330static void twitter_add_permit( struct im_connection *ic, char *who )
331{
332}
333
334static void twitter_rem_permit( struct im_connection *ic, char *who )
335{
336}
337
338static void twitter_add_deny( struct im_connection *ic, char *who )
339{
340}
341
342static void twitter_rem_deny( struct im_connection *ic, char *who )
343{
344}
345
346static int twitter_send_typing( struct im_connection *ic, char *who, int typing )
347{
348        return( 1 );
349}
350
351//static char *twitter_set_display_name( set_t *set, char *value )
352//{
353//      return value;
354//}
355
356void twitter_initmodule()
357{
358        struct prpl *ret = g_new0(struct prpl, 1);
359       
360        ret->name = "twitter";
361        ret->login = twitter_login;
362        ret->init = twitter_init;
363        ret->logout = twitter_logout;
364        ret->buddy_msg = twitter_buddy_msg;
365        ret->get_info = twitter_get_info;
366        ret->set_my_name = twitter_set_my_name;
367        ret->add_buddy = twitter_add_buddy;
368        ret->remove_buddy = twitter_remove_buddy;
369        ret->chat_msg = twitter_chat_msg;
370        ret->chat_invite = twitter_chat_invite;
371        ret->chat_leave = twitter_chat_leave;
372        ret->chat_with = twitter_chat_with;
373        ret->keepalive = twitter_keepalive;
374        ret->add_permit = twitter_add_permit;
375        ret->rem_permit = twitter_rem_permit;
376        ret->add_deny = twitter_add_deny;
377        ret->rem_deny = twitter_rem_deny;
378        ret->send_typing = twitter_send_typing;
379        ret->handle_cmp = g_strcasecmp;
380
381        register_protocol(ret);
382
383        // Initialise the twitter_connections GSList.
384        twitter_connections = NULL;
385}
386
Note: See TracBrowser for help on using the repository browser.