source: protocols/twitter/twitter_lib.c @ 5983eca

Last change on this file since 5983eca was 5983eca, checked in by Wilmer van der Gaast <wilmer@…>, at 2011-06-11T11:28:01Z

Re-indent Twitter code. It just lacks *any* kind of consistency. Flags used:
-i8 -kr -ts8 -ut -l100 Because K&R isn't so bad after all but spaces are
definitely evil. (Not that GNU indent understands how to use tabs, oh well.)

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