source: protocols/twitter/twitter_lib.c @ 2528cda

Last change on this file since 2528cda was b890626, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-08-08T13:42:57Z

Add a few more commands (including RT) and the ability to send replies.
That's it for now, this is already not very pretty, but just offers the bare
basic functionality.

  • Property mode set to 100644
File size: 21.9 KB
RevLine 
[1b221e0]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
[08579a1]24/* For strptime(): */
[daae10f]25#if(__sun)
26#else
[08579a1]27#define _XOPEN_SOURCE
[daae10f]28#endif
[08579a1]29
[1b221e0]30#include "twitter_http.h"
31#include "twitter.h"
32#include "bitlbee.h"
33#include "url.h"
34#include "misc.h"
35#include "base64.h"
36#include "xmltree.h"
37#include "twitter_lib.h"
38#include <ctype.h>
39#include <errno.h>
40
[e4e0b37]41/* GLib < 2.12.0 doesn't have g_ascii_strtoll(), work around using system strtoll(). */
42/* GLib < 2.12.4 can be buggy: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=488013 */
43#if !GLIB_CHECK_VERSION(2,12,5)
44#include <stdlib.h>
45#include <limits.h>
46#define g_ascii_strtoll strtoll
47#endif
48
[1b221e0]49#define TXL_STATUS 1
[62d2cfb]50#define TXL_USER 2
51#define TXL_ID 3
52
[1b221e0]53struct twitter_xml_list {
[62d2cfb]54        int type;
[8203da9]55        gint64 next_cursor;
[1b221e0]56        GSList *list;
57        gpointer data;
58};
59
60struct twitter_xml_user {
61        char *name;
62        char *screen_name;
63};
64
65struct twitter_xml_status {
[08579a1]66        time_t created_at;
[1b221e0]67        char *text;
68        struct twitter_xml_user *user;
69        guint64 id;
70};
71
[d6aa6dd]72static void twitter_groupchat_init(struct im_connection *ic);
73
[62d2cfb]74/**
75 * Frees a twitter_xml_user struct.
76 */
77static void txu_free(struct twitter_xml_user *txu)
78{
[a26af5c]79        if (txu == NULL)
80                return;
[62d2cfb]81        g_free(txu->name);
82        g_free(txu->screen_name);
[2abceca]83        g_free(txu);
[62d2cfb]84}
85
86
87/**
88 * Frees a twitter_xml_status struct.
89 */
90static void txs_free(struct twitter_xml_status *txs)
91{
92        g_free(txs->text);
93        txu_free(txs->user);
[2abceca]94        g_free(txs);
[62d2cfb]95}
96
97/**
98 * Free a twitter_xml_list struct.
99 * type is the type of list the struct holds.
100 */
101static void txl_free(struct twitter_xml_list *txl)
102{
103        GSList *l;
[a26af5c]104        if (txl == NULL)
105                return;
[62d2cfb]106        for ( l = txl->list; l ; l = g_slist_next(l) )
107                if (txl->type == TXL_STATUS)
108                        txs_free((struct twitter_xml_status *)l->data);
109                else if (txl->type == TXL_ID)
110                        g_free(l->data);
111        g_slist_free(txl->list);
112}
113
114/**
115 * Add a buddy if it is not allready added, set the status to logged in.
116 */
[3e69802]117static void twitter_add_buddy(struct im_connection *ic, char *name, const char *fullname)
[62d2cfb]118{
[1014cab]119        struct twitter_data *td = ic->proto_data;
120
[62d2cfb]121        // Check if the buddy is allready in the buddy list.
[21c87a7]122        if (!bee_user_by_handle( ic->bee, ic, name ))
[62d2cfb]123        {
[e88fbe27]124                char *mode = set_getstr(&ic->acc->set, "mode");
125               
[62d2cfb]126                // The buddy is not in the list, add the buddy and set the status to logged in.
127                imcb_add_buddy( ic, name, NULL );
[3e69802]128                imcb_rename_buddy( ic, name, fullname );
[e88fbe27]129                if (g_strcasecmp(mode, "chat") == 0)
[5c18a76]130                {
131                        /* Necessary so that nicks always get translated to the
132                           exact Twitter username. */
133                        imcb_buddy_nick_hint( ic, name, name );
[1014cab]134                        imcb_chat_add_buddy( td->home_timeline_gc, name );
[5c18a76]135                }
[e88fbe27]136                else if (g_strcasecmp(mode, "many") == 0)
[1014cab]137                        imcb_buddy_status( ic, name, OPT_LOGGED_IN, NULL, NULL );
[62d2cfb]138        }
139}
[1b221e0]140
[a7b9ec7]141/* Warning: May return a malloc()ed value, which will be free()d on the next
142   call. Only for short-term use. */
143static char *twitter_parse_error(struct http_request *req)
144{
145        static char *ret = NULL;
[3d93aed]146        struct xt_parser *xp = NULL;
[a7b9ec7]147        struct xt_node *node;
148       
149        g_free(ret);
150        ret = NULL;
151       
[3d93aed]152        if (req->body_size > 0)
[a7b9ec7]153        {
[3d93aed]154                xp = xt_new(NULL, NULL);
155                xt_feed(xp, req->reply_body, req->body_size);
156               
157                if ((node = xt_find_node(xp->root, "hash")) &&
158                    (node = xt_find_node(node->children, "error")) &&
159                    node->text_len > 0)
160                {
161                        ret = g_strdup_printf("%s (%s)", req->status_string, node->text);
162                        xt_free(xp);
163                        return ret;
164                }
165               
[a7b9ec7]166                xt_free(xp);
167        }
[3d93aed]168       
169        return req->status_string;
[a7b9ec7]170}
171
[1b221e0]172static void twitter_http_get_friends_ids(struct http_request *req);
173
174/**
175 * Get the friends ids.
176 */
[8203da9]177void twitter_get_friends_ids(struct im_connection *ic, gint64 next_cursor)
[1b221e0]178{
179        // Primitive, but hey! It works...     
180        char* args[2];
181        args[0] = "cursor";
[8203da9]182        args[1] = g_strdup_printf ("%lld", (long long) next_cursor);
[bb5ce4d1]183        twitter_http(ic, TWITTER_FRIENDS_IDS_URL, twitter_http_get_friends_ids, ic, 0, args, 2);
[1b221e0]184
185        g_free(args[1]);
186}
187
188/**
189 * Function to help fill a list.
190 */
191static xt_status twitter_xt_next_cursor( struct xt_node *node, struct twitter_xml_list *txl )
192{
[bd64716]193        char *end = NULL;
194       
195        if( node->text )
196                txl->next_cursor = g_ascii_strtoll( node->text, &end, 10 );
197        if( end == NULL )
198                txl->next_cursor = -1;
[1b221e0]199
200        return XT_HANDLED;
201}
202
203/**
204 * Fill a list of ids.
205 */
206static xt_status twitter_xt_get_friends_id_list( struct xt_node *node, struct twitter_xml_list *txl )
207{
208        struct xt_node *child;
[62d2cfb]209       
210        // Set the list type.
211        txl->type = TXL_ID;
[1b221e0]212
213        // The root <statuses> node should hold the list of statuses <status>
214        // Walk over the nodes children.
215        for( child = node->children ; child ; child = child->next )
216        {
217                if ( g_strcasecmp( "id", child->name ) == 0)
218                {
219                        // Add the item to the list.
[2abceca]220                        txl->list = g_slist_append (txl->list, g_memdup( child->text, child->text_len + 1 ));
[1b221e0]221                }
222                else if ( g_strcasecmp( "next_cursor", child->name ) == 0)
223                {
224                        twitter_xt_next_cursor(child, txl);
225                }
226        }
227
228        return XT_HANDLED;
229}
230
231/**
232 * Callback for getting the friends ids.
233 */
234static void twitter_http_get_friends_ids(struct http_request *req)
235{
236        struct im_connection *ic;
237        struct xt_parser *parser;
238        struct twitter_xml_list *txl;
[3bd4a93]239        struct twitter_data *td;
[1b221e0]240
241        ic = req->data;
242
[62d2cfb]243        // Check if the connection is still active.
244        if( !g_slist_find( twitter_connections, ic ) )
245                return;
[37aa317]246       
247        td = ic->proto_data;
[62d2cfb]248
[1b221e0]249        // Check if the HTTP request went well.
250        if (req->status_code != 200) {
251                // It didn't go well, output the error and return.
[3bd4a93]252                if (++td->http_fails >= 5)
[a7b9ec7]253                        imcb_error(ic, "Could not retrieve friends: %s", twitter_parse_error(req));
[3bd4a93]254               
[1b221e0]255                return;
[3bd4a93]256        } else {
257                td->http_fails = 0;
[1b221e0]258        }
259
260        txl = g_new0(struct twitter_xml_list, 1);
261
262        // Parse the data.
263        parser = xt_new( NULL, txl );
264        xt_feed( parser, req->reply_body, req->body_size );
265        twitter_xt_get_friends_id_list(parser->root, txl);
266        xt_free( parser );
267
268        if (txl->next_cursor)
269                twitter_get_friends_ids(ic, txl->next_cursor);
270
[62d2cfb]271        txl_free(txl);
[1b221e0]272        g_free(txl);
273}
274
275/**
276 * Function to fill a twitter_xml_user struct.
277 * It sets:
278 *  - the name and
279 *  - the screen_name.
280 */
281static xt_status twitter_xt_get_user( struct xt_node *node, struct twitter_xml_user *txu )
282{
283        struct xt_node *child;
284
285        // Walk over the nodes children.
286        for( child = node->children ; child ; child = child->next )
287        {
288                if ( g_strcasecmp( "name", child->name ) == 0)
289                {
290                        txu->name = g_memdup( child->text, child->text_len + 1 );
291                }
292                else if (g_strcasecmp( "screen_name", child->name ) == 0)
293                {
294                        txu->screen_name = g_memdup( child->text, child->text_len + 1 );
295                }
296        }
297        return XT_HANDLED;
298}
299
[62d2cfb]300/**
301 * Function to fill a twitter_xml_list struct.
302 * It sets:
303 *  - all <user>s from the <users> element.
304 */
305static xt_status twitter_xt_get_users( struct xt_node *node, struct twitter_xml_list *txl )
306{
307        struct twitter_xml_user *txu;
308        struct xt_node *child;
309
310        // Set the type of the list.
311        txl->type = TXL_USER;
312
313        // The root <users> node should hold the list of users <user>
314        // Walk over the nodes children.
315        for( child = node->children ; child ; child = child->next )
316        {
317                if ( g_strcasecmp( "user", child->name ) == 0)
318                {
319                        txu = g_new0(struct twitter_xml_user, 1);
320                        twitter_xt_get_user(child, txu);
321                        // Put the item in the front of the list.
322                        txl->list = g_slist_prepend (txl->list, txu);
323                }
324        }
325
326        return XT_HANDLED;
327}
328
329/**
330 * Function to fill a twitter_xml_list struct.
331 * It calls twitter_xt_get_users to get the <user>s from a <users> element.
332 * It sets:
333 *  - the next_cursor.
334 */
335static xt_status twitter_xt_get_user_list( struct xt_node *node, struct twitter_xml_list *txl )
336{
337        struct xt_node *child;
338
339        // Set the type of the list.
340        txl->type = TXL_USER;
341
342        // The root <user_list> node should hold a users <users> element
343        // Walk over the nodes children.
344        for( child = node->children ; child ; child = child->next )
345        {
346                if ( g_strcasecmp( "users", child->name ) == 0)
347                {
348                        twitter_xt_get_users(child, txl);
349                }
350                else if ( g_strcasecmp( "next_cursor", child->name ) == 0)
351                {
352                        twitter_xt_next_cursor(child, txl);
353                }
354        }
355
356        return XT_HANDLED;
357}
358
359
[1b221e0]360/**
361 * Function to fill a twitter_xml_status struct.
362 * It sets:
363 *  - the status text and
364 *  - the created_at timestamp and
365 *  - the status id and
366 *  - the user in a twitter_xml_user struct.
367 */
368static xt_status twitter_xt_get_status( struct xt_node *node, struct twitter_xml_status *txs )
369{
[e193aeb]370        struct xt_node *child, *rt;
371        gboolean truncated = FALSE;
[1b221e0]372
373        // Walk over the nodes children.
374        for( child = node->children ; child ; child = child->next )
375        {
376                if ( g_strcasecmp( "text", child->name ) == 0)
377                {
378                        txs->text = g_memdup( child->text, child->text_len + 1 );
379                }
[e193aeb]380                else if (g_strcasecmp( "truncated", child->name ) == 0 && child->text)
381                {
382                        truncated = bool2int(child->text);
383                }
384                else if (g_strcasecmp( "retweeted_status", child->name ) == 0)
385                {
386                        rt = child;
387                }
[1b221e0]388                else if (g_strcasecmp( "created_at", child->name ) == 0)
389                {
[08579a1]390                        struct tm parsed;
391                       
392                        /* Very sensitive to changes to the formatting of
393                           this field. :-( Also assumes the timezone used
394                           is UTC since C time handling functions suck. */
395                        if( strptime( child->text, "%a %b %d %H:%M:%S %z %Y", &parsed ) != NULL )
396                                txs->created_at = mktime_utc( &parsed );
[1b221e0]397                }
398                else if (g_strcasecmp( "user", child->name ) == 0)
399                {
400                        txs->user = g_new0(struct twitter_xml_user, 1);
401                        twitter_xt_get_user( child, txs->user );
402                }
403                else if (g_strcasecmp( "id", child->name ) == 0)
404                {
405                        txs->id = g_ascii_strtoull (child->text, NULL, 10);
406                }
407        }
[e193aeb]408       
409        /* If it's a truncated retweet, get the original because dots suck. */
410        if (truncated && rt)
411        {
412                struct twitter_xml_status *rtxs = g_new0(struct twitter_xml_status, 1);
413                if (twitter_xt_get_status(rt, rtxs) != XT_HANDLED)
414                {
415                        txs_free(rtxs);
416                        return XT_HANDLED;
417                }
418               
419                g_free(txs->text);
420                txs->text = g_strdup_printf("RT @%s: %s", rtxs->user->screen_name, rtxs->text);
421                txs_free(rtxs);
422        }
423       
[1b221e0]424        return XT_HANDLED;
425}
426
427/**
428 * Function to fill a twitter_xml_list struct.
429 * It sets:
430 *  - all <status>es within the <status> element and
431 *  - the next_cursor.
432 */
[203a2d2]433static xt_status twitter_xt_get_status_list( struct im_connection *ic, struct xt_node *node, struct twitter_xml_list *txl )
[1b221e0]434{
435        struct twitter_xml_status *txs;
436        struct xt_node *child;
[203a2d2]437        bee_user_t *bu;
[1b221e0]438
[62d2cfb]439        // Set the type of the list.
440        txl->type = TXL_STATUS;
441
[1b221e0]442        // The root <statuses> node should hold the list of statuses <status>
443        // Walk over the nodes children.
444        for( child = node->children ; child ; child = child->next )
445        {
446                if ( g_strcasecmp( "status", child->name ) == 0)
447                {
448                        txs = g_new0(struct twitter_xml_status, 1);
449                        twitter_xt_get_status(child, txs);
450                        // Put the item in the front of the list.
451                        txl->list = g_slist_prepend (txl->list, txs);
[203a2d2]452                       
453                        if (txs->user && txs->user->screen_name &&
454                            (bu = bee_user_by_handle(ic->bee, ic, txs->user->screen_name)))
455                        {
456                                struct twitter_user_data *tud = bu->data;
457                               
458                                if (txs->id > tud->last_id)
459                                {
460                                        tud->last_id = txs->id;
461                                        tud->last_time = txs->created_at;
462                                }
463                        }
[1b221e0]464                }
465                else if ( g_strcasecmp( "next_cursor", child->name ) == 0)
466                {
467                        twitter_xt_next_cursor(child, txl);
468                }
469        }
470
471        return XT_HANDLED;
472}
473
474static void twitter_http_get_home_timeline(struct http_request *req);
475
476/**
477 * Get the timeline.
478 */
[8203da9]479void twitter_get_home_timeline(struct im_connection *ic, gint64 next_cursor)
[1b221e0]480{
481        struct twitter_data *td = ic->proto_data;
482
483        char* args[4];
484        args[0] = "cursor";
[8203da9]485        args[1] = g_strdup_printf ("%lld", (long long) next_cursor);
[1b221e0]486        if (td->home_timeline_id) {
487                args[2] = "since_id";
[0519b0a]488                args[3] = g_strdup_printf ("%llu", (long long unsigned int) td->home_timeline_id);
[1b221e0]489        }
490
[bb5ce4d1]491        twitter_http(ic, TWITTER_HOME_TIMELINE_URL, twitter_http_get_home_timeline, ic, 0, args, td->home_timeline_id ? 4 : 2);
[1b221e0]492
493        g_free(args[1]);
494        if (td->home_timeline_id) {
495                g_free(args[3]);
496        }
497}
498
[d6aa6dd]499static void twitter_groupchat_init(struct im_connection *ic)
500{
501        char *name_hint;
502        struct groupchat *gc;
503        struct twitter_data *td = ic->proto_data;
504       
505        td->home_timeline_gc = gc = imcb_chat_new( ic, "home/timeline" );
506       
[ffcdf13]507        name_hint = g_strdup_printf( "%s_%s", td->prefix, ic->acc->user );
[d6aa6dd]508        imcb_chat_name_hint( gc, name_hint );
509        g_free( name_hint );
510}
511
[62d2cfb]512/**
513 * Function that is called to see the statuses in a groupchat window.
514 */
515static void twitter_groupchat(struct im_connection *ic, GSList *list)
516{
517        struct twitter_data *td = ic->proto_data;
518        GSList *l = NULL;
519        struct twitter_xml_status *status;
520        struct groupchat *gc;
521
522        // Create a new groupchat if it does not exsist.
523        if (!td->home_timeline_gc)
[d6aa6dd]524                twitter_groupchat_init(ic);
525       
526        gc = td->home_timeline_gc;
527        if (!gc->joined)
[62d2cfb]528                imcb_chat_add_buddy( gc, ic->acc->user );
529
530        for ( l = list; l ; l = g_slist_next(l) )
531        {
532                status = l->data;
[a26af5c]533                if (status->user == NULL || status->text == NULL)
534                        continue;
535
[3e69802]536                twitter_add_buddy(ic, status->user->screen_name, status->user->name);
[d569019]537               
[0b3ffb1]538                strip_html(status->text);
539               
[62d2cfb]540                // Say it!
[d569019]541                if (g_strcasecmp(td->user, status->user->screen_name) == 0)
542                        imcb_chat_log (gc, "Your Tweet: %s", status->text);
543                else
[08579a1]544                        imcb_chat_msg (gc, status->user->screen_name, status->text, 0, status->created_at );
[d569019]545               
[62d2cfb]546                // Update the home_timeline_id to hold the highest id, so that by the next request
547                // we won't pick up the updates allready in the list.
548                td->home_timeline_id = td->home_timeline_id < status->id ? status->id : td->home_timeline_id;
549        }
550}
551
552/**
553 * Function that is called to see statuses as private messages.
554 */
555static void twitter_private_message_chat(struct im_connection *ic, GSList *list)
556{
557        struct twitter_data *td = ic->proto_data;
558        GSList *l = NULL;
559        struct twitter_xml_status *status;
[e88fbe27]560        char from[MAX_STRING];
561        gboolean mode_one;
562       
563        mode_one = g_strcasecmp( set_getstr( &ic->acc->set, "mode" ), "one" ) == 0;
[62d2cfb]564
[e88fbe27]565        if( mode_one )
566        {
[ffcdf13]567                g_snprintf( from, sizeof( from ) - 1, "%s_%s", td->prefix, ic->acc->user );
[e88fbe27]568                from[MAX_STRING-1] = '\0';
569        }
570       
[62d2cfb]571        for ( l = list; l ; l = g_slist_next(l) )
572        {
[e88fbe27]573                char *text = NULL;
574               
[62d2cfb]575                status = l->data;
[e88fbe27]576               
[0b3ffb1]577                strip_html( status->text );
[e88fbe27]578                if( mode_one )
579                        text = g_strdup_printf( "\002<\002%s\002>\002 %s",
580                                                status->user->screen_name, status->text );
[55b1e69]581                else
582                        twitter_add_buddy(ic, status->user->screen_name, status->user->name);
[e88fbe27]583               
584                imcb_buddy_msg( ic,
585                                mode_one ? from : status->user->screen_name,
586                                mode_one ? text : status->text,
587                                0, status->created_at );
588               
[62d2cfb]589                // Update the home_timeline_id to hold the highest id, so that by the next request
590                // we won't pick up the updates allready in the list.
591                td->home_timeline_id = td->home_timeline_id < status->id ? status->id : td->home_timeline_id;
[e88fbe27]592               
593                g_free( text );
[62d2cfb]594        }
595}
596
[1b221e0]597/**
598 * Callback for getting the home timeline.
599 */
600static void twitter_http_get_home_timeline(struct http_request *req)
601{
[62d2cfb]602        struct im_connection *ic = req->data;
[37aa317]603        struct twitter_data *td;
[1b221e0]604        struct xt_parser *parser;
605        struct twitter_xml_list *txl;
[62d2cfb]606
607        // Check if the connection is still active.
608        if( !g_slist_find( twitter_connections, ic ) )
609                return;
[37aa317]610       
611        td = ic->proto_data;
[1b221e0]612
613        // Check if the HTTP request went well.
[3bd4a93]614        if (req->status_code == 200)
615        {
616                td->http_fails = 0;
[c2ecadc]617                if (!(ic->flags & OPT_LOGGED_IN))
[3bd4a93]618                        imcb_connected(ic);
619        }
620        else if (req->status_code == 401)
621        {
622                imcb_error( ic, "Authentication failure" );
623                imc_logout( ic, FALSE );
624                return;
625        }
626        else
627        {
[1b221e0]628                // It didn't go well, output the error and return.
[3bd4a93]629                if (++td->http_fails >= 5)
[a7b9ec7]630                        imcb_error(ic, "Could not retrieve " TWITTER_HOME_TIMELINE_URL ": %s", twitter_parse_error(req));
[3bd4a93]631               
[1b221e0]632                return;
633        }
634
635        txl = g_new0(struct twitter_xml_list, 1);
636        txl->list = NULL;
[62d2cfb]637
[1b221e0]638        // Parse the data.
639        parser = xt_new( NULL, txl );
640        xt_feed( parser, req->reply_body, req->body_size );
641        // The root <statuses> node should hold the list of statuses <status>
[203a2d2]642        twitter_xt_get_status_list(ic, parser->root, txl);
[1b221e0]643        xt_free( parser );
644
[62d2cfb]645        // See if the user wants to see the messages in a groupchat window or as private messages.
[e88fbe27]646        if (g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "chat") == 0)
[62d2cfb]647                twitter_groupchat(ic, txl->list);
[b4dd253]648        else
[62d2cfb]649                twitter_private_message_chat(ic, txl->list);
[1b221e0]650
651        // Free the structure. 
[62d2cfb]652        txl_free(txl);
[1b221e0]653        g_free(txl);
654}
655
656/**
[62d2cfb]657 * Callback for getting (twitter)friends...
658 *
659 * Be afraid, be very afraid! This function will potentially add hundreds of "friends". "Who has
660 * hundreds of friends?" you wonder? You probably not, since you are reading the source of
661 * BitlBee... Get a life and meet new people!
[1b221e0]662 */
[62d2cfb]663static void twitter_http_get_statuses_friends(struct http_request *req)
[1b221e0]664{
[62d2cfb]665        struct im_connection *ic = req->data;
[37aa317]666        struct twitter_data *td;
[62d2cfb]667        struct xt_parser *parser;
668        struct twitter_xml_list *txl;
[2abceca]669        GSList *l = NULL;
670        struct twitter_xml_user *user;
[62d2cfb]671
672        // Check if the connection is still active.
673        if( !g_slist_find( twitter_connections, ic ) )
674                return;
[37aa317]675       
676        td = ic->proto_data;
677       
[62d2cfb]678        // Check if the HTTP request went well.
[d6aa6dd]679        if (req->status_code == 401)
680        {
681                imcb_error( ic, "Authentication failure" );
682                imc_logout( ic, FALSE );
683                return;
684        } else if (req->status_code != 200) {
[62d2cfb]685                // It didn't go well, output the error and return.
[d6aa6dd]686                imcb_error(ic, "Could not retrieve " TWITTER_SHOW_FRIENDS_URL ": %s", twitter_parse_error(req));
687                imc_logout( ic, TRUE );
[62d2cfb]688                return;
[3bd4a93]689        } else {
690                td->http_fails = 0;
[62d2cfb]691        }
[d6aa6dd]692       
693        if( !td->home_timeline_gc &&
694            g_strcasecmp( set_getstr( &ic->acc->set, "mode" ), "chat" ) == 0 )
695                twitter_groupchat_init( ic );
[62d2cfb]696
697        txl = g_new0(struct twitter_xml_list, 1);
698        txl->list = NULL;
699
700        // Parse the data.
701        parser = xt_new( NULL, txl );
702        xt_feed( parser, req->reply_body, req->body_size );
703
704        // Get the user list from the parsed xml feed.
705        twitter_xt_get_user_list(parser->root, txl);
706        xt_free( parser );
707
708        // Add the users as buddies.
[1b221e0]709        for ( l = txl->list; l ; l = g_slist_next(l) )
[62d2cfb]710        {
711                user = l->data;
[3e69802]712                twitter_add_buddy(ic, user->screen_name, user->name);
[62d2cfb]713        }
[1b221e0]714
[62d2cfb]715        // if the next_cursor is set to something bigger then 0 there are more friends to gather.
716        if (txl->next_cursor > 0)
[051372c]717        {
[62d2cfb]718                twitter_get_statuses_friends(ic, txl->next_cursor);
[051372c]719        }
720        else
721        {
722                td->flags |= TWITTER_HAVE_FRIENDS;
723                twitter_login_finish(ic);
724        }
725       
[62d2cfb]726        // Free the structure.
727        txl_free(txl);
728        g_free(txl);
[1b221e0]729}
730
731/**
[62d2cfb]732 * Get the friends.
[1b221e0]733 */
[8203da9]734void twitter_get_statuses_friends(struct im_connection *ic, gint64 next_cursor)
[1b221e0]735{
[62d2cfb]736        char* args[2];
737        args[0] = "cursor";
[8203da9]738        args[1] = g_strdup_printf ("%lld", (long long) next_cursor);
[62d2cfb]739
[bb5ce4d1]740        twitter_http(ic, TWITTER_SHOW_FRIENDS_URL, twitter_http_get_statuses_friends, ic, 0, args, 2);
[62d2cfb]741
742        g_free(args[1]);
[1b221e0]743}
744
745/**
[7d53efb]746 * Callback to use after sending a post request to twitter.
[1b221e0]747 */
[7d53efb]748static void twitter_http_post(struct http_request *req)
[1b221e0]749{
750        struct im_connection *ic = req->data;
[7b87539]751        struct twitter_data *td;
[1b221e0]752
[62d2cfb]753        // Check if the connection is still active.
754        if( !g_slist_find( twitter_connections, ic ) )
755                return;
756
[7b87539]757        td = ic->proto_data;
758        td->last_status_id = 0;
759       
[1b221e0]760        // Check if the HTTP request went well.
761        if (req->status_code != 200) {
762                // It didn't go well, output the error and return.
[ba3233c]763                imcb_error(ic, "HTTP error: %s", twitter_parse_error(req));
[1b221e0]764                return;
765        }
[7b87539]766       
767        if (req->body_size > 0)
768        {
769                struct xt_parser *xp = NULL;
770                struct xt_node *node;
771               
772                xp = xt_new(NULL, NULL);
773                xt_feed(xp, req->reply_body, req->body_size);
774               
775                if ((node = xt_find_node(xp->root, "status")) &&
776                    (node = xt_find_node(node->children, "id")) && node->text)
777                        td->last_status_id = g_ascii_strtoull( node->text, NULL, 10 );
778               
779                xt_free(xp);
780        }
[1b221e0]781}
782
783/**
784 * Function to POST a new status to twitter.
785 */ 
[b890626]786void twitter_post_status(struct im_connection *ic, char *msg, guint64 in_reply_to)
[1b221e0]787{
[b890626]788        char* args[4] = {
789                "status", msg,
790                "in_reply_to_status_id",
791                g_strdup_printf("%llu", (unsigned long long) in_reply_to)
792        };
793        twitter_http(ic, TWITTER_STATUS_UPDATE_URL, twitter_http_post, ic, 1,
794                     args, in_reply_to ? 4 : 2);
795        g_free(args[3]);
[1b221e0]796}
797
798
[62d2cfb]799/**
800 * Function to POST a new message to twitter.
801 */
802void twitter_direct_messages_new(struct im_connection *ic, char *who, char *msg)
803{
804        char* args[4];
805        args[0] = "screen_name";
806        args[1] = who;
807        args[2] = "text";
808        args[3] = msg;
809        // Use the same callback as for twitter_post_status, since it does basically the same.
[ba3233c]810        twitter_http(ic, TWITTER_DIRECT_MESSAGES_NEW_URL, twitter_http_post, ic, 1, args, 4);
[62d2cfb]811//      g_free(args[1]);
812//      g_free(args[3]);
813}
[7d53efb]814
815void twitter_friendships_create_destroy(struct im_connection *ic, char *who, int create)
816{
817        char* args[2];
818        args[0] = "screen_name";
819        args[1] = who;
[ba3233c]820        twitter_http(ic, create ? TWITTER_FRIENDSHIPS_CREATE_URL : TWITTER_FRIENDSHIPS_DESTROY_URL, twitter_http_post, ic, 1, args, 2);
[a26af5c]821}
[7b87539]822
823void twitter_status_destroy(struct im_connection *ic, guint64 id)
824{
825        char *url;
826        url = g_strdup_printf("%s%llu%s", TWITTER_STATUS_DESTROY_URL, (unsigned long long) id, ".xml");
827        twitter_http(ic, url, twitter_http_post, ic, 1, NULL, 0);
828        g_free(url);
829}
[b890626]830
831void twitter_status_retweet(struct im_connection *ic, guint64 id)
832{
833        char *url;
834        url = g_strdup_printf("%s%llu%s", TWITTER_STATUS_RETWEET_URL, (unsigned long long) id, ".xml");
835        twitter_http(ic, url, twitter_http_post, ic, 1, NULL, 0);
836        g_free(url);
837}
Note: See TracBrowser for help on using the repository browser.