source: protocols/twitter/twitter_lib.c @ b194fe7

Last change on this file since b194fe7 was 6eca2eb, checked in by Wilmer van der Gaast <wilmer@…>, at 2011-04-18T14:14:08Z

Try to show better Twitter error messages. Sadly this doesn't always work
since Twitter can't seem to make up their mind on the formatting of their
error responses, sometimes using XML and sometimes plain text.

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