source: protocols/twitter/twitter_lib.c @ 5155570

Last change on this file since 5155570 was ce81acd, checked in by Wilmer van der Gaast <wilmer@…>, at 2011-03-28T23:28:46Z

For #721, add the numbers in front of tweets if show_ids is enabled. Left
to do: Document the feature and allow using the numbers in rt/replies.

  • Property mode set to 100644
File size: 23.3 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;
[ce81acd]69        guint64 id, reply_to;
[1b221e0]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);
[fd65edb]112        g_free(txl);
[62d2cfb]113}
114
115/**
116 * Add a buddy if it is not allready added, set the status to logged in.
117 */
[3e69802]118static void twitter_add_buddy(struct im_connection *ic, char *name, const char *fullname)
[62d2cfb]119{
[1014cab]120        struct twitter_data *td = ic->proto_data;
121
[62d2cfb]122        // Check if the buddy is allready in the buddy list.
[21c87a7]123        if (!bee_user_by_handle( ic->bee, ic, name ))
[62d2cfb]124        {
[e88fbe27]125                char *mode = set_getstr(&ic->acc->set, "mode");
126               
[62d2cfb]127                // The buddy is not in the list, add the buddy and set the status to logged in.
128                imcb_add_buddy( ic, name, NULL );
[3e69802]129                imcb_rename_buddy( ic, name, fullname );
[e88fbe27]130                if (g_strcasecmp(mode, "chat") == 0)
[5c18a76]131                {
132                        /* Necessary so that nicks always get translated to the
133                           exact Twitter username. */
134                        imcb_buddy_nick_hint( ic, name, name );
[1014cab]135                        imcb_chat_add_buddy( td->home_timeline_gc, name );
[5c18a76]136                }
[e88fbe27]137                else if (g_strcasecmp(mode, "many") == 0)
[1014cab]138                        imcb_buddy_status( ic, name, OPT_LOGGED_IN, NULL, NULL );
[62d2cfb]139        }
140}
[1b221e0]141
[a7b9ec7]142/* Warning: May return a malloc()ed value, which will be free()d on the next
143   call. Only for short-term use. */
144static char *twitter_parse_error(struct http_request *req)
145{
146        static char *ret = NULL;
[3d93aed]147        struct xt_parser *xp = NULL;
[a7b9ec7]148        struct xt_node *node;
149       
150        g_free(ret);
151        ret = NULL;
152       
[3d93aed]153        if (req->body_size > 0)
[a7b9ec7]154        {
[3d93aed]155                xp = xt_new(NULL, NULL);
156                xt_feed(xp, req->reply_body, req->body_size);
157               
158                if ((node = xt_find_node(xp->root, "hash")) &&
159                    (node = xt_find_node(node->children, "error")) &&
160                    node->text_len > 0)
161                {
162                        ret = g_strdup_printf("%s (%s)", req->status_string, node->text);
163                        xt_free(xp);
164                        return ret;
165                }
166               
[a7b9ec7]167                xt_free(xp);
168        }
[3d93aed]169       
170        return req->status_string;
[a7b9ec7]171}
172
[1b221e0]173static void twitter_http_get_friends_ids(struct http_request *req);
174
175/**
176 * Get the friends ids.
177 */
[8203da9]178void twitter_get_friends_ids(struct im_connection *ic, gint64 next_cursor)
[1b221e0]179{
180        // Primitive, but hey! It works...     
181        char* args[2];
182        args[0] = "cursor";
[8203da9]183        args[1] = g_strdup_printf ("%lld", (long long) next_cursor);
[bb5ce4d1]184        twitter_http(ic, TWITTER_FRIENDS_IDS_URL, twitter_http_get_friends_ids, ic, 0, args, 2);
[1b221e0]185
186        g_free(args[1]);
187}
188
189/**
190 * Function to help fill a list.
191 */
192static xt_status twitter_xt_next_cursor( struct xt_node *node, struct twitter_xml_list *txl )
193{
[bd64716]194        char *end = NULL;
195       
196        if( node->text )
197                txl->next_cursor = g_ascii_strtoll( node->text, &end, 10 );
198        if( end == NULL )
199                txl->next_cursor = -1;
[1b221e0]200
201        return XT_HANDLED;
202}
203
204/**
205 * Fill a list of ids.
206 */
207static xt_status twitter_xt_get_friends_id_list( struct xt_node *node, struct twitter_xml_list *txl )
208{
209        struct xt_node *child;
[62d2cfb]210       
211        // Set the list type.
212        txl->type = TXL_ID;
[1b221e0]213
214        // The root <statuses> node should hold the list of statuses <status>
215        // Walk over the nodes children.
216        for( child = node->children ; child ; child = child->next )
217        {
218                if ( g_strcasecmp( "id", child->name ) == 0)
219                {
220                        // Add the item to the list.
[2abceca]221                        txl->list = g_slist_append (txl->list, g_memdup( child->text, child->text_len + 1 ));
[1b221e0]222                }
223                else if ( g_strcasecmp( "next_cursor", child->name ) == 0)
224                {
225                        twitter_xt_next_cursor(child, txl);
226                }
227        }
228
229        return XT_HANDLED;
230}
231
232/**
233 * Callback for getting the friends ids.
234 */
235static void twitter_http_get_friends_ids(struct http_request *req)
236{
237        struct im_connection *ic;
238        struct xt_parser *parser;
239        struct twitter_xml_list *txl;
[3bd4a93]240        struct twitter_data *td;
[1b221e0]241
242        ic = req->data;
243
[62d2cfb]244        // Check if the connection is still active.
245        if( !g_slist_find( twitter_connections, ic ) )
246                return;
[37aa317]247       
248        td = ic->proto_data;
[62d2cfb]249
[1b221e0]250        // Check if the HTTP request went well.
251        if (req->status_code != 200) {
252                // It didn't go well, output the error and return.
[3bd4a93]253                if (++td->http_fails >= 5)
[a7b9ec7]254                        imcb_error(ic, "Could not retrieve friends: %s", twitter_parse_error(req));
[3bd4a93]255               
[1b221e0]256                return;
[3bd4a93]257        } else {
258                td->http_fails = 0;
[1b221e0]259        }
260
261        txl = g_new0(struct twitter_xml_list, 1);
262
263        // Parse the data.
264        parser = xt_new( NULL, txl );
265        xt_feed( parser, req->reply_body, req->body_size );
266        twitter_xt_get_friends_id_list(parser->root, txl);
267        xt_free( parser );
268
269        if (txl->next_cursor)
270                twitter_get_friends_ids(ic, txl->next_cursor);
271
[62d2cfb]272        txl_free(txl);
[1b221e0]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
[2b02617]359#ifdef __GLIBC__
360#define TWITTER_TIME_FORMAT "%a %b %d %H:%M:%S %z %Y"
361#else
362#define TWITTER_TIME_FORMAT "%a %b %d %H:%M:%S +0000 %Y"
363#endif
[62d2cfb]364
[1b221e0]365/**
366 * Function to fill a twitter_xml_status struct.
367 * It sets:
368 *  - the status text and
369 *  - the created_at timestamp and
370 *  - the status id and
371 *  - the user in a twitter_xml_user struct.
372 */
373static xt_status twitter_xt_get_status( struct xt_node *node, struct twitter_xml_status *txs )
374{
[bd599b9]375        struct xt_node *child, *rt = NULL;
[e193aeb]376        gboolean truncated = FALSE;
[1b221e0]377
378        // Walk over the nodes children.
379        for( child = node->children ; child ; child = child->next )
380        {
381                if ( g_strcasecmp( "text", child->name ) == 0)
382                {
383                        txs->text = g_memdup( child->text, child->text_len + 1 );
384                }
[e193aeb]385                else if (g_strcasecmp( "truncated", child->name ) == 0 && child->text)
386                {
387                        truncated = bool2int(child->text);
388                }
389                else if (g_strcasecmp( "retweeted_status", child->name ) == 0)
390                {
391                        rt = child;
392                }
[1b221e0]393                else if (g_strcasecmp( "created_at", child->name ) == 0)
394                {
[08579a1]395                        struct tm parsed;
396                       
397                        /* Very sensitive to changes to the formatting of
398                           this field. :-( Also assumes the timezone used
399                           is UTC since C time handling functions suck. */
[2b02617]400                        if( strptime( child->text, TWITTER_TIME_FORMAT, &parsed ) != NULL )
[08579a1]401                                txs->created_at = mktime_utc( &parsed );
[1b221e0]402                }
403                else if (g_strcasecmp( "user", child->name ) == 0)
404                {
405                        txs->user = g_new0(struct twitter_xml_user, 1);
406                        twitter_xt_get_user( child, txs->user );
407                }
408                else if (g_strcasecmp( "id", child->name ) == 0)
409                {
410                        txs->id = g_ascii_strtoull (child->text, NULL, 10);
411                }
[ce81acd]412                else if (g_strcasecmp( "in_reply_to_status_id", child->name ) == 0)
413                {
414                        txs->reply_to = g_ascii_strtoull (child->text, NULL, 10);
415                }
[1b221e0]416        }
[e193aeb]417       
418        /* If it's a truncated retweet, get the original because dots suck. */
419        if (truncated && rt)
420        {
421                struct twitter_xml_status *rtxs = g_new0(struct twitter_xml_status, 1);
422                if (twitter_xt_get_status(rt, rtxs) != XT_HANDLED)
423                {
424                        txs_free(rtxs);
425                        return XT_HANDLED;
426                }
427               
428                g_free(txs->text);
429                txs->text = g_strdup_printf("RT @%s: %s", rtxs->user->screen_name, rtxs->text);
430                txs_free(rtxs);
431        }
432       
[1b221e0]433        return XT_HANDLED;
434}
435
436/**
437 * Function to fill a twitter_xml_list struct.
438 * It sets:
439 *  - all <status>es within the <status> element and
440 *  - the next_cursor.
441 */
[203a2d2]442static xt_status twitter_xt_get_status_list( struct im_connection *ic, struct xt_node *node, struct twitter_xml_list *txl )
[1b221e0]443{
444        struct twitter_xml_status *txs;
445        struct xt_node *child;
[203a2d2]446        bee_user_t *bu;
[1b221e0]447
[62d2cfb]448        // Set the type of the list.
449        txl->type = TXL_STATUS;
450
[1b221e0]451        // The root <statuses> node should hold the list of statuses <status>
452        // Walk over the nodes children.
453        for( child = node->children ; child ; child = child->next )
454        {
455                if ( g_strcasecmp( "status", child->name ) == 0)
456                {
457                        txs = g_new0(struct twitter_xml_status, 1);
458                        twitter_xt_get_status(child, txs);
459                        // Put the item in the front of the list.
460                        txl->list = g_slist_prepend (txl->list, txs);
[203a2d2]461                       
462                        if (txs->user && txs->user->screen_name &&
463                            (bu = bee_user_by_handle(ic->bee, ic, txs->user->screen_name)))
464                        {
465                                struct twitter_user_data *tud = bu->data;
466                               
467                                if (txs->id > tud->last_id)
468                                {
469                                        tud->last_id = txs->id;
470                                        tud->last_time = txs->created_at;
471                                }
472                        }
[1b221e0]473                }
474                else if ( g_strcasecmp( "next_cursor", child->name ) == 0)
475                {
476                        twitter_xt_next_cursor(child, txl);
477                }
478        }
479
480        return XT_HANDLED;
481}
482
483static void twitter_http_get_home_timeline(struct http_request *req);
484
485/**
486 * Get the timeline.
487 */
[8203da9]488void twitter_get_home_timeline(struct im_connection *ic, gint64 next_cursor)
[1b221e0]489{
490        struct twitter_data *td = ic->proto_data;
491
492        char* args[4];
493        args[0] = "cursor";
[8203da9]494        args[1] = g_strdup_printf ("%lld", (long long) next_cursor);
[1b221e0]495        if (td->home_timeline_id) {
496                args[2] = "since_id";
[0519b0a]497                args[3] = g_strdup_printf ("%llu", (long long unsigned int) td->home_timeline_id);
[1b221e0]498        }
499
[bb5ce4d1]500        twitter_http(ic, TWITTER_HOME_TIMELINE_URL, twitter_http_get_home_timeline, ic, 0, args, td->home_timeline_id ? 4 : 2);
[1b221e0]501
502        g_free(args[1]);
503        if (td->home_timeline_id) {
504                g_free(args[3]);
505        }
506}
507
[ce81acd]508static char *twitter_msg_add_id(struct im_connection *ic,
509    struct twitter_xml_status *txs, const char *prefix)
510{
511        struct twitter_data *td = ic->proto_data;
512        char *ret = NULL;
513       
514        if (!set_getbool(&ic->acc->set, "show_ids"))
515        {
516                if (*prefix)
517                        return g_strconcat(prefix, txs->text, NULL);
518                else
519                        return NULL;
520        }
521       
522        td->log[td->log_id].id = txs->id;
523        td->log[td->log_id].bu = bee_user_by_handle(ic->bee, ic, txs->user->screen_name);
524        if (txs->reply_to)
525        {
526                int i;
527                for (i = 0; i < TWITTER_LOG_LENGTH; i ++)
528                        if (td->log[i].id == txs->reply_to)
529                        {
530                                ret = g_strdup_printf( "\002[\002%02d->%02d\002]\002 %s%s",
531                                                       td->log_id, i, prefix, txs->text);
532                                break;
533                        }
534        }
535        if (ret == NULL)
536                ret = g_strdup_printf( "\002[\002%02d\002]\002 %s%s",
537                                       td->log_id, prefix, txs->text);
538        td->log_id = (td->log_id + 1) % TWITTER_LOG_LENGTH;
539       
540        return ret;
541}
542
[d6aa6dd]543static void twitter_groupchat_init(struct im_connection *ic)
544{
545        char *name_hint;
546        struct groupchat *gc;
547        struct twitter_data *td = ic->proto_data;
[fd65edb]548        GSList *l;
[d6aa6dd]549       
550        td->home_timeline_gc = gc = imcb_chat_new( ic, "home/timeline" );
551       
[ffcdf13]552        name_hint = g_strdup_printf( "%s_%s", td->prefix, ic->acc->user );
[d6aa6dd]553        imcb_chat_name_hint( gc, name_hint );
554        g_free( name_hint );
[fd65edb]555       
556        for( l = ic->bee->users; l; l = l->next )
557        {
558                bee_user_t *bu = l->data;
559                if( bu->ic == ic )
560                        imcb_chat_add_buddy( td->home_timeline_gc, bu->handle );
561        }
[d6aa6dd]562}
563
[62d2cfb]564/**
565 * Function that is called to see the statuses in a groupchat window.
566 */
567static void twitter_groupchat(struct im_connection *ic, GSList *list)
568{
569        struct twitter_data *td = ic->proto_data;
570        GSList *l = NULL;
571        struct twitter_xml_status *status;
572        struct groupchat *gc;
573
574        // Create a new groupchat if it does not exsist.
575        if (!td->home_timeline_gc)
[d6aa6dd]576                twitter_groupchat_init(ic);
577       
578        gc = td->home_timeline_gc;
579        if (!gc->joined)
[62d2cfb]580                imcb_chat_add_buddy( gc, ic->acc->user );
581
582        for ( l = list; l ; l = g_slist_next(l) )
583        {
[ce81acd]584                char *msg;
585               
[62d2cfb]586                status = l->data;
[a26af5c]587                if (status->user == NULL || status->text == NULL)
588                        continue;
589
[3e69802]590                twitter_add_buddy(ic, status->user->screen_name, status->user->name);
[d569019]591               
[0b3ffb1]592                strip_html(status->text);
[ce81acd]593                msg = twitter_msg_add_id(ic, status, "");
[0b3ffb1]594               
[62d2cfb]595                // Say it!
[d569019]596                if (g_strcasecmp(td->user, status->user->screen_name) == 0)
[ce81acd]597                        imcb_chat_log(gc, "You: %s", msg ? msg : status->text);
[d569019]598                else
[ce81acd]599                        imcb_chat_msg(gc, status->user->screen_name,
600                                      msg ? msg : status->text, 0, status->created_at );
601               
602                g_free(msg);
[d569019]603               
[62d2cfb]604                // Update the home_timeline_id to hold the highest id, so that by the next request
[ce81acd]605                // we won't pick up the updates already in the list.
606                td->home_timeline_id = MAX(td->home_timeline_id, status->id);
[62d2cfb]607        }
608}
609
610/**
611 * Function that is called to see statuses as private messages.
612 */
613static void twitter_private_message_chat(struct im_connection *ic, GSList *list)
614{
615        struct twitter_data *td = ic->proto_data;
616        GSList *l = NULL;
617        struct twitter_xml_status *status;
[e88fbe27]618        char from[MAX_STRING];
619        gboolean mode_one;
620       
621        mode_one = g_strcasecmp( set_getstr( &ic->acc->set, "mode" ), "one" ) == 0;
[62d2cfb]622
[e88fbe27]623        if( mode_one )
624        {
[ffcdf13]625                g_snprintf( from, sizeof( from ) - 1, "%s_%s", td->prefix, ic->acc->user );
[e88fbe27]626                from[MAX_STRING-1] = '\0';
627        }
628       
[62d2cfb]629        for ( l = list; l ; l = g_slist_next(l) )
630        {
[ce81acd]631                char *prefix = NULL, *text = NULL;
[e88fbe27]632               
[62d2cfb]633                status = l->data;
[e88fbe27]634               
[0b3ffb1]635                strip_html( status->text );
[e88fbe27]636                if( mode_one )
[ce81acd]637                        prefix = g_strdup_printf("\002<\002%s\002>\002 ",
638                                                 status->user->screen_name);
[55b1e69]639                else
640                        twitter_add_buddy(ic, status->user->screen_name, status->user->name);
[e88fbe27]641               
[ce81acd]642                text = twitter_msg_add_id(ic, status, prefix ? prefix : "");
643               
[e88fbe27]644                imcb_buddy_msg( ic,
645                                mode_one ? from : status->user->screen_name,
[ce81acd]646                                text ? text : status->text,
[e88fbe27]647                                0, status->created_at );
648               
[62d2cfb]649                // Update the home_timeline_id to hold the highest id, so that by the next request
[ce81acd]650                // we won't pick up the updates already in the list.
651                td->home_timeline_id = MAX(td->home_timeline_id,  status->id);
[e88fbe27]652               
653                g_free( text );
[ce81acd]654                g_free( prefix );
[62d2cfb]655        }
656}
657
[1b221e0]658/**
659 * Callback for getting the home timeline.
660 */
661static void twitter_http_get_home_timeline(struct http_request *req)
662{
[62d2cfb]663        struct im_connection *ic = req->data;
[37aa317]664        struct twitter_data *td;
[1b221e0]665        struct xt_parser *parser;
666        struct twitter_xml_list *txl;
[62d2cfb]667
668        // Check if the connection is still active.
669        if( !g_slist_find( twitter_connections, ic ) )
670                return;
[37aa317]671       
672        td = ic->proto_data;
[1b221e0]673
674        // Check if the HTTP request went well.
[3bd4a93]675        if (req->status_code == 200)
676        {
677                td->http_fails = 0;
[c2ecadc]678                if (!(ic->flags & OPT_LOGGED_IN))
[3bd4a93]679                        imcb_connected(ic);
680        }
681        else if (req->status_code == 401)
682        {
683                imcb_error( ic, "Authentication failure" );
684                imc_logout( ic, FALSE );
685                return;
686        }
687        else
688        {
[1b221e0]689                // It didn't go well, output the error and return.
[3bd4a93]690                if (++td->http_fails >= 5)
[a7b9ec7]691                        imcb_error(ic, "Could not retrieve " TWITTER_HOME_TIMELINE_URL ": %s", twitter_parse_error(req));
[3bd4a93]692               
[1b221e0]693                return;
694        }
695
696        txl = g_new0(struct twitter_xml_list, 1);
697        txl->list = NULL;
[62d2cfb]698
[1b221e0]699        // Parse the data.
700        parser = xt_new( NULL, txl );
701        xt_feed( parser, req->reply_body, req->body_size );
702        // The root <statuses> node should hold the list of statuses <status>
[203a2d2]703        twitter_xt_get_status_list(ic, parser->root, txl);
[1b221e0]704        xt_free( parser );
705
[62d2cfb]706        // See if the user wants to see the messages in a groupchat window or as private messages.
[fd65edb]707        if (txl->list == NULL)
708                ;
709        else if (g_strcasecmp(set_getstr(&ic->acc->set, "mode"), "chat") == 0)
[62d2cfb]710                twitter_groupchat(ic, txl->list);
[b4dd253]711        else
[62d2cfb]712                twitter_private_message_chat(ic, txl->list);
[1b221e0]713
714        // Free the structure. 
[62d2cfb]715        txl_free(txl);
[1b221e0]716}
717
718/**
[62d2cfb]719 * Callback for getting (twitter)friends...
720 *
721 * Be afraid, be very afraid! This function will potentially add hundreds of "friends". "Who has
722 * hundreds of friends?" you wonder? You probably not, since you are reading the source of
723 * BitlBee... Get a life and meet new people!
[1b221e0]724 */
[62d2cfb]725static void twitter_http_get_statuses_friends(struct http_request *req)
[1b221e0]726{
[62d2cfb]727        struct im_connection *ic = req->data;
[37aa317]728        struct twitter_data *td;
[62d2cfb]729        struct xt_parser *parser;
730        struct twitter_xml_list *txl;
[2abceca]731        GSList *l = NULL;
732        struct twitter_xml_user *user;
[62d2cfb]733
734        // Check if the connection is still active.
735        if( !g_slist_find( twitter_connections, ic ) )
736                return;
[37aa317]737       
738        td = ic->proto_data;
739       
[62d2cfb]740        // Check if the HTTP request went well.
[d6aa6dd]741        if (req->status_code == 401)
742        {
743                imcb_error( ic, "Authentication failure" );
744                imc_logout( ic, FALSE );
745                return;
746        } else if (req->status_code != 200) {
[62d2cfb]747                // It didn't go well, output the error and return.
[d6aa6dd]748                imcb_error(ic, "Could not retrieve " TWITTER_SHOW_FRIENDS_URL ": %s", twitter_parse_error(req));
749                imc_logout( ic, TRUE );
[62d2cfb]750                return;
[3bd4a93]751        } else {
752                td->http_fails = 0;
[62d2cfb]753        }
[d6aa6dd]754       
755        if( !td->home_timeline_gc &&
756            g_strcasecmp( set_getstr( &ic->acc->set, "mode" ), "chat" ) == 0 )
757                twitter_groupchat_init( ic );
[62d2cfb]758
759        txl = g_new0(struct twitter_xml_list, 1);
760        txl->list = NULL;
761
762        // Parse the data.
763        parser = xt_new( NULL, txl );
764        xt_feed( parser, req->reply_body, req->body_size );
765
766        // Get the user list from the parsed xml feed.
767        twitter_xt_get_user_list(parser->root, txl);
768        xt_free( parser );
769
770        // Add the users as buddies.
[1b221e0]771        for ( l = txl->list; l ; l = g_slist_next(l) )
[62d2cfb]772        {
773                user = l->data;
[3e69802]774                twitter_add_buddy(ic, user->screen_name, user->name);
[62d2cfb]775        }
[1b221e0]776
[62d2cfb]777        // if the next_cursor is set to something bigger then 0 there are more friends to gather.
778        if (txl->next_cursor > 0)
[051372c]779        {
[62d2cfb]780                twitter_get_statuses_friends(ic, txl->next_cursor);
[051372c]781        }
782        else
783        {
784                td->flags |= TWITTER_HAVE_FRIENDS;
785                twitter_login_finish(ic);
786        }
787       
[62d2cfb]788        // Free the structure.
789        txl_free(txl);
[1b221e0]790}
791
792/**
[62d2cfb]793 * Get the friends.
[1b221e0]794 */
[8203da9]795void twitter_get_statuses_friends(struct im_connection *ic, gint64 next_cursor)
[1b221e0]796{
[62d2cfb]797        char* args[2];
798        args[0] = "cursor";
[8203da9]799        args[1] = g_strdup_printf ("%lld", (long long) next_cursor);
[62d2cfb]800
[bb5ce4d1]801        twitter_http(ic, TWITTER_SHOW_FRIENDS_URL, twitter_http_get_statuses_friends, ic, 0, args, 2);
[62d2cfb]802
803        g_free(args[1]);
[1b221e0]804}
805
806/**
[7d53efb]807 * Callback to use after sending a post request to twitter.
[1b221e0]808 */
[7d53efb]809static void twitter_http_post(struct http_request *req)
[1b221e0]810{
811        struct im_connection *ic = req->data;
[7b87539]812        struct twitter_data *td;
[1b221e0]813
[62d2cfb]814        // Check if the connection is still active.
815        if( !g_slist_find( twitter_connections, ic ) )
816                return;
817
[7b87539]818        td = ic->proto_data;
819        td->last_status_id = 0;
820       
[1b221e0]821        // Check if the HTTP request went well.
822        if (req->status_code != 200) {
823                // It didn't go well, output the error and return.
[ba3233c]824                imcb_error(ic, "HTTP error: %s", twitter_parse_error(req));
[1b221e0]825                return;
826        }
[7b87539]827       
828        if (req->body_size > 0)
829        {
830                struct xt_parser *xp = NULL;
831                struct xt_node *node;
832               
833                xp = xt_new(NULL, NULL);
834                xt_feed(xp, req->reply_body, req->body_size);
835               
836                if ((node = xt_find_node(xp->root, "status")) &&
837                    (node = xt_find_node(node->children, "id")) && node->text)
838                        td->last_status_id = g_ascii_strtoull( node->text, NULL, 10 );
839               
840                xt_free(xp);
841        }
[1b221e0]842}
843
844/**
845 * Function to POST a new status to twitter.
846 */ 
[b890626]847void twitter_post_status(struct im_connection *ic, char *msg, guint64 in_reply_to)
[1b221e0]848{
[b890626]849        char* args[4] = {
850                "status", msg,
851                "in_reply_to_status_id",
852                g_strdup_printf("%llu", (unsigned long long) in_reply_to)
853        };
854        twitter_http(ic, TWITTER_STATUS_UPDATE_URL, twitter_http_post, ic, 1,
855                     args, in_reply_to ? 4 : 2);
856        g_free(args[3]);
[1b221e0]857}
858
859
[62d2cfb]860/**
861 * Function to POST a new message to twitter.
862 */
863void twitter_direct_messages_new(struct im_connection *ic, char *who, char *msg)
864{
865        char* args[4];
866        args[0] = "screen_name";
867        args[1] = who;
868        args[2] = "text";
869        args[3] = msg;
870        // Use the same callback as for twitter_post_status, since it does basically the same.
[ba3233c]871        twitter_http(ic, TWITTER_DIRECT_MESSAGES_NEW_URL, twitter_http_post, ic, 1, args, 4);
[62d2cfb]872//      g_free(args[1]);
873//      g_free(args[3]);
874}
[7d53efb]875
876void twitter_friendships_create_destroy(struct im_connection *ic, char *who, int create)
877{
878        char* args[2];
879        args[0] = "screen_name";
880        args[1] = who;
[ba3233c]881        twitter_http(ic, create ? TWITTER_FRIENDSHIPS_CREATE_URL : TWITTER_FRIENDSHIPS_DESTROY_URL, twitter_http_post, ic, 1, args, 2);
[a26af5c]882}
[7b87539]883
884void twitter_status_destroy(struct im_connection *ic, guint64 id)
885{
886        char *url;
887        url = g_strdup_printf("%s%llu%s", TWITTER_STATUS_DESTROY_URL, (unsigned long long) id, ".xml");
888        twitter_http(ic, url, twitter_http_post, ic, 1, NULL, 0);
889        g_free(url);
890}
[b890626]891
892void twitter_status_retweet(struct im_connection *ic, guint64 id)
893{
894        char *url;
895        url = g_strdup_printf("%s%llu%s", TWITTER_STATUS_RETWEET_URL, (unsigned long long) id, ".xml");
896        twitter_http(ic, url, twitter_http_post, ic, 1, NULL, 0);
897        g_free(url);
898}
Note: See TracBrowser for help on using the repository browser.